mvc-kit 2.12.4 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/agent-config/bin/postinstall.mjs +4 -3
  2. package/agent-config/bin/setup.mjs +5 -1
  3. package/agent-config/claude-code/agents/mvc-kit-architect.md +11 -8
  4. package/agent-config/claude-code/skills/guide/SKILL.md +20 -7
  5. package/agent-config/claude-code/skills/guide/patterns.md +12 -0
  6. package/agent-config/claude-code/skills/guide/recipes.md +510 -0
  7. package/agent-config/claude-code/skills/guide/testing.md +297 -0
  8. package/agent-config/claude-code/skills/review/SKILL.md +3 -13
  9. package/agent-config/claude-code/skills/review/checklist.md +30 -5
  10. package/agent-config/claude-code/skills/scaffold/SKILL.md +4 -13
  11. package/agent-config/lib/install-claude.mjs +84 -25
  12. package/dist/Channel.cjs +276 -300
  13. package/dist/Channel.cjs.map +1 -1
  14. package/dist/Channel.js +275 -299
  15. package/dist/Channel.js.map +1 -1
  16. package/dist/Collection.cjs +424 -504
  17. package/dist/Collection.cjs.map +1 -1
  18. package/dist/Collection.js +423 -503
  19. package/dist/Collection.js.map +1 -1
  20. package/dist/Controller.cjs +70 -67
  21. package/dist/Controller.cjs.map +1 -1
  22. package/dist/Controller.js +69 -66
  23. package/dist/Controller.js.map +1 -1
  24. package/dist/EventBus.cjs +77 -88
  25. package/dist/EventBus.cjs.map +1 -1
  26. package/dist/EventBus.js +76 -87
  27. package/dist/EventBus.js.map +1 -1
  28. package/dist/Feed.cjs +81 -77
  29. package/dist/Feed.cjs.map +1 -1
  30. package/dist/Feed.js +80 -76
  31. package/dist/Feed.js.map +1 -1
  32. package/dist/Model.cjs +181 -207
  33. package/dist/Model.cjs.map +1 -1
  34. package/dist/Model.js +179 -205
  35. package/dist/Model.js.map +1 -1
  36. package/dist/Pagination.cjs +75 -73
  37. package/dist/Pagination.cjs.map +1 -1
  38. package/dist/Pagination.js +74 -72
  39. package/dist/Pagination.js.map +1 -1
  40. package/dist/Pending.cjs +255 -287
  41. package/dist/Pending.cjs.map +1 -1
  42. package/dist/Pending.js +253 -285
  43. package/dist/Pending.js.map +1 -1
  44. package/dist/PersistentCollection.cjs +242 -285
  45. package/dist/PersistentCollection.cjs.map +1 -1
  46. package/dist/PersistentCollection.js +241 -284
  47. package/dist/PersistentCollection.js.map +1 -1
  48. package/dist/Resource.cjs +166 -174
  49. package/dist/Resource.cjs.map +1 -1
  50. package/dist/Resource.js +164 -172
  51. package/dist/Resource.js.map +1 -1
  52. package/dist/Selection.cjs +84 -94
  53. package/dist/Selection.cjs.map +1 -1
  54. package/dist/Selection.js +83 -93
  55. package/dist/Selection.js.map +1 -1
  56. package/dist/Service.cjs +54 -55
  57. package/dist/Service.cjs.map +1 -1
  58. package/dist/Service.js +53 -54
  59. package/dist/Service.js.map +1 -1
  60. package/dist/Sorting.cjs +102 -101
  61. package/dist/Sorting.cjs.map +1 -1
  62. package/dist/Sorting.js +102 -101
  63. package/dist/Sorting.js.map +1 -1
  64. package/dist/Trackable.cjs +112 -80
  65. package/dist/Trackable.cjs.map +1 -1
  66. package/dist/Trackable.js +111 -79
  67. package/dist/Trackable.js.map +1 -1
  68. package/dist/ViewModel.cjs +528 -576
  69. package/dist/ViewModel.cjs.map +1 -1
  70. package/dist/ViewModel.js +525 -573
  71. package/dist/ViewModel.js.map +1 -1
  72. package/dist/bindPublicMethods.cjs +43 -24
  73. package/dist/bindPublicMethods.cjs.map +1 -1
  74. package/dist/bindPublicMethods.js +43 -24
  75. package/dist/bindPublicMethods.js.map +1 -1
  76. package/dist/errors.cjs +67 -68
  77. package/dist/errors.cjs.map +1 -1
  78. package/dist/errors.js +68 -71
  79. package/dist/errors.js.map +1 -1
  80. package/dist/mvc-kit.cjs +44 -46
  81. package/dist/mvc-kit.js +5 -32
  82. package/dist/produceDraft.cjs +105 -95
  83. package/dist/produceDraft.cjs.map +1 -1
  84. package/dist/produceDraft.js +106 -97
  85. package/dist/produceDraft.js.map +1 -1
  86. package/dist/react/components/CardList.cjs +30 -40
  87. package/dist/react/components/CardList.cjs.map +1 -1
  88. package/dist/react/components/CardList.js +31 -41
  89. package/dist/react/components/CardList.js.map +1 -1
  90. package/dist/react/components/DataTable.cjs +146 -169
  91. package/dist/react/components/DataTable.cjs.map +1 -1
  92. package/dist/react/components/DataTable.js +147 -170
  93. package/dist/react/components/DataTable.js.map +1 -1
  94. package/dist/react/components/InfiniteScroll.cjs +51 -42
  95. package/dist/react/components/InfiniteScroll.cjs.map +1 -1
  96. package/dist/react/components/InfiniteScroll.js +52 -43
  97. package/dist/react/components/InfiniteScroll.js.map +1 -1
  98. package/dist/react/components/types.cjs +10 -6
  99. package/dist/react/components/types.cjs.map +1 -1
  100. package/dist/react/components/types.js +11 -9
  101. package/dist/react/components/types.js.map +1 -1
  102. package/dist/react/guards.cjs +10 -6
  103. package/dist/react/guards.cjs.map +1 -1
  104. package/dist/react/guards.js +11 -9
  105. package/dist/react/guards.js.map +1 -1
  106. package/dist/react/provider.cjs +23 -20
  107. package/dist/react/provider.cjs.map +1 -1
  108. package/dist/react/provider.js +23 -21
  109. package/dist/react/provider.js.map +1 -1
  110. package/dist/react/use-event-bus.cjs +24 -20
  111. package/dist/react/use-event-bus.cjs.map +1 -1
  112. package/dist/react/use-event-bus.js +24 -21
  113. package/dist/react/use-event-bus.js.map +1 -1
  114. package/dist/react/use-instance.cjs +43 -36
  115. package/dist/react/use-instance.cjs.map +1 -1
  116. package/dist/react/use-instance.js +43 -36
  117. package/dist/react/use-instance.js.map +1 -1
  118. package/dist/react/use-local.cjs +48 -64
  119. package/dist/react/use-local.cjs.map +1 -1
  120. package/dist/react/use-local.js +47 -63
  121. package/dist/react/use-local.js.map +1 -1
  122. package/dist/react/use-model.cjs +84 -98
  123. package/dist/react/use-model.cjs.map +1 -1
  124. package/dist/react/use-model.js +84 -100
  125. package/dist/react/use-model.js.map +1 -1
  126. package/dist/react/use-singleton.cjs +19 -23
  127. package/dist/react/use-singleton.cjs.map +1 -1
  128. package/dist/react/use-singleton.js +16 -20
  129. package/dist/react/use-singleton.js.map +1 -1
  130. package/dist/react/use-subscribe-only.cjs +28 -22
  131. package/dist/react/use-subscribe-only.cjs.map +1 -1
  132. package/dist/react/use-subscribe-only.js +28 -22
  133. package/dist/react/use-subscribe-only.js.map +1 -1
  134. package/dist/react/use-teardown.cjs +20 -19
  135. package/dist/react/use-teardown.cjs.map +1 -1
  136. package/dist/react/use-teardown.js +20 -19
  137. package/dist/react/use-teardown.js.map +1 -1
  138. package/dist/react-native/NativeCollection.cjs +98 -78
  139. package/dist/react-native/NativeCollection.cjs.map +1 -1
  140. package/dist/react-native/NativeCollection.js +97 -77
  141. package/dist/react-native/NativeCollection.js.map +1 -1
  142. package/dist/react-native.cjs +2 -4
  143. package/dist/react-native.js +1 -4
  144. package/dist/react.cjs +24 -26
  145. package/dist/react.js +1 -17
  146. package/dist/singleton.cjs +28 -22
  147. package/dist/singleton.cjs.map +1 -1
  148. package/dist/singleton.js +29 -26
  149. package/dist/singleton.js.map +1 -1
  150. package/dist/walkPrototypeChain.cjs +20 -12
  151. package/dist/walkPrototypeChain.cjs.map +1 -1
  152. package/dist/walkPrototypeChain.js +21 -13
  153. package/dist/walkPrototypeChain.js.map +1 -1
  154. package/dist/web/IndexedDBCollection.cjs +53 -36
  155. package/dist/web/IndexedDBCollection.cjs.map +1 -1
  156. package/dist/web/IndexedDBCollection.js +52 -35
  157. package/dist/web/IndexedDBCollection.js.map +1 -1
  158. package/dist/web/WebStorageCollection.cjs +82 -84
  159. package/dist/web/WebStorageCollection.cjs.map +1 -1
  160. package/dist/web/WebStorageCollection.js +81 -83
  161. package/dist/web/WebStorageCollection.js.map +1 -1
  162. package/dist/web/idb.cjs +107 -99
  163. package/dist/web/idb.cjs.map +1 -1
  164. package/dist/web/idb.js +108 -105
  165. package/dist/web/idb.js.map +1 -1
  166. package/dist/web.cjs +4 -6
  167. package/dist/web.js +1 -5
  168. package/dist/wrapAsyncMethods.cjs +141 -168
  169. package/dist/wrapAsyncMethods.cjs.map +1 -1
  170. package/dist/wrapAsyncMethods.js +141 -168
  171. package/dist/wrapAsyncMethods.js.map +1 -1
  172. package/package.json +8 -8
  173. package/src/Pending.test.ts +1 -2
  174. package/src/Sorting.test.ts +1 -1
  175. package/src/produceDraft.test.ts +3 -3
  176. package/src/react/components/CardList.test.tsx +1 -1
  177. package/src/react/components/DataTable.test.tsx +1 -1
  178. package/src/react/components/InfiniteScroll.test.tsx +5 -5
  179. package/dist/mvc-kit.cjs.map +0 -1
  180. package/dist/mvc-kit.js.map +0 -1
  181. package/dist/react-native.cjs.map +0 -1
  182. package/dist/react-native.js.map +0 -1
  183. package/dist/react.cjs.map +0 -1
  184. package/dist/react.js.map +0 -1
  185. package/dist/web.cjs.map +0 -1
  186. package/dist/web.js.map +0 -1
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"}