@g9tm/syncdata 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/LICENSE.md +21 -0
  2. package/PARITY.md +71 -0
  3. package/README.md +71 -0
  4. package/THIRD-PARTY-NOTICES.md +31 -0
  5. package/dist/abstractions/digest.d.ts +27 -0
  6. package/dist/abstractions/digest.js +78 -0
  7. package/dist/abstractions/digest.js.map +1 -0
  8. package/dist/abstractions/enums.d.ts +42 -0
  9. package/dist/abstractions/enums.js +60 -0
  10. package/dist/abstractions/enums.js.map +1 -0
  11. package/dist/abstractions/errors.d.ts +72 -0
  12. package/dist/abstractions/errors.js +90 -0
  13. package/dist/abstractions/errors.js.map +1 -0
  14. package/dist/abstractions/guid.d.ts +24 -0
  15. package/dist/abstractions/guid.js +93 -0
  16. package/dist/abstractions/guid.js.map +1 -0
  17. package/dist/abstractions/key.d.ts +16 -0
  18. package/dist/abstractions/key.js +47 -0
  19. package/dist/abstractions/key.js.map +1 -0
  20. package/dist/abstractions/long.d.ts +103 -0
  21. package/dist/abstractions/long.js +351 -0
  22. package/dist/abstractions/long.js.map +1 -0
  23. package/dist/abstractions/parameters.d.ts +49 -0
  24. package/dist/abstractions/parameters.js +153 -0
  25. package/dist/abstractions/parameters.js.map +1 -0
  26. package/dist/brotli-js/decode.d.ts +8 -0
  27. package/dist/brotli-js/decode.js +2004 -0
  28. package/dist/brotli-js/decode.js.map +1 -0
  29. package/dist/brotli-js/index.d.ts +5 -0
  30. package/dist/brotli-js/index.js +19 -0
  31. package/dist/brotli-js/index.js.map +1 -0
  32. package/dist/client/engine.d.ts +110 -0
  33. package/dist/client/engine.js +1158 -0
  34. package/dist/client/engine.js.map +1 -0
  35. package/dist/client/model.d.ts +61 -0
  36. package/dist/client/model.js +154 -0
  37. package/dist/client/model.js.map +1 -0
  38. package/dist/client/progress.d.ts +101 -0
  39. package/dist/client/progress.js +251 -0
  40. package/dist/client/progress.js.map +1 -0
  41. package/dist/client/runs.d.ts +219 -0
  42. package/dist/client/runs.js +239 -0
  43. package/dist/client/runs.js.map +1 -0
  44. package/dist/client/storeTypes.d.ts +428 -0
  45. package/dist/client/storeTypes.js +256 -0
  46. package/dist/client/storeTypes.js.map +1 -0
  47. package/dist/client/transport.d.ts +19 -0
  48. package/dist/client/transport.js +2 -0
  49. package/dist/client/transport.js.map +1 -0
  50. package/dist/index.d.ts +27 -0
  51. package/dist/index.js +35 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/internal/abort.d.ts +21 -0
  54. package/dist/internal/abort.js +125 -0
  55. package/dist/internal/abort.js.map +1 -0
  56. package/dist/internal/base64.d.ts +8 -0
  57. package/dist/internal/base64.js +85 -0
  58. package/dist/internal/base64.js.map +1 -0
  59. package/dist/internal/dotnetFormat.d.ts +32 -0
  60. package/dist/internal/dotnetFormat.js +207 -0
  61. package/dist/internal/dotnetFormat.js.map +1 -0
  62. package/dist/internal/md5.d.ts +2 -0
  63. package/dist/internal/md5.js +97 -0
  64. package/dist/internal/md5.js.map +1 -0
  65. package/dist/internal/sha256.d.ts +20 -0
  66. package/dist/internal/sha256.js +153 -0
  67. package/dist/internal/sha256.js.map +1 -0
  68. package/dist/internal/utf8.d.ts +8 -0
  69. package/dist/internal/utf8.js +143 -0
  70. package/dist/internal/utf8.js.map +1 -0
  71. package/dist/node/index.d.ts +9 -0
  72. package/dist/node/index.js +35 -0
  73. package/dist/node/index.js.map +1 -0
  74. package/dist/node/sqliteDriver.d.ts +11 -0
  75. package/dist/node/sqliteDriver.js +252 -0
  76. package/dist/node/sqliteDriver.js.map +1 -0
  77. package/dist/protocol/dtos.d.ts +280 -0
  78. package/dist/protocol/dtos.js +33 -0
  79. package/dist/protocol/dtos.js.map +1 -0
  80. package/dist/protocol/protocol.d.ts +36 -0
  81. package/dist/protocol/protocol.js +63 -0
  82. package/dist/protocol/protocol.js.map +1 -0
  83. package/dist/protocol/rowBatch/columnLayout.d.ts +4 -0
  84. package/dist/protocol/rowBatch/columnLayout.js +309 -0
  85. package/dist/protocol/rowBatch/columnLayout.js.map +1 -0
  86. package/dist/protocol/rowBatch/format.d.ts +74 -0
  87. package/dist/protocol/rowBatch/format.js +163 -0
  88. package/dist/protocol/rowBatch/format.js.map +1 -0
  89. package/dist/protocol/rowBatch/reader.d.ts +119 -0
  90. package/dist/protocol/rowBatch/reader.js +459 -0
  91. package/dist/protocol/rowBatch/reader.js.map +1 -0
  92. package/dist/protocol/rowBatch/writer.d.ts +101 -0
  93. package/dist/protocol/rowBatch/writer.js +412 -0
  94. package/dist/protocol/rowBatch/writer.js.map +1 -0
  95. package/dist/protocol/wire.d.ts +32 -0
  96. package/dist/protocol/wire.js +329 -0
  97. package/dist/protocol/wire.js.map +1 -0
  98. package/dist/signalr/index.d.ts +2 -0
  99. package/dist/signalr/index.js +5 -0
  100. package/dist/signalr/index.js.map +1 -0
  101. package/dist/signalr/protocols.d.ts +17 -0
  102. package/dist/signalr/protocols.js +109 -0
  103. package/dist/signalr/protocols.js.map +1 -0
  104. package/dist/signalr/transport.d.ts +67 -0
  105. package/dist/signalr/transport.js +286 -0
  106. package/dist/signalr/transport.js.map +1 -0
  107. package/dist/sqlite/apply.d.ts +91 -0
  108. package/dist/sqlite/apply.js +657 -0
  109. package/dist/sqlite/apply.js.map +1 -0
  110. package/dist/sqlite/commands.d.ts +24 -0
  111. package/dist/sqlite/commands.js +167 -0
  112. package/dist/sqlite/commands.js.map +1 -0
  113. package/dist/sqlite/connection.d.ts +81 -0
  114. package/dist/sqlite/connection.js +322 -0
  115. package/dist/sqlite/connection.js.map +1 -0
  116. package/dist/sqlite/driver.d.ts +63 -0
  117. package/dist/sqlite/driver.js +9 -0
  118. package/dist/sqlite/driver.js.map +1 -0
  119. package/dist/sqlite/index.d.ts +6 -0
  120. package/dist/sqlite/index.js +7 -0
  121. package/dist/sqlite/index.js.map +1 -0
  122. package/dist/sqlite/internals.d.ts +63 -0
  123. package/dist/sqlite/internals.js +194 -0
  124. package/dist/sqlite/internals.js.map +1 -0
  125. package/dist/sqlite/options.d.ts +78 -0
  126. package/dist/sqlite/options.js +78 -0
  127. package/dist/sqlite/options.js.map +1 -0
  128. package/dist/sqlite/push.d.ts +9 -0
  129. package/dist/sqlite/push.js +272 -0
  130. package/dist/sqlite/push.js.map +1 -0
  131. package/dist/sqlite/query.d.ts +38 -0
  132. package/dist/sqlite/query.js +476 -0
  133. package/dist/sqlite/query.js.map +1 -0
  134. package/dist/sqlite/revocation.d.ts +28 -0
  135. package/dist/sqlite/revocation.js +337 -0
  136. package/dist/sqlite/revocation.js.map +1 -0
  137. package/dist/sqlite/sql.d.ts +60 -0
  138. package/dist/sqlite/sql.js +324 -0
  139. package/dist/sqlite/sql.js.map +1 -0
  140. package/dist/sqlite/store.d.ts +116 -0
  141. package/dist/sqlite/store.js +664 -0
  142. package/dist/sqlite/store.js.map +1 -0
  143. package/dist/sqlite/values.d.ts +29 -0
  144. package/dist/sqlite/values.js +288 -0
  145. package/dist/sqlite/values.js.map +1 -0
  146. package/dist/testing/index.d.ts +61 -0
  147. package/dist/testing/index.js +175 -0
  148. package/dist/testing/index.js.map +1 -0
  149. package/package.json +104 -0
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Iman Kari (G9TM)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/PARITY.md ADDED
@@ -0,0 +1,71 @@
1
+ # @g9tm/syncdata — parity with the .NET G9SyncData client
2
+
3
+ This package is the TypeScript twin of the .NET client packages of G9SyncData. **One change lands on both sides**: a
4
+ feature, fix, error code or wire change made to the .NET client is made here in the same change (and the other way
5
+ round), with the same version (`G9SyncDataVersion` in `Directory.Build.props`; `js/scripts/check-versions.mjs` enforces
6
+ it). This file maps every .NET piece to its twin and lists every deliberate difference.
7
+
8
+ ## How parity is proven
9
+
10
+ | Proof | Where |
11
+ |---|---|
12
+ | Golden vectors written by .NET, checked by TypeScript: varints, zig-zag, MD5, digest buckets and row hashes, canonical texts, key normalisation, subscription keys, G9RowBatch frames in both layouts (read in 4 encodings, re-written to the same bytes), the local schema SQL and model hashes, storage text forms, and the server's hub messages in JSON and MessagePack | `tests/G9SyncData.Tests/Protocol/JsGoldenVectorsTests.cs` → `test/golden/dotnet-vectors.json` → `test/protocol.test.ts`, `test/sql.test.ts`, `test/values.test.ts`, `test/hub.test.ts` |
13
+ | Requests written by TypeScript, parsed by the .NET server's own hub protocols (JSON and Nerdbank MessagePack) and compared with the .NET samples | `test/hub.test.ts` → `test/golden/js-vectors.json` → `JsGoldenVectorsTests.Requests_the_TypeScript_client_writes_deserialize_on_the_server` |
14
+ | The TypeScript engine + store + transport against the real .NET sample server (50,000 rows, push, pokes, reconcile, unsubscribe), over both hub protocols | `test/interop/server.test.ts` (`npm run test:interop`) |
15
+ | The database the TypeScript store writes equals the one the .NET store writes for the same sync: app rows and storage classes, every `g9_*` row, the schema, the stored JSON | `test/interop/server.test.ts` → database parity |
16
+ | The same scenario in a Lynx-shaped runtime (no BigInt/TextEncoder/URL/WebSocket, native modules behind a copying bridge) | `@g9tm/syncdata-lynx` `npm run test:lynxsim` |
17
+
18
+ `G9_UPDATE_GOLDEN=1` rewrites the golden files after an intended change; commit them with the change that needs them.
19
+
20
+ ## Map
21
+
22
+ | .NET | TypeScript | Notes |
23
+ |---|---|---|
24
+ | `G9ESyncColumnType`, `G9ESyncDirection`, `G9EConflictPolicy`, `G9EMoveOutPolicy` | same names (const objects) | wire numbers identical |
25
+ | `G9SyncErrorCodes`, `G9CSyncException` | `G9SyncErrorCodes`, `G9SyncException` (`code`, `retryable`, `details`) | |
26
+ | `long` (exact) | `G9Long` (hi/lo, no BigInt) | digests, row hashes, masks, ticks, int64 values; plain `number` for versions, ids, counts |
27
+ | `G9CSyncKey` | `G9SyncKey` | `Guid.Parse` forms D, N, B, P (not X) |
28
+ | `G9CSyncDigest`, `G9CMd5` | `G9SyncDigest`, `md5` | `canonicalText` takes the SQLite storage class (`typeof`) |
29
+ | `G9CSyncParameters` | `G9SyncParameters` (`setGuid`, `setInt64`, `setGuidList`, …) | `Uri.EscapeDataString` reproduced byte for byte |
30
+ | `G9Dt*` DTOs | same names, camelCase fields | `ServerTimeUtc` → `serverTimeUtcMs`; GUIDs as canonical strings |
31
+ | `G9SyncProtocol`, `G9SyncHubErrors` | same | |
32
+ | `G9CRowBatchWriter` / `G9CRowBatchReader` | `G9RowBatchWriter` / `openRowBatch` → `G9RowBatchReader` | async open (a Lynx decoder crosses the bridge) |
33
+ | Brotli (`System.IO.Compression`) | `G9FrameDecompressor`: `useNodeBrotli()`, `useLynxBrotli()` (native), `jsBrotliDecompressor()` | push frames are uncompressed unless a `G9FrameCompressor` is given (Node: `nodeBrotliCompressor()`); every server reads both |
34
+ | `G9CSyncClientModel(Builder)` | `G9SyncClientModel(Builder)` | no source generator: build the model in code, **tables in the order the .NET generator emits them** (entity type name, ordinal) for identical `tid`s |
35
+ | `G9CSyncEngine`, `G9CSyncEngineOptions` | `G9SyncEngine`, `G9SyncEngineOptions` (durations in ms) | events → `onRowsApplied` / `onRunCompleted`; `CancellationToken` → `AbortSignal`; `IProgress` → callbacks; `ILogger` → `G9SyncLogger` |
36
+ | `G9CSyncRun`, `G9CSyncRequest`, `G9CSyncRunResult`, `G9CSyncPostStep` | `G9SyncRun`, `G9SyncRequest`, `G9SyncRunResult`, `G9SyncPostStep` | `run.progress()` is the bounded progress channel |
37
+ | `G9CSyncProgressTracker`, `G9DtSyncProgress` | `G9SyncProgressTracker`, `G9DtSyncProgress` (`etaMs`) | same planning, creep and monotonicity |
38
+ | `IG9SyncClientStore` | `G9SyncClientStore` | |
39
+ | `G9CSqliteSyncStore`, options | `G9SqliteSyncStore` (`@g9tm/syncdata/sqlite`) over a `G9SqliteDriver` | same tables, triggers, SQL, JSON blobs and storage forms |
40
+ | `EnqueueCommand(sqlite3 appConnection, …)` | `enqueueCommandOnSession({ driver, session, tx }, …)` | SAVEPOINT inside the app's transaction |
41
+ | `IncarnationMarkerPath` | `incarnationMarker` (`fileIncarnationMarker(fileSystem, path)`) | any `G9FileSystem` (Lynx: the SignalR native module's) |
42
+ | `ExportPendingChangesAsync(path)` | `exportPendingChanges(writeLine)` | the caller writes the lines where it wants |
43
+ | `CheckIntegrityAsync` | `checkIntegrity()` | |
44
+ | `G9CSqliteException` (`IG9SyncLocalStoreFailure`) | `G9SqliteStoreError` (`localStoreFailure`) | classified from the driver's code |
45
+ | `G9CSignalRSyncTransport`, options | `G9SignalRSyncTransport` (`@g9tm/syncdata/signalr`) | over `G9Client` of `@g9tm/signalr-supernetcore-client`; Lynx: `createLynxSyncTransport` |
46
+ | `G9CInMemorySyncTransport`, `G9CTransportFaults` | `G9FaultInjectingTransport`, `G9TransportFaults` (`@g9tm/syncdata/testing`) | wraps a real transport (no in-process server engine in JavaScript) |
47
+
48
+ ## Deliberate differences
49
+
50
+ - **Applying rows.** The .NET store runs one prepared statement per row and decides from each result. Through a driver
51
+ every call is a bridge round trip on Lynx, so the TypeScript store queues the row statements and runs them in batches
52
+ (`statementsPerBatch`, default 1,500); the per-row decisions move into SQL — the metadata upsert reads `changes()` of
53
+ the row upsert, the clock moves with `UPDATE g9_clock … WHERE changes() > 0`. The resulting rows, metadata and clock
54
+ are identical (database parity test).
55
+ - **Skipping triggers during an apply.** There is no `SQLITE_DBCONFIG_ENABLE_TRIGGER` through a driver. For a staged
56
+ pull of at least `skipTriggersMinRows` (default 5,000) rows the store drops its own capture and command triggers inside
57
+ the apply transaction and re-creates them from their stored SQL before commit — never while a foreign trigger exists.
58
+ Smaller applies run with triggers on and report it (`diagnostics.triggersSkipped`).
59
+ - **Releasing many rows.** An unsubscribe, an access revocation or an expired ephemeral subscription disposes its
60
+ released rows in bulk (`disposeReleasedRows`): metadata, pending commands and app rows read in three queries, the
61
+ revocation policy deciding in the .NET store's order, writes batched with each row's explicit `local_seq`, the clock
62
+ moved once. Same database (`test/revocation.test.ts` against the row-by-row path, interop parity against .NET); row
63
+ by row while a foreign trigger exists. A move-out inside a pull stays row by row.
64
+ - **Two sessions.** A writer session under the store's lock and a reader session, instead of a pool.
65
+ - **Reading values to push.** Decimal and String columns are selected as `CAST(… AS TEXT)`: exactly the text .NET's
66
+ `GetString` / `GetDecimal` parse.
67
+ - **DateTime text format.** Only the default `yyyy-MM-dd'T'HH:mm:ss.fffffff` is supported.
68
+ - **Trigger text line endings.** A .NET build from a Windows checkout embeds CRLF in the trigger SQL it creates; the
69
+ TypeScript store writes LF. SQLite ignores the difference.
70
+ - **Not ported:** the Dotmim.Sync retirement (`RetireDotmimCaptureAsync` — a MAUI-only migration), OpenTelemetry
71
+ (`G9CSyncClientTelemetry`; log through `G9SyncLogger`), DI extensions.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # @g9tm/syncdata
2
+
3
+ The TypeScript twin of the [G9SyncData](https://github.com/ImanKari/G9SyncData) .NET client: offline-first sync between
4
+ a device's SQLite database and a PostgreSQL server, for **Lynx** apps (with
5
+ [`@g9tm/syncdata-lynx`](../lynx/README.md)), Node and browsers. Same protocol, same engine behaviour, and **the same
6
+ local database** as the .NET client — a database the MAUI app wrote opens in the Lynx app and keeps syncing.
7
+
8
+ | Entry point | What |
9
+ |---|---|
10
+ | `@g9tm/syncdata` | the engine (`G9SyncEngine`), the client model, the protocol (DTOs, G9RowBatch frames), errors, digests |
11
+ | `@g9tm/syncdata/sqlite` | the SQLite client store (`G9SqliteSyncStore`) over any `G9SqliteDriver` |
12
+ | `@g9tm/syncdata/signalr` | the SignalR transport (`G9SignalRSyncTransport`), JSON and MessagePack with exact 64-bit values |
13
+ | `@g9tm/syncdata/node` | node:sqlite driver (`nodeSqliteDriver`), Brotli through node:zlib (`useNodeBrotli`) |
14
+ | `@g9tm/syncdata/testing` | fault injection over any transport (`G9FaultInjectingTransport`) |
15
+ | `@g9tm/syncdata/brotli-js` | a pure-JavaScript Brotli decoder for runtimes without a native one |
16
+
17
+ Version 1.2.1 — always equal to the .NET packages' version. What maps to what, and every deliberate difference:
18
+ [PARITY.md](PARITY.md).
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm install @g9tm/syncdata @g9tm/signalr-supernetcore-client @microsoft/signalr @microsoft/signalr-protocol-msgpack @msgpack/msgpack
24
+ ```
25
+
26
+ The SignalR packages are optional peers: only `@g9tm/syncdata/signalr` needs them.
27
+
28
+ ## Use (Node)
29
+
30
+ ```ts
31
+ import { G9ESyncColumnType as T, G9SyncClientModelBuilder, G9SyncEngine, G9SyncParameters } from '@g9tm/syncdata';
32
+ import { G9SqliteSyncStore } from '@g9tm/syncdata/sqlite';
33
+ import { G9SignalRSyncTransport } from '@g9tm/syncdata/signalr';
34
+ import { nodeSqliteDriver, useNodeBrotli } from '@g9tm/syncdata/node';
35
+
36
+ useNodeBrotli(); // the server compresses frames with Brotli
37
+
38
+ // Tables in the order the .NET source generator emits them (entity type name), so both clients number them alike.
39
+ const model = new G9SyncClientModelBuilder()
40
+ .table('farm.Pot', 'Pot', (t) =>
41
+ t.key('Id').column('SiteId', T.Guid, false).column('Code', T.String, false).column('Weight', T.Decimal).index(null, false, 'SiteId'))
42
+ .table('farm.Site', 'Site', (t) => t.key('Id').column('Name', T.String, false))
43
+ .build();
44
+
45
+ const store = new G9SqliteSyncStore({ driver: nodeSqliteDriver(), databasePath: './device.db' });
46
+ const transport = new G9SignalRSyncTransport({ url: 'https://sync.example.com/g9sync', accessTokenFactory: () => token });
47
+ const engine = new G9SyncEngine(store, transport, model, { appVersion: '1.0.0' });
48
+
49
+ await engine.initialize();
50
+ await engine.subscribe('sites');
51
+ await engine.subscribe('site-pots', new G9SyncParameters().setGuid('SiteId', siteId));
52
+ const run = engine.run({ subscriptions: ['site-pots'] });
53
+ run.onProgress((p) => console.log(p.phase, Math.round(p.overall * 100) + '%'));
54
+ const result = await run.completion;
55
+ if (!result.isSuccess) console.error(result.error?.code, result.error?.message);
56
+ ```
57
+
58
+ Local edits are ordinary SQL on the app's own connection: the store's triggers capture them and the next run pushes
59
+ them. Commands (`engine.executeCommand`), the change feed (`readChangeFeed`), rejections, quarantine and the run reports
60
+ work as on .NET ([07-Client-Engine](../../../AiGuides/07-Client-Engine.md), [08-Client-Sqlite](../../../AiGuides/08-Client-Sqlite.md)).
61
+
62
+ ## Lynx
63
+
64
+ On Lynx's background thread use [`@g9tm/syncdata-lynx`](../lynx/README.md): `useLynxBrotli()` and
65
+ `createLynxSyncTransport({ url })` (MessagePack over the G9 native WebSocket — release Lynx builds have none of their
66
+ own), and G9LynxControls' native SQLite driver for the store. The code ships as ES2019 and uses no API the background
67
+ thread lacks (no BigInt, `TextEncoder`, `URL`, `btoa`, `globalThis` timers) — the build fails otherwise.
68
+
69
+ ## License
70
+
71
+ MIT. The Brotli decoder in `brotli-js` is Google's (MIT): [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md).
@@ -0,0 +1,31 @@
1
+ # Third-party notices
2
+
3
+ `@g9tm/syncdata` ships one piece of third-party code.
4
+
5
+ ## Brotli decoder (`dist/brotli-js/decode.js`)
6
+
7
+ Google's JavaScript Brotli decoder, `js/decode.ts` of https://github.com/google/brotli at commit
8
+ `3499acbb7ac7818c1b929a8c9c5c5f8a634751da` (2025-10-22), vendored unmodified in `src/brotli-js/decode.ts`. It is only
9
+ loaded through the `@g9tm/syncdata/brotli-js` entry point.
10
+
11
+ ```text
12
+ Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors.
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in
22
+ all copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30
+ THE SOFTWARE.
31
+ ```
@@ -0,0 +1,27 @@
1
+ import { G9Long } from './long.js';
2
+ /**
3
+ * The reconciliation hash (twin of `G9CSyncDigest`), defined so PostgreSQL computes the same numbers in plain SQL:
4
+ * bucket = first byte of md5(key); row hash = first 8 bytes (big-endian, signed) of md5(`key:version[:computed…]`);
5
+ * bucket digest = XOR of the row hashes in the bucket.
6
+ */
7
+ export declare const G9SyncDigest: {
8
+ /** Number of first-level buckets. */
9
+ BucketCount: number;
10
+ /** Bucket of a canonical key. */
11
+ getBucket(canonicalKey: string): number;
12
+ /** Bucket of a canonical key given as UTF-8. */
13
+ getBucketUtf8(canonicalKeyUtf8: Uint8Array): number;
14
+ /**
15
+ * Row hash of a canonical key, its server version and optional computed-column canonical texts (a null value
16
+ * contributes an empty field, like .NET's `StringBuilder.Append(null)`).
17
+ */
18
+ getRowHash(canonicalKey: string, version: number | G9Long, computedValues?: readonly (string | null)[]): G9Long;
19
+ /**
20
+ * Canonical text of a computed value, identical to what the server SQL produces (`G9CSyncDigest.CanonicalText`).
21
+ * `value` is what the SQLite driver read: null, a number, an `{ int64 }` tag or {@link G9Long}, text, or a BLOB.
22
+ * `storage` says whether a number came from an INTEGER or a REAL cell (JavaScript cannot tell 5 from 5.0, .NET
23
+ * formats them alike, but 1e15 as a REAL is `1E+15` and as an INTEGER `1000000000000000`); without it, integral
24
+ * numbers count as INTEGER.
25
+ */
26
+ canonicalText(type: number, value: unknown, storage?: "integer" | "real"): string | null;
27
+ };
@@ -0,0 +1,78 @@
1
+ import { formatDouble } from '../internal/dotnetFormat.js';
2
+ import { md5 } from '../internal/md5.js';
3
+ import { utf8Encode } from '../internal/utf8.js';
4
+ import { G9ESyncColumnType } from './enums.js';
5
+ import { tryNormalizeGuid } from './guid.js';
6
+ import { G9Long } from './long.js';
7
+ const HASH = new Uint8Array(16);
8
+ /**
9
+ * The reconciliation hash (twin of `G9CSyncDigest`), defined so PostgreSQL computes the same numbers in plain SQL:
10
+ * bucket = first byte of md5(key); row hash = first 8 bytes (big-endian, signed) of md5(`key:version[:computed…]`);
11
+ * bucket digest = XOR of the row hashes in the bucket.
12
+ */
13
+ export const G9SyncDigest = {
14
+ /** Number of first-level buckets. */
15
+ BucketCount: 256,
16
+ /** Bucket of a canonical key. */
17
+ getBucket(canonicalKey) {
18
+ md5(utf8Encode(canonicalKey), HASH);
19
+ return HASH[0];
20
+ },
21
+ /** Bucket of a canonical key given as UTF-8. */
22
+ getBucketUtf8(canonicalKeyUtf8) {
23
+ md5(canonicalKeyUtf8, HASH);
24
+ return HASH[0];
25
+ },
26
+ /**
27
+ * Row hash of a canonical key, its server version and optional computed-column canonical texts (a null value
28
+ * contributes an empty field, like .NET's `StringBuilder.Append(null)`).
29
+ */
30
+ getRowHash(canonicalKey, version, computedValues) {
31
+ var _a;
32
+ let text = canonicalKey + ':' + (version instanceof G9Long ? version.toString() : G9Long.fromNumber(version).toString());
33
+ if (computedValues)
34
+ for (let i = 0; i < computedValues.length; i++)
35
+ text += ':' + ((_a = computedValues[i]) !== null && _a !== void 0 ? _a : '');
36
+ md5(utf8Encode(text), HASH);
37
+ return G9Long.readBE(HASH, 0);
38
+ },
39
+ /**
40
+ * Canonical text of a computed value, identical to what the server SQL produces (`G9CSyncDigest.CanonicalText`).
41
+ * `value` is what the SQLite driver read: null, a number, an `{ int64 }` tag or {@link G9Long}, text, or a BLOB.
42
+ * `storage` says whether a number came from an INTEGER or a REAL cell (JavaScript cannot tell 5 from 5.0, .NET
43
+ * formats them alike, but 1e15 as a REAL is `1E+15` and as an INTEGER `1000000000000000`); without it, integral
44
+ * numbers count as INTEGER.
45
+ */
46
+ canonicalText(type, value, storage) {
47
+ if (value === null || value === undefined)
48
+ return null;
49
+ if (typeof value === 'boolean')
50
+ return value ? '1' : '0';
51
+ if (value instanceof G9Long || (typeof value === 'object' && value !== null && typeof value.int64 === 'string')) {
52
+ const long = G9Long.from(value);
53
+ if (type === G9ESyncColumnType.Boolean)
54
+ return long.isZero() ? '0' : '1';
55
+ return long.toString();
56
+ }
57
+ if (typeof value === 'number') {
58
+ const integer = storage === 'integer' || (storage === undefined && Math.floor(value) === value && Math.abs(value) <= 9007199254740991);
59
+ if (integer) {
60
+ if (type === G9ESyncColumnType.Boolean)
61
+ return value !== 0 ? '1' : '0';
62
+ return G9Long.fromNumber(value).toString();
63
+ }
64
+ return formatDouble(value);
65
+ }
66
+ if (typeof value === 'string') {
67
+ if (type === G9ESyncColumnType.Guid) {
68
+ const guid = tryNormalizeGuid(value);
69
+ if (guid !== null)
70
+ return guid;
71
+ }
72
+ return value;
73
+ }
74
+ // A BLOB: .NET's object.ToString() of a byte[].
75
+ return 'System.Byte[]';
76
+ },
77
+ };
78
+ //# sourceMappingURL=digest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest.js","sourceRoot":"","sources":["../../src/abstractions/digest.ts"],"names":[],"mappings":"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","sourcesContent":["import { formatDouble } from '../internal/dotnetFormat.js';\nimport { md5 } from '../internal/md5.js';\nimport { utf8Encode } from '../internal/utf8.js';\nimport { G9ESyncColumnType } from './enums.js';\nimport { tryNormalizeGuid } from './guid.js';\nimport { G9Long } from './long.js';\n\nconst HASH = new Uint8Array(16);\n\n/**\n * The reconciliation hash (twin of `G9CSyncDigest`), defined so PostgreSQL computes the same numbers in plain SQL:\n * bucket = first byte of md5(key); row hash = first 8 bytes (big-endian, signed) of md5(`key:version[:computed…]`);\n * bucket digest = XOR of the row hashes in the bucket.\n */\nexport const G9SyncDigest = {\n /** Number of first-level buckets. */\n BucketCount: 256,\n\n /** Bucket of a canonical key. */\n getBucket(canonicalKey: string): number {\n md5(utf8Encode(canonicalKey), HASH);\n return HASH[0] as number;\n },\n\n /** Bucket of a canonical key given as UTF-8. */\n getBucketUtf8(canonicalKeyUtf8: Uint8Array): number {\n md5(canonicalKeyUtf8, HASH);\n return HASH[0] as number;\n },\n\n /**\n * Row hash of a canonical key, its server version and optional computed-column canonical texts (a null value\n * contributes an empty field, like .NET's `StringBuilder.Append(null)`).\n */\n getRowHash(canonicalKey: string, version: number | G9Long, computedValues?: readonly (string | null)[]): G9Long {\n let text = canonicalKey + ':' + (version instanceof G9Long ? version.toString() : G9Long.fromNumber(version).toString());\n if (computedValues) for (let i = 0; i < computedValues.length; i++) text += ':' + (computedValues[i] ?? '');\n md5(utf8Encode(text), HASH);\n return G9Long.readBE(HASH, 0);\n },\n\n /**\n * Canonical text of a computed value, identical to what the server SQL produces (`G9CSyncDigest.CanonicalText`).\n * `value` is what the SQLite driver read: null, a number, an `{ int64 }` tag or {@link G9Long}, text, or a BLOB.\n * `storage` says whether a number came from an INTEGER or a REAL cell (JavaScript cannot tell 5 from 5.0, .NET\n * formats them alike, but 1e15 as a REAL is `1E+15` and as an INTEGER `1000000000000000`); without it, integral\n * numbers count as INTEGER.\n */\n canonicalText(type: number, value: unknown, storage?: 'integer' | 'real'): string | null {\n if (value === null || value === undefined) return null;\n if (typeof value === 'boolean') return value ? '1' : '0';\n if (value instanceof G9Long || (typeof value === 'object' && value !== null && typeof (value as { int64?: unknown }).int64 === 'string')) {\n const long = G9Long.from(value as G9Long);\n if (type === G9ESyncColumnType.Boolean) return long.isZero() ? '0' : '1';\n return long.toString();\n }\n if (typeof value === 'number') {\n const integer = storage === 'integer' || (storage === undefined && Math.floor(value) === value && Math.abs(value) <= 9007199254740991);\n if (integer) {\n if (type === G9ESyncColumnType.Boolean) return value !== 0 ? '1' : '0';\n return G9Long.fromNumber(value).toString();\n }\n return formatDouble(value);\n }\n if (typeof value === 'string') {\n if (type === G9ESyncColumnType.Guid) {\n const guid = tryNormalizeGuid(value);\n if (guid !== null) return guid;\n }\n return value;\n }\n // A BLOB: .NET's object.ToString() of a byte[].\n return 'System.Byte[]';\n },\n};\n"]}
@@ -0,0 +1,42 @@
1
+ /** The portable column types (`G9ESyncColumnType`). */
2
+ export declare const G9ESyncColumnType: {
3
+ readonly Unspecified: 0;
4
+ readonly Boolean: 1;
5
+ readonly Int32: 2;
6
+ readonly Int64: 3;
7
+ readonly Double: 4;
8
+ readonly Decimal: 5;
9
+ readonly String: 6;
10
+ readonly Guid: 7;
11
+ readonly DateTime: 8;
12
+ readonly DateTimeOffset: 9;
13
+ readonly TimeSpan: 10;
14
+ readonly Bytes: 11;
15
+ };
16
+ export type G9ESyncColumnType = (typeof G9ESyncColumnType)[keyof typeof G9ESyncColumnType];
17
+ /** The .NET enum member name of a column type (`G9ESyncColumnType.ToString()`), e.g. `Guid`. */
18
+ export declare function columnTypeName(type: number): string;
19
+ /** True for a defined, specified column type. */
20
+ export declare function isColumnType(type: unknown): type is G9ESyncColumnType;
21
+ /** Which way rows of a table may travel (`G9ESyncDirection`). */
22
+ export declare const G9ESyncDirection: {
23
+ readonly Bidirectional: 0;
24
+ readonly DownloadOnly: 1;
25
+ readonly UploadOnly: 2;
26
+ };
27
+ export type G9ESyncDirection = (typeof G9ESyncDirection)[keyof typeof G9ESyncDirection];
28
+ /** How the server resolves a conflicting upload (`G9EConflictPolicy`). */
29
+ export declare const G9EConflictPolicy: {
30
+ readonly ServerWins: 0;
31
+ readonly ClientWins: 1;
32
+ readonly ColumnMerge: 2;
33
+ readonly LastWriterWins: 3;
34
+ readonly Custom: 4;
35
+ };
36
+ export type G9EConflictPolicy = (typeof G9EConflictPolicy)[keyof typeof G9EConflictPolicy];
37
+ /** What the client does with a row that left a subscription (`G9EMoveOutPolicy`). */
38
+ export declare const G9EMoveOutPolicy: {
39
+ readonly Delete: 0;
40
+ readonly Keep: 1;
41
+ };
42
+ export type G9EMoveOutPolicy = (typeof G9EMoveOutPolicy)[keyof typeof G9EMoveOutPolicy];
@@ -0,0 +1,60 @@
1
+ // Twins of G9SyncData.Abstractions/G9SyncEnums.cs and the protocol enums. Values are the wire numbers; the objects
2
+ // give them names (const objects, not TypeScript enums, so the shipped JavaScript stays plain ES2019).
3
+ /** The portable column types (`G9ESyncColumnType`). */
4
+ export const G9ESyncColumnType = {
5
+ Unspecified: 0,
6
+ Boolean: 1,
7
+ Int32: 2,
8
+ Int64: 3,
9
+ Double: 4,
10
+ Decimal: 5,
11
+ String: 6,
12
+ Guid: 7,
13
+ DateTime: 8,
14
+ DateTimeOffset: 9,
15
+ TimeSpan: 10,
16
+ Bytes: 11,
17
+ };
18
+ const COLUMN_TYPE_NAMES = [
19
+ 'Unspecified',
20
+ 'Boolean',
21
+ 'Int32',
22
+ 'Int64',
23
+ 'Double',
24
+ 'Decimal',
25
+ 'String',
26
+ 'Guid',
27
+ 'DateTime',
28
+ 'DateTimeOffset',
29
+ 'TimeSpan',
30
+ 'Bytes',
31
+ ];
32
+ /** The .NET enum member name of a column type (`G9ESyncColumnType.ToString()`), e.g. `Guid`. */
33
+ export function columnTypeName(type) {
34
+ var _a;
35
+ return (_a = COLUMN_TYPE_NAMES[type]) !== null && _a !== void 0 ? _a : String(type);
36
+ }
37
+ /** True for a defined, specified column type. */
38
+ export function isColumnType(type) {
39
+ return typeof type === 'number' && type >= 1 && type <= 11 && Math.floor(type) === type;
40
+ }
41
+ /** Which way rows of a table may travel (`G9ESyncDirection`). */
42
+ export const G9ESyncDirection = {
43
+ Bidirectional: 0,
44
+ DownloadOnly: 1,
45
+ UploadOnly: 2,
46
+ };
47
+ /** How the server resolves a conflicting upload (`G9EConflictPolicy`). */
48
+ export const G9EConflictPolicy = {
49
+ ServerWins: 0,
50
+ ClientWins: 1,
51
+ ColumnMerge: 2,
52
+ LastWriterWins: 3,
53
+ Custom: 4,
54
+ };
55
+ /** What the client does with a row that left a subscription (`G9EMoveOutPolicy`). */
56
+ export const G9EMoveOutPolicy = {
57
+ Delete: 0,
58
+ Keep: 1,
59
+ };
60
+ //# sourceMappingURL=enums.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enums.js","sourceRoot":"","sources":["../../src/abstractions/enums.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,uGAAuG;AAEvG,uDAAuD;AACvD,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,WAAW,EAAE,CAAC;IACd,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,CAAC;IACR,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;IACV,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,CAAC;IACP,QAAQ,EAAE,CAAC;IACX,cAAc,EAAE,CAAC;IACjB,QAAQ,EAAE,EAAE;IACZ,KAAK,EAAE,EAAE;CACD,CAAC;AAGX,MAAM,iBAAiB,GAAG;IACxB,aAAa;IACb,SAAS;IACT,OAAO;IACP,OAAO;IACP,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,MAAM;IACN,UAAU;IACV,gBAAgB;IAChB,UAAU;IACV,OAAO;CACC,CAAC;AAEX,gGAAgG;AAChG,MAAM,UAAU,cAAc,CAAC,IAAY;;IACzC,OAAO,MAAA,iBAAiB,CAAC,IAAI,CAAC,mCAAI,MAAM,CAAC,IAAI,CAAC,CAAC;AACjD,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;AAC1F,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,aAAa,EAAE,CAAC;IAChB,YAAY,EAAE,CAAC;IACf,UAAU,EAAE,CAAC;CACL,CAAC;AAGX,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,CAAC;IACd,cAAc,EAAE,CAAC;IACjB,MAAM,EAAE,CAAC;CACD,CAAC;AAGX,qFAAqF;AACrF,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,CAAC;CACC,CAAC","sourcesContent":["// Twins of G9SyncData.Abstractions/G9SyncEnums.cs and the protocol enums. Values are the wire numbers; the objects\n// give them names (const objects, not TypeScript enums, so the shipped JavaScript stays plain ES2019).\n\n/** The portable column types (`G9ESyncColumnType`). */\nexport const G9ESyncColumnType = {\n Unspecified: 0,\n Boolean: 1,\n Int32: 2,\n Int64: 3,\n Double: 4,\n Decimal: 5,\n String: 6,\n Guid: 7,\n DateTime: 8,\n DateTimeOffset: 9,\n TimeSpan: 10,\n Bytes: 11,\n} as const;\nexport type G9ESyncColumnType = (typeof G9ESyncColumnType)[keyof typeof G9ESyncColumnType];\n\nconst COLUMN_TYPE_NAMES = [\n 'Unspecified',\n 'Boolean',\n 'Int32',\n 'Int64',\n 'Double',\n 'Decimal',\n 'String',\n 'Guid',\n 'DateTime',\n 'DateTimeOffset',\n 'TimeSpan',\n 'Bytes',\n] as const;\n\n/** The .NET enum member name of a column type (`G9ESyncColumnType.ToString()`), e.g. `Guid`. */\nexport function columnTypeName(type: number): string {\n return COLUMN_TYPE_NAMES[type] ?? String(type);\n}\n\n/** True for a defined, specified column type. */\nexport function isColumnType(type: unknown): type is G9ESyncColumnType {\n return typeof type === 'number' && type >= 1 && type <= 11 && Math.floor(type) === type;\n}\n\n/** Which way rows of a table may travel (`G9ESyncDirection`). */\nexport const G9ESyncDirection = {\n Bidirectional: 0,\n DownloadOnly: 1,\n UploadOnly: 2,\n} as const;\nexport type G9ESyncDirection = (typeof G9ESyncDirection)[keyof typeof G9ESyncDirection];\n\n/** How the server resolves a conflicting upload (`G9EConflictPolicy`). */\nexport const G9EConflictPolicy = {\n ServerWins: 0,\n ClientWins: 1,\n ColumnMerge: 2,\n LastWriterWins: 3,\n Custom: 4,\n} as const;\nexport type G9EConflictPolicy = (typeof G9EConflictPolicy)[keyof typeof G9EConflictPolicy];\n\n/** What the client does with a row that left a subscription (`G9EMoveOutPolicy`). */\nexport const G9EMoveOutPolicy = {\n Delete: 0,\n Keep: 1,\n} as const;\nexport type G9EMoveOutPolicy = (typeof G9EMoveOutPolicy)[keyof typeof G9EMoveOutPolicy];\n"]}
@@ -0,0 +1,72 @@
1
+ /** Stable, support-facing error codes. The client classifies failures by these codes, never by message text. */
2
+ export declare const G9SyncErrorCodes: {
3
+ readonly NetworkFailure: "G9S-NET-FAILURE";
4
+ readonly ServerError: "G9S-SRV-ERROR";
5
+ readonly Canceled: "G9S-CANCELED";
6
+ readonly Unauthorized: "G9S-AUTH-401";
7
+ readonly UpgradeRequired: "G9S-VER-UPGRADE";
8
+ readonly ProtocolMismatch: "G9S-VER-PROTOCOL";
9
+ readonly ModelInvalid: "G9S-MODEL-INVALID";
10
+ readonly SchemaUpgradeRequired: "G9S-SCH-UPGRADE";
11
+ readonly UnknownTable: "G9S-SCH-UNKNOWN-TABLE";
12
+ /** A column declared with a type the server's column does not have (ADR-0061): it syncs in neither direction. */
13
+ readonly ModelTypeMismatch: "G9S-MODEL-TYPE-MISMATCH";
14
+ /** The server is at its limit of concurrent pulls or pushes (ADR-0062). Retryable. */
15
+ readonly ServerBusy: "G9S-SRV-BUSY";
16
+ readonly UnknownSubscription: "G9S-SUB-UNKNOWN";
17
+ readonly InvalidParameters: "G9S-SUB-PARAM";
18
+ readonly SubscriptionForbidden: "G9S-SUB-FORBIDDEN";
19
+ readonly ResyncEpoch: "G9S-RSY-EPOCH";
20
+ readonly ResyncHorizon: "G9S-RSY-HORIZON";
21
+ readonly ResyncAuthChanged: "G9S-RSY-AUTH";
22
+ readonly TokenInvalid: "G9S-TOKEN-INVALID";
23
+ readonly TokenExpired: "G9S-TOKEN-EXPIRED";
24
+ readonly RejectValidation: "G9S-REJ-VALIDATION";
25
+ readonly RejectConstraint: "G9S-REJ-CONSTRAINT";
26
+ readonly RejectPermission: "G9S-REJ-PERMISSION";
27
+ readonly RejectDirection: "G9S-REJ-DIRECTION";
28
+ readonly RejectTooMany: "G9S-REJ-TOO-MANY";
29
+ /** One row is larger than the server accepts in a push: rejected on the device, every other row still uploads. */
30
+ readonly RejectTooLarge: "G9S-REJ-TOO-LARGE";
31
+ readonly RejectMalformed: "G9S-REJ-MALFORMED";
32
+ readonly CommandUnknown: "G9S-CMD-UNKNOWN";
33
+ readonly CommandRejected: "G9S-CMD-REJECTED";
34
+ readonly ConflictServerWins: "G9S-CON-SERVER-WINS";
35
+ readonly ConflictColumns: "G9S-CON-COLUMNS";
36
+ readonly ConflictDeleted: "G9S-CON-DELETED";
37
+ readonly ConflictResolverRejected: "G9S-CON-RESOLVER";
38
+ readonly LocalStoreError: "G9S-LOCAL-STORE";
39
+ /** The device database is damaged or not a database (ADR-0068). Export what is readable and rebuild from the server. */
40
+ readonly LocalStoreCorrupt: "G9S-LOCAL-CORRUPT";
41
+ /** The device ran out of storage while the store was writing. */
42
+ readonly LocalStoreDiskFull: "G9S-LOCAL-DISK-FULL";
43
+ /** Another connection held the write lock longer than the busy timeout. Retryable. */
44
+ readonly LocalStoreBusy: "G9S-LOCAL-BUSY";
45
+ readonly Unknown: "G9S-UNKNOWN";
46
+ };
47
+ /** True for failures a later attempt may succeed at without any change to data or code (`G9SyncErrorCodes.IsRetryable`). */
48
+ export declare function isRetryableCode(code: string): boolean;
49
+ /** Options of {@link G9SyncException}. */
50
+ export interface G9SyncExceptionOptions {
51
+ /** Overrides the default retryability of the code. */
52
+ retryable?: boolean;
53
+ /** Structured detail (JSON or text) for logs. Never shown to end users. */
54
+ details?: string | null;
55
+ /** The underlying failure. */
56
+ cause?: unknown;
57
+ }
58
+ /**
59
+ * The single error type G9SyncData throws for expected failures (twin of `G9CSyncException`). Always carries a stable
60
+ * {@link code}; callers switch on the code, never on the message.
61
+ */
62
+ export declare class G9SyncException extends Error {
63
+ /** Stable error code (see {@link G9SyncErrorCodes}). */
64
+ readonly code: string;
65
+ /** True when retrying without changes may succeed. */
66
+ readonly retryable: boolean;
67
+ /** Optional structured detail (JSON or text) for logs. */
68
+ readonly details: string | null;
69
+ constructor(code: string, message: string, options?: G9SyncExceptionOptions);
70
+ }
71
+ /** True for a {@link G9SyncException} (also across bundles that each carry a copy of the class). */
72
+ export declare function isSyncException(error: unknown): error is G9SyncException;
@@ -0,0 +1,90 @@
1
+ // Twins of G9SyncData.Abstractions/G9SyncErrorCodes.cs and G9CSyncException.cs. A code is a public API: never
2
+ // repurpose one, only add — and add it on both sides in the same change.
3
+ /** Stable, support-facing error codes. The client classifies failures by these codes, never by message text. */
4
+ export const G9SyncErrorCodes = {
5
+ NetworkFailure: 'G9S-NET-FAILURE',
6
+ ServerError: 'G9S-SRV-ERROR',
7
+ Canceled: 'G9S-CANCELED',
8
+ Unauthorized: 'G9S-AUTH-401',
9
+ UpgradeRequired: 'G9S-VER-UPGRADE',
10
+ ProtocolMismatch: 'G9S-VER-PROTOCOL',
11
+ ModelInvalid: 'G9S-MODEL-INVALID',
12
+ SchemaUpgradeRequired: 'G9S-SCH-UPGRADE',
13
+ UnknownTable: 'G9S-SCH-UNKNOWN-TABLE',
14
+ /** A column declared with a type the server's column does not have (ADR-0061): it syncs in neither direction. */
15
+ ModelTypeMismatch: 'G9S-MODEL-TYPE-MISMATCH',
16
+ /** The server is at its limit of concurrent pulls or pushes (ADR-0062). Retryable. */
17
+ ServerBusy: 'G9S-SRV-BUSY',
18
+ UnknownSubscription: 'G9S-SUB-UNKNOWN',
19
+ InvalidParameters: 'G9S-SUB-PARAM',
20
+ SubscriptionForbidden: 'G9S-SUB-FORBIDDEN',
21
+ ResyncEpoch: 'G9S-RSY-EPOCH',
22
+ ResyncHorizon: 'G9S-RSY-HORIZON',
23
+ ResyncAuthChanged: 'G9S-RSY-AUTH',
24
+ TokenInvalid: 'G9S-TOKEN-INVALID',
25
+ TokenExpired: 'G9S-TOKEN-EXPIRED',
26
+ RejectValidation: 'G9S-REJ-VALIDATION',
27
+ RejectConstraint: 'G9S-REJ-CONSTRAINT',
28
+ RejectPermission: 'G9S-REJ-PERMISSION',
29
+ RejectDirection: 'G9S-REJ-DIRECTION',
30
+ RejectTooMany: 'G9S-REJ-TOO-MANY',
31
+ /** One row is larger than the server accepts in a push: rejected on the device, every other row still uploads. */
32
+ RejectTooLarge: 'G9S-REJ-TOO-LARGE',
33
+ RejectMalformed: 'G9S-REJ-MALFORMED',
34
+ CommandUnknown: 'G9S-CMD-UNKNOWN',
35
+ CommandRejected: 'G9S-CMD-REJECTED',
36
+ ConflictServerWins: 'G9S-CON-SERVER-WINS',
37
+ ConflictColumns: 'G9S-CON-COLUMNS',
38
+ ConflictDeleted: 'G9S-CON-DELETED',
39
+ ConflictResolverRejected: 'G9S-CON-RESOLVER',
40
+ LocalStoreError: 'G9S-LOCAL-STORE',
41
+ /** The device database is damaged or not a database (ADR-0068). Export what is readable and rebuild from the server. */
42
+ LocalStoreCorrupt: 'G9S-LOCAL-CORRUPT',
43
+ /** The device ran out of storage while the store was writing. */
44
+ LocalStoreDiskFull: 'G9S-LOCAL-DISK-FULL',
45
+ /** Another connection held the write lock longer than the busy timeout. Retryable. */
46
+ LocalStoreBusy: 'G9S-LOCAL-BUSY',
47
+ Unknown: 'G9S-UNKNOWN',
48
+ };
49
+ const RETRYABLE = {
50
+ 'G9S-NET-FAILURE': true,
51
+ 'G9S-SRV-ERROR': true,
52
+ 'G9S-SRV-BUSY': true,
53
+ 'G9S-TOKEN-EXPIRED': true,
54
+ 'G9S-RSY-EPOCH': true,
55
+ 'G9S-RSY-HORIZON': true,
56
+ 'G9S-RSY-AUTH': true,
57
+ 'G9S-LOCAL-BUSY': true,
58
+ };
59
+ /** True for failures a later attempt may succeed at without any change to data or code (`G9SyncErrorCodes.IsRetryable`). */
60
+ export function isRetryableCode(code) {
61
+ return RETRYABLE[code] === true;
62
+ }
63
+ /**
64
+ * The single error type G9SyncData throws for expected failures (twin of `G9CSyncException`). Always carries a stable
65
+ * {@link code}; callers switch on the code, never on the message.
66
+ */
67
+ export class G9SyncException extends Error {
68
+ constructor(code, message, options) {
69
+ var _a, _b;
70
+ super(message);
71
+ this.name = 'G9SyncException';
72
+ this.code = code;
73
+ this.retryable = (_a = options === null || options === void 0 ? void 0 : options.retryable) !== null && _a !== void 0 ? _a : isRetryableCode(code);
74
+ this.details = (_b = options === null || options === void 0 ? void 0 : options.details) !== null && _b !== void 0 ? _b : null;
75
+ if (options && 'cause' in options) {
76
+ Object.defineProperty(this, 'cause', { value: options.cause, enumerable: false, writable: true, configurable: true });
77
+ }
78
+ // ES2019 output: keep instanceof working for subclasses compiled to functions.
79
+ Object.setPrototypeOf(this, new.target.prototype);
80
+ }
81
+ }
82
+ /** True for a {@link G9SyncException} (also across bundles that each carry a copy of the class). */
83
+ export function isSyncException(error) {
84
+ return (error instanceof G9SyncException ||
85
+ (typeof error === 'object' &&
86
+ error !== null &&
87
+ error.name === 'G9SyncException' &&
88
+ typeof error.code === 'string'));
89
+ }
90
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/abstractions/errors.ts"],"names":[],"mappings":"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","sourcesContent":["// Twins of G9SyncData.Abstractions/G9SyncErrorCodes.cs and G9CSyncException.cs. A code is a public API: never\n// repurpose one, only add — and add it on both sides in the same change.\n\n/** Stable, support-facing error codes. The client classifies failures by these codes, never by message text. */\nexport const G9SyncErrorCodes = {\n NetworkFailure: 'G9S-NET-FAILURE',\n ServerError: 'G9S-SRV-ERROR',\n Canceled: 'G9S-CANCELED',\n\n Unauthorized: 'G9S-AUTH-401',\n UpgradeRequired: 'G9S-VER-UPGRADE',\n ProtocolMismatch: 'G9S-VER-PROTOCOL',\n\n ModelInvalid: 'G9S-MODEL-INVALID',\n SchemaUpgradeRequired: 'G9S-SCH-UPGRADE',\n UnknownTable: 'G9S-SCH-UNKNOWN-TABLE',\n /** A column declared with a type the server's column does not have (ADR-0061): it syncs in neither direction. */\n ModelTypeMismatch: 'G9S-MODEL-TYPE-MISMATCH',\n /** The server is at its limit of concurrent pulls or pushes (ADR-0062). Retryable. */\n ServerBusy: 'G9S-SRV-BUSY',\n\n UnknownSubscription: 'G9S-SUB-UNKNOWN',\n InvalidParameters: 'G9S-SUB-PARAM',\n SubscriptionForbidden: 'G9S-SUB-FORBIDDEN',\n\n ResyncEpoch: 'G9S-RSY-EPOCH',\n ResyncHorizon: 'G9S-RSY-HORIZON',\n ResyncAuthChanged: 'G9S-RSY-AUTH',\n TokenInvalid: 'G9S-TOKEN-INVALID',\n TokenExpired: 'G9S-TOKEN-EXPIRED',\n\n RejectValidation: 'G9S-REJ-VALIDATION',\n RejectConstraint: 'G9S-REJ-CONSTRAINT',\n RejectPermission: 'G9S-REJ-PERMISSION',\n RejectDirection: 'G9S-REJ-DIRECTION',\n RejectTooMany: 'G9S-REJ-TOO-MANY',\n /** One row is larger than the server accepts in a push: rejected on the device, every other row still uploads. */\n RejectTooLarge: 'G9S-REJ-TOO-LARGE',\n RejectMalformed: 'G9S-REJ-MALFORMED',\n\n CommandUnknown: 'G9S-CMD-UNKNOWN',\n CommandRejected: 'G9S-CMD-REJECTED',\n\n ConflictServerWins: 'G9S-CON-SERVER-WINS',\n ConflictColumns: 'G9S-CON-COLUMNS',\n ConflictDeleted: 'G9S-CON-DELETED',\n ConflictResolverRejected: 'G9S-CON-RESOLVER',\n\n LocalStoreError: 'G9S-LOCAL-STORE',\n /** The device database is damaged or not a database (ADR-0068). Export what is readable and rebuild from the server. */\n LocalStoreCorrupt: 'G9S-LOCAL-CORRUPT',\n /** The device ran out of storage while the store was writing. */\n LocalStoreDiskFull: 'G9S-LOCAL-DISK-FULL',\n /** Another connection held the write lock longer than the busy timeout. Retryable. */\n LocalStoreBusy: 'G9S-LOCAL-BUSY',\n\n Unknown: 'G9S-UNKNOWN',\n} as const;\n\nconst RETRYABLE: { [code: string]: true } = {\n 'G9S-NET-FAILURE': true,\n 'G9S-SRV-ERROR': true,\n 'G9S-SRV-BUSY': true,\n 'G9S-TOKEN-EXPIRED': true,\n 'G9S-RSY-EPOCH': true,\n 'G9S-RSY-HORIZON': true,\n 'G9S-RSY-AUTH': true,\n 'G9S-LOCAL-BUSY': true,\n};\n\n/** True for failures a later attempt may succeed at without any change to data or code (`G9SyncErrorCodes.IsRetryable`). */\nexport function isRetryableCode(code: string): boolean {\n return RETRYABLE[code] === true;\n}\n\n/** Options of {@link G9SyncException}. */\nexport interface G9SyncExceptionOptions {\n /** Overrides the default retryability of the code. */\n retryable?: boolean;\n /** Structured detail (JSON or text) for logs. Never shown to end users. */\n details?: string | null;\n /** The underlying failure. */\n cause?: unknown;\n}\n\n/**\n * The single error type G9SyncData throws for expected failures (twin of `G9CSyncException`). Always carries a stable\n * {@link code}; callers switch on the code, never on the message.\n */\nexport class G9SyncException extends Error {\n /** Stable error code (see {@link G9SyncErrorCodes}). */\n readonly code: string;\n /** True when retrying without changes may succeed. */\n readonly retryable: boolean;\n /** Optional structured detail (JSON or text) for logs. */\n readonly details: string | null;\n\n constructor(code: string, message: string, options?: G9SyncExceptionOptions) {\n super(message);\n this.name = 'G9SyncException';\n this.code = code;\n this.retryable = options?.retryable ?? isRetryableCode(code);\n this.details = options?.details ?? null;\n if (options && 'cause' in options) {\n Object.defineProperty(this, 'cause', { value: options.cause, enumerable: false, writable: true, configurable: true });\n }\n // ES2019 output: keep instanceof working for subclasses compiled to functions.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** True for a {@link G9SyncException} (also across bundles that each carry a copy of the class). */\nexport function isSyncException(error: unknown): error is G9SyncException {\n return (\n error instanceof G9SyncException ||\n (typeof error === 'object' &&\n error !== null &&\n (error as { name?: unknown }).name === 'G9SyncException' &&\n typeof (error as { code?: unknown }).code === 'string')\n );\n}\n"]}