mvc-kit 2.12.5 → 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
|
@@ -1,41 +1,48 @@
|
|
|
1
1
|
import { useRef, useSyncExternalStore } from "react";
|
|
2
|
+
//#region src/react/use-instance.ts
|
|
2
3
|
function hasAsyncSubscription(obj) {
|
|
3
|
-
|
|
4
|
+
return obj !== null && typeof obj === "object" && typeof obj.subscribeAsync === "function";
|
|
4
5
|
}
|
|
5
|
-
|
|
6
|
+
var SERVER_SNAPSHOT = () => 0;
|
|
7
|
+
/**
|
|
8
|
+
* Subscribe to an existing Subscribable instance.
|
|
9
|
+
* No ownership - caller manages the instance lifecycle.
|
|
10
|
+
*
|
|
11
|
+
* If the instance has a `subscribeAsync` method (duck-typed),
|
|
12
|
+
* a combined subscription ensures async state changes also
|
|
13
|
+
* trigger React re-renders.
|
|
14
|
+
*/
|
|
6
15
|
function useInstance(subscribable) {
|
|
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
|
-
useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);
|
|
36
|
-
return subscribable.state;
|
|
16
|
+
const ref = useRef(null);
|
|
17
|
+
if (!ref.current || ref.current.subscribable !== subscribable) {
|
|
18
|
+
const version = { current: ref.current?.version ?? 0 };
|
|
19
|
+
ref.current = {
|
|
20
|
+
version: version.current,
|
|
21
|
+
subscribable,
|
|
22
|
+
subscribe: (onStoreChange) => {
|
|
23
|
+
const unsub1 = subscribable.subscribe(() => {
|
|
24
|
+
version.current++;
|
|
25
|
+
ref.current.version = version.current;
|
|
26
|
+
onStoreChange();
|
|
27
|
+
});
|
|
28
|
+
let unsub2;
|
|
29
|
+
if (hasAsyncSubscription(subscribable)) unsub2 = subscribable.subscribeAsync(() => {
|
|
30
|
+
version.current++;
|
|
31
|
+
ref.current.version = version.current;
|
|
32
|
+
onStoreChange();
|
|
33
|
+
});
|
|
34
|
+
return () => {
|
|
35
|
+
unsub1();
|
|
36
|
+
unsub2?.();
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
getSnapshot: () => version.current
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);
|
|
43
|
+
return subscribable.state;
|
|
37
44
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
//# sourceMappingURL=use-instance.js.map
|
|
45
|
+
//#endregion
|
|
46
|
+
export { useInstance };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=use-instance.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-instance.js","sources":["../../src/react/use-instance.ts"],"sourcesContent":["import { useSyncExternalStore, useRef } from 'react';\nimport type { Subscribable } from '../types';\n\nfunction hasAsyncSubscription(obj: unknown): obj is { subscribeAsync(cb: () => void): () => void } {\n return (\n obj !== null &&\n typeof obj === 'object' &&\n typeof (obj as any).subscribeAsync === 'function'\n );\n}\n\nconst SERVER_SNAPSHOT = () => 0;\n\ninterface InstanceRef<S> {\n version: number;\n subscribable: Subscribable<S>;\n subscribe: (onStoreChange: () => void) => () => void;\n getSnapshot: () => number;\n}\n\n/**\n * Subscribe to an existing Subscribable instance.\n * No ownership - caller manages the instance lifecycle.\n *\n * If the instance has a `subscribeAsync` method (duck-typed),\n * a combined subscription ensures async state changes also\n * trigger React re-renders.\n */\nexport function useInstance<S>(subscribable: Subscribable<S>): S {\n const ref = useRef<InstanceRef<S> | null>(null);\n\n if (!ref.current || ref.current.subscribable !== subscribable) {\n const version = { current: ref.current?.version ?? 0 };\n ref.current = {\n version: version.current,\n subscribable,\n subscribe: (onStoreChange: () => void) => {\n const unsub1 = subscribable.subscribe(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n let unsub2: (() => void) | undefined;\n if (hasAsyncSubscription(subscribable)) {\n unsub2 = subscribable.subscribeAsync(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n }\n return () => { unsub1(); unsub2?.(); };\n },\n getSnapshot: () => version.current,\n };\n }\n\n useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);\n\n return subscribable.state;\n}\n"],"
|
|
1
|
+
{"version":3,"file":"use-instance.js","names":[],"sources":["../../src/react/use-instance.ts"],"sourcesContent":["import { useSyncExternalStore, useRef } from 'react';\nimport type { Subscribable } from '../types';\n\nfunction hasAsyncSubscription(obj: unknown): obj is { subscribeAsync(cb: () => void): () => void } {\n return (\n obj !== null &&\n typeof obj === 'object' &&\n typeof (obj as any).subscribeAsync === 'function'\n );\n}\n\nconst SERVER_SNAPSHOT = () => 0;\n\ninterface InstanceRef<S> {\n version: number;\n subscribable: Subscribable<S>;\n subscribe: (onStoreChange: () => void) => () => void;\n getSnapshot: () => number;\n}\n\n/**\n * Subscribe to an existing Subscribable instance.\n * No ownership - caller manages the instance lifecycle.\n *\n * If the instance has a `subscribeAsync` method (duck-typed),\n * a combined subscription ensures async state changes also\n * trigger React re-renders.\n */\nexport function useInstance<S>(subscribable: Subscribable<S>): S {\n const ref = useRef<InstanceRef<S> | null>(null);\n\n if (!ref.current || ref.current.subscribable !== subscribable) {\n const version = { current: ref.current?.version ?? 0 };\n ref.current = {\n version: version.current,\n subscribable,\n subscribe: (onStoreChange: () => void) => {\n const unsub1 = subscribable.subscribe(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n let unsub2: (() => void) | undefined;\n if (hasAsyncSubscription(subscribable)) {\n unsub2 = subscribable.subscribeAsync(() => {\n version.current++;\n ref.current!.version = version.current;\n onStoreChange();\n });\n }\n return () => { unsub1(); unsub2?.(); };\n },\n getSnapshot: () => version.current,\n };\n }\n\n useSyncExternalStore(ref.current.subscribe, ref.current.getSnapshot, SERVER_SNAPSHOT);\n\n return subscribable.state;\n}\n"],"mappings":";;AAGA,SAAS,qBAAqB,KAAqE;AACjG,QACE,QAAQ,QACR,OAAO,QAAQ,YACf,OAAQ,IAAY,mBAAmB;;AAI3C,IAAM,wBAAwB;;;;;;;;;AAiB9B,SAAgB,YAAe,cAAkC;CAC/D,MAAM,MAAM,OAA8B,KAAK;AAE/C,KAAI,CAAC,IAAI,WAAW,IAAI,QAAQ,iBAAiB,cAAc;EAC7D,MAAM,UAAU,EAAE,SAAS,IAAI,SAAS,WAAW,GAAG;AACtD,MAAI,UAAU;GACZ,SAAS,QAAQ;GACjB;GACA,YAAY,kBAA8B;IACxC,MAAM,SAAS,aAAa,gBAAgB;AAC1C,aAAQ;AACR,SAAI,QAAS,UAAU,QAAQ;AAC/B,oBAAe;MACf;IACF,IAAI;AACJ,QAAI,qBAAqB,aAAa,CACpC,UAAS,aAAa,qBAAqB;AACzC,aAAQ;AACR,SAAI,QAAS,UAAU,QAAQ;AAC/B,oBAAe;MACf;AAEJ,iBAAa;AAAE,aAAQ;AAAE,eAAU;;;GAErC,mBAAmB,QAAQ;GAC5B;;AAGH,sBAAqB,IAAI,QAAQ,WAAW,IAAI,QAAQ,aAAa,gBAAgB;AAErF,QAAO,aAAa"}
|
package/dist/react/use-local.cjs
CHANGED
|
@@ -1,69 +1,53 @@
|
|
|
1
|
-
"use
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const useSubscribeOnly = require("./use-subscribe-only.cjs");
|
|
1
|
+
const require_use_instance = require("./use-instance.cjs");
|
|
2
|
+
const require_guards = require("./guards.cjs");
|
|
3
|
+
const require_use_subscribe_only = require("./use-subscribe-only.cjs");
|
|
4
|
+
let react = require("react");
|
|
5
|
+
//#region src/react/use-local.ts
|
|
7
6
|
function depsChanged(prev, next) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
return false;
|
|
7
|
+
if (prev === void 0) return false;
|
|
8
|
+
if (prev.length !== next.length) return true;
|
|
9
|
+
for (let i = 0; i < prev.length; i++) if (!Object.is(prev[i], next[i])) return true;
|
|
10
|
+
return false;
|
|
14
11
|
}
|
|
15
12
|
function useLocal(classOrFactory, ...rest) {
|
|
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
|
-
if (!mountedRef.current) {
|
|
53
|
-
instance.dispose();
|
|
54
|
-
}
|
|
55
|
-
}, 0);
|
|
56
|
-
};
|
|
57
|
-
}, deps ?? []);
|
|
58
|
-
if (guards.isSubscribable(instanceRef.current)) {
|
|
59
|
-
const state = useInstance.useInstance(instanceRef.current);
|
|
60
|
-
return [state, instanceRef.current];
|
|
61
|
-
}
|
|
62
|
-
if (guards.isSubscribeOnly(instanceRef.current)) {
|
|
63
|
-
useSubscribeOnly.useSubscribeOnly(instanceRef.current);
|
|
64
|
-
return instanceRef.current;
|
|
65
|
-
}
|
|
66
|
-
return instanceRef.current;
|
|
13
|
+
let args;
|
|
14
|
+
let deps;
|
|
15
|
+
if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {
|
|
16
|
+
deps = rest[rest.length - 1];
|
|
17
|
+
args = rest.slice(0, -1);
|
|
18
|
+
} else {
|
|
19
|
+
args = rest;
|
|
20
|
+
deps = void 0;
|
|
21
|
+
}
|
|
22
|
+
const instanceRef = (0, react.useRef)(null);
|
|
23
|
+
const mountedRef = (0, react.useRef)(false);
|
|
24
|
+
const prevDepsRef = (0, react.useRef)(void 0);
|
|
25
|
+
if (deps !== void 0 && depsChanged(prevDepsRef.current, deps)) {
|
|
26
|
+
instanceRef.current?.dispose();
|
|
27
|
+
instanceRef.current = null;
|
|
28
|
+
}
|
|
29
|
+
if (deps !== void 0) prevDepsRef.current = deps;
|
|
30
|
+
if (!instanceRef.current || instanceRef.current.disposed) if (typeof classOrFactory === "function" && classOrFactory.prototype && classOrFactory.prototype.constructor === classOrFactory) instanceRef.current = new classOrFactory(...args);
|
|
31
|
+
else instanceRef.current = classOrFactory();
|
|
32
|
+
(0, react.useEffect)(() => {
|
|
33
|
+
const instance = instanceRef.current;
|
|
34
|
+
mountedRef.current = true;
|
|
35
|
+
if (require_guards.isInitializable(instance)) instance.init();
|
|
36
|
+
return () => {
|
|
37
|
+
mountedRef.current = false;
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
if (!mountedRef.current) instance.dispose();
|
|
40
|
+
}, 0);
|
|
41
|
+
};
|
|
42
|
+
}, deps ?? []);
|
|
43
|
+
if (require_guards.isSubscribable(instanceRef.current)) return [require_use_instance.useInstance(instanceRef.current), instanceRef.current];
|
|
44
|
+
if (require_guards.isSubscribeOnly(instanceRef.current)) {
|
|
45
|
+
require_use_subscribe_only.useSubscribeOnly(instanceRef.current);
|
|
46
|
+
return instanceRef.current;
|
|
47
|
+
}
|
|
48
|
+
return instanceRef.current;
|
|
67
49
|
}
|
|
50
|
+
//#endregion
|
|
68
51
|
exports.useLocal = useLocal;
|
|
69
|
-
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=use-local.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-local.cjs","sources":["../../src/react/use-local.ts"],"sourcesContent":["import { useRef, useEffect } from 'react';\nimport type { DependencyList } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\nimport { useSubscribeOnly } from './use-subscribe-only';\nimport type { StateOf } from './types';\n\nfunction depsChanged(prev: DependencyList | undefined, next: DependencyList): boolean {\n if (prev === undefined) return false;\n if (prev.length !== next.length) return true;\n for (let i = 0; i < prev.length; i++) {\n if (!Object.is(prev[i], next[i])) return true;\n }\n return false;\n}\n\n// ── With deps (class + initialState + deps) ────────────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<any> & Disposable>(\n Class: new (initialState: StateOf<T>) => T,\n initialState: StateOf<T>,\n deps: DependencyList,\n): [StateOf<T>, T];\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T,\n deps: DependencyList,\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(\n factory: () => T,\n deps: DependencyList,\n): T;\n\n// ── Without deps (existing overloads, unchanged) ───────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<\n T extends Subscribable<S> & Disposable,\n S = StateOf<T>,\n Args extends unknown[] = unknown[]\n>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T;\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(factory: () => T): T;\n\n// ── Implementation ─────────────────────────────────────────────────\n\nexport function useLocal<T extends Disposable, S = StateOf<T>>(\n classOrFactory: (new (...args: unknown[]) => T) | (() => T),\n ...rest: unknown[]\n): [S, T] | T {\n // ── Detect deps: last arg is an array → treat as deps ──\n let args: unknown[];\n let deps: DependencyList | undefined;\n\n if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {\n deps = rest[rest.length - 1] as DependencyList;\n args = rest.slice(0, -1);\n } else {\n args = rest;\n deps = undefined;\n }\n\n const instanceRef = useRef<T | null>(null);\n const mountedRef = useRef(false);\n const prevDepsRef = useRef<DependencyList | undefined>(undefined);\n\n // ── Render phase: dep-change detection ──\n if (deps !== undefined && depsChanged(prevDepsRef.current, deps)) {\n instanceRef.current?.dispose();\n instanceRef.current = null;\n }\n if (deps !== undefined) {\n prevDepsRef.current = deps;\n }\n\n // ── Create instance if needed ──\n if (!instanceRef.current || instanceRef.current.disposed) {\n const isClass =\n typeof classOrFactory === 'function' &&\n classOrFactory.prototype &&\n classOrFactory.prototype.constructor === classOrFactory;\n\n if (isClass) {\n instanceRef.current = new (classOrFactory as new (...a: unknown[]) => T)(...args);\n } else {\n instanceRef.current = (classOrFactory as () => T)();\n }\n }\n\n // ── Effect: init + deferred cleanup ──\n useEffect(() => {\n const instance = instanceRef.current!; // capture for cleanup closure\n mountedRef.current = true;\n if (isInitializable(instance)) {\n instance.init();\n }\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n instance.dispose();\n }\n }, 0);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps ?? []);\n\n // ── Subscribe to state if Subscribable ──\n if (isSubscribable(instanceRef.current)) {\n const state = useInstance(instanceRef.current as unknown as Subscribable<S>);\n return [state, instanceRef.current];\n }\n\n // ── Subscribe for re-renders if subscribe-only (e.g., Trackable) ──\n if (isSubscribeOnly(instanceRef.current)) {\n useSubscribeOnly(instanceRef.current);\n return instanceRef.current;\n }\n\n return instanceRef.current;\n}\n"],"
|
|
1
|
+
{"version":3,"file":"use-local.cjs","names":[],"sources":["../../src/react/use-local.ts"],"sourcesContent":["import { useRef, useEffect } from 'react';\nimport type { DependencyList } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\nimport { useSubscribeOnly } from './use-subscribe-only';\nimport type { StateOf } from './types';\n\nfunction depsChanged(prev: DependencyList | undefined, next: DependencyList): boolean {\n if (prev === undefined) return false;\n if (prev.length !== next.length) return true;\n for (let i = 0; i < prev.length; i++) {\n if (!Object.is(prev[i], next[i])) return true;\n }\n return false;\n}\n\n// ── With deps (class + initialState + deps) ────────────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<any> & Disposable>(\n Class: new (initialState: StateOf<T>) => T,\n initialState: StateOf<T>,\n deps: DependencyList,\n): [StateOf<T>, T];\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T,\n deps: DependencyList,\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(\n factory: () => T,\n deps: DependencyList,\n): T;\n\n// ── Without deps (existing overloads, unchanged) ───────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<\n T extends Subscribable<S> & Disposable,\n S = StateOf<T>,\n Args extends unknown[] = unknown[]\n>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T;\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(factory: () => T): T;\n\n// ── Implementation ─────────────────────────────────────────────────\n\nexport function useLocal<T extends Disposable, S = StateOf<T>>(\n classOrFactory: (new (...args: unknown[]) => T) | (() => T),\n ...rest: unknown[]\n): [S, T] | T {\n // ── Detect deps: last arg is an array → treat as deps ──\n let args: unknown[];\n let deps: DependencyList | undefined;\n\n if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {\n deps = rest[rest.length - 1] as DependencyList;\n args = rest.slice(0, -1);\n } else {\n args = rest;\n deps = undefined;\n }\n\n const instanceRef = useRef<T | null>(null);\n const mountedRef = useRef(false);\n const prevDepsRef = useRef<DependencyList | undefined>(undefined);\n\n // ── Render phase: dep-change detection ──\n if (deps !== undefined && depsChanged(prevDepsRef.current, deps)) {\n instanceRef.current?.dispose();\n instanceRef.current = null;\n }\n if (deps !== undefined) {\n prevDepsRef.current = deps;\n }\n\n // ── Create instance if needed ──\n if (!instanceRef.current || instanceRef.current.disposed) {\n const isClass =\n typeof classOrFactory === 'function' &&\n classOrFactory.prototype &&\n classOrFactory.prototype.constructor === classOrFactory;\n\n if (isClass) {\n instanceRef.current = new (classOrFactory as new (...a: unknown[]) => T)(...args);\n } else {\n instanceRef.current = (classOrFactory as () => T)();\n }\n }\n\n // ── Effect: init + deferred cleanup ──\n useEffect(() => {\n const instance = instanceRef.current!; // capture for cleanup closure\n mountedRef.current = true;\n if (isInitializable(instance)) {\n instance.init();\n }\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n instance.dispose();\n }\n }, 0);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps ?? []);\n\n // ── Subscribe to state if Subscribable ──\n if (isSubscribable(instanceRef.current)) {\n const state = useInstance(instanceRef.current as unknown as Subscribable<S>);\n return [state, instanceRef.current];\n }\n\n // ── Subscribe for re-renders if subscribe-only (e.g., Trackable) ──\n if (isSubscribeOnly(instanceRef.current)) {\n useSubscribeOnly(instanceRef.current);\n return instanceRef.current;\n }\n\n return instanceRef.current;\n}\n"],"mappings":";;;;;AAQA,SAAS,YAAY,MAAkC,MAA+B;AACpF,KAAI,SAAS,KAAA,EAAW,QAAO;AAC/B,KAAI,KAAK,WAAW,KAAK,OAAQ,QAAO;AACxC,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAC/B,KAAI,CAAC,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,CAAE,QAAO;AAE3C,QAAO;;AA4ET,SAAgB,SACd,gBACA,GAAG,MACS;CAEZ,IAAI;CACJ,IAAI;AAEJ,KAAI,KAAK,SAAS,KAAK,MAAM,QAAQ,KAAK,KAAK,SAAS,GAAG,EAAE;AAC3D,SAAO,KAAK,KAAK,SAAS;AAC1B,SAAO,KAAK,MAAM,GAAG,GAAG;QACnB;AACL,SAAO;AACP,SAAO,KAAA;;CAGT,MAAM,eAAA,GAAA,MAAA,QAA+B,KAAK;CAC1C,MAAM,cAAA,GAAA,MAAA,QAAoB,MAAM;CAChC,MAAM,eAAA,GAAA,MAAA,QAAiD,KAAA,EAAU;AAGjE,KAAI,SAAS,KAAA,KAAa,YAAY,YAAY,SAAS,KAAK,EAAE;AAChE,cAAY,SAAS,SAAS;AAC9B,cAAY,UAAU;;AAExB,KAAI,SAAS,KAAA,EACX,aAAY,UAAU;AAIxB,KAAI,CAAC,YAAY,WAAW,YAAY,QAAQ,SAM9C,KAJE,OAAO,mBAAmB,cAC1B,eAAe,aACf,eAAe,UAAU,gBAAgB,eAGzC,aAAY,UAAU,IAAK,eAA8C,GAAG,KAAK;KAEjF,aAAY,UAAW,gBAA4B;AAKvD,EAAA,GAAA,MAAA,iBAAgB;EACd,MAAM,WAAW,YAAY;AAC7B,aAAW,UAAU;AACrB,MAAI,eAAA,gBAAgB,SAAS,CAC3B,UAAS,MAAM;AAEjB,eAAa;AACX,cAAW,UAAU;AACrB,oBAAiB;AACf,QAAI,CAAC,WAAW,QACd,UAAS,SAAS;MAEnB,EAAE;;IAGN,QAAQ,EAAE,CAAC;AAGd,KAAI,eAAA,eAAe,YAAY,QAAQ,CAErC,QAAO,CADO,qBAAA,YAAY,YAAY,QAAsC,EAC7D,YAAY,QAAQ;AAIrC,KAAI,eAAA,gBAAgB,YAAY,QAAQ,EAAE;AACxC,6BAAA,iBAAiB,YAAY,QAAQ;AACrC,SAAO,YAAY;;AAGrB,QAAO,YAAY"}
|
package/dist/react/use-local.js
CHANGED
|
@@ -1,69 +1,53 @@
|
|
|
1
|
-
import { useRef, useEffect } from "react";
|
|
2
|
-
import { isInitializable, isSubscribable, isSubscribeOnly } from "./guards.js";
|
|
3
1
|
import { useInstance } from "./use-instance.js";
|
|
2
|
+
import { isInitializable, isSubscribable, isSubscribeOnly } from "./guards.js";
|
|
4
3
|
import { useSubscribeOnly } from "./use-subscribe-only.js";
|
|
4
|
+
import { useEffect, useRef } from "react";
|
|
5
|
+
//#region src/react/use-local.ts
|
|
5
6
|
function depsChanged(prev, next) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
return false;
|
|
7
|
+
if (prev === void 0) return false;
|
|
8
|
+
if (prev.length !== next.length) return true;
|
|
9
|
+
for (let i = 0; i < prev.length; i++) if (!Object.is(prev[i], next[i])) return true;
|
|
10
|
+
return false;
|
|
12
11
|
}
|
|
13
12
|
function useLocal(classOrFactory, ...rest) {
|
|
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
|
-
if (!mountedRef.current) {
|
|
51
|
-
instance.dispose();
|
|
52
|
-
}
|
|
53
|
-
}, 0);
|
|
54
|
-
};
|
|
55
|
-
}, deps ?? []);
|
|
56
|
-
if (isSubscribable(instanceRef.current)) {
|
|
57
|
-
const state = useInstance(instanceRef.current);
|
|
58
|
-
return [state, instanceRef.current];
|
|
59
|
-
}
|
|
60
|
-
if (isSubscribeOnly(instanceRef.current)) {
|
|
61
|
-
useSubscribeOnly(instanceRef.current);
|
|
62
|
-
return instanceRef.current;
|
|
63
|
-
}
|
|
64
|
-
return instanceRef.current;
|
|
13
|
+
let args;
|
|
14
|
+
let deps;
|
|
15
|
+
if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {
|
|
16
|
+
deps = rest[rest.length - 1];
|
|
17
|
+
args = rest.slice(0, -1);
|
|
18
|
+
} else {
|
|
19
|
+
args = rest;
|
|
20
|
+
deps = void 0;
|
|
21
|
+
}
|
|
22
|
+
const instanceRef = useRef(null);
|
|
23
|
+
const mountedRef = useRef(false);
|
|
24
|
+
const prevDepsRef = useRef(void 0);
|
|
25
|
+
if (deps !== void 0 && depsChanged(prevDepsRef.current, deps)) {
|
|
26
|
+
instanceRef.current?.dispose();
|
|
27
|
+
instanceRef.current = null;
|
|
28
|
+
}
|
|
29
|
+
if (deps !== void 0) prevDepsRef.current = deps;
|
|
30
|
+
if (!instanceRef.current || instanceRef.current.disposed) if (typeof classOrFactory === "function" && classOrFactory.prototype && classOrFactory.prototype.constructor === classOrFactory) instanceRef.current = new classOrFactory(...args);
|
|
31
|
+
else instanceRef.current = classOrFactory();
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const instance = instanceRef.current;
|
|
34
|
+
mountedRef.current = true;
|
|
35
|
+
if (isInitializable(instance)) instance.init();
|
|
36
|
+
return () => {
|
|
37
|
+
mountedRef.current = false;
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
if (!mountedRef.current) instance.dispose();
|
|
40
|
+
}, 0);
|
|
41
|
+
};
|
|
42
|
+
}, deps ?? []);
|
|
43
|
+
if (isSubscribable(instanceRef.current)) return [useInstance(instanceRef.current), instanceRef.current];
|
|
44
|
+
if (isSubscribeOnly(instanceRef.current)) {
|
|
45
|
+
useSubscribeOnly(instanceRef.current);
|
|
46
|
+
return instanceRef.current;
|
|
47
|
+
}
|
|
48
|
+
return instanceRef.current;
|
|
65
49
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
//# sourceMappingURL=use-local.js.map
|
|
50
|
+
//#endregion
|
|
51
|
+
export { useLocal };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=use-local.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-local.js","sources":["../../src/react/use-local.ts"],"sourcesContent":["import { useRef, useEffect } from 'react';\nimport type { DependencyList } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\nimport { useSubscribeOnly } from './use-subscribe-only';\nimport type { StateOf } from './types';\n\nfunction depsChanged(prev: DependencyList | undefined, next: DependencyList): boolean {\n if (prev === undefined) return false;\n if (prev.length !== next.length) return true;\n for (let i = 0; i < prev.length; i++) {\n if (!Object.is(prev[i], next[i])) return true;\n }\n return false;\n}\n\n// ── With deps (class + initialState + deps) ────────────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<any> & Disposable>(\n Class: new (initialState: StateOf<T>) => T,\n initialState: StateOf<T>,\n deps: DependencyList,\n): [StateOf<T>, T];\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T,\n deps: DependencyList,\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(\n factory: () => T,\n deps: DependencyList,\n): T;\n\n// ── Without deps (existing overloads, unchanged) ───────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<\n T extends Subscribable<S> & Disposable,\n S = StateOf<T>,\n Args extends unknown[] = unknown[]\n>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T;\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(factory: () => T): T;\n\n// ── Implementation ─────────────────────────────────────────────────\n\nexport function useLocal<T extends Disposable, S = StateOf<T>>(\n classOrFactory: (new (...args: unknown[]) => T) | (() => T),\n ...rest: unknown[]\n): [S, T] | T {\n // ── Detect deps: last arg is an array → treat as deps ──\n let args: unknown[];\n let deps: DependencyList | undefined;\n\n if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {\n deps = rest[rest.length - 1] as DependencyList;\n args = rest.slice(0, -1);\n } else {\n args = rest;\n deps = undefined;\n }\n\n const instanceRef = useRef<T | null>(null);\n const mountedRef = useRef(false);\n const prevDepsRef = useRef<DependencyList | undefined>(undefined);\n\n // ── Render phase: dep-change detection ──\n if (deps !== undefined && depsChanged(prevDepsRef.current, deps)) {\n instanceRef.current?.dispose();\n instanceRef.current = null;\n }\n if (deps !== undefined) {\n prevDepsRef.current = deps;\n }\n\n // ── Create instance if needed ──\n if (!instanceRef.current || instanceRef.current.disposed) {\n const isClass =\n typeof classOrFactory === 'function' &&\n classOrFactory.prototype &&\n classOrFactory.prototype.constructor === classOrFactory;\n\n if (isClass) {\n instanceRef.current = new (classOrFactory as new (...a: unknown[]) => T)(...args);\n } else {\n instanceRef.current = (classOrFactory as () => T)();\n }\n }\n\n // ── Effect: init + deferred cleanup ──\n useEffect(() => {\n const instance = instanceRef.current!; // capture for cleanup closure\n mountedRef.current = true;\n if (isInitializable(instance)) {\n instance.init();\n }\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n instance.dispose();\n }\n }, 0);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps ?? []);\n\n // ── Subscribe to state if Subscribable ──\n if (isSubscribable(instanceRef.current)) {\n const state = useInstance(instanceRef.current as unknown as Subscribable<S>);\n return [state, instanceRef.current];\n }\n\n // ── Subscribe for re-renders if subscribe-only (e.g., Trackable) ──\n if (isSubscribeOnly(instanceRef.current)) {\n useSubscribeOnly(instanceRef.current);\n return instanceRef.current;\n }\n\n return instanceRef.current;\n}\n"],"
|
|
1
|
+
{"version":3,"file":"use-local.js","names":[],"sources":["../../src/react/use-local.ts"],"sourcesContent":["import { useRef, useEffect } from 'react';\nimport type { DependencyList } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { isSubscribable, isSubscribeOnly, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\nimport { useSubscribeOnly } from './use-subscribe-only';\nimport type { StateOf } from './types';\n\nfunction depsChanged(prev: DependencyList | undefined, next: DependencyList): boolean {\n if (prev === undefined) return false;\n if (prev.length !== next.length) return true;\n for (let i = 0; i < prev.length; i++) {\n if (!Object.is(prev[i], next[i])) return true;\n }\n return false;\n}\n\n// ── With deps (class + initialState + deps) ────────────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<any> & Disposable>(\n Class: new (initialState: StateOf<T>) => T,\n initialState: StateOf<T>,\n deps: DependencyList,\n): [StateOf<T>, T];\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T,\n deps: DependencyList,\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Disposes and recreates when deps change.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(\n factory: () => T,\n deps: DependencyList,\n): T;\n\n// ── Without deps (existing overloads, unchanged) ───────────────────\n\n/**\n * Create component-scoped Subscribable instance, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<\n T extends Subscribable<S> & Disposable,\n S = StateOf<T>,\n Args extends unknown[] = unknown[]\n>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T;\n\n/**\n * Create component-scoped Subscribable instance via factory, auto-disposed on unmount.\n * Returns [state, instance] tuple.\n */\nexport function useLocal<T extends Subscribable<S> & Disposable, S = StateOf<T>>(\n factory: () => T\n): [S, T];\n\n/**\n * Create component-scoped Disposable instance via factory (non-Subscribable), auto-disposed on unmount.\n * Returns the instance directly.\n */\nexport function useLocal<T extends Disposable>(factory: () => T): T;\n\n// ── Implementation ─────────────────────────────────────────────────\n\nexport function useLocal<T extends Disposable, S = StateOf<T>>(\n classOrFactory: (new (...args: unknown[]) => T) | (() => T),\n ...rest: unknown[]\n): [S, T] | T {\n // ── Detect deps: last arg is an array → treat as deps ──\n let args: unknown[];\n let deps: DependencyList | undefined;\n\n if (rest.length > 0 && Array.isArray(rest[rest.length - 1])) {\n deps = rest[rest.length - 1] as DependencyList;\n args = rest.slice(0, -1);\n } else {\n args = rest;\n deps = undefined;\n }\n\n const instanceRef = useRef<T | null>(null);\n const mountedRef = useRef(false);\n const prevDepsRef = useRef<DependencyList | undefined>(undefined);\n\n // ── Render phase: dep-change detection ──\n if (deps !== undefined && depsChanged(prevDepsRef.current, deps)) {\n instanceRef.current?.dispose();\n instanceRef.current = null;\n }\n if (deps !== undefined) {\n prevDepsRef.current = deps;\n }\n\n // ── Create instance if needed ──\n if (!instanceRef.current || instanceRef.current.disposed) {\n const isClass =\n typeof classOrFactory === 'function' &&\n classOrFactory.prototype &&\n classOrFactory.prototype.constructor === classOrFactory;\n\n if (isClass) {\n instanceRef.current = new (classOrFactory as new (...a: unknown[]) => T)(...args);\n } else {\n instanceRef.current = (classOrFactory as () => T)();\n }\n }\n\n // ── Effect: init + deferred cleanup ──\n useEffect(() => {\n const instance = instanceRef.current!; // capture for cleanup closure\n mountedRef.current = true;\n if (isInitializable(instance)) {\n instance.init();\n }\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n instance.dispose();\n }\n }, 0);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps ?? []);\n\n // ── Subscribe to state if Subscribable ──\n if (isSubscribable(instanceRef.current)) {\n const state = useInstance(instanceRef.current as unknown as Subscribable<S>);\n return [state, instanceRef.current];\n }\n\n // ── Subscribe for re-renders if subscribe-only (e.g., Trackable) ──\n if (isSubscribeOnly(instanceRef.current)) {\n useSubscribeOnly(instanceRef.current);\n return instanceRef.current;\n }\n\n return instanceRef.current;\n}\n"],"mappings":";;;;;AAQA,SAAS,YAAY,MAAkC,MAA+B;AACpF,KAAI,SAAS,KAAA,EAAW,QAAO;AAC/B,KAAI,KAAK,WAAW,KAAK,OAAQ,QAAO;AACxC,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAC/B,KAAI,CAAC,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,CAAE,QAAO;AAE3C,QAAO;;AA4ET,SAAgB,SACd,gBACA,GAAG,MACS;CAEZ,IAAI;CACJ,IAAI;AAEJ,KAAI,KAAK,SAAS,KAAK,MAAM,QAAQ,KAAK,KAAK,SAAS,GAAG,EAAE;AAC3D,SAAO,KAAK,KAAK,SAAS;AAC1B,SAAO,KAAK,MAAM,GAAG,GAAG;QACnB;AACL,SAAO;AACP,SAAO,KAAA;;CAGT,MAAM,cAAc,OAAiB,KAAK;CAC1C,MAAM,aAAa,OAAO,MAAM;CAChC,MAAM,cAAc,OAAmC,KAAA,EAAU;AAGjE,KAAI,SAAS,KAAA,KAAa,YAAY,YAAY,SAAS,KAAK,EAAE;AAChE,cAAY,SAAS,SAAS;AAC9B,cAAY,UAAU;;AAExB,KAAI,SAAS,KAAA,EACX,aAAY,UAAU;AAIxB,KAAI,CAAC,YAAY,WAAW,YAAY,QAAQ,SAM9C,KAJE,OAAO,mBAAmB,cAC1B,eAAe,aACf,eAAe,UAAU,gBAAgB,eAGzC,aAAY,UAAU,IAAK,eAA8C,GAAG,KAAK;KAEjF,aAAY,UAAW,gBAA4B;AAKvD,iBAAgB;EACd,MAAM,WAAW,YAAY;AAC7B,aAAW,UAAU;AACrB,MAAI,gBAAgB,SAAS,CAC3B,UAAS,MAAM;AAEjB,eAAa;AACX,cAAW,UAAU;AACrB,oBAAiB;AACf,QAAI,CAAC,WAAW,QACd,UAAS,SAAS;MAEnB,EAAE;;IAGN,QAAQ,EAAE,CAAC;AAGd,KAAI,eAAe,YAAY,QAAQ,CAErC,QAAO,CADO,YAAY,YAAY,QAAsC,EAC7D,YAAY,QAAQ;AAIrC,KAAI,gBAAgB,YAAY,QAAQ,EAAE;AACxC,mBAAiB,YAAY,QAAQ;AACrC,SAAO,YAAY;;AAGrB,QAAO,YAAY"}
|
package/dist/react/use-model.cjs
CHANGED
|
@@ -1,107 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const require_guards = require("./guards.cjs");
|
|
2
|
+
let react = require("react");
|
|
3
|
+
//#region src/react/use-model.ts
|
|
4
|
+
/**
|
|
5
|
+
* Bind to a component-scoped Model with validation and dirty state exposed.
|
|
6
|
+
*/
|
|
5
7
|
function useModel(factory) {
|
|
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
|
-
}, 0);
|
|
32
|
-
};
|
|
33
|
-
}, []);
|
|
34
|
-
const model = modelRef.current;
|
|
35
|
-
return {
|
|
36
|
-
state: model.state,
|
|
37
|
-
errors: model.errors,
|
|
38
|
-
valid: model.valid,
|
|
39
|
-
dirty: model.dirty,
|
|
40
|
-
model
|
|
41
|
-
};
|
|
8
|
+
const modelRef = (0, react.useRef)(null);
|
|
9
|
+
const mountedRef = (0, react.useRef)(false);
|
|
10
|
+
if (!modelRef.current || modelRef.current.disposed) modelRef.current = factory();
|
|
11
|
+
const modelSubscribe = (0, react.useCallback)((onStoreChange) => modelRef.current.subscribe(onStoreChange), []);
|
|
12
|
+
const modelSnapshot = (0, react.useCallback)(() => modelRef.current.state, []);
|
|
13
|
+
(0, react.useSyncExternalStore)(modelSubscribe, modelSnapshot, modelSnapshot);
|
|
14
|
+
(0, react.useEffect)(() => {
|
|
15
|
+
mountedRef.current = true;
|
|
16
|
+
if (require_guards.isInitializable(modelRef.current)) modelRef.current.init();
|
|
17
|
+
return () => {
|
|
18
|
+
mountedRef.current = false;
|
|
19
|
+
setTimeout(() => {
|
|
20
|
+
if (!mountedRef.current) modelRef.current?.dispose();
|
|
21
|
+
}, 0);
|
|
22
|
+
};
|
|
23
|
+
}, []);
|
|
24
|
+
const model = modelRef.current;
|
|
25
|
+
return {
|
|
26
|
+
state: model.state,
|
|
27
|
+
errors: model.errors,
|
|
28
|
+
valid: model.valid,
|
|
29
|
+
dirty: model.dirty,
|
|
30
|
+
model
|
|
31
|
+
};
|
|
42
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Create a component-scoped Model with lifecycle management (init + dispose)
|
|
35
|
+
* but NO state subscription. The parent component never re-renders from
|
|
36
|
+
* model state changes.
|
|
37
|
+
*
|
|
38
|
+
* Designed for the per-field isolation pattern: parent creates the model
|
|
39
|
+
* via `useModelRef`, children subscribe to individual fields via `useField`.
|
|
40
|
+
*/
|
|
43
41
|
function useModelRef(factory) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
modelRef.current?.dispose();
|
|
59
|
-
}
|
|
60
|
-
}, 0);
|
|
61
|
-
};
|
|
62
|
-
}, []);
|
|
63
|
-
return modelRef.current;
|
|
42
|
+
const modelRef = (0, react.useRef)(null);
|
|
43
|
+
const mountedRef = (0, react.useRef)(false);
|
|
44
|
+
if (!modelRef.current || modelRef.current.disposed) modelRef.current = factory();
|
|
45
|
+
(0, react.useEffect)(() => {
|
|
46
|
+
mountedRef.current = true;
|
|
47
|
+
if (require_guards.isInitializable(modelRef.current)) modelRef.current.init();
|
|
48
|
+
return () => {
|
|
49
|
+
mountedRef.current = false;
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
if (!mountedRef.current) modelRef.current?.dispose();
|
|
52
|
+
}, 0);
|
|
53
|
+
};
|
|
54
|
+
}, []);
|
|
55
|
+
return modelRef.current;
|
|
64
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Bind to a single Model field with surgical re-renders.
|
|
59
|
+
*/
|
|
65
60
|
function useField(model, field) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
(value) => {
|
|
93
|
-
const partial = { [field]: value };
|
|
94
|
-
model.set(partial);
|
|
95
|
-
},
|
|
96
|
-
[model, field]
|
|
97
|
-
);
|
|
98
|
-
return {
|
|
99
|
-
value: snapshot.value,
|
|
100
|
-
error: snapshot.error,
|
|
101
|
-
set
|
|
102
|
-
};
|
|
61
|
+
const getSnapshot = (0, react.useCallback)(() => {
|
|
62
|
+
return {
|
|
63
|
+
value: model.state[field],
|
|
64
|
+
error: model.errors[field]
|
|
65
|
+
};
|
|
66
|
+
}, [model, field]);
|
|
67
|
+
const cachedRef = (0, react.useRef)(getSnapshot());
|
|
68
|
+
const snapshot = (0, react.useSyncExternalStore)((0, react.useCallback)((onStoreChange) => {
|
|
69
|
+
return model.subscribe(() => {
|
|
70
|
+
const next = getSnapshot();
|
|
71
|
+
const current = cachedRef.current;
|
|
72
|
+
if (next.value !== current.value || next.error !== current.error) {
|
|
73
|
+
cachedRef.current = next;
|
|
74
|
+
onStoreChange();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}, [model, getSnapshot]), () => cachedRef.current, () => cachedRef.current);
|
|
78
|
+
const set = (0, react.useCallback)((value) => {
|
|
79
|
+
const partial = { [field]: value };
|
|
80
|
+
model.set(partial);
|
|
81
|
+
}, [model, field]);
|
|
82
|
+
return {
|
|
83
|
+
value: snapshot.value,
|
|
84
|
+
error: snapshot.error,
|
|
85
|
+
set
|
|
86
|
+
};
|
|
103
87
|
}
|
|
88
|
+
//#endregion
|
|
104
89
|
exports.useField = useField;
|
|
105
90
|
exports.useModel = useModel;
|
|
106
91
|
exports.useModelRef = useModelRef;
|
|
107
|
-
|
|
92
|
+
|
|
93
|
+
//# sourceMappingURL=use-model.cjs.map
|