@volynets/reflex-store 0.0.0-stage → 0.0.2
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/LICENSE +21 -0
- package/README.md +237 -2
- package/SPECIFICATION.md +137 -0
- package/dist/advanced.d.ts +12 -0
- package/dist/advanced.js +10 -0
- package/dist/chunks/collections-yHy0wH0f.js +268 -0
- package/dist/chunks/compiler-Dsb3E2ck.js +2516 -0
- package/dist/chunks/createStore-DHj16Ehk.js +12 -0
- package/dist/chunks/hydrate-DMnHCI5q.js +138 -0
- package/dist/chunks/keyed-CQKtL8a7.js +383 -0
- package/dist/chunks/names-Cx4YcvsH.js +11 -0
- package/dist/chunks/values-DCB6zyMz.js +142 -0
- package/dist/compiled-store.d.ts +4 -0
- package/dist/compiled-store.js +4 -0
- package/dist/compiler/swc.wasm +0 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +8 -0
- package/dist/licenses/NOTICE.txt +1 -0
- package/dist/licenses/SWC-APACHE-2.0.txt +202 -0
- package/dist/runtime/core.d.ts +2 -0
- package/dist/runtime/core.js +1 -0
- package/dist/runtime/internal.d.ts +3 -0
- package/dist/runtime/internal.js +41 -0
- package/dist/runtime.d.ts +29 -0
- package/dist/runtime.js +88 -0
- package/dist/selectors/index.d.ts +13 -0
- package/dist/selectors/index.js +16 -0
- package/dist/selectors/shared.d.ts +3 -0
- package/dist/selectors/shared.js +64 -0
- package/dist/store.d.ts +9 -0
- package/dist/store.js +4 -0
- package/dist/types/contracts-BCsRPdQb.d.ts +78 -0
- package/dist/types/createStore-BJ66-pfq.d.ts +43 -0
- package/dist/types/internal-BIzJSPwN.d.ts +18 -0
- package/dist/types/keyed-CdSYzIOc.d.ts +18 -0
- package/dist/types/shared-CdTKzvLe.d.ts +114 -0
- package/dist/vite.d.ts +68 -0
- package/dist/vite.js +87 -0
- package/examples/task-board/README.md +15 -0
- package/examples/task-board/src/reactivity.ts +33 -0
- package/examples/task-board/src/task-board.ts +159 -0
- package/examples/task-board/vite.config.ts +6 -0
- package/package.json +119 -4
- package/src/store/README.md +59 -0
- package/src/store/TRANSFORM_SPEC.md +85 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declares a compile-time store shape for the compiled-store transform.
|
|
3
|
+
*
|
|
4
|
+
* Data properties become leaf cells; methods become batched actions; getters
|
|
5
|
+
* become lazy computed values. leaf() and opaque() mark object values as
|
|
6
|
+
* replaceable leaves. If this function executes at runtime, the transform did
|
|
7
|
+
* not run.
|
|
8
|
+
*/ function createStore(_shape, _options) {
|
|
9
|
+
throw new Error("createStore() is a compile-time API and must be erased by a transform before runtime.");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { createStore as c };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { d as createDerivedProjection } from './keyed-CQKtL8a7.js';
|
|
2
|
+
import { i as isPlain, h as getStoreControl, j as depthOf } from './values-DCB6zyMz.js';
|
|
3
|
+
import { untracked } from '@volynets/reflex-runtime/internal';
|
|
4
|
+
import { t as transaction } from './collections-yHy0wH0f.js';
|
|
5
|
+
|
|
6
|
+
/** Marks one compiled store value as a replaceable leaf. */ function leaf(value) {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a lazy, read-only structured projection from a pure-return callback.
|
|
12
|
+
* Return a plain object; use reactiveMap for state with dynamic keys.
|
|
13
|
+
*/ function derive(compute, options = {}) {
|
|
14
|
+
return createDerivedProjection(()=>{
|
|
15
|
+
const value = compute();
|
|
16
|
+
if (!isPlain(value) || Array.isArray(value)) throw new TypeError("derive() must return a plain object");
|
|
17
|
+
return value;
|
|
18
|
+
}, options);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Wrap one synchronous logical state change, preserving arguments and receiver. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
function action(callback) {
|
|
23
|
+
if (Object.prototype.toString.call(callback) === "[object AsyncFunction]") throw new TypeError("action() requires a synchronous callback");
|
|
24
|
+
let receiver;
|
|
25
|
+
let arguments_;
|
|
26
|
+
// Reuse executors; save/restore the frame for recursion and eager re-entry.
|
|
27
|
+
const invoke = ()=>{
|
|
28
|
+
const result = callback.apply(receiver, arguments_);
|
|
29
|
+
if (result && typeof result.then === "function") throw new TypeError("action() cannot return a promise");
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
const execute = ()=>transaction(invoke);
|
|
33
|
+
return function(...args) {
|
|
34
|
+
const previousReceiver = receiver;
|
|
35
|
+
const previousArguments = arguments_;
|
|
36
|
+
// Reuse one executor frame while preserving receiver across recursive actions.
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
38
|
+
receiver = this;
|
|
39
|
+
arguments_ = args;
|
|
40
|
+
try {
|
|
41
|
+
return untracked(execute);
|
|
42
|
+
} finally{
|
|
43
|
+
receiver = previousReceiver;
|
|
44
|
+
arguments_ = previousArguments;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const hydrationShapes = new WeakMap();
|
|
50
|
+
function hydrationShape(control) {
|
|
51
|
+
const cached = hydrationShapes.get(control);
|
|
52
|
+
if (cached) return cached;
|
|
53
|
+
const branches = control.branches.map((path)=>({
|
|
54
|
+
path,
|
|
55
|
+
keys: new Set()
|
|
56
|
+
}));
|
|
57
|
+
const parents = new Map(branches.map((branch)=>[
|
|
58
|
+
JSON.stringify(branch.path),
|
|
59
|
+
branch
|
|
60
|
+
]));
|
|
61
|
+
for (const candidates of [
|
|
62
|
+
control.paths,
|
|
63
|
+
control.branches
|
|
64
|
+
]){
|
|
65
|
+
for (const path of candidates){
|
|
66
|
+
if (path.length === 0) continue;
|
|
67
|
+
parents.get(JSON.stringify(path.slice(0, -1)))?.keys.add(path[path.length - 1]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
hydrationShapes.set(control, branches);
|
|
71
|
+
return branches;
|
|
72
|
+
}
|
|
73
|
+
/** Restore all compiled data fields after validating the complete static schema. */ function hydrate(store, data) {
|
|
74
|
+
const control = getStoreControl(store);
|
|
75
|
+
if (!control?.hydrate || !control.paths || !control.branches) throw new TypeError("hydrate() requires a compiled store");
|
|
76
|
+
untracked(()=>{
|
|
77
|
+
for (const { path, keys } of hydrationShape(control)){
|
|
78
|
+
const branch = readPath(data, path);
|
|
79
|
+
if (!isPlain(branch) || Array.isArray(branch)) throw new TypeError("Invalid hydration branch: " + path.join("."));
|
|
80
|
+
const actual = Reflect.ownKeys(branch);
|
|
81
|
+
if (actual.length !== keys.size) throw new TypeError("Hydration data does not match the compiled store shape");
|
|
82
|
+
for (const key of actual)if (typeof key !== "string" || !keys.has(key)) throw new TypeError("Hydration data does not match the compiled store shape");
|
|
83
|
+
}
|
|
84
|
+
const seen = new Map();
|
|
85
|
+
const paths = control.paths;
|
|
86
|
+
const ordered = new Array(paths.length);
|
|
87
|
+
for(let i = 0; i < paths.length; i++)ordered[i] = restoreValue(readPath(data, paths[i]), seen);
|
|
88
|
+
transaction(()=>control.hydrate(ordered));
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function restoreValue(value, seen) {
|
|
92
|
+
if (value === null || typeof value !== "object" || depthOf(value) === "opaque") return value;
|
|
93
|
+
const cached = seen.get(value);
|
|
94
|
+
if (cached !== undefined) return cached;
|
|
95
|
+
if (value instanceof Map) {
|
|
96
|
+
const result = new Map();
|
|
97
|
+
seen.set(value, result);
|
|
98
|
+
for (const [key, item] of value)result.set(key, restoreValue(item, seen));
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
if (value instanceof Set) {
|
|
102
|
+
const result = new Set(value);
|
|
103
|
+
seen.set(value, result);
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
if (!isPlain(value)) return value;
|
|
107
|
+
const array = Array.isArray(value);
|
|
108
|
+
const result = array ? [] : Object.create(Object.getPrototypeOf(value));
|
|
109
|
+
seen.set(value, result);
|
|
110
|
+
const output = {
|
|
111
|
+
configurable: true,
|
|
112
|
+
writable: true,
|
|
113
|
+
enumerable: true,
|
|
114
|
+
value: undefined
|
|
115
|
+
};
|
|
116
|
+
for (const key of Reflect.ownKeys(value)){
|
|
117
|
+
if (array && key === "length") continue;
|
|
118
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
119
|
+
if (!("value" in descriptor)) throw new TypeError("Hydration values must contain data properties");
|
|
120
|
+
output.enumerable = descriptor.enumerable;
|
|
121
|
+
output.value = restoreValue(descriptor.value, seen);
|
|
122
|
+
Object.defineProperty(result, key, output);
|
|
123
|
+
}
|
|
124
|
+
if (array) result.length = value.length;
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
function readPath(data, path) {
|
|
128
|
+
let value = data;
|
|
129
|
+
for (const key of path){
|
|
130
|
+
if (value === null || typeof value !== "object") throw new TypeError("Missing hydration field: " + path.join("."));
|
|
131
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
132
|
+
if (!descriptor || !("value" in descriptor)) throw new TypeError("Hydration fields must be own data properties: " + path.join("."));
|
|
133
|
+
value = descriptor.value;
|
|
134
|
+
}
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export { action as a, derive as d, hydrate as h, leaf as l };
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { setCurrentConsumer, currentConsumer, createConsumer, readConsumerLazy, disposeNode, untracked, unlinkAllSources, CONSUMER_INITIAL_STATE, createProducer, disposeWatcher, createWatcher, runWatcher, Both, readProducer, writeProducer } from '@volynets/reflex-runtime/internal';
|
|
2
|
+
import { f as cloneValue, g as storeControls, j as depthOf, k as isStructural } from './values-DCB6zyMz.js';
|
|
3
|
+
import { a as storeName, s as setStoreName } from './names-Cx4YcvsH.js';
|
|
4
|
+
import { readProjectionPath } from '../selectors/shared.js';
|
|
5
|
+
import { t as transaction } from './collections-yHy0wH0f.js';
|
|
6
|
+
|
|
7
|
+
/** A memo with owner-controlled suspension; no node exists until its first read. */ class Demand {
|
|
8
|
+
constructor(compute){
|
|
9
|
+
this.compute = compute;
|
|
10
|
+
this.disposed = false;
|
|
11
|
+
}
|
|
12
|
+
read() {
|
|
13
|
+
if (this.disposed) throw new Error("Cannot read a disposed store derivation");
|
|
14
|
+
const node = this.node ??= createConsumer(this.compute);
|
|
15
|
+
return readConsumerLazy.call(node);
|
|
16
|
+
}
|
|
17
|
+
peek() {
|
|
18
|
+
return untracked(()=>this.read());
|
|
19
|
+
}
|
|
20
|
+
collect() {
|
|
21
|
+
const node = this.node;
|
|
22
|
+
if (!node || node.firstOut !== null) return;
|
|
23
|
+
unlinkAllSources(node);
|
|
24
|
+
node.state = CONSUMER_INITIAL_STATE;
|
|
25
|
+
node.payload = undefined;
|
|
26
|
+
}
|
|
27
|
+
dispose() {
|
|
28
|
+
this.disposed = true;
|
|
29
|
+
if (this.node) disposeNode(this.node);
|
|
30
|
+
this.node = undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Retain tracked semantic locations until their owner's next collection boundary. */ class Observations {
|
|
34
|
+
constructor(compute, equals = Object.is, keyEquals){
|
|
35
|
+
this.compute = compute;
|
|
36
|
+
this.equals = equals;
|
|
37
|
+
this.keyEquals = keyEquals;
|
|
38
|
+
}
|
|
39
|
+
read(key) {
|
|
40
|
+
if (this.keyEquals && this.entries) {
|
|
41
|
+
const previous = currentConsumer;
|
|
42
|
+
setCurrentConsumer(null);
|
|
43
|
+
try {
|
|
44
|
+
for (const known of this.entries.keys()){
|
|
45
|
+
if (this.keyEquals(known, key)) {
|
|
46
|
+
key = known;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} finally{
|
|
51
|
+
setCurrentConsumer(previous);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
let node = this.entries?.get(key);
|
|
55
|
+
if (!node) {
|
|
56
|
+
if (currentConsumer === null) return this.compute(key);
|
|
57
|
+
let initialized = false;
|
|
58
|
+
let previous;
|
|
59
|
+
node = createConsumer(()=>{
|
|
60
|
+
const next = this.compute(key);
|
|
61
|
+
if (!initialized || !(this.equals === Object.is ? Object.is(previous, next) : untracked(()=>this.equals(previous, next)))) previous = next;
|
|
62
|
+
initialized = true;
|
|
63
|
+
return previous;
|
|
64
|
+
});
|
|
65
|
+
(this.entries ??= new Map()).set(key, node);
|
|
66
|
+
}
|
|
67
|
+
return readConsumerLazy.call(node);
|
|
68
|
+
}
|
|
69
|
+
collect() {
|
|
70
|
+
if (!this.entries) return;
|
|
71
|
+
for (const [key, node] of this.entries){
|
|
72
|
+
if (node.firstOut !== null) continue;
|
|
73
|
+
this.entries.delete(key);
|
|
74
|
+
disposeNode(node);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
dispose() {
|
|
78
|
+
if (!this.entries) return;
|
|
79
|
+
for (const node of this.entries.values())disposeNode(node);
|
|
80
|
+
this.entries = undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const sameKeys = (a, b)=>{
|
|
85
|
+
if (a.length !== b.length) return false;
|
|
86
|
+
for(let i = 0; i < a.length; i++)if (a[i] !== b[i]) return false;
|
|
87
|
+
return true;
|
|
88
|
+
};
|
|
89
|
+
class Projection {
|
|
90
|
+
constructor(fn, seed, options, returnsValue = false){
|
|
91
|
+
this.options = options;
|
|
92
|
+
this.views = new Set();
|
|
93
|
+
this.disposed = false;
|
|
94
|
+
const clone = options.clone ?? cloneValue;
|
|
95
|
+
let initialSeed = seed;
|
|
96
|
+
let current;
|
|
97
|
+
let initialized = false;
|
|
98
|
+
this.state = new Demand(()=>{
|
|
99
|
+
const draft = returnsValue ? undefined : untracked(()=>clone(initialized ? current : initialSeed));
|
|
100
|
+
const result = fn(draft);
|
|
101
|
+
const next = result === undefined ? draft : untracked(()=>clone(result));
|
|
102
|
+
const equals = options.equals;
|
|
103
|
+
if (!initialized || !(equals === undefined || equals === Object.is ? Object.is(current, next) : untracked(()=>equals(current, next)))) current = next;
|
|
104
|
+
initialSeed = undefined;
|
|
105
|
+
initialized = true;
|
|
106
|
+
return current;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
view(path, array) {
|
|
110
|
+
const view = new View(this, path, array);
|
|
111
|
+
this.views.add(new WeakRef(view));
|
|
112
|
+
return view;
|
|
113
|
+
}
|
|
114
|
+
read(path) {
|
|
115
|
+
if (this.disposed) throw new Error("Cannot read a disposed store projection");
|
|
116
|
+
const value = this.state.read();
|
|
117
|
+
return path.length === 0 ? value : readProjectionPath(value, path);
|
|
118
|
+
}
|
|
119
|
+
collect() {
|
|
120
|
+
for (const ref of this.views){
|
|
121
|
+
const view = ref.deref();
|
|
122
|
+
if (view) view.collect();
|
|
123
|
+
else this.views.delete(ref);
|
|
124
|
+
}
|
|
125
|
+
this.state.collect();
|
|
126
|
+
}
|
|
127
|
+
dispose() {
|
|
128
|
+
this.disposed = true;
|
|
129
|
+
for (const ref of this.views)ref.deref()?.dispose();
|
|
130
|
+
this.views.clear();
|
|
131
|
+
this.state.dispose();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
class View {
|
|
135
|
+
constructor(owner, path, array){
|
|
136
|
+
this.owner = owner;
|
|
137
|
+
this.path = path;
|
|
138
|
+
this.children = new Map();
|
|
139
|
+
const read = ()=>owner.read(path);
|
|
140
|
+
const parent = ()=>{
|
|
141
|
+
const value = read();
|
|
142
|
+
return typeof value === "object" && value !== null ? value : undefined;
|
|
143
|
+
};
|
|
144
|
+
const get = (key)=>{
|
|
145
|
+
const value = parent();
|
|
146
|
+
return value === undefined ? undefined : Reflect.get(value, key);
|
|
147
|
+
};
|
|
148
|
+
const valueAt = (key)=>{
|
|
149
|
+
const value = parent();
|
|
150
|
+
return this.wrap(key, value === undefined ? undefined : Reflect.get(value, key), value);
|
|
151
|
+
};
|
|
152
|
+
const existsAt = (key)=>{
|
|
153
|
+
const value = parent();
|
|
154
|
+
return value !== undefined && key in value;
|
|
155
|
+
};
|
|
156
|
+
const descriptorAt = (key)=>{
|
|
157
|
+
const value = parent();
|
|
158
|
+
return value === undefined ? undefined : Object.getOwnPropertyDescriptor(value, key)?.enumerable;
|
|
159
|
+
};
|
|
160
|
+
const keysAt = ()=>{
|
|
161
|
+
const value = parent();
|
|
162
|
+
return value === undefined ? [] : Reflect.ownKeys(value);
|
|
163
|
+
};
|
|
164
|
+
const dispose = ()=>owner.dispose();
|
|
165
|
+
this.proxy = new Proxy(array ? [] : Object.create(null), {
|
|
166
|
+
get: (_target, key)=>{
|
|
167
|
+
if (key === storeName) return owner.options.name;
|
|
168
|
+
if (key === Symbol.dispose) return dispose;
|
|
169
|
+
// Inspect one parent snapshot without subscribing to structural identity.
|
|
170
|
+
const previous = currentConsumer;
|
|
171
|
+
let container;
|
|
172
|
+
let value;
|
|
173
|
+
setCurrentConsumer(null);
|
|
174
|
+
try {
|
|
175
|
+
container = parent();
|
|
176
|
+
value = container === undefined ? undefined : Reflect.get(container, key);
|
|
177
|
+
} finally{
|
|
178
|
+
setCurrentConsumer(previous);
|
|
179
|
+
}
|
|
180
|
+
if (this.shouldWrap(value, container)) return this.child(key, Array.isArray(value));
|
|
181
|
+
return (this.values ??= new Observations(valueAt)).read(key);
|
|
182
|
+
},
|
|
183
|
+
has: (_target, key)=>(this.exists ??= new Observations(existsAt)).read(key),
|
|
184
|
+
ownKeys: ()=>{
|
|
185
|
+
const keys = (this.keys ??= new Observations(keysAt, sameKeys)).read(undefined);
|
|
186
|
+
return array && !keys.includes("length") ? [
|
|
187
|
+
...keys,
|
|
188
|
+
"length"
|
|
189
|
+
] : [
|
|
190
|
+
...keys
|
|
191
|
+
];
|
|
192
|
+
},
|
|
193
|
+
getOwnPropertyDescriptor: (_target, key)=>{
|
|
194
|
+
const enumerable = (this.descriptors ??= new Observations(descriptorAt)).read(key);
|
|
195
|
+
if (array && key === "length") return {
|
|
196
|
+
configurable: false,
|
|
197
|
+
enumerable: false,
|
|
198
|
+
writable: true,
|
|
199
|
+
value: untracked(()=>get(key)) ?? 0
|
|
200
|
+
};
|
|
201
|
+
if (enumerable === undefined) return undefined;
|
|
202
|
+
return {
|
|
203
|
+
configurable: true,
|
|
204
|
+
enumerable,
|
|
205
|
+
writable: false,
|
|
206
|
+
value: untracked(()=>get(key))
|
|
207
|
+
};
|
|
208
|
+
},
|
|
209
|
+
set: ()=>false,
|
|
210
|
+
deleteProperty: ()=>false,
|
|
211
|
+
defineProperty: ()=>false,
|
|
212
|
+
setPrototypeOf: ()=>false,
|
|
213
|
+
preventExtensions: ()=>false
|
|
214
|
+
});
|
|
215
|
+
storeControls.set(this.proxy, {
|
|
216
|
+
raw: read,
|
|
217
|
+
collect: ()=>owner.collect(),
|
|
218
|
+
dispose
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
shouldWrap(value, parent) {
|
|
222
|
+
return this.owner.options.depth !== "shallow" && (parent === undefined || depthOf(parent) !== "shallow") && isStructural(value);
|
|
223
|
+
}
|
|
224
|
+
wrap(key, value, parent) {
|
|
225
|
+
return this.shouldWrap(value, parent) ? this.child(key, Array.isArray(value)) : value;
|
|
226
|
+
}
|
|
227
|
+
child(key, array) {
|
|
228
|
+
const existing = this.children.get(key);
|
|
229
|
+
const cached = existing?.view.deref();
|
|
230
|
+
if (cached && existing.array === array) return cached.proxy;
|
|
231
|
+
const view = this.owner.view([
|
|
232
|
+
...this.path,
|
|
233
|
+
key
|
|
234
|
+
], array);
|
|
235
|
+
this.children.set(key, {
|
|
236
|
+
array,
|
|
237
|
+
view: new WeakRef(view)
|
|
238
|
+
});
|
|
239
|
+
return view.proxy;
|
|
240
|
+
}
|
|
241
|
+
collect() {
|
|
242
|
+
this.values?.collect();
|
|
243
|
+
this.exists?.collect();
|
|
244
|
+
this.descriptors?.collect();
|
|
245
|
+
this.keys?.collect();
|
|
246
|
+
for (const [key, entry] of this.children)if (!entry.view.deref()) this.children.delete(key);
|
|
247
|
+
}
|
|
248
|
+
dispose() {
|
|
249
|
+
this.values?.dispose();
|
|
250
|
+
this.exists?.dispose();
|
|
251
|
+
this.descriptors?.dispose();
|
|
252
|
+
this.keys?.dispose();
|
|
253
|
+
this.children.clear();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function createStoreProjection(fn, seed, options = {}) {
|
|
257
|
+
const owner = new Projection(fn, seed, options);
|
|
258
|
+
return owner.view([], Array.isArray(seed)).proxy;
|
|
259
|
+
}
|
|
260
|
+
/** Pure-return derivations never consume a mutable draft of their previous state. */ function createDerivedProjection(compute, options) {
|
|
261
|
+
const owner = new Projection(compute, {}, options, true);
|
|
262
|
+
return owner.view([], false).proxy;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function createSignalNode(payload) {
|
|
266
|
+
return createProducer(payload);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/// <reference lib="esnext.disposable" />
|
|
270
|
+
function withDispose(resource, name) {
|
|
271
|
+
setStoreName(resource, name);
|
|
272
|
+
Object.defineProperty(resource, Symbol.dispose, {
|
|
273
|
+
configurable: true,
|
|
274
|
+
value: ()=>resource.dispose()
|
|
275
|
+
});
|
|
276
|
+
return resource;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Map uses SameValueZero. Preserve Object.is semantics for signed zero.
|
|
280
|
+
const negativeZero = Symbol("negative zero");
|
|
281
|
+
function identityKey(key) {
|
|
282
|
+
return key === 0 && 1 / key === -Infinity ? negativeZero : key;
|
|
283
|
+
}
|
|
284
|
+
function createSelector(source, options = {}) {
|
|
285
|
+
const equals = options.equals ?? Object.is;
|
|
286
|
+
const identityEquality = equals === Object.is;
|
|
287
|
+
const entries = new Map();
|
|
288
|
+
let watcher;
|
|
289
|
+
let current;
|
|
290
|
+
let initialized = false;
|
|
291
|
+
let disposed = false;
|
|
292
|
+
const lookup = (key)=>{
|
|
293
|
+
if (identityEquality) return entries.get(identityKey(key));
|
|
294
|
+
return untracked(()=>{
|
|
295
|
+
for (const entry of entries.values())if (equals(entry.key, key)) return entry;
|
|
296
|
+
return undefined;
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
const sync = (next)=>{
|
|
300
|
+
if (initialized && (identityEquality ? Object.is(current, next) : untracked(()=>equals(current, next)))) return;
|
|
301
|
+
const previous = initialized ? lookup(current) : undefined;
|
|
302
|
+
current = next;
|
|
303
|
+
initialized = true;
|
|
304
|
+
const selected = lookup(next);
|
|
305
|
+
if (!previous && !selected) return;
|
|
306
|
+
transaction(()=>{
|
|
307
|
+
if (previous) writeProducer(previous.node, false);
|
|
308
|
+
if (selected) writeProducer(selected.node, true);
|
|
309
|
+
});
|
|
310
|
+
};
|
|
311
|
+
const read = (key)=>{
|
|
312
|
+
if (disposed) throw new Error("Cannot read a disposed selector");
|
|
313
|
+
if (currentConsumer === null) return identityEquality ? Object.is(source(), key) : untracked(()=>equals(source(), key));
|
|
314
|
+
if (!watcher) {
|
|
315
|
+
watcher = createWatcher(()=>sync(source()));
|
|
316
|
+
try {
|
|
317
|
+
runWatcher(watcher);
|
|
318
|
+
} catch (error) {
|
|
319
|
+
disposeWatcher(watcher);
|
|
320
|
+
watcher = undefined;
|
|
321
|
+
throw error;
|
|
322
|
+
}
|
|
323
|
+
} else if ((watcher.state & Both) !== 0) {
|
|
324
|
+
sync(untracked(source));
|
|
325
|
+
}
|
|
326
|
+
let entry = lookup(key);
|
|
327
|
+
if (!entry) {
|
|
328
|
+
entry = {
|
|
329
|
+
key,
|
|
330
|
+
node: createSignalNode(identityEquality ? Object.is(current, key) : untracked(()=>equals(current, key)))
|
|
331
|
+
};
|
|
332
|
+
entries.set(identityKey(key), entry);
|
|
333
|
+
}
|
|
334
|
+
return readProducer(entry.node);
|
|
335
|
+
};
|
|
336
|
+
return withDispose(Object.assign(read, {
|
|
337
|
+
collect () {
|
|
338
|
+
for (const [key, entry] of entries){
|
|
339
|
+
if (entry.node.firstOut !== null) continue;
|
|
340
|
+
entries.delete(key);
|
|
341
|
+
disposeNode(entry.node);
|
|
342
|
+
}
|
|
343
|
+
if (entries.size === 0 && watcher) {
|
|
344
|
+
disposeWatcher(watcher);
|
|
345
|
+
watcher = undefined;
|
|
346
|
+
initialized = false;
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
dispose () {
|
|
350
|
+
disposed = true;
|
|
351
|
+
if (watcher) disposeWatcher(watcher);
|
|
352
|
+
watcher = undefined;
|
|
353
|
+
for (const entry of entries.values())disposeNode(entry.node);
|
|
354
|
+
entries.clear();
|
|
355
|
+
}
|
|
356
|
+
}), options.name);
|
|
357
|
+
}
|
|
358
|
+
function createKeyedProjection(source, keyOf, project, options = {}) {
|
|
359
|
+
const keyEquals = options.keyEquals ?? Object.is;
|
|
360
|
+
const valueEquals = options.equals ?? Object.is;
|
|
361
|
+
const sourceValue = new Demand(source);
|
|
362
|
+
const projected = new Demand(()=>project(sourceValue.read()));
|
|
363
|
+
const keys = new Observations((key)=>{
|
|
364
|
+
const activeKey = keyOf(sourceValue.read());
|
|
365
|
+
const requestedKey = key === negativeZero ? -0 : key;
|
|
366
|
+
const matches = keyEquals === Object.is ? Object.is(activeKey, requestedKey) : untracked(()=>keyEquals(activeKey, requestedKey));
|
|
367
|
+
return matches ? projected.read() : options.fallback;
|
|
368
|
+
}, valueEquals === Object.is ? Object.is : (a, b)=>a === undefined || b === undefined ? Object.is(a, b) : valueEquals(a, b), options.keyEquals ? (a, b)=>keyEquals(a === negativeZero ? -0 : a, b === negativeZero ? -0 : b) : undefined);
|
|
369
|
+
return withDispose(Object.assign((key)=>keys.read(identityKey(key)), {
|
|
370
|
+
collect () {
|
|
371
|
+
keys.collect();
|
|
372
|
+
projected.collect();
|
|
373
|
+
sourceValue.collect();
|
|
374
|
+
},
|
|
375
|
+
dispose () {
|
|
376
|
+
keys.dispose();
|
|
377
|
+
projected.dispose();
|
|
378
|
+
sourceValue.dispose();
|
|
379
|
+
}
|
|
380
|
+
}), options.name);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export { createKeyedProjection as a, createStoreProjection as b, createSelector as c, createDerivedProjection as d };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const storeName = Symbol.for("@volynets/reflex-store/name");
|
|
2
|
+
function getStoreName(resource) {
|
|
3
|
+
return resource[storeName];
|
|
4
|
+
}
|
|
5
|
+
function setStoreName(resource, name) {
|
|
6
|
+
if (name !== undefined) Object.defineProperty(resource, storeName, {
|
|
7
|
+
value: name
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { storeName as a, getStoreName as g, setStoreName as s };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { untracked } from '@volynets/reflex-runtime/internal';
|
|
2
|
+
|
|
3
|
+
const depths = new WeakMap();
|
|
4
|
+
function mark(value, depth) {
|
|
5
|
+
depths.set(value, depth);
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
const deep = (value)=>mark(value, "deep");
|
|
9
|
+
const shallow = (value)=>mark(value, "shallow");
|
|
10
|
+
const ref = (value)=>mark(value, "ref");
|
|
11
|
+
const opaque = (value)=>mark(value, "opaque");
|
|
12
|
+
function depthOf(value) {
|
|
13
|
+
return depths.get(value) ?? "deep";
|
|
14
|
+
}
|
|
15
|
+
function isPlain(value) {
|
|
16
|
+
if (typeof value !== "object" || value === null) return false;
|
|
17
|
+
const proto = Object.getPrototypeOf(value);
|
|
18
|
+
return Array.isArray(value) || proto === Object.prototype || proto === null;
|
|
19
|
+
}
|
|
20
|
+
function isStructural(value) {
|
|
21
|
+
return isPlain(value) && isStructuralDepth(depthOf(value));
|
|
22
|
+
}
|
|
23
|
+
function isStructuralDepth(depth) {
|
|
24
|
+
return depth !== "ref" && depth !== "opaque";
|
|
25
|
+
}
|
|
26
|
+
/** Copy structural data, retaining explicit reference boundaries and foreign objects. */ function cloneValue(value, seen = new Map()) {
|
|
27
|
+
if (!isPlain(value)) return value;
|
|
28
|
+
const depth = depthOf(value);
|
|
29
|
+
if (!isStructuralDepth(depth)) return value;
|
|
30
|
+
if (seen.has(value)) return seen.get(value);
|
|
31
|
+
const array = Array.isArray(value);
|
|
32
|
+
const result = array ? [] : Object.create(Object.getPrototypeOf(value));
|
|
33
|
+
seen.set(value, result);
|
|
34
|
+
if (depth !== "deep") depths.set(result, depth);
|
|
35
|
+
const output = {
|
|
36
|
+
configurable: true,
|
|
37
|
+
enumerable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: undefined
|
|
40
|
+
};
|
|
41
|
+
for (const key of Reflect.ownKeys(value)){
|
|
42
|
+
if (array && key === "length") continue;
|
|
43
|
+
output.enumerable = Object.getOwnPropertyDescriptor(value, key).enumerable;
|
|
44
|
+
output.value = depth === "shallow" ? value[key] : cloneValue(value[key], seen);
|
|
45
|
+
Object.defineProperty(result, key, output);
|
|
46
|
+
}
|
|
47
|
+
if (array) result.length = value.length;
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
function getStoreControl(store) {
|
|
51
|
+
return storeControls.get(store) ?? store[Symbol.for("@volynets/reflex-store/data/1")];
|
|
52
|
+
}
|
|
53
|
+
const storeControls = new WeakMap();
|
|
54
|
+
function raw(store) {
|
|
55
|
+
return untracked(()=>{
|
|
56
|
+
const control = typeof store === "object" && store !== null ? getStoreControl(store) : undefined;
|
|
57
|
+
return control ? control.raw() : store;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function snapshot(store) {
|
|
61
|
+
// Snapshot copies shallow/ref plain data too; opaque data cannot be serialized generically.
|
|
62
|
+
const seen = new Map();
|
|
63
|
+
const copy = (value)=>{
|
|
64
|
+
if (typeof value === "object" && value !== null && depthOf(value) === "opaque") return value;
|
|
65
|
+
if (value instanceof Set) {
|
|
66
|
+
if (seen.has(value)) return seen.get(value);
|
|
67
|
+
const result = new Set(value);
|
|
68
|
+
seen.set(value, result);
|
|
69
|
+
const immutable = ()=>{
|
|
70
|
+
throw new TypeError("Cannot mutate a snapshot");
|
|
71
|
+
};
|
|
72
|
+
Object.defineProperties(result, {
|
|
73
|
+
add: {
|
|
74
|
+
value: immutable
|
|
75
|
+
},
|
|
76
|
+
delete: {
|
|
77
|
+
value: immutable
|
|
78
|
+
},
|
|
79
|
+
clear: {
|
|
80
|
+
value: immutable
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return Object.freeze(result);
|
|
84
|
+
}
|
|
85
|
+
if (value instanceof Map) {
|
|
86
|
+
if (seen.has(value)) return seen.get(value);
|
|
87
|
+
const result = new Map();
|
|
88
|
+
seen.set(value, result);
|
|
89
|
+
for (const [key, item] of value)result.set(key, copy(item));
|
|
90
|
+
const immutable = ()=>{
|
|
91
|
+
throw new TypeError("Cannot mutate a snapshot");
|
|
92
|
+
};
|
|
93
|
+
Object.defineProperties(result, {
|
|
94
|
+
set: {
|
|
95
|
+
value: immutable
|
|
96
|
+
},
|
|
97
|
+
delete: {
|
|
98
|
+
value: immutable
|
|
99
|
+
},
|
|
100
|
+
clear: {
|
|
101
|
+
value: immutable
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return Object.freeze(result);
|
|
105
|
+
}
|
|
106
|
+
if (!isPlain(value) || depthOf(value) === "opaque") return value;
|
|
107
|
+
if (seen.has(value)) return seen.get(value);
|
|
108
|
+
const array = Array.isArray(value);
|
|
109
|
+
const result = array ? [] : Object.create(Object.getPrototypeOf(value));
|
|
110
|
+
seen.set(value, result);
|
|
111
|
+
const output = {
|
|
112
|
+
enumerable: true,
|
|
113
|
+
value: undefined
|
|
114
|
+
};
|
|
115
|
+
for (const key of Reflect.ownKeys(value)){
|
|
116
|
+
if (array && key === "length") continue;
|
|
117
|
+
output.enumerable = Object.getOwnPropertyDescriptor(value, key).enumerable;
|
|
118
|
+
output.value = copy(value[key]);
|
|
119
|
+
Object.defineProperty(result, key, output);
|
|
120
|
+
}
|
|
121
|
+
if (array) result.length = value.length;
|
|
122
|
+
return Object.freeze(result);
|
|
123
|
+
};
|
|
124
|
+
return untracked(()=>{
|
|
125
|
+
const control = typeof store === "object" && store !== null ? getStoreControl(store) : undefined;
|
|
126
|
+
// Capture every cell before cloning user values, preserving point-in-time reads.
|
|
127
|
+
const data = control ? control.raw() : store;
|
|
128
|
+
return control?.snapshot ? control.snapshot(data, copy) : copy(data);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function disposeStore(store) {
|
|
132
|
+
const control = getStoreControl(store);
|
|
133
|
+
if (!control) throw new TypeError("Expected a runtime store or projection");
|
|
134
|
+
control.dispose();
|
|
135
|
+
}
|
|
136
|
+
/** Reclaim locations with no consumers at an explicit owner lifecycle boundary. */ function collectStore(store) {
|
|
137
|
+
const control = getStoreControl(store);
|
|
138
|
+
if (!control) throw new TypeError("Expected a runtime store or projection");
|
|
139
|
+
control.collect();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export { disposeStore as a, ref as b, collectStore as c, deep as d, shallow as e, cloneValue as f, storeControls as g, getStoreControl as h, isPlain as i, depthOf as j, isStructural as k, opaque as o, raw as r, snapshot as s };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/// <reference lib="esnext.disposable" />
|
|
2
|
+
export { C as CompiledStore, b as StoreShape, c as createStore } from './types/createStore-BJ66-pfq.js';
|
|
3
|
+
export { b as CompiledStoreDiagnostic, c as CompiledStoreLoweringTarget, d as CompiledStoreLoweringTargetOptions, e as CompiledStorePathContext, f as CompiledStoreTemporaryContext, g as CompiledStoreTransformError, C as CompiledStoreTransformOptions, a as CompiledStoreTransformResult } from './types/contracts-BCsRPdQb.js';
|
|
4
|
+
export { compileStore, transformCompiledStore } from './store.js';
|