atom.io 0.39.1 → 0.40.1

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.
Files changed (88) hide show
  1. package/dist/internal/index.d.ts +68 -63
  2. package/dist/internal/index.d.ts.map +1 -1
  3. package/dist/internal/index.js +171 -161
  4. package/dist/internal/index.js.map +1 -1
  5. package/dist/introspection/index.d.ts +2 -2
  6. package/dist/introspection/index.d.ts.map +1 -1
  7. package/dist/introspection/index.js.map +1 -1
  8. package/dist/main/index.d.ts +19 -15
  9. package/dist/main/index.d.ts.map +1 -1
  10. package/dist/main/index.js +16 -13
  11. package/dist/main/index.js.map +1 -1
  12. package/dist/react/index.d.ts +3 -3
  13. package/dist/react/index.d.ts.map +1 -1
  14. package/dist/react/index.js.map +1 -1
  15. package/dist/react-devtools/index.js +9 -6
  16. package/dist/react-devtools/index.js.map +1 -1
  17. package/dist/realtime-client/index.d.ts +3 -3
  18. package/dist/realtime-client/index.d.ts.map +1 -1
  19. package/dist/realtime-client/index.js +3 -4
  20. package/dist/realtime-client/index.js.map +1 -1
  21. package/dist/realtime-server/index.d.ts +2 -2
  22. package/dist/realtime-server/index.d.ts.map +1 -1
  23. package/package.json +2 -2
  24. package/src/internal/atom/create-regular-atom.ts +3 -2
  25. package/src/internal/atom/dispose-atom.ts +6 -6
  26. package/src/internal/events/ingest-selector-update.ts +13 -4
  27. package/src/internal/families/create-readonly-held-selector-family.ts +3 -4
  28. package/src/internal/families/create-readonly-pure-selector-family.ts +4 -9
  29. package/src/internal/families/create-regular-atom-family.ts +3 -4
  30. package/src/internal/families/create-selector-family.ts +6 -6
  31. package/src/internal/families/create-writable-held-selector-family.ts +2 -2
  32. package/src/internal/families/create-writable-pure-selector-family.ts +3 -7
  33. package/src/internal/families/dispose-from-store.ts +9 -2
  34. package/src/internal/get-state/get-from-store.ts +10 -3
  35. package/src/internal/get-state/reduce-reference.ts +15 -2
  36. package/src/internal/index.ts +8 -8
  37. package/src/internal/install-into-store.ts +2 -1
  38. package/src/internal/join/create-join.ts +2 -2
  39. package/src/internal/join/find-relations-in-store.ts +2 -2
  40. package/src/internal/join/get-internal-relations-from-store.ts +2 -2
  41. package/src/internal/join/get-join.ts +5 -2
  42. package/src/internal/join/join-internal.ts +15 -20
  43. package/src/internal/lineage.ts +12 -1
  44. package/src/internal/molecule.ts +64 -36
  45. package/src/internal/mutable/create-mutable-atom-family.ts +4 -4
  46. package/src/internal/mutable/create-mutable-atom.ts +2 -2
  47. package/src/internal/mutable/tracker-family.ts +3 -3
  48. package/src/internal/operation.ts +3 -1
  49. package/src/internal/selector/create-readonly-held-selector.ts +2 -2
  50. package/src/internal/selector/create-readonly-pure-selector.ts +2 -2
  51. package/src/internal/selector/create-writable-held-selector.ts +2 -2
  52. package/src/internal/selector/create-writable-pure-selector.ts +2 -2
  53. package/src/internal/set-state/become.ts +1 -3
  54. package/src/internal/set-state/dispatch-state-update.ts +13 -11
  55. package/src/internal/set-state/reset-in-store.ts +11 -13
  56. package/src/internal/set-state/set-into-store.ts +27 -3
  57. package/src/internal/store/store.ts +14 -12
  58. package/src/internal/timeline/create-timeline.ts +136 -100
  59. package/src/internal/timeline/time-travel.ts +43 -31
  60. package/src/internal/transaction/abort-transaction.ts +3 -15
  61. package/src/internal/transaction/act-upon-store.ts +1 -5
  62. package/src/internal/transaction/apply-transaction.ts +5 -17
  63. package/src/internal/transaction/assign-transaction-to-continuity.ts +2 -7
  64. package/src/internal/transaction/build-transaction.ts +3 -4
  65. package/src/internal/transaction/create-transaction.ts +5 -6
  66. package/src/internal/transaction/get-epoch-number.ts +1 -7
  67. package/src/internal/transaction/set-epoch-number.ts +4 -12
  68. package/src/introspection/attach-introspection-states.ts +4 -2
  69. package/src/introspection/attach-timeline-family.ts +2 -2
  70. package/src/introspection/attach-transaction-logs.ts +2 -2
  71. package/src/introspection/attach-type-selectors.ts +2 -2
  72. package/src/main/dispose-state.ts +1 -5
  73. package/src/main/events.ts +8 -4
  74. package/src/main/get-state.ts +3 -6
  75. package/src/main/logger.ts +1 -1
  76. package/src/main/realm.ts +36 -12
  77. package/src/main/reset-state.ts +1 -5
  78. package/src/main/set-state.ts +4 -11
  79. package/src/main/silo.ts +4 -3
  80. package/src/main/timeline.ts +1 -7
  81. package/src/react/store-context.tsx +3 -3
  82. package/src/react-devtools/Button.tsx +3 -2
  83. package/src/react-devtools/TimelineIndex.tsx +0 -2
  84. package/src/react-devtools/Updates.tsx +14 -9
  85. package/src/react-devtools/store.ts +2 -2
  86. package/src/realtime-client/continuity/register-and-attempt-confirmed-update.ts +4 -8
  87. package/src/realtime-client/sync-continuity.ts +2 -2
  88. package/src/realtime-server/index.ts +2 -2
@@ -13,6 +13,10 @@ function newest(scion) {
13
13
  while (scion.child !== null) scion = scion.child;
14
14
  return scion;
15
15
  }
