@rimbu/common 0.12.3 → 1.0.0-alpha.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.
Files changed (139) hide show
  1. package/README.md +21 -4
  2. package/{src/async-optlazy.ts → dist/bun/async-optlazy.mts} +1 -1
  3. package/{src/async-reducer.ts → dist/bun/async-reducer.mts} +17 -8
  4. package/{src/collect.ts → dist/bun/collect.mts} +1 -1
  5. package/{src/comp.ts → dist/bun/comp.mts} +1 -1
  6. package/{src/index-range.ts → dist/bun/index-range.mts} +1 -1
  7. package/{src/index.ts → dist/bun/index.mts} +1 -1
  8. package/dist/bun/internal.mts +13 -0
  9. package/{src/reducer.ts → dist/bun/reducer.mts} +5 -3
  10. package/dist/cjs/async-optlazy.cjs +44 -0
  11. package/dist/cjs/async-reducer.cjs +1621 -0
  12. package/dist/cjs/collect.cjs +33 -0
  13. package/dist/cjs/comp.cjs +1621 -0
  14. package/dist/cjs/eq.cjs +210 -0
  15. package/dist/cjs/err.cjs +55 -0
  16. package/dist/cjs/index-range.cjs +114 -0
  17. package/dist/cjs/index.cjs +1685 -0
  18. package/dist/cjs/internal.cjs +1685 -0
  19. package/dist/cjs/optlazy.cjs +41 -0
  20. package/dist/cjs/range.cjs +52 -0
  21. package/dist/cjs/reducer.cjs +1621 -0
  22. package/dist/cjs/traverse-state.cjs +49 -0
  23. package/dist/cjs/types.cjs +18 -0
  24. package/dist/cjs/update.cjs +35 -0
  25. package/dist/{module/async-optlazy.js → esm/async-optlazy.mjs} +5 -8
  26. package/dist/esm/async-optlazy.mjs.map +1 -0
  27. package/dist/{module/async-reducer.js → esm/async-reducer.mjs} +67 -69
  28. package/dist/esm/async-reducer.mjs.map +1 -0
  29. package/dist/{module/collect.js → esm/collect.mjs} +1 -1
  30. package/dist/esm/collect.mjs.map +1 -0
  31. package/dist/{module/comp.js → esm/comp.mjs} +2 -2
  32. package/dist/esm/comp.mjs.map +1 -0
  33. package/dist/{module/eq.js → esm/eq.mjs} +1 -1
  34. package/dist/esm/eq.mjs.map +1 -0
  35. package/dist/{module/err.js → esm/err.mjs} +1 -1
  36. package/dist/esm/err.mjs.map +1 -0
  37. package/dist/{module/index-range.js → esm/index-range.mjs} +2 -3
  38. package/dist/esm/index-range.mjs.map +1 -0
  39. package/dist/esm/index.mjs +7 -0
  40. package/dist/esm/index.mjs.map +1 -0
  41. package/dist/esm/internal.mjs +14 -0
  42. package/dist/esm/internal.mjs.map +1 -0
  43. package/dist/{module/optlazy.js → esm/optlazy.mjs} +1 -1
  44. package/dist/esm/optlazy.mjs.map +1 -0
  45. package/dist/{module/range.js → esm/range.mjs} +1 -1
  46. package/dist/esm/range.mjs.map +1 -0
  47. package/dist/{module/reducer.js → esm/reducer.mjs} +10 -8
  48. package/dist/esm/reducer.mjs.map +1 -0
  49. package/dist/{module/traverse-state.js → esm/traverse-state.mjs} +1 -1
  50. package/dist/esm/traverse-state.mjs.map +1 -0
  51. package/dist/esm/types.mjs +2 -0
  52. package/dist/esm/types.mjs.map +1 -0
  53. package/dist/{module/update.js → esm/update.mjs} +1 -1
  54. package/dist/esm/update.mjs.map +1 -0
  55. package/dist/types/{async-optlazy.d.ts → async-optlazy.d.mts} +1 -1
  56. package/dist/types/{async-reducer.d.ts → async-reducer.d.mts} +2 -2
  57. package/dist/types/{collect.d.ts → collect.d.mts} +1 -1
  58. package/dist/types/{comp.d.ts → comp.d.mts} +1 -1
  59. package/dist/types/{index-range.d.ts → index-range.d.mts} +1 -1
  60. package/dist/types/{index.d.ts → index.d.mts} +1 -1
  61. package/dist/types/internal.d.mts +13 -0
  62. package/dist/types/{reducer.d.ts → reducer.d.mts} +1 -1
  63. package/package.json +27 -21
  64. package/src/async-optlazy.mts +49 -0
  65. package/src/async-reducer.mts +1157 -0
  66. package/src/collect.mts +43 -0
  67. package/src/comp.mts +625 -0
  68. package/src/eq.mts +406 -0
  69. package/src/err.mts +43 -0
  70. package/src/index-range.mts +120 -0
  71. package/{dist/module/index.js → src/index.mts} +2 -2
  72. package/src/internal.mts +13 -0
  73. package/{dist/main/optlazy.js → src/optlazy.mts} +24 -14
  74. package/src/range.mts +58 -0
  75. package/src/reducer.mts +1025 -0
  76. package/src/traverse-state.mts +59 -0
  77. package/src/types.mts +40 -0
  78. package/{dist/main/update.js → src/update.mts} +10 -10
  79. package/dist/main/async-optlazy.js +0 -48
  80. package/dist/main/async-optlazy.js.map +0 -1
  81. package/dist/main/async-reducer.js +0 -872
  82. package/dist/main/async-reducer.js.map +0 -1
  83. package/dist/main/collect.js +0 -11
  84. package/dist/main/collect.js.map +0 -1
  85. package/dist/main/comp.js +0 -542
  86. package/dist/main/comp.js.map +0 -1
  87. package/dist/main/eq.js +0 -382
  88. package/dist/main/eq.js.map +0 -1
  89. package/dist/main/err.js +0 -62
  90. package/dist/main/err.js.map +0 -1
  91. package/dist/main/index-range.js +0 -110
  92. package/dist/main/index-range.js.map +0 -1
  93. package/dist/main/index.js +0 -10
  94. package/dist/main/index.js.map +0 -1
  95. package/dist/main/internal.js +0 -17
  96. package/dist/main/internal.js.map +0 -1
  97. package/dist/main/optlazy.js.map +0 -1
  98. package/dist/main/range.js +0 -38
  99. package/dist/main/range.js.map +0 -1
  100. package/dist/main/reducer.js +0 -694
  101. package/dist/main/reducer.js.map +0 -1
  102. package/dist/main/traverse-state.js +0 -33
  103. package/dist/main/traverse-state.js.map +0 -1
  104. package/dist/main/types.js +0 -3
  105. package/dist/main/types.js.map +0 -1
  106. package/dist/main/update.js.map +0 -1
  107. package/dist/module/async-optlazy.js.map +0 -1
  108. package/dist/module/async-reducer.js.map +0 -1
  109. package/dist/module/collect.js.map +0 -1
  110. package/dist/module/comp.js.map +0 -1
  111. package/dist/module/eq.js.map +0 -1
  112. package/dist/module/err.js.map +0 -1
  113. package/dist/module/index-range.js.map +0 -1
  114. package/dist/module/index.js.map +0 -1
  115. package/dist/module/internal.js +0 -14
  116. package/dist/module/internal.js.map +0 -1
  117. package/dist/module/optlazy.js.map +0 -1
  118. package/dist/module/range.js.map +0 -1
  119. package/dist/module/reducer.js.map +0 -1
  120. package/dist/module/traverse-state.js.map +0 -1
  121. package/dist/module/types.js +0 -2
  122. package/dist/module/types.js.map +0 -1
  123. package/dist/module/update.js.map +0 -1
  124. package/dist/types/internal.d.ts +0 -13
  125. package/src/internal.ts +0 -13
  126. /package/{src/eq.ts → dist/bun/eq.mts} +0 -0
  127. /package/{src/err.ts → dist/bun/err.mts} +0 -0
  128. /package/{src/optlazy.ts → dist/bun/optlazy.mts} +0 -0
  129. /package/{src/range.ts → dist/bun/range.mts} +0 -0
  130. /package/{src/traverse-state.ts → dist/bun/traverse-state.mts} +0 -0
  131. /package/{src/types.ts → dist/bun/types.mts} +0 -0
  132. /package/{src/update.ts → dist/bun/update.mts} +0 -0
  133. /package/dist/types/{eq.d.ts → eq.d.mts} +0 -0
  134. /package/dist/types/{err.d.ts → err.d.mts} +0 -0
  135. /package/dist/types/{optlazy.d.ts → optlazy.d.mts} +0 -0
  136. /package/dist/types/{range.d.ts → range.d.mts} +0 -0
  137. /package/dist/types/{traverse-state.d.ts → traverse-state.d.mts} +0 -0
  138. /package/dist/types/{types.d.ts → types.d.mts} +0 -0
  139. /package/dist/types/{update.d.ts → update.d.mts} +0 -0
