@firsthandjs/data 0.6.2 → 0.7.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/README.md CHANGED
@@ -67,6 +67,10 @@ components asking at once make one request, which is waste removed rather than
67
67
  staleness introduced. `force` drops the entry, which is how an invalidation
68
68
  reaches all the way down.
69
69
 
70
+ **An action never touches it** — not read from, not written to, whatever its
71
+ method or key. A cache holds representations, and what an action gets back is
72
+ the answer to doing something.
73
+
70
74
  A key is an **identity** and a **request**: `GET /api/me` is the same URL for
71
75
  everybody, so the identity — the `authorization` header by default, or a
72
76
  `scope` function you give — is what stops one account being served the answer
package/dist/cache.d.ts CHANGED
@@ -34,9 +34,20 @@
34
34
  * is the point where the store's invalidation and the transport's memory
35
35
  * meet, and without it an invalidation would be answered out of the cache it
36
36
  * was meant to defeat.
37
- * - **Forgets.** `forget(key)`, `forget()` for all of it, and the oldest entry
38
- * goes when `max` is reached.
37
+ * - **Stays out of an action's way.** A `mutating` request is run and nothing
38
+ * else: not served from here, not shared with anybody, and not kept.
39
+ * - **Forgets.** `forget(key)`, `forget()` for all of it, `forgetTagged(…)`
40
+ * for everything an invalidation was about, and the oldest entry goes when
41
+ * `max` is reached.
42
+ *
43
+ * The last of those is the one worth explaining. An entry remembers what the
44
+ * request said it was about, and a store that is given this cache throws those
45
+ * entries away when it invalidates — so a list nobody is watching is not
46
+ * served a stale answer when somebody walks back to it. The tags are
47
+ * **metadata, never the key**: identity is still the scope and the request,
48
+ * which is what ADR-0022 is about and what this deliberately does not undo.
39
49
  */
50
+ import { type Tag } from './tags.js';
40
51
  import type { Loader } from './store.js';
41
52
  /**
42
53
  * A key for a value, stable however the value was written.
@@ -89,6 +100,15 @@ export interface CacheClient {
89
100
  peek(key: string): unknown;
90
101
  /** Forgets one key, or everything. */
91
102
  forget(key?: string): void;
103
+ /**
104
+ * Forgets every entry whose request said it was about one of these.
105
+ *
106
+ * What `store.invalidate` calls on a cache it was given. Matching is the
107
+ * tags' own rule — fewer variables match more — and an entry that never said
108
+ * what it was about is never matched, because nothing can be concluded about
109
+ * it.
110
+ */
111
+ forgetTagged(patterns: readonly Tag[]): void;
92
112
  /** How many entries are held, in flight included. */
93
113
  readonly size: number;
94
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAmBhD;AAED,MAAM,WAAW,YAAY;IAC3B;;;;;;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;AAED,MAAM,WAAW,WAAW;IAC1B;;;;;;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,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAaD,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,YAAiB,GAAG,WAAW,CAkHzE"}
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,WAAW,YAAY;IAC3B;;;;;;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;AAED,MAAM,WAAW,WAAW;IAC1B;;;;;;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;AAeD,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,YAAiB,GAAG,WAAW,CAmIzE"}
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAEzC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,OAAO;gBAHb,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM;IACpB,yCAAyC;IAChC,IAAI,EAAE,OAAO;CAKzB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAC;IACrD;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,YAAY,GAAG,WAAW,CAAC;IACpD,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAC1C;AAED,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACvD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACnD,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACnD,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACrD,mEAAmE;IACnE,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACtD;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAAC;IAC/C,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;CACzC;AAmDD,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CA6D/E"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAEzC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,OAAO;gBAHb,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM;IACpB,yCAAyC;IAChC,IAAI,EAAE,OAAO;CAKzB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAC;IACrD;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,YAAY,GAAG,WAAW,CAAC;IACpD,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAC1C;AAED,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACvD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACnD,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACnD,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACrD,mEAAmE;IACnE,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACtD;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAAC;IAC/C,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;CACzC;AAmDD,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CAoE/E"}
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  export { createData, DataContext, useData, useInvalidate } from './resource.js';
10
10
  export { useResource, useAction, fromObservable, fromPromise } from './resource.js';
11
11
  export type { Action, ObservableLike, BridgeOptions, ResourceOptions } from './resource.js';
