create-taserjs 0.0.1 → 0.0.2

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 (70) hide show
  1. package/dist/cjs/addons/arktype/index.cjs +6 -0
  2. package/dist/cjs/addons/arktype/index.cjs.map +1 -0
  3. package/dist/cjs/addons/drizzle/index.cjs +9 -2
  4. package/dist/cjs/addons/drizzle/index.cjs.map +1 -1
  5. package/dist/cjs/addons/kysely/index.cjs +9 -2
  6. package/dist/cjs/addons/kysely/index.cjs.map +1 -1
  7. package/dist/cjs/addons/registry.cjs +16 -12
  8. package/dist/cjs/addons/registry.cjs.map +1 -1
  9. package/dist/cjs/addons/valibot/index.cjs +6 -0
  10. package/dist/cjs/addons/valibot/index.cjs.map +1 -0
  11. package/dist/cjs/addons/validators.cjs +2 -2
  12. package/dist/cjs/addons/validators.cjs.map +1 -1
  13. package/dist/cjs/cli.cjs +2 -0
  14. package/dist/cjs/cli.cjs.map +1 -1
  15. package/dist/cjs/commands/create.cjs +1 -0
  16. package/dist/cjs/commands/create.cjs.map +1 -1
  17. package/dist/cjs/commands/create.d.cts +1 -0
  18. package/dist/cjs/core/package-manager.cjs +5 -13
  19. package/dist/cjs/core/package-manager.cjs.map +1 -1
  20. package/dist/cjs/core/package-manager.d.cts +2 -1
  21. package/dist/cjs/core/parse-options.cjs +10 -0
  22. package/dist/cjs/core/parse-options.cjs.map +1 -1
  23. package/dist/cjs/core/parse-options.d.cts +1 -0
  24. package/dist/cjs/core/project-config.cjs +1 -0
  25. package/dist/cjs/core/project-config.cjs.map +1 -1
  26. package/dist/cjs/core/scaffold-engine.cjs +4 -2
  27. package/dist/cjs/core/scaffold-engine.cjs.map +1 -1
  28. package/dist/cjs/templates/base.cjs +9 -6
  29. package/dist/cjs/templates/base.cjs.map +1 -1
  30. package/dist/esm/addons/arktype/index.js +7 -0
  31. package/dist/esm/addons/arktype/index.js.map +1 -0
  32. package/dist/esm/addons/drizzle/index.js +9 -2
  33. package/dist/esm/addons/drizzle/index.js.map +1 -1
  34. package/dist/esm/addons/kysely/index.js +9 -2
  35. package/dist/esm/addons/kysely/index.js.map +1 -1
  36. package/dist/esm/addons/registry.js +6 -2
  37. package/dist/esm/addons/registry.js.map +1 -1
  38. package/dist/esm/addons/valibot/index.js +7 -0
  39. package/dist/esm/addons/valibot/index.js.map +1 -0
  40. package/dist/esm/addons/validators.js +2 -2
  41. package/dist/esm/addons/validators.js.map +1 -1
  42. package/dist/esm/cli.js +2 -0
  43. package/dist/esm/cli.js.map +1 -1
  44. package/dist/esm/commands/create.d.ts +1 -0
  45. package/dist/esm/commands/create.js +2 -1
  46. package/dist/esm/commands/create.js.map +1 -1
  47. package/dist/esm/core/package-manager.d.ts +2 -1
  48. package/dist/esm/core/package-manager.js +5 -14
  49. package/dist/esm/core/package-manager.js.map +1 -1
  50. package/dist/esm/core/parse-options.d.ts +1 -0
  51. package/dist/esm/core/parse-options.js +11 -2
  52. package/dist/esm/core/parse-options.js.map +1 -1
  53. package/dist/esm/core/project-config.js +1 -0
  54. package/dist/esm/core/project-config.js.map +1 -1
  55. package/dist/esm/core/scaffold-engine.js +4 -2
  56. package/dist/esm/core/scaffold-engine.js.map +1 -1
  57. package/dist/esm/templates/base.js +9 -6
  58. package/dist/esm/templates/base.js.map +1 -1
  59. package/package.json +1 -1
  60. package/src/addons/drizzle/index.ts +15 -2
  61. package/src/addons/kysely/index.ts +15 -2
  62. package/src/addons/registry.ts +5 -1
  63. package/src/addons/validators.ts +2 -2
  64. package/src/cli.ts +2 -0
  65. package/src/commands/create.ts +6 -0
  66. package/src/core/package-manager.ts +7 -10
  67. package/src/core/parse-options.ts +17 -0
  68. package/src/core/project-config.ts +4 -0
  69. package/src/core/scaffold-engine.ts +2 -0
  70. package/src/templates/base.ts +9 -6
