cogsbox-shape 0.5.117 → 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.
- package/dist/schema.js +89 -57
- package/package.json +1 -1
package/dist/schema.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { z
|
|
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(
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
537
|
-
|
|
538
|
-
if
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const
|
|
543
|
-
//
|
|
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[
|
|
565
|
+
selectedRelationShapes[relationKey] = z.array(relationSchema);
|
|
546
566
|
}
|
|
547
567
|
else {
|
|
548
|
-
selectedRelationShapes[
|
|
568
|
+
selectedRelationShapes[relationKey] = relationSchema.optional();
|
|
549
569
|
}
|
|
550
570
|
}
|
|
551
571
|
}
|
|
552
572
|
}
|
|
553
|
-
//
|
|
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
|
|
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:
|
|
563
|
-
client: buildView(
|
|
564
|
-
validation: buildView(
|
|
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) {
|
|
@@ -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
716
|
createView: (selection) => {
|
|
696
|
-
const view = createViewObject(tableName, selection, finalRegistry);
|
|
697
|
-
const defaults = computeViewDefaults(tableName, selection, finalRegistry);
|
|
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(
|
|
709
|
-
|
|
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(
|
|
713
|
-
|
|
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
|
-
|
|
728
|
-
//
|
|
729
|
-
|
|
730
|
-
|
|
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 (
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
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
|
|
754
|
-
|
|
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.
|
|
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",
|