cogsbox-shape 0.5.70 → 0.5.72

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/cli.js CHANGED
@@ -6,38 +6,48 @@ import { generateSQL } from "./generateSQL.js";
6
6
  import { unlink, writeFile } from "fs/promises";
7
7
  import { spawnSync } from "child_process";
8
8
  const program = new Command();
9
- program.command("generate-sql <file>").action(async (file) => {
9
+ program
10
+ .name("cogsbox-shape")
11
+ .description("CLI for cogsbox-shape schema tools")
12
+ .version("0.5.70");
13
+ program
14
+ .command("generate-sql <file>")
15
+ .description("Generate SQL from your schema definitions")
16
+ .option("-o, --output <path>", "Output SQL file path", "./cogsbox-shape-sql.sql")
17
+ .option("--no-foreign-keys", "Generate SQL without foreign key constraints")
18
+ .action(async (file, options) => {
10
19
  try {
11
20
  const fullPath = path.resolve(process.cwd(), file);
12
21
  if (file.endsWith(".ts")) {
13
- // Create a virtual module that imports and outputs the schema
22
+ // Create a virtual module that imports and USES the schema directly
14
23
  const virtualModule = `
15
- import { schemas } from '${pathToFileURL(fullPath).href}';
16
- console.log(JSON.stringify(schemas));
17
- `;
24
+ import { schemas } from '${pathToFileURL(fullPath).href}';
25
+ import { generateSQL } from '${pathToFileURL(path.join(process.cwd(), "dist/generateSQL.js")).href}';
26
+
27
+ generateSQL(schemas, '${options.output}', { includeForeignKeys: ${options.foreignKeys !== false} })
28
+ .then(() => console.log('Done'))
29
+ .catch(err => console.error(err));
30
+ `;
18
31
  // Write this to a temporary file
19
32
  const tmpFile = path.join(path.dirname(fullPath), ".tmp-schema-loader.ts");
20
33
  await writeFile(tmpFile, virtualModule, "utf8");
21
34
  const result = spawnSync("npx", ["tsx", tmpFile], {
22
35
  encoding: "utf8",
23
- stdio: ["inherit", "pipe", "pipe"],
36
+ stdio: "inherit",
24
37
  });
25
38
  // Clean up temp file
26
39
  await unlink(tmpFile).catch(() => { });
27
40
  if (result.error) {
28
41
  throw result.error;
29
42
  }
30
- if (result.stderr) {
31
- console.error("stderr:", result.stderr);
32
- }
33
- const schema = JSON.parse(result.stdout);
34
- await generateSQL(schema);
35
43
  }
36
44
  else {
37
45
  const schema = await import(pathToFileURL(fullPath).href);
38
- await generateSQL(schema.schemas);
46
+ await generateSQL(schema.schemas, options.output, {
47
+ includeForeignKeys: options.foreignKeys,
48
+ });
39
49
  }
40
- console.log("Generated SQL successfully");
50
+ console.log(`Generated SQL successfully at ${options.output}`);
41
51
  }
42
52
  catch (error) {
43
53
  console.error("Error:", error);
@@ -1,6 +1,7 @@
1
- import type { Schema } from "./schema";
2
- type SchemaInput = Record<string, Schema<any>> | {
3
- schemas: Record<string, Schema<any>>;
1
+ type SchemaInput = Record<string, any> | {
2
+ schemas: Record<string, any>;
4
3
  };
5
- export declare function generateSQL(input: SchemaInput, outputPath?: string): Promise<string>;
4
+ export declare function generateSQL(input: SchemaInput, outputPath?: string, options?: {
5
+ includeForeignKeys?: boolean;
6
+ }): Promise<string>;
6
7
  export {};
@@ -1,5 +1,4 @@
1
1
  import fs from "fs/promises";
2
- // SQL Type mapping
3
2
  const sqlTypeMap = {
4
3
  int: "INTEGER",
5
4
  varchar: (length = 255) => `VARCHAR(${length})`,
@@ -17,46 +16,105 @@ function isWrappedSchema(input) {
17
16
  input.schemas !== null &&
18
17
  typeof input.schemas === "object");
19
18
  }
20
- export async function generateSQL(input, outputPath = "cogsbox-shape-sql.sql") {
19
+ export async function generateSQL(input, outputPath = "cogsbox-shape-sql.sql", options = { includeForeignKeys: true }) {
21
20
  if (!input) {
22
21
  throw new Error("No schema input provided");
23
22
  }
24
- // Extract schemas using type guard
25
23
  const schemas = isWrappedSchema(input) ? input.schemas : input;
26
24
  if (!schemas || typeof schemas !== "object") {
27
25
  throw new Error("Invalid schemas input");
28
26
  }
29
27
  const sql = [];
30
- // Generate SQL for each schema
31
28
  for (const [name, schema] of Object.entries(schemas)) {
32
29
  const tableName = schema._tableName;
30
+ if (!tableName) {
31
+ console.warn(`Skipping schema '${name}' - no _tableName found`);
32
+ continue;
33
+ }
33
34
  const fields = [];
34
35
  const foreignKeys = [];
35
- // Process each field in the schema
36
36
  for (const [fieldName, field] of Object.entries(schema)) {
37
- if (fieldName === "_tableName")
37
+ // Skip metadata fields
38
+ const f = field; // Just cast once
39
+ console.log(`Processing field: ${fieldName}`, f);
40
+ // Skip metadata fields
41
+ if (fieldName === "_tableName" ||
42
+ fieldName === "SchemaWrapperBrand" ||
43
+ fieldName.startsWith("__") ||
44
+ typeof f !== "object" ||
45
+ !f)
46
+ continue;
47
+ // Handle reference fields
48
+ if (f.type === "reference" && f.to) {
49
+ const referencedField = f.to();
50
+ const targetTableName = referencedField.__parentTableType._tableName;
51
+ const targetFieldName = referencedField.__meta._key;
52
+ console.log(`Found reference field: ${fieldName} -> ${targetTableName}.${targetFieldName}`);
53
+ fields.push(` ${fieldName} INTEGER NOT NULL`);
54
+ if (options.includeForeignKeys) {
55
+ foreignKeys.push(` FOREIGN KEY (${fieldName}) REFERENCES ${targetTableName}(${targetFieldName})`);
56
+ }
38
57
  continue;
39
- // Handle regular fields
40
- if ("sql" in field) {
41
- const { type, nullable, pk, length } = field.sql;
58
+ }
59
+ // Get the actual field definition from enriched structure
60
+ let fieldDef = f;
61
+ // If it's an enriched field, extract the original field definition
62
+ if (f.__meta && f.__meta._fieldType) {
63
+ fieldDef = f.__meta._fieldType;
64
+ }
65
+ // Now check if fieldDef has config
66
+ if (fieldDef && fieldDef.config && fieldDef.config.sql) {
67
+ const sqlConfig = fieldDef.config.sql;
68
+ // Handle relation configs (hasMany, hasOne, etc.)
69
+ if (["hasMany", "hasOne", "belongsTo", "manyToMany"].includes(sqlConfig.type)) {
70
+ // Only belongsTo creates a column
71
+ if (sqlConfig.type === "belongsTo" &&
72
+ sqlConfig.fromKey &&
73
+ sqlConfig.schema) {
74
+ fields.push(` ${sqlConfig.fromKey} INTEGER`);
75
+ if (options.includeForeignKeys) {
76
+ const targetSchema = sqlConfig.schema();
77
+ foreignKeys.push(` FOREIGN KEY (${sqlConfig.fromKey}) REFERENCES ${targetSchema._tableName}(id)`);
78
+ }
79
+ }
80
+ continue;
81
+ }
82
+ // Handle regular SQL types
83
+ const { type, nullable, pk, length, default: defaultValue } = sqlConfig;
84
+ if (!sqlTypeMap[type]) {
85
+ console.warn(`Unknown SQL type: ${type} for field ${fieldName}`);
86
+ continue;
87
+ }
42
88
  const sqlType = typeof sqlTypeMap[type] === "function"
43
89
  ? sqlTypeMap[type](length)
44
90
  : sqlTypeMap[type];
45
- fields.push(` ${fieldName} ${sqlType}${pk ? " PRIMARY KEY" : ""}${nullable ? "" : " NOT NULL"}`);
46
- }
47
- // Handle relations
48
- if (typeof field === "function") {
49
- const relation = field();
50
- if (relation.type === "belongsTo") {
51
- fields.push(` ${relation.fromKey} INTEGER`);
52
- foreignKeys.push(` FOREIGN KEY (${relation.fromKey}) REFERENCES ${relation.schema._tableName}(id)`);
91
+ let fieldDefStr = ` ${fieldName} ${sqlType}`;
92
+ if (pk)
93
+ fieldDefStr += " PRIMARY KEY AUTO_INCREMENT";
94
+ if (!nullable && !pk)
95
+ fieldDefStr += " NOT NULL";
96
+ // Handle defaults
97
+ if (defaultValue !== undefined &&
98
+ defaultValue !== "CURRENT_TIMESTAMP") {
99
+ fieldDefStr += ` DEFAULT ${typeof defaultValue === "string" ? `'${defaultValue}'` : defaultValue}`;
100
+ }
101
+ else if (defaultValue === "CURRENT_TIMESTAMP") {
102
+ fieldDefStr += " DEFAULT CURRENT_TIMESTAMP";
53
103
  }
104
+ fields.push(fieldDefStr);
54
105
  }
55
106
  }
56
- // Combine fields and foreign keys
57
- const allFields = [...fields, ...foreignKeys];
107
+ // Combine fields and foreign keys based on option
108
+ const allFields = options.includeForeignKeys
109
+ ? [...fields, ...foreignKeys]
110
+ : fields;
58
111
  // Create table SQL
59
- sql.push(`CREATE TABLE ${tableName} (\n${allFields.join(",\n")}\n);\n`);
112
+ if (allFields.length > 0) {
113
+ sql.push(`CREATE TABLE ${tableName} (\n${allFields.join(",\n")}\n);\n`);
114
+ }
115
+ else {
116
+ console.warn(`Warning: Table ${tableName} has no fields`);
117
+ }
60
118
  }
61
119
  // Write to file
62
120
  const sqlContent = sql.join("\n");
package/dist/schema.d.ts CHANGED
@@ -55,12 +55,24 @@ type SQLToZodType<T extends SQLType, TDefault extends boolean> = T["pk"] extends
55
55
  default: "CURRENT_TIMESTAMP";
56
56
  } ? TDefault extends true ? never : z.ZodDate : z.ZodDate : never;
57
57
  type ZodTypeFromPrimitive<T> = T extends string ? z.ZodString : T extends number ? z.ZodNumber : T extends boolean ? z.ZodBoolean : T extends Date ? z.ZodDate : z.ZodAny;
58
+ type IsLiteralType<T> = T extends string ? string extends T ? false : true : T extends number ? number extends T ? false : true : T extends boolean ? boolean extends T ? false : true : false;
58
59
  interface IBuilderMethods<T extends SQLType | RelationConfig<any>, TSql extends z.ZodTypeAny, TNew extends z.ZodTypeAny, TInitialValue, TClient extends z.ZodTypeAny, TValidation extends z.ZodTypeAny> {
59
60
  initialState: {
60
61
  <const TResult>(defaultValue: TResult): TResult extends () => infer R ? R extends z.ZodTypeAny ? Prettify<Builder<"new", T, TSql, R, z.infer<R>, InferSmartClientType<TSql, R>, InferSmartClientType<TSql, R>>> : Prettify<Builder<"new", T, TSql, z.ZodLiteral<R>, R, z.ZodUnion<[TSql, z.ZodLiteral<R>]>, z.ZodUnion<[TSql, z.ZodLiteral<R>]>>> : TResult extends z.ZodTypeAny ? Prettify<Builder<"new", T, TSql, TResult, z.infer<TResult>, InferSmartClientType<TSql, TResult>, InferSmartClientType<TSql, TResult>>> : TResult extends string | number | boolean ? Prettify<Builder<"new", T, TSql, z.ZodLiteral<TResult>, TResult, z.ZodUnion<[TSql, z.ZodLiteral<TResult>]>, z.ZodUnion<[TSql, z.ZodLiteral<TResult>]>>> : Prettify<Builder<"new", T, TSql, ZodTypeFromPrimitive<TResult>, TResult, InferSmartClientType<TSql, ZodTypeFromPrimitive<TResult>>, InferSmartClientType<TSql, ZodTypeFromPrimitive<TResult>>>>;
61
62
  <TNewNext extends z.ZodTypeAny, const TDefaultNext>(schema: ((tools: {
62
63
  sql: TSql;
63
- }) => TNewNext) | TNewNext, defaultValue: TDefaultNext | (() => TDefaultNext)): Prettify<Builder<"new", T, TSql, TNewNext, TDefaultNext extends () => infer R ? R : TDefaultNext, InferSmartClientType<TSql, TNewNext>, InferSmartClientType<TSql, TNewNext>>>;
64
+ }) => TNewNext) | TNewNext, defaultValue: TDefaultNext): Prettify<Builder<"new", T, TSql, z.ZodUnion<[
65
+ TNewNext,
66
+ z.ZodLiteral<TDefaultNext extends () => infer R ? R : TDefaultNext>
67
+ ]>, IsLiteralType<z.infer<TNewNext>> extends true ? TDefaultNext extends () => infer R ? R : TDefaultNext : z.infer<TNewNext>, z.ZodUnion<[
68
+ TSql,
69
+ TNewNext,
70
+ z.ZodLiteral<TDefaultNext extends () => infer R ? R : TDefaultNext>
71
+ ]>, z.ZodUnion<[
72
+ TSql,
73
+ TNewNext,
74
+ z.ZodLiteral<TDefaultNext extends () => infer R ? R : TDefaultNext>
75
+ ]>>>;
64
76
  };
65
77
  client: <TClientNext extends z.ZodTypeAny>(schema: ((tools: {
66
78
  sql: TSql;
package/dist/schema.js CHANGED
@@ -156,8 +156,12 @@ function createBuilder(config) {
156
156
  : schemaOrDefault
157
157
  : config.sqlZod; // If only a primitive is passed, the "new" schema is still the SQL one.
158
158
  const finalDefaultValue = hasSchemaArg
159
- ? defaultValue()
160
- : schemaOrDefault;
159
+ ? isFunction(defaultValue)
160
+ ? defaultValue() // If it's a function, call it
161
+ : defaultValue // If it's a direct value, use it as-is
162
+ : isFunction(schemaOrDefault)
163
+ ? schemaOrDefault()
164
+ : schemaOrDefault;
161
165
  const newCompletedStages = new Set(completedStages);
162
166
  newCompletedStages.add("new");
163
167
  // ---- THIS IS THE RUNTIME FIX THAT MATCHES YOUR INTERFACE ----
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogsbox-shape",
3
- "version": "0.5.70",
3
+ "version": "0.5.72",
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",