mvc-kit 2.12.5 → 2.13.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.
Files changed (186) hide show
  1. package/agent-config/bin/postinstall.mjs +4 -3
  2. package/agent-config/bin/setup.mjs +5 -1
  3. package/agent-config/claude-code/agents/mvc-kit-architect.md +11 -8
  4. package/agent-config/claude-code/skills/guide/SKILL.md +20 -7
  5. package/agent-config/claude-code/skills/guide/patterns.md +12 -0
  6. package/agent-config/claude-code/skills/guide/recipes.md +510 -0
  7. package/agent-config/claude-code/skills/guide/testing.md +297 -0
  8. package/agent-config/claude-code/skills/review/SKILL.md +3 -13
  9. package/agent-config/claude-code/skills/review/checklist.md +30 -5
  10. package/agent-config/claude-code/skills/scaffold/SKILL.md +4 -13
  11. package/agent-config/lib/install-claude.mjs +84 -25
  12. package/dist/Channel.cjs +276 -300
  13. package/dist/Channel.cjs.map +1 -1
  14. package/dist/Channel.js +275 -299
  15. package/dist/Channel.js.map +1 -1
  16. package/dist/Collection.cjs +424 -504
  17. package/dist/Collection.cjs.map +1 -1
  18. package/dist/Collection.js +423 -503
  19. package/dist/Collection.js.map +1 -1
  20. package/dist/Controller.cjs +70 -67
  21. package/dist/Controller.cjs.map +1 -1
  22. package/dist/Controller.js +69 -66
  23. package/dist/Controller.js.map +1 -1
  24. package/dist/EventBus.cjs +77 -88
  25. package/dist/EventBus.cjs.map +1 -1
  26. package/dist/EventBus.js +76 -87
  27. package/dist/EventBus.js.map +1 -1
  28. package/dist/Feed.cjs +81 -77
  29. package/dist/Feed.cjs.map +1 -1
  30. package/dist/Feed.js +80 -76
  31. package/dist/Feed.js.map +1 -1
  32. package/dist/Model.cjs +181 -207
  33. package/dist/Model.cjs.map +1 -1
  34. package/dist/Model.js +179 -205
  35. package/dist/Model.js.map +1 -1
  36. package/dist/Pagination.cjs +75 -73
  37. package/dist/Pagination.cjs.map +1 -1
  38. package/dist/Pagination.js +74 -72
  39. package/dist/Pagination.js.map +1 -1
  40. package/dist/Pending.cjs +255 -287
  41. package/dist/Pending.cjs.map +1 -1
  42. package/dist/Pending.js +253 -285
  43. package/dist/Pending.js.map +1 -1
  44. package/dist/PersistentCollection.cjs +242 -285
  45. package/dist/PersistentCollection.cjs.map +1 -1
  46. package/dist/PersistentCollection.js +241 -284
  47. package/dist/PersistentCollection.js.map +1 -1
  48. package/dist/Resource.cjs +166 -174
  49. package/dist/Resource.cjs.map +1 -1
  50. package/dist/Resource.js +164 -172
  51. package/dist/Resource.js.map +1 -1
  52. package/dist/Selection.cjs +84 -94
  53. package/dist/Selection.cjs.map +1 -1
  54. package/dist/Selection.js +83 -93
  55. package/dist/Selection.js.map +1 -1
  56. package/dist/Service.cjs +54 -55
  57. package/dist/Service.cjs.map +1 -1
  58. package/dist/Service.js +53 -54
  59. package/dist/Service.js.map +1 -1
  60. package/dist/Sorting.cjs +102 -101
  61. package/dist/Sorting.cjs.map +1 -1
  62. package/dist/Sorting.js +102 -101
  63. package/dist/Sorting.js.map +1 -1
  64. package/dist/Trackable.cjs +112 -80
  65. package/dist/Trackable.cjs.map +1 -1
  66. package/dist/Trackable.js +111 -79
  67. package/dist/Trackable.js.map +1 -1
  68. package/dist/ViewModel.cjs +528 -576
  69. package/dist/ViewModel.cjs.map +1 -1
  70. package/dist/ViewModel.js +525 -573
  71. package/dist/ViewModel.js.map +1 -1
  72. package/dist/bindPublicMethods.cjs +43 -24
  73. package/dist/bindPublicMethods.cjs.map +1 -1
  74. package/dist/bindPublicMethods.js +43 -24
  75. package/dist/bindPublicMethods.js.map +1 -1
  76. package/dist/errors.cjs +67 -68
  77. package/dist/errors.cjs.map +1 -1
  78. package/dist/errors.js +68 -71
  79. package/dist/errors.js.map +1 -1
  80. package/dist/mvc-kit.cjs +44 -46
  81. package/dist/mvc-kit.js +5 -32
  82. package/dist/produceDraft.cjs +105 -95
  83. package/dist/produceDraft.cjs.map +1 -1
  84. package/dist/produceDraft.js +106 -97
  85. package/dist/produceDraft.js.map +1 -1
  86. package/dist/react/components/CardList.cjs +30 -40
  87. package/dist/react/components/CardList.cjs.map +1 -1
  88. package/dist/react/components/CardList.js +31 -41
  89. package/dist/react/components/CardList.js.map +1 -1
  90. package/dist/react/components/DataTable.cjs +146 -169
  91. package/dist/react/components/DataTable.cjs.map +1 -1
  92. package/dist/react/components/DataTable.js +147 -170
  93. package/dist/react/components/DataTable.js.map +1 -1
  94. package/dist/react/components/InfiniteScroll.cjs +51 -42
  95. package/dist/react/components/InfiniteScroll.cjs.map +1 -1
  96. package/dist/react/components/InfiniteScroll.js +52 -43
  97. package/dist/react/components/InfiniteScroll.js.map +1 -1
  98. package/dist/react/components/types.cjs +10 -6
  99. package/dist/react/components/types.cjs.map +1 -1
  100. package/dist/react/components/types.js +11 -9
  101. package/dist/react/components/types.js.map +1 -1
  102. package/dist/react/guards.cjs +10 -6
  103. package/dist/react/guards.cjs.map +1 -1
  104. package/dist/react/guards.js +11 -9
  105. package/dist/react/guards.js.map +1 -1
  106. package/dist/react/provider.cjs +23 -20
  107. package/dist/react/provider.cjs.map +1 -1
  108. package/dist/react/provider.js +23 -21
  109. package/dist/react/provider.js.map +1 -1
  110. package/dist/react/use-event-bus.cjs +24 -20
  111. package/dist/react/use-event-bus.cjs.map +1 -1
  112. package/dist/react/use-event-bus.js +24 -21
  113. package/dist/react/use-event-bus.js.map +1 -1
  114. package/dist/react/use-instance.cjs +43 -36
  115. package/dist/react/use-instance.cjs.map +1 -1
  116. package/dist/react/use-instance.js +43 -36
  117. package/dist/react/use-instance.js.map +1 -1
  118. package/dist/react/use-local.cjs +48 -64
  119. package/dist/react/use-local.cjs.map +1 -1
  120. package/dist/react/use-local.js +47 -63
  121. package/dist/react/use-local.js.map +1 -1
  122. package/dist/react/use-model.cjs +84 -98
  123. package/dist/react/use-model.cjs.map +1 -1
  124. package/dist/react/use-model.js +84 -100
  125. package/dist/react/use-model.js.map +1 -1
  126. package/dist/react/use-singleton.cjs +19 -23
  127. package/dist/react/use-singleton.cjs.map +1 -1
  128. package/dist/react/use-singleton.js +16 -20
  129. package/dist/react/use-singleton.js.map +1 -1
  130. package/dist/react/use-subscribe-only.cjs +28 -22
  131. package/dist/react/use-subscribe-only.cjs.map +1 -1
  132. package/dist/react/use-subscribe-only.js +28 -22
  133. package/dist/react/use-subscribe-only.js.map +1 -1
  134. package/dist/react/use-teardown.cjs +20 -19
  135. package/dist/react/use-teardown.cjs.map +1 -1
  136. package/dist/react/use-teardown.js +20 -19
  137. package/dist/react/use-teardown.js.map +1 -1
  138. package/dist/react-native/NativeCollection.cjs +98 -78
  139. package/dist/react-native/NativeCollection.cjs.map +1 -1
  140. package/dist/react-native/NativeCollection.js +97 -77
  141. package/dist/react-native/NativeCollection.js.map +1 -1
  142. package/dist/react-native.cjs +2 -4
  143. package/dist/react-native.js +1 -4
  144. package/dist/react.cjs +24 -26
  145. package/dist/react.js +1 -17
  146. package/dist/singleton.cjs +28 -22
  147. package/dist/singleton.cjs.map +1 -1
  148. package/dist/singleton.js +29 -26
  149. package/dist/singleton.js.map +1 -1
  150. package/dist/walkPrototypeChain.cjs +20 -12
  151. package/dist/walkPrototypeChain.cjs.map +1 -1
  152. package/dist/walkPrototypeChain.js +21 -13
  153. package/dist/walkPrototypeChain.js.map +1 -1
  154. package/dist/web/IndexedDBCollection.cjs +53 -36
  155. package/dist/web/IndexedDBCollection.cjs.map +1 -1
  156. package/dist/web/IndexedDBCollection.js +52 -35
  157. package/dist/web/IndexedDBCollection.js.map +1 -1
  158. package/dist/web/WebStorageCollection.cjs +82 -84
  159. package/dist/web/WebStorageCollection.cjs.map +1 -1
  160. package/dist/web/WebStorageCollection.js +81 -83
  161. package/dist/web/WebStorageCollection.js.map +1 -1
  162. package/dist/web/idb.cjs +107 -99
  163. package/dist/web/idb.cjs.map +1 -1
  164. package/dist/web/idb.js +108 -105
  165. package/dist/web/idb.js.map +1 -1
  166. package/dist/web.cjs +4 -6
  167. package/dist/web.js +1 -5
  168. package/dist/wrapAsyncMethods.cjs +141 -168
  169. package/dist/wrapAsyncMethods.cjs.map +1 -1
  170. package/dist/wrapAsyncMethods.js +141 -168
  171. package/dist/wrapAsyncMethods.js.map +1 -1
  172. package/package.json +8 -8
  173. package/src/Pending.test.ts +1 -2
  174. package/src/Sorting.test.ts +1 -1
  175. package/src/produceDraft.test.ts +3 -3
  176. package/src/react/components/CardList.test.tsx +1 -1
  177. package/src/react/components/DataTable.test.tsx +1 -1
  178. package/src/react/components/InfiniteScroll.test.tsx +5 -5
  179. package/dist/mvc-kit.cjs.map +0 -1
  180. package/dist/mvc-kit.js.map +0 -1
  181. package/dist/react-native.cjs.map +0 -1
  182. package/dist/react-native.js.map +0 -1
  183. package/dist/react.cjs.map +0 -1
  184. package/dist/react.js.map +0 -1
  185. package/dist/web.cjs.map +0 -1
  186. package/dist/web.js.map +0 -1