package/src/eq.mts ADDED
@@ -0,0 +1,406 @@
1
+ /**
2
+ * A function returning true if given `v1` and `v2` should be considered equal.
3
+ */
4
+ export type Eq<T> = (v1: T, v2: T) => boolean;
5
+
6
+ export namespace Eq {
7
+ export function convertAnyToString(value: any): string {
8
+ if (
9
+ typeof value !== 'object' ||
10
+ null === value ||
11
+ !('toString' in value) ||
12
+ typeof value.toString !== 'function' ||
13
+ value.toString !== Object.prototype.toString
14
+ ) {
15
+ return String(value);
16
+ }
17
+
18
+ return JSON.stringify(value);
19
+ }
20
+
21
+ const _anyFlatEq: Eq<any> = createAnyEq('FLAT');
22
+ const _anyShallowEq: Eq<any> = createAnyEq('SHALLOW');
23
+ const _anyDeepEq: Eq<any> = createAnyEq('DEEP');
24
+
25
+ /**
26
+ * Returns the default Eq instance, which is the Eq.anyDeepEq() instance.
27
+ */
28
+ export function defaultEq(): Eq<any> {
29
+ return _anyDeepEq;
30
+ }
31
+
32
+ /**
33
+ * An Eq instance that uses `Object.is` to determine if two objects are equal.
34
+ * @example
35
+ * ```ts
36
+ * const eq = Eq.objectIs
37
+ * console.log(eq(5, 5))
38
+ * // => true
39
+ * console.log(eq(5, 'a'))
40
+ * // => false
41
+ * ```
42
+ */
43
+ export const objectIs: Eq<any> = Object.is;
44
+
45
+ const _valueOfEq: Eq<{ valueOf(): any }> = (v1, v2) =>
46
+ Object.is(v1.valueOf(), v2.valueOf());
47
+
48
+ /**
49
+ * Returns an Eq instance for objects that have a `valueOf` method. It returns true if the `.valueOf` values of both given objects are equal.
50
+ * @typeparam T - the object type containing a valueOf function of type V
51
+ * @typeparam V - the valueOf result type
52
+ * @example
53
+ * ```ts
54
+ * const eq = Eq.valueOfEq()
55
+ * console.log(eq(new Number(5), new Number(5)))
56
+ * // => true
57
+ * console.log(eq(new Number(5), new Number(3)))
58
+ * // => false
59
+ * ```
60
+ */
61
+ export function valueOfEq<T extends { valueOf(): V }, V>(): Eq<T> {
62
+ return _valueOfEq;
63
+ }
64
+
65
+ /**
66
+ * Returns an Eq instance that compares Date objects according to their `valueOf` value.
67
+ * @example
68
+ * ```ts
69
+ * const eq = Eq.dateEq()
70
+ * console.log(eq(new Date(2020, 1, 1), new Date(2020, 1, 1))
71
+ * // => true
72
+ * console.log(eq(new Date(2020, 1, 1), new Date(2020, 2, 1))
73
+ * // => false
74
+ * ```
75
+ */
76
+ export function dateEq(): Eq<Date> {
77
+ return _valueOfEq;
78
+ }
79
+
80
+ function createIterableEq<T>(itemEq: Eq<T>): Eq<Iterable<T>> {
81
+ return (v1, v2) => {
82
+ if (Object.is(v1, v2)) return true;
83
+
84
+ const iter1 = v1[Symbol.iterator]();
85
+ const iter2 = v2[Symbol.iterator]();
86
+
87
+ while (true) {
88
+ const value1 = iter1.next();
89
+ const value2 = iter2.next();
90
+
91
+ if (value1.done || value2.done) return value1.done === value2.done;
92
+
93
+ if (!itemEq(value1.value, value2.value)) return false;
94
+ }
95
+ };
96
+ }
97
+
98
+ const _iterableAnyEq: Eq<Iterable<any>> = createIterableEq(defaultEq());
99
+
100
+ /**
101
+ * Returns an Eq instance that compares Iterables by comparing their elements with the given `itemEq` Eq instance.
102
+ * @typeparam T - the Iterable element type
103
+ * @param itemEq - (optional) the Eq instance to use to compare the Iterable's elements
104
+ * @example
105
+ * ```ts
106
+ * const eq = Eq.iterableEq();
107
+ * console.log(eq([1, 2, 3], [1, 2, 3])
108
+ * // => true
109
+ * console.log(eq([1, 2, 3], [1, 3, 2])
110
+ * // => false
111
+ * ```
112
+ */
113
+ export function iterableEq<T>(itemEq?: Eq<T>): Eq<Iterable<T>> {
114
+ if (undefined === itemEq) return _iterableAnyEq;
115
+
116
+ return createIterableEq(itemEq);
117
+ }
118
+
119
+ function createObjectEq(valueEq: Eq<any>): Eq<Record<any, any>> {
120
+ return (v1, v2) => {
121
+ if (Object.is(v1, v2)) return true;
122
+
123
+ if (v1.constructor !== v2.constructor) return false;
124
+
125
+ for (const key in v1) {
126
+ if (!(key in v2)) return false;
127
+ }
128
+
129
+ for (const key in v2) {
130
+ if (!(key in v1)) return false;
131
+ }
132
+
133
+ for (const key in v1) {
134
+ const value1 = v1[key];
135
+ const value2 = v2[key];
136
+
137
+ if (!valueEq(value1, value2)) return false;
138
+ }
139
+
140
+ return true;
141
+ };
142
+ }
143
+
144
+ const _objectEq: Eq<Record<any, any>> = createObjectEq(defaultEq());
145
+
146
+ /**
147
+ * Returns an Eq instance that checks equality of objects containing property values of type V by iteratively
148
+ * applying given `valueEq` to each of the object's property values.
149
+ * @typeparam - the object property value type
150
+ * @param valueEq - (optional) the Eq instance to use to compare property values
151
+ * @example
152
+ * ```ts
153
+ * const eq = Eq.objectEq()
154
+ * console.log(eq({ a: 1, b: { c: 2 }}, { b: { c: 2 }, a: 1 }))
155
+ * // => true
156
+ * console.log(eq({ a: 1, b: { c: 2 }}, { a: 1, b: { c: 3 }}))
157
+ * // => false
158
+ * ```
159
+ */
160
+ export function objectEq<V = any>(valueEq?: Eq<V>): Eq<Record<any, V>> {
161
+ if (undefined === valueEq) return _objectEq;
162
+
163
+ return createObjectEq(valueEq);
164
+ }
165
+
166
+ function createAnyEq(mode: 'FLAT' | 'SHALLOW' | 'DEEP'): Eq<any> {
167
+ const result: Eq<any> = (v1, v2): boolean => {
168
+ if (Object.is(v1, v2)) return true;
169
+
170
+ const type1 = typeof v1;
171
+ const type2 = typeof v2;
172
+
173
+ if (type1 !== type2) return false;
174
+
175
+ switch (type1) {
176
+ case 'undefined':
177
+ case 'bigint':
178
+ case 'boolean':
179
+ case 'number':
180
+ case 'string':
181
+ case 'symbol':
182
+ case 'function':
183
+ return Object.is(v1, v2);
184
+ case 'object': {
185
+ if (v1 === null || v2 === null) return false;
186
+
187
+ if (v1.constructor !== v2.constructor) {
188
+ return false;
189
+ }
190
+
191
+ if (
192
+ v1 instanceof Boolean ||
193
+ v1 instanceof Date ||
194
+ v1 instanceof Number ||
195
+ v1 instanceof String
196
+ ) {
197
+ return _valueOfEq(v1, v2);
198
+ }
199
+
200
+ if (mode !== 'FLAT') {
201
+ if (Symbol.iterator in v1 && Symbol.iterator in v2) {
202
+ if (mode === 'SHALLOW') {
203
+ return createIterableEq(_anyFlatEq)(v1, v2);
204
+ }
205
+
206
+ return createIterableEq(result)(v1, v2);
207
+ }
208
+
209
+ if (mode === 'SHALLOW') {
210
+ return createObjectEq(_anyFlatEq)(v1, v2);
211
+ }
212
+
213
+ return _objectEq(v1, v2);
214
+ }
215
+
216
+ // cannot establish that they are equal in flat mode
217
+ return false;
218
+ }
219
+ }
220
+ };
221
+
222
+ return result;
223
+ }
224
+
225
+ /**
226
+ * Returns an Eq instance that checks equality of any values. For composed values (objects and iterables)
227
+ * it will compare with Object.is.
228
+ * @typeparam T - the value type
229
+ * @example
230
+ * ```ts
231
+ * const eq = anyFlatEq()
232
+ * console.log(eq(1, 'a'))
233
+ * // => false
234
+ * console.log(eq({ a: 1, b: 2 }, { b: 2, a: 1 }))
235
+ * // => false
236
+ * ```
237
+ */
238
+ export function anyFlatEq<T = any>(): Eq<T> {
239
+ return _anyFlatEq;
240
+ }
241
+
242
+ /**
243
+ * Returns an Eq instance that checks equality of any values. For composed values (objects and iterables)
244
+ * it will enter 1 level, and if again compound values are found, they are compared
245
+ * with Object.is.
246
+ * @typeparam T - the value type
247
+ * @example
248
+ * ```ts
249
+ * const eq = anyFlatEq()
250
+ * console.log(eq(1, 'a'))
251
+ * // => false
252
+ * console.log(eq({ a: 1, b: 2 }, { b: 2, a: 1 }))
253
+ * // => true
254
+ * console.log(eq([{ a: 1, b: 2 }], [{ b: 2, a: 1 }]))
255
+ * // => false
256
+ * ```
257
+ */
258
+ export function anyShallowEq<T = any>(): Eq<T> {
259
+ return _anyShallowEq;
260
+ }
261
+
262
+ /**
263
+ * Returns an Eq instance that checks equality of any values. For composed values (objects and iterables)
264
+ * it will recursively compare the contained values.
265
+ * @note may have poor performance for deeply nested types and large arrays, and objects with circular structures
266
+ * may cause infinite loops
267
+ * @typeparam T - the value type
268
+ * @example
269
+ * ```ts
270
+ * const eq = anyFlatEq()
271
+ * console.log(eq(1, 'a'))
272
+ * // => false
273
+ * console.log(eq({ a: 1, b: 2 }, { b: 2, a: 1 }))
274
+ * // => true
275
+ * console.log(eq([{ a: 1, b: 2 }], [{ b: 2, a: 1 }]))
276
+ * // => false
277
+ * ```
278
+ */
279
+ export function anyDeepEq<T = any>(): Eq<T> {
280
+ return _anyDeepEq;
281
+ }
282
+
283
+ const _defaultCollator = Intl.Collator('und');
284
+
285
+ const _defaultStringCollatorEq: Eq<any> = (v1, v2) =>
286
+ _defaultCollator.compare(v1, v2) === 0;
287
+
288
+ /**
289
+ * Returns an Eq instance that considers strings equal taking the given or default locale into account.
290
+ * @param locales - (optional) a locale or list of locales
291
+ * @param options - (optional) see String.localeCompare for details
292
+ * @example
293
+ * ```ts
294
+ * const eq = Eq.createStringCollatorEq()
295
+ * console.log(eq('a', 'a'))
296
+ * // => true
297
+ * console.log(eq('abc', 'aBc'))
298
+ * // => false
299
+ * ```
300
+ */
301
+ export function createStringCollatorEq(
302
+ ...args: ConstructorParameters<typeof Intl.Collator>
303
+ ): Eq<string> {
304
+ if (args.length === 0) return _defaultStringCollatorEq;
305
+
306
+ const collator = Intl.Collator(...args);
307
+
308
+ return (v1, v2) => collator.compare(v1, v2) === 0;
309
+ }
310
+
311
+ const _stringCaseInsensitiveEq: Eq<string> = createStringCollatorEq('und', {
312
+ sensitivity: 'accent',
313
+ });
314
+
315
+ /**
316
+ * Returns an Eq instance that considers strings equal regardless of their case.
317
+ * @example
318
+ * ```ts
319
+ * const eq = Eq.stringCaseInsentitiveEq()
320
+ * console.log(eq('aB', 'Ab'))
321
+ * // => true
322
+ * console.log(eq('aBc', 'abB'))
323
+ * // => false
324
+ * ```
325
+ */
326
+ export function stringCaseInsentitiveEq(): Eq<string> {
327
+ return _stringCaseInsensitiveEq;
328
+ }
329
+
330
+ const _stringCharCodeEq: Eq<string> = (v1, v2) => {
331
+ const len = v1.length;
332
+
333
+ if (len !== v2.length) return false;
334
+
335
+ let i = -1;
336
+
337
+ while (++i < len) {
338
+ if (v1.charCodeAt(i) !== v2.charCodeAt(i)) return false;
339
+ }
340
+
341
+ return true;
342
+ };
343
+
344
+ /**
345
+ * Returns an Eq instance that considers strings equal when all their charcodes are equal.
346
+ * @example
347
+ * ```ts
348
+ * const eq = Eq.stringCharCodeEq()
349
+ * console.log(eq('a', 'a'))
350
+ * // => true
351
+ * console.log(eq('abc', 'aBc'))
352
+ * // => false
353
+ * ```
354
+ */
355
+ export function stringCharCodeEq(): Eq<string> {
356
+ return _stringCharCodeEq;
357
+ }
358
+
359
+ const _anyToStringEq: Eq<any> = (v1, v2) =>
360
+ convertAnyToString(v1) === convertAnyToString(v2);
361
+
362
+ export function anyToStringEq(): Eq<any> {
363
+ return _anyToStringEq;
364
+ }
365
+
366
+ const _anyJsonEq: Eq<any> = (v1, v2) =>
367
+ JSON.stringify(v1) === JSON.stringify(v2);
368
+
369
+ /**
370
+ * Returns an Eq instance that considers values equal their JSON.stringify values are equal.
371
+ * @example
372
+ * ```ts
373
+ * const eq = Eq.anyJsonEq()
374
+ * console.log(eq({ a: 1, b: 2 }, { a: 1, b: 2 }))
375
+ * // => true
376
+ * console.log(eq({ a: 1, b: 2 }, { b: 2, a: 1 }))
377
+ * // => false
378
+ * ```
379
+ */
380
+ export function anyJsonEq(): Eq<any> {
381
+ return _anyJsonEq;
382
+ }
383
+
384
+ /**
385
+ * Returns an `Eq` instance for tuples that considers two tuples [A, B] and [C, D] equal if [A, B] equals [C, D],
386
+ * or if [A, B] equals [D, C]
387
+ * @param eq - (optional) an alternative `Eq` instance to use for the values in the tuple
388
+ * @example
389
+ * ```ts
390
+ * const eq = Eq.tupleSymmetric()
391
+ * console.log(eq([1, 2], [1, 2]))
392
+ * // => true
393
+ * console.log(eq([1, 2], [2, 1]))
394
+ * // => true
395
+ * console.log(eq([1, 3], [2, 1]))
396
+ * // => false
397
+ * ```
398
+ */
399
+ export function tupleSymmetric<T>(
400
+ eq: Eq<T> = defaultEq()
401
+ ): Eq<readonly [T, T]> {
402
+ return (tup1: readonly [T, T], tup2: readonly [T, T]): boolean =>
403
+ (eq(tup1[0], tup2[0]) && eq(tup1[1], tup2[1])) ||
404
+ (eq(tup1[0], tup2[1]) && eq(tup1[1], tup2[0]));
405
+ }
406
+ }
package/src/err.mts ADDED
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Throws an `Err.ForcedError` error when called.
3
+ * @example
4
+ * ```ts
5
+ * const emptyMap = HashMap.empty<number, string>()
6
+ * emptyMap.get(5, Err);
7
+ * // throws: Err.CustomError(message: 'Err: forced to throw error')
8
+ * ```
9
+ */
10
+ export function Err(): never {
11
+ return ErrBase.msg('Err: Forced to throw error')();
12
+ }
13
+
14
+ export namespace ErrBase {
15
+ /**
16
+ * A custom error instance.
17
+ */
18
+ export abstract class CustomError {
19
+ constructor(readonly message: string) {}
20
+
21
+ get name() {
22
+ return this.constructor.name;
23
+ }
24
+ }
25
+
26
+ export class ForcedError extends CustomError {}
27
+
28
+ /**
29
+ * Returns a function that, when called, throws an `Err.ForcedError` with the given `message` string.
30
+ * @param message - the message to put in the `Err.ForcedError` instance.
31
+ * @example
32
+ * ```ts
33
+ * const emptyMap = HashMap.empty<number, string>()
34
+ * emptyMap.get(5, ErrBase.msg('not found'));
35
+ * // throws: Err.CustomError(message: 'not found')
36
+ * ```
37
+ */
38
+ export function msg(message: string): () => never {
39
+ return function (): never {
40
+ throw new ErrBase.ForcedError(message);
41
+ };
42
+ }
43
+ }
@@ -0,0 +1,120 @@
1
+ import type { Range } from './internal.mjs';
2
+
3
+ /**
4
+ * A flexible range specification for numeric indices.
5
+ * If a start or end is defined, a tuple can be used where the second item is a boolean
6
+ * indicating whether that end is inclusive or exclusive.<br/>
7
+ * An IndexRange can have one of the following forms:<br/>
8
+ * <br/>
9
+ * - { amount: number }<br/>
10
+ * - { start: number }<br/>
11
+ * - { start: number, amount: number }<br/>
12
+ * - { start: number, end: number }<br/>
13
+ * - { start: number, end: [number, boolean] }<br/>
14
+ * - { start: [number, boolean] }<br/>
15
+ * - { start: [number, boolean], amount: number }<br/>
16
+ * - { start: [number, boolean], end: number }<br/>
17
+ * - { start: [number, boolean], end: [number, boolean] }<br/>
18
+ * - { end: number }<br/>
19
+ * - { end: [number, boolean] }<br/>
20
+ */
21
+ export type IndexRange =
22
+ | { amount: number; start?: number | [number, boolean]; end?: undefined }
23
+ | Range<number>;
24
+
25
+ export namespace IndexRange {
26
+ /**
27
+ * Returns, given the `range` `IndexRange`, a normalized tuple containing the
28
+ * start index, and optionally an end index.
29
+ * @param range - the `IndexRange` to use
30
+ */
31
+ export function getIndexRangeIndices(
32
+ range: IndexRange
33
+ ): [number, number | undefined] {
34
+ if (undefined !== range.amount) {
35
+ if (undefined === range.start) return [0, range.amount - 1];
36
+
37
+ if (Array.isArray(range.start)) {
38
+ const [start, includeStart] = range.start;
39
+ if (includeStart) return [start, start + range.amount - 1];
40
+ return [start + 1, start + 1 + range.amount - 1];
41
+ }
42
+ return [range.start, range.start + range.amount - 1];
43
+ }
44
+
45
+ let start = 0;
46
+ let end: number | undefined = undefined;
47
+
48
+ if (`start` in range) {
49
+ if (Array.isArray(range.start)) {
50
+ if (range.start[1]) start = range.start[0];
51
+ else start = range.start[0] + 1;
52
+ } else start = range.start ?? 0;
53
+ }
54
+
55
+ if (`end` in range) {
56
+ if (Array.isArray(range.end)) {
57
+ if (range.end[1]) end = range.end[0];
58
+ else end = range.end[0] - 1;
59
+ } else end = range.end;
60
+ }
61
+
62
+ return [start, end];
63
+ }
64
+
65
+ /**
66
+ * Returns, given the `range` `IndexRange`, and a target maximum `length`, the actual index range.
67
+ * This can be one of three options:
68
+ * - 'empty': there are no elements within the range
69
+ * - 'all': all elements are within the range
70
+ * - [start: number, end: number]: an inclusive range of element indices within the given range
71
+ * @param range - the `IndexRange` to use
72
+ * @param length - the target maximum length
73
+ */
74
+ export function getIndicesFor(
75
+ range: IndexRange,
76
+ length: number
77
+ ): [number, number] | 'empty' | 'all' {
78
+ if (length <= 0) return 'empty';
79
+
80
+ let start = 0;
81
+ let end = length - 1;
82
+
83
+ if (undefined !== range.start) {
84
+ if (Array.isArray(range.start)) {
85
+ start = range.start[0];
86
+ if (!range.start[1]) start++;
87
+ } else start = range.start;
88
+
89
+ if (start >= length || -start > length) return 'empty';
90
+ if (start < 0) start = length + start;
91
+ }
92
+
93
+ if (undefined !== range.amount) {
94
+ if (range.amount <= 0) return 'empty';
95
+ if (undefined === range.start) {
96
+ if (range.amount >= length) return 'all';
97
+ return [0, Math.min(end, range.amount - 1)];
98
+ }
99
+
100
+ end = start + range.amount - 1;
101
+ } else if (undefined !== range.end) {
102
+ if (Array.isArray(range.end)) {
103
+ end = range.end[0];
104
+ if (!range.end[1]) {
105
+ if (end === 0) return 'empty';
106
+ end--;
107
+ }
108
+ } else end = range.end;
109
+
110
+ if (end < 0) end = length + end;
111
+ }
112
+
113
+ if (end < start) return 'empty';
114
+ end = Math.min(length - 1, end);
115
+
116
+ if (start === 0 && end === length - 1) return 'all';
117
+
118
+ return [start, end];
119
+ }
120
+ }
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * The `@rimbu/common` package provides many commonly used types and utilities that can also be of use to Rimbu users.<br/>
5
5
  */
