db-model-router 1.0.3 → 1.0.5

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 (97) hide show
  1. package/README.md +283 -25
  2. package/TODO.md +14 -0
  3. package/dbmr.schema.json +333 -0
  4. package/demo/.dockerignore +7 -0
  5. package/demo/.env.example +13 -0
  6. package/demo/Dockerfile +20 -0
  7. package/demo/app.js +37 -0
  8. package/demo/commons/add_migration.js +43 -0
  9. package/demo/commons/db.js +17 -0
  10. package/demo/commons/migrate.js +65 -0
  11. package/demo/commons/security.js +30 -0
  12. package/demo/commons/session.js +13 -0
  13. package/demo/dbmr.schema.json +362 -0
  14. package/demo/docs/llm.md +197 -0
  15. package/demo/llms.txt +70 -0
  16. package/demo/middleware/logger.js +67 -0
  17. package/demo/migrations/20260430155808_create_migrations_table.sql +6 -0
  18. package/demo/migrations/20260430155809_create_tables.sql +207 -0
  19. package/demo/models/addresses.js +22 -0
  20. package/demo/models/cart_items.js +18 -0
  21. package/demo/models/carts.js +16 -0
  22. package/demo/models/categories.js +20 -0
  23. package/demo/models/coupons.js +23 -0
  24. package/demo/models/order_items.js +21 -0
  25. package/demo/models/orders.js +25 -0
  26. package/demo/models/payments.js +21 -0
  27. package/demo/models/product_images.js +18 -0
  28. package/demo/models/product_reviews.js +20 -0
  29. package/demo/models/product_variants.js +20 -0
  30. package/demo/models/products.js +30 -0
  31. package/demo/models/shipments.js +19 -0
  32. package/demo/models/users.js +19 -0
  33. package/demo/models/wishlists.js +15 -0
  34. package/demo/openapi.json +5872 -0
  35. package/demo/package-lock.json +2810 -0
  36. package/demo/package.json +34 -0
  37. package/demo/routes/addresses.js +6 -0
  38. package/demo/routes/carts/cart_items.js +7 -0
  39. package/demo/routes/carts.js +6 -0
  40. package/demo/routes/categories.js +6 -0
  41. package/demo/routes/coupons.js +6 -0
  42. package/demo/routes/docs.js +18 -0
  43. package/demo/routes/health.js +35 -0
  44. package/demo/routes/index.js +39 -0
  45. package/demo/routes/orders/order_items.js +7 -0
  46. package/demo/routes/orders/payments.js +7 -0
  47. package/demo/routes/orders/shipments.js +7 -0
  48. package/demo/routes/orders.js +6 -0
  49. package/demo/routes/products/product_images.js +7 -0
  50. package/demo/routes/products/product_reviews.js +7 -0
  51. package/demo/routes/products/product_variants.js +7 -0
  52. package/demo/routes/products.js +6 -0
  53. package/demo/routes/users.js +6 -0
  54. package/demo/routes/wishlists.js +6 -0
  55. package/docker-compose.yml +1 -1
  56. package/package.json +16 -7
  57. package/scripts/demo-create.js +47 -0
  58. package/skill/SKILL.md +464 -0
  59. package/skill/references/cockroachdb.md +49 -0
  60. package/skill/references/dynamodb.md +53 -0
  61. package/skill/references/mongodb.md +56 -0
  62. package/skill/references/mssql.md +55 -0
  63. package/skill/references/oracle.md +52 -0
  64. package/skill/references/postgres.md +50 -0
  65. package/skill/references/redis.md +53 -0
  66. package/skill/references/sqlite3.md +43 -0
  67. package/src/cli/commands/generate.js +58 -17
  68. package/src/cli/commands/help.js +185 -0
  69. package/src/cli/commands/init.js +42 -14
  70. package/src/cli/commands/inspect.js +21 -3
  71. package/src/cli/diff-engine.js +52 -22
  72. package/src/cli/generate-docs-route.js +31 -0
  73. package/src/cli/generate-migration.js +356 -0
  74. package/src/cli/generate-model.js +5 -4
  75. package/src/cli/generate-route.js +79 -45
  76. package/src/cli/init/dependencies.js +17 -5
  77. package/src/cli/init/generators.js +1073 -64
  78. package/src/cli/init/prompt.js +37 -5
  79. package/src/cli/init.js +148 -25
  80. package/src/cli/main.js +90 -10
  81. package/src/cockroachdb/db.js +90 -59
  82. package/src/commons/route.js +20 -20
  83. package/src/commons/validator.js +58 -1
  84. package/src/dynamodb/db.js +50 -27
  85. package/src/index.js +2 -0
  86. package/src/mongodb/db.js +1 -0
  87. package/src/mssql/db.js +89 -61
  88. package/src/mysql/db.js +1 -0
  89. package/src/oracle/db.js +1 -0
  90. package/src/postgres/db.js +61 -41
  91. package/src/redis/db.js +1 -0
  92. package/src/schema/schema-parser.js +43 -1
  93. package/src/schema/schema-printer.js +8 -5
  94. package/src/schema/schema-to-meta.js +4 -0
  95. package/src/schema/schema-validator.js +20 -1
  96. package/src/sqlite3/db.js +1 -0
  97. package/docs/SKILL.md +0 -374
