@abele/sync-sdk

中文文档
0.3.17发布于 2026/11/05GPL-3.0-onlyTypeScriptES Module近 30 天下载 3649

Node client SDK to sync a folder or your own storage with an Abele sync server by connection key, including a task and note parser.

原始使用文档
文档目录9 节
  1. API and operations
  2. Opening and adapters
  3. Item sets (server 0.5.0)
  4. State, locks and recovery
  5. Synchronization and delivery
  6. Reading, history and decisions
  7. Errors
  8. Licence
  9. Included notes parser and types

@abele/sync-sdk

Installation target: npm install @abele/sync-sdk.

import { open, parseKey } from "@abele/sync-sdk";
const key = process.env.SYNC_KEY;
parseKey(key); // local validation
const client = await open({ key, dir: "./mirror" });
try {
  await client.syncOnce();
  console.log(await client.replica.list());
} finally {
  await client.close();
}

API and operations

SDK 0.3.17 batches removal retention into durable cohorts. Native V2 hosts can opt in with onRemovalCohort and the borrowed cohort.retain handle; existing onRemoval hosts keep working. Public cohort helpers are available through the core export. See the migration checklist for ownership and callback contracts. This dev artifact is not a publication or deployment.

Custom hosts must follow the 0.3.0 migration checklist. pause() and stop() interrupt active syncs. An interrupted syncOnce() rejects with exported SyncCancelled; treat it as intentional cancellation before inspecting its inherited offline code. Cancellation emits no error event or offline retry.

Requires Node.js 22+ and ESM. SDK 0.3.17 pairs with core, node and protocol 0.5.0, with server targets 0.3.3 and 0.5.0 (compat.json). Protocol 2 is required; its number alone does not guarantee compatibility with every server build. The matching core, Node adapters and protocol are bundled inside this package.

Opening and adapters

parseKey(key) validates and decodes locally without network access. Its result contains the token address and either a legacy scope or v2 item previews; treat it as a credential. open(options) checks capabilities and the active connection, then opens local state and completes recovery. Call syncOnce() explicitly for current server data.

client.scope reports the server connection's folder, group or file scope. Paths are relative to that projected replica. There is no SDK group management API. client.readOnly reports server permission; there is no local read-only option. Incoming sync can still write local files. Use server permissions to prevent uploads.

Choose either { key, dir, stateDir? } for SDK-owned Node adapters, or { key, files, state } for caller-owned adapters. FileSystem and StateStore are exported core interfaces, not constructors. Supply both custom adapters and do not combine them with directory options. Their durability, watching and final disposal belong to the caller; closing the SDK does not dispose them.

Optional transport.fetch and transport.WebSocket replace Node globals. clock: () => number supplies engine time in milliseconds, not the filesystem lock/recovery clocks. logger: (line: string) => void receives generic activity notices. backoffMs: [min, max] and fallbackMs configure retry and periodic sync delays in milliseconds. deleteGuard and defer control decisions below.

Item sets (server 0.5.0)

import { ItemClient, AccessClient, open } from "@abele/sync-sdk";

const owner = ItemClient.fromToken(ownerKey, { fetch });
if (await owner.supportsItems()) {
  const added = await owner.addItem(targetTokenId, {
    kind: "file",
    id: fileId,
  });
  const recipient = ItemClient.fromToken(recipientKey, { fetch });
  const inventory = await recipient.items();
  const client = await open({
    key: recipientKey,
    item: added.id,
    files,
    state,
  });
  try {
    await client.syncOnce();
  } finally {
    await client.close();
  }
  await owner.removeItem(targetTokenId, added.id); // detach access, keep content
}

items() lists the calling key's items, including removed entries. ItemClient.tokenItems(tokenId) and AccessClient.tokenItems(tokenId) list an owned key's items without its recipient secret. AccessClient.tokens() includes optional item_count values when supported; older servers omit them. ./protocol exports V2TokenItemsResponseSchema and V2TokenItemsResponse for owner inventory responses.

Item inventory, previews, totals and item counts are filtered to caller coverage. Hidden child items are omitted; directly previewing one returns HTTP 403. Group overlap_count includes undisclosed shares, so it may exceed overlaps.length even below the response cap. Undisclosed overlaps have no array entry, path or identifier; do not infer absence from an empty array. The overlap-detail schema is unchanged: disclosed entries still have all fields. ItemClient surfaces 403 as AbeleError with code: "forbidden" and status: 403, matching server error envelopes from AccessClient. Both AbeleError and HttpError are exported at the SDK root (a non-envelope AccessClient refusal can be HttpError with status: 403). This changes ItemClient 403 handling from HttpError/protocol to AbeleError/forbidden. Consumers must handle omitted items and partial totals without treating them as complete key inventory or vault-wide deletion impact.

