mutts 1.0.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 (82) hide show
  1. package/README.md +150 -0
  2. package/dist/chunks/decorator-BXsign4Z.js +176 -0
  3. package/dist/chunks/decorator-BXsign4Z.js.map +1 -0
  4. package/dist/chunks/decorator-CPbZNnsX.esm.js +168 -0
  5. package/dist/chunks/decorator-CPbZNnsX.esm.js.map +1 -0
  6. package/dist/decorator.d.ts +50 -0
  7. package/dist/decorator.esm.js +2 -0
  8. package/dist/decorator.esm.js.map +1 -0
  9. package/dist/decorator.js +11 -0
  10. package/dist/decorator.js.map +1 -0
  11. package/dist/destroyable.d.ts +48 -0
  12. package/dist/destroyable.esm.js +91 -0
  13. package/dist/destroyable.esm.js.map +1 -0
  14. package/dist/destroyable.js +98 -0
  15. package/dist/destroyable.js.map +1 -0
  16. package/dist/eventful.d.ts +11 -0
  17. package/dist/eventful.esm.js +88 -0
  18. package/dist/eventful.esm.js.map +1 -0
  19. package/dist/eventful.js +90 -0
  20. package/dist/eventful.js.map +1 -0
  21. package/dist/index.d.ts +15 -0
  22. package/dist/index.esm.js +7 -0
  23. package/dist/index.esm.js.map +1 -0
  24. package/dist/index.js +52 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/indexable.d.ts +31 -0
  27. package/dist/indexable.esm.js +85 -0
  28. package/dist/indexable.esm.js.map +1 -0
  29. package/dist/indexable.js +89 -0
  30. package/dist/indexable.js.map +1 -0
  31. package/dist/mutts.umd.js +2 -0
  32. package/dist/mutts.umd.js.map +1 -0
  33. package/dist/mutts.umd.min.js +2 -0
  34. package/dist/mutts.umd.min.js.map +1 -0
  35. package/dist/promiseChain.d.ts +11 -0
  36. package/dist/promiseChain.esm.js +72 -0
  37. package/dist/promiseChain.esm.js.map +1 -0
  38. package/dist/promiseChain.js +74 -0
  39. package/dist/promiseChain.js.map +1 -0
  40. package/dist/reactive.d.ts +114 -0
  41. package/dist/reactive.esm.js +1455 -0
  42. package/dist/reactive.esm.js.map +1 -0
  43. package/dist/reactive.js +1472 -0
  44. package/dist/reactive.js.map +1 -0
  45. package/dist/std-decorators.d.ts +17 -0
  46. package/dist/std-decorators.esm.js +161 -0
  47. package/dist/std-decorators.esm.js.map +1 -0
  48. package/dist/std-decorators.js +169 -0
  49. package/dist/std-decorators.js.map +1 -0
  50. package/docs/decorator.md +300 -0
  51. package/docs/destroyable.md +294 -0
  52. package/docs/events.md +225 -0
  53. package/docs/indexable.md +561 -0
  54. package/docs/promiseChain.md +218 -0
  55. package/docs/reactive.md +2072 -0
  56. package/docs/std-decorators.md +558 -0
  57. package/package.json +132 -0
  58. package/src/decorator.test.ts +495 -0
  59. package/src/decorator.ts +205 -0
  60. package/src/destroyable.test.ts +155 -0
  61. package/src/destroyable.ts +158 -0
  62. package/src/eventful.test.ts +380 -0
  63. package/src/eventful.ts +69 -0
  64. package/src/index.ts +7 -0
  65. package/src/indexable.test.ts +388 -0
  66. package/src/indexable.ts +124 -0
  67. package/src/promiseChain.test.ts +201 -0
  68. package/src/promiseChain.ts +99 -0
  69. package/src/reactive/array.test.ts +923 -0
  70. package/src/reactive/array.ts +352 -0
  71. package/src/reactive/core.test.ts +1663 -0
  72. package/src/reactive/core.ts +866 -0
  73. package/src/reactive/index.ts +28 -0
  74. package/src/reactive/interface.test.ts +1477 -0
  75. package/src/reactive/interface.ts +231 -0
  76. package/src/reactive/map.test.ts +866 -0
  77. package/src/reactive/map.ts +162 -0
  78. package/src/reactive/set.test.ts +289 -0
  79. package/src/reactive/set.ts +142 -0
  80. package/src/std-decorators.test.ts +679 -0
  81. package/src/std-decorators.ts +182 -0
  82. package/src/utils.ts +52 -0