16
+ function eldest(scion) {
17
+ while (scion.parent !== null) scion = scion.parent;
18
+ return scion;
19
+ }
16
20
 
17
21
  //#endregion
18
22
  //#region src/internal/store/circular-buffer.ts
@@ -663,7 +667,8 @@ function openOperation(store, token) {
663
667
  done: /* @__PURE__ */ new Set(),
664
668
  prev: /* @__PURE__ */ new Map(),
665
669
  timestamp: Date.now(),
666
- token
670
+ token,
671
+ subEvents: []
667
672
  };
668
673
  store.logger.info(`⭕`, token.type, token.key, `operation start in store "${store.config.name}"${isChildStore(store) ? ` ${store.transactionMeta.phase} "${store.transactionMeta.update.token.key}"` : ``}`);
669
674
  return store;
@@ -871,31 +876,17 @@ function mintInStore(store, family, key, mustCreate) {
871
876
 
872
877
  //#endregion
873
878
  //#region src/internal/transaction/abort-transaction.ts
874
- const abortTransaction = (store) => {
875
- const target = newest(store);
876
- if (!isChildStore(target)) {
877
- store.logger.warn(`🐞`, `transaction`, `???`, `abortTransaction called outside of a transaction. This is probably a bug in AtomIO.`);
878
- return;
879
- }
880
- store.logger.info(`🪂`, `transaction`, target.transactionMeta.update.token.key, `Aborting transaction`);
879
+ const abortTransaction = (target) => {
880
+ target.logger.info(`🪂`, `transaction`, target.transactionMeta.update.token.key, `Aborting transaction`);
881
881
  target.parent.child = null;
882
882
  };
883
883
 
884
- //#endregion
885
- //#region src/internal/not-found-error.ts
886
- var NotFoundError = class extends Error {
887
- constructor(token, store) {
888
- super(`${PRETTY_TOKEN_TYPES[token.type]} ${stringifyJson(token.key)} not found in store "${store.config.name}".`);
889
- }
890
- };
891
-
892
884
  //#endregion
893
885
  //#region src/internal/transaction/act-upon-store.ts
894
886
  function actUponStore(store, token, id) {
895
887
  return (...parameters) => {
896
888
  const tx = withdraw(store, token);
897
- if (tx) return tx.run(parameters, id);
898
- throw new NotFoundError(token, store);
889
+ return tx.run(parameters, id);
899
890
  };
900
891
  }
901
892
 
@@ -970,17 +961,8 @@ function setIntoStore(store, ...params) {
970
961
  //#region src/internal/set-state/reset-in-store.ts
971
962
  const RESET_STATE = Symbol(`RESET`);
972
963
  function resetInStore(store, ...params) {
973
- let token;
974
- let family;
975
- let key;
976
- if (params.length === 1) {
977
- token = params[0];
978
- setIntoStore(store, token, RESET_STATE);
979
- } else {
980
- family = params[0];
981
- key = params[1];
982
- setIntoStore(store, family, key, RESET_STATE);
983
- }
964
+ const subParams = [...params, RESET_STATE];
965
+ setIntoStore(store, ...subParams);
984
966
  }
985
967
 
986
968
  //#endregion
@@ -1098,25 +1080,42 @@ function fuseWithinStore(store, type, sideA, sideB) {
1098
1080
  allocateIntoStore(store, above, compoundKey, `all`);
1099
1081
  return compoundKey;
1100
1082
  }
1101
- function deallocateFromStore(store, claim) {
1083
+ function createDeallocateTX(store) {
1084
+ return createTransaction(store, {
1085
+ key: `[Internal] deallocate`,
1086
+ do: (_, claim) => {
1087
+ deallocateFromStore(newest(store), claim);
1088
+ }
1089
+ });
1090
+ }
1091
+ function deallocateFromStore(target, claim) {
1102
1092
  const stringKey = stringifyJson(claim);
1103
- const molecule = store.molecules.get(stringKey);
1093
+ const molecule = target.molecules.get(stringKey);
1104
1094
  if (!molecule) {
1105
- const disposal = store.disposalTraces.buffer.find((item) => item?.key === stringKey);
1106
- store.logger.error(`❌`, `key`, claim, `deallocation failed:`, `Could not find allocation for ${stringKey} in store "${store.config.name}".`, disposal ? `\n This state was most recently deallocated\n${disposal.trace}` : `No previous disposal trace for ${stringKey} was found.`);
1095
+ const disposal = target.disposalTraces.buffer.find((item) => item?.key === stringKey);
1096
+ target.logger.error(`❌`, `key`, claim, `deallocation failed:`, `Could not find allocation for ${stringKey} in store "${target.config.name}".`, disposal ? `\n This state was most recently deallocated\n${disposal.trace}` : `No previous disposal trace for ${stringKey} was found.`);
1107
1097
  return;
1108
1098
  }
1109
- const joinKeys = store.moleculeJoins.getRelatedKeys(molecule.key);
1099
+ const joinKeys = target.moleculeJoins.getRelatedKeys(stringKey);
1110
1100
  if (joinKeys) for (const joinKey of joinKeys) {
1111
- const join$1 = store.joins.get(joinKey);
1112
- if (join$1) {
1113
- join$1.relations.delete(molecule.key);
1114
- join$1.molecules.delete(molecule.stringKey);
1115
- }
1101
+ const join$1 = target.joins.get(joinKey);
1102
+ if (join$1) join$1.relations.delete(claim);
1116
1103
  }
1117
- store.moleculeJoins.delete(molecule.stringKey);
1104
+ target.moleculeJoins.delete(stringKey);
1118
1105
  const provenance = [];
1119
1106
  const values = [];
1107
+ const relatedMolecules = target.moleculeGraph.getRelationEntries({ downstreamMoleculeKey: stringKey });
1108
+ if (relatedMolecules) for (const [relatedStringKey, { source }] of relatedMolecules) if (source === stringKey) {
1109
+ const relatedKey = parseJson(relatedStringKey);
1110
+ deallocateFromStore(target, relatedKey);
1111
+ } else provenance.push(source);
1112
+ const familyKeys = target.moleculeData.getRelatedKeys(molecule.stringKey);
1113
+ if (familyKeys) for (const familyKey of familyKeys) {
1114
+ const family = target.families.get(familyKey);
1115
+ const value = getFromStore(target, family, claim);
1116
+ values.push([family.key, value]);
1117
+ disposeFromStore(target, family, claim);
1118
+ }
1120
1119
  const disposalEvent = {
1121
1120
  type: `molecule_disposal`,
1122
1121
  key: molecule.key,
@@ -1124,34 +1123,28 @@ function deallocateFromStore(store, claim) {
1124
1123
  provenance,
1125
1124
  timestamp: Date.now()
1126
1125
  };
1127
- const target = newest(store);
1128
1126
  target.molecules.delete(stringKey);
1129
1127
  const isTransaction = isChildStore(target) && target.transactionMeta.phase === `building`;
1130
1128
  if (isTransaction) target.transactionMeta.update.subEvents.push(disposalEvent);
1131
- const relatedMolecules = store.moleculeGraph.getRelationEntries({ downstreamMoleculeKey: molecule.stringKey });
1132
- if (relatedMolecules) for (const [relatedStringKey, { source }] of relatedMolecules) if (source === molecule.stringKey) {
1133
- const relatedKey = parseJson(relatedStringKey);
1134
- deallocateFromStore(store, relatedKey);
1135
- } else provenance.push(source);
1136
- const familyKeys = target.moleculeData.getRelatedKeys(molecule.stringKey);
1137
- if (familyKeys) for (const familyKey of familyKeys) {
1138
- const family = target.families.get(familyKey);
1139
- const token = findInStore(store, family, molecule.key);
1140
- const value = getFromStore(store, token);
1141
- values.push([family.key, value]);
1142
- disposeFromStore(store, token);
1143
- }
1144
1129
  target.moleculeGraph.delete(molecule.stringKey);
1145
1130
  target.moleculeJoins.delete(molecule.stringKey);
1146
1131
  target.moleculeData.delete(molecule.stringKey);
1147
1132
  if (!isTransaction) target.on.moleculeDisposal.next(disposalEvent);
1148
1133
  target.molecules.delete(molecule.stringKey);
1149
1134
  const trace = getTrace(/* @__PURE__ */ new Error());
1150
- store.disposalTraces.add({
1135
+ target.disposalTraces.add({
1151
1136
  key: stringKey,
1152
1137
  trace
1153
1138
  });
1154
1139
  }
1140
+ function createClaimTX(store) {
1141
+ return createTransaction(store, {
1142
+ key: `[Internal] claim`,
1143
+ do: (_, newProvenance, claim, exclusive) => {
1144
+ claimWithinStore(store, newProvenance, claim, exclusive);
1145
+ }
1146
+ });
1147
+ }
1155
1148
  function claimWithinStore(store, newProvenance, claim, exclusive) {
1156
1149
  const stringKey = stringifyJson(claim);
1157
1150
  const target = newest(store);
@@ -1267,9 +1260,10 @@ function ingestMoleculeTransferEvent(store, event, applying) {
1267
1260
  //#region src/internal/events/ingest-selector-update.ts
1268
1261
  function ingestSelectorUpdateEvent(store, selectorUpdate, applying) {
1269
1262
  let updates;
1270
- if (applying === `newValue`) updates = selectorUpdate.atomUpdates;
1271
- else updates = selectorUpdate.atomUpdates.toReversed();
1272
- for (const atomUpdate of updates) ingestAtomUpdateEvent(store, atomUpdate, applying);
1263
+ if (applying === `newValue`) updates = selectorUpdate.subEvents;
1264
+ else updates = selectorUpdate.subEvents.toReversed();
1265
+ for (const atomUpdate of updates) if (atomUpdate.type === `state_creation`) ingestCreationEvent(store, atomUpdate, applying);
1266
+ else ingestAtomUpdateEvent(store, atomUpdate, applying);
1273
1267
  }
1274
1268
 
1275
1269
  //#endregion
@@ -1312,8 +1306,6 @@ function getEpochNumberOfContinuity(store, continuityKey) {
1312
1306
  return epoch;
1313
1307
  }
1314
1308
  function getEpochNumberOfAction(store, transactionKey) {
1315
- const isRoot = isRootStore(store);
1316
- if (!isRoot) return void 0;
1317
1309
  const continuityKey = getContinuityKey(store, transactionKey);
1318
1310
  if (continuityKey === void 0) return void 0;
1319
1311
  return getEpochNumberOfContinuity(store, continuityKey);
@@ -1322,12 +1314,9 @@ function getEpochNumberOfAction(store, transactionKey) {
1322
1314
  //#endregion
1323
1315
  //#region src/internal/transaction/set-epoch-number.ts
1324
1316
  function setEpochNumberOfContinuity(store, continuityKey, newEpoch) {
1325
- const isRoot = isRootStore(store);
1326
- if (isRoot && continuityKey) store.transactionMeta.epoch.set(continuityKey, newEpoch);
1317
+ store.transactionMeta.epoch.set(continuityKey, newEpoch);
1327
1318
  }
1328
1319
  function setEpochNumberOfAction(store, transactionKey, newEpoch) {
1329
- const isRoot = isRootStore(store);
1330
- if (!isRoot) return;
1331
1320
  const continuityKey = getContinuityKey(store, transactionKey);
1332
1321
  if (continuityKey !== void 0) store.transactionMeta.epoch.set(continuityKey, newEpoch);
1333
1322
  }
@@ -1337,16 +1326,12 @@ function setEpochNumberOfAction(store, transactionKey, newEpoch) {
1337
1326
  function applyTransaction(store, output) {
1338
1327
  const child = newest(store);
1339
1328
  const { parent } = child;
1340
- if (parent === null || !isChildStore(child) || child.transactionMeta?.phase !== `building`) {
1341
- store.logger.warn(`🐞`, `transaction`, `???`, `applyTransaction called outside of a transaction. This is probably a bug in AtomIO.`);
1342
- return;
1343
- }
1344
1329
  child.transactionMeta.phase = `applying`;
1345
1330
  child.transactionMeta.update.output = output;
1346
1331
  parent.child = null;
1347
1332
  parent.on.transactionApplying.next(child.transactionMeta);
1348
1333
  const { subEvents: updates } = child.transactionMeta.update;
1349
- store.logger.info(`🛄`, `transaction`, child.transactionMeta.update.token.key, `Applying transaction with ${updates.length} updates:`, updates);
1334
+ store.logger.info(`🛄`, `transaction`, child.transactionMeta.update.token.key, `applying ${updates.length} subEvents:`, updates);
1350
1335
  ingestTransactionOutcomeEvent(parent, child.transactionMeta.update, `newValue`);
1351
1336
  if (isRootStore(parent)) {
1352
1337
  setEpochNumberOfAction(parent, child.transactionMeta.update.token.key, child.transactionMeta.update.epoch);
@@ -1355,7 +1340,7 @@ function applyTransaction(store, output) {
1355
1340
  type: `transaction`
1356
1341
  });
1357
1342
  myTransaction?.subject.next(child.transactionMeta.update);
1358
- store.logger.info(`🛬`, `transaction`, child.transactionMeta.update.token.key, `Finished applying transaction.`);
1343
+ store.logger.info(`🛬`, `transaction`, child.transactionMeta.update.token.key, `applied`);
1359
1344
  } else if (isChildStore(parent)) parent.transactionMeta.update.subEvents.push(child.transactionMeta.update);
1360
1345
  parent.on.transactionApplying.next(null);
1361
1346
  }
@@ -1363,8 +1348,6 @@ function applyTransaction(store, output) {
1363
1348
  //#endregion
1364
1349
  //#region src/internal/transaction/assign-transaction-to-continuity.ts
1365
1350
  function assignTransactionToContinuity(store, continuityKey, transactionKey) {
1366
- const isRoot = isRootStore(store);
1367
- if (!isRoot) return;
1368
1351
  const { epoch, actionContinuities } = store.transactionMeta;
1369
1352
  actionContinuities.set(continuityKey, transactionKey);
1370
1353
  if (!epoch.has(continuityKey)) epoch.set(continuityKey, -1);
@@ -1441,7 +1424,7 @@ const buildTransaction = (store, token, params, id) => {
1441
1424
  };
1442
1425
  const child = Object.assign(childBase, { transactionMeta });
1443
1426
  parent.child = child;
1444
- store.logger.info(`🛫`, `transaction`, token.key, `Building transaction with params:`, params);
1427
+ store.logger.info(`🛫`, `transaction`, token.key, `building with params:`, params);
1445
1428
  return child;
1446
1429
  };
1447
1430
 
@@ -1455,15 +1438,14 @@ function createTransaction(store, options) {
1455
1438
  type: `transaction`,
1456
1439
  run: (params, id) => {
1457
1440
  const token$1 = deposit(newTransaction);
1458
- const childStore = buildTransaction(store, token$1, params, id);
1441
+ const target$1 = buildTransaction(store, token$1, params, id);
1459
1442
  try {
1460
- const target$1 = newest(store);
1461
- const { toolkit } = childStore.transactionMeta;
1443
+ const { toolkit } = target$1.transactionMeta;
1462
1444
  const output = options.do(toolkit, ...params);
1463
1445
  applyTransaction(target$1, output);
1464
1446
  return output;
1465
1447
  } catch (thrown) {
1466
- abortTransaction(target);
1448
+ abortTransaction(target$1);
1467
1449
  store.logger.warn(`💥`, `transaction`, key, `caught:`, thrown);
1468
1450
  throw thrown;
1469
1451
  }
@@ -1493,12 +1475,14 @@ function dispatchOrDeferStateUpdate(target, state, { oldValue, newValue }, state
1493
1475
  if (stateIsNewlyCreated && family) {
1494
1476
  state.subject.next({ newValue });
1495
1477
  const stateCreationEvent = {
1478
+ checkpoint: true,
1496
1479
  type: `state_creation`,
1497
1480
  subType: `writable`,
1498
1481
  token,
1499
1482
  timestamp: Date.now(),
1500
1483
  value: newValue
1501
1484
  };
1485
+ target.operation.subEvents.push(stateCreationEvent);
1502
1486
  const familySubject = family.subject;
1503
1487
  familySubject.next(stateCreationEvent);
1504
1488
  const innerTarget = newest(target);
@@ -1525,11 +1509,11 @@ function dispatchOrDeferStateUpdate(target, state, { oldValue, newValue }, state
1525
1509
  if (isRootStore(target)) {
1526
1510
  switch (type) {
1527
1511
  case `mutable_atom`:
1528
- target.logger.info(`📢`, type, key, `is now (`, newValue, `) subscribers:`, subject.subscribers);
1512
+ target.logger.info(`📢`, type, key, `is now (`, newValue, `) subscribers:`, subject.subscribers.keys());
1529
1513
  break;
1530
1514
  case `atom`:
1531
1515
  case `writable_pure_selector`:
1532
- case `writable_held_selector`: target.logger.info(`📢`, type, key, `went (`, oldValue, `->`, newValue, `) subscribers:`, subject.subscribers);
1516
+ case `writable_held_selector`: target.logger.info(`📢`, type, key, `went (`, oldValue, `->`, newValue, `) subscribers:`, subject.subscribers.keys());
1533
1517
  }
1534
1518
  subject.next(update);
1535
1519
  }
@@ -2308,9 +2292,22 @@ function reduceReference(store, ...params) {
2308
2292
  const isCounterfeit = `counterfeit` in token;
2309
2293
  const isNewlyCreated = Boolean(brandNewToken) && isCounterfeit === false;
2310
2294
  if (isNewlyCreated && family) {
2295
+ let subType;
2296
+ switch (token.type) {
2297
+ case `readonly_pure_selector`:
2298
+ case `readonly_held_selector`:
2299
+ subType = `readable`;
2300
+ break;
2301
+ case `atom`:
2302
+ case `mutable_atom`:
2303
+ case `writable_pure_selector`:
2304
+ case `writable_held_selector`:
2305
+ subType = `writable`;
2306
+ break;
2307
+ }
2311
2308
  const stateCreationEvent = {
2312
2309
  type: `state_creation`,
2313
- subType: `readable`,
2310
+ subType,
2314
2311
  token,
2315
2312
  timestamp: Date.now()
2316
2313
  };
@@ -2968,14 +2965,14 @@ function disposeAtom(store, atomToken) {
2968
2965
  const familyToken = getFamilyOfToken(store, atomToken);
2969
2966
  const atomFamily$1 = withdraw(store, familyToken);
2970
2967
  const subject = atomFamily$1.subject;
2971
- const disposal = {
2968
+ const disposalEvent = {
2972
2969
  type: `state_disposal`,
2973
2970
  subType: `atom`,
2974
2971
  token: atomToken,
2975
2972
  value: lastValue,
2976
2973
  timestamp: Date.now()
2977
2974
  };
2978
- subject.next(disposal);
2975
+ subject.next(disposalEvent);
2979
2976
  const isChild = isChildStore(target);
2980
2977
  target.atoms.delete(key);
2981
2978
  target.valueMap.delete(key);
@@ -2993,7 +2990,8 @@ function disposeAtom(store, atomToken) {
2993
2990
  const mostRecentUpdate = target.transactionMeta.update.subEvents.at(-1);
2994
2991
  const wasMoleculeDisposal = mostRecentUpdate?.type === `molecule_disposal`;
2995
2992
  const updateAlreadyCaptured = wasMoleculeDisposal && mostRecentUpdate.values.some(([k]) => k === atom.family?.key);
2996
- if (!updateAlreadyCaptured) target.transactionMeta.update.subEvents.push(disposal);
2993
+ const isTracker = hasRole(atom, `tracker:signal`);
2994
+ if (!updateAlreadyCaptured && !isTracker) target.transactionMeta.update.subEvents.push(disposalEvent);
2997
2995
  } else store.on.atomDisposal.next(atomToken);
2998
2996
  }
2999
2997
  }
@@ -3045,7 +3043,6 @@ var Join = class {
3045
3043
  toolkit;
3046
3044
  options;
3047
3045
  defaultContent;
3048
- molecules = /* @__PURE__ */ new Map();
3049
3046
  relations;
3050
3047
  states;
3051
3048
  core;
@@ -3095,6 +3092,9 @@ var Join = class {
3095
3092
  bKeys.delete(a);
3096
3093
  return bKeys;
3097
3094
  });
3095
+ const [x, y] = [a, b].sort();
3096
+ const compositeKey = `${x}:${y}`;
3097
+ this.store.moleculeJoins.delete(compositeKey);
3098
3098
  };
3099
3099
  const replaceRelationsSafely = (toolkit, a, newRelationsOfA) => {
3100
3100
  const { find, get, set } = toolkit;
@@ -3124,9 +3124,9 @@ var Join = class {
3124
3124
  }
3125
3125
  if (!newRelationBIsAlreadyRelated && relationsOfB.size > 0) relationsOfB.clear();
3126
3126
  for (const previousOwner of previousOwnersToDispose) {
3127
- const sorted = [newRelationB, previousOwner].sort();
3128
- const compositeKey = `"${sorted[0]}:${sorted[1]}"`;
3129
- this.molecules.delete(compositeKey);
3127
+ const [x, y] = [newRelationB, previousOwner].sort();
3128
+ const compositeKey = `${x}:${y}`;
3129
+ store.moleculeJoins.delete(compositeKey);
3130
3130
  }
3131
3131
  }
3132
3132
  if (!newRelationBIsAlreadyRelated) relationsOfB.add(a);
@@ -3159,8 +3159,8 @@ var Join = class {
3159
3159
  const baseExternalStoreConfiguration = {
3160
3160
  getRelatedKeys: (key) => getRelatedKeys(this.toolkit, key),
3161
3161
  addRelation: (a, b) => {
3162
- this.store.moleculeJoins.set(a, options.key);
3163
- this.store.moleculeJoins.set(b, options.key);
3162
+ this.store.moleculeJoins.set(`"${a}"`, options.key);
3163
+ this.store.moleculeJoins.set(`"${b}"`, options.key);
3164
3164
  addRelation(this.toolkit, a, b);
3165
3165
  },
3166
3166
  deleteRelation: (a, b) => {
@@ -3193,9 +3193,7 @@ var Join = class {
3193
3193
  setContent: (contentKey, content) => {
3194
3194
  setContent(this.toolkit, contentKey, content);
3195
3195
  },
3196
- deleteContent: (contentKey) => {
3197
- this.realm.deallocate(contentKey);
3198
- }
3196
+ deleteContent: (_) => {}
3199
3197
  };
3200
3198
  externalStore = Object.assign(baseExternalStoreConfiguration, externalStoreWithContentConfiguration);
3201
3199
  } else externalStore = baseExternalStoreConfiguration;
@@ -3205,8 +3203,8 @@ var Join = class {
3205
3203
  isBType: options.isBType,
3206
3204
  makeContentKey: (...args) => {
3207
3205
  const [a, b] = args;
3208
- const sorted = args.sort();
3209
- const compositeKey = `${sorted[0]}:${sorted[1]}`;
3206
+ const [x, y] = args.sort();
3207
+ const compositeKey = `${x}:${y}`;
3210
3208
  const aMolecule = store.molecules.get(stringifyJson(a));
3211
3209
  const bMolecule = store.molecules.get(stringifyJson(b));
3212
3210
  if (!aMolecule) this.realm.allocate(options.key, a);
@@ -3364,7 +3362,8 @@ function getJoin(token, store) {
3364
3362
  const rootJoinMap = IMPLICIT.STORE.joins;
3365
3363
  const rootJoin = rootJoinMap.get(token.key);
3366
3364
  if (rootJoin === void 0) throw new Error(`Join "${token.key}" not found in store "${store.config.name}"`);
3367
- myJoin = new Join(rootJoin.options, rootJoin.defaultContent, store);
3365
+ const root = eldest(store);
3366
+ myJoin = new Join(rootJoin.options, rootJoin.defaultContent, root);
3368
3367
  store.joins.set(token.key, myJoin);
3369
3368
  }
3370
3369
  return myJoin;
@@ -3499,6 +3498,14 @@ function getInternalRelationsFromStore(token, store) {
3499
3498
  return family;
3500
3499
  }
3501
3500
 
3501
+ //#endregion
3502
+ //#region src/internal/not-found-error.ts
3503
+ var NotFoundError = class extends Error {
3504
+ constructor(token, store) {
3505
+ super(`${PRETTY_TOKEN_TYPES[token.type]} ${stringifyJson(token.key)} not found in store "${store.config.name}".`);
3506
+ }
3507
+ };
3508
+
3502
3509
  //#endregion
3503
3510
  //#region src/internal/reserved-keys.ts
3504
3511
  function isReservedIntrospectionKey(value) {
@@ -3521,7 +3528,6 @@ function createTimeline(store, options, data) {
3521
3528
  subject: new Subject(),
3522
3529
  subscriptions: /* @__PURE__ */ new Map()
3523
3530
  };
3524
- if (options.shouldCapture) tl.shouldCapture = options.shouldCapture;
3525
3531
  const timelineKey = options.key;
3526
3532
  const target = newest(store);
3527
3533
  for (const initialTopic of options.scope) switch (initialTopic.type) {
@@ -3587,60 +3593,19 @@ function addAtomToTimeline(store, atomToken, tl) {
3587
3593
  const txUpdateInProgress = target.on.transactionApplying.state?.update;
3588
3594
  store.logger.info(`⏳`, `timeline`, tl.key, `atom`, atomToken.key, `went`, update.oldValue, `->`, update.newValue, txUpdateInProgress ? `in transaction "${txUpdateInProgress.token.key}"` : currentSelectorToken ? `in selector "${currentSelectorToken.key}"` : ``);
3589
3595
  if (tl.timeTraveling === null) if (txUpdateInProgress) joinTransaction(store, tl, txUpdateInProgress);
3590
- else if (currentSelectorToken && currentSelectorTime) {
3591
- let latestUpdate = tl.history.at(-1);
3592
- if (currentSelectorTime !== tl.selectorTime) {
3593
- latestUpdate = {
3594
- type: `selector_update`,
3595
- timestamp: currentSelectorTime,
3596
- token: currentSelectorToken,
3597
- atomUpdates: []
3598
- };
3599
- latestUpdate.atomUpdates.push({
3600
- type: `atom_update`,
3601
- token: atomToken,
3602
- update,
3603
- timestamp: Date.now()
3604
- });
3605
- if (tl.at !== tl.history.length) tl.history.splice(tl.at);
3606
- tl.history.push(latestUpdate);
3607
- store.logger.info(`⌛`, `timeline`, tl.key, `got a selector_update "${currentSelectorToken.key}" with`, latestUpdate.atomUpdates.map((atomUpdate) => atomUpdate.token.key));
3608
- tl.at = tl.history.length;
3609
- tl.selectorTime = currentSelectorTime;
3610
- } else if (latestUpdate?.type === `selector_update`) {
3611
- latestUpdate.atomUpdates.push({
3612
- type: `atom_update`,
3613
- token: atomToken,
3614
- update,
3615
- timestamp: Date.now()
3616
- });
3617
- store.logger.info(`⌛`, `timeline`, tl.key, `set selector_update "${currentSelectorToken.key}" to`, latestUpdate?.atomUpdates.map((atomUpdate) => atomUpdate.token.key));
3618
- }
3619
- if (latestUpdate) {
3620
- const willCaptureSelectorUpdate = tl.shouldCapture?.(latestUpdate, tl) ?? true;
3621
- if (willCaptureSelectorUpdate) tl.subject.next(latestUpdate);
3622
- else {
3623
- tl.history.pop();
3624
- tl.at = tl.history.length;
3625
- }
3626
- }
3627
- } else {
3596
+ else if (currentSelectorToken && currentSelectorTime) buildSelectorUpdate(store, tl, atomToken, update, currentSelectorToken, currentSelectorTime);
3597
+ else {
3628
3598
  const timestamp = Date.now();
3629
3599
  tl.selectorTime = null;
3630
- if (tl.at !== tl.history.length) tl.history.splice(tl.at);
3631
3600
  const atomUpdate = {
3601
+ checkpoint: true,
3632
3602
  type: `atom_update`,
3633
3603
  token: deposit(atom),
3634
3604
  update,
3635
3605
  timestamp
3636
3606
  };
3637
- const willCapture = tl.shouldCapture?.(atomUpdate, tl) ?? true;
3638
3607
  store.logger.info(`⌛`, `timeline`, tl.key, `got an atom_update to "${atom.key}"`);
3639
- if (willCapture) {
3640
- tl.history.push(atomUpdate);
3641
- tl.at = tl.history.length;
3642
- tl.subject.next(atomUpdate);
3643
- }
3608
+ addToHistory(tl, atomUpdate);
3644
3609
  }
3645
3610
  }));
3646
3611
  }
@@ -3669,23 +3634,55 @@ function joinTransaction(store, tl, txUpdateInProgress) {
3669
3634
  unsubscribe();
3670
3635
  tl.transactionKey = null;
3671
3636
  if (tl.timeTraveling === null && currentTxInstanceId) {
3672
- if (tl.at !== tl.history.length) tl.history.splice(tl.at);
3673
3637
  const timelineTopics = store.timelineTopics.getRelatedKeys(tl.key);
3674
3638
  const subEventsFiltered = filterTransactionSubEvents(transactionUpdate.subEvents, timelineTopics);
3675
3639
  const timelineTransactionUpdate = {
3640
+ checkpoint: true,
3676
3641
  ...transactionUpdate,
3677
3642
  subEvents: subEventsFiltered
3678
3643
  };
3679
- const willCapture = tl.shouldCapture?.(timelineTransactionUpdate, tl) ?? true;
3680
- if (willCapture) {
3681
- tl.history.push(timelineTransactionUpdate);
3682
- tl.at = tl.history.length;
3683
- tl.subject.next(timelineTransactionUpdate);
3684
- }
3644
+ addToHistory(tl, timelineTransactionUpdate);
3685
3645
  }
3686
3646
  });
3687
3647
  }
3688
3648
  }
3649
+ function buildSelectorUpdate(store, tl, atomToken, eventOrUpdate, currentSelectorToken, currentSelectorTime) {
3650
+ let latestUpdate = tl.history.at(-1);
3651
+ if (currentSelectorTime !== tl.selectorTime) {
3652
+ const selectorUpdate = latestUpdate = {
3653
+ checkpoint: true,
3654
+ type: `selector_update`,
3655
+ timestamp: currentSelectorTime,
3656
+ token: currentSelectorToken,
3657
+ subEvents: []
3658
+ };
3659
+ if (`type` in eventOrUpdate) latestUpdate.subEvents.push(eventOrUpdate);
3660
+ else latestUpdate.subEvents.push({
3661
+ type: `atom_update`,
3662
+ token: atomToken,
3663
+ update: eventOrUpdate,
3664
+ timestamp: Date.now()
3665
+ });
3666
+ addToHistory(tl, latestUpdate);
3667
+ tl.selectorTime = currentSelectorTime;
3668
+ store.logger.info(`⌛`, `timeline`, tl.key, `got a selector_update "${currentSelectorToken.key}" with`, latestUpdate.subEvents.map((event) => event.token.key));
3669
+ const operation = store.operation;
3670
+ const unsub = store.on.operationClose.subscribe(`timeline:${tl.key} (needs to gather nested selector creations)`, () => {
3671
+ unsub();
3672
+ if (operation.open) selectorUpdate.subEvents = [...operation.subEvents, ...selectorUpdate.subEvents];
3673
+ });
3674
+ } else if (latestUpdate?.type === `selector_update`) {
3675
+ if (`type` in eventOrUpdate) latestUpdate.subEvents.push(eventOrUpdate);
3676
+ else latestUpdate.subEvents.push({
3677
+ type: `atom_update`,
3678
+ token: atomToken,
3679
+ update: eventOrUpdate,
3680
+ timestamp: Date.now()
3681
+ });
3682
+ store.logger.info(`⌛`, `timeline`, tl.key, `set selector_update "${currentSelectorToken.key}" to`, latestUpdate?.subEvents.map((event) => event.token.key));
3683
+ }
3684
+ if (latestUpdate) tl.subject.next(latestUpdate);
3685
+ }
3689
3686
  function filterTransactionSubEvents(updates, timelineTopics) {
3690
3687
  return updates.filter((updateFromTx) => {
3691
3688
  if (updateFromTx.type === `transaction_outcome`) return true;
@@ -3714,16 +3711,15 @@ function filterTransactionSubEvents(updates, timelineTopics) {
3714
3711
  });
3715
3712
  }
3716
3713
  function handleStateLifecycleEvent(store, event, tl) {
3714
+ const currentSelectorToken = store.operation.open && store.operation.token.type === `writable_pure_selector` ? store.operation.token : null;
3715
+ const currentSelectorTime = store.operation.open && store.operation.token.type === `writable_pure_selector` ? store.operation.timestamp : null;
3717
3716
  if (!tl.timeTraveling) {
3718
3717
  const target = newest(store);
3719
3718
  if (isChildStore(target)) {} else {
3720
3719
  const txUpdateInProgress = target.on.transactionApplying.state;
3721
3720
  if (txUpdateInProgress) joinTransaction(store, tl, txUpdateInProgress.update);
3722
- else {
3723
- tl.history.push(event);
3724
- tl.at = tl.history.length;
3725
- tl.subject.next(event);
3726
- }
3721
+ else if (currentSelectorToken && currentSelectorTime && event.type === `state_creation`) buildSelectorUpdate(store, tl, event.token, event, currentSelectorToken, currentSelectorTime);
3722
+ else addToHistory(tl, event);
3727
3723
  }
3728
3724
  }
3729
3725
  switch (event.type) {
@@ -3736,6 +3732,12 @@ function handleStateLifecycleEvent(store, event, tl) {
3736
3732
  break;
3737
3733
  }
3738
3734
  }
3735
+ function addToHistory(tl, event) {
3736
+ if (tl.at !== tl.history.length) tl.history.splice(tl.at);
3737
+ tl.history.push(event);
3738
+ tl.at = tl.history.length;
3739
+ tl.subject.next(event);
3740
+ }
3739
3741
 
3740
3742
  //#endregion
3741
3743
  //#region src/internal/timeline/time-travel.ts
@@ -3751,10 +3753,21 @@ const timeTravel = (store, action, token) => {
3751
3753
  return;
3752
3754
  }
3753
3755
  timelineData.timeTraveling = action === `redo` ? `into_future` : `into_past`;
3754
- if (action === `undo`) --timelineData.at;
3755
- const event = timelineData.history[timelineData.at];
3756
+ let nextIndex = timelineData.at;
3757
+ let events;
3758
+ switch (action) {
3759
+ case `undo`:
3760
+ --nextIndex;
3761
+ while (nextIndex !== 0 && timelineData.history[nextIndex].checkpoint !== true) --nextIndex;
3762
+ events = timelineData.history.slice(nextIndex, timelineData.at).reverse();
3763
+ break;
3764
+ case `redo`:
3765
+ ++nextIndex;
3766
+ events = timelineData.history.slice(timelineData.at, nextIndex);
3767
+ }
3768
+ timelineData.at = nextIndex;
3756
3769
  const applying = action === `redo` ? `newValue` : `oldValue`;
3757
- switch (event.type) {
3770
+ for (const event of events) switch (event.type) {
3758
3771
  case `atom_update`:
3759
3772
  ingestAtomUpdateEvent(store, event, applying);
3760
3773
  break;
@@ -3770,15 +3783,12 @@ const timeTravel = (store, action, token) => {
3770
3783
  case `state_disposal`:
3771
3784
  ingestDisposalEvent(store, event, applying);
3772
3785
  break;
3773
- case `molecule_creation`:
3774
- case `molecule_disposal`:
3775
3786
  }
3776
- if (action === `redo`) ++timelineData.at;
3777
3787
  timelineData.subject.next(action);
3778
3788
  timelineData.timeTraveling = null;
3779
- store.logger.info(`⏹️`, `timeline`, token.key, `"${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`);
3789
+ store.logger.info(`⏸️`, `timeline`, token.key, `"${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`);
3780
3790
  };
3781
3791
 
3782
3792
  //#endregion
3783
- export { COUNTERFEIT, CircularBuffer, FAMILY_MEMBER_TOKEN_TYPES, FamilyTracker, Future, IMPLICIT, INTERNAL_ROLES, Join, Junction, MapOverlay, NotFoundError, RESET_STATE, RelationsOverlay, SetOverlay, StatefulSubject, Store, Subject, TRANSACTION_PHASES, Tracker, abortTransaction, actUponStore, allocateIntoStore, applyTransaction, arbitrary, assignTransactionToContinuity, become, buildTransaction, capitalize, claimWithinStore, clearStore, closeOperation, createJoin, createMutableAtom, createMutableAtomFamily, createReadonlyHeldSelector, createReadonlyPureSelector, createReadonlyPureSelectorFamily, createRegularAtom, createRegularAtomFamily, createSelectorFamily, createStandaloneSelector, createTimeline, createTransaction, createWritableHeldSelector, createWritablePureSelector, createWritablePureSelectorFamily, deallocateFromStore, deposit, disposeAtom, disposeFromStore, disposeSelector, editRelationsInStore, evictCachedValue, evictDownstreamFromAtom, evictDownstreamFromSelector, findInStore, findRelationsInStore, fuseWithinStore, getContinuityKey, getEnvironmentData, getEpochNumberOfAction, getEpochNumberOfContinuity, getFromStore, getInternalRelationsFromStore, getJoin, getJsonFamily, getJsonToken, getSelectorDependencyKeys, getTrace, getUpdateFamily, getUpdateToken, hasRole, ingestAtomUpdateEvent, ingestCreationEvent, ingestDisposalEvent, ingestMoleculeCreationEvent, ingestMoleculeDisposalEvent, ingestMoleculeTransferEvent, ingestSelectorUpdateEvent, ingestTransactionOutcomeEvent, installIntoStore, isAtomKey, isChildStore, isDone, isReadonlySelectorKey, isReservedIntrospectionKey, isRootStore, isSelectorKey, isStateKey, isTransceiver, makeRootMoleculeInStore, markDone, mint, newest, openOperation, readFromCache, readOrComputeValue, recallState, registerSelector, resetAtomOrSelector, resetInStore, seekInStore, setAtomOrSelector, setEpochNumberOfAction, setEpochNumberOfContinuity, setIntoStore, subscribeInStore, subscribeToRootDependency, subscribeToState, subscribeToTimeline, subscribeToTransaction, timeTravel, traceRootSelectorAtoms, updateSelectorAtoms, withdraw, writeToCache };
3793
+ export { COUNTERFEIT, CircularBuffer, FAMILY_MEMBER_TOKEN_TYPES, FamilyTracker, Future, IMPLICIT, INTERNAL_ROLES, Join, Junction, MapOverlay, NotFoundError, RESET_STATE, RelationsOverlay, SetOverlay, StatefulSubject, Store, Subject, TRANSACTION_PHASES, Tracker, abortTransaction, actUponStore, allocateIntoStore, applyTransaction, arbitrary, assignTransactionToContinuity, become, buildTransaction, capitalize, claimWithinStore, clearStore, closeOperation, createClaimTX, createDeallocateTX, createJoin, createMutableAtom, createMutableAtomFamily, createReadonlyHeldSelector, createReadonlyPureSelector, createReadonlyPureSelectorFamily, createRegularAtom, createRegularAtomFamily, createSelectorFamily, createStandaloneSelector, createTimeline, createTransaction, createWritableHeldSelector, createWritablePureSelector, createWritablePureSelectorFamily, deallocateFromStore, deposit, disposeAtom, disposeFromStore, disposeSelector, editRelationsInStore, eldest, evictCachedValue, evictDownstreamFromAtom, evictDownstreamFromSelector, findInStore, findRelationsInStore, fuseWithinStore, getContinuityKey, getEnvironmentData, getEpochNumberOfAction, getEpochNumberOfContinuity, getFromStore, getInternalRelationsFromStore, getJoin, getJsonFamily, getJsonToken, getSelectorDependencyKeys, getTrace, getUpdateFamily, getUpdateToken, hasRole, ingestAtomUpdateEvent, ingestCreationEvent, ingestDisposalEvent, ingestMoleculeCreationEvent, ingestMoleculeDisposalEvent, ingestMoleculeTransferEvent, ingestSelectorUpdateEvent, ingestTransactionOutcomeEvent, installIntoStore, isAtomKey, isChildStore, isDone, isReadonlySelectorKey, isReservedIntrospectionKey, isRootStore, isSelectorKey, isStateKey, isTransceiver, makeRootMoleculeInStore, markDone, mint, newest, openOperation, readFromCache, readOrComputeValue, recallState, registerSelector, resetAtomOrSelector, resetInStore, seekInStore, setAtomOrSelector, setEpochNumberOfAction, setEpochNumberOfContinuity, setIntoStore, subscribeInStore, subscribeToRootDependency, subscribeToState, subscribeToTimeline, subscribeToTransaction, timeTravel, traceRootSelectorAtoms, updateSelectorAtoms, withdraw, writeToCache };
3784
3794
  //# sourceMappingURL=index.js.map