@abraca/dabra 1.9.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -232,6 +232,23 @@ declare class DocumentCache {
232
232
  }
233
233
  //#endregion
234
234
  //#region packages/provider/src/AbracadabraClient.d.ts
235
+ /**
236
+ * Reason classifications surfaced to `onAuthFailed`. Consumers can decide
237
+ * whether to silently re-register the keypair (`user_not_found`) or
238
+ * surface a hard block (`account_revoked`, `forbidden`).
239
+ */
240
+ type AuthFailureReason = "user_not_found" | "account_revoked" | "forbidden" | "unauthorized";
241
+ interface AuthFailureContext {
242
+ /** HTTP status code from the failed request. */
243
+ status: number;
244
+ /** Server-provided error message verbatim. */
245
+ message: string;
246
+ /** Best-guess classification from the message text. */
247
+ reason: AuthFailureReason;
248
+ /** Method + path of the failed request, useful for debugging. */
249
+ method: string;
250
+ path: string;
251
+ }
235
252
  interface AbracadabraClientConfig {
236
253
  /** Server base URL (http or https). WebSocket URL is derived automatically. */
237
254
  url: string;
@@ -250,6 +267,19 @@ interface AbracadabraClientConfig {
250
267
  * cache entries automatically.
251
268
  */
252
269
  cache?: DocumentCache;
270
+ /**
271
+ * Called whenever a REST call returns 401/403 with a recoverable reason
272
+ * (typically "user not found" — server's DB was wiped or repointed).
273
+ * Consumers wire this to their reauth path (in cou-sh: `_reauthFn`)
274
+ * so silently-failing background fetches still trigger key re-registration
275
+ * without waiting for the next WS action to surface the problem.
276
+ *
277
+ * The callback runs OUT-OF-BAND of the failing request — the original
278
+ * Promise still rejects so callers handle their own error path.
279
+ * Long-running auth handlers should debounce or short-circuit duplicate
280
+ * concurrent invocations.
281
+ */
282
+ onAuthFailed?: (ctx: AuthFailureContext) => void;
253
283
  }
254
284
  declare class AbracadabraClient {
255
285
  private _token;
@@ -258,6 +288,7 @@ declare class AbracadabraClient {
258
288
  private readonly storageKey;
259
289
  private readonly _fetch;
260
290
  readonly cache: DocumentCache | null;
291
+ private readonly _onAuthFailed;
261
292
  constructor(config: AbracadabraClientConfig);
262
293
  get token(): string | null;
263
294
  set token(value: string | null);
@@ -397,8 +428,89 @@ declare class AbracadabraClient {
397
428
  seq: number;
398
429
  data: Uint8Array;
399
430
  }[]>;
400
- /** Clear token from memory and storage. */
431
+ /**
432
+ * Clear token from memory and storage. Local-only; does NOT notify the
433
+ * server. Use {@link logoutServer} or {@link logoutAll} when you also
434
+ * want the JWT to land in the server's revocation cache.
435
+ */
401
436
  logout(): void;
437
+ /**
438
+ * Revoke the current JWT server-side and clear local state. Adds the
439
+ * token's `jti` to the server's revocation cache so subsequent requests
440
+ * with this token return 401, even if the JWT signature still verifies
441
+ * and `exp` hasn't passed. Safe to call when no token is set — degrades
442
+ * to a local clear.
443
+ *
444
+ * Network errors are swallowed (the local state is always cleared) so
445
+ * a sign-out flow can't get stuck on a flaky connection. The endpoint
446
+ * itself is idempotent.
447
+ */
448
+ logoutServer(): Promise<void>;
449
+ /**
450
+ * Bump the user's `tokens_invalid_before` watermark, revoking every
451
+ * outstanding JWT for this user — every device, every browser, every
452
+ * pending background tab. The current token is required (this is the
453
+ * user's "I am who I say I am" assertion). After the call returns,
454
+ * future authenticated requests with any pre-existing token return 401.
455
+ */
456
+ logoutAll(): Promise<void>;
457
+ /**
458
+ * Request an email verification message be sent to the current user's
459
+ * registered email address. Requires authentication. Server enforces a
460
+ * per-user rate limit (1/min, 10/day). Returns 404 if email
461
+ * verification is disabled in `[auth.email_verification]`.
462
+ */
463
+ requestEmailVerification(): Promise<void>;
464
+ /**
465
+ * Confirm an email verification token (typically opened from a link in
466
+ * the verification email). On success the server sets
467
+ * `users.email_verified_at`. No auth required — the token itself is
468
+ * the proof.
469
+ */
470
+ confirmEmailVerification(token: string): Promise<void>;
471
+ /**
472
+ * Initiate a password reset for a user identified by username or
473
+ * email. The server always returns 202 to avoid leaking whether the
474
+ * identifier exists; a real reset email is only sent if the lookup
475
+ * matched. Heavily rate-limited per-identifier and per-IP.
476
+ */
477
+ requestPasswordReset(opts: {
478
+ identifier: string;
479
+ }): Promise<void>;
480
+ /**
481
+ * Complete a password reset using the token from the reset email.
482
+ * On success the user's password is updated and every existing JWT for
483
+ * the account is invalidated (the `tokens_invalid_before` watermark
484
+ * bumps). The caller is NOT auto-logged-in — call {@link login} after.
485
+ */
486
+ confirmPasswordReset(opts: {
487
+ token: string;
488
+ newPassword: string;
489
+ }): Promise<void>;
490
+ /**
491
+ * Change the current user's password. Requires the current password —
492
+ * a stolen JWT alone can't pivot to "I now own this account forever"
493
+ * because the lockout counter (shared with `/auth/login`) trips after
494
+ * a few wrong tries. The endpoint also bumps `tokens_invalid_before`,
495
+ * so other sessions are forced to re-auth.
496
+ */
497
+ changePassword(opts: {
498
+ currentPassword: string;
499
+ newPassword: string;
500
+ }): Promise<void>;
501
+ /**
502
+ * Add a password to an account that doesn't have one yet — for example,
503
+ * a key-based soft identity opting into a recovery credential. Requires
504
+ * the caller to be authenticated. The server checks
505
+ * `users.password_hash IS NULL` and returns 409 if a password is already
506
+ * set — use {@link changePassword} in that case.
507
+ *
508
+ * Setting a password does not bump `tokens_invalid_before`: it adds an
509
+ * orthogonal credential rather than rotating an existing one. Other
510
+ * devices keep their sessions; this just unlocks the password-login
511
+ * code path for future logins on new devices.
512
+ */
513
+ setPassword(newPassword: string): Promise<void>;
402
514
  /** Get the current user's profile. */
403
515
  getMe(): Promise<UserProfile>;
404
516
  /** Update the current user's display name. */
@@ -413,13 +525,51 @@ declare class AbracadabraClient {
413
525
  getDoc(docId: string): Promise<DocumentMeta>;
414
526
  /** Delete a document (requires Owner role). Cascades to children and uploads. */
415
527
  deleteDoc(docId: string): Promise<void>;
416
- /** List immediate child documents. */
417
- listChildren(docId: string): Promise<string[]>;
418
- /** Create a child document under a parent (requires write permission). */
528
+ /**
529
+ * Restore a soft-deleted document (and its descendants). Requires the
530
+ * same `manage` permission as {@link deleteDoc}, and works even when
531
+ * the doc currently has `deleted_at` set — the cascade resolver walks
532
+ * the ancestor chain regardless of soft-delete state. Returns the
533
+ * number of restored rows in the audit log; the SDK call returns
534
+ * `void` because the wire response is 204.
535
+ */
536
+ restoreDoc(docId: string): Promise<void>;
537
+ /**
538
+ * Full-text search over document labels via `GET /docs/search`. The
539
+ * server filters each candidate hit through the cascade resolver, so
540
+ * results only include docs the caller can read at viewer or above.
541
+ * Anonymous callers are permitted but only see public docs.
542
+ *
543
+ * `limit` is clamped to `[1, 50]` server-side; the default is 20.
544
+ * Hits arrive in best-first order. `snippet` is HTML with `<mark>`
545
+ * markers around matched tokens — sanitize before injecting.
546
+ */
547
+ searchDocs(query: string, opts?: {
548
+ limit?: number;
549
+ }): Promise<DocSearchHit[]>;
550
+ /**
551
+ * List the direct children of a document, returning full metadata. Pass
552
+ * no argument to list the children of the server root — what the
553
+ * dashboard renders as the Spaces sidebar.
554
+ *
555
+ * The cache (when configured) stores the bare `id[]` topology used by
556
+ * recursive tree walks; callers that need it can read `meta.id` from
557
+ * the returned metas.
558
+ */
559
+ listChildren(parentId?: string): Promise<DocumentMeta[]>;
560
+ /**
561
+ * Create a child document under a parent (requires write permission).
562
+ *
563
+ * `kind` is the well-known tag (`Kind.Channel`, `Kind.Page`, etc.); the
564
+ * server stores it in `documents.kind` but does not enforce semantics.
565
+ * `description` is freeform metadata.
566
+ */
419
567
  createChild(docId: string, opts?: {
420
568
  child_id?: string;
421
569
  doc_type?: string;
422
570
  label?: string;
571
+ kind?: string;
572
+ description?: string;
423
573
  }): Promise<DocumentMeta>;
424
574
  /** Broadcast a stateless message to all connected clients on a document (requires manage permission). */
425
575
  broadcast(docId: string, payload: string): Promise<void>;
@@ -456,10 +606,6 @@ declare class AbracadabraClient {
456
606
  revokeInvite(code: string): Promise<void>;
457
607
  /** Redeem an invite code for the currently authenticated user. */
458
608
  redeemInvite(code: string): Promise<void>;
459
- /** List root documents (replaces listSpaces for new code). */
460
- listRootDocuments(): Promise<DocumentMeta[]>;
461
- /** Get the hub document, or null if none is configured. */
462
- getHubDocument(): Promise<DocumentMeta | null>;
463
609
  /** Set the public_access level for a document. Pass null to inherit from parent. */
464
610
  setDocumentAccess(docId: string, publicAccess: string | null): Promise<void>;
465
611
  /** Get the public_access info for a document. */
@@ -467,47 +613,77 @@ declare class AbracadabraClient {
467
613
  public_access: string | null;
468
614
  effective_public_access: string | null;
469
615
  }>;
470
- /** Update document metadata (label, description, is_hub). Requires manage permission. */
616
+ /** Update document metadata (label, description, kind). Requires manage permission. */
471
617
  updateDocumentMeta(docId: string, opts: {
472
- label?: string;
618
+ label?: string | null;
473
619
  description?: string | null;
474
- is_hub?: boolean;
620
+ kind?: string | null;
475
621
  }): Promise<void>;
476
622
  /**
477
- * List spaces visible to the caller. No auth required for public spaces.
478
- * @deprecated Use {@link listRootDocuments} instead.
479
- */
480
- listSpaces(): Promise<SpaceMeta[]>;
481
- /**
482
- * Get a single space by ID.
483
- * @deprecated Use {@link getDoc} instead.
623
+ * List Spaces visible to the caller top-level docs (children of the
624
+ * server root) tagged with `kind: "space"`. Authenticated users see
625
+ * spaces resolving to any role; anonymous users see public ones.
484
626
  */
485
- getSpace(spaceId: string): Promise<SpaceMeta>;
627
+ listSpaces(): Promise<DocumentMeta[]>;
486
628
  /**
487
- * Get the hub space, or null if none is configured.
488
- * @deprecated Use {@link getHubDocument} instead.
489
- */
490
- getHubSpace(): Promise<SpaceMeta | null>;
491
- /**
492
- * Create a new space (auth required).
493
- * @deprecated Use {@link createDoc} + {@link updateDocumentMeta} instead.
629
+ * Create a new top-level Space. Equivalent to a `POST /docs` with
630
+ * `kind: "space"` plus the supplied metadata in one round trip.
631
+ *
632
+ * `visibility: "public"` sets `public_access = "observer"` (anonymous
633
+ * read, no awareness or writes). `"private"` (the default) leaves
634
+ * `public_access` unset so only explicit grants apply.
494
635
  */
495
636
  createSpace(opts: {
496
637
  name: string;
497
638
  description?: string;
498
- visibility?: SpaceMeta["visibility"];
639
+ visibility?: "public" | "private";
499
640
  id?: string;
500
- }): Promise<SpaceMeta>;
641
+ }): Promise<DocumentMeta>;
501
642
  /**
502
- * Update an existing space (Owner or admin required).
503
- * @deprecated Use {@link updateDocumentMeta} + {@link setDocumentAccess} instead.
643
+ * Update a Space's metadata. `visibility` flips `public_access` between
644
+ * `"observer"` (public) and `"none"` (private). To leave visibility
645
+ * untouched, omit it. Pass any property as `null` to clear it.
504
646
  */
505
- updateSpace(spaceId: string, opts: Partial<Pick<SpaceMeta, "name" | "description" | "visibility" | "is_hub">>): Promise<SpaceMeta>;
647
+ updateSpace(docId: string, opts: {
648
+ name?: string | null;
649
+ description?: string | null;
650
+ visibility?: "public" | "private";
651
+ }): Promise<void>;
652
+ /** Delete a Space (and every doc nested under it). Requires manage permission. */
653
+ deleteSpace(docId: string): Promise<void>;
654
+ /**
655
+ * Look up the DM doc between the calling user and `otherUserPk`, or
656
+ * create it if none exists. The doc id is deterministically derived from
657
+ * the sorted pubkey pair (see {@link deriveDmDocId}) so both sides
658
+ * compute the same target — race-tolerant by construction. The created
659
+ * doc has `kind = "dm"`, `public_access = "none"`, and explicit Editor
660
+ * permissions for both participants.
661
+ *
662
+ * The cascade resolver enforces that no other user can read the DM,
663
+ * because:
664
+ * - `public_access = "none"` blocks anonymous + falls through to the
665
+ * server-wide `[access].authenticated` floor for everyone else;
666
+ * - the explicit Editor rows only exist for the two participants, so
667
+ * non-participants get only the (capped) authenticated floor;
668
+ * - the doc lives at server root, so there's no ancestor that could
669
+ * leak via a higher cascade grant.
670
+ *
671
+ * Note: when `[access].authenticated >= "viewer"` is set server-wide,
672
+ * non-participants would still be able to *read* the DM via the
673
+ * authenticated floor. That's the documented "private docs need
674
+ * authenticated=none" caveat from REDESIGN.md §9 — operators wanting
675
+ * sealed DMs configure the server accordingly.
676
+ *
677
+ * @param otherUserPk Base64url-encoded Ed25519 public key of the other party.
678
+ * @returns The DM doc's id (existing or newly created).
679
+ */
680
+ findOrCreateDmDoc(otherUserPk: string): Promise<string>;
506
681
  /**
507
- * Delete a space and its root document (Owner or admin required).
508
- * @deprecated Use {@link deleteDoc} instead.
682
+ * The reserved server root document id. Convenience accessor for the
683
+ * client; identical to {@link SERVER_ROOT_ID}. Use this as the parent for
684
+ * top-level docs / Spaces.
509
685
  */
510
- deleteSpace(spaceId: string): Promise<void>;
686
+ get rootDocId(): string;
511
687
  /** List all users (requires elevated role: admin or service). */
512
688
  adminListUsers(): Promise<{
513
689
  users: (UserProfile & {
@@ -528,13 +704,61 @@ declare class AbracadabraClient {
528
704
  refCountsRepaired: number;
529
705
  blobsSwept: number;
530
706
  }>;
707
+ /**
708
+ * Clear the lockout state on a user account: zeroes the failed-login
709
+ * counter and `locked_until`. Requires elevated role (Admin or
710
+ * Service). The action is recorded in the audit log under
711
+ * `admin.user_unlock`.
712
+ */
713
+ adminUnlockUser(userId: string): Promise<void>;
714
+ /**
715
+ * Page through the audit log. Filters AND-combine; `limit` defaults to
716
+ * 100 server-side. Requires elevated role.
717
+ */
718
+ adminAuditList(opts?: AuditQueryOpts): Promise<AuditLogEntry[]>;
719
+ /**
720
+ * Stream the audit log as NDJSON (one JSON object per line) for SIEM
721
+ * ingestion. Filters mirror {@link adminAuditList} minus `limit`/`offset`.
722
+ * The server pages internally so memory usage is bounded; this method
723
+ * buffers the full response into a string and is therefore best for
724
+ * moderate exports — large dumps should consume `/admin/audit/export`
725
+ * directly with a streaming HTTP client.
726
+ */
727
+ adminAuditExport(opts?: Omit<AuditQueryOpts, "limit" | "offset">): Promise<string>;
728
+ /**
729
+ * Verify the integrity of the audit-log hash chain. Returns the result
730
+ * unchanged: `status: "ok"` when the chain is intact, `status: "broken"`
731
+ * with a `break` payload identifying the first divergent row when
732
+ * tampering is detected. Wraps `GET /admin/audit/verify`. Requires
733
+ * elevated role.
734
+ *
735
+ * Note: the server returns HTTP 409 on a broken chain — this method
736
+ * special-cases the 409 status and returns the body as a successful
737
+ * result rather than throwing, because "broken" is a valid answer
738
+ * from the verifier, not an error.
739
+ */
740
+ adminAuditVerify(): Promise<AuditVerifyResult>;
741
+ /**
742
+ * Download a tar archive of the schema-meaningful tables (users,
743
+ * documents, permissions, invites, optionally audit log). Requires
744
+ * elevated role. The server gzips the response when the client sends
745
+ * `Accept-Encoding: gzip` — `fetch` handles that transparently, so
746
+ * the returned Blob is the raw tar bytes.
747
+ */
748
+ adminBackupDump(opts?: {
749
+ includeAudit?: boolean;
750
+ }): Promise<Blob>;
531
751
  /** List snapshot metadata for a document. */
532
752
  listSnapshots(docId: string, opts?: {
533
753
  limit?: number;
534
754
  offset?: number;
535
755
  }): Promise<SnapshotMeta[]>;
536
- /** Fetch a single snapshot including its base64-encoded data blob. */
537
- getSnapshot(docId: string, version: number): Promise<SnapshotData>;
756
+ /** Fetch a single snapshot including its base64-encoded data blob.
757
+ * Pass `{ include: "files" }` to also receive the joined upload list
758
+ * (each `fileBlock` / `coverUploadId` resolved against `uploads`). */
759
+ getSnapshot(docId: string, version: number, opts?: {
760
+ include?: "files" | string;
761
+ }): Promise<SnapshotData>;
538
762
  /** Create a manual snapshot of the current document state. */
539
763
  createSnapshot(docId: string, opts?: {
540
764
  label?: string;
@@ -548,7 +772,18 @@ declare class AbracadabraClient {
548
772
  /** Health check — no auth required. */
549
773
  health(): Promise<HealthStatus>;
550
774
  /**
551
- * Fetch server metadata including the optional `index_doc_id` entry point.
775
+ * Readiness probe pings the database. Returns 200 with
776
+ * `status: "ready"` only when the server can serve traffic, 503 with
777
+ * `status: "unready"` otherwise (load balancers / Kubernetes probes
778
+ * use the status code; the body is informational).
779
+ *
780
+ * The 503 case is special-cased here: instead of throwing, the method
781
+ * returns the unready body so callers can react to the state without
782
+ * try/catch boilerplate.
783
+ */
784
+ readyz(): Promise<ReadyzStatus>;
785
+ /**
786
+ * Fetch server metadata including `root_doc_id` and the `[access]` policy.
552
787
  * No auth required.
553
788
  */
554
789
  serverInfo(): Promise<ServerInfo>;
@@ -782,6 +1017,14 @@ interface AbracadabraProviderConfiguration extends Omit<AbracadabraBaseProviderC
782
1017
  * sharing one local store. Used for the identity doc.
783
1018
  */
784
1019
  serverAgnostic?: boolean;
1020
+ /**
1021
+ * Maximum number of simultaneously cached child providers before LRU
1022
+ * eviction reclaims the least-recently-used unpinned ones. Default 20.
1023
+ * Apps that legitimately need more docs alive (e.g. background-sync of
1024
+ * a working set) should raise this; pin individual children with
1025
+ * `pinChild()` to make them eviction-immune regardless of cap.
1026
+ */
1027
+ maxChildren?: number;
785
1028
  }
786
1029
  /**
787
1030
  * AbracadabraProvider extends AbracadabraBaseProvider with:
@@ -815,8 +1058,9 @@ declare class AbracadabraProvider extends AbracadabraBaseProvider {
815
1058
  private childAccessTimes;
816
1059
  /** Pinned children that must not be evicted (e.g. actively viewed docs) */
817
1060
  private pinnedChildren;
818
- /** Max simultaneously cached child providers before LRU eviction kicks in */
819
- private static readonly MAX_CHILDREN;
1061
+ /** Default cap on simultaneously cached child providers; configurable per-instance via `maxChildren`. */
1062
+ private static readonly DEFAULT_MAX_CHILDREN;
1063
+ private readonly maxChildren;
820
1064
  private abracadabraConfig;
821
1065
  private readonly boundHandleYSubdocsChange;
822
1066
  /**
@@ -937,7 +1181,7 @@ declare class AbracadabraProvider extends AbracadabraBaseProvider {
937
1181
  destroy(): void;
938
1182
  }
939
1183
  //#endregion
940
- //#region packages/provider/node_modules/lib0/encoding.d.ts
1184
+ //#region node_modules/lib0/encoding.d.ts
941
1185
  /**
942
1186
  * A BinaryEncoder handles the encoding to an Uint8Array.
943
1187
  */
@@ -981,7 +1225,7 @@ declare const Forbidden: CloseEvent;
981
1225
  */
982
1226
  declare const ConnectionTimeout: CloseEvent;
983
1227
  //#endregion
984
- //#region packages/provider/node_modules/lib0/decoding.d.ts
1228
+ //#region node_modules/lib0/decoding.d.ts
985
1229
  /**
986
1230
  * A Decoder handles the decoding of an Uint8Array.
987
1231
  * @template {ArrayBufferLike} [Buf=ArrayBufferLike]
@@ -1208,18 +1452,57 @@ interface UserProfile {
1208
1452
  role: string;
1209
1453
  /** Account-level Ed25519 public key (base64url). Canonical user identity. */
1210
1454
  publicKey: string | null;
1455
+ /**
1456
+ * Whether the user has a password set. `false` for key-based soft
1457
+ * identities until they opt in via {@link AbracadabraClient.setPassword}.
1458
+ * Drives the UI's "Set password" vs "Change password" branching.
1459
+ */
1460
+ hasPassword?: boolean;
1211
1461
  }
1212
1462
  interface DocumentMeta {
1213
1463
  id: string;
1214
1464
  parent_id: string | null;
1465
+ /**
1466
+ * Renderer hint — `"kanban"`, `"checklist"`, `"outline"`, `"graph"`,
1467
+ * `"sheet"`, `"mindmap"`, `"doc"`, etc. Drives the dashboard's choice of
1468
+ * Vue component for this doc. Orthogonal to {@link kind}.
1469
+ */
1215
1470
  doc_type?: string | null;
1216
1471
  label?: string | null;
1217
1472
  description?: string | null;
1218
1473
  public_access?: string | null;
1219
1474
  owner_id?: string | null;
1220
- is_hub?: boolean;
1475
+ /**
1476
+ * Structural role hint — `"server"` for the reserved server-root doc,
1477
+ * `"space"` for top-level workspace containers, `null`/absent otherwise.
1478
+ * Distinct from {@link doc_type}: a Space (`kind: "space"`) can contain
1479
+ * many Kanban boards (each `doc_type: "kanban"`).
1480
+ */
1481
+ kind?: string | null;
1221
1482
  updated_at?: number | null;
1222
1483
  }
1484
+ /**
1485
+ * Well-known structural roles for the {@link DocumentMeta.kind} field. These
1486
+ * are conventions only — the server treats `kind` as an opaque string. Use
1487
+ * these constants instead of string literals to avoid typos.
1488
+ */
1489
+ declare const Kind: {
1490
+ /** The reserved server root document. There is exactly one per server. */readonly Server: "server"; /** A top-level workspace container — what the dashboard calls a "Space". */
1491
+ readonly Space: "space"; /** A regular content page rendered by the editor. */
1492
+ readonly Page: "page"; /** A group chat container under a Space. */
1493
+ readonly Channel: "channel"; /** A direct-message container at the server root with two-member permissions. */
1494
+ readonly Dm: "dm"; /** A child of channel/dm holding the messages Y.Array. */
1495
+ readonly ChannelPeriod: "channel-period"; /** A per-user inbox doc holding inbox entries; child of server root. */
1496
+ readonly Inbox: "inbox";
1497
+ };
1498
+ type Kind = typeof Kind[keyof typeof Kind];
1499
+ /**
1500
+ * Hardcoded UUID of the reserved server root document. Every doc in the tree
1501
+ * chains up to this row, so a permission grant on this doc cascades into
1502
+ * every descendant. Mirrors the constant in the Rust crate
1503
+ * (`crate::types::SERVER_ROOT_ID_STR`).
1504
+ */
1505
+ declare const SERVER_ROOT_ID = "00000000-0000-0000-0000-000000000000";
1223
1506
  interface UploadMeta {
1224
1507
  id: string;
1225
1508
  doc_id: string;
@@ -1265,9 +1548,28 @@ interface SnapshotMeta {
1265
1548
  label?: string | null;
1266
1549
  created_by?: string | null;
1267
1550
  created_at: number;
1551
+ /** Number of uploads referenced by this snapshot's CRDT state.
1552
+ * Always 0 on servers older than the `snapshot_files` migration, or for
1553
+ * snapshots created before that migration ran (until the operator runs
1554
+ * `POST /admin/snapshots/backfill-refs`). */
1555
+ file_count?: number;
1556
+ /** Signed byte delta vs. the previous-version snapshot for this doc.
1557
+ * `null` for the first snapshot of a doc (or when the previous version
1558
+ * has been pruned). Lets the UI render `+12 KB` / `−3 KB`. */
1559
+ delta_bytes?: number | null;
1560
+ }
1561
+ interface SnapshotFileEntry {
1562
+ id: string;
1563
+ doc_id: string;
1564
+ filename: string;
1565
+ mime_type?: string | null;
1566
+ size?: number | null;
1268
1567
  }
1269
1568
  interface SnapshotData extends SnapshotMeta {
1270
1569
  data: string;
1570
+ /** Populated only when fetched with `?include=files`. Each entry is an
1571
+ * upload referenced by this snapshot's CRDT state. */
1572
+ files?: SnapshotFileEntry[];
1271
1573
  }
1272
1574
  interface SnapshotCreateResult {
1273
1575
  version: number;
@@ -1297,10 +1599,21 @@ interface ServerInfo {
1297
1599
  version?: string;
1298
1600
  /** Hocuspocus wire protocol version (currently 2). */
1299
1601
  protocol_version?: number;
1300
- /** Entry-point document ID advertised by the server, if configured. */
1301
- index_doc_id?: string;
1302
- /** Default role assigned to users without explicit permissions. */
1303
- default_role?: string;
1602
+ /**
1603
+ * The reserved server root document id. Every doc in the tree chains up
1604
+ * to this row; granting Admin here cascades to every doc. Hardcoded by
1605
+ * the server — clients can also use {@link SERVER_ROOT_ID} directly.
1606
+ */
1607
+ root_doc_id?: string;
1608
+ /**
1609
+ * Server-wide access policy. Drives client-side decisions like "show the
1610
+ * sign-up CTA?" (anonymous mode) or "warn before publishing?" (anonymous
1611
+ * grants).
1612
+ */
1613
+ access?: {
1614
+ /** `"none"` rejects unauthenticated requests; `"observer"` allows public read. */anonymous?: "none" | "observer"; /** Server-wide floor for authed users with no explicit grant. */
1615
+ authenticated?: "none" | "observer" | "viewer" | "editor";
1616
+ };
1304
1617
  /** Enabled auth methods (e.g. ["crypto", "jwt"]). */
1305
1618
  auth_methods?: string[];
1306
1619
  /** Whether open registration is enabled. */
@@ -1312,23 +1625,85 @@ interface ServerInfo {
1312
1625
  default_mode?: string;
1313
1626
  minimum_mode?: string;
1314
1627
  };
1628
+ /**
1629
+ * Ed25519 public key (base64url, no padding) the server uses to sign every
1630
+ * accepted `messages:*` record (`server_sig`). Clients can verify message
1631
+ * placement / ordering by checking each record's `server_sig` against this
1632
+ * key over the canonical `{ msg_id, period_id, ts, client_sig }` payload.
1633
+ */
1634
+ messages_signer_pubkey?: string;
1315
1635
  }
1316
1636
  interface SearchResult {
1317
1637
  docId: string;
1318
1638
  /** Number of matching trigrams — higher is better. */
1319
1639
  score: number;
1320
1640
  }
1321
- interface SpaceMeta {
1322
- id: string;
1641
+ /**
1642
+ * A single hit returned by the server's full-text search endpoint
1643
+ * (`GET /docs/search`). Distinct from {@link SearchResult}, which is the
1644
+ * client-side trigram index in `SearchIndex.ts`.
1645
+ *
1646
+ * `snippet` is server-rendered HTML with `<mark>` wrappers around the
1647
+ * matched tokens — safe to inject after standard sanitization. `rank` is
1648
+ * a backend-specific score (SQLite bm25 vs Postgres ts_rank); the only
1649
+ * guarantee is that hits arrive in best-first order.
1650
+ */
1651
+ interface DocSearchHit {
1323
1652
  doc_id: string;
1324
- name: string;
1325
- description: string | null;
1326
- visibility: "public" | "private" | "invite";
1327
- is_hub: boolean;
1328
- owner_id: string | null;
1329
- created_at: number;
1330
- updated_at: number;
1331
- public_access?: string | null;
1653
+ parent_id: string | null;
1654
+ label: string | null;
1655
+ kind: string | null;
1656
+ /** HTML-safe snippet with `<mark>` markers around the matched tokens. */
1657
+ snippet: string;
1658
+ rank: number;
1659
+ }
1660
+ /**
1661
+ * A single row from `GET /admin/audit`. Mirrors the server's audit log
1662
+ * row format (see `audit::AuditLogRow`). `metadata` is the parsed JSON
1663
+ * object the server stored, or `null` if the row had no metadata. Each
1664
+ * row carries an internal `prev_hash` / `row_hash` chain that admins
1665
+ * verify with {@link AuditVerifyResult}.
1666
+ */
1667
+ interface AuditLogEntry {
1668
+ id: string;
1669
+ ts: number;
1670
+ event_type: string;
1671
+ actor_user_id: string | null;
1672
+ actor_ip: string | null;
1673
+ request_id: string | null;
1674
+ target_type: string | null;
1675
+ target_id: string | null;
1676
+ metadata: Record<string, unknown> | null;
1677
+ outcome: string;
1678
+ }
1679
+ /** Filters for `GET /admin/audit`. All optional and AND-combined. */
1680
+ interface AuditQueryOpts {
1681
+ event_type?: string;
1682
+ actor_user_id?: string;
1683
+ target_type?: string;
1684
+ target_id?: string;
1685
+ since_ts?: number;
1686
+ until_ts?: number;
1687
+ limit?: number;
1688
+ offset?: number;
1689
+ }
1690
+ /** Response of `GET /admin/audit/verify`. `status: "broken"` carries `break`. */
1691
+ interface AuditVerifyResult {
1692
+ status: "ok" | "broken";
1693
+ message?: string;
1694
+ break?: {
1695
+ rows_checked: number;
1696
+ row_id: string;
1697
+ reason: string;
1698
+ };
1699
+ }
1700
+ /** Response of `GET /readyz`. HTTP 200 when ready, 503 when not. */
1701
+ interface ReadyzStatus {
1702
+ status: "ready" | "unready";
1703
+ version: string;
1704
+ checks: {
1705
+ database: "ok" | "error";
1706
+ };
1332
1707
  }
1333
1708
  interface InviteRow {
1334
1709
  code: string;
@@ -1567,6 +1942,193 @@ declare const HocuspocusProviderWebsocket: typeof AbracadabraWS;
1567
1942
  /** @deprecated Use AbracadabraWS */
1568
1943
  type HocuspocusProviderWebsocket = AbracadabraWS;
1569
1944
  //#endregion
1945
+ //#region packages/provider/src/RpcClient.d.ts
1946
+ /**
1947
+ * RPC v1 — request/response over `MSG_STATELESS`.
1948
+ *
1949
+ * Mirrors `docs/rpc-v1.md` in the abracadabra-rs server. Frames travel as
1950
+ * `MSG_STATELESS` payloads with the literal prefix `rpc:v1:` followed by a
1951
+ * JSON envelope. The provider's existing `sendStateless` / `stateless` event
1952
+ * stream is the only transport surface this layer touches; everything else
1953
+ * (state machine, deadlines, handler registry, dedupe of self-replies) lives
1954
+ * here.
1955
+ */
1956
+ declare const RPC_PREFIX = "rpc:v1:";
1957
+ type RpcKind = "req" | "ack" | "nack" | "progress" | "result" | "cancel" | "hb" | "register" | "unregister";
1958
+ interface RpcFrame {
1959
+ kind: RpcKind;
1960
+ id: string;
1961
+ ts?: number;
1962
+ from?: string;
1963
+ to?: string;
1964
+ method?: string;
1965
+ args?: unknown;
1966
+ deadline_ms?: number;
1967
+ data?: unknown;
1968
+ error?: RpcErrorPayload;
1969
+ by?: "server" | "runner";
1970
+ runner_id?: string;
1971
+ seq?: number;
1972
+ methods?: string[];
1973
+ }
1974
+ interface RpcErrorPayload {
1975
+ code: string;
1976
+ message: string;
1977
+ details?: unknown;
1978
+ }
1979
+ type RpcErrorCode = "NO_HANDLER" | "HANDLER_GONE" | "TIMEOUT" | "CANCELLED" | "RATE_LIMITED" | "UNAUTHORIZED" | "SCHEMA" | "INTERNAL" | "APP" | string;
1980
+ /** Thrown by `rpc.call(...)` on terminal nack / error / timeout. */
1981
+ declare class RpcError extends Error {
1982
+ code: RpcErrorCode;
1983
+ details?: unknown;
1984
+ constructor(code: RpcErrorCode, message: string, details?: unknown);
1985
+ }
1986
+ /**
1987
+ * Minimal provider surface RpcClient needs. The base provider already
1988
+ * satisfies it; the indirection is here so handlers can be tested against
1989
+ * a stub without booting a Y.Doc.
1990
+ */
1991
+ interface RpcTransport {
1992
+ sendStateless(payload: string): void;
1993
+ on(event: string, fn: Function): unknown;
1994
+ off(event: string, fn?: Function): unknown;
1995
+ }
1996
+ type RpcTarget = {
1997
+ kind: "role";
1998
+ role: "service";
1999
+ } | {
2000
+ kind: "user";
2001
+ userId: string;
2002
+ } | {
2003
+ kind: "runner";
2004
+ sessionId: string;
2005
+ };
2006
+ interface RpcCallOptions {
2007
+ /** Defaults to `{ kind: "role", role: "service" }`. */
2008
+ target?: RpcTarget;
2009
+ /** Wall-clock deadline in ms. Server clamps to its own ceiling. */
2010
+ deadline?: number;
2011
+ /** Abort the call (sends `rpc:cancel` to the runner). */
2012
+ signal?: AbortSignal;
2013
+ /** Fired when a runner claims the call (after the server ack). */
2014
+ onClaim?: (runnerId: string) => void;
2015
+ /** Fired for every `rpc:progress` frame the runner emits. */
2016
+ onProgress?: (data: unknown, seq: number) => void;
2017
+ }
2018
+ interface RpcCallHandle<T = unknown> extends Promise<T> {
2019
+ /** RPC id (UUID v7) — useful for log correlation. */
2020
+ readonly id: string;
2021
+ /** Runner session id, populated once a runner has claimed the call. */
2022
+ readonly claimedBy: string | undefined;
2023
+ /** Cancel the call. Equivalent to `signal.abort()` if a signal was passed. */
2024
+ cancel(reason?: string): void;
2025
+ }
2026
+ /**
2027
+ * Handler signature for a runner-side RPC handler.
2028
+ *
2029
+ * Returning a value emits `rpc:result(ok)`. Throwing emits
2030
+ * `rpc:result(err)` — `RpcError` instances pass through their `code` and
2031
+ * `details`; any other thrown value becomes `INTERNAL`.
2032
+ *
2033
+ * Returning an async generator turns each `yield` into a `rpc:progress`
2034
+ * frame (with monotonic `seq`); the generator's return value is the final
2035
+ * `result.data`.
2036
+ */
2037
+ type RpcHandler = (args: unknown, ctx: RpcHandlerContext) => unknown | Promise<unknown> | AsyncGenerator<unknown, unknown, void>;
2038
+ interface RpcHandlerContext {
2039
+ id: string;
2040
+ method: string;
2041
+ /** Server-stamped caller user id. Trusted. */
2042
+ from: string;
2043
+ /** Aborted when the caller cancels or the deadline is reached. */
2044
+ signal: AbortSignal;
2045
+ }
2046
+ /**
2047
+ * Per-provider RPC layer. Construct one and attach by passing the provider
2048
+ * (or any `RpcTransport`). Disposes on `destroy()`.
2049
+ *
2050
+ * Caller side: `rpc.call('ai.summarize@1', { docId }, { onProgress })`.
2051
+ *
2052
+ * Runner side: `rpc.handle('ai.summarize@1', async (args, ctx) => …)`.
2053
+ */
2054
+ declare class RpcClient extends EventEmitter {
2055
+ private readonly transport;
2056
+ private readonly pending;
2057
+ private readonly handlers;
2058
+ /** Tracks live runner-side invocations so we can route `rpc:cancel`. */
2059
+ private readonly runningHandlers;
2060
+ /** Pending register frames awaiting server ack — resolved by id. */
2061
+ private readonly pendingRegistrations;
2062
+ /** Grace timer armed on `disconnect`; cancelled on `connect`. Fires
2063
+ * `HANDLER_GONE` on every pending RPC if the WS is still down after
2064
+ * the grace window. v1 used to wait `deadline + 5s` which could be
2065
+ * 35s on default deadlines — way too long for a definitively-dead
2066
+ * connection. */
2067
+ private disconnectGrace;
2068
+ private static readonly DISCONNECT_GRACE_MS;
2069
+ private readonly onStatelessBound;
2070
+ /**
2071
+ * Replays all live `register` frames whenever the underlying provider
2072
+ * (re)syncs — covers WebSocket reconnects where the server allocated a
2073
+ * new session id and the old capability entries were freed.
2074
+ */
2075
+ private readonly onSyncedBound;
2076
+ private readonly onDisconnectBound;
2077
+ private readonly onConnectBound;
2078
+ private destroyed;
2079
+ constructor(transport: RpcTransport);
2080
+ private armDisconnectGrace;
2081
+ private cancelDisconnectGrace;
2082
+ /**
2083
+ * Called when the WS has been down for the full grace window. Every
2084
+ * in-flight call is dead from the server's perspective (its `close_session`
2085
+ * already fired `HANDLER_GONE` to a now-disconnected caller; on reconnect
2086
+ * the server-side session is fresh, so any cached cancel/result frames
2087
+ * the server tried to deliver were lost). Fail pending immediately.
2088
+ */
2089
+ private failPendingOnDisconnect;
2090
+ /**
2091
+ * Re-emit a `register` frame for every locally-known handler. Called on
2092
+ * every `synced` event from the provider, since a reconnected WS gets a
2093
+ * fresh server-side session with empty capability state.
2094
+ */
2095
+ private replayRegistrations;
2096
+ destroy(): void;
2097
+ /**
2098
+ * Send an RPC and return a handle that resolves with the runner's result.
2099
+ * Throws `RpcError` on nack / handler error / timeout / cancellation.
2100
+ */
2101
+ call<T = unknown>(method: string, args?: unknown, options?: RpcCallOptions): RpcCallHandle<T>;
2102
+ private abortInFlight;
2103
+ /**
2104
+ * Register a handler for `method` and advertise it to the server so
2105
+ * incoming `to: role:service` calls resolve here. Returns an unsubscribe
2106
+ * function. Calling `register` twice for the same method replaces the
2107
+ * handler and re-advertises.
2108
+ *
2109
+ * The advertise is fire-and-forget; if you need to wait until the server
2110
+ * has acknowledged registration (typical in tests with a separate caller
2111
+ * connection), `await rpc.ready()` after registering all handlers.
2112
+ */
2113
+ handle(method: string, handler: RpcHandler): () => void;
2114
+ /**
2115
+ * Resolve once every outstanding `register` frame has been acknowledged
2116
+ * by the server. Useful when a separate connection is about to issue
2117
+ * `rpc.call(...)` and you need the capability registry to be live first.
2118
+ * Resolves immediately if there's nothing pending.
2119
+ */
2120
+ ready(): Promise<void>;
2121
+ private send;
2122
+ private receive;
2123
+ private onAck;
2124
+ private onProgress;
2125
+ private onResult;
2126
+ private onNack;
2127
+ private settle;
2128
+ private onReq;
2129
+ private onCancel;
2130
+ }
2131
+ //#endregion
1570
2132
  //#region packages/provider/src/AbracadabraBaseProvider.d.ts
1571
2133
  type AbracadabraBaseProviderConfiguration = Required<Pick<CompleteAbracadabraBaseProviderConfiguration, "name">> & Partial<CompleteAbracadabraBaseProviderConfiguration> & ((Required<Pick<CompleteAbracadabraWSConfiguration, "url">> & Partial<Pick<CompleteAbracadabraWSConfiguration, "preserveTrailingSlash">>) | Required<Pick<CompleteAbracadabraBaseProviderConfiguration, "websocketProvider">>);
1572
2134
  /** @deprecated Use AbracadabraBaseProviderConfiguration */
@@ -1637,6 +2199,13 @@ declare class AbracadabraBaseProvider extends EventEmitter {
1637
2199
  intervals: {
1638
2200
  forceSync: ReturnType<typeof setInterval> | null;
1639
2201
  };
2202
+ /**
2203
+ * Lazily-constructed RPC v1 client. See `RpcClient`/`docs/rpc-v1.md`.
2204
+ * Bound to this provider's stateless transport, so calls and handlers
2205
+ * ride the doc this provider is subscribed to.
2206
+ */
2207
+ private _rpc;
2208
+ get rpc(): RpcClient;
1640
2209
  constructor(configuration: AbracadabraBaseProviderConfiguration);
1641
2210
  boundDocumentUpdateHandler: (update: Uint8Array, origin: any) => void;
1642
2211
  boundAwarenessUpdateHandler: ({
@@ -1732,7 +2301,7 @@ declare const awarenessStatesToArray: (states: Map<number, Record<string, any>>)
1732
2301
  declare class SubdocMessage extends OutgoingMessage {
1733
2302
  type: MessageType;
1734
2303
  description: string;
1735
- get(args: Partial<OutgoingMessageArguments>): Encoder;
2304
+ get(args: Partial<AbracadabraOutgoingMessageArguments>): Encoder;
1736
2305
  }
1737
2306
  //#endregion
1738
2307
  //#region packages/provider/src/SearchIndex.d.ts
@@ -1986,18 +2555,15 @@ declare function makeEncryptedYText(ydoc: Y.Doc, fieldName: string, docKey: Cryp
1986
2555
  * Attach an observer that writes `updatedAt` to the root doc-tree entry for
1987
2556
  * `childDocId` whenever the child doc receives a non-offline update.
1988
2557
  *
1989
- * Writes are throttled: the first qualifying update records the timestamp;
1990
- * a trailing-edge timer flushes it to the tree map after `throttleMs`.
1991
- *
1992
2558
  * @param treeMap The root doc's "doc-tree" Y.Map.
1993
2559
  * @param childDocId The child document's UUID (key in treeMap).
1994
2560
  * @param childDoc The child Y.Doc to observe.
1995
2561
  * @param offlineStore The child provider's OfflineStore (used to detect
1996
2562
  * offline-replay origins and skip them). Pass null when
1997
2563
  * the offline store is disabled.
1998
- * @param options Optional config. `throttleMs` controls the write
1999
- * interval (default 5000).
2000
- * @returns Cleanup function — call on provider destroy. Flushes
2564
+ * @param options Optional config. `throttleMs` controls the throttle
2565
+ * window (default 5000).
2566
+ * @returns Cleanup function — call on provider destroy. Flushes
2001
2567
  * any pending write before detaching.
2002
2568
  */
2003
2569
  declare function attachUpdatedAtObserver(treeMap: Y.Map<any>, childDocId: string, childDoc: Y.Doc, offlineStore: OfflineStore | null, options?: {
@@ -2375,6 +2941,10 @@ declare class FileTransferHandle extends EventEmitter {
2375
2941
  _setProgress(p: number): void;
2376
2942
  /** @internal */
2377
2943
  _setStatus(s: FileTransferStatus): void;
2944
+ /** @internal — public alias for the protected emit so the parent
2945
+ * `FileTransferChannel` (a different class instance) can dispatch events
2946
+ * on this handle. Protected access is by-class, not by-hierarchy. */
2947
+ _emit(event: string, ...args: unknown[]): void;
2378
2948
  }
2379
2949
  /**
2380
2950
  * Chunked binary file transfer over a dedicated WebRTC data channel.
@@ -2988,7 +3558,6 @@ declare class DeviceRegistrationService {
2988
3558
  //#region packages/provider/src/ChatClient.d.ts
2989
3559
  /**
2990
3560
  * Minimal provider surface ChatClient needs. Matches `AbracadabraBaseProvider`.
2991
- * Kept as an interface so consumers can pass any compatible transport.
2992
3561
  */
2993
3562
  interface ChatClientTransport {
2994
3563
  sendStateless(payload: string): void;
@@ -2996,98 +3565,305 @@ interface ChatClientTransport {
2996
3565
  off(event: string, fn?: Function): unknown;
2997
3566
  }
2998
3567
  /**
2999
- * Typed client for the Abracadabra chat feature.
3568
+ * Send a chat message into a channel doc.
3000
3569
  *
3001
- * Wraps a connected provider (or base provider) and translates JSON envelopes
3002
- * on the stateless channel into typed method calls and events.
3570
+ * `channel_doc_id` is the UUID of the channel/dm doc `documents.id` for
3571
+ * a row with `kind = "channel"` or `kind = "dm"`. Group channels under a
3572
+ * Space pass the channel doc's id; DMs pass the DM doc's id (callers
3573
+ * resolve "the DM doc between user A and user B" via their own path —
3574
+ * typically `client.listChildren(serverRootId)` filtered by `kind === "dm"`
3575
+ * with both pubkeys in `permissions`).
3576
+ */
3577
+ interface SendMessageInput {
3578
+ channel_doc_id: string;
3579
+ content: string;
3580
+ /** Cleartext recipient pubkeys for server-side mention fan-out. */
3581
+ mentions?: string[];
3582
+ /** Message id this is a reply to. */
3583
+ reply_to?: string;
3584
+ /**
3585
+ * Optional Ed25519 signature over the canonical envelope, base64url. When
3586
+ * server config `[messages].require_client_sig = true`, sends without a
3587
+ * sig are rejected. Default-config servers accept unsigned sends — the
3588
+ * threat model relies on JWT-bound session auth + the server's own
3589
+ * `server_sig` over `(msg_id, period_id, ts, sig)`.
3590
+ */
3591
+ sig?: string;
3592
+ /** Client-side timestamp (ms) for skew-tolerance check. Optional. */
3593
+ ts_hint?: number;
3594
+ }
3595
+ interface SendMessageResult {
3596
+ message_id: string;
3597
+ period_id: string;
3598
+ ts: number;
3599
+ server_sig: string;
3600
+ }
3601
+ interface EditMessageInput {
3602
+ channel_doc_id: string;
3603
+ message_id: string;
3604
+ content: string;
3605
+ sig?: string;
3606
+ }
3607
+ interface DeleteMessageInput {
3608
+ channel_doc_id: string;
3609
+ message_id: string;
3610
+ }
3611
+ interface MarkReadInput {
3612
+ channel_doc_id: string;
3613
+ period_id: string;
3614
+ message_id: string;
3615
+ /** Unix milliseconds. Defaults to server time. */
3616
+ ts?: number;
3617
+ }
3618
+ /**
3619
+ * Thin façade over the `messages:*` stateless protocol.
3620
+ *
3621
+ * The unified messages model stores chat history as Y.Array entries on
3622
+ * `kind = "channel-period"` child docs of channel/dm docs (and notifications
3623
+ * as entries on a per-user `kind = "inbox"` doc). Reading history, listing
3624
+ * channels, observing real-time messages, and tracking read cursors are all
3625
+ * Y.Doc-shaped operations that the SDK consumer performs via
3626
+ * `AbracadabraClient` + `AbracadabraProvider` directly — not through this
3627
+ * façade. ChatClient handles only the *write-and-validate* path (sends,
3628
+ * edits, deletes, typing, mark-read), where the server is the sole authority
3629
+ * over the channel-period Y.Array.
3003
3630
  *
3004
3631
  * Events emitted:
3005
- * - `message` ChatMessage (new message broadcast)
3006
- * - `typing` → ChatTypingEvent (typing indicator broadcast)
3007
- * - `readReceipt` → ChatReadReceipt (mark_read broadcast)
3632
+ * - `typing` ChatTypingEvent (typing broadcast on the channel doc)
3633
+ *
3634
+ * For incoming-message observation, the consumer opens an
3635
+ * `AbracadabraProvider` on the active period doc and observes its
3636
+ * `messages` Y.Array directly. The dashboard's `useChat` composable does
3637
+ * this; the integration tests do this; external consumers should mirror
3638
+ * the pattern.
3008
3639
  */
3009
3640
  declare class ChatClient extends EventEmitter {
3010
3641
  private readonly provider;
3011
3642
  private readonly responseTimeoutMs;
3012
3643
  private readonly pending;
3013
3644
  private readonly boundOnStateless;
3645
+ private readonly boundOnServerError;
3014
3646
  constructor(provider: ChatClientTransport, options?: {
3015
3647
  responseTimeoutMs?: number;
3016
3648
  });
3017
- /** Stop listening for chat messages. Does not disconnect the underlying provider. */
3018
3649
  destroy(): void;
3019
- /** Send a chat message on a channel. Fire-and-forget (the server broadcasts). */
3020
- sendMessage(input: SendChatMessageInput): void;
3021
- /** Fetch historical messages for a channel. Resolves with the server response. */
3022
- getHistory(input: GetChatHistoryInput): Promise<{
3023
- channel: string;
3024
- messages: ChatMessage[];
3025
- }>;
3026
- /** Broadcast a typing indicator on a channel. */
3027
- sendTyping(channel: string): void;
3028
- /** List the current user's channels (ordered by last activity). */
3029
- listChannels(): Promise<{
3030
- channels: ChatChannel[];
3031
- }>;
3032
- /** Mark a channel read up to `timestamp` (unix ms). */
3033
- markRead(channel: string, timestamp: number): void;
3034
- /** Fetch per-user read cursors for a channel. */
3035
- getReadCursors(channel: string): Promise<{
3036
- channel: string;
3037
- cursors: ChatReadCursor[];
3038
- }>;
3039
- onMessage(fn: (m: ChatMessage) => void): this;
3650
+ /** Send a chat message. Resolves with the server-assigned id, ts, and signatures. */
3651
+ send(input: SendMessageInput): Promise<SendMessageResult>;
3652
+ /** Edit a previously-sent message. Append-only server records an `edit` entry. */
3653
+ edit(input: EditMessageInput): Promise<void>;
3654
+ /** Delete a message. Append-only — server records a `tombstone` entry. */
3655
+ delete(input: DeleteMessageInput): Promise<void>;
3656
+ /** Broadcast a typing indicator on a channel. Fire-and-forget. */
3657
+ typing(channel_doc_id: string): void;
3658
+ /** Mark a (period, message) position as read for the calling user. Fire-and-forget. */
3659
+ markRead(input: MarkReadInput): void;
3660
+ /** Observe typing broadcasts. */
3040
3661
  onTyping(fn: (e: ChatTypingEvent) => void): this;
3041
- onReadReceipt(fn: (e: ChatReadReceipt) => void): this;
3042
3662
  private enqueue;
3043
3663
  private removePending;
3044
3664
  private resolveNext;
3665
+ private rejectNext;
3045
3666
  private handleStateless;
3667
+ private handleServerError;
3668
+ }
3669
+ //#endregion
3670
+ //#region packages/provider/src/EncryptedChatClient.d.ts
3671
+ /**
3672
+ * Returns true if `content` is wrapped by this module (i.e. an `e2e:1:`
3673
+ * envelope). Cleartext content always returns false.
3674
+ */
3675
+ declare function isEncryptedContent(content: string): boolean;
3676
+ /**
3677
+ * Decrypt an `e2e:1:` envelope back to cleartext using the channel's
3678
+ * DocKey. Returns null if the envelope is malformed (don't surface
3679
+ * partial decryption — show the user a placeholder instead).
3680
+ */
3681
+ declare function decryptChatContent(content: string, docKey: CryptoKey): Promise<string | null>;
3682
+ /**
3683
+ * Encrypt cleartext into an `e2e:1:` envelope ready to send as the
3684
+ * stateless `messages:send` `content` field.
3685
+ */
3686
+ declare function encryptChatContent(content: string, docKey: CryptoKey): Promise<string>;
3687
+ /**
3688
+ * Per-channel encryption-mode + DocKey resolver. Caches the encryption
3689
+ * mode lookup for `cacheTtlMs`; the DocKey itself is cached inside
3690
+ * `DocKeyManager`. Returns `null` for non-E2E channels.
3691
+ */
3692
+ declare class ChannelKeyResolver {
3693
+ private readonly client;
3694
+ private readonly docKeys;
3695
+ private readonly keystore;
3696
+ private readonly options;
3697
+ private modeCache;
3698
+ private static readonly DEFAULT_TTL_MS;
3699
+ constructor(client: AbracadabraClient, docKeys: DocKeyManager, keystore: CryptoIdentityKeystore, options?: {
3700
+ cacheTtlMs?: number;
3701
+ });
3702
+ modeFor(channelDocId: string): Promise<"none" | "cse" | "e2e">;
3703
+ /**
3704
+ * Returns the DocKey for an E2E channel, or null for cleartext channels
3705
+ * (no envelope to fetch). Throws if the channel is E2E but no envelope
3706
+ * exists for the current user — the caller should let the send fail
3707
+ * loudly rather than fall back to cleartext, which would silently
3708
+ * downgrade the threat model.
3709
+ */
3710
+ docKeyFor(channelDocId: string): Promise<CryptoKey | null>;
3711
+ /** Drop the cached encryption mode for `channelDocId` (or all). */
3712
+ clearCache(channelDocId?: string): void;
3713
+ }
3714
+ /**
3715
+ * Drop-in wrapper around an existing ChatClient that intercepts the
3716
+ * write side (`send` / `edit`) and encrypts content for E2E channels.
3717
+ * Reading is handled separately — the consumer pumps the period's Y.Array
3718
+ * directly and calls `decryptChatContent` per record.
3719
+ *
3720
+ * Channels in `none` or `cse` mode are passed through untouched.
3721
+ */
3722
+ declare class EncryptedChatClient {
3723
+ private readonly inner;
3724
+ private readonly resolver;
3725
+ constructor(inner: ChatClient, resolver: ChannelKeyResolver);
3726
+ send(input: SendMessageInput): Promise<SendMessageResult>;
3727
+ edit(input: EditMessageInput): Promise<void>;
3728
+ delete(...args: Parameters<ChatClient["delete"]>): Promise<void>;
3729
+ markRead(...args: Parameters<ChatClient["markRead"]>): void;
3730
+ typing(...args: Parameters<ChatClient["typing"]>): void;
3731
+ destroy(): void;
3732
+ }
3733
+ //#endregion
3734
+ //#region packages/provider/src/messageRecord.d.ts
3735
+ /**
3736
+ * Decode + fold message records from a period doc's `messages` Y.Array
3737
+ * into the flat `ChatMessage[]` shape the dashboard renders.
3738
+ *
3739
+ * The server appends three kinds of records to the array (see
3740
+ * `abracadabra-rs/crates/abracadabra/src/messages.rs`):
3741
+ *
3742
+ * - `record_kind: "message"` — a real message (default kind).
3743
+ * - `record_kind: "edit"` — an updated version of `target_id`.
3744
+ * - `record_kind: "tombstone"` — `target_id` is deleted.
3745
+ *
3746
+ * Folding rules:
3747
+ * - For each `target_id`, apply edits in `ts` order; the *last* edit's
3748
+ * content wins. The original message's id, sender_id, and ts stay.
3749
+ * `editedAt` is the latest edit's ts.
3750
+ * - Any target_id with a tombstone is dropped from the output entirely.
3751
+ * - Records without record_kind default to "message" (back-compat).
3752
+ *
3753
+ * Cross-period folding: edits and tombstones can in principle target a
3754
+ * message in a *prior* period (e.g. user edits a message that lived on
3755
+ * the previous period after rollover). Callers that paginate across
3756
+ * periods should fold the joined list, not each period independently.
3757
+ */
3758
+ interface MessageRecord {
3759
+ record_kind?: 'message' | 'edit' | 'tombstone';
3760
+ id: string;
3761
+ target_id?: string;
3762
+ sender_id: string;
3763
+ channel_doc_id: string;
3764
+ period_id: string;
3765
+ ts: number;
3766
+ content: string;
3767
+ mentions?: string[];
3768
+ reply_to?: string | null;
3769
+ sig?: string;
3770
+ server_sig?: string;
3046
3771
  }
3772
+ interface FoldedMessage {
3773
+ id: string;
3774
+ channel: string;
3775
+ senderId: string;
3776
+ senderName?: string;
3777
+ content: string;
3778
+ createdAt: number;
3779
+ /** Set if the message has been edited at least once. */
3780
+ editedAt?: number;
3781
+ /** Reply target message id, if any. */
3782
+ replyTo?: string;
3783
+ }
3784
+ /**
3785
+ * Fold an ordered list of records (oldest-first) into the visible
3786
+ * messages a UI should render. Records may come from one period or
3787
+ * across multiple periods concatenated in time order.
3788
+ */
3789
+ declare function foldRecords(records: MessageRecord[]): FoldedMessage[];
3790
+ /**
3791
+ * Decode whatever the Y.Array stored for an entry into a MessageRecord.
3792
+ * Note: the server stamps `ts` server-side via `Any::BigInt(now_ts_ms())`,
3793
+ * so on the wire it round-trips as a JS BigInt — `typeof` is `'bigint'`,
3794
+ * not `'number'`. Accept both and coerce.
3795
+ */
3796
+ declare function recordFromYAny(any: unknown): MessageRecord | null;
3047
3797
  //#endregion
3048
3798
  //#region packages/provider/src/NotificationsClient.d.ts
3799
+ /** Per-user inbox doc entry. Mirrors `InboxEntry` on the server. */
3800
+ interface InboxEntry {
3801
+ id: string;
3802
+ /** "mention" | "reply" | "dm" | "system". */
3803
+ kind: string;
3804
+ channel_doc_id: string;
3805
+ message_id?: string | null;
3806
+ sender_id?: string | null;
3807
+ sender_name?: string | null;
3808
+ /** Empty for E2E channels (server can't read body). Filled otherwise. */
3809
+ preview?: string | null;
3810
+ ts: number;
3811
+ /** Synthesized client-side from the inbox doc's `read` Y.Map. */
3812
+ read: boolean;
3813
+ }
3814
+ interface FetchInboxInput {
3815
+ before?: number;
3816
+ limit?: number;
3817
+ unread_only?: boolean;
3818
+ }
3819
+ interface MarkReadInput$1 {
3820
+ id?: string;
3821
+ ids?: string[];
3822
+ source_id?: string;
3823
+ all?: boolean;
3824
+ }
3049
3825
  /**
3050
- * Typed client for the Abracadabra notifications feature.
3826
+ * Thin façade over the `messages:inbox_*` stateless protocol.
3051
3827
  *
3052
- * Emits:
3053
- * - `new` → NotificationRecord (incoming notify:new broadcast)
3054
- * - `readUpdate` NotificationReadUpdate (broadcast after mark_read / mark_all_read)
3828
+ * Per-user notifications live as Y.Array entries on the user's inbox doc
3829
+ * (`kind = "inbox"`, owned by the user, child of the server root, server-only
3830
+ * writer). Real-time observation of new entries is a Y.Array observer the
3831
+ * consumer attaches via `AbracadabraProvider` on the inbox doc.
3832
+ *
3833
+ * This client handles only the request/response layer:
3834
+ * - `fetch()` issues `messages:inbox_fetch`, returns the entry list.
3835
+ * - `markRead()` issues `messages:inbox_mark_read` (id / ids / source_id /
3836
+ * all variants).
3837
+ *
3838
+ * For incoming notification observation, open an AbracadabraProvider on the
3839
+ * inbox doc and observe its `entries` Y.Array directly. The dashboard's
3840
+ * `useNotifications` composable does this.
3055
3841
  */
3056
3842
  declare class NotificationsClient extends EventEmitter {
3057
3843
  private readonly provider;
3058
3844
  private readonly responseTimeoutMs;
3059
3845
  private readonly pending;
3060
3846
  private readonly boundOnStateless;
3847
+ private readonly boundOnServerError;
3061
3848
  constructor(provider: ChatClientTransport, options?: {
3062
3849
  responseTimeoutMs?: number;
3063
3850
  });
3064
3851
  destroy(): void;
3852
+ /** Fetch a slice of the calling user's inbox. Resolves with the entries. */
3853
+ fetch(input?: FetchInboxInput): Promise<{
3854
+ entries: InboxEntry[];
3855
+ }>;
3065
3856
  /**
3066
- * Create a notification targeting a specific recipient. Requires elevated role
3067
- * (service or admin); a `server:error` event with code `forbidden` is emitted
3068
- * by the underlying provider if the caller lacks permission.
3857
+ * Mark inbox entries read. One of `id`, `ids`, `source_id`, or `all`.
3858
+ * Returns when the server has acked.
3069
3859
  */
3070
- create(input: CreateNotificationInput): void;
3071
- /** Fetch notification history for the current user. */
3072
- fetch(input?: FetchNotificationsInput): Promise<{
3073
- notifications: NotificationRecord[];
3074
- }>;
3075
- /** Mark a single notification, or a batch, as read. */
3076
- markRead(target: {
3077
- id: string;
3078
- } | {
3079
- ids: string[];
3080
- }): void;
3081
- /** Mark every notification for the current user as read. */
3082
- markAllRead(): void;
3083
- /** Mark all notifications generated by the given source (e.g. a chat channel) as read. */
3084
- markReadBySource(sourceId: string): void;
3085
- onNew(fn: (n: NotificationRecord) => void): this;
3086
- onReadUpdate(fn: (u: NotificationReadUpdate) => void): this;
3860
+ markRead(input: MarkReadInput$1): Promise<void>;
3087
3861
  private enqueue;
3088
3862
  private removePending;
3089
3863
  private resolveNext;
3864
+ private rejectNext;
3090
3865
  private handleStateless;
3866
+ private handleServerError;
3091
3867
  }
3092
3868
  //#endregion
3093
3869
  //#region node_modules/@scure/bip39/esm/wordlists/english.d.ts
@@ -3157,4 +3933,536 @@ declare function wrapSeed(seed: Uint8Array, wrappingKeyBytes: Uint8Array): Promi
3157
3933
  */
3158
3934
  declare function unwrapSeed(ciphertext: ArrayBuffer, iv: Uint8Array, wrappingKeyBytes: Uint8Array): Promise<Uint8Array>;
3159
3935
  //#endregion
3160
- export { AbracadabraBaseProvider, AbracadabraBaseProviderConfiguration, AbracadabraClient, AbracadabraClientConfig, AbracadabraOutgoingMessageArguments, AbracadabraProvider, AbracadabraProviderConfiguration, AbracadabraWS, AbracadabraWSConfiguration, AbracadabraWebRTC, type AbracadabraWebRTCConfiguration, AbracadabraWebSocketConn, AuthMessageType, AuthorizedScope, AwarenessError, BackgroundSyncManager, type BackgroundSyncManagerOptions, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, type ChatChannel, ChatClient, type ChatClientTransport, type ChatMessage, type ChatReadCursor, type ChatReadReceipt, type ChatTypingEvent, CloseEvent, CompleteAbracadabraBaseProviderConfiguration, CompleteAbracadabraWSConfiguration, CompleteHocuspocusProviderConfiguration, CompleteHocuspocusProviderWebsocketConfiguration, ConnectionTimeout, Constructable, ConstructableOutgoingMessage, type CreateNotificationInput, CryptoIdentity, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, DevicePairingChannel, type DevicePairingConfig, DeviceRegistrationService, type DeviceServerStatus, type DeviceTier, type DocEncryptionInfo, DocKeyManager, type DocSyncState, DocumentCache, type DocumentCacheOptions, DocumentMeta, E2EAbracadabraProvider, E2EEChannel, type E2EEIdentity, E2EOfflineStore, EffectivePermissionEntry, EffectivePermissionsResponse, EffectiveRole, EncryptedYMap, EncryptedYText, type FetchNotificationsInput, FileBlobStore, FileTransferChannel, FileTransferHandle, type FileTransferMeta, type FileTransferStatus, Forbidden, type GetChatHistoryInput, HealthStatus, HocusPocusWebSocket, HocuspocusProvider, HocuspocusProviderConfiguration, HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration, HocuspocusWebSocket, type IdentityDeviceEntry, type IdentityDocConfiguration, IdentityDocProvider, type IdentityProfile, type IdentityServerEntry, type IdentitySpaceEntry, InviteRow, KEY_EXCHANGE_CHANNEL, ManualSignaling, type ManualSignalingBlob, MessageTooBig, MessageType, type NotificationReadUpdate, type NotificationRecord, NotificationsClient, OfflineStore, OutgoingMessageArguments, OutgoingMessageInterface, type PairingRequest, type PairingResult, PeerConnection, type PeerInfo, type PeerState, PendingSubdoc, PermissionEntry, PublicKeyInfo, ResetConnection, SearchIndex, SearchResult, type SendChatMessageInput, ServerInfo, type SignalingIncoming, type SignalingOutgoing, SignalingSocket, SnapshotCreateResult, SnapshotData, SnapshotForkResult, SnapshotMeta, SnapshotRestoreResult, SpaceMeta, StatesArray, SubdocMessage, SubdocRegisteredEvent, Unauthorized, UploadInfo, UploadMeta, UploadQueueEntry, UploadQueueStatus, UserProfile, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, wordlist as bip39Wordlist, decryptField, deriveIdentityDocId, deriveSeedWrappingKey, encryptField, generateMnemonic, makeEncryptedYMap, makeEncryptedYText, mnemonicToEd25519Seed, mnemonicToKeyPair, onAuthenticatedParameters, onAuthenticationFailedParameters, onAwarenessChangeParameters, onAwarenessUpdateParameters, onCloseParameters, onCompactedParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onServerErrorParameters, onStatelessParameters, onStatusParameters, onSubdocLoadedParameters, onSubdocRegisteredParameters, onSyncedParameters, onUnsyncedChangesParameters, readAuthMessage, unwrapSeed, validateMnemonic, wrapSeed, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest };
3936
+ //#region packages/provider/src/DocTypes.d.ts
3937
+ /**
3938
+ * Canonical type definitions for Abracadabra document tree entries, page
3939
+ * metadata, and the static page-type catalog.
3940
+ *
3941
+ * These types were previously scattered across `mcp/converters/types.ts` and
3942
+ * `mcp/converters/page-types.ts`. By living in the provider package they
3943
+ * become first-class SDK types that any consumer (`@abraca/dabra`) can import
3944
+ * directly, removing the need for fragile relative-path re-exports.
3945
+ */
3946
+ type MetaFieldType = 'datetimerange' | 'daterange' | 'timerange' | 'datetime' | 'date' | 'time' | 'slider' | 'number' | 'toggle' | 'select' | 'multiselect' | 'colorPreset' | 'colorPicker' | 'location' | 'icon' | 'textarea' | 'url' | 'rating' | 'tags' | 'members';
3947
+ interface UserMetaField {
3948
+ id: string;
3949
+ type: string;
3950
+ label?: string;
3951
+ key?: string;
3952
+ latKey?: string;
3953
+ lngKey?: string;
3954
+ startKey?: string;
3955
+ endKey?: string;
3956
+ allDayKey?: string;
3957
+ presets?: string[];
3958
+ options?: string[];
3959
+ min?: number;
3960
+ max?: number;
3961
+ step?: number;
3962
+ unit?: string;
3963
+ default?: boolean;
3964
+ }
3965
+ interface PageMeta extends Record<string, unknown> {
3966
+ color?: string;
3967
+ icon?: string;
3968
+ subtitle?: string;
3969
+ note?: string;
3970
+ datetimeStart?: string;
3971
+ datetimeEnd?: string;
3972
+ allDay?: boolean;
3973
+ dateStart?: string;
3974
+ dateEnd?: string;
3975
+ timeStart?: string;
3976
+ timeEnd?: string;
3977
+ dateTaken?: string;
3978
+ checked?: boolean;
3979
+ priority?: number;
3980
+ status?: string;
3981
+ taskProgress?: number;
3982
+ rating?: number;
3983
+ tags?: string[];
3984
+ members?: {
3985
+ id: string;
3986
+ label: string;
3987
+ }[];
3988
+ url?: string;
3989
+ email?: string;
3990
+ phone?: string;
3991
+ number?: number;
3992
+ unit?: string;
3993
+ coverUploadId?: string;
3994
+ coverDocId?: string;
3995
+ coverMimeType?: string;
3996
+ geoType?: 'marker' | 'line' | 'measure';
3997
+ geoLat?: number;
3998
+ geoLng?: number;
3999
+ geoDescription?: string;
4000
+ deskX?: number;
4001
+ deskY?: number;
4002
+ deskZ?: number;
4003
+ deskMode?: string;
4004
+ mmX?: number;
4005
+ mmY?: number;
4006
+ graphX?: number;
4007
+ graphY?: number;
4008
+ graphPinned?: boolean;
4009
+ spShape?: string;
4010
+ spOpacity?: number;
4011
+ spX?: number;
4012
+ spY?: number;
4013
+ spZ?: number;
4014
+ spRX?: number;
4015
+ spRY?: number;
4016
+ spRZ?: number;
4017
+ spSX?: number;
4018
+ spSY?: number;
4019
+ spSZ?: number;
4020
+ spModelUploadId?: string;
4021
+ spModelDocId?: string;
4022
+ slidesTransition?: string;
4023
+ slidesTheme?: string;
4024
+ mediaDuration?: number;
4025
+ mediaWidth?: number;
4026
+ mediaHeight?: number;
4027
+ mediaCamera?: string;
4028
+ mediaLens?: string;
4029
+ mediaIso?: number;
4030
+ mediaFocalLength?: number;
4031
+ mediaAperture?: number;
4032
+ mediaShutterSpeed?: string;
4033
+ mediaArtist?: string;
4034
+ mediaAlbum?: string;
4035
+ mediaGenre?: string;
4036
+ mediaYear?: number;
4037
+ bold?: boolean;
4038
+ italic?: boolean;
4039
+ textColor?: string;
4040
+ bgColor?: string;
4041
+ align?: string;
4042
+ formula?: string;
4043
+ fileType?: string;
4044
+ entry?: boolean;
4045
+ kanbanColumnWidth?: string;
4046
+ galleryColumns?: number;
4047
+ galleryAspect?: string;
4048
+ galleryCardStyle?: string;
4049
+ galleryShowLabels?: boolean;
4050
+ gallerySortBy?: string;
4051
+ calendarView?: string;
4052
+ calendarWeekStart?: string;
4053
+ calendarShowWeekNumbers?: boolean;
4054
+ tableMode?: string;
4055
+ tableSortKey?: string;
4056
+ tableSortDir?: string;
4057
+ tableColumns?: any[];
4058
+ tableColumnWidths?: Record<string, number>;
4059
+ tableColumnOrder?: string[];
4060
+ timelineZoom?: string;
4061
+ timelinePixelsPerDay?: number;
4062
+ timelineCenterDate?: string;
4063
+ checklistFilter?: string;
4064
+ checklistSort?: string;
4065
+ mapShowLabels?: boolean;
4066
+ spatialGridVisible?: boolean;
4067
+ graphSpacing?: string;
4068
+ graphShowLabels?: boolean;
4069
+ graphEdgeThickness?: string;
4070
+ mmSpacing?: string;
4071
+ showRefEdges?: boolean;
4072
+ chartType?: string;
4073
+ chartMetric?: string;
4074
+ chartColorScheme?: string;
4075
+ chartLimit?: number;
4076
+ chartShowLegend?: boolean;
4077
+ chartShowValues?: boolean;
4078
+ mediaRepeat?: string;
4079
+ mediaShuffle?: boolean;
4080
+ sheetsDefaultColWidth?: number;
4081
+ sheetsDefaultRowHeight?: number;
4082
+ sheetsShowGridlines?: boolean;
4083
+ sheetsColumnWidths?: Record<string, number>;
4084
+ sheetsRowHeights?: Record<string, number>;
4085
+ sheetsFreezeRows?: number;
4086
+ sheetsFreezeCols?: number;
4087
+ _metaFields?: UserMetaField[];
4088
+ _metaInitialized?: boolean;
4089
+ }
4090
+ interface TreeEntry {
4091
+ id: string;
4092
+ label: string;
4093
+ parentId: string | null;
4094
+ order: number;
4095
+ type?: string;
4096
+ meta?: PageMeta;
4097
+ createdAt?: number;
4098
+ updatedAt?: number;
4099
+ }
4100
+ interface TreeNode {
4101
+ id: string;
4102
+ label: string;
4103
+ type?: string;
4104
+ meta?: PageMeta;
4105
+ order: number;
4106
+ children: TreeNode[];
4107
+ }
4108
+ interface TreeSearchResult {
4109
+ id: string;
4110
+ label: string;
4111
+ type?: string;
4112
+ meta?: PageMeta;
4113
+ /** Ancestor labels from root → parent. */
4114
+ path: string[];
4115
+ }
4116
+ interface PageTypeMetaField {
4117
+ type: MetaFieldType;
4118
+ label?: string;
4119
+ /** single-value fields */
4120
+ key?: string;
4121
+ /** location */
4122
+ latKey?: string;
4123
+ lngKey?: string;
4124
+ /** ranges */
4125
+ startKey?: string;
4126
+ endKey?: string;
4127
+ allDayKey?: string;
4128
+ /** color preset */
4129
+ presets?: string[];
4130
+ /** icon / select / tags */
4131
+ options?: string[];
4132
+ /** slider / number */
4133
+ min?: number;
4134
+ max?: number;
4135
+ step?: number;
4136
+ unit?: string;
4137
+ /** toggle default */
4138
+ default?: boolean;
4139
+ }
4140
+ interface PageTypeInfo {
4141
+ key: string;
4142
+ label: string;
4143
+ icon: string;
4144
+ description?: string;
4145
+ /** true = core type (always available); false = requires plugin */
4146
+ core: boolean;
4147
+ plugin?: string;
4148
+ supportsChildren: boolean;
4149
+ childLabel?: string;
4150
+ grandchildLabel?: string;
4151
+ /** -1 = unlimited depth */
4152
+ defaultDepth?: number;
4153
+ /** Fields that apply to this doc's descendants (children, grandchildren, ...) */
4154
+ metaSchema?: PageTypeMetaField[];
4155
+ /** Fields written to this doc's own meta on first render (renderer config) */
4156
+ defaultMetaFields?: PageTypeMetaField[];
4157
+ }
4158
+ declare const GEO_TYPE_META_SCHEMAS: Record<string, PageTypeMetaField[]>;
4159
+ declare const PAGE_TYPES: Record<string, PageTypeInfo>;
4160
+ declare const TYPE_ALIASES: Record<string, string>;
4161
+ declare function resolvePageType(key: string | undefined): PageTypeInfo | undefined;
4162
+ //#endregion
4163
+ //#region packages/provider/src/TreeManager.d.ts
4164
+ declare class TreeManager {
4165
+ private dm;
4166
+ constructor(dm: DocumentManager);
4167
+ /** Read all tree entries as plain objects. */
4168
+ readEntries(): TreeEntry[];
4169
+ /** Get immediate children of a parent (sorted by order). */
4170
+ childrenOf(parentId: string | null): TreeEntry[];
4171
+ /** Get all descendants recursively. */
4172
+ descendantsOf(parentId: string | null): TreeEntry[];
4173
+ /** Build nested tree JSON. */
4174
+ buildTree(rootId?: string | null, maxDepth?: number): TreeNode[];
4175
+ private _buildTree;
4176
+ /** Find a single entry by ID. */
4177
+ get(docId: string): TreeEntry | null;
4178
+ /** Search by label (case-insensitive substring match). */
4179
+ find(query: string, rootId?: string | null): TreeSearchResult[];
4180
+ /** Create a new document in the tree. Returns the new entry. */
4181
+ create(opts: {
4182
+ parentId?: string | null;
4183
+ label: string;
4184
+ type?: string;
4185
+ meta?: Partial<PageMeta>;
4186
+ }): TreeEntry;
4187
+ /**
4188
+ * Create a document and auto-apply renderer defaults from the page type's
4189
+ * `defaultMetaFields` (toggle fields with `default` values).
4190
+ */
4191
+ createWithTypeDefaults(opts: {
4192
+ parentId?: string | null;
4193
+ label: string;
4194
+ type: string;
4195
+ extraMeta?: Partial<PageMeta>;
4196
+ }): TreeEntry;
4197
+ /** Rename a document. */
4198
+ rename(docId: string, label: string): void;
4199
+ /** Move a document to a new parent. */
4200
+ move(docId: string, newParentId?: string | null, order?: number): void;
4201
+ /** Change the page type of a document. */
4202
+ changeType(docId: string, type: string): void;
4203
+ /**
4204
+ * Soft-delete a document and descendants (move to trash).
4205
+ * @returns count of deleted documents
4206
+ */
4207
+ delete(docId: string): number;
4208
+ /** Duplicate a document (shallow clone). Returns the new entry. */
4209
+ duplicate(docId: string): TreeEntry;
4210
+ /** Restore a document from trash back into the tree. */
4211
+ restore(docId: string): void;
4212
+ /** List trashed documents. */
4213
+ listTrash(): TreeEntry[];
4214
+ /** Get enabled plugin names from space-plugins map. */
4215
+ getEnabledPlugins(): string[];
4216
+ private _descendantIds;
4217
+ }
4218
+ //#endregion
4219
+ //#region packages/provider/src/DocConverters.d.ts
4220
+ /**
4221
+ * Converts a Y.XmlFragment (TipTap document) to markdown.
4222
+ * Extracts the title from the documentHeader element.
4223
+ *
4224
+ * @returns `{ title, markdown }` where title is the H1/header text
4225
+ */
4226
+ declare function yjsToMarkdown(fragment: Y.XmlFragment): {
4227
+ title: string;
4228
+ markdown: string;
4229
+ };
4230
+ declare function filenameToLabel(raw: string): string;
4231
+ interface FrontmatterResult {
4232
+ title?: string;
4233
+ meta: Partial<PageMeta>;
4234
+ body: string;
4235
+ }
4236
+ declare function parseFrontmatter(markdown: string): FrontmatterResult;
4237
+ declare function populateYDocFromMarkdown(fragment: Y.XmlFragment, markdown: string, fallbackTitle?: string): void;
4238
+ declare function buildHeadingElement(text: string, level?: 1 | 2 | 3 | 4 | 5 | 6): Y.XmlElement;
4239
+ declare function buildParagraphElement(text: string): Y.XmlElement;
4240
+ declare function buildBulletListElement(items: string[]): Y.XmlElement;
4241
+ declare function buildOrderedListElement(items: string[]): Y.XmlElement;
4242
+ declare function buildTaskListElement(items: Array<{
4243
+ text: string;
4244
+ checked?: boolean;
4245
+ }>): Y.XmlElement;
4246
+ declare function buildCodeBlockElement(code: string, language?: string): Y.XmlElement;
4247
+ declare function buildBlockquoteElement(text: string): Y.XmlElement;
4248
+ declare function buildHorizontalRuleElement(): Y.XmlElement;
4249
+ /** Parse markdown into block Y.XmlElements (no header/meta). */
4250
+ declare function buildBlocksFromMarkdown(markdown: string): Y.XmlElement[];
4251
+ interface DocumentBlock {
4252
+ type: string;
4253
+ attrs: Record<string, unknown>;
4254
+ text: string;
4255
+ items?: string[];
4256
+ children?: DocumentBlock[];
4257
+ }
4258
+ declare function readBlocksFromFragment(fragment: Y.XmlFragment): DocumentBlock[];
4259
+ //#endregion
4260
+ //#region packages/provider/src/ContentManager.d.ts
4261
+ interface DocumentContent {
4262
+ label: string;
4263
+ type?: string;
4264
+ meta?: PageMeta;
4265
+ title: string;
4266
+ markdown: string;
4267
+ children: Array<{
4268
+ id: string;
4269
+ label: string;
4270
+ type?: string;
4271
+ meta?: PageMeta;
4272
+ }>;
4273
+ }
4274
+ declare class ContentManager {
4275
+ private dm;
4276
+ constructor(dm: DocumentManager);
4277
+ /**
4278
+ * Read document content as markdown.
4279
+ * Returns the title extracted from the TipTap documentHeader, the markdown
4280
+ * body, tree metadata, and immediate children.
4281
+ */
4282
+ read(docId: string): Promise<DocumentContent>;
4283
+ /**
4284
+ * Write markdown content to a document.
4285
+ * Supports optional YAML frontmatter for title and metadata.
4286
+ *
4287
+ * @param mode - "replace" clears existing content first (default),
4288
+ * "append" adds to the end.
4289
+ */
4290
+ write(docId: string, markdown: string, mode?: "replace" | "append"): Promise<void>;
4291
+ private _appendElements;
4292
+ appendHeading(docId: string, text: string, opts?: {
4293
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
4294
+ }): Promise<void>;
4295
+ appendParagraph(docId: string, text: string): Promise<void>;
4296
+ appendBulletList(docId: string, items: string[]): Promise<void>;
4297
+ appendOrderedList(docId: string, items: string[]): Promise<void>;
4298
+ appendTaskList(docId: string, items: Array<{
4299
+ text: string;
4300
+ checked?: boolean;
4301
+ }>): Promise<void>;
4302
+ appendCodeBlock(docId: string, code: string, opts?: {
4303
+ language?: string;
4304
+ }): Promise<void>;
4305
+ appendBlockquote(docId: string, text: string): Promise<void>;
4306
+ appendHorizontalRule(docId: string): Promise<void>;
4307
+ appendMarkdown(docId: string, markdown: string): Promise<void>;
4308
+ getBlocks(docId: string): Promise<DocumentBlock[]>;
4309
+ /**
4310
+ * Get the raw Y.XmlFragment for a document (the 'default' fragment
4311
+ * that TipTap uses for document content).
4312
+ */
4313
+ getFragment(docId: string): Promise<Y.XmlFragment>;
4314
+ /**
4315
+ * Get the raw Y.Doc for a document (synced).
4316
+ */
4317
+ getDoc(docId: string): Promise<Y.Doc>;
4318
+ }
4319
+ //#endregion
4320
+ //#region packages/provider/src/MetaManager.d.ts
4321
+ interface DocumentMetaInfo {
4322
+ id: string;
4323
+ label: string;
4324
+ type?: string;
4325
+ meta: PageMeta;
4326
+ }
4327
+ declare class MetaManager {
4328
+ private dm;
4329
+ constructor(dm: DocumentManager);
4330
+ /** Read metadata for a document. Returns null if not found. */
4331
+ get(docId: string): DocumentMetaInfo | null;
4332
+ /**
4333
+ * Merge fields into a document's metadata.
4334
+ * Existing keys not in the update are preserved.
4335
+ */
4336
+ update(docId: string, meta: Partial<PageMeta>): void;
4337
+ /**
4338
+ * Replace all metadata on a document.
4339
+ * This overwrites the entire meta object.
4340
+ */
4341
+ set(docId: string, meta: PageMeta): void;
4342
+ /**
4343
+ * Clear specific metadata keys (set them to null/undefined).
4344
+ */
4345
+ clear(docId: string, keys: string[]): void;
4346
+ }
4347
+ //#endregion
4348
+ //#region packages/provider/src/DocumentManager.d.ts
4349
+ interface DocumentManagerConfig {
4350
+ /** Server base URL (http or https). */
4351
+ url: string;
4352
+ /** Display name for awareness (cursor labels, presence). */
4353
+ name?: string;
4354
+ /** Cursor / awareness color. */
4355
+ color?: string;
4356
+ /** Invite code for first-time registration. */
4357
+ inviteCode?: string;
4358
+ /** Suppress stderr logging. */
4359
+ quiet?: boolean;
4360
+ /** Disable IndexedDB offline persistence. */
4361
+ disableOfflineStore?: boolean;
4362
+ /** Custom fetch for Node.js environments. */
4363
+ fetch?: typeof globalThis.fetch;
4364
+ /**
4365
+ * Pre-configured AbracadabraClient instance.
4366
+ * When provided, the `url` and `fetch` fields are ignored — the client's
4367
+ * configuration is used instead. This is useful when the consumer has
4368
+ * already authenticated or configured the client externally.
4369
+ */
4370
+ client?: AbracadabraClient;
4371
+ /**
4372
+ * Shared WebSocket connection. Required in Node.js environments — pass an
4373
+ * AbracadabraWS constructed with WebSocketPolyfill set to the `ws` package.
4374
+ * When omitted, each provider creates its own connection from client.wsUrl.
4375
+ * The caller owns this instance and must destroy it after dm.destroy().
4376
+ */
4377
+ websocketProvider?: AbracadabraWS;
4378
+ /**
4379
+ * Known root document ID. When provided, connect() skips server discovery
4380
+ * and connects directly to this document. Useful in tests or CLIs where
4381
+ * the entry-point docId is already known.
4382
+ */
4383
+ rootDocId?: string;
4384
+ }
4385
+ declare class DocumentManager {
4386
+ readonly client: AbracadabraClient;
4387
+ /** Tree CRUD operations. */
4388
+ readonly tree: TreeManager;
4389
+ /** Document content read/write. */
4390
+ readonly content: ContentManager;
4391
+ /** Document metadata read/write. */
4392
+ readonly meta: MetaManager;
4393
+ private _config;
4394
+ private _serverInfo;
4395
+ private _rootDocId;
4396
+ private _spaces;
4397
+ private _rootDoc;
4398
+ private _rootProvider;
4399
+ private childCache;
4400
+ constructor(config: DocumentManagerConfig);
4401
+ get displayName(): string;
4402
+ get displayColor(): string;
4403
+ get serverInfo(): ServerInfo | null;
4404
+ get rootDocId(): string | null;
4405
+ get rootDocument(): Y.Doc | null;
4406
+ get rootProvider(): AbracadabraProvider | null;
4407
+ /**
4408
+ * Spaces visible to the caller — direct children of the server root with
4409
+ * `kind === "space"`. Populated by {@link connect}.
4410
+ */
4411
+ get spaces(): DocumentMeta[];
4412
+ get isConnected(): boolean;
4413
+ /** Get the root doc-tree Y.Map. */
4414
+ getTreeMap(): Y.Map<any> | null;
4415
+ /** Get the root doc-trash Y.Map. */
4416
+ getTrashMap(): Y.Map<any> | null;
4417
+ /** Get the root space-plugins Y.Map. */
4418
+ getPluginsMap(): Y.Map<any> | null;
4419
+ /**
4420
+ * Connect to the server: discover the entry-point document, sync the root
4421
+ * Y.Doc, and set awareness.
4422
+ *
4423
+ * **Authentication must be done before calling connect().**
4424
+ * Call `dm.client.loginWithKey(publicKey, signFn)` or `dm.client.login()`
4425
+ * to authenticate first.
4426
+ *
4427
+ * When `rootDocId` is set in the config, server discovery is skipped and
4428
+ * the manager connects directly to that document.
4429
+ */
4430
+ connect(): Promise<void>;
4431
+ /** Switch active space to a different root document. */
4432
+ switchSpace(docId: string): Promise<void>;
4433
+ /** Create, sync, and set awareness on a root provider for the given docId. */
4434
+ private _connectToRoot;
4435
+ /** Graceful shutdown. */
4436
+ destroy(): Promise<void>;
4437
+ /** Get or create a child provider for a document (synced). */
4438
+ getChildProvider(docId: string): Promise<AbracadabraProvider>;
4439
+ private log;
4440
+ }
4441
+ //#endregion
4442
+ //#region packages/provider/src/DocUtils.d.ts
4443
+ /**
4444
+ * Wait for a provider's `synced` event with a timeout.
4445
+ * Resolves immediately if the provider is already synced.
4446
+ */
4447
+ declare function waitForSync(provider: {
4448
+ isSynced?: boolean;
4449
+ on(event: string, cb: () => void): void;
4450
+ off(event: string, cb: () => void): void;
4451
+ }, timeoutMs?: number): Promise<void>;
4452
+ /**
4453
+ * Wrap a promise with a timeout.
4454
+ */
4455
+ declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, message?: string): Promise<T>;
4456
+ /**
4457
+ * Normalize a document ID so the hub/root doc ID is treated as the tree root
4458
+ * (null). This lets callers pass the hub doc_id from list_spaces as
4459
+ * parentId/rootId and get the expected root-level results instead of an empty
4460
+ * set.
4461
+ */
4462
+ declare function normalizeRootId(id: string | null | undefined, rootDocId: string | null): string | null;
4463
+ /**
4464
+ * Safely read a tree map value, converting Y.Map to plain object if needed.
4465
+ */
4466
+ declare function toPlain(val: unknown): unknown;
4467
+ //#endregion
4468
+ export { AbracadabraBaseProvider, AbracadabraBaseProviderConfiguration, AbracadabraClient, AbracadabraClientConfig, AbracadabraOutgoingMessageArguments, AbracadabraProvider, AbracadabraProviderConfiguration, AbracadabraWS, AbracadabraWSConfiguration, AbracadabraWebRTC, type AbracadabraWebRTCConfiguration, AbracadabraWebSocketConn, AuditLogEntry, AuditQueryOpts, AuditVerifyResult, AuthFailureContext, AuthFailureReason, AuthMessageType, AuthorizedScope, AwarenessError, BackgroundSyncManager, type BackgroundSyncManagerOptions, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, ChannelKeyResolver, type ChatChannel, ChatClient, type ChatClientTransport, type MarkReadInput as ChatMarkReadInput, type ChatMessage, type ChatReadCursor, type ChatReadReceipt, type ChatTypingEvent, CloseEvent, CompleteAbracadabraBaseProviderConfiguration, CompleteAbracadabraWSConfiguration, CompleteHocuspocusProviderConfiguration, CompleteHocuspocusProviderWebsocketConfiguration, ConnectionTimeout, Constructable, ConstructableOutgoingMessage, ContentManager, type CreateNotificationInput, CryptoIdentity, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, type DeleteMessageInput, DevicePairingChannel, type DevicePairingConfig, DeviceRegistrationService, type DeviceServerStatus, type DeviceTier, type DocEncryptionInfo, DocKeyManager, DocSearchHit, type DocSyncState, type DocumentBlock, DocumentCache, type DocumentCacheOptions, type DocumentContent, DocumentManager, type DocumentManagerConfig, DocumentMeta, type DocumentMetaInfo, E2EAbracadabraProvider, E2EEChannel, type E2EEIdentity, E2EOfflineStore, type EditMessageInput, EffectivePermissionEntry, EffectivePermissionsResponse, EffectiveRole, EncryptedChatClient, EncryptedYMap, EncryptedYText, type FetchInboxInput, type FetchNotificationsInput, FileBlobStore, FileTransferChannel, FileTransferHandle, type FileTransferMeta, type FileTransferStatus, type FoldedMessage, Forbidden, GEO_TYPE_META_SCHEMAS, type GetChatHistoryInput, HealthStatus, HocusPocusWebSocket, HocuspocusProvider, HocuspocusProviderConfiguration, HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration, HocuspocusWebSocket, type IdentityDeviceEntry, type IdentityDocConfiguration, IdentityDocProvider, type IdentityProfile, type IdentityServerEntry, type IdentitySpaceEntry, type InboxEntry, type MarkReadInput$1 as InboxMarkReadInput, InviteRow, KEY_EXCHANGE_CHANNEL, Kind, ManualSignaling, type ManualSignalingBlob, type MessageRecord, MessageTooBig, MessageType, MetaFieldType, MetaManager, type NotificationReadUpdate, type NotificationRecord, NotificationsClient, OfflineStore, OutgoingMessageArguments, OutgoingMessageInterface, PAGE_TYPES, PageMeta, PageTypeInfo, PageTypeMetaField, type PairingRequest, type PairingResult, PeerConnection, type PeerInfo, type PeerState, PendingSubdoc, PermissionEntry, PublicKeyInfo, RPC_PREFIX, ReadyzStatus, ResetConnection, type RpcCallHandle, type RpcCallOptions, RpcClient, RpcError, type RpcErrorCode, type RpcErrorPayload, type RpcFrame, type RpcHandler, type RpcHandlerContext, type RpcKind, type RpcTarget, type RpcTransport, SERVER_ROOT_ID, SearchIndex, SearchResult, type SendChatMessageInput, type SendMessageInput, type SendMessageResult, ServerInfo, type SignalingIncoming, type SignalingOutgoing, SignalingSocket, SnapshotCreateResult, SnapshotData, SnapshotFileEntry, SnapshotForkResult, SnapshotMeta, SnapshotRestoreResult, StatesArray, SubdocMessage, SubdocRegisteredEvent, TYPE_ALIASES, TreeEntry, TreeManager, TreeNode, TreeSearchResult, Unauthorized, UploadInfo, UploadMeta, UploadQueueEntry, UploadQueueStatus, UserMetaField, UserProfile, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, wordlist as bip39Wordlist, buildBlockquoteElement, buildBlocksFromMarkdown, buildBulletListElement, buildCodeBlockElement, buildHeadingElement, buildHorizontalRuleElement, buildOrderedListElement, buildParagraphElement, buildTaskListElement, decryptChatContent, decryptField, deriveIdentityDocId, deriveSeedWrappingKey, encryptChatContent, encryptField, filenameToLabel, foldRecords, generateMnemonic, isEncryptedContent, makeEncryptedYMap, makeEncryptedYText, mnemonicToEd25519Seed, mnemonicToKeyPair, normalizeRootId, onAuthenticatedParameters, onAuthenticationFailedParameters, onAwarenessChangeParameters, onAwarenessUpdateParameters, onCloseParameters, onCompactedParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onServerErrorParameters, onStatelessParameters, onStatusParameters, onSubdocLoadedParameters, onSubdocRegisteredParameters, onSyncedParameters, onUnsyncedChangesParameters, parseFrontmatter, populateYDocFromMarkdown, readAuthMessage, readBlocksFromFragment, recordFromYAny, resolvePageType, toPlain, unwrapSeed, validateMnemonic, waitForSync, withTimeout, wrapSeed, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest, yjsToMarkdown };