@streetui/state 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 StreetUI contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # @streetui/state
2
+
3
+ StreetUI's reactive core: signals, derived values, effects, batching, stores —
4
+ and `resource()`, the framework-native primitive for async data.
5
+
6
+ This document focuses on `resource()` and its error-handling companion,
7
+ `errorBoundary` (which lives in `@streetui/dsl` because it is a DSL block). For
8
+ the signal primitives (`signal`, `derived`, `effect`, `batch`) see the inline
9
+ docs in `src/signal.ts`.
10
+
11
+ ## Why a resource primitive
12
+
13
+ Async data has the same three hard problems in every app: *state* (is it
14
+ loading, did it fail, what's the value?), *races* (a slow request must not
15
+ overwrite a newer one), and *lifecycle* (a request must stop touching the UI
16
+ once its owner is gone). `resource()` solves all three on top of StreetUI's
17
+ existing signals — no second reactive system, no virtual DOM, and no HTTP client
18
+ baked in. The loader is any `Promise`-returning function, so `fetch()` works
19
+ directly and any transport is supported.
20
+
21
+ ## `resource(loader, options?)`
22
+
23
+ ```ts
24
+ import { resource } from '@streetui/state';
25
+
26
+ const users = resource<User[]>(
27
+ ({ signal }) => fetch('/api/users', { signal }).then((r) => {
28
+ if (!r.ok) throw new Error(`HTTP ${r.status}`);
29
+ return r.json() as Promise<User[]>;
30
+ }),
31
+ );
32
+ ```
33
+
34
+ `resource<T>(...)` returns a strongly typed `Resource<T>` — `data` is
35
+ `T | undefined`, and `error` is `unknown` (never `any`).
36
+
37
+ ### Loader
38
+
39
+ ```ts
40
+ type ResourceLoader<T> = (ctx: { signal: AbortSignal }) => Promise<T> | T;
41
+ ```
42
+
43
+ The loader receives an `AbortSignal`. Forward it to `fetch` (or any
44
+ signal-aware operation) so in-flight work is cancelled when the resource is
45
+ superseded or disposed. Cancellation is opt-in: ignoring the signal is fine, it
46
+ just means the request runs to completion (its late result is discarded, never
47
+ applied).
48
+
49
+ ### State (all reactive signals)
50
+
51
+ | Member | Type | Meaning |
52
+ |---|---|---|
53
+ | `status` | `ReadonlySignal<'idle' \| 'loading' \| 'success' \| 'error'>` | Lifecycle state |
54
+ | `data` | `ReadonlySignal<T \| undefined>` | Latest successful value |
55
+ | `error` | `ReadonlySignal<unknown>` | Latest error (`undefined` when none) |
56
+ | `loading` | `ReadonlySignal<boolean>` | `status === 'loading'` |
57
+ | `isRefetching` | `ReadonlySignal<boolean>` | Loading *while* previous `data` is retained |
58
+
59
+ These are read-only views over internal signals — consumers cannot `.set` them.
60
+ Because they are ordinary signals, `when(...)`, `listOf(...)`, `derived(...)`
61
+ and effects all work with them unchanged.
62
+
63
+ **Refetching is not a separate status.** A refetch enters `status === 'loading'`
64
+ but keeps the previous `data`; use `isRefetching` (or `loading && data !== undefined`)
65
+ to show a "refreshing" affordance without hiding the current data.
66
+
67
+ ### Methods
68
+
69
+ - `refetch(): Promise<void>` — start a new request. The previous `data` is
70
+ preserved during the reload (and on failure), so the UI never flashes empty.
71
+ - `dispose(): void` — abort any in-flight request, drop `watch` subscriptions,
72
+ and permanently stop the resource from writing state. Idempotent.
73
+
74
+ ### Options
75
+
76
+ ```ts
77
+ interface ResourceOptions {
78
+ immediate?: boolean; // default true — load on creation
79
+ watch?: ReadonlyArray<ReadonlySignal<unknown>>; // explicit dependencies
80
+ onCleanup?: (fn: () => void) => void; // auto-register dispose
81
+ }
82
+ ```
83
+
84
+ - **`immediate`** — set `false` to stay `'idle'` until the first `refetch()`.
85
+ - **`watch`** — dependencies are *explicit*. When any listed signal changes, the
86
+ resource refetches. There is no hidden auto-tracking, so there is no way to
87
+ create an accidental infinite refetch loop.
88
+ - **`onCleanup`** — pass a route's `ctx.onCleanup` (or any registrar) and
89
+ `dispose` is registered for you.
90
+
91
+ ```ts
92
+ const query = signal('');
93
+ const results = resource(
94
+ ({ signal }) => search(query.get(), signal),
95
+ { watch: [query], onCleanup: ctx.onCleanup },
96
+ );
97
+ ```
98
+
99
+ ## Guarantees
100
+
101
+ - **Race safety.** Each run gets a monotonic id; when a response arrives its id
102
+ is compared to the latest. An older response can never overwrite a newer one.
103
+ - **Abort.** Every run creates an `AbortController`; superseding a run or
104
+ disposing the resource aborts the previous one. A self-triggered `AbortError`
105
+ is swallowed (it is not a real failure and is never surfaced as `error`).
106
+ - **Lifecycle.** After `dispose()` no further state writes happen — a late
107
+ resolution or rejection is ignored. Wire `dispose` to the owner's cleanup and
108
+ the resource can never update detached DOM.
109
+
110
+ ## Error boundaries (`errorBoundary`, from `@streetui/dsl`)
111
+
112
+ `errorBoundary` contains failures to a region of the tree instead of letting one
113
+ failed async area take down the app:
114
+
115
+ ```ts
116
+ c.errorBoundary('users', (body) => {
117
+ body.when(users.loading, (l) => l.text('Loading…'));
118
+ body.listOf('list', derived(() => users.data.get() ?? []), (u, _i, x) =>
119
+ x.text(u.name),
120
+ );
121
+ }, {
122
+ source: users.error, // one signal or an array of them
123
+ onRetry: () => void users.refetch(),
124
+ fallback: (fb, error, retry) => {
125
+ fb.text(`Failed: ${(error as Error).message}`);
126
+ fb.button('Retry', { onClick: retry });
127
+ },
128
+ });
129
+ ```
130
+
131
+ Behaviour:
132
+
133
+ - Enters the error state when any observed **`source`** signal becomes non-null,
134
+ **or** when the body builder throws synchronously while building.
135
+ - **`retry()`** clears the local error, invokes `onRetry` (typically
136
+ `resource.refetch`), and re-attempts the body — without rebuilding the app.
137
+ - Reuses the same reactive `when()` machinery, so the fallback subtree and every
138
+ handler/subscription inside it is torn down on removal.
139
+ - It does **not** trap arbitrary global errors, and errors remain observable via
140
+ the resource's `error` signal.
141
+
142
+ ## SSR seeding
143
+
144
+ For server rendering, `resource()` accepts a server-resolved result so the
145
+ client does not re-fetch on hydration:
146
+
147
+ ```ts
148
+ const users = resource(loadUsers, {
149
+ initialData: seed, // from the SSR state island (see @streetui/renderer)
150
+ // initialError: err, // or seed an error instead
151
+ // initialStatus: 'success',
152
+ });
153
+ ```
154
+
155
+ When any of `initialData` / `initialError` / `initialStatus` is provided, the
156
+ resource starts in the corresponding non-`idle` state (`'success'` from
157
+ `initialData`, `'error'` from `initialError`, or an explicit `initialStatus`)
158
+ and **skips the automatic initial load**. It can still `refetch()` on demand;
159
+ pass `immediate: true` if you deliberately want a client-side refetch right after
160
+ hydration. Read the seed out of the SSR island with `readState` from
161
+ `@streetui/renderer`.
162
+
163
+ ## What this is *not*
164
+
165
+ `resource()` is deliberately small. It is not a caching layer, query library,
166
+ mutation framework, global store, suspense integration, or devtools. Those are
167
+ separate concerns that can be composed on top if and when they are needed.
package/dist/index.cjs ADDED
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DerivedSignal: () => DerivedSignal,
24
+ Signal: () => Signal,
25
+ Store: () => Store,
26
+ batch: () => batch,
27
+ createStore: () => createStore,
28
+ derived: () => derived,
29
+ effect: () => effect,
30
+ isBatching: () => isBatching,
31
+ observerCount: () => observerCount,
32
+ resource: () => resource,
33
+ signal: () => signal,
34
+ signalKind: () => signalKind
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/signal.ts
39
+ var _activeConsumer = null;
40
+ function withConsumer(consumer, fn) {
41
+ const prev = _activeConsumer;
42
+ _activeConsumer = consumer;
43
+ try {
44
+ return fn();
45
+ } finally {
46
+ _activeConsumer = prev;
47
+ }
48
+ }
49
+ var _batchDepth = 0;
50
+ var _pendingFlushes = /* @__PURE__ */ new Map();
51
+ function _enqueueBatchFlush(sig, value) {
52
+ _pendingFlushes.set(sig, { signal: sig, value });
53
+ }
54
+ function _drainBatch() {
55
+ const flushes = [..._pendingFlushes.values()];
56
+ _pendingFlushes.clear();
57
+ for (const { signal: sig, value } of flushes) {
58
+ sig._flushBatch(value);
59
+ }
60
+ }
61
+ var Signal = class {
62
+ _value;
63
+ _subscribers = /* @__PURE__ */ new Set();
64
+ _consumers = /* @__PURE__ */ new Set();
65
+ constructor(initial) {
66
+ this._value = initial;
67
+ }
68
+ get() {
69
+ if (_activeConsumer !== null) {
70
+ this._consumers.add(_activeConsumer);
71
+ _activeConsumer._addSource(this);
72
+ }
73
+ return this._value;
74
+ }
75
+ peek() {
76
+ return this._value;
77
+ }
78
+ set(value) {
79
+ if (Object.is(this._value, value)) return;
80
+ this._value = value;
81
+ if (_batchDepth > 0) {
82
+ _enqueueBatchFlush(this, value);
83
+ } else {
84
+ this._flush(value);
85
+ }
86
+ }
87
+ update(fn) {
88
+ this.set(fn(this._value));
89
+ }
90
+ subscribe(fn) {
91
+ this._subscribers.add(fn);
92
+ return () => {
93
+ this._subscribers.delete(fn);
94
+ };
95
+ }
96
+ _removeConsumer(consumer) {
97
+ this._consumers.delete(consumer);
98
+ }
99
+ /**
100
+ * Called by the batch machinery after the batch has completed.
101
+ * Notifies subscribers with the final coalesced value.
102
+ */
103
+ _flushBatch(value) {
104
+ this._flush(value);
105
+ }
106
+ _flush(value) {
107
+ for (const sub of [...this._subscribers]) sub(value);
108
+ for (const consumer of [...this._consumers]) consumer._invalidate();
109
+ }
110
+ /**
111
+ * @internal DevTools inspection only. The number of live observers
112
+ * (direct subscribers plus derived/effect consumers). Read-only; never
113
+ * mutates reactive state.
114
+ */
115
+ _observerCount() {
116
+ return this._subscribers.size + this._consumers.size;
117
+ }
118
+ };
119
+ var DerivedSignal = class {
120
+ _value = void 0;
121
+ _dirty = true;
122
+ _disposed = false;
123
+ _fn;
124
+ _subscribers = /* @__PURE__ */ new Set();
125
+ /** All upstream sources this derived currently reads from. */
126
+ _sources = /* @__PURE__ */ new Set();
127
+ /** Downstream consumers that depend on this derived. */
128
+ _consumers = /* @__PURE__ */ new Set();
129
+ constructor(fn) {
130
+ this._fn = fn;
131
+ }
132
+ get() {
133
+ if (_activeConsumer !== null) {
134
+ this._consumers.add(_activeConsumer);
135
+ _activeConsumer._addSource(this);
136
+ }
137
+ if (this._dirty) this._recompute();
138
+ return this._value;
139
+ }
140
+ peek() {
141
+ if (this._dirty) this._recompute();
142
+ return this._value;
143
+ }
144
+ subscribe(fn) {
145
+ if (this._dirty) this._recompute();
146
+ this._subscribers.add(fn);
147
+ return () => {
148
+ this._subscribers.delete(fn);
149
+ };
150
+ }
151
+ _addSource(src) {
152
+ this._sources.add(src);
153
+ }
154
+ _removeConsumer(consumer) {
155
+ this._consumers.delete(consumer);
156
+ }
157
+ _invalidate() {
158
+ if (this._disposed) return;
159
+ this._dirty = true;
160
+ const newVal = this.peek();
161
+ for (const sub of [...this._subscribers]) sub(newVal);
162
+ for (const consumer of [...this._consumers]) consumer._invalidate();
163
+ }
164
+ _recompute() {
165
+ for (const src of this._sources) src._removeConsumer(this);
166
+ this._sources.clear();
167
+ this._value = withConsumer(this, this._fn);
168
+ this._dirty = false;
169
+ }
170
+ dispose() {
171
+ this._disposed = true;
172
+ for (const src of this._sources) src._removeConsumer(this);
173
+ this._sources.clear();
174
+ this._subscribers.clear();
175
+ this._consumers.clear();
176
+ }
177
+ /**
178
+ * @internal DevTools inspection only. Live observers (subscribers plus
179
+ * downstream consumers). Read-only.
180
+ */
181
+ _observerCount() {
182
+ return this._subscribers.size + this._consumers.size;
183
+ }
184
+ };
185
+ var Effect = class {
186
+ _fn;
187
+ _cleanup = void 0;
188
+ _disposed = false;
189
+ _sources = /* @__PURE__ */ new Set();
190
+ constructor(fn) {
191
+ this._fn = fn;
192
+ this._run();
193
+ }
194
+ _addSource(src) {
195
+ this._sources.add(src);
196
+ }
197
+ _invalidate() {
198
+ if (this._disposed) return;
199
+ this._run();
200
+ }
201
+ _run() {
202
+ for (const src of this._sources) src._removeConsumer(this);
203
+ this._sources.clear();
204
+ if (typeof this._cleanup === "function") this._cleanup();
205
+ const result = withConsumer(this, this._fn);
206
+ this._cleanup = typeof result === "function" ? result : void 0;
207
+ }
208
+ dispose() {
209
+ this._disposed = true;
210
+ for (const src of this._sources) src._removeConsumer(this);
211
+ this._sources.clear();
212
+ if (typeof this._cleanup === "function") this._cleanup();
213
+ this._cleanup = void 0;
214
+ }
215
+ };
216
+ function signal(initial) {
217
+ return new Signal(initial);
218
+ }
219
+ function derived(fn) {
220
+ return new DerivedSignal(fn);
221
+ }
222
+ function effect(fn) {
223
+ const e = new Effect(fn);
224
+ return () => e.dispose();
225
+ }
226
+ function batch(fn) {
227
+ _batchDepth++;
228
+ try {
229
+ fn();
230
+ } finally {
231
+ _batchDepth--;
232
+ if (_batchDepth === 0) {
233
+ _drainBatch();
234
+ }
235
+ }
236
+ }
237
+ function isBatching() {
238
+ return _batchDepth > 0;
239
+ }
240
+ function signalKind(source) {
241
+ return source instanceof DerivedSignal ? "derived" : "writable";
242
+ }
243
+ function observerCount(source) {
244
+ const maybe = source;
245
+ return typeof maybe._observerCount === "function" ? maybe._observerCount() : void 0;
246
+ }
247
+
248
+ // src/store.ts
249
+ var Store = class {
250
+ _signals;
251
+ constructor(initial) {
252
+ const signals = {};
253
+ for (const key in initial) {
254
+ if (Object.prototype.hasOwnProperty.call(initial, key)) {
255
+ signals[key] = signal(initial[key]);
256
+ }
257
+ }
258
+ this._signals = signals;
259
+ }
260
+ get(key) {
261
+ const s = this._signals[key];
262
+ if (s === void 0) {
263
+ throw new Error(`Store: unknown key "${String(key)}"`);
264
+ }
265
+ return s.get();
266
+ }
267
+ set(key, value) {
268
+ const s = this._signals[key];
269
+ if (s === void 0) {
270
+ throw new Error(`Store: unknown key "${String(key)}"`);
271
+ }
272
+ s.set(value);
273
+ }
274
+ signal(key) {
275
+ const s = this._signals[key];
276
+ if (s === void 0) {
277
+ throw new Error(`Store: unknown key "${String(key)}"`);
278
+ }
279
+ return s;
280
+ }
281
+ subscribe(key, fn) {
282
+ return this.signal(key).subscribe(fn);
283
+ }
284
+ getSnapshot() {
285
+ const snap = {};
286
+ for (const key in this._signals) {
287
+ if (Object.prototype.hasOwnProperty.call(this._signals, key)) {
288
+ snap[key] = this._signals[key]?.peek();
289
+ }
290
+ }
291
+ return snap;
292
+ }
293
+ };
294
+ function createStore(initial) {
295
+ return new Store(initial);
296
+ }
297
+
298
+ // src/resource.ts
299
+ function isAbortError(err) {
300
+ return err instanceof Error && err.name === "AbortError" || typeof DOMException !== "undefined" && err instanceof DOMException && err.name === "AbortError";
301
+ }
302
+ function resource(loader, options = {}) {
303
+ const hasInitial = options.initialData !== void 0 || options.initialError !== void 0 || options.initialStatus !== void 0;
304
+ const seededStatus = options.initialStatus ?? (options.initialData !== void 0 ? "success" : options.initialError !== void 0 ? "error" : "idle");
305
+ const status = signal(seededStatus);
306
+ const data = signal(options.initialData);
307
+ const error = signal(options.initialError);
308
+ const loading = derived(() => status.get() === "loading");
309
+ const isRefetching = derived(() => status.get() === "loading" && data.get() !== void 0);
310
+ let disposed = false;
311
+ let runId = 0;
312
+ let controller = null;
313
+ const load = async () => {
314
+ if (disposed) return;
315
+ controller?.abort();
316
+ const myRun = ++runId;
317
+ const myController = new AbortController();
318
+ controller = myController;
319
+ batch(() => {
320
+ error.set(void 0);
321
+ status.set("loading");
322
+ });
323
+ try {
324
+ const result = await loader({ signal: myController.signal });
325
+ if (disposed || myRun !== runId) return;
326
+ batch(() => {
327
+ data.set(result);
328
+ error.set(void 0);
329
+ status.set("success");
330
+ });
331
+ } catch (err) {
332
+ if (disposed || myRun !== runId) return;
333
+ if (isAbortError(err)) return;
334
+ batch(() => {
335
+ error.set(err);
336
+ status.set("error");
337
+ });
338
+ }
339
+ };
340
+ const refetch = () => load();
341
+ const watchUnsubs = [];
342
+ if (options.watch !== void 0) {
343
+ for (const dep of options.watch) {
344
+ watchUnsubs.push(
345
+ dep.subscribe(() => {
346
+ if (!disposed) void load();
347
+ })
348
+ );
349
+ }
350
+ }
351
+ const dispose = () => {
352
+ if (disposed) return;
353
+ disposed = true;
354
+ controller?.abort();
355
+ controller = null;
356
+ for (const unsub of watchUnsubs) unsub();
357
+ watchUnsubs.length = 0;
358
+ };
359
+ if (options.onCleanup !== void 0) {
360
+ options.onCleanup(dispose);
361
+ }
362
+ if (options.immediate === true || options.immediate !== false && !hasInitial) {
363
+ void load();
364
+ }
365
+ return {
366
+ status,
367
+ data,
368
+ error,
369
+ loading,
370
+ isRefetching,
371
+ refetch,
372
+ dispose
373
+ };
374
+ }
375
+ // Annotate the CommonJS export names for ESM import in node:
376
+ 0 && (module.exports = {
377
+ DerivedSignal,
378
+ Signal,
379
+ Store,
380
+ batch,
381
+ createStore,
382
+ derived,
383
+ effect,
384
+ isBatching,
385
+ observerCount,
386
+ resource,
387
+ signal,
388
+ signalKind
389
+ });
390
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/signal.ts","../src/store.ts","../src/resource.ts"],"sourcesContent":["export * from './signal.js';\nexport * from './store.js';\nexport * from './resource.js';\n","/**\n * StreetUI reactive signals — framework-owned reactivity, no external libraries.\n *\n * Architecture:\n * Signal<T> — writable, holds a value, notifies on change\n * DerivedSignal<T> — read-only, lazily computed from other signals\n * effect() — side-effect that re-runs when dependencies change\n * batch() — run multiple updates before notifying\n */\n\nexport type Subscriber<T> = (value: T) => void;\nexport type Unsubscribe = () => void;\n\n// ── Tracking ──────────────────────────────────────────────────────────────────\n\n/**\n * Any reactive source that can have downstream consumers attached.\n * Both Signal and DerivedSignal implement this.\n */\nexport interface ReactiveSource<T> {\n get(): T;\n peek(): T;\n subscribe(fn: Subscriber<T>): Unsubscribe;\n /** Internal: remove a downstream consumer. */\n _removeConsumer(consumer: ReactiveConsumer): void;\n}\n\n/**\n * A downstream consumer (DerivedSignal or Effect) that can be invalidated\n * and can register itself as depending on a source.\n */\nexport interface ReactiveConsumer {\n _invalidate(): void;\n /** Internal: called by a source to register a dependency. */\n _addSource(src: ReactiveSource<unknown>): void;\n}\n\n/** The active consumer being tracked during a computation. */\nlet _activeConsumer: ReactiveConsumer | null = null;\n\nfunction withConsumer<T>(consumer: ReactiveConsumer, fn: () => T): T {\n const prev = _activeConsumer;\n _activeConsumer = consumer;\n try {\n return fn();\n } finally {\n _activeConsumer = prev;\n }\n}\n\n// ── Batch state (must be before Signal class so .set() can reference it) ──────\n\n/** Depth counter — batches nest; only the outermost flush propagates. */\nlet _batchDepth = 0;\n\ninterface PendingFlush {\n signal: Signal<unknown>;\n value: unknown;\n}\n\n/**\n * Keyed by signal identity — last-write-wins for each signal within a batch.\n * An ordered Map preserves first-seen insertion order for stable flush sequence.\n */\nconst _pendingFlushes = new Map<Signal<unknown>, PendingFlush>();\n\nfunction _enqueueBatchFlush(sig: Signal<unknown>, value: unknown): void {\n _pendingFlushes.set(sig, { signal: sig, value });\n}\n\nfunction _drainBatch(): void {\n const flushes = [..._pendingFlushes.values()];\n _pendingFlushes.clear();\n for (const { signal: sig, value } of flushes) {\n sig._flushBatch(value);\n }\n}\n\n// ── Signal ────────────────────────────────────────────────────────────────────\n\nexport interface ReadonlySignal<T> {\n get(): T;\n peek(): T;\n subscribe(fn: Subscriber<T>): Unsubscribe;\n}\n\nexport class Signal<T> implements ReactiveSource<T>, ReadonlySignal<T> {\n protected _value: T;\n private readonly _subscribers: Set<Subscriber<T>> = new Set();\n private readonly _consumers: Set<ReactiveConsumer> = new Set();\n\n constructor(initial: T) {\n this._value = initial;\n }\n\n get(): T {\n if (_activeConsumer !== null) {\n this._consumers.add(_activeConsumer);\n _activeConsumer._addSource(this as ReactiveSource<unknown>);\n }\n return this._value;\n }\n\n peek(): T {\n return this._value;\n }\n\n set(value: T): void {\n if (Object.is(this._value, value)) return;\n this._value = value;\n if (_batchDepth > 0) {\n // Defer notification until the batch flushes.\n // The stored value is always the latest (last-write-wins for same signal).\n _enqueueBatchFlush(this as unknown as Signal<unknown>, value);\n } else {\n this._flush(value);\n }\n }\n\n update(fn: (current: T) => T): void {\n this.set(fn(this._value));\n }\n\n subscribe(fn: Subscriber<T>): Unsubscribe {\n this._subscribers.add(fn);\n return () => { this._subscribers.delete(fn); };\n }\n\n _removeConsumer(consumer: ReactiveConsumer): void {\n this._consumers.delete(consumer);\n }\n\n /**\n * Called by the batch machinery after the batch has completed.\n * Notifies subscribers with the final coalesced value.\n */\n _flushBatch(value: unknown): void {\n this._flush(value as T);\n }\n\n private _flush(value: T): void {\n for (const sub of [...this._subscribers]) sub(value);\n for (const consumer of [...this._consumers]) consumer._invalidate();\n }\n\n /**\n * @internal DevTools inspection only. The number of live observers\n * (direct subscribers plus derived/effect consumers). Read-only; never\n * mutates reactive state.\n */\n _observerCount(): number {\n return this._subscribers.size + this._consumers.size;\n }\n}\n\n// ── DerivedSignal ─────────────────────────────────────────────────────────────\n\nexport class DerivedSignal<T> implements ReactiveSource<T>, ReactiveConsumer, ReadonlySignal<T> {\n private _value: T | undefined = undefined;\n private _dirty = true;\n private _disposed = false;\n private readonly _fn: () => T;\n private readonly _subscribers: Set<Subscriber<T>> = new Set();\n /** All upstream sources this derived currently reads from. */\n private readonly _sources: Set<ReactiveSource<unknown>> = new Set();\n /** Downstream consumers that depend on this derived. */\n private readonly _consumers: Set<ReactiveConsumer> = new Set();\n\n constructor(fn: () => T) {\n this._fn = fn;\n }\n\n get(): T {\n // Register this derived with the outer consumer if any\n if (_activeConsumer !== null) {\n this._consumers.add(_activeConsumer);\n _activeConsumer._addSource(this as ReactiveSource<unknown>);\n }\n if (this._dirty) this._recompute();\n return this._value as T;\n }\n\n peek(): T {\n if (this._dirty) this._recompute();\n return this._value as T;\n }\n\n subscribe(fn: Subscriber<T>): Unsubscribe {\n // Eagerly compute so sources track this derived as a consumer\n if (this._dirty) this._recompute();\n this._subscribers.add(fn);\n return () => { this._subscribers.delete(fn); };\n }\n\n _addSource(src: ReactiveSource<unknown>): void {\n this._sources.add(src);\n }\n\n _removeConsumer(consumer: ReactiveConsumer): void {\n this._consumers.delete(consumer);\n }\n\n _invalidate(): void {\n if (this._disposed) return;\n this._dirty = true;\n const newVal = this.peek(); // recompute\n for (const sub of [...this._subscribers]) sub(newVal);\n for (const consumer of [...this._consumers]) consumer._invalidate();\n }\n\n private _recompute(): void {\n // Detach from all current sources before re-tracking\n for (const src of this._sources) src._removeConsumer(this);\n this._sources.clear();\n\n this._value = withConsumer(this, this._fn);\n this._dirty = false;\n }\n\n dispose(): void {\n this._disposed = true;\n for (const src of this._sources) src._removeConsumer(this);\n this._sources.clear();\n this._subscribers.clear();\n this._consumers.clear();\n }\n\n /**\n * @internal DevTools inspection only. Live observers (subscribers plus\n * downstream consumers). Read-only.\n */\n _observerCount(): number {\n return this._subscribers.size + this._consumers.size;\n }\n}\n\n// ── effect ────────────────────────────────────────────────────────────────────\n\nclass Effect implements ReactiveConsumer {\n private readonly _fn: () => void | (() => void);\n private _cleanup: (() => void) | undefined = undefined;\n private _disposed = false;\n private readonly _sources: Set<ReactiveSource<unknown>> = new Set();\n\n constructor(fn: () => void | (() => void)) {\n this._fn = fn;\n this._run();\n }\n\n _addSource(src: ReactiveSource<unknown>): void {\n this._sources.add(src);\n }\n\n _invalidate(): void {\n if (this._disposed) return;\n this._run();\n }\n\n private _run(): void {\n // Detach from all current sources before re-tracking\n for (const src of this._sources) src._removeConsumer(this);\n this._sources.clear();\n\n if (typeof this._cleanup === 'function') this._cleanup();\n const result = withConsumer(this, this._fn);\n this._cleanup = typeof result === 'function' ? result : undefined;\n }\n\n dispose(): void {\n this._disposed = true;\n for (const src of this._sources) src._removeConsumer(this);\n this._sources.clear();\n if (typeof this._cleanup === 'function') this._cleanup();\n this._cleanup = undefined;\n }\n}\n\n// ── Public API ─────────────────────────────────────────────────────────────────\n\nexport function signal<T>(initial: T): Signal<T> {\n return new Signal(initial);\n}\n\nexport function derived<T>(fn: () => T): DerivedSignal<T> {\n return new DerivedSignal(fn);\n}\n\nexport function effect(fn: () => void | (() => void)): Unsubscribe {\n const e = new Effect(fn);\n return () => e.dispose();\n}\n\n/**\n * Run multiple signal updates as an atomic batch.\n *\n * Within the callback, calls to signal.set() are deferred — each signal\n * accumulates its latest value. When the outermost batch() returns,\n * each modified signal fires its subscribers exactly once with the final\n * value. Nested batch() calls are supported; the flush only runs when the\n * outermost batch exits.\n *\n * Example:\n * batch(() => {\n * count.set(1);\n * count.set(2);\n * count.set(3);\n * });\n * // subscribers see count = 3 exactly once\n */\nexport function batch(fn: () => void): void {\n _batchDepth++;\n try {\n fn();\n } finally {\n _batchDepth--;\n if (_batchDepth === 0) {\n _drainBatch();\n }\n }\n}\n\n/** True when inside a batch() call. Useful for advanced scheduling integration. */\nexport function isBatching(): boolean {\n return _batchDepth > 0;\n}\n\n// ── Inspection (read-only, DevTools-facing) ─────────────────────────────────\n\n/** Whether a signal is writable (`signal()`) or computed (`derived()`). */\nexport type SignalKind = 'writable' | 'derived';\n\n/** Classify a reactive value as writable or derived. */\nexport function signalKind(source: ReadonlySignal<unknown>): SignalKind {\n return source instanceof DerivedSignal ? 'derived' : 'writable';\n}\n\n/**\n * The number of live observers on a signal — direct subscribers plus derived\n * or effect consumers — or `undefined` if the source does not expose the count.\n * Read-only; safe for DevTools. Never mutates reactive state.\n */\nexport function observerCount(source: ReadonlySignal<unknown>): number | undefined {\n const maybe = source as { _observerCount?: () => number };\n return typeof maybe._observerCount === 'function' ? maybe._observerCount() : undefined;\n}\n","/**\n * A simple reactive store built on top of signals.\n * Useful for structured state with multiple fields.\n */\n\nimport { Signal, signal, type Subscriber, type Unsubscribe } from './signal.js';\n\nexport type StoreState = Record<string, unknown>;\n\nexport class Store<T extends StoreState> {\n private readonly _signals: Record<string, Signal<unknown>>;\n\n constructor(initial: T) {\n const signals: Record<string, Signal<unknown>> = {};\n for (const key in initial) {\n if (Object.prototype.hasOwnProperty.call(initial, key)) {\n signals[key] = signal(initial[key] as unknown);\n }\n }\n this._signals = signals;\n }\n\n get<K extends keyof T>(key: K): T[K] {\n const s = this._signals[key as string];\n if (s === undefined) {\n throw new Error(`Store: unknown key \"${String(key)}\"`);\n }\n return s.get() as T[K];\n }\n\n set<K extends keyof T>(key: K, value: T[K]): void {\n const s = this._signals[key as string];\n if (s === undefined) {\n throw new Error(`Store: unknown key \"${String(key)}\"`);\n }\n s.set(value as unknown);\n }\n\n signal<K extends keyof T>(key: K): Signal<T[K]> {\n const s = this._signals[key as string];\n if (s === undefined) {\n throw new Error(`Store: unknown key \"${String(key)}\"`);\n }\n return s as unknown as Signal<T[K]>;\n }\n\n subscribe<K extends keyof T>(key: K, fn: Subscriber<T[K]>): Unsubscribe {\n return this.signal(key).subscribe(fn);\n }\n\n getSnapshot(): T {\n const snap = {} as T;\n for (const key in this._signals) {\n if (Object.prototype.hasOwnProperty.call(this._signals, key)) {\n (snap as Record<string, unknown>)[key] = this._signals[key]?.peek();\n }\n }\n return snap;\n }\n}\n\nexport function createStore<T extends StoreState>(initial: T): Store<T> {\n return new Store(initial);\n}\n","/**\n * StreetUI async resources — framework-native asynchronous data.\n *\n * A `resource` wraps a Promise-returning loader and exposes its lifecycle as\n * ordinary StreetUI signals (status / data / error), so it composes with\n * `derived`, `effect`, `when()`, `listOf` and the renderer with no second\n * reactive system.\n *\n * State machine:\n *\n * idle ──(load)──▶ loading ──(resolve)──▶ success\n * │\n * └────(reject)──────▶ error\n *\n * Refetch keeps the previously-loaded `data` visible while `status` is\n * `'loading'` again (see `isRefetching`) — there is no separate `'refetching'`\n * status; it is expressed through `status === 'loading'` with `data` still set.\n *\n * The resource is transport-agnostic: the loader is any function returning a\n * value or a Promise. When it accepts the provided `AbortSignal`, in-flight\n * work is cancelled on `dispose()` or when a newer request supersedes it.\n */\n\nimport { signal, derived, batch, type ReadonlySignal, type Unsubscribe } from './signal.js';\n\nexport type ResourceStatus = 'idle' | 'loading' | 'success' | 'error';\n\n/** Context handed to the loader; carries an AbortSignal for cancellation. */\nexport interface ResourceLoaderContext {\n readonly signal: AbortSignal;\n}\n\n/** Any value-or-Promise producing function. Receives an abort-aware context. */\nexport type ResourceLoader<T> = (ctx: ResourceLoaderContext) => Promise<T> | T;\n\nexport interface ResourceOptions<T = unknown> {\n /** Load immediately on creation. Defaults to `true`. When `false`, stays `idle` until `refetch()`. */\n readonly immediate?: boolean;\n /**\n * Explicit reactive dependencies. When any listed signal changes, the\n * resource refetches. Dependencies are explicit (not auto-tracked from the\n * loader body) so there is no risk of an accidental infinite refetch loop.\n */\n readonly watch?: ReadonlyArray<ReadonlySignal<unknown>>;\n /**\n * Optional teardown registrar (e.g. a route's `ctx.onCleanup`). When given,\n * the resource registers its own `dispose` so it is cleaned up automatically\n * when its owner is removed.\n */\n readonly onCleanup?: (fn: () => void) => void;\n /**\n * Server-provided initial value for hydration. When present the resource\n * starts in `'success'` with this data already visible, and the initial\n * auto-load is skipped (so the client does not refetch data the server\n * already resolved). This is the client half of SSR resource transfer; the\n * server side awaits `refetch()` before serializing. Set `immediate: true`\n * explicitly to force a client refetch anyway.\n */\n readonly initialData?: T;\n /** Server-provided initial error for hydration (mirrors `initialData`). */\n readonly initialError?: unknown;\n /**\n * Explicit initial status override. Rarely needed — inferred as `'success'`\n * from `initialData` or `'error'` from `initialError`.\n */\n readonly initialStatus?: ResourceStatus;\n}\n\nexport interface Resource<T> {\n /** Reactive lifecycle status. */\n readonly status: ReadonlySignal<ResourceStatus>;\n /** The last successfully-loaded value, or `undefined` before first success. */\n readonly data: ReadonlySignal<T | undefined>;\n /** The most recent error, or `undefined` when there is none. Typed `unknown` — never `any`. */\n readonly error: ReadonlySignal<unknown>;\n /** Convenience: `status === 'loading'`. */\n readonly loading: ReadonlySignal<boolean>;\n /** Convenience: loading while previously-loaded data is still present (a refetch). */\n readonly isRefetching: ReadonlySignal<boolean>;\n /** Trigger a new request. Resolves when the request settles (or is superseded). */\n refetch(): Promise<void>;\n /** Cancel in-flight work, drop watchers, and ignore any late results. Idempotent. */\n dispose(): void;\n}\n\n/** True for an abort we initiated (so it is not surfaced as a real error). */\nfunction isAbortError(err: unknown): boolean {\n return (\n err instanceof Error && err.name === 'AbortError'\n ) || (\n typeof DOMException !== 'undefined' && err instanceof DOMException && err.name === 'AbortError'\n );\n}\n\nexport function resource<T>(loader: ResourceLoader<T>, options: ResourceOptions<T> = {}): Resource<T> {\n // Seed hydrated initial state (SSR transfer). `initialStatus` wins if given,\n // otherwise infer success/error from the provided value.\n const hasInitial =\n options.initialData !== undefined ||\n options.initialError !== undefined ||\n options.initialStatus !== undefined;\n\n const seededStatus: ResourceStatus =\n options.initialStatus ??\n (options.initialData !== undefined\n ? 'success'\n : options.initialError !== undefined\n ? 'error'\n : 'idle');\n\n const status = signal<ResourceStatus>(seededStatus);\n const data = signal<T | undefined>(options.initialData);\n const error = signal<unknown>(options.initialError);\n\n const loading = derived<boolean>(() => status.get() === 'loading');\n const isRefetching = derived<boolean>(() => status.get() === 'loading' && data.get() !== undefined);\n\n let disposed = false;\n /** Monotonic request id — only the newest request may write state (race guard). */\n let runId = 0;\n let controller: AbortController | null = null;\n\n const load = async (): Promise<void> => {\n if (disposed) return;\n\n // Supersede any in-flight request: abort it and bump the run id so its\n // eventual resolution is ignored.\n controller?.abort();\n const myRun = ++runId;\n const myController = new AbortController();\n controller = myController;\n\n // Enter loading. Preserve `data` (so a refetch keeps showing the old value)\n // and clear any previous error.\n batch(() => {\n error.set(undefined);\n status.set('loading');\n });\n\n try {\n const result = await loader({ signal: myController.signal });\n // Ignore stale or cancelled resolutions.\n if (disposed || myRun !== runId) return;\n batch(() => {\n data.set(result);\n error.set(undefined);\n status.set('success');\n });\n } catch (err) {\n if (disposed || myRun !== runId) return;\n // A self-initiated abort is not a user-visible error.\n if (isAbortError(err)) return;\n batch(() => {\n error.set(err);\n status.set('error');\n });\n }\n };\n\n const refetch = (): Promise<void> => load();\n\n // Explicit reactive dependencies: refetch when any watched signal changes.\n const watchUnsubs: Unsubscribe[] = [];\n if (options.watch !== undefined) {\n for (const dep of options.watch) {\n watchUnsubs.push(\n dep.subscribe(() => {\n if (!disposed) void load();\n }),\n );\n }\n }\n\n const dispose = (): void => {\n if (disposed) return;\n disposed = true;\n controller?.abort();\n controller = null;\n for (const unsub of watchUnsubs) unsub();\n watchUnsubs.length = 0;\n };\n\n if (options.onCleanup !== undefined) {\n options.onCleanup(dispose);\n }\n\n // Auto-load on creation unless disabled. When hydrated initial state is\n // present we skip the initial load by default (the server already resolved\n // it); an explicit `immediate: true` still forces a refetch.\n if (options.immediate === true || (options.immediate !== false && !hasInitial)) {\n void load();\n }\n\n return {\n status,\n data,\n error,\n loading,\n isRefetching,\n refetch,\n dispose,\n };\n}\n"],"mappings":"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","names":[]}