@kyneta/yjs-schema 1.2.0 → 1.3.1

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/create.ts","../src/substrate.ts","../src/change-mapping.ts","../src/yjs-resolve.ts","../src/populate.ts","../src/reader.ts","../src/version.ts","../src/sync.ts","../src/bind-yjs.ts"],"sourcesContent":["// @kyneta/yjs-schema — Yjs CRDT substrate for @kyneta/schema.\n//\n// Provides a Substrate<YjsVersion> implementation that wraps a Y.Doc\n// with schema-aware typed reads, writes, versioning, and export/import.\n//\n// Batteries-included API (most users):\n// createYjsDoc, createYjsDocFromEntirety, version, exportEntirety,\n// exportSince, merge, change, subscribe, applyChanges\n//\n// Low-level primitives (power users):\n// createYjsSubstrate, yjsSubstrateFactory, yjsReader,\n// resolveYjsType, stepIntoYjs, applyChangeToYjs, eventsToOps, YjsVersion\n\n// ---------------------------------------------------------------------------\n// Batteries-included API — one import, one createYjsDoc call, done\n// ---------------------------------------------------------------------------\n\n// Mutation & observation (re-exported from @kyneta/schema for convenience)\n// Schema definition (re-exported for convenience)\nexport {\n applyChanges,\n change,\n Schema,\n subscribe,\n subscribeNode,\n} from \"@kyneta/schema\"\n// Construction\nexport { createYjsDoc, createYjsDocFromEntirety } from \"./create.js\"\n// Sync primitives (Yjs-specific)\nexport {\n exportEntirety,\n exportSince,\n merge,\n version,\n} from \"./sync.js\"\n\n// Types (re-exported for convenience)\nexport type { Changeset } from \"@kyneta/changefeed\"\nexport type { Op, Ref, SubstratePayload } from \"@kyneta/schema\"\n\n// ---------------------------------------------------------------------------\n// Low-level primitives — for power users and custom substrate compositions\n// ---------------------------------------------------------------------------\n\n// Namespace — the yjs substrate namespace (replaces standalone escape hatch;\n// the old `yjs(ref)` call is now `yjs.unwrap(ref)`)\nexport { yjs } from \"./bind-yjs.js\"\nexport type { YjsCaps } from \"./bind-yjs.js\"\n// Change mapping\nexport { applyChangeToYjs, eventsToOps } from \"./change-mapping.js\"\n// Container creation\nexport { ensureContainers } from \"./populate.js\"\n// Reader\nexport { yjsReader } from \"./reader.js\"\n// Substrate\nexport {\n createYjsSubstrate,\n yjsReplicaFactory,\n yjsSubstrateFactory,\n} from \"./substrate.js\"\n// Version\nexport { YjsVersion } from \"./version.js\"\n// Container resolution\nexport { resolveYjsType, stepIntoYjs } from \"./yjs-resolve.js\"","// create — batteries-included document construction backed by YjsSubstrate.\n//\n// Provides `createYjsDoc` and `createYjsDocFromEntirety` functions that\n// hide the interpret pipeline and layer composition behind a single call.\n//\n// Internally tracks substrates via a module-scoped WeakMap so that sync\n// primitives (`version`, `exportEntirety`, `merge` in sync.ts)\n// can retrieve the substrate from just a doc ref.\n//\n// `getSubstrate` is exported for use by `sync.ts` but is NOT re-exported\n// from the barrel (`index.ts`). It is an internal cross-module helper.\n//\n// Two forms for `createYjsDoc`:\n// createYjsDoc(schema, yjsDoc) — \"bring your own doc\" (wrap existing)\n// createYjsDoc(schema) — create a fresh empty Y.Doc\n\nimport type {\n Ref,\n Schema as SchemaType,\n Substrate,\n SubstratePayload,\n} from \"@kyneta/schema\"\nimport {\n interpret,\n observation,\n readable,\n registerSubstrate,\n writable,\n} from \"@kyneta/schema\"\nimport type * as Y from \"yjs\"\nimport { createYjsSubstrate, yjsSubstrateFactory } from \"./substrate.js\"\nimport type { YjsVersion } from \"./version.js\"\n\n// ---------------------------------------------------------------------------\n// Substrate tracking (module-scoped)\n// ---------------------------------------------------------------------------\n\nconst substrates = new WeakMap<object, Substrate<YjsVersion>>()\n\n/**\n * Retrieve the substrate associated with a doc created by `createYjsDoc`\n * or `createYjsDocFromEntirety`.\n *\n * Exported for `sync.ts` — NOT re-exported from the barrel.\n *\n * @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.\n */\nexport function getSubstrate(doc: object): Substrate<YjsVersion> {\n const s = substrates.get(doc)\n if (!s) {\n throw new Error(\n \"version/exportEntirety/merge called on an object without a YjsSubstrate. \" +\n \"Use a doc created by createYjsDoc() or createYjsDocFromEntirety().\",\n )\n }\n return s\n}\n\n// ---------------------------------------------------------------------------\n// registerDoc — internal helper (interpret + WeakMap registration)\n// ---------------------------------------------------------------------------\n\nfunction registerDoc(\n schema: SchemaType,\n substrate: Substrate<YjsVersion>,\n): any {\n // The `as any` on the builder avoids TS2589 — interpret's fluent API\n // produces deeply recursive types when S is the abstract SchemaType.\n // The public createYjsDoc/createYjsDocFromEntirety signatures provide\n // the correct Ref<S> return type via interface call signature patterns.\n const doc: any = (interpret as any)(schema, substrate.context())\n .with(readable)\n .with(writable)\n .with(observation)\n .done()\n substrates.set(doc, substrate)\n // Also register in the general unwrap() registry so that the\n // yjs() escape hatch can discover the substrate from the ref.\n registerSubstrate(doc, substrate)\n return doc\n}\n\n// ---------------------------------------------------------------------------\n// isYDoc — runtime check for Y.Doc\n// ---------------------------------------------------------------------------\n\nfunction isYDoc(value: unknown): value is Y.Doc {\n return (\n value !== null &&\n value !== undefined &&\n typeof value === \"object\" &&\n \"getMap\" in value &&\n \"getText\" in value &&\n \"getArray\" in value &&\n \"transact\" in value &&\n typeof (value as any).transact === \"function\" &&\n // Y.Doc has clientID; distinguish from other objects\n \"clientID\" in value &&\n typeof (value as any).clientID === \"number\"\n )\n}\n\n// ---------------------------------------------------------------------------\n// createYjsDoc\n// ---------------------------------------------------------------------------\n\n// Interface call signature avoids TS2589 on Ref<S> when S is generic.\n\n/**\n * Create a live Yjs-backed document.\n *\n * **Form 1 — bring your own doc:**\n * ```ts\n * const yjsDoc = new Y.Doc()\n * const doc = createYjsDoc(mySchema, yjsDoc)\n * ```\n *\n * **Form 2 — fresh empty doc:**\n * ```ts\n * const doc = createYjsDoc(mySchema)\n *\n * // Apply initial content via change():\n * change(doc, d => {\n * d.title.insert(0, \"Hello\")\n * d.items.push({ name: \"First item\" })\n * })\n * ```\n *\n * Returns a full-stack `Ref<S>` — callable, navigable, writable,\n * transactable, and observable. Backed by a `YjsSubstrate` with\n * CRDT collaboration support.\n *\n * The returned ref observes **all** mutations to the underlying Y.Doc,\n * regardless of source (local kyneta writes, merge, external\n * `Y.applyUpdate()`, external raw Yjs API mutations).\n *\n * @param schema - The schema describing the document structure.\n * @param doc - Optional `Y.Doc` instance to wrap. If omitted, a fresh\n * empty Y.Doc is created with containers matching the schema.\n */\ntype CreateYjsDoc = <S extends SchemaType>(schema: S, doc?: Y.Doc) => Ref<S>\n\nexport const createYjsDoc: CreateYjsDoc = (schema, doc) => {\n if (doc !== undefined && isYDoc(doc)) {\n // Bring your own doc — wrap the existing Y.Doc\n return registerDoc(schema, createYjsSubstrate(doc, schema))\n }\n // Fresh empty doc\n return registerDoc(schema, yjsSubstrateFactory.create(schema))\n}\n\n// ---------------------------------------------------------------------------\n// createYjsDocFromEntirety\n// ---------------------------------------------------------------------------\n\ntype CreateYjsDocFromEntirety = <S extends SchemaType>(\n schema: S,\n payload: SubstratePayload,\n) => Ref<S>\n\n/**\n * Reconstruct a live Yjs-backed document from a substrate entirety payload.\n *\n * The payload must have been produced by `exportEntirety()` on a\n * compatible document. This is the entry point for SSR hydration\n * and reconnection past log compaction.\n *\n * ```ts\n * const payload = exportEntirety(docA)\n * const docB = createYjsDocFromEntirety(MySchema, payload)\n * // docB has the same state as docA at the time of export\n * ```\n */\nexport const createYjsDocFromEntirety: CreateYjsDocFromEntirety = (\n schema,\n payload,\n) => registerDoc(schema, yjsSubstrateFactory.fromEntirety(payload, schema))\n","// substrate — YjsSubstrate implementation.\n//\n// Implements Substrate<YjsVersion> with:\n// - Imperative local writes (prepare accumulates, onFlush applies in transact)\n// - Persistent observeDeep event bridge for external changes\n// - Single re-entrancy guard + transaction.origin check\n//\n// The event bridge contract: wrapping a Y.Doc in a kyneta substrate\n// means subscribing to the kyneta doc observes ALL mutations to the\n// underlying Y.Doc, regardless of source (local kyneta writes,\n// merge, external Y.applyUpdate, external raw Yjs API mutations).\n\nimport type {\n ChangeBase,\n Path,\n Reader,\n Replica,\n ReplicaFactory,\n Schema as SchemaNode,\n Substrate,\n SubstrateFactory,\n SubstratePayload,\n WritableContext,\n} from \"@kyneta/schema\"\nimport { BACKING_DOC, buildWritableContext, executeBatch } from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\nimport { applyChangeToYjs, eventsToOps } from \"./change-mapping.js\"\nimport { ensureContainers } from \"./populate.js\"\nimport { yjsReader } from \"./reader.js\"\nimport { YjsVersion } from \"./version.js\"\n\n// ---------------------------------------------------------------------------\n// Origin tag — used to suppress echo from our own transactions\n// ---------------------------------------------------------------------------\n\nconst KYNETA_ORIGIN = \"kyneta-prepare\"\n\n// ---------------------------------------------------------------------------\n// createYjsSubstrate — wrap a user-provided Y.Doc\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a `Substrate<YjsVersion>` wrapping a user-provided Y.Doc.\n *\n * This is the \"bring your own doc\" entry point. The user creates and\n * manages the Y.Doc (possibly via a Yjs provider); this function wraps\n * it with a schema-aware overlay providing typed reads, writes,\n * versioning, and export/merge through the standard Substrate interface.\n *\n * **Event bridge contract:** A persistent `observeDeep` handler is\n * registered on the root Y.Map at construction time. All non-kyneta\n * mutations to the Y.Doc (merges, external local writes) are bridged\n * to the kyneta changefeed. Subscribing to the kyneta doc observes all\n * mutations regardless of source.\n *\n * @param doc - The Y.Doc to wrap. The substrate does NOT own the doc;\n * the caller is responsible for its lifecycle.\n * @param schema - The root schema for the document.\n */\nexport function createYjsSubstrate(\n doc: Y.Doc,\n schema: SchemaNode,\n): Substrate<YjsVersion> {\n // --- Closure-scoped state ---\n\n // Accumulated changes from prepare(), drained by onFlush().\n const pendingChanges: Array<{ path: Path; change: ChangeBase }> = []\n\n // Re-entrancy guard: set true around our doc.transact() in onFlush\n // AND around executeBatch in the event bridge. When true, prepare()\n // skips Yjs-side work (changes are already applied by Yjs or about\n // to be), and onFlush() skips transact/commit.\n let inOurTransaction = false\n\n // Stashed origin from merge for the event bridge to pick up.\n let pendingMergeOrigin: string | undefined\n\n // Lazy-built WritableContext (same pattern as PlainSubstrate / LoroSubstrate).\n let cachedCtx: WritableContext | undefined\n\n // The root Y.Map — all schema fields are children of this single map.\n const rootMap = doc.getMap(\"root\")\n\n // The Reader — live view over the Yjs shared type tree.\n const reader: Reader = yjsReader(doc, schema)\n\n // --- Substrate object ---\n\n const substrate = {\n [BACKING_DOC]: doc,\n\n reader: reader,\n\n prepare(path: Path, change: ChangeBase): void {\n if (!inOurTransaction) {\n // Local write: accumulate for flush.\n // No Yjs side effects — mutations happen at flush time.\n pendingChanges.push({ path, change })\n }\n // During event handler replay: no-op on Yjs side.\n // wrappedPrepare (changefeed layer) still buffers the op.\n },\n\n onFlush(_origin?: string): void {\n if (!inOurTransaction && pendingChanges.length > 0) {\n // Local write: apply accumulated changes within a single\n // Yjs transaction tagged with our origin for echo suppression.\n inOurTransaction = true\n try {\n doc.transact(() => {\n for (const { path, change } of pendingChanges) {\n applyChangeToYjs(rootMap, schema, path, change)\n }\n }, KYNETA_ORIGIN)\n pendingChanges.length = 0\n } finally {\n inOurTransaction = false\n }\n }\n // During event handler replay: no-op on Yjs side.\n // wrappedFlush (changefeed layer) still delivers notifications.\n },\n\n context(): WritableContext {\n if (!cachedCtx) {\n cachedCtx = buildWritableContext(substrate)\n }\n return cachedCtx\n },\n\n version(): YjsVersion {\n return new YjsVersion(Y.encodeStateVector(doc))\n },\n\n baseVersion(): YjsVersion {\n // Yjs substrate: base is always the initial state (no advance supported).\n return new YjsVersion(new Uint8Array([0]))\n },\n\n advance(_to: YjsVersion): void {\n throw new Error(\n \"advance() on a live Yjs substrate is not yet supported. \" +\n \"Use advance() on a YjsReplica instead.\",\n )\n },\n\n exportEntirety(): SubstratePayload {\n return {\n kind: \"entirety\",\n encoding: \"binary\",\n data: Y.encodeStateAsUpdate(doc),\n }\n },\n\n exportSince(since: YjsVersion): SubstratePayload | null {\n try {\n const bytes = Y.encodeStateAsUpdate(doc, since.sv)\n return { kind: \"since\", encoding: \"binary\", data: bytes }\n } catch {\n return null\n }\n },\n\n merge(payload: SubstratePayload, origin?: string): void {\n if (\n payload.encoding !== \"binary\" ||\n !(payload.data instanceof Uint8Array)\n ) {\n throw new Error(\n \"YjsSubstrate.merge expects binary-encoded payloads. \" +\n \"If you recently switched CRDT backends, stale clients may be sending incompatible data.\",\n )\n }\n // Stash origin for the event bridge to pick up\n pendingMergeOrigin = origin\n try {\n Y.applyUpdate(doc, payload.data, origin ?? \"remote\")\n } finally {\n pendingMergeOrigin = undefined\n }\n // That's it — the observeDeep handler bridges events to the\n // changefeed via executeBatch.\n },\n }\n\n // --- Event bridge (registered once at construction) ---\n\n rootMap.observeDeep((events, transaction) => {\n // Ignore our own transactions (changefeed already captured via wrappedPrepare)\n if (transaction.origin === KYNETA_ORIGIN) {\n return\n }\n\n // Convert Yjs events → kyneta Ops\n const ops = eventsToOps(events, schema)\n if (ops.length === 0) {\n return\n }\n\n // Determine origin: prefer stashed kyneta origin (from merge),\n // fall back to the transaction's origin if it's a string.\n const origin =\n pendingMergeOrigin ??\n (typeof transaction.origin === \"string\" ? transaction.origin : undefined)\n\n // Lazily ensure the context is built\n const ctx = substrate.context()\n\n // Feed through executeBatch for changefeed delivery.\n // The inOurTransaction guard prevents prepare/onFlush from doing\n // Yjs-side work — the changes are already applied by Yjs.\n inOurTransaction = true\n try {\n executeBatch(ctx, ops, origin)\n } finally {\n inOurTransaction = false\n }\n })\n\n return substrate as Substrate<YjsVersion>\n}\n\n// ---------------------------------------------------------------------------\n// yjsSubstrateFactory — SubstrateFactory<YjsVersion>\n// ---------------------------------------------------------------------------\n\n/**\n * Factory for constructing Yjs-backed substrates.\n *\n * - `create(schema)` — creates a fresh Y.Doc with empty containers\n * matching the schema structure. No seed data — initial content\n * should be applied via `change()` after construction.\n * - `fromEntirety(payload, schema)` — creates a Y.Doc from an entirety\n * payload, returns a substrate.\n * - `parseVersion(serialized)` — deserializes a YjsVersion.\n */\n// ---------------------------------------------------------------------------\n// yjsReplicaFactory — ReplicaFactory<YjsVersion>\n// ---------------------------------------------------------------------------\n\n/**\n * Schema-free replica factory for Yjs substrates.\n *\n * Constructs headless `Replica<YjsVersion>` instances backed by bare\n * `Y.Doc`s — no schema walking, no container initialization, no\n * Reader, no event bridge, no changefeed. Just the CRDT runtime\n * with version tracking and export/merge.\n *\n * Used by conduit participants (stores, routing servers)\n * that need to accumulate state, compute per-peer deltas, and compact\n * storage without ever interpreting document fields.\n */\nexport function createYjsReplica(doc: Y.Doc): Replica<YjsVersion> {\n let currentDoc = doc\n let currentBase: YjsVersion = new YjsVersion(\n Y.encodeStateVector(new Y.Doc()),\n )\n\n return {\n get [BACKING_DOC]() {\n return currentDoc\n },\n\n version(): YjsVersion {\n return new YjsVersion(Y.encodeStateVector(currentDoc))\n },\n\n baseVersion(): YjsVersion {\n return currentBase\n },\n\n advance(to: YjsVersion): void {\n const baseCmp = currentBase.compare(to)\n if (baseCmp === \"ahead\") {\n throw new Error(\"advance(): target is behind base version\")\n }\n const currentCmp = to.compare(this.version())\n if (currentCmp === \"ahead\") {\n throw new Error(\"advance(): target is ahead of current version\")\n }\n\n // Yjs can only do full projection (to = version).\n // For any to < version, it's a no-op — undershoot contract.\n if (currentCmp !== \"equal\") return\n\n // Full projection: create a new doc with current state, no history.\n const update = Y.encodeStateAsUpdate(currentDoc)\n const newDoc = new Y.Doc()\n Y.applyUpdate(newDoc, update)\n currentDoc = newDoc\n currentBase = new YjsVersion(Y.encodeStateVector(currentDoc))\n },\n\n exportEntirety(): SubstratePayload {\n return {\n kind: \"entirety\",\n encoding: \"binary\",\n data: Y.encodeStateAsUpdate(currentDoc),\n }\n },\n\n exportSince(since: YjsVersion): SubstratePayload | null {\n try {\n const bytes = Y.encodeStateAsUpdate(currentDoc, since.sv)\n return { kind: \"since\", encoding: \"binary\", data: bytes }\n } catch {\n return null\n }\n },\n\n merge(payload: SubstratePayload, _origin?: string): void {\n if (\n payload.encoding !== \"binary\" ||\n !(payload.data instanceof Uint8Array)\n ) {\n throw new Error(\n \"YjsReplica.merge expects binary-encoded payloads. \" +\n \"If you recently switched CRDT backends, stale clients may be sending incompatible data.\",\n )\n }\n Y.applyUpdate(currentDoc, payload.data)\n },\n } as Replica<YjsVersion>\n}\n\nexport const yjsReplicaFactory: ReplicaFactory<YjsVersion> = {\n replicaType: [\"yjs\", 1, 0] as const,\n\n createEmpty(): Replica<YjsVersion> {\n return createYjsReplica(new Y.Doc())\n },\n\n fromEntirety(payload: SubstratePayload): Replica<YjsVersion> {\n if (\n payload.encoding !== \"binary\" ||\n !(payload.data instanceof Uint8Array)\n ) {\n throw new Error(\n \"YjsReplicaFactory.fromEntirety only supports binary-encoded payloads\",\n )\n }\n const doc = new Y.Doc()\n Y.applyUpdate(doc, payload.data)\n return createYjsReplica(doc)\n },\n\n parseVersion(serialized: string): YjsVersion {\n return YjsVersion.parse(serialized)\n },\n}\n\n// ---------------------------------------------------------------------------\n// yjsSubstrateFactory — SubstrateFactory<YjsVersion>\n// ---------------------------------------------------------------------------\n\nexport const yjsSubstrateFactory: SubstrateFactory<YjsVersion> = {\n replica: yjsReplicaFactory,\n\n createReplica(): Replica<YjsVersion> {\n // Default random clientID — safe for hydration (no local writes).\n return createYjsReplica(new Y.Doc())\n },\n\n upgrade(\n replica: Replica<YjsVersion>,\n schema: SchemaNode,\n ): Substrate<YjsVersion> {\n const doc = (replica as any)[BACKING_DOC] as Y.Doc\n // No identity injection for the standalone factory (no peerId).\n // Conditional ensureContainers: skip fields that already exist\n // from hydrated state.\n ensureContainers(doc, schema, true)\n return createYjsSubstrate(doc, schema)\n },\n\n create(schema: SchemaNode): Substrate<YjsVersion> {\n // Fresh doc — unconditional ensureContainers (nothing to conflict with).\n const doc = new Y.Doc()\n ensureContainers(doc, schema)\n return createYjsSubstrate(doc, schema)\n },\n\n fromEntirety(\n payload: SubstratePayload,\n schema: SchemaNode,\n ): Substrate<YjsVersion> {\n // Two-phase path: createReplica → merge → upgrade\n const replica = this.createReplica()\n replica.merge(payload)\n return this.upgrade(replica, schema)\n },\n\n parseVersion(serialized: string): YjsVersion {\n return YjsVersion.parse(serialized)\n },\n}\n","// change-mapping — bidirectional change mapping between kyneta and Yjs.\n//\n// Two directions:\n//\n// 1. kyneta → Yjs (`applyChangeToYjs`): Resolves the target Yjs shared\n// type at a path, then applies the change imperatively via Yjs API.\n// No intermediate diff format — direct imperative mutations.\n//\n// 2. Yjs → kyneta (`eventsToOps`): Converts `observeDeep` events into\n// kyneta `Op[]` for changefeed delivery. Each Y.YEvent maps to one Op\n// with a path derived from `event.path` (relative to the observed root\n// Y.Map) and a Change derived from the event's delta/keys.\n//\n// Structured inserts use populate-then-attach order: new shared types\n// are fully populated before being inserted into their parent container.\n// This produces a single observeDeep event with the complete struct,\n// rather than a cascade of child MapChange events.\n\nimport type {\n ChangeBase,\n IncrementChange,\n MapChange,\n Op,\n Path,\n ReplaceChange,\n Schema as SchemaNode,\n SequenceChange,\n SequenceInstruction,\n TextChange,\n TextInstruction,\n} from \"@kyneta/schema\"\nimport { advanceSchema, expandMapOpsToLeaves, KIND, RawPath } from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\nimport { resolveYjsType } from \"./yjs-resolve.js\"\n\n// ---------------------------------------------------------------------------\n// Direction 1: kyneta → Yjs (`applyChangeToYjs`)\n// ---------------------------------------------------------------------------\n\n/**\n * Apply a kyneta Change to the Yjs shared type tree imperatively.\n *\n * Resolves the target shared type at `path`, then applies the change\n * via the appropriate Yjs API. Must be called within a `doc.transact()`\n * for atomicity and correct event batching.\n *\n * @param rootMap - The root `Y.Map` obtained via `doc.getMap(\"root\")`\n * @param rootSchema - The root document schema\n * @param path - The path to the target\n * @param change - The kyneta Change to apply\n */\nexport function applyChangeToYjs(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n change: ChangeBase,\n): void {\n switch (change.type) {\n case \"text\":\n applyTextChange(rootMap, rootSchema, path, change as TextChange)\n return\n\n case \"sequence\":\n applySequenceChange(rootMap, rootSchema, path, change as SequenceChange)\n return\n\n case \"map\":\n applyMapChange(rootMap, rootSchema, path, change as MapChange)\n return\n\n case \"replace\":\n applyReplaceChange(rootMap, rootSchema, path, change as ReplaceChange)\n return\n\n case \"increment\":\n throw new Error(\n `Yjs substrate does not support \"${change.type}\" changes. ` +\n `Counter requires a CRDT backend that supports counters (e.g. Loro). ` +\n `Attempted IncrementChange with amount=${(change as IncrementChange).amount} at path [${pathToString(path)}].`,\n )\n\n case \"tree\":\n throw new Error(\n `Yjs substrate does not support \"${change.type}\" changes. ` +\n `Tree requires a CRDT backend that supports trees (e.g. Loro). ` +\n `Attempted TreeChange at path [${pathToString(path)}].`,\n )\n\n default:\n throw new Error(\n `applyChangeToYjs: unsupported change type \"${change.type}\"`,\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Text change\n// ---------------------------------------------------------------------------\n\nfunction applyTextChange(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n change: TextChange,\n): void {\n const resolved = resolveYjsType(rootMap, rootSchema, path)\n if (!(resolved instanceof Y.Text)) {\n throw new Error(\n `applyChangeToYjs: TextChange target at path [${pathToString(path)}] is not a Y.Text`,\n )\n }\n\n // Yjs Y.Text.applyDelta uses the Quill Delta format, which is\n // structurally identical to kyneta TextInstruction[].\n resolved.applyDelta(change.instructions as any)\n}\n\n// ---------------------------------------------------------------------------\n// Sequence change\n// ---------------------------------------------------------------------------\n\nfunction applySequenceChange(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n change: SequenceChange,\n): void {\n const resolved = resolveYjsType(rootMap, rootSchema, path)\n if (!(resolved instanceof Y.Array)) {\n throw new Error(\n `applyChangeToYjs: SequenceChange target at path [${pathToString(path)}] is not a Y.Array`,\n )\n }\n\n // Resolve the item schema for structured insert detection\n const targetSchema = resolveSchemaAtPath(rootSchema, path)\n const itemSchema = getItemSchema(targetSchema)\n\n let cursor = 0\n for (const instruction of change.instructions) {\n if (\"retain\" in instruction) {\n cursor += instruction.retain\n } else if (\"delete\" in instruction) {\n resolved.delete(cursor, instruction.delete)\n // cursor stays — deleted items shift remaining items down\n } else if (\"insert\" in instruction) {\n const items = instruction.insert as readonly unknown[]\n const yjsItems = items.map(item =>\n maybeCreateSharedType(item, itemSchema),\n )\n resolved.insert(cursor, yjsItems)\n cursor += items.length\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Map change\n// ---------------------------------------------------------------------------\n\nfunction applyMapChange(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n change: MapChange,\n): void {\n const resolved = resolveYjsType(rootMap, rootSchema, path)\n if (!(resolved instanceof Y.Map)) {\n throw new Error(\n `applyChangeToYjs: MapChange target at path [${pathToString(path)}] is not a Y.Map`,\n )\n }\n\n // Resolve the schema at this path for structured value detection\n const targetSchema = resolveSchemaAtPath(rootSchema, path)\n\n // Apply deletes first\n if (change.delete) {\n for (const key of change.delete) {\n resolved.delete(key)\n }\n }\n\n // Apply sets\n if (change.set) {\n for (const [key, value] of Object.entries(change.set)) {\n const fieldSchema = getFieldSchema(targetSchema, key)\n const yjsValue = maybeCreateSharedType(value, fieldSchema)\n resolved.set(key, yjsValue)\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Replace change\n// ---------------------------------------------------------------------------\n\nfunction applyReplaceChange(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n change: ReplaceChange,\n): void {\n if (path.length === 0) {\n throw new Error(\n \"applyChangeToYjs: ReplaceChange at root path is not supported\",\n )\n }\n\n // Target the parent container, using the last segment to identify\n // which child to replace.\n const lastSeg = path.segments[path.segments.length - 1]!\n const parentPath = path.slice(0, -1)\n const parent = resolveYjsType(rootMap, rootSchema, parentPath)\n\n const resolved = lastSeg.resolve()\n if (parent instanceof Y.Map && lastSeg.role === \"key\") {\n // Resolve schema for the target field for structured value detection\n const targetSchema = resolveSchemaAtPath(rootSchema, path)\n const yjsValue = maybeCreateSharedType(change.value, targetSchema)\n parent.set(resolved as string, yjsValue)\n } else if (parent instanceof Y.Array && lastSeg.role === \"index\") {\n const targetSchema = resolveSchemaAtPath(rootSchema, path)\n const yjsValue = maybeCreateSharedType(change.value, targetSchema)\n parent.delete(resolved as number, 1)\n parent.insert(resolved as number, [yjsValue])\n } else {\n throw new Error(\n `applyChangeToYjs: ReplaceChange parent at path [${pathToString(parentPath)}] ` +\n `is not a Y.Map or Y.Array (got ${typeof parent})`,\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Structured value creation (populate-then-attach pattern)\n// ---------------------------------------------------------------------------\n\n/**\n * If the schema says the value should be a shared type (product → Y.Map,\n * sequence → Y.Array, text → Y.Text), create and populate it.\n * Otherwise return the plain value as-is.\n *\n * Uses populate-then-attach: the new shared type is fully populated\n * before being returned for insertion into its parent.\n */\nfunction maybeCreateSharedType(\n value: unknown,\n schema: SchemaNode | undefined,\n): unknown {\n if (schema === undefined) return value\n\n switch (schema[KIND]) {\n // First-class text → Y.Text\n case \"text\": {\n const text = new Y.Text()\n if (typeof value === \"string\" && value.length > 0) {\n text.insert(0, value)\n }\n return text\n }\n\n case \"product\": {\n if (\n value === null ||\n value === undefined ||\n typeof value !== \"object\" ||\n Array.isArray(value)\n ) {\n return value\n }\n return createStructuredMap(value as Record<string, unknown>, schema)\n }\n\n case \"sequence\": {\n if (!Array.isArray(value)) return value\n const arr = new Y.Array()\n const itemSchema = schema.item\n const items = (value as unknown[]).map(item =>\n maybeCreateSharedType(item, itemSchema),\n )\n arr.insert(0, items)\n return arr\n }\n\n case \"map\": {\n if (\n value === null ||\n value === undefined ||\n typeof value !== \"object\" ||\n Array.isArray(value)\n ) {\n return value\n }\n const map = new Y.Map()\n const valueSchema = schema.item\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n map.set(k, maybeCreateSharedType(v, valueSchema))\n }\n return map\n }\n\n // Unsupported first-class CRDT types — should not reach here\n // (rejected at bind time by caps check)\n case \"counter\":\n case \"set\":\n case \"tree\":\n case \"movable\":\n throw new Error(\n `Yjs substrate does not support [KIND]=\"${schema[KIND]}\". ` +\n `This should have been caught at bind() time.`,\n )\n\n default:\n // Scalar, sum — return as plain value\n return value\n }\n}\n\n/**\n * Create a Y.Map from a plain object, recursively creating nested\n * shared types as guided by the product schema.\n *\n * Follows populate-then-attach: fully populates the map before the\n * caller inserts it into a parent container.\n */\nfunction createStructuredMap(\n obj: Record<string, unknown>,\n productSchema: SchemaNode,\n): Y.Map<any> {\n const map = new Y.Map()\n\n if (productSchema[KIND] !== \"product\") {\n // Fallback: set all values as plain\n for (const [key, val] of Object.entries(obj)) {\n map.set(key, val)\n }\n return map\n }\n\n // Process fields present in the value object\n for (const [key, val] of Object.entries(obj)) {\n if (val === undefined) continue\n const fieldSchema = productSchema.fields[key]\n const yjsVal = fieldSchema ? maybeCreateSharedType(val, fieldSchema) : val\n map.set(key, yjsVal)\n }\n\n // Create shared types for first-class CRDT fields declared in the schema\n // but missing from the value object. This ensures Yjs containers\n // exist for later mutation (e.g. .insert() on a text field inside\n // a struct inside a record/list).\n for (const [key, fieldSchema] of Object.entries(\n productSchema.fields as Record<string, SchemaNode>,\n )) {\n if (key in obj) continue // already processed above\n if (fieldSchema[KIND] === \"text\") {\n map.set(key, new Y.Text())\n }\n }\n\n return map\n}\n\n// ---------------------------------------------------------------------------\n// Direction 2: Yjs → kyneta (`eventsToOps`)\n// ---------------------------------------------------------------------------\n\n/**\n * Convert `observeDeep` events into kyneta `Op[]` for changefeed delivery.\n *\n * Each `Y.YEvent` in the array maps to one Op with:\n * - `path`: derived from `event.path` (relative to the observed root Y.Map)\n * - `change`: derived from the event's delta/keys based on target type\n *\n * `event.path` in `observeDeep` is relative to the observed shared type.\n * Since we observe `rootMap` (the single root Y.Map), paths map directly\n * to kyneta `PathSegment[]`.\n *\n * @param events - The events from the `observeDeep` callback\n */\nexport function eventsToOps(events: Y.YEvent<any>[], schema: SchemaNode): Op[] {\n const ops: Op[] = []\n\n for (const event of events) {\n const kynetaPath = yjsPathToKynetaPath(event.path)\n const change = eventToChange(event)\n if (change) {\n ops.push({ path: kynetaPath, change })\n }\n }\n\n return expandMapOpsToLeaves(ops, schema)\n}\n\n// ---------------------------------------------------------------------------\n// Yjs path → kyneta Path conversion\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a Yjs event path (array of string | number) to a kyneta Path.\n *\n * `event.path` from `observeDeep` is relative to the observed type.\n * Strings become key segments, numbers become index segments.\n */\nfunction yjsPathToKynetaPath(yjsPath: (string | number)[]): RawPath {\n let path = RawPath.empty\n for (const segment of yjsPath) {\n if (typeof segment === \"string\") {\n path = path.field(segment)\n } else if (typeof segment === \"number\") {\n path = path.item(segment)\n }\n }\n return path\n}\n\n// ---------------------------------------------------------------------------\n// Per-type event → Change converters\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a single Yjs event into a kyneta Change.\n * Returns null for event types we can't map.\n */\nfunction eventToChange(event: Y.YEvent<any>): ChangeBase | null {\n if (event.target instanceof Y.Text) {\n return textEventToChange(event)\n }\n if (event.target instanceof Y.Array) {\n return arrayEventToChange(event)\n }\n if (event.target instanceof Y.Map) {\n return mapEventToChange(event)\n }\n return null\n}\n\n/**\n * Y.Text event → TextChange.\n *\n * `event.delta` uses the Quill Delta format, structurally identical to\n * kyneta `TextInstruction[]`. We strip the `attributes` field (rich text\n * formatting not surfaced by kyneta).\n */\nfunction textEventToChange(event: Y.YEvent<any>): TextChange {\n const instructions: TextInstruction[] = []\n\n for (const delta of event.delta) {\n if (delta.retain !== undefined) {\n instructions.push({ retain: delta.retain as number })\n } else if (delta.insert !== undefined) {\n instructions.push({ insert: delta.insert as string })\n } else if (delta.delete !== undefined) {\n instructions.push({ delete: delta.delete as number })\n }\n }\n\n return { type: \"text\", instructions }\n}\n\n/**\n * Y.Array event → SequenceChange.\n *\n * `event.changes.delta` provides the same cursor-based ops as kyneta\n * SequenceInstruction[]. Container values (Y.Map, Y.Array) in insert\n * arrays are converted to plain objects via `.toJSON()`.\n */\nfunction arrayEventToChange(event: Y.YEvent<any>): SequenceChange {\n const instructions: SequenceInstruction[] = []\n\n for (const delta of event.changes.delta) {\n if (delta.retain !== undefined) {\n instructions.push({ retain: delta.retain as number })\n } else if (delta.delete !== undefined) {\n instructions.push({ delete: delta.delete as number })\n } else if (delta.insert !== undefined) {\n const items = (delta.insert as unknown[]).map((item: unknown) =>\n extractEventValue(item),\n )\n instructions.push({ insert: items })\n }\n }\n\n return { type: \"sequence\", instructions }\n}\n\n/**\n * Y.Map event → MapChange.\n *\n * `event.changes.keys` is a `Map<string, { action: 'add'|'update'|'delete', ... }>`.\n * - `action: 'add'|'update'` → `set[key] = map.get(key)`\n * - `action: 'delete'` → `delete.push(key)`\n */\nfunction mapEventToChange(event: Y.YEvent<any>): MapChange | null {\n const set: Record<string, unknown> = {}\n const deleteKeys: string[] = []\n let hasSet = false\n let hasDelete = false\n\n const target = event.target as Y.Map<any>\n\n event.changes.keys.forEach((change: { action: string }, key: string) => {\n if (change.action === \"add\" || change.action === \"update\") {\n const value = target.get(key)\n set[key] = extractEventValue(value)\n hasSet = true\n } else if (change.action === \"delete\") {\n deleteKeys.push(key)\n hasDelete = true\n }\n })\n\n if (!hasSet && !hasDelete) return null\n\n return {\n type: \"map\",\n ...(hasSet ? { set } : {}),\n ...(hasDelete ? { delete: deleteKeys } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Value extraction from Yjs events\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a Yjs value from an event into a plain value.\n * Container values (Y.Map, Y.Array, Y.Text) → `.toJSON()`.\n * Plain values → returned as-is.\n */\nfunction extractEventValue(value: unknown): unknown {\n if (value instanceof Y.Map) return value.toJSON()\n if (value instanceof Y.Array) return value.toJSON()\n if (value instanceof Y.Text) return value.toJSON()\n return value\n}\n\n// ---------------------------------------------------------------------------\n// Schema helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the schema at a given path by walking through advanceSchema.\n */\nfunction resolveSchemaAtPath(rootSchema: SchemaNode, path: Path): SchemaNode {\n let schema = rootSchema\n for (const seg of path.segments) {\n schema = advanceSchema(schema, seg)\n }\n return schema\n}\n\n/**\n * Get the item schema from a sequence schema, if available.\n */\nfunction getItemSchema(schema: SchemaNode): SchemaNode | undefined {\n if (schema[KIND] === \"sequence\") return schema.item\n if (schema[KIND] === \"movable\") return schema.item\n return undefined\n}\n\n/**\n * Get the field schema from a product or map schema for a given key.\n */\nfunction getFieldSchema(\n schema: SchemaNode,\n key: string,\n): SchemaNode | undefined {\n if (schema[KIND] === \"product\") {\n return schema.fields[key]\n }\n if (schema[KIND] === \"map\") {\n return schema.item\n }\n if (schema[KIND] === \"set\") {\n return schema.item\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// Path formatting\n// ---------------------------------------------------------------------------\n\nfunction pathToString(path: Path): string {\n return path.segments.map(seg => String(seg.resolve())).join(\".\")\n}","// yjs-resolve — Yjs-specific path resolution.\n//\n// Implements stepIntoYjs and resolveYjsType for schema-guided\n// navigation of the Yjs shared type tree.\n//\n// resolveYjsType is a left-fold over path segments, accumulating\n// (currentType, currentSchema) at each step. This mirrors how\n// resolveContainer works for Loro — but uses `instanceof` for\n// runtime type discrimination instead of Loro's `.kind()` method.\n//\n// Root container strategy: All schema fields are children of a single\n// root `Y.Map` obtained via `doc.getMap(\"root\")`. This root map holds\n// shared types (Y.Text, Y.Array, Y.Map) and plain values uniformly.\n// Using a single root Y.Map enables one `observeDeep` call that\n// captures all mutations with correct relative paths.\n\nimport type { Path, Schema as SchemaNode, Segment } from \"@kyneta/schema\"\nimport { advanceSchema, KIND } from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\n\n// ---------------------------------------------------------------------------\n// stepIntoYjs — single step of the fold\n// ---------------------------------------------------------------------------\n\n/**\n * Navigate one step deeper into the Yjs shared type tree.\n *\n * Uses `instanceof` for runtime type discrimination:\n * - `Y.Map` → `.get(key)`\n * - `Y.Array` → `.get(index)`\n * - `Y.Text` → terminal (cannot step further)\n * - Plain value → terminal (return `undefined`)\n *\n * @param current - The current position (a Yjs shared type or plain value)\n * @param segment - The path segment to follow\n */\nexport function stepIntoYjs(current: unknown, segment: Segment): unknown {\n const resolved = segment.resolve()\n\n if (current instanceof Y.Map) {\n return current.get(resolved as string)\n }\n\n if (current instanceof Y.Array) {\n return current.get(resolved as number)\n }\n\n if (current instanceof Y.Text) {\n throw new Error(`yjs-resolve: cannot step into Y.Text`)\n }\n\n // Plain value — terminal, cannot step further\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// resolveYjsType — full path resolution via left-fold\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a Yjs shared type (or plain value) at the given path.\n *\n * Left-folds over path segments using `advanceSchema` for pure schema\n * descent and `stepIntoYjs` for Yjs-specific navigation.\n *\n * Returns the Yjs shared type or plain value at the terminal position.\n * For an empty path, returns the root map itself.\n *\n * @param rootMap - The root `Y.Map` obtained via `doc.getMap(\"root\")`\n * @param rootSchema - The root document schema\n * @param path - The path to resolve\n */\nexport function resolveYjsType(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n): unknown {\n let current: unknown = rootMap\n let schema = rootSchema\n\n for (let i = 0; i < path.length; i++) {\n const seg = path.segments[i]!\n const nextSchema = advanceSchema(schema, seg)\n\n // For the first segment, we step into the root map directly.\n // For subsequent segments, we use stepIntoYjs on the current value.\n current = stepIntoYjs(current, seg)\n schema = nextSchema\n }\n\n return current\n}\n","// populate — Yjs container creation from schema structure.\n//\n// Ensures that the correct Yjs shared types (Y.Text, Y.Array, Y.Map)\n// exist in a Y.Doc's root map to match the schema structure, and that\n// scalar/sum fields are initialized with Zero.structural defaults.\n//\n// This is NOT seed data — it's structural completeness, matching what\n// PlainSubstrate does when it initializes its store with Zero.structural.\n// The Yjs store reader expects to find values at every schema path;\n// without this, unset scalars would return undefined instead of their\n// type-correct zero (\"\", 0, false).\n//\n// Root container strategy: All schema fields are children of a single\n// root `Y.Map` obtained via `doc.getMap(\"root\")`. This root map holds\n// shared types (Y.Text, Y.Array, Y.Map) and plain value slots uniformly.\n\nimport type { Schema as SchemaNode } from \"@kyneta/schema\"\nimport { KIND, STRUCTURAL_YJS_CLIENT_ID, Zero } from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\n\n// ---------------------------------------------------------------------------\n// ensureContainers — top-level entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Ensure that a Y.Doc's root map contains the correct Yjs shared types\n * matching the schema structure.\n *\n * Obtains the root map via `doc.getMap(\"root\")`, reads the root product\n * schema's fields, and creates empty containers for each field within a\n * single `doc.transact()` call for atomicity.\n *\n * When `conditional` is true, fields that already exist in the root map\n * are skipped. This is the correct mode after hydration — containers\n * present from stored state must not be overwritten (each `rootMap.set()`\n * is a CRDT write that advances the version vector and may conflict\n * with stored operations).\n *\n * When `conditional` is false (default), all fields are created\n * unconditionally. This is the correct mode for fresh documents.\n *\n * **Structural identity:** This function temporarily sets `doc.clientID`\n * to `STRUCTURAL_YJS_CLIENT_ID` (0) for the duration of container creation,\n * then restores the caller's clientID. This produces byte-identical\n * structural ops across all peers, enabling Yjs deduplication on merge.\n *\n * @param doc - The Y.Doc to prepare\n * @param schema - The root document schema (a ProductSchema)\n * @param conditional - If true, skip fields that already exist in the root map.\n * Context: jj:smmulzkm (two-phase substrate construction)\n */\nexport function ensureContainers(\n doc: Y.Doc,\n schema: SchemaNode,\n conditional = false,\n): void {\n const rootMap = doc.getMap(\"root\")\n\n if (schema[KIND] !== \"product\") {\n return\n }\n\n // Switch to structural identity for deterministic container creation.\n // All peers produce byte-identical structural ops at clientID 0.\n const savedClientID = doc.clientID\n doc.clientID = STRUCTURAL_YJS_CLIENT_ID\n\n try {\n doc.transact(() => {\n for (const [key, fieldSchema] of Object.entries(schema.fields).sort(\n ([a], [b]) => a.localeCompare(b),\n )) {\n if (conditional && rootMap.has(key)) continue\n ensureRootField(rootMap, key, fieldSchema as SchemaNode)\n }\n })\n } finally {\n // Restore the caller's identity for application writes.\n doc.clientID = savedClientID\n }\n}\n\n// ---------------------------------------------------------------------------\n// ensureRootField — create a single root-level container\n// ---------------------------------------------------------------------------\n\n/**\n * Ensure a root-level Yjs shared type exists for a schema field.\n *\n * Dispatches on `[KIND]`:\n * - `\"text\"` → empty Y.Text\n * - `\"product\"` → empty Y.Map (recursive for nested products)\n * - `\"sequence\"` → empty Y.Array\n * - `\"map\"` → empty Y.Map\n * - `\"scalar\"` / `\"sum\"` → Zero.structural default\n * - `\"counter\"` / `\"set\"` / `\"tree\"` / `\"movable\"` → throw (not supported by Yjs)\n */\nfunction ensureRootField(\n rootMap: Y.Map<unknown>,\n key: string,\n fieldSchema: SchemaNode,\n): void {\n switch (fieldSchema[KIND]) {\n case \"text\":\n rootMap.set(key, new Y.Text())\n return\n\n case \"product\":\n rootMap.set(key, ensureMapContainers(fieldSchema))\n return\n\n case \"sequence\":\n rootMap.set(key, new Y.Array())\n return\n\n case \"map\":\n rootMap.set(key, new Y.Map())\n return\n\n case \"scalar\":\n case \"sum\": {\n // Plain values don't need shared type containers, but they DO\n // need structural zero defaults so the store reader returns\n // type-correct values (e.g. \"\" not undefined for strings).\n const zero = Zero.structural(fieldSchema)\n if (zero !== undefined) {\n rootMap.set(key, zero)\n }\n return\n }\n\n case \"counter\":\n case \"set\":\n case \"tree\":\n case \"movable\":\n throw new Error(\n `Yjs substrate does not support [KIND]=\"${fieldSchema[KIND]}\". ` +\n `Supported kinds: text, product, sequence, map, scalar, sum. ` +\n `Encountered unsupported kind at root field \"${key}\".`,\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// ensureMapContainers — recursively create nested Y.Map structure\n// ---------------------------------------------------------------------------\n\n/**\n * Create an empty Y.Map with nested shared type children matching\n * the product schema's field structure.\n *\n * Only creates containers for fields that require Yjs shared types\n * (text → Y.Text, product → Y.Map, sequence → Y.Array, map → Y.Map).\n * Scalar and sum fields are set to their structural zero defaults.\n */\nfunction ensureMapContainers(schema: SchemaNode): Y.Map<unknown> {\n const map = new Y.Map()\n\n if (schema[KIND] !== \"product\") return map\n\n for (const [key, fieldSchema] of Object.entries(\n schema.fields as Record<string, SchemaNode>,\n ).sort(([a], [b]) => a.localeCompare(b))) {\n switch (fieldSchema[KIND]) {\n case \"text\":\n map.set(key, new Y.Text())\n break\n\n case \"product\":\n map.set(key, ensureMapContainers(fieldSchema))\n break\n\n case \"sequence\":\n map.set(key, new Y.Array())\n break\n\n case \"map\":\n map.set(key, new Y.Map())\n break\n\n case \"scalar\":\n case \"sum\": {\n const zero = Zero.structural(fieldSchema)\n if (zero !== undefined) {\n map.set(key, zero)\n }\n break\n }\n\n case \"counter\":\n case \"set\":\n case \"tree\":\n case \"movable\":\n throw new Error(\n `Yjs substrate does not support [KIND]=\"${fieldSchema[KIND]}\". ` +\n `Supported kinds: text, product, sequence, map, scalar, sum. ` +\n `Encountered unsupported kind at nested field \"${key}\".`,\n )\n }\n }\n\n return map\n}","// store-reader — YjsReader implementation.\n//\n// Implements Reader via schema-guided live navigation of the\n// Yjs shared type tree. Each read operation resolves the shared type\n// at the given path using resolveYjsType, then extracts the\n// appropriate value based on `instanceof` discrimination.\n//\n// Y.Text → .toJSON() (string), Y.Map → .toJSON() (plain object),\n// Y.Array → .toJSON() (plain array), plain values → as-is.\n\nimport type { Path, Reader, Schema as SchemaNode } from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\nimport { resolveYjsType } from \"./yjs-resolve.js\"\n\n// ---------------------------------------------------------------------------\n// Value extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Extract a plain value from a Yjs shared type or return a plain value as-is.\n *\n * - Y.Text → `.toJSON()` (string)\n * - Y.Map → `.toJSON()` (plain object snapshot — for product/map reads)\n * - Y.Array → `.toJSON()` (plain array snapshot)\n * - Plain values (string, number, boolean, null) → returned as-is\n */\nfunction extractValue(resolved: unknown): unknown {\n if (resolved instanceof Y.Text) {\n return resolved.toJSON()\n }\n if (resolved instanceof Y.Map) {\n return resolved.toJSON()\n }\n if (resolved instanceof Y.Array) {\n return resolved.toJSON()\n }\n // Plain scalar value (string, number, boolean, null, etc.)\n return resolved\n}\n\n// ---------------------------------------------------------------------------\n// yjsReader\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a Reader that navigates the Yjs shared type tree live,\n * using the schema as a type witness to determine navigation at each\n * path segment.\n *\n * The reader is a live view — mutations to the underlying Y.Doc\n * (via `doc.transact()`, or `Y.applyUpdate()`) are immediately\n * visible through the reader.\n *\n * Internally obtains the root map via `doc.getMap(\"root\")`.\n *\n * @param doc - The Y.Doc to read from.\n * @param schema - The root schema for the document.\n */\nexport function yjsReader(doc: Y.Doc, schema: SchemaNode): Reader {\n const rootMap = doc.getMap(\"root\")\n\n return {\n read(path: Path): unknown {\n if (path.length === 0) {\n // Root read — return the full root map as JSON\n return rootMap.toJSON()\n }\n const resolved = resolveYjsType(rootMap, schema, path)\n return extractValue(resolved)\n },\n\n arrayLength(path: Path): number {\n const resolved = resolveYjsType(rootMap, schema, path)\n if (resolved instanceof Y.Array) {\n return resolved.length\n }\n // Graceful fallback for plain array values\n if (Array.isArray(resolved)) {\n return resolved.length\n }\n return 0\n },\n\n keys(path: Path): string[] {\n const resolved = resolveYjsType(rootMap, schema, path)\n if (resolved instanceof Y.Map) {\n return Array.from(resolved.keys())\n }\n // Graceful fallback for plain object values\n if (\n resolved !== null &&\n resolved !== undefined &&\n typeof resolved === \"object\" &&\n !Array.isArray(resolved)\n ) {\n return Object.keys(resolved as Record<string, unknown>)\n }\n return []\n },\n\n hasKey(path: Path, key: string): boolean {\n const resolved = resolveYjsType(rootMap, schema, path)\n if (resolved instanceof Y.Map) {\n return resolved.has(key)\n }\n // Graceful fallback for plain object values\n if (\n resolved !== null &&\n resolved !== undefined &&\n typeof resolved === \"object\" &&\n !Array.isArray(resolved)\n ) {\n return key in (resolved as Record<string, unknown>)\n }\n return false\n },\n }\n}\n","// YjsVersion — Version implementation wrapping Yjs state vectors.\n//\n// Yjs state vectors (`Y.encodeStateVector(doc)`) are the complete peer\n// state used for sync diffing — matching the semantics of kyneta's\n// Version interface.\n//\n// Serialization uses base64-encoded bytes for text-safe embedding in\n// HTML meta tags, script tags, etc.\n//\n// Yjs does not export a state vector comparison function, so we\n// implement standard version-vector partial-order comparison over\n// decoded `Map<number, number>` (clientID → clock) maps ourselves.\n\nimport type { Version } from \"@kyneta/schema\"\nimport { versionVectorMeet } from \"@kyneta/schema\"\nimport { decodeStateVector } from \"yjs\"\n\n// ---------------------------------------------------------------------------\n// Base64 helpers (platform-agnostic, no Node.js Buffer dependency)\n// ---------------------------------------------------------------------------\n\nfunction uint8ArrayToBase64(bytes: Uint8Array): string {\n let binary = \"\"\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]!)\n }\n return btoa(binary)\n}\n\nfunction base64ToUint8Array(base64: string): Uint8Array {\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n\n// ---------------------------------------------------------------------------\n// State vector encoding — manual varint (unsigned LEB128)\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a state vector map to Yjs's binary state vector format.\n *\n * Yjs does not export `encodeStateVector(map)` — only `Y.encodeStateVector(doc)`\n * which requires a full doc. This implements the same binary format directly:\n * `[entryCount: varint, (clientId: varint, clock: varint)*]`\n *\n * Each value is encoded as an unsigned LEB128 varint.\n */\nfunction encodeStateVector(map: Map<number, number>): Uint8Array {\n const bytes: number[] = []\n\n function writeVarUint(value: number): void {\n while (value > 0x7f) {\n bytes.push((value & 0x7f) | 0x80)\n value >>>= 7\n }\n bytes.push(value & 0x7f)\n }\n\n writeVarUint(map.size)\n for (const [clientId, clock] of map) {\n writeVarUint(clientId)\n writeVarUint(clock)\n }\n\n return new Uint8Array(bytes)\n}\n\n// ---------------------------------------------------------------------------\n// YjsVersion\n// ---------------------------------------------------------------------------\n\n/**\n * A Version wrapping a Yjs state vector.\n *\n * State vectors track the complete peer state — which operations from\n * each client have been observed. This is the right abstraction for sync\n * diffing: `exportSince(version)` uses the state vector to compute the\n * minimal update payload via `Y.encodeStateAsUpdate(doc, sv)`.\n *\n * `serialize()` encodes to base64 for text-safe embedding.\n * `compare()` decodes both state vectors and performs standard\n * version-vector partial-order comparison over the client-clock maps.\n */\nexport class YjsVersion implements Version {\n readonly sv: Uint8Array\n\n constructor(sv: Uint8Array) {\n this.sv = sv\n }\n\n /**\n * Serialize the state vector to a base64 string.\n *\n * The encoding is: raw state vector bytes → base64.\n * This is text-safe for embedding in HTML meta tags, URL parameters, etc.\n */\n serialize(): string {\n return uint8ArrayToBase64(this.sv)\n }\n\n /**\n * Compare with another version using version-vector partial order.\n *\n * Decodes both state vectors via `Y.decodeStateVector()` to get\n * `Map<number, number>` (clientID → clock), then compares:\n *\n * - Collect the union of all client IDs from both maps.\n * - For each client, compare clocks (missing client = clock 0).\n * - If all clocks in `this` ≤ `other` and at least one strictly less → `\"behind\"`\n * - If all clocks in `this` ≥ `other` and at least one strictly greater → `\"ahead\"`\n * - If all clocks equal → `\"equal\"`\n * - Otherwise → `\"concurrent\"`\n *\n * Throws if `other` is not a `YjsVersion`.\n */\n compare(other: Version): \"behind\" | \"equal\" | \"ahead\" | \"concurrent\" {\n if (!(other instanceof YjsVersion)) {\n throw new Error(\"YjsVersion can only be compared with another YjsVersion\")\n }\n\n const thisMap = decodeStateVector(this.sv)\n const otherMap = decodeStateVector(other.sv)\n\n // Collect the union of all client IDs\n const allClients = new Set<number>()\n for (const id of thisMap.keys()) allClients.add(id)\n for (const id of otherMap.keys()) allClients.add(id)\n\n let hasLess = false\n let hasGreater = false\n\n for (const clientId of allClients) {\n const thisClock = thisMap.get(clientId) ?? 0\n const otherClock = otherMap.get(clientId) ?? 0\n\n if (thisClock < otherClock) {\n hasLess = true\n }\n if (thisClock > otherClock) {\n hasGreater = true\n }\n\n // Early exit: if we've seen both less and greater, it's concurrent\n if (hasLess && hasGreater) {\n return \"concurrent\"\n }\n }\n\n if (hasLess && !hasGreater) return \"behind\"\n if (hasGreater && !hasLess) return \"ahead\"\n return \"equal\"\n }\n\n /**\n * Greatest lower bound (lattice meet) of two Yjs versions.\n *\n * Decodes both state vectors, computes the component-wise minimum\n * via the shared `versionVectorMeet` utility, and encodes the result\n * back to a Yjs state vector.\n *\n * @throws If `other` is not a `YjsVersion`.\n */\n meet(other: Version): YjsVersion {\n if (!(other instanceof YjsVersion)) {\n throw new Error(\n \"YjsVersion can only be meet'd with another YjsVersion\",\n )\n }\n const thisMap = decodeStateVector(this.sv)\n const otherMap = decodeStateVector(other.sv)\n const result = versionVectorMeet(thisMap, otherMap)\n return new YjsVersion(encodeStateVector(result))\n }\n\n /**\n * Parse a serialized YjsVersion string back into a YjsVersion.\n *\n * The inverse of `serialize()`: base64 → `Uint8Array`.\n */\n static parse(serialized: string): YjsVersion {\n if (serialized === \"\") {\n throw new Error(\"Invalid YjsVersion value: (empty string)\")\n }\n const bytes = base64ToUint8Array(serialized)\n return new YjsVersion(bytes)\n }\n}\n","// sync — sync primitives for YjsSubstrate-backed documents.\n//\n// These functions provide version tracking, entirety export, and merge\n// for documents created via `createYjsDoc` or\n// `createYjsDocFromEntirety`. They discover the substrate via the\n// module-scoped WeakMap in `create.ts`.\n//\n// Unlike PlainSubstrate's sync (which returns Op[] for deltas),\n// YjsSubstrate's sync uses binary SubstratePayload for both entireties\n// and deltas — these are Yjs's native state-as-update bytes.\n\nimport type { SubstratePayload } from \"@kyneta/schema\"\nimport { getSubstrate } from \"./create.js\"\nimport type { YjsVersion } from \"./version.js\"\n\n// ---------------------------------------------------------------------------\n// version — current YjsVersion\n// ---------------------------------------------------------------------------\n\n/**\n * Current version as a `YjsVersion` (wrapping a Yjs state vector).\n *\n * Use `.serialize()` to get a text-safe string for embedding in HTML\n * meta tags, URL parameters, etc.\n *\n * @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.\n * @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.\n */\nexport function version(doc: object): YjsVersion {\n return getSubstrate(doc).version()\n}\n\n// ---------------------------------------------------------------------------\n// exportEntirety — full state for reconstruction\n// ---------------------------------------------------------------------------\n\n/**\n * Export the full substrate entirety — sufficient for a new peer to\n * reconstruct an equivalent document via `createYjsDocFromEntirety()`.\n *\n * Returns a binary `SubstratePayload` (Yjs state-as-update bytes).\n *\n * @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.\n * @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.\n */\nexport function exportEntirety(doc: object): SubstratePayload {\n return getSubstrate(doc).exportEntirety()\n}\n\n// ---------------------------------------------------------------------------\n// exportSince — delta since a version\n// ---------------------------------------------------------------------------\n\n/**\n * Export a delta payload containing all changes since the given version.\n *\n * Returns a binary `SubstratePayload` (Yjs update bytes), or `null`\n * if the delta cannot be computed.\n *\n * ```ts\n * const v0 = version(docA)\n * change(docA, d => d.title.insert(0, \"Hi\"))\n * const delta = exportSince(docA, v0)\n * merge(docB, delta!)\n * ```\n *\n * @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.\n * @param since - The version to diff from.\n * @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.\n */\nexport function exportSince(\n doc: object,\n since: YjsVersion,\n): SubstratePayload | null {\n return getSubstrate(doc).exportSince(since)\n}\n\n// ---------------------------------------------------------------------------\n// merge — apply a delta from another peer\n// ---------------------------------------------------------------------------\n\n/**\n * Import a delta payload into a live document.\n *\n * The payload must have been produced by `exportSince()` or\n * `exportEntirety()` on a compatible document.\n *\n * After import, the changefeed fires for all subscribers — the event\n * bridge handles this automatically.\n *\n * ```ts\n * const delta = exportSince(docA, sinceVersion)\n * merge(docB, delta!, \"sync\")\n * ```\n *\n * @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.\n * @param payload - The delta or entirety payload to merge.\n * @param origin - Optional provenance tag for the changeset.\n * @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.\n */\nexport function merge(\n doc: object,\n payload: SubstratePayload,\n origin?: string,\n): void {\n getSubstrate(doc).merge(payload, origin)\n}\n","// bind-yjs — Yjs CRDT substrate namespace and factory.\n//\n// Provides the `yjs` substrate namespace (`yjs.bind()`, `yjs.replica()`,\n// `yjs.unwrap()`) and the internal factory builder that injects a\n// deterministic numeric Yjs clientID derived from the exchange's peerId.\n//\n// Yjs clientID is a uint32 number. We use FNV-1a hash truncated to\n// 32 bits, mirroring the Loro binding's hashPeerId pattern but\n// targeting Yjs's number type (not Loro's bigint/53-bit PeerID).\n//\n// Usage:\n// import { yjs } from \"@kyneta/yjs-schema\"\n//\n// const TodoDoc = yjs.bind(Schema.struct({\n// title: Schema.text(),\n// items: Schema.list(Schema.struct({ name: Schema.string() })),\n// }))\n//\n// const doc = exchange.get(\"my-doc\", TodoDoc)\n\nimport type {\n CrdtStrategy,\n Replica,\n Schema as SchemaNode,\n Substrate,\n SubstrateFactory,\n SubstrateNamespace,\n SubstratePayload,\n} from \"@kyneta/schema\"\nimport {\n BACKING_DOC,\n createSubstrateNamespace,\n STRUCTURAL_YJS_CLIENT_ID,\n unwrap,\n} from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\nimport { ensureContainers } from \"./populate.js\"\nimport {\n createYjsReplica,\n createYjsSubstrate,\n yjsReplicaFactory,\n} from \"./substrate.js\"\nimport { YjsVersion } from \"./version.js\"\n\n// ---------------------------------------------------------------------------\n// Peer ID hashing — deterministic string → numeric Yjs clientID\n// ---------------------------------------------------------------------------\n\n/**\n * Hash a string peerId to a deterministic numeric Yjs clientID.\n *\n * Yjs clientIDs are unsigned 32-bit integers. We use FNV-1a hash to\n * produce a deterministic uint32 from the string peerId.\n *\n * The hash is deterministic: the same string always produces the same\n * numeric clientID, across restarts and across machines.\n */\nfunction hashPeerId(peerId: string): number {\n // FNV-1a 32-bit hash\n let hash = 0x811c9dc5\n for (let i = 0; i < peerId.length; i++) {\n hash ^= peerId.charCodeAt(i)\n // Multiply by FNV prime 0x01000193.\n // Use Math.imul for correct 32-bit integer multiplication.\n hash = Math.imul(hash, 0x01000193)\n }\n // Ensure unsigned 32-bit integer\n const result = hash >>> 0\n // Reserve 0 for structural ops — real peers never collide\n return result === STRUCTURAL_YJS_CLIENT_ID ? 1 : result\n}\n\n// ---------------------------------------------------------------------------\n// createYjsFactory — factory builder with peer identity injection\n// ---------------------------------------------------------------------------\n\n/**\n * Create a SubstrateFactory<YjsVersion> that sets doc.clientID\n * on every new Y.Doc with a deterministic uint32 clientID derived\n * from the exchange's string peerId.\n */\nfunction createYjsFactory(peerId: string): SubstrateFactory<YjsVersion> {\n const numericClientId = hashPeerId(peerId)\n\n return {\n replica: yjsReplicaFactory,\n\n createReplica(): Replica<YjsVersion> {\n // Default random clientID — safe for hydration (no local writes).\n // Identity is set at upgrade() time, after hydration.\n return createYjsReplica(new Y.Doc())\n },\n\n upgrade(\n replica: Replica<YjsVersion>,\n schema: SchemaNode,\n ): Substrate<YjsVersion> {\n const doc = (replica as any)[BACKING_DOC] as Y.Doc\n // Set stable identity AFTER hydration — avoids Yjs clientID\n // conflict detection that would reassign to a random value.\n doc.clientID = numericClientId\n // Conditional ensureContainers: skip fields that already exist\n // from hydrated state (each set() is a CRDT write).\n ensureContainers(doc, schema, true)\n return createYjsSubstrate(doc, schema)\n },\n\n create(schema: SchemaNode): Substrate<YjsVersion> {\n // Fresh doc — set identity immediately, unconditional containers.\n const doc = new Y.Doc()\n doc.clientID = numericClientId\n ensureContainers(doc, schema)\n return createYjsSubstrate(doc, schema)\n },\n\n fromEntirety(\n payload: SubstratePayload,\n schema: SchemaNode,\n ): Substrate<YjsVersion> {\n // Two-phase path: createReplica → merge → upgrade\n // Identity is set at upgrade() time, after hydration —\n // avoids Yjs clientID conflict detection.\n const replica = this.createReplica()\n replica.merge(payload)\n return this.upgrade(replica, schema)\n },\n\n parseVersion(serialized: string): YjsVersion {\n return YjsVersion.parse(serialized)\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// yjs — the Yjs CRDT substrate namespace\n// ---------------------------------------------------------------------------\n\n/**\n * The Yjs CRDT substrate namespace.\n *\n * - `yjs.bind(schema)` — collaborative sync (default)\n * - `yjs.bind(schema, \"ephemeral\")` — ephemeral/presence broadcast\n * - `yjs.replica()` — collaborative replication (default)\n * - `yjs.replica(\"ephemeral\")` — ephemeral replication\n * - `yjs.unwrap(ref)` — access the underlying Y.Doc\n *\n * Strategy is constrained to `CrdtStrategy` (`\"collaborative\" | \"ephemeral\"`).\n * Passing `\"authoritative\"` is a compile error.\n */\n/** The closed set of capability tags that the Yjs substrate supports. */\nexport type YjsCaps = \"text\" | \"json\"\n\nexport const yjs: SubstrateNamespace<CrdtStrategy, YjsCaps> & {\n /** Access the underlying `Y.Doc` backing a ref. */\n unwrap(ref: object): Y.Doc\n} = {\n ...createSubstrateNamespace<CrdtStrategy, YjsCaps>({\n strategies: {\n collaborative: {\n factory: ctx => createYjsFactory(ctx.peerId),\n replicaFactory: yjsReplicaFactory,\n },\n ephemeral: {\n factory: ctx => createYjsFactory(ctx.peerId),\n replicaFactory: yjsReplicaFactory,\n },\n },\n defaultStrategy: \"collaborative\",\n }),\n\n unwrap(ref: object): Y.Doc {\n let substrate: any\n try {\n substrate = unwrap(ref)\n } catch {\n throw new Error(\n \"yjs.unwrap() requires a ref backed by a Yjs substrate. \" +\n \"Use a doc created by exchange.get() with a yjs.bind() schema, \" +\n \"or by createYjsDoc().\",\n )\n }\n\n const doc = substrate[BACKING_DOC]\n if (\n !doc ||\n typeof doc !== \"object\" ||\n typeof (doc as any).getMap !== \"function\" ||\n typeof (doc as any).clientID !== \"number\"\n ) {\n throw new Error(\n \"yjs.unwrap() requires a ref backed by a Yjs substrate. \" +\n \"The ref has a substrate but it is not a Yjs substrate. \" +\n \"Use a doc created with a yjs.bind() schema or createYjsDoc().\",\n )\n }\n return doc as Y.Doc\n },\n}\n"],"mappings":"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","names":["Y","advanceSchema","KIND","Y","change","KIND","advanceSchema","KIND","Y","Y","change","BACKING_DOC","STRUCTURAL_YJS_CLIENT_ID","Y","STRUCTURAL_YJS_CLIENT_ID","BACKING_DOC"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/bind-yjs.ts","../src/populate.ts","../src/substrate.ts","../src/change-mapping.ts","../src/yjs-resolve.ts","../src/reader.ts","../src/version.ts"],"sourcesContent":["// @kyneta/yjs-schema — Yjs CRDT substrate for @kyneta/schema.\n//\n// Provides a Substrate<YjsVersion> implementation that wraps a Y.Doc\n// with schema-aware typed reads, writes, versioning, and export/import.\n//\n// The single entry point is `createDoc(yjs.bind(schema))`. For the\n// batteries-included API, import from this package. For the composable\n// toolkit, import from `@kyneta/schema` directly.\n\n// ---------------------------------------------------------------------------\n// Generic API (re-exported from @kyneta/schema for convenience)\n// ---------------------------------------------------------------------------\n\n// Types (re-exported for convenience)\nexport type { Changeset } from \"@kyneta/changefeed\"\nexport type { DocRef, Op, Ref, SubstratePayload } from \"@kyneta/schema\"\n// Construction\n// Mutation & observation (re-exported from @kyneta/schema for convenience)\n// Schema definition (re-exported for convenience)\n// Native escape hatch\n// Sync primitives (generic — work for any substrate)\nexport {\n applyChanges,\n change,\n createDoc,\n createRef,\n exportEntirety,\n exportSince,\n merge,\n NATIVE,\n Schema,\n subscribe,\n subscribeNode,\n unwrap,\n version,\n} from \"@kyneta/schema\"\n\n// ---------------------------------------------------------------------------\n// Yjs-specific exports\n// ---------------------------------------------------------------------------\n\nexport type { YjsCaps } from \"./bind-yjs.js\"\n// Namespace\nexport { yjs } from \"./bind-yjs.js\"\n// Change mapping\nexport { applyChangeToYjs, eventsToOps } from \"./change-mapping.js\"\n// NativeMap — the Yjs functor\nexport type { YjsNativeMap } from \"./native-map.js\"\n// Container creation\nexport { ensureContainers } from \"./populate.js\"\n// Reader\nexport { yjsReader } from \"./reader.js\"\n// Substrate\nexport {\n createYjsSubstrate,\n yjsReplicaFactory,\n yjsSubstrateFactory,\n} from \"./substrate.js\"\n// Version\nexport { YjsVersion } from \"./version.js\"\n// Container resolution\nexport { resolveYjsType, stepIntoYjs } from \"./yjs-resolve.js\"\n","// bind-yjs — Yjs CRDT substrate namespace and factory.\n//\n// Provides the `yjs` substrate namespace (`yjs.bind()`, `yjs.replica()`)\n// and the internal factory builder that injects a deterministic numeric\n// Yjs clientID derived from the exchange's peerId.\n//\n// Yjs clientID is a uint32 number. We use FNV-1a hash truncated to\n// 32 bits, mirroring the Loro binding's hashPeerId pattern but\n// targeting Yjs's number type (not Loro's bigint/53-bit PeerID).\n//\n// Usage:\n// import { yjs } from \"@kyneta/yjs-schema\"\n//\n// const TodoDoc = yjs.bind(Schema.struct({\n// title: Schema.text(),\n// items: Schema.list(Schema.struct({ name: Schema.string() })),\n// }))\n//\n// const doc = exchange.get(\"my-doc\", TodoDoc)\n\nimport type {\n CrdtStrategy,\n Replica,\n Schema as SchemaNode,\n Substrate,\n SubstrateFactory,\n SubstrateNamespace,\n SubstratePayload,\n} from \"@kyneta/schema\"\nimport {\n BACKING_DOC,\n createSubstrateNamespace,\n STRUCTURAL_YJS_CLIENT_ID,\n} from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\nimport type { YjsNativeMap } from \"./native-map.js\"\nimport { ensureContainers } from \"./populate.js\"\nimport {\n createYjsReplica,\n createYjsSubstrate,\n yjsReplicaFactory,\n} from \"./substrate.js\"\nimport { YjsVersion } from \"./version.js\"\n\n// ---------------------------------------------------------------------------\n// Peer ID hashing — deterministic string → numeric Yjs clientID\n// ---------------------------------------------------------------------------\n\n/**\n * Hash a string peerId to a deterministic numeric Yjs clientID.\n *\n * Yjs clientIDs are unsigned 32-bit integers. We use FNV-1a hash to\n * produce a deterministic uint32 from the string peerId.\n *\n * The hash is deterministic: the same string always produces the same\n * numeric clientID, across restarts and across machines.\n */\nfunction hashPeerId(peerId: string): number {\n // FNV-1a 32-bit hash\n let hash = 0x811c9dc5\n for (let i = 0; i < peerId.length; i++) {\n hash ^= peerId.charCodeAt(i)\n // Multiply by FNV prime 0x01000193.\n // Use Math.imul for correct 32-bit integer multiplication.\n hash = Math.imul(hash, 0x01000193)\n }\n // Ensure unsigned 32-bit integer\n const result = hash >>> 0\n // Reserve 0 for structural ops — real peers never collide\n return result === STRUCTURAL_YJS_CLIENT_ID ? 1 : result\n}\n\n// ---------------------------------------------------------------------------\n// createYjsFactory — factory builder with peer identity injection\n// ---------------------------------------------------------------------------\n\n/**\n * Create a SubstrateFactory<YjsVersion> that sets doc.clientID\n * on every new Y.Doc with a deterministic uint32 clientID derived\n * from the exchange's string peerId.\n */\nfunction createYjsFactory(peerId: string): SubstrateFactory<YjsVersion> {\n const numericClientId = hashPeerId(peerId)\n\n return {\n replica: yjsReplicaFactory,\n\n createReplica(): Replica<YjsVersion> {\n // Default random clientID — safe for hydration (no local writes).\n // Identity is set at upgrade() time, after hydration.\n return createYjsReplica(new Y.Doc())\n },\n\n upgrade(\n replica: Replica<YjsVersion>,\n schema: SchemaNode,\n ): Substrate<YjsVersion> {\n const doc = (replica as any)[BACKING_DOC] as Y.Doc\n // Set stable identity AFTER hydration — avoids Yjs clientID\n // conflict detection that would reassign to a random value.\n doc.clientID = numericClientId\n // Conditional ensureContainers: skip fields that already exist\n // from hydrated state (each set() is a CRDT write).\n ensureContainers(doc, schema, true)\n return createYjsSubstrate(doc, schema)\n },\n\n create(schema: SchemaNode): Substrate<YjsVersion> {\n // Fresh doc — set identity immediately, unconditional containers.\n const doc = new Y.Doc()\n doc.clientID = numericClientId\n ensureContainers(doc, schema)\n return createYjsSubstrate(doc, schema)\n },\n\n fromEntirety(\n payload: SubstratePayload,\n schema: SchemaNode,\n ): Substrate<YjsVersion> {\n // Two-phase path: createReplica → merge → upgrade\n // Identity is set at upgrade() time, after hydration —\n // avoids Yjs clientID conflict detection.\n const replica = this.createReplica()\n replica.merge(payload)\n return this.upgrade(replica, schema)\n },\n\n parseVersion(serialized: string): YjsVersion {\n return YjsVersion.parse(serialized)\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// yjs — the Yjs CRDT substrate namespace\n// ---------------------------------------------------------------------------\n\n/**\n * The Yjs CRDT substrate namespace.\n *\n * - `yjs.bind(schema)` — collaborative sync (default)\n * - `yjs.bind(schema, \"ephemeral\")` — ephemeral/presence broadcast\n * - `yjs.replica()` — collaborative replication (default)\n * - `yjs.replica(\"ephemeral\")` — ephemeral replication\n *\n * Strategy is constrained to `CrdtStrategy` (`\"collaborative\" | \"ephemeral\"`).\n * Passing `\"authoritative\"` is a compile error.\n *\n * To access the underlying Y.Doc, use `unwrap(ref)` from `@kyneta/schema`.\n */\n/** The closed set of capability tags that the Yjs substrate supports. */\nexport type YjsCaps = \"text\" | \"json\"\n\nexport const yjs: SubstrateNamespace<CrdtStrategy, YjsCaps, YjsNativeMap> =\n createSubstrateNamespace<CrdtStrategy, YjsCaps, YjsNativeMap>({\n strategies: {\n collaborative: {\n factory: ctx => createYjsFactory(ctx.peerId),\n replicaFactory: yjsReplicaFactory,\n },\n ephemeral: {\n factory: ctx => createYjsFactory(ctx.peerId),\n replicaFactory: yjsReplicaFactory,\n },\n },\n defaultStrategy: \"collaborative\",\n })\n","// populate — Yjs container creation from schema structure.\n//\n// Ensures that the correct Yjs shared types (Y.Text, Y.Array, Y.Map)\n// exist in a Y.Doc's root map to match the schema structure, and that\n// scalar/sum fields are initialized with Zero.structural defaults.\n//\n// This is NOT seed data — it's structural completeness, matching what\n// PlainSubstrate does when it initializes its store with Zero.structural.\n// The Yjs store reader expects to find values at every schema path;\n// without this, unset scalars would return undefined instead of their\n// type-correct zero (\"\", 0, false).\n//\n// Root container strategy: All schema fields are children of a single\n// root `Y.Map` obtained via `doc.getMap(\"root\")`. This root map holds\n// shared types (Y.Text, Y.Array, Y.Map) and plain value slots uniformly.\n\nimport type { Schema as SchemaNode } from \"@kyneta/schema\"\nimport { KIND, STRUCTURAL_YJS_CLIENT_ID, Zero } from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\n\n// ---------------------------------------------------------------------------\n// ensureContainers — top-level entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Ensure that a Y.Doc's root map contains the correct Yjs shared types\n * matching the schema structure.\n *\n * Obtains the root map via `doc.getMap(\"root\")`, reads the root product\n * schema's fields, and creates empty containers for each field within a\n * single `doc.transact()` call for atomicity.\n *\n * When `conditional` is true, fields that already exist in the root map\n * are skipped. This is the correct mode after hydration — containers\n * present from stored state must not be overwritten (each `rootMap.set()`\n * is a CRDT write that advances the version vector and may conflict\n * with stored operations).\n *\n * When `conditional` is false (default), all fields are created\n * unconditionally. This is the correct mode for fresh documents.\n *\n * **Structural identity:** This function temporarily sets `doc.clientID`\n * to `STRUCTURAL_YJS_CLIENT_ID` (0) for the duration of container creation,\n * then restores the caller's clientID. This produces byte-identical\n * structural ops across all peers, enabling Yjs deduplication on merge.\n *\n * @param doc - The Y.Doc to prepare\n * @param schema - The root document schema (a ProductSchema)\n * @param conditional - If true, skip fields that already exist in the root map.\n * Context: jj:smmulzkm (two-phase substrate construction)\n */\nexport function ensureContainers(\n doc: Y.Doc,\n schema: SchemaNode,\n conditional = false,\n): void {\n const rootMap = doc.getMap(\"root\")\n\n if (schema[KIND] !== \"product\") {\n return\n }\n\n // Switch to structural identity for deterministic container creation.\n // All peers produce byte-identical structural ops at clientID 0.\n const savedClientID = doc.clientID\n doc.clientID = STRUCTURAL_YJS_CLIENT_ID\n\n try {\n doc.transact(() => {\n for (const [key, fieldSchema] of Object.entries(schema.fields).sort(\n ([a], [b]) => a.localeCompare(b),\n )) {\n if (conditional && rootMap.has(key)) continue\n ensureRootField(rootMap, key, fieldSchema as SchemaNode)\n }\n })\n } finally {\n // Restore the caller's identity for application writes.\n doc.clientID = savedClientID\n }\n}\n\n// ---------------------------------------------------------------------------\n// ensureRootField — create a single root-level container\n// ---------------------------------------------------------------------------\n\n/**\n * Ensure a root-level Yjs shared type exists for a schema field.\n *\n * Dispatches on `[KIND]`:\n * - `\"text\"` → empty Y.Text\n * - `\"product\"` → empty Y.Map (recursive for nested products)\n * - `\"sequence\"` → empty Y.Array\n * - `\"map\"` → empty Y.Map\n * - `\"scalar\"` / `\"sum\"` → Zero.structural default\n * - `\"counter\"` / `\"set\"` / `\"tree\"` / `\"movable\"` → throw (not supported by Yjs)\n */\nfunction ensureRootField(\n rootMap: Y.Map<unknown>,\n key: string,\n fieldSchema: SchemaNode,\n): void {\n switch (fieldSchema[KIND]) {\n case \"text\":\n rootMap.set(key, new Y.Text())\n return\n\n case \"product\":\n rootMap.set(key, ensureMapContainers(fieldSchema))\n return\n\n case \"sequence\":\n rootMap.set(key, new Y.Array())\n return\n\n case \"map\":\n rootMap.set(key, new Y.Map())\n return\n\n case \"scalar\":\n case \"sum\": {\n // Plain values don't need shared type containers, but they DO\n // need structural zero defaults so the store reader returns\n // type-correct values (e.g. \"\" not undefined for strings).\n const zero = Zero.structural(fieldSchema)\n if (zero !== undefined) {\n rootMap.set(key, zero)\n }\n return\n }\n\n case \"counter\":\n case \"set\":\n case \"tree\":\n case \"movable\":\n throw new Error(\n `Yjs substrate does not support [KIND]=\"${fieldSchema[KIND]}\". ` +\n `Supported kinds: text, product, sequence, map, scalar, sum. ` +\n `Encountered unsupported kind at root field \"${key}\".`,\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// ensureMapContainers — recursively create nested Y.Map structure\n// ---------------------------------------------------------------------------\n\n/**\n * Create an empty Y.Map with nested shared type children matching\n * the product schema's field structure.\n *\n * Only creates containers for fields that require Yjs shared types\n * (text → Y.Text, product → Y.Map, sequence → Y.Array, map → Y.Map).\n * Scalar and sum fields are set to their structural zero defaults.\n */\nfunction ensureMapContainers(schema: SchemaNode): Y.Map<unknown> {\n const map = new Y.Map()\n\n if (schema[KIND] !== \"product\") return map\n\n for (const [key, fieldSchema] of Object.entries(\n schema.fields as Record<string, SchemaNode>,\n ).sort(([a], [b]) => a.localeCompare(b))) {\n switch (fieldSchema[KIND]) {\n case \"text\":\n map.set(key, new Y.Text())\n break\n\n case \"product\":\n map.set(key, ensureMapContainers(fieldSchema))\n break\n\n case \"sequence\":\n map.set(key, new Y.Array())\n break\n\n case \"map\":\n map.set(key, new Y.Map())\n break\n\n case \"scalar\":\n case \"sum\": {\n const zero = Zero.structural(fieldSchema)\n if (zero !== undefined) {\n map.set(key, zero)\n }\n break\n }\n\n case \"counter\":\n case \"set\":\n case \"tree\":\n case \"movable\":\n throw new Error(\n `Yjs substrate does not support [KIND]=\"${fieldSchema[KIND]}\". ` +\n `Supported kinds: text, product, sequence, map, scalar, sum. ` +\n `Encountered unsupported kind at nested field \"${key}\".`,\n )\n }\n }\n\n return map\n}\n","// substrate — YjsSubstrate implementation.\n//\n// Implements Substrate<YjsVersion> with:\n// - Imperative local writes (prepare accumulates, onFlush applies in transact)\n// - Persistent observeDeep event bridge for external changes\n// - Single re-entrancy guard + transaction.origin check\n//\n// The event bridge contract: wrapping a Y.Doc in a kyneta substrate\n// means subscribing to the kyneta doc observes ALL mutations to the\n// underlying Y.Doc, regardless of source (local kyneta writes,\n// merge, external Y.applyUpdate, external raw Yjs API mutations).\n\nimport type {\n ChangeBase,\n Path,\n Reader,\n Replica,\n ReplicaFactory,\n Schema as SchemaNode,\n Substrate,\n SubstrateFactory,\n SubstratePayload,\n WritableContext,\n} from \"@kyneta/schema\"\nimport {\n BACKING_DOC,\n buildWritableContext,\n executeBatch,\n KIND,\n} from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\nimport { applyChangeToYjs, eventsToOps } from \"./change-mapping.js\"\nimport { ensureContainers } from \"./populate.js\"\nimport { yjsReader } from \"./reader.js\"\nimport { YjsVersion } from \"./version.js\"\nimport { resolveYjsType } from \"./yjs-resolve.js\"\n\n// ---------------------------------------------------------------------------\n// Origin tag — used to suppress echo from our own transactions\n// ---------------------------------------------------------------------------\n\nconst KYNETA_ORIGIN = \"kyneta-prepare\"\n\n// ---------------------------------------------------------------------------\n// createYjsSubstrate — wrap a user-provided Y.Doc\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a `Substrate<YjsVersion>` wrapping a user-provided Y.Doc.\n *\n * This is the \"bring your own doc\" entry point. The user creates and\n * manages the Y.Doc (possibly via a Yjs provider); this function wraps\n * it with a schema-aware overlay providing typed reads, writes,\n * versioning, and export/merge through the standard Substrate interface.\n *\n * **Event bridge contract:** A persistent `observeDeep` handler is\n * registered on the root Y.Map at construction time. All non-kyneta\n * mutations to the Y.Doc (merges, external local writes) are bridged\n * to the kyneta changefeed. Subscribing to the kyneta doc observes all\n * mutations regardless of source.\n *\n * @param doc - The Y.Doc to wrap. The substrate does NOT own the doc;\n * the caller is responsible for its lifecycle.\n * @param schema - The root schema for the document.\n */\nexport function createYjsSubstrate(\n doc: Y.Doc,\n schema: SchemaNode,\n): Substrate<YjsVersion> {\n // --- Closure-scoped state ---\n\n // Accumulated changes from prepare(), drained by onFlush().\n const pendingChanges: Array<{ path: Path; change: ChangeBase }> = []\n\n // Re-entrancy guard: set true around our doc.transact() in onFlush\n // AND around executeBatch in the event bridge. When true, prepare()\n // skips Yjs-side work (changes are already applied by Yjs or about\n // to be), and onFlush() skips transact/commit.\n let inOurTransaction = false\n\n // Stashed origin from merge for the event bridge to pick up.\n let pendingMergeOrigin: string | undefined\n\n // Lazy-built WritableContext (same pattern as PlainSubstrate / LoroSubstrate).\n let cachedCtx: WritableContext | undefined\n\n // The root Y.Map — all schema fields are children of this single map.\n const rootMap = doc.getMap(\"root\")\n\n // The Reader — live view over the Yjs shared type tree.\n const reader: Reader = yjsReader(doc, schema)\n\n // --- Substrate object ---\n\n const substrate = {\n [BACKING_DOC]: doc,\n\n reader: reader,\n\n prepare(path: Path, change: ChangeBase): void {\n if (!inOurTransaction) {\n // Local write: accumulate for flush.\n // No Yjs side effects — mutations happen at flush time.\n pendingChanges.push({ path, change })\n }\n // During event handler replay: no-op on Yjs side.\n // wrappedPrepare (changefeed layer) still buffers the op.\n },\n\n onFlush(_origin?: string): void {\n if (!inOurTransaction && pendingChanges.length > 0) {\n // Local write: apply accumulated changes within a single\n // Yjs transaction tagged with our origin for echo suppression.\n inOurTransaction = true\n try {\n doc.transact(() => {\n for (const { path, change } of pendingChanges) {\n applyChangeToYjs(rootMap, schema, path, change)\n }\n }, KYNETA_ORIGIN)\n pendingChanges.length = 0\n } finally {\n inOurTransaction = false\n }\n }\n // During event handler replay: no-op on Yjs side.\n // wrappedFlush (changefeed layer) still delivers notifications.\n },\n\n context(): WritableContext {\n if (!cachedCtx) {\n cachedCtx = buildWritableContext(substrate)\n // Attach nativeResolver — used by interpretImpl to set [NATIVE]\n // on every ref. The resolver maps schema positions to Yjs shared types.\n ;(cachedCtx as any).nativeResolver = (\n nodeSchema: SchemaNode,\n path: { segments: readonly unknown[] },\n ) => {\n if (path.segments.length === 0) return doc\n if (nodeSchema[KIND] === \"scalar\" || nodeSchema[KIND] === \"sum\")\n return undefined\n return resolveYjsType(rootMap, schema, path as any)\n }\n }\n return cachedCtx\n },\n\n version(): YjsVersion {\n return new YjsVersion(Y.encodeStateVector(doc))\n },\n\n baseVersion(): YjsVersion {\n // Yjs substrate: base is always the initial state (no advance supported).\n return new YjsVersion(new Uint8Array([0]))\n },\n\n advance(_to: YjsVersion): void {\n throw new Error(\n \"advance() on a live Yjs substrate is not yet supported. \" +\n \"Use advance() on a YjsReplica instead.\",\n )\n },\n\n exportEntirety(): SubstratePayload {\n return {\n kind: \"entirety\",\n encoding: \"binary\",\n data: Y.encodeStateAsUpdate(doc),\n }\n },\n\n exportSince(since: YjsVersion): SubstratePayload | null {\n try {\n const bytes = Y.encodeStateAsUpdate(doc, since.sv)\n return { kind: \"since\", encoding: \"binary\", data: bytes }\n } catch {\n return null\n }\n },\n\n merge(payload: SubstratePayload, origin?: string): void {\n if (\n payload.encoding !== \"binary\" ||\n !(payload.data instanceof Uint8Array)\n ) {\n throw new Error(\n \"YjsSubstrate.merge expects binary-encoded payloads. \" +\n \"If you recently switched CRDT backends, stale clients may be sending incompatible data.\",\n )\n }\n // Stash origin for the event bridge to pick up\n pendingMergeOrigin = origin\n try {\n Y.applyUpdate(doc, payload.data, origin ?? \"remote\")\n } finally {\n pendingMergeOrigin = undefined\n }\n // That's it — the observeDeep handler bridges events to the\n // changefeed via executeBatch.\n },\n }\n\n // --- Event bridge (registered once at construction) ---\n\n rootMap.observeDeep((events, transaction) => {\n // Ignore our own transactions (changefeed already captured via wrappedPrepare)\n if (transaction.origin === KYNETA_ORIGIN) {\n return\n }\n\n // Convert Yjs events → kyneta Ops\n const ops = eventsToOps(events, schema)\n if (ops.length === 0) {\n return\n }\n\n // Determine origin: prefer stashed kyneta origin (from merge),\n // fall back to the transaction's origin if it's a string.\n const origin =\n pendingMergeOrigin ??\n (typeof transaction.origin === \"string\" ? transaction.origin : undefined)\n\n // Lazily ensure the context is built\n const ctx = substrate.context()\n\n // Feed through executeBatch for changefeed delivery.\n // The inOurTransaction guard prevents prepare/onFlush from doing\n // Yjs-side work — the changes are already applied by Yjs.\n inOurTransaction = true\n try {\n executeBatch(ctx, ops, origin)\n } finally {\n inOurTransaction = false\n }\n })\n\n return substrate as Substrate<YjsVersion>\n}\n\n// ---------------------------------------------------------------------------\n// yjsSubstrateFactory — SubstrateFactory<YjsVersion>\n// ---------------------------------------------------------------------------\n\n/**\n * Factory for constructing Yjs-backed substrates.\n *\n * - `create(schema)` — creates a fresh Y.Doc with empty containers\n * matching the schema structure. No seed data — initial content\n * should be applied via `change()` after construction.\n * - `fromEntirety(payload, schema)` — creates a Y.Doc from an entirety\n * payload, returns a substrate.\n * - `parseVersion(serialized)` — deserializes a YjsVersion.\n */\n// ---------------------------------------------------------------------------\n// yjsReplicaFactory — ReplicaFactory<YjsVersion>\n// ---------------------------------------------------------------------------\n\n/**\n * Schema-free replica factory for Yjs substrates.\n *\n * Constructs headless `Replica<YjsVersion>` instances backed by bare\n * `Y.Doc`s — no schema walking, no container initialization, no\n * Reader, no event bridge, no changefeed. Just the CRDT runtime\n * with version tracking and export/merge.\n *\n * Used by conduit participants (stores, routing servers)\n * that need to accumulate state, compute per-peer deltas, and compact\n * storage without ever interpreting document fields.\n */\nexport function createYjsReplica(doc: Y.Doc): Replica<YjsVersion> {\n let currentDoc = doc\n let currentBase: YjsVersion = new YjsVersion(Y.encodeStateVector(new Y.Doc()))\n\n return {\n get [BACKING_DOC]() {\n return currentDoc\n },\n\n version(): YjsVersion {\n return new YjsVersion(Y.encodeStateVector(currentDoc))\n },\n\n baseVersion(): YjsVersion {\n return currentBase\n },\n\n advance(to: YjsVersion): void {\n const baseCmp = currentBase.compare(to)\n if (baseCmp === \"ahead\") {\n throw new Error(\"advance(): target is behind base version\")\n }\n const currentCmp = to.compare(this.version())\n if (currentCmp === \"ahead\") {\n throw new Error(\"advance(): target is ahead of current version\")\n }\n\n // Yjs can only do full projection (to = version).\n // For any to < version, it's a no-op — undershoot contract.\n if (currentCmp !== \"equal\") return\n\n // Full projection: create a new doc with current state, no history.\n const update = Y.encodeStateAsUpdate(currentDoc)\n const newDoc = new Y.Doc()\n Y.applyUpdate(newDoc, update)\n currentDoc = newDoc\n currentBase = new YjsVersion(Y.encodeStateVector(currentDoc))\n },\n\n exportEntirety(): SubstratePayload {\n return {\n kind: \"entirety\",\n encoding: \"binary\",\n data: Y.encodeStateAsUpdate(currentDoc),\n }\n },\n\n exportSince(since: YjsVersion): SubstratePayload | null {\n try {\n const bytes = Y.encodeStateAsUpdate(currentDoc, since.sv)\n return { kind: \"since\", encoding: \"binary\", data: bytes }\n } catch {\n return null\n }\n },\n\n merge(payload: SubstratePayload, _origin?: string): void {\n if (\n payload.encoding !== \"binary\" ||\n !(payload.data instanceof Uint8Array)\n ) {\n throw new Error(\n \"YjsReplica.merge expects binary-encoded payloads. \" +\n \"If you recently switched CRDT backends, stale clients may be sending incompatible data.\",\n )\n }\n Y.applyUpdate(currentDoc, payload.data)\n },\n } as Replica<YjsVersion>\n}\n\nexport const yjsReplicaFactory: ReplicaFactory<YjsVersion> = {\n replicaType: [\"yjs\", 1, 0] as const,\n\n createEmpty(): Replica<YjsVersion> {\n return createYjsReplica(new Y.Doc())\n },\n\n fromEntirety(payload: SubstratePayload): Replica<YjsVersion> {\n if (\n payload.encoding !== \"binary\" ||\n !(payload.data instanceof Uint8Array)\n ) {\n throw new Error(\n \"YjsReplicaFactory.fromEntirety only supports binary-encoded payloads\",\n )\n }\n const doc = new Y.Doc()\n Y.applyUpdate(doc, payload.data)\n return createYjsReplica(doc)\n },\n\n parseVersion(serialized: string): YjsVersion {\n return YjsVersion.parse(serialized)\n },\n}\n\n// ---------------------------------------------------------------------------\n// yjsSubstrateFactory — SubstrateFactory<YjsVersion>\n// ---------------------------------------------------------------------------\n\nexport const yjsSubstrateFactory: SubstrateFactory<YjsVersion> = {\n replica: yjsReplicaFactory,\n\n createReplica(): Replica<YjsVersion> {\n // Default random clientID — safe for hydration (no local writes).\n return createYjsReplica(new Y.Doc())\n },\n\n upgrade(\n replica: Replica<YjsVersion>,\n schema: SchemaNode,\n ): Substrate<YjsVersion> {\n const doc = (replica as any)[BACKING_DOC] as Y.Doc\n // No identity injection for the standalone factory (no peerId).\n // Conditional ensureContainers: skip fields that already exist\n // from hydrated state.\n ensureContainers(doc, schema, true)\n return createYjsSubstrate(doc, schema)\n },\n\n create(schema: SchemaNode): Substrate<YjsVersion> {\n // Fresh doc — unconditional ensureContainers (nothing to conflict with).\n const doc = new Y.Doc()\n ensureContainers(doc, schema)\n return createYjsSubstrate(doc, schema)\n },\n\n fromEntirety(\n payload: SubstratePayload,\n schema: SchemaNode,\n ): Substrate<YjsVersion> {\n // Two-phase path: createReplica → merge → upgrade\n const replica = this.createReplica()\n replica.merge(payload)\n return this.upgrade(replica, schema)\n },\n\n parseVersion(serialized: string): YjsVersion {\n return YjsVersion.parse(serialized)\n },\n}\n","// change-mapping — bidirectional change mapping between kyneta and Yjs.\n//\n// Two directions:\n//\n// 1. kyneta → Yjs (`applyChangeToYjs`): Resolves the target Yjs shared\n// type at a path, then applies the change imperatively via Yjs API.\n// No intermediate diff format — direct imperative mutations.\n//\n// 2. Yjs → kyneta (`eventsToOps`): Converts `observeDeep` events into\n// kyneta `Op[]` for changefeed delivery. Each Y.YEvent maps to one Op\n// with a path derived from `event.path` (relative to the observed root\n// Y.Map) and a Change derived from the event's delta/keys.\n//\n// Structured inserts use populate-then-attach order: new shared types\n// are fully populated before being inserted into their parent container.\n// This produces a single observeDeep event with the complete struct,\n// rather than a cascade of child MapChange events.\n\nimport type {\n ChangeBase,\n IncrementChange,\n MapChange,\n Op,\n Path,\n ReplaceChange,\n Schema as SchemaNode,\n SequenceChange,\n SequenceInstruction,\n TextChange,\n TextInstruction,\n} from \"@kyneta/schema\"\nimport {\n advanceSchema,\n expandMapOpsToLeaves,\n KIND,\n RawPath,\n} from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\nimport { resolveYjsType } from \"./yjs-resolve.js\"\n\n// ---------------------------------------------------------------------------\n// Direction 1: kyneta → Yjs (`applyChangeToYjs`)\n// ---------------------------------------------------------------------------\n\n/**\n * Apply a kyneta Change to the Yjs shared type tree imperatively.\n *\n * Resolves the target shared type at `path`, then applies the change\n * via the appropriate Yjs API. Must be called within a `doc.transact()`\n * for atomicity and correct event batching.\n *\n * @param rootMap - The root `Y.Map` obtained via `doc.getMap(\"root\")`\n * @param rootSchema - The root document schema\n * @param path - The path to the target\n * @param change - The kyneta Change to apply\n */\nexport function applyChangeToYjs(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n change: ChangeBase,\n): void {\n switch (change.type) {\n case \"text\":\n applyTextChange(rootMap, rootSchema, path, change as TextChange)\n return\n\n case \"sequence\":\n applySequenceChange(rootMap, rootSchema, path, change as SequenceChange)\n return\n\n case \"map\":\n applyMapChange(rootMap, rootSchema, path, change as MapChange)\n return\n\n case \"replace\":\n applyReplaceChange(rootMap, rootSchema, path, change as ReplaceChange)\n return\n\n case \"increment\":\n throw new Error(\n `Yjs substrate does not support \"${change.type}\" changes. ` +\n `Counter requires a CRDT backend that supports counters (e.g. Loro). ` +\n `Attempted IncrementChange with amount=${(change as IncrementChange).amount} at path [${pathToString(path)}].`,\n )\n\n case \"tree\":\n throw new Error(\n `Yjs substrate does not support \"${change.type}\" changes. ` +\n `Tree requires a CRDT backend that supports trees (e.g. Loro). ` +\n `Attempted TreeChange at path [${pathToString(path)}].`,\n )\n\n default:\n throw new Error(\n `applyChangeToYjs: unsupported change type \"${change.type}\"`,\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Text change\n// ---------------------------------------------------------------------------\n\nfunction applyTextChange(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n change: TextChange,\n): void {\n const resolved = resolveYjsType(rootMap, rootSchema, path)\n if (!(resolved instanceof Y.Text)) {\n throw new Error(\n `applyChangeToYjs: TextChange target at path [${pathToString(path)}] is not a Y.Text`,\n )\n }\n\n // Yjs Y.Text.applyDelta uses the Quill Delta format, which is\n // structurally identical to kyneta TextInstruction[].\n resolved.applyDelta(change.instructions as any)\n}\n\n// ---------------------------------------------------------------------------\n// Sequence change\n// ---------------------------------------------------------------------------\n\nfunction applySequenceChange(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n change: SequenceChange,\n): void {\n const resolved = resolveYjsType(rootMap, rootSchema, path)\n if (!(resolved instanceof Y.Array)) {\n throw new Error(\n `applyChangeToYjs: SequenceChange target at path [${pathToString(path)}] is not a Y.Array`,\n )\n }\n\n // Resolve the item schema for structured insert detection\n const targetSchema = resolveSchemaAtPath(rootSchema, path)\n const itemSchema = getItemSchema(targetSchema)\n\n let cursor = 0\n for (const instruction of change.instructions) {\n if (\"retain\" in instruction) {\n cursor += instruction.retain\n } else if (\"delete\" in instruction) {\n resolved.delete(cursor, instruction.delete)\n // cursor stays — deleted items shift remaining items down\n } else if (\"insert\" in instruction) {\n const items = instruction.insert as readonly unknown[]\n const yjsItems = items.map(item =>\n maybeCreateSharedType(item, itemSchema),\n )\n resolved.insert(cursor, yjsItems)\n cursor += items.length\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Map change\n// ---------------------------------------------------------------------------\n\nfunction applyMapChange(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n change: MapChange,\n): void {\n const resolved = resolveYjsType(rootMap, rootSchema, path)\n if (!(resolved instanceof Y.Map)) {\n throw new Error(\n `applyChangeToYjs: MapChange target at path [${pathToString(path)}] is not a Y.Map`,\n )\n }\n\n // Resolve the schema at this path for structured value detection\n const targetSchema = resolveSchemaAtPath(rootSchema, path)\n\n // Apply deletes first\n if (change.delete) {\n for (const key of change.delete) {\n resolved.delete(key)\n }\n }\n\n // Apply sets\n if (change.set) {\n for (const [key, value] of Object.entries(change.set)) {\n const fieldSchema = getFieldSchema(targetSchema, key)\n const yjsValue = maybeCreateSharedType(value, fieldSchema)\n resolved.set(key, yjsValue)\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Replace change\n// ---------------------------------------------------------------------------\n\nfunction applyReplaceChange(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n change: ReplaceChange,\n): void {\n if (path.length === 0) {\n throw new Error(\n \"applyChangeToYjs: ReplaceChange at root path is not supported\",\n )\n }\n\n // Target the parent container, using the last segment to identify\n // which child to replace.\n const lastSeg = path.segments[path.segments.length - 1]!\n const parentPath = path.slice(0, -1)\n const parent = resolveYjsType(rootMap, rootSchema, parentPath)\n\n const resolved = lastSeg.resolve()\n if (parent instanceof Y.Map && lastSeg.role === \"key\") {\n // Resolve schema for the target field for structured value detection\n const targetSchema = resolveSchemaAtPath(rootSchema, path)\n const yjsValue = maybeCreateSharedType(change.value, targetSchema)\n parent.set(resolved as string, yjsValue)\n } else if (parent instanceof Y.Array && lastSeg.role === \"index\") {\n const targetSchema = resolveSchemaAtPath(rootSchema, path)\n const yjsValue = maybeCreateSharedType(change.value, targetSchema)\n parent.delete(resolved as number, 1)\n parent.insert(resolved as number, [yjsValue])\n } else {\n throw new Error(\n `applyChangeToYjs: ReplaceChange parent at path [${pathToString(parentPath)}] ` +\n `is not a Y.Map or Y.Array (got ${typeof parent})`,\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Structured value creation (populate-then-attach pattern)\n// ---------------------------------------------------------------------------\n\n/**\n * If the schema says the value should be a shared type (product → Y.Map,\n * sequence → Y.Array, text → Y.Text), create and populate it.\n * Otherwise return the plain value as-is.\n *\n * Uses populate-then-attach: the new shared type is fully populated\n * before being returned for insertion into its parent.\n */\nfunction maybeCreateSharedType(\n value: unknown,\n schema: SchemaNode | undefined,\n): unknown {\n if (schema === undefined) return value\n\n switch (schema[KIND]) {\n // First-class text → Y.Text\n case \"text\": {\n const text = new Y.Text()\n if (typeof value === \"string\" && value.length > 0) {\n text.insert(0, value)\n }\n return text\n }\n\n case \"product\": {\n if (\n value === null ||\n value === undefined ||\n typeof value !== \"object\" ||\n Array.isArray(value)\n ) {\n return value\n }\n return createStructuredMap(value as Record<string, unknown>, schema)\n }\n\n case \"sequence\": {\n if (!Array.isArray(value)) return value\n const arr = new Y.Array()\n const itemSchema = schema.item\n const items = (value as unknown[]).map(item =>\n maybeCreateSharedType(item, itemSchema),\n )\n arr.insert(0, items)\n return arr\n }\n\n case \"map\": {\n if (\n value === null ||\n value === undefined ||\n typeof value !== \"object\" ||\n Array.isArray(value)\n ) {\n return value\n }\n const map = new Y.Map()\n const valueSchema = schema.item\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n map.set(k, maybeCreateSharedType(v, valueSchema))\n }\n return map\n }\n\n // Unsupported first-class CRDT types — should not reach here\n // (rejected at bind time by caps check)\n case \"counter\":\n case \"set\":\n case \"tree\":\n case \"movable\":\n throw new Error(\n `Yjs substrate does not support [KIND]=\"${schema[KIND]}\". ` +\n `This should have been caught at bind() time.`,\n )\n\n default:\n // Scalar, sum — return as plain value\n return value\n }\n}\n\n/**\n * Create a Y.Map from a plain object, recursively creating nested\n * shared types as guided by the product schema.\n *\n * Follows populate-then-attach: fully populates the map before the\n * caller inserts it into a parent container.\n */\nfunction createStructuredMap(\n obj: Record<string, unknown>,\n productSchema: SchemaNode,\n): Y.Map<any> {\n const map = new Y.Map()\n\n if (productSchema[KIND] !== \"product\") {\n // Fallback: set all values as plain\n for (const [key, val] of Object.entries(obj)) {\n map.set(key, val)\n }\n return map\n }\n\n // Process fields present in the value object\n for (const [key, val] of Object.entries(obj)) {\n if (val === undefined) continue\n const fieldSchema = productSchema.fields[key]\n const yjsVal = fieldSchema ? maybeCreateSharedType(val, fieldSchema) : val\n map.set(key, yjsVal)\n }\n\n // Create shared types for first-class CRDT fields declared in the schema\n // but missing from the value object. This ensures Yjs containers\n // exist for later mutation (e.g. .insert() on a text field inside\n // a struct inside a record/list).\n for (const [key, fieldSchema] of Object.entries(\n productSchema.fields as Record<string, SchemaNode>,\n )) {\n if (key in obj) continue // already processed above\n if (fieldSchema[KIND] === \"text\") {\n map.set(key, new Y.Text())\n }\n }\n\n return map\n}\n\n// ---------------------------------------------------------------------------\n// Direction 2: Yjs → kyneta (`eventsToOps`)\n// ---------------------------------------------------------------------------\n\n/**\n * Convert `observeDeep` events into kyneta `Op[]` for changefeed delivery.\n *\n * Each `Y.YEvent` in the array maps to one Op with:\n * - `path`: derived from `event.path` (relative to the observed root Y.Map)\n * - `change`: derived from the event's delta/keys based on target type\n *\n * `event.path` in `observeDeep` is relative to the observed shared type.\n * Since we observe `rootMap` (the single root Y.Map), paths map directly\n * to kyneta `PathSegment[]`.\n *\n * @param events - The events from the `observeDeep` callback\n */\nexport function eventsToOps(events: Y.YEvent<any>[], schema: SchemaNode): Op[] {\n const ops: Op[] = []\n\n for (const event of events) {\n const kynetaPath = yjsPathToKynetaPath(event.path)\n const change = eventToChange(event)\n if (change) {\n ops.push({ path: kynetaPath, change })\n }\n }\n\n return expandMapOpsToLeaves(ops, schema)\n}\n\n// ---------------------------------------------------------------------------\n// Yjs path → kyneta Path conversion\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a Yjs event path (array of string | number) to a kyneta Path.\n *\n * `event.path` from `observeDeep` is relative to the observed type.\n * Strings become key segments, numbers become index segments.\n */\nfunction yjsPathToKynetaPath(yjsPath: (string | number)[]): RawPath {\n let path = RawPath.empty\n for (const segment of yjsPath) {\n if (typeof segment === \"string\") {\n path = path.field(segment)\n } else if (typeof segment === \"number\") {\n path = path.item(segment)\n }\n }\n return path\n}\n\n// ---------------------------------------------------------------------------\n// Per-type event → Change converters\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a single Yjs event into a kyneta Change.\n * Returns null for event types we can't map.\n */\nfunction eventToChange(event: Y.YEvent<any>): ChangeBase | null {\n if (event.target instanceof Y.Text) {\n return textEventToChange(event)\n }\n if (event.target instanceof Y.Array) {\n return arrayEventToChange(event)\n }\n if (event.target instanceof Y.Map) {\n return mapEventToChange(event)\n }\n return null\n}\n\n/**\n * Y.Text event → TextChange.\n *\n * `event.delta` uses the Quill Delta format, structurally identical to\n * kyneta `TextInstruction[]`. We strip the `attributes` field (rich text\n * formatting not surfaced by kyneta).\n */\nfunction textEventToChange(event: Y.YEvent<any>): TextChange {\n const instructions: TextInstruction[] = []\n\n for (const delta of event.delta) {\n if (delta.retain !== undefined) {\n instructions.push({ retain: delta.retain as number })\n } else if (delta.insert !== undefined) {\n instructions.push({ insert: delta.insert as string })\n } else if (delta.delete !== undefined) {\n instructions.push({ delete: delta.delete as number })\n }\n }\n\n return { type: \"text\", instructions }\n}\n\n/**\n * Y.Array event → SequenceChange.\n *\n * `event.changes.delta` provides the same cursor-based ops as kyneta\n * SequenceInstruction[]. Container values (Y.Map, Y.Array) in insert\n * arrays are converted to plain objects via `.toJSON()`.\n */\nfunction arrayEventToChange(event: Y.YEvent<any>): SequenceChange {\n const instructions: SequenceInstruction[] = []\n\n for (const delta of event.changes.delta) {\n if (delta.retain !== undefined) {\n instructions.push({ retain: delta.retain as number })\n } else if (delta.delete !== undefined) {\n instructions.push({ delete: delta.delete as number })\n } else if (delta.insert !== undefined) {\n const items = (delta.insert as unknown[]).map((item: unknown) =>\n extractEventValue(item),\n )\n instructions.push({ insert: items })\n }\n }\n\n return { type: \"sequence\", instructions }\n}\n\n/**\n * Y.Map event → MapChange.\n *\n * `event.changes.keys` is a `Map<string, { action: 'add'|'update'|'delete', ... }>`.\n * - `action: 'add'|'update'` → `set[key] = map.get(key)`\n * - `action: 'delete'` → `delete.push(key)`\n */\nfunction mapEventToChange(event: Y.YEvent<any>): MapChange | null {\n const set: Record<string, unknown> = {}\n const deleteKeys: string[] = []\n let hasSet = false\n let hasDelete = false\n\n const target = event.target as Y.Map<any>\n\n event.changes.keys.forEach((change: { action: string }, key: string) => {\n if (change.action === \"add\" || change.action === \"update\") {\n const value = target.get(key)\n set[key] = extractEventValue(value)\n hasSet = true\n } else if (change.action === \"delete\") {\n deleteKeys.push(key)\n hasDelete = true\n }\n })\n\n if (!hasSet && !hasDelete) return null\n\n return {\n type: \"map\",\n ...(hasSet ? { set } : {}),\n ...(hasDelete ? { delete: deleteKeys } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Value extraction from Yjs events\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a Yjs value from an event into a plain value.\n * Container values (Y.Map, Y.Array, Y.Text) → `.toJSON()`.\n * Plain values → returned as-is.\n */\nfunction extractEventValue(value: unknown): unknown {\n if (value instanceof Y.Map) return value.toJSON()\n if (value instanceof Y.Array) return value.toJSON()\n if (value instanceof Y.Text) return value.toJSON()\n return value\n}\n\n// ---------------------------------------------------------------------------\n// Schema helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the schema at a given path by walking through advanceSchema.\n */\nfunction resolveSchemaAtPath(rootSchema: SchemaNode, path: Path): SchemaNode {\n let schema = rootSchema\n for (const seg of path.segments) {\n schema = advanceSchema(schema, seg)\n }\n return schema\n}\n\n/**\n * Get the item schema from a sequence schema, if available.\n */\nfunction getItemSchema(schema: SchemaNode): SchemaNode | undefined {\n if (schema[KIND] === \"sequence\") return schema.item\n if (schema[KIND] === \"movable\") return schema.item\n return undefined\n}\n\n/**\n * Get the field schema from a product or map schema for a given key.\n */\nfunction getFieldSchema(\n schema: SchemaNode,\n key: string,\n): SchemaNode | undefined {\n if (schema[KIND] === \"product\") {\n return schema.fields[key]\n }\n if (schema[KIND] === \"map\") {\n return schema.item\n }\n if (schema[KIND] === \"set\") {\n return schema.item\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// Path formatting\n// ---------------------------------------------------------------------------\n\nfunction pathToString(path: Path): string {\n return path.segments.map(seg => String(seg.resolve())).join(\".\")\n}\n","// yjs-resolve — Yjs-specific path resolution.\n//\n// Implements stepIntoYjs and resolveYjsType for schema-guided\n// navigation of the Yjs shared type tree.\n//\n// resolveYjsType is a left-fold over path segments, accumulating\n// (currentType, currentSchema) at each step. This mirrors how\n// resolveContainer works for Loro — but uses `instanceof` for\n// runtime type discrimination instead of Loro's `.kind()` method.\n//\n// Root container strategy: All schema fields are children of a single\n// root `Y.Map` obtained via `doc.getMap(\"root\")`. This root map holds\n// shared types (Y.Text, Y.Array, Y.Map) and plain values uniformly.\n// Using a single root Y.Map enables one `observeDeep` call that\n// captures all mutations with correct relative paths.\n\nimport type { Path, Schema as SchemaNode, Segment } from \"@kyneta/schema\"\nimport { advanceSchema } from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\n\n// ---------------------------------------------------------------------------\n// stepIntoYjs — single step of the fold\n// ---------------------------------------------------------------------------\n\n/**\n * Navigate one step deeper into the Yjs shared type tree.\n *\n * Uses `instanceof` for runtime type discrimination:\n * - `Y.Map` → `.get(key)`\n * - `Y.Array` → `.get(index)`\n * - `Y.Text` → terminal (cannot step further)\n * - Plain value → terminal (return `undefined`)\n *\n * @param current - The current position (a Yjs shared type or plain value)\n * @param segment - The path segment to follow\n */\nexport function stepIntoYjs(current: unknown, segment: Segment): unknown {\n const resolved = segment.resolve()\n\n if (current instanceof Y.Map) {\n return current.get(resolved as string)\n }\n\n if (current instanceof Y.Array) {\n return current.get(resolved as number)\n }\n\n if (current instanceof Y.Text) {\n throw new Error(`yjs-resolve: cannot step into Y.Text`)\n }\n\n // Plain value — terminal, cannot step further\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// resolveYjsType — full path resolution via left-fold\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a Yjs shared type (or plain value) at the given path.\n *\n * Left-folds over path segments using `advanceSchema` for pure schema\n * descent and `stepIntoYjs` for Yjs-specific navigation.\n *\n * Returns the Yjs shared type or plain value at the terminal position.\n * For an empty path, returns the root map itself.\n *\n * @param rootMap - The root `Y.Map` obtained via `doc.getMap(\"root\")`\n * @param rootSchema - The root document schema\n * @param path - The path to resolve\n */\nexport function resolveYjsType(\n rootMap: Y.Map<any>,\n rootSchema: SchemaNode,\n path: Path,\n): unknown {\n let current: unknown = rootMap\n let schema = rootSchema\n\n for (let i = 0; i < path.length; i++) {\n const seg = path.segments[i]!\n const nextSchema = advanceSchema(schema, seg)\n\n // For the first segment, we step into the root map directly.\n // For subsequent segments, we use stepIntoYjs on the current value.\n current = stepIntoYjs(current, seg)\n schema = nextSchema\n }\n\n return current\n}\n","// store-reader — YjsReader implementation.\n//\n// Implements Reader via schema-guided live navigation of the\n// Yjs shared type tree. Each read operation resolves the shared type\n// at the given path using resolveYjsType, then extracts the\n// appropriate value based on `instanceof` discrimination.\n//\n// Y.Text → .toJSON() (string), Y.Map → .toJSON() (plain object),\n// Y.Array → .toJSON() (plain array), plain values → as-is.\n\nimport type { Path, Reader, Schema as SchemaNode } from \"@kyneta/schema\"\nimport * as Y from \"yjs\"\nimport { resolveYjsType } from \"./yjs-resolve.js\"\n\n// ---------------------------------------------------------------------------\n// Value extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Extract a plain value from a Yjs shared type or return a plain value as-is.\n *\n * - Y.Text → `.toJSON()` (string)\n * - Y.Map → `.toJSON()` (plain object snapshot — for product/map reads)\n * - Y.Array → `.toJSON()` (plain array snapshot)\n * - Plain values (string, number, boolean, null) → returned as-is\n */\nfunction extractValue(resolved: unknown): unknown {\n if (resolved instanceof Y.Text) {\n return resolved.toJSON()\n }\n if (resolved instanceof Y.Map) {\n return resolved.toJSON()\n }\n if (resolved instanceof Y.Array) {\n return resolved.toJSON()\n }\n // Plain scalar value (string, number, boolean, null, etc.)\n return resolved\n}\n\n// ---------------------------------------------------------------------------\n// yjsReader\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a Reader that navigates the Yjs shared type tree live,\n * using the schema as a type witness to determine navigation at each\n * path segment.\n *\n * The reader is a live view — mutations to the underlying Y.Doc\n * (via `doc.transact()`, or `Y.applyUpdate()`) are immediately\n * visible through the reader.\n *\n * Internally obtains the root map via `doc.getMap(\"root\")`.\n *\n * @param doc - The Y.Doc to read from.\n * @param schema - The root schema for the document.\n */\nexport function yjsReader(doc: Y.Doc, schema: SchemaNode): Reader {\n const rootMap = doc.getMap(\"root\")\n\n return {\n read(path: Path): unknown {\n if (path.length === 0) {\n // Root read — return the full root map as JSON\n return rootMap.toJSON()\n }\n const resolved = resolveYjsType(rootMap, schema, path)\n return extractValue(resolved)\n },\n\n arrayLength(path: Path): number {\n const resolved = resolveYjsType(rootMap, schema, path)\n if (resolved instanceof Y.Array) {\n return resolved.length\n }\n // Graceful fallback for plain array values\n if (Array.isArray(resolved)) {\n return resolved.length\n }\n return 0\n },\n\n keys(path: Path): string[] {\n const resolved = resolveYjsType(rootMap, schema, path)\n if (resolved instanceof Y.Map) {\n return Array.from(resolved.keys())\n }\n // Graceful fallback for plain object values\n if (\n resolved !== null &&\n resolved !== undefined &&\n typeof resolved === \"object\" &&\n !Array.isArray(resolved)\n ) {\n return Object.keys(resolved as Record<string, unknown>)\n }\n return []\n },\n\n hasKey(path: Path, key: string): boolean {\n const resolved = resolveYjsType(rootMap, schema, path)\n if (resolved instanceof Y.Map) {\n return resolved.has(key)\n }\n // Graceful fallback for plain object values\n if (\n resolved !== null &&\n resolved !== undefined &&\n typeof resolved === \"object\" &&\n !Array.isArray(resolved)\n ) {\n return key in (resolved as Record<string, unknown>)\n }\n return false\n },\n }\n}\n","// YjsVersion — Version implementation wrapping Yjs state vectors.\n//\n// Yjs state vectors (`Y.encodeStateVector(doc)`) are the complete peer\n// state used for sync diffing — matching the semantics of kyneta's\n// Version interface.\n//\n// Serialization uses base64-encoded bytes for text-safe embedding in\n// HTML meta tags, script tags, etc.\n//\n// Yjs does not export a state vector comparison function, so we\n// implement standard version-vector partial-order comparison over\n// decoded `Map<number, number>` (clientID → clock) maps ourselves.\n\nimport type { Version } from \"@kyneta/schema\"\nimport {\n base64ToUint8Array,\n uint8ArrayToBase64,\n versionVectorCompare,\n versionVectorMeet,\n} from \"@kyneta/schema\"\nimport { decodeStateVector } from \"yjs\"\n\n// ---------------------------------------------------------------------------\n// State vector encoding — manual varint (unsigned LEB128)\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a state vector map to Yjs's binary state vector format.\n *\n * Yjs does not export `encodeStateVector(map)` — only `Y.encodeStateVector(doc)`\n * which requires a full doc. This implements the same binary format directly:\n * `[entryCount: varint, (clientId: varint, clock: varint)*]`\n *\n * Each value is encoded as an unsigned LEB128 varint.\n */\nfunction encodeStateVector(map: Map<number, number>): Uint8Array {\n const bytes: number[] = []\n\n function writeVarUint(value: number): void {\n while (value > 0x7f) {\n bytes.push((value & 0x7f) | 0x80)\n value >>>= 7\n }\n bytes.push(value & 0x7f)\n }\n\n writeVarUint(map.size)\n for (const [clientId, clock] of map) {\n writeVarUint(clientId)\n writeVarUint(clock)\n }\n\n return new Uint8Array(bytes)\n}\n\n// ---------------------------------------------------------------------------\n// YjsVersion\n// ---------------------------------------------------------------------------\n\n/**\n * A Version wrapping a Yjs state vector.\n *\n * State vectors track the complete peer state — which operations from\n * each client have been observed. This is the right abstraction for sync\n * diffing: `exportSince(version)` uses the state vector to compute the\n * minimal update payload via `Y.encodeStateAsUpdate(doc, sv)`.\n *\n * `serialize()` encodes to base64 for text-safe embedding.\n * `compare()` decodes both state vectors and performs standard\n * version-vector partial-order comparison over the client-clock maps.\n */\nexport class YjsVersion implements Version {\n readonly sv: Uint8Array\n\n constructor(sv: Uint8Array) {\n this.sv = sv\n }\n\n /**\n * Serialize the state vector to a base64 string.\n *\n * The encoding is: raw state vector bytes → base64.\n * This is text-safe for embedding in HTML meta tags, URL parameters, etc.\n */\n serialize(): string {\n return uint8ArrayToBase64(this.sv)\n }\n\n /**\n * Compare with another version using version-vector partial order.\n *\n * Delegates to the shared `versionVectorCompare` utility after decoding\n * both state vectors via `Y.decodeStateVector()`.\n *\n * @throws If `other` is not a `YjsVersion`.\n */\n compare(other: Version): \"behind\" | \"equal\" | \"ahead\" | \"concurrent\" {\n if (!(other instanceof YjsVersion)) {\n throw new Error(\"YjsVersion can only be compared with another YjsVersion\")\n }\n return versionVectorCompare(\n decodeStateVector(this.sv),\n decodeStateVector(other.sv),\n )\n }\n\n /**\n * Greatest lower bound (lattice meet) of two Yjs versions.\n *\n * Decodes both state vectors, computes the component-wise minimum\n * via the shared `versionVectorMeet` utility, and encodes the result\n * back to a Yjs state vector.\n *\n * @throws If `other` is not a `YjsVersion`.\n */\n meet(other: Version): YjsVersion {\n if (!(other instanceof YjsVersion)) {\n throw new Error(\"YjsVersion can only be meet'd with another YjsVersion\")\n }\n const thisMap = decodeStateVector(this.sv)\n const otherMap = decodeStateVector(other.sv)\n const result = versionVectorMeet(thisMap, otherMap)\n return new YjsVersion(encodeStateVector(result))\n }\n\n /**\n * Parse a serialized YjsVersion string back into a YjsVersion.\n *\n * The inverse of `serialize()`: base64 → `Uint8Array`.\n */\n static parse(serialized: string): YjsVersion {\n if (serialized === \"\") {\n throw new Error(\"Invalid YjsVersion value: (empty string)\")\n }\n const bytes = base64ToUint8Array(serialized)\n return new YjsVersion(bytes)\n }\n}\n"],"mappings":"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","names":["BACKING_DOC","STRUCTURAL_YJS_CLIENT_ID","Y","KIND","Y","advanceSchema","KIND","Y","Y","change","KIND","advanceSchema","Y","change","KIND","STRUCTURAL_YJS_CLIENT_ID","BACKING_DOC"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kyneta/yjs-schema",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Yjs CRDT substrate for @kyneta/schema — collaborative data types with typed refs",
5
5
  "author": "Duane Johnson",
