@dxos/util 0.8.4-main.c85a9c8dae → 0.8.4-main.cb12b3f963
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/lib/browser/index.mjs +208 -127
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +208 -127
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/array-to-hex.d.ts.map +1 -1
- package/dist/types/src/array.d.ts.map +1 -1
- package/dist/types/src/binder.d.ts.map +1 -1
- package/dist/types/src/bitfield.d.ts.map +1 -1
- package/dist/types/src/browser-storage.d.ts +9 -0
- package/dist/types/src/browser-storage.d.ts.map +1 -0
- package/dist/types/src/callback-collection.d.ts.map +1 -1
- package/dist/types/src/callback.d.ts.map +1 -1
- package/dist/types/src/case.d.ts.map +1 -1
- package/dist/types/src/chunk-array.d.ts.map +1 -1
- package/dist/types/src/circular-buffer.d.ts.map +1 -1
- package/dist/types/src/clear-undefined.d.ts.map +1 -1
- package/dist/types/src/complex.d.ts +15 -0
- package/dist/types/src/complex.d.ts.map +1 -1
- package/dist/types/src/composite-key.d.ts +10 -0
- package/dist/types/src/composite-key.d.ts.map +1 -0
- package/dist/types/src/composite-key.test.d.ts +2 -0
- package/dist/types/src/composite-key.test.d.ts.map +1 -0
- package/dist/types/src/deep.d.ts.map +1 -1
- package/dist/types/src/defer-function.d.ts.map +1 -1
- package/dist/types/src/defer.d.ts.map +1 -1
- package/dist/types/src/di-container.d.ts.map +1 -1
- package/dist/types/src/di-key.d.ts +7 -5
- package/dist/types/src/di-key.d.ts.map +1 -1
- package/dist/types/src/entry.d.ts.map +1 -1
- package/dist/types/src/error-format.d.ts.map +1 -1
- package/dist/types/src/filename.d.ts.map +1 -1
- package/dist/types/src/for-each-async.d.ts.map +1 -1
- package/dist/types/src/human-hash.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +4 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/instance-id.d.ts.map +1 -1
- package/dist/types/src/interval.d.ts.map +1 -1
- package/dist/types/src/join-tables.d.ts.map +1 -1
- package/dist/types/src/json.d.ts.map +1 -1
- package/dist/types/src/map-values.d.ts.map +1 -1
- package/dist/types/src/map.d.ts.map +1 -1
- package/dist/types/src/object.d.ts.map +1 -1
- package/dist/types/src/order-keys.d.ts.map +1 -1
- package/dist/types/src/order.d.ts.map +1 -1
- package/dist/types/src/pick.d.ts.map +1 -1
- package/dist/types/src/platform.d.ts +1 -1
- package/dist/types/src/position.d.ts +1 -1
- package/dist/types/src/position.d.ts.map +1 -1
- package/dist/types/src/random.d.ts.map +1 -1
- package/dist/types/src/range.d.ts.map +1 -1
- package/dist/types/src/reducers.d.ts.map +1 -1
- package/dist/types/src/remove-undefined-keys.d.ts.map +1 -1
- package/dist/types/src/retry.d.ts.map +1 -1
- package/dist/types/src/safe-await.d.ts.map +1 -1
- package/dist/types/src/safe-instanceof.d.ts.map +1 -1
- package/dist/types/src/safe-parse.d.ts.map +1 -1
- package/dist/types/src/safe-stringify.d.ts.map +1 -1
- package/dist/types/src/sliding-window-summary.d.ts.map +1 -1
- package/dist/types/src/sort.d.ts.map +1 -1
- package/dist/types/src/string.d.ts.map +1 -1
- package/dist/types/src/sum.d.ts.map +1 -1
- package/dist/types/src/throw-unhandled-error.d.ts.map +1 -1
- package/dist/types/src/to-fallback.d.ts +4 -4
- package/dist/types/src/to-fallback.d.ts.map +1 -1
- package/dist/types/src/tracer.d.ts.map +1 -1
- package/dist/types/src/tree.d.ts.map +1 -1
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/uint8array.d.ts.map +1 -1
- package/dist/types/src/unit.d.ts.map +1 -1
- package/dist/types/src/url.d.ts.map +1 -1
- package/dist/types/src/weak.d.ts +15 -0
- package/dist/types/src/weak.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -9
- package/src/browser-storage.ts +60 -0
- package/src/complex.ts +32 -0
- package/src/composite-key.test.ts +31 -0
- package/src/composite-key.ts +16 -0
- package/src/di-key.ts +10 -8
- package/src/error-format.ts +3 -1
- package/src/index.ts +4 -2
- package/src/string.ts +6 -2
- package/src/unit.ts +6 -2
- package/src/weak.ts +52 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/util",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.cb12b3f963",
|
|
4
4
|
"description": "Temporary bucket for misc functions, which should graduate into separate packages.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -23,22 +23,19 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"types": "dist/types/src/index.d.ts",
|
|
26
|
-
"typesVersions": {
|
|
27
|
-
"*": {}
|
|
28
|
-
},
|
|
29
26
|
"files": [
|
|
30
27
|
"dist",
|
|
31
28
|
"src"
|
|
32
29
|
],
|
|
33
30
|
"dependencies": {
|
|
34
31
|
"@hazae41/symbol-dispose-polyfill": "^1.0.2",
|
|
35
|
-
"@dxos/debug": "0.8.4-main.
|
|
36
|
-
"@dxos/invariant": "0.8.4-main.
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
32
|
+
"@dxos/debug": "0.8.4-main.cb12b3f963",
|
|
33
|
+
"@dxos/invariant": "0.8.4-main.cb12b3f963",
|
|
34
|
+
"@dxos/keys": "0.8.4-main.cb12b3f963",
|
|
35
|
+
"@dxos/node-std": "0.8.4-main.cb12b3f963"
|
|
39
36
|
},
|
|
40
37
|
"devDependencies": {
|
|
41
|
-
"@dxos/crypto": "0.8.4-main.
|
|
38
|
+
"@dxos/crypto": "0.8.4-main.cb12b3f963"
|
|
42
39
|
},
|
|
43
40
|
"publishConfig": {
|
|
44
41
|
"access": "public"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
/** Delete all IndexedDB databases for this origin. */
|
|
6
|
+
export const clearIndexedDB = async (): Promise<void> => {
|
|
7
|
+
const dbs = await indexedDB.databases();
|
|
8
|
+
const results = await Promise.allSettled(
|
|
9
|
+
dbs
|
|
10
|
+
.filter((db) => db.name != null)
|
|
11
|
+
.map(
|
|
12
|
+
(db) =>
|
|
13
|
+
new Promise<void>((resolve, reject) => {
|
|
14
|
+
const request = indexedDB.deleteDatabase(db.name!);
|
|
15
|
+
request.onsuccess = () => resolve();
|
|
16
|
+
request.onerror = () => reject(request.error);
|
|
17
|
+
request.onblocked = () => reject(new Error(`IndexedDB deletion blocked: ${db.name}`));
|
|
18
|
+
}),
|
|
19
|
+
),
|
|
20
|
+
);
|
|
21
|
+
const failures = results.filter((r): r is PromiseRejectedResult => r.status === 'rejected');
|
|
22
|
+
if (failures.length > 0) {
|
|
23
|
+
throw new AggregateError(
|
|
24
|
+
failures.map((r) => r.reason),
|
|
25
|
+
'Failed to delete some IndexedDB databases',
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Remove all entries from the Origin Private File System. */
|
|
31
|
+
export const clearOPFS = async (): Promise<void> => {
|
|
32
|
+
const root = await navigator.storage.getDirectory();
|
|
33
|
+
const errors: unknown[] = [];
|
|
34
|
+
for await (const [name] of root.entries()) {
|
|
35
|
+
try {
|
|
36
|
+
await root.removeEntry(name, { recursive: true });
|
|
37
|
+
} catch (err) {
|
|
38
|
+
errors.push(err);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (errors.length > 0) {
|
|
42
|
+
throw new AggregateError(errors, 'Failed to remove some OPFS entries');
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Unregister all service workers for this origin. */
|
|
47
|
+
export const clearServiceWorkers = async (): Promise<void> => {
|
|
48
|
+
const regs = await navigator.serviceWorker.getRegistrations();
|
|
49
|
+
for (const reg of regs) {
|
|
50
|
+
await reg.unregister();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** Delete all HTTP caches for this origin. */
|
|
55
|
+
export const clearCaches = async (): Promise<void> => {
|
|
56
|
+
const keys = await caches.keys();
|
|
57
|
+
for (const key of keys) {
|
|
58
|
+
await caches.delete(key);
|
|
59
|
+
}
|
|
60
|
+
};
|
package/src/complex.ts
CHANGED
|
@@ -214,6 +214,38 @@ export class ComplexMap<K, V> implements Map<K, V> {
|
|
|
214
214
|
return this;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Returns the value for the given key if present, otherwise inserts and returns the default value.
|
|
219
|
+
* @param key - The key to look up or insert.
|
|
220
|
+
* @param defaultValue - The value to insert if the key is not present.
|
|
221
|
+
* @returns The existing or newly inserted value.
|
|
222
|
+
*/
|
|
223
|
+
getOrInsert(key: K, defaultValue: V): V {
|
|
224
|
+
const primitive = this._keyProjection(key);
|
|
225
|
+
if (this._values.has(primitive)) {
|
|
226
|
+
return this._values.get(primitive)!;
|
|
227
|
+
}
|
|
228
|
+
this.set(key, defaultValue);
|
|
229
|
+
return defaultValue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Returns the value for the given key if present, otherwise computes, inserts, and returns a new value.
|
|
234
|
+
* The callback is only invoked when the key is missing.
|
|
235
|
+
* @param key - The key to look up or insert.
|
|
236
|
+
* @param callbackfn - Function to compute the value if the key is not present.
|
|
237
|
+
* @returns The existing or newly computed value.
|
|
238
|
+
*/
|
|
239
|
+
getOrInsertComputed(key: K, callbackfn: (key: K) => V): V {
|
|
240
|
+
const primitive = this._keyProjection(key);
|
|
241
|
+
if (this._values.has(primitive)) {
|
|
242
|
+
return this._values.get(primitive)!;
|
|
243
|
+
}
|
|
244
|
+
const value = callbackfn(key);
|
|
245
|
+
this.set(key, value);
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
|
|
217
249
|
get size(): number {
|
|
218
250
|
return this._keys.size;
|
|
219
251
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { compositeKey, splitCompositeKey } from './composite-key';
|
|
8
|
+
|
|
9
|
+
describe('compositeKey', () => {
|
|
10
|
+
test('joins parts with colon', ({ expect }) => {
|
|
11
|
+
expect(compositeKey('a', 'b', 'c')).to.eq('a:b:c');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('single part returns as-is', ({ expect }) => {
|
|
15
|
+
expect(compositeKey('only')).to.eq('only');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('handles empty strings in parts', ({ expect }) => {
|
|
19
|
+
expect(compositeKey('', 'b')).to.eq(':b');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('splitCompositeKey', () => {
|
|
24
|
+
test('splits on colon', ({ expect }) => {
|
|
25
|
+
expect(splitCompositeKey('a:b:c')).to.deep.eq(['a', 'b', 'c']);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('single part returns array of one', ({ expect }) => {
|
|
29
|
+
expect(splitCompositeKey('only')).to.deep.eq(['only']);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
const SEPARATOR = ':';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Build a composite key from string parts joined by a colon separator.
|
|
9
|
+
* Parts must not contain the separator character.
|
|
10
|
+
*/
|
|
11
|
+
export const compositeKey = (...parts: string[]): string => parts.join(SEPARATOR);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Split a composite key back into its parts.
|
|
15
|
+
*/
|
|
16
|
+
export const splitCompositeKey = (key: string): string[] => key.split(SEPARATOR);
|
package/src/di-key.ts
CHANGED
|
@@ -47,7 +47,7 @@ export type DiKey<T> =
|
|
|
47
47
|
}
|
|
48
48
|
| SymbolDiKey<T>;
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
class DiKeyConstructor {
|
|
51
51
|
/**
|
|
52
52
|
* Needed to ensure referential equality of combined keys.
|
|
53
53
|
*
|
|
@@ -57,7 +57,7 @@ export const DiKey = new (class DiKeyConstructor {
|
|
|
57
57
|
*/
|
|
58
58
|
// TODO(dmaretskyi): Disable private members lowering for dev env.
|
|
59
59
|
// TODO(dmaretskyi): Could be a weak map after a NodeJS upgrade.
|
|
60
|
-
|
|
60
|
+
private _combinedRegistry = new WeakMap();
|
|
61
61
|
|
|
62
62
|
define<T>(name: string): DiKey<T> {
|
|
63
63
|
return new SymbolDiKey(name);
|
|
@@ -88,24 +88,26 @@ export const DiKey = new (class DiKeyConstructor {
|
|
|
88
88
|
* Maintains referential equality: `DiKey.combine(A, B) === DiKey.combine(A, B)`
|
|
89
89
|
*/
|
|
90
90
|
combine(...ids: DiKey<any>[]) {
|
|
91
|
-
const map = this
|
|
92
|
-
return defaultMap(this
|
|
91
|
+
const map = this._lookupCombined(this._combinedRegistry, ids);
|
|
92
|
+
return defaultMap(this._combinedRegistry, map, () => this._combinedDescription(ids));
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
getSingletonFactory<T>(id: DiKey<T>): SingletonFactory<T> | undefined {
|
|
96
96
|
return (id as any)[symbolSingleton];
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
private _lookupCombined(map: WeakMap<any, any>, [first, ...rest]: DiKey<any>[]): WeakMap<any, any> {
|
|
100
100
|
const value = defaultMap(map, first, () => new Map());
|
|
101
101
|
if (rest.length > 0) {
|
|
102
|
-
return this
|
|
102
|
+
return this._lookupCombined(value, rest);
|
|
103
103
|
} else {
|
|
104
104
|
return value;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
private _combinedDescription([first, ...rest]: DiKey<any>[]) {
|
|
109
109
|
return `${this.stringify(first)}<${rest.map(this.stringify).join(', ')}>`;
|
|
110
110
|
}
|
|
111
|
-
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const DiKey = new DiKeyConstructor();
|
package/src/error-format.ts
CHANGED
|
@@ -13,7 +13,9 @@ export const formatErrorWithCauses = (error: Error): string => {
|
|
|
13
13
|
while (current) {
|
|
14
14
|
const prefix = level === 0 ? '' : `Caused by: `;
|
|
15
15
|
lines.push(prefix + (current.stack ?? String(current)));
|
|
16
|
-
if (!(current.cause instanceof Error))
|
|
16
|
+
if (!(current.cause instanceof Error)) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
17
19
|
current = current.cause;
|
|
18
20
|
level += 1;
|
|
19
21
|
}
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './array';
|
|
|
7
7
|
export * from './assume';
|
|
8
8
|
export * from './binder';
|
|
9
9
|
export * from './bitfield';
|
|
10
|
+
export * from './browser-storage';
|
|
10
11
|
export * from './callback-collection';
|
|
11
12
|
export * from './callback';
|
|
12
13
|
export * from './case';
|
|
@@ -14,10 +15,12 @@ export * from './chunk-array';
|
|
|
14
15
|
export * from './circular-buffer';
|
|
15
16
|
export * from './clear-undefined';
|
|
16
17
|
export * from './complex';
|
|
18
|
+
export * from './composite-key';
|
|
17
19
|
export * from './deep';
|
|
18
20
|
export * from './defer-function';
|
|
19
21
|
export * from './defer';
|
|
20
22
|
export * from './entry';
|
|
23
|
+
export * from './error-format';
|
|
21
24
|
export * from './filename';
|
|
22
25
|
export * from './for-each-async';
|
|
23
26
|
export * from './human-hash';
|
|
@@ -38,6 +41,7 @@ export * from './random';
|
|
|
38
41
|
export * from './range';
|
|
39
42
|
export * from './reducers';
|
|
40
43
|
export * from './remove-undefined-keys';
|
|
44
|
+
export * from './retry';
|
|
41
45
|
export * from './safe-await';
|
|
42
46
|
export * from './safe-instanceof';
|
|
43
47
|
export * from './safe-parse';
|
|
@@ -55,5 +59,3 @@ export * from './uint8array';
|
|
|
55
59
|
export * from './unit';
|
|
56
60
|
export * from './url';
|
|
57
61
|
export * from './weak';
|
|
58
|
-
export * from './error-format';
|
|
59
|
-
export * from './retry';
|
package/src/string.ts
CHANGED
|
@@ -32,8 +32,12 @@ export function trim(strings: TemplateStringsArray, ...values: any[]) {
|
|
|
32
32
|
// Split into lines and trim leading/trailing blank lines.
|
|
33
33
|
const lines = raw.split('\n');
|
|
34
34
|
|
|
35
|
-
while (lines.length && !lines[0].trim())
|
|
36
|
-
|
|
35
|
+
while (lines.length && !lines[0].trim()) {
|
|
36
|
+
lines.shift();
|
|
37
|
+
}
|
|
38
|
+
while (lines.length && !lines[lines.length - 1].trim()) {
|
|
39
|
+
lines.pop();
|
|
40
|
+
}
|
|
37
41
|
|
|
38
42
|
// Find smallest indent across all non-blank lines.
|
|
39
43
|
const minIndent = Math.min(...lines.filter((l) => l.trim()).map((l) => l.match(/^[ \t]*/)?.[0].length ?? 0));
|
package/src/unit.ts
CHANGED
|
@@ -20,11 +20,15 @@ export type UnitFormat<T = any> = (n: number, precision?: number) => UnitValue<T
|
|
|
20
20
|
const createFormat = (unit: Unit): UnitFormat<string> => {
|
|
21
21
|
return (n: number, precision = unit.precision ?? 0) => {
|
|
22
22
|
const value = n / unit.quotient;
|
|
23
|
+
const formattedValue = value.toLocaleString('en-US', {
|
|
24
|
+
minimumFractionDigits: precision,
|
|
25
|
+
maximumFractionDigits: precision,
|
|
26
|
+
});
|
|
23
27
|
return {
|
|
24
28
|
unit,
|
|
25
29
|
value,
|
|
26
|
-
formattedValue
|
|
27
|
-
toString: () => `${
|
|
30
|
+
formattedValue,
|
|
31
|
+
toString: () => `${formattedValue}${unit.symbol}`,
|
|
28
32
|
};
|
|
29
33
|
};
|
|
30
34
|
};
|
package/src/weak.ts
CHANGED
|
@@ -7,13 +7,20 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export class WeakDictionary<K, V extends object> implements Map<K, V> {
|
|
9
9
|
private readonly _internal = new Map<K, WeakRef<V>>();
|
|
10
|
-
private readonly _finalization = new FinalizationRegistry
|
|
11
|
-
|
|
10
|
+
private readonly _finalization = new FinalizationRegistry<{ key: K; ref: WeakRef<V> }>(({ key, ref }) => {
|
|
11
|
+
if (this._internal.get(key) === ref) {
|
|
12
|
+
this._internal.delete(key);
|
|
13
|
+
}
|
|
12
14
|
});
|
|
13
15
|
|
|
14
16
|
constructor(entries?: [K, V][]) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
if (entries) {
|
|
18
|
+
for (const [key, value] of entries) {
|
|
19
|
+
const ref = new WeakRef(value);
|
|
20
|
+
this._internal.set(key, ref);
|
|
21
|
+
this._register(key, value, ref);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
*entries(): SetIterator<[K, V]> {
|
|
@@ -55,11 +62,48 @@ export class WeakDictionary<K, V extends object> implements Map<K, V> {
|
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
set(key: K, value: V): this {
|
|
58
|
-
this._internal.
|
|
59
|
-
|
|
65
|
+
const previous = this._internal.get(key)?.deref();
|
|
66
|
+
if (previous) {
|
|
67
|
+
this._unregister(previous);
|
|
68
|
+
}
|
|
69
|
+
const ref = new WeakRef(value);
|
|
70
|
+
this._internal.set(key, ref);
|
|
71
|
+
this._register(key, value, ref);
|
|
60
72
|
return this;
|
|
61
73
|
}
|
|
62
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Returns the value for the given key if present, otherwise inserts and returns the default value.
|
|
77
|
+
* @param key - The key to look up or insert.
|
|
78
|
+
* @param defaultValue - The value to insert if the key is not present.
|
|
79
|
+
* @returns The existing or newly inserted value.
|
|
80
|
+
*/
|
|
81
|
+
getOrInsert(key: K, defaultValue: V): V {
|
|
82
|
+
const existing = this.get(key);
|
|
83
|
+
if (existing !== undefined) {
|
|
84
|
+
return existing;
|
|
85
|
+
}
|
|
86
|
+
this.set(key, defaultValue);
|
|
87
|
+
return defaultValue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Returns the value for the given key if present, otherwise computes, inserts, and returns a new value.
|
|
92
|
+
* The callback is only invoked when the key is missing.
|
|
93
|
+
* @param key - The key to look up or insert.
|
|
94
|
+
* @param callbackfn - Function to compute the value if the key is not present.
|
|
95
|
+
* @returns The existing or newly computed value.
|
|
96
|
+
*/
|
|
97
|
+
getOrInsertComputed(key: K, callbackfn: (key: K) => V): V {
|
|
98
|
+
const existing = this.get(key);
|
|
99
|
+
if (existing !== undefined) {
|
|
100
|
+
return existing;
|
|
101
|
+
}
|
|
102
|
+
const value = callbackfn(key);
|
|
103
|
+
this.set(key, value);
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
|
|
63
107
|
has(key: K): boolean {
|
|
64
108
|
return this._internal.has(key) && this._internal.get(key)!.deref() !== undefined;
|
|
65
109
|
}
|
|
@@ -96,14 +140,8 @@ export class WeakDictionary<K, V extends object> implements Map<K, V> {
|
|
|
96
140
|
});
|
|
97
141
|
}
|
|
98
142
|
|
|
99
|
-
private _register(key: K, value: V): void {
|
|
100
|
-
this._finalization.register(
|
|
101
|
-
value,
|
|
102
|
-
() => {
|
|
103
|
-
this._internal.delete(key);
|
|
104
|
-
},
|
|
105
|
-
value,
|
|
106
|
-
);
|
|
143
|
+
private _register(key: K, value: V, ref: WeakRef<V>): void {
|
|
144
|
+
this._finalization.register(value, { key, ref }, value);
|
|
107
145
|
}
|
|
108
146
|
|
|
109
147
|
private _unregister(value: V): void {
|