mvc-kit 2.12.4 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/agent-config/bin/postinstall.mjs +4 -3
  2. package/agent-config/bin/setup.mjs +5 -1
  3. package/agent-config/claude-code/agents/mvc-kit-architect.md +11 -8
  4. package/agent-config/claude-code/skills/guide/SKILL.md +20 -7
  5. package/agent-config/claude-code/skills/guide/patterns.md +12 -0
  6. package/agent-config/claude-code/skills/guide/recipes.md +510 -0
  7. package/agent-config/claude-code/skills/guide/testing.md +297 -0
  8. package/agent-config/claude-code/skills/review/SKILL.md +3 -13
  9. package/agent-config/claude-code/skills/review/checklist.md +30 -5
  10. package/agent-config/claude-code/skills/scaffold/SKILL.md +4 -13
  11. package/agent-config/lib/install-claude.mjs +84 -25
  12. package/dist/Channel.cjs +276 -300
  13. package/dist/Channel.cjs.map +1 -1
  14. package/dist/Channel.js +275 -299
  15. package/dist/Channel.js.map +1 -1
  16. package/dist/Collection.cjs +424 -504
  17. package/dist/Collection.cjs.map +1 -1
  18. package/dist/Collection.js +423 -503
  19. package/dist/Collection.js.map +1 -1
  20. package/dist/Controller.cjs +70 -67
  21. package/dist/Controller.cjs.map +1 -1
  22. package/dist/Controller.js +69 -66
  23. package/dist/Controller.js.map +1 -1
  24. package/dist/EventBus.cjs +77 -88
  25. package/dist/EventBus.cjs.map +1 -1
  26. package/dist/EventBus.js +76 -87
  27. package/dist/EventBus.js.map +1 -1
  28. package/dist/Feed.cjs +81 -77
  29. package/dist/Feed.cjs.map +1 -1
  30. package/dist/Feed.js +80 -76
  31. package/dist/Feed.js.map +1 -1
  32. package/dist/Model.cjs +181 -207
  33. package/dist/Model.cjs.map +1 -1
  34. package/dist/Model.js +179 -205
  35. package/dist/Model.js.map +1 -1
  36. package/dist/Pagination.cjs +75 -73
  37. package/dist/Pagination.cjs.map +1 -1
  38. package/dist/Pagination.js +74 -72
  39. package/dist/Pagination.js.map +1 -1
  40. package/dist/Pending.cjs +255 -287
  41. package/dist/Pending.cjs.map +1 -1
  42. package/dist/Pending.js +253 -285
  43. package/dist/Pending.js.map +1 -1
  44. package/dist/PersistentCollection.cjs +242 -285
  45. package/dist/PersistentCollection.cjs.map +1 -1
  46. package/dist/PersistentCollection.js +241 -284
  47. package/dist/PersistentCollection.js.map +1 -1
  48. package/dist/Resource.cjs +166 -174
  49. package/dist/Resource.cjs.map +1 -1
  50. package/dist/Resource.js +164 -172
  51. package/dist/Resource.js.map +1 -1
  52. package/dist/Selection.cjs +84 -94
  53. package/dist/Selection.cjs.map +1 -1
  54. package/dist/Selection.js +83 -93
  55. package/dist/Selection.js.map +1 -1
  56. package/dist/Service.cjs +54 -55
  57. package/dist/Service.cjs.map +1 -1
  58. package/dist/Service.js +53 -54
  59. package/dist/Service.js.map +1 -1
  60. package/dist/Sorting.cjs +102 -101
  61. package/dist/Sorting.cjs.map +1 -1
  62. package/dist/Sorting.js +102 -101
  63. package/dist/Sorting.js.map +1 -1
  64. package/dist/Trackable.cjs +112 -80
  65. package/dist/Trackable.cjs.map +1 -1
  66. package/dist/Trackable.js +111 -79
  67. package/dist/Trackable.js.map +1 -1
  68. package/dist/ViewModel.cjs +528 -576
  69. package/dist/ViewModel.cjs.map +1 -1
  70. package/dist/ViewModel.js +525 -573
  71. package/dist/ViewModel.js.map +1 -1
  72. package/dist/bindPublicMethods.cjs +43 -24
  73. package/dist/bindPublicMethods.cjs.map +1 -1
  74. package/dist/bindPublicMethods.js +43 -24
  75. package/dist/bindPublicMethods.js.map +1 -1
  76. package/dist/errors.cjs +67 -68
  77. package/dist/errors.cjs.map +1 -1
  78. package/dist/errors.js +68 -71
  79. package/dist/errors.js.map +1 -1
  80. package/dist/mvc-kit.cjs +44 -46
  81. package/dist/mvc-kit.js +5 -32
  82. package/dist/produceDraft.cjs +105 -95
  83. package/dist/produceDraft.cjs.map +1 -1
  84. package/dist/produceDraft.js +106 -97
  85. package/dist/produceDraft.js.map +1 -1
  86. package/dist/react/components/CardList.cjs +30 -40
  87. package/dist/react/components/CardList.cjs.map +1 -1
  88. package/dist/react/components/CardList.js +31 -41
  89. package/dist/react/components/CardList.js.map +1 -1
  90. package/dist/react/components/DataTable.cjs +146 -169
  91. package/dist/react/components/DataTable.cjs.map +1 -1
  92. package/dist/react/components/DataTable.js +147 -170
  93. package/dist/react/components/DataTable.js.map +1 -1
  94. package/dist/react/components/InfiniteScroll.cjs +51 -42
  95. package/dist/react/components/InfiniteScroll.cjs.map +1 -1
  96. package/dist/react/components/InfiniteScroll.js +52 -43
  97. package/dist/react/components/InfiniteScroll.js.map +1 -1
  98. package/dist/react/components/types.cjs +10 -6
  99. package/dist/react/components/types.cjs.map +1 -1
  100. package/dist/react/components/types.js +11 -9
  101. package/dist/react/components/types.js.map +1 -1
  102. package/dist/react/guards.cjs +10 -6
  103. package/dist/react/guards.cjs.map +1 -1
  104. package/dist/react/guards.js +11 -9
  105. package/dist/react/guards.js.map +1 -1
  106. package/dist/react/provider.cjs +23 -20
  107. package/dist/react/provider.cjs.map +1 -1
  108. package/dist/react/provider.js +23 -21
  109. package/dist/react/provider.js.map +1 -1
  110. package/dist/react/use-event-bus.cjs +24 -20
  111. package/dist/react/use-event-bus.cjs.map +1 -1
  112. package/dist/react/use-event-bus.js +24 -21
  113. package/dist/react/use-event-bus.js.map +1 -1
  114. package/dist/react/use-instance.cjs +43 -36
  115. package/dist/react/use-instance.cjs.map +1 -1
  116. package/dist/react/use-instance.js +43 -36
  117. package/dist/react/use-instance.js.map +1 -1
  118. package/dist/react/use-local.cjs +48 -64
  119. package/dist/react/use-local.cjs.map +1 -1
  120. package/dist/react/use-local.js +47 -63
  121. package/dist/react/use-local.js.map +1 -1
  122. package/dist/react/use-model.cjs +84 -98
  123. package/dist/react/use-model.cjs.map +1 -1
  124. package/dist/react/use-model.js +84 -100
  125. package/dist/react/use-model.js.map +1 -1
  126. package/dist/react/use-singleton.cjs +19 -23
  127. package/dist/react/use-singleton.cjs.map +1 -1
  128. package/dist/react/use-singleton.js +16 -20
  129. package/dist/react/use-singleton.js.map +1 -1
  130. package/dist/react/use-subscribe-only.cjs +28 -22
  131. package/dist/react/use-subscribe-only.cjs.map +1 -1
  132. package/dist/react/use-subscribe-only.js +28 -22
  133. package/dist/react/use-subscribe-only.js.map +1 -1
  134. package/dist/react/use-teardown.cjs +20 -19
  135. package/dist/react/use-teardown.cjs.map +1 -1
  136. package/dist/react/use-teardown.js +20 -19
  137. package/dist/react/use-teardown.js.map +1 -1
  138. package/dist/react-native/NativeCollection.cjs +98 -78
  139. package/dist/react-native/NativeCollection.cjs.map +1 -1
  140. package/dist/react-native/NativeCollection.js +97 -77
  141. package/dist/react-native/NativeCollection.js.map +1 -1
  142. package/dist/react-native.cjs +2 -4
  143. package/dist/react-native.js +1 -4
  144. package/dist/react.cjs +24 -26
  145. package/dist/react.js +1 -17
  146. package/dist/singleton.cjs +28 -22
  147. package/dist/singleton.cjs.map +1 -1
  148. package/dist/singleton.js +29 -26
  149. package/dist/singleton.js.map +1 -1
  150. package/dist/walkPrototypeChain.cjs +20 -12
  151. package/dist/walkPrototypeChain.cjs.map +1 -1
  152. package/dist/walkPrototypeChain.js +21 -13
  153. package/dist/walkPrototypeChain.js.map +1 -1
  154. package/dist/web/IndexedDBCollection.cjs +53 -36
  155. package/dist/web/IndexedDBCollection.cjs.map +1 -1
  156. package/dist/web/IndexedDBCollection.js +52 -35
  157. package/dist/web/IndexedDBCollection.js.map +1 -1
  158. package/dist/web/WebStorageCollection.cjs +82 -84
  159. package/dist/web/WebStorageCollection.cjs.map +1 -1
  160. package/dist/web/WebStorageCollection.js +81 -83
  161. package/dist/web/WebStorageCollection.js.map +1 -1
  162. package/dist/web/idb.cjs +107 -99
  163. package/dist/web/idb.cjs.map +1 -1
  164. package/dist/web/idb.js +108 -105
  165. package/dist/web/idb.js.map +1 -1
  166. package/dist/web.cjs +4 -6
  167. package/dist/web.js +1 -5
  168. package/dist/wrapAsyncMethods.cjs +141 -168
  169. package/dist/wrapAsyncMethods.cjs.map +1 -1
  170. package/dist/wrapAsyncMethods.js +141 -168
  171. package/dist/wrapAsyncMethods.js.map +1 -1
  172. package/package.json +8 -8
  173. package/src/Pending.test.ts +1 -2
  174. package/src/Sorting.test.ts +1 -1
  175. package/src/produceDraft.test.ts +3 -3
  176. package/src/react/components/CardList.test.tsx +1 -1
  177. package/src/react/components/DataTable.test.tsx +1 -1
  178. package/src/react/components/InfiniteScroll.test.tsx +5 -5
  179. package/dist/mvc-kit.cjs.map +0 -1
  180. package/dist/mvc-kit.js.map +0 -1
  181. package/dist/react-native.cjs.map +0 -1
  182. package/dist/react-native.js.map +0 -1
  183. package/dist/react.cjs.map +0 -1
  184. package/dist/react.js.map +0 -1
  185. package/dist/web.cjs.map +0 -1
  186. package/dist/web.js.map +0 -1
