cogsbox-shape 0.5.218 → 0.5.220
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.
|
@@ -68,7 +68,12 @@ type ShapeKeyValidationParams = {
|
|
|
68
68
|
path: string[];
|
|
69
69
|
keys?: readonly string[];
|
|
70
70
|
getState?: () => unknown;
|
|
71
|
+
/** When true (default), writes filtered issues to shadow validation metadata. */
|
|
72
|
+
persist?: boolean;
|
|
73
|
+
/** When true, clears existing errors on sibling fields outside `keys`. */
|
|
74
|
+
clearOutsideKeys?: boolean;
|
|
71
75
|
};
|
|
76
|
+
export type ValidateGroupOptions = Pick<ShapeKeyValidationParams, "persist" | "clearOutsideKeys">;
|
|
72
77
|
export declare function wireShapeValidationOptions(box: ShapeSchemaBox, params: TransformStateParams): void;
|
|
73
78
|
/** Cross-field refine errors only — field rules are handled by state via setOptions. */
|
|
74
79
|
export declare function validateShapeRefines(box: ShapeSchemaBox, params: FormUpdateParams): void;
|
|
@@ -117,6 +117,102 @@ function addValidationIssues(params, issues) {
|
|
|
117
117
|
params.addZodErrors(issues);
|
|
118
118
|
notifyValidationPaths(params.stateKey, issues.map((issue) => issue.path));
|
|
119
119
|
}
|
|
120
|
+
function createShadowValidationBridge(stateKey) {
|
|
121
|
+
const store = getGlobalStore.getState();
|
|
122
|
+
return {
|
|
123
|
+
stateKey,
|
|
124
|
+
getState: () => store.getShadowValue(stateKey, []),
|
|
125
|
+
addZodErrors: (issues) => {
|
|
126
|
+
for (const error of issues) {
|
|
127
|
+
const errorPath = error.path;
|
|
128
|
+
const currentMeta = store.getShadowMetadata(stateKey, errorPath) || {};
|
|
129
|
+
store.setShadowMetadata(stateKey, errorPath, {
|
|
130
|
+
...currentMeta,
|
|
131
|
+
validation: {
|
|
132
|
+
status: "INVALID",
|
|
133
|
+
errors: [
|
|
134
|
+
{
|
|
135
|
+
source: "client",
|
|
136
|
+
message: error.message,
|
|
137
|
+
severity: "error",
|
|
138
|
+
code: error.code,
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
lastValidated: Date.now(),
|
|
142
|
+
validatedValue: undefined,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
clearZodErrors: (paths) => {
|
|
148
|
+
for (const path of paths) {
|
|
149
|
+
const currentMeta = store.getShadowMetadata(stateKey, path) || {};
|
|
150
|
+
store.setShadowMetadata(stateKey, path, {
|
|
151
|
+
...currentMeta,
|
|
152
|
+
validation: {
|
|
153
|
+
status: "NOT_VALIDATED",
|
|
154
|
+
errors: [],
|
|
155
|
+
lastValidated: Date.now(),
|
|
156
|
+
validatedValue: undefined,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function notifyStateComponents(stateKey) {
|
|
164
|
+
const store = getGlobalStore.getState();
|
|
165
|
+
const stateEntry = store.getShadowMetadata(stateKey, []);
|
|
166
|
+
if (!stateEntry?.components)
|
|
167
|
+
return;
|
|
168
|
+
const updates = new Set();
|
|
169
|
+
stateEntry.components.forEach((component) => {
|
|
170
|
+
const reactiveTypes = component
|
|
171
|
+
? Array.isArray(component.reactiveType)
|
|
172
|
+
? component.reactiveType
|
|
173
|
+
: [component.reactiveType || "component"]
|
|
174
|
+
: null;
|
|
175
|
+
if (!reactiveTypes?.includes("none")) {
|
|
176
|
+
updates.add(() => component.forceUpdate());
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
queueMicrotask(() => {
|
|
180
|
+
updates.forEach((update) => update());
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function persistValidateGroupResults(params, keys, mapped) {
|
|
184
|
+
const validationParams = createShadowValidationBridge(params.stateKey);
|
|
185
|
+
if (params.clearOutsideKeys === true) {
|
|
186
|
+
clearOutsideGroupValidation(params, keys, validationParams);
|
|
187
|
+
}
|
|
188
|
+
const keyPaths = keys.map((key) => [...params.path, key]);
|
|
189
|
+
const activeKeys = new Set(mapped.map((issue) => pathKey(issue.path)));
|
|
190
|
+
const stalePaths = keyPaths.filter((targetPath) => !activeKeys.has(pathKey(targetPath)));
|
|
191
|
+
clearValidationPaths(validationParams, stalePaths);
|
|
192
|
+
if (mapped.length > 0) {
|
|
193
|
+
addValidationIssues(validationParams, mapped);
|
|
194
|
+
}
|
|
195
|
+
notifyStateComponents(params.stateKey);
|
|
196
|
+
}
|
|
197
|
+
function clearOutsideGroupValidation(params, keys, validationParams) {
|
|
198
|
+
const store = getGlobalStore.getState();
|
|
199
|
+
const parentNode = store.getShadowNode(params.stateKey, params.path);
|
|
200
|
+
if (!parentNode)
|
|
201
|
+
return;
|
|
202
|
+
const keySet = new Set(keys);
|
|
203
|
+
const outsidePaths = [];
|
|
204
|
+
for (const childKey of Object.keys(parentNode)) {
|
|
205
|
+
if (childKey === "_meta" || keySet.has(childKey))
|
|
206
|
+
continue;
|
|
207
|
+
const fieldPath = [...params.path, childKey];
|
|
208
|
+
const status = store.getShadowMetadata(params.stateKey, fieldPath)
|
|
209
|
+
?.validation?.status;
|
|
210
|
+
if (status === "INVALID") {
|
|
211
|
+
outsidePaths.push(fieldPath);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
clearValidationPaths(validationParams, outsidePaths);
|
|
215
|
+
}
|
|
120
216
|
function issueMatchesSelectedKeys(issue, parentPath, selectedKeys) {
|
|
121
217
|
const issuePath = issue.path.map(String);
|
|
122
218
|
if (issuePath.length <= parentPath.length)
|
|
@@ -196,6 +292,22 @@ function resolveUpdateRefineTarget(entry, updatePath, oldValue, newValue) {
|
|
|
196
292
|
relatedPaths: resolveRelatedPaths(updatePath, relatedFields),
|
|
197
293
|
};
|
|
198
294
|
}
|
|
295
|
+
function clearStaleRefineValidation(box, params, target, state) {
|
|
296
|
+
const entry = box[params.stateKey];
|
|
297
|
+
const clientSchema = entry?.validators?.client ?? entry?.schemas.client;
|
|
298
|
+
if (!entry || !clientSchema)
|
|
299
|
+
return;
|
|
300
|
+
const result = clientSchema.safeParse(state);
|
|
301
|
+
if (result.success) {
|
|
302
|
+
clearValidationPaths(params, target.relatedPaths);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const issues = result.error.issues.filter((issue) => issueMatchesRelatedFields(issue, target.relatedFields));
|
|
306
|
+
const mapped = mapZodIssues(issues);
|
|
307
|
+
const activeKeys = new Set(mapped.map((entry) => pathKey(entry.path)));
|
|
308
|
+
const stalePaths = target.relatedPaths.filter((targetPath) => !activeKeys.has(pathKey(targetPath)));
|
|
309
|
+
clearValidationPaths(params, stalePaths);
|
|
310
|
+
}
|
|
199
311
|
function applyRefineValidation(box, params, target, state) {
|
|
200
312
|
const entry = box[params.stateKey];
|
|
201
313
|
const clientSchema = entry?.validators?.client ?? entry?.schemas.client;
|
|
@@ -253,7 +365,7 @@ export function validateShapeRefinesOnUpdate(box, params) {
|
|
|
253
365
|
const target = resolveUpdateRefineTarget(entry, params.update.path, params.update.oldValue, params.update.newValue);
|
|
254
366
|
if (!target)
|
|
255
367
|
return;
|
|
256
|
-
|
|
368
|
+
clearStaleRefineValidation(box, params, target, params.getState());
|
|
257
369
|
}
|
|
258
370
|
export function validateShapeKeys(box, params) {
|
|
259
371
|
const entry = box[params.stateKey];
|
|
@@ -263,7 +375,11 @@ export function validateShapeKeys(box, params) {
|
|
|
263
375
|
const rootState = params.getState?.() ?? getGlobalStore.getState().getShadowValue(params.stateKey, []);
|
|
264
376
|
const result = clientSchema.safeParse(rootState);
|
|
265
377
|
const selectedKeys = params.keys ? new Set(params.keys) : null;
|
|
378
|
+
const shouldPersist = params.persist !== false && !!params.keys?.length;
|
|
266
379
|
if (result.success) {
|
|
380
|
+
if (shouldPersist) {
|
|
381
|
+
persistValidateGroupResults(params, params.keys, []);
|
|
382
|
+
}
|
|
267
383
|
return {
|
|
268
384
|
success: true,
|
|
269
385
|
results: params.keys?.map((key) => ({
|
|
@@ -278,6 +394,9 @@ export function validateShapeKeys(box, params) {
|
|
|
278
394
|
? result.error.issues.filter((issue) => issueMatchesSelectedKeys(issue, params.path, selectedKeys))
|
|
279
395
|
: result.error.issues;
|
|
280
396
|
const mapped = mapZodIssues(issues);
|
|
397
|
+
if (shouldPersist) {
|
|
398
|
+
persistValidateGroupResults(params, params.keys, mapped);
|
|
399
|
+
}
|
|
281
400
|
return {
|
|
282
401
|
success: mapped.length === 0,
|
|
283
402
|
results: params.keys?.map((key) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogsbox-shape",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.220",
|
|
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",
|