@@ -1,41 +1,48 @@
1
1
  import { useRef, useSyncExternalStore } from "react";
2
+ //#region src/react/use-instance.ts
2
3
  function hasAsyncSubscription(obj) {
3
- return obj !== null && typeof obj === "object" && typeof obj.subscribeAsync === "function";
4
+ return obj !== null && typeof obj === "object" && typeof obj.subscribeAsync === "function";
4
5
  }
5
- const SERVER_SNAPSHOT = () => 0;
6
+ var SERVER_SNAPSHOT = () => 0;
7
+ /**
8
+ * Subscribe to an existing Subscribable instance.
9
+ * No ownership - caller manages the instance lifecycle.
10
+ *
11
+ * If the instance has a `subscribeAsync` method (duck-typed),
12
+ * a combined subscription ensures async state changes also
13
+ * trigger React re-renders.
14
+ */
6
15
  function useInstance(subscribable) {
7
- const ref = useRef(null);
8
- if (!ref.current || ref.current.subscribable !== subscribable) {
9
- const version = { current: ref.current?.version ?? 0 };
10
- ref.current = {
11
- version: version.current,
12
- subscribable,
13
- subscribe: (onStoreChange) => {
14
- const unsub1 = subscribable.subscribe(() => {
15
- version.current++;
16
- ref.current.version = version.current;
17
- onStoreChange();
18
- });
19
- let unsub2;
20
- if (hasAsyncSubscription(subscribable)) {
21
- unsub2 = subscribable.subscribeAsync(() => {
22
- version.current++;
23
- ref.current.version = version.current;
24
- onStoreChange();
25
- });
26
- }
27
- return () => {
28
- unsub1();
29
- unsub2?.();
30
- };
31
- },
32
- getSnapshot: () => version.current
33
- };
34
- }
35
- useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);
36
- return subscribable.state;
16
+ const ref = useRef(null);
17
+ if (!ref.current || ref.current.subscribable !== subscribable) {
18
+ const version = { current: ref.current?.version ?? 0 };
19
+ ref.current = {
20
+ version: version.current,
21
+ subscribable,
22
+ subscribe: (onStoreChange) => {
23
+ const unsub1 = subscribable.subscribe(() => {
24
+ version.current++;
25
+ ref.current.version = version.current;
26
+ onStoreChange();
27
+ });
28
+ let unsub2;
29
+ if (hasAsyncSubscription(subscribable)) unsub2 = subscribable.subscribeAsync(() => {
30
+ version.current++;
31
+ ref.current.version = version.current;
32
+ onStoreChange();
33
+ });
34
+ return () => {
35
+ unsub1();
36
+ unsub2?.();
37
+ };
38
+ },
39
+ getSnapshot: () => version.current
40
+ };
41
+ }
42
+ useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);
43
+ return subscribable.state;
37
44
  }
