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/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 inferredDbType = (() => {
51
+ const sqlZodType = (() => {
131
52
  let baseType;
132
53
  if (sqlConfig.pk) {
133
- baseType = z.number(); // DB PKs are always numbers
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
- // Create JSON Schema version immediately
198
- const jsonSchema = zodToJsonSchema(inferredDbType);
199
- return {
200
- sql: sqlConfig,
201
- dbType: inferredDbType,
202
- zodDbSchema: inferredDbType,
203
- zodClientSchema: inferredClientType,
204
- jsonSchema,
205
- defaultValue: inferDefaultFromZod(inferredDbType, sqlConfig),
206
- client: createClient({
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 serialized = createSerializableSchema(schema);
459
- const dbFields = {};
426
+ const sqlFields = {};
460
427
  const clientFields = {};
428
+ const validationFields = {};
461
429
  const defaultValues = {};
462
- // ... existing schema building logic ...
463
- for (const [key, value] of Object.entries(schema)) {
430
+ for (const key in schema) {
464
431
  if (key === "_tableName")
465
432
  continue;
466
- if (typeof value === "function") {
467
- const relation = value();
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
- throw new Error(`Invalid relation for key ${key}`);
438
+ continue;
470
439
  }
471
- const childSchema = createSchema(relation.schema);
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));
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
- dbFields[key] = z.object(childSchema.dbSchema.shape);
483
- clientFields[key] = z.object(childSchema.clientSchema.shape);
484
- defaultValues[key] = childSchema.defaultValues;
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
- dbFields[key] = value.zodDbSchema;
489
- clientFields[key] = value.zodClientSchema;
490
- defaultValues[key] =
491
- value.defaultValue ?? inferDefaultFromZod(value.zodClientSchema);
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
- dbSchema: dbSchemaObj,
499
- clientSchema: clientSchemaObj,
500
- mixedSchema: mixedSchema,
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.23",
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",