@foretag/tanstack-db-surrealdb 0.6.11 → 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/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 IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
292
- var firstRow = (res) => {
293
- if (!res) return void 0;
294
- if (Array.isArray(res)) return res[0];
295
- return res;
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(`No key configured for kid '${incomingKid}'.`);
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 subsetIds = /* @__PURE__ */ new Map();
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 = activeOnDemandIds.has(id);
1148
+ const wasVisible = activeSubsets.has(id);
1118
1149
  if (isStrictOnDemand && !wasVisible && message.action !== "DELETE")
1119
1150
  return;
1120
1151
  if (message.action === "DELETE") {
1121
- for (const ids of subsetIds.values()) ids.delete(id);
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 && !activeOnDemandIds.has(id)) return;
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
- subsetIds.set(key, ids);
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
- subsetIds.delete(key);
1227
- updateActiveOnDemandIds();
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
- subsetIds.clear();
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 = firstRow2(
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