package/dist/errors.js CHANGED
@@ -1,79 +1,76 @@
1
- class HttpError extends Error {
2
- constructor(status, message) {
3
- super(message ?? `HTTP ${status}`);
4
- this.status = status;
5
- this.name = "HttpError";
6
- }
7
- }
1
+ //#region src/errors.ts
2
+ /**
3
+ * Typed HTTP error for services to throw.
4
+ */
5
+ var HttpError = class extends Error {
6
+ constructor(status, message) {
7
+ super(message ?? `HTTP ${status}`);
8
+ this.status = status;
9
+ this.name = "HttpError";
10
+ }
11
+ };
12
+ /**
13
+ * Guard for AbortError — the most-repeated check in async ViewModels.
14
+ * Uses duck-typing so the core lib doesn't require DOM types.
15
+ */
8
16
  function isAbortError(error) {
9
- return error instanceof Error && error.name === "AbortError";
17
+ return error instanceof Error && error.name === "AbortError";
10
18
  }
11
19
  function classifyHttpStatus(status) {
12
- if (status === 401) return "unauthorized";
13
- if (status === 403) return "forbidden";
14
- if (status === 404) return "not_found";
15
- if (status === 422) return "validation";
16
- if (status === 429) return "rate_limited";
17
- if (status >= 500) return "server_error";
18
- return "unknown";
20
+ if (status === 401) return "unauthorized";
21
+ if (status === 403) return "forbidden";
22
+ if (status === 404) return "not_found";
23
+ if (status === 422) return "validation";
24
+ if (status === 429) return "rate_limited";
25
+ if (status >= 500) return "server_error";
26
+ return "unknown";
19
27
  }
