@raiyyan/brisk 0.0.0-stage → 0.2.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 (206) hide show
  1. package/AGENTS.md +535 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1745 -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 +113 -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/init.d.ts +16 -0
  21. package/dist/src/cli/init.d.ts.map +1 -0
  22. package/dist/src/cli/init.js +149 -0
  23. package/dist/src/cli/init.js.map +1 -0
  24. package/dist/src/cli/names.d.ts +14 -0
  25. package/dist/src/cli/names.d.ts.map +1 -0
  26. package/dist/src/cli/names.js +25 -0
  27. package/dist/src/cli/names.js.map +1 -0
  28. package/dist/src/cli/render.d.ts +30 -0
  29. package/dist/src/cli/render.d.ts.map +1 -0
  30. package/dist/src/cli/render.js +101 -0
  31. package/dist/src/cli/render.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 +81 -0
  35. package/dist/src/cli/system.js.map +1 -0
  36. package/dist/src/client/client.d.ts +64 -0
  37. package/dist/src/client/client.d.ts.map +1 -0
  38. package/dist/src/client/client.js +170 -0
  39. package/dist/src/client/client.js.map +1 -0
  40. package/dist/src/client/index.d.ts +8 -0
  41. package/dist/src/client/index.d.ts.map +1 -0
  42. package/dist/src/client/index.js +7 -0
  43. package/dist/src/client/index.js.map +1 -0
  44. package/dist/src/config/env.d.ts +28 -0
  45. package/dist/src/config/env.d.ts.map +1 -0
  46. package/dist/src/config/env.js +35 -0
  47. package/dist/src/config/env.js.map +1 -0
  48. package/dist/src/config/logger.d.ts +32 -0
  49. package/dist/src/config/logger.d.ts.map +1 -0
  50. package/dist/src/config/logger.js +31 -0
  51. package/dist/src/config/logger.js.map +1 -0
  52. package/dist/src/di/container.d.ts +194 -0
  53. package/dist/src/di/container.d.ts.map +1 -0
  54. package/dist/src/di/container.js +225 -0
  55. package/dist/src/di/container.js.map +1 -0
  56. package/dist/src/di/index.d.ts +8 -0
  57. package/dist/src/di/index.d.ts.map +1 -0
  58. package/dist/src/di/index.js +8 -0
  59. package/dist/src/di/index.js.map +1 -0
  60. package/dist/src/errors/http-errors.d.ts +295 -0
  61. package/dist/src/errors/http-errors.d.ts.map +1 -0
  62. package/dist/src/errors/http-errors.js +311 -0
  63. package/dist/src/errors/http-errors.js.map +1 -0
  64. package/dist/src/errors/index.d.ts +8 -0
  65. package/dist/src/errors/index.d.ts.map +1 -0
  66. package/dist/src/errors/index.js +8 -0
  67. package/dist/src/errors/index.js.map +1 -0
  68. package/dist/src/http/app.d.ts +229 -0
  69. package/dist/src/http/app.d.ts.map +1 -0
  70. package/dist/src/http/app.js +45 -0
  71. package/dist/src/http/app.js.map +1 -0
  72. package/dist/src/http/context.d.ts +66 -0
  73. package/dist/src/http/context.d.ts.map +1 -0
  74. package/dist/src/http/context.js +96 -0
  75. package/dist/src/http/context.js.map +1 -0
  76. package/dist/src/http/controller.d.ts +195 -0
  77. package/dist/src/http/controller.d.ts.map +1 -0
  78. package/dist/src/http/controller.js +290 -0
  79. package/dist/src/http/controller.js.map +1 -0
  80. package/dist/src/http/error-handler.d.ts +86 -0
  81. package/dist/src/http/error-handler.d.ts.map +1 -0
  82. package/dist/src/http/error-handler.js +147 -0
  83. package/dist/src/http/error-handler.js.map +1 -0
  84. package/dist/src/http/instance.d.ts +14 -0
  85. package/dist/src/http/instance.d.ts.map +1 -0
  86. package/dist/src/http/instance.js +377 -0
  87. package/dist/src/http/instance.js.map +1 -0
  88. package/dist/src/http/middleware.d.ts +141 -0
  89. package/dist/src/http/middleware.d.ts.map +1 -0
  90. package/dist/src/http/middleware.js +160 -0
  91. package/dist/src/http/middleware.js.map +1 -0
  92. package/dist/src/http/openapi.d.ts +34 -0
  93. package/dist/src/http/openapi.d.ts.map +1 -0
  94. package/dist/src/http/openapi.js +151 -0
  95. package/dist/src/http/openapi.js.map +1 -0
  96. package/dist/src/http/path-params.d.ts +24 -0
  97. package/dist/src/http/path-params.d.ts.map +1 -0
  98. package/dist/src/http/path-params.js +2 -0
  99. package/dist/src/http/path-params.js.map +1 -0
  100. package/dist/src/http/routes.d.ts +141 -0
  101. package/dist/src/http/routes.d.ts.map +1 -0
  102. package/dist/src/http/routes.js +2 -0
  103. package/dist/src/http/routes.js.map +1 -0
  104. package/dist/src/http/server.d.ts +23 -0
  105. package/dist/src/http/server.d.ts.map +1 -0
  106. package/dist/src/http/server.js +22 -0
  107. package/dist/src/http/server.js.map +1 -0
  108. package/dist/src/http/validation.d.ts +16 -0
  109. package/dist/src/http/validation.d.ts.map +1 -0
  110. package/dist/src/http/validation.js +51 -0
  111. package/dist/src/http/validation.js.map +1 -0
  112. package/dist/src/index.d.ts +22 -0
  113. package/dist/src/index.d.ts.map +1 -0
  114. package/dist/src/index.js +19 -0
  115. package/dist/src/index.js.map +1 -0
  116. package/dist/src/testing/index.d.ts +7 -0
  117. package/dist/src/testing/index.d.ts.map +1 -0
  118. package/dist/src/testing/index.js +7 -0
  119. package/dist/src/testing/index.js.map +1 -0
  120. package/dist/src/testing/test-client.d.ts +67 -0
  121. package/dist/src/testing/test-client.d.ts.map +1 -0
  122. package/dist/src/testing/test-client.js +78 -0
  123. package/dist/src/testing/test-client.js.map +1 -0
  124. package/package.json +89 -3
  125. package/starters/minimal/.env.example +3 -0
  126. package/starters/minimal/.oxfmtrc.json +5 -0
  127. package/starters/minimal/.oxlintrc.json +14 -0
  128. package/starters/minimal/AGENTS.md +23 -0
  129. package/starters/minimal/CLAUDE.md +2 -0
  130. package/starters/minimal/README.md +53 -0
  131. package/starters/minimal/_gitignore +3 -0
  132. package/starters/minimal/package.json +29 -0
  133. package/starters/minimal/src/app.ts +14 -0
  134. package/starters/minimal/src/env.ts +7 -0
  135. package/starters/minimal/src/features/index.ts +8 -0
  136. package/starters/minimal/src/features/notes/notes.controller.ts +38 -0
  137. package/starters/minimal/src/features/notes/notes.repository.ts +36 -0
  138. package/starters/minimal/src/features/notes/notes.schema.ts +24 -0
  139. package/starters/minimal/src/features/notes/notes.service.ts +33 -0
  140. package/starters/minimal/src/main.ts +4 -0
  141. package/starters/minimal/src/schemas.ts +26 -0
  142. package/starters/minimal/templates/feature/src/features/index.ts.controllers.insert +1 -0
  143. package/starters/minimal/templates/feature/src/features/index.ts.imports.insert +1 -0
  144. package/starters/minimal/templates/feature/src/features/{{kebab}}/{{kebab}}.controller.ts +38 -0
  145. package/starters/minimal/templates/feature/src/features/{{kebab}}/{{kebab}}.repository.ts +36 -0
  146. package/starters/minimal/templates/feature/src/features/{{kebab}}/{{kebab}}.schema.ts +24 -0
  147. package/starters/minimal/templates/feature/src/features/{{kebab}}/{{kebab}}.service.ts +33 -0
  148. package/starters/minimal/templates/feature/test/{{kebab}}.service.test.ts +20 -0
  149. package/starters/minimal/templates/feature/test/{{kebab}}.test.ts +35 -0
  150. package/starters/minimal/templates/middleware/src/middlewares/{{kebab}}.ts +12 -0
  151. package/starters/minimal/test/notes.service.test.ts +20 -0
  152. package/starters/minimal/test/notes.test.ts +35 -0
  153. package/starters/minimal/tsconfig.json +17 -0
  154. package/starters/minimal/vitest.config.ts +9 -0
  155. package/starters/sqlite-auth/.dockerignore +8 -0
  156. package/starters/sqlite-auth/.env.example +9 -0
  157. package/starters/sqlite-auth/.oxfmtrc.json +5 -0
  158. package/starters/sqlite-auth/.oxlintrc.json +14 -0
  159. package/starters/sqlite-auth/AGENTS.md +23 -0
  160. package/starters/sqlite-auth/CLAUDE.md +2 -0
  161. package/starters/sqlite-auth/Dockerfile +23 -0
  162. package/starters/sqlite-auth/README.md +93 -0
  163. package/starters/sqlite-auth/_gitignore +5 -0
  164. package/starters/sqlite-auth/compose.yaml +14 -0
  165. package/starters/sqlite-auth/package.json +35 -0
  166. package/starters/sqlite-auth/public/.gitkeep +0 -0
  167. package/starters/sqlite-auth/src/app.ts +39 -0
  168. package/starters/sqlite-auth/src/auth/tokens.ts +41 -0
  169. package/starters/sqlite-auth/src/database/clock.ts +4 -0
  170. package/starters/sqlite-auth/src/database/database.ts +29 -0
  171. package/starters/sqlite-auth/src/database/errors.ts +11 -0
  172. package/starters/sqlite-auth/src/database/migrations.ts +29 -0
  173. package/starters/sqlite-auth/src/env.ts +17 -0
  174. package/starters/sqlite-auth/src/features/auth/auth.controller.ts +28 -0
  175. package/starters/sqlite-auth/src/features/index.ts +12 -0
  176. package/starters/sqlite-auth/src/features/notes/notes.controller.ts +39 -0
  177. package/starters/sqlite-auth/src/features/notes/notes.repository.ts +47 -0
  178. package/starters/sqlite-auth/src/features/notes/notes.schema.ts +24 -0
  179. package/starters/sqlite-auth/src/features/notes/notes.service.ts +33 -0
  180. package/starters/sqlite-auth/src/features/users/admin-seed.ts +21 -0
  181. package/starters/sqlite-auth/src/features/users/users.controller.ts +42 -0
  182. package/starters/sqlite-auth/src/features/users/users.errors.ts +13 -0
  183. package/starters/sqlite-auth/src/features/users/users.repository.ts +77 -0
  184. package/starters/sqlite-auth/src/features/users/users.schema.ts +35 -0
  185. package/starters/sqlite-auth/src/features/users/users.service.ts +80 -0
  186. package/starters/sqlite-auth/src/main.ts +4 -0
  187. package/starters/sqlite-auth/src/middlewares/authenticate.ts +29 -0
  188. package/starters/sqlite-auth/src/schemas.ts +26 -0
  189. package/starters/sqlite-auth/templates/feature/src/database/migrations.ts.migrations.insert +5 -0
  190. package/starters/sqlite-auth/templates/feature/src/features/index.ts.controllers.insert +1 -0
  191. package/starters/sqlite-auth/templates/feature/src/features/index.ts.imports.insert +1 -0
  192. package/starters/sqlite-auth/templates/feature/src/features/{{kebab}}/{{kebab}}.controller.ts +39 -0
  193. package/starters/sqlite-auth/templates/feature/src/features/{{kebab}}/{{kebab}}.repository.ts +47 -0
  194. package/starters/sqlite-auth/templates/feature/src/features/{{kebab}}/{{kebab}}.schema.ts +24 -0
  195. package/starters/sqlite-auth/templates/feature/src/features/{{kebab}}/{{kebab}}.service.ts +33 -0
  196. package/starters/sqlite-auth/templates/feature/test/{{kebab}}.service.test.ts +20 -0
  197. package/starters/sqlite-auth/templates/feature/test/{{kebab}}.test.ts +41 -0
  198. package/starters/sqlite-auth/templates/middleware/src/middlewares/{{kebab}}.ts +12 -0
  199. package/starters/sqlite-auth/test/auth.test.ts +18 -0
  200. package/starters/sqlite-auth/test/helpers.ts +13 -0
  201. package/starters/sqlite-auth/test/notes.service.test.ts +20 -0
  202. package/starters/sqlite-auth/test/notes.test.ts +41 -0
  203. package/starters/sqlite-auth/test/users.service.test.ts +25 -0
  204. package/starters/sqlite-auth/test/users.test.ts +164 -0
  205. package/starters/sqlite-auth/tsconfig.json +17 -0
  206. package/starters/sqlite-auth/vitest.config.ts +15 -0
