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
package/dist/EventBus.cjs CHANGED
@@ -1,89 +1,78 @@
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"]);
5
- class EventBus {
6
- _disposed = false;
7
- _handlers = /* @__PURE__ */ new Map();
8
- _abortController = null;
9
- _cleanups = null;
10
- constructor() {
11
- bindPublicMethods.bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
12
- }
13
- /** Whether this instance has been disposed. */
14
- get disposed() {
15
- return this._disposed;
16
- }
17
- /** AbortSignal that fires when this instance is disposed. Lazily created. */
18
- get disposeSignal() {
19
- if (!this._abortController) {
20
- this._abortController = new AbortController();
21
- }
22
- return this._abortController.signal;
23
- }
24
- /**
25
- * Emit an event with a payload.
26
- */
27
- emit(event, payload) {
28
- if (this._disposed) {
29
- throw new Error("Cannot emit on disposed EventBus");
30
- }
31
- const handlers = this._handlers.get(event);
32
- if (handlers) {
33
- for (const handler of handlers) {
34
- handler(payload);
35
- }
36
- }
37
- }
38
- /**
39
- * Subscribe to an event. Returns unsubscribe function.
40
- */
41
- on(event, handler) {
42
- if (this._disposed) {
43
- return () => {
44
- };
45
- }
46
- let handlers = this._handlers.get(event);
47
- if (!handlers) {
48
- handlers = /* @__PURE__ */ new Set();
49
- this._handlers.set(event, handlers);
50
- }
51
- handlers.add(handler);
52
- return () => {
53
- handlers.delete(handler);
54
- };
55
- }
56
- /**
57
- * Subscribe to an event once. Auto-unsubscribes after first invocation.
58
- */
59
- once(event, handler) {
60
- const unsubscribe = this.on(event, (payload) => {
61
- unsubscribe();
62
- handler(payload);
63
- });
64
- return unsubscribe;
65
- }
66
- /** Tears down the instance, releasing all subscriptions and resources. */
67
- dispose() {
68
- if (this._disposed) {
69
- return;
70
- }
71
- this._disposed = true;
72
- this._abortController?.abort();
73
- if (this._cleanups) {
74
- for (const fn of this._cleanups) fn();
75
- this._cleanups = null;
76
- }
77
- this.onDispose?.();
78
- this._handlers.clear();
79
- }
80
- /** Registers a cleanup function to be called on dispose. @protected */
81
- addCleanup(fn) {
82
- if (!this._cleanups) {
83
- this._cleanups = [];
84
- }
85
- this._cleanups.push(fn);
86
- }
87
- }
1
+ const require_bindPublicMethods = require("./bindPublicMethods.cjs");
2
+ //#region src/EventBus.ts
3
+ var PROTECTED_KEYS = new Set(["addCleanup"]);
4
+ /**
5
+ * Typed pub/sub event bus.
6
+ */
7
+ var EventBus = class {
8
+ _disposed = false;
9
+ _handlers = /* @__PURE__ */ new Map();
10
+ _abortController = null;
11
+ _cleanups = null;
12
+ constructor() {
13
+ require_bindPublicMethods.bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
14
+ }
15
+ /** Whether this instance has been disposed. */
16
+ get disposed() {
17
+ return this._disposed;
18
+ }
19
+ /** AbortSignal that fires when this instance is disposed. Lazily created. */
20
+ get disposeSignal() {
21
+ if (!this._abortController) this._abortController = new AbortController();
22
+ return this._abortController.signal;
23
+ }
24
+ /**
25
+ * Emit an event with a payload.
26
+ */
27
+ emit(event, payload) {
28
+ if (this._disposed) throw new Error("Cannot emit on disposed EventBus");
29
+ const handlers = this._handlers.get(event);
30
+ if (handlers) for (const handler of handlers) handler(payload);
31
+ }
32
+ /**
33
+ * Subscribe to an event. Returns unsubscribe function.
34
+ */
35
+ on(event, handler) {
36
+ if (this._disposed) return () => {};
37
+ let handlers = this._handlers.get(event);
38
+ if (!handlers) {
39
+ handlers = /* @__PURE__ */ new Set();
40
+ this._handlers.set(event, handlers);
41
+ }
42
+ handlers.add(handler);
43
+ return () => {
44
+ handlers.delete(handler);
45
+ };
46
+ }
47
+ /**
48
+ * Subscribe to an event once. Auto-unsubscribes after first invocation.
49
+ */
50
+ once(event, handler) {
51
+ const unsubscribe = this.on(event, (payload) => {
52
+ unsubscribe();
53
+ handler(payload);
54
+ });
55
+ return unsubscribe;
56
+ }
57
+ /** Tears down the instance, releasing all subscriptions and resources. */
58
+ dispose() {
59
+ if (this._disposed) return;
60
+ this._disposed = true;
61
+ this._abortController?.abort();
62
+ if (this._cleanups) {
63
+ for (const fn of this._cleanups) fn();
64
+ this._cleanups = null;
65
+ }
66
+ this.onDispose?.();
67
+ this._handlers.clear();
68
+ }
69
+ /** Registers a cleanup function to be called on dispose. @protected */
70
+ addCleanup(fn) {
71
+ if (!this._cleanups) this._cleanups = [];
72
+ this._cleanups.push(fn);
73
+ }
74
+ };
75
+ //#endregion
88
76
  exports.EventBus = EventBus;
