mutts 1.0.6 → 1.0.8

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 (196) hide show
  1. package/README.md +61 -23
  2. package/dist/async/browser.d.ts +2 -0
  3. package/dist/async/browser.d.ts.map +1 -0
  4. package/dist/async/index.d.ts +18 -0
  5. package/dist/async/index.d.ts.map +1 -0
  6. package/dist/async/node.d.ts +2 -0
  7. package/dist/async/node.d.ts.map +1 -0
  8. package/dist/{chunks/index-CDCOjzTy.js → browser.cjs} +5913 -4382
  9. package/dist/browser.cjs.map +1 -0
  10. package/dist/browser.d.ts +1655 -0
  11. package/dist/browser.esm.js +305 -0
  12. package/dist/browser.esm.js.map +1 -0
  13. package/dist/chunks/async-browser-CA0jPWIi.cjs +304 -0
  14. package/dist/chunks/async-browser-CA0jPWIi.cjs.map +1 -0
  15. package/dist/chunks/async-core-UqHzvJ-S.cjs +25 -0
  16. package/dist/chunks/async-core-UqHzvJ-S.cjs.map +1 -0
  17. package/dist/chunks/async-node-BYHuGTni.cjs +103 -0
  18. package/dist/chunks/async-node-BYHuGTni.cjs.map +1 -0
  19. package/dist/chunks/{index-DiP0RXoZ.esm.js → index-DhaOVusv.esm.js} +5851 -4345
  20. package/dist/chunks/index-DhaOVusv.esm.js.map +1 -0
  21. package/dist/decorator.d.ts +17 -18
  22. package/dist/decorator.d.ts.map +1 -0
  23. package/dist/destroyable.d.ts +12 -15
  24. package/dist/destroyable.d.ts.map +1 -0
  25. package/dist/devtools/devtool/devtools.d.ts +1 -0
  26. package/dist/devtools/devtool/devtools.d.ts.map +1 -0
  27. package/dist/devtools/devtool/panel.d.ts +2 -0
  28. package/dist/devtools/devtool/panel.d.ts.map +1 -0
  29. package/dist/devtools/panel.js.map +1 -1
  30. package/dist/entry-browser.d.ts +3 -0
  31. package/dist/entry-browser.d.ts.map +1 -0
  32. package/dist/entry-node.d.ts +3 -0
  33. package/dist/entry-node.d.ts.map +1 -0
  34. package/dist/eventful.d.ts +3 -5
  35. package/dist/eventful.d.ts.map +1 -0
  36. package/dist/index.d.ts +13 -19
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/indexable.d.ts +10 -10
  39. package/dist/indexable.d.ts.map +1 -0
  40. package/dist/introspection.d.ts +27 -0
  41. package/dist/introspection.d.ts.map +1 -0
  42. package/dist/iterableWeak.d.ts +53 -0
  43. package/dist/iterableWeak.d.ts.map +1 -0
  44. package/dist/mixins.d.ts +25 -0
  45. package/dist/mixins.d.ts.map +1 -0
  46. package/dist/mutts.umd.js +1 -1
  47. package/dist/mutts.umd.js.map +1 -1
  48. package/dist/mutts.umd.min.js +1 -1
  49. package/dist/mutts.umd.min.js.map +1 -1
  50. package/dist/node.cjs +105 -0
  51. package/dist/node.cjs.map +1 -0
  52. package/dist/node.d.ts +1 -0
  53. package/dist/node.esm.js +104 -0
  54. package/dist/node.esm.js.map +1 -0
  55. package/dist/promiseChain.d.ts +4 -5
  56. package/dist/promiseChain.d.ts.map +1 -0
  57. package/dist/reactive/array.d.ts +49 -0
  58. package/dist/reactive/array.d.ts.map +1 -0
  59. package/dist/reactive/buffer.d.ts +44 -0
  60. package/dist/reactive/buffer.d.ts.map +1 -0
  61. package/dist/reactive/change.d.ts +29 -0
  62. package/dist/reactive/change.d.ts.map +1 -0
  63. package/dist/reactive/debug.d.ts +111 -0
  64. package/dist/reactive/debug.d.ts.map +1 -0
  65. package/dist/reactive/deep-touch.d.ts +28 -0
  66. package/dist/reactive/deep-touch.d.ts.map +1 -0
  67. package/dist/reactive/deep-watch-state.d.ts +25 -0
  68. package/dist/reactive/deep-watch-state.d.ts.map +1 -0
  69. package/dist/reactive/deep-watch.d.ts +19 -0
  70. package/dist/reactive/deep-watch.d.ts.map +1 -0
  71. package/dist/reactive/effect-context.d.ts +7 -0
  72. package/dist/reactive/effect-context.d.ts.map +1 -0
  73. package/dist/reactive/effects.d.ts +151 -0
  74. package/dist/reactive/effects.d.ts.map +1 -0
  75. package/dist/reactive/index.d.ts +20 -0
  76. package/dist/reactive/index.d.ts.map +1 -0
  77. package/dist/reactive/interface.d.ts +64 -0
  78. package/dist/reactive/interface.d.ts.map +1 -0
  79. package/dist/reactive/map.d.ts +30 -0
  80. package/dist/reactive/map.d.ts.map +1 -0
  81. package/dist/reactive/memoize.d.ts +5 -0
  82. package/dist/reactive/memoize.d.ts.map +1 -0
  83. package/dist/reactive/non-reactive-state.d.ts +9 -0
  84. package/dist/reactive/non-reactive-state.d.ts.map +1 -0
  85. package/dist/reactive/non-reactive.d.ts +11 -0
  86. package/dist/reactive/non-reactive.d.ts.map +1 -0
  87. package/dist/reactive/project.d.ts +41 -0
  88. package/dist/reactive/project.d.ts.map +1 -0
  89. package/dist/reactive/proxy-state.d.ts +8 -0
  90. package/dist/reactive/proxy-state.d.ts.map +1 -0
  91. package/dist/reactive/proxy.d.ts +23 -0
  92. package/dist/reactive/proxy.d.ts.map +1 -0
  93. package/dist/reactive/record.d.ts +116 -0
  94. package/dist/reactive/record.d.ts.map +1 -0
  95. package/dist/reactive/register.d.ts +64 -0
  96. package/dist/reactive/register.d.ts.map +1 -0
  97. package/dist/reactive/registry.d.ts +20 -0
  98. package/dist/reactive/registry.d.ts.map +1 -0
  99. package/dist/reactive/set.d.ts +28 -0
  100. package/dist/reactive/set.d.ts.map +1 -0
  101. package/dist/reactive/tracking.d.ts +7 -0
  102. package/dist/reactive/tracking.d.ts.map +1 -0
  103. package/dist/reactive/types.d.ts +376 -0
  104. package/dist/reactive/types.d.ts.map +1 -0
  105. package/dist/std-decorators.d.ts +9 -11
  106. package/dist/std-decorators.d.ts.map +1 -0
  107. package/dist/utils.d.ts +49 -0
  108. package/dist/utils.d.ts.map +1 -0
  109. package/dist/zone.d.ts +40 -0
  110. package/dist/zone.d.ts.map +1 -0
  111. package/docs/ai/api-reference.md +0 -2
  112. package/docs/reactive/advanced.md +2 -5
  113. package/docs/reactive/collections.md +0 -125
  114. package/docs/reactive/core.md +27 -24
  115. package/docs/reactive/debugging.md +12 -2
  116. package/docs/reactive/project.md +1 -1
  117. package/docs/reactive/scan.md +78 -0
  118. package/docs/reactive.md +2 -1
  119. package/docs/std-decorators.md +69 -0
  120. package/docs/zone.md +95 -0
  121. package/package.json +67 -23
  122. package/src/async/browser.ts +319 -0
  123. package/src/async/index.ts +23 -0
  124. package/src/async/node.ts +104 -0
  125. package/src/decorator.ts +5 -1
  126. package/src/destroyable.ts +1 -1
  127. package/src/entry-browser.ts +5 -0
  128. package/src/entry-node.ts +5 -0
  129. package/src/index.d.ts +12 -9
  130. package/src/index.ts +23 -14
  131. package/src/indexable.ts +42 -0
  132. package/src/mixins.ts +2 -2
  133. package/src/reactive/array.ts +274 -179
  134. package/src/reactive/buffer.ts +168 -0
  135. package/src/reactive/change.ts +2 -2
  136. package/src/reactive/effect-context.ts +15 -91
  137. package/src/reactive/effects.ts +119 -179
  138. package/src/reactive/index.ts +11 -13
  139. package/src/reactive/interface.ts +19 -33
  140. package/src/reactive/map.ts +49 -62
  141. package/src/reactive/memoize.ts +19 -9
  142. package/src/reactive/project.ts +43 -22
  143. package/src/reactive/proxy.ts +16 -41
  144. package/src/reactive/record.ts +3 -3
  145. package/src/reactive/register.ts +5 -7
  146. package/src/reactive/registry.ts +9 -17
  147. package/src/reactive/set.ts +43 -57
  148. package/src/reactive/tracking.ts +1 -29
  149. package/src/reactive/types.ts +46 -23
  150. package/src/utils.ts +80 -37
  151. package/src/zone.ts +138 -0
  152. package/dist/chunks/_tslib-BgjropY9.js +0 -81
  153. package/dist/chunks/_tslib-BgjropY9.js.map +0 -1
  154. package/dist/chunks/_tslib-MCKDzsSq.esm.js +0 -75
  155. package/dist/chunks/_tslib-MCKDzsSq.esm.js.map +0 -1
  156. package/dist/chunks/decorator-BGILvPtN.esm.js +0 -627
  157. package/dist/chunks/decorator-BGILvPtN.esm.js.map +0 -1
  158. package/dist/chunks/decorator-BQ2eBTCj.js +0 -651
  159. package/dist/chunks/decorator-BQ2eBTCj.js.map +0 -1
  160. package/dist/chunks/index-CDCOjzTy.js.map +0 -1
  161. package/dist/chunks/index-DiP0RXoZ.esm.js.map +0 -1
  162. package/dist/decorator.esm.js +0 -2
  163. package/dist/decorator.esm.js.map +0 -1
  164. package/dist/decorator.js +0 -11
  165. package/dist/decorator.js.map +0 -1
  166. package/dist/destroyable.esm.js +0 -109
  167. package/dist/destroyable.esm.js.map +0 -1
  168. package/dist/destroyable.js +0 -116
  169. package/dist/destroyable.js.map +0 -1
  170. package/dist/eventful.esm.js +0 -66
  171. package/dist/eventful.esm.js.map +0 -1
  172. package/dist/eventful.js +0 -68
  173. package/dist/eventful.js.map +0 -1
  174. package/dist/index.esm.js +0 -53
  175. package/dist/index.esm.js.map +0 -1
  176. package/dist/index.js +0 -139
  177. package/dist/index.js.map +0 -1
  178. package/dist/indexable.esm.js +0 -285
  179. package/dist/indexable.esm.js.map +0 -1
  180. package/dist/indexable.js +0 -291
  181. package/dist/indexable.js.map +0 -1
  182. package/dist/promiseChain.esm.js +0 -78
  183. package/dist/promiseChain.esm.js.map +0 -1
  184. package/dist/promiseChain.js +0 -80
  185. package/dist/promiseChain.js.map +0 -1
  186. package/dist/reactive.d.ts +0 -910
  187. package/dist/reactive.esm.js +0 -5
  188. package/dist/reactive.esm.js.map +0 -1
  189. package/dist/reactive.js +0 -59
  190. package/dist/reactive.js.map +0 -1
  191. package/dist/std-decorators.esm.js +0 -196
  192. package/dist/std-decorators.esm.js.map +0 -1
  193. package/dist/std-decorators.js +0 -204
  194. package/dist/std-decorators.js.map +0 -1
  195. package/src/reactive/mapped.ts +0 -129
  196. package/src/reactive/zone.ts +0 -208
