@mocanvas/store 1.0.0 → 4.0.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.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { nanoid } from 'nanoid';
2
2
  import { generateKeyBetween, generateNKeysBetween } from 'fractional-indexing';
3
- import { computed, unsafe__withoutCapture, atom, transact } from '@mocanvas/state';
3
+ import { computed, unsafe__withoutCapture, isUninitialized, RESET_VALUE, withDiff, atom, transact } from '@mocanvas/state';
4
+ export { AtomMap, AtomSet } from '@mocanvas/state';
4
5
 
5
6
  // src/ids.ts
6
7
  var UNIQUE_ID_LENGTH = 21;
@@ -368,6 +369,25 @@ function applyMigrationToStore(migration, store, direction) {
368
369
  return store;
369
370
  }
370
371
 
372
+ // src/legacy.ts
373
+ function isSerializedSchemaV1(schema) {
374
+ return schema.schemaVersion === 1;
375
+ }
376
+ var MigrationFailureReason = {
377
+ /** The persisted schema names a sequence version higher than this schema knows. */
378
+ TargetVersionTooNew: "target-version-too-new",
379
+ /** The persisted data is older than the oldest migration that survives. */
380
+ TargetVersionTooOld: "target-version-too-old",
381
+ /** A record's type is not registered in this schema and cannot be migrated. */
382
+ UnrecognizedType: "unrecognized-type",
383
+ /** A migration function threw. */
384
+ MigrationError: "migration-error",
385
+ /** The persisted schema itself is malformed. */
386
+ IncompatibleSubtype: "incompatible-subtype",
387
+ /** The persisted schema version is not one this store understands. */
388
+ UnknownSchemaVersion: "unknown-schema-version"
389
+ };
390
+
371
391
  // src/StoreSchema.ts
