@pyreon/svelte-compat 0.17.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/lib/index.js ADDED
@@ -0,0 +1,316 @@
1
+ import { i as jsx, n as getCurrentCtx, r as getHookIndex } from "./_chunks/jsx-runtime-Cbf3f1f5.js";
2
+ import { ErrorBoundary, For, Match, Show, Suspense, Switch, createContext, onMount as onMount$1, onUnmount, provide, useContext } from "@pyreon/core";
3
+ import { mount as mount$1 } from "@pyreon/runtime-dom";
4
+
5
+ //#region src/index.ts
6
+ const _countSink = globalThis;
7
+ const noop = () => {};
8
+ let _plainDepth = 0;
9
+ function plainSubscribe(fn) {
10
+ _plainDepth++;
11
+ try {
12
+ return fn();
13
+ } finally {
14
+ _plainDepth--;
15
+ }
16
+ }
17
+ /**
18
+ * Svelte's `safe_not_equal`: primitives dedup, objects/functions always
19
+ * notify (so in-place-mutated store objects still propagate).
20
+ */
21
+ function safeNotEqual(a, b) {
22
+ return a != a ? b == b : a !== b || a !== null && typeof a === "object" || typeof a === "function";
23
+ }
24
+ /**
25
+ * Svelte-compatible `writable`. A faithful Svelte store: a Set of
26
+ * subscribers notified synchronously on `set`/`update`. NOT signal-
27
+ * backed — Svelte's own `writable` is signal-free, and `derived` here
28
+ * subscribes to its inputs explicitly, so no signal auto-tracking is
29
+ * needed.
30
+ *
31
+ * Subscribing inside a compat component body is the faithful equivalent
32
+ * of Svelte's `$store` auto-subscription: the subscriber carries the
33
+ * component's `scheduleRerender`, so a store write re-renders the
34
+ * component — the same "write drives re-render" model the sibling
35
+ * solid-compat layer uses. Crucially this is NOT a persistent tracking
36
+ * effect: such an effect, created inside the wrapper accessor's run, is
37
+ * collected as an inner effect and disposed on the NEXT re-render (the
38
+ * cached path never recreates it), so store changes stopped propagating
39
+ * after the first one. A plain subscriber Set has no such hazard — it
40
+ * lives until `unsub` (registered in `ctx.unmountCallbacks`).
41
+ *
42
+ * `start` runs when the subscriber count goes 0→1 and its returned
43
+ * `stop` runs at 1→0. Synchronous `set` calls inside `start` mutate the
44
+ * value but do NOT notify (the store isn't "ready" until `start`
45
+ * returns), so the first subscriber sees exactly the post-start value —
46
+ * matching Svelte's `derived` (one emission, no spurious initial).
47
+ */
48
+ function writable(value, start = noop) {
49
+ let v = value;
50
+ let stop;
51
+ const subs = /* @__PURE__ */ new Set();
52
+ const setVal = (next) => {
53
+ if (!safeNotEqual(v, next)) return;
54
+ v = next;
55
+ if (!stop) return;
56
+ for (const s of subs) s.invalidate(v);
57
+ for (const s of subs) {
58
+ s.run(v);
59
+ s.rerender?.();
60
+ }
61
+ };
62
+ const set = (next) => setVal(next);
63
+ const update = (fn) => setVal(fn(v));
64
+ const addSub = (entry) => {
65
+ subs.add(entry);
66
+ if (subs.size === 1) stop = start(set, update) || noop;
67
+ entry.run(v);
68
+ return () => {
69
+ subs.delete(entry);
70
+ if (subs.size === 0 && stop) {
71
+ stop();
72
+ stop = void 0;
73
+ }
74
+ };
75
+ };
76
+ return {
77
+ set,
78
+ update,
79
+ subscribe(run, invalidate = noop) {
80
+ const ctx = _plainDepth === 0 ? getCurrentCtx() : null;
81
+ if (ctx) {
82
+ const idx = getHookIndex();
83
+ const cached = ctx.hooks[idx];
84
+ if (cached) {
85
+ run(v);
86
+ if (!ctx.unmountCallbacks.includes(cached.unsub)) {
87
+ ctx.unmountCallbacks.push(cached.unsub);
88
+ if (process.env.NODE_ENV !== "production") _countSink.__pyreon_count__?.("svelte-compat.subscribe.cachedRePush");
89
+ }
90
+ return cached.unsub;
91
+ }
92
+ const unsub = addSub({
93
+ run,
94
+ invalidate,
95
+ rerender: () => {
96
+ if (!ctx.unmounted) ctx.scheduleRerender();
97
+ }
98
+ });
99
+ ctx.hooks[idx] = { unsub };
100
+ ctx.unmountCallbacks.push(unsub);
101
+ return unsub;
102
+ }
103
+ return addSub({
104
+ run,
105
+ invalidate
106
+ });
107
+ }
108
+ };
109
+ }
110
+ /** Svelte-compatible `readable` — a `writable` with `set`/`update` hidden. */
111
+ function readable(value, start) {
112
+ return { subscribe: writable(value, start).subscribe };
113
+ }
114
+ /** Svelte-compatible `readonly` — view of a store exposing only `subscribe`. */
115
+ function readonly(store) {
116
+ return { subscribe: store.subscribe };
117
+ }
118
+ /** Svelte-compatible `get` — read a store's value synchronously. */
119
+ function get(store) {
120
+ let value;
121
+ plainSubscribe(() => {
122
+ store.subscribe((v) => {
123
+ value = v;
124
+ })();
125
+ });
126
+ return value;
127
+ }
128
+ /**
129
+ * Svelte-compatible `derived`. Supports both the sync form
130
+ * `(values) => result` and the async/cleanup form
131
+ * `(values, set, update?) => stop`.
132
+ */
133
+ function derived(stores, fn, initialValue) {
134
+ const single = !Array.isArray(stores);
135
+ const storeArr = single ? [stores] : stores;
136
+ const isAsync = fn.length >= 2;
137
+ return { subscribe: writable(initialValue, (set, update) => {
138
+ let inited = false;
139
+ const values = [];
140
+ let cleanup;
141
+ const sync = () => {
142
+ if (cleanup) {
143
+ cleanup();
144
+ cleanup = void 0;
145
+ }
146
+ const input = single ? values[0] : values;
147
+ if (isAsync) cleanup = fn(input, set, update);
148
+ else set(fn(input));
149
+ };
150
+ const unsubs = plainSubscribe(() => storeArr.map((s, i) => s.subscribe((v) => {
151
+ values[i] = v;
152
+ if (inited) sync();
153
+ })));
154
+ inited = true;
155
+ sync();
156
+ return () => {
157
+ for (const u of unsubs) u();
158
+ if (cleanup) cleanup();
159
+ };
160
+ }).subscribe };
161
+ }
162
+ /** Svelte-compatible `onMount` — runs after the component's first render. */
163
+ function onMount(fn) {
164
+ const ctx = getCurrentCtx();
165
+ if (ctx) {
166
+ const idx = getHookIndex();
167
+ if (idx >= ctx.hooks.length) {
168
+ let cleanup;
169
+ const unmountCb = () => {
170
+ if (cleanup) cleanup();
171
+ };
172
+ ctx.hooks[idx] = unmountCb;
173
+ ctx.pendingEffects.push({
174
+ fn: () => {
175
+ const c = fn();
176
+ cleanup = typeof c === "function" ? c : void 0;
177
+ return cleanup;
178
+ },
179
+ deps: void 0,
180
+ cleanup: void 0
181
+ });
182
+ ctx.unmountCallbacks.push(unmountCb);
183
+ } else {
184
+ const stored = ctx.hooks[idx];
185
+ if (typeof stored === "function") {
186
+ const cb = stored;
187
+ if (!ctx.unmountCallbacks.includes(cb)) ctx.unmountCallbacks.push(cb);
188
+ }
189
+ }
190
+ return;
191
+ }
192
+ onMount$1(fn);
193
+ }
194
+ /** Svelte-compatible `onDestroy` — runs when the component unmounts. */
195
+ function onDestroy(fn) {
196
+ const ctx = getCurrentCtx();
197
+ if (ctx) {
198
+ const idx = getHookIndex();
199
+ if (idx >= ctx.hooks.length) {
200
+ ctx.hooks[idx] = fn;
201
+ ctx.unmountCallbacks.push(fn);
202
+ } else {
203
+ const stored = ctx.hooks[idx];
204
+ if (typeof stored === "function") {
205
+ const cb = stored;
206
+ if (!ctx.unmountCallbacks.includes(cb)) ctx.unmountCallbacks.push(cb);
207
+ }
208
+ }
209
+ return;
210
+ }
211
+ onUnmount(fn);
212
+ }
213
+ /**
214
+ * Svelte-compatible `beforeUpdate` / `afterUpdate`. The compat wrapper
215
+ * re-renders by tearing down + rebuilding (no per-update diff), so these
216
+ * map to a post-first-render hook rather than Svelte's per-tick timing —
217
+ * the documented boundary (most Svelte interop uses onMount/onDestroy).
218
+ */
219
+ function beforeUpdate(fn) {
220
+ const ctx = getCurrentCtx();
221
+ if (ctx) {
222
+ const idx = getHookIndex();
223
+ if (idx >= ctx.hooks.length) {
224
+ ctx.hooks[idx] = true;
225
+ fn();
226
+ }
227
+ return;
228
+ }
229
+ fn();
230
+ }
231
+ function afterUpdate(fn) {
232
+ onMount(() => {
233
+ fn();
234
+ });
235
+ }
236
+ /** Svelte-compatible `tick` — resolves after the current microtask. */
237
+ function tick() {
238
+ return new Promise((resolve) => queueMicrotask(resolve));
239
+ }
240
+ const CTX_REGISTRY = Symbol.for("pyreon:svelte-ctx-registry");
241
+ function ctxFor(key) {
242
+ const g = globalThis;
243
+ let reg = g[CTX_REGISTRY];
244
+ if (!reg) {
245
+ reg = /* @__PURE__ */ new Map();
246
+ g[CTX_REGISTRY] = reg;
247
+ }
248
+ let c = reg.get(key);
249
+ if (!c) {
250
+ c = createContext(void 0);
251
+ reg.set(key, c);
252
+ }
253
+ return c;
254
+ }
255
+ /** Svelte-compatible `setContext` — provides a value for descendants. */
256
+ function setContext(key, context) {
257
+ provide(ctxFor(key), context);
258
+ return context;
259
+ }
260
+ /** Svelte-compatible `getContext` — reads the nearest provided value. */
261
+ function getContext(key) {
262
+ return useContext(ctxFor(key));
263
+ }
264
+ /** Svelte-compatible `hasContext` — whether a value was provided up-tree. */
265
+ function hasContext(key) {
266
+ return useContext(ctxFor(key)) !== void 0;
267
+ }
268
+ /** Svelte-compatible `getAllContexts` — best-effort (not tracked per-key). */
269
+ function getAllContexts() {
270
+ return /* @__PURE__ */ new Map();
271
+ }
272
+ /**
273
+ * Svelte-compatible `createEventDispatcher`. Svelte's compiler turns
274
+ * `<Child on:foo>` into a prop; here events are forwarded to the
275
+ * current component's `on<Type>` / `on:<type>` prop with a CustomEvent
276
+ * (mirrors how the sibling compat layers map child events to props).
277
+ */
278
+ function createEventDispatcher() {
279
+ const props = getCurrentCtx()?.props ?? {};
280
+ return (type, detail) => {
281
+ const evt = typeof CustomEvent === "function" ? new CustomEvent(type, { detail }) : {
282
+ type,
283
+ detail
284
+ };
285
+ const handler = props[`on${type.charAt(0).toUpperCase()}${type.slice(1)}`] ?? props[`on:${type}`] ?? props[`on${type}`];
286
+ if (typeof handler === "function") handler(evt);
287
+ return !evt.defaultPrevented;
288
+ };
289
+ }
290
+ /**
291
+ * Svelte-5-compatible `mount` — mounts a compat component into a target.
292
+ * Thin wrapper over Pyreon's runtime mount. Returns the props object
293
+ * (Svelte 5 returns the component exports; here props are the surface).
294
+ */
295
+ function mount(Component, options) {
296
+ const props = options.props ?? {};
297
+ props[UNMOUNT] = mount$1(jsx(Component, props), options.target);
298
+ return props;
299
+ }
300
+ const UNMOUNT = Symbol.for("pyreon:svelte-unmount");
301
+ /** Svelte-5-compatible `unmount` — disposes a component mounted via `mount`. */
302
+ function unmount(mounted) {
303
+ const d = mounted?.[UNMOUNT];
304
+ if (typeof d === "function") d();
305
+ }
306
+ /**
307
+ * Svelte-5-compatible `flushSync` — runs `fn` then flushes. Pyreon
308
+ * batches synchronously, so this just invokes `fn`.
309
+ */
310
+ function flushSync(fn) {
311
+ return fn ? fn() : void 0;
312
+ }
313
+
314
+ //#endregion
315
+ export { ErrorBoundary, For, Match, Show, Suspense, Switch, afterUpdate, beforeUpdate, createEventDispatcher, derived, flushSync, get, getAllContexts, getContext, hasContext, mount, onDestroy, onMount, readable, readonly, setContext, tick, unmount, writable };
316
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["pyreonCreateContext","pyreonUseContext","pyreonMount"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @pyreon/svelte-compat\n *\n * Svelte-compatible **importable runtime API** powered by Pyreon's\n * reactive engine. Mirrors the scope boundary of the sibling compat\n * layers (react/preact/vue/solid-compat): it shims the APIs Svelte code\n * actually `import`s —\n *\n * - `svelte/store` → `writable` `readable` `derived` `get` `readonly`\n * - `svelte` → `onMount` `onDestroy` `beforeUpdate` `afterUpdate`\n * `tick` `setContext` `getContext` `hasContext`\n * `getAllContexts` `createEventDispatcher`\n * `mount` `unmount` `flushSync`\n *\n * It does NOT implement the `.svelte` single-file-component compiler or\n * the non-importable Svelte 5 rune *syntax* (`$state`/`$derived`/\n * `$effect`) — those are compiler constructs, not runtime imports, the\n * same boundary solid-compat draws around Solid's compiler. Components\n * here are plain functions returning JSX that run on Pyreon via the\n * shared compat JSX runtime (re-render on store change).\n *\n * Store model: a faithful Svelte store — a plain Set of subscribers\n * notified synchronously on `set`/`update` (signal-free, exactly like\n * Svelte's own `writable`; `derived` subscribes to its inputs\n * explicitly). The store contract (`subscribe(run, invalidate?) →\n * unsubscribe`, lazy `start(set, update?) → stop` notifier) matches\n * Svelte exactly. Subscribing inside a compat component body re-renders\n * it on store change (the faithful `$store` auto-subscription\n * equivalent) without a disposable tracking effect.\n */\n\nimport type { ComponentFn, Props, VNodeChild } from '@pyreon/core'\nimport {\n ErrorBoundary,\n For,\n Match,\n nativeCompat,\n createContext as pyreonCreateContext,\n onMount as pyreonOnMount,\n onUnmount as pyreonOnUnmount,\n provide as pyreonProvide,\n useContext as pyreonUseContext,\n Show,\n Suspense,\n Switch,\n} from '@pyreon/core'\nimport { mount as pyreonMount } from '@pyreon/runtime-dom'\nimport { getCurrentCtx, getHookIndex, jsx } from './jsx-runtime'\n\n// Dev-mode counter sink — see packages/internals/perf-harness for contract.\nconst _countSink = globalThis as { __pyreon_count__?: (name: string, n?: number) => void }\n\n// ─── Store types (Svelte API surface) ───────────────────────────────────────\n\nexport type Subscriber<T> = (value: T) => void\nexport type Invalidator<T> = (value?: T) => void\nexport type Unsubscriber = () => void\nexport type Updater<T> = (value: T) => T\n/** `(set, update?) => stop?` — lazy notifier, runs on first subscriber. */\nexport type StartStopNotifier<T> = (\n set: (value: T) => void,\n update: (fn: Updater<T>) => void,\n) => Unsubscriber | void\n\nexport interface Readable<T> {\n subscribe(run: Subscriber<T>, invalidate?: Invalidator<T>): Unsubscriber\n}\nexport interface Writable<T> extends Readable<T> {\n set(value: T): void\n update(updater: Updater<T>): void\n}\n\nconst noop = () => {\n /* noop */\n}\n\n// Internal subscriptions (`get()`, `derived`'s input wiring) must NOT take\n// the render-aware hook-indexed path even when they happen to run during a\n// component render — doing so would consume the component's hook indices and\n// desync onMount/onDestroy. This depth counter suppresses the render-aware\n// branch for the duration of an internal subscribe.\nlet _plainDepth = 0\nfunction plainSubscribe<R>(fn: () => R): R {\n _plainDepth++\n try {\n return fn()\n } finally {\n _plainDepth--\n }\n}\n\n// ─── writable ────────────────────────────────────────────────────────────────\n\n/**\n * Svelte's `safe_not_equal`: primitives dedup, objects/functions always\n * notify (so in-place-mutated store objects still propagate).\n */\nfunction safeNotEqual(a: unknown, b: unknown): boolean {\n // eslint-disable-next-line no-self-compare\n return a != a\n ? // eslint-disable-next-line no-self-compare\n b == b\n : a !== b || (a !== null && typeof a === 'object') || typeof a === 'function'\n}\n\ninterface SubEntry<T> {\n run: Subscriber<T>\n invalidate: Invalidator<T>\n /** Component re-render trigger — set only for render-aware subscriptions. */\n rerender?: () => void\n}\n\n/**\n * Svelte-compatible `writable`. A faithful Svelte store: a Set of\n * subscribers notified synchronously on `set`/`update`. NOT signal-\n * backed — Svelte's own `writable` is signal-free, and `derived` here\n * subscribes to its inputs explicitly, so no signal auto-tracking is\n * needed.\n *\n * Subscribing inside a compat component body is the faithful equivalent\n * of Svelte's `$store` auto-subscription: the subscriber carries the\n * component's `scheduleRerender`, so a store write re-renders the\n * component — the same \"write drives re-render\" model the sibling\n * solid-compat layer uses. Crucially this is NOT a persistent tracking\n * effect: such an effect, created inside the wrapper accessor's run, is\n * collected as an inner effect and disposed on the NEXT re-render (the\n * cached path never recreates it), so store changes stopped propagating\n * after the first one. A plain subscriber Set has no such hazard — it\n * lives until `unsub` (registered in `ctx.unmountCallbacks`).\n *\n * `start` runs when the subscriber count goes 0→1 and its returned\n * `stop` runs at 1→0. Synchronous `set` calls inside `start` mutate the\n * value but do NOT notify (the store isn't \"ready\" until `start`\n * returns), so the first subscriber sees exactly the post-start value —\n * matching Svelte's `derived` (one emission, no spurious initial).\n */\nexport function writable<T>(value?: T, start: StartStopNotifier<T> = noop): Writable<T> {\n let v = value as T\n let stop: Unsubscriber | void\n const subs = new Set<SubEntry<T>>()\n\n const setVal = (next: T): void => {\n if (!safeNotEqual(v, next)) return\n v = next\n if (!stop) return // not \"ready\" — Svelte's gate (start hasn't returned)\n for (const s of subs) s.invalidate(v)\n for (const s of subs) {\n s.run(v)\n s.rerender?.()\n }\n }\n const set = (next: T): void => setVal(next)\n const update = (fn: Updater<T>): void => setVal(fn(v))\n\n const addSub = (entry: SubEntry<T>): Unsubscriber => {\n subs.add(entry)\n if (subs.size === 1) stop = start(set, update) || noop\n entry.run(v) // Svelte: subscriber invoked immediately with current value\n return () => {\n subs.delete(entry)\n if (subs.size === 0 && stop) {\n stop()\n stop = undefined\n }\n }\n }\n\n return {\n set,\n update,\n subscribe(run: Subscriber<T>, invalidate: Invalidator<T> = noop): Unsubscriber {\n // Render-aware path: inside a compat component body (and not an\n // internal `_plainDepth` subscription), carry the component's\n // scheduleRerender so a store write re-renders it. Hook-indexed so\n // the one live subscription is created exactly once across\n // re-renders; the cached pass just refreshes the component-local.\n const ctx = _plainDepth === 0 ? getCurrentCtx() : null\n if (ctx) {\n const idx = getHookIndex()\n const cached = ctx.hooks[idx] as { unsub: Unsubscriber } | undefined\n if (cached) {\n run(v)\n // Re-push the cached unsub into the (possibly-reset)\n // unmountCallbacks array. When a parent re-renders and\n // preserves the ChildInstance, the wrapper resets\n // `ctx.unmountCallbacks = []` to drop stale cycle-N\n // callbacks before cycle-N+1 begins (`jsx-runtime.ts:172`).\n // Without this re-push the cached subscription's unsub is\n // lost from the array and the subscription stays active on\n // the store forever — one leaked subscriber per\n // `writable.subscribe()` call per parent re-render cycle.\n if (!ctx.unmountCallbacks.includes(cached.unsub)) {\n ctx.unmountCallbacks.push(cached.unsub)\n // Leak-class D diagnostic — emit per re-push that fires\n // during the cached fast-path. Non-zero confirms parent\n // re-renders are actually exercising the cached subscribe\n // path (and the unsub stays bound to the live cleanup\n // array). Zero on a render-heavy workload = either no\n // cached subscriptions OR — bug — the includes() guard\n // suppressed a valid re-push.\n if (process.env.NODE_ENV !== 'production')\n _countSink.__pyreon_count__?.('svelte-compat.subscribe.cachedRePush')\n }\n return cached.unsub\n }\n const entry: SubEntry<T> = {\n run,\n invalidate,\n rerender: () => {\n if (!ctx.unmounted) ctx.scheduleRerender()\n },\n }\n const unsub = addSub(entry)\n ctx.hooks[idx] = { unsub }\n ctx.unmountCallbacks.push(unsub)\n return unsub\n }\n return addSub({ run, invalidate })\n },\n }\n}\n\n// ─── readable ────────────────────────────────────────────────────────────────\n\n/** Svelte-compatible `readable` — a `writable` with `set`/`update` hidden. */\nexport function readable<T>(value?: T, start?: StartStopNotifier<T>): Readable<T> {\n const w = writable<T>(value, start)\n return { subscribe: w.subscribe }\n}\n\n// ─── readonly ────────────────────────────────────────────────────────────────\n\n/** Svelte-compatible `readonly` — view of a store exposing only `subscribe`. */\nexport function readonly<T>(store: Readable<T>): Readable<T> {\n return { subscribe: store.subscribe }\n}\n\n// ─── get ─────────────────────────────────────────────────────────────────────\n\n/** Svelte-compatible `get` — read a store's value synchronously. */\nexport function get<T>(store: Readable<T>): T {\n let value!: T\n plainSubscribe(() => {\n const unsub = store.subscribe((v) => {\n value = v\n })\n unsub()\n })\n return value\n}\n\n// ─── derived ─────────────────────────────────────────────────────────────────\n\ntype Stores =\n | Readable<unknown>\n | [Readable<unknown>, ...Array<Readable<unknown>>]\n | Array<Readable<unknown>>\ntype StoresValues<T> = T extends Readable<infer U>\n ? U\n : { [K in keyof T]: T[K] extends Readable<infer U> ? U : never }\n\n/**\n * Svelte-compatible `derived`. Supports both the sync form\n * `(values) => result` and the async/cleanup form\n * `(values, set, update?) => stop`.\n */\nexport function derived<S extends Stores, T>(\n stores: S,\n fn:\n | ((values: StoresValues<S>) => T)\n | ((\n values: StoresValues<S>,\n set: (value: T) => void,\n update: (fn: Updater<T>) => void,\n ) => Unsubscriber | void),\n initialValue?: T,\n): Readable<T> {\n const single = !Array.isArray(stores)\n const storeArr = (single ? [stores] : stores) as Array<Readable<unknown>>\n const isAsync = fn.length >= 2\n\n const out = writable<T>(initialValue as T, (set, update) => {\n let inited = false\n const values: unknown[] = []\n let cleanup: Unsubscriber | void\n\n const sync = () => {\n if (cleanup) {\n cleanup()\n cleanup = undefined\n }\n const input = (single ? values[0] : values) as StoresValues<S>\n if (isAsync) {\n cleanup = (\n fn as (v: StoresValues<S>, s: (x: T) => void, u: (f: Updater<T>) => void) => Unsubscriber | void\n )(input, set, update)\n } else {\n set((fn as (v: StoresValues<S>) => T)(input))\n }\n }\n\n const unsubs = plainSubscribe(() =>\n storeArr.map((s, i) =>\n s.subscribe((v) => {\n values[i] = v\n if (inited) sync()\n }),\n ),\n )\n inited = true\n sync()\n\n return () => {\n for (const u of unsubs) u()\n if (cleanup) cleanup()\n }\n })\n\n return { subscribe: out.subscribe }\n}\n\n// ─── lifecycle (svelte) ──────────────────────────────────────────────────────\n\ntype CleanupFn = () => void\n\n/** Svelte-compatible `onMount` — runs after the component's first render. */\nexport function onMount(fn: () => CleanupFn | void): void {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx >= ctx.hooks.length) {\n // Svelte's onMount may return a cleanup that runs on destroy. The\n // shared jsx-runtime schedules pendingEffects post-render but never\n // invokes their stored cleanup on unmount, so wire it explicitly\n // into unmountCallbacks (runs in the wrapper's onUnmount).\n let cleanup: CleanupFn | undefined\n const unmountCb = () => {\n if (cleanup) cleanup()\n }\n // Store the cleanup callback in the hook slot (was `true`) so it can be\n // re-pushed after a parent re-render resets `ctx.unmountCallbacks`.\n ctx.hooks[idx] = unmountCb\n ctx.pendingEffects.push({\n fn: () => {\n const c = fn()\n cleanup = typeof c === 'function' ? c : undefined\n return cleanup\n },\n deps: undefined,\n cleanup: undefined,\n })\n ctx.unmountCallbacks.push(unmountCb)\n } else {\n // Re-render of a preserved child: the wrapper reset\n // `ctx.unmountCallbacks = []` (jsx-runtime.ts:172), dropping this hook's\n // cleanup. Re-push it so it still runs on final unmount — the lifecycle\n // sibling of the #739 `writable.subscribe` re-push. `includes()` guards\n // against a double-push within the same render.\n const stored = ctx.hooks[idx]\n if (typeof stored === 'function') {\n const cb = stored as () => void\n if (!ctx.unmountCallbacks.includes(cb)) ctx.unmountCallbacks.push(cb)\n }\n }\n return\n }\n pyreonOnMount(fn)\n}\n\n/** Svelte-compatible `onDestroy` — runs when the component unmounts. */\nexport function onDestroy(fn: () => void): void {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx >= ctx.hooks.length) {\n // Store the callback in the hook slot (was `true`) so it survives a\n // parent re-render that resets `ctx.unmountCallbacks` (see onMount).\n ctx.hooks[idx] = fn\n ctx.unmountCallbacks.push(fn)\n } else {\n // Re-render: re-push the dropped destroy callback (the #739 lifecycle\n // sibling) so it still fires on final unmount.\n const stored = ctx.hooks[idx]\n if (typeof stored === 'function') {\n const cb = stored as () => void\n if (!ctx.unmountCallbacks.includes(cb)) ctx.unmountCallbacks.push(cb)\n }\n }\n return\n }\n pyreonOnUnmount(fn)\n}\n\n/**\n * Svelte-compatible `beforeUpdate` / `afterUpdate`. The compat wrapper\n * re-renders by tearing down + rebuilding (no per-update diff), so these\n * map to a post-first-render hook rather than Svelte's per-tick timing —\n * the documented boundary (most Svelte interop uses onMount/onDestroy).\n */\nexport function beforeUpdate(fn: () => void): void {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx >= ctx.hooks.length) {\n ctx.hooks[idx] = true\n fn() // before the first render commits\n }\n return\n }\n fn()\n}\nexport function afterUpdate(fn: () => void): void {\n onMount(() => {\n fn()\n })\n}\n\n/** Svelte-compatible `tick` — resolves after the current microtask. */\nexport function tick(): Promise<void> {\n return new Promise<void>((resolve) => queueMicrotask(resolve))\n}\n\n// ─── context (svelte) ────────────────────────────────────────────────────────\n\nconst CTX_REGISTRY = Symbol.for('pyreon:svelte-ctx-registry')\ntype CtxMap = Map<unknown, ReturnType<typeof pyreonCreateContext<unknown>>>\n\nfunction ctxFor(key: unknown): ReturnType<typeof pyreonCreateContext<unknown>> {\n const g = globalThis as Record<symbol, unknown>\n let reg = g[CTX_REGISTRY] as CtxMap | undefined\n if (!reg) {\n reg = new Map()\n g[CTX_REGISTRY] = reg\n }\n let c = reg.get(key)\n if (!c) {\n c = pyreonCreateContext<unknown>(undefined)\n reg.set(key, c)\n }\n return c\n}\n\n/** Svelte-compatible `setContext` — provides a value for descendants. */\nexport function setContext<T>(key: unknown, context: T): T {\n pyreonProvide(ctxFor(key), context)\n return context\n}\n/** Svelte-compatible `getContext` — reads the nearest provided value. */\nexport function getContext<T>(key: unknown): T {\n return pyreonUseContext(ctxFor(key)) as T\n}\n/** Svelte-compatible `hasContext` — whether a value was provided up-tree. */\nexport function hasContext(key: unknown): boolean {\n return pyreonUseContext(ctxFor(key)) !== undefined\n}\n/** Svelte-compatible `getAllContexts` — best-effort (not tracked per-key). */\nexport function getAllContexts<T extends Map<unknown, unknown> = Map<unknown, unknown>>(): T {\n return new Map() as T\n}\n\n// ─── createEventDispatcher (svelte) ──────────────────────────────────────────\n\n/**\n * Svelte-compatible `createEventDispatcher`. Svelte's compiler turns\n * `<Child on:foo>` into a prop; here events are forwarded to the\n * current component's `on<Type>` / `on:<type>` prop with a CustomEvent\n * (mirrors how the sibling compat layers map child events to props).\n */\nexport function createEventDispatcher<EventMap extends Record<string, unknown> = Record<string, unknown>>(): <\n Type extends keyof EventMap & string,\n>(\n type: Type,\n detail?: EventMap[Type],\n) => boolean {\n const ctx = getCurrentCtx()\n const props = (ctx?.props ?? {}) as Record<string, unknown>\n return (type, detail) => {\n const evt =\n typeof CustomEvent === 'function'\n ? new CustomEvent(type, { detail })\n : ({ type, detail } as unknown as CustomEvent)\n const cap = `on${type.charAt(0).toUpperCase()}${type.slice(1)}`\n const handler = (props[cap] ?? props[`on:${type}`] ?? props[`on${type}`]) as\n | ((e: unknown) => void)\n | undefined\n if (typeof handler === 'function') handler(evt)\n return !(evt as CustomEvent).defaultPrevented\n }\n}\n\n// ─── mount / unmount / flushSync (Svelte 5 client API) ───────────────────────\n\ntype MountTargetOptions<P> = { target: Element; props?: P; context?: Map<unknown, unknown> }\n\n/**\n * Svelte-5-compatible `mount` — mounts a compat component into a target.\n * Thin wrapper over Pyreon's runtime mount. Returns the props object\n * (Svelte 5 returns the component exports; here props are the surface).\n */\nexport function mount<P extends Record<string, unknown>>(\n Component: (props: P) => VNodeChild,\n options: MountTargetOptions<P>,\n): P {\n const props = (options.props ?? ({} as P)) as P\n // Route through the compat JSX runtime so the component runs inside the\n // shared wrapper (lifecycle + store-driven re-render), exactly as a\n // JSX-rendered child would.\n const vnode = jsx(Component as unknown as ComponentFn, props as unknown as Props)\n const dispose = pyreonMount(vnode, options.target as HTMLElement)\n ;(props as Record<symbol, unknown>)[UNMOUNT] = dispose\n return props\n}\n\nconst UNMOUNT = Symbol.for('pyreon:svelte-unmount')\n\n/** Svelte-5-compatible `unmount` — disposes a component mounted via `mount`. */\nexport function unmount(mounted: Record<symbol, unknown>): void {\n const d = mounted?.[UNMOUNT] as (() => void) | undefined\n if (typeof d === 'function') d()\n}\n\n/**\n * Svelte-5-compatible `flushSync` — runs `fn` then flushes. Pyreon\n * batches synchronously, so this just invokes `fn`.\n */\nexport function flushSync<T>(fn?: () => T): T | undefined {\n return fn ? fn() : undefined\n}\n\n// ─── createEventDispatcher needs props on ctx ────────────────────────────────\n// (jsx-runtime stores `props` on the RenderContext — see jsx-runtime.ts)\n\n// ─── Re-exports from @pyreon/core (control-flow parity) ──────────────────────\n\nexport { ErrorBoundary, For, Match, Show, Suspense, Switch }\n\n// Mark the compat surface so framework Providers route natively.\nvoid nativeCompat\n"],"mappings":";;;;;AAkDA,MAAM,aAAa;AAsBnB,MAAM,aAAa,CAEnB;AAOA,IAAI,cAAc;AAClB,SAAS,eAAkB,IAAgB;CACzC;CACA,IAAI;EACF,OAAO,GAAG;CACZ,UAAU;EACR;CACF;AACF;;;;;AAQA,SAAS,aAAa,GAAY,GAAqB;CAErD,OAAO,KAAK,IAER,KAAK,IACL,MAAM,KAAM,MAAM,QAAQ,OAAO,MAAM,YAAa,OAAO,MAAM;AACvE;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,SAAgB,SAAY,OAAW,QAA8B,MAAmB;CACtF,IAAI,IAAI;CACR,IAAI;CACJ,MAAM,uBAAO,IAAI,IAAiB;CAElC,MAAM,UAAU,SAAkB;EAChC,IAAI,CAAC,aAAa,GAAG,IAAI,GAAG;EAC5B,IAAI;EACJ,IAAI,CAAC,MAAM;EACX,KAAK,MAAM,KAAK,MAAM,EAAE,WAAW,CAAC;EACpC,KAAK,MAAM,KAAK,MAAM;GACpB,EAAE,IAAI,CAAC;GACP,EAAE,WAAW;EACf;CACF;CACA,MAAM,OAAO,SAAkB,OAAO,IAAI;CAC1C,MAAM,UAAU,OAAyB,OAAO,GAAG,CAAC,CAAC;CAErD,MAAM,UAAU,UAAqC;EACnD,KAAK,IAAI,KAAK;EACd,IAAI,KAAK,SAAS,GAAG,OAAO,MAAM,KAAK,MAAM,KAAK;EAClD,MAAM,IAAI,CAAC;EACX,aAAa;GACX,KAAK,OAAO,KAAK;GACjB,IAAI,KAAK,SAAS,KAAK,MAAM;IAC3B,KAAK;IACL,OAAO;GACT;EACF;CACF;CAEA,OAAO;EACL;EACA;EACA,UAAU,KAAoB,aAA6B,MAAoB;GAM7E,MAAM,MAAM,gBAAgB,IAAI,cAAc,IAAI;GAClD,IAAI,KAAK;IACP,MAAM,MAAM,aAAa;IACzB,MAAM,SAAS,IAAI,MAAM;IACzB,IAAI,QAAQ;KACV,IAAI,CAAC;KAUL,IAAI,CAAC,IAAI,iBAAiB,SAAS,OAAO,KAAK,GAAG;MAChD,IAAI,iBAAiB,KAAK,OAAO,KAAK;MAQtC,IAAI,QAAQ,IAAI,aAAa,cAC3B,WAAW,mBAAmB,sCAAsC;KACxE;KACA,OAAO,OAAO;IAChB;IAQA,MAAM,QAAQ,OAAO;KANnB;KACA;KACA,gBAAgB;MACd,IAAI,CAAC,IAAI,WAAW,IAAI,iBAAiB;KAC3C;IAEuB,CAAC;IAC1B,IAAI,MAAM,OAAO,EAAE,MAAM;IACzB,IAAI,iBAAiB,KAAK,KAAK;IAC/B,OAAO;GACT;GACA,OAAO,OAAO;IAAE;IAAK;GAAW,CAAC;EACnC;CACF;AACF;;AAKA,SAAgB,SAAY,OAAW,OAA2C;CAEhF,OAAO,EAAE,WADC,SAAY,OAAO,KACT,EAAE,UAAU;AAClC;;AAKA,SAAgB,SAAY,OAAiC;CAC3D,OAAO,EAAE,WAAW,MAAM,UAAU;AACtC;;AAKA,SAAgB,IAAO,OAAuB;CAC5C,IAAI;CACJ,qBAAqB;EAInB,AAHc,MAAM,WAAW,MAAM;GACnC,QAAQ;EACV,CACI,EAAE;CACR,CAAC;CACD,OAAO;AACT;;;;;;AAiBA,SAAgB,QACd,QACA,IAOA,cACa;CACb,MAAM,SAAS,CAAC,MAAM,QAAQ,MAAM;CACpC,MAAM,WAAY,SAAS,CAAC,MAAM,IAAI;CACtC,MAAM,UAAU,GAAG,UAAU;CAuC7B,OAAO,EAAE,WArCG,SAAY,eAAoB,KAAK,WAAW;EAC1D,IAAI,SAAS;EACb,MAAM,SAAoB,CAAC;EAC3B,IAAI;EAEJ,MAAM,aAAa;GACjB,IAAI,SAAS;IACX,QAAQ;IACR,UAAU;GACZ;GACA,MAAM,QAAS,SAAS,OAAO,KAAK;GACpC,IAAI,SACF,UACE,GACA,OAAO,KAAK,MAAM;QAEpB,IAAK,GAAiC,KAAK,CAAC;EAEhD;EAEA,MAAM,SAAS,qBACb,SAAS,KAAK,GAAG,MACf,EAAE,WAAW,MAAM;GACjB,OAAO,KAAK;GACZ,IAAI,QAAQ,KAAK;EACnB,CAAC,CACH,CACF;EACA,SAAS;EACT,KAAK;EAEL,aAAa;GACX,KAAK,MAAM,KAAK,QAAQ,EAAE;GAC1B,IAAI,SAAS,QAAQ;EACvB;CACF,CAEsB,EAAE,UAAU;AACpC;;AAOA,SAAgB,QAAQ,IAAkC;CACxD,MAAM,MAAM,cAAc;CAC1B,IAAI,KAAK;EACP,MAAM,MAAM,aAAa;EACzB,IAAI,OAAO,IAAI,MAAM,QAAQ;GAK3B,IAAI;GACJ,MAAM,kBAAkB;IACtB,IAAI,SAAS,QAAQ;GACvB;GAGA,IAAI,MAAM,OAAO;GACjB,IAAI,eAAe,KAAK;IACtB,UAAU;KACR,MAAM,IAAI,GAAG;KACb,UAAU,OAAO,MAAM,aAAa,IAAI;KACxC,OAAO;IACT;IACA,MAAM;IACN,SAAS;GACX,CAAC;GACD,IAAI,iBAAiB,KAAK,SAAS;EACrC,OAAO;GAML,MAAM,SAAS,IAAI,MAAM;GACzB,IAAI,OAAO,WAAW,YAAY;IAChC,MAAM,KAAK;IACX,IAAI,CAAC,IAAI,iBAAiB,SAAS,EAAE,GAAG,IAAI,iBAAiB,KAAK,EAAE;GACtE;EACF;EACA;CACF;CACA,UAAc,EAAE;AAClB;;AAGA,SAAgB,UAAU,IAAsB;CAC9C,MAAM,MAAM,cAAc;CAC1B,IAAI,KAAK;EACP,MAAM,MAAM,aAAa;EACzB,IAAI,OAAO,IAAI,MAAM,QAAQ;GAG3B,IAAI,MAAM,OAAO;GACjB,IAAI,iBAAiB,KAAK,EAAE;EAC9B,OAAO;GAGL,MAAM,SAAS,IAAI,MAAM;GACzB,IAAI,OAAO,WAAW,YAAY;IAChC,MAAM,KAAK;IACX,IAAI,CAAC,IAAI,iBAAiB,SAAS,EAAE,GAAG,IAAI,iBAAiB,KAAK,EAAE;GACtE;EACF;EACA;CACF;CACA,UAAgB,EAAE;AACpB;;;;;;;AAQA,SAAgB,aAAa,IAAsB;CACjD,MAAM,MAAM,cAAc;CAC1B,IAAI,KAAK;EACP,MAAM,MAAM,aAAa;EACzB,IAAI,OAAO,IAAI,MAAM,QAAQ;GAC3B,IAAI,MAAM,OAAO;GACjB,GAAG;EACL;EACA;CACF;CACA,GAAG;AACL;AACA,SAAgB,YAAY,IAAsB;CAChD,cAAc;EACZ,GAAG;CACL,CAAC;AACH;;AAGA,SAAgB,OAAsB;CACpC,OAAO,IAAI,SAAe,YAAY,eAAe,OAAO,CAAC;AAC/D;AAIA,MAAM,eAAe,OAAO,IAAI,4BAA4B;AAG5D,SAAS,OAAO,KAA+D;CAC7E,MAAM,IAAI;CACV,IAAI,MAAM,EAAE;CACZ,IAAI,CAAC,KAAK;EACR,sBAAM,IAAI,IAAI;EACd,EAAE,gBAAgB;CACpB;CACA,IAAI,IAAI,IAAI,IAAI,GAAG;CACnB,IAAI,CAAC,GAAG;EACN,IAAIA,cAA6B,MAAS;EAC1C,IAAI,IAAI,KAAK,CAAC;CAChB;CACA,OAAO;AACT;;AAGA,SAAgB,WAAc,KAAc,SAAe;CACzD,QAAc,OAAO,GAAG,GAAG,OAAO;CAClC,OAAO;AACT;;AAEA,SAAgB,WAAc,KAAiB;CAC7C,OAAOC,WAAiB,OAAO,GAAG,CAAC;AACrC;;AAEA,SAAgB,WAAW,KAAuB;CAChD,OAAOA,WAAiB,OAAO,GAAG,CAAC,MAAM;AAC3C;;AAEA,SAAgB,iBAA6E;CAC3F,uBAAO,IAAI,IAAI;AACjB;;;;;;;AAUA,SAAgB,wBAKH;CAEX,MAAM,QADM,cACK,GAAG,SAAS,CAAC;CAC9B,QAAQ,MAAM,WAAW;EACvB,MAAM,MACJ,OAAO,gBAAgB,aACnB,IAAI,YAAY,MAAM,EAAE,OAAO,CAAC,IAC/B;GAAE;GAAM;EAAO;EAEtB,MAAM,UAAW,MAAM,KADN,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,QAC7B,MAAM,MAAM,WAAW,MAAM,KAAK;EAGjE,IAAI,OAAO,YAAY,YAAY,QAAQ,GAAG;EAC9C,OAAO,CAAE,IAAoB;CAC/B;AACF;;;;;;AAWA,SAAgB,MACd,WACA,SACG;CACH,MAAM,QAAS,QAAQ,SAAU,CAAC;CAMjC,AAAC,MAAkC,WADpBC,QADF,IAAI,WAAqC,KACvB,GAAG,QAAQ,MACU;CACrD,OAAO;AACT;AAEA,MAAM,UAAU,OAAO,IAAI,uBAAuB;;AAGlD,SAAgB,QAAQ,SAAwC;CAC9D,MAAM,IAAI,UAAU;CACpB,IAAI,OAAO,MAAM,YAAY,EAAE;AACjC;;;;;AAMA,SAAgB,UAAa,IAA6B;CACxD,OAAO,KAAK,GAAG,IAAI;AACrB"}
@@ -0,0 +1,3 @@
1
+ import { a as jsxs, i as jsx, t as Fragment } from "./_chunks/jsx-runtime-Cbf3f1f5.js";
2
+
3
+ export { Fragment, jsx, jsxs };
package/lib/store.js ADDED
@@ -0,0 +1,3 @@
1
+ import { derived, get, readable, readonly, writable } from "./index.js";
2
+
3
+ export { derived, get, readable, readonly, writable };
@@ -0,0 +1,106 @@
1
+ import { ErrorBoundary, For, Match, Show, Suspense, Switch, VNodeChild } from "@pyreon/core";
2
+
3
+ //#region src/index.d.ts
4
+ type Subscriber<T> = (value: T) => void;
5
+ type Invalidator<T> = (value?: T) => void;
6
+ type Unsubscriber = () => void;
7
+ type Updater<T> = (value: T) => T;
8
+ /** `(set, update?) => stop?` — lazy notifier, runs on first subscriber. */
9
+ type StartStopNotifier<T> = (set: (value: T) => void, update: (fn: Updater<T>) => void) => Unsubscriber | void;
10
+ interface Readable<T> {
11
+ subscribe(run: Subscriber<T>, invalidate?: Invalidator<T>): Unsubscriber;
12
+ }
13
+ interface Writable<T> extends Readable<T> {
14
+ set(value: T): void;
15
+ update(updater: Updater<T>): void;
16
+ }
17
+ /**
18
+ * Svelte-compatible `writable`. A faithful Svelte store: a Set of
19
+ * subscribers notified synchronously on `set`/`update`. NOT signal-
20
+ * backed — Svelte's own `writable` is signal-free, and `derived` here
21
+ * subscribes to its inputs explicitly, so no signal auto-tracking is
22
+ * needed.
23
+ *
24
+ * Subscribing inside a compat component body is the faithful equivalent
25
+ * of Svelte's `$store` auto-subscription: the subscriber carries the
26
+ * component's `scheduleRerender`, so a store write re-renders the
27
+ * component — the same "write drives re-render" model the sibling
28
+ * solid-compat layer uses. Crucially this is NOT a persistent tracking
29
+ * effect: such an effect, created inside the wrapper accessor's run, is
30
+ * collected as an inner effect and disposed on the NEXT re-render (the
31
+ * cached path never recreates it), so store changes stopped propagating
32
+ * after the first one. A plain subscriber Set has no such hazard — it
33
+ * lives until `unsub` (registered in `ctx.unmountCallbacks`).
34
+ *
35
+ * `start` runs when the subscriber count goes 0→1 and its returned
36
+ * `stop` runs at 1→0. Synchronous `set` calls inside `start` mutate the
37
+ * value but do NOT notify (the store isn't "ready" until `start`
38
+ * returns), so the first subscriber sees exactly the post-start value —
39
+ * matching Svelte's `derived` (one emission, no spurious initial).
40
+ */
41
+ declare function writable<T>(value?: T, start?: StartStopNotifier<T>): Writable<T>;
42
+ /** Svelte-compatible `readable` — a `writable` with `set`/`update` hidden. */
43
+ declare function readable<T>(value?: T, start?: StartStopNotifier<T>): Readable<T>;
44
+ /** Svelte-compatible `readonly` — view of a store exposing only `subscribe`. */
45
+ declare function readonly<T>(store: Readable<T>): Readable<T>;
46
+ /** Svelte-compatible `get` — read a store's value synchronously. */
47
+ declare function get<T>(store: Readable<T>): T;
48
+ type Stores = Readable<unknown> | [Readable<unknown>, ...Array<Readable<unknown>>] | Array<Readable<unknown>>;
49
+ type StoresValues<T> = T extends Readable<infer U> ? U : { [K in keyof T]: T[K] extends Readable<infer U> ? U : never };
50
+ /**
51
+ * Svelte-compatible `derived`. Supports both the sync form
52
+ * `(values) => result` and the async/cleanup form
53
+ * `(values, set, update?) => stop`.
54
+ */
55
+ declare function derived<S extends Stores, T>(stores: S, fn: ((values: StoresValues<S>) => T) | ((values: StoresValues<S>, set: (value: T) => void, update: (fn: Updater<T>) => void) => Unsubscriber | void), initialValue?: T): Readable<T>;
56
+ type CleanupFn = () => void;
57
+ /** Svelte-compatible `onMount` — runs after the component's first render. */
58
+ declare function onMount(fn: () => CleanupFn | void): void;
59
+ /** Svelte-compatible `onDestroy` — runs when the component unmounts. */
60
+ declare function onDestroy(fn: () => void): void;
61
+ /**
62
+ * Svelte-compatible `beforeUpdate` / `afterUpdate`. The compat wrapper
63
+ * re-renders by tearing down + rebuilding (no per-update diff), so these
64
+ * map to a post-first-render hook rather than Svelte's per-tick timing —
65
+ * the documented boundary (most Svelte interop uses onMount/onDestroy).
66
+ */
67
+ declare function beforeUpdate(fn: () => void): void;
68
+ declare function afterUpdate(fn: () => void): void;
69
+ /** Svelte-compatible `tick` — resolves after the current microtask. */
70
+ declare function tick(): Promise<void>;
71
+ /** Svelte-compatible `setContext` — provides a value for descendants. */
72
+ declare function setContext<T>(key: unknown, context: T): T;
73
+ /** Svelte-compatible `getContext` — reads the nearest provided value. */
74
+ declare function getContext<T>(key: unknown): T;
75
+ /** Svelte-compatible `hasContext` — whether a value was provided up-tree. */
76
+ declare function hasContext(key: unknown): boolean;
77
+ /** Svelte-compatible `getAllContexts` — best-effort (not tracked per-key). */
78
+ declare function getAllContexts<T extends Map<unknown, unknown> = Map<unknown, unknown>>(): T;
79
+ /**
80
+ * Svelte-compatible `createEventDispatcher`. Svelte's compiler turns
81
+ * `<Child on:foo>` into a prop; here events are forwarded to the
82
+ * current component's `on<Type>` / `on:<type>` prop with a CustomEvent
83
+ * (mirrors how the sibling compat layers map child events to props).
84
+ */
85
+ declare function createEventDispatcher<EventMap extends Record<string, unknown> = Record<string, unknown>>(): <Type extends keyof EventMap & string>(type: Type, detail?: EventMap[Type]) => boolean;
86
+ type MountTargetOptions<P> = {
87
+ target: Element;
88
+ props?: P;
89
+ context?: Map<unknown, unknown>;
90
+ };
91
+ /**
92
+ * Svelte-5-compatible `mount` — mounts a compat component into a target.
93
+ * Thin wrapper over Pyreon's runtime mount. Returns the props object
94
+ * (Svelte 5 returns the component exports; here props are the surface).
95
+ */
96
+ declare function mount<P extends Record<string, unknown>>(Component: (props: P) => VNodeChild, options: MountTargetOptions<P>): P;
97
+ /** Svelte-5-compatible `unmount` — disposes a component mounted via `mount`. */
98
+ declare function unmount(mounted: Record<symbol, unknown>): void;
99
+ /**
100
+ * Svelte-5-compatible `flushSync` — runs `fn` then flushes. Pyreon
101
+ * batches synchronously, so this just invokes `fn`.
102
+ */
103
+ declare function flushSync<T>(fn?: () => T): T | undefined;
104
+ //#endregion
105
+ export { ErrorBoundary, For, Invalidator, Match, Readable, Show, StartStopNotifier, Subscriber, Suspense, Switch, Unsubscriber, Updater, Writable, afterUpdate, beforeUpdate, createEventDispatcher, derived, flushSync, get, getAllContexts, getContext, hasContext, mount, onDestroy, onMount, readable, readonly, setContext, tick, unmount, writable };
106
+ //# sourceMappingURL=index2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/index.ts"],"mappings":";;;KAsDY,UAAA,OAAiB,KAAA,EAAO,CAAC;AAAA,KACzB,WAAA,OAAkB,KAAA,GAAQ,CAAC;AAAA,KAC3B,YAAA;AAAA,KACA,OAAA,OAAc,KAAA,EAAO,CAAA,KAAM,CAAC;AAExC;AAAA,KAAY,iBAAA,OACV,GAAA,GAAM,KAAA,EAAO,CAAA,WACb,MAAA,GAAS,EAAA,EAAI,OAAA,CAAQ,CAAA,eAClB,YAAA;AAAA,UAEY,QAAA;EACf,SAAA,CAAU,GAAA,EAAK,UAAA,CAAW,CAAA,GAAI,UAAA,GAAa,WAAA,CAAY,CAAA,IAAK,YAAA;AAAA;AAAA,UAE7C,QAAA,YAAoB,QAAA,CAAS,CAAA;EAC5C,GAAA,CAAI,KAAA,EAAO,CAAA;EACX,MAAA,CAAO,OAAA,EAAS,OAAA,CAAQ,CAAA;AAAA;;;;;;;;;;;AAPT;AAEjB;;;;;;;;;;;;;iBAwEgB,QAAA,GAAA,CAAY,KAAA,GAAQ,CAAA,EAAG,KAAA,GAAO,iBAAA,CAAkB,CAAA,IAAY,QAAA,CAAS,CAAA;;iBAyFrE,QAAA,GAAA,CAAY,KAAA,GAAQ,CAAA,EAAG,KAAA,GAAQ,iBAAA,CAAkB,CAAA,IAAK,QAAA,CAAS,CAAA;;iBAQ/D,QAAA,GAAA,CAAY,KAAA,EAAO,QAAA,CAAS,CAAA,IAAK,QAAA,CAAS,CAAA;;iBAO1C,GAAA,GAAA,CAAO,KAAA,EAAO,QAAA,CAAS,CAAA,IAAK,CAAA;AAAA,KAavC,MAAA,GACD,QAAA,aACC,QAAA,cAAsB,KAAA,CAAM,QAAA,cAC7B,KAAA,CAAM,QAAA;AAAA,KACL,YAAA,MAAkB,CAAA,SAAU,QAAA,YAC7B,CAAA,iBACc,CAAA,GAAI,CAAA,CAAE,CAAA,UAAW,QAAA,YAAoB,CAAA;;;;;;iBAOvC,OAAA,WAAkB,MAAA,IAAA,CAChC,MAAA,EAAQ,CAAA,EACR,EAAA,IACM,MAAA,EAAQ,YAAA,CAAa,CAAA,MAAO,CAAA,MAE5B,MAAA,EAAQ,YAAA,CAAa,CAAA,GACrB,GAAA,GAAM,KAAA,EAAO,CAAA,WACb,MAAA,GAAS,EAAA,EAAI,OAAA,CAAQ,CAAA,eAClB,YAAA,UACT,YAAA,GAAe,CAAA,GACd,QAAA,CAAS,CAAA;AAAA,KA+CP,SAAA;;iBAGW,OAAA,CAAQ,EAA0B,QAAhB,SAAS;;iBA4C3B,SAAA,CAAU,EAAc;;;;;;;iBA6BxB,YAAA,CAAa,EAAc;AAAA,iBAY3B,WAAA,CAAY,EAAc;AAnR1C;AAAA,iBA0RgB,IAAA,CAAA,GAAQ,OAAO;;iBAyBf,UAAA,GAAA,CAAc,GAAA,WAAc,OAAA,EAAS,CAAA,GAAI,CAAC;;iBAK1C,UAAA,GAAA,CAAc,GAAA,YAAe,CAAC;;iBAI9B,UAAA,CAAW,GAAY;;iBAIvB,cAAA,WAAyB,GAAA,qBAAwB,GAAA,mBAAA,CAAA,GAA0B,CAAA;;;;;;;iBAY3E,qBAAA,kBAAuC,MAAA,oBAA0B,MAAA,kBAAA,CAAA,uBAC5D,QAAA,WAEnB,IAAA,EAAM,IAAA,EACN,MAAA,GAAS,QAAA,CAAS,IAAA;AAAA,KAoBf,kBAAA;EAA0B,MAAA,EAAQ,OAAA;EAAS,KAAA,GAAQ,CAAA;EAAG,OAAA,GAAU,GAAA;AAAA;;;;;;iBAOrD,KAAA,WAAgB,MAAA,kBAAA,CAC9B,SAAA,GAAY,KAAA,EAAO,CAAA,KAAM,UAAA,EACzB,OAAA,EAAS,kBAAA,CAAmB,CAAA,IAC3B,CAAA;;iBAca,OAAA,CAAQ,OAAgC,EAAvB,MAAM;;;;;iBASvB,SAAA,GAAA,CAAa,EAAA,SAAW,CAAA,GAAI,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { ComponentFn, Fragment, Props, VNode, VNodeChild } from "@pyreon/core";
2
+
3
+ //#region src/jsx-runtime.d.ts
4
+ declare function jsx(type: string | ComponentFn | symbol, props: Props & {
5
+ children?: VNodeChild | VNodeChild[];
6
+ }, key?: string | number | null): VNode;
7
+ declare const jsxs: typeof jsx;
8
+ //#endregion
9
+ export { Fragment, jsx, jsxs };
10
+ //# sourceMappingURL=jsx-runtime2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-runtime2.d.ts","names":[],"sources":["../../../src/jsx-runtime.ts"],"mappings":";;;iBA6QgB,GAAA,CACd,IAAA,WAAe,WAAA,WACf,KAAA,EAAO,KAAA;EAAU,QAAA,GAAW,UAAA,GAAa,UAAA;AAAA,GACzC,GAAA,4BACC,KAAA;AAAA,cAyCU,IAAA,SAAI,GAAM"}
@@ -0,0 +1,56 @@
1
+ //#region src/index.d.ts
2
+ type Subscriber<T> = (value: T) => void;
3
+ type Invalidator<T> = (value?: T) => void;
4
+ type Unsubscriber = () => void;
5
+ type Updater<T> = (value: T) => T;
6
+ /** `(set, update?) => stop?` — lazy notifier, runs on first subscriber. */
7
+ type StartStopNotifier<T> = (set: (value: T) => void, update: (fn: Updater<T>) => void) => Unsubscriber | void;
8
+ interface Readable<T> {
9
+ subscribe(run: Subscriber<T>, invalidate?: Invalidator<T>): Unsubscriber;
10
+ }
11
+ interface Writable<T> extends Readable<T> {
12
+ set(value: T): void;
13
+ update(updater: Updater<T>): void;
14
+ }
15
+ /**
16
+ * Svelte-compatible `writable`. A faithful Svelte store: a Set of
17
+ * subscribers notified synchronously on `set`/`update`. NOT signal-
18
+ * backed — Svelte's own `writable` is signal-free, and `derived` here
19
+ * subscribes to its inputs explicitly, so no signal auto-tracking is
20
+ * needed.
21
+ *
22
+ * Subscribing inside a compat component body is the faithful equivalent
23
+ * of Svelte's `$store` auto-subscription: the subscriber carries the
24
+ * component's `scheduleRerender`, so a store write re-renders the
25
+ * component — the same "write drives re-render" model the sibling
26
+ * solid-compat layer uses. Crucially this is NOT a persistent tracking
27
+ * effect: such an effect, created inside the wrapper accessor's run, is
28
+ * collected as an inner effect and disposed on the NEXT re-render (the
29
+ * cached path never recreates it), so store changes stopped propagating
30
+ * after the first one. A plain subscriber Set has no such hazard — it
31
+ * lives until `unsub` (registered in `ctx.unmountCallbacks`).
32
+ *
33
+ * `start` runs when the subscriber count goes 0→1 and its returned
34
+ * `stop` runs at 1→0. Synchronous `set` calls inside `start` mutate the
35
+ * value but do NOT notify (the store isn't "ready" until `start`
36
+ * returns), so the first subscriber sees exactly the post-start value —
37
+ * matching Svelte's `derived` (one emission, no spurious initial).
38
+ */
39
+ declare function writable<T>(value?: T, start?: StartStopNotifier<T>): Writable<T>;
40
+ /** Svelte-compatible `readable` — a `writable` with `set`/`update` hidden. */
41
+ declare function readable<T>(value?: T, start?: StartStopNotifier<T>): Readable<T>;
42
+ /** Svelte-compatible `readonly` — view of a store exposing only `subscribe`. */
43
+ declare function readonly<T>(store: Readable<T>): Readable<T>;
44
+ /** Svelte-compatible `get` — read a store's value synchronously. */
45
+ declare function get<T>(store: Readable<T>): T;
46
+ type Stores = Readable<unknown> | [Readable<unknown>, ...Array<Readable<unknown>>] | Array<Readable<unknown>>;
47
+ type StoresValues<T> = T extends Readable<infer U> ? U : { [K in keyof T]: T[K] extends Readable<infer U> ? U : never };
48
+ /**
49
+ * Svelte-compatible `derived`. Supports both the sync form
50
+ * `(values) => result` and the async/cleanup form
51
+ * `(values, set, update?) => stop`.
52
+ */
53
+ declare function derived<S extends Stores, T>(stores: S, fn: ((values: StoresValues<S>) => T) | ((values: StoresValues<S>, set: (value: T) => void, update: (fn: Updater<T>) => void) => Unsubscriber | void), initialValue?: T): Readable<T>;
54
+ //#endregion
55
+ export { type Invalidator, type Readable, type StartStopNotifier, type Subscriber, type Unsubscriber, type Updater, type Writable, derived, get, readable, readonly, writable };
56
+ //# sourceMappingURL=store2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store2.d.ts","names":[],"sources":["../../../src/index.ts"],"mappings":";KAsDY,UAAA,OAAiB,KAAA,EAAO,CAAC;AAAA,KACzB,WAAA,OAAkB,KAAA,GAAQ,CAAC;AAAA,KAC3B,YAAA;AAAA,KACA,OAAA,OAAc,KAAA,EAAO,CAAA,KAAM,CAAC;AAExC;AAAA,KAAY,iBAAA,OACV,GAAA,GAAM,KAAA,EAAO,CAAA,WACb,MAAA,GAAS,EAAA,EAAI,OAAA,CAAQ,CAAA,eAClB,YAAA;AAAA,UAEY,QAAA;EACf,SAAA,CAAU,GAAA,EAAK,UAAA,CAAW,CAAA,GAAI,UAAA,GAAa,WAAA,CAAY,CAAA,IAAK,YAAA;AAAA;AAAA,UAE7C,QAAA,YAAoB,QAAA,CAAS,CAAA;EAC5C,GAAA,CAAI,KAAA,EAAO,CAAA;EACX,MAAA,CAAO,OAAA,EAAS,OAAA,CAAQ,CAAA;AAAA;;;;;;;;;;;AAPT;AAEjB;;;;;;;;;;;;;iBAwEgB,QAAA,GAAA,CAAY,KAAA,GAAQ,CAAA,EAAG,KAAA,GAAO,iBAAA,CAAkB,CAAA,IAAY,QAAA,CAAS,CAAA;;iBAyFrE,QAAA,GAAA,CAAY,KAAA,GAAQ,CAAA,EAAG,KAAA,GAAQ,iBAAA,CAAkB,CAAA,IAAK,QAAA,CAAS,CAAA;;iBAQ/D,QAAA,GAAA,CAAY,KAAA,EAAO,QAAA,CAAS,CAAA,IAAK,QAAA,CAAS,CAAA;;iBAO1C,GAAA,GAAA,CAAO,KAAA,EAAO,QAAA,CAAS,CAAA,IAAK,CAAA;AAAA,KAavC,MAAA,GACD,QAAA,aACC,QAAA,cAAsB,KAAA,CAAM,QAAA,cAC7B,KAAA,CAAM,QAAA;AAAA,KACL,YAAA,MAAkB,CAAA,SAAU,QAAA,YAC7B,CAAA,iBACc,CAAA,GAAI,CAAA,CAAE,CAAA,UAAW,QAAA,YAAoB,CAAA;;;;;;iBAOvC,OAAA,WAAkB,MAAA,IAAA,CAChC,MAAA,EAAQ,CAAA,EACR,EAAA,IACM,MAAA,EAAQ,YAAA,CAAa,CAAA,MAAO,CAAA,MAE5B,MAAA,EAAQ,YAAA,CAAa,CAAA,GACrB,GAAA,GAAM,KAAA,EAAO,CAAA,WACb,MAAA,GAAS,EAAA,EAAI,OAAA,CAAQ,CAAA,eAClB,YAAA,UACT,YAAA,GAAe,CAAA,GACd,QAAA,CAAS,CAAA"}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@pyreon/svelte-compat",
3
+ "version": "0.17.0",
4
+ "description": "Svelte-compatible API shim for Pyreon — write Svelte-style stores / lifecycle code that runs on Pyreon's reactive engine",
5
+ "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/svelte-compat#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/pyreon/pyreon/issues"
8
+ },
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/pyreon/pyreon.git",
13
+ "directory": "packages/tools/svelte-compat"
14
+ },
15
+ "files": [
16
+ "lib",
17
+ "src",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "type": "module",
22
+ "sideEffects": false,
23
+ "main": "./lib/index.js",
24
+ "module": "./lib/index.js",
25
+ "types": "./lib/types/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "bun": "./src/index.ts",
29
+ "import": "./lib/index.js",
30
+ "types": "./lib/types/index.d.ts"
31
+ },
32
+ "./store": {
33
+ "bun": "./src/store.ts",
34
+ "import": "./lib/store.js",
35
+ "types": "./lib/types/store.d.ts"
36
+ },
37
+ "./jsx-runtime": {
38
+ "bun": "./src/jsx-runtime.ts",
39
+ "import": "./lib/jsx-runtime.js",
40
+ "types": "./lib/types/jsx-runtime.d.ts"
41
+ },
42
+ "./jsx-dev-runtime": {
43
+ "bun": "./src/jsx-runtime.ts",
44
+ "import": "./lib/jsx-runtime.js",
45
+ "types": "./lib/types/jsx-runtime.d.ts"
46
+ }
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "scripts": {
52
+ "build": "vl_rolldown_build",
53
+ "dev": "vl_rolldown_build-watch",
54
+ "test": "vitest run",
55
+ "test:browser": "vitest run --config ./vitest.browser.config.ts",
56
+ "typecheck": "tsc --noEmit",
57
+ "lint": "oxlint .",
58
+ "prepublishOnly": "bun run build"
59
+ },
60
+ "devDependencies": {
61
+ "@happy-dom/global-registrator": "^20.9.0",
62
+ "@pyreon/test-utils": "^0.13.13",
63
+ "@pyreon/vitest-config": "0.13.1",
64
+ "@vitest/browser-playwright": "^4.1.7",
65
+ "happy-dom": "^20.9.0"
66
+ },
67
+ "peerDependencies": {
68
+ "@pyreon/core": "^0.26.1",
69
+ "@pyreon/reactivity": "^0.26.1",
70
+ "@pyreon/runtime-dom": "^0.26.1"
71
+ }
72
+ }
package/src/env.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Minimal process type — just enough for `process.env.NODE_ENV` checks.
3
+ * Avoids requiring @types/node in consumers that import pyreon source
4
+ * via the `"bun"` export condition.
5
+ */
6
+ declare var process: { env: { NODE_ENV?: string } }