@dvina/sdk

中文文档
5.0.67发布于 2026/11/13MITTypeScriptES Module近 30 天下载 7197

Type-safe SDK for the Dvina GraphQL API

原始使用文档
文档目录21 节
  1. Installation
  2. Workspace Development
  3. Quick Start
  4. Options
  5. Queries
  6. Fetching a Single Entity
  7. Fetching a Connection (List)
  8. Smart Fetch (Including Relations)
  9. Lazy Relation Fetching
  10. Pagination
  11. Mutations
  12. Reactive Queries (watch)
  13. Chat
  14. Realtime events
  15. File Upload
  16. Framework Adapters
  17. React
  18. Error Handling
  19. Architecture
  20. Cleanup
  21. License

@dvina/sdk

Type-safe SDK for the Dvina GraphQL API. Offline-first architecture with normalized local store (IndexedDB), real-time sync, a React adapter and an AI SDK chat transport.

Installation

npm i @dvina/sdk

Workspace Development

From the repository root:

pnpm bootstrap
pnpm --filter @dvina/sdk run generate:workspace
pnpm --filter @dvina/sdk run build

Or use the root shortcut:

pnpm build:sdk

generate:workspace first builds the codegen helper packages, then regenerates SDK sources. build:workspace runs the full generate + build flow in one command.

Peer dependency:

npm i graphql

Framework adapters (optional):

# React
npm i react

# Chat (AI SDK)
npm i ai

Quick Start

import { DvinaSdk } from '@dvina/sdk';

const sdk = new DvinaSdk({ token: 'your-auth-token' });

For production apps with token refresh:

const sdk = new DvinaSdk({
    getToken: async () => {
        // return a fresh token from your auth provider
        return await authService.getAccessToken();
    },
});

Options

OptionTypeDescription
tokenstringStatic auth token (scripts, CLI, tests)
getToken() => Promise<string>Async token callback (production apps)
baseUrlstringAPI base URL without protocol. Default: api.dvina.ai
languagestring | (() => string)Preferred language for API responses

Queries

Fetching a Single Entity

const chat = await sdk.chat({ id: 'chat-id' }).fetch();

console.log(chat.id);
console.log(chat.title);

Fetching a Connection (List)

const insights = await sdk.insights({ first: 20 }).fetch();

for (const insight of insights.nodes) {
    console.log(insight.id, insight.name);
}

console.log(insights.totalCount);
console.log(insights.pageInfo.hasNextPage);

Smart Fetch (Including Relations)

Chain relation methods before .fetch() to include related data in a single request:

// Include agent data inline with each chat
const chats = await sdk.chats({ first: 50 }).agent().fetch();

// agent is directly available on each node (no extra request)
for (const chat of chats.nodes) {
    console.log(chat.agent.id, chat.agent.name);
}

// Single entity with includes
const chat = await sdk.chat({ id: 'chat-id' }).agent().fetch();
console.log(chat.agent.name);

Nested includes with variables:

const databases = await sdk.databases({ first: 100 }).tables({ first: 10 }).fetch();

for (const database of databases.nodes) {
    for (const table of database.tables.nodes) {
        console.log(table.name);
    }
}

Lazy Relation Fetching

Without Smart Fetch, relations are fetched on demand via method calls:

const chat = await sdk.chat({ id: 'chat-id' }).fetch();

// Separate request for the agent
const agent = await chat.agent().fetch();
console.log(agent.name);

Pagination

Connections follow the Relay cursor specification with built-in pagination helpers:

const insights = await sdk.insights({ first: 20 }).fetch();

// Load next page (appends nodes, updates pageInfo)
if (insights.pageInfo.hasNextPage) {
    await insights.fetchNext();
}

// Load previous page (prepends nodes, updates pageInfo)
if (insights.pageInfo.hasPreviousPage) {
    await insights.fetchPrevious();
}

Utility functions:

import { extractNodes, mergeConnections } from '@dvina/sdk/pagination';

const allNodes = extractNodes(insights);

// Returns a new connection without modifying the originals
const merged = mergeConnections(firstPage, secondPage);

Mutations

// Create
const agent = await sdk.createAgent({
    data: { name: 'My Agent', description: 'An assistant' },
});

// Update
const updated = await sdk.updateInsight({
    where: { id: 'insight-id' },
    data: { name: 'Updated Name' },
});

// Delete
await sdk.deleteChat({ where: { id: 'chat-id' } });

Mutations automatically update the local store with optimistic updates and cache rules.

Reactive Queries (watch)

Use .watch() instead of .fetch() to get a reactive DvinaQueryRef that emits new values whenever the underlying data changes (via local mutations, delta sync, or cache updates):

const ref = sdk.insights({ first: 20 }).watch();

// Vanilla JavaScript — AsyncIterable
for await (const insights of ref) {
    console.log('Updated:', insights.nodes.length);
}

// Clean up when done
ref.dispose();

DvinaQueryRef properties:

Property/MethodDescription
currentMost recently emitted value (or undefined)
refetch()Force re-fetch from network
fetchMore()Fetch additional pages and merge
dispose()Unsubscribe and clean up

Chat

Chat runs over the AI SDK UI message stream. @dvina/sdk/ai provides the transport for useChat and the message types:

