mvc-kit 2.5.3 → 2.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/Channel.cjs +291 -0
  2. package/dist/Channel.cjs.map +1 -0
  3. package/dist/Channel.js +291 -0
  4. package/dist/Channel.js.map +1 -0
  5. package/dist/Collection.cjs +452 -0
  6. package/dist/Collection.cjs.map +1 -0
  7. package/dist/Collection.js +452 -0
  8. package/dist/Collection.js.map +1 -0
  9. package/dist/Controller.cjs +57 -0
  10. package/dist/Controller.cjs.map +1 -0
  11. package/dist/Controller.js +57 -0
  12. package/dist/Controller.js.map +1 -0
  13. package/dist/EventBus.cjs +84 -0
  14. package/dist/EventBus.cjs.map +1 -0
  15. package/dist/EventBus.js +84 -0
  16. package/dist/EventBus.js.map +1 -0
  17. package/dist/Model.cjs +175 -0
  18. package/dist/Model.cjs.map +1 -0
  19. package/dist/Model.js +175 -0
  20. package/dist/Model.js.map +1 -0
  21. package/dist/PersistentCollection.cjs +285 -0
  22. package/dist/PersistentCollection.cjs.map +1 -0
  23. package/dist/PersistentCollection.js +285 -0
  24. package/dist/PersistentCollection.js.map +1 -0
  25. package/dist/Resource.cjs +308 -0
  26. package/dist/Resource.cjs.map +1 -0
  27. package/dist/Resource.js +308 -0
  28. package/dist/Resource.js.map +1 -0
  29. package/dist/Service.cjs +51 -0
  30. package/dist/Service.cjs.map +1 -0
  31. package/dist/Service.js +51 -0
  32. package/dist/Service.js.map +1 -0
  33. package/dist/ViewModel.cjs +582 -0
  34. package/dist/ViewModel.cjs.map +1 -0
  35. package/dist/ViewModel.d.ts +1 -7
  36. package/dist/ViewModel.d.ts.map +1 -1
  37. package/dist/ViewModel.js +582 -0
  38. package/dist/ViewModel.js.map +1 -0
  39. package/dist/errors.cjs +79 -0
  40. package/dist/errors.cjs.map +1 -0
  41. package/dist/errors.js +79 -0
  42. package/dist/errors.js.map +1 -0
  43. package/dist/mvc-kit.cjs +29 -1
  44. package/dist/mvc-kit.cjs.map +1 -1
  45. package/dist/mvc-kit.js +27 -1132
  46. package/dist/mvc-kit.js.map +1 -1
  47. package/dist/react/guards.cjs +7 -0
  48. package/dist/react/guards.cjs.map +1 -0
  49. package/dist/react/guards.js +7 -0
  50. package/dist/react/guards.js.map +1 -0
  51. package/dist/react/provider.cjs +26 -0
  52. package/dist/react/provider.cjs.map +1 -0
  53. package/dist/react/provider.js +26 -0
  54. package/dist/react/provider.js.map +1 -0
  55. package/dist/react/use-event-bus.cjs +26 -0
  56. package/dist/react/use-event-bus.cjs.map +1 -0
  57. package/dist/react/use-event-bus.js +26 -0
  58. package/dist/react/use-event-bus.js.map +1 -0
  59. package/dist/react/use-instance.cjs +31 -0
  60. package/dist/react/use-instance.cjs.map +1 -0
  61. package/dist/react/use-instance.js +31 -0
  62. package/dist/react/use-instance.js.map +1 -0
  63. package/dist/react/use-local.cjs +64 -0
  64. package/dist/react/use-local.cjs.map +1 -0
  65. package/dist/react/use-local.js +64 -0
  66. package/dist/react/use-local.js.map +1 -0
  67. package/dist/react/use-model.cjs +80 -0
  68. package/dist/react/use-model.cjs.map +1 -0
  69. package/dist/react/use-model.js +80 -0
  70. package/dist/react/use-model.js.map +1 -0
  71. package/dist/react/use-singleton.cjs +21 -0
  72. package/dist/react/use-singleton.cjs.map +1 -0
  73. package/dist/react/use-singleton.js +21 -0
  74. package/dist/react/use-singleton.js.map +1 -0
  75. package/dist/react/use-teardown.cjs +22 -0
  76. package/dist/react/use-teardown.cjs.map +1 -0
  77. package/dist/react/use-teardown.js +22 -0
  78. package/dist/react/use-teardown.js.map +1 -0
  79. package/dist/react-native/NativeCollection.cjs +76 -0
  80. package/dist/react-native/NativeCollection.cjs.map +1 -0
  81. package/dist/react-native/NativeCollection.js +76 -0
  82. package/dist/react-native/NativeCollection.js.map +1 -0
  83. package/dist/react-native.cjs +4 -1
  84. package/dist/react-native.cjs.map +1 -1
  85. package/dist/react-native.js +2 -60
  86. package/dist/react-native.js.map +1 -1
  87. package/dist/react.cjs +19 -1
  88. package/dist/react.cjs.map +1 -1
  89. package/dist/react.js +17 -145
  90. package/dist/react.js.map +1 -1
  91. package/dist/singleton.cjs +34 -0
  92. package/dist/singleton.cjs.map +1 -0
  93. package/dist/singleton.js +34 -0
  94. package/dist/singleton.js.map +1 -0
  95. package/dist/walkPrototypeChain.cjs +15 -0
  96. package/dist/walkPrototypeChain.cjs.map +1 -0
  97. package/dist/walkPrototypeChain.d.ts +9 -0
  98. package/dist/walkPrototypeChain.d.ts.map +1 -0
  99. package/dist/walkPrototypeChain.js +15 -0
  100. package/dist/walkPrototypeChain.js.map +1 -0
  101. package/dist/web/IndexedDBCollection.cjs +37 -0
  102. package/dist/web/IndexedDBCollection.cjs.map +1 -0
  103. package/dist/web/IndexedDBCollection.js +37 -0
  104. package/dist/web/IndexedDBCollection.js.map +1 -0
  105. package/dist/web/WebStorageCollection.cjs +85 -0
  106. package/dist/web/WebStorageCollection.cjs.map +1 -0
  107. package/dist/web/WebStorageCollection.js +85 -0
  108. package/dist/web/WebStorageCollection.js.map +1 -0
  109. package/dist/web/idb.cjs +121 -0
  110. package/dist/web/idb.cjs.map +1 -0
  111. package/dist/web/idb.js +121 -0
  112. package/dist/web/idb.js.map +1 -0
  113. package/dist/web.cjs +6 -1
  114. package/dist/web.cjs.map +1 -1
  115. package/dist/web.js +4 -178
  116. package/dist/web.js.map +1 -1
  117. package/package.json +4 -2
  118. package/dist/PersistentCollection-B8kNECDj.cjs +0 -2
  119. package/dist/PersistentCollection-B8kNECDj.cjs.map +0 -1
  120. package/dist/PersistentCollection-CbYqzFHc.js +0 -542
  121. package/dist/PersistentCollection-CbYqzFHc.js.map +0 -1
  122. package/dist/singleton-CaEXSbYg.js +0 -89
  123. package/dist/singleton-CaEXSbYg.js.map +0 -1
  124. package/dist/singleton-L-u2W_lX.cjs +0 -2
  125. package/dist/singleton-L-u2W_lX.cjs.map +0 -1
