@vizamodo/edge-cache-core 0.3.26 → 0.3.28

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.
@@ -1,6 +1,31 @@
1
- type Options = {
2
- ttlSec?: number;
1
+ export type CacheOptions = {
2
+ ttlSec: number;
3
3
  forceRefresh?: boolean;
4
4
  };
5
- export declare function getCachedOrFetch<T>(key: string, fetcher: () => Promise<T>, options?: Options): Promise<T>;
6
- export {};
5
+ /**
6
+ * Wrap a fetcher result to override the TTL via an absolute expiry timestamp.
7
+ * Use this when the upstream API tells you exactly when the data expires
8
+ * (e.g. OAuth token expiresAt, CDN Cache-Control, etc.).
9
+ *
10
+ * @example
11
+ * fetcher: async () => {
12
+ * const token = await getToken();
13
+ * return wrapResult(token.accessToken, token.expiresAt); // ISO string
14
+ * }
15
+ */
16
+ export type WrappedResult<T> = {
17
+ readonly __wrapped: true;
18
+ readonly value: T;
19
+ readonly expiresAt?: string;
20
+ };
21
+ export declare function wrapResult<T>(value: T, expiresAt?: string): WrappedResult<T>;
22
+ /**
23
+ * Two-level cache: L1 in-memory → L2 edge → L3 fetcher.
24
+ *
25
+ * - ttlSec: edge cache TTL in seconds (REQUIRED). Pass 0 to skip edge write.
26
+ * - forceRefresh: skips L1 + L2, always calls the fetcher and writes back.
27
+ * - WrappedResult: return wrapResult(value, expiresAt) from fetcher to override TTL
28
+ * with an absolute expiry from the upstream source.
29
+ * - Edge and memory write failures are non-fatal and will not throw.
30
+ */
31
+ export declare function getCachedOrFetch<T>(key: string, fetcher: () => Promise<T | WrappedResult<T>>, options: CacheOptions): Promise<T>;
@@ -1,22 +1,61 @@
1
1
  import { getEdgeCache, setEdgeCache } from "./edge-cache";
2
2
  const memory = new Map();
3
- export async function getCachedOrFetch(key, fetcher, options) {
4
- // L1: memory (skip if forceRefresh)
5
- if (!options?.forceRefresh && memory.has(key)) {
6
- return memory.get(key);
3
+ export function wrapResult(value, expiresAt) {
4
+ return { __wrapped: true, value, expiresAt };
5
+ }
6
+ // ─── Internals ─────────────────────────────────────────────────────────────
7
+ const TTL_BUFFER_SEC = 300; // 5-minute safety buffer
8
+ const TTL_MIN_SEC = 60; // never cache for less than 1 minute
9
+ function isWrapped(result) {
10
+ return (typeof result === "object" &&
11
+ result !== null &&
12
+ result.__wrapped === true);
13
+ }
14
+ function ttlFromExpiresAt(expiresAt) {
15
+ const ms = new Date(expiresAt).getTime();
16
+ if (Number.isNaN(ms)) {
17
+ return TTL_MIN_SEC; // fallback safe
7
18
  }
8
- // L2: edge cache (skip if forceRefresh)
9
- if (!options?.forceRefresh) {
19
+ const ttl = Math.floor((ms - Date.now()) / 1000) - TTL_BUFFER_SEC;
20
+ return Math.max(ttl, TTL_MIN_SEC);
21
+ }
22
+ // ─── Main ──────────────────────────────────────────────────────────────────
23
+ /**
24
+ * Two-level cache: L1 in-memory → L2 edge → L3 fetcher.
25
+ *
26
+ * - ttlSec: edge cache TTL in seconds (REQUIRED). Pass 0 to skip edge write.
27
+ * - forceRefresh: skips L1 + L2, always calls the fetcher and writes back.
28
+ * - WrappedResult: return wrapResult(value, expiresAt) from fetcher to override TTL
29
+ * with an absolute expiry from the upstream source.
30
+ * - Edge and memory write failures are non-fatal and will not throw.
31
+ */
32
+ export async function getCachedOrFetch(key, fetcher, options) {
33
+ const { forceRefresh = false, ttlSec } = options;
34
+ if (!forceRefresh) {
35
+ // L1: memory
36
+ if (memory.has(key)) {
37
+ return memory.get(key);
38
+ }
39
+ // L2: edge cache
10
40
  const edge = await getEdgeCache(key);
11
- if (edge) {
41
+ if (edge !== null) {
12
42
  memory.set(key, edge);
13
43
  return edge;
14
44
  }
15
45
  }
16
- // L3: fetch
17
- const value = await fetcher();
18
- // write back
46
+ // L3: fetch from source
47
+ const result = await fetcher();
48
+ const value = isWrapped(result) ? result.value : result;
49
+ const finalTtl = isWrapped(result) && result.expiresAt
50
+ ? ttlFromExpiresAt(result.expiresAt)
51
+ : ttlSec;
52
+ // Write back to both layers — edge write is best-effort
19
53
  memory.set(key, value);
20
- await setEdgeCache(key, value, options?.ttlSec ?? 3000);
54
+ try {
55
+ await setEdgeCache(key, value, finalTtl);
56
+ }
57
+ catch {
58
+ // non-fatal
59
+ }
21
60
  return value;
22
61
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizamodo/edge-cache-core",
3
- "version": "0.3.26",
3
+ "version": "0.3.28",
4
4
  "description": "Edge cache primitives for Cloudflare Workers (L1 memory + L2 edge cache)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",