import { useChat } from '@ai-sdk/react';
import { createDvinaChatTransport, stopDvinaChat, type DvinaUIMessage } from '@dvina/sdk/ai';

const chatOptions = { apiUrl: sdk.apiUrl, getToken: (options) => sdk.getToken(options) };
const transport = createDvinaChatTransport(chatOptions);

function Chat({ chatId }: { chatId: string }) {
    const { messages, sendMessage } = useChat<DvinaUIMessage>({ id: chatId, transport, resume: true });
    // render messages, call sendMessage({ text })
}

// Stop the running answer, on whichever API instance runs it
await stopDvinaChat(chatOptions, chatId);

The transport sends only the newest message; the server keeps the history. A reconnecting client resumes the running stream.

Realtime events

User-scoped updates arrive over the sync SSE connection as snapshots:

const usage = sdk.events.tokenUsageStatus().watch(); // also activeChats(), devicePresence(), mcpAuth()
for await (const event of usage) {
    console.log(event.data);
}

mcpAuth() lists MCP servers waiting for the user to authorize (open authorizationUrl) and recent outcomes.

File Upload

const file = new File(['content'], 'document.pdf', { type: 'application/pdf' });

const result = await sdk.uploadFile(file, 'document.pdf', {
    onProgress: (event) => {
        console.log(`${event.percent}% uploaded`);
    },
});

console.log('File ID:', result.id);

Framework Adapters

React

import { useLiveQuery } from '@dvina/sdk/react';

function InsightsPage() {
    const insights = useLiveQuery(() => sdk.insights({ first: 20 }).watch(), []);

    if (!insights) return <div>Loading...</div>;

    return (
        <ul>
            {insights.nodes.map((i) => (
                <li key={i.id}>{i.name}</li>
            ))}
        </ul>
    );
}

The hook is built on useSyncExternalStore. The query ref is created again only when the dependency array changes, so inline .watch() calls are safe.

// Reactive query — ref recreated when `id` changes
function InsightDetail({ id }: { id: string }) {
    const insight = useLiveQuery(() => sdk.insight({ id }).watch(), [id]);

    if (!insight) return <div>Loading...</div>;
    return <h1>{insight.name}</h1>;
}

useLiveQuery disposes the query ref once no component uses it (unmount or dependency change); a component shown again gets a fresh ref.

Error Handling

The SDK provides a typed error hierarchy:

import { DvinaError, DvinaGraphQLError, DvinaNetworkError, DvinaAuthenticationError } from '@dvina/sdk';

try {
    await sdk.chat({ id: 'invalid' }).fetch();
} catch (error) {
    if (error instanceof DvinaAuthenticationError) {
        // 401/403 — redirect to login
    } else if (error instanceof DvinaGraphQLError) {
        // Server returned GraphQL errors
        console.error(error.errors);
    } else if (error instanceof DvinaNetworkError) {
        // Connection failure, timeout, etc.
        console.error(error.status);
    }
}
Error ClassWhen
DvinaErrorBase class for all SDK errors
DvinaGraphQLErrorGraphQL response contains errors
DvinaNetworkErrorNetwork failure, timeout, non-OK HTTP
DvinaAuthenticationError401/403 authentication failure

Architecture

The SDK uses an offline-first, normalized store architecture:

  • Queries are routed through the SyncEngine, which normalizes GraphQL responses into entity tables in IndexedDB (via Dexie).
  • Mutations apply optimistic updates immediately, then reconcile with server responses.
  • Delta Sync maintains a persistent SSE connection that streams entity changes, keeping the local store up to date in real-time.
  • Reactive queries (watch()) are backed by Dexie's liveQuery — any write to a relevant table automatically re-emits.
DvinaSdk
├── Queries/Mutations → HTTP Transport → SyncEngine → IndexedDB (Dexie)
├── Delta Sync        → SSE Transport → IndexedDB (Dexie)
├── Realtime events   → SSE Transport → sdk.events
└── Chat              → AI SDK UI message stream (@dvina/sdk/ai)

Cleanup

// Stop delta sync
sdk.stopSync();

// Full teardown (stops sync and the billing stream, closes the local database)
sdk.destroy();

License

MIT

版本与发布记录

@dvina/sdk 共发布 129 个版本,首次发布于 2026/03/18;过去 12 个月发布了 129 个版本。

版本发布日期距今
5.0.67latest2026/11/13今天
5.0.662026/11/121 天前
5.0.652026/11/121 天前
5.0.632026/11/111 天前
5.0.622026/11/112 天前
5.0.612026/11/112 天前

关于 @dvina/sdk 的常见问题

@dvina/sdk 是什么?

Type-safe SDK for the Dvina GraphQL API

如何安装 @dvina/sdk?

在项目目录运行 npm install @dvina/sdk,或使用 pnpm add / yarn add / bun add。

@dvina/sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dvina/sdk 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@dvina/sdk 可以商用吗?

@dvina/sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@dvina/sdk 的下载量有多少?

根据 npm 官方下载统计,@dvina/sdk 在 2026/10/13 – 2026/11/11 这 30 天内被下载 7,197 次,日均约 240 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dvina/sdk 还在维护吗?

最新版本 5.0.67 发布于 2026/11/13(今天);至今共发布 129 个版本,其中过去 12 个月发布了 129 个;首次发布于 2026/03/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。