@gera2ld/async-memo 1.0.0 → 2.0.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
@@ -18,7 +18,7 @@ const myApi = asyncMemo(myApiCall, {
18
18
 
19
19
  // Call from anywhere
20
20
  async function someAction() {
21
- const response = await myApi(params);
21
+ const response = await myApi.call(params);
22
22
  }
23
23
 
24
24
  // Get the current value anytime
@@ -26,12 +26,13 @@ function getValueSync() {
26
26
  return myApi.get(params);
27
27
  }
28
28
 
29
- // Get context to avoid passing args around
29
+ // Prepare a bound context to avoid passing args around
30
30
  function otherPlace() {
31
- const ctx = myApi.context(params);
31
+ const ctx = myApi.prepare(params);
32
32
  ctx.get(); // sync get
33
33
  ctx.reload(); // refresh data
34
34
  ctx.isSettled(); // check loading state
35
+ ctx.set(value); // override cache
35
36
  }
36
37
  ```
37
38
 
@@ -95,6 +96,7 @@ function createCache<T>() {
95
96
  return {
96
97
  get(cacheKey: string) { ... },
97
98
  set(cacheKey: string, data?: T) { ... },
99
+ keys(): IterableIterator<string> { ... },
98
100
  clear() { ... },
99
101
  };
100
102
  }
@@ -120,6 +122,9 @@ function createCache<T>() {
120
122
  set(cacheKey: string, data?: T) {
121
123
  target.value = { ...target.value, [cacheKey]: data };
122
124
  },
125
+ *keys() {
126
+ yield* Object.keys(target.value);
127
+ },
123
128
  clear() {
124
129
  target.value = {};
125
130
  },
@@ -144,6 +149,9 @@ function createCache<T>() {
144
149
  set(cacheKey: string, data?: T) {
145
150
  target.value = { ...target.value, [cacheKey]: data };
146
151
  },
152
+ *keys() {
153
+ yield* Object.keys(target.value);
154
+ },
147
155
  clear() {
148
156
  target.value = {};
149
157
  },
package/dist/index.d.ts CHANGED
@@ -13,25 +13,6 @@ export interface CacheContext<U> {
13
13
  /** Whether the value returned by `get` is fresh. */
14
14
  isFresh(): boolean;
15
15
  }
16
- export interface CachedFunction<T extends unknown[], U, S extends CacheStorage> {
17
- (...args: T): Promise<U>;
18
- /** Return the currently cached value immediately. */
19
- get(...args: T): U | undefined;
20
- /** Delete the currently cached value. */
21
- delete(...args: T): void;
22
- /** Invalidate the current cached value and send a new request without deleting the old value. */
23
- reload(...args: T): Promise<U>;
24
- /** Whether the latest request is settled. */
25
- isSettled(...args: T): boolean;
26
- /** Whether the value returned by `get` is fresh. */
27
- isFresh(...args: T): boolean;
28
- /** Clear cache. */
29
- clear(): void;
30
- /** Get the cache context so we don't need to pass `args` around. */
31
- context(...args: T): CacheContext<U>;
32
- /** The cache storage, only used for testing purpose. */
33
- cache: S;
34
- }
35
16
  type CachePrimitiveKey = string | number | undefined;
36
17
  export interface CacheOptions<T extends unknown[]> {
37
18
  /**
@@ -49,7 +30,7 @@ export interface CacheOptions<T extends unknown[]> {
49
30
  */
50
31
  mustRevalidate: boolean;
51
32
  /**
52
- * Time to live, -1 for always. Default as `-1`.
33
+ * Time to live in ms, -1 for always. Default as `-1`.
53
34
  */
54
35
  ttl: number;
55
36
  }
@@ -63,9 +44,40 @@ export interface CacheData<U = unknown> {
63
44
  export interface CacheStorage<U = unknown> {
64
45
  get(cacheGroup: string): CacheData<U> | undefined;
65
46
  set(cacheGroup: string, data?: CacheData<U>): void;
47
+ keys(): IterableIterator<string>;
66
48
  clear(): void;
67
49
  }
68
50
  export declare function createAsyncMemoStorage(): Map<string, CacheData<unknown>>;
69
- export declare function createAsyncMemo<S extends CacheStorage = ReturnType<typeof createAsyncMemoStorage>>(cacheFactory?: () => S): <U, T extends unknown[]>(fn: (...args: T) => Promise<U>, options?: Partial<CacheOptions<T>>) => CachedFunction<T, U, S>;
70
- export declare const asyncMemo: <U, T extends unknown[]>(fn: (...args: T) => Promise<U>, options?: Partial<CacheOptions<T>> | undefined) => CachedFunction<T, U, Map<string, CacheData<unknown>>>;
51
+ export declare class AsyncMemoContext<T extends unknown[], U, S extends CacheStorage = ReturnType<typeof createAsyncMemoStorage>> {
52
+ cache: S;
53
+ private fn;
54
+ private mustRevalidate;
55
+ private resolver;
56
+ private ttl;
57
+ constructor(fn: (...args: T) => Promise<U>, options?: Partial<CacheOptions<T>>, cacheFactory?: () => S);
58
+ private resolveKey;
59
+ private getCachedData;
60
+ private isSettledByKey;
61
+ private isFreshByKey;
62
+ private getByKey;
63
+ private computeExpiresAt;
64
+ private setByKey;
65
+ private removeByKey;
66
+ private createPendingEntry;
67
+ private onSettled;
68
+ private reloadByKey;
69
+ private callByKey;
70
+ isSettled(...args: T): boolean;
71
+ isFresh(...args: T): boolean;
72
+ get(...args: T): U | undefined;
73
+ remove(...args: T): void;
74
+ reload(...args: T): Promise<U>;
75
+ call(...args: T): Promise<U>;
76
+ getContext(...args: T): CacheContext<U>;
77
+ prepare(...args: T): CacheContext<U>;
78
+ clear(): void;
79
+ prune(): void;
80
+ }
81
+ export declare function createAsyncMemo<S extends CacheStorage = ReturnType<typeof createAsyncMemoStorage>>(cacheFactory?: () => S): <U, T extends unknown[]>(fn: (...args: T) => Promise<U>, options?: Partial<CacheOptions<T>>) => AsyncMemoContext<T, U, S>;
82
+ export declare const asyncMemo: <U, T extends unknown[]>(fn: (...args: T) => Promise<U>, options?: Partial<CacheOptions<T>> | undefined) => AsyncMemoContext<T, U, Map<string, CacheData<unknown>>>;
71
83
  export {};
package/dist/index.js CHANGED
@@ -1,104 +1,139 @@
1
- const K = {
2
- mustRevalidate: !1,
3
- resolver: () => "",
4
- ttl: -1
1
+ //#region src/index.ts
2
+ var e = -1, t = {
3
+ mustRevalidate: !1,
4
+ resolver: () => "",
5
+ ttl: e
5
6
  };
6
- function O() {
7
- return /* @__PURE__ */ new Map();
7
+ function n() {
8
+ return /* @__PURE__ */ new Map();
8
9
  }
9
- function P(x) {
10
- return (y, D) => {
11
- const n = (x || O)(), { mustRevalidate: M, resolver: w, ttl: a } = {
12
- ...K,
13
- ...D
14
- }, F = (...e) => {
15
- const s = w(...e);
16
- return (Array.isArray(s) ? s : [s, s]).map((r) => `${r ?? ""}`);
17
- }, o = (e) => (...s) => {
18
- const t = F(...s);
19
- return e(t, s);
20
- }, i = ([e, s]) => {
21
- const t = n.get(e);
22
- return t?.key === s && t.settled;
23
- }, l = ([e, s]) => {
24
- const t = n.get(e);
25
- return t?.key === s && t.settled && (t.expiresAt < 0 || t.expiresAt > Date.now());
26
- }, h = ([e, s]) => {
27
- const t = n.get(e);
28
- if (t && (!M || l([e, s])))
29
- return t.value;
30
- }, v = ([e, s], t, r = a) => {
31
- const c = r < 0 ? r : Date.now() + r;
32
- n.set(e, {
33
- key: s,
34
- ...n.get(e),
35
- promise: t == null ? Promise.reject() : Promise.resolve(t),
36
- value: t,
37
- expiresAt: c,
38
- settled: !0
39
- });
40
- }, f = ([e]) => {
41
- n.set(e);
42
- }, S = () => {
43
- n.clear();
44
- }, d = ([e, s], t) => {
45
- const r = n.get(e), c = y(...t), u = {
46
- ...r,
47
- key: s,
48
- promise: c,
49
- // Set to -1 until the promise is either resolved or rejected
50
- expiresAt: -1,
51
- settled: !1
52
- };
53
- n.set(e, u);
54
- const p = (m, C) => {
55
- if (n.get(e) !== u)
56
- return;
57
- let g;
58
- m ? g = 0 : g = a < 0 ? a : Date.now() + a, n.set(e, {
59
- ...u,
60
- value: C,
61
- expiresAt: g,
62
- settled: !0
63
- });
64
- };
65
- return c.then(
66
- (m) => {
67
- p(!1, m);
68
- },
69
- () => {
70
- p(!0);
71
- }
72
- ), c;
73
- }, A = (e, s) => {
74
- const [t, r] = e, c = n.get(t);
75
- return c?.key === r && (l(e) || !i(e)) ? c.promise : d(e, s);
76
- }, j = (e, s) => ({
77
- call: () => A(e, s),
78
- get: () => h(e),
79
- set: (t, r = -1) => {
80
- v(e, t, r);
81
- },
82
- delete: () => f(e),
83
- reload: () => d(e, s),
84
- isSettled: () => i(e),
85
- isFresh: () => l(e)
86
- });
87
- return Object.assign(o(A), {
88
- get: o(h),
89
- delete: o(f),
90
- reload: o(d),
91
- isSettled: o(i),
92
- isFresh: o(l),
93
- context: o(j),
94
- clear: S,
95
- cache: n
96
- });
97
- };
98
- }
99
- const _ = P();
100
- export {
101
- _ as asyncMemo,
102
- P as createAsyncMemo,
103
- O as createAsyncMemoStorage
10
+ var r = class {
11
+ cache;
12
+ fn;
13
+ mustRevalidate;
14
+ resolver;
15
+ ttl;
16
+ constructor(e, r, i) {
17
+ this.fn = e, this.cache = (i || n)();
18
+ let a = {
19
+ ...t,
20
+ ...r
21
+ };
22
+ this.mustRevalidate = a.mustRevalidate, this.resolver = a.resolver, this.ttl = a.ttl;
23
+ }
24
+ resolveKey(...e) {
25
+ let t = this.resolver(...e);
26
+ return (Array.isArray(t) ? t : [t, t]).map((e) => `${e ?? ""}`);
27
+ }
28
+ getCachedData([e, t]) {
29
+ let n = this.cache.get(e);
30
+ return n?.key === t ? n : void 0;
31
+ }
32
+ isSettledByKey(e) {
33
+ return this.getCachedData(e)?.settled ?? !1;
34
+ }
35
+ isFreshByKey(e) {
36
+ let t = this.getCachedData(e);
37
+ return t?.settled ? t.expiresAt < 0 || t.expiresAt > Date.now() : !1;
38
+ }
39
+ getByKey(e) {
40
+ let t = this.getCachedData(e);
41
+ if (t && (!this.mustRevalidate || this.isFreshByKey(e))) return t.value;
42
+ }
43
+ computeExpiresAt(e) {
44
+ return e < 0 ? e : Date.now() + e;
45
+ }
46
+ setByKey([e, t], n, r) {
47
+ let i = r ?? this.ttl;
48
+ this.cache.set(e, {
49
+ key: t,
50
+ ...this.cache.get(e),
51
+ promise: n == null ? Promise.reject() : Promise.resolve(n),
52
+ value: n,
53
+ expiresAt: this.computeExpiresAt(i),
54
+ settled: !0
55
+ });
56
+ }
57
+ removeByKey([e]) {
58
+ this.cache.set(e);
59
+ }
60
+ createPendingEntry([t, n], r, i) {
61
+ let a = {
62
+ ...i,
63
+ key: n,
64
+ promise: r,
65
+ expiresAt: e,
66
+ settled: !1
67
+ };
68
+ return this.cache.set(t, a), a;
69
+ }
70
+ onSettled([e], t, n, r) {
71
+ this.cache.get(e) === t && this.cache.set(e, {
72
+ ...t,
73
+ value: r,
74
+ expiresAt: n ? 0 : this.computeExpiresAt(this.ttl),
75
+ settled: !0
76
+ });
77
+ }
78
+ reloadByKey(e, t) {
79
+ let n = this.fn(...t), r = this.createPendingEntry(e, n, this.getCachedData(e));
80
+ return n.then((t) => this.onSettled(e, r, !1, t), () => this.onSettled(e, r, !0)), n;
81
+ }
82
+ callByKey(e, t) {
83
+ let n = this.getCachedData(e);
84
+ return n && (this.isFreshByKey(e) || !this.isSettledByKey(e)) ? n.promise : this.reloadByKey(e, t);
85
+ }
86
+ isSettled(...e) {
87
+ return this.isSettledByKey(this.resolveKey(...e));
88
+ }
89
+ isFresh(...e) {
90
+ return this.isFreshByKey(this.resolveKey(...e));
91
+ }
92
+ get(...e) {
93
+ return this.getByKey(this.resolveKey(...e));
94
+ }
95
+ remove(...e) {
96
+ this.removeByKey(this.resolveKey(...e));
97
+ }
98
+ reload(...e) {
99
+ let t = this.resolveKey(...e);
100
+ return this.reloadByKey(t, e);
101
+ }
102
+ call(...e) {
103
+ let t = this.resolveKey(...e);
104
+ return this.callByKey(t, e);
105
+ }
106
+ getContext(...t) {
107
+ let n = this.resolveKey(...t);
108
+ return {
109
+ call: () => this.callByKey(n, t),
110
+ get: () => this.getByKey(n),
111
+ set: (t, r = e) => {
112
+ this.setByKey(n, t, r);
113
+ },
114
+ delete: () => this.removeByKey(n),
115
+ reload: () => this.reloadByKey(n, t),
116
+ isSettled: () => this.isSettledByKey(n),
117
+ isFresh: () => this.isFreshByKey(n)
118
+ };
119
+ }
120
+ prepare(...e) {
121
+ return this.getContext(...e);
122
+ }
123
+ clear() {
124
+ this.cache.clear();
125
+ }
126
+ prune() {
127
+ let e = Date.now();
128
+ for (let t of this.cache.keys()) {
129
+ let n = this.cache.get(t);
130
+ n && n.settled && n.expiresAt > 0 && n.expiresAt < e && this.cache.set(t);
131
+ }
132
+ }
104
133
  };
134
+ function i(e) {
135
+ return (t, n) => new r(t, n, e);
136
+ }
137
+ var a = i();
138
+ //#endregion
139
+ export { r as AsyncMemoContext, a as asyncMemo, i as createAsyncMemo, n as createAsyncMemoStorage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gera2ld/async-memo",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Cache asynchronous functions where they should be",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,6 +15,13 @@
15
15
  "files": [
16
16
  "dist"
17
17
  ],
18
+ "scripts": {
19
+ "build:js": "vite build",
20
+ "build:types": "tsc",
21
+ "build": "pnpm run build:js && pnpm run build:types",
22
+ "test": "vitest --run",
23
+ "prepublishOnly": "pnpm run build"
24
+ },
18
25
  "publishConfig": {
19
26
  "access": "public",
20
27
  "registry": "https://registry.npmjs.org/"
@@ -31,14 +38,8 @@
31
38
  "url": "https://github.com/gera2ld/async-memo.git"
32
39
  },
33
40
  "devDependencies": {
34
- "typescript": "^5.3.3",
35
- "vite": "^7.3.1",
36
- "vitest": "^4.0.18"
37
- },
38
- "scripts": {
39
- "build:js": "vite build",
40
- "build:types": "tsc",
41
- "build": "pnpm run /^build:/",
42
- "test": "vitest --run"
41
+ "typescript": "^6.0.3",
42
+ "vite": "^8.1.0",
43
+ "vitest": "^4.1.9"
43
44
  }
44
- }
45
+ }