@rapidd/build 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Rapidd Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @rapidd/build
2
+
3
+ Dynamic code generator that transforms Prisma schemas into complete Express.js CRUD APIs with intelligent PostgreSQL RLS-to-JavaScript translation.
4
+
5
+ ## Features
6
+
7
+ - πŸš€ **Automatic CRUD API Generation** - Creates Express.js routes from Prisma models
8
+ - πŸ”’ **RLS Translation** - Converts PostgreSQL Row-Level Security policies to JavaScript/Prisma filters
9
+ - 🎯 **Dynamic & Schema-Aware** - Zero hardcoding, adapts to any database structure
10
+ - πŸ”— **Relationship Handling** - Supports 1:1, 1:n, n:m including junction tables
11
+ - πŸ‘₯ **Role-Based Access Control** - Properly handles role checks in filters
12
+ - πŸ“Š **Model Generation** - Creates CRUD model classes with capitalized filenames
13
+ - πŸ—ΊοΈ **Relationships JSON** - Generates complete relationship mappings with foreign keys
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @rapidd/build
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ # Generate in current directory (default)
25
+ npx rapidd build
26
+
27
+ # Generate in specific directory
28
+ npx rapidd build --output ./generated
29
+
30
+ # Specify custom user table
31
+ npx rapidd build --user-table accounts
32
+ ```
33
+
34
+ ## Generated Structure
35
+
36
+ ```
37
+ ./
38
+ β”œβ”€β”€ src/Model/
39
+ β”‚ β”œβ”€β”€ User.js
40
+ β”‚ β”œβ”€β”€ Post.js
41
+ β”‚ └── ...
42
+ β”œβ”€β”€ routes/
43
+ β”‚ β”œβ”€β”€ user.js
44
+ β”‚ β”œβ”€β”€ post.js
45
+ β”‚ └── ...
46
+ └── rapidd/
47
+ β”œβ”€β”€ rls.js
48
+ β”œβ”€β”€ relationships.json
49
+ └── rapidd.js
50
+ ```
51
+
52
+ ## RLS Translation Example
53
+
54
+ **PostgreSQL Policy:**
55
+ ```sql
56
+ CREATE POLICY user_policy ON posts
57
+ FOR SELECT
58
+ USING (author_id = current_user_id() OR current_user_role() IN ('admin', 'moderator'));
59
+ ```
60
+
61
+ **Generated JavaScript:**
62
+ ```javascript
63
+ hasAccess: (data, user) => {
64
+ return data?.author_id === user?.id || ['admin', 'moderator'].includes(user?.role);
65
+ },
66
+ getAccessFilter: (user) => {
67
+ if (['admin', 'moderator'].includes(user?.role)) return {};
68
+ return { author_id: user?.id };
69
+ }
70
+ ```
71
+
72
+ ## CLI Options
73
+
74
+ - `-o, --output <path>` - Output directory (default: `./`)
75
+ - `-s, --schema <path>` - Prisma schema file (default: `./prisma/schema.prisma`)
76
+ - `-u, --user-table <name>` - User table name for RLS (default: auto-detected)
77
+
78
+ ## Usage with PostgreSQL RLS
79
+
80
+ ```bash
81
+ DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" npx rapidd build
82
+ ```
83
+
84
+ ## License
85
+
86
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+ const path = require('path');
5
+ const { buildModels } = require('../src/commands/build');
6
+
7
+ const program = new Command();
8
+
9
+ program
10
+ .name('rapidd')
11
+ .description('Rapidd build tool for generating model files from Prisma schema')
12
+ .version('0.1.0');
13
+
14
+ program
15
+ .command('build')
16
+ .description('Build model files from Prisma schema')
17
+ .option('-s, --schema <path>', 'Path to Prisma schema file', process.env.PRISMA_SCHEMA_PATH || './prisma/schema.prisma')
18
+ .option('-o, --output <path>', 'Output base directory', './')
19
+ .option('--user-table <name>', 'Name of the user table for RLS (default: auto-detect from user/users)')
20
+ .action(async (options) => {
21
+ try {
22
+ await buildModels(options);
23
+ console.log('\nβœ“ Build completed successfully');
24
+ } catch (error) {
25
+ console.error('Error building models:', error.message);
26
+ process.exit(1);
27
+ }
28
+ });
29
+
30
+ program.parse(process.argv);
package/index.js ADDED
@@ -0,0 +1,11 @@
1
+ const { buildModels } = require('./src/commands/build');
2
+ const { parsePrismaSchema, parsePrismaDMMF } = require('./src/parsers/prismaParser');
3
+ const { generateModelFile, generateAllModels } = require('./src/generators/modelGenerator');
4
+
5
+ module.exports = {
6
+ buildModels,
7
+ parsePrismaSchema,
8
+ parsePrismaDMMF,
9
+ generateModelFile,
10
+ generateAllModels
11
+ };
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@rapidd/build",
3
+ "version": "1.0.0",
4
+ "description": "Dynamic code generator that transforms Prisma schemas into Express.js CRUD APIs with PostgreSQL RLS-to-JavaScript translation",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "rapidd": "./bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1",
11
+ "prepublishOnly": "echo 'Ready to publish'"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/rapidd/build"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/rapidd/build/issues"
19
+ },
20
+ "homepage": "https://github.com/rapidd/build#readme",
21
+ "keywords": [
22
+ "rapidd",
23
+ "prisma",
24
+ "code-generation",
25
+ "orm",
26
+ "express",
27
+ "routes",
28
+ "rls",
29
+ "row-level-security",
30
+ "crud",
31
+ "api-generator",
32
+ "postgresql",
33
+ "mysql",
34
+ "database",
35
+ "security",
36
+ "access-control",
37
+ "authentication",
38
+ "authorization"
39
+ ],
40
+ "author": "Rapidd Team",
41
+ "license": "MIT",
42
+ "engines": {
43
+ "node": ">=14.0.0"
44
+ },
45
+ "files": [
46
+ "bin/",
47
+ "src/",
48
+ "index.js",
49
+ "README.md"
50
+ ],
51
+ "dependencies": {
52
+ "@prisma/client": "^5.0.0",
53
+ "commander": "^11.0.0",
54
+ "dotenv": "^16.0.0",
55
+ "pg": "^8.16.3"
56
+ },
57
+ "devDependencies": {
58
+ "prisma": "^5.0.0"
59
+ },
60
+ "peerDependencies": {
61
+ "@prisma/client": "^5.0.0"
62
+ }
63
+ }
@@ -0,0 +1,448 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { parsePrismaSchema, parsePrismaDMMF } = require('../parsers/prismaParser');
4
+ const { generateAllModels } = require('../generators/modelGenerator');
5
+ const { generateRelationshipsFromDMMF, generateRelationshipsFromSchema } = require('../generators/relationshipsGenerator');
6
+ const { generateRLS } = require('../generators/rlsGeneratorV2');
7
+ const { parseDatasource } = require('../parsers/datasourceParser');
8
+ const { generateAllRoutes } = require('../generators/routeGenerator');
9
+
10
+ /**
11
+ * Generate src/Model.js base class file
12
+ */
13
+ function generateBaseModelFile(modelJsPath) {
14
+ const content = `const { QueryBuilder, prisma } = require("./QueryBuilder");
15
+ const {ErrorResponse} = require('./Api');
16
+
17
+ class Model {
18
+ /**
19
+ * @param {string} name
20
+ * @param {{'user': {}}} options
21
+ */
22
+ constructor(name, options){
23
+ this.modelName = name;
24
+ this.options = options || {}
25
+ this.user = this.options.user || {'id': 1, 'role': 'application'};
26
+ this.user_id = this.user ? this.user.id : null;
27
+ }
28
+
29
+ _select = (fields) => this.constructor.queryBuilder.select(fields);
30
+ _filter = (q) => this.constructor.queryBuilder.filter(q);
31
+ _include = (include) => this.constructor.queryBuilder.include(include, this.user);
32
+ _getAccessFilter = () => this.constructor.getAccessFilter(this.user);
33
+ _hasAccess = (data) => this.constructor.hasAccess(data, this.user) || false;
34
+ _omit = () => this.constructor.queryBuilder.omit(this.user);
35
+
36
+ /**
37
+ *
38
+ * @param {string} q
39
+ * @property {string|Object} include
40
+ * @param {number} limit
41
+ * @param {number} offset
42
+ * @param {string} sortBy
43
+ * @param {'asc'|'desc'} sortOrder
44
+ * @returns {Promise<Object[]>}
45
+ */
46
+ _getMany = async (q = {}, include = "", limit = 25, offset = 0, sortBy = "id", sortOrder = "asc", options = {})=>{
47
+ const take = this.take(Number(limit));
48
+ const skip = this.skip(Number(offset));
49
+
50
+ sortBy = sortBy.trim();
51
+ sortOrder = sortOrder.trim();
52
+ if (!sortBy.includes('.') && this.fields[sortBy] == undefined) {
53
+ throw new ErrorResponse(\`Parameter sortBy '\${sortBy}' is not a valid field of \${this.constructor.name}\`, 400);
54
+ }
55
+
56
+ // Query the database using Prisma with filters, pagination, and limits
57
+ return await this.prisma.findMany({
58
+ 'where': this.filter(q),
59
+ 'include': this.include(include),
60
+ 'take': take,
61
+ 'skip': skip,
62
+ 'orderBy': this.sort(sortBy, sortOrder),
63
+ 'omit': this._omit(),
64
+ ...options
65
+ });
66
+ }
67
+ /**
68
+ * @param {number} id
69
+ * @param {string | Object} include
70
+ * @returns {Promise<{} | null>}
71
+ */
72
+ _get = async (id, include, options = {}) =>{
73
+ const {omit, ..._options} = options;
74
+ id = Number(id)
75
+ // To determine if the record is inaccessible, either due to non-existence or insufficient permissions, two simultaneous queries are performed.
76
+ const _response = this.prisma.findUnique({
77
+ 'where': {
78
+ 'id': id,
79
+ ...this.getAccessFilter()
80
+ },
81
+ 'include': this.include(include),
82
+ 'omit': {...this._omit(), ...omit},
83
+ ..._options
84
+ });
85
+
86
+ const _checkExistence = this.prisma.findUnique({
87
+ 'where': {
88
+ 'id': id
89
+ },
90
+ 'select': {
91
+ 'id': true
92
+ }
93
+ });
94
+
95
+ const [response, checkExistence] = await Promise.all([_response, _checkExistence]);
96
+
97
+ if(response == null){
98
+ if(checkExistence == null){
99
+ throw new ErrorResponse("Record not found", 404);
100
+ }
101
+ throw new ErrorResponse("No permission", 403);
102
+ }
103
+ if(response.id != checkExistence?.id){ // IN CASE access_filter CONTAINS id FIELD
104
+ throw new ErrorResponse("No permission", 403);
105
+ }
106
+ return response;
107
+ }
108
+ /**
109
+ * @param {{}} data
110
+ * @returns {Promise<Object>}
111
+ */
112
+ _create = async (data, options = {}) => {
113
+ // VALIDATE PASSED FIELDS AND RELATIONSHIPS
114
+ this.constructor.queryBuilder.create(data, this.user_id);
115
+
116
+ // CREATE
117
+ return await this.prisma.create({
118
+ 'data': data,
119
+ 'include': this.include('ALL'),
120
+ ...options
121
+ });
122
+ }
123
+
124
+ /**
125
+ * @param {number} id
126
+ * @param {{}} data
127
+ * @returns {Promise<Object>}
128
+ */
129
+ _update = async (id, data, options = {}) => {
130
+ id = Number(id);
131
+ // GET DATA FIRST
132
+ const current_data = await this._get(id, "ALL");
133
+
134
+ // VALIDATE PASSED FIELDS AND RELATIONSHIPS
135
+ this.constructor.queryBuilder.update(id, data, this.user_id);
136
+ return await this.prisma.update({
137
+ 'where': {
138
+ 'id': id
139
+ },
140
+ 'data': data,
141
+ 'include': this.include('ALL'),
142
+ ...options
143
+ });
144
+ }
145
+
146
+ /**
147
+ *
148
+ * @param {string} q
149
+ * @returns {Promise<number>}
150
+ */
151
+ _count = async (q = {}) => {
152
+ return await this.prisma.count({
153
+ 'where': this.filter(q)
154
+ });
155
+ }
156
+
157
+ /**
158
+ * @param {number} id
159
+ * @returns {Promise<Object>}
160
+ */
161
+ _delete = async (id, options = {}) => {
162
+ // GET DATA FIRST
163
+ const current_data = await this._get(id);
164
+
165
+ return await this.prisma.delete({
166
+ 'where': {
167
+ id: parseInt(id)
168
+ },
169
+ 'select': this.select(),
170
+ ...options
171
+ });
172
+ }
173
+
174
+ /**
175
+ *
176
+ * @param {string} q
177
+ * @property {string|Object} include
178
+ * @param {number} limit
179
+ * @param {number} offset
180
+ * @param {string} sortBy
181
+ * @param {'asc'|'desc'} sortOrder
182
+ * @returns {Promise<Object[]>}
183
+ */
184
+ async getMany(q = {}, include = "", limit = 25, offset = 0, sortBy = "id", sortOrder = "asc"){
185
+ return await this._getMany(q, include, Number(limit), Number(offset), sortBy, sortOrder);
186
+ }
187
+ /**
188
+ * @param {number} id
189
+ * @param {string | Object} include
190
+ * @returns {Promise<{} | null>}
191
+ */
192
+ async get(id, include, options = {}){
193
+ return await this._get(Number(id), include, options);
194
+ }
195
+
196
+ /**
197
+ * @param {number} id
198
+ * @param {{}} data
199
+ * @returns {Promise<Object>}
200
+ */
201
+ async update(id, data, options = {}){
202
+ return await this._update(Number(id), data, options);
203
+ }
204
+
205
+ /**
206
+ *
207
+ * @param {string} q
208
+ * @returns {Promise<number>}
209
+ */
210
+ async count(q = {}) {
211
+ return await this._count(q);
212
+ }
213
+
214
+ /**
215
+ * @param {number} id
216
+ * @returns {Promise<Object>}
217
+ */
218
+ async delete(id, data, options = {}){
219
+ return await this._delete(Number(id), data, options);
220
+ }
221
+
222
+ select(fields){
223
+ return this._select(fields);
224
+ }
225
+ filter(include){
226
+ return {...this._filter(include), ...this.getAccessFilter()};
227
+ }
228
+ include(include){
229
+ return this._include(include);
230
+ }
231
+ sort(sortBy, sortOrder) {
232
+ return this.constructor.queryBuilder.sort(sortBy, sortOrder);
233
+ }
234
+ take(limit){
235
+ return this.constructor.queryBuilder.take(Number(limit));
236
+ }
237
+ skip(offset){
238
+ const parsed = parseInt(offset);
239
+ if(isNaN(parsed) || parsed < 0){
240
+ return 0;
241
+ }
242
+ return parsed;
243
+ }
244
+
245
+ /**
246
+ *
247
+ * @returns {Object}
248
+ */
249
+ getAccessFilter(){
250
+ const filter = this._getAccessFilter()
251
+ if(this.user.role == "application" || filter == true){
252
+ return {};
253
+ }
254
+ return this._getAccessFilter();
255
+ }
256
+
257
+ /**
258
+ *
259
+ * @param {*} data
260
+ * @returns {boolean}
261
+ */
262
+ hasAccess(data) {
263
+ return this.user.role == "application" ? true : this._hasAccess(data, this.user);
264
+ }
265
+
266
+ set modelName (name){
267
+ this.name = name;
268
+ this.prisma = prisma[name];
269
+ this.fields = this.prisma.fields;
270
+ }
271
+
272
+ static relatedObjects = [];
273
+ static Error = ErrorResponse;
274
+ }
275
+
276
+ module.exports = {Model, QueryBuilder, prisma};
277
+ `;
278
+
279
+ fs.writeFileSync(modelJsPath, content);
280
+ console.log('βœ“ Generated src/Model.js');
281
+ }
282
+
283
+ /**
284
+ * Generate rapidd/rapidd.js file
285
+ */
286
+ function generateRapiddFile(rapiddJsPath) {
287
+ const content = `const { PrismaClient, Prisma } = require('../prisma/client');
288
+ const rls = require('./rls');
289
+
290
+ const prisma = new PrismaClient();
291
+
292
+ module.exports = {prisma, Prisma, rls};
293
+ `;
294
+
295
+ // Ensure rapidd directory exists
296
+ const rapiddDir = path.dirname(rapiddJsPath);
297
+ if (!fs.existsSync(rapiddDir)) {
298
+ fs.mkdirSync(rapiddDir, { recursive: true });
299
+ }
300
+
301
+ fs.writeFileSync(rapiddJsPath, content);
302
+ console.log('βœ“ Generated rapidd/rapidd.js');
303
+ }
304
+
305
+ /**
306
+ * Build models from Prisma schema
307
+ * @param {Object} options - Build options
308
+ * @param {string} options.schema - Path to Prisma schema file
309
+ * @param {string} options.output - Output directory for generated models
310
+ * @param {string} options.relationships - Path to relationships.json file
311
+ */
312
+ async function buildModels(options) {
313
+ const schemaPath = path.resolve(process.cwd(), options.schema);
314
+ const outputBase = path.resolve(process.cwd(), options.output);
315
+
316
+ // If output is "/", use process.cwd() as the base
317
+ const baseDir = options.output === '/' ? process.cwd() : outputBase;
318
+
319
+ // Construct paths
320
+ const srcDir = path.join(baseDir, 'src');
321
+ const modelDir = path.join(srcDir, 'Model');
322
+ const modelJsPath = path.join(srcDir, 'Model.js');
323
+ const rapiddDir = path.join(baseDir, 'rapidd');
324
+ const relationshipsPath = path.join(rapiddDir, 'relationships.json');
325
+ const rlsPath = path.join(rapiddDir, 'rls.js');
326
+ const rapiddJsPath = path.join(rapiddDir, 'rapidd.js');
327
+ const routesDir = path.join(baseDir, 'routes', 'api', 'v1');
328
+ const logsDir = path.join(baseDir, 'logs');
329
+
330
+ console.log('Building Rapidd models...');
331
+ console.log(`Schema: ${schemaPath}`);
332
+ console.log(`Output: ${baseDir}`);
333
+
334
+ // Create logs directory
335
+ if (!fs.existsSync(logsDir)) {
336
+ fs.mkdirSync(logsDir, { recursive: true });
337
+ }
338
+
339
+ // Check if schema file exists
340
+ if (!fs.existsSync(schemaPath)) {
341
+ throw new Error(`Prisma schema file not found at: ${schemaPath}`);
342
+ }
343
+
344
+ // Run npx prisma generate first
345
+ console.log('\nRunning npx prisma generate...');
346
+ const { execSync } = require('child_process');
347
+ try {
348
+ execSync(`npx prisma generate --schema=${schemaPath}`, {
349
+ stdio: 'inherit',
350
+ cwd: process.cwd()
351
+ });
352
+ console.log('βœ“ Prisma client generated successfully\n');
353
+ } catch (error) {
354
+ console.warn('⚠ Warning: Failed to generate Prisma client');
355
+ console.warn('Continuing with schema parsing fallback...\n');
356
+ }
357
+
358
+ // Try to use Prisma DMMF first (if prisma generate has been run)
359
+ let parsedData = null;
360
+ const prismaClientPath = path.join(process.cwd(), 'prisma', 'client');
361
+ let usedDMMF = false;
362
+
363
+ try {
364
+ parsedData = await parsePrismaDMMF(prismaClientPath);
365
+ if (parsedData) {
366
+ console.log('Using Prisma generated client (DMMF)');
367
+ usedDMMF = true;
368
+ }
369
+ } catch (error) {
370
+ // Fall back to schema parsing
371
+ }
372
+
373
+ // If DMMF parsing failed, parse schema file directly
374
+ if (!parsedData) {
375
+ console.log('Parsing Prisma schema file...');
376
+ parsedData = parsePrismaSchema(schemaPath);
377
+ }
378
+
379
+ const { models, enums } = parsedData;
380
+
381
+ console.log(`Found ${Object.keys(models).length} models`);
382
+
383
+ // Generate model files
384
+ generateAllModels(models, modelDir, modelJsPath);
385
+
386
+ // Generate src/Model.js (base Model class)
387
+ console.log('\nGenerating src/Model.js...');
388
+ generateBaseModelFile(modelJsPath);
389
+
390
+ // Generate rapidd/rapidd.js
391
+ console.log('Generating rapidd/rapidd.js...');
392
+ generateRapiddFile(rapiddJsPath);
393
+
394
+ // Generate relationships.json
395
+ console.log(`\nGenerating relationships.json...`);
396
+
397
+ try {
398
+ if (usedDMMF) {
399
+ await generateRelationshipsFromDMMF(prismaClientPath, relationshipsPath);
400
+ } else {
401
+ generateRelationshipsFromSchema(schemaPath, relationshipsPath);
402
+ }
403
+ console.log(`βœ“ Relationships file generated at: ${relationshipsPath}`);
404
+ } catch (error) {
405
+ console.error('Failed to generate relationships.json:', error.message);
406
+ console.log('Note: You may need to create relationships.json manually.');
407
+ }
408
+
409
+ // Generate RLS configuration
410
+ console.log(`\nGenerating RLS configuration...`);
411
+
412
+ // Load relationships for Prisma filter building
413
+ let relationships = {};
414
+ try {
415
+ if (fs.existsSync(relationshipsPath)) {
416
+ relationships = JSON.parse(fs.readFileSync(relationshipsPath, 'utf8'));
417
+ }
418
+ } catch (error) {
419
+ console.warn('Could not load relationships.json:', error.message);
420
+ }
421
+
422
+ try {
423
+ // Parse datasource from Prisma schema to get database URL
424
+ const datasource = parseDatasource(schemaPath);
425
+
426
+ await generateRLS(
427
+ models,
428
+ rlsPath,
429
+ datasource.url,
430
+ datasource.isPostgreSQL,
431
+ options.userTable,
432
+ relationships
433
+ );
434
+ } catch (error) {
435
+ console.error('Failed to generate RLS:', error.message);
436
+ console.log('Generating permissive RLS fallback...');
437
+ await generateRLS(models, rlsPath, null, false, options.userTable, relationships);
438
+ }
439
+
440
+ // Generate routes
441
+ generateAllRoutes(models, routesDir);
442
+
443
+ return { models, enums };
444
+ }
445
+
446
+ module.exports = {
447
+ buildModels
448
+ };