mvc-kit 2.5.3 → 2.5.4

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.
Files changed (125) hide show
  1. package/dist/Channel.cjs +291 -0
  2. package/dist/Channel.cjs.map +1 -0
  3. package/dist/Channel.js +291 -0
  4. package/dist/Channel.js.map +1 -0
  5. package/dist/Collection.cjs +452 -0
  6. package/dist/Collection.cjs.map +1 -0
  7. package/dist/Collection.js +452 -0
  8. package/dist/Collection.js.map +1 -0
  9. package/dist/Controller.cjs +57 -0
  10. package/dist/Controller.cjs.map +1 -0
  11. package/dist/Controller.js +57 -0
  12. package/dist/Controller.js.map +1 -0
  13. package/dist/EventBus.cjs +84 -0
  14. package/dist/EventBus.cjs.map +1 -0
  15. package/dist/EventBus.js +84 -0
  16. package/dist/EventBus.js.map +1 -0
  17. package/dist/Model.cjs +175 -0
  18. package/dist/Model.cjs.map +1 -0
  19. package/dist/Model.js +175 -0
  20. package/dist/Model.js.map +1 -0
  21. package/dist/PersistentCollection.cjs +285 -0
  22. package/dist/PersistentCollection.cjs.map +1 -0
  23. package/dist/PersistentCollection.js +285 -0
  24. package/dist/PersistentCollection.js.map +1 -0
  25. package/dist/Resource.cjs +308 -0
  26. package/dist/Resource.cjs.map +1 -0
  27. package/dist/Resource.js +308 -0
  28. package/dist/Resource.js.map +1 -0
  29. package/dist/Service.cjs +51 -0
  30. package/dist/Service.cjs.map +1 -0
  31. package/dist/Service.js +51 -0
  32. package/dist/Service.js.map +1 -0
  33. package/dist/ViewModel.cjs +582 -0
  34. package/dist/ViewModel.cjs.map +1 -0
  35. package/dist/ViewModel.d.ts +1 -7
  36. package/dist/ViewModel.d.ts.map +1 -1
  37. package/dist/ViewModel.js +582 -0
  38. package/dist/ViewModel.js.map +1 -0
  39. package/dist/errors.cjs +79 -0
  40. package/dist/errors.cjs.map +1 -0
  41. package/dist/errors.js +79 -0
  42. package/dist/errors.js.map +1 -0
  43. package/dist/mvc-kit.cjs +29 -1
  44. package/dist/mvc-kit.cjs.map +1 -1
  45. package/dist/mvc-kit.js +27 -1132
  46. package/dist/mvc-kit.js.map +1 -1
  47. package/dist/react/guards.cjs +7 -0
  48. package/dist/react/guards.cjs.map +1 -0
  49. package/dist/react/guards.js +7 -0
  50. package/dist/react/guards.js.map +1 -0
  51. package/dist/react/provider.cjs +26 -0
  52. package/dist/react/provider.cjs.map +1 -0
  53. package/dist/react/provider.js +26 -0
  54. package/dist/react/provider.js.map +1 -0
  55. package/dist/react/use-event-bus.cjs +26 -0
  56. package/dist/react/use-event-bus.cjs.map +1 -0
  57. package/dist/react/use-event-bus.js +26 -0
  58. package/dist/react/use-event-bus.js.map +1 -0
  59. package/dist/react/use-instance.cjs +31 -0
  60. package/dist/react/use-instance.cjs.map +1 -0
  61. package/dist/react/use-instance.js +31 -0
  62. package/dist/react/use-instance.js.map +1 -0
  63. package/dist/react/use-local.cjs +64 -0
  64. package/dist/react/use-local.cjs.map +1 -0
  65. package/dist/react/use-local.js +64 -0
  66. package/dist/react/use-local.js.map +1 -0
  67. package/dist/react/use-model.cjs +80 -0
  68. package/dist/react/use-model.cjs.map +1 -0
  69. package/dist/react/use-model.js +80 -0
  70. package/dist/react/use-model.js.map +1 -0
  71. package/dist/react/use-singleton.cjs +21 -0
  72. package/dist/react/use-singleton.cjs.map +1 -0
  73. package/dist/react/use-singleton.js +21 -0
  74. package/dist/react/use-singleton.js.map +1 -0
  75. package/dist/react/use-teardown.cjs +22 -0
  76. package/dist/react/use-teardown.cjs.map +1 -0
  77. package/dist/react/use-teardown.js +22 -0
  78. package/dist/react/use-teardown.js.map +1 -0
  79. package/dist/react-native/NativeCollection.cjs +76 -0
  80. package/dist/react-native/NativeCollection.cjs.map +1 -0
  81. package/dist/react-native/NativeCollection.js +76 -0
  82. package/dist/react-native/NativeCollection.js.map +1 -0
  83. package/dist/react-native.cjs +4 -1
  84. package/dist/react-native.cjs.map +1 -1
  85. package/dist/react-native.js +2 -60
  86. package/dist/react-native.js.map +1 -1
  87. package/dist/react.cjs +19 -1
  88. package/dist/react.cjs.map +1 -1
  89. package/dist/react.js +17 -145
  90. package/dist/react.js.map +1 -1
  91. package/dist/singleton.cjs +34 -0
  92. package/dist/singleton.cjs.map +1 -0
  93. package/dist/singleton.js +34 -0
  94. package/dist/singleton.js.map +1 -0
  95. package/dist/walkPrototypeChain.cjs +15 -0
  96. package/dist/walkPrototypeChain.cjs.map +1 -0
  97. package/dist/walkPrototypeChain.d.ts +9 -0
  98. package/dist/walkPrototypeChain.d.ts.map +1 -0
  99. package/dist/walkPrototypeChain.js +15 -0
  100. package/dist/walkPrototypeChain.js.map +1 -0
  101. package/dist/web/IndexedDBCollection.cjs +37 -0
  102. package/dist/web/IndexedDBCollection.cjs.map +1 -0
  103. package/dist/web/IndexedDBCollection.js +37 -0
  104. package/dist/web/IndexedDBCollection.js.map +1 -0
  105. package/dist/web/WebStorageCollection.cjs +85 -0
  106. package/dist/web/WebStorageCollection.cjs.map +1 -0
  107. package/dist/web/WebStorageCollection.js +85 -0
  108. package/dist/web/WebStorageCollection.js.map +1 -0
  109. package/dist/web/idb.cjs +121 -0
  110. package/dist/web/idb.cjs.map +1 -0
  111. package/dist/web/idb.js +121 -0
  112. package/dist/web/idb.js.map +1 -0
  113. package/dist/web.cjs +6 -1
  114. package/dist/web.cjs.map +1 -1
  115. package/dist/web.js +4 -178
  116. package/dist/web.js.map +1 -1
  117. package/package.json +4 -2
  118. package/dist/PersistentCollection-B8kNECDj.cjs +0 -2
  119. package/dist/PersistentCollection-B8kNECDj.cjs.map +0 -1
  120. package/dist/PersistentCollection-CbYqzFHc.js +0 -542
  121. package/dist/PersistentCollection-CbYqzFHc.js.map +0 -1
  122. package/dist/singleton-CaEXSbYg.js +0 -89
  123. package/dist/singleton-CaEXSbYg.js.map +0 -1
  124. package/dist/singleton-L-u2W_lX.cjs +0 -2
  125. package/dist/singleton-L-u2W_lX.cjs.map +0 -1
@@ -1,63 +1,5 @@
1
- import { P as s } from "./PersistentCollection-CbYqzFHc.js";
2
- const r = typeof __MVC_KIT_DEV__ < "u" && __MVC_KIT_DEV__;
3
- let e = null;
4
- class c extends s {
5
- /**
6
- * Configure the default storage adapter for all NativeCollection subclasses.
7
- * Call once at app startup. Per-class method overrides take priority.
8
- */
9
- static configure(t) {
10
- e = t;
11
- }
12
- /** Reset the configured adapter (for testing). */
13
- static resetAdapter() {
14
- e = null;
15
- }
16
- // ── Per-class override points ──
17
- getItem(t) {
18
- if (e) return e.getItem(t);
19
- throw r ? new Error(
20
- `[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`
21
- ) : new Error("[mvc-kit] No storage adapter configured.");
22
- }
23
- setItem(t, i) {
24
- if (e) return e.setItem(t, i);
25
- throw r ? new Error(
26
- `[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`
27
- ) : new Error("[mvc-kit] No storage adapter configured.");
28
- }
29
- removeItem(t) {
30
- if (e) return e.removeItem(t);
31
- throw r ? new Error(
32
- `[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`
33
- ) : new Error("[mvc-kit] No storage adapter configured.");
34
- }
35
- // ── Persist interface (blob strategy) ──
36
- async persistGetAll() {
37
- const t = await this.getItem(this.storageKey);
38
- if (!t) return [];
39
- try {
40
- return this.deserialize(t);
41
- } catch {
42
- return r && console.warn(
43
- `[mvc-kit] Corrupted data in storage key "${this.storageKey}". Ignoring stored data.`
44
- ), [];
45
- }
46
- }
47
- async persistGet(t) {
48
- return (await this.persistGetAll()).find((o) => o.id === t) ?? null;
49
- }
50
- async persistSet(t) {
51
- await this.setItem(this.storageKey, this.serialize([...this.items]));
52
- }
53
- async persistRemove(t) {
54
- await this.setItem(this.storageKey, this.serialize([...this.items]));
55
- }
56
- async persistClear() {
57
- await this.removeItem(this.storageKey);
58
- }
59
- }
1
+ import { NativeCollection } from "./react-native/NativeCollection.js";
60
2
  export {
61
- c as NativeCollection
3
+ NativeCollection
62
4
  };