38
- export {
39
- useInstance
40
- };
41
- //# sourceMappingURL=use-instance.js.map
45
+ //#endregion
46
+ export { useInstance };
47
+
48
+ //# sourceMappingURL=use-instance.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-instance.js","sources":["../../src/react/use-instance.ts"],"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\nconst SERVER_SNAPSHOT = () => 0;\n\ninterface InstanceRef<S> {\n version: number;\n subscribable: Subscribable<S>;\n subscribe: (onStoreChange: () => void) => () => void;\n getSnapshot: () => number;\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 combined subscription ensures async state changes also\n * trigger React re-renders.\n */\nexport function useInstance<S>(subscribable: Subscribable<S>): S {\n const ref = useRef<InstanceRef<S> | null>(null);\n\n if (!ref.current || ref.current.subscribable !== subscribable) {\n const version = { current: ref.current?.version ?? 0 };\n ref.current = {\n version: version.current,\n subscribable,\n subscribe: (onStoreChange: () => void) => {\n const unsub1 = subscribable.subscribe(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n let unsub2: (() => void) | undefined;\n if (hasAsyncSubscription(subscribable)) {\n unsub2 = subscribable.subscribeAsync(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n }\n return () => { unsub1(); unsub2?.(); };\n },\n getSnapshot: () => version.current,\n };\n }\n\n useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);\n\n return subscribable.state;\n}\n"],"names":[],"mappings":";AAGA,SAAS,qBAAqB,KAAqE;AACjG,SACE,QAAQ,QACR,OAAO,QAAQ,YACf,OAAQ,IAAY,mBAAmB;AAE3C;AAEA,MAAM,kBAAkB,MAAM;AAiBvB,SAAS,YAAe,cAAkC;AAC/D,QAAM,MAAM,OAA8B,IAAI;AAE9C,MAAI,CAAC,IAAI,WAAW,IAAI,QAAQ,iBAAiB,cAAc;AAC7D,UAAM,UAAU,EAAE,SAAS,IAAI,SAAS,WAAW,EAAA;AACnD,QAAI,UAAU;AAAA,MACZ,SAAS,QAAQ;AAAA,MACjB;AAAA,MACA,WAAW,CAAC,kBAA8B;AACxC,cAAM,SAAS,aAAa,UAAU,MAAM;AAC1C,kBAAQ;AACR,cAAI,QAAS,UAAU,QAAQ;AAC/B,wBAAA;AAAA,QACF,CAAC;AACD,YAAI;AACJ,YAAI,qBAAqB,YAAY,GAAG;AACtC,mBAAS,aAAa,eAAe,MAAM;AACzC,oBAAQ;AACR,gBAAI,QAAS,UAAU,QAAQ;AAC/B,0BAAA;AAAA,UACF,CAAC;AAAA,QACH;AACA,eAAO,MAAM;AAAE,iBAAA;AAAU,mBAAA;AAAA,QAAY;AAAA,MACvC;AAAA,MACA,aAAa,MAAM,QAAQ;AAAA,IAAA;AAAA,EAE/B;AAEA,uBAAqB,IAAI,QAAQ,WAAW,IAAI,QAAQ,aAAa,eAAe;AAEpF,SAAO,aAAa;AACtB;"}
1
+ {"version":3,"file":"use-instance.js","names":[],"sources":["../../src/react/use-instance.ts"],"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\nconst SERVER_SNAPSHOT = () => 0;\n\ninterface InstanceRef<S> {\n version: number;\n subscribable: Subscribable<S>;\n subscribe: (onStoreChange: () => void) => () => void;\n getSnapshot: () => number;\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 combined subscription ensures async state changes also\n * trigger React re-renders.\n */\nexport function useInstance<S>(subscribable: Subscribable<S>): S {\n const ref = useRef<InstanceRef<S> | null>(null);\n\n if (!ref.current || ref.current.subscribable !== subscribable) {\n const version = { current: ref.current?.version ?? 0 };\n ref.current = {\n version: version.current,\n subscribable,\n subscribe: (onStoreChange: () => void) => {\n const unsub1 = subscribable.subscribe(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n let unsub2: (() => void) | undefined;\n if (hasAsyncSubscription(subscribable)) {\n unsub2 = subscribable.subscribeAsync(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n }\n return () => { unsub1(); unsub2?.(); };\n },\n getSnapshot: () => version.current,\n };\n }\n\n useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);\n\n return subscribable.state;\n}\n"],"mappings":";;AAGA,SAAS,qBAAqB,KAAqE;AACjG,QACE,QAAQ,QACR,OAAO,QAAQ,YACf,OAAQ,IAAY,mBAAmB;;AAI3C,IAAM,wBAAwB;;;;;;;;;AAiB9B,SAAgB,YAAe,cAAkC;CAC/D,MAAM,MAAM,OAA8B,KAAK;AAE/C,KAAI,CAAC,IAAI,WAAW,IAAI,QAAQ,iBAAiB,cAAc;EAC7D,MAAM,UAAU,EAAE,SAAS,IAAI,SAAS,WAAW,GAAG;AACtD,MAAI,UAAU;GACZ,SAAS,QAAQ;GACjB;GACA,YAAY,kBAA8B;IACxC,MAAM,SAAS,aAAa,gBAAgB;AAC1C,aAAQ;AACR,SAAI,QAAS,UAAU,QAAQ;AAC/B,oBAAe;MACf;IACF,IAAI;AACJ,QAAI,qBAAqB,aAAa,CACpC,UAAS,aAAa,qBAAqB;AACzC,aAAQ;AACR,SAAI,QAAS,UAAU,QAAQ;AAC/B,oBAAe;MACf;AAEJ,iBAAa;AAAE,aAAQ;AAAE,eAAU;;;GAErC,mBAAmB,QAAQ;GAC5B;;AAGH,sBAAqB,IAAI,QAAQ,WAAW,IAAI,QAAQ,aAAa,gBAAgB;AAErF,QAAO,aAAa"}
@@ -1,69 +1,53 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const react = require("react");
4
- const guards = require("./guards.cjs");
5
- const useInstance = require("./use-instance.cjs");
6
- const useSubscribeOnly = require("./use-subscribe-only.cjs");
1
+ const require_use_instance = require("./use-instance.cjs");
2
+ const require_guards = require("./guards.cjs");
3
+ const require_use_subscribe_only = require("./use-subscribe-only.cjs");
4
+ let react = require("react");
5
+ //#region src/react/use-local.ts
7
6
  function depsChanged(prev, next) {
8
- if (prev === void 0) return false;
9
- if (prev.length !== next.length) return true;
10
- for (let i = 0; i < prev.length; i++) {
11
- if (!Object.is(prev[i], next[i])) return true;
12
- }
13
- return false;
7
+ if (prev === void 0) return false;
8
+ if (prev.length !== next.length) return true;
9
+ for (let i = 0; i < prev.length; i++) if (!Object.is(prev[i], next[i])) return true;
10
+ return false;
14
11
  }
