cogsbox-shape 0.5.117 → 0.5.119

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 +141 -80
  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() {
@@ -127,53 +126,82 @@ function createBuilder(config) {
127
126
  clientTransform: config.clientTransform, // <-- FIX: Make sure transform is passed through
128
127
  validationTransform: config.validationTransform, // <-- FIX: Make sure transform is passed through
129
128
  },
130
- initialState: (value, schemaModifier) => {
129
+ initialState: (value, schemaOrModifier) => {
131
130
  if (completedStages.has("new")) {
132
131
  throw new Error("initialState() can only be called once in the chain");
133
132
  }
134
133
  let actualValue;
135
134
  let baseSchema;
136
- // Check if value is a Zod schema
135
+ let finalSchema;
136
+ // Check if value is a Zod schema (single argument case)
137
137
  if (value && typeof value === "object" && "_def" in value) {
138
138
  // It's a Zod schema - infer the default value
139
139
  baseSchema = value;
140
140
  actualValue = inferDefaultFromZod(baseSchema, config.sqlConfig);
141
+ finalSchema = baseSchema;
141
142
  }
142
143
  else {
143
144
  // Get the actual value
144
145
  actualValue = isFunction(value) ? value() : value;
145
- // Create base Zod schema from the value type
146
- // Check if it's a literal value (string, number, boolean)
147
- if (typeof actualValue === "string" ||
148
- typeof actualValue === "number" ||
149
- typeof actualValue === "boolean") {
150
- baseSchema = z.literal(actualValue);
146
+ // If second parameter is provided and is a Zod schema, use it directly
147
+ if (schemaOrModifier &&
148
+ typeof schemaOrModifier === "object" &&
149
+ "_def" in schemaOrModifier) {
150
+ finalSchema = schemaOrModifier;
151
151
  }
152
- else if (actualValue instanceof Date) {
153
- baseSchema = z.date();
154
- }
155
- else if (actualValue === null) {
156
- baseSchema = z.null();
157
- }
158
- else if (actualValue === undefined) {
159
- baseSchema = z.undefined();
152
+ else if (isFunction(schemaOrModifier)) {
153
+ // It's a schema modifier function
154
+ // Create base Zod schema from the value type
155
+ if (typeof actualValue === "string" ||
156
+ typeof actualValue === "number" ||
157
+ typeof actualValue === "boolean") {
158
+ baseSchema = z.literal(actualValue);
159
+ }
160
+ else if (actualValue instanceof Date) {
161
+ baseSchema = z.date();
162
+ }
163
+ else if (actualValue === null) {
164
+ baseSchema = z.null();
165
+ }
166
+ else if (actualValue === undefined) {
167
+ baseSchema = z.undefined();
168
+ }
169
+ else {
170
+ baseSchema = z.any();
171
+ }
172
+ // Apply the modifier
173
+ finalSchema = schemaOrModifier(baseSchema);
160
174
  }
161
175
  else {
162
- baseSchema = z.any();
176
+ // No schema provided, create from value type
177
+ if (typeof actualValue === "string" ||
178
+ typeof actualValue === "number" ||
179
+ typeof actualValue === "boolean") {
180
+ baseSchema = z.literal(actualValue);
181
+ }
182
+ else if (actualValue instanceof Date) {
183
+ baseSchema = z.date();
184
+ }
185
+ else if (actualValue === null) {
186
+ baseSchema = z.null();
187
+ }
188
+ else if (actualValue === undefined) {
189
+ baseSchema = z.undefined();
190
+ }
191
+ else {
192
+ baseSchema = z.any();
193
+ }
194
+ finalSchema = baseSchema;
163
195
  }
164
196
  }
165
- // Apply schema modifier if provided
166
- const newSchema = isFunction(schemaModifier)
167
- ? schemaModifier(baseSchema)
168
- : baseSchema;
169
197
  const newCompletedStages = new Set(completedStages);
170
198
  newCompletedStages.add("new");
171
199
  // Create union for client/validation
172
- const clientValidationSchema = z.union([config.sqlZod, newSchema]);
200
+ const clientValidationSchema = z.union([config.sqlZod, finalSchema]);
173
201
  return createBuilder({
174
202
  ...config,
175
203
  stage: "new",
176
- newZod: newSchema,
204
+ newZod: finalSchema,
177
205
  initialValue: actualValue,
178
206
  clientZod: clientValidationSchema,
179
207
  validationZod: clientValidationSchema,
@@ -516,52 +544,70 @@ export function createSchema(schema, relations) {
516
544
  toDb,
517
545
  };
518
546
  }
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;
547
+ function createViewObject(initialRegistryKey, // The key for the starting schema, e.g., "users"
548
+ selection, registry, tableNameToRegistryKeyMap // The lookup map
549
+ ) {
550
+ /**
551
+ * A recursive helper function that builds a Zod schema for a given schema and its selected relations.
552
+ * It is defined inside createViewObject to have access to the `registry` and `tableNameToRegistryKeyMap` via a closure.
553
+ *
554
+ * @param currentRegistryKey - The user-defined key for the current schema being processed (e.g., "users", then "posts").
555
+ * @param subSelection - The part of the selection object for the current schema (e.g., { comments: true } or just `true`).
556
+ * @param schemaType - Whether to build the 'client' or 'validation' schema.
557
+ * @returns A ZodObject representing the composed schema.
558
+ */
559
+ function buildView(currentRegistryKey, subSelection, schemaType) {
560
+ // 1. Find the current schema's definition in the registry using its KEY.
561
+ const registryEntry = registry[currentRegistryKey];
562
+ if (!registryEntry) {
563
+ throw new Error(`Schema with key "${currentRegistryKey}" not found in the registry.`);
564
+ }
565
+ // 2. Get the base Zod schema (primitives and references only) for the current level.
524
566
  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
567
  const primitiveShape = baseSchema.shape;
529
- // 2. If the selection is just `true`, we don't need to add any relations.
568
+ // 3. If the selection is just `true`, we are done at this level. Return the base primitive schema.
530
569
  if (subSelection === true) {
531
570
  return z.object(primitiveShape);
532
571
  }
533
- // 3. Build a new shape object for the selected relations.
572
+ // 4. If the selection is an object, we need to process its relations.
534
573
  const selectedRelationShapes = {};
535
574
  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.
575
+ // Iterate over the keys in the selection object (e.g., "posts", "profile").
576
+ for (const relationKey in subSelection) {
577
+ // Check if this key corresponds to a valid relation in the raw schema definition.
578
+ const relationBuilder = registryEntry.rawSchema[relationKey];
579
+ const isRelation = relationBuilder?.config?.sql?.schema;
580
+ if (subSelection[relationKey] && isRelation) {
581
+ const relationConfig = relationBuilder.config.sql;
582
+ // 5. KEY STEP: Get the internal `_tableName` of the TARGET schema (e.g., "post_table").
583
+ const targetTableName = relationConfig.schema()._tableName;
584
+ // 6. KEY STEP: Use the map to find the REGISTRY KEY for that target schema (e.g., "posts").
585
+ const nextRegistryKey = tableNameToRegistryKeyMap[targetTableName];
586
+ if (!nextRegistryKey) {
587
+ throw new Error(`Could not resolve registry key for table "${targetTableName}"`);
588
+ }
589
+ // 7. RECURSIVE CALL: Call `buildView` for the related schema, passing the
590
+ // CORRECT registry key and the sub-selection for that relation.
591
+ const relationSchema = buildView(nextRegistryKey, subSelection[relationKey], schemaType);
592
+ // 8. Wrap the resulting schema in an array or optional based on the relation type.
544
593
  if (["hasMany", "manyToMany"].includes(relationConfig.type)) {
545
- selectedRelationShapes[key] = z.array(subSchema);
594
+ selectedRelationShapes[relationKey] = z.array(relationSchema);
546
595
  }
547
596
  else {
548
- selectedRelationShapes[key] = subSchema.optional();
597
+ selectedRelationShapes[relationKey] = relationSchema.optional();
549
598
  }
550
599
  }
551
600
  }
552
601
  }
553
- // 4. Combine the primitive shape and the new relation shapes into one final shape.
602
+ // 9. Combine the base primitive fields with the newly built relational schemas.
554
603
  const finalShape = { ...primitiveShape, ...selectedRelationShapes };
555
- // 5. Return a brand new, clean Zod object from the final shape.
556
604
  return z.object(finalShape);
557
- // --- END OF THE FIX ---
558
605
  }
559
- // The main function builds the final object with both schemas.
560
- const sourceRegistryEntry = registry[tableName];
606
+ // The main function's return value. It kicks off the recursive process for both client and validation schemas.
561
607
  return {
562
- sql: sourceRegistryEntry.zodSchemas.sqlSchema,
563
- client: buildView(tableName, "client", selection),
564
- validation: buildView(tableName, "validation", selection),
608
+ sql: registry[initialRegistryKey].zodSchemas.sqlSchema,
609
+ client: buildView(initialRegistryKey, selection, "client"),
610
+ validation: buildView(initialRegistryKey, selection, "validation"),
565
611
  };
566
612
  }
567
613
  export function createSchemaBox(schemas, resolver) {
@@ -675,6 +721,11 @@ export function createSchemaBox(schemas, resolver) {
675
721
  });
676
722
  };
677
723
  const cleanerRegistry = {};
724
+ const tableNameToRegistryKeyMap = {};
725
+ for (const key in finalRegistry) {
726
+ const tableName = finalRegistry[key].rawSchema._tableName;
727
+ tableNameToRegistryKeyMap[tableName] = key;
728
+ }
678
729
  for (const tableName in finalRegistry) {
679
730
  const entry = finalRegistry[tableName];
680
731
  cleanerRegistry[tableName] = {
@@ -691,26 +742,35 @@ export function createSchemaBox(schemas, resolver) {
691
742
  defaults: entry.zodSchemas.defaultValues,
692
743
  nav: createNavProxy(tableName, finalRegistry),
693
744
  // Add this
694
- RelationSelection: {},
695
745
  createView: (selection) => {
696
- const view = createViewObject(tableName, selection, finalRegistry);
697
- const defaults = computeViewDefaults(tableName, selection, finalRegistry);
746
+ const view = createViewObject(tableName, selection, finalRegistry, tableNameToRegistryKeyMap);
747
+ const defaults = computeViewDefaults(tableName, selection, finalRegistry, tableNameToRegistryKeyMap);
698
748
  console.log("View defaults:", defaults); // ADD THIS
699
749
  return {
700
750
  ...view,
701
751
  defaults: defaults,
702
752
  };
703
753
  },
754
+ RelationSelection: {},
704
755
  };
705
756
  }
706
757
  return cleanerRegistry;
707
758
  }
708
- function computeViewDefaults(tableName, selection, registry, visited = new Set()) {
709
- if (visited.has(tableName)) {
759
+ function computeViewDefaults(currentRegistryKey, // Renamed for clarity, e.g., "users"
760
+ selection, registry, tableNameToRegistryKeyMap, // Accept the map
761
+ visited = new Set()) {
762
+ if (visited.has(currentRegistryKey)) {
710
763
  return undefined; // Prevent circular references
711
764
  }
712
- visited.add(tableName);
713
- const entry = registry[tableName];
765
+ visited.add(currentRegistryKey);
766
+ // This lookup now uses the correct key every time.
767
+ const entry = registry[currentRegistryKey];
768
+ // This check prevents the crash.
769
+ if (!entry) {
770
+ // This case should ideally not be hit if the map is correct, but it's safe to have.
771
+ console.warn(`Could not find entry for key "${currentRegistryKey}" in registry while computing defaults.`);
772
+ return {};
773
+ }
714
774
  const rawSchema = entry.rawSchema;
715
775
  const baseDefaults = { ...entry.zodSchemas.defaultValues };
716
776
  if (selection === true || typeof selection !== "object") {
@@ -724,13 +784,16 @@ function computeViewDefaults(tableName, selection, registry, visited = new Set()
724
784
  if (!field?.config?.sql?.schema)
725
785
  continue;
726
786
  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;
787
+ // --- THE CORE FIX ---
788
+ // 1. Get the internal _tableName of the related schema (e.g., "post_table")
789
+ const targetTableName = relationConfig.schema()._tableName;
790
+ // 2. Use the map to find the correct registry key for it (e.g., "posts")
791
+ const nextRegistryKey = tableNameToRegistryKeyMap[targetTableName];
792
+ if (!nextRegistryKey)
793
+ continue; // Could not resolve, skip this relation
731
794
  // Handle different default configurations
795
+ const defaultConfig = relationConfig.defaultConfig;
732
796
  if (defaultConfig === undefined) {
733
- // Don't include in defaults
734
797
  delete baseDefaults[key];
735
798
  }
736
799
  else if (defaultConfig === null) {
@@ -739,23 +802,21 @@ function computeViewDefaults(tableName, selection, registry, visited = new Set()
739
802
  else if (Array.isArray(defaultConfig)) {
740
803
  baseDefaults[key] = [];
741
804
  }
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
- }
805
+ else if (relationConfig.type === "hasMany" ||
806
+ relationConfig.type === "manyToMany") {
807
+ const count = defaultConfig?.count || relationConfig.defaultCount || 1;
808
+ baseDefaults[key] = Array.from({ length: count }, () =>
809
+ // 3. Make the recursive call with the CORRECT key
810
+ computeViewDefaults(nextRegistryKey, selection[key], registry, tableNameToRegistryKeyMap, // Pass the map along
811
+ new Set(visited)));
752
812
  }
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)));
813
+ else {
814
+ // hasOne or belongsTo
815
+ baseDefaults[key] =
816
+ // 3. Make the recursive call with the CORRECT key
817
+ computeViewDefaults(nextRegistryKey, selection[key], registry, tableNameToRegistryKeyMap, // Pass the map along
818
+ new Set(visited));
755
819
  }
756
820
  }
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
821
  return baseDefaults;
761
822
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogsbox-shape",
3
- "version": "0.5.117",
3
+ "version": "0.5.119",
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",