@atproto-labs/simple-store-memory 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # @atproto-labs/simple-store-memory
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#2482](https://github.com/bluesky-social/atproto/pull/2482) [`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add OAuth provider capability & support for DPoP signed tokens
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646)]:
12
+ - @atproto-labs/simple-store@0.1.0
package/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ Dual MIT/Apache-2.0 License
2
+
3
+ Copyright (c) 2022-2024 Bluesky PBC, and Contributors
4
+
5
+ Except as otherwise noted in individual files, this software is licensed under the MIT license (<http://opensource.org/licenses/MIT>), or the Apache License, Version 2.0 (<http://www.apache.org/licenses/LICENSE-2.0>).
6
+
7
+ Downstream projects and end users may chose either license individually, or both together, at their discretion. The motivation for this dual-licensing is the additional software patent assurance provided by Apache 2.0.
@@ -0,0 +1,52 @@
1
+ import { SimpleStore, Key, Value } from '@atproto-labs/simple-store';
2
+ export type SimpleStoreMemoryOptions<K extends Key, V extends Value> = {
3
+ /**
4
+ * The maximum number of entries in the cache.
5
+ */
6
+ max?: number;
7
+ /**
8
+ * The time-to-live of a cache entry, in milliseconds.
9
+ */
10
+ ttl?: number;
11
+ /**
12
+ * Whether to automatically prune expired entries.
13
+ */
14
+ ttlAutopurge?: boolean;
15
+ /**
16
+ * The maximum total size of the cache, in units defined by the sizeCalculation
17
+ * function.
18
+ *
19
+ * @default No limit
20
+ */
21
+ maxSize?: number;
22
+ /**
23
+ * The maximum size of a single cache entry, in units defined by the
24
+ * sizeCalculation function.
25
+ *
26
+ * @default No limit
27
+ */
28
+ maxEntrySize?: number;
29
+ /**
30
+ * A function that returns the size of a value. The size is used to determine
31
+ * when the cache should be pruned, based on `maxSize`.
32
+ *
33
+ * @default The (rough) size in bytes used in memory.
34
+ */
35
+ sizeCalculation?: (value: V, key: K) => number;
36
+ } & ({
37
+ max: number;
38
+ } | {
39
+ maxSize: number;
40
+ } | {
41
+ ttl: number;
42
+ ttlAutopurge: boolean;
43
+ });
44
+ export declare class SimpleStoreMemory<K extends Key, V extends Value> implements SimpleStore<K, V> {
45
+ #private;
46
+ constructor({ sizeCalculation, ...options }: SimpleStoreMemoryOptions<K, V>);
47
+ get(key: K): V | undefined;
48
+ set(key: K, value: V): void;
49
+ del(key: K): void;
50
+ clear(): void;
51
+ }
52
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAKpE,MAAM,MAAM,wBAAwB,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,KAAK,IAAI;IACrE;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IAEtB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,MAAM,CAAA;CAC/C,GAAG,CACA;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACf;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GACnB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CACzC,CAAA;AAYD,qBAAa,iBAAiB,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,KAAK,CAC3D,YAAW,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;;gBAIhB,EAAE,eAAe,EAAE,GAAG,OAAO,EAAE,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC;IAe3E,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAO1B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAI3B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI;IAIjB,KAAK,IAAI,IAAI;CAGd"}
package/dist/index.js ADDED
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _SimpleStoreMemory_cache;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.SimpleStoreMemory = void 0;
16
+ const lru_cache_1 = require("lru-cache");
17
+ const util_js_1 = require("./util.js");
18
+ // LRUCache does not allow storing "null", so we use a symbol to represent it.
19
+ const nullSymbol = Symbol('nullItem');
20
+ const toLruValue = (value) => (value === null ? nullSymbol : value);
21
+ const fromLruValue = (value) => (value === nullSymbol ? null : value);
22
+ class SimpleStoreMemory {
23
+ constructor({ sizeCalculation, ...options }) {
24
+ _SimpleStoreMemory_cache.set(this, void 0);
25
+ __classPrivateFieldSet(this, _SimpleStoreMemory_cache, new lru_cache_1.LRUCache({
26
+ ...options,
27
+ allowStale: false,
28
+ updateAgeOnGet: false,
29
+ updateAgeOnHas: false,
30
+ sizeCalculation: sizeCalculation
31
+ ? (value, key) => sizeCalculation(fromLruValue(value), key)
32
+ : options.maxEntrySize != null || options.maxSize != null
33
+ ? // maxEntrySize and maxSize require a size calculation function.
34
+ util_js_1.roughSizeOfObject
35
+ : undefined,
36
+ }), "f");
37
+ }
38
+ get(key) {
39
+ const value = __classPrivateFieldGet(this, _SimpleStoreMemory_cache, "f").get(key);
40
+ if (value === undefined)
41
+ return undefined;
42
+ return fromLruValue(value);
43
+ }
44
+ set(key, value) {
45
+ __classPrivateFieldGet(this, _SimpleStoreMemory_cache, "f").set(key, toLruValue(value));
46
+ }
47
+ del(key) {
48
+ __classPrivateFieldGet(this, _SimpleStoreMemory_cache, "f").delete(key);
49
+ }
50
+ clear() {
51
+ __classPrivateFieldGet(this, _SimpleStoreMemory_cache, "f").clear();
52
+ }
53
+ }
54
+ exports.SimpleStoreMemory = SimpleStoreMemory;
55
+ _SimpleStoreMemory_cache = new WeakMap();
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,yCAAoC;AAEpC,uCAA6C;AA+C7C,8EAA8E;AAC9E,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAA;AAIrC,MAAM,UAAU,GAAG,CAAkB,KAAQ,EAAE,EAAE,CAC/C,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAkB,CAAA;AACxD,MAAM,YAAY,GAAG,CAAkB,KAAoB,EAAE,EAAE,CAC7D,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAM,CAAA;AAE5C,MAAa,iBAAiB;IAK5B,YAAY,EAAE,eAAe,EAAE,GAAG,OAAO,EAAkC;QAF3E,2CAAkC;QAGhC,uBAAA,IAAI,4BAAU,IAAI,oBAAQ,CAAmB;YAC3C,GAAG,OAAO;YACV,UAAU,EAAE,KAAK;YACjB,cAAc,EAAE,KAAK;YACrB,cAAc,EAAE,KAAK;YACrB,eAAe,EAAE,eAAe;gBAC9B,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,eAAe,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC;gBAC3D,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI,IAAI,IAAI,OAAO,CAAC,OAAO,IAAI,IAAI;oBACvD,CAAC,CAAC,gEAAgE;wBAChE,2BAAiB;oBACnB,CAAC,CAAC,SAAS;SAChB,CAAC,MAAA,CAAA;IACJ,CAAC;IAED,GAAG,CAAC,GAAM;QACR,MAAM,KAAK,GAAG,uBAAA,IAAI,gCAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAClC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QAEzC,OAAO,YAAY,CAAC,KAAK,CAAC,CAAA;IAC5B,CAAC;IAED,GAAG,CAAC,GAAM,EAAE,KAAQ;QAClB,uBAAA,IAAI,gCAAO,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;IACzC,CAAC;IAED,GAAG,CAAC,GAAM;QACR,uBAAA,IAAI,gCAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;IAED,KAAK;QACH,uBAAA,IAAI,gCAAO,CAAC,KAAK,EAAE,CAAA;IACrB,CAAC;CACF;AAtCD,8CAsCC"}
package/dist/util.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @see {@link https://stackoverflow.com/a/11900218/356537}
3
+ */
4
+ export declare function roughSizeOfObject(value: unknown): number;
5
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAuExD"}
package/dist/util.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.roughSizeOfObject = void 0;
4
+ const knownSizes = new WeakMap();
5
+ /**
6
+ * @see {@link https://stackoverflow.com/a/11900218/356537}
7
+ */
8
+ function roughSizeOfObject(value) {
9
+ const objectList = new Set();
10
+ const stack = [value]; // This would be more efficient using a circular buffer
11
+ let bytes = 0;
12
+ while (stack.length) {
13
+ const value = stack.pop();
14
+ // > All objects on the heap start with a shape descriptor, which takes one
15
+ // > pointer size (usually 4 bytes these days, thanks to "pointer
16
+ // > compression" on 64-bit platforms).
17
+ switch (typeof value) {
18
+ // Types are ordered by frequency
19
+ case 'string':
20
+ // https://stackoverflow.com/a/68791382/356537
21
+ bytes += 12 + 4 * Math.ceil(value.length / 4);
22
+ break;
23
+ case 'number':
24
+ bytes += 12; // Shape descriptor + double
25
+ break;
26
+ case 'boolean':
27
+ bytes += 4; // Shape descriptor
28
+ break;
29
+ case 'object':
30
+ bytes += 4; // Shape descriptor
31
+ if (value === null) {
32
+ break;
33
+ }
34
+ if (knownSizes.has(value)) {
35
+ bytes += knownSizes.get(value);
36
+ break;
37
+ }
38
+ if (objectList.has(value))
39
+ continue;
40
+ objectList.add(value);
41
+ if (Array.isArray(value)) {
42
+ bytes += 4;
43
+ stack.push(...value);
44
+ }
45
+ else {
46
+ bytes += 8;
47
+ const keys = Object.getOwnPropertyNames(value);
48
+ for (let i = 0; i < keys.length; i++) {
49
+ bytes += 4;
50
+ const key = keys[i];
51
+ const val = value[key];
52
+ if (val !== undefined)
53
+ stack.push(val);
54
+ stack.push(key);
55
+ }
56
+ }
57
+ break;
58
+ case 'function':
59
+ bytes += 8; // Shape descriptor + pointer (assuming functions are shared)
60
+ break;
61
+ case 'symbol':
62
+ bytes += 8; // Shape descriptor + pointer
63
+ break;
64
+ case 'bigint':
65
+ bytes += 16; // Shape descriptor + BigInt
66
+ break;
67
+ }
68
+ }
69
+ if (typeof value === 'object' && value !== null) {
70
+ knownSizes.set(value, bytes);
71
+ }
72
+ return bytes;
73
+ }
74
+ exports.roughSizeOfObject = roughSizeOfObject;
75
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;;AAAA,MAAM,UAAU,GAAG,IAAI,OAAO,EAAkB,CAAA;AAEhD;;GAEG;AACH,SAAgB,iBAAiB,CAAC,KAAc;IAC9C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAE,CAAA;IAC5B,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAA,CAAC,uDAAuD;IAC7E,IAAI,KAAK,GAAG,CAAC,CAAA;IAEb,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,CAAA;QAEzB,2EAA2E;QAC3E,iEAAiE;QACjE,uCAAuC;QAEvC,QAAQ,OAAO,KAAK,EAAE,CAAC;YACrB,iCAAiC;YACjC,KAAK,QAAQ;gBACX,8CAA8C;gBAC9C,KAAK,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;gBAC7C,MAAK;YACP,KAAK,QAAQ;gBACX,KAAK,IAAI,EAAE,CAAA,CAAC,4BAA4B;gBACxC,MAAK;YACP,KAAK,SAAS;gBACZ,KAAK,IAAI,CAAC,CAAA,CAAC,mBAAmB;gBAC9B,MAAK;YACP,KAAK,QAAQ;gBACX,KAAK,IAAI,CAAC,CAAA,CAAC,mBAAmB;gBAE9B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBACnB,MAAK;gBACP,CAAC;gBAED,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1B,KAAK,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAE,CAAA;oBAC/B,MAAK;gBACP,CAAC;gBAED,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;oBAAE,SAAQ;gBACnC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;gBAErB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACzB,KAAK,IAAI,CAAC,CAAA;oBACV,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAA;gBACtB,CAAC;qBAAM,CAAC;oBACN,KAAK,IAAI,CAAC,CAAA;oBACV,MAAM,IAAI,GAAG,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAA;oBAC9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;wBACrC,KAAK,IAAI,CAAC,CAAA;wBACV,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;wBACnB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;wBACtB,IAAI,GAAG,KAAK,SAAS;4BAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;wBACtC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;oBACjB,CAAC;gBACH,CAAC;gBACD,MAAK;YACP,KAAK,UAAU;gBACb,KAAK,IAAI,CAAC,CAAA,CAAC,6DAA6D;gBACxE,MAAK;YACP,KAAK,QAAQ;gBACX,KAAK,IAAI,CAAC,CAAA,CAAC,6BAA6B;gBACxC,MAAK;YACP,KAAK,QAAQ;gBACX,KAAK,IAAI,EAAE,CAAA,CAAC,4BAA4B;gBACxC,MAAK;QACT,CAAC;IACH,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IAC9B,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAvED,8CAuEC"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@atproto-labs/simple-store-memory",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "description": "Memory based simple-store implementation",
6
+ "keywords": [
7
+ "cache",
8
+ "isomorphic",
9
+ "memory"
10
+ ],
11
+ "homepage": "https://atproto.com",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/bluesky-social/atproto",
15
+ "directory": "packages/internal/simple-store-memory"
16
+ },
17
+ "type": "commonjs",
18
+ "main": "dist/index.js",
19
+ "types": "dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ }
25
+ },
26
+ "dependencies": {
27
+ "lru-cache": "^10.2.0",
28
+ "@atproto-labs/simple-store": "0.1.0"
29
+ },
30
+ "devDependencies": {
31
+ "typescript": "^5.3.3"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc --build tsconfig.build.json"
35
+ }
36
+ }
package/src/index.ts ADDED
@@ -0,0 +1,99 @@
1
+ import { SimpleStore, Key, Value } from '@atproto-labs/simple-store'
2
+ import { LRUCache } from 'lru-cache'
3
+
4
+ import { roughSizeOfObject } from './util.js'
5
+
6
+ export type SimpleStoreMemoryOptions<K extends Key, V extends Value> = {
7
+ /**
8
+ * The maximum number of entries in the cache.
9
+ */
10
+ max?: number
11
+
12
+ /**
13
+ * The time-to-live of a cache entry, in milliseconds.
14
+ */
15
+ ttl?: number
16
+
17
+ /**
18
+ * Whether to automatically prune expired entries.
19
+ */
20
+ ttlAutopurge?: boolean
21
+
22
+ /**
23
+ * The maximum total size of the cache, in units defined by the sizeCalculation
24
+ * function.
25
+ *
26
+ * @default No limit
27
+ */
28
+ maxSize?: number
29
+
30
+ /**
31
+ * The maximum size of a single cache entry, in units defined by the
32
+ * sizeCalculation function.
33
+ *
34
+ * @default No limit
35
+ */
36
+ maxEntrySize?: number
37
+
38
+ /**
39
+ * A function that returns the size of a value. The size is used to determine
40
+ * when the cache should be pruned, based on `maxSize`.
41
+ *
42
+ * @default The (rough) size in bytes used in memory.
43
+ */
44
+ sizeCalculation?: (value: V, key: K) => number
45
+ } & ( // Memory is not infinite, so at least one pruning option is required.
46
+ | { max: number }
47
+ | { maxSize: number }
48
+ | { ttl: number; ttlAutopurge: boolean }
49
+ )
50
+
51
+ // LRUCache does not allow storing "null", so we use a symbol to represent it.
52
+ const nullSymbol = Symbol('nullItem')
53
+ type AsLruValue<V extends Value> = V extends null
54
+ ? typeof nullSymbol
55
+ : Exclude<V, null>
56
+ const toLruValue = <V extends Value>(value: V) =>
57
+ (value === null ? nullSymbol : value) as AsLruValue<V>
58
+ const fromLruValue = <V extends Value>(value: AsLruValue<V>) =>
59
+ (value === nullSymbol ? null : value) as V
60
+
61
+ export class SimpleStoreMemory<K extends Key, V extends Value>
62
+ implements SimpleStore<K, V>
63
+ {
64
+ #cache: LRUCache<K, AsLruValue<V>>
65
+
66
+ constructor({ sizeCalculation, ...options }: SimpleStoreMemoryOptions<K, V>) {
67
+ this.#cache = new LRUCache<K, AsLruValue<V>>({
68
+ ...options,
69
+ allowStale: false,
70
+ updateAgeOnGet: false,
71
+ updateAgeOnHas: false,
72
+ sizeCalculation: sizeCalculation
73
+ ? (value, key) => sizeCalculation(fromLruValue(value), key)
74
+ : options.maxEntrySize != null || options.maxSize != null
75
+ ? // maxEntrySize and maxSize require a size calculation function.
76
+ roughSizeOfObject
77
+ : undefined,
78
+ })
79
+ }
80
+
81
+ get(key: K): V | undefined {
82
+ const value = this.#cache.get(key)
83
+ if (value === undefined) return undefined
84
+
85
+ return fromLruValue(value)
86
+ }
87
+
88
+ set(key: K, value: V): void {
89
+ this.#cache.set(key, toLruValue(value))
90
+ }
91
+
92
+ del(key: K): void {
93
+ this.#cache.delete(key)
94
+ }
95
+
96
+ clear(): void {
97
+ this.#cache.clear()
98
+ }
99
+ }
package/src/util.ts ADDED
@@ -0,0 +1,77 @@
1
+ const knownSizes = new WeakMap<object, number>()
2
+
3
+ /**
4
+ * @see {@link https://stackoverflow.com/a/11900218/356537}
5
+ */
6
+ export function roughSizeOfObject(value: unknown): number {
7
+ const objectList = new Set()
8
+ const stack = [value] // This would be more efficient using a circular buffer
9
+ let bytes = 0
10
+
11
+ while (stack.length) {
12
+ const value = stack.pop()
13
+
14
+ // > All objects on the heap start with a shape descriptor, which takes one
15
+ // > pointer size (usually 4 bytes these days, thanks to "pointer
16
+ // > compression" on 64-bit platforms).
17
+
18
+ switch (typeof value) {
19
+ // Types are ordered by frequency
20
+ case 'string':
21
+ // https://stackoverflow.com/a/68791382/356537
22
+ bytes += 12 + 4 * Math.ceil(value.length / 4)
23
+ break
24
+ case 'number':
25
+ bytes += 12 // Shape descriptor + double
26
+ break
27
+ case 'boolean':
28
+ bytes += 4 // Shape descriptor
29
+ break
30
+ case 'object':
31
+ bytes += 4 // Shape descriptor
32
+
33
+ if (value === null) {
34
+ break
35
+ }
36
+
37
+ if (knownSizes.has(value)) {
38
+ bytes += knownSizes.get(value)!
39
+ break
40
+ }
41
+
42
+ if (objectList.has(value)) continue
43
+ objectList.add(value)
44
+
45
+ if (Array.isArray(value)) {
46
+ bytes += 4
47
+ stack.push(...value)
48
+ } else {
49
+ bytes += 8
50
+ const keys = Object.getOwnPropertyNames(value)
51
+ for (let i = 0; i < keys.length; i++) {
52
+ bytes += 4
53
+ const key = keys[i]
54
+ const val = value[key]
55
+ if (val !== undefined) stack.push(val)
56
+ stack.push(key)
57
+ }
58
+ }
59
+ break
60
+ case 'function':
61
+ bytes += 8 // Shape descriptor + pointer (assuming functions are shared)
62
+ break
63
+ case 'symbol':
64
+ bytes += 8 // Shape descriptor + pointer
65
+ break
66
+ case 'bigint':
67
+ bytes += 16 // Shape descriptor + BigInt
68
+ break
69
+ }
70
+ }
71
+
72
+ if (typeof value === 'object' && value !== null) {
73
+ knownSizes.set(value, bytes)
74
+ }
75
+
76
+ return bytes
77
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../../tsconfig/isomorphic.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./dist"
6
+ },
7
+ "include": ["./src"]
8
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "include": [],
3
+ "references": [{ "path": "./tsconfig.build.json" }]
4
+ }