15
12
  function useLocal(classOrFactory, ...rest) {
16
- let args;
17
- let deps;
18
- if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {
19
- deps = rest[rest.length - 1];
20
- args = rest.slice(0, -1);
21
- } else {
22
- args = rest;
23
- deps = void 0;
24
- }
25
- const instanceRef = react.useRef(null);
26
- const mountedRef = react.useRef(false);
27
- const prevDepsRef = react.useRef(void 0);
28
- if (deps !== void 0 && depsChanged(prevDepsRef.current, deps)) {
29
- instanceRef.current?.dispose();
30
- instanceRef.current = null;
31
- }
32
- if (deps !== void 0) {
33
- prevDepsRef.current = deps;
34
- }
35
- if (!instanceRef.current || instanceRef.current.disposed) {
36
- const isClass = typeof classOrFactory === "function" && classOrFactory.prototype && classOrFactory.prototype.constructor === classOrFactory;
37
- if (isClass) {
38
- instanceRef.current = new classOrFactory(...args);
39
- } else {
40
- instanceRef.current = classOrFactory();
41
- }
42
- }
43
- react.useEffect(() => {
44
- const instance = instanceRef.current;
45
- mountedRef.current = true;
46
- if (guards.isInitializable(instance)) {
47
- instance.init();
48
- }
49
- return () => {
50
- mountedRef.current = false;
51
- setTimeout(() => {
52
- if (!mountedRef.current) {
53
- instance.dispose();
54
- }
55
- }, 0);
56
- };
57
- }, deps ?? []);
58
- if (guards.isSubscribable(instanceRef.current)) {
59
- const state = useInstance.useInstance(instanceRef.current);
60
- return [state, instanceRef.current];
61
- }
62
- if (guards.isSubscribeOnly(instanceRef.current)) {
63
- useSubscribeOnly.useSubscribeOnly(instanceRef.current);
64
- return instanceRef.current;
65
- }
66
- return instanceRef.current;
13
+ let args;
14
+ let deps;
15
+ if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {
16
+ deps = rest[rest.length - 1];
17
+ args = rest.slice(0, -1);
18
+ } else {
19
+ args = rest;
20
+ deps = void 0;
21
+ }
22
+ const instanceRef = (0, react.useRef)(null);
23
+ const mountedRef = (0, react.useRef)(false);
24
+ const prevDepsRef = (0, react.useRef)(void 0);
25
+ if (deps !== void 0 && depsChanged(prevDepsRef.current, deps)) {
26
+ instanceRef.current?.dispose();
27
+ instanceRef.current = null;
28
+ }
29
+ if (deps !== void 0) prevDepsRef.current = deps;
30
+ if (!instanceRef.current || instanceRef.current.disposed) if (typeof classOrFactory === "function" && classOrFactory.prototype && classOrFactory.prototype.constructor === classOrFactory) instanceRef.current = new classOrFactory(...args);
31
+ else instanceRef.current = classOrFactory();
32
+ (0, react.useEffect)(() => {
33
+ const instance = instanceRef.current;
34
+ mountedRef.current = true;
35
+ if (require_guards.isInitializable(instance)) instance.init();
36
+ return () => {
37
+ mountedRef.current = false;
38
+ setTimeout(() => {
39
+ if (!mountedRef.current) instance.dispose();
40
+ }, 0);
41
+ };
42
+ }, deps ?? []);
43
+ if (require_guards.isSubscribable(instanceRef.current)) return [require_use_instance.useInstance(instanceRef.current), instanceRef.current];
44
+ if (require_guards.isSubscribeOnly(instanceRef.current)) {
45
+ require_use_subscribe_only.useSubscribeOnly(instanceRef.current);
46
+ return instanceRef.current;
47
+ }
48
+ return instanceRef.current;
67
49
  }
50
+ //#endregion
68
51
  exports.useLocal = useLocal;
