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.
- package/agent-config/bin/postinstall.mjs +4 -3
- package/agent-config/bin/setup.mjs +5 -1
- package/agent-config/claude-code/agents/mvc-kit-architect.md +11 -8
- package/agent-config/claude-code/skills/guide/SKILL.md +20 -7
- package/agent-config/claude-code/skills/guide/patterns.md +12 -0
- package/agent-config/claude-code/skills/guide/recipes.md +510 -0
- package/agent-config/claude-code/skills/guide/testing.md +297 -0
- package/agent-config/claude-code/skills/review/SKILL.md +3 -13
- package/agent-config/claude-code/skills/review/checklist.md +30 -5
- package/agent-config/claude-code/skills/scaffold/SKILL.md +4 -13
- package/agent-config/lib/install-claude.mjs +84 -25
- package/dist/Channel.cjs +276 -300
- package/dist/Channel.cjs.map +1 -1
- package/dist/Channel.js +275 -299
- package/dist/Channel.js.map +1 -1
- package/dist/Collection.cjs +424 -504
- package/dist/Collection.cjs.map +1 -1
- package/dist/Collection.js +423 -503
- package/dist/Collection.js.map +1 -1
- package/dist/Controller.cjs +70 -67
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.js +69 -66
- package/dist/Controller.js.map +1 -1
- package/dist/EventBus.cjs +77 -88
- package/dist/EventBus.cjs.map +1 -1
- package/dist/EventBus.js +76 -87
- package/dist/EventBus.js.map +1 -1
- package/dist/Feed.cjs +81 -77
- package/dist/Feed.cjs.map +1 -1
- package/dist/Feed.js +80 -76
- package/dist/Feed.js.map +1 -1
- package/dist/Model.cjs +181 -207
- package/dist/Model.cjs.map +1 -1
- package/dist/Model.js +179 -205
- package/dist/Model.js.map +1 -1
- package/dist/Pagination.cjs +75 -73
- package/dist/Pagination.cjs.map +1 -1
- package/dist/Pagination.js +74 -72
- package/dist/Pagination.js.map +1 -1
- package/dist/Pending.cjs +255 -287
- package/dist/Pending.cjs.map +1 -1
- package/dist/Pending.js +253 -285
- package/dist/Pending.js.map +1 -1
- package/dist/PersistentCollection.cjs +242 -285
- package/dist/PersistentCollection.cjs.map +1 -1
- package/dist/PersistentCollection.js +241 -284
- package/dist/PersistentCollection.js.map +1 -1
- package/dist/Resource.cjs +166 -174
- package/dist/Resource.cjs.map +1 -1
- package/dist/Resource.js +164 -172
- package/dist/Resource.js.map +1 -1
- package/dist/Selection.cjs +84 -94
- package/dist/Selection.cjs.map +1 -1
- package/dist/Selection.js +83 -93
- package/dist/Selection.js.map +1 -1
- package/dist/Service.cjs +54 -55
- package/dist/Service.cjs.map +1 -1
- package/dist/Service.js +53 -54
- package/dist/Service.js.map +1 -1
- package/dist/Sorting.cjs +102 -101
- package/dist/Sorting.cjs.map +1 -1
- package/dist/Sorting.js +102 -101
- package/dist/Sorting.js.map +1 -1
- package/dist/Trackable.cjs +112 -80
- package/dist/Trackable.cjs.map +1 -1
- package/dist/Trackable.js +111 -79
- package/dist/Trackable.js.map +1 -1
- package/dist/ViewModel.cjs +528 -576
- package/dist/ViewModel.cjs.map +1 -1
- package/dist/ViewModel.js +525 -573
- package/dist/ViewModel.js.map +1 -1
- package/dist/bindPublicMethods.cjs +43 -24
- package/dist/bindPublicMethods.cjs.map +1 -1
- package/dist/bindPublicMethods.js +43 -24
- package/dist/bindPublicMethods.js.map +1 -1
- package/dist/errors.cjs +67 -68
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js +68 -71
- package/dist/errors.js.map +1 -1
- package/dist/mvc-kit.cjs +44 -46
- package/dist/mvc-kit.js +5 -32
- package/dist/produceDraft.cjs +105 -95
- package/dist/produceDraft.cjs.map +1 -1
- package/dist/produceDraft.js +106 -97
- package/dist/produceDraft.js.map +1 -1
- package/dist/react/components/CardList.cjs +30 -40
- package/dist/react/components/CardList.cjs.map +1 -1
- package/dist/react/components/CardList.js +31 -41
- package/dist/react/components/CardList.js.map +1 -1
- package/dist/react/components/DataTable.cjs +146 -169
- package/dist/react/components/DataTable.cjs.map +1 -1
- package/dist/react/components/DataTable.js +147 -170
- package/dist/react/components/DataTable.js.map +1 -1
- package/dist/react/components/InfiniteScroll.cjs +51 -42
- package/dist/react/components/InfiniteScroll.cjs.map +1 -1
- package/dist/react/components/InfiniteScroll.js +52 -43
- package/dist/react/components/InfiniteScroll.js.map +1 -1
- package/dist/react/components/types.cjs +10 -6
- package/dist/react/components/types.cjs.map +1 -1
- package/dist/react/components/types.js +11 -9
- package/dist/react/components/types.js.map +1 -1
- package/dist/react/guards.cjs +10 -6
- package/dist/react/guards.cjs.map +1 -1
- package/dist/react/guards.js +11 -9
- package/dist/react/guards.js.map +1 -1
- package/dist/react/provider.cjs +23 -20
- package/dist/react/provider.cjs.map +1 -1
- package/dist/react/provider.js +23 -21
- package/dist/react/provider.js.map +1 -1
- package/dist/react/use-event-bus.cjs +24 -20
- package/dist/react/use-event-bus.cjs.map +1 -1
- package/dist/react/use-event-bus.js +24 -21
- package/dist/react/use-event-bus.js.map +1 -1
- package/dist/react/use-instance.cjs +43 -36
- package/dist/react/use-instance.cjs.map +1 -1
- package/dist/react/use-instance.js +43 -36
- package/dist/react/use-instance.js.map +1 -1
- package/dist/react/use-local.cjs +48 -64
- package/dist/react/use-local.cjs.map +1 -1
- package/dist/react/use-local.js +47 -63
- package/dist/react/use-local.js.map +1 -1
- package/dist/react/use-model.cjs +84 -98
- package/dist/react/use-model.cjs.map +1 -1
- package/dist/react/use-model.js +84 -100
- package/dist/react/use-model.js.map +1 -1
- package/dist/react/use-singleton.cjs +19 -23
- package/dist/react/use-singleton.cjs.map +1 -1
- package/dist/react/use-singleton.js +16 -20
- package/dist/react/use-singleton.js.map +1 -1
- package/dist/react/use-subscribe-only.cjs +28 -22
- package/dist/react/use-subscribe-only.cjs.map +1 -1
- package/dist/react/use-subscribe-only.js +28 -22
- package/dist/react/use-subscribe-only.js.map +1 -1
- package/dist/react/use-teardown.cjs +20 -19
- package/dist/react/use-teardown.cjs.map +1 -1
- package/dist/react/use-teardown.js +20 -19
- package/dist/react/use-teardown.js.map +1 -1
- package/dist/react-native/NativeCollection.cjs +98 -78
- package/dist/react-native/NativeCollection.cjs.map +1 -1
- package/dist/react-native/NativeCollection.js +97 -77
- package/dist/react-native/NativeCollection.js.map +1 -1
- package/dist/react-native.cjs +2 -4
- package/dist/react-native.js +1 -4
- package/dist/react.cjs +24 -26
- package/dist/react.js +1 -17
- package/dist/singleton.cjs +28 -22
- package/dist/singleton.cjs.map +1 -1
- package/dist/singleton.js +29 -26
- package/dist/singleton.js.map +1 -1
- package/dist/walkPrototypeChain.cjs +20 -12
- package/dist/walkPrototypeChain.cjs.map +1 -1
- package/dist/walkPrototypeChain.js +21 -13
- package/dist/walkPrototypeChain.js.map +1 -1
- package/dist/web/IndexedDBCollection.cjs +53 -36
- package/dist/web/IndexedDBCollection.cjs.map +1 -1
- package/dist/web/IndexedDBCollection.js +52 -35
- package/dist/web/IndexedDBCollection.js.map +1 -1
- package/dist/web/WebStorageCollection.cjs +82 -84
- package/dist/web/WebStorageCollection.cjs.map +1 -1
- package/dist/web/WebStorageCollection.js +81 -83
- package/dist/web/WebStorageCollection.js.map +1 -1
- package/dist/web/idb.cjs +107 -99
- package/dist/web/idb.cjs.map +1 -1
- package/dist/web/idb.js +108 -105
- package/dist/web/idb.js.map +1 -1
- package/dist/web.cjs +4 -6
- package/dist/web.js +1 -5
- package/dist/wrapAsyncMethods.cjs +141 -168
- package/dist/wrapAsyncMethods.cjs.map +1 -1
- package/dist/wrapAsyncMethods.js +141 -168
- package/dist/wrapAsyncMethods.js.map +1 -1
- package/package.json +8 -8
- package/src/Pending.test.ts +1 -2
- package/src/Sorting.test.ts +1 -1
- package/src/produceDraft.test.ts +3 -3
- package/src/react/components/CardList.test.tsx +1 -1
- package/src/react/components/DataTable.test.tsx +1 -1
- package/src/react/components/InfiniteScroll.test.tsx +5 -5
- package/dist/mvc-kit.cjs.map +0 -1
- package/dist/mvc-kit.js.map +0 -1
- package/dist/react-native.cjs.map +0 -1
- package/dist/react-native.js.map +0 -1
- package/dist/react.cjs.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/web.cjs.map +0 -1
- package/dist/web.js.map +0 -1
package/dist/EventBus.cjs
CHANGED
|
@@ -1,89 +1,78 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=EventBus.cjs.map
|
package/dist/EventBus.cjs.map
CHANGED
|
@@ -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"],"
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
//#
|
|
75
|
+
//#endregion
|
|
76
|
+
export { EventBus };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=EventBus.js.map
|
package/dist/EventBus.js.map
CHANGED
|
@@ -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"],"
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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"],"
|
|
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"}
|