@spooky-sync/core 0.0.1-canary.128 → 0.0.1-canary.129
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 +9 -0
- package/dist/index.js +22 -4
- package/package.json +3 -3
- package/src/modules/data/index.ts +44 -4
- package/src/sp00ky.ts +5 -0
package/dist/index.d.ts
CHANGED
|
@@ -475,6 +475,15 @@ declare class DataModule<S extends SchemaStructure> {
|
|
|
475
475
|
* `seen` dedupes within the batch.
|
|
476
476
|
*/
|
|
477
477
|
private collectEmbeddedChildren;
|
|
478
|
+
/**
|
|
479
|
+
* Prepare a subquery-bearing row (preload / hydration) for the schemafull
|
|
480
|
+
* local store: replace an embedded FORWARD-relation object (`author = { id, … }`)
|
|
481
|
+
* with its RecordId so a `record<…>` field coerces, and DROP reverse-subquery
|
|
482
|
+
* ARRAYS (`comments = [ … ]`) since their rows are cached separately as their
|
|
483
|
+
* own bodies. A flat record — as the live `SELECT * FROM $ids` sync returns,
|
|
484
|
+
* with relations already RecordIds — passes through unchanged.
|
|
485
|
+
*/
|
|
486
|
+
private flattenRelationsForStorage;
|
|
478
487
|
/**
|
|
479
488
|
* Instant-hydrate: ingest rows fetched one-shot from the remote (the query's own
|
|
480
489
|
* surql run directly) so the query DISPLAYS immediately, while the full realtime
|
package/dist/index.js
CHANGED
|
@@ -2689,13 +2689,29 @@ var DataModule = class {
|
|
|
2689
2689
|
batch.push({
|
|
2690
2690
|
table,
|
|
2691
2691
|
op: "CREATE",
|
|
2692
|
-
record:
|
|
2692
|
+
record: this.flattenRelationsForStorage(child, tableSchema),
|
|
2693
2693
|
version: child._00_rv || 1
|
|
2694
2694
|
});
|
|
2695
2695
|
}
|
|
2696
2696
|
}
|
|
2697
2697
|
}
|
|
2698
2698
|
/**
|
|
2699
|
+
* Prepare a subquery-bearing row (preload / hydration) for the schemafull
|
|
2700
|
+
* local store: replace an embedded FORWARD-relation object (`author = { id, … }`)
|
|
2701
|
+
* with its RecordId so a `record<…>` field coerces, and DROP reverse-subquery
|
|
2702
|
+
* ARRAYS (`comments = [ … ]`) since their rows are cached separately as their
|
|
2703
|
+
* own bodies. A flat record — as the live `SELECT * FROM $ids` sync returns,
|
|
2704
|
+
* with relations already RecordIds — passes through unchanged.
|
|
2705
|
+
*/
|
|
2706
|
+
flattenRelationsForStorage(record, tableSchema) {
|
|
2707
|
+
const isEmbeddedRecord = (v) => !!v && typeof v === "object" && !(v instanceof RecordId) && v.id instanceof RecordId;
|
|
2708
|
+
const flat = {};
|
|
2709
|
+
for (const [k, v] of Object.entries(record)) if (isEmbeddedRecord(v)) flat[k] = v.id;
|
|
2710
|
+
else if (Array.isArray(v) && v.some(isEmbeddedRecord)) continue;
|
|
2711
|
+
else flat[k] = v;
|
|
2712
|
+
return tableSchema ? cleanRecord(tableSchema.columns, flat) : flat;
|
|
2713
|
+
}
|
|
2714
|
+
/**
|
|
2699
2715
|
* Instant-hydrate: ingest rows fetched one-shot from the remote (the query's own
|
|
2700
2716
|
* surql run directly) so the query DISPLAYS immediately, while the full realtime
|
|
2701
2717
|
* registration proceeds in the background. Ingests with versions (`_00_rv`) so the
|
|
@@ -2725,10 +2741,11 @@ var DataModule = class {
|
|
|
2725
2741
|
* (preload).
|
|
2726
2742
|
*/
|
|
2727
2743
|
async buildAndSaveCacheBatch(tableName, rows) {
|
|
2744
|
+
const tableSchema = this.schema.tables.find((t) => t.name === tableName);
|
|
2728
2745
|
const batch = rows.map((record) => ({
|
|
2729
2746
|
table: tableName,
|
|
2730
2747
|
op: "CREATE",
|
|
2731
|
-
record,
|
|
2748
|
+
record: this.flattenRelationsForStorage(record, tableSchema),
|
|
2732
2749
|
version: record._00_rv || 1
|
|
2733
2750
|
}));
|
|
2734
2751
|
const seen = new Set(rows.map((r) => encodeRecordId(r.id)));
|
|
@@ -5101,8 +5118,8 @@ function parseBackendInfo(raw) {
|
|
|
5101
5118
|
|
|
5102
5119
|
//#endregion
|
|
5103
5120
|
//#region src/modules/devtools/index.ts
|
|
5104
|
-
const CORE_VERSION = "0.0.1-canary.
|
|
5105
|
-
const WASM_VERSION = "0.0.1-canary.
|
|
5121
|
+
const CORE_VERSION = "0.0.1-canary.129";
|
|
5122
|
+
const WASM_VERSION = "0.0.1-canary.129";
|
|
5106
5123
|
const SURREAL_VERSION = "3.0.3";
|
|
5107
5124
|
var DevToolsService = class {
|
|
5108
5125
|
eventsHistory = [];
|
|
@@ -7310,6 +7327,7 @@ var Sp00kyClient = class {
|
|
|
7310
7327
|
await this.streamProcessor.reset();
|
|
7311
7328
|
this.streamProcessor.setPermissions(extractSelectPermissions(this.config.schemaSurql));
|
|
7312
7329
|
this.cache.clearVersionLookups();
|
|
7330
|
+
this.preloadedHashes.clear();
|
|
7313
7331
|
} finally {
|
|
7314
7332
|
reopen();
|
|
7315
7333
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spooky-sync/core",
|
|
3
|
-
"version": "0.0.1-canary.
|
|
3
|
+
"version": "0.0.1-canary.129",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@spooky-sync/query-builder": "0.0.1-canary.
|
|
63
|
-
"@spooky-sync/ssp-wasm": "0.0.1-canary.
|
|
62
|
+
"@spooky-sync/query-builder": "0.0.1-canary.129",
|
|
63
|
+
"@spooky-sync/ssp-wasm": "0.0.1-canary.129",
|
|
64
64
|
"@sqlite.org/sqlite-wasm": "3.53.0-build1",
|
|
65
65
|
"@surrealdb/wasm": "^3.0.3",
|
|
66
66
|
"fast-json-patch": "^3.1.1",
|
|
@@ -739,15 +739,47 @@ export class DataModule<S extends SchemaStructure> {
|
|
|
739
739
|
batch.push({
|
|
740
740
|
table,
|
|
741
741
|
op: 'CREATE',
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
742
|
+
// Flatten the child too: a nested comment carries its own embedded
|
|
743
|
+
// `author` object which the schemafull `record<user>` field would
|
|
744
|
+
// reject. (Its author is captured as a separate row by the recursion
|
|
745
|
+
// above.)
|
|
746
|
+
record: this.flattenRelationsForStorage(child, tableSchema),
|
|
745
747
|
version: (child._00_rv as number) || 1,
|
|
746
748
|
});
|
|
747
749
|
}
|
|
748
750
|
}
|
|
749
751
|
}
|
|
750
752
|
|
|
753
|
+
/**
|
|
754
|
+
* Prepare a subquery-bearing row (preload / hydration) for the schemafull
|
|
755
|
+
* local store: replace an embedded FORWARD-relation object (`author = { id, … }`)
|
|
756
|
+
* with its RecordId so a `record<…>` field coerces, and DROP reverse-subquery
|
|
757
|
+
* ARRAYS (`comments = [ … ]`) since their rows are cached separately as their
|
|
758
|
+
* own bodies. A flat record — as the live `SELECT * FROM $ids` sync returns,
|
|
759
|
+
* with relations already RecordIds — passes through unchanged.
|
|
760
|
+
*/
|
|
761
|
+
private flattenRelationsForStorage(
|
|
762
|
+
record: Record<string, any>,
|
|
763
|
+
tableSchema?: { columns: Record<string, any> }
|
|
764
|
+
): RecordWithId {
|
|
765
|
+
const isEmbeddedRecord = (v: unknown): boolean =>
|
|
766
|
+
!!v &&
|
|
767
|
+
typeof v === 'object' &&
|
|
768
|
+
!(v instanceof RecordId) &&
|
|
769
|
+
(v as { id?: unknown }).id instanceof RecordId;
|
|
770
|
+
const flat: Record<string, any> = {};
|
|
771
|
+
for (const [k, v] of Object.entries(record)) {
|
|
772
|
+
if (isEmbeddedRecord(v)) {
|
|
773
|
+
flat[k] = (v as { id: unknown }).id;
|
|
774
|
+
} else if (Array.isArray(v) && v.some(isEmbeddedRecord)) {
|
|
775
|
+
continue;
|
|
776
|
+
} else {
|
|
777
|
+
flat[k] = v;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return (tableSchema ? cleanRecord(tableSchema.columns, flat) : flat) as RecordWithId;
|
|
781
|
+
}
|
|
782
|
+
|
|
751
783
|
/**
|
|
752
784
|
* Instant-hydrate: ingest rows fetched one-shot from the remote (the query's own
|
|
753
785
|
* surql run directly) so the query DISPLAYS immediately, while the full realtime
|
|
@@ -792,10 +824,18 @@ export class DataModule<S extends SchemaStructure> {
|
|
|
792
824
|
tableName: string,
|
|
793
825
|
rows: RecordWithId[]
|
|
794
826
|
): Promise<void> {
|
|
827
|
+
const tableSchema = this.schema.tables.find((t) => t.name === tableName);
|
|
795
828
|
const batch: CacheRecord[] = rows.map((record) => ({
|
|
796
829
|
table: tableName,
|
|
797
830
|
op: 'CREATE' as const,
|
|
798
|
-
|
|
831
|
+
// Flatten embedded relations first: a preload/hydration row can carry a
|
|
832
|
+
// forward relation as a full nested OBJECT (`author = { id, … }`) and a
|
|
833
|
+
// reverse subquery as an array of objects (`comments = [ … ]`). The
|
|
834
|
+
// schemafull local field is `record<user>`, which rejects an object
|
|
835
|
+
// (`Couldn't coerce … found { id: …, username: … }`) and would throw the
|
|
836
|
+
// WHOLE batch. Store the parent with `author` as its RecordId and drop the
|
|
837
|
+
// subquery arrays — the children are cached as their own rows below.
|
|
838
|
+
record: this.flattenRelationsForStorage(record, tableSchema),
|
|
799
839
|
version: (record._00_rv as number) || 1,
|
|
800
840
|
}));
|
|
801
841
|
|
package/src/sp00ky.ts
CHANGED
|
@@ -589,6 +589,11 @@ export class Sp00kyClient<S extends SchemaStructure> {
|
|
|
589
589
|
await this.streamProcessor.reset();
|
|
590
590
|
this.streamProcessor.setPermissions(extractSelectPermissions(this.config.schemaSurql));
|
|
591
591
|
this.cache.clearVersionLookups();
|
|
592
|
+
// Preload dedup is per-bucket: the `_00_preload` markers + cached rows it
|
|
593
|
+
// guards live in the local store we just swapped away from. Keeping the
|
|
594
|
+
// hashes would make `preload()` skip warming the NEW bucket (its store is
|
|
595
|
+
// empty), so every thread/comment prewarm silently no-ops after login.
|
|
596
|
+
this.preloadedHashes.clear();
|
|
592
597
|
} finally {
|
|
593
598
|
reopen();
|
|
594
599
|
}
|