@oazmi/superbuild 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.
Files changed (118) hide show
  1. package/esm/_dnt.shims.d.ts +2 -0
  2. package/esm/_dnt.shims.d.ts.map +1 -0
  3. package/esm/_dnt.shims.js +57 -0
  4. package/esm/deps/jsr.io/@oazmi/esbuild-types/0.28.0/src/mod.d.ts +598 -0
  5. package/esm/deps/jsr.io/@oazmi/esbuild-types/0.28.0/src/mod.d.ts.map +1 -0
  6. package/esm/deps/jsr.io/@oazmi/esbuild-types/0.28.0/src/mod.js +1 -0
  7. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/alias.d.ts +617 -0
  8. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/alias.d.ts.map +1 -0
  9. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/alias.js +671 -0
  10. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/array1d.d.ts +487 -0
  11. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/array1d.d.ts.map +1 -0
  12. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/array1d.js +536 -0
  13. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/array2d.d.ts +266 -0
  14. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/array2d.d.ts.map +1 -0
  15. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/array2d.js +318 -0
  16. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/binder.d.ts +368 -0
  17. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/binder.d.ts.map +1 -0
  18. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/binder.js +380 -0
  19. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/crossenv.d.ts +711 -0
  20. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/crossenv.d.ts.map +1 -0
  21. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/crossenv.js +1173 -0
  22. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/cryptoman.d.ts +171 -0
  23. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/cryptoman.d.ts.map +1 -0
  24. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/cryptoman.js +308 -0
  25. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/deps.d.ts +10 -0
  26. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/deps.d.ts.map +1 -0
  27. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/deps.js +10 -0
  28. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/eightpack.d.ts +168 -0
  29. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/eightpack.d.ts.map +1 -0
  30. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/eightpack.js +236 -0
  31. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/eightpack_varint.d.ts +90 -0
  32. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/eightpack_varint.d.ts.map +1 -0
  33. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/eightpack_varint.js +237 -0
  34. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/numericarray.d.ts +213 -0
  35. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/numericarray.d.ts.map +1 -0
  36. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/numericarray.js +363 -0
  37. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/numericmethods.d.ts +233 -0
  38. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/numericmethods.d.ts.map +1 -0
  39. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/numericmethods.js +256 -0
  40. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/pathman.d.ts +1436 -0
  41. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/pathman.d.ts.map +1 -0
  42. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/pathman.js +1730 -0
  43. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/promiseman.d.ts +647 -0
  44. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/promiseman.d.ts.map +1 -0
  45. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/promiseman.js +762 -0
  46. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/stringman.d.ts +538 -0
  47. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/stringman.d.ts.map +1 -0
  48. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/stringman.js +626 -0
  49. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/struct.d.ts +734 -0
  50. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/struct.d.ts.map +1 -0
  51. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/struct.js +764 -0
  52. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/typedbuffer.d.ts +137 -0
  53. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/typedbuffer.d.ts.map +1 -0
  54. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/typedbuffer.js +234 -0
  55. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/typedefs.d.ts +391 -0
  56. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/typedefs.d.ts.map +1 -0
  57. package/esm/deps/jsr.io/@oazmi/kitchensink/0.10.0/src/typedefs.js +8 -0
  58. package/esm/deps.d.ts +56 -0
  59. package/esm/deps.d.ts.map +1 -0
  60. package/esm/deps.js +37 -0
  61. package/esm/esbuild/metafile.d.ts +90 -0
  62. package/esm/esbuild/metafile.d.ts.map +1 -0
  63. package/esm/esbuild/metafile.js +275 -0
  64. package/esm/esbuild/native.d.ts +95 -0
  65. package/esm/esbuild/native.d.ts.map +1 -0
  66. package/esm/esbuild/native.js +127 -0
  67. package/esm/esbuild/outputfile.d.ts +86 -0
  68. package/esm/esbuild/outputfile.d.ts.map +1 -0
  69. package/esm/esbuild/outputfile.js +221 -0
  70. package/esm/esbuild/strongtypes.d.ts +90 -0
  71. package/esm/esbuild/strongtypes.d.ts.map +1 -0
  72. package/esm/esbuild/strongtypes.js +8 -0
  73. package/esm/esbuild/typedefs.d.ts +121 -0
  74. package/esm/esbuild/typedefs.d.ts.map +1 -0
  75. package/esm/esbuild/typedefs.js +55 -0
  76. package/esm/esbuild/weaktypes.d.ts +46 -0
  77. package/esm/esbuild/weaktypes.d.ts.map +1 -0
  78. package/esm/esbuild/weaktypes.js +8 -0
  79. package/esm/funcdefs.d.ts +89 -0
  80. package/esm/funcdefs.d.ts.map +1 -0
  81. package/esm/funcdefs.js +145 -0
  82. package/esm/mod.d.ts +4 -0
  83. package/esm/mod.d.ts.map +1 -0
  84. package/esm/mod.js +2 -0
  85. package/esm/package.json +3 -0
  86. package/esm/plugins/emissions_driver.d.ts +33 -0
  87. package/esm/plugins/emissions_driver.d.ts.map +1 -0
  88. package/esm/plugins/emissions_driver.js +260 -0
  89. package/esm/plugins/long_build.d.ts +138 -0
  90. package/esm/plugins/long_build.d.ts.map +1 -0
  91. package/esm/plugins/long_build.js +288 -0
  92. package/esm/plugins/native_replica.d.ts +48 -0
  93. package/esm/plugins/native_replica.d.ts.map +1 -0
  94. package/esm/plugins/native_replica.js +122 -0
  95. package/esm/super/build.d.ts +53 -0
  96. package/esm/super/build.d.ts.map +1 -0
  97. package/esm/super/build.js +39 -0
  98. package/esm/super/build_context.d.ts +83 -0
  99. package/esm/super/build_context.d.ts.map +1 -0
  100. package/esm/super/build_context.js +124 -0
  101. package/esm/super/mod.d.ts +12 -0
  102. package/esm/super/mod.d.ts.map +1 -0
  103. package/esm/super/mod.js +9 -0
  104. package/esm/super/plugin.d.ts +23 -0
  105. package/esm/super/plugin.d.ts.map +1 -0
  106. package/esm/super/plugin.js +31 -0
  107. package/esm/super/plugin_build.d.ts +38 -0
  108. package/esm/super/plugin_build.d.ts.map +1 -0
  109. package/esm/super/plugin_build.js +198 -0
  110. package/esm/super/typedefs.d.ts +323 -0
  111. package/esm/super/typedefs.d.ts.map +1 -0
  112. package/esm/super/typedefs.js +10 -0
  113. package/esm/typedefs.d.ts +21 -0
  114. package/esm/typedefs.d.ts.map +1 -0
  115. package/esm/typedefs.js +5 -0
  116. package/license.md +37 -0
  117. package/package.json +88 -0
  118. package/readme.md +181 -0
