@praxisjs/core 2.1.0 → 2.3.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/CHANGELOG.md +30 -0
- package/dist/component/internals.d.ts +12 -2
- package/dist/component/internals.d.ts.map +1 -1
- package/dist/component/internals.js +25 -7
- package/dist/component/internals.js.map +1 -1
- package/dist/internal.d.ts +3 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +3 -2
- package/dist/internal.js.map +1 -1
- package/dist/reactive/history.d.ts +2 -0
- package/dist/reactive/history.d.ts.map +1 -1
- package/dist/reactive/history.js +2 -1
- package/dist/reactive/history.js.map +1 -1
- package/dist/signal/batch.d.ts +1 -3
- package/dist/signal/batch.d.ts.map +1 -1
- package/dist/signal/batch.js +4 -16
- package/dist/signal/batch.js.map +1 -1
- package/dist/signal/computed.d.ts.map +1 -1
- package/dist/signal/computed.js +19 -61
- package/dist/signal/computed.js.map +1 -1
- package/dist/signal/effect.d.ts +6 -12
- package/dist/signal/effect.d.ts.map +1 -1
- package/dist/signal/effect.js +11 -74
- package/dist/signal/effect.js.map +1 -1
- package/dist/signal/graph.d.ts +111 -0
- package/dist/signal/graph.d.ts.map +1 -0
- package/dist/signal/graph.js +513 -0
- package/dist/signal/graph.js.map +1 -0
- package/dist/signal/index.d.ts +2 -1
- package/dist/signal/index.d.ts.map +1 -1
- package/dist/signal/index.js +1 -0
- package/dist/signal/index.js.map +1 -1
- package/dist/signal/peek.d.ts.map +1 -1
- package/dist/signal/peek.js +2 -9
- package/dist/signal/peek.js.map +1 -1
- package/dist/signal/persisted.d.ts +11 -1
- package/dist/signal/persisted.d.ts.map +1 -1
- package/dist/signal/persisted.js +39 -5
- package/dist/signal/persisted.js.map +1 -1
- package/dist/signal/selector.d.ts +5 -0
- package/dist/signal/selector.d.ts.map +1 -0
- package/dist/signal/selector.js +128 -0
- package/dist/signal/selector.js.map +1 -0
- package/dist/signal/signal.d.ts +0 -5
- package/dist/signal/signal.d.ts.map +1 -1
- package/dist/signal/signal.js +11 -81
- package/dist/signal/signal.js.map +1 -1
- package/package.json +13 -4
- package/src/component/internals.ts +35 -10
- package/src/internal.ts +27 -0
- package/src/reactive/history.ts +4 -1
- package/src/signal/batch.ts +5 -19
- package/src/signal/computed.ts +20 -63
- package/src/signal/effect.ts +12 -83
- package/src/signal/graph.ts +562 -0
- package/src/signal/index.ts +2 -1
- package/src/signal/peek.ts +2 -8
- package/src/signal/persisted.ts +54 -5
- package/src/signal/selector.ts +140 -0
- package/src/signal/signal.ts +11 -79
- package/dist/__tests__/batch.test.d.ts +0 -2
- package/dist/__tests__/batch.test.d.ts.map +0 -1
- package/dist/__tests__/batch.test.js +0 -73
- package/dist/__tests__/batch.test.js.map +0 -1
- package/dist/__tests__/component.test.d.ts +0 -2
- package/dist/__tests__/component.test.d.ts.map +0 -1
- package/dist/__tests__/component.test.js +0 -124
- package/dist/__tests__/component.test.js.map +0 -1
- package/dist/__tests__/computed.test.d.ts +0 -2
- package/dist/__tests__/computed.test.d.ts.map +0 -1
- package/dist/__tests__/computed.test.js +0 -252
- package/dist/__tests__/computed.test.js.map +0 -1
- package/dist/__tests__/effect.test.d.ts +0 -2
- package/dist/__tests__/effect.test.d.ts.map +0 -1
- package/dist/__tests__/effect.test.js +0 -189
- package/dist/__tests__/effect.test.js.map +0 -1
- package/dist/__tests__/peek.test.d.ts +0 -2
- package/dist/__tests__/peek.test.d.ts.map +0 -1
- package/dist/__tests__/peek.test.js +0 -26
- package/dist/__tests__/peek.test.js.map +0 -1
- package/dist/__tests__/persisted-signal.test.d.ts +0 -2
- package/dist/__tests__/persisted-signal.test.d.ts.map +0 -1
- package/dist/__tests__/persisted-signal.test.js +0 -156
- package/dist/__tests__/persisted-signal.test.js.map +0 -1
- package/dist/__tests__/reactive.test.d.ts +0 -2
- package/dist/__tests__/reactive.test.d.ts.map +0 -1
- package/dist/__tests__/reactive.test.js +0 -264
- package/dist/__tests__/reactive.test.js.map +0 -1
- package/dist/__tests__/resource-focus.test.d.ts +0 -2
- package/dist/__tests__/resource-focus.test.d.ts.map +0 -1
- package/dist/__tests__/resource-focus.test.js +0 -50
- package/dist/__tests__/resource-focus.test.js.map +0 -1
- package/dist/__tests__/resource-server-pass.test.d.ts +0 -2
- package/dist/__tests__/resource-server-pass.test.d.ts.map +0 -1
- package/dist/__tests__/resource-server-pass.test.js +0 -78
- package/dist/__tests__/resource-server-pass.test.js.map +0 -1
- package/dist/__tests__/resource.test.d.ts +0 -2
- package/dist/__tests__/resource.test.d.ts.map +0 -1
- package/dist/__tests__/resource.test.js +0 -336
- package/dist/__tests__/resource.test.js.map +0 -1
- package/dist/__tests__/signal.test.d.ts +0 -2
- package/dist/__tests__/signal.test.d.ts.map +0 -1
- package/dist/__tests__/signal.test.js +0 -186
- package/dist/__tests__/signal.test.js.map +0 -1
- package/dist/__tests__/synced.test.d.ts +0 -2
- package/dist/__tests__/synced.test.d.ts.map +0 -1
- package/dist/__tests__/synced.test.js +0 -142
- package/dist/__tests__/synced.test.js.map +0 -1
- package/dist/__tests__/track.test.d.ts +0 -2
- package/dist/__tests__/track.test.d.ts.map +0 -1
- package/dist/__tests__/track.test.js +0 -54
- package/dist/__tests__/track.test.js.map +0 -1
- package/src/__tests__/batch.test.ts +0 -87
- package/src/__tests__/component.test.ts +0 -165
- package/src/__tests__/computed.test.ts +0 -290
- package/src/__tests__/effect.test.ts +0 -226
- package/src/__tests__/peek.test.ts +0 -31
- package/src/__tests__/persisted-signal.test.ts +0 -194
- package/src/__tests__/reactive.test.ts +0 -305
- package/src/__tests__/resource-focus.test.ts +0 -61
- package/src/__tests__/resource-server-pass.test.ts +0 -90
- package/src/__tests__/resource.test.ts +0 -421
- package/src/__tests__/signal.test.ts +0 -213
- package/src/__tests__/synced.test.ts +0 -173
- package/src/__tests__/track.test.ts +0 -61
- package/tsconfig.json +0 -8
package/dist/signal/index.js
CHANGED
package/dist/signal/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/signal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/signal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAqD,eAAe,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAqB,YAAY,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAsC,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peek.d.ts","sourceRoot":"","sources":["../../src/signal/peek.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAIzD,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"peek.d.ts","sourceRoot":"","sources":["../../src/signal/peek.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAIzD,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAE1D"}
|
package/dist/signal/peek.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { untrack } from "./effect";
|
|
2
2
|
export function peek(source) {
|
|
3
|
-
|
|
4
|
-
runEffect(null);
|
|
5
|
-
try {
|
|
6
|
-
return source();
|
|
7
|
-
}
|
|
8
|
-
finally {
|
|
9
|
-
runEffect(prev);
|
|
10
|
-
}
|
|
3
|
+
return untrack(source);
|
|
11
4
|
}
|
|
12
5
|
//# sourceMappingURL=peek.js.map
|
package/dist/signal/peek.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peek.js","sourceRoot":"","sources":["../../src/signal/peek.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"peek.js","sourceRoot":"","sources":["../../src/signal/peek.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,MAAM,UAAU,IAAI,CAAI,MAA+B;IACrD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -3,6 +3,16 @@ export interface PersistedSignalOptions<T> {
|
|
|
3
3
|
serialize?: (value: T) => string;
|
|
4
4
|
deserialize?: (value: string) => T;
|
|
5
5
|
syncTabs?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Milliseconds to hold back writes to `localStorage`. Writes within the window are coalesced
|
|
8
|
+
* into one, using the latest value; pending data is flushed on `pagehide` and `close()`.
|
|
9
|
+
* `0` (default) writes on every `set`.
|
|
10
|
+
*/
|
|
11
|
+
writeDelay?: number;
|
|
6
12
|
}
|
|
7
|
-
export
|
|
13
|
+
export interface PersistedSignal<T> extends Signal<T> {
|
|
14
|
+
/** Stops listening for `storage` events from other tabs. The signal keeps working locally. */
|
|
15
|
+
close(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function persistedSignal<T>(key: string, initialValue: T, options?: PersistedSignalOptions<T>): PersistedSignal<T>;
|
|
8
18
|
//# sourceMappingURL=persisted.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persisted.d.ts","sourceRoot":"","sources":["../../src/signal/persisted.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI/C,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC;IACjC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"persisted.d.ts","sourceRoot":"","sources":["../../src/signal/persisted.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI/C,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC;IACjC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,CAAE,SAAQ,MAAM,CAAC,CAAC,CAAC;IACnD,8FAA8F;IAC9F,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,eAAe,CAAC,CAAC,EAC/B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,EACf,OAAO,GAAE,sBAAsB,CAAC,CAAC,CAAM,sBA8GxC"}
|
package/dist/signal/persisted.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { signal } from "./signal";
|
|
2
2
|
export function persistedSignal(key, initialValue, options = {}) {
|
|
3
|
-
const { serialize = JSON.stringify, deserialize = JSON.parse, syncTabs = true, } = options;
|
|
3
|
+
const { serialize = JSON.stringify, deserialize = JSON.parse, syncTabs = true, writeDelay = 0, } = options;
|
|
4
4
|
function getStoredValue() {
|
|
5
5
|
try {
|
|
6
6
|
const stored = localStorage.getItem(key);
|
|
@@ -24,23 +24,47 @@ export function persistedSignal(key, initialValue, options = {}) {
|
|
|
24
24
|
console.warn(`Failed to serialize value for key "${key}":`, e);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
let pendingWrite;
|
|
28
|
+
let writeTimer;
|
|
29
|
+
function flushWrite() {
|
|
30
|
+
clearTimeout(writeTimer);
|
|
31
|
+
writeTimer = undefined;
|
|
32
|
+
if (!pendingWrite)
|
|
33
|
+
return;
|
|
34
|
+
const { value } = pendingWrite;
|
|
35
|
+
pendingWrite = undefined;
|
|
36
|
+
setStoredValue(value);
|
|
37
|
+
}
|
|
38
|
+
// The timer starts at the first write of a burst (throttle, not debounce) so a value that keeps
|
|
39
|
+
// changing still reaches storage within `writeDelay`.
|
|
40
|
+
function scheduleWrite(value) {
|
|
41
|
+
if (writeDelay <= 0) {
|
|
42
|
+
setStoredValue(value);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
pendingWrite = { value };
|
|
46
|
+
writeTimer ??= setTimeout(flushWrite, writeDelay);
|
|
47
|
+
}
|
|
27
48
|
const inner = signal(getStoredValue());
|
|
28
49
|
function read() {
|
|
29
50
|
return inner();
|
|
30
51
|
}
|
|
31
52
|
function set(value) {
|
|
32
|
-
|
|
53
|
+
scheduleWrite(value);
|
|
33
54
|
inner.set(value);
|
|
34
55
|
}
|
|
35
56
|
function update(fh) {
|
|
36
57
|
const newValue = fh(inner());
|
|
37
|
-
|
|
58
|
+
scheduleWrite(newValue);
|
|
38
59
|
inner.set(newValue);
|
|
39
60
|
}
|
|
61
|
+
let removeStorageListener;
|
|
40
62
|
if (syncTabs) {
|
|
41
|
-
|
|
63
|
+
const onStorage = (event) => {
|
|
42
64
|
if (event.key !== key || event.storageArea !== localStorage)
|
|
43
65
|
return;
|
|
66
|
+
// Another tab's value wins; a still-pending local write would overwrite it later.
|
|
67
|
+
pendingWrite = undefined;
|
|
44
68
|
try {
|
|
45
69
|
const newValue = event.newValue
|
|
46
70
|
? deserialize(event.newValue)
|
|
@@ -51,13 +75,23 @@ export function persistedSignal(key, initialValue, options = {}) {
|
|
|
51
75
|
console.warn(`Failed to deserialize value for key "${key}" from storage event:`, e);
|
|
52
76
|
inner.set(initialValue);
|
|
53
77
|
}
|
|
54
|
-
}
|
|
78
|
+
};
|
|
79
|
+
window.addEventListener("storage", onStorage);
|
|
80
|
+
removeStorageListener = () => { window.removeEventListener("storage", onStorage); };
|
|
55
81
|
}
|
|
82
|
+
if (writeDelay > 0)
|
|
83
|
+
window.addEventListener("pagehide", flushWrite);
|
|
56
84
|
const source = read;
|
|
57
85
|
source.set = set;
|
|
58
86
|
source.update = update;
|
|
59
87
|
source.subscribe = inner.subscribe.bind(inner);
|
|
60
88
|
source.__isSignal = true;
|
|
89
|
+
source.close = () => {
|
|
90
|
+
removeStorageListener?.();
|
|
91
|
+
removeStorageListener = undefined;
|
|
92
|
+
window.removeEventListener("pagehide", flushWrite);
|
|
93
|
+
flushWrite();
|
|
94
|
+
};
|
|
61
95
|
return source;
|
|
62
96
|
}
|
|
63
97
|
//# sourceMappingURL=persisted.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persisted.js","sourceRoot":"","sources":["../../src/signal/persisted.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"persisted.js","sourceRoot":"","sources":["../../src/signal/persisted.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type Selector<K> = (key: K) => boolean;
|
|
2
|
+
export type SelectorEquals<K, T> = (key: K, value: T) => boolean;
|
|
3
|
+
export declare function selector<T>(source: () => T): Selector<T>;
|
|
4
|
+
export declare function selector<T, K>(source: () => T, equals: SelectorEquals<K, T>): Selector<K>;
|
|
5
|
+
//# sourceMappingURL=selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../../src/signal/selector.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;AAC9C,MAAM,MAAM,cAAc,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;AAyCjE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC1D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { activeSub, createEffect, DIRTY, disposeEffect, endBatch, notify, PENDING, startBatch, track, WatchedNode } from "./graph";
|
|
2
|
+
// Keys live in a Map, so the default comparison has to be the Map's own (SameValueZero): a key
|
|
3
|
+
// that compares equal must be the one the lookup finds, or a bucket would never be notified.
|
|
4
|
+
function sameValueZero(a, b) {
|
|
5
|
+
return a === b || (Number.isNaN(a) && Number.isNaN(b));
|
|
6
|
+
}
|
|
7
|
+
// One class for every selector's buckets (rather than one declared per selector call), so the
|
|
8
|
+
// graph code that reads and links them always sees the same shape.
|
|
9
|
+
// Declared and assigned rather than parameter properties, which compile to class fields that are
|
|
10
|
+
// defined one by one and make buckets (one per row reading a selector) slower to create.
|
|
11
|
+
class Bucket extends WatchedNode {
|
|
12
|
+
constructor(key, owner) {
|
|
13
|
+
super();
|
|
14
|
+
this.key = key;
|
|
15
|
+
this.owner = owner;
|
|
16
|
+
}
|
|
17
|
+
// A bucket drops itself from its selector once its last subscriber unlinks (re-run or disposal).
|
|
18
|
+
unwatched() {
|
|
19
|
+
this.owner.drop(this.key);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Keeps the bucket shape alive across full GCs; see the shape keepers in graph.ts.
|
|
23
|
+
Object.defineProperty(Bucket, "shapeKeeper", {
|
|
24
|
+
value: new Bucket(undefined, {
|
|
25
|
+
drop() {
|
|
26
|
+
// Never called: the keeper has no subscriber to lose.
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
export function selector(source, equals) {
|
|
31
|
+
const compare = equals ?? sameValueZero;
|
|
32
|
+
const buckets = new Map();
|
|
33
|
+
let current;
|
|
34
|
+
let sourceEffect = null;
|
|
35
|
+
let releaseScheduled = false;
|
|
36
|
+
// The source subscription goes with the last bucket, so removed rows leave nothing behind.
|
|
37
|
+
const owner = {
|
|
38
|
+
drop(key) {
|
|
39
|
+
buckets.delete(key);
|
|
40
|
+
if (buckets.size === 0)
|
|
41
|
+
scheduleRelease();
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
function changedBuckets(prev, next) {
|
|
45
|
+
const changed = [];
|
|
46
|
+
if (equals === undefined) {
|
|
47
|
+
if (sameValueZero(prev, next))
|
|
48
|
+
return changed;
|
|
49
|
+
const prevBucket = buckets.get(prev);
|
|
50
|
+
const nextBucket = buckets.get(next);
|
|
51
|
+
if (prevBucket !== undefined)
|
|
52
|
+
changed.push(prevBucket);
|
|
53
|
+
if (nextBucket !== undefined)
|
|
54
|
+
changed.push(nextBucket);
|
|
55
|
+
return changed;
|
|
56
|
+
}
|
|
57
|
+
for (const [key, bucket] of buckets) {
|
|
58
|
+
if (compare(key, prev) !== compare(key, next))
|
|
59
|
+
changed.push(bucket);
|
|
60
|
+
}
|
|
61
|
+
return changed;
|
|
62
|
+
}
|
|
63
|
+
// Batched so an effect reading several affected keys re-runs once, and so no subscriber runs
|
|
64
|
+
// (and unlinks) while the buckets are still being walked.
|
|
65
|
+
function notifyChanged(prev, next) {
|
|
66
|
+
const changed = changedBuckets(prev, next);
|
|
67
|
+
if (changed.length === 0)
|
|
68
|
+
return;
|
|
69
|
+
startBatch();
|
|
70
|
+
try {
|
|
71
|
+
for (const bucket of changed) {
|
|
72
|
+
bucket.version++;
|
|
73
|
+
notify(bucket);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
endBatch();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function activate() {
|
|
81
|
+
let initialized = false;
|
|
82
|
+
sourceEffect = createEffect(() => {
|
|
83
|
+
const next = source();
|
|
84
|
+
const prev = current;
|
|
85
|
+
current = next;
|
|
86
|
+
if (!initialized) {
|
|
87
|
+
initialized = true;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
notifyChanged(prev, next);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// An effect re-running unlinks its bucket right before reading the key again, and a list
|
|
94
|
+
// re-render unlinks every bucket before the new rows subscribe. Waiting a microtask keeps the
|
|
95
|
+
// source subscribed through both instead of stopping and re-reading it each time.
|
|
96
|
+
function scheduleRelease() {
|
|
97
|
+
if (releaseScheduled)
|
|
98
|
+
return;
|
|
99
|
+
releaseScheduled = true;
|
|
100
|
+
queueMicrotask(() => {
|
|
101
|
+
releaseScheduled = false;
|
|
102
|
+
if (buckets.size > 0)
|
|
103
|
+
return;
|
|
104
|
+
const stop = sourceEffect;
|
|
105
|
+
sourceEffect = null;
|
|
106
|
+
disposeEffect(stop);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return function select(key) {
|
|
110
|
+
if (activeSub === undefined) {
|
|
111
|
+
// `current` lags the source while the source effect waits to run (a write inside a batch, or
|
|
112
|
+
// a computed source, whose effects run a microtask later). A tracked read keeps the cached
|
|
113
|
+
// value, which the effect's run then corrects through the buckets; nothing corrects this one.
|
|
114
|
+
const fresh = sourceEffect !== null && (sourceEffect.flags & (DIRTY | PENDING)) === 0;
|
|
115
|
+
return compare(key, fresh ? current : source());
|
|
116
|
+
}
|
|
117
|
+
if (sourceEffect === null)
|
|
118
|
+
activate();
|
|
119
|
+
let bucket = buckets.get(key);
|
|
120
|
+
if (bucket === undefined) {
|
|
121
|
+
bucket = new Bucket(key, owner);
|
|
122
|
+
buckets.set(key, bucket);
|
|
123
|
+
}
|
|
124
|
+
track(bucket);
|
|
125
|
+
return compare(key, current);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector.js","sourceRoot":"","sources":["../../src/signal/selector.ts"],"names":[],"mappings":"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"}
|
package/dist/signal/signal.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
1
|
import type { Signal } from "@praxisjs/shared";
|
|
2
|
-
import { type Effect, type SubList, type SubscriberHolder } from "./effect";
|
|
3
|
-
export type { SubList } from "./effect";
|
|
4
|
-
export declare function addSub(holder: SubscriberHolder, eff: Effect): void;
|
|
5
|
-
export declare function removeSub(holder: SubscriberHolder, eff: Effect): void;
|
|
6
|
-
export declare function notifySubs(subs: Exclude<SubList, null>, batching: boolean): void;
|
|
7
2
|
export declare function signal<T>(initialValue: T): Signal<T>;
|
|
8
3
|
//# sourceMappingURL=signal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/signal/signal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/signal/signal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAK/C,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAepD"}
|
package/dist/signal/signal.js
CHANGED
|
@@ -1,86 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export function addSub(holder, eff) {
|
|
4
|
-
const subs = holder.subs;
|
|
5
|
-
if (subs === null) {
|
|
6
|
-
holder.subs = eff;
|
|
7
|
-
}
|
|
8
|
-
else if (typeof subs === "function") {
|
|
9
|
-
if (subs !== eff)
|
|
10
|
-
holder.subs = [subs, eff];
|
|
11
|
-
}
|
|
12
|
-
else {
|
|
13
|
-
if (!subs.includes(eff))
|
|
14
|
-
subs.push(eff);
|
|
15
|
-
}
|
|
16
|
-
recordDependency(eff, holder);
|
|
17
|
-
}
|
|
18
|
-
export function removeSub(holder, eff) {
|
|
19
|
-
const subs = holder.subs;
|
|
20
|
-
if (subs === null)
|
|
21
|
-
return;
|
|
22
|
-
if (typeof subs === "function") {
|
|
23
|
-
if (subs === eff)
|
|
24
|
-
holder.subs = null;
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
const idx = subs.indexOf(eff);
|
|
28
|
-
if (idx >= 0)
|
|
29
|
-
subs.splice(idx, 1);
|
|
30
|
-
if (subs.length === 0)
|
|
31
|
-
holder.subs = null;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
export function notifySubs(subs, batching) {
|
|
35
|
-
if (typeof subs === "function") {
|
|
36
|
-
if (batching)
|
|
37
|
-
enqueueEffect(subs);
|
|
38
|
-
else
|
|
39
|
-
subs();
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const snapshot = [...subs];
|
|
43
|
-
if (batching) {
|
|
44
|
-
for (const sub of snapshot)
|
|
45
|
-
enqueueEffect(sub);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
let lastError;
|
|
49
|
-
let hasError = false;
|
|
50
|
-
for (const sub of snapshot) {
|
|
51
|
-
try {
|
|
52
|
-
sub();
|
|
53
|
-
}
|
|
54
|
-
catch (e) {
|
|
55
|
-
lastError = e;
|
|
56
|
-
hasError = true;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (hasError)
|
|
60
|
-
throw lastError;
|
|
61
|
-
}
|
|
1
|
+
import { untrack } from "./effect";
|
|
2
|
+
import { createEffect, disposeEffect, readSignal, SignalNode, writeSignal } from "./graph";
|
|
62
3
|
export function signal(initialValue) {
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
addSub(holder, activeEffect);
|
|
68
|
-
return value;
|
|
69
|
-
}
|
|
70
|
-
const s = read;
|
|
71
|
-
s.set = (newValue) => {
|
|
72
|
-
if (Object.is(value, newValue))
|
|
73
|
-
return;
|
|
74
|
-
value = newValue;
|
|
75
|
-
if (holder.subs !== null)
|
|
76
|
-
notifySubs(holder.subs, isBatching());
|
|
77
|
-
};
|
|
78
|
-
s.update = (fn) => { s.set(fn(value)); };
|
|
4
|
+
const node = new SignalNode(initialValue);
|
|
5
|
+
const s = (() => readSignal(node));
|
|
6
|
+
s.set = (newValue) => { writeSignal(node, newValue); };
|
|
7
|
+
s.update = (fn) => { writeSignal(node, fn(node.value)); };
|
|
79
8
|
s.subscribe = (fn) => {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
9
|
+
const sub = createEffect(() => {
|
|
10
|
+
const value = readSignal(node);
|
|
11
|
+
untrack(() => { fn(value); });
|
|
12
|
+
});
|
|
13
|
+
return () => { disposeEffect(sub); };
|
|
84
14
|
};
|
|
85
15
|
s.__isSignal = true;
|
|
86
16
|
return s;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/signal/signal.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/signal/signal.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3F,MAAM,UAAU,MAAM,CAAI,YAAe;IACvC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC;IAE1C,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAc,CAAC;IAChD,CAAC,CAAC,GAAG,GAAG,CAAC,QAAW,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC,MAAM,GAAG,CAAC,EAAkB,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,SAAS,GAAG,CAAC,EAAsB,EAAE,EAAE;QACvC,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE;YAC5B,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAC/B,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC;IACF,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC;IACpB,OAAO,CAAC,CAAC;AACX,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisjs/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
5
6
|
"main": "./dist/index.js",
|
|
6
7
|
"types": "./dist/index.d.ts",
|
|
7
8
|
"exports": {
|
|
@@ -14,14 +15,22 @@
|
|
|
14
15
|
"types": "./dist/internal.d.ts"
|
|
15
16
|
}
|
|
16
17
|
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src",
|
|
21
|
+
"!dist/**/__tests__",
|
|
22
|
+
"!src/**/__tests__",
|
|
23
|
+
"CHANGELOG.md"
|
|
24
|
+
],
|
|
17
25
|
"devDependencies": {
|
|
18
26
|
"typescript": "^6.0.3"
|
|
19
27
|
},
|
|
20
28
|
"dependencies": {
|
|
21
|
-
"@praxisjs/shared": "0.3.
|
|
29
|
+
"@praxisjs/shared": "0.3.2"
|
|
22
30
|
},
|
|
23
31
|
"scripts": {
|
|
24
|
-
"build": "tsc",
|
|
25
|
-
"dev": "tsc --watch"
|
|
32
|
+
"build": "tsc -p tsconfig.build.json",
|
|
33
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
34
|
+
"typecheck": "tsc --noEmit"
|
|
26
35
|
}
|
|
27
36
|
}
|
|
@@ -3,15 +3,22 @@ export const componentPropsType: unique symbol = Symbol("praxis.componentPropsTy
|
|
|
3
3
|
interface ComponentInternals<T extends object> {
|
|
4
4
|
rawProps: T;
|
|
5
5
|
mounted: boolean;
|
|
6
|
-
anchor
|
|
6
|
+
anchor: Node | undefined;
|
|
7
|
+
start: Comment | undefined;
|
|
7
8
|
defaults: Record<string, unknown>;
|
|
8
9
|
stateDirty: boolean;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
// Kept on the instance under a symbol rather than in a WeakMap: every @Prop read goes through
|
|
13
|
+
// here, and a property load is much cheaper than a WeakMap lookup.
|
|
14
|
+
const INTERNALS: unique symbol = Symbol("praxis.internals");
|
|
15
|
+
|
|
16
|
+
interface InternalsHost {
|
|
17
|
+
[INTERNALS]?: ComponentInternals<object>;
|
|
18
|
+
}
|
|
12
19
|
|
|
13
20
|
function getInternals(instance: object): ComponentInternals<object> {
|
|
14
|
-
const internals =
|
|
21
|
+
const internals = (instance as InternalsHost)[INTERNALS];
|
|
15
22
|
if (!internals) {
|
|
16
23
|
throw new TypeError("Expected a PraxisJS component instance.");
|
|
17
24
|
}
|
|
@@ -22,12 +29,14 @@ export function initComponentInternals(
|
|
|
22
29
|
instance: object,
|
|
23
30
|
props: object,
|
|
24
31
|
): void {
|
|
25
|
-
|
|
32
|
+
(instance as InternalsHost)[INTERNALS] = {
|
|
26
33
|
rawProps: { ...props },
|
|
27
34
|
mounted: false,
|
|
35
|
+
anchor: undefined,
|
|
36
|
+
start: undefined,
|
|
28
37
|
defaults: {},
|
|
29
38
|
stateDirty: false,
|
|
30
|
-
}
|
|
39
|
+
};
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
export function getComponentProps(instance: object): object {
|
|
@@ -73,13 +82,17 @@ export function getComponentDefaults(
|
|
|
73
82
|
return getInternals(instance).defaults;
|
|
74
83
|
}
|
|
75
84
|
|
|
76
|
-
|
|
85
|
+
/**
|
|
86
|
+
* A node of the mounted component's output: its only root node, or the comment that closes its
|
|
87
|
+
* region when it renders several nodes. Its `parentElement` is the element the component sits in.
|
|
88
|
+
*/
|
|
89
|
+
export function getComponentAnchor(instance: object): Node | undefined {
|
|
77
90
|
return getInternals(instance).anchor;
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
export function setComponentAnchor(
|
|
81
94
|
instance: object,
|
|
82
|
-
anchor:
|
|
95
|
+
anchor: Node | undefined,
|
|
83
96
|
): void {
|
|
84
97
|
getInternals(instance).anchor = anchor;
|
|
85
98
|
}
|
|
@@ -96,14 +109,14 @@ export function setComponentMounted(
|
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
export function markStateDirty(instance: object): void {
|
|
99
|
-
const internals =
|
|
112
|
+
const internals = (instance as InternalsHost)[INTERNALS];
|
|
100
113
|
if (internals) {
|
|
101
114
|
internals.stateDirty = true;
|
|
102
115
|
}
|
|
103
116
|
}
|
|
104
117
|
|
|
105
118
|
export function isStateDirty(instance: object): boolean {
|
|
106
|
-
const internals =
|
|
119
|
+
const internals = (instance as InternalsHost)[INTERNALS];
|
|
107
120
|
if (internals) return internals.stateDirty;
|
|
108
121
|
return false;
|
|
109
122
|
}
|
|
@@ -112,8 +125,20 @@ export function setStateDirty(
|
|
|
112
125
|
instance: object,
|
|
113
126
|
stateDirty: boolean,
|
|
114
127
|
): void {
|
|
115
|
-
const internals =
|
|
128
|
+
const internals = (instance as InternalsHost)[INTERNALS];
|
|
116
129
|
if (internals) {
|
|
117
130
|
internals.stateDirty = stateDirty;
|
|
118
131
|
}
|
|
119
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The comment that opens a component's region, when it has one: a component whose render sets it
|
|
136
|
+
* (with the closing comment as its anchor) brings its own boundaries, and mountComponent adds none.
|
|
137
|
+
*/
|
|
138
|
+
export function getComponentStart(instance: object): Comment | undefined {
|
|
139
|
+
return getInternals(instance).start;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function setComponentStart(instance: object, start: Comment | undefined): void {
|
|
143
|
+
getInternals(instance).start = start;
|
|
144
|
+
}
|
package/src/internal.ts
CHANGED
|
@@ -15,10 +15,35 @@ export {
|
|
|
15
15
|
batch,
|
|
16
16
|
effect,
|
|
17
17
|
untrack,
|
|
18
|
+
selector,
|
|
19
|
+
type Selector,
|
|
20
|
+
type SelectorEquals,
|
|
18
21
|
type PersistedSignalOptions,
|
|
22
|
+
type PersistedSignal,
|
|
19
23
|
type SyncedSignal,
|
|
20
24
|
} from "./signal";
|
|
21
25
|
export { RootComponent } from "./component";
|
|
26
|
+
export {
|
|
27
|
+
createEffect,
|
|
28
|
+
createContextEffect,
|
|
29
|
+
disposeEffect,
|
|
30
|
+
startBatch,
|
|
31
|
+
endBatch,
|
|
32
|
+
EffectNode,
|
|
33
|
+
SignalNode,
|
|
34
|
+
readSignal,
|
|
35
|
+
writeSignal,
|
|
36
|
+
ComputedNode,
|
|
37
|
+
readComputed,
|
|
38
|
+
type ReactiveNode,
|
|
39
|
+
pauseTracking,
|
|
40
|
+
resumeTracking,
|
|
41
|
+
setReactiveObserver,
|
|
42
|
+
setCurrentOwner,
|
|
43
|
+
getEffectOwner,
|
|
44
|
+
type ReactiveObserver,
|
|
45
|
+
type EffectCleanup,
|
|
46
|
+
} from "./signal/graph";
|
|
22
47
|
export {
|
|
23
48
|
componentPropsType,
|
|
24
49
|
getComponentAnchor,
|
|
@@ -26,6 +51,8 @@ export {
|
|
|
26
51
|
getComponentDefaults,
|
|
27
52
|
getComponentProps,
|
|
28
53
|
getComponentRawProp,
|
|
54
|
+
getComponentStart,
|
|
55
|
+
setComponentStart,
|
|
29
56
|
isComponentMounted,
|
|
30
57
|
isStateDirty,
|
|
31
58
|
markStateDirty,
|
package/src/reactive/history.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface HistoryElement<T> {
|
|
|
12
12
|
undo(): void;
|
|
13
13
|
redo(): void;
|
|
14
14
|
clear(): void;
|
|
15
|
+
/** Stops tracking the source. Existing entries stay readable; no new ones are recorded. */
|
|
16
|
+
destroy(): void;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export function history<T>(
|
|
@@ -25,7 +27,7 @@ export function history<T>(
|
|
|
25
27
|
let _ignoreNext = false;
|
|
26
28
|
let _initialized = false;
|
|
27
29
|
|
|
28
|
-
effect(() => {
|
|
30
|
+
const stop = effect(() => {
|
|
29
31
|
const value = source();
|
|
30
32
|
|
|
31
33
|
if (!_initialized) {
|
|
@@ -83,5 +85,6 @@ export function history<T>(
|
|
|
83
85
|
_past.set([]);
|
|
84
86
|
_future.set([]);
|
|
85
87
|
},
|
|
88
|
+
destroy: stop,
|
|
86
89
|
};
|
|
87
90
|
}
|