@foretag/tanstack-db-surrealdb 0.6.12 → 0.7.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/README.md +164 -75
- package/dist/index.d.mts +28 -7
- package/dist/index.d.ts +28 -7
- package/dist/index.js +342 -305
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +342 -306
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -11
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { persistedCollectionOptions } from '@tanstack/db-sqlite-persistence-core';
|
|
1
2
|
import { queryCollectionOptions } from '@tanstack/query-db-collection';
|
|
2
3
|
import { LoroDoc } from 'loro-crdt';
|
|
3
4
|
import { Table, RecordId, Features } from 'surrealdb';
|
|
5
|
+
import { hashKey } from '@tanstack/query-core';
|
|
4
6
|
import { parseOrderByExpression, parseWhereExpression } from '@tanstack/db';
|
|
5
7
|
|
|
6
8
|
// src/index.ts
|
|
@@ -288,12 +290,313 @@ var toRecordId = (tableName, id) => {
|
|
|
288
290
|
const key = normalized.startsWith(prefixed) ? normalized.slice(prefixed.length) : normalized;
|
|
289
291
|
return new RecordId(tableName, key);
|
|
290
292
|
};
|
|
291
|
-
var
|
|
292
|
-
var
|
|
293
|
-
|
|
294
|
-
if (
|
|
295
|
-
|
|
293
|
+
var SURREAL_COLLECTION_ID_PREFIX = "surreal";
|
|
294
|
+
var patchedCollections = /* @__PURE__ */ new WeakSet();
|
|
295
|
+
var normalizeDeleteKeyAgainstState = (state, key) => {
|
|
296
|
+
if (state.has(key)) return key;
|
|
297
|
+
const canonical = asCanonicalRecordIdString(key);
|
|
298
|
+
if (!canonical) return key;
|
|
299
|
+
return state.has(canonical) ? canonical : key;
|
|
300
|
+
};
|
|
301
|
+
var patchCollectionDeleteForRecordIds = (collection) => {
|
|
302
|
+
if (!collection || typeof collection !== "object") return;
|
|
303
|
+
if (patchedCollections.has(collection)) return;
|
|
304
|
+
const candidate = collection;
|
|
305
|
+
if (typeof candidate.delete !== "function") return;
|
|
306
|
+
const originalDelete = candidate.delete.bind(collection);
|
|
307
|
+
Object.defineProperty(collection, "delete", {
|
|
308
|
+
configurable: true,
|
|
309
|
+
writable: true,
|
|
310
|
+
value: (keys, config) => {
|
|
311
|
+
const state = collection.state ?? /* @__PURE__ */ new Map();
|
|
312
|
+
const normalizedKeys = Array.isArray(keys) ? keys.map((key) => normalizeDeleteKeyAgainstState(state, key)) : normalizeDeleteKeyAgainstState(state, keys);
|
|
313
|
+
return originalDelete(normalizedKeys, config);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
patchedCollections.add(collection);
|
|
317
|
+
};
|
|
318
|
+
var deriveCollectionId = (tableName, queryKey) => `${SURREAL_COLLECTION_ID_PREFIX}:${tableName}:${hashKey(queryKey)}`;
|
|
319
|
+
|
|
320
|
+
// src/util.ts
|
|
321
|
+
var encoder = new TextEncoder();
|
|
322
|
+
var decoder = new TextDecoder();
|
|
323
|
+
var hasBuffer = typeof Buffer !== "undefined";
|
|
324
|
+
function toBytes(value) {
|
|
325
|
+
if (typeof value === "string") return encoder.encode(value);
|
|
326
|
+
return encoder.encode(JSON.stringify(value));
|
|
327
|
+
}
|
|
328
|
+
function fromBytes(bytes, deserialize = false) {
|
|
329
|
+
if (deserialize) return JSON.parse(decoder.decode(bytes));
|
|
330
|
+
return decoder.decode(bytes);
|
|
331
|
+
}
|
|
332
|
+
function toBase64(bytes) {
|
|
333
|
+
if (hasBuffer) return Buffer.from(bytes).toString("base64");
|
|
334
|
+
return btoa(String.fromCharCode(...bytes));
|
|
335
|
+
}
|
|
336
|
+
function fromBase64(base64) {
|
|
337
|
+
if (hasBuffer) return new Uint8Array(Buffer.from(base64, "base64"));
|
|
338
|
+
const bin = atob(base64);
|
|
339
|
+
const out = new Uint8Array(bin.length);
|
|
340
|
+
for (let i = 0; i < bin.length; i++) {
|
|
341
|
+
out[i] = bin.charCodeAt(i);
|
|
342
|
+
}
|
|
343
|
+
return out;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/internal/envelope.ts
|
|
347
|
+
var ENVELOPE_FIELDS = [
|
|
348
|
+
"version",
|
|
349
|
+
"algorithm",
|
|
350
|
+
"key_id",
|
|
351
|
+
"nonce",
|
|
352
|
+
"ciphertext"
|
|
353
|
+
];
|
|
354
|
+
function defaultAad(ctx) {
|
|
355
|
+
if (ctx.kind === "base") return toBytes(`${ctx.table}:${ctx.id}`);
|
|
356
|
+
const base = ctx.baseTable ?? ctx.table;
|
|
357
|
+
return toBytes(`${ctx.table}:${base}:${ctx.id}`);
|
|
358
|
+
}
|
|
359
|
+
var toEnvelope = (value) => {
|
|
360
|
+
const version = value.version;
|
|
361
|
+
const algorithm = value.algorithm;
|
|
362
|
+
const keyId = value.key_id;
|
|
363
|
+
const nonce = value.nonce;
|
|
364
|
+
const ciphertext = value.ciphertext;
|
|
365
|
+
if (typeof version !== "number" || typeof algorithm !== "string" || typeof keyId !== "string" || typeof nonce !== "string" || typeof ciphertext !== "string") {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
v: version,
|
|
370
|
+
alg: algorithm,
|
|
371
|
+
kid: keyId,
|
|
372
|
+
n: nonce,
|
|
373
|
+
ct: ciphertext
|
|
374
|
+
};
|
|
375
|
+
};
|
|
376
|
+
var toStoredEnvelope = (envelope) => ({
|
|
377
|
+
version: envelope.v,
|
|
378
|
+
algorithm: envelope.alg,
|
|
379
|
+
key_id: envelope.kid,
|
|
380
|
+
nonce: envelope.n,
|
|
381
|
+
ciphertext: envelope.ct
|
|
382
|
+
});
|
|
383
|
+
var stripEnvelopeFields = (value) => {
|
|
384
|
+
const copy = { ...value };
|
|
385
|
+
for (const key of ENVELOPE_FIELDS) delete copy[key];
|
|
386
|
+
return copy;
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// src/internal/records.ts
|
|
390
|
+
var firstRow = (result) => {
|
|
391
|
+
if (!result) return void 0;
|
|
392
|
+
if (Array.isArray(result)) return result[0];
|
|
393
|
+
return result;
|
|
394
|
+
};
|
|
395
|
+
var toRecordArray = (rows) => {
|
|
396
|
+
if (!rows) return [];
|
|
397
|
+
return Array.isArray(rows) ? rows : [rows];
|
|
398
|
+
};
|
|
399
|
+
var omitUndefined = (obj) => Object.fromEntries(
|
|
400
|
+
Object.entries(obj).filter(([, value]) => value !== void 0)
|
|
401
|
+
);
|
|
402
|
+
var isPlainObject2 = (value) => typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
|
|
403
|
+
async function queryRows(db, sql, bindings) {
|
|
404
|
+
const result = await db.query(sql, bindings ?? {});
|
|
405
|
+
if (Array.isArray(result)) {
|
|
406
|
+
const first = result[0];
|
|
407
|
+
if (Array.isArray(first)) return first;
|
|
408
|
+
}
|
|
409
|
+
return [];
|
|
410
|
+
}
|
|
411
|
+
var TEMP_ID_PREFIX = "__temp__";
|
|
412
|
+
var createTempRecordId = (tableName) => {
|
|
413
|
+
const suffix = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
414
|
+
return new RecordId(tableName, `${TEMP_ID_PREFIX}${suffix}`);
|
|
296
415
|
};
|
|
416
|
+
var isTempId = (id, tableName) => {
|
|
417
|
+
const normalized = toRecordIdString(id);
|
|
418
|
+
const key = normalized.startsWith(`${tableName}:`) ? normalized.slice(tableName.length + 1) : normalized;
|
|
419
|
+
return key.startsWith(TEMP_ID_PREFIX);
|
|
420
|
+
};
|
|
421
|
+
function createInsertSchema(tableName) {
|
|
422
|
+
return {
|
|
423
|
+
"~standard": {
|
|
424
|
+
version: 1,
|
|
425
|
+
vendor: "tanstack-db-surrealdb",
|
|
426
|
+
validate: (value) => {
|
|
427
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
428
|
+
return {
|
|
429
|
+
issues: [{ message: "Insert data must be an object." }]
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
const data = normalizeRecordIdLikeFields({
|
|
433
|
+
...value
|
|
434
|
+
});
|
|
435
|
+
if (!data.id)
|
|
436
|
+
data.id = createTempRecordId(tableName);
|
|
437
|
+
return { value: data };
|
|
438
|
+
},
|
|
439
|
+
types: void 0
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
var subsetCacheKey = (subset) => {
|
|
444
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
445
|
+
return JSON.stringify(subset, (_key, value) => {
|
|
446
|
+
if (value instanceof Date) return value.toISOString();
|
|
447
|
+
if (value instanceof RecordId) return toRecordIdString(value);
|
|
448
|
+
if (typeof value === "bigint") return value.toString();
|
|
449
|
+
if (typeof value === "function")
|
|
450
|
+
return `[fn:${value.name || "anonymous"}]`;
|
|
451
|
+
if (value && typeof value === "object") {
|
|
452
|
+
const canonical = asCanonicalRecordIdString(value);
|
|
453
|
+
if (canonical) return canonical;
|
|
454
|
+
if (seen.has(value)) return "[Circular]";
|
|
455
|
+
seen.add(value);
|
|
456
|
+
}
|
|
457
|
+
return value;
|
|
458
|
+
}) ?? "";
|
|
459
|
+
};
|
|
460
|
+
var normalizeSubsetValuesInPlace = (value, seen = /* @__PURE__ */ new WeakSet(), preferredByCanonical = /* @__PURE__ */ new Map()) => {
|
|
461
|
+
if (Array.isArray(value)) {
|
|
462
|
+
for (const entry of value) {
|
|
463
|
+
normalizeSubsetValuesInPlace(entry, seen, preferredByCanonical);
|
|
464
|
+
}
|
|
465
|
+
return preferredByCanonical;
|
|
466
|
+
}
|
|
467
|
+
if (!value || typeof value !== "object") return preferredByCanonical;
|
|
468
|
+
if (seen.has(value)) return preferredByCanonical;
|
|
469
|
+
seen.add(value);
|
|
470
|
+
const obj = value;
|
|
471
|
+
if (obj.type === "val" && "value" in obj) {
|
|
472
|
+
const canonical = asCanonicalRecordIdString(obj.value);
|
|
473
|
+
if (canonical) {
|
|
474
|
+
const preferred = preferRecordIdLikeIdentity(obj.value);
|
|
475
|
+
obj.value = preferred;
|
|
476
|
+
preferredByCanonical.set(canonical, preferred);
|
|
477
|
+
} else {
|
|
478
|
+
obj.value = preferRecordIdLikeIdentityDeep(obj.value);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
for (const child of Object.values(obj)) {
|
|
482
|
+
normalizeSubsetValuesInPlace(child, seen, preferredByCanonical);
|
|
483
|
+
}
|
|
484
|
+
return preferredByCanonical;
|
|
485
|
+
};
|
|
486
|
+
var primeRecordIdIdentityFromSubset = (subset) => {
|
|
487
|
+
if (!subset) return /* @__PURE__ */ new Map();
|
|
488
|
+
return normalizeSubsetValuesInPlace(subset);
|
|
489
|
+
};
|
|
490
|
+
var rebindRecordIdIdentityDeep = (value, preferredByCanonical) => {
|
|
491
|
+
const canonical = asCanonicalRecordIdString(value);
|
|
492
|
+
if (canonical && preferredByCanonical.has(canonical)) {
|
|
493
|
+
const preferred = preferredByCanonical.get(canonical);
|
|
494
|
+
return {
|
|
495
|
+
value: preferred,
|
|
496
|
+
changed: value !== preferred
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
if (Array.isArray(value)) {
|
|
500
|
+
let changed2 = false;
|
|
501
|
+
const out2 = value.map((entry) => {
|
|
502
|
+
const rebound = rebindRecordIdIdentityDeep(
|
|
503
|
+
entry,
|
|
504
|
+
preferredByCanonical
|
|
505
|
+
);
|
|
506
|
+
changed2 = changed2 || rebound.changed;
|
|
507
|
+
return rebound.value;
|
|
508
|
+
});
|
|
509
|
+
return changed2 ? { value: out2, changed: true } : { value, changed: false };
|
|
510
|
+
}
|
|
511
|
+
if (!isPlainObject2(value)) return { value, changed: false };
|
|
512
|
+
let changed = false;
|
|
513
|
+
const out = {};
|
|
514
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
515
|
+
const rebound = rebindRecordIdIdentityDeep(entry, preferredByCanonical);
|
|
516
|
+
if (rebound.changed) changed = true;
|
|
517
|
+
out[key] = rebound.value;
|
|
518
|
+
}
|
|
519
|
+
return changed ? { value: out, changed: true } : { value, changed: false };
|
|
520
|
+
};
|
|
521
|
+
var applyPreferredRecordIdIdentityToCollection = (ctx, preferredByCanonical) => {
|
|
522
|
+
if (!preferredByCanonical.size) return;
|
|
523
|
+
const collection = ctx.collection;
|
|
524
|
+
if (!collection || typeof collection.entries !== "function") return;
|
|
525
|
+
const rebound = [];
|
|
526
|
+
for (const [, row] of collection.entries()) {
|
|
527
|
+
const updated = rebindRecordIdIdentityDeep(row, preferredByCanonical);
|
|
528
|
+
if (!updated.changed) continue;
|
|
529
|
+
rebound.push(updated.value);
|
|
530
|
+
}
|
|
531
|
+
if (!rebound.length) return;
|
|
532
|
+
ctx.begin();
|
|
533
|
+
try {
|
|
534
|
+
for (const row of rebound) {
|
|
535
|
+
ctx.write({
|
|
536
|
+
type: "delete",
|
|
537
|
+
value: { id: row.id }
|
|
538
|
+
});
|
|
539
|
+
ctx.write({ type: "insert", value: row });
|
|
540
|
+
}
|
|
541
|
+
} finally {
|
|
542
|
+
ctx.commit();
|
|
543
|
+
}
|
|
544
|
+
const collectionWithInternals = collection;
|
|
545
|
+
const entries = collectionWithInternals._state?.entries?.();
|
|
546
|
+
const indexes = collectionWithInternals._indexes?.indexes;
|
|
547
|
+
if (entries && indexes) {
|
|
548
|
+
for (const index of indexes.values()) {
|
|
549
|
+
index.build?.(entries);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
// src/internal/subset-tracker.ts
|
|
555
|
+
var ActiveSubsetTracker = class {
|
|
556
|
+
subsetIds = /* @__PURE__ */ new Map();
|
|
557
|
+
activeIds = /* @__PURE__ */ new Set();
|
|
558
|
+
get size() {
|
|
559
|
+
return this.subsetIds.size;
|
|
560
|
+
}
|
|
561
|
+
has(id) {
|
|
562
|
+
return this.activeIds.has(id);
|
|
563
|
+
}
|
|
564
|
+
set(key, ids) {
|
|
565
|
+
this.subsetIds.set(key, ids);
|
|
566
|
+
this.rebuildActiveIds();
|
|
567
|
+
}
|
|
568
|
+
delete(key) {
|
|
569
|
+
this.subsetIds.delete(key);
|
|
570
|
+
this.rebuildActiveIds();
|
|
571
|
+
}
|
|
572
|
+
deleteId(id) {
|
|
573
|
+
for (const ids of this.subsetIds.values()) ids.delete(id);
|
|
574
|
+
this.rebuildActiveIds();
|
|
575
|
+
}
|
|
576
|
+
clear() {
|
|
577
|
+
this.subsetIds.clear();
|
|
578
|
+
this.rebuildActiveIds();
|
|
579
|
+
}
|
|
580
|
+
rebuildActiveIds() {
|
|
581
|
+
this.activeIds.clear();
|
|
582
|
+
for (const ids of this.subsetIds.values()) {
|
|
583
|
+
for (const id of ids) this.activeIds.add(id);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
var isTableObject = (value) => typeof value === "object" && value !== null && "name" in value && typeof value.name === "string";
|
|
588
|
+
var toTableOptions = (table) => {
|
|
589
|
+
if (typeof table === "string") return { name: table };
|
|
590
|
+
if (table instanceof Table) return { name: table.name };
|
|
591
|
+
if (isTableObject(table)) return table;
|
|
592
|
+
throw new Error("Expected table as string, Table, or { name }.");
|
|
593
|
+
};
|
|
594
|
+
var toTableResource = (table) => {
|
|
595
|
+
const normalized = toTableOptions(table);
|
|
596
|
+
return new Table(normalized.name);
|
|
597
|
+
};
|
|
598
|
+
var tableNameOf = (table) => toTableOptions(table).name;
|
|
599
|
+
var IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
297
600
|
var toFieldPath = (value) => {
|
|
298
601
|
if (!Array.isArray(value)) {
|
|
299
602
|
throw new Error("Expected a field path array in where expression.");
|
|
@@ -503,43 +806,17 @@ function manageTable(db, config) {
|
|
|
503
806
|
if (killed) return;
|
|
504
807
|
killed = true;
|
|
505
808
|
if (live) void live.kill();
|
|
506
|
-
};
|
|
507
|
-
};
|
|
508
|
-
return {
|
|
509
|
-
listAll,
|
|
510
|
-
loadSubset,
|
|
511
|
-
create,
|
|
512
|
-
update,
|
|
513
|
-
remove,
|
|
514
|
-
softDelete: remove,
|
|
515
|
-
subscribe
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// src/util.ts
|
|
520
|
-
var encoder = new TextEncoder();
|
|
521
|
-
var decoder = new TextDecoder();
|
|
522
|
-
var hasBuffer = typeof Buffer !== "undefined";
|
|
523
|
-
function toBytes(value) {
|
|
524
|
-
if (typeof value === "string") return encoder.encode(value);
|
|
525
|
-
return encoder.encode(JSON.stringify(value));
|
|
526
|
-
}
|
|
527
|
-
function fromBytes(bytes, deserialize = false) {
|
|
528
|
-
if (deserialize) return JSON.parse(decoder.decode(bytes));
|
|
529
|
-
return decoder.decode(bytes);
|
|
530
|
-
}
|
|
531
|
-
function toBase64(bytes) {
|
|
532
|
-
if (hasBuffer) return Buffer.from(bytes).toString("base64");
|
|
533
|
-
return btoa(String.fromCharCode(...bytes));
|
|
534
|
-
}
|
|
535
|
-
function fromBase64(base64) {
|
|
536
|
-
if (hasBuffer) return new Uint8Array(Buffer.from(base64, "base64"));
|
|
537
|
-
const bin = atob(base64);
|
|
538
|
-
const out = new Uint8Array(bin.length);
|
|
539
|
-
for (let i = 0; i < bin.length; i++) {
|
|
540
|
-
out[i] = bin.charCodeAt(i);
|
|
541
|
-
}
|
|
542
|
-
return out;
|
|
809
|
+
};
|
|
810
|
+
};
|
|
811
|
+
return {
|
|
812
|
+
listAll,
|
|
813
|
+
loadSubset,
|
|
814
|
+
create,
|
|
815
|
+
update,
|
|
816
|
+
remove,
|
|
817
|
+
softDelete: remove,
|
|
818
|
+
subscribe
|
|
819
|
+
};
|
|
543
820
|
}
|
|
544
821
|
|
|
545
822
|
// src/encryption/index.ts
|
|
@@ -562,7 +839,9 @@ var WebCryptoAESGCM = class _WebCryptoAESGCM {
|
|
|
562
839
|
this.kid = options.kid ?? DEFAULT_KID;
|
|
563
840
|
this.resolveKey = options.resolveKey ?? ((incomingKid) => {
|
|
564
841
|
if (incomingKid !== this.kid) {
|
|
565
|
-
throw new Error(
|
|
842
|
+
throw new Error(
|
|
843
|
+
`No key configured for kid '${incomingKid}'.`
|
|
844
|
+
);
|
|
566
845
|
}
|
|
567
846
|
return key;
|
|
568
847
|
});
|
|
@@ -628,251 +907,10 @@ var WebCryptoAESGCM = class _WebCryptoAESGCM {
|
|
|
628
907
|
};
|
|
629
908
|
|
|
630
909
|
// src/index.ts
|
|
631
|
-
var TEMP_ID_PREFIX = "__temp__";
|
|
632
|
-
var ENVELOPE_FIELDS = [
|
|
633
|
-
"version",
|
|
634
|
-
"algorithm",
|
|
635
|
-
"key_id",
|
|
636
|
-
"nonce",
|
|
637
|
-
"ciphertext"
|
|
638
|
-
];
|
|
639
910
|
var NOOP = () => {
|
|
640
911
|
};
|
|
641
|
-
var patchedCollections = /* @__PURE__ */ new WeakSet();
|
|
642
|
-
var normalizeDeleteKeyAgainstState = (state, key) => {
|
|
643
|
-
if (state.has(key)) return key;
|
|
644
|
-
const canonical = asCanonicalRecordIdString(key);
|
|
645
|
-
if (!canonical) return key;
|
|
646
|
-
return state.has(canonical) ? canonical : key;
|
|
647
|
-
};
|
|
648
|
-
var patchCollectionDeleteForRecordIds = (collection) => {
|
|
649
|
-
if (!collection || typeof collection !== "object") return;
|
|
650
|
-
if (patchedCollections.has(collection)) return;
|
|
651
|
-
const candidate = collection;
|
|
652
|
-
if (typeof candidate.delete !== "function") return;
|
|
653
|
-
const originalDelete = candidate.delete.bind(collection);
|
|
654
|
-
Object.defineProperty(collection, "delete", {
|
|
655
|
-
configurable: true,
|
|
656
|
-
writable: true,
|
|
657
|
-
value: (keys, config) => {
|
|
658
|
-
const state = collection.state ?? /* @__PURE__ */ new Map();
|
|
659
|
-
const normalizedKeys = Array.isArray(keys) ? keys.map((key) => normalizeDeleteKeyAgainstState(state, key)) : normalizeDeleteKeyAgainstState(state, keys);
|
|
660
|
-
return originalDelete(normalizedKeys, config);
|
|
661
|
-
}
|
|
662
|
-
});
|
|
663
|
-
patchedCollections.add(collection);
|
|
664
|
-
};
|
|
665
|
-
var isTableObject = (value) => typeof value === "object" && value !== null && "name" in value && typeof value.name === "string";
|
|
666
|
-
var toTableOptions = (table) => {
|
|
667
|
-
if (typeof table === "string") return { name: table };
|
|
668
|
-
if (table instanceof Table) return { name: table.name };
|
|
669
|
-
if (isTableObject(table)) return table;
|
|
670
|
-
throw new Error("Expected table as string, Table, or { name }.");
|
|
671
|
-
};
|
|
672
|
-
var toTableResource = (table) => {
|
|
673
|
-
const normalized = toTableOptions(table);
|
|
674
|
-
return new Table(normalized.name);
|
|
675
|
-
};
|
|
676
|
-
var tableNameOf = (table) => toTableOptions(table).name;
|
|
677
912
|
var getWriteUtils = (utils) => typeof utils === "object" && utils !== null ? utils : {};
|
|
678
|
-
var firstRow2 = (result) => {
|
|
679
|
-
if (!result) return void 0;
|
|
680
|
-
if (Array.isArray(result)) return result[0];
|
|
681
|
-
return result;
|
|
682
|
-
};
|
|
683
|
-
var omitUndefined = (obj) => Object.fromEntries(
|
|
684
|
-
Object.entries(obj).filter(([, value]) => value !== void 0)
|
|
685
|
-
);
|
|
686
|
-
var createTempRecordId = (tableName) => {
|
|
687
|
-
const suffix = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
688
|
-
return new RecordId(tableName, `${TEMP_ID_PREFIX}${suffix}`);
|
|
689
|
-
};
|
|
690
|
-
var isTempId = (id, tableName) => {
|
|
691
|
-
const normalized = toRecordIdString(id);
|
|
692
|
-
const key = normalized.startsWith(`${tableName}:`) ? normalized.slice(tableName.length + 1) : normalized;
|
|
693
|
-
return key.startsWith(TEMP_ID_PREFIX);
|
|
694
|
-
};
|
|
695
|
-
var toEnvelope = (value) => {
|
|
696
|
-
const version = value.version;
|
|
697
|
-
const algorithm = value.algorithm;
|
|
698
|
-
const keyId = value.key_id;
|
|
699
|
-
const nonce = value.nonce;
|
|
700
|
-
const ciphertext = value.ciphertext;
|
|
701
|
-
if (typeof version !== "number" || typeof algorithm !== "string" || typeof keyId !== "string" || typeof nonce !== "string" || typeof ciphertext !== "string") {
|
|
702
|
-
return null;
|
|
703
|
-
}
|
|
704
|
-
return {
|
|
705
|
-
v: version,
|
|
706
|
-
alg: algorithm,
|
|
707
|
-
kid: keyId,
|
|
708
|
-
n: nonce,
|
|
709
|
-
ct: ciphertext
|
|
710
|
-
};
|
|
711
|
-
};
|
|
712
|
-
var toStoredEnvelope = (envelope) => ({
|
|
713
|
-
version: envelope.v,
|
|
714
|
-
algorithm: envelope.alg,
|
|
715
|
-
key_id: envelope.kid,
|
|
716
|
-
nonce: envelope.n,
|
|
717
|
-
ciphertext: envelope.ct
|
|
718
|
-
});
|
|
719
|
-
var stripEnvelopeFields = (value) => {
|
|
720
|
-
const copy = { ...value };
|
|
721
|
-
for (const key of ENVELOPE_FIELDS) delete copy[key];
|
|
722
|
-
return copy;
|
|
723
|
-
};
|
|
724
|
-
var toRecordArray = (rows) => {
|
|
725
|
-
if (!rows) return [];
|
|
726
|
-
return Array.isArray(rows) ? rows : [rows];
|
|
727
|
-
};
|
|
728
|
-
async function queryRows(db, sql, bindings) {
|
|
729
|
-
const result = await db.query(sql, bindings ?? {});
|
|
730
|
-
if (Array.isArray(result)) {
|
|
731
|
-
const first = result[0];
|
|
732
|
-
if (Array.isArray(first)) return first;
|
|
733
|
-
return [];
|
|
734
|
-
}
|
|
735
|
-
return [];
|
|
736
|
-
}
|
|
737
|
-
function createInsertSchema(tableName) {
|
|
738
|
-
return {
|
|
739
|
-
"~standard": {
|
|
740
|
-
version: 1,
|
|
741
|
-
vendor: "tanstack-db-surrealdb",
|
|
742
|
-
validate: (value) => {
|
|
743
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
744
|
-
return {
|
|
745
|
-
issues: [{ message: "Insert data must be an object." }]
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
const data = normalizeRecordIdLikeFields({
|
|
749
|
-
...value
|
|
750
|
-
});
|
|
751
|
-
if (!data.id)
|
|
752
|
-
data.id = createTempRecordId(tableName);
|
|
753
|
-
return { value: data };
|
|
754
|
-
},
|
|
755
|
-
types: void 0
|
|
756
|
-
}
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
function defaultAad(ctx) {
|
|
760
|
-
if (ctx.kind === "base") return toBytes(`${ctx.table}:${ctx.id}`);
|
|
761
|
-
const base = ctx.baseTable ?? ctx.table;
|
|
762
|
-
return toBytes(`${ctx.table}:${base}:${ctx.id}`);
|
|
763
|
-
}
|
|
764
913
|
var syncModeFrom = (syncMode) => syncMode ?? "eager";
|
|
765
|
-
var subsetCacheKey = (subset) => {
|
|
766
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
767
|
-
return JSON.stringify(subset, (_key, value) => {
|
|
768
|
-
if (value instanceof Date) return value.toISOString();
|
|
769
|
-
if (value instanceof RecordId) return toRecordIdString(value);
|
|
770
|
-
if (typeof value === "bigint") return value.toString();
|
|
771
|
-
if (typeof value === "function")
|
|
772
|
-
return `[fn:${value.name || "anonymous"}]`;
|
|
773
|
-
if (value && typeof value === "object") {
|
|
774
|
-
const canonical = asCanonicalRecordIdString(value);
|
|
775
|
-
if (canonical) return canonical;
|
|
776
|
-
if (seen.has(value)) return "[Circular]";
|
|
777
|
-
seen.add(value);
|
|
778
|
-
}
|
|
779
|
-
return value;
|
|
780
|
-
}) ?? "";
|
|
781
|
-
};
|
|
782
|
-
var isPlainObject2 = (value) => typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
|
|
783
|
-
var normalizeSubsetValuesInPlace = (value, seen = /* @__PURE__ */ new WeakSet(), preferredByCanonical = /* @__PURE__ */ new Map()) => {
|
|
784
|
-
if (Array.isArray(value)) {
|
|
785
|
-
for (const entry of value) {
|
|
786
|
-
normalizeSubsetValuesInPlace(entry, seen, preferredByCanonical);
|
|
787
|
-
}
|
|
788
|
-
return preferredByCanonical;
|
|
789
|
-
}
|
|
790
|
-
if (!value || typeof value !== "object") return preferredByCanonical;
|
|
791
|
-
if (seen.has(value)) return preferredByCanonical;
|
|
792
|
-
seen.add(value);
|
|
793
|
-
const obj = value;
|
|
794
|
-
if (obj.type === "val" && "value" in obj) {
|
|
795
|
-
const canonical = asCanonicalRecordIdString(obj.value);
|
|
796
|
-
if (canonical) {
|
|
797
|
-
const preferred = preferRecordIdLikeIdentity(obj.value);
|
|
798
|
-
obj.value = preferred;
|
|
799
|
-
preferredByCanonical.set(canonical, preferred);
|
|
800
|
-
} else {
|
|
801
|
-
obj.value = preferRecordIdLikeIdentityDeep(obj.value);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
for (const child of Object.values(obj)) {
|
|
805
|
-
normalizeSubsetValuesInPlace(child, seen, preferredByCanonical);
|
|
806
|
-
}
|
|
807
|
-
return preferredByCanonical;
|
|
808
|
-
};
|
|
809
|
-
var primeRecordIdIdentityFromSubset = (subset) => {
|
|
810
|
-
if (!subset) return /* @__PURE__ */ new Map();
|
|
811
|
-
return normalizeSubsetValuesInPlace(subset);
|
|
812
|
-
};
|
|
813
|
-
var rebindRecordIdIdentityDeep = (value, preferredByCanonical) => {
|
|
814
|
-
const canonical = asCanonicalRecordIdString(value);
|
|
815
|
-
if (canonical && preferredByCanonical.has(canonical)) {
|
|
816
|
-
const preferred = preferredByCanonical.get(canonical);
|
|
817
|
-
return {
|
|
818
|
-
value: preferred,
|
|
819
|
-
changed: value !== preferred
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
if (Array.isArray(value)) {
|
|
823
|
-
let changed2 = false;
|
|
824
|
-
const out2 = value.map((entry) => {
|
|
825
|
-
const rebound = rebindRecordIdIdentityDeep(
|
|
826
|
-
entry,
|
|
827
|
-
preferredByCanonical
|
|
828
|
-
);
|
|
829
|
-
changed2 = changed2 || rebound.changed;
|
|
830
|
-
return rebound.value;
|
|
831
|
-
});
|
|
832
|
-
return changed2 ? { value: out2, changed: true } : { value, changed: false };
|
|
833
|
-
}
|
|
834
|
-
if (!isPlainObject2(value)) return { value, changed: false };
|
|
835
|
-
let changed = false;
|
|
836
|
-
const out = {};
|
|
837
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
838
|
-
const rebound = rebindRecordIdIdentityDeep(entry, preferredByCanonical);
|
|
839
|
-
if (rebound.changed) changed = true;
|
|
840
|
-
out[key] = rebound.value;
|
|
841
|
-
}
|
|
842
|
-
return changed ? { value: out, changed: true } : { value, changed: false };
|
|
843
|
-
};
|
|
844
|
-
var applyPreferredRecordIdIdentityToCollection = (ctx, preferredByCanonical) => {
|
|
845
|
-
if (!preferredByCanonical.size) return;
|
|
846
|
-
const collection = ctx.collection;
|
|
847
|
-
if (!collection || typeof collection.entries !== "function") return;
|
|
848
|
-
const rebound = [];
|
|
849
|
-
for (const [, row] of collection.entries()) {
|
|
850
|
-
const updated = rebindRecordIdIdentityDeep(row, preferredByCanonical);
|
|
851
|
-
if (!updated.changed) continue;
|
|
852
|
-
rebound.push(updated.value);
|
|
853
|
-
}
|
|
854
|
-
if (!rebound.length) return;
|
|
855
|
-
ctx.begin();
|
|
856
|
-
try {
|
|
857
|
-
for (const row of rebound) {
|
|
858
|
-
ctx.write({
|
|
859
|
-
type: "delete",
|
|
860
|
-
value: { id: row.id }
|
|
861
|
-
});
|
|
862
|
-
ctx.write({ type: "insert", value: row });
|
|
863
|
-
}
|
|
864
|
-
} finally {
|
|
865
|
-
ctx.commit();
|
|
866
|
-
}
|
|
867
|
-
const collectionWithInternals = collection;
|
|
868
|
-
const entries = collectionWithInternals._state?.entries?.();
|
|
869
|
-
const indexes = collectionWithInternals._indexes?.indexes;
|
|
870
|
-
if (entries && indexes) {
|
|
871
|
-
for (const index of indexes.values()) {
|
|
872
|
-
index.build?.(entries);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
};
|
|
876
914
|
function modernSurrealCollectionOptions(config) {
|
|
877
915
|
const {
|
|
878
916
|
db,
|
|
@@ -894,9 +932,9 @@ function modernSurrealCollectionOptions(config) {
|
|
|
894
932
|
const queryDrivenUsesSubsets = queryDrivenSyncMode === "on-demand";
|
|
895
933
|
const tableOptions = toTableOptions(table);
|
|
896
934
|
const tableName = tableOptions.name;
|
|
935
|
+
const collectionId = config.id ?? deriveCollectionId(tableName, queryKey);
|
|
897
936
|
const tableResource = toTableResource(table);
|
|
898
|
-
const
|
|
899
|
-
const activeOnDemandIds = /* @__PURE__ */ new Set();
|
|
937
|
+
const activeSubsets = new ActiveSubsetTracker();
|
|
900
938
|
const e2eeEnabled = e2ee?.enabled === true;
|
|
901
939
|
const crdtEnabled = crdt?.enabled === true;
|
|
902
940
|
const defaultCrdtProfile = crdtEnabled ? createLoroProfile(crdt.profile) : void 0;
|
|
@@ -949,7 +987,6 @@ function modernSurrealCollectionOptions(config) {
|
|
|
949
987
|
const updatesTableName = crdtEnabled ? tableNameOf(crdt.updatesTable) : void 0;
|
|
950
988
|
const updatesTable = crdtEnabled ? toTableResource(crdt.updatesTable) : void 0;
|
|
951
989
|
const snapshotsTableName = crdtEnabled && crdt.snapshotsTable ? tableNameOf(crdt.snapshotsTable) : void 0;
|
|
952
|
-
crdtEnabled && crdt.snapshotsTable ? toTableResource(crdt.snapshotsTable) : void 0;
|
|
953
990
|
const getDoc = (id) => {
|
|
954
991
|
const existing = docs.get(id);
|
|
955
992
|
if (existing) return existing;
|
|
@@ -1090,12 +1127,6 @@ function modernSurrealCollectionOptions(config) {
|
|
|
1090
1127
|
commit();
|
|
1091
1128
|
}
|
|
1092
1129
|
};
|
|
1093
|
-
const updateActiveOnDemandIds = () => {
|
|
1094
|
-
activeOnDemandIds.clear();
|
|
1095
|
-
for (const ids of subsetIds.values()) {
|
|
1096
|
-
for (const id of ids) activeOnDemandIds.add(id);
|
|
1097
|
-
}
|
|
1098
|
-
};
|
|
1099
1130
|
const createSyncRuntime = (ctx) => {
|
|
1100
1131
|
let cleanupBaseLive = NOOP;
|
|
1101
1132
|
let cleanupUpdateLive = NOOP;
|
|
@@ -1114,12 +1145,11 @@ function modernSurrealCollectionOptions(config) {
|
|
|
1114
1145
|
if (message.action === "KILLED") return;
|
|
1115
1146
|
const row = message.value;
|
|
1116
1147
|
const id = toRecordKeyString(row.id);
|
|
1117
|
-
const wasVisible =
|
|
1148
|
+
const wasVisible = activeSubsets.has(id);
|
|
1118
1149
|
if (isStrictOnDemand && !wasVisible && message.action !== "DELETE")
|
|
1119
1150
|
return;
|
|
1120
1151
|
if (message.action === "DELETE") {
|
|
1121
|
-
|
|
1122
|
-
updateActiveOnDemandIds();
|
|
1152
|
+
activeSubsets.deleteId(id);
|
|
1123
1153
|
if (isStrictOnDemand && !wasVisible) return;
|
|
1124
1154
|
ctx.begin();
|
|
1125
1155
|
try {
|
|
@@ -1165,7 +1195,7 @@ function modernSurrealCollectionOptions(config) {
|
|
|
1165
1195
|
const value = message.value;
|
|
1166
1196
|
const id = idFromDocRef(value.doc);
|
|
1167
1197
|
if (value.actor && value.actor === resolveActor(id)) return;
|
|
1168
|
-
if (isStrictOnDemand && !
|
|
1198
|
+
if (isStrictOnDemand && !activeSubsets.has(id)) return;
|
|
1169
1199
|
const doc = getDoc(id);
|
|
1170
1200
|
const bytes = await decodeUpdateBytes(value, "update");
|
|
1171
1201
|
if (!bytes.byteLength) return;
|
|
@@ -1196,8 +1226,7 @@ function modernSurrealCollectionOptions(config) {
|
|
|
1196
1226
|
(row) => toRecordKeyString(row.id)
|
|
1197
1227
|
)
|
|
1198
1228
|
);
|
|
1199
|
-
|
|
1200
|
-
updateActiveOnDemandIds();
|
|
1229
|
+
activeSubsets.set(key, ids);
|
|
1201
1230
|
if (!crdtEnabled) {
|
|
1202
1231
|
await hydratePlainRows(
|
|
1203
1232
|
rows,
|
|
@@ -1223,9 +1252,8 @@ function modernSurrealCollectionOptions(config) {
|
|
|
1223
1252
|
const unloadSubset = (subset) => {
|
|
1224
1253
|
primeRecordIdIdentityFromSubset(subset);
|
|
1225
1254
|
const key = subsetCacheKey(subset);
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
if (subsetIds.size === 0) {
|
|
1255
|
+
activeSubsets.delete(key);
|
|
1256
|
+
if (activeSubsets.size === 0) {
|
|
1229
1257
|
cleanupBaseLive();
|
|
1230
1258
|
cleanupUpdateLive();
|
|
1231
1259
|
}
|
|
@@ -1239,8 +1267,7 @@ function modernSurrealCollectionOptions(config) {
|
|
|
1239
1267
|
};
|
|
1240
1268
|
const cleanup = () => {
|
|
1241
1269
|
killed = true;
|
|
1242
|
-
|
|
1243
|
-
updateActiveOnDemandIds();
|
|
1270
|
+
activeSubsets.clear();
|
|
1244
1271
|
cleanupBaseLive();
|
|
1245
1272
|
cleanupUpdateLive();
|
|
1246
1273
|
};
|
|
@@ -1252,6 +1279,7 @@ function modernSurrealCollectionOptions(config) {
|
|
|
1252
1279
|
};
|
|
1253
1280
|
};
|
|
1254
1281
|
const base = queryCollectionOptions({
|
|
1282
|
+
id: collectionId,
|
|
1255
1283
|
schema: createInsertSchema(tableName),
|
|
1256
1284
|
getKey,
|
|
1257
1285
|
queryKey,
|
|
@@ -1312,7 +1340,7 @@ function modernSurrealCollectionOptions(config) {
|
|
|
1312
1340
|
const recordPayload = await encodeBaseRow(payload, idKey);
|
|
1313
1341
|
if (isTempId(normalized.id, tableName)) {
|
|
1314
1342
|
const created = await db.create(tableResource).content(recordPayload);
|
|
1315
|
-
const createdRow =
|
|
1343
|
+
const createdRow = firstRow(
|
|
1316
1344
|
toRecordArray(created)
|
|
1317
1345
|
);
|
|
1318
1346
|
const createdId = createdRow && createdRow.id ? createdRow.id : normalized.id;
|
|
@@ -1524,7 +1552,15 @@ function modernSurrealCollectionOptions(config) {
|
|
|
1524
1552
|
function surrealCollectionOptions(config) {
|
|
1525
1553
|
return modernSurrealCollectionOptions(config);
|
|
1526
1554
|
}
|
|
1555
|
+
function persistedSurrealCollectionOptions(config) {
|
|
1556
|
+
const { persistence, schemaVersion, ...surrealConfig } = config;
|
|
1557
|
+
return persistedCollectionOptions({
|
|
1558
|
+
persistence,
|
|
1559
|
+
schemaVersion,
|
|
1560
|
+
...modernSurrealCollectionOptions(surrealConfig)
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1527
1563
|
|
|
1528
|
-
export { WebCryptoAESGCM, applyLoroJsonChange, applyLoroRichtextChange, createLoroProfile, materializeLoroJson, materializeLoroRichtext, surrealCollectionOptions, toRecordKeyString };
|
|
1564
|
+
export { WebCryptoAESGCM, applyLoroJsonChange, applyLoroRichtextChange, createLoroProfile, materializeLoroJson, materializeLoroRichtext, persistedSurrealCollectionOptions, surrealCollectionOptions, toRecordKeyString };
|
|
1529
1565
|
//# sourceMappingURL=index.mjs.map
|
|
1530
1566
|
//# sourceMappingURL=index.mjs.map
|