@floegence/floe-webapp-core 0.68.0 → 0.69.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.
@@ -38,6 +38,8 @@ export interface CachedResourceDefinition<T> {
38
38
  }
39
39
  export interface ResourceCache {
40
40
  resource<T>(definition: CachedResourceDefinition<T>): CachedResource<T>;
41
+ /** Abort pending refreshes without retiring snapshots, storage, hydration or subscribers. */
42
+ cancelRefreshes(scope: string): void;
41
43
  clearScope(scope: string): Promise<void>;
42
44
  flush(): Promise<void>;
43
45
  dispose(): void;
@@ -1,177 +1,186 @@
1
- async function N(h, i) {
2
- const g = [...await h.list()].sort((e, t) => e.lastAccessedAt - t.lastAccessedAt);
3
- let l = g.reduce((e, t) => e + t.bytes, 0);
4
- const n = [];
5
- for (const e of g) {
6
- if (l <= Math.max(0, i)) break;
7
- await h.remove(e.key), n.push(e.key), l -= e.bytes;
1
+ async function J(y, l) {
2
+ const m = [...await y.list()].sort((r, s) => r.lastAccessedAt - s.lastAccessedAt);
3
+ let i = m.reduce((r, s) => r + s.bytes, 0);
4
+ const a = [];
5
+ for (const r of m) {
6
+ if (i <= Math.max(0, l)) break;
7
+ await y.remove(r.key), a.push(r.key), i -= r.bytes;
8
8
  }
9
- return n;
9
+ return a;
10
10
  }
11
- function I(h) {
12
- const i = h.storage, g = Math.max(0, h.maxBytes ?? 32 * 1024 * 1024), l = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
13
- let e, t = Promise.resolve(), o = !1;
14
- const d = (s) => (t = t.then(s).catch(() => {
15
- }), t), v = async () => {
16
- const s = await N({
17
- list: async () => (await i.list()).map((r) => ({ ...r, lastAccessedAt: Math.max(r.lastAccessedAt, l.get(r.key)?.accessedAt ?? 0) })),
18
- remove: (r) => i.remove(r)
19
- }, g);
20
- for (const r of s) l.get(r)?.evicted();
21
- }, y = async () => {
22
- clearTimeout(e), e = void 0;
23
- const s = [...n];
24
- n.clear(), s.length && d(async () => {
25
- for (const [r, f] of s)
26
- if (f.valid())
11
+ function z(y) {
12
+ const l = y.storage, m = Math.max(0, y.maxBytes ?? 32 * 1024 * 1024), i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map();
13
+ let r, s = Promise.resolve(), n = !1;
14
+ const d = (t) => (s = s.then(t).catch(() => {
15
+ }), s), f = async () => {
16
+ const t = await J({
17
+ list: async () => (await l.list()).map((e) => ({ ...e, lastAccessedAt: Math.max(e.lastAccessedAt, i.get(e.key)?.accessedAt ?? 0) })),
18
+ remove: (e) => l.remove(e)
19
+ }, m);
20
+ for (const e of t) i.get(e)?.evicted();
21
+ }, p = async () => {
22
+ clearTimeout(r), r = void 0;
23
+ const t = [...a];
24
+ a.clear(), t.length && d(async () => {
25
+ for (const [e, v] of t)
26
+ if (v.valid())
27
27
  try {
28
- await i.set(r, f.value), f.stored();
28
+ await l.set(e, v.value), v.stored();
29
29
  } catch {
30
30
  }
31
- await v();
32
- }), await t;
33
- }, k = (s, r, f, a) => {
34
- n.set(s, { value: r, valid: f, stored: a }), e === void 0 && (e = setTimeout(() => {
35
- y();
36
- }, h.writeDelayMs ?? 100));
31
+ await f();
32
+ }), await s;
33
+ }, x = (t, e, v, o) => {
34
+ a.set(t, { value: e, valid: v, stored: o }), r === void 0 && (r = setTimeout(() => {
35
+ p();
36
+ }, y.writeDelayMs ?? 100));
37
37
  };
38
38
  return {
39
- resource: (s) => {
40
- if (o) throw new Error("Resource cache is disposed");
41
- if (!s.scope || !s.key || !Number.isSafeInteger(s.version)) throw new Error("Invalid resource identity");
42
- const r = JSON.stringify([s.scope, s.key, s.version]), f = l.get(r);
43
- if (f)
44
- return f.accessedAt = Date.now(), f.handle;
45
- let a = { data: void 0, restoring: !0, refreshing: !1, stale: !0, error: void 0 };
46
- const D = /* @__PURE__ */ new Set();
47
- let R = 0, p = 0, q, x, b, w;
48
- const m = () => {
49
- for (const c of D) c();
50
- }, E = (c) => {
51
- ++R, a = { data: c, restoring: !1, refreshing: a.refreshing, stale: !1, error: void 0 };
39
+ resource: (t) => {
40
+ if (n) throw new Error("Resource cache is disposed");
41
+ if (!t.scope || !t.key || !Number.isSafeInteger(t.version)) throw new Error("Invalid resource identity");
42
+ const e = JSON.stringify([t.scope, t.key, t.version]), v = i.get(e);
43
+ if (v)
44
+ return v.accessedAt = Date.now(), v.handle;
45
+ let o = { data: void 0, restoring: !0, refreshing: !1, stale: !0, error: void 0 };
46
+ const S = /* @__PURE__ */ new Set();
47
+ let D = 0, w = 0, E, R, g, h, M;
48
+ const b = () => {
49
+ for (const c of S) c();
50
+ }, P = (c) => {
51
+ ++D, o = { data: c, restoring: !1, refreshing: o.refreshing, stale: !1, error: void 0 };
52
52
  try {
53
- const u = JSON.stringify({ version: s.version, data: s.persist ? s.persist(c) : c });
54
- q = u, u !== x ? k(r, u, () => q === u, () => {
55
- x = u;
56
- }) : n.delete(r);
53
+ const u = JSON.stringify({ version: t.version, data: t.persist ? t.persist(c) : c });
54
+ E = u, u !== R ? x(e, u, () => E === u, () => {
55
+ R = u;
56
+ }) : a.delete(e);
57
57
  } catch {
58
58
  }
59
- m();
60
- }, B = R, C = i.get(r).then((c) => {
61
- if (!(o || R !== B || c === null))
59
+ b();
60
+ }, C = D, O = l.get(e).then((c) => {
61
+ if (!(n || D !== C || c === null))
62
62
  try {
63
63
  const u = JSON.parse(c);
64
- if (u.version !== s.version) throw new Error("Unsupported snapshot");
65
- const A = s.decode(u.data);
66
- a = { ...a, data: A, stale: !0 }, q = c, x = c;
64
+ if (u.version !== t.version) throw new Error("Unsupported snapshot");
65
+ const A = t.decode(u.data);
66
+ o = { ...o, data: A, stale: !0 }, E = c, R = c;
67
67
  } catch {
68
- d(() => i.remove(r));
68
+ d(() => l.remove(e));
69
69
  }
70
70
  }).catch(() => {
71
71
  }).finally(() => {
72
- o || !a.restoring || (a = { ...a, restoring: !1 }, m());
73
- }), M = {
74
- snapshot: () => a,
75
- subscribe: (c) => (D.add(c), () => {
76
- D.delete(c);
72
+ n || !o.restoring || (o = { ...o, restoring: !1 }, b());
73
+ }), q = {
74
+ snapshot: () => o,
75
+ subscribe: (c) => (S.add(c), () => {
76
+ S.delete(c);
77
77
  }),
78
- hydrate: () => C,
78
+ hydrate: () => O,
79
79
  refresh: (c) => {
80
- if (w) return w;
81
- const u = ++p, A = new AbortController();
82
- return b = A, a = { ...a, refreshing: !0, error: void 0 }, m(), w = Promise.resolve().then(() => c(A.signal)).then((S) => (!o && u === p && !A.signal.aborted && E(S), S), (S) => {
83
- throw !o && u === p && !A.signal.aborted && (a = { ...a, stale: !0, error: S }, m()), S;
84
- }).finally(() => {
85
- u === p && (w = void 0, b = void 0, a = { ...a, refreshing: !1 }, m());
86
- }), w;
80
+ if (h) return h;
81
+ const u = ++w, A = new AbortController();
82
+ g = A, o = { ...o, refreshing: !0, error: void 0 }, b();
83
+ const B = new Promise((k, I) => {
84
+ M = () => I(new DOMException("Resource refresh cancelled", "AbortError"));
85
+ }), N = Promise.resolve().then(() => c(A.signal)).then((k) => (!n && u === w && !A.signal.aborted && P(k), k), (k) => {
86
+ throw !n && u === w && !A.signal.aborted && (o = { ...o, stale: !0, error: k }, b()), k;
87
+ });
88
+ return h = Promise.race([N, B]).finally(() => {
89
+ u === w && (h = void 0, g = void 0, M = void 0, o = { ...o, refreshing: !1 }, b());
90
+ }), h;
87
91
  },
88
92
  set: (c) => {
89
- ++p, b?.abort(), w = void 0, a = { ...a, refreshing: !1 }, E(c);
93
+ ++w, g?.abort(), h = void 0, o = { ...o, refreshing: !1 }, P(c);
90
94
  },
91
95
  invalidate: (c = !1) => {
92
- ++R, ++p, b?.abort(), b = void 0, w = void 0, q = void 0, x = void 0, n.delete(r), a = { data: c ? void 0 : a.data, restoring: !1, refreshing: !1, stale: !0, error: void 0 }, m(), d(() => i.remove(r));
96
+ ++D, ++w, g?.abort(), g = void 0, h = void 0, E = void 0, R = void 0, a.delete(e), o = { data: c ? void 0 : o.data, restoring: !1, refreshing: !1, stale: !0, error: void 0 }, b(), d(() => l.remove(e));
93
97
  }
94
98
  };
95
- return l.set(r, { scope: s.scope, accessedAt: Date.now(), evicted: () => {
96
- x = void 0;
97
- }, handle: M, stop: () => {
98
- ++p, b?.abort(), D.clear();
99
- } }), M;
99
+ return i.set(e, { scope: t.scope, accessedAt: Date.now(), evicted: () => {
100
+ R = void 0;
101
+ }, handle: q, cancelRefresh: () => {
102
+ h && (++w, M?.(), M = void 0, g?.abort(), g = void 0, h = void 0, o = { ...o, refreshing: !1, stale: !0 }, b());
103
+ }, stop: () => {
104
+ ++w, g?.abort(), S.clear();
105
+ } }), q;
100
106
  },
101
- flush: y,
102
- clearScope: async (s) => {
103
- for (const r of l.values()) r.scope === s && r.handle.invalidate(!0);
107
+ flush: p,
108
+ cancelRefreshes: (t) => {
109
+ for (const e of i.values()) e.scope === t && e.cancelRefresh();
110
+ },
111
+ clearScope: async (t) => {
112
+ for (const e of i.values()) e.scope === t && e.handle.invalidate(!0);
104
113
  await d(async () => {
105
- for (const r of await i.list())
114
+ for (const e of await l.list())
106
115
  try {
107
- JSON.parse(r.key)[0] === s && await i.remove(r.key);
116
+ JSON.parse(e.key)[0] === t && await l.remove(e.key);
108
117
  } catch {
109
118
  }
110
119
  });
111
120
  },
112
121
  dispose: () => {
113
- if (!o) {
114
- o = !0;
115
- for (const s of l.values()) s.stop();
116
- y(), l.clear();
122
+ if (!n) {
123
+ n = !0;
124
+ for (const t of i.values()) t.stop();
125
+ p(), i.clear();
117
126
  }
118
127
  }
119
128
  };
120
129
  }
121
- function J(h = "floe-resource-cache") {
122
- let i;
123
- const g = () => i ??= new Promise((n, e) => {
124
- const t = indexedDB.open(h, 1);
125
- t.onupgradeneeded = () => t.result.createObjectStore("snapshots", { keyPath: "key" }), t.onsuccess = () => {
126
- const o = t.result;
127
- o.onversionchange = () => {
128
- o.close(), i = void 0;
129
- }, n(o);
130
- }, t.onerror = () => {
131
- i = void 0, e(t.error);
132
- }, t.onblocked = () => {
133
- i = void 0, e(new Error("Resource cache database is blocked"));
130
+ function L(y = "floe-resource-cache") {
131
+ let l;
132
+ const m = () => l ??= new Promise((a, r) => {
133
+ const s = indexedDB.open(y, 1);
134
+ s.onupgradeneeded = () => s.result.createObjectStore("snapshots", { keyPath: "key" }), s.onsuccess = () => {
135
+ const n = s.result;
136
+ n.onversionchange = () => {
137
+ n.close(), l = void 0;
138
+ }, a(n);
139
+ }, s.onerror = () => {
140
+ l = void 0, r(s.error);
141
+ }, s.onblocked = () => {
142
+ l = void 0, r(new Error("Resource cache database is blocked"));
134
143
  };
135
- }), l = async (n, e) => {
136
- const t = await g();
137
- return new Promise((o, d) => {
138
- const v = t.transaction("snapshots", n);
139
- let y;
140
- v.oncomplete = () => o(y), v.onerror = () => d(v.error), v.onabort = () => d(v.error ?? new Error("Resource cache transaction aborted")), e(v.objectStore("snapshots"), (k) => {
141
- y = k;
144
+ }), i = async (a, r) => {
145
+ const s = await m();
146
+ return new Promise((n, d) => {
147
+ const f = s.transaction("snapshots", a);
148
+ let p;
149
+ f.oncomplete = () => n(p), f.onerror = () => d(f.error), f.onabort = () => d(f.error ?? new Error("Resource cache transaction aborted")), r(f.objectStore("snapshots"), (x) => {
150
+ p = x;
142
151
  });
143
152
  });
144
153
  };
145
154
  return {
146
- get: (n) => l("readwrite", (e, t) => {
147
- const o = e.get(n);
148
- o.onsuccess = () => {
149
- const d = o.result;
150
- d && e.put({ ...d, lastAccessedAt: Date.now() }), t(d?.value ?? null);
155
+ get: (a) => i("readwrite", (r, s) => {
156
+ const n = r.get(a);
157
+ n.onsuccess = () => {
158
+ const d = n.result;
159
+ d && r.put({ ...d, lastAccessedAt: Date.now() }), s(d?.value ?? null);
151
160
  };
152
161
  }),
153
- set: (n, e) => l("readwrite", (t, o) => {
154
- t.put({ key: n, value: e, bytes: new TextEncoder().encode(e).byteLength, lastAccessedAt: Date.now() }), o(void 0);
162
+ set: (a, r) => i("readwrite", (s, n) => {
163
+ s.put({ key: a, value: r, bytes: new TextEncoder().encode(r).byteLength, lastAccessedAt: Date.now() }), n(void 0);
155
164
  }),
156
- remove: (n) => l("readwrite", (e, t) => {
157
- e.delete(n), t(void 0);
165
+ remove: (a) => i("readwrite", (r, s) => {
166
+ r.delete(a), s(void 0);
158
167
  }),
159
- list: () => l("readonly", (n, e) => {
160
- const t = [], o = n.openCursor();
161
- o.onsuccess = () => {
162
- const d = o.result;
168
+ list: () => i("readonly", (a, r) => {
169
+ const s = [], n = a.openCursor();
170
+ n.onsuccess = () => {
171
+ const d = n.result;
163
172
  if (!d) {
164
- e(t);
173
+ r(s);
165
174
  return;
166
175
  }
167
- const { key: v, bytes: y, lastAccessedAt: k } = d.value;
168
- t.push({ key: v, bytes: y, lastAccessedAt: k }), d.continue();
176
+ const { key: f, bytes: p, lastAccessedAt: x } = d.value;
177
+ s.push({ key: f, bytes: p, lastAccessedAt: x }), d.continue();
169
178
  };
170
179
  })
171
180
  };
172
181
  }
173
182
  export {
174
- J as createIndexedDBResourceCacheStorage,
175
- I as createResourceCache,
176
- N as enforceResourceCacheBudget
183
+ L as createIndexedDBResourceCacheStorage,
184
+ z as createResourceCache,
185
+ J as enforceResourceCacheBudget
177
186
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floegence/floe-webapp-core",
3
- "version": "0.68.0",
3
+ "version": "0.69.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",