mutts 1.0.4 → 1.0.5
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.
- package/README.md +1 -1
- package/dist/chunks/{index-GRBSx0mB.js → index-Cvxdw6Ax.js} +164 -12
- package/dist/chunks/index-Cvxdw6Ax.js.map +1 -0
- package/dist/chunks/{index-79Kk8D6e.esm.js → index-qiWwozOc.esm.js} +163 -13
- package/dist/chunks/index-qiWwozOc.esm.js.map +1 -0
- package/dist/destroyable.esm.js.map +1 -1
- package/dist/destroyable.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/mutts.umd.js +1 -1
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/reactive.d.ts +29 -1
- package/dist/reactive.esm.js +1 -1
- package/dist/reactive.js +3 -1
- package/dist/reactive.js.map +1 -1
- package/dist/std-decorators.esm.js.map +1 -1
- package/dist/std-decorators.js.map +1 -1
- package/docs/reactive/core.md +16 -16
- package/docs/reactive.md +7 -0
- package/package.json +1 -1
- package/src/destroyable.ts +2 -2
- package/src/reactive/array.ts +3 -5
- package/src/reactive/change.ts +6 -2
- package/src/reactive/effects.ts +70 -1
- package/src/reactive/index.ts +2 -1
- package/src/reactive/interface.ts +1 -1
- package/src/reactive/map.ts +6 -6
- package/src/reactive/mapped.ts +2 -3
- package/src/reactive/project.ts +103 -6
- package/src/reactive/set.ts +6 -6
- package/src/reactive/types.ts +22 -0
- package/src/reactive/zone.ts +1 -1
- package/src/std-decorators.ts +1 -1
- package/dist/chunks/index-79Kk8D6e.esm.js.map +0 -1
- package/dist/chunks/index-GRBSx0mB.js.map +0 -1
- /package/{src/reactive/project.project.md → docs/reactive/project.md} +0 -0
package/src/reactive/project.ts
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import { ReflectGet, ReflectSet } from '../utils'
|
|
2
|
+
import { setEffectName } from './debug'
|
|
3
|
+
import { getActiveEffect } from './effect-context'
|
|
2
4
|
import { effect, untracked } from './effects'
|
|
3
5
|
import { cleanedBy, cleanup } from './interface'
|
|
4
6
|
import { reactive } from './proxy'
|
|
5
7
|
import { Register } from './register'
|
|
6
|
-
import { type ScopedCallback } from './types'
|
|
8
|
+
import { type ProjectionContext, projectionInfo, type ScopedCallback } from './types'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Maps projection effects (item effects) to their projection context
|
|
12
|
+
*/
|
|
13
|
+
export const effectProjectionMetadata = new WeakMap<ScopedCallback, ProjectionContext>()
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the projection context of the currently running effect, if any.
|
|
17
|
+
*/
|
|
18
|
+
export function getActiveProjection(): ProjectionContext | undefined {
|
|
19
|
+
const active = getActiveEffect()
|
|
20
|
+
return active ? effectProjectionMetadata.get(active) : undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* TODO
|
|
24
|
+
It seems to work and I feel like it's correct but I couldn't validate theoretically that `ascend`
|
|
25
|
+
is the correct way to deal with nested effects.
|
|
26
|
+
*/
|
|
7
27
|
|
|
8
28
|
type ProjectOldValue<Target> = Target extends readonly (infer Item)[]
|
|
9
29
|
? Item
|
|
@@ -50,8 +70,17 @@ function defineAccessValue<Access extends { get(): unknown; set(value: unknown):
|
|
|
50
70
|
function makeCleanup<Result extends object>(
|
|
51
71
|
target: Result,
|
|
52
72
|
effectMap: Map<unknown, ScopedCallback>,
|
|
53
|
-
onDispose: () => void
|
|
73
|
+
onDispose: () => void,
|
|
74
|
+
metadata?: any
|
|
54
75
|
): ProjectResult<Result> {
|
|
76
|
+
if (metadata) {
|
|
77
|
+
Object.defineProperty(target, projectionInfo, {
|
|
78
|
+
value: metadata,
|
|
79
|
+
writable: false,
|
|
80
|
+
enumerable: false,
|
|
81
|
+
configurable: true,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
55
84
|
return cleanedBy(target, () => {
|
|
56
85
|
onDispose()
|
|
57
86
|
for (const stop of effectMap.values()) stop?.()
|
|
@@ -80,6 +109,9 @@ function projectArray<SourceValue, ResultValue>(
|
|
|
80
109
|
}
|
|
81
110
|
}
|
|
82
111
|
|
|
112
|
+
const parent = getActiveProjection()
|
|
113
|
+
const depth = parent ? parent.depth + 1 : 0
|
|
114
|
+
|
|
83
115
|
const cleanupLength = effect(function projectArrayLengthEffect({ ascend }) {
|
|
84
116
|
const length = observedSource.length
|
|
85
117
|
normalizeTargetLength(length)
|
|
@@ -102,13 +134,27 @@ function projectArray<SourceValue, ResultValue>(
|
|
|
102
134
|
const produced = apply(accessBase, target)
|
|
103
135
|
target[index] = produced
|
|
104
136
|
})
|
|
137
|
+
setEffectName(stop, `project[${depth}]:${index}`)
|
|
138
|
+
effectProjectionMetadata.set(stop, {
|
|
139
|
+
source: observedSource,
|
|
140
|
+
key: index,
|
|
141
|
+
target,
|
|
142
|
+
depth,
|
|
143
|
+
parent,
|
|
144
|
+
})
|
|
105
145
|
indexEffects.set(i, stop)
|
|
106
146
|
})
|
|
107
147
|
}
|
|
108
148
|
for (const index of existing) if (index >= length) disposeIndex(index)
|
|
109
149
|
})
|
|
110
150
|
|
|
111
|
-
return makeCleanup(target, indexEffects, () => cleanupLength()
|
|
151
|
+
return makeCleanup(target, indexEffects, () => cleanupLength(), {
|
|
152
|
+
source: observedSource,
|
|
153
|
+
target,
|
|
154
|
+
apply,
|
|
155
|
+
depth,
|
|
156
|
+
parent,
|
|
157
|
+
} as ProjectionContext)
|
|
112
158
|
}
|
|
113
159
|
|
|
114
160
|
function projectRegister<Key extends PropertyKey, SourceValue, ResultValue>(
|
|
@@ -135,6 +181,9 @@ function projectRegister<Key extends PropertyKey, SourceValue, ResultValue>(
|
|
|
135
181
|
}
|
|
136
182
|
}
|
|
137
183
|
|
|
184
|
+
const parent = getActiveProjection()
|
|
185
|
+
const depth = parent ? parent.depth + 1 : 0
|
|
186
|
+
|
|
138
187
|
const cleanupKeys = effect(function projectRegisterEffect({ ascend }) {
|
|
139
188
|
const keys = new Set<Key>()
|
|
140
189
|
for (const key of observedSource.mapKeys()) keys.add(key)
|
|
@@ -158,6 +207,14 @@ function projectRegister<Key extends PropertyKey, SourceValue, ResultValue>(
|
|
|
158
207
|
const produced = apply(accessBase, target)
|
|
159
208
|
target.set(key, produced)
|
|
160
209
|
})
|
|
210
|
+
setEffectName(stop, `project[${depth}]:${String(key)}`)
|
|
211
|
+
effectProjectionMetadata.set(stop, {
|
|
212
|
+
source: observedSource,
|
|
213
|
+
key,
|
|
214
|
+
target,
|
|
215
|
+
depth,
|
|
216
|
+
parent,
|
|
217
|
+
})
|
|
161
218
|
keyEffects.set(key, stop)
|
|
162
219
|
})
|
|
163
220
|
}
|
|
@@ -165,7 +222,13 @@ function projectRegister<Key extends PropertyKey, SourceValue, ResultValue>(
|
|
|
165
222
|
for (const key of Array.from(keyEffects.keys())) if (!keys.has(key)) disposeKey(key)
|
|
166
223
|
})
|
|
167
224
|
|
|
168
|
-
return makeCleanup(target, keyEffects, () => cleanupKeys()
|
|
225
|
+
return makeCleanup(target, keyEffects, () => cleanupKeys(), {
|
|
226
|
+
source: observedSource,
|
|
227
|
+
target,
|
|
228
|
+
apply,
|
|
229
|
+
depth,
|
|
230
|
+
parent,
|
|
231
|
+
} as ProjectionContext)
|
|
169
232
|
}
|
|
170
233
|
|
|
171
234
|
function projectRecord<Source extends Record<PropertyKey, any>, ResultValue>(
|
|
@@ -191,6 +254,9 @@ function projectRecord<Source extends Record<PropertyKey, any>, ResultValue>(
|
|
|
191
254
|
}
|
|
192
255
|
}
|
|
193
256
|
|
|
257
|
+
const parent = getActiveProjection()
|
|
258
|
+
const depth = parent ? parent.depth + 1 : 0
|
|
259
|
+
|
|
194
260
|
const cleanupKeys = effect(function projectRecordEffect({ ascend }) {
|
|
195
261
|
const keys = new Set<PropertyKey>()
|
|
196
262
|
for (const key in observedSource) keys.add(key)
|
|
@@ -222,6 +288,14 @@ function projectRecord<Source extends Record<PropertyKey, any>, ResultValue>(
|
|
|
222
288
|
const produced = apply(accessBase, target)
|
|
223
289
|
;(target as any)[sourceKey] = produced
|
|
224
290
|
})
|
|
291
|
+
setEffectName(stop, `project[${depth}]:${String(key)}`)
|
|
292
|
+
effectProjectionMetadata.set(stop, {
|
|
293
|
+
source: observedSource,
|
|
294
|
+
key,
|
|
295
|
+
target,
|
|
296
|
+
depth,
|
|
297
|
+
parent,
|
|
298
|
+
})
|
|
225
299
|
keyEffects.set(key, stop)
|
|
226
300
|
})
|
|
227
301
|
}
|
|
@@ -229,7 +303,13 @@ function projectRecord<Source extends Record<PropertyKey, any>, ResultValue>(
|
|
|
229
303
|
for (const key of Array.from(keyEffects.keys())) if (!keys.has(key)) disposeKey(key)
|
|
230
304
|
})
|
|
231
305
|
|
|
232
|
-
return makeCleanup(target, keyEffects, () => cleanupKeys()
|
|
306
|
+
return makeCleanup(target, keyEffects, () => cleanupKeys(), {
|
|
307
|
+
source: observedSource,
|
|
308
|
+
target,
|
|
309
|
+
apply,
|
|
310
|
+
depth,
|
|
311
|
+
parent,
|
|
312
|
+
} as ProjectionContext)
|
|
233
313
|
}
|
|
234
314
|
|
|
235
315
|
function projectMap<Key, Value, ResultValue>(
|
|
@@ -250,6 +330,9 @@ function projectMap<Key, Value, ResultValue>(
|
|
|
250
330
|
}
|
|
251
331
|
}
|
|
252
332
|
|
|
333
|
+
const parent = getActiveProjection()
|
|
334
|
+
const depth = parent ? parent.depth + 1 : 0
|
|
335
|
+
|
|
253
336
|
const cleanupKeys = effect(function projectMapEffect({ ascend }) {
|
|
254
337
|
const keys = new Set<Key>()
|
|
255
338
|
for (const key of observedSource.keys()) keys.add(key)
|
|
@@ -273,6 +356,14 @@ function projectMap<Key, Value, ResultValue>(
|
|
|
273
356
|
const produced = apply(accessBase, target)
|
|
274
357
|
target.set(key, produced)
|
|
275
358
|
})
|
|
359
|
+
setEffectName(stop, `project[${depth}]:${String(key)}`)
|
|
360
|
+
effectProjectionMetadata.set(stop, {
|
|
361
|
+
source: observedSource,
|
|
362
|
+
key,
|
|
363
|
+
target,
|
|
364
|
+
depth,
|
|
365
|
+
parent,
|
|
366
|
+
})
|
|
276
367
|
keyEffects.set(key, stop)
|
|
277
368
|
})
|
|
278
369
|
}
|
|
@@ -280,7 +371,13 @@ function projectMap<Key, Value, ResultValue>(
|
|
|
280
371
|
for (const key of Array.from(keyEffects.keys())) if (!keys.has(key)) disposeKey(key)
|
|
281
372
|
})
|
|
282
373
|
|
|
283
|
-
return makeCleanup(target, keyEffects, () => cleanupKeys()
|
|
374
|
+
return makeCleanup(target, keyEffects, () => cleanupKeys(), {
|
|
375
|
+
source: observedSource,
|
|
376
|
+
target,
|
|
377
|
+
apply,
|
|
378
|
+
depth,
|
|
379
|
+
parent,
|
|
380
|
+
} as ProjectionContext)
|
|
284
381
|
}
|
|
285
382
|
|
|
286
383
|
type ProjectOverload = {
|
package/src/reactive/set.ts
CHANGED
|
@@ -11,14 +11,14 @@ const native = Symbol('native')
|
|
|
11
11
|
* Only tracks individual value operations, no size tracking (WeakSet limitation)
|
|
12
12
|
*/
|
|
13
13
|
export class ReactiveWeakSet<T extends object> {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
readonly [native]!: WeakSet<T>
|
|
15
|
+
readonly content!: symbol
|
|
16
16
|
|
|
17
17
|
constructor(original: WeakSet<T>) {
|
|
18
18
|
Object.defineProperties(this, {
|
|
19
19
|
[native]: { value: original },
|
|
20
20
|
[prototypeForwarding]: { value: original },
|
|
21
|
-
content: { value: Symbol('
|
|
21
|
+
content: { value: Symbol('WeakSetContent') },
|
|
22
22
|
[Symbol.toStringTag]: { value: 'ReactiveWeakSet' },
|
|
23
23
|
})
|
|
24
24
|
}
|
|
@@ -52,13 +52,13 @@ export class ReactiveWeakSet<T extends object> {
|
|
|
52
52
|
* Tracks size changes, individual value operations, and collection-wide operations
|
|
53
53
|
*/
|
|
54
54
|
export class ReactiveSet<T> {
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
readonly [native]!: Set<T>
|
|
56
|
+
readonly content!: symbol
|
|
57
57
|
constructor(original: Set<T>) {
|
|
58
58
|
Object.defineProperties(this, {
|
|
59
59
|
[native]: { value: original },
|
|
60
60
|
[prototypeForwarding]: { value: original },
|
|
61
|
-
content: { value: Symbol('
|
|
61
|
+
content: { value: Symbol('SetContent') },
|
|
62
62
|
[Symbol.toStringTag]: { value: 'ReactiveSet' },
|
|
63
63
|
})
|
|
64
64
|
}
|
package/src/reactive/types.ts
CHANGED
|
@@ -141,6 +141,22 @@ export const prototypeForwarding: unique symbol = Symbol('prototype-forwarding')
|
|
|
141
141
|
*/
|
|
142
142
|
export const allProps = Symbol('all-props')
|
|
143
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Symbol for accessing projection information on reactive objects
|
|
146
|
+
*/
|
|
147
|
+
export const projectionInfo = Symbol('projection-info')
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Context for a running projection item effect
|
|
151
|
+
*/
|
|
152
|
+
export interface ProjectionContext {
|
|
153
|
+
source: any
|
|
154
|
+
key?: any
|
|
155
|
+
target: any
|
|
156
|
+
depth: number
|
|
157
|
+
parent?: ProjectionContext
|
|
158
|
+
}
|
|
159
|
+
|
|
144
160
|
// Symbol to mark functions with their root function
|
|
145
161
|
const rootFunction = Symbol('root-function')
|
|
146
162
|
|
|
@@ -250,6 +266,12 @@ export const options = {
|
|
|
250
266
|
* @default 100
|
|
251
267
|
*/
|
|
252
268
|
maxEffectChain: 100,
|
|
269
|
+
/**
|
|
270
|
+
* Maximum number of times an effect can be triggered by the same cause in a single batch
|
|
271
|
+
* Used to detect aggressive re-computation or infinite loops
|
|
272
|
+
* @default 10
|
|
273
|
+
*/
|
|
274
|
+
maxTriggerPerBatch: 10,
|
|
253
275
|
/**
|
|
254
276
|
* Debug purpose: maximum effect reaction (like call stack max depth)
|
|
255
277
|
* Used to prevent infinite loops
|
package/src/reactive/zone.ts
CHANGED
package/src/std-decorators.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decorator, GenericClassDecorator } from './decorator'
|
|
1
|
+
import { decorator, type GenericClassDecorator } from './decorator'
|
|
2
2
|
|
|
3
3
|
// In order to avoid async re-entrance, we could use zone.js or something like that.
|
|
4
4
|
const syncCalculating: { object: object; prop: PropertyKey }[] = []
|