cogsbox-shape 0.5.213 → 0.5.214
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.
|
@@ -47,10 +47,27 @@ type FormUpdateParams = {
|
|
|
47
47
|
}>) => void;
|
|
48
48
|
clearZodErrors?: (paths: string[][]) => void;
|
|
49
49
|
};
|
|
50
|
+
type UpdateParams = {
|
|
51
|
+
stateKey: string;
|
|
52
|
+
update: {
|
|
53
|
+
path: string[];
|
|
54
|
+
updateType: string;
|
|
55
|
+
oldValue: unknown;
|
|
56
|
+
newValue: unknown;
|
|
57
|
+
};
|
|
58
|
+
getState: () => unknown;
|
|
59
|
+
addZodErrors: (errors: Array<{
|
|
60
|
+
path: string[];
|
|
61
|
+
message: string;
|
|
62
|
+
code?: string;
|
|
63
|
+
}>) => void;
|
|
64
|
+
clearZodErrors?: (paths: string[][]) => void;
|
|
65
|
+
};
|
|
50
66
|
export declare function wireShapeValidationOptions(box: ShapeSchemaBox, params: TransformStateParams): void;
|
|
51
67
|
/** Cross-field refine errors only — field rules are handled by state via setOptions. */
|
|
52
68
|
export declare function validateShapeRefines(box: ShapeSchemaBox, params: FormUpdateParams): void;
|
|
69
|
+
export declare function validateShapeRefinesOnUpdate(box: ShapeSchemaBox, params: UpdateParams): void;
|
|
53
70
|
export declare function createShapePlugin<const TBox extends ShapeSchemaBox>(box: TBox): import("cogsbox-state").CogsPluginBuilder<"shape", {
|
|
54
71
|
logs: boolean | undefined;
|
|
55
|
-
}, unknown, unknown, never, {}, true,
|
|
72
|
+
}, unknown, unknown, never, {}, true, true, true, false, false, true, InferShapeBoxState<TBox>>;
|
|
56
73
|
export {};
|
|
@@ -126,6 +126,71 @@ function issueMatchesRelatedFields(issue, relatedFields) {
|
|
|
126
126
|
const leaf = String(issue.path.at(-1) ?? "");
|
|
127
127
|
return relatedFields.has(leaf);
|
|
128
128
|
}
|
|
129
|
+
function getChangedObjectFields(oldValue, newValue) {
|
|
130
|
+
if (oldValue === null ||
|
|
131
|
+
newValue === null ||
|
|
132
|
+
typeof oldValue !== "object" ||
|
|
133
|
+
typeof newValue !== "object" ||
|
|
134
|
+
Array.isArray(oldValue) ||
|
|
135
|
+
Array.isArray(newValue)) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const fields = new Set();
|
|
139
|
+
const oldRecord = oldValue;
|
|
140
|
+
const newRecord = newValue;
|
|
141
|
+
for (const key of new Set([...Object.keys(oldRecord), ...Object.keys(newRecord)])) {
|
|
142
|
+
if (!Object.is(oldRecord[key], newRecord[key]))
|
|
143
|
+
fields.add(key);
|
|
144
|
+
}
|
|
145
|
+
return fields;
|
|
146
|
+
}
|
|
147
|
+
function resolveUpdateRefineTarget(entry, updatePath, oldValue, newValue) {
|
|
148
|
+
const changedObjectFields = getChangedObjectFields(oldValue, newValue);
|
|
149
|
+
if (changedObjectFields) {
|
|
150
|
+
const parentPath = updatePath;
|
|
151
|
+
const relatedFields = new Set();
|
|
152
|
+
for (const field of changedObjectFields) {
|
|
153
|
+
const groupFields = getRelatedFields(entry, field);
|
|
154
|
+
if (!groupFields)
|
|
155
|
+
continue;
|
|
156
|
+
for (const related of groupFields)
|
|
157
|
+
relatedFields.add(related);
|
|
158
|
+
}
|
|
159
|
+
if (relatedFields.size === 0)
|
|
160
|
+
return null;
|
|
161
|
+
return {
|
|
162
|
+
relatedFields,
|
|
163
|
+
relatedPaths: [...relatedFields].map((field) => [...parentPath, field]),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const field = updatePath.at(-1);
|
|
167
|
+
if (!field)
|
|
168
|
+
return null;
|
|
169
|
+
const relatedFields = getRelatedFields(entry, field);
|
|
170
|
+
if (!relatedFields)
|
|
171
|
+
return null;
|
|
172
|
+
return {
|
|
173
|
+
relatedFields,
|
|
174
|
+
relatedPaths: resolveRelatedPaths(updatePath, relatedFields),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function applyRefineValidation(box, params, target, state) {
|
|
178
|
+
const entry = box[params.stateKey];
|
|
179
|
+
const clientSchema = entry?.validators?.client ?? entry?.schemas.client;
|
|
180
|
+
if (!entry || !clientSchema)
|
|
181
|
+
return;
|
|
182
|
+
const result = clientSchema.safeParse(state);
|
|
183
|
+
if (result.success) {
|
|
184
|
+
clearValidationPaths(params, target.relatedPaths);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const issues = result.error.issues.filter((issue) => issueMatchesRelatedFields(issue, target.relatedFields));
|
|
188
|
+
const mapped = mapZodIssues(issues);
|
|
189
|
+
const activeKeys = new Set(mapped.map((entry) => pathKey(entry.path)));
|
|
190
|
+
const stalePaths = target.relatedPaths.filter((targetPath) => !activeKeys.has(pathKey(targetPath)));
|
|
191
|
+
clearValidationPaths(params, stalePaths);
|
|
192
|
+
addValidationIssues(params, mapped);
|
|
193
|
+
}
|
|
129
194
|
export function wireShapeValidationOptions(box, params) {
|
|
130
195
|
const entry = box[params.stateKey];
|
|
131
196
|
if (!entry)
|
|
@@ -144,8 +209,7 @@ export function validateShapeRefines(box, params) {
|
|
|
144
209
|
return;
|
|
145
210
|
}
|
|
146
211
|
const entry = box[params.stateKey];
|
|
147
|
-
|
|
148
|
-
if (!entry || !clientSchema)
|
|
212
|
+
if (!entry)
|
|
149
213
|
return;
|
|
150
214
|
const field = params.path.at(-1);
|
|
151
215
|
if (!field)
|
|
@@ -153,18 +217,21 @@ export function validateShapeRefines(box, params) {
|
|
|
153
217
|
const relatedFields = getRelatedFields(entry, field);
|
|
154
218
|
if (!relatedFields)
|
|
155
219
|
return;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
220
|
+
applyRefineValidation(box, params, {
|
|
221
|
+
relatedFields,
|
|
222
|
+
relatedPaths: resolveRelatedPaths(params.path, relatedFields),
|
|
223
|
+
}, getStateForValidation(params));
|
|
224
|
+
}
|
|
225
|
+
export function validateShapeRefinesOnUpdate(box, params) {
|
|
226
|
+
if (params.update.updateType !== "update")
|
|
160
227
|
return;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
228
|
+
const entry = box[params.stateKey];
|
|
229
|
+
if (!entry)
|
|
230
|
+
return;
|
|
231
|
+
const target = resolveUpdateRefineTarget(entry, params.update.path, params.update.oldValue, params.update.newValue);
|
|
232
|
+
if (!target)
|
|
233
|
+
return;
|
|
234
|
+
applyRefineValidation(box, params, target, params.getState());
|
|
168
235
|
}
|
|
169
236
|
function buildInitialState(box) {
|
|
170
237
|
const state = {};
|
|
@@ -193,5 +260,8 @@ export function createShapePlugin(box) {
|
|
|
193
260
|
console.log("[shape]", params.stateKey, params.path, params.event.activityType);
|
|
194
261
|
}
|
|
195
262
|
validateShapeRefines(box, params);
|
|
263
|
+
})
|
|
264
|
+
.onUpdate((params) => {
|
|
265
|
+
validateShapeRefinesOnUpdate(box, params);
|
|
196
266
|
});
|
|
197
267
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogsbox-shape",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.214",
|
|
4
4
|
"description": "A TypeScript library for creating type-safe database schemas with Zod validation, SQL type definitions, and automatic client/server transformations. Unifies client, server, and database types through a single schema definition, with built-in support for relationships and serialization.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|