@firsthandjs/data 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache.d.ts +24 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.dev.js +133 -23
- package/dist/index.js +2 -2
- package/dist/resource.d.ts +0 -14
- package/dist/resource.d.ts.map +1 -1
- package/dist/store.d.ts +12 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/transfer.d.ts +59 -0
- package/dist/transfer.d.ts.map +1 -0
- package/package.json +3 -3
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
|
};
|
package/dist/cache.d.ts.map
CHANGED
|
@@ -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,
|
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
}
|
|
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:
|
|
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
|
|
2
|
-
`,
|
|
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};
|
package/dist/resource.d.ts
CHANGED
|
@@ -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'];
|
package/dist/resource.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"
|
|
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. */
|
package/dist/store.d.ts.map
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "0.9.0",
|
|
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.
|
|
32
|
-
"@firsthandjs/dom": "0.
|
|
31
|
+
"@firsthandjs/core": "0.9.0",
|
|
32
|
+
"@firsthandjs/dom": "0.9.0"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
|
35
35
|
"node": ">=20.11.0"
|