mvc-kit 2.12.4 → 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 +1 @@
1
- {"version":3,"file":"use-model.cjs","sources":["../../src/react/use-model.ts"],"sourcesContent":["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 const modelSubscribe = useCallback(\n (onStoreChange: () => void) => modelRef.current!.subscribe(onStoreChange),\n []\n );\n const modelSnapshot = useCallback(\n () => modelRef.current!.state,\n []\n );\n useSyncExternalStore(modelSubscribe, modelSnapshot, modelSnapshot);\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/**\n * Create a component-scoped Model with lifecycle management (init + dispose)\n * but NO state subscription. The parent component never re-renders from\n * model state changes.\n *\n * Designed for the per-field isolation pattern: parent creates the model\n * via `useModelRef`, children subscribe to individual fields via `useField`.\n */\nexport function useModelRef<M extends Model<any>>(factory: () => 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 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 return modelRef.current;\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"],"names":["useRef","useCallback","useSyncExternalStore","useEffect","isInitializable"],"mappings":";;;;AAkBO,SAAS,SACd,SAC4B;AAC5B,QAAM,WAAWA,MAAAA,OAAiB,IAAI;AACtC,QAAM,aAAaA,MAAAA,OAAO,KAAK;AAE/B,MAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,UAAU;AAClD,aAAS,UAAU,QAAA;AAAA,EACrB;AAEA,QAAM,iBAAiBC,MAAAA;AAAAA,IACrB,CAAC,kBAA8B,SAAS,QAAS,UAAU,aAAa;AAAA,IACxE,CAAA;AAAA,EAAC;AAEH,QAAM,gBAAgBA,MAAAA;AAAAA,IACpB,MAAM,SAAS,QAAS;AAAA,IACxB,CAAA;AAAA,EAAC;AAEHC,6BAAqB,gBAAgB,eAAe,aAAa;AAEjEC,QAAAA,UAAU,MAAM;AACd,eAAW,UAAU;AACrB,QAAIC,OAAAA,gBAAgB,SAAS,OAAO,GAAG;AACrC,eAAS,QAAQ,KAAA;AAAA,IACnB;AACA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,mBAAS,SAAS,QAAA;AAAA,QACpB;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,QAAM,QAAQ,SAAS;AAEvB,SAAO;AAAA,IACL,OAAO,MAAM;AAAA,IACb,QAAQ,MAAM;AAAA,IACd,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,IACb;AAAA,EAAA;AAEJ;AAUO,SAAS,YAAkC,SAAqB;AACrE,QAAM,WAAWJ,MAAAA,OAAiB,IAAI;AACtC,QAAM,aAAaA,MAAAA,OAAO,KAAK;AAE/B,MAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,UAAU;AAClD,aAAS,UAAU,QAAA;AAAA,EACrB;AAEAG,QAAAA,UAAU,MAAM;AACd,eAAW,UAAU;AACrB,QAAIC,OAAAA,gBAAgB,SAAS,OAAO,GAAG;AACrC,eAAS,QAAQ,KAAA;AAAA,IACnB;AACA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,mBAAS,SAAS,QAAA;AAAA,QACpB;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,SAAO,SAAS;AAClB;AAYO,SAAS,SACd,OACA,OACmB;AAEnB,QAAM,cAAcH,MAAAA,YAAY,MAAM;AACpC,WAAO;AAAA,MACL,OAAO,MAAM,MAAM,KAAK;AAAA,MACxB,OAAO,MAAM,OAAO,KAAK;AAAA,IAAA;AAAA,EAE7B,GAAG,CAAC,OAAO,KAAK,CAAC;AAGjB,QAAM,YAAYD,aAAO,aAAa;AAEtC,QAAM,YAAYC,MAAAA;AAAAA,IAChB,CAAC,kBAA8B;AAC7B,aAAO,MAAM,UAAU,MAAM;AAC3B,cAAM,OAAO,YAAA;AACb,cAAM,UAAU,UAAU;AAG1B,YAAI,KAAK,UAAU,QAAQ,SAAS,KAAK,UAAU,QAAQ,OAAO;AAChE,oBAAU,UAAU;AACpB,wBAAA;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,CAAC,OAAO,WAAW;AAAA,EAAA;AAGrB,QAAM,WAAWC,MAAAA;AAAAA,IACf;AAAA,IACA,MAAM,UAAU;AAAA,IAChB,MAAM,UAAU;AAAA,EAAA;AAGlB,QAAM,MAAMD,MAAAA;AAAAA,IACV,CAAC,UAAgB;AAGf,YAAM,UAAsB,EAAE,CAAC,KAAK,GAAG,MAAA;AACtC,YAA4D,IAAI,OAAO;AAAA,IAC1E;AAAA,IACA,CAAC,OAAO,KAAK;AAAA,EAAA;AAGf,SAAO;AAAA,IACL,OAAO,SAAS;AAAA,IAChB,OAAO,SAAS;AAAA,IAChB;AAAA,EAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"use-model.cjs","names":[],"sources":["../../src/react/use-model.ts"],"sourcesContent":["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 const modelSubscribe = useCallback(\n (onStoreChange: () => void) => modelRef.current!.subscribe(onStoreChange),\n []\n );\n const modelSnapshot = useCallback(\n () => modelRef.current!.state,\n []\n );\n useSyncExternalStore(modelSubscribe, modelSnapshot, modelSnapshot);\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/**\n * Create a component-scoped Model with lifecycle management (init + dispose)\n * but NO state subscription. The parent component never re-renders from\n * model state changes.\n *\n * Designed for the per-field isolation pattern: parent creates the model\n * via `useModelRef`, children subscribe to individual fields via `useField`.\n */\nexport function useModelRef<M extends Model<any>>(factory: () => 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 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 return modelRef.current;\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"],"mappings":";;;;;;AAkBA,SAAgB,SACd,SAC4B;CAC5B,MAAM,YAAA,GAAA,MAAA,QAA4B,KAAK;CACvC,MAAM,cAAA,GAAA,MAAA,QAAoB,MAAM;AAEhC,KAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,SACxC,UAAS,UAAU,SAAS;CAG9B,MAAM,kBAAA,GAAA,MAAA,cACH,kBAA8B,SAAS,QAAS,UAAU,cAAc,EACzE,EAAE,CACH;CACD,MAAM,iBAAA,GAAA,MAAA,mBACE,SAAS,QAAS,OACxB,EAAE,CACH;AACD,EAAA,GAAA,MAAA,sBAAqB,gBAAgB,eAAe,cAAc;AAElE,EAAA,GAAA,MAAA,iBAAgB;AACd,aAAW,UAAU;AACrB,MAAI,eAAA,gBAAgB,SAAS,QAAQ,CACnC,UAAS,QAAQ,MAAM;AAEzB,eAAa;AACX,cAAW,UAAU;AACrB,oBAAiB;AACf,QAAI,CAAC,WAAW,QACd,UAAS,SAAS,SAAS;MAE5B,EAAE;;IAEN,EAAE,CAAC;CAEN,MAAM,QAAQ,SAAS;AAEvB,QAAO;EACL,OAAO,MAAM;EACb,QAAQ,MAAM;EACd,OAAO,MAAM;EACb,OAAO,MAAM;EACb;EACD;;;;;;;;;;AAWH,SAAgB,YAAkC,SAAqB;CACrE,MAAM,YAAA,GAAA,MAAA,QAA4B,KAAK;CACvC,MAAM,cAAA,GAAA,MAAA,QAAoB,MAAM;AAEhC,KAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,SACxC,UAAS,UAAU,SAAS;AAG9B,EAAA,GAAA,MAAA,iBAAgB;AACd,aAAW,UAAU;AACrB,MAAI,eAAA,gBAAgB,SAAS,QAAQ,CACnC,UAAS,QAAQ,MAAM;AAEzB,eAAa;AACX,cAAW,UAAU;AACrB,oBAAiB;AACf,QAAI,CAAC,WAAW,QACd,UAAS,SAAS,SAAS;MAE5B,EAAE;;IAEN,EAAE,CAAC;AAEN,QAAO,SAAS;;;;;AAalB,SAAgB,SACd,OACA,OACmB;CAEnB,MAAM,eAAA,GAAA,MAAA,mBAAgC;AACpC,SAAO;GACL,OAAO,MAAM,MAAM;GACnB,OAAO,MAAM,OAAO;GACrB;IACA,CAAC,OAAO,MAAM,CAAC;CAGlB,MAAM,aAAA,GAAA,MAAA,QAAmB,aAAa,CAAC;CAkBvC,MAAM,YAAA,GAAA,MAAA,uBAAA,GAAA,MAAA,cAfH,kBAA8B;AAC7B,SAAO,MAAM,gBAAgB;GAC3B,MAAM,OAAO,aAAa;GAC1B,MAAM,UAAU,UAAU;AAG1B,OAAI,KAAK,UAAU,QAAQ,SAAS,KAAK,UAAU,QAAQ,OAAO;AAChE,cAAU,UAAU;AACpB,mBAAe;;IAEjB;IAEJ,CAAC,OAAO,YAAY,CACrB,QAIO,UAAU,eACV,UAAU,QACjB;CAED,MAAM,OAAA,GAAA,MAAA,cACH,UAAgB;EAGf,MAAM,UAAsB,GAAG,QAAQ,OAAO;AAC7C,QAA4D,IAAI,QAAQ;IAE3E,CAAC,OAAO,MAAM,CACf;AAED,QAAO;EACL,OAAO,SAAS;EAChB,OAAO,SAAS;EAChB;EACD"}
@@ -1,107 +1,91 @@
1
- import { useCallback, useRef, useSyncExternalStore, useEffect } from "react";
2
1
  import { isInitializable } from "./guards.js";
2
+ import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
3
+ //#region src/react/use-model.ts
4
+ /**
5
+ * Bind to a component-scoped Model with validation and dirty state exposed.
6
+ */
3
7
  function useModel(factory) {
4
- const modelRef = useRef(null);
5
- const mountedRef = useRef(false);
6
- if (!modelRef.current || modelRef.current.disposed) {
7
- modelRef.current = factory();
8
- }
9
- const modelSubscribe = useCallback(
10
- (onStoreChange) => modelRef.current.subscribe(onStoreChange),
11
- []
12
- );
13
- const modelSnapshot = useCallback(
14
- () => modelRef.current.state,
15
- []
16
- );
17
- useSyncExternalStore(modelSubscribe, modelSnapshot, modelSnapshot);
18
- useEffect(() => {
19
- mountedRef.current = true;
20
- if (isInitializable(modelRef.current)) {
21
- modelRef.current.init();
22
- }
23
- return () => {
24
- mountedRef.current = false;
25
- setTimeout(() => {
26
- if (!mountedRef.current) {
27
- modelRef.current?.dispose();
28
- }
29
- }, 0);
30
- };
31
- }, []);
32
- const model = modelRef.current;
33
- return {
34
- state: model.state,
35
- errors: model.errors,
36
- valid: model.valid,
37
- dirty: model.dirty,
38
- model
39
- };
8
+ const modelRef = useRef(null);
9
+ const mountedRef = useRef(false);
10
+ if (!modelRef.current || modelRef.current.disposed) modelRef.current = factory();
11
+ const modelSubscribe = useCallback((onStoreChange) => modelRef.current.subscribe(onStoreChange), []);
12
+ const modelSnapshot = useCallback(() => modelRef.current.state, []);
13
+ useSyncExternalStore(modelSubscribe, modelSnapshot, modelSnapshot);
14
+ useEffect(() => {
15
+ mountedRef.current = true;
16
+ if (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
+ };
40
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
+ */
41
41
  function useModelRef(factory) {
42
- const modelRef = useRef(null);
43
- const mountedRef = useRef(false);
44
- if (!modelRef.current || modelRef.current.disposed) {
45
- modelRef.current = factory();
46
- }
47
- useEffect(() => {
48
- mountedRef.current = true;
49
- if (isInitializable(modelRef.current)) {
50
- modelRef.current.init();
51
- }
52
- return () => {
53
- mountedRef.current = false;
54
- setTimeout(() => {
55
- if (!mountedRef.current) {
56
- modelRef.current?.dispose();
57
- }
58
- }, 0);
59
- };
60
- }, []);
61
- return modelRef.current;
42
+ const modelRef = useRef(null);
43
+ const mountedRef = useRef(false);
44
+ if (!modelRef.current || modelRef.current.disposed) modelRef.current = factory();
45
+ useEffect(() => {
46
+ mountedRef.current = true;
47
+ if (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;
62
56
  }
57
+ /**
58
+ * Bind to a single Model field with surgical re-renders.
59
+ */
63
60
  function useField(model, field) {
64
- const getSnapshot = useCallback(() => {
65
- return {
66
- value: model.state[field],
67
- error: model.errors[field]
68
- };
69
- }, [model, field]);
70
- const cachedRef = useRef(getSnapshot());
71
- const subscribe = useCallback(
72
- (onStoreChange) => {
73
- return model.subscribe(() => {
74
- const next = getSnapshot();
75
- const current = cachedRef.current;
76
- if (next.value !== current.value || next.error !== current.error) {
77
- cachedRef.current = next;
78
- onStoreChange();
79
- }
80
- });
81
- },
82
- [model, getSnapshot]
83
- );
84
- const snapshot = useSyncExternalStore(
85
- subscribe,
86
- () => cachedRef.current,
87
- () => cachedRef.current
88
- );
89
- const set = useCallback(
90
- (value) => {
91
- const partial = { [field]: value };
92
- model.set(partial);
93
- },
94
- [model, field]
95
- );
96
- return {
97
- value: snapshot.value,
98
- error: snapshot.error,
99
- set
100
- };
61
+ const getSnapshot = useCallback(() => {
62
+ return {
63
+ value: model.state[field],
64
+ error: model.errors[field]
65
+ };
66
+ }, [model, field]);
67
+ const cachedRef = useRef(getSnapshot());
68
+ const snapshot = useSyncExternalStore(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 = 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
+ };
101
87
  }
102
- export {
103
- useField,
104
- useModel,
105
- useModelRef
106
- };
107
- //# sourceMappingURL=use-model.js.map
88
+ //#endregion
89
+ export { useField, useModel, useModelRef };
90
+
91
+ //# sourceMappingURL=use-model.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-model.js","sources":["../../src/react/use-model.ts"],"sourcesContent":["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 const modelSubscribe = useCallback(\n (onStoreChange: () => void) => modelRef.current!.subscribe(onStoreChange),\n []\n );\n const modelSnapshot = useCallback(\n () => modelRef.current!.state,\n []\n );\n useSyncExternalStore(modelSubscribe, modelSnapshot, modelSnapshot);\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/**\n * Create a component-scoped Model with lifecycle management (init + dispose)\n * but NO state subscription. The parent component never re-renders from\n * model state changes.\n *\n * Designed for the per-field isolation pattern: parent creates the model\n * via `useModelRef`, children subscribe to individual fields via `useField`.\n */\nexport function useModelRef<M extends Model<any>>(factory: () => 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 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 return modelRef.current;\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"],"names":[],"mappings":";;AAkBO,SAAS,SACd,SAC4B;AAC5B,QAAM,WAAW,OAAiB,IAAI;AACtC,QAAM,aAAa,OAAO,KAAK;AAE/B,MAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,UAAU;AAClD,aAAS,UAAU,QAAA;AAAA,EACrB;AAEA,QAAM,iBAAiB;AAAA,IACrB,CAAC,kBAA8B,SAAS,QAAS,UAAU,aAAa;AAAA,IACxE,CAAA;AAAA,EAAC;AAEH,QAAM,gBAAgB;AAAA,IACpB,MAAM,SAAS,QAAS;AAAA,IACxB,CAAA;AAAA,EAAC;AAEH,uBAAqB,gBAAgB,eAAe,aAAa;AAEjE,YAAU,MAAM;AACd,eAAW,UAAU;AACrB,QAAI,gBAAgB,SAAS,OAAO,GAAG;AACrC,eAAS,QAAQ,KAAA;AAAA,IACnB;AACA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,mBAAS,SAAS,QAAA;AAAA,QACpB;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,QAAM,QAAQ,SAAS;AAEvB,SAAO;AAAA,IACL,OAAO,MAAM;AAAA,IACb,QAAQ,MAAM;AAAA,IACd,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,IACb;AAAA,EAAA;AAEJ;AAUO,SAAS,YAAkC,SAAqB;AACrE,QAAM,WAAW,OAAiB,IAAI;AACtC,QAAM,aAAa,OAAO,KAAK;AAE/B,MAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,UAAU;AAClD,aAAS,UAAU,QAAA;AAAA,EACrB;AAEA,YAAU,MAAM;AACd,eAAW,UAAU;AACrB,QAAI,gBAAgB,SAAS,OAAO,GAAG;AACrC,eAAS,QAAQ,KAAA;AAAA,IACnB;AACA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,mBAAS,SAAS,QAAA;AAAA,QACpB;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,SAAO,SAAS;AAClB;AAYO,SAAS,SACd,OACA,OACmB;AAEnB,QAAM,cAAc,YAAY,MAAM;AACpC,WAAO;AAAA,MACL,OAAO,MAAM,MAAM,KAAK;AAAA,MACxB,OAAO,MAAM,OAAO,KAAK;AAAA,IAAA;AAAA,EAE7B,GAAG,CAAC,OAAO,KAAK,CAAC;AAGjB,QAAM,YAAY,OAAO,aAAa;AAEtC,QAAM,YAAY;AAAA,IAChB,CAAC,kBAA8B;AAC7B,aAAO,MAAM,UAAU,MAAM;AAC3B,cAAM,OAAO,YAAA;AACb,cAAM,UAAU,UAAU;AAG1B,YAAI,KAAK,UAAU,QAAQ,SAAS,KAAK,UAAU,QAAQ,OAAO;AAChE,oBAAU,UAAU;AACpB,wBAAA;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,CAAC,OAAO,WAAW;AAAA,EAAA;AAGrB,QAAM,WAAW;AAAA,IACf;AAAA,IACA,MAAM,UAAU;AAAA,IAChB,MAAM,UAAU;AAAA,EAAA;AAGlB,QAAM,MAAM;AAAA,IACV,CAAC,UAAgB;AAGf,YAAM,UAAsB,EAAE,CAAC,KAAK,GAAG,MAAA;AACtC,YAA4D,IAAI,OAAO;AAAA,IAC1E;AAAA,IACA,CAAC,OAAO,KAAK;AAAA,EAAA;AAGf,SAAO;AAAA,IACL,OAAO,SAAS;AAAA,IAChB,OAAO,SAAS;AAAA,IAChB;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"use-model.js","names":[],"sources":["../../src/react/use-model.ts"],"sourcesContent":["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 const modelSubscribe = useCallback(\n (onStoreChange: () => void) => modelRef.current!.subscribe(onStoreChange),\n []\n );\n const modelSnapshot = useCallback(\n () => modelRef.current!.state,\n []\n );\n useSyncExternalStore(modelSubscribe, modelSnapshot, modelSnapshot);\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/**\n * Create a component-scoped Model with lifecycle management (init + dispose)\n * but NO state subscription. The parent component never re-renders from\n * model state changes.\n *\n * Designed for the per-field isolation pattern: parent creates the model\n * via `useModelRef`, children subscribe to individual fields via `useField`.\n */\nexport function useModelRef<M extends Model<any>>(factory: () => 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 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 return modelRef.current;\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"],"mappings":";;;;;;AAkBA,SAAgB,SACd,SAC4B;CAC5B,MAAM,WAAW,OAAiB,KAAK;CACvC,MAAM,aAAa,OAAO,MAAM;AAEhC,KAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,SACxC,UAAS,UAAU,SAAS;CAG9B,MAAM,iBAAiB,aACpB,kBAA8B,SAAS,QAAS,UAAU,cAAc,EACzE,EAAE,CACH;CACD,MAAM,gBAAgB,kBACd,SAAS,QAAS,OACxB,EAAE,CACH;AACD,sBAAqB,gBAAgB,eAAe,cAAc;AAElE,iBAAgB;AACd,aAAW,UAAU;AACrB,MAAI,gBAAgB,SAAS,QAAQ,CACnC,UAAS,QAAQ,MAAM;AAEzB,eAAa;AACX,cAAW,UAAU;AACrB,oBAAiB;AACf,QAAI,CAAC,WAAW,QACd,UAAS,SAAS,SAAS;MAE5B,EAAE;;IAEN,EAAE,CAAC;CAEN,MAAM,QAAQ,SAAS;AAEvB,QAAO;EACL,OAAO,MAAM;EACb,QAAQ,MAAM;EACd,OAAO,MAAM;EACb,OAAO,MAAM;EACb;EACD;;;;;;;;;;AAWH,SAAgB,YAAkC,SAAqB;CACrE,MAAM,WAAW,OAAiB,KAAK;CACvC,MAAM,aAAa,OAAO,MAAM;AAEhC,KAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,SACxC,UAAS,UAAU,SAAS;AAG9B,iBAAgB;AACd,aAAW,UAAU;AACrB,MAAI,gBAAgB,SAAS,QAAQ,CACnC,UAAS,QAAQ,MAAM;AAEzB,eAAa;AACX,cAAW,UAAU;AACrB,oBAAiB;AACf,QAAI,CAAC,WAAW,QACd,UAAS,SAAS,SAAS;MAE5B,EAAE;;IAEN,EAAE,CAAC;AAEN,QAAO,SAAS;;;;;AAalB,SAAgB,SACd,OACA,OACmB;CAEnB,MAAM,cAAc,kBAAkB;AACpC,SAAO;GACL,OAAO,MAAM,MAAM;GACnB,OAAO,MAAM,OAAO;GACrB;IACA,CAAC,OAAO,MAAM,CAAC;CAGlB,MAAM,YAAY,OAAO,aAAa,CAAC;CAkBvC,MAAM,WAAW,qBAhBC,aACf,kBAA8B;AAC7B,SAAO,MAAM,gBAAgB;GAC3B,MAAM,OAAO,aAAa;GAC1B,MAAM,UAAU,UAAU;AAG1B,OAAI,KAAK,UAAU,QAAQ,SAAS,KAAK,UAAU,QAAQ,OAAO;AAChE,cAAU,UAAU;AACpB,mBAAe;;IAEjB;IAEJ,CAAC,OAAO,YAAY,CACrB,QAIO,UAAU,eACV,UAAU,QACjB;CAED,MAAM,MAAM,aACT,UAAgB;EAGf,MAAM,UAAsB,GAAG,QAAQ,OAAO;AAC7C,QAA4D,IAAI,QAAQ;IAE3E,CAAC,OAAO,MAAM,CACf;AAED,QAAO;EACL,OAAO,SAAS;EAChB,OAAO,SAAS;EAChB;EACD"}
@@ -1,26 +1,22 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const react = require("react");
4
- const singleton = require("../singleton.cjs");
5
- const useInstance = require("./use-instance.cjs");
6
- const guards = require("./guards.cjs");
7
- const useSubscribeOnly = require("./use-subscribe-only.cjs");
1
+ const require_singleton = require("../singleton.cjs");
2
+ const require_use_instance = require("./use-instance.cjs");
3
+ const require_guards = require("./guards.cjs");
4
+ const require_use_subscribe_only = require("./use-subscribe-only.cjs");
5
+ let react = require("react");
6
+ //#region src/react/use-singleton.ts
8
7
  function useSingleton(Class, ...args) {
9
- const instance = singleton.singleton(Class, ...args);
10
- react.useEffect(() => {
11
- if (guards.isInitializable(instance)) {
12
- instance.init();
13
- }
14
- }, [instance]);
15
- if (guards.isSubscribable(instance)) {
16
- const state = useInstance.useInstance(instance);
17
- return [state, instance];
18
- }
19
- if (guards.isSubscribeOnly(instance)) {
20
- useSubscribeOnly.useSubscribeOnly(instance);
21
- return instance;
22
- }
23
- return instance;
8
+ const instance = require_singleton.singleton(Class, ...args);
9
+ (0, react.useEffect)(() => {
10
+ if (require_guards.isInitializable(instance)) instance.init();
11
+ }, [instance]);
12
+ if (require_guards.isSubscribable(instance)) return [require_use_instance.useInstance(instance), instance];
13
+ if (require_guards.isSubscribeOnly(instance)) {
14
+ require_use_subscribe_only.useSubscribeOnly(instance);
15
+ return instance;
16
+ }
17
+ return instance;
24
18
  }
19
+ //#endregion
25
20
  exports.useSingleton = useSingleton;
26
- //# sourceMappingURL=use-singleton.cjs.map
21
+
22
+ //# sourceMappingURL=use-singleton.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-singleton.cjs","sources":["../../src/react/use-singleton.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { singleton } from '../singleton';\nimport { useInstance } from './use-instance';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useSubscribeOnly } from './use-subscribe-only';\nimport type { StateOf } from './types';\n\n/**\n * Get singleton Subscribable with DEFAULT_STATE — no constructor args needed.\n */\nexport function useSingleton<\n T extends Subscribable<S> & Disposable, S = StateOf<T>,\n>(Class: (new (...args: any[]) => T) & { DEFAULT_STATE: unknown }): [S, T];\n\n/**\n * Get singleton Disposable with DEFAULT_STATE — no constructor args needed.\n */\nexport function useSingleton<T extends Disposable>(\n Class: (new (...args: any[]) => T) & { DEFAULT_STATE: unknown },\n): T;\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 if (isSubscribeOnly(instance)) {\n useSubscribeOnly(instance);\n return instance;\n }\n\n return instance;\n}\n"],"names":["singleton","useEffect","isInitializable","isSubscribable","useInstance","isSubscribeOnly","useSubscribeOnly"],"mappings":";;;;;;;AA6CO,SAAS,aACd,UACG,MACS;AACZ,QAAM,WAAWA,UAAAA,UAAU,OAAO,GAAG,IAAI;AAEzCC,QAAAA,UAAU,MAAM;AACd,QAAIC,OAAAA,gBAAgB,QAAQ,GAAG;AAC7B,eAAS,KAAA;AAAA,IACX;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAEb,MAAIC,OAAAA,eAAe,QAAQ,GAAG;AAC5B,UAAM,QAAQC,YAAAA,YAAY,QAAQ;AAClC,WAAO,CAAC,OAAO,QAAQ;AAAA,EACzB;AAEA,MAAIC,OAAAA,gBAAgB,QAAQ,GAAG;AAC7BC,qBAAAA,iBAAiB,QAAQ;AACzB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;;"}
1
+ {"version":3,"file":"use-singleton.cjs","names":[],"sources":["../../src/react/use-singleton.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { singleton } from '../singleton';\nimport { useInstance } from './use-instance';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useSubscribeOnly } from './use-subscribe-only';\nimport type { StateOf } from './types';\n\n/**\n * Get singleton Subscribable with DEFAULT_STATE — no constructor args needed.\n */\nexport function useSingleton<\n T extends Subscribable<S> & Disposable, S = StateOf<T>,\n>(Class: (new (...args: any[]) => T) & { DEFAULT_STATE: unknown }): [S, T];\n\n/**\n * Get singleton Disposable with DEFAULT_STATE — no constructor args needed.\n */\nexport function useSingleton<T extends Disposable>(\n Class: (new (...args: any[]) => T) & { DEFAULT_STATE: unknown },\n): T;\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 if (isSubscribeOnly(instance)) {\n useSubscribeOnly(instance);\n return instance;\n }\n\n return instance;\n}\n"],"mappings":";;;;;;AA6CA,SAAgB,aACd,OACA,GAAG,MACS;CACZ,MAAM,WAAW,kBAAA,UAAU,OAAO,GAAG,KAAK;AAE1C,EAAA,GAAA,MAAA,iBAAgB;AACd,MAAI,eAAA,gBAAgB,SAAS,CAC3B,UAAS,MAAM;IAEhB,CAAC,SAAS,CAAC;AAEd,KAAI,eAAA,eAAe,SAAS,CAE1B,QAAO,CADO,qBAAA,YAAY,SAAS,EACpB,SAAS;AAG1B,KAAI,eAAA,gBAAgB,SAAS,EAAE;AAC7B,6BAAA,iBAAiB,SAAS;AAC1B,SAAO;;AAGT,QAAO"}
@@ -1,26 +1,22 @@
1
- import { useEffect } from "react";
2
1
  import { singleton } from "../singleton.js";
3
2
  import { useInstance } from "./use-instance.js";
4
3
  import { isInitializable, isSubscribable, isSubscribeOnly } from "./guards.js";
5
4
  import { useSubscribeOnly } from "./use-subscribe-only.js";
5
+ import { useEffect } from "react";
6
+ //#region src/react/use-singleton.ts
6
7
  function useSingleton(Class, ...args) {
7
- const instance = singleton(Class, ...args);
8
- useEffect(() => {
9
- if (isInitializable(instance)) {
10
- instance.init();
11
- }
12
- }, [instance]);
13
- if (isSubscribable(instance)) {
14
- const state = useInstance(instance);
15
- return [state, instance];
16
- }
17
- if (isSubscribeOnly(instance)) {
18
- useSubscribeOnly(instance);
19
- return instance;
20
- }
21
- return instance;
8
+ const instance = singleton(Class, ...args);
9
+ useEffect(() => {
10
+ if (isInitializable(instance)) instance.init();
11
+ }, [instance]);
12
+ if (isSubscribable(instance)) return [useInstance(instance), instance];
13
+ if (isSubscribeOnly(instance)) {
14
+ useSubscribeOnly(instance);
15
+ return instance;
16
+ }
17
+ return instance;
22
18
  }
23
- export {
24
- useSingleton
25
- };
26
- //# sourceMappingURL=use-singleton.js.map
19
+ //#endregion
20
+ export { useSingleton };
21
+
22
+ //# sourceMappingURL=use-singleton.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-singleton.js","sources":["../../src/react/use-singleton.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { singleton } from '../singleton';\nimport { useInstance } from './use-instance';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useSubscribeOnly } from './use-subscribe-only';\nimport type { StateOf } from './types';\n\n/**\n * Get singleton Subscribable with DEFAULT_STATE — no constructor args needed.\n */\nexport function useSingleton<\n T extends Subscribable<S> & Disposable, S = StateOf<T>,\n>(Class: (new (...args: any[]) => T) & { DEFAULT_STATE: unknown }): [S, T];\n\n/**\n * Get singleton Disposable with DEFAULT_STATE — no constructor args needed.\n */\nexport function useSingleton<T extends Disposable>(\n Class: (new (...args: any[]) => T) & { DEFAULT_STATE: unknown },\n): T;\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 if (isSubscribeOnly(instance)) {\n useSubscribeOnly(instance);\n return instance;\n }\n\n return instance;\n}\n"],"names":[],"mappings":";;;;;AA6CO,SAAS,aACd,UACG,MACS;AACZ,QAAM,WAAW,UAAU,OAAO,GAAG,IAAI;AAEzC,YAAU,MAAM;AACd,QAAI,gBAAgB,QAAQ,GAAG;AAC7B,eAAS,KAAA;AAAA,IACX;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAEb,MAAI,eAAe,QAAQ,GAAG;AAC5B,UAAM,QAAQ,YAAY,QAAQ;AAClC,WAAO,CAAC,OAAO,QAAQ;AAAA,EACzB;AAEA,MAAI,gBAAgB,QAAQ,GAAG;AAC7B,qBAAiB,QAAQ;AACzB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;"}
1
+ {"version":3,"file":"use-singleton.js","names":[],"sources":["../../src/react/use-singleton.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { singleton } from '../singleton';\nimport { useInstance } from './use-instance';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useSubscribeOnly } from './use-subscribe-only';\nimport type { StateOf } from './types';\n\n/**\n * Get singleton Subscribable with DEFAULT_STATE — no constructor args needed.\n */\nexport function useSingleton<\n T extends Subscribable<S> & Disposable, S = StateOf<T>,\n>(Class: (new (...args: any[]) => T) & { DEFAULT_STATE: unknown }): [S, T];\n\n/**\n * Get singleton Disposable with DEFAULT_STATE — no constructor args needed.\n */\nexport function useSingleton<T extends Disposable>(\n Class: (new (...args: any[]) => T) & { DEFAULT_STATE: unknown },\n): T;\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 if (isSubscribeOnly(instance)) {\n useSubscribeOnly(instance);\n return instance;\n }\n\n return instance;\n}\n"],"mappings":";;;;;;AA6CA,SAAgB,aACd,OACA,GAAG,MACS;CACZ,MAAM,WAAW,UAAU,OAAO,GAAG,KAAK;AAE1C,iBAAgB;AACd,MAAI,gBAAgB,SAAS,CAC3B,UAAS,MAAM;IAEhB,CAAC,SAAS,CAAC;AAEd,KAAI,eAAe,SAAS,CAE1B,QAAO,CADO,YAAY,SAAS,EACpB,SAAS;AAG1B,KAAI,gBAAgB,SAAS,EAAE;AAC7B,mBAAiB,SAAS;AAC1B,SAAO;;AAGT,QAAO"}
@@ -1,25 +1,31 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const react = require("react");
4
- const SERVER_SNAPSHOT = () => 0;
1
+ let react = require("react");
2
+ //#region src/react/use-subscribe-only.ts
3
+ var SERVER_SNAPSHOT = () => 0;
4
+ /**
5
+ * Subscribe to a notification-only object (has subscribe() but no state).
6
+ * Triggers React re-renders via version counter when the target notifies.
7
+ * @internal
8
+ */
5
9
  function useSubscribeOnly(target) {
6
- const ref = react.useRef(null);
7
- if (!ref.current || ref.current.target !== target) {
8
- const version = { current: ref.current?.version ?? 0 };
9
- ref.current = {
10
- target,
11
- version: version.current,
12
- subscribe: (onStoreChange) => {
13
- return target.subscribe(() => {
14
- version.current++;
15
- ref.current.version = version.current;
16
- onStoreChange();
17
- });
18
- },
19
- getSnapshot: () => version.current
20
- };
21
- }
22
- react.useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);
10
+ const ref = (0, react.useRef)(null);
11
+ if (!ref.current || ref.current.target !== target) {
12
+ const version = { current: ref.current?.version ?? 0 };
13
+ ref.current = {
14
+ target,
15
+ version: version.current,
16
+ subscribe: (onStoreChange) => {
17
+ return target.subscribe(() => {
18
+ version.current++;
19
+ ref.current.version = version.current;
20
+ onStoreChange();
21
+ });
22
+ },
23
+ getSnapshot: () => version.current
24
+ };
25
+ }
26
+ (0, react.useSyncExternalStore)(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);
23
27
  }
28
+ //#endregion
24
29
  exports.useSubscribeOnly = useSubscribeOnly;
25
- //# sourceMappingURL=use-subscribe-only.cjs.map
30
+
31
+ //# sourceMappingURL=use-subscribe-only.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-subscribe-only.cjs","sources":["../../src/react/use-subscribe-only.ts"],"sourcesContent":["import { useSyncExternalStore, useRef } from 'react';\n\ninterface SubscribeOnlyRef {\n target: { subscribe(cb: () => void): () => void };\n version: number;\n subscribe: (onStoreChange: () => void) => () => void;\n getSnapshot: () => number;\n}\n\nconst SERVER_SNAPSHOT = () => 0;\n\n/**\n * Subscribe to a notification-only object (has subscribe() but no state).\n * Triggers React re-renders via version counter when the target notifies.\n * @internal\n */\nexport function useSubscribeOnly(\n target: { subscribe(cb: () => void): () => void },\n): void {\n const ref = useRef<SubscribeOnlyRef | null>(null);\n\n if (!ref.current || ref.current.target !== target) {\n const version = { current: ref.current?.version ?? 0 };\n ref.current = {\n target,\n version: version.current,\n subscribe: (onStoreChange: () => void) => {\n return target.subscribe(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n },\n getSnapshot: () => version.current,\n };\n }\n\n useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);\n}\n"],"names":["useRef","useSyncExternalStore"],"mappings":";;;AASA,MAAM,kBAAkB,MAAM;AAOvB,SAAS,iBACd,QACM;AACN,QAAM,MAAMA,MAAAA,OAAgC,IAAI;AAEhD,MAAI,CAAC,IAAI,WAAW,IAAI,QAAQ,WAAW,QAAQ;AACjD,UAAM,UAAU,EAAE,SAAS,IAAI,SAAS,WAAW,EAAA;AACnD,QAAI,UAAU;AAAA,MACZ;AAAA,MACA,SAAS,QAAQ;AAAA,MACjB,WAAW,CAAC,kBAA8B;AACxC,eAAO,OAAO,UAAU,MAAM;AAC5B,kBAAQ;AACR,cAAI,QAAS,UAAU,QAAQ;AAC/B,wBAAA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MACA,aAAa,MAAM,QAAQ;AAAA,IAAA;AAAA,EAE/B;AAEAC,QAAAA,qBAAqB,IAAI,QAAQ,WAAW,IAAI,QAAQ,aAAa,eAAe;AACtF;;"}
1
+ {"version":3,"file":"use-subscribe-only.cjs","names":[],"sources":["../../src/react/use-subscribe-only.ts"],"sourcesContent":["import { useSyncExternalStore, useRef } from 'react';\n\ninterface SubscribeOnlyRef {\n target: { subscribe(cb: () => void): () => void };\n version: number;\n subscribe: (onStoreChange: () => void) => () => void;\n getSnapshot: () => number;\n}\n\nconst SERVER_SNAPSHOT = () => 0;\n\n/**\n * Subscribe to a notification-only object (has subscribe() but no state).\n * Triggers React re-renders via version counter when the target notifies.\n * @internal\n */\nexport function useSubscribeOnly(\n target: { subscribe(cb: () => void): () => void },\n): void {\n const ref = useRef<SubscribeOnlyRef | null>(null);\n\n if (!ref.current || ref.current.target !== target) {\n const version = { current: ref.current?.version ?? 0 };\n ref.current = {\n target,\n version: version.current,\n subscribe: (onStoreChange: () => void) => {\n return target.subscribe(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n },\n getSnapshot: () => version.current,\n };\n }\n\n useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);\n}\n"],"mappings":";;AASA,IAAM,wBAAwB;;;;;;AAO9B,SAAgB,iBACd,QACM;CACN,MAAM,OAAA,GAAA,MAAA,QAAsC,KAAK;AAEjD,KAAI,CAAC,IAAI,WAAW,IAAI,QAAQ,WAAW,QAAQ;EACjD,MAAM,UAAU,EAAE,SAAS,IAAI,SAAS,WAAW,GAAG;AACtD,MAAI,UAAU;GACZ;GACA,SAAS,QAAQ;GACjB,YAAY,kBAA8B;AACxC,WAAO,OAAO,gBAAgB;AAC5B,aAAQ;AACR,SAAI,QAAS,UAAU,QAAQ;AAC/B,oBAAe;MACf;;GAEJ,mBAAmB,QAAQ;GAC5B;;AAGH,EAAA,GAAA,MAAA,sBAAqB,IAAI,QAAQ,WAAW,IAAI,QAAQ,aAAa,gBAAgB"}
@@ -1,25 +1,31 @@
1
1
  import { useRef, useSyncExternalStore } from "react";
2
- const SERVER_SNAPSHOT = () => 0;
2
+ //#region src/react/use-subscribe-only.ts
3
+ var SERVER_SNAPSHOT = () => 0;
4
+ /**
5
+ * Subscribe to a notification-only object (has subscribe() but no state).
6
+ * Triggers React re-renders via version counter when the target notifies.
7
+ * @internal
8
+ */
3
9
  function useSubscribeOnly(target) {
4
- const ref = useRef(null);
5
- if (!ref.current || ref.current.target !== target) {
6
- const version = { current: ref.current?.version ?? 0 };
7
- ref.current = {
8
- target,
9
- version: version.current,
10
- subscribe: (onStoreChange) => {
11
- return target.subscribe(() => {
12
- version.current++;
13
- ref.current.version = version.current;
14
- onStoreChange();
15
- });
16
- },
17
- getSnapshot: () => version.current
18
- };
19
- }
20
- useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);
10
+ const ref = useRef(null);
11
+ if (!ref.current || ref.current.target !== target) {
12
+ const version = { current: ref.current?.version ?? 0 };
13
+ ref.current = {
14
+ target,
15
+ version: version.current,
16
+ subscribe: (onStoreChange) => {
17
+ return target.subscribe(() => {
18
+ version.current++;
19
+ ref.current.version = version.current;
20
+ onStoreChange();
21
+ });
22
+ },
23
+ getSnapshot: () => version.current
24
+ };
25
+ }
26
+ useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);
21
27
  }
22
- export {
23
- useSubscribeOnly
24
- };
25
- //# sourceMappingURL=use-subscribe-only.js.map
28
+ //#endregion
29
+ export { useSubscribeOnly };
30
+
31
+ //# sourceMappingURL=use-subscribe-only.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-subscribe-only.js","sources":["../../src/react/use-subscribe-only.ts"],"sourcesContent":["import { useSyncExternalStore, useRef } from 'react';\n\ninterface SubscribeOnlyRef {\n target: { subscribe(cb: () => void): () => void };\n version: number;\n subscribe: (onStoreChange: () => void) => () => void;\n getSnapshot: () => number;\n}\n\nconst SERVER_SNAPSHOT = () => 0;\n\n/**\n * Subscribe to a notification-only object (has subscribe() but no state).\n * Triggers React re-renders via version counter when the target notifies.\n * @internal\n */\nexport function useSubscribeOnly(\n target: { subscribe(cb: () => void): () => void },\n): void {\n const ref = useRef<SubscribeOnlyRef | null>(null);\n\n if (!ref.current || ref.current.target !== target) {\n const version = { current: ref.current?.version ?? 0 };\n ref.current = {\n target,\n version: version.current,\n subscribe: (onStoreChange: () => void) => {\n return target.subscribe(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n },\n getSnapshot: () => version.current,\n };\n }\n\n useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);\n}\n"],"names":[],"mappings":";AASA,MAAM,kBAAkB,MAAM;AAOvB,SAAS,iBACd,QACM;AACN,QAAM,MAAM,OAAgC,IAAI;AAEhD,MAAI,CAAC,IAAI,WAAW,IAAI,QAAQ,WAAW,QAAQ;AACjD,UAAM,UAAU,EAAE,SAAS,IAAI,SAAS,WAAW,EAAA;AACnD,QAAI,UAAU;AAAA,MACZ;AAAA,MACA,SAAS,QAAQ;AAAA,MACjB,WAAW,CAAC,kBAA8B;AACxC,eAAO,OAAO,UAAU,MAAM;AAC5B,kBAAQ;AACR,cAAI,QAAS,UAAU,QAAQ;AAC/B,wBAAA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MACA,aAAa,MAAM,QAAQ;AAAA,IAAA;AAAA,EAE/B;AAEA,uBAAqB,IAAI,QAAQ,WAAW,IAAI,QAAQ,aAAa,eAAe;AACtF;"}
1
+ {"version":3,"file":"use-subscribe-only.js","names":[],"sources":["../../src/react/use-subscribe-only.ts"],"sourcesContent":["import { useSyncExternalStore, useRef } from 'react';\n\ninterface SubscribeOnlyRef {\n target: { subscribe(cb: () => void): () => void };\n version: number;\n subscribe: (onStoreChange: () => void) => () => void;\n getSnapshot: () => number;\n}\n\nconst SERVER_SNAPSHOT = () => 0;\n\n/**\n * Subscribe to a notification-only object (has subscribe() but no state).\n * Triggers React re-renders via version counter when the target notifies.\n * @internal\n */\nexport function useSubscribeOnly(\n target: { subscribe(cb: () => void): () => void },\n): void {\n const ref = useRef<SubscribeOnlyRef | null>(null);\n\n if (!ref.current || ref.current.target !== target) {\n const version = { current: ref.current?.version ?? 0 };\n ref.current = {\n target,\n version: version.current,\n subscribe: (onStoreChange: () => void) => {\n return target.subscribe(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n },\n getSnapshot: () => version.current,\n };\n }\n\n useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);\n}\n"],"mappings":";;AASA,IAAM,wBAAwB;;;;;;AAO9B,SAAgB,iBACd,QACM;CACN,MAAM,MAAM,OAAgC,KAAK;AAEjD,KAAI,CAAC,IAAI,WAAW,IAAI,QAAQ,WAAW,QAAQ;EACjD,MAAM,UAAU,EAAE,SAAS,IAAI,SAAS,WAAW,GAAG;AACtD,MAAI,UAAU;GACZ;GACA,SAAS,QAAQ;GACjB,YAAY,kBAA8B;AACxC,WAAO,OAAO,gBAAgB;AAC5B,aAAQ;AACR,SAAI,QAAS,UAAU,QAAQ;AAC/B,oBAAe;MACf;;GAEJ,mBAAmB,QAAQ;GAC5B;;AAGH,sBAAqB,IAAI,QAAQ,WAAW,IAAI,QAAQ,aAAa,gBAAgB"}
@@ -1,22 +1,23 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const react = require("react");
4
- const singleton = require("../singleton.cjs");
1
+ const require_singleton = require("../singleton.cjs");
2
+ let react = require("react");
3
+ //#region src/react/use-teardown.ts
4
+ /**
5
+ * Teardown singleton class(es) on unmount.
6
+ * Uses deferred disposal to handle StrictMode's double-mount cycle.
7
+ */
5
8
  function useTeardown(...Classes) {
6
- const mountedRef = react.useRef(false);
7
- react.useEffect(() => {
8
- mountedRef.current = true;
9
- return () => {
10
- mountedRef.current = false;
11
- setTimeout(() => {
12
- if (!mountedRef.current) {
13
- for (const Class of Classes) {
14
- singleton.teardown(Class);
15
- }
16
- }
17
- }, 0);
18
- };
19
- }, []);
9
+ const mountedRef = (0, react.useRef)(false);
10
+ (0, react.useEffect)(() => {
11
+ mountedRef.current = true;
12
+ return () => {
13
+ mountedRef.current = false;
14
+ setTimeout(() => {
15
+ if (!mountedRef.current) for (const Class of Classes) require_singleton.teardown(Class);
16
+ }, 0);
17
+ };
18
+ }, []);
20
19
  }
20
+ //#endregion
21
21
  exports.useTeardown = useTeardown;
22
- //# sourceMappingURL=use-teardown.cjs.map
22
+
23
+ //# sourceMappingURL=use-teardown.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-teardown.cjs","sources":["../../src/react/use-teardown.ts"],"sourcesContent":["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"],"names":["useRef","useEffect","teardown"],"mappings":";;;;AAQO,SAAS,eACX,SACG;AACN,QAAM,aAAaA,MAAAA,OAAO,KAAK;AAE/BC,QAAAA,UAAU,MAAM;AACd,eAAW,UAAU;AACrB,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,qBAAW,SAAS,SAAS;AAC3BC,sBAAAA,SAAS,KAAK;AAAA,UAChB;AAAA,QACF;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE;AACP;;"}
1
+ {"version":3,"file":"use-teardown.cjs","names":[],"sources":["../../src/react/use-teardown.ts"],"sourcesContent":["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"],"mappings":";;;;;;;AAQA,SAAgB,YACd,GAAG,SACG;CACN,MAAM,cAAA,GAAA,MAAA,QAAoB,MAAM;AAEhC,EAAA,GAAA,MAAA,iBAAgB;AACd,aAAW,UAAU;AACrB,eAAa;AACX,cAAW,UAAU;AACrB,oBAAiB;AACf,QAAI,CAAC,WAAW,QACd,MAAK,MAAM,SAAS,QAClB,mBAAA,SAAS,MAAM;MAGlB,EAAE;;IAEN,EAAE,CAAC"}