20
28
  function isResponseLike(value) {
21
- return typeof value === "object" && value !== null && typeof value.status === "number" && typeof value.statusText === "string" && !(value instanceof Error);
29
+ return typeof value === "object" && value !== null && typeof value.status === "number" && typeof value.statusText === "string" && !(value instanceof Error);
22
30
  }
31
+ /**
32
+ * Maps raw errors to a canonical AppError shape.
33
+ */
23
34
  function classifyError(error) {
24
- if (error instanceof Error && error.name === "AbortError") {
25
- return {
26
- code: "abort",
27
- message: "Request was aborted",
28
- original: error
29
- };
30
- }
31
- if (error instanceof HttpError) {
32
- return {
33
- code: classifyHttpStatus(error.status),
34
- message: error.message,
35
- status: error.status,
36
- original: error
37
- };
38
- }
39
- if (isResponseLike(error)) {
40
- return {
41
- code: classifyHttpStatus(error.status),
42
- message: error.statusText || `HTTP ${error.status}`,
43
- status: error.status,
44
- original: error
45
- };
46
- }
47
- if (error instanceof TypeError && error.message.toLowerCase().includes("fetch")) {
48
- return {
49
- code: "network",
50
- message: error.message,
51
- original: error
52
- };
53
- }
54
- if (error instanceof Error && error.name === "TimeoutError") {
55
- return {
56
- code: "timeout",
57
- message: error.message,
58
- original: error
59
- };
60
- }
61
- if (error instanceof Error) {
62
- return {
63
- code: "unknown",
64
- message: error.message,
65
- original: error
66
- };
67
- }
68
- return {
69
- code: "unknown",
70
- message: String(error),
71
- original: error
72
- };
35
+ if (error instanceof Error && error.name === "AbortError") return {
36
+ code: "abort",
37
+ message: "Request was aborted",
38
+ original: error
39
+ };
40
+ if (error instanceof HttpError) return {
41
+ code: classifyHttpStatus(error.status),
42
+ message: error.message,
43
+ status: error.status,
44
+ original: error
45
+ };
46
+ if (isResponseLike(error)) return {
47
+ code: classifyHttpStatus(error.status),
48
+ message: error.statusText || `HTTP ${error.status}`,
49
+ status: error.status,
50
+ original: error
51
+ };
52
+ if (error instanceof TypeError && error.message.toLowerCase().includes("fetch")) return {
53
+ code: "network",
54
+ message: error.message,
55
+ original: error
56
+ };
57
+ if (error instanceof Error && error.name === "TimeoutError") return {
58
+ code: "timeout",
59
+ message: error.message,
60
+ original: error
61
+ };
62
+ if (error instanceof Error) return {
63
+ code: "unknown",
64
+ message: error.message,
65
+ original: error
66
+ };
67
+ return {
68
+ code: "unknown",
69
+ message: String(error),
70
+ original: error
71
+ };
73
72
  }
