@firsthandjs/data 0.8.0 → 0.9.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/dist/cache.d.ts CHANGED
@@ -109,6 +109,30 @@ export type CacheClient = {
109
109
  * it.
110
110
  */
111
111
  forgetTagged(patterns: readonly Tag[]): void;
112
+ /**
113
+ * Everything answered so far, as plain data.
114
+ *
115
+ * What a server sends to the browser with the page. Resources have no keys
116
+ * — a resource belongs to its call site, which is the whole of ADR-0022 —
117
+ * but the transport's cache does, so this is where handing an answer from
118
+ * one process to another can be done at all. Entries still in flight are
119
+ * left out: there is nothing to hand over yet.
120
+ *
121
+ * ```ts
122
+ * const state = cache.dump(); // on the server
123
+ * cache.seed(state); // in the browser, before render
124
+ * ```
125
+ */
126
+ dump(): Record<string, unknown>;
127
+ /**
128
+ * Puts answers a server produced in, as if they had been `write`n.
129
+ *
130
+ * They stay until something forgets them, so the first render in the
131
+ * browser is served from here rather than asking again.
132
+ */
133
+ seed(entries: Record<string, unknown>): void;
134
+ /** Resolves once nothing this cache is running is still out. */
135
+ settle(passes?: number): Promise<void>;
112
136
  /** How many entries are held, in flight included. */
113
137
  readonly size: number;
114
138
  };
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,EAAiB,KAAK,GAAG,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAmBhD;AAED,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;;OAMG;IACH,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpD;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACzC,0EAA0E;IAC1E,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,sCAAsC;IACtC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;;OAOG;IACH,YAAY,CAAC,QAAQ,EAAE,SAAS,GAAG,EAAE,GAAG,IAAI,CAAC;IAC7C,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAeF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,YAAiB,GAAG,WAAW,CAmIzE"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,EAAiB,KAAK,GAAG,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAmBhD;AAED,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;;OAMG;IACH,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpD;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACzC,0EAA0E;IAC1E,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,sCAAsC;IACtC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;;OAOG;IACH,YAAY,CAAC,QAAQ,EAAE,SAAS,GAAG,EAAE,GAAG,IAAI,CAAC;IAC7C;;;;;;;;;;;;;OAaG;IACH,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC;;;;;OAKG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7C,gEAAgE;IAChE,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAeF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,YAAiB,GAAG,WAAW,CAwKzE"}
package/dist/index.d.ts CHANGED
@@ -18,6 +18,8 @@ export type { ActionContext, DataOptions, DataRequest, Forgetful, DataStore, Loa
18
18
  * through when a resource is invalidated (ADR-0023).
19
19
  */
20
20
  export { createCacheClient, stableKey } from './cache.js';
21
+ export { createMemoryStorage, serialize } from './transfer.js';
22
+ export type { MemoryStorage } from './transfer.js';
21
23
  export type { CacheClient, CacheOptions } from './cache.js';
22
24
  export { tag, tagMatches, anyTagMatches } from './tags.js';
23
25
  export type { Tag, TagVars, Variables } from './tags.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACpF,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC5F,YAAY,EACV,aAAa,EACb,WAAW,EACX,WAAW,EACX,SAAS,EACT,SAAS,EACT,WAAW,EACX,MAAM,EACN,QAAQ,EACR,MAAM,EACN,OAAO,GACR,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC3D,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACnF,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE/F;;;GAGG;AACH,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAClE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACpF,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC5F,YAAY,EACV,aAAa,EACb,WAAW,EACX,WAAW,EACX,SAAS,EACT,SAAS,EACT,WAAW,EACX,MAAM,EACN,QAAQ,EACR,MAAM,EACN,OAAO,GACR,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC3D,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACnF,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE/F;;;GAGG;AACH,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAClE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.dev.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  createContext,
4
4
  effect,
5
+ isRendering,
5
6
  onCleanup,
6
7
  untrack,
7
8
  useContext
@@ -81,6 +82,20 @@ function createData(options = {}) {
81
82
  }
82
83
  recent = recent.filter((one) => !anyTagMatches(one.patterns, tags));
83
84
  },
