@doync/client 0.1.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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/dist/adapter.cjs +1 -0
  4. package/dist/adapter.d.cts +86 -0
  5. package/dist/adapter.d.cts.map +1 -0
  6. package/dist/adapter.d.ts +86 -0
  7. package/dist/adapter.d.ts.map +1 -0
  8. package/dist/adapter.js +2 -0
  9. package/dist/adapter.js.map +1 -0
  10. package/dist/client-C6jAdhbe.cjs +15 -0
  11. package/dist/client-CNyLMCw0.d.ts +812 -0
  12. package/dist/client-CNyLMCw0.d.ts.map +1 -0
  13. package/dist/client-ClV8ce6X.js +16 -0
  14. package/dist/client-ClV8ce6X.js.map +1 -0
  15. package/dist/client-DHXO0dbf.d.cts +812 -0
  16. package/dist/client-DHXO0dbf.d.cts.map +1 -0
  17. package/dist/index.cjs +0 -0
  18. package/dist/index.d.cts +2 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +0 -0
  21. package/dist/internal.cjs +1 -0
  22. package/dist/internal.d.cts +69 -0
  23. package/dist/internal.d.cts.map +1 -0
  24. package/dist/internal.d.ts +69 -0
  25. package/dist/internal.d.ts.map +1 -0
  26. package/dist/internal.js +2 -0
  27. package/dist/internal.js.map +1 -0
  28. package/package.json +79 -0
  29. package/src/adapter.ts +25 -0
  30. package/src/client-mutation-registry.ts +31 -0
  31. package/src/client.ts +100 -0
  32. package/src/engine.ts +3322 -0
  33. package/src/identity.ts +41 -0
  34. package/src/index.ts +36 -0
  35. package/src/internal.ts +37 -0
  36. package/src/migrate.ts +156 -0
  37. package/src/mutations.ts +96 -0
  38. package/src/port.ts +74 -0
  39. package/src/raw-read.ts +135 -0
  40. package/src/replica/db/0000_replica_engine_v0.sql +20 -0
  41. package/src/replica/db/0001_release_stamps.sql +6 -0
  42. package/src/replica/db/meta/0000_snapshot.json +129 -0
  43. package/src/replica/db/meta/0001_snapshot.json +167 -0
  44. package/src/replica/db/meta/_journal.json +20 -0
  45. package/src/replica/db/schema.ts +67 -0
  46. package/src/replica/index.ts +185 -0
  47. package/src/replica/meta.ts +26 -0
  48. package/src/replica/stamps.ts +102 -0
  49. package/src/replica/track.ts +190 -0
  50. package/src/socket-reconnect.ts +242 -0
  51. package/src/socket.ts +44 -0
  52. package/src/sql-raw.d.ts +4 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-DHXO0dbf.d.cts","names":["Input","Output","StandardTypedV1","Props","version","vendor","types","Types","input","output","InferInput","Schema","NonNullable","InferOutput","StandardSchemaV1","validate","value","Options","options","Result","Promise","SuccessResult","FailureResult","issues","libraryOptions","Record","ReadonlyArray","Issue","message","path","PropertyKey","PathSegment","key","StandardJSONSchemaV1","jsonSchema","Converter","Target","target"],"sources":["../../../node_modules/.pnpm/@standard-schema+spec@1.1.0/node_modules/@standard-schema/spec/dist/index.d.ts","../src/port.ts","../src/mutations.ts","../src/socket.ts","../src/replica/meta.ts","../src/replica/stamps.ts","../src/replica/index.ts","../src/engine.ts","../src/client.ts"],"x_google_ignoreList":[0],"mappings":"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"}
