archetype-engine 2.2.0 → 2.3.1

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/README.md CHANGED
@@ -214,6 +214,72 @@ Archetype generates the **missing backend layer**:
214
214
 
215
215
  ---
216
216
 
217
+ ## CLI Commands
218
+
219
+ Archetype provides a suite of commands organized by namespace to avoid conflicts with your Next.js project:
220
+
221
+ ### Archetype Commands (Code Generation & Documentation)
222
+
223
+ ```bash
224
+ npx archetype init # Interactive setup with entity templates
225
+ npx archetype generate # Generate all code from entities
226
+ npx archetype view # View ERD diagram in browser (port 3333)
227
+ npx archetype docs # View OpenAPI/Swagger UI (port 3334)
228
+ npx archetype validate # Validate manifest without generating
229
+ ```
230
+
231
+ ### Project Scripts (Added by `npx archetype init`)
232
+
233
+ New projects automatically get these npm scripts:
234
+
235
+ ```json
236
+ {
237
+ "scripts": {
238
+ // Archetype - Generation & Docs
239
+ "archetype:generate": "archetype generate",
240
+ "archetype:view": "archetype view",
241
+ "archetype:docs": "archetype docs",
242
+ "archetype:check": "archetype validate",
243
+
244
+ // Database - Schema & Data
245
+ "db:push": "drizzle-kit push",
246
+ "db:studio": "drizzle-kit studio",
247
+ "db:seed": "tsx generated/seeds/run.ts",
248
+ "db:seed:reset": "tsx generated/seeds/run.ts --reset",
249
+
250
+ // Testing
251
+ "test:api": "vitest run generated/tests"
252
+ }
253
+ }
254
+ ```
255
+
256
+ ### Common Workflows
257
+
258
+ **Initial setup:**
259
+ ```bash
260
+ npm run archetype:generate # Generate code
261
+ npm run db:push # Create database schema
262
+ npm run db:seed # Add sample data
263
+ npm run dev # Start dev server
264
+ ```
265
+
266
+ **Development loop:**
267
+ ```bash
268
+ # Edit archetype/entities/product.ts
269
+ npm run archetype:generate # Regenerate code
270
+ npm run db:push # Update schema
271
+ npm run dev # Test changes
272
+ ```
273
+
274
+ **Documentation & validation:**
275
+ ```bash
276
+ npm run archetype:view # View entity relationships
277
+ npm run archetype:docs # Browse API endpoints
278
+ npm run archetype:check # Validate entity definitions
279
+ ```
280
+
281
+ ---
282
+
217
283
  ## Roadmap
218
284
 
219
285
  - [x] Core entity system with relations
