create-kubojs 0.2.2 → 0.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
@@ -25,8 +25,8 @@ Follow the prompts to configure your project or use the `--yes` flag for default
25
25
  | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
26
26
  | **TypeScript** | End-to-end type safety across all parts of your application |
27
27
  | **Frontend** | • React with TanStack Router<br>• React with React Router<br>• React with TanStack Start (SSR)<br>• Next.js<br>• SvelteKit<br>• Nuxt (Vue)<br>• SolidJS<br>• Astro<br>• React Native bare Expo<br>• React Native with NativeWind (via Expo)<br>• React Native with Unistyles (via Expo)<br>• None |
28
- | **Backend** | • Hono<br>• Express<br>• Elysia<br>• Fastify<br>• Self (fullstack inside the web app)<br>• Convex<br>• None |
29
- | **API Layer** | • tRPC (type-safe APIs)<br>• oRPC (OpenAPI-compatible type-safe APIs)<br>• Orval (OpenAPI REST APIs with generated Hono handlers)<br>• None |
28
+ | **Backend** | • Hono<br>• Express<br>• Elysia<br>• Fastify<br>• NestJS<br>• Self (fullstack inside the web app)<br>• Convex<br>• None |
29
+ | **API Layer** | • tRPC (type-safe APIs)<br>• oRPC (OpenAPI-compatible type-safe APIs)<br>• Orval (OpenAPI REST APIs with generated handlers or Fetch clients)<br>• None |
30
30
  | **Runtime** | • Bun<br>• Node.js<br>• Cloudflare Workers<br>• None |
31
31
  | **Database** | • SQLite<br>• PostgreSQL<br>• MySQL<br>• MongoDB<br>• None |
32
32
  | **ORM** | • Drizzle (TypeScript-first)<br>• Prisma (feature-rich)<br>• Mongoose (for MongoDB)<br>• None |
@@ -64,7 +64,7 @@ Options:
64
64
  --db-setup <setup> Database setup (turso, d1, neon, supabase, prisma-postgres, planetscale, mongodb-atlas, docker, none)
65
65
  --web-deploy <setup> Web deployment (cloudflare, docker, vercel, none)
66
66
  --server-deploy <setup> Server deployment (cloudflare, docker, vercel, none)
67
- --backend <framework> Backend framework (hono, express, fastify, elysia, convex, self, none)
67
+ --backend <framework> Backend framework (hono, express, fastify, elysia, nestjs, convex, self, none)
68
68
  --runtime <runtime> Runtime (bun, node, workers, none)
69
69
  --api <type> API type (trpc, orpc, orval, none)
70
70
  --directory-conflict <strategy> Directory strategy (merge, overwrite, increment, error)
@@ -233,10 +233,10 @@ npx kubojs --frontend none --backend hono --api trpc --database none --addons no
233
233
 
234
234
  - **Convex backend**: Requires `database`, `orm`, `api`, `runtime`, and `server-deploy` to be `none`; auth can be `better-auth`, `clerk`, or `none` depending frontend compatibility
235
235
  - **Backend 'none'**: If selected, this option will force related options like API, ORM, database, authentication, and runtime to 'none'. Examples will also be disabled (set to none/empty).
236
+ - **NestJS backend**: The initial KuboJS templates support PostgreSQL + Prisma, Better Auth or no authentication, and no API layer. These are current template capabilities, not limitations of NestJS itself; additional database, ORM, API, and example integrations can be added independently.
236
237
  - **Frontend 'none'**: Creates a backend-only project. When selected, PWA, Tauri, Electrobun, and certain examples may be disabled.
237
238
  - **API 'none'**: Disables tRPC/oRPC/Orval setup. Can be used with backend frameworks for REST APIs or custom API implementations.
238
- - **API 'orval'**: Generates a local OpenAPI contract, Hono REST handlers, and a Fetch client. Orval currently requires `--backend hono`; run `bun run api:generate` after changing `apps/api/openapi.yaml`.
239
- The generated `todo` example is not supported by Orval yet.
239
+ - **API 'orval'**: Generates a local OpenAPI contract, REST handlers, and typed Fetch client for Hono. Orval requires `--backend hono`; run `bun run api:generate` after changing `apps/api/openapi.yaml`.
240
240
  - **Database 'none'**: Disables database setup and requires ORM to be `none`.