Both clients expose previewTokenItem(tokenId, itemId, { limit? }) and previewToken(tokenId, { limit? }) for owner-authorized deletion impact previews. They return full file/byte totals, capped file paths and group cross-token overlaps; file/folder previews have empty overlap lists. Key totals deduplicate files across items. The optional file limit defaults to 100 and is capped at 500; overlap lists are capped at 20. These are read-only previews, not deletion authorization or a snapshot guarantee. ./protocol exports V2ContentPreviewQuerySchema, V2ItemContentPreviewSchema, V2TokenContentPreviewSchema, their response types V2ItemContentPreview and V2TokenContentPreview, and the V2_PREVIEW_FILE_LIMIT and V2_PREVIEW_OVERLAP_LIMIT constants. The issuer or whole-vault owner uses addItem(tokenId, selector) and removeItem(tokenId, itemId) to manage a target key. Selectors support folder, group and file; their ids identify server objects, not paths. Use AccessClient.fromToken(key, { fetch }) to issue/list/revoke keys and V2EngineClient from the root or ./core for lower-level engine operations.

deleteItem(itemId, idempotencyKey) deletes the selected item's server content under server ownership checks. It can target an owned recipient key's item. Persist and reuse the same idempotency key when retrying this destructive operation. Removing access and deleting content are separate operations.

supportsItems() probes the authenticated inventory route because the server does not advertise an item capability flag. It returns false on 404 and throws on authentication, connectivity, malformed-response and other server errors. Server 0.3.3 supports legacy keys only. open({ item }) checks item support before opening a content connection; omitting item preserves primary-scope behavior. Each item needs separate files and state. Node replicas persist selection in their connection identity and refuse switching items in an existing state directory.

parseKey accepts legacy payload v1 (scope) and payload v2 (items previews). Use the union's v field to narrow it. Previews are hints; the server connection and inventory provide current authority. ./protocol exports the v2 token, item, selector, connection and deletion schemas and existing payload types; the root also exports ScopeItem, ScopeItemsResponse, V2AccessToken, V2Scope, V2Selector, V2Connection and the core ClientOptions type.

State, locks and recovery

The default dir/.abele-sync stores configuration (including credentials), SQLite state and recovery evidence. Keep it private and out of other sync tools. stateDir can name an external directory, or exactly the default state path; other paths inside the replica, including symlink aliases, are rejected.

External state moves credentials and the ledger outside the replica, but local installation staging and transient directory locks still use dir/.abele-sync for atomic filesystem operations. Do not remove it while active.

Reopen the same directory, key and state directory to resume. The ledger is bound to its connection; external state is also bound to the canonical replica directory. Reusing it for another directory or key is refused. Use a fresh pair for a different replica; do not edit configuration to bypass binding checks.

The Node adapter uses native better-sqlite3: installation needs a binary for the Node ABI/platform, or a native build toolchain if no prebuilt binary exists. An installation/import failure does not imply server incompatibility.

Only one process may own a replica. Lock acquisition can wait to check a previous owner; do not delete a lock to force another writer. Await close() to release SQLite and locks. After interruption reopen the same state to recover journals and pending writes before normal syncing. RecoveryRequiredError (code RECOVERY_REQUIRED) means recovery needs attention; its message contains no local paths or underlying error details. Reopen with the original state directory. If recovery refuses, preserve files and state for investigation. Back up both together while the client is closed.

Synchronization and delivery

syncOnce() returns the core SyncReport: pull, push, secondPull, collisions, deferred, joined. Success need not mean every file transferred: settling files, held deletes and deferred changes may require another pass/action. Sync is bidirectional.

start() enables watch, event-socket and fallback triggers. pause() interrupts the active run and suspends automatic triggers; manual sync still works unless authorization has halted. Interrupted syncOnce() rejects with SyncCancelled, without an error event or offline retry. resume() clears pause/authorization halt and prompts sync. stop() awaits active work and releases automatic resources; await it before restarting. Pause and authorization halt survive stop/start. close() also releases owned adapters, is idempotent, and permanently closes the client. Use finally and handle SIGINT/SIGTERM, as the folder-mirror example does.

on(event, listener) returns an unsubscribe function. Listeners run synchronously; thrown exceptions are isolated. Handle rejections from your own async work.

EventValue
statusCore status; state idle, syncing, offline, paused or error; sanitized error text
syncedSuccessful core report
changeCore AppliedChange after durable apply
conflictFinal push merged/copied outcome, path and IDs; copies include conflict path and IDs
errorSanitized SyncError
revokedOnce per authorization halt, or successful self-revocation
heldDeletesChanged nonempty held-delete list

Applied change notifications can repeat during crash replay. Events are live, not a durable queue or exactly-once subscription. Conflict events cover the final push response, not all historical batches. Build an idempotent consumer by re-reading the replica and upserting derived records by path, rather than appending an action per notification. Reconcile with list() at startup to cover missed notifications. External side effects need their own persistent, atomic deduplication; an in-memory set does not survive crashes.

