atom.io 0.27.1 → 0.27.3

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 (45) hide show
  1. package/data/dist/index.d.ts +12 -12
  2. package/data/dist/index.js +20 -14
  3. package/data/src/dict.ts +4 -4
  4. package/data/src/join.ts +23 -18
  5. package/data/src/struct-family.ts +2 -2
  6. package/dist/chunk-ETCFHO7J.js +3087 -0
  7. package/dist/index.d.ts +4 -31
  8. package/eslint-plugin/dist/index.js +8 -0
  9. package/eslint-plugin/src/walk.ts +8 -0
  10. package/internal/dist/index.d.ts +52 -19
  11. package/internal/dist/index.js +2 -3044
  12. package/internal/src/families/create-atom-family.ts +6 -5
  13. package/internal/src/families/create-readonly-selector-family.ts +38 -29
  14. package/internal/src/families/create-regular-atom-family.ts +40 -31
  15. package/internal/src/families/create-selector-family.ts +6 -5
  16. package/internal/src/families/create-writable-selector-family.ts +39 -30
  17. package/internal/src/families/throw-in-case-of-conflicting-family.ts +18 -0
  18. package/internal/src/index.ts +76 -2
  19. package/internal/src/ingest-updates/ingest-creation-disposal.ts +0 -1
  20. package/internal/src/ingest-updates/ingest-selector-update.ts +1 -1
  21. package/internal/src/molecule/dispose-molecule.ts +0 -1
  22. package/internal/src/molecule/grow-molecule-in-store.ts +27 -17
  23. package/internal/src/mutable/create-mutable-atom-family.ts +41 -32
  24. package/internal/src/mutable/get-json-family.ts +2 -1
  25. package/internal/src/mutable/get-update-family.ts +23 -0
  26. package/internal/src/mutable/index.ts +1 -0
  27. package/internal/src/mutable/tracker-family.ts +5 -3
  28. package/internal/src/not-found-error.ts +2 -35
  29. package/internal/src/pretty-print.ts +37 -0
  30. package/internal/src/store/store.ts +10 -4
  31. package/internal/src/store/withdraw.ts +8 -8
  32. package/introspection/dist/index.js +3 -3
  33. package/introspection/src/attach-timeline-family.ts +1 -1
  34. package/introspection/src/attach-transaction-logs.ts +2 -2
  35. package/json/dist/index.d.ts +2 -2
  36. package/json/dist/index.js +16 -12
  37. package/json/src/select-json-family.ts +21 -18
  38. package/package.json +4 -4
  39. package/realtime-server/dist/index.d.ts +1 -1
  40. package/src/atom.ts +2 -32
  41. package/src/get-state.ts +2 -2
  42. package/src/index.ts +1 -10
  43. package/src/selector.ts +1 -26
  44. package/src/set-state.ts +2 -2
  45. package/src/silo.ts +7 -5
@@ -1,3045 +1,3 @@
1
- import { Junction } from '../../dist/chunk-IBTHB2PI.js';
1
+ export { FamilyTracker, Future, IMPLICIT, LazyMap, Molecule, NotFoundError, StatefulSubject, Store, Subject, TRANSACTION_PHASES, Tracker, abortTransaction, actUponStore, applyTransaction, arbitrary, assignTransactionToContinuity, become, buildTransaction, cacheValue, clearStore, closeOperation, createAtomFamily, createMoleculeFamily, createMutableAtom, createMutableAtomFamily, createReadonlySelector, createReadonlySelectorFamily, createRegularAtom, createRegularAtomFamily, createSelectorFamily, createStandaloneAtom, createStandaloneSelector, createTimeline, createTransaction, createWritableSelector, deposit, disposeAtom, disposeFromStore, disposeMolecule, disposeSelector, eldest, evictCachedValue, findInStore, getContinuityKey, getEnvironmentData, getEpochNumberOfAction, getEpochNumberOfContinuity, getFromStore, getJsonFamily, getJsonToken, getSelectorDependencyKeys, getUpdateFamily, getUpdateToken, growMoleculeInStore, ingestAtomUpdate, ingestCreationEvent, ingestDisposalEvent, ingestMoleculeCreationEvent, ingestMoleculeDisposalEvent, ingestSelectorUpdate, ingestTransactionUpdate, initFamilyMemberInStore, isAtomDefault, isAtomKey, isChildStore, isDone, isReadonlySelectorKey, isRootStore, isSelectorKey, isStateKey, isTransceiver, makeMoleculeInStore, markAtomAsDefault, markAtomAsNotDefault, markDone, newest, openOperation, prettyPrintTokenType, readCachedValue, readOrComputeValue, registerSelector, seekInStore, setAtomOrSelector, setEpochNumberOfAction, setEpochNumberOfContinuity, setIntoStore, subscribeToRootAtoms, subscribeToState, subscribeToTimeline, subscribeToTransaction, timeTravel, traceAllSelectorAtoms, traceSelectorAtoms, updateSelectorAtoms, withdraw } from '../../dist/chunk-ETCFHO7J.js';
2
+ import '../../dist/chunk-IBTHB2PI.js';
2
3
  import '../../dist/chunk-XWL6SNVU.js';
