@spooky-sync/core 0.0.1-canary.4 → 0.0.1-canary.41
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/index.d.ts +18 -367
- package/dist/index.js +268 -287
- package/dist/otel/index.d.ts +21 -0
- package/dist/otel/index.js +86 -0
- package/dist/types.d.ts +361 -0
- package/package.json +34 -5
- package/skills/sp00ky-core/SKILL.md +258 -0
- package/skills/sp00ky-core/references/auth.md +98 -0
- package/skills/sp00ky-core/references/config.md +76 -0
- package/src/events/events.test.ts +2 -1
- package/src/index.ts +1 -1
- package/src/modules/auth/events/index.ts +2 -1
- package/src/modules/auth/index.ts +17 -20
- package/src/modules/cache/index.ts +23 -23
- package/src/modules/cache/types.ts +2 -2
- package/src/modules/data/index.ts +61 -43
- package/src/modules/devtools/index.ts +23 -20
- package/src/modules/sync/engine.ts +31 -21
- package/src/modules/sync/events/index.ts +3 -2
- package/src/modules/sync/queue/queue-down.ts +5 -4
- package/src/modules/sync/queue/queue-up.ts +14 -13
- package/src/modules/sync/scheduler.ts +2 -2
- package/src/modules/sync/sync.ts +47 -35
- package/src/modules/sync/utils.test.ts +2 -2
- package/src/modules/sync/utils.ts +15 -17
- package/src/otel/index.ts +127 -0
- package/src/services/database/database.ts +11 -11
- package/src/services/database/events/index.ts +2 -1
- package/src/services/database/local-migrator.ts +21 -21
- package/src/services/database/local.ts +16 -15
- package/src/services/database/remote.ts +13 -13
- package/src/services/logger/index.ts +6 -101
- package/src/services/persistence/localstorage.ts +2 -2
- package/src/services/persistence/resilient.ts +34 -0
- package/src/services/persistence/surrealdb.ts +9 -9
- package/src/services/stream-processor/index.ts +32 -31
- package/src/services/stream-processor/stream-processor.test.ts +1 -1
- package/src/services/stream-processor/wasm-types.ts +2 -2
- package/src/{spooky.ts → sp00ky.ts} +40 -38
- package/src/types.ts +18 -11
- package/src/utils/index.ts +10 -10
- package/src/utils/parser.ts +2 -1
- package/src/utils/surql.ts +15 -15
- package/src/utils/withRetry.test.ts +1 -1
- package/tsdown.config.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -2,12 +2,7 @@ import { DateTime, Duration, RecordId, Surreal, Uuid, applyDiagnostics, createRe
|
|
|
2
2
|
import { QueryBuilder, RecordId as RecordId$1 } from "@spooky-sync/query-builder";
|
|
3
3
|
import { createWasmWorkerEngines } from "@surrealdb/wasm";
|
|
4
4
|
import pino from "pino";
|
|
5
|
-
import {
|
|
6
|
-
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-proto";
|
|
7
|
-
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
8
|
-
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
9
|
-
import { createContextKey } from "@opentelemetry/api";
|
|
10
|
-
import init, { SpookyProcessor } from "@spooky-sync/ssp-wasm";
|
|
5
|
+
import init, { Sp00kyProcessor } from "@spooky-sync/ssp-wasm";
|
|
11
6
|
|
|
12
7
|
//#region src/utils/surql.ts
|
|
13
8
|
const surql = {
|
|
@@ -33,7 +28,7 @@ const surql = {
|
|
|
33
28
|
return `SELECT ${returnValues.join(",")} FROM ONLY $${idVar}`;
|
|
34
29
|
},
|
|
35
30
|
selectByFieldsAnd(table, whereVar, returnValues) {
|
|
36
|
-
return `SELECT ${returnValues.map((
|
|
31
|
+
return `SELECT ${returnValues.map((rv) => typeof rv === "string" ? rv : `${rv.field} as ${rv.alias}`).join(",")} FROM ${table} WHERE ${whereVar.map((wv) => typeof wv === "string" ? `${wv} = $${wv}` : `${wv.field} = $${wv.variable}`).join(" AND ")}`;
|
|
37
32
|
},
|
|
38
33
|
create(idVar, dataVar) {
|
|
39
34
|
return `CREATE ONLY $${idVar} CONTENT $${dataVar}`;
|
|
@@ -48,7 +43,7 @@ const surql = {
|
|
|
48
43
|
return `UPDATE ONLY $${idVar} MERGE $${dataVar}`;
|
|
49
44
|
},
|
|
50
45
|
updateSet(idVar, keyDataVar) {
|
|
51
|
-
return `UPDATE $${idVar} SET ${keyDataVar.map((
|
|
46
|
+
return `UPDATE $${idVar} SET ${keyDataVar.map((kdv) => typeof kdv === "string" ? `${kdv} = $${kdv}` : "statement" in kdv ? kdv.statement : `${kdv.key} = $${kdv.variable}`).join(", ")}`;
|
|
52
47
|
},
|
|
53
48
|
delete(idVar) {
|
|
54
49
|
return `DELETE $${idVar}`;
|
|
@@ -162,7 +157,7 @@ function parseDuration(duration) {
|
|
|
162
157
|
if (typeof duration !== "string") return 6e5;
|
|
163
158
|
const match = duration.match(/^(\d+)([smh])$/);
|
|
164
159
|
if (!match) return 6e5;
|
|
165
|
-
const val = parseInt(match[1], 10);
|
|
160
|
+
const val = Number.parseInt(match[1], 10);
|
|
166
161
|
switch (match[2]) {
|
|
167
162
|
case "s": return val * 1e3;
|
|
168
163
|
case "h": return val * 36e5;
|
|
@@ -188,7 +183,7 @@ async function withRetry(logger, operation, retries = 3, delayMs = 100) {
|
|
|
188
183
|
attempt: i + 1,
|
|
189
184
|
retries,
|
|
190
185
|
error: msg,
|
|
191
|
-
Category: "
|
|
186
|
+
Category: "sp00ky-client::utils::withRetry"
|
|
192
187
|
}, "Retrying DB operation");
|
|
193
188
|
await new Promise((res) => setTimeout(res, delayMs * (i + 1)));
|
|
194
189
|
continue;
|
|
@@ -221,7 +216,7 @@ var DataModule = class {
|
|
|
221
216
|
this.logger = logger.child({ service: "DataModule" });
|
|
222
217
|
}
|
|
223
218
|
async init() {
|
|
224
|
-
this.logger.info({ Category: "
|
|
219
|
+
this.logger.info({ Category: "sp00ky-client::DataModule::init" }, "DataModule initialized");
|
|
225
220
|
}
|
|
226
221
|
/**
|
|
227
222
|
* Register a query and return its hash for subscriptions
|
|
@@ -233,27 +228,27 @@ var DataModule = class {
|
|
|
233
228
|
});
|
|
234
229
|
this.logger.debug({
|
|
235
230
|
hash,
|
|
236
|
-
Category: "
|
|
231
|
+
Category: "sp00ky-client::DataModule::query"
|
|
237
232
|
}, "Query Initialization: started");
|
|
238
|
-
const recordId = new RecordId("
|
|
233
|
+
const recordId = new RecordId("_00_query", hash);
|
|
239
234
|
if (this.activeQueries.has(hash)) {
|
|
240
235
|
this.logger.debug({
|
|
241
236
|
hash,
|
|
242
|
-
Category: "
|
|
237
|
+
Category: "sp00ky-client::DataModule::query"
|
|
243
238
|
}, "Query Initialization: exists, returning");
|
|
244
239
|
return hash;
|
|
245
240
|
}
|
|
246
241
|
if (this.pendingQueries.has(hash)) {
|
|
247
242
|
this.logger.debug({
|
|
248
243
|
hash,
|
|
249
|
-
Category: "
|
|
244
|
+
Category: "sp00ky-client::DataModule::query"
|
|
250
245
|
}, "Query Initialization: pending, waiting for existing creation");
|
|
251
246
|
await this.pendingQueries.get(hash);
|
|
252
247
|
return hash;
|
|
253
248
|
}
|
|
254
249
|
this.logger.debug({
|
|
255
250
|
hash,
|
|
256
|
-
Category: "
|
|
251
|
+
Category: "sp00ky-client::DataModule::query"
|
|
257
252
|
}, "Query Initialization: not found, creating new query");
|
|
258
253
|
const promise = this.createAndRegisterQuery(hash, recordId, surqlString, params, ttl, tableName);
|
|
259
254
|
this.pendingQueries.set(hash, promise);
|
|
@@ -311,7 +306,7 @@ var DataModule = class {
|
|
|
311
306
|
if (!queryState) {
|
|
312
307
|
this.logger.warn({
|
|
313
308
|
queryHash,
|
|
314
|
-
Category: "
|
|
309
|
+
Category: "sp00ky-client::DataModule::onStreamUpdate"
|
|
315
310
|
}, "Received update for unknown query. Skipping...");
|
|
316
311
|
return;
|
|
317
312
|
}
|
|
@@ -329,7 +324,7 @@ var DataModule = class {
|
|
|
329
324
|
if (prevJson === newJson) {
|
|
330
325
|
this.logger.debug({
|
|
331
326
|
queryHash,
|
|
332
|
-
Category: "
|
|
327
|
+
Category: "sp00ky-client::DataModule::onStreamUpdate"
|
|
333
328
|
}, "Query records unchanged, skipping notification");
|
|
334
329
|
return;
|
|
335
330
|
}
|
|
@@ -339,13 +334,13 @@ var DataModule = class {
|
|
|
339
334
|
this.logger.debug({
|
|
340
335
|
queryHash,
|
|
341
336
|
recordCount: records?.length,
|
|
342
|
-
Category: "
|
|
337
|
+
Category: "sp00ky-client::DataModule::onStreamUpdate"
|
|
343
338
|
}, "Query updated from stream");
|
|
344
339
|
} catch (err) {
|
|
345
340
|
this.logger.error({
|
|
346
341
|
err,
|
|
347
342
|
queryHash,
|
|
348
|
-
Category: "
|
|
343
|
+
Category: "sp00ky-client::DataModule::onStreamUpdate"
|
|
349
344
|
}, "Failed to fetch records for stream update");
|
|
350
345
|
}
|
|
351
346
|
}
|
|
@@ -372,7 +367,7 @@ var DataModule = class {
|
|
|
372
367
|
if (!queryState) {
|
|
373
368
|
this.logger.warn({
|
|
374
369
|
id,
|
|
375
|
-
Category: "
|
|
370
|
+
Category: "sp00ky-client::DataModule::updateQueryLocalArray"
|
|
376
371
|
}, "Query to update local array not found");
|
|
377
372
|
return;
|
|
378
373
|
}
|
|
@@ -387,7 +382,7 @@ var DataModule = class {
|
|
|
387
382
|
if (!queryState) {
|
|
388
383
|
this.logger.warn({
|
|
389
384
|
hash,
|
|
390
|
-
Category: "
|
|
385
|
+
Category: "sp00ky-client::DataModule::updateQueryRemoteArray"
|
|
391
386
|
}, "Query to update remote array not found");
|
|
392
387
|
return;
|
|
393
388
|
}
|
|
@@ -431,6 +426,7 @@ var DataModule = class {
|
|
|
431
426
|
max_retries: options?.max_retries ?? 3,
|
|
432
427
|
retry_strategy: options?.retry_strategy ?? "linear"
|
|
433
428
|
};
|
|
429
|
+
if (options?.timeout != null) record.timeout = options.timeout;
|
|
434
430
|
if (options?.assignedTo) record.assigned_to = options.assignedTo;
|
|
435
431
|
const recordId = `${tableName}:${generateId()}`;
|
|
436
432
|
await this.create(recordId, record);
|
|
@@ -444,7 +440,7 @@ var DataModule = class {
|
|
|
444
440
|
if (!tableSchema) throw new Error(`Table ${tableName} not found`);
|
|
445
441
|
const rid = parseRecordIdString(id);
|
|
446
442
|
const params = parseParams(tableSchema.columns, data);
|
|
447
|
-
const mutationId = parseRecordIdString(`
|
|
443
|
+
const mutationId = parseRecordIdString(`_00_pending_mutations:${Date.now()}`);
|
|
448
444
|
const dataKeys = Object.keys(params).map((key) => ({
|
|
449
445
|
key,
|
|
450
446
|
variable: `data_${key}`
|
|
@@ -474,7 +470,7 @@ var DataModule = class {
|
|
|
474
470
|
for (const callback of this.mutationCallbacks) callback([mutationEvent]);
|
|
475
471
|
this.logger.debug({
|
|
476
472
|
id,
|
|
477
|
-
Category: "
|
|
473
|
+
Category: "sp00ky-client::DataModule::create"
|
|
478
474
|
}, "Record created");
|
|
479
475
|
return target;
|
|
480
476
|
}
|
|
@@ -487,10 +483,10 @@ var DataModule = class {
|
|
|
487
483
|
if (!tableSchema) throw new Error(`Table ${tableName} not found`);
|
|
488
484
|
const rid = parseRecordIdString(id);
|
|
489
485
|
const params = parseParams(tableSchema.columns, data);
|
|
490
|
-
const mutationId = parseRecordIdString(`
|
|
486
|
+
const mutationId = parseRecordIdString(`_00_pending_mutations:${Date.now()}`);
|
|
491
487
|
const [beforeRecord] = await withRetry(this.logger, () => this.local.query("SELECT * FROM ONLY $id", { id: rid }));
|
|
492
488
|
const query = surql.seal(surql.tx([
|
|
493
|
-
surql.updateSet("id", [{ statement: "
|
|
489
|
+
surql.updateSet("id", [{ statement: "_00_rv += 1" }]),
|
|
494
490
|
surql.let("updated", surql.updateMerge("id", "data")),
|
|
495
491
|
surql.createMutation("update", "mid", "id", "data"),
|
|
496
492
|
surql.returnObject([{
|
|
@@ -505,14 +501,14 @@ var DataModule = class {
|
|
|
505
501
|
}));
|
|
506
502
|
const updatedFields = { id: target.id };
|
|
507
503
|
for (const key of Object.keys(data)) if (key in target) updatedFields[key] = target[key];
|
|
508
|
-
if ("
|
|
504
|
+
if ("_00_rv" in target) updatedFields._00_rv = target._00_rv;
|
|
509
505
|
this.replaceRecordInQueries(updatedFields);
|
|
510
506
|
const parsedRecord = parseParams(tableSchema.columns, target);
|
|
511
507
|
await this.cache.save({
|
|
512
508
|
table,
|
|
513
509
|
op: "UPDATE",
|
|
514
510
|
record: parsedRecord,
|
|
515
|
-
version: target.
|
|
511
|
+
version: target._00_rv
|
|
516
512
|
}, true);
|
|
517
513
|
const pushEventOptions = parseUpdateOptions(id, data, options);
|
|
518
514
|
const mutationEvent = {
|
|
@@ -527,7 +523,7 @@ var DataModule = class {
|
|
|
527
523
|
for (const callback of this.mutationCallbacks) callback([mutationEvent]);
|
|
528
524
|
this.logger.debug({
|
|
529
525
|
id,
|
|
530
|
-
Category: "
|
|
526
|
+
Category: "sp00ky-client::DataModule::update"
|
|
531
527
|
}, "Record updated");
|
|
532
528
|
return target;
|
|
533
529
|
}
|
|
@@ -538,13 +534,16 @@ var DataModule = class {
|
|
|
538
534
|
const tableName = extractTablePart(id);
|
|
539
535
|
if (!this.schema.tables.find((t) => t.name === tableName)) throw new Error(`Table ${tableName} not found`);
|
|
540
536
|
const rid = parseRecordIdString(id);
|
|
541
|
-
const mutationId = parseRecordIdString(`
|
|
537
|
+
const mutationId = parseRecordIdString(`_00_pending_mutations:${Date.now()}`);
|
|
538
|
+
const [beforeRecords] = await this.local.query("SELECT * FROM ONLY $id", { id: rid });
|
|
539
|
+
const beforeRecord = beforeRecords ?? {};
|
|
542
540
|
const query = surql.seal(surql.tx([surql.delete("id"), surql.createMutation("delete", "mid", "id")]));
|
|
543
541
|
await withRetry(this.logger, () => this.local.execute(query, {
|
|
544
542
|
id: rid,
|
|
545
543
|
mid: mutationId
|
|
546
544
|
}));
|
|
547
|
-
await this.cache.delete(table, id, true);
|
|
545
|
+
await this.cache.delete(table, id, true, beforeRecord);
|
|
546
|
+
for (const [queryHash, queryState] of this.activeQueries) if (queryState.config.tableName === tableName) await this.notifyQuerySynced(queryHash);
|
|
548
547
|
const mutationEvent = {
|
|
549
548
|
type: "delete",
|
|
550
549
|
mutation_id: mutationId,
|
|
@@ -553,7 +552,7 @@ var DataModule = class {
|
|
|
553
552
|
for (const callback of this.mutationCallbacks) callback([mutationEvent]);
|
|
554
553
|
this.logger.debug({
|
|
555
554
|
id,
|
|
556
|
-
Category: "
|
|
555
|
+
Category: "sp00ky-client::DataModule::delete"
|
|
557
556
|
}, "Record deleted");
|
|
558
557
|
}
|
|
559
558
|
/**
|
|
@@ -568,14 +567,14 @@ var DataModule = class {
|
|
|
568
567
|
this.logger.info({
|
|
569
568
|
id,
|
|
570
569
|
tableName,
|
|
571
|
-
Category: "
|
|
570
|
+
Category: "sp00ky-client::DataModule::rollbackCreate"
|
|
572
571
|
}, "Rolled back optimistic create");
|
|
573
572
|
} catch (err) {
|
|
574
573
|
this.logger.error({
|
|
575
574
|
err,
|
|
576
575
|
id,
|
|
577
576
|
tableName,
|
|
578
|
-
Category: "
|
|
577
|
+
Category: "sp00ky-client::DataModule::rollbackCreate"
|
|
579
578
|
}, "Failed to rollback create");
|
|
580
579
|
}
|
|
581
580
|
}
|
|
@@ -596,20 +595,20 @@ var DataModule = class {
|
|
|
596
595
|
table: tableName,
|
|
597
596
|
op: "UPDATE",
|
|
598
597
|
record: parsedRecord,
|
|
599
|
-
version: beforeRecord.
|
|
598
|
+
version: beforeRecord._00_rv || 1
|
|
600
599
|
}, true);
|
|
601
600
|
await this.replaceRecordInQueries(beforeRecord);
|
|
602
601
|
this.logger.info({
|
|
603
602
|
id,
|
|
604
603
|
tableName,
|
|
605
|
-
Category: "
|
|
604
|
+
Category: "sp00ky-client::DataModule::rollbackUpdate"
|
|
606
605
|
}, "Rolled back optimistic update");
|
|
607
606
|
} catch (err) {
|
|
608
607
|
this.logger.error({
|
|
609
608
|
err,
|
|
610
609
|
id,
|
|
611
610
|
tableName,
|
|
612
|
-
Category: "
|
|
611
|
+
Category: "sp00ky-client::DataModule::rollbackUpdate"
|
|
613
612
|
}, "Failed to rollback update");
|
|
614
613
|
}
|
|
615
614
|
}
|
|
@@ -654,7 +653,7 @@ var DataModule = class {
|
|
|
654
653
|
hash,
|
|
655
654
|
tableName,
|
|
656
655
|
recordCount: queryState.records.length,
|
|
657
|
-
Category: "
|
|
656
|
+
Category: "sp00ky-client::DataModule::query"
|
|
658
657
|
}, "Query registered");
|
|
659
658
|
return hash;
|
|
660
659
|
}
|
|
@@ -689,7 +688,7 @@ var DataModule = class {
|
|
|
689
688
|
} catch (err) {
|
|
690
689
|
this.logger.warn({
|
|
691
690
|
err,
|
|
692
|
-
Category: "
|
|
691
|
+
Category: "sp00ky-client::DataModule::createNewQuery"
|
|
693
692
|
}, "Failed to load initial cached records");
|
|
694
693
|
}
|
|
695
694
|
return {
|
|
@@ -712,7 +711,7 @@ var DataModule = class {
|
|
|
712
711
|
queryState.ttlTimer = setTimeout(() => {
|
|
713
712
|
this.logger.debug({
|
|
714
713
|
id: encodeRecordId(queryState.config.id),
|
|
715
|
-
Category: "
|
|
714
|
+
Category: "sp00ky-client::DataModule::startTTLHeartbeat"
|
|
716
715
|
}, "TTL heartbeat");
|
|
717
716
|
this.startTTLHeartbeat(queryState);
|
|
718
717
|
}, heartbeatTime);
|
|
@@ -744,7 +743,7 @@ function parseUpdateOptions(id, data, options) {
|
|
|
744
743
|
let pushEventOptions = {};
|
|
745
744
|
if (options?.debounced) pushEventOptions = { debounced: {
|
|
746
745
|
delay: options.debounced !== true ? options.debounced?.delay ?? 200 : 200,
|
|
747
|
-
key: (options.debounced !== true ? options.debounced?.key ?? id : id) === "recordId_x_fields" ? `${id}::${Object.keys(data).
|
|
746
|
+
key: (options.debounced !== true ? options.debounced?.key ?? id : id) === "recordId_x_fields" ? `${id}::${Object.keys(data).toSorted().join("#")}` : id
|
|
748
747
|
} };
|
|
749
748
|
return pushEventOptions;
|
|
750
749
|
}
|
|
@@ -781,7 +780,7 @@ var AbstractDatabaseService = class {
|
|
|
781
780
|
this.logger.debug({
|
|
782
781
|
query,
|
|
783
782
|
vars,
|
|
784
|
-
Category: "
|
|
783
|
+
Category: "sp00ky-client::Database::query"
|
|
785
784
|
}, "Executing query");
|
|
786
785
|
const result = await this.client.query(query, vars);
|
|
787
786
|
const duration = performance.now() - startTime;
|
|
@@ -796,7 +795,7 @@ var AbstractDatabaseService = class {
|
|
|
796
795
|
this.logger.trace({
|
|
797
796
|
query,
|
|
798
797
|
result,
|
|
799
|
-
Category: "
|
|
798
|
+
Category: "sp00ky-client::Database::query"
|
|
800
799
|
}, "Query executed successfully");
|
|
801
800
|
} catch (err) {
|
|
802
801
|
const duration = performance.now() - startTime;
|
|
@@ -812,7 +811,7 @@ var AbstractDatabaseService = class {
|
|
|
812
811
|
query,
|
|
813
812
|
vars,
|
|
814
813
|
err,
|
|
815
|
-
Category: "
|
|
814
|
+
Category: "sp00ky-client::Database::query"
|
|
816
815
|
}, "Query execution failed");
|
|
817
816
|
reject(err);
|
|
818
817
|
}
|
|
@@ -824,7 +823,7 @@ var AbstractDatabaseService = class {
|
|
|
824
823
|
return query.extract(raw);
|
|
825
824
|
}
|
|
826
825
|
async close() {
|
|
827
|
-
this.logger.info({ Category: "
|
|
826
|
+
this.logger.info({ Category: "sp00ky-client::Database::close" }, "Closing database connection");
|
|
828
827
|
await this.client.close();
|
|
829
828
|
}
|
|
830
829
|
};
|
|
@@ -1004,7 +1003,7 @@ var LocalDatabaseService = class extends AbstractDatabaseService {
|
|
|
1004
1003
|
type,
|
|
1005
1004
|
phase,
|
|
1006
1005
|
service: "surrealdb:local",
|
|
1007
|
-
Category: "
|
|
1006
|
+
Category: "sp00ky-client::LocalDatabaseService::diagnostics"
|
|
1008
1007
|
}, `Local SurrealDB diagnostics captured ${type}:${phase}`);
|
|
1009
1008
|
})
|
|
1010
1009
|
}), logger, events);
|
|
@@ -1018,30 +1017,30 @@ var LocalDatabaseService = class extends AbstractDatabaseService {
|
|
|
1018
1017
|
this.logger.info({
|
|
1019
1018
|
namespace,
|
|
1020
1019
|
database,
|
|
1021
|
-
Category: "
|
|
1020
|
+
Category: "sp00ky-client::LocalDatabaseService::connect"
|
|
1022
1021
|
}, "Connecting to local database");
|
|
1023
1022
|
try {
|
|
1024
|
-
const storeUrl = (this.getConfig().store ?? "memory") === "memory" ? "mem://" : "indxdb://
|
|
1023
|
+
const storeUrl = (this.getConfig().store ?? "memory") === "memory" ? "mem://" : "indxdb://sp00ky";
|
|
1025
1024
|
this.logger.debug({
|
|
1026
1025
|
storeUrl,
|
|
1027
|
-
Category: "
|
|
1026
|
+
Category: "sp00ky-client::LocalDatabaseService::connect"
|
|
1028
1027
|
}, "[LocalDatabaseService] Calling client.connect");
|
|
1029
1028
|
await this.client.connect(storeUrl, {});
|
|
1030
1029
|
this.logger.debug({
|
|
1031
1030
|
namespace,
|
|
1032
1031
|
database,
|
|
1033
|
-
Category: "
|
|
1032
|
+
Category: "sp00ky-client::LocalDatabaseService::connect"
|
|
1034
1033
|
}, "[LocalDatabaseService] client.connect returned. Calling client.use");
|
|
1035
1034
|
await this.client.use({
|
|
1036
1035
|
namespace,
|
|
1037
1036
|
database
|
|
1038
1037
|
});
|
|
1039
|
-
this.logger.debug({ Category: "
|
|
1040
|
-
this.logger.info({ Category: "
|
|
1038
|
+
this.logger.debug({ Category: "sp00ky-client::LocalDatabaseService::connect" }, "[LocalDatabaseService] client.use returned");
|
|
1039
|
+
this.logger.info({ Category: "sp00ky-client::LocalDatabaseService::connect" }, "Connected to local database");
|
|
1041
1040
|
} catch (err) {
|
|
1042
1041
|
this.logger.error({
|
|
1043
1042
|
err,
|
|
1044
|
-
Category: "
|
|
1043
|
+
Category: "sp00ky-client::LocalDatabaseService::connect"
|
|
1045
1044
|
}, "Failed to connect to local database");
|
|
1046
1045
|
throw err;
|
|
1047
1046
|
}
|
|
@@ -1062,7 +1061,7 @@ var RemoteDatabaseService = class extends AbstractDatabaseService {
|
|
|
1062
1061
|
type,
|
|
1063
1062
|
phase,
|
|
1064
1063
|
service: "surrealdb:remote",
|
|
1065
|
-
Category: "
|
|
1064
|
+
Category: "sp00ky-client::RemoteDatabaseService::diagnostics"
|
|
1066
1065
|
}, `Remote SurrealDB diagnostics captured ${type}:${phase}`);
|
|
1067
1066
|
}) }), logger, events);
|
|
1068
1067
|
this.config = config;
|
|
@@ -1077,7 +1076,7 @@ var RemoteDatabaseService = class extends AbstractDatabaseService {
|
|
|
1077
1076
|
endpoint,
|
|
1078
1077
|
namespace,
|
|
1079
1078
|
database,
|
|
1080
|
-
Category: "
|
|
1079
|
+
Category: "sp00ky-client::RemoteDatabaseService::connect"
|
|
1081
1080
|
}, "Connecting to remote database");
|
|
1082
1081
|
try {
|
|
1083
1082
|
await this.client.connect(endpoint);
|
|
@@ -1086,18 +1085,18 @@ var RemoteDatabaseService = class extends AbstractDatabaseService {
|
|
|
1086
1085
|
database
|
|
1087
1086
|
});
|
|
1088
1087
|
if (token) {
|
|
1089
|
-
this.logger.debug({ Category: "
|
|
1088
|
+
this.logger.debug({ Category: "sp00ky-client::RemoteDatabaseService::connect" }, "Authenticating with token");
|
|
1090
1089
|
await this.client.authenticate(token);
|
|
1091
1090
|
}
|
|
1092
|
-
this.logger.info({ Category: "
|
|
1091
|
+
this.logger.info({ Category: "sp00ky-client::RemoteDatabaseService::connect" }, "Connected to remote database");
|
|
1093
1092
|
} catch (err) {
|
|
1094
1093
|
this.logger.error({
|
|
1095
1094
|
err,
|
|
1096
|
-
Category: "
|
|
1095
|
+
Category: "sp00ky-client::RemoteDatabaseService::connect"
|
|
1097
1096
|
}, "Failed to connect to remote database");
|
|
1098
1097
|
throw err;
|
|
1099
1098
|
}
|
|
1100
|
-
} else this.logger.warn({ Category: "
|
|
1099
|
+
} else this.logger.warn({ Category: "sp00ky-client::RemoteDatabaseService::connect" }, "No endpoint configured for remote database");
|
|
1101
1100
|
}
|
|
1102
1101
|
async signin(params) {
|
|
1103
1102
|
return this.client.signin(params);
|
|
@@ -1115,70 +1114,14 @@ var RemoteDatabaseService = class extends AbstractDatabaseService {
|
|
|
1115
1114
|
|
|
1116
1115
|
//#endregion
|
|
1117
1116
|
//#region src/services/logger/index.ts
|
|
1118
|
-
|
|
1119
|
-
function mapLevelToSeverityNumber(level) {
|
|
1120
|
-
switch (level) {
|
|
1121
|
-
case "trace": return 1;
|
|
1122
|
-
case "debug": return 5;
|
|
1123
|
-
case "info": return 9;
|
|
1124
|
-
case "warn": return 13;
|
|
1125
|
-
case "error": return 17;
|
|
1126
|
-
case "fatal": return 21;
|
|
1127
|
-
default: return 9;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
function createLogger(level = "info", otelEndpoint) {
|
|
1117
|
+
function createLogger(level = "info", transmit) {
|
|
1131
1118
|
const browserConfig = {
|
|
1132
1119
|
asObject: true,
|
|
1133
1120
|
write: (o) => {
|
|
1134
1121
|
console.log(JSON.stringify(o));
|
|
1135
1122
|
}
|
|
1136
1123
|
};
|
|
1137
|
-
if (
|
|
1138
|
-
const loggerProvider = new LoggerProvider({
|
|
1139
|
-
resource: resourceFromAttributes({ [ATTR_SERVICE_NAME]: "spooky-client" }),
|
|
1140
|
-
processors: [new BatchLogRecordProcessor(new OTLPLogExporter({ url: otelEndpoint }))]
|
|
1141
|
-
});
|
|
1142
|
-
const otelLogger = {};
|
|
1143
|
-
const getOtelLogger = (category) => {
|
|
1144
|
-
if (!otelLogger[category]) otelLogger[category] = loggerProvider.getLogger(category);
|
|
1145
|
-
return otelLogger[category];
|
|
1146
|
-
};
|
|
1147
|
-
browserConfig.transmit = {
|
|
1148
|
-
level,
|
|
1149
|
-
send: (levelLabel, logEvent) => {
|
|
1150
|
-
try {
|
|
1151
|
-
const messages = [...logEvent.messages];
|
|
1152
|
-
const severityNumber = mapLevelToSeverityNumber(levelLabel);
|
|
1153
|
-
let body = "";
|
|
1154
|
-
const msg = messages.pop();
|
|
1155
|
-
if (typeof msg === "string") body = msg;
|
|
1156
|
-
else if (msg) body = JSON.stringify(msg);
|
|
1157
|
-
let category = "spooky-client::unknown";
|
|
1158
|
-
const attributes = {};
|
|
1159
|
-
for (const msg of messages) if (typeof msg === "object") {
|
|
1160
|
-
if (msg.Category) {
|
|
1161
|
-
category = msg.Category;
|
|
1162
|
-
delete msg.Category;
|
|
1163
|
-
}
|
|
1164
|
-
Object.assign(attributes, msg);
|
|
1165
|
-
}
|
|
1166
|
-
getOtelLogger(category).emit({
|
|
1167
|
-
severityNumber,
|
|
1168
|
-
severityText: levelLabel.toUpperCase(),
|
|
1169
|
-
body,
|
|
1170
|
-
attributes: {
|
|
1171
|
-
...logEvent.bindings[0],
|
|
1172
|
-
...attributes
|
|
1173
|
-
},
|
|
1174
|
-
timestamp: new Date(logEvent.ts)
|
|
1175
|
-
});
|
|
1176
|
-
} catch (e) {
|
|
1177
|
-
console.warn("Failed to transmit log to OTEL endpoint", e);
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
};
|
|
1181
|
-
}
|
|
1124
|
+
if (transmit) browserConfig.transmit = transmit;
|
|
1182
1125
|
return pino({
|
|
1183
1126
|
level,
|
|
1184
1127
|
browser: browserConfig
|
|
@@ -1203,25 +1146,25 @@ var LocalMigrator = class {
|
|
|
1203
1146
|
const hash = await sha1(schemaSurql);
|
|
1204
1147
|
const { database } = this.localDb.getConfig();
|
|
1205
1148
|
if (await this.isSchemaUpToDate(hash)) {
|
|
1206
|
-
this.logger.info({ Category: "
|
|
1149
|
+
this.logger.info({ Category: "sp00ky-client::LocalMigrator::provision" }, "[Provisioning] Schema is up to date, skipping migration");
|
|
1207
1150
|
return;
|
|
1208
1151
|
}
|
|
1209
1152
|
await this.recreateDatabase(database);
|
|
1210
|
-
const fullSchema = schemaSurql + "\n\n DEFINE TABLE IF NOT EXISTS
|
|
1153
|
+
const fullSchema = schemaSurql + "\n\n DEFINE TABLE IF NOT EXISTS _00_stream_processor_state SCHEMALESS PERMISSIONS FOR select, create, update, delete WHERE true;\n DEFINE TABLE IF NOT EXISTS _00_query SCHEMALESS PERMISSIONS FOR select, create, update, delete WHERE true;\n DEFINE TABLE IF NOT EXISTS _00_schema SCHEMALESS PERMISSIONS FOR select, create, update, delete WHERE true;\n DEFINE TABLE IF NOT EXISTS _00_pending_mutations SCHEMALESS PERMISSIONS FOR select, create, update, delete WHERE true;\n ";
|
|
1211
1154
|
const statements = this.splitStatements(fullSchema);
|
|
1212
1155
|
for (let i = 0; i < statements.length; i++) {
|
|
1213
1156
|
const statement = statements[i];
|
|
1214
1157
|
const cleanStatement = statement.replace(/--.*/g, "").trim();
|
|
1215
1158
|
if (cleanStatement.toUpperCase().startsWith("DEFINE INDEX")) {
|
|
1216
|
-
this.logger.warn({ Category: "
|
|
1159
|
+
this.logger.warn({ Category: "sp00ky-client::LocalMigrator::provision" }, `[Provisioning] Skipping index definition (WASM hang avoidance): ${cleanStatement.substring(0, 50)}...`);
|
|
1217
1160
|
continue;
|
|
1218
1161
|
}
|
|
1219
1162
|
try {
|
|
1220
|
-
this.logger.info({ Category: "
|
|
1163
|
+
this.logger.info({ Category: "sp00ky-client::LocalMigrator::provision" }, `[Provisioning] (${i + 1}/${statements.length}) Executing: ${statement.substring(0, 50)}...`);
|
|
1221
1164
|
await this.localDb.query(statement);
|
|
1222
|
-
this.logger.info({ Category: "
|
|
1165
|
+
this.logger.info({ Category: "sp00ky-client::LocalMigrator::provision" }, `[Provisioning] (${i + 1}/${statements.length}) Done`);
|
|
1223
1166
|
} catch (e) {
|
|
1224
|
-
this.logger.error({ Category: "
|
|
1167
|
+
this.logger.error({ Category: "sp00ky-client::LocalMigrator::provision" }, `[Provisioning] (${i + 1}/${statements.length}) Error executing statement: ${statement}`);
|
|
1225
1168
|
throw e;
|
|
1226
1169
|
}
|
|
1227
1170
|
}
|
|
@@ -1229,22 +1172,22 @@ var LocalMigrator = class {
|
|
|
1229
1172
|
}
|
|
1230
1173
|
async isSchemaUpToDate(hash) {
|
|
1231
1174
|
try {
|
|
1232
|
-
const [lastSchemaRecord] = await this.localDb.query(`SELECT hash, created_at FROM ONLY
|
|
1175
|
+
const [lastSchemaRecord] = await this.localDb.query(`SELECT hash, created_at FROM ONLY _00_schema ORDER BY created_at DESC LIMIT 1;`);
|
|
1233
1176
|
return lastSchemaRecord?.hash === hash;
|
|
1234
|
-
} catch (
|
|
1177
|
+
} catch (_error) {
|
|
1235
1178
|
return false;
|
|
1236
1179
|
}
|
|
1237
1180
|
}
|
|
1238
1181
|
async recreateDatabase(database) {
|
|
1239
1182
|
try {
|
|
1240
|
-
await this.localDb.query(`DEFINE DATABASE
|
|
1241
|
-
} catch (
|
|
1183
|
+
await this.localDb.query(`DEFINE DATABASE _00_temp;`);
|
|
1184
|
+
} catch (_e) {}
|
|
1242
1185
|
try {
|
|
1243
1186
|
await this.localDb.query(`
|
|
1244
|
-
USE DB
|
|
1187
|
+
USE DB _00_temp;
|
|
1245
1188
|
REMOVE DATABASE ${database};
|
|
1246
1189
|
`);
|
|
1247
|
-
} catch (
|
|
1190
|
+
} catch (_e) {}
|
|
1248
1191
|
await this.localDb.query(`
|
|
1249
1192
|
DEFINE DATABASE ${database};
|
|
1250
1193
|
USE DB ${database};
|
|
@@ -1302,7 +1245,7 @@ var LocalMigrator = class {
|
|
|
1302
1245
|
return statements;
|
|
1303
1246
|
}
|
|
1304
1247
|
async createHashRecord(hash) {
|
|
1305
|
-
await this.localDb.query(`UPSERT
|
|
1248
|
+
await this.localDb.query(`UPSERT _00_schema SET hash = $hash, created_at = time::now() WHERE hash = $hash;`, { hash });
|
|
1306
1249
|
}
|
|
1307
1250
|
};
|
|
1308
1251
|
|
|
@@ -1394,7 +1337,7 @@ var UpQueue = class {
|
|
|
1394
1337
|
this.logger.error({
|
|
1395
1338
|
error,
|
|
1396
1339
|
event,
|
|
1397
|
-
Category: "
|
|
1340
|
+
Category: "sp00ky-client::UpQueue::next"
|
|
1398
1341
|
}, "Network error processing mutation, re-queuing");
|
|
1399
1342
|
this.queue.unshift(event);
|
|
1400
1343
|
throw error;
|
|
@@ -1402,7 +1345,7 @@ var UpQueue = class {
|
|
|
1402
1345
|
this.logger.error({
|
|
1403
1346
|
error,
|
|
1404
1347
|
event,
|
|
1405
|
-
Category: "
|
|
1348
|
+
Category: "sp00ky-client::UpQueue::next"
|
|
1406
1349
|
}, "Application error processing mutation, rolling back");
|
|
1407
1350
|
try {
|
|
1408
1351
|
await this.removeEventFromDatabase(event.mutation_id);
|
|
@@ -1410,7 +1353,7 @@ var UpQueue = class {
|
|
|
1410
1353
|
this.logger.error({
|
|
1411
1354
|
error: removeError,
|
|
1412
1355
|
event,
|
|
1413
|
-
Category: "
|
|
1356
|
+
Category: "sp00ky-client::UpQueue::next"
|
|
1414
1357
|
}, "Failed to remove rolled-back mutation from database");
|
|
1415
1358
|
}
|
|
1416
1359
|
if (onRollback) try {
|
|
@@ -1419,7 +1362,7 @@ var UpQueue = class {
|
|
|
1419
1362
|
this.logger.error({
|
|
1420
1363
|
error: rollbackError,
|
|
1421
1364
|
event,
|
|
1422
|
-
Category: "
|
|
1365
|
+
Category: "sp00ky-client::UpQueue::next"
|
|
1423
1366
|
}, "Rollback handler failed");
|
|
1424
1367
|
}
|
|
1425
1368
|
this._events.addEvent({
|
|
@@ -1434,7 +1377,7 @@ var UpQueue = class {
|
|
|
1434
1377
|
this.logger.error({
|
|
1435
1378
|
error,
|
|
1436
1379
|
event,
|
|
1437
|
-
Category: "
|
|
1380
|
+
Category: "sp00ky-client::UpQueue::next"
|
|
1438
1381
|
}, "Failed to remove mutation from database after successful processing");
|
|
1439
1382
|
}
|
|
1440
1383
|
this._events.addEvent({
|
|
@@ -1448,7 +1391,7 @@ var UpQueue = class {
|
|
|
1448
1391
|
}
|
|
1449
1392
|
async loadFromDatabase() {
|
|
1450
1393
|
try {
|
|
1451
|
-
const [records] = await this.local.query(`SELECT * FROM
|
|
1394
|
+
const [records] = await this.local.query(`SELECT * FROM _00_pending_mutations ORDER BY created_at ASC`);
|
|
1452
1395
|
this.queue = records.map((r) => {
|
|
1453
1396
|
switch (r.mutationType) {
|
|
1454
1397
|
case "create": return {
|
|
@@ -1474,7 +1417,7 @@ var UpQueue = class {
|
|
|
1474
1417
|
this.logger.warn({
|
|
1475
1418
|
mutationType: r.mutationType,
|
|
1476
1419
|
record: r,
|
|
1477
|
-
Category: "
|
|
1420
|
+
Category: "sp00ky-client::UpQueue::loadFromDatabase"
|
|
1478
1421
|
}, "Unknown mutation type");
|
|
1479
1422
|
return null;
|
|
1480
1423
|
}
|
|
@@ -1482,7 +1425,7 @@ var UpQueue = class {
|
|
|
1482
1425
|
} catch (error) {
|
|
1483
1426
|
this.logger.error({
|
|
1484
1427
|
error,
|
|
1485
|
-
Category: "
|
|
1428
|
+
Category: "sp00ky-client::UpQueue::loadFromDatabase"
|
|
1486
1429
|
}, "Failed to load pending mutations from database");
|
|
1487
1430
|
}
|
|
1488
1431
|
}
|
|
@@ -1523,7 +1466,7 @@ var DownQueue = class {
|
|
|
1523
1466
|
this.logger.error({
|
|
1524
1467
|
error,
|
|
1525
1468
|
event,
|
|
1526
|
-
Category: "
|
|
1469
|
+
Category: "sp00ky-client::DownQueue::next"
|
|
1527
1470
|
}, "Failed to process query");
|
|
1528
1471
|
this.queue.unshift(event);
|
|
1529
1472
|
throw error;
|
|
@@ -1538,8 +1481,8 @@ var ArraySyncer = class {
|
|
|
1538
1481
|
remoteArray;
|
|
1539
1482
|
needsSort = false;
|
|
1540
1483
|
constructor(localArray, remoteArray) {
|
|
1541
|
-
this.remoteArray = remoteArray.
|
|
1542
|
-
this.localArray = localArray.
|
|
1484
|
+
this.remoteArray = remoteArray.toSorted((a, b) => a[0].localeCompare(b[0]));
|
|
1485
|
+
this.localArray = localArray.toSorted((a, b) => a[0].localeCompare(b[0]));
|
|
1543
1486
|
}
|
|
1544
1487
|
/**
|
|
1545
1488
|
* Inserts an item into the local array
|
|
@@ -1575,7 +1518,6 @@ var ArraySyncer = class {
|
|
|
1575
1518
|
this.localArray.sort((a, b) => a[0].localeCompare(b[0]));
|
|
1576
1519
|
this.needsSort = false;
|
|
1577
1520
|
}
|
|
1578
|
-
console.log("xxxx555", this.localArray, this.remoteArray);
|
|
1579
1521
|
return diffRecordVersionArray(this.localArray, this.remoteArray);
|
|
1580
1522
|
}
|
|
1581
1523
|
};
|
|
@@ -1606,14 +1548,12 @@ function diffRecordVersionArray(local, remote) {
|
|
|
1606
1548
|
};
|
|
1607
1549
|
}
|
|
1608
1550
|
function createDiffFromDbOp(op, recordId, version, versions) {
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
};
|
|
1616
|
-
}
|
|
1551
|
+
const old = versions?.find((record) => record[0] === encodeRecordId(recordId));
|
|
1552
|
+
if (old && old[1] >= version) return {
|
|
1553
|
+
added: [],
|
|
1554
|
+
updated: [],
|
|
1555
|
+
removed: []
|
|
1556
|
+
};
|
|
1617
1557
|
if (op === "CREATE") return {
|
|
1618
1558
|
added: [{
|
|
1619
1559
|
id: recordId,
|
|
@@ -1643,7 +1583,7 @@ function createDiffFromDbOp(op, recordId, version, versions) {
|
|
|
1643
1583
|
* SyncEngine handles the core sync operations: fetching remote records,
|
|
1644
1584
|
* caching them locally, and ingesting into DBSP.
|
|
1645
1585
|
*
|
|
1646
|
-
* This is extracted from
|
|
1586
|
+
* This is extracted from Sp00kySync to separate "how to sync" from "when to sync".
|
|
1647
1587
|
*/
|
|
1648
1588
|
var SyncEngine = class {
|
|
1649
1589
|
logger;
|
|
@@ -1652,7 +1592,7 @@ var SyncEngine = class {
|
|
|
1652
1592
|
this.remote = remote;
|
|
1653
1593
|
this.cache = cache;
|
|
1654
1594
|
this.schema = schema;
|
|
1655
|
-
this.logger = logger.child({ service: "
|
|
1595
|
+
this.logger = logger.child({ service: "Sp00kySync:SyncEngine" });
|
|
1656
1596
|
}
|
|
1657
1597
|
/**
|
|
1658
1598
|
* Sync missing/updated/removed records between local and remote.
|
|
@@ -1665,33 +1605,36 @@ var SyncEngine = class {
|
|
|
1665
1605
|
added,
|
|
1666
1606
|
updated,
|
|
1667
1607
|
removed,
|
|
1668
|
-
Category: "
|
|
1608
|
+
Category: "sp00ky-client::SyncEngine::syncRecords"
|
|
1669
1609
|
}, "SyncEngine.syncRecords diff");
|
|
1670
1610
|
if (removed.length > 0) await this.handleRemovedRecords(removed);
|
|
1671
|
-
const
|
|
1611
|
+
const toFetch = [...added, ...updated];
|
|
1612
|
+
const idsToFetch = toFetch.map((x) => x.id);
|
|
1672
1613
|
if (idsToFetch.length === 0) return;
|
|
1673
|
-
const
|
|
1674
|
-
|
|
1614
|
+
const versionMap = /* @__PURE__ */ new Map();
|
|
1615
|
+
for (const item of toFetch) versionMap.set(encodeRecordId(item.id), item.version);
|
|
1616
|
+
const [remoteResults] = await this.remote.query("SELECT * FROM $idsToFetch", { idsToFetch });
|
|
1675
1617
|
const cacheBatch = [];
|
|
1676
|
-
for (const
|
|
1618
|
+
for (const record of remoteResults) {
|
|
1677
1619
|
if (!record?.id) {
|
|
1678
1620
|
this.logger.warn({
|
|
1679
1621
|
record,
|
|
1680
1622
|
idsToFetch,
|
|
1681
|
-
Category: "
|
|
1682
|
-
}, "Remote record has no id. Skipping record");
|
|
1623
|
+
Category: "sp00ky-client::SyncEngine::syncRecords"
|
|
1624
|
+
}, "Remote record has no id (possibly deleted). Skipping record");
|
|
1683
1625
|
continue;
|
|
1684
1626
|
}
|
|
1685
1627
|
const fullId = encodeRecordId(record.id);
|
|
1686
1628
|
const table = record.id.table.toString();
|
|
1687
1629
|
const isAdded = added.some((item) => encodeRecordId(item.id) === fullId);
|
|
1630
|
+
const version = versionMap.get(fullId) ?? 0;
|
|
1688
1631
|
const localVersion = this.cache.lookup(fullId);
|
|
1689
|
-
if (localVersion &&
|
|
1632
|
+
if (localVersion && version <= localVersion) {
|
|
1690
1633
|
this.logger.info({
|
|
1691
1634
|
recordId: fullId,
|
|
1692
|
-
version
|
|
1635
|
+
version,
|
|
1693
1636
|
localVersion,
|
|
1694
|
-
Category: "
|
|
1637
|
+
Category: "sp00ky-client::SyncEngine::syncRecords"
|
|
1695
1638
|
}, "Local version is higher than remote version. Skipping record");
|
|
1696
1639
|
continue;
|
|
1697
1640
|
}
|
|
@@ -1701,7 +1644,7 @@ var SyncEngine = class {
|
|
|
1701
1644
|
table,
|
|
1702
1645
|
op: isAdded ? "CREATE" : "UPDATE",
|
|
1703
1646
|
record: cleanedRecord,
|
|
1704
|
-
version
|
|
1647
|
+
version
|
|
1705
1648
|
});
|
|
1706
1649
|
}
|
|
1707
1650
|
if (cacheBatch.length > 0) await this.cache.saveBatch(cacheBatch);
|
|
@@ -1713,21 +1656,21 @@ var SyncEngine = class {
|
|
|
1713
1656
|
async handleRemovedRecords(removed) {
|
|
1714
1657
|
this.logger.debug({
|
|
1715
1658
|
removed: removed.map((r) => r.toString()),
|
|
1716
|
-
Category: "
|
|
1659
|
+
Category: "sp00ky-client::SyncEngine::handleRemovedRecords"
|
|
1717
1660
|
}, "Checking removed records");
|
|
1718
1661
|
let existingRemoteIds = /* @__PURE__ */ new Set();
|
|
1719
1662
|
try {
|
|
1720
1663
|
const [existingRemote] = await this.remote.query("SELECT id FROM $ids", { ids: removed });
|
|
1721
1664
|
existingRemoteIds = new Set(existingRemote.map((r) => encodeRecordId(r.id)));
|
|
1722
1665
|
} catch {
|
|
1723
|
-
this.logger.debug({ Category: "
|
|
1666
|
+
this.logger.debug({ Category: "sp00ky-client::SyncEngine::handleRemovedRecords" }, "Remote existence check failed, proceeding with deletion");
|
|
1724
1667
|
}
|
|
1725
1668
|
for (const recordId of removed) {
|
|
1726
1669
|
const recordIdStr = encodeRecordId(recordId);
|
|
1727
1670
|
if (!existingRemoteIds.has(recordIdStr)) {
|
|
1728
1671
|
this.logger.debug({
|
|
1729
1672
|
recordId: recordIdStr,
|
|
1730
|
-
Category: "
|
|
1673
|
+
Category: "sp00ky-client::SyncEngine::handleRemovedRecords"
|
|
1731
1674
|
}, "Deleting confirmed removed record");
|
|
1732
1675
|
await this.cache.delete(recordId.table.name, recordIdStr);
|
|
1733
1676
|
}
|
|
@@ -1806,12 +1749,12 @@ var SyncScheduler = class {
|
|
|
1806
1749
|
//#endregion
|
|
1807
1750
|
//#region src/modules/sync/sync.ts
|
|
1808
1751
|
/**
|
|
1809
|
-
* The main synchronization engine for
|
|
1752
|
+
* The main synchronization engine for Sp00ky.
|
|
1810
1753
|
* Handles the bidirectional synchronization between the local database and the remote backend.
|
|
1811
1754
|
* Uses a queue-based architecture with 'up' (local to remote) and 'down' (remote to local) queues.
|
|
1812
1755
|
* @template S The schema structure type.
|
|
1813
1756
|
*/
|
|
1814
|
-
var
|
|
1757
|
+
var Sp00kySync = class {
|
|
1815
1758
|
clientId = "";
|
|
1816
1759
|
upQueue;
|
|
1817
1760
|
downQueue;
|
|
@@ -1840,7 +1783,7 @@ var SpookySync = class {
|
|
|
1840
1783
|
this.cache = cache;
|
|
1841
1784
|
this.dataModule = dataModule;
|
|
1842
1785
|
this.schema = schema;
|
|
1843
|
-
this.logger = logger.child({ service: "
|
|
1786
|
+
this.logger = logger.child({ service: "Sp00kySync" });
|
|
1844
1787
|
this.upQueue = new UpQueue(this.local, this.logger);
|
|
1845
1788
|
this.downQueue = new DownQueue(this.local, this.logger);
|
|
1846
1789
|
this.syncEngine = new SyncEngine(this.remote, this.cache, this.schema, this.logger);
|
|
@@ -1853,7 +1796,7 @@ var SpookySync = class {
|
|
|
1853
1796
|
* @throws Error if already initialized.
|
|
1854
1797
|
*/
|
|
1855
1798
|
async init(clientId) {
|
|
1856
|
-
if (this.isInit) throw new Error("
|
|
1799
|
+
if (this.isInit) throw new Error("Sp00kySync is already initialized");
|
|
1857
1800
|
this.clientId = clientId;
|
|
1858
1801
|
this.isInit = true;
|
|
1859
1802
|
await this.scheduler.init();
|
|
@@ -1865,29 +1808,37 @@ var SpookySync = class {
|
|
|
1865
1808
|
async startRefLiveQueries() {
|
|
1866
1809
|
this.logger.debug({
|
|
1867
1810
|
clientId: this.clientId,
|
|
1868
|
-
Category: "
|
|
1811
|
+
Category: "sp00ky-client::Sp00kySync::startRefLiveQueries"
|
|
1869
1812
|
}, "Starting ref live queries");
|
|
1870
|
-
const [queryUuid] = await this.remote.query("LIVE SELECT * FROM
|
|
1813
|
+
const [queryUuid] = await this.remote.query("LIVE SELECT * FROM _00_list_ref");
|
|
1871
1814
|
(await this.remote.getClient().liveOf(queryUuid)).subscribe((message) => {
|
|
1872
1815
|
this.logger.debug({
|
|
1873
1816
|
message,
|
|
1874
|
-
Category: "
|
|
1817
|
+
Category: "sp00ky-client::Sp00kySync::startRefLiveQueries"
|
|
1875
1818
|
}, "Live update received");
|
|
1876
1819
|
if (message.action === "KILLED") return;
|
|
1877
1820
|
this.handleRemoteListRefChange(message.action, message.value.in, message.value.out, message.value.version).catch((err) => {
|
|
1878
1821
|
this.logger.error({
|
|
1879
1822
|
err,
|
|
1880
|
-
Category: "
|
|
1823
|
+
Category: "sp00ky-client::Sp00kySync::startRefLiveQueries"
|
|
1881
1824
|
}, "Error handling remote list ref change");
|
|
1882
1825
|
});
|
|
1883
1826
|
});
|
|
1884
1827
|
}
|
|
1885
1828
|
async handleRemoteListRefChange(action, queryId, recordId, version) {
|
|
1829
|
+
if (action === "DELETE") {
|
|
1830
|
+
this.logger.debug({
|
|
1831
|
+
queryId: queryId.toString(),
|
|
1832
|
+
recordId: recordId.toString(),
|
|
1833
|
+
Category: "sp00ky-client::Sp00kySync::handleRemoteListRefChange"
|
|
1834
|
+
}, "Ignoring DELETE on list_ref — should not happen");
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1886
1837
|
const existing = this.dataModule.getQueryById(queryId);
|
|
1887
1838
|
if (!existing) {
|
|
1888
1839
|
this.logger.warn({
|
|
1889
1840
|
queryId: queryId.toString(),
|
|
1890
|
-
Category: "
|
|
1841
|
+
Category: "sp00ky-client::Sp00kySync::handleRemoteListRefChange"
|
|
1891
1842
|
}, "Received remote update for unknown local query");
|
|
1892
1843
|
return;
|
|
1893
1844
|
}
|
|
@@ -1898,7 +1849,7 @@ var SpookySync = class {
|
|
|
1898
1849
|
recordId,
|
|
1899
1850
|
version,
|
|
1900
1851
|
localArray,
|
|
1901
|
-
Category: "
|
|
1852
|
+
Category: "sp00ky-client::Sp00kySync::handleRemoteListRefChange"
|
|
1902
1853
|
}, "Live update is being processed");
|
|
1903
1854
|
const diff = createDiffFromDbOp(action, recordId, version, localArray);
|
|
1904
1855
|
await this.syncEngine.syncRecords(diff);
|
|
@@ -1913,9 +1864,8 @@ var SpookySync = class {
|
|
|
1913
1864
|
async processUpEvent(event) {
|
|
1914
1865
|
this.logger.debug({
|
|
1915
1866
|
event,
|
|
1916
|
-
Category: "
|
|
1867
|
+
Category: "sp00ky-client::Sp00kySync::processUpEvent"
|
|
1917
1868
|
}, "Processing up event");
|
|
1918
|
-
console.log("xx1", event);
|
|
1919
1869
|
switch (event.type) {
|
|
1920
1870
|
case "create":
|
|
1921
1871
|
const dataKeys = Object.keys(event.data).map((key) => ({
|
|
@@ -1941,7 +1891,7 @@ var SpookySync = class {
|
|
|
1941
1891
|
default:
|
|
1942
1892
|
this.logger.error({
|
|
1943
1893
|
event,
|
|
1944
|
-
Category: "
|
|
1894
|
+
Category: "sp00ky-client::Sp00kySync::processUpEvent"
|
|
1945
1895
|
}, "processUpEvent unknown event type");
|
|
1946
1896
|
return;
|
|
1947
1897
|
}
|
|
@@ -1954,7 +1904,7 @@ var SpookySync = class {
|
|
|
1954
1904
|
recordId,
|
|
1955
1905
|
tableName,
|
|
1956
1906
|
error: error.message,
|
|
1957
|
-
Category: "
|
|
1907
|
+
Category: "sp00ky-client::Sp00kySync::handleRollback"
|
|
1958
1908
|
}, "Rolling back failed mutation");
|
|
1959
1909
|
switch (event.type) {
|
|
1960
1910
|
case "create":
|
|
@@ -1964,13 +1914,13 @@ var SpookySync = class {
|
|
|
1964
1914
|
if (event.beforeRecord) await this.dataModule.rollbackUpdate(event.record_id, tableName, event.beforeRecord);
|
|
1965
1915
|
else this.logger.warn({
|
|
1966
1916
|
recordId,
|
|
1967
|
-
Category: "
|
|
1917
|
+
Category: "sp00ky-client::Sp00kySync::handleRollback"
|
|
1968
1918
|
}, "Cannot rollback update: no beforeRecord available. Down-sync will reconcile.");
|
|
1969
1919
|
break;
|
|
1970
1920
|
case "delete":
|
|
1971
1921
|
this.logger.warn({
|
|
1972
1922
|
recordId,
|
|
1973
|
-
Category: "
|
|
1923
|
+
Category: "sp00ky-client::Sp00kySync::handleRollback"
|
|
1974
1924
|
}, "Delete rollback not implemented. Down-sync will reconcile.");
|
|
1975
1925
|
break;
|
|
1976
1926
|
}
|
|
@@ -1983,7 +1933,7 @@ var SpookySync = class {
|
|
|
1983
1933
|
async processDownEvent(event) {
|
|
1984
1934
|
this.logger.debug({
|
|
1985
1935
|
event,
|
|
1986
|
-
Category: "
|
|
1936
|
+
Category: "sp00ky-client::Sp00kySync::processDownEvent"
|
|
1987
1937
|
}, "Processing down event");
|
|
1988
1938
|
switch (event.type) {
|
|
1989
1939
|
case "register": return this.registerQuery(event.payload.hash);
|
|
@@ -2002,7 +1952,7 @@ var SpookySync = class {
|
|
|
2002
1952
|
if (!queryState) {
|
|
2003
1953
|
this.logger.warn({
|
|
2004
1954
|
hash,
|
|
2005
|
-
Category: "
|
|
1955
|
+
Category: "sp00ky-client::Sp00kySync::syncQuery"
|
|
2006
1956
|
}, "Query not found");
|
|
2007
1957
|
return;
|
|
2008
1958
|
}
|
|
@@ -2021,7 +1971,7 @@ var SpookySync = class {
|
|
|
2021
1971
|
try {
|
|
2022
1972
|
this.logger.debug({
|
|
2023
1973
|
queryHash,
|
|
2024
|
-
Category: "
|
|
1974
|
+
Category: "sp00ky-client::Sp00kySync::registerQuery"
|
|
2025
1975
|
}, "Register Query state");
|
|
2026
1976
|
await this.createRemoteQuery(queryHash);
|
|
2027
1977
|
await this.syncQuery(queryHash);
|
|
@@ -2029,7 +1979,7 @@ var SpookySync = class {
|
|
|
2029
1979
|
} catch (e) {
|
|
2030
1980
|
this.logger.error({
|
|
2031
1981
|
err: e,
|
|
2032
|
-
Category: "
|
|
1982
|
+
Category: "sp00ky-client::Sp00kySync::registerQuery"
|
|
2033
1983
|
}, "registerQuery error");
|
|
2034
1984
|
throw e;
|
|
2035
1985
|
}
|
|
@@ -2039,7 +1989,7 @@ var SpookySync = class {
|
|
|
2039
1989
|
if (!queryState) {
|
|
2040
1990
|
this.logger.warn({
|
|
2041
1991
|
queryHash,
|
|
2042
|
-
Category: "
|
|
1992
|
+
Category: "sp00ky-client::Sp00kySync::createRemoteQuery"
|
|
2043
1993
|
}, "Query to register not found");
|
|
2044
1994
|
throw new Error("Query to register not found");
|
|
2045
1995
|
}
|
|
@@ -2050,17 +2000,17 @@ var SpookySync = class {
|
|
|
2050
2000
|
params: queryState.config.params,
|
|
2051
2001
|
ttl: queryState.config.ttl
|
|
2052
2002
|
} });
|
|
2053
|
-
const [items] = await this.remote.query(surql.selectByFieldsAnd("
|
|
2003
|
+
const [items] = await this.remote.query(surql.selectByFieldsAnd("_00_list_ref", ["in"], ["out", "version"]), { in: queryState.config.id });
|
|
2054
2004
|
this.logger.trace({
|
|
2055
2005
|
queryId: encodeRecordId(queryState.config.id),
|
|
2056
2006
|
items,
|
|
2057
|
-
Category: "
|
|
2007
|
+
Category: "sp00ky-client::Sp00kySync::createRemoteQuery"
|
|
2058
2008
|
}, "Got query record version array from remote");
|
|
2059
2009
|
const array = items.map((item) => [encodeRecordId(item.out), item.version]);
|
|
2060
2010
|
this.logger.debug({
|
|
2061
2011
|
queryId: encodeRecordId(queryState.config.id),
|
|
2062
2012
|
array,
|
|
2063
|
-
Category: "
|
|
2013
|
+
Category: "sp00ky-client::Sp00kySync::createRemoteQuery"
|
|
2064
2014
|
}, "createdRemoteQuery");
|
|
2065
2015
|
if (array) await this.dataModule.updateQueryRemoteArray(queryHash, array);
|
|
2066
2016
|
}
|
|
@@ -2069,7 +2019,7 @@ var SpookySync = class {
|
|
|
2069
2019
|
if (!queryState) {
|
|
2070
2020
|
this.logger.warn({
|
|
2071
2021
|
queryHash,
|
|
2072
|
-
Category: "
|
|
2022
|
+
Category: "sp00ky-client::Sp00kySync::heartbeatQuery"
|
|
2073
2023
|
}, "Query to register not found");
|
|
2074
2024
|
throw new Error("Query to register not found");
|
|
2075
2025
|
}
|
|
@@ -2080,7 +2030,7 @@ var SpookySync = class {
|
|
|
2080
2030
|
if (!queryState) {
|
|
2081
2031
|
this.logger.warn({
|
|
2082
2032
|
queryHash,
|
|
2083
|
-
Category: "
|
|
2033
|
+
Category: "sp00ky-client::Sp00kySync::cleanupQuery"
|
|
2084
2034
|
}, "Query to register not found");
|
|
2085
2035
|
throw new Error("Query to register not found");
|
|
2086
2036
|
}
|
|
@@ -2112,7 +2062,7 @@ var DevToolsService = class {
|
|
|
2112
2062
|
this.authService.eventSystem.subscribe(AuthEventTypes.AuthStateChanged, () => {
|
|
2113
2063
|
this.notifyDevTools();
|
|
2114
2064
|
});
|
|
2115
|
-
this.logger.debug({ Category: "
|
|
2065
|
+
this.logger.debug({ Category: "sp00ky-client::DevToolsService::init" }, "Service initialized");
|
|
2116
2066
|
}
|
|
2117
2067
|
getActiveQueries() {
|
|
2118
2068
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -2138,7 +2088,7 @@ var DevToolsService = class {
|
|
|
2138
2088
|
onQueryInitialized(payload) {
|
|
2139
2089
|
this.logger.debug({
|
|
2140
2090
|
payload,
|
|
2141
|
-
Category: "
|
|
2091
|
+
Category: "sp00ky-client::DevToolsService::onQueryInitialized"
|
|
2142
2092
|
}, "QueryInitialized");
|
|
2143
2093
|
const queryHash = this.hashString(payload.queryId.toString());
|
|
2144
2094
|
this.addEvent("QUERY_REQUEST_INIT", {
|
|
@@ -2151,7 +2101,7 @@ var DevToolsService = class {
|
|
|
2151
2101
|
onQueryUpdated(payload) {
|
|
2152
2102
|
this.logger.debug({
|
|
2153
2103
|
id: payload.queryId?.toString(),
|
|
2154
|
-
Category: "
|
|
2104
|
+
Category: "sp00ky-client::DevToolsService::onQueryUpdated"
|
|
2155
2105
|
}, "QueryUpdated");
|
|
2156
2106
|
const queryHash = this.hashString(payload.queryId.toString());
|
|
2157
2107
|
this.addEvent("QUERY_UPDATED", {
|
|
@@ -2163,7 +2113,7 @@ var DevToolsService = class {
|
|
|
2163
2113
|
onStreamUpdate(update) {
|
|
2164
2114
|
this.logger.debug({
|
|
2165
2115
|
update,
|
|
2166
|
-
Category: "
|
|
2116
|
+
Category: "sp00ky-client::DevToolsService::onStreamUpdate"
|
|
2167
2117
|
}, "StreamUpdate");
|
|
2168
2118
|
this.addEvent("STREAM_UPDATE", { updates: [update] });
|
|
2169
2119
|
this.notifyDevTools();
|
|
@@ -2218,8 +2168,8 @@ var DevToolsService = class {
|
|
|
2218
2168
|
}
|
|
2219
2169
|
notifyDevTools() {
|
|
2220
2170
|
if (typeof window !== "undefined") window.postMessage({
|
|
2221
|
-
type: "
|
|
2222
|
-
source: "
|
|
2171
|
+
type: "SP00KY_STATE_CHANGED",
|
|
2172
|
+
source: "sp00ky-devtools-page",
|
|
2223
2173
|
state: this.getState()
|
|
2224
2174
|
}, "*");
|
|
2225
2175
|
}
|
|
@@ -2245,7 +2195,7 @@ var DevToolsService = class {
|
|
|
2245
2195
|
}
|
|
2246
2196
|
exposeToWindow() {
|
|
2247
2197
|
if (typeof window !== "undefined") {
|
|
2248
|
-
window.
|
|
2198
|
+
window.__00__ = {
|
|
2249
2199
|
version: this.version,
|
|
2250
2200
|
getState: () => this.getState(),
|
|
2251
2201
|
clearHistory: () => {
|
|
@@ -2266,7 +2216,7 @@ var DevToolsService = class {
|
|
|
2266
2216
|
} catch (e) {
|
|
2267
2217
|
this.logger.error({
|
|
2268
2218
|
err: e,
|
|
2269
|
-
Category: "
|
|
2219
|
+
Category: "sp00ky-client::DevToolsService::exposeToWindow"
|
|
2270
2220
|
}, "Failed to get table data");
|
|
2271
2221
|
return [];
|
|
2272
2222
|
}
|
|
@@ -2298,7 +2248,7 @@ var DevToolsService = class {
|
|
|
2298
2248
|
this.logger.debug({
|
|
2299
2249
|
query,
|
|
2300
2250
|
target,
|
|
2301
|
-
Category: "
|
|
2251
|
+
Category: "sp00ky-client::DevToolsService::runQuery"
|
|
2302
2252
|
}, "Running query (START)");
|
|
2303
2253
|
const service = target === "remote" ? this.remoteDatabaseService : this.databaseService;
|
|
2304
2254
|
const startTime = Date.now();
|
|
@@ -2309,7 +2259,7 @@ var DevToolsService = class {
|
|
|
2309
2259
|
time: queryTime,
|
|
2310
2260
|
resultType: typeof result,
|
|
2311
2261
|
isArray: Array.isArray(result),
|
|
2312
|
-
Category: "
|
|
2262
|
+
Category: "sp00ky-client::DevToolsService::runQuery"
|
|
2313
2263
|
}, "Database returned result");
|
|
2314
2264
|
const serializeStart = Date.now();
|
|
2315
2265
|
const serialized = this.serializeForDevTools(result);
|
|
@@ -2317,7 +2267,7 @@ var DevToolsService = class {
|
|
|
2317
2267
|
this.logger.debug({
|
|
2318
2268
|
serializeTime,
|
|
2319
2269
|
serializedLength: JSON.stringify(serialized).length,
|
|
2320
|
-
Category: "
|
|
2270
|
+
Category: "sp00ky-client::DevToolsService::runQuery"
|
|
2321
2271
|
}, "Serialization complete");
|
|
2322
2272
|
return {
|
|
2323
2273
|
success: true,
|
|
@@ -2329,7 +2279,7 @@ var DevToolsService = class {
|
|
|
2329
2279
|
err: e,
|
|
2330
2280
|
query,
|
|
2331
2281
|
target,
|
|
2332
|
-
Category: "
|
|
2282
|
+
Category: "sp00ky-client::DevToolsService::runQuery"
|
|
2333
2283
|
}, "Query execution failed");
|
|
2334
2284
|
return {
|
|
2335
2285
|
success: false,
|
|
@@ -2339,13 +2289,14 @@ var DevToolsService = class {
|
|
|
2339
2289
|
}
|
|
2340
2290
|
};
|
|
2341
2291
|
window.postMessage({
|
|
2342
|
-
type: "
|
|
2343
|
-
source: "
|
|
2292
|
+
type: "SP00KY_DETECTED",
|
|
2293
|
+
source: "sp00ky-devtools-page",
|
|
2344
2294
|
data: {
|
|
2345
2295
|
version: this.version,
|
|
2346
2296
|
detected: true
|
|
2347
2297
|
}
|
|
2348
2298
|
}, "*");
|
|
2299
|
+
window.dispatchEvent(new CustomEvent("sp00ky:init"));
|
|
2349
2300
|
}
|
|
2350
2301
|
}
|
|
2351
2302
|
};
|
|
@@ -2396,9 +2347,9 @@ var AuthService = class {
|
|
|
2396
2347
|
async check(accessToken) {
|
|
2397
2348
|
this.isLoading = true;
|
|
2398
2349
|
try {
|
|
2399
|
-
const token = accessToken || await this.persistenceClient.get("
|
|
2350
|
+
const token = accessToken || await this.persistenceClient.get("sp00ky_auth_token");
|
|
2400
2351
|
if (!token) {
|
|
2401
|
-
this.logger.debug({ Category: "
|
|
2352
|
+
this.logger.debug({ Category: "sp00ky-client::AuthService::check" }, "No token found in storage or arguments");
|
|
2402
2353
|
this.isLoading = false;
|
|
2403
2354
|
this.isAuthenticated = false;
|
|
2404
2355
|
this.notifyListeners();
|
|
@@ -2411,22 +2362,22 @@ var AuthService = class {
|
|
|
2411
2362
|
if (user && user.id) {
|
|
2412
2363
|
this.logger.info({
|
|
2413
2364
|
user,
|
|
2414
|
-
Category: "
|
|
2365
|
+
Category: "sp00ky-client::AuthService::check"
|
|
2415
2366
|
}, "Auth check complete (via $auth.id)");
|
|
2416
2367
|
await this.setSession(token, user);
|
|
2417
2368
|
} else {
|
|
2418
|
-
this.logger.warn({ Category: "
|
|
2369
|
+
this.logger.warn({ Category: "sp00ky-client::AuthService::check" }, "$auth.id empty, attempting manual user fetch");
|
|
2419
2370
|
const manualResult = await this.remote.query("SELECT * FROM user WHERE id = $auth.id LIMIT 1");
|
|
2420
2371
|
const manualItems = Array.isArray(manualResult) && Array.isArray(manualResult[0]) ? manualResult[0] : manualResult;
|
|
2421
2372
|
const manualUser = Array.isArray(manualItems) ? manualItems[0] : manualItems;
|
|
2422
2373
|
if (manualUser && manualUser.id) {
|
|
2423
2374
|
this.logger.info({
|
|
2424
2375
|
user: manualUser,
|
|
2425
|
-
Category: "
|
|
2376
|
+
Category: "sp00ky-client::AuthService::check"
|
|
2426
2377
|
}, "Auth check complete (via manual fetch)");
|
|
2427
2378
|
await this.setSession(token, manualUser);
|
|
2428
2379
|
} else {
|
|
2429
|
-
this.logger.warn({ Category: "
|
|
2380
|
+
this.logger.warn({ Category: "sp00ky-client::AuthService::check" }, "Token valid but user not found via fallback");
|
|
2430
2381
|
await this.signOut();
|
|
2431
2382
|
}
|
|
2432
2383
|
}
|
|
@@ -2434,7 +2385,7 @@ var AuthService = class {
|
|
|
2434
2385
|
this.logger.error({
|
|
2435
2386
|
error,
|
|
2436
2387
|
stack: error.stack,
|
|
2437
|
-
Category: "
|
|
2388
|
+
Category: "sp00ky-client::AuthService::check"
|
|
2438
2389
|
}, "Auth check failed");
|
|
2439
2390
|
await this.signOut();
|
|
2440
2391
|
} finally {
|
|
@@ -2448,17 +2399,17 @@ var AuthService = class {
|
|
|
2448
2399
|
this.token = null;
|
|
2449
2400
|
this.currentUser = null;
|
|
2450
2401
|
this.isAuthenticated = false;
|
|
2451
|
-
await this.persistenceClient.remove("
|
|
2402
|
+
await this.persistenceClient.remove("sp00ky_auth_token");
|
|
2452
2403
|
try {
|
|
2453
2404
|
await this.remote.getClient().invalidate();
|
|
2454
|
-
} catch (
|
|
2405
|
+
} catch (_e) {}
|
|
2455
2406
|
this.notifyListeners();
|
|
2456
2407
|
}
|
|
2457
2408
|
async setSession(token, user) {
|
|
2458
2409
|
this.token = token;
|
|
2459
2410
|
this.currentUser = user;
|
|
2460
2411
|
this.isAuthenticated = true;
|
|
2461
|
-
await this.persistenceClient.set("
|
|
2412
|
+
await this.persistenceClient.set("sp00ky_auth_token", token);
|
|
2462
2413
|
this.notifyListeners();
|
|
2463
2414
|
}
|
|
2464
2415
|
async signUp(accessName, params) {
|
|
@@ -2470,13 +2421,13 @@ var AuthService = class {
|
|
|
2470
2421
|
this.logger.info({
|
|
2471
2422
|
accessName,
|
|
2472
2423
|
runtimeParams,
|
|
2473
|
-
Category: "
|
|
2424
|
+
Category: "sp00ky-client::AuthService::signUp"
|
|
2474
2425
|
}, "Attempting signup");
|
|
2475
2426
|
const { access } = await this.remote.getClient().signup({
|
|
2476
2427
|
access: accessName,
|
|
2477
2428
|
variables: runtimeParams
|
|
2478
2429
|
});
|
|
2479
|
-
this.logger.info({ Category: "
|
|
2430
|
+
this.logger.info({ Category: "sp00ky-client::AuthService::signUp" }, "Signup successful, token received");
|
|
2480
2431
|
await this.check(access);
|
|
2481
2432
|
}
|
|
2482
2433
|
async signIn(accessName, params) {
|
|
@@ -2487,7 +2438,7 @@ var AuthService = class {
|
|
|
2487
2438
|
if (missingParams.length > 0) throw new Error(`Missing required signin params for '${accessName}': ${missingParams.join(", ")}`);
|
|
2488
2439
|
this.logger.info({
|
|
2489
2440
|
accessName,
|
|
2490
|
-
Category: "
|
|
2441
|
+
Category: "sp00ky-client::AuthService::signIn"
|
|
2491
2442
|
}, "Attempting signin");
|
|
2492
2443
|
const { access } = await this.remote.getClient().signin({
|
|
2493
2444
|
access: accessName,
|
|
@@ -2526,17 +2477,17 @@ var StreamProcessorService = class {
|
|
|
2526
2477
|
*/
|
|
2527
2478
|
async init() {
|
|
2528
2479
|
if (this.isInitialized) return;
|
|
2529
|
-
this.logger.info({ Category: "
|
|
2480
|
+
this.logger.info({ Category: "sp00ky-client::StreamProcessorService::init" }, "Initializing WASM...");
|
|
2530
2481
|
try {
|
|
2531
2482
|
await init();
|
|
2532
|
-
this.processor = new
|
|
2483
|
+
this.processor = new Sp00kyProcessor();
|
|
2533
2484
|
await this.loadState();
|
|
2534
2485
|
this.isInitialized = true;
|
|
2535
|
-
this.logger.info({ Category: "
|
|
2486
|
+
this.logger.info({ Category: "sp00ky-client::StreamProcessorService::init" }, "Initialized successfully");
|
|
2536
2487
|
} catch (e) {
|
|
2537
2488
|
this.logger.error({
|
|
2538
2489
|
error: e,
|
|
2539
|
-
Category: "
|
|
2490
|
+
Category: "sp00ky-client::StreamProcessorService::init"
|
|
2540
2491
|
}, "Failed to initialize");
|
|
2541
2492
|
throw e;
|
|
2542
2493
|
}
|
|
@@ -2544,20 +2495,20 @@ var StreamProcessorService = class {
|
|
|
2544
2495
|
async loadState() {
|
|
2545
2496
|
if (!this.processor) return;
|
|
2546
2497
|
try {
|
|
2547
|
-
const result = await this.persistenceClient.get("
|
|
2498
|
+
const result = await this.persistenceClient.get("_00_stream_processor_state");
|
|
2548
2499
|
if (Array.isArray(result) && result.length > 0 && Array.isArray(result[0]) && result[0].length > 0 && result[0][0]?.state) {
|
|
2549
2500
|
const state = result[0][0].state;
|
|
2550
2501
|
this.logger.info({
|
|
2551
2502
|
stateLength: state.length,
|
|
2552
|
-
Category: "
|
|
2503
|
+
Category: "sp00ky-client::StreamProcessorService::loadState"
|
|
2553
2504
|
}, "Loading state from DB");
|
|
2554
2505
|
if (typeof this.processor.load_state === "function") this.processor.load_state(state);
|
|
2555
|
-
else this.logger.warn({ Category: "
|
|
2556
|
-
} else this.logger.info({ Category: "
|
|
2506
|
+
else this.logger.warn({ Category: "sp00ky-client::StreamProcessorService::loadState" }, "load_state method not found on processor");
|
|
2507
|
+
} else this.logger.info({ Category: "sp00ky-client::StreamProcessorService::loadState" }, "No saved state found");
|
|
2557
2508
|
} catch (e) {
|
|
2558
2509
|
this.logger.error({
|
|
2559
2510
|
error: e,
|
|
2560
|
-
Category: "
|
|
2511
|
+
Category: "sp00ky-client::StreamProcessorService::loadState"
|
|
2561
2512
|
}, "Failed to load state");
|
|
2562
2513
|
}
|
|
2563
2514
|
}
|
|
@@ -2567,14 +2518,14 @@ var StreamProcessorService = class {
|
|
|
2567
2518
|
if (typeof this.processor.save_state === "function") {
|
|
2568
2519
|
const state = this.processor.save_state();
|
|
2569
2520
|
if (state) {
|
|
2570
|
-
await this.persistenceClient.set("
|
|
2571
|
-
this.logger.trace({ Category: "
|
|
2521
|
+
await this.persistenceClient.set("_00_stream_processor_state", state);
|
|
2522
|
+
this.logger.trace({ Category: "sp00ky-client::StreamProcessorService::saveState" }, "State saved");
|
|
2572
2523
|
}
|
|
2573
2524
|
}
|
|
2574
2525
|
} catch (e) {
|
|
2575
2526
|
this.logger.error({
|
|
2576
2527
|
error: e,
|
|
2577
|
-
Category: "
|
|
2528
|
+
Category: "sp00ky-client::StreamProcessorService::saveState"
|
|
2578
2529
|
}, "Failed to save state");
|
|
2579
2530
|
}
|
|
2580
2531
|
}
|
|
@@ -2588,10 +2539,10 @@ var StreamProcessorService = class {
|
|
|
2588
2539
|
table,
|
|
2589
2540
|
op,
|
|
2590
2541
|
id,
|
|
2591
|
-
Category: "
|
|
2542
|
+
Category: "sp00ky-client::StreamProcessorService::ingest"
|
|
2592
2543
|
}, "Ingesting into ssp");
|
|
2593
2544
|
if (!this.processor) {
|
|
2594
|
-
this.logger.warn({ Category: "
|
|
2545
|
+
this.logger.warn({ Category: "sp00ky-client::StreamProcessorService::ingest" }, "Not initialized, skipping ingest");
|
|
2595
2546
|
return [];
|
|
2596
2547
|
}
|
|
2597
2548
|
try {
|
|
@@ -2602,7 +2553,7 @@ var StreamProcessorService = class {
|
|
|
2602
2553
|
op,
|
|
2603
2554
|
id,
|
|
2604
2555
|
rawUpdates: rawUpdates.length,
|
|
2605
|
-
Category: "
|
|
2556
|
+
Category: "sp00ky-client::StreamProcessorService::ingest"
|
|
2606
2557
|
}, "Ingesting into ssp done");
|
|
2607
2558
|
if (rawUpdates && Array.isArray(rawUpdates) && rawUpdates.length > 0) {
|
|
2608
2559
|
const updates = rawUpdates.map((u) => ({
|
|
@@ -2617,7 +2568,7 @@ var StreamProcessorService = class {
|
|
|
2617
2568
|
} catch (e) {
|
|
2618
2569
|
this.logger.error({
|
|
2619
2570
|
error: e,
|
|
2620
|
-
Category: "
|
|
2571
|
+
Category: "sp00ky-client::StreamProcessorService::ingest"
|
|
2621
2572
|
}, "Ingesting into ssp failed");
|
|
2622
2573
|
}
|
|
2623
2574
|
return [];
|
|
@@ -2628,14 +2579,14 @@ var StreamProcessorService = class {
|
|
|
2628
2579
|
*/
|
|
2629
2580
|
registerQueryPlan(queryPlan) {
|
|
2630
2581
|
if (!this.processor) {
|
|
2631
|
-
this.logger.warn({ Category: "
|
|
2582
|
+
this.logger.warn({ Category: "sp00ky-client::StreamProcessorService::registerQueryPlan" }, "Not initialized, skipping registration");
|
|
2632
2583
|
return;
|
|
2633
2584
|
}
|
|
2634
2585
|
this.logger.debug({
|
|
2635
2586
|
queryHash: queryPlan.queryHash,
|
|
2636
2587
|
surql: queryPlan.surql,
|
|
2637
2588
|
params: queryPlan.params,
|
|
2638
|
-
Category: "
|
|
2589
|
+
Category: "sp00ky-client::StreamProcessorService::registerQueryPlan"
|
|
2639
2590
|
}, "Registering query plan");
|
|
2640
2591
|
try {
|
|
2641
2592
|
const normalizedParams = this.normalizeValue(queryPlan.params);
|
|
@@ -2649,7 +2600,7 @@ var StreamProcessorService = class {
|
|
|
2649
2600
|
});
|
|
2650
2601
|
this.logger.debug({
|
|
2651
2602
|
initialUpdate,
|
|
2652
|
-
Category: "
|
|
2603
|
+
Category: "sp00ky-client::StreamProcessorService::registerQueryPlan"
|
|
2653
2604
|
}, "register_view result");
|
|
2654
2605
|
if (!initialUpdate) throw new Error("Failed to register query plan");
|
|
2655
2606
|
const update = {
|
|
@@ -2661,13 +2612,13 @@ var StreamProcessorService = class {
|
|
|
2661
2612
|
queryHash: queryPlan.queryHash,
|
|
2662
2613
|
surql: queryPlan.surql,
|
|
2663
2614
|
params: queryPlan.params,
|
|
2664
|
-
Category: "
|
|
2615
|
+
Category: "sp00ky-client::StreamProcessorService::registerQueryPlan"
|
|
2665
2616
|
}, "Registered query plan");
|
|
2666
2617
|
return update;
|
|
2667
2618
|
} catch (e) {
|
|
2668
2619
|
this.logger.error({
|
|
2669
2620
|
error: e,
|
|
2670
|
-
Category: "
|
|
2621
|
+
Category: "sp00ky-client::StreamProcessorService::registerQueryPlan"
|
|
2671
2622
|
}, "Error registering query plan");
|
|
2672
2623
|
throw e;
|
|
2673
2624
|
}
|
|
@@ -2683,7 +2634,7 @@ var StreamProcessorService = class {
|
|
|
2683
2634
|
} catch (e) {
|
|
2684
2635
|
this.logger.error({
|
|
2685
2636
|
error: e,
|
|
2686
|
-
Category: "
|
|
2637
|
+
Category: "sp00ky-client::StreamProcessorService::unregisterQueryPlan"
|
|
2687
2638
|
}, "Error unregistering query plan");
|
|
2688
2639
|
}
|
|
2689
2640
|
}
|
|
@@ -2698,7 +2649,7 @@ var StreamProcessorService = class {
|
|
|
2698
2649
|
const result = value.toString();
|
|
2699
2650
|
this.logger.trace({
|
|
2700
2651
|
result,
|
|
2701
|
-
Category: "
|
|
2652
|
+
Category: "sp00ky-client::StreamProcessorService::normalizeValue"
|
|
2702
2653
|
}, "RecordId detected");
|
|
2703
2654
|
return result;
|
|
2704
2655
|
}
|
|
@@ -2741,7 +2692,7 @@ var CacheModule = class {
|
|
|
2741
2692
|
this.logger.debug({
|
|
2742
2693
|
queryHash: update.queryHash,
|
|
2743
2694
|
arrayLength: update.localArray?.length,
|
|
2744
|
-
Category: "
|
|
2695
|
+
Category: "sp00ky-client::CacheModule::onStreamUpdate"
|
|
2745
2696
|
}, "Stream update received");
|
|
2746
2697
|
this.streamUpdateCallback(update);
|
|
2747
2698
|
}
|
|
@@ -2764,7 +2715,7 @@ var CacheModule = class {
|
|
|
2764
2715
|
if (records.length === 0) return;
|
|
2765
2716
|
this.logger.debug({
|
|
2766
2717
|
count: records.length,
|
|
2767
|
-
Category: "
|
|
2718
|
+
Category: "sp00ky-client::CacheModule::saveBatch"
|
|
2768
2719
|
}, "Saving record batch");
|
|
2769
2720
|
try {
|
|
2770
2721
|
const populatedRecords = records.map((record) => {
|
|
@@ -2773,7 +2724,7 @@ var CacheModule = class {
|
|
|
2773
2724
|
...record,
|
|
2774
2725
|
record: {
|
|
2775
2726
|
...record.record,
|
|
2776
|
-
|
|
2727
|
+
_00_rv: record.version
|
|
2777
2728
|
}
|
|
2778
2729
|
};
|
|
2779
2730
|
});
|
|
@@ -2798,13 +2749,13 @@ var CacheModule = class {
|
|
|
2798
2749
|
}
|
|
2799
2750
|
this.logger.debug({
|
|
2800
2751
|
count: records.length,
|
|
2801
|
-
Category: "
|
|
2752
|
+
Category: "sp00ky-client::CacheModule::saveBatch"
|
|
2802
2753
|
}, "Batch saved successfully");
|
|
2803
2754
|
} catch (err) {
|
|
2804
2755
|
this.logger.error({
|
|
2805
2756
|
err,
|
|
2806
2757
|
count: records.length,
|
|
2807
|
-
Category: "
|
|
2758
|
+
Category: "sp00ky-client::CacheModule::saveBatch"
|
|
2808
2759
|
}, "Failed to save batch");
|
|
2809
2760
|
throw err;
|
|
2810
2761
|
}
|
|
@@ -2812,27 +2763,27 @@ var CacheModule = class {
|
|
|
2812
2763
|
/**
|
|
2813
2764
|
* Delete a record from local DB and ingest deletion into DBSP
|
|
2814
2765
|
*/
|
|
2815
|
-
async delete(table, id, skipDbDelete = false) {
|
|
2766
|
+
async delete(table, id, skipDbDelete = false, recordData = {}) {
|
|
2816
2767
|
this.logger.debug({
|
|
2817
2768
|
table,
|
|
2818
2769
|
id,
|
|
2819
|
-
Category: "
|
|
2770
|
+
Category: "sp00ky-client::CacheModule::delete"
|
|
2820
2771
|
}, "Deleting record");
|
|
2821
2772
|
try {
|
|
2822
2773
|
if (!skipDbDelete) await this.local.query("DELETE $id", { id: parseRecordIdString(id) });
|
|
2823
2774
|
delete this.versionLookups[id];
|
|
2824
|
-
|
|
2775
|
+
this.streamProcessor.ingest(table, "DELETE", id, recordData);
|
|
2825
2776
|
this.logger.debug({
|
|
2826
2777
|
table,
|
|
2827
2778
|
id,
|
|
2828
|
-
Category: "
|
|
2779
|
+
Category: "sp00ky-client::CacheModule::delete"
|
|
2829
2780
|
}, "Record deleted successfully");
|
|
2830
2781
|
} catch (err) {
|
|
2831
2782
|
this.logger.error({
|
|
2832
2783
|
err,
|
|
2833
2784
|
table,
|
|
2834
2785
|
id,
|
|
2835
|
-
Category: "
|
|
2786
|
+
Category: "sp00ky-client::CacheModule::delete"
|
|
2836
2787
|
}, "Failed to delete record");
|
|
2837
2788
|
throw err;
|
|
2838
2789
|
}
|
|
@@ -2845,7 +2796,7 @@ var CacheModule = class {
|
|
|
2845
2796
|
this.logger.debug({
|
|
2846
2797
|
queryHash: config.queryHash,
|
|
2847
2798
|
surql: config.surql,
|
|
2848
|
-
Category: "
|
|
2799
|
+
Category: "sp00ky-client::CacheModule::registerQuery"
|
|
2849
2800
|
}, "Registering query");
|
|
2850
2801
|
try {
|
|
2851
2802
|
const update = this.streamProcessor.registerQueryPlan({
|
|
@@ -2862,14 +2813,14 @@ var CacheModule = class {
|
|
|
2862
2813
|
this.logger.debug({
|
|
2863
2814
|
queryHash: config.queryHash,
|
|
2864
2815
|
arrayLength: update.localArray?.length,
|
|
2865
|
-
Category: "
|
|
2816
|
+
Category: "sp00ky-client::CacheModule::registerQuery"
|
|
2866
2817
|
}, "Query registered successfully");
|
|
2867
2818
|
return { localArray: update.localArray };
|
|
2868
2819
|
} catch (err) {
|
|
2869
2820
|
this.logger.error({
|
|
2870
2821
|
err,
|
|
2871
2822
|
queryHash: config.queryHash,
|
|
2872
|
-
Category: "
|
|
2823
|
+
Category: "sp00ky-client::CacheModule::registerQuery"
|
|
2873
2824
|
}, "Failed to register query");
|
|
2874
2825
|
throw err;
|
|
2875
2826
|
}
|
|
@@ -2880,19 +2831,19 @@ var CacheModule = class {
|
|
|
2880
2831
|
unregisterQuery(queryHash) {
|
|
2881
2832
|
this.logger.debug({
|
|
2882
2833
|
queryHash,
|
|
2883
|
-
Category: "
|
|
2834
|
+
Category: "sp00ky-client::CacheModule::unregisterQuery"
|
|
2884
2835
|
}, "Unregistering query");
|
|
2885
2836
|
try {
|
|
2886
2837
|
this.streamProcessor.unregisterQueryPlan(queryHash);
|
|
2887
2838
|
this.logger.debug({
|
|
2888
2839
|
queryHash,
|
|
2889
|
-
Category: "
|
|
2840
|
+
Category: "sp00ky-client::CacheModule::unregisterQuery"
|
|
2890
2841
|
}, "Query unregistered successfully");
|
|
2891
2842
|
} catch (err) {
|
|
2892
2843
|
this.logger.error({
|
|
2893
2844
|
err,
|
|
2894
2845
|
queryHash,
|
|
2895
|
-
Category: "
|
|
2846
|
+
Category: "sp00ky-client::CacheModule::unregisterQuery"
|
|
2896
2847
|
}, "Failed to unregister query");
|
|
2897
2848
|
}
|
|
2898
2849
|
}
|
|
@@ -2930,7 +2881,7 @@ var SurrealDBPersistenceClient = class {
|
|
|
2930
2881
|
}
|
|
2931
2882
|
async set(key, val) {
|
|
2932
2883
|
try {
|
|
2933
|
-
const id = parseRecordIdString(`
|
|
2884
|
+
const id = parseRecordIdString(`_00_kv:${key}`);
|
|
2934
2885
|
await this.db.query(surql.seal(surql.upsert("id", "data")), {
|
|
2935
2886
|
id,
|
|
2936
2887
|
data: { val }
|
|
@@ -2938,40 +2889,69 @@ var SurrealDBPersistenceClient = class {
|
|
|
2938
2889
|
} catch (error) {
|
|
2939
2890
|
this.logger.error({
|
|
2940
2891
|
error,
|
|
2941
|
-
Category: "
|
|
2892
|
+
Category: "sp00ky-client::SurrealDBPersistenceClient::set"
|
|
2942
2893
|
}, "Failed to set KV");
|
|
2943
2894
|
throw error;
|
|
2944
2895
|
}
|
|
2945
2896
|
}
|
|
2946
2897
|
async get(key) {
|
|
2947
2898
|
try {
|
|
2948
|
-
const id = parseRecordIdString(`
|
|
2899
|
+
const id = parseRecordIdString(`_00_kv:${key}`);
|
|
2949
2900
|
const [result] = await this.db.query(surql.seal(surql.selectById("id", ["val"])), { id });
|
|
2950
2901
|
if (!result?.val) return null;
|
|
2951
2902
|
return result.val;
|
|
2952
2903
|
} catch (error) {
|
|
2953
2904
|
this.logger.warn({
|
|
2954
2905
|
error,
|
|
2955
|
-
Category: "
|
|
2906
|
+
Category: "sp00ky-client::SurrealDBPersistenceClient::get"
|
|
2956
2907
|
}, "Failed to get KV");
|
|
2957
2908
|
return null;
|
|
2958
2909
|
}
|
|
2959
2910
|
}
|
|
2960
2911
|
async remove(key) {
|
|
2961
2912
|
try {
|
|
2962
|
-
const id = parseRecordIdString(`
|
|
2913
|
+
const id = parseRecordIdString(`_00_kv:${key}`);
|
|
2963
2914
|
await this.db.query(surql.seal(surql.delete("id")), { id });
|
|
2964
2915
|
} catch (err) {
|
|
2965
2916
|
this.logger.info({
|
|
2966
2917
|
err,
|
|
2967
|
-
Category: "
|
|
2918
|
+
Category: "sp00ky-client::SurrealDBPersistenceClient::remove"
|
|
2968
2919
|
}, "Failed to delete KV");
|
|
2969
2920
|
}
|
|
2970
2921
|
}
|
|
2971
2922
|
};
|
|
2972
2923
|
|
|
2973
2924
|
//#endregion
|
|
2974
|
-
//#region src/
|
|
2925
|
+
//#region src/services/persistence/resilient.ts
|
|
2926
|
+
var ResilientPersistenceClient = class {
|
|
2927
|
+
logger;
|
|
2928
|
+
constructor(inner, logger) {
|
|
2929
|
+
this.inner = inner;
|
|
2930
|
+
this.logger = logger.child({ service: "ResilientPersistenceClient" });
|
|
2931
|
+
}
|
|
2932
|
+
set(key, value) {
|
|
2933
|
+
return this.inner.set(key, value);
|
|
2934
|
+
}
|
|
2935
|
+
async get(key) {
|
|
2936
|
+
try {
|
|
2937
|
+
return await this.inner.get(key);
|
|
2938
|
+
} catch (e) {
|
|
2939
|
+
this.logger.warn({
|
|
2940
|
+
key,
|
|
2941
|
+
error: e,
|
|
2942
|
+
Category: "sp00ky-client::ResilientPersistenceClient::get"
|
|
2943
|
+
}, "Persistence read failed, dropping key");
|
|
2944
|
+
await this.inner.remove(key).catch(() => {});
|
|
2945
|
+
return null;
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
remove(key) {
|
|
2949
|
+
return this.inner.remove(key);
|
|
2950
|
+
}
|
|
2951
|
+
};
|
|
2952
|
+
|
|
2953
|
+
//#endregion
|
|
2954
|
+
//#region src/sp00ky.ts
|
|
2975
2955
|
var BucketHandle = class {
|
|
2976
2956
|
constructor(bucketName, remote) {
|
|
2977
2957
|
this.bucketName = bucketName;
|
|
@@ -3007,7 +2987,7 @@ var BucketHandle = class {
|
|
|
3007
2987
|
return result;
|
|
3008
2988
|
}
|
|
3009
2989
|
};
|
|
3010
|
-
var
|
|
2990
|
+
var Sp00kyClient = class {
|
|
3011
2991
|
local;
|
|
3012
2992
|
remote;
|
|
3013
2993
|
persistenceClient;
|
|
@@ -3033,20 +3013,21 @@ var SpookyClient = class {
|
|
|
3033
3013
|
}
|
|
3034
3014
|
constructor(config) {
|
|
3035
3015
|
this.config = config;
|
|
3036
|
-
const logger = createLogger(config.logLevel ?? "info", config.
|
|
3037
|
-
this.logger = logger.child({ service: "
|
|
3016
|
+
const logger = createLogger(config.logLevel ?? "info", config.otelTransmit);
|
|
3017
|
+
this.logger = logger.child({ service: "Sp00kyClient" });
|
|
3038
3018
|
this.logger.info({
|
|
3039
3019
|
config: {
|
|
3040
3020
|
...config,
|
|
3041
3021
|
schema: "[SchemaStructure]"
|
|
3042
3022
|
},
|
|
3043
|
-
Category: "
|
|
3044
|
-
}, "
|
|
3023
|
+
Category: "sp00ky-client::Sp00kyClient::constructor"
|
|
3024
|
+
}, "Sp00kyClient initialized");
|
|
3045
3025
|
this.local = new LocalDatabaseService(this.config.database, logger);
|
|
3046
3026
|
this.remote = new RemoteDatabaseService(this.config.database, logger);
|
|
3047
3027
|
if (config.persistenceClient === "surrealdb") this.persistenceClient = new SurrealDBPersistenceClient(this.local, logger);
|
|
3048
3028
|
else if (config.persistenceClient === "localstorage" || !config.persistenceClient) this.persistenceClient = new LocalStoragePersistenceClient(logger);
|
|
3049
3029
|
else this.persistenceClient = config.persistenceClient;
|
|
3030
|
+
this.persistenceClient = new ResilientPersistenceClient(this.persistenceClient, logger);
|
|
3050
3031
|
this.streamProcessor = new StreamProcessorService(new EventSystem(["stream_update"]), this.local, this.persistenceClient, logger);
|
|
3051
3032
|
this.migrator = new LocalMigrator(this.local, logger);
|
|
3052
3033
|
this.cache = new CacheModule(this.local, this.streamProcessor, (update) => {
|
|
@@ -3054,7 +3035,7 @@ var SpookyClient = class {
|
|
|
3054
3035
|
}, logger);
|
|
3055
3036
|
this.dataModule = new DataModule(this.cache, this.local, this.config.schema, logger, this.config.streamDebounceTime);
|
|
3056
3037
|
this.auth = new AuthService(this.config.schema, this.remote, this.persistenceClient, logger);
|
|
3057
|
-
this.sync = new
|
|
3038
|
+
this.sync = new Sp00kySync(this.local, this.remote, this.cache, this.dataModule, this.config.schema, this.logger);
|
|
3058
3039
|
this.devTools = new DevToolsService(this.local, this.remote, logger, this.config.schema, this.auth, this.dataModule);
|
|
3059
3040
|
this.streamProcessor.addReceiver(this.devTools);
|
|
3060
3041
|
this.setupCallbacks();
|
|
@@ -3078,34 +3059,34 @@ var SpookyClient = class {
|
|
|
3078
3059
|
});
|
|
3079
3060
|
}
|
|
3080
3061
|
async init() {
|
|
3081
|
-
this.logger.info({ Category: "
|
|
3062
|
+
this.logger.info({ Category: "sp00ky-client::Sp00kyClient::init" }, "Sp00kyClient initialization started");
|
|
3082
3063
|
try {
|
|
3083
3064
|
const clientId = this.config.clientId ?? await this.loadOrGenerateClientId();
|
|
3084
3065
|
this.persistClientId(clientId);
|
|
3085
3066
|
this.logger.debug({
|
|
3086
3067
|
clientId,
|
|
3087
|
-
Category: "
|
|
3068
|
+
Category: "sp00ky-client::Sp00kyClient::init"
|
|
3088
3069
|
}, "Client ID loaded");
|
|
3089
3070
|
await this.local.connect();
|
|
3090
|
-
this.logger.debug({ Category: "
|
|
3071
|
+
this.logger.debug({ Category: "sp00ky-client::Sp00kyClient::init" }, "Local database connected");
|
|
3091
3072
|
await this.migrator.provision(this.config.schemaSurql);
|
|
3092
|
-
this.logger.debug({ Category: "
|
|
3073
|
+
this.logger.debug({ Category: "sp00ky-client::Sp00kyClient::init" }, "Schema provisioned");
|
|
3093
3074
|
await this.remote.connect();
|
|
3094
|
-
this.logger.debug({ Category: "
|
|
3075
|
+
this.logger.debug({ Category: "sp00ky-client::Sp00kyClient::init" }, "Remote database connected");
|
|
3095
3076
|
await this.streamProcessor.init();
|
|
3096
|
-
this.logger.debug({ Category: "
|
|
3077
|
+
this.logger.debug({ Category: "sp00ky-client::Sp00kyClient::init" }, "StreamProcessor initialized");
|
|
3097
3078
|
await this.auth.init();
|
|
3098
|
-
this.logger.debug({ Category: "
|
|
3079
|
+
this.logger.debug({ Category: "sp00ky-client::Sp00kyClient::init" }, "Auth initialized");
|
|
3099
3080
|
await this.dataModule.init();
|
|
3100
|
-
this.logger.debug({ Category: "
|
|
3081
|
+
this.logger.debug({ Category: "sp00ky-client::Sp00kyClient::init" }, "DataModule initialized");
|
|
3101
3082
|
await this.sync.init(clientId);
|
|
3102
|
-
this.logger.debug({ Category: "
|
|
3103
|
-
this.logger.info({ Category: "
|
|
3083
|
+
this.logger.debug({ Category: "sp00ky-client::Sp00kyClient::init" }, "Sync initialized");
|
|
3084
|
+
this.logger.info({ Category: "sp00ky-client::Sp00kyClient::init" }, "Sp00kyClient initialization completed successfully");
|
|
3104
3085
|
} catch (e) {
|
|
3105
3086
|
this.logger.error({
|
|
3106
3087
|
error: e,
|
|
3107
|
-
Category: "
|
|
3108
|
-
}, "
|
|
3088
|
+
Category: "sp00ky-client::Sp00kyClient::init"
|
|
3089
|
+
}, "Sp00kyClient initialization failed");
|
|
3109
3090
|
throw e;
|
|
3110
3091
|
}
|
|
3111
3092
|
}
|
|
@@ -3159,16 +3140,16 @@ var SpookyClient = class {
|
|
|
3159
3140
|
}
|
|
3160
3141
|
persistClientId(id) {
|
|
3161
3142
|
try {
|
|
3162
|
-
this.persistenceClient.set("
|
|
3143
|
+
this.persistenceClient.set("sp00ky_client_id", id);
|
|
3163
3144
|
} catch (e) {
|
|
3164
3145
|
this.logger.warn({
|
|
3165
3146
|
error: e,
|
|
3166
|
-
Category: "
|
|
3147
|
+
Category: "sp00ky-client::Sp00kyClient::persistClientId"
|
|
3167
3148
|
}, "Failed to persist client ID");
|
|
3168
3149
|
}
|
|
3169
3150
|
}
|
|
3170
3151
|
async loadOrGenerateClientId() {
|
|
3171
|
-
const clientId = await this.persistenceClient.get("
|
|
3152
|
+
const clientId = await this.persistenceClient.get("sp00ky_client_id");
|
|
3172
3153
|
if (clientId) return clientId;
|
|
3173
3154
|
const newId = generateId();
|
|
3174
3155
|
await this.persistClientId(newId);
|
|
@@ -3177,4 +3158,4 @@ var SpookyClient = class {
|
|
|
3177
3158
|
};
|
|
3178
3159
|
|
|
3179
3160
|
//#endregion
|
|
3180
|
-
export { AuthEventTypes, AuthService, BucketHandle,
|
|
3161
|
+
export { AuthEventTypes, AuthService, BucketHandle, Sp00kyClient, createAuthEventSystem, fileToUint8Array };
|