@autobe/utils 0.28.1 → 0.29.0

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.
@@ -0,0 +1,4 @@
1
+ export declare namespace ArrayUtil {
2
+ function asyncMap<T, U>(array: T[], callback: (value: T, index: number, array: T[]) => Promise<U>): Promise<U[]>;
3
+ function paddle(contents: string[][]): string[];
4
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ArrayUtil = void 0;
13
+ var ArrayUtil;
14
+ (function (ArrayUtil) {
15
+ function asyncMap(array, callback) {
16
+ return __awaiter(this, void 0, void 0, function* () {
17
+ const result = new Array(array.length);
18
+ for (let i = 0; i < array.length; i++)
19
+ result[i] = yield callback(array[i], i, array);
20
+ return result;
21
+ });
22
+ }
23
+ ArrayUtil.asyncMap = asyncMap;
24
+ function paddle(contents) {
25
+ const output = [];
26
+ contents.forEach((c) => {
27
+ if (c.length === 0)
28
+ return;
29
+ else if (output.length === 0)
30
+ output.push(...c);
31
+ else
32
+ output.push("", ...c);
33
+ });
34
+ return output;
35
+ }
36
+ ArrayUtil.paddle = paddle;
37
+ })(ArrayUtil || (exports.ArrayUtil = ArrayUtil = {}));
38
+ //# sourceMappingURL=ArrayUtil.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ArrayUtil.js","sourceRoot":"","sources":["../src/ArrayUtil.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,IAAiB,SAAS,CAoBzB;AApBD,WAAiB,SAAS;IACxB,SAAsB,QAAQ,CAC5B,KAAU,EACV,QAA6D;;YAE7D,MAAM,MAAM,GAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;gBACnC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;YACjD,OAAO,MAAM,CAAC;QAChB,CAAC;KAAA;IARqB,kBAAQ,WAQ7B,CAAA;IAED,SAAgB,MAAM,CAAC,QAAoB;QACzC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACrB,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;iBACtB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;;gBAC3C,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IARe,gBAAM,SAQrB,CAAA;AACH,CAAC,EApBgB,SAAS,yBAAT,SAAS,QAoBzB"}
package/lib/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ export * from "./prisma";
1
2
  export * from "./interface";
2
3
  export * from "./test";
4
+ export * from "./ArrayUtil";
3
5
  export * from "./MapUtil";
4
6
  export * from "./StringUtil";
package/lib/index.js CHANGED
@@ -14,8 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./prisma"), exports);
17
18
  __exportStar(require("./interface"), exports);
18
19
  __exportStar(require("./test"), exports);
20
+ __exportStar(require("./ArrayUtil"), exports);
19
21
  __exportStar(require("./MapUtil"), exports);
20
22
  __exportStar(require("./StringUtil"), exports);
21
23
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,yCAAuB;AAEvB,4CAA0B;AAC1B,+CAA6B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,8CAA4B;AAC5B,yCAAuB;AAEvB,8CAA4B;AAC5B,4CAA0B;AAC1B,+CAA6B"}
@@ -0,0 +1 @@
1
+ export * from "./writePrismaApplication";
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./writePrismaApplication"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/prisma/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2DAAyC"}
@@ -0,0 +1,5 @@
1
+ import { AutoBePrisma } from "@autobe/interface";
2
+ export declare function writePrismaApplication(props: {
3
+ dbms: "postgres" | "sqlite";
4
+ application: AutoBePrisma.IApplication;
5
+ }): Record<string, string>;
@@ -0,0 +1,342 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.writePrismaApplication = writePrismaApplication;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ const ArrayUtil_1 = require("../ArrayUtil");
9
+ const MapUtil_1 = require("../MapUtil");
10
+ const StringUtil_1 = require("../StringUtil");
11
+ function writePrismaApplication(props) {
12
+ for (const file of props.application.files)
13
+ for (const model of file.models)
14
+ fillMappingName(model);
15
+ return Object.assign(Object.assign({}, Object.fromEntries(props.application.files
16
+ .filter((file) => file.filename !== "main.prisma")
17
+ .map((file) => [
18
+ file.filename,
19
+ writeFile(Object.assign(Object.assign({}, props), { file })),
20
+ ]))), { "main.prisma": props.dbms === "postgres" ? POSTGRES_MAIN_FILE : SQLITE_MAIN_FILE });
21
+ }
22
+ function writeFile(props) {
23
+ return props.file.models
24
+ .map((model) => writeModel(Object.assign(Object.assign({}, props), { model })))
25
+ .join("\n\n");
26
+ }
27
+ function writeModel(props) {
28
+ return [
29
+ writeComment([
30
+ props.model.description,
31
+ "",
32
+ ...(props.model.material ? [] : [`@namespace ${props.file.namespace}`]),
33
+ "@author AutoBE - https://github.com/wrtnlabs/autobe",
34
+ ].join("\n"), 80),
35
+ `model ${props.model.name} {`,
36
+ addIndent(ArrayUtil_1.ArrayUtil.paddle([writeColumns(props), writeRelations(props)]).join("\n")),
37
+ "}",
38
+ ].join("\n");
39
+ }
40
+ function fillMappingName(model) {
41
+ const group = new Map();
42
+ for (const ff of model.foreignFields) {
43
+ MapUtil_1.MapUtil.take(group, ff.relation.targetModel, () => []).push(ff);
44
+ if (ff.relation.targetModel == model.name)
45
+ ff.relation.mappingName = "recursive";
46
+ }
47
+ for (const array of group.values())
48
+ if (array.length !== 1)
49
+ for (const ff of array)
50
+ ff.relation.mappingName = shortName(`${model.name}_of_${ff.name}`);
51
+ }
52
+ /* -----------------------------------------------------------
53
+ COLUMNS
54
+ ----------------------------------------------------------- */
55
+ function writeColumns(props) {
56
+ return [
57
+ "//----",
58
+ "// COLUMNS",
59
+ "//----",
60
+ writePrimary({
61
+ dbms: props.dbms,
62
+ model: props.model,
63
+ field: props.model.primaryField,
64
+ }),
65
+ ...props.model.foreignFields
66
+ .map((x) => [
67
+ "",
68
+ writeField({
69
+ dbms: props.dbms,
70
+ field: x,
71
+ }),
72
+ ])
73
+ .flat(),
74
+ ...props.model.plainFields
75
+ .map((x) => [
76
+ "",
77
+ writeField({
78
+ dbms: props.dbms,
79
+ field: x,
80
+ }),
81
+ ])
82
+ .flat(),
83
+ ];
84
+ }
85
+ function writePrimary(props) {
86
+ const type = props.dbms === "postgres" ? POSTGRES_PHYSICAL_TYPES.uuid : undefined;
87
+ const pkeyName = `${props.model.name}__pkey`;
88
+ const signature = pkeyName.length <= MAX_IDENTIFIER_LENGTH
89
+ ? "@id"
90
+ : `@id(map: "${shortName(pkeyName)}")`;
91
+ return [
92
+ writeComment(props.field.description, 78),
93
+ `${props.field.name} String ${signature}${type ? ` ${type}` : ""}`,
94
+ ].join("\n");
95
+ }
96
+ function writeField(props) {
97
+ const logical = LOGICAL_TYPES[props.field.type];
98
+ const physical = props.dbms === "postgres"
99
+ ? POSTGRES_PHYSICAL_TYPES[props.field.type]
100
+ : undefined;
101
+ return [
102
+ writeComment(props.field.description, 78),
103
+ [
104
+ props.field.name,
105
+ `${logical}${props.field.nullable ? "?" : ""}`,
106
+ ...(physical ? [physical] : []),
107
+ ].join(" "),
108
+ ].join("\n");
109
+ }
110
+ /* -----------------------------------------------------------
111
+ RELATIONS
112
+ ----------------------------------------------------------- */
113
+ function writeRelations(props) {
114
+ const hasRelationships = props.application.files
115
+ .map((otherFile) => otherFile.models.map((otherModel) => otherModel.foreignFields
116
+ .filter((otherForeign) => otherForeign.relation.targetModel === props.model.name)
117
+ .map((otherForeign) => ({
118
+ modelName: otherModel.name,
119
+ unique: otherForeign.unique,
120
+ mappingName: otherForeign.relation.mappingName,
121
+ }))))
122
+ .flat(2);
123
+ const foreignIndexes = props.model.foreignFields.filter((f) => {
124
+ if (f.unique === true)
125
+ return props.model.uniqueIndexes.every((u) => u.fieldNames.length !== 1 || u.fieldNames[0] !== f.name);
126
+ return (props.model.uniqueIndexes.every((u) => u.fieldNames[0] !== f.name) &&
127
+ props.model.plainIndexes.every((p) => p.fieldNames[0] !== f.name));
128
+ });
129
+ const contents = [
130
+ props.model.foreignFields.map((foreign) => writeConstraint({
131
+ dbms: props.dbms,
132
+ model: props.model,
133
+ foreign,
134
+ })),
135
+ hasRelationships.map((r) => {
136
+ var _a;
137
+ return [
138
+ (_a = r.mappingName) !== null && _a !== void 0 ? _a : r.modelName,
139
+ `${r.modelName}${r.unique ? "?" : "[]"}`,
140
+ ...(r.mappingName ? [`@relation("${r.mappingName}")`] : []),
141
+ ].join(" ");
142
+ }),
143
+ foreignIndexes.map((field) => writeForeignIndex({
144
+ model: props.model,
145
+ field,
146
+ })),
147
+ [
148
+ ...props.model.uniqueIndexes.map((unique) => writeUniqueIndex({
149
+ model: props.model,
150
+ unique,
151
+ })),
152
+ ...props.model.plainIndexes.map((plain) => writePlainIndex({
153
+ model: props.model,
154
+ plain,
155
+ })),
156
+ ...(props.dbms === "postgres"
157
+ ? props.model.ginIndexes.map((gin) => writeGinIndex({
158
+ model: props.model,
159
+ gin,
160
+ }))
161
+ : []),
162
+ ],
163
+ ];
164
+ if (contents.every((c) => c.length === 0))
165
+ return [];
166
+ return [
167
+ "//----",
168
+ "// RELATIONS",
169
+ "//----",
170
+ // paddled content
171
+ ...ArrayUtil_1.ArrayUtil.paddle(contents),
172
+ ];
173
+ }
174
+ function writeConstraint(props) {
175
+ // spellchecker:ignore-next-line
176
+ const name = `${props.model.name}_${props.foreign.name}_rela`;
177
+ const tooMuchLong = props.dbms === "postgres" && name.length > MAX_IDENTIFIER_LENGTH;
178
+ const body = [
179
+ props.foreign.relation.name,
180
+ `${props.foreign.relation.targetModel}${props.foreign.nullable ? "?" : ""}`,
181
+ `@relation(${[
182
+ ...(props.foreign.relation.mappingName
183
+ ? [`"${props.foreign.relation.mappingName}"`]
184
+ : []),
185
+ `fields: [${props.foreign.name}]`,
186
+ `references: [id]`,
187
+ `onDelete: Cascade`,
188
+ ...(tooMuchLong ? [`map: "${shortName(name)}"`] : []),
189
+ ].join(", ")})`,
190
+ ].join(" ");
191
+ return tooMuchLong
192
+ ? StringUtil_1.StringUtil.trim `
193
+ // spellchecker: ignore-next-line
194
+ ${body}
195
+ `
196
+ : body;
197
+ }
198
+ function writeForeignIndex(props) {
199
+ const name = `${props.model.name}_${props.field.name}_fkey`;
200
+ const prefix = `@@${props.field.unique === true ? "unique" : "index"}([${props.field.name}]`;
201
+ if (name.length <= MAX_IDENTIFIER_LENGTH)
202
+ return `${prefix})`;
203
+ return StringUtil_1.StringUtil.trim `
204
+ // spellchecker: ignore-next-line
205
+ ${prefix}, map: "${shortName(name)}")
206
+ `;
207
+ }
208
+ function writeUniqueIndex(props) {
209
+ const name = `${props.model.name}_${props.unique.fieldNames.join("_")}_key`;
210
+ const prefix = `@@unique([${props.unique.fieldNames.join(", ")}]`;
211
+ if (name.length <= MAX_IDENTIFIER_LENGTH)
212
+ return `${prefix})`;
213
+ return StringUtil_1.StringUtil.trim `
214
+ // spellchecker: ignore-next-line
215
+ ${prefix}, map: "${shortName(name)}")
216
+ `;
217
+ }
218
+ function writePlainIndex(props) {
219
+ const name = `${props.model.name}_${props.plain.fieldNames.join("_")}_idx`;
220
+ const prefix = `@@index([${props.plain.fieldNames.join(", ")}]`;
221
+ if (name.length <= MAX_IDENTIFIER_LENGTH)
222
+ return `${prefix})`;
223
+ return StringUtil_1.StringUtil.trim `
224
+ // spellchecker: ignore-next-line
225
+ ${prefix}, map: "${shortName(name)}")
226
+ `;
227
+ }
228
+ function writeGinIndex(props) {
229
+ const name = `${props.model.name}_${props.gin.fieldName}_idx`;
230
+ const prefix = `@@index([${props.gin.fieldName}(ops: raw("gin_trgm_ops"))], type: Gin`;
231
+ if (name.length <= MAX_IDENTIFIER_LENGTH)
232
+ return `${prefix})`;
233
+ return StringUtil_1.StringUtil.trim `
234
+ // spellchecker: ignore-next-line
235
+ ${prefix}, map: "${shortName(name)}")
236
+ `;
237
+ }
238
+ /* -----------------------------------------------------------
239
+ BACKGROUND
240
+ ----------------------------------------------------------- */
241
+ function writeComment(content, length) {
242
+ return content
243
+ .split("\r\n")
244
+ .join("\n")
245
+ .split("\n")
246
+ .map((line) => line.trim())
247
+ .map((line) => {
248
+ // 77자에서 "/// " 4자를 뺀 73자가 실제 컨텐츠 최대 길이
249
+ if (line.length <= length - 4)
250
+ return [line];
251
+ const words = line.split(" ");
252
+ const result = [];
253
+ let currentLine = "";
254
+ for (const word of words) {
255
+ const potentialLine = currentLine ? `${currentLine} ${word}` : word;
256
+ if (potentialLine.length <= 73) {
257
+ currentLine = potentialLine;
258
+ }
259
+ else {
260
+ if (currentLine)
261
+ result.push(currentLine);
262
+ currentLine = word;
263
+ }
264
+ }
265
+ if (currentLine)
266
+ result.push(currentLine);
267
+ return result;
268
+ })
269
+ .flat()
270
+ .map((str) => `///${str.length ? ` ${str}` : ""}`)
271
+ .join("\n")
272
+ .trim();
273
+ }
274
+ function addIndent(content) {
275
+ return content
276
+ .split("\r\n")
277
+ .join("\n")
278
+ .split("\n")
279
+ .map((str) => ` ${str}`)
280
+ .join("\n");
281
+ }
282
+ function shortName(name) {
283
+ if (name.length <= MAX_IDENTIFIER_LENGTH)
284
+ return name;
285
+ const hash = crypto_1.default
286
+ .createHash("md5")
287
+ .update(name)
288
+ .digest("hex")
289
+ .substring(0, HASH_TRUNCATION_LENGTH);
290
+ return `${name.substring(0, MAX_IDENTIFIER_LENGTH - HASH_TRUNCATION_LENGTH - 1)}_${hash}`;
291
+ }
292
+ const LOGICAL_TYPES = {
293
+ // native types
294
+ boolean: "Boolean",
295
+ int: "Int",
296
+ double: "Float",
297
+ string: "String",
298
+ // formats
299
+ datetime: "DateTime",
300
+ uuid: "String",
301
+ uri: "String",
302
+ };
303
+ const POSTGRES_PHYSICAL_TYPES = {
304
+ int: "@db.Integer",
305
+ double: "@db.DoublePrecision",
306
+ uuid: "@db.Uuid",
307
+ datetime: "@db.Timestamptz",
308
+ uri: "@db.VarChar(80000)",
309
+ };
310
+ const POSTGRES_MAIN_FILE = StringUtil_1.StringUtil.trim `
311
+ generator client {
312
+ provider = "prisma-client-js"
313
+ engineType = "client"
314
+ previewFeatures = ["postgresqlExtensions", "views"]
315
+ }
316
+ datasource db {
317
+ provider = "postgresql"
318
+ url = env("DATABASE_URL")
319
+ extensions = [pg_trgm]
320
+ }
321
+ generator markdown {
322
+ provider = "prisma-markdown"
323
+ output = "../../docs/ERD.md"
324
+ }
325
+ `;
326
+ const SQLITE_MAIN_FILE = StringUtil_1.StringUtil.trim `
327
+ generator client {
328
+ provider = "prisma-client-js"
329
+ engineType = "client"
330
+ }
331
+ datasource db {
332
+ provider = "sqlite"
333
+ url = "file:../db.sqlite"
334
+ }
335
+ generator markdown {
336
+ provider = "prisma-markdown"
337
+ output = "../../docs/ERD.md"
338
+ }
339
+ `;
340
+ const MAX_IDENTIFIER_LENGTH = 63;
341
+ const HASH_TRUNCATION_LENGTH = 8;
342
+ //# sourceMappingURL=writePrismaApplication.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writePrismaApplication.js","sourceRoot":"","sources":["../../src/prisma/writePrismaApplication.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autobe/utils",
3
- "version": "0.28.1",
3
+ "version": "0.29.0",
4
4
  "description": "AI backend server code generator",