3
- import { stringifyJson, parseJson, selectJson, selectJsonFamily } from 'atom.io/json';
4
- import { AtomIOLogger } from 'atom.io';
5
- import { getJoin, findRelations } from 'atom.io/data';
6
-
7
- // internal/src/arbitrary.ts
8
- function arbitrary(random = Math.random) {
9
- return random().toString(36).slice(2);
10
- }
11
-
12
- // internal/src/future.ts
13
- var Future = class extends Promise {
14
- fate;
15
- resolve;
16
- reject;
17
- constructor(executor) {
18
- let superResolve;
19
- let superReject;
20
- super((resolve, reject) => {
21
- superResolve = resolve;
22
- superReject = reject;
23
- });
24
- this.resolve = superResolve;
25
- this.reject = superReject;
26
- this.use(executor instanceof Promise ? executor : new Promise(executor));
27
- }
28
- pass(promise, value) {
29
- if (promise === this.fate) {
30
- this.resolve(value);
31
- }
32
- }
33
- fail(promise, reason) {
34
- if (promise === this.fate) {
35
- this.reject(reason);
36
- }
37
- }
38
- use(value) {
39
- if (value instanceof Promise) {
40
- const promise = value;
41
- this.fate = promise;
42
- promise.then(
43
- (resolved) => {
44
- this.pass(promise, resolved);
45
- },
46
- (reason) => {
47
- this.fail(promise, reason);
48
- }
49
- );
50
- } else {
51
- this.resolve(value);
52
- this.fate = void 0;
53
- }
54
- }
55
- };
56
-
57
- // internal/src/lineage.ts
58
- function newest(scion) {
59
- while (scion.child !== null) {
60
- scion = scion.child;
61
- }
62
- return scion;
63
- }
64
- function eldest(scion) {
65
- while (scion.parent !== null) {
66
- scion = scion.parent;
67
- }
68
- return scion;
69
- }
70
-
71
- // internal/src/store/deposit.ts
72
- function deposit(state) {
73
- const token = {
74
- key: state.key,
75
- type: state.type
76
- };
77
- if (`family` in state) {
78
- token.family = state.family;
79
- }
80
- return token;
81
- }
82
-
83
- // internal/src/subject.ts
84
- var Subject = class {
85
- Subscriber;
86
- subscribers = /* @__PURE__ */ new Map();
87
- subscribe(key, subscriber) {
88
- this.subscribers.set(key, subscriber);
89
- const unsubscribe = () => {
90
- this.unsubscribe(key);
91
- };
92
- return unsubscribe;
93
- }
94
- unsubscribe(key) {
95
- this.subscribers.delete(key);
96
- }
97
- next(value) {
98
- const subscribers = this.subscribers.values();
99
- for (const subscriber of subscribers) {
100
- subscriber(value);
101
- }
102
- }
103
- };
104
- var StatefulSubject = class extends Subject {
105
- state;
106
- constructor(initialState) {
107
- super();
108
- this.state = initialState;
109
- }
110
- next(value) {
111
- this.state = value;
112
- super.next(value);
113
- }
114
- };
115
-
116
- // internal/src/transaction/is-root-store.ts
117
- function isRootStore(store) {
118
- return `epoch` in store.transactionMeta;
119
- }
120
- function isChildStore(store) {
121
- return `phase` in store.transactionMeta;
122
- }
123
-
124
- // internal/src/transaction/abort-transaction.ts
125
- var abortTransaction = (store) => {
126
- const target = newest(store);
127
- if (!isChildStore(target)) {
128
- store.logger.warn(
129
- `\u{1F41E}`,
130
- `transaction`,
131
- `???`,
132
- `abortTransaction called outside of a transaction. This is probably a bug in AtomIO.`
133
- );
134
- return;
135
- }
136
- store.logger.info(
137
- `\u{1FA82}`,
138
- `transaction`,
139
- target.transactionMeta.update.key,
140
- `Aborting transaction`
141
- );
142
- target.parent.child = null;
143
- };
144
- var capitalize = (str) => str[0].toUpperCase() + str.slice(1);
145
- function prettyPrintTokenType(token) {
146
- switch (token.type) {
147
- case `atom_family`:
148
- return `Atom Family`;
149
- case `molecule_family`:
150
- return `Molecule Family`;
151
- case `readonly_selector`:
152
- return `Readonly Selector`;
153
- case `readonly_selector_family`:
154
- return `Readonly Selector Family`;
155
- case `selector_family`:
156
- return `Selector Family`;
157
- default:
158
- return capitalize(token.type);
159
- }
160
- }
161
- var NotFoundError = class extends Error {
162
- constructor(...params) {
163
- const token = params[0];
164
- const store = params.length === 2 ? params[1] : params[2];
165
- if (params.length === 2) {
166
- super(
167
- `${prettyPrintTokenType(token)} ${stringifyJson(token.key)} not found in store "${store.config.name}".`
168
- );
169
- } else {
170
- const key = params[1];
171
- super(
172
- `${prettyPrintTokenType(token)} "${token.key}" member ${stringifyJson(key)} not found in store "${store.config.name}".`
173
- );
174
- }
175
- }
176
- };
177
-
178
- // internal/src/transaction/act-upon-store.ts
179
- function actUponStore(token, id, store) {
180
- return (...parameters) => {
181
- const tx = withdraw(token, store);
182
- if (tx) {
183
- return tx.run(parameters, id);
184
- }
185
- throw new NotFoundError(token, store);
186
- };
187
- }
188
-
189
- // internal/src/set-state/become.ts
190
- var become = (nextVersionOfThing) => (originalThing) => nextVersionOfThing instanceof Function ? nextVersionOfThing(
191
- originalThing instanceof Function ? originalThing() : originalThing
192
- ) : nextVersionOfThing;
193
-
194
- // internal/src/get-state/read-or-compute-value.ts
195
- var readOrComputeValue = (state, target) => {
196
- if (target.valueMap.has(state.key)) {
197
- target.logger.info(`\u{1F4D6}`, state.type, state.key, `reading cached value`);
198
- return readCachedValue(state, target);
199
- }
200
- if (state.type !== `atom` && state.type !== `mutable_atom`) {
201
- target.logger.info(`\u{1F9EE}`, state.type, state.key, `computing value`);
202
- return state.get();
203
- }
204
- const fallback = state.default instanceof Function ? state.default() : state.default;
205
- target.logger.info(
206
- `\u{1F481}`,
207
- `atom`,
208
- state.key,
209
- `could not find cached value; using default`,
210
- fallback
211
- );
212
- return state.default instanceof Function ? state.default() : state.default;
213
- };
214
-
215
- // internal/src/operation.ts
216
- var openOperation = (token, store) => {
217
- if (store.operation.open) {
218
- const rejectionTime = performance.now();
219
- store.logger.info(
220
- `\u2757`,
221
- token.type,
222
- token.key,
223
- `deferring setState at T-${rejectionTime} until setState for "${store.operation.token.key}" is done`
224
- );
225
- return rejectionTime;
226
- }
227
- store.operation = {
228
- open: true,
229
- done: /* @__PURE__ */ new Set(),
230
- prev: /* @__PURE__ */ new Map(),
231
- time: Date.now(),
232
- token
233
- };
234
- store.logger.info(
235
- `\u2B55`,
236
- token.type,
237
- token.key,
238
- `operation start in store "${store.config.name}"${!isChildStore(store) ? `` : ` ${store.transactionMeta.phase} "${store.transactionMeta.update.key}"`}`
239
- );
240
- };
241
- var closeOperation = (store) => {
242
- if (store.operation.open) {
243
- store.logger.info(
244
- `\u{1F534}`,
245
- store.operation.token.type,
246
- store.operation.token.key,
247
- `operation done in store "${store.config.name}"`
248
- );
249
- }
250
- store.operation = { open: false };
251
- store.on.operationClose.next(store.operation);
252
- };
253
- var isDone = (key, store) => {
254
- if (!store.operation.open) {
255
- store.logger.warn(
256
- `\u{1F41E}`,
257
- `unknown`,
258
- key,
259
- `isDone called outside of an operation. This is probably a bug.`
260
- );
261
- return true;
262
- }
263
- return store.operation.done.has(key);
264
- };
265
- var markDone = (key, store) => {
266
- if (!store.operation.open) {
267
- store.logger.warn(
268
- `\u{1F41E}`,
269
- `unknown`,
270
- key,
271
- `markDone called outside of an operation. This is probably a bug.`
272
- );
273
- return;
274
- }
275
- store.operation.done.add(key);
276
- };
277
-
278
- // internal/src/set-state/emit-update.ts
279
- var emitUpdate = (state, update, store) => {
280
- switch (state.type) {
281
- case `mutable_atom`:
282
- store.logger.info(
283
- `\u{1F4E2}`,
284
- state.type,
285
- state.key,
286
- `is now (`,
287
- update.newValue,
288
- `) subscribers:`,
289
- state.subject.subscribers
290
- );
291
- break;
292
- default:
293
- store.logger.info(
294
- `\u{1F4E2}`,
295
- state.type,
296
- state.key,
297
- `went (`,
298
- update.oldValue,
299
- `->`,
300
- update.newValue,
301
- `) subscribers:`,
302
- state.subject.subscribers
303
- );
304
- }
305
- state.subject.next(update);
306
- };
307
-
308
- // internal/src/set-state/evict-downstream.ts
309
- var evictDownStream = (atom, store) => {
310
- const target = newest(store);
311
- const downstreamKeys = target.selectorAtoms.getRelatedKeys(atom.key);
312
- target.logger.info(
313
- `\u{1F9F9}`,
314
- atom.type,
315
- atom.key,
316
- downstreamKeys ? `evicting ${downstreamKeys.size} states downstream:` : `no downstream states`,
317
- downstreamKeys ?? `to evict`
318
- );
319
- if (downstreamKeys) {
320
- if (target.operation.open) {
321
- target.logger.info(
322
- `\u{1F9F9}`,
323
- atom.type,
324
- atom.key,
325
- `[ ${[...target.operation.done].join(`, `)} ] already done`
326
- );
327
- }
328
- for (const key of downstreamKeys) {
329
- if (isDone(key, target)) {
330
- continue;
331
- }
332
- evictCachedValue(key, target);
333
- markDone(key, target);
334
- }
335
- }
336
- };
337
-
338
- // internal/src/set-state/stow-update.ts
339
- function shouldUpdateBeStowed(key, update) {
340
- if (isTransceiver(update.newValue)) {
341
- return false;
342
- }
343
- if (key.includes(`\u{1F441}\u200D\u{1F5E8}`)) {
344
- return false;
345
- }
346
- return true;
347
- }
348
- var stowUpdate = (state, update, store) => {
349
- const { key } = state;
350
- const target = newest(store);
351
- if (!isChildStore(target) || target.transactionMeta.phase !== `building`) {
352
- store.logger.error(
353
- `\u{1F41E}`,
354
- `atom`,
355
- key,
356
- `stowUpdate called outside of a transaction. This is probably a bug.`
357
- );
358
- return;
359
- }
360
- const shouldStow = shouldUpdateBeStowed(key, update);
361
- if (!shouldStow) {
362
- return;
363
- }
364
- const atomUpdate = {
365
- type: `atom_update`,
366
- key,
367
- ...update
368
- };
369
- if (state.family) {
370
- atomUpdate.family = state.family;
371
- }
372
- target.transactionMeta.update.updates.push(atomUpdate);
373
- store.logger.info(
374
- `\u{1F4C1}`,
375
- `atom`,
376
- key,
377
- `stowed (`,
378
- update.oldValue,
379
- `->`,
380
- update.newValue,
381
- `)`
382
- );
383
- };
384
-
385
- // internal/src/set-state/set-atom.ts
386
- var setAtom = (atom, next, target) => {
387
- const oldValue = readOrComputeValue(atom, target);
388
- let newValue = oldValue;
389
- if (atom.type === `mutable_atom` && isChildStore(target)) {
390
- const { parent } = target;
391
- const copiedValue = copyMutableIfNeeded(atom, parent, target);
392
- newValue = copiedValue;
393
- }
394
- newValue = become(next)(newValue);
395
- target.logger.info(`\u{1F4DD}`, `atom`, atom.key, `set to`, newValue);
396
- newValue = cacheValue(atom.key, newValue, atom.subject, target);
397
- if (isAtomDefault(atom.key, target)) {
398
- markAtomAsNotDefault(atom.key, target);
399
- }
400
- markDone(atom.key, target);
401
- evictDownStream(atom, target);
402
- const update = { oldValue, newValue };
403
- if (isRootStore(target)) {
404
- emitUpdate(atom, update, target);
405
- } else if (target.parent) {
406
- if (target.on.transactionApplying.state === null) {
407
- stowUpdate(atom, update, target);
408
- } else if (atom.key.startsWith(`*`)) {
409
- const mutableKey = atom.key.slice(1);
410
- const mutableAtom = target.atoms.get(mutableKey);
411
- let transceiver = target.valueMap.get(mutableKey);
412
- if (mutableAtom.type === `mutable_atom` && isChildStore(target)) {
413
- const { parent } = target;
414
- const copiedValue = copyMutableIfNeeded(mutableAtom, parent, target);
415
- transceiver = copiedValue;
416
- }
417
- const accepted = transceiver.do(update.newValue) === null;
418
- if (accepted) evictDownStream(mutableAtom, target);
419
- }
420
- }
421
- };
422
-
423
- // internal/src/set-state/set-atom-or-selector.ts
424
- var setAtomOrSelector = (state, value, store) => {
425
- switch (state.type) {
426
- case `atom`:
427
- case `mutable_atom`:
428
- setAtom(state, value, store);
429
- break;
430
- case `selector`:
431
- state.set(value);
432
- break;
433
- }
434
- };
435
- function createRegularAtomFamily(options, store) {
436
- const subject = new Subject();
437
- const atomFamily = Object.assign(
438
- (key) => {
439
- const subKey = stringifyJson(key);
440
- const family = { key: options.key, subKey };
441
- const fullKey = `${options.key}(${subKey})`;
442
- const target = newest(store);
443
- const def = options.default;
444
- const individualOptions = {
445
- key: fullKey,
446
- default: def instanceof Function ? def(key) : def
447
- };
448
- if (options.effects) {
449
- individualOptions.effects = options.effects(key);
450
- }
451
- const token = createRegularAtom(individualOptions, family, target);
452
- subject.next({ type: `state_creation`, token });
453
- return token;
454
- },
455
- {
456
- key: options.key,
457
- type: `atom_family`,
458
- subject,
459
- install: (s) => createRegularAtomFamily(options, s)
460
- }
461
- );
462
- store.families.set(options.key, atomFamily);
463
- return atomFamily;
464
- }
465
-
466
- // internal/src/families/create-atom-family.ts
467
- function createAtomFamily(options, store) {
468
- const isMutable = `mutable` in options;
469
- if (isMutable) {
470
- return createMutableAtomFamily(options, store);
471
- }
472
- return createRegularAtomFamily(options, store);
473
- }
474
-
475
- // internal/src/get-state/get-from-store.ts
476
- function getFromStore(...params) {
477
- let token;
478
- let store;
479
- if (params.length === 2) {
480
- token = params[0];
481
- store = params[1];
482
- } else {
483
- const family = params[0];
484
- const key = params[1];
485
- store = params[2];
486
- const maybeToken = family.type === `molecule_family` ? seekInStore(family, key, store) : store.config.lifespan === `immortal` ? seekInStore(family, key, store) : findInStore(family, key, store);
487
- if (!maybeToken) {
488
- throw new NotFoundError(family, key, store);
489
- }
490
- token = maybeToken;
491
- }
492
- switch (token.type) {
493
- case `atom`:
494
- case `mutable_atom`:
495
- case `selector`:
496
- case `readonly_selector`:
497
- return readOrComputeValue(withdraw(token, store), store);
498
- case `molecule`:
499
- return withdraw(token, store).instance;
500
- }
501
- }
502
-
503
- // internal/src/keys.ts
504
- var isAtomKey = (key, store) => newest(store).atoms.has(key);
505
- var isSelectorKey = (key, store) => newest(store).selectors.has(key);
506
- var isReadonlySelectorKey = (key, store) => newest(store).readonlySelectors.has(key);
507
- var isStateKey = (key, store) => isAtomKey(key, store) || isSelectorKey(key, store) || isReadonlySelectorKey(key, store);
508
-
509
- // internal/src/selector/get-selector-dependency-keys.ts
510
- var getSelectorDependencyKeys = (key, store) => {
511
- const sources = newest(store).selectorGraph.getRelationEntries({ downstreamSelectorKey: key }).filter(([_, { source }]) => source !== key).map(([_, { source }]) => source).filter((source) => isStateKey(source, store));
512
- return sources;
513
- };
514
-
515
- // internal/src/selector/trace-selector-atoms.ts
516
- var traceSelectorAtoms = (directDependencyKey, covered, store) => {
517
- const rootKeys = [];
518
- const indirectDependencyKeys = getSelectorDependencyKeys(
519
- directDependencyKey,
520
- store
521
- );
522
- while (indirectDependencyKeys.length > 0) {
523
- const indirectDependencyKey = indirectDependencyKeys.shift();
524
- if (covered.has(indirectDependencyKey)) {
525
- continue;
526
- }
527
- covered.add(indirectDependencyKey);
528
- if (!isAtomKey(indirectDependencyKey, store)) {
529
- indirectDependencyKeys.push(
530
- ...getSelectorDependencyKeys(indirectDependencyKey, store)
531
- );
532
- } else if (!rootKeys.includes(indirectDependencyKey)) {
533
- rootKeys.push(indirectDependencyKey);
534
- }
535
- }
536
- return rootKeys;
537
- };
538
- var traceAllSelectorAtoms = (selector, store) => {
539
- const selectorKey = selector.key;
540
- const directDependencyKeys = getSelectorDependencyKeys(selectorKey, store);
541
- const covered = /* @__PURE__ */ new Set();
542
- return directDependencyKeys.flatMap(
543
- (depKey) => isAtomKey(depKey, store) ? depKey : traceSelectorAtoms(depKey, covered, store)
544
- );
545
- };
546
-
547
- // internal/src/selector/update-selector-atoms.ts
548
- var updateSelectorAtoms = (selectorKey, dependency, covered, store) => {
549
- const target = newest(store);
550
- if (dependency.type === `atom` || dependency.type === `mutable_atom`) {
551
- target.selectorAtoms.set({
552
- selectorKey,
553
- atomKey: dependency.key
554
- });
555
- store.logger.info(
556
- `\u{1F50D}`,
557
- `selector`,
558
- selectorKey,
559
- `discovers root atom "${dependency.key}"`
560
- );
561
- } else {
562
- const rootKeys = traceSelectorAtoms(dependency.key, covered, store);
563
- store.logger.info(
564
- `\u{1F50D}`,
565
- `selector`,
566
- selectorKey,
567
- `discovers root atoms: [ ${rootKeys.map((key) => `"${key}"`).join(`, `)} ]`
568
- );
569
- for (const atomKey of rootKeys) {
570
- target.selectorAtoms = target.selectorAtoms.set({
571
- selectorKey,
572
- atomKey
573
- });
574
- }
575
- }
576
- covered.add(dependency.key);
577
- };
578
-
579
- // internal/src/selector/register-selector.ts
580
- var registerSelector = (selectorKey, covered, store) => ({
581
- get: (...params) => {
582
- const target = newest(store);
583
- let dependency;
584
- if (params.length === 2) {
585
- const [family, key] = params;
586
- switch (family.type) {
587
- case `molecule_family`:
588
- return getFromStore(family, key, store);
589
- default:
590
- if (store.config.lifespan === `ephemeral`) {
591
- dependency = findInStore(family, key, store);
592
- } else {
593
- const maybeDependency = seekInStore(family, key, store);
594
- if (maybeDependency) {
595
- dependency = maybeDependency;
596
- } else {
597
- throw new NotFoundError(family, key, store);
598
- }
599
- }
600
- }
601
- } else {
602
- [dependency] = params;
603
- }
604
- if (dependency.type === `molecule`) {
605
- return getFromStore(dependency, store);
606
- }
607
- const dependencyState = withdraw(dependency, store);
608
- const dependencyValue = readOrComputeValue(dependencyState, store);
609
- store.logger.info(
610
- `\u{1F50C}`,
611
- `selector`,
612
- selectorKey,
613
- `registers dependency ( "${dependency.key}" =`,
614
- dependencyValue,
615
- `)`
616
- );
617
- target.selectorGraph.set(
618
- {
619
- upstreamSelectorKey: dependency.key,
620
- downstreamSelectorKey: selectorKey
621
- },
622
- {
623
- source: dependency.key
624
- }
625
- );
626
- updateSelectorAtoms(selectorKey, dependency, covered, store);
627
- return dependencyValue;
628
- },
629
- set: (...params) => {
630
- let token;
631
- let value;
632
- if (params.length === 2) {
633
- token = params[0];
634
- value = params[1];
635
- } else {
636
- const family = params[0];
637
- const key = params[1];
638
- value = params[2];
639
- const maybeToken = store.config.lifespan === `ephemeral` ? findInStore(family, key, store) : seekInStore(family, key, store);
640
- if (!maybeToken) {
641
- throw new NotFoundError(family, key, store);
642
- }
643
- token = maybeToken;
644
- }
645
- const target = newest(store);
646
- const state = withdraw(token, target);
647
- setAtomOrSelector(state, value, target);
648
- },
649
- find: (token, key) => findInStore(token, key, store),
650
- seek: (token, key) => seekInStore(token, key, store),
651
- json: (token) => getJsonToken(token, store)
652
- });
653
-
654
- // internal/src/selector/create-readonly-selector.ts
655
- var createReadonlySelector = (options, family, store) => {
656
- const target = newest(store);
657
- const subject = new Subject();
658
- const covered = /* @__PURE__ */ new Set();
659
- const { get, find, seek, json } = registerSelector(
660
- options.key,
661
- covered,
662
- target
663
- );
664
- const getSelf = () => {
665
- const value = options.get({ get, find, seek, json });
666
- cacheValue(options.key, value, subject, newest(store));
667
- covered.clear();
668
- return value;
669
- };
670
- const readonlySelector = {
671
- ...options,
672
- subject,
673
- install: (s) => createReadonlySelector(options, family, s),
674
- get: getSelf,
675
- type: `readonly_selector`,
676
- ...family && { family }
677
- };
678
- target.readonlySelectors.set(options.key, readonlySelector);
679
- const initialValue = getSelf();
680
- store.logger.info(
681
- `\u2728`,
682
- readonlySelector.type,
683
- readonlySelector.key,
684
- `=`,
685
- initialValue
686
- );
687
- const token = {
688
- key: options.key,
689
- type: `readonly_selector`
690
- };
691
- if (family) {
692
- token.family = family;
693
- }
694
- return token;
695
- };
696
-
697
- // internal/src/selector/create-writable-selector.ts
698
- var createWritableSelector = (options, family, store) => {
699
- const target = newest(store);
700
- const subject = new Subject();
701
- const covered = /* @__PURE__ */ new Set();
702
- const toolkit = registerSelector(options.key, covered, target);
703
- const { find, get, seek, json } = toolkit;
704
- const getterToolkit = { find, get, seek, json };
705
- const getSelf = (innerTarget = newest(store)) => {
706
- const value = options.get(getterToolkit);
707
- cacheValue(options.key, value, subject, innerTarget);
708
- covered.clear();
709
- return value;
710
- };
711
- const setSelf = (next) => {
712
- const innerTarget = newest(store);
713
- const oldValue = getSelf(innerTarget);
714
- const newValue = become(next)(oldValue);
715
- store.logger.info(
716
- `\u{1F4DD}`,
717
- `selector`,
718
- options.key,
719
- `set (`,
720
- oldValue,
721
- `->`,
722
- newValue,
723
- `)`
724
- );
725
- cacheValue(options.key, newValue, subject, innerTarget);
726
- markDone(options.key, innerTarget);
727
- if (isRootStore(innerTarget)) {
728
- subject.next({ newValue, oldValue });
729
- }
730
- options.set(toolkit, newValue);
731
- };
732
- const mySelector = {
733
- ...options,
734
- subject,
735
- install: (s) => createWritableSelector(options, family, s),
736
- get: getSelf,
737
- set: setSelf,
738
- type: `selector`,
739
- ...family && { family }
740
- };
741
- target.selectors.set(options.key, mySelector);
742
- const initialValue = getSelf();
743
- store.logger.info(`\u2728`, mySelector.type, mySelector.key, `=`, initialValue);
744
- const token = {
745
- key: options.key,
746
- type: `selector`
747
- };
748
- if (family) {
749
- token.family = family;
750
- }
751
- return token;
752
- };
753
-
754
- // internal/src/selector/create-standalone-selector.ts
755
- function createStandaloneSelector(options, store) {
756
- const isWritable = `set` in options;
757
- if (isWritable) {
758
- const state2 = createWritableSelector(options, void 0, store);
759
- store.on.selectorCreation.next(state2);
760
- return state2;
761
- }
762
- const state = createReadonlySelector(options, void 0, store);
763
- store.on.selectorCreation.next(state);
764
- return state;
765
- }
766
-
767
- // internal/src/selector/dispose-selector.ts
768
- function disposeSelector(selectorToken, store) {
769
- const target = newest(store);
770
- const { key } = selectorToken;
771
- const selector = target.selectors.get(key) ?? target.readonlySelectors.get(key);
772
- if (!selector) {
773
- store.logger.info(
774
- `\u274C`,
775
- `selector`,
776
- key,
777
- `Tried to dispose selector, but it does not exist in the store.`
778
- );
779
- } else if (!selector.family) {
780
- store.logger.error(
781
- `\u274C`,
782
- `selector`,
783
- key,
784
- `Standalone selectors cannot be disposed.`
785
- );
786
- } else {
787
- const molecule = target.molecules.get(selector.family.subKey);
788
- if (molecule) {
789
- molecule.tokens.delete(key);
790
- }
791
- switch (selectorToken.type) {
792
- case `selector`:
793
- {
794
- target.selectors.delete(key);
795
- const family = withdraw(
796
- { key: selector.family.key, type: `selector_family` },
797
- store
798
- );
799
- family.subject.next({
800
- type: `state_disposal`,
801
- token: selectorToken
802
- });
803
- }
804
- break;
805
- case `readonly_selector`:
806
- {
807
- target.readonlySelectors.delete(key);
808
- const family = withdraw(
809
- { key: selector.family.key, type: `readonly_selector_family` },
810
- store
811
- );
812
- family.subject.next({
813
- type: `state_disposal`,
814
- token: selectorToken
815
- });
816
- }
817
- break;
818
- }
819
- target.valueMap.delete(key);
820
- target.selectorAtoms.delete(key);
821
- const downstreamTokens = target.selectorGraph.getRelationEntries({ upstreamSelectorKey: key }).filter(([_, { source }]) => source === key).map(
822
- ([downstreamSelectorKey]) => target.selectors.get(downstreamSelectorKey) ?? target.readonlySelectors.get(downstreamSelectorKey)
823
- );
824
- for (const downstreamToken of downstreamTokens) {
825
- if (downstreamToken) {
826
- disposeSelector(downstreamToken, store);
827
- }
828
- }
829
- target.selectorGraph.delete(key);
830
- store.logger.info(`\u{1F525}`, selectorToken.type, key, `deleted`);
831
- if (isChildStore(target) && target.transactionMeta.phase === `building`) {
832
- target.transactionMeta.update.updates.push({
833
- type: `state_disposal`,
834
- token: selectorToken
835
- });
836
- } else {
837
- store.on.selectorDisposal.next(selectorToken);
838
- }
839
- }
840
- }
841
-
842
- // internal/src/families/create-readonly-selector-family.ts
843
- function createReadonlySelectorFamily(options, store) {
844
- const subject = new Subject();
845
- const readonlySelectorFamily = Object.assign(
846
- (key) => {
847
- const subKey = stringifyJson(key);
848
- const family = { key: options.key, subKey };
849
- const fullKey = `${options.key}(${subKey})`;
850
- const target = newest(store);
851
- const token = createReadonlySelector(
852
- {
853
- key: fullKey,
854
- get: options.get(key)
855
- },
856
- family,
857
- target
858
- );
859
- subject.next({ type: `state_creation`, token });
860
- return token;
861
- },
862
- {
863
- key: options.key,
864
- type: `readonly_selector_family`,
865
- subject,
866
- install: (s) => createReadonlySelectorFamily(options, s)
867
- }
868
- );
869
- store.families.set(options.key, readonlySelectorFamily);
870
- return readonlySelectorFamily;
871
- }
872
- function createWritableSelectorFamily(options, store) {
873
- const subject = new Subject();
874
- const selectorFamily = Object.assign(
875
- (key) => {
876
- const subKey = stringifyJson(key);
877
- const family = { key: options.key, subKey };
878
- const fullKey = `${options.key}(${subKey})`;
879
- const target = newest(store);
880
- const token = createWritableSelector(
881
- {
882
- key: fullKey,
883
- get: options.get(key),
884
- set: options.set(key)
885
- },
886
- family,
887
- target
888
- );
889
- subject.next({ type: `state_creation`, token });
890
- return token;
891
- },
892
- {
893
- key: options.key,
894
- type: `selector_family`,
895
- subject,
896
- install: (s) => createWritableSelectorFamily(options, s)
897
- }
898
- );
899
- store.families.set(options.key, selectorFamily);
900
- return selectorFamily;
901
- }
902
-
903
- // internal/src/families/create-selector-family.ts
904
- function createSelectorFamily(options, store) {
905
- const isWritable = `set` in options;
906
- if (isWritable) {
907
- return createWritableSelectorFamily(options, store);
908
- }
909
- return createReadonlySelectorFamily(options, store);
910
- }
911
-
912
- // internal/src/molecule/dispose-molecule.ts
913
- function disposeMolecule(token, store) {
914
- let molecule;
915
- try {
916
- molecule = withdraw(token, store);
917
- } catch (thrown) {
918
- if (thrown instanceof Error) {
919
- store.logger.error(
920
- `\u{1F41E}`,
921
- `molecule`,
922
- JSON.stringify(token.key),
923
- `Failed to dispose molecule, because it was not found in the store.`,
924
- thrown.message
925
- );
926
- }
927
- return;
928
- }
929
- const { family } = token;
930
- const context = [];
931
- for (const above of molecule.above.values()) {
932
- context.push(deposit(above));
933
- }
934
- const values = [];
935
- for (const stateToken of molecule.tokens.values()) {
936
- const tokenFamily = stateToken.family;
937
- values.push([tokenFamily.key, store.valueMap.get(stateToken.key)]);
938
- }
939
- if (family) {
940
- const Formula = withdraw(family, store);
941
- const disposalEvent = {
942
- type: `molecule_disposal`,
943
- token,
944
- family,
945
- context,
946
- values
947
- };
948
- if (token.family) {
949
- disposalEvent.family = token.family;
950
- }
951
- for (const state of molecule.tokens.values()) {
952
- disposeFromStore(state, store);
953
- }
954
- for (const child of molecule.below.values()) {
955
- if (child.family?.dependsOn === `all`) {
956
- disposeMolecule(child, store);
957
- } else {
958
- child.above.delete(molecule.stringKey);
959
- if (child.above.size === 0) {
960
- disposeMolecule(child, store);
961
- }
962
- }
963
- }
964
- molecule.below.clear();
965
- const isTransaction = isChildStore(store) && store.transactionMeta.phase === `building`;
966
- if (isTransaction) {
967
- store.transactionMeta.update.updates.push(disposalEvent);
968
- } else {
969
- Formula.subject.next(disposalEvent);
970
- }
971
- store.molecules.delete(molecule.stringKey);
972
- }
973
- for (const join of molecule.joins.values()) {
974
- join.molecules.delete(molecule.stringKey);
975
- }
976
- for (const parent of molecule.above.values()) {
977
- parent.below.delete(molecule.stringKey);
978
- }
979
- }
980
-
981
- // internal/src/families/init-family-member.ts
982
- function initFamilyMemberInStore(token, key, store) {
983
- const familyKey = token.key;
984
- const family = store.families.get(familyKey);
985
- if (family === void 0) {
986
- throw new NotFoundError(token, store);
987
- }
988
- const state = family(key);
989
- const target = newest(store);
990
- if (state.family && target.moleculeInProgress === null) {
991
- if (isRootStore(target)) {
992
- switch (state.type) {
993
- case `atom`:
994
- case `mutable_atom`:
995
- store.on.atomCreation.next(state);
996
- break;
997
- case `selector`:
998
- case `readonly_selector`:
999
- store.on.selectorCreation.next(state);
1000
- break;
1001
- }
1002
- } else if (isChildStore(target) && target.on.transactionApplying.state === null) {
1003
- target.transactionMeta.update.updates.push({
1004
- type: `state_creation`,
1005
- token: state
1006
- });
1007
- }
1008
- }
1009
- return state;
1010
- }
1011
- function seekInStore(token, key, store) {
1012
- const subKey = stringifyJson(key);
1013
- const fullKey = `${token.key}(${subKey})`;
1014
- const target = newest(store);
1015
- let state;
1016
- switch (token.type) {
1017
- case `atom_family`:
1018
- case `mutable_atom_family`:
1019
- state = target.atoms.get(fullKey);
1020
- break;
1021
- case `selector_family`:
1022
- state = target.selectors.get(fullKey);
1023
- break;
1024
- case `readonly_selector_family`:
1025
- state = target.readonlySelectors.get(fullKey);
1026
- break;
1027
- case `molecule_family`:
1028
- state = target.molecules.get(stringifyJson(key));
1029
- if (state) {
1030
- return deposit(state);
1031
- }
1032
- }
1033
- if (state) {
1034
- return deposit(state);
1035
- }
1036
- return state;
1037
- }
1038
-
1039
- // internal/src/families/find-in-store.ts
1040
- function findInStore(token, key, store) {
1041
- if (store.config.lifespan === `immortal`) {
1042
- throw new Error(
1043
- `Do not use \`find\` or \`findState\` in an immortal store. Prefer \`seek\` or \`seekState\`.`
1044
- );
1045
- }
1046
- let state = seekInStore(token, key, store);
1047
- if (state) {
1048
- return state;
1049
- }
1050
- state = initFamilyMemberInStore(token, key, store);
1051
- return state;
1052
- }
1053
-
1054
- // internal/src/families/dispose-from-store.ts
1055
- function disposeFromStore(...params) {
1056
- let token;
1057
- let store;
1058
- if (params.length === 2) {
1059
- token = params[0];
1060
- store = params[1];
1061
- } else {
1062
- const family = params[0];
1063
- const key = params[1];
1064
- store = params[2];
1065
- const maybeToken = family.type === `molecule_family` ? seekInStore(family, key, store) : store.config.lifespan === `immortal` ? seekInStore(family, key, store) : findInStore(family, key, store);
1066
- if (!maybeToken) {
1067
- throw new NotFoundError(family, key, store);
1068
- }
1069
- token = maybeToken;
1070
- }
1071
- switch (token.type) {
1072
- case `atom`:
1073
- case `mutable_atom`:
1074
- disposeAtom(token, store);
1075
- break;
1076
- case `selector`:
1077
- case `readonly_selector`:
1078
- disposeSelector(token, store);
1079
- break;
1080
- case `molecule`:
1081
- disposeMolecule(token, store);
1082
- break;
1083
- }
1084
- }
1085
-
1086
- // internal/src/set-state/set-into-store.ts
1087
- function setIntoStore(...params) {
1088
- let token;
1089
- let value;
1090
- let store;
1091
- if (params.length === 3) {
1092
- token = params[0];
1093
- value = params[1];
1094
- store = params[2];
1095
- } else {
1096
- const family = params[0];
1097
- const key = params[1];
1098
- value = params[2];
1099
- store = params[3];
1100
- const maybeToken = store.config.lifespan === `ephemeral` ? findInStore(family, key, store) : seekInStore(family, key, store);
1101
- if (!maybeToken) {
1102
- throw new NotFoundError(family, key, store);
1103
- }
1104
- token = maybeToken;
1105
- }
1106
- const rejectionTime = openOperation(token, store);
1107
- if (rejectionTime) {
1108
- const unsubscribe = store.on.operationClose.subscribe(
1109
- `waiting to set "${token.key}" at T-${rejectionTime}`,
1110
- () => {
1111
- unsubscribe();
1112
- store.logger.info(
1113
- `\u{1F7E2}`,
1114
- token.type,
1115
- token.key,
1116
- `resuming deferred setState from T-${rejectionTime}`
1117
- );
1118
- setIntoStore(token, value, store);
1119
- }
1120
- );
1121
- return;
1122
- }
1123
- const state = withdraw(token, store);
1124
- setAtomOrSelector(state, value, store);
1125
- closeOperation(store);
1126
- }
1127
-
1128
- // internal/src/ingest-updates/ingest-atom-update.ts
1129
- function ingestAtomUpdate(applying, atomUpdate, store) {
1130
- const { key, newValue, oldValue } = atomUpdate;
1131
- const value = applying === `newValue` ? newValue : oldValue;
1132
- const token = { key, type: `atom` };
1133
- if (atomUpdate.family) {
1134
- Object.assign(token, { family: atomUpdate.family });
1135
- }
1136
- setIntoStore(token, value, store);
1137
- }
1138
-
1139
- // internal/src/molecule/create-molecule-family.ts
1140
- function createMoleculeFamily(options, store) {
1141
- const subject = new Subject();
1142
- const token = {
1143
- type: `molecule_family`,
1144
- key: options.key,
1145
- dependsOn: options.dependsOn ?? `all`
1146
- };
1147
- const family = {
1148
- ...token,
1149
- subject,
1150
- new: options.new
1151
- };
1152
- store.moleculeFamilies.set(options.key, family);
1153
- return token;
1154
- }
1155
-
1156
- // internal/src/molecule/grow-molecule-in-store.ts
1157
- function growMoleculeInStore(molecule, family, store) {
1158
- const stateToken = initFamilyMemberInStore(family, molecule.key, store);
1159
- molecule.tokens.set(stateToken.key, stateToken);
1160
- const isTransaction = isChildStore(store) && store.transactionMeta.phase === `building`;
1161
- const moleculeInProgress = store.moleculeInProgress === molecule.key;
1162
- if (!isTransaction && !moleculeInProgress) {
1163
- molecule.subject.next({ type: `state_creation`, token: stateToken });
1164
- }
1165
- return stateToken;
1166
- }
1167
-
1168
- // internal/src/get-environment-data.ts
1169
- function getEnvironmentData(store) {
1170
- return {
1171
- store
1172
- };
1173
- }
1174
- var Molecule = class {
1175
- constructor(ctx, key, family) {
1176
- this.key = key;
1177
- this.stringKey = stringifyJson(key);
1178
- if (family) {
1179
- this.family = family;
1180
- }
1181
- if (ctx) {
1182
- if (Array.isArray(ctx)) {
1183
- for (const molecule of ctx) {
1184
- this.above.set(molecule.stringKey, molecule);
1185
- }
1186
- } else {
1187
- this.above.set(ctx.stringKey, ctx);
1188
- }
1189
- }
1190
- }
1191
- type = `molecule`;
1192
- stringKey;
1193
- family;
1194
- subject = new Subject();
1195
- tokens = /* @__PURE__ */ new Map();
1196
- above = /* @__PURE__ */ new Map();
1197
- below = /* @__PURE__ */ new Map();
1198
- joins = /* @__PURE__ */ new Map();
1199
- instance;
1200
- };
1201
-
1202
- // internal/src/molecule/make-molecule-in-store.ts
1203
- function capitalize2(string) {
1204
- return string[0].toUpperCase() + string.slice(1);
1205
- }
1206
- function makeMoleculeInStore(store, context, familyToken, key, ...params) {
1207
- const rootStore = eldest(store);
1208
- const target = newest(store);
1209
- const stringKey = stringifyJson(key);
1210
- target.moleculeInProgress = stringKey;
1211
- const contextArray = Array.isArray(context) ? context : [context];
1212
- const owners = contextArray.map((ctx) => {
1213
- if (ctx instanceof Molecule) {
1214
- return ctx;
1215
- }
1216
- const ctxStringKey = stringifyJson(ctx.key);
1217
- const molecule2 = store.molecules.get(ctxStringKey);
1218
- if (!molecule2) {
1219
- throw new Error(
1220
- `Molecule ${ctxStringKey} not found in store "${store.config.name}"`
1221
- );
1222
- }
1223
- return molecule2;
1224
- });
1225
- const molecule = new Molecule(owners, key, familyToken);
1226
- target.molecules.set(stringKey, molecule);
1227
- for (const owner of owners) {
1228
- owner.below.set(molecule.stringKey, molecule);
1229
- }
1230
- const toolkit = {
1231
- get: (...ps) => getFromStore(...ps, newest(rootStore)),
1232
- set: (...ps) => {
1233
- setIntoStore(...ps, newest(rootStore));
1234
- },
1235
- seek: (t, k) => seekInStore(t, k, newest(rootStore)),
1236
- json: (t) => getJsonToken(t, newest(rootStore)),
1237
- run: (t, i = arbitrary()) => actUponStore(t, i, newest(store)),
1238
- make: (ctx, f, k, ...args) => makeMoleculeInStore(newest(rootStore), ctx, f, k, ...args),
1239
- dispose: (t) => {
1240
- disposeFromStore(t, newest(rootStore));
1241
- },
1242
- env: () => getEnvironmentData(newest(rootStore)),
1243
- bond: (token2, maybeRole) => {
1244
- if (token2.type === `join`) {
1245
- const { as: role } = maybeRole;
1246
- const join = getJoin(token2, rootStore);
1247
- join.molecules.set(stringKey, molecule);
1248
- molecule.joins.set(token2.key, join);
1249
- const unsubFromFamily = family.subject.subscribe(
1250
- `join:${token2.key}-${stringKey}`,
1251
- (event) => {
1252
- if (event.type === `molecule_disposal` && stringifyJson(event.token.key) === stringKey) {
1253
- unsubFromFamily();
1254
- join.molecules.delete(stringKey);
1255
- }
1256
- }
1257
- );
1258
- if (role === null) {
1259
- return;
1260
- }
1261
- const otherRole = token2.a === role ? token2.b : token2.a;
1262
- const relations = findRelations(token2, key);
1263
- const relatedKeys = relations[`${otherRole}KeysOf${capitalize2(role)}`];
1264
- const relatedEntries = relations[`${otherRole}EntriesOf${capitalize2(role)}`];
1265
- let tokens = { relatedKeys };
1266
- if (relatedEntries) {
1267
- tokens = Object.assign(tokens, { relatedEntries });
1268
- }
1269
- return tokens;
1270
- }
1271
- return growMoleculeInStore(
1272
- molecule,
1273
- withdraw(token2, rootStore),
1274
- newest(rootStore)
1275
- );
1276
- },
1277
- claim: (below, options) => {
1278
- const { exclusive } = options;
1279
- const belowMolecule = newest(store).molecules.get(stringifyJson(below.key));
1280
- if (belowMolecule) {
1281
- if (exclusive) {
1282
- for (const value of belowMolecule.above.values()) {
1283
- value.below.delete(belowMolecule.stringKey);
1284
- }
1285
- belowMolecule.above.clear();
1286
- belowMolecule.above.set(molecule.stringKey, molecule);
1287
- molecule.below.set(belowMolecule.stringKey, belowMolecule);
1288
- } else {
1289
- belowMolecule.above.set(molecule.stringKey, molecule);
1290
- molecule.below.set(belowMolecule.stringKey, belowMolecule);
1291
- }
1292
- }
1293
- },
1294
- spawn: (f, k, ...p) => makeMoleculeInStore(
1295
- newest(store),
1296
- [molecule],
1297
- withdraw(f, store),
1298
- k,
1299
- ...p
1300
- )
1301
- };
1302
- const family = withdraw(familyToken, store);
1303
- const Constructor = family.new;
1304
- molecule.instance = new Constructor(toolkit, key, ...params);
1305
- const token = {
1306
- type: `molecule`,
1307
- key,
1308
- family: familyToken
1309
- };
1310
- const update = {
1311
- type: `molecule_creation`,
1312
- token,
1313
- family: familyToken,
1314
- context: contextArray,
1315
- params
1316
- };
1317
- if (isRootStore(target)) {
1318
- family.subject.next(update);
1319
- } else if (isChildStore(target) && target.on.transactionApplying.state === null) {
1320
- target.transactionMeta.update.updates.push(update);
1321
- }
1322
- target.moleculeInProgress = null;
1323
- return token;
1324
- }
1325
-
1326
- // internal/src/ingest-updates/ingest-creation-disposal.ts
1327
- function ingestCreationEvent(update, applying, store) {
1328
- switch (applying) {
1329
- case `newValue`: {
1330
- createInStore(update.token, store);
1331
- break;
1332
- }
1333
- case `oldValue`: {
1334
- disposeFromStore(update.token, store);
1335
- break;
1336
- }
1337
- }
1338
- }
1339
- function ingestDisposalEvent(update, applying, store) {
1340
- switch (applying) {
1341
- case `newValue`: {
1342
- disposeFromStore(update.token, store);
1343
- break;
1344
- }
1345
- case `oldValue`: {
1346
- createInStore(update.token, store);
1347
- store.valueMap.set(update.token.key, update.value);
1348
- break;
1349
- }
1350
- }
1351
- }
1352
- function createInStore(token, store) {
1353
- if (token.family) {
1354
- const family = store.families.get(token.family.key);
1355
- if (family) {
1356
- const molecule = store.molecules.get(token.family.subKey);
1357
- if (molecule) {
1358
- growMoleculeInStore(molecule, family, store);
1359
- return;
1360
- }
1361
- if (store.config.lifespan === `immortal`) {
1362
- throw new Error(`No molecule found for key "${token.family.subKey}"`);
1363
- }
1364
- initFamilyMemberInStore(family, parseJson(token.family.subKey), store);
1365
- }
1366
- }
1367
- }
1368
- function ingestMoleculeCreationEvent(update, applying, store) {
1369
- switch (applying) {
1370
- case `newValue`:
1371
- makeMoleculeInStore(
1372
- store,
1373
- update.context,
1374
- update.family,
1375
- update.token.key,
1376
- ...update.params
1377
- );
1378
- break;
1379
- case `oldValue`:
1380
- disposeFromStore(update.token, store);
1381
- break;
1382
- }
1383
- }
1384
- function ingestMoleculeDisposalEvent(update, applying, store) {
1385
- switch (applying) {
1386
- case `newValue`:
1387
- disposeFromStore(update.token, store);
1388
- break;
1389
- case `oldValue`:
1390
- {
1391
- const moleculeToken = makeMoleculeInStore(
1392
- store,
1393
- update.context,
1394
- update.family,
1395
- update.token.key
1396
- );
1397
- for (const [familyKey, value] of update.values) {
1398
- const memberKey = `${familyKey}(${stringifyJson(moleculeToken.key)})`;
1399
- const molecule = withdraw(moleculeToken, store);
1400
- const alreadyCreated = molecule.tokens.has(memberKey);
1401
- const family = store.families.get(familyKey);
1402
- if (family && !alreadyCreated) {
1403
- growMoleculeInStore(molecule, family, store);
1404
- }
1405
- store.valueMap.set(memberKey, value);
1406
- }
1407
- }
1408
- break;
1409
- }
1410
- }
1411
-
1412
- // internal/src/ingest-updates/ingest-selector-update.ts
1413
- function ingestSelectorUpdate(applying, selectorUpdate, store) {
1414
- const updates = applying === `newValue` ? selectorUpdate.atomUpdates : [...selectorUpdate.atomUpdates].reverse();
1415
- for (const atomUpdate of updates) {
1416
- ingestAtomUpdate(applying, atomUpdate, store);
1417
- }
1418
- }
1419
-
1420
- // internal/src/ingest-updates/ingest-transaction-update.ts
1421
- function ingestTransactionUpdate(applying, transactionUpdate, store) {
1422
- const updates = applying === `newValue` ? transactionUpdate.updates : [...transactionUpdate.updates].reverse();
1423
- for (const updateFromTransaction of updates) {
1424
- switch (updateFromTransaction.type) {
1425
- case `atom_update`:
1426
- case `selector_update`:
1427
- ingestAtomUpdate(applying, updateFromTransaction, store);
1428
- break;
1429
- case `state_creation`:
1430
- ingestCreationEvent(updateFromTransaction, applying, store);
1431
- break;
1432
- case `state_disposal`:
1433
- ingestDisposalEvent(updateFromTransaction, applying, store);
1434
- break;
1435
- case `molecule_creation`:
1436
- ingestMoleculeCreationEvent(updateFromTransaction, applying, store);
1437
- break;
1438
- case `molecule_disposal`:
1439
- ingestMoleculeDisposalEvent(updateFromTransaction, applying, store);
1440
- break;
1441
- case `transaction_update`:
1442
- ingestTransactionUpdate(applying, updateFromTransaction, store);
1443
- break;
1444
- }
1445
- }
1446
- }
1447
-
1448
- // internal/src/transaction/set-epoch-number.ts
1449
- function setEpochNumberOfContinuity(continuityKey, newEpoch, store) {
1450
- const isRoot = isRootStore(store);
1451
- if (isRoot && continuityKey) {
1452
- store.transactionMeta.epoch.set(continuityKey, newEpoch);
1453
- }
1454
- }
1455
- function setEpochNumberOfAction(transactionKey, newEpoch, store) {
1456
- const isRoot = isRootStore(store);
1457
- if (!isRoot) {
1458
- return;
1459
- }
1460
- const continuityKey = store.transactionMeta.actionContinuities.getRelatedKey(transactionKey);
1461
- if (continuityKey !== void 0) {
1462
- store.transactionMeta.epoch.set(continuityKey, newEpoch);
1463
- }
1464
- }
1465
-
1466
- // internal/src/transaction/apply-transaction.ts
1467
- var applyTransaction = (output, store) => {
1468
- const child = newest(store);
1469
- const { parent } = child;
1470
- if (parent === null || !isChildStore(child) || child.transactionMeta?.phase !== `building`) {
1471
- store.logger.warn(
1472
- `\u{1F41E}`,
1473
- `transaction`,
1474
- `???`,
1475
- `applyTransaction called outside of a transaction. This is probably a bug in AtomIO.`
1476
- );
1477
- return;
1478
- }
1479
- child.transactionMeta.phase = `applying`;
1480
- child.transactionMeta.update.output = output;
1481
- parent.child = null;
1482
- parent.on.transactionApplying.next(child.transactionMeta);
1483
- const { updates } = child.transactionMeta.update;
1484
- store.logger.info(
1485
- `\u{1F6C4}`,
1486
- `transaction`,
1487
- child.transactionMeta.update.key,
1488
- `Applying transaction with ${updates.length} updates:`,
1489
- updates
1490
- );
1491
- ingestTransactionUpdate(`newValue`, child.transactionMeta.update, parent);
1492
- if (isRootStore(parent)) {
1493
- setEpochNumberOfAction(
1494
- child.transactionMeta.update.key,
1495
- child.transactionMeta.update.epoch,
1496
- parent
1497
- );
1498
- const myTransaction = withdraw(
1499
- { key: child.transactionMeta.update.key, type: `transaction` },
1500
- store
1501
- );
1502
- myTransaction?.subject.next(child.transactionMeta.update);
1503
- store.logger.info(
1504
- `\u{1F6EC}`,
1505
- `transaction`,
1506
- child.transactionMeta.update.key,
1507
- `Finished applying transaction.`
1508
- );
1509
- } else if (isChildStore(parent)) {
1510
- parent.transactionMeta.update.updates.push(child.transactionMeta.update);
1511
- }
1512
- parent.on.transactionApplying.next(null);
1513
- };
1514
-
1515
- // internal/src/transaction/assign-transaction-to-continuity.ts
1516
- function assignTransactionToContinuity(continuityKey, transactionKey, store) {
1517
- const isRoot = isRootStore(store);
1518
- if (!isRoot) {
1519
- return;
1520
- }
1521
- const { epoch, actionContinuities } = store.transactionMeta;
1522
- actionContinuities.set(continuityKey, transactionKey);
1523
- if (!epoch.has(continuityKey)) {
1524
- epoch.set(continuityKey, -1);
1525
- }
1526
- }
1527
-
1528
- // internal/src/lazy-map.ts
1529
- var LazyMap = class extends Map {
1530
- constructor(source) {
1531
- super();
1532
- this.source = source;
1533
- }
1534
- deleted = /* @__PURE__ */ new Set();
1535
- get(key) {
1536
- const has = super.has(key);
1537
- if (has) {
1538
- return super.get(key);
1539
- }
1540
- if (!this.deleted.has(key) && this.source.has(key)) {
1541
- const value = this.source.get(key);
1542
- return value;
1543
- }
1544
- return void 0;
1545
- }
1546
- set(key, value) {
1547
- this.deleted.delete(key);
1548
- return super.set(key, value);
1549
- }
1550
- hasOwn(key) {
1551
- return super.has(key);
1552
- }
1553
- has(key) {
1554
- return !this.deleted.has(key) && (super.has(key) || this.source.has(key));
1555
- }
1556
- delete(key) {
1557
- this.deleted.add(key);
1558
- return super.delete(key);
1559
- }
1560
- };
1561
-
1562
- // internal/src/transaction/build-transaction.ts
1563
- var buildTransaction = (key, params, store, id) => {
1564
- const parent = newest(store);
1565
- const childBase = {
1566
- parent,
1567
- child: null,
1568
- on: parent.on,
1569
- loggers: parent.loggers,
1570
- logger: parent.logger,
1571
- config: parent.config,
1572
- atoms: new LazyMap(parent.atoms),
1573
- atomsThatAreDefault: new Set(parent.atomsThatAreDefault),
1574
- families: new LazyMap(parent.families),
1575
- operation: { open: false },
1576
- readonlySelectors: new LazyMap(parent.readonlySelectors),
1577
- timelines: new LazyMap(parent.timelines),
1578
- timelineTopics: new Junction(parent.timelineTopics.toJSON()),
1579
- trackers: /* @__PURE__ */ new Map(),
1580
- transactions: new LazyMap(parent.transactions),
1581
- selectorAtoms: new Junction(parent.selectorAtoms.toJSON()),
1582
- selectorGraph: new Junction(parent.selectorGraph.toJSON(), {
1583
- makeContentKey: (...keys) => keys.sort().join(`:`)
1584
- }),
1585
- selectors: new LazyMap(parent.selectors),
1586
- valueMap: new LazyMap(parent.valueMap),
1587
- molecules: new LazyMap(parent.molecules),
1588
- moleculeFamilies: new LazyMap(parent.moleculeFamilies),
1589
- moleculeInProgress: parent.moleculeInProgress,
1590
- miscResources: new LazyMap(parent.miscResources)
1591
- };
1592
- const epoch = getEpochNumberOfAction(key, store);
1593
- const transactionMeta = {
1594
- phase: `building`,
1595
- update: {
1596
- type: `transaction_update`,
1597
- key,
1598
- id,
1599
- epoch: epoch === void 0 ? Number.NaN : epoch + 1,
1600
- updates: [],
1601
- params,
1602
- output: void 0
1603
- },
1604
- toolkit: {
1605
- get: (...ps) => getFromStore(...ps, child),
1606
- set: (...ps) => {
1607
- setIntoStore(...ps, child);
1608
- },
1609
- run: (token, identifier = arbitrary()) => actUponStore(token, identifier, child),
1610
- find: (token, k) => findInStore(token, k, child),
1611
- seek: (token, k) => seekInStore(token, k, child),
1612
- json: (token) => getJsonToken(token, child),
1613
- make: (context, family, k, ...args) => makeMoleculeInStore(child, context, family, k, ...args),
1614
- dispose: (...ps) => {
1615
- disposeFromStore(...ps, child);
1616
- },
1617
- env: () => getEnvironmentData(child)
1618
- }
1619
- };
1620
- const child = Object.assign(childBase, {
1621
- transactionMeta
1622
- });
1623
- parent.child = child;
1624
- store.logger.info(
1625
- `\u{1F6EB}`,
1626
- `transaction`,
1627
- key,
1628
- `Building transaction with params:`,
1629
- params
1630
- );
1631
- return child;
1632
- };
1633
-
1634
- // internal/src/transaction/create-transaction.ts
1635
- function createTransaction(options, store) {
1636
- const newTransaction = {
1637
- key: options.key,
1638
- type: `transaction`,
1639
- run: (params, id) => {
1640
- const childStore = buildTransaction(options.key, params, store, id);
1641
- try {
1642
- const target2 = newest(store);
1643
- const { toolkit } = childStore.transactionMeta;
1644
- const output = options.do(toolkit, ...params);
1645
- applyTransaction(output, target2);
1646
- return output;
1647
- } catch (thrown) {
1648
- abortTransaction(target);
1649
- store.logger.warn(`\u{1F4A5}`, `transaction`, options.key, `caught:`, thrown);
1650
- throw thrown;
1651
- }
1652
- },
1653
- install: (s) => createTransaction(options, s),
1654
- subject: new Subject()
1655
- };
1656
- const target = newest(store);
1657
- target.transactions.set(newTransaction.key, newTransaction);
1658
- const token = deposit(newTransaction);
1659
- store.on.transactionCreation.next(token);
1660
- return token;
1661
- }
1662
-
1663
- // internal/src/transaction/get-epoch-number.ts
1664
- function getContinuityKey(transactionKey, store) {
1665
- const isRoot = isRootStore(store);
1666
- const continuity = isRoot ? store.transactionMeta.actionContinuities.getRelatedKey(transactionKey) : void 0;
1667
- return continuity;
1668
- }
1669
- function getEpochNumberOfContinuity(continuityKey, store) {
1670
- const isRoot = isRootStore(store);
1671
- const epoch = isRoot && continuityKey ? store.transactionMeta.epoch.get(continuityKey) : void 0;
1672
- return epoch;
1673
- }
1674
- function getEpochNumberOfAction(transactionKey, store) {
1675
- const isRoot = isRootStore(store);
1676
- const continuity = isRoot ? store.transactionMeta.actionContinuities.getRelatedKey(transactionKey) : void 0;
1677
- const epoch = isRoot && continuity !== void 0 ? store.transactionMeta.epoch.get(continuity) : void 0;
1678
- return epoch;
1679
- }
1680
-
1681
- // internal/src/transaction/index.ts
1682
- var TRANSACTION_PHASES = [`idle`, `building`, `applying`];
1683
-
1684
- // internal/src/store/store.ts
1685
- var Store = class {
1686
- parent = null;
1687
- child = null;
1688
- valueMap = /* @__PURE__ */ new Map();
1689
- atoms = /* @__PURE__ */ new Map();
1690
- selectors = /* @__PURE__ */ new Map();
1691
- readonlySelectors = /* @__PURE__ */ new Map();
1692
- atomsThatAreDefault = /* @__PURE__ */ new Set();
1693
- selectorAtoms = new Junction({
1694
- between: [`selectorKey`, `atomKey`],
1695
- cardinality: `n:n`
1696
- });
1697
- selectorGraph = new Junction(
1698
- {
1699
- between: [`upstreamSelectorKey`, `downstreamSelectorKey`],
1700
- cardinality: `n:n`
1701
- },
1702
- {
1703
- makeContentKey: (...keys) => keys.sort().join(`:`)
1704
- }
1705
- );
1706
- trackers = /* @__PURE__ */ new Map();
1707
- families = /* @__PURE__ */ new Map();
1708
- transactions = /* @__PURE__ */ new Map();
1709
- transactionMeta = {
1710
- epoch: /* @__PURE__ */ new Map(),
1711
- actionContinuities: new Junction({
1712
- between: [`continuity`, `action`],
1713
- cardinality: `1:n`
1714
- })
1715
- };
1716
- timelines = /* @__PURE__ */ new Map();
1717
- timelineTopics = new Junction({
1718
- between: [`timelineKey`, `topicKey`],
1719
- cardinality: `1:n`
1720
- });
1721
- molecules = /* @__PURE__ */ new Map();
1722
- moleculeFamilies = /* @__PURE__ */ new Map();
1723
- moleculeInProgress = null;
1724
- miscResources = /* @__PURE__ */ new Map();
1725
- on = {
1726
- atomCreation: new Subject(),
1727
- atomDisposal: new Subject(),
1728
- selectorCreation: new Subject(),
1729
- selectorDisposal: new Subject(),
1730
- timelineCreation: new Subject(),
1731
- transactionCreation: new Subject(),
1732
- transactionApplying: new StatefulSubject(
1733
- null
1734
- ),
1735
- operationClose: new Subject(),
1736
- moleculeCreationStart: new Subject(),
1737
- moleculeCreationDone: new Subject(),
1738
- moleculeDisposal: new Subject()
1739
- };
1740
- operation = { open: false };
1741
- config = {
1742
- name: `IMPLICIT_STORE`,
1743
- lifespan: `ephemeral`
1744
- };
1745
- loggers = [
1746
- new AtomIOLogger(`warn`, (_, __, key) => !key.includes(`\u{1F441}\u200D\u{1F5E8}`))
1747
- ];
1748
- logger = {
1749
- error: (...messages) => {
1750
- for (const logger of this.loggers) logger.error(...messages);
1751
- },
1752
- info: (...messages) => {
1753
- for (const logger of this.loggers) logger.info(...messages);
1754
- },
1755
- warn: (...messages) => {
1756
- for (const logger of this.loggers) logger.warn(...messages);
1757
- }
1758
- };
1759
- constructor(config, store = null) {
1760
- this.config = {
1761
- ...store?.config,
1762
- ...config
1763
- };
1764
- if (store !== null) {
1765
- this.valueMap = new Map(store?.valueMap);
1766
- this.operation = { ...store?.operation };
1767
- if (isRootStore(store)) {
1768
- this.transactionMeta = {
1769
- epoch: new Map(store?.transactionMeta.epoch),
1770
- actionContinuities: new Junction(
1771
- store?.transactionMeta.actionContinuities.toJSON()
1772
- )
1773
- };
1774
- }
1775
- for (const [, family] of store.families) {
1776
- family.install(this);
1777
- }
1778
- const mutableHelpers = /* @__PURE__ */ new Set();
1779
- for (const [, atom] of store.atoms) {
1780
- if (mutableHelpers.has(atom.key)) {
1781
- continue;
1782
- }
1783
- atom.install(this);
1784
- if (atom.type === `mutable_atom`) {
1785
- const originalJsonToken = getJsonToken(atom, store);
1786
- const originalUpdateToken = getUpdateToken(atom);
1787
- mutableHelpers.add(originalJsonToken.key);
1788
- mutableHelpers.add(originalUpdateToken.key);
1789
- }
1790
- }
1791
- for (const [, selector] of store.readonlySelectors) {
1792
- selector.install(this);
1793
- }
1794
- for (const [, selector] of store.selectors) {
1795
- if (mutableHelpers.has(selector.key)) {
1796
- continue;
1797
- }
1798
- selector.install(this);
1799
- }
1800
- for (const [, tx] of store.transactions) {
1801
- tx.install(this);
1802
- }
1803
- for (const [, timeline] of store.timelines) {
1804
- timeline.install(this);
1805
- }
1806
- }
1807
- }
1808
- };
1809
- var IMPLICIT = {
1810
- get STORE() {
1811
- if (!globalThis.ATOM_IO_IMPLICIT_STORE) {
1812
- globalThis.ATOM_IO_IMPLICIT_STORE = new Store({
1813
- name: `IMPLICIT_STORE`,
1814
- lifespan: `ephemeral`
1815
- });
1816
- }
1817
- return globalThis.ATOM_IO_IMPLICIT_STORE;
1818
- }
1819
- };
1820
- var clearStore = (store) => {
1821
- const { config } = store;
1822
- for (const disposable of store.miscResources.values()) {
1823
- disposable[Symbol.dispose]();
1824
- }
1825
- Object.assign(store, new Store(config));
1826
- store.config = config;
1827
- };
1828
- function withdraw(token, store) {
1829
- let withdrawn;
1830
- let target = store;
1831
- while (target !== null) {
1832
- switch (token.type) {
1833
- case `atom`:
1834
- case `mutable_atom`:
1835
- withdrawn = target.atoms.get(token.key);
1836
- break;
1837
- case `selector`:
1838
- withdrawn = target.selectors.get(token.key);
1839
- break;
1840
- case `readonly_selector`:
1841
- withdrawn = target.readonlySelectors.get(token.key);
1842
- break;
1843
- case `atom_family`:
1844
- case `mutable_atom_family`:
1845
- case `selector_family`:
1846
- case `readonly_selector_family`:
1847
- withdrawn = target.families.get(token.key);
1848
- break;
1849
- case `timeline`:
1850
- withdrawn = target.timelines.get(token.key);
1851
- break;
1852
- case `transaction`:
1853
- withdrawn = target.transactions.get(token.key);
1854
- break;
1855
- case `molecule`:
1856
- withdrawn = target.molecules.get(stringifyJson(token.key));
1857
- break;
1858
- case `molecule_family`:
1859
- withdrawn = target.moleculeFamilies.get(token.key);
1860
- break;
1861
- }
1862
- if (withdrawn) {
1863
- return withdrawn;
1864
- }
1865
- target = target.child;
1866
- }
1867
- throw new NotFoundError(token, store);
1868
- }
1869
-
1870
- // internal/src/subscribe/recall-state.ts
1871
- var recallState = (state, store) => {
1872
- const target = newest(store);
1873
- if (target.operation.open) {
1874
- return target.operation.prev.get(state.key);
1875
- }
1876
- return target.valueMap.get(state.key);
1877
- };
1878
-
1879
- // internal/src/subscribe/subscribe-to-root-atoms.ts
1880
- var subscribeToRootAtoms = (selector, store) => {
1881
- const target = newest(store);
1882
- const dependencySubscriptions = traceAllSelectorAtoms(selector, store).map(
1883
- (atomKey) => {
1884
- const atom = target.atoms.get(atomKey);
1885
- if (atom === void 0) {
1886
- throw new Error(
1887
- `Atom "${atomKey}", a dependency of selector "${selector.key}", not found in store "${store.config.name}".`
1888
- );
1889
- }
1890
- return atom.subject.subscribe(
1891
- `${selector.type}:${selector.key}`,
1892
- (atomChange) => {
1893
- store.logger.info(
1894
- `\u{1F4E2}`,
1895
- selector.type,
1896
- selector.key,
1897
- `root`,
1898
- atomKey,
1899
- `went`,
1900
- atomChange.oldValue,
1901
- `->`,
1902
- atomChange.newValue
1903
- );
1904
- const oldValue = recallState(selector, target);
1905
- const newValue = readOrComputeValue(selector, target);
1906
- store.logger.info(
1907
- `\u2728`,
1908
- selector.type,
1909
- selector.key,
1910
- `went`,
1911
- oldValue,
1912
- `->`,
1913
- newValue
1914
- );
1915
- selector.subject.next({ newValue, oldValue });
1916
- }
1917
- );
1918
- }
1919
- );
1920
- return dependencySubscriptions;
1921
- };
1922
-
1923
- // internal/src/subscribe/subscribe-to-state.ts
1924
- function subscribeToState(token, handleUpdate, key, store) {
1925
- function safelyHandleUpdate(update) {
1926
- if (store.operation.open) {
1927
- const unsubscribe2 = store.on.operationClose.subscribe(
1928
- `state subscription ${key}`,
1929
- () => {
1930
- unsubscribe2();
1931
- handleUpdate(update);
1932
- }
1933
- );
1934
- } else {
1935
- handleUpdate(update);
1936
- }
1937
- }
1938
- const state = withdraw(token, store);
1939
- store.logger.info(`\u{1F440}`, state.type, state.key, `Adding subscription "${key}"`);
1940
- const isSelector = state.type === `selector` || state.type === `readonly_selector`;
1941
- let dependencyUnsubFunctions = null;
1942
- let updateHandler = safelyHandleUpdate;
1943
- if (isSelector) {
1944
- dependencyUnsubFunctions = subscribeToRootAtoms(state, store);
1945
- updateHandler = (update) => {
1946
- if (dependencyUnsubFunctions) {
1947
- dependencyUnsubFunctions.length = 0;
1948
- dependencyUnsubFunctions.push(...subscribeToRootAtoms(state, store));
1949
- }
1950
- safelyHandleUpdate(update);
1951
- };
1952
- }
1953
- const mainUnsubFunction = state.subject.subscribe(key, updateHandler);
1954
- const unsubscribe = () => {
1955
- store.logger.info(
1956
- `\u{1F648}`,
1957
- state.type,
1958
- state.key,
1959
- `Removing subscription "${key}"`
1960
- );
1961
- mainUnsubFunction();
1962
- if (dependencyUnsubFunctions) {
1963
- for (const unsubFromDependency of dependencyUnsubFunctions) {
1964
- unsubFromDependency();
1965
- }
1966
- }
1967
- };
1968
- return unsubscribe;
1969
- }
1970
-
1971
- // internal/src/subscribe/subscribe-to-timeline.ts
1972
- var subscribeToTimeline = (token, handleUpdate, key, store) => {
1973
- const tl = withdraw(token, store);
1974
- store.logger.info(`\u{1F440}`, `timeline`, token.key, `Adding subscription "${key}"`);
1975
- const unsubscribe = tl.subject.subscribe(key, handleUpdate);
1976
- return () => {
1977
- store.logger.info(
1978
- `\u{1F648}`,
1979
- `timeline`,
1980
- token.key,
1981
- `Removing subscription "${key}" from timeline`
1982
- );
1983
- unsubscribe();
1984
- };
1985
- };
1986
-
1987
- // internal/src/subscribe/subscribe-to-transaction.ts
1988
- var subscribeToTransaction = (token, handleUpdate, key, store) => {
1989
- const tx = withdraw(token, store);
1990
- store.logger.info(
1991
- `\u{1F440}`,
1992
- `transaction`,
1993
- token.key,
1994
- `Adding subscription "${key}"`
1995
- );
1996
- const unsubscribe = tx.subject.subscribe(key, handleUpdate);
1997
- return () => {
1998
- store.logger.info(
1999
- `\u{1F648}`,
2000
- `transaction`,
2001
- token.key,
2002
- `Removing subscription "${key}"`
2003
- );
2004
- unsubscribe();
2005
- };
2006
- };
2007
-
2008
- // internal/src/mutable/tracker.ts
2009
- var Tracker = class {
2010
- Update;
2011
- initializeState(mutableState, store) {
2012
- const latestUpdateStateKey = `*${mutableState.key}`;
2013
- store.atoms.delete(latestUpdateStateKey);
2014
- store.valueMap.delete(latestUpdateStateKey);
2015
- const familyMetaData = mutableState.family ? {
2016
- key: `*${mutableState.family.key}`,
2017
- subKey: mutableState.family.subKey
2018
- } : void 0;
2019
- const latestUpdateState = createRegularAtom(
2020
- {
2021
- key: latestUpdateStateKey,
2022
- default: null
2023
- },
2024
- familyMetaData,
2025
- store
2026
- );
2027
- if (store.parent?.valueMap.has(latestUpdateStateKey)) {
2028
- const parentValue = store.parent.valueMap.get(latestUpdateStateKey);
2029
- store.valueMap.set(latestUpdateStateKey, parentValue);
2030
- }
2031
- return latestUpdateState;
2032
- }
2033
- unsubscribeFromInnerValue;
2034
- unsubscribeFromState;
2035
- observeCore(mutableState, latestUpdateState, target) {
2036
- const subscriptionKey = `tracker:${target.config.name}:${isChildStore(target) ? target.transactionMeta.update.key : `main`}:${mutableState.key}`;
2037
- const originalInnerValue = getFromStore(mutableState, target);
2038
- this.unsubscribeFromInnerValue = originalInnerValue.subscribe(
2039
- subscriptionKey,
2040
- (update) => {
2041
- setIntoStore(latestUpdateState, update, target);
2042
- }
2043
- );
2044
- this.unsubscribeFromState = subscribeToState(
2045
- mutableState,
2046
- (update) => {
2047
- if (update.newValue !== update.oldValue) {
2048
- this.unsubscribeFromInnerValue();
2049
- this.unsubscribeFromInnerValue = update.newValue.subscribe(
2050
- subscriptionKey,
2051
- (transceiverUpdate) => {
2052
- setIntoStore(latestUpdateState, transceiverUpdate, target);
2053
- }
2054
- );
2055
- }
2056
- },
2057
- subscriptionKey,
2058
- target
2059
- );
2060
- }
2061
- updateCore(mutableState, latestUpdateState, target) {
2062
- const subscriptionKey = `tracker:${target.config.name}:${isChildStore(target) ? target.transactionMeta.update.key : `main`}:${mutableState.key}`;
2063
- subscribeToState(
2064
- latestUpdateState,
2065
- ({ newValue, oldValue }) => {
2066
- const timelineId = target.timelineTopics.getRelatedKey(
2067
- latestUpdateState.key
2068
- );
2069
- if (timelineId) {
2070
- const timelineData = target.timelines.get(timelineId);
2071
- if (timelineData?.timeTraveling) {
2072
- const unsubscribe2 = subscribeToTimeline(
2073
- { key: timelineId, type: `timeline` },
2074
- (update) => {
2075
- unsubscribe2();
2076
- setIntoStore(
2077
- mutableState,
2078
- (transceiver) => {
2079
- if (update === `redo` && newValue) {
2080
- transceiver.do(newValue);
2081
- } else if (update === `undo` && oldValue) {
2082
- transceiver.undo(oldValue);
2083
- }
2084
- return transceiver;
2085
- },
2086
- target
2087
- );
2088
- },
2089
- subscriptionKey,
2090
- target
2091
- );
2092
- return;
2093
- }
2094
- }
2095
- const unsubscribe = target.on.operationClose.subscribe(
2096
- subscriptionKey,
2097
- () => {
2098
- unsubscribe();
2099
- const mutable = getFromStore(mutableState, target);
2100
- const updateNumber = newValue === null ? -1 : mutable.getUpdateNumber(newValue);
2101
- const eventOffset = updateNumber - mutable.cacheUpdateNumber;
2102
- if (newValue && eventOffset === 1) {
2103
- setIntoStore(
2104
- mutableState,
2105
- (transceiver) => (transceiver.do(newValue), transceiver),
2106
- target
2107
- );
2108
- } else {
2109
- target.logger.info(
2110
- `\u274C`,
2111
- `mutable_atom`,
2112
- mutableState.key,
2113
- `could not be updated. Expected update number ${mutable.cacheUpdateNumber + 1}, but got ${updateNumber}`
2114
- );
2115
- }
2116
- }
2117
- );
2118
- },
2119
- subscriptionKey,
2120
- target
2121
- );
2122
- }
2123
- mutableState;
2124
- latestUpdateState;
2125
- dispose;
2126
- constructor(mutableState, store) {
2127
- this.mutableState = mutableState;
2128
- const target = newest(store);
2129
- this.latestUpdateState = this.initializeState(mutableState, target);
2130
- this.observeCore(mutableState, this.latestUpdateState, target);
2131
- this.updateCore(mutableState, this.latestUpdateState, target);
2132
- target.trackers.set(mutableState.key, this);
2133
- this.dispose = () => {
2134
- this.unsubscribeFromInnerValue();
2135
- this.unsubscribeFromState();
2136
- target.trackers.delete(mutableState.key);
2137
- };
2138
- }
2139
- };
2140
-
2141
- // internal/src/mutable/create-mutable-atom.ts
2142
- function createMutableAtom(options, family, store) {
2143
- store.logger.info(
2144
- `\u{1F528}`,
2145
- `atom`,
2146
- options.key,
2147
- `creating in store "${store.config.name}"`
2148
- );
2149
- const target = newest(store);
2150
- const existing = target.atoms.get(options.key);
2151
- if (existing && existing.type === `mutable_atom`) {
2152
- store.logger.error(
2153
- `\u274C`,
2154
- `atom`,
2155
- options.key,
2156
- `Tried to create atom, but it already exists in the store.`
2157
- );
2158
- return deposit(existing);
2159
- }
2160
- const subject = new Subject();
2161
- const newAtom = {
2162
- ...options,
2163
- type: `mutable_atom`,
2164
- install: (s) => {
2165
- s.logger.info(
2166
- `\u{1F6E0}\uFE0F`,
2167
- `atom`,
2168
- options.key,
2169
- `installing in store "${s.config.name}"`
2170
- );
2171
- return createMutableAtom(options, family, s);
2172
- },
2173
- subject
2174
- };
2175
- if (family) {
2176
- newAtom.family = family;
2177
- }
2178
- const initialValue = options.default();
2179
- target.atoms.set(newAtom.key, newAtom);
2180
- markAtomAsDefault(options.key, store);
2181
- cacheValue(options.key, initialValue, subject, target);
2182
- const token = deposit(newAtom);
2183
- if (options.effects) {
2184
- let effectIndex = 0;
2185
- const cleanupFunctions = [];
2186
- for (const effect of options.effects) {
2187
- const cleanup = effect({
2188
- setSelf: (next) => {
2189
- setIntoStore(token, next, store);
2190
- },
2191
- onSet: (handle) => subscribeToState(token, handle, `effect[${effectIndex}]`, store)
2192
- });
2193
- if (cleanup) {
2194
- cleanupFunctions.push(cleanup);
2195
- }
2196
- ++effectIndex;
2197
- }
2198
- newAtom.cleanup = () => {
2199
- for (const cleanup of cleanupFunctions) {
2200
- cleanup();
2201
- }
2202
- };
2203
- }
2204
- new Tracker(token, store);
2205
- if (!family) {
2206
- selectJson(token, options, store);
2207
- }
2208
- return token;
2209
- }
2210
- var FamilyTracker = class {
2211
- Update;
2212
- latestUpdateAtoms;
2213
- mutableAtoms;
2214
- constructor(mutableAtoms, store) {
2215
- this.latestUpdateAtoms = createRegularAtomFamily(
2216
- {
2217
- key: `*${mutableAtoms.key}`,
2218
- default: null
2219
- },
2220
- store
2221
- );
2222
- this.mutableAtoms = mutableAtoms;
2223
- this.mutableAtoms.subject.subscribe(
2224
- `store=${store.config.name}::tracker-atom-family`,
2225
- (event) => {
2226
- if (event.token.family) {
2227
- const key = parseJson(event.token.family.subKey);
2228
- seekInStore(this.latestUpdateAtoms, key, store);
2229
- new Tracker(event.token, store);
2230
- }
2231
- }
2232
- );
2233
- this.latestUpdateAtoms.subject.subscribe(
2234
- `store=${store.config.name}::tracker-atom-family`,
2235
- (event) => {
2236
- if (event.token.family) {
2237
- const key = parseJson(event.token.family.subKey);
2238
- const mutableAtomToken = seekInStore(this.mutableAtoms, key, store);
2239
- if (mutableAtomToken) {
2240
- new Tracker(mutableAtomToken, store);
2241
- }
2242
- }
2243
- }
2244
- );
2245
- }
2246
- };
2247
-
2248
- // internal/src/mutable/create-mutable-atom-family.ts
2249
- function createMutableAtomFamily(options, store) {
2250
- const subject = new Subject();
2251
- const atomFamily = Object.assign(
2252
- (key) => {
2253
- const subKey = stringifyJson(key);
2254
- const family = { key: options.key, subKey };
2255
- const fullKey = `${options.key}(${subKey})`;
2256
- const target = newest(store);
2257
- const individualOptions = {
2258
- key: fullKey,
2259
- default: () => options.default(key),
2260
- toJson: options.toJson,
2261
- fromJson: options.fromJson,
2262
- mutable: true
2263
- };
2264
- if (options.effects) {
2265
- individualOptions.effects = options.effects(key);
2266
- }
2267
- const token = createMutableAtom(individualOptions, family, target);
2268
- subject.next({ type: `state_creation`, token });
2269
- return token;
2270
- },
2271
- {
2272
- key: options.key,
2273
- type: `mutable_atom_family`,
2274
- subject,
2275
- install: (s) => createMutableAtomFamily(options, s),
2276
- toJson: options.toJson,
2277
- fromJson: options.fromJson
2278
- }
2279
- );
2280
- store.families.set(options.key, atomFamily);
2281
- selectJsonFamily(atomFamily, options, store);
2282
- new FamilyTracker(atomFamily, store);
2283
- return atomFamily;
2284
- }
2285
-
2286
- // internal/src/mutable/get-json-family.ts
2287
- var getJsonFamily = (mutableAtomFamily, store) => {
2288
- const target = newest(store);
2289
- const key = `${mutableAtomFamily.key}:JSON`;
2290
- const jsonFamily = target.families.get(key);
2291
- return jsonFamily;
2292
- };
2293
-
2294
- // internal/src/mutable/get-json-token.ts
2295
- var getJsonToken = (mutableAtomToken, store) => {
2296
- if (mutableAtomToken.family) {
2297
- const target = newest(store);
2298
- const jsonFamilyKey = `${mutableAtomToken.family.key}:JSON`;
2299
- const jsonFamilyToken = {
2300
- key: jsonFamilyKey,
2301
- type: `selector_family`
2302
- };
2303
- const family = withdraw(jsonFamilyToken, target);
2304
- const subKey = JSON.parse(mutableAtomToken.family.subKey);
2305
- const jsonToken = findInStore(family, subKey, store);
2306
- return jsonToken;
2307
- }
2308
- const token = {
2309
- type: `selector`,
2310
- key: `${mutableAtomToken.key}:JSON`
2311
- };
2312
- return token;
2313
- };
2314
-
2315
- // internal/src/mutable/get-update-token.ts
2316
- var getUpdateToken = (mutableAtomToken) => {
2317
- const key = `*${mutableAtomToken.key}`;
2318
- const updateToken = { type: `atom`, key };
2319
- if (mutableAtomToken.family) {
2320
- updateToken.family = {
2321
- key: `*${mutableAtomToken.family.key}`,
2322
- subKey: mutableAtomToken.family.subKey
2323
- };
2324
- }
2325
- return updateToken;
2326
- };
2327
-
2328
- // internal/src/mutable/transceiver.ts
2329
- function isTransceiver(value) {
2330
- return typeof value === `object` && value !== null && `do` in value && `undo` in value && `subscribe` in value;
2331
- }
2332
-
2333
- // internal/src/set-state/copy-mutable-if-needed.ts
2334
- function copyMutableIfNeeded(atom, origin, target) {
2335
- const originValue = origin.valueMap.get(atom.key);
2336
- const targetValue = target.valueMap.get(atom.key);
2337
- if (originValue === targetValue) {
2338
- if (originValue === void 0) {
2339
- return typeof atom.default === `function` ? atom.default() : atom.default;
2340
- }
2341
- origin.logger.info(`\u{1F4C3}`, `atom`, atom.key, `copying`);
2342
- const jsonValue = atom.toJson(originValue);
2343
- const copiedValue = atom.fromJson(jsonValue);
2344
- target.valueMap.set(atom.key, copiedValue);
2345
- new Tracker(atom, origin);
2346
- return copiedValue;
2347
- }
2348
- return targetValue;
2349
- }
2350
-
2351
- // internal/src/caching.ts
2352
- function cacheValue(key, value, subject, target) {
2353
- const currentValue = target.valueMap.get(key);
2354
- if (currentValue instanceof Future) {
2355
- const future = currentValue;
2356
- future.use(value);
2357
- }
2358
- if (value instanceof Promise) {
2359
- const future = new Future(value);
2360
- target.valueMap.set(key, future);
2361
- future.then((resolved) => {
2362
- cacheValue(key, resolved, subject, target);
2363
- subject.next({ newValue: resolved, oldValue: future });
2364
- }).catch((thrown) => {
2365
- target.logger.error(`\u{1F4A5}`, `state`, key, `rejected:`, thrown);
2366
- });
2367
- return future;
2368
- }
2369
- target.valueMap.set(key, value);
2370
- return value;
2371
- }
2372
- var readCachedValue = (token, target) => {
2373
- let value = target.valueMap.get(token.key);
2374
- if (token.type === `mutable_atom` && isChildStore(target)) {
2375
- const { parent } = target;
2376
- const copiedValue = copyMutableIfNeeded(token, parent, target);
2377
- value = copiedValue;
2378
- }
2379
- return value;
2380
- };
2381
- var evictCachedValue = (key, target) => {
2382
- const currentValue = target.valueMap.get(key);
2383
- if (currentValue instanceof Future) {
2384
- const future = currentValue;
2385
- const selector = target.selectors.get(key) ?? target.readonlySelectors.get(key);
2386
- if (selector) {
2387
- future.use(selector.get());
2388
- }
2389
- return;
2390
- }
2391
- if (target.operation.open) {
2392
- target.operation.prev.set(key, currentValue);
2393
- }
2394
- target.valueMap.delete(key);
2395
- target.logger.info(`\u{1F5D1}`, `state`, key, `evicted`);
2396
- };
2397
-
2398
- // internal/src/atom/is-default.ts
2399
- var isAtomDefault = (key, store) => {
2400
- const core = newest(store);
2401
- return core.atomsThatAreDefault.has(key);
2402
- };
2403
- var markAtomAsDefault = (key, store) => {
2404
- const core = newest(store);
2405
- core.atomsThatAreDefault = new Set(core.atomsThatAreDefault).add(key);
2406
- };
2407
- var markAtomAsNotDefault = (key, store) => {
2408
- const core = newest(store);
2409
- core.atomsThatAreDefault = new Set(newest(store).atomsThatAreDefault);
2410
- core.atomsThatAreDefault.delete(key);
2411
- };
2412
-
2413
- // internal/src/atom/create-regular-atom.ts
2414
- function createRegularAtom(options, family, store) {
2415
- store.logger.info(
2416
- `\u{1F528}`,
2417
- `atom`,
2418
- options.key,
2419
- `creating in store "${store.config.name}"`
2420
- );
2421
- const target = newest(store);
2422
- const existing = target.atoms.get(options.key);
2423
- if (existing && existing.type === `atom`) {
2424
- store.logger.error(
2425
- `\u274C`,
2426
- `atom`,
2427
- options.key,
2428
- `Tried to create atom, but it already exists in the store.`
2429
- );
2430
- return deposit(existing);
2431
- }
2432
- const subject = new Subject();
2433
- const newAtom = {
2434
- ...options,
2435
- type: `atom`,
2436
- install: (s) => {
2437
- s.logger.info(
2438
- `\u{1F6E0}\uFE0F`,
2439
- `atom`,
2440
- options.key,
2441
- `installing in store "${s.config.name}"`
2442
- );
2443
- return createRegularAtom(options, family, s);
2444
- },
2445
- subject
2446
- };
2447
- if (family) {
2448
- newAtom.family = family;
2449
- }
2450
- let initialValue = options.default;
2451
- if (options.default instanceof Function) {
2452
- initialValue = options.default();
2453
- }
2454
- target.atoms.set(newAtom.key, newAtom);
2455
- markAtomAsDefault(options.key, store);
2456
- cacheValue(options.key, initialValue, subject, target);
2457
- const token = deposit(newAtom);
2458
- if (options.effects) {
2459
- let effectIndex = 0;
2460
- const cleanupFunctions = [];
2461
- for (const effect of options.effects) {
2462
- const cleanup = effect({
2463
- setSelf: (next) => {
2464
- setIntoStore(token, next, store);
2465
- },
2466
- onSet: (handle) => subscribeToState(token, handle, `effect[${effectIndex}]`, store)
2467
- });
2468
- if (cleanup) {
2469
- cleanupFunctions.push(cleanup);
2470
- }
2471
- ++effectIndex;
2472
- }
2473
- newAtom.cleanup = () => {
2474
- for (const cleanup of cleanupFunctions) {
2475
- cleanup();
2476
- }
2477
- };
2478
- }
2479
- return token;
2480
- }
2481
-
2482
- // internal/src/atom/create-standalone-atom.ts
2483
- function createStandaloneAtom(options, store) {
2484
- const isMutable = `mutable` in options;
2485
- if (isMutable) {
2486
- const state2 = createMutableAtom(options, void 0, store);
2487
- store.on.atomCreation.next(state2);
2488
- return state2;
2489
- }
2490
- const state = createRegularAtom(options, void 0, store);
2491
- store.on.atomCreation.next(state);
2492
- return state;
2493
- }
2494
-
2495
- // internal/src/atom/dispose-atom.ts
2496
- function disposeAtom(atomToken, store) {
2497
- const target = newest(store);
2498
- const { key } = atomToken;
2499
- const atom = target.atoms.get(key);
2500
- if (!atom) {
2501
- store.logger.error(
2502
- `\u274C`,
2503
- `atom`,
2504
- key,
2505
- `Tried to dispose atom, but it does not exist in the store.`
2506
- );
2507
- } else if (!atom.family) {
2508
- store.logger.error(`\u274C`, `atom`, key, `Standalone atoms cannot be disposed.`);
2509
- } else {
2510
- atom.cleanup?.();
2511
- const lastValue = store.valueMap.get(atom.key);
2512
- const family = withdraw({ key: atom.family.key, type: `atom_family` }, store);
2513
- family.subject.next({
2514
- type: `state_disposal`,
2515
- token: atomToken,
2516
- value: lastValue
2517
- });
2518
- const molecule = target.molecules.get(atom.family.subKey);
2519
- if (molecule) {
2520
- molecule.tokens.delete(key);
2521
- }
2522
- target.atoms.delete(key);
2523
- target.valueMap.delete(key);
2524
- const selectorKeys = target.selectorAtoms.getRelatedKeys(key);
2525
- if (selectorKeys) {
2526
- for (const selectorKey of selectorKeys) {
2527
- const token = target.selectors.get(selectorKey) ?? target.readonlySelectors.get(selectorKey);
2528
- if (token) {
2529
- disposeSelector(token, store);
2530
- }
2531
- }
2532
- }
2533
- target.selectorAtoms.delete(key);
2534
- target.atomsThatAreDefault.delete(key);
2535
- target.timelineTopics.delete(key);
2536
- if (atomToken.type === `mutable_atom`) {
2537
- const updateToken = getUpdateToken(atomToken);
2538
- disposeAtom(updateToken, store);
2539
- store.trackers.delete(key);
2540
- }
2541
- store.logger.info(`\u{1F525}`, `atom`, key, `deleted`);
2542
- if (isChildStore(target) && target.transactionMeta.phase === `building`) {
2543
- target.transactionMeta.update.updates.push({
2544
- type: `state_disposal`,
2545
- token: atomToken
2546
- });
2547
- } else {
2548
- store.on.atomDisposal.next(atomToken);
2549
- }
2550
- }
2551
- }
2552
- function createTimeline(options, store, data) {
2553
- const tl = {
2554
- type: `timeline`,
2555
- key: options.key,
2556
- at: 0,
2557
- timeTraveling: null,
2558
- selectorTime: null,
2559
- transactionKey: null,
2560
- ...data,
2561
- history: data?.history.map((update) => ({ ...update })) ?? [],
2562
- install: (s) => createTimeline(options, s, tl),
2563
- subject: new Subject(),
2564
- subscriptions: /* @__PURE__ */ new Map()
2565
- };
2566
- if (options.shouldCapture) {
2567
- tl.shouldCapture = options.shouldCapture;
2568
- }
2569
- const timelineKey = options.key;
2570
- const target = newest(store);
2571
- for (const initialTopic of options.scope) {
2572
- switch (initialTopic.type) {
2573
- case `atom`:
2574
- case `mutable_atom`:
2575
- {
2576
- const atomToken = initialTopic;
2577
- const atomKey = atomToken.key;
2578
- let existingTimelineKey = target.timelineTopics.getRelatedKey(atomKey);
2579
- if (`family` in atomToken) {
2580
- const familyKey = atomToken.family.key;
2581
- existingTimelineKey = target.timelineTopics.getRelatedKey(familyKey);
2582
- if (existingTimelineKey) {
2583
- store.logger.error(
2584
- `\u274C`,
2585
- `timeline`,
2586
- options.key,
2587
- `Failed to add atom "${atomKey}" because its family "${familyKey}" already belongs to timeline "${existingTimelineKey}"`
2588
- );
2589
- continue;
2590
- }
2591
- }
2592
- if (existingTimelineKey) {
2593
- store.logger.error(
2594
- `\u274C`,
2595
- `timeline`,
2596
- options.key,
2597
- `Failed to add atom "${atomKey}" because it already belongs to timeline "${existingTimelineKey}"`
2598
- );
2599
- continue;
2600
- }
2601
- addAtomToTimeline(atomToken, tl, store);
2602
- }
2603
- break;
2604
- case `atom_family`:
2605
- case `mutable_atom_family`:
2606
- {
2607
- const familyToken = initialTopic;
2608
- const familyKey = familyToken.key;
2609
- const existingTimelineKey = target.timelineTopics.getRelatedKey(familyKey);
2610
- if (existingTimelineKey) {
2611
- store.logger.error(
2612
- `\u274C`,
2613
- `timeline`,
2614
- options.key,
2615
- `Failed to add atom family "${familyKey}" because it already belongs to timeline "${existingTimelineKey}"`
2616
- );
2617
- continue;
2618
- }
2619
- addAtomFamilyToTimeline(familyToken, tl, store);
2620
- }
2621
- break;
2622
- case `molecule_family`:
2623
- {
2624
- const familyToken = initialTopic;
2625
- const familyKey = familyToken.key;
2626
- const existingTimelineKey = target.timelineTopics.getRelatedKey(familyKey);
2627
- if (existingTimelineKey) {
2628
- store.logger.error(
2629
- `\u274C`,
2630
- `timeline`,
2631
- options.key,
2632
- `Failed to add molecule family "${familyKey}" because it already belongs to timeline "${existingTimelineKey}"`
2633
- );
2634
- continue;
2635
- }
2636
- addMoleculeFamilyToTimeline(familyToken, tl, store);
2637
- }
2638
- break;
2639
- }
2640
- }
2641
- store.timelines.set(options.key, tl);
2642
- const token = {
2643
- key: timelineKey,
2644
- type: `timeline`
2645
- };
2646
- store.on.timelineCreation.next(token);
2647
- return token;
2648
- }
2649
- function addAtomToTimeline(atomToken, tl, store) {
2650
- let maybeAtom = withdraw(atomToken, store);
2651
- if (maybeAtom.type === `mutable_atom`) {
2652
- const updateToken = getUpdateToken(maybeAtom);
2653
- maybeAtom = withdraw(updateToken, store);
2654
- }
2655
- const atom = maybeAtom;
2656
- store.timelineTopics.set(
2657
- { topicKey: atom.key, timelineKey: tl.key },
2658
- { topicType: `atom` }
2659
- );
2660
- tl.subscriptions.set(
2661
- atom.key,
2662
- atom.subject.subscribe(`timeline`, (update) => {
2663
- const target = newest(store);
2664
- const currentSelectorKey = store.operation.open && store.operation.token.type === `selector` ? store.operation.token.key : null;
2665
- const currentSelectorTime = store.operation.open && store.operation.token.type === `selector` ? store.operation.time : null;
2666
- const txUpdateInProgress = target.on.transactionApplying.state?.update;
2667
- store.logger.info(
2668
- `\u23F3`,
2669
- `timeline`,
2670
- tl.key,
2671
- `atom`,
2672
- atomToken.key,
2673
- `went`,
2674
- update.oldValue,
2675
- `->`,
2676
- update.newValue,
2677
- txUpdateInProgress ? `in transaction "${txUpdateInProgress.key}"` : currentSelectorKey ? `in selector "${currentSelectorKey}"` : ``
2678
- );
2679
- if (tl.timeTraveling === null) {
2680
- if (txUpdateInProgress) {
2681
- joinTransaction(tl, txUpdateInProgress, store);
2682
- } else if (currentSelectorKey && currentSelectorTime) {
2683
- let latestUpdate = tl.history.at(-1);
2684
- if (currentSelectorTime !== tl.selectorTime) {
2685
- latestUpdate = {
2686
- type: `selector_update`,
2687
- timestamp: currentSelectorTime,
2688
- key: currentSelectorKey,
2689
- atomUpdates: []
2690
- };
2691
- latestUpdate.atomUpdates.push({
2692
- key: atom.key,
2693
- type: `atom_update`,
2694
- ...update
2695
- });
2696
- if (tl.at !== tl.history.length) {
2697
- tl.history.splice(tl.at);
2698
- }
2699
- tl.history.push(latestUpdate);
2700
- store.logger.info(
2701
- `\u231B`,
2702
- `timeline`,
2703
- tl.key,
2704
- `got a selector_update "${currentSelectorKey}" with`,
2705
- latestUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key)
2706
- );
2707
- tl.at = tl.history.length;
2708
- tl.selectorTime = currentSelectorTime;
2709
- } else {
2710
- if (latestUpdate?.type === `selector_update`) {
2711
- latestUpdate.atomUpdates.push({
2712
- key: atom.key,
2713
- type: `atom_update`,
2714
- ...update
2715
- });
2716
- store.logger.info(
2717
- `\u231B`,
2718
- `timeline`,
2719
- tl.key,
2720
- `set selector_update "${currentSelectorKey}" to`,
2721
- latestUpdate?.atomUpdates.map((atomUpdate) => atomUpdate.key)
2722
- );
2723
- }
2724
- }
2725
- if (latestUpdate) {
2726
- const willCaptureSelectorUpdate = tl.shouldCapture?.(latestUpdate, tl) ?? true;
2727
- if (willCaptureSelectorUpdate) {
2728
- tl.subject.next(latestUpdate);
2729
- } else {
2730
- tl.history.pop();
2731
- tl.at = tl.history.length;
2732
- }
2733
- }
2734
- } else {
2735
- const timestamp = Date.now();
2736
- tl.selectorTime = null;
2737
- if (tl.at !== tl.history.length) {
2738
- tl.history.splice(tl.at);
2739
- }
2740
- const atomUpdate = {
2741
- type: `atom_update`,
2742
- timestamp,
2743
- key: atom.key,
2744
- oldValue: update.oldValue,
2745
- newValue: update.newValue
2746
- };
2747
- if (atom.family) {
2748
- atomUpdate.family = atom.family;
2749
- }
2750
- const willCapture = tl.shouldCapture?.(atomUpdate, tl) ?? true;
2751
- store.logger.info(
2752
- `\u231B`,
2753
- `timeline`,
2754
- tl.key,
2755
- `got an atom_update to "${atom.key}"`
2756
- );
2757
- if (willCapture) {
2758
- tl.history.push(atomUpdate);
2759
- tl.at = tl.history.length;
2760
- tl.subject.next(atomUpdate);
2761
- }
2762
- }
2763
- }
2764
- })
2765
- );
2766
- }
2767
- function addAtomFamilyToTimeline(atomFamilyToken, tl, store) {
2768
- const family = withdraw(atomFamilyToken, store);
2769
- store.timelineTopics.set(
2770
- { topicKey: family.key, timelineKey: tl.key },
2771
- { topicType: `atom_family` }
2772
- );
2773
- tl.subscriptions.set(
2774
- family.key,
2775
- family.subject.subscribe(`timeline`, (creationOrDisposal) => {
2776
- handleStateLifecycleEvent(creationOrDisposal, tl, store);
2777
- })
2778
- );
2779
- for (const atom of store.atoms.values()) {
2780
- if (atom.family?.key === family.key) {
2781
- addAtomToTimeline(atom, tl, store);
2782
- }
2783
- }
2784
- }
2785
- function addMoleculeFamilyToTimeline(familyToken, tl, store) {
2786
- store.timelineTopics.set(
2787
- { topicKey: familyToken.key, timelineKey: tl.key },
2788
- { topicType: `molecule_family` }
2789
- );
2790
- const family = store.moleculeFamilies.get(familyToken.key);
2791
- if (family) {
2792
- tl.subscriptions.set(
2793
- familyToken.key,
2794
- family.subject.subscribe(`timeline:${tl.key}`, (creationOrDisposal) => {
2795
- store.logger.info(
2796
- `\u{1F41E}`,
2797
- `timeline`,
2798
- tl.key,
2799
- `got a molecule creation or disposal`,
2800
- creationOrDisposal
2801
- );
2802
- switch (creationOrDisposal.type) {
2803
- case `molecule_creation`:
2804
- {
2805
- store.timelineTopics.set(
2806
- {
2807
- topicKey: creationOrDisposal.token.key,
2808
- timelineKey: tl.key
2809
- },
2810
- { topicType: `molecule` }
2811
- );
2812
- const txUpdateInProgress = newest(store).on.transactionApplying.state?.update;
2813
- if (txUpdateInProgress) {
2814
- joinTransaction(tl, txUpdateInProgress, store);
2815
- } else if (tl.timeTraveling === null) {
2816
- const event = Object.assign(creationOrDisposal, {
2817
- timestamp: Date.now()
2818
- });
2819
- tl.history.push(event);
2820
- tl.at = tl.history.length;
2821
- tl.subject.next(event);
2822
- }
2823
- const molecule = withdraw(creationOrDisposal.token, store);
2824
- for (const token of molecule.tokens.values()) {
2825
- switch (token.type) {
2826
- case `atom`:
2827
- case `mutable_atom`:
2828
- addAtomToTimeline(token, tl, store);
2829
- break;
2830
- }
2831
- }
2832
- tl.subscriptions.set(
2833
- molecule.key,
2834
- molecule.subject.subscribe(
2835
- `timeline:${tl.key}`,
2836
- (stateCreationOrDisposal) => {
2837
- handleStateLifecycleEvent(stateCreationOrDisposal, tl, store);
2838
- }
2839
- )
2840
- );
2841
- }
2842
- break;
2843
- case `molecule_disposal`:
2844
- {
2845
- const txUpdateInProgress = newest(store).on.transactionApplying.state?.update;
2846
- if (txUpdateInProgress) {
2847
- joinTransaction(tl, txUpdateInProgress, store);
2848
- } else if (tl.timeTraveling === null) {
2849
- const event = Object.assign(creationOrDisposal, {
2850
- timestamp: Date.now()
2851
- });
2852
- tl.history.push(event);
2853
- tl.at = tl.history.length;
2854
- tl.subject.next(event);
2855
- }
2856
- const moleculeKey = creationOrDisposal.token.key;
2857
- tl.subscriptions.get(moleculeKey)?.();
2858
- tl.subscriptions.delete(moleculeKey);
2859
- for (const [familyKey] of creationOrDisposal.values) {
2860
- const stateKey = `${familyKey}(${stringifyJson(moleculeKey)})`;
2861
- tl.subscriptions.get(stateKey)?.();
2862
- tl.subscriptions.delete(stateKey);
2863
- store.timelineTopics.delete(stateKey);
2864
- }
2865
- }
2866
- break;
2867
- }
2868
- })
2869
- );
2870
- }
2871
- }
2872
- function joinTransaction(tl, txUpdateInProgress, store) {
2873
- const currentTxKey = txUpdateInProgress.key;
2874
- const currentTxInstanceId = txUpdateInProgress.id;
2875
- const currentTxToken = {
2876
- key: currentTxKey,
2877
- type: `transaction`
2878
- };
2879
- const currentTransaction = withdraw(currentTxToken, store);
2880
- if (currentTxKey && tl.transactionKey === null) {
2881
- tl.transactionKey = currentTxKey;
2882
- const unsubscribe = currentTransaction.subject.subscribe(
2883
- `timeline:${tl.key}`,
2884
- (transactionUpdate) => {
2885
- unsubscribe();
2886
- tl.transactionKey = null;
2887
- if (tl.timeTraveling === null && currentTxInstanceId) {
2888
- if (tl.at !== tl.history.length) {
2889
- tl.history.splice(tl.at);
2890
- }
2891
- const timelineTopics = store.timelineTopics.getRelatedKeys(tl.key);
2892
- const updates = filterTransactionUpdates(
2893
- transactionUpdate.updates,
2894
- timelineTopics
2895
- );
2896
- const timelineTransactionUpdate = {
2897
- timestamp: Date.now(),
2898
- ...transactionUpdate,
2899
- updates
2900
- };
2901
- const willCapture = tl.shouldCapture?.(timelineTransactionUpdate, tl) ?? true;
2902
- if (willCapture) {
2903
- tl.history.push(timelineTransactionUpdate);
2904
- tl.at = tl.history.length;
2905
- tl.subject.next(timelineTransactionUpdate);
2906
- }
2907
- }
2908
- }
2909
- );
2910
- }
2911
- }
2912
- function filterTransactionUpdates(updates, timelineTopics) {
2913
- return updates.filter((updateFromTx) => {
2914
- if (updateFromTx.type === `transaction_update`) {
2915
- return true;
2916
- }
2917
- let key;
2918
- switch (updateFromTx.type) {
2919
- case `state_creation`:
2920
- case `state_disposal`:
2921
- case `molecule_creation`:
2922
- case `molecule_disposal`:
2923
- key = updateFromTx.token.key;
2924
- break;
2925
- default:
2926
- key = updateFromTx.key;
2927
- break;
2928
- }
2929
- return timelineTopics.has(key);
2930
- }).map((updateFromTx) => {
2931
- if (`updates` in updateFromTx) {
2932
- return {
2933
- ...updateFromTx,
2934
- updates: filterTransactionUpdates(
2935
- updateFromTx.updates,
2936
- timelineTopics
2937
- )
2938
- };
2939
- }
2940
- return updateFromTx;
2941
- });
2942
- }
2943
- function handleStateLifecycleEvent(event, tl, store) {
2944
- const timestamp = Date.now();
2945
- const timelineEvent = Object.assign(event, {
2946
- timestamp
2947
- });
2948
- if (!tl.timeTraveling) {
2949
- const txUpdateInProgress = newest(store).on.transactionApplying.state?.update;
2950
- if (txUpdateInProgress) {
2951
- joinTransaction(tl, txUpdateInProgress, store);
2952
- } else {
2953
- tl.history.push(timelineEvent);
2954
- tl.at = tl.history.length;
2955
- tl.subject.next(timelineEvent);
2956
- }
2957
- }
2958
- switch (event.type) {
2959
- case `state_creation`:
2960
- addAtomToTimeline(event.token, tl, store);
2961
- break;
2962
- case `state_disposal`:
2963
- tl.subscriptions.get(event.token.key)?.();
2964
- tl.subscriptions.delete(event.token.key);
2965
- break;
2966
- }
2967
- }
2968
-
2969
- // internal/src/timeline/time-travel.ts
2970
- var timeTravel = (action, token, store) => {
2971
- store.logger.info(
2972
- action === `redo` ? `\u23E9` : `\u23EA`,
2973
- `timeline`,
2974
- token.key,
2975
- action
2976
- );
2977
- const timelineData = store.timelines.get(token.key);
2978
- if (!timelineData) {
2979
- store.logger.error(
2980
- `\u{1F41E}`,
2981
- `timeline`,
2982
- token.key,
2983
- `Failed to ${action}. This timeline has not been initialized.`
2984
- );
2985
- return;
2986
- }
2987
- if (action === `redo` && timelineData.at === timelineData.history.length || action === `undo` && timelineData.at === 0) {
2988
- store.logger.warn(
2989
- `\u{1F481}`,
2990
- `timeline`,
2991
- token.key,
2992
- `Failed to ${action} at the ${action === `redo` ? `end` : `beginning`} of timeline "${token.key}". There is nothing to ${action}.`
2993
- );
2994
- return;
2995
- }
2996
- timelineData.timeTraveling = action === `redo` ? `into_future` : `into_past`;
2997
- if (action === `undo`) {
2998
- --timelineData.at;
2999
- }
3000
- const update = timelineData.history[timelineData.at];
3001
- const applying = action === `redo` ? `newValue` : `oldValue`;
3002
- switch (update.type) {
3003
- case `atom_update`: {
3004
- ingestAtomUpdate(applying, update, store);
3005
- break;
3006
- }
3007
- case `selector_update`: {
3008
- ingestSelectorUpdate(applying, update, store);
3009
- break;
3010
- }
3011
- case `transaction_update`: {
3012
- ingestTransactionUpdate(applying, update, store);
3013
- break;
3014
- }
3015
- case `state_creation`: {
3016
- ingestCreationEvent(update, applying, store);
3017
- break;
3018
- }
3019
- case `state_disposal`: {
3020
- ingestDisposalEvent(update, applying, store);
3021
- break;
3022
- }
3023
- case `molecule_creation`: {
3024
- ingestMoleculeCreationEvent(update, applying, store);
3025
- break;
3026
- }
3027
- case `molecule_disposal`: {
3028
- ingestMoleculeDisposalEvent(update, applying, store);
3029
- break;
3030
- }
3031
- }
3032
- if (action === `redo`) {
3033
- ++timelineData.at;
3034
- }
3035
- timelineData.subject.next(action);
3036
- timelineData.timeTraveling = null;
3037
- store.logger.info(
3038
- `\u23F9\uFE0F`,
3039
- `timeline`,
3040
- token.key,
3041
- `"${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`
3042
- );
3043
- };
3044
-
3045
- export { FamilyTracker, Future, IMPLICIT, LazyMap, Molecule, NotFoundError, StatefulSubject, Store, Subject, TRANSACTION_PHASES, Tracker, abortTransaction, actUponStore, applyTransaction, arbitrary, assignTransactionToContinuity, become, buildTransaction, cacheValue, clearStore, closeOperation, createAtomFamily, createMoleculeFamily, createMutableAtom, createMutableAtomFamily, createReadonlySelector, createReadonlySelectorFamily, createRegularAtom, createRegularAtomFamily, createSelectorFamily, createStandaloneAtom, createStandaloneSelector, createTimeline, createTransaction, createWritableSelector, deposit, disposeAtom, disposeFromStore, disposeMolecule, disposeSelector, eldest, evictCachedValue, findInStore, getContinuityKey, getEnvironmentData, getEpochNumberOfAction, getEpochNumberOfContinuity, getFromStore, getJsonFamily, getJsonToken, getSelectorDependencyKeys, getUpdateToken, growMoleculeInStore, ingestAtomUpdate, ingestCreationEvent, ingestDisposalEvent, ingestMoleculeCreationEvent, ingestMoleculeDisposalEvent, ingestSelectorUpdate, ingestTransactionUpdate, initFamilyMemberInStore, isAtomDefault, isAtomKey, isChildStore, isDone, isReadonlySelectorKey, isRootStore, isSelectorKey, isStateKey, isTransceiver, makeMoleculeInStore, markAtomAsDefault, markAtomAsNotDefault, markDone, newest, openOperation, readCachedValue, readOrComputeValue, registerSelector, seekInStore, setAtomOrSelector, setEpochNumberOfAction, setEpochNumberOfContinuity, setIntoStore, subscribeToRootAtoms, subscribeToState, subscribeToTimeline, subscribeToTransaction, timeTravel, traceAllSelectorAtoms, traceSelectorAtoms, updateSelectorAtoms, withdraw };