@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,251 @@
1
+ // Twins of G9SyncData.Client/Progress/G9DtSyncProgress.cs and G9CSyncProgressTracker.cs
2
+ // (AiGuides/09-Progress-Model.md): monotonic progress from phase transitions and row/byte counts.
3
+ /** Phases of a sync run, in execution order (`G9ESyncPhase`). */
4
+ export const G9ESyncPhase = {
5
+ Queued: 0,
6
+ Connecting: 1,
7
+ Pushing: 2,
8
+ Reconciling: 3,
9
+ Planning: 4,
10
+ Downloading: 5,
11
+ Applying: 6,
12
+ PostStep: 7,
13
+ Completed: 8,
14
+ Canceled: 9,
15
+ Failed: 10,
16
+ };
17
+ const PHASE_NAMES = ['Queued', 'Connecting', 'Pushing', 'Reconciling', 'Planning', 'Downloading', 'Applying', 'PostStep', 'Completed', 'Canceled', 'Failed'];
18
+ /** The .NET enum member name of a phase (the statistics and PhaseMs keys), e.g. `Downloading`. */
19
+ export function phaseName(phase) {
20
+ var _a;
21
+ return (_a = PHASE_NAMES[phase]) !== null && _a !== void 0 ? _a : String(phase);
22
+ }
23
+ /**
24
+ * Turns phase transitions and counts into monotonic progress. Each phase entered gets a span of the REMAINING bar in
25
+ * proportion to its expected share of the remaining work, so monotonicity holds by construction; a phase without a
26
+ * real signal creeps toward 90% of its span along an exponential curve scaled by its expected duration.
27
+ */
28
+ export class G9SyncProgressTracker {
29
+ constructor(runId, publish, queuedBehind, maxHz) {
30
+ this._plan = [];
31
+ this._durations = {};
32
+ this._runStart = Date.now();
33
+ this._phaseStart = Date.now();
34
+ this._timer = null;
35
+ this._phase = G9ESyncPhase.Queued;
36
+ this._postStep = null;
37
+ this._table = null;
38
+ this._subscription = null;
39
+ this._base = 0;
40
+ this._span = 0;
41
+ this._overall = 0;
42
+ this._fraction = 0;
43
+ this._phaseExpectedMs = 0;
44
+ this._rowsDone = 0;
45
+ this._rowsTotal = 0;
46
+ this._bytesDone = 0;
47
+ this._bytesTotal = 0;
48
+ this._lastPublish = 0;
49
+ this._realSignal = false;
50
+ this._terminal = false;
51
+ this._cancelPending = false;
52
+ this._disposed = false;
53
+ this._lastPublished = null;
54
+ this._runId = runId;
55
+ this._publish = publish;
56
+ this._queuedBehind = queuedBehind;
57
+ this._minIntervalMs = maxHz <= 0 ? 0 : 1000 / maxHz;
58
+ }
59
+ get overall() {
60
+ return this._overall;
61
+ }
62
+ /** Measured milliseconds per phase (post steps keyed `PostStep:name`). */
63
+ get durations() {
64
+ const out = {};
65
+ for (const key of Object.keys(this._durations))
66
+ out[key] = this._durations[key];
67
+ return out;
68
+ }
69
+ /** Appends a phase to the plan with its expected duration. */
70
+ plan(phase, expectedMs, name = null) {
71
+ if (!this._terminal)
72
+ this._plan.push({ phase, name, expectedMs: Math.max(0, expectedMs) });
73
+ }
74
+ /** Updates the expected duration of a planned phase that has not started yet. */
75
+ expect(phase, expectedMs, name = null) {
76
+ for (const entry of this._plan) {
77
+ if (entry.phase === phase && entry.name === name) {
78
+ entry.expectedMs = Math.max(0, expectedMs);
79
+ return;
80
+ }
81
+ }
82
+ }
83
+ enter(phase, rowsTotal = 0, bytesTotal = 0, name = null) {
84
+ if (this._terminal || this._disposed)
85
+ return;
86
+ this._close();
87
+ let index = -1;
88
+ for (let i = 0; i < this._plan.length; i++) {
89
+ const entry = this._plan[i];
90
+ if (entry.phase === phase && entry.name === name) {
91
+ index = i;
92
+ break;
93
+ }
94
+ }
95
+ let expected = 500;
96
+ if (index >= 0) {
97
+ expected = this._plan[index].expectedMs;
98
+ this._plan.splice(0, index + 1);
99
+ }
100
+ expected = Math.max(1, expected);
101
+ let remaining = 0;
102
+ for (const entry of this._plan)
103
+ remaining += entry.expectedMs;
104
+ this._base = this._overall;
105
+ this._span = ((1 - this._base) * expected) / (expected + remaining);
106
+ this._phase = phase;
107
+ this._postStep = name;
108
+ this._rowsDone = 0;
109
+ this._rowsTotal = Math.max(0, rowsTotal);
110
+ this._bytesDone = 0;
111
+ this._bytesTotal = Math.max(0, bytesTotal);
112
+ this._fraction = 0;
113
+ this._realSignal = false;
114
+ this._phaseExpectedMs = expected;
115
+ this._phaseStart = Date.now();
116
+ if (this._timer === null)
117
+ this._timer = setInterval(() => this._creep(), 200);
118
+ this._emit(this._sample(true));
119
+ }
120
+ /** Reports absolute counts for the current phase. */
121
+ report(rowsDone, bytesDone = -1, table = null, subscriptionKey = null) {
122
+ if (this._terminal || this._disposed)
123
+ return;
124
+ this._rowsDone = Math.max(0, rowsDone);
125
+ if (bytesDone >= 0)
126
+ this._bytesDone = bytesDone;
127
+ if (table !== null)
128
+ this._table = table;
129
+ if (subscriptionKey !== null)
130
+ this._subscription = subscriptionKey;
131
+ const fraction = this._rowsTotal > 0 ? this._rowsDone / this._rowsTotal : this._bytesTotal > 0 ? this._bytesDone / this._bytesTotal : this._fraction;
132
+ this._realSignal = this._rowsTotal > 0 || this._bytesTotal > 0;
133
+ this._advance(fraction);
134
+ this._emit(this._sample(false));
135
+ }
136
+ /** Reports a 0..1 fraction of the current phase (post steps). */
137
+ reportFraction(fraction) {
138
+ if (this._terminal || this._disposed)
139
+ return;
140
+ this._realSignal = true;
141
+ this._advance(fraction);
142
+ this._emit(this._sample(false));
143
+ }
144
+ setCancelPending() {
145
+ if (this._terminal || this._disposed)
146
+ return;
147
+ this._cancelPending = true;
148
+ this._emit(this._sample(true));
149
+ }
150
+ complete(terminal) {
151
+ if (this._terminal || this._disposed)
152
+ return;
153
+ this._close();
154
+ this._terminal = true;
155
+ this._stopTimer();
156
+ if (terminal === G9ESyncPhase.Completed) {
157
+ this._overall = 1;
158
+ this._fraction = 1;
159
+ }
160
+ this._phase = terminal;
161
+ this._emit(this._sample(true));
162
+ }
163
+ dispose() {
164
+ if (this._disposed)
165
+ return;
166
+ this._disposed = true;
167
+ this._stopTimer();
168
+ }
169
+ _stopTimer() {
170
+ if (this._timer !== null) {
171
+ clearInterval(this._timer);
172
+ this._timer = null;
173
+ }
174
+ }
175
+ _creep() {
176
+ if (this._terminal || this._disposed || this._realSignal || this._phase === G9ESyncPhase.Queued)
177
+ return;
178
+ const elapsed = Date.now() - this._phaseStart;
179
+ this._advance(0.9 * (1 - Math.exp(-elapsed / Math.max(100, this._phaseExpectedMs))));
180
+ this._emit(this._sample(false));
181
+ }
182
+ _advance(fraction) {
183
+ const clamped = Math.min(Math.max(fraction, 0), 0.995);
184
+ if (clamped > this._fraction)
185
+ this._fraction = clamped;
186
+ const overall = Math.min(0.999, this._base + this._span * this._fraction);
187
+ if (overall > this._overall)
188
+ this._overall = overall;
189
+ }
190
+ _close() {
191
+ var _a, _b;
192
+ const phase = this._phase;
193
+ if (phase === G9ESyncPhase.Queued || phase === G9ESyncPhase.Completed || phase === G9ESyncPhase.Canceled || phase === G9ESyncPhase.Failed)
194
+ return;
195
+ const key = phase === G9ESyncPhase.PostStep ? 'PostStep:' + ((_a = this._postStep) !== null && _a !== void 0 ? _a : '') : phaseName(phase);
196
+ this._durations[key] = ((_b = this._durations[key]) !== null && _b !== void 0 ? _b : 0) + (Date.now() - this._phaseStart);
197
+ this._overall = Math.max(this._overall, Math.min(0.999, this._base + this._span));
198
+ }
199
+ _sample(force) {
200
+ const now = Date.now();
201
+ if (!force && now - this._lastPublish < this._minIntervalMs)
202
+ return null;
203
+ this._lastPublish = now;
204
+ const phaseSeconds = Math.max(0.001, (now - this._phaseStart) / 1000);
205
+ let eta = null;
206
+ if (this._terminal)
207
+ eta = 0;
208
+ else if (this._overall > 0.02)
209
+ eta = ((now - this._runStart) * (1 - this._overall)) / this._overall;
210
+ let queued = 0;
211
+ try {
212
+ queued = this._queuedBehind();
213
+ }
214
+ catch {
215
+ queued = 0;
216
+ }
217
+ return {
218
+ runId: this._runId,
219
+ phase: this._phase,
220
+ overall: this._overall,
221
+ phaseFraction: this._fraction,
222
+ rowsDone: this._rowsDone,
223
+ rowsTotal: this._rowsTotal,
224
+ bytesDone: this._bytesDone,
225
+ bytesTotal: this._bytesTotal,
226
+ subscriptionKey: this._subscription,
227
+ table: this._table,
228
+ rowsPerSecond: this._rowsDone / phaseSeconds,
229
+ etaMs: eta,
230
+ queuedBehind: queued,
231
+ postStepName: this._postStep,
232
+ cancelPending: this._cancelPending,
233
+ };
234
+ }
235
+ /** Publishes a sample unless it would go backwards (lower overall or an earlier phase). */
236
+ _emit(sample) {
237
+ if (sample === null)
238
+ return;
239
+ const last = this._lastPublished;
240
+ if (last !== null && (sample.overall < last.overall || sample.phase < last.phase))
241
+ return;
242
+ this._lastPublished = sample;
243
+ try {
244
+ this._publish(sample);
245
+ }
246
+ catch {
247
+ // A progress consumer must never break the sync.
248
+ }
249
+ }
250
+ }
251
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/client/progress.ts"],"names":[],"mappings":"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","sourcesContent":["// Twins of G9SyncData.Client/Progress/G9DtSyncProgress.cs and G9CSyncProgressTracker.cs\n// (AiGuides/09-Progress-Model.md): monotonic progress from phase transitions and row/byte counts.\n\n/** Phases of a sync run, in execution order (`G9ESyncPhase`). */\nexport const G9ESyncPhase = {\n Queued: 0,\n Connecting: 1,\n Pushing: 2,\n Reconciling: 3,\n Planning: 4,\n Downloading: 5,\n Applying: 6,\n PostStep: 7,\n Completed: 8,\n Canceled: 9,\n Failed: 10,\n} as const;\nexport type G9ESyncPhase = (typeof G9ESyncPhase)[keyof typeof G9ESyncPhase];\n\nconst PHASE_NAMES = ['Queued', 'Connecting', 'Pushing', 'Reconciling', 'Planning', 'Downloading', 'Applying', 'PostStep', 'Completed', 'Canceled', 'Failed'];\n\n/** The .NET enum member name of a phase (the statistics and PhaseMs keys), e.g. `Downloading`. */\nexport function phaseName(phase: number): string {\n return PHASE_NAMES[phase] ?? String(phase);\n}\n\n/**\n * One progress sample. `overall` is monotonic within a run and reaches 1 only when the run completed. The library emits\n * numbers and phases; localising them is the app's job.\n */\nexport interface G9DtSyncProgress {\n runId: string;\n phase: G9ESyncPhase;\n /** 0..1, monotonic. */\n overall: number;\n /** 0..1 inside the phase. */\n phaseFraction: number;\n rowsDone: number;\n rowsTotal: number;\n bytesDone: number;\n bytesTotal: number;\n subscriptionKey: string | null;\n table: string | null;\n rowsPerSecond: number;\n /** Estimated milliseconds to completion of the run, or null while unknown. */\n etaMs: number | null;\n queuedBehind: number;\n postStepName: string | null;\n cancelPending: boolean;\n}\n\ninterface PlanEntry {\n phase: G9ESyncPhase;\n name: string | null;\n expectedMs: number;\n}\n\n/**\n * Turns phase transitions and counts into monotonic progress. Each phase entered gets a span of the REMAINING bar in\n * proportion to its expected share of the remaining work, so monotonicity holds by construction; a phase without a\n * real signal creeps toward 90% of its span along an exponential curve scaled by its expected duration.\n */\nexport class G9SyncProgressTracker {\n private readonly _runId: string;\n private readonly _publish: (progress: G9DtSyncProgress) => void;\n private readonly _queuedBehind: () => number;\n private readonly _minIntervalMs: number;\n private readonly _plan: PlanEntry[] = [];\n private readonly _durations: { [key: string]: number } = {};\n private readonly _runStart = Date.now();\n private _phaseStart = Date.now();\n private _timer: ReturnType<typeof setInterval> | null = null;\n\n private _phase: G9ESyncPhase = G9ESyncPhase.Queued;\n private _postStep: string | null = null;\n private _table: string | null = null;\n private _subscription: string | null = null;\n private _base = 0;\n private _span = 0;\n private _overall = 0;\n private _fraction = 0;\n private _phaseExpectedMs = 0;\n private _rowsDone = 0;\n private _rowsTotal = 0;\n private _bytesDone = 0;\n private _bytesTotal = 0;\n private _lastPublish = 0;\n private _realSignal = false;\n private _terminal = false;\n private _cancelPending = false;\n private _disposed = false;\n private _lastPublished: G9DtSyncProgress | null = null;\n\n constructor(runId: string, publish: (progress: G9DtSyncProgress) => void, queuedBehind: () => number, maxHz: number) {\n this._runId = runId;\n this._publish = publish;\n this._queuedBehind = queuedBehind;\n this._minIntervalMs = maxHz <= 0 ? 0 : 1000 / maxHz;\n }\n\n get overall(): number {\n return this._overall;\n }\n\n /** Measured milliseconds per phase (post steps keyed `PostStep:name`). */\n get durations(): { [key: string]: number } {\n const out: { [key: string]: number } = {};\n for (const key of Object.keys(this._durations)) out[key] = this._durations[key] as number;\n return out;\n }\n\n /** Appends a phase to the plan with its expected duration. */\n plan(phase: G9ESyncPhase, expectedMs: number, name: string | null = null): void {\n if (!this._terminal) this._plan.push({ phase, name, expectedMs: Math.max(0, expectedMs) });\n }\n\n /** Updates the expected duration of a planned phase that has not started yet. */\n expect(phase: G9ESyncPhase, expectedMs: number, name: string | null = null): void {\n for (const entry of this._plan) {\n if (entry.phase === phase && entry.name === name) {\n entry.expectedMs = Math.max(0, expectedMs);\n return;\n }\n }\n }\n\n enter(phase: G9ESyncPhase, rowsTotal = 0, bytesTotal = 0, name: string | null = null): void {\n if (this._terminal || this._disposed) return;\n this._close();\n let index = -1;\n for (let i = 0; i < this._plan.length; i++) {\n const entry = this._plan[i] as PlanEntry;\n if (entry.phase === phase && entry.name === name) {\n index = i;\n break;\n }\n }\n let expected = 500;\n if (index >= 0) {\n expected = (this._plan[index] as PlanEntry).expectedMs;\n this._plan.splice(0, index + 1);\n }\n expected = Math.max(1, expected);\n let remaining = 0;\n for (const entry of this._plan) remaining += entry.expectedMs;\n this._base = this._overall;\n this._span = ((1 - this._base) * expected) / (expected + remaining);\n this._phase = phase;\n this._postStep = name;\n this._rowsDone = 0;\n this._rowsTotal = Math.max(0, rowsTotal);\n this._bytesDone = 0;\n this._bytesTotal = Math.max(0, bytesTotal);\n this._fraction = 0;\n this._realSignal = false;\n this._phaseExpectedMs = expected;\n this._phaseStart = Date.now();\n if (this._timer === null) this._timer = setInterval(() => this._creep(), 200);\n this._emit(this._sample(true));\n }\n\n /** Reports absolute counts for the current phase. */\n report(rowsDone: number, bytesDone = -1, table: string | null = null, subscriptionKey: string | null = null): void {\n if (this._terminal || this._disposed) return;\n this._rowsDone = Math.max(0, rowsDone);\n if (bytesDone >= 0) this._bytesDone = bytesDone;\n if (table !== null) this._table = table;\n if (subscriptionKey !== null) this._subscription = subscriptionKey;\n const fraction = this._rowsTotal > 0 ? this._rowsDone / this._rowsTotal : this._bytesTotal > 0 ? this._bytesDone / this._bytesTotal : this._fraction;\n this._realSignal = this._rowsTotal > 0 || this._bytesTotal > 0;\n this._advance(fraction);\n this._emit(this._sample(false));\n }\n\n /** Reports a 0..1 fraction of the current phase (post steps). */\n reportFraction(fraction: number): void {\n if (this._terminal || this._disposed) return;\n this._realSignal = true;\n this._advance(fraction);\n this._emit(this._sample(false));\n }\n\n setCancelPending(): void {\n if (this._terminal || this._disposed) return;\n this._cancelPending = true;\n this._emit(this._sample(true));\n }\n\n complete(terminal: G9ESyncPhase): void {\n if (this._terminal || this._disposed) return;\n this._close();\n this._terminal = true;\n this._stopTimer();\n if (terminal === G9ESyncPhase.Completed) {\n this._overall = 1;\n this._fraction = 1;\n }\n this._phase = terminal;\n this._emit(this._sample(true));\n }\n\n dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n this._stopTimer();\n }\n\n private _stopTimer(): void {\n if (this._timer !== null) {\n clearInterval(this._timer);\n this._timer = null;\n }\n }\n\n private _creep(): void {\n if (this._terminal || this._disposed || this._realSignal || this._phase === G9ESyncPhase.Queued) return;\n const elapsed = Date.now() - this._phaseStart;\n this._advance(0.9 * (1 - Math.exp(-elapsed / Math.max(100, this._phaseExpectedMs))));\n this._emit(this._sample(false));\n }\n\n private _advance(fraction: number): void {\n const clamped = Math.min(Math.max(fraction, 0), 0.995);\n if (clamped > this._fraction) this._fraction = clamped;\n const overall = Math.min(0.999, this._base + this._span * this._fraction);\n if (overall > this._overall) this._overall = overall;\n }\n\n private _close(): void {\n const phase = this._phase;\n if (phase === G9ESyncPhase.Queued || phase === G9ESyncPhase.Completed || phase === G9ESyncPhase.Canceled || phase === G9ESyncPhase.Failed) return;\n const key = phase === G9ESyncPhase.PostStep ? 'PostStep:' + (this._postStep ?? '') : phaseName(phase);\n this._durations[key] = (this._durations[key] ?? 0) + (Date.now() - this._phaseStart);\n this._overall = Math.max(this._overall, Math.min(0.999, this._base + this._span));\n }\n\n private _sample(force: boolean): G9DtSyncProgress | null {\n const now = Date.now();\n if (!force && now - this._lastPublish < this._minIntervalMs) return null;\n this._lastPublish = now;\n const phaseSeconds = Math.max(0.001, (now - this._phaseStart) / 1000);\n let eta: number | null = null;\n if (this._terminal) eta = 0;\n else if (this._overall > 0.02) eta = ((now - this._runStart) * (1 - this._overall)) / this._overall;\n let queued = 0;\n try {\n queued = this._queuedBehind();\n } catch {\n queued = 0;\n }\n return {\n runId: this._runId,\n phase: this._phase,\n overall: this._overall,\n phaseFraction: this._fraction,\n rowsDone: this._rowsDone,\n rowsTotal: this._rowsTotal,\n bytesDone: this._bytesDone,\n bytesTotal: this._bytesTotal,\n subscriptionKey: this._subscription,\n table: this._table,\n rowsPerSecond: this._rowsDone / phaseSeconds,\n etaMs: eta,\n queuedBehind: queued,\n postStepName: this._postStep,\n cancelPending: this._cancelPending,\n };\n }\n\n /** Publishes a sample unless it would go backwards (lower overall or an earlier phase). */\n private _emit(sample: G9DtSyncProgress | null): void {\n if (sample === null) return;\n const last = this._lastPublished;\n if (last !== null && (sample.overall < last.overall || sample.phase < last.phase)) return;\n this._lastPublished = sample;\n try {\n this._publish(sample);\n } catch {\n // A progress consumer must never break the sync.\n }\n }\n}\n"]}
@@ -0,0 +1,219 @@
1
+ import type { G9ESyncColumnType } from '../abstractions/enums.js';
2
+ import { G9SyncException } from '../abstractions/errors.js';
3
+ import type { G9DtSubscriptionState } from '../protocol/dtos.js';
4
+ import { type G9AbortController } from '../internal/abort.js';
5
+ import type { G9DtSyncProgress, G9ESyncPhase } from './progress.js';
6
+ import { G9ApplyReport, type G9ApplySlice, type G9PendingSummary, type G9SyncRunReport, type G9SyncSubscriptionRecord } from './storeTypes.js';
7
+ /** Scheduling priority (`G9ESyncPriority`). A higher priority pre-empts a running background run. */
8
+ export declare const G9ESyncPriority: {
9
+ readonly Background: 0;
10
+ readonly Foreground: 1;
11
+ readonly UserInteractive: 2;
12
+ };
13
+ export type G9ESyncPriority = (typeof G9ESyncPriority)[keyof typeof G9ESyncPriority];
14
+ export declare function priorityName(priority: number): string;
15
+ /** `G9ESyncOutcome`. */
16
+ export declare const G9ESyncOutcome: {
17
+ readonly Succeeded: 0;
18
+ /** Data moved, but some rows were rejected or some subscriptions reported problems. */
19
+ readonly CompletedWithRejections: 1;
20
+ readonly Failed: 2;
21
+ readonly Canceled: 3;
22
+ };
23
+ export type G9ESyncOutcome = (typeof G9ESyncOutcome)[keyof typeof G9ESyncOutcome];
24
+ export declare function outcomeName(outcome: number): string;
25
+ /** Context handed to an app-owned post step (`G9CSyncPostStepContext`). */
26
+ export interface G9SyncPostStepContext {
27
+ run: G9SyncRun;
28
+ /** Everything this run changed so far. */
29
+ changes: G9ApplyReport;
30
+ /** Report 0..1 of the step; mapped into the step's slice of the bar. */
31
+ progress: (fraction: number) => void;
32
+ }
33
+ /** An app-owned step that runs after the data applied and owns a slice of the progress bar (`G9CSyncPostStep`). */
34
+ export declare class G9SyncPostStep {
35
+ readonly name: string;
36
+ readonly weightHint: number;
37
+ readonly action: (context: G9SyncPostStepContext, signal: AbortSignal) => Promise<void>;
38
+ constructor(name: string, weightHint: number, action: (context: G9SyncPostStepContext, signal: AbortSignal) => Promise<void>);
39
+ }
40
+ /** A sync request (`G9CSyncRequest`). */
41
+ export interface G9SyncRequest {
42
+ /** Subscription keys or names to pull; omitted = every local subscription (background runs skip heavy ones). */
43
+ subscriptions?: readonly string[] | null;
44
+ push?: boolean;
45
+ pull?: boolean;
46
+ /** Reconcile the selected subscriptions even when nothing asks for it. */
47
+ forceReconcile?: boolean;
48
+ priority?: G9ESyncPriority;
49
+ postSteps?: readonly G9SyncPostStep[];
50
+ progress?: ((progress: G9DtSyncProgress) => void) | null;
51
+ signal?: AbortSignal | null;
52
+ }
53
+ /** The request with its defaults filled in. */
54
+ export interface G9ResolvedSyncRequest {
55
+ subscriptions: readonly string[] | null;
56
+ push: boolean;
57
+ pull: boolean;
58
+ forceReconcile: boolean;
59
+ priority: G9ESyncPriority;
60
+ postSteps: readonly G9SyncPostStep[];
61
+ progress: ((progress: G9DtSyncProgress) => void) | null;
62
+ signal: AbortSignal | null;
63
+ }
64
+ export declare function resolveRequest(request: G9SyncRequest | null | undefined): G9ResolvedSyncRequest;
65
+ /** `G9CSyncRequest.CanCoalesce`. */
66
+ export declare function canCoalesce(request: G9ResolvedSyncRequest): boolean;
67
+ /** `G9CSyncRequest.Signature`. */
68
+ export declare function requestSignature(request: G9ResolvedSyncRequest): string;
69
+ /** One defect of the client model, as the server sees it (`G9CSyncModelProblem`). */
70
+ export interface G9SyncModelProblem {
71
+ code: string;
72
+ table: string;
73
+ column: string;
74
+ clientType: G9ESyncColumnType;
75
+ serverType: G9ESyncColumnType;
76
+ }
77
+ export declare function describeModelProblem(problem: G9SyncModelProblem): string;
78
+ /** The result of a run (`G9CSyncRunResult`). */
79
+ export interface G9SyncRunResult {
80
+ runId: string;
81
+ outcome: G9ESyncOutcome;
82
+ error: G9SyncException | null;
83
+ pushed: number;
84
+ rejected: number;
85
+ conflicts: number;
86
+ downloaded: number;
87
+ applied: number;
88
+ durationMs: number;
89
+ /** Milliseconds spent in each phase of this run, keyed by phase name. */
90
+ phaseMs: {
91
+ [phase: string]: number;
92
+ };
93
+ subscriptions: string[];
94
+ /** Subscriptions that could not sync, with the server's reason. */
95
+ problems: G9DtSubscriptionState[];
96
+ /** Defects of the client model the server reported in this run's hello (ADR-0061). */
97
+ modelProblems: G9SyncModelProblem[];
98
+ changes: G9ApplyReport;
99
+ isSuccess: boolean;
100
+ }
101
+ export declare function makeRunResult(fields: Partial<G9SyncRunResult> & {
102
+ runId: string;
103
+ outcome: G9ESyncOutcome;
104
+ }): G9SyncRunResult;
105
+ /** A queued or running sync (`G9CSyncRun`). Await {@link completion}; read progress with {@link onProgress} or {@link progress}. */
106
+ export declare class G9SyncRun {
107
+ readonly runId: string;
108
+ readonly request: G9ResolvedSyncRequest;
109
+ readonly completion: Promise<G9SyncRunResult>;
110
+ latestProgress: G9DtSyncProgress | null;
111
+ isStarted: boolean;
112
+ /** @internal */
113
+ readonly cancellation: G9AbortController;
114
+ /** @internal */
115
+ preempted: boolean;
116
+ private _resolve;
117
+ private readonly _listeners;
118
+ private readonly _buffer;
119
+ private readonly _waiters;
120
+ private _done;
121
+ private _unlink;
122
+ constructor(request: G9ResolvedSyncRequest);
123
+ /** Requests cancellation. Network phases stop at once; an apply in progress finishes atomically first. */
124
+ cancel(): void;
125
+ /** Subscribes to progress samples; returns the unsubscribe function. */
126
+ onProgress(listener: (progress: G9DtSyncProgress) => void): () => void;
127
+ /** The progress stream (bounded: the oldest samples are dropped when a reader falls 64 behind), ending with the run. */
128
+ progress(): AsyncIterable<G9DtSyncProgress>;
129
+ /** @internal */
130
+ publish(progress: G9DtSyncProgress): void;
131
+ /** @internal */
132
+ complete(result: G9SyncRunResult): void;
133
+ }
134
+ /** `G9CSubscriptionOptions`. */
135
+ export interface G9SubscriptionOptions {
136
+ /** Overrides the server definition's ephemeral flag. */
137
+ ephemeral?: boolean;
138
+ ttlMs?: number;
139
+ /** The rows already exist locally: reconcile instead of downloading a snapshot. Call adoptExistingRows first. */
140
+ adoptExistingRows?: boolean;
141
+ }
142
+ /** Engine options (`G9CSyncEngineOptions`). Durations are milliseconds. */
143
+ export interface G9SyncEngineOptions {
144
+ /** Sent at Hello; the server's connect gates may refuse old versions. */
145
+ appVersion?: string | null;
146
+ pushBatchRows?: number;
147
+ /** Transient failures retried inside one run. */
148
+ maxRetries?: number;
149
+ retryBaseDelayMs?: number;
150
+ /** Queue a background pull when the server pokes about changed tables. */
151
+ pullOnChangesAvailable?: boolean;
152
+ changesDebounceMs?: number;
153
+ progressMaxHz?: number;
154
+ fetchKeysPerRequest?: number;
155
+ /** Rows a reconciliation fetches before it applies them (ADR-0066). */
156
+ reconcileApplyRows?: number;
157
+ /** A reconciliation that would fetch at least this many rows of one table hands them to a staged snapshot (ADR-0066). 0 = never. */
158
+ reconcileSnapshotThreshold?: number;
159
+ /** The largest decoded body accepted from one server frame (ADR-0051). */
160
+ maxDecodedFrameBytes?: number;
161
+ /** The newest G9RowBatch body layout to ask the server for (ADR-0040). */
162
+ maxFrameVersion?: number;
163
+ /** Fail a run when the server reports a client column whose type differs (ADR-0061). */
164
+ failOnTypeMismatch?: boolean;
165
+ /** A staged pull older than this is discarded instead of resumed. */
166
+ stagedPullMaxAgeMs?: number;
167
+ /** Most payload bytes kept in stages other than the one a run resumes (ADR-0060). */
168
+ stagedPullsMaxBytes?: number;
169
+ keepDeletedFeedEntries?: number;
170
+ keepRunReports?: number;
171
+ /** Where the engine logs (the ILogger twin); silent when omitted. */
172
+ logger?: G9SyncLogger | null;
173
+ /** Platform integration (foreground service, keep-awake, connectivity). */
174
+ hooks?: G9SyncHostHooks | null;
175
+ }
176
+ export type G9ResolvedEngineOptions = Required<Omit<G9SyncEngineOptions, 'appVersion' | 'logger' | 'hooks'>> & {
177
+ appVersion: string | null;
178
+ logger: G9SyncLogger | null;
179
+ hooks: G9SyncHostHooks | null;
180
+ };
181
+ export declare function resolveEngineOptions(options: G9SyncEngineOptions | null | undefined): G9ResolvedEngineOptions;
182
+ /** Log levels of {@link G9SyncLogger} (Microsoft.Extensions.Logging's). */
183
+ export type G9SyncLogLevel = 'debug' | 'information' | 'warning' | 'error';
184
+ /** Where the library logs. `eventId` is the .NET LoggerMessage event id of the same message. */
185
+ export interface G9SyncLogger {
186
+ log(level: G9SyncLogLevel, eventId: number, message: string, error?: unknown): void;
187
+ }
188
+ /** Platform integration points (`IG9SyncHostHooks`). */
189
+ export interface G9SyncHostHooks {
190
+ /** Called when a run starts executing; the returned scope's `dispose` runs when it ends. */
191
+ beginRun(run: G9SyncRun, signal: AbortSignal): Promise<{
192
+ dispose(): void | Promise<void>;
193
+ } | null>;
194
+ /** False skips automatic background runs. */
195
+ readonly isNetworkAvailable: boolean;
196
+ }
197
+ /** `G9CSyncHealth`. */
198
+ export interface G9SyncHealth {
199
+ clientId: string;
200
+ isConnected: boolean;
201
+ isRunning: boolean;
202
+ queuedRuns: number;
203
+ serverEpoch: string | null;
204
+ lastHelloUtcMs: number | null;
205
+ pending: G9PendingSummary;
206
+ subscriptions: G9SyncSubscriptionRecord[];
207
+ recentRuns: G9SyncRunReport[];
208
+ }
209
+ /** `G9CRowsAppliedEventArgs`. */
210
+ export interface G9RowsAppliedEvent {
211
+ report: G9ApplyReport;
212
+ source: G9ESyncPhase;
213
+ /** Set for a staged pull applied in slices (ADR-0053). */
214
+ slice: G9ApplySlice | null;
215
+ }
216
+ /** `G9CSyncErrors.Normalize`: every failure becomes a G9SyncException with a stable code. */
217
+ export declare function normalizeError(error: unknown): G9SyncException;
218
+ /** `G9CSyncErrors.IsTransient`. */
219
+ export declare function isTransientError(error: unknown): boolean;