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,81 +1,113 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const bindPublicMethods = require("./bindPublicMethods.cjs");
4
- const PROTECTED_KEYS = /* @__PURE__ */ new Set(["addCleanup", "notify"]);
5
- class Trackable {
6
- _listeners = null;
7
- _disposed = false;
8
- _abortController = null;
9
- _cleanups = null;
10
- constructor() {
11
- bindPublicMethods.bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
12
- }
13
- // ── Disposable interface ──
14
- /** Whether this instance has been disposed. */
15
- get disposed() {
16
- return this._disposed;
17
- }
18
- /** AbortSignal that fires when this instance is disposed. Lazily created. */
19
- get disposeSignal() {
20
- if (!this._abortController) {
21
- this._abortController = new AbortController();
22
- }
23
- return this._abortController.signal;
24
- }
25
- /**
26
- * Tear down the instance: abort the dispose signal, run all registered
27
- * cleanups, clear subscribers, and call onDispose. Idempotent.
28
- */
29
- dispose() {
30
- if (this._disposed) return;
31
- this._disposed = true;
32
- this._abortController?.abort();
33
- if (this._cleanups) {
34
- for (const fn of this._cleanups) fn();
35
- this._cleanups = null;
36
- }
37
- this._listeners?.clear();
38
- this.onDispose?.();
39
- }
40
- // ── Subscribable (notification-only, no state) ──
41
- /**
42
- * Subscribe to change notifications. The callback is invoked (with no
43
- * arguments) whenever the subclass calls {@link notify}.
44
- *
45
- * This is the duck-typed contract that ViewModel's auto-tracking system
46
- * recognizes — any object with a `subscribe` method is automatically
47
- * tracked when accessed inside a ViewModel getter.
48
- *
49
- * @returns An unsubscribe function.
50
- */
51
- subscribe(cb) {
52
- if (!this._listeners) this._listeners = /* @__PURE__ */ new Set();
53
- this._listeners.add(cb);
54
- return () => {
55
- this._listeners?.delete(cb);
56
- };
57
- }
58
- /**
59
- * Notify all subscribers that state has changed. Call this after
60
- * mutating internal state to trigger ViewModel getter invalidation
61
- * and React re-renders.
62
- * @protected
63
- */
64
- notify() {
65
- if (this._listeners) {
66
- for (const cb of this._listeners) cb();
67
- }
68
- }
69
- // ── Protected utilities ──
70
- /**
71
- * Register a cleanup function to be called on {@link dispose}.
72
- * Cleanups run in registration order.
73
- * @protected
74
- */
75
- addCleanup(fn) {
76
- if (!this._cleanups) this._cleanups = [];
77
- this._cleanups.push(fn);
78
- }
79
- }
1
+ const require_bindPublicMethods = require("./bindPublicMethods.cjs");
2
+ //#region src/Trackable.ts
3
+ var PROTECTED_KEYS = new Set(["addCleanup", "notify"]);
4
+ /**
5
+ * Base class for custom reactive objects that integrate with ViewModel's
6
+ * auto-tracking system. Provides subscribable notifications, disposal
7
+ * lifecycle, and automatic method binding.
8
+ *
9
+ * Any object with a `subscribe()` method is auto-tracked by ViewModel
10
+ * getters. Trackable gives you that plus cleanup infrastructure and
11
+ * point-free methods — the same building blocks used by Sorting,
12
+ * Selection, Feed, Pagination, and Pending.
13
+ *
14
+ * Use Trackable when integrating third-party SDKs, custom query objects,
15
+ * or any reactive state that doesn't fit ViewModel's state/getter model.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * class RPCQuery<Data> extends Trackable {
20
+ * private _data: Data | undefined;
21
+ * private _loading = false;
22
+ *
23
+ * get data() { return this._data; }
24
+ * get loading() { return this._loading; }
25
+ *
26
+ * async call(): Promise<void> {
27
+ * this._loading = true;
28
+ * this.notify();
29
+ * this._data = await fetchData();
30
+ * this._loading = false;
31
+ * this.notify();
32
+ * }
33
+ * }
34
+ *
35
+ * // Used as a ViewModel property — auto-tracked:
36
+ * class UsersVM extends ViewModel {
37
+ * readonly users = new RPCQuery<User[]>();
38
+ * get userList() { return this.users.data ?? []; }
39
+ * }
40
+ * ```
41
+ */
42
+ var Trackable = class {
43
+ _listeners = null;
44
+ _disposed = false;
45
+ _abortController = null;
46
+ _cleanups = null;
47
+ constructor() {
48
+ require_bindPublicMethods.bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
49
+ }
50
+ /** Whether this instance has been disposed. */
51
+ get disposed() {
52
+ return this._disposed;
53
+ }
54
+ /** AbortSignal that fires when this instance is disposed. Lazily created. */
55
+ get disposeSignal() {
56
+ if (!this._abortController) this._abortController = new AbortController();
57
+ return this._abortController.signal;
58
+ }
59
+ /**
60
+ * Tear down the instance: abort the dispose signal, run all registered
61
+ * cleanups, clear subscribers, and call onDispose. Idempotent.
62
+ */
63
+ dispose() {
64
+ if (this._disposed) return;
65
+ this._disposed = true;
66
+ this._abortController?.abort();
67
+ if (this._cleanups) {
68
+ for (const fn of this._cleanups) fn();
69
+ this._cleanups = null;
70
+ }
71
+ this._listeners?.clear();
72
+ this.onDispose?.();
73
+ }
74
+ /**
75
+ * Subscribe to change notifications. The callback is invoked (with no
76
+ * arguments) whenever the subclass calls {@link notify}.
77
+ *
78
+ * This is the duck-typed contract that ViewModel's auto-tracking system
79
+ * recognizes — any object with a `subscribe` method is automatically
80
+ * tracked when accessed inside a ViewModel getter.
81
+ *
82
+ * @returns An unsubscribe function.
83
+ */
84
+ subscribe(cb) {
85
+ if (!this._listeners) this._listeners = /* @__PURE__ */ new Set();
86
+ this._listeners.add(cb);
87
+ return () => {
88
+ this._listeners?.delete(cb);
89
+ };
90
+ }
91
+ /**
92
+ * Notify all subscribers that state has changed. Call this after
93
+ * mutating internal state to trigger ViewModel getter invalidation
94
+ * and React re-renders.
95
+ * @protected
96
+ */
97
+ notify() {
98
+ if (this._listeners) for (const cb of this._listeners) cb();
99
+ }
100
+ /**
101
+ * Register a cleanup function to be called on {@link dispose}.
102
+ * Cleanups run in registration order.
103
+ * @protected
104
+ */
105
+ addCleanup(fn) {
106
+ if (!this._cleanups) this._cleanups = [];
107
+ this._cleanups.push(fn);
108
+ }
109
+ };
110
+ //#endregion
80
111
  exports.Trackable = Trackable;
