@adrianhall/cloudflare-toolkit 0.0.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/THIRD-PARTY-NOTICES.md +75 -0
  4. package/dist/cli/generate-wrangler-types/index.d.ts +1 -0
  5. package/dist/cli/generate-wrangler-types/index.js +329 -0
  6. package/dist/cli/generate-wrangler-types/index.js.map +1 -0
  7. package/dist/error-CLYcAvBM.d.ts +28 -0
  8. package/dist/errors/index.d.ts +2 -0
  9. package/dist/errors/index.js +3 -0
  10. package/dist/errors-Ciipq_zr.js +58 -0
  11. package/dist/errors-Ciipq_zr.js.map +1 -0
  12. package/dist/factory-BI5gVL_P.js +220 -0
  13. package/dist/factory-BI5gVL_P.js.map +1 -0
  14. package/dist/generators-D8WWEHa1.js +165 -0
  15. package/dist/generators-D8WWEHa1.js.map +1 -0
  16. package/dist/guards/index.d.ts +2 -0
  17. package/dist/guards/index.js +2 -0
  18. package/dist/guards-6K1CVAr5.js +61 -0
  19. package/dist/guards-6K1CVAr5.js.map +1 -0
  20. package/dist/hono/index.d.ts +347 -0
  21. package/dist/hono/index.js +307 -0
  22. package/dist/hono/index.js.map +1 -0
  23. package/dist/index-434HN8jN.d.ts +43 -0
  24. package/dist/index-Byl-ZrCy.d.ts +138 -0
  25. package/dist/index-CUICemFw.d.ts +129 -0
  26. package/dist/index-DRIhR-Xn.d.ts +81 -0
  27. package/dist/index.d.ts +8 -0
  28. package/dist/index.js +8 -0
  29. package/dist/jwt-BvuKtvby.js +328 -0
  30. package/dist/jwt-BvuKtvby.js.map +1 -0
  31. package/dist/logging/index.d.ts +3 -0
  32. package/dist/logging/index.js +3 -0
  33. package/dist/logging-CGHjOVLM.js +24 -0
  34. package/dist/logging-CGHjOVLM.js.map +1 -0
  35. package/dist/policy-CvS6AvvD.js +20 -0
  36. package/dist/policy-CvS6AvvD.js.map +1 -0
  37. package/dist/problem-details/index.d.ts +4 -0
  38. package/dist/problem-details/index.js +3 -0
  39. package/dist/problem-details-CuRsLy3Q.js +37 -0
  40. package/dist/problem-details-CuRsLy3Q.js.map +1 -0
  41. package/dist/silent-CWpHE65X.js +652 -0
  42. package/dist/silent-CWpHE65X.js.map +1 -0
  43. package/dist/testing/index.d.ts +44 -0
  44. package/dist/testing/index.js +2 -0
  45. package/dist/types-Cx6NNILW.d.ts +44 -0
  46. package/dist/types-DCSMb1cp.d.ts +195 -0
  47. package/dist/types-DXboaWOT.d.ts +29 -0
  48. package/dist/vite/index.d.ts +79 -0
  49. package/dist/vite/index.js +417 -0
  50. package/dist/vite/index.js.map +1 -0
  51. package/package.json +137 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adrian Hall
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @adrianhall/cloudflare-toolkit
2
+
3
+ A toolkit of utilities and skills for developing Workers on the Cloudflare Dev Platform.
4
+ `@adrianhall/cloudflare-toolkit` replaces the boilerplate historically scattered across
5
+ multiple GitHub-only libraries — defensive guards, HTTP error generators, RFC 9457 problem
6
+ details, structured logging, and Cloudflare Access-aware Hono/Vite middleware — with a single,
7
+ MIT-licensed, npm-installable package.
8
+
9
+ **Full documentation, guides, and API reference:**
10
+ https://adrianhall.github.io/cloudflare-toolkit
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ npm install @adrianhall/cloudflare-toolkit
16
+ ```
17
+
18
+ ## Quickstart
19
+
20
+ The `hono` subpath exports four independent middleware for a Cloudflare Access-protected Hono
21
+ app: structured logging, Access enforcement, and problem-details-based error handling. Each is
22
+ wired independently:
23
+
24
+ ```ts
25
+ import {
26
+ cloudflareAccess,
27
+ cloudflareLogger,
28
+ problemDetailsErrorHandler,
29
+ notFoundHandler
30
+ } from "@adrianhall/cloudflare-toolkit/hono";
31
+ import { Hono } from "hono";
32
+
33
+ const app = new Hono();
34
+
35
+ app.use(cloudflareLogger({/* ... */}));
36
+ app.use(cloudflareAccess({/* ... */}));
37
+
38
+ app.onError(problemDetailsErrorHandler());
39
+ app.notFound(notFoundHandler());
40
+
41
+ export default app;
42
+ ```
43
+
44
+ See the [documentation site](https://adrianhall.github.io/cloudflare-toolkit) for guides
45
+ covering every export, including the defensive guards, HTTP error generators, the standalone
46
+ logging core, the Vite plugin, and the `generate-wrangler-types` CLI.
47
+
48
+ ## AI Skill
49
+
50
+ An installable [Agent Skill](https://www.npmjs.com/package/skills) teaches coding agents how to
51
+ use every export in this package:
52
+
53
+ ```sh
54
+ npx skills add adrianhall/cloudflare-toolkit
55
+ ```
56
+
57
+ ## License
58
+
59
+ [MIT](./LICENSE)
@@ -0,0 +1,75 @@
1
+ # Third-Party Notices
2
+
3
+ This package vendors (copies source into this repository, rather than depending on) code from
4
+ third-party, MIT-licensed projects. This notice preserves the required copyright/license
5
+ attribution for that vendored code, per docs/specs/SPECv2.md §5.4.
6
+
7
+ ## `src/lib/problem-details/`
8
+
9
+ The RFC 9457 Problem Details core primitives under `src/lib/problem-details/` (`ProblemDetailsError`,
10
+ `problemDetails()`, `statusToPhrase`, `statusToSlug`, `createProblemTypeRegistry`, and the
11
+ `ProblemDetails`/`ProblemDetailsInput` types) are a vendored port of:
12
+
13
+ - [`adrianhall/hono-problem-details`](https://github.com/adrianhall/hono-problem-details) — MIT
14
+ License, Copyright (c) 2026 hono-problem-details contributors.
15
+ - Itself a fork of [`paveg/hono-problem-details`](https://github.com/paveg/hono-problem-details) —
16
+ MIT License, Copyright (c) 2026 hono-problem-details contributors.
17
+
18
+ This code is vendored — copied at a point in time — rather than depended upon, because
19
+ `adrianhall/hono-problem-details` is not published to npm and so cannot be a resolvable
20
+ `dependency`/`peerDependency` of a published package. Vendoring does not imply any ongoing
21
+ relationship with, or endorsement by, either upstream project. This notice is the authoritative
22
+ record of the vendored files' origin — the files themselves carry only a purpose-focused `@file`
23
+ comment, not a source-attribution header.
24
+
25
+ Only the Hono-free core primitives were ported. The `zod`/`valibot`/`openapi`/`standard-schema`/
26
+ `opentelemetry` integrations, and the Hono-specific `problemDetailsHandler`, were intentionally
27
+ **not** ported into this subpath — see docs/specs/SPECv2.md §5.4. The Hono-specific handler is instead
28
+ vendored separately, directly below.
29
+
30
+ ## `src/lib/hono/error-handler.ts`
31
+
32
+ The `problemDetailsErrorHandler` function is a vendored port of upstream's `problemDetailsHandler`
33
+ (renamed to match this toolkit's naming), from the same two projects and under the same MIT
34
+ license as above:
35
+
36
+ - [`adrianhall/hono-problem-details`](https://github.com/adrianhall/hono-problem-details)'s
37
+ `src/handler.ts` — MIT License, Copyright (c) 2026 hono-problem-details contributors.
38
+ - Itself a fork of [`paveg/hono-problem-details`](https://github.com/paveg/hono-problem-details) —
39
+ MIT License, Copyright (c) 2026 hono-problem-details contributors.
40
+
41
+ It is a **direct re-export**, not a toolkit-authored wrapper (docs/specs/SPECv2.md §5.4/§5.5, §9) — the
42
+ only change beyond the rename is dropping the `otelApi` option and its backing
43
+ `integrations/opentelemetry.ts` file, since the opentelemetry integration (along with
44
+ `zod`/`valibot`/`openapi`/`standard-schema`) is explicitly out of scope for v1 (docs/specs/SPECv2.md
45
+ §5.4). It shares the Hono-free primitives above (`statusToPhrase`, `buildProblemResponse`, etc.)
46
+ rather than duplicating them.
47
+
48
+ `notFoundHandler` in the same directory has no vendored equivalent — it is toolkit-authored (see
49
+ docs/specs/SPECv2.md §5.5) and is not covered by this notice.
50
+
51
+ ### MIT License text
52
+
53
+ ```text
54
+ MIT License
55
+
56
+ Copyright (c) 2026 hono-problem-details contributors
57
+
58
+ Permission is hereby granted, free of charge, to any person obtaining a copy
59
+ of this software and associated documentation files (the "Software"), to deal
60
+ in the Software without restriction, including without limitation the rights
61
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
62
+ copies of the Software, and to permit persons to whom the Software is
63
+ furnished to do so, subject to the following conditions:
64
+
65
+ The above copyright notice and this permission notice shall be included in all
66
+ copies or substantial portions of the Software.
67
+
68
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
69
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
70
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
71
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
72
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
73
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
74
+ SOFTWARE.
75
+ ```
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,329 @@
1
+ #!/usr/bin/env node
2
+ import process$1 from "node:process";
3
+ import { constants } from "node:fs";
4
+ import { access, stat } from "node:fs/promises";
5
+ import { isAbsolute, resolve } from "node:path";
6
+ import { Command, CommanderError } from "commander";
7
+ import chalk from "chalk";
8
+ import spawn from "cross-spawn";
9
+ //#region src/cli/generate-wrangler-types/fs.ts
10
+ /**
11
+ * @file A Node.js filesystem adapter for the `generate-wrangler-types` CLI. Wraps
12
+ * `node:fs/promises` behind the {@link FileSystem} interface so that file I/O can be swapped for
13
+ * an in-memory stub in unit tests without mocking the built-in module directly.
14
+ */
15
+ /**
16
+ * Creates a {@link FileSystem} backed by Node's `fs/promises` module.
17
+ *
18
+ * `fileExists` uses `access(F_OK)` and returns `true` for any accessible path.
19
+ * `getModifiedTime` uses `stat().mtimeMs` to retrieve the last-modified time.
20
+ *
21
+ * @returns A {@link FileSystem} implementation that queries real files.
22
+ */
23
+ function createFileSystem() {
24
+ return {
25
+ async fileExists(path) {
26
+ try {
27
+ await access(path, constants.F_OK);
28
+ return true;
29
+ } catch {
30
+ return false;
31
+ }
32
+ },
33
+ async getModifiedTime(path) {
34
+ return (await stat(path)).mtimeMs;
35
+ }
36
+ };
37
+ }
38
+ //#endregion
39
+ //#region src/cli/generate-wrangler-types/logger.ts
40
+ /**
41
+ * @file A private stderr logger for the `generate-wrangler-types` CLI. Not exported from any
42
+ * barrel — this logger is an internal implementation detail of this one CLI, not part of the
43
+ * toolkit's public API surface, and is intentionally a separate, simpler abstraction from the
44
+ * `logging` subpath's `Logger`/`Transport` contract (`src/lib/logging/types.ts`).
45
+ *
46
+ * The split is deliberate, not an oversight: this is a Node-only CLI concern (colored, leveled
47
+ * `stderr` output for a `bin` entry point) rather than the flagship Worker/browser structured
48
+ * logging core, and it is a verbatim port from `cloudflare-scripts`. See
49
+ * `docs/specs/SPECv2.md` §12.3 (ARCH-003) for the full rationale.
50
+ *
51
+ * All output is written to `stderr`. Color is applied when `process.stderr.isTTY === true`;
52
+ * otherwise output is plain text. Log line format: `<ISO-UTC-ms> [<level>] <message>`.
53
+ */
54
+ /** Numeric ordering used to compare {@link LogLevel} values. */
55
+ const LOG_LEVEL_ORDER = {
56
+ debug: 0,
57
+ info: 1,
58
+ warn: 2,
59
+ error: 3
60
+ };
61
+ /**
62
+ * Applies a chalk color to `line` based on the log level.
63
+ *
64
+ * - `debug` → blue
65
+ * - `info` → green
66
+ * - `warn` → yellow
67
+ * - `error` → red
68
+ *
69
+ * @param level - The log level that determines the color.
70
+ * @param line - The fully-formatted log line to colorize.
71
+ * @returns The colorized string.
72
+ */
73
+ function colorize(level, line) {
74
+ switch (level) {
75
+ case "debug": return chalk.blue(line);
76
+ case "info": return chalk.green(line);
77
+ case "warn": return chalk.yellow(line);
78
+ case "error": return chalk.red(line);
79
+ }
80
+ }
81
+ /**
82
+ * Creates the default {@link LogSink} that writes to `process.stderr`.
83
+ *
84
+ * Each line is formatted as `<ISO-UTC-ms> [<level>] <message>`. Color is applied when
85
+ * `process.stderr.isTTY === true`.
86
+ *
87
+ * @returns A `LogSink` that writes to `stderr`.
88
+ */
89
+ function createDefaultSink() {
90
+ const useColor = process.stderr.isTTY === true;
91
+ return (level, message) => {
92
+ const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${message}`;
93
+ const output = useColor ? colorize(level, line) : line;
94
+ process.stderr.write(`${output}\n`);
95
+ };
96
+ }
97
+ /**
98
+ * Creates a {@link Logger} that forwards messages at or above `options.level` to the configured
99
+ * sink.
100
+ *
101
+ * @param options - Logger configuration including the minimum level and an optional custom sink.
102
+ * @returns A {@link Logger} instance.
103
+ */
104
+ function createLogger(options) {
105
+ const { level, sink = createDefaultSink() } = options;
106
+ const minOrder = LOG_LEVEL_ORDER[level];
107
+ function log(msgLevel, message) {
108
+ if (LOG_LEVEL_ORDER[msgLevel] >= minOrder) sink(msgLevel, message);
109
+ }
110
+ return {
111
+ debug: (message) => log("debug", message),
112
+ info: (message) => log("info", message),
113
+ warn: (message) => log("warn", message),
114
+ error: (message) => log("error", message)
115
+ };
116
+ }
117
+ //#endregion
118
+ //#region src/cli/generate-wrangler-types/run.ts
119
+ /**
120
+ * @file Core orchestration logic for the `generate-wrangler-types` CLI.
121
+ *
122
+ * This module owns the full execution pipeline:
123
+ * 1. Argument parsing (Commander, with `--` passthrough for wrangler args)
124
+ * 2. Logger creation
125
+ * 3. Path resolution
126
+ * 4. Wrangler config existence check
127
+ * 5. Freshness check (compare config vs. output modification times)
128
+ * 6. `wrangler types` execution
129
+ *
130
+ * All external I/O is accessed through the injected {@link GenerateWranglerTypesDeps} interfaces,
131
+ * making the function fully testable without touching the real filesystem or spawning processes.
132
+ */
133
+ /**
134
+ * Extracts a human-readable message from an unknown `catch` value.
135
+ *
136
+ * Returns `err.message` when the value is an `Error`, otherwise stringifies it with `String()`.
137
+ *
138
+ * @param err - The caught value (may be anything).
139
+ * @returns A string suitable for log messages or user-facing error output.
140
+ */
141
+ function getErrorMessage(err) {
142
+ return err instanceof Error ? err.message : String(err);
143
+ }
144
+ /**
145
+ * Runs the `generate-wrangler-types` CLI pipeline and returns a POSIX exit code.
146
+ *
147
+ * The function is intentionally pure with respect to side effects: all I/O is delegated to
148
+ * `deps` so that every code path can be exercised in tests without spawning child processes or
149
+ * touching real files.
150
+ *
151
+ * Exit codes:
152
+ * | Code | Meaning |
153
+ * |------|---------|
154
+ * | `0` | Types are already fresh (skipped), or `wrangler types` succeeded |
155
+ * | `1` | Wrangler config file not found (run `provision` first) |
156
+ * | `2` | `wrangler` could not be executed (binary not on PATH / ENOENT) |
157
+ * | `3` | `wrangler types` exited with a non-zero code (code is logged) |
158
+ * | `6` | Argument error (`--verbose` + `--quiet`, unknown option) |
159
+ * | `99` | Unexpected internal error |
160
+ *
161
+ * @param argv - The raw `process.argv` array (first two elements are skipped by Commander).
162
+ * @param deps - Injected I/O dependencies.
163
+ * @returns A numeric exit code as listed above.
164
+ */
165
+ async function run(argv, deps) {
166
+ const { wrangler, logSink } = deps;
167
+ const fs = deps.fs;
168
+ const separatorIndex = argv.indexOf("--");
169
+ const commanderArgv = separatorIndex === -1 ? argv : argv.slice(0, separatorIndex);
170
+ const extraArgs = separatorIndex === -1 ? [] : argv.slice(separatorIndex + 1);
171
+ const program = new Command();
172
+ program.name("generate-wrangler-types").description("Regenerate worker-configuration.d.ts only when wrangler.jsonc has changed").version("0.0.1", "--version", "Print version and exit").option("-c, --config <file>", "Wrangler config file to watch", "wrangler.jsonc").option("-d, --dir <dir>", "Base directory for resolving relative paths", ".").option("-f, --force", "Force regeneration even if types are already fresh").option("-o, --output <file>", "Output .d.ts file path (relative to --dir)", "worker-configuration.d.ts").option("-q, --quiet", "Quiet logging (min level: warn)").option("-v, --verbose", "Verbose logging (min level: debug)").allowUnknownOption(false);
173
+ program.exitOverride();
174
+ try {
175
+ program.parse(commanderArgv);
176
+ } catch (err) {
177
+ if (err instanceof CommanderError) {
178
+ if (err.code === "commander.helpDisplayed" || err.code === "commander.version") return 0;
179
+ return 6;
180
+ }
181
+ process.stderr.write(`Internal error during argument parsing: ${String(err)}\n`);
182
+ return 99;
183
+ }
184
+ const opts = program.opts();
185
+ if (opts.verbose && opts.quiet) {
186
+ process.stderr.write("Error: --verbose (-v) and --quiet (-q) are mutually exclusive\n");
187
+ return 6;
188
+ }
189
+ const logger = createLogger({
190
+ level: opts.verbose ? "debug" : opts.quiet ? "warn" : "info",
191
+ sink: logSink
192
+ });
193
+ const baseDir = opts.dir;
194
+ const configPath = isAbsolute(opts.config) ? opts.config : resolve(baseDir, opts.config);
195
+ const outputPath = isAbsolute(opts.output) ? opts.output : resolve(baseDir, opts.output);
196
+ logger.debug(`Config path: ${configPath}`);
197
+ logger.debug(`Output path: ${outputPath}`);
198
+ if (extraArgs.length > 0) logger.debug(`Wrangler args: ${extraArgs.join(" ")}`);
199
+ if (!await fs.fileExists(configPath)) {
200
+ logger.error(`Wrangler config not found: ${configPath}\n Run \`npm run provision\` to provision infrastructure and generate it.`);
201
+ return 1;
202
+ }
203
+ if (!opts.force) if (await fs.fileExists(outputPath)) {
204
+ let configMtime;
205
+ let outputMtime;
206
+ try {
207
+ configMtime = await fs.getModifiedTime(configPath);
208
+ outputMtime = await fs.getModifiedTime(outputPath);
209
+ } catch (err) {
210
+ process.stderr.write(`Internal error reading file modification times: ${getErrorMessage(err)}\n`);
211
+ return 99;
212
+ }
213
+ if (outputMtime > configMtime) {
214
+ logger.debug(`Types are fresh (output newer than config by ${Math.round(outputMtime - configMtime)}ms) — skipping`);
215
+ return 0;
216
+ }
217
+ logger.info(`generate-types: ${opts.config} is newer than ${opts.output} — regenerating...`);
218
+ } else logger.info(`generate-types: ${opts.output} not found — generating...`);
219
+ else logger.debug("--force: skipping freshness check");
220
+ logger.debug(`Running: npx wrangler types ${outputPath}${extraArgs.length > 0 ? ` ${extraArgs.join(" ")}` : ""}`);
221
+ let result;
222
+ try {
223
+ result = await wrangler.runTypes(outputPath, extraArgs, resolve(baseDir));
224
+ } catch (err) {
225
+ logger.error(`Failed to launch wrangler: ${getErrorMessage(err)}`);
226
+ return 2;
227
+ }
228
+ if (result.stdout.trim().length > 0) for (const line of result.stdout.trimEnd().split("\n")) logger.debug(`wrangler: ${line}`);
229
+ if (result.stderr.trim().length > 0) for (const line of result.stderr.trimEnd().split("\n")) logger.debug(`wrangler (stderr): ${line}`);
230
+ const exitCode = result.exitCode ?? 1;
231
+ if (exitCode !== 0) {
232
+ logger.error(`wrangler types failed with exit code ${exitCode}`);
233
+ return 3;
234
+ }
235
+ logger.info(`Wrote ${outputPath}`);
236
+ return 0;
237
+ }
238
+ //#endregion
239
+ //#region src/cli/generate-wrangler-types/wrangler.ts
240
+ /**
241
+ * The default {@link ExecRunner} that spawns a real child process via `cross-spawn`.
242
+ *
243
+ * Captures stdout and stderr as strings. `cross-spawn` resolves Windows `.cmd`/`.bat` shims
244
+ * (e.g. `npx.cmd`) and safely quotes arguments for `cmd.exe` when unavoidable, without ever
245
+ * handing `command`/`args` to a shell as an unescaped, concatenated string (SEC-002).
246
+ *
247
+ * Exported for direct testing of the real spawn path.
248
+ *
249
+ * @param command - The executable to spawn (always `"npx"` in practice).
250
+ * @param args - Arguments passed to `command`.
251
+ * @param options - Options forwarded to `cross-spawn`.
252
+ * @param options.cwd - Working directory for the spawned process.
253
+ * @returns The process result including exit code and captured output.
254
+ * @throws If the process cannot be spawned (e.g. ENOENT).
255
+ */
256
+ async function defaultExecRunner(command, args, options) {
257
+ return new Promise((resolve, reject) => {
258
+ const child = spawn(command, args, {
259
+ cwd: options.cwd,
260
+ stdio: [
261
+ "ignore",
262
+ "pipe",
263
+ "pipe"
264
+ ]
265
+ });
266
+ let stdout = "";
267
+ let stderr = "";
268
+ child.stdout.on("data", (chunk) => {
269
+ stdout += chunk.toString();
270
+ });
271
+ child.stderr.on("data", (chunk) => {
272
+ stderr += chunk.toString();
273
+ });
274
+ child.on("error", (err) => {
275
+ reject(err);
276
+ });
277
+ child.on("close", (code) => {
278
+ resolve({
279
+ exitCode: code,
280
+ stdout,
281
+ stderr
282
+ });
283
+ });
284
+ });
285
+ }
286
+ /**
287
+ * Creates a {@link WranglerRunner} that executes `npx wrangler types` as a real child process.
288
+ *
289
+ * @param execRunner - Optional override for the process spawner. When omitted, the default
290
+ * `cross-spawn`-backed wrapper is used. Inject a stub in tests.
291
+ * @returns A {@link WranglerRunner} implementation.
292
+ */
293
+ function createWranglerRunner(execRunner) {
294
+ const exec = execRunner ?? defaultExecRunner;
295
+ return { async runTypes(outputPath, extraArgs, cwd) {
296
+ const args = [
297
+ "wrangler",
298
+ "types",
299
+ outputPath,
300
+ ...extraArgs
301
+ ];
302
+ return exec("npx", args, { cwd });
303
+ } };
304
+ }
305
+ //#endregion
306
+ //#region src/cli/generate-wrangler-types/index.ts
307
+ /**
308
+ * @file Entry point for the `generate-wrangler-types` CLI binary.
309
+ *
310
+ * Wires together the real filesystem adapter and Wrangler runner, then delegates to {@link run}
311
+ * which owns all argument parsing and business logic. The process exits with the numeric code
312
+ * returned by `run`.
313
+ *
314
+ * The shebang above is preserved verbatim by tsdown in the built
315
+ * `dist/cli/generate-wrangler-types/index.js` so that `package.json#bin` resolves to a directly
316
+ * executable file once npm links it.
317
+ *
318
+ * This file is a thin wiring shim around `run()`, which is fully covered by
319
+ * `test/node/cli/generate-wrangler-types/run.test.ts`, and is excluded from coverage thresholds.
320
+ */
321
+ const exitCode = await run(process$1.argv, {
322
+ wrangler: createWranglerRunner(),
323
+ fs: createFileSystem()
324
+ });
325
+ process$1.exit(exitCode);
326
+ //#endregion
327
+ export {};
328
+
329
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["process"],"sources":["../../../src/cli/generate-wrangler-types/fs.ts","../../../src/cli/generate-wrangler-types/logger.ts","../../../src/cli/generate-wrangler-types/run.ts","../../../src/cli/generate-wrangler-types/wrangler.ts","../../../src/cli/generate-wrangler-types/index.ts"],"sourcesContent":["/**\n * @file A Node.js filesystem adapter for the `generate-wrangler-types` CLI. Wraps\n * `node:fs/promises` behind the {@link FileSystem} interface so that file I/O can be swapped for\n * an in-memory stub in unit tests without mocking the built-in module directly.\n */\nimport { constants } from \"node:fs\";\nimport { access, stat } from \"node:fs/promises\";\nimport type { FileSystem } from \"./types.js\";\n\n/**\n * Creates a {@link FileSystem} backed by Node's `fs/promises` module.\n *\n * `fileExists` uses `access(F_OK)` and returns `true` for any accessible path.\n * `getModifiedTime` uses `stat().mtimeMs` to retrieve the last-modified time.\n *\n * @returns A {@link FileSystem} implementation that queries real files.\n */\nexport function createFileSystem(): FileSystem {\n return {\n async fileExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n },\n\n async getModifiedTime(path: string): Promise<number> {\n const s = await stat(path);\n return s.mtimeMs;\n }\n };\n}\n","/**\n * @file A private stderr logger for the `generate-wrangler-types` CLI. Not exported from any\n * barrel — this logger is an internal implementation detail of this one CLI, not part of the\n * toolkit's public API surface, and is intentionally a separate, simpler abstraction from the\n * `logging` subpath's `Logger`/`Transport` contract (`src/lib/logging/types.ts`).\n *\n * The split is deliberate, not an oversight: this is a Node-only CLI concern (colored, leveled\n * `stderr` output for a `bin` entry point) rather than the flagship Worker/browser structured\n * logging core, and it is a verbatim port from `cloudflare-scripts`. See\n * `docs/specs/SPECv2.md` §12.3 (ARCH-003) for the full rationale.\n *\n * All output is written to `stderr`. Color is applied when `process.stderr.isTTY === true`;\n * otherwise output is plain text. Log line format: `<ISO-UTC-ms> [<level>] <message>`.\n */\nimport chalk from \"chalk\";\n\n/**\n * The set of supported log levels, ordered from least to most severe.\n *\n * - `debug` — verbose diagnostic output (enabled with `-v`)\n * - `info` — normal operational messages (default)\n * - `warn` — non-fatal anomalies\n * - `error` — failures that halt or degrade operation\n */\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\n/**\n * A low-level output function that receives a fully-resolved log record.\n *\n * The default sink writes colored (or plain) timestamped lines to `stderr`. Tests inject an\n * in-memory sink to capture and assert on log output without printing to the console.\n *\n * @param level - The severity level of the message.\n * @param message - The plain-text message body.\n */\nexport type LogSink = (level: LogLevel, message: string) => void;\n\n/**\n * A structured logger with one method per {@link LogLevel}.\n *\n * Messages below the configured minimum level are silently dropped.\n */\nexport interface Logger {\n /** Emit a `debug`-level message. Dropped unless the logger level is `debug`. */\n debug(message: string): void;\n /** Emit an `info`-level message. */\n info(message: string): void;\n /** Emit a `warn`-level message. */\n warn(message: string): void;\n /** Emit an `error`-level message. */\n error(message: string): void;\n}\n\n/**\n * Options accepted by {@link createLogger}.\n */\nexport interface LoggerOptions {\n /** The minimum severity level that will be forwarded to the sink. */\n level: LogLevel;\n /**\n * Optional custom sink. Defaults to a stderr writer that applies chalk colors when\n * `process.stderr.isTTY` is `true`.\n */\n sink?: LogSink;\n}\n\n/** Numeric ordering used to compare {@link LogLevel} values. */\nconst LOG_LEVEL_ORDER: Readonly<Record<LogLevel, number>> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3\n};\n\n/**\n * Applies a chalk color to `line` based on the log level.\n *\n * - `debug` → blue\n * - `info` → green\n * - `warn` → yellow\n * - `error` → red\n *\n * @param level - The log level that determines the color.\n * @param line - The fully-formatted log line to colorize.\n * @returns The colorized string.\n */\nfunction colorize(level: LogLevel, line: string): string {\n switch (level) {\n case \"debug\":\n return chalk.blue(line);\n case \"info\":\n return chalk.green(line);\n case \"warn\":\n return chalk.yellow(line);\n case \"error\":\n return chalk.red(line);\n }\n}\n\n/**\n * Creates the default {@link LogSink} that writes to `process.stderr`.\n *\n * Each line is formatted as `<ISO-UTC-ms> [<level>] <message>`. Color is applied when\n * `process.stderr.isTTY === true`.\n *\n * @returns A `LogSink` that writes to `stderr`.\n */\nfunction createDefaultSink(): LogSink {\n const useColor = process.stderr.isTTY === true;\n return (level: LogLevel, message: string): void => {\n const timestamp = new Date().toISOString();\n const line = `${timestamp} [${level}] ${message}`;\n const output = useColor ? colorize(level, line) : line;\n process.stderr.write(`${output}\\n`);\n };\n}\n\n/**\n * Creates a {@link Logger} that forwards messages at or above `options.level` to the configured\n * sink.\n *\n * @param options - Logger configuration including the minimum level and an optional custom sink.\n * @returns A {@link Logger} instance.\n */\nexport function createLogger(options: LoggerOptions): Logger {\n const { level, sink = createDefaultSink() } = options;\n const minOrder = LOG_LEVEL_ORDER[level];\n\n function log(msgLevel: LogLevel, message: string): void {\n if (LOG_LEVEL_ORDER[msgLevel] >= minOrder) {\n sink(msgLevel, message);\n }\n }\n\n return {\n debug: (message) => log(\"debug\", message),\n info: (message) => log(\"info\", message),\n warn: (message) => log(\"warn\", message),\n error: (message) => log(\"error\", message)\n };\n}\n","/**\n * @file Core orchestration logic for the `generate-wrangler-types` CLI.\n *\n * This module owns the full execution pipeline:\n * 1. Argument parsing (Commander, with `--` passthrough for wrangler args)\n * 2. Logger creation\n * 3. Path resolution\n * 4. Wrangler config existence check\n * 5. Freshness check (compare config vs. output modification times)\n * 6. `wrangler types` execution\n *\n * All external I/O is accessed through the injected {@link GenerateWranglerTypesDeps} interfaces,\n * making the function fully testable without touching the real filesystem or spawning processes.\n */\nimport { isAbsolute, resolve } from \"node:path\";\nimport { Command, CommanderError } from \"commander\";\nimport type { LogLevel, LogSink } from \"./logger.js\";\nimport { createLogger } from \"./logger.js\";\nimport type { FileSystem, WranglerRunner } from \"./types.js\";\n\n// CLI_VERSION is replaced at build time by tsdown's `define` option (tsdown.config.ts).\ndeclare const CLI_VERSION: string;\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extracts a human-readable message from an unknown `catch` value.\n *\n * Returns `err.message` when the value is an `Error`, otherwise stringifies it with `String()`.\n *\n * @param err - The caught value (may be anything).\n * @returns A string suitable for log messages or user-facing error output.\n */\nfunction getErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * External dependencies injected into {@link run} to keep I/O decoupled from business logic and\n * facilitate unit testing.\n */\nexport interface GenerateWranglerTypesDeps {\n /** Wrangler CLI adapter used to execute `wrangler types`. */\n wrangler: WranglerRunner;\n /** Filesystem adapter used for existence and modification time checks. */\n fs: FileSystem;\n /** If provided, used as the logger sink instead of the default stderr writer. */\n logSink?: LogSink;\n}\n\n// ---------------------------------------------------------------------------\n// Core logic (exported for testing)\n// ---------------------------------------------------------------------------\n\n/**\n * Runs the `generate-wrangler-types` CLI pipeline and returns a POSIX exit code.\n *\n * The function is intentionally pure with respect to side effects: all I/O is delegated to\n * `deps` so that every code path can be exercised in tests without spawning child processes or\n * touching real files.\n *\n * Exit codes:\n * | Code | Meaning |\n * |------|---------|\n * | `0` | Types are already fresh (skipped), or `wrangler types` succeeded |\n * | `1` | Wrangler config file not found (run `provision` first) |\n * | `2` | `wrangler` could not be executed (binary not on PATH / ENOENT) |\n * | `3` | `wrangler types` exited with a non-zero code (code is logged) |\n * | `6` | Argument error (`--verbose` + `--quiet`, unknown option) |\n * | `99` | Unexpected internal error |\n *\n * @param argv - The raw `process.argv` array (first two elements are skipped by Commander).\n * @param deps - Injected I/O dependencies.\n * @returns A numeric exit code as listed above.\n */\nexport async function run(argv: string[], deps: GenerateWranglerTypesDeps): Promise<number> {\n const { wrangler, logSink } = deps;\n const fs = deps.fs;\n\n // -------------------------------------------------------------------------\n // Step 1 — Parse arguments\n //\n // Pre-split argv on `--` so that everything after the separator is forwarded verbatim to\n // `wrangler types`, independently of Commander's passthrough handling. Commander only ever\n // sees the first segment.\n // -------------------------------------------------------------------------\n const separatorIndex = argv.indexOf(\"--\");\n const commanderArgv = separatorIndex === -1 ? argv : argv.slice(0, separatorIndex);\n // Everything after `--` is forwarded verbatim to `wrangler types`.\n const extraArgs = separatorIndex === -1 ? [] : argv.slice(separatorIndex + 1);\n\n const program = new Command();\n program\n .name(\"generate-wrangler-types\")\n .description(\"Regenerate worker-configuration.d.ts only when wrangler.jsonc has changed\")\n .version(CLI_VERSION, \"--version\", \"Print version and exit\")\n .option(\"-c, --config <file>\", \"Wrangler config file to watch\", \"wrangler.jsonc\")\n .option(\"-d, --dir <dir>\", \"Base directory for resolving relative paths\", \".\")\n .option(\"-f, --force\", \"Force regeneration even if types are already fresh\")\n .option(\n \"-o, --output <file>\",\n \"Output .d.ts file path (relative to --dir)\",\n \"worker-configuration.d.ts\"\n )\n .option(\"-q, --quiet\", \"Quiet logging (min level: warn)\")\n .option(\"-v, --verbose\", \"Verbose logging (min level: debug)\")\n .allowUnknownOption(false);\n\n program.exitOverride();\n\n try {\n program.parse(commanderArgv);\n } catch (err: unknown) {\n if (err instanceof CommanderError) {\n // --help and --version exit with code 0 after printing.\n if (err.code === \"commander.helpDisplayed\" || err.code === \"commander.version\") {\n return 0;\n }\n // All other parse errors are argument problems → exit 6.\n return 6;\n }\n // Unexpected error during parse.\n process.stderr.write(`Internal error during argument parsing: ${String(err)}\\n`);\n return 99;\n }\n\n const opts = program.opts<{\n config: string;\n dir: string;\n force?: boolean;\n output: string;\n quiet?: boolean;\n verbose?: boolean;\n }>();\n\n // -------------------------------------------------------------------------\n // Step 2 — Check -v / -q conflict\n // -------------------------------------------------------------------------\n if (opts.verbose && opts.quiet) {\n process.stderr.write(\"Error: --verbose (-v) and --quiet (-q) are mutually exclusive\\n\");\n return 6;\n }\n\n // Create the logger now that we know the level.\n const level: LogLevel =\n opts.verbose ? \"debug\"\n : opts.quiet ? \"warn\"\n : \"info\";\n const logger = createLogger({ level, sink: logSink });\n\n // -------------------------------------------------------------------------\n // Step 3 — Resolve paths\n // -------------------------------------------------------------------------\n const baseDir = opts.dir;\n\n const configPath = isAbsolute(opts.config) ? opts.config : resolve(baseDir, opts.config);\n\n const outputPath = isAbsolute(opts.output) ? opts.output : resolve(baseDir, opts.output);\n\n logger.debug(`Config path: ${configPath}`);\n logger.debug(`Output path: ${outputPath}`);\n if (extraArgs.length > 0) {\n logger.debug(`Wrangler args: ${extraArgs.join(\" \")}`);\n }\n\n // -------------------------------------------------------------------------\n // Step 4 — Check config exists\n // -------------------------------------------------------------------------\n const configExists = await fs.fileExists(configPath);\n if (!configExists) {\n logger.error(\n `Wrangler config not found: ${configPath}\\n`\n + \" Run `npm run provision` to provision infrastructure and generate it.\"\n );\n return 1;\n }\n\n // -------------------------------------------------------------------------\n // Step 5 — Freshness check (skip if output is newer than config)\n // -------------------------------------------------------------------------\n if (!opts.force) {\n const outputExists = await fs.fileExists(outputPath);\n if (outputExists) {\n let configMtime: number;\n let outputMtime: number;\n try {\n configMtime = await fs.getModifiedTime(configPath);\n outputMtime = await fs.getModifiedTime(outputPath);\n } catch (err: unknown) {\n process.stderr.write(\n `Internal error reading file modification times: ${getErrorMessage(err)}\\n`\n );\n return 99;\n }\n\n if (outputMtime > configMtime) {\n logger.debug(\n `Types are fresh (output newer than config by ${Math.round(outputMtime - configMtime)}ms) — skipping`\n );\n return 0;\n }\n\n logger.info(`generate-types: ${opts.config} is newer than ${opts.output} — regenerating...`);\n } else {\n logger.info(`generate-types: ${opts.output} not found — generating...`);\n }\n } else {\n logger.debug(\"--force: skipping freshness check\");\n }\n\n // -------------------------------------------------------------------------\n // Step 6 — Run wrangler types\n // -------------------------------------------------------------------------\n logger.debug(\n `Running: npx wrangler types ${outputPath}${extraArgs.length > 0 ? ` ${extraArgs.join(\" \")}` : \"\"}`\n );\n\n let result: Awaited<ReturnType<WranglerRunner[\"runTypes\"]>>;\n try {\n result = await wrangler.runTypes(outputPath, extraArgs, resolve(baseDir));\n } catch (err: unknown) {\n logger.error(`Failed to launch wrangler: ${getErrorMessage(err)}`);\n return 2;\n }\n\n // Log any captured wrangler output at debug level.\n if (result.stdout.trim().length > 0) {\n for (const line of result.stdout.trimEnd().split(\"\\n\")) {\n logger.debug(`wrangler: ${line}`);\n }\n }\n if (result.stderr.trim().length > 0) {\n for (const line of result.stderr.trimEnd().split(\"\\n\")) {\n logger.debug(`wrangler (stderr): ${line}`);\n }\n }\n\n const exitCode = result.exitCode ?? 1;\n\n if (exitCode !== 0) {\n logger.error(`wrangler types failed with exit code ${exitCode}`);\n return 3;\n }\n\n logger.info(`Wrote ${outputPath}`);\n return 0;\n}\n","/**\n * @file A Wrangler CLI adapter for the `generate-wrangler-types` CLI. Wraps the execution of\n * `npx wrangler types` behind the {@link WranglerRunner} interface so that tests can substitute\n * a stub without spawning a real process.\n *\n * The real implementation spawns `npx wrangler types <outputPath> [extraArgs]` in the given\n * working directory and captures stdout/stderr for logging.\n *\n * `outputPath` (attacker-influenceable via `-o/--output`) and `extraArgs` (everything after a\n * `--` separator, forwarded verbatim) are never passed to a shell for interpretation — see\n * SEC-002 (https://github.com/adrianhall/cloudflare-toolkit/issues/47). Process spawning goes\n * through `cross-spawn` instead of `node:child_process.spawn`'s own `shell: true` option: on\n * POSIX it spawns the target binary directly with no shell involved at all, and on Windows it\n * resolves the `npx`/`wrangler` shim's actual `.cmd` file and safely quotes each argument for\n * `cmd.exe` itself, rather than handing it a single, unescaped, attacker-influenceable command\n * line. A value like `--output \"x;rm -rf ~\"` is therefore passed through as one literal argv\n * element, not interpreted as shell syntax.\n */\nimport type { ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport spawn from \"cross-spawn\";\nimport type { WranglerResult, WranglerRunner } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// ExecRunner abstraction\n// ---------------------------------------------------------------------------\n\n/**\n * A function that spawns a child process and returns its result. Injectable so tests can stub\n * process execution without real spawning.\n */\nexport type ExecRunner = (\n command: string,\n args: string[],\n options: { cwd: string }\n) => Promise<WranglerResult>;\n\n// ---------------------------------------------------------------------------\n// Default real implementation\n// ---------------------------------------------------------------------------\n\n/**\n * The default {@link ExecRunner} that spawns a real child process via `cross-spawn`.\n *\n * Captures stdout and stderr as strings. `cross-spawn` resolves Windows `.cmd`/`.bat` shims\n * (e.g. `npx.cmd`) and safely quotes arguments for `cmd.exe` when unavoidable, without ever\n * handing `command`/`args` to a shell as an unescaped, concatenated string (SEC-002).\n *\n * Exported for direct testing of the real spawn path.\n *\n * @param command - The executable to spawn (always `\"npx\"` in practice).\n * @param args - Arguments passed to `command`.\n * @param options - Options forwarded to `cross-spawn`.\n * @param options.cwd - Working directory for the spawned process.\n * @returns The process result including exit code and captured output.\n * @throws If the process cannot be spawned (e.g. ENOENT).\n */\nexport async function defaultExecRunner(\n command: string,\n args: string[],\n options: { cwd: string }\n): Promise<WranglerResult> {\n return new Promise((resolve, reject) => {\n // `cross-spawn`'s type declarations expose the generic `child_process.SpawnOptions`\n // signature rather than Node's own stdio-tuple-narrowed overloads, so TypeScript sees\n // `stdout`/`stderr` below as possibly `null`. The `[\"ignore\", \"pipe\", \"pipe\"]` tuple\n // guarantees both are real `Readable` streams at runtime — Node's own `spawn`\n // implementation (which `cross-spawn` delegates to) never omits a stream for a `\"pipe\"`\n // stdio element — so this cast restores that guarantee for the type checker.\n const child = spawn(command, args, {\n cwd: options.cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"]\n }) as ChildProcessWithoutNullStreams;\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n\n child.stderr.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n\n child.on(\"error\", (err: Error) => {\n reject(err);\n });\n\n child.on(\"close\", (code: number | null) => {\n resolve({ exitCode: code, stdout, stderr });\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a {@link WranglerRunner} that executes `npx wrangler types` as a real child process.\n *\n * @param execRunner - Optional override for the process spawner. When omitted, the default\n * `cross-spawn`-backed wrapper is used. Inject a stub in tests.\n * @returns A {@link WranglerRunner} implementation.\n */\nexport function createWranglerRunner(execRunner?: ExecRunner): WranglerRunner {\n const exec = execRunner ?? defaultExecRunner;\n\n return {\n async runTypes(outputPath: string, extraArgs: string[], cwd: string): Promise<WranglerResult> {\n const args = [\"wrangler\", \"types\", outputPath, ...extraArgs];\n return exec(\"npx\", args, { cwd });\n }\n };\n}\n","#!/usr/bin/env node\n/// <reference types=\"node\" />\n/**\n * @file Entry point for the `generate-wrangler-types` CLI binary.\n *\n * Wires together the real filesystem adapter and Wrangler runner, then delegates to {@link run}\n * which owns all argument parsing and business logic. The process exits with the numeric code\n * returned by `run`.\n *\n * The shebang above is preserved verbatim by tsdown in the built\n * `dist/cli/generate-wrangler-types/index.js` so that `package.json#bin` resolves to a directly\n * executable file once npm links it.\n *\n * This file is a thin wiring shim around `run()`, which is fully covered by\n * `test/node/cli/generate-wrangler-types/run.test.ts`, and is excluded from coverage thresholds.\n */\nimport process from \"node:process\";\nimport { createFileSystem } from \"./fs.js\";\nimport { run } from \"./run.js\";\nimport { createWranglerRunner } from \"./wrangler.js\";\n\nconst exitCode = await run(process.argv, {\n wrangler: createWranglerRunner(),\n fs: createFileSystem()\n});\nprocess.exit(exitCode);\n"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { n as ProblemDetailsInput, t as ProblemDetails } from "./types-Cx6NNILW.js";
2
+ //#region src/lib/problem-details/error.d.ts
3
+ /**
4
+ * Error class for RFC 9457 Problem Details.
5
+ * Thrown by {@link problemDetails} and caught by the Hono error handler (`@adrianhall/cloudflare-toolkit/hono`)
6
+ * to produce `application/problem+json` responses.
7
+ */
8
+ declare class ProblemDetailsError extends Error {
9
+ /** The normalized RFC 9457 Problem Details object. */
10
+ readonly problemDetails: ProblemDetails;
11
+ /**
12
+ * Create a new {@link ProblemDetailsError}.
13
+ *
14
+ * @param input - Missing `type` defaults to `"about:blank"`; missing `title` is derived from
15
+ * the HTTP status code.
16
+ */
17
+ constructor(input: ProblemDetailsInput);
18
+ /**
19
+ * Build a standalone `application/problem+json` {@link Response} without any Hono handler
20
+ * middleware.
21
+ *
22
+ * @returns The resulting `Response`.
23
+ */
24
+ getResponse(): Response;
25
+ }
26
+ //#endregion
27
+ export { ProblemDetailsError as t };
28
+ //# sourceMappingURL=error-CLYcAvBM.d.ts.map
@@ -0,0 +1,2 @@
1
+ import { a as forbidden, c as methodNotAllowed, d as serviceUnavailable, f as unauthorized, i as contentTooLarge, l as notFound, m as unsupportedMediaType, n as InvalidShapeError, o as gone, p as unprocessableContent, r as badRequest, s as internalServerError, t as NullError, u as notImplemented } from "../index-CUICemFw.js";
2
+ export { InvalidShapeError, NullError, badRequest, contentTooLarge, forbidden, gone, internalServerError, methodNotAllowed, notFound, notImplemented, serviceUnavailable, unauthorized, unprocessableContent, unsupportedMediaType };
@@ -0,0 +1,3 @@
1
+ import { a as internalServerError, c as notImplemented, d as unprocessableContent, f as unsupportedMediaType, i as gone, l as serviceUnavailable, n as contentTooLarge, o as methodNotAllowed, r as forbidden, s as notFound, t as badRequest, u as unauthorized } from "../generators-D8WWEHa1.js";
2
+ import { n as InvalidShapeError, t as NullError } from "../errors-Ciipq_zr.js";
3
+ export { InvalidShapeError, NullError, badRequest, contentTooLarge, forbidden, gone, internalServerError, methodNotAllowed, notFound, notImplemented, serviceUnavailable, unauthorized, unprocessableContent, unsupportedMediaType };
@@ -0,0 +1,58 @@
1
+ import { n as ProblemDetailsError } from "./factory-BI5gVL_P.js";
2
+ import "./generators-D8WWEHa1.js";
3
+ //#region src/lib/errors/invalid-shape-error.ts
4
+ /**
5
+ * @file A specialized internal-server-error class for a non-null value that does not have the
6
+ * shape it was expected to have.
7
+ */
8
+ /**
9
+ * A specialized `internalServerError()`-shaped {@link ProblemDetailsError} for a non-null value
10
+ * that does not have the shape it was expected to have (e.g. not an object, or a property that is
11
+ * missing or the wrong type). Exists as a distinct, named error class — separate from `NullError`
12
+ * — so guard functions (`sqlCount`) have a single, greppable call site for "wrong shape" failures
13
+ * as opposed to "unexpectedly null/undefined" ones. Still handled uniformly by
14
+ * `problemDetailsErrorHandler` because it remains a `ProblemDetailsError`.
15
+ */
16
+ var InvalidShapeError = class extends ProblemDetailsError {
17
+ /**
18
+ * Create a new {@link InvalidShapeError}.
19
+ *
20
+ * @param message - Human-readable explanation of what had an unexpected shape or type.
21
+ */
22
+ constructor(message) {
23
+ super({
24
+ status: 500,
25
+ detail: message
26
+ });
27
+ this.name = "InvalidShapeError";
28
+ }
29
+ };
30
+ //#endregion
31
+ //#region src/lib/errors/null-error.ts
32
+ /**
33
+ * @file A specialized internal-server-error class for an unexpected `null`/`undefined` value.
34
+ */
35
+ /**
36
+ * A specialized `internalServerError()`-shaped {@link ProblemDetailsError} for an unexpected
37
+ * `null`/`undefined` value. Exists as a distinct, named error class so guard functions
38
+ * (`throwIfNull`, `sqlCount`) have a single, greppable call site — it is still handled uniformly
39
+ * by `problemDetailsErrorHandler` because it remains a `ProblemDetailsError`.
40
+ */
41
+ var NullError = class extends ProblemDetailsError {
42
+ /**
43
+ * Create a new {@link NullError}.
44
+ *
45
+ * @param message - Human-readable explanation of what was unexpectedly null/undefined.
46
+ */
47
+ constructor(message) {
48
+ super({
49
+ status: 500,
50
+ detail: message
51
+ });
52
+ this.name = "NullError";
53
+ }
54
+ };
55
+ //#endregion
56
+ export { InvalidShapeError as n, NullError as t };
57
+
58
+ //# sourceMappingURL=errors-Ciipq_zr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-Ciipq_zr.js","names":[],"sources":["../src/lib/errors/invalid-shape-error.ts","../src/lib/errors/null-error.ts"],"sourcesContent":["/**\n * @file A specialized internal-server-error class for a non-null value that does not have the\n * shape it was expected to have.\n */\nimport { ProblemDetailsError } from \"../problem-details/error.js\";\n\n/**\n * A specialized `internalServerError()`-shaped {@link ProblemDetailsError} for a non-null value\n * that does not have the shape it was expected to have (e.g. not an object, or a property that is\n * missing or the wrong type). Exists as a distinct, named error class — separate from `NullError`\n * — so guard functions (`sqlCount`) have a single, greppable call site for \"wrong shape\" failures\n * as opposed to \"unexpectedly null/undefined\" ones. Still handled uniformly by\n * `problemDetailsErrorHandler` because it remains a `ProblemDetailsError`.\n */\nexport class InvalidShapeError extends ProblemDetailsError {\n /**\n * Create a new {@link InvalidShapeError}.\n *\n * @param message - Human-readable explanation of what had an unexpected shape or type.\n */\n constructor(message: string) {\n super({ status: 500, detail: message });\n this.name = \"InvalidShapeError\";\n }\n}\n","/**\n * @file A specialized internal-server-error class for an unexpected `null`/`undefined` value.\n */\nimport { ProblemDetailsError } from \"../problem-details/error.js\";\n\n/**\n * A specialized `internalServerError()`-shaped {@link ProblemDetailsError} for an unexpected\n * `null`/`undefined` value. Exists as a distinct, named error class so guard functions\n * (`throwIfNull`, `sqlCount`) have a single, greppable call site — it is still handled uniformly\n * by `problemDetailsErrorHandler` because it remains a `ProblemDetailsError`.\n */\nexport class NullError extends ProblemDetailsError {\n /**\n * Create a new {@link NullError}.\n *\n * @param message - Human-readable explanation of what was unexpectedly null/undefined.\n */\n constructor(message: string) {\n super({ status: 500, detail: message });\n this.name = \"NullError\";\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAcA,IAAa,oBAAb,cAAuC,oBAAoB;;;;;;CAMzD,YAAY,SAAiB;EAC3B,MAAM;GAAE,QAAQ;GAAK,QAAQ;EAAQ,CAAC;EACtC,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;ACbA,IAAa,YAAb,cAA+B,oBAAoB;;;;;;CAMjD,YAAY,SAAiB;EAC3B,MAAM;GAAE,QAAQ;GAAK,QAAQ;EAAQ,CAAC;EACtC,KAAK,OAAO;CACd;AACF"}