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 CHANGED
@@ -20,7 +20,6 @@ declare const adapters: {
20
20
  prisma: SchemaGenerator;
21
21
  drizzle: SchemaGenerator;
22
22
  kysely: SchemaGenerator;
23
- mongodb: SchemaGenerator;
24
23
  };
25
24
  declare const generateSchema: (opts: {
26
25
  adapter: DBAdapter$1;
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-DyDUFaYK.mjs";
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 || "./src/db/schema.ts";
16
+ const filePath = file || "./schema.ts";
17
17
  const databaseType = adapter.options?.provider;
18
- const projectRoot = process.cwd();
19
- if (!databaseType) throw new Error("Database provider type is undefined during Drizzle schema generation. Please define a `provider` in the Drizzle adapter config.");
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
- const idFieldType = table.fields.id?.type;
105
- let id;
106
- if (databaseType === "pg" && idFieldType === "uuid") id = `uuid('id').primaryKey()`;
107
- else if (databaseType === "mysql") id = `varchar('id', { length: 36 }).primaryKey()`;
108
- else id = `text('id').primaryKey()`;
109
- const indexes = [];
110
- const references = [];
111
- for (const field of Object.keys(fields)) {
112
- if (field === "id") continue;
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
- const ref = referenceMap.get(fieldName);
208
- const shouldSkipReference = skipReferences.has(`${tableDef.tableKey}.${fieldName}`);
209
- let referenceChain = "";
210
- if (ref && !shouldSkipReference) referenceChain = `.references(() => ${ref.referencedTable}.${ref.referencedField}, { onDelete: '${ref.onDelete}' })`;
211
- else if (ref && shouldSkipReference) {
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
- if (table.relations) {
234
- for (const [relationName, relationDef] of Object.entries(table.relations)) {
235
- const referencedModelName = getModelName(relationDef.model);
236
- const foreignKeyField = table.fields[relationDef.foreignKey];
237
- const isSelfReferential = relationDef.model === tableKey || referencedModelName === modelName;
238
- const generateRelationName = (fkName) => {
239
- let cleaned = convertToSnakeCase(fkName, adapter.options?.camelCase).replace(/_by_id$/, "").replace(/_id$/, "");
240
- const participleToNoun = {
241
- "reversed": "reversal",
242
- "created": "creation",
243
- "updated": "update"
244
- };
245
- if (participleToNoun[cleaned]) cleaned = participleToNoun[cleaned];
246
- return `${convertToSnakeCase(modelName, adapter.options?.camelCase)}_${cleaned}`;
247
- };
248
- if (relationDef.kind === "one") {
249
- if (foreignKeyField?.references) {
250
- const fieldRef = `${modelName}.${getFieldName({
251
- model: tableKey,
252
- field: relationDef.foreignKey
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
- for (const oneRel of oneRelations) {
319
- if (!oneRel.relationName || !oneRel.reference) continue;
320
- const oneRelFieldName = oneRel.reference.fieldName;
321
- const matchingMany = manyRelations.find((manyRel) => manyRel.model === oneRel.model && manyRel.reference?.fieldName === oneRelFieldName);
322
- if (matchingMany) matchingMany.relationName = oneRel.relationName;
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, rels] of relationsByModel.entries()) if (rels.length > 1) duplicateRelations.push(...rels);
335
- else singleRelations.push(rels[0]);
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 relationExportName = `${modelName}${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}Relations`;
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}]${relationNameParam}
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}]${relationNameParam}
359
- })`;
360
- }).filter((x) => x !== "").join(",\n ")}${singleRelations.length > 0 && manyRelations.length > 0 ? "," : ""}
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}]${relationNameParam}
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, relationName }) => {
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: path.relative(projectRoot, resolvedSchemaPath),
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 && hasUuid) break;
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
- let jsonArray = [];
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
- let valueArray = [];
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
- let valueArray = [];
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 = "NoAction";
599
- if (attr.references.onDelete === "cascade") action = "Cascade";
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}(length: 191)`;
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-DyDUFaYK.mjs";
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.0",
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.4",
68
- "pecunia-root": "^0.2.5",
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",