@@ -0,0 +1,101 @@
1
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+ import { CliError, write } from "./system.js";
4
+ /** A template file whose text is added to an existing file: `<target>.<marker>.insert`. */
5
+ const INSERT = /^(?<target>.+)\.(?<marker>[\w-]+)\.insert$/;
6
+ /**
7
+ * Renders the project's own `templates/<kind>/` for `names`. Every file is copied with its path and contents
8
+ * rendered; `{{kebab}}`, `{{camel}}`, `{{pascal}}`, `{{snake}}`, `{{entity}}` and `{{entityCamel}}` are the
9
+ * tokens. A file named `<target>.<marker>.insert` is not copied: its text goes into the existing `<target>`,
10
+ * right before the line that holds `@brisk:<marker>`.
11
+ *
12
+ * Everything is checked before anything is written: an unknown token, a file that already exists, or a
13
+ * missing marker stops the run with nothing changed.
14
+ */
15
+ export function renderTemplates(root, kind, names) {
16
+ const plan = planTemplates(join(root, "templates", kind), names, root);
17
+ for (const path of plan.files.keys()) {
18
+ if (existsSync(join(root, path)))
19
+ throw new CliError(`${path} already exists`);
20
+ }
21
+ const updates = new Map();
22
+ for (const { target, marker, text } of plan.inserts) {
23
+ const path = join(root, target);
24
+ if (!existsSync(path))
25
+ throw new CliError(`templates/${kind} adds to ${target}, which does not exist`);
26
+ updates.set(target, insertBefore(updates.get(target) ?? readFileSync(path, "utf8"), marker, text, target));
27
+ }
28
+ for (const [path, content] of plan.files)
29
+ write(join(root, path), content);
30
+ for (const [path, content] of updates)
31
+ writeFileSync(join(root, path), content);
32
+ return {
33
+ created: [...plan.files.keys()].map((path) => join(root, path)),
34
+ updated: [...updates.keys()].map((path) => join(root, path)),
35
+ };
36
+ }
37
+ /** Renders a template folder in memory: the files it creates and the text it adds, nothing written. */
38
+ export function planTemplates(dir, names, root) {
39
+ if (!existsSync(dir)) {
40
+ throw new CliError(`This project has no ${relative(root, dir)}/ to generate from.${listKinds(root)} ` +
41
+ "Templates are files in your project: copy them from a starter at " +
42
+ "https://github.com/MohammadRaiyyan/brisk/tree/main/starters, or write your own.");
43
+ }
44
+ const tokens = tokensFor(names);
45
+ const plan = { files: new Map(), inserts: [] };
46
+ for (const source of walk(dir)) {
47
+ const path = render(relative(dir, source), tokens, source);
48
+ const content = render(readFileSync(source, "utf8"), tokens, source);
49
+ const insert = INSERT.exec(path)?.groups;
50
+ if (insert?.["target"] && insert["marker"]) {
51
+ plan.inserts.push({ target: insert["target"], marker: insert["marker"], text: content });
52
+ }
53
+ else {
54
+ plan.files.set(path, content);
55
+ }
56
+ }
57
+ return plan;
58
+ }
59
+ function tokensFor(names) {
60
+ return {
61
+ kebab: names.kebab,
62
+ camel: names.camel,
63
+ pascal: names.pascal,
64
+ snake: names.kebab.replaceAll("-", "_"),
65
+ entity: names.entity,
66
+ entityCamel: names.entity.charAt(0).toLowerCase() + names.entity.slice(1),
67
+ };
68
+ }
69
+ function render(text, tokens, source) {
70
+ return text.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, name) => {
71
+ const value = tokens[name];
72
+ if (value === undefined) {
73
+ throw new CliError(`Unknown token ${match} in ${source}; known: ${Object.keys(tokens)
74
+ .map((key) => `{{${key}}}`)
75
+ .join(" ")}`);
76
+ }
77
+ return value;
78
+ });
79
+ }
80
+ /** Adds `text` on its own lines right before the line holding `@brisk:<marker>`. */
81
+ function insertBefore(source, marker, text, file) {
82
+ const lines = source.split("\n");
83
+ const at = lines.findIndex((line) => line.includes(`@brisk:${marker}`));
84
+ if (at === -1)
85
+ throw new CliError(`${file} has no line with "@brisk:${marker}" to add to`);
86
+ lines.splice(at, 0, ...text.replace(/\n$/, "").split("\n"));
87
+ return lines.join("\n");
88
+ }
89
+ function walk(dir) {
90
+ return readdirSync(dir, { withFileTypes: true })
91
+ .toSorted((a, b) => a.name.localeCompare(b.name))
92
+ .flatMap((entry) => (entry.isDirectory() ? walk(join(dir, entry.name)) : [join(dir, entry.name)]));
93
+ }
94
+ function listKinds(root) {
95
+ const templates = join(root, "templates");
96
+ if (!existsSync(templates))
97
+ return "";
98
+ const kinds = readdirSync(templates, { withFileTypes: true }).filter((entry) => entry.isDirectory());
99
+ return kinds.length === 0 ? "" : ` Available: ${kinds.map((entry) => entry.name).join(", ")}.`;
100
+ }
101
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/cli/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ /** The framework's package name, as apps depend on it. */
3
+ export declare const PACKAGE = "@raiyyan/brisk";
4
+ /** An error the CLI reports as a message and exit code 1, without a stack trace. */
5
+ export declare class CliError extends Error {
6
+ }
7
+ /** Where the CLI prints, and how it asks; tests pass their own. */
8
+ export interface CliIo {
9
+ log(message: string): void;
10
+ error(message: string): void;
11
+ /** Asks a question and resolves with the answer; absent when the CLI must not prompt. */
12
+ ask?: (question: string) => Promise<string>;
13
+ }
14
+ export type PackageManager = "pnpm" | "npm" | "yarn";
15
+ /** The package manager that ran the CLI, else the one whose lockfile is present, else npm. */
16
+ export declare function detectPackageManager(root: string): PackageManager;
17
+ /** Runs a command in `root` with its output shown, as a person would type it. */
18
+ export declare function run(command: readonly string[], root: string): void;
19
+ /** Writes a file, creating its folder. */
20
+ export declare function write(path: string, content: string): string;
21
+ /**
22
+ * Runs the project's own formatter on the written files, so generated code matches the codebase, and returns
23
+ * the line to print. A failing formatter is reported, never fatal: the files are already written and valid.
24
+ */
25
+ export declare function format(root: string, files: readonly string[]): string | undefined;
26
+ export declare const PackageJson: z.ZodObject<{
27
+ type: z.ZodOptional<z.ZodString>;
28
+ scripts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
29
+ dependencies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
30
+ devDependencies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
31
+ }, z.core.$loose>;
32
+ export type PackageJson = z.infer<typeof PackageJson>;
33
+ /** Reads `package.json`, or an empty one when it does not exist. */
34
+ export declare function readPackageJson(root: string): PackageJson;
35
+ //# sourceMappingURL=system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../../src/cli/system.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,0DAA0D;AAC1D,eAAO,MAAM,OAAO,mBAAmB,CAAC;AAExC,oFAAoF;AACpF,qBAAa,QAAS,SAAQ,KAAK;CAAG;AAEtC,mEAAmE;AACnE,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,yFAAyF;IACzF,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7C;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAErD,8FAA8F;AAC9F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAOjE;AAED,iFAAiF;AACjF,wBAAgB,GAAG,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAIlE;AAED,0CAA0C;AAC1C,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAI3D;AAQD;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAejF;AAED,eAAO,MAAM,WAAW;;;;;iBAKtB,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAQzD"}
@@ -0,0 +1,81 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { z } from "zod";
5
+ /** The framework's package name, as apps depend on it. */
6
+ export const PACKAGE = "@raiyyan/brisk";
7
+ /** An error the CLI reports as a message and exit code 1, without a stack trace. */
8
+ export class CliError extends Error {
9
+ }
10
+ /** The package manager that ran the CLI, else the one whose lockfile is present, else npm. */
11
+ export function detectPackageManager(root) {
12
+ const agent = process.env["npm_config_user_agent"] ?? "";
13
+ if (agent.startsWith("pnpm"))
14
+ return "pnpm";
15
+ if (agent.startsWith("yarn"))
16
+ return "yarn";
17
+ if (existsSync(join(root, "pnpm-lock.yaml")))
18
+ return "pnpm";
19
+ if (existsSync(join(root, "yarn.lock")))
20
+ return "yarn";
21
+ return "npm";
22
+ }
23
+ /** Runs a command in `root` with its output shown, as a person would type it. */
24
+ export function run(command, root) {
25
+ const [bin, ...args] = command;
26
+ if (!bin)
27
+ return;
28
+ execFileSync(bin, args, { cwd: root, stdio: "inherit" });
29
+ }
30
+ /** Writes a file, creating its folder. */
31
+ export function write(path, content) {
32
+ mkdirSync(dirname(path), { recursive: true });
33
+ writeFileSync(path, content);
34
+ return path;
35
+ }
36
+ const FORMATTERS = [
37
+ { bin: "oxfmt", args: [] },
38
+ { bin: "prettier", args: ["--write", "--log-level", "silent"] },
39
+ { bin: "biome", args: ["format", "--write"] },
40
+ ];
41
+ /**
42
+ * Runs the project's own formatter on the written files, so generated code matches the codebase, and returns
43
+ * the line to print. A failing formatter is reported, never fatal: the files are already written and valid.
44
+ */
45
+ export function format(root, files) {
46
+ for (let dir = root;; dir = dirname(dir)) {
47
+ for (const { bin, args } of FORMATTERS) {
48
+ const path = join(dir, "node_modules/.bin", bin);
49
+ if (existsSync(path)) {
50
+ try {
51
+ execFileSync(path, [...args, ...files], { cwd: root, stdio: "ignore" });
52
+ return `formatted with ${bin}`;
53
+ }
54
+ catch {
55
+ return `${bin} failed on the generated files; run it yourself`;
56
+ }
57
+ }
58
+ }
59
+ if (dirname(dir) === dir)
60
+ return undefined;
61
+ }
62
+ }
63
+ export const PackageJson = z.looseObject({
64
+ type: z.string().optional(),
65
+ scripts: z.record(z.string(), z.string()).optional(),
66
+ dependencies: z.record(z.string(), z.string()).optional(),
67
+ devDependencies: z.record(z.string(), z.string()).optional(),
68
+ });
69
+ /** Reads `package.json`, or an empty one when it does not exist. */
70
+ export function readPackageJson(root) {
71
+ const path = join(root, "package.json");
72
+ if (!existsSync(path))
73
+ return {};
74
+ try {
75
+ return PackageJson.parse(JSON.parse(readFileSync(path, "utf8")));
76
+ }
77
+ catch {
78
+ throw new CliError("package.json is not valid JSON; fix it and run the command again.");
79
+ }
80
+ }
81
+ //# sourceMappingURL=system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.js","sourceRoot":"","sources":["../../../src/cli/system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { ApiOf, Client, Manifest } from "../http/routes.ts";
2
+ /** Options for {@link createClient}. */
3
+ export interface ClientOptions {
4
+ /** Origin of the API, such as `https://api.example.com` or `http://127.0.0.1:3000`. */
5
+ baseUrl: string;
6
+ /** Headers for every request, such as `authorization`; a function is called per request. */
7
+ headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
8
+ /** The fetch to use. Default: the global `fetch`. */
9
+ fetch?: typeof fetch;
10
+ }
11
+ /** An app as the client sees it: its route manifest, plus its types. */
12
+ interface AppLike {
13
+ readonly manifest: Manifest;
14
+ readonly "~controllers"?: unknown;
15
+ }
16
+ /**
17
+ * A non-2xx response. Carries brisk's error body (`code`, `message`, `details`, `requestId`) when the server
18
+ * sent one, so callers branch on `error.code` instead of parsing messages.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * try {
23
+ * await api.users.create({ body });
24
+ * } catch (error) {
25
+ * if (error instanceof ApiError && error.code === "EMAIL_TAKEN") showEmailTaken();
26
+ * else throw error;
27
+ * }
28
+ * ```
29
+ */
30
+ export declare class ApiError extends Error {
31
+ readonly status: number;
32
+ readonly code: string;
33
+ readonly details: unknown;
34
+ readonly requestId: string | undefined;
35
+ /** The parsed response body, whatever its shape. */
36
+ readonly body: unknown;
37
+ constructor(status: number, body: unknown);
38
+ }
39
+ /**
40
+ * Creates a typed client for an app's routes: `api.users.get({ params: { id } })` resolves to the route's output
41
+ * and rejects with {@link ApiError} on a non-2xx response.
42
+ *
43
+ * In Node, pass the app or a running instance. In a browser, pass the manifest that `brisk client` writes,
44
+ * with the app's type, so the server's code never reaches the bundle.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * // web/src/api.ts
49
+ * import { createClient } from "@raiyyan/brisk/client";
50
+ * import type { Api } from "../../server/src/app.ts";
51
+ * import { manifest } from "./api.manifest.ts"; // generated by `brisk client`
52
+ *
53
+ * export const api = createClient<Api>(manifest, { baseUrl: import.meta.env.VITE_API_URL });
54
+ * const user = await api.users.get({ params: { id } });
55
+ * ```
56
+ */
57
+ export declare function createClient<A extends AppLike>(app: A, options: ClientOptions): Client<ApiOf<A>>;
58
+ export declare function createClient<T>(manifest: Manifest, options: ClientOptions): Client<ApiOf<T>>;
59
+ /** Fills `:name`, `*name` and optional `{...}` groups from `params`, encoding every segment. */
60
+ export declare function toPath(path: string, params: Record<string, unknown>): string;
61
+ /** `{ tag: ["a", "b"], page: 2 }` gives `?tag=a&tag=b&page=2`; `undefined` and `null` are left out. */
62
+ export declare function toQuery(query: Record<string, unknown> | undefined): string;
63
+ export {};
64
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEjE,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,uFAAuF;IACvF,OAAO,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACpG,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,wEAAwE;AACxE,UAAU,OAAO;IACf,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAEvB,YAAY,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EASxC;CACF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAClG,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAuE9F,gGAAgG;AAChG,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAY5E;AAED,uGAAuG;AACvG,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,CAU1E"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * A non-2xx response. Carries brisk's error body (`code`, `message`, `details`, `requestId`) when the server
3
+ * sent one, so callers branch on `error.code` instead of parsing messages.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * try {
8
+ * await api.users.create({ body });
9
+ * } catch (error) {
10
+ * if (error instanceof ApiError && error.code === "EMAIL_TAKEN") showEmailTaken();
11
+ * else throw error;
12
+ * }
13
+ * ```
14
+ */
15
+ export class ApiError extends Error {
16
+ status;
17
+ code;
18
+ details;
19
+ requestId;
20
+ /** The parsed response body, whatever its shape. */
21
+ body;
22
+ constructor(status, body) {
23
+ const error = errorBody(body);
24
+ super(error?.message ?? `Request failed with status ${status}`);
25
+ this.name = "ApiError";
26
+ this.status = status;
27
+ this.code = error?.code ?? "HTTP_ERROR";
28
+ this.details = error?.details;
29
+ this.requestId = error?.requestId;
30
+ this.body = body;
31
+ }
32
+ }
33
+ export function createClient(source, options) {
34
+ const manifest = isAppLike(source) ? source.manifest : source;
35
+ return build(manifest, options);
36
+ }
37
+ function build(manifest, options) {
38
+ const client = {};
39
+ for (const [name, entry] of Object.entries(manifest)) {
40
+ client[name] = isEndpoint(entry) ? endpoint(entry[0], entry[1], options) : build(entry, options);
41
+ }
42
+ return Object.freeze(client);
43
+ }
44
+ function endpoint(method, path, options) {
45
+ const send = options.fetch ?? fetch;
46
+ const base = options.baseUrl.replace(/\/+$/, "");
47
+ const request = async (input, init, raw) => {
48
+ const shared = typeof options.headers === "function" ? await options.headers() : options.headers;
49
+ const headers = new Headers(init.headers);
50
+ for (const [name, value] of Object.entries({ ...shared, ...input.headers }))
51
+ headers.set(name, value);
52
+ const body = encode(input.body, raw);
53
+ if (typeof body === "string" && !raw && !headers.has("content-type")) {
54
+ headers.set("content-type", "application/json");
55
+ }
56
+ return send(base + toPath(path, input.params ?? {}) + toQuery(input.query), {
57
+ ...init,
58
+ method,
59
+ headers,
60
+ body,
61
+ });
62
+ };
63
+ const call = async (input = {}, init = {}) => {
64
+ const res = await request(input, init, false);
65
+ if (!res.ok)
66
+ throw new ApiError(res.status, await readBody(res));
67
+ if (!isJson(res))
68
+ return res.status === 204 ? undefined : res;
69
+ return readBody(res);
70
+ };
71
+ return Object.assign(call, {
72
+ raw: (input = {}, init = {}) => request(input, init, true),
73
+ });
74
+ }
75
+ /**
76
+ * The body as `fetch` takes it. Files, forms, bytes and streams go as they are, so the browser sets their
77
+ * content type and multipart boundary; a string goes as it is only through `raw`; everything else is JSON.
78
+ */
79
+ function encode(body, raw) {
80
+ if (body === undefined)
81
+ return null;
82
+ if (body instanceof FormData ||
83
+ body instanceof Blob ||
84
+ body instanceof URLSearchParams ||
85
+ body instanceof ArrayBuffer ||
86
+ ArrayBuffer.isView(body) ||
87
+ body instanceof ReadableStream) {
88
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- each of these is a fetch request body
89
+ return body;
90
+ }
91
+ if (raw && typeof body === "string")
92
+ return body;
93
+ return JSON.stringify(body);
94
+ }
95
+ /** Fills `:name`, `*name` and optional `{...}` groups from `params`, encoding every segment. */
96
+ export function toPath(path, params) {
97
+ const filled = path.replace(/\{([^{}]*)\}/g, (_group, inner) => {
98
+ const names = [...inner.matchAll(/[:*]([\w$]+)/g)].map((match) => match[1] ?? "");
99
+ return names.every((name) => params[name] !== undefined) ? inner : "";
100
+ });
101
+ return filled.replace(/([:*])([\w$]+)/g, (_match, kind, name) => {
102
+ const value = params[name];
103
+ if (value === undefined)
104
+ throw new TypeError(`Missing path parameter "${name}" for ${path}`);
105
+ if (kind === "*" && Array.isArray(value))
106
+ return value.map((part) => encodeURIComponent(stringify(part))).join("/");
107
+ return encodeURIComponent(stringify(value));
108
+ });
109
+ }
110
+ /** `{ tag: ["a", "b"], page: 2 }` gives `?tag=a&tag=b&page=2`; `undefined` and `null` are left out. */
111
+ export function toQuery(query) {
112
+ if (!query)
113
+ return "";
114
+ const search = new URLSearchParams();
115
+ for (const [key, value] of Object.entries(query)) {
116
+ for (const item of Array.isArray(value) ? value : [value]) {
117
+ if (item !== undefined && item !== null)
118
+ search.append(key, stringify(item));
119
+ }
120
+ }
121
+ const text = search.toString();
122
+ return text === "" ? "" : `?${text}`;
123
+ }
124
+ /** A path or query value as text: primitives as themselves, a `Date` as ISO, anything else as JSON. */
125
+ function stringify(value) {
126
+ if (typeof value === "string")
127
+ return value;
128
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint")
129
+ return value.toString();
130
+ if (value instanceof Date)
131
+ return value.toISOString();
132
+ return JSON.stringify(value);
133
+ }
134
+ const JSON_TYPE = /^application\/(?:[\w.-]+\+)?json\b/i;
135
+ function isJson(res) {
136
+ return JSON_TYPE.test(res.headers.get("content-type") ?? "");
137
+ }
138
+ async function readBody(res) {
139
+ const text = await res.text();
140
+ if (text === "")
141
+ return undefined;
142
+ if (!isJson(res))
143
+ return text;
144
+ const parsed = JSON.parse(text);
145
+ return parsed;
146
+ }
147
+ function errorBody(body) {
148
+ if (typeof body !== "object" || body === null || !("error" in body))
149
+ return undefined;
150
+ const { error } = body;
151
+ if (typeof error !== "object" || error === null)
152
+ return undefined;
153
+ if (!("code" in error) || typeof error.code !== "string")
154
+ return undefined;
155
+ if (!("message" in error) || typeof error.message !== "string")
156
+ return undefined;
157
+ return {
158
+ code: error.code,
159
+ message: error.message,
160
+ details: "details" in error ? error.details : undefined,
161
+ requestId: "requestId" in error && typeof error.requestId === "string" ? error.requestId : undefined,
162
+ };
163
+ }
164
+ function isAppLike(source) {
165
+ return "manifest" in source && typeof source.manifest === "object" && !Array.isArray(source.manifest);
166
+ }
167
+ function isEndpoint(entry) {
168
+ return Array.isArray(entry);
169
+ }
170
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/client/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The typed client. Safe in browsers: it imports no server code, only types.
3
+ *
4
+ * @module
5
+ */
6
+ export { ApiError, createClient, type ClientOptions } from "./client.ts";
7
+ export type { ApiOf, Call, Client, Jsonify, Manifest, RawBody } from "../http/routes.ts";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACzE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The typed client. Safe in browsers: it imports no server code, only types.
3
+ *
4
+ * @module
5
+ */
6
+ export { ApiError, createClient } from "./client.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ /** Options for {@link defineEnv}. */
3
+ export interface DefineEnvOptions {
4
+ /** Dotenv file loaded into `process.env` when present. Default `.env`. */
5
+ file?: string;
6
+ /** An object to validate instead of `process.env`. Failures then throw instead of exiting. */
7
+ source?: Record<string, string | undefined>;
8
+ }
9
+ /**
10
+ * Validates the environment once, at import, and returns it as a frozen, typed object. Loads `.env` first when
11
+ * the file exists. On failure it prints every invalid variable and exits, so the app never starts
12
+ * half-configured; under `NODE_ENV=test`, or with `source`, it throws instead.
13
+ *
14
+ * Variables are strings: use `z.coerce.number()` for numbers and `z.stringbool()` for booleans. Never
15
+ * `z.coerce.boolean()`, which reads `"false"` as `true`.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * export const env = defineEnv({
20
+ * PORT: z.coerce.number().default(3000),
21
+ * DATABASE_URL: z.url(),
22
+ * LOG_LEVEL: z.enum(["debug", "info", "warn"]).default("info"),
23
+ * });
24
+ * env.PORT; // number
25
+ * ```
26
+ */
27
+ export declare function defineEnv<T extends z.ZodRawShape>(shape: T, options?: DefineEnvOptions): Readonly<z.output<z.ZodObject<T>>>;
28
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC7C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EAC/C,KAAK,EAAE,CAAC,EACR,OAAO,GAAE,gBAAqB,GAC7B,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAYpC"}
@@ -0,0 +1,35 @@
1
+ import { existsSync } from "node:fs";
2
+ import { z } from "zod";
3
+ /**
4
+ * Validates the environment once, at import, and returns it as a frozen, typed object. Loads `.env` first when
5
+ * the file exists. On failure it prints every invalid variable and exits, so the app never starts
6
+ * half-configured; under `NODE_ENV=test`, or with `source`, it throws instead.
7
+ *
8
+ * Variables are strings: use `z.coerce.number()` for numbers and `z.stringbool()` for booleans. Never
9
+ * `z.coerce.boolean()`, which reads `"false"` as `true`.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * export const env = defineEnv({
14
+ * PORT: z.coerce.number().default(3000),
15
+ * DATABASE_URL: z.url(),
16
+ * LOG_LEVEL: z.enum(["debug", "info", "warn"]).default("info"),
17
+ * });
18
+ * env.PORT; // number
19
+ * ```
20
+ */
21
+ export function defineEnv(shape, options = {}) {
22
+ const file = options.file ?? ".env";
23
+ if (!options.source && existsSync(file))
24
+ process.loadEnvFile(file);
25
+ const result = z.object(shape).safeParse(options.source ?? process.env);
26
+ if (result.success)
27
+ return Object.freeze(result.data);
28
+ const lines = result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`);
29
+ const message = `Invalid environment variables:\n${lines.join("\n")}`;
30
+ if (options.source || process.env["NODE_ENV"] === "test")
31
+ throw new Error(message);
32
+ console.error(`\n${message}\n\nCheck your .env file (see .env.example).\n`);
33
+ return process.exit(1);
34
+ }
35
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,SAAS,CACvB,KAAQ,EACR,OAAO,GAAqB,EAAE;IAE9B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC;IACpC,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAEnE,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IACxE,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAClG,MAAM,OAAO,GAAG,mCAAmC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IACtE,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IACnF,OAAO,CAAC,KAAK,CAAC,KAAK,OAAO,gDAAgD,CAAC,CAAC;IAC5E,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACzB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { type Logger as PinoLogger } from "pino";
2
+ /** A pino logger. In a handler, prefer `ctx.log`: it adds the request id to every line. */
3
+ export type Logger = PinoLogger;
4
+ /**
5
+ * The app's logger, as a token to inject. An app provides its `logger` option. A container you build yourself,
6
+ * as in a unit test, gets a silent logger, so services that log need no setup there.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * export class Mailer {
11
+ * readonly #log = inject(Logger);
12
+ * }
13
+ * ```
14
+ */
15
+ export declare const Logger: import("../di/container.ts").InjectionToken<Logger>;
16
+ /** Options for {@link createLogger}. */
17
+ export interface LoggerOptions {
18
+ /** The lowest level written: `trace`, `debug`, `info`, `warn`, `error`, `fatal` or `silent`. Default `info`. */
19
+ level?: string;
20
+ /** Colored, human-readable lines for local development. Needs the optional `pino-pretty` package. */
21
+ pretty?: boolean;
22
+ }
23
+ /**
24
+ * Creates a pino logger for an app's `logger` option: JSON lines, or pretty output in development.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * defineApp({ logger: createLogger({ level: env.LOG_LEVEL, pretty: env.NODE_ENV === "development" }) });
29
+ * ```
30
+ */
31
+ export declare function createLogger({ level, pretty }?: LoggerOptions): Logger;
32
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/config/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AAGvD,2FAA2F;AAC3F,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC;AAEhC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,qDAAiF,CAAC;AAErG,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,gHAAgH;IAChH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qGAAqG;IACrG,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,EAAE,KAAc,EAAE,MAAc,EAAE,GAAE,aAAkB,GAAG,MAAM,CAO3F"}