74
- export {
75
- HttpError,
76
- classifyError,
77
- isAbortError
78
- };
79
- //# sourceMappingURL=errors.js.map
73
+ //#endregion
74
+ export { HttpError, classifyError, isAbortError };
75
+
76
+ //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sources":["../src/errors.ts"],"sourcesContent":["/**\n * Canonical application error shape for consistent error handling.\n */\nexport interface AppError {\n code:\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'validation'\n | 'rate_limited'\n | 'server_error'\n | 'network'\n | 'timeout'\n | 'abort'\n | 'unknown';\n message: string;\n status?: number;\n original?: unknown;\n}\n\n/**\n * Typed HTTP error for services to throw.\n */\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n message?: string\n ) {\n super(message ?? `HTTP ${status}`);\n this.name = 'HttpError';\n }\n}\n\n/**\n * Guard for AbortError — the most-repeated check in async ViewModels.\n * Uses duck-typing so the core lib doesn't require DOM types.\n */\nexport function isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === 'AbortError';\n}\n\nfunction classifyHttpStatus(\n status: number\n): AppError['code'] {\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'forbidden';\n if (status === 404) return 'not_found';\n if (status === 422) return 'validation';\n if (status === 429) return 'rate_limited';\n if (status >= 500) return 'server_error';\n return 'unknown';\n}\n\nfunction isResponseLike(value: unknown): value is { status: number; statusText: string } {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Record<string, unknown>).status === 'number' &&\n typeof (value as Record<string, unknown>).statusText === 'string' &&\n !(value instanceof Error)\n );\n}\n\n/**\n * Maps raw errors to a canonical AppError shape.\n */\nexport function classifyError(error: unknown): AppError {\n // AbortError (fetch cancelled / DOMException)\n if (error instanceof Error && error.name === 'AbortError') {\n return {\n code: 'abort',\n message: 'Request was aborted',\n original: error,\n };\n }\n\n // HttpError (thrown by services)\n if (error instanceof HttpError) {\n return {\n code: classifyHttpStatus(error.status),\n message: error.message,\n status: error.status,\n original: error,\n };\n }\n\n // Raw Response object (duck-typed: has status + statusText, is not an Error)\n if (isResponseLike(error)) {\n return {\n code: classifyHttpStatus(error.status),\n message: error.statusText || `HTTP ${error.status}`,\n status: error.status,\n original: error,\n };\n }\n\n // Network error (fetch failure)\n if (\n error instanceof TypeError &&\n error.message.toLowerCase().includes('fetch')\n ) {\n return {\n code: 'network',\n message: error.message,\n original: error,\n };\n }\n\n // Timeout error\n if (error instanceof Error && error.name === 'TimeoutError') {\n return {\n code: 'timeout',\n message: error.message,\n original: error,\n };\n }\n\n // Generic Error fallback\n if (error instanceof Error) {\n return {\n code: 'unknown',\n message: error.message,\n original: error,\n };\n }\n\n // Non-Error fallback\n return {\n code: 'unknown',\n message: String(error),\n original: error,\n };\n}\n"],"names":[],"mappings":"AAuBO,MAAM,kBAAkB,MAAM;AAAA,EACnC,YACkB,QAChB,SACA;AACA,UAAM,WAAW,QAAQ,MAAM,EAAE;AAHjB,SAAA,SAAA;AAIhB,SAAK,OAAO;AAAA,EACd;AACF;AAMO,SAAS,aAAa,OAAyB;AACpD,SAAO,iBAAiB,SAAS,MAAM,SAAS;AAClD;AAEA,SAAS,mBACP,QACkB;AAClB,MAAI,WAAW,IAAK,QAAO;AAC3B,MAAI,WAAW,IAAK,QAAO;AAC3B,MAAI,WAAW,IAAK,QAAO;AAC3B,MAAI,WAAW,IAAK,QAAO;AAC3B,MAAI,WAAW,IAAK,QAAO;AAC3B,MAAI,UAAU,IAAK,QAAO;AAC1B,SAAO;AACT;AAEA,SAAS,eAAe,OAAiE;AACvF,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAkC,WAAW,YACrD,OAAQ,MAAkC,eAAe,YACzD,EAAE,iBAAiB;AAEvB;AAKO,SAAS,cAAc,OAA0B;AAEtD,MAAI,iBAAiB,SAAS,MAAM,SAAS,cAAc;AACzD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,MACT,UAAU;AAAA,IAAA;AAAA,EAEd;AAGA,MAAI,iBAAiB,WAAW;AAC9B,WAAO;AAAA,MACL,MAAM,mBAAmB,MAAM,MAAM;AAAA,MACrC,SAAS,MAAM;AAAA,MACf,QAAQ,MAAM;AAAA,MACd,UAAU;AAAA,IAAA;AAAA,EAEd;AAGA,MAAI,eAAe,KAAK,GAAG;AACzB,WAAO;AAAA,MACL,MAAM,mBAAmB,MAAM,MAAM;AAAA,MACrC,SAAS,MAAM,cAAc,QAAQ,MAAM,MAAM;AAAA,MACjD,QAAQ,MAAM;AAAA,MACd,UAAU;AAAA,IAAA;AAAA,EAEd;AAGA,MACE,iBAAiB,aACjB,MAAM,QAAQ,cAAc,SAAS,OAAO,GAC5C;AACA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,MAAM;AAAA,MACf,UAAU;AAAA,IAAA;AAAA,EAEd;AAGA,MAAI,iBAAiB,SAAS,MAAM,SAAS,gBAAgB;AAC3D,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,MAAM;AAAA,MACf,UAAU;AAAA,IAAA;AAAA,EAEd;AAGA,MAAI,iBAAiB,OAAO;AAC1B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,MAAM;AAAA,MACf,UAAU;AAAA,IAAA;AAAA,EAEd;AAGA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,OAAO,KAAK;AAAA,IACrB,UAAU;AAAA,EAAA;AAEd;"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Canonical application error shape for consistent error handling.\n */\nexport interface AppError {\n code:\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'validation'\n | 'rate_limited'\n | 'server_error'\n | 'network'\n | 'timeout'\n | 'abort'\n | 'unknown';\n message: string;\n status?: number;\n original?: unknown;\n}\n\n/**\n * Typed HTTP error for services to throw.\n */\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n message?: string\n ) {\n super(message ?? `HTTP ${status}`);\n this.name = 'HttpError';\n }\n}\n\n/**\n * Guard for AbortError — the most-repeated check in async ViewModels.\n * Uses duck-typing so the core lib doesn't require DOM types.\n */\nexport function isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === 'AbortError';\n}\n\nfunction classifyHttpStatus(\n status: number\n): AppError['code'] {\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'forbidden';\n if (status === 404) return 'not_found';\n if (status === 422) return 'validation';\n if (status === 429) return 'rate_limited';\n if (status >= 500) return 'server_error';\n return 'unknown';\n}\n\nfunction isResponseLike(value: unknown): value is { status: number; statusText: string } {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Record<string, unknown>).status === 'number' &&\n typeof (value as Record<string, unknown>).statusText === 'string' &&\n !(value instanceof Error)\n );\n}\n\n/**\n * Maps raw errors to a canonical AppError shape.\n */\nexport function classifyError(error: unknown): AppError {\n // AbortError (fetch cancelled / DOMException)\n if (error instanceof Error && error.name === 'AbortError') {\n return {\n code: 'abort',\n message: 'Request was aborted',\n original: error,\n };\n }\n\n // HttpError (thrown by services)\n if (error instanceof HttpError) {\n return {\n code: classifyHttpStatus(error.status),\n message: error.message,\n status: error.status,\n original: error,\n };\n }\n\n // Raw Response object (duck-typed: has status + statusText, is not an Error)\n if (isResponseLike(error)) {\n return {\n code: classifyHttpStatus(error.status),\n message: error.statusText || `HTTP ${error.status}`,\n status: error.status,\n original: error,\n };\n }\n\n // Network error (fetch failure)\n if (\n error instanceof TypeError &&\n error.message.toLowerCase().includes('fetch')\n ) {\n return {\n code: 'network',\n message: error.message,\n original: error,\n };\n }\n\n // Timeout error\n if (error instanceof Error && error.name === 'TimeoutError') {\n return {\n code: 'timeout',\n message: error.message,\n original: error,\n };\n }\n\n // Generic Error fallback\n if (error instanceof Error) {\n return {\n code: 'unknown',\n message: error.message,\n original: error,\n };\n }\n\n // Non-Error fallback\n return {\n code: 'unknown',\n message: String(error),\n original: error,\n };\n}\n"],"mappings":";;;;AAuBA,IAAa,YAAb,cAA+B,MAAM;CACnC,YACE,QACA,SACA;AACA,QAAM,WAAW,QAAQ,SAAS;AAHlB,OAAA,SAAA;AAIhB,OAAK,OAAO;;;;;;;AAQhB,SAAgB,aAAa,OAAyB;AACpD,QAAO,iBAAiB,SAAS,MAAM,SAAS;;AAGlD,SAAS,mBACP,QACkB;AAClB,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,UAAU,IAAK,QAAO;AAC1B,QAAO;;AAGT,SAAS,eAAe,OAAiE;AACvF,QACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAkC,WAAW,YACrD,OAAQ,MAAkC,eAAe,YACzD,EAAE,iBAAiB;;;;;AAOvB,SAAgB,cAAc,OAA0B;AAEtD,KAAI,iBAAiB,SAAS,MAAM,SAAS,aAC3C,QAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU;EACX;AAIH,KAAI,iBAAiB,UACnB,QAAO;EACL,MAAM,mBAAmB,MAAM,OAAO;EACtC,SAAS,MAAM;EACf,QAAQ,MAAM;EACd,UAAU;EACX;AAIH,KAAI,eAAe,MAAM,CACvB,QAAO;EACL,MAAM,mBAAmB,MAAM,OAAO;EACtC,SAAS,MAAM,cAAc,QAAQ,MAAM;EAC3C,QAAQ,MAAM;EACd,UAAU;EACX;AAIH,KACE,iBAAiB,aACjB,MAAM,QAAQ,aAAa,CAAC,SAAS,QAAQ,CAE7C,QAAO;EACL,MAAM;EACN,SAAS,MAAM;EACf,UAAU;EACX;AAIH,KAAI,iBAAiB,SAAS,MAAM,SAAS,eAC3C,QAAO;EACL,MAAM;EACN,SAAS,MAAM;EACf,UAAU;EACX;AAIH,KAAI,iBAAiB,MACnB,QAAO;EACL,MAAM;EACN,SAAS,MAAM;EACf,UAAU;EACX;AAIH,QAAO;EACL,MAAM;EACN,SAAS,OAAO,MAAM;EACtB,UAAU;EACX"}
package/dist/mvc-kit.cjs CHANGED
@@ -1,47 +1,45 @@
1
- "use strict";
2
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const ViewModel = require("./ViewModel.cjs");
4
- const Model = require("./Model.cjs");
5
- const Collection = require("./Collection.cjs");
6
- const PersistentCollection = require("./PersistentCollection.cjs");
7
- const Resource = require("./Resource.cjs");
8
- const Controller = require("./Controller.cjs");
9
- const Service = require("./Service.cjs");
10
- const EventBus = require("./EventBus.cjs");
11
- const Channel = require("./Channel.cjs");
12
- const Trackable = require("./Trackable.cjs");
13
- const Sorting = require("./Sorting.cjs");
14
- const Pagination = require("./Pagination.cjs");
15
- const Selection = require("./Selection.cjs");
16
- const Feed = require("./Feed.cjs");
17
- const Pending = require("./Pending.cjs");
18
- const errors = require("./errors.cjs");
19
- const bindPublicMethods = require("./bindPublicMethods.cjs");
20
- const produceDraft = require("./produceDraft.cjs");
21
- const singleton = require("./singleton.cjs");
22
- exports.ViewModel = ViewModel.ViewModel;
23
- exports.Model = Model.Model;
24
- exports.Collection = Collection.Collection;
25
- exports.PersistentCollection = PersistentCollection.PersistentCollection;
26
- exports.Resource = Resource.Resource;
27
- exports.Controller = Controller.Controller;
28
- exports.Service = Service.Service;
29
- exports.EventBus = EventBus.EventBus;
30
- exports.Channel = Channel.Channel;
31
- exports.Trackable = Trackable.Trackable;
32
- exports.Sorting = Sorting.Sorting;
33
- exports.Pagination = Pagination.Pagination;
34
- exports.Selection = Selection.Selection;
35
- exports.Feed = Feed.Feed;
36
- exports.Pending = Pending.Pending;
37
- exports.HttpError = errors.HttpError;
38
- exports.classifyError = errors.classifyError;
39
- exports.isAbortError = errors.isAbortError;
40
- exports.bindPublicMethods = bindPublicMethods.bindPublicMethods;
41
- exports.produceDraft = produceDraft.produceDraft;
42
- exports.resolveDraftUpdater = produceDraft.resolveDraftUpdater;
43
- exports.hasSingleton = singleton.hasSingleton;
44
- exports.singleton = singleton.singleton;
45
- exports.teardown = singleton.teardown;
46
- exports.teardownAll = singleton.teardownAll;
47
- //# sourceMappingURL=mvc-kit.cjs.map
2
+ const require_bindPublicMethods = require("./bindPublicMethods.cjs");
3
+ const require_EventBus = require("./EventBus.cjs");
4
+ const require_errors = require("./errors.cjs");
5
+ const require_produceDraft = require("./produceDraft.cjs");
6
+ const require_ViewModel = require("./ViewModel.cjs");
7
+ const require_Model = require("./Model.cjs");
8
+ const require_Collection = require("./Collection.cjs");
9
+ const require_PersistentCollection = require("./PersistentCollection.cjs");
10
+ const require_Resource = require("./Resource.cjs");
11
+ const require_Controller = require("./Controller.cjs");
12
+ const require_Service = require("./Service.cjs");
13
+ const require_Channel = require("./Channel.cjs");
14
+ const require_Trackable = require("./Trackable.cjs");
15
+ const require_Sorting = require("./Sorting.cjs");
16
+ const require_Pagination = require("./Pagination.cjs");
17
+ const require_Selection = require("./Selection.cjs");
18
+ const require_Feed = require("./Feed.cjs");
19
+ const require_Pending = require("./Pending.cjs");
20
+ const require_singleton = require("./singleton.cjs");
21
+ exports.Channel = require_Channel.Channel;
22
+ exports.Collection = require_Collection.Collection;
23
+ exports.Controller = require_Controller.Controller;
24
+ exports.EventBus = require_EventBus.EventBus;
25
+ exports.Feed = require_Feed.Feed;
26
+ exports.HttpError = require_errors.HttpError;
27
+ exports.Model = require_Model.Model;
28
+ exports.Pagination = require_Pagination.Pagination;
29
+ exports.Pending = require_Pending.Pending;
30
+ exports.PersistentCollection = require_PersistentCollection.PersistentCollection;
31
+ exports.Resource = require_Resource.Resource;
32
+ exports.Selection = require_Selection.Selection;
33
+ exports.Service = require_Service.Service;
34
+ exports.Sorting = require_Sorting.Sorting;
35
+ exports.Trackable = require_Trackable.Trackable;
36
+ exports.ViewModel = require_ViewModel.ViewModel;
37
+ exports.bindPublicMethods = require_bindPublicMethods.bindPublicMethods;
38
+ exports.classifyError = require_errors.classifyError;
39
+ exports.hasSingleton = require_singleton.hasSingleton;
40
+ exports.isAbortError = require_errors.isAbortError;
41
+ exports.produceDraft = require_produceDraft.produceDraft;
42
+ exports.resolveDraftUpdater = require_produceDraft.resolveDraftUpdater;
43
+ exports.singleton = require_singleton.singleton;
44
+ exports.teardown = require_singleton.teardown;
45
+ exports.teardownAll = require_singleton.teardownAll;
package/dist/mvc-kit.js CHANGED
@@ -1,3 +1,7 @@
1
+ import { bindPublicMethods } from "./bindPublicMethods.js";
2
+ import { EventBus } from "./EventBus.js";
3
+ import { HttpError, classifyError, isAbortError } from "./errors.js";
4
+ import { produceDraft, resolveDraftUpdater } from "./produceDraft.js";
1
5
  import { ViewModel } from "./ViewModel.js";