69
- //# sourceMappingURL=use-local.cjs.map
52
+
53
+ //# sourceMappingURL=use-local.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-local.cjs","sources":["../../src/react/use-local.ts"],"sourcesContent":["import { useRef, useEffect } from 'react';\nimport type { DependencyList } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\nimport { useSubscribeOnly } from './use-subscribe-only';\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 // ── Subscribe for re-renders if subscribe-only (e.g., Trackable) ──\n if (isSubscribeOnly(instanceRef.current)) {\n useSubscribeOnly(instanceRef.current);\n return instanceRef.current;\n }\n\n return instanceRef.current;\n}\n"],"names":["useRef","useEffect","isInitializable","isSubscribable","useInstance","isSubscribeOnly","useSubscribeOnly"],"mappings":";;;;;;AAQA,SAAS,YAAY,MAAkC,MAA+B;AACpF,MAAI,SAAS,OAAW,QAAO;AAC/B,MAAI,KAAK,WAAW,KAAK,OAAQ,QAAO;AACxC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,EAAG,QAAO;AAAA,EAC3C;AACA,SAAO;AACT;AA2EO,SAAS,SACd,mBACG,MACS;AAEZ,MAAI;AACJ,MAAI;AAEJ,MAAI,KAAK,SAAS,KAAK,MAAM,QAAQ,KAAK,KAAK,SAAS,CAAC,CAAC,GAAG;AAC3D,WAAO,KAAK,KAAK,SAAS,CAAC;AAC3B,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACzB,OAAO;AACL,WAAO;AACP,WAAO;AAAA,EACT;AAEA,QAAM,cAAcA,MAAAA,OAAiB,IAAI;AACzC,QAAM,aAAaA,MAAAA,OAAO,KAAK;AAC/B,QAAM,cAAcA,MAAAA,OAAmC,MAAS;AAGhE,MAAI,SAAS,UAAa,YAAY,YAAY,SAAS,IAAI,GAAG;AAChE,gBAAY,SAAS,QAAA;AACrB,gBAAY,UAAU;AAAA,EACxB;AACA,MAAI,SAAS,QAAW;AACtB,gBAAY,UAAU;AAAA,EACxB;AAGA,MAAI,CAAC,YAAY,WAAW,YAAY,QAAQ,UAAU;AACxD,UAAM,UACJ,OAAO,mBAAmB,cAC1B,eAAe,aACf,eAAe,UAAU,gBAAgB;AAE3C,QAAI,SAAS;AACX,kBAAY,UAAU,IAAK,eAA8C,GAAG,IAAI;AAAA,IAClF,OAAO;AACL,kBAAY,UAAW,eAAA;AAAA,IACzB;AAAA,EACF;AAGAC,QAAAA,UAAU,MAAM;AACd,UAAM,WAAW,YAAY;AAC7B,eAAW,UAAU;AACrB,QAAIC,OAAAA,gBAAgB,QAAQ,GAAG;AAC7B,eAAS,KAAA;AAAA,IACX;AACA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,mBAAS,QAAA;AAAA,QACX;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EAEF,GAAG,QAAQ,EAAE;AAGb,MAAIC,OAAAA,eAAe,YAAY,OAAO,GAAG;AACvC,UAAM,QAAQC,YAAAA,YAAY,YAAY,OAAqC;AAC3E,WAAO,CAAC,OAAO,YAAY,OAAO;AAAA,EACpC;AAGA,MAAIC,OAAAA,gBAAgB,YAAY,OAAO,GAAG;AACxCC,qBAAAA,iBAAiB,YAAY,OAAO;AACpC,WAAO,YAAY;AAAA,EACrB;AAEA,SAAO,YAAY;AACrB;;"}
1
+ {"version":3,"file":"use-local.cjs","names":[],"sources":["../../src/react/use-local.ts"],"sourcesContent":["import { useRef, useEffect } from 'react';\nimport type { DependencyList } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\nimport { useSubscribeOnly } from './use-subscribe-only';\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 // ── Subscribe for re-renders if subscribe-only (e.g., Trackable) ──\n if (isSubscribeOnly(instanceRef.current)) {\n useSubscribeOnly(instanceRef.current);\n return instanceRef.current;\n }\n\n return instanceRef.current;\n}\n"],"mappings":";;;;;AAQA,SAAS,YAAY,MAAkC,MAA+B;AACpF,KAAI,SAAS,KAAA,EAAW,QAAO;AAC/B,KAAI,KAAK,WAAW,KAAK,OAAQ,QAAO;AACxC,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAC/B,KAAI,CAAC,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,CAAE,QAAO;AAE3C,QAAO;;AA4ET,SAAgB,SACd,gBACA,GAAG,MACS;CAEZ,IAAI;CACJ,IAAI;AAEJ,KAAI,KAAK,SAAS,KAAK,MAAM,QAAQ,KAAK,KAAK,SAAS,GAAG,EAAE;AAC3D,SAAO,KAAK,KAAK,SAAS;AAC1B,SAAO,KAAK,MAAM,GAAG,GAAG;QACnB;AACL,SAAO;AACP,SAAO,KAAA;;CAGT,MAAM,eAAA,GAAA,MAAA,QAA+B,KAAK;CAC1C,MAAM,cAAA,GAAA,MAAA,QAAoB,MAAM;CAChC,MAAM,eAAA,GAAA,MAAA,QAAiD,KAAA,EAAU;AAGjE,KAAI,SAAS,KAAA,KAAa,YAAY,YAAY,SAAS,KAAK,EAAE;AAChE,cAAY,SAAS,SAAS;AAC9B,cAAY,UAAU;;AAExB,KAAI,SAAS,KAAA,EACX,aAAY,UAAU;AAIxB,KAAI,CAAC,YAAY,WAAW,YAAY,QAAQ,SAM9C,KAJE,OAAO,mBAAmB,cAC1B,eAAe,aACf,eAAe,UAAU,gBAAgB,eAGzC,aAAY,UAAU,IAAK,eAA8C,GAAG,KAAK;KAEjF,aAAY,UAAW,gBAA4B;AAKvD,EAAA,GAAA,MAAA,iBAAgB;EACd,MAAM,WAAW,YAAY;AAC7B,aAAW,UAAU;AACrB,MAAI,eAAA,gBAAgB,SAAS,CAC3B,UAAS,MAAM;AAEjB,eAAa;AACX,cAAW,UAAU;AACrB,oBAAiB;AACf,QAAI,CAAC,WAAW,QACd,UAAS,SAAS;MAEnB,EAAE;;IAGN,QAAQ,EAAE,CAAC;AAGd,KAAI,eAAA,eAAe,YAAY,QAAQ,CAErC,QAAO,CADO,qBAAA,YAAY,YAAY,QAAsC,EAC7D,YAAY,QAAQ;AAIrC,KAAI,eAAA,gBAAgB,YAAY,QAAQ,EAAE;AACxC,6BAAA,iBAAiB,YAAY,QAAQ;AACrC,SAAO,YAAY;;AAGrB,QAAO,YAAY"}
@@ -1,69 +1,53 @@
1
- import { useRef, useEffect } from "react";
2
- import { isInitializable, isSubscribable, isSubscribeOnly } from "./guards.js";
3
1
  import { useInstance } from "./use-instance.js";