package/dist/index.cjs ADDED
File without changes
@@ -0,0 +1,2 @@
1
+ import { F as ClientMutation, I as ClientMutationHandler, L as ClientMutationRegistry, R as ClientMutationTx, _ as SchemaEventKind, b as ViewStatus, c as FalsyQuery, d as MutationResult, f as OnceView, g as SchemaEvent, h as QueryStatus, l as LogoutBehavior, m as PreloadOptions, o as ConnectionStatus, p as PreloadHandle, s as DoyncClient, u as MutationOptions, v as SubscribeOptions, y as View } from "./client-DHXO0dbf.cjs";
2
+ export type { ClientMutation, ClientMutationHandler, ClientMutationRegistry, ClientMutationTx, ConnectionStatus, DoyncClient, FalsyQuery, LogoutBehavior, MutationOptions, MutationResult, OnceView, PreloadHandle, PreloadOptions, QueryStatus, SchemaEvent, SchemaEventKind, SubscribeOptions, View, ViewStatus };
@@ -0,0 +1,2 @@
1
+ import { F as ClientMutation, I as ClientMutationHandler, L as ClientMutationRegistry, R as ClientMutationTx, _ as SchemaEventKind, b as ViewStatus, c as FalsyQuery, d as MutationResult, f as OnceView, g as SchemaEvent, h as QueryStatus, l as LogoutBehavior, m as PreloadOptions, o as ConnectionStatus, p as PreloadHandle, s as DoyncClient, u as MutationOptions, v as SubscribeOptions, y as View } from "./client-CNyLMCw0.js";
2
+ export type { ClientMutation, ClientMutationHandler, ClientMutationRegistry, ClientMutationTx, ConnectionStatus, DoyncClient, FalsyQuery, LogoutBehavior, MutationOptions, MutationResult, OnceView, PreloadHandle, PreloadOptions, QueryStatus, SchemaEvent, SchemaEventKind, SubscribeOptions, View, ViewStatus };
package/dist/index.js ADDED
File without changes
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./client-C6jAdhbe.cjs");let t=require("@doync/sqlite-parser");function n(e){return encodeURIComponent(e).replaceAll(`_`,`%5F`)}function r(e,t){let r=n(e);return t===null?`doync__${r}__anon.db`:`doync__${r}__${n(t)}.db`}function i(e){let n;try{n=(0,t.parse)(e)}catch(t){return console.warn(`extractWriteTable: failed to parse SQL statement`,{sql:e,err:t}),null}switch(n.kind){case`STMT_INSERT`:case`STMT_UPDATE`:case`STMT_DELETE`:return n.table??null;default:return null}}exports.ClientEngine=e.r,exports.__CONNECTED_CLOCK_META_KEY=e.i,exports.__LOGOUT_BEHAVIOR_META_KEY=e.a,exports.applyBundledMigrations=e.l,exports.asClientMutation=e.v,exports.bundledSchemaDdl=e.h,exports.classifyClientStatement=e.g,exports.createClientEngine=e.n,exports.createEngineTables=e.u,exports.dbFileForUserId=r,exports.dropConsumerTables=e.d,exports.extractWriteTable=i,exports.isFalsyQuery=e.o,exports.normalizeQuerySurface=e.s,exports.readMeta=e.p,exports.replayMigrations=e.f,exports.settledRejection=e.c,exports.splitClientStatements=e._,exports.toClientMutationRegistry=e.b,exports.validateMutationArgs=e.y,exports.writeMeta=e.m;
@@ -0,0 +1,69 @@
1
+ import { A as readMeta, B as validateMutationArgs, C as isFalsyQuery, D as createEngineTables, E as applyBundledMigrations, L as ClientMutationRegistry, O as dropConsumerTables, S as __LOGOUT_BEHAVIOR_META_KEY, T as settledRejection, U as extractWriteTable, a as ClientEngineConfig, i as ClientEngine, j as writeMeta, k as replayMigrations, r as createClientEngine, w as normalizeQuerySurface, x as __CONNECTED_CLOCK_META_KEY, z as asClientMutation } from "./client-DHXO0dbf.cjs";
2
+ import { DoyncSchema, MutationTree } from "@doync/core";
3
+
4
+ //#region src/client-mutation-registry.d.ts
5
+ /**
6
+ * Flat `dottedName → {args, handler}` registry the client engine resolves
7
+ * optimistic bodies against (ADR-0019). Feed the SHARED tree so no `.server.ts`
8
+ * override enters the client bundle. Handler may return a promise (exclusive
9
+ * chain awaits); engine validates args at `mutate()` via carried schema.
10
+ */
11
+ declare function toClientMutationRegistry(tree: MutationTree): ClientMutationRegistry;
12
+ //#endregion
13
+ //#region src/identity.d.ts
14
+ /**
15
+ * Durable replica file for one `(Database name, asserted userId)` pair:
16
+ * `doync__<name>__anon.db` when `userId` is `null`, else
17
+ * `doync__<name>__<userId>.db`. Separators are `__` so hyphens (common in UUIDs
18
+ * and ids) stay unescaped and filenames stay short enough for OPFS. Each half
19
+ * is percent-encoded; `_` is additionally escaped so halves cannot forge a
20
+ * separator.
21
+ */
22
+ declare function dbFileForUserId(name: string, userId: string | null): string;
23
+ //#endregion
24
+ //#region src/migrate.d.ts
25
+ /**
26
+ * Parser-backed SQL classification for client DDL-only bundled-track replay
27
+ * (ADR-0020 / ADR-0006). Same AST as the server conversation, narrower API: 1.
28
+ * split migrations keeping trigger BEGIN…END whole; 2. ddl vs trigger (skip —
29
+ * apply state, never enforce; ADR-0009/0020) vs backfill DML (skip — data via
30
+ * feed; ADR-0006). Fails closed on unparseable input.
31
+ */
32
+ /** How the client treats one already-split migration statement. */
33
+ type ClientStatementKind = "ddl" | "trigger" | "dml" | "pragma" | "other";
34
+ /**
35
+ * Split a migration's SQL into its top-level statements. The parser understands
36
+ * statement structure, so a `CREATE TRIGGER … BEGIN … END` body's internal
37
+ * semicolons stay inside one statement — a migration may mix a trigger with
38
+ * surrounding DDL without mis-splitting. Each statement is returned as its
39
+ * canonical unparse (semantically identical to the source); statements that are
40
+ * empty (whitespace/comments only) yield nothing.
41
+ *
42
+ * Fails closed: an unparseable script throws a clear error rather than applying
43
+ * a partial or mis-read migration. Trusted input only in the sense that the
44
+ * consumer authored the migrations — the parse itself validates their syntax.
45
+ */
46
+ declare function splitClientStatements(sql: string): string[];
47
+ /**
48
+ * Classify ONE already-split statement by its parsed kind. `CREATE TRIGGER` and
49
+ * `DROP TRIGGER` are `'trigger'` (the client neither creates nor drops triggers
50
+ * — it runs none, ADR-0009); every other shape-changing DDL is `'ddl'`
51
+ * (replayed); the row read/write verbs are `'dml'` (backfill, never replayed on
52
+ * the client); `PRAGMA` and anything unrecognized/unparseable land in their own
53
+ * buckets.
54
+ */
55
+ declare function classifyClientStatement(statement: string): ClientStatementKind;
56
+ /**
57
+ * The non-trigger DDL statements of the bundled migrations in the half-open
58
+ * range `(fromVersion, toVersion]` (0-based migration indices `fromVersion …
59
+ * toVersion − 1`), in order — the replayable shape for the client's DDL-only
60
+ * bundled-track replay (ADR-0020). Trigger DDL is skipped (ADR-0009) and
61
+ * backfill DML never runs on the client (ADR-0006). Passing `fromVersion = 0`
62
+ * yields the full track up to `toVersion` (a fresh replica); a higher
63
+ * `fromVersion` yields just the newly-appended migrations (a mid-session
64
+ * catch-up).
65
+ */
66
+ declare function bundledSchemaDdl(schema: DoyncSchema, fromVersion: number, toVersion: number): string[];
67
+ //#endregion
68
+ export { ClientEngine, type ClientEngineConfig, type ClientStatementKind, __CONNECTED_CLOCK_META_KEY, __LOGOUT_BEHAVIOR_META_KEY, applyBundledMigrations, asClientMutation, bundledSchemaDdl, classifyClientStatement, createClientEngine, createEngineTables, dbFileForUserId, dropConsumerTables, extractWriteTable, isFalsyQuery, normalizeQuerySurface, readMeta, replayMigrations, settledRejection, splitClientStatements, toClientMutationRegistry, validateMutationArgs, writeMeta };
69
+ //# sourceMappingURL=internal.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.d.cts","names":[],"sources":["../src/client-mutation-registry.ts","../src/identity.ts","../src/migrate.ts"],"mappings":";;;;;;;;AAqBG;;iBAFa,wBAAA,CACd,IAAA,EAAM,YAAA,GACL,sBAAA;;;;;;;AAFH;;;;iBCgBgB,eAAA,CAAgB,IAAA,UAAc,MAAA;;;;;;ADhB9C;;;;;KEAY,mBAAA;;;AFET;;;;ACcH;;;;AAA8C;;iBCgE9B,qBAAA,CAAsB,GAAA;;AAhFtC;;;;AAAY;AAgFZ;;iBAkBgB,uBAAA,CACd,SAAA,WACC,mBAAA;;AApBmC;AAkBtC;;;;AAEG;AAeH;;;iBAAgB,gBAAA,CACd,MAAA,EAAQ,WAAA,EACR,WAAA,UACA,SAAA"}
@@ -0,0 +1,69 @@
1
+ import { A as readMeta, B as validateMutationArgs, C as isFalsyQuery, D as createEngineTables, E as applyBundledMigrations, L as ClientMutationRegistry, O as dropConsumerTables, S as __LOGOUT_BEHAVIOR_META_KEY, T as settledRejection, U as extractWriteTable, a as ClientEngineConfig, i as ClientEngine, j as writeMeta, k as replayMigrations, r as createClientEngine, w as normalizeQuerySurface, x as __CONNECTED_CLOCK_META_KEY, z as asClientMutation } from "./client-CNyLMCw0.js";
2
+ import { DoyncSchema, MutationTree } from "@doync/core";
3
+
4
+ //#region src/client-mutation-registry.d.ts
5
+ /**
6
+ * Flat `dottedName → {args, handler}` registry the client engine resolves
7
+ * optimistic bodies against (ADR-0019). Feed the SHARED tree so no `.server.ts`
8
+ * override enters the client bundle. Handler may return a promise (exclusive
9
+ * chain awaits); engine validates args at `mutate()` via carried schema.
10
+ */
11
+ declare function toClientMutationRegistry(tree: MutationTree): ClientMutationRegistry;
12
+ //#endregion
13
+ //#region src/identity.d.ts
14
+ /**
15
+ * Durable replica file for one `(Database name, asserted userId)` pair:
16
+ * `doync__<name>__anon.db` when `userId` is `null`, else
17
+ * `doync__<name>__<userId>.db`. Separators are `__` so hyphens (common in UUIDs
18
+ * and ids) stay unescaped and filenames stay short enough for OPFS. Each half
19
+ * is percent-encoded; `_` is additionally escaped so halves cannot forge a
20
+ * separator.
21
+ */
22
+ declare function dbFileForUserId(name: string, userId: string | null): string;
23
+ //#endregion
24
+ //#region src/migrate.d.ts
25
+ /**
26
+ * Parser-backed SQL classification for client DDL-only bundled-track replay
27
+ * (ADR-0020 / ADR-0006). Same AST as the server conversation, narrower API: 1.
28
+ * split migrations keeping trigger BEGIN…END whole; 2. ddl vs trigger (skip —
29
+ * apply state, never enforce; ADR-0009/0020) vs backfill DML (skip — data via
30
+ * feed; ADR-0006). Fails closed on unparseable input.
31
+ */
32
+ /** How the client treats one already-split migration statement. */
33
+ type ClientStatementKind = "ddl" | "trigger" | "dml" | "pragma" | "other";
34
+ /**
35
+ * Split a migration's SQL into its top-level statements. The parser understands
36
+ * statement structure, so a `CREATE TRIGGER … BEGIN … END` body's internal
37
+ * semicolons stay inside one statement — a migration may mix a trigger with
38
+ * surrounding DDL without mis-splitting. Each statement is returned as its
39
+ * canonical unparse (semantically identical to the source); statements that are
40
+ * empty (whitespace/comments only) yield nothing.
41
+ *
42
+ * Fails closed: an unparseable script throws a clear error rather than applying
43
+ * a partial or mis-read migration. Trusted input only in the sense that the
44
+ * consumer authored the migrations — the parse itself validates their syntax.
45
+ */
46
+ declare function splitClientStatements(sql: string): string[];
47
+ /**
48
+ * Classify ONE already-split statement by its parsed kind. `CREATE TRIGGER` and
49
+ * `DROP TRIGGER` are `'trigger'` (the client neither creates nor drops triggers
50
+ * — it runs none, ADR-0009); every other shape-changing DDL is `'ddl'`
51
+ * (replayed); the row read/write verbs are `'dml'` (backfill, never replayed on
52
+ * the client); `PRAGMA` and anything unrecognized/unparseable land in their own
53
+ * buckets.
54
+ */
55
+ declare function classifyClientStatement(statement: string): ClientStatementKind;
56
+ /**
57
+ * The non-trigger DDL statements of the bundled migrations in the half-open
58
+ * range `(fromVersion, toVersion]` (0-based migration indices `fromVersion …
59
+ * toVersion − 1`), in order — the replayable shape for the client's DDL-only
60
+ * bundled-track replay (ADR-0020). Trigger DDL is skipped (ADR-0009) and
61
+ * backfill DML never runs on the client (ADR-0006). Passing `fromVersion = 0`
62
+ * yields the full track up to `toVersion` (a fresh replica); a higher
63
+ * `fromVersion` yields just the newly-appended migrations (a mid-session
64
+ * catch-up).
65
+ */
66
+ declare function bundledSchemaDdl(schema: DoyncSchema, fromVersion: number, toVersion: number): string[];
67
+ //#endregion
68
+ export { ClientEngine, type ClientEngineConfig, type ClientStatementKind, __CONNECTED_CLOCK_META_KEY, __LOGOUT_BEHAVIOR_META_KEY, applyBundledMigrations, asClientMutation, bundledSchemaDdl, classifyClientStatement, createClientEngine, createEngineTables, dbFileForUserId, dropConsumerTables, extractWriteTable, isFalsyQuery, normalizeQuerySurface, readMeta, replayMigrations, settledRejection, splitClientStatements, toClientMutationRegistry, validateMutationArgs, writeMeta };
69
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.d.ts","names":[],"sources":["../src/client-mutation-registry.ts","../src/identity.ts","../src/migrate.ts"],"mappings":";;;;;;;;AAqBG;;iBAFa,wBAAA,CACd,IAAA,EAAM,YAAA,GACL,sBAAA;;;;;;;AAFH;;;;iBCgBgB,eAAA,CAAgB,IAAA,UAAc,MAAA;;;;;;ADhB9C;;;;;KEAY,mBAAA;;;AFET;;;;ACcH;;;;AAA8C;;iBCgE9B,qBAAA,CAAsB,GAAA;;AAhFtC;;;;AAAY;AAgFZ;;iBAkBgB,uBAAA,CACd,SAAA,WACC,mBAAA;;AApBmC;AAkBtC;;;;AAEG;AAeH;;;iBAAgB,gBAAA,CACd,MAAA,EAAQ,WAAA,EACR,WAAA,UACA,SAAA"}
@@ -0,0 +1,2 @@
1
+ import{_ as e,a as t,b as n,c as r,d as i,f as a,g as o,h as s,i as c,l,m as u,n as d,o as f,p,r as m,s as h,u as g,v as _,y as v}from"./client-ClV8ce6X.js";import{parse as y}from"@doync/sqlite-parser";function b(e){return encodeURIComponent(e).replaceAll(`_`,`%5F`)}function x(e,t){let n=b(e);return t===null?`doync__${n}__anon.db`:`doync__${n}__${b(t)}.db`}function S(e){let t;try{t=y(e)}catch(t){return console.warn(`extractWriteTable: failed to parse SQL statement`,{sql:e,err:t}),null}switch(t.kind){case`STMT_INSERT`:case`STMT_UPDATE`:case`STMT_DELETE`:return t.table??null;default:return null}}export{m as ClientEngine,c as __CONNECTED_CLOCK_META_KEY,t as __LOGOUT_BEHAVIOR_META_KEY,l as applyBundledMigrations,_ as asClientMutation,s as bundledSchemaDdl,o as classifyClientStatement,d as createClientEngine,g as createEngineTables,x as dbFileForUserId,i as dropConsumerTables,S as extractWriteTable,f as isFalsyQuery,h as normalizeQuerySurface,p as readMeta,a as replayMigrations,r as settledRejection,e as splitClientStatements,n as toClientMutationRegistry,v as validateMutationArgs,u as writeMeta};
2
+ //# sourceMappingURL=internal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.js","names":[],"sources":["../src/identity.ts","../src/port.ts"],"sourcesContent":["/**\n * Per-(Database name, identity) replica filename (CONTEXT.md Database name;\n * ADR-0019 addendum / closeio/doync#187 / #321) — a PURE helper shared by every\n * platform that owns a durable replica file (web OPFS, mobile op-sqlite, …).\n *\n * The file key is `(name, userId)`: two Database names in one app never collide\n * on the same store, and each identity (`doync__<name>__<userId|anon>.db`) owns\n * its own replica, clientId, cookie, and pending queue so a shared device never\n * leaks one user's rows into another's session. The id is percent-encoded so an\n * exotic identity can never escape the filename (a `/` would otherwise mean a\n * subpath); a numeric/alphanumeric id (the common case, e.g. a GitHub id)\n * encodes to itself, keeping the file readable. Pre-release: no migration of\n * pre-#187 / pre-#321 filenames — wipe/redeploy owns the cutover.\n */\n\n/**\n * Encode one half of the replica filename so it cannot contain the format's\n * `__` separator and cannot form a path segment (`/`, etc.).\n *\n * `encodeURIComponent` leaves `_` intact; escaping `_` → `%5F` keeps `(name:\n * 'a__b', user: 'c')` from colliding with `(name: 'a', user: 'b__c')`.\n * UUID-shaped names (hyphens only) encode to themselves.\n */\nfunction encodeHalf(value: string): string {\n return encodeURIComponent(value).replaceAll('_', '%5F')\n}\n\n/**\n * Durable replica file for one `(Database name, asserted userId)` pair:\n * `doync__<name>__anon.db` when `userId` is `null`, else\n * `doync__<name>__<userId>.db`. Separators are `__` so hyphens (common in UUIDs\n * and ids) stay unescaped and filenames stay short enough for OPFS. Each half\n * is percent-encoded; `_` is additionally escaped so halves cannot forge a\n * separator.\n */\nexport function dbFileForUserId(name: string, userId: string | null): string {\n const encodedName = encodeHalf(name)\n return userId === null\n ? `doync__${encodedName}__anon.db`\n : `doync__${encodedName}__${encodeHalf(userId)}.db`\n}\n","import type { SqlValue } from '@doync/core'\nimport type { Statement } from '@doync/sqlite-parser'\n\nimport { parse } from '@doync/sqlite-parser'\n\n/** One local-replica row: column name → {@link SqlValue}. */\nexport interface LocalRow {\n [column: string]: SqlValue\n}\n\n// ADR-0019 (synchronous local-DB port; platform adapters supply the impl).\n/**\n * Synchronous local SQLite port the client engine drives. Platform adapters\n * (wa-sqlite, node:sqlite, op-sqlite) implement this; app code does not.\n * Transactions and savepoints are ordinary SQL via {@link LocalDb.exec}.\n */\nexport interface LocalDb {\n /**\n * Run one parameterized statement; return its rows (empty for writes/DDL/\n * transaction control).\n */\n exec<T extends LocalRow = LocalRow>(sql: string, ...params: SqlValue[]): T[]\n\n /**\n * Run a multi-statement DDL script (no parameters). Used to replay bundled\n * migrations when creating a fresh replica. Trusted input only.\n */\n execBatch(script: string): void\n\n /**\n * Consumer tables written since the last call, then clear the set. Used so\n * only affected subscriptions re-project after a mutation or rebase.\n */\n drainWrittenTables(): Set<string>\n}\n\n/**\n * Extract the target table of one WRITE statement from its SQL text — the\n * adapter-side stand-in where no synchronous native change feed exists\n * (ADR-0019): node:sqlite has no update hook, and op-sqlite's hook delivers\n * callbacks via `invokeAsync` — a later event-loop turn, unusable for the\n * engine's drain-after-apply contract (closeio/doync#202 device pass).\n *\n * A real parse over `@doync/sqlite-parser`, not a keyword recognizer: the AST\n * attaches a `WITH …` CTE prefix to the DML node itself, so CTE-topped writes\n * (`WITH src AS (…) INSERT INTO t …`) extract their target with no string\n * games. Returns the unquoted table name for INSERT / REPLACE / UPDATE /\n * DELETE, or `null` for a non-write (SELECT, SAVEPOINT, PRAGMA, DDL) or\n * unparseable input.\n *\n * TRIGGER cascades remain invisible to statement text — and stay out of scope\n * by design: client replicas carry no triggers (the bundled-track replay strips\n * CREATE TRIGGER; ADR-0009 — clients apply state, never enforce).\n */\nexport function extractWriteTable(sql: string): string | null {\n let statement: Statement\n try {\n statement = parse(sql)\n } catch (err) {\n console.warn('extractWriteTable: failed to parse SQL statement', {\n sql,\n err,\n })\n return null\n }\n switch (statement.kind) {\n case 'STMT_INSERT':\n case 'STMT_UPDATE':\n case 'STMT_DELETE':\n return statement.table ?? null\n default:\n return null\n }\n}\n"],"mappings":"0MAuBA,SAAS,EAAW,EAAuB,CACzC,OAAO,mBAAmB,CAAK,CAAC,CAAC,WAAW,IAAK,KAAK,CACxD,CAUA,SAAgB,EAAgB,EAAc,EAA+B,CAC3E,IAAM,EAAc,EAAW,CAAI,EACnC,OAAO,IAAW,KACd,UAAU,EAAY,WACtB,UAAU,EAAY,IAAI,EAAW,CAAM,EAAE,IACnD,CCcA,SAAgB,EAAkB,EAA4B,CAC5D,IAAI,EACJ,GAAI,CACF,EAAY,EAAM,CAAG,CACvB,OAAS,EAAK,CAKZ,OAJA,QAAQ,KAAK,mDAAoD,CAC/D,MACA,KACF,CAAC,EACM,IACT,CACA,OAAQ,EAAU,KAAlB,CACE,IAAK,cACL,IAAK,cACL,IAAK,cACH,OAAO,EAAU,OAAS,KAC5B,QACE,OAAO,IACX,CACF"}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@doync/client",
3
+ "version": "0.1.0",
4
+ "description": "doync client engine: optimistic Layer-1 savepoint/rebase over a synchronous local-DB port",
5
+ "keywords": [
6
+ "client",
7
+ "doync",
8
+ "local-first",
9
+ "sqlite",
10
+ "sync-engine"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "Vitor Buzinaro",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/closeio/doync.git",
17
+ "directory": "packages/client"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "src"
22
+ ],
23
+ "type": "module",
24
+ "main": "./dist/index.cjs",
25
+ "module": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "import": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "require": {
34
+ "types": "./dist/index.d.cts",
35
+ "default": "./dist/index.cjs"
36
+ }
37
+ },
38
+ "./adapter": {
39
+ "import": {
40
+ "types": "./dist/adapter.d.ts",
41
+ "default": "./dist/adapter.js"
42
+ },
43
+ "require": {
44
+ "types": "./dist/adapter.d.cts",
45
+ "default": "./dist/adapter.cjs"
46
+ }
47
+ },
48
+ "./internal": {
49
+ "import": {
50
+ "types": "./dist/internal.d.ts",
51
+ "default": "./dist/internal.js"
52
+ },
53
+ "require": {
54
+ "types": "./dist/internal.d.cts",
55
+ "default": "./dist/internal.cjs"
56
+ }
57
+ }
58
+ },
59
+ "dependencies": {
60
+ "@doync/core": "0.1.0",
61
+ "@doync/sqlite-parser": "0.3053001.0"
62
+ },
63
+ "devDependencies": {
64
+ "@standard-schema/spec": "^1.1.0",
65
+ "@types/node": "^24.13.2",
66
+ "drizzle-kit": "^0.31.10",
67
+ "drizzle-orm": "^0.45.2",
68
+ "tsdown": "^0.22.3",
69
+ "typescript": "^7.0.2",
70
+ "unplugin-raw": "^0.7.0",
71
+ "vitest": "4.1.9",
72
+ "@doync/drizzle": "0.1.0"
73
+ },
74
+ "scripts": {
75
+ "build": "rm -rf dist && tsdown",
76
+ "test": "vitest run",
77
+ "typecheck": "tsc --noEmit"
78
+ }
79
+ }
package/src/adapter.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `@doync/client/adapter` (ADR-0033): the adapter-author surface — host-seam
3
+ * ports (`LocalDb`, `SyncSocket`), the reconnect coordinator, and
4
+ * `createClient` / `CreateClientOptions`. Same lockstep semver as the main
5
+ * entry; the distinction is audience (third-party adapter authors), not
6
+ * stability. App-facing call-surface types (`DoyncClient`, views, mutation
7
+ * types, `LogoutBehavior`) live on `@doync/client`. Engine construction
8
+ * plumbing (`createClientEngine`, replica DDL, mutation dispatch helpers) lives
9
+ * under `/internal`.
10
+ *
11
+ * Our own platform packages (`@doync/web`, `@doync/mobile`) use
12
+ * `createClientEngine` from `/internal` rather than `createClient` — the latter
13
+ * is the only real call site audience for a third-party adapter.
14
+ */
15
+
16
+ export { createClient, type CreateClientOptions } from './client'
17
+ export type { LocalDb, LocalRow } from './port'
18
+ export type { SeamStatus, SyncSocket, SyncSocketHandlers } from './socket'
19
+ export {
20
+ createReconnectingSocket,
21
+ type ReconnectSocket,
22
+ type ReconnectingSocket,
23
+ type ReconnectingSocketConfig,
24
+ type SocketCloseInfo,
25
+ } from './socket-reconnect'
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Flatten a shared MutationTree to the engine's name-keyed client mutation
3
+ * registry (ADR-0019/0021, #167). Internal helper for {@link createClient} and
4
+ * `@doync/web` `createWorker` — consumers pass a MutationTree; the lib
5
+ * flattens.
6
+ */
7
+
8
+ import type { MutationTree } from '@doync/core'
9
+
10
+ import { flattenMutations } from '@doync/core/internal'
11
+
12
+ import type { ClientMutation, ClientMutationRegistry } from './mutations'
13
+
14
+ /**
15
+ * Flat `dottedName → {args, handler}` registry the client engine resolves
16
+ * optimistic bodies against (ADR-0019). Feed the SHARED tree so no `.server.ts`
17
+ * override enters the client bundle. Handler may return a promise (exclusive
18
+ * chain awaits); engine validates args at `mutate()` via carried schema.
19
+ */
20
+ export function toClientMutationRegistry(
21
+ tree: MutationTree,
22
+ ): ClientMutationRegistry {
23
+ const out: Record<string, ClientMutation> = {}
24
+ for (const [name, def] of Object.entries(flattenMutations(tree))) {
25
+ out[name] = {
26
+ args: def.args,
27
+ handler: (args, ctx, sql) => def.body(args, ctx, sql),
28
+ }
29
+ }
30
+ return out
31
+ }
package/src/client.ts ADDED
@@ -0,0 +1,100 @@
1
+ import type { DoyncSchema, MutationTree } from '@doync/core'
2
+
3
+ import type { DoyncClient, LogoutBehavior, SchemaEvent } from './engine'
4
+ import type { LocalDb } from './port'
5
+ import type { SyncSocket } from './socket'
6
+
7
+ import { toClientMutationRegistry } from './client-mutation-registry'
8
+ import { ClientEngine, __LOGOUT_BEHAVIOR_META_KEY } from './engine'
9
+ import { createEngineTables, writeMeta } from './replica'
10
+
11
+ // The durable logout-policy seam (ADR-0022) is owned by the engine (the layer
12
+ // that owns `__doync_meta` and the runtime {@link ClientEngine.setLogoutBehavior}).
13
+ // Only LogoutBehavior is public; the meta key stays on `./internal`.
14
+ export type { LogoutBehavior } from './engine'
15
+
16
+ // Identity is the asserted triple `{ token, ctx, userId }` — never derived
17
+ // from a bearer token (ADR-0018 asserted-identity).
18
+ /**
19
+ * Options for {@link createClient}. Platform adapters (`@doync/web`,
20
+ * `@doync/mobile`) fill these; app code rarely calls `createClient` directly.
21
+ */
22
+ export interface CreateClientOptions<TAuthData = unknown> {
23
+ /** Synchronous local SQLite port (wa-sqlite / node:sqlite / op-sqlite). */
24
+ readonly db: LocalDb
25
+ /** Synced schema shared with Origin and Mirror. */
26
+ readonly schema: DoyncSchema
27
+ /**
28
+ * Shared mutation tree from `defineMutations(shared)` — no server overrides
29
+ * in the client bundle.
30
+ */
31
+ readonly mutations: MutationTree
32
+ /** Transport to the Mirror. */
33
+ readonly socket: SyncSocket
34
+ /**
35
+ * Bearer credential for the handshake. Does not drive `userId` or `ctx`. Omit
36
+ * for cookie-session auth.
37
+ */
38
+ readonly token?: string
39
+ /**
40
+ * Projected auth context for optimistic query/mutation resolution (mirror
41
+ * your server `toContext`). Defaults to `{}`.
42
+ */
43
+ readonly ctx?: TAuthData
44
+ /**
45
+ * Authenticated identity, or `null`/omit for anonymous. Never derived from
46
+ * `token`.
47
+ */
48
+ readonly userId?: string | null
49
+ /** Pin a durable client id across restarts. */
50
+ readonly clientId?: string
51
+ /** Observe schema/recovery transitions. */
52
+ readonly onSchemaEvent?: (event: SchemaEvent) => void
53
+ /**
54
+ * Initial {@link LogoutBehavior}, written durably when provided. Omit to keep
55
+ * a previously stored choice (a restart will not revert `forget` to `keep`).
56
+ * Change later via {@link DoyncClient.setLogoutBehavior}.
57
+ */
58
+ readonly logoutBehavior?: LogoutBehavior
59
+ }
60
+
61
+ /**
62
+ * Construct a {@link DoyncClient} from local DB, schema, shared mutations, and a
63
+ * Mirror socket. App code normally goes through `@doync/web` / `@doync/mobile`
64
+ * instead.
65
+ */
66
+ export function createClient<TAuthData = unknown>(
67
+ options: CreateClientOptions<TAuthData>,
68
+ ): DoyncClient {
69
+ return createClientEngine(options)
70
+ }
71
+
72
+ /**
73
+ * Concrete-engine factory (ADR-0033 / closeio/doync#241). Same construction as
74
+ * {@link createClient}, but typed as {@link ClientEngine} so platform adapters
75
+ * (web DB-worker, mobile wrapper) can reach engine-only fields like `clientId`
76
+ * without an unchecked downcast. Exported on `@doync/client/internal` only.
77
+ */
78
+ export function createClientEngine<TAuthData = unknown>(
79
+ options: CreateClientOptions<TAuthData>,
80
+ ): ClientEngine {
81
+ // Logout policy must land before boot (ADR-0022 / #135): construction opens
82
+ // the optimistic overlay (ADR-0019), so a later write on this connection would
83
+ // join the savepoint and roll back. Pre-boot is autocommit.
84
+ if (options.logoutBehavior !== undefined) {
85
+ createEngineTables(options.db)
86
+ writeMeta(options.db, __LOGOUT_BEHAVIOR_META_KEY, options.logoutBehavior)
87
+ }
88
+ return new ClientEngine({
89
+ db: options.db,
90
+ schema: options.schema,
91
+ mutations: toClientMutationRegistry(options.mutations),
92
+ socket: options.socket,
93
+ // Asserted identity (ADR-0018 / #167): never derived.
94
+ ctx: (options.ctx as Record<string, unknown> | undefined) ?? {},
95
+ token: options.token,
96
+ userId: options.userId ?? null,
97
+ clientId: options.clientId,
98
+ onSchemaEvent: options.onSchemaEvent,
99
+ })
100
+ }