89
- //# sourceMappingURL=EventBus.cjs.map
77
+
78
+ //# sourceMappingURL=EventBus.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"EventBus.cjs","sources":["../src/EventBus.ts"],"sourcesContent":["import type { Disposable } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst PROTECTED_KEYS = new Set(['addCleanup']);\ntype Handler<T> = (payload: T) => void;\n\n/**\n * Typed pub/sub event bus.\n */\nexport class EventBus<E extends Record<string, any>> implements Disposable {\n /** Phantom type brand — enables correct inference of E in generic helpers like useEvent(). */\n declare readonly _types: E;\n\n private _disposed = false;\n private _handlers = new Map<keyof E, Set<Handler<unknown>>>();\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 /** 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 * Emit an event with a payload.\n */\n emit<K extends keyof E>(event: K, payload: E[K]): void {\n if (this._disposed) {\n throw new Error('Cannot emit on disposed EventBus');\n }\n\n const handlers = this._handlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(payload);\n }\n }\n }\n\n /**\n * Subscribe to an event. Returns unsubscribe function.\n */\n on<K extends keyof E>(event: K, handler: Handler<E[K]>): () => void {\n if (this._disposed) {\n return () => {};\n }\n\n let handlers = this._handlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this._handlers.set(event, handlers);\n }\n\n handlers.add(handler as Handler<unknown>);\n\n return () => {\n handlers!.delete(handler as Handler<unknown>);\n };\n }\n\n /**\n * Subscribe to an event once. Auto-unsubscribes after first invocation.\n */\n once<K extends keyof E>(event: K, handler: Handler<E[K]>): () => void {\n const unsubscribe = this.on(event, (payload) => {\n unsubscribe();\n handler(payload);\n });\n return unsubscribe;\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\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.onDispose?.();\n this._handlers.clear();\n }\n\n /** Registers a cleanup function to be called on dispose. @protected */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n}\n"],"names":["bindPublicMethods"],"mappings":";;;AAGA,MAAM,iBAAiB,oBAAI,IAAI,CAAC,YAAY,CAAC;AAMtC,MAAM,SAA8D;AAAA,EAIjE,YAAY;AAAA,EACZ,gCAAgB,IAAA;AAAA,EAChB,mBAA2C;AAAA,EAC3C,YAAmC;AAAA,EAE3C,cAAc;AACZA,sBAAAA,kBAAkB,MAAM,OAAO,WAAW,cAAc;AAAA,EAC1D;AAAA;AAAA,EAGA,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,EAKA,KAAwB,OAAU,SAAqB;AACrD,QAAI,KAAK,WAAW;AAClB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAEA,UAAM,WAAW,KAAK,UAAU,IAAI,KAAK;AACzC,QAAI,UAAU;AACZ,iBAAW,WAAW,UAAU;AAC9B,gBAAQ,OAAO;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,GAAsB,OAAU,SAAoC;AAClE,QAAI,KAAK,WAAW;AAClB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AAEA,QAAI,WAAW,KAAK,UAAU,IAAI,KAAK;AACvC,QAAI,CAAC,UAAU;AACb,qCAAe,IAAA;AACf,WAAK,UAAU,IAAI,OAAO,QAAQ;AAAA,IACpC;AAEA,aAAS,IAAI,OAA2B;AAExC,WAAO,MAAM;AACX,eAAU,OAAO,OAA2B;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,KAAwB,OAAU,SAAoC;AACpE,UAAM,cAAc,KAAK,GAAG,OAAO,CAAC,YAAY;AAC9C,kBAAA;AACA,cAAQ,OAAO;AAAA,IACjB,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAAgB;AACd,QAAI,KAAK,WAAW;AAClB;AAAA,IACF;AAEA,SAAK,YAAY;AACjB,SAAK,kBAAkB,MAAA;AACvB,QAAI,KAAK,WAAW;AAClB,iBAAW,MAAM,KAAK,UAAW,IAAA;AACjC,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,YAAA;AACL,SAAK,UAAU,MAAA;AAAA,EACjB;AAAA;AAAA,EAGU,WAAW,IAAsB;AACzC,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,CAAA;AAAA,IACnB;AACA,SAAK,UAAU,KAAK,EAAE;AAAA,EACxB;AAIF;;"}
1
+ {"version":3,"file":"EventBus.cjs","names":[],"sources":["../src/EventBus.ts"],"sourcesContent":["import type { Disposable } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst PROTECTED_KEYS = new Set(['addCleanup']);\ntype Handler<T> = (payload: T) => void;\n\n/**\n * Typed pub/sub event bus.\n */\nexport class EventBus<E extends Record<string, any>> implements Disposable {\n /** Phantom type brand — enables correct inference of E in generic helpers like useEvent(). */\n declare readonly _types: E;\n\n private _disposed = false;\n private _handlers = new Map<keyof E, Set<Handler<unknown>>>();\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 /** 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 * Emit an event with a payload.\n */\n emit<K extends keyof E>(event: K, payload: E[K]): void {\n if (this._disposed) {\n throw new Error('Cannot emit on disposed EventBus');\n }\n\n const handlers = this._handlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(payload);\n }\n }\n }\n\n /**\n * Subscribe to an event. Returns unsubscribe function.\n */\n on<K extends keyof E>(event: K, handler: Handler<E[K]>): () => void {\n if (this._disposed) {\n return () => {};\n }\n\n let handlers = this._handlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this._handlers.set(event, handlers);\n }\n\n handlers.add(handler as Handler<unknown>);\n\n return () => {\n handlers!.delete(handler as Handler<unknown>);\n };\n }\n\n /**\n * Subscribe to an event once. Auto-unsubscribes after first invocation.\n */\n once<K extends keyof E>(event: K, handler: Handler<E[K]>): () => void {\n const unsubscribe = this.on(event, (payload) => {\n unsubscribe();\n handler(payload);\n });\n return unsubscribe;\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\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.onDispose?.();\n this._handlers.clear();\n }\n\n /** Registers a cleanup function to be called on dispose. @protected */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n}\n"],"mappings":";;AAGA,IAAM,iBAAiB,IAAI,IAAI,CAAC,aAAa,CAAC;;;;AAM9C,IAAa,WAAb,MAA2E;CAIzE,YAAoB;CACpB,4BAAoB,IAAI,KAAqC;CAC7D,mBAAmD;CACnD,YAA2C;CAE3C,cAAc;AACZ,4BAAA,kBAAkB,MAAM,OAAO,WAAW,eAAe;;;CAI3D,IAAI,WAAoB;AACtB,SAAO,KAAK;;;CAId,IAAI,gBAA6B;AAC/B,MAAI,CAAC,KAAK,iBACR,MAAK,mBAAmB,IAAI,iBAAiB;AAE/C,SAAO,KAAK,iBAAiB;;;;;CAM/B,KAAwB,OAAU,SAAqB;AACrD,MAAI,KAAK,UACP,OAAM,IAAI,MAAM,mCAAmC;EAGrD,MAAM,WAAW,KAAK,UAAU,IAAI,MAAM;AAC1C,MAAI,SACF,MAAK,MAAM,WAAW,SACpB,SAAQ,QAAQ;;;;;CAQtB,GAAsB,OAAU,SAAoC;AAClE,MAAI,KAAK,UACP,cAAa;EAGf,IAAI,WAAW,KAAK,UAAU,IAAI,MAAM;AACxC,MAAI,CAAC,UAAU;AACb,8BAAW,IAAI,KAAK;AACpB,QAAK,UAAU,IAAI,OAAO,SAAS;;AAGrC,WAAS,IAAI,QAA4B;AAEzC,eAAa;AACX,YAAU,OAAO,QAA4B;;;;;;CAOjD,KAAwB,OAAU,SAAoC;EACpE,MAAM,cAAc,KAAK,GAAG,QAAQ,YAAY;AAC9C,gBAAa;AACb,WAAQ,QAAQ;IAChB;AACF,SAAO;;;CAIT,UAAgB;AACd,MAAI,KAAK,UACP;AAGF,OAAK,YAAY;AACjB,OAAK,kBAAkB,OAAO;AAC9B,MAAI,KAAK,WAAW;AAClB,QAAK,MAAM,MAAM,KAAK,UAAW,KAAI;AACrC,QAAK,YAAY;;AAEnB,OAAK,aAAa;AAClB,OAAK,UAAU,OAAO;;;CAIxB,WAAqB,IAAsB;AACzC,MAAI,CAAC,KAAK,UACR,MAAK,YAAY,EAAE;AAErB,OAAK,UAAU,KAAK,GAAG"}
package/dist/EventBus.js CHANGED
@@ -1,89 +1,78 @@
1
1
  import { bindPublicMethods } from "./bindPublicMethods.js";