2
+ import { isInitializable, isSubscribable, isSubscribeOnly } from "./guards.js";
4
3
  import { useSubscribeOnly } from "./use-subscribe-only.js";
4
+ import { useEffect, useRef } from "react";
5
+ //#region src/react/use-local.ts
5
6
  function depsChanged(prev, next) {
6
- if (prev === void 0) return false;
7
- if (prev.length !== next.length) return true;
8
- for (let i = 0; i < prev.length; i++) {
9
- if (!Object.is(prev[i], next[i])) return true;
10
- }
11
- return false;
7
+ if (prev === void 0) return false;
8
+ if (prev.length !== next.length) return true;
9
+ for (let i = 0; i < prev.length; i++) if (!Object.is(prev[i], next[i])) return true;
10
+ return false;
12
11
  }
13
12
  function useLocal(classOrFactory, ...rest) {
14
- let args;
15
- let deps;
16
- if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {
17
- deps = rest[rest.length - 1];
18
- args = rest.slice(0, -1);
19
- } else {
20
- args = rest;
21
- deps = void 0;
22
- }
23
- const instanceRef = useRef(null);
24
- const mountedRef = useRef(false);
25
- const prevDepsRef = useRef(void 0);
26
- if (deps !== void 0 && depsChanged(prevDepsRef.current, deps)) {
27
- instanceRef.current?.dispose();
28
- instanceRef.current = null;
29
- }
30
- if (deps !== void 0) {
31
- prevDepsRef.current = deps;
32
- }
33
- if (!instanceRef.current || instanceRef.current.disposed) {
34
- const isClass = typeof classOrFactory === "function" && classOrFactory.prototype && classOrFactory.prototype.constructor === classOrFactory;
35
- if (isClass) {
36
- instanceRef.current = new classOrFactory(...args);
37
- } else {
38
- instanceRef.current = classOrFactory();
39
- }
40
- }
41
- useEffect(() => {
42
- const instance = instanceRef.current;
43
- mountedRef.current = true;
44
- if (isInitializable(instance)) {
45
- instance.init();
46
- }
47
- return () => {
48
- mountedRef.current = false;
49
- setTimeout(() => {
50
- if (!mountedRef.current) {
51
- instance.dispose();
52
- }
53
- }, 0);
54
- };
55
- }, deps ?? []);
56
- if (isSubscribable(instanceRef.current)) {
57
- const state = useInstance(instanceRef.current);
58
- return [state, instanceRef.current];
59
- }
60
- if (isSubscribeOnly(instanceRef.current)) {
61
- useSubscribeOnly(instanceRef.current);
62
- return instanceRef.current;
63
- }
64
- return instanceRef.current;
13
+ let args;
14
+ let deps;
15
+ if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {
16
+ deps = rest[rest.length - 1];
17
+ args = rest.slice(0, -1);
18
+ } else {
19
+ args = rest;
20
+ deps = void 0;
21
+ }
22
+ const instanceRef = useRef(null);
23
+ const mountedRef = useRef(false);
24
+ const prevDepsRef = useRef(void 0);
25
+ if (deps !== void 0 && depsChanged(prevDepsRef.current, deps)) {
26
+ instanceRef.current?.dispose();
27
+ instanceRef.current = null;
28
+ }
29
+ if (deps !== void 0) prevDepsRef.current = deps;
30
+ if (!instanceRef.current || instanceRef.current.disposed) if (typeof classOrFactory === "function" && classOrFactory.prototype && classOrFactory.prototype.constructor === classOrFactory) instanceRef.current = new classOrFactory(...args);
31
+ else instanceRef.current = classOrFactory();
32
+ useEffect(() => {
33
+ const instance = instanceRef.current;
34
+ mountedRef.current = true;
35
+ if (isInitializable(instance)) instance.init();
36
+ return () => {
37
+ mountedRef.current = false;
38
+ setTimeout(() => {
39
+ if (!mountedRef.current) instance.dispose();
40
+ }, 0);
41
+ };
42
+ }, deps ?? []);
43
+ if (isSubscribable(instanceRef.current)) return [useInstance(instanceRef.current), instanceRef.current];
44
+ if (isSubscribeOnly(instanceRef.current)) {
45
+ useSubscribeOnly(instanceRef.current);
46
+ return instanceRef.current;
47
+ }
48
+ return instanceRef.current;
65
49
  }
