pecunia-cli 0.3.0 → 0.3.2
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.d.mts +0 -1
- package/dist/api.mjs +1 -1
- package/dist/{generators-DyDUFaYK.mjs → generators-CWgRQHR-.mjs} +160 -334
- package/dist/index.mjs +1 -1
- package/package.json +3 -3
package/dist/api.d.mts
CHANGED
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-CWgRQHR-.mjs";
|
|
2
2
|
|
|
3
3
|
export { adapters, generateDrizzleSchema, generateKyselySchema, generatePrismaSchema, generateSchema };
|
|
@@ -13,14 +13,10 @@ function convertToSnakeCase(str, camelCase) {
|
|
|
13
13
|
}
|
|
14
14
|
const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
15
15
|
const tables = getPaymentTables(options);
|
|
16
|
-
const filePath = file || "./
|
|
16
|
+
const filePath = file || "./schema.ts";
|
|
17
17
|
const databaseType = adapter.options?.provider;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const resolvedSchemaPath = path.isAbsolute(filePath) ? filePath : path.resolve(projectRoot, filePath);
|
|
21
|
-
const schemaDir = path.dirname(resolvedSchemaPath);
|
|
22
|
-
if (!existsSync(schemaDir)) await fs$1.mkdir(schemaDir, { recursive: true });
|
|
23
|
-
const fileExist = existsSync(resolvedSchemaPath);
|
|
18
|
+
if (!databaseType) throw new Error(`Database provider type is undefined during Drizzle schema generation. Please define a \`provider\` in the Drizzle adapter config. Read more at https://better-auth.com/docs/adapters/drizzle`);
|
|
19
|
+
const fileExist = existsSync(filePath);
|
|
24
20
|
let code = generateImport({
|
|
25
21
|
databaseType,
|
|
26
22
|
tables,
|
|
@@ -34,84 +30,83 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
|
34
30
|
schema: tables,
|
|
35
31
|
usePlural: adapter.options?.adapterConfig?.usePlural
|
|
36
32
|
});
|
|
37
|
-
const tableNameMap = /* @__PURE__ */ new Map();
|
|
38
|
-
for (const tableKey in tables) tableNameMap.set(tableKey, getModelName(tableKey));
|
|
39
|
-
function getType(name, field, databaseType$1) {
|
|
40
|
-
name = convertToSnakeCase(name, adapter.options?.camelCase);
|
|
41
|
-
if (field.references?.field === "id") {
|
|
42
|
-
if (databaseType$1 === "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$1];
|
|
52
|
-
throw new TypeError(`Invalid field type for field ${name}`);
|
|
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$1];
|
|
98
|
-
}
|
|
99
|
-
const tableDefinitions = [];
|
|
100
33
|
for (const tableKey in tables) {
|
|
101
34
|
const table = tables[tableKey];
|
|
102
35
|
const modelName = getModelName(tableKey);
|
|
103
36
|
const fields = table.fields;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
37
|
+
function getType(name, field) {
|
|
38
|
+
if (!databaseType) throw new Error(`Database provider type is undefined during Drizzle schema generation. Please define a \`provider\` in the Drizzle adapter config. Read more at https://better-auth.com/docs/adapters/drizzle`);
|
|
39
|
+
name = convertToSnakeCase(name, adapter.options?.camelCase);
|
|
40
|
+
if (field.references?.field === "id") {
|
|
41
|
+
if (databaseType === "pg") return `uuid('${name}')`;
|
|
42
|
+
if (databaseType === "mysql") return `varchar('${name}', { length: 36 })`;
|
|
43
|
+
return `text('${name}')`;
|
|
44
|
+
}
|
|
45
|
+
const type = field.type;
|
|
46
|
+
if (typeof type !== "string") if (Array.isArray(type) && type.every((x) => typeof x === "string")) return {
|
|
47
|
+
sqlite: `text({ enum: [${type.map((x) => `'${x}'`).join(", ")}] })`,
|
|
48
|
+
pg: `text('${name}', { enum: [${type.map((x) => `'${x}'`).join(", ")}] })`,
|
|
49
|
+
mysql: `mysqlEnum([${type.map((x) => `'${x}'`).join(", ")}])`
|
|
50
|
+
}[databaseType];
|
|
51
|
+
else throw new TypeError(`Invalid field type for field ${name} in model ${modelName}`);
|
|
52
|
+
const dbTypeMap = {
|
|
53
|
+
string: {
|
|
54
|
+
sqlite: `text('${name}')`,
|
|
55
|
+
pg: `text('${name}')`,
|
|
56
|
+
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}')`
|
|
57
|
+
},
|
|
58
|
+
boolean: {
|
|
59
|
+
sqlite: `integer('${name}', { mode: 'boolean' })`,
|
|
60
|
+
pg: `boolean('${name}')`,
|
|
61
|
+
mysql: `boolean('${name}')`
|
|
62
|
+
},
|
|
63
|
+
number: {
|
|
64
|
+
sqlite: `integer('${name}')`,
|
|
65
|
+
pg: field.bigint ? `bigint('${name}', { mode: 'number' })` : `integer('${name}')`,
|
|
66
|
+
mysql: field.bigint ? `bigint('${name}', { mode: 'number' })` : `int('${name}')`
|
|
67
|
+
},
|
|
68
|
+
date: {
|
|
69
|
+
sqlite: `integer('${name}', { mode: 'timestamp_ms' })`,
|
|
70
|
+
pg: `timestamp('${name}')`,
|
|
71
|
+
mysql: `timestamp('${name}', { fsp: 3 })`
|
|
72
|
+
},
|
|
73
|
+
"number[]": {
|
|
74
|
+
sqlite: `text('${name}', { mode: "json" })`,
|
|
75
|
+
pg: field.bigint ? `bigint('${name}', { mode: 'number' }).array()` : `integer('${name}').array()`,
|
|
76
|
+
mysql: `text('${name}', { mode: "json" })`
|
|
77
|
+
},
|
|
78
|
+
"string[]": {
|
|
79
|
+
sqlite: `text('${name}', { mode: "json" })`,
|
|
80
|
+
pg: `text('${name}').array()`,
|
|
81
|
+
mysql: `text('${name}', { mode: "json" })`
|
|
82
|
+
},
|
|
83
|
+
json: {
|
|
84
|
+
sqlite: `text('${name}', { mode: "json" })`,
|
|
85
|
+
pg: `jsonb('${name}')`,
|
|
86
|
+
mysql: `json('${name}', { mode: "json" })`
|
|
87
|
+
}
|
|
88
|
+
}[type];
|
|
89
|
+
if (!dbTypeMap) throw new Error(`Unsupported field type '${field.type}' for field '${name}'.`);
|
|
90
|
+
return dbTypeMap[databaseType];
|
|
91
|
+
}
|
|
92
|
+
let id = "";
|
|
93
|
+
if (databaseType === "pg") id = `uuid("id").default(sql\`gen_random_uuid()\`).primaryKey()`;
|
|
94
|
+
else if (databaseType === "mysql") id = `varchar("id", { length: 36 }).primaryKey()`;
|
|
95
|
+
else id = `text("id").primaryKey()`;
|
|
96
|
+
let indexes = [];
|
|
97
|
+
const assignIndexes = (indexes$1) => {
|
|
98
|
+
if (!indexes$1.length) return "";
|
|
99
|
+
let code$1 = [`, (table) => [`];
|
|
100
|
+
for (const index of indexes$1) code$1.push(` ${index.type}("${index.name}").on(table.${index.on}),`);
|
|
101
|
+
code$1.push(`]`);
|
|
102
|
+
return code$1.join("\n");
|
|
103
|
+
};
|
|
104
|
+
const schema = `export const ${modelName} = ${databaseType}Table("${convertToSnakeCase(modelName, adapter.options?.camelCase)}", {
|
|
105
|
+
id: ${id},
|
|
106
|
+
${Object.keys(fields).map((field) => {
|
|
113
107
|
const attr = fields[field];
|
|
114
108
|
const fieldName = attr.fieldName || field;
|
|
109
|
+
let type = getType(fieldName, attr);
|
|
115
110
|
if (attr.index && !attr.unique) indexes.push({
|
|
116
111
|
type: "index",
|
|
117
112
|
name: `${modelName}_${fieldName}_idx`,
|
|
@@ -122,80 +117,6 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
|
122
117
|
name: `${modelName}_${fieldName}_uidx`,
|
|
123
118
|
on: fieldName
|
|
124
119
|
});
|
|
125
|
-
if (attr.references) {
|
|
126
|
-
const referencedModelName = tableNameMap.get(attr.references.model) || getModelName(attr.references.model);
|
|
127
|
-
const onDelete = attr.references.onDelete || "no action";
|
|
128
|
-
references.push({
|
|
129
|
-
fieldName,
|
|
130
|
-
referencedTable: referencedModelName,
|
|
131
|
-
referencedField: getFieldName({
|
|
132
|
-
model: attr.references.model,
|
|
133
|
-
field: attr.references.field
|
|
134
|
-
}),
|
|
135
|
-
onDelete,
|
|
136
|
-
required: attr.required ?? false,
|
|
137
|
-
originalModel: attr.references.model
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
tableDefinitions.push({
|
|
142
|
-
modelName,
|
|
143
|
-
tableKey,
|
|
144
|
-
fields,
|
|
145
|
-
id,
|
|
146
|
-
indexes,
|
|
147
|
-
references
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
const modelKeyToTableKey = /* @__PURE__ */ new Map();
|
|
151
|
-
for (const tableKey in tables) {
|
|
152
|
-
const table = tables[tableKey];
|
|
153
|
-
const modelName = getModelName(tableKey);
|
|
154
|
-
modelKeyToTableKey.set(tableKey, tableKey);
|
|
155
|
-
modelKeyToTableKey.set(modelName, tableKey);
|
|
156
|
-
modelKeyToTableKey.set(table.modelName, tableKey);
|
|
157
|
-
}
|
|
158
|
-
const referenceGraph = /* @__PURE__ */ new Map();
|
|
159
|
-
for (const tableDef of tableDefinitions) for (const ref of tableDef.references) {
|
|
160
|
-
const referencedTableKey = modelKeyToTableKey.get(ref.originalModel);
|
|
161
|
-
if (!referencedTableKey) continue;
|
|
162
|
-
const key = `${tableDef.tableKey}->${referencedTableKey}`;
|
|
163
|
-
referenceGraph.set(key, {
|
|
164
|
-
...ref,
|
|
165
|
-
sourceTable: tableDef.tableKey,
|
|
166
|
-
sourceModelName: tableDef.modelName
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
const skipReferences = /* @__PURE__ */ new Set();
|
|
170
|
-
for (const tableDef of tableDefinitions) for (const ref of tableDef.references) {
|
|
171
|
-
const referencedTableKey = modelKeyToTableKey.get(ref.originalModel);
|
|
172
|
-
if (!referencedTableKey) continue;
|
|
173
|
-
const reverseKey = `${referencedTableKey}->${tableDef.tableKey}`;
|
|
174
|
-
const reverseRef = referenceGraph.get(reverseKey);
|
|
175
|
-
if (reverseRef) {
|
|
176
|
-
const thisIsNullableWithSetNull = !ref.required && ref.onDelete === "set null";
|
|
177
|
-
const reverseIsNullableWithSetNull = !reverseRef.required && reverseRef.onDelete === "set null";
|
|
178
|
-
if (thisIsNullableWithSetNull) continue;
|
|
179
|
-
if (reverseIsNullableWithSetNull) continue;
|
|
180
|
-
if (!ref.required && (reverseRef.required || ref.onDelete !== "cascade" && reverseRef.onDelete === "cascade")) skipReferences.add(`${tableDef.tableKey}.${ref.fieldName}`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
for (const tableDef of tableDefinitions) {
|
|
184
|
-
const { modelName, fields, id, indexes, references } = tableDef;
|
|
185
|
-
const assignIndexes = (indexesToAssign) => {
|
|
186
|
-
if (!indexesToAssign.length) return "";
|
|
187
|
-
const parts = [`, (table) => [`];
|
|
188
|
-
for (const index of indexesToAssign) parts.push(` ${index.type}("${index.name}").on(table.${index.on}),`);
|
|
189
|
-
parts.push(`]`);
|
|
190
|
-
return parts.join("\n");
|
|
191
|
-
};
|
|
192
|
-
const referenceMap = /* @__PURE__ */ new Map();
|
|
193
|
-
for (const ref of references) referenceMap.set(ref.fieldName, ref);
|
|
194
|
-
const fieldDefinitions = Object.keys(fields).filter((field) => field !== "id").map((field) => {
|
|
195
|
-
const attr = fields[field];
|
|
196
|
-
const fieldName = attr.fieldName || field;
|
|
197
|
-
let type = getType(fieldName, attr, databaseType);
|
|
198
|
-
let comment = "";
|
|
199
120
|
if (attr.defaultValue !== null && typeof attr.defaultValue !== "undefined") if (typeof attr.defaultValue === "function") {
|
|
200
121
|
if (attr.type === "date" && attr.defaultValue.toString().includes("new Date()")) if (databaseType === "sqlite") type += `.default(sql\`(cast(unixepoch('subsecond') * 1000 as integer))\`)`;
|
|
201
122
|
else type += `.defaultNow()`;
|
|
@@ -204,23 +125,11 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
|
204
125
|
if (attr.onUpdate && attr.type === "date") {
|
|
205
126
|
if (typeof attr.onUpdate === "function") type += `.$onUpdate(${attr.onUpdate})`;
|
|
206
127
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const reverseKey = `${ref.originalModel}->${tableDef.tableKey}`;
|
|
213
|
-
const reverseRef = referenceGraph.get(reverseKey);
|
|
214
|
-
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`;
|
|
215
|
-
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`;
|
|
216
|
-
}
|
|
217
|
-
const isRequired = attr.required === true;
|
|
218
|
-
const fieldDef = `${fieldName}: ${type}${isRequired ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${referenceChain}`;
|
|
219
|
-
return comment ? `${comment}\n ${fieldDef}` : fieldDef;
|
|
220
|
-
});
|
|
221
|
-
const schema = `export const ${modelName} = ${databaseType}Table("${convertToSnakeCase(modelName, adapter.options?.camelCase)}", {
|
|
222
|
-
id: ${id},
|
|
223
|
-
${fieldDefinitions.join(",\n ")}
|
|
128
|
+
return `${fieldName}: ${type}${attr.required ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${attr.references ? `.references(()=> ${getModelName(attr.references.model)}.${getFieldName({
|
|
129
|
+
model: attr.references.model,
|
|
130
|
+
field: attr.references.field
|
|
131
|
+
})}, { onDelete: '${attr.references.onDelete || "cascade"}' })` : ""}`;
|
|
132
|
+
}).join(",\n ")}
|
|
224
133
|
}${assignIndexes(indexes)});`;
|
|
225
134
|
code += `\n${schema}\n`;
|
|
226
135
|
}
|
|
@@ -230,118 +139,72 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
|
230
139
|
const modelName = getModelName(tableKey);
|
|
231
140
|
const oneRelations = [];
|
|
232
141
|
const manyRelations = [];
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const referenceRef = `${referencedModelName}.${getFieldName({
|
|
255
|
-
model: relationDef.model,
|
|
256
|
-
field: foreignKeyField.references.field || "id"
|
|
257
|
-
})}`;
|
|
258
|
-
oneRelations.push({
|
|
259
|
-
key: relationName,
|
|
260
|
-
model: referencedModelName,
|
|
261
|
-
type: "one",
|
|
262
|
-
reference: {
|
|
263
|
-
field: fieldRef,
|
|
264
|
-
references: referenceRef,
|
|
265
|
-
fieldName: relationDef.foreignKey
|
|
266
|
-
},
|
|
267
|
-
relationName: isSelfReferential ? generateRelationName(relationDef.foreignKey) : void 0
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
} else if (relationDef.kind === "many") {
|
|
271
|
-
const referencedTable = tables[relationDef.model];
|
|
272
|
-
if (referencedTable) {
|
|
273
|
-
const fkField = Object.entries(referencedTable.fields).find(([_, field]) => field.references && (field.references.model === tableKey || field.references.model === getModelName(tableKey)));
|
|
274
|
-
if (fkField) {
|
|
275
|
-
const [fkFieldName] = fkField;
|
|
276
|
-
const fieldRef = `${referencedModelName}.${getFieldName({
|
|
277
|
-
model: relationDef.model,
|
|
278
|
-
field: fkFieldName
|
|
279
|
-
})}`;
|
|
280
|
-
const referenceRef = `${modelName}.${getFieldName({
|
|
281
|
-
model: tableKey,
|
|
282
|
-
field: "id"
|
|
283
|
-
})}`;
|
|
284
|
-
let relationNameForMany;
|
|
285
|
-
if (isSelfReferential) {
|
|
286
|
-
const matchingOne = oneRelations.find((rel) => rel.reference?.fieldName === fkFieldName && rel.model === referencedModelName);
|
|
287
|
-
if (matchingOne?.relationName) relationNameForMany = matchingOne.relationName;
|
|
288
|
-
else {
|
|
289
|
-
let cleaned = convertToSnakeCase(fkFieldName, adapter.options?.camelCase).replace(/_by_id$/, "").replace(/_id$/, "");
|
|
290
|
-
const participleToNoun = {
|
|
291
|
-
"reversed": "reversal",
|
|
292
|
-
"created": "creation",
|
|
293
|
-
"updated": "update"
|
|
294
|
-
};
|
|
295
|
-
if (participleToNoun[cleaned]) cleaned = participleToNoun[cleaned];
|
|
296
|
-
relationNameForMany = `${convertToSnakeCase(modelName, adapter.options?.camelCase)}_${cleaned}`;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
manyRelations.push({
|
|
300
|
-
key: relationName,
|
|
301
|
-
model: referencedModelName,
|
|
302
|
-
type: "many",
|
|
303
|
-
reference: {
|
|
304
|
-
field: fieldRef,
|
|
305
|
-
references: referenceRef,
|
|
306
|
-
fieldName: fkFieldName
|
|
307
|
-
},
|
|
308
|
-
relationName: relationNameForMany
|
|
309
|
-
});
|
|
310
|
-
} else manyRelations.push({
|
|
311
|
-
key: relationName,
|
|
312
|
-
model: referencedModelName,
|
|
313
|
-
type: "many"
|
|
314
|
-
});
|
|
315
|
-
}
|
|
142
|
+
const manyRelationsSet = /* @__PURE__ */ new Set();
|
|
143
|
+
const foreignFields = Object.entries(table.fields).filter(([_, field]) => field.references);
|
|
144
|
+
for (const [fieldName, field] of foreignFields) {
|
|
145
|
+
const referencedModel = field.references.model;
|
|
146
|
+
const relationKey = getModelName(referencedModel);
|
|
147
|
+
const fieldRef = `${getModelName(tableKey)}.${getFieldName({
|
|
148
|
+
model: tableKey,
|
|
149
|
+
field: fieldName
|
|
150
|
+
})}`;
|
|
151
|
+
const referenceRef = `${getModelName(referencedModel)}.${getFieldName({
|
|
152
|
+
model: referencedModel,
|
|
153
|
+
field: field.references.field || "id"
|
|
154
|
+
})}`;
|
|
155
|
+
oneRelations.push({
|
|
156
|
+
key: relationKey,
|
|
157
|
+
model: getModelName(referencedModel),
|
|
158
|
+
type: "one",
|
|
159
|
+
reference: {
|
|
160
|
+
field: fieldRef,
|
|
161
|
+
references: referenceRef,
|
|
162
|
+
fieldName
|
|
316
163
|
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const otherModels = Object.entries(tables).filter(([modelName$1]) => modelName$1 !== tableKey);
|
|
167
|
+
const modelRelationsMap = /* @__PURE__ */ new Map();
|
|
168
|
+
for (const [modelName$1, otherTable] of otherModels) {
|
|
169
|
+
const foreignKeysPointingHere = Object.entries(otherTable.fields).filter(([_, field]) => field.references?.model === tableKey || field.references?.model === getModelName(tableKey));
|
|
170
|
+
if (foreignKeysPointingHere.length === 0) continue;
|
|
171
|
+
const hasUnique = foreignKeysPointingHere.some(([_, field]) => !!field.unique);
|
|
172
|
+
const hasMany$1 = foreignKeysPointingHere.some(([_, field]) => !field.unique);
|
|
173
|
+
modelRelationsMap.set(modelName$1, {
|
|
174
|
+
modelName: modelName$1,
|
|
175
|
+
hasUnique,
|
|
176
|
+
hasMany: hasMany$1
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
for (const { modelName: modelName$1, hasMany: hasMany$1 } of modelRelationsMap.values()) {
|
|
180
|
+
const relationType = hasMany$1 ? "many" : "one";
|
|
181
|
+
let relationKey = getModelName(modelName$1);
|
|
182
|
+
if (!adapter.options?.adapterConfig?.usePlural && relationType === "many") relationKey = `${relationKey}s`;
|
|
183
|
+
if (!manyRelationsSet.has(relationKey)) {
|
|
184
|
+
manyRelationsSet.add(relationKey);
|
|
185
|
+
manyRelations.push({
|
|
186
|
+
key: relationKey,
|
|
187
|
+
model: getModelName(modelName$1),
|
|
188
|
+
type: relationType
|
|
189
|
+
});
|
|
323
190
|
}
|
|
324
191
|
}
|
|
325
192
|
const relationsByModel = /* @__PURE__ */ new Map();
|
|
326
|
-
for (const relation of oneRelations) {
|
|
327
|
-
if (!relation.reference) continue;
|
|
193
|
+
for (const relation of oneRelations) if (relation.reference) {
|
|
328
194
|
const modelKey = relation.key;
|
|
329
195
|
if (!relationsByModel.has(modelKey)) relationsByModel.set(modelKey, []);
|
|
330
196
|
relationsByModel.get(modelKey).push(relation);
|
|
331
197
|
}
|
|
332
198
|
const duplicateRelations = [];
|
|
333
199
|
const singleRelations = [];
|
|
334
|
-
for (const [_modelKey,
|
|
335
|
-
else singleRelations.push(
|
|
336
|
-
for (const relation of duplicateRelations) {
|
|
337
|
-
if (!relation.reference) continue;
|
|
200
|
+
for (const [_modelKey, relations] of relationsByModel.entries()) if (relations.length > 1) duplicateRelations.push(...relations);
|
|
201
|
+
else singleRelations.push(relations[0]);
|
|
202
|
+
for (const relation of duplicateRelations) if (relation.reference) {
|
|
338
203
|
const fieldName = relation.reference.fieldName;
|
|
339
|
-
const
|
|
340
|
-
const relationNameParam = relation.relationName ? `,\n relationName: "${relation.relationName}"` : "";
|
|
341
|
-
const tableRelation = `export const ${relationExportName} = relations(${modelName}, ({ one }) => ({
|
|
204
|
+
const tableRelation = `export const ${`${modelName}${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}Relations`} = relations(${getModelName(table.modelName)}, ({ one }) => ({
|
|
342
205
|
${relation.key}: one(${relation.model}, {
|
|
343
206
|
fields: [${relation.reference.field}],
|
|
344
|
-
references: [${relation.reference.references}]
|
|
207
|
+
references: [${relation.reference.references}],
|
|
345
208
|
})
|
|
346
209
|
}))`;
|
|
347
210
|
relationsString += `\n${tableRelation}\n`;
|
|
@@ -349,37 +212,25 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
|
349
212
|
const hasOne = singleRelations.length > 0;
|
|
350
213
|
const hasMany = manyRelations.length > 0;
|
|
351
214
|
if (hasOne && hasMany) {
|
|
352
|
-
const tableRelation = `export const ${modelName}Relations = relations(${modelName}, ({ one, many }) => ({
|
|
353
|
-
${singleRelations.map((relation) => {
|
|
354
|
-
if (!relation.reference) return "";
|
|
355
|
-
const relationNameParam = relation.relationName ? `,\n relationName: "${relation.relationName}"` : "";
|
|
356
|
-
return ` ${relation.key}: one(${relation.model}, {
|
|
215
|
+
const tableRelation = `export const ${modelName}Relations = relations(${getModelName(table.modelName)}, ({ one, many }) => ({
|
|
216
|
+
${singleRelations.map((relation) => relation.reference ? ` ${relation.key}: one(${relation.model}, {
|
|
357
217
|
fields: [${relation.reference.field}],
|
|
358
|
-
references: [${relation.reference.references}]
|
|
359
|
-
})
|
|
360
|
-
|
|
361
|
-
${manyRelations.map(({ key, model, relationName }) => {
|
|
362
|
-
return ` ${key}: many(${model}${relationName ? `, { relationName: "${relationName}" }` : ""})`;
|
|
363
|
-
}).join(",\n ")}
|
|
218
|
+
references: [${relation.reference.references}],
|
|
219
|
+
})` : "").filter((x) => x !== "").join(",\n ")}${singleRelations.length > 0 && manyRelations.length > 0 ? "," : ""}
|
|
220
|
+
${manyRelations.map(({ key, model }) => ` ${key}: many(${model})`).join(",\n ")}
|
|
364
221
|
}))`;
|
|
365
222
|
relationsString += `\n${tableRelation}\n`;
|
|
366
223
|
} else if (hasOne) {
|
|
367
|
-
const tableRelation = `export const ${modelName}Relations = relations(${modelName}, ({ one }) => ({
|
|
368
|
-
${singleRelations.map((relation) => {
|
|
369
|
-
if (!relation.reference) return "";
|
|
370
|
-
const relationNameParam = relation.relationName ? `,\n relationName: "${relation.relationName}"` : "";
|
|
371
|
-
return ` ${relation.key}: one(${relation.model}, {
|
|
224
|
+
const tableRelation = `export const ${modelName}Relations = relations(${getModelName(table.modelName)}, ({ one }) => ({
|
|
225
|
+
${singleRelations.map((relation) => relation.reference ? ` ${relation.key}: one(${relation.model}, {
|
|
372
226
|
fields: [${relation.reference.field}],
|
|
373
|
-
references: [${relation.reference.references}]
|
|
374
|
-
})
|
|
375
|
-
}).filter((x) => x !== "").join(",\n ")}
|
|
227
|
+
references: [${relation.reference.references}],
|
|
228
|
+
})` : "").filter((x) => x !== "").join(",\n ")}
|
|
376
229
|
}))`;
|
|
377
230
|
relationsString += `\n${tableRelation}\n`;
|
|
378
231
|
} else if (hasMany) {
|
|
379
|
-
const tableRelation = `export const ${modelName}Relations = relations(${modelName}, ({ many }) => ({
|
|
380
|
-
${manyRelations.map(({ key, model
|
|
381
|
-
return ` ${key}: many(${model}${relationName ? `, { relationName: "${relationName}" }` : ""})`;
|
|
382
|
-
}).join(",\n ")}
|
|
232
|
+
const tableRelation = `export const ${modelName}Relations = relations(${getModelName(table.modelName)}, ({ many }) => ({
|
|
233
|
+
${manyRelations.map(({ key, model }) => ` ${key}: many(${model})`).join(",\n ")}
|
|
383
234
|
}))`;
|
|
384
235
|
relationsString += `\n${tableRelation}\n`;
|
|
385
236
|
}
|
|
@@ -387,7 +238,7 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
|
|
|
387
238
|
code += `\n${relationsString}`;
|
|
388
239
|
return {
|
|
389
240
|
code: await prettier.format(code, { parser: "typescript" }),
|
|
390
|
-
fileName:
|
|
241
|
+
fileName: filePath,
|
|
391
242
|
overwrite: fileExist
|
|
392
243
|
};
|
|
393
244
|
};
|
|
@@ -396,14 +247,12 @@ function generateImport({ databaseType, tables }) {
|
|
|
396
247
|
const coreImports = [];
|
|
397
248
|
let hasBigint = false;
|
|
398
249
|
let hasJson = false;
|
|
399
|
-
let hasUuid = false;
|
|
400
250
|
for (const table of Object.values(tables)) {
|
|
401
251
|
for (const field of Object.values(table.fields)) {
|
|
402
252
|
if (field.bigint) hasBigint = true;
|
|
403
253
|
if (field.type === "json") hasJson = true;
|
|
404
|
-
if (field.type === "uuid") hasUuid = true;
|
|
405
254
|
}
|
|
406
|
-
if (hasJson && hasBigint
|
|
255
|
+
if (hasJson && hasBigint) break;
|
|
407
256
|
}
|
|
408
257
|
coreImports.push(`${databaseType}Table`);
|
|
409
258
|
coreImports.push(databaseType === "mysql" ? "varchar, text" : databaseType === "pg" ? "text" : "text");
|
|
@@ -413,8 +262,9 @@ function generateImport({ databaseType, tables }) {
|
|
|
413
262
|
if (Object.values(tables).some((table) => Object.values(table.fields).some((field) => (field.type === "number" || field.type === "number[]") && !field.bigint))) coreImports.push("int");
|
|
414
263
|
if (Object.values(tables).some((table) => Object.values(table.fields).some((field) => typeof field.type !== "string" && Array.isArray(field.type) && field.type.every((x) => typeof x === "string")))) coreImports.push("mysqlEnum");
|
|
415
264
|
} else if (databaseType === "pg") {
|
|
265
|
+
rootImports.push("sql");
|
|
266
|
+
coreImports.push("uuid");
|
|
416
267
|
if (Object.values(tables).some((table) => Object.values(table.fields).some((field) => (field.type === "number" || field.type === "number[]") && !field.bigint))) coreImports.push("integer");
|
|
417
|
-
if (hasUuid) coreImports.push("uuid");
|
|
418
268
|
} else coreImports.push("integer");
|
|
419
269
|
if (hasJson) {
|
|
420
270
|
if (databaseType === "pg") coreImports.push("jsonb");
|
|
@@ -511,6 +361,7 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
|
|
|
511
361
|
}
|
|
512
362
|
}
|
|
513
363
|
}
|
|
364
|
+
const uuidsAreNative = provider === "postgresql";
|
|
514
365
|
const schema = produceSchema(schemaPrisma, (builder) => {
|
|
515
366
|
for (const table in tables) {
|
|
516
367
|
const originalTableName = table;
|
|
@@ -537,7 +388,8 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
|
|
|
537
388
|
}
|
|
538
389
|
}
|
|
539
390
|
const prismaModel = builder.findByType("model", { name: modelName });
|
|
540
|
-
if (!prismaModel) builder.model(modelName).field("id", "String").attribute("id");
|
|
391
|
+
if (!prismaModel) if (uuidsAreNative) builder.model(modelName).field("id", "String").attribute("id").attribute("default(dbgenerated(\"pg_catalog.gen_random_uuid()\"))").attribute("db.Uuid");
|
|
392
|
+
else builder.model(modelName).field("id", "String").attribute("id");
|
|
541
393
|
for (const field in fields) {
|
|
542
394
|
const attr = fields[field];
|
|
543
395
|
const fieldName = attr.fieldName || field;
|
|
@@ -550,7 +402,7 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
|
|
|
550
402
|
const fieldBuilder = builder.model(modelName).field(fieldName, getType({
|
|
551
403
|
isBigint: attr?.bigint || false,
|
|
552
404
|
isOptional: !attr?.required,
|
|
553
|
-
type: "string"
|
|
405
|
+
type: attr.references?.field === "id" ? "string" : attr.type
|
|
554
406
|
}));
|
|
555
407
|
if (field === "id") fieldBuilder.attribute("id");
|
|
556
408
|
if (attr.unique) builder.model(modelName).blockAttribute(`unique([${fieldName}])`);
|
|
@@ -561,7 +413,7 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
|
|
|
561
413
|
fieldBuilder.attribute(`default("${JSON.stringify(attr.defaultValue).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}")`);
|
|
562
414
|
continue;
|
|
563
415
|
}
|
|
564
|
-
|
|
416
|
+
const jsonArray = [];
|
|
565
417
|
for (const value of attr.defaultValue) jsonArray.push(value);
|
|
566
418
|
fieldBuilder.attribute(`default("${JSON.stringify(jsonArray).replace(/"/g, "\\\"")}")`);
|
|
567
419
|
continue;
|
|
@@ -570,11 +422,11 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
|
|
|
570
422
|
fieldBuilder.attribute(`default([])`);
|
|
571
423
|
continue;
|
|
572
424
|
} else if (typeof attr.defaultValue[0] === "string" && attr.type === "string[]") {
|
|
573
|
-
|
|
425
|
+
const valueArray = [];
|
|
574
426
|
for (const value of attr.defaultValue) valueArray.push(JSON.stringify(value));
|
|
575
427
|
fieldBuilder.attribute(`default([${valueArray}])`);
|
|
576
428
|
} else if (typeof attr.defaultValue[0] === "number") {
|
|
577
|
-
|
|
429
|
+
const valueArray = [];
|
|
578
430
|
for (const value of attr.defaultValue) valueArray.push(`${value}`);
|
|
579
431
|
fieldBuilder.attribute(`default([${valueArray}])`);
|
|
580
432
|
}
|
|
@@ -593,11 +445,11 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
|
|
|
593
445
|
if (field === "updatedAt" && attr.onUpdate) fieldBuilder.attribute("updatedAt");
|
|
594
446
|
else if (attr.onUpdate) {}
|
|
595
447
|
if (attr.references) {
|
|
448
|
+
if (uuidsAreNative && attr.references?.field === "id") builder.model(modelName).field(fieldName).attribute(`db.Uuid`);
|
|
596
449
|
const referencedOriginalModelName = getModelName(attr.references.model);
|
|
597
450
|
const referencedCustomModelName = tables[referencedOriginalModelName]?.modelName || referencedOriginalModelName;
|
|
598
|
-
let action = "
|
|
599
|
-
if (attr.references.onDelete === "
|
|
600
|
-
else if (attr.references.onDelete === "no action") action = "NoAction";
|
|
451
|
+
let action = "Cascade";
|
|
452
|
+
if (attr.references.onDelete === "no action") action = "NoAction";
|
|
601
453
|
else if (attr.references.onDelete === "set null") action = "SetNull";
|
|
602
454
|
else if (attr.references.onDelete === "set default") action = "SetDefault";
|
|
603
455
|
else if (attr.references.onDelete === "restrict") action = "Restrict";
|
|
@@ -630,7 +482,8 @@ const generatePrismaSchema = async ({ adapter, options, file }) => {
|
|
|
630
482
|
}
|
|
631
483
|
const field = Object.entries(fields).find(([key, attr]) => (attr.fieldName || key) === fieldName)?.[1];
|
|
632
484
|
let indexField = fieldName;
|
|
633
|
-
if (provider === "mysql" && field && field.type === "string") indexField = `${fieldName}
|
|
485
|
+
if (provider === "mysql" && field && field.type === "string") if (field.references?.field === "id") indexField = `${fieldName}`;
|
|
486
|
+
else indexField = `${fieldName}(length: 191)`;
|
|
634
487
|
builder.model(modelName).blockAttribute(`index([${indexField}])`);
|
|
635
488
|
}
|
|
636
489
|
const hasAttribute = builder.findByType("attribute", {
|
|
@@ -660,39 +513,12 @@ const getNewPrisma = (provider, cwd) => {
|
|
|
660
513
|
}`;
|
|
661
514
|
};
|
|
662
515
|
|
|
663
|
-
//#endregion
|
|
664
|
-
//#region src/generators/mongodb.ts
|
|
665
|
-
const generateMongoDBSchema = async ({ options, file, adapter }) => {
|
|
666
|
-
const tables = getPaymentTables(options);
|
|
667
|
-
const filePath = file || "./mongodb-schema.ts";
|
|
668
|
-
initGetModelName({
|
|
669
|
-
schema: tables,
|
|
670
|
-
usePlural: adapter?.options?.adapterConfig?.usePlural
|
|
671
|
-
});
|
|
672
|
-
initGetFieldName({
|
|
673
|
-
schema: tables,
|
|
674
|
-
usePlural: false
|
|
675
|
-
});
|
|
676
|
-
return {
|
|
677
|
-
code: await prettier.format(`// MongoDB schema setup
|
|
678
|
-
// Generated from schema definitions
|
|
679
|
-
// DO NOT EDIT MANUALLY - This file is auto-generated
|
|
680
|
-
|
|
681
|
-
// Schema definitions for MongoDB collections
|
|
682
|
-
// Use this file to set up your MongoDB collections and indexes
|
|
683
|
-
`, { parser: "typescript" }),
|
|
684
|
-
fileName: filePath,
|
|
685
|
-
overwrite: true
|
|
686
|
-
};
|
|
687
|
-
};
|
|
688
|
-
|
|
689
516
|
//#endregion
|
|
690
517
|
//#region src/generators/index.ts
|
|
691
518
|
const adapters = {
|
|
692
519
|
prisma: generatePrismaSchema,
|
|
693
520
|
drizzle: generateDrizzleSchema,
|
|
694
|
-
kysely: generateKyselySchema
|
|
695
|
-
mongodb: generateMongoDBSchema
|
|
521
|
+
kysely: generateKyselySchema
|
|
696
522
|
};
|
|
697
523
|
const generateSchema = async (opts) => {
|
|
698
524
|
const adapter = opts.adapter;
|
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-CWgRQHR-.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.3.
|
|
3
|
+
"version": "0.3.2",
|
|
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.1.
|
|
68
|
-
"pecunia-root": "^0.2.
|
|
67
|
+
"pecunia-core": "^0.1.6",
|
|
68
|
+
"pecunia-root": "^0.2.7",
|
|
69
69
|
"pg": "^8.16.3",
|
|
70
70
|
"prettier": "^3.6.2",
|
|
71
71
|
"prompts": "^2.4.2",
|