@@ -0,0 +1,764 @@
1
+ /** utility functions for common object structures and `Object` manipulation.
2
+ *
3
+ * @module
4
+ */
5
+ import { array_isArray, object_defineProperty, object_getOwnPropertyDescriptor, object_getOwnPropertyNames, object_getOwnPropertySymbols, object_getPrototypeOf } from "./alias.js";
6
+ import { resolveRange } from "./array1d.js";
7
+ import { max } from "./numericmethods.js";
8
+ /** get an equivalent rectangle where the `height` and `width` are positive.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { assertEquals } from "jsr:@std/assert"
13
+ *
14
+ * const
15
+ * my_rect: Rect = { x: -20, y: 100, width: 50, height: -30 },
16
+ * my_abs_rect = positiveRect(my_rect)
17
+ *
18
+ * assertEquals(my_abs_rect, {
19
+ * x: -20,
20
+ * y: 70,
21
+ * width: 50,
22
+ * height: 30,
23
+ * })
24
+ * ```
25
+ */
26
+ export const positiveRect = (r) => {
27
+ let { x, y, width, height } = r;
28
+ if (width < 0) {
29
+ width *= -1; // width is now positive
30
+ x -= width; // x has been moved further to the left
31
+ }
32
+ if (height < 0) {
33
+ height *= -1; // height is now positive
34
+ y -= height; // y has been moved further to the top
35
+ }
36
+ return { x, y, width, height };
37
+ };
38
+ /** get the constructor of a class's instance.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { assertEquals } from "jsr:@std/assert"
43
+ *
44
+ * class K { constructor(public value: any) { } }
45
+ *
46
+ * const a = new K(1)
47
+ * const b = new (constructorOf(a))(2) // equivalent to `const b = new K(2)`
48
+ *
49
+ * a satisfies K
50
+ * b satisfies K
51
+ * assertEquals(a !== b, true)
52
+ * ```
53
+ */
54
+ export const constructorOf = (class_instance) => {
55
+ return object_getPrototypeOf(class_instance).constructor;
56
+ };
57
+ /** use the constructor of a class's instance to construct a new instance.
58
+ *
59
+ * this is useful for avoiding pollution of code with `new` keyword along with some wonky placement of braces to make your code work.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * class K {
64
+ * value: number
65
+ *
66
+ * constructor(value1: number, value2: number) {
67
+ * this.value = value1 + value2
68
+ * }
69
+ * }
70
+ *
71
+ * const a = new K(1, 1)
72
+ * const b = constructFrom(a, 2, 2) // equivalent to `const b = new K(2, 2)`
73
+ *
74
+ * // vanilla way of constructing `const c = new K(3, 3)` using `a`
75
+ * const c = new (Object.getPrototypeOf(a).constructor)(3, 3)
76
+ *
77
+ * a satisfies K
78
+ * b satisfies K
79
+ * c satisfies K
80
+ * ```
81
+ */
82
+ export const constructFrom = (class_instance, ...args) => {
83
+ return new (constructorOf(class_instance))(...args);
84
+ };
85
+ /** get the prototype object of a class.
86
+ *
87
+ * this is useful when you want to access bound-methods of an instance of a class, such as the ones declared as:
88
+ *
89
+ * ```ts ignore
90
+ * class X {
91
+ * methodOfProto() { }
92
+ * }
93
+ * ```
94
+ *
95
+ * these bound methods are not available via destructure of an instance, because they then lose their `this` context.
96
+ * the only functions that can be destructured without losing their `this` context are the ones declared via assignment:
97
+ *
98
+ * ```ts ignore
99
+ * class X {
100
+ * fn = () => { }
101
+ * fn2 = function () { }
102
+ * }
103
+ * ```
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * import { assertEquals } from "jsr:@std/assert"
108
+ *
109
+ * const
110
+ * array_proto = prototypeOfClass(Array<number>),
111
+ * arr = [1, 2, 3, 4, 5]
112
+ *
113
+ * array_proto.push.call(arr, 6)
114
+ * assertEquals(arr, [1, 2, 3, 4, 5, 6])
115
+ *
116
+ * const slow_push_to_arr = (...values: number[]) => (arr.push(...values))
117
+ * // the following declaration is more performant than `slow_push_to_arr`,
118
+ * // and it also has lower memory footprint.
119
+ * const fast_push_to_arr = array_proto.push.bind(arr)
120
+ *
121
+ * slow_push_to_arr(7, 8) // sloww & bigg
122
+ * fast_push_to_arr(9, 10) // quicc & smol
123
+ * assertEquals(arr, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
124
+ * ```
125
+ */
126
+ export const prototypeOfClass = (cls) => {
127
+ return cls.prototype;
128
+ };
129
+ /** get the prototype chain of an object, with optional slicing options.
130
+ *
131
+ * @param obj the object whose prototype chain is to be found.
132
+ * @param config optional configuration for slicing the full prototype chain.
133
+ * @returns the sliced prototype chain of the given object.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * import { assertEquals, assertThrows } from "jsr:@std/assert"
138
+ *
139
+ * // aliasing our functions for brevity
140
+ * const
141
+ * fn = prototypeChainOfObject,
142
+ * eq = assertEquals
143
+ *
144
+ * class A extends Array { }
145
+ * class B extends A { }
146
+ * class C extends B { }
147
+ * class D extends C { }
148
+ *
149
+ * const
150
+ * a = new A(0),
151
+ * b = new B(0),
152
+ * c = new C(0),
153
+ * d = new D(0)
154
+ *
155
+ * eq(fn(d), [D.prototype, C.prototype, B.prototype, A.prototype, Array.prototype, Object.prototype, null])
156
+ * eq(fn(b), [B.prototype, A.prototype, Array.prototype, Object.prototype, null])
157
+ *
158
+ * // slicing the prototype chain, starting from index 2 till the end
159
+ * eq(fn(d, { start: 2 }), [B.prototype, A.prototype, Array.prototype, Object.prototype, null])
160
+ *
161
+ * // slicing using a negative index
162
+ * eq(fn(d, { start: -2 }), [Object.prototype, null])
163
+ *
164
+ * // slicing using an object
165
+ * eq(fn(d, { start: B.prototype }), [B.prototype, A.prototype, Array.prototype, Object.prototype, null])
166
+ *
167
+ * // when the slicing object is not found, the start index will be assumed to be `0` (default value)
168
+ * eq(fn(d, { start: Set.prototype }), [D.prototype, C.prototype, B.prototype, A.prototype, Array.prototype, Object.prototype, null])
169
+ *
170
+ * // slicing between the `start` index (inclusive) and the end index
171
+ * eq(fn(d, { start: 2, end: 6 }), [B.prototype, A.prototype, Array.prototype, Object.prototype])
172
+ * eq(fn(d, { start: 2, end: -1 }), [B.prototype, A.prototype, Array.prototype, Object.prototype])
173
+ * eq(fn(d, { start: 2, end: null }), [B.prototype, A.prototype, Array.prototype, Object.prototype])
174
+ *
175
+ * // if the end index is not found, the slicing will occur till the end
176
+ * eq(fn(d, { end: Set.prototype}), [D.prototype, C.prototype, B.prototype, A.prototype, Array.prototype, Object.prototype, null])
177
+ *
178
+ * // slicing using a `delta` argument will let you define how many elements you wish to:
179
+ * // - traverse forward from the `start` index
180
+ * // - traverse backwards from the `end` index
181
+ * eq(fn(d, { start: 2, delta: 3 }), [B.prototype, A.prototype, Array.prototype])
182
+ * eq(fn(d, { end: -2, delta: 2 }), [A.prototype, Array.prototype])
183
+ * eq(fn(d, { end: null, delta: 4 }), [B.prototype, A.prototype, Array.prototype, Object.prototype])
184
+ *
185
+ * eq(fn(d, { start: 1, delta: 1 }), fn(c, { start: 0, delta: 1 }))
186
+ * eq(fn(c, { start: 1, delta: 1 }), fn(b, { start: 0, delta: 1 }))
187
+ * eq(fn(b, { start: 1, delta: 1 }), fn(a, { start: 0, delta: 1 }))
188
+ * eq(fn(a, { start: 1, delta: 1 }), fn([], { start: 0, delta: 1 }))
189
+ * eq(fn([], { start: 1, delta: 1 }), fn({}, { start: 0, delta: 1 }))
190
+ *
191
+ * // you may also traverse through the inheritance chain of a class, but it will be a good idea to set your `end` point to `Function.prototype`,
192
+ * // since all class objects are effectively functions (i.e. their common ancestral prototype if `Function.prototype`).
193
+ * eq(fn(D, { start: 0, end: Function.prototype }), [C, B, A, Array])
194
+ * eq(fn(D), [C, B, A, Array, Function.prototype, Object.prototype, null])
195
+ * eq(fn(class {}), [Function.prototype, Object.prototype, null])
196
+ * eq(fn(class extends Object {}), [Object, Function.prototype, Object.prototype, null])
197
+ * eq(fn(class extends Map {}), [Map, Function.prototype, Object.prototype, null])
198
+ *
199
+ * // you cannot acquire the prototype chain of the `null` object
200
+ * assertThrows(() => { fn(null) })
201
+ * ```
202
+ */
203
+ export function prototypeChainOfObject(obj, config = {}) {
204
+ let { start, end, delta } = config;
205
+ const full_chain = [];
206
+ // collecting the full prototype chain, until `obj` becomes `null`, which is always the last thing in a prototype chain.
207
+ while ((obj = object_getPrototypeOf(obj))) {
208
+ full_chain.push(obj);
209
+ }
210
+ full_chain.push(null);
211
+ const full_chain_length = full_chain.length;
212
+ // NOTE: we not only check if `start` and `end` are of type `"object"`, but also if they're of type `"function"`.
213
+ // this is because `typeof Function.prototype === "function"`, and not `"object"`.
214
+ // and as it happens to be the case, I do use `Function.prototype` as the end point in my {@link subclassThroughComposition} function.
215
+ if (isComplex(start)) {
216
+ start = max(0, full_chain.indexOf(start));
217
+ }
218
+ if (isComplex(end)) {
219
+ const end_index = full_chain.indexOf(end);
220
+ end = end_index < 0 ? undefined : end_index;
221
+ }
222
+ // both `start` and `end` are either numbers or undefined now.
223
+ if ((delta !== undefined) && ((start ?? end) !== undefined)) {
224
+ // in here, `delta` is defined and one of either `start` or `end` is undefined
225
+ if (start !== undefined) {
226
+ end = start + delta;
227
+ }
228
+ else {
229
+ start = end - delta;
230
+ }
231
+ }
232
+ // now that any potential `delta` has been resolved into a `start` and `end`,
233
+ // we only need to resolve possible negative indexes and then slice the prototype chain
234
+ [start, end] = resolveRange(start, end, full_chain_length);
235
+ return full_chain.slice(start, end);
236
+ }
237
+ /** get an object's list of **owned** keys (string keys and symbol keys).
238
+ *
239
+ * > [!note]
240
+ * > **owned** keys of an object are not the same as just _any_ key of the object.
241
+ * > an owned key is one that it **directly** owned by the object, not owned through inheritance, such as the class methods.
242
+ * > more precisely, in javascript, which ever member of an object that we call a _property_, is an **owned** key.
243
+ *
244
+ * if you wish to acquire **all remaining** inherited keys of an object, you will want use {@link getInheritedPropertyKeys}.
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * import { assertEquals } from "jsr:@std/assert"
249
+ *
250
+ * const
251
+ * symbol_h = Symbol("symbol h"),
252
+ * symbol_i = Symbol("symbol i"),
253
+ * symbol_j = Symbol("symbol j")
254
+ *
255
+ * class A {
256
+ * a = { v: 1 }
257
+ * b = { v: 2 }
258
+ * c: { v: number }
259
+ * d() { return { v: 4 } }
260
+ * e() { return { v: 5 } }
261
+ * f = () => { return { v: 6 } }
262
+ * g: () => ({ v: number })
263
+ * [symbol_h]() { return { v: 8 } }
264
+ * [symbol_i] = () => { return { v: 9 } }
265
+ *
266
+ * constructor() {
267
+ * this.c = { v: 3 }
268
+ * this.g = () => { return { v: 7 } }
269
+ * }
270
+ * }
271
+ *
272
+ * class B extends A {
273
+ * override a = { v: 11 }
274
+ * override e() { return { v: 15 } }
275
+ * //@ts-ignore: typescript does not permit defining a method over the name of an existing property
276
+ * override g() { return { v: 17 } }
277
+ * [symbol_j] = () => { return { v: 20 } }
278
+ *
279
+ * constructor() { super() }
280
+ * }
281
+ *
282
+ * const
283
+ * a = new A(),
284
+ * b = new B()
285
+ *
286
+ * assertEquals(b.a, { v: 11 })
287
+ * assertEquals(b.b, { v: 2 })
288
+ * assertEquals(b.c, { v: 3 })
289
+ * assertEquals(b.d(), { v: 4 })
290
+ * assertEquals(b.e(), { v: 15 })
291
+ * assertEquals(b.f(), { v: 6 })
292
+ * assertEquals(b.g(), { v: 7 }) // notice that the overridden method is not called, and the property is called instead
293
+ * assertEquals(Object.getPrototypeOf(b).g(), { v: 17 })
294
+ * assertEquals(b[symbol_h](), { v: 8 })
295
+ * assertEquals(b[symbol_i](), { v: 9 })
296
+ * assertEquals(b[symbol_j](), { v: 20 })
297
+ *
298
+ * assertEquals(
299
+ * new Set(getOwnPropertyKeys(a)),
300
+ * new Set(["a", "b", "c", "f", "g", symbol_i]),
301
+ * )
302
+ * assertEquals(
303
+ * new Set(getOwnPropertyKeys(Object.getPrototypeOf(a))),
304
+ * new Set(["constructor", "d", "e", symbol_h]),
305
+ * )
306
+ * assertEquals(
307
+ * new Set(getOwnPropertyKeys(b)),
308
+ * new Set(["a", "b", "c", "f", "g", symbol_i, symbol_j]),
309
+ * )
310
+ * assertEquals(
311
+ * new Set(getOwnPropertyKeys(Object.getPrototypeOf(b))),
312
+ * new Set(["constructor", "e", "g"]),
313
+ * )
314
+ * ```
315
+ */
316
+ export const getOwnPropertyKeys = (obj) => {
317
+ return [
318
+ ...object_getOwnPropertyNames(obj),
319
+ ...object_getOwnPropertySymbols(obj),
320
+ ];
321
+ };
322
+ /** get all **inherited** list of keys (string keys and symbol keys) of an object, up till a certain `depth`.
323
+ *
324
+ * directly owned keys will not be returned.
325
+ * for that, you should use the {@link getOwnPropertyKeys} function.
326
+ *
327
+ * the optional `depth` parameter lets you control how deep you'd like to go collecting the inherited keys.
328
+ *
329
+ * @param obj the object whose inherited keys are to be listed.
330
+ * @param depth the inheritance depth until which the function will accumulate keys for.
331
+ * - if an object `A` is provided as the `depth`,
332
+ * then all inherited keys up until `A` is reached in the inheritance chain will be collected, but not including the keys of `A`.
333
+ * - if a number `N` is provided as the `depth`,
334
+ * then the function will collect keys from `N` number of prototypes up the inheritance chain.
335
+ * - a depth of `0` would imply no traversal.
336
+ * - a depth of `1` would only traverse the first direct prototype of `obj` (i.e. `getOwnPropertyKeys(Object.getPrototypeOf(obj))`).
337
+ * @defaultValue `Object.prototype`
338
+ * @returns an array of keys that the object has inherited (string or symbolic keys).
339
+ *
340
+ * @example
341
+ * ```ts
342
+ * import { assertEquals } from "jsr:@std/assert"
343
+ *
344
+ * const
345
+ * symbol_h = Symbol("symbol h"),
346
+ * symbol_i = Symbol("symbol i"),
347
+ * symbol_j = Symbol("symbol j")
348
+ *
349
+ * class A {
350
+ * a = { v: 1 }
351
+ * b = { v: 2 }
352
+ * c: { v: number }
353
+ * d() { return { v: 4 } }
354
+ * e() { return { v: 5 } }
355
+ * f = () => { return { v: 6 } }
356
+ * g: () => ({ v: number })
357
+ * [symbol_h]() { return { v: 8 } }
358
+ * [symbol_i] = () => { return { v: 9 } }
359
+ *
360
+ * constructor() {
361
+ * this.c = { v: 3 }
362
+ * this.g = () => { return { v: 7 } }
363
+ * }
364
+ * }
365
+ *
366
+ * class B extends A {
367
+ * override a = { v: 11 }
368
+ * override e() { return { v: 15 } }
369
+ * //@ts-ignore: typescript does not permit defining a method over the name of an existing property
370
+ * override g() { return { v: 17 } }
371
+ * [symbol_j] = () => { return { v: 20 } }
372
+ *
373
+ * constructor() { super() }
374
+ * }
375
+ *
376
+ * const
377
+ * a = new A(),
378
+ * b = new B()
379
+ *
380
+ * assertEquals(
381
+ * new Set(getInheritedPropertyKeys(a)),
382
+ * new Set(["constructor", "d", "e", symbol_h]),
383
+ * )
384
+ *
385
+ * // below, notice how the inherited keys of `a` equal to its prototype's owned keys.
386
+ * // this is because methods of instances of `A` are defined on the prototype (i.e. properties of the prototype, rather than the instances').
387
+ * assertEquals(
388
+ * new Set(getInheritedPropertyKeys(a)),
389
+ * new Set(getOwnPropertyKeys(A.prototype)),
390
+ * )
391
+ *
392
+ * // also notice that inherited keys of `A.prototype` comes out as empty here,
393
+ * // even though it does techinally inherit members from its own prototype (which is `Object.prototype`).
394
+ * // the reason is that by default, we do not go deeper than `Object.prototype` to look for more keys.
395
+ * // this is because from an end-user's perspective, those keys are not useful.
396
+ * assertEquals(
397
+ * getInheritedPropertyKeys(A.prototype),
398
+ * [],
399
+ * )
400
+ *
401
+ * assertEquals(
402
+ * new Set(getInheritedPropertyKeys(b)),
403
+ * new Set(["constructor", "e", "g", "d", symbol_h]),
404
+ * )
405
+ * assertEquals(
406
+ * new Set(getInheritedPropertyKeys(b)),
407
+ * new Set([
408
+ * ...getOwnPropertyKeys(B.prototype),
409
+ * ...getInheritedPropertyKeys(B.prototype),
410
+ * ]),
411
+ * )
412
+ * assertEquals(
413
+ * new Set(getInheritedPropertyKeys(B.prototype)),
414
+ * new Set(["constructor", "e", "d", symbol_h]),
415
+ * )
416
+ *
417
+ * // testing out various depth
418
+ * assertEquals(
419
+ * new Set(getInheritedPropertyKeys(a, 1)),
420
+ * new Set(getOwnPropertyKeys(A.prototype)),
421
+ * )
422
+ * assertEquals(
423
+ * new Set(getInheritedPropertyKeys(b, 1)),
424
+ * new Set(getOwnPropertyKeys(B.prototype)),
425
+ * )
426
+ * assertEquals(
427
+ * new Set(getInheritedPropertyKeys(b, A.prototype)),
428
+ * new Set(getOwnPropertyKeys(B.prototype)),
429
+ * )
430
+ * assertEquals(
431
+ * new Set(getInheritedPropertyKeys(b, 2)),
432
+ * new Set([
433
+ * ...getOwnPropertyKeys(B.prototype),
434
+ * ...getOwnPropertyKeys(A.prototype),
435
+ * ]),
436
+ * )
437
+ * // below, we collect all inherited keys of `b`, including those that come from `Object.prototype`,
438
+ * // which is the base ancestral prototype of all objects.
439
+ * // to do that, we set the `depth` to `null`, which is the prototype of `Object.prototype`.
440
+ * // the test below may fail in new versions of javascript, where
441
+ * assertEquals(
442
+ * new Set(getInheritedPropertyKeys(b, null)),
443
+ * new Set([
444
+ * "constructor", "e", "g", "d", symbol_h,
445
+ * "__defineGetter__", "__defineSetter__", "hasOwnProperty", "__lookupGetter__", "__lookupSetter__",
446
+ * "isPrototypeOf", "propertyIsEnumerable", "toString", "valueOf", "toLocaleString",
447
+ * ]),
448
+ * )
449
+ * ```
450
+ */
451
+ export const getInheritedPropertyKeys = (obj, depth = prototypeOfClass(Object)) => {
452
+ // weird but expected facts:
453
+ // - `Object.prototype !== Object.getPrototypeOf(Object)`
454
+ // - `Object.prototype === Object.getPrototypeOf({})`
455
+ // - `null === Object.getPrototypeOf(Object.prototype)`
456
+ // - `you === "ugly"`
457
+ const prototype_chain = prototypeChainOfObject(obj, { start: 0, end: depth }), inherited_keys = prototype_chain
458
+ .map((prototype) => (getOwnPropertyKeys(prototype)))
459
+ .flat(1);
460
+ return [...(new Set(inherited_keys))];
461
+ };
462
+ /** get all **owned** getter property keys of an object `obj` (string keys and symbol keys).
463
+ *
464
+ * TODO: in the future, consider creating a `getInheritedGetterKeys` function with the same signature as `getInheritedPropertyKeys`.
465
+ *
466
+ * > [!note]
467
+ * > inherited getter property keys will not be included.
468
+ * > only directly defined getter property keys (aka owned keys) will be listed.
469
+ */
470
+ export const getOwnGetterKeys = (obj) => {
471
+ return getOwnPropertyKeys(obj).filter((key) => ("get" in object_getOwnPropertyDescriptor(obj, key)));
472
+ };
473
+ /** get all **owned** setter property keys of an object `obj` (string keys and symbol keys).
474
+ *
475
+ * TODO: in the future, consider creating a `getInheritedSetterKeys` function with the same signature as `getInheritedPropertyKeys`.
476
+ *
477
+ * > [!note]
478
+ * > inherited setter property keys will not be included.
479
+ * > only directly defined setter property keys (aka owned keys) will be listed.
480
+ */
481
+ export const getOwnSetterKeys = (obj) => {
482
+ return getOwnPropertyKeys(obj).filter((key) => ("set" in object_getOwnPropertyDescriptor(obj, key)));
483
+ };
484
+ /** create a new object that mirrors that functionality of an existing object `obj`, through composition.
485
+ *
486
+ * @param obj the object that is to be mimicked and composed.
487
+ * @param config control how the mirroring composition should behave.
488
+ * refer to the docs of {@link MirrorObjectThroughCompositionConfig} for more details.
489
+ *
490
+ * @example
491
+ * ```ts
492
+ * import { assertEquals } from "jsr:@std/assert"
493
+ *
494
+ * type TypeofB = Array<number> & { getLength(): number }
495
+ * type TypeofC = TypeofB & { countZeros(): number }
496
+ *
497
+ * const a = Array.prototype
498
+ * const b = {
499
+ * getLength(): number { return this.length }
500
+ * } as TypeofB
501
+ * const c = {
502
+ * countZeros(): number { return [...this].filter((value) => (value === 0)).length }
503
+ * } as TypeofC
504
+ *
505
+ * Object.setPrototypeOf(b, a)
506
+ * Object.setPrototypeOf(c, b)
507
+ *
508
+ * // below, we create an object `d` that mirrors the methods and protperties of `c`, but does not actually inherit it.
509
+ * const d = mirrorObjectThroughComposition(c, {
510
+ * baseKey: "_super",
511
+ * propertyKeys: ["length"],
512
+ * })
513
+ *
514
+ * // notice that `d` does not inherit `c` as its prototype, despite being able to utilize its methods and properties.
515
+ * assertEquals(Object.getPrototypeOf(d), Object.prototype, "`d` does not inherit `c`")
516
+ *
517
+ * d.push(0, 0, 0, 0, 1)
518
+ * assertEquals([...d], [0, 0, 0, 0, 1], "`d` also mirrors symbolic keys (such as iterators)")
519
+ * assertEquals([...c], [0, 0, 0, 0, 1], "mutations made to `d` are applied to `c`")
520
+ * assertEquals(d._super, c, "`c` is accessible via the `baseKey` (\"_super\") property")
521
+ * assertEquals(d.length, 5)
522
+ * assertEquals(c.length, 5)
523
+ * assertEquals(d.getLength(), 5)
524
+ * assertEquals(d.countZeros(), 4)
525
+ * d.splice(0, 3)
526
+ * assertEquals(d.countZeros(), 1)
527
+ * assertEquals(c.countZeros(), 1)
528
+ *
529
+ * // you may even hot-swap the object that is being composed inside of `d`.
530
+ * const e = [1, 2, 3, 4, 5, 6, 7, 8, 9]
531
+ * Object.setPrototypeOf(e, c)
532
+ * d._super = e as TypeofC
533
+ * assertEquals(d.length, 9)
534
+ * assertEquals(d.getLength(), 9)
535
+ * assertEquals(d.countZeros(), 0)
536
+ *
537
+ * // it is also possible for you to provide an existing `target` object onto which the mirroring should occur.
538
+ * // moreover, you can ignore specific keys which you would like not to be mirrored using the `ignoreKeys` option.
539
+ * class F {
540
+ * methodF() { return "press f for your fallen comrades" }
541
+ * // to stop typescript from complaining, we declare the instance methods and properties that will be mirrored.
542
+ * declare _super: typeof c
543
+ * declare length: number
544
+ * declare getLength: undefined
545
+ * declare countZeros: () => number
546
+ * }
547
+ * mirrorObjectThroughComposition(c, {
548
+ * target: F.prototype,
549
+ * baseKey: "_super",
550
+ * propertyKeys: ["length"],
551
+ * ignoreKeys: ["getLength"],
552
+ * })
553
+ * const f = new F()
554
+ * assertEquals(f instanceof F, true)
555
+ * assertEquals(f._super, c)
556
+ * assertEquals(f.length, 2)
557
+ * assertEquals(f.countZeros(), 1)
558
+ * assertEquals(f.getLength, undefined) // the `getLength` is not mirrored because it was present in the `ignoreKeys` option
559
+ * assertEquals(f.methodF(), "press f for your fallen comrades")
560
+ * ```
561
+ */
562
+ export const mirrorObjectThroughComposition = (obj, config) => {
563
+ const { baseKey, mirrorPrototype = true, propertyKeys = [], ignoreKeys = [], target = {} } = config, mirror_obj = target, property_keys = new Set(propertyKeys), ignore_keys = new Set(ignoreKeys), prototype_chain = isBoolean(mirrorPrototype) ? (mirrorPrototype
564
+ ? prototypeChainOfObject(obj, { end: prototypeOfClass(Object) })
565
+ : []) : isArray(mirrorPrototype)
566
+ ? mirrorPrototype
567
+ : prototypeChainOfObject(obj, mirrorPrototype);
568
+ prototype_chain.unshift(obj);
569
+ mirror_obj[baseKey] = obj;
570
+ // dynamically defining the methods composed from the base object `obj`
571
+ for (const prototype of prototype_chain) {
572
+ const prototype_keys = getOwnPropertyKeys(prototype);
573
+ for (const key of prototype_keys) {
574
+ if ((key in mirror_obj) || property_keys.has(key) || ignore_keys.has(key)) {
575
+ continue;
576
+ }
577
+ const { value, ...description } = object_getOwnPropertyDescriptor(prototype, key);
578
+ if (isFunction(value)) {
579
+ // `value` is very likely to be a prototype-bound method.
580
+ // so we create an appropriate proxying/wrapper method for it.
581
+ object_defineProperty(mirror_obj, key, {
582
+ ...description,
583
+ value(...args) {
584
+ // NOTE: the motivation behind returning `this[baseKey][key](...args)` instead of `obj[key](...args)` is that it would
585
+ // allow us to later on dynamically replace the underlying object that is being mirrored, which is a necessity when
586
+ // dealing with class-instance object mirroring that initially being defined on class's own prototype base constructor.
587
+ return this[baseKey][key](...args);
588
+ }
589
+ });
590
+ }
591
+ else {
592
+ // `value` is either an instance-bound property or does not exist exist (i.e. there's a getter/setter method in its place instead)
593
+ property_keys.add(key);
594
+ }
595
+ }
596
+ }
597
+ // dynamically defining the potentially transparent properties (i.e. later defined) of the composed object
598
+ for (const key of property_keys) {
599
+ // TODO: what if there's more to the description of the given key? we're certainly not preserving that information here unfortunately.
600
+ object_defineProperty(mirror_obj, key, {
601
+ get() { return this[baseKey][key]; },
602
+ set(new_value) { this[baseKey][key] = new_value; },
603
+ });
604
+ }
605
+ return mirror_obj;
606
+ };
607
+ /** create a subclass of the given `cls` class through composition rather than ES6 inheritance (aka `extends`).
608
+ * the composed instance resides under the property `this._super`, and all instance methods of the super class are copied/mirrored in the output class.
609
+ *
610
+ * TODO: provide a motivation/justification when compositional-mirroring subclass might be necessary to use instead of ES6 `extends` inheritance.
611
+ *
612
+ * @param cls the class to subclass through composition.
613
+ * @param property_keys specify additional property keys that exist within the instance of the class that need to be mirrored.
614
+ * @returns a class whose instances fully mirror the provided `cls` class's instance, without actually inheriting it.
615
+ *
616
+ * @example
617
+ * ```ts
618
+ * import { assertEquals } from "jsr:@std/assert"
619
+ *
620
+ * class A<T> extends Array<T> { constructor(quantity: number) { super(quantity) } }
621
+ * class B<T> extends subclassThroughComposition(A, {
622
+ * class: {},
623
+ * instance: { propertyKeys: ["length"] },
624
+ * })<T> { }
625
+ *
626
+ * // `B` mirrors the constructor of `A`, as well as provide an additional `"_super"` property to access the enclosed (class which is `A`)
627
+ * B satisfies { new(amount: number): B<any> }
628
+ * B._super satisfies typeof A
629
+ * // the following cannot be satisfied due to `A`'s restrictive constructor: `B satisfies { new(item1: string, item2: string, item3: string): B<string> }`
630
+ * // even though `A`'s super class (`Array`) does permit that kind of constructor signature:
631
+ * Array satisfies { new(item1: string, item2: string, item3: string): Array<string> }
632
+ *
633
+ * const my_array = new B<number>(5)
634
+ *
635
+ * // `my_array` encloses an instance of its "subclass" (which is an instance of class `A`) under the `"_super"` key.
636
+ * assertEquals(my_array instanceof B, true)
637
+ * assertEquals(my_array._super instanceof A, true)
638
+ * my_array._super satisfies A<unknown> // this should have been narrowed to `satisfies A<number>`, but typing class extensions with generics is nearly impossible
639
+ *
640
+ * // `my_array` mirrors the properties and methods of the instances of the "subclass" that it encloses.
641
+ * my_array.fill(1)
642
+ * my_array.push(2)
643
+ * my_array.push(3)
644
+ * assertEquals(my_array.at(-1), 3)
645
+ * assertEquals(my_array._super.at(-1), 3)
646
+ * assertEquals(my_array.length, 7)
647
+ * assertEquals(my_array._super.length, 7)
648
+ *
649
+ * // `my_array` mirrors the symbolic properties and methods as well.
650
+ * assertEquals([...my_array], [1, 1, 1, 1, 1, 2, 3]) // the `Symbol.iterator` method is mirrored successfully
651
+ *
652
+ * // `my_array` when a method that returns `new this.constructor(...)` (such as `Array.prototype.splice`), then an instance of
653
+ * // the enclosed subclass `A` will be created instead of `B` (had we had a regular inheritance via `class B extends A { }`).
654
+ * const
655
+ * splice_of_my_array = my_array.splice(0, 4, 0, 0),
656
+ * slice_of_my_array = my_array.slice(3, 4)
657
+ * assertEquals(splice_of_my_array instanceof A, true)
658
+ * assertEquals(slice_of_my_array instanceof A, true)
659
+ * assertEquals([...splice_of_my_array], [1, 1, 1, 1])
660
+ * assertEquals([...slice_of_my_array], [2])
661
+ * assertEquals([...my_array], [0, 0, 1, 2, 3])
662
+ * assertEquals([...my_array._super], [0, 0, 1, 2, 3])
663
+ *
664
+ * // the class `B` also mirrors static properties and static methods of `A` (and its inherited ones).
665
+ * // this means that any static method that creates a new instance via `new this(...)` will create an instance of `A` instead of `B`.
666
+ * const
667
+ * new_array_1 = B.from(["hello", "world"]),
668
+ * new_array_2 = B.of("goodbye", "world")
669
+ * assertEquals(new_array_1 instanceof A, true)
670
+ * assertEquals(new_array_2 instanceof A, true)
671
+ * assertEquals(new_array_1, A.from(["hello", "world"]))
672
+ * assertEquals(new_array_2, A.of("goodbye", "world"))
673
+ * ```
674
+ */
675
+ export const subclassThroughComposition = (cls, config = {}) => {
676
+ const class_config = config.class ?? {}, instance_config = config.instance ?? {}, instance_base_key = instance_config.baseKey ?? "_super", class_ignore_keys = class_config.ignoreKeys ?? [], new_cls = class {
677
+ constructor(...args) {
678
+ const composite_instance = new cls(...args);
679
+ // @ts-ignore: dynamic computed key assignment is not permitted in typescript
680
+ this[instance_base_key] = composite_instance;
681
+ }
682
+ }, //as ConstructorOf<MirrorComposition<T, INSTANCE_KEY>, Args> & MirrorComposition<CLS, CLASS_KEY>,
683
+ cls_prototype = prototypeOfClass(cls), new_cls_prototype = prototypeOfClass(new_cls);
684
+ // mirroring the static class methods and static properties of `cls` onto `new_cls`
685
+ mirrorObjectThroughComposition(cls, {
686
+ // NOTE: all classes extend the `Function` constructor (i.e. if we had `class A {}`, then `Object.getPrototypeOf(A) === Function.prototype`).
687
+ // but obviously, we don't want to mirror the `Function` methods within `cls` (aka the static methods of `Function`),
688
+ // which is why we set our default class's config `mirrorPrototype.end` to `Function.prototype`.
689
+ mirrorPrototype: { start: 0, end: prototypeOfClass(Function) },
690
+ baseKey: "_super",
691
+ ...class_config,
692
+ // the ignore list ensures that we don't overwrite existing properties of the `target` class.
693
+ // (even though there are checks in place that would prevent overwriting existing keys, it's better to be explicit)
694
+ ignoreKeys: [...class_ignore_keys, "length", "name", "prototype"],
695
+ target: new_cls,
696
+ });
697
+ // mirroring the instance methods and properties of `cls.prototype` onto `new_cls.prototype`
698
+ mirrorObjectThroughComposition(cls_prototype, {
699
+ baseKey: instance_base_key,
700
+ ...instance_config,
701
+ target: new_cls_prototype,
702
+ });
703
+ return new_cls;
704
+ };
705
+ /** monkey patch the prototype of a class.
706
+ *
707
+ * TODO: give usage examples and situations where this will be useful.
708
+ */
709
+ export const monkeyPatchPrototypeOfClass = (cls, key, value) => {
710
+ object_defineProperty(prototypeOfClass(cls), key, { value });
711
+ };
712
+ /** check if `obj` is either an object or function. */
713
+ export const isComplex = (obj) => {
714
+ const obj_type = typeof obj;
715
+ return obj_type === "object" || obj_type === "function";
716
+ };
717
+ /** check if `obj` is neither an object nor a function. */
718
+ export const isPrimitive = (obj) => {
719
+ return !isComplex(obj);
720
+ };
721
+ /** check if `obj` is a `function`.
722
+ *
723
+ * TODO: consider if it would be a good idea to include a generic parameter for the function's signature.
724
+ * i.e.: `<FN extends Function = Function>(obj: any): obj is FN`
725
+ */
726
+ export const isFunction = (obj) => {
727
+ return typeof obj === "function";
728
+ };
729
+ /** check if `obj` is an `Object`. */
730
+ export const isObject = (obj) => {
731
+ return typeof obj === "object";
732
+ };
733
+ /** check if `obj` is an `Array`. */
734
+ export const isArray = array_isArray;
735
+ /** check if `obj` is a `Record`, which is any non-nullable object that isn't an array;
736
+ * kind of like a dictionary.
737
+ */
738
+ export const isRecord = (obj) => {
739
+ return isObject(obj) && obj !== null && !isArray(obj);
740
+ };
741
+ /** check if `obj` is a `string`. */
742
+ export const isString = (obj) => {
743
+ return typeof obj === "string";
744
+ };
745
+ /** check if `obj` is a `number`. */
746
+ export const isNumber = (obj) => {
747
+ return typeof obj === "number";
748
+ };
749
+ /** check if `obj` is a `bigint`. */
750
+ export const isBigint = (obj) => {
751
+ return typeof obj === "bigint";
752
+ };
753
+ /** check if `obj` is either a `number` or a `bigint`. */
754
+ export const isNumeric = (obj) => {
755
+ return typeof obj === "number" || typeof obj === "bigint";
756
+ };
757
+ /** check if `obj` is `boolean`. */
758
+ export const isBoolean = (obj) => {
759
+ return typeof obj === "boolean";
760
+ };
761
+ /** check if `obj` is a `symbol`. */
762
+ export const isSymbol = (obj) => {
763
+ return typeof obj === "symbol";
764
+ };