@@ -84,10 +84,11 @@ export const Middleware = t.middleware('/$')
84
84
  `;
85
85
  }
86
86
  function indexRouteTemplate() {
87
- return `import { t } from '#src/taser.js'
87
+ return `import { reply } from '@taserjs/router'
88
+ import { t } from '#src/taser.js'
88
89
 
89
- export const Route = t.get('/').handler((ctx) => {
90
- return ctx.reply.json({ message: 'Welcome to Taser' })
90
+ export const Route = t.get('/').handler(() => {
91
+ return reply.json({ message: 'Welcome to Taser' })
91
92
  })
92
93
  `;
93
94
  }
@@ -95,10 +96,12 @@ function healthRouteTemplate(ctx) {
95
96
  const lines = [];
96
97
  if (ctx.logger) lines.push(" ctx.logger.info('health check')");
97
98
  if (ctx.db) lines.push(" // ctx.db is available from context boot");
98
- return `import { t } from '#src/taser.js'
99
+ const body = lines.length > 0 ? `${lines.join("\n")}\n` : "";
100
+ return `import { reply } from '@taserjs/router'
101
+ import { t } from '#src/taser.js'
99
102
 
100
- export const Route = t.get('/health').handler((ctx) => {
101
- ${lines.length > 0 ? `${lines.join("\n")}\n` : ""} return ctx.reply.json({ ok: true })
103
+ export const Route = t.get('/health').handler(${lines.length > 0 ? "(ctx)" : "()"} => {
104
+ ${body} return reply.json({ ok: true })
102
105
  })
103
106
  `;
104
107
  }
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":[],"sources":["../../../src/templates/base.ts"],"sourcesContent":["import type { BootBinding } from '../addons/types.js'\nimport type { ScaffoldContext } from '../core/types.js'\n\nexport function packageJsonTemplate(projectName: string, scripts: Record<string, string> = {}): string {\n const pkg = {\n name: projectName,\n version: '1.0.0',\n private: true,\n type: 'module',\n imports: {\n '#src/*': './src/*',\n },\n scripts: {\n 'dev': 'run-p dev:server dev:taser',\n 'dev:server': 'tsx watch src/index.ts',\n 'dev:taser': 'taser watch',\n 'start': 'tsx src/index.ts',\n 'generate': 'taser generate',\n 'build': 'taser generate && tsdown',\n 'serve': 'node dist/index.mjs',\n 'typecheck': 'tsc --noEmit -p tsconfig.json',\n ...scripts,\n },\n }\n\n return `${JSON.stringify(pkg, null, 2)}\\n`\n}\n\nexport function tsdownConfigTemplate(): string {\n return `import { defineConfig } from 'tsdown'\n\nexport default defineConfig({\n entry: ['./src/index.ts'],\n platform: 'node',\n outDir: 'dist',\n clean: true,\n sourcemap: true,\n})\n`\n}\n\nexport function tsconfigTemplate(): string {\n return `${JSON.stringify({\n compilerOptions: {\n target: 'ES2022',\n module: 'NodeNext',\n paths: {\n '#src/*': ['./src/*'],\n },\n strict: true,\n skipLibCheck: true,\n verbatimModuleSyntax: true,\n isolatedModules: true,\n noEmit: true,\n types: ['node'],\n },\n include: ['src'],\n }, null, 2)}\\n`\n}\n\nexport function gitignoreTemplate(): string {\n return `node_modules\ndist\n.DS_Store\n*.log\n.env\nlocal.db\ndrizzle\n`\n}\n\nexport function contextTemplate(bindings: BootBinding[]): string {\n const imports = bindings.map(\n binding => `import { ${binding.factoryName} } from '${binding.importPath}'`,\n )\n\n const bootBody = bindings.length > 0\n ? bindings.map(binding => ` ${binding.key}: ${binding.factoryName}(),`).join('\\n')\n : ''\n\n const bootBlock = bindings.length > 0\n ? ` boot: () => ({\\n${bootBody}\\n }),`\n : ''\n\n const importBlock = imports.length > 0 ? `${imports.join('\\n')}\\n\\n` : ''\n\n return `${importBlock}import { createContext } from '@taserjs/router'\n\nexport const context = createContext({\n${bootBlock}\n request: () => ({\n requestId: crypto.randomUUID(),\n }),\n})\n`\n}\n\nexport function rootLayoutTemplate(): string {\n return `import { bodyLimit } from '@taserjs/router/body-limit'\nimport { secureHeaders } from '@taserjs/router/secure-headers'\n\nimport { t } from '#src/taser.js'\n\nexport const Middleware = t.middleware('/$')\n .use(secureHeaders())\n .use(bodyLimit({ maxSize: 1_000_000 }))\n`\n}\n\nexport function indexRouteTemplate(): string {\n return `import { t } from '#src/taser.js'\n\nexport const Route = t.get('/').handler((ctx) => {\n return ctx.reply.json({ message: 'Welcome to Taser' })\n})\n`\n}\n\nexport function healthRouteTemplate(ctx: ScaffoldContext): string {\n const lines: string[] = []\n\n if (ctx.logger) {\n lines.push(' ctx.logger.info(\\'health check\\')')\n }\n\n if (ctx.db) {\n lines.push(' // ctx.db is available from context boot')\n }\n\n const body = lines.length > 0 ? `${lines.join('\\n')}\\n` : ''\n\n return `import { t } from '#src/taser.js'\n\nexport const Route = t.get('/health').handler((ctx) => {\n${body} return ctx.reply.json({ ok: true })\n})\n`\n}\n\n/** Minimal placeholder until `taser generate` runs. */\nexport function starterManifestTemplate(): string {\n return `/* eslint-disable */\n// Run \\`pnpm generate\\` (taser generate) to replace this file.\nimport { Middleware as RootSplatLayoutImport } from './routes/$.js'\nimport { Route as RootIndexGetRouteImport } from './routes/index.get.js'\nimport { Route as HealthGetRouteImport } from './routes/health.get.js'\n\nexport const routeManifest = {\n layouts: {\n '/$': {\n middlewares: RootSplatLayoutImport,\n },\n },\n routes: {\n '/': {\n GET: {\n layoutChain: ['/$'],\n route: RootIndexGetRouteImport,\n },\n },\n '/health': {\n GET: {\n layoutChain: ['/$'],\n route: HealthGetRouteImport,\n },\n },\n },\n} as const\n\nexport type RoutePathGen = '/' | '/health'\nexport type LayoutIdGen = '/$'\nexport type LayoutTreeGen = {\n '/$': {\n parent: null\n middlewares: typeof RootSplatLayoutImport\n }\n}\nexport type RouteByPathMethodGen = {\n '/': {\n GET: {\n parent: '/$'\n layoutChain: ['/$']\n route: typeof RootIndexGetRouteImport\n }\n }\n '/health': {\n GET: {\n parent: '/$'\n layoutChain: ['/$']\n route: typeof HealthGetRouteImport\n }\n }\n}\nexport type RouteManifest = typeof routeManifest\n\ndeclare module '@taserjs/router' {\n interface RouterRegister {\n RoutePath: RoutePathGen\n LayoutId: LayoutIdGen\n LayoutTree: LayoutTreeGen\n RouteByPathMethod: RouteByPathMethodGen\n }\n}\n`\n}\n"],"mappings":";AAGA,SAAgB,oBAAoB,aAAqB,UAAkC,CAAC,GAAW;CACrG,MAAM,MAAM;EACV,MAAM;EACN,SAAS;EACT,SAAS;EACT,MAAM;EACN,SAAS,EACP,UAAU,UACZ;EACA,SAAS;GACP,OAAO;GACP,cAAc;GACd,aAAa;GACb,SAAS;GACT,YAAY;GACZ,SAAS;GACT,SAAS;GACT,aAAa;GACb,GAAG;EACL;CACF;CAEA,OAAO,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE;AACzC;AAEA,SAAgB,uBAA+B;CAC7C,OAAO;;;;;;;;;;AAUT;AAEA,SAAgB,mBAA2B;CACzC,OAAO,GAAG,KAAK,UAAU;EACvB,iBAAiB;GACf,QAAQ;GACR,QAAQ;GACR,OAAO,EACL,UAAU,CAAC,SAAS,EACtB;GACA,QAAQ;GACR,cAAc;GACd,sBAAsB;GACtB,iBAAiB;GACjB,QAAQ;GACR,OAAO,CAAC,MAAM;EAChB;EACA,SAAS,CAAC,KAAK;CACjB,GAAG,MAAM,CAAC,EAAE;AACd;AAEA,SAAgB,oBAA4B;CAC1C,OAAO;;;;;;;;AAQT;AAEA,SAAgB,gBAAgB,UAAiC;CAC/D,MAAM,UAAU,SAAS,KACvB,YAAW,YAAY,QAAQ,YAAY,WAAW,QAAQ,WAAW,EAC3E;CAEA,MAAM,WAAW,SAAS,SAAS,IAC/B,SAAS,KAAI,YAAW,OAAO,QAAQ,IAAI,IAAI,QAAQ,YAAY,IAAI,CAAC,CAAC,KAAK,IAAI,IAClF;CAEJ,MAAM,YAAY,SAAS,SAAS,IAChC,qBAAqB,SAAS,WAC9B;CAIJ,OAAO,GAFa,QAAQ,SAAS,IAAI,GAAG,QAAQ,KAAK,IAAI,EAAE,QAAQ,GAEjD;;;EAGtB,UAAU;;;;;;AAMZ;AAEA,SAAgB,qBAA6B;CAC3C,OAAO;;;;;;;;;AAST;AAEA,SAAgB,qBAA6B;CAC3C,OAAO;;;;;;AAMT;AAEA,SAAgB,oBAAoB,KAA8B;CAChE,MAAM,QAAkB,CAAC;CAEzB,IAAI,IAAI,QACN,MAAM,KAAK,mCAAqC;CAGlD,IAAI,IAAI,IACN,MAAM,KAAK,4CAA4C;CAKzD,OAAO;;;EAFM,MAAM,SAAS,IAAI,GAAG,MAAM,KAAK,IAAI,EAAE,MAAM,GAKrD;;;AAGP;;AAGA,SAAgB,0BAAkC;CAChD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DT"}
1
+ {"version":3,"file":"base.js","names":[],"sources":["../../../src/templates/base.ts"],"sourcesContent":["import type { BootBinding } from '../addons/types.js'\nimport type { ScaffoldContext } from '../core/types.js'\n\nexport function packageJsonTemplate(projectName: string, scripts: Record<string, string> = {}): string {\n const pkg = {\n name: projectName,\n version: '1.0.0',\n private: true,\n type: 'module',\n imports: {\n '#src/*': './src/*',\n },\n scripts: {\n 'dev': 'run-p dev:server dev:taser',\n 'dev:server': 'tsx watch src/index.ts',\n 'dev:taser': 'taser watch',\n 'start': 'tsx src/index.ts',\n 'generate': 'taser generate',\n 'build': 'taser generate && tsdown',\n 'serve': 'node dist/index.mjs',\n 'typecheck': 'tsc --noEmit -p tsconfig.json',\n ...scripts,\n },\n }\n\n return `${JSON.stringify(pkg, null, 2)}\\n`\n}\n\nexport function tsdownConfigTemplate(): string {\n return `import { defineConfig } from 'tsdown'\n\nexport default defineConfig({\n entry: ['./src/index.ts'],\n platform: 'node',\n outDir: 'dist',\n clean: true,\n sourcemap: true,\n})\n`\n}\n\nexport function tsconfigTemplate(): string {\n return `${JSON.stringify({\n compilerOptions: {\n target: 'ES2022',\n module: 'NodeNext',\n paths: {\n '#src/*': ['./src/*'],\n },\n strict: true,\n skipLibCheck: true,\n verbatimModuleSyntax: true,\n isolatedModules: true,\n noEmit: true,\n types: ['node'],\n },\n include: ['src'],\n }, null, 2)}\\n`\n}\n\nexport function gitignoreTemplate(): string {\n return `node_modules\ndist\n.DS_Store\n*.log\n.env\nlocal.db\ndrizzle\n`\n}\n\nexport function contextTemplate(bindings: BootBinding[]): string {\n const imports = bindings.map(\n binding => `import { ${binding.factoryName} } from '${binding.importPath}'`,\n )\n\n const bootBody = bindings.length > 0\n ? bindings.map(binding => ` ${binding.key}: ${binding.factoryName}(),`).join('\\n')\n : ''\n\n const bootBlock = bindings.length > 0\n ? ` boot: () => ({\\n${bootBody}\\n }),`\n : ''\n\n const importBlock = imports.length > 0 ? `${imports.join('\\n')}\\n\\n` : ''\n\n return `${importBlock}import { createContext } from '@taserjs/router'\n\nexport const context = createContext({\n${bootBlock}\n request: () => ({\n requestId: crypto.randomUUID(),\n }),\n})\n`\n}\n\nexport function rootLayoutTemplate(): string {\n return `import { bodyLimit } from '@taserjs/router/body-limit'\nimport { secureHeaders } from '@taserjs/router/secure-headers'\n\nimport { t } from '#src/taser.js'\n\nexport const Middleware = t.middleware('/$')\n .use(secureHeaders())\n .use(bodyLimit({ maxSize: 1_000_000 }))\n`\n}\n\nexport function indexRouteTemplate(): string {\n return `import { reply } from '@taserjs/router'\nimport { t } from '#src/taser.js'\n\nexport const Route = t.get('/').handler(() => {\n return reply.json({ message: 'Welcome to Taser' })\n})\n`\n}\n\nexport function healthRouteTemplate(ctx: ScaffoldContext): string {\n const lines: string[] = []\n\n if (ctx.logger) {\n lines.push(' ctx.logger.info(\\'health check\\')')\n }\n\n if (ctx.db) {\n lines.push(' // ctx.db is available from context boot')\n }\n\n const body = lines.length > 0 ? `${lines.join('\\n')}\\n` : ''\n const ctxArg = lines.length > 0 ? '(ctx)' : '()'\n\n return `import { reply } from '@taserjs/router'\nimport { t } from '#src/taser.js'\n\nexport const Route = t.get('/health').handler(${ctxArg} => {\n${body} return reply.json({ ok: true })\n})\n`\n}\n\n/** Minimal placeholder until `taser generate` runs. */\nexport function starterManifestTemplate(): string {\n return `/* eslint-disable */\n// Run \\`pnpm generate\\` (taser generate) to replace this file.\nimport { Middleware as RootSplatLayoutImport } from './routes/$.js'\nimport { Route as RootIndexGetRouteImport } from './routes/index.get.js'\nimport { Route as HealthGetRouteImport } from './routes/health.get.js'\n\nexport const routeManifest = {\n layouts: {\n '/$': {\n middlewares: RootSplatLayoutImport,\n },\n },\n routes: {\n '/': {\n GET: {\n layoutChain: ['/$'],\n route: RootIndexGetRouteImport,\n },\n },\n '/health': {\n GET: {\n layoutChain: ['/$'],\n route: HealthGetRouteImport,\n },\n },\n },\n} as const\n\nexport type RoutePathGen = '/' | '/health'\nexport type LayoutIdGen = '/$'\nexport type LayoutTreeGen = {\n '/$': {\n parent: null\n middlewares: typeof RootSplatLayoutImport\n }\n}\nexport type RouteByPathMethodGen = {\n '/': {\n GET: {\n parent: '/$'\n layoutChain: ['/$']\n route: typeof RootIndexGetRouteImport\n }\n }\n '/health': {\n GET: {\n parent: '/$'\n layoutChain: ['/$']\n route: typeof HealthGetRouteImport\n }\n }\n}\nexport type RouteManifest = typeof routeManifest\n\ndeclare module '@taserjs/router' {\n interface RouterRegister {\n RoutePath: RoutePathGen\n LayoutId: LayoutIdGen\n LayoutTree: LayoutTreeGen\n RouteByPathMethod: RouteByPathMethodGen\n }\n}\n`\n}\n"],"mappings":";AAGA,SAAgB,oBAAoB,aAAqB,UAAkC,CAAC,GAAW;CACrG,MAAM,MAAM;EACV,MAAM;EACN,SAAS;EACT,SAAS;EACT,MAAM;EACN,SAAS,EACP,UAAU,UACZ;EACA,SAAS;GACP,OAAO;GACP,cAAc;GACd,aAAa;GACb,SAAS;GACT,YAAY;GACZ,SAAS;GACT,SAAS;GACT,aAAa;GACb,GAAG;EACL;CACF;CAEA,OAAO,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE;AACzC;AAEA,SAAgB,uBAA+B;CAC7C,OAAO;;;;;;;;;;AAUT;AAEA,SAAgB,mBAA2B;CACzC,OAAO,GAAG,KAAK,UAAU;EACvB,iBAAiB;GACf,QAAQ;GACR,QAAQ;GACR,OAAO,EACL,UAAU,CAAC,SAAS,EACtB;GACA,QAAQ;GACR,cAAc;GACd,sBAAsB;GACtB,iBAAiB;GACjB,QAAQ;GACR,OAAO,CAAC,MAAM;EAChB;EACA,SAAS,CAAC,KAAK;CACjB,GAAG,MAAM,CAAC,EAAE;AACd;AAEA,SAAgB,oBAA4B;CAC1C,OAAO;;;;;;;;AAQT;AAEA,SAAgB,gBAAgB,UAAiC;CAC/D,MAAM,UAAU,SAAS,KACvB,YAAW,YAAY,QAAQ,YAAY,WAAW,QAAQ,WAAW,EAC3E;CAEA,MAAM,WAAW,SAAS,SAAS,IAC/B,SAAS,KAAI,YAAW,OAAO,QAAQ,IAAI,IAAI,QAAQ,YAAY,IAAI,CAAC,CAAC,KAAK,IAAI,IAClF;CAEJ,MAAM,YAAY,SAAS,SAAS,IAChC,qBAAqB,SAAS,WAC9B;CAIJ,OAAO,GAFa,QAAQ,SAAS,IAAI,GAAG,QAAQ,KAAK,IAAI,EAAE,QAAQ,GAEjD;;;EAGtB,UAAU;;;;;;AAMZ;AAEA,SAAgB,qBAA6B;CAC3C,OAAO;;;;;;;;;AAST;AAEA,SAAgB,qBAA6B;CAC3C,OAAO;;;;;;;AAOT;AAEA,SAAgB,oBAAoB,KAA8B;CAChE,MAAM,QAAkB,CAAC;CAEzB,IAAI,IAAI,QACN,MAAM,KAAK,mCAAqC;CAGlD,IAAI,IAAI,IACN,MAAM,KAAK,4CAA4C;CAGzD,MAAM,OAAO,MAAM,SAAS,IAAI,GAAG,MAAM,KAAK,IAAI,EAAE,MAAM;CAG1D,OAAO;;;gDAFQ,MAAM,SAAS,IAAI,UAAU,KAKS;EACrD,KAAK;;;AAGP;;AAGA,SAAgB,0BAAkC;CAChD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-taserjs",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Scaffold a new Taser application",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -112,6 +112,18 @@ function driverPackages(driver: DbDriver): string[] {
112
112
  }
113
113
  }
114
114
 
115
+ function driverDevPackages(driver: DbDriver): string[] {
116
+ switch (driver) {
117
+ case 'postgres':
118
+ return ['@types/pg']
119
+ case 'sqlite':
120
+ return ['@types/better-sqlite3']
121
+ case 'mysql':
122
+ default:
123
+ return []
124
+ }
125
+ }
126
+
115
127
  export const drizzleAddon: AddonDefinition = {
116
128
  id: 'drizzle',
117
129
  category: 'database',
@@ -119,8 +131,9 @@ export const drizzleAddon: AddonDefinition = {
119
131
  const driver = ctx.driver ?? 'sqlite'
120
132
  return ['drizzle-orm', ...driverPackages(driver)]
121
133
  },
122
- devDependencies() {
123
- return ['drizzle-kit']
134
+ devDependencies(ctx) {
135
+ const driver = ctx.driver ?? 'sqlite'
136
+ return ['drizzle-kit', ...driverDevPackages(driver)]
124
137
  },
125
138
  scripts() {
126
139
  return {
@@ -73,6 +73,18 @@ function driverPackages(driver: DbDriver): string[] {
73
73
  }
74
74
  }
75
75
 
76
+ function driverDevPackages(driver: DbDriver): string[] {
77
+ switch (driver) {
78
+ case 'postgres':
79
+ return ['@types/pg']
80
+ case 'sqlite':
81
+ return ['@types/better-sqlite3']
82
+ case 'mysql':
83
+ default:
84
+ return []
85
+ }
86
+ }
87
+
76
88
  function envExample(driver: DbDriver): string {
77
89
  switch (driver) {
78
90
  case 'postgres':
@@ -92,8 +104,9 @@ export const kyselyAddon: AddonDefinition = {
92
104
  const driver = ctx.driver ?? 'sqlite'
93
105
  return ['kysely', ...driverPackages(driver)]
94
106
  },
95
- devDependencies() {
96
- return []
107
+ devDependencies(ctx) {
108
+ const driver = ctx.driver ?? 'sqlite'
109
+ return driverDevPackages(driver)
97
110
  },
98
111
  bootBinding() {
99
112
  return {
@@ -1,10 +1,12 @@
1
+ import { arktypeAddon } from './arktype/index.js'
1
2
  import { drizzleAddon } from './drizzle/index.js'
2
3
  import { kyselyAddon } from './kysely/index.js'
3
4
  import { pinoAddon } from './pino/index.js'
4
5
  import { prismaAddon } from './prisma/index.js'
6
+ import { valibotAddon } from './valibot/index.js'
7
+ import { winstonAddon } from './winston/index.js'
5
8
  import { zodAddon } from './zod/index.js'
6
9
  import type { AddonDefinition } from './types.js'
7
- import { winstonAddon } from './winston/index.js'
8
10
  import type { CapabilitiesCatalog, ScaffoldContext } from '../core/types.js'
9
11
  import {
10
12
  DB_DRIVERS,
@@ -22,6 +24,8 @@ const ALL_ADDONS: AddonDefinition[] = [
22
24
  pinoAddon,
23
25
  winstonAddon,
24
26
  zodAddon,
27
+ arktypeAddon,
28
+ valibotAddon,
25
29
  ]
26
30
 
27
31
  const DB_ADDONS = ALL_ADDONS.filter(addon => addon.category === 'database')
@@ -19,12 +19,12 @@ export const VALIDATION_BLOCK_TEMPLATE: Record<ValidatorId, string> = {
19
19
  }`,
20
20
  }
21
21
 
22
- const ROUTE_TEMPLATE = (validator: ValidatorId) => `
22
+ const ROUTE_TEMPLATE = (validator: ValidatorId) => `import { reply } from '@taserjs/router'
23
23
  import { t } from '#src/taser.js'
24
24
  ${IMPORT_LINES[validator]}
25
25
 
26
26
  export const Route = t.get('/'${VALIDATION_BLOCK_TEMPLATE[validator]}).handler((ctx) => {
27
- return ctx.reply.json({ message: \`Hello, \${ctx.query.name}!\` })
27
+ return reply.json({ message: \`Hello, \${ctx.query.name}!\` })
28
28
  })
29
29
  `
30
30
 
package/src/cli.ts CHANGED
@@ -14,6 +14,7 @@ async function main(): Promise<void> {
14
14
  framework: { type: 'string' },
15
15
  db: { type: 'string' },
16
16
  logger: { type: 'string' },
17
+ validator: { type: 'string' },
17
18
  y: { type: 'boolean', short: 'y' },
18
19
  noInstall: { type: 'boolean' },
19
20
  json: { type: 'boolean' },
@@ -26,6 +27,7 @@ async function main(): Promise<void> {
26
27
  ...(values.framework !== undefined ? { framework: values.framework } : {}),
27
28
  ...(values.db !== undefined ? { db: values.db } : {}),
28
29
  ...(values.logger !== undefined ? { logger: values.logger } : {}),
30
+ ...(values.validator !== undefined ? { validator: values.validator } : {}),
29
31
  ...(values.y !== undefined ? { y: values.y } : {}),
30
32
  ...(values.noInstall !== undefined ? { noInstall: values.noInstall } : {}),
31
33
  ...(values.json !== undefined ? { json: values.json } : {}),
@@ -9,6 +9,7 @@ import {
9
9
  parseDbFlag,
10
10
  parseFrameworkFlag,
11
11
  parseLoggerFlag,
12
+ parseValidatorFlag,
12
13
  type ParsedCreateArgs,
13
14
  resolveScaffoldDefaults,
14
15
  } from '../core/parse-options.js'
@@ -245,6 +246,7 @@ export function buildParsedArgsFromCli(values: {
245
246
  framework?: string
246
247
  db?: string
247
248
  logger?: string
249
+ validator?: string
248
250
  y?: boolean
249
251
  noInstall?: boolean
250
252
  json?: boolean
@@ -273,5 +275,9 @@ export function buildParsedArgsFromCli(values: {
273
275
  args.logger = parseLoggerFlag(values.logger)
274
276
  }
275
277
 
278
+ if (values.validator) {
279
+ args.validator = parseValidatorFlag(values.validator)
280
+ }
281
+
276
282
  return args
277
283
  }
@@ -27,18 +27,15 @@ function runCommand(command: string, args: string[], cwd: string): Promise<void>
27
27
  })
28
28
  }
29
29
 
30
- const fallback = {
31
- agent: 'npm',
32
- install: (packages: string[], dev: boolean) => {
33
- return ['install', ...packages, dev ? '-D' : '']
34
- },
35
- }
36
-
37
- function resolveInstallCommand(agent: Agent, packages: string[], dev: boolean): ResolvedCommand {
38
- const command = resolveCommand(agent, 'install', packages)
30
+ export function resolveInstallCommand(agent: Agent, packages: string[], dev: boolean): ResolvedCommand {
31
+ const args = dev ? ['-D', ...packages] : packages
32
+ const command = resolveCommand(agent, 'add', args)
39
33
  if (command) return command
40
34
 
41
- return { command: fallback.agent, args: fallback.install(packages, dev) }
35
+ return {
36
+ command: 'npm',
37
+ args: ['install', ...args],
38
+ }
42
39
  }
43
40
 
44
41
  export async function installPackages(
@@ -4,6 +4,7 @@ import {
4
4
  DEFAULT_DB_DRIVER,
5
5
  FRAMEWORKS,
6
6
  LOGGERS,
7
+ VALIDATORS,
7
8
  type DbDriver,
8
9
  type DbOdm,
9
10
  type Framework,
@@ -40,6 +41,10 @@ function isLoggerId(value: string): value is LoggerId {
40
41
  return (LOGGERS as readonly string[]).includes(value)
41
42
  }
42
43
 
44
+ function isValidatorId(value: string): value is ValidatorId {
45
+ return (VALIDATORS as readonly string[]).includes(value)
46
+ }
47
+
43
48
  export function parseDbFlag(value: string): { db: DbOdm, driver: DbDriver } {
44
49
  const [odm, driver] = value.split(':')
45
50
  if (!odm || !isDbOdm(odm)) {
@@ -61,6 +66,7 @@ export function resolveScaffoldDefaults(args: ParsedCreateArgs): ScaffoldContext
61
66
  const framework = args.framework ?? 'node'
62
67
  const db = args.db
63
68
  const logger = args.logger
69
+ const validator = args.validator
64
70
 
65
71
  if (!args.projectName) {
66
72
  throw new Error('Project name is required')
@@ -81,6 +87,10 @@ export function resolveScaffoldDefaults(args: ParsedCreateArgs): ScaffoldContext
81
87
  result.logger = logger
82
88
  }
83
89
 
90
+ if (validator) {
91
+ result.validator = validator
92
+ }
93
+
84
94
  return result
85
95
  }
86
96
 
@@ -97,3 +107,10 @@ export function parseLoggerFlag(value: string): LoggerId {
97
107
  }
98
108
  return value
99
109
  }
110
+
111
+ export function parseValidatorFlag(value: string): ValidatorId {
112
+ if (!isValidatorId(value)) {
113
+ throw new Error(`Invalid --validator "${value}". Use zod, arktype, or valibot.`)
114
+ }
115
+ return value
116
+ }
@@ -19,6 +19,10 @@ export async function writeProjectConfig(root: string, ctx: ScaffoldContext): Pr
19
19
  config.logger = ctx.logger
20
20
  }
21
21
 
22
+ if (ctx.validator) {
23
+ config.validator = ctx.validator
24
+ }
25
+
22
26
  await writeFile(
23
27
  path.join(root, '.taser.json'),
24
28
  `${JSON.stringify(config, null, 2)}\n`,
@@ -64,6 +64,7 @@ export async function scaffoldProject(options: ScaffoldOptions): Promise<Scaffol
64
64
  framework: ctx.framework,
65
65
  ...(ctx.db ? { db: ctx.db, driver: ctx.driver } : {}),
66
66
  ...(ctx.logger ? { logger: ctx.logger } : {}),
67
+ ...(ctx.validator ? { validator: ctx.validator } : {}),
67
68
  }
68
69
  }
69
70
 
@@ -79,5 +80,6 @@ export async function scaffoldProject(options: ScaffoldOptions): Promise<Scaffol
79
80
  framework: ctx.framework,
80
81
  ...(ctx.db ? { db: ctx.db, driver: ctx.driver } : {}),
81
82
  ...(ctx.logger ? { logger: ctx.logger } : {}),
83
+ ...(ctx.validator ? { validator: ctx.validator } : {}),
82
84
  }
83
85
  }
@@ -108,10 +108,11 @@ export const Middleware = t.middleware('/$')
108
108
  }
109
109
 
110
110
  export function indexRouteTemplate(): string {
111
- return `import { t } from '#src/taser.js'
111
+ return `import { reply } from '@taserjs/router'
112
+ import { t } from '#src/taser.js'
112
113
 
113
- export const Route = t.get('/').handler((ctx) => {
114
- return ctx.reply.json({ message: 'Welcome to Taser' })
114
+ export const Route = t.get('/').handler(() => {
115
+ return reply.json({ message: 'Welcome to Taser' })
115
116
  })
116
117
  `
117
118
  }
@@ -128,11 +129,13 @@ export function healthRouteTemplate(ctx: ScaffoldContext): string {
128
129
  }
129
130
 
130
131
  const body = lines.length > 0 ? `${lines.join('\n')}\n` : ''
132
+ const ctxArg = lines.length > 0 ? '(ctx)' : '()'
131
133
 
132
- return `import { t } from '#src/taser.js'
134
+ return `import { reply } from '@taserjs/router'
135
+ import { t } from '#src/taser.js'
133
136
 
134
- export const Route = t.get('/health').handler((ctx) => {
135
- ${body} return ctx.reply.json({ ok: true })
137
+ export const Route = t.get('/health').handler(${ctxArg} => {
138
+ ${body} return reply.json({ ok: true })
136
139
  })
137
140
  `
138
141
  }