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,352 @@
1
+ import { Indexable } from '../indexable'
2
+ import {
3
+ dependant,
4
+ makeReactiveEntriesIterator,
5
+ makeReactiveIterator,
6
+ prototypeForwarding,
7
+ reactive,
8
+ touched,
9
+ } from './core'
10
+
11
+ const native = Symbol('native')
12
+ const isArray = Array.isArray
13
+ Array.isArray = ((value: any) =>
14
+ // biome-ignore lint/suspicious/useIsArray: We are defining it
15
+ isArray(value) || (value instanceof Array && native in value)) as any
16
+ class ReactiveBaseArray {
17
+ declare readonly [native]: any[]
18
+ }
19
+ function* index(i: number, { length = true } = {}): IterableIterator<number | 'length'> {
20
+ yield i
21
+ if (length) yield 'length'
22
+ }
23
+
24
+ function* range(
25
+ a: number,
26
+ b: number,
27
+ { length = false } = {}
28
+ ): IterableIterator<number | 'length'> {
29
+ const start = Math.min(a, b)
30
+ const end = Math.max(a, b)
31
+ for (let i = start; i <= end; i++) yield i
32
+ if (length) yield 'length'
33
+ }
34
+ export class ReactiveArray extends Indexable(ReactiveBaseArray, {
35
+ get(i: number): any {
36
+ dependant(this[native], i)
37
+ return reactive(this[native][i])
38
+ },
39
+ set(i: number, value: any) {
40
+ const added = i >= this[native].length
41
+ this[native][i] = value
42
+ touched(this[native], { type: 'bunch', method: 'set' }, index(i, { length: added }))
43
+ },
44
+ getLength() {
45
+ dependant(this[native], 'length')
46
+ return this[native].length
47
+ },
48
+ setLength(value: number) {
49
+ const oldLength = this[native].length
50
+ try {
51
+ this[native].length = value
52
+ } finally {
53
+ touched(
54
+ this[native],
55
+ { type: 'set', prop: 'length' },
56
+ range(oldLength, value, { length: true })
57
+ )
58
+ }
59
+ },
60
+ }) {
61
+ declare length: number
62
+ constructor(original: any[]) {
63
+ super()
64
+ Object.defineProperties(this, {
65
+ // We have to make it double, as [native] must be `unique symbol` - impossible through import
66
+ [native]: { value: original },
67
+ [prototypeForwarding]: { value: original },
68
+ })
69
+ }
70
+
71
+ // Safe array access with negative indices
72
+ at(index: number): any {
73
+ const actualIndex = index < 0 ? this[native].length + index : index
74
+ dependant(this, actualIndex)
75
+ if (actualIndex < 0 || actualIndex >= this[native].length) return undefined
76
+ return reactive(this[native][actualIndex])
77
+ }
78
+
79
+ push(...items: any[]) {
80
+ const oldLength = this[native].length
81
+ try {
82
+ return this[native].push(...items)
83
+ } finally {
84
+ touched(
85
+ this,
86
+ { type: 'bunch', method: 'push' },
87
+ range(oldLength, oldLength + items.length - 1, { length: true })
88
+ )
89
+ }
90
+ }
91
+
92
+ pop() {
93
+ if (this[native].length === 0) return undefined
94
+ try {
95
+ return reactive(this[native].pop())
96
+ } finally {
97
+ touched(this, { type: 'bunch', method: 'pop' }, index(this[native].length))
98
+ }
99
+ }
100
+
101
+ shift() {
102
+ if (this[native].length === 0) return undefined
103
+ try {
104
+ return reactive(this[native].shift())
105
+ } finally {
106
+ touched(
107
+ this,
108
+ { type: 'bunch', method: 'shift' },
109
+ range(0, this[native].length + 1, { length: true })
110
+ )
111
+ }
112
+ }
113
+
114
+ unshift(...items: any[]) {
115
+ try {
116
+ return this[native].unshift(...items)
117
+ } finally {
118
+ touched(
119
+ this,
120
+ { type: 'bunch', method: 'unshift' },
121
+ range(0, this[native].length - items.length, { length: true })
122
+ )
123
+ }
124
+ }
125
+
126
+ splice(start: number, deleteCount?: number, ...items: any[]) {
127
+ const oldLength = this[native].length
128
+ if (deleteCount === undefined) deleteCount = oldLength - start
129
+ try {
130
+ if (deleteCount === undefined) return reactive(this[native].splice(start))
131
+ return reactive(this[native].splice(start, deleteCount, ...items))
132
+ } finally {
133
+ touched(
134
+ this,
135
+ { type: 'bunch', method: 'splice' },
136
+ // TODO: edge cases
137
+ deleteCount === items.length
138
+ ? range(start, start + deleteCount)
139
+ : range(start, oldLength + Math.max(items.length - deleteCount, 0), {
140
+ length: true,
141
+ })
142
+ )
143
+ }
144
+ }
145
+
146
+ reverse() {
147
+ try {
148
+ return this[native].reverse()
149
+ } finally {
150
+ touched(this, { type: 'bunch', method: 'reverse' }, range(0, this[native].length - 1))
151
+ }
152
+ }
153
+
154
+ sort(compareFn?: (a: any, b: any) => number) {
155
+ try {
156
+ return this[native].sort(compareFn) as any
157
+ } finally {
158
+ touched(this, { type: 'bunch', method: 'sort' }, range(0, this[native].length - 1))
159
+ }
160
+ }
161
+
162
+ fill(value: any, start?: number, end?: number) {
163
+ try {
164
+ if (start === undefined) return this[native].fill(value) as any
165
+ if (end === undefined) return this[native].fill(value, start) as any
166
+ return this[native].fill(value, start, end) as any
167
+ } finally {
168
+ touched(this, { type: 'bunch', method: 'fill' }, range(0, this[native].length - 1))
169
+ }
170
+ }
171
+
172
+ copyWithin(target: number, start: number, end?: number) {
173
+ try {
174
+ if (end === undefined) return this[native].copyWithin(target, start) as any
175
+ return this[native].copyWithin(target, start, end) as any
176
+ } finally {
177
+ touched(
178
+ this,
179
+ { type: 'bunch', method: 'copyWithin' },
180
+ // TODO: calculate the range properly
181
+ range(0, this[native].length - 1)
182
+ )
183
+ }
184
+ // Touch all affected indices with a single allProps call
185
+ }
186
+
187
+ // Immutable versions of mutator methods
188
+ toReversed(): any[] {
189
+ dependant(this)
190
+ return reactive(this[native].toReversed())
191
+ }
192
+
193
+ toSorted(compareFn?: (a: any, b: any) => number): any[] {
194
+ dependant(this)
195
+ return reactive(this[native].toSorted(compareFn))
196
+ }
197
+
198
+ toSpliced(start: number, deleteCount?: number, ...items: any[]): any[] {
199
+ dependant(this)
200
+ return deleteCount === undefined
201
+ ? this[native].toSpliced(start)
202
+ : this[native].toSpliced(start, deleteCount, ...items)
203
+ }
204
+
205
+ with(index: number, value: any): any[] {
206
+ dependant(this)
207
+ return reactive(this[native].with(index, value))
208
+ }
209
+
210
+ // Iterator methods with reactivity tracking
211
+ entries() {
212
+ dependant(this)
213
+ return makeReactiveEntriesIterator(this[native].entries())
214
+ }
215
+
216
+ keys() {
217
+ dependant(this)
218
+ return this[native].keys()
219
+ }
220
+
221
+ values() {
222
+ dependant(this)
223
+ return makeReactiveIterator(this[native].values())
224
+ }
225
+
226
+ [Symbol.iterator]() {
227
+ dependant(this)
228
+ const nativeIterator = this[native][Symbol.iterator]()
229
+ return {
230
+ next() {
231
+ const result = nativeIterator.next()
232
+ if (result.done) {
233
+ return result
234
+ }
235
+ return { value: reactive(result.value), done: false }
236
+ },
237
+ }
238
+ }
239
+
240
+ indexOf(searchElement: any, fromIndex?: number): number {
241
+ dependant(this)
242
+ return this[native].indexOf(searchElement, fromIndex)
243
+ }
244
+
245
+ lastIndexOf(searchElement: any, fromIndex?: number): number {
246
+ dependant(this)
247
+ return this[native].lastIndexOf(searchElement, fromIndex)
248
+ }
249
+
250
+ includes(searchElement: any, fromIndex?: number): boolean {
251
+ dependant(this)
252
+ return this[native].includes(searchElement, fromIndex)
253
+ }
254
+
255
+ find(
256
+ predicate: (this: any, value: any, index: number, obj: any[]) => boolean,
257
+ thisArg?: any
258
+ ): any {
259
+ dependant(this)
260
+ return reactive(this[native].find(predicate, thisArg))
261
+ }
262
+
263
+ findIndex(
264
+ predicate: (this: any, value: any, index: number, obj: any[]) => boolean,
265
+ thisArg?: any
266
+ ): number {
267
+ dependant(this)
268
+ return this[native].findIndex(predicate, thisArg)
269
+ }
270
+
271
+ flat(): any[] {
272
+ dependant(this)
273
+ return reactive(this[native].flat())
274
+ }
275
+
276
+ flatMap(
277
+ callbackfn: (this: any, value: any, index: number, array: any[]) => any[],
278
+ thisArg?: any
279
+ ): any[] {
280
+ dependant(this)
281
+ return reactive(this[native].flatMap(callbackfn, thisArg))
282
+ }
283
+
284
+ filter(callbackfn: (value: any, index: number, array: any[]) => boolean, thisArg?: any): any[] {
285
+ dependant(this)
286
+ return reactive(this[native].filter(callbackfn as any, thisArg))
287
+ }
288
+
289
+ map(callbackfn: (value: any, index: number, array: any[]) => any, thisArg?: any): any[] {
290
+ dependant(this)
291
+ return reactive(this[native].map(callbackfn as any, thisArg))
292
+ }
293
+
294
+ reduce(
295
+ callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any,
296
+ initialValue?: any
297
+ ): any {
298
+ dependant(this)
299
+ const result =
300
+ initialValue === undefined
301
+ ? this[native].reduce(callbackfn as any)
302
+ : this[native].reduce(callbackfn as any, initialValue)
303
+ return reactive(result)
304
+ }
305
+
306
+ reduceRight(
307
+ callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any,
308
+ initialValue?: any
309
+ ): any {
310
+ dependant(this)
311
+ const result =
312
+ initialValue !== undefined
313
+ ? this[native].reduceRight(callbackfn as any, initialValue)
314
+ : (this[native] as any).reduceRight(callbackfn as any)
315
+ return reactive(result)
316
+ }
317
+
318
+ slice(start?: number, end?: number): any[] {
319
+ for (const i of range(start || 0, end || this[native].length - 1)) dependant(this, i)
320
+ return start === undefined
321
+ ? this[native].slice()
322
+ : end === undefined
323
+ ? this[native].slice(start)
324
+ : this[native].slice(start, end)
325
+ }
326
+
327
+ concat(...items: any[]): any[] {
328
+ dependant(this)
329
+ return reactive(this[native].concat(...items))
330
+ }
331
+
332
+ join(separator?: string): string {
333
+ dependant(this)
334
+ return this[native].join(separator as any)
335
+ }
336
+
337
+ forEach(callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any): void {
338
+ dependant(this)
339
+ this[native].forEach(callbackfn as any, thisArg)
340
+ }
341
+
342
+ // TODO: re-implement for fun dependencies? (eg - every only check the first ones until it find some)
343
+ every(callbackfn: (value: any, index: number, array: any[]) => boolean, thisArg?: any): boolean {
344
+ dependant(this)
345
+ return this[native].every(callbackfn as any, thisArg)
346
+ }
347
+
348
+ some(callbackfn: (value: any, index: number, array: any[]) => boolean, thisArg?: any): boolean {
349
+ dependant(this)
350
+ return this[native].some(callbackfn as any, thisArg)
351
+ }
352
+ }