@absolutejs/sync 1.24.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -2
- package/dist/adapters/drizzle/index.js +3 -2
- package/dist/adapters/drizzle/index.js.map +3 -3
- package/dist/adapters/tanstack-db/index.js.map +3 -3
- package/dist/angular/index.js.map +3 -3
- package/dist/client/index.js.map +6 -6
- package/dist/engine/index.js +25 -6
- package/dist/engine/index.js.map +5 -5
- package/dist/index.js +68 -46
- package/dist/index.js.map +8 -8
- package/dist/plugin.d.ts +1 -40
- package/dist/react/index.js.map +3 -3
- package/dist/svelte/index.js.map +3 -3
- package/dist/testing.js +25 -6
- package/dist/testing.js.map +3 -3
- package/dist/vue/index.js.map +3 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -38,7 +38,13 @@ top-N ordering are maintained incrementally through a composable operator graph
|
|
|
38
38
|
> search, scheduled functions, a live devtools dashboard, conflict-free
|
|
39
39
|
> collaborative editing (CRDTs), CDC for Postgres/MySQL/SQLite, incremental
|
|
40
40
|
> aggregations + joins, and a declarative operator graph) are in place.
|
|
41
|
-
>
|
|
41
|
+
> Operator-grade primitives also shipped — **point-in-time replay**
|
|
42
|
+
> (`engine.replayTo`, 1.22; clickable Replay panel in `syncDevtools`, 1.23) and
|
|
43
|
+
> **tenant migration** (`engine.fence` / `exportSnapshot` / `importSnapshot`,
|
|
44
|
+
> 1.24). Everything ships as subpaths of this one package. See
|
|
45
|
+
> [Substrate complete (G1–G7)](https://absolutejs.com/documentation/substrate-audit)
|
|
46
|
+
> for the consolidated record of the seven cross-cutting substrate gaps and
|
|
47
|
+
> what closed each.
|
|
42
48
|
|
|
43
49
|
## Install
|
|
44
50
|
|
|
@@ -365,7 +371,7 @@ it, ~3 store round-trips every 20ms ran the voice pipeline far slower than real
|
|
|
365
371
|
| `sync({ hub, path?, resolveTopics?, heartbeatMs? })` | Elysia plugin: SSE stream of hub events. |
|
|
366
372
|
| `syncSocket({ engine, path?, resolveContext? })` | Elysia WebSocket plugin for the sync engine. |
|
|
367
373
|
| `scheduled({ engine, prefix?, onError? })` _(`/scheduled` subpath)_ | Elysia plugin: fires the engine's registered schedules on their cron patterns (via `@elysiajs/cron`). Kept off the main entry so `syncSocket` needs no cron dep. |
|
|
368
|
-
| `syncDevtools({ engine, path?, snapshotMs? })` | Elysia plugin: a live devtools dashboard (collections, subscription counts, mutations, schedules, change feed) over SSE. Backed by `engine.inspect()` + `engine.onActivity()`. |
|
|
374
|
+
| `syncDevtools({ engine, path?, snapshotMs? })` | Elysia plugin: a live devtools dashboard (collections, subscription counts, mutations, schedules, change feed) over SSE. Backed by `engine.inspect()` + `engine.onActivity()`. **1.23** also exposes a Point-in-time replay panel (datetime picker + tables filter) and a `GET <path>/replay?at=<ms>&tables=<csv>` JSON endpoint wrapping `engine.replayTo`. |
|
|
369
375
|
| `createWriteBehindCache({ load, persist, remove?, debounceMs?, evict?, onPersistError? })` | In-memory cache + write-behind persistence. |
|
|
370
376
|
|
|
371
377
|
### `@absolutejs/sync/client`
|
|
@@ -438,6 +444,10 @@ mutate({
|
|
|
438
444
|
| `createTextIndex({ key, fields, tokenize?, stopwords?, k1?, b? })` | Incremental BM25 full-text index (keyword search). Implements `SearchIndex`; usable standalone or inside a search collection. |
|
|
439
445
|
| `createVectorIndex({ key, embedding, metric? })` | Incremental vector index (cosine/dot/euclidean exact k-NN) for semantic search — pairs with `@absolutejs/ai` / `@absolutejs/rag` for RAG retrieval on your own data. |
|
|
440
446
|
| `defineSchedule({ name, pattern, run })` + `registerSchedule` / `runSchedule` | Scheduled function: `run({ db, actions })` fires on a cron `pattern`; its writes go live through the change feed. Wire triggers with the `scheduled` plugin (or call `runSchedule(name)` on demand). |
|
|
447
|
+
| `engine.replayTo({ at, tables? })` _(1.22)_ | Walk the bounded change log forward to a target timestamp and return `{ asOfVersion, asOfAt, rows, truncated }`. Forensic incident response ("what did the tenant see at 14:32?") + restore-from-time ("revert to 2 hours ago"). `truncated: true` when the log doesn't extend back to `at` — widen `changeLogRetainMs` for forensic use cases. |
|
|
448
|
+
| `engine.fence({ reason })` _(1.24)_ | Pause new mutations on the engine — the source half of tenant migration. `runMutation` rejects with `EngineFencedError`; subscribe / hydrate / streamChanges stay open. Multiple fences compose; every handle must `lift()` before the engine unfences. `lift()` is idempotent. |
|
|
449
|
+
| `engine.exportSnapshot({ tables?, ctx? })` _(1.24)_ | Walk every registered reader's `all(ctx)` and return a portable `EngineSnapshot { sourceInstanceId, version, exportedAt, tables }`. Detached from `ChangeLogSnapshot` — snapshots carry live state, not history. |
|
|
450
|
+
| `engine.importSnapshot(snapshot, { tables?, onProgress?, ctx? })` _(1.24)_ | Bulk-load an `EngineSnapshot` on the target via each table's registered writer. Returns `{ tablesImported, rowsImported, perTable, skipped }`. Tables in the snapshot without a writer on the target surface in `skipped` so misconfigured targets don't silently drop rows. |
|
|
441
451
|
|
|
442
452
|
### `@absolutejs/sync/crdt`
|
|
443
453
|
|
|
@@ -18,6 +18,7 @@ var __require = import.meta.require;
|
|
|
18
18
|
// src/adapters/drizzle/topics.ts
|
|
19
19
|
import {
|
|
20
20
|
Column,
|
|
21
|
+
getColumnTable,
|
|
21
22
|
getTableColumns,
|
|
22
23
|
getTableName,
|
|
23
24
|
is,
|
|
@@ -79,7 +80,7 @@ var extractKeyFromWhere = (table, where, keyColumn) => {
|
|
|
79
80
|
if (column.name !== resolved.column) {
|
|
80
81
|
return;
|
|
81
82
|
}
|
|
82
|
-
if (getTableName(column
|
|
83
|
+
if (getTableName(getColumnTable(column)) !== getTableName(table)) {
|
|
83
84
|
return;
|
|
84
85
|
}
|
|
85
86
|
const value = param.value;
|
|
@@ -277,5 +278,5 @@ export {
|
|
|
277
278
|
UnsupportedDrizzleFilterError
|
|
278
279
|
};
|
|
279
280
|
|
|
280
|
-
//# debugId=
|
|
281
|
+
//# debugId=440BA9164FF10CD464756E2164756E21
|
|
281
282
|
//# sourceMappingURL=index.js.map
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/adapters/drizzle/topics.ts", "../src/adapters/drizzle/collection.ts", "../src/adapters/drizzle/predicate.ts", "../src/adapters/drizzle/read.ts", "../src/adapters/drizzle/write.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import {\n\tColumn,\n\tgetTableColumns,\n\tgetTableName,\n\tis,\n\tParam,\n\tSQL\n} from 'drizzle-orm';\nimport type { Table } from 'drizzle-orm';\n\n/**\n * Shared topic vocabulary + key resolution for the Drizzle adapter. Both the\n * read side (derive the topics a query depends on) and the write side (publish\n * the topics a mutation invalidates) build on these so the two always agree.\n */\n\n/** The coarse topic every read/write of `table` touches, e.g. `users`. */\nexport const tableTopic = (table: Table): string => getTableName(table);\n\n/** The row-level topic for one key of `table`, e.g. `users:5`. */\nexport const keyTopic = (table: Table, key: string | number): string =>\n\t`${getTableName(table)}:${key}`;\n\ntype ResolvedKey = {\n\t/** JS property name of the key column on the table / result rows. */\n\tproperty: string;\n\t/** Underlying DB column name, as it appears in a SQL expression. */\n\tcolumn: string;\n};\n\n/**\n * Resolve the column to use as the row key: an explicitly requested column (by\n * JS property name), otherwise the table's sole primary key. Returns `undefined`\n * when no single key column applies (composite or missing primary key).\n */\nexport const resolveKeyColumn = (\n\ttable: Table,\n\tkeyColumn?: string\n): ResolvedKey | undefined => {\n\tconst columns = getTableColumns(table);\n\tif (keyColumn !== undefined) {\n\t\tconst column = columns[keyColumn];\n\t\treturn column === undefined\n\t\t\t? undefined\n\t\t\t: { property: keyColumn, column: column.name };\n\t}\n\tconst primaries = Object.entries(columns).filter(\n\t\t([, column]) => column.primary\n\t);\n\tconst primary = primaries.length === 1 ? primaries[0] : undefined;\n\treturn primary === undefined\n\t\t? undefined\n\t\t: { property: primary[0], column: primary[1].name };\n};\n\n/**\n * Best-effort: pull a single key-column equality value out of a Drizzle `where`\n * expression. Recognises only the simple `eq(keyColumn, scalar)` shape — any\n * nesting (`and`/`or`), extra columns/params, a non-`=` operator, or a\n * non-key/cross-table column yields `undefined`.\n *\n * Reads Drizzle's internal `queryChunks`, which is not a stable public API;\n * every branch degrades to `undefined` (coarser topic) rather than throwing, so\n * a Drizzle version bump can only cost precision, never correctness.\n */\nexport const extractKeyFromWhere = (\n\ttable: Table,\n\twhere: SQL,\n\tkeyColumn?: string\n): string | number | undefined => {\n\tconst resolved = resolveKeyColumn(table, keyColumn);\n\tif (resolved === undefined) {\n\t\treturn undefined;\n\t}\n\n\tconst chunks: unknown = (where as { queryChunks?: unknown }).queryChunks;\n\tif (!Array.isArray(chunks)) {\n\t\treturn undefined;\n\t}\n\n\tlet column: Column | undefined;\n\tlet param: Param | undefined;\n\tlet tooComplex = false;\n\tlet operator = '';\n\n\tfor (const chunk of chunks) {\n\t\tif (is(chunk, SQL)) {\n\t\t\t// Nested expression (e.g. and/or) — not a simple equality.\n\t\t\treturn undefined;\n\t\t}\n\t\tif (is(chunk, Column)) {\n\t\t\tif (column !== undefined) {\n\t\t\t\ttooComplex = true;\n\t\t\t}\n\t\t\tcolumn = chunk;\n\t\t} else if (is(chunk, Param)) {\n\t\t\tif (param !== undefined) {\n\t\t\t\ttooComplex = true;\n\t\t\t}\n\t\t\tparam = chunk;\n\t\t} else {\n\t\t\tconst value: unknown = (chunk as { value?: unknown }).value;\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\toperator += value.join('');\n\t\t\t}\n\t\t}\n\t}\n\n\tif (tooComplex || column === undefined || param === undefined) {\n\t\treturn undefined;\n\t}\n\tif (operator.trim() !== '=') {\n\t\treturn undefined;\n\t}\n\tif (column.name !== resolved.column) {\n\t\treturn undefined;\n\t}\n\tif (getTableName(column
|
|
5
|
+
"import {\n\tColumn,\n\tgetColumnTable,\n\tgetTableColumns,\n\tgetTableName,\n\tis,\n\tParam,\n\tSQL\n} from 'drizzle-orm';\nimport type { Table } from 'drizzle-orm';\n\n/**\n * Shared topic vocabulary + key resolution for the Drizzle adapter. Both the\n * read side (derive the topics a query depends on) and the write side (publish\n * the topics a mutation invalidates) build on these so the two always agree.\n */\n\n/** The coarse topic every read/write of `table` touches, e.g. `users`. */\nexport const tableTopic = (table: Table): string => getTableName(table);\n\n/** The row-level topic for one key of `table`, e.g. `users:5`. */\nexport const keyTopic = (table: Table, key: string | number): string =>\n\t`${getTableName(table)}:${key}`;\n\ntype ResolvedKey = {\n\t/** JS property name of the key column on the table / result rows. */\n\tproperty: string;\n\t/** Underlying DB column name, as it appears in a SQL expression. */\n\tcolumn: string;\n};\n\n/**\n * Resolve the column to use as the row key: an explicitly requested column (by\n * JS property name), otherwise the table's sole primary key. Returns `undefined`\n * when no single key column applies (composite or missing primary key).\n */\nexport const resolveKeyColumn = (\n\ttable: Table,\n\tkeyColumn?: string\n): ResolvedKey | undefined => {\n\tconst columns = getTableColumns(table);\n\tif (keyColumn !== undefined) {\n\t\tconst column = columns[keyColumn];\n\t\treturn column === undefined\n\t\t\t? undefined\n\t\t\t: { property: keyColumn, column: column.name };\n\t}\n\tconst primaries = Object.entries(columns).filter(\n\t\t([, column]) => column.primary\n\t);\n\tconst primary = primaries.length === 1 ? primaries[0] : undefined;\n\treturn primary === undefined\n\t\t? undefined\n\t\t: { property: primary[0], column: primary[1].name };\n};\n\n/**\n * Best-effort: pull a single key-column equality value out of a Drizzle `where`\n * expression. Recognises only the simple `eq(keyColumn, scalar)` shape — any\n * nesting (`and`/`or`), extra columns/params, a non-`=` operator, or a\n * non-key/cross-table column yields `undefined`.\n *\n * Reads Drizzle's internal `queryChunks`, which is not a stable public API;\n * every branch degrades to `undefined` (coarser topic) rather than throwing, so\n * a Drizzle version bump can only cost precision, never correctness.\n */\nexport const extractKeyFromWhere = (\n\ttable: Table,\n\twhere: SQL,\n\tkeyColumn?: string\n): string | number | undefined => {\n\tconst resolved = resolveKeyColumn(table, keyColumn);\n\tif (resolved === undefined) {\n\t\treturn undefined;\n\t}\n\n\tconst chunks: unknown = (where as { queryChunks?: unknown }).queryChunks;\n\tif (!Array.isArray(chunks)) {\n\t\treturn undefined;\n\t}\n\n\tlet column: Column | undefined;\n\tlet param: Param | undefined;\n\tlet tooComplex = false;\n\tlet operator = '';\n\n\tfor (const chunk of chunks) {\n\t\tif (is(chunk, SQL)) {\n\t\t\t// Nested expression (e.g. and/or) — not a simple equality.\n\t\t\treturn undefined;\n\t\t}\n\t\tif (is(chunk, Column)) {\n\t\t\tif (column !== undefined) {\n\t\t\t\ttooComplex = true;\n\t\t\t}\n\t\t\tcolumn = chunk;\n\t\t} else if (is(chunk, Param)) {\n\t\t\tif (param !== undefined) {\n\t\t\t\ttooComplex = true;\n\t\t\t}\n\t\t\tparam = chunk;\n\t\t} else {\n\t\t\tconst value: unknown = (chunk as { value?: unknown }).value;\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\toperator += value.join('');\n\t\t\t}\n\t\t}\n\t}\n\n\tif (tooComplex || column === undefined || param === undefined) {\n\t\treturn undefined;\n\t}\n\tif (operator.trim() !== '=') {\n\t\treturn undefined;\n\t}\n\tif (column.name !== resolved.column) {\n\t\treturn undefined;\n\t}\n\tif (getTableName(getColumnTable(column)) !== getTableName(table)) {\n\t\treturn undefined;\n\t}\n\n\tconst value: unknown = param.value;\n\treturn typeof value === 'string' || typeof value === 'number'\n\t\t? value\n\t\t: undefined;\n};\n\n/**\n * Read the key value from each row (e.g. the output of a mutation's\n * `.returning()`), using the table's primary-key column or an explicit\n * `keyColumn`. Rows without a string/number key are skipped.\n */\nexport const extractRowKeys = (\n\ttable: Table,\n\trows: ReadonlyArray<Record<string, unknown>>,\n\tkeyColumn?: string\n): (string | number)[] => {\n\tconst resolved = resolveKeyColumn(table, keyColumn);\n\tif (resolved === undefined) {\n\t\treturn [];\n\t}\n\tconst keys: (string | number)[] = [];\n\tfor (const row of rows) {\n\t\tconst value = row[resolved.property];\n\t\tif (typeof value === 'string' || typeof value === 'number') {\n\t\t\tkeys.push(value);\n\t\t}\n\t}\n\treturn keys;\n};\n",
|
|
6
6
|
"import { getTableName } from 'drizzle-orm';\nimport type { SQL, Table } from 'drizzle-orm';\nimport type {\n\tCollectionContext,\n\tCollectionDefinition\n} from '../../engine/collection';\nimport type { RowKey } from '../../engine/types';\nimport { matchesDrizzleWhere } from './predicate';\nimport { resolveKeyColumn } from './topics';\n\nexport type DrizzleCollectionOptions<T, P = void, Ctx = CollectionContext> = {\n\t/** Collection name clients subscribe to. */\n\tname: string;\n\t/** The Drizzle table this collection reads (drives change routing + key). */\n\ttable: Table;\n\t/** The query filter, written once — powers both hydrate and the matcher. */\n\twhere: (params: P, ctx: Ctx) => SQL;\n\t/** Run the read for `where` (your `db.select()...`), returning the rows. */\n\tfind: (\n\t\twhere: SQL,\n\t\tparams: P,\n\t\tctx: Ctx\n\t) => Promise<Iterable<T>> | Iterable<T>;\n\t/** Row identity. Defaults to the table's single primary key (else `row.id`). */\n\tkey?: (row: T) => RowKey;\n\t/** Key column JS property, if not the table's primary key. */\n\tkeyColumn?: string;\n\t/** Access control; return false (or throw) to deny the subscription. */\n\tauthorize?: (params: P, ctx: Ctx) => boolean | Promise<boolean>;\n};\n\n/**\n * A sync-engine collection from one Drizzle query — the Drizzle counterpart to\n * `prismaCollection`. You write the `where` once: it drives the DB read\n * (`hydrate`) AND the incremental `match` (via {@link matchesDrizzleWhere}), so\n * the two can't drift and you never hand-maintain a separate predicate. A filter\n * the matcher can't evaluate falls back to a refetch, never a wrong result.\n */\nexport const drizzleCollection = <T, P = void, Ctx = CollectionContext>(\n\toptions: DrizzleCollectionOptions<T, P, Ctx>\n): CollectionDefinition<T, P, Ctx> => {\n\tconst keyProp = resolveKeyColumn(\n\t\toptions.table,\n\t\toptions.keyColumn\n\t)?.property;\n\tconst key =\n\t\toptions.key ??\n\t\t((row: T) =>\n\t\t\tkeyProp !== undefined\n\t\t\t\t? (row as Record<string, RowKey>)[keyProp]!\n\t\t\t\t: (row as { id: RowKey }).id);\n\n\treturn {\n\t\tname: options.name,\n\t\ttables: [getTableName(options.table)],\n\t\thydrate: (params, ctx) =>\n\t\t\toptions.find(options.where(params, ctx), params, ctx),\n\t\tmatch: (row, params, ctx) =>\n\t\t\tmatchesDrizzleWhere(\n\t\t\t\toptions.table,\n\t\t\t\toptions.where(params, ctx),\n\t\t\t\trow as Record<string, unknown>\n\t\t\t),\n\t\tkey,\n\t\tauthorize: options.authorize\n\t};\n};\n",
|
|
7
7
|
"import { Column, getTableColumns, is, Param, SQL } from 'drizzle-orm';\nimport type { Table } from 'drizzle-orm';\n\n/**\n * Thrown when a Drizzle `where` uses something the incremental matcher can't\n * evaluate in JS (an unsupported operator, a function, a cross-table column…).\n * The sync engine catches it and degrades that subscription to a refetch, so the\n * result is never wrong — only less efficient. Mirrors the Prisma adapter.\n */\nexport class UnsupportedDrizzleFilterError extends Error {\n\tconstructor(detail: string) {\n\t\tsuper(`Cannot evaluate Drizzle filter \"${detail}\" incrementally`);\n\t\tthis.name = 'UnsupportedDrizzleFilterError';\n\t}\n}\n\nconst isDate = (value: unknown): value is Date => value instanceof Date;\n\nconst equals = (value: unknown, operand: unknown): boolean => {\n\tif (operand === null) {\n\t\treturn value === null || value === undefined;\n\t}\n\tif (isDate(value) && isDate(operand)) {\n\t\treturn value.getTime() === operand.getTime();\n\t}\n\treturn value === operand;\n};\n\nconst order = (value: unknown): number | string =>\n\tisDate(value) ? value.getTime() : (value as number | string);\n\nconst compare = (value: unknown, operand: unknown): number => {\n\tconst a = order(value);\n\tconst b = order(operand);\n\tif (a < b) {\n\t\treturn -1;\n\t}\n\tif (a > b) {\n\t\treturn 1;\n\t}\n\treturn 0;\n};\n\nconst comparable = (value: unknown): boolean =>\n\tvalue !== null && value !== undefined;\n\ntype Classified = {\n\tcols: Column[];\n\tparams: unknown[];\n\tarrays: unknown[][];\n\tsqls: SQL[];\n\tops: string[];\n};\n\n/**\n * Split a condition's `queryChunks` into its parts. Drizzle interleaves columns,\n * bound `Param`s, value arrays (for `in`), nested `SQL` (connectives), and string\n * chunks that carry the operators/parens. Reading `queryChunks` is Drizzle's\n * internal shape, not a stable API — but every unrecognized form throws below\n * (→ refetch), so a version bump can only cost efficiency, never correctness.\n */\nconst classify = (chunks: unknown[]): Classified => {\n\tconst cols: Column[] = [];\n\tconst params: unknown[] = [];\n\tconst arrays: unknown[][] = [];\n\tconst sqls: SQL[] = [];\n\tconst ops: string[] = [];\n\tfor (const chunk of chunks) {\n\t\tif (is(chunk, SQL)) {\n\t\t\tsqls.push(chunk);\n\t\t} else if (is(chunk, Column)) {\n\t\t\tcols.push(chunk);\n\t\t} else if (is(chunk, Param)) {\n\t\t\tparams.push(chunk.value);\n\t\t} else if (Array.isArray(chunk)) {\n\t\t\tarrays.push(\n\t\t\t\tchunk.map((element) =>\n\t\t\t\t\tis(element, Param) ? element.value : element\n\t\t\t\t)\n\t\t\t);\n\t\t} else {\n\t\t\tconst raw = (chunk as { value?: unknown }).value;\n\t\t\tconst text = (\n\t\t\t\tArray.isArray(raw) ? raw.join('') : String(raw ?? '')\n\t\t\t).trim();\n\t\t\tif (text !== '' && text !== '(' && text !== ')') {\n\t\t\t\tops.push(text);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { arrays, cols, ops, params, sqls };\n};\n\nconst evaluateLeaf = (\n\tcolumn: Column,\n\top: string,\n\tparams: unknown[],\n\tarrays: unknown[][],\n\trow: Record<string, unknown>,\n\tpropFor: (column: Column) => string | undefined\n): boolean => {\n\tconst prop = propFor(column);\n\tif (prop === undefined) {\n\t\tthrow new UnsupportedDrizzleFilterError(`column ${column.name}`);\n\t}\n\tconst value = row[prop];\n\tconst operand = params[0];\n\tswitch (op) {\n\t\tcase '=':\n\t\t\treturn equals(value, operand);\n\t\tcase '<>':\n\t\t\treturn !equals(value, operand);\n\t\tcase '>':\n\t\t\treturn comparable(value) && compare(value, operand) > 0;\n\t\tcase '>=':\n\t\t\treturn comparable(value) && compare(value, operand) >= 0;\n\t\tcase '<':\n\t\t\treturn comparable(value) && compare(value, operand) < 0;\n\t\tcase '<=':\n\t\t\treturn comparable(value) && compare(value, operand) <= 0;\n\t\tcase 'in':\n\t\t\treturn (arrays[0] ?? []).some((item) => equals(value, item));\n\t\tcase 'not in':\n\t\t\treturn !(arrays[0] ?? []).some((item) => equals(value, item));\n\t\tcase 'is null':\n\t\t\treturn value === null || value === undefined;\n\t\tcase 'is not null':\n\t\t\treturn value !== null && value !== undefined;\n\t\tdefault:\n\t\t\tthrow new UnsupportedDrizzleFilterError(op);\n\t}\n};\n\nconst evaluateCondition = (\n\tnode: SQL,\n\trow: Record<string, unknown>,\n\tpropFor: (column: Column) => string | undefined\n): boolean => {\n\tconst { cols, params, arrays, sqls, ops } = classify(\n\t\t(node as unknown as { queryChunks: unknown[] }).queryChunks\n\t);\n\n\t// not (cond)\n\tif (\n\t\tops.length === 1 &&\n\t\tops[0] === 'not' &&\n\t\tsqls.length === 1 &&\n\t\tcols.length === 0\n\t) {\n\t\treturn !evaluateCondition(sqls[0]!, row, propFor);\n\t}\n\t// A lone nested condition wrapped in parens — unwrap and recurse.\n\tif (cols.length === 0 && sqls.length === 1 && ops.length === 0) {\n\t\treturn evaluateCondition(sqls[0]!, row, propFor);\n\t}\n\t// and / or over sub-conditions.\n\tif (cols.length === 0 && sqls.length >= 2 && ops.length > 0) {\n\t\tconst connective = ops[0];\n\t\tif (\n\t\t\t(connective === 'and' || connective === 'or') &&\n\t\t\tops.every((op) => op === connective)\n\t\t) {\n\t\t\tconst results = sqls.map((sql) =>\n\t\t\t\tevaluateCondition(sql, row, propFor)\n\t\t\t);\n\t\t\treturn connective === 'and'\n\t\t\t\t? results.every(Boolean)\n\t\t\t\t: results.some(Boolean);\n\t\t}\n\t\tthrow new UnsupportedDrizzleFilterError(ops.join(' '));\n\t}\n\t// Leaf comparison: one column, one operator.\n\tif (cols.length === 1 && sqls.length === 0 && ops.length === 1) {\n\t\treturn evaluateLeaf(cols[0]!, ops[0]!, params, arrays, row, propFor);\n\t}\n\tthrow new UnsupportedDrizzleFilterError(\n\t\tops.join(' ') || 'unrecognized condition'\n\t);\n};\n\n/**\n * Evaluate a Drizzle `where` condition against a plain row in JS — the\n * incremental matcher for {@link drizzleCollection}. Supports\n * `eq`/`ne`/`gt`/`gte`/`lt`/`lte`, `isNull`/`isNotNull`,\n * `inArray`/`notInArray`, and nested `and`/`or`/`not`; anything else throws\n * {@link UnsupportedDrizzleFilterError} (the engine then refetches). Rows are\n * read by JS property name, as Drizzle returns them.\n */\nexport const matchesDrizzleWhere = (\n\ttable: Table,\n\twhere: SQL,\n\trow: Record<string, unknown>\n): boolean => {\n\tconst nameToProp = new Map<string, string>();\n\tfor (const [prop, column] of Object.entries(getTableColumns(table))) {\n\t\tnameToProp.set(column.name, prop);\n\t}\n\n\treturn evaluateCondition(where, row, (column) =>\n\t\tnameToProp.get(column.name)\n\t);\n};\n",
|
|
8
8
|
"import type { SQL, Table } from 'drizzle-orm';\nimport { extractKeyFromWhere, keyTopic, tableTopic } from './topics';\n\nexport type DeriveReadTopicsOptions = {\n\t/**\n\t * Column (its JS property name on the table) to treat as the row key when\n\t * narrowing to a `table:key` topic. Defaults to the table's single\n\t * primary-key column; composite or absent primary keys disable row-level\n\t * narrowing.\n\t */\n\tkeyColumn?: string;\n};\n\nexport type DerivedReadTopics = {\n\t/** Topics this read depends on — subscribe to all of them. */\n\ttopics: string[];\n\t/**\n\t * `true` when derivation narrowed to a specific row (`table:key`); `false`\n\t * when it fell back to the whole-table topic.\n\t */\n\trowLevel: boolean;\n};\n\n/**\n * Derive the reactive topics a read of `table` (optionally filtered by `where`)\n * depends on. A recognised primary-key equality narrows to a single `table:key`\n * topic; everything else subscribes to the whole-table topic, over-invalidating\n * a little rather than missing an update.\n *\n * @example\n * deriveReadTopics(users); // { topics: ['users'], rowLevel: false }\n * deriveReadTopics(users, eq(users.id, 5)); // { topics: ['users:5'], rowLevel: true }\n * deriveReadTopics(users, gt(users.id, 5)); // { topics: ['users'], rowLevel: false }\n */\nexport const deriveReadTopics = (\n\ttable: Table,\n\twhere?: SQL,\n\toptions: DeriveReadTopicsOptions = {}\n): DerivedReadTopics => {\n\tconst key =\n\t\twhere === undefined\n\t\t\t? undefined\n\t\t\t: extractKeyFromWhere(table, where, options.keyColumn);\n\n\tif (key === undefined) {\n\t\treturn { topics: [tableTopic(table)], rowLevel: false };\n\t}\n\treturn { topics: [keyTopic(table, key)], rowLevel: true };\n};\n",
|
|
9
9
|
"import type { SQL, Table } from 'drizzle-orm';\nimport type { ReactiveHub } from '../../reactiveHub';\nimport {\n\textractKeyFromWhere,\n\textractRowKeys,\n\tkeyTopic,\n\ttableTopic\n} from './topics';\n\n/**\n * Drizzle write-side topic publishing (Tier 2).\n *\n * The mirror of {@link deriveReadTopics}: after a mutation commits, publish the\n * topics it invalidates so subscribed reads refetch. Every change publishes the\n * **table** topic (so list/table queries refresh) plus a **row** topic per\n * affected key (so row-level queries refresh) — the exact topics the read side\n * subscribes to.\n *\n * These are the \"route mutations through us\" change source from the roadmap:\n * call them right after your durable write. They work on any DB Drizzle supports\n * and never touch DB-specific machinery; out-of-band writes (caught later by CDC\n * adapters) are the only thing they miss.\n */\n\n/** The kind of mutation, forwarded in the change-event payload. */\nexport type ChangeOp = 'insert' | 'update' | 'delete';\n\n/** Payload carried by every change event the write side publishes. */\nexport type ChangePayload = {\n\t/** Name of the table that changed. */\n\ttable: string;\n\t/** Mutation kind, when the caller provided it. */\n\top?: ChangeOp;\n\t/** Affected row keys (empty for a table-wide change). */\n\tkeys: (string | number)[];\n};\n\nexport type PublishChangeOptions = {\n\t/** Row keys that changed; each emits a `table:key` topic. */\n\tkeys?: ReadonlyArray<string | number>;\n\top?: ChangeOp;\n};\n\n/**\n * Publish the reactive topics a change to `table` invalidates: the whole-table\n * topic (always) plus a `table:key` topic per affected row. Call after the\n * durable write commits. Returns the (de-duplicated) topics published.\n */\nexport const publishChange = (\n\thub: Pick<ReactiveHub, 'publish'>,\n\ttable: Table,\n\toptions: PublishChangeOptions = {}\n): string[] => {\n\tconst name = tableTopic(table);\n\tconst keys = options.keys === undefined ? [] : [...new Set(options.keys)];\n\tconst payload: ChangePayload = { table: name, op: options.op, keys };\n\tconst topics = [\n\t\t...new Set([name, ...keys.map((key) => keyTopic(table, key))])\n\t];\n\tfor (const topic of topics) {\n\t\thub.publish(topic, payload);\n\t}\n\treturn topics;\n};\n\nexport type PublishRowsOptions = {\n\t/** Key column (JS property name); defaults to the table's primary key. */\n\tkeyColumn?: string;\n\top?: ChangeOp;\n};\n\n/**\n * Publish change topics for a set of rows — typically the output of a mutation's\n * `.returning()`, which yields real keys including auto-generated ones. Reads\n * each row's primary-key column (or `keyColumn`) to emit `table:key` topics.\n *\n * @example\n * const rows = await db.insert(users).values(input).returning();\n * publishRows(hub, users, rows, { op: 'insert' });\n */\nexport const publishRows = (\n\thub: Pick<ReactiveHub, 'publish'>,\n\ttable: Table,\n\trows: ReadonlyArray<Record<string, unknown>>,\n\toptions: PublishRowsOptions = {}\n): string[] =>\n\tpublishChange(hub, table, {\n\t\tkeys: extractRowKeys(table, rows, options.keyColumn),\n\t\top: options.op\n\t});\n\nexport type PublishWhereOptions = {\n\t/** Key column (JS property name); defaults to the table's primary key. */\n\tkeyColumn?: string;\n\top?: ChangeOp;\n};\n\n/**\n * Publish change topics for an `update`/`delete` identified by a `where` filter.\n * A simple primary-key equality narrows to that row's topic; any other filter\n * publishes just the table topic, so every affected subscriber refetches and\n * re-evaluates.\n *\n * @example\n * await db.update(users).set(patch).where(eq(users.id, id));\n * publishWhere(hub, users, eq(users.id, id), { op: 'update' });\n */\nexport const publishWhere = (\n\thub: Pick<ReactiveHub, 'publish'>,\n\ttable: Table,\n\twhere: SQL,\n\toptions: PublishWhereOptions = {}\n): string[] => {\n\tconst key = extractKeyFromWhere(table, where, options.keyColumn);\n\treturn publishChange(hub, table, {\n\t\tkeys: key === undefined ? [] : [key],\n\t\top: options.op\n\t});\n};\n"
|
|
10
10
|
],
|
|
11
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
12
|
-
"debugId": "
|
|
11
|
+
"mappings": "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",
|
|
12
|
+
"debugId": "440BA9164FF10CD464756E2164756E21",
|
|
13
13
|
"names": []
|
|
14
14
|
}
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/serializer.ts", "../src/client/syncCollection.ts", "../src/adapters/tanstack-db/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { ClientFrame, ServerFrame } from './engine/connection';\n\n/**\n * Wire-format adapter (1.16.0). The engine and client default to JSON, but\n * for high-throughput deployments — a customer pushing 1 MB initial\n * snapshots, a tenant fan-out hitting 100k frames/sec — a binary serializer\n * (msgpack, cbor, or a custom tagged layout) cuts both bandwidth and\n * parse-side CPU.\n *\n * Both ends of the connection MUST use the same serializer. The default\n * `jsonSerializer` keeps every existing client + server pair working\n * unchanged; opt in to a different one on BOTH `syncSocket` and the client\n * lib to gain the win.\n *\n * The serializer only handles the wire format. Frame-shape validation\n * stays in the engine (`parseFrame` in connection.ts) — it runs on the\n * decoded object, so the same validation works for JSON, msgpack, etc.\n */\nexport type FrameSerializer = {\n\t/** Server→client: encode an outgoing `ServerFrame` for transport. */\n\tencodeServer: (frame: ServerFrame) => string | ArrayBufferLike | Uint8Array;\n\t/** Client→server: encode an outgoing `ClientFrame` for transport. */\n\tencodeClient: (frame: ClientFrame) => string | ArrayBufferLike | Uint8Array;\n\t/**\n\t * Deserialize a wire payload into a raw object. Return `null` for\n\t * unparseable input — the engine's validation step turns that into\n\t * a protocol error.\n\t */\n\tdecode: (raw: unknown) => unknown;\n};\n\n/**\n * Default JSON serializer — what `@absolutejs/sync` has always shipped.\n * Strings go through `JSON.parse`; already-parsed objects pass through\n * (some WS adapters auto-decode). `Uint8Array` / `ArrayBuffer` get\n * UTF-8 decoded first (binary WS frames carrying JSON text).\n */\nexport const jsonSerializer: FrameSerializer = {\n\tdecode: (raw: unknown): unknown => {\n\t\tif (typeof raw === 'string') {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(raw);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tif (raw instanceof Uint8Array) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(new TextDecoder().decode(raw));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tif (raw instanceof ArrayBuffer) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(
|
|
6
|
-
"import type { ServerFrame } from '../engine/connection';\nimport type { RowKey } from '../engine/types';\nimport { jsonSerializer, type FrameSerializer } from '../serializer';\n\nexport type { ServerFrame } from '../engine/connection';\n\nexport type SyncCollectionStatus = 'connecting' | 'ready' | 'closed';\n\nexport type SyncCollectionState<T> = {\n\t/** Visible rows: the server state with pending optimistic mutations applied. */\n\tdata: T[];\n\t/** Connection/sync status. */\n\tstatus: SyncCollectionStatus;\n\t/** Last error message from the server, or `undefined`. */\n\terror: unknown;\n};\n\n/** A working set a mutation's optimistic effect edits in place. */\nexport type OptimisticDraft<T> = {\n\t/** Insert or replace a row by key. */\n\tset: (row: T) => void;\n\t/** Remove a row by key. */\n\tdelete: (key: RowKey) => void;\n};\n\nexport type MutateOptions<T> = {\n\t/** Registered server mutation name. */\n\tname: string;\n\t/** Arguments forwarded to the mutation handler. */\n\targs?: unknown;\n\t/**\n\t * Apply this mutation's effect to the local set immediately for instant UI.\n\t * Reverted automatically if the server rejects it. Omit for a non-optimistic\n\t * mutation (UI updates only once the authoritative diff arrives).\n\t */\n\toptimistic?: (draft: OptimisticDraft<T>) => void;\n};\n\n/** A pending mutation persisted for replay across reloads. */\nexport type PendingMutationRecord = {\n\tmutationId: number;\n\tname: string;\n\targs: unknown;\n};\n\n/**\n * Durable storage for the pending-mutation queue, so unconfirmed mutations\n * survive a page reload (offline). The queue is replayed when the socket\n * connects; records are dropped as they're acked.\n */\nexport type MutationStorage = {\n\tload: () => PendingMutationRecord[] | Promise<PendingMutationRecord[]>;\n\tsave: (records: PendingMutationRecord[]) => void | Promise<void>;\n};\n\n/**\n * A {@link MutationStorage} backed by `localStorage` under `key`. No-ops where\n * `localStorage` is unavailable (e.g. SSR).\n */\nexport const localStorageMutationStorage = (key: string): MutationStorage => ({\n\tload: () => {\n\t\tconst raw = globalThis.localStorage?.getItem(key);\n\t\treturn raw ? (JSON.parse(raw) as PendingMutationRecord[]) : [];\n\t},\n\tsave: (records) => {\n\t\tglobalThis.localStorage?.setItem(key, JSON.stringify(records));\n\t}\n});\n\n/**\n * A persisted snapshot of a collection's server-authoritative rows plus the\n * change-feed `version` they were current as of — the cursor used to resume on\n * the next connect (catch-up diff if the server's changelog still covers it, a\n * fresh snapshot otherwise).\n */\nexport type CollectionCacheSnapshot<T> = {\n\trows: T[];\n\tversion: number;\n};\n\n/**\n * Durable local cache of a collection's confirmed rows, so reads are instant on\n * reload and available offline (local-first). Distinct from {@link\n * MutationStorage}, which persists *unconfirmed writes*: the cache is the\n * read side, the queue is the write side. On startup the cache hydrates the\n * collection before the socket connects; the engine then resumes from the\n * cached `version`.\n */\nexport type CollectionCache<T> = {\n\tload: () =>\n\t\t| CollectionCacheSnapshot<T>\n\t\t| undefined\n\t\t| Promise<CollectionCacheSnapshot<T> | undefined>;\n\tsave: (snapshot: CollectionCacheSnapshot<T>) => void | Promise<void>;\n\t/** Drop the cached snapshot (optional). */\n\tclear?: () => void | Promise<void>;\n};\n\n/**\n * A {@link CollectionCache} backed by `localStorage` under `key`. Synchronous\n * and capped (~5MB); fine for small collections. No-ops where `localStorage`\n * is unavailable (e.g. SSR). For larger sets use {@link indexedDbCollectionCache}.\n */\nexport const localStorageCollectionCache = <T>(\n\tkey: string\n): CollectionCache<T> => ({\n\tload: () => {\n\t\tconst raw = globalThis.localStorage?.getItem(key);\n\t\treturn raw\n\t\t\t? (JSON.parse(raw) as CollectionCacheSnapshot<T>)\n\t\t\t: undefined;\n\t},\n\tsave: (snapshot) => {\n\t\tglobalThis.localStorage?.setItem(key, JSON.stringify(snapshot));\n\t},\n\tclear: () => {\n\t\tglobalThis.localStorage?.removeItem(key);\n\t}\n});\n\nconst openIndexedDb = (\n\tdatabaseName: string,\n\tstoreName: string\n): Promise<IDBDatabase> =>\n\tnew Promise((resolve, reject) => {\n\t\tconst request = globalThis.indexedDB.open(databaseName, 1);\n\t\trequest.onupgradeneeded = () => {\n\t\t\trequest.result.createObjectStore(storeName);\n\t\t};\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error);\n\t});\n\n/**\n * A {@link CollectionCache} backed by IndexedDB — the durable, large-capacity\n * local-first store. Asynchronous; one row per collection `key` in a shared\n * object store. No-ops (resolving to `undefined`) where `indexedDB` is\n * unavailable (e.g. SSR), so the collection falls back to the server snapshot.\n */\nexport const indexedDbCollectionCache = <T>({\n\tkey,\n\tdatabaseName = 'absolutejs-sync',\n\tstoreName = 'collections'\n}: {\n\t/** Distinct entry name within the store (e.g. the collection + params). */\n\tkey: string;\n\t/** IndexedDB database name. Defaults to `absolutejs-sync`. */\n\tdatabaseName?: string;\n\t/** Object-store name. Defaults to `collections`. */\n\tstoreName?: string;\n}): CollectionCache<T> => {\n\tlet handle: Promise<IDBDatabase> | undefined;\n\tconst database = () => {\n\t\thandle ??= openIndexedDb(databaseName, storeName);\n\t\treturn handle;\n\t};\n\tconst withStore = async <R>(\n\t\tmode: IDBTransactionMode,\n\t\trun: (store: IDBObjectStore) => IDBRequest\n\t): Promise<R | undefined> => {\n\t\tif (globalThis.indexedDB === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst db = await database();\n\t\treturn new Promise<R>((resolve, reject) => {\n\t\t\tconst request = run(\n\t\t\t\tdb.transaction(storeName, mode).objectStore(storeName)\n\t\t\t);\n\t\t\trequest.onsuccess = () => resolve(request.result as R);\n\t\t\trequest.onerror = () => reject(request.error);\n\t\t});\n\t};\n\n\treturn {\n\t\tload: () =>\n\t\t\twithStore<CollectionCacheSnapshot<T>>('readonly', (store) =>\n\t\t\t\tstore.get(key)\n\t\t\t),\n\t\tsave: async (snapshot) => {\n\t\t\tawait withStore('readwrite', (store) => store.put(snapshot, key));\n\t\t},\n\t\tclear: async () => {\n\t\t\tawait withStore('readwrite', (store) => store.delete(key));\n\t\t}\n\t};\n};\n\nexport type SyncCollectionOptions<T> = {\n\t/** WebSocket URL of the {@link syncSocket} endpoint (e.g. `ws://host/sync/ws`). */\n\turl: string;\n\t/** Registered collection name to subscribe to. */\n\tcollection: string;\n\t/** Query params forwarded to the server collection's hydrate/match/authorize. */\n\tparams?: unknown;\n\t/** Row identity, used to apply diffs and optimistic edits. Defaults to `row.id`. */\n\tkey?: (row: T) => RowKey;\n\t/** WebSocket implementation; defaults to the global one (pass for tests/SSR). */\n\twebSocketImpl?: typeof WebSocket;\n\t/**\n\t * Base reconnect delay (ms), doubled each attempt up to `maxReconnectMs`.\n\t * Set 0 to disable auto-reconnect. Defaults to 500.\n\t */\n\treconnectMs?: number;\n\t/** Maximum reconnect backoff (ms). Defaults to 10000. */\n\tmaxReconnectMs?: number;\n\t/**\n\t * Persist the pending-mutation queue so it survives a reload (offline) and\n\t * replays on connect. See {@link localStorageMutationStorage}.\n\t */\n\tstorage?: MutationStorage;\n\t/**\n\t * Persist confirmed rows locally for instant reads on reload and offline\n\t * (local-first). Hydrated before the socket connects; the engine then\n\t * resumes from the cached version (catch-up diff, or a fresh snapshot if the\n\t * server's changelog no longer covers it). See {@link\n\t * localStorageCollectionCache} / {@link indexedDbCollectionCache}.\n\t */\n\tcache?: CollectionCache<T>;\n\t/** Called with each server error message. */\n\tonError?: (error: unknown) => void;\n\t/**\n\t * Wire-format serializer (1.16.0). Defaults to `jsonSerializer`. MUST\n\t * match the server's `syncSocket` serializer.\n\t */\n\tserializer?: FrameSerializer;\n};\n\nexport type SyncCollection<T> = {\n\t/** Current state snapshot (stable reference until the next change). */\n\tget: () => SyncCollectionState<T>;\n\t/** Subscribe to state changes; returns an unsubscribe. */\n\tsubscribe: (\n\t\tlistener: (state: SyncCollectionState<T>) => void\n\t) => () => void;\n\t/**\n\t * Run a server mutation, optionally applying it optimistically. Resolves with\n\t * the server's result on ack, rejects (and rolls back) on reject. Pending\n\t * mutations are replayed when the socket reconnects, so they survive a drop.\n\t */\n\tmutate: <R = unknown>(options: MutateOptions<T>) => Promise<R>;\n\t/**\n\t * Force-close the underlying WebSocket without tearing down state. The\n\t * auto-reconnect loop fires after `reconnectMs`; the collection's\n\t * `appliedVersion` is preserved so the resumed subscribe carries `since`\n\t * and the engine replies with a catch-up diff (or a fresh snapshot if\n\t * the change log no longer covers the gap).\n\t *\n\t * Useful for simulating an offline blip in tests and benches that need\n\t * to measure resume cost specifically (vs cold-hydration on a fresh\n\t * collection). No-op if the collection has been `close()`d.\n\t */\n\tdisconnect: () => void;\n\t/** Unsubscribe on the server, close the socket, and stop reconnecting. */\n\tclose: () => void;\n};\n\n// One store subscribes to exactly one collection, so a fixed frame id suffices.\nconst SUBSCRIPTION_ID = 's';\n\ntype PendingMutation<T> = {\n\tmutationId: number;\n\tname: string;\n\targs: unknown;\n\toptimistic?: (draft: OptimisticDraft<T>) => void;\n\tresolve: (result: unknown) => void;\n\treject: (error: unknown) => void;\n};\n\n/**\n * A live collection backed by the WebSocket sync engine. Reads: connect,\n * subscribe, apply the server's snapshot then row-level diffs, re-sync on\n * reconnect. Writes: {@link SyncCollection.mutate} applies an optimistic overlay\n * immediately, sends the mutation, and reconciles on ack (drop the overlay — the\n * authoritative diff already arrived) or reject (roll back). Framework-agnostic\n * (`get` + `subscribe`).\n *\n * Mutations are replayed on reconnect, so make server mutations idempotent —\n * delivery is at-least-once if an ack is lost across a drop.\n */\nexport const createSyncCollection = <T>(\n\toptions: SyncCollectionOptions<T>\n): SyncCollection<T> => {\n\tconst key = options.key ?? ((row: T) => (row as { id: RowKey }).id);\n\tconst reconnectMs = options.reconnectMs ?? 500;\n\tconst maxReconnectMs = options.maxReconnectMs ?? 10_000;\n\tconst serializer: FrameSerializer = options.serializer ?? jsonSerializer;\n\tconst Impl = options.webSocketImpl ?? globalThis.WebSocket;\n\tif (!Impl) {\n\t\tthrow new Error(\n\t\t\t'createSyncCollection requires WebSocket. Run in a browser or pass webSocketImpl.'\n\t\t);\n\t}\n\n\t// Server-authoritative rows; `pending` is the optimistic overlay on top.\n\tconst confirmed = new Map<RowKey, T>();\n\tconst pending: PendingMutation<T>[] = [];\n\tlet mutationSeq = 0;\n\n\tlet state: SyncCollectionState<T> = {\n\t\tdata: [],\n\t\tstatus: 'connecting',\n\t\terror: undefined\n\t};\n\tconst listeners = new Set<(state: SyncCollectionState<T>) => void>();\n\tconst setState = (patch: Partial<SyncCollectionState<T>>) => {\n\t\tstate = { ...state, ...patch };\n\t\tfor (const listener of listeners) {\n\t\t\tlistener(state);\n\t\t}\n\t};\n\n\t/** Recompute visible rows = confirmed + pending optimistic effects. */\n\tconst recompute = (patch: Partial<SyncCollectionState<T>> = {}) => {\n\t\tconst working = new Map(confirmed);\n\t\tconst draft: OptimisticDraft<T> = {\n\t\t\tset: (row) => working.set(key(row), row),\n\t\t\tdelete: (rowKey) => working.delete(rowKey)\n\t\t};\n\t\tfor (const mutation of pending) {\n\t\t\tmutation.optimistic?.(draft);\n\t\t}\n\t\tsetState({ ...patch, data: [...working.values()] });\n\t};\n\n\tlet socket: WebSocket | undefined;\n\tlet connected = false;\n\tlet closed = false;\n\tlet attempt = 0;\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n\t// Highest change-feed version applied; sent as `since` to resume on reconnect.\n\tlet appliedVersion = 0;\n\t// 1.18.0: opaque cross-instance resume cursor. When the server surfaces\n\t// one on snapshot/diff/frame, capture it and round-trip it as `since`\n\t// in preference to `appliedVersion`. Resume across cluster shards.\n\tlet appliedCursor: string | undefined;\n\n\tconst persist = () => {\n\t\tvoid options.storage?.save(\n\t\t\tpending.map((mutation) => ({\n\t\t\t\tmutationId: mutation.mutationId,\n\t\t\t\tname: mutation.name,\n\t\t\t\targs: mutation.args\n\t\t\t}))\n\t\t);\n\t};\n\n\t// Coalesce a burst of confirmed changes (a frame of diffs) into one cache\n\t// write per tick. Persists only the server-authoritative set — never the\n\t// optimistic overlay (those live in the mutation queue instead).\n\tlet cacheScheduled = false;\n\tconst persistCache = () => {\n\t\tif (options.cache === undefined || cacheScheduled) {\n\t\t\treturn;\n\t\t}\n\t\tcacheScheduled = true;\n\t\tqueueMicrotask(() => {\n\t\t\tcacheScheduled = false;\n\t\t\tvoid options.cache?.save({\n\t\t\t\trows: [...confirmed.values()],\n\t\t\t\tversion: appliedVersion\n\t\t\t});\n\t\t});\n\t};\n\n\tconst settlePending = (mutationId: number) => {\n\t\tconst index = pending.findIndex(\n\t\t\t(mutation) => mutation.mutationId === mutationId\n\t\t);\n\t\tif (index === -1) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst [mutation] = pending.splice(index, 1);\n\t\tpersist();\n\t\treturn mutation;\n\t};\n\n\tconst applyFrame = (frame: ServerFrame<T>) => {\n\t\tif (frame.type === 'snapshot') {\n\t\t\tconfirmed.clear();\n\t\t\tfor (const row of frame.rows) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tif (frame.version !== undefined) {\n\t\t\t\tappliedVersion = frame.version;\n\t\t\t}\n\t\t\tif (frame.cursor !== undefined) {\n\t\t\t\tappliedCursor = frame.cursor;\n\t\t\t}\n\t\t\tpersistCache();\n\t\t\trecompute({ status: 'ready', error: undefined });\n\t\t} else if (frame.type === 'diff') {\n\t\t\tfor (const row of frame.removed) {\n\t\t\t\tconfirmed.delete(key(row));\n\t\t\t}\n\t\t\tfor (const row of frame.added) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tfor (const row of frame.changed) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tif (frame.cursor !== undefined) {\n\t\t\t\tappliedCursor = frame.cursor;\n\t\t\t}\n\t\t\tif (frame.version !== undefined) {\n\t\t\t\tappliedVersion = Math.max(appliedVersion, frame.version);\n\t\t\t}\n\t\t\tpersistCache();\n\t\t\t// A diff only arrives once subscribed — including the catch-up diff a\n\t\t\t// resume replies with — so receiving one means we're live.\n\t\t\trecompute({ status: 'ready', error: undefined });\n\t\t} else if (frame.type === 'error') {\n\t\t\tsetState({ error: frame.message });\n\t\t\toptions.onError?.(frame.message);\n\t\t} else if (frame.type === 'ack') {\n\t\t\t// The authoritative diff already arrived (ordered before the ack), so\n\t\t\t// dropping the overlay leaves the confirmed row in place — no flicker.\n\t\t\tconst mutation = settlePending(frame.mutationId);\n\t\t\tif (mutation !== undefined) {\n\t\t\t\trecompute();\n\t\t\t\tmutation.resolve(frame.result);\n\t\t\t}\n\t\t} else if (frame.type === 'reject') {\n\t\t\t// roll the optimistic overlay back.\n\t\t\tconst mutation = settlePending(frame.mutationId);\n\t\t\tif (mutation !== undefined) {\n\t\t\t\trecompute();\n\t\t\t\tmutation.reject(new Error(String(frame.message)));\n\t\t\t}\n\t\t}\n\t\t// A `frame` (multi-collection batch) never reaches a single-collection\n\t\t// store — that's the multiplexed createSyncClient's job — so ignore it.\n\t};\n\n\tconst wsSend = (payload: string | ArrayBufferLike | Uint8Array) => {\n\t\tsocket?.send(payload as string);\n\t};\n\n\tconst sendMutate = (mutation: PendingMutation<T>) => {\n\t\tif (connected) {\n\t\t\twsSend(serializer.encodeClient({\n\t\t\t\ttype: 'mutate',\n\t\t\t\tmutationId: mutation.mutationId,\n\t\t\t\tname: mutation.name,\n\t\t\t\targs: mutation.args\n\t\t\t}));\n\t\t}\n\t};\n\n\tconst connect = () => {\n\t\tif (closed) {\n\t\t\treturn;\n\t\t}\n\t\tsetState({ status: 'connecting' });\n\t\tconst ws = new Impl(options.url);\n\t\tsocket = ws;\n\t\tws.onopen = () => {\n\t\t\tattempt = 0;\n\t\t\tconnected = true;\n\t\t\tws.send(serializer.encodeClient({\n\t\t\t\ttype: 'subscribe',\n\t\t\t\tid: SUBSCRIPTION_ID,\n\t\t\t\tcollection: options.collection,\n\t\t\t\tparams: options.params,\n\t\t\t\t// 1.18.0: prefer the opaque cross-instance cursor when we\n\t\t\t\t// have one; fall back to the local-version number for pre-1.17\n\t\t\t\t// servers (or before any cursor has arrived).\n\t\t\t\tsince:\n\t\t\t\t\tappliedCursor ??\n\t\t\t\t\t(appliedVersion > 0 ? appliedVersion : undefined)\n\t\t\t}) as string);\n\t\t\t// Replay anything still pending across the (re)connect.\n\t\t\tfor (const mutation of pending) {\n\t\t\t\tsendMutate(mutation);\n\t\t\t}\n\t\t};\n\t\tws.onmessage = (event) => {\n\t\t\ttry {\n\t\t\t\tconst decoded = serializer.decode(event.data);\n\t\t\t\tif (decoded !== null && typeof decoded === 'object') {\n\t\t\t\t\tapplyFrame(decoded as ServerFrame<T>);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// ignore non-JSON frames\n\t\t\t}\n\t\t};\n\t\tws.onclose = () => {\n\t\t\tconnected = false;\n\t\t\tif (closed || reconnectMs <= 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst delay = Math.min(reconnectMs * 2 ** attempt, maxReconnectMs);\n\t\t\tattempt += 1;\n\t\t\treconnectTimer = setTimeout(connect, delay);\n\t\t};\n\t};\n\n\t// Reload recovery: re-queue persisted unconfirmed mutations so they replay on\n\t// connect. They carry no optimistic effect or promise (the resumed/snapshot\n\t// state is authoritative); resending produces the diffs that bring them in.\n\tconst hydratePersisted = async () => {\n\t\tif (options.storage === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tconst records = await options.storage.load();\n\t\tfor (const record of records) {\n\t\t\tif (pending.some((m) => m.mutationId === record.mutationId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpending.push({\n\t\t\t\tmutationId: record.mutationId,\n\t\t\t\tname: record.name,\n\t\t\t\targs: record.args,\n\t\t\t\tresolve: () => {},\n\t\t\t\treject: () => {}\n\t\t\t});\n\t\t\tmutationSeq = Math.max(mutationSeq, record.mutationId);\n\t\t}\n\t\tif (connected) {\n\t\t\tfor (const mutation of pending) {\n\t\t\t\tsendMutate(mutation);\n\t\t\t}\n\t\t}\n\t};\n\n\t// Local-first: load cached rows + version before connecting, so reads are\n\t// instant on reload and available offline. The subscribe then resumes from\n\t// the cached version — a catch-up diff if the server's changelog still\n\t// covers it, else a fresh snapshot that replaces the stale cache.\n\tconst hydrateCache = async () => {\n\t\tif (options.cache === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tlet snapshot: CollectionCacheSnapshot<T> | undefined;\n\t\ttry {\n\t\t\tsnapshot = await options.cache.load();\n\t\t} catch {\n\t\t\treturn; // corrupt/unavailable cache: fall back to the server snapshot\n\t\t}\n\t\t// Don't clobber server data if a frame somehow already landed.\n\t\tif (snapshot === undefined || appliedVersion > 0) {\n\t\t\treturn;\n\t\t}\n\t\tfor (const row of snapshot.rows) {\n\t\t\tconfirmed.set(key(row), row);\n\t\t}\n\t\tappliedVersion = snapshot.version;\n\t\trecompute(); // show cached rows immediately (status stays 'connecting')\n\t};\n\n\tif (options.cache === undefined) {\n\t\t// No cache: preserve the original connect-then-hydrate ordering/timing.\n\t\tconnect();\n\t\tvoid hydratePersisted();\n\t} else {\n\t\t// Cache: hydrate reads + queued writes first, then connect so the\n\t\t// subscribe carries the cached resume version.\n\t\tvoid (async () => {\n\t\t\tawait hydrateCache();\n\t\t\tawait hydratePersisted();\n\t\t\tconnect();\n\t\t})();\n\t}\n\n\treturn {\n\t\tget: () => state,\n\t\tsubscribe: (listener) => {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t};\n\t\t},\n\t\tmutate: <R = unknown>(mutateOptions: MutateOptions<T>) =>\n\t\t\tnew Promise<R>((resolve, reject) => {\n\t\t\t\tconst mutation: PendingMutation<T> = {\n\t\t\t\t\tmutationId: (mutationSeq += 1),\n\t\t\t\t\tname: mutateOptions.name,\n\t\t\t\t\targs: mutateOptions.args,\n\t\t\t\t\toptimistic: mutateOptions.optimistic,\n\t\t\t\t\tresolve: (result) => resolve(result as R),\n\t\t\t\t\treject\n\t\t\t\t};\n\t\t\t\tpending.push(mutation);\n\t\t\t\tpersist();\n\t\t\t\trecompute(); // apply the optimistic overlay immediately\n\t\t\t\tsendMutate(mutation);\n\t\t\t}),\n\t\tdisconnect: () => {\n\t\t\t// Force-close the WS without tearing down state. The existing\n\t\t\t// `ws.onclose` handler schedules a reconnect via the auto-\n\t\t\t// reconnect loop (unless the collection has been `close()`d).\n\t\t\t// `appliedVersion` is preserved, so the resumed subscribe carries\n\t\t\t// `since` and the engine sends a catch-up diff (or snapshot if\n\t\t\t// the change log can't cover the gap).\n\t\t\tif (closed || socket === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsocket.close();\n\t\t\t} catch {\n\t\t\t\t// already closing/closed\n\t\t\t}\n\t\t},\n\t\tclose: () => {\n\t\t\tif (closed) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclosed = true;\n\t\t\tconnected = false;\n\t\t\tif (reconnectTimer !== undefined) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\twsSend(serializer.encodeClient({\n\t\t\t\t\ttype: 'unsubscribe',\n\t\t\t\t\tid: SUBSCRIPTION_ID\n\t\t\t\t}));\n\t\t\t\tsocket?.close();\n\t\t\t} catch {\n\t\t\t\t// socket already closing/closed\n\t\t\t}\n\t\t\t// Fail any still-pending mutations so their promises don't hang.\n\t\t\tfor (const mutation of pending.splice(0)) {\n\t\t\t\tmutation.reject(new Error('sync collection closed'));\n\t\t\t}\n\t\t\tpersist();\n\t\t\tsetState({ status: 'closed' });\n\t\t\tlisteners.clear();\n\t\t}\n\t};\n};\n",
|
|
5
|
+
"import type { ClientFrame, ServerFrame } from './engine/connection';\n\n/**\n * Wire-format adapter (1.16.0). The engine and client default to JSON, but\n * for high-throughput deployments — a customer pushing 1 MB initial\n * snapshots, a tenant fan-out hitting 100k frames/sec — a binary serializer\n * (msgpack, cbor, or a custom tagged layout) cuts both bandwidth and\n * parse-side CPU.\n *\n * Both ends of the connection MUST use the same serializer. The default\n * `jsonSerializer` keeps every existing client + server pair working\n * unchanged; opt in to a different one on BOTH `syncSocket` and the client\n * lib to gain the win.\n *\n * The serializer only handles the wire format. Frame-shape validation\n * stays in the engine (`parseFrame` in connection.ts) — it runs on the\n * decoded object, so the same validation works for JSON, msgpack, etc.\n */\nexport type FrameSerializer = {\n\t/** Server→client: encode an outgoing `ServerFrame` for transport. */\n\tencodeServer: (frame: ServerFrame) => string | ArrayBufferLike | Uint8Array;\n\t/** Client→server: encode an outgoing `ClientFrame` for transport. */\n\tencodeClient: (frame: ClientFrame) => string | ArrayBufferLike | Uint8Array;\n\t/**\n\t * Deserialize a wire payload into a raw object. Return `null` for\n\t * unparseable input — the engine's validation step turns that into\n\t * a protocol error.\n\t */\n\tdecode: (raw: unknown) => unknown;\n};\n\n/**\n * Default JSON serializer — what `@absolutejs/sync` has always shipped.\n * Strings go through `JSON.parse`; already-parsed objects pass through\n * (some WS adapters auto-decode). `Uint8Array` / `ArrayBuffer` get\n * UTF-8 decoded first (binary WS frames carrying JSON text).\n */\nexport const jsonSerializer: FrameSerializer = {\n\tdecode: (raw: unknown): unknown => {\n\t\tif (typeof raw === 'string') {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(raw);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tif (raw instanceof Uint8Array) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(new TextDecoder().decode(raw));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tif (raw instanceof ArrayBuffer) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(\n\t\t\t\t\tnew TextDecoder().decode(new Uint8Array(raw))\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn raw;\n\t},\n\tencodeClient: (frame: ClientFrame): string => JSON.stringify(frame),\n\tencodeServer: (frame: ServerFrame): string => JSON.stringify(frame)\n};\n",
|
|
6
|
+
"import type { ServerFrame } from '../engine/connection';\nimport type { RowKey } from '../engine/types';\nimport { jsonSerializer, type FrameSerializer } from '../serializer';\n\nexport type { ServerFrame } from '../engine/connection';\n\nexport type SyncCollectionStatus = 'connecting' | 'ready' | 'closed';\n\nexport type SyncCollectionState<T> = {\n\t/** Visible rows: the server state with pending optimistic mutations applied. */\n\tdata: T[];\n\t/** Connection/sync status. */\n\tstatus: SyncCollectionStatus;\n\t/** Last error message from the server, or `undefined`. */\n\terror: unknown;\n};\n\n/** A working set a mutation's optimistic effect edits in place. */\nexport type OptimisticDraft<T> = {\n\t/** Insert or replace a row by key. */\n\tset: (row: T) => void;\n\t/** Remove a row by key. */\n\tdelete: (key: RowKey) => void;\n};\n\nexport type MutateOptions<T> = {\n\t/** Registered server mutation name. */\n\tname: string;\n\t/** Arguments forwarded to the mutation handler. */\n\targs?: unknown;\n\t/**\n\t * Apply this mutation's effect to the local set immediately for instant UI.\n\t * Reverted automatically if the server rejects it. Omit for a non-optimistic\n\t * mutation (UI updates only once the authoritative diff arrives).\n\t */\n\toptimistic?: (draft: OptimisticDraft<T>) => void;\n};\n\n/** A pending mutation persisted for replay across reloads. */\nexport type PendingMutationRecord = {\n\tmutationId: number;\n\tname: string;\n\targs: unknown;\n};\n\n/**\n * Durable storage for the pending-mutation queue, so unconfirmed mutations\n * survive a page reload (offline). The queue is replayed when the socket\n * connects; records are dropped as they're acked.\n */\nexport type MutationStorage = {\n\tload: () => PendingMutationRecord[] | Promise<PendingMutationRecord[]>;\n\tsave: (records: PendingMutationRecord[]) => void | Promise<void>;\n};\n\n/**\n * A {@link MutationStorage} backed by `localStorage` under `key`. No-ops where\n * `localStorage` is unavailable (e.g. SSR).\n */\nexport const localStorageMutationStorage = (key: string): MutationStorage => ({\n\tload: () => {\n\t\tconst raw = globalThis.localStorage?.getItem(key);\n\t\treturn raw ? (JSON.parse(raw) as PendingMutationRecord[]) : [];\n\t},\n\tsave: (records) => {\n\t\tglobalThis.localStorage?.setItem(key, JSON.stringify(records));\n\t}\n});\n\n/**\n * A persisted snapshot of a collection's server-authoritative rows plus the\n * change-feed `version` they were current as of — the cursor used to resume on\n * the next connect (catch-up diff if the server's changelog still covers it, a\n * fresh snapshot otherwise).\n */\nexport type CollectionCacheSnapshot<T> = {\n\trows: T[];\n\tversion: number;\n};\n\n/**\n * Durable local cache of a collection's confirmed rows, so reads are instant on\n * reload and available offline (local-first). Distinct from {@link\n * MutationStorage}, which persists *unconfirmed writes*: the cache is the\n * read side, the queue is the write side. On startup the cache hydrates the\n * collection before the socket connects; the engine then resumes from the\n * cached `version`.\n */\nexport type CollectionCache<T> = {\n\tload: () =>\n\t\t| CollectionCacheSnapshot<T>\n\t\t| undefined\n\t\t| Promise<CollectionCacheSnapshot<T> | undefined>;\n\tsave: (snapshot: CollectionCacheSnapshot<T>) => void | Promise<void>;\n\t/** Drop the cached snapshot (optional). */\n\tclear?: () => void | Promise<void>;\n};\n\n/**\n * A {@link CollectionCache} backed by `localStorage` under `key`. Synchronous\n * and capped (~5MB); fine for small collections. No-ops where `localStorage`\n * is unavailable (e.g. SSR). For larger sets use {@link indexedDbCollectionCache}.\n */\nexport const localStorageCollectionCache = <T>(\n\tkey: string\n): CollectionCache<T> => ({\n\tload: () => {\n\t\tconst raw = globalThis.localStorage?.getItem(key);\n\t\treturn raw\n\t\t\t? (JSON.parse(raw) as CollectionCacheSnapshot<T>)\n\t\t\t: undefined;\n\t},\n\tsave: (snapshot) => {\n\t\tglobalThis.localStorage?.setItem(key, JSON.stringify(snapshot));\n\t},\n\tclear: () => {\n\t\tglobalThis.localStorage?.removeItem(key);\n\t}\n});\n\nconst openIndexedDb = (\n\tdatabaseName: string,\n\tstoreName: string\n): Promise<IDBDatabase> =>\n\tnew Promise((resolve, reject) => {\n\t\tconst request = globalThis.indexedDB.open(databaseName, 1);\n\t\trequest.onupgradeneeded = () => {\n\t\t\trequest.result.createObjectStore(storeName);\n\t\t};\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error);\n\t});\n\n/**\n * A {@link CollectionCache} backed by IndexedDB — the durable, large-capacity\n * local-first store. Asynchronous; one row per collection `key` in a shared\n * object store. No-ops (resolving to `undefined`) where `indexedDB` is\n * unavailable (e.g. SSR), so the collection falls back to the server snapshot.\n */\nexport const indexedDbCollectionCache = <T>({\n\tkey,\n\tdatabaseName = 'absolutejs-sync',\n\tstoreName = 'collections'\n}: {\n\t/** Distinct entry name within the store (e.g. the collection + params). */\n\tkey: string;\n\t/** IndexedDB database name. Defaults to `absolutejs-sync`. */\n\tdatabaseName?: string;\n\t/** Object-store name. Defaults to `collections`. */\n\tstoreName?: string;\n}): CollectionCache<T> => {\n\tlet handle: Promise<IDBDatabase> | undefined;\n\tconst database = () => {\n\t\thandle ??= openIndexedDb(databaseName, storeName);\n\t\treturn handle;\n\t};\n\tconst withStore = async <R>(\n\t\tmode: IDBTransactionMode,\n\t\trun: (store: IDBObjectStore) => IDBRequest\n\t): Promise<R | undefined> => {\n\t\tif (globalThis.indexedDB === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst db = await database();\n\t\treturn new Promise<R>((resolve, reject) => {\n\t\t\tconst request = run(\n\t\t\t\tdb.transaction(storeName, mode).objectStore(storeName)\n\t\t\t);\n\t\t\trequest.onsuccess = () => resolve(request.result as R);\n\t\t\trequest.onerror = () => reject(request.error);\n\t\t});\n\t};\n\n\treturn {\n\t\tload: () =>\n\t\t\twithStore<CollectionCacheSnapshot<T>>('readonly', (store) =>\n\t\t\t\tstore.get(key)\n\t\t\t),\n\t\tsave: async (snapshot) => {\n\t\t\tawait withStore('readwrite', (store) => store.put(snapshot, key));\n\t\t},\n\t\tclear: async () => {\n\t\t\tawait withStore('readwrite', (store) => store.delete(key));\n\t\t}\n\t};\n};\n\nexport type SyncCollectionOptions<T> = {\n\t/** WebSocket URL of the {@link syncSocket} endpoint (e.g. `ws://host/sync/ws`). */\n\turl: string;\n\t/** Registered collection name to subscribe to. */\n\tcollection: string;\n\t/** Query params forwarded to the server collection's hydrate/match/authorize. */\n\tparams?: unknown;\n\t/** Row identity, used to apply diffs and optimistic edits. Defaults to `row.id`. */\n\tkey?: (row: T) => RowKey;\n\t/** WebSocket implementation; defaults to the global one (pass for tests/SSR). */\n\twebSocketImpl?: typeof WebSocket;\n\t/**\n\t * Base reconnect delay (ms), doubled each attempt up to `maxReconnectMs`.\n\t * Set 0 to disable auto-reconnect. Defaults to 500.\n\t */\n\treconnectMs?: number;\n\t/** Maximum reconnect backoff (ms). Defaults to 10000. */\n\tmaxReconnectMs?: number;\n\t/**\n\t * Persist the pending-mutation queue so it survives a reload (offline) and\n\t * replays on connect. See {@link localStorageMutationStorage}.\n\t */\n\tstorage?: MutationStorage;\n\t/**\n\t * Persist confirmed rows locally for instant reads on reload and offline\n\t * (local-first). Hydrated before the socket connects; the engine then\n\t * resumes from the cached version (catch-up diff, or a fresh snapshot if the\n\t * server's changelog no longer covers it). See {@link\n\t * localStorageCollectionCache} / {@link indexedDbCollectionCache}.\n\t */\n\tcache?: CollectionCache<T>;\n\t/** Called with each server error message. */\n\tonError?: (error: unknown) => void;\n\t/**\n\t * Wire-format serializer (1.16.0). Defaults to `jsonSerializer`. MUST\n\t * match the server's `syncSocket` serializer.\n\t */\n\tserializer?: FrameSerializer;\n};\n\nexport type SyncCollection<T> = {\n\t/** Current state snapshot (stable reference until the next change). */\n\tget: () => SyncCollectionState<T>;\n\t/** Subscribe to state changes; returns an unsubscribe. */\n\tsubscribe: (\n\t\tlistener: (state: SyncCollectionState<T>) => void\n\t) => () => void;\n\t/**\n\t * Run a server mutation, optionally applying it optimistically. Resolves with\n\t * the server's result on ack, rejects (and rolls back) on reject. Pending\n\t * mutations are replayed when the socket reconnects, so they survive a drop.\n\t */\n\tmutate: <R = unknown>(options: MutateOptions<T>) => Promise<R>;\n\t/**\n\t * Force-close the underlying WebSocket without tearing down state. The\n\t * auto-reconnect loop fires after `reconnectMs`; the collection's\n\t * `appliedVersion` is preserved so the resumed subscribe carries `since`\n\t * and the engine replies with a catch-up diff (or a fresh snapshot if\n\t * the change log no longer covers the gap).\n\t *\n\t * Useful for simulating an offline blip in tests and benches that need\n\t * to measure resume cost specifically (vs cold-hydration on a fresh\n\t * collection). No-op if the collection has been `close()`d.\n\t */\n\tdisconnect: () => void;\n\t/** Unsubscribe on the server, close the socket, and stop reconnecting. */\n\tclose: () => void;\n};\n\n// One store subscribes to exactly one collection, so a fixed frame id suffices.\nconst SUBSCRIPTION_ID = 's';\n\ntype PendingMutation<T> = {\n\tmutationId: number;\n\tname: string;\n\targs: unknown;\n\toptimistic?: (draft: OptimisticDraft<T>) => void;\n\tresolve: (result: unknown) => void;\n\treject: (error: unknown) => void;\n};\n\n/**\n * A live collection backed by the WebSocket sync engine. Reads: connect,\n * subscribe, apply the server's snapshot then row-level diffs, re-sync on\n * reconnect. Writes: {@link SyncCollection.mutate} applies an optimistic overlay\n * immediately, sends the mutation, and reconciles on ack (drop the overlay — the\n * authoritative diff already arrived) or reject (roll back). Framework-agnostic\n * (`get` + `subscribe`).\n *\n * Mutations are replayed on reconnect, so make server mutations idempotent —\n * delivery is at-least-once if an ack is lost across a drop.\n */\nexport const createSyncCollection = <T>(\n\toptions: SyncCollectionOptions<T>\n): SyncCollection<T> => {\n\tconst key = options.key ?? ((row: T) => (row as { id: RowKey }).id);\n\tconst reconnectMs = options.reconnectMs ?? 500;\n\tconst maxReconnectMs = options.maxReconnectMs ?? 10_000;\n\tconst serializer: FrameSerializer = options.serializer ?? jsonSerializer;\n\tconst Impl = options.webSocketImpl ?? globalThis.WebSocket;\n\tif (!Impl) {\n\t\tthrow new Error(\n\t\t\t'createSyncCollection requires WebSocket. Run in a browser or pass webSocketImpl.'\n\t\t);\n\t}\n\n\t// Server-authoritative rows; `pending` is the optimistic overlay on top.\n\tconst confirmed = new Map<RowKey, T>();\n\tconst pending: PendingMutation<T>[] = [];\n\tlet mutationSeq = 0;\n\n\tlet state: SyncCollectionState<T> = {\n\t\tdata: [],\n\t\tstatus: 'connecting',\n\t\terror: undefined\n\t};\n\tconst listeners = new Set<(state: SyncCollectionState<T>) => void>();\n\tconst setState = (patch: Partial<SyncCollectionState<T>>) => {\n\t\tstate = { ...state, ...patch };\n\t\tfor (const listener of listeners) {\n\t\t\tlistener(state);\n\t\t}\n\t};\n\n\t/** Recompute visible rows = confirmed + pending optimistic effects. */\n\tconst recompute = (patch: Partial<SyncCollectionState<T>> = {}) => {\n\t\tconst working = new Map(confirmed);\n\t\tconst draft: OptimisticDraft<T> = {\n\t\t\tset: (row) => working.set(key(row), row),\n\t\t\tdelete: (rowKey) => working.delete(rowKey)\n\t\t};\n\t\tfor (const mutation of pending) {\n\t\t\tmutation.optimistic?.(draft);\n\t\t}\n\t\tsetState({ ...patch, data: [...working.values()] });\n\t};\n\n\tlet socket: WebSocket | undefined;\n\tlet connected = false;\n\tlet closed = false;\n\tlet attempt = 0;\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n\t// Highest change-feed version applied; sent as `since` to resume on reconnect.\n\tlet appliedVersion = 0;\n\t// 1.18.0: opaque cross-instance resume cursor. When the server surfaces\n\t// one on snapshot/diff/frame, capture it and round-trip it as `since`\n\t// in preference to `appliedVersion`. Resume across cluster shards.\n\tlet appliedCursor: string | undefined;\n\n\tconst persist = () => {\n\t\tvoid options.storage?.save(\n\t\t\tpending.map((mutation) => ({\n\t\t\t\tmutationId: mutation.mutationId,\n\t\t\t\tname: mutation.name,\n\t\t\t\targs: mutation.args\n\t\t\t}))\n\t\t);\n\t};\n\n\t// Coalesce a burst of confirmed changes (a frame of diffs) into one cache\n\t// write per tick. Persists only the server-authoritative set — never the\n\t// optimistic overlay (those live in the mutation queue instead).\n\tlet cacheScheduled = false;\n\tconst persistCache = () => {\n\t\tif (options.cache === undefined || cacheScheduled) {\n\t\t\treturn;\n\t\t}\n\t\tcacheScheduled = true;\n\t\tqueueMicrotask(() => {\n\t\t\tcacheScheduled = false;\n\t\t\tvoid options.cache?.save({\n\t\t\t\trows: [...confirmed.values()],\n\t\t\t\tversion: appliedVersion\n\t\t\t});\n\t\t});\n\t};\n\n\tconst settlePending = (mutationId: number) => {\n\t\tconst index = pending.findIndex(\n\t\t\t(mutation) => mutation.mutationId === mutationId\n\t\t);\n\t\tif (index === -1) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst [mutation] = pending.splice(index, 1);\n\t\tpersist();\n\t\treturn mutation;\n\t};\n\n\tconst applyFrame = (frame: ServerFrame<T>) => {\n\t\tif (frame.type === 'snapshot') {\n\t\t\tconfirmed.clear();\n\t\t\tfor (const row of frame.rows) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tif (frame.version !== undefined) {\n\t\t\t\tappliedVersion = frame.version;\n\t\t\t}\n\t\t\tif (frame.cursor !== undefined) {\n\t\t\t\tappliedCursor = frame.cursor;\n\t\t\t}\n\t\t\tpersistCache();\n\t\t\trecompute({ status: 'ready', error: undefined });\n\t\t} else if (frame.type === 'diff') {\n\t\t\tfor (const row of frame.removed) {\n\t\t\t\tconfirmed.delete(key(row));\n\t\t\t}\n\t\t\tfor (const row of frame.added) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tfor (const row of frame.changed) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tif (frame.cursor !== undefined) {\n\t\t\t\tappliedCursor = frame.cursor;\n\t\t\t}\n\t\t\tif (frame.version !== undefined) {\n\t\t\t\tappliedVersion = Math.max(appliedVersion, frame.version);\n\t\t\t}\n\t\t\tpersistCache();\n\t\t\t// A diff only arrives once subscribed — including the catch-up diff a\n\t\t\t// resume replies with — so receiving one means we're live.\n\t\t\trecompute({ status: 'ready', error: undefined });\n\t\t} else if (frame.type === 'error') {\n\t\t\tsetState({ error: frame.message });\n\t\t\toptions.onError?.(frame.message);\n\t\t} else if (frame.type === 'ack') {\n\t\t\t// The authoritative diff already arrived (ordered before the ack), so\n\t\t\t// dropping the overlay leaves the confirmed row in place — no flicker.\n\t\t\tconst mutation = settlePending(frame.mutationId);\n\t\t\tif (mutation !== undefined) {\n\t\t\t\trecompute();\n\t\t\t\tmutation.resolve(frame.result);\n\t\t\t}\n\t\t} else if (frame.type === 'reject') {\n\t\t\t// roll the optimistic overlay back.\n\t\t\tconst mutation = settlePending(frame.mutationId);\n\t\t\tif (mutation !== undefined) {\n\t\t\t\trecompute();\n\t\t\t\tmutation.reject(new Error(String(frame.message)));\n\t\t\t}\n\t\t}\n\t\t// A `frame` (multi-collection batch) never reaches a single-collection\n\t\t// store — that's the multiplexed createSyncClient's job — so ignore it.\n\t};\n\n\tconst wsSend = (payload: string | ArrayBufferLike | Uint8Array) => {\n\t\tsocket?.send(payload as string);\n\t};\n\n\tconst sendMutate = (mutation: PendingMutation<T>) => {\n\t\tif (connected) {\n\t\t\twsSend(\n\t\t\t\tserializer.encodeClient({\n\t\t\t\t\ttype: 'mutate',\n\t\t\t\t\tmutationId: mutation.mutationId,\n\t\t\t\t\tname: mutation.name,\n\t\t\t\t\targs: mutation.args\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\t};\n\n\tconst connect = () => {\n\t\tif (closed) {\n\t\t\treturn;\n\t\t}\n\t\tsetState({ status: 'connecting' });\n\t\tconst ws = new Impl(options.url);\n\t\tsocket = ws;\n\t\tws.onopen = () => {\n\t\t\tattempt = 0;\n\t\t\tconnected = true;\n\t\t\tws.send(\n\t\t\t\tserializer.encodeClient({\n\t\t\t\t\ttype: 'subscribe',\n\t\t\t\t\tid: SUBSCRIPTION_ID,\n\t\t\t\t\tcollection: options.collection,\n\t\t\t\t\tparams: options.params,\n\t\t\t\t\t// 1.18.0: prefer the opaque cross-instance cursor when we\n\t\t\t\t\t// have one; fall back to the local-version number for pre-1.17\n\t\t\t\t\t// servers (or before any cursor has arrived).\n\t\t\t\t\tsince:\n\t\t\t\t\t\tappliedCursor ??\n\t\t\t\t\t\t(appliedVersion > 0 ? appliedVersion : undefined)\n\t\t\t\t}) as string\n\t\t\t);\n\t\t\t// Replay anything still pending across the (re)connect.\n\t\t\tfor (const mutation of pending) {\n\t\t\t\tsendMutate(mutation);\n\t\t\t}\n\t\t};\n\t\tws.onmessage = (event) => {\n\t\t\ttry {\n\t\t\t\tconst decoded = serializer.decode(event.data);\n\t\t\t\tif (decoded !== null && typeof decoded === 'object') {\n\t\t\t\t\tapplyFrame(decoded as ServerFrame<T>);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// ignore non-JSON frames\n\t\t\t}\n\t\t};\n\t\tws.onclose = () => {\n\t\t\tconnected = false;\n\t\t\tif (closed || reconnectMs <= 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst delay = Math.min(reconnectMs * 2 ** attempt, maxReconnectMs);\n\t\t\tattempt += 1;\n\t\t\treconnectTimer = setTimeout(connect, delay);\n\t\t};\n\t};\n\n\t// Reload recovery: re-queue persisted unconfirmed mutations so they replay on\n\t// connect. They carry no optimistic effect or promise (the resumed/snapshot\n\t// state is authoritative); resending produces the diffs that bring them in.\n\tconst hydratePersisted = async () => {\n\t\tif (options.storage === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tconst records = await options.storage.load();\n\t\tfor (const record of records) {\n\t\t\tif (pending.some((m) => m.mutationId === record.mutationId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpending.push({\n\t\t\t\tmutationId: record.mutationId,\n\t\t\t\tname: record.name,\n\t\t\t\targs: record.args,\n\t\t\t\tresolve: () => {},\n\t\t\t\treject: () => {}\n\t\t\t});\n\t\t\tmutationSeq = Math.max(mutationSeq, record.mutationId);\n\t\t}\n\t\tif (connected) {\n\t\t\tfor (const mutation of pending) {\n\t\t\t\tsendMutate(mutation);\n\t\t\t}\n\t\t}\n\t};\n\n\t// Local-first: load cached rows + version before connecting, so reads are\n\t// instant on reload and available offline. The subscribe then resumes from\n\t// the cached version — a catch-up diff if the server's changelog still\n\t// covers it, else a fresh snapshot that replaces the stale cache.\n\tconst hydrateCache = async () => {\n\t\tif (options.cache === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tlet snapshot: CollectionCacheSnapshot<T> | undefined;\n\t\ttry {\n\t\t\tsnapshot = await options.cache.load();\n\t\t} catch {\n\t\t\treturn; // corrupt/unavailable cache: fall back to the server snapshot\n\t\t}\n\t\t// Don't clobber server data if a frame somehow already landed.\n\t\tif (snapshot === undefined || appliedVersion > 0) {\n\t\t\treturn;\n\t\t}\n\t\tfor (const row of snapshot.rows) {\n\t\t\tconfirmed.set(key(row), row);\n\t\t}\n\t\tappliedVersion = snapshot.version;\n\t\trecompute(); // show cached rows immediately (status stays 'connecting')\n\t};\n\n\tif (options.cache === undefined) {\n\t\t// No cache: preserve the original connect-then-hydrate ordering/timing.\n\t\tconnect();\n\t\tvoid hydratePersisted();\n\t} else {\n\t\t// Cache: hydrate reads + queued writes first, then connect so the\n\t\t// subscribe carries the cached resume version.\n\t\tvoid (async () => {\n\t\t\tawait hydrateCache();\n\t\t\tawait hydratePersisted();\n\t\t\tconnect();\n\t\t})();\n\t}\n\n\treturn {\n\t\tget: () => state,\n\t\tsubscribe: (listener) => {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t};\n\t\t},\n\t\tmutate: <R = unknown>(mutateOptions: MutateOptions<T>) =>\n\t\t\tnew Promise<R>((resolve, reject) => {\n\t\t\t\tconst mutation: PendingMutation<T> = {\n\t\t\t\t\tmutationId: (mutationSeq += 1),\n\t\t\t\t\tname: mutateOptions.name,\n\t\t\t\t\targs: mutateOptions.args,\n\t\t\t\t\toptimistic: mutateOptions.optimistic,\n\t\t\t\t\tresolve: (result) => resolve(result as R),\n\t\t\t\t\treject\n\t\t\t\t};\n\t\t\t\tpending.push(mutation);\n\t\t\t\tpersist();\n\t\t\t\trecompute(); // apply the optimistic overlay immediately\n\t\t\t\tsendMutate(mutation);\n\t\t\t}),\n\t\tdisconnect: () => {\n\t\t\t// Force-close the WS without tearing down state. The existing\n\t\t\t// `ws.onclose` handler schedules a reconnect via the auto-\n\t\t\t// reconnect loop (unless the collection has been `close()`d).\n\t\t\t// `appliedVersion` is preserved, so the resumed subscribe carries\n\t\t\t// `since` and the engine sends a catch-up diff (or snapshot if\n\t\t\t// the change log can't cover the gap).\n\t\t\tif (closed || socket === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsocket.close();\n\t\t\t} catch {\n\t\t\t\t// already closing/closed\n\t\t\t}\n\t\t},\n\t\tclose: () => {\n\t\t\tif (closed) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclosed = true;\n\t\t\tconnected = false;\n\t\t\tif (reconnectTimer !== undefined) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\twsSend(\n\t\t\t\t\tserializer.encodeClient({\n\t\t\t\t\t\ttype: 'unsubscribe',\n\t\t\t\t\t\tid: SUBSCRIPTION_ID\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\tsocket?.close();\n\t\t\t} catch {\n\t\t\t\t// socket already closing/closed\n\t\t\t}\n\t\t\t// Fail any still-pending mutations so their promises don't hang.\n\t\t\tfor (const mutation of pending.splice(0)) {\n\t\t\t\tmutation.reject(new Error('sync collection closed'));\n\t\t\t}\n\t\t\tpersist();\n\t\t\tsetState({ status: 'closed' });\n\t\t\tlisteners.clear();\n\t\t}\n\t};\n};\n",
|
|
7
7
|
"import type {\n\tCollectionConfig,\n\tDeleteMutationFn,\n\tInsertMutationFn,\n\tPendingMutation,\n\tUpdateMutationFn\n} from '@tanstack/db';\nimport {\n\tcreateSyncCollection,\n\ttype CollectionCache,\n\ttype MutationStorage,\n\ttype SyncCollection,\n\ttype SyncCollectionOptions\n} from '../../client/syncCollection';\nimport type { RowKey } from '../../engine/types';\n\nexport type TanStackRowKey = Extract<RowKey, string | number>;\n\nexport type TanStackMutationCall = {\n\tname: string;\n\targs?: unknown;\n};\n\nexport type TanStackMutationMapper<\n\tT extends object,\n\tTOperation extends 'insert' | 'update' | 'delete'\n> =\n\t| string\n\t| ((\n\t\t\tmutation: PendingMutation<T, TOperation>\n\t ) => TanStackMutationCall | undefined);\n\nexport type SyncTanStackMutations<T extends object> = {\n\tinsert?: TanStackMutationMapper<T, 'insert'>;\n\tupdate?: TanStackMutationMapper<T, 'update'>;\n\tdelete?: TanStackMutationMapper<T, 'delete'>;\n};\n\nexport type SyncTanStackCollectionOptions<\n\tT extends object,\n\tTKey extends TanStackRowKey = TanStackRowKey\n> = Omit<\n\tCollectionConfig<T, TKey>,\n\t'sync' | 'getKey' | 'onInsert' | 'onUpdate' | 'onDelete'\n> & {\n\t/** WebSocket URL of the Absolute Sync endpoint. */\n\turl: string;\n\t/** Registered Absolute Sync collection name. */\n\tcollection: string;\n\t/** Query params forwarded to the server collection hydrate/match/authorize hooks. */\n\tparams?: unknown;\n\t/** Row identity shared by TanStack DB and Absolute Sync. */\n\tgetKey: (row: T) => TKey;\n\twebSocketImpl?: typeof WebSocket;\n\treconnectMs?: number;\n\tmaxReconnectMs?: number;\n\tstorage?: MutationStorage;\n\tcache?: CollectionCache<T>;\n\tonError?: (error: unknown) => void;\n\t/**\n\t * Optional mapping from TanStack mutations to registered Absolute Sync\n\t * mutation names. TanStack already applies optimistic writes, so forwarded\n\t * sync mutations intentionally do not add another optimistic overlay.\n\t */\n\tmutations?: SyncTanStackMutations<T>;\n\t/** Optional prebuilt Absolute Sync collection, useful when sharing lifecycle externally. */\n\tsyncCollection?: SyncCollection<T>;\n};\n\nconst toMutationCall = <\n\tT extends object,\n\tTOperation extends 'insert' | 'update' | 'delete'\n>(\n\tmapper: TanStackMutationMapper<T, TOperation>,\n\tmutation: PendingMutation<T, TOperation>\n): TanStackMutationCall | undefined => {\n\tif (typeof mapper === 'function') {\n\t\treturn mapper(mutation);\n\t}\n\tif (mutation.type === 'insert') {\n\t\treturn {\n\t\t\tname: mapper,\n\t\t\targs: { row: mutation.modified, metadata: mutation.metadata }\n\t\t};\n\t}\n\tif (mutation.type === 'update') {\n\t\treturn {\n\t\t\tname: mapper,\n\t\t\targs: {\n\t\t\t\tkey: mutation.key,\n\t\t\t\trow: mutation.modified,\n\t\t\t\tchanges: mutation.changes,\n\t\t\t\tmetadata: mutation.metadata\n\t\t\t}\n\t\t};\n\t}\n\treturn {\n\t\tname: mapper,\n\t\targs: {\n\t\t\tkey: mutation.key,\n\t\t\trow: mutation.original,\n\t\t\tmetadata: mutation.metadata\n\t\t}\n\t};\n};\n\nconst createMutationHandler =\n\t<T extends object, TOperation extends 'insert' | 'update' | 'delete'>(\n\t\tsync: SyncCollection<T>,\n\t\tmapper: TanStackMutationMapper<T, TOperation> | undefined\n\t) =>\n\tasync ({\n\t\ttransaction\n\t}: {\n\t\ttransaction: {\n\t\t\tmutations: [\n\t\t\t\tPendingMutation<T, TOperation>,\n\t\t\t\t...PendingMutation<T, TOperation>[]\n\t\t\t];\n\t\t};\n\t}) => {\n\t\tif (mapper === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tawait Promise.all(\n\t\t\ttransaction.mutations.map((mutation) => {\n\t\t\t\tconst call = toMutationCall(mapper, mutation);\n\t\t\t\treturn call === undefined\n\t\t\t\t\t? Promise.resolve()\n\t\t\t\t\t: sync.mutate({ name: call.name, args: call.args });\n\t\t\t})\n\t\t);\n\t};\n\nconst createSyncConfig = <T extends object, TKey extends TanStackRowKey>(\n\tsync: SyncCollection<T>,\n\tgetKey: (row: T) => TKey\n): CollectionConfig<T, TKey>['sync'] => ({\n\trowUpdateMode: 'full',\n\tsync: ({ begin, write, commit, markReady }) => {\n\t\tlet previous = new Map<TKey, T>();\n\t\tlet markedReady = false;\n\n\t\tconst flush = () => {\n\t\t\tconst state = sync.get();\n\t\t\tconst next = new Map<TKey, T>();\n\t\t\tfor (const row of state.data) {\n\t\t\t\tnext.set(getKey(row), row);\n\t\t\t}\n\n\t\t\tbegin();\n\t\t\tfor (const [key, row] of next) {\n\t\t\t\tconst old = previous.get(key);\n\t\t\t\tif (old === undefined) {\n\t\t\t\t\twrite({ type: 'insert', value: row });\n\t\t\t\t} else if (!Object.is(old, row)) {\n\t\t\t\t\twrite({ type: 'update', value: row, previousValue: old });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of previous.keys()) {\n\t\t\t\tif (!next.has(key)) {\n\t\t\t\t\twrite({ type: 'delete', key });\n\t\t\t\t}\n\t\t\t}\n\t\t\tprevious = next;\n\t\t\tcommit();\n\n\t\t\tif (state.status === 'ready' && !markedReady) {\n\t\t\t\tmarkedReady = true;\n\t\t\t\tmarkReady();\n\t\t\t}\n\t\t};\n\n\t\tflush();\n\t\tconst unsubscribe = sync.subscribe(flush);\n\t\treturn () => {\n\t\t\tunsubscribe();\n\t\t\tsync.close();\n\t\t};\n\t}\n});\n\nexport const createSyncTanStackCollectionOptions = <\n\tT extends object,\n\tTKey extends TanStackRowKey = TanStackRowKey\n>(\n\toptions: SyncTanStackCollectionOptions<T, TKey>\n): CollectionConfig<T, TKey> => {\n\tconst {\n\t\turl,\n\t\tcollection,\n\t\tparams,\n\t\tgetKey,\n\t\twebSocketImpl,\n\t\treconnectMs,\n\t\tmaxReconnectMs,\n\t\tstorage,\n\t\tcache,\n\t\tonError,\n\t\tmutations,\n\t\tsyncCollection,\n\t\t...collectionOptions\n\t} = options;\n\n\tconst sync =\n\t\tsyncCollection ??\n\t\tcreateSyncCollection<T>({\n\t\t\turl,\n\t\t\tcollection,\n\t\t\tparams,\n\t\t\tkey: getKey as SyncCollectionOptions<T>['key'],\n\t\t\twebSocketImpl,\n\t\t\treconnectMs,\n\t\t\tmaxReconnectMs,\n\t\t\tstorage,\n\t\t\tcache,\n\t\t\tonError\n\t\t});\n\n\treturn {\n\t\t...collectionOptions,\n\t\tgetKey,\n\t\tsync: createSyncConfig(sync, getKey),\n\t\tonInsert: createMutationHandler(\n\t\t\tsync,\n\t\t\tmutations?.insert\n\t\t) as InsertMutationFn<T, TKey>,\n\t\tonUpdate: createMutationHandler(\n\t\t\tsync,\n\t\t\tmutations?.update\n\t\t) as UpdateMutationFn<T, TKey>,\n\t\tonDelete: createMutationHandler(\n\t\t\tsync,\n\t\t\tmutations?.delete\n\t\t) as DeleteMutationFn<T, TKey>\n\t};\n};\n\nexport { createSyncTanStackCollectionOptions as syncTanStackCollectionOptions };\n"
|
|
8
8
|
],
|
|
9
|
-
"mappings": "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",
|
|
9
|
+
"mappings": "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",
|
|
10
10
|
"debugId": "09055351043A07C764756E2164756E21",
|
|
11
11
|
"names": []
|
|
12
12
|
}
|