241
241
  - **ORM 'none'**: Can be used when you want to handle database operations manually or use a different ORM.
242
242
  - **Runtime 'none'**: Only available with Convex backend, backend `none`, or backend `self`.
@@ -245,7 +245,7 @@ npx kubojs --frontend none --backend hono --api trpc --database none --addons no
245
245
  - **Addons 'none'**: Skips all addons.
246
246
  - **Testing 'none'**: Skips all testing tools. Playwright requires a web frontend.
247
247
  - **Examples 'none'**: Skips all example implementations (todo, AI chat).
248
- - **Nuxt, Svelte, SolidJS, and Astro** frontends are not compatible with tRPC; use oRPC, Orval with Hono, or no API layer.
248
+ - **Nuxt, Svelte, SolidJS, and Astro** frontends are not compatible with tRPC; use oRPC, Orval, or no API layer.
249
249
  - **PWA support** requires TanStack Router, React Router, Next.js, or SolidJS
250
250
  - **Tauri desktop app** requires TanStack Router, React Router, TanStack Start, Next.js, Nuxt, SvelteKit, SolidJS, or Astro
251
251
  - **Electrobun desktop app** requires TanStack Router, React Router, TanStack Start, Next.js, Nuxt, SvelteKit, SolidJS, or Astro. Desktop packaging uses static web assets, so SSR-first frontends need a static/export build before desktop builds will work.
package/dist/cli.mjs CHANGED
@@ -1,8 +1,141 @@
1
1
  #!/usr/bin/env node
2
- import { _ as getLatestCLIVersion, g as types_exports, i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli } from "./src-CAMyqVYx.mjs";
2
+ import { _ as getLatestCLIVersion, g as types_exports, i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli } from "./src-9z53Xnti.mjs";
3
3
  import z from "zod";