package/dist/src/cli.js CHANGED
@@ -301,6 +301,89 @@ function buildHTML(mermaidCode) {
301
301
  </body>
302
302
  </html>`;
303
303
  }
304
+ function serveOpenAPIDocs(port = 3334, maxAttempts = 10) {
305
+ // Check if generated/docs/openapi.json exists
306
+ const openapiPath = path.resolve('generated/docs/openapi.json');
307
+ if (!fs.existsSync(openapiPath)) {
308
+ console.error('OpenAPI spec not found at: generated/docs/openapi.json');
309
+ console.error('Run "npx archetype generate" first to generate API documentation');
310
+ process.exit(1);
311
+ }
312
+ const openapiSpec = fs.readFileSync(openapiPath, 'utf-8');
313
+ const html = `<!DOCTYPE html>
314
+ <html lang="en">
315
+ <head>
316
+ <meta charset="UTF-8">
317
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
318
+ <title>API Documentation - Swagger UI</title>
319
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css" />
320
+ <style>
321
+ body { margin: 0; padding: 0; }
322
+ </style>
323
+ </head>
324
+ <body>
325
+ <div id="swagger-ui"></div>
326
+
327
+ <script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js"></script>
328
+ <script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-standalone-preset.js"></script>
329
+ <script>
330
+ window.onload = function() {
331
+ window.ui = SwaggerUIBundle({
332
+ spec: ${openapiSpec},
333
+ dom_id: '#swagger-ui',
334
+ deepLinking: true,
335
+ presets: [
336
+ SwaggerUIBundle.presets.apis,
337
+ SwaggerUIStandalonePreset
338
+ ],
339
+ plugins: [
340
+ SwaggerUIBundle.plugins.DownloadUrl
341
+ ],
342
+ layout: 'StandaloneLayout',
343
+ defaultModelsExpandDepth: 1,
344
+ defaultModelExpandDepth: 1,
345
+ });
346
+ };
347
+ </script>
348
+ </body>
349
+ </html>`;
350
+ const server = http.createServer((req, res) => {
351
+ // Serve OpenAPI JSON spec at /openapi.json
352
+ if (req.url === '/openapi.json') {
353
+ res.writeHead(200, { 'Content-Type': 'application/json' });
354
+ res.end(openapiSpec);
355
+ }
356
+ else {
357
+ // Serve Swagger UI HTML
358
+ res.writeHead(200, { 'Content-Type': 'text/html' });
359
+ res.end(html);
360
+ }
361
+ });
362
+ server.on('error', (err) => {
363
+ if (err.code === 'EADDRINUSE') {
364
+ const nextPort = port + 1;
365
+ if (nextPort - 3334 < maxAttempts) {
366
+ console.log(`Port ${port} is in use, trying ${nextPort}...`);
367
+ serveOpenAPIDocs(nextPort, maxAttempts);
368
+ }
369
+ else {
370
+ console.error(`Could not find an available port (tried ${3334}-${port})`);
371
+ console.error('Try killing the process using the port:');
372
+ console.error(` lsof -i :3334 | grep LISTEN | awk '{print $2}' | xargs kill -9`);
373
+ process.exit(1);
374
+ }
375
+ }
376
+ else {
377
+ console.error('Server error:', err.message);
378
+ process.exit(1);
379
+ }
380
+ });
381
+ server.listen(port, () => {
382
+ console.log(`http://localhost:${port}`);
383
+ console.log('Ctrl+C to stop');
384
+ openBrowser(`http://localhost:${port}`);
385
+ });
386
+ }
304
387
  async function runGenerate(manifest) {
305
388
  // Determine template: CLI flag > config > error
306
389
  const templateId = templateOverride || manifest.template;
@@ -457,7 +540,8 @@ function loadJSONManifest(configFile) {
457
540
  ])),
458
541
  behaviors: e.behaviors,
459
542
  auth: e.auth,
460
- protected: e.protected,
543
+ // Only include protected if it was explicitly set (for validation)
544
+ protected: e._hasProtected ? e.protected : undefined,
461
545
  })),
462
546
  database: manifest.database,
463
547
  mode: manifest.mode.type,
@@ -503,6 +587,10 @@ async function main() {
503
587
  const erd = (0, erd_ir_1.generateERDFromIR)(manifest);
504
588
  serveERD(erd);
505
589
  }
