mvc-kit 2.12.5 → 2.13.1

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 (382) hide show
  1. package/BEST_PRACTICES.md +1390 -0
  2. package/agent-config/bin/postinstall.mjs +4 -3
  3. package/agent-config/bin/setup.mjs +5 -1
  4. package/agent-config/claude-code/agents/mvc-kit-architect.md +16 -8
  5. package/agent-config/claude-code/skills/guide/SKILL.md +29 -7
  6. package/agent-config/claude-code/skills/guide/patterns.md +12 -0
  7. package/agent-config/claude-code/skills/guide/recipes.md +510 -0
  8. package/agent-config/claude-code/skills/guide/testing.md +297 -0
  9. package/agent-config/claude-code/skills/review/SKILL.md +3 -13
  10. package/agent-config/claude-code/skills/review/checklist.md +30 -5
  11. package/agent-config/claude-code/skills/scaffold/SKILL.md +4 -13
  12. package/agent-config/lib/install-claude.mjs +90 -25
  13. package/dist/Channel.cjs +276 -300
  14. package/dist/Channel.cjs.map +1 -1
  15. package/dist/Channel.js +275 -299
  16. package/dist/Channel.js.map +1 -1
  17. package/dist/Collection.cjs +424 -504
  18. package/dist/Collection.cjs.map +1 -1
  19. package/dist/Collection.js +423 -503
  20. package/dist/Collection.js.map +1 -1
  21. package/dist/Controller.cjs +70 -67
  22. package/dist/Controller.cjs.map +1 -1
  23. package/dist/Controller.js +69 -66
  24. package/dist/Controller.js.map +1 -1
  25. package/dist/EventBus.cjs +77 -88
  26. package/dist/EventBus.cjs.map +1 -1
  27. package/dist/EventBus.js +76 -87
  28. package/dist/EventBus.js.map +1 -1
  29. package/dist/Feed.cjs +81 -77
  30. package/dist/Feed.cjs.map +1 -1
  31. package/dist/Feed.js +80 -76
  32. package/dist/Feed.js.map +1 -1
  33. package/dist/Model.cjs +181 -207
  34. package/dist/Model.cjs.map +1 -1
  35. package/dist/Model.js +179 -205
  36. package/dist/Model.js.map +1 -1
  37. package/dist/Pagination.cjs +75 -73
  38. package/dist/Pagination.cjs.map +1 -1
  39. package/dist/Pagination.js +74 -72
  40. package/dist/Pagination.js.map +1 -1
  41. package/dist/Pending.cjs +255 -287
  42. package/dist/Pending.cjs.map +1 -1
  43. package/dist/Pending.js +253 -285
  44. package/dist/Pending.js.map +1 -1
  45. package/dist/PersistentCollection.cjs +242 -285
  46. package/dist/PersistentCollection.cjs.map +1 -1
  47. package/dist/PersistentCollection.js +241 -284
  48. package/dist/PersistentCollection.js.map +1 -1
  49. package/dist/Resource.cjs +166 -174
  50. package/dist/Resource.cjs.map +1 -1
  51. package/dist/Resource.js +164 -172
  52. package/dist/Resource.js.map +1 -1
  53. package/dist/Selection.cjs +84 -94
  54. package/dist/Selection.cjs.map +1 -1
  55. package/dist/Selection.js +83 -93
  56. package/dist/Selection.js.map +1 -1
  57. package/dist/Service.cjs +54 -55
  58. package/dist/Service.cjs.map +1 -1
  59. package/dist/Service.js +53 -54
  60. package/dist/Service.js.map +1 -1
  61. package/dist/Sorting.cjs +102 -101
  62. package/dist/Sorting.cjs.map +1 -1
  63. package/dist/Sorting.js +102 -101
  64. package/dist/Sorting.js.map +1 -1
  65. package/dist/Trackable.cjs +112 -80
  66. package/dist/Trackable.cjs.map +1 -1
  67. package/dist/Trackable.js +111 -79
  68. package/dist/Trackable.js.map +1 -1
  69. package/dist/ViewModel.cjs +528 -576
  70. package/dist/ViewModel.cjs.map +1 -1
  71. package/dist/ViewModel.js +525 -573
  72. package/dist/ViewModel.js.map +1 -1
  73. package/dist/bindPublicMethods.cjs +43 -24
  74. package/dist/bindPublicMethods.cjs.map +1 -1
  75. package/dist/bindPublicMethods.js +43 -24
  76. package/dist/bindPublicMethods.js.map +1 -1
  77. package/dist/errors.cjs +67 -68
  78. package/dist/errors.cjs.map +1 -1
  79. package/dist/errors.js +68 -71
  80. package/dist/errors.js.map +1 -1
  81. package/dist/mvc-kit.cjs +44 -46
  82. package/dist/mvc-kit.js +5 -32
  83. package/dist/produceDraft.cjs +105 -95
  84. package/dist/produceDraft.cjs.map +1 -1
  85. package/dist/produceDraft.js +106 -97
  86. package/dist/produceDraft.js.map +1 -1
  87. package/dist/react/components/CardList.cjs +30 -40
  88. package/dist/react/components/CardList.cjs.map +1 -1
  89. package/dist/react/components/CardList.js +31 -41
  90. package/dist/react/components/CardList.js.map +1 -1
  91. package/dist/react/components/DataTable.cjs +146 -169
  92. package/dist/react/components/DataTable.cjs.map +1 -1
  93. package/dist/react/components/DataTable.js +147 -170
  94. package/dist/react/components/DataTable.js.map +1 -1
  95. package/dist/react/components/InfiniteScroll.cjs +51 -42
  96. package/dist/react/components/InfiniteScroll.cjs.map +1 -1
  97. package/dist/react/components/InfiniteScroll.js +52 -43
  98. package/dist/react/components/InfiniteScroll.js.map +1 -1
  99. package/dist/react/components/types.cjs +10 -6
  100. package/dist/react/components/types.cjs.map +1 -1
  101. package/dist/react/components/types.js +11 -9
  102. package/dist/react/components/types.js.map +1 -1
  103. package/dist/react/guards.cjs +10 -6
  104. package/dist/react/guards.cjs.map +1 -1
  105. package/dist/react/guards.js +11 -9
  106. package/dist/react/guards.js.map +1 -1
  107. package/dist/react/provider.cjs +23 -20
  108. package/dist/react/provider.cjs.map +1 -1
  109. package/dist/react/provider.js +23 -21
  110. package/dist/react/provider.js.map +1 -1
  111. package/dist/react/use-event-bus.cjs +24 -20
  112. package/dist/react/use-event-bus.cjs.map +1 -1
  113. package/dist/react/use-event-bus.js +24 -21
  114. package/dist/react/use-event-bus.js.map +1 -1
  115. package/dist/react/use-instance.cjs +43 -36
  116. package/dist/react/use-instance.cjs.map +1 -1
  117. package/dist/react/use-instance.js +43 -36
  118. package/dist/react/use-instance.js.map +1 -1
  119. package/dist/react/use-local.cjs +48 -64
  120. package/dist/react/use-local.cjs.map +1 -1
  121. package/dist/react/use-local.js +47 -63
  122. package/dist/react/use-local.js.map +1 -1
  123. package/dist/react/use-model.cjs +84 -98
  124. package/dist/react/use-model.cjs.map +1 -1
  125. package/dist/react/use-model.js +84 -100
  126. package/dist/react/use-model.js.map +1 -1
  127. package/dist/react/use-singleton.cjs +19 -23
  128. package/dist/react/use-singleton.cjs.map +1 -1
  129. package/dist/react/use-singleton.js +16 -20
  130. package/dist/react/use-singleton.js.map +1 -1
  131. package/dist/react/use-subscribe-only.cjs +28 -22
  132. package/dist/react/use-subscribe-only.cjs.map +1 -1
  133. package/dist/react/use-subscribe-only.js +28 -22
  134. package/dist/react/use-subscribe-only.js.map +1 -1
  135. package/dist/react/use-teardown.cjs +20 -19
  136. package/dist/react/use-teardown.cjs.map +1 -1
  137. package/dist/react/use-teardown.js +20 -19
  138. package/dist/react/use-teardown.js.map +1 -1
  139. package/dist/react-native/NativeCollection.cjs +98 -78
  140. package/dist/react-native/NativeCollection.cjs.map +1 -1
  141. package/dist/react-native/NativeCollection.js +97 -77
  142. package/dist/react-native/NativeCollection.js.map +1 -1
  143. package/dist/react-native.cjs +2 -4
  144. package/dist/react-native.js +1 -4
  145. package/dist/react.cjs +24 -26
  146. package/dist/react.js +1 -17
  147. package/dist/singleton.cjs +28 -22
  148. package/dist/singleton.cjs.map +1 -1
  149. package/dist/singleton.js +29 -26
  150. package/dist/singleton.js.map +1 -1
  151. package/dist/walkPrototypeChain.cjs +20 -12
  152. package/dist/walkPrototypeChain.cjs.map +1 -1
  153. package/dist/walkPrototypeChain.js +21 -13
  154. package/dist/walkPrototypeChain.js.map +1 -1
  155. package/dist/web/IndexedDBCollection.cjs +53 -36
  156. package/dist/web/IndexedDBCollection.cjs.map +1 -1
  157. package/dist/web/IndexedDBCollection.js +52 -35
  158. package/dist/web/IndexedDBCollection.js.map +1 -1
  159. package/dist/web/WebStorageCollection.cjs +82 -84
  160. package/dist/web/WebStorageCollection.cjs.map +1 -1
  161. package/dist/web/WebStorageCollection.js +81 -83
  162. package/dist/web/WebStorageCollection.js.map +1 -1
  163. package/dist/web/idb.cjs +107 -99
  164. package/dist/web/idb.cjs.map +1 -1
  165. package/dist/web/idb.js +108 -105
  166. package/dist/web/idb.js.map +1 -1
  167. package/dist/web.cjs +4 -6
  168. package/dist/web.js +1 -5
  169. package/dist/wrapAsyncMethods.cjs +141 -168
  170. package/dist/wrapAsyncMethods.cjs.map +1 -1
  171. package/dist/wrapAsyncMethods.js +141 -168
  172. package/dist/wrapAsyncMethods.js.map +1 -1
  173. package/examples/primitive/channel.ts +109 -0
  174. package/examples/primitive/collection.ts +118 -0
  175. package/examples/primitive/controller.ts +118 -0
  176. package/examples/primitive/counter.ts +108 -0
  177. package/examples/primitive/env.d.ts +1 -0
  178. package/examples/primitive/eventbus.ts +77 -0
  179. package/examples/primitive/feed.ts +162 -0
  180. package/examples/primitive/model.ts +82 -0
  181. package/examples/primitive/pagination.ts +91 -0
  182. package/examples/primitive/pending.ts +189 -0
  183. package/examples/primitive/persistent-collection.ts +116 -0
  184. package/examples/primitive/resource.ts +114 -0
  185. package/examples/primitive/selection.ts +96 -0
  186. package/examples/primitive/sorting.ts +112 -0
  187. package/examples/primitive/timer.ts +58 -0
  188. package/examples/primitive/trackable.ts +225 -0
  189. package/examples/primitive/tsconfig.json +20 -0
  190. package/examples/primitive/viewmodel-service.ts +161 -0
  191. package/examples/react/AuthExample/index.html +12 -0
  192. package/examples/react/AuthExample/src/App.tsx +29 -0
  193. package/examples/react/AuthExample/src/components/AdminPage.tsx +51 -0
  194. package/examples/react/AuthExample/src/components/AppHeader.tsx +32 -0
  195. package/examples/react/AuthExample/src/components/AuthGuard.tsx +50 -0
  196. package/examples/react/AuthExample/src/components/AuthScreen.tsx +181 -0
  197. package/examples/react/AuthExample/src/components/DashboardPage.tsx +41 -0
  198. package/examples/react/AuthExample/src/components/ProfilePage.tsx +44 -0
  199. package/examples/react/AuthExample/src/components/Toast.tsx +41 -0
  200. package/examples/react/AuthExample/src/env.d.ts +10 -0
  201. package/examples/react/AuthExample/src/events/AppEventBus.ts +7 -0
  202. package/examples/react/AuthExample/src/main.tsx +10 -0
  203. package/examples/react/AuthExample/src/mock/api.ts +78 -0
  204. package/examples/react/AuthExample/src/models/LoginFormModel.ts +19 -0
  205. package/examples/react/AuthExample/src/models/RegisterFormModel.ts +25 -0
  206. package/examples/react/AuthExample/src/services/AuthService.ts +21 -0
  207. package/examples/react/AuthExample/src/styles.css +445 -0
  208. package/examples/react/AuthExample/src/types/auth.ts +12 -0
  209. package/examples/react/AuthExample/src/viewmodels/AuthViewModel.ts +111 -0
  210. package/examples/react/AuthExample/tsconfig.json +22 -0
  211. package/examples/react/AuthExample/vite.config.ts +18 -0
  212. package/examples/react/ComplexApp/index.html +12 -0
  213. package/examples/react/ComplexApp/src/App.tsx +17 -0
  214. package/examples/react/ComplexApp/src/channels/ActivityChannel.ts +24 -0
  215. package/examples/react/ComplexApp/src/channels/DashboardChannel.ts +26 -0
  216. package/examples/react/ComplexApp/src/channels/ErrorsChannel.ts +5 -0
  217. package/examples/react/ComplexApp/src/channels/LatencyChannel.ts +5 -0
  218. package/examples/react/ComplexApp/src/channels/OrdersChannel.ts +5 -0
  219. package/examples/react/ComplexApp/src/channels/RevenueChannel.ts +5 -0
  220. package/examples/react/ComplexApp/src/channels/TrafficChannel.ts +5 -0
  221. package/examples/react/ComplexApp/src/channels/UsersMetricChannel.ts +5 -0
  222. package/examples/react/ComplexApp/src/collections/DashboardCollection.ts +6 -0
  223. package/examples/react/ComplexApp/src/collections/ErrorsCollection.ts +3 -0
  224. package/examples/react/ComplexApp/src/collections/LatencyCollection.ts +3 -0
  225. package/examples/react/ComplexApp/src/collections/OrdersCollection.ts +3 -0
  226. package/examples/react/ComplexApp/src/collections/RevenueCollection.ts +3 -0
  227. package/examples/react/ComplexApp/src/collections/TrafficCollection.ts +3 -0
  228. package/examples/react/ComplexApp/src/collections/UsersMetricCollection.ts +3 -0
  229. package/examples/react/ComplexApp/src/components/activity/ActivityFeed.tsx +31 -0
  230. package/examples/react/ComplexApp/src/components/activity/ActivityItemRow.tsx +35 -0
  231. package/examples/react/ComplexApp/src/components/dashboard/DashboardCard.tsx +37 -0
  232. package/examples/react/ComplexApp/src/components/dashboard/DashboardPage.tsx +34 -0
  233. package/examples/react/ComplexApp/src/components/layout/Navbar.tsx +32 -0
  234. package/examples/react/ComplexApp/src/components/layout/SocialFeedPanel.tsx +57 -0
  235. package/examples/react/ComplexApp/src/components/shared/Spinner.tsx +3 -0
  236. package/examples/react/ComplexApp/src/components/shared/StatusIndicator.tsx +13 -0
  237. package/examples/react/ComplexApp/src/components/shared/Toast.tsx +40 -0
  238. package/examples/react/ComplexApp/src/env.d.ts +10 -0
  239. package/examples/react/ComplexApp/src/events/AppEventBus.ts +7 -0
  240. package/examples/react/ComplexApp/src/main.tsx +10 -0
  241. package/examples/react/ComplexApp/src/mock-remote/MockWebSocket.ts +38 -0
  242. package/examples/react/ComplexApp/src/mock-remote/activity-api.ts +48 -0
  243. package/examples/react/ComplexApp/src/mock-remote/dashboard-generators.ts +45 -0
  244. package/examples/react/ComplexApp/src/mock-remote/delay.ts +18 -0
  245. package/examples/react/ComplexApp/src/mock-remote/social-api.ts +55 -0
  246. package/examples/react/ComplexApp/src/resources/ActivityResource.ts +12 -0
  247. package/examples/react/ComplexApp/src/resources/SocialFeedResource.ts +17 -0
  248. package/examples/react/ComplexApp/src/styles.css +463 -0
  249. package/examples/react/ComplexApp/src/types/activity.ts +8 -0
  250. package/examples/react/ComplexApp/src/types/dashboard.ts +5 -0
  251. package/examples/react/ComplexApp/src/types/social.ts +8 -0
  252. package/examples/react/ComplexApp/src/types/users.ts +6 -0
  253. package/examples/react/ComplexApp/src/viewmodels/ActivityFeedViewModel.ts +68 -0
  254. package/examples/react/ComplexApp/src/viewmodels/AppStateViewModel.ts +26 -0
  255. package/examples/react/ComplexApp/src/viewmodels/DashboardCardViewModel.ts +69 -0
  256. package/examples/react/ComplexApp/src/viewmodels/ErrorsCardViewModel.ts +9 -0
  257. package/examples/react/ComplexApp/src/viewmodels/LatencyCardViewModel.ts +9 -0
  258. package/examples/react/ComplexApp/src/viewmodels/OrdersCardViewModel.ts +9 -0
  259. package/examples/react/ComplexApp/src/viewmodels/RevenueCardViewModel.ts +9 -0
  260. package/examples/react/ComplexApp/src/viewmodels/SocialFeedViewModel.ts +39 -0
  261. package/examples/react/ComplexApp/src/viewmodels/TrafficCardViewModel.ts +9 -0
  262. package/examples/react/ComplexApp/src/viewmodels/UsersMetricCardViewModel.ts +9 -0
  263. package/examples/react/ComplexApp/tsconfig.json +22 -0
  264. package/examples/react/ComplexApp/vite.config.ts +18 -0
  265. package/examples/react/FullApp/index.html +12 -0
  266. package/examples/react/FullApp/src/App.tsx +28 -0
  267. package/examples/react/FullApp/src/collections/ConversationsCollection.ts +4 -0
  268. package/examples/react/FullApp/src/collections/LocationsCollection.ts +4 -0
  269. package/examples/react/FullApp/src/components/auth/LoginPage.tsx +80 -0
  270. package/examples/react/FullApp/src/components/dashboard/DashboardPage.tsx +29 -0
  271. package/examples/react/FullApp/src/components/dashboard/RecentActivityCard.tsx +35 -0
  272. package/examples/react/FullApp/src/components/dashboard/StatsCard.tsx +19 -0
  273. package/examples/react/FullApp/src/components/layout/AppShell.tsx +31 -0
  274. package/examples/react/FullApp/src/components/layout/Header.tsx +25 -0
  275. package/examples/react/FullApp/src/components/layout/Sidebar.tsx +29 -0
  276. package/examples/react/FullApp/src/components/locations/LocationFilters.tsx +60 -0
  277. package/examples/react/FullApp/src/components/locations/LocationForm.tsx +112 -0
  278. package/examples/react/FullApp/src/components/locations/LocationProfilePage.tsx +81 -0
  279. package/examples/react/FullApp/src/components/locations/LocationsPage.tsx +127 -0
  280. package/examples/react/FullApp/src/components/messaging/ConversationList.tsx +59 -0
  281. package/examples/react/FullApp/src/components/messaging/MessageBubble.tsx +22 -0
  282. package/examples/react/FullApp/src/components/messaging/MessageThread.tsx +100 -0
  283. package/examples/react/FullApp/src/components/messaging/MessagingPage.tsx +52 -0
  284. package/examples/react/FullApp/src/components/shared/ErrorBanner.tsx +3 -0
  285. package/examples/react/FullApp/src/components/shared/Spinner.tsx +7 -0
  286. package/examples/react/FullApp/src/components/shared/Toast.tsx +41 -0
  287. package/examples/react/FullApp/src/components/users/UserFilters.tsx +59 -0
  288. package/examples/react/FullApp/src/components/users/UsersPage.tsx +80 -0
  289. package/examples/react/FullApp/src/components/users/UsersTable.tsx +52 -0
  290. package/examples/react/FullApp/src/env.d.ts +10 -0
  291. package/examples/react/FullApp/src/events/AppEventBus.ts +7 -0
  292. package/examples/react/FullApp/src/main.tsx +10 -0
  293. package/examples/react/FullApp/src/mock/delay.ts +21 -0
  294. package/examples/react/FullApp/src/mock/locations.ts +76 -0
  295. package/examples/react/FullApp/src/mock/messages.ts +237 -0
  296. package/examples/react/FullApp/src/mock/users.ts +84 -0
  297. package/examples/react/FullApp/src/models/LocationFormModel.ts +31 -0
  298. package/examples/react/FullApp/src/models/LoginFormModel.ts +19 -0
  299. package/examples/react/FullApp/src/resources/UsersResource.ts +12 -0
  300. package/examples/react/FullApp/src/services/AuthService.ts +18 -0
  301. package/examples/react/FullApp/src/services/LocationService.ts +23 -0
  302. package/examples/react/FullApp/src/services/MessageService.ts +65 -0
  303. package/examples/react/FullApp/src/services/UserService.ts +23 -0
  304. package/examples/react/FullApp/src/styles.css +767 -0
  305. package/examples/react/FullApp/src/types/conversation.ts +7 -0
  306. package/examples/react/FullApp/src/types/location.ts +12 -0
  307. package/examples/react/FullApp/src/types/message.ts +7 -0
  308. package/examples/react/FullApp/src/types/user.ts +10 -0
  309. package/examples/react/FullApp/src/viewmodels/AuthViewModel.ts +51 -0
  310. package/examples/react/FullApp/src/viewmodels/ConversationsViewModel.ts +89 -0
  311. package/examples/react/FullApp/src/viewmodels/DashboardViewModel.ts +56 -0
  312. package/examples/react/FullApp/src/viewmodels/LocationProfileViewModel.ts +81 -0
  313. package/examples/react/FullApp/src/viewmodels/LocationsViewModel.ts +113 -0
  314. package/examples/react/FullApp/src/viewmodels/MessageThreadViewModel.ts +83 -0
  315. package/examples/react/FullApp/src/viewmodels/UsersViewModel.ts +88 -0
  316. package/examples/react/FullApp/tsconfig.json +22 -0
  317. package/examples/react/FullApp/vite.config.ts +18 -0
  318. package/examples/react/WorkerApp/index.html +12 -0
  319. package/examples/react/WorkerApp/src/App.tsx +24 -0
  320. package/examples/react/WorkerApp/src/channels/MessagingChannel.ts +46 -0
  321. package/examples/react/WorkerApp/src/channels/WorkerStatusChannel.ts +35 -0
  322. package/examples/react/WorkerApp/src/components/auth/LoginPage.tsx +60 -0
  323. package/examples/react/WorkerApp/src/components/layout/AppShell.tsx +31 -0
  324. package/examples/react/WorkerApp/src/components/layout/Header.tsx +23 -0
  325. package/examples/react/WorkerApp/src/components/layout/Sidebar.tsx +28 -0
  326. package/examples/react/WorkerApp/src/components/messaging/ComposeBar.tsx +33 -0
  327. package/examples/react/WorkerApp/src/components/messaging/ConversationList.tsx +59 -0
  328. package/examples/react/WorkerApp/src/components/messaging/MessageBubble.tsx +45 -0
  329. package/examples/react/WorkerApp/src/components/messaging/MessageThread.tsx +93 -0
  330. package/examples/react/WorkerApp/src/components/messaging/MessagingPage.tsx +53 -0
  331. package/examples/react/WorkerApp/src/components/shared/ErrorBanner.tsx +3 -0
  332. package/examples/react/WorkerApp/src/components/shared/PendingBanner.tsx +37 -0
  333. package/examples/react/WorkerApp/src/components/shared/Spinner.tsx +7 -0
  334. package/examples/react/WorkerApp/src/components/shared/Toast.tsx +41 -0
  335. package/examples/react/WorkerApp/src/components/shift/ShiftPage.tsx +98 -0
  336. package/examples/react/WorkerApp/src/components/shift/ShiftTimer.tsx +24 -0
  337. package/examples/react/WorkerApp/src/components/shift/SiteSelector.tsx +27 -0
  338. package/examples/react/WorkerApp/src/components/sites/SiteFilters.tsx +61 -0
  339. package/examples/react/WorkerApp/src/components/sites/SitesPage.tsx +102 -0
  340. package/examples/react/WorkerApp/src/env.d.ts +10 -0
  341. package/examples/react/WorkerApp/src/events/AppEventBus.ts +7 -0
  342. package/examples/react/WorkerApp/src/main.tsx +10 -0
  343. package/examples/react/WorkerApp/src/mock/MockWebSocket.ts +38 -0
  344. package/examples/react/WorkerApp/src/mock/delay.ts +31 -0
  345. package/examples/react/WorkerApp/src/mock/messages.ts +120 -0
  346. package/examples/react/WorkerApp/src/mock/shifts.ts +57 -0
  347. package/examples/react/WorkerApp/src/mock/sites.ts +14 -0
  348. package/examples/react/WorkerApp/src/mock/workers.ts +12 -0
  349. package/examples/react/WorkerApp/src/models/ComposeMessageModel.ts +17 -0
  350. package/examples/react/WorkerApp/src/resources/ConversationsResource.ts +10 -0
  351. package/examples/react/WorkerApp/src/resources/MessagesResource.ts +32 -0
  352. package/examples/react/WorkerApp/src/resources/ShiftResource.ts +73 -0
  353. package/examples/react/WorkerApp/src/resources/SitesResource.ts +11 -0
  354. package/examples/react/WorkerApp/src/resources/WorkersResource.ts +11 -0
  355. package/examples/react/WorkerApp/src/styles.css +756 -0
  356. package/examples/react/WorkerApp/src/types/conversation.ts +7 -0
  357. package/examples/react/WorkerApp/src/types/message.ts +7 -0
  358. package/examples/react/WorkerApp/src/types/shift.ts +13 -0
  359. package/examples/react/WorkerApp/src/types/site.ts +8 -0
  360. package/examples/react/WorkerApp/src/types/worker.ts +8 -0
  361. package/examples/react/WorkerApp/src/viewmodels/AuthViewModel.ts +41 -0
  362. package/examples/react/WorkerApp/src/viewmodels/ConversationsViewModel.ts +83 -0
  363. package/examples/react/WorkerApp/src/viewmodels/MessageThreadViewModel.ts +113 -0
  364. package/examples/react/WorkerApp/src/viewmodels/ShiftViewModel.ts +147 -0
  365. package/examples/react/WorkerApp/src/viewmodels/SitesViewModel.ts +82 -0
  366. package/examples/react/WorkerApp/tsconfig.json +22 -0
  367. package/examples/react/WorkerApp/vite.config.ts +18 -0
  368. package/package.json +11 -9
  369. package/src/Pending.test.ts +1 -2
  370. package/src/Sorting.test.ts +1 -1
  371. package/src/produceDraft.test.ts +3 -3
  372. package/src/react/components/CardList.test.tsx +1 -1
  373. package/src/react/components/DataTable.test.tsx +1 -1
  374. package/src/react/components/InfiniteScroll.test.tsx +5 -5
  375. package/dist/mvc-kit.cjs.map +0 -1
  376. package/dist/mvc-kit.js.map +0 -1
  377. package/dist/react-native.cjs.map +0 -1
  378. package/dist/react-native.js.map +0 -1
  379. package/dist/react.cjs.map +0 -1
  380. package/dist/react.js.map +0 -1
  381. package/dist/web.cjs.map +0 -1
  382. 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"}