ivue 2.4.0 → 2.5.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/bin/ivue.mjs +62 -1
- package/dist/Reactive.d.ts +19 -2
- package/dist/extras.cjs +1 -1
- package/dist/extras.es.js +23 -23
- package/dist/index.cjs +1 -1
- package/dist/index.es.js +54 -50
- package/lib/Reactive.ts +22 -4
- package/lib/Static.ts +14 -0
- package/lib/__tests__/Reactive.vitest.spec.ts +25 -13
- package/lib/__tests__/Static.vitest.spec.ts +60 -1
- package/lib/__tests__/ivue.vitest.spec.ts +17 -5
- package/lib/ivue.ts +15 -1
- package/package.json +7 -3
- package/skills/ivue/SKILL.md +184 -6
- package/skills/ivue/constitution.test.ts +143 -0
- package/skills/ivue/ivue-generator-standard.ts +410 -0
- package/skills/ivue/ivue-house-gate.ts +130 -0
- package/skills/ivue/ivue-standards-check.ts +2464 -0
- package/skills/ivue/ivue-standards-skip.json +17 -0
package/bin/ivue.mjs
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* the installed package, so the manual always matches the engine version
|
|
11
11
|
* the project actually runs.
|
|
12
12
|
*/
|
|
13
|
+
import { createHash } from 'node:crypto';
|
|
13
14
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
14
15
|
import { dirname, join, resolve } from 'node:path';
|
|
15
16
|
import { fileURLToPath } from 'node:url';
|
|
@@ -27,7 +28,11 @@ if (command !== 'skill') {
|
|
|
27
28
|
|
|
28
29
|
npx ivue skill [targets] [--force]
|
|
29
30
|
|
|
30
|
-
Installs the ivue operating manual for coding agents
|
|
31
|
+
Installs the ivue operating manual for coding agents, and (for the
|
|
32
|
+
Claude target) the house gate seed — your own extendable Standard
|
|
33
|
+
gate at .claude/skills/ivue/ivue-house-gate.ts. A house gate you have
|
|
34
|
+
customized is never overwritten; upgrade it with your AI agent using
|
|
35
|
+
the ivue skill, or --force. Targets:
|
|
31
36
|
(none) / --claude .claude/skills/ivue/SKILL.md
|
|
32
37
|
--cursor .cursor/rules/ivue.mdc
|
|
33
38
|
--copilot .github/instructions/ivue.instructions.md
|
|
@@ -58,6 +63,50 @@ const skillDescription =
|
|
|
58
63
|
/description:\s*([^\n]+)/.exec(skillText)?.[1] ??
|
|
59
64
|
'The ivue operating manual.';
|
|
60
65
|
|
|
66
|
+
/**
|
|
67
|
+
* A SEED is a file the user is meant to edit (the house gate). The install
|
|
68
|
+
* stamps it with a hash of its own content; on upgrade, a matching stamp
|
|
69
|
+
* means "untouched since install" (safe to replace), anything else means
|
|
70
|
+
* the user customized it — leave it alone and point at AI reconciliation.
|
|
71
|
+
*/
|
|
72
|
+
const SEED_STAMP = /^\/\/ ivue-seed: ([0-9a-f]{64})\n/;
|
|
73
|
+
|
|
74
|
+
function stampSeed(content) {
|
|
75
|
+
const hash = createHash('sha256').update(content).digest('hex');
|
|
76
|
+
return `// ivue-seed: ${hash}\n${content}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function seedUntouched(existing) {
|
|
80
|
+
const stamp = SEED_STAMP.exec(existing);
|
|
81
|
+
if (!stamp) return false;
|
|
82
|
+
const body = existing.slice(stamp[0].length);
|
|
83
|
+
return createHash('sha256').update(body).digest('hex') === stamp[1];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function installSeed(relativePath, content, label) {
|
|
87
|
+
const targetPath = join(process.cwd(), relativePath);
|
|
88
|
+
const stamped = stampSeed(content);
|
|
89
|
+
if (existsSync(targetPath)) {
|
|
90
|
+
const existing = readFileSync(targetPath, 'utf8');
|
|
91
|
+
if (existing === stamped) {
|
|
92
|
+
console.log(`ivue: ${label} already up to date (ivue v${version}).`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (!seedUntouched(existing) && !force) {
|
|
96
|
+
console.log(
|
|
97
|
+
`ivue: ${label} at ${relativePath} was customized — keeping yours.\n` +
|
|
98
|
+
' To upgrade it, ask your AI agent (with the ivue skill installed) to\n' +
|
|
99
|
+
" reconcile your gate with the new Standard's — it knows both sides.\n" +
|
|
100
|
+
' Or re-run with --force to overwrite your customizations.',
|
|
101
|
+
);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
106
|
+
writeFileSync(targetPath, stamped);
|
|
107
|
+
console.log(`ivue: ${label} installed at ${relativePath} (ivue v${version}).`);
|
|
108
|
+
}
|
|
109
|
+
|
|
61
110
|
/** Write one target idempotently; refuse to clobber local edits sans --force. */
|
|
62
111
|
function install(relativePath, content, label) {
|
|
63
112
|
const targetPath = join(process.cwd(), relativePath);
|
|
@@ -104,6 +153,18 @@ const wantClaude =
|
|
|
104
153
|
|
|
105
154
|
if (wantClaude) {
|
|
106
155
|
install('.claude/skills/ivue/SKILL.md', skillText, 'Claude skill');
|
|
156
|
+
// the house gate seed — the user's own gate, never clobbered once edited
|
|
157
|
+
const housePath = [
|
|
158
|
+
join(packageRoot, 'skills', 'ivue', 'ivue-house-gate.ts'),
|
|
159
|
+
join(packageRoot, '.claude', 'skills', 'ivue', 'ivue-house-gate.ts'),
|
|
160
|
+
].find(existsSync);
|
|
161
|
+
if (housePath) {
|
|
162
|
+
const houseSeed = readFileSync(housePath, 'utf8')
|
|
163
|
+
.replace("from '../../lib/Static'", "from 'ivue/extras'")
|
|
164
|
+
.replace("from './ivue-standards-check'", "from 'ivue/skills/ivue/ivue-standards-check'")
|
|
165
|
+
.replace("from './ivue-generator-standard'", "from 'ivue/skills/ivue/ivue-generator-standard'");
|
|
166
|
+
installSeed('.claude/skills/ivue/ivue-house-gate.ts', houseSeed, 'house gate');
|
|
167
|
+
}
|
|
107
168
|
}
|
|
108
169
|
if (wantAll && !wantCursor) skipped('Cursor rule', '.cursor/', '--cursor');
|
|
109
170
|
if (wantAll && !wantCopilot) skipped('Copilot instructions', '.github/', '--copilot');
|
package/dist/Reactive.d.ts
CHANGED
|
@@ -118,11 +118,28 @@ export declare type ExtractEmitTypes<T extends Record<string, any>> = UnionToInt
|
|
|
118
118
|
}>>;
|
|
119
119
|
/**
|
|
120
120
|
* Extract properties as all-assigned (non-optional) because every one of
|
|
121
|
-
* them carries a default
|
|
121
|
+
* them carries a default — the honesty check: a declared type without a
|
|
122
|
+
* default is a compile error. Props declared `required: true` are FILTERED
|
|
123
|
+
* OUT automatically (a required prop can never carry a default —
|
|
124
|
+
* propsWithDefaults skips them at runtime), so no manual `Omit` is needed;
|
|
125
|
+
* a deliberately default-free OPTIONAL prop is declared `key: undefined`,
|
|
126
|
+
* stating the ruling in the defaults object itself. The `required: true`
|
|
127
|
+
* literal survives `typeof` only through generic inference — declare the
|
|
128
|
+
* types map with `definePropTypes({...})`, never as a bare object const.
|
|
122
129
|
*/
|
|
123
130
|
export declare type ExtractPropDefaultTypes<O> = {
|
|
124
|
-
[K in keyof O
|
|
131
|
+
[K in keyof O as O[K] extends {
|
|
132
|
+
required: true;
|
|
133
|
+
} ? never : K]: K extends keyof ExtractPropTypes<O> ? ExtractPropTypes<O>[K] : never;
|
|
125
134
|
};
|
|
135
|
+
/**
|
|
136
|
+
* Identity helper for a prop-TYPES map. Exists for one reason: in a bare
|
|
137
|
+
* `const propsTypes = {...}`, TypeScript widens `required: true` to
|
|
138
|
+
* `boolean`, which blinds ExtractPropDefaultTypes' required-key filter —
|
|
139
|
+
* generic inference through this call preserves the literal. Costs
|
|
140
|
+
* nothing at runtime.
|
|
141
|
+
*/
|
|
142
|
+
export declare function definePropTypes<T extends VuePropsObject>(types: T): T;
|
|
126
143
|
/**
|
|
127
144
|
* Extend a slots interface T with prefixed 'before--' & 'after--' slots to
|
|
128
145
|
* create fully extensible wrapped components.
|
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 l=Object.hasOwn,c=new Set;exports.LazyShared=class{constructor(s){this.make=s,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(s){var a;const o=class extends s{},u=new Set;for(let n=s;n!==Function.prototype;n=Object.getPrototypeOf(n))for(const t of Reflect.ownKeys(n)){if(u.has(t)||(u.add(t),typeof t=="symbol"&&((a=Symbol.keyFor(t))==null?void 0:a.startsWith("ivue.static")))||c.has(t))continue;const i=Object.getOwnPropertyDescriptor(n,t);if(typeof i.value=="function"){const r=i.value,e=typeof t=="string"?Symbol.for(`ivue.staticBound.${t}`):Symbol("ivue.staticBound");c.add(e),Object.defineProperty(o,t,{configurable:!0,enumerable:i.enumerable,get(){return l(this,e)||Object.defineProperty(this,e,{configurable:!0,value:r.bind(this)}),this[e]}})}else if(i.get&&!i.set&&typeof t=="string"&&t.startsWith("$")){const r=i.get,e=Symbol.for(`ivue.staticCache.${t}`);c.add(e),Object.defineProperty(o,t,{configurable:!0,enumerable:i.enumerable,get(){return l(this,e)||Object.defineProperty(this,e,{configurable:!0,value:r.call(this)}),this[e]}})}}return o};
|
package/dist/extras.es.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
const
|
|
2
|
-
function
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
const h = Object.hasOwn, c = /* @__PURE__ */ new Set();
|
|
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))
|
|
8
9
|
continue;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return u(this, s) || Object.defineProperty(this, s, { configurable: !0, value: o.bind(this) }), this[s];
|
|
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
15
|
} });
|
|
16
|
-
} else if (
|
|
17
|
-
const
|
|
18
|
-
Object.defineProperty(
|
|
19
|
-
return
|
|
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
20
|
} });
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
return
|
|
23
|
+
return i;
|
|
24
24
|
}
|
|
25
|
-
class
|
|
26
|
-
constructor(
|
|
27
|
-
this.make =
|
|
25
|
+
class f {
|
|
26
|
+
constructor(i) {
|
|
27
|
+
this.make = i, this.constructed = !1, this.constructing = !1, this.stored = null;
|
|
28
28
|
}
|
|
29
29
|
get value() {
|
|
30
30
|
if (!this.constructed) {
|
|
@@ -44,6 +44,6 @@ class h {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
export {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
f as LazyShared,
|
|
48
|
+
l as Static
|
|
49
49
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("vue"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("vue"),d=Object.hasOwn,y=Object.getPrototypeOf,w=Object.getOwnPropertyDescriptor,m=Object.getOwnPropertyNames,a=Object.defineProperty,g=Object.prototype,b=Symbol.for("ivue.raw"),p=Symbol.for("ivue.scope"),h=Symbol.for("ivue.processed");function i(e){const c=l.toRaw(e);if(c!==e)return c[b]??(c[b]=c);const r=e[b];return r?r===e?r:l.toRaw(r):e[b]=e}function O(e,c,r,o){a(e,c,{configurable:!0,enumerable:!1,get(){const t=i(this);return t[r]??(t[r]=o.bind(t))},set(t){i(this)[r]=t}})}function S(e,c,r,o,t){const n=c[0]==="$";a(e,c,{configurable:!0,enumerable:!1,get:function(){const u=i(this);if(r in u)return u[r];const f=o.call(u);return n?(u[r]=f,f):(l.isRef(f)?u[r]=f:a(e,c,{configurable:!0,enumerable:!1,get(){return o.call(i(this))},set:t?function(s){return t.call(i(this),s)}:void 0}),f)},set:t?function(u){return t.call(i(this),u)}:void 0})}function v(e){var c;return typeof e=="function"&&!!e.prototype&&!((c=w(e,"prototype"))!=null&&c.writable)}exports.Reactive=function(e){const c=[];let r=e.prototype;for(;r&&r!==g;)c.push(r),r=y(r);c.reverse();for(const o of c){if(d(o,h))continue;const t=m(o),n=[];for(const u of t){if(u==="constructor")continue;const f=w(o,u);if(typeof f.value=="function"){const s=Symbol(u);n.push(s),O(o,u,s,f.value)}else if(f.get){const s=Symbol(u);n.push(s),S(o,u,s,f.get,f.set)}}a(o,h,{value:n})}return d(e.prototype,"$stopEffects")||(a(e.prototype,"$watch",{enumerable:!1,configurable:!0,writable:!0,value:function(...o){const t=i(this);return(t[p]??(t[p]=l.effectScope(!0))).run(()=>l.watch(...o))}}),a(e.prototype,"$watchEffect",{enumerable:!1,configurable:!0,writable:!0,value:function(...o){const t=i(this);return(t[p]??(t[p]=l.effectScope(!0))).run(()=>l.watchEffect(...o))}}),a(e.prototype,"$stopEffects",{enumerable:!1,configurable:!0,writable:!0,value:function(o){const t=i(this);try{const n=t[p];n&&n.stop()}finally{if(delete t[p],(o==null?void 0:o.reset)!==!1){let n=y(t);for(;n&&n!==g;){const u=n[h];if(u)for(const f of u)delete t[f];n=y(n)}}}}})),e},exports.definePropTypes=function(e){return e},exports.isClass=v,exports.propsWithDefaults=function(e,c,r){const o={};for(const t in c){const n=e==null?void 0:e[t],u=c[t];o[t]={...u},u.required||n===void 0||(typeof n=="object"&&n!==null?o[t].default=()=>r?r(n):structuredClone(n):v(n)?o[t].default=()=>n:o[t].default=n)}return o};
|
package/dist/index.es.js
CHANGED
|
@@ -1,99 +1,103 @@
|
|
|
1
|
-
import { effectScope as h, watch as w, watchEffect as O, toRaw as
|
|
2
|
-
const
|
|
3
|
-
function f(
|
|
4
|
-
const c =
|
|
5
|
-
if (c !==
|
|
1
|
+
import { effectScope as h, watch as w, watchEffect as O, toRaw as d, isRef as j } from "vue";
|
|
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
6
|
return c[p] ?? (c[p] = c);
|
|
7
|
-
const r =
|
|
8
|
-
return r ? r ===
|
|
7
|
+
const r = t[p];
|
|
8
|
+
return r ? r === t ? r : d(r) : t[p] = t;
|
|
9
9
|
}
|
|
10
|
-
function
|
|
11
|
-
a(
|
|
12
|
-
const
|
|
13
|
-
return
|
|
14
|
-
}, set(
|
|
15
|
-
f(this)[r] =
|
|
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
16
|
} });
|
|
17
17
|
}
|
|
18
|
-
function
|
|
18
|
+
function $(t, c, r, o, e) {
|
|
19
19
|
const n = c[0] === "$";
|
|
20
|
-
a(
|
|
20
|
+
a(t, c, { configurable: !0, enumerable: !1, get: function() {
|
|
21
21
|
const s = f(this);
|
|
22
22
|
if (r in s)
|
|
23
23
|
return s[r];
|
|
24
24
|
const u = o.call(s);
|
|
25
|
-
return n ? (s[r] = u, u) : (j(u) ? s[r] = u : a(
|
|
25
|
+
return n ? (s[r] = u, u) : (j(u) ? s[r] = u : a(t, c, { configurable: !0, enumerable: !1, get() {
|
|
26
26
|
return o.call(f(this));
|
|
27
|
-
}, set:
|
|
28
|
-
return
|
|
27
|
+
}, set: e ? function(i) {
|
|
28
|
+
return e.call(f(this), i);
|
|
29
29
|
} : void 0 }), u);
|
|
30
|
-
}, set:
|
|
31
|
-
return
|
|
30
|
+
}, set: e ? function(s) {
|
|
31
|
+
return e.call(f(this), s);
|
|
32
32
|
} : void 0 });
|
|
33
33
|
}
|
|
34
|
-
function C(
|
|
34
|
+
function C(t) {
|
|
35
35
|
const c = [];
|
|
36
|
-
let r =
|
|
37
|
-
for (; r && r !==
|
|
36
|
+
let r = t.prototype;
|
|
37
|
+
for (; r && r !== m; )
|
|
38
38
|
c.push(r), r = b(r);
|
|
39
39
|
c.reverse();
|
|
40
40
|
for (const o of c) {
|
|
41
|
-
if (
|
|
41
|
+
if (g(o, y))
|
|
42
42
|
continue;
|
|
43
|
-
const
|
|
44
|
-
for (const s of
|
|
43
|
+
const e = S(o), n = [];
|
|
44
|
+
for (const s of e) {
|
|
45
45
|
if (s === "constructor")
|
|
46
46
|
continue;
|
|
47
|
-
const u =
|
|
47
|
+
const u = v(o, s);
|
|
48
48
|
if (typeof u.value == "function") {
|
|
49
49
|
const i = Symbol(s);
|
|
50
|
-
n.push(i),
|
|
50
|
+
n.push(i), P(o, s, i, u.value);
|
|
51
51
|
} else if (u.get) {
|
|
52
52
|
const i = Symbol(s);
|
|
53
|
-
n.push(i),
|
|
53
|
+
n.push(i), $(o, s, i, u.get, u.set);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
a(o, y, { value: n });
|
|
57
57
|
}
|
|
58
|
-
return
|
|
59
|
-
const
|
|
60
|
-
return (
|
|
61
|
-
} }), a(
|
|
62
|
-
const
|
|
63
|
-
return (
|
|
64
|
-
} }), a(
|
|
65
|
-
const
|
|
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
66
|
try {
|
|
67
|
-
const n =
|
|
67
|
+
const n = e[l];
|
|
68
68
|
n && n.stop();
|
|
69
69
|
} finally {
|
|
70
|
-
if (delete
|
|
71
|
-
let n = b(
|
|
72
|
-
for (; n && n !==
|
|
70
|
+
if (delete e[l], (o == null ? void 0 : o.reset) !== !1) {
|
|
71
|
+
let n = b(e);
|
|
72
|
+
for (; n && n !== m; ) {
|
|
73
73
|
const s = n[y];
|
|
74
74
|
if (s)
|
|
75
75
|
for (const u of s)
|
|
76
|
-
delete
|
|
76
|
+
delete e[u];
|
|
77
77
|
n = b(n);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
} })),
|
|
81
|
+
} })), t;
|
|
82
82
|
}
|
|
83
|
-
function
|
|
83
|
+
function E(t) {
|
|
84
84
|
var c;
|
|
85
|
-
return typeof
|
|
85
|
+
return typeof t == "function" && !!t.prototype && !((c = v(t, "prototype")) != null && c.writable);
|
|
86
86
|
}
|
|
87
|
-
function D(
|
|
87
|
+
function D(t, c, r) {
|
|
88
88
|
const o = {};
|
|
89
|
-
for (const
|
|
90
|
-
const n =
|
|
91
|
-
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
92
|
}
|
|
93
93
|
return o;
|
|
94
94
|
}
|
|
95
|
+
function q(t) {
|
|
96
|
+
return t;
|
|
97
|
+
}
|
|
95
98
|
export {
|
|
96
99
|
C as Reactive,
|
|
97
|
-
|
|
100
|
+
q as definePropTypes,
|
|
101
|
+
E as isClass,
|
|
98
102
|
D as propsWithDefaults
|
|
99
103
|
};
|
package/lib/Reactive.ts
CHANGED
|
@@ -486,14 +486,32 @@ export type ExtractEmitTypes<T extends Record<string, any>> =
|
|
|
486
486
|
|
|
487
487
|
/**
|
|
488
488
|
* Extract properties as all-assigned (non-optional) because every one of
|
|
489
|
-
* them carries a default
|
|
489
|
+
* them carries a default — the honesty check: a declared type without a
|
|
490
|
+
* default is a compile error. Props declared `required: true` are FILTERED
|
|
491
|
+
* OUT automatically (a required prop can never carry a default —
|
|
492
|
+
* propsWithDefaults skips them at runtime), so no manual `Omit` is needed;
|
|
493
|
+
* a deliberately default-free OPTIONAL prop is declared `key: undefined`,
|
|
494
|
+
* stating the ruling in the defaults object itself. The `required: true`
|
|
495
|
+
* literal survives `typeof` only through generic inference — declare the
|
|
496
|
+
* types map with `definePropTypes({...})`, never as a bare object const.
|
|
490
497
|
*/
|
|
491
498
|
export type ExtractPropDefaultTypes<O> = {
|
|
492
|
-
[K in keyof O
|
|
493
|
-
?
|
|
494
|
-
: never;
|
|
499
|
+
[K in keyof O as O[K] extends { required: true }
|
|
500
|
+
? never
|
|
501
|
+
: K]: K extends keyof ExtractPropTypes<O> ? ExtractPropTypes<O>[K] : never;
|
|
495
502
|
};
|
|
496
503
|
|
|
504
|
+
/**
|
|
505
|
+
* Identity helper for a prop-TYPES map. Exists for one reason: in a bare
|
|
506
|
+
* `const propsTypes = {...}`, TypeScript widens `required: true` to
|
|
507
|
+
* `boolean`, which blinds ExtractPropDefaultTypes' required-key filter —
|
|
508
|
+
* generic inference through this call preserves the literal. Costs
|
|
509
|
+
* nothing at runtime.
|
|
510
|
+
*/
|
|
511
|
+
export function definePropTypes<T extends VuePropsObject>(types: T): T {
|
|
512
|
+
return types;
|
|
513
|
+
}
|
|
514
|
+
|
|
497
515
|
/**
|
|
498
516
|
* Extend a slots interface T with prefixed 'before--' & 'after--' slots to
|
|
499
517
|
* create fully extensible wrapped components.
|
package/lib/Static.ts
CHANGED
|
@@ -42,6 +42,11 @@ export type ClassConstructor = new (...arguments_: any[]) => any;
|
|
|
42
42
|
|
|
43
43
|
const hasOwn = Object.hasOwn;
|
|
44
44
|
|
|
45
|
+
// Every bind/cache symbol this module ever issues — so a second wrap can
|
|
46
|
+
// recognize an ancestor's runtime residue even for the unregistered
|
|
47
|
+
// symbols that back symbol-keyed methods.
|
|
48
|
+
const issuedCacheKeys = new Set<PropertyKey>();
|
|
49
|
+
|
|
45
50
|
export function Static<Class extends ClassConstructor>(targetClass: Class): Class {
|
|
46
51
|
const SelectedClass = class extends targetClass {};
|
|
47
52
|
const visitedKeys = new Set<PropertyKey>();
|
|
@@ -55,6 +60,13 @@ export function Static<Class extends ClassConstructor>(targetClass: Class): Clas
|
|
|
55
60
|
if (visitedKeys.has(key)) continue;
|
|
56
61
|
visitedKeys.add(key);
|
|
57
62
|
|
|
63
|
+
// An already-wrapped ancestor that has been READ owns its bind/cache
|
|
64
|
+
// symbol properties. They are runtime residue, not API — re-wrapping
|
|
65
|
+
// them would install an ancestor-bound function where the child's
|
|
66
|
+
// own chain lookup expects to bind for itself.
|
|
67
|
+
if (typeof key === 'symbol' && Symbol.keyFor(key)?.startsWith('ivue.static')) continue;
|
|
68
|
+
if (issuedCacheKeys.has(key)) continue; // the unregistered symbols backing symbol-keyed methods
|
|
69
|
+
|
|
58
70
|
const descriptor = Object.getOwnPropertyDescriptor(currentClass, key)!;
|
|
59
71
|
|
|
60
72
|
if (typeof descriptor.value === 'function') {
|
|
@@ -63,6 +75,7 @@ export function Static<Class extends ClassConstructor>(targetClass: Class): Clas
|
|
|
63
75
|
typeof key === 'string'
|
|
64
76
|
? Symbol.for(`ivue.staticBound.${key}`)
|
|
65
77
|
: Symbol('ivue.staticBound');
|
|
78
|
+
issuedCacheKeys.add(bindKey);
|
|
66
79
|
|
|
67
80
|
Object.defineProperty(SelectedClass, key, {
|
|
68
81
|
configurable: true,
|
|
@@ -85,6 +98,7 @@ export function Static<Class extends ClassConstructor>(targetClass: Class): Clas
|
|
|
85
98
|
) {
|
|
86
99
|
const getter = descriptor.get;
|
|
87
100
|
const cacheKey = Symbol.for(`ivue.staticCache.${key}`);
|
|
101
|
+
issuedCacheKeys.add(cacheKey);
|
|
88
102
|
|
|
89
103
|
Object.defineProperty(SelectedClass, key, {
|
|
90
104
|
configurable: true,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
propsWithDefaults,
|
|
18
18
|
Reactive,
|
|
19
19
|
type ReactiveInstance,
|
|
20
|
+
definePropTypes,
|
|
20
21
|
} from '../Reactive';
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -418,18 +419,18 @@ describe('Reactive()', () => {
|
|
|
418
419
|
}
|
|
419
420
|
}
|
|
420
421
|
class Mid extends Base {
|
|
421
|
-
get summary() {
|
|
422
|
+
override get summary() {
|
|
422
423
|
return computed(() => `(Mid>${super.summary.value})`);
|
|
423
424
|
}
|
|
424
|
-
get chain() {
|
|
425
|
+
override get chain() {
|
|
425
426
|
return super.chain + '->Mid';
|
|
426
427
|
}
|
|
427
428
|
}
|
|
428
429
|
class Leaf extends Mid {
|
|
429
|
-
get summary() {
|
|
430
|
+
override get summary() {
|
|
430
431
|
return computed(() => `{Leaf>${super.summary.value}}`);
|
|
431
432
|
}
|
|
432
|
-
get chain() {
|
|
433
|
+
override get chain() {
|
|
433
434
|
return super.chain + '->Leaf';
|
|
434
435
|
}
|
|
435
436
|
}
|
|
@@ -446,7 +447,7 @@ describe('Reactive()', () => {
|
|
|
446
447
|
}
|
|
447
448
|
}
|
|
448
449
|
class Child extends Base {
|
|
449
|
-
get val() {
|
|
450
|
+
override get val() {
|
|
450
451
|
return computed(() => 10 + super.val.value);
|
|
451
452
|
}
|
|
452
453
|
}
|
|
@@ -491,13 +492,13 @@ describe('Reactive()', () => {
|
|
|
491
492
|
}
|
|
492
493
|
}
|
|
493
494
|
class L2 extends L1 {
|
|
494
|
-
get tag() {
|
|
495
|
+
override get tag() {
|
|
495
496
|
return computed(() => `L2(${super.tag.value})`);
|
|
496
497
|
}
|
|
497
|
-
get name() {
|
|
498
|
+
override get name() {
|
|
498
499
|
return super.name + '>L2';
|
|
499
500
|
}
|
|
500
|
-
greet() {
|
|
501
|
+
override greet() {
|
|
501
502
|
return super.greet() + '/L2';
|
|
502
503
|
}
|
|
503
504
|
}
|
|
@@ -505,18 +506,18 @@ describe('Reactive()', () => {
|
|
|
505
506
|
get extra() {
|
|
506
507
|
return ref(5);
|
|
507
508
|
}
|
|
508
|
-
get tag() {
|
|
509
|
+
override get tag() {
|
|
509
510
|
return computed(() => `L3[${super.tag.value}]`);
|
|
510
511
|
}
|
|
511
|
-
get name() {
|
|
512
|
+
override get name() {
|
|
512
513
|
return super.name + '>L3';
|
|
513
514
|
}
|
|
514
515
|
}
|
|
515
516
|
class L4 extends L3 {
|
|
516
|
-
get tag() {
|
|
517
|
+
override get tag() {
|
|
517
518
|
return computed(() => `L4{${super.tag.value}}`);
|
|
518
519
|
}
|
|
519
|
-
get name() {
|
|
520
|
+
override get name() {
|
|
520
521
|
return super.name + '>L4';
|
|
521
522
|
}
|
|
522
523
|
// computed in the child aggregating refs declared 3 and 1 levels up
|
|
@@ -525,7 +526,7 @@ describe('Reactive()', () => {
|
|
|
525
526
|
() => (this as any).base.value + (this as any).extra.value,
|
|
526
527
|
);
|
|
527
528
|
}
|
|
528
|
-
greet() {
|
|
529
|
+
override greet() {
|
|
529
530
|
return super.greet() + '/L4';
|
|
530
531
|
}
|
|
531
532
|
}
|
|
@@ -1112,3 +1113,14 @@ describe('$watchEffect (scoped watchEffect)', () => {
|
|
|
1112
1113
|
expect(watchRuns).toBe(1); // sibling watcher in the same scope still fires
|
|
1113
1114
|
});
|
|
1114
1115
|
});
|
|
1116
|
+
|
|
1117
|
+
describe('definePropTypes()', () => {
|
|
1118
|
+
it('returns the same types map (identity, literal-preserving)', () => {
|
|
1119
|
+
const types = definePropTypes({
|
|
1120
|
+
title: { type: String, required: true },
|
|
1121
|
+
size: { type: Number },
|
|
1122
|
+
});
|
|
1123
|
+
expect(types.title.required).toBe(true);
|
|
1124
|
+
expect(types.size.type).toBe(Number);
|
|
1125
|
+
});
|
|
1126
|
+
});
|
|
@@ -49,7 +49,7 @@ describe('Static', () => {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
class $Child extends $Base {
|
|
52
|
-
static describe() {
|
|
52
|
+
static override describe() {
|
|
53
53
|
return 'child';
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -426,6 +426,65 @@ describe('Static $-cached getters', () => {
|
|
|
426
426
|
expect(settle()).toBe(70); // inherited method binds to the child, detached
|
|
427
427
|
});
|
|
428
428
|
|
|
429
|
+
// invariant: A prototype level is transformed at most once (ivue.invariants.md)
|
|
430
|
+
it('re-wrapping a subclass never resurrects a parent-bound method through the bind cache', () => {
|
|
431
|
+
// The double-wrap pattern the manual prescribes: a Static parent, a raw
|
|
432
|
+
// subclass extending it, and Static() applied again to the subclass.
|
|
433
|
+
// The parent is READ FIRST, so its bind cache (an own symbol property)
|
|
434
|
+
// exists when the second wrap walks the chain — that cache must be
|
|
435
|
+
// invisible to the walk, or the child's methods run with the parent
|
|
436
|
+
// as receiver.
|
|
437
|
+
class $Fleet {
|
|
438
|
+
static get vessels(): string[] {
|
|
439
|
+
return ['tug'];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
static roster() {
|
|
443
|
+
return this.vessels.join(',');
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const Fleet = Static($Fleet);
|
|
448
|
+
expect(Fleet.roster()).toBe('tug'); // parent reads (and caches its bound method) first
|
|
449
|
+
|
|
450
|
+
class $HarborFleet extends Fleet {
|
|
451
|
+
static override get vessels(): string[] {
|
|
452
|
+
return [...super.vessels, 'ferry'];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const HarborFleet = Static($HarborFleet);
|
|
456
|
+
|
|
457
|
+
expect(HarborFleet.roster()).toBe('tug,ferry'); // the child's override, not the parent's snapshot
|
|
458
|
+
const roster = HarborFleet.roster;
|
|
459
|
+
expect(roster()).toBe('tug,ferry'); // detached, still child-bound
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// invariant: A prototype level is transformed at most once (ivue.invariants.md)
|
|
463
|
+
it('re-wrapping skips the unregistered bind caches of symbol-keyed methods too', () => {
|
|
464
|
+
const describeKind = Symbol('describeKind');
|
|
465
|
+
class $Signal {
|
|
466
|
+
static get kind(): string {
|
|
467
|
+
return 'base';
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
static [describeKind]() {
|
|
471
|
+
return `kind:${this.kind}`;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const Signal = Static($Signal);
|
|
476
|
+
expect((Signal as any)[describeKind]()).toBe('kind:base'); // parent reads first — unregistered bind cache lands
|
|
477
|
+
|
|
478
|
+
class $AlertSignal extends Signal {
|
|
479
|
+
static override get kind(): string {
|
|
480
|
+
return 'alert';
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const AlertSignal = Static($AlertSignal);
|
|
484
|
+
|
|
485
|
+
expect((AlertSignal as any)[describeKind]()).toBe('kind:alert'); // child receiver, not the parent snapshot
|
|
486
|
+
});
|
|
487
|
+
|
|
429
488
|
it('walks the raw inheritance chain — ancestor $-getters cache per receiver', () => {
|
|
430
489
|
class $Base {
|
|
431
490
|
static get scale() {
|