2
6
  import { Model } from "./Model.js";
3
7
  import { Collection } from "./Collection.js";
@@ -5,7 +9,6 @@ import { PersistentCollection } from "./PersistentCollection.js";
5
9
  import { Resource } from "./Resource.js";
6
10
  import { Controller } from "./Controller.js";
7
11
  import { Service } from "./Service.js";
8
- import { EventBus } from "./EventBus.js";
9
12
  import { Channel } from "./Channel.js";
10
13
  import { Trackable } from "./Trackable.js";
11
14
  import { Sorting } from "./Sorting.js";
@@ -13,35 +16,5 @@ import { Pagination } from "./Pagination.js";
13
16
  import { Selection } from "./Selection.js";
14
17
  import { Feed } from "./Feed.js";
15
18
  import { Pending } from "./Pending.js";
16
- import { HttpError, classifyError, isAbortError } from "./errors.js";
17
- import { bindPublicMethods } from "./bindPublicMethods.js";
18
- import { produceDraft, resolveDraftUpdater } from "./produceDraft.js";
19
19
  import { hasSingleton, singleton, teardown, teardownAll } from "./singleton.js";
20
- export {
21
- Channel,
22
- Collection,
23
- Controller,
24
- EventBus,
25
- Feed,
26
- HttpError,
27
- Model,
28
- Pagination,
29
- Pending,
30
- PersistentCollection,
31
- Resource,
32
- Selection,
33
- Service,
34
- Sorting,
35
- Trackable,
36
- ViewModel,
37
- bindPublicMethods,
38
- classifyError,
39
- hasSingleton,
40
- isAbortError,
41
- produceDraft,
42
- resolveDraftUpdater,
43
- singleton,
44
- teardown,
45
- teardownAll
46
- };
47
- //# sourceMappingURL=mvc-kit.js.map
20
+ export { Channel, Collection, Controller, EventBus, Feed, HttpError, Model, Pagination, Pending, PersistentCollection, Resource, Selection, Service, Sorting, Trackable, ViewModel, bindPublicMethods, classifyError, hasSingleton, isAbortError, produceDraft, resolveDraftUpdater, singleton, teardown, teardownAll };
@@ -1,105 +1,115 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
1
+ //#region src/produceDraft.ts
2
+ var __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
3
+ /**
4
+ * Checks if a value is a plain object (POJO).
5
+ * Returns false for arrays, Dates, class instances, null, etc.
6
+ */
4
7
  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
