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-
|
|
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
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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-
|
|
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.
|
|
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
|
|
68
|
-
"pecunia-root": "^0.1.
|
|
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",
|