ilha 0.14.6 → 0.14.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 +2 -2
- package/dist/define.d.ts +1 -1
- package/dist/define.js +1 -1
- package/dist/index.d.ts +53 -32
- package/dist/index.js +465 -12
- package/dist/jsx-dev-runtime.d.ts +2 -2
- package/dist/jsx-dev-runtime.js +1 -1
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/jsx-runtime.js +1 -1
- package/dist/{mount-DiIEX8s7.js → mount-BQ-zcg2x.js} +256 -55
- package/dist/types-CJF2qMK6.d.ts +100 -0
- package/dist/{vnode-C_BdUnIA.d.ts → vnode-CUTNGblA.d.ts} +13 -1
- package/dist/{vnode-DEMDQ0eA.js → vnode-M-oJMKcv.js} +1 -1
- package/package.json +3 -3
- package/dist/types-CyoYWhJf.d.ts +0 -48
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ count.update((n) => n + 1); // patch
|
|
|
51
51
|
In JSX, `{count}` subscribes the render. Derived values use Effect's `Atom.map` or `Atom.transform`:
|
|
52
52
|
|
|
53
53
|
```tsx
|
|
54
|
-
import * as Atom from "effect/
|
|
54
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
55
55
|
import { atom } from "ilha";
|
|
56
56
|
|
|
57
57
|
const items = atom([{ n: 1 }, { n: 2 }]);
|
|
@@ -74,7 +74,7 @@ Paint [Effect `Stream`](https://www.effect.website/docs/v4/api/effect/Stream) va
|
|
|
74
74
|
|
|
75
75
|
```tsx
|
|
76
76
|
import * as Stream from "effect/Stream";
|
|
77
|
-
import * as Atom from "effect/
|
|
77
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
78
78
|
import { atom } from "ilha";
|
|
79
79
|
|
|
80
80
|
function* List() {
|
package/dist/define.d.ts
CHANGED
package/dist/define.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,34 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as h } from "./vnode-
|
|
3
|
-
import * as
|
|
4
|
-
import
|
|
5
|
-
import * as
|
|
1
|
+
import { S as Yielded, b as WatchContext, c as EventSourceStatus, g as UnsafeHtml, h as ResourceOptions, i as Component, l as Fragment, m as ResourceFetcher, n as AtomHandle, o as EventSourceFeed, p as Resource, r as AtomOptions, s as EventSourceOptions, t as AtomEquality, u as Instruction, v as View, x as WatchOnceCleanup, y as WatchCallback } from "./types-CJF2qMK6.js";
|
|
2
|
+
import { t as h } from "./vnode-CUTNGblA.js";
|
|
3
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
4
|
+
import { Equal } from "effect";
|
|
5
|
+
import * as Effect$1 from "effect/Effect";
|
|
6
|
+
import * as Stream$1 from "effect/Stream";
|
|
7
|
+
import { AtomRegistry } from "effect/reactivity/AtomRegistry";
|
|
6
8
|
import "effect/Scope";
|
|
7
|
-
import { AtomRegistry } from "effect/unstable/reactivity/AtomRegistry";
|
|
8
|
-
//#region src/watch.d.ts
|
|
9
|
-
/** Values delivered to watch callbacks. */
|
|
10
|
-
type WatchValue = string | number | boolean | bigint | null | undefined | readonly WatchValue[] | WatchRecord;
|
|
11
|
-
interface WatchRecord {
|
|
12
|
-
readonly [key: string]: WatchValue | undefined;
|
|
13
|
-
}
|
|
14
|
-
/** Optional disposer returned from `watch.once`. */
|
|
15
|
-
type WatchOnceCleanup = () => void;
|
|
16
|
-
interface WatchFn {
|
|
17
|
-
<A extends WatchValue>(source: AtomHandle<A> | Atom.Atom<A> | Stream.Stream<A, unknown, unknown>, fn: (value: A) => void): Instruction<undefined>;
|
|
18
|
-
once: (fn: () => WatchOnceCleanup | undefined) => Instruction<undefined>;
|
|
19
|
-
}
|
|
20
|
-
declare const watch: WatchFn;
|
|
21
|
-
//#endregion
|
|
22
|
-
//#region src/atom.d.ts
|
|
23
|
-
/** Read atoms inside `fn` without subscribing the active render. Sync only. */
|
|
24
|
-
declare const untrack: <A>(fn: () => A) => A;
|
|
25
|
-
interface AtomFn {
|
|
26
|
-
<A>(init: A | Atom.Atom<A> | Effect.Effect<A, unknown, AtomRegistry> | Stream.Stream<A, unknown, AtomRegistry>): AtomHandle<A>;
|
|
27
|
-
lazy: <A>(init: () => A) => AtomHandle<A>;
|
|
28
|
-
}
|
|
29
|
-
declare const atom: AtomFn;
|
|
30
|
-
declare const batch: (f: () => void) => void;
|
|
31
|
-
//#endregion
|
|
32
9
|
//#region src/context.d.ts
|
|
33
10
|
interface ContextProviderProps<A> {
|
|
34
11
|
value: A;
|
|
@@ -57,8 +34,52 @@ interface ErrorBoundaryProps {
|
|
|
57
34
|
/** Catch subtree failures and paint a fallback. Supports reset(). */
|
|
58
35
|
declare const ErrorBoundary: (props: ErrorBoundaryProps) => View;
|
|
59
36
|
//#endregion
|
|
37
|
+
//#region src/watch.d.ts
|
|
38
|
+
/** Values delivered to watch callbacks. */
|
|
39
|
+
type WatchValue = string | number | boolean | bigint | null | undefined | readonly WatchValue[] | WatchRecord;
|
|
40
|
+
interface WatchRecord {
|
|
41
|
+
readonly [key: string]: WatchValue | undefined;
|
|
42
|
+
}
|
|
43
|
+
interface WatchFn {
|
|
44
|
+
<A extends WatchValue>(source: AtomHandle<A> | Atom.Atom<A> | Stream$1.Stream<A, unknown, unknown>, fn: WatchCallback<A>): Instruction<undefined>;
|
|
45
|
+
once: (fn: (ctx: WatchContext) => WatchOnceCleanup | undefined | Promise<WatchOnceCleanup | undefined>) => Instruction<undefined>;
|
|
46
|
+
}
|
|
47
|
+
declare const watch: WatchFn;
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/atom.d.ts
|
|
50
|
+
/** Key-order-insensitive deep equality for JSON-shaped values. */
|
|
51
|
+
declare const structuralEqual: typeof Equal.equals;
|
|
52
|
+
/** Read atoms inside `fn` without subscribing the active render. Sync only. */
|
|
53
|
+
declare const untrack: <A>(fn: () => A) => A;
|
|
54
|
+
interface AtomFn {
|
|
55
|
+
<A>(init: A | Atom.Atom<A> | Effect$1.Effect<A, unknown, AtomRegistry> | Stream$1.Stream<A, unknown, AtomRegistry>, options?: AtomOptions<A>): AtomHandle<A>;
|
|
56
|
+
lazy: <A>(init: () => A) => AtomHandle<A>;
|
|
57
|
+
}
|
|
58
|
+
declare const atom: AtomFn;
|
|
59
|
+
declare const batch: (f: () => void) => void;
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/resource.d.ts
|
|
62
|
+
/** Drop the cached value for `key` and refetch it in live components. */
|
|
63
|
+
declare const invalidate: (key: string) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Fetch-once, share-everywhere async state for `key`. Concurrent mounts
|
|
66
|
+
* dedupe on one in-flight request; remounts seed from the shared cache
|
|
67
|
+
* (and revalidate when `staleWhileRevalidate` holds). Call unconditionally
|
|
68
|
+
* at the top level of a sync component, before any `await`.
|
|
69
|
+
*/
|
|
70
|
+
declare const resource: <T>(key: string, fetcher: ResourceFetcher<T>, opts?: ResourceOptions) => Resource<T>;
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/sse.d.ts
|
|
73
|
+
/**
|
|
74
|
+
* Subscribe to server-sent events. Opens one connection per `(url, event)`
|
|
75
|
+
* per component, reconnects with backoff, and closes on unmount. SSR renders
|
|
76
|
+
* an empty stream with status `"closed"`. Call unconditionally at the top
|
|
77
|
+
* level of a component.
|
|
78
|
+
*/
|
|
79
|
+
declare const fromEventSource: <T = string>(url: string, opts?: EventSourceOptions<T>) => EventSourceFeed<T>;
|
|
80
|
+
//#endregion
|
|
60
81
|
//#region src/when.d.ts
|
|
61
|
-
declare const when: <A, E = never, R = never>(stream: Stream.Stream<A, E, R>, body: (value: A) => Generator<Yielded, View | undefined, View>) => Instruction<undefined, E>;
|
|
82
|
+
declare const when: <A, E = never, R = never>(stream: Stream$1.Stream<A, E, R>, body: (value: A) => Generator<Yielded, View | undefined, View>) => Instruction<undefined, E>;
|
|
62
83
|
//#endregion
|
|
63
84
|
//#region src/mount.d.ts
|
|
64
85
|
interface RenderToStringOptions {
|
|
@@ -85,4 +106,4 @@ declare const renderToString: (fn: Component, opts?: RenderToStringOptions) => P
|
|
|
85
106
|
*/
|
|
86
107
|
declare const unsafe: (html: string) => UnsafeHtml;
|
|
87
108
|
//#endregion
|
|
88
|
-
export { type AtomHandle, type Component, type ContextProviderProps, ErrorBoundary, type ErrorBoundaryProps, type ErrorFallback, type ErrorFallbackProps, Fragment, type IlhaContext, type MountOptions, type RenderToStringOptions, type UnsafeHtml, type View, type WatchOnceCleanup, atom, batch, context, createContext, h, mount, renderToString, unsafe, untrack, watch, when };
|
|
109
|
+
export { type AtomEquality, type AtomHandle, type AtomOptions, type Component, type ContextProviderProps, ErrorBoundary, type ErrorBoundaryProps, type ErrorFallback, type ErrorFallbackProps, type EventSourceFeed, type EventSourceOptions, type EventSourceStatus, Fragment, type IlhaContext, type MountOptions, type RenderToStringOptions, type Resource, type ResourceFetcher, type ResourceOptions, type UnsafeHtml, type View, type WatchCallback, type WatchContext, type WatchOnceCleanup, atom, batch, context, createContext, fromEventSource, h, invalidate, mount, renderToString, resource, structuralEqual, unsafe, untrack, watch, when };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { a as interpret, c as handleOwner, d as
|
|
2
|
-
import { d as errorView, i as Fragment, p as isFunction, t as h } from "./vnode-
|
|
1
|
+
import { _ as makeFiber, a as interpret, c as handleOwner, d as structuralEqual, f as untrack, g as getFiber, h as closeFiber, i as runSetup, l as instr, m as clearFiberView, n as renderToString, o as atom, p as wrapHandle, r as unsafe, s as batch, t as mount, u as isAtomHandle } from "./mount-BQ-zcg2x.js";
|
|
2
|
+
import { d as errorView, g as isString, i as Fragment, p as isFunction, t as h } from "./vnode-M-oJMKcv.js";
|
|
3
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
3
4
|
import * as Effect from "effect/Effect";
|
|
4
5
|
import * as Stream from "effect/Stream";
|
|
5
|
-
import * as Atom from "effect/unstable/reactivity/Atom";
|
|
6
6
|
import * as Exit from "effect/Exit";
|
|
7
7
|
import * as Fiber from "effect/Fiber";
|
|
8
|
+
import * as Registry from "effect/reactivity/AtomRegistry";
|
|
8
9
|
import * as Scope from "effect/Scope";
|
|
9
|
-
import * as
|
|
10
|
+
import * as Queue from "effect/Queue";
|
|
10
11
|
|
|
11
12
|
//#region src/context.ts
|
|
12
13
|
const defaults = /* @__PURE__ */ new WeakMap();
|
|
@@ -77,6 +78,56 @@ const ErrorBoundary = (props) => {
|
|
|
77
78
|
//#endregion
|
|
78
79
|
//#region src/watch.ts
|
|
79
80
|
const WATCH_ONCE = Symbol("ilha.watch.once");
|
|
81
|
+
const newRunCell = () => ({
|
|
82
|
+
cleanup: void 0,
|
|
83
|
+
ctrl: null,
|
|
84
|
+
extra: [],
|
|
85
|
+
gen: 0
|
|
86
|
+
});
|
|
87
|
+
const cancelRun = (cell) => {
|
|
88
|
+
cell.gen += 1;
|
|
89
|
+
cell.ctrl?.abort();
|
|
90
|
+
cell.ctrl = null;
|
|
91
|
+
const run = cell.cleanup;
|
|
92
|
+
cell.cleanup = void 0;
|
|
93
|
+
run?.();
|
|
94
|
+
for (const extra of cell.extra.splice(0)) extra();
|
|
95
|
+
};
|
|
96
|
+
const asCleanup = (value) => isFunction(value) ? value : void 0;
|
|
97
|
+
const settleAsync = async (cell, gen, out) => {
|
|
98
|
+
try {
|
|
99
|
+
const c = await out;
|
|
100
|
+
if (cell.gen === gen) cell.cleanup = asCleanup(c);
|
|
101
|
+
else asCleanup(c)?.();
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error(error);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const settleRun = (cell, gen, out) => {
|
|
107
|
+
if (out instanceof Promise) {
|
|
108
|
+
settleAsync(cell, gen, out);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (cell.gen === gen) cell.cleanup = asCleanup(out);
|
|
112
|
+
else asCleanup(out)?.();
|
|
113
|
+
};
|
|
114
|
+
const runWithSignal = (cell, fn) => {
|
|
115
|
+
cancelRun(cell);
|
|
116
|
+
const { gen } = cell;
|
|
117
|
+
const ctrl = new AbortController();
|
|
118
|
+
cell.ctrl = ctrl;
|
|
119
|
+
const ctx = {
|
|
120
|
+
onCleanup: (extra) => {
|
|
121
|
+
if (ctrl.signal.aborted || cell.gen !== gen) {
|
|
122
|
+
extra();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
cell.extra.push(extra);
|
|
126
|
+
},
|
|
127
|
+
signal: ctrl.signal
|
|
128
|
+
};
|
|
129
|
+
settleRun(cell, gen, fn(ctx));
|
|
130
|
+
};
|
|
80
131
|
const runStreamWatch = (fiber, effect) => {
|
|
81
132
|
const scope = Scope.forkUnsafe(fiber.scope);
|
|
82
133
|
const provided = effect.pipe(Effect.provideService(Registry.AtomRegistry, fiber.registry));
|
|
@@ -110,7 +161,28 @@ const useWatchSlot = (fiber, key, mount, fn) => {
|
|
|
110
161
|
key
|
|
111
162
|
};
|
|
112
163
|
};
|
|
164
|
+
const sourceCells = /* @__PURE__ */ new WeakMap();
|
|
113
165
|
const registerWatch = (fiber, source, fn) => {
|
|
166
|
+
const slot = fiber.watchI ?? 0;
|
|
167
|
+
let cells = sourceCells.get(fiber);
|
|
168
|
+
if (!cells) {
|
|
169
|
+
cells = [];
|
|
170
|
+
sourceCells.set(fiber, cells);
|
|
171
|
+
}
|
|
172
|
+
const entry = cells[slot] ?? {
|
|
173
|
+
cell: newRunCell(),
|
|
174
|
+
fn: void 0
|
|
175
|
+
};
|
|
176
|
+
cells[slot] = entry;
|
|
177
|
+
entry.fn = fn;
|
|
178
|
+
const { cell } = entry;
|
|
179
|
+
const wrapped = (value) => {
|
|
180
|
+
const latest = entry.fn ?? fn;
|
|
181
|
+
runWithSignal(cell, (ctx) => latest(value, ctx));
|
|
182
|
+
};
|
|
183
|
+
const disposeCell = () => {
|
|
184
|
+
cancelRun(cell);
|
|
185
|
+
};
|
|
114
186
|
if (Stream.isStream(source)) {
|
|
115
187
|
useWatchSlot(fiber, source, (run) => {
|
|
116
188
|
let active = true;
|
|
@@ -119,15 +191,22 @@ const registerWatch = (fiber, source, fn) => {
|
|
|
119
191
|
})));
|
|
120
192
|
return () => {
|
|
121
193
|
active = false;
|
|
194
|
+
disposeCell();
|
|
122
195
|
stop();
|
|
123
196
|
};
|
|
124
|
-
},
|
|
197
|
+
}, wrapped);
|
|
125
198
|
return;
|
|
126
199
|
}
|
|
127
200
|
const atom = isAtomHandle(source) ? source.atom : source;
|
|
128
|
-
useWatchSlot(fiber, atom, (run) =>
|
|
201
|
+
useWatchSlot(fiber, atom, (run) => {
|
|
202
|
+
const unsub = fiber.registry.subscribe(atom, run, { immediate: true });
|
|
203
|
+
return () => {
|
|
204
|
+
disposeCell();
|
|
205
|
+
unsub();
|
|
206
|
+
};
|
|
207
|
+
}, wrapped);
|
|
129
208
|
};
|
|
130
|
-
/** Run `fn` once on mount. Return a function to clean up on unmount. */
|
|
209
|
+
/** Run `fn` once on mount. Return a function (or a promise of one) to clean up on unmount. */
|
|
131
210
|
const watchOnce = (fn) => {
|
|
132
211
|
const fiber = getFiber();
|
|
133
212
|
const i = fiber.watchI ?? 0;
|
|
@@ -139,12 +218,11 @@ const watchOnce = (fn) => {
|
|
|
139
218
|
existing.dispose();
|
|
140
219
|
fiber.watchSlots[i] = void 0;
|
|
141
220
|
}
|
|
142
|
-
|
|
221
|
+
const cell = newRunCell();
|
|
222
|
+
runWithSignal(cell, fn);
|
|
143
223
|
fiber.watchSlots[i] = {
|
|
144
224
|
dispose: () => {
|
|
145
|
-
|
|
146
|
-
cleanup = void 0;
|
|
147
|
-
run?.();
|
|
225
|
+
cancelRun(cell);
|
|
148
226
|
},
|
|
149
227
|
key: WATCH_ONCE
|
|
150
228
|
};
|
|
@@ -158,6 +236,381 @@ const watchSource = (source, fn) => {
|
|
|
158
236
|
};
|
|
159
237
|
const watch = Object.assign(watchSource, { once: watchOnce });
|
|
160
238
|
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/resource.ts
|
|
241
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
242
|
+
const liveRefetchers = /* @__PURE__ */ new Map();
|
|
243
|
+
const cells$1 = /* @__PURE__ */ new WeakMap();
|
|
244
|
+
const entryFor = (key) => sharedCache.get(key);
|
|
245
|
+
/** Drop the cached value for `key` and refetch it in live components. */
|
|
246
|
+
const invalidate = (key) => {
|
|
247
|
+
sharedCache.delete(key);
|
|
248
|
+
const live = liveRefetchers.get(key);
|
|
249
|
+
if (!live) return;
|
|
250
|
+
for (const refetch of live) refetch();
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* Fetch-once, share-everywhere async state for `key`. Concurrent mounts
|
|
254
|
+
* dedupe on one in-flight request; remounts seed from the shared cache
|
|
255
|
+
* (and revalidate when `staleWhileRevalidate` holds). Call unconditionally
|
|
256
|
+
* at the top level of a sync component, before any `await`.
|
|
257
|
+
*/
|
|
258
|
+
const resource = (key, fetcher, opts) => {
|
|
259
|
+
const fiber = getFiber();
|
|
260
|
+
const data = atom(void 0);
|
|
261
|
+
const error = atom(void 0);
|
|
262
|
+
const loading = atom(true);
|
|
263
|
+
let byKey = cells$1.get(fiber);
|
|
264
|
+
if (!byKey) {
|
|
265
|
+
byKey = /* @__PURE__ */ new Map();
|
|
266
|
+
cells$1.set(fiber, byKey);
|
|
267
|
+
}
|
|
268
|
+
if (fiber.runtime.ssr) {
|
|
269
|
+
let ssrCell = byKey.get(key);
|
|
270
|
+
if (!ssrCell) {
|
|
271
|
+
const { runtime } = fiber;
|
|
272
|
+
const sc = {
|
|
273
|
+
ctrl: null,
|
|
274
|
+
refetch: async () => {
|
|
275
|
+
sc.ctrl?.abort();
|
|
276
|
+
const ctrl = new AbortController();
|
|
277
|
+
sc.ctrl = ctrl;
|
|
278
|
+
sc.sink.loading.set(true);
|
|
279
|
+
runtime.begin();
|
|
280
|
+
try {
|
|
281
|
+
const v = await fetcher(key, { signal: ctrl.signal });
|
|
282
|
+
if (!ctrl.signal.aborted) {
|
|
283
|
+
sc.sink.data.set(v);
|
|
284
|
+
sc.sink.error.set(void 0);
|
|
285
|
+
sc.sink.loading.set(false);
|
|
286
|
+
}
|
|
287
|
+
return v;
|
|
288
|
+
} catch (fetchError) {
|
|
289
|
+
if (!ctrl.signal.aborted) {
|
|
290
|
+
sc.sink.error.set(fetchError);
|
|
291
|
+
sc.sink.loading.set(false);
|
|
292
|
+
}
|
|
293
|
+
throw fetchError;
|
|
294
|
+
} finally {
|
|
295
|
+
runtime.end();
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
sink: {
|
|
299
|
+
data,
|
|
300
|
+
error,
|
|
301
|
+
loading
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
Effect.runSync(Scope.addFinalizer(fiber.scope, Effect.sync(() => sc.ctrl?.abort())));
|
|
305
|
+
byKey.set(key, sc);
|
|
306
|
+
ssrCell = sc;
|
|
307
|
+
const settleSsr = async () => {
|
|
308
|
+
try {
|
|
309
|
+
await sc.refetch();
|
|
310
|
+
} catch {}
|
|
311
|
+
};
|
|
312
|
+
settleSsr();
|
|
313
|
+
}
|
|
314
|
+
ssrCell.sink = {
|
|
315
|
+
data,
|
|
316
|
+
error,
|
|
317
|
+
loading
|
|
318
|
+
};
|
|
319
|
+
return {
|
|
320
|
+
data,
|
|
321
|
+
error,
|
|
322
|
+
loading,
|
|
323
|
+
refetch: ssrCell.refetch
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
let cell = byKey.get(key);
|
|
327
|
+
if (!cell) {
|
|
328
|
+
cell = {
|
|
329
|
+
alive: true,
|
|
330
|
+
ctrl: null,
|
|
331
|
+
held: false,
|
|
332
|
+
sink: {
|
|
333
|
+
data,
|
|
334
|
+
error,
|
|
335
|
+
loading
|
|
336
|
+
},
|
|
337
|
+
started: false
|
|
338
|
+
};
|
|
339
|
+
byKey.set(key, cell);
|
|
340
|
+
}
|
|
341
|
+
cell.sink = {
|
|
342
|
+
data,
|
|
343
|
+
error,
|
|
344
|
+
loading
|
|
345
|
+
};
|
|
346
|
+
const c = cell;
|
|
347
|
+
const fiberCells = byKey;
|
|
348
|
+
const adoptShared = async (shared) => {
|
|
349
|
+
try {
|
|
350
|
+
const v = await shared;
|
|
351
|
+
if (c.alive) {
|
|
352
|
+
c.sink.data.set(v);
|
|
353
|
+
c.sink.error.set(void 0);
|
|
354
|
+
c.sink.loading.set(false);
|
|
355
|
+
}
|
|
356
|
+
} catch (fetchError) {
|
|
357
|
+
if (c.alive) {
|
|
358
|
+
c.sink.error.set(fetchError);
|
|
359
|
+
c.sink.loading.set(false);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
const start = () => {
|
|
364
|
+
if (!c.alive) return Promise.resolve(c.sink.data());
|
|
365
|
+
const hit = entryFor(key);
|
|
366
|
+
if (hit?.promise) {
|
|
367
|
+
c.sink.loading.set(true);
|
|
368
|
+
adoptShared(hit.promise);
|
|
369
|
+
return hit.promise;
|
|
370
|
+
}
|
|
371
|
+
c.ctrl?.abort();
|
|
372
|
+
const ctrl = new AbortController();
|
|
373
|
+
c.ctrl = ctrl;
|
|
374
|
+
c.sink.loading.set(true);
|
|
375
|
+
const flight = {};
|
|
376
|
+
const run = async () => {
|
|
377
|
+
try {
|
|
378
|
+
const v = await fetcher(key, { signal: ctrl.signal });
|
|
379
|
+
if (entryFor(key)?.promise === flight.promise) sharedCache.set(key, {
|
|
380
|
+
data: v,
|
|
381
|
+
has: true
|
|
382
|
+
});
|
|
383
|
+
if (c.alive && !ctrl.signal.aborted) {
|
|
384
|
+
c.sink.data.set(v);
|
|
385
|
+
c.sink.error.set(void 0);
|
|
386
|
+
c.sink.loading.set(false);
|
|
387
|
+
}
|
|
388
|
+
return v;
|
|
389
|
+
} catch (fetchError) {
|
|
390
|
+
const cur = entryFor(key);
|
|
391
|
+
if (cur?.promise === flight.promise) cur.promise = void 0;
|
|
392
|
+
if (c.alive && !ctrl.signal.aborted) {
|
|
393
|
+
c.sink.error.set(fetchError);
|
|
394
|
+
c.sink.loading.set(false);
|
|
395
|
+
}
|
|
396
|
+
throw fetchError;
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const p = run();
|
|
400
|
+
flight.promise = p;
|
|
401
|
+
const prev = entryFor(key);
|
|
402
|
+
sharedCache.set(key, {
|
|
403
|
+
data: prev?.data,
|
|
404
|
+
has: prev?.has ?? false,
|
|
405
|
+
promise: p
|
|
406
|
+
});
|
|
407
|
+
return p;
|
|
408
|
+
};
|
|
409
|
+
const settleStart = async () => {
|
|
410
|
+
try {
|
|
411
|
+
await start();
|
|
412
|
+
} catch {}
|
|
413
|
+
};
|
|
414
|
+
if (!c.started) {
|
|
415
|
+
c.started = true;
|
|
416
|
+
const swr = opts?.staleWhileRevalidate ?? true;
|
|
417
|
+
const hit = entryFor(key);
|
|
418
|
+
if (hit?.promise) {
|
|
419
|
+
if (hit.has) {
|
|
420
|
+
c.sink.data.set(hit.data);
|
|
421
|
+
c.sink.loading.set(false);
|
|
422
|
+
}
|
|
423
|
+
adoptShared(hit.promise);
|
|
424
|
+
} else if (hit?.has) {
|
|
425
|
+
c.sink.data.set(hit.data);
|
|
426
|
+
c.sink.error.set(void 0);
|
|
427
|
+
c.sink.loading.set(false);
|
|
428
|
+
if (swr) settleStart();
|
|
429
|
+
} else {
|
|
430
|
+
const seed = c.sink.data();
|
|
431
|
+
if (seed === void 0) settleStart();
|
|
432
|
+
else {
|
|
433
|
+
sharedCache.set(key, {
|
|
434
|
+
data: seed,
|
|
435
|
+
has: true
|
|
436
|
+
});
|
|
437
|
+
if (swr) settleStart();
|
|
438
|
+
else c.sink.loading.set(false);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (!c.held) {
|
|
443
|
+
c.held = true;
|
|
444
|
+
const onInvalidate = () => {
|
|
445
|
+
settleStart();
|
|
446
|
+
};
|
|
447
|
+
watch.once(() => {
|
|
448
|
+
let live = liveRefetchers.get(key);
|
|
449
|
+
if (!live) {
|
|
450
|
+
live = /* @__PURE__ */ new Set();
|
|
451
|
+
liveRefetchers.set(key, live);
|
|
452
|
+
}
|
|
453
|
+
live.add(onInvalidate);
|
|
454
|
+
return () => {
|
|
455
|
+
c.alive = false;
|
|
456
|
+
c.ctrl?.abort();
|
|
457
|
+
const s = liveRefetchers.get(key);
|
|
458
|
+
s?.delete(onInvalidate);
|
|
459
|
+
if (s && s.size === 0) liveRefetchers.delete(key);
|
|
460
|
+
if (fiberCells.get(key) === c) fiberCells.delete(key);
|
|
461
|
+
};
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
data,
|
|
466
|
+
error,
|
|
467
|
+
loading,
|
|
468
|
+
refetch: () => start()
|
|
469
|
+
};
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
//#endregion
|
|
473
|
+
//#region src/sse.ts
|
|
474
|
+
const cells = /* @__PURE__ */ new WeakMap();
|
|
475
|
+
const MAX_BACKOFF_MS = 3e4;
|
|
476
|
+
const setStatus = (c, next) => {
|
|
477
|
+
c.state = next;
|
|
478
|
+
for (const sink of c.sinks.values()) sink.status.set(next);
|
|
479
|
+
};
|
|
480
|
+
const connect = ({ cell: c, decode, event, retryBase, url }) => {
|
|
481
|
+
let attempt = 0;
|
|
482
|
+
let timer;
|
|
483
|
+
let source;
|
|
484
|
+
let detach;
|
|
485
|
+
let disposed = false;
|
|
486
|
+
const emit = (value) => {
|
|
487
|
+
c.last = { value };
|
|
488
|
+
for (const sink of c.sinks.values()) sink.latest.set(value);
|
|
489
|
+
for (const push of c.targets) push(value);
|
|
490
|
+
};
|
|
491
|
+
const onMessage = (ev) => {
|
|
492
|
+
const raw = ev.data;
|
|
493
|
+
if (!isString(raw)) return;
|
|
494
|
+
let value;
|
|
495
|
+
try {
|
|
496
|
+
value = decode(raw);
|
|
497
|
+
} catch {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
emit(value);
|
|
501
|
+
};
|
|
502
|
+
const open = () => {
|
|
503
|
+
if (disposed) return;
|
|
504
|
+
setStatus(c, attempt === 0 ? "connecting" : "retrying");
|
|
505
|
+
const es = new EventSource(url);
|
|
506
|
+
source = es;
|
|
507
|
+
const onOpen = () => {
|
|
508
|
+
attempt = 0;
|
|
509
|
+
setStatus(c, "open");
|
|
510
|
+
};
|
|
511
|
+
const onError = () => {
|
|
512
|
+
if (disposed || es.readyState !== EventSource.CLOSED) return;
|
|
513
|
+
detach?.();
|
|
514
|
+
es.close();
|
|
515
|
+
attempt += 1;
|
|
516
|
+
setStatus(c, "retrying");
|
|
517
|
+
timer = setTimeout(open, Math.min(retryBase * 2 ** (attempt - 1), MAX_BACKOFF_MS));
|
|
518
|
+
};
|
|
519
|
+
const messageEvent = event ?? "message";
|
|
520
|
+
es.addEventListener(messageEvent, onMessage);
|
|
521
|
+
es.addEventListener("open", onOpen);
|
|
522
|
+
es.addEventListener("error", onError);
|
|
523
|
+
detach = () => {
|
|
524
|
+
es.removeEventListener(messageEvent, onMessage);
|
|
525
|
+
es.removeEventListener("open", onOpen);
|
|
526
|
+
es.removeEventListener("error", onError);
|
|
527
|
+
};
|
|
528
|
+
};
|
|
529
|
+
open();
|
|
530
|
+
return () => {
|
|
531
|
+
disposed = true;
|
|
532
|
+
clearTimeout(timer);
|
|
533
|
+
detach?.();
|
|
534
|
+
source?.close();
|
|
535
|
+
setStatus(c, "closed");
|
|
536
|
+
};
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* Subscribe to server-sent events. Opens one connection per `(url, event)`
|
|
540
|
+
* per component, reconnects with backoff, and closes on unmount. SSR renders
|
|
541
|
+
* an empty stream with status `"closed"`. Call unconditionally at the top
|
|
542
|
+
* level of a component.
|
|
543
|
+
*/
|
|
544
|
+
const fromEventSource = (url, opts) => {
|
|
545
|
+
const fiber = getFiber();
|
|
546
|
+
const status = atom("connecting");
|
|
547
|
+
const latest = atom(void 0);
|
|
548
|
+
const feedKey = `${url}\n${opts?.event ?? ""}`;
|
|
549
|
+
let byKey = cells.get(fiber);
|
|
550
|
+
if (!byKey) {
|
|
551
|
+
byKey = /* @__PURE__ */ new Map();
|
|
552
|
+
cells.set(fiber, byKey);
|
|
553
|
+
}
|
|
554
|
+
let cell = byKey.get(feedKey);
|
|
555
|
+
if (!cell) {
|
|
556
|
+
const targets = /* @__PURE__ */ new Set();
|
|
557
|
+
const stream = fiber.runtime.ssr ? Stream.empty : Stream.callback((queue) => {
|
|
558
|
+
const push = (value) => {
|
|
559
|
+
Queue.offerUnsafe(queue, value);
|
|
560
|
+
};
|
|
561
|
+
targets.add(push);
|
|
562
|
+
return Effect.acquireRelease(Effect.void, () => Effect.sync(() => targets.delete(push)));
|
|
563
|
+
});
|
|
564
|
+
cell = {
|
|
565
|
+
sinks: /* @__PURE__ */ new Map(),
|
|
566
|
+
state: "connecting",
|
|
567
|
+
stream,
|
|
568
|
+
targets,
|
|
569
|
+
users: 0
|
|
570
|
+
};
|
|
571
|
+
byKey.set(feedKey, cell);
|
|
572
|
+
}
|
|
573
|
+
const sinkKey = latest.atom;
|
|
574
|
+
cell.sinks.set(sinkKey, {
|
|
575
|
+
latest,
|
|
576
|
+
status
|
|
577
|
+
});
|
|
578
|
+
const c = cell;
|
|
579
|
+
const decode = opts?.schema ?? ((raw) => raw);
|
|
580
|
+
const retryBase = opts?.retryBaseMs ?? 1e3;
|
|
581
|
+
watch.once(() => {
|
|
582
|
+
if (fiber.runtime.ssr || globalThis.EventSource === void 0) {
|
|
583
|
+
status.set("closed");
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
c.users += 1;
|
|
587
|
+
if (c.stop) {
|
|
588
|
+
status.set(c.state);
|
|
589
|
+
if (c.last) latest.set(c.last.value);
|
|
590
|
+
} else c.stop = connect({
|
|
591
|
+
cell: c,
|
|
592
|
+
decode,
|
|
593
|
+
event: opts?.event,
|
|
594
|
+
retryBase,
|
|
595
|
+
url
|
|
596
|
+
});
|
|
597
|
+
return () => {
|
|
598
|
+
c.users -= 1;
|
|
599
|
+
if (c.users === 0) {
|
|
600
|
+
const { stop } = c;
|
|
601
|
+
c.stop = void 0;
|
|
602
|
+
stop?.();
|
|
603
|
+
}
|
|
604
|
+
c.sinks.delete(sinkKey);
|
|
605
|
+
};
|
|
606
|
+
});
|
|
607
|
+
return {
|
|
608
|
+
latest,
|
|
609
|
+
status,
|
|
610
|
+
stream: c.stream
|
|
611
|
+
};
|
|
612
|
+
};
|
|
613
|
+
|
|
161
614
|
//#endregion
|
|
162
615
|
//#region src/when.ts
|
|
163
616
|
const when = (stream, body) => instr(Effect.gen(function* whenBody() {
|
|
@@ -180,4 +633,4 @@ const when = (stream, body) => instr(Effect.gen(function* whenBody() {
|
|
|
180
633
|
}));
|
|
181
634
|
|
|
182
635
|
//#endregion
|
|
183
|
-
export { ErrorBoundary, Fragment, atom, batch, context, createContext, h, mount, renderToString, unsafe, untrack, watch, when };
|
|
636
|
+
export { ErrorBoundary, Fragment, atom, batch, context, createContext, fromEventSource, h, invalidate, mount, renderToString, resource, structuralEqual, unsafe, untrack, watch, when };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as JSX } from "./vnode-
|
|
1
|
+
import { l as Fragment } from "./types-CJF2qMK6.js";
|
|
2
|
+
import { n as JSX } from "./vnode-CUTNGblA.js";
|
|
3
3
|
import { jsx, jsxDEV, jsxs } from "./jsx-runtime.js";
|
|
4
4
|
export { Fragment, type JSX, jsx, jsxDEV, jsxs };
|
package/dist/jsx-dev-runtime.js
CHANGED
package/dist/jsx-runtime.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as JSX, t as h } from "./vnode-
|
|
1
|
+
import { a as ComponentFn, d as JsxComponent, f as PropBag, l as Fragment, v as View } from "./types-CJF2qMK6.js";
|
|
2
|
+
import { n as JSX, t as h } from "./vnode-CUTNGblA.js";
|
|
3
3
|
//#region src/jsx-runtime.d.ts
|
|
4
4
|
declare const jsx: (type: string | Fragment | ComponentFn | JsxComponent, props: PropBag | null, key?: string | number) => ReturnType<typeof h>;
|
|
5
5
|
declare const jsxs: (type: string | Fragment | ComponentFn | JsxComponent, props: PropBag | null, key?: string | number) => ReturnType<typeof h>;
|
package/dist/jsx-runtime.js
CHANGED