lakesync 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter-types-DwsQGQS4.d.ts +94 -0
- package/dist/adapter.d.ts +202 -63
- package/dist/adapter.js +20 -5
- package/dist/analyst.js +2 -2
- package/dist/{base-poller-BpUyuG2R.d.ts → base-poller-Y7ORYgUv.d.ts} +78 -19
- package/dist/catalogue.d.ts +1 -1
- package/dist/catalogue.js +3 -3
- package/dist/{chunk-P3FT7QCW.js → chunk-4SG66H5K.js} +395 -252
- package/dist/chunk-4SG66H5K.js.map +1 -0
- package/dist/{chunk-GUJWMK5P.js → chunk-C4KD6YKP.js} +419 -380
- package/dist/chunk-C4KD6YKP.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-IRJ4QRWV.js → chunk-FIIHPQMQ.js} +396 -209
- package/dist/chunk-FIIHPQMQ.js.map +1 -0
- package/dist/{chunk-UAUQGP3B.js → chunk-U2NV4DUX.js} +2 -2
- package/dist/{chunk-NCZYFZ3B.js → chunk-XVP5DJJ7.js} +44 -18
- package/dist/{chunk-NCZYFZ3B.js.map → chunk-XVP5DJJ7.js.map} +1 -1
- package/dist/{chunk-FHVTUKXL.js → chunk-YHYBLU6W.js} +2 -2
- package/dist/{chunk-QMS7TGFL.js → chunk-ZNY4DSFU.js} +29 -15
- package/dist/{chunk-QMS7TGFL.js.map → chunk-ZNY4DSFU.js.map} +1 -1
- package/dist/{chunk-SF7Y6ZUA.js → chunk-ZU7RC7CT.js} +2 -2
- package/dist/client.d.ts +186 -17
- package/dist/client.js +456 -188
- package/dist/client.js.map +1 -1
- package/dist/compactor.d.ts +2 -2
- package/dist/compactor.js +4 -4
- package/dist/connector-jira.d.ts +13 -3
- package/dist/connector-jira.js +7 -3
- package/dist/connector-salesforce.d.ts +13 -3
- package/dist/connector-salesforce.js +7 -3
- package/dist/{coordinator-D32a5rNk.d.ts → coordinator-eGmZMnJ_.d.ts} +120 -30
- package/dist/create-poller-Cc2MGfhh.d.ts +55 -0
- package/dist/factory-DFfR-030.d.ts +33 -0
- package/dist/gateway-server.d.ts +516 -119
- package/dist/gateway-server.js +1201 -4035
- package/dist/gateway-server.js.map +1 -1
- package/dist/gateway.d.ts +69 -106
- package/dist/gateway.js +13 -6
- package/dist/index.d.ts +65 -58
- package/dist/index.js +18 -4
- package/dist/parquet.d.ts +1 -1
- package/dist/parquet.js +3 -3
- package/dist/proto.d.ts +1 -1
- package/dist/proto.js +3 -3
- package/dist/react.d.ts +47 -10
- package/dist/react.js +88 -40
- package/dist/react.js.map +1 -1
- package/dist/{registry-CPTgO9jv.d.ts → registry-Dd8JuW8T.d.ts} +19 -4
- package/dist/{gateway-Bpvatd9n.d.ts → request-handler-B1I5xDOx.d.ts} +193 -20
- package/dist/{resolver-CbuXm3nB.d.ts → resolver-CXxmC0jR.d.ts} +1 -1
- package/dist/{src-RHKJFQKR.js → src-WU7IBVC4.js} +19 -5
- package/dist/{types-CLlD4XOy.d.ts → types-BdGBv2ba.d.ts} +17 -2
- package/dist/{types-D-E0VrfS.d.ts → types-D2C9jTbL.d.ts} +39 -22
- package/package.json +1 -1
- package/dist/auth-CAVutXzx.d.ts +0 -30
- package/dist/chunk-7D4SUZUM.js +0 -38
- package/dist/chunk-GUJWMK5P.js.map +0 -1
- package/dist/chunk-IRJ4QRWV.js.map +0 -1
- package/dist/chunk-P3FT7QCW.js.map +0 -1
- package/dist/db-types-BlN-4KbQ.d.ts +0 -29
- package/dist/src-CLCALYDT.js +0 -25
- package/dist/src-FPJQYQNA.js +0 -27
- package/dist/src-FPJQYQNA.js.map +0 -1
- package/dist/src-RHKJFQKR.js.map +0 -1
- package/dist/types-DSC_EiwR.d.ts +0 -45
- /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/dist/{chunk-UAUQGP3B.js.map → chunk-U2NV4DUX.js.map} +0 -0
- /package/dist/{chunk-FHVTUKXL.js.map → chunk-YHYBLU6W.js.map} +0 -0
- /package/dist/{chunk-SF7Y6ZUA.js.map → chunk-ZU7RC7CT.js.map} +0 -0
- /package/dist/{src-CLCALYDT.js.map → src-WU7IBVC4.js.map} +0 -0
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isDatabaseAdapter,
|
|
3
|
-
isMaterialisable
|
|
4
|
-
} from "./chunk-GUJWMK5P.js";
|
|
5
1
|
import {
|
|
6
2
|
buildPartitionSpec,
|
|
7
3
|
lakeSyncTableName,
|
|
8
4
|
tableSchemaToIceberg
|
|
9
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-U2NV4DUX.js";
|
|
10
6
|
import {
|
|
11
7
|
writeDeltasToParquet
|
|
12
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ZU7RC7CT.js";
|
|
13
9
|
import {
|
|
14
10
|
AdapterNotFoundError,
|
|
15
11
|
BackpressureError,
|
|
12
|
+
COLUMN_TYPES,
|
|
16
13
|
Err,
|
|
17
14
|
FlushError,
|
|
18
15
|
HLC,
|
|
@@ -21,6 +18,8 @@ import {
|
|
|
21
18
|
bigintReplacer,
|
|
22
19
|
bigintReviver,
|
|
23
20
|
filterDeltas,
|
|
21
|
+
isDatabaseAdapter,
|
|
22
|
+
isMaterialisable,
|
|
24
23
|
listConnectorDescriptors,
|
|
25
24
|
resolveLWW,
|
|
26
25
|
rowKey,
|
|
@@ -28,19 +27,84 @@ import {
|
|
|
28
27
|
validateAction,
|
|
29
28
|
validateConnectorConfig,
|
|
30
29
|
validateSyncRules
|
|
31
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-4SG66H5K.js";
|
|
31
|
+
|
|
32
|
+
// ../gateway/src/idempotency-cache.ts
|
|
33
|
+
var DEFAULT_MAX_CACHE_SIZE = 1e4;
|
|
34
|
+
var DEFAULT_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
35
|
+
var MemoryIdempotencyCache = class {
|
|
36
|
+
entries = /* @__PURE__ */ new Map();
|
|
37
|
+
maxSize;
|
|
38
|
+
ttlMs;
|
|
39
|
+
constructor(config) {
|
|
40
|
+
this.maxSize = config?.maxSize ?? DEFAULT_MAX_CACHE_SIZE;
|
|
41
|
+
this.ttlMs = config?.ttlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
42
|
+
}
|
|
43
|
+
/** {@inheritDoc IdempotencyCache.has} */
|
|
44
|
+
has(actionId) {
|
|
45
|
+
const entry = this.entries.get(actionId);
|
|
46
|
+
if (!entry) return false;
|
|
47
|
+
if (Date.now() - entry.cachedAt > this.ttlMs) {
|
|
48
|
+
this.entries.delete(actionId);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
/** {@inheritDoc IdempotencyCache.get} */
|
|
54
|
+
get(key) {
|
|
55
|
+
const entry = this.entries.get(key);
|
|
56
|
+
if (!entry) return void 0;
|
|
57
|
+
if (Date.now() - entry.cachedAt > this.ttlMs) {
|
|
58
|
+
this.entries.delete(key);
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
return entry.value;
|
|
62
|
+
}
|
|
63
|
+
/** {@inheritDoc IdempotencyCache.set} */
|
|
64
|
+
set(actionId, result, idempotencyKey) {
|
|
65
|
+
this.evictStaleEntries();
|
|
66
|
+
const entry = { value: result, cachedAt: Date.now() };
|
|
67
|
+
this.entries.set(actionId, entry);
|
|
68
|
+
if (idempotencyKey) {
|
|
69
|
+
this.entries.set(`idem:${idempotencyKey}`, entry);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Evict expired entries and trim to max size (counting only non-idem entries). */
|
|
73
|
+
evictStaleEntries() {
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
for (const [key, entry] of this.entries) {
|
|
76
|
+
if (now - entry.cachedAt > this.ttlMs) {
|
|
77
|
+
this.entries.delete(key);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const actionKeys = [...this.entries.keys()].filter((k) => !k.startsWith("idem:"));
|
|
81
|
+
if (actionKeys.length > this.maxSize) {
|
|
82
|
+
const excess = actionKeys.length - this.maxSize;
|
|
83
|
+
for (let i = 0; i < excess; i++) {
|
|
84
|
+
this.entries.delete(actionKeys[i]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
32
89
|
|
|
33
90
|
// ../gateway/src/action-dispatcher.ts
|
|
34
91
|
var ActionDispatcher = class {
|
|
35
92
|
actionHandlers = /* @__PURE__ */ new Map();
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
93
|
+
cache;
|
|
94
|
+
/**
|
|
95
|
+
* Create an ActionDispatcher.
|
|
96
|
+
*
|
|
97
|
+
* @param handlers - Optional map of connector name to action handler.
|
|
98
|
+
* @param cacheConfig - Optional cache configuration (used when no `cache` is provided).
|
|
99
|
+
* @param cache - Optional pre-built idempotency cache; defaults to a {@link MemoryIdempotencyCache}.
|
|
100
|
+
*/
|
|
101
|
+
constructor(handlers, cacheConfig, cache) {
|
|
39
102
|
if (handlers) {
|
|
40
103
|
for (const [name, handler] of Object.entries(handlers)) {
|
|
41
104
|
this.actionHandlers.set(name, handler);
|
|
42
105
|
}
|
|
43
106
|
}
|
|
107
|
+
this.cache = cache ?? new MemoryIdempotencyCache(cacheConfig);
|
|
44
108
|
}
|
|
45
109
|
/**
|
|
46
110
|
* Dispatch an action push to registered handlers.
|
|
@@ -61,8 +125,8 @@ var ActionDispatcher = class {
|
|
|
61
125
|
if (!validation.ok) {
|
|
62
126
|
return Err(validation.error);
|
|
63
127
|
}
|
|
64
|
-
if (this.
|
|
65
|
-
const cached = this.
|
|
128
|
+
if (this.cache.has(action.actionId)) {
|
|
129
|
+
const cached = this.cache.get(action.actionId);
|
|
66
130
|
if (cached) {
|
|
67
131
|
results.push(cached);
|
|
68
132
|
continue;
|
|
@@ -70,7 +134,7 @@ var ActionDispatcher = class {
|
|
|
70
134
|
continue;
|
|
71
135
|
}
|
|
72
136
|
if (action.idempotencyKey) {
|
|
73
|
-
const cached = this.
|
|
137
|
+
const cached = this.cache.get(`idem:${action.idempotencyKey}`);
|
|
74
138
|
if (cached) {
|
|
75
139
|
results.push(cached);
|
|
76
140
|
continue;
|
|
@@ -85,7 +149,7 @@ var ActionDispatcher = class {
|
|
|
85
149
|
retryable: false
|
|
86
150
|
};
|
|
87
151
|
results.push(errorResult);
|
|
88
|
-
this.
|
|
152
|
+
this.cache.set(action.actionId, errorResult, action.idempotencyKey);
|
|
89
153
|
continue;
|
|
90
154
|
}
|
|
91
155
|
const supported = handler.supportedActions.some((d) => d.actionType === action.actionType);
|
|
@@ -97,13 +161,13 @@ var ActionDispatcher = class {
|
|
|
97
161
|
retryable: false
|
|
98
162
|
};
|
|
99
163
|
results.push(errorResult);
|
|
100
|
-
this.
|
|
164
|
+
this.cache.set(action.actionId, errorResult, action.idempotencyKey);
|
|
101
165
|
continue;
|
|
102
166
|
}
|
|
103
167
|
const execResult = await handler.executeAction(action, context);
|
|
104
168
|
if (execResult.ok) {
|
|
105
169
|
results.push(execResult.value);
|
|
106
|
-
this.
|
|
170
|
+
this.cache.set(action.actionId, execResult.value, action.idempotencyKey);
|
|
107
171
|
} else {
|
|
108
172
|
const err = execResult.error;
|
|
109
173
|
const errorResult = {
|
|
@@ -114,7 +178,7 @@ var ActionDispatcher = class {
|
|
|
114
178
|
};
|
|
115
179
|
results.push(errorResult);
|
|
116
180
|
if (!errorResult.retryable) {
|
|
117
|
-
this.
|
|
181
|
+
this.cache.set(action.actionId, errorResult, action.idempotencyKey);
|
|
118
182
|
}
|
|
119
183
|
}
|
|
120
184
|
}
|
|
@@ -161,14 +225,6 @@ var ActionDispatcher = class {
|
|
|
161
225
|
}
|
|
162
226
|
return { connectors };
|
|
163
227
|
}
|
|
164
|
-
/** Cache an action result for idempotency deduplication. */
|
|
165
|
-
cacheActionResult(action, result) {
|
|
166
|
-
this.executedActions.add(action.actionId);
|
|
167
|
-
this.idempotencyMap.set(action.actionId, result);
|
|
168
|
-
if (action.idempotencyKey) {
|
|
169
|
-
this.idempotencyMap.set(`idem:${action.idempotencyKey}`, result);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
228
|
};
|
|
173
229
|
|
|
174
230
|
// ../gateway/src/buffer.ts
|
|
@@ -284,9 +340,18 @@ var DeltaBuffer = class {
|
|
|
284
340
|
this.tableLog.delete(table);
|
|
285
341
|
return tableDeltas;
|
|
286
342
|
}
|
|
287
|
-
/**
|
|
288
|
-
|
|
289
|
-
|
|
343
|
+
/**
|
|
344
|
+
* Snapshot the current buffer state without clearing it.
|
|
345
|
+
*
|
|
346
|
+
* Useful for inspecting the buffer contents without draining.
|
|
347
|
+
* Use {@link clear} separately after a successful flush for
|
|
348
|
+
* transactional semantics.
|
|
349
|
+
*/
|
|
350
|
+
snapshot() {
|
|
351
|
+
return { entries: [...this.log], byteSize: this.estimatedBytes };
|
|
352
|
+
}
|
|
353
|
+
/** Clear all buffer state. */
|
|
354
|
+
clear() {
|
|
290
355
|
this.log = [];
|
|
291
356
|
this.index.clear();
|
|
292
357
|
this.deltaIds.clear();
|
|
@@ -294,6 +359,11 @@ var DeltaBuffer = class {
|
|
|
294
359
|
this.createdAt = Date.now();
|
|
295
360
|
this.tableBytes.clear();
|
|
296
361
|
this.tableLog.clear();
|
|
362
|
+
}
|
|
363
|
+
/** Drain the log for flush. Returns log entries and clears both structures. */
|
|
364
|
+
drain() {
|
|
365
|
+
const { entries } = this.snapshot();
|
|
366
|
+
this.clear();
|
|
297
367
|
return entries;
|
|
298
368
|
}
|
|
299
369
|
/** Number of log entries */
|
|
@@ -344,7 +414,7 @@ var MAX_PUSH_PAYLOAD_BYTES = 1048576;
|
|
|
344
414
|
var MAX_DELTAS_PER_PUSH = 1e4;
|
|
345
415
|
var MAX_PULL_LIMIT = 1e4;
|
|
346
416
|
var DEFAULT_PULL_LIMIT = 100;
|
|
347
|
-
var VALID_COLUMN_TYPES =
|
|
417
|
+
var VALID_COLUMN_TYPES = new Set(COLUMN_TYPES);
|
|
348
418
|
var DEFAULT_MAX_BUFFER_BYTES = 4 * 1024 * 1024;
|
|
349
419
|
var DEFAULT_MAX_BUFFER_AGE_MS = 3e4;
|
|
350
420
|
|
|
@@ -359,26 +429,39 @@ function hlcRange(entries) {
|
|
|
359
429
|
}
|
|
360
430
|
return { min, max };
|
|
361
431
|
}
|
|
362
|
-
|
|
363
|
-
if (
|
|
432
|
+
function notifyMaterialisationFailure(entries, error, config) {
|
|
433
|
+
if (!config.onMaterialisationFailure) return;
|
|
434
|
+
const tables = new Set(entries.map((e) => e.table));
|
|
435
|
+
for (const table of tables) {
|
|
436
|
+
const count = entries.filter((e) => e.table === table).length;
|
|
437
|
+
config.onMaterialisationFailure(table, count, error);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
var DatabaseFlushStrategy = class {
|
|
441
|
+
async flush(entries, _byteSize, deps) {
|
|
442
|
+
const adapter = deps.adapter;
|
|
364
443
|
try {
|
|
365
|
-
const result = await
|
|
444
|
+
const result = await adapter.insertDeltas(entries);
|
|
366
445
|
if (!result.ok) {
|
|
367
446
|
deps.restoreEntries(entries);
|
|
368
447
|
return Err(new FlushError(`Database flush failed: ${result.error.message}`));
|
|
369
448
|
}
|
|
370
|
-
if (deps.schemas && deps.schemas.length > 0 && isMaterialisable(
|
|
449
|
+
if (deps.schemas && deps.schemas.length > 0 && isMaterialisable(adapter)) {
|
|
371
450
|
try {
|
|
372
|
-
const matResult = await
|
|
451
|
+
const matResult = await adapter.materialise(entries, deps.schemas);
|
|
373
452
|
if (!matResult.ok) {
|
|
453
|
+
const error = new Error(matResult.error.message);
|
|
374
454
|
console.warn(
|
|
375
455
|
`[lakesync] Materialisation failed (${entries.length} deltas): ${matResult.error.message}`
|
|
376
456
|
);
|
|
457
|
+
notifyMaterialisationFailure(entries, error, deps.config);
|
|
377
458
|
}
|
|
378
459
|
} catch (error) {
|
|
460
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
379
461
|
console.warn(
|
|
380
|
-
`[lakesync] Materialisation error (${entries.length} deltas): ${
|
|
462
|
+
`[lakesync] Materialisation error (${entries.length} deltas): ${err.message}`
|
|
381
463
|
);
|
|
464
|
+
notifyMaterialisationFailure(entries, err, deps.config);
|
|
382
465
|
}
|
|
383
466
|
}
|
|
384
467
|
return Ok(void 0);
|
|
@@ -387,14 +470,14 @@ async function flushEntries(entries, byteSize, deps, keyPrefix) {
|
|
|
387
470
|
return Err(new FlushError(`Unexpected database flush failure: ${toError(error).message}`));
|
|
388
471
|
}
|
|
389
472
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
473
|
+
};
|
|
474
|
+
var LakeJsonFlushStrategy = class {
|
|
475
|
+
async flush(entries, byteSize, deps, keyPrefix) {
|
|
476
|
+
const adapter = deps.adapter;
|
|
477
|
+
try {
|
|
478
|
+
const { min, max } = hlcRange(entries);
|
|
479
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
480
|
+
const prefix = keyPrefix ? `${keyPrefix}-` : "";
|
|
398
481
|
const envelope = {
|
|
399
482
|
version: 1,
|
|
400
483
|
gatewayId: deps.config.gatewayId,
|
|
@@ -404,10 +487,33 @@ async function flushEntries(entries, byteSize, deps, keyPrefix) {
|
|
|
404
487
|
byteSize,
|
|
405
488
|
deltas: entries
|
|
406
489
|
};
|
|
407
|
-
objectKey = `deltas/${date}/${deps.config.gatewayId}/${prefix}${min.toString()}-${max.toString()}.json`;
|
|
408
|
-
data = new TextEncoder().encode(JSON.stringify(envelope, bigintReplacer));
|
|
409
|
-
|
|
410
|
-
|
|
490
|
+
const objectKey = `deltas/${date}/${deps.config.gatewayId}/${prefix}${min.toString()}-${max.toString()}.json`;
|
|
491
|
+
const data = new TextEncoder().encode(JSON.stringify(envelope, bigintReplacer));
|
|
492
|
+
const result = await adapter.putObject(objectKey, data, "application/json");
|
|
493
|
+
if (!result.ok) {
|
|
494
|
+
deps.restoreEntries(entries);
|
|
495
|
+
return Err(new FlushError(`Failed to write flush envelope: ${result.error.message}`));
|
|
496
|
+
}
|
|
497
|
+
if (deps.config.catalogue && deps.config.tableSchema) {
|
|
498
|
+
await commitToCatalogue(
|
|
499
|
+
objectKey,
|
|
500
|
+
data.byteLength,
|
|
501
|
+
entries.length,
|
|
502
|
+
deps.config.catalogue,
|
|
503
|
+
deps.config.tableSchema
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
return Ok(void 0);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
deps.restoreEntries(entries);
|
|
509
|
+
return Err(new FlushError(`Unexpected flush failure: ${toError(error).message}`));
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var LakeParquetFlushStrategy = class {
|
|
514
|
+
async flush(entries, _byteSize, deps, keyPrefix) {
|
|
515
|
+
const adapter = deps.adapter;
|
|
516
|
+
try {
|
|
411
517
|
if (!deps.config.tableSchema) {
|
|
412
518
|
deps.restoreEntries(entries);
|
|
413
519
|
return Err(new FlushError("tableSchema required for Parquet flush"));
|
|
@@ -417,29 +523,43 @@ async function flushEntries(entries, byteSize, deps, keyPrefix) {
|
|
|
417
523
|
deps.restoreEntries(entries);
|
|
418
524
|
return Err(parquetResult.error);
|
|
419
525
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
526
|
+
const { min, max } = hlcRange(entries);
|
|
527
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
528
|
+
const prefix = keyPrefix ? `${keyPrefix}-` : "";
|
|
529
|
+
const objectKey = `deltas/${date}/${deps.config.gatewayId}/${prefix}${min.toString()}-${max.toString()}.parquet`;
|
|
530
|
+
const data = parquetResult.value;
|
|
531
|
+
const result = await adapter.putObject(objectKey, data, "application/vnd.apache.parquet");
|
|
532
|
+
if (!result.ok) {
|
|
533
|
+
deps.restoreEntries(entries);
|
|
534
|
+
return Err(new FlushError(`Failed to write flush envelope: ${result.error.message}`));
|
|
535
|
+
}
|
|
536
|
+
if (deps.config.catalogue && deps.config.tableSchema) {
|
|
537
|
+
await commitToCatalogue(
|
|
538
|
+
objectKey,
|
|
539
|
+
data.byteLength,
|
|
540
|
+
entries.length,
|
|
541
|
+
deps.config.catalogue,
|
|
542
|
+
deps.config.tableSchema
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
return Ok(void 0);
|
|
546
|
+
} catch (error) {
|
|
426
547
|
deps.restoreEntries(entries);
|
|
427
|
-
return Err(new FlushError(`
|
|
548
|
+
return Err(new FlushError(`Unexpected flush failure: ${toError(error).message}`));
|
|
428
549
|
}
|
|
429
|
-
if (deps.config.catalogue && deps.config.tableSchema) {
|
|
430
|
-
await commitToCatalogue(
|
|
431
|
-
objectKey,
|
|
432
|
-
data.byteLength,
|
|
433
|
-
entries.length,
|
|
434
|
-
deps.config.catalogue,
|
|
435
|
-
deps.config.tableSchema
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
|
-
return Ok(void 0);
|
|
439
|
-
} catch (error) {
|
|
440
|
-
deps.restoreEntries(entries);
|
|
441
|
-
return Err(new FlushError(`Unexpected flush failure: ${toError(error).message}`));
|
|
442
550
|
}
|
|
551
|
+
};
|
|
552
|
+
var databaseStrategy = new DatabaseFlushStrategy();
|
|
553
|
+
var lakeJsonStrategy = new LakeJsonFlushStrategy();
|
|
554
|
+
var lakeParquetStrategy = new LakeParquetFlushStrategy();
|
|
555
|
+
function selectFlushStrategy(adapter, format) {
|
|
556
|
+
if (isDatabaseAdapter(adapter)) return databaseStrategy;
|
|
557
|
+
if (format === "json") return lakeJsonStrategy;
|
|
558
|
+
return lakeParquetStrategy;
|
|
559
|
+
}
|
|
560
|
+
async function flushEntries(entries, byteSize, deps, keyPrefix) {
|
|
561
|
+
const strategy = selectFlushStrategy(deps.adapter, deps.config.flushFormat);
|
|
562
|
+
return strategy.flush(entries, byteSize, deps, keyPrefix);
|
|
443
563
|
}
|
|
444
564
|
async function commitToCatalogue(objectKey, fileSizeInBytes, recordCount, catalogue, schema) {
|
|
445
565
|
const { namespace, name } = lakeSyncTableName(schema.table);
|
|
@@ -463,6 +583,117 @@ async function commitToCatalogue(objectKey, fileSizeInBytes, recordCount, catalo
|
|
|
463
583
|
}
|
|
464
584
|
}
|
|
465
585
|
|
|
586
|
+
// ../gateway/src/flush-coordinator.ts
|
|
587
|
+
var FlushCoordinator = class {
|
|
588
|
+
flushing = false;
|
|
589
|
+
/** Whether a flush is currently in progress. */
|
|
590
|
+
get isFlushing() {
|
|
591
|
+
return this.flushing;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Flush all entries from the buffer to the adapter.
|
|
595
|
+
*
|
|
596
|
+
* Drains the buffer first, then writes to the adapter. On failure,
|
|
597
|
+
* entries are restored to the buffer.
|
|
598
|
+
*/
|
|
599
|
+
async flush(buffer, adapter, deps) {
|
|
600
|
+
if (this.flushing) {
|
|
601
|
+
return Err(new FlushError("Flush already in progress"));
|
|
602
|
+
}
|
|
603
|
+
if (buffer.logSize === 0) {
|
|
604
|
+
return Ok(void 0);
|
|
605
|
+
}
|
|
606
|
+
if (!adapter) {
|
|
607
|
+
return Err(new FlushError("No adapter configured"));
|
|
608
|
+
}
|
|
609
|
+
this.flushing = true;
|
|
610
|
+
const byteSize = isDatabaseAdapter(adapter) ? 0 : buffer.byteSize;
|
|
611
|
+
const entries = buffer.drain();
|
|
612
|
+
if (entries.length === 0) {
|
|
613
|
+
this.flushing = false;
|
|
614
|
+
return Ok(void 0);
|
|
615
|
+
}
|
|
616
|
+
try {
|
|
617
|
+
return await flushEntries(entries, byteSize, {
|
|
618
|
+
adapter,
|
|
619
|
+
config: deps.config,
|
|
620
|
+
restoreEntries: (e) => this.restoreEntries(buffer, e),
|
|
621
|
+
schemas: deps.schemas
|
|
622
|
+
});
|
|
623
|
+
} finally {
|
|
624
|
+
this.flushing = false;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Flush a single table's deltas from the buffer.
|
|
629
|
+
*
|
|
630
|
+
* Drains only the specified table's deltas and flushes them,
|
|
631
|
+
* leaving other tables in the buffer.
|
|
632
|
+
*/
|
|
633
|
+
async flushTable(table, buffer, adapter, deps) {
|
|
634
|
+
if (this.flushing) {
|
|
635
|
+
return Err(new FlushError("Flush already in progress"));
|
|
636
|
+
}
|
|
637
|
+
if (!adapter) {
|
|
638
|
+
return Err(new FlushError("No adapter configured"));
|
|
639
|
+
}
|
|
640
|
+
const entries = buffer.drainTable(table);
|
|
641
|
+
if (entries.length === 0) {
|
|
642
|
+
return Ok(void 0);
|
|
643
|
+
}
|
|
644
|
+
this.flushing = true;
|
|
645
|
+
try {
|
|
646
|
+
return await flushEntries(
|
|
647
|
+
entries,
|
|
648
|
+
0,
|
|
649
|
+
{
|
|
650
|
+
adapter,
|
|
651
|
+
config: deps.config,
|
|
652
|
+
restoreEntries: (e) => this.restoreEntries(buffer, e),
|
|
653
|
+
schemas: deps.schemas
|
|
654
|
+
},
|
|
655
|
+
table
|
|
656
|
+
);
|
|
657
|
+
} finally {
|
|
658
|
+
this.flushing = false;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/** Restore drained entries back to the buffer for retry. */
|
|
662
|
+
restoreEntries(buffer, entries) {
|
|
663
|
+
for (const entry of entries) {
|
|
664
|
+
buffer.append(entry);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
// ../gateway/src/source-registry.ts
|
|
670
|
+
var SourceRegistry = class {
|
|
671
|
+
sources = /* @__PURE__ */ new Map();
|
|
672
|
+
constructor(initial) {
|
|
673
|
+
if (initial) {
|
|
674
|
+
for (const [name, adapter] of Object.entries(initial)) {
|
|
675
|
+
this.sources.set(name, adapter);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
/** Register a named source adapter. */
|
|
680
|
+
register(name, adapter) {
|
|
681
|
+
this.sources.set(name, adapter);
|
|
682
|
+
}
|
|
683
|
+
/** Unregister a named source adapter. */
|
|
684
|
+
unregister(name) {
|
|
685
|
+
this.sources.delete(name);
|
|
686
|
+
}
|
|
687
|
+
/** Get a source adapter by name, or undefined if not registered. */
|
|
688
|
+
get(name) {
|
|
689
|
+
return this.sources.get(name);
|
|
690
|
+
}
|
|
691
|
+
/** List all registered source adapter names. */
|
|
692
|
+
list() {
|
|
693
|
+
return [...this.sources.keys()];
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
|
|
466
697
|
// ../gateway/src/gateway.ts
|
|
467
698
|
var SyncGateway = class {
|
|
468
699
|
hlc;
|
|
@@ -470,20 +701,20 @@ var SyncGateway = class {
|
|
|
470
701
|
actions;
|
|
471
702
|
config;
|
|
472
703
|
adapter;
|
|
473
|
-
|
|
704
|
+
sources;
|
|
705
|
+
flushCoordinator;
|
|
474
706
|
constructor(config, adapter) {
|
|
475
707
|
this.config = { sourceAdapters: {}, ...config };
|
|
476
708
|
this.hlc = new HLC();
|
|
477
709
|
this.buffer = new DeltaBuffer();
|
|
478
710
|
this.adapter = this.config.adapter ?? adapter ?? null;
|
|
479
711
|
this.actions = new ActionDispatcher(config.actionHandlers);
|
|
712
|
+
this.sources = new SourceRegistry(this.config.sourceAdapters);
|
|
713
|
+
this.flushCoordinator = new FlushCoordinator();
|
|
480
714
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
this.buffer.append(entry);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
715
|
+
// -----------------------------------------------------------------------
|
|
716
|
+
// Push — pipeline of validation steps then buffer append
|
|
717
|
+
// -----------------------------------------------------------------------
|
|
487
718
|
/**
|
|
488
719
|
* Handle an incoming push from a client.
|
|
489
720
|
*
|
|
@@ -494,14 +725,8 @@ var SyncGateway = class {
|
|
|
494
725
|
* or a `ClockDriftError` if the client clock is too far ahead.
|
|
495
726
|
*/
|
|
496
727
|
handlePush(msg) {
|
|
497
|
-
const
|
|
498
|
-
if (
|
|
499
|
-
return Err(
|
|
500
|
-
new BackpressureError(
|
|
501
|
-
`Buffer backpressure exceeded (${this.buffer.byteSize} >= ${backpressureLimit} bytes)`
|
|
502
|
-
)
|
|
503
|
-
);
|
|
504
|
-
}
|
|
728
|
+
const bpResult = this.checkBackpressure();
|
|
729
|
+
if (!bpResult.ok) return bpResult;
|
|
505
730
|
let accepted = 0;
|
|
506
731
|
const ingested = [];
|
|
507
732
|
for (const delta of msg.deltas) {
|
|
@@ -536,6 +761,18 @@ var SyncGateway = class {
|
|
|
536
761
|
const serverHlc = this.hlc.now();
|
|
537
762
|
return Ok({ serverHlc, accepted, deltas: ingested });
|
|
538
763
|
}
|
|
764
|
+
/** Check buffer backpressure. */
|
|
765
|
+
checkBackpressure() {
|
|
766
|
+
const backpressureLimit = this.config.maxBackpressureBytes ?? this.config.maxBufferBytes * 2;
|
|
767
|
+
if (this.buffer.byteSize >= backpressureLimit) {
|
|
768
|
+
return Err(
|
|
769
|
+
new BackpressureError(
|
|
770
|
+
`Buffer backpressure exceeded (${this.buffer.byteSize} >= ${backpressureLimit} bytes)`
|
|
771
|
+
)
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
return Ok(void 0);
|
|
775
|
+
}
|
|
539
776
|
handlePull(msg, context) {
|
|
540
777
|
if (msg.source) {
|
|
541
778
|
return this.handleAdapterPull(msg, context);
|
|
@@ -580,7 +817,7 @@ var SyncGateway = class {
|
|
|
580
817
|
}
|
|
581
818
|
/** Pull from a named source adapter. */
|
|
582
819
|
async handleAdapterPull(msg, context) {
|
|
583
|
-
const adapter = this.
|
|
820
|
+
const adapter = this.sources.get(msg.source);
|
|
584
821
|
if (!adapter) {
|
|
585
822
|
return Err(new AdapterNotFoundError(`Source adapter "${msg.source}" not found`));
|
|
586
823
|
}
|
|
@@ -598,7 +835,7 @@ var SyncGateway = class {
|
|
|
598
835
|
return Ok({ deltas: sliced, serverHlc, hasMore });
|
|
599
836
|
}
|
|
600
837
|
// -----------------------------------------------------------------------
|
|
601
|
-
// Flush — delegates to
|
|
838
|
+
// Flush — delegates to FlushCoordinator
|
|
602
839
|
// -----------------------------------------------------------------------
|
|
603
840
|
/**
|
|
604
841
|
* Flush the buffer to the configured adapter.
|
|
@@ -611,55 +848,15 @@ var SyncGateway = class {
|
|
|
611
848
|
* @returns A `Result` indicating success or a `FlushError`.
|
|
612
849
|
*/
|
|
613
850
|
async flush() {
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
}
|
|
623
|
-
this.flushing = true;
|
|
624
|
-
if (isDatabaseAdapter(this.adapter)) {
|
|
625
|
-
const entries2 = this.buffer.drain();
|
|
626
|
-
if (entries2.length === 0) {
|
|
627
|
-
this.flushing = false;
|
|
628
|
-
return Ok(void 0);
|
|
629
|
-
}
|
|
630
|
-
try {
|
|
631
|
-
return await flushEntries(entries2, 0, {
|
|
632
|
-
adapter: this.adapter,
|
|
633
|
-
config: {
|
|
634
|
-
gatewayId: this.config.gatewayId,
|
|
635
|
-
flushFormat: this.config.flushFormat,
|
|
636
|
-
tableSchema: this.config.tableSchema,
|
|
637
|
-
catalogue: this.config.catalogue
|
|
638
|
-
},
|
|
639
|
-
restoreEntries: (e) => this.restoreEntries(e),
|
|
640
|
-
schemas: this.config.schemas
|
|
641
|
-
});
|
|
642
|
-
} finally {
|
|
643
|
-
this.flushing = false;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
const byteSize = this.buffer.byteSize;
|
|
647
|
-
const entries = this.buffer.drain();
|
|
648
|
-
try {
|
|
649
|
-
return await flushEntries(entries, byteSize, {
|
|
650
|
-
adapter: this.adapter,
|
|
651
|
-
config: {
|
|
652
|
-
gatewayId: this.config.gatewayId,
|
|
653
|
-
flushFormat: this.config.flushFormat,
|
|
654
|
-
tableSchema: this.config.tableSchema,
|
|
655
|
-
catalogue: this.config.catalogue
|
|
656
|
-
},
|
|
657
|
-
restoreEntries: (e) => this.restoreEntries(e),
|
|
658
|
-
schemas: this.config.schemas
|
|
659
|
-
});
|
|
660
|
-
} finally {
|
|
661
|
-
this.flushing = false;
|
|
662
|
-
}
|
|
851
|
+
return this.flushCoordinator.flush(this.buffer, this.adapter, {
|
|
852
|
+
config: {
|
|
853
|
+
gatewayId: this.config.gatewayId,
|
|
854
|
+
flushFormat: this.config.flushFormat,
|
|
855
|
+
tableSchema: this.config.tableSchema,
|
|
856
|
+
catalogue: this.config.catalogue
|
|
857
|
+
},
|
|
858
|
+
schemas: this.config.schemas
|
|
859
|
+
});
|
|
663
860
|
}
|
|
664
861
|
/**
|
|
665
862
|
* Flush a single table's deltas from the buffer.
|
|
@@ -668,37 +865,15 @@ var SyncGateway = class {
|
|
|
668
865
|
* leaving other tables in the buffer.
|
|
669
866
|
*/
|
|
670
867
|
async flushTable(table) {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
}
|
|
681
|
-
this.flushing = true;
|
|
682
|
-
try {
|
|
683
|
-
return await flushEntries(
|
|
684
|
-
entries,
|
|
685
|
-
0,
|
|
686
|
-
{
|
|
687
|
-
adapter: this.adapter,
|
|
688
|
-
config: {
|
|
689
|
-
gatewayId: this.config.gatewayId,
|
|
690
|
-
flushFormat: this.config.flushFormat,
|
|
691
|
-
tableSchema: this.config.tableSchema,
|
|
692
|
-
catalogue: this.config.catalogue
|
|
693
|
-
},
|
|
694
|
-
restoreEntries: (e) => this.restoreEntries(e),
|
|
695
|
-
schemas: this.config.schemas
|
|
696
|
-
},
|
|
697
|
-
table
|
|
698
|
-
);
|
|
699
|
-
} finally {
|
|
700
|
-
this.flushing = false;
|
|
701
|
-
}
|
|
868
|
+
return this.flushCoordinator.flushTable(table, this.buffer, this.adapter, {
|
|
869
|
+
config: {
|
|
870
|
+
gatewayId: this.config.gatewayId,
|
|
871
|
+
flushFormat: this.config.flushFormat,
|
|
872
|
+
tableSchema: this.config.tableSchema,
|
|
873
|
+
catalogue: this.config.catalogue
|
|
874
|
+
},
|
|
875
|
+
schemas: this.config.schemas
|
|
876
|
+
});
|
|
702
877
|
}
|
|
703
878
|
// -----------------------------------------------------------------------
|
|
704
879
|
// Actions — delegates to ActionDispatcher
|
|
@@ -724,7 +899,7 @@ var SyncGateway = class {
|
|
|
724
899
|
return this.actions.describe();
|
|
725
900
|
}
|
|
726
901
|
// -----------------------------------------------------------------------
|
|
727
|
-
// Source adapters
|
|
902
|
+
// Source adapters — delegates to SourceRegistry
|
|
728
903
|
// -----------------------------------------------------------------------
|
|
729
904
|
/**
|
|
730
905
|
* Register a named source adapter for adapter-sourced pulls.
|
|
@@ -733,7 +908,7 @@ var SyncGateway = class {
|
|
|
733
908
|
* @param adapter - The database adapter to register.
|
|
734
909
|
*/
|
|
735
910
|
registerSource(name, adapter) {
|
|
736
|
-
this.
|
|
911
|
+
this.sources.register(name, adapter);
|
|
737
912
|
}
|
|
738
913
|
/**
|
|
739
914
|
* Unregister a named source adapter.
|
|
@@ -741,7 +916,7 @@ var SyncGateway = class {
|
|
|
741
916
|
* @param name - The source name to remove.
|
|
742
917
|
*/
|
|
743
918
|
unregisterSource(name) {
|
|
744
|
-
|
|
919
|
+
this.sources.unregister(name);
|
|
745
920
|
}
|
|
746
921
|
/**
|
|
747
922
|
* List all registered source adapter names.
|
|
@@ -749,7 +924,16 @@ var SyncGateway = class {
|
|
|
749
924
|
* @returns Array of registered source adapter names.
|
|
750
925
|
*/
|
|
751
926
|
listSources() {
|
|
752
|
-
return
|
|
927
|
+
return this.sources.list();
|
|
928
|
+
}
|
|
929
|
+
// -----------------------------------------------------------------------
|
|
930
|
+
// Rehydration — restore persisted deltas without push validation
|
|
931
|
+
// -----------------------------------------------------------------------
|
|
932
|
+
/** Rehydrate the buffer with persisted deltas (bypasses push validation). */
|
|
933
|
+
rehydrate(deltas) {
|
|
934
|
+
for (const delta of deltas) {
|
|
935
|
+
this.buffer.append(delta);
|
|
936
|
+
}
|
|
753
937
|
}
|
|
754
938
|
// -----------------------------------------------------------------------
|
|
755
939
|
// Buffer queries
|
|
@@ -789,13 +973,17 @@ var SyncGateway = class {
|
|
|
789
973
|
};
|
|
790
974
|
|
|
791
975
|
// ../gateway/src/validation.ts
|
|
792
|
-
function
|
|
793
|
-
let body;
|
|
976
|
+
function parseJson(raw, reviver) {
|
|
794
977
|
try {
|
|
795
|
-
|
|
978
|
+
return Ok(JSON.parse(raw, reviver));
|
|
796
979
|
} catch {
|
|
797
980
|
return Err({ status: 400, message: "Invalid JSON body" });
|
|
798
981
|
}
|
|
982
|
+
}
|
|
983
|
+
function validatePushBody(raw, headerClientId) {
|
|
984
|
+
const parsed = parseJson(raw, bigintReviver);
|
|
985
|
+
if (!parsed.ok) return parsed;
|
|
986
|
+
const body = parsed.value;
|
|
799
987
|
if (!body.clientId || !Array.isArray(body.deltas)) {
|
|
800
988
|
return Err({ status: 400, message: "Missing required fields: clientId, deltas" });
|
|
801
989
|
}
|
|
@@ -840,12 +1028,9 @@ function parsePullParams(params) {
|
|
|
840
1028
|
return Ok(msg);
|
|
841
1029
|
}
|
|
842
1030
|
function validateActionBody(raw, headerClientId) {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
} catch {
|
|
847
|
-
return Err({ status: 400, message: "Invalid JSON body" });
|
|
848
|
-
}
|
|
1031
|
+
const parsed = parseJson(raw, bigintReviver);
|
|
1032
|
+
if (!parsed.ok) return parsed;
|
|
1033
|
+
const body = parsed.value;
|
|
849
1034
|
if (!body.clientId || !Array.isArray(body.actions)) {
|
|
850
1035
|
return Err({ status: 400, message: "Missing required fields: clientId, actions" });
|
|
851
1036
|
}
|
|
@@ -858,12 +1043,9 @@ function validateActionBody(raw, headerClientId) {
|
|
|
858
1043
|
return Ok(body);
|
|
859
1044
|
}
|
|
860
1045
|
function validateSchemaBody(raw) {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
} catch {
|
|
865
|
-
return Err({ status: 400, message: "Invalid JSON body" });
|
|
866
|
-
}
|
|
1046
|
+
const parsed = parseJson(raw);
|
|
1047
|
+
if (!parsed.ok) return parsed;
|
|
1048
|
+
const schema = parsed.value;
|
|
867
1049
|
if (!schema.table || !Array.isArray(schema.columns)) {
|
|
868
1050
|
return Err({ status: 400, message: "Missing required fields: table, columns" });
|
|
869
1051
|
}
|
|
@@ -1000,12 +1182,11 @@ async function handleSaveSchema(raw, store, gatewayId) {
|
|
|
1000
1182
|
return { status: 200, body: { saved: true } };
|
|
1001
1183
|
}
|
|
1002
1184
|
async function handleSaveSyncRules(raw, store, gatewayId) {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
} catch {
|
|
1007
|
-
return { status: 400, body: { error: "Invalid JSON body" } };
|
|
1185
|
+
const parsed = parseJson(raw);
|
|
1186
|
+
if (!parsed.ok) {
|
|
1187
|
+
return { status: parsed.error.status, body: { error: parsed.error.message } };
|
|
1008
1188
|
}
|
|
1189
|
+
const config = parsed.value;
|
|
1009
1190
|
const validation = validateSyncRules(config);
|
|
1010
1191
|
if (!validation.ok) {
|
|
1011
1192
|
return { status: 400, body: { error: validation.error.message } };
|
|
@@ -1014,12 +1195,11 @@ async function handleSaveSyncRules(raw, store, gatewayId) {
|
|
|
1014
1195
|
return { status: 200, body: { saved: true } };
|
|
1015
1196
|
}
|
|
1016
1197
|
async function handleRegisterConnector(raw, store) {
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
body
|
|
1020
|
-
} catch {
|
|
1021
|
-
return { status: 400, body: { error: "Invalid JSON body" } };
|
|
1198
|
+
const parsed = parseJson(raw);
|
|
1199
|
+
if (!parsed.ok) {
|
|
1200
|
+
return { status: parsed.error.status, body: { error: parsed.error.message } };
|
|
1022
1201
|
}
|
|
1202
|
+
const body = parsed.value;
|
|
1023
1203
|
const validation = validateConnectorConfig(body);
|
|
1024
1204
|
if (!validation.ok) {
|
|
1025
1205
|
return { status: 400, body: { error: validation.error.message } };
|
|
@@ -1061,17 +1241,17 @@ function handleMetrics(gateway, extra) {
|
|
|
1061
1241
|
|
|
1062
1242
|
// ../gateway/src/schema-manager.ts
|
|
1063
1243
|
var SchemaManager = class {
|
|
1064
|
-
|
|
1065
|
-
version;
|
|
1066
|
-
allowedColumns;
|
|
1244
|
+
state;
|
|
1067
1245
|
constructor(schema, version) {
|
|
1068
|
-
this.
|
|
1069
|
-
|
|
1070
|
-
|
|
1246
|
+
this.state = {
|
|
1247
|
+
schema,
|
|
1248
|
+
version: version ?? 1,
|
|
1249
|
+
allowedColumns: new Set(schema.columns.map((c) => c.name))
|
|
1250
|
+
};
|
|
1071
1251
|
}
|
|
1072
1252
|
/** Get the current schema and version. */
|
|
1073
1253
|
getSchema() {
|
|
1074
|
-
return { schema: this.
|
|
1254
|
+
return { schema: this.state.schema, version: this.state.version };
|
|
1075
1255
|
}
|
|
1076
1256
|
/**
|
|
1077
1257
|
* Validate that a delta's columns are compatible with the current schema.
|
|
@@ -1084,10 +1264,10 @@ var SchemaManager = class {
|
|
|
1084
1264
|
return Ok(void 0);
|
|
1085
1265
|
}
|
|
1086
1266
|
for (const col of delta.columns) {
|
|
1087
|
-
if (!this.allowedColumns.has(col.column)) {
|
|
1267
|
+
if (!this.state.allowedColumns.has(col.column)) {
|
|
1088
1268
|
return Err(
|
|
1089
1269
|
new SchemaError(
|
|
1090
|
-
`Unknown column "${col.column}" in delta for table "${delta.table}". Schema version ${this.version} does not include this column.`
|
|
1270
|
+
`Unknown column "${col.column}" in delta for table "${delta.table}". Schema version ${this.state.version} does not include this column.`
|
|
1091
1271
|
)
|
|
1092
1272
|
);
|
|
1093
1273
|
}
|
|
@@ -1101,10 +1281,10 @@ var SchemaManager = class {
|
|
|
1101
1281
|
* returns a SchemaError.
|
|
1102
1282
|
*/
|
|
1103
1283
|
evolveSchema(newSchema) {
|
|
1104
|
-
if (newSchema.table !== this.
|
|
1284
|
+
if (newSchema.table !== this.state.schema.table) {
|
|
1105
1285
|
return Err(new SchemaError("Cannot evolve schema: table name mismatch"));
|
|
1106
1286
|
}
|
|
1107
|
-
const oldColumnMap = new Map(this.
|
|
1287
|
+
const oldColumnMap = new Map(this.state.schema.columns.map((c) => [c.name, c.type]));
|
|
1108
1288
|
const newColumnMap = new Map(newSchema.columns.map((c) => [c.name, c.type]));
|
|
1109
1289
|
for (const [name] of oldColumnMap) {
|
|
1110
1290
|
if (!newColumnMap.has(name)) {
|
|
@@ -1125,14 +1305,18 @@ var SchemaManager = class {
|
|
|
1125
1305
|
);
|
|
1126
1306
|
}
|
|
1127
1307
|
}
|
|
1128
|
-
this.
|
|
1129
|
-
this.
|
|
1130
|
-
|
|
1131
|
-
|
|
1308
|
+
const newVersion = this.state.version + 1;
|
|
1309
|
+
this.state = {
|
|
1310
|
+
schema: newSchema,
|
|
1311
|
+
version: newVersion,
|
|
1312
|
+
allowedColumns: new Set(newSchema.columns.map((c) => c.name))
|
|
1313
|
+
};
|
|
1314
|
+
return Ok({ version: newVersion });
|
|
1132
1315
|
}
|
|
1133
1316
|
};
|
|
1134
1317
|
|
|
1135
1318
|
export {
|
|
1319
|
+
MemoryIdempotencyCache,
|
|
1136
1320
|
ActionDispatcher,
|
|
1137
1321
|
DeltaBuffer,
|
|
1138
1322
|
MemoryConfigStore,
|
|
@@ -1146,7 +1330,10 @@ export {
|
|
|
1146
1330
|
hlcRange,
|
|
1147
1331
|
flushEntries,
|
|
1148
1332
|
commitToCatalogue,
|
|
1333
|
+
FlushCoordinator,
|
|
1334
|
+
SourceRegistry,
|
|
1149
1335
|
SyncGateway,
|
|
1336
|
+
parseJson,
|
|
1150
1337
|
validatePushBody,
|
|
1151
1338
|
parsePullParams,
|
|
1152
1339
|
validateActionBody,
|
|
@@ -1166,4 +1353,4 @@ export {
|
|
|
1166
1353
|
handleMetrics,
|
|
1167
1354
|
SchemaManager
|
|
1168
1355
|
};
|
|
1169
|
-
//# sourceMappingURL=chunk-
|
|
1356
|
+
//# sourceMappingURL=chunk-FIIHPQMQ.js.map
|