@dxos/util 0.4.9 → 0.4.10-main.06ef97a

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"di-key.test.d.ts","sourceRoot":"","sources":["../../../src/di-key.test.ts"],"names":[],"mappings":""}
@@ -1,3 +1,6 @@
1
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
2
+ [key: string]: JsonValue;
3
+ };
1
4
  /**
2
5
  * JSON.stringify replacer.
3
6
  */
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../../src/json.ts"],"names":[],"mappings":"AAaA;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG,CAqBpE;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,UAAW,GAAG,oDAAmD,GA+BpF,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,eAAe,aAChB,cAAc,WAClB,MAAM,SAAS,GAAG,QAUvB,CAAC"}
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../../src/json.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAItG;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG,CAqBpE;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,UAAW,GAAG,oDAAmD,GA+BpF,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,eAAe,aAChB,cAAc,WAClB,MAAM,SAAS,GAAG,QAUvB,CAAC"}
@@ -1,5 +1,10 @@
1
+ interface IMap<K, V> {
2
+ get(key: K): V | undefined;
3
+ set(key: K, value: V): this;
4
+ }
1
5
  /**
2
6
  * Get or set map value.
3
7
  */
4
- export declare const defaultMap: <K, V>(map: Map<K, V>, key: K, def: V | (() => V)) => V;
8
+ export declare const defaultMap: <K, V>(map: IMap<K, V>, key: K, def: V | (() => V)) => V;
9
+ export {};
5
10
  //# sourceMappingURL=map.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../../src/map.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,eAAO,MAAM,UAAU,yDAQtB,CAAC"}
1
+ {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../../src/map.ts"],"names":[],"mappings":"AAIA,UAAU,IAAI,CAAC,CAAC,EAAE,CAAC;IACjB,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC3B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CAC7B;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,0DAQtB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/util",
3
- "version": "0.4.9",
3
+ "version": "0.4.10-main.06ef97a",
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",
@@ -16,13 +16,13 @@
16
16
  "src"
17
17
  ],
18
18
  "dependencies": {
19
- "@dxos/debug": "0.4.9",
20
- "@dxos/invariant": "0.4.9",
21
- "@dxos/keys": "0.4.9",
22
- "@dxos/node-std": "0.4.9"
19
+ "@dxos/debug": "0.4.10-main.06ef97a",
20
+ "@dxos/keys": "0.4.10-main.06ef97a",
21
+ "@dxos/node-std": "0.4.10-main.06ef97a",
22
+ "@dxos/invariant": "0.4.10-main.06ef97a"
23
23
  },
24
24
  "devDependencies": {
25
- "@dxos/crypto": "0.4.9"
25
+ "@dxos/crypto": "0.4.10-main.06ef97a"
26
26
  },
