cogsbox-shape 0.5.62 → 0.5.64

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.
Files changed (2) hide show
  1. package/dist/schema.js +60 -75
  2. package/package.json +1 -1
package/dist/schema.js CHANGED
@@ -422,68 +422,35 @@ function isRelation(value) {
422
422
  "toKey" in value &&
423
423
  "schema" in value);
424
424
  }
425
- // In your cogsbox-shape file...
426
- // Replace the entire existing createSchema function with this SINGLE, CORRECT version.
427
- // In your cogsbox-shape file...
428
- // Replace the entire existing createSchema function with this SINGLE, CORRECT version.
429
425
  export function createSchema(schema) {
430
426
  const sqlFields = {};
431
427
  const clientFields = {};
432
428
  const validationFields = {};
433
429
  const defaultValues = {};
434
- // Store the functions to be executed in a second pass
435
430
  const deferredFields = [];
436
- // --- PASS 1: Process all non-function fields ---
431
+ // --- PASS 1: Separate immediate fields from deferred relations/references ---
437
432
  for (const key in schema) {
438
433
  if (key === "_tableName" || key.startsWith("__"))
439
434
  continue;
440
435
  const definition = schema[key];
441
- if (typeof definition === "function") {
442
- // It's a hasMany, belongsTo, etc. Defer it.
436
+ if ((definition && typeof definition.config === "object") || // It's a builder
437
+ typeof definition === "function" || // It's a legacy relation
438
+ (definition && definition.type === "reference") // It's a reference
439
+ ) {
440
+ // Defer all builders, functions, and references to Pass 2
443
441
  deferredFields.push({ key, definition });
444
442
  }
445
- else if (definition && definition.type === "reference") {
446
- // It's a reference. Defer it.
447
- deferredFields.push({ key, definition });
448
- }
449
- else if (definition && typeof definition.config === "object") {
450
- // Check if it's a relation builder
451
- const sqlConfig = definition.config.sql;
452
- if (sqlConfig &&
453
- typeof sqlConfig === "object" &&
454
- ["hasMany", "manyToMany", "hasOne", "belongsTo"].includes(sqlConfig.type)) {
455
- // It's a relation builder - defer it
456
- deferredFields.push({ key, definition: sqlConfig });
457
- }
458
- else {
459
- // It's a standard field builder. Process it now.
460
- sqlFields[key] = definition.config.zodSqlSchema;
461
- clientFields[key] = definition.config.zodClientSchema;
462
- validationFields[key] = definition.config.zodValidationSchema;
463
- defaultValues[key] = definition.config.initialValue;
464
- }
443
+ else {
444
+ // This case should ideally not be hit with the builder pattern, but is safe to have.
445
+ // Process any non-builder, non-deferred fields if they exist.
465
446
  }
466
447
  }
467
- // In createSchema function, in PASS 2 where relations are processed:
448
+ // --- PASS 2: Process all deferred references and relations ---
468
449
  for (const { key, definition } of deferredFields) {
469
450
  let resolvedDefinition = definition;
470
- // If it's a relation like hasMany, call the outer function to get the config object
471
451
  if (typeof resolvedDefinition === "function") {
452
+ // Handle legacy function style: hasMany(...)
472
453
  resolvedDefinition = resolvedDefinition();
473
- }
474
- if (resolvedDefinition && resolvedDefinition.type === "reference") {
475
- const referencedField = resolvedDefinition.to(); // This is now safe to call
476
- if (!referencedField || !referencedField.config) {
477
- throw new Error(`Could not resolve reference for key "${key}"`);
478
- }
479
- sqlFields[key] = referencedField.config.zodSqlSchema;
480
- clientFields[key] = referencedField.config.zodClientSchema;
481
- validationFields[key] = referencedField.config.zodValidationSchema;
482
- defaultValues[key] = referencedField.config.initialValue;
483
- }
484
- else if (resolvedDefinition &&
485
- ["hasMany", "manyToMany", "hasOne", "belongsTo"].includes(resolvedDefinition.type)) {
486
- // Handle legacy function-style relations
487
454
  const relation = resolvedDefinition;
488
455
  const childSchemaResult = createSchema(relation.schema);
489
456
  if (relation.type === "hasMany" || relation.type === "manyToMany") {
@@ -495,46 +462,64 @@ export function createSchema(schema) {
495
462
  defaultValues[key] = Array.from({ length: relation.defaultCount || 0 }, () => childSchemaResult.defaultValues);
496
463
  }
497
464
  else {
498
- // hasOne or belongsTo
499
465
  sqlFields[key] = childSchemaResult.sqlSchema.optional();
500
466
  clientFields[key] = childSchemaResult.clientSchema.optional();
501
467
  validationFields[key] = childSchemaResult.validationSchema.optional();
502
468
  defaultValues[key] = childSchemaResult.defaultValues;
503
469
  }
504
470
  }
505
- else if (definition &&
506
- definition.config &&
507
- definition.config.sql &&
508
- typeof definition.config.sql === "object" &&
509
- ["hasMany", "hasOne", "belongsTo", "manyToMany"].includes(definition.config.sql.type)) {
510
- // This is a builder-style relation like `shape.hasMany().client()`
511
- const config = definition.config;
512
- const relationConfig = config.sql;
513
- const childSchemaResult = createSchema(relationConfig.schema);
514
- // Create the base schemas for the relation
515
- let baseClientSchema;
516
- let baseValidationSchema;
517
- if (relationConfig.type === "hasMany" ||
518
- relationConfig.type === "manyToMany") {
519
- baseClientSchema = z.array(childSchemaResult.clientSchema).optional();
520
- baseValidationSchema = z
521
- .array(childSchemaResult.validationSchema)
522
- .optional();
523
- defaultValues[key] = Array.from({ length: relationConfig.defaultCount || 0 }, () => childSchemaResult.defaultValues);
471
+ else if (resolvedDefinition && resolvedDefinition.type === "reference") {
472
+ // Handle reference fields
473
+ const referencedField = resolvedDefinition.to();
474
+ sqlFields[key] = referencedField.config.zodSqlSchema;
475
+ clientFields[key] = referencedField.config.zodClientSchema;
476
+ validationFields[key] = referencedField.config.zodValidationSchema;
477
+ defaultValues[key] = referencedField.config.initialValue;
478
+ }
479
+ else if (resolvedDefinition && resolvedDefinition.config) {
480
+ // It's a builder object (`shape.sql(...)` or `shape.hasMany(...)`)
481
+ const config = resolvedDefinition.config;
482
+ const sqlConfig = config.sql;
483
+ if (sqlConfig &&
484
+ typeof sqlConfig === "object" &&
485
+ ["hasMany", "hasOne", "belongsTo", "manyToMany"].includes(sqlConfig.type)) {
486
+ // --- THIS IS THE KEY PART FOR RELATION BUILDERS ---
487
+ const relationConfig = sqlConfig;
488
+ const childSchemaResult = createSchema(relationConfig.schema);
489
+ // --- THE FIX IS HERE ---
490
+ // 1. Create the BASE schema WITHOUT .optional()
491
+ let rawClientSchema;
492
+ let rawValidationSchema;
493
+ if (relationConfig.type === "hasMany" ||
494
+ relationConfig.type === "manyToMany") {
495
+ rawClientSchema = z.array(childSchemaResult.clientSchema); // No .optional()
496
+ rawValidationSchema = z.array(childSchemaResult.validationSchema); // No .optional()
497
+ defaultValues[key] = Array.from({ length: relationConfig.defaultCount || 0 }, () => childSchemaResult.defaultValues);
498
+ }
499
+ else {
500
+ rawClientSchema = childSchemaResult.clientSchema; // No .optional()
501
+ rawValidationSchema = childSchemaResult.validationSchema; // No .optional()
502
+ defaultValues[key] = childSchemaResult.defaultValues;
503
+ }
504
+ // 2. Apply the transform to the RAW schema
505
+ const transformedClientSchema = config.clientTransform
506
+ ? config.clientTransform(rawClientSchema)
507
+ : rawClientSchema;
508
+ const transformedValidationSchema = config.validationTransform
509
+ ? config.validationTransform(rawValidationSchema)
510
+ : transformedClientSchema;
511
+ // 3. NOW, make the final, transformed schema optional.
512
+ sqlFields[key] = z.array(childSchemaResult.sqlSchema).optional();
513
+ clientFields[key] = transformedClientSchema.optional();
514
+ validationFields[key] = transformedValidationSchema.optional();
524
515
  }
525
516
  else {
526
- baseClientSchema = childSchemaResult.clientSchema.optional();
527
- baseValidationSchema = childSchemaResult.validationSchema.optional();
528
- defaultValues[key] = childSchemaResult.defaultValues;
517
+ // It's a standard field builder (`shape.sql(...)`)
518
+ sqlFields[key] = config.zodSqlSchema;
519
+ clientFields[key] = config.zodClientSchema;
520
+ validationFields[key] = config.zodValidationSchema;
521
+ defaultValues[key] = config.initialValue;
529
522
  }
530
- sqlFields[key] = z.array(childSchemaResult.sqlSchema).optional(); // SQL schema is never transformed by client
531
- clientFields[key] = config.clientTransform
532
- ? config.clientTransform(baseClientSchema) // Apply the stored function
533
- : baseClientSchema; // Or use the default if no transform exists
534
- // Validation falls back to the client schema (which may have been transformed)
535
- validationFields[key] = config.validationTransform
536
- ? config.validationTransform(baseValidationSchema)
537
- : clientFields[key];
538
523
  }
539
524
  }
540
525
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogsbox-shape",
3
- "version": "0.5.62",
3
+ "version": "0.5.64",
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",