372
392
  var StoreSchema = class _StoreSchema {
373
393
  constructor(types, options) {
@@ -437,7 +457,7 @@ var StoreSchema = class _StoreSchema {
437
457
  throw error;
438
458
  }
439
459
  }
440
- /** The current version of every sequence. */
460
+ /** The current version of every sequence. Always the v2 shape — mocanvas never writes v1. */
441
461
  serialize() {
442
462
  const sequences = {};
443
463
  for (const sequence of Object.values(this.migrations)) {
@@ -457,13 +477,21 @@ var StoreSchema = class _StoreSchema {
457
477
  * do not are ignored with a warning.
458
478
  */
459
479
  getMigrationsSince(persistedSchema) {
480
+ if (isSerializedSchemaV1(persistedSchema)) {
481
+ return {
482
+ type: "error",
483
+ reason: "Schema version 1 (per-record-type versions) predates migration sequences and cannot be migrated automatically"
484
+ };
485
+ }
460
486
  if (persistedSchema.schemaVersion !== 2) {
461
487
  return {
462
488
  type: "error",
463
489
  reason: `Unsupported schema version ${String(persistedSchema.schemaVersion)}`
464
490
  };
465
491
  }
466
- const persisted = persistedSchema.sequences ?? {};
492
+ return this.migrationsSince(persistedSchema.sequences ?? {});
493
+ }
494
+ migrationsSince(persisted) {
467
495
  for (const sequenceId of Object.keys(persisted)) {
468
496
  if (!this.migrations[sequenceId]) {
469
497
  console.warn(`[store] ignoring unknown migration sequence "${sequenceId}" in persisted schema`);
@@ -543,6 +571,36 @@ var StoreSchema = class _StoreSchema {
543
571
  }
544
572
  };
545
573
 
574
+ // src/query.ts
575
+ function matchesQueryValue(matcher, value) {
576
+ if ("eq" in matcher) return Object.is(matcher.eq, value);
577
+ if ("neq" in matcher) return !Object.is(matcher.neq, value);
578
+ return typeof value === "number" && value > matcher.gt;
579
+ }
580
+ function matchesQuery(query, record) {
581
+ for (const key of Object.keys(query)) {
582
+ const matcher = query[key];
583
+ if (matcher === void 0) continue;
584
+ if (!matchesQueryValue(matcher, record[key])) return false;
585
+ }
586
+ return true;
587
+ }
588
+ function getIndexablePropertyOf(query) {
589
+ for (const key of Object.keys(query)) {
590
+ const matcher = query[key];
591
+ if (matcher && "eq" in matcher) return key;
592
+ }
593
+ return void 0;
594
+ }
595
+ function applyCollectionDiff(set, diff) {
596
+ if (diff.removed) for (const value of diff.removed) set.delete(value);
597
+ if (diff.added) for (const value of diff.added) set.add(value);
598
+ return set;
599
+ }
600
+ function isCollectionDiffEmpty(diff) {
601
+ return (diff.added?.size ?? 0) === 0 && (diff.removed?.size ?? 0) === 0;
602
+ }
603
+
546
604
  // src/Store.ts
547
605
  var StoreSideEffects = class {
548
606
  byType = /* @__PURE__ */ new Map();
@@ -703,6 +761,9 @@ function freezeRecord(record) {
703
761
  if (isPlainObject(r["meta"]) && !Object.isFrozen(r["meta"])) Object.freeze(r["meta"]);
704
762
  return Object.freeze(record);
705
763
  }
764
+ function toPredicate(filter) {
765
+ return typeof filter === "function" ? filter : (record) => matchesQuery(filter, record);
766
+ }
706
767
  var StoreQueries = class {
707
768
  constructor(store) {
708
769
  this.store = store;
@@ -710,8 +771,21 @@ var StoreQueries = class {
710
771
  store;
711
772
  idsCache = /* @__PURE__ */ new Map();
712
773
  recordsCache = /* @__PURE__ */ new Map();
713
- /** The set of ids of every record of `typeName`. Maintained incrementally. */
714
- ids(typeName) {
774
+ indexCache = /* @__PURE__ */ new Map();
775
+ historyCache = /* @__PURE__ */ new Map();
776
+ /**
777
+ * The set of ids of every record of `typeName`, optionally narrowed by a
778
+ * filter. Maintained incrementally.
779
+ */
780
+ ids(typeName, filter) {
781
+ if (filter) {
782
+ const records = this.records(typeName, filter);
783
+ return computed(`store:${this.store.id}:ids:${typeName}:filtered`, () => {
784
+ const set = /* @__PURE__ */ new Set();
785
+ for (const record of records.get()) set.add(record.id);
786
+ return set;
787
+ });
788
+ }
715
789
  let c = this.idsCache.get(typeName);
716
790
  if (!c) {
717
791
  const index = this.store.getTypeIndex(typeName);
@@ -723,8 +797,16 @@ var StoreQueries = class {
723
797
  }
724
798
  return c;
725
799
  }
726
- /** Every record of `typeName`, in insertion order. */
727
- records(typeName) {
800
+ /**
801
+ * Every record of `typeName`, in insertion order, optionally narrowed by a
802
+ * filter.
803
+ *
804
+ * A {@link QueryExpression} with an `eq` clause is answered from the index on
805
+ * that property, so a page with ten thousand shapes does not have to be
806
+ * walked to find the twelve on one frame.
807
+ */
808
+ records(typeName, filter) {
809
+ if (filter) return this.filteredRecords(typeName, filter);
728
810
  let c = this.recordsCache.get(typeName);
729
811
  if (!c) {
730
812
  const ids = this.ids(typeName);
@@ -740,19 +822,166 @@ var StoreQueries = class {
740
822
  }
741
823
  return c;
742
824
  }
743
- /** The first record of `typeName` matching `predicate` (or the first record, when omitted). */
744
- record(typeName, predicate) {
745
- const records = this.records(typeName);
746
- return computed(`store:${this.store.id}:record:${typeName}`, () => {
747
- const all = records.get();
748
- if (!predicate) return all[0];
749
- for (const record of all) if (predicate(record)) return record;
750
- return void 0;
751
- });
825
+ filteredRecords(typeName, filter) {
826
+ const predicate = toPredicate(filter);
827
+ const indexedProperty = typeof filter === "function" ? void 0 : getIndexablePropertyOf(filter);
828
+ if (indexedProperty !== void 0) {
829
+ const clause = filter[indexedProperty];
830
+ const wanted = clause && "eq" in clause ? clause.eq : void 0;
831
+ const index = this.index(typeName, indexedProperty);
832
+ return computed(`store:${this.store.id}:records:${typeName}:${String(indexedProperty)}`, () => {
833
+ const bucket = index.get().get(wanted);
834
+ if (!bucket) return [];
835
+ const result = [];
836
+ for (const id of bucket) {
837
+ const record = this.store.get(id);
838
+ if (record !== void 0 && predicate(record)) result.push(record);
839
+ }
840
+ return result;
841
+ });
842
+ }
843
+ const all = this.records(typeName);
844
+ return computed(`store:${this.store.id}:records:${typeName}:filtered`, () => all.get().filter(predicate));
845
+ }
846
+ /** The first record of `typeName` matching `filter` (or the first record, when omitted). */
847
+ record(typeName, filter) {
848
+ const records = filter ? this.filteredRecords(typeName, filter) : this.records(typeName);
849
+ return computed(`store:${this.store.id}:record:${typeName}`, () => records.get()[0]);
752
850
  }
753
851
  /** Non-reactive filter over the records of `typeName`. */
754
- exec(typeName, predicate) {
755
- return unsafe__withoutCapture(() => this.records(typeName).get().filter(predicate));
852
+ exec(typeName, filter) {
853
+ return unsafe__withoutCapture(() => this.filteredRecords(typeName, filter).get());
854
+ }
855
+ /**
856
+ * A live index from the values of one property to the ids of the records
857
+ * holding them.
858
+ *
859
+ * The index is cached per type and property, and it carries diffs: a
860
+ * dependent that already built something from it can ask
861
+ * `index.getDiffSince(epoch)` and patch, instead of walking the whole map
862
+ * again. That is what makes "every shape whose parentId is this frame" cheap
863
+ * enough to recompute on every pointer move.
864
+ */
865
+ index(typeName, property) {
866
+ const key = `${typeName}:${property}`;
867
+ const cached = this.indexCache.get(key);
868
+ if (cached) return cached;
869
+ const history = this.filterHistory(typeName);
870
+ const index = computed(
871
+ `store:${this.store.id}:index:${key}`,
872
+ (previous, lastComputedEpoch) => {
873
+ if (isUninitialized(previous)) {
874
+ history.get();
875
+ return this.buildIndex(typeName, property);
876
+ }
877
+ const diffs = history.getDiffSince(lastComputedEpoch);
878
+ if (diffs === RESET_VALUE) return this.buildIndex(typeName, property);
879
+ const nextMap = new Map(previous);
880
+ const indexDiff = /* @__PURE__ */ new Map();
881
+ let changed = false;
882
+ const remove = (value, id) => {
883
+ const bucket = nextMap.get(value);
884
+ if (!bucket?.has(id)) return;
885
+ const next = new Set(bucket);
886
+ next.delete(id);
887
+ if (next.size === 0) nextMap.delete(value);
888
+ else nextMap.set(value, next);
889
+ const entry = indexDiff.get(value) ?? {};
890
+ (entry.removed ??= /* @__PURE__ */ new Set()).add(id);
891
+ indexDiff.set(value, entry);
892
+ changed = true;
893
+ };
894
+ const add = (value, id) => {
895
+ const bucket = nextMap.get(value);
896
+ if (bucket?.has(id)) return;
897
+ nextMap.set(value, new Set(bucket).add(id));
898
+ const entry = indexDiff.get(value) ?? {};
899
+ (entry.added ??= /* @__PURE__ */ new Set()).add(id);
900
+ indexDiff.set(value, entry);
901
+ changed = true;
902
+ };
903
+ for (const diff of diffs) {
904
+ for (const id in diff.added) {
905
+ const record = diff.added[id];
906
+ if (record?.typeName === typeName) add(record[property], record.id);
907
+ }
908
+ for (const id in diff.updated) {
909
+ const [before, after] = diff.updated[id];
910
+ if (after.typeName !== typeName) continue;
911
+ if (Object.is(before[property], after[property])) continue;
912
+ remove(before[property], before.id);
913
+ add(after[property], after.id);
914
+ }
915
+ for (const id in diff.removed) {
916
+ const record = diff.removed[id];
917
+ if (record?.typeName === typeName) remove(record[property], record.id);
918
+ }
919
+ }
920
+ if (!changed) return previous;
921
+ return withDiff(nextMap, indexDiff);
922
+ },
923
+ { historyLength: 128 }
924
+ );
925
+ this.indexCache.set(key, index);
926
+ return index;
927
+ }
928
+ buildIndex(typeName, property) {
929
+ const map = /* @__PURE__ */ new Map();
930
+ for (const record of this.records(typeName).get()) {
931
+ const value = record[property];
932
+ const bucket = map.get(value);
933
+ if (bucket) bucket.add(record.id);
934
+ else map.set(value, /* @__PURE__ */ new Set([record.id]));
935
+ }
936
+ return map;
937
+ }
938
+ /**
939
+ * The store's history, narrowed to one record type.
940
+ *
941
+ * Its *value* is only a counter — what it is for is the diffs it carries.
942
+ * `filterHistory("shape").getDiffSince(epoch)` is every change to shapes
943
+ * since `epoch`, with changes to other record types dropped, which is how a
944
+ * derived collection stays incremental without re-reading the store.
945
+ */
946
+ filterHistory(typeName) {
947
+ const cached = this.historyCache.get(typeName);
948
+ if (cached) return cached;
949
+ const filtered = computed(
950
+ `store:${this.store.id}:history:${typeName}`,
951
+ (previous, lastComputedEpoch) => {
952
+ const epoch = this.store.history.get();
953
+ if (isUninitialized(previous)) return epoch;
954
+ const diffs = this.store.history.getDiffSince(lastComputedEpoch);
955
+ if (diffs === RESET_VALUE) return epoch;
956
+ const merged = createEmptyRecordsDiff();
957
+ let any = false;
958
+ for (const diff of diffs) {
959
+ for (const id in diff.added) {
960
+ const record = diff.added[id];
961
+ if (record.typeName !== typeName) continue;
962
+ merged.added[id] = record;
963
+ any = true;
964
+ }
965
+ for (const id in diff.updated) {
966
+ const pair = diff.updated[id];
967
+ if (pair[1].typeName !== typeName) continue;
968
+ merged.updated[id] = pair;
969
+ any = true;
970
+ }
971
+ for (const id in diff.removed) {
972
+ const record = diff.removed[id];
973
+ if (record.typeName !== typeName) continue;
974
+ merged.removed[id] = record;
975
+ any = true;
976
+ }
977
+ }
978
+ if (!any) return previous;
979
+ return withDiff(epoch, merged);
980
+ },
981
+ { historyLength: 128 }
982
+ );
983
+ this.historyCache.set(typeName, filtered);
984
+ return filtered;
756
985
  }
757
986
  };
758
987
  var Store = class {
@@ -762,7 +991,15 @@ var Store = class {
762
991
  scopedTypes;
763
992
  sideEffects = new StoreSideEffects();
764
993
  query;
765
- /** Bumped once per completed operation that changed something. */
994
+ /**
995
+ * Bumped once per completed operation that changed something.
996
+ *
997
+ * The counter itself carries no information; the diffs do. The atom keeps a
998
+ * bounded history of the squashed {@link RecordsDiff} of each operation, so a
999
+ * derived collection can ask `history.getDiffSince(epoch)` and patch itself
1000
+ * instead of rebuilding. `store.query.filterHistory(typeName)` is the same
1001
+ * thing narrowed to one record type.
1002
+ */
766
1003
  history;
767
1004
  records = /* @__PURE__ */ new Map();
768
1005
  typeIndexes = /* @__PURE__ */ new Map();
@@ -774,11 +1011,22 @@ var Store = class {
774
1011
  runCallbacks = true;
775
1012
  inOperationComplete = false;
776
1013
  disposed = false;
1014
+ /**
1015
+ * The diff of the operation currently being committed, handed to the history
1016
+ * atom's `computeDiff` as it is written. The atom only sees two counter
1017
+ * values, so the diff has to be staged here for the one write that follows.
1018
+ */
1019
+ pendingHistoryDiff = null;
777
1020
  constructor(options) {
778
1021
  this.id = options.id ?? uniqueId();
779
1022
  this.schema = options.schema;
780
1023
  this.props = options.props;
781
- this.history = atom(`store:${this.id}:history`, 0);
1024
+ this.history = atom(`store:${this.id}:history`, 0, {
1025
+ // 128 operations is deep enough that a dependent which rendered a frame
1026
+ // ago can still patch, and shallow enough that the buffer costs nothing.
1027
+ historyLength: 128,
1028
+ computeDiff: () => this.pendingHistoryDiff ?? RESET_VALUE
1029
+ });
782
1030
  this.query = new StoreQueries(this);
783
1031
  const scoped = { document: /* @__PURE__ */ new Set(), session: /* @__PURE__ */ new Set(), presence: /* @__PURE__ */ new Set() };
784
1032
  for (const type of Object.values(this.schema.types)) {
@@ -1037,6 +1285,28 @@ var Store = class {
1037
1285
  getStoreSnapshot(scope = "document") {
1038
1286
  return { store: this.serialize(scope), schema: this.schema.serialize() };
1039
1287
  }
1288
+ /**
1289
+ * Bring a snapshot saved by an older document up to this store's schema,
1290
+ * without loading it.
1291
+ *
1292
+ * Every migration sequence the schema knows is run — including the ones
1293
+ * `createStore` derives from the shape and binding utils, so a board saved
1294
+ * before a prop existed is backfilled here rather than failing validation on
1295
+ * load. The input is not mutated: the result is a new snapshot carrying this
1296
+ * schema's serialized version, ready for {@link Store.loadStoreSnapshot} (or
1297
+ * for a caller that wants to inspect the migrated records first).
1298
+ *
1299
+ * A snapshot that cannot be migrated — an unknown schema version, a sequence
1300
+ * from a NEWER build than this one, a migration that throws — raises rather
1301
+ * than returning half-migrated data, so a caller can fail closed on it.
1302
+ */
1303
+ migrateSnapshot(snapshot) {
1304
+ const migrated = this.schema.migrateStoreSnapshot(snapshot);
1305
+ if (migrated.type === "error") {
1306
+ throw new Error(`Failed to migrate snapshot: ${migrated.reason}`);
1307
+ }
1308
+ return { store: migrated.value, schema: this.schema.serialize() };
1309
+ }
1040
1310
  /**
1041
1311
  * Replace the store's contents with a snapshot (migrating it first).
1042
1312
  * Existing records in `document` scope and in every scope present in the
@@ -1146,9 +1416,20 @@ var Store = class {
1146
1416
  this.inOperationComplete = false;
1147
1417
  }
1148
1418
  }
1149
- this.history.update((n) => n + 1);
1419
+ this.pendingHistoryDiff = this.squashPendingEntries();
1420
+ try {
1421
+ this.history.update((n) => n + 1);
1422
+ } finally {
1423
+ this.pendingHistoryDiff = null;
1424
+ }
1150
1425
  this.flushHistory();
1151
1426
  }
1427
+ /** One diff describing everything the operation just committed changed. */
1428
+ squashPendingEntries() {
1429
+ const diffs = this.pendingEntries.map((entry) => entry.changes).filter((diff) => !isRecordsDiffEmpty(diff));
1430
+ if (diffs.length === 1) return diffs[0];
1431
+ return squashRecordDiffs(diffs);
1432
+ }
1152
1433
  flushHistory() {
1153
1434
  const entries = this.pendingEntries;
1154
1435
  this.pendingEntries = [];
@@ -1256,6 +1537,119 @@ function storeSnapshotToTldrFile(snapshot) {
1256
1537
  return serializeTldrFile(snapshot.schema, records);
1257
1538
  }
1258
1539
 
1259
- export { RecordType, Store, StoreQueries, StoreSchema, StoreSideEffects, TLDR_FILE_FORMAT_VERSION, UNIQUE_ID_LENGTH, ZERO_INDEX_KEY, ZKEY_SIGNIFICANT_DIGITS, applyChangeToDiff, applyMigrationToStore, applyRecordMigration, cloneRecordsDiff, compareIndexKeys, compareZKeys, createEmptyRecordsDiff, createMigrationIds, createMigrationSequence, createRecordMigrationSequence, createRecordType, freezeRecord, getIndexAbove, getIndexBelow, getIndexBetween, getIndices, getIndicesAbove, getIndicesBelow, getIndicesBetween, indexKeyToZKey, isIndexKey, isRecordLike, isRecordShallowEqual, isRecordsDiffEmpty, parseMigrationId, parseRecordId, parseTldrFile, reverseRecordsDiff, serializeTldrFile, sortByIndex, squashRecordDiffs, squashRecordDiffsMutable, storeSnapshotToTldrFile, tldrFileToStoreSnapshot, uniqueId, validateIndexKey, zKeyToBigInt };
1540
+ // src/computedCache.ts
1541
+ function storeOf(context) {
1542
+ if (context instanceof Store) return context;
1543
+ const store = context?.store;
1544
+ if (store instanceof Store) return store;
1545
+ throw new Error("createComputedCache: context is neither a Store nor an object holding one");
1546
+ }
1547
+ function createComputedCache(name, derive, options) {
1548
+ const perContext = /* @__PURE__ */ new WeakMap();
1549
+ return {
1550
+ get(context, id) {
1551
+ const key = context;
1552
+ if (key === null || typeof key !== "object" && typeof key !== "function") {
1553
+ throw new Error("createComputedCache: context must be an object");
1554
+ }
1555
+ let cache = perContext.get(key);
1556
+ if (!cache) {
1557
+ const areRecordsEqual = options?.areRecordsEqual;
1558
+ const previous = /* @__PURE__ */ new Map();
1559
+ const derivation = areRecordsEqual ? (record) => {
1560
+ const next = record;
1561
+ const last = previous.get(record.id);
1562
+ if (last && areRecordsEqual(last.record, next)) return last.result;
1563
+ const result = derive(context, next);
1564
+ previous.set(record.id, { record: next, result });
1565
+ return result;
1566
+ } : (record) => derive(context, record);
1567
+ cache = storeOf(context).createComputedCache(
1568
+ name,
1569
+ derivation,
1570
+ options?.isEqual ? { isEqual: options.isEqual } : void 0
1571
+ );
1572
+ perContext.set(key, cache);
1573
+ }
1574
+ return cache.get(id);
1575
+ }
1576
+ };
1577
+ }
1578
+
1579
+ // src/devFreeze.ts
1580
+ var IS_DEV = typeof process !== "undefined" && typeof process.env === "object" && process.env["NODE_ENV"] !== "production";
1581
+ function devFreeze(object) {
1582
+ if (!IS_DEV) return object;
1583
+ return deepFreeze(object);
1584
+ }
1585
+ function deepFreeze(object) {
1586
+ if (object === null || typeof object !== "object") return object;
1587
+ if (Object.isFrozen(object)) return object;
1588
+ Object.freeze(object);
1589
+ for (const value of Object.values(object)) deepFreeze(value);
1590
+ if (Array.isArray(object)) for (const value of object) deepFreeze(value);
1591
+ return object;
1592
+ }
1593
+ function assertIdType(id, type) {
1594
+ if (!type.isId(id)) {
1595
+ throw new Error(`Expected ${type.typeName} id, got ${JSON.stringify(id)}`);
1596
+ }
1597
+ }
1598
+
1599
+ // src/storage.ts
1600
+ function createInMemoryStorage() {
1601
+ const records = /* @__PURE__ */ new Map();
1602
+ let schema;
1603
+ return {
1604
+ get: (id) => records.get(id),
1605
+ getAll: () => [...records.values()],
1606
+ set: (record) => {
1607
+ records.set(record.id, record);
1608
+ },
1609
+ delete: (id) => {
1610
+ records.delete(id);
1611
+ },
1612
+ clear: () => records.clear(),
1613
+ getSchema: () => schema,
1614
+ setSchema: (next) => {
1615
+ schema = next;
1616
+ }
1617
+ };
1618
+ }
1619
+
1620
+ // src/graphemes.ts
1621
+ var segmenter;
1622
+ var segmenterChecked = false;
1623
+ function getSegmenter() {
1624
+ if (!segmenterChecked) {
1625
+ segmenterChecked = true;
1626
+ try {
1627
+ if (typeof Intl !== "undefined" && typeof Intl.Segmenter === "function") {
1628
+ segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
1629
+ }
1630
+ } catch {
1631
+ segmenter = void 0;
1632
+ }
1633
+ }
1634
+ return segmenter;
1635
+ }
1636
+ function* iterateGraphemes(str) {
1637
+ const seg = getSegmenter();
1638
+ if (seg) {
1639
+ for (const { segment } of seg.segment(str)) yield segment;
1640
+ return;
1641
+ }
1642
+ for (const codePoint of str) yield codePoint;
1643
+ }
1644
+ function getGraphemes(str) {
1645
+ return [...iterateGraphemes(str)];
1646
+ }
1647
+ function getGraphemeLength(str) {
1648
+ let n = 0;
1649
+ for (const _ of iterateGraphemes(str)) n++;
1650
+ return n;
1651
+ }
1652
+
1653
+ export { MigrationFailureReason, RecordType, Store, StoreQueries, StoreSchema, StoreSideEffects, TLDR_FILE_FORMAT_VERSION, UNIQUE_ID_LENGTH, ZERO_INDEX_KEY, ZKEY_SIGNIFICANT_DIGITS, applyChangeToDiff, applyCollectionDiff, applyMigrationToStore, applyRecordMigration, assertIdType, cloneRecordsDiff, compareIndexKeys, compareZKeys, createComputedCache, createEmptyRecordsDiff, createInMemoryStorage, createMigrationIds, createMigrationSequence, createRecordMigrationSequence, createRecordType, devFreeze, freezeRecord, getGraphemeLength, getGraphemes, getIndexAbove, getIndexBelow, getIndexBetween, getIndexablePropertyOf, getIndices, getIndicesAbove, getIndicesBelow, getIndicesBetween, indexKeyToZKey, isCollectionDiffEmpty, isIndexKey, isRecordLike, isRecordShallowEqual, isRecordsDiffEmpty, isSerializedSchemaV1, iterateGraphemes, matchesQuery, matchesQueryValue, parseMigrationId, parseRecordId, parseTldrFile, reverseRecordsDiff, serializeTldrFile, sortByIndex, squashRecordDiffs, squashRecordDiffsMutable, storeSnapshotToTldrFile, tldrFileToStoreSnapshot, uniqueId, validateIndexKey, zKeyToBigInt };
1260
1654
  //# sourceMappingURL=index.js.map
1261
1655
  //# sourceMappingURL=index.js.map