81
- //# sourceMappingURL=Trackable.cjs.map
112
+
113
+ //# sourceMappingURL=Trackable.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Trackable.cjs","sources":["../src/Trackable.ts"],"sourcesContent":["import type { Disposable } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst PROTECTED_KEYS = new Set(['addCleanup', 'notify']);\n\n/**\n * Base class for custom reactive objects that integrate with ViewModel's\n * auto-tracking system. Provides subscribable notifications, disposal\n * lifecycle, and automatic method binding.\n *\n * Any object with a `subscribe()` method is auto-tracked by ViewModel\n * getters. Trackable gives you that plus cleanup infrastructure and\n * point-free methods — the same building blocks used by Sorting,\n * Selection, Feed, Pagination, and Pending.\n *\n * Use Trackable when integrating third-party SDKs, custom query objects,\n * or any reactive state that doesn't fit ViewModel's state/getter model.\n *\n * @example\n * ```ts\n * class RPCQuery<Data> extends Trackable {\n * private _data: Data | undefined;\n * private _loading = false;\n *\n * get data() { return this._data; }\n * get loading() { return this._loading; }\n *\n * async call(): Promise<void> {\n * this._loading = true;\n * this.notify();\n * this._data = await fetchData();\n * this._loading = false;\n * this.notify();\n * }\n * }\n *\n * // Used as a ViewModel property — auto-tracked:\n * class UsersVM extends ViewModel {\n * readonly users = new RPCQuery<User[]>();\n * get userList() { return this.users.data ?? []; }\n * }\n * ```\n */\nexport class Trackable implements Disposable {\n private _listeners: Set<() => void> | null = null;\n private _disposed = false;\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n\n constructor() {\n bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);\n }\n\n // ── Disposable interface ──\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n /**\n * Tear down the instance: abort the dispose signal, run all registered\n * cleanups, clear subscribers, and call onDispose. Idempotent.\n */\n dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this._listeners?.clear();\n this.onDispose?.();\n }\n\n // ── Subscribable (notification-only, no state) ──\n\n /**\n * Subscribe to change notifications. The callback is invoked (with no\n * arguments) whenever the subclass calls {@link notify}.\n *\n * This is the duck-typed contract that ViewModel's auto-tracking system\n * recognizes — any object with a `subscribe` method is automatically\n * tracked when accessed inside a ViewModel getter.\n *\n * @returns An unsubscribe function.\n */\n subscribe(cb: () => void): () => void {\n if (!this._listeners) this._listeners = new Set();\n this._listeners.add(cb);\n return () => { this._listeners?.delete(cb); };\n }\n\n /**\n * Notify all subscribers that state has changed. Call this after\n * mutating internal state to trigger ViewModel getter invalidation\n * and React re-renders.\n * @protected\n */\n protected notify(): void {\n if (this._listeners) {\n for (const cb of this._listeners) cb();\n }\n }\n\n // ── Protected utilities ──\n\n /**\n * Register a cleanup function to be called on {@link dispose}.\n * Cleanups run in registration order.\n * @protected\n */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) this._cleanups = [];\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called at the end of dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n}\n"],"names":["bindPublicMethods"],"mappings":";;;AAGA,MAAM,iBAAiB,oBAAI,IAAI,CAAC,cAAc,QAAQ,CAAC;AAwChD,MAAM,UAAgC;AAAA,EACnC,aAAqC;AAAA,EACrC,YAAY;AAAA,EACZ,mBAA2C;AAAA,EAC3C,YAAmC;AAAA,EAE3C,cAAc;AACZA,sBAAAA,kBAAkB,MAAM,OAAO,WAAW,cAAc;AAAA,EAC1D;AAAA;AAAA;AAAA,EAKA,IAAI,WAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,gBAA6B;AAC/B,QAAI,CAAC,KAAK,kBAAkB;AAC1B,WAAK,mBAAmB,IAAI,gBAAA;AAAA,IAC9B;AACA,WAAO,KAAK,iBAAiB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAgB;AACd,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AACjB,SAAK,kBAAkB,MAAA;AACvB,QAAI,KAAK,WAAW;AAClB,iBAAW,MAAM,KAAK,UAAW,IAAA;AACjC,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,YAAY,MAAA;AACjB,SAAK,YAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,UAAU,IAA4B;AACpC,QAAI,CAAC,KAAK,WAAY,MAAK,iCAAiB,IAAA;AAC5C,SAAK,WAAW,IAAI,EAAE;AACtB,WAAO,MAAM;AAAE,WAAK,YAAY,OAAO,EAAE;AAAA,IAAG;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,SAAe;AACvB,QAAI,KAAK,YAAY;AACnB,iBAAW,MAAM,KAAK,WAAY,IAAA;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASU,WAAW,IAAsB;AACzC,QAAI,CAAC,KAAK,UAAW,MAAK,YAAY,CAAA;AACtC,SAAK,UAAU,KAAK,EAAE;AAAA,EACxB;AAIF;;"}
1
+ {"version":3,"file":"Trackable.cjs","names":[],"sources":["../src/Trackable.ts"],"sourcesContent":["import type { Disposable } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst PROTECTED_KEYS = new Set(['addCleanup', 'notify']);\n\n/**\n * Base class for custom reactive objects that integrate with ViewModel's\n * auto-tracking system. Provides subscribable notifications, disposal\n * lifecycle, and automatic method binding.\n *\n * Any object with a `subscribe()` method is auto-tracked by ViewModel\n * getters. Trackable gives you that plus cleanup infrastructure and\n * point-free methods — the same building blocks used by Sorting,\n * Selection, Feed, Pagination, and Pending.\n *\n * Use Trackable when integrating third-party SDKs, custom query objects,\n * or any reactive state that doesn't fit ViewModel's state/getter model.\n *\n * @example\n * ```ts\n * class RPCQuery<Data> extends Trackable {\n * private _data: Data | undefined;\n * private _loading = false;\n *\n * get data() { return this._data; }\n * get loading() { return this._loading; }\n *\n * async call(): Promise<void> {\n * this._loading = true;\n * this.notify();\n * this._data = await fetchData();\n * this._loading = false;\n * this.notify();\n * }\n * }\n *\n * // Used as a ViewModel property — auto-tracked:\n * class UsersVM extends ViewModel {\n * readonly users = new RPCQuery<User[]>();\n * get userList() { return this.users.data ?? []; }\n * }\n * ```\n */\nexport class Trackable implements Disposable {\n private _listeners: Set<() => void> | null = null;\n private _disposed = false;\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n\n constructor() {\n bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);\n }\n\n // ── Disposable interface ──\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n /**\n * Tear down the instance: abort the dispose signal, run all registered\n * cleanups, clear subscribers, and call onDispose. Idempotent.\n */\n dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this._listeners?.clear();\n this.onDispose?.();\n }\n\n // ── Subscribable (notification-only, no state) ──\n\n /**\n * Subscribe to change notifications. The callback is invoked (with no\n * arguments) whenever the subclass calls {@link notify}.\n *\n * This is the duck-typed contract that ViewModel's auto-tracking system\n * recognizes — any object with a `subscribe` method is automatically\n * tracked when accessed inside a ViewModel getter.\n *\n * @returns An unsubscribe function.\n */\n subscribe(cb: () => void): () => void {\n if (!this._listeners) this._listeners = new Set();\n this._listeners.add(cb);\n return () => { this._listeners?.delete(cb); };\n }\n\n /**\n * Notify all subscribers that state has changed. Call this after\n * mutating internal state to trigger ViewModel getter invalidation\n * and React re-renders.\n * @protected\n */\n protected notify(): void {\n if (this._listeners) {\n for (const cb of this._listeners) cb();\n }\n }\n\n // ── Protected utilities ──\n\n /**\n * Register a cleanup function to be called on {@link dispose}.\n * Cleanups run in registration order.\n * @protected\n */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) this._cleanups = [];\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called at the end of dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n}\n"],"mappings":";;AAGA,IAAM,iBAAiB,IAAI,IAAI,CAAC,cAAc,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCxD,IAAa,YAAb,MAA6C;CAC3C,aAA6C;CAC7C,YAAoB;CACpB,mBAAmD;CACnD,YAA2C;CAE3C,cAAc;AACZ,4BAAA,kBAAkB,MAAM,OAAO,WAAW,eAAe;;;CAM3D,IAAI,WAAoB;AACtB,SAAO,KAAK;;;CAId,IAAI,gBAA6B;AAC/B,MAAI,CAAC,KAAK,iBACR,MAAK,mBAAmB,IAAI,iBAAiB;AAE/C,SAAO,KAAK,iBAAiB;;;;;;CAO/B,UAAgB;AACd,MAAI,KAAK,UAAW;AACpB,OAAK,YAAY;AACjB,OAAK,kBAAkB,OAAO;AAC9B,MAAI,KAAK,WAAW;AAClB,QAAK,MAAM,MAAM,KAAK,UAAW,KAAI;AACrC,QAAK,YAAY;;AAEnB,OAAK,YAAY,OAAO;AACxB,OAAK,aAAa;;;;;;;;;;;;CAepB,UAAU,IAA4B;AACpC,MAAI,CAAC,KAAK,WAAY,MAAK,6BAAa,IAAI,KAAK;AACjD,OAAK,WAAW,IAAI,GAAG;AACvB,eAAa;AAAE,QAAK,YAAY,OAAO,GAAG;;;;;;;;;CAS5C,SAAyB;AACvB,MAAI,KAAK,WACP,MAAK,MAAM,MAAM,KAAK,WAAY,KAAI;;;;;;;CAW1C,WAAqB,IAAsB;AACzC,MAAI,CAAC,KAAK,UAAW,MAAK,YAAY,EAAE;AACxC,OAAK,UAAU,KAAK,GAAG"}
package/dist/Trackable.js CHANGED
@@ -1,81 +1,113 @@
1
1
  import { bindPublicMethods } from "./bindPublicMethods.js";
