cogsbox-shape 0.5.194 → 0.5.195
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/dist/schema.js
CHANGED
|
@@ -723,9 +723,10 @@ export function createSchema(schema, relations) {
|
|
|
723
723
|
});
|
|
724
724
|
}
|
|
725
725
|
let refinedClientInputSchema = finalClientInputSchema;
|
|
726
|
+
let refinedClientSchema = finalClientSchema;
|
|
726
727
|
if (refinements?.client) {
|
|
727
728
|
const clientRefine = refinements.client;
|
|
728
|
-
|
|
729
|
+
const refineFn = (data, ctx) => {
|
|
729
730
|
const result = clientRefine(data);
|
|
730
731
|
if (!result)
|
|
731
732
|
return;
|
|
@@ -737,6 +738,25 @@ export function createSchema(schema, relations) {
|
|
|
737
738
|
path: err.path,
|
|
738
739
|
});
|
|
739
740
|
}
|
|
741
|
+
};
|
|
742
|
+
refinedClientInputSchema = finalClientInputSchema.superRefine(refineFn);
|
|
743
|
+
refinedClientSchema = finalClientSchema.superRefine(refineFn);
|
|
744
|
+
}
|
|
745
|
+
let refinedSqlSchema = finalSqlSchema;
|
|
746
|
+
if (refinements?.server) {
|
|
747
|
+
const serverRefine = refinements.server;
|
|
748
|
+
refinedSqlSchema = finalSqlSchema.superRefine((data, ctx) => {
|
|
749
|
+
const result = serverRefine(data);
|
|
750
|
+
if (!result)
|
|
751
|
+
return;
|
|
752
|
+
const errors = Array.isArray(result) ? result : [result];
|
|
753
|
+
for (const err of errors) {
|
|
754
|
+
ctx.addIssue({
|
|
755
|
+
code: z.ZodIssueCode.custom,
|
|
756
|
+
message: err.message,
|
|
757
|
+
path: err.path,
|
|
758
|
+
});
|
|
759
|
+
}
|
|
740
760
|
});
|
|
741
761
|
}
|
|
742
762
|
return {
|
|
@@ -745,9 +765,9 @@ export function createSchema(schema, relations) {
|
|
|
745
765
|
deriveDependencies,
|
|
746
766
|
refineDependencies,
|
|
747
767
|
isClientRecord,
|
|
748
|
-
sqlSchema:
|
|
768
|
+
sqlSchema: refinedSqlSchema,
|
|
749
769
|
clientInputSchema: refinedClientInputSchema,
|
|
750
|
-
clientSchema:
|
|
770
|
+
clientSchema: refinedClientSchema,
|
|
751
771
|
serverSchema: refinedValidationSchema,
|
|
752
772
|
defaultValues: defaultValues,
|
|
753
773
|
stateType: {},
|
|
@@ -763,7 +783,7 @@ export function createSchema(schema, relations) {
|
|
|
763
783
|
return toDb(validPatch);
|
|
764
784
|
},
|
|
765
785
|
parseFromDb: (dbData) => {
|
|
766
|
-
const parsed =
|
|
786
|
+
const parsed = refinedSqlSchema.parse(dbData);
|
|
767
787
|
return toClient(parsed);
|
|
768
788
|
},
|
|
769
789
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
import { s, schema, createSchemaBox } from "../schema.js";
|
|
4
|
+
describe("refine runtime behavior", () => {
|
|
5
|
+
function makeRefinedBox() {
|
|
6
|
+
const rules = schema({
|
|
7
|
+
_tableName: "rules",
|
|
8
|
+
id: s.sqlite({ type: "int", pk: true }),
|
|
9
|
+
min: s.sqlite({ type: "int", nullable: true }).clientInput({ value: null, schema: z.number().nullable() }),
|
|
10
|
+
max: s.sqlite({ type: "int", nullable: true }).clientInput({ value: null, schema: z.number().nullable() }),
|
|
11
|
+
label: s.sqlite({ type: "varchar" }).clientInput({ value: "" }),
|
|
12
|
+
}).refine({
|
|
13
|
+
client: (row) => {
|
|
14
|
+
if (row.min !== null && row.max !== null && row.min >= row.max) {
|
|
15
|
+
return { path: ["max"], message: "Max must be > min" };
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
},
|
|
19
|
+
server: (row) => {
|
|
20
|
+
if (row.min !== null && row.max !== null && row.min >= row.max) {
|
|
21
|
+
return { path: ["max"], message: "Max must be > min" };
|
|
22
|
+
}
|
|
23
|
+
if (!row.label) {
|
|
24
|
+
return { path: ["label"], message: "Label required" };
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
return createSchemaBox({ rules }, { rules: {} });
|
|
30
|
+
}
|
|
31
|
+
it("schemas.clientInput catches client refine", () => {
|
|
32
|
+
const box = makeRefinedBox();
|
|
33
|
+
const good = box.rules.schemas.clientInput.safeParse({ id: 1, min: 1, max: 10, label: "x" });
|
|
34
|
+
expect(good.success).toBe(true);
|
|
35
|
+
const bad = box.rules.schemas.clientInput.safeParse({ id: 1, min: 10, max: 1, label: "x" });
|
|
36
|
+
expect(bad.success).toBe(false);
|
|
37
|
+
if (!bad.success) {
|
|
38
|
+
expect(bad.error.issues[0].message).toBe("Max must be > min");
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
it("schemas.client catches client refine", () => {
|
|
42
|
+
const box = makeRefinedBox();
|
|
43
|
+
const bad = box.rules.schemas.client.safeParse({ id: 1, min: 10, max: 1, label: "x" });
|
|
44
|
+
expect(bad.success).toBe(false);
|
|
45
|
+
if (!bad.success) {
|
|
46
|
+
expect(bad.error.issues[0].message).toBe("Max must be > min");
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
it("schemas.server catches server refine", () => {
|
|
50
|
+
const box = makeRefinedBox();
|
|
51
|
+
const bad = box.rules.schemas.server.safeParse({ id: 1, min: 1, max: 10, label: "" });
|
|
52
|
+
expect(bad.success).toBe(false);
|
|
53
|
+
if (!bad.success) {
|
|
54
|
+
expect(bad.error.issues[0].message).toBe("Label required");
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
it("schemas.sql catches server refine", () => {
|
|
58
|
+
const box = makeRefinedBox();
|
|
59
|
+
const bad = box.rules.schemas.sql.safeParse({ id: 1, min: 1, max: 10, label: "" });
|
|
60
|
+
expect(bad.success).toBe(false);
|
|
61
|
+
if (!bad.success) {
|
|
62
|
+
expect(bad.error.issues[0].message).toBe("Label required");
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
it("parseForDb rejects invalid data via server refine", () => {
|
|
66
|
+
const box = makeRefinedBox();
|
|
67
|
+
expect(() => box.rules.transforms.parseForDb({ id: 1, min: 10, max: 1, label: "" })).toThrow();
|
|
68
|
+
});
|
|
69
|
+
it("parseFromDb rejects invalid DB data via server refine", () => {
|
|
70
|
+
const box = makeRefinedBox();
|
|
71
|
+
expect(() => box.rules.transforms.parseFromDb({ id: 1, min: 1, max: 10, label: "" })).toThrow("Label required");
|
|
72
|
+
});
|
|
73
|
+
it("parsePatchForDb does NOT run refine (uses partial base)", () => {
|
|
74
|
+
const box = makeRefinedBox();
|
|
75
|
+
expect(() => box.rules.transforms.parsePatchForDb({ min: 10, max: 1 })).not.toThrow();
|
|
76
|
+
});
|
|
77
|
+
it("unrefined box has no refine on any schema", () => {
|
|
78
|
+
const rules = schema({
|
|
79
|
+
_tableName: "rules",
|
|
80
|
+
id: s.sqlite({ type: "int", pk: true }),
|
|
81
|
+
min: s.sqlite({ type: "int", nullable: true }).clientInput({ value: null, schema: z.number().nullable() }),
|
|
82
|
+
max: s.sqlite({ type: "int", nullable: true }).clientInput({ value: null, schema: z.number().nullable() }),
|
|
83
|
+
});
|
|
84
|
+
const box = createSchemaBox({ rules }, { rules: {} });
|
|
85
|
+
const good = box.rules.schemas.clientInput.safeParse({ id: 1, min: 10, max: 1 });
|
|
86
|
+
expect(good.success).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogsbox-shape",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.195",
|
|
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",
|