4
+ import { format } from "node:util";
4
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ //#region src/commands/downloads.ts
8
+ const PACKAGE_NAME = "create-kubojs";
9
+ const REGISTRY_URL = "https://registry.npmjs.org";
10
+ const DOWNLOADS_API_URL = "https://api.npmjs.org/downloads";
11
+ const MAX_RANGE_MONTHS = 18;
12
+ const REQUEST_TIMEOUT_MS = 15e3;
13
+ const MAX_REQUEST_ATTEMPTS = 3;
14
+ const defaultSleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
15
+ function dateOnly(date) {
16
+ return date.toISOString().slice(0, 10);
17
+ }
18
+ function parseDateOnly(value) {
19
+ const date = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
20
+ if (Number.isNaN(date.getTime()) || dateOnly(date) !== value) throw new Error(`Invalid date: ${value}`);
21
+ return date;
22
+ }
23
+ function addDays(date, days) {
24
+ const result = new Date(date);
25
+ result.setUTCDate(result.getUTCDate() + days);
26
+ return result;
27
+ }
28
+ function addMonthsSafely(date, months) {
29
+ const targetMonth = date.getUTCMonth() + months;
30
+ const targetYear = date.getUTCFullYear() + Math.floor(targetMonth / 12);
31
+ const normalizedMonth = (targetMonth % 12 + 12) % 12;
32
+ const lastDay = new Date(Date.UTC(targetYear, normalizedMonth + 1, 0)).getUTCDate();
33
+ return new Date(Date.UTC(targetYear, normalizedMonth, Math.min(date.getUTCDate(), lastDay)));
34
+ }
35
+ function rangeUrl(packageName, start, end) {
36
+ return `${DOWNLOADS_API_URL}/range/${start}:${end}/${encodeURIComponent(packageName)}`;
37
+ }
38
+ async function fetchJson(url, fetchImpl, sleep = defaultSleep) {
39
+ let lastError;
40
+ for (let attempt = 1; attempt <= MAX_REQUEST_ATTEMPTS; attempt++) {
41
+ let response;
42
+ try {
43
+ response = await fetchImpl(url, {
44
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
45
+ headers: { Accept: "application/json" }
46
+ });
47
+ } catch (error) {
48
+ lastError = error;
49
+ }
50
+ if (response) if (!response.ok) {
51
+ const body = await response.text();
52
+ const error = /* @__PURE__ */ new Error(`Request failed (${response.status}): ${body || response.statusText}`);
53
+ if (response.status < 500 && response.status !== 429) throw error;
54
+ lastError = error;
55
+ } else return await response.json();
56
+ if (attempt < MAX_REQUEST_ATTEMPTS) await sleep(250 * 2 ** (attempt - 1));
57
+ }
58
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
59
+ }
60
+ async function getPackageCreatedDate(packageName, fetchImpl = fetch, sleep = defaultSleep) {
61
+ const created = (await fetchJson(`${REGISTRY_URL}/${encodeURIComponent(packageName)}`, fetchImpl, sleep)).time?.created;
62
+ if (!created) throw new Error(`npm metadata did not include a creation date for ${packageName}`);
63
+ const createdDate = new Date(created);
64
+ if (Number.isNaN(createdDate.getTime())) throw new Error(`npm metadata included an invalid creation date for ${packageName}`);
65
+ return dateOnly(createdDate);
66
+ }
67
+ async function getAllTimeDownloads(options) {
68
+ const packageName = options?.packageName ?? PACKAGE_NAME;
69
+ const fetchImpl = options?.fetchImpl ?? fetch;
70
+ const sleep = options?.sleep ?? defaultSleep;
71
+ const today = options?.today ?? dateOnly(/* @__PURE__ */ new Date());
72
+ const start = await getPackageCreatedDate(packageName, fetchImpl, sleep);
73
+ const endDate = parseDateOnly(today);
74
+ let cursor = parseDateOnly(start);
75
+ let totalDownloads = 0;
76
+ let chunks = 0;
77
+ if (cursor > endDate) throw new Error(`Package creation date ${start} is after today ${today}`);
78
+ while (cursor <= endDate) {
79
+ const nextCursor = addMonthsSafely(cursor, MAX_RANGE_MONTHS);
80
+ const chunkEnd = nextCursor > endDate ? endDate : addDays(nextCursor, -1);
81
+ const response = await fetchJson(rangeUrl(packageName, dateOnly(cursor), dateOnly(chunkEnd)), fetchImpl, sleep);
82
+ for (const entry of response.downloads) {
83
+ if (!Number.isFinite(entry.downloads) || entry.downloads < 0) throw new Error(`Invalid download count for ${entry.day}: ${format(entry.downloads)}`);
84
+ totalDownloads += entry.downloads;
85
+ }
86
+ chunks++;
87
+ cursor = addDays(chunkEnd, 1);
88
+ }
89
+ return {
90
+ packageName,
91
+ start,
92
+ end: today,
93
+ totalDownloads,
94
+ chunks
95
+ };
96
+ }
97
+ function formatHumanReport(report) {
98
+ return [
99
+ `${report.packageName} downloads`,
100
+ `Period: ${report.start} → ${report.end}`,
101
+ `Total: ${report.totalDownloads.toLocaleString("en-US")} downloads`
102
+ ].join("\n");
103
+ }
104
+ function parseArguments(arguments_) {
105
+ const unknownArgument = arguments_.find((argument) => argument !== "--json" && argument !== "--help");
106
+ if (unknownArgument) throw new Error(`Unknown argument: ${unknownArgument}`);
107
+ return {
108
+ json: arguments_.includes("--json"),
109
+ help: arguments_.includes("--help")
110
+ };
111
+ }
112
+ function formatHelp(usage = "bun run downloads [--json]") {
113
+ return [
114
+ `Usage: ${usage}`,
115
+ "",
116
+ `Reports all-time npm downloads for ${PACKAGE_NAME}.`
117
+ ].join("\n");
118
+ }
119
+ async function runDownloadsCommand(arguments_, options, usage = "kubojs stats downloads [--json]") {
120
+ const parsedArguments = parseArguments(arguments_);
121
+ if (parsedArguments.help) {
122
+ console.log(formatHelp(usage));
123
+ return;
124
+ }
125
+ const report = await getAllTimeDownloads(options);
126
+ console.log(parsedArguments.json ? JSON.stringify(report, null, 2) : formatHumanReport(report));
127
+ }
128
+ //#endregion
129
+ //#region src/commands/stats.ts
130
+ const statsCommands = { downloads: runDownloadsCommand };
131
+ async function runStatsCommand(arguments_) {
132
+ const [metric, ...metricArguments] = arguments_;
133
+ if (!metric) throw new Error("Usage: kubojs stats downloads [--json]");
134
+ const command = statsCommands[metric];
135
+ if (!command) throw new Error(`Unknown stats metric: ${metric}`);
136
+ await command(metricArguments);
137
+ }
138
+ //#endregion
6
139
  //#region src/mcp.ts