@@ -0,0 +1,333 @@
1
+ {
2
+ "adapter": "sqlite3",
3
+ "framework": "express",
4
+ "options": {
5
+ "session": "redis",
6
+ "rateLimiting": true,
7
+ "helmet": true,
8
+ "logger": true,
9
+ "loki": false
10
+ },
11
+ "tables": {
12
+ "users": {
13
+ "columns": {
14
+ "user_id": "auto_increment",
15
+ "name": "required|string",
16
+ "email": "required|string",
17
+ "password_hash": "required|string",
18
+ "phone": "string",
19
+ "avatar_url": "string",
20
+ "role": "required|string",
21
+ "is_deleted": "boolean",
22
+ "created_at": "datetime",
23
+ "updated_at": "datetime"
24
+ },
25
+ "pk": "user_id",
26
+ "unique": ["email"],
27
+ "softDelete": "is_deleted",
28
+ "timestamps": {
29
+ "created_at": "created_at",
30
+ "modified_at": "updated_at"
31
+ },
32
+ "parent": null
33
+ },
34
+ "addresses": {
35
+ "columns": {
36
+ "address_id": "auto_increment",
37
+ "user_id": "required|integer",
38
+ "label": "string",
39
+ "line1": "required|string",
40
+ "line2": "string",
41
+ "city": "required|string",
42
+ "state": "required|string",
43
+ "postal_code": "required|string",
44
+ "country": "required|string",
45
+ "is_default": "boolean",
46
+ "created_at": "datetime",
47
+ "updated_at": "datetime"
48
+ },
49
+ "pk": "address_id",
50
+ "unique": ["address_id"],
51
+ "timestamps": {
52
+ "created_at": "created_at",
53
+ "modified_at": "updated_at"
54
+ },
55
+ "parent": null
56
+ },
57
+ "categories": {
58
+ "columns": {
59
+ "category_id": "auto_increment",
60
+ "name": "required|string",
61
+ "slug": "required|string",
62
+ "description": "string",
63
+ "parent_category_id": "integer",
64
+ "image_url": "string",
65
+ "sort_order": "integer",
66
+ "is_active": "boolean",
67
+ "created_at": "datetime",
68
+ "updated_at": "datetime"
69
+ },
70
+ "pk": "category_id",
71
+ "unique": ["slug"],
72
+ "timestamps": {
73
+ "created_at": "created_at",
74
+ "modified_at": "updated_at"
75
+ },
76
+ "parent": null
77
+ },
78
+ "products": {
79
+ "columns": {
80
+ "product_id": "auto_increment",
81
+ "category_id": "required|integer",
82
+ "name": "required|string",
83
+ "slug": "required|string",
84
+ "description": "string",
85
+ "short_description": "string",
86
+ "sku": "required|string",
87
+ "price": "required|numeric",
88
+ "compare_at_price": "numeric",
89
+ "cost_price": "numeric",
90
+ "currency": "required|string",
91
+ "stock_quantity": "required|integer",
92
+ "low_stock_threshold": "integer",
93
+ "weight": "numeric",
94
+ "weight_unit": "string",
95
+ "is_active": "boolean",
96
+ "is_featured": "boolean",
97
+ "is_deleted": "boolean",
98
+ "meta": "object",
99
+ "created_at": "datetime",
100
+ "updated_at": "datetime"
101
+ },
102
+ "pk": "product_id",
103
+ "unique": ["sku", "slug"],
104
+ "softDelete": "is_deleted",
105
+ "timestamps": {
106
+ "created_at": "created_at",
107
+ "modified_at": "updated_at"
108
+ },
109
+ "parent": null
110
+ },
111
+ "product_images": {
112
+ "columns": {
113
+ "product_image_id": "auto_increment",
114
+ "product_id": "required|integer",
115
+ "url": "required|string",
116
+ "alt_text": "string",
117
+ "sort_order": "integer",
118
+ "is_primary": "boolean",
119
+ "created_at": "datetime"
120
+ },
121
+ "pk": "product_image_id",
122
+ "unique": ["product_image_id"],
123
+ "timestamps": {
124
+ "created_at": "created_at"
125
+ },
126
+ "parent": "products"
127
+ },
128
+ "product_variants": {
129
+ "columns": {
130
+ "variant_id": "auto_increment",
131
+ "product_id": "required|integer",
132
+ "name": "required|string",
133
+ "sku": "required|string",
134
+ "price": "required|numeric",
135
+ "stock_quantity": "required|integer",
136
+ "attributes": "object",
137
+ "is_active": "boolean",
138
+ "created_at": "datetime",
139
+ "updated_at": "datetime"
140
+ },
141
+ "pk": "variant_id",
142
+ "unique": ["sku"],
143
+ "timestamps": {
144
+ "created_at": "created_at",
145
+ "modified_at": "updated_at"
146
+ },
147
+ "parent": "products"
148
+ },
149
+ "product_reviews": {
150
+ "columns": {
151
+ "review_id": "auto_increment",
152
+ "product_id": "required|integer",
153
+ "user_id": "required|integer",
154
+ "rating": "required|integer",
155
+ "title": "string",
156
+ "body": "string",
157
+ "is_verified": "boolean",
158
+ "is_approved": "boolean",
159
+ "created_at": "datetime",
160
+ "updated_at": "datetime"
161
+ },
162
+ "pk": "review_id",
163
+ "unique": ["review_id"],
164
+ "timestamps": {
165
+ "created_at": "created_at",
166
+ "modified_at": "updated_at"
167
+ },
168
+ "parent": "products"
169
+ },
170
+ "carts": {
171
+ "columns": {
172
+ "cart_id": "auto_increment",
173
+ "user_id": "integer",
174
+ "session_id": "string",
175
+ "currency": "required|string",
176
+ "created_at": "datetime",
177
+ "updated_at": "datetime"
178
+ },
179
+ "pk": "cart_id",
180
+ "unique": ["cart_id"],
181
+ "timestamps": {
182
+ "created_at": "created_at",
183
+ "modified_at": "updated_at"
184
+ },
185
+ "parent": null
186
+ },
187
+ "cart_items": {
188
+ "columns": {
189
+ "cart_item_id": "auto_increment",
190
+ "cart_id": "required|integer",
191
+ "product_id": "required|integer",
192
+ "variant_id": "integer",
193
+ "quantity": "required|integer",
194
+ "unit_price": "required|numeric",
195
+ "created_at": "datetime",
196
+ "updated_at": "datetime"
197
+ },
198
+ "pk": "cart_item_id",
199
+ "unique": ["cart_item_id"],
200
+ "timestamps": {
201
+ "created_at": "created_at",
202
+ "modified_at": "updated_at"
203
+ },
204
+ "parent": "carts"
205
+ },
206
+ "orders": {
207
+ "columns": {
208
+ "order_id": "auto_increment",
209
+ "user_id": "required|integer",
210
+ "order_number": "required|string",
211
+ "status": "required|string",
212
+ "subtotal": "required|numeric",
213
+ "tax_amount": "required|numeric",
214
+ "shipping_amount": "required|numeric",
215
+ "discount_amount": "numeric",
216
+ "total": "required|numeric",
217
+ "currency": "required|string",
218
+ "shipping_address_id": "integer",
219
+ "billing_address_id": "integer",
220
+ "notes": "string",
221
+ "created_at": "datetime",
222
+ "updated_at": "datetime"
223
+ },
224
+ "pk": "order_id",
225
+ "unique": ["order_number"],
226
+ "timestamps": {
227
+ "created_at": "created_at",
228
+ "modified_at": "updated_at"
229
+ },
230
+ "parent": null
231
+ },
232
+ "order_items": {
233
+ "columns": {
234
+ "order_item_id": "auto_increment",
235
+ "order_id": "required|integer",
236
+ "product_id": "required|integer",
237
+ "variant_id": "integer",
238
+ "product_name": "required|string",
239
+ "sku": "required|string",
240
+ "quantity": "required|integer",
241
+ "unit_price": "required|numeric",
242
+ "total_price": "required|numeric",
243
+ "created_at": "datetime"
244
+ },
245
+ "pk": "order_item_id",
246
+ "unique": ["order_item_id"],
247
+ "timestamps": {
248
+ "created_at": "created_at"
249
+ },
250
+ "parent": "orders"
251
+ },
252
+ "payments": {
253
+ "columns": {
254
+ "payment_id": "auto_increment",
255
+ "order_id": "required|integer",
256
+ "method": "required|string",
257
+ "provider": "string",
258
+ "provider_transaction_id": "string",
259
+ "amount": "required|numeric",
260
+ "currency": "required|string",
261
+ "status": "required|string",
262
+ "paid_at": "datetime",
263
+ "created_at": "datetime",
264
+ "updated_at": "datetime"
265
+ },
266
+ "pk": "payment_id",
267
+ "unique": ["payment_id"],
268
+ "timestamps": {
269
+ "created_at": "created_at",
270
+ "modified_at": "updated_at"
271
+ },
272
+ "parent": "orders"
273
+ },
274
+ "shipments": {
275
+ "columns": {
276
+ "shipment_id": "auto_increment",
277
+ "order_id": "required|integer",
278
+ "carrier": "required|string",
279
+ "tracking_number": "string",
280
+ "status": "required|string",
281
+ "shipped_at": "datetime",
282
+ "delivered_at": "datetime",
283
+ "created_at": "datetime",
284
+ "updated_at": "datetime"
285
+ },
286
+ "pk": "shipment_id",
287
+ "unique": ["shipment_id"],
288
+ "timestamps": {
289
+ "created_at": "created_at",
290
+ "modified_at": "updated_at"
291
+ },
292
+ "parent": "orders"
293
+ },
294
+ "coupons": {
295
+ "columns": {
296
+ "coupon_id": "auto_increment",
297
+ "code": "required|string",
298
+ "description": "string",
299
+ "discount_type": "required|string",
300
+ "discount_value": "required|numeric",
301
+ "min_order_amount": "numeric",
302
+ "max_uses": "integer",
303
+ "used_count": "integer",
304
+ "starts_at": "datetime",
305
+ "expires_at": "datetime",
306
+ "is_active": "boolean",
307
+ "created_at": "datetime",
308
+ "updated_at": "datetime"
309
+ },
310
+ "pk": "coupon_id",
311
+ "unique": ["code"],
312
+ "timestamps": {
313
+ "created_at": "created_at",
314
+ "modified_at": "updated_at"
315
+ },
316
+ "parent": null
317
+ },
318
+ "wishlists": {
319
+ "columns": {
320
+ "wishlist_id": "auto_increment",
321
+ "user_id": "required|integer",
322
+ "product_id": "required|integer",
323
+ "created_at": "datetime"
324
+ },
325
+ "pk": "wishlist_id",
326
+ "unique": ["wishlist_id"],
327
+ "timestamps": {
328
+ "created_at": "created_at"
329
+ },
330
+ "parent": null
331
+ }
332
+ }
333
+ }
@@ -0,0 +1,7 @@
1
+ node_modules
2
+ npm-debug.log
3
+ .env
4
+ .env.example
5
+ .git
6
+ .gitignore
7
+ data
@@ -0,0 +1,13 @@
1
+ # Server
2
+ PORT=3000
3
+
4
+ # Database
5
+ DB_NAME=./data/data.db
6
+
7
+ # Session
8
+ SESSION_SECRET=your_session_secret
9
+
10
+ # Logging
11
+ APP_NAME=your_app_name
12
+ LOG_LEVEL=info
13
+ LOKI_HOST=http://your-loki-host:3100
@@ -0,0 +1,20 @@
1
+ FROM node:alpine
2
+
3
+ WORKDIR /app
4
+
5
+ # Install dependencies
6
+ COPY package*.json ./
7
+ RUN npm ci --omit=dev
8
+
9
+ # Copy application files
10
+ COPY app.js ./
11
+ COPY commons/ ./commons/
12
+ COPY middleware/ ./middleware/
13
+ COPY route/ ./route/
14
+ COPY migrations/ ./migrations/
15
+
16
+ # Expose port
17
+ EXPOSE 3000
18
+
19
+ # Start the application
20
+ CMD ["node", "app.js"]
package/demo/app.js ADDED
@@ -0,0 +1,37 @@
1
+ import express from "express";
2
+ import "./commons/db.js";
3
+ import configureSession from "./commons/session.js";
4
+ import applySecurity from "./commons/security.js";
5
+ import logger from "./middleware/logger.js";
6
+ import route from "./routes/index.js";
7
+
8
+ const app = express();
9
+ const PORT = process.env.PORT || 3000;
10
+
11
+ // Middleware
12
+ app.use(express.json());
13
+ app.use(express.urlencoded({ extended: true }));
14
+
15
+ // Security (helmet, rate limiting, custom headers)
16
+ applySecurity(app);
17
+
18
+ // Session
19
+ app.use(configureSession());
20
+
21
+ // Logger
22
+ app.use(logger);
23
+
24
+ // Routes
25
+ app.use(route);
26
+
27
+ // Error handler
28
+ app.use((err, req, res, next) => {
29
+ console.error(err.stack);
30
+ res.status(500).json({ type: "danger", message: "Internal Server Error" });
31
+ });
32
+
33
+ app.listen(PORT, () => {
34
+ console.log(`Server running on port ${PORT}`);
35
+ });
36
+
37
+ export default app;
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ /**
9
+ * Create a new timestamped migration file.
10
+ * @param {string} migrationsDir - absolute path to migrations folder
11
+ * @param {string} [name] - migration name (default: "migration")
12
+ * @returns {string} the created filename
13
+ */
14
+ export default function addMigration(migrationsDir, name) {
15
+ const migrationName = name || "migration";
16
+ const now = new Date();
17
+ const y = String(now.getFullYear()).padStart(4, "0");
18
+ const mo = String(now.getMonth() + 1).padStart(2, "0");
19
+ const d = String(now.getDate()).padStart(2, "0");
20
+ const h = String(now.getHours()).padStart(2, "0");
21
+ const mi = String(now.getMinutes()).padStart(2, "0");
22
+ const s = String(now.getSeconds()).padStart(2, "0");
23
+ const ts = `${y}${mo}${d}${h}${mi}${s}`;
24
+
25
+ const filename = `${ts}_${migrationName}.sql`;
26
+ const filePath = path.join(migrationsDir, filename);
27
+
28
+ if (!fs.existsSync(migrationsDir)) {
29
+ fs.mkdirSync(migrationsDir, { recursive: true });
30
+ }
31
+
32
+ fs.writeFileSync(filePath, "-- Write your migration SQL here\n");
33
+ console.log(`Created migration: ${filename}`);
34
+ return filename;
35
+ }
36
+
37
+ // Run as standalone script
38
+ const isMain = process.argv[1] && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
39
+ if (isMain) {
40
+ const migrationsDir = path.join(__dirname, "../migrations");
41
+ const name = process.argv[2] || "migration";
42
+ addMigration(migrationsDir, name);
43
+ }
@@ -0,0 +1,17 @@
1
+ import "dotenv/config";
2
+ import dbModelRouter from "db-model-router";
3
+
4
+ // Initialize database adapter
5
+ dbModelRouter.init("sqlite3");
6
+
7
+ // Connect to database
8
+ dbModelRouter.db.connect({
9
+ database: process.env.DB_NAME || "./data/data.db",
10
+ });
11
+
12
+ // Make db available globally across the application
13
+ const db = dbModelRouter.db;
14
+ global.db = db;
15
+
16
+ export { db };
17
+ export default db;
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import crypto from "crypto";
5
+ import { fileURLToPath } from "url";
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+
9
+ /**
10
+ * Run all pending SQL migrations from the migrations directory.
11
+ * @param {object} db - db-model-router db instance
12
+ * @param {string} migrationsDir - absolute path to migrations folder
13
+ */
14
+ export default async function runMigrations(db, migrationsDir) {
15
+ const files = fs.readdirSync(migrationsDir)
16
+ .filter(f => f.endsWith(".sql"))
17
+ .sort();
18
+
19
+ let executed;
20
+ try {
21
+ const result = await db.query("SELECT filename FROM _migrations");
22
+ executed = new Set((result || []).map(r => r.filename));
23
+ } catch (e) {
24
+ executed = new Set();
25
+ }
26
+
27
+ let ran = 0;
28
+ for (const file of files) {
29
+ if (executed.has(file)) {
30
+ console.log(` Skipping (already executed): ${file}`);
31
+ continue;
32
+ }
33
+ const filePath = path.join(migrationsDir, file);
34
+ const content = fs.readFileSync(filePath, "utf8");
35
+ const checksum = crypto.createHash("md5").update(content).digest("hex");
36
+
37
+ console.log(` Running migration: ${file}`);
38
+ await db.query(content);
39
+ await db.query(
40
+ "INSERT INTO _migrations (filename, checksum) VALUES (?, ?)",
41
+ [file, checksum]
42
+ );
43
+ console.log(` Completed: ${file}`);
44
+ ran++;
45
+ }
46
+
47
+ if (ran === 0) {
48
+ console.log("No pending migrations.");
49
+ } else {
50
+ console.log(`\n${ran} migration(s) complete.`);
51
+ }
52
+ }
53
+
54
+ // Run as standalone script
55
+ const isMain = process.argv[1] && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
56
+ if (isMain) {
57
+ await import("dotenv/config");
58
+ const pkg = await import("db-model-router");
59
+ const mod = pkg.default || pkg;
60
+ mod.init("sqlite3");
61
+ const migrationsDir = path.join(__dirname, "../migrations");
62
+ runMigrations(mod.db, migrationsDir)
63
+ .then(() => process.exit(0))
64
+ .catch(err => { console.error("Migration failed:", err); process.exit(1); });
65
+ }
@@ -0,0 +1,30 @@
1
+ import helmet from "helmet";
2
+ import rateLimit from "express-rate-limit";
3
+
4
+ /**
5
+ * Apply security middleware to the Express app.
6
+ * Includes: Helmet, rate limiting, custom security headers.
7
+ * @param {import("express").Application} app
8
+ */
9
+ export default function applySecurity(app) {
10
+ // Helmet — sets various HTTP headers for security
11
+ app.use(helmet());
12
+
13
+ // Rate limiting
14
+ app.use(rateLimit({
15
+ windowMs: 15 * 60 * 1000,
16
+ max: 100,
17
+ standardHeaders: true,
18
+ legacyHeaders: false,
19
+ }));
20
+
21
+ // Custom security headers (override or extend as needed)
22
+ app.use((req, res, next) => {
23
+ res.setHeader("X-Content-Type-Options", "nosniff");
24
+ res.setHeader("X-Frame-Options", "DENY");
25
+ res.setHeader("X-XSS-Protection", "1; mode=block");
26
+ res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
27
+ res.removeHeader("X-Powered-By");
28
+ next();
29
+ });
30
+ }
@@ -0,0 +1,13 @@
1
+ import session from "express-session";
2
+
3
+ /**
4
+ * Configure and return session middleware.
5
+ * Session store: memory
6
+ */
7
+ export default function configureSession() {
8
+ return session({
9
+ secret: process.env.SESSION_SECRET || "change-me",
10
+ resave: false,
11
+ saveUninitialized: false,
12
+ });
13
+ }