cogsbox-shape 0.5.23 → 0.5.26
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 +86 -89
- package/dist/example/schema.d.ts +1419 -633
- package/dist/example/user.d.ts +1409 -905
- package/dist/example/user.js +41 -8
- package/dist/schema copy.d.ts +1568 -0
- package/dist/schema copy.js +680 -0
- package/dist/schema.d.ts +1715 -870
- package/dist/schema.js +157 -189
- package/package.json +1 -3
package/dist/schema.js
CHANGED
|
@@ -10,85 +10,6 @@ export function currentTimeStamp() {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
// Internal type creation helper
|
|
13
|
-
const createClient = ({ sqlConfig, inferredDbType, inferredClientType, baseJsonSchema, serverType, }) => {
|
|
14
|
-
return (assert, defaultValue) => {
|
|
15
|
-
const clientType = isFunction(assert)
|
|
16
|
-
? assert({
|
|
17
|
-
zod: inferredClientType,
|
|
18
|
-
...(serverType && { serverType }),
|
|
19
|
-
})
|
|
20
|
-
: assert || inferredClientType;
|
|
21
|
-
// Handle timestamp default
|
|
22
|
-
let finalSqlConfig = sqlConfig;
|
|
23
|
-
let finalDefaultValue = defaultValue;
|
|
24
|
-
if (defaultValue &&
|
|
25
|
-
typeof defaultValue === "object" &&
|
|
26
|
-
"default" in defaultValue &&
|
|
27
|
-
defaultValue.default === "CURRENT_TIMESTAMP") {
|
|
28
|
-
finalSqlConfig = {
|
|
29
|
-
...sqlConfig,
|
|
30
|
-
default: "CURRENT_TIMESTAMP",
|
|
31
|
-
};
|
|
32
|
-
finalDefaultValue = defaultValue.defaultValue;
|
|
33
|
-
}
|
|
34
|
-
const effectiveDbType = serverType || inferredDbType;
|
|
35
|
-
const clientJsonSchema = zodToJsonSchema(clientType);
|
|
36
|
-
return {
|
|
37
|
-
sql: finalSqlConfig,
|
|
38
|
-
zodDbSchema: effectiveDbType,
|
|
39
|
-
zodClientSchema: clientType,
|
|
40
|
-
jsonSchema: serverType ? clientJsonSchema : baseJsonSchema,
|
|
41
|
-
defaultValue: finalDefaultValue ??
|
|
42
|
-
(serverType
|
|
43
|
-
? inferDefaultFromZod(serverType)
|
|
44
|
-
: finalDefaultValue),
|
|
45
|
-
transform: (transforms) => ({
|
|
46
|
-
sql: finalSqlConfig,
|
|
47
|
-
zodDbSchema: effectiveDbType,
|
|
48
|
-
zodClientSchema: clientType,
|
|
49
|
-
jsonSchema: serverType ? clientJsonSchema : baseJsonSchema,
|
|
50
|
-
defaultValue: finalDefaultValue,
|
|
51
|
-
toClient: transforms.toClient,
|
|
52
|
-
toDb: transforms.toDb,
|
|
53
|
-
transforms: {
|
|
54
|
-
toClient: transforms.toClient.toString(),
|
|
55
|
-
toDb: transforms.toDb.toString(),
|
|
56
|
-
},
|
|
57
|
-
}),
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
export function createTransforms(transforms) {
|
|
62
|
-
return {
|
|
63
|
-
sql: (config) => {
|
|
64
|
-
const base = shape.sql(config);
|
|
65
|
-
return {
|
|
66
|
-
sql: base.sql,
|
|
67
|
-
dbType: base.dbType,
|
|
68
|
-
zodDbSchema: base.zodDbSchema,
|
|
69
|
-
zodClientSchema: base.zodClientSchema,
|
|
70
|
-
client: base.client,
|
|
71
|
-
db: (dbType) => {
|
|
72
|
-
const baseDb = base.db(dbType);
|
|
73
|
-
const transformMethods = Object.entries(transforms).reduce((acc, [key, transform]) => ({
|
|
74
|
-
...acc,
|
|
75
|
-
[key]: () => ({
|
|
76
|
-
sql: config,
|
|
77
|
-
zodDbSchema: baseDb.zodDbSchema,
|
|
78
|
-
zodClientSchema: z.unknown(),
|
|
79
|
-
toClient: transform.toClient,
|
|
80
|
-
toDb: transform.toDb,
|
|
81
|
-
}),
|
|
82
|
-
}), {});
|
|
83
|
-
return {
|
|
84
|
-
...baseDb,
|
|
85
|
-
client: Object.assign(baseDb.client, transformMethods),
|
|
86
|
-
};
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
13
|
export const shape = {
|
|
93
14
|
// Integer fields
|
|
94
15
|
int: (config = {}) => shape.sql({
|
|
@@ -127,22 +48,16 @@ export const shape = {
|
|
|
127
48
|
...config,
|
|
128
49
|
}),
|
|
129
50
|
sql: (sqlConfig) => {
|
|
130
|
-
const
|
|
51
|
+
const sqlZodType = (() => {
|
|
131
52
|
let baseType;
|
|
132
53
|
if (sqlConfig.pk) {
|
|
133
|
-
baseType = z.number();
|
|
54
|
+
baseType = z.number();
|
|
134
55
|
}
|
|
135
56
|
else {
|
|
136
57
|
switch (sqlConfig.type) {
|
|
137
58
|
case "int":
|
|
138
59
|
baseType = z.number();
|
|
139
60
|
break;
|
|
140
|
-
case "varchar":
|
|
141
|
-
case "char":
|
|
142
|
-
case "text":
|
|
143
|
-
case "longtext":
|
|
144
|
-
baseType = z.string();
|
|
145
|
-
break;
|
|
146
61
|
case "boolean":
|
|
147
62
|
baseType = z.boolean();
|
|
148
63
|
break;
|
|
@@ -151,42 +66,8 @@ export const shape = {
|
|
|
151
66
|
baseType = z.date();
|
|
152
67
|
break;
|
|
153
68
|
default:
|
|
154
|
-
throw new Error(`Unsupported type: ${sqlConfig}`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (sqlConfig.nullable) {
|
|
158
|
-
baseType = baseType.nullable();
|
|
159
|
-
}
|
|
160
|
-
return baseType;
|
|
161
|
-
})();
|
|
162
|
-
const inferredClientType = (() => {
|
|
163
|
-
let baseType;
|
|
164
|
-
if (sqlConfig.pk) {
|
|
165
|
-
baseType = z.string(); // Client PKs are always strings
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
switch (sqlConfig.type) {
|
|
169
|
-
case "int":
|
|
170
|
-
baseType = z.number();
|
|
171
|
-
break;
|
|
172
|
-
case "varchar":
|
|
173
|
-
case "char":
|
|
174
|
-
case "text":
|
|
175
|
-
case "longtext":
|
|
176
69
|
baseType = z.string();
|
|
177
70
|
break;
|
|
178
|
-
case "boolean":
|
|
179
|
-
baseType = z.boolean();
|
|
180
|
-
break;
|
|
181
|
-
case "date":
|
|
182
|
-
case "datetime":
|
|
183
|
-
if (sqlConfig.default === "CURRENT_TIMESTAMP") {
|
|
184
|
-
baseType = z.date().optional();
|
|
185
|
-
}
|
|
186
|
-
baseType = z.date();
|
|
187
|
-
break;
|
|
188
|
-
default:
|
|
189
|
-
throw new Error(`Unsupported type: ${sqlConfig}`);
|
|
190
71
|
}
|
|
191
72
|
}
|
|
192
73
|
if (sqlConfig.nullable) {
|
|
@@ -194,44 +75,131 @@ export const shape = {
|
|
|
194
75
|
}
|
|
195
76
|
return baseType;
|
|
196
77
|
})();
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
sqlConfig,
|
|
208
|
-
inferredDbType,
|
|
209
|
-
inferredClientType,
|
|
210
|
-
baseJsonSchema: jsonSchema,
|
|
211
|
-
}),
|
|
212
|
-
db: (assert) => {
|
|
213
|
-
const serverType = isFunction(assert)
|
|
214
|
-
? assert({ zod: inferredDbType })
|
|
215
|
-
: assert;
|
|
216
|
-
return {
|
|
217
|
-
sql: sqlConfig,
|
|
218
|
-
dbType: serverType,
|
|
219
|
-
zodDbSchema: serverType,
|
|
220
|
-
zodClientSchema: inferredClientType,
|
|
221
|
-
jsonSchema: zodToJsonSchema(serverType),
|
|
222
|
-
defaultValue: inferDefaultFromZod(serverType),
|
|
223
|
-
client: createClient({
|
|
224
|
-
sqlConfig,
|
|
225
|
-
inferredDbType,
|
|
226
|
-
inferredClientType,
|
|
227
|
-
baseJsonSchema: jsonSchema,
|
|
228
|
-
serverType,
|
|
229
|
-
}),
|
|
230
|
-
};
|
|
231
|
-
},
|
|
232
|
-
};
|
|
78
|
+
// Initialize with sql type for all schemas
|
|
79
|
+
return createBuilder({
|
|
80
|
+
stage: "sql",
|
|
81
|
+
sqlConfig: sqlConfig,
|
|
82
|
+
sqlZod: sqlZodType,
|
|
83
|
+
newZod: sqlZodType,
|
|
84
|
+
initialValue: undefined,
|
|
85
|
+
clientZod: sqlZodType,
|
|
86
|
+
validationZod: sqlZodType,
|
|
87
|
+
});
|
|
233
88
|
},
|
|
234
89
|
};
|
|
90
|
+
function createBuilder(config) {
|
|
91
|
+
// Initialize completed stages tracker
|
|
92
|
+
const completedStages = config.completedStages || new Set(["sql"]);
|
|
93
|
+
const builderObject = {
|
|
94
|
+
config: {
|
|
95
|
+
sql: config.sqlConfig,
|
|
96
|
+
zodSqlSchema: config.sqlZod,
|
|
97
|
+
zodNewSchema: config.newZod,
|
|
98
|
+
initialValue: config.initialValue ||
|
|
99
|
+
inferDefaultFromZod(config.clientZod, config.sqlConfig),
|
|
100
|
+
zodClientSchema: config.clientZod,
|
|
101
|
+
zodValidationSchema: config.validationZod,
|
|
102
|
+
},
|
|
103
|
+
initialState: (schemaOrDefault, defaultValue) => {
|
|
104
|
+
// Runtime validation
|
|
105
|
+
if (completedStages.has("new")) {
|
|
106
|
+
throw new Error("initialState() can only be called once in the chain");
|
|
107
|
+
}
|
|
108
|
+
if (completedStages.has("client")) {
|
|
109
|
+
throw new Error("initialState() must be called before client()");
|
|
110
|
+
}
|
|
111
|
+
if (completedStages.has("validation")) {
|
|
112
|
+
throw new Error("initialState() must be called before validation()");
|
|
113
|
+
}
|
|
114
|
+
// Handle overload - if no second param, first param is the default
|
|
115
|
+
const hasTypeParam = defaultValue !== undefined;
|
|
116
|
+
const newSchema = hasTypeParam
|
|
117
|
+
? isFunction(schemaOrDefault)
|
|
118
|
+
? schemaOrDefault({ sql: config.sqlZod })
|
|
119
|
+
: schemaOrDefault
|
|
120
|
+
: config.sqlZod; // Keep SQL type if just setting default
|
|
121
|
+
const finalDefaultValue = hasTypeParam
|
|
122
|
+
? defaultValue()
|
|
123
|
+
: schemaOrDefault();
|
|
124
|
+
const newCompletedStages = new Set(completedStages);
|
|
125
|
+
newCompletedStages.add("new");
|
|
126
|
+
const newClientZod = hasTypeParam
|
|
127
|
+
? z.union([config.sqlZod, newSchema])
|
|
128
|
+
: config.sqlZod;
|
|
129
|
+
return createBuilder({
|
|
130
|
+
...config,
|
|
131
|
+
stage: "new",
|
|
132
|
+
newZod: newSchema,
|
|
133
|
+
initialValue: finalDefaultValue,
|
|
134
|
+
clientZod: newClientZod,
|
|
135
|
+
validationZod: hasTypeParam
|
|
136
|
+
? z.union([config.sqlZod, newSchema])
|
|
137
|
+
: config.sqlZod,
|
|
138
|
+
completedStages: newCompletedStages,
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
client: (assert) => {
|
|
142
|
+
// Runtime validation
|
|
143
|
+
if (completedStages.has("client")) {
|
|
144
|
+
throw new Error("client() can only be called once in the chain");
|
|
145
|
+
}
|
|
146
|
+
if (completedStages.has("validation")) {
|
|
147
|
+
throw new Error("client() must be called before validation()");
|
|
148
|
+
}
|
|
149
|
+
const clientSchema = isFunction(assert)
|
|
150
|
+
? assert({ sql: config.sqlZod, initialState: config.newZod })
|
|
151
|
+
: assert;
|
|
152
|
+
const newCompletedStages = new Set(completedStages);
|
|
153
|
+
newCompletedStages.add("client");
|
|
154
|
+
return createBuilder({
|
|
155
|
+
...config,
|
|
156
|
+
stage: "client",
|
|
157
|
+
clientZod: clientSchema,
|
|
158
|
+
// Always set validation to match client when client is set
|
|
159
|
+
validationZod: clientSchema,
|
|
160
|
+
completedStages: newCompletedStages,
|
|
161
|
+
});
|
|
162
|
+
},
|
|
163
|
+
validation: (assert) => {
|
|
164
|
+
// Runtime validation
|
|
165
|
+
if (completedStages.has("validation")) {
|
|
166
|
+
throw new Error("validation() can only be called once in the chain");
|
|
167
|
+
}
|
|
168
|
+
const validationSchema = isFunction(assert)
|
|
169
|
+
? assert({
|
|
170
|
+
sql: config.sqlZod,
|
|
171
|
+
initialState: config.newZod,
|
|
172
|
+
client: config.clientZod,
|
|
173
|
+
})
|
|
174
|
+
: assert;
|
|
175
|
+
const newCompletedStages = new Set(completedStages);
|
|
176
|
+
newCompletedStages.add("validation");
|
|
177
|
+
return createBuilder({
|
|
178
|
+
...config,
|
|
179
|
+
stage: "validation",
|
|
180
|
+
validationZod: validationSchema,
|
|
181
|
+
completedStages: newCompletedStages,
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
transform: (transforms) => {
|
|
185
|
+
// Runtime validation
|
|
186
|
+
if (!completedStages.has("validation") &&
|
|
187
|
+
!completedStages.has("client")) {
|
|
188
|
+
throw new Error("transform() requires at least client() or validation() to be called first");
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
config: {
|
|
192
|
+
...builderObject.config,
|
|
193
|
+
transforms: {
|
|
194
|
+
toClient: transforms.toClient,
|
|
195
|
+
toDb: transforms.toDb,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
return builderObject;
|
|
202
|
+
}
|
|
235
203
|
export function hasMany(config) {
|
|
236
204
|
return () => ({
|
|
237
205
|
type: "hasMany",
|
|
@@ -455,51 +423,51 @@ export function createMixedValidationSchema(schema, clientSchema, dbSchema) {
|
|
|
455
423
|
return z.object(mixedFields);
|
|
456
424
|
}
|
|
457
425
|
export function createSchema(schema) {
|
|
458
|
-
const
|
|
459
|
-
const dbFields = {};
|
|
426
|
+
const sqlFields = {};
|
|
460
427
|
const clientFields = {};
|
|
428
|
+
const validationFields = {};
|
|
461
429
|
const defaultValues = {};
|
|
462
|
-
|
|
463
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
430
|
+
for (const key in schema) {
|
|
464
431
|
if (key === "_tableName")
|
|
465
432
|
continue;
|
|
466
|
-
|
|
467
|
-
|
|
433
|
+
const field = schema[key];
|
|
434
|
+
// Case 1: Handle relation functions (hasMany, hasOne, etc.)
|
|
435
|
+
if (typeof field === "function") {
|
|
436
|
+
const relation = field();
|
|
468
437
|
if (!isRelation(relation)) {
|
|
469
|
-
|
|
438
|
+
continue;
|
|
470
439
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
440
|
+
// Recursively process the nested schema
|
|
441
|
+
const childSchemaResult = createSchema(relation.schema);
|
|
442
|
+
// For to-many relations, wrap schemas in z.array()
|
|
443
|
+
if (relation.type === "hasMany" || relation.type === "manyToMany") {
|
|
444
|
+
sqlFields[key] = z.array(childSchemaResult.sqlSchema);
|
|
445
|
+
clientFields[key] = z.array(childSchemaResult.clientSchema);
|
|
446
|
+
validationFields[key] = z.array(childSchemaResult.validationSchema);
|
|
447
|
+
// Create an array of default values for the relation
|
|
476
448
|
const count = relation.defaultCount || 0;
|
|
477
|
-
defaultValues[key] = Array.from({ length: count }, () =>
|
|
478
|
-
...childSchema.defaultValues,
|
|
479
|
-
}));
|
|
449
|
+
defaultValues[key] = Array.from({ length: count }, () => childSchemaResult.defaultValues);
|
|
480
450
|
}
|
|
481
451
|
else {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
452
|
+
// For to-one relations, use schemas directly
|
|
453
|
+
sqlFields[key] = childSchemaResult.sqlSchema;
|
|
454
|
+
clientFields[key] = childSchemaResult.clientSchema;
|
|
455
|
+
validationFields[key] = childSchemaResult.validationSchema;
|
|
456
|
+
defaultValues[key] = childSchemaResult.defaultValues;
|
|
485
457
|
}
|
|
486
|
-
continue;
|
|
487
458
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
459
|
+
// Case 2: Handle regular builder fields
|
|
460
|
+
else if (field && typeof field === "object" && "config" in field) {
|
|
461
|
+
sqlFields[key] = field.config.zodSqlSchema;
|
|
462
|
+
clientFields[key] = field.config.zodClientSchema;
|
|
463
|
+
validationFields[key] = field.config.zodValidationSchema;
|
|
464
|
+
defaultValues[key] = field.config.initialValue;
|
|
465
|
+
}
|
|
492
466
|
}
|
|
493
|
-
const clientSchemaObj = z.object(clientFields);
|
|
494
|
-
const dbSchemaObj = z.object(dbFields);
|
|
495
|
-
// Pass the built schemas to avoid circular reference
|
|
496
|
-
const mixedSchema = createMixedValidationSchema(schema, clientSchemaObj, dbSchemaObj);
|
|
497
467
|
return {
|
|
498
|
-
|
|
499
|
-
clientSchema:
|
|
500
|
-
|
|
468
|
+
sqlSchema: z.object(sqlFields),
|
|
469
|
+
clientSchema: z.object(clientFields),
|
|
470
|
+
validationSchema: z.object(validationFields),
|
|
501
471
|
defaultValues: defaultValues,
|
|
502
|
-
initialValues: () => defaultValues,
|
|
503
|
-
serialized: serialized,
|
|
504
472
|
};
|
|
505
473
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogsbox-shape",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.26",
|
|
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",
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
"repository": "github:cogsbox/cogsbox-shape",
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": " tsc",
|
|
13
|
-
"prepublishOnly": "npm run build",
|
|
14
13
|
"lint": "eslint src --ext .ts",
|
|
15
14
|
"format": "prettier --write \"src/**/*.ts\""
|
|
16
15
|
},
|
|
@@ -34,7 +33,6 @@
|
|
|
34
33
|
"license": "MIT",
|
|
35
34
|
"dependencies": {
|
|
36
35
|
"commander": "^13.1.0",
|
|
37
|
-
"ts-node": "^10.9.2",
|
|
38
36
|
"tsx": "^4.19.3",
|
|
39
37
|
"uuid": "^9.0.0",
|
|
40
38
|
"zod": "^3.22.4",
|