@floegence/floe-webapp-core 0.66.0 → 0.66.1

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