2
- const PROTECTED_KEYS = /* @__PURE__ */ new Set(["addCleanup"]);
3
- class EventBus {
4
- _disposed = false;
5
- _handlers = /* @__PURE__ */ new Map();
6
- _abortController = null;
7
- _cleanups = null;
8
- constructor() {
9
- bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
10
- }
11
- /** Whether this instance has been disposed. */
12
- get disposed() {
13
- return this._disposed;
14
- }
15
- /** AbortSignal that fires when this instance is disposed. Lazily created. */
16
- get disposeSignal() {
17
- if (!this._abortController) {
18
- this._abortController = new AbortController();
19
- }
20
- return this._abortController.signal;
21
- }
22
- /**
23
- * Emit an event with a payload.
24
- */
25
- emit(event, payload) {
26
- if (this._disposed) {
27
- throw new Error("Cannot emit on disposed EventBus");
28
- }
29
- const handlers = this._handlers.get(event);
30
- if (handlers) {
31
- for (const handler of handlers) {
32
- handler(payload);
33
- }
34
- }
35
- }
36
- /**
37
- * Subscribe to an event. Returns unsubscribe function.
38
- */
39
- on(event, handler) {
40
- if (this._disposed) {
41
- return () => {
42
- };
43
- }
44
- let handlers = this._handlers.get(event);
45
- if (!handlers) {
46
- handlers = /* @__PURE__ */ new Set();
47
- this._handlers.set(event, handlers);
48
- }
49
- handlers.add(handler);
50
- return () => {
51
- handlers.delete(handler);
52
- };
53
- }
54
- /**
55
- * Subscribe to an event once. Auto-unsubscribes after first invocation.
56
- */
57
- once(event, handler) {
58
- const unsubscribe = this.on(event, (payload) => {
59
- unsubscribe();
60
- handler(payload);
61
- });
62
- return unsubscribe;
63
- }
64
- /** Tears down the instance, releasing all subscriptions and resources. */
65
- dispose() {
66
- if (this._disposed) {
67
- return;
68
- }
69
- this._disposed = true;
70
- this._abortController?.abort();
71
- if (this._cleanups) {
72
- for (const fn of this._cleanups) fn();
73
- this._cleanups = null;
74
- }
75
- this.onDispose?.();
76
- this._handlers.clear();
77
- }
78
- /** Registers a cleanup function to be called on dispose. @protected */
79
- addCleanup(fn) {
80
- if (!this._cleanups) {
81
- this._cleanups = [];
82
- }
83
- this._cleanups.push(fn);
84
- }
85
- }
86
- export {
87
- EventBus
2
+ //#region src/EventBus.ts
3
+ var PROTECTED_KEYS = new Set(["addCleanup"]);
4
+ /**
5
+ * Typed pub/sub event bus.
6
+ */
7
+ var EventBus = class {
8
+ _disposed = false;
9
+ _handlers = /* @__PURE__ */ new Map();
10
+ _abortController = null;
11
+ _cleanups = null;
12
+ constructor() {
13
+ bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
14
+ }
15
+ /** Whether this instance has been disposed. */
16
+ get disposed() {
17
+ return this._disposed;
18
+ }
19
+ /** AbortSignal that fires when this instance is disposed. Lazily created. */
20
+ get disposeSignal() {
21
+ if (!this._abortController) this._abortController = new AbortController();
22
+ return this._abortController.signal;
23
+ }
24
+ /**
25
+ * Emit an event with a payload.
26
+ */
27
+ emit(event, payload) {
28
+ if (this._disposed) throw new Error("Cannot emit on disposed EventBus");
29
+ const handlers = this._handlers.get(event);
30
+ if (handlers) for (const handler of handlers) handler(payload);
31
+ }
32
+ /**
33
+ * Subscribe to an event. Returns unsubscribe function.
34
+ */
35
+ on(event, handler) {
36
+ if (this._disposed) return () => {};
37
+ let handlers = this._handlers.get(event);
38
+ if (!handlers) {
39
+ handlers = /* @__PURE__ */ new Set();
40
+ this._handlers.set(event, handlers);
41
+ }
42
+ handlers.add(handler);
43
+ return () => {
44
+ handlers.delete(handler);
45
+ };
46
+ }
47
+ /**
48
+ * Subscribe to an event once. Auto-unsubscribes after first invocation.
49
+ */
50
+ once(event, handler) {
51
+ const unsubscribe = this.on(event, (payload) => {
52
+ unsubscribe();
53
+ handler(payload);
54
+ });
55
+ return unsubscribe;
56
+ }
57
+ /** Tears down the instance, releasing all subscriptions and resources. */
58
+ dispose() {
59
+ if (this._disposed) return;
60
+ this._disposed = true;
61
+ this._abortController?.abort();
62
+ if (this._cleanups) {
63
+ for (const fn of this._cleanups) fn();
64
+ this._cleanups = null;
65
+ }
66
+ this.onDispose?.();
67
+ this._handlers.clear();
68
+ }
69
+ /** Registers a cleanup function to be called on dispose. @protected */
70
+ addCleanup(fn) {
71
+ if (!this._cleanups) this._cleanups = [];
72
+ this._cleanups.push(fn);
73
+ }
88
74
  };
89
- //# sourceMappingURL=EventBus.js.map
75
+ //#endregion
76
+ export { EventBus };
77
+
78
+ //# sourceMappingURL=EventBus.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EventBus.js","sources":["../src/EventBus.ts"],"sourcesContent":["import type { Disposable } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst PROTECTED_KEYS = new Set(['addCleanup']);\ntype Handler<T> = (payload: T) => void;\n\n/**\n * Typed pub/sub event bus.\n */\nexport class EventBus<E extends Record<string, any>> implements Disposable {\n /** Phantom type brand — enables correct inference of E in generic helpers like useEvent(). */\n declare readonly _types: E;\n\n private _disposed = false;\n private _handlers = new Map<keyof E, Set<Handler<unknown>>>();\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 /** 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 * Emit an event with a payload.\n */\n emit<K extends keyof E>(event: K, payload: E[K]): void {\n if (this._disposed) {\n throw new Error('Cannot emit on disposed EventBus');\n }\n\n const handlers = this._handlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(payload);\n }\n }\n }\n\n /**\n * Subscribe to an event. Returns unsubscribe function.\n */\n on<K extends keyof E>(event: K, handler: Handler<E[K]>): () => void {\n if (this._disposed) {\n return () => {};\n }\n\n let handlers = this._handlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this._handlers.set(event, handlers);\n }\n\n handlers.add(handler as Handler<unknown>);\n\n return () => {\n handlers!.delete(handler as Handler<unknown>);\n };\n }\n\n /**\n * Subscribe to an event once. Auto-unsubscribes after first invocation.\n */\n once<K extends keyof E>(event: K, handler: Handler<E[K]>): () => void {\n const unsubscribe = this.on(event, (payload) => {\n unsubscribe();\n handler(payload);\n });\n return unsubscribe;\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\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.onDispose?.();\n this._handlers.clear();\n }\n\n /** Registers a cleanup function to be called on dispose. @protected */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n}\n"],"names":[],"mappings":";AAGA,MAAM,iBAAiB,oBAAI,IAAI,CAAC,YAAY,CAAC;AAMtC,MAAM,SAA8D;AAAA,EAIjE,YAAY;AAAA,EACZ,gCAAgB,IAAA;AAAA,EAChB,mBAA2C;AAAA,EAC3C,YAAmC;AAAA,EAE3C,cAAc;AACZ,sBAAkB,MAAM,OAAO,WAAW,cAAc;AAAA,EAC1D;AAAA;AAAA,EAGA,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,EAKA,KAAwB,OAAU,SAAqB;AACrD,QAAI,KAAK,WAAW;AAClB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAEA,UAAM,WAAW,KAAK,UAAU,IAAI,KAAK;AACzC,QAAI,UAAU;AACZ,iBAAW,WAAW,UAAU;AAC9B,gBAAQ,OAAO;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,GAAsB,OAAU,SAAoC;AAClE,QAAI,KAAK,WAAW;AAClB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AAEA,QAAI,WAAW,KAAK,UAAU,IAAI,KAAK;AACvC,QAAI,CAAC,UAAU;AACb,qCAAe,IAAA;AACf,WAAK,UAAU,IAAI,OAAO,QAAQ;AAAA,IACpC;AAEA,aAAS,IAAI,OAA2B;AAExC,WAAO,MAAM;AACX,eAAU,OAAO,OAA2B;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,KAAwB,OAAU,SAAoC;AACpE,UAAM,cAAc,KAAK,GAAG,OAAO,CAAC,YAAY;AAC9C,kBAAA;AACA,cAAQ,OAAO;AAAA,IACjB,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAAgB;AACd,QAAI,KAAK,WAAW;AAClB;AAAA,IACF;AAEA,SAAK,YAAY;AACjB,SAAK,kBAAkB,MAAA;AACvB,QAAI,KAAK,WAAW;AAClB,iBAAW,MAAM,KAAK,UAAW,IAAA;AACjC,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,YAAA;AACL,SAAK,UAAU,MAAA;AAAA,EACjB;AAAA;AAAA,EAGU,WAAW,IAAsB;AACzC,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,CAAA;AAAA,IACnB;AACA,SAAK,UAAU,KAAK,EAAE;AAAA,EACxB;AAIF;"}
1
+ {"version":3,"file":"EventBus.js","names":[],"sources":["../src/EventBus.ts"],"sourcesContent":["import type { Disposable } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst PROTECTED_KEYS = new Set(['addCleanup']);\ntype Handler<T> = (payload: T) => void;\n\n/**\n * Typed pub/sub event bus.\n */\nexport class EventBus<E extends Record<string, any>> implements Disposable {\n /** Phantom type brand — enables correct inference of E in generic helpers like useEvent(). */\n declare readonly _types: E;\n\n private _disposed = false;\n private _handlers = new Map<keyof E, Set<Handler<unknown>>>();\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 /** 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 * Emit an event with a payload.\n */\n emit<K extends keyof E>(event: K, payload: E[K]): void {\n if (this._disposed) {\n throw new Error('Cannot emit on disposed EventBus');\n }\n\n const handlers = this._handlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(payload);\n }\n }\n }\n\n /**\n * Subscribe to an event. Returns unsubscribe function.\n */\n on<K extends keyof E>(event: K, handler: Handler<E[K]>): () => void {\n if (this._disposed) {\n return () => {};\n }\n\n let handlers = this._handlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this._handlers.set(event, handlers);\n }\n\n handlers.add(handler as Handler<unknown>);\n\n return () => {\n handlers!.delete(handler as Handler<unknown>);\n };\n }\n\n /**\n * Subscribe to an event once. Auto-unsubscribes after first invocation.\n */\n once<K extends keyof E>(event: K, handler: Handler<E[K]>): () => void {\n const unsubscribe = this.on(event, (payload) => {\n unsubscribe();\n handler(payload);\n });\n return unsubscribe;\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\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.onDispose?.();\n this._handlers.clear();\n }\n\n /** Registers a cleanup function to be called on dispose. @protected */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n}\n"],"mappings":";;AAGA,IAAM,iBAAiB,IAAI,IAAI,CAAC,aAAa,CAAC;;;;AAM9C,IAAa,WAAb,MAA2E;CAIzE,YAAoB;CACpB,4BAAoB,IAAI,KAAqC;CAC7D,mBAAmD;CACnD,YAA2C;CAE3C,cAAc;AACZ,oBAAkB,MAAM,OAAO,WAAW,eAAe;;;CAI3D,IAAI,WAAoB;AACtB,SAAO,KAAK;;;CAId,IAAI,gBAA6B;AAC/B,MAAI,CAAC,KAAK,iBACR,MAAK,mBAAmB,IAAI,iBAAiB;AAE/C,SAAO,KAAK,iBAAiB;;;;;CAM/B,KAAwB,OAAU,SAAqB;AACrD,MAAI,KAAK,UACP,OAAM,IAAI,MAAM,mCAAmC;EAGrD,MAAM,WAAW,KAAK,UAAU,IAAI,MAAM;AAC1C,MAAI,SACF,MAAK,MAAM,WAAW,SACpB,SAAQ,QAAQ;;;;;CAQtB,GAAsB,OAAU,SAAoC;AAClE,MAAI,KAAK,UACP,cAAa;EAGf,IAAI,WAAW,KAAK,UAAU,IAAI,MAAM;AACxC,MAAI,CAAC,UAAU;AACb,8BAAW,IAAI,KAAK;AACpB,QAAK,UAAU,IAAI,OAAO,SAAS;;AAGrC,WAAS,IAAI,QAA4B;AAEzC,eAAa;AACX,YAAU,OAAO,QAA4B;;;;;;CAOjD,KAAwB,OAAU,SAAoC;EACpE,MAAM,cAAc,KAAK,GAAG,QAAQ,YAAY;AAC9C,gBAAa;AACb,WAAQ,QAAQ;IAChB;AACF,SAAO;;;CAIT,UAAgB;AACd,MAAI,KAAK,UACP;AAGF,OAAK,YAAY;AACjB,OAAK,kBAAkB,OAAO;AAC9B,MAAI,KAAK,WAAW;AAClB,QAAK,MAAM,MAAM,KAAK,UAAW,KAAI;AACrC,QAAK,YAAY;;AAEnB,OAAK,aAAa;AAClB,OAAK,UAAU,OAAO;;;CAIxB,WAAqB,IAAsB;AACzC,MAAI,CAAC,KAAK,UACR,MAAK,YAAY,EAAE;AAErB,OAAK,UAAU,KAAK,GAAG"}
package/dist/Feed.cjs CHANGED
@@ -1,78 +1,82 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const Trackable = require("./Trackable.cjs");
4
- class Feed extends Trackable.Trackable {
5
- _cursor = null;
6
- _hasMore = true;
7
- _items = Object.freeze([]);
8
- constructor() {
9
- super();
10
- }
11
- // ── Readable state ──
12
- /** Current cursor position for the next page fetch, or null if at the beginning. */
13
- get cursor() {
14
- return this._cursor;
15
- }
16
- /** Whether more pages are available from the server. */
17
- get hasMore() {
18
- return this._hasMore;
19
- }
20
- /** Accumulated items across all loaded pages. */
21
- get items() {
22
- return this._items;
23
- }
24
- /** Total number of accumulated items. */
25
- get count() {
26
- return this._items.length;
27
- }
28
- // ── Actions ──
29
- /** Update cursor/hasMore only (backward-compatible, does NOT affect items). */
30
- setResult(result) {
31
- this._hasMore = result.hasMore;
32
- this._cursor = result.cursor ?? null;
33
- this.notify();
34
- }
35
- /** Append page items and update cursor/hasMore. */
36
- appendPage(page) {
37
- this._items = Object.freeze([...this._items, ...page.items]);
38
- this._hasMore = page.hasMore;
39
- this._cursor = page.cursor ?? null;
40
- this.notify();
41
- }
42
- /** Prepend page items and update cursor/hasMore. */
43
- prependPage(page) {
44
- this._items = Object.freeze([...page.items, ...this._items]);
45
- this._hasMore = page.hasMore;
46
- this._cursor = page.cursor ?? null;
47
- this.notify();
48
- }
49
- /** Add items without affecting cursor/hasMore. */
50
- push(...items) {
51
- if (items.length === 0) return;
52
- this._items = Object.freeze([...this._items, ...items]);
53
- this.notify();
54
- }
55
- /** Remove items that don't match the predicate. No-op if nothing is filtered out. */
56
- filter(predicate) {
57
- const filtered = this._items.filter(predicate);
58
- if (filtered.length === this._items.length) return;
59
- this._items = Object.freeze(filtered);
60
- this.notify();
61
- }
62
- /** Replace all items and update cursor/hasMore atomically. Ideal for pull-to-refresh. */
63
- replacePage(page) {
64
- this._items = Object.freeze([...page.items]);
65
- this._hasMore = page.hasMore;
66
- this._cursor = page.cursor ?? null;
67
- this.notify();
68
- }
69
- /** Reset to initial empty state with hasMore=true. */
70
- reset() {
71
- this._cursor = null;
72
- this._hasMore = true;
73
- this._items = Object.freeze([]);
74
- this.notify();
75
- }
76
- }
1
+ const require_Trackable = require("./Trackable.cjs");
2
+ //#region src/Feed.ts
3
+ /**
4
+ * Cursor-based pagination state for server-side paginated feeds.
5
+ * Accumulates items across pages, tracks cursor position and hasMore flag.
6
+ * Subscribable — auto-tracked when used as a ViewModel property.
7
+ */
8
+ var Feed = class extends require_Trackable.Trackable {
9
+ _cursor = null;
10
+ _hasMore = true;
11
+ _items = Object.freeze([]);
12
+ constructor() {
13
+ super();
14
+ }
15
+ /** Current cursor position for the next page fetch, or null if at the beginning. */
16
+ get cursor() {
17
+ return this._cursor;
18
+ }
19
+ /** Whether more pages are available from the server. */
20
+ get hasMore() {
21
+ return this._hasMore;
22
+ }
23
+ /** Accumulated items across all loaded pages. */
24
+ get items() {
25
+ return this._items;
26
+ }
27
+ /** Total number of accumulated items. */
28
+ get count() {
29
+ return this._items.length;
30
+ }
31
+ /** Update cursor/hasMore only (backward-compatible, does NOT affect items). */
32
+ setResult(result) {
33
+ this._hasMore = result.hasMore;
34
+ this._cursor = result.cursor ?? null;
35
+ this.notify();
36
+ }
37
+ /** Append page items and update cursor/hasMore. */
38
+ appendPage(page) {
39
+ this._items = Object.freeze([...this._items, ...page.items]);
40
+ this._hasMore = page.hasMore;
41
+ this._cursor = page.cursor ?? null;
42
+ this.notify();
43
+ }
44
+ /** Prepend page items and update cursor/hasMore. */
45
+ prependPage(page) {
46
+ this._items = Object.freeze([...page.items, ...this._items]);
47
+ this._hasMore = page.hasMore;
48
+ this._cursor = page.cursor ?? null;
49
+ this.notify();
50
+ }
51
+ /** Add items without affecting cursor/hasMore. */
52
+ push(...items) {
53
+ if (items.length === 0) return;
54
+ this._items = Object.freeze([...this._items, ...items]);
55
+ this.notify();
56
+ }
57
+ /** Remove items that don't match the predicate. No-op if nothing is filtered out. */
58
+ filter(predicate) {
59
+ const filtered = this._items.filter(predicate);
60
+ if (filtered.length === this._items.length) return;
61
+ this._items = Object.freeze(filtered);
62
+ this.notify();
63
+ }
64
+ /** Replace all items and update cursor/hasMore atomically. Ideal for pull-to-refresh. */
65
+ replacePage(page) {
66
+ this._items = Object.freeze([...page.items]);
67
+ this._hasMore = page.hasMore;
68
+ this._cursor = page.cursor ?? null;
69
+ this.notify();
70
+ }
71
+ /** Reset to initial empty state with hasMore=true. */
72
+ reset() {
73
+ this._cursor = null;
74
+ this._hasMore = true;
75
+ this._items = Object.freeze([]);
76
+ this.notify();
77
+ }
78
+ };
79
+ //#endregion
77
80
  exports.Feed = Feed;
78
- //# sourceMappingURL=Feed.cjs.map
81
+
82
+ //# sourceMappingURL=Feed.cjs.map
package/dist/Feed.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Feed.cjs","sources":["../src/Feed.ts"],"sourcesContent":["import { Trackable } from './Trackable';\n\n/** Represents a page of items from a paginated API response. */\nexport interface FeedPage<T> {\n items: T[];\n hasMore: boolean;\n cursor?: string | null;\n}\n\n/**\n * Cursor-based pagination state for server-side paginated feeds.\n * Accumulates items across pages, tracks cursor position and hasMore flag.\n * Subscribable — auto-tracked when used as a ViewModel property.\n */\nexport class Feed<T = unknown> extends Trackable {\n private _cursor: string | null = null;\n private _hasMore: boolean = true;\n private _items: readonly T[] = Object.freeze([] as T[]);\n\n constructor() {\n super();\n }\n\n // ── Readable state ──\n\n /** Current cursor position for the next page fetch, or null if at the beginning. */\n get cursor(): string | null {\n return this._cursor;\n }\n\n /** Whether more pages are available from the server. */\n get hasMore(): boolean {\n return this._hasMore;\n }\n\n /** Accumulated items across all loaded pages. */\n get items(): readonly T[] {\n return this._items;\n }\n\n /** Total number of accumulated items. */\n get count(): number {\n return this._items.length;\n }\n\n // ── Actions ──\n\n /** Update cursor/hasMore only (backward-compatible, does NOT affect items). */\n setResult(result: { hasMore: boolean; cursor?: string | null }): void {\n this._hasMore = result.hasMore;\n this._cursor = result.cursor ?? null;\n this.notify();\n }\n\n /** Append page items and update cursor/hasMore. */\n appendPage(page: FeedPage<T>): void {\n this._items = Object.freeze([...this._items, ...page.items]);\n this._hasMore = page.hasMore;\n this._cursor = page.cursor ?? null;\n this.notify();\n }\n\n /** Prepend page items and update cursor/hasMore. */\n prependPage(page: FeedPage<T>): void {\n this._items = Object.freeze([...page.items, ...this._items]);\n this._hasMore = page.hasMore;\n this._cursor = page.cursor ?? null;\n this.notify();\n }\n\n /** Add items without affecting cursor/hasMore. */\n push(...items: T[]): void {\n if (items.length === 0) return;\n this._items = Object.freeze([...this._items, ...items]);\n this.notify();\n }\n\n /** Remove items that don't match the predicate. No-op if nothing is filtered out. */\n filter(predicate: (item: T) => boolean): void {\n const filtered = this._items.filter(predicate);\n if (filtered.length === this._items.length) return;\n this._items = Object.freeze(filtered);\n this.notify();\n }\n\n /** Replace all items and update cursor/hasMore atomically. Ideal for pull-to-refresh. */\n replacePage(page: FeedPage<T>): void {\n this._items = Object.freeze([...page.items]);\n this._hasMore = page.hasMore;\n this._cursor = page.cursor ?? null;\n this.notify();\n }\n\n /** Reset to initial empty state with hasMore=true. */\n reset(): void {\n this._cursor = null;\n this._hasMore = true;\n this._items = Object.freeze([] as T[]);\n this.notify();\n }\n}\n"],"names":["Trackable"],"mappings":";;;AAcO,MAAM,aAA0BA,UAAAA,UAAU;AAAA,EACvC,UAAyB;AAAA,EACzB,WAAoB;AAAA,EACpB,SAAuB,OAAO,OAAO,EAAS;AAAA,EAEtD,cAAc;AACZ,UAAA;AAAA,EACF;AAAA;AAAA;AAAA,EAKA,IAAI,SAAwB;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,UAAmB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,QAAsB;AACxB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,QAAgB;AAClB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA,EAKA,UAAU,QAA4D;AACpE,SAAK,WAAW,OAAO;AACvB,SAAK,UAAU,OAAO,UAAU;AAChC,SAAK,OAAA;AAAA,EACP;AAAA;AAAA,EAGA,WAAW,MAAyB;AAClC,SAAK,SAAS,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,GAAG,KAAK,KAAK,CAAC;AAC3D,SAAK,WAAW,KAAK;AACrB,SAAK,UAAU,KAAK,UAAU;AAC9B,SAAK,OAAA;AAAA,EACP;AAAA;AAAA,EAGA,YAAY,MAAyB;AACnC,SAAK,SAAS,OAAO,OAAO,CAAC,GAAG,KAAK,OAAO,GAAG,KAAK,MAAM,CAAC;AAC3D,SAAK,WAAW,KAAK;AACrB,SAAK,UAAU,KAAK,UAAU;AAC9B,SAAK,OAAA;AAAA,EACP;AAAA;AAAA,EAGA,QAAQ,OAAkB;AACxB,QAAI,MAAM,WAAW,EAAG;AACxB,SAAK,SAAS,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,GAAG,KAAK,CAAC;AACtD,SAAK,OAAA;AAAA,EACP;AAAA;AAAA,EAGA,OAAO,WAAuC;AAC5C,UAAM,WAAW,KAAK,OAAO,OAAO,SAAS;AAC7C,QAAI,SAAS,WAAW,KAAK,OAAO,OAAQ;AAC5C,SAAK,SAAS,OAAO,OAAO,QAAQ;AACpC,SAAK,OAAA;AAAA,EACP;AAAA;AAAA,EAGA,YAAY,MAAyB;AACnC,SAAK,SAAS,OAAO,OAAO,CAAC,GAAG,KAAK,KAAK,CAAC;AAC3C,SAAK,WAAW,KAAK;AACrB,SAAK,UAAU,KAAK,UAAU;AAC9B,SAAK,OAAA;AAAA,EACP;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,UAAU;AACf,SAAK,WAAW;AAChB,SAAK,SAAS,OAAO,OAAO,CAAA,CAAS;AACrC,SAAK,OAAA;AAAA,EACP;AACF;;"}
1
+ {"version":3,"file":"Feed.cjs","names":[],"sources":["../src/Feed.ts"],"sourcesContent":["import { Trackable } from './Trackable';\n\n/** Represents a page of items from a paginated API response. */\nexport interface FeedPage<T> {\n items: T[];\n hasMore: boolean;\n cursor?: string | null;\n}\n\n/**\n * Cursor-based pagination state for server-side paginated feeds.\n * Accumulates items across pages, tracks cursor position and hasMore flag.\n * Subscribable — auto-tracked when used as a ViewModel property.\n */\nexport class Feed<T = unknown> extends Trackable {\n private _cursor: string | null = null;\n private _hasMore: boolean = true;\n private _items: readonly T[] = Object.freeze([] as T[]);\n\n constructor() {\n super();\n }\n\n // ── Readable state ──\n\n /** Current cursor position for the next page fetch, or null if at the beginning. */\n get cursor(): string | null {\n return this._cursor;\n }\n\n /** Whether more pages are available from the server. */\n get hasMore(): boolean {\n return this._hasMore;\n }\n\n /** Accumulated items across all loaded pages. */\n get items(): readonly T[] {\n return this._items;\n }\n\n /** Total number of accumulated items. */\n get count(): number {\n return this._items.length;\n }\n\n // ── Actions ──\n\n /** Update cursor/hasMore only (backward-compatible, does NOT affect items). */\n setResult(result: { hasMore: boolean; cursor?: string | null }): void {\n this._hasMore = result.hasMore;\n this._cursor = result.cursor ?? null;\n this.notify();\n }\n\n /** Append page items and update cursor/hasMore. */\n appendPage(page: FeedPage<T>): void {\n this._items = Object.freeze([...this._items, ...page.items]);\n this._hasMore = page.hasMore;\n this._cursor = page.cursor ?? null;\n this.notify();\n }\n\n /** Prepend page items and update cursor/hasMore. */\n prependPage(page: FeedPage<T>): void {\n this._items = Object.freeze([...page.items, ...this._items]);\n this._hasMore = page.hasMore;\n this._cursor = page.cursor ?? null;\n this.notify();\n }\n\n /** Add items without affecting cursor/hasMore. */\n push(...items: T[]): void {\n if (items.length === 0) return;\n this._items = Object.freeze([...this._items, ...items]);\n this.notify();\n }\n\n /** Remove items that don't match the predicate. No-op if nothing is filtered out. */\n filter(predicate: (item: T) => boolean): void {\n const filtered = this._items.filter(predicate);\n if (filtered.length === this._items.length) return;\n this._items = Object.freeze(filtered);\n this.notify();\n }\n\n /** Replace all items and update cursor/hasMore atomically. Ideal for pull-to-refresh. */\n replacePage(page: FeedPage<T>): void {\n this._items = Object.freeze([...page.items]);\n this._hasMore = page.hasMore;\n this._cursor = page.cursor ?? null;\n this.notify();\n }\n\n /** Reset to initial empty state with hasMore=true. */\n reset(): void {\n this._cursor = null;\n this._hasMore = true;\n this._items = Object.freeze([] as T[]);\n this.notify();\n }\n}\n"],"mappings":";;;;;;;AAcA,IAAa,OAAb,cAAuC,kBAAA,UAAU;CAC/C,UAAiC;CACjC,WAA4B;CAC5B,SAA+B,OAAO,OAAO,EAAE,CAAQ;CAEvD,cAAc;AACZ,SAAO;;;CAMT,IAAI,SAAwB;AAC1B,SAAO,KAAK;;;CAId,IAAI,UAAmB;AACrB,SAAO,KAAK;;;CAId,IAAI,QAAsB;AACxB,SAAO,KAAK;;;CAId,IAAI,QAAgB;AAClB,SAAO,KAAK,OAAO;;;CAMrB,UAAU,QAA4D;AACpE,OAAK,WAAW,OAAO;AACvB,OAAK,UAAU,OAAO,UAAU;AAChC,OAAK,QAAQ;;;CAIf,WAAW,MAAyB;AAClC,OAAK,SAAS,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,GAAG,KAAK,MAAM,CAAC;AAC5D,OAAK,WAAW,KAAK;AACrB,OAAK,UAAU,KAAK,UAAU;AAC9B,OAAK,QAAQ;;;CAIf,YAAY,MAAyB;AACnC,OAAK,SAAS,OAAO,OAAO,CAAC,GAAG,KAAK,OAAO,GAAG,KAAK,OAAO,CAAC;AAC5D,OAAK,WAAW,KAAK;AACrB,OAAK,UAAU,KAAK,UAAU;AAC9B,OAAK,QAAQ;;;CAIf,KAAK,GAAG,OAAkB;AACxB,MAAI,MAAM,WAAW,EAAG;AACxB,OAAK,SAAS,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,GAAG,MAAM,CAAC;AACvD,OAAK,QAAQ;;;CAIf,OAAO,WAAuC;EAC5C,MAAM,WAAW,KAAK,OAAO,OAAO,UAAU;AAC9C,MAAI,SAAS,WAAW,KAAK,OAAO,OAAQ;AAC5C,OAAK,SAAS,OAAO,OAAO,SAAS;AACrC,OAAK,QAAQ;;;CAIf,YAAY,MAAyB;AACnC,OAAK,SAAS,OAAO,OAAO,CAAC,GAAG,KAAK,MAAM,CAAC;AAC5C,OAAK,WAAW,KAAK;AACrB,OAAK,UAAU,KAAK,UAAU;AAC9B,OAAK,QAAQ;;;CAIf,QAAc;AACZ,OAAK,UAAU;AACf,OAAK,WAAW;AAChB,OAAK,SAAS,OAAO,OAAO,EAAE,CAAQ;AACtC,OAAK,QAAQ"}