66
- export {
67
- useLocal
68
- };
69
- //# sourceMappingURL=use-local.js.map
50
+ //#endregion
51
+ export { useLocal };
52
+
53
+ //# sourceMappingURL=use-local.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-local.js","sources":["../../src/react/use-local.ts"],"sourcesContent":["import { useRef, useEffect } from 'react';\nimport type { DependencyList } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\nimport { useSubscribeOnly } from './use-subscribe-only';\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 // ── Subscribe for re-renders if subscribe-only (e.g., Trackable) ──\n if (isSubscribeOnly(instanceRef.current)) {\n useSubscribeOnly(instanceRef.current);\n return instanceRef.current;\n }\n\n return instanceRef.current;\n}\n"],"names":[],"mappings":";;;;AAQA,SAAS,YAAY,MAAkC,MAA+B;AACpF,MAAI,SAAS,OAAW,QAAO;AAC/B,MAAI,KAAK,WAAW,KAAK,OAAQ,QAAO;AACxC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,EAAG,QAAO;AAAA,EAC3C;AACA,SAAO;AACT;AA2EO,SAAS,SACd,mBACG,MACS;AAEZ,MAAI;AACJ,MAAI;AAEJ,MAAI,KAAK,SAAS,KAAK,MAAM,QAAQ,KAAK,KAAK,SAAS,CAAC,CAAC,GAAG;AAC3D,WAAO,KAAK,KAAK,SAAS,CAAC;AAC3B,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACzB,OAAO;AACL,WAAO;AACP,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,OAAiB,IAAI;AACzC,QAAM,aAAa,OAAO,KAAK;AAC/B,QAAM,cAAc,OAAmC,MAAS;AAGhE,MAAI,SAAS,UAAa,YAAY,YAAY,SAAS,IAAI,GAAG;AAChE,gBAAY,SAAS,QAAA;AACrB,gBAAY,UAAU;AAAA,EACxB;AACA,MAAI,SAAS,QAAW;AACtB,gBAAY,UAAU;AAAA,EACxB;AAGA,MAAI,CAAC,YAAY,WAAW,YAAY,QAAQ,UAAU;AACxD,UAAM,UACJ,OAAO,mBAAmB,cAC1B,eAAe,aACf,eAAe,UAAU,gBAAgB;AAE3C,QAAI,SAAS;AACX,kBAAY,UAAU,IAAK,eAA8C,GAAG,IAAI;AAAA,IAClF,OAAO;AACL,kBAAY,UAAW,eAAA;AAAA,IACzB;AAAA,EACF;AAGA,YAAU,MAAM;AACd,UAAM,WAAW,YAAY;AAC7B,eAAW,UAAU;AACrB,QAAI,gBAAgB,QAAQ,GAAG;AAC7B,eAAS,KAAA;AAAA,IACX;AACA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,mBAAS,QAAA;AAAA,QACX;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EAEF,GAAG,QAAQ,EAAE;AAGb,MAAI,eAAe,YAAY,OAAO,GAAG;AACvC,UAAM,QAAQ,YAAY,YAAY,OAAqC;AAC3E,WAAO,CAAC,OAAO,YAAY,OAAO;AAAA,EACpC;AAGA,MAAI,gBAAgB,YAAY,OAAO,GAAG;AACxC,qBAAiB,YAAY,OAAO;AACpC,WAAO,YAAY;AAAA,EACrB;AAEA,SAAO,YAAY;AACrB;"}
1
+ {"version":3,"file":"use-local.js","names":[],"sources":["../../src/react/use-local.ts"],"sourcesContent":["import { useRef, useEffect } from 'react';\nimport type { DependencyList } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\nimport { useSubscribeOnly } from './use-subscribe-only';\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 // ── Subscribe for re-renders if subscribe-only (e.g., Trackable) ──\n if (isSubscribeOnly(instanceRef.current)) {\n useSubscribeOnly(instanceRef.current);\n return instanceRef.current;\n }\n\n return instanceRef.current;\n}\n"],"mappings":";;;;;AAQA,SAAS,YAAY,MAAkC,MAA+B;AACpF,KAAI,SAAS,KAAA,EAAW,QAAO;AAC/B,KAAI,KAAK,WAAW,KAAK,OAAQ,QAAO;AACxC,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAC/B,KAAI,CAAC,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,CAAE,QAAO;AAE3C,QAAO;;AA4ET,SAAgB,SACd,gBACA,GAAG,MACS;CAEZ,IAAI;CACJ,IAAI;AAEJ,KAAI,KAAK,SAAS,KAAK,MAAM,QAAQ,KAAK,KAAK,SAAS,GAAG,EAAE;AAC3D,SAAO,KAAK,KAAK,SAAS;AAC1B,SAAO,KAAK,MAAM,GAAG,GAAG;QACnB;AACL,SAAO;AACP,SAAO,KAAA;;CAGT,MAAM,cAAc,OAAiB,KAAK;CAC1C,MAAM,aAAa,OAAO,MAAM;CAChC,MAAM,cAAc,OAAmC,KAAA,EAAU;AAGjE,KAAI,SAAS,KAAA,KAAa,YAAY,YAAY,SAAS,KAAK,EAAE;AAChE,cAAY,SAAS,SAAS;AAC9B,cAAY,UAAU;;AAExB,KAAI,SAAS,KAAA,EACX,aAAY,UAAU;AAIxB,KAAI,CAAC,YAAY,WAAW,YAAY,QAAQ,SAM9C,KAJE,OAAO,mBAAmB,cAC1B,eAAe,aACf,eAAe,UAAU,gBAAgB,eAGzC,aAAY,UAAU,IAAK,eAA8C,GAAG,KAAK;KAEjF,aAAY,UAAW,gBAA4B;AAKvD,iBAAgB;EACd,MAAM,WAAW,YAAY;AAC7B,aAAW,UAAU;AACrB,MAAI,gBAAgB,SAAS,CAC3B,UAAS,MAAM;AAEjB,eAAa;AACX,cAAW,UAAU;AACrB,oBAAiB;AACf,QAAI,CAAC,WAAW,QACd,UAAS,SAAS;MAEnB,EAAE;;IAGN,QAAQ,EAAE,CAAC;AAGd,KAAI,eAAe,YAAY,QAAQ,CAErC,QAAO,CADO,YAAY,YAAY,QAAsC,EAC7D,YAAY,QAAQ;AAIrC,KAAI,gBAAgB,YAAY,QAAQ,EAAE;AACxC,mBAAiB,YAAY,QAAQ;AACrC,SAAO,YAAY;;AAGrB,QAAO,YAAY"}
@@ -1,107 +1,93 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const react = require("react");
4
- const guards = require("./guards.cjs");
1
+ const require_guards = require("./guards.cjs");
2
+ let react = require("react");
3
+ //#region src/react/use-model.ts
4
+ /**
5
+ * Bind to a component-scoped Model with validation and dirty state exposed.
6
+ */
5
7
  function useModel(factory) {
6
- const modelRef = react.useRef(null);
7
- const mountedRef = react.useRef(false);
8
- if (!modelRef.current || modelRef.current.disposed) {
9
- modelRef.current = factory();
10
- }
11
- const modelSubscribe = react.useCallback(
12
- (onStoreChange) => modelRef.current.subscribe(onStoreChange),
13
- []
14
- );
15
- const modelSnapshot = react.useCallback(
16
- () => modelRef.current.state,
17
- []
18
- );
19
- react.useSyncExternalStore(modelSubscribe, modelSnapshot, modelSnapshot);
20
- react.useEffect(() => {
21
- mountedRef.current = true;
22
- if (guards.isInitializable(modelRef.current)) {
23
- modelRef.current.init();
24
- }
25
- return () => {
26
- mountedRef.current = false;
27
- setTimeout(() => {
28
- if (!mountedRef.current) {
29
- modelRef.current?.dispose();
30
- }
31
- }, 0);
32
- };
33
- }, []);
34
- const model = modelRef.current;
35
- return {
36
- state: model.state,
37
- errors: model.errors,
38
- valid: model.valid,
39
- dirty: model.dirty,
40
- model
41
- };
8
+ const modelRef = (0, react.useRef)(null);
9
+ const mountedRef = (0, react.useRef)(false);
10
+ if (!modelRef.current || modelRef.current.disposed) modelRef.current = factory();
11
+ const modelSubscribe = (0, react.useCallback)((onStoreChange) => modelRef.current.subscribe(onStoreChange), []);
12
+ const modelSnapshot = (0, react.useCallback)(() => modelRef.current.state, []);
13
+ (0, react.useSyncExternalStore)(modelSubscribe, modelSnapshot, modelSnapshot);
14
+ (0, react.useEffect)(() => {
15
+ mountedRef.current = true;
16
+ if (require_guards.isInitializable(modelRef.current)) modelRef.current.init();
17
+ return () => {
18
+ mountedRef.current = false;
19
+ setTimeout(() => {
20
+ if (!mountedRef.current) modelRef.current?.dispose();
21
+ }, 0);
22
+ };
23
+ }, []);
24
+ const model = modelRef.current;
25
+ return {
26
+ state: model.state,
27
+ errors: model.errors,
28
+ valid: model.valid,
29
+ dirty: model.dirty,
30
+ model
31
+ };
42
32
  }