@@ -0,0 +1,64 @@
1
+ import { useRef, useEffect } from "react";
2
+ import { isInitializable, isSubscribable } from "./guards.js";
3
+ import { useInstance } from "./use-instance.js";
4
+ function depsChanged(prev, next) {
5
+ if (prev === void 0) return false;
6
+ if (prev.length !== next.length) return true;
7
+ for (let i = 0; i < prev.length; i++) {
8
+ if (!Object.is(prev[i], next[i])) return true;
9
+ }
10
+ return false;
11
+ }
12
+ function useLocal(classOrFactory, ...rest) {
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) {
30
+ prevDepsRef.current = deps;
31
+ }
32
+ if (!instanceRef.current || instanceRef.current.disposed) {
33
+ const isClass = typeof classOrFactory === "function" && classOrFactory.prototype && classOrFactory.prototype.constructor === classOrFactory;
34
+ if (isClass) {
35
+ instanceRef.current = new classOrFactory(...args);
36
+ } else {
37
+ instanceRef.current = classOrFactory();
38
+ }
39
+ }
40
+ useEffect(() => {
41
+ const instance = instanceRef.current;
42
+ mountedRef.current = true;
43
+ if (isInitializable(instance)) {
44
+ instance.init();
45
+ }
46
+ return () => {
47
+ mountedRef.current = false;
48
+ setTimeout(() => {
49
+ if (!mountedRef.current) {
50
+ instance.dispose();
51
+ }
52
+ }, 0);
53
+ };
54
+ }, deps ?? []);
55
+ if (isSubscribable(instanceRef.current)) {
56
+ const state = useInstance(instanceRef.current);
57
+ return [state, instanceRef.current];
58
+ }
59
+ return instanceRef.current;
60
+ }
61
+ export {
62
+ useLocal
63
+ };
64
+ //# sourceMappingURL=use-local.js.map
@@ -0,0 +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, isInitializable } from './guards';\nimport { useInstance } from './use-instance';\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 return instanceRef.current;\n}\n"],"names":[],"mappings":";;;AAOA,SAAS,YAAY,MAAkC,MAA+B;AACpF,MAAI,SAAS,OAAW,QAAO;AAC/B,MAAI,KAAK,WAAW,KAAK,OAAQ,QAAO;AACxC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,EAAG,QAAO;AAAA,EAC3C;AACA,SAAO;AACT;AA2EO,SAAS,SACd,mBACG,MACS;AAEZ,MAAI;AACJ,MAAI;AAEJ,MAAI,KAAK,SAAS,KAAK,MAAM,QAAQ,KAAK,KAAK,SAAS,CAAC,CAAC,GAAG;AAC3D,WAAO,KAAK,KAAK,SAAS,CAAC;AAC3B,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACzB,OAAO;AACL,WAAO;AACP,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,OAAiB,IAAI;AACzC,QAAM,aAAa,OAAO,KAAK;AAC/B,QAAM,cAAc,OAAmC,MAAS;AAGhE,MAAI,SAAS,UAAa,YAAY,YAAY,SAAS,IAAI,GAAG;AAChE,gBAAY,SAAS,QAAA;AACrB,gBAAY,UAAU;AAAA,EACxB;AACA,MAAI,SAAS,QAAW;AACtB,gBAAY,UAAU;AAAA,EACxB;AAGA,MAAI,CAAC,YAAY,WAAW,YAAY,QAAQ,UAAU;AACxD,UAAM,UACJ,OAAO,mBAAmB,cAC1B,eAAe,aACf,eAAe,UAAU,gBAAgB;AAE3C,QAAI,SAAS;AACX,kBAAY,UAAU,IAAK,eAA8C,GAAG,IAAI;AAAA,IAClF,OAAO;AACL,kBAAY,UAAW,eAAA;AAAA,IACzB;AAAA,EACF;AAGA,YAAU,MAAM;AACd,UAAM,WAAW,YAAY;AAC7B,eAAW,UAAU;AACrB,QAAI,gBAAgB,QAAQ,GAAG;AAC7B,eAAS,KAAA;AAAA,IACX;AACA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,mBAAS,QAAA;AAAA,QACX;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EAEF,GAAG,QAAQ,EAAE;AAGb,MAAI,eAAe,YAAY,OAAO,GAAG;AACvC,UAAM,QAAQ,YAAY,YAAY,OAAqC;AAC3E,WAAO,CAAC,OAAO,YAAY,OAAO;AAAA,EACpC;AAEA,SAAO,YAAY;AACrB;"}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const react = require("react");
4
+ const guards = require("./guards.cjs");
5
+ function useModel(factory) {
6
+ const modelRef = react.useRef(null);
7
+ const mountedRef = react.useRef(false);
8
+ if (!modelRef.current || modelRef.current.disposed) {
9
+ modelRef.current = factory();
10
+ }
11
+ react.useSyncExternalStore(
12
+ (onStoreChange) => modelRef.current.subscribe(onStoreChange),
13
+ () => modelRef.current.state,
14
+ () => modelRef.current.state
15
+ );
16
+ react.useEffect(() => {
17
+ mountedRef.current = true;
18
+ if (guards.isInitializable(modelRef.current)) {
19
+ modelRef.current.init();
20
+ }
21
+ return () => {
22
+ mountedRef.current = false;
23
+ setTimeout(() => {
24
+ if (!mountedRef.current) {
25
+ modelRef.current?.dispose();
26
+ }
27
+ }, 0);
28
+ };
29
+ }, []);
30
+ const model = modelRef.current;
31
+ return {
32
+ state: model.state,
33
+ errors: model.errors,
34
+ valid: model.valid,
35
+ dirty: model.dirty,
36
+ model
37
+ };
38
+ }
39
+ function useField(model, field) {
40
+ const getSnapshot = react.useCallback(() => {
41
+ return {
42
+ value: model.state[field],
43
+ error: model.errors[field]
44
+ };
45
+ }, [model, field]);
46
+ const cachedRef = react.useRef(getSnapshot());
47
+ const subscribe = react.useCallback(
48
+ (onStoreChange) => {
49
+ return model.subscribe(() => {
50
+ const next = getSnapshot();
51
+ const current = cachedRef.current;
52
+ if (next.value !== current.value || next.error !== current.error) {
53
+ cachedRef.current = next;
54
+ onStoreChange();
55
+ }
56
+ });
57
+ },
58
+ [model, getSnapshot]
59
+ );
60
+ const snapshot = react.useSyncExternalStore(
61
+ subscribe,
62
+ () => cachedRef.current,
63
+ () => cachedRef.current
64
+ );
65
+ const set = react.useCallback(
66
+ (value) => {
67
+ const partial = { [field]: value };
68
+ model.set(partial);
69
+ },
70
+ [model, field]
71
+ );
72
+ return {
73
+ value: snapshot.value,
74
+ error: snapshot.error,
75
+ set
76
+ };
77
+ }
78
+ exports.useField = useField;
79
+ exports.useModel = useModel;
80
+ //# sourceMappingURL=use-model.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-model.cjs","sources":["../../src/react/use-model.ts"],"sourcesContent":["import { useRef, useEffect, useSyncExternalStore, useCallback } from 'react';\nimport type { Model } from '../Model';\nimport type { ValidationErrors } from '../types';\nimport type { StateOf } from './types';\nimport { isInitializable } from './guards';\n\n/** Return type of `useModel`, providing state, validation, and model access. */\nexport interface ModelHandle<S extends object, M extends Model<S>> {\n state: S;\n errors: ValidationErrors<S>;\n valid: boolean;\n dirty: boolean;\n model: M;\n}\n\n/**\n * Bind to a component-scoped Model with validation and dirty state exposed.\n */\nexport function useModel<M extends Model<any>>(\n factory: () => M\n): ModelHandle<StateOf<M>, M> {\n const modelRef = useRef<M | null>(null);\n const mountedRef = useRef(false);\n\n if (!modelRef.current || modelRef.current.disposed) {\n modelRef.current = factory();\n }\n\n useSyncExternalStore(\n (onStoreChange) => modelRef.current!.subscribe(onStoreChange),\n () => modelRef.current!.state,\n () => modelRef.current!.state,\n );\n\n useEffect(() => {\n mountedRef.current = true;\n if (isInitializable(modelRef.current)) {\n modelRef.current.init();\n }\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n modelRef.current?.dispose();\n }\n }, 0);\n };\n }, []);\n\n const model = modelRef.current;\n\n return {\n state: model.state,\n errors: model.errors,\n valid: model.valid,\n dirty: model.dirty,\n model,\n };\n}\n\n/** Return type of `useField`, providing a single field's value, error, and setter. */\nexport interface FieldHandle<V> {\n value: V;\n error: string | undefined;\n set: (value: V) => void;\n}\n\n/**\n * Bind to a single Model field with surgical re-renders.\n */\nexport function useField<S extends object, K extends keyof S>(\n model: Model<S>,\n field: K\n): FieldHandle<S[K]> {\n // Track the field value and error for comparison\n const getSnapshot = useCallback(() => {\n return {\n value: model.state[field],\n error: model.errors[field],\n };\n }, [model, field]);\n\n // Use object comparison for subscription\n const cachedRef = useRef(getSnapshot());\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n return model.subscribe(() => {\n const next = getSnapshot();\n const current = cachedRef.current;\n\n // Only trigger re-render if field value or error changed\n if (next.value !== current.value || next.error !== current.error) {\n cachedRef.current = next;\n onStoreChange();\n }\n });\n },\n [model, getSnapshot]\n );\n\n const snapshot = useSyncExternalStore(\n subscribe,\n () => cachedRef.current,\n () => cachedRef.current\n );\n\n const set = useCallback(\n (value: S[K]) => {\n // Access the protected set method through type assertion\n // The Model subclass should expose a setter method\n const partial: Partial<S> = { [field]: value } as unknown as Partial<S>;\n (model as unknown as { set: (partial: Partial<S>) => void }).set(partial);\n },\n [model, field]\n );\n\n return {\n value: snapshot.value,\n error: snapshot.error,\n set,\n };\n}\n"],"names":["useRef","useSyncExternalStore","useEffect","isInitializable","useCallback"],"mappings":";;;;AAkBO,SAAS,SACd,SAC4B;AAC5B,QAAM,WAAWA,MAAAA,OAAiB,IAAI;AACtC,QAAM,aAAaA,MAAAA,OAAO,KAAK;AAE/B,MAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,UAAU;AAClD,aAAS,UAAU,QAAA;AAAA,EACrB;AAEAC,QAAAA;AAAAA,IACE,CAAC,kBAAkB,SAAS,QAAS,UAAU,aAAa;AAAA,IAC5D,MAAM,SAAS,QAAS;AAAA,IACxB,MAAM,SAAS,QAAS;AAAA,EAAA;AAG1BC,QAAAA,UAAU,MAAM;AACd,eAAW,UAAU;AACrB,QAAIC,OAAAA,gBAAgB,SAAS,OAAO,GAAG;AACrC,eAAS,QAAQ,KAAA;AAAA,IACnB;AACA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,mBAAS,SAAS,QAAA;AAAA,QACpB;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,QAAM,QAAQ,SAAS;AAEvB,SAAO;AAAA,IACL,OAAO,MAAM;AAAA,IACb,QAAQ,MAAM;AAAA,IACd,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,IACb;AAAA,EAAA;AAEJ;AAYO,SAAS,SACd,OACA,OACmB;AAEnB,QAAM,cAAcC,MAAAA,YAAY,MAAM;AACpC,WAAO;AAAA,MACL,OAAO,MAAM,MAAM,KAAK;AAAA,MACxB,OAAO,MAAM,OAAO,KAAK;AAAA,IAAA;AAAA,EAE7B,GAAG,CAAC,OAAO,KAAK,CAAC;AAGjB,QAAM,YAAYJ,aAAO,aAAa;AAEtC,QAAM,YAAYI,MAAAA;AAAAA,IAChB,CAAC,kBAA8B;AAC7B,aAAO,MAAM,UAAU,MAAM;AAC3B,cAAM,OAAO,YAAA;AACb,cAAM,UAAU,UAAU;AAG1B,YAAI,KAAK,UAAU,QAAQ,SAAS,KAAK,UAAU,QAAQ,OAAO;AAChE,oBAAU,UAAU;AACpB,wBAAA;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,CAAC,OAAO,WAAW;AAAA,EAAA;AAGrB,QAAM,WAAWH,MAAAA;AAAAA,IACf;AAAA,IACA,MAAM,UAAU;AAAA,IAChB,MAAM,UAAU;AAAA,EAAA;AAGlB,QAAM,MAAMG,MAAAA;AAAAA,IACV,CAAC,UAAgB;AAGf,YAAM,UAAsB,EAAE,CAAC,KAAK,GAAG,MAAA;AACtC,YAA4D,IAAI,OAAO;AAAA,IAC1E;AAAA,IACA,CAAC,OAAO,KAAK;AAAA,EAAA;AAGf,SAAO;AAAA,IACL,OAAO,SAAS;AAAA,IAChB,OAAO,SAAS;AAAA,IAChB;AAAA,EAAA;AAEJ;;;"}
@@ -0,0 +1,80 @@
1
+ import { useCallback, useRef, useSyncExternalStore, useEffect } from "react";
2
+ import { isInitializable } from "./guards.js";
3
+ function useModel(factory) {
4
+ const modelRef = useRef(null);
5
+ const mountedRef = useRef(false);
6
+ if (!modelRef.current || modelRef.current.disposed) {
7
+ modelRef.current = factory();
8
+ }
9
+ useSyncExternalStore(
10
+ (onStoreChange) => modelRef.current.subscribe(onStoreChange),
11
+ () => modelRef.current.state,
12
+ () => modelRef.current.state
13
+ );
14
+ useEffect(() => {
15
+ mountedRef.current = true;
16
+ if (isInitializable(modelRef.current)) {
17
+ modelRef.current.init();
18
+ }
19
+ return () => {
20
+ mountedRef.current = false;
21
+ setTimeout(() => {
22
+ if (!mountedRef.current) {
23
+ modelRef.current?.dispose();
24
+ }
25
+ }, 0);
26
+ };
27
+ }, []);
28
+ const model = modelRef.current;
29
+ return {
30
+ state: model.state,
31
+ errors: model.errors,
32
+ valid: model.valid,
33
+ dirty: model.dirty,
34
+ model
35
+ };
36
+ }
37
+ function useField(model, field) {
38
+ const getSnapshot = useCallback(() => {
39
+ return {
40
+ value: model.state[field],
41
+ error: model.errors[field]
42
+ };
43
+ }, [model, field]);
44
+ const cachedRef = useRef(getSnapshot());
45
+ const subscribe = useCallback(
46
+ (onStoreChange) => {
47
+ return model.subscribe(() => {
48
+ const next = getSnapshot();
49
+ const current = cachedRef.current;
50
+ if (next.value !== current.value || next.error !== current.error) {
51
+ cachedRef.current = next;
52
+ onStoreChange();
53
+ }
54
+ });
55
+ },
56
+ [model, getSnapshot]
57
+ );
58
+ const snapshot = useSyncExternalStore(
59
+ subscribe,
60
+ () => cachedRef.current,
61
+ () => cachedRef.current
62
+ );
63
+ const set = useCallback(
64
+ (value) => {
65
+ const partial = { [field]: value };
66
+ model.set(partial);
67
+ },
68
+ [model, field]
69
+ );
70
+ return {
71
+ value: snapshot.value,
72
+ error: snapshot.error,
73
+ set
74
+ };
75
+ }
76
+ export {
77
+ useField,
78
+ useModel
79
+ };
80
+ //# sourceMappingURL=use-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-model.js","sources":["../../src/react/use-model.ts"],"sourcesContent":["import { useRef, useEffect, useSyncExternalStore, useCallback } from 'react';\nimport type { Model } from '../Model';\nimport type { ValidationErrors } from '../types';\nimport type { StateOf } from './types';\nimport { isInitializable } from './guards';\n\n/** Return type of `useModel`, providing state, validation, and model access. */\nexport interface ModelHandle<S extends object, M extends Model<S>> {\n state: S;\n errors: ValidationErrors<S>;\n valid: boolean;\n dirty: boolean;\n model: M;\n}\n\n/**\n * Bind to a component-scoped Model with validation and dirty state exposed.\n */\nexport function useModel<M extends Model<any>>(\n factory: () => M\n): ModelHandle<StateOf<M>, M> {\n const modelRef = useRef<M | null>(null);\n const mountedRef = useRef(false);\n\n if (!modelRef.current || modelRef.current.disposed) {\n modelRef.current = factory();\n }\n\n useSyncExternalStore(\n (onStoreChange) => modelRef.current!.subscribe(onStoreChange),\n () => modelRef.current!.state,\n () => modelRef.current!.state,\n );\n\n useEffect(() => {\n mountedRef.current = true;\n if (isInitializable(modelRef.current)) {\n modelRef.current.init();\n }\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n modelRef.current?.dispose();\n }\n }, 0);\n };\n }, []);\n\n const model = modelRef.current;\n\n return {\n state: model.state,\n errors: model.errors,\n valid: model.valid,\n dirty: model.dirty,\n model,\n };\n}\n\n/** Return type of `useField`, providing a single field's value, error, and setter. */\nexport interface FieldHandle<V> {\n value: V;\n error: string | undefined;\n set: (value: V) => void;\n}\n\n/**\n * Bind to a single Model field with surgical re-renders.\n */\nexport function useField<S extends object, K extends keyof S>(\n model: Model<S>,\n field: K\n): FieldHandle<S[K]> {\n // Track the field value and error for comparison\n const getSnapshot = useCallback(() => {\n return {\n value: model.state[field],\n error: model.errors[field],\n };\n }, [model, field]);\n\n // Use object comparison for subscription\n const cachedRef = useRef(getSnapshot());\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n return model.subscribe(() => {\n const next = getSnapshot();\n const current = cachedRef.current;\n\n // Only trigger re-render if field value or error changed\n if (next.value !== current.value || next.error !== current.error) {\n cachedRef.current = next;\n onStoreChange();\n }\n });\n },\n [model, getSnapshot]\n );\n\n const snapshot = useSyncExternalStore(\n subscribe,\n () => cachedRef.current,\n () => cachedRef.current\n );\n\n const set = useCallback(\n (value: S[K]) => {\n // Access the protected set method through type assertion\n // The Model subclass should expose a setter method\n const partial: Partial<S> = { [field]: value } as unknown as Partial<S>;\n (model as unknown as { set: (partial: Partial<S>) => void }).set(partial);\n },\n [model, field]\n );\n\n return {\n value: snapshot.value,\n error: snapshot.error,\n set,\n };\n}\n"],"names":[],"mappings":";;AAkBO,SAAS,SACd,SAC4B;AAC5B,QAAM,WAAW,OAAiB,IAAI;AACtC,QAAM,aAAa,OAAO,KAAK;AAE/B,MAAI,CAAC,SAAS,WAAW,SAAS,QAAQ,UAAU;AAClD,aAAS,UAAU,QAAA;AAAA,EACrB;AAEA;AAAA,IACE,CAAC,kBAAkB,SAAS,QAAS,UAAU,aAAa;AAAA,IAC5D,MAAM,SAAS,QAAS;AAAA,IACxB,MAAM,SAAS,QAAS;AAAA,EAAA;AAG1B,YAAU,MAAM;AACd,eAAW,UAAU;AACrB,QAAI,gBAAgB,SAAS,OAAO,GAAG;AACrC,eAAS,QAAQ,KAAA;AAAA,IACnB;AACA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,mBAAS,SAAS,QAAA;AAAA,QACpB;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,QAAM,QAAQ,SAAS;AAEvB,SAAO;AAAA,IACL,OAAO,MAAM;AAAA,IACb,QAAQ,MAAM;AAAA,IACd,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,IACb;AAAA,EAAA;AAEJ;AAYO,SAAS,SACd,OACA,OACmB;AAEnB,QAAM,cAAc,YAAY,MAAM;AACpC,WAAO;AAAA,MACL,OAAO,MAAM,MAAM,KAAK;AAAA,MACxB,OAAO,MAAM,OAAO,KAAK;AAAA,IAAA;AAAA,EAE7B,GAAG,CAAC,OAAO,KAAK,CAAC;AAGjB,QAAM,YAAY,OAAO,aAAa;AAEtC,QAAM,YAAY;AAAA,IAChB,CAAC,kBAA8B;AAC7B,aAAO,MAAM,UAAU,MAAM;AAC3B,cAAM,OAAO,YAAA;AACb,cAAM,UAAU,UAAU;AAG1B,YAAI,KAAK,UAAU,QAAQ,SAAS,KAAK,UAAU,QAAQ,OAAO;AAChE,oBAAU,UAAU;AACpB,wBAAA;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,CAAC,OAAO,WAAW;AAAA,EAAA;AAGrB,QAAM,WAAW;AAAA,IACf;AAAA,IACA,MAAM,UAAU;AAAA,IAChB,MAAM,UAAU;AAAA,EAAA;AAGlB,QAAM,MAAM;AAAA,IACV,CAAC,UAAgB;AAGf,YAAM,UAAsB,EAAE,CAAC,KAAK,GAAG,MAAA;AACtC,YAA4D,IAAI,OAAO;AAAA,IAC1E;AAAA,IACA,CAAC,OAAO,KAAK;AAAA,EAAA;AAGf,SAAO;AAAA,IACL,OAAO,SAAS;AAAA,IAChB,OAAO,SAAS;AAAA,IAChB;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const react = require("react");
4
+ const singleton = require("../singleton.cjs");
5
+ const useInstance = require("./use-instance.cjs");
6
+ const guards = require("./guards.cjs");
7
+ function useSingleton(Class, ...args) {
8
+ const instance = singleton.singleton(Class, ...args);
9
+ react.useEffect(() => {
10
+ if (guards.isInitializable(instance)) {
11
+ instance.init();
12
+ }
13
+ }, [instance]);
14
+ if (guards.isSubscribable(instance)) {
15
+ const state = useInstance.useInstance(instance);
16
+ return [state, instance];
17
+ }
18
+ return instance;
19
+ }
20
+ exports.useSingleton = useSingleton;
21
+ //# sourceMappingURL=use-singleton.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-singleton.cjs","sources":["../../src/react/use-singleton.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { singleton } from '../singleton';\nimport { useInstance } from './use-instance';\nimport { isSubscribable, isInitializable } from './guards';\nimport type { StateOf } from './types';\n\n/**\n * Get singleton Subscribable instance and subscribe to its state.\n * Returns [state, instance] tuple.\n */\nexport function useSingleton<\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 * Get singleton Disposable instance (non-Subscribable).\n * Returns the instance directly.\n */\nexport function useSingleton<T extends Disposable, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T;\n\n// Implementation\nexport function useSingleton<T extends Disposable, S = StateOf<T>, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T] | T {\n const instance = singleton(Class, ...args);\n\n useEffect(() => {\n if (isInitializable(instance)) {\n instance.init();\n }\n }, [instance]);\n\n if (isSubscribable(instance)) {\n const state = useInstance(instance) as S;\n return [state, instance];\n }\n\n return instance;\n}\n"],"names":["singleton","useEffect","isInitializable","isSubscribable","useInstance"],"mappings":";;;;;;AA8BO,SAAS,aACd,UACG,MACS;AACZ,QAAM,WAAWA,UAAAA,UAAU,OAAO,GAAG,IAAI;AAEzCC,QAAAA,UAAU,MAAM;AACd,QAAIC,OAAAA,gBAAgB,QAAQ,GAAG;AAC7B,eAAS,KAAA;AAAA,IACX;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAEb,MAAIC,OAAAA,eAAe,QAAQ,GAAG;AAC5B,UAAM,QAAQC,YAAAA,YAAY,QAAQ;AAClC,WAAO,CAAC,OAAO,QAAQ;AAAA,EACzB;AAEA,SAAO;AACT;;"}
@@ -0,0 +1,21 @@
1
+ import { useEffect } from "react";
2
+ import { singleton } from "../singleton.js";
3
+ import { useInstance } from "./use-instance.js";
4
+ import { isInitializable, isSubscribable } from "./guards.js";
5
+ function useSingleton(Class, ...args) {
6
+ const instance = singleton(Class, ...args);
7
+ useEffect(() => {
8
+ if (isInitializable(instance)) {
9
+ instance.init();
10
+ }
11
+ }, [instance]);
12
+ if (isSubscribable(instance)) {
13
+ const state = useInstance(instance);
14
+ return [state, instance];
15
+ }
16
+ return instance;
17
+ }
18
+ export {
19
+ useSingleton
20
+ };
21
+ //# sourceMappingURL=use-singleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-singleton.js","sources":["../../src/react/use-singleton.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { Subscribable, Disposable } from '../types';\nimport { singleton } from '../singleton';\nimport { useInstance } from './use-instance';\nimport { isSubscribable, isInitializable } from './guards';\nimport type { StateOf } from './types';\n\n/**\n * Get singleton Subscribable instance and subscribe to its state.\n * Returns [state, instance] tuple.\n */\nexport function useSingleton<\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 * Get singleton Disposable instance (non-Subscribable).\n * Returns the instance directly.\n */\nexport function useSingleton<T extends Disposable, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): T;\n\n// Implementation\nexport function useSingleton<T extends Disposable, S = StateOf<T>, Args extends unknown[] = unknown[]>(\n Class: new (...args: Args) => T,\n ...args: Args\n): [S, T] | T {\n const instance = singleton(Class, ...args);\n\n useEffect(() => {\n if (isInitializable(instance)) {\n instance.init();\n }\n }, [instance]);\n\n if (isSubscribable(instance)) {\n const state = useInstance(instance) as S;\n return [state, instance];\n }\n\n return instance;\n}\n"],"names":[],"mappings":";;;;AA8BO,SAAS,aACd,UACG,MACS;AACZ,QAAM,WAAW,UAAU,OAAO,GAAG,IAAI;AAEzC,YAAU,MAAM;AACd,QAAI,gBAAgB,QAAQ,GAAG;AAC7B,eAAS,KAAA;AAAA,IACX;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAEb,MAAI,eAAe,QAAQ,GAAG;AAC5B,UAAM,QAAQ,YAAY,QAAQ;AAClC,WAAO,CAAC,OAAO,QAAQ;AAAA,EACzB;AAEA,SAAO;AACT;"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const react = require("react");
4
+ const singleton = require("../singleton.cjs");
5
+ function useTeardown(...Classes) {
6
+ const mountedRef = react.useRef(false);
7
+ react.useEffect(() => {
8
+ mountedRef.current = true;
9
+ return () => {
10
+ mountedRef.current = false;
11
+ setTimeout(() => {
12
+ if (!mountedRef.current) {
13
+ for (const Class of Classes) {
14
+ singleton.teardown(Class);
15
+ }
16
+ }
17
+ }, 0);
18
+ };
19
+ }, []);
20
+ }
21
+ exports.useTeardown = useTeardown;
22
+ //# sourceMappingURL=use-teardown.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-teardown.cjs","sources":["../../src/react/use-teardown.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport type { Disposable } from '../types';\nimport { teardown } from '../singleton';\n\n/**\n * Teardown singleton class(es) on unmount.\n * Uses deferred disposal to handle StrictMode's double-mount cycle.\n */\nexport function useTeardown(\n ...Classes: Array<new (...args: unknown[]) => Disposable>\n): void {\n const mountedRef = useRef(false);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n for (const Class of Classes) {\n teardown(Class);\n }\n }\n }, 0);\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n}\n"],"names":["useRef","useEffect","teardown"],"mappings":";;;;AAQO,SAAS,eACX,SACG;AACN,QAAM,aAAaA,MAAAA,OAAO,KAAK;AAE/BC,QAAAA,UAAU,MAAM;AACd,eAAW,UAAU;AACrB,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,qBAAW,SAAS,SAAS;AAC3BC,sBAAAA,SAAS,KAAK;AAAA,UAChB;AAAA,QACF;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE;AACP;;"}
@@ -0,0 +1,22 @@
1
+ import { useRef, useEffect } from "react";
2
+ import { teardown } from "../singleton.js";
3
+ function useTeardown(...Classes) {
4
+ const mountedRef = useRef(false);
5
+ useEffect(() => {
6
+ mountedRef.current = true;
7
+ return () => {
8
+ mountedRef.current = false;
9
+ setTimeout(() => {
10
+ if (!mountedRef.current) {
11
+ for (const Class of Classes) {
12
+ teardown(Class);
13
+ }
14
+ }
15
+ }, 0);
16
+ };
17
+ }, []);
18
+ }
19
+ export {
20
+ useTeardown
21
+ };
22
+ //# sourceMappingURL=use-teardown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-teardown.js","sources":["../../src/react/use-teardown.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport type { Disposable } from '../types';\nimport { teardown } from '../singleton';\n\n/**\n * Teardown singleton class(es) on unmount.\n * Uses deferred disposal to handle StrictMode's double-mount cycle.\n */\nexport function useTeardown(\n ...Classes: Array<new (...args: unknown[]) => Disposable>\n): void {\n const mountedRef = useRef(false);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n setTimeout(() => {\n if (!mountedRef.current) {\n for (const Class of Classes) {\n teardown(Class);\n }\n }\n }, 0);\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n}\n"],"names":[],"mappings":";;AAQO,SAAS,eACX,SACG;AACN,QAAM,aAAa,OAAO,KAAK;AAE/B,YAAU,MAAM;AACd,eAAW,UAAU;AACrB,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW,MAAM;AACf,YAAI,CAAC,WAAW,SAAS;AACvB,qBAAW,SAAS,SAAS;AAC3B,qBAAS,KAAK;AAAA,UAChB;AAAA,QACF;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE;AACP;"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const PersistentCollection = require("../PersistentCollection.cjs");
4
+ const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
5
+ let _adapter = null;
6
+ class NativeCollection extends PersistentCollection.PersistentCollection {
7
+ /**
8
+ * Configure the default storage adapter for all NativeCollection subclasses.
9
+ * Call once at app startup. Per-class method overrides take priority.
10
+ */
11
+ static configure(adapter) {
12
+ _adapter = adapter;
13
+ }
14
+ /** Reset the configured adapter (for testing). */
15
+ static resetAdapter() {
16
+ _adapter = null;
17
+ }
18
+ // ── Per-class override points ──
19
+ getItem(key) {
20
+ if (_adapter) return _adapter.getItem(key);
21
+ if (__DEV__) {
22
+ throw new Error(
23
+ `[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`
24
+ );
25
+ }
26
+ throw new Error("[mvc-kit] No storage adapter configured.");
27
+ }
28
+ setItem(key, value) {
29
+ if (_adapter) return _adapter.setItem(key, value);
30
+ if (__DEV__) {
31
+ throw new Error(
32
+ `[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`
33
+ );
34
+ }
35
+ throw new Error("[mvc-kit] No storage adapter configured.");
36
+ }
37
+ removeItem(key) {
38
+ if (_adapter) return _adapter.removeItem(key);
39
+ if (__DEV__) {
40
+ throw new Error(
41
+ `[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`
42
+ );
43
+ }
44
+ throw new Error("[mvc-kit] No storage adapter configured.");
45
+ }
46
+ // ── Persist interface (blob strategy) ──
47
+ async persistGetAll() {
48
+ const raw = await this.getItem(this.storageKey);
49
+ if (!raw) return [];
50
+ try {
51
+ return this.deserialize(raw);
52
+ } catch {
53
+ if (__DEV__) {
54
+ console.warn(
55
+ `[mvc-kit] Corrupted data in storage key "${this.storageKey}". Ignoring stored data.`
56
+ );
57
+ }
58
+ return [];
59
+ }
60
+ }
61
+ async persistGet(id) {
62
+ const all = await this.persistGetAll();
63
+ return all.find((i) => i.id === id) ?? null;
64
+ }
65
+ async persistSet(_items) {
66
+ await this.setItem(this.storageKey, this.serialize([...this.items]));
67
+ }
68
+ async persistRemove(_ids) {
69
+ await this.setItem(this.storageKey, this.serialize([...this.items]));
70
+ }
71
+ async persistClear() {
72
+ await this.removeItem(this.storageKey);
73
+ }
74
+ }
75
+ exports.NativeCollection = NativeCollection;
76
+ //# sourceMappingURL=NativeCollection.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeCollection.cjs","sources":["../../src/react-native/NativeCollection.ts"],"sourcesContent":["import { PersistentCollection } from '../PersistentCollection';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\ninterface StorageAdapter {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n removeItem(key: string): Promise<void>;\n}\n\nlet _adapter: StorageAdapter | null = null;\n\n/**\n * PersistentCollection for React Native, backed by any async key-value store.\n * Uses blob strategy (full state as a single JSON string under `storageKey`).\n *\n * **Requires manual `hydrate()` call** (async storage).\n *\n * ## Setup (once at app startup)\n *\n * ```ts\n * import { NativeCollection } from 'mvc-kit/react-native';\n * import AsyncStorage from '@react-native-async-storage/async-storage';\n *\n * NativeCollection.configure({\n * getItem: (key) => AsyncStorage.getItem(key),\n * setItem: (key, value) => AsyncStorage.setItem(key, value),\n * removeItem: (key) => AsyncStorage.removeItem(key),\n * });\n * ```\n *\n * ## Usage\n *\n * ```ts\n * class TodosCollection extends NativeCollection<Todo> {\n * protected readonly storageKey = 'todos';\n * }\n * ```\n *\n * ## Per-class override (edge cases)\n *\n * ```ts\n * class SecureCollection extends NativeCollection<Secret> {\n * protected readonly storageKey = 'secrets';\n * protected async getItem(key: string) { return SecureStore.getItem(key); }\n * protected async setItem(key: string, value: string) { await SecureStore.setItem(key, value); }\n * protected async removeItem(key: string) { await SecureStore.removeItem(key); }\n * }\n * ```\n */\nexport abstract class NativeCollection<\n T extends { id: string | number },\n> extends PersistentCollection<T> {\n /**\n * Configure the default storage adapter for all NativeCollection subclasses.\n * Call once at app startup. Per-class method overrides take priority.\n */\n static configure(adapter: StorageAdapter): void {\n _adapter = adapter;\n }\n\n /** Reset the configured adapter (for testing). */\n static resetAdapter(): void {\n _adapter = null;\n }\n\n // ── Per-class override points ──\n\n protected getItem(key: string): Promise<string | null> {\n if (_adapter) return _adapter.getItem(key);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n protected setItem(key: string, value: string): Promise<void> {\n if (_adapter) return _adapter.setItem(key, value);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n protected removeItem(key: string): Promise<void> {\n if (_adapter) return _adapter.removeItem(key);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n // ── Persist interface (blob strategy) ──\n\n protected async persistGetAll(): Promise<T[]> {\n const raw = await this.getItem(this.storageKey);\n if (!raw) return [];\n try {\n return this.deserialize(raw);\n } catch {\n if (__DEV__) {\n console.warn(\n `[mvc-kit] Corrupted data in storage key \"${this.storageKey}\". Ignoring stored data.`,\n );\n }\n return [];\n }\n }\n\n protected async persistGet(id: T['id']): Promise<T | null> {\n const all = await this.persistGetAll();\n return all.find((i) => i.id === id) ?? null;\n }\n\n protected async persistSet(_items: T[]): Promise<void> {\n await this.setItem(this.storageKey, this.serialize([...this.items]));\n }\n\n protected async persistRemove(_ids: T['id'][]): Promise<void> {\n await this.setItem(this.storageKey, this.serialize([...this.items]));\n }\n\n protected async persistClear(): Promise<void> {\n await this.removeItem(this.storageKey);\n }\n}\n"],"names":["PersistentCollection"],"mappings":";;;AAEA,MAAM,UAAU,OAAO,oBAAoB,eAAe;AAQ1D,IAAI,WAAkC;AAwC/B,MAAe,yBAEZA,qBAAAA,qBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhC,OAAO,UAAU,SAA+B;AAC9C,eAAW;AAAA,EACb;AAAA;AAAA,EAGA,OAAO,eAAqB;AAC1B,eAAW;AAAA,EACb;AAAA;AAAA,EAIU,QAAQ,KAAqC;AACrD,QAAI,SAAU,QAAO,SAAS,QAAQ,GAAG;AACzC,QAAI,SAAS;AACX,YAAM,IAAI;AAAA,QACR,gDAAgD,KAAK,YAAY,IAAI;AAAA,MAAA;AAAA,IAGzE;AACA,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA,EAEU,QAAQ,KAAa,OAA8B;AAC3D,QAAI,SAAU,QAAO,SAAS,QAAQ,KAAK,KAAK;AAChD,QAAI,SAAS;AACX,YAAM,IAAI;AAAA,QACR,gDAAgD,KAAK,YAAY,IAAI;AAAA,MAAA;AAAA,IAGzE;AACA,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA,EAEU,WAAW,KAA4B;AAC/C,QAAI,SAAU,QAAO,SAAS,WAAW,GAAG;AAC5C,QAAI,SAAS;AACX,YAAM,IAAI;AAAA,QACR,gDAAgD,KAAK,YAAY,IAAI;AAAA,MAAA;AAAA,IAGzE;AACA,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA;AAAA,EAIA,MAAgB,gBAA8B;AAC5C,UAAM,MAAM,MAAM,KAAK,QAAQ,KAAK,UAAU;AAC9C,QAAI,CAAC,IAAK,QAAO,CAAA;AACjB,QAAI;AACF,aAAO,KAAK,YAAY,GAAG;AAAA,IAC7B,QAAQ;AACN,UAAI,SAAS;AACX,gBAAQ;AAAA,UACN,4CAA4C,KAAK,UAAU;AAAA,QAAA;AAAA,MAE/D;AACA,aAAO,CAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAgB,WAAW,IAAgC;AACzD,UAAM,MAAM,MAAM,KAAK,cAAA;AACvB,WAAO,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK;AAAA,EACzC;AAAA,EAEA,MAAgB,WAAW,QAA4B;AACrD,UAAM,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC;AAAA,EACrE;AAAA,EAEA,MAAgB,cAAc,MAAgC;AAC5D,UAAM,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC;AAAA,EACrE;AAAA,EAEA,MAAgB,eAA8B;AAC5C,UAAM,KAAK,WAAW,KAAK,UAAU;AAAA,EACvC;AACF;;"}
@@ -0,0 +1,76 @@
1
+ import { PersistentCollection } from "../PersistentCollection.js";
2
+ const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
3
+ let _adapter = null;
4
+ class NativeCollection extends PersistentCollection {
5
+ /**
6
+ * Configure the default storage adapter for all NativeCollection subclasses.
7
+ * Call once at app startup. Per-class method overrides take priority.
8
+ */
9
+ static configure(adapter) {
10
+ _adapter = adapter;
11
+ }
12
+ /** Reset the configured adapter (for testing). */
13
+ static resetAdapter() {
14
+ _adapter = null;
15
+ }
16
+ // ── Per-class override points ──
17
+ getItem(key) {
18
+ if (_adapter) return _adapter.getItem(key);
19
+ if (__DEV__) {
20
+ throw new Error(
21
+ `[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`
22
+ );
23
+ }
24
+ throw new Error("[mvc-kit] No storage adapter configured.");
25
+ }
26
+ setItem(key, value) {
27
+ if (_adapter) return _adapter.setItem(key, value);
28
+ if (__DEV__) {
29
+ throw new Error(
30
+ `[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`
31
+ );
32
+ }
33
+ throw new Error("[mvc-kit] No storage adapter configured.");
34
+ }
35
+ removeItem(key) {
36
+ if (_adapter) return _adapter.removeItem(key);
37
+ if (__DEV__) {
38
+ throw new Error(
39
+ `[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`
40
+ );
41
+ }
42
+ throw new Error("[mvc-kit] No storage adapter configured.");
43
+ }
44
+ // ── Persist interface (blob strategy) ──
45
+ async persistGetAll() {
46
+ const raw = await this.getItem(this.storageKey);
47
+ if (!raw) return [];
48
+ try {
49
+ return this.deserialize(raw);
50
+ } catch {
51
+ if (__DEV__) {
52
+ console.warn(
53
+ `[mvc-kit] Corrupted data in storage key "${this.storageKey}". Ignoring stored data.`
54
+ );
55
+ }
56
+ return [];
57
+ }
58
+ }
59
+ async persistGet(id) {
60
+ const all = await this.persistGetAll();
61
+ return all.find((i) => i.id === id) ?? null;
62
+ }
63
+ async persistSet(_items) {
64
+ await this.setItem(this.storageKey, this.serialize([...this.items]));
65
+ }
66
+ async persistRemove(_ids) {
67
+ await this.setItem(this.storageKey, this.serialize([...this.items]));
68
+ }
69
+ async persistClear() {
70
+ await this.removeItem(this.storageKey);
71
+ }
72
+ }
73
+ export {
74
+ NativeCollection
75
+ };
76
+ //# sourceMappingURL=NativeCollection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeCollection.js","sources":["../../src/react-native/NativeCollection.ts"],"sourcesContent":["import { PersistentCollection } from '../PersistentCollection';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\ninterface StorageAdapter {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n removeItem(key: string): Promise<void>;\n}\n\nlet _adapter: StorageAdapter | null = null;\n\n/**\n * PersistentCollection for React Native, backed by any async key-value store.\n * Uses blob strategy (full state as a single JSON string under `storageKey`).\n *\n * **Requires manual `hydrate()` call** (async storage).\n *\n * ## Setup (once at app startup)\n *\n * ```ts\n * import { NativeCollection } from 'mvc-kit/react-native';\n * import AsyncStorage from '@react-native-async-storage/async-storage';\n *\n * NativeCollection.configure({\n * getItem: (key) => AsyncStorage.getItem(key),\n * setItem: (key, value) => AsyncStorage.setItem(key, value),\n * removeItem: (key) => AsyncStorage.removeItem(key),\n * });\n * ```\n *\n * ## Usage\n *\n * ```ts\n * class TodosCollection extends NativeCollection<Todo> {\n * protected readonly storageKey = 'todos';\n * }\n * ```\n *\n * ## Per-class override (edge cases)\n *\n * ```ts\n * class SecureCollection extends NativeCollection<Secret> {\n * protected readonly storageKey = 'secrets';\n * protected async getItem(key: string) { return SecureStore.getItem(key); }\n * protected async setItem(key: string, value: string) { await SecureStore.setItem(key, value); }\n * protected async removeItem(key: string) { await SecureStore.removeItem(key); }\n * }\n * ```\n */\nexport abstract class NativeCollection<\n T extends { id: string | number },\n> extends PersistentCollection<T> {\n /**\n * Configure the default storage adapter for all NativeCollection subclasses.\n * Call once at app startup. Per-class method overrides take priority.\n */\n static configure(adapter: StorageAdapter): void {\n _adapter = adapter;\n }\n\n /** Reset the configured adapter (for testing). */\n static resetAdapter(): void {\n _adapter = null;\n }\n\n // ── Per-class override points ──\n\n protected getItem(key: string): Promise<string | null> {\n if (_adapter) return _adapter.getItem(key);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n protected setItem(key: string, value: string): Promise<void> {\n if (_adapter) return _adapter.setItem(key, value);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n protected removeItem(key: string): Promise<void> {\n if (_adapter) return _adapter.removeItem(key);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n // ── Persist interface (blob strategy) ──\n\n protected async persistGetAll(): Promise<T[]> {\n const raw = await this.getItem(this.storageKey);\n if (!raw) return [];\n try {\n return this.deserialize(raw);\n } catch {\n if (__DEV__) {\n console.warn(\n `[mvc-kit] Corrupted data in storage key \"${this.storageKey}\". Ignoring stored data.`,\n );\n }\n return [];\n }\n }\n\n protected async persistGet(id: T['id']): Promise<T | null> {\n const all = await this.persistGetAll();\n return all.find((i) => i.id === id) ?? null;\n }\n\n protected async persistSet(_items: T[]): Promise<void> {\n await this.setItem(this.storageKey, this.serialize([...this.items]));\n }\n\n protected async persistRemove(_ids: T['id'][]): Promise<void> {\n await this.setItem(this.storageKey, this.serialize([...this.items]));\n }\n\n protected async persistClear(): Promise<void> {\n await this.removeItem(this.storageKey);\n }\n}\n"],"names":[],"mappings":";AAEA,MAAM,UAAU,OAAO,oBAAoB,eAAe;AAQ1D,IAAI,WAAkC;AAwC/B,MAAe,yBAEZ,qBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhC,OAAO,UAAU,SAA+B;AAC9C,eAAW;AAAA,EACb;AAAA;AAAA,EAGA,OAAO,eAAqB;AAC1B,eAAW;AAAA,EACb;AAAA;AAAA,EAIU,QAAQ,KAAqC;AACrD,QAAI,SAAU,QAAO,SAAS,QAAQ,GAAG;AACzC,QAAI,SAAS;AACX,YAAM,IAAI;AAAA,QACR,gDAAgD,KAAK,YAAY,IAAI;AAAA,MAAA;AAAA,IAGzE;AACA,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA,EAEU,QAAQ,KAAa,OAA8B;AAC3D,QAAI,SAAU,QAAO,SAAS,QAAQ,KAAK,KAAK;AAChD,QAAI,SAAS;AACX,YAAM,IAAI;AAAA,QACR,gDAAgD,KAAK,YAAY,IAAI;AAAA,MAAA;AAAA,IAGzE;AACA,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA,EAEU,WAAW,KAA4B;AAC/C,QAAI,SAAU,QAAO,SAAS,WAAW,GAAG;AAC5C,QAAI,SAAS;AACX,YAAM,IAAI;AAAA,QACR,gDAAgD,KAAK,YAAY,IAAI;AAAA,MAAA;AAAA,IAGzE;AACA,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA;AAAA,EAIA,MAAgB,gBAA8B;AAC5C,UAAM,MAAM,MAAM,KAAK,QAAQ,KAAK,UAAU;AAC9C,QAAI,CAAC,IAAK,QAAO,CAAA;AACjB,QAAI;AACF,aAAO,KAAK,YAAY,GAAG;AAAA,IAC7B,QAAQ;AACN,UAAI,SAAS;AACX,gBAAQ;AAAA,UACN,4CAA4C,KAAK,UAAU;AAAA,QAAA;AAAA,MAE/D;AACA,aAAO,CAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAgB,WAAW,IAAgC;AACzD,UAAM,MAAM,MAAM,KAAK,cAAA;AACvB,WAAO,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK;AAAA,EACzC;AAAA,EAEA,MAAgB,WAAW,QAA4B;AACrD,UAAM,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC;AAAA,EACrE;AAAA,EAEA,MAAgB,cAAc,MAAgC;AAC5D,UAAM,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC;AAAA,EACrE;AAAA,EAEA,MAAgB,eAA8B;AAC5C,UAAM,KAAK,WAAW,KAAK,UAAU;AAAA,EACvC;AACF;"}
@@ -1,2 +1,5 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("./PersistentCollection-B8kNECDj.cjs"),r=typeof __MVC_KIT_DEV__<"u"&&__MVC_KIT_DEV__;let e=null;class a extends s.PersistentCollection{static configure(t){e=t}static resetAdapter(){e=null}getItem(t){if(e)return e.getItem(t);throw r?new Error(`[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`):new Error("[mvc-kit] No storage adapter configured.")}setItem(t,i){if(e)return e.setItem(t,i);throw r?new Error(`[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`):new Error("[mvc-kit] No storage adapter configured.")}removeItem(t){if(e)return e.removeItem(t);throw r?new Error(`[mvc-kit] No storage adapter configured for "${this.constructor.name}". Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`):new Error("[mvc-kit] No storage adapter configured.")}async persistGetAll(){const t=await this.getItem(this.storageKey);if(!t)return[];try{return this.deserialize(t)}catch{return r&&console.warn(`[mvc-kit] Corrupted data in storage key "${this.storageKey}". Ignoring stored data.`),[]}}async persistGet(t){return(await this.persistGetAll()).find(o=>o.id===t)??null}async persistSet(t){await this.setItem(this.storageKey,this.serialize([...this.items]))}async persistRemove(t){await this.setItem(this.storageKey,this.serialize([...this.items]))}async persistClear(){await this.removeItem(this.storageKey)}}exports.NativeCollection=a;
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const NativeCollection = require("./react-native/NativeCollection.cjs");
4
+ exports.NativeCollection = NativeCollection.NativeCollection;
2
5
  //# sourceMappingURL=react-native.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"react-native.cjs","sources":["../src/react-native/NativeCollection.ts"],"sourcesContent":["import { PersistentCollection } from '../PersistentCollection';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\ninterface StorageAdapter {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n removeItem(key: string): Promise<void>;\n}\n\nlet _adapter: StorageAdapter | null = null;\n\n/**\n * PersistentCollection for React Native, backed by any async key-value store.\n * Uses blob strategy (full state as a single JSON string under `storageKey`).\n *\n * **Requires manual `hydrate()` call** (async storage).\n *\n * ## Setup (once at app startup)\n *\n * ```ts\n * import { NativeCollection } from 'mvc-kit/react-native';\n * import AsyncStorage from '@react-native-async-storage/async-storage';\n *\n * NativeCollection.configure({\n * getItem: (key) => AsyncStorage.getItem(key),\n * setItem: (key, value) => AsyncStorage.setItem(key, value),\n * removeItem: (key) => AsyncStorage.removeItem(key),\n * });\n * ```\n *\n * ## Usage\n *\n * ```ts\n * class TodosCollection extends NativeCollection<Todo> {\n * protected readonly storageKey = 'todos';\n * }\n * ```\n *\n * ## Per-class override (edge cases)\n *\n * ```ts\n * class SecureCollection extends NativeCollection<Secret> {\n * protected readonly storageKey = 'secrets';\n * protected async getItem(key: string) { return SecureStore.getItem(key); }\n * protected async setItem(key: string, value: string) { await SecureStore.setItem(key, value); }\n * protected async removeItem(key: string) { await SecureStore.removeItem(key); }\n * }\n * ```\n */\nexport abstract class NativeCollection<\n T extends { id: string | number },\n> extends PersistentCollection<T> {\n /**\n * Configure the default storage adapter for all NativeCollection subclasses.\n * Call once at app startup. Per-class method overrides take priority.\n */\n static configure(adapter: StorageAdapter): void {\n _adapter = adapter;\n }\n\n /** Reset the configured adapter (for testing). */\n static resetAdapter(): void {\n _adapter = null;\n }\n\n // ── Per-class override points ──\n\n protected getItem(key: string): Promise<string | null> {\n if (_adapter) return _adapter.getItem(key);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n protected setItem(key: string, value: string): Promise<void> {\n if (_adapter) return _adapter.setItem(key, value);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n protected removeItem(key: string): Promise<void> {\n if (_adapter) return _adapter.removeItem(key);\n if (__DEV__) {\n throw new Error(\n `[mvc-kit] No storage adapter configured for \"${this.constructor.name}\". ` +\n `Call NativeCollection.configure() at app startup, or override getItem/setItem/removeItem.`,\n );\n }\n throw new Error('[mvc-kit] No storage adapter configured.');\n }\n\n // ── Persist interface (blob strategy) ──\n\n protected async persistGetAll(): Promise<T[]> {\n const raw = await this.getItem(this.storageKey);\n if (!raw) return [];\n try {\n return this.deserialize(raw);\n } catch {\n if (__DEV__) {\n console.warn(\n `[mvc-kit] Corrupted data in storage key \"${this.storageKey}\". Ignoring stored data.`,\n );\n }\n return [];\n }\n }\n\n protected async persistGet(id: T['id']): Promise<T | null> {\n const all = await this.persistGetAll();\n return all.find((i) => i.id === id) ?? null;\n }\n\n protected async persistSet(_items: T[]): Promise<void> {\n await this.setItem(this.storageKey, this.serialize([...this.items]));\n }\n\n protected async persistRemove(_ids: T['id'][]): Promise<void> {\n await this.setItem(this.storageKey, this.serialize([...this.items]));\n }\n\n protected async persistClear(): Promise<void> {\n await this.removeItem(this.storageKey);\n }\n}\n"],"names":["__DEV__","_adapter","NativeCollection","PersistentCollection","adapter","key","value","raw","id","i","_items","_ids"],"mappings":"uIAEMA,EAAU,OAAO,gBAAoB,KAAe,gBAQ1D,IAAIC,EAAkC,KAwC/B,MAAeC,UAEZC,EAAAA,oBAAwB,CAKhC,OAAO,UAAUC,EAA+B,CAC9CH,EAAWG,CACb,CAGA,OAAO,cAAqB,CAC1BH,EAAW,IACb,CAIU,QAAQI,EAAqC,CACrD,GAAIJ,EAAU,OAAOA,EAAS,QAAQI,CAAG,EACzC,MAAIL,EACI,IAAI,MACR,gDAAgD,KAAK,YAAY,IAAI,8FAAA,EAInE,IAAI,MAAM,0CAA0C,CAC5D,CAEU,QAAQK,EAAaC,EAA8B,CAC3D,GAAIL,EAAU,OAAOA,EAAS,QAAQI,EAAKC,CAAK,EAChD,MAAIN,EACI,IAAI,MACR,gDAAgD,KAAK,YAAY,IAAI,8FAAA,EAInE,IAAI,MAAM,0CAA0C,CAC5D,CAEU,WAAWK,EAA4B,CAC/C,GAAIJ,EAAU,OAAOA,EAAS,WAAWI,CAAG,EAC5C,MAAIL,EACI,IAAI,MACR,gDAAgD,KAAK,YAAY,IAAI,8FAAA,EAInE,IAAI,MAAM,0CAA0C,CAC5D,CAIA,MAAgB,eAA8B,CAC5C,MAAMO,EAAM,MAAM,KAAK,QAAQ,KAAK,UAAU,EAC9C,GAAI,CAACA,EAAK,MAAO,CAAA,EACjB,GAAI,CACF,OAAO,KAAK,YAAYA,CAAG,CAC7B,MAAQ,CACN,OAAIP,GACF,QAAQ,KACN,4CAA4C,KAAK,UAAU,0BAAA,EAGxD,CAAA,CACT,CACF,CAEA,MAAgB,WAAWQ,EAAgC,CAEzD,OADY,MAAM,KAAK,cAAA,GACZ,KAAMC,GAAMA,EAAE,KAAOD,CAAE,GAAK,IACzC,CAEA,MAAgB,WAAWE,EAA4B,CACrD,MAAM,KAAK,QAAQ,KAAK,WAAY,KAAK,UAAU,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CACrE,CAEA,MAAgB,cAAcC,EAAgC,CAC5D,MAAM,KAAK,QAAQ,KAAK,WAAY,KAAK,UAAU,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CACrE,CAEA,MAAgB,cAA8B,CAC5C,MAAM,KAAK,WAAW,KAAK,UAAU,CACvC,CACF"}
1
+ {"version":3,"file":"react-native.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}