cogsbox-shape 0.5.116 → 0.5.118

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 +91 -59
  2. package/package.json +1 -1
package/dist/schema.js CHANGED
@@ -1,5 +1,4 @@
1
- import { z, ZodType } from "zod";
2
- import { ca } from "zod/v4/locales";
1
+ import { z } from "zod";
3
2
  export const isFunction = (fn) => typeof fn === "function";
4
3
  // Function to create a properly typed current timestamp config
5
4
  export function currentTimeStamp() {
@@ -516,52 +515,70 @@ export function createSchema(schema, relations) {
516
515
  toDb,
517
516
  };
518
517
  }
519
- function createViewObject(tableName, selection, registry) {
520
- // A recursive helper that builds ONE schema (client or validation) for a view.
521
- function buildView(currentTable, schemaType, subSelection) {
522
- const registryEntry = registry[currentTable];
523
- const rawSchema = registryEntry.rawSchema;
518
+ function createViewObject(initialRegistryKey, // The key for the starting schema, e.g., "users"
519
+ selection, registry, tableNameToRegistryKeyMap // The lookup map
520
+ ) {
521
+ /**
522
+ * A recursive helper function that builds a Zod schema for a given schema and its selected relations.
523
+ * It is defined inside createViewObject to have access to the `registry` and `tableNameToRegistryKeyMap` via a closure.
524
+ *
525
+ * @param currentRegistryKey - The user-defined key for the current schema being processed (e.g., "users", then "posts").
526
+ * @param subSelection - The part of the selection object for the current schema (e.g., { comments: true } or just `true`).
527
+ * @param schemaType - Whether to build the 'client' or 'validation' schema.
528
+ * @returns A ZodObject representing the composed schema.
529
+ */
530
+ function buildView(currentRegistryKey, subSelection, schemaType) {
531
+ // 1. Find the current schema's definition in the registry using its KEY.
532
+ const registryEntry = registry[currentRegistryKey];
533
+ if (!registryEntry) {
534
+ throw new Error(`Schema with key "${currentRegistryKey}" not found in the registry.`);
535
+ }
536
+ // 2. Get the base Zod schema (primitives and references only) for the current level.
524
537
  const baseSchema = registryEntry.zodSchemas[`${schemaType}Schema`];
525
- // --- START OF THE FIX ---
526
- // 1. Get the shape of the base schema (e.g., { id: z.ZodNumber, name: z.ZodString })
527
- // The base schema correctly contains only primitive/referenced fields.
528
538
  const primitiveShape = baseSchema.shape;
529
- // 2. If the selection is just `true`, we don't need to add any relations.
539
+ // 3. If the selection is just `true`, we are done at this level. Return the base primitive schema.
530
540
  if (subSelection === true) {
531
541
  return z.object(primitiveShape);
532
542
  }
533
- // 3. Build a new shape object for the selected relations.
543
+ // 4. If the selection is an object, we need to process its relations.
534
544
  const selectedRelationShapes = {};
535
545
  if (typeof subSelection === "object") {
536
- for (const key in subSelection) {
537
- // We only care about keys that are actual relations in the raw schema.
538
- if (subSelection[key] && rawSchema[key]?.config?.sql?.schema) {
539
- const relationConfig = rawSchema[key].config.sql;
540
- const targetTable = relationConfig.schema()._tableName;
541
- // Recursively build the sub-schema for the relation.
542
- const subSchema = buildView(targetTable, schemaType, subSelection[key]);
543
- // Wrap it in an array or optional as needed.
546
+ // Iterate over the keys in the selection object (e.g., "posts", "profile").
547
+ for (const relationKey in subSelection) {
548
+ // Check if this key corresponds to a valid relation in the raw schema definition.
549
+ const relationBuilder = registryEntry.rawSchema[relationKey];
550
+ const isRelation = relationBuilder?.config?.sql?.schema;
551
+ if (subSelection[relationKey] && isRelation) {
552
+ const relationConfig = relationBuilder.config.sql;
553
+ // 5. KEY STEP: Get the internal `_tableName` of the TARGET schema (e.g., "post_table").
554
+ const targetTableName = relationConfig.schema()._tableName;
555
+ // 6. KEY STEP: Use the map to find the REGISTRY KEY for that target schema (e.g., "posts").
556
+ const nextRegistryKey = tableNameToRegistryKeyMap[targetTableName];
557
+ if (!nextRegistryKey) {
558
+ throw new Error(`Could not resolve registry key for table "${targetTableName}"`);
559
+ }
560
+ // 7. RECURSIVE CALL: Call `buildView` for the related schema, passing the
561
+ // CORRECT registry key and the sub-selection for that relation.
562
+ const relationSchema = buildView(nextRegistryKey, subSelection[relationKey], schemaType);
563
+ // 8. Wrap the resulting schema in an array or optional based on the relation type.
544
564
  if (["hasMany", "manyToMany"].includes(relationConfig.type)) {
545
- selectedRelationShapes[key] = z.array(subSchema);
565
+ selectedRelationShapes[relationKey] = z.array(relationSchema);
546
566
  }
547
567
  else {
548
- selectedRelationShapes[key] = subSchema.optional();
568
+ selectedRelationShapes[relationKey] = relationSchema.optional();
549
569
  }
550
570
  }
551
571
  }
552
572
  }
553
- // 4. Combine the primitive shape and the new relation shapes into one final shape.
573
+ // 9. Combine the base primitive fields with the newly built relational schemas.
554
574
  const finalShape = { ...primitiveShape, ...selectedRelationShapes };
555
- // 5. Return a brand new, clean Zod object from the final shape.
556
575
  return z.object(finalShape);
557
- // --- END OF THE FIX ---
558
576
  }
559
- // The main function builds the final object with both schemas.
560
- const sourceRegistryEntry = registry[tableName];
577
+ // The main function's return value. It kicks off the recursive process for both client and validation schemas.
561
578
  return {
562
- sql: sourceRegistryEntry.zodSchemas.sqlSchema,
563
- client: buildView(tableName, "client", selection),
564
- validation: buildView(tableName, "validation", selection),
579
+ sql: registry[initialRegistryKey].zodSchemas.sqlSchema,
580
+ client: buildView(initialRegistryKey, selection, "client"),
581
+ validation: buildView(initialRegistryKey, selection, "validation"),
565
582
  };
566
583
  }
567
584
  export function createSchemaBox(schemas, resolver) {
@@ -590,7 +607,7 @@ export function createSchemaBox(schemas, resolver) {
590
607
  },
591
608
  });
592
609
  const resolutionConfig = resolver(schemaProxy);
593
- const resolvedSchemas = { ...schemas };
610
+ const resolvedSchemas = schemas;
594
611
  // STAGE 1: Resolve references
595
612
  for (const tableName in schemas) {
596
613
  for (const fieldName in schemas[tableName]) {
@@ -675,6 +692,11 @@ export function createSchemaBox(schemas, resolver) {
675
692
  });
676
693
  };
677
694
  const cleanerRegistry = {};
695
+ const tableNameToRegistryKeyMap = {};
696
+ for (const key in finalRegistry) {
697
+ const tableName = finalRegistry[key].rawSchema._tableName;
698
+ tableNameToRegistryKeyMap[tableName] = key;
699
+ }
678
700
  for (const tableName in finalRegistry) {
679
701
  const entry = finalRegistry[tableName];
680
702
  cleanerRegistry[tableName] = {
@@ -691,26 +713,35 @@ export function createSchemaBox(schemas, resolver) {
691
713
  defaults: entry.zodSchemas.defaultValues,
692
714
  nav: createNavProxy(tableName, finalRegistry),
693
715
  // Add this
694
- RelationSelection: {},
695
- createView: function (selection) {
696
- const view = createViewObject(tableName, selection, finalRegistry);
697
- const defaults = computeViewDefaults(tableName, selection, finalRegistry);
716
+ createView: (selection) => {
717
+ const view = createViewObject(tableName, selection, finalRegistry, tableNameToRegistryKeyMap);
718
+ const defaults = computeViewDefaults(tableName, selection, finalRegistry, tableNameToRegistryKeyMap);
698
719
  console.log("View defaults:", defaults); // ADD THIS
699
720
  return {
700
721
  ...view,
701
722
  defaults: defaults,
702
723
  };
703
724
  },
725
+ RelationSelection: {},
704
726
  };
705
727
  }
706
728
  return cleanerRegistry;
707
729
  }
708
- function computeViewDefaults(tableName, selection, registry, visited = new Set()) {
709
- if (visited.has(tableName)) {
730
+ function computeViewDefaults(currentRegistryKey, // Renamed for clarity, e.g., "users"
731
+ selection, registry, tableNameToRegistryKeyMap, // Accept the map
732
+ visited = new Set()) {
733
+ if (visited.has(currentRegistryKey)) {
710
734
  return undefined; // Prevent circular references
711
735
  }
712
- visited.add(tableName);
713
- const entry = registry[tableName];
736
+ visited.add(currentRegistryKey);
737
+ // This lookup now uses the correct key every time.
738
+ const entry = registry[currentRegistryKey];
739
+ // This check prevents the crash.
740
+ if (!entry) {
741
+ // This case should ideally not be hit if the map is correct, but it's safe to have.
742
+ console.warn(`Could not find entry for key "${currentRegistryKey}" in registry while computing defaults.`);
743
+ return {};
744
+ }
714
745
  const rawSchema = entry.rawSchema;
715
746
  const baseDefaults = { ...entry.zodSchemas.defaultValues };
716
747
  if (selection === true || typeof selection !== "object") {
@@ -724,13 +755,16 @@ function computeViewDefaults(tableName, selection, registry, visited = new Set()
724
755
  if (!field?.config?.sql?.schema)
725
756
  continue;
726
757
  const relationConfig = field.config.sql;
727
- const targetTable = relationConfig.schema()._tableName;
728
- // ----- FIX IS HERE -----
729
- // Look for the defaultConfig on the nested relationConfig object.
730
- const defaultConfig = relationConfig.defaultConfig;
758
+ // --- THE CORE FIX ---
759
+ // 1. Get the internal _tableName of the related schema (e.g., "post_table")
760
+ const targetTableName = relationConfig.schema()._tableName;
761
+ // 2. Use the map to find the correct registry key for it (e.g., "posts")
762
+ const nextRegistryKey = tableNameToRegistryKeyMap[targetTableName];
763
+ if (!nextRegistryKey)
764
+ continue; // Could not resolve, skip this relation
731
765
  // Handle different default configurations
766
+ const defaultConfig = relationConfig.defaultConfig;
732
767
  if (defaultConfig === undefined) {
733
- // Don't include in defaults
734
768
  delete baseDefaults[key];
735
769
  }
736
770
  else if (defaultConfig === null) {
@@ -739,23 +773,21 @@ function computeViewDefaults(tableName, selection, registry, visited = new Set()
739
773
  else if (Array.isArray(defaultConfig)) {
740
774
  baseDefaults[key] = [];
741
775
  }
742
- else if (defaultConfig === true) {
743
- // Generate based on nested selection
744
- if (relationConfig.type === "hasMany" ||
745
- relationConfig.type === "manyToMany") {
746
- const count = relationConfig.defaultCount || 1;
747
- baseDefaults[key] = Array.from({ length: count }, () => computeViewDefaults(targetTable, selection[key], registry, new Set(visited)));
748
- }
749
- else {
750
- baseDefaults[key] = computeViewDefaults(targetTable, selection[key], registry, new Set(visited));
751
- }
776
+ else if (relationConfig.type === "hasMany" ||
777
+ relationConfig.type === "manyToMany") {
778
+ const count = defaultConfig?.count || relationConfig.defaultCount || 1;
779
+ baseDefaults[key] = Array.from({ length: count }, () =>
780
+ // 3. Make the recursive call with the CORRECT key
781
+ computeViewDefaults(nextRegistryKey, selection[key], registry, tableNameToRegistryKeyMap, // Pass the map along
782
+ new Set(visited)));
752
783
  }
753
- else if (typeof defaultConfig === "object" && "count" in defaultConfig) {
754
- baseDefaults[key] = Array.from({ length: defaultConfig.count }, () => computeViewDefaults(targetTable, selection[key], registry, new Set(visited)));
784
+ else {
785
+ // hasOne or belongsTo
786
+ baseDefaults[key] =
787
+ // 3. Make the recursive call with the CORRECT key
788
+ computeViewDefaults(nextRegistryKey, selection[key], registry, tableNameToRegistryKeyMap, // Pass the map along
789
+ new Set(visited));
755
790
  }
756
791
  }
757
- // NOTE: This was in the original code but is not needed for the recursive logic.
758
- // It's safe to remove, but also safe to keep.
759
- // visited.delete(tableName);
760
792
  return baseDefaults;
761
793
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogsbox-shape",
3
- "version": "0.5.116",
3
+ "version": "0.5.118",
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",