12
- export type { ActionContext, DataOptions, DataRequest, DataStore, LoadContext, Loader, Resource, Status, Storage, } from './store.js';
12
+ export type { ActionContext, DataOptions, DataRequest, Forgetful, DataStore, LoadContext, Loader, Resource, Status, Storage, } from './store.js';
13
13
  /**
14
14
  * The cache — one of them, and the transport's.
15
15
  *
@@ -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,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,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
@@ -57,14 +57,42 @@ function readable(value) {
57
57
  // packages/data/src/store.ts
58
58
  function createData(options = {}) {
59
59
  const held = /* @__PURE__ */ new Set();
60
+ const remember = options.remember ?? 6e4;
61
+ let recent = [];
62
+ const now = () => Date.now();
60
63
  return {
61
64
  storage: options.storage,
65
+ missed: (entry, tags) => {
66
+ if (remember === 0 || recent.length === 0) {
67
+ return false;
68
+ }
69
+ const since = now() - remember;
70
+ recent = recent.filter((one) => one.at >= since);
71
+ return recent.some(
72
+ // Younger than this resource's last answer, and about what it has just
73
+ // said it is about. A resource that has answered *since* the
74
+ // invalidation has already taken it into account.
75
+ (one) => one.at > entry.answeredAt && anyTagMatches(one.patterns, tags)
76
+ );
77
+ },
78
+ settled: (tags) => {
79
+ if (recent.length === 0) {
80
+ return;
81
+ }
82
+ recent = recent.filter((one) => !anyTagMatches(one.patterns, tags));
83
+ },
62
84
  hold: (entry) => {
63
85
  held.add(entry);
64
86
  return () => held.delete(entry);
65
87
  },
66
88
  invalidate: async (...patterns) => {
67
89
  const waiting = [];
90
+ for (const cache of options.caches ?? []) {
91
+ cache.forgetTagged(patterns);
92
+ }
93
+ if (remember > 0) {
94
+ recent.push({ patterns, at: now() });
95
+ }
68
96
  for (const entry of [...held]) {
69
97
  if (entry.controller !== null) {
70
98
  entry.pending.push(...patterns);
@@ -78,6 +106,7 @@ function createData(options = {}) {
78
106
  await Promise.all(waiting);
79
107
  },
80
108
  clear: () => {
109
+ recent = [];
81
110
  for (const entry of [...held]) {
82
111
  entry.controller?.abort();
83
112
  held.delete(entry);
@@ -103,11 +132,13 @@ function createHeld(store, name) {
103
132
  pending: [],
104
133
  superseded: false,
105
134
  disposed: false,
135
+ answeredAt: 0,
106
136
  name
107
137
  };
108
138
  return entry;
109
139
  }
110
140
  function succeed(entry, value) {
141
+ entry.answeredAt = Date.now();
111
142
  batch(() => {
112
143
  entry.data.value = value;
113
144
  entry.error.value = void 0;
@@ -164,20 +195,43 @@ function useResource(load, options = {}) {
164
195
  if (entry.data.peek() === void 0) {
165
196
  entry.status.value = "loading";
166
197
  }
198
+ let forced = force;
167
199
  const declare = (...next) => {
168
200
  entry.tags = next;
201
+ if (!forced && store.missed(entry, next)) {
202
+ forced = true;
203
+ }
169
204
  if (entry.pending.length > 0 && anyTagMatches(entry.pending, next)) {
170
205
  entry.superseded = true;
171
206
  }
172
207
  };
173
- const request = { signal: controller.signal, force, tags: declare };
174
- const context = { ...request, request, tags: declare };
208
+ const request = {
209
+ signal: controller.signal,
210
+ get force() {
211
+ return forced;
212
+ },
213
+ // Read after `tags()` by a cache that keeps them, which is every client
214
+ // in this project: they declare first and look in their cache second.
215
+ get declared() {
216
+ return entry.tags;
217
+ },
218
+ tags: declare
219
+ };
220
+ const context = {
221
+ signal: controller.signal,
222
+ get force() {
223
+ return forced;
224
+ },
225
+ request,
226
+ tags: declare
227
+ };
175
228
  return load(context).then(async (value) => {
176
229
  if (controller.signal.aborted || entry.disposed) {
177
230
  return void 0;
178
231
  }
179
232
  entry.controller = null;
180
233
  succeed(entry, value);
234
+ store.settled(entry.tags);
181
235
  if (options.persist !== void 0) {
182
236
  try {
183
237
  store.storage?.write?.(options.persist, value);
@@ -251,12 +305,17 @@ function useAction(run) {
251
305
  () => run(input, {
252
306
  signal: current.signal,
253
307
  invalidates,
254
- // An action changes something, so nothing it sends may be answered
255
- // out of a cache: `force` is not a choice here. `tags` is the
256
- // store's invalidation, so a client that knows what a mutation
257
- // changed — a document with `@invalidates` — reports it without
258
- // the call site repeating it.
259
- request: { signal: current.signal, force: true, tags: invalidates }
308
+ // An action changes something, so nothing it sends may be
309
+ // answered out of a cache (`force`) or kept in one (`mutating`).
310
+ // `tags` is the store's invalidation, so a client that knows what
311
+ // a mutation changed — a document with `@invalidates` — reports it
312
+ // without the call site repeating it.
313
+ request: {
314
+ signal: current.signal,
315
+ force: true,
316
+ mutating: true,
317
+ tags: invalidates
318
+ }
260
319
  })
261
320
  );
262
321
  if (current.signal.aborted || entry.disposed) {
@@ -366,6 +425,7 @@ function createCacheClient(options = {}) {
366
425
  write: (key, value) => {
367
426
  keep(key, {
368
427
  value,
428
+ tags: [],
369
429
  expires: ttl === 0 ? Infinity : now() + ttl,
370
430
  inflight: null,
371
431
  controller: null,
@@ -381,7 +441,17 @@ function createCacheClient(options = {}) {
381
441
  }
382
442
  drop(key);
383
443
  },
444
+ forgetTagged: (patterns) => {
445
+ for (const [key, entry] of [...entries]) {
446
+ if (entry.tags.length > 0 && anyTagMatches(patterns, entry.tags)) {
447
+ drop(key);
448
+ }
449
+ }
450
+ },
384
451
  read: (key, produce) => async (request) => {
452
+ if (request.mutating === true) {
453
+ return await produce(request);
454
+ }
385
455
  const held = entries.get(key);
386
456
  if (request.force) {
387
457
  drop(key);
@@ -398,6 +468,9 @@ function createCacheClient(options = {}) {
398
468
  const controller = new AbortController();
399
469
  const entry = {
400
470
  value: void 0,
471
+ // What the request has been declared to be about by now. A client
472
+ // declares before it looks here, which is what makes this possible.
473
+ tags: request.declared ?? [],
401
474
  expires: 0,
402
475
  inflight: null,
403
476
  controller,
@@ -697,7 +770,9 @@ function createFetchClient(options = {}) {
697
770
  const scope = options.scope === void 0 ? merged.headers.get("authorization") ?? "" : untrack2(options.scope);
698
771
  const named = typeof init.cacheKey === "string" ? init.cacheKey : method === "GET" || method === "HEAD" ? `${method} ${target}` : void 0;
699
772
  const key = named === void 0 ? void 0 : `${scope}\0${named}`;
700
- if (cache === void 0 || init.cacheKey === false || key === void 0) {
773
+ if (cache === void 0 || init.cacheKey === false || key === void 0 || // An action. Even a `cacheKey` does not put its answer in here: the
774
+ // call site asked for a key, not for its writes to be remembered.
775
+ request.mutating === true) {
701
776
  return await send(options, target, merged, request);
702
777
  }
703
778
  return await cache.read(key, (shared) => send(options, target, merged, shared))(
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{createContext as z,effect as N,onCleanup as D,untrack as Q,useContext as _}from"@firsthandjs/core";import{batch as S,signal as b}from"@firsthandjs/core";var J=Object.freeze({});function R(e,r=J){return{name:e,vars:r}}function L(e,r){if(e.name!==r.name)return!1;for(let n of Object.keys(e.vars))if(!Object.is(e.vars[n],r.vars[n]))return!1;return!0}function m(e,r){for(let n of e)for(let t of r)if(L(n,t))return!0;return!1}function P(e={}){let r=new Set;return{storage:e.storage,hold:n=>(r.add(n),()=>r.delete(n)),invalidate:async(...n)=>{let t=[];for(let a of[...r])a.controller!==null&&a.pending.push(...n),m(n,a.tags)&&(a.name,a.tags,t.push(a.run(!0)));await Promise.all(t)},clear:()=>{for(let n of[...r])n.controller?.abort(),r.delete(n);try{e.storage?.clear?.()}catch{}},get size(){return r.size}}}function x(e,r){return{data:b(void 0),error:b(void 0),status:b("idle"),loading:b(!1),tags:[],controller:null,pending:[],superseded:!1,disposed:!1,name:r}}function h(e,r){S(()=>{e.data.value=r,e.error.value=void 0,e.status.value="success",e.loading.value=!1})}function v(e,r){S(()=>{e.error.value=r,e.status.value="error",e.loading.value=!1})}function C(e,r){return{data:e.data,error:e.error,status:e.status,loading:e.loading,reload:()=>e.run(!0),dispose:()=>{e.disposed=!0,e.controller?.abort(),r()}}}var A=z();function y(){return _(A).value}function G(){let e=y();return(...r)=>e.invalidate(...r)}function V(e,r={}){let n=y(),t=x(n,r.persist),a=n.hold(t);r.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 u=(...d)=>{t.tags=d,t.pending.length>0&&m(t.pending,d)&&(t.superseded=!0)},c={signal:i.signal,force:o,tags:u},g={...c,request:c,tags:u};return e(g).then(async d=>{if(!(i.signal.aborted||t.disposed)){if(t.controller=null,h(t,d),r.persist!==void 0)try{n.storage?.write?.(r.persist,d)}catch{}return t.superseded?(t.superseded=!1,t.run(!0)):d}}).catch(d=>{i.signal.aborted||t.disposed||(t.controller=null,v(t,d))})};let s=r.persist,l=n.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&&(h(t,o),t.loading.value=!0)}catch{}})(),N(()=>{t.run(!1)}),D(()=>{t.disposed=!0,t.controller?.abort(),r.persist,t.tags,a()}),C(t,a)}function M(e){let r=y(),n=x(r,void 0),t=null;return D(()=>{n.disposed=!0,t?.abort()}),{data:n.data,error:n.error,status:n.status,running:n.loading,run:async a=>{t?.abort(),t=new AbortController;let s=t,l=[];n.loading.value=!0,n.status.value="loading";try{let o=(...u)=>{l=u},i=await Q(()=>e(a,{signal:s.signal,invalidates:o,request:{signal:s.signal,force:!0,tags:o}}));return s.signal.aborted||n.disposed?void 0:(h(n,i),l.length>0&&await r.invalidate(...l),i)}catch(o){if(s.signal.aborted||n.disposed)return;v(n,o);return}}}}function K(e,r={}){let n=y(),t=x(n,void 0),a=n.hold(t);t.run=()=>r.reload===void 0?Promise.resolve(void 0):U(r.reload()),t.loading.value=!0,t.status.value="loading";let s=e.subscribe({next:o=>{h(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(([,n])=>n!==void 0).sort(([n],[t])=>n<t?-1:1).map(([n,t])=>`${n}:${k(t)}`).join(",")}}`}function O(e={}){let r=e.ttl??0,n=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>n)for(let u of a.keys()){s(u);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,expires:r===0?1/0:t()+r,inflight:null,controller:null,waiting:0})},forget:o=>{if(o===void 0){for(let i of[...a.keys()])s(i);return}s(o)},read:(o,i)=>async u=>{let c=a.get(o);if(u.force)s(o);else if(c!==void 0){if(c.inflight!==null)return await $(c,u);if(c.expires>t())return l(o,c),c.value;a.delete(o)}let g=new AbortController,d={value:void 0,expires:0,inflight:null,controller:g,waiting:0},T=i({signal:g.signal,force:u.force}).then(p=>(a.get(o)===d&&(r===0?a.delete(o):(d.value=p,d.expires=t()+r,d.inflight=null,d.controller=null)),p)).catch(p=>{throw a.get(o)===d&&a.delete(o),p});return d.inflight=T,l(o,d),await $(d,u)}}}async function $(e,r){e.waiting+=1;let n=()=>{e.waiting<=1&&e.inflight!==null&&e.controller?.abort()};r.signal.addEventListener("abort",n,{once:!0});try{return await e.inflight}finally{r.signal.removeEventListener("abort",n),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]*/,f=class extends Error{constructor(r){super(r),this.name="FirsthandDirectiveError"}};function q(e,r){if(e.startsWith('"""',r)){let t=e.indexOf('"""',r+3);return t===-1?e.length:t+3}let n=r+1;for(;n<e.length&&e[n]!=='"';)n+=e[n]==="\\"?2:1;return n+1}function X(e,r){let n=0,t=r;for(;t<e.length;){let a=e[t];if(a==='"'){t=q(e,t);continue}if(a==="("||a==="["||a==="{")n++;else if((a===")"||a==="]"||a==="}")&&(n--,n===0))return t+1;t++}throw new f(`unclosed arguments in ${e.slice(r,r+40)}`)}function Y(e,r){try{return JSON.parse(e)}catch{throw new f(`@${r}: ${e} is not a valid string`)}}function ee(e,r){let n={},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 f(`@${r}: expected an argument name at "${e.slice(t)}"`);if(t+=s[0].length,a(),e[t]!==":")throw new f(`@${r}: argument ${s[0]} has no value`);t++,a();let l=te(e,r,t);n[s[0]]=l.value,t=l.next,a()}return n}function te(e,r,n){let t=e[n];if(t==="$"){let i=E.exec(e.slice(n+1));if(i===null)throw new f(`@${r}: expected a variable name after $`);return{value:{variable:i[0]},next:n+1+i[0].length}}if(t==='"'){let i=q(e,n),u=e.slice(n,i);return{value:{literal:u.startsWith('"""')?u.slice(3,-3).trim():Y(u,r)},next:i}}if(t==="["||t==="{")throw new f(`@${r}: a tag variable must be a scalar, not a list or object`);let a=/^[^\s,)]+/.exec(e.slice(n));if(a===null)throw new f(`@${r}: expected a value`);let s=a[0],l=n+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 r=[],n=[],t="",a=0,s=0;for(;a<e.length;){let l=e[a];if(l==='"'){a=q(e,a);continue}if(l==="#"){let p=e.indexOf(`
2
- `,a);a=p===-1?e.length:p;continue}if(l!=="@"){a++;continue}let o=Z.exec(e.slice(a));if(o===null){a++;continue}let i=a+o[0].length,u="",c=i;for(;c<e.length&&/\s/.test(e[c]);)c++;if(e[c]==="("){let p=X(e,c);u=e.slice(c+1,p-1),i=p}let g=ee(u,o[1]),d=g.name;if(d===void 0||!("literal"in d)||typeof d.literal!="string")throw new f(`@${o[1]} needs a literal name, as in @${o[1]}(name: "user", id: $id)`);delete g.name,(o[1]==="tag"?r:n).push({name:d.literal,vars:g});let T=a;for(;T>s&&/\s/.test(e[T-1]);)T--;t+=e.slice(s,T),s=i,a=i}return{tags:r,invalidates:n,stripped:t+e.slice(s)}}function re(e){let{tags:r,invalidates:n,stripped:t}=ne(e),a=W.exec(t.replace(/#[^\n]*/g,""));return{source:t,operation:a?.[2]??"",kind:a?.[1]??"query",tags:r,invalidates:n}}function ae(e,r){return e.map(n=>{let t={};for(let[a,s]of Object.entries(n.vars)){if("literal"in s){t[a]=s.literal;continue}let l=r[s.variable];l!=null&&(t[a]=typeof l=="object"?JSON.stringify(l):l)}return R(n.name,t)})}import{untrack as j}from"@firsthandjs/core";var w=class extends Error{constructor(n,t,a){super(`HTTP ${String(n)} for ${t}`);this.status=n;this.url=t;this.body=a;this.name="FirsthandHttpError"}status;url;body};function oe(e,r,n){let t=/^[a-z]+:\/\//i.test(r)?r:`${e.baseUrl??""}${r}`,{json:a,cacheKey:s,...l}=n,o={...e.init,...l},i=new Headers(typeof e.headers=="function"?j(e.headers):e.headers);for(let[u,c]of new Headers(n.headers??{}))i.set(u,c);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 I(e,r,n,t){let s=await(e.fetch??globalThis.fetch)(r,{...n,signal:t.signal}),l=await s.text(),o=l===""?void 0:JSON.parse(l);if(!s.ok)throw new w(s.status,r,o);return o}function H(e={}){let r=e.cache===void 0||e.cache===!1?void 0:"read"in e.cache?e.cache:O(e.cache),n={cache:r,request:(t,a={})=>async s=>{let[l,o]=oe(e,t,a),i=(o.method??"GET").toUpperCase(),u=e.scope===void 0?o.headers.get("authorization")??"":j(e.scope),c=typeof a.cacheKey=="string"?a.cacheKey:i==="GET"||i==="HEAD"?`${i} ${l}`:void 0,g=c===void 0?void 0:`${u}\0${c}`;return r===void 0||a.cacheKey===!1||g===void 0?await I(e,l,o,s):await r.read(g,d=>I(e,l,o,d))(s)},get:(t,a={})=>n.request(t,a),post:(t,a={})=>n.request(t,{method:"POST",...a}),put:(t,a={})=>n.request(t,{method:"PUT",...a}),patch:(t,a={})=>n.request(t,{method:"PATCH",...a}),remove:(t,a={})=>n.request(t,{method:"DELETE",...a}),with:t=>H({...e,...t,cache:t.cache??r??!1})};return n}export{A as DataContext,f as FirsthandDirectiveError,w as FirsthandHttpError,m as anyTagMatches,O as createCacheClient,P as createData,H as createFetchClient,K as fromObservable,B as fromPromise,re as parseGraphQL,ae as resolveTags,k as stableKey,R as tag,L as tagMatches,M as useAction,y as useData,G as useInvalidate,V as useResource};
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 m(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&&m(i.patterns,l))},settled:s=>{t.length!==0&&(t=t.filter(l=>!m(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),m(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 y(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=(...g)=>{t.tags=g,!d&&r.missed(t,g)&&(d=!0),t.pending.length>0&&m(t.pending,g)&&(t.superseded=!0)},f={signal:i.signal,get force(){return d},get declared(){return t.tags},tags:u},c={signal:i.signal,get force(){return d},request:f,tags:u};return e(c).then(async g=>{if(!(i.signal.aborted||t.disposed)){if(t.controller=null,y(t,g),r.settled(t.tags),n.persist!==void 0)try{r.storage?.write?.(n.persist,g)}catch{}return t.superseded?(t.superseded=!1,t.run(!0)):g}}).catch(g=>{i.signal.aborted||t.disposed||(t.controller=null,v(t,g))})};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&&(y(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:(y(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=>{y(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&&m(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,c={value:void 0,tags:d.declared??[],expires:0,inflight:null,controller:f,waiting:0},g=i({signal:f.signal,force:d.force}).then(T=>(a.get(o)===c&&(n===0?a.delete(o):(c.value=T,c.expires=t()+n,c.inflight=null,c.controller=null)),T)).catch(T=>{throw a.get(o)===c&&a.delete(o),T});return c.inflight=g,l(o,c),await $(c,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 T=e.indexOf(`
2
+ `,a);a=T===-1?e.length:T;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 T=X(e,u);d=e.slice(u+1,T-1),i=T}let f=ee(d,o[1]),c=f.name;if(c===void 0||!("literal"in c)||typeof c.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:c.literal,vars:f});let g=a;for(;g>s&&/\s/.test(e[g-1]);)g--;t+=e.slice(s,g),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,c=>F(e,l,o,c))(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,m 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 +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,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;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,CAqGb;AAED,MAAM,WAAW,MAAM,CAAC,CAAC,EAAE,CAAC;IAC1B,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;AAED;;;;;;;;;;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,CA0Dd;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,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;AAED,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;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":"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,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;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,CAqIb;AAED,MAAM,WAAW,MAAM,CAAC,CAAC,EAAE,CAAC;IAC1B,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;AAED;;;;;;;;;;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,WAAW,cAAc,CAAC,CAAC;IAC/B,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;AAED,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;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
@@ -39,6 +39,30 @@ export interface DataRequest {
39
39
  * answer — which is what makes an invalidation reach all the way down.
40
40
  */
41
41
  readonly force: boolean;
42
+ /**
43
+ * True when this request is part of an action: it changes something.
44
+ *
45
+ * Nothing an action sends touches the cache — not read from it, which
46
+ * `force` already prevents, and **not written to it**. A cache holds
47
+ * representations, and what an action gets back is the answer to *doing*
48
+ * something: a mutation result, a receipt, a recalculated report. Storing it
49
+ * under the URL it was sent to means a later read is served an answer that
50
+ * was never a representation of anything, and nothing looks wrong on the way
51
+ * there.
52
+ *
53
+ * It is separate from `force` because the two say different things. `force`
54
+ * is "do not answer me from memory"; this is "do not remember me".
55
+ */
56
+ readonly mutating?: boolean;
57
+ /**
58
+ * What this request has been said to be about, if anything.
59
+ *
60
+ * A cache may keep it beside the entry — as metadata, never as the key —
61
+ * so that an invalidation can throw the entry away instead of waiting for
62
+ * somebody to ask for it again. Identity stays what it was: the scope and
63
+ * the request (ADR-0022, ADR-0025).
64
+ */
65
+ readonly declared?: readonly Tag[];
42
66
  /**
43
67
  * Where a client reports what this request is about, when it knows.
44
68
  *
@@ -108,6 +132,46 @@ export interface Storage {
108
132
  export interface DataOptions {
109
133
  /** Where named resources are kept between visits. */
110
134
  readonly storage?: Storage;
135
+ /**
136
+ * Caches to empty when something is invalidated.
137
+ *
138
+ * A cache given here is told which tags were invalidated, and drops the
139
+ * entries whose requests said they were about them — so a list nobody is
140
+ * watching does not keep a stale answer for anybody who walks back to it.
141
+ * The tags are metadata on the entry; identity is still the scope and the
142
+ * request (ADR-0025).
143
+ *
144
+ * A cache that is *not* given here is not touched, which is the right
145
+ * default for somebody else's: Apollo's and urql's caches are theirs, and
146
+ * `force` is how a run reaches past them.
147
+ */
148
+ readonly caches?: readonly Forgetful[];
149
+ /**
150
+ * How long an invalidation is remembered for resources that did not exist
151
+ * when it happened, in ms. Default 60 000; `0` switches it off.
152
+ *
153
+ * An invalidation reaches every resource that is **alive**. Nothing is
154
+ * watching a list while you are two pages away from it, so a mutation there
155
+ * reaches nothing — and walking back creates a *new* resource, which asks
156
+ * the transport, which may still be holding the answer from before.
157
+ *
158
+ * So the store remembers what was invalidated and when. A resource whose
159
+ * first run declares a tag that was invalidated since it was created runs
160
+ * with `force`, which is what makes it reach past a cache exactly once. The
161
+ * window exists because the memory is about a cache's contents, and a cache
162
+ * entry does not live for ever; a minute is longer than any sensible `ttl`
163
+ * and short enough to be forgotten.
164
+ */
165
+ readonly remember?: number;
166
+ }
167
+ /**
168
+ * The part of a cache a store touches: what to forget, and nothing else.
169
+ *
170
+ * Declared structurally so that the store depends on no cache in particular —
171
+ * ours satisfies it, and so does thirty lines of somebody's own.
172
+ */
173
+ export interface Forgetful {
174
+ forgetTagged(patterns: readonly Tag[]): void;
111
175
  }
112
176
  interface Held<T = unknown> {
113
177
  data: Signal<T | undefined>;
@@ -123,6 +187,8 @@ interface Held<T = unknown> {
123
187
  superseded: boolean;
124
188
  disposed: boolean;
125
189
  name: string | undefined;
190
+ /** When this resource last carried an answer. 0 until it has one. */
191
+ answeredAt: number;
126
192
  run: (force: boolean) => Promise<T | undefined>;
127
193
  }
128
194
  export interface DataStore {
@@ -134,6 +200,16 @@ export interface DataStore {
134
200
  readonly size: number;
135
201
  /** Internal: a resource registers itself here. */
136
202
  hold(held: Held): () => void;
203
+ /**
204
+ * Internal: whether an invalidation that this resource missed applies to the
205
+ * tags it has just declared.
206
+ */
207
+ missed(entry: Held, tags: readonly Tag[]): boolean;
208
+ /**
209
+ * Internal: a run carrying these tags has answered, so an invalidation about
210
+ * them has been acted on and is no longer owed to anybody.
211
+ */
212
+ settled(tags: readonly Tag[]): void;
137
213
  /** Internal: the storage this store was given. */
138
214
  readonly storage: Storage | undefined;
139
215
  }
@@ -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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firsthandjs/data",
3
- "version": "0.6.2",
3
+ "version": "0.7.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.6.2",
32
- "@firsthandjs/dom": "0.6.2"
31
+ "@firsthandjs/core": "0.7.0",
32
+ "@firsthandjs/dom": "0.7.0"
33
33
  },
34
34
  "engines": {
35
35
  "node": ">=20.11.0"