@@ -1,72 +1,126 @@
1
- import { Indexable } from '../indexable'
1
+ import { FoolProof } from '../utils'
2
2
  import { touched } from './change'
3
3
  import { makeReactiveEntriesIterator, makeReactiveIterator } from './non-reactive'
4
4
  import { reactive } from './proxy'
5
5
  import { unwrap } from './proxy-state'
6
6
  import { dependant } from './tracking'
7
- import { prototypeForwarding } from './types'
8
-
9
- export const native = Symbol('native')
10
- const isArray = Array.isArray
11
- Array.isArray = ((value: any) =>
12
- isArray(value) ||
13
- (value &&
14
- typeof value === 'object' &&
15
- prototypeForwarding in value &&
16
- Array.isArray(value[prototypeForwarding]))) as any
17
- export class ReactiveBaseArray {
18
- readonly [native]!: any[]
19
7
 
8
+ function* index(i: number, { length = true } = {}): IterableIterator<number | 'length'> {
9
+ if (length) yield 'length'
10
+ yield i
11
+ }
12
+
13
+ function* range(
14
+ a: number,
15
+ b: number,
16
+ { length = false } = {}
17
+ ): IterableIterator<number | 'length'> {
18
+ const start = Math.min(a, b)
19
+ const end = Math.max(a, b)
20
+ if (length) yield 'length'
21
+ for (let i = start; i <= end; i++) yield i
22
+ }
23
+ export abstract class Indexer extends Array {
24
+ get(i: number): any {
25
+ dependant(this, i)
26
+ return reactive(this[i])
27
+ }
28
+ set(i: number, value: any) {
29
+ const added = i >= this.length
30
+ this[i] = value
31
+ touched(this, { type: 'set', prop: i }, index(i, { length: added }))
32
+ }
33
+ getLength() {
34
+ dependant(this, 'length')
35
+ return this.length
36
+ }
37
+ setLength(value: number) {
38
+ const oldLength = this.length
39
+ try {
40
+ this.length = value
41
+ } finally {
42
+ touched(this, { type: 'set', prop: 'length' }, range(oldLength, value, { length: true }))
43
+ }
44
+ }
45
+ }
46
+ const indexLess = { get: FoolProof.get, set: FoolProof.set }
47
+ Object.assign(FoolProof, {
48
+ get(obj: any, prop: any, receiver: any) {
49
+ if (obj instanceof Array && typeof prop === 'string') {
50
+ if (prop === 'length') return Indexer.prototype.getLength.call(obj)
51
+ const index = parseInt(prop)
52
+ if (!Number.isNaN(index)) return Indexer.prototype.get.call(obj, index)
53
+ }
54
+ return indexLess.get(obj, prop, receiver)
55
+ },
56
+ set(obj: any, prop: any, value: any, receiver: any) {
57
+ if (obj instanceof Array && typeof prop === 'string') {
58
+ if (prop === 'length') return Indexer.prototype.setLength.call(obj, value)
59
+ const index = parseInt(prop)
60
+ if (!Number.isNaN(index)) return Indexer.prototype.set.call(obj, index, value)
61
+ }
62
+ return indexLess.set(obj, prop, value, receiver)
63
+ },
64
+ })
65
+
66
+ export abstract class ReactiveArray extends Array {
67
+ toJSON() {
68
+ return this
69
+ }
70
+ get [Symbol.toStringTag]() {
71
+ return 'ReactiveArray'
72
+ }
20
73
  // Safe array access with negative indices
21
74
  at(index: number): any {
22
- const actualIndex = index < 0 ? this[native].length + index : index
75
+ const actualIndex = index < 0 ? this.length + index : index
23
76
  dependant(this, actualIndex)
24
- if (actualIndex < 0 || actualIndex >= this[native].length) return undefined
25
- return reactive(this[native][actualIndex])
77
+ if (index < 0) dependant(this, 'length')
78
+ if (actualIndex < 0 || actualIndex >= this.length) return undefined
79
+ return reactive(this[actualIndex])
26
80
  }
27
81
 
28
82
  // Immutable versions of mutator methods
29
83
  toReversed(): any[] {
30
84
  dependant(this)
31
- return reactive(this[native].toReversed())
85
+ return reactive(this.toReversed())
32
86
  }
33
87
 
34
88
  toSorted(compareFn?: (a: any, b: any) => number): any[] {
35
89
  dependant(this)
36
- return reactive(this[native].toSorted(compareFn))
90
+ return reactive(this.toSorted(compareFn))
37
91
  }
38
92
 
39
93
  toSpliced(start: number, deleteCount?: number, ...items: any[]): any[] {
40
94
  dependant(this)
41
95
  return deleteCount === undefined
42
- ? this[native].toSpliced(start)
43
- : this[native].toSpliced(start, deleteCount, ...items)
96
+ ? this.toSpliced(start)
97
+ : this.toSpliced(start, deleteCount, ...items)
44
98
  }
45
99
 
46
100
  with(index: number, value: any): any[] {
47
101
  dependant(this)
48
- return reactive(this[native].with(index, value))
102
+ return reactive(this.with(index, value))
49
103
  }
50
104
 
51
105
  // Iterator methods with reactivity tracking
52
106
  entries() {
53
107
  dependant(this)
54
- return makeReactiveEntriesIterator(this[native].entries())
108
+ return makeReactiveEntriesIterator(this.entries())
55
109
  }
56
110
 
57
111
  keys() {
58
112
  dependant(this, 'length')
59
- return this[native].keys()
113
+ return this.keys()
60
114
  }
61
115
 
62
116
  values() {
63
117
  dependant(this)
64
- return makeReactiveIterator(this[native].values())
118
+ return makeReactiveIterator(this.values())
65
119
  }
66
120
 
67
- [Symbol.iterator]() {
121
+ [Symbol.iterator](): ArrayIterator<any> {
68
122
  dependant(this)
69
- const nativeIterator = this[native][Symbol.iterator]()
123
+ const nativeIterator = this[Symbol.iterator]()
70
124
  return {
71
125
  next() {
72
126
  const result = nativeIterator.next()
@@ -75,43 +129,65 @@ export class ReactiveBaseArray {
75
129
  }
76
130
  return { value: reactive(result.value), done: false }
77
131
  },
78
- }
132
+ [Symbol.iterator]() {
133
+ return this
134
+ },
135
+ [Symbol.dispose]() {},
136
+ } as any
79
137
  }
80
138
 
81
139
  indexOf(searchElement: any, fromIndex?: number): number {
82
- dependant(this)
140
+ const length = this.length
141
+ let i = fromIndex === undefined ? 0 : fromIndex
142
+ if (i < 0) i = Math.max(length + i, 0)
143
+
83
144
  const unwrappedSearch = unwrap(searchElement)
84
- // Check both wrapped and unwrapped versions since array may contain either
85
- const index = this[native].indexOf(unwrappedSearch, fromIndex)
86
- if (index !== -1) return index
87
- // If not found with unwrapped, try with wrapped (in case array contains wrapped version)
88
- return this[native].indexOf(searchElement, fromIndex)
145
+
146
+ for (; i < length; i++) {
147
+ dependant(this, i)
148
+ const item = this[i]
149
+ if (item === searchElement || item === unwrappedSearch || unwrap(item) === unwrappedSearch) {
150
+ return i
151
+ }
152
+ }
153
+
154
+ dependant(this, 'length')
155
+ return -1
89
156
  }
90
157
 
91
158
  lastIndexOf(searchElement: any, fromIndex?: number): number {
92
- dependant(this)
159
+ const length = this.length
160
+ let i = fromIndex === undefined ? length - 1 : fromIndex
161
+ if (i >= length) i = length - 1
162
+ if (i < 0) i = Math.max(length + i, -1) // -1 ensures loop condition i >= 0 works correctly
163
+
93
164
  const unwrappedSearch = unwrap(searchElement)
94
- // Check both wrapped and unwrapped versions since array may contain either
95
- const index = this[native].lastIndexOf(unwrappedSearch, fromIndex)
96
- if (index !== -1) return index
97
- // If not found with unwrapped, try with wrapped (in case array contains wrapped version)
98
- return this[native].lastIndexOf(searchElement, fromIndex)
165
+
166
+ for (; i >= 0; i--) {
167
+ dependant(this, i)
168
+ const item = this[i]
169
+ if (item === searchElement || item === unwrappedSearch || unwrap(item) === unwrappedSearch) {
170
+ return i
171
+ }
172
+ }
173
+
174
+ // If we scanned the whole relevant part and didn't find it, we depend on length
175
+ // (because adding elements might shift indices or add the element)
176
+ // Actually for lastIndexOf, if we start from end, length dependency is implicit in the start index calculation?
177
+ // But if we return -1, it means we didn't find it.
178
+ // If we push an element, should lastIndexOf update?
179
+ // Yes, if the new element is the one we are looking for.
180
+ dependant(this, 'length')
181
+ return -1
99
182
  }
100
183
 
101
184
  includes(searchElement: any, fromIndex?: number): boolean {
102
- dependant(this)
103
- const unwrappedSearch = unwrap(searchElement)
104
- // Check both wrapped and unwrapped versions since array may contain either
105
- return (
106
- this[native].includes(unwrappedSearch, fromIndex) ||
107
- this[native].includes(searchElement, fromIndex)
108
- )
185
+ return this.indexOf(searchElement, fromIndex) !== -1
109
186
  }
110
187
 
111
188
  find(predicate: (this: any, value: any, index: number, obj: any[]) => boolean, thisArg?: any): any
112
189
  find(searchElement: any, fromIndex?: number): any
113
190
  find(predicateOrElement: any, thisArg?: any): any {
114
- dependant(this)
115
191
  if (typeof predicateOrElement === 'function') {
116
192
  const predicate = predicateOrElement as (
117
193
  this: any,
@@ -119,17 +195,23 @@ export class ReactiveBaseArray {
119
195
  index: number,
120
196
  obj: any[]
121
197
  ) => boolean
122
- return reactive(
123
- this[native].find(
124
- (value, index, array) => predicate.call(thisArg, reactive(value), index, array),
125
- thisArg
126
- )
127
- )
198
+ const length = this.length
199
+
200
+ for (let i = 0; i < length; i++) {
201
+ dependant(this, i)
202
+ const val = reactive(this[i])
203
+ if (predicate.call(thisArg, val, i, this)) {
204
+ return val
205
+ }
206
+ }
207
+
208
+ dependant(this, 'length')
209
+ return undefined
128
210
  }
129
211
  const fromIndex = typeof thisArg === 'number' ? thisArg : undefined
130
- const index = this[native].indexOf(predicateOrElement, fromIndex)
212
+ const index = this.indexOf(predicateOrElement, fromIndex)
131
213
  if (index === -1) return undefined
132
- return reactive(this[native][index])
214
+ return reactive(this[index])
133
215
  }
134
216
 
135
217
  findIndex(
@@ -138,7 +220,6 @@ export class ReactiveBaseArray {
138
220
  ): number
139
221
  findIndex(searchElement: any, fromIndex?: number): number
140
222
  findIndex(predicateOrElement: any, thisArg?: any): number {
141
- dependant(this)
142
223
  if (typeof predicateOrElement === 'function') {
143
224
  const predicate = predicateOrElement as (
144
225
  this: any,
@@ -146,18 +227,26 @@ export class ReactiveBaseArray {
146
227
  index: number,
147
228
  obj: any[]
148
229
  ) => boolean
149
- return this[native].findIndex(
150
- (value, index, array) => predicate.call(thisArg, reactive(value), index, array),
151
- thisArg
152
- )
230
+ const length = this.length
231
+
232
+ for (let i = 0; i < length; i++) {
233
+ dependant(this, i)
234
+ const val = reactive(this[i])
235
+ if (predicate.call(thisArg, val, i, this)) {
236
+ return i
237
+ }
238
+ }
239
+
240
+ dependant(this, 'length')
241
+ return -1
153
242
  }
154
243
  const fromIndex = typeof thisArg === 'number' ? thisArg : undefined
155
- return this[native].indexOf(predicateOrElement, fromIndex)
244
+ return this.indexOf(predicateOrElement, fromIndex)
156
245
  }
157
246
 
158
- flat(): any[] {
247
+ flat(depth?: number): any[] {
159
248
  dependant(this)
160
- return reactive(this[native].flat())
249
+ return reactive(depth === undefined ? this.flat() : this.flat(depth))
161
250
  }
162
251
 
163
252
  flatMap(
@@ -165,20 +254,25 @@ export class ReactiveBaseArray {
165
254
  thisArg?: any
166
255
  ): any[] {
167
256
  dependant(this)
168
- return reactive(this[native].flatMap(callbackfn, thisArg))
257
+ return reactive(
258
+ this.flatMap(
259
+ (item, index, array) => callbackfn.call(thisArg, reactive(item), index, array),
260
+ thisArg
261
+ )
262
+ )
169
263
  }
170
264
 
171
265
  filter(callbackfn: (value: any, index: number, array: any[]) => boolean, thisArg?: any): any[] {
172
266
  dependant(this)
173
267
  return reactive(
174
- this[native].filter((item, index, array) => callbackfn(reactive(item), index, array), thisArg)
268
+ this.filter((item, index, array) => callbackfn(reactive(item), index, array), thisArg)
175
269
  )
176
270
  }
177
271
 
178
272
  map(callbackfn: (value: any, index: number, array: any[]) => any, thisArg?: any): any[] {
179
273
  dependant(this)
180
274
  return reactive(
181
- this[native].map((item, index, array) => callbackfn(reactive(item), index, array), thisArg)
275
+ this.map((item, index, array) => callbackfn(reactive(item), index, array), thisArg)
182
276
  )
183
277
  }
184
278
 
@@ -189,8 +283,8 @@ export class ReactiveBaseArray {
189
283
  dependant(this)
190
284
  const result =
191
285
  initialValue === undefined
192
- ? this[native].reduce(callbackfn as any)
193
- : this[native].reduce(callbackfn as any, initialValue)
286
+ ? this.reduce(callbackfn as any)
287
+ : this.reduce(callbackfn as any, initialValue)
194
288
  return reactive(result)
195
289
  }
196
290
 
@@ -201,110 +295,75 @@ export class ReactiveBaseArray {
201
295
  dependant(this)
202
296
  const result =
203
297
  initialValue !== undefined
204
- ? this[native].reduceRight(callbackfn as any, initialValue)
205
- : (this[native] as any).reduceRight(callbackfn as any)
298
+ ? this.reduceRight(callbackfn as any, initialValue)
299
+ : (this as any).reduceRight(callbackfn as any)
206
300
  return reactive(result)
207
301
  }
208
302
 
209
303
  slice(start?: number, end?: number): any[] {
210
- for (const i of range(start || 0, end || this[native].length - 1)) dependant(this, i)
304
+ for (const i of range(start || 0, end || this.length - 1)) dependant(this, i)
211
305
  return start === undefined
212
- ? this[native].slice()
306
+ ? this.slice()
213
307
  : end === undefined
214
- ? this[native].slice(start)
215
- : this[native].slice(start, end)
308
+ ? this.slice(start)
309
+ : this.slice(start, end)
216
310
  }
217
311
 
218
312
  concat(...items: any[]): any[] {
219
313
  dependant(this)
220
- return reactive(this[native].concat(...items))
314
+ return reactive(this.concat(...items))
221
315
  }
222
316
 
223
317
  join(separator?: string): string {
224
318
  dependant(this)
225
- return this[native].join(separator as any)
319
+ return this.join(separator as any)
226
320
  }
227
321
 
228
322
  forEach(callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any): void {
229
323
  dependant(this)
230
- this[native].forEach((value, index, array) => {
324
+ this.forEach((value, index, array) => {
231
325
  callbackfn.call(thisArg, reactive(value), index, array)
232
326
  })
233
327
  }
234
328
 
235
- // TODO: re-implement for fun dependencies? (eg - every only check the first ones until it find some),
236
329
  // no need to make it dependant on indexes after the found one
330
+ every<S>(
331
+ predicate: (value: any, index: number, array: any[]) => value is S,
332
+ thisArg?: any
333
+ ): this is S[]
334
+ every(callbackfn: (value: any, index: number, array: any[]) => boolean, thisArg?: any): boolean
237
335
  every(callbackfn: (value: any, index: number, array: any[]) => boolean, thisArg?: any): boolean {
238
- dependant(this)
239
- return this[native].every(
240
- (value, index, array) => callbackfn.call(thisArg, reactive(value), index, array),
241
- thisArg
242
- )
336
+ const length = this.length
337
+
338
+ for (let i = 0; i < length; i++) {
339
+ dependant(this, i)
340
+ if (!callbackfn.call(thisArg, reactive(this[i]), i, this)) {
341
+ return false
342
+ }
343
+ }
344
+
345
+ dependant(this, 'length')
346
+ return true
243
347
  }
244
348
 
245
349
  some(callbackfn: (value: any, index: number, array: any[]) => boolean, thisArg?: any): boolean {
246
- dependant(this)
247
- return this[native].some(
248
- (value, index, array) => callbackfn.call(thisArg, reactive(value), index, array),
249
- thisArg
250
- )
251
- }
252
- }
253
- function* index(i: number, { length = true } = {}): IterableIterator<number | 'length'> {
254
- if (length) yield 'length'
255
- yield i
256
- }
350
+ const length = this.length
257
351
 
258
- function* range(
259
- a: number,
260
- b: number,
261
- { length = false } = {}
262
- ): IterableIterator<number | 'length'> {
263
- const start = Math.min(a, b)
264
- const end = Math.max(a, b)
265
- if (length) yield 'length'
266
- for (let i = start; i <= end; i++) yield i
267
- }
268
- /**
269
- * Reactive wrapper around JavaScript's Array class with full array method support
270
- * Tracks length changes, individual index operations, and collection-wide operations
271
- */
272
- export class ReactiveArray extends Indexable(ReactiveBaseArray, {
273
- get(i: number): any {
274
- dependant(this, i)
275
- return reactive(this[native][i])
276
- },
277
- set(i: number, value: any) {
278
- const added = i >= this[native].length
279
- this[native][i] = value
280
- touched(this, { type: 'set', prop: i }, index(i, { length: added }))
281
- },
282
- getLength() {
283
- dependant(this, 'length')
284
- return this[native].length
285
- },
286
- setLength(value: number) {
287
- const oldLength = this[native].length
288
- try {
289
- this[native].length = value
290
- } finally {
291
- touched(this, { type: 'set', prop: 'length' }, range(oldLength, value, { length: true }))
352
+ for (let i = 0; i < length; i++) {
353
+ dependant(this, i)
354
+ if (callbackfn.call(thisArg, reactive(this[i]), i, this)) {
355
+ return true
356
+ }
292
357
  }
293
- },
294
- }) {
295
- constructor(original: any[]) {
296
- super()
297
- Object.defineProperties(this, {
298
- // We have to make it double, as [native] must be `unique symbol` - impossible through import
299
- [native]: { value: original },
300
- [prototypeForwarding]: { value: original },
301
- })
302
- }
303
358
 
359
+ dependant(this, 'length')
360
+ return false
361
+ }
362
+ // Side-effectful
304
363
  push(...items: any[]) {
305
- const oldLength = this[native].length
364
+ const oldLength = this.length
306
365
  try {
307
- return this[native].push(...items)
366
+ return this.push(...items)
308
367
  } finally {
309
368
  touched(
310
369
  this,
@@ -315,98 +374,134 @@ export class ReactiveArray extends Indexable(ReactiveBaseArray, {
315
374
  }
316
375
 
317
376
  pop() {
318
- if (this[native].length === 0) return undefined
377
+ if (this.length === 0) return undefined
319
378
  try {
320
- return reactive(this[native].pop())
379
+ return reactive(this.pop())
321
380
  } finally {
322
- touched(this, { type: 'bunch', method: 'pop' }, index(this[native].length))
381
+ touched(this, { type: 'bunch', method: 'pop' }, index(this.length))
323
382
  }
324
383
  }
325
384
 
326
385
  shift() {
327
- if (this[native].length === 0) return undefined
386
+ if (this.length === 0) return undefined
328
387
  try {
329
- return reactive(this[native].shift())
388
+ return reactive(this.shift())
330
389
  } finally {
331
- touched(
332
- this,
333
- { type: 'bunch', method: 'shift' },
334
- range(0, this[native].length + 1, { length: true })
335
- )
390
+ touched(this, { type: 'bunch', method: 'shift' }, range(0, this.length + 1, { length: true }))
336
391
  }
337
392
  }
338
393
 
339
394
  unshift(...items: any[]) {
340
395
  try {
341
- return this[native].unshift(...items)
396
+ return this.unshift(...items)
342
397
  } finally {
343
398
  touched(
344
399
  this,
345
400
  { type: 'bunch', method: 'unshift' },
346
- range(0, this[native].length - items.length, { length: true })
401
+ range(0, this.length - items.length, { length: true })
347
402
  )
348
403
  }
349
404
  }
350
405
 
351
406
  splice(start: number, deleteCount?: number, ...items: any[]) {
352
- const oldLength = this[native].length
353
- if (deleteCount === undefined) deleteCount = oldLength - start
407
+ const oldLength = this.length
408
+
409
+ // Normalize start index
410
+ let actualStart = start
411
+ if (actualStart < 0) actualStart = Math.max(oldLength + actualStart, 0)
412
+ else actualStart = Math.min(actualStart, oldLength)
413
+
414
+ // Normalize deleteCount
415
+ let actualDeleteCount = deleteCount
416
+ if (actualDeleteCount === undefined) {
417
+ actualDeleteCount = oldLength - actualStart
418
+ } else {
419
+ actualDeleteCount = Math.max(0, Math.min(actualDeleteCount, oldLength - actualStart))
420
+ }
421
+
354
422
  try {
355
- if (deleteCount === undefined) return reactive(this[native].splice(start))
356
- return reactive(this[native].splice(start, deleteCount, ...items))
423
+ if (deleteCount === undefined) return reactive(this.splice(start))
424
+ return reactive(this.splice(start, deleteCount, ...items))
357
425
  } finally {
358
426
  touched(
359
427
  this,
360
428
  { type: 'bunch', method: 'splice' },
361
- // TODO: edge cases
362
- deleteCount === items.length
363
- ? range(start, start + deleteCount)
364
- : range(start, oldLength + Math.max(items.length - deleteCount, 0), {
365
- length: true,
366
- })
429
+ actualDeleteCount === items.length
430
+ ? range(actualStart, actualStart + actualDeleteCount - 1)
431
+ : range(actualStart, oldLength + Math.max(items.length - actualDeleteCount, 0), {
432
+ length: true,
433
+ })
367
434
  )
368
435
  }
369
436
  }
370
437
 
371
438
  reverse() {
372
439
  try {
373
- return this[native].reverse()
440
+ return this.reverse()
374
441
  } finally {
375
- touched(this, { type: 'bunch', method: 'reverse' }, range(0, this[native].length - 1))
442
+ touched(this, { type: 'bunch', method: 'reverse' }, range(0, this.length - 1))
376
443
  }
377
444
  }
378
445
 
379
446
  sort(compareFn?: (a: any, b: any) => number) {
380
447
  compareFn = compareFn || ((a, b) => a.toString().localeCompare(b.toString()))
381
448
  try {
382
- return this[native].sort((a, b) => compareFn(reactive(a), reactive(b))) as any
449
+ return this.sort((a, b) => compareFn(reactive(a), reactive(b))) as any
383
450
  } finally {
384
- touched(this, { type: 'bunch', method: 'sort' }, range(0, this[native].length - 1))
451
+ touched(this, { type: 'bunch', method: 'sort' }, range(0, this.length - 1))
385
452
  }
386
453
  }
387
454
 
388
455
  fill(value: any, start?: number, end?: number) {
456
+ const len = this.length
457
+ let k = start === undefined ? 0 : start
458
+ if (k < 0) k = Math.max(len + k, 0)
459
+ else k = Math.min(k, len)
460
+
461
+ let final = end === undefined ? len : end
462
+ if (final < 0) final = Math.max(len + final, 0)
463
+ else final = Math.min(final, len)
464
+
389
465
  try {
390
- if (start === undefined) return this[native].fill(value) as any
391
- if (end === undefined) return this[native].fill(value, start) as any
392
- return this[native].fill(value, start, end) as any
466
+ if (start === undefined) return this.fill(value) as any
467
+ if (end === undefined) return this.fill(value, start) as any
468
+ return this.fill(value, start, end) as any
393
469
  } finally {
394
- touched(this, { type: 'bunch', method: 'fill' }, range(0, this[native].length - 1))
470
+ if (final > k) {
471
+ touched(this, { type: 'bunch', method: 'fill' }, range(k, final - 1))
472
+ }
395
473
  }
396
474
  }
397
475
 
398
476
  copyWithin(target: number, start: number, end?: number) {
399
477
  try {
400
- if (end === undefined) return this[native].copyWithin(target, start) as any
401
- return this[native].copyWithin(target, start, end) as any
478
+ if (end === undefined) return this.copyWithin(target, start) as any
479
+ return this.copyWithin(target, start, end) as any
402
480
  } finally {
403
- touched(
404
- this,
405
- { type: 'bunch', method: 'copyWithin' },
406
- // TODO: calculate the range properly
407
- range(0, this[native].length - 1)
408
- )
481
+ const len = this.length
482
+
483
+ let to = target
484
+ if (to < 0) to = Math.max(len + to, 0)
485
+ else if (to >= len) to = len
486
+
487
+ let from = start
488
+ if (from < 0) from = Math.max(len + from, 0)
489
+ else if (from >= len) from = len
490
+
491
+ let final = end === undefined ? len : end
492
+ if (final < 0) final = Math.max(len + final, 0)
493
+ else if (final >= len) final = len
494
+
495
+ const count = Math.min(final - from, len - to)
496
+
497
+ if (count > 0) {
498
+ touched(
499
+ this,
500
+ { type: 'bunch', method: 'copyWithin' },
501
+ range(to, to + count - 1)
502
+ )
503
+ }
504
+
409
505
  }
410
- // Touch all affected indices with a single allProps call
411
506
  }
412
507
  }