@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
@@ -0,0 +1,428 @@
1
+ import type { G9EMoveOutPolicy } from '../abstractions/enums.js';
2
+ import type { G9Long } from '../abstractions/long.js';
3
+ import type { G9SyncParameters } from '../abstractions/parameters.js';
4
+ import type { G9DtBackfillRequest, G9DtCommand, G9DtHelloResult, G9DtNegotiatedTable, G9DtPullContinuation, G9DtPullPlan, G9DtPushResult, G9DtSubscriptionDefinition, G9DtSubscriptionRef, G9DtSyncChunk, G9DtTableColumns, G9DtTableDigest } from '../protocol/dtos.js';
5
+ import type { G9SyncClientModel } from './model.js';
6
+ /** What the client last learned from the server (`G9CClientServerState`). */
7
+ export interface G9ClientServerState {
8
+ epoch: string | null;
9
+ lastHelloUtcMs: number | null;
10
+ /** Server clock minus device clock, ms. */
11
+ serverClockOffsetMs: number;
12
+ tables: G9DtNegotiatedTable[];
13
+ definitions: G9DtSubscriptionDefinition[];
14
+ }
15
+ /** `G9ESubscriptionState`. */
16
+ export declare const G9ESubscriptionState: {
17
+ readonly Active: 0;
18
+ readonly NeedsReconcile: 1;
19
+ readonly Failed: 2;
20
+ };
21
+ export type G9ESubscriptionState = (typeof G9ESubscriptionState)[keyof typeof G9ESubscriptionState];
22
+ /** A local subscription (`G9CSyncSubscriptionRecord`). Times are epoch milliseconds UTC. */
23
+ export interface G9SyncSubscriptionRecord {
24
+ key: string;
25
+ name: string;
26
+ parameters: G9SyncParameters;
27
+ /** Membership slot (bit) for persistent subscriptions; the shared ephemeral slot otherwise. */
28
+ slot: number;
29
+ ephemeral: boolean;
30
+ ttlMs: number | null;
31
+ cursor: string | null;
32
+ /** The move-out policy the server last announced; kept so it still applies once access is gone. */
33
+ moveOutPolicy: G9EMoveOutPolicy | null;
34
+ definitionHash: string | null;
35
+ authHash: string | null;
36
+ state: G9ESubscriptionState;
37
+ lastErrorCode: string | null;
38
+ createdUtcMs: number;
39
+ lastSuccessUtcMs: number | null;
40
+ lastReconcileUtcMs: number | null;
41
+ lastAccessUtcMs: number | null;
42
+ }
43
+ /** `G9CSyncSubscriptionRecord.ToReference()`. */
44
+ export declare function toSubscriptionRef(record: G9SyncSubscriptionRecord): G9DtSubscriptionRef;
45
+ /** A partial update of a subscription record; absent members are left unchanged (`G9CSubscriptionUpdate`). */
46
+ export interface G9SubscriptionUpdate {
47
+ cursor?: string | null;
48
+ definitionHash?: string | null;
49
+ authHash?: string | null;
50
+ state?: G9ESubscriptionState | null;
51
+ errorCode?: string | null;
52
+ clearError?: boolean;
53
+ succeeded?: boolean;
54
+ reconciled?: boolean;
55
+ accessed?: boolean;
56
+ clearCursor?: boolean;
57
+ }
58
+ /** A push batch built from dirty rows (`G9CPushBatch`). */
59
+ export interface G9PushBatch {
60
+ batchId: number;
61
+ /** Highest local change sequence included; rows changed after it stay dirty after the acknowledgement. */
62
+ maxSequence: number;
63
+ frames: Uint8Array[];
64
+ tables: G9DtTableColumns[];
65
+ rowCount: number;
66
+ /** True when the byte budget ended the batch early: more pending rows follow. */
67
+ truncated: boolean;
68
+ /** Rows rejected on the device while building the batch (G9S-REJ-TOO-LARGE). */
69
+ rejectedLocally: number;
70
+ /** Commands carried by this batch, in device order. */
71
+ commands: G9DtCommand[];
72
+ }
73
+ /** A pull being staged, persisted across restarts (`G9CStagedPull`). */
74
+ export interface G9StagedPull {
75
+ runId: string;
76
+ plan: G9DtPullPlan;
77
+ createdUtcMs: number;
78
+ backfills: G9DtBackfillRequest[];
79
+ /** What the world looked like when the plan was made (ADR-0046); a run only resumes a stage whose generation matches. */
80
+ generation: string | null;
81
+ /** The cursor each planned subscription had when the plan was made, by subscription key (ADR-0060). */
82
+ plannedFromCursors: {
83
+ [subscriptionKey: string]: string | null;
84
+ };
85
+ /** The device clock when the stage began, assigned by the store. */
86
+ stageSequence: number;
87
+ }
88
+ /** One committed slice of a staged pull (`G9CApplySlice`). */
89
+ export interface G9ApplySlice {
90
+ report: G9ApplyReport;
91
+ rowsApplied: number;
92
+ rowsAppliedTotal: number;
93
+ rowsPlanned: number;
94
+ isFinal: boolean;
95
+ }
96
+ /** `G9CResumePoint`. */
97
+ export interface G9ResumePoint {
98
+ continuation: G9DtPullContinuation | null;
99
+ stagedRows: number;
100
+ stagedBytes: number;
101
+ complete: boolean;
102
+ }
103
+ /** Why a table large enough for index deferral kept its indexes live. */
104
+ export declare const REASON_TABLE_NOT_EMPTY = "table-not-empty";
105
+ /** How an apply ran, as opposed to what it changed (`G9CApplyDiagnostics`, ADR-0058). */
106
+ export declare class G9ApplyDiagnostics {
107
+ transactions: number;
108
+ transactionsWithTriggersSkipped: number;
109
+ readonly foreignTriggers: string[];
110
+ readonly deferredIndexes: {
111
+ [table: string]: number;
112
+ };
113
+ readonly liveIndexReasons: {
114
+ [table: string]: string;
115
+ };
116
+ readonly rowsByTable: {
117
+ [table: string]: number;
118
+ };
119
+ readonly msByTable: {
120
+ [table: string]: number;
121
+ };
122
+ indexRebuildMs: number;
123
+ elapsedMs: number;
124
+ /** True when every transaction ran with trigger execution off, false when any ran with it on, null when nothing was applied. */
125
+ get triggersSkipped(): boolean | null;
126
+ get deferredIndexCount(): number;
127
+ merge(other: G9ApplyDiagnostics): void;
128
+ /** One line for a log: what engaged, what did not and why, and the tables that took the time. */
129
+ toString(): string;
130
+ }
131
+ /** What an apply changed (`G9CApplyReport`). */
132
+ export declare class G9ApplyReport {
133
+ upserts: number;
134
+ deletes: number;
135
+ moveOuts: number;
136
+ membershipAdded: number;
137
+ /** Rows skipped because they carry a pending local change. */
138
+ skippedDirty: number;
139
+ accepted: number;
140
+ rejected: number;
141
+ conflicts: number;
142
+ commandsCompleted: number;
143
+ revokedDeleted: number;
144
+ revokedQuarantined: number;
145
+ revokedRetained: number;
146
+ /** Changed keys per local table (capped per table). */
147
+ readonly changedKeys: {
148
+ [localTable: string]: string[];
149
+ };
150
+ /** Upserts whose row already held exactly the incoming values (ADR-0058). */
151
+ unchangedRows: number;
152
+ readonly diagnostics: G9ApplyDiagnostics;
153
+ get total(): number;
154
+ merge(other: G9ApplyReport): void;
155
+ }
156
+ /** A local row as seen by reconciliation (`G9CLocalRowEntry`). */
157
+ export interface G9LocalRowEntry {
158
+ key: string;
159
+ bucket: number;
160
+ version: number;
161
+ hash: G9Long;
162
+ member: boolean;
163
+ dirty: boolean;
164
+ }
165
+ export interface G9PendingTable {
166
+ localTable: string;
167
+ upserts: number;
168
+ deletes: number;
169
+ rejected: number;
170
+ }
171
+ export interface G9PendingSummary {
172
+ totalRows: number;
173
+ rejectedRows: number;
174
+ /** Commands recorded on the device and not yet executed by the server. */
175
+ pendingCommands: number;
176
+ tables: G9PendingTable[];
177
+ }
178
+ /** One entry of the local change feed (`G9CChangeFeedEntry`). */
179
+ export interface G9ChangeFeedEntry {
180
+ sequence: number;
181
+ key: string;
182
+ deleted: boolean;
183
+ localPending: boolean;
184
+ }
185
+ export interface G9SyncRejection {
186
+ localTable: string;
187
+ key: string;
188
+ code: string;
189
+ message: string | null;
190
+ sqlState: string | null;
191
+ constraint: string | null;
192
+ column: string | null;
193
+ details: string | null;
194
+ atUtcMs: number;
195
+ }
196
+ /** A persisted summary of one run (`G9CSyncRunReport`); serialized with the .NET JSON names into g9_runs. */
197
+ export interface G9SyncRunReport {
198
+ runId: string;
199
+ startedUtcMs: number;
200
+ durationMs: number;
201
+ outcome: string;
202
+ priority: string;
203
+ errorCode: string | null;
204
+ errorMessage: string | null;
205
+ pushed: number;
206
+ rejected: number;
207
+ conflicts: number;
208
+ downloaded: number;
209
+ applied: number;
210
+ downloadedBytes: number;
211
+ subscriptions: string[];
212
+ phaseMs: {
213
+ [phase: string]: number;
214
+ };
215
+ triggersSkipped: boolean | null;
216
+ foreignTriggers: string[];
217
+ applyTransactions: number;
218
+ indexesDeferred: number;
219
+ indexRebuildMs: number;
220
+ liveIndexReasons: {
221
+ [table: string]: string;
222
+ };
223
+ unchangedRows: number;
224
+ }
225
+ /** `G9ELocalCommandState`. */
226
+ export declare const G9ELocalCommandState: {
227
+ readonly Pending: 0;
228
+ readonly Ok: 1;
229
+ readonly Rejected: 2;
230
+ readonly Superseded: 3;
231
+ };
232
+ export type G9ELocalCommandState = (typeof G9ELocalCommandState)[keyof typeof G9ELocalCommandState];
233
+ /** A command recorded on this device (`G9CLocalCommand`). */
234
+ export interface G9LocalCommand {
235
+ commandId: string;
236
+ sequence: number;
237
+ name: string;
238
+ version: number;
239
+ payload: string;
240
+ createdUtcMs: number;
241
+ state: G9ELocalCommandState;
242
+ code: string | null;
243
+ message: string | null;
244
+ result: string | null;
245
+ completedUtcMs: number | null;
246
+ }
247
+ /** What to record (`G9CCommandRequest`). */
248
+ export interface G9CommandRequest {
249
+ name: string;
250
+ version?: number;
251
+ /** JSON payload for the server's handler. */
252
+ payload: string;
253
+ }
254
+ /** A value an app binds through {@link G9LocalCommandWriter}: the SQLite driver's value forms, plus booleans. */
255
+ export type G9LocalSqlArgument = null | number | string | boolean | ArrayBuffer | Uint8Array | {
256
+ int64: string;
257
+ } | G9Long;
258
+ /**
259
+ * Writes a command's local effects in the transaction that records it (`IG9LocalCommandWriter`). Rows written here are
260
+ * not uploaded as row changes; they are protected from pulls until the server's result arrives.
261
+ */
262
+ export interface G9LocalCommandWriter {
263
+ /** Executes SQL; parameters bind positionally as `$p0`, `$p1`, … */
264
+ execute(sql: string, ...args: G9LocalSqlArgument[]): Promise<number>;
265
+ scalar(sql: string, ...args: G9LocalSqlArgument[]): Promise<unknown>;
266
+ }
267
+ /** `G9ERevocationReason`. */
268
+ export declare const G9ERevocationReason: {
269
+ readonly LeftScope: 0;
270
+ readonly Unsubscribed: 1;
271
+ readonly Expired: 2;
272
+ readonly AccessRevoked: 3;
273
+ };
274
+ export type G9ERevocationReason = (typeof G9ERevocationReason)[keyof typeof G9ERevocationReason];
275
+ /** `G9ERevocationDisposition`. */
276
+ export declare const G9ERevocationDisposition: {
277
+ readonly Delete: 0;
278
+ readonly MarkRevoked: 1;
279
+ readonly Quarantine: 2;
280
+ readonly Retain: 3;
281
+ };
282
+ export type G9ERevocationDisposition = (typeof G9ERevocationDisposition)[keyof typeof G9ERevocationDisposition];
283
+ /** A row no subscription holds any more, as a revocation rule sees it (`G9CRevokedRow`). */
284
+ export interface G9RevokedRow {
285
+ localTable: string;
286
+ key: string;
287
+ reason: G9ERevocationReason;
288
+ subscriptionKey: string | null;
289
+ hasPendingChanges: boolean;
290
+ isRejected: boolean;
291
+ /** Column values (key included) as stored locally; empty when the row no longer exists locally. */
292
+ values: {
293
+ [column: string]: unknown;
294
+ };
295
+ }
296
+ /** Lets a custom rule change the row before its disposition applies. */
297
+ export interface G9RevokedRowEditor {
298
+ set(column: string, value: unknown): void;
299
+ }
300
+ export interface G9RevocationRule {
301
+ readonly name: string;
302
+ readonly condition: (row: G9RevokedRow) => boolean;
303
+ readonly decide: (row: G9RevokedRow, editor: G9RevokedRowEditor) => G9ERevocationDisposition;
304
+ readonly retainWhile: ((row: G9RevokedRow) => boolean) | null;
305
+ }
306
+ /** Ordered revocation rules (`G9CRevocationPolicy`): the first matching rule decides. */
307
+ export declare class G9RevocationPolicy {
308
+ readonly rules: readonly G9RevocationRule[];
309
+ private constructor();
310
+ /** No rules: delete clean rows, quarantine rows with pending changes. */
311
+ static readonly default: G9RevocationPolicy;
312
+ static create(configure: (builder: G9RevocationPolicyBuilder) => void): G9RevocationPolicy;
313
+ decide(row: G9RevokedRow, editor: G9RevokedRowEditor): {
314
+ rule: G9RevocationRule | null;
315
+ disposition: G9ERevocationDisposition;
316
+ };
317
+ find(name: string): G9RevocationRule | null;
318
+ }
319
+ export declare class G9RevocationPolicyBuilder {
320
+ readonly rules: G9RevocationRule[];
321
+ when(name: string, condition: (row: G9RevokedRow) => boolean): G9RevocationActionBuilder;
322
+ /** Rows of one local table. */
323
+ forTable(name: string, localTable: string, condition?: (row: G9RevokedRow) => boolean): G9RevocationActionBuilder;
324
+ }
325
+ export declare class G9RevocationActionBuilder {
326
+ private readonly _owner;
327
+ private readonly _name;
328
+ private readonly _condition;
329
+ constructor(owner: G9RevocationPolicyBuilder, name: string, condition: (row: G9RevokedRow) => boolean);
330
+ delete(): G9RevocationPolicyBuilder;
331
+ markRevoked(): G9RevocationPolicyBuilder;
332
+ quarantine(): G9RevocationPolicyBuilder;
333
+ retainWhile(retainWhile: (row: G9RevokedRow) => boolean): G9RevocationPolicyBuilder;
334
+ custom(decide: (row: G9RevokedRow, editor: G9RevokedRowEditor) => G9ERevocationDisposition): G9RevocationPolicyBuilder;
335
+ private _add;
336
+ }
337
+ /** A row in the holding area (`G9CQuarantinedRow`). */
338
+ export interface G9QuarantinedRow {
339
+ localTable: string;
340
+ key: string;
341
+ reason: G9ERevocationReason;
342
+ rule: string | null;
343
+ hadPendingChanges: boolean;
344
+ values: {
345
+ [column: string]: unknown;
346
+ };
347
+ atUtcMs: number;
348
+ }
349
+ /** A row kept in the app's table after revocation (`G9CRetainedRow`). */
350
+ export interface G9RetainedRow {
351
+ localTable: string;
352
+ key: string;
353
+ disposition: G9ERevocationDisposition;
354
+ reason: G9ERevocationReason;
355
+ rule: string | null;
356
+ atUtcMs: number;
357
+ }
358
+ /** What went wrong inside a client store, in terms an application can act on (`G9ELocalStoreFailure`). */
359
+ export declare const G9ELocalStoreFailure: {
360
+ readonly Other: 0;
361
+ readonly Corrupt: 1;
362
+ readonly DiskFull: 2;
363
+ readonly Busy: 3;
364
+ };
365
+ export type G9ELocalStoreFailure = (typeof G9ELocalStoreFailure)[keyof typeof G9ELocalStoreFailure];
366
+ /** Implemented by a store's errors so the engine can give a run's failure a stable code (`IG9SyncLocalStoreFailure`). */
367
+ export interface G9SyncLocalStoreFailure {
368
+ readonly localStoreFailure: G9ELocalStoreFailure;
369
+ }
370
+ export declare function isLocalStoreFailure(error: unknown): error is G9SyncLocalStoreFailure;
371
+ /**
372
+ * The client storage provider (`IG9SyncClientStore`): local tables, change capture, subscription membership,
373
+ * staging, apply and metadata. Every method takes an optional AbortSignal (the CancellationToken twin).
374
+ */
375
+ export interface G9SyncClientStore {
376
+ initialize(model: G9SyncClientModel, signal?: AbortSignal): Promise<void>;
377
+ getClientId(signal?: AbortSignal): Promise<string>;
378
+ getServerState(signal?: AbortSignal): Promise<G9ClientServerState>;
379
+ saveHello(hello: G9DtHelloResult, signal?: AbortSignal): Promise<void>;
380
+ getPendingBackfills(signal?: AbortSignal): Promise<G9DtBackfillRequest[]>;
381
+ getSubscriptions(signal?: AbortSignal): Promise<G9SyncSubscriptionRecord[]>;
382
+ ensureSubscription(name: string, parameters: G9SyncParameters, ephemeral: boolean, ttlMs: number | null, adoptExistingRows: boolean, signal?: AbortSignal): Promise<G9SyncSubscriptionRecord>;
383
+ removeSubscription(key: string, policy: G9EMoveOutPolicy, signal?: AbortSignal): Promise<void>;
384
+ releaseSubscriptionRows(key: string, signal?: AbortSignal): Promise<G9ApplyReport>;
385
+ updateSubscription(key: string, update: G9SubscriptionUpdate, signal?: AbortSignal): Promise<void>;
386
+ collectExpiredEphemeral(utcNowMs: number, signal?: AbortSignal): Promise<number>;
387
+ buildPushBatch(tables: readonly G9DtNegotiatedTable[], maxRows: number, maxBytes: number, signal?: AbortSignal): Promise<G9PushBatch | null>;
388
+ forgetUploadedRows(localTable: string, keys: readonly string[] | null, signal?: AbortSignal): Promise<number>;
389
+ applyPushResult(batch: G9PushBatch, result: G9DtPushResult, signal?: AbortSignal): Promise<G9ApplyReport>;
390
+ getStagedPull(signal?: AbortSignal): Promise<G9StagedPull | null>;
391
+ /** The newest staged pull planned under exactly this generation, or null (ADR-0060). */
392
+ getStagedPullForGeneration(generation: string, signal?: AbortSignal): Promise<G9StagedPull | null>;
393
+ beginStagedPull(pull: G9StagedPull, signal?: AbortSignal): Promise<void>;
394
+ pruneStagedPulls(olderThanUtcMs: number, maxBytes: number, keepGeneration: string | null, signal?: AbortSignal): Promise<number>;
395
+ discardAllStagedPulls(signal?: AbortSignal): Promise<number>;
396
+ stageChunk(runId: string, chunk: G9DtSyncChunk, signal?: AbortSignal): Promise<void>;
397
+ getResumePoint(runId: string, signal?: AbortSignal): Promise<G9ResumePoint>;
398
+ applyStagedPull(runId: string, rowsApplied: ((rows: number) => void) | null, slices: ((slice: G9ApplySlice) => void) | null, signal?: AbortSignal): Promise<G9ApplyReport>;
399
+ discardStagedPull(runId: string, signal?: AbortSignal): Promise<void>;
400
+ computeDigest(subscriptionKey: string, table: G9DtNegotiatedTable, signal?: AbortSignal): Promise<G9DtTableDigest>;
401
+ readLocalEntries(subscriptionKey: string, table: G9DtNegotiatedTable, buckets: ReadonlySet<number>, serverKeys: ReadonlySet<string>, signal?: AbortSignal): Promise<G9LocalRowEntry[]>;
402
+ applyReconcile(subscriptionKey: string, table: G9DtNegotiatedTable, fetchedFrames: readonly Uint8Array[], addMembership: readonly string[], moveOut: readonly string[], policy: G9EMoveOutPolicy, signal?: AbortSignal): Promise<G9ApplyReport>;
403
+ getPendingSummary(signal?: AbortSignal): Promise<G9PendingSummary>;
404
+ enqueueCommand(request: G9CommandRequest, applyLocally: ((writer: G9LocalCommandWriter, signal?: AbortSignal) => Promise<void>) | null, signal?: AbortSignal): Promise<G9LocalCommand>;
405
+ getQuarantined(signal?: AbortSignal): Promise<G9QuarantinedRow[]>;
406
+ restoreQuarantined(localTable: string, key: string, signal?: AbortSignal): Promise<number>;
407
+ discardQuarantined(localTable: string | null, key: string | null, signal?: AbortSignal): Promise<number>;
408
+ getRetained(signal?: AbortSignal): Promise<G9RetainedRow[]>;
409
+ reevaluateRetained(signal?: AbortSignal): Promise<number>;
410
+ getCommands(state: G9ELocalCommandState | null, limit: number, signal?: AbortSignal): Promise<G9LocalCommand[]>;
411
+ readChangeFeed(localTable: string, afterSequence: number, limit: number, signal?: AbortSignal): Promise<G9ChangeFeedEntry[]>;
412
+ getChangeSequence(signal?: AbortSignal): Promise<number>;
413
+ getChangeFeedFloor(signal?: AbortSignal): Promise<number>;
414
+ getRejections(signal?: AbortSignal): Promise<G9SyncRejection[]>;
415
+ retryRejected(localTable: string | null, key: string | null, signal?: AbortSignal): Promise<number>;
416
+ markDirty(localTable: string, keys: readonly string[], signal?: AbortSignal): Promise<number>;
417
+ adoptExistingRows(signal?: AbortSignal): Promise<number>;
418
+ saveRunReport(report: G9SyncRunReport, keep: number, signal?: AbortSignal): Promise<void>;
419
+ getRecentRunReports(count: number, signal?: AbortSignal): Promise<G9SyncRunReport[]>;
420
+ getPhaseStatistics(signal?: AbortSignal): Promise<{
421
+ [key: string]: number;
422
+ }>;
423
+ recordPhaseStatistics(samples: {
424
+ [key: string]: number;
425
+ }, signal?: AbortSignal): Promise<void>;
426
+ purgeDeletedFeed(keep: number, signal?: AbortSignal): Promise<number>;
427
+ dispose(): Promise<void>;
428
+ }
@@ -0,0 +1,256 @@
1
+ /** `G9ESubscriptionState`. */
2
+ export const G9ESubscriptionState = {
3
+ Active: 0,
4
+ NeedsReconcile: 1,
5
+ Failed: 2,
6
+ };
7
+ /** `G9CSyncSubscriptionRecord.ToReference()`. */
8
+ export function toSubscriptionRef(record) {
9
+ return {
10
+ name: record.name,
11
+ parameters: record.parameters.values,
12
+ cursor: record.cursor,
13
+ definitionHash: record.definitionHash,
14
+ authHash: record.authHash,
15
+ };
16
+ }
17
+ // ---------------------------------------------------------------- reports
18
+ /** Why a table large enough for index deferral kept its indexes live. */
19
+ export const REASON_TABLE_NOT_EMPTY = 'table-not-empty';
20
+ const MAX_FOREIGN_TRIGGER_NAMES = 10;
21
+ /** How an apply ran, as opposed to what it changed (`G9CApplyDiagnostics`, ADR-0058). */
22
+ export class G9ApplyDiagnostics {
23
+ constructor() {
24
+ this.transactions = 0;
25
+ this.transactionsWithTriggersSkipped = 0;
26
+ this.foreignTriggers = [];
27
+ this.deferredIndexes = {};
28
+ this.liveIndexReasons = {};
29
+ this.rowsByTable = {};
30
+ this.msByTable = {};
31
+ this.indexRebuildMs = 0;
32
+ this.elapsedMs = 0;
33
+ }
34
+ /** True when every transaction ran with trigger execution off, false when any ran with it on, null when nothing was applied. */
35
+ get triggersSkipped() {
36
+ return this.transactions === 0 ? null : this.transactionsWithTriggersSkipped === this.transactions;
37
+ }
38
+ get deferredIndexCount() {
39
+ let total = 0;
40
+ for (const key of Object.keys(this.deferredIndexes))
41
+ total += this.deferredIndexes[key];
42
+ return total;
43
+ }
44
+ merge(other) {
45
+ this.transactions += other.transactions;
46
+ this.transactionsWithTriggersSkipped += other.transactionsWithTriggersSkipped;
47
+ this.indexRebuildMs += other.indexRebuildMs;
48
+ this.elapsedMs += other.elapsedMs;
49
+ for (const name of other.foreignTriggers) {
50
+ if (this.foreignTriggers.length >= MAX_FOREIGN_TRIGGER_NAMES)
51
+ break;
52
+ if (!this.foreignTriggers.some((n) => n.toLowerCase() === name.toLowerCase()))
53
+ this.foreignTriggers.push(name);
54
+ }
55
+ addAll(this.deferredIndexes, other.deferredIndexes);
56
+ for (const key of Object.keys(other.liveIndexReasons))
57
+ this.liveIndexReasons[key] = other.liveIndexReasons[key];
58
+ addAll(this.rowsByTable, other.rowsByTable);
59
+ addAll(this.msByTable, other.msByTable);
60
+ }
61
+ /** One line for a log: what engaged, what did not and why, and the tables that took the time. */
62
+ toString() {
63
+ if (this.transactions === 0)
64
+ return 'nothing applied';
65
+ let text = `${this.transactions} transaction(s) in ${this.elapsedMs} ms; triggers `;
66
+ text +=
67
+ this.triggersSkipped === true
68
+ ? 'skipped'
69
+ : this.transactionsWithTriggersSkipped === 0
70
+ ? 'LIVE'
71
+ : `skipped in ${this.transactionsWithTriggersSkipped} of ${this.transactions}`;
72
+ if (this.foreignTriggers.length > 0)
73
+ text += ' (foreign: ' + this.foreignTriggers.join(', ') + ')';
74
+ const deferred = Object.keys(this.deferredIndexes);
75
+ if (deferred.length > 0) {
76
+ text += `; ${this.deferredIndexCount} index(es) rebuilt after the load in ${this.indexRebuildMs} ms (` + deferred.map((t) => t + ':' + this.deferredIndexes[t]).join(', ') + ')';
77
+ }
78
+ const live = Object.keys(this.liveIndexReasons);
79
+ if (live.length > 0)
80
+ text += '; live indexes on ' + live.map((t) => t + ' (' + this.liveIndexReasons[t] + ')').join(', ');
81
+ const slowest = Object.keys(this.msByTable)
82
+ .sort((a, b) => this.msByTable[b] - this.msByTable[a])
83
+ .slice(0, 4);
84
+ if (slowest.length > 0) {
85
+ text +=
86
+ '; ' +
87
+ slowest
88
+ .map((t) => {
89
+ var _a;
90
+ const ms = this.msByTable[t];
91
+ const rows = (_a = this.rowsByTable[t]) !== null && _a !== void 0 ? _a : 0;
92
+ const rate = ms > 0 ? Math.floor((rows * 1000) / ms) : rows;
93
+ return `${t} ${rows} rows in ${ms} ms (${rate}/s)`;
94
+ })
95
+ .join(', ');
96
+ }
97
+ return text;
98
+ }
99
+ }
100
+ function addAll(target, source) {
101
+ var _a;
102
+ for (const key of Object.keys(source))
103
+ target[key] = ((_a = target[key]) !== null && _a !== void 0 ? _a : 0) + source[key];
104
+ }
105
+ /** What an apply changed (`G9CApplyReport`). */
106
+ export class G9ApplyReport {
107
+ constructor() {
108
+ this.upserts = 0;
109
+ this.deletes = 0;
110
+ this.moveOuts = 0;
111
+ this.membershipAdded = 0;
112
+ /** Rows skipped because they carry a pending local change. */
113
+ this.skippedDirty = 0;
114
+ this.accepted = 0;
115
+ this.rejected = 0;
116
+ this.conflicts = 0;
117
+ this.commandsCompleted = 0;
118
+ this.revokedDeleted = 0;
119
+ this.revokedQuarantined = 0;
120
+ this.revokedRetained = 0;
121
+ /** Changed keys per local table (capped per table). */
122
+ this.changedKeys = {};
123
+ /** Upserts whose row already held exactly the incoming values (ADR-0058). */
124
+ this.unchangedRows = 0;
125
+ this.diagnostics = new G9ApplyDiagnostics();
126
+ }
127
+ get total() {
128
+ return this.upserts + this.deletes + this.moveOuts;
129
+ }
130
+ merge(other) {
131
+ var _a;
132
+ this.unchangedRows += other.unchangedRows;
133
+ this.diagnostics.merge(other.diagnostics);
134
+ this.upserts += other.upserts;
135
+ this.deletes += other.deletes;
136
+ this.moveOuts += other.moveOuts;
137
+ this.membershipAdded += other.membershipAdded;
138
+ this.skippedDirty += other.skippedDirty;
139
+ this.accepted += other.accepted;
140
+ this.rejected += other.rejected;
141
+ this.conflicts += other.conflicts;
142
+ this.commandsCompleted += other.commandsCompleted;
143
+ this.revokedDeleted += other.revokedDeleted;
144
+ this.revokedQuarantined += other.revokedQuarantined;
145
+ this.revokedRetained += other.revokedRetained;
146
+ for (const table of Object.keys(other.changedKeys)) {
147
+ const list = (_a = this.changedKeys[table]) !== null && _a !== void 0 ? _a : (this.changedKeys[table] = []);
148
+ for (const key of other.changedKeys[table])
149
+ list.push(key);
150
+ }
151
+ }
152
+ }
153
+ // ---------------------------------------------------------------- local commands (ADR-0025)
154
+ /** `G9ELocalCommandState`. */
155
+ export const G9ELocalCommandState = {
156
+ Pending: 0,
157
+ Ok: 1,
158
+ Rejected: 2,
159
+ Superseded: 3,
160
+ };
161
+ // ---------------------------------------------------------------- revocation (ADR-0026)
162
+ /** `G9ERevocationReason`. */
163
+ export const G9ERevocationReason = {
164
+ LeftScope: 0,
165
+ Unsubscribed: 1,
166
+ Expired: 2,
167
+ AccessRevoked: 3,
168
+ };
169
+ /** `G9ERevocationDisposition`. */
170
+ export const G9ERevocationDisposition = {
171
+ Delete: 0,
172
+ MarkRevoked: 1,
173
+ Quarantine: 2,
174
+ Retain: 3,
175
+ };
176
+ /** Ordered revocation rules (`G9CRevocationPolicy`): the first matching rule decides. */
177
+ export class G9RevocationPolicy {
178
+ constructor(rules) {
179
+ this.rules = rules;
180
+ }
181
+ static create(configure) {
182
+ const builder = new G9RevocationPolicyBuilder();
183
+ configure(builder);
184
+ return new G9RevocationPolicy(builder.rules.slice());
185
+ }
186
+ decide(row, editor) {
187
+ for (const rule of this.rules) {
188
+ if (rule.condition(row))
189
+ return { rule, disposition: rule.decide(row, editor) };
190
+ }
191
+ return { rule: null, disposition: row.hasPendingChanges ? G9ERevocationDisposition.Quarantine : G9ERevocationDisposition.Delete };
192
+ }
193
+ find(name) {
194
+ for (const rule of this.rules)
195
+ if (rule.name === name)
196
+ return rule;
197
+ return null;
198
+ }
199
+ }
200
+ /** No rules: delete clean rows, quarantine rows with pending changes. */
201
+ G9RevocationPolicy.default = new G9RevocationPolicy([]);
202
+ export class G9RevocationPolicyBuilder {
203
+ constructor() {
204
+ this.rules = [];
205
+ }
206
+ when(name, condition) {
207
+ if (typeof name !== 'string' || name.trim() === '')
208
+ throw new TypeError('A rule needs a name.');
209
+ if (this.rules.some((r) => r.name === name))
210
+ throw new RangeError(`Revocation rule '${name}' is declared twice.`);
211
+ return new G9RevocationActionBuilder(this, name, condition);
212
+ }
213
+ /** Rows of one local table. */
214
+ forTable(name, localTable, condition) {
215
+ const table = localTable.toLowerCase();
216
+ return this.when(name, (row) => row.localTable.toLowerCase() === table && (condition ? condition(row) : true));
217
+ }
218
+ }
219
+ export class G9RevocationActionBuilder {
220
+ constructor(owner, name, condition) {
221
+ this._owner = owner;
222
+ this._name = name;
223
+ this._condition = condition;
224
+ }
225
+ delete() {
226
+ return this._add(() => G9ERevocationDisposition.Delete, null);
227
+ }
228
+ markRevoked() {
229
+ return this._add(() => G9ERevocationDisposition.MarkRevoked, null);
230
+ }
231
+ quarantine() {
232
+ return this._add(() => G9ERevocationDisposition.Quarantine, null);
233
+ }
234
+ retainWhile(retainWhile) {
235
+ return this._add(() => G9ERevocationDisposition.Retain, retainWhile);
236
+ }
237
+ custom(decide) {
238
+ return this._add(decide, null);
239
+ }
240
+ _add(decide, retainWhile) {
241
+ this._owner.rules.push({ name: this._name, condition: this._condition, decide, retainWhile });
242
+ return this._owner;
243
+ }
244
+ }
245
+ // ---------------------------------------------------------------- failures
246
+ /** What went wrong inside a client store, in terms an application can act on (`G9ELocalStoreFailure`). */
247
+ export const G9ELocalStoreFailure = {
248
+ Other: 0,
249
+ Corrupt: 1,
250
+ DiskFull: 2,
251
+ Busy: 3,
252
+ };
253
+ export function isLocalStoreFailure(error) {
254
+ return typeof error === 'object' && error !== null && typeof error.localStoreFailure === 'number';
255
+ }
256
+ //# sourceMappingURL=storeTypes.js.map