ivue 2.0.0 → 2.1.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.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `Static()` — the static-side sibling of `Reactive()`.
3
+ *
4
+ * Lazily binds every visible static method of a capability class without
5
+ * touching the raw class, so methods stay referentially stable and safe to
6
+ * retain as callbacks (routers, watchers, command handlers) while the
7
+ * selected `Class` slot remains replaceable by a kernel/plugin.
8
+ *
9
+ * This is the namespace pattern's backend adapter: canonical namespace +
10
+ * mutable `Class` slot + late reads, for STATELESS capability classes (a
11
+ * function bag). Never wrap stateful/reactive instance classes with it — use
12
+ * `Reactive()` for those; `Static()` operates on the single class object and
13
+ * has no instance dimension.
14
+ *
15
+ * Ships from `ivue/extras` (not the reactive core) so the primary `ivue`
16
+ * entry stays minimal.
17
+ */
18
+ export declare type ClassConstructor = new (...arguments_: any[]) => any;
19
+ export declare function Static<Class extends ClassConstructor>(targetClass: Class): Class;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),exports.Static=function(o){const r=class extends o{},c=new Set;for(let e=o;e!==Function.prototype;e=Object.getPrototypeOf(e))for(const t of Reflect.ownKeys(e)){if(c.has(t))continue;c.add(t);const n=Object.getOwnPropertyDescriptor(e,t);if(typeof n.value!="function")continue;const u=n.value;Object.defineProperty(r,t,{configurable:!0,enumerable:n.enumerable,get(){const i=u.bind(this);return Object.defineProperty(this,t,{...n,value:i}),i}})}return r};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `ivue/extras` — the ivue toolkit beyond the reactive core.
3
+ *
4
+ * Kept out of the primary `ivue` entry so that entry stays minimal (the
5
+ * 1.1kb reactive engine). Import these explicitly:
6
+ *
7
+ * import { Static } from 'ivue/extras';
8
+ */
9
+ export { Static, type ClassConstructor } from './Static';
@@ -0,0 +1,22 @@
1
+ function f(o) {
2
+ const r = class extends o {
3
+ }, c = /* @__PURE__ */ new Set();
4
+ for (let e = o; e !== Function.prototype; e = Object.getPrototypeOf(e))
5
+ for (const t of Reflect.ownKeys(e)) {
6
+ if (c.has(t))
7
+ continue;
8
+ c.add(t);
9
+ const n = Object.getOwnPropertyDescriptor(e, t);
10
+ if (typeof n.value != "function")
11
+ continue;
12
+ const s = n.value;
13
+ Object.defineProperty(r, t, { configurable: !0, enumerable: n.enumerable, get() {
14
+ const i = s.bind(this);
15
+ return Object.defineProperty(this, t, { ...n, value: i }), i;
16
+ } });
17
+ }
18
+ return r;
19
+ }
20
+ export {
21
+ f as Static
22
+ };
package/dist/index.cjs ADDED
@@ -0,0 +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};
package/lib/Static.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * `Static()` — the static-side sibling of `Reactive()`.
3
+ *
4
+ * Lazily binds every visible static method of a capability class without
5
+ * touching the raw class, so methods stay referentially stable and safe to
6
+ * retain as callbacks (routers, watchers, command handlers) while the
7
+ * selected `Class` slot remains replaceable by a kernel/plugin.
8
+ *
9
+ * This is the namespace pattern's backend adapter: canonical namespace +
10
+ * mutable `Class` slot + late reads, for STATELESS capability classes (a
11
+ * function bag). Never wrap stateful/reactive instance classes with it — use
12
+ * `Reactive()` for those; `Static()` operates on the single class object and
13
+ * has no instance dimension.
14
+ *
15
+ * Ships from `ivue/extras` (not the reactive core) so the primary `ivue`
16
+ * entry stays minimal.
17
+ */
18
+ export type ClassConstructor = new (...arguments_: any[]) => any;
19
+
20
+ export function Static<Class extends ClassConstructor>(targetClass: Class): Class {
21
+ const SelectedClass = class extends targetClass {};
22
+ const visitedKeys = new Set<PropertyKey>();
23
+
24
+ for (
25
+ let currentClass: ClassConstructor = targetClass;
26
+ currentClass !== Function.prototype;
27
+ currentClass = Object.getPrototypeOf(currentClass)
28
+ ) {
29
+ for (const key of Reflect.ownKeys(currentClass)) {
30
+ if (visitedKeys.has(key)) continue;
31
+ visitedKeys.add(key);
32
+
33
+ const descriptor = Object.getOwnPropertyDescriptor(currentClass, key)!;
34
+ if (typeof descriptor.value !== 'function') continue;
35
+ const method = descriptor.value;
36
+
37
+ Object.defineProperty(SelectedClass, key, {
38
+ configurable: true,
39
+ enumerable: descriptor.enumerable,
40
+ get(this: ClassConstructor) {
41
+ const boundMethod = method.bind(this);
42
+ Object.defineProperty(this, key, { ...descriptor, value: boundMethod });
43
+ return boundMethod;
44
+ },
45
+ });
46
+ }
47
+ }
48
+
49
+ return SelectedClass as Class;
50
+ }
@@ -0,0 +1,99 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { Static } from '../Static';
3
+
4
+ describe('Static', () => {
5
+ it('returns a subclass and leaves the raw class untouched', () => {
6
+ class $Mailer {
7
+ static send(message: string) {
8
+ return `sent:${message}`;
9
+ }
10
+ }
11
+
12
+ const Mailer = Static($Mailer);
13
+
14
+ expect(Mailer).not.toBe($Mailer);
15
+ expect(Object.getPrototypeOf(Mailer)).toBe($Mailer);
16
+ // the raw class keeps its plain data-property descriptor
17
+ const rawDescriptor = Object.getOwnPropertyDescriptor($Mailer, 'send')!;
18
+ expect(typeof rawDescriptor.value).toBe('function');
19
+ expect(rawDescriptor.get).toBeUndefined();
20
+ });
21
+
22
+ it('binds methods lazily with stable identity, safe to detach', () => {
23
+ class $Counter {
24
+ static total = 0;
25
+ static bump() {
26
+ this.total++;
27
+ return this.total;
28
+ }
29
+ }
30
+
31
+ const Counter = Static($Counter);
32
+ const firstRead = Counter.bump;
33
+
34
+ expect(Counter.bump).toBe(firstRead); // cached after first access
35
+ const detached = Counter.bump;
36
+ expect(detached()).toBe(1); // `this` survives detachment
37
+ expect(Counter.total).toBe(1);
38
+ });
39
+
40
+ it('walks the static inheritance chain; child overrides win', () => {
41
+ class $Base {
42
+ static describe() {
43
+ return 'base';
44
+ }
45
+ static inheritedOnly() {
46
+ return 'inherited';
47
+ }
48
+ }
49
+ class $Child extends $Base {
50
+ static describe() {
51
+ return 'child';
52
+ }
53
+ }
54
+
55
+ const Child = Static($Child);
56
+ const describe = Child.describe;
57
+ const inheritedOnly = Child.inheritedOnly;
58
+
59
+ expect(describe()).toBe('child');
60
+ expect(inheritedOnly()).toBe('inherited');
61
+ });
62
+
63
+ it('skips non-function statics and preserves enumerability', () => {
64
+ class $Config {
65
+ static baseUrl = 'https://example.test';
66
+ static resolve(path: string) {
67
+ return this.baseUrl + path;
68
+ }
69
+ }
70
+
71
+ const Config = Static($Config);
72
+
73
+ expect(Config.baseUrl).toBe('https://example.test');
74
+ // class static methods are non-enumerable by construction — the lazy
75
+ // accessor and the materialized bound method must both preserve that
76
+ const before = Object.getOwnPropertyDescriptor(Config, 'resolve')!;
77
+ expect(before.enumerable).toBe(false);
78
+ Config.resolve; // materialize the bound method
79
+ const after = Object.getOwnPropertyDescriptor(Config, 'resolve')!;
80
+ expect(after.enumerable).toBe(false);
81
+ expect(Config.resolve('/path')).toBe('https://example.test/path');
82
+ });
83
+
84
+ it('binds `this` to the wrapped class, so statics compose', () => {
85
+ class $Report {
86
+ static header() {
87
+ return 'HEADER';
88
+ }
89
+ static render() {
90
+ return `${this.header()}|body`;
91
+ }
92
+ }
93
+
94
+ const Report = Static($Report);
95
+ const render = Report.render;
96
+
97
+ expect(render()).toBe('HEADER|body');
98
+ });
99
+ });
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { describe, expect, it } from 'vitest';
5
5
  import * as packageEntry from '../index';