6
- export * from './internal';
7
- //# sourceMappingURL=index.js.map
6
+
7
+ export * from './internal.mjs';
@@ -0,0 +1,13 @@
1
+ export * from './collect.mjs';
2
+ export * from './comp.mjs';
3
+ export * from './eq.mjs';
4
+ export * from './err.mjs';
5
+ export * from './index-range.mjs';
6
+ export * from './optlazy.mjs';
7
+ export * from './range.mjs';
8
+ export * from './reducer.mjs';
9
+ export * from './traverse-state.mjs';
10
+ export * from './types.mjs';
11
+ export * from './update.mjs';
12
+ export * from './async-optlazy.mjs';
13
+ export * from './async-reducer.mjs';
@@ -1,6 +1,9 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OptLazyOr = exports.OptLazy = void 0;
1
+ /**
2
+ * A potentially lazy value of type T.
3
+ * @typeparam T - the value type
4
+ */
5
+ export type OptLazy<T> = T | (() => T);
6
+
4
7
  /**
5
8
  * Returns the value contained in an `OptLazy` instance of type T.
6
9
  * @param optLazy - the `OptLazy` value of type T
@@ -11,12 +14,19 @@ exports.OptLazyOr = exports.OptLazy = void 0;
11
14
  * OptLazy(() => () => 1) // => () => 1
12
15
  * ```
13
16
  */