63
5
  //# sourceMappingURL=react-native.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"react-native.js","sources":["../src/react-native/NativeCollection.ts"],"sourcesContent":["import { PersistentCollection } from '../PersistentCollection';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\ninterface StorageAdapter {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n removeItem(key: string): Promise<void>;\n}\n\nlet _adapter: StorageAdapter | null = null;\n\n/**\n * PersistentCollection for React Native, backed by any async key-value store.\n * Uses blob strategy (full state as a single JSON string under `storageKey`).\n *\n * **Requires manual `hydrate()` call** (async storage).\n *\n * ## Setup (once at app startup)\n *\n * ```ts\n * import { NativeCollection } from 'mvc-kit/react-native';\n * import AsyncStorage from '@react-native-async-storage/async-storage';\n *\n * NativeCollection.configure({\n * getItem: (key) => AsyncStorage.getItem(key),\n * setItem: (key, value) => AsyncStorage.setItem(key, value),\n * removeItem: (key) => AsyncStorage.removeItem(key),\n * });\n * ```\n *\n * ## Usage\n *\n * ```ts\n * class TodosCollection extends NativeCollection<Todo> {\n * protected readonly storageKey = 'todos';\n * }\n * ```\n *\n * ## Per-class override (edge cases)\n *\n * ```ts\n * class SecureCollection extends NativeCollection<Secret> {\n * protected readonly storageKey = 'secrets';\n * protected async getItem(key: string) { return SecureStore.getItem(key); }\n * protected async setItem(key: string, value: string) { await SecureStore.setItem(key, value); }\n * protected async removeItem(key: string) { await SecureStore.removeItem(key); }\n * }\n * ```\n */\nexport abstract class NativeCollection<\n T extends { id: string | number },\n> extends PersistentCollection<T> {\n /**\n * Configure the default storage adapter for all NativeCollection subclasses.\n * Call once at app startup. Per-class method overrides take priority.\n */\n static configure(adapter: StorageAdapter): void {\n _adapter = adapter;\n }\n\n /** Reset the configured adapter (for testing). */\n static resetAdapter(): void {\n _adapter = null;\n }\n\n // ── Per-class override points ──\n\n protected getItem(key: string): Promise<string | null> {\n if (_adapter) return _adapter.getItem(key);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n protected setItem(key: string, value: string): Promise<void> {\n if (_adapter) return _adapter.setItem(key, value);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n protected removeItem(key: string): Promise<void> {\n if (_adapter) return _adapter.removeItem(key);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n // ── Persist interface (blob strategy) ──\n\n protected async persistGetAll(): Promise<T[]> {\n const raw = await this.getItem(this.storageKey);\n if (!raw) return [];\n try {\n return this.deserialize(raw);\n } catch {\n if (__DEV__) {\n console.warn(\n `[mvc-kit] Corrupted data in storage key \"${this.storageKey}\". Ignoring stored data.`,\n );\n }\n return [];\n }\n }\n\n protected async persistGet(id: T['id']): Promise<T | null> {\n const all = await this.persistGetAll();\n return all.find((i) => i.id === id) ?? null;\n }\n\n protected async persistSet(_items: T[]): Promise<void> {\n await this.setItem(this.storageKey, this.serialize([...this.items]));\n }\n\n protected async persistRemove(_ids: T['id'][]): Promise<void> {\n await this.setItem(this.storageKey, this.serialize([...this.items]));\n }\n\n protected async persistClear(): Promise<void> {\n await this.removeItem(this.storageKey);\n }\n}\n"],"names":["__DEV__","_adapter","NativeCollection","PersistentCollection","adapter","key","value","raw","id","i","_items","_ids"],"mappings":";AAEA,MAAMA,IAAU,OAAO,kBAAoB,OAAe;AAQ1D,IAAIC,IAAkC;AAwC/B,MAAeC,UAEZC,EAAwB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhC,OAAO,UAAUC,GAA+B;AAC9C,IAAAH,IAAWG;AAAA,EACb;AAAA;AAAA,EAGA,OAAO,eAAqB;AAC1B,IAAAH,IAAW;AAAA,EACb;AAAA;AAAA,EAIU,QAAQI,GAAqC;AACrD,QAAIJ,EAAU,QAAOA,EAAS,QAAQI,CAAG;AACzC,UAAIL,IACI,IAAI;AAAA,MACR,gDAAgD,KAAK,YAAY,IAAI;AAAA,IAAA,IAInE,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA,EAEU,QAAQK,GAAaC,GAA8B;AAC3D,QAAIL,EAAU,QAAOA,EAAS,QAAQI,GAAKC,CAAK;AAChD,UAAIN,IACI,IAAI;AAAA,MACR,gDAAgD,KAAK,YAAY,IAAI;AAAA,IAAA,IAInE,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA,EAEU,WAAWK,GAA4B;AAC/C,QAAIJ,EAAU,QAAOA,EAAS,WAAWI,CAAG;AAC5C,UAAIL,IACI,IAAI;AAAA,MACR,gDAAgD,KAAK,YAAY,IAAI;AAAA,IAAA,IAInE,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA;AAAA,EAIA,MAAgB,gBAA8B;AAC5C,UAAMO,IAAM,MAAM,KAAK,QAAQ,KAAK,UAAU;AAC9C,QAAI,CAACA,EAAK,QAAO,CAAA;AACjB,QAAI;AACF,aAAO,KAAK,YAAYA,CAAG;AAAA,IAC7B,QAAQ;AACN,aAAIP,KACF,QAAQ;AAAA,QACN,4CAA4C,KAAK,UAAU;AAAA,MAAA,GAGxD,CAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAgB,WAAWQ,GAAgC;AAEzD,YADY,MAAM,KAAK,cAAA,GACZ,KAAK,CAACC,MAAMA,EAAE,OAAOD,CAAE,KAAK;AAAA,EACzC;AAAA,EAEA,MAAgB,WAAWE,GAA4B;AACrD,UAAM,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC;AAAA,EACrE;AAAA,EAEA,MAAgB,cAAcC,GAAgC;AAC5D,UAAM,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC;AAAA,EACrE;AAAA,EAEA,MAAgB,eAA8B;AAC5C,UAAM,KAAK,WAAW,KAAK,UAAU;AAAA,EACvC;AACF;"}