@@ -0,0 +1,561 @@
1
+ # Indexable Documentation
2
+
3
+ ## 1. Overview
4
+
5
+ ### Introduction
6
+
7
+ The `Indexable` utility is a powerful TypeScript/JavaScript function that enables classes to have numeric index access similar to arrays, while maintaining full control over how values are retrieved and stored. It uses JavaScript's Proxy API to intercept property access and provide custom indexing behavior.
8
+
9
+ ### What is Indexable?
10
+
11
+ `Indexable` is a factory function that creates classes with numeric index access capabilities. It allows you to:
12
+ - Access object properties using numeric indices (e.g., `obj[0]`, `obj[1]`)
13
+ - Customize how values are retrieved and stored
14
+ - Extend existing classes with index access
15
+ - Create read-only or read-write indexable objects
16
+ - Maintain type safety with TypeScript
17
+
18
+ ### Key Concepts
19
+
20
+ - **Index Access**: Numeric property access like arrays (`obj[0]`)
21
+ - **Proxy Interception**: Uses JavaScript Proxy to intercept property access
22
+ - **Accessor Functions**: Custom functions that define how to get/set values
23
+ - **Symbol-Based Methods**: Uses `getAt` and `setAt` symbols for custom access logic
24
+ - **Type Safety**: Full TypeScript support with generic types
25
+
26
+ ### Use Cases
27
+
28
+ - Custom collection classes
29
+ - Data structures with numeric indexing
30
+ - Wrapper classes for external data sources
31
+ - Immutable data structures
32
+ - Performance-optimized access patterns
33
+
34
+ ## 2. API Reference
35
+
36
+ ### Core Symbols
37
+
38
+ #### getAt
39
+ ```typescript
40
+ export const getAt = Symbol('getAt')
41
+ ```
42
+ A symbol used to define custom getter logic for numeric index access. Classes can implement this method to control how values are retrieved.
43
+
44
+ #### setAt
45
+ ```typescript
46
+ export const setAt = Symbol('setAt')
47
+ ```
48
+ A symbol used to define custom setter logic for numeric index access. Classes can implement this method to control how values are stored.
49
+
50
+ ### Main Function
51
+
52
+ #### Indexable()
53
+ ```typescript
54
+ export function Indexable<Items, Base extends abstract new (...args: any[]) => any>(
55
+ base: Base,
56
+ accessor: Accessor<InstanceType<Base>, Items>
57
+ ): new (...args: ConstructorParameters<Base>) => InstanceType<Base> & { [x: number]: Items }
58
+ ```
59
+
60
+ The main factory function that creates indexable classes. It has multiple overloads to support different use cases.
61
+
62
+ ### Interfaces
63
+
64
+ #### IndexingAt
65
+ ```typescript
66
+ interface IndexingAt<Items = any> {
67
+ [getAt](index: number): Items
68
+ }
69
+ ```
70
+ Interface for classes that implement custom getter logic using the `getAt` symbol.
71
+
72
+ #### Accessor
73
+ ```typescript
74
+ interface Accessor<T, Items> {
75
+ get(this: T, index: number): Items
76
+ set?(this: T, index: number, value: Items): void
77
+ }
78
+ ```
79
+ Interface defining how to access and optionally modify values at numeric indices.
80
+
81
+ #### AbstractGetAt
82
+ ```typescript
83
+ abstract class AbstractGetAt<Items = any> {
84
+ abstract [getAt](index: number): Items
85
+ }
86
+ ```
87
+ Abstract base class for creating indexable classes with custom getter logic.
88
+
89
+ ### Type Utilities
90
+
91
+ #### AtReturnType
92
+ ```typescript
93
+ type AtReturnType<T> = T extends { [getAt](index: number): infer R } ? R : never
94
+ ```
95
+ Utility type that extracts the return type from a class's `getAt` method.
96
+
97
+ ## 3. Function Overloads
98
+
99
+ ### Overload 1: Base Class with Accessor
100
+ ```typescript
101
+ Indexable<Items, Base>(base: Base, accessor: Accessor<InstanceType<Base>, Items>)
102
+ ```
103
+ Creates an indexable class that extends an existing base class with custom accessor functions.
104
+
105
+ **Example:**
106
+ ```typescript
107
+ class MyClass {
108
+ constructor(public data: number[]) {}
109
+ }
110
+
111
+ const IndexableMyClass = Indexable(MyClass, {
112
+ get(this: MyClass, index: number) {
113
+ return this.data[index] * 2; // Custom access logic
114
+ },
115
+ set(this: MyClass, index: number, value: number) {
116
+ this.data[index] = value / 2; // Custom storage logic
117
+ }
118
+ });
119
+
120
+ const instance = new IndexableMyClass([1, 2, 3]);
121
+ console.log(instance[0]); // 2 (1 * 2)
122
+ instance[1] = 8; // Stores 4 (8 / 2)
123
+ ```
124
+
125
+ ### Overload 2: Accessor Only
126
+ ```typescript
127
+ Indexable<Items>(accessor: Accessor<any, Items>)
128
+ ```
129
+ Creates an indexable class with custom accessor functions but no base class.
130
+
131
+ **Example:**
132
+ ```typescript
133
+ const IndexableClass = Indexable({
134
+ get(this: any, index: number) {
135
+ return `Item ${index}`;
136
+ }
137
+ });
138
+
139
+ const instance = new IndexableClass();
140
+ console.log(instance[0]); // "Item 0"
141
+ console.log(instance[5]); // "Item 5"
142
+ ```
143
+
144
+ ### Overload 3: Base Class with getAt Method
145
+ ```typescript
146
+ Indexable<Base extends new (...args: any[]) => IndexingAt>(base: Base)
147
+ ```
148
+ Creates an indexable class from a base class that already implements the `getAt` method.
149
+
150
+ **Example:**
151
+ ```typescript
152
+ class DataStore {
153
+ constructor(private items: string[]) {}
154
+
155
+ [getAt](index: number): string {
156
+ return this.items[index] || 'default';
157
+ }
158
+ }
159
+
160
+ const IndexableDataStore = Indexable(DataStore);
161
+ const store = new IndexableDataStore(['a', 'b', 'c']);
162
+ console.log(store[0]); // "a"
163
+ console.log(store[10]); // "default"
164
+ ```
165
+
166
+ ### Overload 4: Abstract Class
167
+ ```typescript
168
+ Indexable<Items>()
169
+ ```
170
+ Creates an abstract indexable class that must be extended with a `getAt` implementation.
171
+
172
+ **Example:**
173
+ ```typescript
174
+ const AbstractIndexable = Indexable<string>();
175
+
176
+ class StringArray extends AbstractIndexable {
177
+ constructor(private items: string[]) {
178
+ super();
179
+ }
180
+
181
+ [getAt](index: number): string {
182
+ return this.items[index] || '';
183
+ }
184
+ }
185
+
186
+ const array = new StringArray(['hello', 'world']);
187
+ console.log(array[0]); // "hello"
188
+ ```
189
+
190
+ ## 4. Implementation Details
191
+
192
+ ### Proxy-Based Architecture
193
+
194
+ The Indexable utility uses JavaScript's Proxy API to intercept property access. When you access a numeric property (e.g., `obj[0]`), the proxy:
195
+
196
+ 1. Checks if the property exists on the target object
197
+ 2. If it's a numeric property, calls the appropriate accessor function
198
+ 3. If it's a regular property, delegates to the original object
199
+ 4. Handles property setting with similar logic
200
+
201
+ ### Prototype Chain Management
202
+
203
+ The utility carefully manages the prototype chain to ensure:
204
+ - Methods from the base class are accessible
205
+ - The proxy intercepts numeric property access
206
+ - Regular property access works normally
207
+ - Inheritance works correctly
208
+
209
+ ### Property Access Interception
210
+
211
+ ```typescript
212
+ // Simplified version of the proxy logic
213
+ new Proxy(base.prototype, {
214
+ get(target, prop, receiver) {
215
+ if (typeof prop === 'string') {
216
+ const numProp = Number(prop);
217
+ if (!Number.isNaN(numProp)) {
218
+ return accessor.get.call(receiver, numProp);
219
+ }
220
+ }
221
+ return target[prop];
222
+ }
223
+ })
224
+ ```
225
+
226
+ ### Error Handling
227
+
228
+ The utility provides clear error messages for common issues:
229
+ - Missing `getAt` method: "Indexable class must have an [getAt] method"
230
+ - Read-only access: "Indexable class has read-only numeric index access"
231
+
232
+ ## 5. Usage Examples
233
+
234
+ ### Basic Usage
235
+
236
+ ```typescript
237
+ // Create a simple indexable class
238
+ const SimpleIndexable = Indexable({
239
+ get(this: any, index: number) {
240
+ return `Value at index ${index}`;
241
+ }
242
+ });
243
+
244
+ const obj = new SimpleIndexable();
245
+ console.log(obj[0]); // "Value at index 0"
246
+ console.log(obj[42]); // "Value at index 42"
247
+ ```
248
+
249
+ ### Custom Accessor Functions
250
+
251
+ ```typescript
252
+ // Create an indexable class with custom logic
253
+ class DataWrapper {
254
+ constructor(private data: any[]) {}
255
+ }
256
+
257
+ const IndexableWrapper = Indexable(DataWrapper, {
258
+ get(this: DataWrapper, index: number) {
259
+ const value = this.data[index];
260
+ return value ? value.toUpperCase() : 'NOT_FOUND';
261
+ },
262
+ set(this: DataWrapper, index: number, value: any) {
263
+ this.data[index] = value.toLowerCase();
264
+ }
265
+ });
266
+
267
+ const wrapper = new IndexableWrapper(['hello', 'world']);
268
+ console.log(wrapper[0]); // "HELLO"
269
+ wrapper[1] = 'UNIVERSE';
270
+ console.log(wrapper[1]); // "universe"
271
+ ```
272
+
273
+ ### Extending Existing Classes
274
+
275
+ ```typescript
276
+ // Extend a built-in class
277
+ class CustomArray extends Array {
278
+ constructor(...items: number[]) {
279
+ super(...items);
280
+ }
281
+ }
282
+
283
+ const IndexableCustomArray = Indexable(CustomArray, {
284
+ get(this: CustomArray, index: number) {
285
+ const value = super[index];
286
+ return value ? value * 2 : 0;
287
+ }
288
+ });
289
+
290
+ const arr = new IndexableCustomArray(1, 2, 3);
291
+ console.log(arr[0]); // 2 (1 * 2)
292
+ console.log(arr[1]); // 4 (2 * 2)
293
+ ```
294
+
295
+ ### Read-Only Indexable Classes
296
+
297
+ ```typescript
298
+ // Create a read-only indexable class
299
+ const ReadOnlyIndexable = Indexable({
300
+ get(this: any, index: number) {
301
+ return `Read-only value at ${index}`;
302
+ }
303
+ // No set method = read-only
304
+ });
305
+
306
+ const readOnly = new ReadOnlyIndexable();
307
+ console.log(readOnly[0]); // "Read-only value at 0"
308
+ // readOnly[0] = "new value"; // Error: read-only access
309
+ ```
310
+
311
+ ### Abstract Indexable Classes
312
+
313
+ ```typescript
314
+ // Create an abstract indexable class
315
+ const AbstractStringIndexable = Indexable<string>();
316
+
317
+ class StringCollection extends AbstractStringIndexable {
318
+ private items: string[] = [];
319
+
320
+ [getAt](index: number): string {
321
+ return this.items[index] || '';
322
+ }
323
+
324
+ add(item: string) {
325
+ this.items.push(item);
326
+ }
327
+ }
328
+
329
+ const collection = new StringCollection();
330
+ collection.add('first');
331
+ collection.add('second');
332
+ console.log(collection[0]); // "first"
333
+ console.log(collection[1]); // "second"
334
+ ```
335
+
336
+ ## 6. Advanced Patterns
337
+
338
+ ### Combining with Other Decorators
339
+
340
+ ```typescript
341
+ // Combine with class decorators
342
+ function Logged(target: any) {
343
+ return class extends target {
344
+ constructor(...args: any[]) {
345
+ console.log(`Creating ${target.name}`);
346
+ super(...args);
347
+ }
348
+ };
349
+ }
350
+
351
+ const LoggedIndexable = Logged(Indexable({
352
+ get(this: any, index: number) {
353
+ return `Item ${index}`;
354
+ }
355
+ }));
356
+ ```
357
+
358
+ ### Performance Considerations
359
+
360
+ - **Proxy Overhead**: Each property access goes through the proxy, adding minimal overhead
361
+ - **Memory Usage**: Proxy objects use slightly more memory than regular objects
362
+ - **Caching**: Consider caching frequently accessed values if performance is critical
363
+ - **Batch Operations**: For bulk operations, access the underlying data directly
364
+
365
+ ### Memory Management
366
+
367
+ - **Weak References**: Consider using WeakMap/WeakSet for storing references
368
+ - **Cleanup**: Implement cleanup methods if your accessor functions create closures
369
+ - **Circular References**: Be careful to avoid circular references in accessor functions
370
+
371
+ ## 7. Troubleshooting
372
+
373
+ ### Common Issues
374
+
375
+ **TypeScript Errors:**
376
+ ```typescript
377
+ // Error: Property '0' does not exist on type 'MyClass'
378
+ // Solution: Ensure the class is properly typed with Indexable
379
+ const MyIndexableClass = Indexable(MyClass, accessor);
380
+ ```
381
+
382
+ **Runtime Errors:**
383
+ ```typescript
384
+ // Error: Indexable class must have an [getAt] method
385
+ // Solution: Implement the getAt method or provide an accessor function
386
+ ```
387
+
388
+ **Property Access Issues:**
389
+ ```typescript
390
+ // Error: Cannot set property '0' of read-only object
391
+ // Solution: Implement the setAt method or provide a set accessor
392
+ ```
393
+
394
+ ### Debugging Tips
395
+
396
+ 1. **Check Accessor Functions**: Ensure your accessor functions are properly defined
397
+ 2. **Verify Base Class**: Make sure the base class is compatible with Indexable
398
+ 3. **Type Checking**: Use TypeScript's strict mode to catch type errors early
399
+ 4. **Console Logging**: Add logging to accessor functions to debug access patterns
400
+
401
+ ### Performance Issues
402
+
403
+ - **Profile Property Access**: Use browser dev tools to profile property access
404
+ - **Optimize Accessors**: Keep accessor functions lightweight
405
+ - **Consider Alternatives**: For high-performance scenarios, consider direct property access
406
+
407
+ ## 8. Best Practices
408
+
409
+ ### When to Use Indexable
410
+
411
+ **Use Indexable when:**
412
+ - You need numeric index access on custom classes
413
+ - You want to customize how values are retrieved/stored
414
+ - You're building data structure libraries
415
+ - You need type-safe index access
416
+
417
+ **Consider alternatives when:**
418
+ - Performance is critical and proxy overhead is unacceptable
419
+ - You only need simple array-like behavior (use Array directly)
420
+ - You're working with existing code that doesn't support proxies
421
+
422
+ ### Design Patterns
423
+
424
+ 1. **Accessor Pattern**: Separate access logic from data storage
425
+ 2. **Proxy Pattern**: Use proxies for cross-cutting concerns
426
+ 3. **Factory Pattern**: Create indexable classes dynamically
427
+ 4. **Template Method**: Define abstract behavior with concrete implementations
428
+
429
+ ### Testing Strategies
430
+
431
+ ```typescript
432
+ // Test index access
433
+ describe('IndexableClass', () => {
434
+ it('should access values by index', () => {
435
+ const instance = new IndexableClass();
436
+ expect(instance[0]).toBe('expected value');
437
+ });
438
+
439
+ it('should handle out-of-bounds access', () => {
440
+ const instance = new IndexableClass();
441
+ expect(instance[999]).toBe('default value');
442
+ });
443
+
444
+ it('should set values by index', () => {
445
+ const instance = new IndexableClass();
446
+ instance[0] = 'new value';
447
+ expect(instance[0]).toBe('new value');
448
+ });
449
+ });
450
+ ```
451
+
452
+ ## 9. Migration Guide
453
+
454
+ ### From Manual Index Access
455
+
456
+ **Before:**
457
+ ```typescript
458
+ class MyClass {
459
+ getValue(index: number) {
460
+ return this.data[index];
461
+ }
462
+
463
+ setValue(index: number, value: any) {
464
+ this.data[index] = value;
465
+ }
466
+ }
467
+
468
+ const obj = new MyClass();
469
+ const value = obj.getValue(0);
470
+ obj.setValue(1, 'new value');
471
+ ```
472
+
473
+ **After:**
474
+ ```typescript
475
+ const IndexableMyClass = Indexable(MyClass, {
476
+ get(this: MyClass, index: number) {
477
+ return this.data[index];
478
+ },
479
+ set(this: MyClass, index: number, value: any) {
480
+ this.data[index] = value;
481
+ }
482
+ });
483
+
484
+ const obj = new IndexableMyClass();
485
+ const value = obj[0];
486
+ obj[1] = 'new value';
487
+ ```
488
+
489
+ ### From Array-Like Classes
490
+
491
+ **Before:**
492
+ ```typescript
493
+ class CustomArray {
494
+ private items: any[] = [];
495
+
496
+ get(index: number) {
497
+ return this.items[index];
498
+ }
499
+
500
+ set(index: number, value: any) {
501
+ this.items[index] = value;
502
+ }
503
+ }
504
+ ```
505
+
506
+ **After:**
507
+ ```typescript
508
+ const IndexableCustomArray = Indexable(CustomArray, {
509
+ get(this: CustomArray, index: number) {
510
+ return this.items[index];
511
+ },
512
+ set(this: CustomArray, index: number, value: any) {
513
+ this.items[index] = value;
514
+ }
515
+ });
516
+ ```
517
+
518
+ ### Breaking Changes
519
+
520
+ - **Proxy Support**: Requires ES6+ environments with Proxy support
521
+ - **Type Safety**: May require TypeScript configuration updates
522
+ - **Performance**: Introduces proxy overhead for property access
523
+
524
+ ## 10. Appendix
525
+
526
+ ### TypeScript Configuration
527
+
528
+ ```json
529
+ {
530
+ "compilerOptions": {
531
+ "target": "ES6",
532
+ "lib": ["ES6", "DOM"],
533
+ "strict": true,
534
+ "experimentalDecorators": true
535
+ }
536
+ }
537
+ ```
538
+
539
+ ### Browser Compatibility
540
+
541
+ - **ES6+**: Full support in modern browsers
542
+ - **IE11**: No support (no Proxy API)
543
+ - **Node.js**: 6.0+ for full support, 0.12+ with polyfills
544
+
545
+ ### Related Utilities
546
+
547
+ - **Proxy**: JavaScript's built-in proxy API
548
+ - **Symbol**: Used for custom property keys
549
+ - **Object.setPrototypeOf**: For prototype chain manipulation
550
+
551
+ ### Changelog
552
+
553
+ **v1.0.0**
554
+ - Initial release
555
+ - Support for all four function overloads
556
+ - Full TypeScript support
557
+ - Proxy-based implementation
558
+
559
+ ---
560
+
561
+ This documentation provides a comprehensive guide to using the Indexable utility. The utility is particularly powerful for creating custom data structures that need numeric index access while maintaining full control over the access patterns and maintaining type safety.