@radically-straightforward/utilities 0.0.1 → 0.0.2

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 +1,9 @@
1
1
  # Changelog
2
+
3
+ ## 0.0.2 · 2024-01-05
4
+
5
+ - **Breaking Change:** Modified `intern()` to be shallow, which may be easier to reason about and faster.
6
+
7
+ ## 0.0.1 · 2024-01-04
8
+
9
+ - Preliminary release with `intern()`.
package/README.md CHANGED
@@ -19,7 +19,13 @@ import * as node from "@radically-straightforward/utilities";
19
19
  ### `intern()`
20
20
 
21
21
  ```typescript
22
- export function intern<T extends WeakKey>(value: T): T;
22
+ export function intern<
23
+ T extends
24
+ | Array<unknown>
25
+ | {
26
+ [key: string]: unknown;
27
+ },
28
+ >(value: T): T;
23
29
  ```
24
30
 
25
31
  [Interning](<https://en.wikipedia.org/wiki/Interning_(computer_science)>) a value makes it unique across the program, which is useful for checking equality with `===` (reference equality), using it as a key in a `Map`, adding it to a `Set`, and so forth:
@@ -65,23 +71,21 @@ $([1]) === $([1]); // => true
65
71
 
66
72
  > **Note:** We recommend that you alias `intern as $` when importing it to make your code less noisy.
67
73
 
68
- > **Note:** The default notion of equality used to intern values is [Lodash’s `isEqual()`](https://lodash.com/docs/4.17.15#isEqual). You may change that by overriding `intern.isEqual: (value: any, other: any) => boolean` before using `intern()` for the first time.
69
- >
70
- > In particular, note that `intern()` uses a notion of equality that is deep: it compares, for example, objects within objects by value. This is more ergonomic, because it means that you only have to call `intern()` on the outer object, for example, `$({ a: { b: 2 } })` instead of `$({ a: $({ b: 2 }) })`. But this is slower.
71
- >
72
- > You may replace the notion of equality with shallow equality and use the `$({ a: $({ b: 2 }) })` pattern to speed things up. That is, for example, [what React does](https://legacy.reactjs.org/docs/react-api.html#reactpurecomponent). It’s also what the [**JavaScript Records & Tuples Proposal**](https://github.com/tc39/proposal-record-tuple) includes as of January 2024, so it may make your code easier to adapt in the future.
74
+ > **Node:** Inner values must be either primitives or interned values themselves, for example, `$([1, $({})])` is valid, but `$([1, {}])` is not.
73
75
 
74
- > **Note:** You must not mutate an interned value. Interned values are deeply [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) with [`deep-freeze-es6`](https://npm.im/deep-freeze-es6) to prevent you from mutating them.
76
+ > **Node:** Currently only arrays (tuples) and objects (records) may be interned. In the future we may support more types, for example, `Map`, `Set`, regular expressions, and so forth.
77
+
78
+ > **Note:** You must not mutate an interned value. Interned values are [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) to prevent mutation.
75
79
 
76
80
  > **Note:** Interning a value is a costly operation which grows more expensive as you intern more values. Only intern values when really necessary.
77
81
 
78
- > **Note:** The pool of interned values is available as `intern.pool: Map<Symbol, WeakRef<any>>`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up values that have been garbage collected.
82
+ > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
79
83
 
80
84
  **Related Work**
81
85
 
82
86
  **[JavaScript Records & Tuples Proposal](https://github.com/tc39/proposal-record-tuple)**
83
87
 
84
- A proposal to include immutable objects (Records) and immutable arrays (Tuples) in JavaScript. This subsumes most of the need for `intern()` even though it doesn’t cover `Map`s, `Set`s, regular expressions, and so forth.
88
+ A proposal to include immutable objects (Records) and immutable arrays (Tuples) in JavaScript. This subsumes most of the need for `intern()`.
85
89
 
86
90
  It includes a [polyfill](https://github.com/bloomberg/record-tuple-polyfill) which works very similarly to `intern()` but requires different functions for different data types.
87
91
 
@@ -123,10 +127,4 @@ Similar to `collections-deep-equal` but either incomplete, or lacking type defin
123
127
  - <https://twitter.com/swannodette/status/1067962983924539392>
124
128
  - <https://gist.github.com/modernserf/c000e62d40f678cf395e3f360b9b0e43>
125
129
 
126
- **Implementation Notes**
127
-
128
- - Besides [Lodash’s `isEqual()`](https://lodash.com/docs/4.17.15#isEqual) we also considered defaulting to [Node.js’s notion of deep equality](https://nodejs.org/dist/latest-v21.x/docs/api/util.html#utilisdeepstrictequalval1-val2) with the [`deep-equal`](https://npm.im/package/deep-equal) polyfill for the browser.
129
-
130
- - Besides [`deep-freeze-es6`](https://npm.im/deep-freeze-es6) we also considered doing the deep freezing with [`deep-freeze-strict`](https://npm.im/deep-freeze-strict), [`deep-freeze-node`](https://npm.im/deep-freeze-node), and [`deep-freeze`](https://npm.im/deep-freeze).
131
-
132
130
  <!-- DOCUMENTATION END: ./source/index.mts -->
package/build/index.d.mts CHANGED
@@ -42,23 +42,21 @@
42
42
  *
43
43
  * > **Note:** We recommend that you alias `intern as $` when importing it to make your code less noisy.
44
44
  *
45
- * > **Note:** The default notion of equality used to intern values is [Lodash’s `isEqual()`](https://lodash.com/docs/4.17.15#isEqual). You may change that by overriding `intern.isEqual: (value: any, other: any) => boolean` before using `intern()` for the first time.
46
- * >
47
- * > In particular, note that `intern()` uses a notion of equality that is deep: it compares, for example, objects within objects by value. This is more ergonomic, because it means that you only have to call `intern()` on the outer object, for example, `$({ a: { b: 2 } })` instead of `$({ a: $({ b: 2 }) })`. But this is slower.
48
- * >
49
- * > You may replace the notion of equality with shallow equality and use the `$({ a: $({ b: 2 }) })` pattern to speed things up. That is, for example, [what React does](https://legacy.reactjs.org/docs/react-api.html#reactpurecomponent). It’s also what the [**JavaScript Records & Tuples Proposal**](https://github.com/tc39/proposal-record-tuple) includes as of January 2024, so it may make your code easier to adapt in the future.
45
+ * > **Node:** Inner values must be either primitives or interned values themselves, for example, `$([1, $({})])` is valid, but `$([1, {}])` is not.
50
46
  *
51
- * > **Note:** You must not mutate an interned value. Interned values are deeply [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) with [`deep-freeze-es6`](https://npm.im/deep-freeze-es6) to prevent you from mutating them.
47
+ * > **Node:** Currently only arrays (tuples) and objects (records) may be interned. In the future we may support more types, for example, `Map`, `Set`, regular expressions, and so forth.
48
+ *
49
+ * > **Note:** You must not mutate an interned value. Interned values are [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) to prevent mutation.
52
50
  *
53
51
  * > **Note:** Interning a value is a costly operation which grows more expensive as you intern more values. Only intern values when really necessary.
54
52
  *
55
- * > **Note:** The pool of interned values is available as `intern.pool: Map<Symbol, WeakRef<any>>`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up values that have been garbage collected.
53
+ * > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
56
54
  *
57
55
  * **Related Work**
58
56
  *
59
57
  * **[JavaScript Records & Tuples Proposal](https://github.com/tc39/proposal-record-tuple)**
60
58
  *
61
- * A proposal to include immutable objects (Records) and immutable arrays (Tuples) in JavaScript. This subsumes most of the need for `intern()` even though it doesn’t cover `Map`s, `Set`s, regular expressions, and so forth.
59
+ * A proposal to include immutable objects (Records) and immutable arrays (Tuples) in JavaScript. This subsumes most of the need for `intern()`.
62
60
  *
63
61
  * It includes a [polyfill](https://github.com/bloomberg/record-tuple-polyfill) which works very similarly to `intern()` but requires different functions for different data types.
64
62
  *
@@ -99,17 +97,19 @@
99
97
  * - <https://medium.com/@modernserf/the-tyranny-of-triple-equals-de46cc0c5723>
100
98
  * - <https://twitter.com/swannodette/status/1067962983924539392>
101
99
  * - <https://gist.github.com/modernserf/c000e62d40f678cf395e3f360b9b0e43>
102
- *
103
- * **Implementation Notes**
104
- *
105
- * - Besides [Lodash’s `isEqual()`](https://lodash.com/docs/4.17.15#isEqual) we also considered defaulting to [Node.js’s notion of deep equality](https://nodejs.org/dist/latest-v21.x/docs/api/util.html#utilisdeepstrictequalval1-val2) with the [`deep-equal`](https://npm.im/package/deep-equal) polyfill for the browser.
106
- *
107
- * - Besides [`deep-freeze-es6`](https://npm.im/deep-freeze-es6) we also considered doing the deep freezing with [`deep-freeze-strict`](https://npm.im/deep-freeze-strict), [`deep-freeze-node`](https://npm.im/deep-freeze-node), and [`deep-freeze`](https://npm.im/deep-freeze).
108
100
  */
109
- export declare function intern<T extends WeakKey>(value: T): T;
101
+ export declare function intern<T extends Array<unknown> | {
102
+ [key: string]: unknown;
103
+ }>(value: T): T;
110
104
  export declare namespace intern {
111
- var isEqual: (value: any, other: any) => boolean;
112
- var pool: Map<Symbol, WeakRef<any>>;
113
- var finalizationRegistry: FinalizationRegistry<Symbol>;
105
+ var interned: symbol;
106
+ var pools: {
107
+ tuple: Map<Symbol, WeakRef<any>>;
108
+ record: Map<Symbol, WeakRef<any>>;
109
+ };
110
+ var finalizationRegistry: FinalizationRegistry<{
111
+ type: "tuple" | "record";
112
+ key: Symbol;
113
+ }>;
114
114
  }
115
115
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2GG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAUrD;yBAVe,MAAM"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,KAAK,CAAC,OAAO,CAAC,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,EAC1E,KAAK,EAAE,CAAC,GACP,CAAC,CA2CH;yBA7Ce,MAAM"}
package/build/index.mjs CHANGED
@@ -1,5 +1,3 @@
1
- import lodash from "lodash";
2
- import deepFreeze from "deep-freeze-es6";
3
1
  /**
4
2
  * [Interning](<https://en.wikipedia.org/wiki/Interning_(computer_science)>) a value makes it unique across the program, which is useful for checking equality with `===` (reference equality), using it as a key in a `Map`, adding it to a `Set`, and so forth:
5
3
  *
@@ -44,23 +42,21 @@ import deepFreeze from "deep-freeze-es6";
44
42
  *
45
43
  * > **Note:** We recommend that you alias `intern as $` when importing it to make your code less noisy.
46
44
  *
47
- * > **Note:** The default notion of equality used to intern values is [Lodash’s `isEqual()`](https://lodash.com/docs/4.17.15#isEqual). You may change that by overriding `intern.isEqual: (value: any, other: any) => boolean` before using `intern()` for the first time.
48
- * >
49
- * > In particular, note that `intern()` uses a notion of equality that is deep: it compares, for example, objects within objects by value. This is more ergonomic, because it means that you only have to call `intern()` on the outer object, for example, `$({ a: { b: 2 } })` instead of `$({ a: $({ b: 2 }) })`. But this is slower.
50
- * >
51
- * > You may replace the notion of equality with shallow equality and use the `$({ a: $({ b: 2 }) })` pattern to speed things up. That is, for example, [what React does](https://legacy.reactjs.org/docs/react-api.html#reactpurecomponent). It’s also what the [**JavaScript Records & Tuples Proposal**](https://github.com/tc39/proposal-record-tuple) includes as of January 2024, so it may make your code easier to adapt in the future.
45
+ * > **Node:** Inner values must be either primitives or interned values themselves, for example, `$([1, $({})])` is valid, but `$([1, {}])` is not.
52
46
  *
53
- * > **Note:** You must not mutate an interned value. Interned values are deeply [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) with [`deep-freeze-es6`](https://npm.im/deep-freeze-es6) to prevent you from mutating them.
47
+ * > **Node:** Currently only arrays (tuples) and objects (records) may be interned. In the future we may support more types, for example, `Map`, `Set`, regular expressions, and so forth.
48
+ *
49
+ * > **Note:** You must not mutate an interned value. Interned values are [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) to prevent mutation.
54
50
  *
55
51
  * > **Note:** Interning a value is a costly operation which grows more expensive as you intern more values. Only intern values when really necessary.
56
52
  *
57
- * > **Note:** The pool of interned values is available as `intern.pool: Map<Symbol, WeakRef<any>>`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up values that have been garbage collected.
53
+ * > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
58
54
  *
59
55
  * **Related Work**
60
56
  *
61
57
  * **[JavaScript Records & Tuples Proposal](https://github.com/tc39/proposal-record-tuple)**
62
58
  *
63
- * A proposal to include immutable objects (Records) and immutable arrays (Tuples) in JavaScript. This subsumes most of the need for `intern()` even though it doesn’t cover `Map`s, `Set`s, regular expressions, and so forth.
59
+ * A proposal to include immutable objects (Records) and immutable arrays (Tuples) in JavaScript. This subsumes most of the need for `intern()`.
64
60
  *
65
61
  * It includes a [polyfill](https://github.com/bloomberg/record-tuple-polyfill) which works very similarly to `intern()` but requires different functions for different data types.
66
62
  *
@@ -101,29 +97,50 @@ import deepFreeze from "deep-freeze-es6";
101
97
  * - <https://medium.com/@modernserf/the-tyranny-of-triple-equals-de46cc0c5723>
102
98
  * - <https://twitter.com/swannodette/status/1067962983924539392>
103
99
  * - <https://gist.github.com/modernserf/c000e62d40f678cf395e3f360b9b0e43>
104
- *
105
- * **Implementation Notes**
106
- *
107
- * - Besides [Lodash’s `isEqual()`](https://lodash.com/docs/4.17.15#isEqual) we also considered defaulting to [Node.js’s notion of deep equality](https://nodejs.org/dist/latest-v21.x/docs/api/util.html#utilisdeepstrictequalval1-val2) with the [`deep-equal`](https://npm.im/package/deep-equal) polyfill for the browser.
108
- *
109
- * - Besides [`deep-freeze-es6`](https://npm.im/deep-freeze-es6) we also considered doing the deep freezing with [`deep-freeze-strict`](https://npm.im/deep-freeze-strict), [`deep-freeze-node`](https://npm.im/deep-freeze-node), and [`deep-freeze`](https://npm.im/deep-freeze).
110
100
  */
111
101
  export function intern(value) {
112
- for (const internWeakRef of intern.pool.values()) {
102
+ const type = Array.isArray(value)
103
+ ? "tuple"
104
+ : typeof value === "object" && value !== null
105
+ ? "record"
106
+ : (() => {
107
+ throw new Error(`Failed to intern value.`);
108
+ })();
109
+ const keys = Object.keys(value);
110
+ for (const internWeakRef of intern.pools[type].values()) {
113
111
  const internValue = internWeakRef.deref();
114
- if (intern.isEqual(value, internValue))
112
+ if (internValue === undefined ||
113
+ keys.length !== Object.keys(internValue).length)
114
+ continue;
115
+ if (keys.every((key) => value[key] === internValue[key]))
115
116
  return internValue;
116
117
  }
118
+ for (const innerValue of Object.values(value))
119
+ if (!([
120
+ "string",
121
+ "number",
122
+ "bigint",
123
+ "boolean",
124
+ "symbol",
125
+ "undefined",
126
+ ].includes(typeof innerValue) ||
127
+ innerValue === null ||
128
+ innerValue[intern.interned] === true))
129
+ throw new Error(`Failed to intern value because of non-interned inner value.`);
117
130
  const key = Symbol();
118
- deepFreeze(value);
119
- intern.pool.set(key, new WeakRef(value));
120
- intern.finalizationRegistry.register(value, key);
131
+ value[intern.interned] = true;
132
+ Object.freeze(value);
133
+ intern.pools[type].set(key, new WeakRef(value));
134
+ intern.finalizationRegistry.register(value, { type, key });
121
135
  return value;
122
136
  }
123
- intern.isEqual = lodash.isEqual;
124
- intern.pool = new Map();
125
- intern.finalizationRegistry = new FinalizationRegistry((key) => {
126
- intern.pool.delete(key);
137
+ intern.interned = Symbol("interned");
138
+ intern.pools = {
139
+ tuple: new Map(),
140
+ record: new Map(),
141
+ };
142
+ intern.finalizationRegistry = new FinalizationRegistry(({ type, key }) => {
143
+ intern.pools[type].delete(key);
127
144
  });
128
145
  /*
129
146
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,UAAU,MAAM,iBAAiB,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2GG;AACH,MAAM,UAAU,MAAM,CAAoB,KAAQ;IAChD,KAAK,MAAM,aAAa,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACjD,MAAM,WAAW,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC;QAC1C,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC;YAAE,OAAO,WAAW,CAAC;IAC7D,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,UAAU,CAAC,KAAK,CAAC,CAAC;IAClB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,MAAM,CAAC,oBAAoB,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;AAEhC,MAAM,CAAC,IAAI,GAAG,IAAI,GAAG,EAAwB,CAAC;AAE9C,MAAM,CAAC,oBAAoB,GAAG,IAAI,oBAAoB,CAAS,CAAC,GAAG,EAAE,EAAE;IACrE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;EAkBE;AACF,MAAM;AACN,+CAA+C;AAC/C,KAAK;AACL,+KAA+K;AAC/K,uBAAuB;AACvB,iDAAiD;AACjD,8FAA8F;AAC9F,oJAAoJ;AACpJ,KAAK;AACL,8DAA8D;AAC9D,KAAK;AACL,sFAAsF;AACtF,KAAK;AACL,yFAAyF;AACzF,KAAK;AACL,oEAAoE;AACpE,KAAK;AACL,ySAAyS;AACzS,KAAK;AACL,2HAA2H;AAC3H,KAAK;AACL,iBAAiB;AACjB,KAAK;AACL,mBAAmB;AACnB,8DAA8D;AAC9D,KAAK;AACL,8EAA8E;AAC9E,kEAAkE;AAClE,SAAS;AACT,mCAAmC;AACnC,4BAA4B;AAC5B,SAAS;AACT,kBAAkB;AAClB,yFAAyF;AACzF,QAAQ;AACR,mCAAmC;AACnC,2BAA2B;AAC3B,SAAS;AACT,MAAM;AACN,iCAAiC;AACjC,MAAM;AACN,gBAAgB;AAChB,8BAA8B;AAC9B,wDAAwD;AACxD,2CAA2C;AAC3C,6CAA6C;AAC7C,+BAA+B;AAC/B,iDAAiD;AACjD,mBAAmB;AACnB,+BAA+B;AAC/B,2BAA2B;AAC3B,qBAAqB;AACrB,uBAAuB;AACvB,oEAAoE;AACpE,uBAAuB;AACvB,gDAAgD;AAChD,YAAY;AACZ,4BAA4B;AAC5B,iDAAiD;AACjD,QAAQ;AACR,UAAU;AACV,aAAa;AACb,mBAAmB;AACnB,iCAAiC;AACjC,SAAS;AACT,oBAAoB;AACpB,gCAAgC;AAChC,iCAAiC;AACjC,SAAS;AACT,OAAO;AACP,IAAI"}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ test("intern()", () => {
5
5
  // @ts-ignore
6
6
  assert(([1] === [1]) === false);
7
7
  assert($([1]) === $([1]));
8
+ assert($([1]) !== $([2]));
8
9
  {
9
10
  const map = new Map();
10
11
  map.set([1], 1);
@@ -33,7 +34,10 @@ test("intern()", () => {
33
34
  assert.equal(set.size, 1);
34
35
  assert(set.has($([1])));
35
36
  }
36
- assert.notEqual($([1]), $([2]));
37
+ assert.throws(() => {
38
+ $([1, {}]);
39
+ });
40
+ assert($([1, $({})]) === $([1, $({})]));
37
41
  assert.throws(() => {
38
42
  $([1])[0] = 2;
39
43
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.mjs","sourceRoot":"","sources":["../source/index.test.mts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,aAAa,CAAC;AAE1C,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE;IACpB,aAAa;IACb,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;IAChC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1B,CAAC;QACC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACxC,CAAC;IAED,CAAC;QACC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,CAAC;QACC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACb,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,CAAC;QACC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE;QACjB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ;AACR,uBAAuB;AACvB,MAAM;AACN,+BAA+B;AAC/B,YAAY;AACZ,8EAA8E;AAC9E,YAAY;AACZ,eAAe;AACf,OAAO;AACP,kBAAkB;AAClB,+EAA+E;AAC/E,mEAAmE;AACnE,UAAU;AACV,oCAAoC;AACpC,6BAA6B;AAC7B,UAAU;AACV,mBAAmB;AACnB,0FAA0F;AAC1F,SAAS;AACT,oCAAoC;AACpC,4BAA4B;AAC5B,OAAO;AACP,KAAK"}
1
+ {"version":3,"file":"index.test.mjs","sourceRoot":"","sources":["../source/index.test.mts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radically-straightforward/utilities",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "🛠️ Utilities for Node.js and the browser",
5
5
  "keywords": [
6
6
  "node",
@@ -27,14 +27,9 @@
27
27
  "prepare": "tsc && documentation",
28
28
  "test": "npm run prepare && node --test && prettier --check \"./README.md\" --check \"./package.json\" --check \"./tsconfig.json\" --check \"./source/**/*.mts\""
29
29
  },
30
- "dependencies": {
31
- "deep-freeze-es6": "^3.0.2",
32
- "lodash": "^4.17.21"
33
- },
34
30
  "devDependencies": {
35
31
  "@radically-straightforward/documentation": "^1.0.1",
36
32
  "@radically-straightforward/tsconfig": "^1.0.0",
37
- "@types/lodash": "^4.14.202",
38
33
  "@types/node": "^20.10.6",
39
34
  "prettier": "^3.1.1",
40
35
  "typescript": "^5.3.3"
package/source/index.mts CHANGED
@@ -1,6 +1,3 @@
1
- import lodash from "lodash";
2
- import deepFreeze from "deep-freeze-es6";
3
-
4
1
  /**
5
2
  * [Interning](<https://en.wikipedia.org/wiki/Interning_(computer_science)>) a value makes it unique across the program, which is useful for checking equality with `===` (reference equality), using it as a key in a `Map`, adding it to a `Set`, and so forth:
6
3
  *
@@ -45,23 +42,21 @@ import deepFreeze from "deep-freeze-es6";
45
42
  *
46
43
  * > **Note:** We recommend that you alias `intern as $` when importing it to make your code less noisy.
47
44
  *
48
- * > **Note:** The default notion of equality used to intern values is [Lodash’s `isEqual()`](https://lodash.com/docs/4.17.15#isEqual). You may change that by overriding `intern.isEqual: (value: any, other: any) => boolean` before using `intern()` for the first time.
49
- * >
50
- * > In particular, note that `intern()` uses a notion of equality that is deep: it compares, for example, objects within objects by value. This is more ergonomic, because it means that you only have to call `intern()` on the outer object, for example, `$({ a: { b: 2 } })` instead of `$({ a: $({ b: 2 }) })`. But this is slower.
51
- * >
52
- * > You may replace the notion of equality with shallow equality and use the `$({ a: $({ b: 2 }) })` pattern to speed things up. That is, for example, [what React does](https://legacy.reactjs.org/docs/react-api.html#reactpurecomponent). It’s also what the [**JavaScript Records & Tuples Proposal**](https://github.com/tc39/proposal-record-tuple) includes as of January 2024, so it may make your code easier to adapt in the future.
45
+ * > **Node:** Inner values must be either primitives or interned values themselves, for example, `$([1, $({})])` is valid, but `$([1, {}])` is not.
46
+ *
47
+ * > **Node:** Currently only arrays (tuples) and objects (records) may be interned. In the future we may support more types, for example, `Map`, `Set`, regular expressions, and so forth.
53
48
  *
54
- * > **Note:** You must not mutate an interned value. Interned values are deeply [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) with [`deep-freeze-es6`](https://npm.im/deep-freeze-es6) to prevent you from mutating them.
49
+ * > **Note:** You must not mutate an interned value. Interned values are [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) to prevent mutation.
55
50
  *
56
51
  * > **Note:** Interning a value is a costly operation which grows more expensive as you intern more values. Only intern values when really necessary.
57
52
  *
58
- * > **Note:** The pool of interned values is available as `intern.pool: Map<Symbol, WeakRef<any>>`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up values that have been garbage collected.
53
+ * > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
59
54
  *
60
55
  * **Related Work**
61
56
  *
62
57
  * **[JavaScript Records & Tuples Proposal](https://github.com/tc39/proposal-record-tuple)**
63
58
  *
64
- * A proposal to include immutable objects (Records) and immutable arrays (Tuples) in JavaScript. This subsumes most of the need for `intern()` even though it doesn’t cover `Map`s, `Set`s, regular expressions, and so forth.
59
+ * A proposal to include immutable objects (Records) and immutable arrays (Tuples) in JavaScript. This subsumes most of the need for `intern()`.
65
60
  *
66
61
  * It includes a [polyfill](https://github.com/bloomberg/record-tuple-polyfill) which works very similarly to `intern()` but requires different functions for different data types.
67
62
  *
@@ -102,31 +97,66 @@ import deepFreeze from "deep-freeze-es6";
102
97
  * - <https://medium.com/@modernserf/the-tyranny-of-triple-equals-de46cc0c5723>
103
98
  * - <https://twitter.com/swannodette/status/1067962983924539392>
104
99
  * - <https://gist.github.com/modernserf/c000e62d40f678cf395e3f360b9b0e43>
105
- *
106
- * **Implementation Notes**
107
- *
108
- * - Besides [Lodash’s `isEqual()`](https://lodash.com/docs/4.17.15#isEqual) we also considered defaulting to [Node.js’s notion of deep equality](https://nodejs.org/dist/latest-v21.x/docs/api/util.html#utilisdeepstrictequalval1-val2) with the [`deep-equal`](https://npm.im/package/deep-equal) polyfill for the browser.
109
- *
110
- * - Besides [`deep-freeze-es6`](https://npm.im/deep-freeze-es6) we also considered doing the deep freezing with [`deep-freeze-strict`](https://npm.im/deep-freeze-strict), [`deep-freeze-node`](https://npm.im/deep-freeze-node), and [`deep-freeze`](https://npm.im/deep-freeze).
111
100
  */
112
- export function intern<T extends WeakKey>(value: T): T {
113
- for (const internWeakRef of intern.pool.values()) {
101
+ export function intern<T extends Array<unknown> | { [key: string]: unknown }>(
102
+ value: T,
103
+ ): T {
104
+ const type = Array.isArray(value)
105
+ ? "tuple"
106
+ : typeof value === "object" && value !== null
107
+ ? "record"
108
+ : (() => {
109
+ throw new Error(`Failed to intern value.`);
110
+ })();
111
+ const keys = Object.keys(value);
112
+ for (const internWeakRef of intern.pools[type].values()) {
114
113
  const internValue = internWeakRef.deref();
115
- if (intern.isEqual(value, internValue)) return internValue;
114
+ if (
115
+ internValue === undefined ||
116
+ keys.length !== Object.keys(internValue).length
117
+ )
118
+ continue;
119
+ if (keys.every((key) => (value as any)[key] === internValue[key]))
120
+ return internValue;
116
121
  }
122
+ for (const innerValue of Object.values(value))
123
+ if (
124
+ !(
125
+ [
126
+ "string",
127
+ "number",
128
+ "bigint",
129
+ "boolean",
130
+ "symbol",
131
+ "undefined",
132
+ ].includes(typeof innerValue) ||
133
+ innerValue === null ||
134
+ (innerValue as any)[intern.interned] === true
135
+ )
136
+ )
137
+ throw new Error(
138
+ `Failed to intern value because of non-interned inner value.`,
139
+ );
117
140
  const key = Symbol();
118
- deepFreeze(value);
119
- intern.pool.set(key, new WeakRef(value));
120
- intern.finalizationRegistry.register(value, key);
141
+ (value as any)[intern.interned] = true;
142
+ Object.freeze(value);
143
+ intern.pools[type].set(key, new WeakRef(value));
144
+ intern.finalizationRegistry.register(value, { type, key });
121
145
  return value;
122
146
  }
123
147
 
124
- intern.isEqual = lodash.isEqual;
148
+ intern.interned = Symbol("interned");
125
149
 
126
- intern.pool = new Map<Symbol, WeakRef<any>>();
150
+ intern.pools = {
151
+ tuple: new Map<Symbol, WeakRef<any>>(),
152
+ record: new Map<Symbol, WeakRef<any>>(),
153
+ };
127
154
 
128
- intern.finalizationRegistry = new FinalizationRegistry<Symbol>((key) => {
129
- intern.pool.delete(key);
155
+ intern.finalizationRegistry = new FinalizationRegistry<{
156
+ type: "tuple" | "record";
157
+ key: Symbol;
158
+ }>(({ type, key }) => {
159
+ intern.pools[type].delete(key);
130
160
  });
131
161
 
132
162
  /*
@@ -7,6 +7,7 @@ test("intern()", () => {
7
7
  // @ts-ignore
8
8
  assert(([1] === [1]) === false);
9
9
  assert($([1]) === $([1]));
10
+ assert($([1]) !== $([2]));
10
11
 
11
12
  {
12
13
  const map = new Map();
@@ -40,7 +41,10 @@ test("intern()", () => {
40
41
  assert(set.has($([1])));
41
42
  }
42
43
 
43
- assert.notEqual($([1]), $([2]));
44
+ assert.throws(() => {
45
+ $([1, {}]);
46
+ });
47
+ assert($([1, $({})]) === $([1, $({})]));
44
48
 
45
49
  assert.throws(() => {
46
50
  $([1])[0] = 2;