@appweaver/cli 1.0.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.
Files changed (76) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +7 -0
  3. package/build/build-command.d.ts +2 -0
  4. package/build/build-command.js +15 -0
  5. package/build/build-project.d.ts +8 -0
  6. package/build/build-project.js +19 -0
  7. package/build/index.d.ts +2 -0
  8. package/build/index.js +18 -0
  9. package/generate/generate-command.d.ts +2 -0
  10. package/generate/generate-command.js +38 -0
  11. package/generate/generate-schema.d.ts +12 -0
  12. package/generate/generate-schema.js +475 -0
  13. package/generate/generate-types.d.ts +10 -0
  14. package/generate/generate-types.js +86 -0
  15. package/generate/index.d.ts +3 -0
  16. package/generate/index.js +19 -0
  17. package/migrate/index.d.ts +1 -0
  18. package/migrate/index.js +17 -0
  19. package/migrate/migrate-command.d.ts +2 -0
  20. package/migrate/migrate-command.js +13 -0
  21. package/migration/index.d.ts +1 -0
  22. package/migration/index.js +17 -0
  23. package/migration/migration-command.d.ts +2 -0
  24. package/migration/migration-command.js +34 -0
  25. package/openapi/index.d.ts +1 -0
  26. package/openapi/index.js +17 -0
  27. package/openapi/openapi-command.d.ts +2 -0
  28. package/openapi/openapi-command.js +46 -0
  29. package/package.json +56 -0
  30. package/seed/index.d.ts +1 -0
  31. package/seed/index.js +17 -0
  32. package/seed/seed-command.d.ts +2 -0
  33. package/seed/seed-command.js +33 -0
  34. package/skill/GUIDELINES.md +298 -0
  35. package/skill/SKILL.md +593 -0
  36. package/skill/references/cache.md +207 -0
  37. package/skill/references/cli.md +213 -0
  38. package/skill/references/client.md +507 -0
  39. package/skill/references/configuration.md +402 -0
  40. package/skill/references/database.md +134 -0
  41. package/skill/references/dependency-injection.md +214 -0
  42. package/skill/references/events.md +152 -0
  43. package/skill/references/mailer.md +235 -0
  44. package/skill/references/queue.md +196 -0
  45. package/skill/references/resources.md +961 -0
  46. package/skill/references/scheduler.md +184 -0
  47. package/skill/references/security.md +694 -0
  48. package/skill/references/storage.md +251 -0
  49. package/start/index.d.ts +2 -0
  50. package/start/index.js +18 -0
  51. package/start/start-command.d.ts +2 -0
  52. package/start/start-command.js +17 -0
  53. package/start/start-project.d.ts +8 -0
  54. package/start/start-project.js +147 -0
  55. package/testing/index.d.ts +1 -0
  56. package/testing/index.js +17 -0
  57. package/testing/testing-command.d.ts +2 -0
  58. package/testing/testing-command.js +96 -0
  59. package/update/index.d.ts +2 -0
  60. package/update/index.js +18 -0
  61. package/update/update-command.d.ts +2 -0
  62. package/update/update-command.js +84 -0
  63. package/update/update-packages.d.ts +10 -0
  64. package/update/update-packages.js +45 -0
  65. package/update/update-skill.d.ts +8 -0
  66. package/update/update-skill.js +93 -0
  67. package/utils/index.d.ts +3 -0
  68. package/utils/index.js +19 -0
  69. package/utils/loader-util.d.ts +29 -0
  70. package/utils/loader-util.js +132 -0
  71. package/utils/path-util.d.ts +41 -0
  72. package/utils/path-util.js +98 -0
  73. package/utils/process-util.d.ts +39 -0
  74. package/utils/process-util.js +92 -0
  75. package/weaver.d.ts +2 -0
  76. package/weaver.js +53 -0