Reading, history and decisions

replica.list(prefix?), read(path) and readText(path) read local data, including unsynced edits. Listing is sorted, recursive and literal-prefix based: Notes/ selects descendants; Notes also matches Notes-old.md. Entries have path, byte size, mtime, and optional ledger fileId. Text is UTF-8. State paths are excluded; absolute, traversal and backslash paths are rejected.

history(path, { limit?, before? }?) reads server versions. restore(path, versionId) uses the engine's serialized restore operation. trash() lists deleted files; restoreDeleted(pathOrId) restores a listed path or file ID. Restore returns a core commit result; rejection throws HistoryError. Sync afterwards to fetch restored content. Server retention and permissions apply.

The default delete guard holds 50 deletions, or at least 10 making up a quarter of synced entries. Recent deletes count for a default 15-minute window. deleteGuard: { count, floor, share, windowMs? } overrides thresholds; deleteGuard: false disables protection. heldDeletes() lists path/fileId pairs. Pass reviewed IDs to decideDeletes({ kind: "confirm" | "restore", fileIds }). Confirm sends deletes; restore retrieves missing local files from live server copies, not trash. The result is { decided, report }. A decision starts sync unless paused/halted, when it waits for syncing to resume.

defer: (wirePath) => boolean stages matching remote changes instead of writing them locally. deferred() lists staged changes; applyDeferred(versionIds?) applies selected versions through the engine. Omit IDs to apply all current staged changes; pass reviewed IDs to avoid unseen replacements. This is data deferral, not a code approval/execution facility.

disconnect() stops, forgets local connection state/credentials and closes, keeping user files. Custom metadata stores require metadataKeys and setMeta for complete forgetting, otherwise HistoryError("unsupported") is thrown. revoke() revokes this connection's server token and stops syncing, retaining local state. A self-revoked client cannot start/resume; close or disconnect it separately.

Errors

Branch on exported classes and codes, not message text.

ClassCode
InvalidKeyINVALID_KEY
IncompatibleServerINCOMPATIBLE_SERVER
ReplicaConfigurationErrorREPLICA_CONFIGURATION
SyncCancelledoffline (intentional interruption; check class first)
SyncErroroffline, unauthorized, other
HistoryErrorclosed, unauthorized, offline, forbidden, not_found, unsupported, other

Invalid adapters/paths can throw TypeError; lifecycle misuse can throw ordinary errors. Some open() transport and native filesystem/SQLite/lock failures retain upstream error shapes at this version. Do not log arbitrary errors/causes or connection credentials. SDK event/history errors and sync failures are sanitized.

Licence

GPL-3.0-only; see LICENSE. Conveying this package or a combined derivative application requires GPLv3 compliance, including applicable source, notice and licence obligations. Private modification and use do not require publication; network access alone creates no AGPL-style source offer. Distribution to others still carries GPL obligations, even when private or commercial.

Included notes parser and types

The task and note parser is included:

import { taskFromFrontmatter, noteFromMarkdown } from "@abele/sync-sdk/notes";
const task = taskFromFrontmatter({ groups: ["work"] });

Use @abele/sync-sdk/core and @abele/sync-sdk/protocol for the core and protocol types referenced by the public SDK API, including adapter contracts and decoded connection tokens. No separate Abele packages are needed.

The core, protocol and Node adapter code comes from abele-sync source. See NOTICE and the included upstream licences. The notes parser is built from this repository. Public YAML helper declarations require @types/js-yaml, which is included as a dependency.

版本与发布记录

@abele/sync-sdk 共发布 7 个版本,首次发布于 2026/10/20;过去 12 个月发布了 7 个版本。

版本发布日期距今
0.3.17latest2026/11/051 天前
0.3.162026/11/042 天前
0.3.02026/10/2314 天前
0.2.02026/10/2115 天前
0.1.12026/10/2017 天前
0.1.02026/10/2017 天前

关于 @abele/sync-sdk 的常见问题

@abele/sync-sdk 是什么?

Node client SDK to sync a folder or your own storage with an Abele sync server by connection key, including a task and note parser.

如何安装 @abele/sync-sdk?

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

@abele/sync-sdk 支持 TypeScript 吗?

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

@abele/sync-sdk 支持 ESM 还是 CommonJS?

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

@abele/sync-sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@abele/sync-sdk 可以商用吗?

@abele/sync-sdk 使用 GPL-3.0-only 许可。强 Copyleft:分发包含该代码的软件时,整个作品需以 GPL-3.0 兼容许可开源,并附带专利与反锁定条款。仅内部使用不触发此义务。以上为概括,不构成法律意见,请以许可原文为准。

@abele/sync-sdk 的下载量有多少?

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

@abele/sync-sdk 还在维护吗?

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