+ if (value === null || typeof value !== "object") return false;
9
+ const proto = Object.getPrototypeOf(value);
10
+ return proto === Object.prototype || proto === null;
8
11
  }
9
12
  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
- };
13
+ let copy = null;
14
+ const children = /* @__PURE__ */ new Map();
15
+ function ensureCopy() {
16
+ if (!copy) copy = { ...original };
17
+ return copy;
18
+ }
19
+ return {
20
+ proxy: new Proxy({}, {
21
+ get(_, prop) {
22
+ if (typeof prop === "symbol") return original[prop];
23
+ const key = prop;
24
+ if (children.has(key)) return children.get(key).proxy;
25
+ const value = (copy ?? original)[key];
26
+ if (isPlainObject(value)) {
27
+ const child = createDraftNode(value);
28
+ children.set(key, child);
29
+ return child.proxy;
30
+ }
31
+ if (__DEV__ && Array.isArray(value)) return Object.freeze([...value]);
32
+ return value;
33
+ },
34
+ set(_, prop, value) {
35
+ if (typeof prop === "symbol") return true;
36
+ const key = prop;
37
+ if ((copy ?? original)[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)) return {
50
+ value: source[prop],
51
+ writable: true,
52
+ enumerable: true,
53
+ configurable: true
54
+ };
55
+ },
56
+ has(_, prop) {
57
+ return prop in (copy ?? original);
58
+ }
59
+ }),
60
+ changed() {
61
+ if (copy) return true;
62
+ for (const child of children.values()) if (child.changed()) return true;
63
+ return false;
64
+ },
65
+ finalize() {
66
+ for (const [key, child] of children) if (child.changed()) {
67
+ ensureCopy();
68
+ copy[key] = child.finalize();
69
+ }
70
+ return copy ?? original;
71
+ }
72
+ };
77
73
  }
