pecunia-cli 0.1.6 → 0.1.8

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/api.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { a as generateKyselySchema, n as generateSchema, o as generateDrizzleSchema, r as generatePrismaSchema, t as adapters } from "./generators-BSLjSGA9.mjs";
1
+ import { a as generateKyselySchema, n as generateSchema, o as generateDrizzleSchema, r as generatePrismaSchema, t as adapters } from "./generators-DtWk04TQ.mjs";
2
2
 
3
3
  export { adapters, generateDrizzleSchema, generateKyselySchema, generatePrismaSchema, generateSchema };
@@ -32,89 +32,82 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
32
32
  });
33
33
  const tableNameMap = /* @__PURE__ */ new Map();
34
34
  for (const tableKey in tables) tableNameMap.set(tableKey, getModelName(tableKey));
35
+ function getType(name, field, databaseType$1) {
36
+ name = convertToSnakeCase(name, adapter.options?.camelCase);
37
+ if (field.references?.field === "id") {
38
+ if (databaseType$1 === "mysql") return `varchar('${name}', { length: 36 })`;
39
+ return `text('${name}')`;
40
+ }
41
+ const type = field.type;
42
+ if (typeof type !== "string") {
43
+ if (Array.isArray(type) && type.every((x) => typeof x === "string")) return {
44
+ sqlite: `text({ enum: [${type.map((x) => `'${x}'`).join(", ")}] })`,
45
+ pg: `text('${name}', { enum: [${type.map((x) => `'${x}'`).join(", ")}] })`,
46
+ mysql: `mysqlEnum([${type.map((x) => `'${x}'`).join(", ")}])`
47
+ }[databaseType$1];
48
+ throw new TypeError(`Invalid field type for field ${name}`);
49
+ }
50
+ const dbTypeMap = {
51
+ string: {
52
+ sqlite: `text('${name}')`,
53
+ pg: `text('${name}')`,
54
+ mysql: field.unique ? `varchar('${name}', { length: 255 })` : field.references ? `varchar('${name}', { length: 36 })` : field.sortable ? `varchar('${name}', { length: 255 })` : field.index ? `varchar('${name}', { length: 255 })` : `text('${name}')`
55
+ },
56
+ boolean: {
57
+ sqlite: `integer('${name}', { mode: 'boolean' })`,
58
+ pg: `boolean('${name}')`,
59
+ mysql: `boolean('${name}')`
60
+ },
61
+ number: {
62
+ sqlite: `integer('${name}')`,
63
+ pg: field.bigint ? `bigint('${name}', { mode: 'number' })` : `integer('${name}')`,
64
+ mysql: field.bigint ? `bigint('${name}', { mode: 'number' })` : `int('${name}')`
65
+ },
66
+ date: {
67
+ sqlite: `integer('${name}', { mode: 'timestamp_ms' })`,
68
+ pg: `timestamp('${name}')`,
69
+ mysql: `timestamp('${name}', { fsp: 3 })`
70
+ },
71
+ "number[]": {
72
+ sqlite: `text('${name}', { mode: "json" })`,
73
+ pg: field.bigint ? `bigint('${name}', { mode: 'number' }).array()` : `integer('${name}').array()`,
74
+ mysql: `text('${name}', { mode: 'json' })`
75
+ },
76
+ "string[]": {
77
+ sqlite: `text('${name}', { mode: "json" })`,
78
+ pg: `text('${name}').array()`,
79
+ mysql: `text('${name}', { mode: "json" })`
80
+ },
81
+ json: {
82
+ sqlite: `text('${name}', { mode: "json" })`,
83
+ pg: `jsonb('${name}')`,
84
+ mysql: `json('${name}', { mode: "json" })`
85
+ },
86
+ uuid: {
87
+ sqlite: `text('${name}')`,
88
+ pg: `uuid('${name}')`,
89
+ mysql: `varchar('${name}', { length: 36 })`
90
+ }
91
+ }[type];
92
+ if (!dbTypeMap) throw new Error(`Unsupported field type '${field.type}' for field '${name}'.`);
93
+ return dbTypeMap[databaseType$1];
94
+ }
95
+ const tableDefinitions = [];
35
96
  for (const tableKey in tables) {
36
97
  const table = tables[tableKey];
37
98
  const modelName = getModelName(tableKey);
38
99
  const fields = table.fields;
39
- function getType(name, field) {
40
- name = convertToSnakeCase(name, adapter.options?.camelCase);
41
- if (field.references?.field === "id") {
42
- if (databaseType === "mysql") return `varchar('${name}', { length: 36 })`;
43
- return `text('${name}')`;
44
- }
45
- const type = field.type;
46
- if (typeof type !== "string") {
47
- if (Array.isArray(type) && type.every((x) => typeof x === "string")) return {
48
- sqlite: `text({ enum: [${type.map((x) => `'${x}'`).join(", ")}] })`,
49
- pg: `text('${name}', { enum: [${type.map((x) => `'${x}'`).join(", ")}] })`,
50
- mysql: `mysqlEnum([${type.map((x) => `'${x}'`).join(", ")}])`
51
- }[databaseType];
52
- throw new TypeError(`Invalid field type for field ${name} in model ${modelName}`);
53
- }
54
- const dbTypeMap = {
55
- string: {
56
- sqlite: `text('${name}')`,
57
- pg: `text('${name}')`,
58
- mysql: field.unique ? `varchar('${name}', { length: 255 })` : field.references ? `varchar('${name}', { length: 36 })` : field.sortable ? `varchar('${name}', { length: 255 })` : field.index ? `varchar('${name}', { length: 255 })` : `text('${name}')`
59
- },
60
- boolean: {
61
- sqlite: `integer('${name}', { mode: 'boolean' })`,
62
- pg: `boolean('${name}')`,
63
- mysql: `boolean('${name}')`
64
- },
65
- number: {
66
- sqlite: `integer('${name}')`,
67
- pg: field.bigint ? `bigint('${name}', { mode: 'number' })` : `integer('${name}')`,
68
- mysql: field.bigint ? `bigint('${name}', { mode: 'number' })` : `int('${name}')`
69
- },
70
- date: {
71
- sqlite: `integer('${name}', { mode: 'timestamp_ms' })`,
72
- pg: `timestamp('${name}')`,
73
- mysql: `timestamp('${name}', { fsp: 3 })`
74
- },
75
- "number[]": {
76
- sqlite: `text('${name}', { mode: "json" })`,
77
- pg: field.bigint ? `bigint('${name}', { mode: 'number' }).array()` : `integer('${name}').array()`,
78
- mysql: `text('${name}', { mode: 'json' })`
79
- },
80
- "string[]": {
81
- sqlite: `text('${name}', { mode: "json" })`,
82
- pg: `text('${name}').array()`,
83
- mysql: `text('${name}', { mode: "json" })`
84
- },
85
- json: {
86
- sqlite: `text('${name}', { mode: "json" })`,
87
- pg: `jsonb('${name}')`,
88
- mysql: `json('${name}', { mode: "json" })`
89
- },
90
- uuid: {
91
- sqlite: `text('${name}')`,
92
- pg: `uuid('${name}')`,
93
- mysql: `varchar('${name}', { length: 36 })`
94
- }
95
- }[type];
96
- if (!dbTypeMap) throw new Error(`Unsupported field type '${field.type}' for field '${name}'.`);
97
- return dbTypeMap[databaseType];
98
- }
99
100
  const idFieldType = table.fields.id?.type;
100
101
  let id;
101
102
  if (databaseType === "pg" && idFieldType === "uuid") id = `uuid('id').primaryKey()`;
102
103
  else if (databaseType === "mysql") id = `varchar('id', { length: 36 }).primaryKey()`;
103
104
  else id = `text('id').primaryKey()`;
104
105
  const indexes = [];
105
- const assignIndexes = (indexesToAssign) => {
106
- if (!indexesToAssign.length) return "";
107
- const parts = [`, (table) => [`];
108
- for (const index of indexesToAssign) parts.push(` ${index.type}("${index.name}").on(table.${index.on}),`);
109
- parts.push(`]`);
110
- return parts.join("\n");
111
- };
112
- const schema = `export const ${modelName} = ${databaseType}Table("${convertToSnakeCase(modelName, adapter.options?.camelCase)}", {
113
- id: ${id},
114
- ${Object.keys(fields).filter((field) => field !== "id").map((field) => {
106
+ const references = [];
107
+ for (const field of Object.keys(fields)) {
108
+ if (field === "id") continue;
115
109
  const attr = fields[field];
116
110
  const fieldName = attr.fieldName || field;
117
- let type = getType(fieldName, attr);
118
111
  if (attr.index && !attr.unique) indexes.push({
119
112
  type: "index",
120
113
  name: `${modelName}_${fieldName}_idx`,
@@ -125,6 +118,75 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
125
118
  name: `${modelName}_${fieldName}_uidx`,
126
119
  on: fieldName
127
120
  });
121
+ if (attr.references) {
122
+ const referencedModelName = tableNameMap.get(attr.references.model) || getModelName(attr.references.model);
123
+ references.push({
124
+ fieldName,
125
+ referencedTable: referencedModelName,
126
+ referencedField: getFieldName({
127
+ model: attr.references.model,
128
+ field: attr.references.field
129
+ }),
130
+ onDelete: attr.references.onDelete || "cascade",
131
+ required: attr.required || false,
132
+ originalModel: attr.references.model
133
+ });
134
+ }
135
+ }
136
+ tableDefinitions.push({
137
+ modelName,
138
+ tableKey,
139
+ fields,
140
+ id,
141
+ indexes,
142
+ references
143
+ });
144
+ }
145
+ const modelKeyToTableKey = /* @__PURE__ */ new Map();
146
+ for (const tableKey in tables) {
147
+ const table = tables[tableKey];
148
+ const modelName = getModelName(tableKey);
149
+ modelKeyToTableKey.set(tableKey, tableKey);
150
+ modelKeyToTableKey.set(modelName, tableKey);
151
+ modelKeyToTableKey.set(table.modelName, tableKey);
152
+ }
153
+ const referenceGraph = /* @__PURE__ */ new Map();
154
+ for (const tableDef of tableDefinitions) for (const ref of tableDef.references) {
155
+ const referencedTableKey = modelKeyToTableKey.get(ref.originalModel);
156
+ if (!referencedTableKey) continue;
157
+ const key = `${tableDef.tableKey}->${referencedTableKey}`;
158
+ referenceGraph.set(key, {
159
+ ...ref,
160
+ sourceTable: tableDef.tableKey,
161
+ sourceModelName: tableDef.modelName
162
+ });
163
+ }
164
+ const skipReferences = /* @__PURE__ */ new Set();
165
+ for (const tableDef of tableDefinitions) for (const ref of tableDef.references) {
166
+ const referencedTableKey = modelKeyToTableKey.get(ref.originalModel);
167
+ if (!referencedTableKey) continue;
168
+ const reverseKey = `${referencedTableKey}->${tableDef.tableKey}`;
169
+ const reverseRef = referenceGraph.get(reverseKey);
170
+ if (reverseRef) {
171
+ if (!ref.required && ref.onDelete === "set null" && (reverseRef.required || reverseRef.onDelete !== "set null")) skipReferences.add(`${tableDef.tableKey}.${ref.fieldName}`);
172
+ }
173
+ }
174
+ for (const tableDef of tableDefinitions) {
175
+ const { modelName, fields, id, indexes, references } = tableDef;
176
+ const assignIndexes = (indexesToAssign) => {
177
+ if (!indexesToAssign.length) return "";
178
+ const parts = [`, (table) => [`];
179
+ for (const index of indexesToAssign) parts.push(` ${index.type}("${index.name}").on(table.${index.on}),`);
180
+ parts.push(`]`);
181
+ return parts.join("\n");
182
+ };
183
+ const referenceMap = /* @__PURE__ */ new Map();
184
+ for (const ref of references) referenceMap.set(ref.fieldName, ref);
185
+ const fieldDefinitions = Object.keys(fields).filter((field) => field !== "id").map((field) => {
186
+ const attr = fields[field];
187
+ const fieldName = attr.fieldName || field;
188
+ let type = getType(fieldName, attr, databaseType);
189
+ let comment = "";
128
190
  if (attr.defaultValue !== null && typeof attr.defaultValue !== "undefined") if (typeof attr.defaultValue === "function") {
129
191
  if (attr.type === "date" && attr.defaultValue.toString().includes("new Date()")) if (databaseType === "sqlite") type += `.default(sql\`(cast(unixepoch('subsecond') * 1000 as integer))\`)`;
130
192
  else type += `.defaultNow()`;
@@ -133,11 +195,22 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
133
195
  if (attr.onUpdate && attr.type === "date") {
134
196
  if (typeof attr.onUpdate === "function") type += `.$onUpdate(${attr.onUpdate})`;
135
197
  }
136
- return `${fieldName}: ${type}${attr.required ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${attr.references ? `.references(()=> ${tableNameMap.get(attr.references.model) || getModelName(attr.references.model)}.${getFieldName({
137
- model: attr.references.model,
138
- field: attr.references.field
139
- })}, { onDelete: '${attr.references.onDelete || "cascade"}' })` : ""}`;
140
- }).join(",\n ")}
198
+ const ref = referenceMap.get(fieldName);
199
+ const shouldSkipReference = skipReferences.has(`${tableDef.tableKey}.${fieldName}`);
200
+ let referenceChain = "";
201
+ if (ref && !shouldSkipReference) referenceChain = `.references(() => ${ref.referencedTable}.${ref.referencedField}, { onDelete: '${ref.onDelete}' })`;
202
+ else if (ref && shouldSkipReference) {
203
+ const reverseKey = `${ref.originalModel}->${tableDef.tableKey}`;
204
+ const reverseRef = referenceGraph.get(reverseKey);
205
+ if (reverseRef) comment = `\n // FK constraint removed to break circular dependency with ${ref.referencedTable}\n // Primary FK: ${reverseRef.sourceModelName}.${reverseRef.fieldName} -> ${modelName}.${fieldName}\n // This field still maintains referential integrity via application logic and Drizzle relations`;
206
+ else comment = `\n // FK constraint removed to break circular dependency with ${ref.referencedTable}\n // This field still maintains referential integrity via application logic and Drizzle relations`;
207
+ }
208
+ const fieldDef = `${fieldName}: ${type}${attr.required ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${referenceChain}`;
209
+ return comment ? `${comment}\n ${fieldDef}` : fieldDef;
210
+ });
211
+ const schema = `export const ${modelName} = ${databaseType}Table("${convertToSnakeCase(modelName, adapter.options?.camelCase)}", {
212
+ id: ${id},
213
+ ${fieldDefinitions.join(",\n ")}
141
214
  }${assignIndexes(indexes)});`;
142
215
  code += `\n${schema}\n`;
143
216
  }
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as getPackageInfo, n as generateSchema } from "./generators-BSLjSGA9.mjs";
2
+ import { i as getPackageInfo, n as generateSchema } from "./generators-DtWk04TQ.mjs";
3
3
  import { Command } from "commander";
4
4
  import fs, { existsSync, readFileSync } from "node:fs";
5
5
  import fs$1 from "node:fs/promises";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pecunia-cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "module": "dist/index.mjs",
6
6
  "main": "./dist/index.mjs",
@@ -64,8 +64,8 @@
64
64
  "dotenv": "^17.2.2",
65
65
  "drizzle-orm": "^0.33.0",
66
66
  "open": "^10.2.0",
67
- "pecunia-core": "^0.0.9",
68
- "pecunia-root": "^0.1.7",
67
+ "pecunia-core": "^0.1.0",
68
+ "pecunia-root": "^0.1.8",
69
69
  "pg": "^8.16.3",
70
70
  "prettier": "^3.6.2",
71
71
  "prompts": "^2.4.2",