@memoized-dom/data 0.0.1

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 ADDED
@@ -0,0 +1,591 @@
1
+ # `@memoized-dom/data`
2
+
3
+ Browser-first fetch resources and callable actions for memoized-dom.
4
+
5
+ > **Current scope:** the package is usable in ordinary JavaScript and in
6
+ > compiled memoized-dom components. The compiler treats imported resource and
7
+ > action values like other opaque third-party state: while their getters are
8
+ > rendered, it rereads them through the existing volatile frame fallback. No
9
+ > `$fetch`-specific compiler adapter or method allowlist is involved.
10
+
11
+ ## Public API
12
+
13
+ The package exposes a default runtime plus an isolated-runtime factory:
14
+
15
+ ```ts
16
+ import {
17
+ $action,
18
+ $fetch,
19
+ clearDataRuntime,
20
+ createDataRuntime,
21
+ RequestError,
22
+ } from '@memoized-dom/data';
23
+ ```
24
+
25
+ - `$fetch` automatically performs a read request and returns a stable resource.
26
+ - `$action` creates a lazy callable operation for writes.
27
+ - `createDataRuntime` creates an isolated request, cache, and action boundary.
28
+ - `clearDataRuntime` clears active work and retained state in the default runtime.
29
+ - `RequestError` describes network, HTTP, decoding, and validation failures.
30
+
31
+ There is no provider, hook, or mutable global configuration API.
32
+
33
+ ### Isolated runtimes
34
+
35
+ Use a separate runtime for each server request, test, tenant, or other ownership
36
+ boundary that must not share request state:
37
+
38
+ ```ts
39
+ const data = createDataRuntime({
40
+ baseURL: 'https://api.example.test/',
41
+ fetch: customFetch,
42
+ });
43
+
44
+ const users = data.$fetch<User[]>('users');
45
+ data.clear();
46
+ ```
47
+
48
+ `baseURL` resolves relative targets and `fetch` injects a compatible fetch
49
+ implementation. In a browser, the default base URL is `location.href`. A
50
+ non-browser runtime must provide `baseURL` when it uses relative targets.
51
+
52
+ `clear()` aborts active reads and actions, resets their visible pending state,
53
+ detaches live read resources, and drops retained request data. Existing resource
54
+ and action objects remain valid; a detached resource can be refreshed to start
55
+ new work. `clearDataRuntime()` performs the same operation on the exported
56
+ default `$fetch` and `$action` runtime.
57
+
58
+ ### Component ownership
59
+
60
+ A runtime created inside a component should be cleared with the component:
61
+
62
+ ```tsx
63
+ function Users() {
64
+ const data = createDataRuntime();
65
+ const users = data.$fetch<User[]>('/api/users');
66
+ cleanup(data.clear);
67
+
68
+ return <p>{users.pending ? 'Loading' : users.data?.length}</p>;
69
+ }
70
+ ```
71
+
72
+ Memoized-dom component factories run once, so this does not recreate the
73
+ runtime on every update. `cleanup(data.clear)` aborts component-owned reads and
74
+ actions when the component is removed. When using the default runtime, a
75
+ component can instead own one resource with `cleanup(users.abort)`.
76
+
77
+ Cleanup is explicit today because ordinary third-party libraries remain usable
78
+ without implementing a memoized-dom lifecycle interface.
79
+
80
+ ## `$fetch`
81
+
82
+ ### Basic request
83
+
84
+ ```ts
85
+ interface User {
86
+ id: string;
87
+ name: string;
88
+ }
89
+
90
+ const users = $fetch<User[]>('/api/users');
91
+ ```
92
+
93
+ The request starts automatically. `$fetch<User[]>()` tells TypeScript that a
94
+ successful decoded response is expected to be `User[]`.
95
+
96
+ The generic is a developer assertion. It does not validate the server response
97
+ at runtime. Optional runtime validation is explained later.
98
+
99
+ ### Resource state
100
+
101
+ ```ts
102
+ users.data; // User[] | undefined
103
+ users.error; // RequestError | null
104
+ users.status; // 'idle' | 'pending' | 'success' | 'error'
105
+ users.pending; // boolean
106
+ users.refreshing; // boolean
107
+ ```
108
+
109
+ The initial cold request has this state:
110
+
111
+ ```ts
112
+ users.status === 'pending';
113
+ users.pending === true;
114
+ users.refreshing === false;
115
+ users.data === undefined;
116
+ users.error === null;
117
+ ```
118
+
119
+ After success:
120
+
121
+ ```ts
122
+ users.status === 'success';
123
+ users.pending === false;
124
+ users.data !== undefined;
125
+ ```
126
+
127
+ After an initial failure:
128
+
129
+ ```ts
130
+ users.status === 'error';
131
+ users.pending === false;
132
+ users.data === undefined;
133
+ users.error instanceof RequestError;
134
+ ```
135
+
136
+ During a refresh, previous data remains available:
137
+
138
+ ```ts
139
+ users.status === 'success';
140
+ users.pending === true;
141
+ users.refreshing === true;
142
+ users.data; // previous successful data
143
+ ```
144
+
145
+ A failed refresh preserves previous data. `status` remains `success`, and
146
+ `error` contains the refresh failure so the application may display a
147
+ non-blocking warning.
148
+
149
+ ### Query parameters
150
+
151
+ ```ts
152
+ const users = $fetch<User[]>('/api/users', {
153
+ query: {
154
+ search: 'Ada',
155
+ page: 2,
156
+ active: true,
157
+ tag: ['compiler', 'typescript'],
158
+ },
159
+ });
160
+ ```
161
+
162
+ Query values may be strings, numbers, booleans, `null`, arrays of those values,
163
+ or `undefined`. An `undefined` value is omitted. Arrays produce repeated query
164
+ fields. Query keys are normalized so equivalent requests share the same
165
+ identity regardless of object property order. URL fragments are removed because
166
+ they are not sent in HTTP requests and must not split request identity.
167
+
168
+ ### Request headers
169
+
170
+ ```ts
171
+ const profile = $fetch<Profile>('/api/profile', {
172
+ headers: {
173
+ Authorization: `Bearer ${token}`,
174
+ },
175
+ });
176
+ ```
177
+
178
+ Headers participate in automatic request identity. Requests with different
179
+ authorization values therefore do not share data by default. Headers are copied
180
+ when the resource is created, so later mutation of a supplied `Headers` object
181
+ cannot make request execution disagree with its identity.
182
+
183
+ ### Paused resource
184
+
185
+ `null` is the only paused target:
186
+
187
+ ```ts
188
+ const user = $fetch<User>(
189
+ userId ? `/api/users/${userId}` : null,
190
+ );
191
+ ```
192
+
193
+ A paused resource has `status: 'idle'` and performs no request. Automatic
194
+ reevaluation when `userId` changes is a deferred request-argument optimization;
195
+ the current call captures only the value passed at creation.
196
+
197
+ When request arguments change today, replace the component-local resource
198
+ explicitly and release the previous one:
199
+
200
+ ```tsx
201
+ let users = loadUsers(search);
202
+
203
+ function setSearch(next: string) {
204
+ search = next;
205
+ const previous = users;
206
+ users = loadUsers(search);
207
+ previous.abort();
208
+ }
209
+ ```
210
+
211
+ Because `users` is ordinary component `let` state, the existing compiler
212
+ updates its consumers. This is explicit resource ownership, not `$fetch`
213
+ recognition.
214
+
215
+ ### Manual refresh
216
+
217
+ ```ts
218
+ const latestUsers = await users.refresh();
219
+ ```
220
+
221
+ `refresh()` always performs a new request and resolves with its decoded result.
222
+ Existing data remains visible while it runs.
223
+
224
+ ### Abort
225
+
226
+ ```ts
227
+ users.abort();
228
+ ```
229
+
230
+ Aborting detaches this resource from its active shared request. If it was the
231
+ last consumer, the underlying request is aborted. If another resource still
232
+ uses that request, the request continues for the other resource.
233
+
234
+ Cancellation is not stored as `resource.error`.
235
+
236
+ Cancellation is also a client-side settlement boundary. Even when an injected
237
+ fetch implementation ignores `AbortSignal`, an aborted resource or action will
238
+ not accept its late result.
239
+
240
+ ### Replacing data
241
+
242
+ `update()` requires the callback to return the new top-level value:
243
+
244
+ ```ts
245
+ users.update(current => [
246
+ ...(current ?? []),
247
+ newUser,
248
+ ]);
249
+ ```
250
+
251
+ ### Direct mutation
252
+
253
+ `mutate()` ignores the callback's result and preserves the existing top-level
254
+ value:
255
+
256
+ ```ts
257
+ users.mutate(current => {
258
+ current?.push(newUser);
259
+ });
260
+ ```
261
+
262
+ These are deliberately separate. `Array.push()` returns a number, so an API
263
+ that treats callback returns as optional replacements could accidentally store
264
+ that number instead of the array.
265
+
266
+ Use `mutate()` for direct in-place changes and `update()` when a replacement is
267
+ required.
268
+
269
+ Both operations supersede a read that was already in flight. The older request
270
+ is aborted, and its result is ignored even when an injected fetch implementation
271
+ does not cooperate with `AbortSignal`. This prevents a stale server snapshot
272
+ from overwriting a newer local or optimistic write.
273
+
274
+ ## Resource sharing
275
+
276
+ The default sharing mode is active sharing:
277
+
278
+ ```ts
279
+ const first = $fetch<User[]>('/api/users');
280
+ const second = $fetch<User[]>('/api/users');
281
+ ```
282
+
283
+ When both declarations describe the same normalized request:
284
+
285
+ 1. The first resource starts the request.
286
+ 2. The second resource joins that request instead of sending another one.
287
+ 3. Both resources observe the same decoded value.
288
+ 4. A third resource created while either remains active receives that value
289
+ immediately without another request.
290
+ 5. After the final resource is disposed by the future integration layer, the
291
+ entry is removed.
292
+ 6. A later resource performs a new request.
293
+
294
+ The identity includes the normalized URL, query, headers, and validator. An
295
+ explicit key can replace automatic identity when necessary:
296
+
297
+ ```ts
298
+ const profile = $fetch<Profile>('/api/profile', {
299
+ key: ['profile', accountId],
300
+ });
301
+ ```
302
+
303
+ ### Sharing options
304
+
305
+ The current package implements only three clear modes:
306
+
307
+ ```ts
308
+ // Default: share while at least one resource is active.
309
+ $fetch<User[]>('/api/users');
310
+
311
+ // Private: never share this resource's request or result.
312
+ $fetch<User[]>('/api/users', {
313
+ cache: false,
314
+ });
315
+
316
+ // Retain for this package instance until its internal store is cleared.
317
+ $fetch<User[]>('/api/users', {
318
+ cache: { scope: 'app' },
319
+ });
320
+ ```
321
+
322
+ No freshness or retention duration is implemented in version `0.0.1`. Timing
323
+ options remain a design discussion and are not part of the package API.
324
+
325
+ This sharing store is separate from the browser HTTP cache, service workers,
326
+ and server `Cache-Control` headers.
327
+
328
+ ## Optional response validation
329
+
330
+ This short form trusts the developer's generic assertion:
331
+
332
+ ```ts
333
+ const users = $fetch<User[]>('/api/users');
334
+ ```
335
+
336
+ For an external or untrusted API, a Standard Schema validator can infer and
337
+ check the decoded response:
338
+
339
+ ```ts
340
+ const UserSchema = z.object({
341
+ id: z.string(),
342
+ name: z.string(),
343
+ });
344
+
345
+ const users = $fetch('/external/users', {
346
+ validate: z.array(UserSchema),
347
+ });
348
+ ```
349
+
350
+ Here `users.data` is inferred as `User[] | undefined`. If the response does not
351
+ match the schema, invalid data is not stored and `users.error.kind` is
352
+ `'validation'`.
353
+
354
+ `validate` is optional. It does not replace the generic form and is not required
355
+ for application-owned endpoints.
356
+
357
+ ## Destructuring
358
+
359
+ In the standalone package, normal JavaScript destructuring is a snapshot:
360
+
361
+ ```ts
362
+ const { data, pending } = users;
363
+ ```
364
+
365
+ Those two variables do not change by themselves because this package does not
366
+ rewrite JavaScript. The current compiler's opaque frame fallback also cannot
367
+ replay a value that was copied out once. Keep the resource object when values
368
+ must be read later:
369
+
370
+ ```tsx
371
+ const users = $fetch<User[]>('/api/users');
372
+
373
+ function render() {
374
+ return users.pending ? 'Loading' : users.data;
375
+ }
376
+ ```
377
+
378
+ Direct resource getters used by JSX are reread while the component is mounted.
379
+ Live destructuring could be added later, but it is not required for `$fetch` to
380
+ render correctly and must not depend on package or method names.
381
+
382
+ Methods are bound functions and are safe to extract:
383
+
384
+ ```ts
385
+ const { refresh } = users;
386
+ await refresh();
387
+ ```
388
+
389
+ ## `$action`
390
+
391
+ ### Creating and invoking an action
392
+
393
+ ```ts
394
+ interface Todo {
395
+ id: string;
396
+ title: string;
397
+ }
398
+
399
+ interface CreateTodo {
400
+ title: string;
401
+ }
402
+
403
+ const createTodo = $action<Todo, CreateTodo>('/api/todos', {
404
+ method: 'POST',
405
+ });
406
+
407
+ const created = await createTodo({
408
+ title: 'Write documentation',
409
+ });
410
+ ```
411
+
412
+ Creating the action sends no request. Calling it performs one invocation and
413
+ returns that invocation's promise.
414
+
415
+ Plain object and array inputs are encoded as JSON. Strings, `FormData`, blobs,
416
+ URL search parameters, array buffers, and other supported native request bodies
417
+ are passed through without JSON conversion.
418
+
419
+ The default method is `POST`. Supported methods are `POST`, `PUT`, `PATCH`, and
420
+ `DELETE`.
421
+
422
+ ### Action state
423
+
424
+ ```ts
425
+ createTodo.data; // Todo | undefined
426
+ createTodo.error; // RequestError | null
427
+ createTodo.status; // 'idle' | 'pending' | 'success' | 'error'
428
+ createTodo.pending; // boolean
429
+ ```
430
+
431
+ Every invocation receives its own promise. The initial implementation permits
432
+ parallel calls. Visible `data`, `error`, and `status` belong to the most recently
433
+ started invocation; an older request settling late cannot overwrite newer
434
+ visible state.
435
+
436
+ ```ts
437
+ createTodo.abort();
438
+ createTodo.reset();
439
+ ```
440
+
441
+ `abort()` stops active client requests. It cannot guarantee that a server did
442
+ not already process a mutation. `reset()` aborts active work and returns visible
443
+ state to `idle`. Late results from fetch implementations that ignore abort are
444
+ discarded. With parallel calls, aborting one invocation does not allow it to
445
+ overwrite the state of a newer invocation.
446
+
447
+ ### Success and error callbacks
448
+
449
+ ```ts
450
+ const createTodo = $action<Todo, CreateTodo>('/api/todos', {
451
+ onSuccess(created, input) {
452
+ console.log('Created', created.id, 'from', input.title);
453
+ },
454
+
455
+ onError(error, input) {
456
+ console.error('Could not create', input.title, error);
457
+ },
458
+ });
459
+ ```
460
+
461
+ Normal `try`/`catch` around the returned invocation promise remains valid and
462
+ does not require callbacks.
463
+
464
+ ## Optimistic collection changes
465
+
466
+ An array resource exposes three typed change constructors:
467
+
468
+ ```ts
469
+ todos.append(temporary);
470
+ todos.replace(current, temporary);
471
+ todos.remove(current);
472
+ ```
473
+
474
+ They apply immediately and return an opaque optimistic change consumed by an
475
+ action invocation. Each change is single-use; passing the same change to a
476
+ second invocation throws instead of committing or rolling it back twice.
477
+
478
+ ### Create
479
+
480
+ ```ts
481
+ const created = await createTodo(input, {
482
+ optimistic: todos.append(temporary),
483
+ });
484
+ ```
485
+
486
+ - `temporary` appears immediately.
487
+ - Failure removes only that temporary item.
488
+ - Success replaces that exact item with `created`, the action result.
489
+ - The list is not fetched again.
490
+
491
+ ### Update
492
+
493
+ ```ts
494
+ const saved = await updateTodo(input, {
495
+ optimistic: todos.replace(existing, optimisticVersion),
496
+ });
497
+ ```
498
+
499
+ - `existing` is replaced immediately.
500
+ - Failure restores `existing`.
501
+ - Success installs `saved` in place of `optimisticVersion`.
502
+
503
+ `existing` should be the actual object reference obtained from `todos.data`.
504
+
505
+ ### Delete
506
+
507
+ ```ts
508
+ await deleteTodo(existing.id, {
509
+ optimistic: todos.remove<void>(existing),
510
+ });
511
+ ```
512
+
513
+ - `existing` is removed immediately.
514
+ - Failure reinserts it at its previous position.
515
+ - Success keeps it removed.
516
+
517
+ If `existing` is not present, `replace()` and `remove()` return safe no-op
518
+ changes. Duplicate object references are handled one occurrence at a time.
519
+
520
+ Rollback operations target their own temporary/current item instead of
521
+ restoring a complete old array. A failed older action therefore does not erase
522
+ unrelated later additions.
523
+
524
+ ## Optional related-resource refresh
525
+
526
+ Creating an item normally returns that item, so the optimistic list should
527
+ commit from the result rather than refetch itself:
528
+
529
+ ```ts
530
+ await createTodo(input, {
531
+ optimistic: todos.append(temporary),
532
+ });
533
+ ```
534
+
535
+ Refresh is only for another resource whose authoritative value cannot be
536
+ derived from the returned item:
537
+
538
+ ```ts
539
+ await createTodo(input, {
540
+ optimistic: todos.append(temporary),
541
+ refresh: [todoStatistics],
542
+ });
543
+ ```
544
+
545
+ The action result reconciles `todos`. Only `todoStatistics` is requested again.
546
+ Refresh requests start after the action succeeds and do not delay the action's
547
+ returned result.
548
+
549
+ ## Errors
550
+
551
+ ```ts
552
+ class RequestError<TData = unknown> extends Error {
553
+ readonly kind: 'network' | 'http' | 'decode' | 'validation';
554
+ readonly status: number | null;
555
+ readonly statusText: string | null;
556
+ readonly data: TData | undefined;
557
+ readonly issues: readonly StandardSchemaIssue[] | undefined;
558
+ }
559
+ ```
560
+
561
+ Automatic `$fetch` requests store failures in `resource.error` without causing
562
+ an unhandled rejection. Awaited `refresh()` and action calls reject normally.
563
+
564
+ ## Compiler behavior and optional future optimization
565
+
566
+ The current compiler needs no data-specific integration. An imported resource
567
+ whose getters participate in rendered output is an opaque value, so its owner
568
+ is marked volatile and reevaluated once per visible animation frame. This also
569
+ supports structural output such as loading branches and
570
+ `resource.data?.map(...)` lists. Polling stops when the owner is unmounted.
571
+
572
+ This is the same compatibility path used for animation engines, external
573
+ stores, and other third-party objects. Those libraries do not need to implement
574
+ a framework interface.
575
+
576
+ The package also exposes subscribe, immutable-snapshot, and dispose hooks from
577
+ `@memoized-dom/data/internal`. Generated code does not use them today. They are
578
+ available if measurements later justify an optional push optimization:
579
+
580
+ - notify only when resource/action state changes instead of pulling per frame;
581
+ - provide more precise invalidation;
582
+ - automate resource ownership and cleanup;
583
+ - recreate a resource when compiled request arguments change.
584
+
585
+ That optimization must preserve the opaque fallback. It must not make a
586
+ special interface mandatory for third-party libraries, recognize `$fetch` by
587
+ name, or approve mutation methods from a list. Direct writes to derived values
588
+ remain illegal; opaque receiver calls retain ordinary JavaScript semantics.
589
+
590
+ The complete evolving design and deferred server behavior live in the root
591
+ `data-loading-api.md` document.
@@ -0,0 +1,44 @@
1
+ import { SnapshotNotifier } from './notifications';
2
+ import type { FetchEnvironment } from './resource';
3
+ import type { Action, ActionListener, ActionOptions, ActionSnapshot } from './types';
4
+ interface MutableActionSnapshot<T> {
5
+ data: T | undefined;
6
+ error: import('./errors').RequestError | null;
7
+ status: ActionSnapshot<T>['status'];
8
+ pending: boolean;
9
+ }
10
+ interface ActionInvocation {
11
+ readonly abortController: AbortController;
12
+ readonly sequence: number;
13
+ cancelled: boolean;
14
+ }
15
+ declare class ActionController<T> {
16
+ readonly store: ActionStore;
17
+ readonly invocations: Set<ActionInvocation>;
18
+ readonly notifier: SnapshotNotifier<ActionSnapshot<T>>;
19
+ snapshot: MutableActionSnapshot<T>;
20
+ sequence: number;
21
+ disposed: boolean;
22
+ hasData: boolean;
23
+ constructor(store: ActionStore);
24
+ begin(invocation: ActionInvocation): void;
25
+ isVisible(invocation: ActionInvocation): boolean;
26
+ succeed(invocation: ActionInvocation, result: T): void;
27
+ fail(invocation: ActionInvocation, error: import('./errors').RequestError): void;
28
+ cancelInvocation(invocation: ActionInvocation, reason?: unknown): void;
29
+ finish(invocation: ActionInvocation): void;
30
+ abort(reset: boolean): void;
31
+ dispose(): void;
32
+ }
33
+ export declare class ActionStore {
34
+ private readonly active;
35
+ track(controller: ActionController<unknown>): void;
36
+ untrack(controller: ActionController<unknown>): void;
37
+ clear(): void;
38
+ }
39
+ export declare function createAction<TResult, TInput>(environment: FetchEnvironment, store: ActionStore, target: string | URL, options: ActionOptions<TResult, TInput>): Action<TResult, TInput>;
40
+ export declare function subscribeAction<T, TInput>(action: Action<T, TInput>, listener: ActionListener<T>): () => void;
41
+ export declare function actionSnapshot<T, TInput>(action: Action<T, TInput>): ActionSnapshot<T>;
42
+ export declare function disposeAction<T, TInput>(action: Action<T, TInput>): void;
43
+ export {};
44
+ //# sourceMappingURL=action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AASnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EACV,MAAM,EAEN,cAAc,EACd,aAAa,EACb,cAAc,EACf,MAAM,SAAS,CAAC;AAEjB,UAAU,qBAAqB,CAAC,CAAC;IAC/B,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,KAAK,EAAE,OAAO,UAAU,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9C,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,gBAAgB;IACxB,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAMD,cAAM,gBAAgB,CAAC,CAAC;IAaV,QAAQ,CAAC,KAAK,EAAE,WAAW;IAZvC,QAAQ,CAAC,WAAW,wBAA+B;IACnD,QAAQ,CAAC,QAAQ,sCAAuD;IACxE,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAKhC;IACF,QAAQ,SAAK;IACb,QAAQ,UAAS;IACjB,OAAO,UAAS;gBAEK,KAAK,EAAE,WAAW;IAEvC,KAAK,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI;IAWzC,SAAS,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO;IAIhD,OAAO,CAAC,UAAU,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI;IAStD,IAAI,CACF,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,OAAO,UAAU,EAAE,YAAY,GACrC,IAAI;IAOP,gBAAgB,CAAC,UAAU,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI;IAiBtE,MAAM,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI;IAY1C,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAkB3B,OAAO,IAAI,IAAI;CAOhB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAE/D,KAAK,CAAC,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,IAAI;IAIlD,OAAO,CAAC,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,IAAI;IAIpD,KAAK,IAAI,IAAI;CAGd;AAaD,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAC1C,WAAW,EAAE,gBAAgB,EAC7B,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,MAAM,GAAG,GAAG,EACpB,OAAO,EAAE,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,GACtC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CA+GzB;AAUD,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EACvC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,EACzB,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,GAC1B,MAAM,IAAI,CAIZ;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EACtC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GACxB,cAAc,CAAC,CAAC,CAAC,CAInB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EACrC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GACxB,IAAI,CAEN"}
@@ -0,0 +1 @@
1
+ var h=class extends Error{kind;status;statusText;data;issues;constructor(e,n){super(e,{cause:n.cause}),this.name="RequestError",this.kind=n.kind,this.status=n.status??null,this.statusText=n.statusText??null,this.data=n.data,this.issues=n.issues}};function T(t){return(t instanceof DOMException||t instanceof Error)&&t.name==="AbortError"}function v(t){return t instanceof h?t:new h(t instanceof Error?t.message:"Network request failed",{kind:"network",cause:t})}function ne(t){if(t.length===0)return;let e=t.length===1?t[0]:new AggregateError(t,"Data runtime listeners failed"),n=globalThis.reportError;try{n?.(e)}catch{}}var g=class{constructor(e){this.current=e}current;listeners=new Set;emitting=!1;pending=!1;notify(){if(this.emitting){this.pending=!0;return}this.emitting=!0;let e=[];try{do{this.pending=!1;let n=this.current();for(let s of[...this.listeners])if(this.listeners.has(s)){try{s(n)}catch(r){e.push(r)}if(this.pending)break}}while(this.pending)}finally{this.emitting=!1}ne(e)}subscribe(e){this.listeners.add(e);try{e(this.current())}catch(n){throw this.listeners.delete(e),n}return()=>this.listeners.delete(e)}clear(){this.listeners.clear()}};var F=new WeakMap;function b(t){let e=Object.freeze({kind:"memoized-dom.optimistic-change"});return F.set(e,t),e}function U(t){let e=F.get(t);if(e===void 0)throw new TypeError("Invalid optimistic change. Use append(), replace(), or remove() on a fetch resource.");return F.delete(t),e}async function j(t,e){let n=await t["~standard"].validate(e);if(n.issues!==void 0)throw new h("Response validation failed",{kind:"validation",issues:n.issues,data:e});return n.value}var P=new WeakMap,se=1;function re(t){if(t===void 0)return"none";let e=P.get(t);return e===void 0&&(e=se++,P.set(t,e)),String(e)}function oe(t,e){let n=t.indexOf("#"),s=n===-1?t:t.slice(0,n),r=s.indexOf("?"),o=r===-1?s:s.slice(0,r),i=new URLSearchParams(r===-1?"":s.slice(r+1));if(e!==void 0)for(let c of Object.keys(e).sort()){let u=e[c];if(u!==void 0)if(i.delete(c),Array.isArray(u))for(let f of u)i.append(c,String(f));else i.append(c,String(u))}i.sort();let a=i.toString();return`${o}${a===""?"":`?${a}`}`}function R(t,e,n){let s=t instanceof URL?t.href:t,r=n===void 0?s:new URL(s,n).href;return oe(r,e)}function ie(t){return[...new Headers(t).entries()].sort(([n],[s])=>n.localeCompare(s)).map(([n,s])=>`${encodeURIComponent(n)}=${encodeURIComponent(s)}`).join("&")}function ae(t){return(typeof t=="string"?[t]:t).map(n=>`${typeof n}:${encodeURIComponent(String(n))}`).join("|")}function V(t,e,n,s){return n!==void 0?`explicit|${ae(n)}`:`GET|${t}|${ie(e)}|schema:${re(s)}`}async function k(t,e){let n;try{t.status===204||t.status===205?n=void 0:n=(t.headers.get("content-type")?.toLowerCase()??"").includes("json")?await t.json():await t.text()}catch(s){throw T(s)?s:t.ok?new h("Failed to decode response",{kind:"decode",status:t.status,statusText:t.statusText,cause:s}):new h(`Request failed with status ${t.status}`,{kind:"http",status:t.status,statusText:t.statusText,cause:s})}if(!t.ok)throw new h(`Request failed with status ${t.status}`,{kind:"http",status:t.status,statusText:t.statusText,data:n});return e===void 0?n:j(e,n)}function l(t){return t.reason??new DOMException("The operation was aborted","AbortError")}function m(t,e){return e.aborted?Promise.reject(l(e)):new Promise((n,s)=>{let r=()=>s(l(e));e.addEventListener("abort",r,{once:!0}),Promise.resolve().then(t).then(n,s).finally(()=>{e.removeEventListener("abort",r)})})}function M(t,e){if(t!==void 0)return typeof t=="string"||t instanceof Blob||t instanceof FormData||t instanceof URLSearchParams||t instanceof ArrayBuffer||ArrayBuffer.isView(t)?t:(e.has("content-type")||e.set("content-type","application/json"),JSON.stringify(t))}function H(t){return Object.freeze({...t})}var I=class{constructor(e){this.store=e}store;invocations=new Set;notifier=new g(()=>H(this.snapshot));snapshot={data:void 0,error:null,status:"idle",pending:!1};sequence=0;disposed=!1;hasData=!1;begin(e){this.invocations.size===0&&this.store.track(this),this.invocations.add(e),this.snapshot.error=null,this.snapshot.status="pending",this.snapshot.pending=!0,this.notifier.notify()}isVisible(e){return!e.cancelled&&e.sequence===this.sequence}succeed(e,n){this.isVisible(e)&&(this.snapshot.data=n,this.snapshot.error=null,this.snapshot.status="success",this.hasData=!0,this.notifier.notify())}fail(e,n){this.isVisible(e)&&(this.snapshot.error=n,this.snapshot.status="error",this.notifier.notify())}cancelInvocation(e,n){if(e.cancelled)return;let s=this.isVisible(e);e.cancelled=!0,e.abortController.abort(n),s&&(this.sequence++,this.snapshot.error=null,this.snapshot.status=this.hasData?"success":"idle");let r=[...this.invocations].some(o=>!o.cancelled);(s||this.snapshot.pending!==r)&&(this.snapshot.pending=r,this.notifier.notify())}finish(e){this.invocations.delete(e),this.invocations.size===0&&this.store.untrack(this);let n=[...this.invocations].some(s=>!s.cancelled);this.snapshot.pending!==n&&(this.snapshot.pending=n,this.notifier.notify())}abort(e){this.sequence++;for(let n of this.invocations)n.cancelled=!0,n.abortController.abort();this.snapshot.error=null,this.snapshot.pending=!1,e?(this.snapshot.data=void 0,this.snapshot.status="idle",this.hasData=!1):this.snapshot.status=this.hasData?"success":"idle",this.notifier.notify()}dispose(){this.disposed||(this.abort(!0),this.disposed=!0,this.notifier.clear(),this.store.untrack(this))}},S=class{active=new Set;track(e){this.active.add(e)}untrack(e){this.active.delete(e)}clear(){for(let e of[...this.active])e.abort(!0)}},$=new WeakMap;function ce(t,e){return t===void 0?()=>{}:(t.addEventListener("abort",e,{once:!0}),()=>t.removeEventListener("abort",e))}function z(t,e,n,s){let r=new I(e),o=R(n,s.query,t.baseURL),a=async(c,u={})=>{let f=u.optimistic===void 0?void 0:U(u.optimistic);if(r.disposed)throw f?.rollback(),new Error("Cannot invoke a disposed action");if(u.signal?.aborted)throw f?.rollback(),l(u.signal);let d={sequence:++r.sequence,abortController:new AbortController,cancelled:!1};r.begin(d);let _=ce(u.signal,()=>r.cancelInvocation(d,l(u.signal))),x=!1;try{let p=new Headers(s.headers),y=M(c,p),ee=await m(()=>t.fetch()(o,{method:s.method??"POST",headers:p,body:y,signal:d.abortController.signal}),d.abortController.signal),w=await m(()=>k(ee,s.validate),d.abortController.signal);if(d.cancelled)throw l(d.abortController.signal);if(f?.commit(w),x=!0,r.succeed(d,w),await s.onSuccess?.(w,c),d.cancelled)throw l(d.abortController.signal);for(let te of u.refresh??[])te.refresh().catch(()=>{});return w}catch(p){if(x||f?.rollback(),d.cancelled||T(p))throw r.cancelInvocation(d),p;if(x)throw p;let y=v(p);r.fail(d,y);try{await s.onError?.(y,c)}catch{}throw y}finally{_(),r.finish(d)}};return Object.defineProperties(a,{data:{get:()=>r.snapshot.data},error:{get:()=>r.snapshot.error},status:{get:()=>r.snapshot.status},pending:{get:()=>r.snapshot.pending}}),a.abort=()=>{if(r.disposed)throw new Error("Cannot abort a disposed action");r.abort(!1)},a.reset=()=>{if(r.disposed)throw new Error("Cannot reset a disposed action");r.abort(!0)},$.set(a,r),a}function O(t){let e=$.get(t);if(e===void 0)throw new TypeError("Value is not an action from @memoized-dom/data");return e}function Se(t,e){let n=O(t);if(n.disposed)throw new Error("Cannot subscribe to a disposed action");return n.notifier.subscribe(e)}function Ce(t){return H(O(t).snapshot)}function Ee(t){O(t).dispose()}var q=()=>({data:void 0,error:null,status:"idle",pending:!1,refreshing:!1});function Q(t){return Object.freeze({...t})}function B(t){return t??"active"}var C=class{constructor(e,n){this.store=e;this.descriptor=n;this.cache=n.cache===!1?"active":n.cache}store;descriptor;consumers=new Set;snapshot=q();hasData=!1;cache;request=null;controller=null;generation=0;add(e){this.consumers.add(e),e.receive(this,this.snapshot)}remove(e,n){this.consumers.delete(e),this.consumers.size===0&&(this.cache==="active"?this.store.delete(this,n):this.cancelRequest(n))}cancelRequest(e){this.controller===null&&this.request===null||(this.generation++,this.controller?.abort(e),this.controller=null,this.request=null,this.snapshot.error=null,this.snapshot.pending=!1,this.snapshot.refreshing=!1,this.snapshot.status=this.hasData?"success":"idle")}upgradeCache(e){typeof e=="object"&&(this.cache=e)}emit(){for(let e of this.consumers)e.receive(this,this.snapshot)}update(e){let n=this.snapshot.data;this.snapshot.data=e(n),this.snapshot.error=null,this.snapshot.status="success",this.hasData=!0,this.emit()}mutate(e){let n=this.snapshot.data;e(n),this.snapshot.data=n,this.snapshot.error=null,this.snapshot.status="success",this.hasData=!0,this.emit()}start(e=!1){if(this.request!==null)return this.request;if(!e&&this.hasData)return Promise.resolve(this.snapshot.data);let n=++this.generation,s=new AbortController;this.controller=s,this.snapshot.error=null,this.snapshot.pending=!0,this.snapshot.refreshing=this.hasData,this.snapshot.status=this.hasData?"success":"pending",this.emit();let r=m(()=>this.store.environment.fetch()(this.descriptor.url,{method:"GET",headers:this.descriptor.headers,signal:s.signal}),s.signal).then(o=>m(()=>k(o,this.descriptor.schema),s.signal)).then(o=>(n!==this.generation||(this.snapshot={data:o,error:null,status:"success",pending:!1,refreshing:!1},this.hasData=!0,this.emit()),o),o=>{throw n!==this.generation?o:s.signal.aborted||T(o)?(this.snapshot.pending=!1,this.snapshot.refreshing=!1,this.snapshot.status=this.hasData?"success":"idle",this.emit(),o):(this.snapshot.error=v(o),this.snapshot.pending=!1,this.snapshot.refreshing=!1,this.snapshot.status=this.hasData?"success":"error",this.emit(),this.snapshot.error)}).finally(()=>{n===this.generation&&(this.request=null,this.controller=null)});return this.request=r,r.catch(()=>{}),r}dispose(e=!1,n){this.cancelRequest(n);for(let s of[...this.consumers])s.detachFrom(this,e);this.consumers.clear()}},E=class{constructor(e){this.environment=e}environment;entries=new Map;allEntries=new Set;acquire(e,n,s=!1){let r;if(e.cache===!1)r=new C(this,e),this.allEntries.add(r);else{let i=this.entries.get(e.identity);i===void 0?(r=new C(this,e),this.allEntries.add(r)):r=i,this.entries.set(e.identity,r),r.upgradeCache(e.cache)}return r.add(n),(s||r.snapshot.status==="idle"||r.snapshot.status==="error")&&r.start(s).catch(()=>{}),r}delete(e,n){this.entries.get(e.descriptor.identity)===e&&this.entries.delete(e.descriptor.identity),this.allEntries.delete(e),e.dispose(!1,n)}clear(){for(let e of[...this.allEntries])e.dispose(!0);this.entries.clear(),this.allEntries.clear()}},A=class{constructor(e,n,s=!1){this.store=e;this.descriptor=n;this.paused=s;let r=n.signal;if(r!==void 0){let o=()=>this.abort(l(r));if(r.aborted)return;r.addEventListener("abort",o,{once:!0}),this.removeSignalListener=()=>r.removeEventListener("abort",o)}s||this.attach(!1)}store;descriptor;paused;snapshot=q();entry=null;disposed=!1;removeSignalListener=null;notifier=new g(()=>Q(this.snapshot));attach(e){if(this.disposed)throw new Error("Cannot refresh a disposed fetch resource");this.entry=this.store.acquire(this.descriptor,this,e)}receive(e,n){this.entry!==null&&this.entry!==e||(this.snapshot={...n},this.notify())}detachFrom(e,n=!1){this.entry===e&&(this.entry=null,n&&(this.snapshot=q(),this.notify()))}notify(){this.notifier.notify()}refresh(){if(this.disposed)return Promise.reject(new Error("Cannot refresh a disposed fetch resource"));if(this.paused)return Promise.reject(new TypeError("Cannot refresh a fetch resource with a null target"));let e=this.descriptor.signal;if(e?.aborted)return Promise.reject(l(e));this.entry===null&&this.attach(!0);let n=this.entry.start(!0);return e===void 0?n:m(()=>n,e)}abort(e){if(this.disposed)throw new Error("Cannot abort a disposed fetch resource");this.detach(!0,e)}detach(e,n){let s=this.entry;s!==null&&(this.entry=null,s.remove(this,n)),this.snapshot.pending=!1,this.snapshot.refreshing=!1,this.snapshot.status=this.snapshot.status==="success"?"success":"idle",this.snapshot.error=null,e&&this.notify()}update(e){if(this.disposed)throw new Error("Cannot update a disposed fetch resource");if(this.entry===null){let n=this.snapshot.data;this.snapshot.data=e(n),this.snapshot.status="success",this.snapshot.error=null,this.notify();return}this.entry.update(e)}mutate(e){if(this.disposed)throw new Error("Cannot mutate a disposed fetch resource");if(this.entry===null){e(this.snapshot.data),this.snapshot.status="success",this.snapshot.error=null,this.notify();return}this.entry.mutate(e)}dispose(){this.disposed||(this.detach(!1),this.disposed=!0,this.removeSignalListener?.(),this.removeSignalListener=null,this.notifier.clear())}},G=new WeakMap;function L(t){return Array.isArray(t.snapshot.data)?t.snapshot.data:[]}var K=new WeakMap;function J(t){let e=K.get(t);return e===void 0&&(e=new Set,K.set(t,e)),e}function X(t,e,n){if(Object.is(t[e.index],n))return e.index;let s=-1;for(let r=0;r<t.length;r++)if(Object.is(t[r],n)){if(s!==-1)return-1;s=r}return s}function W(t,e,n,s,r){n.outcome=s,n.result=r;let o=e.base;for(let a of e.steps)a.outcome==="pending"?o=a.temporary:a.outcome==="committed"&&(o=a.result);let i=e.visible;e.visible=o,Object.is(i,o)||t.update(a=>{let c=[...a??[]],u=X(c,e,i);return u!==-1&&(c[u]=o),c}),e.steps.every(a=>a.outcome!=="pending")&&J(t).delete(e)}function ue(t,e){let n=L(t).length;return t.update(s=>[...s??[],e]),b({rollback(){t.update(s=>{let r=[...s??[]],o=r[n]===e?n:r.lastIndexOf(e);return o!==-1&&r.splice(o,1),r})},commit(s){t.update(r=>{let o=[...r??[]],i=o[n]===e?n:o.lastIndexOf(e);return i!==-1&&(o[i]=s),o})}})}function de(t,e,n){let s=L(t),r=s.indexOf(e);if(r===-1)return b({rollback(){},commit(){}});let o=J(t),i=[...o].find(c=>Object.is(c.visible,e)&&X(s,c,e)===r);i===void 0&&(i={index:r,base:e,visible:e,steps:[]},o.add(i));let a={temporary:n,outcome:"pending"};return i.steps.push(a),i.visible=n,t.update(c=>(c??[]).map((u,f)=>f===r?n:u)),b({rollback(){W(t,i,a,"rolled-back")},commit(c){W(t,i,a,"committed",c)}})}function he(t,e){let n=L(t).indexOf(e);return n===-1?b({rollback(){},commit(){}}):(t.update(s=>{let r=[...s??[]];return r[n]===e&&r.splice(n,1),r}),b({rollback(){t.update(s=>{let r=[...s??[]];return r.splice(Math.min(n,r.length),0,e),r})},commit(){}}))}function Y(t,e,n,s){if(n===null)return N(new A(t,{url:"",headers:s.headers,identity:"paused",cache:B(s.cache),schema:s.validate,signal:s.signal},!0));let r=R(n,s.query,e.baseURL),o=new Headers(s.headers),i={url:r,headers:o,identity:V(r,o,s.key,s.validate),cache:B(s.cache),schema:s.validate,signal:s.signal};return N(new A(t,i))}function N(t){let e=t,n={get data(){return t.snapshot.data},get error(){return t.snapshot.error},get status(){return t.snapshot.status},get pending(){return t.snapshot.pending},get refreshing(){return t.snapshot.refreshing},refresh:()=>t.refresh(),abort:()=>t.abort(),update:s=>t.update(s),mutate:s=>t.mutate(s),append:s=>ue(e,s),replace:(s,r)=>de(e,s,r),remove:s=>he(e,s)};return G.set(n,t),n}function D(t){let e=G.get(t);if(e===void 0)throw new TypeError("Value is not a fetch resource from @memoized-dom/data");return e}function qe(t,e){let n=D(t);if(n.disposed)throw new Error("Cannot subscribe to a disposed fetch resource");return n.notifier.subscribe(e)}function Le(t){D(t).dispose()}function De(t){return Q(D(t).snapshot)}function Z(t,e){return{baseURL:e??(typeof location>"u"?void 0:location.href),fetch(){let s=t??globalThis.fetch;if(s===void 0)throw new Error("No fetch implementation is available");return s}}}function Ve(t={}){let e=Z(t.fetch,t.baseURL),n=new E(e),s=new S;return{$fetch:((i,a={})=>Y(n,e,i,a)),$action:((i,a={})=>z(e,s,i,a)),clear(){s.clear(),n.clear()}}}export{h as a,Se as b,Ce as c,Ee as d,qe as e,Le as f,De as g,Ve as h};
@@ -0,0 +1,4 @@
1
+ import type { DataRuntime, DataRuntimeOptions } from './types';
2
+ /** Create an isolated request/cache/action ownership boundary. */
3
+ export declare function createDataRuntime(options?: DataRuntimeOptions): DataRuntime;
4
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,WAAW,EACX,kBAAkB,EAInB,MAAM,SAAS,CAAC;AAEjB,kEAAkE;AAClE,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,kBAAuB,GAC/B,WAAW,CAmCb"}
@@ -0,0 +1,21 @@
1
+ import type { StandardSchemaIssue } from './types';
2
+ export type RequestErrorKind = 'network' | 'http' | 'decode' | 'validation';
3
+ export interface RequestErrorOptions<TData> {
4
+ readonly kind: RequestErrorKind;
5
+ readonly status?: number | null;
6
+ readonly statusText?: string | null;
7
+ readonly data?: TData;
8
+ readonly issues?: readonly StandardSchemaIssue[];
9
+ readonly cause?: unknown;
10
+ }
11
+ export declare class RequestError<TData = unknown> extends Error {
12
+ readonly kind: RequestErrorKind;
13
+ readonly status: number | null;
14
+ readonly statusText: string | null;
15
+ readonly data: TData | undefined;
16
+ readonly issues: readonly StandardSchemaIssue[] | undefined;
17
+ constructor(message: string, options: RequestErrorOptions<TData>);
18
+ }
19
+ export declare function isAbortError(error: unknown): boolean;
20
+ export declare function toRequestError(error: unknown): RequestError;
21
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,MAAM,GACN,QAAQ,GACR,YAAY,CAAC;AAEjB,MAAM,WAAW,mBAAmB,CAAC,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,qBAAa,YAAY,CAAC,KAAK,GAAG,OAAO,CAAE,SAAQ,KAAK;IACtD,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,GAAG,SAAS,CAAC;gBAEhD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,CAAC,KAAK,CAAC;CASjE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAMpD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAM3D"}
@@ -0,0 +1,8 @@
1
+ export { createDataRuntime } from './client';
2
+ export declare const $fetch: import("./types").FetchFunction;
3
+ export declare const $action: import("./types").ActionFunction;
4
+ export declare const clearDataRuntime: () => void;
5
+ export { RequestError } from './errors';
6
+ export type { Action, ActionCallOptions, ActionFunction, ActionMethod, ActionOptions, AppCacheOptions, AsyncStatus, DataRuntime, DataRuntimeOptions, FetchCache, FetchCollectionChanges, FetchFunction, FetchOptions, FetchResource, FetchResourceCore, InferSchemaOutput, OptimisticChange, Query, QueryPrimitive, QueryValue, RequestKey, RequestKeyPart, StandardSchemaIssue, StandardSchemaResult, StandardSchemaV1, ValidatedFetchOptions, } from './types';
7
+ export type { RequestErrorKind, RequestErrorOptions } from './errors';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,eAAO,MAAM,MAAM,iCAAwB,CAAC;AAC5C,eAAO,MAAM,OAAO,kCAAyB,CAAC;AAC9C,eAAO,MAAM,gBAAgB,YAAuB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,YAAY,EACV,MAAM,EACN,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,eAAe,EACf,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,sBAAsB,EACtB,aAAa,EACb,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,EACL,cAAc,EACd,UAAU,EACV,UAAU,EACV,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{a as o,h as t}from"./chunks/chunk-2CT6VOBD.js";var e=t(),a=e.$fetch,c=e.$action,n=e.clear;export{c as $action,a as $fetch,o as RequestError,n as clearDataRuntime,t as createDataRuntime};
@@ -0,0 +1,5 @@
1
+ export { createDataRuntime } from './client';
2
+ export { actionSnapshot, disposeAction, subscribeAction, } from './action';
3
+ export { disposeFetchResource, fetchResourceSnapshot, subscribeFetchResource, } from './resource';
4
+ export type { ActionListener, ActionSnapshot, DataRuntime, DataRuntimeOptions, ResourceListener, ResourceSnapshot, } from './types';
5
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EACL,cAAc,EACd,aAAa,EACb,eAAe,GAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,SAAS,CAAC"}
@@ -0,0 +1 @@
1
+ import{b as e,c as t,d as o,e as s,f as r,g as c,h as i}from"./chunks/chunk-2CT6VOBD.js";export{t as actionSnapshot,i as createDataRuntime,o as disposeAction,r as disposeFetchResource,c as fetchResourceSnapshot,e as subscribeAction,s as subscribeFetchResource};
@@ -0,0 +1,14 @@
1
+ type Listener<T> = (value: T) => void;
2
+ /** Reentrancy-safe notification queue shared by resources and actions. */
3
+ export declare class SnapshotNotifier<T> {
4
+ private readonly current;
5
+ readonly listeners: Set<Listener<T>>;
6
+ private emitting;
7
+ private pending;
8
+ constructor(current: () => T);
9
+ notify(): void;
10
+ subscribe(listener: Listener<T>): () => void;
11
+ clear(): void;
12
+ }
13
+ export {};
14
+ //# sourceMappingURL=notifications.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAkBtC,0EAA0E;AAC1E,qBAAa,gBAAgB,CAAC,CAAC;IAKjB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,QAAQ,CAAC,SAAS,mBAA0B;IAC5C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEK,OAAO,EAAE,MAAM,CAAC;IAE7C,MAAM,IAAI,IAAI;IA4Bd,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAW5C,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,9 @@
1
+ import type { OptimisticChange } from './types';
2
+ interface OptimisticHandlers<TResult> {
3
+ readonly commit: (result: TResult) => void;
4
+ readonly rollback: () => void;
5
+ }
6
+ export declare function createOptimisticChange<TResult>(changeHandlers: OptimisticHandlers<TResult>): OptimisticChange<TResult>;
7
+ export declare function optimisticHandlers<TResult>(change: OptimisticChange<TResult>): OptimisticHandlers<TResult>;
8
+ export {};
9
+ //# sourceMappingURL=optimistic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimistic.d.ts","sourceRoot":"","sources":["../src/optimistic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,UAAU,kBAAkB,CAAC,OAAO;IAClC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;CAC/B;AAID,wBAAgB,sBAAsB,CAAC,OAAO,EAC5C,cAAc,EAAE,kBAAkB,CAAC,OAAO,CAAC,GAC1C,gBAAgB,CAAC,OAAO,CAAC,CAS3B;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EACxC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAChC,kBAAkB,CAAC,OAAO,CAAC,CAS7B"}
@@ -0,0 +1,9 @@
1
+ import type { Query, RequestKey, StandardSchemaV1 } from './types';
2
+ export declare function resolveRequestURL(target: string | URL, query: Query | undefined, baseURL: string | URL | undefined): string;
3
+ export declare function fetchIdentity(url: string, headers: HeadersInit | undefined, key: RequestKey | undefined, schema: StandardSchemaV1 | undefined): string;
4
+ export declare function decodeResponse(response: Response, schema: StandardSchemaV1 | undefined): Promise<unknown>;
5
+ export declare function abortReason(signal: AbortSignal): unknown;
6
+ /** Reject client interest immediately even when a custom fetcher ignores signals. */
7
+ export declare function abortable<T>(operation: () => PromiseLike<T> | T, signal: AbortSignal): Promise<T>;
8
+ export declare function encodeActionBody(input: unknown, headers: Headers): BodyInit | undefined;
9
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,KAAK,EACL,UAAU,EACV,gBAAgB,EACjB,MAAM,SAAS,CAAC;AA0CjB,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,GAAG,GAAG,EACpB,KAAK,EAAE,KAAK,GAAG,SAAS,EACxB,OAAO,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,GAChC,MAAM,CAIR;AAiBD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,WAAW,GAAG,SAAS,EAChC,GAAG,EAAE,UAAU,GAAG,SAAS,EAC3B,MAAM,EAAE,gBAAgB,GAAG,SAAS,GACnC,MAAM,CAGR;AAED,wBAAsB,cAAc,CAClC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,gBAAgB,GAAG,SAAS,GACnC,OAAO,CAAC,OAAO,CAAC,CA6ClB;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAExD;AAED,qFAAqF;AACrF,wBAAgB,SAAS,CAAC,CAAC,EACzB,SAAS,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,EACnC,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,CAAC,CAAC,CASZ;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,OAAO,GACf,QAAQ,GAAG,SAAS,CAiBtB"}
@@ -0,0 +1,81 @@
1
+ import { SnapshotNotifier } from './notifications';
2
+ import type { FetchCache, FetchOptions, FetchResource, ResourceListener, ResourceSnapshot, StandardSchemaV1 } from './types';
3
+ interface FetchDescriptor {
4
+ readonly url: string;
5
+ readonly headers: HeadersInit | undefined;
6
+ readonly identity: string;
7
+ readonly cache: FetchCache;
8
+ readonly schema: StandardSchemaV1 | undefined;
9
+ readonly signal: AbortSignal | undefined;
10
+ }
11
+ export interface FetchEnvironment {
12
+ readonly fetch: () => typeof globalThis.fetch;
13
+ readonly baseURL: string | URL | undefined;
14
+ }
15
+ interface MutableSnapshot<T> {
16
+ data: T | undefined;
17
+ error: import('./errors').RequestError | null;
18
+ status: ResourceSnapshot<T>['status'];
19
+ pending: boolean;
20
+ refreshing: boolean;
21
+ }
22
+ declare class FetchEntry {
23
+ readonly store: FetchStore;
24
+ readonly descriptor: FetchDescriptor;
25
+ readonly consumers: Set<ResourceController<unknown>>;
26
+ snapshot: MutableSnapshot<unknown>;
27
+ hasData: boolean;
28
+ cache: Exclude<FetchCache, false>;
29
+ request: Promise<unknown> | null;
30
+ controller: AbortController | null;
31
+ private generation;
32
+ constructor(store: FetchStore, descriptor: FetchDescriptor);
33
+ add(controller: ResourceController<unknown>): void;
34
+ remove(controller: ResourceController<unknown>, reason?: unknown): void;
35
+ private cancelRequest;
36
+ upgradeCache(cache: FetchCache): void;
37
+ emit(): void;
38
+ update<T>(change: (current: T | undefined) => T): void;
39
+ mutate<T>(change: (current: T | undefined) => void): void;
40
+ start(force?: boolean): Promise<unknown>;
41
+ dispose(resetConsumers?: boolean, reason?: unknown): void;
42
+ }
43
+ export declare class FetchStore {
44
+ readonly environment: FetchEnvironment;
45
+ readonly entries: Map<string, FetchEntry>;
46
+ readonly allEntries: Set<FetchEntry>;
47
+ constructor(environment: FetchEnvironment);
48
+ acquire(descriptor: FetchDescriptor, consumer: ResourceController<unknown>, force?: boolean): FetchEntry;
49
+ delete(entry: FetchEntry, reason?: unknown): void;
50
+ clear(): void;
51
+ }
52
+ declare class ResourceController<T> {
53
+ readonly store: FetchStore;
54
+ readonly descriptor: FetchDescriptor;
55
+ readonly paused: boolean;
56
+ snapshot: MutableSnapshot<T>;
57
+ entry: FetchEntry | null;
58
+ disposed: boolean;
59
+ private removeSignalListener;
60
+ readonly notifier: SnapshotNotifier<ResourceSnapshot<T>>;
61
+ constructor(store: FetchStore, descriptor: FetchDescriptor, paused?: boolean);
62
+ attach(force: boolean): void;
63
+ receive(entry: FetchEntry, snapshot: MutableSnapshot<unknown>): void;
64
+ detachFrom(entry: FetchEntry, reset?: boolean): void;
65
+ notify(): void;
66
+ refresh(): Promise<T>;
67
+ abort(reason?: unknown): void;
68
+ private detach;
69
+ update(change: (current: T | undefined) => T): void;
70
+ mutate(change: (current: T | undefined) => void): void;
71
+ dispose(): void;
72
+ }
73
+ export declare function createFetchResource<T>(store: FetchStore, environment: FetchEnvironment, target: string | URL | null, options: FetchOptions & {
74
+ readonly validate?: StandardSchemaV1;
75
+ }): FetchResource<T>;
76
+ export declare function subscribeFetchResource<T>(resource: FetchResource<T>, listener: ResourceListener<T>): () => void;
77
+ export declare function disposeFetchResource<T>(resource: FetchResource<T>): void;
78
+ export declare function fetchResourceSnapshot<T>(resource: FetchResource<T>): ResourceSnapshot<T>;
79
+ export declare function createFetchEnvironment(fetcher: typeof globalThis.fetch | undefined, baseURL: string | URL | undefined): FetchEnvironment;
80
+ export {};
81
+ //# sourceMappingURL=resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { InferSchemaOutput, StandardSchemaV1 } from './types';
2
+ export declare function validateValue<TSchema extends StandardSchemaV1>(schema: TSchema, value: unknown): Promise<InferSchemaOutput<TSchema>>;
3
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEnE,wBAAsB,aAAa,CAAC,OAAO,SAAS,gBAAgB,EAClE,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAUrC"}
@@ -0,0 +1,130 @@
1
+ export type AsyncStatus = 'idle' | 'pending' | 'success' | 'error';
2
+ export type QueryPrimitive = string | number | boolean | null;
3
+ export type QueryValue = QueryPrimitive | undefined | readonly QueryPrimitive[];
4
+ export type Query = Readonly<Record<string, QueryValue>>;
5
+ export type RequestKeyPart = string | number | boolean | null;
6
+ export type RequestKey = string | readonly RequestKeyPart[];
7
+ export interface StandardSchemaV1<TInput = unknown, TOutput = TInput> {
8
+ readonly '~standard': {
9
+ readonly version: 1;
10
+ readonly vendor: string;
11
+ readonly validate: (value: unknown) => StandardSchemaResult<TOutput> | Promise<StandardSchemaResult<TOutput>>;
12
+ readonly types?: {
13
+ readonly input: TInput;
14
+ readonly output: TOutput;
15
+ };
16
+ };
17
+ }
18
+ export type StandardSchemaResult<T> = {
19
+ readonly value: T;
20
+ readonly issues?: undefined;
21
+ } | {
22
+ readonly issues: readonly StandardSchemaIssue[];
23
+ readonly value?: undefined;
24
+ };
25
+ export interface StandardSchemaIssue {
26
+ readonly message: string;
27
+ readonly path?: readonly (PropertyKey | {
28
+ readonly key: PropertyKey;
29
+ })[];
30
+ }
31
+ export type InferSchemaOutput<TSchema extends StandardSchemaV1> = TSchema extends StandardSchemaV1<unknown, infer TOutput> ? TOutput : never;
32
+ export interface AppCacheOptions {
33
+ readonly scope: 'app';
34
+ }
35
+ export type FetchCache = false | 'active' | AppCacheOptions;
36
+ export interface FetchOptions {
37
+ readonly query?: Query;
38
+ readonly headers?: HeadersInit;
39
+ readonly key?: RequestKey;
40
+ readonly cache?: FetchCache;
41
+ readonly signal?: AbortSignal;
42
+ }
43
+ export interface ValidatedFetchOptions<TSchema extends StandardSchemaV1> extends FetchOptions {
44
+ readonly validate: TSchema;
45
+ }
46
+ export type ActionMethod = 'POST' | 'PUT' | 'PATCH' | 'DELETE';
47
+ export interface ActionOptions<TResult, TInput> {
48
+ readonly method?: ActionMethod;
49
+ readonly query?: Query;
50
+ readonly headers?: HeadersInit;
51
+ readonly validate?: StandardSchemaV1<unknown, TResult>;
52
+ readonly onSuccess?: (result: TResult, input: TInput) => void | Promise<void>;
53
+ readonly onError?: (error: import('./errors').RequestError, input: TInput) => void | Promise<void>;
54
+ }
55
+ /** Opaque change produced by a resource and consumed by an action call. */
56
+ export interface OptimisticChange<TResult = unknown> {
57
+ readonly kind: 'memoized-dom.optimistic-change';
58
+ /** Invariant phantom type connecting the change to an action result. */
59
+ readonly resultType?: (result: TResult) => TResult;
60
+ }
61
+ export interface RefreshableResource {
62
+ refresh(): Promise<unknown>;
63
+ }
64
+ export interface ActionCallOptions<TResult> {
65
+ readonly optimistic?: OptimisticChange<TResult>;
66
+ readonly refresh?: readonly RefreshableResource[];
67
+ readonly signal?: AbortSignal;
68
+ }
69
+ export interface FetchResourceCore<T> extends RefreshableResource {
70
+ readonly data: T | undefined;
71
+ readonly error: import('./errors').RequestError | null;
72
+ readonly status: AsyncStatus;
73
+ readonly pending: boolean;
74
+ readonly refreshing: boolean;
75
+ refresh(): Promise<T>;
76
+ abort(): void;
77
+ update(change: (current: T | undefined) => T): void;
78
+ mutate(change: (current: T | undefined) => void): void;
79
+ }
80
+ export interface FetchCollectionChanges<TItem> {
81
+ append(temporary: TItem): OptimisticChange<TItem>;
82
+ replace(current: TItem, temporary: TItem): OptimisticChange<TItem>;
83
+ remove<TResult = unknown>(current: TItem): OptimisticChange<TResult>;
84
+ }
85
+ export type FetchResource<T> = FetchResourceCore<T> & (T extends TItemArray<infer TItem> ? FetchCollectionChanges<TItem> : object);
86
+ type TItemArray<TItem> = TItem[];
87
+ export interface FetchFunction {
88
+ <T = unknown>(target: string | URL | null, options?: FetchOptions): FetchResource<T>;
89
+ <TSchema extends StandardSchemaV1>(target: string | URL | null, options: ValidatedFetchOptions<TSchema>): FetchResource<InferSchemaOutput<TSchema>>;
90
+ }
91
+ interface ActionState<TResult> {
92
+ readonly data: TResult | undefined;
93
+ readonly error: import('./errors').RequestError | null;
94
+ readonly status: AsyncStatus;
95
+ readonly pending: boolean;
96
+ abort(): void;
97
+ reset(): void;
98
+ }
99
+ type ActionCall<TResult, TInput> = [TInput] extends [void] ? (input?: TInput, options?: ActionCallOptions<TResult>) => Promise<TResult> : (input: TInput, options?: ActionCallOptions<TResult>) => Promise<TResult>;
100
+ export type Action<TResult, TInput = void> = ActionState<TResult> & ActionCall<TResult, TInput>;
101
+ export interface ActionFunction {
102
+ <TResult, TInput = void>(target: string | URL, options?: ActionOptions<TResult, TInput>): Action<TResult, TInput>;
103
+ }
104
+ export interface DataRuntimeOptions {
105
+ readonly fetch?: typeof globalThis.fetch;
106
+ readonly baseURL?: string | URL;
107
+ }
108
+ export interface DataRuntime {
109
+ readonly $fetch: FetchFunction;
110
+ readonly $action: ActionFunction;
111
+ /** Abort active work, detach live reads, and empty retained request data. */
112
+ clear(): void;
113
+ }
114
+ export interface ResourceSnapshot<T> {
115
+ readonly data: T | undefined;
116
+ readonly error: import('./errors').RequestError | null;
117
+ readonly status: AsyncStatus;
118
+ readonly pending: boolean;
119
+ readonly refreshing: boolean;
120
+ }
121
+ export type ResourceListener<T> = (snapshot: ResourceSnapshot<T>) => void;
122
+ export interface ActionSnapshot<T> {
123
+ readonly data: T | undefined;
124
+ readonly error: import('./errors').RequestError | null;
125
+ readonly status: AsyncStatus;
126
+ readonly pending: boolean;
127
+ }
128
+ export type ActionListener<T> = (snapshot: ActionSnapshot<T>) => void;
129
+ export {};
130
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAC9D,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,SAAS,GACT,SAAS,cAAc,EAAE,CAAC;AAC9B,MAAM,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AAEzD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAC9D,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,cAAc,EAAE,CAAC;AAE5D,MAAM,WAAW,gBAAgB,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,MAAM;IAClE,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE,CACjB,KAAK,EAAE,OAAO,KAEZ,oBAAoB,CAAC,OAAO,CAAC,GAC7B,OAAO,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE;YACf,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;SAC1B,CAAC;KACH,CAAC;CACH;AAED,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAC9B;IAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAE,GAClD;IACE,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;CAC5B,CAAC;AAEN,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG;QAAE,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAA;KAAE,CAAC,EAAE,CAAC;CAC1E;AAED,MAAM,MAAM,iBAAiB,CAAC,OAAO,SAAS,gBAAgB,IAC5D,OAAO,SAAS,gBAAgB,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,GACpD,OAAO,GACP,KAAK,CAAC;AAEZ,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,eAAe,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB,CACpC,OAAO,SAAS,gBAAgB,CAChC,SAAQ,YAAY;IACpB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE/D,MAAM,WAAW,aAAa,CAAC,OAAO,EAAE,MAAM;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvD,QAAQ,CAAC,SAAS,CAAC,EAAE,CACnB,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,KACV,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,CACjB,KAAK,EAAE,OAAO,UAAU,EAAE,YAAY,EACtC,KAAK,EAAE,MAAM,KACV,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB,CAAC,OAAO,GAAG,OAAO;IACjD,QAAQ,CAAC,IAAI,EAAE,gCAAgC,CAAC;IAEhD,wEAAwE;IACxE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;CACpD;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB,CAAC,OAAO;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAChD,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,CAAE,SAAQ,mBAAmB;IAC/D,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,UAAU,EAAE,YAAY,GAAG,IAAI,CAAC;IACvD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAE7B,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;IACtB,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG,IAAI,CAAC;IACpD,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,GAAG,IAAI,CAAC;CACxD;AAED,MAAM,WAAW,sBAAsB,CAAC,KAAK;IAC3C,MAAM,CAAC,SAAS,EAAE,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAClD,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACnE,MAAM,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,EAAE,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;CACtE;AAED,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,GACjD,CAAC,CAAC,SAAS,UAAU,CAAC,MAAM,KAAK,CAAC,GAC9B,sBAAsB,CAAC,KAAK,CAAC,GAC7B,MAAM,CAAC,CAAC;AAEd,KAAK,UAAU,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC;AAEjC,MAAM,WAAW,aAAa;IAC5B,CAAC,CAAC,GAAG,OAAO,EACV,MAAM,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,EAC3B,OAAO,CAAC,EAAE,YAAY,GACrB,aAAa,CAAC,CAAC,CAAC,CAAC;IAEpB,CAAC,OAAO,SAAS,gBAAgB,EAC/B,MAAM,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,EAC3B,OAAO,EAAE,qBAAqB,CAAC,OAAO,CAAC,GACtC,aAAa,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;CAC9C;AAED,UAAU,WAAW,CAAC,OAAO;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,OAAO,UAAU,EAAE,YAAY,GAAG,IAAI,CAAC;IACvD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,KAAK,IAAI,IAAI,CAAC;IACd,KAAK,IAAI,IAAI,CAAC;CACf;AAED,KAAK,UAAU,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,GACtD,CACE,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,KACjC,OAAO,CAAC,OAAO,CAAC,GACrB,CACE,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,KACjC,OAAO,CAAC,OAAO,CAAC,CAAC;AAE1B,MAAM,MAAM,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,IAAI,WAAW,CAAC,OAAO,CAAC,GAC/D,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAE9B,MAAM,WAAW,cAAc;IAC7B,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,MAAM,GAAG,GAAG,EACpB,OAAO,CAAC,EAAE,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,GACvC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IAEjC,6EAA6E;IAC7E,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,UAAU,EAAE,YAAY,GAAG,IAAI,CAAC;IACvD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAChC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAC1B,IAAI,CAAC;AAEV,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,UAAU,EAAE,YAAY,GAAG,IAAI,CAAC;IACvD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@memoized-dom/data",
3
+ "version": "0.0.1",
4
+ "description": "Browser-first fetch resources and callable actions for memoized-dom",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./internal": {
18
+ "types": "./dist/internal.d.ts",
19
+ "import": "./dist/internal.js",
20
+ "default": "./dist/internal.js"
21
+ }
22
+ },
23
+ "scripts": {
24
+ "build": "bun run ./scripts/clean.ts && esbuild ./src/index.ts ./src/internal.ts --bundle --outdir=./dist --platform=browser --format=esm --target=es2022 --minify --splitting --entry-names=[name] --chunk-names=chunks/[name]-[hash] && tsc -p tsconfig.build.json",
25
+ "test": "vitest run --config vitest.config.ts && tsc -p tsconfig.test.json"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ }
30
+ }