cogsbox-shape 0.5.214 → 0.5.215
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
export { createShapePlugin, validateShapeRefines, wireShapeValidationOptions, type InferShapeBoxState, type ShapeRefineInfo, type ShapeSchemaBox, type ShapeSchemaBoxEntry, } from "./plugin.js";
|
|
1
|
+
export { createShapePlugin, validateShapeKeys, validateShapeRefines, wireShapeValidationOptions, type InferShapeBoxState, type ShapeRefineInfo, type ShapeSchemaBox, type ShapeSchemaBoxEntry, } from "./plugin.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { createShapePlugin, validateShapeRefines, wireShapeValidationOptions, } from "./plugin.js";
|
|
1
|
+
export { createShapePlugin, validateShapeKeys, validateShapeRefines, wireShapeValidationOptions, } from "./plugin.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ChainMethodContext } from "cogsbox-state";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
/** Minimal shape of a createSchemaBox entry — matches journalSchemaBox.journalTechnical etc. */
|
|
3
4
|
export type ShapeRefineInfo = {
|
|
@@ -63,11 +64,36 @@ type UpdateParams = {
|
|
|
63
64
|
}>) => void;
|
|
64
65
|
clearZodErrors?: (paths: string[][]) => void;
|
|
65
66
|
};
|
|
67
|
+
type ShapeKeyValidationParams = {
|
|
68
|
+
stateKey: string;
|
|
69
|
+
path: string[];
|
|
70
|
+
keys?: readonly string[];
|
|
71
|
+
getState?: () => unknown;
|
|
72
|
+
};
|
|
66
73
|
export declare function wireShapeValidationOptions(box: ShapeSchemaBox, params: TransformStateParams): void;
|
|
67
74
|
/** Cross-field refine errors only — field rules are handled by state via setOptions. */
|
|
68
75
|
export declare function validateShapeRefines(box: ShapeSchemaBox, params: FormUpdateParams): void;
|
|
69
76
|
export declare function validateShapeRefinesOnUpdate(box: ShapeSchemaBox, params: UpdateParams): void;
|
|
77
|
+
export declare function validateShapeKeys(box: ShapeSchemaBox, params: ShapeKeyValidationParams): {
|
|
78
|
+
success: boolean;
|
|
79
|
+
results: {
|
|
80
|
+
key: string;
|
|
81
|
+
path: string[];
|
|
82
|
+
success: boolean;
|
|
83
|
+
data: any;
|
|
84
|
+
}[];
|
|
85
|
+
};
|
|
70
86
|
export declare function createShapePlugin<const TBox extends ShapeSchemaBox>(box: TBox): import("cogsbox-state").CogsPluginBuilder<"shape", {
|
|
71
87
|
logs: boolean | undefined;
|
|
72
|
-
}, unknown, unknown, never, {
|
|
88
|
+
}, unknown, unknown, never, {
|
|
89
|
+
validateShape: import("cogsbox-state").ChainMethodDefinition<(ctx: ChainMethodContext, keys?: readonly string[]) => {
|
|
90
|
+
success: boolean;
|
|
91
|
+
results: {
|
|
92
|
+
key: string;
|
|
93
|
+
path: string[];
|
|
94
|
+
success: boolean;
|
|
95
|
+
data: any;
|
|
96
|
+
}[];
|
|
97
|
+
}>;
|
|
98
|
+
}, true, true, true, true, false, true, InferShapeBoxState<TBox>>;
|
|
73
99
|
export {};
|
|
@@ -108,6 +108,41 @@ function addValidationIssues(params, issues) {
|
|
|
108
108
|
params.addZodErrors(issues);
|
|
109
109
|
notifyValidationPaths(params.stateKey, issues.map((issue) => issue.path));
|
|
110
110
|
}
|
|
111
|
+
function setValidationIssues(stateKey, issues) {
|
|
112
|
+
if (issues.length === 0)
|
|
113
|
+
return;
|
|
114
|
+
const store = getGlobalStore.getState();
|
|
115
|
+
for (const issue of issues) {
|
|
116
|
+
const currentMeta = store.getShadowMetadata(stateKey, issue.path) || {};
|
|
117
|
+
store.setShadowMetadata(stateKey, issue.path, {
|
|
118
|
+
...currentMeta,
|
|
119
|
+
validation: {
|
|
120
|
+
status: "INVALID",
|
|
121
|
+
errors: [
|
|
122
|
+
{
|
|
123
|
+
source: "client",
|
|
124
|
+
message: issue.message,
|
|
125
|
+
severity: "error",
|
|
126
|
+
code: issue.code,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
lastValidated: Date.now(),
|
|
130
|
+
validatedValue: store.getShadowValue(stateKey, issue.path),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
notifyValidationPaths(stateKey, issues.map((issue) => issue.path));
|
|
135
|
+
}
|
|
136
|
+
function issueMatchesSelectedKeys(issue, parentPath, selectedKeys) {
|
|
137
|
+
const issuePath = issue.path.map(String);
|
|
138
|
+
if (issuePath.length <= parentPath.length)
|
|
139
|
+
return false;
|
|
140
|
+
for (let index = 0; index < parentPath.length; index++) {
|
|
141
|
+
if (issuePath[index] !== parentPath[index])
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return selectedKeys.has(issuePath[parentPath.length]);
|
|
145
|
+
}
|
|
111
146
|
function getRelatedFields(entry, field) {
|
|
112
147
|
const groupIndexes = entry.refineInfo?.fieldToGroup[field];
|
|
113
148
|
if (!groupIndexes?.length)
|
|
@@ -138,7 +173,10 @@ function getChangedObjectFields(oldValue, newValue) {
|
|
|
138
173
|
const fields = new Set();
|
|
139
174
|
const oldRecord = oldValue;
|
|
140
175
|
const newRecord = newValue;
|
|
141
|
-
for (const key of new Set([
|
|
176
|
+
for (const key of new Set([
|
|
177
|
+
...Object.keys(oldRecord),
|
|
178
|
+
...Object.keys(newRecord),
|
|
179
|
+
])) {
|
|
142
180
|
if (!Object.is(oldRecord[key], newRecord[key]))
|
|
143
181
|
fields.add(key);
|
|
144
182
|
}
|
|
@@ -233,6 +271,64 @@ export function validateShapeRefinesOnUpdate(box, params) {
|
|
|
233
271
|
return;
|
|
234
272
|
applyRefineValidation(box, params, target, params.getState());
|
|
235
273
|
}
|
|
274
|
+
export function validateShapeKeys(box, params) {
|
|
275
|
+
const entry = box[params.stateKey];
|
|
276
|
+
const clientSchema = entry?.validators?.client ?? entry?.schemas.client;
|
|
277
|
+
if (!entry || !clientSchema)
|
|
278
|
+
return { success: true, results: [] };
|
|
279
|
+
const store = getGlobalStore.getState();
|
|
280
|
+
const rootState = params.getState?.() ?? store.getShadowValue(params.stateKey, []);
|
|
281
|
+
const result = clientSchema.safeParse(rootState);
|
|
282
|
+
const selectedKeys = params.keys ? new Set(params.keys) : null;
|
|
283
|
+
const targetPaths = params.keys?.map((key) => [...params.path, key]) ??
|
|
284
|
+
(result.success
|
|
285
|
+
? []
|
|
286
|
+
: mapZodIssues(result.error.issues).map((issue) => issue.path));
|
|
287
|
+
if (result.success) {
|
|
288
|
+
clearValidationPaths({
|
|
289
|
+
stateKey: params.stateKey,
|
|
290
|
+
getState: () => rootState,
|
|
291
|
+
addZodErrors: () => { },
|
|
292
|
+
}, targetPaths);
|
|
293
|
+
return {
|
|
294
|
+
success: true,
|
|
295
|
+
results: params.keys?.map((key) => ({
|
|
296
|
+
key,
|
|
297
|
+
path: [...params.path, key],
|
|
298
|
+
success: true,
|
|
299
|
+
data: store.getShadowValue(params.stateKey, [...params.path, key]),
|
|
300
|
+
})) ?? [],
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const issues = selectedKeys
|
|
304
|
+
? result.error.issues.filter((issue) => issueMatchesSelectedKeys(issue, params.path, selectedKeys))
|
|
305
|
+
: result.error.issues;
|
|
306
|
+
const mapped = mapZodIssues(issues);
|
|
307
|
+
const activeKeys = new Set(mapped.map((issue) => pathKey(issue.path)));
|
|
308
|
+
const stalePaths = targetPaths.filter((targetPath) => !activeKeys.has(pathKey(targetPath)));
|
|
309
|
+
clearValidationPaths({
|
|
310
|
+
stateKey: params.stateKey,
|
|
311
|
+
getState: () => rootState,
|
|
312
|
+
addZodErrors: () => { },
|
|
313
|
+
}, stalePaths);
|
|
314
|
+
setValidationIssues(params.stateKey, mapped);
|
|
315
|
+
return {
|
|
316
|
+
success: mapped.length === 0,
|
|
317
|
+
results: params.keys?.map((key) => {
|
|
318
|
+
const keyPath = [...params.path, key];
|
|
319
|
+
const keyIssues = mapped.filter((issue) => issue.path[params.path.length] === key);
|
|
320
|
+
return {
|
|
321
|
+
key,
|
|
322
|
+
path: keyPath,
|
|
323
|
+
success: keyIssues.length === 0,
|
|
324
|
+
data: keyIssues.length === 0
|
|
325
|
+
? store.getShadowValue(params.stateKey, keyPath)
|
|
326
|
+
: undefined,
|
|
327
|
+
error: keyIssues.length === 0 ? undefined : { issues: keyIssues },
|
|
328
|
+
};
|
|
329
|
+
}) ?? [],
|
|
330
|
+
};
|
|
331
|
+
}
|
|
236
332
|
function buildInitialState(box) {
|
|
237
333
|
const state = {};
|
|
238
334
|
for (const key of Object.keys(box)) {
|
|
@@ -263,5 +359,13 @@ export function createShapePlugin(box) {
|
|
|
263
359
|
})
|
|
264
360
|
.onUpdate((params) => {
|
|
265
361
|
validateShapeRefinesOnUpdate(box, params);
|
|
266
|
-
})
|
|
362
|
+
})
|
|
363
|
+
.methods((m) => ({
|
|
364
|
+
validateShape: m.object((ctx, keys) => validateShapeKeys(box, {
|
|
365
|
+
stateKey: ctx.stateKey,
|
|
366
|
+
path: ctx.path,
|
|
367
|
+
keys,
|
|
368
|
+
getState: ctx.$get,
|
|
369
|
+
})),
|
|
370
|
+
}));
|
|
267
371
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogsbox-shape",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.215",
|
|
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",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"author": "",
|
|
66
66
|
"license": "MIT",
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"cogsbox-state": "^0.5.
|
|
68
|
+
"cogsbox-state": "^0.5.488",
|
|
69
69
|
"commander": "^13.1.0",
|
|
70
70
|
"kysely": "^0.29.2",
|
|
71
71
|
"tsx": "^4.19.3",
|