mvc-kit 2.12.0 → 2.12.2

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 (139) hide show
  1. package/agent-config/bin/postinstall.mjs +5 -3
  2. package/agent-config/bin/setup.mjs +3 -4
  3. package/agent-config/claude-code/agents/mvc-kit-architect.md +14 -0
  4. package/agent-config/claude-code/skills/guide/api-reference.md +24 -2
  5. package/agent-config/lib/install-claude.mjs +19 -33
  6. package/dist/Model.cjs +9 -1
  7. package/dist/Model.cjs.map +1 -1
  8. package/dist/Model.d.ts +1 -1
  9. package/dist/Model.d.ts.map +1 -1
  10. package/dist/Model.js +9 -1
  11. package/dist/Model.js.map +1 -1
  12. package/dist/ViewModel.cjs +9 -1
  13. package/dist/ViewModel.cjs.map +1 -1
  14. package/dist/ViewModel.d.ts +1 -1
  15. package/dist/ViewModel.d.ts.map +1 -1
  16. package/dist/ViewModel.js +9 -1
  17. package/dist/ViewModel.js.map +1 -1
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/mvc-kit.cjs +3 -0
  21. package/dist/mvc-kit.cjs.map +1 -1
  22. package/dist/mvc-kit.js +3 -0
  23. package/dist/mvc-kit.js.map +1 -1
  24. package/dist/produceDraft.cjs +105 -0
  25. package/dist/produceDraft.cjs.map +1 -0
  26. package/dist/produceDraft.d.ts +19 -0
  27. package/dist/produceDraft.d.ts.map +1 -0
  28. package/dist/produceDraft.js +105 -0
  29. package/dist/produceDraft.js.map +1 -0
  30. package/package.json +4 -2
  31. package/src/Channel.md +408 -0
  32. package/src/Channel.test.ts +957 -0
  33. package/src/Channel.ts +429 -0
  34. package/src/Collection.md +533 -0
  35. package/src/Collection.test.ts +1559 -0
  36. package/src/Collection.ts +653 -0
  37. package/src/Controller.md +306 -0
  38. package/src/Controller.test.ts +380 -0
  39. package/src/Controller.ts +90 -0
  40. package/src/EventBus.md +308 -0
  41. package/src/EventBus.test.ts +295 -0
  42. package/src/EventBus.ts +110 -0
  43. package/src/Feed.md +218 -0
  44. package/src/Feed.test.ts +442 -0
  45. package/src/Feed.ts +101 -0
  46. package/src/Model.md +524 -0
  47. package/src/Model.test.ts +642 -0
  48. package/src/Model.ts +260 -0
  49. package/src/Pagination.md +168 -0
  50. package/src/Pagination.test.ts +244 -0
  51. package/src/Pagination.ts +92 -0
  52. package/src/Pending.md +380 -0
  53. package/src/Pending.test.ts +1719 -0
  54. package/src/Pending.ts +390 -0
  55. package/src/PersistentCollection.md +183 -0
  56. package/src/PersistentCollection.test.ts +649 -0
  57. package/src/PersistentCollection.ts +375 -0
  58. package/src/Resource.ViewModel.test.ts +503 -0
  59. package/src/Resource.md +239 -0
  60. package/src/Resource.test.ts +786 -0
  61. package/src/Resource.ts +231 -0
  62. package/src/Selection.md +155 -0
  63. package/src/Selection.test.ts +326 -0
  64. package/src/Selection.ts +117 -0
  65. package/src/Service.md +440 -0
  66. package/src/Service.test.ts +241 -0
  67. package/src/Service.ts +72 -0
  68. package/src/Sorting.md +170 -0
  69. package/src/Sorting.test.ts +334 -0
  70. package/src/Sorting.ts +135 -0
  71. package/src/Trackable.md +166 -0
  72. package/src/Trackable.test.ts +236 -0
  73. package/src/Trackable.ts +129 -0
  74. package/src/ViewModel.async.test.ts +813 -0
  75. package/src/ViewModel.derived.test.ts +1583 -0
  76. package/src/ViewModel.md +1111 -0
  77. package/src/ViewModel.test.ts +1236 -0
  78. package/src/ViewModel.ts +800 -0
  79. package/src/bindPublicMethods.test.ts +126 -0
  80. package/src/bindPublicMethods.ts +48 -0
  81. package/src/env.d.ts +5 -0
  82. package/src/errors.test.ts +155 -0
  83. package/src/errors.ts +133 -0
  84. package/src/index.ts +49 -0
  85. package/src/produceDraft.md +90 -0
  86. package/src/produceDraft.test.ts +394 -0
  87. package/src/produceDraft.ts +168 -0
  88. package/src/react/components/CardList.md +97 -0
  89. package/src/react/components/CardList.test.tsx +142 -0
  90. package/src/react/components/CardList.tsx +68 -0
  91. package/src/react/components/DataTable.md +179 -0
  92. package/src/react/components/DataTable.test.tsx +599 -0
  93. package/src/react/components/DataTable.tsx +267 -0
  94. package/src/react/components/InfiniteScroll.md +116 -0
  95. package/src/react/components/InfiniteScroll.test.tsx +218 -0
  96. package/src/react/components/InfiniteScroll.tsx +70 -0
  97. package/src/react/components/types.ts +90 -0
  98. package/src/react/derived.test.tsx +261 -0
  99. package/src/react/guards.ts +24 -0
  100. package/src/react/index.ts +40 -0
  101. package/src/react/provider.test.tsx +143 -0
  102. package/src/react/provider.tsx +55 -0
  103. package/src/react/strict-mode.test.tsx +266 -0
  104. package/src/react/types.ts +25 -0
  105. package/src/react/use-event-bus.md +214 -0
  106. package/src/react/use-event-bus.test.tsx +168 -0
  107. package/src/react/use-event-bus.ts +40 -0
  108. package/src/react/use-instance.md +204 -0
  109. package/src/react/use-instance.test.tsx +350 -0
  110. package/src/react/use-instance.ts +60 -0
  111. package/src/react/use-local.md +457 -0
  112. package/src/react/use-local.rapid-remount.test.tsx +503 -0
  113. package/src/react/use-local.test.tsx +692 -0
  114. package/src/react/use-local.ts +165 -0
  115. package/src/react/use-model.md +364 -0
  116. package/src/react/use-model.test.tsx +394 -0
  117. package/src/react/use-model.ts +161 -0
  118. package/src/react/use-singleton.md +415 -0
  119. package/src/react/use-singleton.test.tsx +296 -0
  120. package/src/react/use-singleton.ts +69 -0
  121. package/src/react/use-subscribe-only.ts +39 -0
  122. package/src/react/use-teardown.md +169 -0
  123. package/src/react/use-teardown.test.tsx +86 -0
  124. package/src/react/use-teardown.ts +27 -0
  125. package/src/react-native/NativeCollection.test.ts +250 -0
  126. package/src/react-native/NativeCollection.ts +138 -0
  127. package/src/react-native/index.ts +1 -0
  128. package/src/singleton.md +310 -0
  129. package/src/singleton.test.ts +204 -0
  130. package/src/singleton.ts +70 -0
  131. package/src/types.ts +70 -0
  132. package/src/walkPrototypeChain.ts +22 -0
  133. package/src/web/IndexedDBCollection.test.ts +235 -0
  134. package/src/web/IndexedDBCollection.ts +66 -0
  135. package/src/web/WebStorageCollection.test.ts +214 -0
  136. package/src/web/WebStorageCollection.ts +116 -0
  137. package/src/web/idb.ts +184 -0
  138. package/src/web/index.ts +2 -0
  139. package/src/wrapAsyncMethods.ts +249 -0
@@ -57,7 +57,7 @@ export declare abstract class ViewModel<S extends object = EmptyViewModelState,
57
57
  * NOT called for subscribable member notifications — those use
58
58
  * a separate notification path (see _trackSubscribables).
59
59
  */
