cogsbox-shape 0.5.23 → 0.5.25
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.d.ts +1715 -870
- package/dist/schema.js +137 -195
- 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,25 @@ 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
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
// ... existing relation logic ...
|
|
473
|
-
if (relation.type === "hasMany") {
|
|
474
|
-
dbFields[key] = z.array(z.object(childSchema.dbSchema.shape));
|
|
475
|
-
clientFields[key] = z.array(z.object(childSchema.clientSchema.shape));
|
|
476
|
-
const count = relation.defaultCount || 0;
|
|
477
|
-
defaultValues[key] = Array.from({ length: count }, () => ({
|
|
478
|
-
...childSchema.defaultValues,
|
|
479
|
-
}));
|
|
480
|
-
}
|
|
481
|
-
else {
|
|
482
|
-
dbFields[key] = z.object(childSchema.dbSchema.shape);
|
|
483
|
-
clientFields[key] = z.object(childSchema.clientSchema.shape);
|
|
484
|
-
defaultValues[key] = childSchema.defaultValues;
|
|
485
|
-
}
|
|
486
|
-
continue;
|
|
433
|
+
const field = schema[key];
|
|
434
|
+
if (field && typeof field === "object" && "config" in field) {
|
|
435
|
+
sqlFields[key] = field.config.zodSqlSchema; //field.config' is of type 'unknown
|
|
436
|
+
clientFields[key] = field.config.zodClientSchema;
|
|
437
|
+
validationFields[key] = field.config.zodValidationSchema;
|
|
438
|
+
defaultValues[key] = field.config.initialValue;
|
|
487
439
|
}
|
|
488
|
-
dbFields[key] = value.zodDbSchema;
|
|
489
|
-
clientFields[key] = value.zodClientSchema;
|
|
490
|
-
defaultValues[key] =
|
|
491
|
-
value.defaultValue ?? inferDefaultFromZod(value.zodClientSchema);
|
|
492
440
|
}
|
|
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
441
|
return {
|
|
498
|
-
|
|
499
|
-
clientSchema:
|
|
500
|
-
|
|
442
|
+
sqlSchema: z.object(sqlFields),
|
|
443
|
+
clientSchema: z.object(clientFields),
|
|
444
|
+
validationSchema: z.object(validationFields),
|
|
501
445
|
defaultValues: defaultValues,
|
|
502
|
-
initialValues: () => defaultValues,
|
|
503
|
-
serialized: serialized,
|
|
504
446
|
};
|
|
505
447
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogsbox-shape",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.25",
|
|
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",
|