6
6
  "license": "MIT",
@@ -30,8 +30,8 @@
30
30
  "./src/*": "./src/*"
31
31
  },
32
32
  "peerDependencies": {
33
- "@kyneta/changefeed": "^1.0.0",
34
- "@kyneta/schema": "^1.2.0",
33
+ "@kyneta/changefeed": "^1.3.1",
34
+ "@kyneta/schema": "^1.3.1",
35
35
  "yjs": ">=13.6.0"
36
36
  },
37
37
  "devDependencies": {
@@ -39,8 +39,8 @@
39
39
  "typescript": "^5.9.2",
40
40
  "vitest": "^4.0.17",
41
41
  "yjs": "^13.6.30",
42
- "@kyneta/schema": "^1.2.0",
43
- "@kyneta/changefeed": "^1.1.0"
42
+ "@kyneta/schema": "^1.3.1",
43
+ "@kyneta/changefeed": "^1.3.1"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup",
@@ -124,9 +124,7 @@ describe("yjs.bind() rejects schemas with unsupported caps", () => {
124
124
 
125
125
  it("rejects tree", () => {
126
126
  const schema = Schema.struct({
127
- hierarchy: Schema.tree(
128
- Schema.struct({ label: Schema.string() }),
129
- ),
127
+ hierarchy: Schema.tree(Schema.struct({ label: Schema.string() })),
130
128
  })
131
129
  // @ts-expect-error — tree is not in YjsCaps
132
130
  yjs.bind(schema)
@@ -144,9 +142,7 @@ describe("yjs.bind() rejects schemas with unsupported caps", () => {
144
142
  const schema = Schema.struct({
145
143
  items: Schema.list(
146
144
  Schema.struct({
147
- meta: Schema.record(
148
- Schema.struct({ hits: Schema.counter() }),
149
- ),
145
+ meta: Schema.record(Schema.struct({ hits: Schema.counter() })),
150
146
  }),
151
147
  ),
152
148
  })
@@ -165,9 +161,7 @@ describe("yjs.bind() rejects schemas with unsupported caps", () => {
165
161
 
166
162
  it("rejects counter inside list", () => {
167
163
  const schema = Schema.struct({
168
- scores: Schema.list(
169
- Schema.struct({ value: Schema.counter() }),
170
- ),
164
+ scores: Schema.list(Schema.struct({ value: Schema.counter() })),
171
165
  })
172
166
  // @ts-expect-error — counter nested inside list struct
173
167
  yjs.bind(schema)
@@ -194,9 +188,7 @@ describe("cross-substrate: universal schema vs substrate-specific schema", () =>
194
188
  const loroSpecificSchema = Schema.struct({
195
189
  title: Schema.text(),
196
190
  count: Schema.counter(),
197
- tasks: Schema.movableList(
198
- Schema.struct({ name: Schema.string() }),
199
- ),
191
+ tasks: Schema.movableList(Schema.struct({ name: Schema.string() })),
200
192
  })
201
193
 
202
194
  it("universal schema is Yjs-compatible (ExtractCaps check)", () => {
@@ -330,4 +322,4 @@ describe("yjs.bind() rejects non-product root schemas", () => {
330
322
  // @ts-expect-error — SequenceSchema<ProductSchema> is still not ProductSchema
331
323
  yjs.bind(Schema.list(Schema.struct({ name: Schema.string() })))
332
324
  })
333
- })
325
+ })