Connect SDK

Records and operations

Read, query, watch, create, update, move, and delete records through canonical mdbase envelopes and revision preconditions.

Operation envelopes

Record-facing operations return MdbaseOperationEnvelope<Result>. A successful envelope contains valid: true, the typed result, and any non-fatal diagnostics. Validation failures return diagnostics in the same envelope. Transport failures use exceptions.

unwrapOperation(envelope) returns the result or throws MdbaseOperationValidationError. Use it when the calling layer uses exceptions. Keep the raw envelope when diagnostics are part of the interface.

Read one path or query a collection

records.ts
const record = unwrapOperation(await connection.read({
  path: "tasks/release.md"
}));

const open = unwrapOperation(await connection.query({
  types: ["task"],
  where: 'status == "open" && due != null',
  order_by: [
    { field: "due", direction: "asc" }
  ],
  limit: 100
}));

Results contain effective frontmatter, raw frontmatter where applicable, matched types, file metadata, and a revision. CEL evaluates against effective values. Defaults and computed fields participate in evaluation; the source file remains unchanged.

Page large result sets against one snapshot

large-query.ts
for await (const page of connection.queryPages(
  { types: ["task"], where: 'status != "done"' },
  {
    firstPageSize: 100,
    pageSize: 1000,
    signal: abortController.signal,
    onProgress: ({ loaded }) => renderProgress(loaded)
  }
)) {
  appendRows(page.results);
}

queryPages() preserves the snapshot token returned by the first page and rejects a changed token. queryAll() provides the same behavior when holding the complete result set in memory is acceptable.

Create and update with explicit revisions

write.ts
const created = unwrapOperation(await connection.create({
  path: "tasks/release.md",
  type: "task",
  frontmatter: {
    type: "task",
    title: "Prepare release",
    status: "open"
  },
  body: "Release checklist."
}));

unwrapOperation(await connection.update({
  path: created.path,
  patch: { status: "done" },
  if_revision: created.revision
}));

patch is the canonical v0.3 partial-frontmatter update. A mismatched if_revision produces concurrent_modification. Refresh the record and ask the user or domain logic to resolve the conflict before submitting another write.

Resume changes by cursor

watch.ts
const controller = new AbortController();

for await (const change of connection.watch({
  signal: controller.signal,
  onStatus: (status) => renderWatchStatus(status)
})) {
  applyChange(change);
}

watch() uses the collection authority's resumable change cursor. The local change feed remains at the collection authority. The iterator reconnects transient routes by default and reports reset_required when the consumer must refresh a complete view.

Show authoritative impact before destructive work

rename.ts
const preview = unwrapOperation(await connection.preflightRename({
  from: "tasks/release.md",
  to: "archive/release.md",
  update_refs: true,
  if_revision: current.revision
}));

showImpact(preview.references_affected, preview.warnings);

await connection.renameWithProgress({
  from: preview.from,
  to: preview.to,
  update_refs: true,
  if_revision: current.revision
}, {
  preflight: preview,
  signal: controller.signal,
  onProgress: renderMutationProgress
});

Preflight calculates the canonical operation's impact and preserves both records and the change cursor. Reuse its result in renameWithProgress() or deleteWithProgress() so the confirmation shown to the user matches the operation that will run.

Encrypted local mutations have durable outcomes.

If the caller stops waiting after dispatch, inspect pendingMutation() and recover the exact connector receipt with resumePendingMutation(). Wait for that outcome before submitting a different mutation.