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