package/LICENSE ADDED
@@ -0,0 +1 @@
1
+ UNLICENSED
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Appweaver - CLI
2
+
3
+ > Simple, fast, and reliable web application builder tool.
4
+
5
+ ## License
6
+
7
+ UNLICENSED
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function buildCommand(program: Command): void;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCommand = buildCommand;
4
+ const build_project_1 = require("./build-project");
5
+ function buildCommand(program) {
6
+ program
7
+ .command('build')
8
+ .alias('b')
9
+ .description('Build the application.')
10
+ .option('-p, --project [path]', 'TypeScript project config file.', 'tsconfig.build.json')
11
+ .action(async (_, command) => {
12
+ const projectFile = command.getOptionValue('project');
13
+ process.exit(await (0, build_project_1.buildProject)(projectFile));
14
+ });
15
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Builds the project by compiling TypeScript files and replacing alias paths.
3
+ *
4
+ * @param {string} projectFile - The path to the TypeScript project configuration file.
5
+ * @return {Promise<number>} A promise that resolves to the status code of the TypeScript compilation process.
6
+ * Returns 0 if the compilation process is successful.
7
+ */
8
+ export declare function buildProject(projectFile: string): Promise<number>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildProject = buildProject;
4
+ const tsc_alias_1 = require("tsc-alias");
5
+ const common_1 = require("@appweaver/common");
6
+ const utils_1 = require("../utils");
7
+ /**
8
+ * Builds the project by compiling TypeScript files and replacing alias paths.
9
+ *
10
+ * @param {string} projectFile - The path to the TypeScript project configuration file.
11
+ * @return {Promise<number>} A promise that resolves to the status code of the TypeScript compilation process.
12
+ * Returns 0 if the compilation process is successful.
13
+ */
14
+ async function buildProject(projectFile) {
15
+ await (0, utils_1.rimrafPath)(common_1.config.APP_BUILD_PATH, true);
16
+ const tscStatus = await (0, utils_1.runProcess)('tsc', ['-p', projectFile]);
17
+ await (0, tsc_alias_1.replaceTscAliasPaths)({ configFile: projectFile });
18
+ return tscStatus || 0;
19
+ }
@@ -0,0 +1,2 @@
1
+ export * from './build-command';
2
+ export * from './build-project';
package/build/index.js ADDED
@@ -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("./build-command"), exports);
18
+ __exportStar(require("./build-project"), exports);
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function generateCommand(program: Command): void;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateCommand = generateCommand;
4
+ const common_1 = require("@appweaver/common");
5
+ const generate_types_1 = require("./generate-types");
6
+ const generate_schema_1 = require("./generate-schema");
7
+ const utils_1 = require("../utils");
8
+ function generateCommand(program) {
9
+ program
10
+ .command('generate')
11
+ .alias('g')
12
+ .description('Generate application types and/or schemas.')
13
+ .option('-t, --types', 'Generate TypeScript types.')
14
+ .option('-s, --schema', 'Generate Prisma schema.')
15
+ .option('--modelPattern [pattern]', 'Glob pattern for finding model files. (default: from config or env).')
16
+ .option('--typesPath [path]', 'Output path for generated types. (default: from config or env).')
17
+ .option('--schemaPath [path]', 'Output path for generated Prisma schema. (default: from config or env).')
18
+ .option('--clientPath [path]', 'Output path for generated Prisma client (default: from config or env).')
19
+ .option('--verbose', 'Print verbose output.')
20
+ .action(async (_, command) => {
21
+ const quiet = !command.getOptionValue('verbose');
22
+ const generateAll = !command.getOptionValue('types') && !command.getOptionValue('schema');
23
+ const models = await (0, utils_1.loadModels)(command.getOptionValue('modelPattern') ?? common_1.config.RESOURCE_MODEL_PATTERN);
24
+ let typeGenerateResult = 0;
25
+ if (command.getOptionValue('types') || generateAll) {
26
+ typeGenerateResult = await (0, generate_types_1.generateTypes)(models, command.getOptionValue('typesPath') ??
27
+ common_1.config.RESOURCE_GENERATED_TYPES_PATH, quiet);
28
+ }
29
+ let schemaGenerateResult = 0;
30
+ if (command.getOptionValue('schema') || generateAll) {
31
+ schemaGenerateResult = await (0, generate_schema_1.generateSchema)(models, command.getOptionValue('schemaPath') ?? common_1.config.DATABASE_SCHEMA_PATH, command.getOptionValue('clientPath') ??
32
+ common_1.config.DATABASE_CLIENT_OUTPUT_DIR_PATH, quiet);
33
+ }
34
+ if (typeGenerateResult !== 0 || schemaGenerateResult !== 0) {
35
+ process.exit(typeGenerateResult || schemaGenerateResult);
36
+ }
37
+ });
38
+ }
@@ -0,0 +1,12 @@
1
+ import { ResourceModel } from '@appweaver/common';
2
+ /**
3
+ * Generates a Prisma schema file based on the provided resource models and saves it to the specified file path.
4
+ *
5
+ * @param {Record<string, ResourceModel>} models - An object containing resource model configurations, where each key is
6
+ * the model name.
7
+ * @param {string} schemaPath - The file path where the generated Prisma schema should be stored.
8
+ * @param {string} clientPath - The file path where the Prisma client output should be generated.
9
+ * @param {boolean} [quiet=false] - Optional flag to suppress logs. Default is false.
10
+ * @return {Promise<number>} A promise that resolves to the number of models processed for the schema generation.
11
+ */
12
+ export declare function generateSchema(models: Record<string, ResourceModel>, schemaPath: string, clientPath: string, quiet?: boolean): Promise<number>;
@@ -0,0 +1,475 @@
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.generateSchema = generateSchema;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const common_1 = require("@appweaver/common");
10
+ const utils_1 = require("../utils");
11
+ /**
12
+ * Generates a Prisma schema file based on the provided resource models and saves it to the specified file path.
13
+ *
14
+ * @param {Record<string, ResourceModel>} models - An object containing resource model configurations, where each key is
15
+ * the model name.
16
+ * @param {string} schemaPath - The file path where the generated Prisma schema should be stored.
17
+ * @param {string} clientPath - The file path where the Prisma client output should be generated.
18
+ * @param {boolean} [quiet=false] - Optional flag to suppress logs. Default is false.
19
+ * @return {Promise<number>} A promise that resolves to the number of models processed for the schema generation.
20
+ */
21
+ async function generateSchema(models, schemaPath, clientPath, quiet = false) {
22
+ const cwd = process.cwd();
23
+ try {
24
+ await (0, utils_1.ensureDirExists)(node_path_1.default.join(cwd, schemaPath));
25
+ const prismaModels = {};
26
+ const prismaEnums = {};
27
+ const dbType = databaseType();
28
+ const relativeOutputPath = (0, utils_1.relativePathFrom)(schemaPath, clientPath);
29
+ const schemaContent = [
30
+ `// This is your Prisma schema file,`,
31
+ `// learn more about it in the docs: https://pris.ly/d/prisma-schema`,
32
+ ``,
33
+ `// Generated by Appweaver. Please do not edit this file manually.`,
34
+ ``,
35
+ `datasource db {`,
36
+ ` provider = "${dbType}"`,
37
+ `}`,
38
+ ``,
39
+ `generator client {`,
40
+ ` provider = "prisma-client"`,
41
+ ` output = "${relativeOutputPath}"`,
42
+ common_1.config.APP_RUNTIME === common_1.Runtime.Bun ? ' runtime = "bun"' : undefined,
43
+ `}`,
44
+ ``
45
+ ].filter((l) => l !== undefined);
46
+ let authModel;
47
+ for (const model of Object.values(models)) {
48
+ if ((0, common_1.isResourceAuthModel)(model)) {
49
+ authModel = model;
50
+ break;
51
+ }
52
+ }
53
+ // Create Prisma models and enums from resource model config
54
+ for (const [name, schema] of Object.entries(models)) {
55
+ if (schema.config.generateSchema === false) {
56
+ continue;
57
+ }
58
+ prismaModels[name] = {
59
+ id: createIdSchema(schema.config.id),
60
+ scalars: createScalarsSchema(name, schema.config.scalars),
61
+ relations: createRelationsSchema(name, schema.config.relations),
62
+ files: createFilesSchema(name, schema.config.files),
63
+ audit: createAuditSchema(name, authModel, schema.config.audit),
64
+ index: createIndexSchema(schema.config.index),
65
+ tableName: schema.config.tableName
66
+ };
67
+ for (const [fieldName, scalar] of Object.entries(schema.config.scalars ?? {})) {
68
+ if (scalar.type === 'enum' && scalar.values?.length) {
69
+ prismaEnums[`${name}${(0, common_1.capitalize)(fieldName)}`] = scalar.values;
70
+ }
71
+ }
72
+ }
73
+ // Resolve model relations to match Prisma schema relationship convention
74
+ const createdByAuthUser = [];
75
+ const fileFields = [];
76
+ for (const [name, model] of Object.entries(prismaModels)) {
77
+ const modelSchema = Object.entries(models).find(([key]) => key === name)?.[1];
78
+ for (const relation of model.relations) {
79
+ if (relation.type.startsWith('Int')) {
80
+ continue;
81
+ }
82
+ const relationConfig = modelSchema?.config.relations?.[relation.name];
83
+ const referencedName = relation.type
84
+ .replaceAll('[]', '')
85
+ .replaceAll('?', '');
86
+ const referencedModel = prismaModels[referencedName];
87
+ const mappedField = referencedModel.relations.find((r) => r.name === relationConfig?.mappedBy);
88
+ if (mappedField?.type.startsWith('Int')) {
89
+ continue;
90
+ }
91
+ if (mappedField && mappedField.attributes?.length) {
92
+ // Ensure that mapped fields have the same reference name
93
+ const relationAttribute = mappedField.attributes[0];
94
+ const relationParts = relationAttribute.split('"');
95
+ const referenceName = relation.attributes?.[0].split('"')[1];
96
+ relationParts.splice(1, 1, `${referenceName}`);
97
+ if (relationConfig?.unique) {
98
+ mappedField.attributes[0] = `@relation("${referenceName}")`;
99
+ }
100
+ else {
101
+ mappedField.attributes[0] = relationParts.join('"');
102
+ }
103
+ }
104
+ else {
105
+ // For unmapped relations add a default relation field using the same
106
+ // reference name
107
+ const refName = (0, common_1.uncapitalize)((0, common_1.plural)(name));
108
+ if (!referencedModel.relations.some((r) => r.name === refName)) {
109
+ referencedModel.relations.push({
110
+ name: (0, common_1.uncapitalize)((0, common_1.plural)(name)),
111
+ type: `${name}[]`,
112
+ attributes: relation.attributes
113
+ });
114
+ }
115
+ }
116
+ }
117
+ for (const auditField of model.audit) {
118
+ if (auditField.name === 'createdBy') {
119
+ const referenceName = auditField.attributes?.[0].split('"')[1];
120
+ createdByAuthUser.push({
121
+ name: `created${(0, common_1.plural)(name)}`,
122
+ type: `${name}[]`,
123
+ attributes: [`@relation("${referenceName}")`]
124
+ });
125
+ }
126
+ }
127
+ for (const fileField of model.files) {
128
+ const referenceName = fileField.attributes?.[0].split('"')[1];
129
+ if (referenceName) {
130
+ fileFields.push({
131
+ name: `${fileField.name}${(0, common_1.plural)(name)}`,
132
+ type: `${name}[]`,
133
+ attributes: [`@relation("${referenceName}")`]
134
+ });
135
+ }
136
+ }
137
+ }
138
+ // Add auth model ownership relations
139
+ const authUserModel = prismaModels[authModel?.name ?? ''];
140
+ if (authUserModel) {
141
+ authUserModel.extra = {
142
+ 'Ownership models referenced with createdById column': createdByAuthUser
143
+ };
144
+ }
145
+ // Add file fields relations
146
+ const fileModel = prismaModels['File'];
147
+ if (fileModel) {
148
+ fileModel.extra = {
149
+ 'Related models with file columns': fileFields
150
+ };
151
+ }
152
+ // Add Prisma models to the schema content
153
+ for (const [name, model] of Object.entries(prismaModels)) {
154
+ schemaContent.push(`model ${name} {`);
155
+ // Group 1: id + scalars
156
+ const initialFields = [model.id, ...model.scalars];
157
+ const { nameLength, typeLength } = calculateMaxLengths(initialFields);
158
+ schemaContent.push(buildPrismaField(model.id, nameLength, typeLength));
159
+ for (const scalar of model.scalars) {
160
+ schemaContent.push(buildPrismaField(scalar, nameLength, typeLength));
161
+ }
162
+ // Group 2: relations
163
+ if (model.relations.length > 0) {
164
+ schemaContent.push(``);
165
+ schemaContent.push(` /// Related columns`);
166
+ const { nameLength, typeLength } = calculateMaxLengths(model.relations);
167
+ for (const relation of model.relations) {
168
+ schemaContent.push(buildPrismaField(relation, nameLength, typeLength));
169
+ }
170
+ }
171
+ // Group 3: files
172
+ if (model.files.length > 0) {
173
+ schemaContent.push(``);
174
+ schemaContent.push(` /// File columns`);
175
+ const { nameLength, typeLength } = calculateMaxLengths(model.files);
176
+ for (const file of model.files) {
177
+ schemaContent.push(buildPrismaField(file, nameLength, typeLength));
178
+ }
179
+ }
180
+ // Group 4: extra fields
181
+ for (const [title, extraFields] of Object.entries(model.extra ?? {})) {
182
+ if (extraFields.length === 0) {
183
+ continue;
184
+ }
185
+ schemaContent.push(``);
186
+ schemaContent.push(` /// ${title}`);
187
+ const { nameLength, typeLength } = calculateMaxLengths(extraFields);
188
+ for (const extraField of extraFields) {
189
+ schemaContent.push(buildPrismaField(extraField, nameLength, typeLength));
190
+ }
191
+ }
192
+ // Group 5: audit
193
+ if (model.audit.length > 0) {
194
+ schemaContent.push(``);
195
+ schemaContent.push(` /// Audit columns`);
196
+ const { nameLength, typeLength } = calculateMaxLengths(model.audit);
197
+ for (const audit of model.audit) {
198
+ schemaContent.push(buildPrismaField(audit, nameLength, typeLength));
199
+ }
200
+ }
201
+ if (model.index.length) {
202
+ schemaContent.push(``);
203
+ }
204
+ for (const index of model.index) {
205
+ schemaContent.push(` ${index}`);
206
+ }
207
+ if (model.tableName) {
208
+ schemaContent.push(``);
209
+ schemaContent.push(` @@map("${model.tableName}")`);
210
+ }
211
+ schemaContent.push(`}`, ``);
212
+ }
213
+ // Add Prisma enums to the schema content
214
+ for (const [name, enumValues] of Object.entries(prismaEnums)) {
215
+ schemaContent.push(`enum ${name} {`);
216
+ for (const value of enumValues) {
217
+ schemaContent.push(` ${value}`);
218
+ }
219
+ schemaContent.push(`}`, ``);
220
+ }
221
+ const outputPath = node_path_1.default.join(cwd, schemaPath);
222
+ let oldSchema;
223
+ try {
224
+ oldSchema = await promises_1.default.readFile(outputPath, 'utf8');
225
+ }
226
+ catch (error) {
227
+ // Schema currently does not exist
228
+ }
229
+ await promises_1.default.writeFile(outputPath, schemaContent.join('\n'));
230
+ const code = await (0, utils_1.runProcess)('prisma', ['generate'], { quiet });
231
+ if (code !== 0 && oldSchema) {
232
+ await promises_1.default.writeFile(outputPath, oldSchema);
233
+ console.error(`Schema generation failed.${quiet ? ' Start with --verbose flag to see error details.' : ''}`);
234
+ return 1;
235
+ }
236
+ else {
237
+ console.log(`Schema generated to ${node_path_1.default.relative(cwd, schemaPath)}`);
238
+ return 0;
239
+ }
240
+ }
241
+ catch (error) {
242
+ console.error('Schema generation failed', error);
243
+ return 2;
244
+ }
245
+ }
246
+ function buildPrismaField(field, nameLength = 0, typeLength = 0) {
247
+ const paddedName = field.name.padEnd(nameLength);
248
+ const paddedType = field.type.padEnd(typeLength);
249
+ return ` ${paddedName} ${paddedType} ${field.attributes?.join(' ') ?? ''}`.trimEnd();
250
+ }
251
+ function calculateMaxLengths(fields) {
252
+ let nameLength = 0;
253
+ let typeLength = 0;
254
+ for (const field of fields) {
255
+ if (field.name.length > nameLength) {
256
+ nameLength = field.name.length;
257
+ }
258
+ if (field.type.length > typeLength) {
259
+ typeLength = field.type.length;
260
+ }
261
+ }
262
+ return { nameLength, typeLength };
263
+ }
264
+ function createIdSchema(id = {}) {
265
+ const defaultType = !id.generator || id.generator === 'autoincrement()' ? 'Int' : 'String';
266
+ const defaultGenerator = id.type !== 'string' ? 'autoincrement()' : 'uuid()';
267
+ return {
268
+ name: 'id',
269
+ type: (0, common_1.capitalize)(id.type ?? defaultType),
270
+ attributes: ['@id', `@default(${id.generator ?? defaultGenerator})`]
271
+ };
272
+ }
273
+ function createScalarsSchema(modelName, scalars = {}) {
274
+ const fields = [];
275
+ for (const [name, scalar] of Object.entries(scalars)) {
276
+ fields.push(createScalarSchema(name, modelName, scalar));
277
+ }
278
+ return fields;
279
+ }
280
+ function createScalarSchema(name, modelName, scalar) {
281
+ const attributes = [];
282
+ const isSqlite = databaseType() === common_1.DatabaseType.Sqlite;
283
+ const sanitize = (val) => {
284
+ if ((0, common_1.isNumber)(val) || (0, common_1.isBoolean)(val)) {
285
+ return val;
286
+ }
287
+ else if ((0, common_1.isString)(val)) {
288
+ return val.replace(/"/g, '\\"');
289
+ }
290
+ else {
291
+ return JSON.stringify(val).replace(/"/g, '\\"');
292
+ }
293
+ };
294
+ let typeSuffix = scalar.required === false ? '?' : '';
295
+ if (scalar.array) {
296
+ typeSuffix = '[]';
297
+ }
298
+ const type = scalar.type === 'enum' && scalar.values?.length
299
+ ? `${modelName}${(0, common_1.capitalize)(name)}${typeSuffix}`
300
+ : `${(0, common_1.capitalize)(scalar.type)}${typeSuffix}`;
301
+ if (scalar.unique) {
302
+ attributes.push(`@unique`);
303
+ }
304
+ let defaultAttribute;
305
+ if (scalar.default !== undefined) {
306
+ if (['string', 'dateTime', 'json'].includes(scalar.type) && !scalar.array) {
307
+ defaultAttribute = `@default("${sanitize(scalar.default)}")`;
308
+ }
309
+ else if (scalar.array) {
310
+ const defaultValues = (0, common_1.isArray)(scalar.default)
311
+ ? scalar.default
312
+ : [scalar.default];
313
+ const mappedValues = defaultValues
314
+ .map((v) => ['string', 'dateTime', 'json'].includes(scalar.type)
315
+ ? `"${sanitize(v)}"`
316
+ : `${v}`)
317
+ .join(', ');
318
+ defaultAttribute = `@default([${mappedValues}])`;
319
+ }
320
+ else {
321
+ defaultAttribute = `@default(${sanitize(scalar.default)})`;
322
+ }
323
+ }
324
+ else if (scalar.defaultGenerator !== undefined) {
325
+ defaultAttribute = `@default(${scalar.defaultGenerator})`;
326
+ }
327
+ else if (scalar.defaultExpression?.length) {
328
+ defaultAttribute = `@default(dbgenerated("${scalar.defaultExpression}"))`;
329
+ }
330
+ if (defaultAttribute) {
331
+ attributes.push(defaultAttribute);
332
+ }
333
+ if (scalar.type === 'string' && scalar.maxLength && !isSqlite) {
334
+ attributes.push(`@db.VarChar(${scalar.maxLength})`);
335
+ }
336
+ return {
337
+ name,
338
+ type,
339
+ attributes
340
+ };
341
+ }
342
+ function createRelationsSchema(modelName, relations = {}) {
343
+ const fields = [];
344
+ for (const [name, relation] of Object.entries(relations)) {
345
+ fields.push(...createRelationSchema(name, modelName, relation));
346
+ }
347
+ return fields;
348
+ }
349
+ function createRelationSchema(name, modelName, relation) {
350
+ const attributes = [];
351
+ const relationName = `${modelName}${(0, common_1.capitalize)(name)}${relation.model}`;
352
+ const relationFieldName = `${name}Id`;
353
+ const relationSuffix = relation.required === false ? '?' : '';
354
+ const type = relation.array
355
+ ? `${relation.model}[]`
356
+ : `${relation.model}${relationSuffix}`;
357
+ if (relation.array) {
358
+ attributes.push(`@relation("${relationName}")`);
359
+ }
360
+ else {
361
+ const referentialActions = [];
362
+ if (relation.onDelete) {
363
+ referentialActions.push(`onDelete: ${(0, common_1.capitalize)(relation.onDelete)}`);
364
+ }
365
+ if (relation.onUpdate) {
366
+ referentialActions.push(`onUpdate: ${(0, common_1.capitalize)(relation.onUpdate)}`);
367
+ }
368
+ const referentialConfig = referentialActions.length > 0 ? `, ${referentialActions.join(', ')}` : '';
369
+ attributes.push(`@relation("${relationName}", fields: [${relationFieldName}], references: [id]${referentialConfig})`);
370
+ }
371
+ const relationFields = [
372
+ {
373
+ name,
374
+ type,
375
+ attributes
376
+ }
377
+ ];
378
+ if (!relation.array && relation.owner) {
379
+ relationFields.push({
380
+ name: relationFieldName,
381
+ type: `Int${relationSuffix}`,
382
+ attributes: relation.unique === true ? ['@unique'] : []
383
+ });
384
+ }
385
+ return relationFields;
386
+ }
387
+ function createFilesSchema(modelName, files = {}) {
388
+ const fields = [];
389
+ for (const [name, file] of Object.entries(files)) {
390
+ fields.push(...createFileSchema(name, modelName, file));
391
+ }
392
+ return fields;
393
+ }
394
+ function createFileSchema(name, modelName, file) {
395
+ const attributes = [];
396
+ const type = file.array ? 'File[]' : 'File?';
397
+ const fileRelationName = `${modelName}${(0, common_1.capitalize)(name)}File`;
398
+ const fileRelationFieldName = `${name}Id`;
399
+ if (file.array) {
400
+ attributes.push(`@relation("${fileRelationName}")`);
401
+ }
402
+ else {
403
+ attributes.push(`@relation("${fileRelationName}", fields: [${fileRelationFieldName}], references: [id])`);
404
+ }
405
+ const fileFields = [
406
+ {
407
+ name,
408
+ type,
409
+ attributes
410
+ }
411
+ ];
412
+ if (!file.array) {
413
+ fileFields.push({
414
+ name: fileRelationFieldName,
415
+ type: 'Int?',
416
+ attributes: ['@unique']
417
+ });
418
+ }
419
+ return fileFields;
420
+ }
421
+ function createAuditSchema(modelName, authModel, audit = {}) {
422
+ const defaultAudit = {
423
+ updatedAt: true,
424
+ createdAt: true,
425
+ createdById: true
426
+ };
427
+ const mergedAudit = { ...defaultAudit, ...audit };
428
+ const fields = [];
429
+ const authModelName = authModel?.name;
430
+ if (mergedAudit.updatedAt) {
431
+ fields.push({
432
+ name: 'updatedAt',
433
+ type: 'DateTime',
434
+ attributes: ['@updatedAt']
435
+ });
436
+ }
437
+ if (mergedAudit.createdAt) {
438
+ fields.push({
439
+ name: 'createdAt',
440
+ type: 'DateTime',
441
+ attributes: ['@default(now())']
442
+ });
443
+ }
444
+ if (mergedAudit.createdById && authModelName) {
445
+ fields.push({
446
+ name: 'createdBy',
447
+ type: `${authModelName}?`,
448
+ attributes: [
449
+ `@relation("${modelName}CreatedBy${authModelName}", fields: [createdById], references: [id])`
450
+ ]
451
+ });
452
+ fields.push({
453
+ name: 'createdById',
454
+ type: 'Int?'
455
+ });
456
+ }
457
+ return fields;
458
+ }
459
+ function createIndexSchema(index) {
460
+ const indexes = [];
461
+ if (!index || index.length === 0) {
462
+ return indexes;
463
+ }
464
+ for (const idx of index) {
465
+ const indexValue = (0, common_1.isArray)(idx) ? `[${idx.join(', ')}]` : idx;
466
+ const indexExpression = `@@index(${indexValue})`;
467
+ if (!indexes.includes(indexExpression)) {
468
+ indexes.push(indexExpression);
469
+ }
470
+ }
471
+ return indexes;
472
+ }
473
+ function databaseType() {
474
+ return (0, common_1.resolveDatabaseType)(common_1.config.DATABASE_TYPE, common_1.config.DATABASE_URL);
475
+ }
@@ -0,0 +1,10 @@
1
+ import { ResourceModel } from '@appweaver/common';
2
+ /**
3
+ * Generates TypeScript type definitions for the given resource models and writes them to a specified directory.
4
+ *
5
+ * @param {Record<string, ResourceModel>} models - A set of resource models for which TypeScript types will be generated.
6
+ * @param {string} typesPath - The relative path to the directory where the generated types will be written.
7
+ * @param {boolean} [quiet=false] - If true, suppresses detailed logging during the operation.
8
+ * @return {Promise<number>} A promise that resolves to a status code: `0` for success, `2` for failure.
9
+ */
10
+ export declare function generateTypes(models: Record<string, ResourceModel>, typesPath: string, quiet?: boolean): Promise<number>;