@atproto-labs/simple-store 0.4.0 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atproto-labs/simple-store
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#4967](https://github.com/bluesky-social/atproto/pull/4967) [`9fc720c`](https://github.com/bluesky-social/atproto/commit/9fc720ce75f3ee88a5e48a9be919b07c7647f6f5) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Use TypeScript 7 to build package
8
+
3
9
  ## 0.4.0
4
10
 
5
11
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"cached-getter.d.ts","sourceRoot":"","sources":["../src/cached-getter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cached-getter.d.ts","sourceRoot":"","sources":["../src/cached-getter.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cached-getter.js","sourceRoot":"","sources":["../src/cached-getter.ts"],"names":[],"mappings":"AAmDA,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,IAAI,CAAA;AAC7B,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,KAAK,CAAA;AAE/B;;;GAGG;AACH,MAAM,OAAO,YAAY;IAOvB,YACW,MAAuB,EACvB,KAAwB,EACxB,UAAqC,EAAE;QAFvC,WAAM,GAAN,MAAM,CAAiB;QACvB,UAAK,GAAL,KAAK,CAAmB;QACxB,YAAO,GAAP,OAAO,CAAgC;QALjC,YAAO,GAAG,IAAI,GAAG,EAAqB,CAAA;IAMpD,CAAC;IAUJ,KAAK,CAAC,GAAG,CACP,GAAM,EACN,EACE,MAAM,EACN,OAAO,EACP,UAAU,GAAG,KAAK,EAClB,OAAO,GAAG,KAAK,MACb,EAAyB;QAE7B,MAAM,EAAE,cAAc,EAAE,CAAA;QAExB,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,OAAO,CAAA;QAE/C,MAAM,WAAW,GAAqC,OAAO;YAC3D,CAAC,CAAC,WAAW,CAAC,2CAA2C;YACzD,CAAC,CAAC,UAAU,IAAI,OAAO,IAAI,IAAI;gBAC7B,CAAC,CAAC,UAAU,CAAC,4CAA4C;gBACzD,CAAC,CAAC,KAAK,EAAE,KAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;QAEtD,qEAAqE;QACrE,2EAA2E;QAC3E,yEAAyE;QACzE,yEAAyE;QACzE,4EAA4E;QAC5E,wCAAwC;QACxC,IAAI,qBAAiD,CAAA;QACrD,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACvD,IAAI,CAAC;gBACH,wEAAwE;gBACxE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,qBAAqB,CAAA;gBAEtD,qDAAqD;gBACrD,IAAI,OAAO;oBAAE,OAAO,KAAK,CAAA;gBACzB,oEAAoE;gBACpE,+DAA+D;gBAC/D,IAAI,MAAM,WAAW,CAAC,KAAK,CAAC;oBAAE,OAAO,KAAK,CAAA;YAC5C,CAAC;YAAC,MAAM,CAAC;gBACP,mEAAmE;gBACnE,4BAA4B;YAC9B,CAAC;YAED,2CAA2C;YAC3C,MAAM,EAAE,cAAc,EAAE,CAAA;QAC1B,CAAC;QAED,MAAM,oBAAoB,GAAmB,OAAO,CAAC,OAAO,EAAE;aAC3D,IAAI,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;YAEzD,IAAI,WAAW,KAAK,SAAS,IAAI,CAAC,MAAM,WAAW,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;gBAClE,iEAAiE;gBACjE,gEAAgE;gBAChE,sEAAsE;gBACtE,8DAA8D;gBAC9D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAA;YAC/C,CAAC;YAED,OAAO,OAAO,CAAC,OAAO,EAAE;iBACrB,IAAI,CAAC,KAAK,IAAI,EAAE;gBACf,MAAM,OAAO,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAsB,CAAA;gBAChE,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,CAAC,CAAA;YAC1D,CAAC,CAAC;iBACD,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;gBACnB,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;oBAC9B,IAAI,CAAC;wBACH,IAAI,MAAM,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,CAAC;4BACjD,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;wBAChC,CAAC;oBACH,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,MAAM,IAAI,cAAc,CACtB,CAAC,GAAG,EAAE,KAAK,CAAC,EACZ,mCAAmC,CACpC,CAAA;oBACH,CAAC;gBACH,CAAC;gBACD,MAAM,GAAG,CAAA;YACX,CAAC,CAAC;iBACD,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;gBACpB,6DAA6D;gBAC7D,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;gBAChC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;YACjC,CAAC,CAAC,CAAA;QACN,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAC1B,CAAC,CAAC,CAAA;QAEJ,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,kEAAkE;YAClE,mEAAmE;YACnE,sEAAsE;YACtE,6CAA6C;YAC7C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAA;QAE3C,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,oBAAoB,CAAA;QAC5C,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAM,EAAE,OAAoB;QAC1C,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAC3C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,SAAS,CAAA;QAClB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAM,EAAE,KAAQ;QAC9B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,CAAA;YAC/C,MAAM,YAAY,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;QACvC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAM,EAAE,MAAgB;QACtC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC3B,CAAC;CACF","sourcesContent":["import { GetOptions, Key, SimpleStore, Value } from './simple-store.js'\nimport { Awaitable, ContextOptions } from './util.js'\n\nexport type { GetOptions }\nexport type GetCachedOptions<C = void> = ContextOptions<C> & {\n signal?: AbortSignal\n\n /**\n * Do not use the cache to get the value. Always get a new value from the\n * getter function.\n *\n * @default false\n */\n noCache?: boolean\n\n /**\n * When getting a value from the cache, allow the value to be returned even if\n * it is stale.\n *\n * Has no effect if the `isStale` option was not provided to the CachedGetter.\n *\n * @default true // If the CachedGetter has an isStale option\n * @default false // If no isStale option was provided to the CachedGetter\n */\n allowStale?: boolean\n}\n\nexport type GetterOptions<C = void> = {\n context: C extends void ? undefined : C\n noCache: boolean\n signal?: AbortSignal\n}\n\nexport type Getter<K extends Key, V extends Value, C = void> = (\n key: K,\n options: GetterOptions<C>,\n storedValue: undefined | V,\n) => Awaitable<V>\n\nexport type CachedGetterOptions<K extends Key, V extends Value> = {\n isStale?: (key: K, value: V) => boolean | PromiseLike<boolean>\n onStoreError?: (err: unknown, key: K, value: V) => void | PromiseLike<void>\n deleteOnError?: (\n err: unknown,\n key: K,\n value: V,\n ) => boolean | PromiseLike<boolean>\n}\n\ntype PendingItem<V> = Promise<{ value: V; isFresh: boolean }>\n\nconst returnTrue = () => true\nconst returnFalse = () => false\n\n/**\n * Wrapper utility that uses a store to speed up the retrieval of values from an\n * (expensive) getter function.\n */\nexport class CachedGetter<\n K extends Key = string,\n V extends Value = Value,\n C = void,\n> {\n private readonly pending = new Map<K, PendingItem<V>>()\n\n constructor(\n readonly getter: Getter<K, V, C>,\n readonly store: SimpleStore<K, V>,\n readonly options: CachedGetterOptions<K, V> = {},\n ) {}\n\n async get(\n key: C extends void ? K : never,\n options?: GetCachedOptions<C>,\n ): Promise<V>\n async get(\n key: C extends void ? never : K,\n options: GetCachedOptions<C>,\n ): Promise<V>\n async get(\n key: K,\n {\n signal,\n context,\n allowStale = false,\n noCache = false,\n } = {} as GetCachedOptions<C>,\n ): Promise<V> {\n signal?.throwIfAborted()\n\n const { isStale, deleteOnError } = this.options\n\n const allowStored: (value: V) => Awaitable<boolean> = noCache\n ? returnFalse // Never allow stored values to be returned\n : allowStale || isStale == null\n ? returnTrue // Always allow stored values to be returned\n : async (value: V) => !(await isStale(key, value))\n\n // As long as concurrent requests are made for the same key, only one\n // request will be made to the getStored & getter functions at a time. This\n // works because there is no async operation between the while() loop and\n // the pending.set() call below. Because of the single threaded nature of\n // JavaScript, the pending item will be set before the next iteration of the\n // while loop of any concurrent request.\n let previousExecutionFlow: undefined | PendingItem<V>\n while ((previousExecutionFlow = this.pending.get(key))) {\n try {\n // If a concurrent request is already in progress, wait for it to finish\n const { isFresh, value } = await previousExecutionFlow\n\n // Use the concurrent request's result if it is fresh\n if (isFresh) return value\n // Use the concurrent request's result if not fresh (loaded from the\n // store), and matches the conditions for using a stored value.\n if (await allowStored(value)) return value\n } catch {\n // Ignore errors from previous execution flows (they will have been\n // propagated by that flow).\n }\n\n // Break the loop if the signal was aborted\n signal?.throwIfAborted()\n }\n\n const currentExecutionFlow: PendingItem<V> = Promise.resolve()\n .then(async () => {\n const storedValue = await this.getStored(key, { signal })\n\n if (storedValue !== undefined && (await allowStored(storedValue))) {\n // Use the stored value as return value for the current execution\n // flow. Notify other concurrent execution flows (that should be\n // \"stuck\" in the loop before until this promise resolves) that we got\n // a value, but that it came from the store (isFresh = false).\n return { isFresh: false, value: storedValue }\n }\n\n return Promise.resolve()\n .then(async () => {\n const options = { signal, noCache, context } as GetterOptions<C>\n return this.getter.call(null, key, options, storedValue)\n })\n .catch(async (err) => {\n if (storedValue !== undefined) {\n try {\n if (await deleteOnError?.(err, key, storedValue)) {\n await this.delStored(key, err)\n }\n } catch (error) {\n throw new AggregateError(\n [err, error],\n 'Error while deleting stored value',\n )\n }\n }\n throw err\n })\n .then(async (value) => {\n // The value should be stored even is the signal was aborted.\n await this.setStored(key, value)\n return { isFresh: true, value }\n })\n })\n .finally(() => {\n this.pending.delete(key)\n })\n\n if (this.pending.has(key)) {\n // This should never happen. Indeed, there must not be any 'await'\n // statement between this and the loop iteration check meaning that\n // this.pending.get returned undefined. It is there to catch bugs that\n // would occur in future changes to the code.\n throw new Error('Concurrent request for the same key')\n }\n\n this.pending.set(key, currentExecutionFlow)\n\n const { value } = await currentExecutionFlow\n return value\n }\n\n async getStored(key: K, options?: GetOptions): Promise<V | undefined> {\n try {\n return await this.store.get(key, options)\n } catch (err) {\n return undefined\n }\n }\n\n async setStored(key: K, value: V): Promise<void> {\n try {\n await this.store.set(key, value)\n } catch (err) {\n const onStoreError = this.options?.onStoreError\n await onStoreError?.(err, key, value)\n }\n }\n\n async delStored(key: K, _cause?: unknown): Promise<void> {\n await this.store.del(key)\n }\n}\n"]}
1
+ {"version":3,"file":"cached-getter.js","sourceRoot":"","sources":["../src/cached-getter.ts"],"names":[],"mappings":"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","sourcesContent":["import { GetOptions, Key, SimpleStore, Value } from './simple-store.js'\nimport { Awaitable, ContextOptions } from './util.js'\n\nexport type { GetOptions }\nexport type GetCachedOptions<C = void> = ContextOptions<C> & {\n signal?: AbortSignal\n\n /**\n * Do not use the cache to get the value. Always get a new value from the\n * getter function.\n *\n * @default false\n */\n noCache?: boolean\n\n /**\n * When getting a value from the cache, allow the value to be returned even if\n * it is stale.\n *\n * Has no effect if the `isStale` option was not provided to the CachedGetter.\n *\n * @default true // If the CachedGetter has an isStale option\n * @default false // If no isStale option was provided to the CachedGetter\n */\n allowStale?: boolean\n}\n\nexport type GetterOptions<C = void> = {\n context: C extends void ? undefined : C\n noCache: boolean\n signal?: AbortSignal\n}\n\nexport type Getter<K extends Key, V extends Value, C = void> = (\n key: K,\n options: GetterOptions<C>,\n storedValue: undefined | V,\n) => Awaitable<V>\n\nexport type CachedGetterOptions<K extends Key, V extends Value> = {\n isStale?: (key: K, value: V) => boolean | PromiseLike<boolean>\n onStoreError?: (err: unknown, key: K, value: V) => void | PromiseLike<void>\n deleteOnError?: (\n err: unknown,\n key: K,\n value: V,\n ) => boolean | PromiseLike<boolean>\n}\n\ntype PendingItem<V> = Promise<{ value: V; isFresh: boolean }>\n\nconst returnTrue = () => true\nconst returnFalse = () => false\n\n/**\n * Wrapper utility that uses a store to speed up the retrieval of values from an\n * (expensive) getter function.\n */\nexport class CachedGetter<\n K extends Key = string,\n V extends Value = Value,\n C = void,\n> {\n private readonly pending = new Map<K, PendingItem<V>>()\n\n constructor(\n readonly getter: Getter<K, V, C>,\n readonly store: SimpleStore<K, V>,\n readonly options: CachedGetterOptions<K, V> = {},\n ) {}\n\n async get(\n key: C extends void ? K : never,\n options?: GetCachedOptions<C>,\n ): Promise<V>\n async get(\n key: C extends void ? never : K,\n options: GetCachedOptions<C>,\n ): Promise<V>\n async get(\n key: K,\n {\n signal,\n context,\n allowStale = false,\n noCache = false,\n } = {} as GetCachedOptions<C>,\n ): Promise<V> {\n signal?.throwIfAborted()\n\n const { isStale, deleteOnError } = this.options\n\n const allowStored: (value: V) => Awaitable<boolean> = noCache\n ? returnFalse // Never allow stored values to be returned\n : allowStale || isStale == null\n ? returnTrue // Always allow stored values to be returned\n : async (value: V) => !(await isStale(key, value))\n\n // As long as concurrent requests are made for the same key, only one\n // request will be made to the getStored & getter functions at a time. This\n // works because there is no async operation between the while() loop and\n // the pending.set() call below. Because of the single threaded nature of\n // JavaScript, the pending item will be set before the next iteration of the\n // while loop of any concurrent request.\n let previousExecutionFlow: undefined | PendingItem<V>\n while ((previousExecutionFlow = this.pending.get(key))) {\n try {\n // If a concurrent request is already in progress, wait for it to finish\n const { isFresh, value } = await previousExecutionFlow\n\n // Use the concurrent request's result if it is fresh\n if (isFresh) return value\n // Use the concurrent request's result if not fresh (loaded from the\n // store), and matches the conditions for using a stored value.\n if (await allowStored(value)) return value\n } catch {\n // Ignore errors from previous execution flows (they will have been\n // propagated by that flow).\n }\n\n // Break the loop if the signal was aborted\n signal?.throwIfAborted()\n }\n\n const currentExecutionFlow: PendingItem<V> = Promise.resolve()\n .then(async () => {\n const storedValue = await this.getStored(key, { signal })\n\n if (storedValue !== undefined && (await allowStored(storedValue))) {\n // Use the stored value as return value for the current execution\n // flow. Notify other concurrent execution flows (that should be\n // \"stuck\" in the loop before until this promise resolves) that we got\n // a value, but that it came from the store (isFresh = false).\n return { isFresh: false, value: storedValue }\n }\n\n return Promise.resolve()\n .then(async () => {\n const options = { signal, noCache, context } as GetterOptions<C>\n return this.getter.call(null, key, options, storedValue)\n })\n .catch(async (err) => {\n if (storedValue !== undefined) {\n try {\n if (await deleteOnError?.(err, key, storedValue)) {\n await this.delStored(key, err)\n }\n } catch (error) {\n throw new AggregateError(\n [err, error],\n 'Error while deleting stored value',\n )\n }\n }\n throw err\n })\n .then(async (value) => {\n // The value should be stored even is the signal was aborted.\n await this.setStored(key, value)\n return { isFresh: true, value }\n })\n })\n .finally(() => {\n this.pending.delete(key)\n })\n\n if (this.pending.has(key)) {\n // This should never happen. Indeed, there must not be any 'await'\n // statement between this and the loop iteration check meaning that\n // this.pending.get returned undefined. It is there to catch bugs that\n // would occur in future changes to the code.\n throw new Error('Concurrent request for the same key')\n }\n\n this.pending.set(key, currentExecutionFlow)\n\n const { value } = await currentExecutionFlow\n return value\n }\n\n async getStored(key: K, options?: GetOptions): Promise<V | undefined> {\n try {\n return await this.store.get(key, options)\n } catch (err) {\n return undefined\n }\n }\n\n async setStored(key: K, value: V): Promise<void> {\n try {\n await this.store.set(key, value)\n } catch (err) {\n const onStoreError = this.options?.onStoreError\n await onStoreError?.(err, key, value)\n }\n }\n\n async delStored(key: K, _cause?: unknown): Promise<void> {\n await this.store.del(key)\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto-labs/simple-store",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },
@@ -23,10 +23,8 @@
23
23
  "default": "./dist/index.js"
24
24
  }
25
25
  },
26
- "devDependencies": {
27
- "typescript": "^6.0.3"
28
- },
26
+ "devDependencies": {},
29
27
  "scripts": {
30
- "build": "tsc --build tsconfig.build.json"
28
+ "build": "tsgo --build tsconfig.build.json"
31
29
  }
32
30
  }
@@ -2,7 +2,7 @@
2
2
  "extends": "../../../tsconfig/isomorphic.json",
3
3
  "compilerOptions": {
4
4
  "rootDir": "./src",
5
- "outDir": "./dist"
5
+ "outDir": "./dist",
6
6
  },
7
- "include": ["./src"]
7
+ "include": ["./src"],
8
8
  }
@@ -1 +1 @@
1
- {"root":["./src/cached-getter.ts","./src/index.ts","./src/simple-store.ts","./src/util.ts"],"version":"6.0.3"}
1
+ {"version":"7.0.0-dev.20260614.1","root":["./src/cached-getter.ts","./src/index.ts","./src/simple-store.ts","./src/util.ts"]}
package/tsconfig.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "include": [],
3
- "references": [{ "path": "./tsconfig.build.json" }]
3
+ "references": [{ "path": "./tsconfig.build.json" }],
4
4
  }