1
+ {"version":3,"file":"react-native.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
package/dist/react.cjs CHANGED
@@ -1,2 +1,20 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react"),a=require("./singleton-L-u2W_lX.cjs"),g=require("react/jsx-runtime");function y(e){return e!==null&&typeof e=="object"&&typeof e.subscribeAsync=="function"}function l(e){const t=u.useSyncExternalStore(r=>e.subscribe(r),()=>e.state,()=>e.state),n=u.useRef(0);return u.useSyncExternalStore(r=>y(e)?e.subscribeAsync(()=>{n.current++,r()}):()=>{},()=>n.current,()=>0),t}const v=e=>e!==null&&typeof e=="object"&&"state"in e&&"subscribe"in e&&typeof e.subscribe=="function",d=e=>e!==null&&typeof e=="object"&&"init"in e&&typeof e.init=="function";function S(e,t){if(e===void 0)return!1;if(e.length!==t.length)return!0;for(let n=0;n<e.length;n++)if(!Object.is(e[n],t[n]))return!0;return!1}function b(e,...t){let n,r;t.length>0&&Array.isArray(t[t.length-1])?(r=t[t.length-1],n=t.slice(0,-1)):(n=t,r=void 0);const s=u.useRef(null),c=u.useRef(!1),i=u.useRef(void 0);return r!==void 0&&S(i.current,r)&&(s.current?.dispose(),s.current=null),r!==void 0&&(i.current=r),(!s.current||s.current.disposed)&&(typeof e=="function"&&e.prototype&&e.prototype.constructor===e?s.current=new e(...n):s.current=e()),u.useEffect(()=>{const o=s.current;return c.current=!0,d(o)&&o.init(),()=>{c.current=!1,setTimeout(()=>{c.current||o.dispose()},0)}},r??[]),v(s.current)?[l(s.current),s.current]:s.current}function E(e,...t){const n=a.singleton(e,...t);return u.useEffect(()=>{d(n)&&n.init()},[n]),v(n)?[l(n),n]:n}function h(e){const t=u.useRef(null),n=u.useRef(!1);(!t.current||t.current.disposed)&&(t.current=e()),u.useSyncExternalStore(s=>t.current.subscribe(s),()=>t.current.state,()=>t.current.state),u.useEffect(()=>(n.current=!0,d(t.current)&&t.current.init(),()=>{n.current=!1,setTimeout(()=>{n.current||t.current?.dispose()},0)}),[]);const r=t.current;return{state:r.state,errors:r.errors,valid:r.valid,dirty:r.dirty,model:r}}function C(e,t){const n=u.useCallback(()=>({value:e.state[t],error:e.errors[t]}),[e,t]),r=u.useRef(n()),s=u.useCallback(o=>e.subscribe(()=>{const f=n(),p=r.current;(f.value!==p.value||f.error!==p.error)&&(r.current=f,o())}),[e,n]),c=u.useSyncExternalStore(s,()=>r.current,()=>r.current),i=u.useCallback(o=>{const f={[t]:o};e.set(f)},[e,t]);return{value:c.value,error:c.error,set:i}}function m(e,t,n){const r=e instanceof a.EventBus?e:e.events,s=u.useRef(n);s.current=n,u.useEffect(()=>r.on(t,i=>{s.current(i)}),[r,t])}function x(e){return u.useCallback((t,n)=>{e.emit(t,n)},[e])}function T(...e){const t=u.useRef(!1);u.useEffect(()=>(t.current=!0,()=>{t.current=!1,setTimeout(()=>{if(!t.current)for(const n of e)a.teardown(n)},0)}),[])}const R=u.createContext(null);function w({provide:e,children:t}){const n=u.useMemo(()=>{const r=new Map;for(const[s,c]of e)r.set(s,c);return r},[e]);return g.jsx(R.Provider,{value:n,children:t})}function A(e,...t){const n=u.useContext(R);return n?.has(e)?n.get(e):a.singleton(e,...t)}exports.Provider=w;exports.useEmit=x;exports.useEvent=m;exports.useField=C;exports.useInstance=l;exports.useLocal=b;exports.useModel=h;exports.useResolve=A;exports.useSingleton=E;exports.useTeardown=T;
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const useInstance = require("./react/use-instance.cjs");
4
+ const useLocal = require("./react/use-local.cjs");
5
+ const useSingleton = require("./react/use-singleton.cjs");
6
+ const useModel = require("./react/use-model.cjs");
7
+ const useEventBus = require("./react/use-event-bus.cjs");
8
+ const useTeardown = require("./react/use-teardown.cjs");
9
+ const provider = require("./react/provider.cjs");
10
+ exports.useInstance = useInstance.useInstance;
11
+ exports.useLocal = useLocal.useLocal;
12
+ exports.useSingleton = useSingleton.useSingleton;
13
+ exports.useField = useModel.useField;
14
+ exports.useModel = useModel.useModel;
15
+ exports.useEmit = useEventBus.useEmit;
16
+ exports.useEvent = useEventBus.useEvent;
17
+ exports.useTeardown = useTeardown.useTeardown;
18
+ exports.Provider = provider.Provider;
19
+ exports.useResolve = provider.useResolve;
2
20
  //# sourceMappingURL=react.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.cjs","sources":["../src/react/use-instance.ts","../src/react/guards.ts","../src/react/use-local.ts","../src/react/use-singleton.ts","../src/react/use-model.ts","../src/react/use-event-bus.ts","../src/react/use-teardown.ts","../src/react/provider.tsx"],"sourcesContent":["import { useSyncExternalStore, useRef } from 'react';\nimport type { Subscribable } from '../types';\n\nfunction hasAsyncSubscription(obj: unknown): obj is { subscribeAsync(cb: () => void): () => void } {\n return (\n obj !== null &&\n typeof obj === 'object' &&\n typeof (obj as any).subscribeAsync === 'function'\n );\n}\n\n/**\n * Subscribe to an existing Subscribable instance.\n * No ownership - caller manages the instance lifecycle.\n *\n * If the instance has a `subscribeAsync` method (duck-typed),\n * a second subscription ensures async state changes also\n * trigger React re-renders.\n */\nexport function useInstance<S>(subscribable: Subscribable<S>): S {\n const state = useSyncExternalStore(\n (onStoreChange) => subscribable.subscribe(onStoreChange),\n () => subscribable.state,\n () => subscribable.state // SSR snapshot\n );\n\n // Async subscription — forces re-render when any async status changes.\n // Duck-typed: safe for Collection/Model (they don't have subscribeAsync).\n const versionRef = useRef(0);\n useSyncExternalStore(\n (onStoreChange) => {\n if (!hasAsyncSubscription(subscribable)) return () => {};\n return subscribable.subscribeAsync(() => {\n versionRef.current++;\n onStoreChange();\n });\n },\n () => versionRef.current,\n () => 0 // SSR: no async ops server-side\n );\n\n return state;\n}\n","import type { Subscribable } from '../types';\n\n/** @internal Type guard for Subscribable */\nexport const isSubscribable = (obj: unknown): obj is Subscribable<unknown> =>\n obj !== null &&\n typeof obj === 'object' &&\n 'state' in obj &&\n 'subscribe' in obj &&\n typeof (obj as Subscribable<unknown>).subscribe === 'function';\n\n/** @internal Type guard for Initializable */\nexport const isInitializable = (obj: unknown): obj is { init(): void | Promise<void> } =>\n obj !== null &&\n typeof obj === 'object' &&\n 'init' in obj &&\n typeof (obj as any).init === 'function';\n","import { useRef, useEffect } from 'react';\nimport type { DependencyList } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { isSubscribable, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\nimport type { StateOf } from './types';\n\nfunction depsChanged(prev: DependencyList | undefined, next: DependencyList): boolean {\n if (prev === undefined) return false;\n if (prev.length !== next.length) return true;\n for (let i = 0; i < prev.length; i++) {\n if (!Object.is(prev[i], next[i])) return true;\n }\n return false;\n}\n\n// ── With deps (class + initialState + deps) ────────────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<any> & Disposable>(\n Class: new (initialState: StateOf<T>) => T,\n initialState: StateOf<T>,\n deps: DependencyList,\n): [StateOf<T>, T];\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T,\n deps: DependencyList,\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(\n factory: () => T,\n deps: DependencyList,\n): T;\n\n// ── Without deps (existing overloads, unchanged) ───────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<\n T extends Subscribable<S> & Disposable,\n S = StateOf<T>,\n Args extends unknown[] = unknown[]\n>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T;\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(factory: () => T): T;\n\n// ── Implementation ─────────────────────────────────────────────────\n\nexport function useLocal<T extends Disposable, S = StateOf<T>>(\n classOrFactory: (new (...args: unknown[]) => T) | (() => T),\n ...rest: unknown[]\n): [S, T] | T {\n // ── Detect deps: last arg is an array → treat as deps ──\n let args: unknown[];\n let deps: DependencyList | undefined;\n\n if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {\n deps = rest[rest.length - 1] as DependencyList;\n args = rest.slice(0, -1);\n } else {\n args = rest;\n deps = undefined;\n }\n\n const instanceRef = useRef<T | null>(null);\n const mountedRef = useRef(false);\n const prevDepsRef = useRef<DependencyList | undefined>(undefined);\n\n // ── Render phase: dep-change detection ──\n if (deps !== undefined && depsChanged(prevDepsRef.current, deps)) {\n instanceRef.current?.dispose();\n instanceRef.current = null;\n }\n if (deps !== undefined) {\n prevDepsRef.current = deps;\n }\n\n // ── Create instance if needed ──\n if (!instanceRef.current || instanceRef.current.disposed) {\n const isClass =\n typeof classOrFactory === 'function' &&\n classOrFactory.prototype &&\n classOrFactory.prototype.constructor === classOrFactory;\n\n if (isClass) {\n instanceRef.current = new (classOrFactory as new (...a: unknown[]) => T)(...args);\n } else {\n instanceRef.current = (classOrFactory as () => T)();\n }\n }\n\n // ── Effect: init + deferred cleanup ──\n useEffect(() => {\n const instance = instanceRef.current!; // capture for cleanup closure\n mountedRef.current = true;\n if (isInitializable(instance)) {\n instance.init();\n }\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n instance.dispose();\n }\n }, 0);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps ?? []);\n\n // ── Subscribe to state if Subscribable ──\n if (isSubscribable(instanceRef.current)) {\n const state = useInstance(instanceRef.current as unknown as Subscribable<S>);\n return [state, instanceRef.current];\n }\n\n return instanceRef.current;\n}\n","import { useEffect } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { singleton } from '../singleton';\nimport { useInstance } from './use-instance';\nimport { isSubscribable, isInitializable } from './guards';\nimport type { StateOf } from './types';\n\n/**\n * Get singleton Subscribable instance and subscribe to its state.\n * Returns [state, instance] tuple.\n */\nexport function useSingleton<\n T extends Subscribable<S> & Disposable,\n S = StateOf<T>,\n Args extends unknown[] = unknown[]\n>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T];\n\n/**\n * Get singleton Disposable instance (non-Subscribable).\n * Returns the instance directly.\n */\nexport function useSingleton<T extends Disposable, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T;\n\n// Implementation\nexport function useSingleton<T extends Disposable, S = StateOf<T>, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T] | T {\n const instance = singleton(Class, ...args);\n\n useEffect(() => {\n if (isInitializable(instance)) {\n instance.init();\n }\n }, [instance]);\n\n if (isSubscribable(instance)) {\n const state = useInstance(instance) as S;\n return [state, instance];\n }\n\n return instance;\n}\n","import { useRef, useEffect, useSyncExternalStore, useCallback } from 'react';\nimport type { Model } from '../Model';\nimport type { ValidationErrors } from '../types';\nimport type { StateOf } from './types';\nimport { isInitializable } from './guards';\n\n/** Return type of `useModel`, providing state, validation, and model access. */\nexport interface ModelHandle<S extends object, M extends Model<S>> {\n state: S;\n errors: ValidationErrors<S>;\n valid: boolean;\n dirty: boolean;\n model: M;\n}\n\n/**\n * Bind to a component-scoped Model with validation and dirty state exposed.\n */\nexport function useModel<M extends Model<any>>(\n factory: () => M\n): ModelHandle<StateOf<M>, M> {\n const modelRef = useRef<M | null>(null);\n const mountedRef = useRef(false);\n\n if (!modelRef.current || modelRef.current.disposed) {\n modelRef.current = factory();\n }\n\n useSyncExternalStore(\n (onStoreChange) => modelRef.current!.subscribe(onStoreChange),\n () => modelRef.current!.state,\n () => modelRef.current!.state,\n );\n\n useEffect(() => {\n mountedRef.current = true;\n if (isInitializable(modelRef.current)) {\n modelRef.current.init();\n }\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n modelRef.current?.dispose();\n }\n }, 0);\n };\n }, []);\n\n const model = modelRef.current;\n\n return {\n state: model.state,\n errors: model.errors,\n valid: model.valid,\n dirty: model.dirty,\n model,\n };\n}\n\n/** Return type of `useField`, providing a single field's value, error, and setter. */\nexport interface FieldHandle<V> {\n value: V;\n error: string | undefined;\n set: (value: V) => void;\n}\n\n/**\n * Bind to a single Model field with surgical re-renders.\n */\nexport function useField<S extends object, K extends keyof S>(\n model: Model<S>,\n field: K\n): FieldHandle<S[K]> {\n // Track the field value and error for comparison\n const getSnapshot = useCallback(() => {\n return {\n value: model.state[field],\n error: model.errors[field],\n };\n }, [model, field]);\n\n // Use object comparison for subscription\n const cachedRef = useRef(getSnapshot());\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n return model.subscribe(() => {\n const next = getSnapshot();\n const current = cachedRef.current;\n\n // Only trigger re-render if field value or error changed\n if (next.value !== current.value || next.error !== current.error) {\n cachedRef.current = next;\n onStoreChange();\n }\n });\n },\n [model, getSnapshot]\n );\n\n const snapshot = useSyncExternalStore(\n subscribe,\n () => cachedRef.current,\n () => cachedRef.current\n );\n\n const set = useCallback(\n (value: S[K]) => {\n // Access the protected set method through type assertion\n // The Model subclass should expose a setter method\n const partial: Partial<S> = { [field]: value } as unknown as Partial<S>;\n (model as unknown as { set: (partial: Partial<S>) => void }).set(partial);\n },\n [model, field]\n );\n\n return {\n value: snapshot.value,\n error: snapshot.error,\n set,\n };\n}\n","import { useEffect, useCallback, useRef } from 'react';\nimport { EventBus } from '../EventBus';\n\n/**\n * Subscribe to a typed event, auto-unsubscribes on unmount.\n * Accepts an EventBus directly or any object with an `events` property (e.g. a ViewModel).\n */\nexport function useEvent<E extends Record<string, any>, K extends keyof E>(\n source: EventBus<E> | { events: EventBus<E> },\n event: K,\n handler: (payload: E[K]) => void\n): void {\n const bus = source instanceof EventBus ? source : source.events;\n\n // Use ref to keep handler stable across re-renders\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const unsubscribe = bus.on(event, (payload) => {\n handlerRef.current(payload);\n });\n\n return unsubscribe;\n }, [bus, event]);\n}\n\n/**\n * Get a stable emit function for an EventBus.\n */\nexport function useEmit<E extends Record<string, any>>(\n bus: EventBus<E>\n): <K extends keyof E>(event: K, payload: E[K]) => void {\n return useCallback(\n <K extends keyof E>(event: K, payload: E[K]) => {\n bus.emit(event, payload);\n },\n [bus]\n );\n}\n","import { useEffect, useRef } from 'react';\nimport type { Disposable } from '../types';\nimport { teardown } from '../singleton';\n\n/**\n * Teardown singleton class(es) on unmount.\n * Uses deferred disposal to handle StrictMode's double-mount cycle.\n */\nexport function useTeardown(\n ...Classes: Array<new (...args: unknown[]) => Disposable>\n): void {\n const mountedRef = useRef(false);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n for (const Class of Classes) {\n teardown(Class);\n }\n }\n }, 0);\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n}\n","import { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { singleton } from '../singleton';\nimport type { Disposable } from '../types';\nimport type { ProviderRegistry } from './types';\n\nconst ProviderContext = createContext<ProviderRegistry | null>(null);\n\n/** Props for the `Provider` component used to inject test/Storybook dependencies. */\nexport interface ProviderProps {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n provide: Array<[new (...args: any[]) => any, any]>;\n children: ReactNode;\n}\n\n/**\n * DI container for testing and Storybook.\n */\nexport function Provider({ provide, children }: ProviderProps): ReactNode {\n const registry = useMemo(() => {\n const map: ProviderRegistry = new Map();\n for (const [Class, instance] of provide) {\n map.set(Class, instance);\n }\n return map;\n }, [provide]);\n\n return (\n <ProviderContext.Provider value={registry}>\n {children}\n </ProviderContext.Provider>\n );\n}\n\n/**\n * Resolve from Provider context or fallback to singleton().\n */\nexport function useResolve<T, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T {\n const registry = useContext(ProviderContext);\n\n if (registry?.has(Class)) {\n return registry.get(Class) as T;\n }\n return singleton(Class as new (...args: Args) => T & Disposable, ...args);\n}\n"],"names":["hasAsyncSubscription","obj","useInstance","subscribable","state","useSyncExternalStore","onStoreChange","versionRef","useRef","isSubscribable","isInitializable","depsChanged","prev","next","i","useLocal","classOrFactory","rest","args","deps","instanceRef","mountedRef","prevDepsRef","useEffect","instance","useSingleton","Class","singleton","useModel","factory","modelRef","model","useField","field","getSnapshot","useCallback","cachedRef","subscribe","current","snapshot","set","value","partial","useEvent","source","event","handler","bus","EventBus","handlerRef","payload","useEmit","useTeardown","Classes","teardown","ProviderContext","createContext","Provider","provide","children","registry","useMemo","map","useResolve","useContext"],"mappings":"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"}
1
+ {"version":3,"file":"react.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"}
package/dist/react.js CHANGED
@@ -1,148 +1,20 @@
1
- import { useSyncExternalStore as p, useRef as i, useEffect as a, useCallback as l, useMemo as R, useContext as b, createContext as C } from "react";
2
- import { s as g, E as x, t as E } from "./singleton-CaEXSbYg.js";
3
- import { jsx as S } from "react/jsx-runtime";
4
- function A(t) {
5
- return t !== null && typeof t == "object" && typeof t.subscribeAsync == "function";
6
- }
7
- function y(t) {
8
- const e = p(
9
- (r) => t.subscribe(r),
10
- () => t.state,
11
- () => t.state
12
- // SSR snapshot
13
- ), n = i(0);
14
- return p(
15
- (r) => A(t) ? t.subscribeAsync(() => {
16
- n.current++, r();
17
- }) : () => {
18
- },
19
- () => n.current,
20
- () => 0
21
- // SSR: no async ops server-side
22
- ), e;
23
- }
24
- const h = (t) => t !== null && typeof t == "object" && "state" in t && "subscribe" in t && typeof t.subscribe == "function", d = (t) => t !== null && typeof t == "object" && "init" in t && typeof t.init == "function";
25
- function w(t, e) {
26
- if (t === void 0) return !1;
27
- if (t.length !== e.length) return !0;
28
- for (let n = 0; n < t.length; n++)
29
- if (!Object.is(t[n], e[n])) return !0;
30
- return !1;
31
- }
32
- function I(t, ...e) {
33
- let n, r;
34
- e.length > 0 && Array.isArray(e[e.length - 1]) ? (r = e[e.length - 1], n = e.slice(0, -1)) : (n = e, r = void 0);
35
- const u = i(null), s = i(!1), o = i(void 0);
36
- return r !== void 0 && w(o.current, r) && (u.current?.dispose(), u.current = null), r !== void 0 && (o.current = r), (!u.current || u.current.disposed) && (typeof t == "function" && t.prototype && t.prototype.constructor === t ? u.current = new t(...n) : u.current = t()), a(() => {
37
- const c = u.current;
38
- return s.current = !0, d(c) && c.init(), () => {
39
- s.current = !1, setTimeout(() => {
40
- s.current || c.dispose();
41
- }, 0);
42
- };
43
- }, r ?? []), h(u.current) ? [y(u.current), u.current] : u.current;
44
- }
45
- function k(t, ...e) {
46
- const n = g(t, ...e);
47
- return a(() => {
48
- d(n) && n.init();
49
- }, [n]), h(n) ? [y(n), n] : n;
50
- }
51
- function z(t) {
52
- const e = i(null), n = i(!1);
53
- (!e.current || e.current.disposed) && (e.current = t()), p(
54
- (u) => e.current.subscribe(u),
55
- () => e.current.state,
56
- () => e.current.state
57
- ), a(() => (n.current = !0, d(e.current) && e.current.init(), () => {
58
- n.current = !1, setTimeout(() => {
59
- n.current || e.current?.dispose();
60
- }, 0);
61
- }), []);
62
- const r = e.current;
63
- return {
64
- state: r.state,
65
- errors: r.errors,
66
- valid: r.valid,
67
- dirty: r.dirty,
68
- model: r
69
- };
70
- }
71
- function B(t, e) {
72
- const n = l(() => ({
73
- value: t.state[e],
74
- error: t.errors[e]
75
- }), [t, e]), r = i(n()), u = l(
76
- (c) => t.subscribe(() => {
77
- const f = n(), v = r.current;
78
- (f.value !== v.value || f.error !== v.error) && (r.current = f, c());
79
- }),
80
- [t, n]
81
- ), s = p(
82
- u,
83
- () => r.current,
84
- () => r.current
85
- ), o = l(
86
- (c) => {
87
- const f = { [e]: c };
88
- t.set(f);
89
- },
90
- [t, e]
91
- );
92
- return {
93
- value: s.value,
94
- error: s.error,
95
- set: o
96
- };
97
- }
98
- function D(t, e, n) {
99
- const r = t instanceof x ? t : t.events, u = i(n);
100
- u.current = n, a(() => r.on(e, (o) => {
101
- u.current(o);
102
- }), [r, e]);
103
- }
104
- function L(t) {
105
- return l(
106
- (e, n) => {
107
- t.emit(e, n);
108
- },
109
- [t]
110
- );
111
- }
112
- function q(...t) {
113
- const e = i(!1);
114
- a(() => (e.current = !0, () => {
115
- e.current = !1, setTimeout(() => {
116
- if (!e.current)
117
- for (const n of t)
118
- E(n);
119
- }, 0);
120
- }), []);
121
- }
122
- const m = C(null);
123
- function G({ provide: t, children: e }) {
124
- const n = R(() => {
125
- const r = /* @__PURE__ */ new Map();
126
- for (const [u, s] of t)
127
- r.set(u, s);
128
- return r;
129
- }, [t]);
130
- return /* @__PURE__ */ S(m.Provider, { value: n, children: e });
131
- }
132
- function H(t, ...e) {
133
- const n = b(m);
134
- return n?.has(t) ? n.get(t) : g(t, ...e);
135
- }
1
+ import { useInstance } from "./react/use-instance.js";
2
+ import { useLocal } from "./react/use-local.js";
3
+ import { useSingleton } from "./react/use-singleton.js";
4
+ import { useField, useModel } from "./react/use-model.js";
5
+ import { useEmit, useEvent } from "./react/use-event-bus.js";
6
+ import { useTeardown } from "./react/use-teardown.js";
7
+ import { Provider, useResolve } from "./react/provider.js";
136
8
  export {
137
- G as Provider,
138
- L as useEmit,
139
- D as useEvent,
140
- B as useField,
141
- y as useInstance,
142
- I as useLocal,
143
- z as useModel,
144
- H as useResolve,
145
- k as useSingleton,
146
- q as useTeardown
9
+ Provider,
10
+ useEmit,
11
+ useEvent,
12
+ useField,
13
+ useInstance,
14
+ useLocal,
15
+ useModel,
16
+ useResolve,
17
+ useSingleton,
18
+ useTeardown
147
19
  };
148
20
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","sources":["../src/react/use-instance.ts","../src/react/guards.ts","../src/react/use-local.ts","../src/react/use-singleton.ts","../src/react/use-model.ts","../src/react/use-event-bus.ts","../src/react/use-teardown.ts","../src/react/provider.tsx"],"sourcesContent":["import { useSyncExternalStore, useRef } from 'react';\nimport type { Subscribable } from '../types';\n\nfunction hasAsyncSubscription(obj: unknown): obj is { subscribeAsync(cb: () => void): () => void } {\n return (\n obj !== null &&\n typeof obj === 'object' &&\n typeof (obj as any).subscribeAsync === 'function'\n );\n}\n\n/**\n * Subscribe to an existing Subscribable instance.\n * No ownership - caller manages the instance lifecycle.\n *\n * If the instance has a `subscribeAsync` method (duck-typed),\n * a second subscription ensures async state changes also\n * trigger React re-renders.\n */\nexport function useInstance<S>(subscribable: Subscribable<S>): S {\n const state = useSyncExternalStore(\n (onStoreChange) => subscribable.subscribe(onStoreChange),\n () => subscribable.state,\n () => subscribable.state // SSR snapshot\n );\n\n // Async subscription — forces re-render when any async status changes.\n // Duck-typed: safe for Collection/Model (they don't have subscribeAsync).\n const versionRef = useRef(0);\n useSyncExternalStore(\n (onStoreChange) => {\n if (!hasAsyncSubscription(subscribable)) return () => {};\n return subscribable.subscribeAsync(() => {\n versionRef.current++;\n onStoreChange();\n });\n },\n () => versionRef.current,\n () => 0 // SSR: no async ops server-side\n );\n\n return state;\n}\n","import type { Subscribable } from '../types';\n\n/** @internal Type guard for Subscribable */\nexport const isSubscribable = (obj: unknown): obj is Subscribable<unknown> =>\n obj !== null &&\n typeof obj === 'object' &&\n 'state' in obj &&\n 'subscribe' in obj &&\n typeof (obj as Subscribable<unknown>).subscribe === 'function';\n\n/** @internal Type guard for Initializable */\nexport const isInitializable = (obj: unknown): obj is { init(): void | Promise<void> } =>\n obj !== null &&\n typeof obj === 'object' &&\n 'init' in obj &&\n typeof (obj as any).init === 'function';\n","import { useRef, useEffect } from 'react';\nimport type { DependencyList } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { isSubscribable, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\nimport type { StateOf } from './types';\n\nfunction depsChanged(prev: DependencyList | undefined, next: DependencyList): boolean {\n if (prev === undefined) return false;\n if (prev.length !== next.length) return true;\n for (let i = 0; i < prev.length; i++) {\n if (!Object.is(prev[i], next[i])) return true;\n }\n return false;\n}\n\n// ── With deps (class + initialState + deps) ────────────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<any> & Disposable>(\n Class: new (initialState: StateOf<T>) => T,\n initialState: StateOf<T>,\n deps: DependencyList,\n): [StateOf<T>, T];\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T,\n deps: DependencyList,\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(\n factory: () => T,\n deps: DependencyList,\n): T;\n\n// ── Without deps (existing overloads, unchanged) ───────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<\n T extends Subscribable<S> & Disposable,\n S = StateOf<T>,\n Args extends unknown[] = unknown[]\n>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T;\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(factory: () => T): T;\n\n// ── Implementation ─────────────────────────────────────────────────\n\nexport function useLocal<T extends Disposable, S = StateOf<T>>(\n classOrFactory: (new (...args: unknown[]) => T) | (() => T),\n ...rest: unknown[]\n): [S, T] | T {\n // ── Detect deps: last arg is an array → treat as deps ──\n let args: unknown[];\n let deps: DependencyList | undefined;\n\n if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {\n deps = rest[rest.length - 1] as DependencyList;\n args = rest.slice(0, -1);\n } else {\n args = rest;\n deps = undefined;\n }\n\n const instanceRef = useRef<T | null>(null);\n const mountedRef = useRef(false);\n const prevDepsRef = useRef<DependencyList | undefined>(undefined);\n\n // ── Render phase: dep-change detection ──\n if (deps !== undefined && depsChanged(prevDepsRef.current, deps)) {\n instanceRef.current?.dispose();\n instanceRef.current = null;\n }\n if (deps !== undefined) {\n prevDepsRef.current = deps;\n }\n\n // ── Create instance if needed ──\n if (!instanceRef.current || instanceRef.current.disposed) {\n const isClass =\n typeof classOrFactory === 'function' &&\n classOrFactory.prototype &&\n classOrFactory.prototype.constructor === classOrFactory;\n\n if (isClass) {\n instanceRef.current = new (classOrFactory as new (...a: unknown[]) => T)(...args);\n } else {\n instanceRef.current = (classOrFactory as () => T)();\n }\n }\n\n // ── Effect: init + deferred cleanup ──\n useEffect(() => {\n const instance = instanceRef.current!; // capture for cleanup closure\n mountedRef.current = true;\n if (isInitializable(instance)) {\n instance.init();\n }\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n instance.dispose();\n }\n }, 0);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps ?? []);\n\n // ── Subscribe to state if Subscribable ──\n if (isSubscribable(instanceRef.current)) {\n const state = useInstance(instanceRef.current as unknown as Subscribable<S>);\n return [state, instanceRef.current];\n }\n\n return instanceRef.current;\n}\n","import { useEffect } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { singleton } from '../singleton';\nimport { useInstance } from './use-instance';\nimport { isSubscribable, isInitializable } from './guards';\nimport type { StateOf } from './types';\n\n/**\n * Get singleton Subscribable instance and subscribe to its state.\n * Returns [state, instance] tuple.\n */\nexport function useSingleton<\n T extends Subscribable<S> & Disposable,\n S = StateOf<T>,\n Args extends unknown[] = unknown[]\n>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T];\n\n/**\n * Get singleton Disposable instance (non-Subscribable).\n * Returns the instance directly.\n */\nexport function useSingleton<T extends Disposable, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T;\n\n// Implementation\nexport function useSingleton<T extends Disposable, S = StateOf<T>, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T] | T {\n const instance = singleton(Class, ...args);\n\n useEffect(() => {\n if (isInitializable(instance)) {\n instance.init();\n }\n }, [instance]);\n\n if (isSubscribable(instance)) {\n const state = useInstance(instance) as S;\n return [state, instance];\n }\n\n return instance;\n}\n","import { useRef, useEffect, useSyncExternalStore, useCallback } from 'react';\nimport type { Model } from '../Model';\nimport type { ValidationErrors } from '../types';\nimport type { StateOf } from './types';\nimport { isInitializable } from './guards';\n\n/** Return type of `useModel`, providing state, validation, and model access. */\nexport interface ModelHandle<S extends object, M extends Model<S>> {\n state: S;\n errors: ValidationErrors<S>;\n valid: boolean;\n dirty: boolean;\n model: M;\n}\n\n/**\n * Bind to a component-scoped Model with validation and dirty state exposed.\n */\nexport function useModel<M extends Model<any>>(\n factory: () => M\n): ModelHandle<StateOf<M>, M> {\n const modelRef = useRef<M | null>(null);\n const mountedRef = useRef(false);\n\n if (!modelRef.current || modelRef.current.disposed) {\n modelRef.current = factory();\n }\n\n useSyncExternalStore(\n (onStoreChange) => modelRef.current!.subscribe(onStoreChange),\n () => modelRef.current!.state,\n () => modelRef.current!.state,\n );\n\n useEffect(() => {\n mountedRef.current = true;\n if (isInitializable(modelRef.current)) {\n modelRef.current.init();\n }\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n modelRef.current?.dispose();\n }\n }, 0);\n };\n }, []);\n\n const model = modelRef.current;\n\n return {\n state: model.state,\n errors: model.errors,\n valid: model.valid,\n dirty: model.dirty,\n model,\n };\n}\n\n/** Return type of `useField`, providing a single field's value, error, and setter. */\nexport interface FieldHandle<V> {\n value: V;\n error: string | undefined;\n set: (value: V) => void;\n}\n\n/**\n * Bind to a single Model field with surgical re-renders.\n */\nexport function useField<S extends object, K extends keyof S>(\n model: Model<S>,\n field: K\n): FieldHandle<S[K]> {\n // Track the field value and error for comparison\n const getSnapshot = useCallback(() => {\n return {\n value: model.state[field],\n error: model.errors[field],\n };\n }, [model, field]);\n\n // Use object comparison for subscription\n const cachedRef = useRef(getSnapshot());\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n return model.subscribe(() => {\n const next = getSnapshot();\n const current = cachedRef.current;\n\n // Only trigger re-render if field value or error changed\n if (next.value !== current.value || next.error !== current.error) {\n cachedRef.current = next;\n onStoreChange();\n }\n });\n },\n [model, getSnapshot]\n );\n\n const snapshot = useSyncExternalStore(\n subscribe,\n () => cachedRef.current,\n () => cachedRef.current\n );\n\n const set = useCallback(\n (value: S[K]) => {\n // Access the protected set method through type assertion\n // The Model subclass should expose a setter method\n const partial: Partial<S> = { [field]: value } as unknown as Partial<S>;\n (model as unknown as { set: (partial: Partial<S>) => void }).set(partial);\n },\n [model, field]\n );\n\n return {\n value: snapshot.value,\n error: snapshot.error,\n set,\n };\n}\n","import { useEffect, useCallback, useRef } from 'react';\nimport { EventBus } from '../EventBus';\n\n/**\n * Subscribe to a typed event, auto-unsubscribes on unmount.\n * Accepts an EventBus directly or any object with an `events` property (e.g. a ViewModel).\n */\nexport function useEvent<E extends Record<string, any>, K extends keyof E>(\n source: EventBus<E> | { events: EventBus<E> },\n event: K,\n handler: (payload: E[K]) => void\n): void {\n const bus = source instanceof EventBus ? source : source.events;\n\n // Use ref to keep handler stable across re-renders\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const unsubscribe = bus.on(event, (payload) => {\n handlerRef.current(payload);\n });\n\n return unsubscribe;\n }, [bus, event]);\n}\n\n/**\n * Get a stable emit function for an EventBus.\n */\nexport function useEmit<E extends Record<string, any>>(\n bus: EventBus<E>\n): <K extends keyof E>(event: K, payload: E[K]) => void {\n return useCallback(\n <K extends keyof E>(event: K, payload: E[K]) => {\n bus.emit(event, payload);\n },\n [bus]\n );\n}\n","import { useEffect, useRef } from 'react';\nimport type { Disposable } from '../types';\nimport { teardown } from '../singleton';\n\n/**\n * Teardown singleton class(es) on unmount.\n * Uses deferred disposal to handle StrictMode's double-mount cycle.\n */\nexport function useTeardown(\n ...Classes: Array<new (...args: unknown[]) => Disposable>\n): void {\n const mountedRef = useRef(false);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n for (const Class of Classes) {\n teardown(Class);\n }\n }\n }, 0);\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n}\n","import { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { singleton } from '../singleton';\nimport type { Disposable } from '../types';\nimport type { ProviderRegistry } from './types';\n\nconst ProviderContext = createContext<ProviderRegistry | null>(null);\n\n/** Props for the `Provider` component used to inject test/Storybook dependencies. */\nexport interface ProviderProps {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n provide: Array<[new (...args: any[]) => any, any]>;\n children: ReactNode;\n}\n\n/**\n * DI container for testing and Storybook.\n */\nexport function Provider({ provide, children }: ProviderProps): ReactNode {\n const registry = useMemo(() => {\n const map: ProviderRegistry = new Map();\n for (const [Class, instance] of provide) {\n map.set(Class, instance);\n }\n return map;\n }, [provide]);\n\n return (\n <ProviderContext.Provider value={registry}>\n {children}\n </ProviderContext.Provider>\n );\n}\n\n/**\n * Resolve from Provider context or fallback to singleton().\n */\nexport function useResolve<T, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T {\n const registry = useContext(ProviderContext);\n\n if (registry?.has(Class)) {\n return registry.get(Class) as T;\n }\n return singleton(Class as new (...args: Args) => T & Disposable, ...args);\n}\n"],"names":["hasAsyncSubscription","obj","useInstance","subscribable","state","useSyncExternalStore","onStoreChange","versionRef","useRef","isSubscribable","isInitializable","depsChanged","prev","next","i","useLocal","classOrFactory","rest","args","deps","instanceRef","mountedRef","prevDepsRef","useEffect","instance","useSingleton","Class","singleton","useModel","factory","modelRef","model","useField","field","getSnapshot","useCallback","cachedRef","subscribe","current","snapshot","set","value","partial","useEvent","source","event","handler","bus","EventBus","handlerRef","payload","useEmit","useTeardown","Classes","teardown","ProviderContext","createContext","Provider","provide","children","registry","useMemo","map","useResolve","useContext"],"mappings":"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"}
1
+ {"version":3,"file":"react.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const registry = /* @__PURE__ */ new Map();
4
+ function singleton(Class, ...args) {
5
+ const existing = registry.get(Class);
6
+ if (existing && !existing.disposed) {
7
+ return existing;
8
+ }
9
+ const instance = new Class(...args);
10
+ registry.set(Class, instance);
11
+ return instance;
12
+ }
13
+ function hasSingleton(Class) {
14
+ const existing = registry.get(Class);
15
+ return existing !== void 0 && !existing.disposed;
16
+ }
17
+ function teardown(Class) {
18
+ const instance = registry.get(Class);
19
+ if (instance) {
20
+ instance.dispose();
21
+ registry.delete(Class);
22
+ }
23
+ }
24
+ function teardownAll() {
25
+ for (const instance of registry.values()) {
26
+ instance.dispose();
27
+ }
28
+ registry.clear();
29
+ }
30
+ exports.hasSingleton = hasSingleton;
31
+ exports.singleton = singleton;
32
+ exports.teardown = teardown;
33
+ exports.teardownAll = teardownAll;
34
+ //# sourceMappingURL=singleton.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleton.cjs","sources":["../src/singleton.ts"],"sourcesContent":["import type { Disposable } from './types';\n\n// Using 'any' for registry types to avoid variance issues with generics\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyDisposableClass = new (...args: any[]) => Disposable;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst registry = new Map<AnyDisposableClass, Disposable>();\n\n/**\n * Get-or-create a singleton instance for the given class.\n * Returns the existing instance if one exists and is not disposed; otherwise creates a new one.\n */\nexport function singleton<T extends Disposable, Args extends unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T {\n const existing = registry.get(Class as AnyDisposableClass);\n\n if (existing && !existing.disposed) {\n return existing as T;\n }\n\n const instance = new Class(...args);\n registry.set(Class as AnyDisposableClass, instance);\n return instance;\n}\n\n/**\n * Check if a singleton instance exists for a class.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function hasSingleton(Class: new (...args: any[]) => Disposable): boolean {\n const existing = registry.get(Class);\n return existing !== undefined && !existing.disposed;\n}\n\n/**\n * Disposes and removes the singleton instance for the given class.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function teardown(Class: new (...args: any[]) => Disposable): void {\n const instance = registry.get(Class);\n if (instance) {\n instance.dispose();\n registry.delete(Class);\n }\n}\n\n/**\n * Disposes all singletons and clears the registry. Typically used in test cleanup.\n */\nexport function teardownAll(): void {\n for (const instance of registry.values()) {\n instance.dispose();\n }\n registry.clear();\n}\n"],"names":[],"mappings":";;AAMA,MAAM,+BAAe,IAAA;AAMd,SAAS,UACd,UACG,MACA;AACH,QAAM,WAAW,SAAS,IAAI,KAA2B;AAEzD,MAAI,YAAY,CAAC,SAAS,UAAU;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,IAAI,MAAM,GAAG,IAAI;AAClC,WAAS,IAAI,OAA6B,QAAQ;AAClD,SAAO;AACT;AAMO,SAAS,aAAa,OAAoD;AAC/E,QAAM,WAAW,SAAS,IAAI,KAAK;AACnC,SAAO,aAAa,UAAa,CAAC,SAAS;AAC7C;AAMO,SAAS,SAAS,OAAiD;AACxE,QAAM,WAAW,SAAS,IAAI,KAAK;AACnC,MAAI,UAAU;AACZ,aAAS,QAAA;AACT,aAAS,OAAO,KAAK;AAAA,EACvB;AACF;AAKO,SAAS,cAAoB;AAClC,aAAW,YAAY,SAAS,UAAU;AACxC,aAAS,QAAA;AAAA,EACX;AACA,WAAS,MAAA;AACX;;;;;"}
@@ -0,0 +1,34 @@
1
+ const registry = /* @__PURE__ */ new Map();
2
+ function singleton(Class, ...args) {
3
+ const existing = registry.get(Class);
4
+ if (existing && !existing.disposed) {
5
+ return existing;
6
+ }
7
+ const instance = new Class(...args);
8
+ registry.set(Class, instance);
9
+ return instance;
10
+ }
11
+ function hasSingleton(Class) {
12
+ const existing = registry.get(Class);
13
+ return existing !== void 0 && !existing.disposed;
14
+ }
15
+ function teardown(Class) {
16
+ const instance = registry.get(Class);
17
+ if (instance) {
18
+ instance.dispose();
19
+ registry.delete(Class);
20
+ }
21
+ }
22
+ function teardownAll() {
23
+ for (const instance of registry.values()) {
24
+ instance.dispose();
25
+ }
26
+ registry.clear();
27
+ }
28
+ export {
29
+ hasSingleton,
30
+ singleton,
31
+ teardown,
32
+ teardownAll
33
+ };
34
+ //# sourceMappingURL=singleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleton.js","sources":["../src/singleton.ts"],"sourcesContent":["import type { Disposable } from './types';\n\n// Using 'any' for registry types to avoid variance issues with generics\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyDisposableClass = new (...args: any[]) => Disposable;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst registry = new Map<AnyDisposableClass, Disposable>();\n\n/**\n * Get-or-create a singleton instance for the given class.\n * Returns the existing instance if one exists and is not disposed; otherwise creates a new one.\n */\nexport function singleton<T extends Disposable, Args extends unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T {\n const existing = registry.get(Class as AnyDisposableClass);\n\n if (existing && !existing.disposed) {\n return existing as T;\n }\n\n const instance = new Class(...args);\n registry.set(Class as AnyDisposableClass, instance);\n return instance;\n}\n\n/**\n * Check if a singleton instance exists for a class.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function hasSingleton(Class: new (...args: any[]) => Disposable): boolean {\n const existing = registry.get(Class);\n return existing !== undefined && !existing.disposed;\n}\n\n/**\n * Disposes and removes the singleton instance for the given class.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function teardown(Class: new (...args: any[]) => Disposable): void {\n const instance = registry.get(Class);\n if (instance) {\n instance.dispose();\n registry.delete(Class);\n }\n}\n\n/**\n * Disposes all singletons and clears the registry. Typically used in test cleanup.\n */\nexport function teardownAll(): void {\n for (const instance of registry.values()) {\n instance.dispose();\n }\n registry.clear();\n}\n"],"names":[],"mappings":"AAMA,MAAM,+BAAe,IAAA;AAMd,SAAS,UACd,UACG,MACA;AACH,QAAM,WAAW,SAAS,IAAI,KAA2B;AAEzD,MAAI,YAAY,CAAC,SAAS,UAAU;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,IAAI,MAAM,GAAG,IAAI;AAClC,WAAS,IAAI,OAA6B,QAAQ;AAClD,SAAO;AACT;AAMO,SAAS,aAAa,OAAoD;AAC/E,QAAM,WAAW,SAAS,IAAI,KAAK;AACnC,SAAO,aAAa,UAAa,CAAC,SAAS;AAC7C;AAMO,SAAS,SAAS,OAAiD;AACxE,QAAM,WAAW,SAAS,IAAI,KAAK;AACnC,MAAI,UAAU;AACZ,aAAS,QAAA;AACT,aAAS,OAAO,KAAK;AAAA,EACvB;AACF;AAKO,SAAS,cAAoB;AAClC,aAAW,YAAY,SAAS,UAAU;AACxC,aAAS,QAAA;AAAA,EACX;AACA,WAAS,MAAA;AACX;"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ function walkPrototypeChain(instance, stopAt, visitor) {
4
+ let proto = Object.getPrototypeOf(instance);
5
+ while (proto && proto !== stopAt) {
6
+ const descriptors = Object.getOwnPropertyDescriptors(proto);
7
+ for (const [key, desc] of Object.entries(descriptors)) {
8
+ if (key === "constructor") continue;
9
+ visitor(key, desc, proto);
10
+ }
11
+ proto = Object.getPrototypeOf(proto);
12
+ }
13
+ }
14
+ exports.walkPrototypeChain = walkPrototypeChain;
15
+ //# sourceMappingURL=walkPrototypeChain.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walkPrototypeChain.cjs","sources":["../src/walkPrototypeChain.ts"],"sourcesContent":["/**\n * Walk the prototype chain from `instance`'s class up to (but not including)\n * `stopAt`. Calls `visitor` for each own property descriptor found.\n *\n * Shared utility — used by ViewModel's _memoizeGetters() and _wrapMethods(),\n * and by Resource's _wrapMethods().\n */\nexport function walkPrototypeChain(\n instance: object,\n stopAt: object,\n visitor: (key: string, desc: PropertyDescriptor, proto: object) => void,\n): void {\n let proto = Object.getPrototypeOf(instance);\n while (proto && proto !== stopAt) {\n const descriptors = Object.getOwnPropertyDescriptors(proto);\n for (const [key, desc] of Object.entries(descriptors)) {\n if (key === 'constructor') continue;\n visitor(key, desc, proto);\n }\n proto = Object.getPrototypeOf(proto);\n }\n}\n"],"names":[],"mappings":";;AAOO,SAAS,mBACd,UACA,QACA,SACM;AACN,MAAI,QAAQ,OAAO,eAAe,QAAQ;AAC1C,SAAO,SAAS,UAAU,QAAQ;AAChC,UAAM,cAAc,OAAO,0BAA0B,KAAK;AAC1D,eAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,WAAW,GAAG;AACrD,UAAI,QAAQ,cAAe;AAC3B,cAAQ,KAAK,MAAM,KAAK;AAAA,IAC1B;AACA,YAAQ,OAAO,eAAe,KAAK;AAAA,EACrC;AACF;;"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Walk the prototype chain from `instance`'s class up to (but not including)
3
+ * `stopAt`. Calls `visitor` for each own property descriptor found.
4
+ *
5
+ * Shared utility — used by ViewModel's _memoizeGetters() and _wrapMethods(),
6
+ * and by Resource's _wrapMethods().
7
+ */
8
+ export declare function walkPrototypeChain(instance: object, stopAt: object, visitor: (key: string, desc: PropertyDescriptor, proto: object) => void): void;
9
+ //# sourceMappingURL=walkPrototypeChain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walkPrototypeChain.d.ts","sourceRoot":"","sources":["../src/walkPrototypeChain.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GACtE,IAAI,CAUN"}
@@ -0,0 +1,15 @@
1
+ function walkPrototypeChain(instance, stopAt, visitor) {
2
+ let proto = Object.getPrototypeOf(instance);
3
+ while (proto && proto !== stopAt) {
4
+ const descriptors = Object.getOwnPropertyDescriptors(proto);
5
+ for (const [key, desc] of Object.entries(descriptors)) {
6
+ if (key === "constructor") continue;
7
+ visitor(key, desc, proto);
8
+ }
9
+ proto = Object.getPrototypeOf(proto);
10
+ }
11
+ }
12
+ export {
13
+ walkPrototypeChain
14
+ };
15
+ //# sourceMappingURL=walkPrototypeChain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walkPrototypeChain.js","sources":["../src/walkPrototypeChain.ts"],"sourcesContent":["/**\n * Walk the prototype chain from `instance`'s class up to (but not including)\n * `stopAt`. Calls `visitor` for each own property descriptor found.\n *\n * Shared utility — used by ViewModel's _memoizeGetters() and _wrapMethods(),\n * and by Resource's _wrapMethods().\n */\nexport function walkPrototypeChain(\n instance: object,\n stopAt: object,\n visitor: (key: string, desc: PropertyDescriptor, proto: object) => void,\n): void {\n let proto = Object.getPrototypeOf(instance);\n while (proto && proto !== stopAt) {\n const descriptors = Object.getOwnPropertyDescriptors(proto);\n for (const [key, desc] of Object.entries(descriptors)) {\n if (key === 'constructor') continue;\n visitor(key, desc, proto);\n }\n proto = Object.getPrototypeOf(proto);\n }\n}\n"],"names":[],"mappings":"AAOO,SAAS,mBACd,UACA,QACA,SACM;AACN,MAAI,QAAQ,OAAO,eAAe,QAAQ;AAC1C,SAAO,SAAS,UAAU,QAAQ;AAChC,UAAM,cAAc,OAAO,0BAA0B,KAAK;AAC1D,eAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,WAAW,GAAG;AACrD,UAAI,QAAQ,cAAe;AAC3B,cAAQ,KAAK,MAAM,KAAK;AAAA,IAC1B;AACA,YAAQ,OAAO,eAAe,KAAK;AAAA,EACrC;AACF;"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const PersistentCollection = require("../PersistentCollection.cjs");
4
+ const idb = require("./idb.cjs");
5
+ class IndexedDBCollection extends PersistentCollection.PersistentCollection {
6
+ /** IndexedDB database name. Override to use a separate database. */
7
+ static DB_NAME = "mvc-kit";
8
+ get _dbName() {
9
+ return this.constructor.DB_NAME;
10
+ }
11
+ _getStore(mode) {
12
+ return idb.getStore(this._dbName, this.storageKey, mode);
13
+ }
14
+ // ── Persist interface (per-item strategy) ──
15
+ async persistGetAll() {
16
+ const store = await this._getStore("readonly");
17
+ return idb.idbGetAll(store);
18
+ }
19
+ async persistGet(id) {
20
+ const store = await this._getStore("readonly");
21
+ return idb.idbGet(store, id);
22
+ }
23
+ async persistSet(items) {
24
+ const store = await this._getStore("readwrite");
25
+ return idb.idbPut(store, items);
26
+ }
27
+ async persistRemove(ids) {
28
+ const store = await this._getStore("readwrite");
29
+ return idb.idbDelete(store, ids);
30
+ }
31
+ async persistClear() {
32
+ const store = await this._getStore("readwrite");
33
+ return idb.idbClear(store);
34
+ }
35
+ }
36
+ exports.IndexedDBCollection = IndexedDBCollection;
37
+ //# sourceMappingURL=IndexedDBCollection.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IndexedDBCollection.cjs","sources":["../../src/web/IndexedDBCollection.ts"],"sourcesContent":["import { PersistentCollection } from '../PersistentCollection';\nimport { getStore, idbGetAll, idbGet, idbPut, idbDelete, idbClear } from './idb';\n\n/**\n * PersistentCollection backed by IndexedDB. Stores items individually by `id`\n * in a dedicated object store (named by `storageKey`).\n *\n * **Requires manual `hydrate()` call** (async storage).\n * Typically called in ViewModel's `onInit()`.\n *\n * Uses per-item strategy: each item is stored as a separate entry in the object store.\n * No JSON serialization needed — IndexedDB uses structured cloning.\n *\n * ```ts\n * class MessagesCollection extends IndexedDBCollection<Message> {\n * protected readonly storageKey = 'messages';\n * }\n *\n * // In ViewModel:\n * async onInit() {\n * await this.collection.hydrate();\n * if (this.collection.length === 0) this.load();\n * }\n * ```\n */\nexport abstract class IndexedDBCollection<\n T extends { id: string | number },\n> extends PersistentCollection<T> {\n /** IndexedDB database name. Override to use a separate database. */\n static DB_NAME = 'mvc-kit';\n\n private get _dbName(): string {\n return (this.constructor as typeof IndexedDBCollection).DB_NAME;\n }\n\n private _getStore(mode: IDBTransactionMode) {\n return getStore(this._dbName, this.storageKey, mode);\n }\n\n // ── Persist interface (per-item strategy) ──\n\n protected async persistGetAll(): Promise<T[]> {\n const store = await this._getStore('readonly');\n return idbGetAll<T>(store);\n }\n\n protected async persistGet(id: T['id']): Promise<T | null> {\n const store = await this._getStore('readonly');\n return idbGet<T>(store, id as IDBValidKey);\n }\n\n protected async persistSet(items: T[]): Promise<void> {\n const store = await this._getStore('readwrite');\n return idbPut(store, items);\n }\n\n protected async persistRemove(ids: T['id'][]): Promise<void> {\n const store = await this._getStore('readwrite');\n return idbDelete(store, ids as IDBValidKey[]);\n }\n\n protected async persistClear(): Promise<void> {\n const store = await this._getStore('readwrite');\n return idbClear(store);\n }\n}\n"],"names":["PersistentCollection","getStore","idbGetAll","idbGet","idbPut","idbDelete","idbClear"],"mappings":";;;;AAyBO,MAAe,4BAEZA,qBAAAA,qBAAwB;AAAA;AAAA,EAEhC,OAAO,UAAU;AAAA,EAEjB,IAAY,UAAkB;AAC5B,WAAQ,KAAK,YAA2C;AAAA,EAC1D;AAAA,EAEQ,UAAU,MAA0B;AAC1C,WAAOC,IAAAA,SAAS,KAAK,SAAS,KAAK,YAAY,IAAI;AAAA,EACrD;AAAA;AAAA,EAIA,MAAgB,gBAA8B;AAC5C,UAAM,QAAQ,MAAM,KAAK,UAAU,UAAU;AAC7C,WAAOC,IAAAA,UAAa,KAAK;AAAA,EAC3B;AAAA,EAEA,MAAgB,WAAW,IAAgC;AACzD,UAAM,QAAQ,MAAM,KAAK,UAAU,UAAU;AAC7C,WAAOC,IAAAA,OAAU,OAAO,EAAiB;AAAA,EAC3C;AAAA,EAEA,MAAgB,WAAW,OAA2B;AACpD,UAAM,QAAQ,MAAM,KAAK,UAAU,WAAW;AAC9C,WAAOC,IAAAA,OAAO,OAAO,KAAK;AAAA,EAC5B;AAAA,EAEA,MAAgB,cAAc,KAA+B;AAC3D,UAAM,QAAQ,MAAM,KAAK,UAAU,WAAW;AAC9C,WAAOC,IAAAA,UAAU,OAAO,GAAoB;AAAA,EAC9C;AAAA,EAEA,MAAgB,eAA8B;AAC5C,UAAM,QAAQ,MAAM,KAAK,UAAU,WAAW;AAC9C,WAAOC,IAAAA,SAAS,KAAK;AAAA,EACvB;AACF;;"}
@@ -0,0 +1,37 @@
1
+ import { PersistentCollection } from "../PersistentCollection.js";
2
+ import { getStore, idbGetAll, idbGet, idbPut, idbDelete, idbClear } from "./idb.js";
3
+ class IndexedDBCollection extends PersistentCollection {
4
+ /** IndexedDB database name. Override to use a separate database. */
5
+ static DB_NAME = "mvc-kit";
6
+ get _dbName() {
7
+ return this.constructor.DB_NAME;
8
+ }
9
+ _getStore(mode) {
10
+ return getStore(this._dbName, this.storageKey, mode);
11
+ }
12
+ // ── Persist interface (per-item strategy) ──
13
+ async persistGetAll() {
14
+ const store = await this._getStore("readonly");
15
+ return idbGetAll(store);
16
+ }
17
+ async persistGet(id) {
18
+ const store = await this._getStore("readonly");
19
+ return idbGet(store, id);
20
+ }
21
+ async persistSet(items) {
22
+ const store = await this._getStore("readwrite");
23
+ return idbPut(store, items);
24
+ }
25
+ async persistRemove(ids) {
26
+ const store = await this._getStore("readwrite");
27
+ return idbDelete(store, ids);
28
+ }
29
+ async persistClear() {
30
+ const store = await this._getStore("readwrite");
31
+ return idbClear(store);
32
+ }
33
+ }
34
+ export {
35
+ IndexedDBCollection
36
+ };
37
+ //# sourceMappingURL=IndexedDBCollection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IndexedDBCollection.js","sources":["../../src/web/IndexedDBCollection.ts"],"sourcesContent":["import { PersistentCollection } from '../PersistentCollection';\nimport { getStore, idbGetAll, idbGet, idbPut, idbDelete, idbClear } from './idb';\n\n/**\n * PersistentCollection backed by IndexedDB. Stores items individually by `id`\n * in a dedicated object store (named by `storageKey`).\n *\n * **Requires manual `hydrate()` call** (async storage).\n * Typically called in ViewModel's `onInit()`.\n *\n * Uses per-item strategy: each item is stored as a separate entry in the object store.\n * No JSON serialization needed — IndexedDB uses structured cloning.\n *\n * ```ts\n * class MessagesCollection extends IndexedDBCollection<Message> {\n * protected readonly storageKey = 'messages';\n * }\n *\n * // In ViewModel:\n * async onInit() {\n * await this.collection.hydrate();\n * if (this.collection.length === 0) this.load();\n * }\n * ```\n */\nexport abstract class IndexedDBCollection<\n T extends { id: string | number },\n> extends PersistentCollection<T> {\n /** IndexedDB database name. Override to use a separate database. */\n static DB_NAME = 'mvc-kit';\n\n private get _dbName(): string {\n return (this.constructor as typeof IndexedDBCollection).DB_NAME;\n }\n\n private _getStore(mode: IDBTransactionMode) {\n return getStore(this._dbName, this.storageKey, mode);\n }\n\n // ── Persist interface (per-item strategy) ──\n\n protected async persistGetAll(): Promise<T[]> {\n const store = await this._getStore('readonly');\n return idbGetAll<T>(store);\n }\n\n protected async persistGet(id: T['id']): Promise<T | null> {\n const store = await this._getStore('readonly');\n return idbGet<T>(store, id as IDBValidKey);\n }\n\n protected async persistSet(items: T[]): Promise<void> {\n const store = await this._getStore('readwrite');\n return idbPut(store, items);\n }\n\n protected async persistRemove(ids: T['id'][]): Promise<void> {\n const store = await this._getStore('readwrite');\n return idbDelete(store, ids as IDBValidKey[]);\n }\n\n protected async persistClear(): Promise<void> {\n const store = await this._getStore('readwrite');\n return idbClear(store);\n }\n}\n"],"names":[],"mappings":";;AAyBO,MAAe,4BAEZ,qBAAwB;AAAA;AAAA,EAEhC,OAAO,UAAU;AAAA,EAEjB,IAAY,UAAkB;AAC5B,WAAQ,KAAK,YAA2C;AAAA,EAC1D;AAAA,EAEQ,UAAU,MAA0B;AAC1C,WAAO,SAAS,KAAK,SAAS,KAAK,YAAY,IAAI;AAAA,EACrD;AAAA;AAAA,EAIA,MAAgB,gBAA8B;AAC5C,UAAM,QAAQ,MAAM,KAAK,UAAU,UAAU;AAC7C,WAAO,UAAa,KAAK;AAAA,EAC3B;AAAA,EAEA,MAAgB,WAAW,IAAgC;AACzD,UAAM,QAAQ,MAAM,KAAK,UAAU,UAAU;AAC7C,WAAO,OAAU,OAAO,EAAiB;AAAA,EAC3C;AAAA,EAEA,MAAgB,WAAW,OAA2B;AACpD,UAAM,QAAQ,MAAM,KAAK,UAAU,WAAW;AAC9C,WAAO,OAAO,OAAO,KAAK;AAAA,EAC5B;AAAA,EAEA,MAAgB,cAAc,KAA+B;AAC3D,UAAM,QAAQ,MAAM,KAAK,UAAU,WAAW;AAC9C,WAAO,UAAU,OAAO,GAAoB;AAAA,EAC9C;AAAA,EAEA,MAAgB,eAA8B;AAC5C,UAAM,QAAQ,MAAM,KAAK,UAAU,WAAW;AAC9C,WAAO,SAAS,KAAK;AAAA,EACvB;AACF;"}