cachetta 0.1.0 → 0.3.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/index.js CHANGED
@@ -1,51 +1,39 @@
1
- import { promises as c } from "fs";
2
- import { normalize as I, resolve as D, dirname as C, join as $ } from "path";
3
- import { randomBytes as U, createHash as N } from "crypto";
4
- import { inspect as O } from "util";
5
- const R = (e) => typeof e == "object" && e !== null, F = (e) => R(e) && ("path" in e || "write" in e || "read" in e || "duration" in e || "lruSize" in e || "condition" in e || "staleDuration" in e), j = (e) => typeof e == "function" && "__cacheBuddy__" in e && e.__cacheBuddy__ === !0, _ = Symbol("LRU_MISS");
6
- class m extends Error {
7
- constructor(t) {
8
- super(t), this.name = "CachettaError";
9
- }
10
- }
11
- class M extends m {
12
- constructor(t) {
13
- super(`Invalid cache path (path traversal detected): ${t}`), this.name = "InvalidPathError";
14
- }
15
- }
16
- class E extends m {
17
- constructor(t) {
18
- super(`Unsupported cache format: ${t}`), this.name = "UnsupportedFormatError";
1
+ import { promises as c, statSync as F, readFileSync as T, mkdirSync as G, writeFileSync as j, renameSync as A, unlinkSync as P } from "fs";
2
+ import { deserialize as E, serialize as z } from "v8";
3
+ import { normalize as W, resolve as b, dirname as x, join as S } from "path";
4
+ import { randomBytes as L, createHash as H } from "crypto";
5
+ import { inspect as J } from "util";
6
+ const K = (e) => typeof e == "object" && e !== null, k = (e) => K(e) && ("path" in e || "write" in e || "read" in e || "duration" in e || "lruSize" in e || "condition" in e || "staleDuration" in e), I = (e) => typeof e == "function" && "__cacheBuddy__" in e && e.__cacheBuddy__ === !0, d = Symbol("LRU_MISS");
7
+ async function f(e) {
8
+ try {
9
+ return (await c.stat(e)).mtime.getTime();
10
+ } catch (t) {
11
+ if (t.code === "ENOENT")
12
+ return null;
13
+ throw t;
19
14
  }
20
15
  }
21
- function v(e) {
22
- var i;
23
- const n = (i = String(e).split("/").pop()) == null ? void 0 : i.split(".");
24
- if (!n || n.length === 1 || n[n.length - 1] === "")
25
- throw new m(`Missing file extension: ${e}`);
26
- return n[n.length - 1];
27
- }
28
- async function d(e) {
16
+ function p(e) {
29
17
  try {
30
- return (await c.stat(e)).mtime.getTime();
18
+ return F(e).mtime.getTime();
31
19
  } catch (t) {
32
20
  if (t.code === "ENOENT")
33
21
  return null;
34
22
  throw t;
35
23
  }
36
24
  }
37
- function A(e, t, n) {
25
+ function N(e, t, n) {
38
26
  if (e > t)
39
27
  throw new Error(
40
28
  `Invalid arguments, cache time ${e} cannot be greater than now ${t}`
41
29
  );
42
30
  return t - e >= n;
43
31
  }
44
- let P = "warn";
45
- const x = ["error", "warn", "info", "debug"], G = (e) => {
46
- const t = x.indexOf(P);
47
- return x.indexOf(e) <= t;
48
- }, J = (e) => {
32
+ let R = "warn";
33
+ const D = ["error", "warn", "info", "debug"], q = (e) => {
34
+ const t = D.indexOf(R);
35
+ return D.indexOf(e) <= t;
36
+ }, Q = (e) => {
49
37
  switch (e) {
50
38
  case "debug":
51
39
  return console.debug;
@@ -56,154 +44,239 @@ const x = ["error", "warn", "info", "debug"], G = (e) => {
56
44
  case "error":
57
45
  return console.error;
58
46
  }
59
- }, p = (e) => (...t) => {
60
- if (G(e)) {
61
- const n = J(e);
47
+ }, w = (e) => (...t) => {
48
+ if (q(e)) {
49
+ const n = Q(e);
62
50
  n && n("[Cachetta]", ...t);
63
51
  }
64
52
  };
65
- let a = {
66
- debug: p("debug"),
67
- info: p("info"),
68
- warn: p("warn"),
69
- error: p("error")
53
+ let o = {
54
+ debug: w("debug"),
55
+ info: w("info"),
56
+ warn: w("warn"),
57
+ error: w("error")
70
58
  };
71
- function Z(e) {
72
- P = e;
59
+ function ct(e) {
60
+ R = e;
73
61
  }
74
- function k(e) {
75
- a = e;
62
+ function ht(e) {
63
+ o = e;
76
64
  }
77
- async function T({ duration: e, read: t }, n) {
78
- const i = e ?? 6048e5, r = await d(n);
65
+ async function V({ duration: e, read: t }, n) {
66
+ const i = e ?? 6048e5, r = await f(n);
79
67
  if (r === null)
80
- return a.debug(`Cache time is null for ${n}`), !1;
68
+ return o.debug(`Cache time is null for ${n}`), !1;
81
69
  if (i <= 0)
82
- return a.debug(`Cache length is ${i}, considering expired for ${n}`), !1;
70
+ return o.debug(`Cache length is ${i}, considering expired for ${n}`), !1;
83
71
  const s = Date.now();
84
- return r > s ? (a.debug(`Cache time ${r} is ahead of now ${s}, treating as valid cache for ${n}`), t ?? !0) : A(r, s, i) ? (a.debug(
72
+ return r > s ? (o.debug(`Cache time ${r} is ahead of now ${s}, treating as valid cache for ${n}`), t ?? !0) : N(r, s, i) ? (o.debug(
85
73
  `Cache is expired (${r}, expected ${r + i}) for ${n}`
86
- ), !1) : (a.debug(
74
+ ), !1) : (o.debug(
87
75
  `Cache is not expired (${r}, expected ${r + i}) for ${n}`
88
76
  ), t ?? !0);
89
77
  }
90
- function h(e) {
91
- const t = I(e);
78
+ function X({ duration: e, read: t }, n) {
79
+ const i = e ?? 6048e5, r = p(n);
80
+ if (r === null)
81
+ return o.debug(`Cache time is null for ${n}`), !1;
82
+ if (i <= 0)
83
+ return o.debug(`Cache length is ${i}, considering expired for ${n}`), !1;
84
+ const s = Date.now();
85
+ return r > s ? (o.debug(`Cache time ${r} is ahead of now ${s}, treating as valid cache for ${n}`), t ?? !0) : N(r, s, i) ? (o.debug(
86
+ `Cache is expired (${r}, expected ${r + i}) for ${n}`
87
+ ), !1) : (o.debug(
88
+ `Cache is not expired (${r}, expected ${r + i}) for ${n}`
89
+ ), t ?? !0);
90
+ }
91
+ class C extends Error {
92
+ constructor(t) {
93
+ super(t), this.name = "CachettaError";
94
+ }
95
+ }
96
+ class Y extends C {
97
+ constructor(t) {
98
+ super(`Invalid cache path (path traversal detected): ${t}`), this.name = "InvalidPathError";
99
+ }
100
+ }
101
+ function u(e) {
102
+ const t = W(e);
92
103
  if (t.split(/[/\\]/).includes(".."))
93
- throw new M(e);
94
- return D(t);
104
+ throw new Y(e);
105
+ return b(t);
95
106
  }
96
- const K = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
97
- async function z(e) {
98
- const t = v(e);
99
- if (t !== "json")
100
- throw new E(t);
107
+ async function U(e) {
101
108
  try {
102
- const n = await c.readFile(e, "utf8");
103
- return JSON.parse(n, (i, r) => {
104
- if (!K.has(i))
105
- return r;
106
- });
107
- } catch (n) {
108
- return n.code === "ENOENT" ? null : n instanceof SyntaxError ? (a.error(`Corrupt JSON: ${n}`), null) : (a.error(`Read error: ${n}`), null);
109
+ const t = await c.readFile(e);
110
+ return E(t);
111
+ } catch (t) {
112
+ return t.code === "ENOENT" || o.error(`Read error: ${t}`), null;
113
+ }
114
+ }
115
+ function M(e) {
116
+ try {
117
+ const t = T(e);
118
+ return E(t);
119
+ } catch (t) {
120
+ return t.code === "ENOENT" || o.error(`Read error: ${t}`), null;
109
121
  }
110
122
  }
111
- async function W(e, ...t) {
112
- if (!j(e))
113
- throw new m(`Invalid value provided, you must provide an instance of Cachetta: ${e}`);
123
+ async function Z(e, ...t) {
124
+ if (!I(e))
125
+ throw new C(`Invalid value provided, you must provide an instance of Cachetta: ${e}`);
114
126
  const n = e._getPath(...t);
115
- h(n);
127
+ u(n);
116
128
  const i = e._lruGet(n);
117
- if (i !== _)
118
- return a.debug(`LRU cache hit for ${n}`), i;
119
- if (await T(e, n)) {
120
- a.debug(`Using cache at ${n}`);
121
- const r = await z(n);
122
- return r !== null && (a.debug(`Used cache at ${n}`), e._lruSet(n, r)), r;
129
+ if (i !== d)
130
+ return o.debug(`LRU cache hit for ${n}`), i;
131
+ if (await V(e, n)) {
132
+ o.debug(`Using cache at ${n}`);
133
+ const r = await U(n);
134
+ return r !== null && (o.debug(`Used cache at ${n}`), e._lruSet(n, r)), r;
123
135
  } else
124
- return a.debug("cache.read is false, skipping cache"), null;
136
+ return o.debug("cache.read is false, skipping cache"), null;
125
137
  }
126
- async function H(e, ...t) {
138
+ function B(e, ...t) {
139
+ if (!I(e))
140
+ throw new C(`Invalid value provided, you must provide an instance of Cachetta: ${e}`);
141
+ const n = e._getPath(...t);
142
+ u(n);
143
+ const i = e._lruGet(n);
144
+ if (i !== d)
145
+ return o.debug(`LRU cache hit for ${n}`), i;
146
+ if (X(e, n)) {
147
+ o.debug(`Using cache at ${n}`);
148
+ const r = M(n);
149
+ return r !== null && (o.debug(`Used cache at ${n}`), e._lruSet(n, r)), r;
150
+ } else
151
+ return o.debug("cache.read is false, skipping cache"), null;
152
+ }
153
+ async function tt(e, ...t) {
154
+ if (!e.staleDuration || !e.read) return null;
155
+ const n = e._getPath(...t);
156
+ u(n);
157
+ const i = await f(n);
158
+ if (i === null) return null;
159
+ const r = Date.now() - i, s = r >= e.duration, a = r < e.duration + e.staleDuration;
160
+ return s && a ? (o.debug(`Returning stale cache for ${n} (age: ${r}ms)`), U(n)) : null;
161
+ }
162
+ function et(e, ...t) {
127
163
  if (!e.staleDuration || !e.read) return null;
128
164
  const n = e._getPath(...t);
129
- h(n);
130
- const i = await d(n);
165
+ u(n);
166
+ const i = p(n);
131
167
  if (i === null) return null;
132
- const r = Date.now() - i, s = r >= e.duration, o = r < e.duration + e.staleDuration;
133
- return s && o ? (a.debug(`Returning stale cache for ${n} (age: ${r}ms)`), z(n)) : null;
168
+ const r = Date.now() - i, s = r >= e.duration, a = r < e.duration + e.staleDuration;
169
+ return s && a ? (o.debug(`Returning stale cache for ${n} (age: ${r}ms)`), M(n)) : null;
134
170
  }
135
- const b = /* @__PURE__ */ new Set();
136
- async function y(e, t, ...n) {
171
+ const y = /* @__PURE__ */ new Set();
172
+ async function v(e, t, ...n) {
137
173
  if (!e || !e.write)
138
174
  return;
139
175
  const i = e._getPath(...n);
140
- h(i);
141
- const r = v(i), s = D(C(i));
142
- if (b.has(s) || (await c.mkdir(s, { recursive: !0 }), b.add(s)), r === "json") {
143
- const o = JSON.stringify(t), u = $(s, `.cachetta-${U(8).toString("hex")}.tmp`);
176
+ u(i);
177
+ const r = b(x(i));
178
+ y.has(r) || (await c.mkdir(r, { recursive: !0 }), y.add(r));
179
+ const s = z(t), a = S(r, `.cachetta-${L(8).toString("hex")}.tmp`);
180
+ try {
181
+ await c.writeFile(a, s), await c.rename(a, i), e._lruSet(i, t);
182
+ } catch (l) {
144
183
  try {
145
- await c.writeFile(u, o, "utf8"), await c.rename(u, i), e._lruSet(i, t);
146
- } catch (l) {
147
- try {
148
- await c.unlink(u);
149
- } catch {
150
- }
151
- throw l;
184
+ await c.unlink(a);
185
+ } catch {
152
186
  }
153
- } else
154
- throw new E(r);
187
+ throw l;
188
+ }
189
+ }
190
+ function nt(e, t, ...n) {
191
+ if (!e || !e.write)
192
+ return;
193
+ const i = e._getPath(...n);
194
+ u(i);
195
+ const r = b(x(i));
196
+ y.has(r) || (G(r, { recursive: !0 }), y.add(r));
197
+ const s = z(t), a = S(r, `.cachetta-${L(8).toString("hex")}.tmp`);
198
+ try {
199
+ j(a, s), A(a, i), e._lruSet(i, t);
200
+ } catch (l) {
201
+ try {
202
+ P(a);
203
+ } catch {
204
+ }
205
+ throw l;
206
+ }
155
207
  }
156
- const w = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Set(), g = (e, t) => {
208
+ const m = /* @__PURE__ */ new Map(), $ = /* @__PURE__ */ new Set(), _ = (e, t) => {
157
209
  async function n(...i) {
158
- const r = await W(e, ...i);
210
+ const r = await Z(e, ...i);
159
211
  if (r != null)
160
212
  return r;
161
213
  const s = e._getPath(...i);
162
214
  if (e.staleDuration) {
163
- const l = await H(e, ...i);
164
- if (l != null)
165
- return !S.has(s) && !w.has(s) && (S.add(s), (async () => {
215
+ const h = await tt(e, ...i);
216
+ if (h != null)
217
+ return !$.has(s) && !m.has(s) && ($.add(s), (async () => {
166
218
  try {
167
- const f = await t.apply(this, i);
168
- (!e.condition || e.condition(f)) && await y(e, f, ...i);
169
- } catch (f) {
170
- a.error(`Background revalidation failed for ${s}: ${f}`);
219
+ const g = await t.apply(this, i);
220
+ (!e.condition || e.condition(g)) && await v(e, g, ...i);
221
+ } catch (g) {
222
+ o.error(`Background revalidation failed for ${s}: ${g}`);
171
223
  } finally {
172
- S.delete(s);
224
+ $.delete(s);
173
225
  }
174
- })()), l;
226
+ })()), h;
175
227
  }
176
- const o = w.get(s);
177
- if (o)
178
- return o;
179
- const u = (async () => {
180
- const l = await t.apply(this, i);
181
- return (!e.condition || e.condition(l)) && await y(e, l, ...i), l;
228
+ const a = m.get(s);
229
+ if (a)
230
+ return a;
231
+ const l = (async () => {
232
+ const h = await t.apply(this, i);
233
+ return (!e.condition || e.condition(h)) && await v(e, h, ...i), h;
182
234
  })();
183
- w.set(s, u);
235
+ m.set(s, l);
184
236
  try {
185
- return await u;
237
+ return await l;
186
238
  } finally {
187
- w.delete(s);
239
+ m.delete(s);
240
+ }
241
+ }
242
+ return n;
243
+ }, it = (e, t) => {
244
+ function n(...i) {
245
+ const r = B(e, ...i);
246
+ if (r != null)
247
+ return r;
248
+ if (e.staleDuration) {
249
+ const a = et(e, ...i);
250
+ if (a != null)
251
+ return a;
188
252
  }
253
+ const s = t.apply(this, i);
254
+ return (!e.condition || e.condition(s)) && nt(e, s, ...i), s;
189
255
  }
190
256
  return n;
191
- }, Y = 10080 * 60 * 1e3;
192
- class L extends Function {
257
+ }, rt = 10080 * 60 * 1e3;
258
+ class O extends Function {
193
259
  constructor(t) {
194
- super(), this.__cacheBuddy__ = !0, this.path = t.path, this.write = t.write ?? !0, this.read = t.read ?? !0, this.duration = t.duration ?? Y, this.lruSize = t.lruSize, this.condition = t.condition, this.staleDuration = t.staleDuration, this._lru = this.lruSize ? /* @__PURE__ */ new Map() : void 0;
260
+ super(), this.__cacheBuddy__ = !0, this.path = t.path, this.write = t.write ?? !0, this.read = t.read ?? !0, this.duration = t.duration ?? rt, this.lruSize = t.lruSize, this.condition = t.condition, this.staleDuration = t.staleDuration, this._lru = this.lruSize ? /* @__PURE__ */ new Map() : void 0;
195
261
  const n = this.call.bind(this), i = Object.assign(
196
262
  n,
197
263
  this,
198
264
  {
199
265
  copy: this.copy.bind(this),
200
266
  wrap: this.wrap.bind(this),
267
+ wrapSync: this.wrapSync.bind(this),
201
268
  invalidate: this.invalidate.bind(this),
269
+ invalidateSync: this.invalidateSync.bind(this),
202
270
  clear: this.invalidate.bind(this),
203
271
  // alias
272
+ clearSync: this.invalidateSync.bind(this),
273
+ // alias
204
274
  exists: this.exists.bind(this),
275
+ existsSync: this.existsSync.bind(this),
205
276
  age: this.age.bind(this),
277
+ ageSync: this.ageSync.bind(this),
206
278
  info: this.info.bind(this),
279
+ infoSync: this.infoSync.bind(this),
207
280
  _getPath: this._getPath.bind(this),
208
281
  _lruGet: this._lruGet.bind(this),
209
282
  _lruSet: this._lruSet.bind(this),
@@ -214,17 +287,10 @@ class L extends Function {
214
287
  staleDuration: this.staleDuration
215
288
  }
216
289
  );
217
- return i[O.custom] = () => `Cachetta { path: '${this.path}', write: ${this.write}, read: ${this.read}, duration: ${this.duration} }`, i;
290
+ return i[J.custom] = () => `Cachetta { path: '${this.path}', write: ${this.write}, read: ${this.read}, duration: ${this.duration} }`, i;
218
291
  }
219
- /**
220
- * Creates a copy of this Cachetta instance with overridden configuration.
221
- * Useful for creating variations of a base cache configuration.
222
- *
223
- * @param kwargs - Partial configuration to override
224
- * @returns A new Cachetta instance with the specified overrides
225
- */
226
292
  copy(t) {
227
- return new L({
293
+ return new O({
228
294
  path: t.path ?? this.path,
229
295
  write: t.write ?? this.write,
230
296
  read: t.read ?? this.read,
@@ -234,24 +300,15 @@ class L extends Function {
234
300
  staleDuration: t.staleDuration ?? this.staleDuration
235
301
  });
236
302
  }
237
- /**
238
- * Wraps a function with caching behavior. Alias for calling the cache instance directly.
239
- *
240
- * @param fn - The function to wrap
241
- * @returns A cached version of the function
242
- */
243
303
  wrap(t) {
244
- return g(this, t);
304
+ return _(this, t);
305
+ }
306
+ wrapSync(t) {
307
+ return it(this, t);
245
308
  }
246
- /**
247
- * Deletes the cache file on disk and clears LRU entries for this path.
248
- * No-op if the cache file does not exist.
249
- *
250
- * @param args - Arguments to resolve the cache path (when using a path function)
251
- */
252
309
  async invalidate(...t) {
253
310
  const n = this._getPath(...t);
254
- h(n), this._lru && this._lru.delete(n);
311
+ u(n), this._lru && this._lru.delete(n);
255
312
  try {
256
313
  await c.unlink(n);
257
314
  } catch (i) {
@@ -259,76 +316,71 @@ class L extends Function {
259
316
  throw i;
260
317
  }
261
318
  }
262
- /**
263
- * Checks whether the cache file exists on disk.
264
- *
265
- * @param args - Arguments to resolve the cache path (when using a path function)
266
- * @returns true if the cache file exists
267
- */
319
+ invalidateSync(...t) {
320
+ const n = this._getPath(...t);
321
+ u(n), this._lru && this._lru.delete(n);
322
+ try {
323
+ P(n);
324
+ } catch (i) {
325
+ if (i.code !== "ENOENT")
326
+ throw i;
327
+ }
328
+ }
268
329
  async exists(...t) {
269
330
  const n = this._getPath(...t);
270
- return h(n), await d(n) !== null;
331
+ return u(n), await f(n) !== null;
332
+ }
333
+ existsSync(...t) {
334
+ const n = this._getPath(...t);
335
+ return u(n), p(n) !== null;
271
336
  }
272
- /**
273
- * Returns the age of the cache file in milliseconds, or null if it does not exist.
274
- *
275
- * @param args - Arguments to resolve the cache path (when using a path function)
276
- * @returns Age in ms, or null
277
- */
278
337
  async age(...t) {
279
338
  const n = this._getPath(...t);
280
- h(n);
281
- const i = await d(n);
282
- return i === null ? null : Date.now() - i;
339
+ u(n);
340
+ const i = await f(n);
341
+ return i === null ? null : Math.max(0, Date.now() - i);
342
+ }
343
+ ageSync(...t) {
344
+ const n = this._getPath(...t);
345
+ u(n);
346
+ const i = p(n);
347
+ return i === null ? null : Math.max(0, Date.now() - i);
283
348
  }
284
- /**
285
- * Returns detailed information about the cache state.
286
- *
287
- * @param args - Arguments to resolve the cache path (when using a path function)
288
- * @returns CacheInfo with exists, age, expired, stale, and path fields
289
- */
290
349
  async info(...t) {
291
350
  const n = this._getPath(...t);
292
- h(n);
293
- const i = await d(n);
351
+ u(n);
352
+ const i = await f(n);
353
+ if (i === null)
354
+ return { exists: !1, age: null, expired: !1, stale: !1, path: n };
355
+ const r = Math.max(0, Date.now() - i), s = r >= this.duration, a = s && this.staleDuration != null && r < this.duration + this.staleDuration;
356
+ return { exists: !0, age: r, expired: s, stale: a, path: n };
357
+ }
358
+ infoSync(...t) {
359
+ const n = this._getPath(...t);
360
+ u(n);
361
+ const i = p(n);
294
362
  if (i === null)
295
363
  return { exists: !1, age: null, expired: !1, stale: !1, path: n };
296
- const r = Date.now() - i, s = r >= this.duration, o = s && this.staleDuration != null && r < this.duration + this.staleDuration;
297
- return { exists: !0, age: r, expired: s, stale: o, path: n };
364
+ const r = Math.max(0, Date.now() - i), s = r >= this.duration, a = s && this.staleDuration != null && r < this.duration + this.staleDuration;
365
+ return { exists: !0, age: r, expired: s, stale: a, path: n };
298
366
  }
299
- /**
300
- * Internal method to resolve the cache path.
301
- * When path is a string and arguments are provided, auto-generates a unique
302
- * cache path by hashing the arguments.
303
- * @internal
304
- */
305
367
  _getPath(...t) {
306
368
  if (typeof this.path == "string") {
307
369
  if (t.length === 0)
308
370
  return this.path;
309
- const n = N("sha256").update(JSON.stringify(t)).digest("hex").slice(0, 16), i = C(this.path), r = this.path.split("/").pop(), s = r.lastIndexOf(".");
371
+ const n = H("sha256").update(JSON.stringify(t)).digest("hex").slice(0, 16), i = x(this.path), r = this.path.split("/").pop(), s = r.lastIndexOf(".");
310
372
  if (s === -1)
311
- return $(i, `${r}-${n}`);
312
- const o = r.slice(0, s), u = r.slice(s);
313
- return $(i, `${o}-${n}${u}`);
373
+ return S(i, `${r}-${n}`);
374
+ const a = r.slice(0, s), l = r.slice(s);
375
+ return S(i, `${a}-${n}${l}`);
314
376
  }
315
377
  return this.path(...t);
316
378
  }
317
- /**
318
- * Get a value from the in-memory LRU cache.
319
- * Returns undefined if LRU is disabled, key not found, or entry is expired.
320
- * @internal
321
- */
322
379
  _lruGet(t) {
323
- if (!this._lru) return _;
380
+ if (!this._lru) return d;
324
381
  const n = this._lru.get(t);
325
- return n ? Date.now() - n.timestamp > this.duration ? (this._lru.delete(t), _) : (this._lru.delete(t), this._lru.set(t, n), n.value) : _;
382
+ return n ? Date.now() - n.timestamp > this.duration ? (this._lru.delete(t), d) : (this._lru.delete(t), this._lru.set(t, n), n.value) : d;
326
383
  }
327
- /**
328
- * Set a value in the in-memory LRU cache.
329
- * No-op if LRU is disabled.
330
- * @internal
331
- */
332
384
  _lruSet(t, n) {
333
385
  if (!(!this._lru || !this.lruSize)) {
334
386
  if (this._lru.size >= this.lruSize && !this._lru.has(t)) {
@@ -340,29 +392,30 @@ class L extends Function {
340
392
  }
341
393
  // Implementation signature
342
394
  call(t, n, i) {
343
- if (F(t)) {
395
+ if (k(t)) {
344
396
  const s = t;
345
397
  return this.copy(s);
346
398
  }
347
399
  if (i) {
348
400
  const s = i.value;
349
- return i.value = g(this, s), i;
401
+ return i.value = _(this, s), i;
350
402
  }
351
403
  const r = t;
352
404
  if (n) {
353
- const s = n, o = this.copy(s);
354
- return g(o, r);
405
+ const s = n, a = this.copy(s);
406
+ return _(a, r);
355
407
  }
356
- return g(this, r);
408
+ return _(this, r);
357
409
  }
358
410
  }
359
411
  export {
360
- L as Cachetta,
361
- m as CachettaError,
362
- M as InvalidPathError,
363
- E as UnsupportedFormatError,
364
- W as readCache,
365
- Z as setLogLevel,
366
- k as setLogger,
367
- y as writeCache
412
+ O as Cachetta,
413
+ C as CachettaError,
414
+ Y as InvalidPathError,
415
+ Z as readCache,
416
+ B as readCacheSync,
417
+ ct as setLogLevel,
418
+ ht as setLogger,
419
+ v as writeCache,
420
+ nt as writeCacheSync
368
421
  };
@@ -1,4 +1,4 @@
1
- import { CacheConfig, CacheInfo, CachableFunction, PathFn } from './types.js';
1
+ import { CacheConfig, CacheInfo, CachableFunction, CachableFunctionSync, PathFn } from './types.js';
2
2
  import { LRU_MISS } from './constants.js';
3
3
  interface LruEntry {
4
4
  value: unknown;
@@ -18,67 +18,19 @@ export declare class Cachetta<Path extends string | PathFn<any> = string> extend
18
18
  /** @internal */
19
19
  _lru: Map<string, LruEntry> | undefined;
20
20
  constructor(config: CacheConfig<Path>);
21
- /**
22
- * Creates a copy of this Cachetta instance with overridden configuration.
23
- * Useful for creating variations of a base cache configuration.
24
- *
25
- * @param kwargs - Partial configuration to override
26
- * @returns A new Cachetta instance with the specified overrides
27
- */
28
21
  copy<NewPath extends string | PathFn<any> = string>(kwargs: Partial<CacheConfig<NewPath>>): Cachetta<NewPath>;
29
- /**
30
- * Wraps a function with caching behavior. Alias for calling the cache instance directly.
31
- *
32
- * @param fn - The function to wrap
33
- * @returns A cached version of the function
34
- */
35
22
  wrap(fn: CachableFunction): CachableFunction;
36
- /**
37
- * Deletes the cache file on disk and clears LRU entries for this path.
38
- * No-op if the cache file does not exist.
39
- *
40
- * @param args - Arguments to resolve the cache path (when using a path function)
41
- */
23
+ wrapSync(fn: CachableFunctionSync): CachableFunctionSync;
42
24
  invalidate(...args: unknown[]): Promise<void>;
43
- /**
44
- * Checks whether the cache file exists on disk.
45
- *
46
- * @param args - Arguments to resolve the cache path (when using a path function)
47
- * @returns true if the cache file exists
48
- */
25
+ invalidateSync(...args: unknown[]): void;
49
26
  exists(...args: unknown[]): Promise<boolean>;
50
- /**
51
- * Returns the age of the cache file in milliseconds, or null if it does not exist.
52
- *
53
- * @param args - Arguments to resolve the cache path (when using a path function)
54
- * @returns Age in ms, or null
55
- */
27
+ existsSync(...args: unknown[]): boolean;
56
28
  age(...args: unknown[]): Promise<number | null>;
57
- /**
58
- * Returns detailed information about the cache state.
59
- *
60
- * @param args - Arguments to resolve the cache path (when using a path function)
61
- * @returns CacheInfo with exists, age, expired, stale, and path fields
62
- */
29
+ ageSync(...args: unknown[]): number | null;
63
30
  info(...args: unknown[]): Promise<CacheInfo>;
64
- /**
65
- * Internal method to resolve the cache path.
66
- * When path is a string and arguments are provided, auto-generates a unique
67
- * cache path by hashing the arguments.
68
- * @internal
69
- */
31
+ infoSync(...args: unknown[]): CacheInfo;
70
32
  _getPath(...args: unknown[]): string;
71
- /**
72
- * Get a value from the in-memory LRU cache.
73
- * Returns undefined if LRU is disabled, key not found, or entry is expired.
74
- * @internal
75
- */
76
33
  _lruGet(key: string): unknown | typeof LRU_MISS;
77
- /**
78
- * Set a value in the in-memory LRU cache.
79
- * No-op if LRU is disabled.
80
- * @internal
81
- */
82
34
  _lruSet(key: string, value: unknown): void;
83
35
  call(target: CachableFunction, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor;
84
36
  call(target: CachableFunction): CachableFunction;
@@ -4,6 +4,3 @@ export declare class CachettaError extends Error {
4
4
  export declare class InvalidPathError extends CachettaError {
5
5
  constructor(cachePath: string);
6
6
  }
7
- export declare class UnsupportedFormatError extends CachettaError {
8
- constructor(extension: string);
9
- }
@@ -1,6 +1,6 @@
1
1
  export { Cachetta } from './Cachetta.js';
2
- export { writeCache } from './write-cache.js';
3
- export { readCache } from './read-cache.js';
2
+ export { writeCache, writeCacheSync } from './write-cache.js';
3
+ export { readCache, readCacheSync } from './read-cache.js';
4
4
  export { setLogLevel, setLogger } from './utils/logger.js';
5
- export { CachettaError, InvalidPathError, UnsupportedFormatError } from './errors.js';
6
- export type { CacheConfig, CacheInfo, PathFn, CachableFunction, Logger, LogLevel } from './types.js';
5
+ export { CachettaError, InvalidPathError } from './errors.js';
6
+ export type { CacheConfig, CacheInfo, PathFn, CachableFunction, CachableFunctionSync, Logger, LogLevel } from './types.js';
@@ -1,8 +1,11 @@
1
1
  import { Cachetta } from './Cachetta.js';
2
2
  export declare function readCache<T>(cacheBuddy: Cachetta<any>, ...args: unknown[]): Promise<T | null>;
3
+ export declare function readCacheSync<T>(cacheBuddy: Cachetta<any>, ...args: unknown[]): T | null;
3
4
  /**
4
5
  * Reads stale cache data: returns data only if the file exists and is within the
5
6
  * staleDuration window (expired but not yet past duration + staleDuration).
6
7
  * @internal
7
8
  */
8
9
  export declare function readStaleCache<T>(cacheBuddy: Cachetta<any>, ...args: unknown[]): Promise<T | null>;
10
+ /** @internal */
11
+ export declare function readStaleCacheSync<T>(cacheBuddy: Cachetta<any>, ...args: unknown[]): T | null;
@@ -18,6 +18,7 @@ export interface CacheInfo {
18
18
  }
19
19
  export type PathFn<T extends unknown[] = unknown[]> = (...args: T) => string;
20
20
  export type CachableFunction = (...args: unknown[]) => unknown;
21
+ export type CachableFunctionSync = (...args: unknown[]) => unknown;
21
22
  export interface Logger {
22
23
  debug: (...messages: unknown[]) => void;
23
24
  info: (...messages: unknown[]) => void;
@@ -1,3 +1,4 @@
1
1
  import { Cachetta } from '../Cachetta.js';
2
- import { CachableFunction } from '../types.js';
2
+ import { CachableFunction, CachableFunctionSync } from '../types.js';
3
3
  export declare const cacheFn: (cache: Cachetta<any>, originalMethod: CachableFunction) => (this: ThisParameterType<typeof originalMethod>, ...args: Parameters<typeof originalMethod>) => Promise<unknown>;
4
+ export declare const cacheFnSync: (cache: Cachetta<any>, originalMethod: CachableFunctionSync) => (this: any, ...args: unknown[]) => unknown;
@@ -1,2 +1,3 @@
1
1
  import { PathLike } from 'fs';
2
2
  export declare function getLastUpdated(filePath: string | PathLike): Promise<number | null>;
3
+ export declare function getLastUpdatedSync(filePath: string | PathLike): number | null;
@@ -1,2 +1,3 @@
1
1
  import { CacheConfig } from '../types.js';
2
2
  export declare function shouldUseReadCache({ duration, read }: Pick<CacheConfig, 'duration' | 'read'>, cachePath: string): Promise<boolean>;
3
+ export declare function shouldUseReadCacheSync({ duration, read }: Pick<CacheConfig, 'duration' | 'read'>, cachePath: string): boolean;
@@ -1,2 +1,3 @@
1
1
  import { Cachetta } from './Cachetta.js';
2
2
  export declare function writeCache<T>(cache: Cachetta<any>, data: T, ...args: unknown[]): Promise<void>;
3
+ export declare function writeCacheSync<T>(cache: Cachetta<any>, data: T, ...args: unknown[]): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cachetta",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -15,7 +15,35 @@
15
15
  "dist"
16
16
  ],
17
17
  "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/thekevinscott/cachetta.git",
21
+ "directory": "packages/javascript"
22
+ },
23
+ "scripts": {
24
+ "test:unit": "wireit",
25
+ "test:unit:watch": "wireit",
26
+ "test:integration": "wireit",
27
+ "test:integration:watch": "wireit",
28
+ "lint": "wireit",
29
+ "typecheck": "wireit",
30
+ "build": "wireit"
31
+ },
18
32
  "wireit": {
33
+ "lint": {
34
+ "command": "eslint src",
35
+ "files": [
36
+ "./src/**/*.ts",
37
+ "./eslint.config.js"
38
+ ]
39
+ },
40
+ "typecheck": {
41
+ "command": "tsc --noEmit",
42
+ "files": [
43
+ "./src/**/*.ts",
44
+ "./tsconfig.json"
45
+ ]
46
+ },
19
47
  "test:unit": {
20
48
  "command": "vitest run -c vitest.config.unit.ts"
21
49
  },
@@ -49,20 +77,15 @@
49
77
  }
50
78
  },
51
79
  "devDependencies": {
80
+ "@eslint/js": "^9.39.2",
52
81
  "@types/node": "^22.10.0",
82
+ "cachetta": "workspace:*",
83
+ "eslint": "^9.39.2",
53
84
  "typescript": "^5.7.2",
85
+ "typescript-eslint": "^8.55.0",
54
86
  "vite": "^6.0.0",
55
87
  "vite-plugin-dts": "^4.5.4",
56
88
  "vitest": "^2.1.6",
57
- "wireit": "^0.14.9",
58
- "cachetta": "0.1.0"
59
- },
60
- "dependencies": {},
61
- "scripts": {
62
- "test:unit": "wireit",
63
- "test:unit:watch": "wireit",
64
- "test:integration": "wireit",
65
- "test:integration:watch": "wireit",
66
- "build": "wireit"
89
+ "wireit": "^0.14.9"
67
90
  }
68
- }
91
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Kevin Scott
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,2 +0,0 @@
1
- import { PathLike } from 'fs';
2
- export declare function getExtension(cachePath: string | PathLike): string;
@@ -1 +0,0 @@
1
- export {};