@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.
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/THIRD-PARTY-NOTICES.md +75 -0
- package/dist/cli/generate-wrangler-types/index.d.ts +1 -0
- package/dist/cli/generate-wrangler-types/index.js +329 -0
- package/dist/cli/generate-wrangler-types/index.js.map +1 -0
- package/dist/error-CLYcAvBM.d.ts +28 -0
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +3 -0
- package/dist/errors-Ciipq_zr.js +58 -0
- package/dist/errors-Ciipq_zr.js.map +1 -0
- package/dist/factory-BI5gVL_P.js +220 -0
- package/dist/factory-BI5gVL_P.js.map +1 -0
- package/dist/generators-D8WWEHa1.js +165 -0
- package/dist/generators-D8WWEHa1.js.map +1 -0
- package/dist/guards/index.d.ts +2 -0
- package/dist/guards/index.js +2 -0
- package/dist/guards-6K1CVAr5.js +61 -0
- package/dist/guards-6K1CVAr5.js.map +1 -0
- package/dist/hono/index.d.ts +347 -0
- package/dist/hono/index.js +307 -0
- package/dist/hono/index.js.map +1 -0
- package/dist/index-434HN8jN.d.ts +43 -0
- package/dist/index-Byl-ZrCy.d.ts +138 -0
- package/dist/index-CUICemFw.d.ts +129 -0
- package/dist/index-DRIhR-Xn.d.ts +81 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/jwt-BvuKtvby.js +328 -0
- package/dist/jwt-BvuKtvby.js.map +1 -0
- package/dist/logging/index.d.ts +3 -0
- package/dist/logging/index.js +3 -0
- package/dist/logging-CGHjOVLM.js +24 -0
- package/dist/logging-CGHjOVLM.js.map +1 -0
- package/dist/policy-CvS6AvvD.js +20 -0
- package/dist/policy-CvS6AvvD.js.map +1 -0
- package/dist/problem-details/index.d.ts +4 -0
- package/dist/problem-details/index.js +3 -0
- package/dist/problem-details-CuRsLy3Q.js +37 -0
- package/dist/problem-details-CuRsLy3Q.js.map +1 -0
- package/dist/silent-CWpHE65X.js +652 -0
- package/dist/silent-CWpHE65X.js.map +1 -0
- package/dist/testing/index.d.ts +44 -0
- package/dist/testing/index.js +2 -0
- package/dist/types-Cx6NNILW.d.ts +44 -0
- package/dist/types-DCSMb1cp.d.ts +195 -0
- package/dist/types-DXboaWOT.d.ts +29 -0
- package/dist/vite/index.d.ts +79 -0
- package/dist/vite/index.js +417 -0
- package/dist/vite/index.js.map +1 -0
- package/package.json +137 -0
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
import { r as valueOrDefault } from "./guards-6K1CVAr5.js";
|
|
2
|
+
//#region src/lib/logging/levels.ts
|
|
3
|
+
/**
|
|
4
|
+
* Stable numeric weights for each log level.
|
|
5
|
+
*
|
|
6
|
+
* A record is emitted when: `LOG_LEVELS[record.level] >= LOG_LEVELS[logger.level]`
|
|
7
|
+
*/
|
|
8
|
+
const LOG_LEVELS = {
|
|
9
|
+
trace: 10,
|
|
10
|
+
debug: 20,
|
|
11
|
+
info: 30,
|
|
12
|
+
warn: 40,
|
|
13
|
+
error: 50,
|
|
14
|
+
fatal: 60
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Return the numeric weight for `level`.
|
|
18
|
+
*
|
|
19
|
+
* Throws a `TypeError` for any value that is not a recognized `LogLevel`. TypeScript consumers
|
|
20
|
+
* should rely on the `LogLevel` union type and never reach this error path under normal usage.
|
|
21
|
+
* Deliberately not implemented with `throwIfNull`: this throws a `TypeError` for an unrecognized
|
|
22
|
+
* string, not a `null`/`undefined` guard, so using that helper would change the thrown error
|
|
23
|
+
* type.
|
|
24
|
+
*
|
|
25
|
+
* @param level - The level to look up.
|
|
26
|
+
* @returns The numeric weight for `level`.
|
|
27
|
+
* @throws {TypeError} If `level` is not one of the six recognized `LogLevel` values.
|
|
28
|
+
*/
|
|
29
|
+
function levelValue(level) {
|
|
30
|
+
const value = LOG_LEVELS[level];
|
|
31
|
+
if (value === void 0) throw new TypeError(`Unknown log level: ${String(level)}. Expected one of: ${Object.keys(LOG_LEVELS).join(", ")}`);
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/lib/logging/internal/optional-field.ts
|
|
36
|
+
/**
|
|
37
|
+
* @file An internal helper for the logging subpath — not one of the toolkit's public defensive
|
|
38
|
+
* guards (`src/lib/guards`) and not exported from `src/lib/logging/index.ts`.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Returns `{ [prop]: o[prop] }` when `prop` is an own property of `o` with a non-`undefined`
|
|
42
|
+
* value, otherwise returns `{}`.
|
|
43
|
+
*
|
|
44
|
+
* Intended for safely spreading optional fields onto plain objects without introducing
|
|
45
|
+
* `undefined`-valued keys:
|
|
46
|
+
*
|
|
47
|
+
* ```ts
|
|
48
|
+
* const result = {
|
|
49
|
+
* name: err.name,
|
|
50
|
+
* ...optionalField(err, "stack"),
|
|
51
|
+
* };
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @param o - The object to read `prop` from.
|
|
55
|
+
* @param prop - The property to conditionally include.
|
|
56
|
+
* @returns `{ [prop]: o[prop] }` when defined, otherwise `{}`.
|
|
57
|
+
*/
|
|
58
|
+
function optionalField(o, prop) {
|
|
59
|
+
return o[prop] !== void 0 ? { [prop]: o[prop] } : {};
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/lib/logging/serialize.ts
|
|
63
|
+
/**
|
|
64
|
+
* @file Error serialization for the logging subpath.
|
|
65
|
+
*
|
|
66
|
+
* `serializeError()` converts `Error` instances to plain objects so transports can safely
|
|
67
|
+
* forward structured context without raw `Error` values escaping into JSON serialization or
|
|
68
|
+
* console methods. Only top-level context values are serialized by the logger (`logger.ts`);
|
|
69
|
+
* nested errors are left as-is unless they appear as a direct `cause` of a top-level error.
|
|
70
|
+
*/
|
|
71
|
+
/**
|
|
72
|
+
* Serialize `value` if it is an `Error`; return it unchanged otherwise.
|
|
73
|
+
*
|
|
74
|
+
* - Non-`Error` values are returned as-is.
|
|
75
|
+
* - `Error` instances become plain objects with `name`, `message`, and optionally `stack` and
|
|
76
|
+
* `cause`.
|
|
77
|
+
* - `cause` is shallowly serialized when it is itself an `Error`.
|
|
78
|
+
*
|
|
79
|
+
* @param value - The value to serialize, if it is an `Error`.
|
|
80
|
+
* @returns A plain-object serialization of `value` when it is an `Error`, otherwise `value`
|
|
81
|
+
* unchanged.
|
|
82
|
+
*/
|
|
83
|
+
function serializeError(value) {
|
|
84
|
+
if (!(value instanceof Error)) return value;
|
|
85
|
+
const serialized = {
|
|
86
|
+
name: value.name,
|
|
87
|
+
message: value.message,
|
|
88
|
+
...optionalField(value, "stack")
|
|
89
|
+
};
|
|
90
|
+
if (value.cause !== void 0) serialized.cause = value.cause instanceof Error ? {
|
|
91
|
+
name: value.cause.name,
|
|
92
|
+
message: value.cause.message,
|
|
93
|
+
...optionalField(value.cause, "stack")
|
|
94
|
+
} : value.cause;
|
|
95
|
+
return serialized;
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/lib/logging/logger.ts
|
|
99
|
+
/**
|
|
100
|
+
* @file The core logger implementation. This is the framework-agnostic core that the
|
|
101
|
+
* `cloudflareLogger` Hono middleware wraps — it must never import `hono`.
|
|
102
|
+
*
|
|
103
|
+
* `createLogger()` constructs a `Logger` that:
|
|
104
|
+
* - Filters records below the configured level before touching context.
|
|
105
|
+
* - Merges bindings and per-call context into a new object (never mutates input).
|
|
106
|
+
* - Serializes top-level `Error` values in context before delivering to transport.
|
|
107
|
+
* - Wraps transport delivery in try/catch so transport failures never escape.
|
|
108
|
+
* - Supports child loggers that inherit transport, level, clock, and error handler.
|
|
109
|
+
*/
|
|
110
|
+
/**
|
|
111
|
+
* Internal factory used by both `createLogger` and child loggers.
|
|
112
|
+
*
|
|
113
|
+
* All logger state is captured in the closure; no class is used. Both root loggers and child
|
|
114
|
+
* loggers are created through this function, which keeps the child creation path identical to
|
|
115
|
+
* the root path.
|
|
116
|
+
*
|
|
117
|
+
* @param level - Minimum level to emit.
|
|
118
|
+
* @param transport - Destination for emitted records.
|
|
119
|
+
* @param bindings - Key-value pairs merged into every record's context.
|
|
120
|
+
* @param clock - Produces the timestamp for each record.
|
|
121
|
+
* @param onTransportError - Optional callback for transport failures.
|
|
122
|
+
* @returns A `Logger` bound to the supplied state.
|
|
123
|
+
*/
|
|
124
|
+
function makeLogger(level, transport, bindings, clock, onTransportError) {
|
|
125
|
+
const currentLevelValue = levelValue(level);
|
|
126
|
+
/**
|
|
127
|
+
* Returns `true` when records at `candidate` severity will be emitted. Used both for the
|
|
128
|
+
* public `isLevelEnabled` method and internally before constructing a record.
|
|
129
|
+
*
|
|
130
|
+
* @param candidate - The level to test against the configured minimum level.
|
|
131
|
+
* @returns `true` when `candidate` is at or above the configured minimum level.
|
|
132
|
+
*/
|
|
133
|
+
function isLevelEnabled(candidate) {
|
|
134
|
+
return levelValue(candidate) >= currentLevelValue;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Core emit path shared by all six level methods.
|
|
138
|
+
*
|
|
139
|
+
* Exits immediately when `logLevel` is below the configured minimum so that disabled calls
|
|
140
|
+
* never access the `context` argument. When enabled, merges bindings with per-call context,
|
|
141
|
+
* serializes top-level `Error` values, builds the `LogRecord`, and delivers it to the
|
|
142
|
+
* transport inside a try/catch.
|
|
143
|
+
*
|
|
144
|
+
* @param logLevel - Severity of this record.
|
|
145
|
+
* @param message - Human-readable description of the event.
|
|
146
|
+
* @param context - Optional per-call structured context.
|
|
147
|
+
*/
|
|
148
|
+
function emit(logLevel, message, context) {
|
|
149
|
+
if (!isLevelEnabled(logLevel)) return;
|
|
150
|
+
const merged = context !== void 0 ? {
|
|
151
|
+
...bindings,
|
|
152
|
+
...context
|
|
153
|
+
} : { ...bindings };
|
|
154
|
+
const serializedContext = {};
|
|
155
|
+
for (const key of Object.keys(merged)) serializedContext[key] = serializeError(merged[key]);
|
|
156
|
+
const record = {
|
|
157
|
+
time: clock().toISOString(),
|
|
158
|
+
level: logLevel,
|
|
159
|
+
levelValue: levelValue(logLevel),
|
|
160
|
+
message,
|
|
161
|
+
context: serializedContext
|
|
162
|
+
};
|
|
163
|
+
try {
|
|
164
|
+
transport.log(record);
|
|
165
|
+
} catch (error) {
|
|
166
|
+
try {
|
|
167
|
+
onTransportError?.(error, record);
|
|
168
|
+
} catch {}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
get level() {
|
|
173
|
+
return level;
|
|
174
|
+
},
|
|
175
|
+
isLevelEnabled,
|
|
176
|
+
trace(message, context) {
|
|
177
|
+
emit("trace", message, context);
|
|
178
|
+
},
|
|
179
|
+
debug(message, context) {
|
|
180
|
+
emit("debug", message, context);
|
|
181
|
+
},
|
|
182
|
+
info(message, context) {
|
|
183
|
+
emit("info", message, context);
|
|
184
|
+
},
|
|
185
|
+
warn(message, context) {
|
|
186
|
+
emit("warn", message, context);
|
|
187
|
+
},
|
|
188
|
+
error(message, context) {
|
|
189
|
+
emit("error", message, context);
|
|
190
|
+
},
|
|
191
|
+
fatal(message, context) {
|
|
192
|
+
emit("fatal", message, context);
|
|
193
|
+
},
|
|
194
|
+
child(childBindings) {
|
|
195
|
+
return makeLogger(level, transport, {
|
|
196
|
+
...bindings,
|
|
197
|
+
...childBindings
|
|
198
|
+
}, clock, onTransportError);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Create a new `Logger` with the provided options.
|
|
204
|
+
*
|
|
205
|
+
* - `options.transport` is required.
|
|
206
|
+
* - `options.level` defaults to `"info"`.
|
|
207
|
+
* - `options.clock` defaults to `() => new Date()`.
|
|
208
|
+
* - `options.bindings` are merged into every emitted record.
|
|
209
|
+
* - `options.onTransportError` receives transport errors without crashing the app.
|
|
210
|
+
*
|
|
211
|
+
* @param options - Logger construction options.
|
|
212
|
+
* @returns A new `Logger`.
|
|
213
|
+
*/
|
|
214
|
+
function createLogger(options) {
|
|
215
|
+
const { transport, level = "info", bindings = {}, clock = () => /* @__PURE__ */ new Date(), onTransportError } = options;
|
|
216
|
+
return makeLogger(level, transport, bindings, clock, onTransportError);
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region src/lib/logging/internal/console.ts
|
|
220
|
+
/**
|
|
221
|
+
* Return the named console method from `c`, falling back to `c.log` if the method is absent or
|
|
222
|
+
* not a function.
|
|
223
|
+
*
|
|
224
|
+
* Rationale: logging must not crash because a host environment is missing a specific console
|
|
225
|
+
* method. `console.log` is the safest baseline and is present in every JS environment that
|
|
226
|
+
* supports `console` at all.
|
|
227
|
+
*
|
|
228
|
+
* @param c - The console-like object to query.
|
|
229
|
+
* @param method - The preferred method name.
|
|
230
|
+
* @returns The requested method if callable, otherwise `c.log`.
|
|
231
|
+
*/
|
|
232
|
+
function getConsoleMethod(c, method) {
|
|
233
|
+
const candidate = c[method];
|
|
234
|
+
if (typeof candidate === "function") return candidate.bind(c);
|
|
235
|
+
return c.log.bind(c);
|
|
236
|
+
}
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/lib/logging/transports/browser.ts
|
|
239
|
+
/**
|
|
240
|
+
* @file A transport that formats records for browser DevTools using `%c` styled level badges.
|
|
241
|
+
*
|
|
242
|
+
* `createBrowserTransport()` maps severity levels to appropriate console methods and passes the
|
|
243
|
+
* context object as a separate argument when non-empty, so DevTools can expand it interactively.
|
|
244
|
+
*
|
|
245
|
+
* Level-to-method mapping:
|
|
246
|
+
* trace, debug → console.debug
|
|
247
|
+
* info → console.info
|
|
248
|
+
* warn → console.warn
|
|
249
|
+
* error, fatal → console.error
|
|
250
|
+
*/
|
|
251
|
+
/** Default CSS badge styles keyed by level. */
|
|
252
|
+
const DEFAULT_STYLES = {
|
|
253
|
+
trace: "color: #9ca3af; font-weight: bold",
|
|
254
|
+
debug: "color: #6b7280; font-weight: bold",
|
|
255
|
+
info: "color: #3b82f6; font-weight: bold",
|
|
256
|
+
warn: "color: #f59e0b; font-weight: bold",
|
|
257
|
+
error: "color: #ef4444; font-weight: bold",
|
|
258
|
+
fatal: "color: #dc2626; font-weight: bold; text-decoration: underline"
|
|
259
|
+
};
|
|
260
|
+
/** Map each log level to the preferred console method name. */
|
|
261
|
+
const LEVEL_METHOD$1 = {
|
|
262
|
+
trace: "debug",
|
|
263
|
+
debug: "debug",
|
|
264
|
+
info: "info",
|
|
265
|
+
warn: "warn",
|
|
266
|
+
error: "error",
|
|
267
|
+
fatal: "error"
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* Create a browser transport optimized for DevTools output.
|
|
271
|
+
*
|
|
272
|
+
* @param options - Optional level style overrides.
|
|
273
|
+
* @param _console - Injected console-like object (defaults to global `console`). Used in tests.
|
|
274
|
+
* @returns A `Transport` that writes styled records to the browser console.
|
|
275
|
+
*/
|
|
276
|
+
function createBrowserTransport(options, _console = console) {
|
|
277
|
+
const levelStyles = {
|
|
278
|
+
...DEFAULT_STYLES,
|
|
279
|
+
...options?.levelStyles
|
|
280
|
+
};
|
|
281
|
+
return { log(record) {
|
|
282
|
+
const methodName = LEVEL_METHOD$1[record.level];
|
|
283
|
+
const method = getConsoleMethod(_console, methodName);
|
|
284
|
+
const style = levelStyles[record.level];
|
|
285
|
+
const badge = record.level.toUpperCase();
|
|
286
|
+
if (Object.keys(record.context).length > 0) method(`%c${badge}`, style, record.message, record.context);
|
|
287
|
+
else method(`%c${badge}`, style, record.message);
|
|
288
|
+
} };
|
|
289
|
+
}
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/lib/logging/transports/capture.ts
|
|
292
|
+
/**
|
|
293
|
+
* Create a capture transport that stores records in memory.
|
|
294
|
+
*
|
|
295
|
+
* @returns A `CaptureTransport` with `.records`, `.clear()`, and `.find()`.
|
|
296
|
+
*/
|
|
297
|
+
function createCaptureTransport() {
|
|
298
|
+
let internal = [];
|
|
299
|
+
return {
|
|
300
|
+
log(record) {
|
|
301
|
+
internal.push(record);
|
|
302
|
+
},
|
|
303
|
+
get records() {
|
|
304
|
+
return internal.slice();
|
|
305
|
+
},
|
|
306
|
+
clear() {
|
|
307
|
+
internal = [];
|
|
308
|
+
},
|
|
309
|
+
find(level) {
|
|
310
|
+
return internal.filter((r) => r.level === level);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region src/lib/logging/internal/safe-json.ts
|
|
316
|
+
/**
|
|
317
|
+
* @file Internal safe JSON/string formatting for the logging subpath. Not exported from
|
|
318
|
+
* `src/lib/logging/index.ts`; used by `createConsoleTransport` and `createStructuredTransport`.
|
|
319
|
+
*
|
|
320
|
+
* `safeStringify()` serializes arbitrary values to a compact JSON string while handling the
|
|
321
|
+
* common non-JSON types that appear in structured log context:
|
|
322
|
+
* - Circular references → `"[Circular]"` (a shared/diamond reference reachable via two
|
|
323
|
+
* non-nested paths — e.g. `{ a: shared, b: shared }` — is NOT a circular reference and is
|
|
324
|
+
* serialized in full at both locations; only true ancestor cycles are replaced)
|
|
325
|
+
* - `bigint` → `"<n>n"` (e.g. `42n` → `"42n"`)
|
|
326
|
+
* - `symbol` → `"Symbol(description)"`
|
|
327
|
+
* - `function` → `"[Function name]"` or `"[Function (anonymous)]"`
|
|
328
|
+
* - `undefined` → omitted from objects, `"undefined"` at top level
|
|
329
|
+
*/
|
|
330
|
+
/**
|
|
331
|
+
* A stable placeholder emitted when `JSON.stringify` itself throws unexpectedly (e.g. a getter
|
|
332
|
+
* that throws mid-serialization after the circular-reference check has passed).
|
|
333
|
+
*/
|
|
334
|
+
const FALLBACK = "[FormattingError]";
|
|
335
|
+
/**
|
|
336
|
+
* Convert a single value to a JSON-safe replacement.
|
|
337
|
+
*
|
|
338
|
+
* Called from the `JSON.stringify` replacer for every value encountered during serialization.
|
|
339
|
+
* Non-JSON-safe primitives (`bigint`, `symbol`, `function`) are converted to descriptive
|
|
340
|
+
* strings. All other values are returned unchanged so that `JSON.stringify` handles them
|
|
341
|
+
* normally.
|
|
342
|
+
*
|
|
343
|
+
* Exported for direct unit testing so the default return path is reachable without going
|
|
344
|
+
* through the full replacer loop.
|
|
345
|
+
*
|
|
346
|
+
* @param value - The raw value at the current key.
|
|
347
|
+
* @returns A JSON-safe replacement value.
|
|
348
|
+
*/
|
|
349
|
+
function replaceNonJsonValue(value) {
|
|
350
|
+
if (typeof value === "bigint") return `${value.toString()}n`;
|
|
351
|
+
if (typeof value === "symbol") return value.toString();
|
|
352
|
+
if (typeof value === "function") {
|
|
353
|
+
const name = value.name;
|
|
354
|
+
return name ? `[Function ${name}]` : "[Function (anonymous)]";
|
|
355
|
+
}
|
|
356
|
+
return value;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Serialize `value` to a compact JSON string.
|
|
360
|
+
*
|
|
361
|
+
* Handles:
|
|
362
|
+
* - Circular references (replaced with `"[Circular]"`); shared/diamond references (the same
|
|
363
|
+
* object reachable via two different, non-nested paths) are NOT treated as circular and are
|
|
364
|
+
* serialized in full at every location
|
|
365
|
+
* - `bigint` (serialized as `"<n>n"`)
|
|
366
|
+
* - `symbol` (serialized as `"Symbol(description)"`)
|
|
367
|
+
* - `function` (serialized as `"[Function name]"`)
|
|
368
|
+
* - `undefined` at the top level (returns `"undefined"`)
|
|
369
|
+
* - Unexpected formatter errors (returns `"[FormattingError]"`)
|
|
370
|
+
*
|
|
371
|
+
* @param value - The value to serialize.
|
|
372
|
+
* @returns A JSON string representation.
|
|
373
|
+
*/
|
|
374
|
+
function safeStringify(value) {
|
|
375
|
+
if (value === void 0) return "undefined";
|
|
376
|
+
const stack = [];
|
|
377
|
+
try {
|
|
378
|
+
return JSON.stringify(value, function replacer(_key, val) {
|
|
379
|
+
if (typeof val === "bigint" || typeof val === "symbol" || typeof val === "function") return replaceNonJsonValue(val);
|
|
380
|
+
if (val !== null && typeof val === "object") {
|
|
381
|
+
const holderIndex = stack.indexOf(this);
|
|
382
|
+
if (holderIndex === -1) stack.push(this);
|
|
383
|
+
else stack.length = holderIndex + 1;
|
|
384
|
+
if (stack.includes(val)) return "[Circular]";
|
|
385
|
+
stack.push(val);
|
|
386
|
+
}
|
|
387
|
+
return val;
|
|
388
|
+
});
|
|
389
|
+
} catch {
|
|
390
|
+
return FALLBACK;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
//#endregion
|
|
394
|
+
//#region src/lib/logging/internal/sanitize.ts
|
|
395
|
+
/**
|
|
396
|
+
* @file An internal helper for the logging subpath — not exported from
|
|
397
|
+
* `src/lib/logging/index.ts`.
|
|
398
|
+
*
|
|
399
|
+
* `sanitizeTerminalText()` neutralizes terminal-injection risk (SEC-007) in strings that are
|
|
400
|
+
* about to be written to a real terminal by `createConsoleTransport()`. `record.message` is
|
|
401
|
+
* caller-supplied and may embed attacker-controlled data (e.g. an email address or path). Left
|
|
402
|
+
* unescaped, it could contain newlines that forge fake log lines, or ANSI escape sequences
|
|
403
|
+
* (`\x1b...`) that manipulate the terminal (colors, cursor movement, title changes) when the
|
|
404
|
+
* output is later viewed through a real terminal such as `wrangler tail`.
|
|
405
|
+
*
|
|
406
|
+
* `record.context` does not need the same treatment: `createConsoleTransport()` always renders
|
|
407
|
+
* it via `safeStringify()` (`JSON.stringify` under the hood), which already escapes every C0
|
|
408
|
+
* control character — including `\x1b` — as `\uXXXX` per the JSON spec.
|
|
409
|
+
*/
|
|
410
|
+
/**
|
|
411
|
+
* Control characters (C0: `\x00`-`\x1f`, plus DEL `\x7f`) that could forge terminal output or
|
|
412
|
+
* trigger ANSI escape sequences. Every terminal escape sequence begins with `\x1b` (ESC), so
|
|
413
|
+
* escaping it alone is sufficient to neutralize ANSI injection; the rest of the range is
|
|
414
|
+
* escaped defensively for the same reason.
|
|
415
|
+
*/
|
|
416
|
+
const CONTROL_CHAR_PATTERN = /[\x00-\x1f\x7f]/g;
|
|
417
|
+
/** Readable escape sequences for the most common control characters. */
|
|
418
|
+
const NAMED_ESCAPES = {
|
|
419
|
+
"\n": "\\n",
|
|
420
|
+
"\r": "\\r",
|
|
421
|
+
" ": "\\t"
|
|
422
|
+
};
|
|
423
|
+
/**
|
|
424
|
+
* Replace every C0 control character and DEL in `value` with a visible, non-executable escape
|
|
425
|
+
* sequence.
|
|
426
|
+
*
|
|
427
|
+
* Common whitespace controls (`\n`, `\r`, `\t`) are rendered as their familiar backslash
|
|
428
|
+
* escapes; every other control character (including ESC, `\x1b`) is rendered as a two-digit hex
|
|
429
|
+
* escape (`\xHH`). This preserves the original information for debugging while preventing the
|
|
430
|
+
* string from forging additional log lines or executing terminal escape sequences.
|
|
431
|
+
*
|
|
432
|
+
* @param value - The raw string to sanitize before writing it to a terminal.
|
|
433
|
+
* @returns `value` with every control character replaced by a visible escape sequence.
|
|
434
|
+
*/
|
|
435
|
+
function sanitizeTerminalText(value) {
|
|
436
|
+
return value.replace(CONTROL_CHAR_PATTERN, (char) => {
|
|
437
|
+
const named = NAMED_ESCAPES[char];
|
|
438
|
+
if (named !== void 0) return named;
|
|
439
|
+
return `\\x${char.charCodeAt(0).toString(16).padStart(2, "0")}`;
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
//#endregion
|
|
443
|
+
//#region src/lib/logging/transports/console.ts
|
|
444
|
+
/**
|
|
445
|
+
* @file A transport that formats records as human-readable single-line output intended for
|
|
446
|
+
* terminal environments including `wrangler dev`.
|
|
447
|
+
*
|
|
448
|
+
* `createConsoleTransport()` supports optional ANSI color codes and configurable timestamp
|
|
449
|
+
* formats.
|
|
450
|
+
*
|
|
451
|
+
* `record.message` is sanitized via `sanitizeTerminalText()` (SEC-007) before it is written,
|
|
452
|
+
* escaping control/ANSI characters so caller-supplied data cannot forge additional log lines or
|
|
453
|
+
* inject terminal escape sequences when the output is later viewed in a real terminal (e.g.
|
|
454
|
+
* `wrangler tail`).
|
|
455
|
+
*
|
|
456
|
+
* Level-to-method mapping:
|
|
457
|
+
* trace, debug, info → console.log
|
|
458
|
+
* warn, error, fatal → console.error
|
|
459
|
+
*
|
|
460
|
+
* Output format:
|
|
461
|
+
* [timestamp] LEVEL message [{"key":"value"}]
|
|
462
|
+
*/
|
|
463
|
+
const RESET = "\x1B[0m";
|
|
464
|
+
/** Map each level to its ANSI color code. */
|
|
465
|
+
const LEVEL_COLOR = {
|
|
466
|
+
trace: "\x1B[90m",
|
|
467
|
+
debug: "\x1B[37m",
|
|
468
|
+
info: "\x1B[36m",
|
|
469
|
+
warn: "\x1B[33m",
|
|
470
|
+
error: "\x1B[31m",
|
|
471
|
+
fatal: "\x1B[35m"
|
|
472
|
+
};
|
|
473
|
+
/**
|
|
474
|
+
* Extract an `HH:MM:SS` time string from an ISO 8601 timestamp. Falls back to the first 8
|
|
475
|
+
* characters if the expected `T` separator is absent.
|
|
476
|
+
*
|
|
477
|
+
* Exported for direct unit testing of the fallback branch.
|
|
478
|
+
*
|
|
479
|
+
* @param isoString - An ISO 8601 timestamp string.
|
|
480
|
+
* @returns The `HH:MM:SS` portion, or the first 8 characters if no `T` separator is found.
|
|
481
|
+
*/
|
|
482
|
+
function extractTime(isoString) {
|
|
483
|
+
const tIndex = isoString.indexOf("T");
|
|
484
|
+
if (tIndex === -1) return isoString.slice(0, 8);
|
|
485
|
+
return isoString.slice(tIndex + 1, tIndex + 9);
|
|
486
|
+
}
|
|
487
|
+
/** Fixed-width (5 chars) uppercase level labels. */
|
|
488
|
+
const LEVEL_LABEL = {
|
|
489
|
+
trace: "TRACE",
|
|
490
|
+
debug: "DEBUG",
|
|
491
|
+
info: "INFO ",
|
|
492
|
+
warn: "WARN ",
|
|
493
|
+
error: "ERROR",
|
|
494
|
+
fatal: "FATAL"
|
|
495
|
+
};
|
|
496
|
+
/**
|
|
497
|
+
* Create a console transport for terminal/wrangler dev output.
|
|
498
|
+
*
|
|
499
|
+
* @param options - Timestamp and color options.
|
|
500
|
+
* @param _console - Injected console-like object (defaults to global `console`). Used in tests.
|
|
501
|
+
* @returns A `Transport` that writes formatted lines to the terminal.
|
|
502
|
+
*/
|
|
503
|
+
function createConsoleTransport(options, _console = console) {
|
|
504
|
+
const colors = valueOrDefault(options?.colors, true);
|
|
505
|
+
const timestamp = valueOrDefault(options?.timestamp, "time");
|
|
506
|
+
return { log(record) {
|
|
507
|
+
const method = getConsoleMethod(_console, record.level === "warn" || record.level === "error" || record.level === "fatal" ? "error" : "log");
|
|
508
|
+
const parts = [];
|
|
509
|
+
if (timestamp === "time") {
|
|
510
|
+
const ts = extractTime(record.time);
|
|
511
|
+
parts.push(colors ? `\x1b[90m${ts}${RESET}` : ts);
|
|
512
|
+
} else if (timestamp === "iso") parts.push(colors ? `\x1b[90m${record.time}${RESET}` : record.time);
|
|
513
|
+
const label = LEVEL_LABEL[record.level];
|
|
514
|
+
if (colors) {
|
|
515
|
+
const color = LEVEL_COLOR[record.level];
|
|
516
|
+
parts.push(`${color}${label}${RESET}`);
|
|
517
|
+
} else parts.push(label);
|
|
518
|
+
parts.push(sanitizeTerminalText(record.message));
|
|
519
|
+
if (Object.keys(record.context).length > 0) parts.push(safeStringify(record.context));
|
|
520
|
+
method(parts.join(" "));
|
|
521
|
+
} };
|
|
522
|
+
}
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region src/lib/logging/transports/structured.ts
|
|
525
|
+
/**
|
|
526
|
+
* @file A transport that emits records as structured payloads intended for Cloudflare Workers
|
|
527
|
+
* Logs.
|
|
528
|
+
*
|
|
529
|
+
* Workers Logs automatically extracts and indexes fields from JSON object logs, so
|
|
530
|
+
* `createStructuredTransport()` defaults to object logging (`stringify: false`) rather than
|
|
531
|
+
* string logging.
|
|
532
|
+
*
|
|
533
|
+
* Payload shape: `{ time, level, message, ...context }`
|
|
534
|
+
* Reserved keys (`time`, `level`, `message`) from the record take precedence over identically
|
|
535
|
+
* named context keys.
|
|
536
|
+
*
|
|
537
|
+
* Level-to-method mapping:
|
|
538
|
+
* trace, debug → console.debug
|
|
539
|
+
* info → console.log
|
|
540
|
+
* warn → console.warn
|
|
541
|
+
* error, fatal → console.error
|
|
542
|
+
*/
|
|
543
|
+
/** Map each log level to the preferred console method name. */
|
|
544
|
+
const LEVEL_METHOD = {
|
|
545
|
+
trace: "debug",
|
|
546
|
+
debug: "debug",
|
|
547
|
+
info: "log",
|
|
548
|
+
warn: "warn",
|
|
549
|
+
error: "error",
|
|
550
|
+
fatal: "error"
|
|
551
|
+
};
|
|
552
|
+
/**
|
|
553
|
+
* Create a structured transport for Cloudflare Workers Logs.
|
|
554
|
+
*
|
|
555
|
+
* @param options - Optional stringify flag.
|
|
556
|
+
* @param _console - Injected console-like object (defaults to global `console`). Used in tests.
|
|
557
|
+
* @returns A `Transport` that writes structured payloads to the console.
|
|
558
|
+
*/
|
|
559
|
+
function createStructuredTransport(options, _console = console) {
|
|
560
|
+
const stringify = valueOrDefault(options?.stringify, false);
|
|
561
|
+
return { log(record) {
|
|
562
|
+
const methodName = LEVEL_METHOD[record.level];
|
|
563
|
+
const method = getConsoleMethod(_console, methodName);
|
|
564
|
+
const payload = {
|
|
565
|
+
...record.context,
|
|
566
|
+
time: record.time,
|
|
567
|
+
level: record.level,
|
|
568
|
+
message: record.message
|
|
569
|
+
};
|
|
570
|
+
if (stringify) method(safeStringify(payload));
|
|
571
|
+
else method(payload);
|
|
572
|
+
} };
|
|
573
|
+
}
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/lib/logging/resolve.ts
|
|
576
|
+
/**
|
|
577
|
+
* @file A default-config helper that maps an environment + runtime pair to a ready-to-use
|
|
578
|
+
* `{ level, transport }` pair.
|
|
579
|
+
*
|
|
580
|
+
* `resolveLoggerConfig()` is optional policy — `createLogger` does not require it. Applications
|
|
581
|
+
* that need environment-specific configuration without hand-wiring transports can call this
|
|
582
|
+
* helper and pass the result directly to `createLogger`.
|
|
583
|
+
*
|
|
584
|
+
* Policy table:
|
|
585
|
+
*
|
|
586
|
+
* | Environment | Runtime | Level | Transport |
|
|
587
|
+
* |---------------|-----------|---------|------------|
|
|
588
|
+
* | test | browser | trace | capture |
|
|
589
|
+
* | test | worker | trace | capture |
|
|
590
|
+
* | development | browser | info | browser |
|
|
591
|
+
* | development | worker | debug | console |
|
|
592
|
+
* | production | browser | warn | browser |
|
|
593
|
+
* | production | worker | warn | structured |
|
|
594
|
+
* | unknown | browser | warn | browser |
|
|
595
|
+
* | unknown | worker | warn | structured |
|
|
596
|
+
*
|
|
597
|
+
* There is no `detectRuntime()` helper in the public API. Applications are expected to know
|
|
598
|
+
* whether they are constructing a browser logger or a Worker logger.
|
|
599
|
+
*/
|
|
600
|
+
/**
|
|
601
|
+
* Resolve a `{ level, transport }` configuration for the given environment and runtime.
|
|
602
|
+
*
|
|
603
|
+
* Each call creates a **fresh** transport instance. If you call this helper more than once with
|
|
604
|
+
* the same arguments you will receive independent transport instances, which is intentional for
|
|
605
|
+
* test isolation.
|
|
606
|
+
*
|
|
607
|
+
* Unknown or `undefined` environments are treated as `"production"`.
|
|
608
|
+
*
|
|
609
|
+
* @param environment - One of `"test"`, `"development"`, `"production"`, or any other string.
|
|
610
|
+
* `undefined` maps to production behavior.
|
|
611
|
+
* @param runtime - Either `"browser"` or `"worker"`.
|
|
612
|
+
* @returns A fresh `ResolvedLoggerConfig` ready to pass to `createLogger`.
|
|
613
|
+
*/
|
|
614
|
+
function resolveLoggerConfig(environment, runtime) {
|
|
615
|
+
const env = environment === "test" || environment === "development" ? environment : "production";
|
|
616
|
+
if (env === "test") return {
|
|
617
|
+
level: "trace",
|
|
618
|
+
transport: createCaptureTransport()
|
|
619
|
+
};
|
|
620
|
+
if (env === "development") {
|
|
621
|
+
if (runtime === "browser") return {
|
|
622
|
+
level: "info",
|
|
623
|
+
transport: createBrowserTransport()
|
|
624
|
+
};
|
|
625
|
+
return {
|
|
626
|
+
level: "debug",
|
|
627
|
+
transport: createConsoleTransport()
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
if (runtime === "browser") return {
|
|
631
|
+
level: "warn",
|
|
632
|
+
transport: createBrowserTransport()
|
|
633
|
+
};
|
|
634
|
+
return {
|
|
635
|
+
level: "warn",
|
|
636
|
+
transport: createStructuredTransport()
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
//#endregion
|
|
640
|
+
//#region src/lib/logging/transports/silent.ts
|
|
641
|
+
/**
|
|
642
|
+
* Create a silent transport that discards all records.
|
|
643
|
+
*
|
|
644
|
+
* @returns A `Transport` that does nothing.
|
|
645
|
+
*/
|
|
646
|
+
function createSilentTransport() {
|
|
647
|
+
return { log() {} };
|
|
648
|
+
}
|
|
649
|
+
//#endregion
|
|
650
|
+
export { createCaptureTransport as a, serializeError as c, createConsoleTransport as i, resolveLoggerConfig as n, createBrowserTransport as o, createStructuredTransport as r, createLogger as s, createSilentTransport as t };
|
|
651
|
+
|
|
652
|
+
//# sourceMappingURL=silent-CWpHE65X.js.map
|