74
+ /**
75
+ * Creates a copy-on-write draft proxy of the given state, runs the mutator,
76
+ * and returns only the changed top-level keys as a Partial.
77
+ * Returns null if nothing was modified.
78
+ *
79
+ * - Nested plain objects use copy-on-write structural sharing
80
+ * - Same-value assignments are no-ops
81
+ * - Reads reflect prior writes within the same draft
82
+ * - Only POJOs are proxied; class instances, arrays, Dates pass through as-is
83
+ * - Arrays must be replaced via assignment, not mutated in place
84
+ */
78
85
  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;
86
+ const root = createDraftNode(state);
87
+ mutator(root.proxy);
88
+ if (!root.changed()) return null;
89
+ const finalized = root.finalize();
90
+ const partial = {};
91
+ let hasChanges = false;
92
+ for (const key of Object.keys(finalized)) if (finalized[key] !== state[key]) {
93
+ partial[key] = finalized[key];
94
+ hasChanges = true;
95
+ }
96
+ return hasChanges ? partial : null;
92
97
  }
98
+ /**
99
+ * Resolves a function-form updater through produceDraft.
100
+ * Handles both patterns: explicit return (existing updater) and void return (draft mode).
101
+ * Returns the partial to apply, or null if nothing changed.
102
+ */
93
103
  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;