85
+ settle: async (passes = 10) => {
86
+ for (let pass = 0; pass < passes; pass++) {
87
+ const waiting = [];
88
+ for (const entry of held) {
89
+ if (entry.inflight !== null) {
90
+ waiting.push(entry.inflight);
91
+ }
92
+ }
93
+ if (waiting.length === 0) {
94
+ return;
95
+ }
96
+ await Promise.all(waiting);
97
+ }
98
+ },
84
99
  hold: (entry) => {
85
100
  held.add(entry);
86
101
  return () => held.delete(entry);
@@ -130,6 +145,7 @@ function createHeld(store, name) {
130
145
  tags: [],
131
146
  controller: null,
132
147
  pending: [],
148
+ inflight: null,
133
149
  superseded: false,
134
150
  disposed: false,
135
151
  answeredAt: 0,
@@ -177,6 +193,16 @@ function useInvalidate() {
177
193
  const store = useData();
178
194
  return (...patterns) => store.invalidate(...patterns);
179
195
  }
196
+ function seed(entry, stored) {
197
+ if (stored === void 0 || entry.data.peek() !== void 0 || entry.disposed) {
198
+ return;
199
+ }
200
+ succeed(entry, stored);
201
+ entry.loading.value = true;
202
+ }
203
+ function isThenable(value) {
204
+ return typeof value === "object" && value !== null && typeof value.then === "function";
205
+ }
180
206
  function useResource(load, options = {}) {
181
207
  const store = useData();
182
208
  const entry = createHeld(store, options.persist);
@@ -231,7 +257,7 @@ function useResource(load, options = {}) {
231
257
  request,
232
258
  tags: declare
233
259
  };
234
- return load(context).then(async (value) => {
260
+ const running = load(context).then(async (value) => {
235
261
  if (controller.signal.aborted || entry.disposed) {
236
262
  return void 0;
237
263
  }
@@ -259,24 +285,45 @@ function useResource(load, options = {}) {
259
285
  fail(entry, error);
260
286
  return void 0;
261
287
  });
288
+ entry.inflight = running;
289
+ void running.then(() => {
290
+ if (entry.inflight === running) {
291
+ entry.inflight = null;
292
+ }
293
+ });
294
+ return running;
262
295
  };
263
296
  const persist = options.persist;
264
297
  const storage = store.storage;
265
298
  if (persist !== void 0 && storage?.read !== void 0) {
266
- void (async () => {
267
- try {
268
- const stored = await storage.read?.(persist);
269
- if (stored !== void 0 && entry.data.peek() === void 0 && !entry.disposed) {
270
- succeed(entry, stored);
271
- entry.loading.value = true;
299
+ let stored;
300
+ try {
301
+ stored = storage.read(persist);
302
+ } catch {
303
+ stored = void 0;
304
+ }
305
+ if (isThenable(stored)) {
306
+ void (async () => {
307
+ try {
308
+ seed(entry, await stored);
309
+ } catch {
272
310
  }
273
- } catch {
274
- }
275
- })();
311
+ })();
312
+ } else {
313
+ seed(entry, stored);
314
+ }
315
+ }
316
+ if (isRendering()) {
317
+ if (entry.data.peek() === void 0) {
318
+ void entry.run(false);
319
+ } else {
320
+ entry.loading.value = false;
321
+ }
322
+ } else {
323
+ effect(() => {
324
+ void entry.run(false);
325
+ });
276
326
  }
277
- effect(() => {
278
- void entry.run(false);
279
- });
280
327
  onCleanup(() => {
281
328
  entry.disposed = true;
282
329
  entry.controller?.abort();
@@ -409,6 +456,16 @@ function createCacheClient(options = {}) {
409
456
  entry?.controller?.abort();
410
457
  entries.delete(key);
411
458
  };
459
+ const put = (key, value) => {
460
+ keep(key, {
461
+ value,
462
+ tags: [],
463
+ expires: ttl === 0 ? Infinity : now() + ttl,
464
+ inflight: null,
465
+ controller: null,
466
+ waiting: 0
467
+ });
468
+ };
412
469
  const keep = (key, entry) => {
413
470
  entries.delete(key);
414
471
  entries.set(key, entry);
@@ -423,6 +480,34 @@ function createCacheClient(options = {}) {
423
480
  get size() {
424
481
  return entries.size;
425
482
  },
483
+ dump: () => {
484
+ const out = {};
485
+ for (const [key, entry] of entries) {
486
+ if (entry.inflight === null) {
487
+ out[key] = entry.value;
488
+ }
489
+ }
490
+ return out;
491
+ },
492
+ seed: (values) => {
493
+ for (const key in values) {
494
+ put(key, values[key]);
495
+ }
496
+ },
497
+ settle: async (passes = 10) => {
498
+ for (let pass = 0; pass < passes; pass++) {
499
+ const waiting = [];
500
+ for (const entry of entries.values()) {
501
+ if (entry.inflight !== null) {
502
+ waiting.push(entry.inflight);
503
+ }
504
+ }
505
+ if (waiting.length === 0) {
506
+ return;
507
+ }
508
+ await Promise.all(waiting.map(async (one) => one.catch(() => void 0)));
509
+ }
510
+ },
426
511
  peek: (key) => {
427
512
  const entry = entries.get(key);
428
513
  if (entry === void 0 || entry.expires <= now()) {
@@ -430,16 +515,7 @@ function createCacheClient(options = {}) {
430
515
  }
431
516
  return entry.value;
432
517
  },
433
- write: (key, value) => {
434
- keep(key, {
435
- value,
436
- tags: [],
437
- expires: ttl === 0 ? Infinity : now() + ttl,
438
- inflight: null,
439
- controller: null,
440
- waiting: 0
441
- });
442
- },
518
+ write: put,
443
519
  forget: (key) => {
444
520
  if (key === void 0) {
445
521
  for (const held of [...entries.keys()]) {
@@ -524,6 +600,38 @@ async function share(entry, request) {
524
600
  }
525
601
  }
526
602
 
603
+ // packages/data/src/transfer.ts
604
+ function createMemoryStorage(initial) {
605
+ const values = new Map(initial === void 0 ? [] : Object.entries(initial));
606
+ return {
607
+ get size() {
608
+ return values.size;
609
+ },
610
+ read: (name) => values.get(name),
611
+ write: (name, data) => {
612
+ values.set(name, data);
613
+ },
614
+ clear: () => {
615
+ values.clear();
616
+ },
617
+ dump: () => Object.fromEntries(values),
618
+ seed: (next) => {
619
+ for (const name in next) {
620
+ values.set(name, next[name]);
621
+ }
622
+ }
623
+ };
624
+ }
625
+ function serialize(value) {
626
+ return JSON.stringify(value).replace(UNSAFE, (one) => ESCAPES[one]);
627
+ }
628
+ var UNSAFE = /[<\u2028\u2029]/g;
629
+ var ESCAPES = {
630
+ "<": "\\u003c",
631
+ "\u2028": "\\u2028",
632
+ "\u2029": "\\u2029"
633
+ };
634
+
527
635
  // packages/data/src/document.ts
528
636
  var DIRECTIVE = /^@(tag|invalidates)\b/;
529
637
  var OPERATION = /\b(query|mutation|subscription)\b[^\S\n]*([A-Za-z_]\w*)?/;
@@ -811,10 +919,12 @@ export {
811
919
  createCacheClient,
812
920
  createData,
813
921
  createFetchClient,
922
+ createMemoryStorage,
814
923
  fromObservable,
815
924
  fromPromise,
816
925
  parseGraphQL,
817
926
  resolveTags,
927
+ serialize,
818
928
  stableKey,
819
929
  tag,
820
930
  tagMatches,
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{createContext as z,effect as N,onCleanup as D,untrack as _,useContext as M}from"@firsthandjs/core";import{batch as S,signal as b}from"@firsthandjs/core";var H=Object.freeze({});function R(e,n=H){return{name:e,vars:n}}function L(e,n){if(e.name!==n.name)return!1;for(let r of Object.keys(e.vars))if(!Object.is(e.vars[r],n.vars[r]))return!1;return!0}function y(e,n){for(let r of e)for(let t of n)if(L(r,t))return!0;return!1}function A(e={}){let n=new Set,r=e.remember??6e4,t=[],a=()=>Date.now();return{storage:e.storage,missed:(s,l)=>{if(r===0||t.length===0)return!1;let o=a()-r;return t=t.filter(i=>i.at>=o),t.some(i=>i.at>s.answeredAt&&y(i.patterns,l))},settled:s=>{t.length!==0&&(t=t.filter(l=>!y(l.patterns,s)))},hold:s=>(n.add(s),()=>n.delete(s)),invalidate:async(...s)=>{let l=[];for(let o of e.caches??[])o.forgetTagged(s);r>0&&t.push({patterns:s,at:a()});for(let o of[...n])o.controller!==null&&o.pending.push(...s),y(s,o.tags)&&(o.name,o.tags,l.push(o.run(!0)));await Promise.all(l)},clear:()=>{t=[];for(let s of[...n])s.controller?.abort(),n.delete(s);try{e.storage?.clear?.()}catch{}},get size(){return n.size}}}function x(e,n){return{data:b(void 0),error:b(void 0),status:b("idle"),loading:b(!1),tags:[],controller:null,pending:[],superseded:!1,disposed:!1,answeredAt:0,name:n}}function m(e,n){e.answeredAt=Date.now(),S(()=>{e.data.value=n,e.error.value=void 0,e.status.value="success",e.loading.value=!1})}function v(e,n){S(()=>{e.error.value=n,e.status.value="error",e.loading.value=!1})}function C(e,n){return{data:e.data,error:e.error,status:e.status,loading:e.loading,reload:()=>e.run(!0),dispose:()=>{e.disposed=!0,e.controller?.abort(),n()}}}var P=z();function h(){return M(P).value}function Q(){let e=h();return(...n)=>e.invalidate(...n)}function V(e,n={}){let r=h(),t=x(r,n.persist),a=r.hold(t);n.persist,t.run=o=>{if(t.disposed)return Promise.resolve(void 0);t.controller?.abort();let i=new AbortController;t.controller=i,t.pending=[],t.superseded=!1,t.loading.value=!0,t.data.peek()===void 0&&(t.status.value="loading");let d=o,u=!1,f=(...c)=>{t.tags=c,!d&&r.missed(t,c)&&(d=!0,u&&(t.superseded=!0)),t.pending.length>0&&y(t.pending,c)&&(t.superseded=!0)},g={signal:i.signal,get force(){return u=!0,d},get declared(){return t.tags},tags:f},T={signal:i.signal,get force(){return u=!0,d},request:g,tags:f};return e(T).then(async c=>{if(!(i.signal.aborted||t.disposed)){if(t.controller=null,m(t,c),t.superseded||r.settled(t.tags),n.persist!==void 0)try{r.storage?.write?.(n.persist,c)}catch{}return t.superseded?(t.superseded=!1,t.run(!0)):c}}).catch(c=>{i.signal.aborted||t.disposed||(t.controller=null,v(t,c))})};let s=n.persist,l=r.storage;return s!==void 0&&l?.read!==void 0&&(async()=>{try{let o=await l.read?.(s);o!==void 0&&t.data.peek()===void 0&&!t.disposed&&(m(t,o),t.loading.value=!0)}catch{}})(),N(()=>{t.run(!1)}),D(()=>{t.disposed=!0,t.controller?.abort(),n.persist,t.tags,a()}),C(t,a)}function G(e){let n=h(),r=x(n,void 0),t=null;return D(()=>{r.disposed=!0,t?.abort()}),{data:r.data,error:r.error,status:r.status,running:r.loading,run:async a=>{t?.abort(),t=new AbortController;let s=t,l=[];r.loading.value=!0,r.status.value="loading";try{let o=(...d)=>{l=d},i=await _(()=>e(a,{signal:s.signal,invalidates:o,request:{signal:s.signal,force:!0,mutating:!0,tags:o}}));return s.signal.aborted||r.disposed?void 0:(m(r,i),l.length>0&&await n.invalidate(...l),i)}catch(o){if(s.signal.aborted||r.disposed)return;v(r,o);return}}}}function K(e,n={}){let r=h(),t=x(r,void 0),a=r.hold(t);t.run=()=>n.reload===void 0?Promise.resolve(void 0):U(n.reload()),t.loading.value=!0,t.status.value="loading";let s=e.subscribe({next:o=>{m(t,o)},error:o=>{v(t,o)}}),l=typeof s=="function"?s:()=>{s.unsubscribe()};return D(()=>{t.disposed=!0,l(),a()}),C(t,()=>{l(),a()})}function B(e){return V(()=>e())}async function U(e){await e}function k(e){return e===void 0?"undefined":e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(k).join(",")}]`:e instanceof Date?e.toISOString():`{${Object.entries(e).filter(([,r])=>r!==void 0).sort(([r],[t])=>r<t?-1:1).map(([r,t])=>`${r}:${k(t)}`).join(",")}}`}function O(e={}){let n=e.ttl??0,r=e.max??100,t=e.now??(()=>Date.now()),a=new Map,s=o=>{a.get(o)?.controller?.abort(),a.delete(o)},l=(o,i)=>{if(a.delete(o),a.set(o,i),a.size>r)for(let d of a.keys()){s(d);break}};return{get size(){return a.size},peek:o=>{let i=a.get(o);if(!(i===void 0||i.expires<=t()))return i.value},write:(o,i)=>{l(o,{value:i,tags:[],expires:n===0?1/0:t()+n,inflight:null,controller:null,waiting:0})},forget:o=>{if(o===void 0){for(let i of[...a.keys()])s(i);return}s(o)},forgetTagged:o=>{for(let[i,d]of[...a])d.tags.length>0&&y(o,d.tags)&&s(i)},read:(o,i)=>async d=>{if(d.mutating===!0)return await i(d);let u=a.get(o);if(d.force)s(o);else if(u!==void 0){if(u.inflight!==null)return await $(u,d);if(u.expires>t())return l(o,u),u.value;a.delete(o)}let f=new AbortController,g={value:void 0,tags:d.declared??[],expires:0,inflight:null,controller:f,waiting:0},T=i({signal:f.signal,force:d.force}).then(c=>(a.get(o)===g&&(n===0?a.delete(o):(g.value=c,g.expires=t()+n,g.inflight=null,g.controller=null)),c)).catch(c=>{throw a.get(o)===g&&a.delete(o),c});return g.inflight=T,l(o,g),await $(g,d)}}}async function $(e,n){e.waiting+=1;let r=()=>{e.waiting<=1&&e.inflight!==null&&e.controller?.abort()};n.signal.addEventListener("abort",r,{once:!0});try{return await e.inflight}finally{n.signal.removeEventListener("abort",r),e.waiting-=1}}var Z=/^@(tag|invalidates)\b/,W=/\b(query|mutation|subscription)\b[^\S\n]*([A-Za-z_]\w*)?/,E=/^[_A-Za-z][_0-9A-Za-z]*/,p=class extends Error{constructor(n){super(n),this.name="FirsthandDirectiveError"}};function q(e,n){if(e.startsWith('"""',n)){let t=e.indexOf('"""',n+3);return t===-1?e.length:t+3}let r=n+1;for(;r<e.length&&e[r]!=='"';)r+=e[r]==="\\"?2:1;return r+1}function X(e,n){let r=0,t=n;for(;t<e.length;){let a=e[t];if(a==='"'){t=q(e,t);continue}if(a==="("||a==="["||a==="{")r++;else if((a===")"||a==="]"||a==="}")&&(r--,r===0))return t+1;t++}throw new p(`unclosed arguments in ${e.slice(n,n+40)}`)}function Y(e,n){try{return JSON.parse(e)}catch{throw new p(`@${n}: ${e} is not a valid string`)}}function ee(e,n){let r={},t=0,a=()=>{for(;t<e.length&&/[\s,]/.test(e[t]);)t++};for(a();t<e.length;){let s=E.exec(e.slice(t));if(s===null)throw new p(`@${n}: expected an argument name at "${e.slice(t)}"`);if(t+=s[0].length,a(),e[t]!==":")throw new p(`@${n}: argument ${s[0]} has no value`);t++,a();let l=te(e,n,t);r[s[0]]=l.value,t=l.next,a()}return r}function te(e,n,r){let t=e[r];if(t==="$"){let i=E.exec(e.slice(r+1));if(i===null)throw new p(`@${n}: expected a variable name after $`);return{value:{variable:i[0]},next:r+1+i[0].length}}if(t==='"'){let i=q(e,r),d=e.slice(r,i);return{value:{literal:d.startsWith('"""')?d.slice(3,-3).trim():Y(d,n)},next:i}}if(t==="["||t==="{")throw new p(`@${n}: a tag variable must be a scalar, not a list or object`);let a=/^[^\s,)]+/.exec(e.slice(r));if(a===null)throw new p(`@${n}: expected a value`);let s=a[0],l=r+s.length;if(s==="true"||s==="false")return{value:{literal:s==="true"},next:l};if(s==="null")return{value:{literal:null},next:l};let o=Number(s);return{value:{literal:Number.isNaN(o)?s:o},next:l}}function ne(e){let n=[],r=[],t="",a=0,s=0;for(;a<e.length;){let l=e[a];if(l==='"'){a=q(e,a);continue}if(l==="#"){let c=e.indexOf(`
2
- `,a);a=c===-1?e.length:c;continue}if(l!=="@"){a++;continue}let o=Z.exec(e.slice(a));if(o===null){a++;continue}let i=a+o[0].length,d="",u=i;for(;u<e.length&&/\s/.test(e[u]);)u++;if(e[u]==="("){let c=X(e,u);d=e.slice(u+1,c-1),i=c}let f=ee(d,o[1]),g=f.name;if(g===void 0||!("literal"in g)||typeof g.literal!="string")throw new p(`@${o[1]} needs a literal name, as in @${o[1]}(name: "user", id: $id)`);delete f.name,(o[1]==="tag"?n:r).push({name:g.literal,vars:f});let T=a;for(;T>s&&/\s/.test(e[T-1]);)T--;t+=e.slice(s,T),s=i,a=i}return{tags:n,invalidates:r,stripped:t+e.slice(s)}}function re(e){let{tags:n,invalidates:r,stripped:t}=ne(e),a=W.exec(t.replace(/#[^\n]*/g,""));return{source:t,operation:a?.[2]??"",kind:a?.[1]??"query",tags:n,invalidates:r}}function ae(e,n){return e.map(r=>{let t={};for(let[a,s]of Object.entries(r.vars)){if("literal"in s){t[a]=s.literal;continue}let l=n[s.variable];l!=null&&(t[a]=typeof l=="object"?JSON.stringify(l):l)}return R(r.name,t)})}import{untrack as I}from"@firsthandjs/core";var w=class extends Error{constructor(r,t,a){super(`HTTP ${String(r)} for ${t}`);this.status=r;this.url=t;this.body=a;this.name="FirsthandHttpError"}status;url;body};function oe(e,n,r){let t=/^[a-z]+:\/\//i.test(n)?n:`${e.baseUrl??""}${n}`,{json:a,cacheKey:s,...l}=r,o={...e.init,...l},i=new Headers(typeof e.headers=="function"?I(e.headers):e.headers);for(let[d,u]of new Headers(r.headers??{}))i.set(d,u);return a!==void 0&&(o.body=JSON.stringify(a),i.has("content-type")||i.set("content-type","application/json")),o.headers=i,[t,o]}async function F(e,n,r,t){let s=await(e.fetch??globalThis.fetch)(n,{...r,signal:t.signal}),l=await s.text(),o=l===""?void 0:JSON.parse(l);if(!s.ok)throw new w(s.status,n,o);return o}function j(e={}){let n=e.cache===void 0||e.cache===!1?void 0:"read"in e.cache?e.cache:O(e.cache),r={cache:n,request:(t,a={})=>async s=>{let[l,o]=oe(e,t,a),i=(o.method??"GET").toUpperCase(),d=e.scope===void 0?o.headers.get("authorization")??"":I(e.scope),u=typeof a.cacheKey=="string"?a.cacheKey:i==="GET"||i==="HEAD"?`${i} ${l}`:void 0,f=u===void 0?void 0:`${d}\0${u}`;return n===void 0||a.cacheKey===!1||f===void 0||s.mutating===!0?await F(e,l,o,s):await n.read(f,g=>F(e,l,o,g))(s)},get:(t,a={})=>r.request(t,a),post:(t,a={})=>r.request(t,{method:"POST",...a}),put:(t,a={})=>r.request(t,{method:"PUT",...a}),patch:(t,a={})=>r.request(t,{method:"PATCH",...a}),remove:(t,a={})=>r.request(t,{method:"DELETE",...a}),with:t=>j({...e,...t,cache:t.cache??n??!1})};return r}export{P as DataContext,p as FirsthandDirectiveError,w as FirsthandHttpError,y as anyTagMatches,O as createCacheClient,A as createData,j as createFetchClient,K as fromObservable,B as fromPromise,re as parseGraphQL,ae as resolveTags,k as stableKey,R as tag,L as tagMatches,G as useAction,h as useData,Q as useInvalidate,V as useResource};
1
+ import{createContext as N,effect as _,isRendering as Q,onCleanup as D,untrack as G,useContext as K}from"@firsthandjs/core";import{batch as A,signal as v}from"@firsthandjs/core";var z=Object.freeze({});function k(e,n=z){return{name:e,vars:n}}function q(e,n){if(e.name!==n.name)return!1;for(let r of Object.keys(e.vars))if(!Object.is(e.vars[r],n.vars[r]))return!1;return!0}function h(e,n){for(let r of e)for(let t of n)if(q(r,t))return!0;return!1}function L(e={}){let n=new Set,r=e.remember??6e4,t=[],o=()=>Date.now();return{storage:e.storage,missed:(s,l)=>{if(r===0||t.length===0)return!1;let i=o()-r;return t=t.filter(a=>a.at>=i),t.some(a=>a.at>s.answeredAt&&h(a.patterns,l))},settled:s=>{t.length!==0&&(t=t.filter(l=>!h(l.patterns,s)))},settle:async(s=10)=>{for(let l=0;l<s;l++){let i=[];for(let a of n)a.inflight!==null&&i.push(a.inflight);if(i.length===0)return;await Promise.all(i)}},hold:s=>(n.add(s),()=>n.delete(s)),invalidate:async(...s)=>{let l=[];for(let i of e.caches??[])i.forgetTagged(s);r>0&&t.push({patterns:s,at:o()});for(let i of[...n])i.controller!==null&&i.pending.push(...s),h(s,i.tags)&&(i.name,i.tags,l.push(i.run(!0)));await Promise.all(l)},clear:()=>{t=[];for(let s of[...n])s.controller?.abort(),n.delete(s);try{e.storage?.clear?.()}catch{}},get size(){return n.size}}}function x(e,n){return{data:v(void 0),error:v(void 0),status:v("idle"),loading:v(!1),tags:[],controller:null,pending:[],inflight:null,superseded:!1,disposed:!1,answeredAt:0,name:n}}function T(e,n){e.answeredAt=Date.now(),A(()=>{e.data.value=n,e.error.value=void 0,e.status.value="success",e.loading.value=!1})}function w(e,n){A(()=>{e.error.value=n,e.status.value="error",e.loading.value=!1})}function C(e,n){return{data:e.data,error:e.error,status:e.status,loading:e.loading,reload:()=>e.run(!0),dispose:()=>{e.disposed=!0,e.controller?.abort(),n()}}}var $=N();function b(){return K($).value}function U(){let e=b();return(...n)=>e.invalidate(...n)}function V(e,n){n===void 0||e.data.peek()!==void 0||e.disposed||(T(e,n),e.loading.value=!0)}function B(e){return typeof e=="object"&&e!==null&&typeof e.then=="function"}function j(e,n={}){let r=b(),t=x(r,n.persist),o=r.hold(t);n.persist,t.run=i=>{if(t.disposed)return Promise.resolve(void 0);t.controller?.abort();let a=new AbortController;t.controller=a,t.pending=[],t.superseded=!1,t.loading.value=!0,t.data.peek()===void 0&&(t.status.value="loading");let d=i,u=!1,c=(...f)=>{t.tags=f,!d&&r.missed(t,f)&&(d=!0,u&&(t.superseded=!0)),t.pending.length>0&&h(t.pending,f)&&(t.superseded=!0)},y={signal:a.signal,get force(){return u=!0,d},get declared(){return t.tags},tags:c},g={signal:a.signal,get force(){return u=!0,d},request:y,tags:c},p=e(g).then(async f=>{if(!(a.signal.aborted||t.disposed)){if(t.controller=null,T(t,f),t.superseded||r.settled(t.tags),n.persist!==void 0)try{r.storage?.write?.(n.persist,f)}catch{}return t.superseded?(t.superseded=!1,t.run(!0)):f}}).catch(f=>{a.signal.aborted||t.disposed||(t.controller=null,w(t,f))});return t.inflight=p,p.then(()=>{t.inflight===p&&(t.inflight=null)}),p};let s=n.persist,l=r.storage;if(s!==void 0&&l?.read!==void 0){let i;try{i=l.read(s)}catch{i=void 0}B(i)?(async()=>{try{V(t,await i)}catch{}})():V(t,i)}return Q()?t.data.peek()===void 0?t.run(!1):t.loading.value=!1:_(()=>{t.run(!1)}),D(()=>{t.disposed=!0,t.controller?.abort(),n.persist,t.tags,o()}),C(t,o)}function Z(e){let n=b(),r=x(n,void 0),t=null;return D(()=>{r.disposed=!0,t?.abort()}),{data:r.data,error:r.error,status:r.status,running:r.loading,run:async o=>{t?.abort(),t=new AbortController;let s=t,l=[];r.loading.value=!0,r.status.value="loading";try{let i=(...d)=>{l=d},a=await G(()=>e(o,{signal:s.signal,invalidates:i,request:{signal:s.signal,force:!0,mutating:!0,tags:i}}));return s.signal.aborted||r.disposed?void 0:(T(r,a),l.length>0&&await n.invalidate(...l),a)}catch(i){if(s.signal.aborted||r.disposed)return;w(r,i);return}}}}function W(e,n={}){let r=b(),t=x(r,void 0),o=r.hold(t);t.run=()=>n.reload===void 0?Promise.resolve(void 0):Y(n.reload()),t.loading.value=!0,t.status.value="loading";let s=e.subscribe({next:i=>{T(t,i)},error:i=>{w(t,i)}}),l=typeof s=="function"?s:()=>{s.unsubscribe()};return D(()=>{t.disposed=!0,l(),o()}),C(t,()=>{l(),o()})}function X(e){return j(()=>e())}async function Y(e){await e}function O(e){return e===void 0?"undefined":e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(O).join(",")}]`:e instanceof Date?e.toISOString():`{${Object.entries(e).filter(([,r])=>r!==void 0).sort(([r],[t])=>r<t?-1:1).map(([r,t])=>`${r}:${O(t)}`).join(",")}}`}function S(e={}){let n=e.ttl??0,r=e.max??100,t=e.now??(()=>Date.now()),o=new Map,s=a=>{o.get(a)?.controller?.abort(),o.delete(a)},l=(a,d)=>{i(a,{value:d,tags:[],expires:n===0?1/0:t()+n,inflight:null,controller:null,waiting:0})},i=(a,d)=>{if(o.delete(a),o.set(a,d),o.size>r)for(let u of o.keys()){s(u);break}};return{get size(){return o.size},dump:()=>{let a={};for(let[d,u]of o)u.inflight===null&&(a[d]=u.value);return a},seed:a=>{for(let d in a)l(d,a[d])},settle:async(a=10)=>{for(let d=0;d<a;d++){let u=[];for(let c of o.values())c.inflight!==null&&u.push(c.inflight);if(u.length===0)return;await Promise.all(u.map(async c=>c.catch(()=>{})))}},peek:a=>{let d=o.get(a);if(!(d===void 0||d.expires<=t()))return d.value},write:l,forget:a=>{if(a===void 0){for(let d of[...o.keys()])s(d);return}s(a)},forgetTagged:a=>{for(let[d,u]of[...o])u.tags.length>0&&h(a,u.tags)&&s(d)},read:(a,d)=>async u=>{if(u.mutating===!0)return await d(u);let c=o.get(a);if(u.force)s(a);else if(c!==void 0){if(c.inflight!==null)return await E(c,u);if(c.expires>t())return i(a,c),c.value;o.delete(a)}let y=new AbortController,g={value:void 0,tags:u.declared??[],expires:0,inflight:null,controller:y,waiting:0},p=d({signal:y.signal,force:u.force}).then(f=>(o.get(a)===g&&(n===0?o.delete(a):(g.value=f,g.expires=t()+n,g.inflight=null,g.controller=null)),f)).catch(f=>{throw o.get(a)===g&&o.delete(a),f});return g.inflight=p,i(a,g),await E(g,u)}}}async function E(e,n){e.waiting+=1;let r=()=>{e.waiting<=1&&e.inflight!==null&&e.controller?.abort()};n.signal.addEventListener("abort",r,{once:!0});try{return await e.inflight}finally{n.signal.removeEventListener("abort",r),e.waiting-=1}}function ee(e){let n=new Map(e===void 0?[]:Object.entries(e));return{get size(){return n.size},read:r=>n.get(r),write:(r,t)=>{n.set(r,t)},clear:()=>{n.clear()},dump:()=>Object.fromEntries(n),seed:r=>{for(let t in r)n.set(t,r[t])}}}function te(e){return JSON.stringify(e).replace(ne,n=>re[n])}var ne=/[<\u2028\u2029]/g,re={"<":"\\u003c","\u2028":"\\u2028","\u2029":"\\u2029"};var oe=/^@(tag|invalidates)\b/,ae=/\b(query|mutation|subscription)\b[^\S\n]*([A-Za-z_]\w*)?/,F=/^[_A-Za-z][_0-9A-Za-z]*/,m=class extends Error{constructor(n){super(n),this.name="FirsthandDirectiveError"}};function P(e,n){if(e.startsWith('"""',n)){let t=e.indexOf('"""',n+3);return t===-1?e.length:t+3}let r=n+1;for(;r<e.length&&e[r]!=='"';)r+=e[r]==="\\"?2:1;return r+1}function se(e,n){let r=0,t=n;for(;t<e.length;){let o=e[t];if(o==='"'){t=P(e,t);continue}if(o==="("||o==="["||o==="{")r++;else if((o===")"||o==="]"||o==="}")&&(r--,r===0))return t+1;t++}throw new m(`unclosed arguments in ${e.slice(n,n+40)}`)}function ie(e,n){try{return JSON.parse(e)}catch{throw new m(`@${n}: ${e} is not a valid string`)}}function le(e,n){let r={},t=0,o=()=>{for(;t<e.length&&/[\s,]/.test(e[t]);)t++};for(o();t<e.length;){let s=F.exec(e.slice(t));if(s===null)throw new m(`@${n}: expected an argument name at "${e.slice(t)}"`);if(t+=s[0].length,o(),e[t]!==":")throw new m(`@${n}: argument ${s[0]} has no value`);t++,o();let l=de(e,n,t);r[s[0]]=l.value,t=l.next,o()}return r}function de(e,n,r){let t=e[r];if(t==="$"){let a=F.exec(e.slice(r+1));if(a===null)throw new m(`@${n}: expected a variable name after $`);return{value:{variable:a[0]},next:r+1+a[0].length}}if(t==='"'){let a=P(e,r),d=e.slice(r,a);return{value:{literal:d.startsWith('"""')?d.slice(3,-3).trim():ie(d,n)},next:a}}if(t==="["||t==="{")throw new m(`@${n}: a tag variable must be a scalar, not a list or object`);let o=/^[^\s,)]+/.exec(e.slice(r));if(o===null)throw new m(`@${n}: expected a value`);let s=o[0],l=r+s.length;if(s==="true"||s==="false")return{value:{literal:s==="true"},next:l};if(s==="null")return{value:{literal:null},next:l};let i=Number(s);return{value:{literal:Number.isNaN(i)?s:i},next:l}}function ue(e){let n=[],r=[],t="",o=0,s=0;for(;o<e.length;){let l=e[o];if(l==='"'){o=P(e,o);continue}if(l==="#"){let p=e.indexOf(`
2
+ `,o);o=p===-1?e.length:p;continue}if(l!=="@"){o++;continue}let i=oe.exec(e.slice(o));if(i===null){o++;continue}let a=o+i[0].length,d="",u=a;for(;u<e.length&&/\s/.test(e[u]);)u++;if(e[u]==="("){let p=se(e,u);d=e.slice(u+1,p-1),a=p}let c=le(d,i[1]),y=c.name;if(y===void 0||!("literal"in y)||typeof y.literal!="string")throw new m(`@${i[1]} needs a literal name, as in @${i[1]}(name: "user", id: $id)`);delete c.name,(i[1]==="tag"?n:r).push({name:y.literal,vars:c});let g=o;for(;g>s&&/\s/.test(e[g-1]);)g--;t+=e.slice(s,g),s=a,o=a}return{tags:n,invalidates:r,stripped:t+e.slice(s)}}function ce(e){let{tags:n,invalidates:r,stripped:t}=ue(e),o=ae.exec(t.replace(/#[^\n]*/g,""));return{source:t,operation:o?.[2]??"",kind:o?.[1]??"query",tags:n,invalidates:r}}function ge(e,n){return e.map(r=>{let t={};for(let[o,s]of Object.entries(r.vars)){if("literal"in s){t[o]=s.literal;continue}let l=n[s.variable];l!=null&&(t[o]=typeof l=="object"?JSON.stringify(l):l)}return k(r.name,t)})}import{untrack as I}from"@firsthandjs/core";var R=class extends Error{constructor(r,t,o){super(`HTTP ${String(r)} for ${t}`);this.status=r;this.url=t;this.body=o;this.name="FirsthandHttpError"}status;url;body};function fe(e,n,r){let t=/^[a-z]+:\/\//i.test(n)?n:`${e.baseUrl??""}${n}`,{json:o,cacheKey:s,...l}=r,i={...e.init,...l},a=new Headers(typeof e.headers=="function"?I(e.headers):e.headers);for(let[d,u]of new Headers(r.headers??{}))a.set(d,u);return o!==void 0&&(i.body=JSON.stringify(o),a.has("content-type")||a.set("content-type","application/json")),i.headers=a,[t,i]}async function H(e,n,r,t){let s=await(e.fetch??globalThis.fetch)(n,{...r,signal:t.signal}),l=await s.text(),i=l===""?void 0:JSON.parse(l);if(!s.ok)throw new R(s.status,n,i);return i}function J(e={}){let n=e.cache===void 0||e.cache===!1?void 0:"read"in e.cache?e.cache:S(e.cache),r={cache:n,request:(t,o={})=>async s=>{let[l,i]=fe(e,t,o),a=(i.method??"GET").toUpperCase(),d=e.scope===void 0?i.headers.get("authorization")??"":I(e.scope),u=typeof o.cacheKey=="string"?o.cacheKey:a==="GET"||a==="HEAD"?`${a} ${l}`:void 0,c=u===void 0?void 0:`${d}\0${u}`;return n===void 0||o.cacheKey===!1||c===void 0||s.mutating===!0?await H(e,l,i,s):await n.read(c,y=>H(e,l,i,y))(s)},get:(t,o={})=>r.request(t,o),post:(t,o={})=>r.request(t,{method:"POST",...o}),put:(t,o={})=>r.request(t,{method:"PUT",...o}),patch:(t,o={})=>r.request(t,{method:"PATCH",...o}),remove:(t,o={})=>r.request(t,{method:"DELETE",...o}),with:t=>J({...e,...t,cache:t.cache??n??!1})};return r}export{$ as DataContext,m as FirsthandDirectiveError,R as FirsthandHttpError,h as anyTagMatches,S as createCacheClient,L as createData,J as createFetchClient,ee as createMemoryStorage,W as fromObservable,X as fromPromise,ce as parseGraphQL,ge as resolveTags,te as serialize,O as stableKey,k as tag,q as tagMatches,Z as useAction,b as useData,U as useInvalidate,j as useResource};
@@ -16,20 +16,6 @@ export type ResourceOptions = {
16
16
  */
17
17
  readonly persist?: string;
18
18
  };
19
- /**
20
- * Loads something, and keeps it as reactive state.
21
- *
22
- * ```tsx
23
- * const user = useResource(async ({ signal, tags }) => {
24
- * tags(tag('user', { id: props.id }));
25
- * return (await fetch(`/api/users/${props.id}`, { signal })).json() as Promise<User>;
26
- * });
27
- * ```
28
- *
29
- * Everything read before the first `await` is a dependency, exactly as in an
30
- * `effect` — including a token read to build a header. Read it with `peek()`
31
- * if that is not what you want.
32
- */
33
19
  export declare function useResource<T>(load: (context: LoadContext) => Promise<T>, options?: ResourceOptions): Resource<T>;
34
20
  export type Action<I, R> = {
35
21
  readonly data: Resource<R>['data'];
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAKL,KAAK,aAAa,EAElB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAiB,KAAK,GAAG,EAAE,MAAM,WAAW,CAAC;AAGpD,eAAO,MAAM,WAAW,gDAA6B,CAAC;AAEtD,wBAAgB,OAAO,IAAI,SAAS,CAEnC;AAED,6DAA6D;AAC7D,wBAAgB,aAAa,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAGrE;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,IAAI,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,OAAO,GAAE,eAAoB,GAC5B,QAAQ,CAAC,CAAC,CAAC,CA2Jb;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,IAAI;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACzC,mEAAmE;IACnE,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAC5B,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GACpD,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CA+Dd;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,SAAS,CAAC,QAAQ,EAAE;QAClB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;QAC1B,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;KAClC,GAAG;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE,aAAkB,GAC1B,QAAQ,CAAC,CAAC,CAAC,CAoCb;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAErE;AAQD,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAKL,KAAK,aAAa,EAElB,KAAK,SAAS,EAEd,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAiB,KAAK,GAAG,EAAE,MAAM,WAAW,CAAC;AAGpD,eAAO,MAAM,WAAW,gDAA6B,CAAC;AAEtD,wBAAgB,OAAO,IAAI,SAAS,CAEnC;AAED,6DAA6D;AAC7D,wBAAgB,aAAa,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAGrE;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAmCF,wBAAgB,WAAW,CAAC,CAAC,EAC3B,IAAI,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,OAAO,GAAE,eAAoB,GAC5B,QAAQ,CAAC,CAAC,CAAC,CA+Lb;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,IAAI;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACzC,mEAAmE;IACnE,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAC5B,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GACpD,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CA+Dd;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,SAAS,CAAC,QAAQ,EAAE;QAClB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;QAC1B,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;KAClC,GAAG;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE,aAAkB,GAC1B,QAAQ,CAAC,CAAC,CAAC,CAoCb;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAErE;AAQD,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC"}
package/dist/store.d.ts CHANGED
@@ -189,6 +189,8 @@ type Held<T = unknown> = {
189
189
  name: string | undefined;
190
190
  /** When this resource last carried an answer. 0 until it has one. */
191
191
  answeredAt: number;
192
+ /** The run that is out, while there is one. What `settle` waits for. */
193
+ inflight: Promise<unknown> | null;
192
194
  run: (force: boolean) => Promise<T | undefined>;
193
195
  };
194
196
  export type DataStore = {
@@ -196,6 +198,16 @@ export type DataStore = {
196
198
  invalidate(...patterns: Tag[]): Promise<void>;
197
199
  /** Forgets every resource and empties the storage. */
198
200
  clear(): void;
201
+ /**
202
+ * Resolves once nothing is loading.
203
+ *
204
+ * What a server render waits on: the view is rendered, this is awaited, and
205
+ * the view is rendered again with the answers in hand. A run that starts
206
+ * another — a resource invalidated while it was out — is waited for too, up
207
+ * to `passes`; past that the render goes ahead with whatever is there
208
+ * rather than never returning.
209
+ */
210
+ settle(passes?: number): Promise<void>;
199
211
  /** How many resources are alive. For tests and devtools. */
200
212
  readonly size: number;
201
213
  /** Internal: a resource registers itself here. */
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Handing a server's answers to the browser.
3
+ *
4
+ * A resource has no key — it belongs to its call site, which is what ADR-0022
5
+ * is about — so there is nothing to serialise it under. A **named** resource
6
+ * does: `persist` is a name the application chose, and a name is exactly what
7
+ * state transfer needs. So transfer is not a new concept here. It is the one
8
+ * that was already there, pointed at a different destination:
9
+ *
10
+ * ```ts
11
+ * // the server
12
+ * const storage = createMemoryStorage();
13
+ * const data = createData({ storage });
14
+ * const body = await renderToStringAsync(() => <App />, {
15
+ * settle: () => data.settle(),
16
+ * });
17
+ * const page = `${body}<script>window.__FIRSTHAND_DATA__ = ${serialize(storage.dump())}</script>`;
18
+ *
19
+ * // the browser
20
+ * const data = createData({ storage: createMemoryStorage(window.__FIRSTHAND_DATA__) });
21
+ * hydrate(() => <App />, document.getElementById('app')!);
22
+ * ```
23
+ *
24
+ * A resource without a `persist` name still renders on the server — it loads,
25
+ * it settles, and its answer is in the markup. What it does not do is arrive
26
+ * in the browser already answered, because there is nothing to put it under.
27
+ * That is a decision the call site makes, in one word, and can see.
28
+ */
29
+ import type { Storage } from './store.js';
30
+ export type MemoryStorage = Storage & {
31
+ /** Everything held, as plain data, for putting in a page. */
32
+ dump(): Record<string, unknown>;
33
+ /** Puts a server's answers in. */
34
+ seed(values: Record<string, unknown>): void;
35
+ /** How many names are held. */
36
+ readonly size: number;
37
+ };
38
+ /**
39
+ * A storage that is a plain object and answers straight away.
40
+ *
41
+ * Answering straight away is the whole point: a resource seeded from here has
42
+ * its value *during* the render that reads it, so the markup a server sends
43
+ * carries data rather than a spinner, and the browser's first paint is that
44
+ * same markup rather than a spinner replacing it.
45
+ *
46
+ * One per request on a server. It holds what that render produced and nothing
47
+ * else, so nothing leaks from one visitor's page into another's.
48
+ */
49
+ export declare function createMemoryStorage(initial?: Record<string, unknown>): MemoryStorage;
50
+ /**
51
+ * JSON for a `<script>` element.
52
+ *
53
+ * `JSON.stringify` alone is not safe there: a string containing `</script>`
54
+ * closes the element, and `<!--` starts a comment the parser does not end
55
+ * where you think. The characters are escaped as unicode, which JSON reads
56
+ * back as themselves.
57
+ */
58
+ export declare function serialize(value: unknown): string;
59
+ //# sourceMappingURL=transfer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../src/transfer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG;IACpC,6DAA6D;IAC7D,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,kCAAkC;IAClC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5C,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,CAoBpF;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEhD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firsthandjs/data",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "Resources, actions and invalidation for Firsthand: reactive async state, with the cache one layer down.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -28,8 +28,8 @@
28
28
  "LICENSE"
29
29
  ],
30
30
  "dependencies": {
31
- "@firsthandjs/core": "0.8.0",
32
- "@firsthandjs/dom": "0.8.0"
31
+ "@firsthandjs/core": "0.9.1",
32
+ "@firsthandjs/dom": "0.9.1"
33
33
  },
34
34
  "engines": {
35
35
  "node": ">=20.11.0"