@mocanvas/state 1.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +421 -0
- package/BENCHMARK.md +519 -0
- package/CLEAN_ROOM.md +50 -0
- package/COMPAT.md +282 -0
- package/CUSTOM_SHAPES.md +880 -0
- package/LICENSE +110 -16
- package/MIGRATION.md +807 -0
- package/README.md +24 -4
- package/UI.md +256 -0
- package/dist/{chunk-XCRMDUZF.js → chunk-KGMTQKZV.js} +214 -12
- package/dist/chunk-KGMTQKZV.js.map +1 -0
- package/dist/core-DVexut58.d.ts +325 -0
- package/dist/index.d.ts +91 -137
- package/dist/index.js +284 -1
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +20 -2
- package/dist/react.js +10 -2
- package/dist/react.js.map +1 -1
- package/package.json +11 -13
- package/dist/chunk-XCRMDUZF.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,144 +1,98 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* -----
|
|
6
|
-
* - A single global epoch counter advances on every atom write.
|
|
7
|
-
* - Every signal remembers `lastChangedEpoch`, the epoch of the write that
|
|
8
|
-
* last changed its value.
|
|
9
|
-
* - Computeds are lazy and pull-based: `get()` verifies the cached value by
|
|
10
|
-
* comparing the `lastChangedEpoch` of each recorded parent against the
|
|
11
|
-
* epoch at which the computed was last verified (`lastCheckedEpoch`).
|
|
12
|
-
* Recomputation happens only when a parent actually changed.
|
|
13
|
-
* - Reactions (effects) are push-notified: an atom write walks the children
|
|
14
|
-
* graph and marks reachable reactions pending. Pending reactions execute
|
|
15
|
-
* once, after the outermost transaction commits, and re-check their
|
|
16
|
-
* dependencies' epochs before running so that equal computed values never
|
|
17
|
-
* trigger a run.
|
|
18
|
-
* - Dependency tracking uses a single "active dependent" slot that behaves
|
|
19
|
-
* like a capture stack through the native call stack (save/restore).
|
|
20
|
-
*/
|
|
21
|
-
declare const UNINITIALIZED: unique symbol;
|
|
22
|
-
type Uninitialized = typeof UNINITIALIZED;
|
|
23
|
-
declare const EMPTY_ARRAY: readonly [];
|
|
24
|
-
interface Signal<T> {
|
|
1
|
+
export { A as Atom, a as AtomOptions, C as ComputeDiff, b as Computed, c as ComputedOptions, E as EMPTY_ARRAY, d as EffectScheduler, e as EffectSchedulerOptions, H as HistoryBuffer, R as RESET_VALUE, f as ReactOptions, g as Reactor, h as RenderTracker, i as ResetValue, S as Signal, U as UNINITIALIZED, j as Uninitialized, W as WithDiff, k as atom, l as computed, m as getComputedInstance, n as getGlobalEpoch, o as isAtom, p as isComputed, q as isSignal, r as isUninitialized, s as isWithDiff, t as localStorageAtom, u as react, v as reactor, w as subscribeToSignal, x as transact, y as transaction, z as unsafe__withoutCapture, B as whyAmIRunning, D as withDiff } from './core-DVexut58.js';
|
|
2
|
+
|
|
3
|
+
/** A reactive map from `K` to `V`, backed by one atom per key. */
|
|
4
|
+
declare class AtomMap<K, V> {
|
|
25
5
|
readonly name: string;
|
|
26
|
-
|
|
27
|
-
lastChangedEpoch: number;
|
|
28
|
-
}
|
|
29
|
-
interface Atom<T> extends Signal<T> {
|
|
30
|
-
set(value: T): T;
|
|
31
|
-
update(fn: (prev: T) => T): T;
|
|
32
|
-
}
|
|
33
|
-
interface Computed<T> extends Signal<T> {
|
|
34
|
-
/** Epoch at which the cached value was last verified against its parents. */
|
|
35
|
-
readonly lastCheckedEpoch: number;
|
|
36
|
-
}
|
|
37
|
-
interface AtomOptions<T> {
|
|
38
|
-
isEqual?: (a: T, b: T) => boolean;
|
|
39
|
-
}
|
|
40
|
-
interface ComputedOptions<T> {
|
|
41
|
-
isEqual?: (a: T, b: T) => boolean;
|
|
42
|
-
}
|
|
43
|
-
interface ReactOptions {
|
|
6
|
+
private readonly atoms;
|
|
44
7
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
8
|
+
* Bumped whenever a key is added or removed. Anything that depends on which
|
|
9
|
+
* keys exist — `size`, iteration, a `get` that missed — reads this, so it is
|
|
10
|
+
* invalidated by an insert without being invalidated by every value write.
|
|
48
11
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
12
|
+
private readonly keyEpoch;
|
|
13
|
+
constructor(name: string, entries?: Iterable<readonly [K, V]>);
|
|
14
|
+
/** The value for `key`, or `undefined` when there is none. */
|
|
15
|
+
get(key: K): V | undefined;
|
|
16
|
+
/** Whether `key` has a value. */
|
|
17
|
+
has(key: K): boolean;
|
|
18
|
+
/** Set `key`, returning the map so calls can be chained. */
|
|
19
|
+
set(key: K, value: V): this;
|
|
20
|
+
/**
|
|
21
|
+
* The value for `key`, inserting `defaultValue` first if there is none.
|
|
22
|
+
*
|
|
23
|
+
* `defaultValue` is always evaluated by the caller; use
|
|
24
|
+
* {@link AtomMap.getOrInsertComputed} when producing it is expensive.
|
|
25
|
+
*/
|
|
26
|
+
getOrInsert(key: K, defaultValue: V): V;
|
|
27
|
+
/**
|
|
28
|
+
* The value for `key`, inserting `create()` first if there is none.
|
|
29
|
+
* `create` runs **only** when the key is absent.
|
|
30
|
+
*/
|
|
31
|
+
getOrInsertComputed(key: K, create: (key: K) => V): V;
|
|
32
|
+
/**
|
|
33
|
+
* The value for `key`, or `UNINITIALIZED` when there is none — the one read
|
|
34
|
+
* that can tell a stored `undefined` apart from a missing key. Subscribes the
|
|
35
|
+
* same way {@link AtomMap.get} does.
|
|
36
|
+
*/
|
|
37
|
+
private peek;
|
|
38
|
+
/** Replace `key`'s value with `fn` applied to it. Throws if `key` is absent. */
|
|
39
|
+
update(key: K, fn: (prev: V) => V): V;
|
|
40
|
+
/** Remove `key`. Returns whether it was there. */
|
|
41
|
+
delete(key: K): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Remove several keys at once, in one transaction. Returns the keys that
|
|
44
|
+
* were actually there.
|
|
45
|
+
*
|
|
46
|
+
* Deleting in a loop wakes every dependent once per key; a bulk delete —
|
|
47
|
+
* clearing a selection, dropping a page's shapes — should wake them once.
|
|
48
|
+
*/
|
|
49
|
+
deleteMany(keys: Iterable<K>): [K, V][];
|
|
50
|
+
get [Symbol.toStringTag](): string;
|
|
51
|
+
/** Remove every entry. */
|
|
52
|
+
clear(): void;
|
|
53
|
+
/** How many entries the map holds. */
|
|
54
|
+
get size(): number;
|
|
55
|
+
/** The live keys, in insertion order. */
|
|
56
|
+
keys(): IterableIterator<K>;
|
|
57
|
+
/** The live values, in insertion order. */
|
|
58
|
+
values(): IterableIterator<V>;
|
|
59
|
+
/** The live entries, in insertion order. */
|
|
60
|
+
entries(): IterableIterator<[K, V]>;
|
|
61
|
+
[Symbol.iterator](): IterableIterator<[K, V]>;
|
|
62
|
+
forEach(fn: (value: V, key: K, map: AtomMap<K, V>) => void, thisArg?: unknown): void;
|
|
75
63
|
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
declare
|
|
79
|
-
declare function atom<T>(name: string, initialValue: T, options?: AtomOptions<T>): Atom<T>;
|
|
80
|
-
declare function computed<T>(name: string, fn: (prev: T | Uninitialized) => T, options?: ComputedOptions<T>): Computed<T>;
|
|
81
|
-
declare abstract class EffectBase implements Dependent {
|
|
64
|
+
|
|
65
|
+
/** A reactive set of `T`, backed by one boolean atom per member. */
|
|
66
|
+
declare class AtomSet<T> {
|
|
82
67
|
readonly name: string;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
private listener;
|
|
112
|
-
constructor(name: string);
|
|
113
|
-
__schedule(): void;
|
|
114
|
-
private execute;
|
|
115
|
-
private bump;
|
|
116
|
-
/** Runs `fn(a, b)` while recording every signal read as a dependency. */
|
|
117
|
-
track<A, B, R>(fn: (a: A, b: B) => R, a: A, b: B): R;
|
|
118
|
-
readonly getSnapshot: () => number;
|
|
119
|
-
readonly subscribe: (listener: () => void) => (() => void);
|
|
120
|
-
readonly unsubscribe: () => void;
|
|
68
|
+
/**
|
|
69
|
+
* One atom per value ever seen. A removed value keeps its atom, holding
|
|
70
|
+
* `false`, so anything already subscribed to that value is told it left
|
|
71
|
+
* rather than silently going stale.
|
|
72
|
+
*/
|
|
73
|
+
private readonly atoms;
|
|
74
|
+
/** Bumped whenever membership changes, for readers of the set's shape. */
|
|
75
|
+
private readonly membershipEpoch;
|
|
76
|
+
constructor(name: string, values?: Iterable<T>);
|
|
77
|
+
get [Symbol.toStringTag](): string;
|
|
78
|
+
/** Whether `value` is a member. */
|
|
79
|
+
has(value: T): boolean;
|
|
80
|
+
/** Add `value`, returning the set so calls can be chained. */
|
|
81
|
+
add(value: T): this;
|
|
82
|
+
/** Remove `value`. Returns whether it was a member. */
|
|
83
|
+
delete(value: T): boolean;
|
|
84
|
+
/** Remove every member. */
|
|
85
|
+
clear(): void;
|
|
86
|
+
/** How many members the set holds. */
|
|
87
|
+
get size(): number;
|
|
88
|
+
/** The live members, in insertion order. */
|
|
89
|
+
keys(): IterableIterator<T>;
|
|
90
|
+
/** The live members, in insertion order. Same as {@link AtomSet.keys}, as on `Set`. */
|
|
91
|
+
values(): IterableIterator<T>;
|
|
92
|
+
/** `[value, value]` pairs, as on `Set`. */
|
|
93
|
+
entries(): IterableIterator<[T, T]>;
|
|
94
|
+
[Symbol.iterator](): IterableIterator<T>;
|
|
95
|
+
forEach(fn: (value: T, value2: T, set: AtomSet<T>) => void, thisArg?: unknown): void;
|
|
121
96
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Calls `onChange` whenever `signal`'s value changes. The initial subscription
|
|
124
|
-
* does not call `onChange`. Returns an unsubscribe function.
|
|
125
|
-
*/
|
|
126
|
-
declare function subscribeToSignal(signal: Signal<unknown>, onChange: () => void): () => void;
|
|
127
|
-
/**
|
|
128
|
-
* Batches atom writes: dependents are notified as writes happen, but effects
|
|
129
|
-
* run once, after the outermost transaction commits. If `fn` throws, writes
|
|
130
|
-
* made inside the transaction are rolled back and the error is rethrown.
|
|
131
|
-
*/
|
|
132
|
-
declare function transact<T>(fn: () => T): T;
|
|
133
|
-
/**
|
|
134
|
-
* Like `transact`, but `fn` receives a `rollback` function that restores every
|
|
135
|
-
* atom written inside this transaction (including committed nested ones) to
|
|
136
|
-
* the value it had when this transaction began. The transaction stays open
|
|
137
|
-
* after a rollback, so further writes are recorded again.
|
|
138
|
-
*/
|
|
139
|
-
declare function transaction<T>(fn: (rollback: () => void) => T): T;
|
|
140
|
-
declare function isSignal(value: unknown): value is Signal<unknown>;
|
|
141
|
-
declare function isAtom(value: unknown): value is Atom<unknown>;
|
|
142
|
-
declare function isComputed(value: unknown): value is Computed<unknown>;
|
|
143
97
|
|
|
144
|
-
export {
|
|
98
|
+
export { AtomMap, AtomSet };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,286 @@
|
|
|
1
|
-
|
|
1
|
+
import { atom, UNINITIALIZED, transact, getWithoutCapture } from './chunk-KGMTQKZV.js';
|
|
2
|
+
export { EMPTY_ARRAY, HistoryBuffer, RESET_VALUE, RenderTracker, UNINITIALIZED, WithDiff, atom, computed, getComputedInstance, getGlobalEpoch, isAtom, isComputed, isSignal, isUninitialized, isWithDiff, localStorageAtom, react, reactor, subscribeToSignal, transact, transaction, unsafe__withoutCapture, whyAmIRunning, withDiff } from './chunk-KGMTQKZV.js';
|
|
3
|
+
|
|
4
|
+
// src/AtomMap.ts
|
|
5
|
+
var AtomMap = class {
|
|
6
|
+
constructor(name, entries) {
|
|
7
|
+
this.name = name;
|
|
8
|
+
this.keyEpoch = atom(`${name}:keys`, 0);
|
|
9
|
+
if (entries) {
|
|
10
|
+
for (const [key, value] of entries) {
|
|
11
|
+
this.atoms.set(key, atom(`${name}:${String(key)}`, value));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
name;
|
|
16
|
+
atoms = /* @__PURE__ */ new Map();
|
|
17
|
+
/**
|
|
18
|
+
* Bumped whenever a key is added or removed. Anything that depends on which
|
|
19
|
+
* keys exist — `size`, iteration, a `get` that missed — reads this, so it is
|
|
20
|
+
* invalidated by an insert without being invalidated by every value write.
|
|
21
|
+
*/
|
|
22
|
+
keyEpoch;
|
|
23
|
+
/** The value for `key`, or `undefined` when there is none. */
|
|
24
|
+
get(key) {
|
|
25
|
+
const a = this.atoms.get(key);
|
|
26
|
+
if (!a) {
|
|
27
|
+
this.keyEpoch.get();
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
const value = a.get();
|
|
31
|
+
return value === UNINITIALIZED ? void 0 : value;
|
|
32
|
+
}
|
|
33
|
+
/** Whether `key` has a value. */
|
|
34
|
+
has(key) {
|
|
35
|
+
const a = this.atoms.get(key);
|
|
36
|
+
if (!a) {
|
|
37
|
+
this.keyEpoch.get();
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return a.get() !== UNINITIALIZED;
|
|
41
|
+
}
|
|
42
|
+
/** Set `key`, returning the map so calls can be chained. */
|
|
43
|
+
set(key, value) {
|
|
44
|
+
const a = this.atoms.get(key);
|
|
45
|
+
if (a) {
|
|
46
|
+
transact(() => {
|
|
47
|
+
const wasAbsent = getWithoutCapture(a) === UNINITIALIZED;
|
|
48
|
+
a.set(value);
|
|
49
|
+
if (wasAbsent) this.keyEpoch.update((n) => n + 1);
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
this.atoms.set(key, atom(`${this.name}:${String(key)}`, value));
|
|
53
|
+
this.keyEpoch.update((n) => n + 1);
|
|
54
|
+
}
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The value for `key`, inserting `defaultValue` first if there is none.
|
|
59
|
+
*
|
|
60
|
+
* `defaultValue` is always evaluated by the caller; use
|
|
61
|
+
* {@link AtomMap.getOrInsertComputed} when producing it is expensive.
|
|
62
|
+
*/
|
|
63
|
+
getOrInsert(key, defaultValue) {
|
|
64
|
+
const existing = this.peek(key);
|
|
65
|
+
if (existing !== UNINITIALIZED) return existing;
|
|
66
|
+
this.set(key, defaultValue);
|
|
67
|
+
return defaultValue;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The value for `key`, inserting `create()` first if there is none.
|
|
71
|
+
* `create` runs **only** when the key is absent.
|
|
72
|
+
*/
|
|
73
|
+
getOrInsertComputed(key, create) {
|
|
74
|
+
const existing = this.peek(key);
|
|
75
|
+
if (existing !== UNINITIALIZED) return existing;
|
|
76
|
+
const value = create(key);
|
|
77
|
+
this.set(key, value);
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The value for `key`, or `UNINITIALIZED` when there is none — the one read
|
|
82
|
+
* that can tell a stored `undefined` apart from a missing key. Subscribes the
|
|
83
|
+
* same way {@link AtomMap.get} does.
|
|
84
|
+
*/
|
|
85
|
+
peek(key) {
|
|
86
|
+
const a = this.atoms.get(key);
|
|
87
|
+
if (!a) {
|
|
88
|
+
this.keyEpoch.get();
|
|
89
|
+
return UNINITIALIZED;
|
|
90
|
+
}
|
|
91
|
+
return a.get();
|
|
92
|
+
}
|
|
93
|
+
/** Replace `key`'s value with `fn` applied to it. Throws if `key` is absent. */
|
|
94
|
+
update(key, fn) {
|
|
95
|
+
const a = this.atoms.get(key);
|
|
96
|
+
const prev = a ? getWithoutCapture(a) : UNINITIALIZED;
|
|
97
|
+
if (!a || prev === UNINITIALIZED) {
|
|
98
|
+
throw new Error(`AtomMap ${this.name}: no entry for ${String(key)}`);
|
|
99
|
+
}
|
|
100
|
+
const next = fn(prev);
|
|
101
|
+
a.set(next);
|
|
102
|
+
return next;
|
|
103
|
+
}
|
|
104
|
+
/** Remove `key`. Returns whether it was there. */
|
|
105
|
+
delete(key) {
|
|
106
|
+
const a = this.atoms.get(key);
|
|
107
|
+
if (!a || getWithoutCapture(a) === UNINITIALIZED) return false;
|
|
108
|
+
transact(() => {
|
|
109
|
+
a.set(UNINITIALIZED);
|
|
110
|
+
this.keyEpoch.update((n) => n + 1);
|
|
111
|
+
});
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Remove several keys at once, in one transaction. Returns the keys that
|
|
116
|
+
* were actually there.
|
|
117
|
+
*
|
|
118
|
+
* Deleting in a loop wakes every dependent once per key; a bulk delete —
|
|
119
|
+
* clearing a selection, dropping a page's shapes — should wake them once.
|
|
120
|
+
*/
|
|
121
|
+
deleteMany(keys) {
|
|
122
|
+
const deleted = [];
|
|
123
|
+
transact(() => {
|
|
124
|
+
for (const key of keys) {
|
|
125
|
+
const a = this.atoms.get(key);
|
|
126
|
+
if (!a) continue;
|
|
127
|
+
const previous = getWithoutCapture(a);
|
|
128
|
+
if (previous === UNINITIALIZED) continue;
|
|
129
|
+
a.set(UNINITIALIZED);
|
|
130
|
+
deleted.push([key, previous]);
|
|
131
|
+
}
|
|
132
|
+
if (deleted.length > 0) this.keyEpoch.update((n) => n + 1);
|
|
133
|
+
});
|
|
134
|
+
return deleted;
|
|
135
|
+
}
|
|
136
|
+
get [Symbol.toStringTag]() {
|
|
137
|
+
return "AtomMap";
|
|
138
|
+
}
|
|
139
|
+
/** Remove every entry. */
|
|
140
|
+
clear() {
|
|
141
|
+
transact(() => {
|
|
142
|
+
let removedAny = false;
|
|
143
|
+
for (const a of this.atoms.values()) {
|
|
144
|
+
if (getWithoutCapture(a) === UNINITIALIZED) continue;
|
|
145
|
+
a.set(UNINITIALIZED);
|
|
146
|
+
removedAny = true;
|
|
147
|
+
}
|
|
148
|
+
if (removedAny) this.keyEpoch.update((n) => n + 1);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/** How many entries the map holds. */
|
|
152
|
+
get size() {
|
|
153
|
+
this.keyEpoch.get();
|
|
154
|
+
let n = 0;
|
|
155
|
+
for (const a of this.atoms.values()) if (a.get() !== UNINITIALIZED) n++;
|
|
156
|
+
return n;
|
|
157
|
+
}
|
|
158
|
+
/** The live keys, in insertion order. */
|
|
159
|
+
*keys() {
|
|
160
|
+
this.keyEpoch.get();
|
|
161
|
+
for (const [key, a] of this.atoms) {
|
|
162
|
+
if (a.get() !== UNINITIALIZED) yield key;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/** The live values, in insertion order. */
|
|
166
|
+
*values() {
|
|
167
|
+
this.keyEpoch.get();
|
|
168
|
+
for (const a of this.atoms.values()) {
|
|
169
|
+
const value = a.get();
|
|
170
|
+
if (value !== UNINITIALIZED) yield value;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** The live entries, in insertion order. */
|
|
174
|
+
*entries() {
|
|
175
|
+
this.keyEpoch.get();
|
|
176
|
+
for (const [key, a] of this.atoms) {
|
|
177
|
+
const value = a.get();
|
|
178
|
+
if (value !== UNINITIALIZED) yield [key, value];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
[Symbol.iterator]() {
|
|
182
|
+
return this.entries();
|
|
183
|
+
}
|
|
184
|
+
forEach(fn, thisArg) {
|
|
185
|
+
for (const [key, value] of this.entries()) fn.call(thisArg, value, key, this);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// src/AtomSet.ts
|
|
190
|
+
var AtomSet = class {
|
|
191
|
+
constructor(name, values) {
|
|
192
|
+
this.name = name;
|
|
193
|
+
this.membershipEpoch = atom(`${name}:members`, 0);
|
|
194
|
+
if (values) {
|
|
195
|
+
for (const value of values) this.atoms.set(value, atom(`${name}:${String(value)}`, true));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
name;
|
|
199
|
+
/**
|
|
200
|
+
* One atom per value ever seen. A removed value keeps its atom, holding
|
|
201
|
+
* `false`, so anything already subscribed to that value is told it left
|
|
202
|
+
* rather than silently going stale.
|
|
203
|
+
*/
|
|
204
|
+
atoms = /* @__PURE__ */ new Map();
|
|
205
|
+
/** Bumped whenever membership changes, for readers of the set's shape. */
|
|
206
|
+
membershipEpoch;
|
|
207
|
+
get [Symbol.toStringTag]() {
|
|
208
|
+
return "AtomSet";
|
|
209
|
+
}
|
|
210
|
+
/** Whether `value` is a member. */
|
|
211
|
+
has(value) {
|
|
212
|
+
const a = this.atoms.get(value);
|
|
213
|
+
if (!a) {
|
|
214
|
+
this.membershipEpoch.get();
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
return a.get();
|
|
218
|
+
}
|
|
219
|
+
/** Add `value`, returning the set so calls can be chained. */
|
|
220
|
+
add(value) {
|
|
221
|
+
const a = this.atoms.get(value);
|
|
222
|
+
if (a) {
|
|
223
|
+
if (getWithoutCapture(a)) return this;
|
|
224
|
+
transact(() => {
|
|
225
|
+
a.set(true);
|
|
226
|
+
this.membershipEpoch.update((n) => n + 1);
|
|
227
|
+
});
|
|
228
|
+
} else {
|
|
229
|
+
this.atoms.set(value, atom(`${this.name}:${String(value)}`, true));
|
|
230
|
+
this.membershipEpoch.update((n) => n + 1);
|
|
231
|
+
}
|
|
232
|
+
return this;
|
|
233
|
+
}
|
|
234
|
+
/** Remove `value`. Returns whether it was a member. */
|
|
235
|
+
delete(value) {
|
|
236
|
+
const a = this.atoms.get(value);
|
|
237
|
+
if (!a || !getWithoutCapture(a)) return false;
|
|
238
|
+
transact(() => {
|
|
239
|
+
a.set(false);
|
|
240
|
+
this.membershipEpoch.update((n) => n + 1);
|
|
241
|
+
});
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
/** Remove every member. */
|
|
245
|
+
clear() {
|
|
246
|
+
transact(() => {
|
|
247
|
+
let removedAny = false;
|
|
248
|
+
for (const a of this.atoms.values()) {
|
|
249
|
+
if (!getWithoutCapture(a)) continue;
|
|
250
|
+
a.set(false);
|
|
251
|
+
removedAny = true;
|
|
252
|
+
}
|
|
253
|
+
if (removedAny) this.membershipEpoch.update((n) => n + 1);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
/** How many members the set holds. */
|
|
257
|
+
get size() {
|
|
258
|
+
this.membershipEpoch.get();
|
|
259
|
+
let n = 0;
|
|
260
|
+
for (const a of this.atoms.values()) if (a.get()) n++;
|
|
261
|
+
return n;
|
|
262
|
+
}
|
|
263
|
+
/** The live members, in insertion order. */
|
|
264
|
+
*keys() {
|
|
265
|
+
this.membershipEpoch.get();
|
|
266
|
+
for (const [value, a] of this.atoms) if (a.get()) yield value;
|
|
267
|
+
}
|
|
268
|
+
/** The live members, in insertion order. Same as {@link AtomSet.keys}, as on `Set`. */
|
|
269
|
+
values() {
|
|
270
|
+
return this.keys();
|
|
271
|
+
}
|
|
272
|
+
/** `[value, value]` pairs, as on `Set`. */
|
|
273
|
+
*entries() {
|
|
274
|
+
for (const value of this.keys()) yield [value, value];
|
|
275
|
+
}
|
|
276
|
+
[Symbol.iterator]() {
|
|
277
|
+
return this.keys();
|
|
278
|
+
}
|
|
279
|
+
forEach(fn, thisArg) {
|
|
280
|
+
for (const value of this.keys()) fn.call(thisArg, value, value, this);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
export { AtomMap, AtomSet };
|
|
2
285
|
//# sourceMappingURL=index.js.map
|
|
3
286
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
1
|
+
{"version":3,"sources":["../src/AtomMap.ts","../src/AtomSet.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * A reactive `Map`.\n *\n * Reading one key subscribes to that key alone, so a computed that looks up a\n * single entry is not woken by every unrelated write. Reading the map's shape\n * (`size`, iteration, a miss on `get`) subscribes to the key set instead, so a\n * later insert or delete does wake it.\n */\nimport { atom, getWithoutCapture, transact, UNINITIALIZED, type Atom, type Uninitialized } from \"./core\"\n\n/** A reactive map from `K` to `V`, backed by one atom per key. */\nexport class AtomMap<K, V> {\n\tprivate readonly atoms = new Map<K, Atom<V | Uninitialized>>()\n\t/**\n\t * Bumped whenever a key is added or removed. Anything that depends on which\n\t * keys exist — `size`, iteration, a `get` that missed — reads this, so it is\n\t * invalidated by an insert without being invalidated by every value write.\n\t */\n\tprivate readonly keyEpoch: Atom<number>\n\n\tconstructor(\n\t\treadonly name: string,\n\t\tentries?: Iterable<readonly [K, V]>,\n\t) {\n\t\tthis.keyEpoch = atom(`${name}:keys`, 0)\n\t\tif (entries) {\n\t\t\tfor (const [key, value] of entries) {\n\t\t\t\tthis.atoms.set(key, atom(`${name}:${String(key)}`, value as V | Uninitialized))\n\t\t\t}\n\t\t}\n\t}\n\n\t/** The value for `key`, or `undefined` when there is none. */\n\tget(key: K): V | undefined {\n\t\tconst a = this.atoms.get(key)\n\t\tif (!a) {\n\t\t\t// Depend on the key set, so inserting `key` later invalidates this read.\n\t\t\tthis.keyEpoch.get()\n\t\t\treturn undefined\n\t\t}\n\t\tconst value = a.get()\n\t\treturn value === UNINITIALIZED ? undefined : value\n\t}\n\n\t/** Whether `key` has a value. */\n\thas(key: K): boolean {\n\t\tconst a = this.atoms.get(key)\n\t\tif (!a) {\n\t\t\tthis.keyEpoch.get()\n\t\t\treturn false\n\t\t}\n\t\treturn a.get() !== UNINITIALIZED\n\t}\n\n\t/** Set `key`, returning the map so calls can be chained. */\n\tset(key: K, value: V): this {\n\t\tconst a = this.atoms.get(key)\n\t\tif (a) {\n\t\t\t// One transaction, so a subscriber that reads both the value and the key\n\t\t\t// set (`size`, iteration) is notified once rather than twice.\n\t\t\ttransact(() => {\n\t\t\t\tconst wasAbsent = getWithoutCapture(a) === UNINITIALIZED\n\t\t\t\ta.set(value)\n\t\t\t\tif (wasAbsent) this.keyEpoch.update((n) => n + 1)\n\t\t\t})\n\t\t} else {\n\t\t\tthis.atoms.set(key, atom(`${this.name}:${String(key)}`, value as V | Uninitialized))\n\t\t\tthis.keyEpoch.update((n) => n + 1)\n\t\t}\n\t\treturn this\n\t}\n\n\t/**\n\t * The value for `key`, inserting `defaultValue` first if there is none.\n\t *\n\t * `defaultValue` is always evaluated by the caller; use\n\t * {@link AtomMap.getOrInsertComputed} when producing it is expensive.\n\t */\n\tgetOrInsert(key: K, defaultValue: V): V {\n\t\tconst existing = this.peek(key)\n\t\tif (existing !== UNINITIALIZED) return existing\n\t\tthis.set(key, defaultValue)\n\t\treturn defaultValue\n\t}\n\n\t/**\n\t * The value for `key`, inserting `create()` first if there is none.\n\t * `create` runs **only** when the key is absent.\n\t */\n\tgetOrInsertComputed(key: K, create: (key: K) => V): V {\n\t\tconst existing = this.peek(key)\n\t\tif (existing !== UNINITIALIZED) return existing\n\t\tconst value = create(key)\n\t\tthis.set(key, value)\n\t\treturn value\n\t}\n\n\t/**\n\t * The value for `key`, or `UNINITIALIZED` when there is none — the one read\n\t * that can tell a stored `undefined` apart from a missing key. Subscribes the\n\t * same way {@link AtomMap.get} does.\n\t */\n\tprivate peek(key: K): V | Uninitialized {\n\t\tconst a = this.atoms.get(key)\n\t\tif (!a) {\n\t\t\tthis.keyEpoch.get()\n\t\t\treturn UNINITIALIZED\n\t\t}\n\t\treturn a.get()\n\t}\n\n\t/** Replace `key`'s value with `fn` applied to it. Throws if `key` is absent. */\n\tupdate(key: K, fn: (prev: V) => V): V {\n\t\tconst a = this.atoms.get(key)\n\t\tconst prev = a ? getWithoutCapture(a) : UNINITIALIZED\n\t\tif (!a || prev === UNINITIALIZED) {\n\t\t\tthrow new Error(`AtomMap ${this.name}: no entry for ${String(key)}`)\n\t\t}\n\t\tconst next = fn(prev)\n\t\ta.set(next)\n\t\treturn next\n\t}\n\n\t/** Remove `key`. Returns whether it was there. */\n\tdelete(key: K): boolean {\n\t\tconst a = this.atoms.get(key)\n\t\tif (!a || getWithoutCapture(a) === UNINITIALIZED) return false\n\t\ttransact(() => {\n\t\t\t// The atom is kept and emptied rather than dropped, so anything already\n\t\t\t// subscribed to this key is notified of the removal.\n\t\t\ta.set(UNINITIALIZED)\n\t\t\tthis.keyEpoch.update((n) => n + 1)\n\t\t})\n\t\treturn true\n\t}\n\n\t/**\n\t * Remove several keys at once, in one transaction. Returns the keys that\n\t * were actually there.\n\t *\n\t * Deleting in a loop wakes every dependent once per key; a bulk delete —\n\t * clearing a selection, dropping a page's shapes — should wake them once.\n\t */\n\tdeleteMany(keys: Iterable<K>): [K, V][] {\n\t\tconst deleted: [K, V][] = []\n\t\ttransact(() => {\n\t\t\tfor (const key of keys) {\n\t\t\t\tconst a = this.atoms.get(key)\n\t\t\t\tif (!a) continue\n\t\t\t\tconst previous = getWithoutCapture(a)\n\t\t\t\tif (previous === UNINITIALIZED) continue\n\t\t\t\ta.set(UNINITIALIZED)\n\t\t\t\tdeleted.push([key, previous])\n\t\t\t}\n\t\t\tif (deleted.length > 0) this.keyEpoch.update((n) => n + 1)\n\t\t})\n\t\treturn deleted\n\t}\n\n\tget [Symbol.toStringTag](): string {\n\t\treturn \"AtomMap\"\n\t}\n\n\t/** Remove every entry. */\n\tclear(): void {\n\t\ttransact(() => {\n\t\t\tlet removedAny = false\n\t\t\tfor (const a of this.atoms.values()) {\n\t\t\t\tif (getWithoutCapture(a) === UNINITIALIZED) continue\n\t\t\t\ta.set(UNINITIALIZED)\n\t\t\t\tremovedAny = true\n\t\t\t}\n\t\t\tif (removedAny) this.keyEpoch.update((n) => n + 1)\n\t\t})\n\t}\n\n\t/** How many entries the map holds. */\n\tget size(): number {\n\t\tthis.keyEpoch.get()\n\t\tlet n = 0\n\t\tfor (const a of this.atoms.values()) if (a.get() !== UNINITIALIZED) n++\n\t\treturn n\n\t}\n\n\t/** The live keys, in insertion order. */\n\t*keys(): IterableIterator<K> {\n\t\tthis.keyEpoch.get()\n\t\tfor (const [key, a] of this.atoms) {\n\t\t\tif (a.get() !== UNINITIALIZED) yield key\n\t\t}\n\t}\n\n\t/** The live values, in insertion order. */\n\t*values(): IterableIterator<V> {\n\t\tthis.keyEpoch.get()\n\t\tfor (const a of this.atoms.values()) {\n\t\t\tconst value = a.get()\n\t\t\tif (value !== UNINITIALIZED) yield value\n\t\t}\n\t}\n\n\t/** The live entries, in insertion order. */\n\t*entries(): IterableIterator<[K, V]> {\n\t\tthis.keyEpoch.get()\n\t\tfor (const [key, a] of this.atoms) {\n\t\t\tconst value = a.get()\n\t\t\tif (value !== UNINITIALIZED) yield [key, value]\n\t\t}\n\t}\n\n\t[Symbol.iterator](): IterableIterator<[K, V]> {\n\t\treturn this.entries()\n\t}\n\n\tforEach(fn: (value: V, key: K, map: AtomMap<K, V>) => void, thisArg?: unknown): void {\n\t\tfor (const [key, value] of this.entries()) fn.call(thisArg, value, key, this)\n\t}\n}\n","/**\n * A reactive `Set`.\n *\n * The counterpart of {@link AtomMap} for membership: asking whether one value\n * is in the set subscribes to that value alone, so a computed that watches a\n * single id is not woken every time an unrelated one is added. Reading the\n * set's shape (`size`, iteration) subscribes to the membership epoch instead.\n *\n * That split is the whole reason to reach for this over a plain `Set` in an\n * atom: `atom(\"selection\", new Set(...))` invalidates every reader on every\n * change, however small.\n */\nimport { atom, getWithoutCapture, transact, type Atom } from \"./core\"\n\n/** A reactive set of `T`, backed by one boolean atom per member. */\nexport class AtomSet<T> {\n\t/**\n\t * One atom per value ever seen. A removed value keeps its atom, holding\n\t * `false`, so anything already subscribed to that value is told it left\n\t * rather than silently going stale.\n\t */\n\tprivate readonly atoms = new Map<T, Atom<boolean>>()\n\t/** Bumped whenever membership changes, for readers of the set's shape. */\n\tprivate readonly membershipEpoch: Atom<number>\n\n\tconstructor(\n\t\treadonly name: string,\n\t\tvalues?: Iterable<T>,\n\t) {\n\t\tthis.membershipEpoch = atom(`${name}:members`, 0)\n\t\tif (values) {\n\t\t\tfor (const value of values) this.atoms.set(value, atom(`${name}:${String(value)}`, true))\n\t\t}\n\t}\n\n\tget [Symbol.toStringTag](): string {\n\t\treturn \"AtomSet\"\n\t}\n\n\t/** Whether `value` is a member. */\n\thas(value: T): boolean {\n\t\tconst a = this.atoms.get(value)\n\t\tif (!a) {\n\t\t\t// Depend on membership, so adding `value` later invalidates this read.\n\t\t\tthis.membershipEpoch.get()\n\t\t\treturn false\n\t\t}\n\t\treturn a.get()\n\t}\n\n\t/** Add `value`, returning the set so calls can be chained. */\n\tadd(value: T): this {\n\t\tconst a = this.atoms.get(value)\n\t\tif (a) {\n\t\t\tif (getWithoutCapture(a)) return this\n\t\t\t// One transaction, so a reader of both the member and the set's shape\n\t\t\t// is notified once rather than twice.\n\t\t\ttransact(() => {\n\t\t\t\ta.set(true)\n\t\t\t\tthis.membershipEpoch.update((n) => n + 1)\n\t\t\t})\n\t\t} else {\n\t\t\tthis.atoms.set(value, atom(`${this.name}:${String(value)}`, true))\n\t\t\tthis.membershipEpoch.update((n) => n + 1)\n\t\t}\n\t\treturn this\n\t}\n\n\t/** Remove `value`. Returns whether it was a member. */\n\tdelete(value: T): boolean {\n\t\tconst a = this.atoms.get(value)\n\t\tif (!a || !getWithoutCapture(a)) return false\n\t\ttransact(() => {\n\t\t\ta.set(false)\n\t\t\tthis.membershipEpoch.update((n) => n + 1)\n\t\t})\n\t\treturn true\n\t}\n\n\t/** Remove every member. */\n\tclear(): void {\n\t\ttransact(() => {\n\t\t\tlet removedAny = false\n\t\t\tfor (const a of this.atoms.values()) {\n\t\t\t\tif (!getWithoutCapture(a)) continue\n\t\t\t\ta.set(false)\n\t\t\t\tremovedAny = true\n\t\t\t}\n\t\t\tif (removedAny) this.membershipEpoch.update((n) => n + 1)\n\t\t})\n\t}\n\n\t/** How many members the set holds. */\n\tget size(): number {\n\t\tthis.membershipEpoch.get()\n\t\tlet n = 0\n\t\tfor (const a of this.atoms.values()) if (a.get()) n++\n\t\treturn n\n\t}\n\n\t/** The live members, in insertion order. */\n\t*keys(): IterableIterator<T> {\n\t\tthis.membershipEpoch.get()\n\t\tfor (const [value, a] of this.atoms) if (a.get()) yield value\n\t}\n\n\t/** The live members, in insertion order. Same as {@link AtomSet.keys}, as on `Set`. */\n\tvalues(): IterableIterator<T> {\n\t\treturn this.keys()\n\t}\n\n\t/** `[value, value]` pairs, as on `Set`. */\n\t*entries(): IterableIterator<[T, T]> {\n\t\tfor (const value of this.keys()) yield [value, value]\n\t}\n\n\t[Symbol.iterator](): IterableIterator<T> {\n\t\treturn this.keys()\n\t}\n\n\tforEach(fn: (value: T, value2: T, set: AtomSet<T>) => void, thisArg?: unknown): void {\n\t\tfor (const value of this.keys()) fn.call(thisArg, value, value, this)\n\t}\n}\n"]}
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ComponentType } from 'react';
|
|
2
|
-
import { Atom, Computed, ComputedOptions, Signal } from './
|
|
2
|
+
import { A as Atom, b as Computed, c as ComputedOptions, S as Signal } from './core-DVexut58.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Subscribes the component to a signal and returns its current value.
|
|
@@ -20,6 +20,24 @@ declare function useComputed<T>(name: string, fn: () => T, options: ComputedOpti
|
|
|
20
20
|
declare function useReactor(name: string, fn: () => void, deps: unknown[]): void;
|
|
21
21
|
/** Like `useReactor` but re-runs synchronously as soon as a dependency changes. */
|
|
22
22
|
declare function useQuickReactor(name: string, fn: () => void, deps: unknown[]): void;
|
|
23
|
+
/**
|
|
24
|
+
* Run `render` with signal tracking on, and re-render the calling component
|
|
25
|
+
* whenever anything it read changes.
|
|
26
|
+
*
|
|
27
|
+
* This is the hook {@link track} is built from, exposed for the cases the
|
|
28
|
+
* wrapper cannot cover: a component that only wants part of its body tracked,
|
|
29
|
+
* a class component's render delegate, or a render function received as a
|
|
30
|
+
* prop, where there is no component to wrap.
|
|
31
|
+
*
|
|
32
|
+
* ```tsx
|
|
33
|
+
* function Panel({ shape }: { shape: TLShape }) {
|
|
34
|
+
* return useStateTracking("Panel", () => <span>{editor.getShapePageBounds(shape)?.w}</span>)
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* The name is for debugging only; it shows up in {@link whyAmIRunning} output.
|
|
39
|
+
*/
|
|
40
|
+
declare function useStateTracking<T>(name: string, render: () => T): T;
|
|
23
41
|
/**
|
|
24
42
|
* Wraps a function component so that it re-renders whenever any signal read
|
|
25
43
|
* during its render changes. The result is memoized on props. Supports plain
|
|
@@ -27,4 +45,4 @@ declare function useQuickReactor(name: string, fn: () => void, deps: unknown[]):
|
|
|
27
45
|
*/
|
|
28
46
|
declare function track<C extends ComponentType<any>>(Component: C): C;
|
|
29
47
|
|
|
30
|
-
export { track, useAtom, useComputed, useQuickReactor, useReactor, useValue };
|
|
48
|
+
export { track, useAtom, useComputed, useQuickReactor, useReactor, useStateTracking, useValue };
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EMPTY_ARRAY, computed, atom, reactor, getWithoutCapture, subscribeToSignal
|
|
1
|
+
import { EMPTY_ARRAY, computed, atom, reactor, RenderTracker, getWithoutCapture, subscribeToSignal } from './chunk-KGMTQKZV.js';
|
|
2
2
|
import { useMemo, useSyncExternalStore, useState, useEffect, memo, forwardRef } from 'react';
|
|
3
3
|
|
|
4
4
|
function useSignalStore(signal) {
|
|
@@ -62,6 +62,14 @@ function useTrackedRender(name, render, props, ref) {
|
|
|
62
62
|
useSyncExternalStore(tracker.subscribe, tracker.getSnapshot, tracker.getSnapshot);
|
|
63
63
|
return tracker.track(render, props, ref);
|
|
64
64
|
}
|
|
65
|
+
function useStateTracking(name, render) {
|
|
66
|
+
const [tracker] = useState(() => new RenderTracker(name));
|
|
67
|
+
useSyncExternalStore(tracker.subscribe, tracker.getSnapshot, tracker.getSnapshot);
|
|
68
|
+
return tracker.track(runRenderFn, render, null);
|
|
69
|
+
}
|
|
70
|
+
function runRenderFn(fn) {
|
|
71
|
+
return fn();
|
|
72
|
+
}
|
|
65
73
|
function track(Component) {
|
|
66
74
|
if (typeof Component === "function") {
|
|
67
75
|
const fn = Component;
|
|
@@ -90,6 +98,6 @@ function track(Component) {
|
|
|
90
98
|
throw new Error("track() expects a function component, or one wrapped in memo() or forwardRef()");
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
export { track, useAtom, useComputed, useQuickReactor, useReactor, useValue };
|
|
101
|
+
export { track, useAtom, useComputed, useQuickReactor, useReactor, useStateTracking, useValue };
|
|
94
102
|
//# sourceMappingURL=react.js.map
|
|
95
103
|
//# sourceMappingURL=react.js.map
|