5
5
  "main": "lib/index.js",
6
6
  "keywords": [],
@@ -27,9 +27,10 @@
27
27
  "@samchon/openapi": "^5.0.1",
28
28
  "tstl": "^3.0.0",
29
29
  "typia": "^10.0.2",
30
- "@autobe/interface": "^0.28.1"
30
+ "@autobe/interface": "^0.29.0"
31
31
  },
32
32
  "devDependencies": {
33
+ "@types/node": "^24.10.1",
33
34
  "rimraf": "^6.0.1",
34
35
  "ts-patch": "^3.3.0",
35
36
  "typescript": "~5.9.3"
@@ -0,0 +1,21 @@
1
+ export namespace ArrayUtil {
2
+ export async function asyncMap<T, U>(
3
+ array: T[],
4
+ callback: (value: T, index: number, array: T[]) => Promise<U>,
5
+ ): Promise<U[]> {
6
+ const result: U[] = new Array(array.length);
7
+ for (let i = 0; i < array.length; i++)
8
+ result[i] = await callback(array[i], i, array);
9
+ return result;
10
+ }
11
+
12
+ export function paddle(contents: string[][]): string[] {
13
+ const output: string[] = [];
14
+ contents.forEach((c) => {
15
+ if (c.length === 0) return;
16
+ else if (output.length === 0) output.push(...c);
17
+ else output.push("", ...c);
18
+ });
19
+ return output;
20
+ }
21
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
+ export * from "./prisma";
1
2
  export * from "./interface";
2
3
  export * from "./test";
3
4
 
5
+ export * from "./ArrayUtil";
4
6
  export * from "./MapUtil";
5
7
  export * from "./StringUtil";
@@ -0,0 +1 @@
1
+ export * from "./writePrismaApplication";
@@ -0,0 +1,439 @@
1
+ import { AutoBePrisma } from "@autobe/interface";
2
+ import crypto from "crypto";
3
+
4
+ import { ArrayUtil } from "../ArrayUtil";
5
+ import { MapUtil } from "../MapUtil";
6
+ import { StringUtil } from "../StringUtil";
7
+
8
+ export function writePrismaApplication(props: {
9
+ dbms: "postgres" | "sqlite";
10
+ application: AutoBePrisma.IApplication;
11
+ }): Record<string, string> {
12
+ for (const file of props.application.files)
13
+ for (const model of file.models) fillMappingName(model);
14
+ return {
15
+ ...Object.fromEntries(
16
+ props.application.files
17
+ .filter((file) => file.filename !== "main.prisma")
18
+ .map((file) => [
19
+ file.filename,
20
+ writeFile({
21
+ ...props,
22
+ file,
23
+ }),
24
+ ]),
25
+ ),
26
+ "main.prisma":
27
+ props.dbms === "postgres" ? POSTGRES_MAIN_FILE : SQLITE_MAIN_FILE,
28
+ };
29
+ }
30
+
31
+ function writeFile(props: {
32
+ dbms: "postgres" | "sqlite";
33
+ application: AutoBePrisma.IApplication;
34
+ file: AutoBePrisma.IFile;
35
+ }): string {
36
+ return props.file.models
37
+ .map((model) =>
38
+ writeModel({
39
+ ...props,
40
+ model,
41
+ }),
42
+ )
43
+ .join("\n\n");
44
+ }
45
+
46
+ function writeModel(props: {
47
+ dbms: "postgres" | "sqlite";
48
+ application: AutoBePrisma.IApplication;
49
+ file: AutoBePrisma.IFile;
50
+ model: AutoBePrisma.IModel;
51
+ }): string {
52
+ return [
53
+ writeComment(
54
+ [
55
+ props.model.description,
56
+ "",
57
+ ...(props.model.material ? [] : [`@namespace ${props.file.namespace}`]),
58
+ "@author AutoBE - https://github.com/wrtnlabs/autobe",
59
+ ].join("\n"),
60
+ 80,
61
+ ),
62
+ `model ${props.model.name} {`,
63
+ addIndent(
64
+ ArrayUtil.paddle([writeColumns(props), writeRelations(props)]).join("\n"),
65
+ ),
66
+ "}",
67
+ ].join("\n");
68
+ }
69
+
70
+ function fillMappingName(model: AutoBePrisma.IModel): void {
71
+ const group: Map<string, AutoBePrisma.IForeignField[]> = new Map();
72
+ for (const ff of model.foreignFields) {
73
+ MapUtil.take(group, ff.relation.targetModel, () => []).push(ff);
74
+ if (ff.relation.targetModel == model.name)
75
+ ff.relation.mappingName = "recursive";
76
+ }
77
+ for (const array of group.values())
78
+ if (array.length !== 1)
79
+ for (const ff of array)
80
+ ff.relation.mappingName = shortName(`${model.name}_of_${ff.name}`);
81
+ }
82
+
83
+ /* -----------------------------------------------------------
84
+ COLUMNS
85
+ ----------------------------------------------------------- */
86
+ function writeColumns(props: {
87
+ dbms: "postgres" | "sqlite";
88
+ model: AutoBePrisma.IModel;
89
+ }): string[] {
90
+ return [
91
+ "//----",
92
+ "// COLUMNS",
93
+ "//----",
94
+ writePrimary({
95
+ dbms: props.dbms,
96
+ model: props.model,
97
+ field: props.model.primaryField,
98
+ }),
99
+ ...props.model.foreignFields
100
+ .map((x) => [
101
+ "",
102
+ writeField({
103
+ dbms: props.dbms,
104
+ field: x,
105
+ }),
106
+ ])
107
+ .flat(),
108
+ ...props.model.plainFields
109
+ .map((x) => [
110
+ "",
111
+ writeField({
112
+ dbms: props.dbms,
113
+ field: x,
114
+ }),
115
+ ])
116
+ .flat(),
117
+ ];
118
+ }
119
+
120
+ function writePrimary(props: {
121
+ dbms: "postgres" | "sqlite";
122
+ model: AutoBePrisma.IModel;
123
+ field: AutoBePrisma.IPrimaryField;
124
+ }): string {
125
+ const type: string | undefined =
126
+ props.dbms === "postgres" ? POSTGRES_PHYSICAL_TYPES.uuid : undefined;
127
+ const pkeyName: string = `${props.model.name}__pkey`;
128
+ const signature: string =
129
+ pkeyName.length <= MAX_IDENTIFIER_LENGTH
130
+ ? "@id"
131
+ : `@id(map: "${shortName(pkeyName)}")`;
132
+ return [
133
+ writeComment(props.field.description, 78),
134
+ `${props.field.name} String ${signature}${type ? ` ${type}` : ""}`,
135
+ ].join("\n");
136
+ }
137
+
138
+ function writeField(props: {
139
+ dbms: "postgres" | "sqlite";
140
+ field: AutoBePrisma.IPlainField;
141
+ }): string {
142
+ const logical: string = LOGICAL_TYPES[props.field.type];
143
+ const physical: string | undefined =
144
+ props.dbms === "postgres"
145
+ ? POSTGRES_PHYSICAL_TYPES[
146
+ props.field.type as keyof typeof POSTGRES_PHYSICAL_TYPES
147
+ ]
148
+ : undefined;
149
+ return [
150
+ writeComment(props.field.description, 78),
151
+ [
152
+ props.field.name,
153
+ `${logical}${props.field.nullable ? "?" : ""}`,
154
+ ...(physical ? [physical] : []),
155
+ ].join(" "),
156
+ ].join("\n");
157
+ }
158
+
159
+ /* -----------------------------------------------------------
160
+ RELATIONS
161
+ ----------------------------------------------------------- */
162
+ function writeRelations(props: {
163
+ dbms: "postgres" | "sqlite";
164
+ application: AutoBePrisma.IApplication;
165
+ model: AutoBePrisma.IModel;
166
+ }): string[] {
167
+ interface IHasRelationship {
168
+ modelName: string;
169
+ unique: boolean;
170
+ mappingName?: string;
171
+ }
172
+ const hasRelationships: IHasRelationship[] = props.application.files
173
+ .map((otherFile) =>
174
+ otherFile.models.map((otherModel) =>
175
+ otherModel.foreignFields
176
+ .filter(
177
+ (otherForeign) =>
178
+ otherForeign.relation.targetModel === props.model.name,
179
+ )
180
+ .map((otherForeign) => ({
181
+ modelName: otherModel.name,
182
+ unique: otherForeign.unique,
183
+ mappingName: otherForeign.relation.mappingName,
184
+ })),
185
+ ),
186
+ )
187
+ .flat(2);
188
+ const foreignIndexes: AutoBePrisma.IForeignField[] =
189
+ props.model.foreignFields.filter((f) => {
190
+ if (f.unique === true)
191
+ return props.model.uniqueIndexes.every(
192
+ (u) => u.fieldNames.length !== 1 || u.fieldNames[0] !== f.name,
193
+ );
194
+ return (
195
+ props.model.uniqueIndexes.every((u) => u.fieldNames[0] !== f.name) &&
196
+ props.model.plainIndexes.every((p) => p.fieldNames[0] !== f.name)
197
+ );
198
+ });
199
+ const contents: string[][] = [
200
+ props.model.foreignFields.map((foreign) =>
201
+ writeConstraint({
202
+ dbms: props.dbms,
203
+ model: props.model,
204
+ foreign,
205
+ }),
206
+ ),
207
+ hasRelationships.map((r) =>
208
+ [
209
+ r.mappingName ?? r.modelName,
210
+ `${r.modelName}${r.unique ? "?" : "[]"}`,
211
+ ...(r.mappingName ? [`@relation("${r.mappingName}")`] : []),
212
+ ].join(" "),
213
+ ),
214
+ foreignIndexes.map((field) =>
215
+ writeForeignIndex({
216
+ model: props.model,
217
+ field,
218
+ }),
219
+ ),
220
+ [
221
+ ...props.model.uniqueIndexes.map((unique) =>
222
+ writeUniqueIndex({
223
+ model: props.model,
224
+ unique,
225
+ }),
226
+ ),
227
+ ...props.model.plainIndexes.map((plain) =>
228
+ writePlainIndex({
229
+ model: props.model,
230
+ plain,
231
+ }),
232
+ ),
233
+ ...(props.dbms === "postgres"
234
+ ? props.model.ginIndexes.map((gin) =>
235
+ writeGinIndex({
236
+ model: props.model,
237
+ gin,
238
+ }),
239
+ )
240
+ : []),
241
+ ],
242
+ ];
243
+ if (contents.every((c) => c.length === 0)) return [];
244
+ return [
245
+ "//----",
246
+ "// RELATIONS",
247
+ "//----",
248
+ // paddled content
249
+ ...ArrayUtil.paddle(contents),
250
+ ];
251
+ }
252
+
253
+ function writeConstraint(props: {
254
+ dbms: "postgres" | "sqlite";
255
+ model: AutoBePrisma.IModel;
256
+ foreign: AutoBePrisma.IForeignField;
257
+ }): string {
258
+ // spellchecker:ignore-next-line
259
+ const name: string = `${props.model.name}_${props.foreign.name}_rela`;
260
+ const tooMuchLong: boolean =
261
+ props.dbms === "postgres" && name.length > MAX_IDENTIFIER_LENGTH;
262
+ const body: string = [
263
+ props.foreign.relation.name,
264
+ `${props.foreign.relation.targetModel}${props.foreign.nullable ? "?" : ""}`,
265
+ `@relation(${[
266
+ ...(props.foreign.relation.mappingName
267
+ ? [`"${props.foreign.relation.mappingName}"`]
268
+ : []),
269
+ `fields: [${props.foreign.name}]`,
270
+ `references: [id]`,
271
+ `onDelete: Cascade`,
272
+ ...(tooMuchLong ? [`map: "${shortName(name)}"`] : []),
273
+ ].join(", ")})`,
274
+ ].join(" ");
275
+ return tooMuchLong
276
+ ? StringUtil.trim`
277
+ // spellchecker: ignore-next-line
278
+ ${body}
279
+ `
280
+ : body;
281
+ }
282
+
283
+ function writeForeignIndex(props: {
284
+ model: AutoBePrisma.IModel;
285
+ field: AutoBePrisma.IForeignField;
286
+ }): string {
287
+ const name: string = `${props.model.name}_${props.field.name}_fkey`;
288
+ const prefix: string = `@@${props.field.unique === true ? "unique" : "index"}([${props.field.name}]`;
289
+ if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`;
290
+ return StringUtil.trim`
291
+ // spellchecker: ignore-next-line
292
+ ${prefix}, map: "${shortName(name)}")
293
+ `;
294
+ }
295
+
296
+ function writeUniqueIndex(props: {
297
+ model: AutoBePrisma.IModel;
298
+ unique: AutoBePrisma.IUniqueIndex;
299
+ }): string {
300
+ const name: string = `${props.model.name}_${props.unique.fieldNames.join("_")}_key`;
301
+ const prefix: string = `@@unique([${props.unique.fieldNames.join(", ")}]`;
302
+ if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`;
303
+ return StringUtil.trim`
304
+ // spellchecker: ignore-next-line
305
+ ${prefix}, map: "${shortName(name)}")
306
+ `;
307
+ }
308
+
309
+ function writePlainIndex(props: {
310
+ model: AutoBePrisma.IModel;
311
+ plain: AutoBePrisma.IPlainIndex;
312
+ }): string {
313
+ const name: string = `${props.model.name}_${props.plain.fieldNames.join("_")}_idx`;
314
+ const prefix: string = `@@index([${props.plain.fieldNames.join(", ")}]`;
315
+ if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`;
316
+ return StringUtil.trim`
317
+ // spellchecker: ignore-next-line
318
+ ${prefix}, map: "${shortName(name)}")
319
+ `;
320
+ }
321
+
322
+ function writeGinIndex(props: {
323
+ model: AutoBePrisma.IModel;
324
+ gin: AutoBePrisma.IGinIndex;
325
+ }): string {
326
+ const name: string = `${props.model.name}_${props.gin.fieldName}_idx`;
327
+ const prefix: string = `@@index([${props.gin.fieldName}(ops: raw("gin_trgm_ops"))], type: Gin`;
328
+ if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`;
329
+ return StringUtil.trim`
330
+ // spellchecker: ignore-next-line
331
+ ${prefix}, map: "${shortName(name)}")
332
+ `;
333
+ }
334
+
335
+ /* -----------------------------------------------------------
336
+ BACKGROUND
337
+ ----------------------------------------------------------- */
338
+ function writeComment(content: string, length: number): string {
339
+ return content
340
+ .split("\r\n")
341
+ .join("\n")
342
+ .split("\n")
343
+ .map((line) => line.trim())
344
+ .map((line) => {
345
+ // 77자에서 "/// " 4자를 뺀 73자가 실제 컨텐츠 최대 길이
346
+ if (line.length <= length - 4) return [line];
347
+ const words: string[] = line.split(" ");
348
+ const result: string[] = [];
349
+ let currentLine = "";
350
+
351
+ for (const word of words) {
352
+ const potentialLine = currentLine ? `${currentLine} ${word}` : word;
353
+ if (potentialLine.length <= 73) {
354
+ currentLine = potentialLine;
355
+ } else {
356
+ if (currentLine) result.push(currentLine);
357
+ currentLine = word;
358
+ }
359
+ }
360
+
361
+ if (currentLine) result.push(currentLine);
362
+ return result;
363
+ })
364
+ .flat()
365
+ .map((str) => `///${str.length ? ` ${str}` : ""}`)
366
+ .join("\n")
367
+ .trim();
368
+ }
369
+
370
+ function addIndent(content: string): string {
371
+ return content
372
+ .split("\r\n")
373
+ .join("\n")
374
+ .split("\n")
375
+ .map((str) => ` ${str}`)
376
+ .join("\n");
377
+ }
378
+
379
+ function shortName(name: string): string {
380
+ if (name.length <= MAX_IDENTIFIER_LENGTH) return name;
381
+ const hash: string = crypto
382
+ .createHash("md5")
383
+ .update(name)
384
+ .digest("hex")
385
+ .substring(0, HASH_TRUNCATION_LENGTH);
386
+ return `${name.substring(0, MAX_IDENTIFIER_LENGTH - HASH_TRUNCATION_LENGTH - 1)}_${hash}`;
387
+ }
388
+
389
+ const LOGICAL_TYPES = {
390
+ // native types
391
+ boolean: "Boolean",
392
+ int: "Int",
393
+ double: "Float",
394
+ string: "String",
395
+ // formats
396
+ datetime: "DateTime",
397
+ uuid: "String",
398
+ uri: "String",
399
+ };
400
+ const POSTGRES_PHYSICAL_TYPES = {
401
+ int: "@db.Integer",
402
+ double: "@db.DoublePrecision",
403
+ uuid: "@db.Uuid",
404
+ datetime: "@db.Timestamptz",
405
+ uri: "@db.VarChar(80000)",
406
+ };
407
+
408
+ const POSTGRES_MAIN_FILE = StringUtil.trim`
409
+ generator client {
410
+ provider = "prisma-client-js"
411
+ engineType = "client"
412
+ previewFeatures = ["postgresqlExtensions", "views"]
413
+ }
414
+ datasource db {
415
+ provider = "postgresql"
416
+ url = env("DATABASE_URL")
417
+ extensions = [pg_trgm]
418
+ }
419
+ generator markdown {
420
+ provider = "prisma-markdown"
421
+ output = "../../docs/ERD.md"
422
+ }
423
+ `;
424
+ const SQLITE_MAIN_FILE = StringUtil.trim`
425
+ generator client {
426
+ provider = "prisma-client-js"
427
+ engineType = "client"
428
+ }
429
+ datasource db {
430
+ provider = "sqlite"
431
+ url = "file:../db.sqlite"
432
+ }
433
+ generator markdown {
434
+ provider = "prisma-markdown"
435
+ output = "../../docs/ERD.md"
436
+ }
437
+ `;
438
+ const MAX_IDENTIFIER_LENGTH = 63;
439
+ const HASH_TRUNCATION_LENGTH = 8;