mvc-kit 2.12.4 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-config/bin/postinstall.mjs +4 -3
- package/agent-config/bin/setup.mjs +5 -1
- package/agent-config/claude-code/agents/mvc-kit-architect.md +11 -8
- package/agent-config/claude-code/skills/guide/SKILL.md +20 -7
- package/agent-config/claude-code/skills/guide/patterns.md +12 -0
- package/agent-config/claude-code/skills/guide/recipes.md +510 -0
- package/agent-config/claude-code/skills/guide/testing.md +297 -0
- package/agent-config/claude-code/skills/review/SKILL.md +3 -13
- package/agent-config/claude-code/skills/review/checklist.md +30 -5
- package/agent-config/claude-code/skills/scaffold/SKILL.md +4 -13
- package/agent-config/lib/install-claude.mjs +84 -25
- package/dist/Channel.cjs +276 -300
- package/dist/Channel.cjs.map +1 -1
- package/dist/Channel.js +275 -299
- package/dist/Channel.js.map +1 -1
- package/dist/Collection.cjs +424 -504
- package/dist/Collection.cjs.map +1 -1
- package/dist/Collection.js +423 -503
- package/dist/Collection.js.map +1 -1
- package/dist/Controller.cjs +70 -67
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.js +69 -66
- package/dist/Controller.js.map +1 -1
- package/dist/EventBus.cjs +77 -88
- package/dist/EventBus.cjs.map +1 -1
- package/dist/EventBus.js +76 -87
- package/dist/EventBus.js.map +1 -1
- package/dist/Feed.cjs +81 -77
- package/dist/Feed.cjs.map +1 -1
- package/dist/Feed.js +80 -76
- package/dist/Feed.js.map +1 -1
- package/dist/Model.cjs +181 -207
- package/dist/Model.cjs.map +1 -1
- package/dist/Model.js +179 -205
- package/dist/Model.js.map +1 -1
- package/dist/Pagination.cjs +75 -73
- package/dist/Pagination.cjs.map +1 -1
- package/dist/Pagination.js +74 -72
- package/dist/Pagination.js.map +1 -1
- package/dist/Pending.cjs +255 -287
- package/dist/Pending.cjs.map +1 -1
- package/dist/Pending.js +253 -285
- package/dist/Pending.js.map +1 -1
- package/dist/PersistentCollection.cjs +242 -285
- package/dist/PersistentCollection.cjs.map +1 -1
- package/dist/PersistentCollection.js +241 -284
- package/dist/PersistentCollection.js.map +1 -1
- package/dist/Resource.cjs +166 -174
- package/dist/Resource.cjs.map +1 -1
- package/dist/Resource.js +164 -172
- package/dist/Resource.js.map +1 -1
- package/dist/Selection.cjs +84 -94
- package/dist/Selection.cjs.map +1 -1
- package/dist/Selection.js +83 -93
- package/dist/Selection.js.map +1 -1
- package/dist/Service.cjs +54 -55
- package/dist/Service.cjs.map +1 -1
- package/dist/Service.js +53 -54
- package/dist/Service.js.map +1 -1
- package/dist/Sorting.cjs +102 -101
- package/dist/Sorting.cjs.map +1 -1
- package/dist/Sorting.js +102 -101
- package/dist/Sorting.js.map +1 -1
- package/dist/Trackable.cjs +112 -80
- package/dist/Trackable.cjs.map +1 -1
- package/dist/Trackable.js +111 -79
- package/dist/Trackable.js.map +1 -1
- package/dist/ViewModel.cjs +528 -576
- package/dist/ViewModel.cjs.map +1 -1
- package/dist/ViewModel.js +525 -573
- package/dist/ViewModel.js.map +1 -1
- package/dist/bindPublicMethods.cjs +43 -24
- package/dist/bindPublicMethods.cjs.map +1 -1
- package/dist/bindPublicMethods.js +43 -24
- package/dist/bindPublicMethods.js.map +1 -1
- package/dist/errors.cjs +67 -68
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js +68 -71
- package/dist/errors.js.map +1 -1
- package/dist/mvc-kit.cjs +44 -46
- package/dist/mvc-kit.js +5 -32
- package/dist/produceDraft.cjs +105 -95
- package/dist/produceDraft.cjs.map +1 -1
- package/dist/produceDraft.js +106 -97
- package/dist/produceDraft.js.map +1 -1
- package/dist/react/components/CardList.cjs +30 -40
- package/dist/react/components/CardList.cjs.map +1 -1
- package/dist/react/components/CardList.js +31 -41
- package/dist/react/components/CardList.js.map +1 -1
- package/dist/react/components/DataTable.cjs +146 -169
- package/dist/react/components/DataTable.cjs.map +1 -1
- package/dist/react/components/DataTable.js +147 -170
- package/dist/react/components/DataTable.js.map +1 -1
- package/dist/react/components/InfiniteScroll.cjs +51 -42
- package/dist/react/components/InfiniteScroll.cjs.map +1 -1
- package/dist/react/components/InfiniteScroll.js +52 -43
- package/dist/react/components/InfiniteScroll.js.map +1 -1
- package/dist/react/components/types.cjs +10 -6
- package/dist/react/components/types.cjs.map +1 -1
- package/dist/react/components/types.js +11 -9
- package/dist/react/components/types.js.map +1 -1
- package/dist/react/guards.cjs +10 -6
- package/dist/react/guards.cjs.map +1 -1
- package/dist/react/guards.js +11 -9
- package/dist/react/guards.js.map +1 -1
- package/dist/react/provider.cjs +23 -20
- package/dist/react/provider.cjs.map +1 -1
- package/dist/react/provider.js +23 -21
- package/dist/react/provider.js.map +1 -1
- package/dist/react/use-event-bus.cjs +24 -20
- package/dist/react/use-event-bus.cjs.map +1 -1
- package/dist/react/use-event-bus.js +24 -21
- package/dist/react/use-event-bus.js.map +1 -1
- package/dist/react/use-instance.cjs +43 -36
- package/dist/react/use-instance.cjs.map +1 -1
- package/dist/react/use-instance.js +43 -36
- package/dist/react/use-instance.js.map +1 -1
- package/dist/react/use-local.cjs +48 -64
- package/dist/react/use-local.cjs.map +1 -1
- package/dist/react/use-local.js +47 -63
- package/dist/react/use-local.js.map +1 -1
- package/dist/react/use-model.cjs +84 -98
- package/dist/react/use-model.cjs.map +1 -1
- package/dist/react/use-model.js +84 -100
- package/dist/react/use-model.js.map +1 -1
- package/dist/react/use-singleton.cjs +19 -23
- package/dist/react/use-singleton.cjs.map +1 -1
- package/dist/react/use-singleton.js +16 -20
- package/dist/react/use-singleton.js.map +1 -1
- package/dist/react/use-subscribe-only.cjs +28 -22
- package/dist/react/use-subscribe-only.cjs.map +1 -1
- package/dist/react/use-subscribe-only.js +28 -22
- package/dist/react/use-subscribe-only.js.map +1 -1
- package/dist/react/use-teardown.cjs +20 -19
- package/dist/react/use-teardown.cjs.map +1 -1
- package/dist/react/use-teardown.js +20 -19
- package/dist/react/use-teardown.js.map +1 -1
- package/dist/react-native/NativeCollection.cjs +98 -78
- package/dist/react-native/NativeCollection.cjs.map +1 -1
- package/dist/react-native/NativeCollection.js +97 -77
- package/dist/react-native/NativeCollection.js.map +1 -1
- package/dist/react-native.cjs +2 -4
- package/dist/react-native.js +1 -4
- package/dist/react.cjs +24 -26
- package/dist/react.js +1 -17
- package/dist/singleton.cjs +28 -22
- package/dist/singleton.cjs.map +1 -1
- package/dist/singleton.js +29 -26
- package/dist/singleton.js.map +1 -1
- package/dist/walkPrototypeChain.cjs +20 -12
- package/dist/walkPrototypeChain.cjs.map +1 -1
- package/dist/walkPrototypeChain.js +21 -13
- package/dist/walkPrototypeChain.js.map +1 -1
- package/dist/web/IndexedDBCollection.cjs +53 -36
- package/dist/web/IndexedDBCollection.cjs.map +1 -1
- package/dist/web/IndexedDBCollection.js +52 -35
- package/dist/web/IndexedDBCollection.js.map +1 -1
- package/dist/web/WebStorageCollection.cjs +82 -84
- package/dist/web/WebStorageCollection.cjs.map +1 -1
- package/dist/web/WebStorageCollection.js +81 -83
- package/dist/web/WebStorageCollection.js.map +1 -1
- package/dist/web/idb.cjs +107 -99
- package/dist/web/idb.cjs.map +1 -1
- package/dist/web/idb.js +108 -105
- package/dist/web/idb.js.map +1 -1
- package/dist/web.cjs +4 -6
- package/dist/web.js +1 -5
- package/dist/wrapAsyncMethods.cjs +141 -168
- package/dist/wrapAsyncMethods.cjs.map +1 -1
- package/dist/wrapAsyncMethods.js +141 -168
- package/dist/wrapAsyncMethods.js.map +1 -1
- package/package.json +8 -8
- package/src/Pending.test.ts +1 -2
- package/src/Sorting.test.ts +1 -1
- package/src/produceDraft.test.ts +3 -3
- package/src/react/components/CardList.test.tsx +1 -1
- package/src/react/components/DataTable.test.tsx +1 -1
- package/src/react/components/InfiniteScroll.test.tsx +5 -5
- package/dist/mvc-kit.cjs.map +0 -1
- package/dist/mvc-kit.js.map +0 -1
- package/dist/react-native.cjs.map +0 -1
- package/dist/react-native.js.map +0 -1
- package/dist/react.cjs.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/web.cjs.map +0 -1
- package/dist/web.js.map +0 -1
package/dist/errors.js
CHANGED
|
@@ -1,79 +1,76 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
17
|
+
return error instanceof Error && error.name === "AbortError";
|
|
10
18
|
}
|
|
11
19
|
function classifyHttpStatus(status) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
};
|
|
79
|
-
//# sourceMappingURL=errors.js.map
|
|
73
|
+
//#endregion
|
|
74
|
+
export { HttpError, classifyError, isAbortError };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -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"],"
|
|
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
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
exports.
|
|
23
|
-
exports.
|
|
24
|
-
exports.
|
|
25
|
-
exports.
|
|
26
|
-
exports.
|
|
27
|
-
exports.
|
|
28
|
-
exports.
|
|
29
|
-
exports.
|
|
30
|
-
exports.
|
|
31
|
-
exports.
|
|
32
|
-
exports.
|
|
33
|
-
exports.
|
|
34
|
-
exports.
|
|
35
|
-
exports.
|
|
36
|
-
exports.
|
|
37
|
-
exports.
|
|
38
|
-
exports.
|
|
39
|
-
exports.
|
|
40
|
-
exports.
|
|
41
|
-
exports.
|
|
42
|
-
exports.
|
|
43
|
-
exports.
|
|
44
|
-
exports.singleton =
|
|
45
|
-
exports.teardown =
|
|
46
|
-
exports.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 };
|
package/dist/produceDraft.cjs
CHANGED
|
@@ -1,105 +1,115 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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"],"
|
|
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"}
|