14
- function OptLazy(optLazy) {
15
- if (optLazy instanceof Function)
16
- return optLazy();
17
- return optLazy;
17
+ export function OptLazy<T>(optLazy: OptLazy<T>): T {
18
+ if (optLazy instanceof Function) return optLazy();
19
+ return optLazy;
18
20
  }
19
- exports.OptLazy = OptLazy;
21
+
22
+ /**
23
+ * A potentially lazy value that, in case of a lazy function, received a default value
24
+ * that it can return.
25
+ * @typeparam T - the value type
26
+ * @typeparam O - the default value type
27
+ */
28
+ export type OptLazyOr<T, O> = T | ((none: O) => T | O);
29
+
20
30
  /**
21
31
  * Returns the value contained in an `OptLazyOr` instance of type T, or the given
22
32
  * `otherValue` if the lazy function returns it.
@@ -29,10 +39,10 @@ exports.OptLazy = OptLazy;
29
39
  * OptLazyOr((none) => none, 'a') // => 'a'
30
40
  * ```
31
41
  */
32
- function OptLazyOr(optLazyOr, otherValue) {
33
- if (optLazyOr instanceof Function)
34
- return optLazyOr(otherValue);
35
- return optLazyOr;
42
+ export function OptLazyOr<T, O>(
43
+ optLazyOr: OptLazyOr<T, O>,
44
+ otherValue: O
45
+ ): T | O {
46
+ if (optLazyOr instanceof Function) return optLazyOr(otherValue);
47
+ return optLazyOr;
36
48
  }
37
- exports.OptLazyOr = OptLazyOr;
38
- //# sourceMappingURL=optlazy.js.map
package/src/range.mts ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * A range definition for any type of (orderable) value.
3
+ * If a start or end is defined, a tuple can be used where the second item is a boolean
4
+ * indicating whether that end is inclusive (true) or exclusive (false).<br/>
5
+ * A Range of type T can have one of the following forms:<br/>
6
+ * <br/>
7
+ * - { end: T }<br/>
8
+ * - { end: [T, boolean] }<br/>
9
+ * - { start: T }<br/>
10
+ * - { start: T, end: T }<br/>
11
+ * - { start: T, end: [T, boolean] }<br/>
12
+ * - { start: [T, boolean] }<br/>
13
+ * - { start: [T, boolean], end: T }<br/>
14
+ * - { start: [T, boolean], end: [T, boolean] }<br/>
15
+ */
16
+ export type Range<T> =
17
+ | { start: T | [T, boolean]; end?: T | [T, boolean]; amount?: undefined }
18
+ | { start?: T | [T, boolean]; end: T | [T, boolean]; amount?: undefined };
19
+
20
+ export namespace Range {
21
+ /**
22
+ * Simplifies a given `range` `Range` input for easier processing, by returning optional
23
+ * start and end ranges including whether they are inclusive or exclusive
24
+ * @param range - the `Range` to use
25
+ */
26
+ export function getNormalizedRange<T>(range: Range<T>): {
27
+ start?: [T, boolean] | undefined;
28
+ end?: [T, boolean] | undefined;
29
+ } {
30
+ let start: [T, boolean] | undefined = undefined;
31
+ let end: [T, boolean] | undefined = undefined;
32
+
33
+ if (`start` in range && undefined !== range.start) {
34
+ if (
35
+ Array.isArray(range.start) &&
36
+ range.start.length === 2 &&
37
+ typeof range.start[1] === 'boolean'
38
+ ) {
39
+ start = range.start;
40
+ } else {
41
+ start = [range.start as T, true];
42
+ }
43
+ }
44
+ if (`end` in range && undefined !== range.end) {
45
+ if (
46
+ Array.isArray(range.end) &&
47
+ range.end.length === 2 &&
48
+ typeof range.end[1] === 'boolean'
49
+ ) {
50
+ end = range.end;
51
+ } else {
52
+ end = [range.end as T, true];
53
+ }
54
+ }
55
+
56
+ return { start, end };
57
+ }
58
+ }