60
- protected set(partialOrUpdater: Partial<S> | Updater<S>): void;
60
+ protected set(partialOrUpdater: Partial<S> | Updater<S> | ((draft: S) => void)): void;
61
61
  /**
62
62
  * Emits a typed event via the internal EventBus.
63
63
  * Safe to call during dispose cleanup callbacks.
@@ -1 +1 @@
1
- {"version":3,"file":"ViewModel.d.ts","sourceRoot":"","sources":["../src/ViewModel.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ViewModel.d.ts","sourceRoot":"","sources":["../src/ViewModel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAKtC,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGxF,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AA2F1D,MAAM,MAAM,eAAe,CAAC,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI;KAC1D,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK;CAC/F,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;AAEhC,KAAK,QAAQ,CAAC,CAAC,IAAI;IACjB,QAAQ,EAAE,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,GAAG,SAAS;CAC9C,CAAC;AAOF,KAAK,mBAAmB,GAAG;IAAE,OAAO,EAAE,qBAAqB,CAAA;CAAC,CAAA;AAE5D;;;GAGG;AACH,8BAAsB,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,mBAAmB,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,CAAE,YAAW,YAAY,CAAC,CAAC,CAAC;IACpI,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,SAAS,CAA4B;IAG7C,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,eAAe,CAAoC;IAI3D,OAAO,CAAC,YAAY,CAA+C;IACnE,OAAO,CAAC,eAAe,CAAuC;IAC9D,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,UAAU,CAAoC;IAEtD,gFAAgF;IAChF,MAAM,CAAC,aAAa,SAAQ;gBAEhB,GAAG,IAAI,EAAE,mBAAmB,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;IAO/F,mCAAmC;IACnC,IAAI,KAAK,IAAI,CAAC,CAEb;IAED,+CAA+C;IAC/C,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,sCAAsC;IACtC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,6EAA6E;IAC7E,IAAI,aAAa,IAAI,WAAW,CAK/B;IAED,4EAA4E;IAC5E,IAAI,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,CAKxB;IAED,iFAAiF;IACjF,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5B;;;;;;;;;OASG;IACH,SAAS,CAAC,GAAG,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI;IAmDrF;;;;OAIG;IACH,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAQhE,oEAAoE;IACpE,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAY5C,0EAA0E;IAC1E,OAAO,IAAI,IAAI;IAoBf;;;OAGG;IACH,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BzC;;;;;OAKG;IACH,SAAS,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAO1C,2FAA2F;IAC3F,SAAS,CAAC,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAOpF,mHAAmH;IACnH,SAAS,CAAC,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS;QAAE,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;KAAE,EAC1G,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,GAC7C,MAAM,IAAI;IAOb,2IAA2I;IAC3I,SAAS,CAAC,WAAW,CACnB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS;QAAE,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;KAAE,EAErG,OAAO,EAAE,CAAC,EACV,IAAI,EAAE,CAAC,EACP,MAAM,EAAE;QAAE,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAA;KAAE,GACjD,MAAM,IAAI;IAOb,kFAAkF;IAClF,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI;IACxC,4FAA4F;IAC5F,SAAS,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IACzC,uFAAuF;IACvF,SAAS,CAAC,SAAS,CAAC,IAAI,IAAI;IAI5B,gFAAgF;IAChF,IAAI,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,CAsB1B;IAED,qEAAqE;IACrE,cAAc,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAOhD,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,sBAAsB;IAU9B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAcrB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAsDvB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,kBAAkB;IA8B1B;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,mBAAmB;IAgD3B;;;;;OAKG;IACH,OAAO,CAAC,WAAW;IAcnB;;;;;;;;;OASG;IACH,OAAO,CAAC,WAAW;CA+IpB"}
package/dist/ViewModel.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { EventBus } from "./EventBus.js";
2
2
  import { walkPrototypeChain } from "./walkPrototypeChain.js";
3
3
  import { wrapAsyncMethods } from "./wrapAsyncMethods.js";
4
+ import { resolveDraftUpdater } from "./produceDraft.js";
4
5
  const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
5
6
  function freeze(obj) {
6
7
  return __DEV__ ? Object.freeze(obj) : obj;
@@ -121,7 +122,14 @@ class ViewModel {
121
122
  );
122
123
  return;
123
124
  }
124
- const partial = typeof partialOrUpdater === "function" ? partialOrUpdater(this._state) : partialOrUpdater;
125
+ let partial;
126
+ if (typeof partialOrUpdater === "function") {
127
+ const result = resolveDraftUpdater(this._state, partialOrUpdater);
128
+ if (!result) return;
129
+ partial = result;
130
+ } else {
131
+ partial = partialOrUpdater;
132
+ }
125
133
  let hasChanges = false;
126
134
  const current = this._state;
127
135
  for (const key in partial) {
@@ -1 +1 @@
1
- {"version":3,"file":"ViewModel.js","sources":["../src/ViewModel.ts"],"sourcesContent":["import { EventBus } from './EventBus';\nimport { walkPrototypeChain } from './walkPrototypeChain';\nimport { wrapAsyncMethods } from './wrapAsyncMethods';\nimport type { InternalTaskState } from './wrapAsyncMethods';\nimport type { Listener, Updater, Subscribable, TaskState, EventPayload } from './types';\n\n// Re-export for backwards compatibility\nexport type { Listener, Updater } from './types';\nexport { walkPrototypeChain } from './walkPrototypeChain';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\nfunction freeze<T>(obj: T): T {\n return __DEV__ ? Object.freeze(obj) as T : obj;\n}\n\n// ── Class metadata cache ─────────────────────────────────────────\n// Caches prototype walk results per class to avoid repeated Object.getOwnPropertyDescriptors\n// on every init(). Single walk extracts getters, methods, and reserved key violations.\n\ninterface ClassMemberInfo {\n getters: Array<{ key: string; get: () => unknown }>;\n methods: Array<{ key: string; fn: Function }>;\n reservedKeys: string[];\n}\n\nconst classMembers = new WeakMap<Function, ClassMemberInfo>();\n\nfunction getClassMemberInfo(\n instance: object,\n stopPrototype: object,\n reservedKeys: readonly string[],\n lifecycleHooks: Set<string>,\n): ClassMemberInfo {\n const ctor = instance.constructor;\n let info = classMembers.get(ctor);\n if (info) return info;\n\n const getters: ClassMemberInfo['getters'] = [];\n const methods: ClassMemberInfo['methods'] = [];\n const found: string[] = [];\n const processedGetters = new Set<string>();\n const processedMethods = new Set<string>();\n\n walkPrototypeChain(instance, stopPrototype, (key, desc) => {\n // Check reserved keys\n if (reservedKeys.includes(key as any)) {\n found.push(key);\n }\n\n // Collect getters (most-derived wins)\n if (desc.get && !processedGetters.has(key)) {\n processedGetters.add(key);\n getters.push({ key, get: desc.get });\n }\n\n // Collect wrappable methods (most-derived wins)\n if (!desc.get && !desc.set && typeof desc.value === 'function' &&\n !key.startsWith('_') && !lifecycleHooks.has(key) && !processedMethods.has(key)) {\n processedMethods.add(key);\n methods.push({ key, fn: desc.value });\n }\n });\n\n info = { getters, methods, reservedKeys: found };\n classMembers.set(ctor, info);\n return info;\n}\n\n// ── Auto-tracking types ──────────────────────────────────────────\n\ninterface TrackedSource {\n source: { subscribe(cb: () => void): () => void };\n revision: number;\n unsubscribe: () => void;\n}\n\n// ── Module-scoped tracking context ──────────────────────────────\n// Active during a memoized getter's Tier 3 recompute. Child getters\n// check these to bubble their cached deps to the parent. Module-scoped\n// (like Vue's activeEffect / Solid's Listener) for zero-cost Tier 1 checks.\n\nlet _activeStateTracking: Set<string> | null = null;\nlet _activeSourceTracking: Map<string, TrackedSource> | null = null;\n\n// ── Auto-tracking utilities ──────────────────────────────────────\n\nfunction isAutoTrackable(value: unknown): boolean {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as any).subscribe === 'function'\n );\n}\n\n// ── Async tracking types ─────────────────────────────────────────\n\nconst DEFAULT_TASK_STATE: TaskState = Object.freeze({ loading: false, error: null, errorCode: null });\n\nexport type AsyncMethodKeys<T, Base = ViewModel<any, any>> = {\n [K in Exclude<keyof T, keyof Base>]: T[K] extends (...args: any[]) => Promise<any> ? K : never;\n}[Exclude<keyof T, keyof Base>];\n\ntype AsyncMap<T> = {\n readonly [K in AsyncMethodKeys<T>]: TaskState;\n};\n\nconst RESERVED_ASYNC_KEYS = ['async', 'subscribeAsync'] as const;\nconst LIFECYCLE_HOOKS = new Set(['onInit', 'onSet', 'onDispose']);\n\n// ── ViewModel ────────────────────────────────────────────────────\n\ntype EmptyViewModelState = { __brand: 'EmptyViewModelState'}\n\n/**\n * Reactive state container with computed getters, automatic async tracking, and typed events.\n * Subclass and define state shape, getters, and action methods. Use with `useLocal` in React.\n */\nexport abstract class ViewModel<S extends object = EmptyViewModelState, E extends Record<string, any> = {}> implements Subscribable<S> {\n private _state: Readonly<S>;\n private _initialState: Readonly<S>;\n private _disposed = false;\n private _initialized = false;\n private _listeners = new Set<Listener<S>>();\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n private _subscriptionCleanups: (() => void)[] | null = null;\n private _eventBus: EventBus<E> | null = null;\n\n // ── Reactive derived state (RFC 1) ─────────────────────────────\n private _revision = 0;\n private _trackedSources = new Map<string, TrackedSource>();\n\n // ── Async tracking (RFC 2) ──────────────────────────────────────\n // Lazily allocated on first async method wrap to keep construction fast.\n private _asyncStates: Map<string, InternalTaskState> | null = null;\n private _asyncSnapshots: Map<string, TaskState> | null = null;\n private _asyncListeners: Set<() => void> | null = null;\n private _asyncProxy: AsyncMap<this> | null = null;\n private _activeOps: Map<string, number> | null = null;\n\n /** DEV-only timeout (ms) for detecting ghost async operations after dispose. */\n static GHOST_TIMEOUT = 3000;\n\n constructor(...args: EmptyViewModelState extends S ? [] | [initialState: S] : [initialState: S]) {\n const initialState = (args[0] ?? {} as S);\n this._state = freeze({ ...initialState });\n this._initialState = this._state;\n this._guardAndBind();\n }\n\n /** Current frozen state object. */\n get state(): S {\n return this._state;\n }\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** Whether init() has been called. */\n get initialized(): boolean {\n return this._initialized;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n /** Lazily-created typed EventBus for emitting and subscribing to events. */\n get events(): EventBus<E> {\n if (!this._eventBus) {\n this._eventBus = new EventBus<E>();\n }\n return this._eventBus;\n }\n\n /** Initializes the instance. Called automatically by React hooks after mount. */\n init(): void | Promise<void> {\n if (this._initialized || this._disposed) return;\n this._initialized = true;\n this._trackSubscribables();\n this._installStateProxy();\n this._processMembers();\n return this.onInit?.();\n }\n\n /**\n * Merges partial state into current state. If no values actually\n * changed by reference, this is a no-op.\n *\n * Triggers React re-render via listener notification. Called when:\n * - User code calls set() to update source state\n *\n * NOT called for subscribable member notifications — those use\n * a separate notification path (see _trackSubscribables).\n */\n protected set(partialOrUpdater: Partial<S> | Updater<S>): void {\n if (this._disposed) return;\n\n // __DEV__ guard: set() inside a getter would cause infinite loops.\n // After init(), getters are auto-memoized; set() → notify → recompute → set() → ∞\n if (__DEV__ && _activeStateTracking) {\n console.error(\n '[mvc-kit] set() called inside a getter. ' +\n 'Getters must be pure — they read state and return a value. ' +\n 'They must never call set(), which would cause an infinite ' +\n 'render loop. Move this logic to an action method.'\n );\n return;\n }\n\n const partial =\n typeof partialOrUpdater === 'function'\n ? partialOrUpdater(this._state)\n : partialOrUpdater;\n\n // Check if any values actually changed (shallow equality).\n // Uses for..in to avoid Object.keys() array allocation.\n let hasChanges = false;\n const current = this._state;\n for (const key in partial) {\n if ((partial as any)[key] !== (current as any)[key]) {\n hasChanges = true;\n break;\n }\n }\n\n if (!hasChanges) {\n return;\n }\n\n const prev = this._state;\n const next = freeze({ ...prev, ...partial });\n this._state = next;\n this._revision++;\n\n this.onSet?.(prev, next);\n\n for (const listener of this._listeners) {\n listener(next, prev);\n }\n }\n\n /**\n * Emits a typed event via the internal EventBus.\n * Safe to call during dispose cleanup callbacks.\n * @protected\n */\n protected emit<K extends keyof E>(event: K, payload: E[K]): void {\n // During dispose sequence: _disposed is true but eventBus not yet disposed.\n // Cleanup callbacks can still emit. After eventBus.dispose(), this is a no-op.\n // If eventBus was never created, fall back to _disposed check.\n if (this._eventBus?.disposed ?? this._disposed) return;\n this.events.emit(event, payload);\n }\n\n /** Subscribes to state changes. Returns an unsubscribe function. */\n subscribe(listener: Listener<S>): () => void {\n if (this._disposed) {\n return () => {};\n }\n\n this._listeners.add(listener);\n\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\n this._disposed = true;\n\n this._teardownSubscriptions();\n\n // Async tracking cleanup — handled by addCleanup registered in _processMembers()\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this._eventBus?.dispose();\n this.onDispose?.();\n this._listeners.clear();\n }\n\n /**\n * Resets state to initial values (or provided state), aborts in-flight work,\n * clears async tracking, and re-runs onInit().\n */\n reset(newState?: S): void | Promise<void> {\n if (this._disposed) return;\n\n // 1. Abort in-flight, lazy-recreate on next disposeSignal access\n this._abortController?.abort();\n this._abortController = null;\n\n this._teardownSubscriptions();\n\n // 2. Reset state\n this._state = newState ? freeze({ ...newState }) : this._initialState;\n this._revision++;\n\n // 3. Clear async tracking (preserve listeners — React still subscribed)\n this._asyncStates?.clear();\n this._asyncSnapshots?.clear();\n this._notifyAsync();\n\n // 4. Re-track subscribable members (fresh subscriptions)\n this._trackSubscribables();\n\n // 5. Notify state listeners (React re-renders with new state)\n for (const listener of this._listeners) {\n listener(this._state, this._state);\n }\n\n // 6. Re-run onInit\n return this.onInit?.();\n }\n\n /**\n * Registers a cleanup function to be called on dispose. Used internally for things like method wrapper\n * cleanup and event bus disposal, but can also be used by subclasses for custom cleanup logic.\n * @param fn\n * @protected\n */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Subscribes to an external Subscribable with automatic cleanup on dispose. @protected */\n protected subscribeTo<T>(source: Subscribable<T>, listener: Listener<T>): () => void {\n const unsubscribe = source.subscribe(listener);\n if (!this._subscriptionCleanups) this._subscriptionCleanups = [];\n this._subscriptionCleanups.push(unsubscribe);\n return unsubscribe;\n }\n\n /** Subscribes to a typed event on a Channel or EventBus with automatic cleanup on dispose and reset. @protected */\n protected listenTo<K extends string, S extends { on(event: K, handler: (payload: any) => void): () => void }>(\n source: S,\n event: K,\n handler: (payload: EventPayload<S, K>) => void,\n ): () => void {\n const unsubscribe = source.on(event, handler);\n if (!this._subscriptionCleanups) this._subscriptionCleanups = [];\n this._subscriptionCleanups.push(unsubscribe);\n return unsubscribe;\n }\n\n /** Pipes a Channel event into a Collection via upsert. Calls channel.init() and registers auto-cleanup on dispose and reset. @protected */\n protected pipeChannel<\n K extends string,\n C extends { init(): void | Promise<void>; on(event: K, handler: (payload: any) => void): () => void },\n >(\n channel: C,\n type: K,\n target: { upsert(item: EventPayload<C, K>): void },\n ): () => void {\n channel.init();\n return this.listenTo(channel, type, (payload) => {\n target.upsert(payload);\n });\n }\n\n /** Lifecycle hook called after every set() with the previous state. @protected */\n protected onSet?(prev: S, next: S): void;\n /** Lifecycle hook called at the end of init(). Override to load initial data. @protected */\n protected onInit?(): void | Promise<void>;\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n\n // ── Async tracking API ──────────────────────────────────────────\n\n /** Proxy providing `TaskState` (loading, error, errorCode) per async method. */\n get async(): AsyncMap<this> {\n if (!this._asyncProxy) {\n const self = this;\n this._asyncProxy = new Proxy({} as AsyncMap<this>, {\n get(_, prop: string) {\n return self._asyncSnapshots?.get(prop) ?? DEFAULT_TASK_STATE;\n },\n has(_, prop: string) {\n return self._asyncSnapshots?.has(prop) ?? false;\n },\n ownKeys() {\n return self._asyncSnapshots ? Array.from(self._asyncSnapshots.keys()) : [];\n },\n getOwnPropertyDescriptor(_, prop: string) {\n if (self._asyncSnapshots?.has(prop)) {\n return { configurable: true, enumerable: true, value: self._asyncSnapshots.get(prop) };\n }\n return undefined;\n },\n });\n }\n return this._asyncProxy;\n }\n\n /** Subscribes to async state changes. Used for React integration. */\n subscribeAsync(listener: () => void): () => void {\n if (this._disposed) return () => {};\n if (!this._asyncListeners) this._asyncListeners = new Set();\n this._asyncListeners.add(listener);\n return () => { this._asyncListeners!.delete(listener); };\n }\n\n private _notifyAsync(): void {\n if (!this._asyncListeners) return;\n for (const listener of this._asyncListeners) {\n listener();\n }\n }\n\n // ── Async tracking internals ────────────────────────────────────\n\n private _teardownSubscriptions(): void {\n for (const tracked of this._trackedSources.values()) tracked.unsubscribe();\n this._trackedSources.clear();\n\n if (this._subscriptionCleanups) {\n for (const fn of this._subscriptionCleanups) fn();\n this._subscriptionCleanups = null;\n }\n }\n\n /**\n * Guards reserved keys and auto-binds subclass methods in a single pass\n * using the cached class metadata from getClassMemberInfo.\n */\n private _guardAndBind(): void {\n const info = getClassMemberInfo(this, ViewModel.prototype, RESERVED_ASYNC_KEYS, LIFECYCLE_HOOKS);\n if (info.reservedKeys.length > 0) {\n throw new Error(\n `[mvc-kit] \"${info.reservedKeys[0]}\" is a reserved property on ViewModel and cannot be overridden.`\n );\n }\n for (let i = 0; i < info.methods.length; i++) {\n (this as any)[info.methods[i].key] = info.methods[i].fn.bind(this);\n }\n }\n\n // ── Member processing (merged getter memoization + async method wrapping) ──\n\n /**\n * Uses cached class metadata to memoize getters (RFC 1) and delegates\n * async method wrapping to the shared wrapAsyncMethods helper (RFC 2).\n * Class metadata is computed once per class via getClassMemberInfo() and reused\n * across all instances — avoids repeated prototype walks.\n */\n private _processMembers(): void {\n const info = getClassMemberInfo(this, ViewModel.prototype, RESERVED_ASYNC_KEYS, LIFECYCLE_HOOKS);\n\n // Memoize getters\n for (let i = 0; i < info.getters.length; i++) {\n this._wrapGetter(info.getters[i].key, info.getters[i].get);\n }\n\n // DEV: Instance property reserved key check (must run even if no methods to wrap)\n if (__DEV__) {\n for (const key of RESERVED_ASYNC_KEYS) {\n if (Object.getOwnPropertyDescriptor(this, key)?.value !== undefined) {\n throw new Error(\n `[mvc-kit] \"${key}\" is a reserved property on ViewModel and cannot be overridden.`\n );\n }\n }\n }\n\n // Skip async wrapping if no methods to wrap\n if (info.methods.length === 0) return;\n\n // Lazily allocate async tracking collections\n if (!this._asyncStates) this._asyncStates = new Map();\n if (!this._asyncSnapshots) this._asyncSnapshots = new Map();\n if (!this._asyncListeners) this._asyncListeners = new Set();\n\n // Initialize DEV-only active ops tracking\n if (__DEV__) {\n this._activeOps = new Map();\n }\n\n // Wrap async methods (shared with Resource)\n wrapAsyncMethods({\n instance: this,\n stopPrototype: ViewModel.prototype,\n reservedKeys: RESERVED_ASYNC_KEYS,\n lifecycleHooks: LIFECYCLE_HOOKS,\n isDisposed: () => this._disposed,\n isInitialized: () => this._initialized,\n asyncStates: this._asyncStates,\n asyncSnapshots: this._asyncSnapshots,\n asyncListeners: this._asyncListeners,\n notifyAsync: () => this._notifyAsync(),\n addCleanup: (fn) => this.addCleanup(fn),\n ghostTimeout: (this.constructor as typeof ViewModel).GHOST_TIMEOUT,\n className: 'ViewModel',\n activeOps: this._activeOps,\n methods: info.methods,\n });\n }\n\n // ── Auto-tracking internals ────────────────────────────────────\n\n /**\n * Installs a context-sensitive state getter on the instance.\n *\n * During getter tracking (_activeStateTracking is active): returns a Proxy\n * that records which state properties are accessed. The Proxy is created\n * lazily on first tracking access to keep init() fast.\n *\n * Otherwise: returns the frozen state object directly. This is critical\n * for React's useSyncExternalStore — it needs a changing reference to\n * detect state updates and trigger re-renders.\n */\n private _installStateProxy(): void {\n let stateProxy: S | null = null;\n\n Object.defineProperty(this, 'state', {\n get: () => {\n if (_activeStateTracking) {\n if (!stateProxy) {\n stateProxy = new Proxy({} as S, {\n get: (_, prop: string) => {\n _activeStateTracking?.add(prop);\n return (this._state as any)[prop];\n },\n ownKeys: () => Reflect.ownKeys(this._state as object),\n getOwnPropertyDescriptor: (_, prop) =>\n Reflect.getOwnPropertyDescriptor(this._state as object, prop),\n set: () => {\n throw new Error('Cannot mutate state directly. Use set() instead.');\n },\n has: (_, prop) => prop in (this._state as object),\n });\n }\n return stateProxy;\n }\n return this._state;\n },\n configurable: true,\n enumerable: true,\n });\n }\n\n /**\n * Scans own instance properties for Subscribable objects and sets up\n * automatic dependency tracking for each one found.\n *\n * For each subscribable member:\n * 1. Subscribe to it. On notification: bump its tracked revision\n * AND the VM's global revision, then force a new state reference\n * and notify listeners so React re-renders.\n * 2. Replace the instance property with a getter that participates\n * in dependency tracking.\n * 3. Register unsubscribe in the dispose chain.\n *\n * Called during init(), AFTER all subclass property initializers\n * have run (they execute during the constructor, before init()).\n */\n private _trackSubscribables(): void {\n for (const key of Object.getOwnPropertyNames(this)) {\n const value = (this as any)[key];\n if (!isAutoTrackable(value)) continue;\n\n let tracked: TrackedSource;\n\n const onSourceNotify = () => {\n if (this._disposed) return;\n\n // Source notified — bump revisions for getter invalidation\n tracked.revision++;\n this._revision++;\n\n for (const listener of this._listeners) {\n listener(this._state, this._state);\n }\n };\n\n const unsubState = value.subscribe(onSourceNotify);\n const unsubAsync =\n typeof value.subscribeAsync === 'function'\n ? value.subscribeAsync(onSourceNotify)\n : undefined;\n\n tracked = {\n source: value,\n revision: 0,\n unsubscribe: unsubAsync\n ? () => { unsubState(); unsubAsync(); }\n : unsubState,\n };\n\n this._trackedSources.set(key, tracked);\n\n // Replace the instance property with a tracking getter.\n // The original value is captured in the closure.\n Object.defineProperty(this, key, {\n get: () => {\n _activeSourceTracking?.set(key, tracked);\n return value;\n },\n configurable: true,\n enumerable: false,\n });\n }\n }\n\n /**\n * Bubbles cached dependency records to the active parent tracking context.\n * Called from Tier 1/Tier 2 cache hits during nested getter composition\n * so the parent getter records the full transitive dependency set.\n * Extracted to keep the getter closure small for V8 inlining.\n */\n private _bubbleDeps(stateDepKeys: string[] | undefined, sourceDepKeys: string[] | undefined): void {\n const st = _activeStateTracking!;\n if (stateDepKeys) {\n for (let i = 0; i < stateDepKeys.length; i++) st.add(stateDepKeys[i]);\n }\n if (sourceDepKeys) {\n const srt = _activeSourceTracking!;\n for (let i = 0; i < sourceDepKeys.length; i++) {\n const ts = this._trackedSources.get(sourceDepKeys[i]);\n if (ts) srt.set(sourceDepKeys[i], ts);\n }\n }\n }\n\n /**\n * Replaces a single prototype getter with a memoized version on this\n * instance. The memoized getter tracks both state dependencies and\n * subscribable member dependencies, caching its result and\n * revalidating through a three-tier strategy:\n *\n * Tier 1 (fast): revision unchanged → return cached (1 int compare)\n * Tier 2 (medium): revision changed but this getter's deps didn't → return cached\n * Tier 3 (slow): at least one dep changed → full recompute with tracking\n */\n private _wrapGetter(key: string, original: () => unknown): void {\n // Per-getter cache state, private to this getter on this instance.\n let cached: unknown;\n let validatedAtRevision = -1;\n\n // Array-based dep tracking — avoids Map iterator allocation in Tier 2\n let stateDepKeys: string[] | undefined;\n let stateDepValues: unknown[] | undefined;\n let sourceDepKeys: string[] | undefined;\n let sourceDepRevisions: number[] | undefined;\n\n // Reusable tracking containers — allocated on first Tier 3, reused via clear()\n let trackingSet: Set<string> | undefined;\n let trackingMap: Map<string, TrackedSource> | undefined;\n\n Object.defineProperty(this, key, {\n get: () => {\n // ── Tier 1: Fast path (1 integer compare) ───────────────\n if (validatedAtRevision === this._revision) {\n if (_activeStateTracking) this._bubbleDeps(stateDepKeys, sourceDepKeys);\n return cached;\n }\n\n // After dispose, revision never changes so Tier 1 hits if\n // getter was ever called. Guard the uncalled-before-dispose edge case.\n if (this._disposed) return cached;\n\n // ── Tier 2: Medium path — array-based dep check ─────────\n if (stateDepKeys !== undefined) {\n let fresh = true;\n\n // Check state deps by reference (array iteration, no iterator alloc)\n const state = this._state as any;\n for (let i = 0; i < stateDepKeys.length; i++) {\n if (state[stateDepKeys[i]] !== stateDepValues![i]) {\n fresh = false;\n break;\n }\n }\n\n // Check subscribable deps by revision\n if (fresh && sourceDepKeys !== undefined && sourceDepKeys.length > 0) {\n for (let i = 0; i < sourceDepKeys.length; i++) {\n const ts = this._trackedSources.get(sourceDepKeys[i]);\n if (ts && ts.revision !== sourceDepRevisions![i]) {\n fresh = false;\n break;\n }\n }\n }\n\n if (fresh) {\n if (_activeStateTracking) this._bubbleDeps(stateDepKeys, sourceDepKeys);\n validatedAtRevision = this._revision;\n return cached;\n }\n }\n\n // ── Tier 3: Slow path — full recompute ─────────────────\n // Save parent tracking context for nested getter composition\n const parentStateTracking = _activeStateTracking;\n const parentSourceTracking = _activeSourceTracking;\n\n // Reuse tracking containers (clear instead of allocate)\n if (trackingSet) {\n trackingSet.clear();\n } else {\n trackingSet = new Set();\n }\n if (trackingMap) {\n trackingMap.clear();\n } else {\n trackingMap = new Map();\n }\n\n _activeStateTracking = trackingSet;\n _activeSourceTracking = trackingMap;\n\n try {\n cached = original.call(this);\n } catch (e) {\n // Don't cache failed computations\n _activeStateTracking = parentStateTracking;\n _activeSourceTracking = parentSourceTracking;\n throw e;\n }\n\n // Restore parent tracking context\n _activeStateTracking = parentStateTracking;\n _activeSourceTracking = parentSourceTracking;\n\n // Bubble deps up to parent getter if nested\n if (parentStateTracking) {\n for (const d of trackingSet) parentStateTracking.add(d);\n }\n if (parentSourceTracking) {\n for (const [k, v] of trackingMap) {\n parentSourceTracking.set(k, v);\n }\n }\n\n // Snapshot state dep values into arrays for Tier 2\n const depCount = trackingSet.size;\n if (!stateDepKeys || stateDepKeys.length !== depCount) {\n stateDepKeys = new Array(depCount);\n stateDepValues = new Array(depCount);\n }\n {\n let i = 0;\n const state = this._state as any;\n for (const d of trackingSet) {\n stateDepKeys[i] = d;\n stateDepValues![i] = state[d];\n i++;\n }\n }\n\n // Snapshot subscribable revisions into arrays\n const sourceCount = trackingMap.size;\n if (sourceCount > 0) {\n if (!sourceDepKeys || sourceDepKeys.length !== sourceCount) {\n sourceDepKeys = new Array(sourceCount);\n sourceDepRevisions = new Array(sourceCount);\n }\n let i = 0;\n for (const [memberKey, tracked] of trackingMap) {\n sourceDepKeys[i] = memberKey;\n sourceDepRevisions![i] = tracked.revision;\n i++;\n }\n } else {\n sourceDepKeys = undefined;\n sourceDepRevisions = undefined;\n }\n\n validatedAtRevision = this._revision;\n\n return cached;\n },\n configurable: true,\n enumerable: true,\n });\n }\n}\n"],"names":[],"mappings":";;;AAUA,MAAM,UAAU,OAAO,oBAAoB,eAAe;AAE1D,SAAS,OAAU,KAAW;AAC5B,SAAO,UAAU,OAAO,OAAO,GAAG,IAAS;AAC7C;AAYA,MAAM,mCAAmB,QAAA;AAEzB,SAAS,mBACP,UACA,eACA,cACA,gBACiB;AACjB,QAAM,OAAO,SAAS;AACtB,MAAI,OAAO,aAAa,IAAI,IAAI;AAChC,MAAI,KAAM,QAAO;AAEjB,QAAM,UAAsC,CAAA;AAC5C,QAAM,UAAsC,CAAA;AAC5C,QAAM,QAAkB,CAAA;AACxB,QAAM,uCAAuB,IAAA;AAC7B,QAAM,uCAAuB,IAAA;AAE7B,qBAAmB,UAAU,eAAe,CAAC,KAAK,SAAS;AAEzD,QAAI,aAAa,SAAS,GAAU,GAAG;AACrC,YAAM,KAAK,GAAG;AAAA,IAChB;AAGA,QAAI,KAAK,OAAO,CAAC,iBAAiB,IAAI,GAAG,GAAG;AAC1C,uBAAiB,IAAI,GAAG;AACxB,cAAQ,KAAK,EAAE,KAAK,KAAK,KAAK,KAAK;AAAA,IACrC;AAGA,QAAI,CAAC,KAAK,OAAO,CAAC,KAAK,OAAO,OAAO,KAAK,UAAU,cAChD,CAAC,IAAI,WAAW,GAAG,KAAK,CAAC,eAAe,IAAI,GAAG,KAAK,CAAC,iBAAiB,IAAI,GAAG,GAAG;AAClF,uBAAiB,IAAI,GAAG;AACxB,cAAQ,KAAK,EAAE,KAAK,IAAI,KAAK,OAAO;AAAA,IACtC;AAAA,EACF,CAAC;AAED,SAAO,EAAE,SAAS,SAAS,cAAc,MAAA;AACzC,eAAa,IAAI,MAAM,IAAI;AAC3B,SAAO;AACT;AAeA,IAAI,uBAA2C;AAC/C,IAAI,wBAA2D;AAI/D,SAAS,gBAAgB,OAAyB;AAChD,SACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAQ,MAAc,cAAc;AAExC;AAIA,MAAM,qBAAgC,OAAO,OAAO,EAAE,SAAS,OAAO,OAAO,MAAM,WAAW,MAAM;AAUpG,MAAM,sBAAsB,CAAC,SAAS,gBAAgB;AACtD,MAAM,kBAAkB,oBAAI,IAAI,CAAC,UAAU,SAAS,WAAW,CAAC;AAUzD,MAAe,UAAiH;AAAA,EAC7H;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,iCAAiB,IAAA;AAAA,EACjB,mBAA2C;AAAA,EAC3C,YAAmC;AAAA,EACnC,wBAA+C;AAAA,EAC/C,YAAgC;AAAA;AAAA,EAGhC,YAAY;AAAA,EACZ,sCAAsB,IAAA;AAAA;AAAA;AAAA,EAItB,eAAsD;AAAA,EACtD,kBAAiD;AAAA,EACjD,kBAA0C;AAAA,EAC1C,cAAqC;AAAA,EACrC,aAAyC;AAAA;AAAA,EAGjD,OAAO,gBAAgB;AAAA,EAEvB,eAAe,MAAkF;AAC/F,UAAM,eAAgB,KAAK,CAAC,KAAK,CAAA;AACjC,SAAK,SAAS,OAAO,EAAE,GAAG,cAAc;AACxC,SAAK,gBAAgB,KAAK;AAC1B,SAAK,cAAA;AAAA,EACP;AAAA;AAAA,EAGA,IAAI,QAAW;AACb,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,WAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,cAAuB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,gBAA6B;AAC/B,QAAI,CAAC,KAAK,kBAAkB;AAC1B,WAAK,mBAAmB,IAAI,gBAAA;AAAA,IAC9B;AACA,WAAO,KAAK,iBAAiB;AAAA,EAC/B;AAAA;AAAA,EAGA,IAAI,SAAsB;AACxB,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,IAAI,SAAA;AAAA,IACvB;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,OAA6B;AAC3B,QAAI,KAAK,gBAAgB,KAAK,UAAW;AACzC,SAAK,eAAe;AACpB,SAAK,oBAAA;AACL,SAAK,mBAAA;AACL,SAAK,gBAAA;AACL,WAAO,KAAK,SAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYU,IAAI,kBAAiD;AAC7D,QAAI,KAAK,UAAW;AAIpB,QAAI,WAAW,sBAAsB;AACnC,cAAQ;AAAA,QACN;AAAA,MAAA;AAKF;AAAA,IACF;AAEA,UAAM,UACJ,OAAO,qBAAqB,aACxB,iBAAiB,KAAK,MAAM,IAC5B;AAIN,QAAI,aAAa;AACjB,UAAM,UAAU,KAAK;AACrB,eAAW,OAAO,SAAS;AACzB,UAAK,QAAgB,GAAG,MAAO,QAAgB,GAAG,GAAG;AACnD,qBAAa;AACb;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,YAAY;AACf;AAAA,IACF;AAEA,UAAM,OAAO,KAAK;AAClB,UAAM,OAAO,OAAO,EAAE,GAAG,MAAM,GAAG,SAAS;AAC3C,SAAK,SAAS;AACd,SAAK;AAEL,SAAK,QAAQ,MAAM,IAAI;AAEvB,eAAW,YAAY,KAAK,YAAY;AACtC,eAAS,MAAM,IAAI;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOU,KAAwB,OAAU,SAAqB;AAI/D,QAAI,KAAK,WAAW,YAAY,KAAK,UAAW;AAChD,SAAK,OAAO,KAAK,OAAO,OAAO;AAAA,EACjC;AAAA;AAAA,EAGA,UAAU,UAAmC;AAC3C,QAAI,KAAK,WAAW;AAClB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AAEA,SAAK,WAAW,IAAI,QAAQ;AAE5B,WAAO,MAAM;AACX,WAAK,WAAW,OAAO,QAAQ;AAAA,IACjC;AAAA,EACF;AAAA;AAAA,EAGA,UAAgB;AACd,QAAI,KAAK,WAAW;AAClB;AAAA,IACF;AAEA,SAAK,YAAY;AAEjB,SAAK,uBAAA;AAGL,SAAK,kBAAkB,MAAA;AACvB,QAAI,KAAK,WAAW;AAClB,iBAAW,MAAM,KAAK,UAAW,IAAA;AACjC,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,WAAW,QAAA;AAChB,SAAK,YAAA;AACL,SAAK,WAAW,MAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAoC;AACxC,QAAI,KAAK,UAAW;AAGpB,SAAK,kBAAkB,MAAA;AACvB,SAAK,mBAAmB;AAExB,SAAK,uBAAA;AAGL,SAAK,SAAS,WAAW,OAAO,EAAE,GAAG,SAAA,CAAU,IAAI,KAAK;AACxD,SAAK;AAGL,SAAK,cAAc,MAAA;AACnB,SAAK,iBAAiB,MAAA;AACtB,SAAK,aAAA;AAGL,SAAK,oBAAA;AAGL,eAAW,YAAY,KAAK,YAAY;AACtC,eAAS,KAAK,QAAQ,KAAK,MAAM;AAAA,IACnC;AAGA,WAAO,KAAK,SAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,WAAW,IAAsB;AACzC,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,CAAA;AAAA,IACnB;AACA,SAAK,UAAU,KAAK,EAAE;AAAA,EACxB;AAAA;AAAA,EAGU,YAAe,QAAyB,UAAmC;AACnF,UAAM,cAAc,OAAO,UAAU,QAAQ;AAC7C,QAAI,CAAC,KAAK,sBAAuB,MAAK,wBAAwB,CAAA;AAC9D,SAAK,sBAAsB,KAAK,WAAW;AAC3C,WAAO;AAAA,EACT;AAAA;AAAA,EAGU,SACR,QACA,OACA,SACY;AACZ,UAAM,cAAc,OAAO,GAAG,OAAO,OAAO;AAC5C,QAAI,CAAC,KAAK,sBAAuB,MAAK,wBAAwB,CAAA;AAC9D,SAAK,sBAAsB,KAAK,WAAW;AAC3C,WAAO;AAAA,EACT;AAAA;AAAA,EAGU,YAIR,SACA,MACA,QACY;AACZ,YAAQ,KAAA;AACR,WAAO,KAAK,SAAS,SAAS,MAAM,CAAC,YAAY;AAC/C,aAAO,OAAO,OAAO;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA,EAYA,IAAI,QAAwB;AAC1B,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,OAAO;AACb,WAAK,cAAc,IAAI,MAAM,IAAsB;AAAA,QACjD,IAAI,GAAG,MAAc;AACnB,iBAAO,KAAK,iBAAiB,IAAI,IAAI,KAAK;AAAA,QAC5C;AAAA,QACA,IAAI,GAAG,MAAc;AACnB,iBAAO,KAAK,iBAAiB,IAAI,IAAI,KAAK;AAAA,QAC5C;AAAA,QACA,UAAU;AACR,iBAAO,KAAK,kBAAkB,MAAM,KAAK,KAAK,gBAAgB,KAAA,CAAM,IAAI,CAAA;AAAA,QAC1E;AAAA,QACA,yBAAyB,GAAG,MAAc;AACxC,cAAI,KAAK,iBAAiB,IAAI,IAAI,GAAG;AACnC,mBAAO,EAAE,cAAc,MAAM,YAAY,MAAM,OAAO,KAAK,gBAAgB,IAAI,IAAI,EAAA;AAAA,UACrF;AACA,iBAAO;AAAA,QACT;AAAA,MAAA,CACD;AAAA,IACH;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,eAAe,UAAkC;AAC/C,QAAI,KAAK,UAAW,QAAO,MAAM;AAAA,IAAC;AAClC,QAAI,CAAC,KAAK,gBAAiB,MAAK,sCAAsB,IAAA;AACtD,SAAK,gBAAgB,IAAI,QAAQ;AACjC,WAAO,MAAM;AAAE,WAAK,gBAAiB,OAAO,QAAQ;AAAA,IAAG;AAAA,EACzD;AAAA,EAEQ,eAAqB;AAC3B,QAAI,CAAC,KAAK,gBAAiB;AAC3B,eAAW,YAAY,KAAK,iBAAiB;AAC3C,eAAA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAIQ,yBAA+B;AACrC,eAAW,WAAW,KAAK,gBAAgB,OAAA,WAAkB,YAAA;AAC7D,SAAK,gBAAgB,MAAA;AAErB,QAAI,KAAK,uBAAuB;AAC9B,iBAAW,MAAM,KAAK,sBAAuB,IAAA;AAC7C,WAAK,wBAAwB;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAsB;AAC5B,UAAM,OAAO,mBAAmB,MAAM,UAAU,WAAW,qBAAqB,eAAe;AAC/F,QAAI,KAAK,aAAa,SAAS,GAAG;AAChC,YAAM,IAAI;AAAA,QACR,cAAc,KAAK,aAAa,CAAC,CAAC;AAAA,MAAA;AAAA,IAEtC;AACA,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,QAAQ,KAAK;AAC3C,WAAa,KAAK,QAAQ,CAAC,EAAE,GAAG,IAAI,KAAK,QAAQ,CAAC,EAAE,GAAG,KAAK,IAAI;AAAA,IACnE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,kBAAwB;AAC9B,UAAM,OAAO,mBAAmB,MAAM,UAAU,WAAW,qBAAqB,eAAe;AAG/F,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,QAAQ,KAAK;AAC5C,WAAK,YAAY,KAAK,QAAQ,CAAC,EAAE,KAAK,KAAK,QAAQ,CAAC,EAAE,GAAG;AAAA,IAC3D;AAGA,QAAI,SAAS;AACX,iBAAW,OAAO,qBAAqB;AACrC,YAAI,OAAO,yBAAyB,MAAM,GAAG,GAAG,UAAU,QAAW;AACnE,gBAAM,IAAI;AAAA,YACR,cAAc,GAAG;AAAA,UAAA;AAAA,QAErB;AAAA,MACF;AAAA,IACF;AAGA,QAAI,KAAK,QAAQ,WAAW,EAAG;AAG/B,QAAI,CAAC,KAAK,aAAc,MAAK,mCAAmB,IAAA;AAChD,QAAI,CAAC,KAAK,gBAAiB,MAAK,sCAAsB,IAAA;AACtD,QAAI,CAAC,KAAK,gBAAiB,MAAK,sCAAsB,IAAA;AAGtD,QAAI,SAAS;AACX,WAAK,iCAAiB,IAAA;AAAA,IACxB;AAGA,qBAAiB;AAAA,MACf,UAAU;AAAA,MACV,eAAe,UAAU;AAAA,MACzB,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,YAAY,MAAM,KAAK;AAAA,MACvB,eAAe,MAAM,KAAK;AAAA,MAC1B,aAAa,KAAK;AAAA,MAClB,gBAAgB,KAAK;AAAA,MACrB,gBAAgB,KAAK;AAAA,MACrB,aAAa,MAAM,KAAK,aAAA;AAAA,MACxB,YAAY,CAAC,OAAO,KAAK,WAAW,EAAE;AAAA,MACtC,cAAe,KAAK,YAAiC;AAAA,MACrD,WAAW;AAAA,MACX,WAAW,KAAK;AAAA,MAChB,SAAS,KAAK;AAAA,IAAA,CACf;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeQ,qBAA2B;AACjC,QAAI,aAAuB;AAE3B,WAAO,eAAe,MAAM,SAAS;AAAA,MACnC,KAAK,MAAM;AACT,YAAI,sBAAsB;AACxB,cAAI,CAAC,YAAY;AACf,yBAAa,IAAI,MAAM,IAAS;AAAA,cAC9B,KAAK,CAAC,GAAG,SAAiB;AACxB,sCAAsB,IAAI,IAAI;AAC9B,uBAAQ,KAAK,OAAe,IAAI;AAAA,cAClC;AAAA,cACA,SAAS,MAAM,QAAQ,QAAQ,KAAK,MAAgB;AAAA,cACpD,0BAA0B,CAAC,GAAG,SAC5B,QAAQ,yBAAyB,KAAK,QAAkB,IAAI;AAAA,cAC9D,KAAK,MAAM;AACT,sBAAM,IAAI,MAAM,kDAAkD;AAAA,cACpE;AAAA,cACA,KAAK,CAAC,GAAG,SAAS,QAAS,KAAK;AAAA,YAAA,CACjC;AAAA,UACH;AACA,iBAAO;AAAA,QACT;AACA,eAAO,KAAK;AAAA,MACd;AAAA,MACA,cAAc;AAAA,MACd,YAAY;AAAA,IAAA,CACb;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBQ,sBAA4B;AAClC,eAAW,OAAO,OAAO,oBAAoB,IAAI,GAAG;AAClD,YAAM,QAAS,KAAa,GAAG;AAC/B,UAAI,CAAC,gBAAgB,KAAK,EAAG;AAE7B,UAAI;AAEJ,YAAM,iBAAiB,MAAM;AAC3B,YAAI,KAAK,UAAW;AAGpB,gBAAQ;AACR,aAAK;AAEL,mBAAW,YAAY,KAAK,YAAY;AACtC,mBAAS,KAAK,QAAQ,KAAK,MAAM;AAAA,QACnC;AAAA,MACF;AAEA,YAAM,aAAa,MAAM,UAAU,cAAc;AACjD,YAAM,aACJ,OAAO,MAAM,mBAAmB,aAC5B,MAAM,eAAe,cAAc,IACnC;AAEN,gBAAU;AAAA,QACR,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,aAAa,aACT,MAAM;AAAE,qBAAA;AAAc,qBAAA;AAAA,QAAc,IACpC;AAAA,MAAA;AAGN,WAAK,gBAAgB,IAAI,KAAK,OAAO;AAIrC,aAAO,eAAe,MAAM,KAAK;AAAA,QAC/B,KAAK,MAAM;AACT,iCAAuB,IAAI,KAAK,OAAO;AACvC,iBAAO;AAAA,QACT;AAAA,QACA,cAAc;AAAA,QACd,YAAY;AAAA,MAAA,CACb;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,YAAY,cAAoC,eAA2C;AACjG,UAAM,KAAK;AACX,QAAI,cAAc;AAChB,eAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,IAAK,IAAG,IAAI,aAAa,CAAC,CAAC;AAAA,IACtE;AACA,QAAI,eAAe;AACjB,YAAM,MAAM;AACZ,eAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,cAAM,KAAK,KAAK,gBAAgB,IAAI,cAAc,CAAC,CAAC;AACpD,YAAI,GAAI,KAAI,IAAI,cAAc,CAAC,GAAG,EAAE;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,YAAY,KAAa,UAA+B;AAE9D,QAAI;AACJ,QAAI,sBAAsB;AAG1B,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AAGJ,QAAI;AACJ,QAAI;AAEJ,WAAO,eAAe,MAAM,KAAK;AAAA,MAC/B,KAAK,MAAM;AAET,YAAI,wBAAwB,KAAK,WAAW;AAC1C,cAAI,qBAAsB,MAAK,YAAY,cAAc,aAAa;AACtE,iBAAO;AAAA,QACT;AAIA,YAAI,KAAK,UAAW,QAAO;AAG3B,YAAI,iBAAiB,QAAW;AAC9B,cAAI,QAAQ;AAGZ,gBAAM,QAAQ,KAAK;AACnB,mBAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC5C,gBAAI,MAAM,aAAa,CAAC,CAAC,MAAM,eAAgB,CAAC,GAAG;AACjD,sBAAQ;AACR;AAAA,YACF;AAAA,UACF;AAGA,cAAI,SAAS,kBAAkB,UAAa,cAAc,SAAS,GAAG;AACpE,qBAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,oBAAM,KAAK,KAAK,gBAAgB,IAAI,cAAc,CAAC,CAAC;AACpD,kBAAI,MAAM,GAAG,aAAa,mBAAoB,CAAC,GAAG;AAChD,wBAAQ;AACR;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAEA,cAAI,OAAO;AACT,gBAAI,qBAAsB,MAAK,YAAY,cAAc,aAAa;AACtE,kCAAsB,KAAK;AAC3B,mBAAO;AAAA,UACT;AAAA,QACF;AAIA,cAAM,sBAAsB;AAC5B,cAAM,uBAAuB;AAG7B,YAAI,aAAa;AACf,sBAAY,MAAA;AAAA,QACd,OAAO;AACL,4CAAkB,IAAA;AAAA,QACpB;AACA,YAAI,aAAa;AACf,sBAAY,MAAA;AAAA,QACd,OAAO;AACL,4CAAkB,IAAA;AAAA,QACpB;AAEA,+BAAuB;AACvB,gCAAwB;AAExB,YAAI;AACF,mBAAS,SAAS,KAAK,IAAI;AAAA,QAC7B,SAAS,GAAG;AAEV,iCAAuB;AACvB,kCAAwB;AACxB,gBAAM;AAAA,QACR;AAGA,+BAAuB;AACvB,gCAAwB;AAGxB,YAAI,qBAAqB;AACvB,qBAAW,KAAK,YAAa,qBAAoB,IAAI,CAAC;AAAA,QACxD;AACA,YAAI,sBAAsB;AACxB,qBAAW,CAAC,GAAG,CAAC,KAAK,aAAa;AAChC,iCAAqB,IAAI,GAAG,CAAC;AAAA,UAC/B;AAAA,QACF;AAGA,cAAM,WAAW,YAAY;AAC7B,YAAI,CAAC,gBAAgB,aAAa,WAAW,UAAU;AACrD,yBAAe,IAAI,MAAM,QAAQ;AACjC,2BAAiB,IAAI,MAAM,QAAQ;AAAA,QACrC;AACA;AACE,cAAI,IAAI;AACR,gBAAM,QAAQ,KAAK;AACnB,qBAAW,KAAK,aAAa;AAC3B,yBAAa,CAAC,IAAI;AAClB,2BAAgB,CAAC,IAAI,MAAM,CAAC;AAC5B;AAAA,UACF;AAAA,QACF;AAGA,cAAM,cAAc,YAAY;AAChC,YAAI,cAAc,GAAG;AACnB,cAAI,CAAC,iBAAiB,cAAc,WAAW,aAAa;AAC1D,4BAAgB,IAAI,MAAM,WAAW;AACrC,iCAAqB,IAAI,MAAM,WAAW;AAAA,UAC5C;AACA,cAAI,IAAI;AACR,qBAAW,CAAC,WAAW,OAAO,KAAK,aAAa;AAC9C,0BAAc,CAAC,IAAI;AACnB,+BAAoB,CAAC,IAAI,QAAQ;AACjC;AAAA,UACF;AAAA,QACF,OAAO;AACL,0BAAgB;AAChB,+BAAqB;AAAA,QACvB;AAEA,8BAAsB,KAAK;AAE3B,eAAO;AAAA,MACT;AAAA,MACA,cAAc;AAAA,MACd,YAAY;AAAA,IAAA,CACb;AAAA,EACH;AACF;"}
1
+ {"version":3,"file":"ViewModel.js","sources":["../src/ViewModel.ts"],"sourcesContent":["import { EventBus } from './EventBus';\nimport { walkPrototypeChain } from './walkPrototypeChain';\nimport { wrapAsyncMethods } from './wrapAsyncMethods';\nimport { resolveDraftUpdater } from './produceDraft';\nimport type { InternalTaskState } from './wrapAsyncMethods';\nimport type { Listener, Updater, Subscribable, TaskState, EventPayload } from './types';\n\n// Re-export for backwards compatibility\nexport type { Listener, Updater } from './types';\nexport { walkPrototypeChain } from './walkPrototypeChain';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\nfunction freeze<T>(obj: T): T {\n return __DEV__ ? Object.freeze(obj) as T : obj;\n}\n\n// ── Class metadata cache ─────────────────────────────────────────\n// Caches prototype walk results per class to avoid repeated Object.getOwnPropertyDescriptors\n// on every init(). Single walk extracts getters, methods, and reserved key violations.\n\ninterface ClassMemberInfo {\n getters: Array<{ key: string; get: () => unknown }>;\n methods: Array<{ key: string; fn: Function }>;\n reservedKeys: string[];\n}\n\nconst classMembers = new WeakMap<Function, ClassMemberInfo>();\n\nfunction getClassMemberInfo(\n instance: object,\n stopPrototype: object,\n reservedKeys: readonly string[],\n lifecycleHooks: Set<string>,\n): ClassMemberInfo {\n const ctor = instance.constructor;\n let info = classMembers.get(ctor);\n if (info) return info;\n\n const getters: ClassMemberInfo['getters'] = [];\n const methods: ClassMemberInfo['methods'] = [];\n const found: string[] = [];\n const processedGetters = new Set<string>();\n const processedMethods = new Set<string>();\n\n walkPrototypeChain(instance, stopPrototype, (key, desc) => {\n // Check reserved keys\n if (reservedKeys.includes(key as any)) {\n found.push(key);\n }\n\n // Collect getters (most-derived wins)\n if (desc.get && !processedGetters.has(key)) {\n processedGetters.add(key);\n getters.push({ key, get: desc.get });\n }\n\n // Collect wrappable methods (most-derived wins)\n if (!desc.get && !desc.set && typeof desc.value === 'function' &&\n !key.startsWith('_') && !lifecycleHooks.has(key) && !processedMethods.has(key)) {\n processedMethods.add(key);\n methods.push({ key, fn: desc.value });\n }\n });\n\n info = { getters, methods, reservedKeys: found };\n classMembers.set(ctor, info);\n return info;\n}\n\n// ── Auto-tracking types ──────────────────────────────────────────\n\ninterface TrackedSource {\n source: { subscribe(cb: () => void): () => void };\n revision: number;\n unsubscribe: () => void;\n}\n\n// ── Module-scoped tracking context ──────────────────────────────\n// Active during a memoized getter's Tier 3 recompute. Child getters\n// check these to bubble their cached deps to the parent. Module-scoped\n// (like Vue's activeEffect / Solid's Listener) for zero-cost Tier 1 checks.\n\nlet _activeStateTracking: Set<string> | null = null;\nlet _activeSourceTracking: Map<string, TrackedSource> | null = null;\n\n// ── Auto-tracking utilities ──────────────────────────────────────\n\nfunction isAutoTrackable(value: unknown): boolean {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as any).subscribe === 'function'\n );\n}\n\n// ── Async tracking types ─────────────────────────────────────────\n\nconst DEFAULT_TASK_STATE: TaskState = Object.freeze({ loading: false, error: null, errorCode: null });\n\nexport type AsyncMethodKeys<T, Base = ViewModel<any, any>> = {\n [K in Exclude<keyof T, keyof Base>]: T[K] extends (...args: any[]) => Promise<any> ? K : never;\n}[Exclude<keyof T, keyof Base>];\n\ntype AsyncMap<T> = {\n readonly [K in AsyncMethodKeys<T>]: TaskState;\n};\n\nconst RESERVED_ASYNC_KEYS = ['async', 'subscribeAsync'] as const;\nconst LIFECYCLE_HOOKS = new Set(['onInit', 'onSet', 'onDispose']);\n\n// ── ViewModel ────────────────────────────────────────────────────\n\ntype EmptyViewModelState = { __brand: 'EmptyViewModelState'}\n\n/**\n * Reactive state container with computed getters, automatic async tracking, and typed events.\n * Subclass and define state shape, getters, and action methods. Use with `useLocal` in React.\n */\nexport abstract class ViewModel<S extends object = EmptyViewModelState, E extends Record<string, any> = {}> implements Subscribable<S> {\n private _state: Readonly<S>;\n private _initialState: Readonly<S>;\n private _disposed = false;\n private _initialized = false;\n private _listeners = new Set<Listener<S>>();\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n private _subscriptionCleanups: (() => void)[] | null = null;\n private _eventBus: EventBus<E> | null = null;\n\n // ── Reactive derived state (RFC 1) ─────────────────────────────\n private _revision = 0;\n private _trackedSources = new Map<string, TrackedSource>();\n\n // ── Async tracking (RFC 2) ──────────────────────────────────────\n // Lazily allocated on first async method wrap to keep construction fast.\n private _asyncStates: Map<string, InternalTaskState> | null = null;\n private _asyncSnapshots: Map<string, TaskState> | null = null;\n private _asyncListeners: Set<() => void> | null = null;\n private _asyncProxy: AsyncMap<this> | null = null;\n private _activeOps: Map<string, number> | null = null;\n\n /** DEV-only timeout (ms) for detecting ghost async operations after dispose. */\n static GHOST_TIMEOUT = 3000;\n\n constructor(...args: EmptyViewModelState extends S ? [] | [initialState: S] : [initialState: S]) {\n const initialState = (args[0] ?? {} as S);\n this._state = freeze({ ...initialState });\n this._initialState = this._state;\n this._guardAndBind();\n }\n\n /** Current frozen state object. */\n get state(): S {\n return this._state;\n }\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** Whether init() has been called. */\n get initialized(): boolean {\n return this._initialized;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n /** Lazily-created typed EventBus for emitting and subscribing to events. */\n get events(): EventBus<E> {\n if (!this._eventBus) {\n this._eventBus = new EventBus<E>();\n }\n return this._eventBus;\n }\n\n /** Initializes the instance. Called automatically by React hooks after mount. */\n init(): void | Promise<void> {\n if (this._initialized || this._disposed) return;\n this._initialized = true;\n this._trackSubscribables();\n this._installStateProxy();\n this._processMembers();\n return this.onInit?.();\n }\n\n /**\n * Merges partial state into current state. If no values actually\n * changed by reference, this is a no-op.\n *\n * Triggers React re-render via listener notification. Called when:\n * - User code calls set() to update source state\n *\n * NOT called for subscribable member notifications — those use\n * a separate notification path (see _trackSubscribables).\n */\n protected set(partialOrUpdater: Partial<S> | Updater<S> | ((draft: S) => void)): void {\n if (this._disposed) return;\n\n // __DEV__ guard: set() inside a getter would cause infinite loops.\n // After init(), getters are auto-memoized; set() → notify → recompute → set() → ∞\n if (__DEV__ && _activeStateTracking) {\n console.error(\n '[mvc-kit] set() called inside a getter. ' +\n 'Getters must be pure — they read state and return a value. ' +\n 'They must never call set(), which would cause an infinite ' +\n 'render loop. Move this logic to an action method.'\n );\n return;\n }\n\n let partial: Partial<S>;\n if (typeof partialOrUpdater === 'function') {\n const result = resolveDraftUpdater<S>(this._state, partialOrUpdater as (s: S) => Partial<S> | void);\n if (!result) return;\n partial = result;\n } else {\n partial = partialOrUpdater;\n }\n\n // Check if any values actually changed (shallow equality).\n // Uses for..in to avoid Object.keys() array allocation.\n let hasChanges = false;\n const current = this._state;\n for (const key in partial) {\n if ((partial as any)[key] !== (current as any)[key]) {\n hasChanges = true;\n break;\n }\n }\n\n if (!hasChanges) {\n return;\n }\n\n const prev = this._state;\n const next = freeze({ ...prev, ...partial });\n this._state = next;\n this._revision++;\n\n this.onSet?.(prev, next);\n\n for (const listener of this._listeners) {\n listener(next, prev);\n }\n }\n\n /**\n * Emits a typed event via the internal EventBus.\n * Safe to call during dispose cleanup callbacks.\n * @protected\n */\n protected emit<K extends keyof E>(event: K, payload: E[K]): void {\n // During dispose sequence: _disposed is true but eventBus not yet disposed.\n // Cleanup callbacks can still emit. After eventBus.dispose(), this is a no-op.\n // If eventBus was never created, fall back to _disposed check.\n if (this._eventBus?.disposed ?? this._disposed) return;\n this.events.emit(event, payload);\n }\n\n /** Subscribes to state changes. Returns an unsubscribe function. */\n subscribe(listener: Listener<S>): () => void {\n if (this._disposed) {\n return () => {};\n }\n\n this._listeners.add(listener);\n\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\n this._disposed = true;\n\n this._teardownSubscriptions();\n\n // Async tracking cleanup — handled by addCleanup registered in _processMembers()\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this._eventBus?.dispose();\n this.onDispose?.();\n this._listeners.clear();\n }\n\n /**\n * Resets state to initial values (or provided state), aborts in-flight work,\n * clears async tracking, and re-runs onInit().\n */\n reset(newState?: S): void | Promise<void> {\n if (this._disposed) return;\n\n // 1. Abort in-flight, lazy-recreate on next disposeSignal access\n this._abortController?.abort();\n this._abortController = null;\n\n this._teardownSubscriptions();\n\n // 2. Reset state\n this._state = newState ? freeze({ ...newState }) : this._initialState;\n this._revision++;\n\n // 3. Clear async tracking (preserve listeners — React still subscribed)\n this._asyncStates?.clear();\n this._asyncSnapshots?.clear();\n this._notifyAsync();\n\n // 4. Re-track subscribable members (fresh subscriptions)\n this._trackSubscribables();\n\n // 5. Notify state listeners (React re-renders with new state)\n for (const listener of this._listeners) {\n listener(this._state, this._state);\n }\n\n // 6. Re-run onInit\n return this.onInit?.();\n }\n\n /**\n * Registers a cleanup function to be called on dispose. Used internally for things like method wrapper\n * cleanup and event bus disposal, but can also be used by subclasses for custom cleanup logic.\n * @param fn\n * @protected\n */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Subscribes to an external Subscribable with automatic cleanup on dispose. @protected */\n protected subscribeTo<T>(source: Subscribable<T>, listener: Listener<T>): () => void {\n const unsubscribe = source.subscribe(listener);\n if (!this._subscriptionCleanups) this._subscriptionCleanups = [];\n this._subscriptionCleanups.push(unsubscribe);\n return unsubscribe;\n }\n\n /** Subscribes to a typed event on a Channel or EventBus with automatic cleanup on dispose and reset. @protected */\n protected listenTo<K extends string, S extends { on(event: K, handler: (payload: any) => void): () => void }>(\n source: S,\n event: K,\n handler: (payload: EventPayload<S, K>) => void,\n ): () => void {\n const unsubscribe = source.on(event, handler);\n if (!this._subscriptionCleanups) this._subscriptionCleanups = [];\n this._subscriptionCleanups.push(unsubscribe);\n return unsubscribe;\n }\n\n /** Pipes a Channel event into a Collection via upsert. Calls channel.init() and registers auto-cleanup on dispose and reset. @protected */\n protected pipeChannel<\n K extends string,\n C extends { init(): void | Promise<void>; on(event: K, handler: (payload: any) => void): () => void },\n >(\n channel: C,\n type: K,\n target: { upsert(item: EventPayload<C, K>): void },\n ): () => void {\n channel.init();\n return this.listenTo(channel, type, (payload) => {\n target.upsert(payload);\n });\n }\n\n /** Lifecycle hook called after every set() with the previous state. @protected */\n protected onSet?(prev: S, next: S): void;\n /** Lifecycle hook called at the end of init(). Override to load initial data. @protected */\n protected onInit?(): void | Promise<void>;\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n\n // ── Async tracking API ──────────────────────────────────────────\n\n /** Proxy providing `TaskState` (loading, error, errorCode) per async method. */\n get async(): AsyncMap<this> {\n if (!this._asyncProxy) {\n const self = this;\n this._asyncProxy = new Proxy({} as AsyncMap<this>, {\n get(_, prop: string) {\n return self._asyncSnapshots?.get(prop) ?? DEFAULT_TASK_STATE;\n },\n has(_, prop: string) {\n return self._asyncSnapshots?.has(prop) ?? false;\n },\n ownKeys() {\n return self._asyncSnapshots ? Array.from(self._asyncSnapshots.keys()) : [];\n },\n getOwnPropertyDescriptor(_, prop: string) {\n if (self._asyncSnapshots?.has(prop)) {\n return { configurable: true, enumerable: true, value: self._asyncSnapshots.get(prop) };\n }\n return undefined;\n },\n });\n }\n return this._asyncProxy;\n }\n\n /** Subscribes to async state changes. Used for React integration. */\n subscribeAsync(listener: () => void): () => void {\n if (this._disposed) return () => {};\n if (!this._asyncListeners) this._asyncListeners = new Set();\n this._asyncListeners.add(listener);\n return () => { this._asyncListeners!.delete(listener); };\n }\n\n private _notifyAsync(): void {\n if (!this._asyncListeners) return;\n for (const listener of this._asyncListeners) {\n listener();\n }\n }\n\n // ── Async tracking internals ────────────────────────────────────\n\n private _teardownSubscriptions(): void {\n for (const tracked of this._trackedSources.values()) tracked.unsubscribe();\n this._trackedSources.clear();\n\n if (this._subscriptionCleanups) {\n for (const fn of this._subscriptionCleanups) fn();\n this._subscriptionCleanups = null;\n }\n }\n\n /**\n * Guards reserved keys and auto-binds subclass methods in a single pass\n * using the cached class metadata from getClassMemberInfo.\n */\n private _guardAndBind(): void {\n const info = getClassMemberInfo(this, ViewModel.prototype, RESERVED_ASYNC_KEYS, LIFECYCLE_HOOKS);\n if (info.reservedKeys.length > 0) {\n throw new Error(\n `[mvc-kit] \"${info.reservedKeys[0]}\" is a reserved property on ViewModel and cannot be overridden.`\n );\n }\n for (let i = 0; i < info.methods.length; i++) {\n (this as any)[info.methods[i].key] = info.methods[i].fn.bind(this);\n }\n }\n\n // ── Member processing (merged getter memoization + async method wrapping) ──\n\n /**\n * Uses cached class metadata to memoize getters (RFC 1) and delegates\n * async method wrapping to the shared wrapAsyncMethods helper (RFC 2).\n * Class metadata is computed once per class via getClassMemberInfo() and reused\n * across all instances — avoids repeated prototype walks.\n */\n private _processMembers(): void {\n const info = getClassMemberInfo(this, ViewModel.prototype, RESERVED_ASYNC_KEYS, LIFECYCLE_HOOKS);\n\n // Memoize getters\n for (let i = 0; i < info.getters.length; i++) {\n this._wrapGetter(info.getters[i].key, info.getters[i].get);\n }\n\n // DEV: Instance property reserved key check (must run even if no methods to wrap)\n if (__DEV__) {\n for (const key of RESERVED_ASYNC_KEYS) {\n if (Object.getOwnPropertyDescriptor(this, key)?.value !== undefined) {\n throw new Error(\n `[mvc-kit] \"${key}\" is a reserved property on ViewModel and cannot be overridden.`\n );\n }\n }\n }\n\n // Skip async wrapping if no methods to wrap\n if (info.methods.length === 0) return;\n\n // Lazily allocate async tracking collections\n if (!this._asyncStates) this._asyncStates = new Map();\n if (!this._asyncSnapshots) this._asyncSnapshots = new Map();\n if (!this._asyncListeners) this._asyncListeners = new Set();\n\n // Initialize DEV-only active ops tracking\n if (__DEV__) {\n this._activeOps = new Map();\n }\n\n // Wrap async methods (shared with Resource)\n wrapAsyncMethods({\n instance: this,\n stopPrototype: ViewModel.prototype,\n reservedKeys: RESERVED_ASYNC_KEYS,\n lifecycleHooks: LIFECYCLE_HOOKS,\n isDisposed: () => this._disposed,\n isInitialized: () => this._initialized,\n asyncStates: this._asyncStates,\n asyncSnapshots: this._asyncSnapshots,\n asyncListeners: this._asyncListeners,\n notifyAsync: () => this._notifyAsync(),\n addCleanup: (fn) => this.addCleanup(fn),\n ghostTimeout: (this.constructor as typeof ViewModel).GHOST_TIMEOUT,\n className: 'ViewModel',\n activeOps: this._activeOps,\n methods: info.methods,\n });\n }\n\n // ── Auto-tracking internals ────────────────────────────────────\n\n /**\n * Installs a context-sensitive state getter on the instance.\n *\n * During getter tracking (_activeStateTracking is active): returns a Proxy\n * that records which state properties are accessed. The Proxy is created\n * lazily on first tracking access to keep init() fast.\n *\n * Otherwise: returns the frozen state object directly. This is critical\n * for React's useSyncExternalStore — it needs a changing reference to\n * detect state updates and trigger re-renders.\n */\n private _installStateProxy(): void {\n let stateProxy: S | null = null;\n\n Object.defineProperty(this, 'state', {\n get: () => {\n if (_activeStateTracking) {\n if (!stateProxy) {\n stateProxy = new Proxy({} as S, {\n get: (_, prop: string) => {\n _activeStateTracking?.add(prop);\n return (this._state as any)[prop];\n },\n ownKeys: () => Reflect.ownKeys(this._state as object),\n getOwnPropertyDescriptor: (_, prop) =>\n Reflect.getOwnPropertyDescriptor(this._state as object, prop),\n set: () => {\n throw new Error('Cannot mutate state directly. Use set() instead.');\n },\n has: (_, prop) => prop in (this._state as object),\n });\n }\n return stateProxy;\n }\n return this._state;\n },\n configurable: true,\n enumerable: true,\n });\n }\n\n /**\n * Scans own instance properties for Subscribable objects and sets up\n * automatic dependency tracking for each one found.\n *\n * For each subscribable member:\n * 1. Subscribe to it. On notification: bump its tracked revision\n * AND the VM's global revision, then force a new state reference\n * and notify listeners so React re-renders.\n * 2. Replace the instance property with a getter that participates\n * in dependency tracking.\n * 3. Register unsubscribe in the dispose chain.\n *\n * Called during init(), AFTER all subclass property initializers\n * have run (they execute during the constructor, before init()).\n */\n private _trackSubscribables(): void {\n for (const key of Object.getOwnPropertyNames(this)) {\n const value = (this as any)[key];\n if (!isAutoTrackable(value)) continue;\n\n let tracked: TrackedSource;\n\n const onSourceNotify = () => {\n if (this._disposed) return;\n\n // Source notified — bump revisions for getter invalidation\n tracked.revision++;\n this._revision++;\n\n for (const listener of this._listeners) {\n listener(this._state, this._state);\n }\n };\n\n const unsubState = value.subscribe(onSourceNotify);\n const unsubAsync =\n typeof value.subscribeAsync === 'function'\n ? value.subscribeAsync(onSourceNotify)\n : undefined;\n\n tracked = {\n source: value,\n revision: 0,\n unsubscribe: unsubAsync\n ? () => { unsubState(); unsubAsync(); }\n : unsubState,\n };\n\n this._trackedSources.set(key, tracked);\n\n // Replace the instance property with a tracking getter.\n // The original value is captured in the closure.\n Object.defineProperty(this, key, {\n get: () => {\n _activeSourceTracking?.set(key, tracked);\n return value;\n },\n configurable: true,\n enumerable: false,\n });\n }\n }\n\n /**\n * Bubbles cached dependency records to the active parent tracking context.\n * Called from Tier 1/Tier 2 cache hits during nested getter composition\n * so the parent getter records the full transitive dependency set.\n * Extracted to keep the getter closure small for V8 inlining.\n */\n private _bubbleDeps(stateDepKeys: string[] | undefined, sourceDepKeys: string[] | undefined): void {\n const st = _activeStateTracking!;\n if (stateDepKeys) {\n for (let i = 0; i < stateDepKeys.length; i++) st.add(stateDepKeys[i]);\n }\n if (sourceDepKeys) {\n const srt = _activeSourceTracking!;\n for (let i = 0; i < sourceDepKeys.length; i++) {\n const ts = this._trackedSources.get(sourceDepKeys[i]);\n if (ts) srt.set(sourceDepKeys[i], ts);\n }\n }\n }\n\n /**\n * Replaces a single prototype getter with a memoized version on this\n * instance. The memoized getter tracks both state dependencies and\n * subscribable member dependencies, caching its result and\n * revalidating through a three-tier strategy:\n *\n * Tier 1 (fast): revision unchanged → return cached (1 int compare)\n * Tier 2 (medium): revision changed but this getter's deps didn't → return cached\n * Tier 3 (slow): at least one dep changed → full recompute with tracking\n */\n private _wrapGetter(key: string, original: () => unknown): void {\n // Per-getter cache state, private to this getter on this instance.\n let cached: unknown;\n let validatedAtRevision = -1;\n\n // Array-based dep tracking — avoids Map iterator allocation in Tier 2\n let stateDepKeys: string[] | undefined;\n let stateDepValues: unknown[] | undefined;\n let sourceDepKeys: string[] | undefined;\n let sourceDepRevisions: number[] | undefined;\n\n // Reusable tracking containers — allocated on first Tier 3, reused via clear()\n let trackingSet: Set<string> | undefined;\n let trackingMap: Map<string, TrackedSource> | undefined;\n\n Object.defineProperty(this, key, {\n get: () => {\n // ── Tier 1: Fast path (1 integer compare) ───────────────\n if (validatedAtRevision === this._revision) {\n if (_activeStateTracking) this._bubbleDeps(stateDepKeys, sourceDepKeys);\n return cached;\n }\n\n // After dispose, revision never changes so Tier 1 hits if\n // getter was ever called. Guard the uncalled-before-dispose edge case.\n if (this._disposed) return cached;\n\n // ── Tier 2: Medium path — array-based dep check ─────────\n if (stateDepKeys !== undefined) {\n let fresh = true;\n\n // Check state deps by reference (array iteration, no iterator alloc)\n const state = this._state as any;\n for (let i = 0; i < stateDepKeys.length; i++) {\n if (state[stateDepKeys[i]] !== stateDepValues![i]) {\n fresh = false;\n break;\n }\n }\n\n // Check subscribable deps by revision\n if (fresh && sourceDepKeys !== undefined && sourceDepKeys.length > 0) {\n for (let i = 0; i < sourceDepKeys.length; i++) {\n const ts = this._trackedSources.get(sourceDepKeys[i]);\n if (ts && ts.revision !== sourceDepRevisions![i]) {\n fresh = false;\n break;\n }\n }\n }\n\n if (fresh) {\n if (_activeStateTracking) this._bubbleDeps(stateDepKeys, sourceDepKeys);\n validatedAtRevision = this._revision;\n return cached;\n }\n }\n\n // ── Tier 3: Slow path — full recompute ─────────────────\n // Save parent tracking context for nested getter composition\n const parentStateTracking = _activeStateTracking;\n const parentSourceTracking = _activeSourceTracking;\n\n // Reuse tracking containers (clear instead of allocate)\n if (trackingSet) {\n trackingSet.clear();\n } else {\n trackingSet = new Set();\n }\n if (trackingMap) {\n trackingMap.clear();\n } else {\n trackingMap = new Map();\n }\n\n _activeStateTracking = trackingSet;\n _activeSourceTracking = trackingMap;\n\n try {\n cached = original.call(this);\n } catch (e) {\n // Don't cache failed computations\n _activeStateTracking = parentStateTracking;\n _activeSourceTracking = parentSourceTracking;\n throw e;\n }\n\n // Restore parent tracking context\n _activeStateTracking = parentStateTracking;\n _activeSourceTracking = parentSourceTracking;\n\n // Bubble deps up to parent getter if nested\n if (parentStateTracking) {\n for (const d of trackingSet) parentStateTracking.add(d);\n }\n if (parentSourceTracking) {\n for (const [k, v] of trackingMap) {\n parentSourceTracking.set(k, v);\n }\n }\n\n // Snapshot state dep values into arrays for Tier 2\n const depCount = trackingSet.size;\n if (!stateDepKeys || stateDepKeys.length !== depCount) {\n stateDepKeys = new Array(depCount);\n stateDepValues = new Array(depCount);\n }\n {\n let i = 0;\n const state = this._state as any;\n for (const d of trackingSet) {\n stateDepKeys[i] = d;\n stateDepValues![i] = state[d];\n i++;\n }\n }\n\n // Snapshot subscribable revisions into arrays\n const sourceCount = trackingMap.size;\n if (sourceCount > 0) {\n if (!sourceDepKeys || sourceDepKeys.length !== sourceCount) {\n sourceDepKeys = new Array(sourceCount);\n sourceDepRevisions = new Array(sourceCount);\n }\n let i = 0;\n for (const [memberKey, tracked] of trackingMap) {\n sourceDepKeys[i] = memberKey;\n sourceDepRevisions![i] = tracked.revision;\n i++;\n }\n } else {\n sourceDepKeys = undefined;\n sourceDepRevisions = undefined;\n }\n\n validatedAtRevision = this._revision;\n\n return cached;\n },\n configurable: true,\n enumerable: true,\n });\n }\n}\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -23,5 +23,6 @@ export type { PendingOperation, PendingEntry } from './Pending';
23
23
  export type { AppError } from './errors';
24
24
  export { HttpError, isAbortError, classifyError } from './errors';
25
25
  export { bindPublicMethods } from './bindPublicMethods';
26
+ export { produceDraft, resolveDraftUpdater } from './produceDraft';
26
27
  export { singleton, hasSingleton, teardown, teardownAll } from './singleton';
27
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,YAAY,GACb,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAGhE,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,YAAY,GACb,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAGhE,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAGnE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
package/dist/mvc-kit.cjs CHANGED
@@ -17,6 +17,7 @@ const Feed = require("./Feed.cjs");
17
17
  const Pending = require("./Pending.cjs");
18
18
  const errors = require("./errors.cjs");
19
19
  const bindPublicMethods = require("./bindPublicMethods.cjs");
20
+ const produceDraft = require("./produceDraft.cjs");
20
21
  const singleton = require("./singleton.cjs");
21
22
  exports.ViewModel = ViewModel.ViewModel;
22
23
  exports.Model = Model.Model;
@@ -37,6 +38,8 @@ exports.HttpError = errors.HttpError;
37
38
  exports.classifyError = errors.classifyError;
38
39
  exports.isAbortError = errors.isAbortError;
39
40
  exports.bindPublicMethods = bindPublicMethods.bindPublicMethods;
41
+ exports.produceDraft = produceDraft.produceDraft;
42
+ exports.resolveDraftUpdater = produceDraft.resolveDraftUpdater;
40
43
  exports.hasSingleton = singleton.hasSingleton;
41
44
  exports.singleton = singleton.singleton;
42
45
  exports.teardown = singleton.teardown;
@@ -1 +1 @@
1
- {"version":3,"file":"mvc-kit.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"mvc-kit.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/mvc-kit.js CHANGED
@@ -15,6 +15,7 @@ import { Feed } from "./Feed.js";
15
15
  import { Pending } from "./Pending.js";
16
16
  import { HttpError, classifyError, isAbortError } from "./errors.js";
17
17
  import { bindPublicMethods } from "./bindPublicMethods.js";
18
+ import { produceDraft, resolveDraftUpdater } from "./produceDraft.js";
18
19
  import { hasSingleton, singleton, teardown, teardownAll } from "./singleton.js";
19
20
  export {
20
21
  Channel,
@@ -37,6 +38,8 @@ export {
37
38
  classifyError,
38
39
  hasSingleton,
39
40
  isAbortError,
41
+ produceDraft,
42
+ resolveDraftUpdater,
40
43
  singleton,
41
44
  teardown,
42
45
  teardownAll
@@ -1 +1 @@
1
- {"version":3,"file":"mvc-kit.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"mvc-kit.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
4
+ function isPlainObject(value) {
5
+ if (value === null || typeof value !== "object") return false;
6
+ const proto = Object.getPrototypeOf(value);
7
+ return proto === Object.prototype || proto === null;
8
+ }
9
+ function createDraftNode(original) {
10
+ let copy = null;
11
+ const children = /* @__PURE__ */ new Map();
12
+ function ensureCopy() {
13
+ if (!copy) copy = { ...original };
14
+ return copy;
15
+ }
16
+ const proxy = new Proxy({}, {
17
+ get(_, prop) {
18
+ if (typeof prop === "symbol") return original[prop];
19
+ const key = prop;
20
+ if (children.has(key)) return children.get(key).proxy;
21
+ const source = copy ?? original;
22
+ const value = source[key];
23
+ if (isPlainObject(value)) {
24
+ const child = createDraftNode(value);
25
+ children.set(key, child);
26
+ return child.proxy;
27
+ }
28
+ if (__DEV__ && Array.isArray(value)) {
29
+ return Object.freeze([...value]);
30
+ }
31
+ return value;
32
+ },
33
+ set(_, prop, value) {
34
+ if (typeof prop === "symbol") return true;
35
+ const key = prop;
36
+ const source = copy ?? original;
37
+ if (source[key] !== value) {
38
+ ensureCopy();
39
+ copy[key] = value;
40
+ children.delete(key);
41
+ }
42
+ return true;
43
+ },
44
+ ownKeys() {
45
+ return Reflect.ownKeys(copy ?? original);
46
+ },
47
+ getOwnPropertyDescriptor(_, prop) {
48
+ const source = copy ?? original;
49
+ if (Object.prototype.hasOwnProperty.call(source, prop)) {
50
+ return { value: source[prop], writable: true, enumerable: true, configurable: true };
51
+ }
52
+ return void 0;
53
+ },
54
+ has(_, prop) {
55
+ return prop in (copy ?? original);
56
+ }
57
+ });
58
+ return {
59
+ proxy,
60
+ changed() {
61
+ if (copy) return true;
62
+ for (const child of children.values()) {
63
+ if (child.changed()) return true;
64
+ }
65
+ return false;
66
+ },
67
+ finalize() {
68
+ for (const [key, child] of children) {
69
+ if (child.changed()) {
70
+ ensureCopy();
71
+ copy[key] = child.finalize();
72
+ }
73
+ }
74
+ return copy ?? original;
75
+ }
76
+ };
77
+ }
78
+ function produceDraft(state, mutator) {
79
+ const root = createDraftNode(state);
80
+ mutator(root.proxy);
81
+ if (!root.changed()) return null;
82
+ const finalized = root.finalize();
83
+ const partial = {};
84
+ let hasChanges = false;
85
+ for (const key of Object.keys(finalized)) {
86
+ if (finalized[key] !== state[key]) {
87
+ partial[key] = finalized[key];
88
+ hasChanges = true;
89
+ }
90
+ }
91
+ return hasChanges ? partial : null;
92
+ }
93
+ function resolveDraftUpdater(state, updater) {
94
+ let explicitReturn;
95
+ const draftChanges = produceDraft(state, (draft) => {
96
+ const result = updater(draft);
97
+ if (result !== void 0 && result !== null && typeof result === "object") {
98
+ explicitReturn = result;
99
+ }
100
+ });
101
+ return explicitReturn ?? draftChanges;
102
+ }
103
+ exports.produceDraft = produceDraft;
104
+ exports.resolveDraftUpdater = resolveDraftUpdater;
105
+ //# sourceMappingURL=produceDraft.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"produceDraft.cjs","sources":["../src/produceDraft.ts"],"sourcesContent":["const __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\n/**\n * Checks if a value is a plain object (POJO).\n * Returns false for arrays, Dates, class instances, null, etc.\n */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\ninterface DraftNode<T extends object = object> {\n proxy: T;\n changed(): boolean;\n finalize(): T;\n}\n\nfunction createDraftNode<T extends object>(original: Readonly<T>): DraftNode<T> {\n let copy: Record<string, unknown> | null = null;\n const children = new Map<string, DraftNode>();\n\n function ensureCopy(): Record<string, unknown> {\n if (!copy) copy = { ...(original as Record<string, unknown>) };\n return copy;\n }\n\n // Use empty object as proxy target to avoid invariant violations\n // with frozen state objects. All reads/writes go through the handler.\n const proxy = new Proxy({} as T, {\n get(_, prop) {\n if (typeof prop === 'symbol') return (original as any)[prop];\n\n const key = prop as string;\n\n // Return cached child draft proxy\n if (children.has(key)) return children.get(key)!.proxy;\n\n // Read from copy (if mutated) or original\n const source: any = copy ?? original;\n const value = source[key];\n\n // Auto-draft nested plain objects\n if (isPlainObject(value)) {\n const child = createDraftNode(value as Record<string, unknown>);\n children.set(key, child);\n return child.proxy;\n }\n\n // DEV: freeze arrays so mutation methods (push, splice) throw immediately\n // instead of silently mutating the original state\n if (__DEV__ && Array.isArray(value)) {\n return Object.freeze([...value]);\n }\n\n return value;\n },\n\n set(_, prop, value) {\n if (typeof prop === 'symbol') return true;\n\n const key = prop as string;\n const source: any = copy ?? original;\n\n if (source[key] !== value) {\n ensureCopy();\n copy![key] = value;\n // Discard child draft — value was fully replaced\n children.delete(key);\n }\n return true;\n },\n\n ownKeys() {\n return Reflect.ownKeys((copy ?? original) as object);\n },\n\n getOwnPropertyDescriptor(_, prop) {\n const source = (copy ?? original) as Record<string, unknown>;\n if (Object.prototype.hasOwnProperty.call(source, prop)) {\n return { value: source[prop as string], writable: true, enumerable: true, configurable: true };\n }\n return undefined;\n },\n\n has(_, prop) {\n return prop in ((copy ?? original) as object);\n },\n });\n\n return {\n proxy,\n\n changed(): boolean {\n if (copy) return true;\n for (const child of children.values()) {\n if (child.changed()) return true;\n }\n return false;\n },\n\n finalize(): T {\n // Merge child results bottom-up\n for (const [key, child] of children) {\n if (child.changed()) {\n ensureCopy();\n copy![key] = child.finalize();\n }\n }\n return (copy ?? original) as T;\n },\n };\n}\n\n/**\n * Creates a copy-on-write draft proxy of the given state, runs the mutator,\n * and returns only the changed top-level keys as a Partial.\n * Returns null if nothing was modified.\n *\n * - Nested plain objects use copy-on-write structural sharing\n * - Same-value assignments are no-ops\n * - Reads reflect prior writes within the same draft\n * - Only POJOs are proxied; class instances, arrays, Dates pass through as-is\n * - Arrays must be replaced via assignment, not mutated in place\n */\nexport function produceDraft<S extends object>(\n state: Readonly<S>,\n mutator: (draft: S) => void,\n): Partial<S> | null {\n const root = createDraftNode(state);\n mutator(root.proxy);\n\n if (!root.changed()) return null;\n\n const finalized = root.finalize();\n\n // Extract only changed top-level keys\n const partial: Record<string, unknown> = {};\n let hasChanges = false;\n\n for (const key of Object.keys(finalized)) {\n if ((finalized as any)[key] !== (state as any)[key]) {\n partial[key] = (finalized as any)[key];\n hasChanges = true;\n }\n }\n\n return hasChanges ? (partial as Partial<S>) : null;\n}\n\n/**\n * Resolves a function-form updater through produceDraft.\n * Handles both patterns: explicit return (existing updater) and void return (draft mode).\n * Returns the partial to apply, or null if nothing changed.\n */\nexport function resolveDraftUpdater<S extends object>(\n state: Readonly<S>,\n updater: (stateOrDraft: S) => Partial<S> | void,\n): Partial<S> | null {\n let explicitReturn: Partial<S> | undefined;\n const draftChanges = produceDraft<S>(state, (draft) => {\n const result = updater(draft);\n if (result !== undefined && result !== null && typeof result === 'object') {\n explicitReturn = result;\n }\n });\n return explicitReturn ?? draftChanges;\n}\n"],"names":[],"mappings":";;AAAA,MAAM,UAAU,OAAO,oBAAoB,eAAe;AAM1D,SAAS,cAAc,OAAkD;AACvE,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,QAAM,QAAQ,OAAO,eAAe,KAAK;AACzC,SAAO,UAAU,OAAO,aAAa,UAAU;AACjD;AAQA,SAAS,gBAAkC,UAAqC;AAC9E,MAAI,OAAuC;AAC3C,QAAM,+BAAe,IAAA;AAErB,WAAS,aAAsC;AAC7C,QAAI,CAAC,KAAM,QAAO,EAAE,GAAI,SAAA;AACxB,WAAO;AAAA,EACT;AAIA,QAAM,QAAQ,IAAI,MAAM,IAAS;AAAA,IAC/B,IAAI,GAAG,MAAM;AACX,UAAI,OAAO,SAAS,SAAU,QAAQ,SAAiB,IAAI;AAE3D,YAAM,MAAM;AAGZ,UAAI,SAAS,IAAI,GAAG,UAAU,SAAS,IAAI,GAAG,EAAG;AAGjD,YAAM,SAAc,QAAQ;AAC5B,YAAM,QAAQ,OAAO,GAAG;AAGxB,UAAI,cAAc,KAAK,GAAG;AACxB,cAAM,QAAQ,gBAAgB,KAAgC;AAC9D,iBAAS,IAAI,KAAK,KAAK;AACvB,eAAO,MAAM;AAAA,MACf;AAIA,UAAI,WAAW,MAAM,QAAQ,KAAK,GAAG;AACnC,eAAO,OAAO,OAAO,CAAC,GAAG,KAAK,CAAC;AAAA,MACjC;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,IAAI,GAAG,MAAM,OAAO;AAClB,UAAI,OAAO,SAAS,SAAU,QAAO;AAErC,YAAM,MAAM;AACZ,YAAM,SAAc,QAAQ;AAE5B,UAAI,OAAO,GAAG,MAAM,OAAO;AACzB,mBAAA;AACA,aAAM,GAAG,IAAI;AAEb,iBAAS,OAAO,GAAG;AAAA,MACrB;AACA,aAAO;AAAA,IACT;AAAA,IAEA,UAAU;AACR,aAAO,QAAQ,QAAS,QAAQ,QAAmB;AAAA,IACrD;AAAA,IAEA,yBAAyB,GAAG,MAAM;AAChC,YAAM,SAAU,QAAQ;AACxB,UAAI,OAAO,UAAU,eAAe,KAAK,QAAQ,IAAI,GAAG;AACtD,eAAO,EAAE,OAAO,OAAO,IAAc,GAAG,UAAU,MAAM,YAAY,MAAM,cAAc,KAAA;AAAA,MAC1F;AACA,aAAO;AAAA,IACT;AAAA,IAEA,IAAI,GAAG,MAAM;AACX,aAAO,SAAU,QAAQ;AAAA,IAC3B;AAAA,EAAA,CACD;AAED,SAAO;AAAA,IACL;AAAA,IAEA,UAAmB;AACjB,UAAI,KAAM,QAAO;AACjB,iBAAW,SAAS,SAAS,UAAU;AACrC,YAAI,MAAM,QAAA,EAAW,QAAO;AAAA,MAC9B;AACA,aAAO;AAAA,IACT;AAAA,IAEA,WAAc;AAEZ,iBAAW,CAAC,KAAK,KAAK,KAAK,UAAU;AACnC,YAAI,MAAM,WAAW;AACnB,qBAAA;AACA,eAAM,GAAG,IAAI,MAAM,SAAA;AAAA,QACrB;AAAA,MACF;AACA,aAAQ,QAAQ;AAAA,IAClB;AAAA,EAAA;AAEJ;AAaO,SAAS,aACd,OACA,SACmB;AACnB,QAAM,OAAO,gBAAgB,KAAK;AAClC,UAAQ,KAAK,KAAK;AAElB,MAAI,CAAC,KAAK,QAAA,EAAW,QAAO;AAE5B,QAAM,YAAY,KAAK,SAAA;AAGvB,QAAM,UAAmC,CAAA;AACzC,MAAI,aAAa;AAEjB,aAAW,OAAO,OAAO,KAAK,SAAS,GAAG;AACxC,QAAK,UAAkB,GAAG,MAAO,MAAc,GAAG,GAAG;AACnD,cAAQ,GAAG,IAAK,UAAkB,GAAG;AACrC,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO,aAAc,UAAyB;AAChD;AAOO,SAAS,oBACd,OACA,SACmB;AACnB,MAAI;AACJ,QAAM,eAAe,aAAgB,OAAO,CAAC,UAAU;AACrD,UAAM,SAAS,QAAQ,KAAK;AAC5B,QAAI,WAAW,UAAa,WAAW,QAAQ,OAAO,WAAW,UAAU;AACzE,uBAAiB;AAAA,IACnB;AAAA,EACF,CAAC;AACD,SAAO,kBAAkB;AAC3B;;;"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Creates a copy-on-write draft proxy of the given state, runs the mutator,
3
+ * and returns only the changed top-level keys as a Partial.
4
+ * Returns null if nothing was modified.
5
+ *
6
+ * - Nested plain objects use copy-on-write structural sharing
7
+ * - Same-value assignments are no-ops
8
+ * - Reads reflect prior writes within the same draft
9
+ * - Only POJOs are proxied; class instances, arrays, Dates pass through as-is
10
+ * - Arrays must be replaced via assignment, not mutated in place
11
+ */
12
+ export declare function produceDraft<S extends object>(state: Readonly<S>, mutator: (draft: S) => void): Partial<S> | null;
13
+ /**
14
+ * Resolves a function-form updater through produceDraft.
15
+ * Handles both patterns: explicit return (existing updater) and void return (draft mode).
16
+ * Returns the partial to apply, or null if nothing changed.
17
+ */
18
+ export declare function resolveDraftUpdater<S extends object>(state: Readonly<S>, updater: (stateOrDraft: S) => Partial<S> | void): Partial<S> | null;
19
+ //# sourceMappingURL=produceDraft.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"produceDraft.d.ts","sourceRoot":"","sources":["../src/produceDraft.ts"],"names":[],"mappings":"AAkHA;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAClB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAC1B,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAoBnB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,EAClD,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAClB,OAAO,EAAE,CAAC,YAAY,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,GAC9C,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CASnB"}
@@ -0,0 +1,105 @@
1
+ const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
2
+ function isPlainObject(value) {
3
+ if (value === null || typeof value !== "object") return false;
4
+ const proto = Object.getPrototypeOf(value);
5
+ return proto === Object.prototype || proto === null;
6
+ }
7
+ function createDraftNode(original) {
8
+ let copy = null;
9
+ const children = /* @__PURE__ */ new Map();
10
+ function ensureCopy() {
11
+ if (!copy) copy = { ...original };
12
+ return copy;
13
+ }
14
+ const proxy = new Proxy({}, {
15
+ get(_, prop) {
16
+ if (typeof prop === "symbol") return original[prop];
17
+ const key = prop;
18
+ if (children.has(key)) return children.get(key).proxy;
19
+ const source = copy ?? original;
20
+ const value = source[key];
21
+ if (isPlainObject(value)) {
22
+ const child = createDraftNode(value);
23
+ children.set(key, child);
24
+ return child.proxy;
25
+ }
26
+ if (__DEV__ && Array.isArray(value)) {
27
+ return Object.freeze([...value]);
28
+ }
29
+ return value;
30
+ },
31
+ set(_, prop, value) {
32
+ if (typeof prop === "symbol") return true;
33
+ const key = prop;
34
+ const source = copy ?? original;
35
+ if (source[key] !== value) {
36
+ ensureCopy();
37
+ copy[key] = value;
38
+ children.delete(key);
39
+ }
40
+ return true;
41
+ },
42
+ ownKeys() {
43
+ return Reflect.ownKeys(copy ?? original);
44
+ },
45
+ getOwnPropertyDescriptor(_, prop) {
46
+ const source = copy ?? original;
47
+ if (Object.prototype.hasOwnProperty.call(source, prop)) {
48
+ return { value: source[prop], writable: true, enumerable: true, configurable: true };
49
+ }
50
+ return void 0;
51
+ },
52
+ has(_, prop) {
53
+ return prop in (copy ?? original);
54
+ }
55
+ });
56
+ return {
57
+ proxy,
58
+ changed() {
59
+ if (copy) return true;
60
+ for (const child of children.values()) {
61
+ if (child.changed()) return true;
62
+ }
63
+ return false;
64
+ },
65
+ finalize() {
66
+ for (const [key, child] of children) {
67
+ if (child.changed()) {
68
+ ensureCopy();
69
+ copy[key] = child.finalize();
70
+ }
71
+ }
72
+ return copy ?? original;
73
+ }
74
+ };
75
+ }
76
+ function produceDraft(state, mutator) {
77
+ const root = createDraftNode(state);
78
+ mutator(root.proxy);
79
+ if (!root.changed()) return null;
80
+ const finalized = root.finalize();
81
+ const partial = {};
82
+ let hasChanges = false;
83
+ for (const key of Object.keys(finalized)) {
84
+ if (finalized[key] !== state[key]) {
85
+ partial[key] = finalized[key];
86
+ hasChanges = true;
87
+ }
88
+ }
89
+ return hasChanges ? partial : null;
90
+ }
91
+ function resolveDraftUpdater(state, updater) {
92
+ let explicitReturn;
93
+ const draftChanges = produceDraft(state, (draft) => {
94
+ const result = updater(draft);
95
+ if (result !== void 0 && result !== null && typeof result === "object") {
96
+ explicitReturn = result;
97
+ }
98
+ });
99
+ return explicitReturn ?? draftChanges;
100
+ }
101
+ export {
102
+ produceDraft,
103
+ resolveDraftUpdater
104
+ };
105
+ //# sourceMappingURL=produceDraft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"produceDraft.js","sources":["../src/produceDraft.ts"],"sourcesContent":["const __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\n/**\n * Checks if a value is a plain object (POJO).\n * Returns false for arrays, Dates, class instances, null, etc.\n */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\ninterface DraftNode<T extends object = object> {\n proxy: T;\n changed(): boolean;\n finalize(): T;\n}\n\nfunction createDraftNode<T extends object>(original: Readonly<T>): DraftNode<T> {\n let copy: Record<string, unknown> | null = null;\n const children = new Map<string, DraftNode>();\n\n function ensureCopy(): Record<string, unknown> {\n if (!copy) copy = { ...(original as Record<string, unknown>) };\n return copy;\n }\n\n // Use empty object as proxy target to avoid invariant violations\n // with frozen state objects. All reads/writes go through the handler.\n const proxy = new Proxy({} as T, {\n get(_, prop) {\n if (typeof prop === 'symbol') return (original as any)[prop];\n\n const key = prop as string;\n\n // Return cached child draft proxy\n if (children.has(key)) return children.get(key)!.proxy;\n\n // Read from copy (if mutated) or original\n const source: any = copy ?? original;\n const value = source[key];\n\n // Auto-draft nested plain objects\n if (isPlainObject(value)) {\n const child = createDraftNode(value as Record<string, unknown>);\n children.set(key, child);\n return child.proxy;\n }\n\n // DEV: freeze arrays so mutation methods (push, splice) throw immediately\n // instead of silently mutating the original state\n if (__DEV__ && Array.isArray(value)) {\n return Object.freeze([...value]);\n }\n\n return value;\n },\n\n set(_, prop, value) {\n if (typeof prop === 'symbol') return true;\n\n const key = prop as string;\n const source: any = copy ?? original;\n\n if (source[key] !== value) {\n ensureCopy();\n copy![key] = value;\n // Discard child draft — value was fully replaced\n children.delete(key);\n }\n return true;\n },\n\n ownKeys() {\n return Reflect.ownKeys((copy ?? original) as object);\n },\n\n getOwnPropertyDescriptor(_, prop) {\n const source = (copy ?? original) as Record<string, unknown>;\n if (Object.prototype.hasOwnProperty.call(source, prop)) {\n return { value: source[prop as string], writable: true, enumerable: true, configurable: true };\n }\n return undefined;\n },\n\n has(_, prop) {\n return prop in ((copy ?? original) as object);\n },\n });\n\n return {\n proxy,\n\n changed(): boolean {\n if (copy) return true;\n for (const child of children.values()) {\n if (child.changed()) return true;\n }\n return false;\n },\n\n finalize(): T {\n // Merge child results bottom-up\n for (const [key, child] of children) {\n if (child.changed()) {\n ensureCopy();\n copy![key] = child.finalize();\n }\n }\n return (copy ?? original) as T;\n },\n };\n}\n\n/**\n * Creates a copy-on-write draft proxy of the given state, runs the mutator,\n * and returns only the changed top-level keys as a Partial.\n * Returns null if nothing was modified.\n *\n * - Nested plain objects use copy-on-write structural sharing\n * - Same-value assignments are no-ops\n * - Reads reflect prior writes within the same draft\n * - Only POJOs are proxied; class instances, arrays, Dates pass through as-is\n * - Arrays must be replaced via assignment, not mutated in place\n */\nexport function produceDraft<S extends object>(\n state: Readonly<S>,\n mutator: (draft: S) => void,\n): Partial<S> | null {\n const root = createDraftNode(state);\n mutator(root.proxy);\n\n if (!root.changed()) return null;\n\n const finalized = root.finalize();\n\n // Extract only changed top-level keys\n const partial: Record<string, unknown> = {};\n let hasChanges = false;\n\n for (const key of Object.keys(finalized)) {\n if ((finalized as any)[key] !== (state as any)[key]) {\n partial[key] = (finalized as any)[key];\n hasChanges = true;\n }\n }\n\n return hasChanges ? (partial as Partial<S>) : null;\n}\n\n/**\n * Resolves a function-form updater through produceDraft.\n * Handles both patterns: explicit return (existing updater) and void return (draft mode).\n * Returns the partial to apply, or null if nothing changed.\n */\nexport function resolveDraftUpdater<S extends object>(\n state: Readonly<S>,\n updater: (stateOrDraft: S) => Partial<S> | void,\n): Partial<S> | null {\n let explicitReturn: Partial<S> | undefined;\n const draftChanges = produceDraft<S>(state, (draft) => {\n const result = updater(draft);\n if (result !== undefined && result !== null && typeof result === 'object') {\n explicitReturn = result;\n }\n });\n return explicitReturn ?? draftChanges;\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mvc-kit",
3
- "version": "2.12.0",
3
+ "version": "2.12.2",
4
4
  "description": "Zero-magic, class-based reactive ViewModel library",
5
5
  "type": "module",
6
6
  "main": "./dist/mvc-kit.cjs",
@@ -64,7 +64,9 @@
64
64
  "files": [
65
65
  "./mvc-kit-logo.jpg",
66
66
  "dist",
67
- "agent-config"
67
+ "src",
68
+ "agent-config",
69
+ "README.md"
68
70
  ],
69
71
  "bin": {
70
72
  "mvc-kit-setup": "./agent-config/bin/setup.mjs"