2
- const PROTECTED_KEYS = /* @__PURE__ */ new Set(["addCleanup", "notify"]);
3
- class Trackable {
4
- _listeners = null;
5
- _disposed = false;
6
- _abortController = null;
7
- _cleanups = null;
8
- constructor() {
9
- bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
10
- }
11
- // ── Disposable interface ──
12
- /** Whether this instance has been disposed. */
13
- get disposed() {
14
- return this._disposed;
15
- }
16
- /** AbortSignal that fires when this instance is disposed. Lazily created. */
17
- get disposeSignal() {
18
- if (!this._abortController) {
19
- this._abortController = new AbortController();
20
- }
21
- return this._abortController.signal;
22
- }
23
- /**
24
- * Tear down the instance: abort the dispose signal, run all registered
25
- * cleanups, clear subscribers, and call onDispose. Idempotent.
26
- */
27
- dispose() {
28
- if (this._disposed) return;
29
- this._disposed = true;
30
- this._abortController?.abort();
31
- if (this._cleanups) {
32
- for (const fn of this._cleanups) fn();
33
- this._cleanups = null;
34
- }
35
- this._listeners?.clear();
36
- this.onDispose?.();
37
- }
38
- // ── Subscribable (notification-only, no state) ──
39
- /**
40
- * Subscribe to change notifications. The callback is invoked (with no
41
- * arguments) whenever the subclass calls {@link notify}.
42
- *
43
- * This is the duck-typed contract that ViewModel's auto-tracking system
44
- * recognizes — any object with a `subscribe` method is automatically
45
- * tracked when accessed inside a ViewModel getter.
46
- *
47
- * @returns An unsubscribe function.
48
- */
49
- subscribe(cb) {
50
- if (!this._listeners) this._listeners = /* @__PURE__ */ new Set();
51
- this._listeners.add(cb);
52
- return () => {
53
- this._listeners?.delete(cb);
54
- };
55
- }
56
- /**
57
- * Notify all subscribers that state has changed. Call this after
58
- * mutating internal state to trigger ViewModel getter invalidation
59
- * and React re-renders.
60
- * @protected
61
- */
62
- notify() {
63
- if (this._listeners) {
64
- for (const cb of this._listeners) cb();
65
- }
66
- }
67
- // ── Protected utilities ──
68
- /**
69
- * Register a cleanup function to be called on {@link dispose}.
70
- * Cleanups run in registration order.
71
- * @protected
72
- */
73
- addCleanup(fn) {
74
- if (!this._cleanups) this._cleanups = [];
75
- this._cleanups.push(fn);
76
- }
77
- }
78
- export {
79
- Trackable
2
+ //#region src/Trackable.ts
3
+ var PROTECTED_KEYS = new Set(["addCleanup", "notify"]);
4
+ /**
5
+ * Base class for custom reactive objects that integrate with ViewModel's
6
+ * auto-tracking system. Provides subscribable notifications, disposal
7
+ * lifecycle, and automatic method binding.
8
+ *
9
+ * Any object with a `subscribe()` method is auto-tracked by ViewModel
10
+ * getters. Trackable gives you that plus cleanup infrastructure and
11
+ * point-free methods the same building blocks used by Sorting,
12
+ * Selection, Feed, Pagination, and Pending.
13
+ *
14
+ * Use Trackable when integrating third-party SDKs, custom query objects,
15
+ * or any reactive state that doesn't fit ViewModel's state/getter model.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * class RPCQuery<Data> extends Trackable {
20
+ * private _data: Data | undefined;
21
+ * private _loading = false;
22
+ *
23
+ * get data() { return this._data; }
24
+ * get loading() { return this._loading; }
25
+ *
26
+ * async call(): Promise<void> {
27
+ * this._loading = true;
28
+ * this.notify();
29
+ * this._data = await fetchData();
30
+ * this._loading = false;
31
+ * this.notify();
32
+ * }
33
+ * }
34
+ *
35
+ * // Used as a ViewModel property — auto-tracked:
36
+ * class UsersVM extends ViewModel {
37
+ * readonly users = new RPCQuery<User[]>();
38
+ * get userList() { return this.users.data ?? []; }
39
+ * }
40
+ * ```
41
+ */
42
+ var Trackable = class {
43
+ _listeners = null;
44
+ _disposed = false;
45
+ _abortController = null;
46
+ _cleanups = null;
47
+ constructor() {
48
+ bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
49
+ }
50
+ /** Whether this instance has been disposed. */
51
+ get disposed() {
52
+ return this._disposed;
53
+ }
54
+ /** AbortSignal that fires when this instance is disposed. Lazily created. */
55
+ get disposeSignal() {
56
+ if (!this._abortController) this._abortController = new AbortController();
57
+ return this._abortController.signal;
58
+ }
59
+ /**
60
+ * Tear down the instance: abort the dispose signal, run all registered
61
+ * cleanups, clear subscribers, and call onDispose. Idempotent.
62
+ */
63
+ dispose() {
64
+ if (this._disposed) return;
65
+ this._disposed = true;
66
+ this._abortController?.abort();
67
+ if (this._cleanups) {
68
+ for (const fn of this._cleanups) fn();
69
+ this._cleanups = null;
70
+ }
71
+ this._listeners?.clear();
72
+ this.onDispose?.();
73
+ }
74
+ /**
75
+ * Subscribe to change notifications. The callback is invoked (with no
76
+ * arguments) whenever the subclass calls {@link notify}.
77
+ *
78
+ * This is the duck-typed contract that ViewModel's auto-tracking system
79
+ * recognizes — any object with a `subscribe` method is automatically
80
+ * tracked when accessed inside a ViewModel getter.
81
+ *
82
+ * @returns An unsubscribe function.
83
+ */
84
+ subscribe(cb) {
85
+ if (!this._listeners) this._listeners = /* @__PURE__ */ new Set();
86
+ this._listeners.add(cb);
87
+ return () => {
88
+ this._listeners?.delete(cb);
89
+ };
90
+ }
91
+ /**
92
+ * Notify all subscribers that state has changed. Call this after
93
+ * mutating internal state to trigger ViewModel getter invalidation
94
+ * and React re-renders.
95
+ * @protected
96
+ */
97
+ notify() {
98
+ if (this._listeners) for (const cb of this._listeners) cb();
99
+ }
100
+ /**
101
+ * Register a cleanup function to be called on {@link dispose}.
102
+ * Cleanups run in registration order.
103
+ * @protected
104
+ */
105
+ addCleanup(fn) {
106
+ if (!this._cleanups) this._cleanups = [];
107
+ this._cleanups.push(fn);
108
+ }
80
109
  };
81
- //# sourceMappingURL=Trackable.js.map
110
+ //#endregion
111
+ export { Trackable };
112
+
113
+ //# sourceMappingURL=Trackable.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Trackable.js","sources":["../src/Trackable.ts"],"sourcesContent":["import type { Disposable } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst PROTECTED_KEYS = new Set(['addCleanup', 'notify']);\n\n/**\n * Base class for custom reactive objects that integrate with ViewModel's\n * auto-tracking system. Provides subscribable notifications, disposal\n * lifecycle, and automatic method binding.\n *\n * Any object with a `subscribe()` method is auto-tracked by ViewModel\n * getters. Trackable gives you that plus cleanup infrastructure and\n * point-free methods — the same building blocks used by Sorting,\n * Selection, Feed, Pagination, and Pending.\n *\n * Use Trackable when integrating third-party SDKs, custom query objects,\n * or any reactive state that doesn't fit ViewModel's state/getter model.\n *\n * @example\n * ```ts\n * class RPCQuery<Data> extends Trackable {\n * private _data: Data | undefined;\n * private _loading = false;\n *\n * get data() { return this._data; }\n * get loading() { return this._loading; }\n *\n * async call(): Promise<void> {\n * this._loading = true;\n * this.notify();\n * this._data = await fetchData();\n * this._loading = false;\n * this.notify();\n * }\n * }\n *\n * // Used as a ViewModel property — auto-tracked:\n * class UsersVM extends ViewModel {\n * readonly users = new RPCQuery<User[]>();\n * get userList() { return this.users.data ?? []; }\n * }\n * ```\n */\nexport class Trackable implements Disposable {\n private _listeners: Set<() => void> | null = null;\n private _disposed = false;\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n\n constructor() {\n bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);\n }\n\n // ── Disposable interface ──\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n /**\n * Tear down the instance: abort the dispose signal, run all registered\n * cleanups, clear subscribers, and call onDispose. Idempotent.\n */\n dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this._listeners?.clear();\n this.onDispose?.();\n }\n\n // ── Subscribable (notification-only, no state) ──\n\n /**\n * Subscribe to change notifications. The callback is invoked (with no\n * arguments) whenever the subclass calls {@link notify}.\n *\n * This is the duck-typed contract that ViewModel's auto-tracking system\n * recognizes — any object with a `subscribe` method is automatically\n * tracked when accessed inside a ViewModel getter.\n *\n * @returns An unsubscribe function.\n */\n subscribe(cb: () => void): () => void {\n if (!this._listeners) this._listeners = new Set();\n this._listeners.add(cb);\n return () => { this._listeners?.delete(cb); };\n }\n\n /**\n * Notify all subscribers that state has changed. Call this after\n * mutating internal state to trigger ViewModel getter invalidation\n * and React re-renders.\n * @protected\n */\n protected notify(): void {\n if (this._listeners) {\n for (const cb of this._listeners) cb();\n }\n }\n\n // ── Protected utilities ──\n\n /**\n * Register a cleanup function to be called on {@link dispose}.\n * Cleanups run in registration order.\n * @protected\n */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) this._cleanups = [];\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called at the end of dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n}\n"],"names":[],"mappings":";AAGA,MAAM,iBAAiB,oBAAI,IAAI,CAAC,cAAc,QAAQ,CAAC;AAwChD,MAAM,UAAgC;AAAA,EACnC,aAAqC;AAAA,EACrC,YAAY;AAAA,EACZ,mBAA2C;AAAA,EAC3C,YAAmC;AAAA,EAE3C,cAAc;AACZ,sBAAkB,MAAM,OAAO,WAAW,cAAc;AAAA,EAC1D;AAAA;AAAA;AAAA,EAKA,IAAI,WAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,gBAA6B;AAC/B,QAAI,CAAC,KAAK,kBAAkB;AAC1B,WAAK,mBAAmB,IAAI,gBAAA;AAAA,IAC9B;AACA,WAAO,KAAK,iBAAiB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAgB;AACd,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AACjB,SAAK,kBAAkB,MAAA;AACvB,QAAI,KAAK,WAAW;AAClB,iBAAW,MAAM,KAAK,UAAW,IAAA;AACjC,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,YAAY,MAAA;AACjB,SAAK,YAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,UAAU,IAA4B;AACpC,QAAI,CAAC,KAAK,WAAY,MAAK,iCAAiB,IAAA;AAC5C,SAAK,WAAW,IAAI,EAAE;AACtB,WAAO,MAAM;AAAE,WAAK,YAAY,OAAO,EAAE;AAAA,IAAG;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,SAAe;AACvB,QAAI,KAAK,YAAY;AACnB,iBAAW,MAAM,KAAK,WAAY,IAAA;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASU,WAAW,IAAsB;AACzC,QAAI,CAAC,KAAK,UAAW,MAAK,YAAY,CAAA;AACtC,SAAK,UAAU,KAAK,EAAE;AAAA,EACxB;AAIF;"}
1
+ {"version":3,"file":"Trackable.js","names":[],"sources":["../src/Trackable.ts"],"sourcesContent":["import type { Disposable } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst PROTECTED_KEYS = new Set(['addCleanup', 'notify']);\n\n/**\n * Base class for custom reactive objects that integrate with ViewModel's\n * auto-tracking system. Provides subscribable notifications, disposal\n * lifecycle, and automatic method binding.\n *\n * Any object with a `subscribe()` method is auto-tracked by ViewModel\n * getters. Trackable gives you that plus cleanup infrastructure and\n * point-free methods — the same building blocks used by Sorting,\n * Selection, Feed, Pagination, and Pending.\n *\n * Use Trackable when integrating third-party SDKs, custom query objects,\n * or any reactive state that doesn't fit ViewModel's state/getter model.\n *\n * @example\n * ```ts\n * class RPCQuery<Data> extends Trackable {\n * private _data: Data | undefined;\n * private _loading = false;\n *\n * get data() { return this._data; }\n * get loading() { return this._loading; }\n *\n * async call(): Promise<void> {\n * this._loading = true;\n * this.notify();\n * this._data = await fetchData();\n * this._loading = false;\n * this.notify();\n * }\n * }\n *\n * // Used as a ViewModel property — auto-tracked:\n * class UsersVM extends ViewModel {\n * readonly users = new RPCQuery<User[]>();\n * get userList() { return this.users.data ?? []; }\n * }\n * ```\n */\nexport class Trackable implements Disposable {\n private _listeners: Set<() => void> | null = null;\n private _disposed = false;\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n\n constructor() {\n bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);\n }\n\n // ── Disposable interface ──\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n /**\n * Tear down the instance: abort the dispose signal, run all registered\n * cleanups, clear subscribers, and call onDispose. Idempotent.\n */\n dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this._listeners?.clear();\n this.onDispose?.();\n }\n\n // ── Subscribable (notification-only, no state) ──\n\n /**\n * Subscribe to change notifications. The callback is invoked (with no\n * arguments) whenever the subclass calls {@link notify}.\n *\n * This is the duck-typed contract that ViewModel's auto-tracking system\n * recognizes — any object with a `subscribe` method is automatically\n * tracked when accessed inside a ViewModel getter.\n *\n * @returns An unsubscribe function.\n */\n subscribe(cb: () => void): () => void {\n if (!this._listeners) this._listeners = new Set();\n this._listeners.add(cb);\n return () => { this._listeners?.delete(cb); };\n }\n\n /**\n * Notify all subscribers that state has changed. Call this after\n * mutating internal state to trigger ViewModel getter invalidation\n * and React re-renders.\n * @protected\n */\n protected notify(): void {\n if (this._listeners) {\n for (const cb of this._listeners) cb();\n }\n }\n\n // ── Protected utilities ──\n\n /**\n * Register a cleanup function to be called on {@link dispose}.\n * Cleanups run in registration order.\n * @protected\n */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) this._cleanups = [];\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called at the end of dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n}\n"],"mappings":";;AAGA,IAAM,iBAAiB,IAAI,IAAI,CAAC,cAAc,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCxD,IAAa,YAAb,MAA6C;CAC3C,aAA6C;CAC7C,YAAoB;CACpB,mBAAmD;CACnD,YAA2C;CAE3C,cAAc;AACZ,oBAAkB,MAAM,OAAO,WAAW,eAAe;;;CAM3D,IAAI,WAAoB;AACtB,SAAO,KAAK;;;CAId,IAAI,gBAA6B;AAC/B,MAAI,CAAC,KAAK,iBACR,MAAK,mBAAmB,IAAI,iBAAiB;AAE/C,SAAO,KAAK,iBAAiB;;;;;;CAO/B,UAAgB;AACd,MAAI,KAAK,UAAW;AACpB,OAAK,YAAY;AACjB,OAAK,kBAAkB,OAAO;AAC9B,MAAI,KAAK,WAAW;AAClB,QAAK,MAAM,MAAM,KAAK,UAAW,KAAI;AACrC,QAAK,YAAY;;AAEnB,OAAK,YAAY,OAAO;AACxB,OAAK,aAAa;;;;;;;;;;;;CAepB,UAAU,IAA4B;AACpC,MAAI,CAAC,KAAK,WAAY,MAAK,6BAAa,IAAI,KAAK;AACjD,OAAK,WAAW,IAAI,GAAG;AACvB,eAAa;AAAE,QAAK,YAAY,OAAO,GAAG;;;;;;;;;CAS5C,SAAyB;AACvB,MAAI,KAAK,WACP,MAAK,MAAM,MAAM,KAAK,WAAY,KAAI;;;;;;;CAW1C,WAAqB,IAAsB;AACzC,MAAI,CAAC,KAAK,UAAW,MAAK,YAAY,EAAE;AACxC,OAAK,UAAU,KAAK,GAAG"}