6
+ import * as extrasEntry from '../extras';
6
7
  import { Kernel, kernel } from '../kernel';
7
8
 
8
9
  describe('package entry (index.ts)', () => {
@@ -11,6 +12,12 @@ describe('package entry (index.ts)', () => {
11
12
  });
12
13
  });
13
14
 
15
+ describe('extras entry (extras.ts)', () => {
16
+ it('re-exports the toolkit beyond the core', () => {
17
+ expect(typeof extrasEntry.Static).toBe('function');
18
+ });
19
+ });
20
+
14
21
  describe('Kernel', () => {
15
22
  it('sets, gets, falls back, clears', () => {
16
23
  const container = new Kernel();
package/lib/extras.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `ivue/extras` — the ivue toolkit beyond the reactive core.
3
+ *
4
+ * Kept out of the primary `ivue` entry so that entry stays minimal (the
5
+ * 1.1kb reactive engine). Import these explicitly:
6
+ *
7
+ * import { Static } from 'ivue/extras';
8
+ */
9
+ export { Static, type ClassConstructor } from './Static';
package/package.json CHANGED
@@ -1,17 +1,22 @@
1
1
  {
2
2
  "name": "ivue",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Infinite Vue – Class Based Architecture for Vue 3",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
8
8
  "types": "./dist/index.d.ts",
9
9
  "import": "./dist/index.es.js",
10
- "require": "./dist/index.umd.js"
10
+ "require": "./dist/index.cjs"
11
+ },
12
+ "./extras": {
13
+ "types": "./dist/extras.d.ts",
14
+ "import": "./dist/extras.es.js",
15
+ "require": "./dist/extras.cjs"
11
16
  },
12
17
  "./package.json": "./package.json"
13
18
  },
14
- "main": "./dist/index.umd.js",
19
+ "main": "./dist/index.cjs",
15
20
  "module": "./dist/index.es.js",
16
21
  "types": "./dist/index.d.ts",
17
22
  "scripts": {
package/dist/index.umd.js DELETED
@@ -1 +0,0 @@
1
- (function(l,f){typeof exports=="object"&&typeof module<"u"?f(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],f):f((l=typeof globalThis<"u"?globalThis:l||self).ivue={},l.Vue)})(this,function(l,f){"use strict";const g=Object.hasOwn,d=Object.getPrototypeOf,v=Object.getOwnPropertyDescriptor,O=Object.getOwnPropertyNames,a=Object.defineProperty,m=Object.prototype,y=Symbol.for("ivue.raw"),b=Symbol.for("ivue.scope"),h=Symbol.for("ivue.processed");function s(e){const r=f.toRaw(e);if(r!==e)return r[y]??(r[y]=r);const n=e[y];return n?n===e?n:f.toRaw(n):e[y]=e}function j(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 u=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 u?(c[n]=i,i):(f.isRef(i)?c[n]=i:a(e,r,{configurable:!0,enumerable:!1,get(){return o.call(s(this))},set:t?function(p){return t.call(s(this),p)}:void 0}),i)},set:t?function(c){return t.call(s(this),c)}:void 0})}const w=e=>{var r;return typeof e=="function"&&!!e.prototype&&!((r=v(e,"prototype"))!=null&&r.writable)};l.Reactive=function(e){const r=[];let n=e.prototype;for(;n&&n!==m;)r.push(n),n=d(n);r.reverse();for(const o of r){if(g(o,h))continue;const t=O(o),u=[];for(const c of t){if(c==="constructor")continue;const i=v(o,c);if(typeof i.value=="function"){const p=Symbol(c);u.push(p),j(o,c,p,i.value)}else if(i.get){const p=Symbol(c);u.push(p),S(o,c,p,i.get,i.set)}}a(o,h,{value:u})}return g(e.prototype,"$stopEffects")||(a(e.prototype,"$watch",{enumerable:!1,configurable:!0,writable:!0,value:function(...o){const t=s(this);return(t[b]??(t[b]=f.effectScope(!0))).run(()=>f.watch(...o))}}),a(e.prototype,"$watchEffect",{enumerable:!1,configurable:!0,writable:!0,value:function(...o){const t=s(this);return(t[b]??(t[b]=f.effectScope(!0))).run(()=>f.watchEffect(...o))}}),a(e.prototype,"$stopEffects",{enumerable:!1,configurable:!0,writable:!0,value:function(){const o=s(this);try{const t=o[b];t&&t.stop()}finally{delete o[b];let t=d(o);for(;t&&t!==m;){const u=t[h];if(u)for(const c of u)delete o[c];t=d(t)}}}})),e},l.isClass=w,l.propsWithDefaults=(e,r,n)=>{const o={};for(const t in r){const u=e==null?void 0:e[t],c=r[t];o[t]={...c},c.required||u===void 0||(typeof u=="object"&&u!==null?o[t].default=()=>n?n(u):structuredClone(u):w(u)?o[t].default=()=>u:o[t].default=u)}return o},Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});