27
27
  "publishConfig": {
28
28
  "access": "public"
@@ -0,0 +1,32 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { test } from 'vitest';
6
+
7
+ import { DiContainer } from './di-container';
8
+ import { DiKey } from './di-key';
9
+
10
+ test('DiContainer', ({ expect }) => {
11
+ const A = DiKey.define<string>('A');
12
+ class B {}
13
+
14
+ const generic = DiKey.define('Generic');
15
+ type Generic<T> = { foo: T };
16
+ const Generic = <T>(param: DiKey<T>): DiKey<Generic<T>> => DiKey.combine(generic, param);
17
+
18
+ const Singleton = DiKey.singleton('Singleton', () => 'singleton');
19
+
20
+ const b = new B();
21
+ const container = new DiContainer()
22
+ .provide(B, b)
23
+ .provide(Generic(A), {
24
+ foo: 'Generic A',
25
+ })
26
+ .provide(Generic(B), { foo: b });
27
+
28
+ expect(container.get(B)).toBe(b);
29
+ expect(container.get(Generic(A)).foo).toBe('Generic A');
30
+ expect(container.get(Generic(B)).foo).toBe(b);
31
+ expect(container.get(Singleton)).toBe('singleton');
32
+ });
@@ -0,0 +1,44 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { DiKey } from './di-key';
6
+
7
+ export class DiContainer {
8
+ #entries = new Map<DiKey<any>, any>();
9
+
10
+ get<T>(key: DiKey<T>): T {
11
+ const value = this.#entries.get(key);
12
+ if (value === undefined) {
13
+ const factory = DiKey.getSingletonFactory(key);
14
+ if (factory != null) {
15
+ const singletonValue = factory();
16
+ this.#entries.set(key, singletonValue);
17
+ return singletonValue;
18
+ }
19
+
20
+ throw new Error(`Key ${DiKey.stringify(key)} does not exist in the container`);
21
+ }
22
+
23
+ return value;
24
+ }
25
+
26
+ provide<T>(key: DiKey<T>, value: T): this {
27
+ if (DiKey.getSingletonFactory(key) != null) {
28
+ throw new Error(`Key ${DiKey.stringify(key)} is a singleton key and cannot be provided directly`);
29
+ }
30
+ if (this.#entries.has(key)) {
31
+ throw new Error(`Key ${DiKey.stringify(key)} already exists in the container`);
32
+ }
33
+
34
+ this.#entries.set(key, value);
35
+
36
+ return this;
37
+ }
38
+
39
+ delete<T>(key: DiKey<T>): this {
40
+ this.#entries.delete(key);
41
+
42
+ return this;
43
+ }
44
+ }
@@ -0,0 +1,16 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { test } from 'vitest';
6
+
7
+ import { DiKey } from './di-key';
8
+
9
+ test('DiKey', ({ expect }) => {
10
+ const A = DiKey.define('A');
11
+ class B {}
12
+
13
+ // eslint-disable-next-line no-self-compare
14
+ expect(DiKey.combine(A, B) === DiKey.combine(A, B)).toBeTruthy();
15
+ expect(DiKey.stringify(DiKey.combine(A, B))).toEqual('A<B>');
16
+ });
package/src/di-key.ts ADDED
@@ -0,0 +1,111 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { inspect } from 'node:util';
6
+
7
+ import { defaultMap } from './map';
8
+
9
+ export const symbolSingleton = Symbol('singleton');
10
+
11
+ export type SingletonFactory<T> = () => T;
12
+
13
+ export class SymbolDiKey<T> {
14
+ symbol: symbol;
15
+
16
+ constructor(name: string) {
17
+ this.symbol = Symbol(name);
18
+ }
19
+
20
+ public [symbolSingleton]?: SingletonFactory<T> = undefined;
21
+
22
+ toString() {
23
+ return String(this.symbol).slice(7, -1);
24
+ }
25
+
26
+ [inspect.custom]() {
27
+ return this.toString();
28
+ }
29
+ }
30
+
31
+ export type ConstructorDiKey<T> = {
32
+ new (...args: any[]): T;
33
+
34
+ [symbolSingleton]?: SingletonFactory<T>;
35
+ };
36
+
37
+ /**
38
+ * Represents a key for an entry in a DI container.
39
+ * Class constructors can be used as is.
40
+ * Multiple keys can be combined into a single key to represent parameterized types.
41
+ * Composite keys maintain referential equality.
42
+ * Keys can optionally have a singleton factory attached to them.
43
+ */
44
+ export type DiKey<T> =
45
+ | {
46
+ new (...args: any[]): T;
47
+ }
48
+ | SymbolDiKey<T>;
49
+
50
+ export const DiKey = new (class DiKeyConstructor {
51
+ /**
52
+ * Needed to ensure referential equality of combined keys.
53
+ *
54
+ * This is a multilevel map of maps, where the maps are also used as keys.
55
+ *
56
+ * Good luck :)
57
+ */
58
+ // TODO(dmaretskyi): Disable private members lowering for dev env.
59
+ // TODO(dmaretskyi): Could be a weak map after a NodeJS upgrade.
60
+ #combinedRegistry = new WeakMap();
61
+
62
+ define<T>(name: string): DiKey<T> {
63
+ return new SymbolDiKey(name);
64
+ }
65
+
66
+ singleton<T>(name: string, factory: SingletonFactory<T>): DiKey<T> {
67
+ const id = new SymbolDiKey(name);
68
+ id[symbolSingleton] = factory;
69
+ return id as any;
70
+ }
71
+
72
+ stringify(id: DiKey<any>): string {
73
+ switch (typeof id) {
74
+ case 'symbol':
75
+ return String(id).slice(7, -1);
76
+ case 'function':
77
+ return id.name;
78
+ default:
79
+ return String(id);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Create composite keys to represent parameterized types.
85
+ *
86
+ * @example DiKey.combine(A, B, C) => "A<B, C>"
87
+ *
88
+ * Maintains referential equality: `DiKey.combine(A, B) === DiKey.combine(A, B)`
89
+ */
90
+ combine(...ids: DiKey<any>[]) {
91
+ const map = this.#lookupCombined(this.#combinedRegistry, ids);
92
+ return defaultMap(this.#combinedRegistry, map, () => this.#combinedDescription(ids));
93
+ }
94
+
95
+ getSingletonFactory<T>(id: DiKey<T>): SingletonFactory<T> | undefined {
96
+ return (id as any)[symbolSingleton];
97
+ }
98
+
99
+ #lookupCombined(map: WeakMap<any, any>, [first, ...rest]: DiKey<any>[]): WeakMap<any, any> {
100
+ const value = defaultMap(map, first, () => new Map());
101
+ if (rest.length > 0) {
102
+ return this.#lookupCombined(value, rest);
103
+ } else {
104
+ return value;
105
+ }
106
+ }
107
+
108
+ #combinedDescription([first, ...rest]: DiKey<any>[]) {
109
+ return `${this.stringify(first)}<${rest.map(this.stringify).join(', ')}>`;
110
+ }
111
+ })();
package/src/json.ts CHANGED
@@ -9,6 +9,8 @@ import { PublicKey } from '@dxos/keys';
9
9
  import { humanize } from './human-hash';
10
10
  import { arrayToBuffer } from './uint8array';
11
11
 
12
+ export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
13
+
12
14
  const MAX_DEPTH = 5;
13
15
 
14
16
  /**
package/src/map.ts CHANGED
@@ -2,10 +2,15 @@
2
2
  // Copyright 2020 DXOS.org
3
3
  //
4
4
 
5
+ interface IMap<K, V> {
6
+ get(key: K): V | undefined;
7
+ set(key: K, value: V): this;
8
+ }
9
+
5
10
  /**
6
11
  * Get or set map value.
7
12
  */
8
- export const defaultMap = <K, V>(map: Map<K, V>, key: K, def: V | (() => V)) => {
13
+ export const defaultMap = <K, V>(map: IMap<K, V>, key: K, def: V | (() => V)) => {
9
14
  let value = map.get(key);
10
15
  if (value === undefined) {
11
16
  value = typeof def === 'function' ? (def as () => V)() : def;