7
140
  const ToolResponseSchema = z.object({
8
141
  ok: z.boolean(),
@@ -293,6 +426,12 @@ This command is intended to be launched by an MCP client, for example:
293
426
  process.exit(0);
294
427
  }
295
428
  await startBtsMcpServer();
296
- } else await createBtsCli().run();
429
+ } else if (command === "stats") try {
430
+ await runStatsCommand(args);
431
+ } catch (error) {
432
+ console.error(error instanceof Error ? error.message : error);
433
+ process.exitCode = 1;
434
+ }
435
+ else await createBtsCli().run();
297
436
  //#endregion
298
437
  export {};
package/dist/index.d.mts CHANGED
@@ -4,6 +4,7 @@ import * as _$better_result0 from "better-result";
4
4
  import { Result, Result as Result$1 } from "better-result";
5
5
  import { TrpcCliMeta, createCli } from "trpc-cli";
6
6
  import z from "zod";
7
+ import { CompatibilityIssueCode } from "@kubojs/types";
7
8
  import { EMBEDDED_TEMPLATES, GeneratorError, GeneratorError as GeneratorError$1, GeneratorOptions, TEMPLATE_COUNT, VirtualDirectory, VirtualFile, VirtualFileSystem, VirtualFileTree, VirtualFileTree as VirtualFileTree$1, VirtualNode, generate } from "@kubojs/template-generator";
8
9
 
9
10
  //#region src/types.d.ts
@@ -58,6 +59,7 @@ declare class CLIError extends CLIError_base {}
58
59
  declare const ValidationError_base: _$better_result0.TaggedErrorClass<"ValidationError", {
59
60
  field?: string;
60
61
  value?: unknown;
62
+ compatibilityCode?: CompatibilityIssueCode;
61
63
  message: string;
62
64
  }>;
