@syncular/client 0.0.1-60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blobs/index.d.ts +7 -0
- package/dist/blobs/index.d.ts.map +1 -0
- package/dist/blobs/index.js +7 -0
- package/dist/blobs/index.js.map +1 -0
- package/dist/blobs/manager.d.ts +345 -0
- package/dist/blobs/manager.d.ts.map +1 -0
- package/dist/blobs/manager.js +749 -0
- package/dist/blobs/manager.js.map +1 -0
- package/dist/blobs/migrate.d.ts +14 -0
- package/dist/blobs/migrate.d.ts.map +1 -0
- package/dist/blobs/migrate.js +59 -0
- package/dist/blobs/migrate.js.map +1 -0
- package/dist/blobs/types.d.ts +62 -0
- package/dist/blobs/types.d.ts.map +1 -0
- package/dist/blobs/types.js +5 -0
- package/dist/blobs/types.js.map +1 -0
- package/dist/client.d.ts +338 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +834 -0
- package/dist/client.js.map +1 -0
- package/dist/conflicts.d.ts +31 -0
- package/dist/conflicts.d.ts.map +1 -0
- package/dist/conflicts.js +118 -0
- package/dist/conflicts.js.map +1 -0
- package/dist/create-client.d.ts +115 -0
- package/dist/create-client.d.ts.map +1 -0
- package/dist/create-client.js +162 -0
- package/dist/create-client.js.map +1 -0
- package/dist/engine/SyncEngine.d.ts +215 -0
- package/dist/engine/SyncEngine.d.ts.map +1 -0
- package/dist/engine/SyncEngine.js +1066 -0
- package/dist/engine/SyncEngine.js.map +1 -0
- package/dist/engine/index.d.ts +6 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +6 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/types.d.ts +230 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +7 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/handlers/create-handler.d.ts +110 -0
- package/dist/handlers/create-handler.d.ts.map +1 -0
- package/dist/handlers/create-handler.js +140 -0
- package/dist/handlers/create-handler.js.map +1 -0
- package/dist/handlers/registry.d.ts +15 -0
- package/dist/handlers/registry.d.ts.map +1 -0
- package/dist/handlers/registry.js +29 -0
- package/dist/handlers/registry.js.map +1 -0
- package/dist/handlers/types.d.ts +83 -0
- package/dist/handlers/types.d.ts.map +1 -0
- package/dist/handlers/types.js +5 -0
- package/dist/handlers/types.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/migrate.d.ts +19 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +106 -0
- package/dist/migrate.js.map +1 -0
- package/dist/mutations.d.ts +138 -0
- package/dist/mutations.d.ts.map +1 -0
- package/dist/mutations.js +611 -0
- package/dist/mutations.js.map +1 -0
- package/dist/outbox.d.ts +112 -0
- package/dist/outbox.d.ts.map +1 -0
- package/dist/outbox.js +304 -0
- package/dist/outbox.js.map +1 -0
- package/dist/plugins/incrementing-version.d.ts +34 -0
- package/dist/plugins/incrementing-version.d.ts.map +1 -0
- package/dist/plugins/incrementing-version.js +83 -0
- package/dist/plugins/incrementing-version.js.map +1 -0
- package/dist/plugins/index.d.ts +3 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +3 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/types.d.ts +49 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +15 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/proxy/connection.d.ts +33 -0
- package/dist/proxy/connection.d.ts.map +1 -0
- package/dist/proxy/connection.js +153 -0
- package/dist/proxy/connection.js.map +1 -0
- package/dist/proxy/dialect.d.ts +46 -0
- package/dist/proxy/dialect.d.ts.map +1 -0
- package/dist/proxy/dialect.js +58 -0
- package/dist/proxy/dialect.js.map +1 -0
- package/dist/proxy/driver.d.ts +42 -0
- package/dist/proxy/driver.d.ts.map +1 -0
- package/dist/proxy/driver.js +78 -0
- package/dist/proxy/driver.js.map +1 -0
- package/dist/proxy/index.d.ts +10 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/index.js +10 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/proxy/mutations.d.ts +9 -0
- package/dist/proxy/mutations.d.ts.map +1 -0
- package/dist/proxy/mutations.js +11 -0
- package/dist/proxy/mutations.js.map +1 -0
- package/dist/pull-engine.d.ts +45 -0
- package/dist/pull-engine.d.ts.map +1 -0
- package/dist/pull-engine.js +391 -0
- package/dist/pull-engine.js.map +1 -0
- package/dist/push-engine.d.ts +18 -0
- package/dist/push-engine.d.ts.map +1 -0
- package/dist/push-engine.js +155 -0
- package/dist/push-engine.js.map +1 -0
- package/dist/query/FingerprintCollector.d.ts +18 -0
- package/dist/query/FingerprintCollector.d.ts.map +1 -0
- package/dist/query/FingerprintCollector.js +28 -0
- package/dist/query/FingerprintCollector.js.map +1 -0
- package/dist/query/QueryContext.d.ts +33 -0
- package/dist/query/QueryContext.d.ts.map +1 -0
- package/dist/query/QueryContext.js +16 -0
- package/dist/query/QueryContext.js.map +1 -0
- package/dist/query/fingerprint.d.ts +61 -0
- package/dist/query/fingerprint.d.ts.map +1 -0
- package/dist/query/fingerprint.js +91 -0
- package/dist/query/fingerprint.js.map +1 -0
- package/dist/query/index.d.ts +7 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +7 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/tracked-select.d.ts +18 -0
- package/dist/query/tracked-select.d.ts.map +1 -0
- package/dist/query/tracked-select.js +90 -0
- package/dist/query/tracked-select.js.map +1 -0
- package/dist/schema.d.ts +83 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +7 -0
- package/dist/schema.js.map +1 -0
- package/dist/sync-loop.d.ts +32 -0
- package/dist/sync-loop.d.ts.map +1 -0
- package/dist/sync-loop.js +249 -0
- package/dist/sync-loop.js.map +1 -0
- package/dist/utils/id.d.ts +8 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +19 -0
- package/dist/utils/id.js.map +1 -0
- package/package.json +58 -0
- package/src/blobs/index.ts +7 -0
- package/src/blobs/manager.ts +1027 -0
- package/src/blobs/migrate.ts +67 -0
- package/src/blobs/types.ts +84 -0
- package/src/client.ts +1222 -0
- package/src/conflicts.ts +180 -0
- package/src/create-client.ts +297 -0
- package/src/engine/SyncEngine.ts +1337 -0
- package/src/engine/index.ts +6 -0
- package/src/engine/types.ts +268 -0
- package/src/handlers/create-handler.ts +287 -0
- package/src/handlers/registry.ts +36 -0
- package/src/handlers/types.ts +102 -0
- package/src/index.ts +25 -0
- package/src/migrate.ts +122 -0
- package/src/mutations.ts +926 -0
- package/src/outbox.ts +397 -0
- package/src/plugins/incrementing-version.ts +133 -0
- package/src/plugins/index.ts +2 -0
- package/src/plugins/types.ts +63 -0
- package/src/proxy/connection.ts +191 -0
- package/src/proxy/dialect.ts +76 -0
- package/src/proxy/driver.ts +126 -0
- package/src/proxy/index.ts +10 -0
- package/src/proxy/mutations.ts +18 -0
- package/src/pull-engine.ts +518 -0
- package/src/push-engine.ts +201 -0
- package/src/query/FingerprintCollector.ts +29 -0
- package/src/query/QueryContext.ts +54 -0
- package/src/query/fingerprint.ts +109 -0
- package/src/query/index.ts +10 -0
- package/src/query/tracked-select.ts +139 -0
- package/src/schema.ts +94 -0
- package/src/sync-loop.ts +368 -0
- package/src/utils/id.ts +20 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @syncular/client - Sync pull engine
|
|
3
|
+
*/
|
|
4
|
+
import { sql } from 'kysely';
|
|
5
|
+
// Simple JSON serialization cache to avoid repeated stringification
|
|
6
|
+
// of the same objects during pull operations
|
|
7
|
+
const jsonCache = new WeakMap();
|
|
8
|
+
const jsonCacheStats = { hits: 0, misses: 0 };
|
|
9
|
+
const SNAPSHOT_CHUNK_CONCURRENCY = 8;
|
|
10
|
+
function serializeJsonCached(obj) {
|
|
11
|
+
if (obj === null || typeof obj !== 'object') {
|
|
12
|
+
return JSON.stringify(obj);
|
|
13
|
+
}
|
|
14
|
+
const cached = jsonCache.get(obj);
|
|
15
|
+
if (cached !== undefined) {
|
|
16
|
+
jsonCacheStats.hits++;
|
|
17
|
+
return cached;
|
|
18
|
+
}
|
|
19
|
+
jsonCacheStats.misses++;
|
|
20
|
+
const serialized = JSON.stringify(obj);
|
|
21
|
+
// Only cache objects that are likely to be reused (not one-off empty objects)
|
|
22
|
+
if (Object.keys(obj).length > 0) {
|
|
23
|
+
jsonCache.set(obj, serialized);
|
|
24
|
+
}
|
|
25
|
+
return serialized;
|
|
26
|
+
}
|
|
27
|
+
function isGzipBytes(bytes) {
|
|
28
|
+
return bytes.length >= 2 && bytes[0] === 0x1f && bytes[1] === 0x8b;
|
|
29
|
+
}
|
|
30
|
+
async function maybeGunzip(bytes) {
|
|
31
|
+
if (!isGzipBytes(bytes))
|
|
32
|
+
return bytes;
|
|
33
|
+
// Prefer Web Streams decompression when available (browser/modern runtimes).
|
|
34
|
+
if (typeof DecompressionStream !== 'undefined') {
|
|
35
|
+
const stream = new ReadableStream({
|
|
36
|
+
start(controller) {
|
|
37
|
+
controller.enqueue(new Uint8Array(bytes));
|
|
38
|
+
controller.close();
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
const decompressed = stream.pipeThrough(new DecompressionStream('gzip'));
|
|
42
|
+
const reader = decompressed.getReader();
|
|
43
|
+
try {
|
|
44
|
+
const chunks = [];
|
|
45
|
+
let total = 0;
|
|
46
|
+
while (true) {
|
|
47
|
+
const { value, done } = await reader.read();
|
|
48
|
+
if (done)
|
|
49
|
+
break;
|
|
50
|
+
if (!value)
|
|
51
|
+
continue;
|
|
52
|
+
chunks.push(value);
|
|
53
|
+
total += value.length;
|
|
54
|
+
}
|
|
55
|
+
const out = new Uint8Array(total);
|
|
56
|
+
let offset = 0;
|
|
57
|
+
for (const chunk of chunks) {
|
|
58
|
+
out.set(chunk, offset);
|
|
59
|
+
offset += chunk.length;
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
reader.releaseLock();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// If the runtime didn't auto-decompress `Content-Encoding: gzip`, and doesn't
|
|
68
|
+
// support DecompressionStream, we can't safely decode the chunk.
|
|
69
|
+
throw new Error('Snapshot chunk appears gzip-compressed but gzip decompression is not available in this runtime');
|
|
70
|
+
}
|
|
71
|
+
function parseNdjsonRows(text) {
|
|
72
|
+
const rows = [];
|
|
73
|
+
for (const line of text.split('\n')) {
|
|
74
|
+
if (!line)
|
|
75
|
+
continue;
|
|
76
|
+
rows.push(JSON.parse(line));
|
|
77
|
+
}
|
|
78
|
+
return rows;
|
|
79
|
+
}
|
|
80
|
+
async function computeSha256Hex(bytes) {
|
|
81
|
+
// Use crypto.subtle if available (browsers, modern Node/Bun)
|
|
82
|
+
if (typeof crypto !== 'undefined' && crypto.subtle) {
|
|
83
|
+
// Create a fresh ArrayBuffer to satisfy crypto.subtle's type requirements
|
|
84
|
+
const buffer = new ArrayBuffer(bytes.length);
|
|
85
|
+
new Uint8Array(buffer).set(bytes);
|
|
86
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
|
|
87
|
+
const hashArray = new Uint8Array(hashBuffer);
|
|
88
|
+
return Array.from(hashArray, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
89
|
+
}
|
|
90
|
+
// Fallback for Node.js/Bun without crypto.subtle
|
|
91
|
+
if (typeof globalThis.require === 'function') {
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
93
|
+
const { createHash } = await import('node:crypto');
|
|
94
|
+
return createHash('sha256').update(Buffer.from(bytes)).digest('hex');
|
|
95
|
+
}
|
|
96
|
+
throw new Error('No crypto implementation available for SHA-256. ' +
|
|
97
|
+
'Ensure crypto.subtle is available or running in Node.js/Bun.');
|
|
98
|
+
}
|
|
99
|
+
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
100
|
+
if (items.length === 0)
|
|
101
|
+
return [];
|
|
102
|
+
const workerCount = Math.max(1, Math.min(concurrency, items.length));
|
|
103
|
+
const results = new Array(items.length);
|
|
104
|
+
let nextIndex = 0;
|
|
105
|
+
async function worker() {
|
|
106
|
+
while (nextIndex < items.length) {
|
|
107
|
+
const index = nextIndex;
|
|
108
|
+
nextIndex += 1;
|
|
109
|
+
const item = items[index];
|
|
110
|
+
if (item === undefined)
|
|
111
|
+
continue;
|
|
112
|
+
results[index] = await mapper(item, index);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
116
|
+
return results;
|
|
117
|
+
}
|
|
118
|
+
async function materializeChunkedSnapshots(transport, response) {
|
|
119
|
+
const chunkCache = new Map();
|
|
120
|
+
const decoder = new TextDecoder();
|
|
121
|
+
const subscriptions = await Promise.all(response.subscriptions.map(async (sub) => {
|
|
122
|
+
if (!sub.bootstrap)
|
|
123
|
+
return sub;
|
|
124
|
+
if (!sub.snapshots || sub.snapshots.length === 0)
|
|
125
|
+
return sub;
|
|
126
|
+
const snapshots = await mapWithConcurrency(sub.snapshots, SNAPSHOT_CHUNK_CONCURRENCY, async (snapshot) => {
|
|
127
|
+
const chunks = snapshot.chunks ?? [];
|
|
128
|
+
if (chunks.length === 0) {
|
|
129
|
+
return snapshot;
|
|
130
|
+
}
|
|
131
|
+
const parsedRowsByChunk = await mapWithConcurrency(chunks, SNAPSHOT_CHUNK_CONCURRENCY, async (chunk) => {
|
|
132
|
+
const promise = chunkCache.get(chunk.id) ??
|
|
133
|
+
transport.fetchSnapshotChunk({ chunkId: chunk.id });
|
|
134
|
+
chunkCache.set(chunk.id, promise);
|
|
135
|
+
const raw = await promise;
|
|
136
|
+
const bytes = await maybeGunzip(raw);
|
|
137
|
+
// Verify chunk integrity using sha256 hash
|
|
138
|
+
if (chunk.sha256) {
|
|
139
|
+
const actualHash = await computeSha256Hex(bytes);
|
|
140
|
+
if (actualHash !== chunk.sha256) {
|
|
141
|
+
throw new Error(`Snapshot chunk integrity check failed: expected sha256 ${chunk.sha256}, got ${actualHash}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const text = decoder.decode(bytes);
|
|
145
|
+
return parseNdjsonRows(text);
|
|
146
|
+
});
|
|
147
|
+
const rows = [];
|
|
148
|
+
for (const parsedRows of parsedRowsByChunk) {
|
|
149
|
+
rows.push(...parsedRows);
|
|
150
|
+
}
|
|
151
|
+
return { ...snapshot, rows, chunks: undefined };
|
|
152
|
+
});
|
|
153
|
+
return { ...sub, snapshots };
|
|
154
|
+
}));
|
|
155
|
+
// Clear chunk cache after processing to prevent memory accumulation
|
|
156
|
+
chunkCache.clear();
|
|
157
|
+
return { ...response, subscriptions };
|
|
158
|
+
}
|
|
159
|
+
function parseBootstrapState(value) {
|
|
160
|
+
if (!value)
|
|
161
|
+
return null;
|
|
162
|
+
try {
|
|
163
|
+
// Handle both string (raw JSON) and object (already deserialized by SerializePlugin)
|
|
164
|
+
const parsed = typeof value === 'string'
|
|
165
|
+
? JSON.parse(value)
|
|
166
|
+
: value;
|
|
167
|
+
if (!parsed || typeof parsed !== 'object')
|
|
168
|
+
return null;
|
|
169
|
+
if (typeof parsed.asOfCommitSeq !== 'number')
|
|
170
|
+
return null;
|
|
171
|
+
if (!Array.isArray(parsed.tables))
|
|
172
|
+
return null;
|
|
173
|
+
if (typeof parsed.tableIndex !== 'number')
|
|
174
|
+
return null;
|
|
175
|
+
if (parsed.rowCursor !== null && typeof parsed.rowCursor !== 'string')
|
|
176
|
+
return null;
|
|
177
|
+
return parsed;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Build a pull request from subscription state. Exported for use
|
|
185
|
+
* by the combined sync path in sync-loop.ts.
|
|
186
|
+
*/
|
|
187
|
+
export async function buildPullRequest(db, options) {
|
|
188
|
+
const stateId = options.stateId ?? 'default';
|
|
189
|
+
const existingResult = await sql `
|
|
190
|
+
select
|
|
191
|
+
${sql.ref('state_id')},
|
|
192
|
+
${sql.ref('subscription_id')},
|
|
193
|
+
${sql.ref('shape')},
|
|
194
|
+
${sql.ref('scopes_json')},
|
|
195
|
+
${sql.ref('params_json')},
|
|
196
|
+
${sql.ref('cursor')},
|
|
197
|
+
${sql.ref('bootstrap_state_json')},
|
|
198
|
+
${sql.ref('status')},
|
|
199
|
+
${sql.ref('created_at')},
|
|
200
|
+
${sql.ref('updated_at')}
|
|
201
|
+
from ${sql.table('sync_subscription_state')}
|
|
202
|
+
where ${sql.ref('state_id')} = ${sql.val(stateId)}
|
|
203
|
+
`.execute(db);
|
|
204
|
+
const existing = existingResult.rows;
|
|
205
|
+
const existingById = new Map();
|
|
206
|
+
for (const row of existing)
|
|
207
|
+
existingById.set(row.subscription_id, row);
|
|
208
|
+
const request = {
|
|
209
|
+
clientId: options.clientId,
|
|
210
|
+
limitCommits: options.limitCommits ?? 50,
|
|
211
|
+
limitSnapshotRows: options.limitSnapshotRows ?? 1000,
|
|
212
|
+
maxSnapshotPages: options.maxSnapshotPages,
|
|
213
|
+
dedupeRows: options.dedupeRows,
|
|
214
|
+
subscriptions: (options.subscriptions ?? []).map((sub) => ({
|
|
215
|
+
...sub,
|
|
216
|
+
cursor: Math.max(-1, existingById.get(sub.id)?.cursor ?? -1),
|
|
217
|
+
bootstrapState: parseBootstrapState(existingById.get(sub.id)?.bootstrap_state_json),
|
|
218
|
+
})),
|
|
219
|
+
};
|
|
220
|
+
return { request, existing, existingById, stateId };
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Apply a pull response (run plugins + write to local DB).
|
|
224
|
+
* Exported for use by the combined sync path in sync-loop.ts.
|
|
225
|
+
*/
|
|
226
|
+
export async function applyPullResponse(db, transport, shapes, options, pullState, rawResponse) {
|
|
227
|
+
const { request, existing, existingById, stateId } = pullState;
|
|
228
|
+
const hydrated = await materializeChunkedSnapshots(transport, rawResponse);
|
|
229
|
+
const ctx = {
|
|
230
|
+
actorId: options.actorId ?? 'unknown',
|
|
231
|
+
clientId: options.clientId,
|
|
232
|
+
};
|
|
233
|
+
const plugins = options.plugins ?? [];
|
|
234
|
+
let responseToApply = hydrated;
|
|
235
|
+
for (const plugin of plugins) {
|
|
236
|
+
if (!plugin.afterPull)
|
|
237
|
+
continue;
|
|
238
|
+
responseToApply = await plugin.afterPull(ctx, {
|
|
239
|
+
request,
|
|
240
|
+
response: responseToApply,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
await db.transaction().execute(async (trx) => {
|
|
244
|
+
const desiredIds = new Set((options.subscriptions ?? []).map((s) => s.id));
|
|
245
|
+
// Remove local data for subscriptions that are no longer desired.
|
|
246
|
+
for (const row of existing) {
|
|
247
|
+
if (desiredIds.has(row.subscription_id))
|
|
248
|
+
continue;
|
|
249
|
+
// Clear data for this shape matching the subscription's scopes
|
|
250
|
+
if (row.shape) {
|
|
251
|
+
try {
|
|
252
|
+
const scopes = row.scopes_json
|
|
253
|
+
? typeof row.scopes_json === 'string'
|
|
254
|
+
? JSON.parse(row.scopes_json)
|
|
255
|
+
: row.scopes_json
|
|
256
|
+
: {};
|
|
257
|
+
await shapes.getOrThrow(row.shape).clearAll({ trx, scopes });
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// ignore missing shape handler
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
await sql `
|
|
264
|
+
delete from ${sql.table('sync_subscription_state')}
|
|
265
|
+
where ${sql.ref('state_id')} = ${sql.val(stateId)}
|
|
266
|
+
and ${sql.ref('subscription_id')} = ${sql.val(row.subscription_id)}
|
|
267
|
+
`.execute(trx);
|
|
268
|
+
}
|
|
269
|
+
const subsById = new Map();
|
|
270
|
+
for (const s of options.subscriptions ?? [])
|
|
271
|
+
subsById.set(s.id, s);
|
|
272
|
+
for (const sub of responseToApply.subscriptions) {
|
|
273
|
+
const def = subsById.get(sub.id);
|
|
274
|
+
const prev = existingById.get(sub.id);
|
|
275
|
+
// Revoked: clear data and drop the subscription row.
|
|
276
|
+
if (sub.status === 'revoked') {
|
|
277
|
+
if (prev?.shape) {
|
|
278
|
+
try {
|
|
279
|
+
const scopes = prev.scopes_json
|
|
280
|
+
? typeof prev.scopes_json === 'string'
|
|
281
|
+
? JSON.parse(prev.scopes_json)
|
|
282
|
+
: prev.scopes_json
|
|
283
|
+
: {};
|
|
284
|
+
await shapes.getOrThrow(prev.shape).clearAll({ trx, scopes });
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
// ignore missing handler
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
await sql `
|
|
291
|
+
delete from ${sql.table('sync_subscription_state')}
|
|
292
|
+
where ${sql.ref('state_id')} = ${sql.val(stateId)}
|
|
293
|
+
and ${sql.ref('subscription_id')} = ${sql.val(sub.id)}
|
|
294
|
+
`.execute(trx);
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
// Apply snapshots (bootstrap mode)
|
|
298
|
+
if (sub.bootstrap) {
|
|
299
|
+
for (const snapshot of sub.snapshots ?? []) {
|
|
300
|
+
const handler = shapes.getOrThrow(snapshot.table);
|
|
301
|
+
// Call onSnapshotStart hook when starting a new snapshot
|
|
302
|
+
if (snapshot.isFirstPage && handler.onSnapshotStart) {
|
|
303
|
+
await handler.onSnapshotStart({
|
|
304
|
+
trx,
|
|
305
|
+
table: snapshot.table,
|
|
306
|
+
scopes: sub.scopes,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
await handler.applySnapshot({ trx }, snapshot);
|
|
310
|
+
// Call onSnapshotEnd hook when snapshot is complete
|
|
311
|
+
if (snapshot.isLastPage && handler.onSnapshotEnd) {
|
|
312
|
+
await handler.onSnapshotEnd({
|
|
313
|
+
trx,
|
|
314
|
+
table: snapshot.table,
|
|
315
|
+
scopes: sub.scopes,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
// Apply incremental changes
|
|
322
|
+
for (const commit of sub.commits) {
|
|
323
|
+
for (const change of commit.changes) {
|
|
324
|
+
const handler = shapes.getOrThrow(change.table);
|
|
325
|
+
await handler.applyChange({ trx }, change);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// Persist subscription cursor + metadata.
|
|
330
|
+
// Use cached JSON serialization to avoid repeated stringification
|
|
331
|
+
const now = Date.now();
|
|
332
|
+
const paramsJson = serializeJsonCached(def?.params ?? {});
|
|
333
|
+
const scopesJson = serializeJsonCached(def?.scopes ?? {});
|
|
334
|
+
const bootstrapStateJson = sub.bootstrap
|
|
335
|
+
? sub.bootstrapState
|
|
336
|
+
? serializeJsonCached(sub.bootstrapState)
|
|
337
|
+
: null
|
|
338
|
+
: null;
|
|
339
|
+
const shape = def?.shape ?? 'unknown';
|
|
340
|
+
await sql `
|
|
341
|
+
insert into ${sql.table('sync_subscription_state')} (
|
|
342
|
+
${sql.join([
|
|
343
|
+
sql.ref('state_id'),
|
|
344
|
+
sql.ref('subscription_id'),
|
|
345
|
+
sql.ref('shape'),
|
|
346
|
+
sql.ref('scopes_json'),
|
|
347
|
+
sql.ref('params_json'),
|
|
348
|
+
sql.ref('cursor'),
|
|
349
|
+
sql.ref('bootstrap_state_json'),
|
|
350
|
+
sql.ref('status'),
|
|
351
|
+
sql.ref('created_at'),
|
|
352
|
+
sql.ref('updated_at'),
|
|
353
|
+
])}
|
|
354
|
+
) values (
|
|
355
|
+
${sql.join([
|
|
356
|
+
sql.val(stateId),
|
|
357
|
+
sql.val(sub.id),
|
|
358
|
+
sql.val(shape),
|
|
359
|
+
sql.val(scopesJson),
|
|
360
|
+
sql.val(paramsJson),
|
|
361
|
+
sql.val(sub.nextCursor),
|
|
362
|
+
sql.val(bootstrapStateJson),
|
|
363
|
+
sql.val('active'),
|
|
364
|
+
sql.val(now),
|
|
365
|
+
sql.val(now),
|
|
366
|
+
])}
|
|
367
|
+
)
|
|
368
|
+
on conflict (${sql.join([sql.ref('state_id'), sql.ref('subscription_id')])})
|
|
369
|
+
do update set
|
|
370
|
+
${sql.ref('shape')} = ${sql.val(shape)},
|
|
371
|
+
${sql.ref('scopes_json')} = ${sql.val(scopesJson)},
|
|
372
|
+
${sql.ref('params_json')} = ${sql.val(paramsJson)},
|
|
373
|
+
${sql.ref('cursor')} = ${sql.val(sub.nextCursor)},
|
|
374
|
+
${sql.ref('bootstrap_state_json')} = ${sql.val(bootstrapStateJson)},
|
|
375
|
+
${sql.ref('status')} = ${sql.val('active')},
|
|
376
|
+
${sql.ref('updated_at')} = ${sql.val(now)}
|
|
377
|
+
`.execute(trx);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
return responseToApply;
|
|
381
|
+
}
|
|
382
|
+
export async function syncPullOnce(db, transport, shapes, options) {
|
|
383
|
+
const pullState = await buildPullRequest(db, options);
|
|
384
|
+
const { clientId, ...pullBody } = pullState.request;
|
|
385
|
+
const combined = await transport.sync({ clientId, pull: pullBody });
|
|
386
|
+
if (!combined.pull) {
|
|
387
|
+
return { ok: true, subscriptions: [] };
|
|
388
|
+
}
|
|
389
|
+
return applyPullResponse(db, transport, shapes, options, pullState, combined.pull);
|
|
390
|
+
}
|
|
391
|
+
//# sourceMappingURL=pull-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull-engine.js","sourceRoot":"","sources":["../src/pull-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @syncular/client - Sync push engine (commit-based)
|
|
3
|
+
*/
|
|
4
|
+
import type { SyncPushResponse, SyncTransport } from '@syncular/core';
|
|
5
|
+
import type { Kysely } from 'kysely';
|
|
6
|
+
import type { SyncClientPlugin } from './plugins/types';
|
|
7
|
+
import type { SyncClientDb } from './schema';
|
|
8
|
+
export interface SyncPushOnceOptions {
|
|
9
|
+
clientId: string;
|
|
10
|
+
actorId?: string;
|
|
11
|
+
plugins?: SyncClientPlugin[];
|
|
12
|
+
}
|
|
13
|
+
export interface SyncPushOnceResult {
|
|
14
|
+
pushed: boolean;
|
|
15
|
+
response?: SyncPushResponse;
|
|
16
|
+
}
|
|
17
|
+
export declare function syncPushOnce<DB extends SyncClientDb>(db: Kysely<DB>, transport: SyncTransport, options: SyncPushOnceOptions): Promise<SyncPushOnceResult>;
|
|
18
|
+
//# sourceMappingURL=push-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-engine.d.ts","sourceRoot":"","sources":["../src/push-engine.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAEV,gBAAgB,EAChB,aAAa,EACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAQrC,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B;AAiBD,wBAAsB,YAAY,CAAC,EAAE,SAAS,YAAY,EACxD,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAmJ7B"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @syncular/client - Sync push engine (commit-based)
|
|
3
|
+
*/
|
|
4
|
+
import { upsertConflictsForRejectedCommit } from './conflicts';
|
|
5
|
+
import { getNextSendableOutboxCommit, markOutboxCommitAcked, markOutboxCommitFailed, markOutboxCommitPending, } from './outbox';
|
|
6
|
+
function hasPushViaWs(transport) {
|
|
7
|
+
return 'pushViaWs' in transport && typeof transport.pushViaWs === 'function';
|
|
8
|
+
}
|
|
9
|
+
function clonePushRequest(request) {
|
|
10
|
+
if (typeof structuredClone === 'function')
|
|
11
|
+
return structuredClone(request);
|
|
12
|
+
return JSON.parse(JSON.stringify(request));
|
|
13
|
+
}
|
|
14
|
+
export async function syncPushOnce(db, transport, options) {
|
|
15
|
+
// getNextSendableOutboxCommit now atomically claims the commit
|
|
16
|
+
// (marks it as 'sending' and returns it in one operation)
|
|
17
|
+
const next = await getNextSendableOutboxCommit(db);
|
|
18
|
+
if (!next)
|
|
19
|
+
return { pushed: false };
|
|
20
|
+
const request = {
|
|
21
|
+
clientId: options.clientId,
|
|
22
|
+
clientCommitId: next.client_commit_id,
|
|
23
|
+
operations: next.operations,
|
|
24
|
+
schemaVersion: next.schema_version,
|
|
25
|
+
};
|
|
26
|
+
const ctx = {
|
|
27
|
+
actorId: options.actorId ?? 'unknown',
|
|
28
|
+
clientId: options.clientId,
|
|
29
|
+
};
|
|
30
|
+
const plugins = options.plugins ?? [];
|
|
31
|
+
let requestToSend = request;
|
|
32
|
+
if (plugins.length > 0) {
|
|
33
|
+
try {
|
|
34
|
+
requestToSend = clonePushRequest(request);
|
|
35
|
+
for (const plugin of plugins) {
|
|
36
|
+
if (!plugin.beforePush)
|
|
37
|
+
continue;
|
|
38
|
+
requestToSend = await plugin.beforePush(ctx, requestToSend);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
43
|
+
await markOutboxCommitPending(db, { id: next.id, error: message });
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
let res;
|
|
48
|
+
try {
|
|
49
|
+
// Try WS push first if the transport supports it
|
|
50
|
+
let wsResponse = null;
|
|
51
|
+
if (hasPushViaWs(transport)) {
|
|
52
|
+
wsResponse = await transport.pushViaWs(requestToSend);
|
|
53
|
+
}
|
|
54
|
+
if (wsResponse) {
|
|
55
|
+
res = wsResponse;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Fall back to HTTP
|
|
59
|
+
const combined = await transport.sync({
|
|
60
|
+
clientId: requestToSend.clientId,
|
|
61
|
+
push: {
|
|
62
|
+
clientCommitId: requestToSend.clientCommitId,
|
|
63
|
+
operations: requestToSend.operations,
|
|
64
|
+
schemaVersion: requestToSend.schemaVersion,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
if (!combined.push) {
|
|
68
|
+
throw new Error('Server returned no push response');
|
|
69
|
+
}
|
|
70
|
+
res = combined.push;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
75
|
+
// Treat transport exceptions as retryable. The sync loop already applies backoff,
|
|
76
|
+
// and failed commits are reserved for terminal server rejections (e.g. conflicts).
|
|
77
|
+
await markOutboxCommitPending(db, { id: next.id, error: message });
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
let responseToUse = res;
|
|
81
|
+
if (plugins.length > 0) {
|
|
82
|
+
try {
|
|
83
|
+
for (const plugin of plugins) {
|
|
84
|
+
if (!plugin.afterPush)
|
|
85
|
+
continue;
|
|
86
|
+
responseToUse = await plugin.afterPush(ctx, {
|
|
87
|
+
request: requestToSend,
|
|
88
|
+
response: responseToUse,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
// The server already received and processed this commit. Persist the raw response
|
|
94
|
+
// so we don't end up retrying a commit that was already applied.
|
|
95
|
+
const responseJson = JSON.stringify(res);
|
|
96
|
+
if (res.status === 'applied' || res.status === 'cached') {
|
|
97
|
+
await markOutboxCommitAcked(db, {
|
|
98
|
+
id: next.id,
|
|
99
|
+
commitSeq: res.commitSeq ?? null,
|
|
100
|
+
responseJson,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
await upsertConflictsForRejectedCommit(db, {
|
|
105
|
+
outboxCommitId: next.id,
|
|
106
|
+
clientCommitId: next.client_commit_id,
|
|
107
|
+
response: res,
|
|
108
|
+
});
|
|
109
|
+
await markOutboxCommitFailed(db, {
|
|
110
|
+
id: next.id,
|
|
111
|
+
error: 'REJECTED',
|
|
112
|
+
responseJson,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
throw err;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const responseJson = JSON.stringify(responseToUse);
|
|
119
|
+
if (responseToUse.status === 'applied' || responseToUse.status === 'cached') {
|
|
120
|
+
await markOutboxCommitAcked(db, {
|
|
121
|
+
id: next.id,
|
|
122
|
+
commitSeq: responseToUse.commitSeq ?? null,
|
|
123
|
+
responseJson,
|
|
124
|
+
});
|
|
125
|
+
return { pushed: true, response: responseToUse };
|
|
126
|
+
}
|
|
127
|
+
// Check if all errors are retriable - if so, keep pending for retry
|
|
128
|
+
const errorResults = responseToUse.results.filter((r) => r.status === 'error');
|
|
129
|
+
const allRetriable = errorResults.length > 0 && errorResults.every((r) => r.retriable === true);
|
|
130
|
+
if (allRetriable) {
|
|
131
|
+
// All errors are retriable - keep commit pending for retry
|
|
132
|
+
const errorMessages = errorResults
|
|
133
|
+
.map((r) => r.error ?? 'Unknown error')
|
|
134
|
+
.join('; ');
|
|
135
|
+
await markOutboxCommitPending(db, {
|
|
136
|
+
id: next.id,
|
|
137
|
+
error: `Retriable: ${errorMessages}`,
|
|
138
|
+
responseJson,
|
|
139
|
+
});
|
|
140
|
+
return { pushed: true, response: responseToUse };
|
|
141
|
+
}
|
|
142
|
+
// Terminal rejection - mark as failed and record conflicts
|
|
143
|
+
await upsertConflictsForRejectedCommit(db, {
|
|
144
|
+
outboxCommitId: next.id,
|
|
145
|
+
clientCommitId: next.client_commit_id,
|
|
146
|
+
response: responseToUse,
|
|
147
|
+
});
|
|
148
|
+
await markOutboxCommitFailed(db, {
|
|
149
|
+
id: next.id,
|
|
150
|
+
error: 'REJECTED',
|
|
151
|
+
responseJson,
|
|
152
|
+
});
|
|
153
|
+
return { pushed: true, response: responseToUse };
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=push-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-engine.js","sourceRoot":"","sources":["../src/push-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @syncular/client - Fingerprint Collector
|
|
3
|
+
*
|
|
4
|
+
* Accumulates fingerprint data from multiple queries.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Fingerprint collector accumulates fingerprint data from multiple queries
|
|
8
|
+
*/
|
|
9
|
+
export declare class FingerprintCollector {
|
|
10
|
+
private fingerprints;
|
|
11
|
+
add(fingerprint: string): void;
|
|
12
|
+
/**
|
|
13
|
+
* Get combined fingerprint from all collected fingerprints
|
|
14
|
+
*/
|
|
15
|
+
getCombined(): string;
|
|
16
|
+
clear(): void;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=FingerprintCollector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FingerprintCollector.d.ts","sourceRoot":"","sources":["../../src/query/FingerprintCollector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,YAAY,CAAgB;IAEpC,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAE7B;IAED;;OAEG;IACH,WAAW,IAAI,MAAM,CAIpB;IAED,KAAK,IAAI,IAAI,CAEZ;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @syncular/client - Fingerprint Collector
|
|
3
|
+
*
|
|
4
|
+
* Accumulates fingerprint data from multiple queries.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Fingerprint collector accumulates fingerprint data from multiple queries
|
|
8
|
+
*/
|
|
9
|
+
export class FingerprintCollector {
|
|
10
|
+
fingerprints = [];
|
|
11
|
+
add(fingerprint) {
|
|
12
|
+
this.fingerprints.push(fingerprint);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get combined fingerprint from all collected fingerprints
|
|
16
|
+
*/
|
|
17
|
+
getCombined() {
|
|
18
|
+
if (this.fingerprints.length === 0)
|
|
19
|
+
return '';
|
|
20
|
+
if (this.fingerprints.length === 1)
|
|
21
|
+
return this.fingerprints[0] ?? '';
|
|
22
|
+
return this.fingerprints.join('|');
|
|
23
|
+
}
|
|
24
|
+
clear() {
|
|
25
|
+
this.fingerprints = [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=FingerprintCollector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FingerprintCollector.js","sourceRoot":"","sources":["../../src/query/FingerprintCollector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,OAAO,oBAAoB;IACvB,YAAY,GAAa,EAAE,CAAC;IAEpC,GAAG,CAAC,WAAmB,EAAQ;QAC7B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAAA,CACrC;IAED;;OAEG;IACH,WAAW,GAAW;QACpB,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAC9C,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACtE,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAAA,CACpC;IAED,KAAK,GAAS;QACZ,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IAAA,CACxB;CACF"}
|