ivue 2.2.2 → 2.4.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 CHANGED
@@ -79,6 +79,24 @@ const { count } = counter;
79
79
 
80
80
  Full walkthrough: [Getting Started](https://ivue.dev/guide/getting-started).
81
81
 
82
+ ## Built for humans and AI
83
+
84
+ ivue ships with a
85
+ [Standard Operating Manual](https://ivue.dev/guide/standard) — the complete
86
+ authoring standard as annotated templates, rules, and a review checklist. It
87
+ reads as documentation and works as a drop-in skill for AI coding agents, so
88
+ generated code follows the same standard your team writes:
89
+
90
+ ```sh
91
+ npx ivue skill # installs .claude/skills/ivue/SKILL.md, version-locked
92
+ npx ivue skill --all # + Codex/Cursor/Copilot where already in use
93
+ ```
94
+
95
+ Agents holding the Standard have
96
+ [derived correct patterns its own author never wrote](https://ivue.dev/blog/patterns-the-author-never-wrote) —
97
+ the manual is a generator, not a catalog. The wider argument:
98
+ [Reactive framework for the AI era](https://ivue.dev/blog/reactive-framework-for-the-ai-era).
99
+
82
100
  ## Why classes, why now
83
101
 
84
102
  - **Native class API** — `extends`, `super`, getters, setters, private
@@ -207,24 +225,6 @@ Taken all the way down: a fully reactive spreadsheet model holding
207
225
  floor — because in ivue, everything costs proportional to what's *observed*,
208
226
  nothing costs proportional to what *exists*.
209
227
 
210
- ## Built for humans and AI
211
-
212
- ivue ships with a
213
- [Standard Operating Manual](https://ivue.dev/guide/standard) — the complete
214
- authoring standard as annotated templates, rules, and a review checklist. It
215
- reads as documentation and works as a drop-in skill for AI coding agents, so
216
- generated code follows the same standard your team writes:
217
-
218
- ```sh
219
- npx ivue skill # installs .claude/skills/ivue/SKILL.md, version-locked
220
- npx ivue skill --all # + Codex/Cursor/Copilot where already in use
221
- ```
222
-
223
- Agents holding the Standard have
224
- [derived correct patterns its own author never wrote](https://ivue.dev/blog/patterns-the-author-never-wrote) —
225
- the manual is a generator, not a catalog. The wider argument:
226
- [Reactive framework for the AI era](https://ivue.dev/blog/reactive-framework-for-the-ai-era).
227
-
228
228
  ## Go deeper
229
229
 
230
230
  - [Fundamental Principles](https://ivue.dev/guide/principles) — the design, from first ideas
@@ -0,0 +1,49 @@
1
+ /**
2
+ * `LazyShared<T>` — the safe shared-store cell for static classes.
3
+ *
4
+ * The static-class split (see `Static()`): `$`-prefixed static getters
5
+ * are compute-once-PER-RECEIVER caches — perfect for memos and per-class
6
+ * tuning, where a subclass forking its own copy is the feature. A SHARED
7
+ * store (a registry, a ledger) must never live there: per-receiver
8
+ * caching means a subclass reading `this.$store` silently forks the
9
+ * registry. The fix is a `static readonly` FIELD — one reference on the
10
+ * declaring class, inherited, never receiver-cached — but an eager field
11
+ * initializer runs at MODULE LOAD, so it may only hold dependency-free
12
+ * values; the moment it constructs another namespace's class it races
13
+ * import cycles.
14
+ *
15
+ * `LazyShared` closes the triangle. The field eagerly stores the CELL
16
+ * (load-safe — a thunk evaluates nothing), the thunk runs on first
17
+ * `.value` read (cycle-safe — every module in any import cycle has
18
+ * finished loading), and memoization mutates cell-internal state
19
+ * (fork-safe — no receiver, subclass included, can fork it):
20
+ *
21
+ * class $SearchRegistry {
22
+ * protected static readonly sharedBackend = new LazyShared(
23
+ * () => new SearchBackend.Class(),
24
+ * );
25
+ * protected static get $backend() {
26
+ * return this.sharedBackend.value; // the field IS the pin
27
+ * }
28
+ * }
29
+ *
30
+ * A thunk that reads its own cell (directly or through another cell)
31
+ * throws a NAMED cycle error instead of a bare stack overflow, and a
32
+ * failed construction leaves the cell retryable, never poisoned.
33
+ *
34
+ * Ships from `ivue/extras` (not the reactive core) so the primary `ivue`
35
+ * entry stays minimal.
36
+ */
37
+ export declare class LazyShared<T> {
38
+ private readonly make;
39
+ constructor(make: () => T);
40
+ private constructed;
41
+ private constructing;
42
+ private stored;
43
+ get value(): T;
44
+ /**
45
+ * Drop the constructed value; the next read constructs again. For
46
+ * tests and process recomposition — production code never resets.
47
+ */
48
+ reset(): void;
49
+ }
@@ -32,7 +32,7 @@ export declare type VuePropsWithDefaults<T extends VuePropsObject> = {
32
32
  * @param val Any value
33
33
  * @returns boolean If it's a JavaScript Class returns true
34
34
  */
35
- export declare const isClass: (val: any) => boolean;
35
+ export declare function isClass(val: any): boolean;
36
36
  /**
37
37
  * Creates props with defaults in defineComponent() style.
38
38
  *
@@ -64,7 +64,7 @@ export declare const isClass: (val: any) => boolean;
64
64
  * @param customCloner Optional cloner used for object/array defaults (defaults to structuredClone)
65
65
  * @returns Props declared in defineComponent() style with all properties having default property declared.
66
66
  */
67
- export declare const propsWithDefaults: <T extends VuePropsObject>(defaults: Record<string, any>, typedProps: T, customCloner?: ((val: any) => any) | undefined) => VuePropsWithDefaults<T>;
67
+ export declare function propsWithDefaults<T extends VuePropsObject>(defaults: Record<string, any>, typedProps: T, customCloner?: (val: any) => any): VuePropsWithDefaults<T>;
68
68
  /**
69
69
  * Type Utilities
70
70
  */
@@ -84,8 +84,12 @@ export declare type ReactiveInstance<T> = T & WritableGetters<T> & {
84
84
  $watch: typeof watch;
85
85
  /** Register a watchEffect in the instance's lazy effect scope (same signature as Vue `watchEffect`). */
86
86
  $watchEffect: typeof watchEffect;
87
- /** Stop the instance's effect scope and drop cached cells. */
88
- $stopEffects: () => void;
87
+ /** Stop the instance's effect scope and drop cached cells (the next
88
+ * touch re-materializes). `{ reset: false }` stops watchers only —
89
+ * every cached cell survives with its current value. */
90
+ $stopEffects: (options?: {
91
+ reset?: boolean;
92
+ }) => void;
89
93
  };
90
94
  export declare type ReactiveClass<C extends new (...args: any) => any> = {
91
95
  [Key in keyof C]: C[Key];
package/dist/extras.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=Object.hasOwn;exports.Static=function(c){const r=class extends c{},s=new Set;for(let o=c;o!==Function.prototype;o=Object.getPrototypeOf(o))for(const e of Reflect.ownKeys(o)){if(s.has(e))continue;s.add(e);const t=Object.getOwnPropertyDescriptor(o,e);if(typeof t.value=="function"){const i=t.value,n=typeof e=="string"?Symbol.for(`ivue.staticBound.${e}`):Symbol("ivue.staticBound");Object.defineProperty(r,e,{configurable:!0,enumerable:t.enumerable,get(){return u(this,n)||Object.defineProperty(this,n,{configurable:!0,value:i.bind(this)}),this[n]}})}else if(t.get&&!t.set&&typeof e=="string"&&e.startsWith("$")){const i=t.get,n=Symbol.for(`ivue.staticCache.${e}`);Object.defineProperty(r,e,{configurable:!0,enumerable:t.enumerable,get(){return u(this,n)||Object.defineProperty(this,n,{configurable:!0,value:i.call(this)}),this[n]}})}}return r};
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=Object.hasOwn;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){const i=class extends s{},c=new Set;for(let r=s;r!==Function.prototype;r=Object.getPrototypeOf(r))for(const t of Reflect.ownKeys(r)){if(c.has(t))continue;c.add(t);const e=Object.getOwnPropertyDescriptor(r,t);if(typeof e.value=="function"){const o=e.value,n=typeof t=="string"?Symbol.for(`ivue.staticBound.${t}`):Symbol("ivue.staticBound");Object.defineProperty(i,t,{configurable:!0,enumerable:e.enumerable,get(){return u(this,n)||Object.defineProperty(this,n,{configurable:!0,value:o.bind(this)}),this[n]}})}else if(e.get&&!e.set&&typeof t=="string"&&t.startsWith("$")){const o=e.get,n=Symbol.for(`ivue.staticCache.${t}`);Object.defineProperty(i,t,{configurable:!0,enumerable:e.enumerable,get(){return u(this,n)||Object.defineProperty(this,n,{configurable:!0,value:o.call(this)}),this[n]}})}}return i};
package/dist/extras.d.ts CHANGED
@@ -7,3 +7,4 @@
7
7
  * import { Static } from 'ivue/extras';
8
8
  */
9
9
  export { Static, type ClassConstructor } from './Static';
10
+ export { LazyShared } from './LazyShared';
package/dist/extras.es.js CHANGED
@@ -1,27 +1,49 @@
1
- const a = Object.hasOwn;
2
- function u(s) {
3
- const r = class extends s {
1
+ const u = Object.hasOwn;
2
+ function a(i) {
3
+ const n = class extends i {
4
4
  }, c = /* @__PURE__ */ new Set();
5
- for (let n = s; n !== Function.prototype; n = Object.getPrototypeOf(n))
6
- for (const e of Reflect.ownKeys(n)) {
7
- if (c.has(e))
5
+ for (let r = i; r !== Function.prototype; r = Object.getPrototypeOf(r))
6
+ for (const t of Reflect.ownKeys(r)) {
7
+ if (c.has(t))
8
8
  continue;
9
- c.add(e);
10
- const t = Object.getOwnPropertyDescriptor(n, e);
11
- if (typeof t.value == "function") {
12
- const i = t.value, o = typeof e == "string" ? Symbol.for(`ivue.staticBound.${e}`) : Symbol("ivue.staticBound");
13
- Object.defineProperty(r, e, { configurable: !0, enumerable: t.enumerable, get() {
14
- return a(this, o) || Object.defineProperty(this, o, { configurable: !0, value: i.bind(this) }), this[o];
9
+ c.add(t);
10
+ const e = Object.getOwnPropertyDescriptor(r, t);
11
+ if (typeof e.value == "function") {
12
+ const o = e.value, s = typeof t == "string" ? Symbol.for(`ivue.staticBound.${t}`) : Symbol("ivue.staticBound");
13
+ Object.defineProperty(n, t, { configurable: !0, enumerable: e.enumerable, get() {
14
+ return u(this, s) || Object.defineProperty(this, s, { configurable: !0, value: o.bind(this) }), this[s];
15
15
  } });
16
- } else if (t.get && !t.set && typeof e == "string" && e.startsWith("$")) {
17
- const i = t.get, o = Symbol.for(`ivue.staticCache.${e}`);
18
- Object.defineProperty(r, e, { configurable: !0, enumerable: t.enumerable, get() {
19
- return a(this, o) || Object.defineProperty(this, o, { configurable: !0, value: i.call(this) }), this[o];
16
+ } else if (e.get && !e.set && typeof t == "string" && t.startsWith("$")) {
17
+ const o = e.get, s = Symbol.for(`ivue.staticCache.${t}`);
18
+ Object.defineProperty(n, t, { configurable: !0, enumerable: e.enumerable, get() {
19
+ return u(this, s) || Object.defineProperty(this, s, { configurable: !0, value: o.call(this) }), this[s];
20
20
  } });
21
21
  }
22
22
  }
23
- return r;
23
+ return n;
24
+ }
25
+ class h {
26
+ constructor(n) {
27
+ this.make = n, 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
+ }
24
45
  }
25
46
  export {
26
- u as Static
47
+ h as LazyShared,
48
+ a as Static
27
49
  };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("vue"),g=Object.hasOwn,y=Object.getPrototypeOf,w=Object.getOwnPropertyDescriptor,m=Object.getOwnPropertyNames,a=Object.defineProperty,d=Object.prototype,b=Symbol.for("ivue.raw"),p=Symbol.for("ivue.scope"),h=Symbol.for("ivue.processed");function s(e){const r=l.toRaw(e);if(r!==e)return r[b]??(r[b]=r);const n=e[b];return n?n===e?n:l.toRaw(n):e[b]=e}function O(e,r,n,o){a(e,r,{configurable:!0,enumerable:!1,get(){const t=s(this);return t[n]??(t[n]=o.bind(t))},set(t){s(this)[n]=t}})}function S(e,r,n,o,t){const f=r[0]==="$";a(e,r,{configurable:!0,enumerable:!1,get:function(){const c=s(this);if(n in c)return c[n];const i=o.call(c);return f?(c[n]=i,i):(l.isRef(i)?c[n]=i:a(e,r,{configurable:!0,enumerable:!1,get(){return o.call(s(this))},set:t?function(u){return t.call(s(this),u)}:void 0}),i)},set:t?function(c){return t.call(s(this),c)}:void 0})}const v=e=>{var r;return typeof e=="function"&&!!e.prototype&&!((r=w(e,"prototype"))!=null&&r.writable)};exports.Reactive=function(e){const r=[];let n=e.prototype;for(;n&&n!==d;)r.push(n),n=y(n);r.reverse();for(const o of r){if(g(o,h))continue;const t=m(o),f=[];for(const c of t){if(c==="constructor")continue;const i=w(o,c);if(typeof i.value=="function"){const u=Symbol(c);f.push(u),O(o,c,u,i.value)}else if(i.get){const u=Symbol(c);f.push(u),S(o,c,u,i.get,i.set)}}a(o,h,{value:f})}return g(e.prototype,"$stopEffects")||(a(e.prototype,"$watch",{enumerable:!1,configurable:!0,writable:!0,value:function(...o){const t=s(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=s(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(){const o=s(this);try{const t=o[p];t&&t.stop()}finally{delete o[p];let t=y(o);for(;t&&t!==d;){const f=t[h];if(f)for(const c of f)delete o[c];t=y(t)}}}})),e},exports.isClass=v,exports.propsWithDefaults=(e,r,n)=>{const o={};for(const t in r){const f=e==null?void 0:e[t],c=r[t];o[t]={...c},c.required||f===void 0||(typeof f=="object"&&f!==null?o[t].default=()=>n?n(f):structuredClone(f):v(f)?o[t].default=()=>f:o[t].default=f)}return o};
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("vue"),g=Object.hasOwn,y=Object.getPrototypeOf,w=Object.getOwnPropertyDescriptor,m=Object.getOwnPropertyNames,a=Object.defineProperty,d=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!==d;)c.push(r),r=y(r);c.reverse();for(const o of c){if(g(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 g(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!==d;){const u=n[h];if(u)for(const f of u)delete t[f];n=y(n)}}}}})),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,59 +1,59 @@
1
1
  import { effectScope as h, watch as w, watchEffect as O, toRaw as g, isRef as j } from "vue";
2
2
  const m = Object.hasOwn, b = Object.getPrototypeOf, d = Object.getOwnPropertyDescriptor, S = Object.getOwnPropertyNames, a = Object.defineProperty, v = Object.prototype, p = Symbol.for("ivue.raw"), l = Symbol.for("ivue.scope"), y = Symbol.for("ivue.processed");
3
3
  function f(e) {
4
- const r = g(e);
5
- if (r !== e)
6
- return r[p] ?? (r[p] = r);
7
- const n = e[p];
8
- return n ? n === e ? n : g(n) : e[p] = e;
4
+ const c = g(e);
5
+ if (c !== e)
6
+ return c[p] ?? (c[p] = c);
7
+ const r = e[p];
8
+ return r ? r === e ? r : g(r) : e[p] = e;
9
9
  }
10
- function $(e, r, n, o) {
11
- a(e, r, { configurable: !0, enumerable: !1, get() {
10
+ function $(e, c, r, o) {
11
+ a(e, c, { configurable: !0, enumerable: !1, get() {
12
12
  const t = f(this);
13
- return t[n] ?? (t[n] = o.bind(t));
13
+ return t[r] ?? (t[r] = o.bind(t));
14
14
  }, set(t) {
15
- f(this)[n] = t;
15
+ f(this)[r] = t;
16
16
  } });
17
17
  }
18
- function E(e, r, n, o, t) {
19
- const s = r[0] === "$";
20
- a(e, r, { configurable: !0, enumerable: !1, get: function() {
21
- const c = f(this);
22
- if (n in c)
23
- return c[n];
24
- const u = o.call(c);
25
- return s ? (c[n] = u, u) : (j(u) ? c[n] = u : a(e, r, { configurable: !0, enumerable: !1, get() {
18
+ function E(e, c, r, o, t) {
19
+ const n = c[0] === "$";
20
+ a(e, 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(e, c, { configurable: !0, enumerable: !1, get() {
26
26
  return o.call(f(this));
27
27
  }, set: t ? function(i) {
28
28
  return t.call(f(this), i);
29
29
  } : void 0 }), u);
30
- }, set: t ? function(c) {
31
- return t.call(f(this), c);
30
+ }, set: t ? function(s) {
31
+ return t.call(f(this), s);
32
32
  } : void 0 });
33
33
  }
34
34
  function C(e) {
35
- const r = [];
36
- let n = e.prototype;
37
- for (; n && n !== v; )
38
- r.push(n), n = b(n);
39
- r.reverse();
40
- for (const o of r) {
35
+ const c = [];
36
+ let r = e.prototype;
37
+ for (; r && r !== v; )
38
+ c.push(r), r = b(r);
39
+ c.reverse();
40
+ for (const o of c) {
41
41
  if (m(o, y))
42
42
  continue;
43
- const t = S(o), s = [];
44
- for (const c of t) {
45
- if (c === "constructor")
43
+ const t = S(o), n = [];
44
+ for (const s of t) {
45
+ if (s === "constructor")
46
46
  continue;
47
- const u = d(o, c);
47
+ const u = d(o, s);
48
48
  if (typeof u.value == "function") {
49
- const i = Symbol(c);
50
- s.push(i), $(o, c, i, u.value);
49
+ const i = Symbol(s);
50
+ n.push(i), $(o, s, i, u.value);
51
51
  } else if (u.get) {
52
- const i = Symbol(c);
53
- s.push(i), E(o, c, i, u.get, u.set);
52
+ const i = Symbol(s);
53
+ n.push(i), E(o, s, i, u.get, u.set);
54
54
  }
55
55
  }
56
- a(o, y, { value: s });
56
+ a(o, y, { value: n });
57
57
  }
58
58
  return m(e.prototype, "$stopEffects") || (a(e.prototype, "$watch", { enumerable: !1, configurable: !0, writable: !0, value: function(...o) {
59
59
  const t = f(this);
@@ -61,35 +61,37 @@ function C(e) {
61
61
  } }), a(e.prototype, "$watchEffect", { enumerable: !1, configurable: !0, writable: !0, value: function(...o) {
62
62
  const t = f(this);
63
63
  return (t[l] ?? (t[l] = h(!0))).run(() => O(...o));
64
- } }), a(e.prototype, "$stopEffects", { enumerable: !1, configurable: !0, writable: !0, value: function() {
65
- const o = f(this);
64
+ } }), a(e.prototype, "$stopEffects", { enumerable: !1, configurable: !0, writable: !0, value: function(o) {
65
+ const t = f(this);
66
66
  try {
67
- const t = o[l];
68
- t && t.stop();
67
+ const n = t[l];
68
+ n && n.stop();
69
69
  } finally {
70
- delete o[l];
71
- let t = b(o);
72
- for (; t && t !== v; ) {
73
- const s = t[y];
74
- if (s)
75
- for (const c of s)
76
- delete o[c];
77
- t = b(t);
70
+ if (delete t[l], (o == null ? void 0 : o.reset) !== !1) {
71
+ let n = b(t);
72
+ for (; n && n !== v; ) {
73
+ const s = n[y];
74
+ if (s)
75
+ for (const u of s)
76
+ delete t[u];
77
+ n = b(n);
78
+ }
78
79
  }
79
80
  }
80
81
  } })), e;
81
82
  }
82
- const P = (e) => {
83
- var r;
84
- return typeof e == "function" && !!e.prototype && !((r = d(e, "prototype")) != null && r.writable);
85
- }, D = (e, r, n) => {
83
+ function P(e) {
84
+ var c;
85
+ return typeof e == "function" && !!e.prototype && !((c = d(e, "prototype")) != null && c.writable);
86
+ }
87
+ function D(e, c, r) {
86
88
  const o = {};
87
- for (const t in r) {
88
- const s = e == null ? void 0 : e[t], c = r[t];
89
- o[t] = { ...c }, c.required || s === void 0 || (typeof s == "object" && s !== null ? o[t].default = () => n ? n(s) : structuredClone(s) : P(s) ? o[t].default = () => s : o[t].default = s);
89
+ for (const t in c) {
90
+ const n = e == null ? void 0 : e[t], s = c[t];
91
+ o[t] = { ...s }, s.required || n === void 0 || (typeof n == "object" && n !== null ? o[t].default = () => r ? r(n) : structuredClone(n) : P(n) ? o[t].default = () => n : o[t].default = n);
90
92
  }
91
93
  return o;
92
- };
94
+ }
93
95
  export {
94
96
  C as Reactive,
95
97
  P as isClass,
@@ -0,0 +1,71 @@
1
+ /**
2
+ * `LazyShared<T>` — the safe shared-store cell for static classes.
3
+ *
4
+ * The static-class split (see `Static()`): `$`-prefixed static getters
5
+ * are compute-once-PER-RECEIVER caches — perfect for memos and per-class
6
+ * tuning, where a subclass forking its own copy is the feature. A SHARED
7
+ * store (a registry, a ledger) must never live there: per-receiver
8
+ * caching means a subclass reading `this.$store` silently forks the
9
+ * registry. The fix is a `static readonly` FIELD — one reference on the
10
+ * declaring class, inherited, never receiver-cached — but an eager field
11
+ * initializer runs at MODULE LOAD, so it may only hold dependency-free
12
+ * values; the moment it constructs another namespace's class it races
13
+ * import cycles.
14
+ *
15
+ * `LazyShared` closes the triangle. The field eagerly stores the CELL
16
+ * (load-safe — a thunk evaluates nothing), the thunk runs on first
17
+ * `.value` read (cycle-safe — every module in any import cycle has
18
+ * finished loading), and memoization mutates cell-internal state
19
+ * (fork-safe — no receiver, subclass included, can fork it):
20
+ *
21
+ * class $SearchRegistry {
22
+ * protected static readonly sharedBackend = new LazyShared(
23
+ * () => new SearchBackend.Class(),
24
+ * );
25
+ * protected static get $backend() {
26
+ * return this.sharedBackend.value; // the field IS the pin
27
+ * }
28
+ * }
29
+ *
30
+ * A thunk that reads its own cell (directly or through another cell)
31
+ * throws a NAMED cycle error instead of a bare stack overflow, and a
32
+ * failed construction leaves the cell retryable, never poisoned.
33
+ *
34
+ * Ships from `ivue/extras` (not the reactive core) so the primary `ivue`
35
+ * entry stays minimal.
36
+ */
37
+ export class LazyShared<T> {
38
+ constructor(private readonly make: () => T) {}
39
+
40
+ private constructed = false;
41
+ private constructing = false;
42
+ private stored: T | null = null;
43
+
44
+ get value(): T {
45
+ if (!this.constructed) {
46
+ if (this.constructing) {
47
+ throw new Error(
48
+ 'LazyShared thunk cycle: this cell is read inside its own ' +
49
+ 'construction. Break the dependency between the two thunks.',
50
+ );
51
+ }
52
+ this.constructing = true;
53
+ try {
54
+ this.stored = this.make();
55
+ this.constructed = true;
56
+ } finally {
57
+ this.constructing = false;
58
+ }
59
+ }
60
+ return this.stored as T;
61
+ }
62
+
63
+ /**
64
+ * Drop the constructed value; the next read constructs again. For
65
+ * tests and process recomposition — production code never resets.
66
+ */
67
+ reset(): void {
68
+ this.constructed = false;
69
+ this.stored = null;
70
+ }
71
+ }
package/lib/Reactive.ts CHANGED
@@ -257,15 +257,19 @@ export function Reactive<C extends new (...args: any) => any>(
257
257
 
258
258
  /**
259
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().
260
+ * $watch) and drop all cached cells so refs/computeds become collectable
261
+ * the next touch re-materializes fresh cells (disposal is a reset).
262
+ * Pass { reset: false } to stop the watchers ONLY: every cached cell
263
+ * survives with its current value, and the instance can $watch again in a
264
+ * fresh scope. No hooks — ivue never calls user code; compose richer
265
+ * cleanup as an ordinary method that does its own work and then calls
266
+ * $stopEffects().
263
267
  */
264
268
  defineProperty(targetClass.prototype, '$stopEffects', {
265
269
  enumerable: false,
266
270
  configurable: true,
267
271
  writable: true,
268
- value: function (this: any) {
272
+ value: function (this: any, options?: { reset?: boolean }) {
269
273
  const raw = resolveRaw(this);
270
274
  try {
271
275
  const scope = raw[SCOPE];
@@ -274,18 +278,22 @@ export function Reactive<C extends new (...args: any) => any>(
274
278
  // SCOPE is ivue-owned but is not a method/getter cache key.
275
279
  delete raw[SCOPE];
276
280
 
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];
281
+ // { reset: false } = stop watchers only; cells keep their
282
+ // values (a `return` here would swallow a throwing stop()).
283
+ if (options?.reset !== false) {
284
+ // Each processed prototype's PROCESSED marker carries the
285
+ // symbols it may cache on an instance. Walk Child -> Base and
286
+ // remove only those known keys.
287
+ let prototype = getPrototypeOf(raw);
288
+ while (prototype && prototype !== objectPrototype) {
289
+ const cacheKeys = prototype[PROCESSED] as
290
+ | readonly symbol[]
291
+ | undefined;
292
+ if (cacheKeys) {
293
+ for (const cacheKey of cacheKeys) delete raw[cacheKey];
294
+ }
295
+ prototype = getPrototypeOf(prototype);
287
296
  }
288
- prototype = getPrototypeOf(prototype);
289
297
  }
290
298
  }
291
299
  },
@@ -321,7 +329,7 @@ export type VuePropsWithDefaults<T extends VuePropsObject> = {
321
329
  * @param val Any value
322
330
  * @returns boolean If it's a JavaScript Class returns true
323
331
  */
324
- export const isClass = (val: any): boolean => {
332
+ export function isClass(val: any): boolean {
325
333
  if (typeof val !== 'function') return false; // Not a function, so not a class function either
326
334
 
327
335
  if (!val.prototype) return false; // Arrow function, so not a class
@@ -333,7 +341,7 @@ export const isClass = (val: any): boolean => {
333
341
  } else {
334
342
  return true; // Class -> Not a function
335
343
  }
336
- };
344
+ }
337
345
  /**
338
346
  * Creates props with defaults in defineComponent() style.
339
347
  *
@@ -365,12 +373,12 @@ export const isClass = (val: any): boolean => {
365
373
  * @param customCloner Optional cloner used for object/array defaults (defaults to structuredClone)
366
374
  * @returns Props declared in defineComponent() style with all properties having default property declared.
367
375
  */
368
- export const propsWithDefaults = <T extends VuePropsObject>(
376
+ export function propsWithDefaults<T extends VuePropsObject>(
369
377
  defaults: Record<string, any>,
370
378
  typedProps: T,
371
379
  // Optional: Allows user to pass a custom cloner if structuredClone isn't enough
372
380
  customCloner?: (val: any) => any,
373
- ): VuePropsWithDefaults<T> => {
381
+ ): VuePropsWithDefaults<T> {
374
382
  // NON-MUTATING: descriptor objects are routinely SHARED between props
375
383
  // maps (`{ ...baseParamsTypes, extra }` — the spread copies the outer
376
384
  // object but every inner `{ type }` descriptor stays the same reference).
@@ -396,7 +404,7 @@ export const propsWithDefaults = <T extends VuePropsObject>(
396
404
  }
397
405
  }
398
406
  return result as VuePropsWithDefaults<T>;
399
- };
407
+ }
400
408
 
401
409
  /**
402
410
  * Type Utilities
@@ -430,8 +438,10 @@ export type ReactiveInstance<T> = T &
430
438
  $watch: typeof watch;
431
439
  /** Register a watchEffect in the instance's lazy effect scope (same signature as Vue `watchEffect`). */
432
440
  $watchEffect: typeof watchEffect;
433
- /** Stop the instance's effect scope and drop cached cells. */
434
- $stopEffects: () => void;
441
+ /** Stop the instance's effect scope and drop cached cells (the next
442
+ * touch re-materializes). `{ reset: false }` stops watchers only —
443
+ * every cached cell survives with its current value. */
444
+ $stopEffects: (options?: { reset?: boolean }) => void;
435
445
  };
436
446
 
437
447
  export type ReactiveClass<C extends new (...args: any) => any> = {