@pyreon/solid-compat 0.13.0 → 0.14.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.
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
5386
5386
  </script>
5387
5387
  <script>
5388
5388
  /*<!--*/
5389
- const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"491c766f-1","name":"jsx-runtime.ts"},{"uid":"491c766f-3","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"491c766f-1":{"renderedLength":186,"gzipLength":138,"brotliLength":0,"metaUid":"491c766f-0"},"491c766f-3":{"renderedLength":5815,"gzipLength":1730,"brotliLength":0,"metaUid":"491c766f-2"}},"nodeMetas":{"491c766f-0":{"id":"/src/jsx-runtime.ts","moduleParts":{"index.js":"491c766f-1"},"imported":[{"uid":"491c766f-4"},{"uid":"491c766f-5"}],"importedBy":[{"uid":"491c766f-2"}]},"491c766f-2":{"id":"/src/index.ts","moduleParts":{"index.js":"491c766f-3"},"imported":[{"uid":"491c766f-4"},{"uid":"491c766f-5"},{"uid":"491c766f-0"}],"importedBy":[],"isEntry":true},"491c766f-4":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"491c766f-2"},{"uid":"491c766f-0"}]},"491c766f-5":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"491c766f-2"},{"uid":"491c766f-0"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5389
+ const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"7b35df5d-1","name":"jsx-runtime.ts"},{"uid":"7b35df5d-3","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"7b35df5d-1":{"renderedLength":186,"gzipLength":138,"brotliLength":0,"metaUid":"7b35df5d-0"},"7b35df5d-3":{"renderedLength":19175,"gzipLength":5477,"brotliLength":0,"metaUid":"7b35df5d-2"}},"nodeMetas":{"7b35df5d-0":{"id":"/src/jsx-runtime.ts","moduleParts":{"index.js":"7b35df5d-1"},"imported":[{"uid":"7b35df5d-4"},{"uid":"7b35df5d-5"}],"importedBy":[{"uid":"7b35df5d-2"}]},"7b35df5d-2":{"id":"/src/index.ts","moduleParts":{"index.js":"7b35df5d-3"},"imported":[{"uid":"7b35df5d-4"},{"uid":"7b35df5d-5"},{"uid":"7b35df5d-0"}],"importedBy":[],"isEntry":true},"7b35df5d-4":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"7b35df5d-2"},{"uid":"7b35df5d-0"}]},"7b35df5d-5":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"7b35df5d-2"},{"uid":"7b35df5d-0"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5390
5390
 
5391
5391
  const run = () => {
5392
5392
  const width = window.innerWidth;
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
5386
5386
  </script>
5387
5387
  <script>
5388
5388
  /*<!--*/
5389
- const data = {"version":2,"tree":{"name":"root","children":[{"name":"jsx-runtime.js","children":[{"name":"src","children":[{"uid":"a89fb633-1","name":"jsx-runtime.ts"},{"uid":"a89fb633-3","name":"jsx-dev-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"a89fb633-1":{"renderedLength":3959,"gzipLength":1335,"brotliLength":0,"metaUid":"a89fb633-0"},"a89fb633-3":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"a89fb633-2"}},"nodeMetas":{"a89fb633-0":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-runtime.js":"a89fb633-1"},"imported":[{"uid":"a89fb633-4"},{"uid":"a89fb633-5"}],"importedBy":[{"uid":"a89fb633-2"}]},"a89fb633-2":{"id":"/src/jsx-dev-runtime.ts","moduleParts":{"jsx-runtime.js":"a89fb633-3"},"imported":[{"uid":"a89fb633-0"}],"importedBy":[],"isEntry":true},"a89fb633-4":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"a89fb633-0"}]},"a89fb633-5":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"a89fb633-0"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5389
+ const data = {"version":2,"tree":{"name":"root","children":[{"name":"jsx-runtime.js","children":[{"name":"src","children":[{"uid":"e600c1ac-1","name":"jsx-runtime.ts"},{"uid":"e600c1ac-3","name":"jsx-dev-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"e600c1ac-1":{"renderedLength":4138,"gzipLength":1377,"brotliLength":0,"metaUid":"e600c1ac-0"},"e600c1ac-3":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"e600c1ac-2"}},"nodeMetas":{"e600c1ac-0":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-runtime.js":"e600c1ac-1"},"imported":[{"uid":"e600c1ac-4"},{"uid":"e600c1ac-5"}],"importedBy":[{"uid":"e600c1ac-2"}]},"e600c1ac-2":{"id":"/src/jsx-dev-runtime.ts","moduleParts":{"jsx-runtime.js":"e600c1ac-3"},"imported":[{"uid":"e600c1ac-0"}],"importedBy":[],"isEntry":true},"e600c1ac-4":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"e600c1ac-0"}]},"e600c1ac-5":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"e600c1ac-0"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5390
5390
 
5391
5391
  const run = () => {
5392
5392
  const width = window.innerWidth;
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { ErrorBoundary, For, Match, Show, Suspense, Switch, createContext as pyreonCreateContext, onMount as onMount$1, onUnmount, useContext as pyreonUseContext } from "@pyreon/core";
2
- import { batch as pyreonBatch, computed, createSelector as createSelector$1, effect, effectScope, getCurrentScope, runUntracked, setCurrentScope, signal } from "@pyreon/reactivity";
1
+ import { ErrorBoundary, For, Match, Show, Suspense, Switch, createContext as createContext$1, onMount as onMount$1, onUnmount, provide, useContext as useContext$1 } from "@pyreon/core";
2
+ import { batch as pyreonBatch, computed, createSelector as createSelector$1, effect, effectScope, getCurrentScope, onCleanup as onCleanup$1, runUntracked, setCurrentScope, signal } from "@pyreon/reactivity";
3
3
 
4
4
  //#region src/jsx-runtime.ts
5
5
  let _currentCtx = null;
@@ -13,48 +13,93 @@ function getHookIndex() {
13
13
 
14
14
  //#endregion
15
15
  //#region src/index.ts
16
- function createSignal(initialValue) {
16
+ function createSignal(initialValue, options) {
17
+ const neverEqual = options?.equals === false;
17
18
  const ctx = getCurrentCtx();
18
19
  if (ctx) {
19
20
  const idx = getHookIndex();
20
- if (idx >= ctx.hooks.length) ctx.hooks[idx] = signal(initialValue);
21
- const s = ctx.hooks[idx];
22
- const { scheduleRerender } = ctx;
23
- const getter = () => s();
21
+ if (idx >= ctx.hooks.length) {
22
+ const { scheduleRerender } = ctx;
23
+ let getter;
24
+ let setter;
25
+ if (neverEqual) {
26
+ const s = signal({ v: initialValue });
27
+ getter = () => s().v;
28
+ setter = (v) => {
29
+ const prev = s.peek().v;
30
+ const next = typeof v === "function" ? v(prev) : v;
31
+ s.set({ v: next });
32
+ scheduleRerender();
33
+ };
34
+ } else {
35
+ const s = signal(initialValue);
36
+ getter = () => s();
37
+ setter = (v) => {
38
+ const prev = s.peek();
39
+ const next = typeof v === "function" ? v(prev) : v;
40
+ if (shouldSkipUpdate(prev, next, options)) return;
41
+ s.set(next);
42
+ scheduleRerender();
43
+ };
44
+ }
45
+ ctx.hooks[idx] = {
46
+ signal: null,
47
+ getter,
48
+ setter
49
+ };
50
+ }
51
+ const entry = ctx.hooks[idx];
52
+ return [entry.getter, entry.setter];
53
+ }
54
+ if (neverEqual) {
55
+ const s = signal({ v: initialValue });
56
+ const getter = () => s().v;
24
57
  const setter = (v) => {
25
- if (typeof v === "function") s.update(v);
26
- else s.set(v);
27
- scheduleRerender();
58
+ const prev = s.peek().v;
59
+ const next = typeof v === "function" ? v(prev) : v;
60
+ s.set({ v: next });
28
61
  };
29
62
  return [getter, setter];
30
63
  }
31
64
  const s = signal(initialValue);
32
65
  const getter = () => s();
33
66
  const setter = (v) => {
34
- if (typeof v === "function") s.update(v);
35
- else s.set(v);
67
+ const prev = s.peek();
68
+ const next = typeof v === "function" ? v(prev) : v;
69
+ if (shouldSkipUpdate(prev, next, options)) return;
70
+ s.set(next);
36
71
  };
37
72
  return [getter, setter];
38
73
  }
74
+ /** Solid default: skip update when prev === next. Custom `equals` fn for user-defined comparison. */
75
+ function shouldSkipUpdate(prev, next, options) {
76
+ if (typeof options?.equals === "function") return options.equals(prev, next);
77
+ return prev === next;
78
+ }
39
79
  /**
40
80
  * Solid-compatible `createEffect` — creates a reactive side effect.
41
81
  *
82
+ * Supports the `(prev) => next` signature with an optional initial value,
83
+ * matching Solid's `createEffect<T>(fn: (prev: T) => T, initialValue: T)`.
84
+ *
42
85
  * In component context: hook-indexed, only created on first render. The effect
43
86
  * uses Pyreon's native tracking so signal reads are automatically tracked.
44
87
  * A re-entrance guard prevents infinite loops from signal writes inside
45
88
  * the effect.
46
89
  */
47
- function createEffect(fn) {
90
+ function createEffect(fn, initialValue) {
48
91
  const ctx = getCurrentCtx();
49
92
  if (ctx) {
50
93
  const idx = getHookIndex();
51
94
  if (idx < ctx.hooks.length) return;
95
+ let prevValue = initialValue;
52
96
  let running = false;
53
97
  const e = effect(() => {
54
98
  if (running) return;
55
99
  running = true;
56
100
  try {
57
- fn();
101
+ const result = fn(prevValue);
102
+ if (result !== void 0) prevValue = result;
58
103
  } finally {
59
104
  running = false;
60
105
  }
@@ -64,7 +109,11 @@ function createEffect(fn) {
64
109
  ctx.unmountCallbacks.push(stop);
65
110
  return;
66
111
  }
67
- effect(fn);
112
+ let prevValue = initialValue;
113
+ effect(() => {
114
+ const result = fn(prevValue);
115
+ if (result !== void 0) prevValue = result;
116
+ });
68
117
  }
69
118
  /**
70
119
  * Solid-compatible `createRenderEffect` — same as createEffect.
@@ -76,18 +125,34 @@ function createRenderEffect(fn) {
76
125
  /**
77
126
  * Solid-compatible `createMemo` — derives a value from reactive sources.
78
127
  *
128
+ * Supports the `(prev) => next` signature with an optional initial value,
129
+ * matching Solid's `createMemo<T>(fn: (prev: T) => T, initialValue: T)`.
130
+ *
79
131
  * In component context: hook-indexed, only created on first render.
80
132
  * Uses Pyreon's native computed for auto-tracking.
81
133
  */
82
- function createMemo(fn) {
134
+ function createMemo(fn, initialValue) {
83
135
  const ctx = getCurrentCtx();
84
136
  if (ctx) {
85
137
  const idx = getHookIndex();
86
- if (idx >= ctx.hooks.length) ctx.hooks[idx] = computed(fn);
138
+ if (idx >= ctx.hooks.length) {
139
+ let prevValue = initialValue;
140
+ const c = computed(() => {
141
+ const result = fn(prevValue);
142
+ prevValue = result;
143
+ return result;
144
+ });
145
+ ctx.hooks[idx] = c;
146
+ }
87
147
  const c = ctx.hooks[idx];
88
148
  return () => c();
89
149
  }
90
- const c = computed(fn);
150
+ let prevValue = initialValue;
151
+ const c = computed(() => {
152
+ const result = fn(prevValue);
153
+ prevValue = result;
154
+ return result;
155
+ });
91
156
  return () => c();
92
157
  }
93
158
  function createRoot(fn) {
@@ -100,7 +165,7 @@ function createRoot(fn) {
100
165
  setCurrentScope(prev);
101
166
  }
102
167
  }
103
- function on(deps, fn) {
168
+ function on(deps, fn, options) {
104
169
  let prevInput;
105
170
  let prevValue;
106
171
  let initialized = false;
@@ -108,6 +173,10 @@ function on(deps, fn) {
108
173
  const input = Array.isArray(deps) ? deps.map((d) => d()) : deps();
109
174
  if (!initialized) {
110
175
  initialized = true;
176
+ if (options?.defer) {
177
+ prevInput = input;
178
+ return prevValue;
179
+ }
111
180
  prevValue = fn(input, void 0, void 0);
112
181
  prevInput = input;
113
182
  return prevValue;
@@ -243,6 +312,43 @@ function lazy(loader) {
243
312
  LazyComp.preload = load;
244
313
  return LazyComp;
245
314
  }
315
+ const SOLID_CTX = Symbol.for("pyreon:solid-ctx");
316
+ const SOLID_CTX_BRAND = SOLID_CTX;
317
+ const NATIVE_COMPONENT = Symbol.for("pyreon:native-compat");
318
+ /**
319
+ * Solid-compatible `createContext` — creates a context with a `.Provider`
320
+ * component. Uses Pyreon's native context stack for tree-scoped nesting.
321
+ */
322
+ function createContext(defaultValue) {
323
+ const pyreonCtx = createContext$1(defaultValue);
324
+ const Provider = (props) => {
325
+ const { value, children } = props;
326
+ provide(pyreonCtx, value);
327
+ return children ?? null;
328
+ };
329
+ Provider[NATIVE_COMPONENT] = true;
330
+ return {
331
+ [SOLID_CTX_BRAND]: true,
332
+ id: pyreonCtx.id,
333
+ defaultValue,
334
+ Provider
335
+ };
336
+ }
337
+ /**
338
+ * Solid-compatible `useContext` — reads the nearest provided value for a context.
339
+ * Works with both compat contexts (from this module's `createContext`) and
340
+ * Pyreon native contexts (from `@pyreon/core`).
341
+ */
342
+ function useContext(context) {
343
+ if (SOLID_CTX in context) {
344
+ const solidCtx = context;
345
+ return useContext$1({
346
+ id: solidCtx.id,
347
+ defaultValue: solidCtx.defaultValue
348
+ });
349
+ }
350
+ return useContext$1(context);
351
+ }
246
352
  function getOwner() {
247
353
  return getCurrentScope();
248
354
  }
@@ -255,7 +361,341 @@ function runWithOwner(owner, fn) {
255
361
  setCurrentScope(prev);
256
362
  }
257
363
  }
364
+ function createResource(sourceOrFetcher, maybeFetcherOrOptions, maybeOptions) {
365
+ const hasSource = typeof maybeFetcherOrOptions === "function";
366
+ const source = hasSource ? sourceOrFetcher : (() => true);
367
+ const fetcher = hasSource ? maybeFetcherOrOptions : sourceOrFetcher;
368
+ const initialValue = (hasSource ? maybeOptions : typeof maybeFetcherOrOptions === "object" ? maybeFetcherOrOptions : void 0)?.initialValue;
369
+ const [data, setData] = createSignal(initialValue);
370
+ const [loading, setLoading] = createSignal(false);
371
+ const [error, setError] = createSignal(void 0);
372
+ let latestValue = initialValue;
373
+ let fetchPromise = null;
374
+ const doFetch = () => {
375
+ const src = typeof source === "function" ? source() : source;
376
+ if (src === false || src === null || src === void 0) return;
377
+ setLoading(true);
378
+ setError(void 0);
379
+ try {
380
+ const result = fetcher(src, { value: latestValue });
381
+ if (result instanceof Promise) {
382
+ fetchPromise = result;
383
+ result.then((val) => {
384
+ latestValue = val;
385
+ fetchPromise = null;
386
+ setData(() => val);
387
+ setLoading(false);
388
+ }, (err) => {
389
+ fetchPromise = null;
390
+ setError(() => err instanceof Error ? err : new Error(String(err)));
391
+ setLoading(false);
392
+ });
393
+ } else {
394
+ latestValue = result;
395
+ fetchPromise = null;
396
+ setData(() => result);
397
+ setLoading(false);
398
+ }
399
+ } catch (err) {
400
+ setError(() => err instanceof Error ? err : new Error(String(err)));
401
+ setLoading(false);
402
+ }
403
+ };
404
+ if (hasSource && typeof source === "function") createEffect(() => {
405
+ source();
406
+ doFetch();
407
+ });
408
+ else doFetch();
409
+ const resource = (() => {
410
+ const err = error();
411
+ if (err) throw err;
412
+ const current = data();
413
+ if (loading() && fetchPromise && current === void 0) throw fetchPromise;
414
+ return current;
415
+ });
416
+ Object.defineProperty(resource, "loading", {
417
+ get: () => loading(),
418
+ enumerable: true
419
+ });
420
+ Object.defineProperty(resource, "error", {
421
+ get: () => error(),
422
+ enumerable: true
423
+ });
424
+ Object.defineProperty(resource, "latest", {
425
+ get: () => latestValue,
426
+ enumerable: true
427
+ });
428
+ const mutate = (v) => {
429
+ if (typeof v === "function") {
430
+ const next = v(data());
431
+ latestValue = next;
432
+ setData(() => next);
433
+ } else {
434
+ latestValue = v;
435
+ setData(() => v);
436
+ }
437
+ };
438
+ const refetch = () => doFetch();
439
+ return [resource, {
440
+ mutate,
441
+ refetch
442
+ }];
443
+ }
444
+ /**
445
+ * Deep clones plain objects and arrays. Functions, DOM nodes, class instances,
446
+ * and other non-plain values are kept by reference — `structuredClone` would
447
+ * throw on them.
448
+ */
449
+ function deepClone(obj) {
450
+ if (obj === null || typeof obj !== "object") return obj;
451
+ if (Array.isArray(obj)) return obj.map((item) => deepClone(item));
452
+ if (obj.constructor !== Object && obj.constructor !== Array) return obj;
453
+ const result = {};
454
+ for (const key of Object.keys(obj)) result[key] = deepClone(obj[key]);
455
+ return result;
456
+ }
457
+ function createStore(initialValue) {
458
+ const signals = /* @__PURE__ */ new Map();
459
+ let raw = deepClone(initialValue);
460
+ function getByPath(obj, path) {
461
+ if (!path) return obj;
462
+ return path.split(".").reduce((o, k) => o?.[k], obj);
463
+ }
464
+ function getSignal(path) {
465
+ let sig = signals.get(path);
466
+ if (!sig) {
467
+ sig = signal(getByPath(raw, path));
468
+ signals.set(path, sig);
469
+ }
470
+ return sig;
471
+ }
472
+ function resolveValue(basePath) {
473
+ return basePath ? getByPath(raw, basePath) : raw;
474
+ }
475
+ function makeProxy(basePath) {
476
+ return new Proxy({}, {
477
+ get(_target, prop) {
478
+ if (typeof prop === "symbol") return resolveValue(basePath)?.[prop];
479
+ const path = basePath ? `${basePath}.${String(prop)}` : String(prop);
480
+ getSignal(path)();
481
+ const value = getByPath(raw, path);
482
+ if (value !== null && typeof value === "object") return makeProxy(path);
483
+ return value;
484
+ },
485
+ has(_target, prop) {
486
+ const current = resolveValue(basePath);
487
+ return current !== null && typeof current === "object" && prop in current;
488
+ },
489
+ ownKeys(_target) {
490
+ if (basePath) getSignal(basePath)();
491
+ else getSignal("__keys__")();
492
+ const current = resolveValue(basePath);
493
+ return current !== null && typeof current === "object" ? Reflect.ownKeys(current) : [];
494
+ },
495
+ getOwnPropertyDescriptor(_target, prop) {
496
+ const current = resolveValue(basePath);
497
+ if (current !== null && typeof current === "object") return Object.getOwnPropertyDescriptor(current, prop);
498
+ },
499
+ set() {
500
+ console.warn("[Pyreon] Direct mutation on store is not supported. Use the setter function.");
501
+ return true;
502
+ }
503
+ });
504
+ }
505
+ const proxy = makeProxy("");
506
+ function updateRaw(newRaw) {
507
+ const oldRaw = raw;
508
+ raw = newRaw;
509
+ for (const [path, sig] of signals) {
510
+ const oldVal = getByPath(oldRaw, path);
511
+ const newVal = getByPath(newRaw, path);
512
+ if (!Object.is(oldVal, newVal)) sig.set(newVal);
513
+ }
514
+ }
515
+ /**
516
+ * Applies a value at a path, supporting numeric indices (array access)
517
+ * and filter predicates (functions that select matching array items).
518
+ */
519
+ function applyAtPath(obj, path, value) {
520
+ if (path.length === 0) {
521
+ if (typeof value === "function") {
522
+ const result = value(obj);
523
+ Object.assign(obj, result);
524
+ } else Object.assign(obj, value);
525
+ return;
526
+ }
527
+ const [head, ...rest] = path;
528
+ if (typeof head === "function") {
529
+ if (Array.isArray(obj)) {
530
+ for (let i = 0; i < obj.length; i++) if (head(obj[i], i)) if (rest.length === 0) obj[i] = typeof value === "function" ? value(obj[i]) : value;
531
+ else applyAtPath(obj[i], rest, value);
532
+ }
533
+ return;
534
+ }
535
+ const key = head;
536
+ if (rest.length === 0) obj[key] = typeof value === "function" ? value(obj[key]) : value;
537
+ else applyAtPath(obj[key], rest, value);
538
+ }
539
+ const setStore = (...args) => {
540
+ if (args.length === 1 && typeof args[0] === "function") {
541
+ const draft = deepClone(raw);
542
+ args[0](draft);
543
+ updateRaw(draft);
544
+ } else if (args.length >= 2) {
545
+ const value = args[args.length - 1];
546
+ const pathArgs = args.slice(0, -1);
547
+ const draft = deepClone(raw);
548
+ applyAtPath(draft, pathArgs, value);
549
+ updateRaw(draft);
550
+ }
551
+ };
552
+ return [proxy, setStore];
553
+ }
554
+ /**
555
+ * Solid-compatible `reconcile` — replaces the entire store state with the given value.
556
+ * Used with setStore: `setStore(reconcile(newData))`
557
+ */
558
+ function reconcile(value) {
559
+ return () => value;
560
+ }
561
+ /**
562
+ * Solid-compatible `unwrap` — returns a deep clone of the store's raw data,
563
+ * stripping the reactive proxy.
564
+ */
565
+ function unwrap(value) {
566
+ return deepClone(value);
567
+ }
568
+ /**
569
+ * Solid-compatible `produce` — creates an Immer-like updater function for stores.
570
+ * Returns a function that clones the state, applies mutations, and returns the result.
571
+ */
572
+ function produce(fn) {
573
+ return (state) => {
574
+ const draft = deepClone(state);
575
+ fn(draft);
576
+ return draft;
577
+ };
578
+ }
579
+ /**
580
+ * Solid-compatible `startTransition` — runs a function as a transition.
581
+ * In Pyreon, this is a no-op wrapper that calls the function synchronously.
582
+ */
583
+ function startTransition(fn) {
584
+ fn();
585
+ }
586
+ /**
587
+ * Solid-compatible `useTransition` — returns `[isPending, startTransition]`.
588
+ * In Pyreon, transitions are not deferred — isPending is always false.
589
+ */
590
+ function useTransition() {
591
+ return [() => false, (fn) => fn()];
592
+ }
593
+ /**
594
+ * Solid-compatible `observable` — converts a signal accessor to an observable.
595
+ * Returns an object with a `subscribe` method that tracks signal changes.
596
+ */
597
+ function observable(input) {
598
+ return { subscribe(observer) {
599
+ const e = effect(() => {
600
+ observer.next(input());
601
+ });
602
+ return { unsubscribe: () => e.dispose() };
603
+ } };
604
+ }
605
+ /**
606
+ * Solid-compatible `from` — converts an observable or producer into a signal accessor.
607
+ * Accepts either a producer function `(setter) => cleanup` or an observable with `.subscribe()`.
608
+ */
609
+ function from(producer) {
610
+ const [value, setValue] = createSignal(void 0);
611
+ if (typeof producer === "function") onCleanup$1(producer((v) => setValue(() => v)));
612
+ else {
613
+ const sub = producer.subscribe({ next: (v) => setValue(() => v) });
614
+ onCleanup$1(() => sub.unsubscribe());
615
+ }
616
+ return value;
617
+ }
618
+ /**
619
+ * Solid-compatible `mapArray` — maps a reactive list by item identity.
620
+ * Each item is a static value, while the index is a reactive accessor.
621
+ */
622
+ function mapArray(list, mapFn) {
623
+ return createMemo(() => {
624
+ return list().map((item, i) => mapFn(item, () => i));
625
+ });
626
+ }
627
+ /**
628
+ * Solid-compatible `indexArray` — maps a reactive list by index position.
629
+ * Each item is a reactive accessor, while the index is a static number.
630
+ */
631
+ function indexArray(list, mapFn) {
632
+ return createMemo(() => {
633
+ return list().map((item, i) => mapFn(() => item, i));
634
+ });
635
+ }
636
+ /**
637
+ * Solid-compatible `Index` — like `For` but keyed by index.
638
+ * Items are reactive accessors, indices are static numbers.
639
+ *
640
+ * In Solid, `<Index>` keeps DOM nodes stable per index position.
641
+ * Here we use a computed that maps items to `(item: () => T, index: number)`.
642
+ */
643
+ function Index(props) {
644
+ const list = typeof props.each === "function" ? props.each : () => props.each;
645
+ const mapped = createMemo(() => {
646
+ return list().map((item, i) => props.children(() => item, i));
647
+ });
648
+ return (() => mapped());
649
+ }
650
+ let _uniqueIdCounter = 0;
651
+ /**
652
+ * Solid-compatible `createUniqueId` — returns a unique string identifier.
653
+ */
654
+ function createUniqueId() {
655
+ return `solid-${(_uniqueIdCounter++).toString(36)}`;
656
+ }
657
+ /**
658
+ * Solid-compatible `DEV` — an object in dev mode, `undefined` in production.
659
+ * Used for conditional dev-only code: `if (DEV) { ... }`
660
+ */
661
+ const DEV = import.meta.env?.DEV === true ? {} : void 0;
662
+ /**
663
+ * Solid-compatible `catchError` — wraps a function and catches synchronous errors.
664
+ */
665
+ function catchError(tryFn, onError) {
666
+ try {
667
+ return tryFn();
668
+ } catch (e) {
669
+ onError(e instanceof Error ? e : new Error(String(e)));
670
+ return;
671
+ }
672
+ }
673
+ /**
674
+ * Solid-compatible `createDeferred` — creates a memo that updates on next idle frame.
675
+ * In Pyreon there is no concurrent scheduling, so this behaves the same as `createMemo`.
676
+ */
677
+ function createDeferred(fn) {
678
+ return createMemo(fn);
679
+ }
680
+ /**
681
+ * Solid-compatible `createReaction` — manual tracking primitive.
682
+ * Returns a function that accepts a tracking function. When any tracked
683
+ * dependency changes, `onInvalidate` fires (but only after the first run).
684
+ */
685
+ function createReaction(onInvalidate) {
686
+ return (trackingFn) => {
687
+ let first = true;
688
+ effect(() => {
689
+ trackingFn();
690
+ if (first) {
691
+ first = false;
692
+ return;
693
+ }
694
+ onInvalidate();
695
+ });
696
+ };
697
+ }
258
698
 
259
699
  //#endregion
260
- export { ErrorBoundary, For, Match, Show, Suspense, Switch, pyreonBatch as batch, children, createEffect as createComputed, createEffect, pyreonCreateContext as createContext, createMemo, createRenderEffect, createRoot, createSelector, createSignal, getOwner, lazy, mergeProps, on, onCleanup, onMount, runWithOwner, splitProps, runUntracked as untrack, pyreonUseContext as useContext };
700
+ export { DEV, ErrorBoundary, For, Index, Match, Show, Suspense, Switch, pyreonBatch as batch, catchError, children, createEffect as createComputed, createEffect, createContext, createDeferred, createMemo, createReaction, createRenderEffect, createResource, createRoot, createSelector, createSignal, createStore, createUniqueId, from, getOwner, indexArray, lazy, mapArray, mergeProps, observable, on, onCleanup, onMount, produce, reconcile, runWithOwner, splitProps, startTransition, runUntracked as untrack, unwrap, useContext, useTransition };
261
701
  //# sourceMappingURL=index.js.map