round-core 0.0.6 → 0.0.8
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/README.md +21 -0
- package/dist/index.d.ts +341 -326
- package/dist/vite-plugin.js +52 -3
- package/package.json +7 -3
- package/.github/workflows/benchmarks.yml +0 -44
- package/Round.png +0 -0
- package/benchmarks/apps/react/index.html +0 -9
- package/benchmarks/apps/react/main.jsx +0 -25
- package/benchmarks/apps/react/vite.config.js +0 -12
- package/benchmarks/apps/round/index.html +0 -11
- package/benchmarks/apps/round/main.jsx +0 -22
- package/benchmarks/apps/round/vite.config.js +0 -15
- package/benchmarks/bun.lock +0 -497
- package/benchmarks/dist-bench/react/assets/index-9KGqIPOU.js +0 -8
- package/benchmarks/dist-bench/react/index.html +0 -10
- package/benchmarks/dist-bench/round/assets/index-CBBIRhox.js +0 -52
- package/benchmarks/dist-bench/round/index.html +0 -8
- package/benchmarks/package.json +0 -22
- package/benchmarks/scripts/measure-build.js +0 -64
- package/benchmarks/tests/runtime.bench.js +0 -51
- package/benchmarks/vitest.config.js +0 -8
- package/bun.lock +0 -425
- package/cli.js +0 -2
- package/index.js +0 -2
- package/logo.svg +0 -10
- package/src/cli.js +0 -608
- package/src/compiler/index.js +0 -2
- package/src/compiler/transformer.js +0 -443
- package/src/compiler/vite-plugin.js +0 -472
- package/src/index.d.ts +0 -326
- package/src/index.js +0 -45
- package/src/runtime/context.js +0 -101
- package/src/runtime/dom.js +0 -403
- package/src/runtime/error-boundary.js +0 -48
- package/src/runtime/error-reporter.js +0 -13
- package/src/runtime/error-store.js +0 -85
- package/src/runtime/errors.js +0 -152
- package/src/runtime/lifecycle.js +0 -142
- package/src/runtime/markdown.js +0 -72
- package/src/runtime/router.js +0 -468
- package/src/runtime/signals.js +0 -548
- package/src/runtime/store.js +0 -215
- package/src/runtime/suspense.js +0 -128
- package/vite.config.build.js +0 -48
- package/vite.config.js +0 -10
- package/vitest.config.js +0 -8
package/src/runtime/signals.js
DELETED
|
@@ -1,548 +0,0 @@
|
|
|
1
|
-
import { onMount, triggerUpdate, getCurrentComponent } from './lifecycle.js';
|
|
2
|
-
import { reportErrorSafe } from './error-reporter.js';
|
|
3
|
-
|
|
4
|
-
let context = [];
|
|
5
|
-
|
|
6
|
-
function isPromiseLike(v) {
|
|
7
|
-
return v && (typeof v === 'object' || typeof v === 'function') && typeof v.then === 'function';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function subscribe(running, subscriptions) {
|
|
11
|
-
subscriptions.add(running);
|
|
12
|
-
running.dependencies.add(subscriptions);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Run a function without tracking any signals it reads.
|
|
17
|
-
* Any signals accessed inside `fn` will not become dependencies of the current effect.
|
|
18
|
-
* @template T
|
|
19
|
-
* @param {() => T} fn The function to execute.
|
|
20
|
-
* @returns {T} The return value of `fn`.
|
|
21
|
-
*/
|
|
22
|
-
export function untrack(fn) {
|
|
23
|
-
context.push(null);
|
|
24
|
-
try {
|
|
25
|
-
return typeof fn === 'function' ? fn() : undefined;
|
|
26
|
-
} finally {
|
|
27
|
-
context.pop();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Create a reactive side-effect that runs whenever its signal dependencies change.
|
|
33
|
-
* @param {(() => any) | any[]} arg1 Either the callback function or an array of explicit dependencies.
|
|
34
|
-
* @param {(() => any)} [arg2] The callback function if the first argument was explicit dependencies.
|
|
35
|
-
* @param {object} [arg3] Optional configuration (e.g., { onLoad: false }).
|
|
36
|
-
* @returns {() => void} A function to stop and cleanup the effect.
|
|
37
|
-
*/
|
|
38
|
-
export function effect(arg1, arg2, arg3) {
|
|
39
|
-
let callback;
|
|
40
|
-
let explicitDeps = null;
|
|
41
|
-
let options = { onLoad: true };
|
|
42
|
-
|
|
43
|
-
let owner = getCurrentComponent();
|
|
44
|
-
|
|
45
|
-
if (typeof arg1 === 'function') {
|
|
46
|
-
callback = arg1;
|
|
47
|
-
if (arg2 && typeof arg2 === 'object') {
|
|
48
|
-
options = { ...options, ...arg2 };
|
|
49
|
-
}
|
|
50
|
-
} else {
|
|
51
|
-
explicitDeps = arg1;
|
|
52
|
-
callback = arg2;
|
|
53
|
-
if (arg3 && typeof arg3 === 'object') {
|
|
54
|
-
options = { ...options, ...arg3 };
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const execute = () => {
|
|
59
|
-
if (typeof execute._cleanup === 'function') {
|
|
60
|
-
try {
|
|
61
|
-
execute._cleanup();
|
|
62
|
-
} catch (e) {
|
|
63
|
-
const name = owner ? (owner.name ?? 'Anonymous') : null;
|
|
64
|
-
reportErrorSafe(e, { phase: 'effect.cleanup', component: name });
|
|
65
|
-
}
|
|
66
|
-
execute._cleanup = null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
cleanup(execute);
|
|
70
|
-
context.push(execute);
|
|
71
|
-
try {
|
|
72
|
-
if (explicitDeps) {
|
|
73
|
-
if (Array.isArray(explicitDeps)) {
|
|
74
|
-
explicitDeps.forEach(dep => {
|
|
75
|
-
if (typeof dep === 'function') dep();
|
|
76
|
-
});
|
|
77
|
-
} else if (typeof explicitDeps === 'function') {
|
|
78
|
-
explicitDeps();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (typeof callback === 'function') {
|
|
82
|
-
const res = callback();
|
|
83
|
-
if (typeof res === 'function') {
|
|
84
|
-
execute._cleanup = res;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (owner && owner.isMounted) triggerUpdate(owner);
|
|
89
|
-
|
|
90
|
-
} catch (e) {
|
|
91
|
-
if (isPromiseLike(e)) throw e;
|
|
92
|
-
const name = owner ? (owner.name ?? 'Anonymous') : null;
|
|
93
|
-
reportErrorSafe(e, { phase: 'effect', component: name });
|
|
94
|
-
} finally {
|
|
95
|
-
context.pop();
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
execute.dependencies = new Set();
|
|
100
|
-
execute._cleanup = null;
|
|
101
|
-
|
|
102
|
-
if (options.onLoad) {
|
|
103
|
-
onMount(execute);
|
|
104
|
-
} else {
|
|
105
|
-
execute();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return () => {
|
|
109
|
-
if (typeof execute._cleanup === 'function') {
|
|
110
|
-
try {
|
|
111
|
-
execute._cleanup();
|
|
112
|
-
} catch (e) {
|
|
113
|
-
const name = owner ? (owner.name ?? 'Anonymous') : null;
|
|
114
|
-
reportErrorSafe(e, { phase: 'effect.cleanup', component: name });
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
execute._cleanup = null;
|
|
118
|
-
cleanup(execute);
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function cleanup(running) {
|
|
123
|
-
running.dependencies.forEach(dep => dep.delete(running));
|
|
124
|
-
running.dependencies.clear();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function defineBindMarkerIfNeeded(source, target) {
|
|
128
|
-
if (source && source.bind === true) {
|
|
129
|
-
try {
|
|
130
|
-
Object.defineProperty(target, 'bind', {
|
|
131
|
-
enumerable: true,
|
|
132
|
-
configurable: false,
|
|
133
|
-
writable: false,
|
|
134
|
-
value: true
|
|
135
|
-
});
|
|
136
|
-
} catch {
|
|
137
|
-
try { target.bind = true; } catch { }
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function attachHelpers(s) {
|
|
143
|
-
if (!s || typeof s !== 'function') return s;
|
|
144
|
-
if (typeof s.transform === 'function' && typeof s.validate === 'function' && typeof s.$pick === 'function') return s;
|
|
145
|
-
|
|
146
|
-
s.$pick = (p) => {
|
|
147
|
-
return pick(s, p);
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
s.transform = (fromInput, toOutput) => {
|
|
151
|
-
const fromFn = typeof fromInput === 'function' ? fromInput : (v) => v;
|
|
152
|
-
const toFn = typeof toOutput === 'function' ? toOutput : (v) => v;
|
|
153
|
-
|
|
154
|
-
const wrapped = function (...args) {
|
|
155
|
-
if (args.length > 0) {
|
|
156
|
-
return s(fromFn(args[0]));
|
|
157
|
-
}
|
|
158
|
-
return toFn(s());
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
wrapped.peek = () => toFn(s.peek());
|
|
162
|
-
Object.defineProperty(wrapped, 'value', {
|
|
163
|
-
enumerable: true,
|
|
164
|
-
get() {
|
|
165
|
-
return wrapped.peek();
|
|
166
|
-
},
|
|
167
|
-
set(v) {
|
|
168
|
-
wrapped(v);
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
defineBindMarkerIfNeeded(s, wrapped);
|
|
173
|
-
return attachHelpers(wrapped);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
s.validate = (validator, options = {}) => {
|
|
177
|
-
const validateFn = typeof validator === 'function' ? validator : null;
|
|
178
|
-
const error = signal(null);
|
|
179
|
-
const validateOn = (options && typeof options === 'object' && typeof options.validateOn === 'string')
|
|
180
|
-
? options.validateOn
|
|
181
|
-
: 'input';
|
|
182
|
-
const validateInitial = Boolean(options && typeof options === 'object' && options.validateInitial);
|
|
183
|
-
|
|
184
|
-
const wrapped = function (...args) {
|
|
185
|
-
if (args.length > 0) {
|
|
186
|
-
const next = args[0];
|
|
187
|
-
if (validateFn) {
|
|
188
|
-
let res = true;
|
|
189
|
-
try {
|
|
190
|
-
res = validateFn(next, s.peek());
|
|
191
|
-
} catch {
|
|
192
|
-
res = 'Invalid value';
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (res === true || res === undefined || res === null) {
|
|
196
|
-
error(null);
|
|
197
|
-
return s(next);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (typeof res === 'string' && res.length) {
|
|
201
|
-
error(res);
|
|
202
|
-
} else {
|
|
203
|
-
error('Invalid value');
|
|
204
|
-
}
|
|
205
|
-
return s.peek();
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
error(null);
|
|
209
|
-
return s(next);
|
|
210
|
-
}
|
|
211
|
-
return s();
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
wrapped.check = () => {
|
|
215
|
-
if (!validateFn) {
|
|
216
|
-
error(null);
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
const cur = s.peek();
|
|
220
|
-
let res = true;
|
|
221
|
-
try {
|
|
222
|
-
res = validateFn(cur, cur);
|
|
223
|
-
} catch {
|
|
224
|
-
res = 'Invalid value';
|
|
225
|
-
}
|
|
226
|
-
if (res === true || res === undefined || res === null) {
|
|
227
|
-
error(null);
|
|
228
|
-
return true;
|
|
229
|
-
}
|
|
230
|
-
if (typeof res === 'string' && res.length) error(res);
|
|
231
|
-
else error('Invalid value');
|
|
232
|
-
return false;
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
wrapped.peek = () => s.peek();
|
|
236
|
-
Object.defineProperty(wrapped, 'value', {
|
|
237
|
-
enumerable: true,
|
|
238
|
-
get() {
|
|
239
|
-
return wrapped.peek();
|
|
240
|
-
},
|
|
241
|
-
set(v) {
|
|
242
|
-
wrapped(v);
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
wrapped.error = error;
|
|
247
|
-
wrapped.__round_validateOn = validateOn;
|
|
248
|
-
if (validateInitial) {
|
|
249
|
-
try { wrapped.check(); } catch { }
|
|
250
|
-
}
|
|
251
|
-
defineBindMarkerIfNeeded(s, wrapped);
|
|
252
|
-
return attachHelpers(wrapped);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
return s;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Create a reactive signal.
|
|
260
|
-
* @template T
|
|
261
|
-
* @param {T} [initialValue] The starting value.
|
|
262
|
-
* @returns {RoundSignal<T>} A signal function that reads/writes the value.
|
|
263
|
-
*/
|
|
264
|
-
export function signal(initialValue) {
|
|
265
|
-
let value = initialValue;
|
|
266
|
-
const subscriptions = new Set();
|
|
267
|
-
|
|
268
|
-
const read = () => {
|
|
269
|
-
const running = context[context.length - 1];
|
|
270
|
-
if (running) {
|
|
271
|
-
subscribe(running, subscriptions);
|
|
272
|
-
}
|
|
273
|
-
return value;
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
const peek = () => value;
|
|
277
|
-
|
|
278
|
-
const write = (newValue) => {
|
|
279
|
-
if (value !== newValue) {
|
|
280
|
-
value = newValue;
|
|
281
|
-
[...subscriptions].forEach(sub => sub());
|
|
282
|
-
}
|
|
283
|
-
return value;
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
const signal = function (...args) {
|
|
287
|
-
if (args.length > 0) {
|
|
288
|
-
return write(args[0]);
|
|
289
|
-
}
|
|
290
|
-
return read();
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
Object.defineProperty(signal, 'value', {
|
|
294
|
-
enumerable: true,
|
|
295
|
-
get() {
|
|
296
|
-
return peek();
|
|
297
|
-
},
|
|
298
|
-
set(v) {
|
|
299
|
-
write(v);
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
signal.peek = peek;
|
|
304
|
-
|
|
305
|
-
return attachHelpers(signal);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Create a bindable signal intended for two-way DOM bindings.
|
|
310
|
-
* @template T
|
|
311
|
-
* @param {T} [initialValue] The starting value.
|
|
312
|
-
* @returns {RoundSignal<T>} A signal function marked as bindable.
|
|
313
|
-
*/
|
|
314
|
-
export function bindable(initialValue) {
|
|
315
|
-
const s = signal(initialValue);
|
|
316
|
-
try {
|
|
317
|
-
Object.defineProperty(s, 'bind', {
|
|
318
|
-
enumerable: true,
|
|
319
|
-
configurable: false,
|
|
320
|
-
writable: false,
|
|
321
|
-
value: true
|
|
322
|
-
});
|
|
323
|
-
} catch {
|
|
324
|
-
// Fallback if defineProperty fails
|
|
325
|
-
try { s.bind = true; } catch { }
|
|
326
|
-
}
|
|
327
|
-
return attachHelpers(s);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function isSignalLike(v) {
|
|
331
|
-
return typeof v === 'function' && typeof v.peek === 'function' && ('value' in v);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function getIn(obj, path) {
|
|
335
|
-
let cur = obj;
|
|
336
|
-
for (const key of path) {
|
|
337
|
-
if (cur == null) return undefined;
|
|
338
|
-
cur = cur[key];
|
|
339
|
-
}
|
|
340
|
-
return cur;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function setIn(obj, path, value) {
|
|
344
|
-
if (!Array.isArray(path) || path.length === 0) return value;
|
|
345
|
-
|
|
346
|
-
const root = (obj && typeof obj === 'object') ? obj : {};
|
|
347
|
-
const out = Array.isArray(root) ? root.slice() : { ...root };
|
|
348
|
-
|
|
349
|
-
let curOut = out;
|
|
350
|
-
let curIn = root;
|
|
351
|
-
|
|
352
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
353
|
-
const key = path[i];
|
|
354
|
-
const nextIn = (curIn && typeof curIn === 'object') ? curIn[key] : undefined;
|
|
355
|
-
const nextOut = (nextIn && typeof nextIn === 'object')
|
|
356
|
-
? (Array.isArray(nextIn) ? nextIn.slice() : { ...nextIn })
|
|
357
|
-
: {};
|
|
358
|
-
|
|
359
|
-
curOut[key] = nextOut;
|
|
360
|
-
curOut = nextOut;
|
|
361
|
-
curIn = nextIn;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
curOut[path[path.length - 1]] = value;
|
|
365
|
-
return out;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
function parsePath(path) {
|
|
369
|
-
if (Array.isArray(path)) return path.map(p => String(p));
|
|
370
|
-
if (typeof path === 'string') return path.split('.').filter(Boolean);
|
|
371
|
-
return [String(path)];
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Create a read/write view of a specific path within a signal object.
|
|
376
|
-
* @param {RoundSignal<any>} root The source signal.
|
|
377
|
-
* @param {string | string[]} path The property path (e.g., 'user.profile.name' or ['user', 'profile', 'name']).
|
|
378
|
-
* @returns {RoundSignal<any>} A signal-like view of the path.
|
|
379
|
-
*/
|
|
380
|
-
export function pick(root, path) {
|
|
381
|
-
if (!isSignalLike(root)) {
|
|
382
|
-
throw new Error('[round] pick(root, path) expects root to be a signal (use bindable.object(...) or signal({...})).');
|
|
383
|
-
}
|
|
384
|
-
const pathArr = parsePath(path);
|
|
385
|
-
|
|
386
|
-
const view = function (...args) {
|
|
387
|
-
if (args.length > 0) {
|
|
388
|
-
const nextRoot = setIn(root.peek(), pathArr, args[0]);
|
|
389
|
-
return root(nextRoot);
|
|
390
|
-
}
|
|
391
|
-
const v = root();
|
|
392
|
-
return getIn(v, pathArr);
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
view.peek = () => getIn(root.peek(), pathArr);
|
|
396
|
-
Object.defineProperty(view, 'value', {
|
|
397
|
-
enumerable: true,
|
|
398
|
-
get() {
|
|
399
|
-
return view.peek();
|
|
400
|
-
},
|
|
401
|
-
set(v) {
|
|
402
|
-
view(v);
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
if (root.bind === true) {
|
|
407
|
-
try {
|
|
408
|
-
Object.defineProperty(view, 'bind', {
|
|
409
|
-
enumerable: true,
|
|
410
|
-
configurable: false,
|
|
411
|
-
writable: false,
|
|
412
|
-
value: true
|
|
413
|
-
});
|
|
414
|
-
} catch {
|
|
415
|
-
try { view.bind = true; } catch { }
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
return view;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
function createBindableObjectProxy(root, basePath) {
|
|
423
|
-
const cache = new Map();
|
|
424
|
-
|
|
425
|
-
const handler = {
|
|
426
|
-
get(_target, prop) {
|
|
427
|
-
if (prop === Symbol.toStringTag) return 'BindableObject';
|
|
428
|
-
if (prop === Symbol.iterator) return undefined;
|
|
429
|
-
if (prop === 'peek') return () => (basePath.length ? pick(root, basePath).peek() : root.peek());
|
|
430
|
-
if (prop === 'value') return (basePath.length ? pick(root, basePath).peek() : root.peek());
|
|
431
|
-
if (prop === 'bind') return true;
|
|
432
|
-
if (prop === '$pick') {
|
|
433
|
-
return (p) => {
|
|
434
|
-
const nextPath = basePath.concat(parsePath(p));
|
|
435
|
-
return createBindableObjectProxy(root, nextPath);
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
if (prop === '_root') return root;
|
|
439
|
-
if (prop === '_path') return basePath.slice();
|
|
440
|
-
|
|
441
|
-
// Allow calling the proxy (it's a function proxy below)
|
|
442
|
-
if (prop === 'call' || prop === 'apply') {
|
|
443
|
-
return Reflect.get(_target, prop);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const key = String(prop);
|
|
447
|
-
const nextPath = basePath.concat(key);
|
|
448
|
-
const cacheKey = nextPath.join('.');
|
|
449
|
-
if (cache.has(cacheKey)) return cache.get(cacheKey);
|
|
450
|
-
|
|
451
|
-
// If the stored value at this path is itself a signal/bindable, return it directly.
|
|
452
|
-
// This enables bindable.object({ email: bindable('').validate(...) }) patterns.
|
|
453
|
-
try {
|
|
454
|
-
const stored = getIn(root.peek(), nextPath);
|
|
455
|
-
if (isSignalLike(stored)) {
|
|
456
|
-
cache.set(cacheKey, stored);
|
|
457
|
-
return stored;
|
|
458
|
-
}
|
|
459
|
-
} catch {
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
const next = createBindableObjectProxy(root, nextPath);
|
|
463
|
-
cache.set(cacheKey, next);
|
|
464
|
-
return next;
|
|
465
|
-
},
|
|
466
|
-
set(_target, prop, value) {
|
|
467
|
-
const key = String(prop);
|
|
468
|
-
const nextPath = basePath.concat(key);
|
|
469
|
-
try {
|
|
470
|
-
const stored = getIn(root.peek(), nextPath);
|
|
471
|
-
if (isSignalLike(stored)) {
|
|
472
|
-
stored(value);
|
|
473
|
-
return true;
|
|
474
|
-
}
|
|
475
|
-
} catch {
|
|
476
|
-
}
|
|
477
|
-
pick(root, nextPath)(value);
|
|
478
|
-
return true;
|
|
479
|
-
},
|
|
480
|
-
has(_target, prop) {
|
|
481
|
-
// IMPORTANT: Proxy invariants require that if the target has a non-configurable
|
|
482
|
-
// property, the `has` trap must return true.
|
|
483
|
-
try {
|
|
484
|
-
if (Reflect.has(_target, prop)) return true;
|
|
485
|
-
} catch {
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const v = basePath.length ? pick(root, basePath).peek() : root.peek();
|
|
489
|
-
return v != null && Object.prototype.hasOwnProperty.call(v, prop);
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
|
|
493
|
-
// Function proxy so you can do user() / user.name() etc.
|
|
494
|
-
const fn = function (...args) {
|
|
495
|
-
if (args.length > 0) {
|
|
496
|
-
if (basePath.length) return pick(root, basePath)(args[0]);
|
|
497
|
-
return root(args[0]);
|
|
498
|
-
}
|
|
499
|
-
if (basePath.length) return pick(root, basePath)();
|
|
500
|
-
return root();
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
// Make it signal-like
|
|
504
|
-
fn.peek = () => (basePath.length ? pick(root, basePath).peek() : root.peek());
|
|
505
|
-
Object.defineProperty(fn, 'value', {
|
|
506
|
-
enumerable: true,
|
|
507
|
-
get() {
|
|
508
|
-
return fn.peek();
|
|
509
|
-
},
|
|
510
|
-
set(v) {
|
|
511
|
-
fn(v);
|
|
512
|
-
}
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
try {
|
|
516
|
-
Object.defineProperty(fn, 'bind', {
|
|
517
|
-
enumerable: true,
|
|
518
|
-
configurable: false,
|
|
519
|
-
writable: false,
|
|
520
|
-
value: true
|
|
521
|
-
});
|
|
522
|
-
} catch {
|
|
523
|
-
try { fn.bind = true; } catch { }
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
return new Proxy(fn, handler);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
bindable.object = function (initialObject = {}) {
|
|
530
|
-
const root = bindable((initialObject && typeof initialObject === 'object') ? initialObject : {});
|
|
531
|
-
return createBindableObjectProxy(root, []);
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
/**
|
|
535
|
-
* Create a read-only computed signal derived from other signals.
|
|
536
|
-
* @template T
|
|
537
|
-
* @param {() => T} fn A function that computes the value.
|
|
538
|
-
* @returns {(() => T)} A function that returns the derived value.
|
|
539
|
-
*/
|
|
540
|
-
export function derive(fn) {
|
|
541
|
-
const derived = signal();
|
|
542
|
-
|
|
543
|
-
effect(() => {
|
|
544
|
-
derived(fn());
|
|
545
|
-
}, { onLoad: false });
|
|
546
|
-
|
|
547
|
-
return () => derived();
|
|
548
|
-
}
|