104
+ let explicitReturn;
105
+ const draftChanges = produceDraft(state, (draft) => {
106
+ const result = updater(draft);
107
+ if (result !== void 0 && result !== null && typeof result === "object") explicitReturn = result;
108
+ });
109
+ return explicitReturn ?? draftChanges;
102
110
  }
111
+ //#endregion
103
112
  exports.produceDraft = produceDraft;
104
113
  exports.resolveDraftUpdater = resolveDraftUpdater;
105
- //# sourceMappingURL=produceDraft.cjs.map
114
+
115
+ //# sourceMappingURL=produceDraft.cjs.map
@@ -1 +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":"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"}
1
+ {"version":3,"file":"produceDraft.cjs","names":[],"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"],"mappings":";AAAA,IAAM,UAAU,OAAO,oBAAoB,eAAe;;;;;AAM1D,SAAS,cAAc,OAAkD;AACvE,KAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;CACxD,MAAM,QAAQ,OAAO,eAAe,MAAM;AAC1C,QAAO,UAAU,OAAO,aAAa,UAAU;;AASjD,SAAS,gBAAkC,UAAqC;CAC9E,IAAI,OAAuC;CAC3C,MAAM,2BAAW,IAAI,KAAwB;CAE7C,SAAS,aAAsC;AAC7C,MAAI,CAAC,KAAM,QAAO,EAAE,GAAI,UAAsC;AAC9D,SAAO;;AAkET,QAAO;EACL,OA9DY,IAAI,MAAM,EAAE,EAAO;GAC/B,IAAI,GAAG,MAAM;AACX,QAAI,OAAO,SAAS,SAAU,QAAQ,SAAiB;IAEvD,MAAM,MAAM;AAGZ,QAAI,SAAS,IAAI,IAAI,CAAE,QAAO,SAAS,IAAI,IAAI,CAAE;IAIjD,MAAM,SADc,QAAQ,UACP;AAGrB,QAAI,cAAc,MAAM,EAAE;KACxB,MAAM,QAAQ,gBAAgB,MAAiC;AAC/D,cAAS,IAAI,KAAK,MAAM;AACxB,YAAO,MAAM;;AAKf,QAAI,WAAW,MAAM,QAAQ,MAAM,CACjC,QAAO,OAAO,OAAO,CAAC,GAAG,MAAM,CAAC;AAGlC,WAAO;;GAGT,IAAI,GAAG,MAAM,OAAO;AAClB,QAAI,OAAO,SAAS,SAAU,QAAO;IAErC,MAAM,MAAM;AAGZ,SAFoB,QAAQ,UAEjB,SAAS,OAAO;AACzB,iBAAY;AACZ,UAAM,OAAO;AAEb,cAAS,OAAO,IAAI;;AAEtB,WAAO;;GAGT,UAAU;AACR,WAAO,QAAQ,QAAS,QAAQ,SAAoB;;GAGtD,yBAAyB,GAAG,MAAM;IAChC,MAAM,SAAU,QAAQ;AACxB,QAAI,OAAO,UAAU,eAAe,KAAK,QAAQ,KAAK,CACpD,QAAO;KAAE,OAAO,OAAO;KAAiB,UAAU;KAAM,YAAY;KAAM,cAAc;KAAM;;GAKlG,IAAI,GAAG,MAAM;AACX,WAAO,SAAU,QAAQ;;GAE5B,CAAC;EAKA,UAAmB;AACjB,OAAI,KAAM,QAAO;AACjB,QAAK,MAAM,SAAS,SAAS,QAAQ,CACnC,KAAI,MAAM,SAAS,CAAE,QAAO;AAE9B,UAAO;;EAGT,WAAc;AAEZ,QAAK,MAAM,CAAC,KAAK,UAAU,SACzB,KAAI,MAAM,SAAS,EAAE;AACnB,gBAAY;AACZ,SAAM,OAAO,MAAM,UAAU;;AAGjC,UAAQ,QAAQ;;EAEnB;;;;;;;;;;;;;AAcH,SAAgB,aACd,OACA,SACmB;CACnB,MAAM,OAAO,gBAAgB,MAAM;AACnC,SAAQ,KAAK,MAAM;AAEnB,KAAI,CAAC,KAAK,SAAS,CAAE,QAAO;CAE5B,MAAM,YAAY,KAAK,UAAU;CAGjC,MAAM,UAAmC,EAAE;CAC3C,IAAI,aAAa;AAEjB,MAAK,MAAM,OAAO,OAAO,KAAK,UAAU,CACtC,KAAK,UAAkB,SAAU,MAAc,MAAM;AACnD,UAAQ,OAAQ,UAAkB;AAClC,eAAa;;AAIjB,QAAO,aAAc,UAAyB;;;;;;;AAQhD,SAAgB,oBACd,OACA,SACmB;CACnB,IAAI;CACJ,MAAM,eAAe,aAAgB,QAAQ,UAAU;EACrD,MAAM,SAAS,QAAQ,MAAM;AAC7B,MAAI,WAAW,KAAA,KAAa,WAAW,QAAQ,OAAO,WAAW,SAC/D,kBAAiB;GAEnB;AACF,QAAO,kBAAkB"}