33
+ /**
34
+ * Create a component-scoped Model with lifecycle management (init + dispose)
35
+ * but NO state subscription. The parent component never re-renders from
36
+ * model state changes.
37
+ *
38
+ * Designed for the per-field isolation pattern: parent creates the model
39
+ * via `useModelRef`, children subscribe to individual fields via `useField`.
40
+ */
43
41
  function useModelRef(factory) {
44
- const modelRef = react.useRef(null);
45
- const mountedRef = react.useRef(false);
46
- if (!modelRef.current || modelRef.current.disposed) {
47
- modelRef.current = factory();
48
- }
49
- react.useEffect(() => {
50
- mountedRef.current = true;
51
- if (guards.isInitializable(modelRef.current)) {
52
- modelRef.current.init();
53
- }
54
- return () => {
55
- mountedRef.current = false;
56
- setTimeout(() => {
57
- if (!mountedRef.current) {
58
- modelRef.current?.dispose();
59
- }
60
- }, 0);
61
- };
62
- }, []);
63
- return modelRef.current;
42
+ const modelRef = (0, react.useRef)(null);
43
+ const mountedRef = (0, react.useRef)(false);
44
+ if (!modelRef.current || modelRef.current.disposed) modelRef.current = factory();
45
+ (0, react.useEffect)(() => {
46
+ mountedRef.current = true;
47
+ if (require_guards.isInitializable(modelRef.current)) modelRef.current.init();
48
+ return () => {
49
+ mountedRef.current = false;
50
+ setTimeout(() => {
51
+ if (!mountedRef.current) modelRef.current?.dispose();
52
+ }, 0);
53
+ };
54
+ }, []);
55
+ return modelRef.current;
64
56
  }
57
+ /**
58
+ * Bind to a single Model field with surgical re-renders.
59
+ */
65
60
  function useField(model, field) {
66
- const getSnapshot = react.useCallback(() => {
67
- return {
68
- value: model.state[field],
69
- error: model.errors[field]
70
- };
71
- }, [model, field]);
72
- const cachedRef = react.useRef(getSnapshot());
73
- const subscribe = react.useCallback(
74
- (onStoreChange) => {
75
- return model.subscribe(() => {
76
- const next = getSnapshot();
77
- const current = cachedRef.current;
78
- if (next.value !== current.value || next.error !== current.error) {
79
- cachedRef.current = next;
80
- onStoreChange();
81
- }
82
- });
83
- },
84
- [model, getSnapshot]
85
- );
86
- const snapshot = react.useSyncExternalStore(
87
- subscribe,
88
- () => cachedRef.current,
89
- () => cachedRef.current
90
- );
91
- const set = react.useCallback(
92
- (value) => {
93
- const partial = { [field]: value };
94
- model.set(partial);
95
- },
96
- [model, field]
97
- );
98
- return {
99
- value: snapshot.value,
100
- error: snapshot.error,
101
- set
102
- };
61
+ const getSnapshot = (0, react.useCallback)(() => {
62
+ return {
63
+ value: model.state[field],
64
+ error: model.errors[field]
65
+ };
66
+ }, [model, field]);
67
+ const cachedRef = (0, react.useRef)(getSnapshot());
68
+ const snapshot = (0, react.useSyncExternalStore)((0, react.useCallback)((onStoreChange) => {
69
+ return model.subscribe(() => {
70
+ const next = getSnapshot();
71
+ const current = cachedRef.current;
72
+ if (next.value !== current.value || next.error !== current.error) {
73
+ cachedRef.current = next;
74
+ onStoreChange();
75
+ }
76
+ });
77
+ }, [model, getSnapshot]), () => cachedRef.current, () => cachedRef.current);
78
+ const set = (0, react.useCallback)((value) => {
79
+ const partial = { [field]: value };
80
+ model.set(partial);
81
+ }, [model, field]);
82
+ return {
83
+ value: snapshot.value,
84
+ error: snapshot.error,
85
+ set
86
+ };
103
87
  }
88
+ //#endregion
104
89
  exports.useField = useField;
105
90
  exports.useModel = useModel;
106
91
  exports.useModelRef = useModelRef;
107
- //# sourceMappingURL=use-model.cjs.map
92
+
93
+ //# sourceMappingURL=use-model.cjs.map