63
65
  /**
@@ -67,6 +69,7 @@ declare class ValidationError extends ValidationError_base {
67
69
  constructor(args: {
68
70
  field?: string;
69
71
  value?: unknown;
72
+ compatibilityCode?: CompatibilityIssueCode;
70
73
  message: string;
71
74
  });
72
75
  }
@@ -179,7 +182,7 @@ declare const router: _$_trpc_server0.TRPCBuiltRouter<{
179
182
  payments?: unknown;
180
183
  observability?: unknown;
181
184
  disableObservability?: boolean | undefined;
182
- communication?: "none" | "resend" | "notifique" | "arara" | undefined;
185
+ communication?: unknown;
183
186
  frontend?: ("none" | "tanstack-router" | "react-router" | "tanstack-start" | "next" | "nuxt" | "native-bare" | "native-uniwind" | "native-unistyles" | "svelte" | "solid" | "astro")[] | undefined;
184
187
  addons?: ("none" | "pwa" | "tauri" | "electrobun" | "biome" | "lefthook" | "husky" | "mcp" | "turborepo" | "vite-plus" | "oxlint" | "opentui" | "skills" | "s3-storage")[] | undefined;
185
188
  examples?: ("none" | "todo" | "ai")[] | undefined;
@@ -188,12 +191,12 @@ declare const router: _$_trpc_server0.TRPCBuiltRouter<{
188
191
  packageManager?: "bun" | "npm" | "pnpm" | undefined;
189
192
  install?: boolean | undefined;
190
193
  dbSetup?: "none" | "turso" | "neon" | "prisma-postgres" | "planetscale" | "mongodb-atlas" | "supabase" | "d1" | "docker" | undefined;
191
- backend?: "none" | "hono" | "express" | "fastify" | "elysia" | "convex" | "self" | undefined;
194
+ backend?: "none" | "hono" | "express" | "fastify" | "elysia" | "nestjs" | "convex" | "self" | undefined;
192
195
  runtime?: "none" | "bun" | "node" | "workers" | undefined;
193
196
  api?: "none" | "trpc" | "orpc" | "orval" | undefined;
194
- webDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "guaracloud" | undefined;
195
- serverDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "guaracloud" | undefined;
196
- directoryConflict?: "merge" | "overwrite" | "increment" | "error" | undefined;
197
+ webDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "railway" | "guaracloud" | undefined;
198
+ serverDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "railway" | "guaracloud" | undefined;
199
+ directoryConflict?: "error" | "merge" | "overwrite" | "increment" | undefined;
197
200
  renderTitle?: boolean | undefined;
198
201
  disableAnalytics?: boolean | undefined;
199
202
  manualDb?: boolean | undefined;
@@ -265,7 +268,7 @@ declare const router: _$_trpc_server0.TRPCBuiltRouter<{
265
268
  payments?: unknown;
266
269
  observability?: unknown;
267
270
  disableObservability?: boolean | undefined;
268
- communication?: "none" | "resend" | "notifique" | "arara" | undefined;
271
+ communication?: unknown;
269
272
  frontend?: ("none" | "tanstack-router" | "react-router" | "tanstack-start" | "next" | "nuxt" | "native-bare" | "native-uniwind" | "native-unistyles" | "svelte" | "solid" | "astro")[] | undefined;
270
273
  addons?: ("none" | "pwa" | "tauri" | "electrobun" | "biome" | "lefthook" | "husky" | "mcp" | "turborepo" | "vite-plus" | "oxlint" | "opentui" | "skills" | "s3-storage")[] | undefined;
271
274
  examples?: ("none" | "todo" | "ai")[] | undefined;
@@ -274,12 +277,12 @@ declare const router: _$_trpc_server0.TRPCBuiltRouter<{
274
277
  packageManager?: "bun" | "npm" | "pnpm" | undefined;
275
278
  install?: boolean | undefined;
276
279
  dbSetup?: "none" | "turso" | "neon" | "prisma-postgres" | "planetscale" | "mongodb-atlas" | "supabase" | "d1" | "docker" | undefined;
277
- backend?: "none" | "hono" | "express" | "fastify" | "elysia" | "convex" | "self" | undefined;
280
+ backend?: "none" | "hono" | "express" | "fastify" | "elysia" | "nestjs" | "convex" | "self" | undefined;
278
281
  runtime?: "none" | "bun" | "node" | "workers" | undefined;
279
282
  api?: "none" | "trpc" | "orpc" | "orval" | undefined;
280
- webDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "guaracloud" | undefined;
281
- serverDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "guaracloud" | undefined;
282
- directoryConflict?: "merge" | "overwrite" | "increment" | "error" | undefined;
283
+ webDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "railway" | "guaracloud" | undefined;
284
+ serverDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "railway" | "guaracloud" | undefined;
285
+ directoryConflict?: "error" | "merge" | "overwrite" | "increment" | undefined;
283
286
  renderTitle?: boolean | undefined;
284
287
  disableAnalytics?: boolean | undefined;
285
288
  manualDb?: boolean | undefined;
@@ -337,8 +340,8 @@ declare const router: _$_trpc_server0.TRPCBuiltRouter<{
337
340
  }[] | undefined;
338
341
  } | undefined;
339
342
  } | undefined;
340
- webDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "guaracloud" | undefined;
341
- serverDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "guaracloud" | undefined;
343
+ webDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "railway" | "guaracloud" | undefined;
344
+ serverDeploy?: "none" | "docker" | "cloudflare" | "vercel" | "railway" | "guaracloud" | undefined;
342
345
  projectDir?: string | undefined;
343
346
  install?: boolean | undefined;
344
347
  packageManager?: "bun" | "npm" | "pnpm" | undefined;
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { C as UserCancelledError, S as ProjectCreationError, a as TEMPLATE_COUNT, b as DatabaseSetupError, c as builder, d as createVirtual, f as docs, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, v as CLIError, w as ValidationError, x as DirectoryConflictError, y as CompatibilityError } from "./src-CAMyqVYx.mjs";
2
+ import { C as UserCancelledError, S as ProjectCreationError, a as TEMPLATE_COUNT, b as DatabaseSetupError, c as builder, d as createVirtual, f as docs, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, v as CLIError, w as ValidationError, x as DirectoryConflictError, y as CompatibilityError } from "./src-9z53Xnti.mjs";
3
3
  export { CLIError, CompatibilityError, DatabaseSetupError, DirectoryConflictError, EMBEDDED_TEMPLATES, GeneratorError, ProjectCreationError, Result, SchemaNameSchema, TEMPLATE_COUNT, UserCancelledError, ValidationError, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generate, getSchemaResult, router };