ivue 2.3.0 → 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.
@@ -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/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,
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> = {
@@ -735,6 +735,64 @@ describe('Reactive()', () => {
735
735
  expect(() => instance.$stopEffects()).not.toThrow();
736
736
  });
737
737
 
738
+ it('{ reset: false } stops watchers but every cached cell survives', async () => {
739
+ class Session {
740
+ get counter() {
741
+ return ref(0);
742
+ }
743
+ get doubled() {
744
+ return computed(() => (this as any).counter.value * 2);
745
+ }
746
+ }
747
+ const instance: any = new (Reactive(Session))();
748
+ let observed = 0;
749
+ instance.$watchEffect(() => {
750
+ observed = instance.doubled.value; // first-touches the computed IN the scope
751
+ });
752
+ const counterCell = instance.counter;
753
+ const doubledCell = instance.doubled;
754
+ instance.counter.value = 21;
755
+ await nextTick();
756
+ expect(observed).toBe(42);
757
+
758
+ instance.$stopEffects({ reset: false });
759
+
760
+ // watchers are dead…
761
+ instance.counter.value = 100;
762
+ await nextTick();
763
+ expect(observed).toBe(42);
764
+ // …but the cells survive with their CURRENT values (no re-init)
765
+ expect(instance.counter).toBe(counterCell);
766
+ expect(instance.doubled).toBe(doubledCell);
767
+ expect(instance.counter.value).toBe(100);
768
+ // the surviving computed still evaluates (pull-based past scope death)
769
+ expect(instance.doubled.value).toBe(200);
770
+
771
+ // and a SECOND life works: a fresh scope tracks the old cells
772
+ let observedAgain = 0;
773
+ instance.$watch(
774
+ () => instance.doubled.value,
775
+ (doubledValue: number) => {
776
+ observedAgain = doubledValue;
777
+ },
778
+ );
779
+ instance.counter.value = 7;
780
+ await nextTick();
781
+ expect(observedAgain).toBe(14);
782
+ });
783
+
784
+ it('default call still resets — cells re-initialize after teardown', () => {
785
+ class Session {
786
+ get counter() {
787
+ return ref(0);
788
+ }
789
+ }
790
+ const instance: any = new (Reactive(Session))();
791
+ instance.counter.value = 41;
792
+ instance.$stopEffects();
793
+ expect(instance.counter.value).toBe(0);
794
+ });
795
+
738
796
  it('$stopEffects is injected only once (idempotent re-Reactive)', () => {
739
797
  class Store {
740
798
  m() {
package/lib/ivue.ts CHANGED
@@ -1,3 +1,20 @@
1
+ /**
2
+ * ============================================================
3
+ * DEPRECATED — ivue v1 (the reactive-proxy engine)
4
+ * ============================================================
5
+ *
6
+ * This file is the ORIGINAL v1 implementation: `ivue(Class, ...args)`
7
+ * wraps instances in `reactive()` and converts accessors to computeds.
8
+ * It is kept in-tree for reference and migration only.
9
+ *
10
+ * v2 is `Reactive()` in ./Reactive.ts (the package's main entry) — a
11
+ * one-time prototype transform: instances stay plain objects, plain
12
+ * getters cost zero bytes, creation measures 55-253x faster. New code
13
+ * must never import from this file.
14
+ *
15
+ * Migration guide: https://ivue.dev/guide/standard
16
+ */
17
+
1
18
  import type { ComputedRef, ExtractPropTypes, Ref, ToRef } from 'vue';
2
19
  import { computed, markRaw, reactive, ref, shallowRef, toRef } from 'vue';
3
20
  import type { Ref as DemiRef } from 'vue-demi';
@@ -399,6 +416,8 @@ const getClassPropertiesAccessorsMap = (obj: object): PropsMapValue => {
399
416
  * get getter () { return 'hello world'; }
400
417
  * }
401
418
  *
419
+ * @deprecated v1 engine — use `Reactive()` from the main `ivue` entry
420
+ * instead; see https://ivue.dev/guide/standard for the migration.
402
421
  * @param className Any Class
403
422
  * @param args Class constructor arguments that you would pass to a `new AnyClass(args...)`
404
423
  * @returns {IVue<T>}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ivue",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Infinite Vue – Class Based Architecture for Vue 3",
5
5
  "type": "module",
6
6
  "exports": {
@@ -47,6 +47,7 @@
47
47
  "render:form-header": "node docs_v2/scripts/brand-image-generator.mjs form-header",
48
48
  "render:banner": "node docs_v2/scripts/brand-image-generator.mjs blog",
49
49
  "render:diagram": "node docs_v2/scripts/brand-image-generator.mjs diagram",
50
+ "render:page-og": "node docs_v2/scripts/page-og-generator.mjs",
50
51
  "render:embeds": "node docs_v2/scripts/blog-embed-shots.mjs",
51
52
  "render:code-shots": "node docs_v2/scripts/blog-code-shots.mjs",
52
53
  "sync:blog-dates": "node docs_v2/scripts/blog-dates-generator.mjs",
@@ -387,14 +387,32 @@ class $Session {
387
387
  // Outliving instance: $watch/$watchEffect register in the
388
388
  // instance's lazy effectScope — there is no component scope here
389
389
  // to reap plain watch.
390
+ // WATCHERS live behind a method, not inline in the constructor —
391
+ // the constructor calls it once, and the instance can RESTART its
392
+ // watchers after a keep-state stop (see suspend() below).
390
393
  constructor() {
394
+ this.startWatchers();
395
+ // If constructed INSIDE some scope, auto-wire teardown instead:
396
+ // getCurrentScope() && onScopeDispose(() => this.$stopEffects());
397
+ }
398
+
399
+ startWatchers() {
391
400
  this.$watch(
392
401
  () => this.user.value,
393
402
  (user, previousUser) => this.onUserChanged(user, previousUser),
394
403
  );
395
404
  this.$watchEffect(() => this.persist());
396
- // If constructed INSIDE some scope, auto-wire teardown instead:
397
- // getCurrentScope() && onScopeDispose(() => this.$stopEffects());
405
+ }
406
+
407
+ // SUSPEND / RESUME: { reset: false } stops the watchers ONLY — every
408
+ // cached cell survives with its current value. startWatchers() in a
409
+ // fresh scope resumes. (Default $stopEffects() also CLEARS the cells:
410
+ // the next touch re-runs initializers — disposal is a reset.)
411
+ suspend() {
412
+ this.$stopEffects({ reset: false });
413
+ }
414
+ resume() {
415
+ this.startWatchers();
398
416
  }
399
417
 
400
418
  // CLEANUP composes as an ORDINARY method — no hooks, no reserved
@@ -490,7 +508,11 @@ until mount — use `?.` in watch getters).
490
508
  its leaf reads subscribe directly (non-intuitive but structural).
491
509
  - The source MUST be the FUNCTION form. `watch(instance.plainGetter, cb)` passes a
492
510
  dead snapshot and never fires.
493
- - `$stopEffects()` stops the instance scope and clears cached Refs/Computeds;
511
+ - `$stopEffects()` stops the instance scope and clears cached Refs/Computeds
512
+ (the next touch re-materializes — disposal is a reset);
513
+ `$stopEffects({ reset: false })` stops the WATCHERS only — every cached
514
+ cell survives with its current value, and `startWatchers()` in a fresh
515
+ scope resumes (the suspend/resume pattern above);
494
516
  instances that never `$watch` allocate no scope. There are NO hooks — richer
495
517
  cleanup is an ordinary method that does its work and then calls
496
518
  `$stopEffects()` itself. Every outliving instance needs an OWNER that calls