@raiyyan/brisk 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/AGENTS.md +467 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1417 -2
  4. package/dist/bin/brisk.d.ts +3 -0
  5. package/dist/bin/brisk.d.ts.map +1 -0
  6. package/dist/bin/brisk.js +13 -0
  7. package/dist/bin/brisk.js.map +1 -0
  8. package/dist/src/cli/agents.d.ts +7 -0
  9. package/dist/src/cli/agents.d.ts.map +1 -0
  10. package/dist/src/cli/agents.js +55 -0
  11. package/dist/src/cli/agents.js.map +1 -0
  12. package/dist/src/cli/cli.d.ts +5 -0
  13. package/dist/src/cli/cli.d.ts.map +1 -0
  14. package/dist/src/cli/cli.js +127 -0
  15. package/dist/src/cli/cli.js.map +1 -0
  16. package/dist/src/cli/client.d.ts +12 -0
  17. package/dist/src/cli/client.d.ts.map +1 -0
  18. package/dist/src/cli/client.js +76 -0
  19. package/dist/src/cli/client.js.map +1 -0
  20. package/dist/src/cli/generate.d.ts +9 -0
  21. package/dist/src/cli/generate.d.ts.map +1 -0
  22. package/dist/src/cli/generate.js +39 -0
  23. package/dist/src/cli/generate.js.map +1 -0
  24. package/dist/src/cli/init.d.ts +19 -0
  25. package/dist/src/cli/init.d.ts.map +1 -0
  26. package/dist/src/cli/init.js +125 -0
  27. package/dist/src/cli/init.js.map +1 -0
  28. package/dist/src/cli/names.d.ts +14 -0
  29. package/dist/src/cli/names.d.ts.map +1 -0
  30. package/dist/src/cli/names.js +25 -0
  31. package/dist/src/cli/names.js.map +1 -0
  32. package/dist/src/cli/system.d.ts +35 -0
  33. package/dist/src/cli/system.d.ts.map +1 -0
  34. package/dist/src/cli/system.js +83 -0
  35. package/dist/src/cli/system.js.map +1 -0
  36. package/dist/src/cli/templates/auth.d.ts +4 -0
  37. package/dist/src/cli/templates/auth.d.ts.map +1 -0
  38. package/dist/src/cli/templates/auth.js +90 -0
  39. package/dist/src/cli/templates/auth.js.map +1 -0
  40. package/dist/src/cli/templates/database.d.ts +4 -0
  41. package/dist/src/cli/templates/database.d.ts.map +1 -0
  42. package/dist/src/cli/templates/database.js +107 -0
  43. package/dist/src/cli/templates/database.js.map +1 -0
  44. package/dist/src/cli/templates/docker.d.ts +4 -0
  45. package/dist/src/cli/templates/docker.d.ts.map +1 -0
  46. package/dist/src/cli/templates/docker.js +102 -0
  47. package/dist/src/cli/templates/docker.js.map +1 -0
  48. package/dist/src/cli/templates/feature.d.ts +8 -0
  49. package/dist/src/cli/templates/feature.d.ts.map +1 -0
  50. package/dist/src/cli/templates/feature.js +145 -0
  51. package/dist/src/cli/templates/feature.js.map +1 -0
  52. package/dist/src/cli/templates/project.d.ts +24 -0
  53. package/dist/src/cli/templates/project.d.ts.map +1 -0
  54. package/dist/src/cli/templates/project.js +166 -0
  55. package/dist/src/cli/templates/project.js.map +1 -0
  56. package/dist/src/client/client.d.ts +64 -0
  57. package/dist/src/client/client.d.ts.map +1 -0
  58. package/dist/src/client/client.js +143 -0
  59. package/dist/src/client/client.js.map +1 -0
  60. package/dist/src/client/index.d.ts +8 -0
  61. package/dist/src/client/index.d.ts.map +1 -0
  62. package/dist/src/client/index.js +7 -0
  63. package/dist/src/client/index.js.map +1 -0
  64. package/dist/src/config/env.d.ts +28 -0
  65. package/dist/src/config/env.d.ts.map +1 -0
  66. package/dist/src/config/env.js +35 -0
  67. package/dist/src/config/env.js.map +1 -0
  68. package/dist/src/config/logger.d.ts +32 -0
  69. package/dist/src/config/logger.d.ts.map +1 -0
  70. package/dist/src/config/logger.js +31 -0
  71. package/dist/src/config/logger.js.map +1 -0
  72. package/dist/src/di/container.d.ts +186 -0
  73. package/dist/src/di/container.d.ts.map +1 -0
  74. package/dist/src/di/container.js +208 -0
  75. package/dist/src/di/container.js.map +1 -0
  76. package/dist/src/di/index.d.ts +8 -0
  77. package/dist/src/di/index.d.ts.map +1 -0
  78. package/dist/src/di/index.js +8 -0
  79. package/dist/src/di/index.js.map +1 -0
  80. package/dist/src/errors/http-errors.d.ts +295 -0
  81. package/dist/src/errors/http-errors.d.ts.map +1 -0
  82. package/dist/src/errors/http-errors.js +311 -0
  83. package/dist/src/errors/http-errors.js.map +1 -0
  84. package/dist/src/errors/index.d.ts +8 -0
  85. package/dist/src/errors/index.d.ts.map +1 -0
  86. package/dist/src/errors/index.js +8 -0
  87. package/dist/src/errors/index.js.map +1 -0
  88. package/dist/src/http/app.d.ts +200 -0
  89. package/dist/src/http/app.d.ts.map +1 -0
  90. package/dist/src/http/app.js +45 -0
  91. package/dist/src/http/app.js.map +1 -0
  92. package/dist/src/http/context.d.ts +66 -0
  93. package/dist/src/http/context.d.ts.map +1 -0
  94. package/dist/src/http/context.js +96 -0
  95. package/dist/src/http/context.js.map +1 -0
  96. package/dist/src/http/controller.d.ts +178 -0
  97. package/dist/src/http/controller.d.ts.map +1 -0
  98. package/dist/src/http/controller.js +317 -0
  99. package/dist/src/http/controller.js.map +1 -0
  100. package/dist/src/http/error-handler.d.ts +59 -0
  101. package/dist/src/http/error-handler.d.ts.map +1 -0
  102. package/dist/src/http/error-handler.js +124 -0
  103. package/dist/src/http/error-handler.js.map +1 -0
  104. package/dist/src/http/instance.d.ts +14 -0
  105. package/dist/src/http/instance.d.ts.map +1 -0
  106. package/dist/src/http/instance.js +309 -0
  107. package/dist/src/http/instance.js.map +1 -0
  108. package/dist/src/http/middleware.d.ts +54 -0
  109. package/dist/src/http/middleware.d.ts.map +1 -0
  110. package/dist/src/http/middleware.js +101 -0
  111. package/dist/src/http/middleware.js.map +1 -0
  112. package/dist/src/http/openapi.d.ts +34 -0
  113. package/dist/src/http/openapi.d.ts.map +1 -0
  114. package/dist/src/http/openapi.js +123 -0
  115. package/dist/src/http/openapi.js.map +1 -0
  116. package/dist/src/http/path-params.d.ts +24 -0
  117. package/dist/src/http/path-params.d.ts.map +1 -0
  118. package/dist/src/http/path-params.js +2 -0
  119. package/dist/src/http/path-params.js.map +1 -0
  120. package/dist/src/http/routes.d.ts +119 -0
  121. package/dist/src/http/routes.d.ts.map +1 -0
  122. package/dist/src/http/routes.js +2 -0
  123. package/dist/src/http/routes.js.map +1 -0
  124. package/dist/src/http/server.d.ts +23 -0
  125. package/dist/src/http/server.d.ts.map +1 -0
  126. package/dist/src/http/server.js +22 -0
  127. package/dist/src/http/server.js.map +1 -0
  128. package/dist/src/index.d.ts +22 -0
  129. package/dist/src/index.d.ts.map +1 -0
  130. package/dist/src/index.js +19 -0
  131. package/dist/src/index.js.map +1 -0
  132. package/dist/src/testing/index.d.ts +7 -0
  133. package/dist/src/testing/index.d.ts.map +1 -0
  134. package/dist/src/testing/index.js +7 -0
  135. package/dist/src/testing/index.js.map +1 -0
  136. package/dist/src/testing/test-client.d.ts +65 -0
  137. package/dist/src/testing/test-client.d.ts.map +1 -0
  138. package/dist/src/testing/test-client.js +77 -0
  139. package/dist/src/testing/test-client.js.map +1 -0
  140. package/package.json +88 -3
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=brisk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brisk.d.ts","sourceRoot":"","sources":["../../bin/brisk.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { createInterface } from "node:readline/promises";
3
+ import { runCli } from "../src/cli/cli.js";
4
+ const interactive = process.stdin.isTTY && process.stdout.isTTY;
5
+ const prompt = interactive ? createInterface({ input: process.stdin, output: process.stdout }) : undefined;
6
+ const code = await runCli(process.argv.slice(2), process.cwd(), {
7
+ log: console.log,
8
+ error: console.error,
9
+ ...(prompt ? { ask: (question) => prompt.question(question) } : {}),
10
+ });
11
+ prompt?.close();
12
+ process.exit(code);
13
+ //# sourceMappingURL=brisk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brisk.js","sourceRoot":"","sources":["../../bin/brisk.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;AAChE,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAE3G,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE;IAC9D,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,KAAK,EAAE,OAAO,CAAC,KAAK;IACpB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;CAC5E,CAAC,CAAC;AACH,MAAM,EAAE,KAAK,EAAE,CAAC;AAChB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Coding agents read the project's own `AGENTS.md` (Claude Code reads `CLAUDE.md`), not files in node_modules.
3
+ * Writes a brisk section between markers into `AGENTS.md`, refreshed in place on later runs without touching
4
+ * the rest of the file, and makes `CLAUDE.md` import both it and the full guide shipped in the package.
5
+ */
6
+ export declare function writeAgentGuides(root: string): string[];
7
+ //# sourceMappingURL=agents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../src/cli/agents.ts"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA2BvD"}
@@ -0,0 +1,55 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { PACKAGE } from "./templates/project.js";
4
+ const GUIDE = `node_modules/${PACKAGE}/AGENTS.md`;
5
+ const START = "<!-- brisk:start -->";
6
+ const END = "<!-- brisk:end -->";
7
+ /**
8
+ * Coding agents read the project's own `AGENTS.md` (Claude Code reads `CLAUDE.md`), not files in node_modules.
9
+ * Writes a brisk section between markers into `AGENTS.md`, refreshed in place on later runs without touching
10
+ * the rest of the file, and makes `CLAUDE.md` import both it and the full guide shipped in the package.
11
+ */
12
+ export function writeAgentGuides(root) {
13
+ const agentsPath = join(root, "AGENTS.md");
14
+ const block = `${START}\n${section}\n${END}`;
15
+ const existing = existsSync(agentsPath) ? readFileSync(agentsPath, "utf8") : "";
16
+ const start = existing.indexOf(START);
17
+ const end = existing.indexOf(END);
18
+ const agents = start !== -1 && end > start
19
+ ? existing.slice(0, start) + block + existing.slice(end + END.length)
20
+ : existing === ""
21
+ ? `# AGENTS.md\n\n${block}\n`
22
+ : `${existing.trimEnd()}\n\n${block}\n`;
23
+ writeFileSync(agentsPath, agents);
24
+ const claudePath = join(root, "CLAUDE.md");
25
+ const claude = existsSync(claudePath) ? readFileSync(claudePath, "utf8") : "";
26
+ const imports = ["@AGENTS.md", `@${GUIDE}`].filter((line) => !claude.split("\n").includes(line));
27
+ if (imports.length > 0) {
28
+ writeFileSync(claudePath, `${claude.trimEnd()}${claude === "" ? "" : "\n"}${imports.join("\n")}\n`);
29
+ }
30
+ return [
31
+ `${existing === "" ? "created" : "updated"} AGENTS.md`,
32
+ imports.length > 0
33
+ ? `${claude === "" ? "created" : "updated"} CLAUDE.md`
34
+ : "CLAUDE.md already imports the guide",
35
+ ];
36
+ }
37
+ const section = `## Building with brisk
38
+
39
+ This app uses [brisk](https://www.npmjs.com/package/${PACKAGE}), a typed layer over Express 5 and Zod.
40
+ Before writing or changing server code, read the full guide: \`${GUIDE}\`.
41
+
42
+ Essentials:
43
+
44
+ - Features live in \`src/features/<name>/\` as \`schema.ts → repository.ts → service.ts → controller.ts\`; create them with \`npx brisk g feature <name>\`.
45
+ - The app is declared once in \`src/app.ts\` with \`defineApp\`; \`app.listen(port)\` runs it, \`createTestClient(app, { providers })\` tests it.
46
+ - Routes are named: a controller's setup returns \`{ list: route.get(...), create: route.post(...) }\`. The names form the typed client (\`api.users.create({ body })\`).
47
+ - Validate every input with a Zod schema on the route; set \`response\` to a DTO schema so internal fields never leak.
48
+ - Dependencies: \`readonly #repo = inject(Repository)\` in class fields. No decorators, no constructor wiring.
49
+ - Errors: \`throw new NotFoundError("…")\` (or another error class) anywhere. Never try/catch to build responses; never call \`next()\`.
50
+ - Shared request logic: \`defineMiddleware\` returning an object types \`ctx\` (\`return { user }\` gives \`ctx.user\`).
51
+ - Plain Express needs (CORS, static files, uploads, webhooks, GraphQL) go in \`defineApp({ configureExpress })\`.
52
+ - Long-lived work (WebSockets, jobs, consumers, pools) is a provider class with \`onInit\`, \`onReady\`, \`onDrain\`, \`onShutdown\`.
53
+ - Test with \`createTestClient\` from \`${PACKAGE}/testing\`: \`api.call.<controller>.<route>(input)\` is typed; \`api.post(path)\` checks raw HTTP.
54
+ `;
55
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../../src/cli/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,MAAM,KAAK,GAAG,gBAAgB,OAAO,YAAY,CAAC;AAClD,MAAM,KAAK,GAAG,sBAAsB,CAAC;AACrC,MAAM,GAAG,GAAG,oBAAoB,CAAC;AAEjC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,GAAG,KAAK,KAAK,OAAO,KAAK,GAAG,EAAE,CAAC;IAC7C,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChF,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,MAAM,GACV,KAAK,KAAK,CAAC,CAAC,IAAI,GAAG,GAAG,KAAK;QACzB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC;QACrE,CAAC,CAAC,QAAQ,KAAK,EAAE;YACf,CAAC,CAAC,kBAAkB,KAAK,IAAI;YAC7B,CAAC,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAElC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,MAAM,OAAO,GAAG,CAAC,YAAY,EAAE,IAAI,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACjG,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,aAAa,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtG,CAAC;IAED,OAAO;QACL,GAAG,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,YAAY;QACtD,OAAO,CAAC,MAAM,GAAG,CAAC;YAChB,CAAC,CAAC,GAAG,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,YAAY;YACtD,CAAC,CAAC,qCAAqC;KAC1C,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,GAAG;;sDAEsC,OAAO;iEACI,KAAK;;;;;;;;;;;;;0CAa5B,OAAO;CAChD,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { type CliIo } from "./system.ts";
2
+ export type { CliIo } from "./system.ts";
3
+ /** Entry point of the `brisk` binary; resolves with the exit code. */
4
+ export declare function runCli(argv: readonly string[], root: string, io?: CliIo): Promise<number>;
5
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/cli/cli.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAG3D,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAwBzC,sEAAsE;AACtE,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAE,KAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAqExG"}
@@ -0,0 +1,127 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+ import { parseArgs } from "node:util";
4
+ import { writeAgentGuides } from "./agents.js";
5
+ import { generateClient, generateOpenApi } from "./client.js";
6
+ import { generateFeature, generateMiddleware } from "./generate.js";
7
+ import { initProject } from "./init.js";
8
+ import { toNames } from "./names.js";
9
+ import { CliError, format } from "./system.js";
10
+ import { REGISTRY } from "./templates/project.js";
11
+ const USAGE = `Usage: brisk <command> [options]
12
+
13
+ Commands:
14
+ init Scaffold an app here (never overwrites files); asks about each option
15
+ --db <none|sqlite|postgres> Database provider, migrations and a 409 for unique violations
16
+ --auth JWT bearer auth: authenticate and requireRole middlewares
17
+ --docker Dockerfile, compose.yaml and .dockerignore
18
+ --ci GitHub Actions workflow: typecheck and tests
19
+ --web CORS, rate limiting, static files and pretty dev logs
20
+ --pm <pnpm|npm|yarn> Package manager (default: detected)
21
+ --yes, -y Take defaults for everything not given; ask nothing
22
+ --no-install Print the install commands instead of running them
23
+ g feature <name> Feature folder (schema, repository, service, controller) and test; registers it
24
+ g middleware <name> Typed middleware in src/middlewares
25
+ client --out <file> Write the route manifest for a frontend's typed client
26
+ openapi --out <file> Write the OpenAPI 3.1 document built from the routes' schemas
27
+ --app <file> Module exporting the app, for client and openapi (default src/app.ts)
28
+ agents Add or refresh the brisk guide for AI coding agents (AGENTS.md, CLAUDE.md)
29
+ help, --version
30
+
31
+ Run from the app's root.`;
32
+ /** Entry point of the `brisk` binary; resolves with the exit code. */
33
+ export async function runCli(argv, root, io = console) {
34
+ try {
35
+ const { values, positionals } = parseArgs({
36
+ args: [...argv],
37
+ allowPositionals: true,
38
+ allowNegative: true,
39
+ options: {
40
+ db: { type: "string" },
41
+ auth: { type: "boolean" },
42
+ docker: { type: "boolean" },
43
+ ci: { type: "boolean" },
44
+ web: { type: "boolean" },
45
+ pm: { type: "string" },
46
+ yes: { type: "boolean", short: "y", default: false },
47
+ install: { type: "boolean", default: true },
48
+ out: { type: "string" },
49
+ app: { type: "string", default: "src/app.ts" },
50
+ help: { type: "boolean", short: "h", default: false },
51
+ version: { type: "boolean", default: false },
52
+ },
53
+ });
54
+ const [command, kind, name] = positionals;
55
+ if (values.version) {
56
+ io.log(version());
57
+ return 0;
58
+ }
59
+ if (values.help || command === "help" || command === undefined) {
60
+ io.log(USAGE);
61
+ return command === undefined && !values.help ? 1 : 0;
62
+ }
63
+ if (command === "init") {
64
+ for (const line of await initProject(root, values, io))
65
+ io.log(line);
66
+ return 0;
67
+ }
68
+ if (command === "agents") {
69
+ for (const line of writeAgentGuides(root))
70
+ io.log(line);
71
+ return 0;
72
+ }
73
+ if (command === "client" || command === "openapi") {
74
+ if (!values.out)
75
+ throw new CliError(`brisk ${command} needs --out <file>`);
76
+ const generate = command === "client" ? generateClient : generateOpenApi;
77
+ for (const line of await generate(root, { app: values.app, out: values.out }))
78
+ io.log(line);
79
+ return 0;
80
+ }
81
+ if ((command !== "g" && command !== "generate") || !name)
82
+ throw new CliError(USAGE);
83
+ const names = toNames(name);
84
+ const files = kind === "feature"
85
+ ? generateFeature(root, names)
86
+ : kind === "middleware"
87
+ ? generateMiddleware(root, names)
88
+ : null;
89
+ if (!files)
90
+ throw new CliError(USAGE);
91
+ for (const file of files)
92
+ io.log(`created ${relative(root, file)}`);
93
+ if (kind === "feature")
94
+ io.log(`updated ${REGISTRY}`);
95
+ const formatted = format(root, kind === "feature" ? [...files, join(root, REGISTRY)] : files);
96
+ if (formatted)
97
+ io.log(formatted);
98
+ return 0;
99
+ }
100
+ catch (error) {
101
+ if (error instanceof CliError) {
102
+ io.error(error.message);
103
+ return 1;
104
+ }
105
+ if (error instanceof TypeError && "code" in error && String(error.code).startsWith("ERR_PARSE_ARGS")) {
106
+ io.error(`${error.message}\n\n${USAGE}`);
107
+ return 1;
108
+ }
109
+ throw error;
110
+ }
111
+ }
112
+ /** The installed brisk version: the nearest `package.json` above this file, in sources and in `dist` alike. */
113
+ function version() {
114
+ for (let dir = new URL(".", import.meta.url); dir.pathname !== "/"; dir = new URL("..", dir)) {
115
+ try {
116
+ const pkg = JSON.parse(readFileSync(new URL("package.json", dir), "utf8"));
117
+ const value = typeof pkg === "object" && pkg !== null ? Reflect.get(pkg, "version") : undefined;
118
+ if (typeof value === "string")
119
+ return value;
120
+ }
121
+ catch {
122
+ continue;
123
+ }
124
+ }
125
+ return "unknown";
126
+ }
127
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../src/cli/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ interface Target {
2
+ /** The module exporting `app` (made with `defineApp`), relative to the project root. */
3
+ app: string;
4
+ /** The file to write, relative to the project root. */
5
+ out: string;
6
+ }
7
+ /** Writes the app's route manifest as a TypeScript module, for a frontend's typed client. */
8
+ export declare function generateClient(root: string, { app, out }: Target): Promise<string[]>;
9
+ /** Writes the app's OpenAPI 3.1 document as JSON. */
10
+ export declare function generateOpenApi(root: string, { app, out }: Target): Promise<string[]>;
11
+ export {};
12
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/cli/client.ts"],"names":[],"mappings":"AAYA,UAAU,MAAM;IACd,wFAAwF;IACxF,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,6FAA6F;AAC7F,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAW1F;AAED,qDAAqD;AACrD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAI3F"}
@@ -0,0 +1,76 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { existsSync } from "node:fs";
10
+ import { join, relative } from "node:path";
11
+ import { pathToFileURL } from "node:url";
12
+ import { CliError, write } from "./system.js";
13
+ /** Writes the app's route manifest as a TypeScript module, for a frontend's typed client. */
14
+ export async function generateClient(root, { app, out }) {
15
+ const { manifest } = await loadApp(root, app);
16
+ const target = write(join(root, out), `// Generated by \`brisk client\` from ${app}. Do not edit: run it again after changing routes.
17
+ import type { Manifest } from "@raiyyan/brisk/client";
18
+
19
+ export const manifest = ${serialize(manifest, "")} as const satisfies Manifest;
20
+ `);
21
+ return [`wrote ${relative(root, target)}`, "use it: createClient<Api>(manifest, { baseUrl })"];
22
+ }
23
+ /** Writes the app's OpenAPI 3.1 document as JSON. */
24
+ export async function generateOpenApi(root, { app, out }) {
25
+ const { openapi } = await loadApp(root, app);
26
+ const target = write(join(root, out), `${JSON.stringify(openapi(), null, 2)}\n`);
27
+ return [`wrote ${relative(root, target)}`];
28
+ }
29
+ /**
30
+ * Imports the app module and reads its `app` export. Nothing listens, but the module runs and the routes are
31
+ * built: the environment must be valid, as for starting the app.
32
+ */
33
+ async function loadApp(root, app) {
34
+ const source = join(root, app);
35
+ if (!existsSync(source)) {
36
+ throw new CliError(`${app} not found: pass --app <path to the module exporting app>`);
37
+ }
38
+ const module = await import(__rewriteRelativeImportExtension(pathToFileURL(source).href));
39
+ const loaded = findApp(module);
40
+ if (!loaded)
41
+ throw new CliError(`${app} must export app, made with defineApp()`);
42
+ return loaded;
43
+ }
44
+ function findApp(module) {
45
+ if (typeof module !== "object" || module === null)
46
+ return undefined;
47
+ const app = Reflect.get(module, "app");
48
+ if (typeof app !== "object" || app === null)
49
+ return undefined;
50
+ const manifest = Reflect.get(app, "manifest");
51
+ const openapi = Reflect.get(app, "openapi");
52
+ if (!isManifest(manifest))
53
+ return undefined;
54
+ return {
55
+ manifest,
56
+ openapi: () => (typeof openapi === "function" ? Reflect.apply(openapi, app, []) : {}),
57
+ };
58
+ }
59
+ /** The manifest as source, one route per line: `"get": ["GET", "/api/users/:id"],`. */
60
+ function serialize(manifest, indent) {
61
+ const inner = `${indent} `;
62
+ const lines = Object.entries(manifest).map(([name, entry]) => {
63
+ const value = isEndpoint(entry)
64
+ ? `[${entry.map((part) => JSON.stringify(part)).join(", ")}]`
65
+ : serialize(entry, inner);
66
+ return `${inner}${JSON.stringify(name)}: ${value},`;
67
+ });
68
+ return `{\n${lines.join("\n")}\n${indent}}`;
69
+ }
70
+ function isEndpoint(entry) {
71
+ return Array.isArray(entry);
72
+ }
73
+ function isManifest(value) {
74
+ return typeof value === "object" && value !== null && !Array.isArray(value);
75
+ }
76
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/cli/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Names } from "./names.ts";
2
+ /**
3
+ * Writes a feature folder (schema, repository, service, controller) and its test, and registers the controller
4
+ * in `src/features/index.ts` under its camel-case name, which becomes its key in the typed client.
5
+ */
6
+ export declare function generateFeature(root: string, names: Names): string[];
7
+ /** Writes a typed middleware to `src/middlewares/<name>.ts`. */
8
+ export declare function generateMiddleware(root: string, names: Names): string[];
9
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/cli/generate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAYxC;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CA2BpE;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAIvE"}
@@ -0,0 +1,39 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { CliError, write } from "./system.js";
4
+ import { controllerTemplate, middlewareTemplate, repositoryTemplate, schemaTemplate, serviceTemplate, testTemplate, } from "./templates/feature.js";
5
+ import { CONTROLLERS_MARKER, IMPORTS_MARKER, REGISTRY } from "./templates/project.js";
6
+ /**
7
+ * Writes a feature folder (schema, repository, service, controller) and its test, and registers the controller
8
+ * in `src/features/index.ts` under its camel-case name, which becomes its key in the typed client.
9
+ */
10
+ export function generateFeature(root, names) {
11
+ const { kebab, camel } = names;
12
+ const dir = join(root, "src/features", kebab);
13
+ if (existsSync(dir))
14
+ throw new CliError(`src/features/${kebab} already exists`);
15
+ const registry = join(root, REGISTRY);
16
+ const source = existsSync(registry) ? readFileSync(registry, "utf8") : "";
17
+ if (!source.includes(IMPORTS_MARKER) || !source.includes(CONTROLLERS_MARKER)) {
18
+ throw new CliError(`${REGISTRY} must contain "${IMPORTS_MARKER}" and "${CONTROLLERS_MARKER}"`);
19
+ }
20
+ const created = [
21
+ write(join(dir, `${kebab}.schema.ts`), schemaTemplate(names)),
22
+ write(join(dir, `${kebab}.repository.ts`), repositoryTemplate(names)),
23
+ write(join(dir, `${kebab}.service.ts`), serviceTemplate(names)),
24
+ write(join(dir, `${kebab}.controller.ts`), controllerTemplate(names)),
25
+ write(join(root, "test", `${kebab}.test.ts`), testTemplate(names)),
26
+ ];
27
+ writeFileSync(registry, source
28
+ .replace(IMPORTS_MARKER, `import { ${camel}Controller } from "./${kebab}/${kebab}.controller.ts";\n${IMPORTS_MARKER}`)
29
+ .replace(/^(\s*)\/\/ @brisk:controllers/m, `$1${camel}: ${camel}Controller,\n$1${CONTROLLERS_MARKER}`));
30
+ return created;
31
+ }
32
+ /** Writes a typed middleware to `src/middlewares/<name>.ts`. */
33
+ export function generateMiddleware(root, names) {
34
+ const path = join(root, "src/middlewares", `${names.kebab}.ts`);
35
+ if (existsSync(path))
36
+ throw new CliError(`src/middlewares/${names.kebab}.ts already exists`);
37
+ return [write(path, middlewareTemplate(names))];
38
+ }
39
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/cli/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,YAAY,GACb,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEtF;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAAY;IACxD,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC;IAC9C,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,QAAQ,CAAC,gBAAgB,KAAK,iBAAiB,CAAC,CAAC;IAChF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC7E,MAAM,IAAI,QAAQ,CAAC,GAAG,QAAQ,kBAAkB,cAAc,UAAU,kBAAkB,GAAG,CAAC,CAAC;IACjG,CAAC;IAED,MAAM,OAAO,GAAG;QACd,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,YAAY,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;QAC7D,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,gBAAgB,CAAC,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACrE,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,aAAa,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,gBAAgB,CAAC,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACrE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,UAAU,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;KACnE,CAAC;IACF,aAAa,CACX,QAAQ,EACR,MAAM;SACH,OAAO,CACN,cAAc,EACd,YAAY,KAAK,wBAAwB,KAAK,IAAI,KAAK,qBAAqB,cAAc,EAAE,CAC7F;SACA,OAAO,CAAC,gCAAgC,EAAE,KAAK,KAAK,KAAK,KAAK,kBAAkB,kBAAkB,EAAE,CAAC,CACzG,CAAC;IACF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,KAAY;IAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,iBAAiB,EAAE,GAAG,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC;IAChE,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,QAAQ,CAAC,mBAAmB,KAAK,CAAC,KAAK,oBAAoB,CAAC,CAAC;IAC7F,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type CliIo } from "./system.ts";
2
+ /** `brisk init` flags; an option left out is asked for interactively, or takes its default with `--yes`. */
3
+ export interface InitFlags {
4
+ db?: string | undefined;
5
+ auth?: boolean | undefined;
6
+ docker?: boolean | undefined;
7
+ ci?: boolean | undefined;
8
+ web?: boolean | undefined;
9
+ pm?: string | undefined;
10
+ yes: boolean;
11
+ install: boolean;
12
+ }
13
+ /**
14
+ * Scaffolds a runnable app with the chosen options. Existing files are never overwritten; `package.json` gains
15
+ * `"type": "module"` and the scripts it lacks; missing packages are installed with the project's package manager
16
+ * (or printed, with `--no-install`).
17
+ */
18
+ export declare function initProject(root: string, flags: InitFlags, io: CliIo): Promise<string[]>;
19
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/init.ts"],"names":[],"mappings":"AAGA,OAAO,EAOL,KAAK,KAAK,EAEX,MAAM,aAAa,CAAC;AAGrB,4GAA4G;AAC5G,MAAM,WAAW,SAAS;IACxB,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,EAAE,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACzB,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAKD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAwD9F"}
@@ -0,0 +1,125 @@
1
+ import { existsSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { writeAgentGuides } from "./agents.js";
4
+ import { addCommand, CliError, detectPackageManager, readPackageJson, run, write, } from "./system.js";
5
+ import { projectPlan } from "./templates/project.js";
6
+ /** `npm init` writes a `test` script that only fails; init replaces it. */
7
+ const NPM_TEST_STUB = "Error: no test specified";
8
+ /**
9
+ * Scaffolds a runnable app with the chosen options. Existing files are never overwritten; `package.json` gains
10
+ * `"type": "module"` and the scripts it lacks; missing packages are installed with the project's package manager
11
+ * (or printed, with `--no-install`).
12
+ */
13
+ export async function initProject(root, flags, io) {
14
+ const options = await resolveOptions(root, flags, io);
15
+ const plan = projectPlan(options);
16
+ const lines = [];
17
+ for (const [path, content] of Object.entries(plan.files)) {
18
+ const target = join(root, path);
19
+ if (existsSync(target)) {
20
+ lines.push(`kept ${path}`);
21
+ continue;
22
+ }
23
+ write(target, content);
24
+ lines.push(`created ${path}`);
25
+ }
26
+ const pkg = readPackageJson(root);
27
+ const added = Object.entries(plan.scripts).filter(([name]) => {
28
+ const existing = pkg.scripts?.[name];
29
+ return existing === undefined || existing.includes(NPM_TEST_STUB);
30
+ });
31
+ writeFileSync(join(root, "package.json"), `${JSON.stringify({ ...pkg, type: pkg.type ?? "module", scripts: { ...pkg.scripts, ...Object.fromEntries(added) } }, null, 2)}\n`);
32
+ lines.push(`updated package.json${added.length > 0 ? ` (scripts: ${added.map(([name]) => name).join(", ")})` : ""}`);
33
+ const installed = { ...pkg.dependencies, ...pkg.devDependencies };
34
+ const missing = (names) => [...new Set(names)].filter((name) => installed[name] === undefined);
35
+ const commands = [
36
+ ...(missing(plan.dependencies).length > 0
37
+ ? [addCommand(options.pm, missing(plan.dependencies), false)]
38
+ : []),
39
+ ...(missing(plan.devDependencies).length > 0
40
+ ? [addCommand(options.pm, missing(plan.devDependencies), true)]
41
+ : []),
42
+ ];
43
+ lines.push(...writeAgentGuides(root));
44
+ for (const line of lines)
45
+ io.log(line);
46
+ const failed = flags.install ? install(commands, root, io) : [];
47
+ if (!flags.install)
48
+ for (const command of commands)
49
+ io.log(`install: ${command.join(" ")}`);
50
+ if (failed.length > 0) {
51
+ throw new CliError([
52
+ "The project files are in place, but installing packages failed. Fix the cause above, then run:",
53
+ ...failed.map((command) => ` ${command.join(" ")}`),
54
+ ...(options.pm === "pnpm"
55
+ ? ["If pnpm blocked build scripts, review them with: pnpm approve-builds"]
56
+ : []),
57
+ ].join("\n"));
58
+ }
59
+ return ["", ...nextSteps(options)];
60
+ }
61
+ /** Runs each install command with its output shown; returns the ones that failed. Never throws. */
62
+ function install(commands, root, io) {
63
+ return commands.filter((command) => {
64
+ io.log(`$ ${command.join(" ")}`);
65
+ try {
66
+ run(command, root);
67
+ return false;
68
+ }
69
+ catch {
70
+ return true;
71
+ }
72
+ });
73
+ }
74
+ async function resolveOptions(root, flags, io) {
75
+ const ask = flags.yes ? undefined : io.ask;
76
+ const db = flags.db ?? (ask ? await choose(ask, "Database", ["none", "sqlite", "postgres"]) : "none");
77
+ if (db !== "none" && db !== "sqlite" && db !== "postgres") {
78
+ throw new CliError(`Unknown database "${db}": use none, sqlite or postgres.`);
79
+ }
80
+ const pm = flags.pm ?? detectPackageManager(root);
81
+ if (!isPackageManager(pm))
82
+ throw new CliError(`Unknown package manager "${pm}": use pnpm, npm or yarn.`);
83
+ const confirm = async (flag, question) => flag ?? (ask ? await yesNo(ask, question) : false);
84
+ return {
85
+ db,
86
+ auth: await confirm(flags.auth, "JWT bearer auth (authenticate and requireRole middlewares)?"),
87
+ docker: await confirm(flags.docker, "Dockerfile and compose.yaml?"),
88
+ ci: await confirm(flags.ci, "GitHub Actions CI (typecheck and tests)?"),
89
+ web: await confirm(flags.web, "Web extras (CORS, rate limiting, static files, pretty dev logs)?"),
90
+ pm,
91
+ };
92
+ }
93
+ async function choose(ask, question, choices) {
94
+ for (;;) {
95
+ // oxlint-disable-next-line no-await-in-loop -- asks again until the answer is valid
96
+ const answer = (await ask(`${question} (${choices.join(" / ")}) [${choices[0] ?? ""}]: `))
97
+ .trim()
98
+ .toLowerCase();
99
+ if (answer === "")
100
+ return choices[0] ?? "";
101
+ if (choices.includes(answer))
102
+ return answer;
103
+ }
104
+ }
105
+ async function yesNo(ask, question) {
106
+ const answer = (await ask(`${question} (y/N): `)).trim().toLowerCase();
107
+ return answer === "y" || answer === "yes";
108
+ }
109
+ function isPackageManager(value) {
110
+ return value === "pnpm" || value === "npm" || value === "yarn";
111
+ }
112
+ function nextSteps({ pm, db, docker }) {
113
+ const exec = pm === "npm" ? "npx" : pm;
114
+ return [
115
+ "Next:",
116
+ " cp .env.example .env",
117
+ ...(db === "postgres" && docker ? [" docker compose up -d db"] : []),
118
+ ...(db === "postgres"
119
+ ? [` ${pm} run db:generate && ${pm} run db:migrate # after defining tables`]
120
+ : []),
121
+ ` ${exec} brisk g feature users`,
122
+ ` ${pm} run dev`,
123
+ ];
124
+ }
125
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /** The spellings of a name a generator needs. */
2
+ export interface Names {
3
+ /** Folder, file and route name, as typed: `blog-posts`. */
4
+ kebab: string;
5
+ /** Controller and middleware variables, and the client's key: `blogPosts`. */
6
+ camel: string;
7
+ /** Service and repository classes: `BlogPosts`. */
8
+ pascal: string;
9
+ /** One record and its schemas: `BlogPost`, `CreateBlogPostBody`. */
10
+ entity: string;
11
+ }
12
+ /** `"BlogPosts"`, `"blog_posts"` and `"blog posts"` all give `blog-posts`, `blogPosts`, `BlogPosts`, `BlogPost`. */
13
+ export declare function toNames(raw: string): Names;
14
+ //# sourceMappingURL=names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../../src/cli/names.ts"],"names":[],"mappings":"AAEA,iDAAiD;AACjD,MAAM,WAAW,KAAK;IACpB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,oHAAoH;AACpH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAW1C"}
@@ -0,0 +1,25 @@
1
+ import { CliError } from "./system.js";
2
+ /** `"BlogPosts"`, `"blog_posts"` and `"blog posts"` all give `blog-posts`, `blogPosts`, `BlogPosts`, `BlogPost`. */
3
+ export function toNames(raw) {
4
+ const kebab = raw
5
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
6
+ .replace(/[\s_]+/g, "-")
7
+ .toLowerCase();
8
+ if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(kebab)) {
9
+ throw new CliError(`Invalid name "${raw}": use letters, digits and dashes, starting with a letter.`);
10
+ }
11
+ const camel = kebab.replace(/-([a-z0-9])/g, (_match, char) => char.toUpperCase());
12
+ const pascal = camel.charAt(0).toUpperCase() + camel.slice(1);
13
+ return { kebab, camel, pascal, entity: singular(pascal) };
14
+ }
15
+ /** `Products` gives `Product`, `Categories` gives `Category`, `Addresses` gives `Address`; `Status` stays. */
16
+ function singular(word) {
17
+ if (/[^aeiou]ies$/.test(word))
18
+ return `${word.slice(0, -3)}y`;
19
+ if (/(ss|sh|ch|x|z|us)es$/.test(word))
20
+ return word.slice(0, -2);
21
+ if (/(ss|us|is)$/.test(word))
22
+ return word;
23
+ return word.endsWith("s") ? word.slice(0, -1) : word;
24
+ }
25
+ //# sourceMappingURL=names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.js","sourceRoot":"","sources":["../../../src/cli/names.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAcvC,oHAAoH;AACpH,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,MAAM,KAAK,GAAG,GAAG;SACd,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,WAAW,EAAE,CAAC;IACjB,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,QAAQ,CAAC,iBAAiB,GAAG,4DAA4D,CAAC,CAAC;IACvG,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC1F,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC9D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;AAC5D,CAAC;AAED,8GAA8G;AAC9G,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9D,IAAI,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvD,CAAC"}