ivue 1.6.1 → 2.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.
- package/README.md +171 -33
- package/bin/ivue.mjs +152 -0
- package/dist/Reactive.d.ts +131 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +88 -206
- package/dist/index.umd.js +1 -1
- package/lib/Reactive.ts +285 -114
- package/lib/__tests__/Reactive.vitest.spec.ts +1056 -0
- package/lib/__tests__/ReactiveAdversarial.vitest.spec.ts +182 -0
- package/lib/__tests__/coverage-completion.vitest.spec.ts +24 -0
- package/lib/__tests__/ivue.vitest.spec.ts +76 -19
- package/lib/index.ts +1 -2
- package/package.json +39 -10
- package/skills/ivue/SKILL.md +726 -0
- package/dist/ivue.d.ts +0 -310
package/lib/Reactive.ts
CHANGED
|
@@ -1,100 +1,128 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
effectScope,
|
|
3
|
+
isRef,
|
|
4
|
+
toRaw,
|
|
5
|
+
watch,
|
|
6
|
+
watchEffect,
|
|
7
|
+
type ExtractPropTypes,
|
|
8
|
+
type Ref,
|
|
9
|
+
} from 'vue';
|
|
2
10
|
|
|
3
11
|
/**
|
|
4
12
|
* Constants & Helpers
|
|
5
13
|
*/
|
|
6
|
-
const
|
|
7
|
-
Object.prototype.hasOwnProperty
|
|
8
|
-
);
|
|
14
|
+
const hasOwn = Object.hasOwn;
|
|
9
15
|
const getPrototypeOf = Object.getPrototypeOf;
|
|
10
16
|
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
11
|
-
const defineProperty = Object.defineProperty;
|
|
12
17
|
const getOwnPropertyNames = Object.getOwnPropertyNames;
|
|
18
|
+
const defineProperty = Object.defineProperty;
|
|
13
19
|
const objectPrototype = Object.prototype;
|
|
14
|
-
const getOwnPropertySymbols = Object.getOwnPropertySymbols;
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const RAW = Symbol('
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
+
// Identity symbols are global so every bundled copy agrees with objects
|
|
22
|
+
// stamped by another copy of the engine.
|
|
23
|
+
const RAW = Symbol.for('ivue.raw'); // Per-instance back-pointer to the raw object
|
|
24
|
+
const SCOPE = Symbol.for('ivue.scope'); // Lazily-created per-instance effect scope
|
|
25
|
+
// Marks a prototype level as "Reactified"; its VALUE is the list of
|
|
26
|
+
// engine-created instance-cache symbols for that level, so teardown can
|
|
27
|
+
// remove exactly the engine's cells and nothing else.
|
|
28
|
+
const PROCESSED = Symbol.for('ivue.processed');
|
|
21
29
|
|
|
22
30
|
/**
|
|
23
|
-
*
|
|
31
|
+
* Resolve the TRUE raw instance from whatever `this` the engine was entered
|
|
32
|
+
* with — the raw object itself, a Vue `reactive()` proxy, or a foreign proxy
|
|
33
|
+
* chain such as Vue's component expose proxy.
|
|
34
|
+
*
|
|
35
|
+
* Neither primitive is sufficient alone:
|
|
36
|
+
*
|
|
37
|
+
* - `toRaw(this)` cannot unwrap a foreign (non-Vue-reactive) proxy — e.g.
|
|
38
|
+
* Vue's component expose proxy — so it can return the proxy unchanged.
|
|
39
|
+
* - The RAW back-pointer read through a Vue reactive proxy comes back
|
|
40
|
+
* DEEP-WRAPPED: a reactive proxy wraps symbol-keyed object reads in
|
|
41
|
+
* `reactive(raw)`. Binding a method (or computed closure) to that wrapped
|
|
42
|
+
* value poisons the per-instance cache with ref-unwrapping `this`
|
|
43
|
+
* semantics — `this.x.value` then crashes, because `this.x` auto-unwraps
|
|
44
|
+
* to the plain value.
|
|
45
|
+
*
|
|
46
|
+
* So: try `toRaw()` first (one step for the common reactive-proxy path —
|
|
47
|
+
* consulting the pointer first costs a wrap+unwrap round-trip per access),
|
|
48
|
+
* and fall back to the pointer, normalized with `toRaw()`, for everything
|
|
49
|
+
* `toRaw()` cannot see through.
|
|
24
50
|
*/
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
51
|
+
function resolveRaw(self: any) {
|
|
52
|
+
const unwrapped = toRaw(self);
|
|
53
|
+
if (unwrapped !== self) {
|
|
54
|
+
// Genuine Vue reactive proxy. Stamp the back-pointer (once, directly on
|
|
55
|
+
// the raw — no proxy set traps) so foreign proxy chains can still
|
|
56
|
+
// resolve the true raw through it.
|
|
57
|
+
return unwrapped[RAW] ?? (unwrapped[RAW] = unwrapped);
|
|
58
|
+
}
|
|
59
|
+
// `self` is the raw object itself, or a foreign proxy over it.
|
|
60
|
+
const viaPointer = self[RAW];
|
|
61
|
+
if (viaPointer) {
|
|
62
|
+
// A pointer read through a proxy chain may come back deep-wrapped —
|
|
63
|
+
// normalize; on the raw object it is already the raw itself.
|
|
64
|
+
return viaPointer === self ? viaPointer : toRaw(viaPointer);
|
|
34
65
|
}
|
|
35
|
-
|
|
36
|
-
return
|
|
66
|
+
// First-ever engine access on a plain raw instance (direct `new Class()`).
|
|
67
|
+
return (self[RAW] = self);
|
|
37
68
|
}
|
|
38
69
|
|
|
39
70
|
/**
|
|
40
|
-
* Convert method to a lazy-bound prototype method.
|
|
71
|
+
* Convert a method to a lazy-bound prototype method.
|
|
72
|
+
*
|
|
73
|
+
* The bound function is created once, on first access, and cached on the raw
|
|
74
|
+
* object under a unique per-(prototype,key) symbol — giving referentially
|
|
75
|
+
* stable, correctly-bound methods with zero per-instance construction cost.
|
|
41
76
|
*/
|
|
42
|
-
function convertToLazyBoundMethod(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
77
|
+
function convertToLazyBoundMethod(
|
|
78
|
+
proto: any,
|
|
79
|
+
key: string,
|
|
80
|
+
superKey: symbol,
|
|
81
|
+
originalFn: (...args: any[]) => any,
|
|
82
|
+
) {
|
|
48
83
|
defineProperty(proto, key, {
|
|
49
84
|
configurable: true,
|
|
50
85
|
enumerable: false,
|
|
51
86
|
get(this: any) {
|
|
52
|
-
const raw =
|
|
87
|
+
const raw = resolveRaw(this);
|
|
53
88
|
return raw[superKey] ?? (raw[superKey] = originalFn.bind(raw));
|
|
54
89
|
},
|
|
55
90
|
set(this: any, newFn: any) {
|
|
56
|
-
|
|
91
|
+
resolveRaw(this)[superKey] = newFn;
|
|
57
92
|
},
|
|
58
93
|
});
|
|
59
94
|
}
|
|
60
95
|
|
|
61
96
|
/**
|
|
62
|
-
* Convert a
|
|
97
|
+
* Convert a getter to a lazily-cached Ref cell.
|
|
98
|
+
*
|
|
99
|
+
* Only ever called when the descriptor has a getter, so `originalGetter` is
|
|
100
|
+
* always defined here. A getter that returns any Ref — ref(), shallowRef(),
|
|
101
|
+
* computed() (a ComputedRef IS a Ref) — is cached under the instance symbol
|
|
102
|
+
* (stable reactive identity); a getter that returns a plain value
|
|
103
|
+
* de-optimizes back to a native getter on the prototype, removing all
|
|
104
|
+
* overhead for future instances.
|
|
63
105
|
*/
|
|
64
|
-
function
|
|
65
|
-
proto:
|
|
106
|
+
function convertToLazyRef(
|
|
107
|
+
proto: any,
|
|
66
108
|
key: string,
|
|
67
|
-
superKey: symbol
|
|
109
|
+
superKey: symbol,
|
|
110
|
+
originalGetter: (this: any) => any,
|
|
111
|
+
originalSetter: ((this: any, v: any) => any) | undefined,
|
|
68
112
|
) {
|
|
69
|
-
|
|
70
|
-
const originalGetter = desc?.get;
|
|
71
|
-
const originalSetter = desc?.set;
|
|
72
|
-
|
|
73
|
-
// Optimization: Properties starting with $ are assumed singletons
|
|
113
|
+
// Optimization: Properties starting with $ are assumed singletons.
|
|
74
114
|
const cacheWhole = key[0] === '$';
|
|
75
115
|
|
|
76
|
-
// DEV WARNING: Setter/Getter mismatch
|
|
77
|
-
if (import.meta.env.DEV && originalGetter && originalSetter) {
|
|
78
|
-
try {
|
|
79
|
-
const testVal = originalGetter.call({});
|
|
80
|
-
if (isRef(testVal)) {
|
|
81
|
-
console.warn(
|
|
82
|
-
`[ivue] API conflict on "${key}": Getter returns Ref but Setter is standard.`
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
} catch (e) {}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
116
|
const newGetter = function (this: any) {
|
|
89
|
-
const raw =
|
|
117
|
+
const raw = resolveRaw(this);
|
|
90
118
|
|
|
91
|
-
// 1. Check
|
|
119
|
+
// 1. Check cache
|
|
92
120
|
if (superKey in raw) return raw[superKey];
|
|
93
121
|
|
|
94
|
-
// 2. Execute
|
|
95
|
-
const result = originalGetter
|
|
122
|
+
// 2. Execute original
|
|
123
|
+
const result = originalGetter.call(raw);
|
|
96
124
|
|
|
97
|
-
// 3. Handle
|
|
125
|
+
// 3. Handle result
|
|
98
126
|
if (cacheWhole) {
|
|
99
127
|
// Cache result forever (Singleton pattern)
|
|
100
128
|
raw[superKey] = result;
|
|
@@ -105,19 +133,17 @@ function convertToLazyComputed<T extends object>(
|
|
|
105
133
|
// Cache Ref instance (Reactivity pattern)
|
|
106
134
|
raw[superKey] = result;
|
|
107
135
|
} else {
|
|
108
|
-
// DE-OPTIMIZATION: It's just a value. Restore
|
|
109
|
-
//
|
|
136
|
+
// DE-OPTIMIZATION: It's just a value. Restore a native getter on the
|
|
137
|
+
// prototype, removing the wrapper overhead for all future instances.
|
|
110
138
|
defineProperty(proto, key, {
|
|
111
139
|
configurable: true,
|
|
112
140
|
enumerable: false,
|
|
113
|
-
get:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
: undefined,
|
|
141
|
+
get(this: any) {
|
|
142
|
+
return originalGetter.call(resolveRaw(this));
|
|
143
|
+
},
|
|
118
144
|
set: originalSetter
|
|
119
145
|
? function (this: any, v: any) {
|
|
120
|
-
return originalSetter.call(
|
|
146
|
+
return originalSetter.call(resolveRaw(this), v);
|
|
121
147
|
}
|
|
122
148
|
: undefined,
|
|
123
149
|
});
|
|
@@ -129,10 +155,10 @@ function convertToLazyComputed<T extends object>(
|
|
|
129
155
|
defineProperty(proto, key, {
|
|
130
156
|
configurable: true,
|
|
131
157
|
enumerable: false,
|
|
132
|
-
get:
|
|
158
|
+
get: newGetter,
|
|
133
159
|
set: originalSetter
|
|
134
160
|
? function (this: any, v: any) {
|
|
135
|
-
return originalSetter.call(
|
|
161
|
+
return originalSetter.call(resolveRaw(this), v);
|
|
136
162
|
}
|
|
137
163
|
: undefined,
|
|
138
164
|
});
|
|
@@ -141,10 +167,10 @@ function convertToLazyComputed<T extends object>(
|
|
|
141
167
|
/**
|
|
142
168
|
* Create a reactive class.
|
|
143
169
|
* @param targetClass The class to make reactive.
|
|
144
|
-
* @returns A reactive version of the class.
|
|
170
|
+
* @returns A reactive version of the class (the same class, transformed in place).
|
|
145
171
|
*/
|
|
146
172
|
export function Reactive<C extends new (...args: any) => any>(
|
|
147
|
-
targetClass: C
|
|
173
|
+
targetClass: C,
|
|
148
174
|
): ReactiveClass<C> & { Instance: ReactiveInstance<InstanceType<C>> } {
|
|
149
175
|
const chain: any[] = [];
|
|
150
176
|
|
|
@@ -158,53 +184,109 @@ export function Reactive<C extends new (...args: any) => any>(
|
|
|
158
184
|
chain.reverse();
|
|
159
185
|
|
|
160
186
|
for (const prototype of chain) {
|
|
161
|
-
// OPTIMIZATION: Skip if this prototype layer is already "Reactified"
|
|
187
|
+
// OPTIMIZATION: Skip if this prototype layer is already "Reactified".
|
|
162
188
|
// This handles diamond inheritance and multiple Reactive children safely.
|
|
163
|
-
if (
|
|
189
|
+
if (hasOwn(prototype, PROCESSED)) continue;
|
|
164
190
|
|
|
165
191
|
const names = getOwnPropertyNames(prototype);
|
|
192
|
+
const cacheKeys: symbol[] = [];
|
|
166
193
|
|
|
167
194
|
for (const key of names) {
|
|
168
195
|
if (key === 'constructor') continue;
|
|
169
|
-
const desc = getOwnPropertyDescriptor(prototype, key)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
196
|
+
const desc = getOwnPropertyDescriptor(prototype, key)!;
|
|
197
|
+
|
|
198
|
+
if (typeof desc.value === 'function') {
|
|
199
|
+
// A fresh symbol per (prototype,key). Because each prototype level gets
|
|
200
|
+
// its own symbol, a child override and its `super` counterpart cache
|
|
201
|
+
// under different keys and never collide.
|
|
202
|
+
const superKey = Symbol(key);
|
|
203
|
+
cacheKeys.push(superKey);
|
|
204
|
+
convertToLazyBoundMethod(prototype, key, superKey, desc.value);
|
|
176
205
|
} else if (desc.get) {
|
|
177
|
-
|
|
206
|
+
const superKey = Symbol(key);
|
|
207
|
+
cacheKeys.push(superKey);
|
|
208
|
+
convertToLazyRef(prototype, key, superKey, desc.get, desc.set);
|
|
178
209
|
}
|
|
179
210
|
}
|
|
180
211
|
|
|
181
|
-
// Mark this
|
|
212
|
+
// Mark this prototype level as processed; the marker carries the
|
|
213
|
+
// level's engine cache keys (see PROCESSED above).
|
|
182
214
|
defineProperty(prototype, PROCESSED, {
|
|
183
|
-
|
|
184
|
-
enumerable: false,
|
|
185
|
-
value: true,
|
|
215
|
+
value: cacheKeys,
|
|
186
216
|
});
|
|
187
217
|
}
|
|
188
218
|
|
|
189
|
-
// Inject
|
|
190
|
-
|
|
191
|
-
|
|
219
|
+
// Inject the $watch/$watchEffect/$stopEffects helpers. The guard is an
|
|
220
|
+
// IDEMPOTENCY SENTINEL only — one key stands for the whole trio, so a
|
|
221
|
+
// repeated Reactive() call (diamond imports, duplicate bundled engine
|
|
222
|
+
// copies) skips re-injection. It is NOT override protection: the $-helper
|
|
223
|
+
// names are reserved engine API (richer cleanup is an ordinary method
|
|
224
|
+
// that calls $stopEffects() itself — ivue never auto-calls user code).
|
|
225
|
+
if (!hasOwn(targetClass.prototype, '$stopEffects')) {
|
|
226
|
+
/**
|
|
227
|
+
* Register a watcher in this instance's lazily-created effect scope.
|
|
228
|
+
* The scope is allocated only on first use, so pure-data classes that
|
|
229
|
+
* never watch pay nothing. Has the same signature as Vue's `watch`.
|
|
230
|
+
*/
|
|
231
|
+
defineProperty(targetClass.prototype, '$watch', {
|
|
232
|
+
enumerable: false,
|
|
233
|
+
configurable: true,
|
|
234
|
+
writable: true,
|
|
235
|
+
value: function (this: any, ...args: any[]) {
|
|
236
|
+
const raw = resolveRaw(this);
|
|
237
|
+
const scope =
|
|
238
|
+
raw[SCOPE] ?? (raw[SCOPE] = effectScope(true /* detached */));
|
|
239
|
+
return scope.run(() => (watch as any)(...args));
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Register a watchEffect in the same lazy per-instance scope.
|
|
245
|
+
*/
|
|
246
|
+
defineProperty(targetClass.prototype, '$watchEffect', {
|
|
247
|
+
enumerable: false,
|
|
248
|
+
configurable: true,
|
|
249
|
+
writable: true,
|
|
250
|
+
value: function (this: any, ...args: any[]) {
|
|
251
|
+
const raw = resolveRaw(this);
|
|
252
|
+
const scope =
|
|
253
|
+
raw[SCOPE] ?? (raw[SCOPE] = effectScope(true /* detached */));
|
|
254
|
+
return scope.run(() => (watchEffect as any)(...args));
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Tear down the instance: stop its effect scope (any watchers created via
|
|
260
|
+
* $watch) and drop all cached cells so refs/computeds become collectable.
|
|
261
|
+
* No hooks — ivue never calls user code; compose richer cleanup as an
|
|
262
|
+
* ordinary method that does its own work and then calls $stopEffects().
|
|
263
|
+
*/
|
|
264
|
+
defineProperty(targetClass.prototype, '$stopEffects', {
|
|
192
265
|
enumerable: false,
|
|
193
266
|
configurable: true,
|
|
194
267
|
writable: true,
|
|
195
268
|
value: function (this: any) {
|
|
196
|
-
const raw =
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
269
|
+
const raw = resolveRaw(this);
|
|
270
|
+
try {
|
|
271
|
+
const scope = raw[SCOPE];
|
|
272
|
+
if (scope) scope.stop();
|
|
273
|
+
} finally {
|
|
274
|
+
// SCOPE is ivue-owned but is not a method/getter cache key.
|
|
275
|
+
delete raw[SCOPE];
|
|
276
|
+
|
|
277
|
+
// Each processed prototype's PROCESSED marker carries the
|
|
278
|
+
// symbols it may cache on an instance. Walk Child -> Base and
|
|
279
|
+
// remove only those known keys.
|
|
280
|
+
let prototype = getPrototypeOf(raw);
|
|
281
|
+
while (prototype && prototype !== objectPrototype) {
|
|
282
|
+
const cacheKeys = prototype[PROCESSED] as
|
|
283
|
+
| readonly symbol[]
|
|
284
|
+
| undefined;
|
|
285
|
+
if (cacheKeys) {
|
|
286
|
+
for (const cacheKey of cacheKeys) delete raw[cacheKey];
|
|
287
|
+
}
|
|
288
|
+
prototype = getPrototypeOf(prototype);
|
|
206
289
|
}
|
|
207
|
-
delete raw[symbol];
|
|
208
290
|
}
|
|
209
291
|
},
|
|
210
292
|
});
|
|
@@ -273,34 +355,47 @@ export const isClass = (val: any): boolean => {
|
|
|
273
355
|
* but leave primitive properties and functions intact so that
|
|
274
356
|
* the final object is fully defineComponent() style compatible.
|
|
275
357
|
*
|
|
358
|
+
* The default cloner is the native `structuredClone` (zero-dependency, handles
|
|
359
|
+
* plain data, Map/Set/Date/typed arrays, circular refs). For defaults that
|
|
360
|
+
* contain class instances or functions — which `structuredClone` cannot clone —
|
|
361
|
+
* pass a `customCloner` such as lodash `cloneDeep`.
|
|
362
|
+
*
|
|
276
363
|
* @param defaults Regular object of default key -> values
|
|
277
364
|
* @param typedProps Props declared in defineComponent() style with type and possibly required declared, but without default
|
|
365
|
+
* @param customCloner Optional cloner used for object/array defaults (defaults to structuredClone)
|
|
278
366
|
* @returns Props declared in defineComponent() style with all properties having default property declared.
|
|
279
367
|
*/
|
|
280
368
|
export const propsWithDefaults = <T extends VuePropsObject>(
|
|
281
369
|
defaults: Record<string, any>,
|
|
282
370
|
typedProps: T,
|
|
283
371
|
// Optional: Allows user to pass a custom cloner if structuredClone isn't enough
|
|
284
|
-
customCloner?: (val: any) => any
|
|
372
|
+
customCloner?: (val: any) => any,
|
|
285
373
|
): VuePropsWithDefaults<T> => {
|
|
374
|
+
// NON-MUTATING: descriptor objects are routinely SHARED between props
|
|
375
|
+
// maps (`{ ...baseParamsTypes, extra }` — the spread copies the outer
|
|
376
|
+
// object but every inner `{ type }` descriptor stays the same reference).
|
|
377
|
+
// Writing `.default` in place would silently rewrite the base component's
|
|
378
|
+
// defaults; each descriptor is copied instead.
|
|
379
|
+
const result: Record<string, any> = {};
|
|
286
380
|
for (const prop in typedProps) {
|
|
287
381
|
const def = defaults?.[prop];
|
|
288
382
|
const typed = typedProps[prop];
|
|
383
|
+
result[prop] = { ...typed };
|
|
289
384
|
|
|
290
385
|
if (typed.required || def === undefined) continue;
|
|
291
386
|
|
|
292
387
|
if (typeof def === 'object' && def !== null) {
|
|
293
|
-
|
|
388
|
+
result[prop].default = () =>
|
|
294
389
|
customCloner ? customCloner(def) : structuredClone(def);
|
|
295
390
|
} else {
|
|
296
391
|
if (isClass(def)) {
|
|
297
|
-
|
|
392
|
+
result[prop].default = () => def;
|
|
298
393
|
} else {
|
|
299
|
-
|
|
394
|
+
result[prop].default = def;
|
|
300
395
|
}
|
|
301
396
|
}
|
|
302
397
|
}
|
|
303
|
-
return
|
|
398
|
+
return result as VuePropsWithDefaults<T>;
|
|
304
399
|
};
|
|
305
400
|
|
|
306
401
|
/**
|
|
@@ -310,29 +405,105 @@ type GetterKeys<T> = {
|
|
|
310
405
|
[K in keyof T]: T[K] extends (...args: any[]) => any
|
|
311
406
|
? never
|
|
312
407
|
: T[K] extends undefined
|
|
313
|
-
|
|
314
|
-
|
|
408
|
+
? never
|
|
409
|
+
: K;
|
|
315
410
|
}[keyof T];
|
|
316
411
|
|
|
317
412
|
type GetterReturn<T, K extends keyof T> = T[K] extends (...args: any[]) => any
|
|
318
413
|
? never
|
|
319
414
|
: T[K];
|
|
320
415
|
type WritableComputedLike = Ref<any> & { set: (...args: any[]) => any };
|
|
321
|
-
type IsWritableGetter<R> =
|
|
322
|
-
? true
|
|
323
|
-
: R extends WritableComputedLike
|
|
324
|
-
? true
|
|
325
|
-
: false;
|
|
416
|
+
type IsWritableGetter<R> =
|
|
417
|
+
R extends Ref<any> ? true : R extends WritableComputedLike ? true : false;
|
|
326
418
|
|
|
327
419
|
type WritableGetters<T> = {
|
|
328
|
-
[
|
|
329
|
-
|
|
330
|
-
|
|
420
|
+
[
|
|
421
|
+
K in GetterKeys<T> as IsWritableGetter<GetterReturn<T, K>> extends true
|
|
422
|
+
? K
|
|
423
|
+
: never
|
|
424
|
+
]-?: T[K];
|
|
331
425
|
};
|
|
332
426
|
|
|
333
427
|
export type ReactiveInstance<T> = T &
|
|
334
|
-
WritableGetters<T> & {
|
|
428
|
+
WritableGetters<T> & {
|
|
429
|
+
/** Register a watcher in the instance's lazy effect scope (same signature as Vue `watch`). */
|
|
430
|
+
$watch: typeof watch;
|
|
431
|
+
/** Register a watchEffect in the instance's lazy effect scope (same signature as Vue `watchEffect`). */
|
|
432
|
+
$watchEffect: typeof watchEffect;
|
|
433
|
+
/** Stop the instance's effect scope and drop cached cells. */
|
|
434
|
+
$stopEffects: () => void;
|
|
435
|
+
};
|
|
335
436
|
|
|
336
437
|
export type ReactiveClass<C extends new (...args: any) => any> = new (
|
|
337
438
|
...args: ConstructorParameters<C>
|
|
338
439
|
) => ReactiveInstance<InstanceType<C>>;
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Component-authoring type utilities (types only — erased at build time).
|
|
443
|
+
* These complement `propsWithDefaults` for the params/defaults component
|
|
444
|
+
* architecture: object-declared emits, extensible slots, and precise
|
|
445
|
+
* handler-parameter extraction.
|
|
446
|
+
*/
|
|
447
|
+
|
|
448
|
+
/** Any JavaScript function of any type. */
|
|
449
|
+
export type AnyFn = (...args: any[]) => any;
|
|
450
|
+
|
|
451
|
+
/** Convert Record to Union Type. */
|
|
452
|
+
export type RecordToUnion<T extends Record<string, any>> = T[keyof T];
|
|
453
|
+
|
|
454
|
+
/** Gets object T property by key K. */
|
|
455
|
+
export type ValueOf<T extends Record<any, any>, K extends keyof T> = T[K];
|
|
456
|
+
|
|
457
|
+
/** Convert Union Type to Intersection Type. */
|
|
458
|
+
export type UnionToIntersection<U> = (
|
|
459
|
+
U extends any ? (k: U) => void : never
|
|
460
|
+
) extends (k: infer I) => void
|
|
461
|
+
? I
|
|
462
|
+
: never;
|
|
463
|
+
|
|
464
|
+
/** Prefix keys of an interface T with a prefix P. */
|
|
465
|
+
export type PrefixKeys<T, P extends string | undefined = undefined> = {
|
|
466
|
+
[K in Extract<keyof T, string> as P extends string ? `${P}${K}` : K]: T[K];
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
/** Extracts object-declared emit validators into the emit-function interface. */
|
|
470
|
+
export type ExtractEmitTypes<T extends Record<string, any>> =
|
|
471
|
+
UnionToIntersection<
|
|
472
|
+
RecordToUnion<{
|
|
473
|
+
[K in keyof T]: (evt: K, ...args: Parameters<T[K]>) => void;
|
|
474
|
+
}>
|
|
475
|
+
>;
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Extract properties as all-assigned (non-optional) because every one of
|
|
479
|
+
* them carries a default.
|
|
480
|
+
*/
|
|
481
|
+
export type ExtractPropDefaultTypes<O> = {
|
|
482
|
+
[K in keyof O]: K extends keyof ExtractPropTypes<O>
|
|
483
|
+
? ExtractPropTypes<O>[K]
|
|
484
|
+
: never;
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Extend a slots interface T with prefixed 'before--' & 'after--' slots to
|
|
489
|
+
* create fully extensible wrapped components.
|
|
490
|
+
*/
|
|
491
|
+
export type ExtendSlots<T> = PrefixKeys<T, 'before--'> &
|
|
492
|
+
T &
|
|
493
|
+
PrefixKeys<T, 'after--'>;
|
|
494
|
+
|
|
495
|
+
/** Get function arguments Parameters<F> parameter by index K. */
|
|
496
|
+
export type FnParameter<F extends AnyFn, K extends number> = Parameters<F>[K];
|
|
497
|
+
|
|
498
|
+
/** Get interface T property K's function arguments as Parameters. */
|
|
499
|
+
export type IFnParameters<
|
|
500
|
+
T extends Record<any, any>,
|
|
501
|
+
K extends string,
|
|
502
|
+
> = Parameters<Required<Pick<T, K>>[K]>;
|
|
503
|
+
|
|
504
|
+
/** Get interface T property P's function parameter by index K. */
|
|
505
|
+
export type IFnParameter<
|
|
506
|
+
T extends Record<any, any>,
|
|
507
|
+
P extends keyof T,
|
|
508
|
+
K extends number,
|
|
509
|
+
> = FnParameter<NonNullable<T[P]> extends AnyFn ? NonNullable<T[P]> : never, K>;
|