ivue 2.5.0 → 2.6.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 +1 -1
- package/dist/LazyShared.d.ts +4 -4
- package/dist/Reactive.d.ts +18 -8
- package/dist/clone.d.ts +3 -0
- package/dist/extras.cjs +1 -1
- package/dist/extras.d.ts +1 -0
- package/dist/extras.es.js +1 -49
- package/dist/index.cjs +1 -1
- package/dist/index.es.js +1 -103
- package/dist/nestedProps.d.ts +67 -0
- package/lib/LazyShared.ts +4 -4
- package/lib/Reactive.ts +32 -19
- package/lib/Static.ts +16 -0
- package/lib/__tests__/Reactive.vitest.spec.ts +45 -1
- package/lib/__tests__/nestedProps.vitest.spec.ts +153 -0
- package/lib/clone.ts +21 -0
- package/lib/extras.ts +1 -0
- package/lib/nestedProps.ts +126 -0
- package/package.json +8 -4
- package/skills/ivue/SKILL.md +236 -102
- package/skills/ivue/constitution.test.ts +2 -2
- package/skills/ivue/ivue-docs-skip.json +37 -0
- package/skills/ivue/ivue-generator-standard.ts +24 -24
- package/skills/ivue/ivue-house-gate.ts +5 -4
- package/skills/ivue/ivue-standards-check.ts +485 -300
package/README.md
CHANGED
|
@@ -189,7 +189,7 @@ export namespace TextSegmentation {
|
|
|
189
189
|
[**Invar**](https://ivue.dev/examples/invar) is ivue at full scale: a
|
|
190
190
|
complete terminal IDE — editor, workspace search, git, terminals, LSP,
|
|
191
191
|
agents — running on ivue classes under Bun, with no DOM and no Vue
|
|
192
|
-
components. **
|
|
192
|
+
components. **108,000 source lines, 384 classes, 39 invariant contracts,
|
|
193
193
|
zero import cycles**, built almost entirely by AI agents holding the
|
|
194
194
|
[Standard](https://ivue.dev/guide/standard) as their base discipline.
|
|
195
195
|
|
package/dist/LazyShared.d.ts
CHANGED
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
* entry stays minimal.
|
|
36
36
|
*/
|
|
37
37
|
export declare class LazyShared<T> {
|
|
38
|
-
|
|
38
|
+
protected readonly make: () => T;
|
|
39
39
|
constructor(make: () => T);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
protected constructed: boolean;
|
|
41
|
+
protected constructing: boolean;
|
|
42
|
+
protected stored: T | null;
|
|
43
43
|
get value(): T;
|
|
44
44
|
/**
|
|
45
45
|
* Drop the constructed value; the next read constructs again. For
|
package/dist/Reactive.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { watch, watchEffect, type ExtractPropTypes, type Ref } from 'vue';
|
|
2
|
+
export { clone } from './clone';
|
|
2
3
|
/**
|
|
3
4
|
* Create a reactive class.
|
|
4
5
|
* @param targetClass The class to make reactive.
|
|
@@ -54,14 +55,14 @@ export declare function isClass(val: any): boolean;
|
|
|
54
55
|
* but leave primitive properties and functions intact so that
|
|
55
56
|
* the final object is fully defineComponent() style compatible.
|
|
56
57
|
*
|
|
57
|
-
* The default cloner
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* The default cloner copies acyclic plain-object and array trees while
|
|
59
|
+
* retaining callbacks, class constructors and opaque objects by reference.
|
|
60
|
+
* For independent Map/Set/Date values or cyclic data, pass a `customCloner`
|
|
61
|
+
* such as `structuredClone` when the values support it.
|
|
61
62
|
*
|
|
62
63
|
* @param defaults Regular object of default key -> values
|
|
63
64
|
* @param typedProps Props declared in defineComponent() style with type and possibly required declared, but without default
|
|
64
|
-
* @param customCloner Optional cloner used for object/array defaults (defaults to
|
|
65
|
+
* @param customCloner Optional cloner used for object/array defaults (defaults to clone)
|
|
65
66
|
* @returns Props declared in defineComponent() style with all properties having default property declared.
|
|
66
67
|
*/
|
|
67
68
|
export declare function propsWithDefaults<T extends VuePropsObject>(defaults: Record<string, any>, typedProps: T, customCloner?: (val: any) => any): VuePropsWithDefaults<T>;
|
|
@@ -79,7 +80,16 @@ declare type IsWritableGetter<R> = R extends Ref<any> ? true : R extends Writabl
|
|
|
79
80
|
declare type WritableGetters<T> = {
|
|
80
81
|
[K in GetterKeys<T> as IsWritableGetter<GetterReturn<T, K>> extends true ? K : never]-?: T[K];
|
|
81
82
|
};
|
|
82
|
-
|
|
83
|
+
/**
|
|
84
|
+
* The members `Reactive()` installs on every instance's prototype. A raw
|
|
85
|
+
* class body cannot see them (the transform runs after the class is typed),
|
|
86
|
+
* so a class that calls them merges this interface into its own instance
|
|
87
|
+
* type — one declaration beside the class, zero runtime:
|
|
88
|
+
*
|
|
89
|
+
* class $Session { constructor() { this.$watch(…) } }
|
|
90
|
+
* interface $Session extends ReactiveHelpers {}
|
|
91
|
+
*/
|
|
92
|
+
export interface ReactiveHelpers {
|
|
83
93
|
/** Register a watcher in the instance's lazy effect scope (same signature as Vue `watch`). */
|
|
84
94
|
$watch: typeof watch;
|
|
85
95
|
/** Register a watchEffect in the instance's lazy effect scope (same signature as Vue `watchEffect`). */
|
|
@@ -90,7 +100,8 @@ export declare type ReactiveInstance<T> = T & WritableGetters<T> & {
|
|
|
90
100
|
$stopEffects: (options?: {
|
|
91
101
|
reset?: boolean;
|
|
92
102
|
}) => void;
|
|
93
|
-
}
|
|
103
|
+
}
|
|
104
|
+
export declare type ReactiveInstance<T> = T & WritableGetters<T> & ReactiveHelpers;
|
|
94
105
|
export declare type ReactiveClass<C extends new (...args: any) => any> = {
|
|
95
106
|
[Key in keyof C]: C[Key];
|
|
96
107
|
} & (new (...args: ConstructorParameters<C>) => ReactiveInstance<InstanceType<C>>);
|
|
@@ -151,4 +162,3 @@ export declare type FnParameter<F extends AnyFn, K extends number> = Parameters<
|
|
|
151
162
|
export declare type IFnParameters<T extends Record<any, any>, K extends string> = Parameters<Required<Pick<T, K>>[K]>;
|
|
152
163
|
/** Get interface T property P's function parameter by index K. */
|
|
153
164
|
export declare type IFnParameter<T extends Record<any, any>, P extends keyof T, K extends number> = FnParameter<NonNullable<T[P]> extends AnyFn ? NonNullable<T[P]> : never, K>;
|
|
154
|
-
export {};
|
package/dist/clone.d.ts
ADDED
package/dist/extras.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=Object.hasOwn,e=new Set;function o(t){if(Array.isArray(t)){const e=new Array(t.length);for(let n=0;n<t.length;n++)n in t&&(e[n]=o(t[n]));return e}if(null===t||"object"!=typeof t||t.constructor!==Object&&void 0!==t.constructor)return t;const e={__proto__:Object.getPrototypeOf(t),...t};for(const t in e)e[t]=o(e[t]);return e}function n(t){return null!==t&&"object"==typeof t&&(t.constructor===Object||void 0===t.constructor)}function r(t,e,o){for(const s in e){const c=t[s],i=e[s];void 0===c?t[s]=o(i):n(c)&&n(i)&&r(c,i,o)}}exports.LazyShared=class{constructor(t){this.make=t,this.constructed=!1,this.constructing=!1,this.stored=null}get value(){if(!this.constructed){if(this.constructing)throw new Error("LazyShared thunk cycle: this cell is read inside its own construction. Break the dependency between the two thunks.");this.constructing=!0;try{this.stored=this.make(),this.constructed=!0}finally{this.constructing=!1}}return this.stored}reset(){this.constructed=!1,this.stored=null}},exports.Static=function(o){var n;const r=class extends o{},s=new Set;for(let c=o;c!==Function.prototype;c=Object.getPrototypeOf(c))for(const o of Reflect.ownKeys(c)){if(s.has(o)||(s.add(o),"symbol"==typeof o&&null!=(n=Symbol.keyFor(o))&&n.startsWith("ivue.static"))||e.has(o))continue;const i=Object.getOwnPropertyDescriptor(c,o);if("function"==typeof i.value){const n=i.value,s="string"==typeof o?Symbol.for(`ivue.staticBound.${o}`):Symbol("ivue.staticBound");e.add(s),Object.defineProperty(r,o,{configurable:!0,enumerable:i.enumerable,get(){return t(this,s)||Object.defineProperty(this,s,{configurable:!0,value:n.bind(this)}),this[s]}})}else if(i.get&&!i.set&&"string"==typeof o&&o.startsWith("$")){const n=i.get,s=Symbol.for(`ivue.staticCache.${o}`);e.add(s),Object.defineProperty(r,o,{configurable:!0,enumerable:i.enumerable,get(){return t(this,s)||Object.defineProperty(this,s,{configurable:!0,value:n.call(this)}),this[s]}})}}return r},exports.nestedProps=function(t,e,s=o){for(const o in e){const c=t[o],i=e[o];n(c)&&n(i)&&r(c,i,s)}return t};
|
package/dist/extras.d.ts
CHANGED
package/dist/extras.es.js
CHANGED
|
@@ -1,49 +1 @@
|
|
|
1
|
-
const
|
|
2
|
-
function l(o) {
|
|
3
|
-
var a;
|
|
4
|
-
const i = class extends o {
|
|
5
|
-
}, u = /* @__PURE__ */ new Set();
|
|
6
|
-
for (let n = o; n !== Function.prototype; n = Object.getPrototypeOf(n))
|
|
7
|
-
for (const t of Reflect.ownKeys(n)) {
|
|
8
|
-
if (u.has(t) || (u.add(t), typeof t == "symbol" && ((a = Symbol.keyFor(t)) == null ? void 0 : a.startsWith("ivue.static"))) || c.has(t))
|
|
9
|
-
continue;
|
|
10
|
-
const s = Object.getOwnPropertyDescriptor(n, t);
|
|
11
|
-
if (typeof s.value == "function") {
|
|
12
|
-
const r = s.value, e = typeof t == "string" ? Symbol.for(`ivue.staticBound.${t}`) : Symbol("ivue.staticBound");
|
|
13
|
-
c.add(e), Object.defineProperty(i, t, { configurable: !0, enumerable: s.enumerable, get() {
|
|
14
|
-
return h(this, e) || Object.defineProperty(this, e, { configurable: !0, value: r.bind(this) }), this[e];
|
|
15
|
-
} });
|
|
16
|
-
} else if (s.get && !s.set && typeof t == "string" && t.startsWith("$")) {
|
|
17
|
-
const r = s.get, e = Symbol.for(`ivue.staticCache.${t}`);
|
|
18
|
-
c.add(e), Object.defineProperty(i, t, { configurable: !0, enumerable: s.enumerable, get() {
|
|
19
|
-
return h(this, e) || Object.defineProperty(this, e, { configurable: !0, value: r.call(this) }), this[e];
|
|
20
|
-
} });
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return i;
|
|
24
|
-
}
|
|
25
|
-
class f {
|
|
26
|
-
constructor(i) {
|
|
27
|
-
this.make = i, this.constructed = !1, this.constructing = !1, this.stored = null;
|
|
28
|
-
}
|
|
29
|
-
get value() {
|
|
30
|
-
if (!this.constructed) {
|
|
31
|
-
if (this.constructing)
|
|
32
|
-
throw new Error("LazyShared thunk cycle: this cell is read inside its own construction. Break the dependency between the two thunks.");
|
|
33
|
-
this.constructing = !0;
|
|
34
|
-
try {
|
|
35
|
-
this.stored = this.make(), this.constructed = !0;
|
|
36
|
-
} finally {
|
|
37
|
-
this.constructing = !1;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return this.stored;
|
|
41
|
-
}
|
|
42
|
-
reset() {
|
|
43
|
-
this.constructed = !1, this.stored = null;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
export {
|
|
47
|
-
f as LazyShared,
|
|
48
|
-
l as Static
|
|
49
|
-
};
|
|
1
|
+
const t=Object.hasOwn,e=/* @__PURE__ */new Set;function n(n){var o;const r=class extends n{},s=/* @__PURE__ */new Set;for(let c=n;c!==Function.prototype;c=Object.getPrototypeOf(c))for(const n of Reflect.ownKeys(c)){if(s.has(n)||(s.add(n),"symbol"==typeof n&&null!=(o=Symbol.keyFor(n))&&o.startsWith("ivue.static"))||e.has(n))continue;const i=Object.getOwnPropertyDescriptor(c,n);if("function"==typeof i.value){const o=i.value,s="string"==typeof n?Symbol.for(`ivue.staticBound.${n}`):Symbol("ivue.staticBound");e.add(s),Object.defineProperty(r,n,{configurable:!0,enumerable:i.enumerable,get(){return t(this,s)||Object.defineProperty(this,s,{configurable:!0,value:o.bind(this)}),this[s]}})}else if(i.get&&!i.set&&"string"==typeof n&&n.startsWith("$")){const o=i.get,s=Symbol.for(`ivue.staticCache.${n}`);e.add(s),Object.defineProperty(r,n,{configurable:!0,enumerable:i.enumerable,get(){return t(this,s)||Object.defineProperty(this,s,{configurable:!0,value:o.call(this)}),this[s]}})}}return r}class o{constructor(t){this.make=t,this.constructed=!1,this.constructing=!1,this.stored=null}get value(){if(!this.constructed){if(this.constructing)throw new Error("LazyShared thunk cycle: this cell is read inside its own construction. Break the dependency between the two thunks.");this.constructing=!0;try{this.stored=this.make(),this.constructed=!0}finally{this.constructing=!1}}return this.stored}reset(){this.constructed=!1,this.stored=null}}function r(t){if(Array.isArray(t)){const e=new Array(t.length);for(let n=0;n<t.length;n++)n in t&&(e[n]=r(t[n]));return e}if(null===t||"object"!=typeof t||t.constructor!==Object&&void 0!==t.constructor)return t;const e={__proto__:Object.getPrototypeOf(t),...t};for(const t in e)e[t]=r(e[t]);return e}function s(t){return null!==t&&"object"==typeof t&&(t.constructor===Object||void 0===t.constructor)}function c(t,e,n){for(const o in e){const r=t[o],i=e[o];void 0===r?t[o]=n(i):s(r)&&s(i)&&c(r,i,n)}}function i(t,e,n=r){for(const o in e){const r=t[o],i=e[o];s(r)&&s(i)&&c(r,i,n)}return t}export{o as LazyShared,n as Static,i as nestedProps};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("vue");function e(t){if(Array.isArray(t)){const o=new Array(t.length);for(let n=0;n<t.length;n++)n in t&&(o[n]=e(t[n]));return o}if(null===t||"object"!=typeof t||t.constructor!==Object&&void 0!==t.constructor)return t;const o={__proto__:Object.getPrototypeOf(t),...t};for(const t in o)o[t]=e(o[t]);return o}const o=Object.hasOwn,n=Object.getPrototypeOf,r=Object.getOwnPropertyDescriptor,c=Object.getOwnPropertyNames,u=Object.defineProperty,s=Object.prototype,i=Symbol.for("ivue.raw"),f=Symbol.for("ivue.scope"),l=Symbol.for("ivue.processed");function a(e){const o=t.toRaw(e);if(o!==e)return o[i]??(o[i]=o);const n=e[i];return n?n===e?n:t.toRaw(n):e[i]=e}function p(t,e,o,n){u(t,e,{configurable:!0,enumerable:!1,get(){const t=a(this);return t[o]??(t[o]=n.bind(t))},set(t){a(this)[o]=t}})}function b(e,o,n,r,c){const s="$"===o[0];u(e,o,{configurable:!0,enumerable:!1,get:function(){const i=a(this);if(n in i)return i[n];const f=r.call(i);return s?(i[n]=f,f):(t.isRef(f)?i[n]=f:u(e,o,{configurable:!0,enumerable:!1,get(){return r.call(a(this))},set:c?function(t){return c.call(a(this),t)}:void 0}),f)},set:c?function(t){return c.call(a(this),t)}:void 0})}function y(t){var e;return!("function"!=typeof t||!t.prototype||null!=(e=r(t,"prototype"))&&e.writable)}exports.Reactive=function(e){const i=[];let y=e.prototype;for(;y&&y!==s;)i.push(y),y=n(y);i.reverse();for(const t of i){if(o(t,l))continue;const e=c(t),n=[];for(const o of e){if("constructor"===o)continue;const e=r(t,o);if("function"==typeof e.value){const r=Symbol(o);n.push(r),p(t,o,r,e.value)}else if(e.get){const r=Symbol(o);n.push(r),b(t,o,r,e.get,e.set)}}u(t,l,{value:n})}return o(e.prototype,"$stopEffects")||(u(e.prototype,"$watch",{enumerable:!1,configurable:!0,writable:!0,value:function(...e){const o=a(this);return(o[f]??(o[f]=t.effectScope(!0))).run((()=>t.watch(...e)))}}),u(e.prototype,"$watchEffect",{enumerable:!1,configurable:!0,writable:!0,value:function(...e){const o=a(this);return(o[f]??(o[f]=t.effectScope(!0))).run((()=>t.watchEffect(...e)))}}),u(e.prototype,"$stopEffects",{enumerable:!1,configurable:!0,writable:!0,value:function(t){const e=a(this);try{const t=e[f];t&&t.stop()}finally{if(delete e[f],!1!==(null==t?void 0:t.reset)){let t=n(e);for(;t&&t!==s;){const o=t[l];if(o)for(const t of o)delete e[t];t=n(t)}}}}})),e},exports.clone=e,exports.definePropTypes=function(t){return t},exports.isClass=y,exports.propsWithDefaults=function(t,o,n){const r={};for(const c in o){const u=null==t?void 0:t[c],s=o[c];r[c]={...s},!s.required&&void 0!==u&&("object"==typeof u&&null!==u?r[c].default=()=>n?n(u):e(u):y(u)?r[c].default=()=>u:r[c].default=u)}return r};
|
package/dist/index.es.js
CHANGED
|
@@ -1,103 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
const g = Object.hasOwn, b = Object.getPrototypeOf, v = Object.getOwnPropertyDescriptor, S = Object.getOwnPropertyNames, a = Object.defineProperty, m = Object.prototype, p = Symbol.for("ivue.raw"), l = Symbol.for("ivue.scope"), y = Symbol.for("ivue.processed");
|
|
3
|
-
function f(t) {
|
|
4
|
-
const c = d(t);
|
|
5
|
-
if (c !== t)
|
|
6
|
-
return c[p] ?? (c[p] = c);
|
|
7
|
-
const r = t[p];
|
|
8
|
-
return r ? r === t ? r : d(r) : t[p] = t;
|
|
9
|
-
}
|
|
10
|
-
function P(t, c, r, o) {
|
|
11
|
-
a(t, c, { configurable: !0, enumerable: !1, get() {
|
|
12
|
-
const e = f(this);
|
|
13
|
-
return e[r] ?? (e[r] = o.bind(e));
|
|
14
|
-
}, set(e) {
|
|
15
|
-
f(this)[r] = e;
|
|
16
|
-
} });
|
|
17
|
-
}
|
|
18
|
-
function $(t, c, r, o, e) {
|
|
19
|
-
const n = c[0] === "$";
|
|
20
|
-
a(t, c, { configurable: !0, enumerable: !1, get: function() {
|
|
21
|
-
const s = f(this);
|
|
22
|
-
if (r in s)
|
|
23
|
-
return s[r];
|
|
24
|
-
const u = o.call(s);
|
|
25
|
-
return n ? (s[r] = u, u) : (j(u) ? s[r] = u : a(t, c, { configurable: !0, enumerable: !1, get() {
|
|
26
|
-
return o.call(f(this));
|
|
27
|
-
}, set: e ? function(i) {
|
|
28
|
-
return e.call(f(this), i);
|
|
29
|
-
} : void 0 }), u);
|
|
30
|
-
}, set: e ? function(s) {
|
|
31
|
-
return e.call(f(this), s);
|
|
32
|
-
} : void 0 });
|
|
33
|
-
}
|
|
34
|
-
function C(t) {
|
|
35
|
-
const c = [];
|
|
36
|
-
let r = t.prototype;
|
|
37
|
-
for (; r && r !== m; )
|
|
38
|
-
c.push(r), r = b(r);
|
|
39
|
-
c.reverse();
|
|
40
|
-
for (const o of c) {
|
|
41
|
-
if (g(o, y))
|
|
42
|
-
continue;
|
|
43
|
-
const e = S(o), n = [];
|
|
44
|
-
for (const s of e) {
|
|
45
|
-
if (s === "constructor")
|
|
46
|
-
continue;
|
|
47
|
-
const u = v(o, s);
|
|
48
|
-
if (typeof u.value == "function") {
|
|
49
|
-
const i = Symbol(s);
|
|
50
|
-
n.push(i), P(o, s, i, u.value);
|
|
51
|
-
} else if (u.get) {
|
|
52
|
-
const i = Symbol(s);
|
|
53
|
-
n.push(i), $(o, s, i, u.get, u.set);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
a(o, y, { value: n });
|
|
57
|
-
}
|
|
58
|
-
return g(t.prototype, "$stopEffects") || (a(t.prototype, "$watch", { enumerable: !1, configurable: !0, writable: !0, value: function(...o) {
|
|
59
|
-
const e = f(this);
|
|
60
|
-
return (e[l] ?? (e[l] = h(!0))).run(() => w(...o));
|
|
61
|
-
} }), a(t.prototype, "$watchEffect", { enumerable: !1, configurable: !0, writable: !0, value: function(...o) {
|
|
62
|
-
const e = f(this);
|
|
63
|
-
return (e[l] ?? (e[l] = h(!0))).run(() => O(...o));
|
|
64
|
-
} }), a(t.prototype, "$stopEffects", { enumerable: !1, configurable: !0, writable: !0, value: function(o) {
|
|
65
|
-
const e = f(this);
|
|
66
|
-
try {
|
|
67
|
-
const n = e[l];
|
|
68
|
-
n && n.stop();
|
|
69
|
-
} finally {
|
|
70
|
-
if (delete e[l], (o == null ? void 0 : o.reset) !== !1) {
|
|
71
|
-
let n = b(e);
|
|
72
|
-
for (; n && n !== m; ) {
|
|
73
|
-
const s = n[y];
|
|
74
|
-
if (s)
|
|
75
|
-
for (const u of s)
|
|
76
|
-
delete e[u];
|
|
77
|
-
n = b(n);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
} })), t;
|
|
82
|
-
}
|
|
83
|
-
function E(t) {
|
|
84
|
-
var c;
|
|
85
|
-
return typeof t == "function" && !!t.prototype && !((c = v(t, "prototype")) != null && c.writable);
|
|
86
|
-
}
|
|
87
|
-
function D(t, c, r) {
|
|
88
|
-
const o = {};
|
|
89
|
-
for (const e in c) {
|
|
90
|
-
const n = t == null ? void 0 : t[e], s = c[e];
|
|
91
|
-
o[e] = { ...s }, s.required || n === void 0 || (typeof n == "object" && n !== null ? o[e].default = () => r ? r(n) : structuredClone(n) : E(n) ? o[e].default = () => n : o[e].default = n);
|
|
92
|
-
}
|
|
93
|
-
return o;
|
|
94
|
-
}
|
|
95
|
-
function q(t) {
|
|
96
|
-
return t;
|
|
97
|
-
}
|
|
98
|
-
export {
|
|
99
|
-
C as Reactive,
|
|
100
|
-
q as definePropTypes,
|
|
101
|
-
E as isClass,
|
|
102
|
-
D as propsWithDefaults
|
|
103
|
-
};
|
|
1
|
+
import{effectScope as t,watch as e,watchEffect as o,toRaw as n,isRef as r}from"vue";function c(t){if(Array.isArray(t)){const e=new Array(t.length);for(let o=0;o<t.length;o++)o in t&&(e[o]=c(t[o]));return e}if(null===t||"object"!=typeof t||t.constructor!==Object&&void 0!==t.constructor)return t;const e={__proto__:Object.getPrototypeOf(t),...t};for(const t in e)e[t]=c(e[t]);return e}const s=Object.hasOwn,u=Object.getPrototypeOf,i=Object.getOwnPropertyDescriptor,f=Object.getOwnPropertyNames,l=Object.defineProperty,a=Object.prototype,p=Symbol.for("ivue.raw"),b=Symbol.for("ivue.scope"),y=Symbol.for("ivue.processed");function h(t){const e=n(t);if(e!==t)return e[p]??(e[p]=e);const o=t[p];return o?o===t?o:n(o):t[p]=t}function v(t,e,o,n){l(t,e,{configurable:!0,enumerable:!1,get(){const t=h(this);return t[o]??(t[o]=n.bind(t))},set(t){h(this)[o]=t}})}function g(t,e,o,n,c){const s="$"===e[0];l(t,e,{configurable:!0,enumerable:!1,get:function(){const u=h(this);if(o in u)return u[o];const i=n.call(u);return s?(u[o]=i,i):(r(i)?u[o]=i:l(t,e,{configurable:!0,enumerable:!1,get(){return n.call(h(this))},set:c?function(t){return c.call(h(this),t)}:void 0}),i)},set:c?function(t){return c.call(h(this),t)}:void 0})}function d(n){const r=[];let c=n.prototype;for(;c&&c!==a;)r.push(c),c=u(c);r.reverse();for(const t of r){if(s(t,y))continue;const e=f(t),o=[];for(const n of e){if("constructor"===n)continue;const e=i(t,n);if("function"==typeof e.value){const r=Symbol(n);o.push(r),v(t,n,r,e.value)}else if(e.get){const r=Symbol(n);o.push(r),g(t,n,r,e.get,e.set)}}l(t,y,{value:o})}return s(n.prototype,"$stopEffects")||(l(n.prototype,"$watch",{enumerable:!1,configurable:!0,writable:!0,value:function(...o){const n=h(this);return(n[b]??(n[b]=t(!0))).run((()=>e(...o)))}}),l(n.prototype,"$watchEffect",{enumerable:!1,configurable:!0,writable:!0,value:function(...e){const n=h(this);return(n[b]??(n[b]=t(!0))).run((()=>o(...e)))}}),l(n.prototype,"$stopEffects",{enumerable:!1,configurable:!0,writable:!0,value:function(t){const e=h(this);try{const t=e[b];t&&t.stop()}finally{if(delete e[b],!1!==(null==t?void 0:t.reset)){let t=u(e);for(;t&&t!==a;){const o=t[y];if(o)for(const t of o)delete e[t];t=u(t)}}}}})),n}function m(t){var e;return!("function"!=typeof t||!t.prototype||null!=(e=i(t,"prototype"))&&e.writable)}function w(t,e,o){const n={};for(const r in e){const s=null==t?void 0:t[r],u=e[r];n[r]={...u},!u.required&&void 0!==s&&("object"==typeof s&&null!==s?n[r].default=()=>o?o(s):c(s):m(s)?n[r].default=()=>s:n[r].default=s)}return n}function O(t){return t}export{d as Reactive,c as clone,O as definePropTypes,m as isClass,w as propsWithDefaults};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nestedProps(props, defaults)` — fill a nested object prop from its
|
|
3
|
+
* defaults, in place, so the class reads complete props at every depth.
|
|
4
|
+
*
|
|
5
|
+
* Vue resolves a prop's default only when the prop is ABSENT: pass
|
|
6
|
+
* `{ wheel: { gain: 2 } }` for a prop whose default is
|
|
7
|
+
* `{ wheel: { gain: 1, follow: 0.1 }, touch: { … } }` and the component
|
|
8
|
+
* receives exactly the object it was passed — `follow` and `touch` are
|
|
9
|
+
* gone. `propsWithDefaults` decides what the default IS and clones it per
|
|
10
|
+
* instance; it cannot reach inside a supplied object. The fill is possible
|
|
11
|
+
* at all because the defaults are a value the class owns — a compiler-only
|
|
12
|
+
* default has nothing to fill from.
|
|
13
|
+
*
|
|
14
|
+
* The semantics are lodash's `defaultsDeep` with arrays taken whole: for
|
|
15
|
+
* every prop whose value and default are both plain objects, each leaf the
|
|
16
|
+
* supplied object lacks is written into it from the default, recursively;
|
|
17
|
+
* a leaf it has is kept. Missing plain-object and array defaults are copied
|
|
18
|
+
* recursively so instances never share those mutable containers. Arrays
|
|
19
|
+
* are never merged; class instances and functions retain their identity. Vue's
|
|
20
|
+
* props proxy is shallow, so the nested objects are the parent's own and
|
|
21
|
+
* are written directly; the props object itself is untouched and returned.
|
|
22
|
+
*
|
|
23
|
+
* Call it once, at the seam where props enter the class:
|
|
24
|
+
*
|
|
25
|
+
* constructor(props: Scroller.Props, public emit: Scroller.Emits) {
|
|
26
|
+
* this.props = nestedProps(props, this.self.propsDefaults);
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* The parent passes a stable object: a constant inline literal (Vue hoists
|
|
30
|
+
* it), a `ref`'s value, a store field. An object built anew on every
|
|
31
|
+
* parent render is a new, unfilled object each time.
|
|
32
|
+
*
|
|
33
|
+
* `customCloner` is the same policy knob `propsWithDefaults` has: it copies
|
|
34
|
+
* each default branch written into the props. The default, `clone`, copies
|
|
35
|
+
* plain containers and keeps callbacks, constructors and opaque objects by
|
|
36
|
+
* reference, so no two instances share a mutable default. Pass
|
|
37
|
+
* `structuredClone` when `Date`, `Map` or `Set` defaults need their own
|
|
38
|
+
* copies; pass the identity, `value => value`, only when the caller owns
|
|
39
|
+
* every default it passes — a fresh tree per instance, never a shared one.
|
|
40
|
+
*
|
|
41
|
+
* `NestedPartial<T>` is the matching declaration for the prop's type — the
|
|
42
|
+
* shape an author may pass — and `NestedProps<P, D>` the type of the
|
|
43
|
+
* filled props, where every key both sides carry as an object is complete.
|
|
44
|
+
*
|
|
45
|
+
* Ships from `ivue/extras` (not the reactive core) so the primary `ivue`
|
|
46
|
+
* entry stays minimal.
|
|
47
|
+
*/
|
|
48
|
+
/** Every key optional at every plain-object depth; arrays stay whole. */
|
|
49
|
+
export declare type NestedPartial<T> = T extends readonly unknown[] ? T : T extends object ? {
|
|
50
|
+
[K in keyof T]?: NestedPartial<T[K]>;
|
|
51
|
+
} : T;
|
|
52
|
+
/** The filled props' type: a key both sides carry as a plain object is complete. */
|
|
53
|
+
export declare type NestedProps<P, D> = {
|
|
54
|
+
[K in keyof P as K extends keyof D ? K : never]-?: NestedLeaf<NonNullable<P[K]>, D[K & keyof D]>;
|
|
55
|
+
} & {
|
|
56
|
+
[K in keyof P as K extends keyof D ? never : K]: P[K];
|
|
57
|
+
};
|
|
58
|
+
/** A value the fill takes whole: not a plain container. */
|
|
59
|
+
declare type Opaque = Date | RegExp | Map<unknown, unknown> | Set<unknown> | ((...args: never[]) => unknown);
|
|
60
|
+
declare type NestedLeaf<V, D> = V extends readonly unknown[] ? V : V extends Opaque ? V : V extends object ? D extends readonly unknown[] ? V : D extends object ? NestedProps<V, D> : V : V;
|
|
61
|
+
/**
|
|
62
|
+
* Fill every nested object prop from its default, in place, and return
|
|
63
|
+
* the props typed as complete. Top-level props are Vue's to default and
|
|
64
|
+
* are never written.
|
|
65
|
+
*/
|
|
66
|
+
export declare function nestedProps<P extends object, D extends object>(props: P, defaults: D, customCloner?: (value: unknown) => unknown): NestedProps<P, D>;
|
|
67
|
+
export {};
|
package/lib/LazyShared.ts
CHANGED
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
* entry stays minimal.
|
|
36
36
|
*/
|
|
37
37
|
export class LazyShared<T> {
|
|
38
|
-
constructor(
|
|
38
|
+
constructor(protected readonly make: () => T) {}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
protected constructed = false;
|
|
41
|
+
protected constructing = false;
|
|
42
|
+
protected stored: T | null = null;
|
|
43
43
|
|
|
44
44
|
get value(): T {
|
|
45
45
|
if (!this.constructed) {
|
package/lib/Reactive.ts
CHANGED
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
type ExtractPropTypes,
|
|
8
8
|
type Ref,
|
|
9
9
|
} from 'vue';
|
|
10
|
+
import { clone } from './clone';
|
|
11
|
+
|
|
12
|
+
export { clone } from './clone';
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* Constants & Helpers
|
|
@@ -363,20 +366,20 @@ export function isClass(val: any): boolean {
|
|
|
363
366
|
* but leave primitive properties and functions intact so that
|
|
364
367
|
* the final object is fully defineComponent() style compatible.
|
|
365
368
|
*
|
|
366
|
-
* The default cloner
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
369
|
+
* The default cloner copies acyclic plain-object and array trees while
|
|
370
|
+
* retaining callbacks, class constructors and opaque objects by reference.
|
|
371
|
+
* For independent Map/Set/Date values or cyclic data, pass a `customCloner`
|
|
372
|
+
* such as `structuredClone` when the values support it.
|
|
370
373
|
*
|
|
371
374
|
* @param defaults Regular object of default key -> values
|
|
372
375
|
* @param typedProps Props declared in defineComponent() style with type and possibly required declared, but without default
|
|
373
|
-
* @param customCloner Optional cloner used for object/array defaults (defaults to
|
|
376
|
+
* @param customCloner Optional cloner used for object/array defaults (defaults to clone)
|
|
374
377
|
* @returns Props declared in defineComponent() style with all properties having default property declared.
|
|
375
378
|
*/
|
|
376
379
|
export function propsWithDefaults<T extends VuePropsObject>(
|
|
377
380
|
defaults: Record<string, any>,
|
|
378
381
|
typedProps: T,
|
|
379
|
-
// Optional:
|
|
382
|
+
// Optional: Choose different ownership semantics for opaque objects or cycles.
|
|
380
383
|
customCloner?: (val: any) => any,
|
|
381
384
|
): VuePropsWithDefaults<T> {
|
|
382
385
|
// NON-MUTATING: descriptor objects are routinely SHARED between props
|
|
@@ -394,7 +397,7 @@ export function propsWithDefaults<T extends VuePropsObject>(
|
|
|
394
397
|
|
|
395
398
|
if (typeof def === 'object' && def !== null) {
|
|
396
399
|
result[prop].default = () =>
|
|
397
|
-
customCloner ? customCloner(def) :
|
|
400
|
+
customCloner ? customCloner(def) : clone(def);
|
|
398
401
|
} else {
|
|
399
402
|
if (isClass(def)) {
|
|
400
403
|
result[prop].default = () => def;
|
|
@@ -432,17 +435,27 @@ type WritableGetters<T> = {
|
|
|
432
435
|
]-?: T[K];
|
|
433
436
|
};
|
|
434
437
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
438
|
+
/**
|
|
439
|
+
* The members `Reactive()` installs on every instance's prototype. A raw
|
|
440
|
+
* class body cannot see them (the transform runs after the class is typed),
|
|
441
|
+
* so a class that calls them merges this interface into its own instance
|
|
442
|
+
* type — one declaration beside the class, zero runtime:
|
|
443
|
+
*
|
|
444
|
+
* class $Session { constructor() { this.$watch(…) } }
|
|
445
|
+
* interface $Session extends ReactiveHelpers {}
|
|
446
|
+
*/
|
|
447
|
+
export interface ReactiveHelpers {
|
|
448
|
+
/** Register a watcher in the instance's lazy effect scope (same signature as Vue `watch`). */
|
|
449
|
+
$watch: typeof watch;
|
|
450
|
+
/** Register a watchEffect in the instance's lazy effect scope (same signature as Vue `watchEffect`). */
|
|
451
|
+
$watchEffect: typeof watchEffect;
|
|
452
|
+
/** Stop the instance's effect scope and drop cached cells (the next
|
|
453
|
+
* touch re-materializes). `{ reset: false }` stops watchers only —
|
|
454
|
+
* every cached cell survives with its current value. */
|
|
455
|
+
$stopEffects: (options?: { reset?: boolean }) => void;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export type ReactiveInstance<T> = T & WritableGetters<T> & ReactiveHelpers;
|
|
446
459
|
|
|
447
460
|
export type ReactiveClass<C extends new (...args: any) => any> = {
|
|
448
461
|
[Key in keyof C]: C[Key];
|
|
@@ -534,4 +547,4 @@ export type IFnParameter<
|
|
|
534
547
|
T extends Record<any, any>,
|
|
535
548
|
P extends keyof T,
|
|
536
549
|
K extends number,
|
|
537
|
-
> = FnParameter<NonNullable<T[P]> extends AnyFn ? NonNullable<T[P]> : never, K>;
|
|
550
|
+
> = FnParameter<NonNullable<T[P]> extends AnyFn ? NonNullable<T[P]> : never, K>;
|
package/lib/Static.ts
CHANGED
|
@@ -70,6 +70,22 @@ export function Static<Class extends ClassConstructor>(targetClass: Class): Clas
|
|
|
70
70
|
const descriptor = Object.getOwnPropertyDescriptor(currentClass, key)!;
|
|
71
71
|
|
|
72
72
|
if (typeof descriptor.value === 'function') {
|
|
73
|
+
// HOT-LOOP READY — measured, twice, after two wrong theories. The
|
|
74
|
+
// bind happens ONCE: the first read defines an own bind-key
|
|
75
|
+
// property holding the bound function; every later read returns
|
|
76
|
+
// it. A HOISTED bound method is exactly plain-function speed
|
|
77
|
+
// (in-browser, 9M calls, fresh-page medians: module fn 31.7ms,
|
|
78
|
+
// hoisted bound method 30.0ms). The ONLY per-call cost is reading
|
|
79
|
+
// the method THROUGH the accessor inside the loop (84.6ms same
|
|
80
|
+
// loop) — so in a million-call loop, destructure the methods once
|
|
81
|
+
// (`const { method } = X.Class` — a late read of the mutable slot,
|
|
82
|
+
// so a subclass swap is still honored) and pay the accessor once.
|
|
83
|
+
// Ordinary call counts never notice any of this.
|
|
84
|
+
//
|
|
85
|
+
// Benchmark honestly: a shared bench(fn) harness makes the call
|
|
86
|
+
// site megamorphic and slows every variant measured after the
|
|
87
|
+
// first — that artifact once misread bound calls as "48% slower."
|
|
88
|
+
// Fresh page per variant, dedicated loops.
|
|
73
89
|
const method = descriptor.value;
|
|
74
90
|
const bindKey =
|
|
75
91
|
typeof key === 'string'
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from 'vue';
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
+
clone,
|
|
16
17
|
isClass,
|
|
17
18
|
propsWithDefaults,
|
|
18
19
|
Reactive,
|
|
@@ -986,7 +987,7 @@ describe('propsWithDefaults()', () => {
|
|
|
986
987
|
expect(out.nul.default).toBe(null); // null → else branch, assigned directly
|
|
987
988
|
});
|
|
988
989
|
|
|
989
|
-
it('wraps object/array defaults in a factory that
|
|
990
|
+
it('wraps object/array defaults in a factory that copies their containers', () => {
|
|
990
991
|
const typed = { o: { type: Object }, a: { type: Array } };
|
|
991
992
|
const defaults = { o: { nested: { k: 1 } }, a: [1, 2, 3] };
|
|
992
993
|
const out = propsWithDefaults(defaults, { ...typed }) as Record<
|
|
@@ -1022,6 +1023,49 @@ describe('propsWithDefaults()', () => {
|
|
|
1022
1023
|
expect(v).toEqual({ k: 1, cloned: true });
|
|
1023
1024
|
});
|
|
1024
1025
|
|
|
1026
|
+
it('isolates default containers while preserving nested constructors, callbacks and opaque objects', () => {
|
|
1027
|
+
class Runner {}
|
|
1028
|
+
class Tuning { gain = 1; }
|
|
1029
|
+
const onChange = () => 1;
|
|
1030
|
+
const tuning = new Tuning();
|
|
1031
|
+
const date = new Date(0);
|
|
1032
|
+
const cache = new Map([['gain', 1]]);
|
|
1033
|
+
const defaults = { options: { limits: { count: 5 }, runner: Runner, onChange, tuning, date, cache } };
|
|
1034
|
+
const props = propsWithDefaults(defaults, { options: { type: Object } }) as Record<string, any>;
|
|
1035
|
+
const first = props.options.default();
|
|
1036
|
+
const second = props.options.default();
|
|
1037
|
+
|
|
1038
|
+
first.limits.count = 2;
|
|
1039
|
+
expect(second.limits.count).toBe(5);
|
|
1040
|
+
expect(defaults.options.limits.count).toBe(5);
|
|
1041
|
+
expect(first.runner).toBe(Runner);
|
|
1042
|
+
expect(first.onChange).toBe(onChange);
|
|
1043
|
+
expect(first.tuning).toBe(tuning);
|
|
1044
|
+
expect(first.date).toBe(date);
|
|
1045
|
+
expect(first.cache).toBe(cache);
|
|
1046
|
+
// The public copier uses the same ownership policy as the props factory.
|
|
1047
|
+
const copied = clone(defaults.options);
|
|
1048
|
+
expect(copied.limits).not.toBe(defaults.options.limits);
|
|
1049
|
+
expect(copied.runner).toBe(Runner);
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
it('accepts structuredClone explicitly for cyclic data and independent built-in objects', () => {
|
|
1053
|
+
const options: { date: Date; cache: Map<string, number>; self?: unknown } = {
|
|
1054
|
+
date: new Date(0), cache: new Map([['gain', 1]]),
|
|
1055
|
+
};
|
|
1056
|
+
options.self = options;
|
|
1057
|
+
const props = propsWithDefaults({ options }, { options: { type: Object } }, structuredClone) as Record<string, any>;
|
|
1058
|
+
const first = props.options.default();
|
|
1059
|
+
const second = props.options.default();
|
|
1060
|
+
|
|
1061
|
+
expect(first.self).toBe(first);
|
|
1062
|
+
expect(first.date).not.toBe(second.date);
|
|
1063
|
+
expect(first.date.getTime()).toBe(0);
|
|
1064
|
+
first.cache.set('gain', 9);
|
|
1065
|
+
expect(second.cache.get('gain')).toBe(1);
|
|
1066
|
+
expect(options.cache.get('gain')).toBe(1);
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1025
1069
|
it('wraps a class default in a factory that returns the class itself', () => {
|
|
1026
1070
|
class Cool {
|
|
1027
1071
|
x = 1;
|