mutts 1.0.6 → 1.0.7
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/browser.d.ts +2 -0
- package/dist/browser.esm.js +70 -0
- package/dist/browser.esm.js.map +1 -0
- package/dist/browser.js +161 -0
- package/dist/browser.js.map +1 -0
- package/dist/chunks/{index-CDCOjzTy.js → index-BFYK02LG.js} +5760 -4338
- package/dist/chunks/index-BFYK02LG.js.map +1 -0
- package/dist/chunks/{index-DiP0RXoZ.esm.js → index-CNR6QRUl.esm.js} +5440 -4054
- package/dist/chunks/index-CNR6QRUl.esm.js.map +1 -0
- package/dist/devtools/panel.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/node.d.ts +2 -0
- package/dist/node.esm.js +45 -0
- package/dist/node.esm.js.map +1 -0
- package/dist/node.js +136 -0
- package/dist/node.js.map +1 -0
- package/docs/ai/api-reference.md +0 -2
- package/docs/reactive/advanced.md +2 -5
- package/docs/reactive/collections.md +0 -125
- package/docs/reactive/core.md +27 -24
- package/docs/reactive/debugging.md +12 -2
- package/docs/reactive/project.md +1 -1
- package/docs/reactive/scan.md +78 -0
- package/docs/reactive.md +2 -1
- package/docs/std-decorators.md +1 -0
- package/docs/zone.md +88 -0
- package/package.json +42 -10
- package/src/async/browser.ts +87 -0
- package/src/async/index.ts +8 -0
- package/src/async/node.ts +46 -0
- package/src/decorator.ts +5 -1
- package/src/destroyable.ts +1 -1
- package/src/index.ts +22 -14
- package/src/indexable.ts +42 -0
- package/src/mixins.ts +2 -2
- package/src/reactive/array.ts +149 -141
- package/src/reactive/buffer.ts +168 -0
- package/src/reactive/change.ts +2 -2
- package/src/reactive/effect-context.ts +15 -91
- package/src/reactive/effects.ts +119 -179
- package/src/reactive/index.ts +10 -13
- package/src/reactive/interface.ts +19 -33
- package/src/reactive/map.ts +48 -61
- package/src/reactive/memoize.ts +19 -9
- package/src/reactive/project.ts +43 -22
- package/src/reactive/proxy.ts +16 -41
- package/src/reactive/record.ts +3 -3
- package/src/reactive/register.ts +5 -7
- package/src/reactive/registry.ts +9 -17
- package/src/reactive/set.ts +42 -56
- package/src/reactive/tracking.ts +1 -29
- package/src/reactive/types.ts +46 -23
- package/src/utils.ts +80 -37
- package/src/zone.ts +127 -0
- package/dist/chunks/_tslib-BgjropY9.js +0 -81
- package/dist/chunks/_tslib-BgjropY9.js.map +0 -1
- package/dist/chunks/_tslib-MCKDzsSq.esm.js +0 -75
- package/dist/chunks/_tslib-MCKDzsSq.esm.js.map +0 -1
- package/dist/chunks/decorator-BGILvPtN.esm.js +0 -627
- package/dist/chunks/decorator-BGILvPtN.esm.js.map +0 -1
- package/dist/chunks/decorator-BQ2eBTCj.js +0 -651
- package/dist/chunks/decorator-BQ2eBTCj.js.map +0 -1
- package/dist/chunks/index-CDCOjzTy.js.map +0 -1
- package/dist/chunks/index-DiP0RXoZ.esm.js.map +0 -1
- package/dist/decorator.d.ts +0 -107
- package/dist/decorator.esm.js +0 -2
- package/dist/decorator.esm.js.map +0 -1
- package/dist/decorator.js +0 -11
- package/dist/decorator.js.map +0 -1
- package/dist/destroyable.d.ts +0 -90
- package/dist/destroyable.esm.js +0 -109
- package/dist/destroyable.esm.js.map +0 -1
- package/dist/destroyable.js +0 -116
- package/dist/destroyable.js.map +0 -1
- package/dist/eventful.d.ts +0 -20
- package/dist/eventful.esm.js +0 -66
- package/dist/eventful.esm.js.map +0 -1
- package/dist/eventful.js +0 -68
- package/dist/eventful.js.map +0 -1
- package/dist/index.d.ts +0 -19
- package/dist/index.esm.js +0 -53
- package/dist/index.esm.js.map +0 -1
- package/dist/index.js +0 -139
- package/dist/index.js.map +0 -1
- package/dist/indexable.d.ts +0 -243
- package/dist/indexable.esm.js +0 -285
- package/dist/indexable.esm.js.map +0 -1
- package/dist/indexable.js +0 -291
- package/dist/indexable.js.map +0 -1
- package/dist/promiseChain.d.ts +0 -21
- package/dist/promiseChain.esm.js +0 -78
- package/dist/promiseChain.esm.js.map +0 -1
- package/dist/promiseChain.js +0 -80
- package/dist/promiseChain.js.map +0 -1
- package/dist/reactive.d.ts +0 -910
- package/dist/reactive.esm.js +0 -5
- package/dist/reactive.esm.js.map +0 -1
- package/dist/reactive.js +0 -59
- package/dist/reactive.js.map +0 -1
- package/dist/std-decorators.d.ts +0 -52
- package/dist/std-decorators.esm.js +0 -196
- package/dist/std-decorators.esm.js.map +0 -1
- package/dist/std-decorators.js +0 -204
- package/dist/std-decorators.js.map +0 -1
- package/src/reactive/mapped.ts +0 -129
- package/src/reactive/zone.ts +0 -208
package/docs/zone.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Zones (`mutts/zone`)
|
|
2
|
+
|
|
3
|
+
Zones provide a high-performance **context management system** that follows the execution flow, ensuring your variables "stay put" even across asynchronous boundaries like `Promises`, `setTimeout`, or `queueMicrotask`.
|
|
4
|
+
|
|
5
|
+
## Basic Usage
|
|
6
|
+
|
|
7
|
+
A `Zone` represents a piece of storage that is scoped to the current execution block.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { Zone } from 'mutts/zone';
|
|
11
|
+
|
|
12
|
+
const myZone = new Zone<string>();
|
|
13
|
+
|
|
14
|
+
myZone.with("context-value", () => {
|
|
15
|
+
// Inside this function, the zone is active
|
|
16
|
+
console.log(myZone.active); // "context-value"
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
console.log(myZone.active); // undefined
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Async Propagation
|
|
23
|
+
|
|
24
|
+
By default, zones are lost when an async operation yields control (e.g., after `await`). To fix this, `mutts` provides `configureAsyncZone()`.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { configureAsyncZone, asyncZone, Zone } from 'mutts/zone';
|
|
28
|
+
|
|
29
|
+
const requestId = new Zone<string>();
|
|
30
|
+
|
|
31
|
+
// 1. Tell the global aggregator to track this zone
|
|
32
|
+
asyncZone.add(requestId);
|
|
33
|
+
|
|
34
|
+
// 2. Patch global async primitives (once per app)
|
|
35
|
+
configureAsyncZone();
|
|
36
|
+
|
|
37
|
+
// 3. Usage
|
|
38
|
+
requestId.with("req-123", async () => {
|
|
39
|
+
await somePromise();
|
|
40
|
+
// Context is automatically preserved across await!
|
|
41
|
+
console.log(requestId.active); // "req-123"
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Core API
|
|
46
|
+
|
|
47
|
+
### `AZone<T>` (Abstract)
|
|
48
|
+
The base class for all zone implementations.
|
|
49
|
+
- `active: T | undefined`: The current value in the zone.
|
|
50
|
+
- `with<R>(value: T, fn: () => R): R`: Executes `fn` with `value` set as active.
|
|
51
|
+
- `root<R>(fn: () => R): R`: Executes `fn` with the zone cleared (undefined).
|
|
52
|
+
- `zoned: FunctionWrapper`: A getter that returns a function which, when called, restores the zone to its **current** state.
|
|
53
|
+
|
|
54
|
+
### `Zone<T>`
|
|
55
|
+
Simple stack-based storage.
|
|
56
|
+
|
|
57
|
+
### `ZoneHistory<T>`
|
|
58
|
+
A zone wrapper that maintains a `history` of previously active values in the current stack.
|
|
59
|
+
- Useful for **Cycle Detection**.
|
|
60
|
+
- Prevents re-entering the same value if already in the history.
|
|
61
|
+
- `present: AZone<T>`: Access the current value without the history overhead.
|
|
62
|
+
|
|
63
|
+
### `ZoneAggregator`
|
|
64
|
+
Combines multiple zones into one.
|
|
65
|
+
- Entering an aggregator (with `.with()`) enters all its member zones.
|
|
66
|
+
- `asyncZone` is a global aggregator used for the async patches.
|
|
67
|
+
|
|
68
|
+
## Manual Context Bridging
|
|
69
|
+
|
|
70
|
+
If you are using an API that `mutts` doesn't automatically patch, you can use the `.zoned` capture mechanism:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
const wrap = myZone.zoned; // Snapshot the current context
|
|
74
|
+
|
|
75
|
+
// Pass the wrapper to an unmanaged callback
|
|
76
|
+
externalLib.on('event', () => {
|
|
77
|
+
wrap(() => {
|
|
78
|
+
console.log("Context is back:", myZone.active);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Integration with Reactivity
|
|
84
|
+
|
|
85
|
+
The `mutts` reactivity system uses zones internally to track the `activeEffect`.
|
|
86
|
+
- Every `effect()` execution runs inside the `effectHistory` zone.
|
|
87
|
+
- Circular dependency detection is powered by `ZoneHistory`.
|
|
88
|
+
- Async effects survive `await` because `effectHistory` is a member of the global `asyncZone`.
|
package/package.json
CHANGED
|
@@ -1,19 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mutts",
|
|
3
3
|
"description": "Modern UTility TS: A collection of TypeScript utilities",
|
|
4
|
-
"version": "1.0.
|
|
5
|
-
"main": "
|
|
6
|
-
"module": "
|
|
7
|
-
"types": "
|
|
4
|
+
"version": "1.0.7",
|
|
5
|
+
"main": "dist/browser.js",
|
|
6
|
+
"module": "dist/browser.esm.js",
|
|
7
|
+
"types": "dist/browser.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
"node": {
|
|
11
|
+
"types": "./dist/node.d.ts",
|
|
12
|
+
"import": "./dist/node.esm.js",
|
|
13
|
+
"require": "./dist/node.js"
|
|
14
|
+
},
|
|
15
|
+
"default": {
|
|
16
|
+
"types": "./dist/browser.d.ts",
|
|
17
|
+
"import": "./dist/browser.esm.js",
|
|
18
|
+
"require": "./dist/browser.js"
|
|
19
|
+
}
|
|
15
20
|
},
|
|
16
|
-
"./
|
|
21
|
+
"./browser": {
|
|
22
|
+
"types": "./dist/browser.d.ts",
|
|
23
|
+
"import": "./dist/browser.esm.js",
|
|
24
|
+
"require": "./dist/browser.js"
|
|
25
|
+
},
|
|
26
|
+
"./node": {
|
|
27
|
+
"types": "./dist/node.d.ts",
|
|
28
|
+
"import": "./dist/node.esm.js",
|
|
29
|
+
"require": "./dist/node.js"
|
|
30
|
+
},
|
|
31
|
+
"./src": {
|
|
32
|
+
"node": {
|
|
33
|
+
"types": "./src/async/node.ts",
|
|
34
|
+
"import": "./src/async/node.ts"
|
|
35
|
+
},
|
|
36
|
+
"default": {
|
|
37
|
+
"types": "./src/async/browser.ts",
|
|
38
|
+
"import": "./src/async/browser.ts"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"./src/browser": {
|
|
42
|
+
"types": "./src/async/browser.ts",
|
|
43
|
+
"import": "./src/async/browser.ts"
|
|
44
|
+
},
|
|
45
|
+
"./src/node": {
|
|
46
|
+
"types": "./src/async/node.ts",
|
|
47
|
+
"import": "./src/async/node.ts"
|
|
48
|
+
}
|
|
17
49
|
},
|
|
18
50
|
"files": [
|
|
19
51
|
"dist",
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Hook, Restorer, asyncHooks } from '.'
|
|
2
|
+
|
|
3
|
+
const hooks = new Set<Hook>()
|
|
4
|
+
|
|
5
|
+
asyncHooks.addHook = function (hook: Hook) {
|
|
6
|
+
hooks.add(hook)
|
|
7
|
+
return () => {
|
|
8
|
+
hooks.delete(hook)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export * from '../index'
|
|
13
|
+
|
|
14
|
+
function wrap<Args extends any[], R>(fn: ((...args: Args) => R) | null | undefined) {
|
|
15
|
+
if (typeof fn !== 'function') return fn
|
|
16
|
+
const restorers = new Set<Restorer>()
|
|
17
|
+
for (const hook of hooks) restorers.add(hook())
|
|
18
|
+
|
|
19
|
+
return function (this: any, ...args: Args) {
|
|
20
|
+
const undoers = new Set<() => void>()
|
|
21
|
+
for (const restore of restorers) undoers.add(restore())
|
|
22
|
+
try {
|
|
23
|
+
return fn.apply(this, args)
|
|
24
|
+
} finally {
|
|
25
|
+
for (const undo of undoers) undo()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const originals = {
|
|
31
|
+
then: Promise.prototype.then,
|
|
32
|
+
catch: Promise.prototype.catch,
|
|
33
|
+
finally: Promise.prototype.finally,
|
|
34
|
+
setTimeout: globalThis.setTimeout,
|
|
35
|
+
setInterval: globalThis.setInterval,
|
|
36
|
+
setImmediate: globalThis.setImmediate,
|
|
37
|
+
requestAnimationFrame: globalThis.requestAnimationFrame,
|
|
38
|
+
queueMicrotask: globalThis.queueMicrotask,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Promise.prototype.then = function <T, R1, R2>(
|
|
42
|
+
this: Promise<T>,
|
|
43
|
+
onFulfilled?: ((value: T) => R1 | PromiseLike<R1>) | null,
|
|
44
|
+
onRejected?: ((reason: any) => R2 | PromiseLike<R2>) | null
|
|
45
|
+
): Promise<R1 | R2> {
|
|
46
|
+
return originals.then.call(this, wrap(onFulfilled), wrap(onRejected))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Promise.prototype.catch = function <T>(
|
|
50
|
+
this: Promise<T>,
|
|
51
|
+
onRejected?: ((reason: any) => T | PromiseLike<T>) | null
|
|
52
|
+
): Promise<T> {
|
|
53
|
+
return originals.catch.call(this, wrap(onRejected))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
Promise.prototype.finally = function <T>(
|
|
57
|
+
this: Promise<T>,
|
|
58
|
+
onFinally?: (() => void) | null
|
|
59
|
+
): Promise<T> {
|
|
60
|
+
return originals.finally.call(this, wrap(onFinally))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
globalThis.setTimeout = ((callback: Function, ...args: any[]) => {
|
|
64
|
+
return originals.setTimeout.call(globalThis, wrap(callback as any), ...args)
|
|
65
|
+
}) as any
|
|
66
|
+
|
|
67
|
+
globalThis.setInterval = ((callback: Function, ...args: any[]) => {
|
|
68
|
+
return originals.setInterval.call(globalThis, wrap(callback as any), ...args)
|
|
69
|
+
}) as any
|
|
70
|
+
|
|
71
|
+
if (originals.setImmediate) {
|
|
72
|
+
globalThis.setImmediate = ((callback: Function, ...args: any[]) => {
|
|
73
|
+
return originals.setImmediate.call(globalThis, wrap(callback as any), ...args)
|
|
74
|
+
}) as any
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (originals.requestAnimationFrame) {
|
|
78
|
+
globalThis.requestAnimationFrame = (callback: FrameRequestCallback) => {
|
|
79
|
+
return originals.requestAnimationFrame.call(globalThis, wrap(callback))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (originals.queueMicrotask) {
|
|
84
|
+
globalThis.queueMicrotask = (callback: VoidFunction): void => {
|
|
85
|
+
originals.queueMicrotask.call(globalThis, wrap(callback))
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createHook } from 'node:async_hooks'
|
|
2
|
+
import { Hook, Restorer, asyncHooks } from '.'
|
|
3
|
+
|
|
4
|
+
const hooks = new Set<Hook>()
|
|
5
|
+
const restorersPerAsyncId = new Map<number, Set<Restorer>>()
|
|
6
|
+
const undoersPerAsyncId = new Map<number, Set<() => void>>()
|
|
7
|
+
|
|
8
|
+
createHook({
|
|
9
|
+
init(asyncId) {
|
|
10
|
+
const restorers = new Set<Restorer>()
|
|
11
|
+
for (const hook of hooks) {
|
|
12
|
+
restorers.add(hook())
|
|
13
|
+
}
|
|
14
|
+
restorersPerAsyncId.set(asyncId, restorers)
|
|
15
|
+
},
|
|
16
|
+
before(asyncId) {
|
|
17
|
+
const restorers = restorersPerAsyncId.get(asyncId)
|
|
18
|
+
if (restorers) {
|
|
19
|
+
const undoers = new Set<() => void>()
|
|
20
|
+
for (const restore of restorers) {
|
|
21
|
+
undoers.add(restore())
|
|
22
|
+
}
|
|
23
|
+
undoersPerAsyncId.set(asyncId, undoers)
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
after(asyncId) {
|
|
27
|
+
const undoers = undoersPerAsyncId.get(asyncId)
|
|
28
|
+
if (undoers) {
|
|
29
|
+
for (const undo of undoers) undo()
|
|
30
|
+
undoersPerAsyncId.delete(asyncId)
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
destroy(asyncId) {
|
|
34
|
+
restorersPerAsyncId.delete(asyncId)
|
|
35
|
+
undoersPerAsyncId.delete(asyncId)
|
|
36
|
+
}
|
|
37
|
+
}).enable()
|
|
38
|
+
|
|
39
|
+
asyncHooks.addHook = function (hook: Hook) {
|
|
40
|
+
hooks.add(hook)
|
|
41
|
+
return () => {
|
|
42
|
+
hooks.delete(hook)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export * from '../index'
|
package/src/decorator.ts
CHANGED
|
@@ -62,7 +62,11 @@ type DDMethod<T> = (
|
|
|
62
62
|
name: PropertyKey
|
|
63
63
|
) => ((this: T, ...args: any[]) => any) | void
|
|
64
64
|
|
|
65
|
-
type DDGetter<T> = (
|
|
65
|
+
type DDGetter<T> = (
|
|
66
|
+
original: (this: T) => any,
|
|
67
|
+
target: any,
|
|
68
|
+
name: PropertyKey
|
|
69
|
+
) => ((this: T) => any) | void
|
|
66
70
|
|
|
67
71
|
type DDSetter<T> = (
|
|
68
72
|
original: (this: T, value: any) => void,
|
package/src/destroyable.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -4,29 +4,37 @@ export * from './eventful'
|
|
|
4
4
|
export * from './indexable'
|
|
5
5
|
export * from './iterableWeak'
|
|
6
6
|
export * from './mixins'
|
|
7
|
+
export * from './promiseChain'
|
|
7
8
|
export * from './reactive'
|
|
8
9
|
export * from './std-decorators'
|
|
9
10
|
export * from './utils'
|
|
11
|
+
export * from './zone'
|
|
10
12
|
|
|
11
13
|
import pkg from '../package.json'
|
|
14
|
+
|
|
12
15
|
const { version } = pkg
|
|
13
16
|
|
|
14
17
|
// Singleton verification
|
|
15
18
|
const GLOBAL_MUTTS_KEY = '__MUTTS_INSTANCE__'
|
|
16
|
-
const globalScope =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const globalScope = (
|
|
20
|
+
typeof globalThis !== 'undefined'
|
|
21
|
+
? globalThis
|
|
22
|
+
: typeof window !== 'undefined'
|
|
23
|
+
? window
|
|
24
|
+
: typeof global !== 'undefined'
|
|
25
|
+
? global
|
|
26
|
+
: false
|
|
27
|
+
) as any
|
|
28
|
+
if (globalScope) {
|
|
21
29
|
// Detect the source of this instance safely across different environments
|
|
22
30
|
let source = 'mutts/index'
|
|
31
|
+
const viteEval = eval
|
|
23
32
|
try {
|
|
24
33
|
// @ts-ignore
|
|
25
34
|
if (typeof __filename !== 'undefined') source = __filename
|
|
26
|
-
// @ts-ignore
|
|
27
35
|
else {
|
|
28
36
|
// Using eval to avoid SyntaxError in CJS environments where import.meta is not allowed
|
|
29
|
-
const meta =
|
|
37
|
+
const meta = viteEval('import.meta')
|
|
30
38
|
if (meta && meta.url) source = meta.url
|
|
31
39
|
}
|
|
32
40
|
} catch (e) {
|
|
@@ -36,18 +44,18 @@ if(globalScope) {
|
|
|
36
44
|
const currentSourceInfo = {
|
|
37
45
|
version,
|
|
38
46
|
source,
|
|
39
|
-
timestamp: Date.now()
|
|
47
|
+
timestamp: Date.now(),
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
if (globalScope[GLOBAL_MUTTS_KEY]) {
|
|
43
51
|
const existing = globalScope[GLOBAL_MUTTS_KEY]
|
|
44
52
|
throw new Error(
|
|
45
|
-
`[Mutts] Multiple instances detected!\n` +
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
`[Mutts] Multiple instances detected!\n` +
|
|
54
|
+
`Existing instance: ${JSON.stringify(existing, null, 2)}\n` +
|
|
55
|
+
`New instance: ${JSON.stringify(currentSourceInfo, null, 2)}\n` +
|
|
56
|
+
`This usually happens when 'mutts' is both installed as a dependency and bundled, ` +
|
|
57
|
+
`or when different versions are loaded. ` +
|
|
58
|
+
`Please check your build configuration (aliases, externals) to ensure a single source of truth.`
|
|
51
59
|
)
|
|
52
60
|
}
|
|
53
61
|
|
package/src/indexable.ts
CHANGED
|
@@ -140,6 +140,48 @@ export function Indexable<Items, Base extends abstract new (...args: any[]) => a
|
|
|
140
140
|
})
|
|
141
141
|
return true
|
|
142
142
|
},
|
|
143
|
+
has(target, prop) {
|
|
144
|
+
if (prop in target) return true
|
|
145
|
+
if (typeof prop === 'string') {
|
|
146
|
+
if (prop === 'length' && accessor.getLength) return true
|
|
147
|
+
const numProp = Number(prop)
|
|
148
|
+
if (!Number.isNaN(numProp)) return true
|
|
149
|
+
}
|
|
150
|
+
return false
|
|
151
|
+
},
|
|
152
|
+
ownKeys(target) {
|
|
153
|
+
const keys = Reflect.ownKeys(target)
|
|
154
|
+
if (accessor.getLength) {
|
|
155
|
+
keys.push('length')
|
|
156
|
+
const len = accessor.getLength.call(this as any)
|
|
157
|
+
for (let i = 0; i < len; i++) keys.push(String(i))
|
|
158
|
+
}
|
|
159
|
+
return keys
|
|
160
|
+
},
|
|
161
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
162
|
+
if (prop in target) return Object.getOwnPropertyDescriptor(target, prop)
|
|
163
|
+
if (typeof prop === 'string') {
|
|
164
|
+
if (prop === 'length' && accessor.getLength) {
|
|
165
|
+
return {
|
|
166
|
+
enumerable: false,
|
|
167
|
+
configurable: true,
|
|
168
|
+
get: () => accessor.getLength!.call(this as any),
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const numProp = Number(prop)
|
|
172
|
+
if (!Number.isNaN(numProp)) {
|
|
173
|
+
return {
|
|
174
|
+
enumerable: true,
|
|
175
|
+
configurable: true,
|
|
176
|
+
get: () => accessor.get!.call(this as any, numProp),
|
|
177
|
+
set: accessor.set
|
|
178
|
+
? (v: any) => accessor.set!.call(this as any, numProp, v)
|
|
179
|
+
: undefined,
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return undefined
|
|
184
|
+
},
|
|
143
185
|
})
|
|
144
186
|
)
|
|
145
187
|
return Indexable
|
package/src/mixins.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FoolProof, isConstructor } from './utils'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A mixin function that takes a base class and returns a new class with mixed-in functionality
|
|
@@ -85,7 +85,7 @@ export function mixin<MixinFn extends (base: any) => new (...args: any[]) => any
|
|
|
85
85
|
const originalPrototype = baseClass.prototype
|
|
86
86
|
const proxiedPrototype = new Proxy(originalPrototype, {
|
|
87
87
|
get(target, prop, receiver) {
|
|
88
|
-
const value =
|
|
88
|
+
const value = FoolProof.get(target, prop, receiver)
|
|
89
89
|
|
|
90
90
|
// Only wrap methods that are likely to access private fields
|
|
91
91
|
// Skip symbols and special properties that the reactive system needs
|