590
+ else if (command === 'docs') {
591
+ // Serve OpenAPI documentation
592
+ serveOpenAPIDocs();
593
+ }
506
594
  else if (command === 'init') {
507
595
  // Run the TUI init flow
508
596
  await (0, init_1.init)({ yes: yesFlag, headless: headlessFlag });
@@ -520,6 +608,7 @@ async function main() {
520
608
  console.log(' archetype generate [config] - Generate code from entities');
521
609
  console.log(' archetype validate [config] - Validate manifest without generating');
522
610
  console.log(' archetype view [config] - View ERD diagram in browser');
611
+ console.log(' archetype docs - View OpenAPI/Swagger documentation in browser');
523
612
  console.log(' archetype mcp - Start MCP server (for Claude Desktop/Code)');
524
613
  console.log('');
525
614
  console.log('Flags:');
@@ -131,6 +131,8 @@ export interface EntityIR {
131
131
  auth: boolean;
132
132
  /** Normalized protection config for CRUD operations */
133
133
  protected: ProtectedIR;
134
+ /** Whether protected was explicitly set (for validation) */
135
+ _hasProtected: boolean;
134
136
  /** External API source (optional - inherits from manifest if not specified) */
135
137
  source?: ExternalSourceConfig;
136
138
  /** Normalized hooks config */
@@ -1 +1 @@
1
- {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/entity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/entity.ts"],"names":[],"mappings":"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"}
@@ -74,6 +74,7 @@ function compileEntity(name, definition) {
74
74
  },
75
75
  auth: definition.auth || false,
76
76
  protected: normalizeProtected(definition.protected),
77
+ _hasProtected: definition.protected !== undefined && definition.protected !== false,
77
78
  source: definition.source,
78
79
  hooks: normalizeHooks(definition.hooks),
79
80
  };
@@ -1 +1 @@
1
- {"version":3,"file":"dependencies.d.ts","sourceRoot":"","sources":["../../../src/init/dependencies.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAA;AAC1D,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAA;AAC1C,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;AAE1E,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,QAAQ,CAAA;IACd,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,IAAI,EAAE,OAAO,CAAA;IACb,aAAa,CAAC,EAAE,YAAY,EAAE,CAAA;IAC9B,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACrB,eAAe,EAAE,OAAO,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAGD,eAAO,MAAM,gBAAgB,UAS5B,CAAA;AAGD,eAAO,MAAM,mBAAmB,UAE/B,CAAA;AAGD,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAa5F,CAAA;AAGD,eAAO,MAAM,gBAAgB;;;CAG5B,CAAA;AAGD,eAAO,MAAM,oBAAoB,UAQhC,CAAA;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAiCzF;AAGD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,QAAiB,GAAG,UAAU,CAoB5F"}
1
+ {"version":3,"file":"dependencies.d.ts","sourceRoot":"","sources":["../../../src/init/dependencies.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAA;AAC1D,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAA;AAC1C,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;AAE1E,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,QAAQ,CAAA;IACd,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,IAAI,EAAE,OAAO,CAAA;IACb,aAAa,CAAC,EAAE,YAAY,EAAE,CAAA;IAC9B,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACrB,eAAe,EAAE,OAAO,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAGD,eAAO,MAAM,gBAAgB,UAS5B,CAAA;AAGD,eAAO,MAAM,mBAAmB,UAI/B,CAAA;AAGD,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAa5F,CAAA;AAGD,eAAO,MAAM,gBAAgB;;;CAG5B,CAAA;AAGD,eAAO,MAAM,oBAAoB,UAQhC,CAAA;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAiCzF;AAGD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,QAAiB,GAAG,UAAU,CAoB5F"}
@@ -18,6 +18,8 @@ exports.coreDependencies = [
18
18
  // Dev dependencies (always installed)
19
19
  exports.coreDevDependencies = [
20
20
  'drizzle-kit',
21
+ 'tsx', // For running seed scripts
22
+ 'vitest', // For running tests
21
23
  ];
22
24
  // Database-specific dependencies
23
25
  exports.databaseDependencies = {
@@ -7,6 +7,7 @@ export declare function getTrpcServerTemplate(config: InitConfig): string;
7
7
  export declare function getTrpcClientTemplate(): string;
8
8
  export declare function getProvidersTemplate(): string;
9
9
  export declare function getApiRouteTemplate(config: InitConfig): string;
10
+ export declare function getVitestConfigTemplate(structure: ProjectStructure): string;
10
11
  export declare function getDrizzleConfigTemplate(database: DatabaseType): string;
11
12
  export declare function getAuthTemplate(config: InitConfig): string;
12
13
  export declare function getAuthRouteTemplate(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/init/templates.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAI9D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA2G5D;AAGD,wBAAgB,qBAAqB,IAAI,MAAM,CAe9C;AAGD,wBAAgB,wBAAwB,IAAI,MAAM,CAejD;AAGD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CA2B5D;AAGD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAyGhE;AAGD,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAGD,wBAAgB,oBAAoB,IAAI,MAAM,CAyB7C;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAgC9D;AAGD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAsBvE;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA6E1D;AAGD,wBAAgB,oBAAoB,IAAI,MAAM,CAK7C;AAGD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAkChE;AAGD,wBAAgB,oBAAoB,IAAI,MAAM,CA6B7C;AAGD,wBAAgB,mBAAmB,IAAI,MAAM,CAgN5C;AAGD,wBAAgB,sBAAsB,IAAI,MAAM,CAyM/C;AAGD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,GAAG,YAAY,EAAE,CAsFnG;AAGD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAahF"}
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/init/templates.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAI9D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA2G5D;AAGD,wBAAgB,qBAAqB,IAAI,MAAM,CAe9C;AAGD,wBAAgB,wBAAwB,IAAI,MAAM,CAejD;AAGD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CA2B5D;AAGD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAyGhE;AAGD,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAGD,wBAAgB,oBAAoB,IAAI,MAAM,CAyB7C;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAgC9D;AAGD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM,CAiB3E;AAGD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAsBvE;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA6E1D;AAGD,wBAAgB,oBAAoB,IAAI,MAAM,CAK7C;AAGD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAkChE;AAGD,wBAAgB,oBAAoB,IAAI,MAAM,CA6B7C;AAGD,wBAAgB,mBAAmB,IAAI,MAAM,CAyN5C;AAGD,wBAAgB,sBAAsB,IAAI,MAAM,CAyM/C;AAGD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,GAAG,YAAY,EAAE,CAwFnG;AAGD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBhF"}
@@ -9,6 +9,7 @@ exports.getTrpcServerTemplate = getTrpcServerTemplate;
9
9
  exports.getTrpcClientTemplate = getTrpcClientTemplate;
10
10
  exports.getProvidersTemplate = getProvidersTemplate;
11
11
  exports.getApiRouteTemplate = getApiRouteTemplate;
12
+ exports.getVitestConfigTemplate = getVitestConfigTemplate;
12
13
  exports.getDrizzleConfigTemplate = getDrizzleConfigTemplate;
13
14
  exports.getAuthTemplate = getAuthTemplate;
14
15
  exports.getAuthRouteTemplate = getAuthRouteTemplate;
@@ -350,6 +351,25 @@ const handler = (req: Request) =>
350
351
  export { handler as GET, handler as POST }
351
352
  `;
352
353
  }
354
+ // vitest.config.ts
355
+ function getVitestConfigTemplate(structure) {
356
+ const prefix = structure.useSrcDir ? './src/' : './';
357
+ return `import { defineConfig } from 'vitest/config'
358
+ import path from 'path'
359
+
360
+ export default defineConfig({
361
+ test: {
362
+ globals: true,
363
+ environment: 'node',
364
+ },
365
+ resolve: {
366
+ alias: {
367
+ '@': path.resolve(__dirname, '${prefix}'),
368
+ },
369
+ },
370
+ })
371
+ `;
372
+ }
353
373
  // drizzle.config.ts
354
374
  function getDrizzleConfigTemplate(database) {
355
375
  const dialectMap = {
@@ -614,11 +634,20 @@ npm run db:push
614
634
  ## Available Commands
615
635
 
616
636
  \`\`\`bash
637
+ # Archetype commands
617
638
  npm run archetype:generate # Generate code from entities
618
- npm run archetype:view # View ERD in browser
639
+ npm run archetype:view # View ERD diagram in browser
640
+ npm run archetype:docs # View API docs (Swagger UI)
641
+ npm run archetype:check # Validate manifest without generating
642
+
643
+ # Database commands
619
644
  npm run db:push # Push schema to database
620
- npm run db:studio # Open Drizzle Studio
621
- npx archetype validate manifest.json --json # Validate manifest
645
+ npm run db:studio # Open Drizzle Studio (database GUI)
646
+ npm run db:seed # Seed database with sample data
647
+ npm run db:seed:reset # Reset database and seed
648
+
649
+ # Testing
650
+ npm run test:api # Run generated API tests
622
651
  \`\`\`
623
652
 
624
653
  ## Examples
@@ -851,20 +880,20 @@ When discussing code, use \`file:line\` format: \`archetype/entities/user.ts:12\
851
880
 
852
881
  ## Commands Reference
853
882
  \`\`\`bash
854
- # Generate code from entities
855
- npm run archetype:generate
856
-
857
- # View ERD diagram
858
- npm run archetype:view
859
-
860
- # Push schema to database (full mode)
861
- npm run db:push
883
+ # Archetype - Generate & View
884
+ npm run archetype:generate # Generate code from entities
885
+ npm run archetype:view # View ERD diagram in browser
886
+ npm run archetype:docs # View API docs (Swagger UI)
887
+ npm run archetype:check # Validate manifest without generating
862
888
 
863
- # Open Drizzle Studio (full mode)
864
- npm run db:studio
889
+ # Database (full mode only)
890
+ npm run db:push # Push schema to database
891
+ npm run db:studio # Open Drizzle Studio (database GUI)
892
+ npm run db:seed # Seed database with sample data
893
+ npm run db:seed:reset # Reset database and seed
865
894
 
866
- # Validate manifest
867
- npx archetype validate manifest.json --json
895
+ # Testing
896
+ npm run test:api # Run generated API tests
868
897
  \`\`\`
869
898
 
870
899
  ## Examples
@@ -932,6 +961,8 @@ function getAllTemplateFiles(config, structure) {
932
961
  const files = [
933
962
  // Config - always needed
934
963
  { path: 'archetype.config.ts', content: getConfigTemplate(config) },
964
+ // Vitest config for path aliases
965
+ { path: 'vitest.config.ts', content: getVitestConfigTemplate(structure) },
935
966
  // Gitignore - always needed
936
967
  { path: '.gitignore', content: getGitignoreTemplate() },
937
968
  // AI assistant guidance files
@@ -993,13 +1024,19 @@ function getAllTemplateFiles(config, structure) {
993
1024
  // package.json scripts to add
994
1025
  function getPackageJsonScripts(config) {
995
1026
  const scripts = {
1027
+ // Archetype - core commands
996
1028
  'archetype:generate': 'archetype generate',
997
1029
  'archetype:view': 'archetype view',
1030
+ 'archetype:docs': 'archetype docs',
1031
+ 'archetype:check': 'archetype validate',
998
1032
  };
999
1033
  // Only add database scripts for full mode
1000
1034
  if (config.mode === 'full') {
1001
1035
  scripts['db:push'] = 'drizzle-kit push';
1002
1036
  scripts['db:studio'] = 'drizzle-kit studio';
1037
+ scripts['db:seed'] = 'tsx generated/seeds/run.ts';
1038
+ scripts['db:seed:reset'] = 'tsx generated/seeds/run.ts --reset';
1039
+ scripts['test:api'] = 'vitest run generated/tests';
1003
1040
  }
1004
1041
  return scripts;
1005
1042
  }
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/json/parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAc,MAAM,WAAW,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAmB,OAAO,EAAE,MAAM,WAAW,CAAA;AAC3E,OAAO,EAAE,UAAU,EAA6B,MAAM,aAAa,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EACL,SAAS,EACT,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,SAAS,EACV,MAAM,SAAS,CAAA;AAEhB;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,CA8C5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,cAAc,CAMxE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,kBAAkB,GAAG,oBAAoB,CAuBxF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,WAAW,CAWtE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAuBpE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,CAsC5D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,UAAU,CAkEzE;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAIpF"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/json/parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAc,MAAM,WAAW,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAmB,OAAO,EAAE,MAAM,WAAW,CAAA;AAC3E,OAAO,EAAE,UAAU,EAA6B,MAAM,aAAa,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EACL,SAAS,EACT,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,SAAS,EACV,MAAM,SAAS,CAAA;AAEhB;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,CA8C5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,cAAc,CAMxE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,kBAAkB,GAAG,oBAAoB,CAuBxF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,WAAW,CAWtE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAuBpE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,CAuC5D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,UAAU,CAkEzE;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAIpF"}
@@ -215,6 +215,7 @@ function parseEntityJSON(entity) {
215
215
  behaviors,
216
216
  auth: entity.auth || false,
217
217
  protected: parseProtectedJSON(entity.protected),
218
+ _hasProtected: entity.protected !== undefined && entity.protected !== false,
218
219
  source,
219
220
  hooks: parseHooksJSON(entity.hooks),
220
221
  };
@@ -1 +1 @@
1
- {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../../../src/templates/nextjs-drizzle-trpc/generators/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,yBAAyB,CAAA;AAuWvE;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,SAwC3B,CAAA"}
1
+ {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../../../src/templates/nextjs-drizzle-trpc/generators/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,yBAAyB,CAAA;AA0WvE;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,SAwC3B,CAAA"}
@@ -116,9 +116,9 @@ function generateMockValue(fieldName, field, index) {
116
116
  return `faker ? faker.datatype.boolean() : i % 2 === 0`;
117
117
  case 'date':
118
118
  if (lowerName.includes('birth')) {
119
- return `faker ? faker.date.birthdate() : new Date(1990 + (i % 30), i % 12, 1 + (i % 28))`;
119
+ return `faker ? faker.date.birthdate().toISOString() : new Date(1990 + (i % 30), i % 12, 1 + (i % 28)).toISOString()`;
120
120
  }
121
- return `faker ? faker.date.recent() : new Date()`;
121
+ return `faker ? faker.date.recent().toISOString() : new Date().toISOString()`;
122
122
  case 'enum':
123
123
  const enumValues = field.enumValues || [];
124
124
  return `faker ? faker.helpers.arrayElement(${JSON.stringify(enumValues)}) : ${JSON.stringify(enumValues)}[i % ${enumValues.length}]`;
@@ -161,6 +161,8 @@ function generateEntitySeedFunction(entity) {
161
161
  lines.push(` }`);
162
162
  lines.push(``);
163
163
  lines.push(` const data = Array.from({ length: count }, (_, i) => ({`);
164
+ // Add ID field
165
+ lines.push(` id: faker ? faker.string.uuid() : \`${entityName.toLowerCase()}-\${Date.now()}-\${i}\`,`);
164
166
  // Generate field values
165
167
  for (const [fieldName, field] of seedableFields) {
166
168
  const value = generateMockValue(fieldName, field, 0);
@@ -173,8 +175,8 @@ function generateEntitySeedFunction(entity) {
173
175
  }
174
176
  // Add timestamps if enabled
175
177
  if (entity.behaviors.timestamps) {
176
- lines.push(` createdAt: faker ? faker.date.recent({ days: 30 }) : new Date(),`);
177
- lines.push(` updatedAt: faker ? faker.date.recent({ days: 7 }) : new Date(),`);
178
+ lines.push(` createdAt: faker ? faker.date.recent({ days: 30 }).toISOString() : new Date().toISOString(),`);
179
+ lines.push(` updatedAt: faker ? faker.date.recent({ days: 7 }).toISOString() : new Date().toISOString(),`);
178
180
  }
179
181
  lines.push(` }))`);
180
182
  lines.push(``);
@@ -1 +1 @@
1
- {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../../../src/templates/nextjs-drizzle-trpc/generators/test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,yBAAyB,CAAA;AAmgBvE;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,SAuB3B,CAAA"}
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../../../src/templates/nextjs-drizzle-trpc/generators/test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,yBAAyB,CAAA;AAogBvE;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,SAuB3B,CAAA"}
@@ -110,7 +110,10 @@ function generateInvalidValue(fieldName, field) {
110
110
  }
111
111
  const minLength = field.validations.find(v => v.type === 'minLength');
112
112
  if (minLength) {
113
- return { value: `'x'`, reason: `below minimum length of ${minLength.value}` };
113
+ const minVal = minLength.value;
114
+ // Generate a string shorter than the minimum (empty string if min is 1)
115
+ const invalidStr = minVal > 1 ? 'x'.repeat(minVal - 1) : '';
116
+ return { value: `'${invalidStr}'`, reason: `below minimum length of ${minLength.value}` };
114
117
  }
115
118
  const maxLength = field.validations.find(v => v.type === 'maxLength');
116
119
  if (maxLength) {
@@ -165,16 +168,14 @@ function generateEntityTest(entity, manifest) {
165
168
  const lines = [];
166
169
  // Imports
167
170
  lines.push(`import { describe, it, expect, beforeEach } from 'vitest'`);
168
- lines.push(`import { appRouter } from '@/generated/trpc/routers'`);
169
- lines.push(`import { createCallerFactory } from '@trpc/server'`);
170
- lines.push(``);
171
- lines.push(`// Create tRPC caller for testing`);
172
- lines.push(`const createCaller = createCallerFactory(appRouter)`);
171
+ lines.push(`import { appRouter } from '../trpc/routers'`);
172
+ lines.push(`import { db } from '@/server/db'`);
173
173
  lines.push(``);
174
174
  // Mock contexts
175
175
  if (hasAuth) {
176
176
  lines.push(`// Mock authenticated context`);
177
177
  lines.push(`const mockAuthContext = {`);
178
+ lines.push(` db,`);
178
179
  lines.push(` session: {`);
179
180
  lines.push(` user: { id: 'test-user-123', email: 'test@example.com', name: 'Test User' }`);
180
181
  lines.push(` }`);
@@ -183,14 +184,15 @@ function generateEntityTest(entity, manifest) {
183
184
  }
184
185
  lines.push(`// Mock unauthenticated context`);
185
186
  lines.push(`const mockPublicContext = {`);
187
+ lines.push(` db,`);
186
188
  lines.push(` session: null`);
187
189
  lines.push(`}`);
188
190
  lines.push(``);
189
191
  // Test suite
190
192
  lines.push(`describe('${entityName} Router', () => {`);
191
- lines.push(` const publicCaller = createCaller(mockPublicContext)`);
193
+ lines.push(` const publicCaller = appRouter.createCaller(mockPublicContext)`);
192
194
  if (hasAuth) {
193
- lines.push(` const authCaller = createCaller(mockAuthContext)`);
195
+ lines.push(` const authCaller = appRouter.createCaller(mockAuthContext)`);
194
196
  }
195
197
  lines.push(``);
196
198
  // Valid test data
@@ -340,10 +342,9 @@ function generateEntityTest(entity, manifest) {
340
342
  lines.push(` })`);
341
343
  lines.push(``);
342
344
  const getCaller = entity.protected.get ? 'authCaller' : 'publicCaller';
343
- lines.push(` it('should throw error for non-existent ID', async () => {`);
344
- lines.push(` await expect(`);
345
- lines.push(` ${getCaller}.${routerName}.get({ id: 'non-existent-id' })`);
346
- lines.push(` ).rejects.toThrow()`);
345
+ lines.push(` it('should return null for non-existent ID', async () => {`);
346
+ lines.push(` const result = await ${getCaller}.${routerName}.get({ id: 'non-existent-id' })`);
347
+ lines.push(` expect(result).toBeNull()`);
347
348
  lines.push(` })`);
348
349
  lines.push(``);
349
350
  lines.push(` })`);
@@ -378,7 +379,7 @@ function generateEntityTest(entity, manifest) {
378
379
  lines.push(``);
379
380
  lines.push(` expect(result.${firstField}).toBe(updateData.${firstField})`);
380
381
  if (hasTimestamps) {
381
- lines.push(` expect(new Date(result.updatedAt).getTime()).toBeGreaterThan(new Date(created.updatedAt).getTime())`);
382
+ lines.push(` expect(new Date(result.updatedAt).getTime()).toBeGreaterThanOrEqual(new Date(created.updatedAt).getTime())`);
382
383
  }
383
384
  }
384
385
  lines.push(` })`);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/validation/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAuC,MAAM,eAAe,CAAA;AAEjF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAA;IACZ,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,KAAK,EAAE,OAAO,CAAA;IACd,mCAAmC;IACnC,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,yCAAyC;IACzC,QAAQ,EAAE,eAAe,EAAE,CAAA;CAC5B;AAGD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;CAgClB,CAAA;AA6PV;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,gBAAgB,CA2DzE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/validation/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAuC,MAAM,eAAe,CAAA;AAEjF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAA;IACZ,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,KAAK,EAAE,OAAO,CAAA;IACd,mCAAmC;IACnC,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,yCAAyC;IACzC,QAAQ,EAAE,eAAe,EAAE,CAAA;CAC5B;AAGD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;CAgClB,CAAA;AA8PV;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,gBAAgB,CA2DzE"}
@@ -139,17 +139,9 @@ function validateEntity(entity, entityNames, authEnabled) {
139
139
  errors.push(...validateRelation(relationName, relation, entity.name, entityNames));
140
140
  }
141
141
  }
142
- // Check protected requires auth
142
+ // Check protected requires auth (only if explicitly set)
143
143
  if (entity.protected !== undefined && entity.protected !== false) {
144
- if (!authEnabled) {
145
- errors.push({
146
- code: exports.ValidationCodes.AUTH_REQUIRED_FOR_PROTECTED,
147
- path: `${path}.protected`,
148
- message: `Entity '${entity.name}' has protected operations but auth is not enabled`,
149
- suggestion: `Add auth: { enabled: true } to manifest, or remove protected from entity`,
150
- });
151
- }
152
- // Validate protected value
144
+ // Validate protected value format
153
145
  const validProtectedStrings = ['write', 'all'];
154
146
  if (typeof entity.protected !== 'boolean' &&
155
147
  typeof entity.protected !== 'object' &&
@@ -161,6 +153,15 @@ function validateEntity(entity, entityNames, authEnabled) {
161
153
  suggestion: `Use one of: true, false, 'write', 'all', or an object with list/get/create/update/remove`,
162
154
  });
163
155
  }
156
+ // Only require auth if protected is explicitly enabled
157
+ if (!authEnabled) {
158
+ errors.push({
159
+ code: exports.ValidationCodes.AUTH_REQUIRED_FOR_PROTECTED,
160
+ path: `${path}.protected`,
161
+ message: `Entity '${entity.name}' has protected operations but auth is not enabled`,
162
+ suggestion: `Add auth: { enabled: true } to manifest, or remove protected from entity`,
163
+ });
164
+ }
164
165
  }
165
166
  // Validate external source
166
167
  if (entity.source && !entity.source.baseUrl) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archetype-engine",
3
- "version": "2.2.0",
3
+ "version": "2.3.1",
4
4
  "description": "Type-safe backend generator for Next.js. Define entities once, get Drizzle schemas, tRPC APIs, Zod validation, and React hooks instantly.",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",