cogsbox-shape 0.5.207 → 0.5.209
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/README.md +31 -5
- package/cogsbox-shape-db/dist/table-db.js +3 -2
- package/dist/generateSQL.js +6 -0
- package/dist/schema.d.ts +36 -2
- package/dist/schema.js +27 -4
- package/dist/vitest/fullSchema.test.js +14 -3
- package/dist/vitest/generateSQL.test.js +28 -0
- package/dist/vitest/refineRuntime.test.js +16 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -307,6 +307,28 @@ schema({ ... })
|
|
|
307
307
|
|
|
308
308
|
**Note**: `parsePatchForDb` uses the base schema (without refinement) since partial data may not satisfy cross-field rules.
|
|
309
309
|
|
|
310
|
+
### Schemas vs Validators
|
|
311
|
+
|
|
312
|
+
Each box entry exposes two sets of Zod schemas:
|
|
313
|
+
|
|
314
|
+
- **`schemas`** — plain `ZodObject` shapes. Always composable with `.pick()`, `.omit()`, `.partial()`, etc. Use these for form field extraction, type inference, and partial validation.
|
|
315
|
+
- **`validators`** — schema + refinements. These are `ZodEffects` when `.refine()` is used, otherwise the same `ZodObject`. Use these for full validation that enforces cross-field rules.
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
const box = createSchemaBox({ events }, { events: {} });
|
|
319
|
+
|
|
320
|
+
// Base schema — always a ZodObject, always composable
|
|
321
|
+
box.events.schemas.client.pick({ startDate: true, endDate: true }); // works!
|
|
322
|
+
|
|
323
|
+
// Validator — enforces refine rules
|
|
324
|
+
box.events.validators.client.safeParse(data); // runs cross-field checks
|
|
325
|
+
|
|
326
|
+
// Internal transforms use validators automatically
|
|
327
|
+
box.events.transforms.parseForDb(data); // uses validator.server
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Why the split? After `.refine()`, Zod wraps the schema in `ZodEffects`, which loses `.shape`, `.pick()`, `.omit()`, and `.partial()`. By keeping the base schema separate from refinements, you can always compose the shape while still enforcing cross-field rules when needed.
|
|
331
|
+
|
|
310
332
|
### Schema Object Structure
|
|
311
333
|
|
|
312
334
|
The returned schema object has a clear separation of concerns:
|
|
@@ -314,16 +336,17 @@ The returned schema object has a clear separation of concerns:
|
|
|
314
336
|
```typescript
|
|
315
337
|
const schema = createSchema(mySchema);
|
|
316
338
|
|
|
317
|
-
schema.schemas; // { sql, client, clientChecked, server } —
|
|
339
|
+
schema.schemas; // { sql, client, clientChecked, server } — ZodObject shapes (composable)
|
|
340
|
+
schema.validators; // { sql, client, clientChecked, server } — with refinements enforced
|
|
318
341
|
schema.transforms; // { toClient, toDb, parseForDb, parseFromDb } — transformations
|
|
319
342
|
schema.defaults; // Default values for forms
|
|
320
|
-
schema.generateDefaults; // Function to generate fresh client defaults (executes randomizers)
|
|
343
|
+
schema.generateDefaults; // Function to generate fresh client defaults (executes randomizers)
|
|
321
344
|
schema.pk; // Primary key field names
|
|
322
345
|
schema.clientPk; // Client-side primary key field names
|
|
323
346
|
schema.isClientRecord; // Function to check if a record is client-created
|
|
324
347
|
schema.deriveDependencies; // Derive function dependencies ({ [field]: string[] })
|
|
325
348
|
schema.refineInfo; // Refinement info ({ groups: RefineEntry[], fieldToGroup: Record<string, number[]> })
|
|
326
|
-
```
|
|
349
|
+
```
|
|
327
350
|
|
|
328
351
|
## Using Schemas
|
|
329
352
|
|
|
@@ -364,8 +387,11 @@ const { toClient, toDb, parseForDb, parseFromDb } = schema.transforms;
|
|
|
364
387
|
const [data, setData] = useState(generateDefaults());
|
|
365
388
|
// { id: "new_a1b2c3d4", name: "", email: "", isActive: true }
|
|
366
389
|
|
|
367
|
-
// Validate explicitly
|
|
368
|
-
const result = server.safeParse(data);
|
|
390
|
+
// Validate explicitly (use validators for refinement enforcement)
|
|
391
|
+
const result = schema.validators.server.safeParse(data);
|
|
392
|
+
|
|
393
|
+
// Or use the base schema for shape operations
|
|
394
|
+
const pickedSchema = schema.schemas.server.pick({ email: true, age: true });
|
|
369
395
|
|
|
370
396
|
// Or handle validation & transformation in a single step!
|
|
371
397
|
const safeDbRow = parseForDb(data);
|
|
@@ -35,8 +35,9 @@ export class TableDB {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
if (opts?.limit !== undefined) {
|
|
39
|
+
query = query.limit(opts.limit);
|
|
40
|
+
}
|
|
40
41
|
if (opts?.offset !== undefined) {
|
|
41
42
|
query = query.offset(opts.offset);
|
|
42
43
|
}
|
package/dist/generateSQL.js
CHANGED
|
@@ -27,6 +27,8 @@ function sqlType(dialect, fieldName, tableName, config) {
|
|
|
27
27
|
switch (config.type) {
|
|
28
28
|
case "int":
|
|
29
29
|
return "INTEGER";
|
|
30
|
+
case "real":
|
|
31
|
+
return "REAL";
|
|
30
32
|
case "boolean":
|
|
31
33
|
return "INTEGER";
|
|
32
34
|
case "varchar":
|
|
@@ -45,6 +47,8 @@ function sqlType(dialect, fieldName, tableName, config) {
|
|
|
45
47
|
switch (config.type) {
|
|
46
48
|
case "int":
|
|
47
49
|
return "INTEGER";
|
|
50
|
+
case "real":
|
|
51
|
+
return "REAL";
|
|
48
52
|
case "boolean":
|
|
49
53
|
return "BOOLEAN";
|
|
50
54
|
case "varchar":
|
|
@@ -70,6 +74,8 @@ function sqlType(dialect, fieldName, tableName, config) {
|
|
|
70
74
|
switch (config.type) {
|
|
71
75
|
case "int":
|
|
72
76
|
return "INTEGER";
|
|
77
|
+
case "real":
|
|
78
|
+
return "DOUBLE";
|
|
73
79
|
case "boolean":
|
|
74
80
|
return "TINYINT(1)";
|
|
75
81
|
case "varchar":
|
package/dist/schema.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ type SQLTypeConfig = ({
|
|
|
14
14
|
type: "int";
|
|
15
15
|
nullable?: boolean;
|
|
16
16
|
default?: number;
|
|
17
|
+
} | {
|
|
18
|
+
type: "real";
|
|
19
|
+
nullable?: boolean;
|
|
20
|
+
default?: number;
|
|
17
21
|
} | {
|
|
18
22
|
type: "boolean";
|
|
19
23
|
nullable?: boolean;
|
|
@@ -54,11 +58,11 @@ type BaseConfig = {
|
|
|
54
58
|
};
|
|
55
59
|
type SQLToZodType<T extends SQLTypeInput, TDefault extends boolean> = T["pk"] extends true ? TDefault extends true ? z.ZodString : z.ZodNumber : T["nullable"] extends true ? T["type"] extends "varchar" | "char" | "text" | "longtext" ? z.ZodNullable<z.ZodString> : T["type"] extends "enum" ? T extends {
|
|
56
60
|
values: infer TValues extends readonly [string, ...string[]];
|
|
57
|
-
} ? z.ZodNullable<z.ZodType<TValues[number]>> : never : T["type"] extends "int" ? z.ZodNullable<z.ZodNumber> : T["type"] extends "boolean" ? z.ZodNullable<z.ZodNumber> : T["type"] extends "date" | "datetime" | "timestamp" ? T extends {
|
|
61
|
+
} ? z.ZodNullable<z.ZodType<TValues[number]>> : never : T["type"] extends "int" | "real" ? z.ZodNullable<z.ZodNumber> : T["type"] extends "boolean" ? z.ZodNullable<z.ZodNumber> : T["type"] extends "date" | "datetime" | "timestamp" ? T extends {
|
|
58
62
|
default: "CURRENT_TIMESTAMP";
|
|
59
63
|
} ? TDefault extends true ? never : z.ZodNullable<z.ZodDate> : z.ZodNullable<z.ZodDate> : never : T["type"] extends "varchar" | "char" | "text" | "longtext" ? z.ZodString : T["type"] extends "enum" ? T extends {
|
|
60
64
|
values: infer TValues extends readonly [string, ...string[]];
|
|
61
|
-
} ? z.ZodType<TValues[number]> : never : T["type"] extends "int" ? z.ZodNumber : T["type"] extends "boolean" ? z.ZodNumber : T["type"] extends "date" | "datetime" | "timestamp" ? T extends {
|
|
65
|
+
} ? z.ZodType<TValues[number]> : never : T["type"] extends "int" | "real" ? z.ZodNumber : T["type"] extends "boolean" ? z.ZodNumber : T["type"] extends "date" | "datetime" | "timestamp" ? T extends {
|
|
62
66
|
default: "CURRENT_TIMESTAMP";
|
|
63
67
|
} ? TDefault extends true ? never : z.ZodDate : z.ZodDate : never;
|
|
64
68
|
type ZodTypeFromPrimitive<T> = T extends string ? z.ZodString : T extends number ? z.ZodNumber : T extends boolean ? z.ZodBoolean : T extends Date ? z.ZodDate : z.ZodAny;
|
|
@@ -336,6 +340,12 @@ export declare function createSchema<T extends {
|
|
|
336
340
|
clientSchema: z.ZodObject<Prettify<DeriveSchemaByKey<TActualSchema, "zodClientSchema">>>;
|
|
337
341
|
clientCheckedSchema: z.ZodObject<Prettify<DeriveSchemaByKey<TActualSchema, "zodClientCheckedSchema">>>;
|
|
338
342
|
serverSchema: z.ZodObject<Prettify<DeriveSchemaByKey<TActualSchema, "zodValidationSchema">>>;
|
|
343
|
+
validators: {
|
|
344
|
+
sql: z.ZodTypeAny;
|
|
345
|
+
client: z.ZodTypeAny;
|
|
346
|
+
clientChecked: z.ZodTypeAny;
|
|
347
|
+
server: z.ZodTypeAny;
|
|
348
|
+
};
|
|
339
349
|
defaultValues: Prettify<DeriveDefaults<TActualSchema>>;
|
|
340
350
|
stateType: Prettify<DeriveStateType<TActualSchema>>;
|
|
341
351
|
generateDefaults: () => Prettify<DeriveDefaults<TActualSchema>>;
|
|
@@ -400,6 +410,12 @@ type ResolvedRegistryWithSchemas<S extends Record<string, SchemaWithPlaceholders
|
|
|
400
410
|
clientSchema: z.ZodObject<Prettify<DeriveSchemaByKey<ResolveSchema<S[K], K extends keyof R ? (R[K] extends object ? R[K] : {}) : {}>, "zodClientSchema">>>;
|
|
401
411
|
clientCheckedSchema: z.ZodObject<Prettify<DeriveSchemaByKey<ResolveSchema<S[K], K extends keyof R ? (R[K] extends object ? R[K] : {}) : {}>, "zodClientCheckedSchema">>>;
|
|
402
412
|
serverSchema: z.ZodObject<Prettify<DeriveSchemaByKey<ResolveSchema<S[K], K extends keyof R ? (R[K] extends object ? R[K] : {}) : {}>, "zodValidationSchema">>>;
|
|
413
|
+
validators: {
|
|
414
|
+
sql: z.ZodTypeAny;
|
|
415
|
+
client: z.ZodTypeAny;
|
|
416
|
+
clientChecked: z.ZodTypeAny;
|
|
417
|
+
server: z.ZodTypeAny;
|
|
418
|
+
};
|
|
403
419
|
defaultValues: Prettify<DeriveDefaults<ResolveSchema<S[K], K extends keyof R ? (R[K] extends object ? R[K] : {}) : {}>>>;
|
|
404
420
|
stateType: Prettify<DeriveStateType<ResolveSchema<S[K], K extends keyof R ? (R[K] extends object ? R[K] : {}) : {}>>>;
|
|
405
421
|
};
|
|
@@ -511,6 +527,12 @@ export type DeriveViewResult<TTableName extends keyof TRegistry, TSelection, TRe
|
|
|
511
527
|
clientChecked: z.ZodObject<_DeriveViewShape<TTableName, TSelection, TRegistry, "clientCheckedSchema">>;
|
|
512
528
|
server: z.ZodObject<_DeriveViewShape<TTableName, TSelection, TRegistry, "serverSchema">>;
|
|
513
529
|
};
|
|
530
|
+
validators: {
|
|
531
|
+
sql: TRegistry[TTableName]["zodSchemas"]["validators"]["sql"];
|
|
532
|
+
client: TRegistry[TTableName]["zodSchemas"]["validators"]["client"];
|
|
533
|
+
clientChecked: TRegistry[TTableName]["zodSchemas"]["validators"]["clientChecked"];
|
|
534
|
+
server: TRegistry[TTableName]["zodSchemas"]["validators"]["server"];
|
|
535
|
+
};
|
|
514
536
|
transforms: {
|
|
515
537
|
toClient: TRegistry[TTableName]["transforms"]["toClient"];
|
|
516
538
|
toDb: TRegistry[TTableName]["transforms"]["toDb"];
|
|
@@ -560,6 +582,12 @@ type RegistryShape = Record<string, {
|
|
|
560
582
|
clientSchema: z.ZodObject<any>;
|
|
561
583
|
clientCheckedSchema: z.ZodObject<any>;
|
|
562
584
|
serverSchema: z.ZodObject<any>;
|
|
585
|
+
validators: {
|
|
586
|
+
sql: z.ZodTypeAny;
|
|
587
|
+
client: z.ZodTypeAny;
|
|
588
|
+
clientChecked: z.ZodTypeAny;
|
|
589
|
+
server: z.ZodTypeAny;
|
|
590
|
+
};
|
|
563
591
|
defaultValues: any;
|
|
564
592
|
stateType: any;
|
|
565
593
|
deriveDependencies: Record<string, string[]>;
|
|
@@ -595,6 +623,12 @@ type CreateSchemaBoxReturn<S extends Record<string, SchemaWithPlaceholders>, R e
|
|
|
595
623
|
clientChecked: Resolved[K]["zodSchemas"]["clientCheckedSchema"];
|
|
596
624
|
server: Resolved[K]["zodSchemas"]["serverSchema"];
|
|
597
625
|
};
|
|
626
|
+
validators: {
|
|
627
|
+
sql: Resolved[K]["zodSchemas"]["validators"]["sql"];
|
|
628
|
+
client: Resolved[K]["zodSchemas"]["validators"]["client"];
|
|
629
|
+
clientChecked: Resolved[K]["zodSchemas"]["validators"]["clientChecked"];
|
|
630
|
+
server: Resolved[K]["zodSchemas"]["validators"]["server"];
|
|
631
|
+
};
|
|
598
632
|
transforms: {
|
|
599
633
|
toClient: (dbData: z.infer<Resolved[K]["zodSchemas"]["sqlSchema"]>) => z.infer<Resolved[K]["zodSchemas"]["clientSchema"]>;
|
|
600
634
|
toDb: (clientData: z.infer<Resolved[K]["zodSchemas"]["clientSchema"]>) => z.infer<Resolved[K]["zodSchemas"]["sqlSchema"]>;
|
package/dist/schema.js
CHANGED
|
@@ -18,6 +18,9 @@ function createSqlBuilder(dialect, sqlConfig) {
|
|
|
18
18
|
case "int":
|
|
19
19
|
baseType = z.number();
|
|
20
20
|
break;
|
|
21
|
+
case "real":
|
|
22
|
+
baseType = z.number();
|
|
23
|
+
break;
|
|
21
24
|
case "boolean":
|
|
22
25
|
baseType = z.number();
|
|
23
26
|
break;
|
|
@@ -467,6 +470,8 @@ function inferDefaultFromZod(zodType, sqlConfig) {
|
|
|
467
470
|
return sqlTypeConfig.default ?? sqlTypeConfig.values[0];
|
|
468
471
|
case "int":
|
|
469
472
|
return 0;
|
|
473
|
+
case "real":
|
|
474
|
+
return 0;
|
|
470
475
|
case "boolean":
|
|
471
476
|
return false;
|
|
472
477
|
case "date":
|
|
@@ -787,10 +792,16 @@ export function createSchema(schema, relations) {
|
|
|
787
792
|
deriveDependencies,
|
|
788
793
|
refineInfo: { groups: refineGroups ?? [], fieldToGroup },
|
|
789
794
|
isClientRecord,
|
|
790
|
-
sqlSchema:
|
|
791
|
-
clientSchema:
|
|
792
|
-
clientCheckedSchema:
|
|
793
|
-
serverSchema:
|
|
795
|
+
sqlSchema: finalSqlSchema,
|
|
796
|
+
clientSchema: finalClientSchema,
|
|
797
|
+
clientCheckedSchema: finalClientCheckedSchema,
|
|
798
|
+
serverSchema: finalValidationSchema,
|
|
799
|
+
validators: {
|
|
800
|
+
sql: refinedSqlSchema,
|
|
801
|
+
client: refinedClientSchema,
|
|
802
|
+
clientChecked: refinedClientCheckedSchema,
|
|
803
|
+
server: refinedValidationSchema,
|
|
804
|
+
},
|
|
794
805
|
defaultValues: defaultValues,
|
|
795
806
|
stateType: {},
|
|
796
807
|
generateDefaults,
|
|
@@ -1028,6 +1039,12 @@ export function createSchemaBox(schemas, resolutions) {
|
|
|
1028
1039
|
clientChecked: entry.zodSchemas.clientCheckedSchema,
|
|
1029
1040
|
server: entry.zodSchemas.serverSchema,
|
|
1030
1041
|
},
|
|
1042
|
+
validators: {
|
|
1043
|
+
sql: entry.zodSchemas.validators.sql,
|
|
1044
|
+
client: entry.zodSchemas.validators.client,
|
|
1045
|
+
clientChecked: entry.zodSchemas.validators.clientChecked,
|
|
1046
|
+
server: entry.zodSchemas.validators.server,
|
|
1047
|
+
},
|
|
1031
1048
|
transforms: {
|
|
1032
1049
|
toClient: entry.transforms.toClient,
|
|
1033
1050
|
toDb: entry.transforms.toDb,
|
|
@@ -1172,6 +1189,12 @@ export function createSchemaBox(schemas, resolutions) {
|
|
|
1172
1189
|
clientChecked: view.clientChecked,
|
|
1173
1190
|
server: view.server,
|
|
1174
1191
|
},
|
|
1192
|
+
validators: {
|
|
1193
|
+
sql: view.sql,
|
|
1194
|
+
client: view.client,
|
|
1195
|
+
clientChecked: view.clientChecked,
|
|
1196
|
+
server: view.server,
|
|
1197
|
+
},
|
|
1175
1198
|
transforms: {
|
|
1176
1199
|
toClient: viewToClient,
|
|
1177
1200
|
toDb: viewToDb,
|
|
@@ -31,6 +31,17 @@ describe("Schema Builder Type Tests (with expect-type)", () => {
|
|
|
31
31
|
expect(statusField.config.zodSqlSchema.parse("draft")).toBe("draft");
|
|
32
32
|
expect(() => statusField.config.zodSqlSchema.parse("deleted")).toThrow();
|
|
33
33
|
});
|
|
34
|
+
it("should correctly type a real (float) field", () => {
|
|
35
|
+
const tempField = s.sqlite({ type: "real" });
|
|
36
|
+
expectTypeOf(tempField.config.zodSqlSchema).toEqualTypeOf();
|
|
37
|
+
expect(tempField.config.zodSqlSchema.parse(3.14)).toBe(3.14);
|
|
38
|
+
});
|
|
39
|
+
it("should correctly type a nullable real field", () => {
|
|
40
|
+
const nullableReal = s.sqlite({ type: "real", nullable: true });
|
|
41
|
+
expectTypeOf(nullableReal.config.zodClientCheckedSchema).toEqualTypeOf();
|
|
42
|
+
expect(nullableReal.config.zodSqlSchema.parse(null)).toBeNull();
|
|
43
|
+
expect(nullableReal.config.zodSqlSchema.parse(2.5)).toBe(2.5);
|
|
44
|
+
});
|
|
34
45
|
});
|
|
35
46
|
describe("Chainable Methods", () => {
|
|
36
47
|
it("should create a union type when .client provides a different type", () => {
|
|
@@ -368,7 +379,7 @@ describe("client vs clientChecked schema divergence after .clientCheck()", () =>
|
|
|
368
379
|
id: 1, password: "abc", confirmPassword: "def",
|
|
369
380
|
});
|
|
370
381
|
expect(clientResult.success).toBe(true);
|
|
371
|
-
const clientCheckedResult = box.forms.
|
|
382
|
+
const clientCheckedResult = box.forms.validators.clientChecked.safeParse({
|
|
372
383
|
id: 1, password: "abc", confirmPassword: "def",
|
|
373
384
|
});
|
|
374
385
|
expect(clientCheckedResult.success).toBe(false);
|
|
@@ -1204,7 +1215,7 @@ describe("refine", () => {
|
|
|
1204
1215
|
}),
|
|
1205
1216
|
]);
|
|
1206
1217
|
const box = createSchemaBox({ forms }, { forms: {} });
|
|
1207
|
-
const result = box.forms.
|
|
1218
|
+
const result = box.forms.validators.client.safeParse({
|
|
1208
1219
|
id: 1,
|
|
1209
1220
|
password: "secret",
|
|
1210
1221
|
confirmPassword: "different",
|
|
@@ -1213,7 +1224,7 @@ describe("refine", () => {
|
|
|
1213
1224
|
if (!result.success) {
|
|
1214
1225
|
expect(result.error.issues[0].message).toBe("Passwords must match");
|
|
1215
1226
|
}
|
|
1216
|
-
const validResult = box.forms.
|
|
1227
|
+
const validResult = box.forms.validators.client.safeParse({
|
|
1217
1228
|
id: 1,
|
|
1218
1229
|
password: "secret",
|
|
1219
1230
|
confirmPassword: "secret",
|
|
@@ -67,4 +67,32 @@ describe("generateSQL dialect columns", () => {
|
|
|
67
67
|
});
|
|
68
68
|
await expect(withOutputFile((path) => generateSQL({ posts }, path))).rejects.toThrow(/Mixed SQL dialects/);
|
|
69
69
|
});
|
|
70
|
+
it("generates REAL column for SQLite real type", async () => {
|
|
71
|
+
const measurements = schema({
|
|
72
|
+
_tableName: "measurements",
|
|
73
|
+
id: s.sqlite({ type: "int", pk: true }),
|
|
74
|
+
temperature: s.sqlite({ type: "real" }),
|
|
75
|
+
humidity: s.sqlite({ type: "real", nullable: true }),
|
|
76
|
+
});
|
|
77
|
+
const sql = await withOutputFile((path) => generateSQL({ measurements }, path));
|
|
78
|
+
expect(sql).toContain("id INTEGER PRIMARY KEY");
|
|
79
|
+
expect(sql).toContain("temperature REAL NOT NULL");
|
|
80
|
+
expect(sql).toContain("humidity REAL");
|
|
81
|
+
});
|
|
82
|
+
it("generates REAL for Postgres and DOUBLE for MySQL real type", async () => {
|
|
83
|
+
const pgData = schema({
|
|
84
|
+
_tableName: "readings",
|
|
85
|
+
id: s.postgres({ type: "int", pk: true }),
|
|
86
|
+
value: s.postgres({ type: "real" }),
|
|
87
|
+
});
|
|
88
|
+
const pgSql = await withOutputFile((path) => generateSQL({ pgData }, path));
|
|
89
|
+
expect(pgSql).toContain("value REAL NOT NULL");
|
|
90
|
+
const myData = schema({
|
|
91
|
+
_tableName: "readings",
|
|
92
|
+
id: s.mysql({ type: "int", pk: true }),
|
|
93
|
+
value: s.mysql({ type: "real" }),
|
|
94
|
+
});
|
|
95
|
+
const mySql = await withOutputFile((path) => generateSQL({ myData }, path));
|
|
96
|
+
expect(mySql).toContain("value DOUBLE NOT NULL");
|
|
97
|
+
});
|
|
70
98
|
});
|
|
@@ -32,16 +32,16 @@ describe("refine runtime behavior", () => {
|
|
|
32
32
|
]);
|
|
33
33
|
return createSchemaBox({ rules }, { rules: {} });
|
|
34
34
|
}
|
|
35
|
-
it("
|
|
35
|
+
it("validators.client catches client refine", () => {
|
|
36
36
|
const box = makeRefinedBox();
|
|
37
|
-
const good = box.rules.
|
|
37
|
+
const good = box.rules.validators.client.safeParse({
|
|
38
38
|
id: 1,
|
|
39
39
|
min: 1,
|
|
40
40
|
max: 10,
|
|
41
41
|
label: "x",
|
|
42
42
|
});
|
|
43
43
|
expect(good.success).toBe(true);
|
|
44
|
-
const bad = box.rules.
|
|
44
|
+
const bad = box.rules.validators.client.safeParse({
|
|
45
45
|
id: 1,
|
|
46
46
|
min: 10,
|
|
47
47
|
max: 1,
|
|
@@ -52,9 +52,9 @@ describe("refine runtime behavior", () => {
|
|
|
52
52
|
expect(bad.error.issues[0].message).toBe("Max must be > min");
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
|
-
it("
|
|
55
|
+
it("validators.clientChecked catches client refine", () => {
|
|
56
56
|
const box = makeRefinedBox();
|
|
57
|
-
const bad = box.rules.
|
|
57
|
+
const bad = box.rules.validators.clientChecked.safeParse({
|
|
58
58
|
id: 1,
|
|
59
59
|
min: 10,
|
|
60
60
|
max: 1,
|
|
@@ -65,9 +65,9 @@ describe("refine runtime behavior", () => {
|
|
|
65
65
|
expect(bad.error.issues[0].message).toBe("Max must be > min");
|
|
66
66
|
}
|
|
67
67
|
});
|
|
68
|
-
it("
|
|
68
|
+
it("validators.server catches server refine", () => {
|
|
69
69
|
const box = makeRefinedBox();
|
|
70
|
-
const bad = box.rules.
|
|
70
|
+
const bad = box.rules.validators.server.safeParse({
|
|
71
71
|
id: 1,
|
|
72
72
|
min: 1,
|
|
73
73
|
max: 10,
|
|
@@ -78,9 +78,9 @@ describe("refine runtime behavior", () => {
|
|
|
78
78
|
expect(bad.error.issues[0].message).toBe("Label required");
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
|
-
it("
|
|
81
|
+
it("validators.sql catches server refine", () => {
|
|
82
82
|
const box = makeRefinedBox();
|
|
83
|
-
const bad = box.rules.
|
|
83
|
+
const bad = box.rules.validators.sql.safeParse({
|
|
84
84
|
id: 1,
|
|
85
85
|
min: 1,
|
|
86
86
|
max: 10,
|
|
@@ -122,36 +122,18 @@ describe("refine runtime behavior", () => {
|
|
|
122
122
|
});
|
|
123
123
|
expect(good.success).toBe(true);
|
|
124
124
|
});
|
|
125
|
-
it("
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
id: s.sqlite({ type: "int", pk: true }),
|
|
129
|
-
min: s
|
|
130
|
-
.sqlite({ type: "int", field: "minField" })
|
|
131
|
-
.client({ value: 0 }),
|
|
132
|
-
max: s.sqlite({ type: "int" }).client({ value: 0 }),
|
|
133
|
-
}).refine((r) => [
|
|
134
|
-
r("clientCheck", (row) => row.min >= row.max
|
|
135
|
-
? { path: ["max"], message: "Max must be > min" }
|
|
136
|
-
: undefined, ["min", "max"]),
|
|
137
|
-
]);
|
|
138
|
-
const journal = schema({
|
|
139
|
-
_tableName: "journal",
|
|
140
|
-
id: s.sqlite({ type: "int", pk: true }),
|
|
141
|
-
rules: s.hasOne(true),
|
|
142
|
-
});
|
|
143
|
-
const box = createSchemaBox({ rules, journal }, {
|
|
144
|
-
journal: { rules: { fromKey: "id", toKey: rules.id } },
|
|
145
|
-
});
|
|
146
|
-
const view = box.journal.createView({ rules: true });
|
|
147
|
-
const bad = view.schemas.clientChecked.safeParse({
|
|
125
|
+
it("validators enforce refine on direct table schemas", () => {
|
|
126
|
+
const box = makeRefinedBox();
|
|
127
|
+
const bad = box.rules.validators.clientChecked.safeParse({
|
|
148
128
|
id: 1,
|
|
149
|
-
|
|
129
|
+
min: 10,
|
|
130
|
+
max: 1,
|
|
131
|
+
label: "x",
|
|
150
132
|
});
|
|
151
133
|
expect(bad.success).toBe(false);
|
|
152
134
|
if (!bad.success) {
|
|
153
135
|
expect(bad.error.issues[0]).toMatchObject({
|
|
154
|
-
path: ["
|
|
136
|
+
path: ["max"],
|
|
155
137
|
message: "Max must be > min",
|
|
156
138
|
});
|
|
157
139
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogsbox-shape",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.209",
|
|
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",
|