hono-utils 0.3.9 → 0.5.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.
- package/dist/index.cjs +137 -13935
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -6
- package/dist/index.d.mts +467 -0
- package/dist/index.d.ts +14 -6
- package/dist/index.mjs +478 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +36 -23
- package/dist/index.js +0 -14290
- package/dist/index.js.map +0 -1
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import { createMiddleware } from 'hono/factory';
|
|
2
|
+
import { Logger } from 'hierarchical-area-logger';
|
|
3
|
+
import { HTTPException } from 'hono/http-exception';
|
|
4
|
+
import { validator } from 'hono/validator';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { UAParser } from 'ua-parser-js';
|
|
7
|
+
import { init } from '@paralleldrive/cuid2';
|
|
8
|
+
import { hc, parseResponse } from 'hono/client';
|
|
9
|
+
|
|
10
|
+
const logger = (details, parentEventIdHeader) => createMiddleware(async (c, next) => {
|
|
11
|
+
const logger2 = new Logger({
|
|
12
|
+
details,
|
|
13
|
+
withParentEventId: !!parentEventIdHeader,
|
|
14
|
+
parentEventId: parentEventIdHeader ? c.req.raw.headers.get(parentEventIdHeader) ?? void 0 : void 0,
|
|
15
|
+
defaultArea: new URL(c.req.raw.url).pathname,
|
|
16
|
+
path: new URL(c.req.raw.url).pathname
|
|
17
|
+
});
|
|
18
|
+
c.set("logger", logger2);
|
|
19
|
+
c.set("eventId", logger2.eventId);
|
|
20
|
+
await next();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const jsonValidator = (schema) => validator("json", (value) => {
|
|
24
|
+
const parsed = schema.safeParse(value);
|
|
25
|
+
if (!parsed.success) {
|
|
26
|
+
throw new HTTPException(400, {
|
|
27
|
+
message: z.prettifyError(parsed.error)
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return parsed.data;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const isBot = ({ threshold, allowVerifiedBot, useLogger } = {
|
|
34
|
+
threshold: 49
|
|
35
|
+
}) => createMiddleware(async (c, next) => {
|
|
36
|
+
const logger = useLogger ? (c.get("logger") ?? new Logger({
|
|
37
|
+
details: {
|
|
38
|
+
service: "dummy"
|
|
39
|
+
}
|
|
40
|
+
})).getArea("middeware:isBot") : void 0;
|
|
41
|
+
logger?.debug("Retrieving bot management data from cloudflare data");
|
|
42
|
+
const botManagement = c.req.raw.cf?.botManagement;
|
|
43
|
+
if (!botManagement) {
|
|
44
|
+
throw new Error("botManagement is not available");
|
|
45
|
+
}
|
|
46
|
+
const score = botManagement?.score ?? 54;
|
|
47
|
+
const verifiedBot = botManagement?.verifiedBot ?? false;
|
|
48
|
+
const isBotByScore = score < threshold;
|
|
49
|
+
const isVerifiedAllowed = !!(allowVerifiedBot && verifiedBot);
|
|
50
|
+
const isBot2 = isBotByScore;
|
|
51
|
+
logger?.debug("Setting isBot variable in context", {
|
|
52
|
+
isBotByScore,
|
|
53
|
+
isVerifiedAllowed,
|
|
54
|
+
isBot: isBot2
|
|
55
|
+
});
|
|
56
|
+
logger?.info("isBot variable set");
|
|
57
|
+
c.set("isBot", isBot2);
|
|
58
|
+
if (isBotByScore && !isVerifiedAllowed) {
|
|
59
|
+
throw new HTTPException(401, { message: "Bot detected" });
|
|
60
|
+
}
|
|
61
|
+
await next();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const setI18n = (translations, language, defaultLanguage) => (input, params, overrideLanguage) => {
|
|
65
|
+
const result = input.split(".").reduce(
|
|
66
|
+
(acc, key) => {
|
|
67
|
+
if (acc && typeof acc === "object" && key in acc) {
|
|
68
|
+
return acc[key];
|
|
69
|
+
} else {
|
|
70
|
+
if (translations[defaultLanguage] && typeof translations[defaultLanguage] === "object" && key in translations[defaultLanguage]) {
|
|
71
|
+
return translations[defaultLanguage][key];
|
|
72
|
+
} else {
|
|
73
|
+
return input;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
translations[overrideLanguage ?? language]
|
|
78
|
+
);
|
|
79
|
+
if (params) {
|
|
80
|
+
return Object.keys(params).reduce(
|
|
81
|
+
(acc, key) => {
|
|
82
|
+
return acc.replace(
|
|
83
|
+
new RegExp(`{{${key}}}`, "g"),
|
|
84
|
+
params[key].toString()
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
typeof result === "string" ? result : input
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return typeof result === "string" ? result : input;
|
|
91
|
+
};
|
|
92
|
+
const i18n = (translations, defaultLanguage) => ({
|
|
93
|
+
middleware: createMiddleware(async ({ set, var: { language } }, next) => {
|
|
94
|
+
if (!language) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"Language variable should be initialized before translations middleware"
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
set("t", setI18n(translations, language, defaultLanguage));
|
|
100
|
+
await next();
|
|
101
|
+
}),
|
|
102
|
+
queue: (language) => setI18n(translations, language ?? defaultLanguage, defaultLanguage)
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const queue = ({
|
|
106
|
+
name,
|
|
107
|
+
queueHandler,
|
|
108
|
+
finalizeLogHandler
|
|
109
|
+
}) => createMiddleware(async ({ env, set, get }, next) => {
|
|
110
|
+
const queueEnv = env[name];
|
|
111
|
+
const language = get("language");
|
|
112
|
+
if (!queueEnv) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Queue environment variable ${name} is not defined`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const logger = get("logger");
|
|
118
|
+
let parentEventId;
|
|
119
|
+
if (finalizeLogHandler) {
|
|
120
|
+
if (!logger) {
|
|
121
|
+
throw new Error("Logger should be initialized before queue middleware");
|
|
122
|
+
}
|
|
123
|
+
parentEventId = logger?.eventId;
|
|
124
|
+
}
|
|
125
|
+
const producer = queueHandler.getProducer(queueEnv, {
|
|
126
|
+
parentEventId,
|
|
127
|
+
language
|
|
128
|
+
});
|
|
129
|
+
set(name, producer);
|
|
130
|
+
await next();
|
|
131
|
+
if (finalizeLogHandler) {
|
|
132
|
+
await producer(
|
|
133
|
+
finalizeLogHandler,
|
|
134
|
+
logger.dump()
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const setResponseHandlers = (c) => {
|
|
140
|
+
return {
|
|
141
|
+
raw: (data) => {
|
|
142
|
+
const { status, ...rest } = data;
|
|
143
|
+
return c.json(rest, status || 200);
|
|
144
|
+
},
|
|
145
|
+
success: (message, data, status) => {
|
|
146
|
+
const statusCode = status || 200;
|
|
147
|
+
return c.json({ message, data }, statusCode);
|
|
148
|
+
},
|
|
149
|
+
successNoContent: (message, status) => {
|
|
150
|
+
const statusCode = status || 200;
|
|
151
|
+
return c.json({ message }, statusCode);
|
|
152
|
+
},
|
|
153
|
+
error: (message, status) => {
|
|
154
|
+
throw new HTTPException(status, {
|
|
155
|
+
message
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
websocket: (socket) => {
|
|
159
|
+
return new Response(null, {
|
|
160
|
+
status: 101,
|
|
161
|
+
webSocket: socket
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
const response = createMiddleware(async (c, next) => {
|
|
167
|
+
c.set("res", setResponseHandlers(c));
|
|
168
|
+
if (c.var.eventId) {
|
|
169
|
+
c.header("x-event-id", c.var.eventId);
|
|
170
|
+
}
|
|
171
|
+
await next();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
async function deriveRawKey(input, salt, iterations) {
|
|
175
|
+
const encoder = new TextEncoder();
|
|
176
|
+
const baseKey = await crypto.subtle.importKey(
|
|
177
|
+
"raw",
|
|
178
|
+
encoder.encode(input),
|
|
179
|
+
{ name: "PBKDF2" },
|
|
180
|
+
false,
|
|
181
|
+
["deriveBits"]
|
|
182
|
+
);
|
|
183
|
+
const derivedBits = await crypto.subtle.deriveBits(
|
|
184
|
+
{
|
|
185
|
+
name: "PBKDF2",
|
|
186
|
+
salt: salt.buffer,
|
|
187
|
+
iterations,
|
|
188
|
+
hash: "SHA-256"
|
|
189
|
+
},
|
|
190
|
+
baseKey,
|
|
191
|
+
256
|
|
192
|
+
);
|
|
193
|
+
return new Uint8Array(derivedBits);
|
|
194
|
+
}
|
|
195
|
+
async function hash$1(input, salt, iterations = 6e5) {
|
|
196
|
+
const encoder = new TextEncoder();
|
|
197
|
+
const saltBuffer = encoder.encode(salt);
|
|
198
|
+
const hashBuffer = await deriveRawKey(input, saltBuffer, iterations);
|
|
199
|
+
return Array.from(hashBuffer).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
200
|
+
}
|
|
201
|
+
async function verify(input, salt, storedHash, iterations) {
|
|
202
|
+
const encoder = new TextEncoder();
|
|
203
|
+
const saltBuffer = encoder.encode(salt);
|
|
204
|
+
const generatedBuffer = await deriveRawKey(input, saltBuffer, iterations);
|
|
205
|
+
const storedBuffer = new Uint8Array(
|
|
206
|
+
storedHash.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
|
|
207
|
+
);
|
|
208
|
+
if (generatedBuffer.length !== storedBuffer.length) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
let result = 0;
|
|
212
|
+
for (let i = 0; i < generatedBuffer.length; i++) {
|
|
213
|
+
result |= generatedBuffer[i] ^ storedBuffer[i];
|
|
214
|
+
}
|
|
215
|
+
return result === 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const pbkdf2 = {
|
|
219
|
+
__proto__: null,
|
|
220
|
+
hash: hash$1,
|
|
221
|
+
verify: verify
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
function generateSalt(length = 16) {
|
|
225
|
+
const buffer = new Uint8Array(length);
|
|
226
|
+
crypto.getRandomValues(buffer);
|
|
227
|
+
return btoa(String.fromCharCode(...buffer));
|
|
228
|
+
}
|
|
229
|
+
async function hash({
|
|
230
|
+
input,
|
|
231
|
+
algorithm = "SHA-256",
|
|
232
|
+
pepper,
|
|
233
|
+
salt
|
|
234
|
+
}) {
|
|
235
|
+
let textToHash = input;
|
|
236
|
+
if (pepper) {
|
|
237
|
+
textToHash = `${pepper}${input}`;
|
|
238
|
+
}
|
|
239
|
+
if (salt) {
|
|
240
|
+
textToHash = `${textToHash}${salt}`;
|
|
241
|
+
}
|
|
242
|
+
const buffer = new TextEncoder().encode(textToHash);
|
|
243
|
+
const hashBuffer = await crypto.subtle.digest(algorithm, buffer);
|
|
244
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
245
|
+
const hashedString = hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
246
|
+
return hashedString;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const sha = {
|
|
250
|
+
__proto__: null,
|
|
251
|
+
generateSalt: generateSalt,
|
|
252
|
+
hash: hash
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const clientInfo = (config) => createMiddleware(async ({ env, req, set, get }, next) => {
|
|
256
|
+
const logger = config?.useLogger ? (get("logger") ?? new Logger({
|
|
257
|
+
details: {
|
|
258
|
+
service: "dummy"
|
|
259
|
+
}
|
|
260
|
+
})).getArea("middeware:clientInfo") : void 0;
|
|
261
|
+
if (!req.raw.cf) {
|
|
262
|
+
throw new Error("Cloudflare data is not available");
|
|
263
|
+
}
|
|
264
|
+
logger?.debug("Retrieving hash secret from environment");
|
|
265
|
+
const hashSecret = env[config?.hashSecretBinding ?? "HASH_SECRET"];
|
|
266
|
+
if (!hashSecret) {
|
|
267
|
+
throw new Error("Hash secret is not available");
|
|
268
|
+
}
|
|
269
|
+
const {
|
|
270
|
+
latitude,
|
|
271
|
+
longitude,
|
|
272
|
+
city,
|
|
273
|
+
country,
|
|
274
|
+
continent,
|
|
275
|
+
colo,
|
|
276
|
+
asn,
|
|
277
|
+
isEUCountry,
|
|
278
|
+
region,
|
|
279
|
+
regionCode,
|
|
280
|
+
postalCode,
|
|
281
|
+
timezone
|
|
282
|
+
} = req.raw.cf;
|
|
283
|
+
logger?.debug("Forming cloudflare client data object");
|
|
284
|
+
const cf = {
|
|
285
|
+
latitude,
|
|
286
|
+
longitude,
|
|
287
|
+
city,
|
|
288
|
+
country,
|
|
289
|
+
continent,
|
|
290
|
+
colo,
|
|
291
|
+
asn,
|
|
292
|
+
isEUCountry: Boolean(isEUCountry),
|
|
293
|
+
region,
|
|
294
|
+
regionCode,
|
|
295
|
+
postalCode,
|
|
296
|
+
timezone
|
|
297
|
+
};
|
|
298
|
+
logger?.debug("Parsing user agent");
|
|
299
|
+
const userAgent = req.raw.headers.get("user-agent") ?? "Unknown";
|
|
300
|
+
const parser = new UAParser(userAgent);
|
|
301
|
+
const userAgentParsed = parser.getResult();
|
|
302
|
+
const { browser, device, engine, os, cpu } = userAgentParsed;
|
|
303
|
+
const ua = {
|
|
304
|
+
browser: {
|
|
305
|
+
name: browser.name ?? "Unknown",
|
|
306
|
+
version: browser.version ?? "Unknown"
|
|
307
|
+
},
|
|
308
|
+
device: {
|
|
309
|
+
model: device.model ?? "Unknown",
|
|
310
|
+
vendor: device.vendor ?? "Unknown",
|
|
311
|
+
type: device.type ?? "desktop"
|
|
312
|
+
},
|
|
313
|
+
engine: {
|
|
314
|
+
name: engine.name ?? "Unknown",
|
|
315
|
+
version: engine.version ?? "Unknown"
|
|
316
|
+
},
|
|
317
|
+
os: {
|
|
318
|
+
name: os.name ?? "Unknown",
|
|
319
|
+
version: os.version ?? "Unknown"
|
|
320
|
+
},
|
|
321
|
+
cpu: cpu.architecture ?? "Unknown"
|
|
322
|
+
};
|
|
323
|
+
logger?.debug("Getting ip address");
|
|
324
|
+
const ip = req.raw.headers.get("CF-Connecting-IP") ?? "127.0.0.1";
|
|
325
|
+
const clientContent = { ...cf, ...ua, ip };
|
|
326
|
+
logger?.debug("Setting client data into context");
|
|
327
|
+
set("client", {
|
|
328
|
+
...clientContent,
|
|
329
|
+
userAgent,
|
|
330
|
+
securityHash: await hash({
|
|
331
|
+
input: config?.securityHashString?.(clientContent) ?? `${ip}${city}${country}${userAgent}`,
|
|
332
|
+
algorithm: "SHA-512",
|
|
333
|
+
pepper: hashSecret
|
|
334
|
+
})
|
|
335
|
+
});
|
|
336
|
+
logger?.info("Client data variable set");
|
|
337
|
+
await next();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
class QueueHandler {
|
|
341
|
+
/**
|
|
342
|
+
* @param config - Configuration object.
|
|
343
|
+
* @param config.schema - The Zod schema used to parse and validate incoming message content.
|
|
344
|
+
* @param config.setContext - A factory function to generate context (e.g., DB connections, logging) for each message.
|
|
345
|
+
*/
|
|
346
|
+
constructor(config) {
|
|
347
|
+
this.config = config;
|
|
348
|
+
}
|
|
349
|
+
handlers = {};
|
|
350
|
+
/**
|
|
351
|
+
* Registers a specific processing function for a given message type (handlerName).
|
|
352
|
+
* * @template {keyof z.infer<Schema>} HandlerName - A specific key defined in your Zod schema.
|
|
353
|
+
* @param handlerName - The key identifying which handler to use.
|
|
354
|
+
* @param handler - The asynchronous logic to execute when this message type is received.
|
|
355
|
+
* @returns The current instance for fluent chaining.
|
|
356
|
+
*/
|
|
357
|
+
addHandler(handlerName, handler) {
|
|
358
|
+
this.handlers[handlerName] = handler;
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Generates the Cloudflare Worker queue consumer function.
|
|
363
|
+
* * This handles the batch processing loop, parses the message body against the
|
|
364
|
+
* schema, injects context, and manages concurrency via `Promise.allSettled`.
|
|
365
|
+
* * @returns An async function compatible with the Cloudflare `queue` export.
|
|
366
|
+
*/
|
|
367
|
+
getConsumer() {
|
|
368
|
+
return async (batch, env) => {
|
|
369
|
+
await Promise.allSettled(
|
|
370
|
+
batch.messages.map(
|
|
371
|
+
async ({ body: { handler, content, ...restMessage }, ...rest }) => {
|
|
372
|
+
const handlerFunction = this.handlers[handler];
|
|
373
|
+
if (!handlerFunction) {
|
|
374
|
+
console.warn(
|
|
375
|
+
`No handler registered for handlerName "${String(handler)}"`
|
|
376
|
+
);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const parsedContent = this.config.schema.shape[handler].parse(content);
|
|
380
|
+
const { retry, ack, ...metadata } = rest;
|
|
381
|
+
const context = this.config.setContext(env, restMessage);
|
|
382
|
+
await handlerFunction(parsedContent, {
|
|
383
|
+
context: { ...context, retry, ack },
|
|
384
|
+
metadata: {
|
|
385
|
+
...restMessage,
|
|
386
|
+
handler,
|
|
387
|
+
...metadata
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
)
|
|
392
|
+
);
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Creates a factory function for sending typed messages to a specific queue.
|
|
397
|
+
* * @param queue - The Cloudflare Queue binding (e.g., `env.MY_QUEUE`).
|
|
398
|
+
* @param params - Global parameters to include in every message (e.g., trace IDs).
|
|
399
|
+
* @returns A producer function that accepts a `handler` key and the corresponding typed content.
|
|
400
|
+
*/
|
|
401
|
+
getProducer(queue, params) {
|
|
402
|
+
const createId = init({ fingerprint: "queue" });
|
|
403
|
+
return async (handler, content) => {
|
|
404
|
+
const eventId = createId();
|
|
405
|
+
return queue.send(
|
|
406
|
+
{ content, handler, eventId, ...params },
|
|
407
|
+
{ contentType: "json" }
|
|
408
|
+
);
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const defaultMessageMap = {
|
|
414
|
+
internalError: "internal-error",
|
|
415
|
+
notFound: "not-found"
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const onError = (parseError) => async (err, { json, env, get }) => {
|
|
419
|
+
try {
|
|
420
|
+
const status = "status" in err ? err.status : 500;
|
|
421
|
+
return json(
|
|
422
|
+
{
|
|
423
|
+
message: !parseError ? err.message : await parseError(err, env, get) ?? defaultMessageMap.internalError
|
|
424
|
+
},
|
|
425
|
+
status
|
|
426
|
+
);
|
|
427
|
+
} catch (error) {
|
|
428
|
+
console.error("Failed on error handler:", err);
|
|
429
|
+
console.error("Failed to parse error:", error);
|
|
430
|
+
return json({ message: defaultMessageMap.internalError }, 500);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const onNotFound = async (c) => {
|
|
435
|
+
return c.json(
|
|
436
|
+
{
|
|
437
|
+
message: defaultMessageMap.notFound
|
|
438
|
+
},
|
|
439
|
+
404
|
|
440
|
+
);
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const createTypedClient = () => {
|
|
444
|
+
return (options) => async (fn, callbacks) => {
|
|
445
|
+
const client = hc(options.url, {
|
|
446
|
+
headers: options.headers,
|
|
447
|
+
fetch: options.fetch
|
|
448
|
+
});
|
|
449
|
+
callbacks?.onStart?.();
|
|
450
|
+
let responseHeaders = new Headers();
|
|
451
|
+
try {
|
|
452
|
+
const response = await fn(client);
|
|
453
|
+
responseHeaders = response.headers;
|
|
454
|
+
const data = await parseResponse(response);
|
|
455
|
+
callbacks?.onSuccess?.(data, responseHeaders);
|
|
456
|
+
return data;
|
|
457
|
+
} catch (err) {
|
|
458
|
+
const { detail, statusCode } = err;
|
|
459
|
+
const status = statusCode ?? 500;
|
|
460
|
+
if (!detail) {
|
|
461
|
+
options.errorHandler?.(status, {
|
|
462
|
+
message: "Fetch malformed"
|
|
463
|
+
});
|
|
464
|
+
throw new HTTPException(status, { message: "Fetch malformed" });
|
|
465
|
+
}
|
|
466
|
+
const { message } = detail;
|
|
467
|
+
const eventId = responseHeaders.get("x-event-id") ?? void 0;
|
|
468
|
+
options.errorHandler?.(status, { ...detail, eventId, status });
|
|
469
|
+
callbacks?.onError?.(detail, responseHeaders);
|
|
470
|
+
throw new HTTPException(status, { message });
|
|
471
|
+
} finally {
|
|
472
|
+
callbacks?.onEnd?.();
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
export { pbkdf2 as PBKDF2, QueueHandler, sha as SHA, clientInfo, createTypedClient, i18n, isBot, jsonValidator, logger, onError, onNotFound, queue, response };
|
|
478
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/middleware/logger.ts","../src/middleware/validator.ts","../src/middleware/isBot.ts","../src/middleware/i18n.ts","../src/middleware/queue.ts","../src/middleware/response.ts","../src/crypto/pbkdf2.ts","../src/crypto/sha.ts","../src/middleware/clientInfo.ts","../src/queue/QueueHandler.ts","../src/router/constants.ts","../src/router/onError.ts","../src/router/onNotFound.ts","../src/client/createTypedClient.ts"],"sourcesContent":["import { createMiddleware } from 'hono/factory';\nimport { type Details, Logger } from 'hierarchical-area-logger';\n\nexport type HonoLoggerVariables = {\n logger: Logger;\n eventId: string;\n};\n\n/**\n * Logger middleware\n *\n * @description\n * This middleware sets up a logger.\n *\n * @param details - Details to pass to the logger\n * @param parentEventIdHeader - Header name to use for parent event ID\n *\n * @example\n * ```ts\n * app.use('*', logger(\n * {\n * service: 'logger',\n * },\n * parentEventIdHeader: 'parent-event-id',\n * ));\n * ```\n */\nexport const logger = (details: Details, parentEventIdHeader?: string) =>\n createMiddleware<{\n Variables: HonoLoggerVariables;\n }>(async (c, next) => {\n const logger = new Logger({\n details,\n withParentEventId: !!parentEventIdHeader,\n parentEventId: parentEventIdHeader\n ? (c.req.raw.headers.get(parentEventIdHeader) ?? undefined)\n : undefined,\n defaultArea: new URL(c.req.raw.url).pathname,\n path: new URL(c.req.raw.url).pathname,\n });\n\n c.set('logger', logger);\n c.set('eventId', logger.eventId);\n\n await next();\n });\n","import { HTTPException } from 'hono/http-exception';\nimport { validator } from 'hono/validator';\nimport { z } from 'zod';\n\n/**\n * @description\n * Validator middleware for JSON data.\n *\n * @param schema - Zod schema for validation\n *\n * @example\n * ```ts\n * app.post('/user', jsonValidator(z.object({\n * name: z.string(),\n * age: z.number(),\n * })), (c) => {\n * const { name, age } = await c.req.valid('json');\n * return c.json({ name, age });\n * });\n * ```\n */\nexport const jsonValidator = <T extends z.ZodRawShape>(\n schema: z.ZodObject<T>\n) =>\n validator('json', (value) => {\n const parsed = schema.safeParse(value);\n if (!parsed.success) {\n throw new HTTPException(400, {\n message: z.prettifyError(parsed.error),\n });\n }\n return parsed.data;\n });\n","import { HTTPException } from 'hono/http-exception';\nimport { Logger } from 'hierarchical-area-logger';\nimport type { IncomingRequestCfPropertiesBotManagementBase } from '@cloudflare/workers-types';\nimport { createMiddleware } from 'hono/factory';\nimport { MiddlewareWithLoggingCapability } from './types';\nimport { HonoLoggerVariables } from './logger';\n\nexport type HonoIsBotVariables = {\n isBot: boolean;\n};\n\n/**\n * Middleware to detect and optionally block requests based on Cloudflare's Bot Management score.\n * @param {Object} options - Configuration options for bot detection.\n * @param {number} [options.threshold=49] - The bot score threshold (1-100).\n * Cloudflare scores below this value are treated as bots. A lower score indicates a higher\n * probability that the request is automated.\n * @param {boolean} [options.allowVerifiedBot] - If true, the middleware strictly requires\n * bots to be \"verified\" (e.g., Googlebot, Bingbot).\n * Note: If this is enabled, non-verified bots will be blocked regardless of their score.\n * @returns {MiddlewareHandler} A Hono middleware handler.\n * @throws {HTTPException} Throws a 401 \"Bot detected\" error if the score is below the\n * threshold or if a non-verified bot is detected when `allowVerifiedBot` is true.\n * @example\n * ```ts\n * const app = new Hono<{ Variables: HonoIsBotVariables }>();\n * // Block suspected bots with a score lower than 50\n * app.use('/api/*', isBot({ threshold: 50, allowVerifiedBot: true }));\n * ```\n */\nexport const isBot: MiddlewareWithLoggingCapability<{\n threshold: number;\n allowVerifiedBot?: boolean;\n}> = (\n { threshold, allowVerifiedBot, useLogger } = {\n threshold: 49,\n }\n) =>\n createMiddleware<{\n Variables: HonoIsBotVariables & HonoLoggerVariables;\n }>(async (c, next) => {\n const logger = useLogger\n ? (\n c.get('logger') ??\n new Logger({\n details: {\n service: 'dummy',\n },\n })\n ).getArea('middeware:isBot')\n : undefined;\n\n logger?.debug('Retrieving bot management data from cloudflare data');\n const botManagement = c.req.raw.cf?.botManagement as\n IncomingRequestCfPropertiesBotManagementBase | undefined;\n\n if (!botManagement) {\n throw new Error('botManagement is not available');\n }\n\n const score = botManagement?.score ?? 54;\n const verifiedBot = botManagement?.verifiedBot ?? false;\n\n // Determine if this request should be treated as a bot based on score,\n // allowing verified bots when configured.\n const isBotByScore = score < threshold;\n const isVerifiedAllowed = !!(allowVerifiedBot && verifiedBot);\n const isBot = isBotByScore;\n\n logger?.debug('Setting isBot variable in context', {\n isBotByScore,\n isVerifiedAllowed,\n isBot,\n });\n\n logger?.info('isBot variable set');\n c.set('isBot', isBot);\n\n // Block non-verified bots when score is below threshold, or when non-verified\n // bots are not allowed by configuration.\n if (isBotByScore && !isVerifiedAllowed) {\n throw new HTTPException(401, { message: 'Bot detected' });\n }\n\n await next();\n });\n","import { createMiddleware } from 'hono/factory';\n\nexport type HonoLanguageVariables = {\n language: string;\n};\n\ntype I18nShape = {\n [key: string]: I18nShape | string;\n};\n\nexport type HonoI18nVariables = {\n t: ReturnType<typeof setI18n>;\n};\n\n/**\n * Set i18n\n *\n * @param translations - Translations object\n * @param language - Language\n * @param defaultLanguage - Default language\n */\nconst setI18n =\n <T extends I18nShape>(\n translations: T,\n language: keyof T,\n defaultLanguage: string\n ) =>\n (\n input: string,\n params?: Record<string, string | number>,\n overrideLanguage?: keyof T\n ): string => {\n const result = input.split('.').reduce(\n (acc: unknown, key: string) => {\n if (acc && typeof acc === 'object' && key in acc) {\n return (acc as I18nShape)[key];\n } else {\n if (\n translations[defaultLanguage] &&\n typeof translations[defaultLanguage] === 'object' &&\n key in translations[defaultLanguage]\n ) {\n return (translations[defaultLanguage] as I18nShape)[key];\n } else {\n return input;\n }\n }\n },\n translations[overrideLanguage ?? language]\n );\n\n if (params) {\n return Object.keys(params).reduce(\n (acc, key) => {\n return acc.replace(\n new RegExp(`{{${key}}}`, 'g'),\n params[key].toString()\n );\n },\n typeof result === 'string' ? result : input\n );\n }\n\n return typeof result === 'string' ? result : input;\n };\n\n/**\n * I18n Middleware and Queue Factory.\n * @description\n * Sets up the `t` translation function in the Hono context.\n * Note: Requires a preceding middleware to set the `language` variable.\n * @template T - The shape of the translations object.\n * @param translations - The dictionary of translations.\n * @param defaultLanguage - The fallback language code.\n * @returns An object containing the `middleware` for Hono and a `queue` helper for background tasks.\n * @example\n * ```ts\n * const translations = { en: { hi: \"Hi {{name}}\" }, fr: { hi: \"Salut {{name}}\" } };\n * // 1. Set language first\n * app.use('*', async (c, next) => {\n * c.set('language', 'en');\n * await next();\n * });\n * // 2. Initialize i18n\n * const { middleware } = i18n(translations, 'en');\n * app.use('*', middleware);\n * // 3. Use in routes\n * app.get('/', (c) => c.text(c.var.t('hi', { name: 'User' })));\n * ```\n */\nexport const i18n = <T extends I18nShape>(\n translations: T,\n defaultLanguage: string\n) => ({\n middleware: createMiddleware<{\n Variables: HonoLanguageVariables & HonoI18nVariables;\n }>(async ({ set, var: { language } }, next) => {\n if (!language) {\n throw new Error(\n 'Language variable should be initialized before translations middleware'\n );\n }\n set('t', setI18n<T>(translations, language, defaultLanguage));\n await next();\n }),\n queue: (language?: keyof T) =>\n setI18n(translations, language ?? defaultLanguage, defaultLanguage),\n});\n","import { createMiddleware } from 'hono/factory';\nimport z, { type ZodObject } from 'zod';\nimport type { Queue } from '@cloudflare/workers-types';\nimport { QueueHandler } from '../queue/QueueHandler';\nimport { HonoLoggerVariables } from './logger';\n\n/**\n * Middleware to initialize a Queue producer and attach it to the Hono context.\n * @description\n * This middleware manages Cloudflare Queue interactions. If `finalizeLogHandler` is provided,\n * it will automatically capture the current logger state and send it to the queue after\n * the request is processed (post-`next()`).\n * @param options.name - The name of the Queue binding defined in your `wrangler.toml`.\n * @param options.queueHandler - An instance of your custom QueueHandler.\n * @param [options.finalizeLogHandler] - Optional: The specific message type/key to use when finalizing the log.\n * @example\n * ```ts\n * // OPTIONAL\n * app.use('*', logger(\n * {\n * service: 'logger',\n * },\n * parentEventIdHeader: 'parent-event-id',\n * ));\n *\n * app.use('*', queue({\n * name: 'queue',\n * queueHandler: new QueueHandler(),\n * finalizeLogHandler: 'log'\n * }));\n * ```\n */\nexport const queue = <\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Schema extends ZodObject<any>,\n Environment,\n Context,\n Key extends keyof z.infer<Schema>,\n>({\n name,\n queueHandler,\n finalizeLogHandler,\n}: {\n name: string;\n queueHandler: QueueHandler<Schema, Environment, Context>;\n finalizeLogHandler?: Key;\n}) =>\n createMiddleware<{\n Bindings: Record<string, Queue>;\n Variables: HonoLoggerVariables &\n Record<\n string,\n ReturnType<QueueHandler<Schema, Environment, Context>['getProducer']>\n >;\n }>(async ({ env, set, get }, next) => {\n const queueEnv = env[name] as Queue | undefined;\n\n const language = get('language') as unknown as string | undefined;\n\n if (!queueEnv) {\n throw new Error(\n `Queue environment variable ${name as string} is not defined`\n );\n }\n\n const logger = get('logger');\n\n let parentEventId: string | undefined;\n\n if (finalizeLogHandler) {\n if (!logger) {\n throw new Error('Logger should be initialized before queue middleware');\n }\n parentEventId = logger?.eventId;\n }\n\n const producer = queueHandler.getProducer(queueEnv, {\n parentEventId,\n language,\n });\n set(name, producer);\n\n await next();\n\n if (finalizeLogHandler) {\n await producer(\n finalizeLogHandler,\n logger!.dump() as z.infer<Schema>[Key]\n );\n }\n });\n","import type { Context } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport type {\n ClientErrorStatusCode,\n ContentfulStatusCode,\n SuccessStatusCode,\n} from 'hono/utils/http-status';\nimport { HTTPException } from 'hono/http-exception';\nimport type { HonoLoggerVariables } from './logger';\n\nexport type HonoResponseVariables = {\n res: ReturnType<typeof setResponseHandlers>;\n};\n\nconst setResponseHandlers = (c: Context) => {\n return {\n raw: <T extends { status?: ContentfulStatusCode }>(data: T) => {\n const { status, ...rest } = data;\n return c.json(rest, status || (200 as ContentfulStatusCode));\n },\n success: <T extends object>(\n message: string,\n data: T,\n status?: SuccessStatusCode\n ) => {\n const statusCode: ContentfulStatusCode =\n (status as ContentfulStatusCode) || 200;\n\n return c.json({ message, data }, statusCode);\n },\n successNoContent: (message: string, status?: SuccessStatusCode) => {\n const statusCode: ContentfulStatusCode =\n (status as ContentfulStatusCode) || 200;\n return c.json({ message }, statusCode);\n },\n error: (message: string, status: ClientErrorStatusCode) => {\n throw new HTTPException(status, {\n message,\n });\n },\n websocket: (socket: WebSocket) => {\n return new Response(null, {\n status: 101,\n webSocket: socket,\n });\n },\n };\n};\n\n/**\n * Response Middleware\n * @description\n * Injects a `res` object into the Hono context (`c.var.res`).\n * This ensures all outgoing responses follow a consistent schema and include\n * tracing metadata (like `eventId`) automatically.\n * @example\n * ```ts\n * app.use('*', response());\n * ```\n */\nexport const response = createMiddleware<{\n Variables: HonoResponseVariables & HonoLoggerVariables;\n}>(async (c, next) => {\n c.set('res', setResponseHandlers(c));\n if (c.var.eventId) {\n c.header('x-event-id', c.var.eventId);\n }\n await next();\n});\n","/**\n * Internal helper to derive raw cryptographic bits using the PBKDF2 algorithm.\n * * @internal\n * @param {string} input - The plain-text string (password/key) to be hashed.\n * @param {Uint8Array} salt - A cryptographically random salt. Recommended minimum 16 bytes.\n * @param {number} iterations - The number of iterations to perform. (e.g., 600,000).\n * @returns {Promise<Uint8Array>} A promise that resolves to the derived 256-bit key.\n * @throws {Error} If the crypto operation fails or parameters are invalid.\n */\nasync function deriveRawKey(\n input: string,\n salt: Uint8Array,\n iterations: number\n): Promise<Uint8Array> {\n const encoder = new TextEncoder();\n\n const baseKey = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(input),\n { name: 'PBKDF2' },\n false,\n ['deriveBits']\n );\n\n const derivedBits = await crypto.subtle.deriveBits(\n {\n name: 'PBKDF2',\n salt: salt.buffer as ArrayBuffer,\n iterations: iterations,\n hash: 'SHA-256',\n },\n baseKey,\n 256\n );\n\n return new Uint8Array(derivedBits);\n}\n\n/**\n * Derives a secure hex-encoded hash from an input string using PBKDF2-HMAC-SHA256.\n * * @example\n * const passwordHash = await hash(\"myPassword123\", \"random-salt-string\");\n * * @param {string} input - The plain-text string to hash.\n * @param {string} salt - The salt string used to randomize the hash.\n * @param {number} [iterations=600000] - The CPU cost factor. Default is 600,000.\n * @returns {Promise<string>} A hex-encoded string representing the derived key.\n */\nexport async function hash(\n input: string,\n salt: string,\n iterations: number = 600000\n): Promise<string> {\n const encoder = new TextEncoder();\n const saltBuffer = encoder.encode(salt);\n const hashBuffer = await deriveRawKey(input, saltBuffer, iterations);\n\n return Array.from(hashBuffer)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Verifies if an input string matches a stored hash using a constant-time comparison.\n * This prevents timing attacks by ensuring the execution time does not reveal\n * how many characters of the hash were correct.\n * * @param {string} input - The plain-text string to verify.\n * @param {string} salt - The salt used during the original hashing.\n * @param {string} storedHash - The hex-encoded hash previously stored in the database.\n * @param {number} iterations - The iteration count used for the original hash.\n * @returns {Promise<boolean>} Resolves to `true` if the input matches the hash, otherwise `false`.\n */\nexport async function verify(\n input: string,\n salt: string,\n storedHash: string,\n iterations: number\n): Promise<boolean> {\n const encoder = new TextEncoder();\n const saltBuffer = encoder.encode(salt);\n\n // Generate the current hash\n const generatedBuffer = await deriveRawKey(input, saltBuffer, iterations);\n\n // Convert stored hex back to buffer for a pure byte-to-byte comparison\n const storedBuffer = new Uint8Array(\n storedHash.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n );\n\n // Constant-time comparison on bytes\n if (generatedBuffer.length !== storedBuffer.length) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < generatedBuffer.length; i++) {\n result |= generatedBuffer[i] ^ storedBuffer[i];\n }\n\n return result === 0;\n}\n","/**\n * Generates a cryptographically strong random salt.\n */\nexport function generateSalt(length: number = 16): string {\n const buffer = new Uint8Array(length);\n crypto.getRandomValues(buffer);\n return btoa(String.fromCharCode(...buffer));\n}\n\n/**\n * Calculates the SHA hash of the given input string.\n * @param algorithm - The algorithm to be used for hashing. Defaults to 256.\n * @param pepper - An optional string to be prepended to the input before hashing.\n * @param salt - An optional string to be appended to the input before hashing.\n * @returns A Promise that resolves to the hashed string.\n */\nexport async function hash({\n input,\n algorithm = 'SHA-256',\n pepper,\n salt,\n}: {\n input: string;\n algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512';\n pepper?: string;\n salt?: string;\n}): Promise<string> {\n let textToHash = input;\n if (pepper) {\n textToHash = `${pepper}${input}`;\n }\n if (salt) {\n textToHash = `${textToHash}${salt}`;\n }\n const buffer = new TextEncoder().encode(textToHash);\n const hashBuffer = await crypto.subtle.digest(algorithm, buffer);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashedString = hashArray\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n\n return hashedString;\n}\n","import { createMiddleware } from 'hono/factory';\nimport { UAParser } from 'ua-parser-js';\nimport type {\n Iso3166Alpha2Code,\n ContinentCode,\n} from '@cloudflare/workers-types';\nimport { SHA } from '../crypto';\nimport { MiddlewareWithLoggingCapability } from './types';\nimport { Logger } from 'hierarchical-area-logger';\nimport { HonoLoggerVariables } from './logger';\n\ntype CFData = {\n isEUCountry: boolean;\n latitude: string;\n longitude: string;\n city: string;\n country: Iso3166Alpha2Code | 'T1';\n continent: ContinentCode;\n region?: string;\n regionCode?: string;\n postalCode: string;\n timezone: string;\n colo: string;\n asn?: number;\n};\n\ntype UserAgentData = {\n browser: {\n name: string;\n version: string;\n };\n device: {\n model: string;\n vendor: string;\n type: UAParser.IDevice['type'] | 'desktop';\n };\n engine: {\n name: string;\n version: string;\n };\n os: {\n name: string;\n version: string;\n };\n cpu: string;\n};\n\nexport type HonoClientInfoVariables = {\n client: CFData & {\n ip: string;\n userAgent: string;\n securityHash: string;\n } & UserAgentData;\n};\n\n/**\n * Hono middleware that extracts and injects comprehensive client information\n * into the request context, including geolocation data from Cloudflare and\n * parsed User-Agent details.\n * @param {Object} [config] - Optional configuration for the middleware.\n * @param {Function} [config.securityHashString] - A custom function to generate the seed string\n * used for the security hash. Receives Cloudflare and User-Agent data as input.\n * Defaults to `${city}${country}${continent}`.\n * @param {string} [config.hashSecretBinding] - The name of the environment variable\n * containing the hash secret. Defaults to 'HASH_SECRET'.\n * @returns {MiddlewareHandler} A Hono middleware handler.\n * @throws {Error} Throws an error if Cloudflare (`req.raw.cf`) data is missing.\n * @example\n * ```ts\n * const app = new Hono<{ Variables: HonoClientInfoVariables }>();\n *\n * app.use('*', clientInfo({\n * useLogger: true // for debugging purposes\n * }));\n *\n * app.get('/me', (c) => {\n * const client = c.get('client');\n * return c.json({\n * location: `${client.city}, ${client.country}`,\n * device: client.device.type\n * });\n * });\n * ```\n */\nexport const clientInfo: MiddlewareWithLoggingCapability<{\n hashSecretBinding?: string;\n securityHashString?: (\n content: CFData & UserAgentData & { ip: string }\n ) => string;\n}> = (config) =>\n createMiddleware<{\n Bindings: Record<string, string>;\n Variables: HonoClientInfoVariables & HonoLoggerVariables;\n }>(async ({ env, req, set, get }, next) => {\n const logger = config?.useLogger\n ? (\n get('logger') ??\n new Logger({\n details: {\n service: 'dummy',\n },\n })\n ).getArea('middeware:clientInfo')\n : undefined;\n\n if (!req.raw.cf) {\n throw new Error('Cloudflare data is not available');\n }\n\n logger?.debug('Retrieving hash secret from environment');\n const hashSecret = env[config?.hashSecretBinding ?? 'HASH_SECRET'] as\n string | undefined;\n\n if (!hashSecret) {\n throw new Error('Hash secret is not available');\n }\n\n const {\n latitude,\n longitude,\n city,\n country,\n continent,\n colo,\n asn,\n isEUCountry,\n region,\n regionCode,\n postalCode,\n timezone,\n } = req.raw.cf as CFData;\n\n logger?.debug('Forming cloudflare client data object');\n const cf = {\n latitude,\n longitude,\n city,\n country,\n continent,\n colo,\n asn,\n isEUCountry: Boolean(isEUCountry),\n region,\n regionCode,\n postalCode,\n timezone,\n };\n\n logger?.debug('Parsing user agent');\n const userAgent = req.raw.headers.get('user-agent') ?? 'Unknown';\n const parser = new UAParser(userAgent);\n const userAgentParsed = parser.getResult();\n\n const { browser, device, engine, os, cpu } = userAgentParsed;\n\n const ua = {\n browser: {\n name: browser.name ?? 'Unknown',\n version: browser.version ?? 'Unknown',\n },\n device: {\n model: device.model ?? 'Unknown',\n vendor: device.vendor ?? 'Unknown',\n type: device.type ?? 'desktop',\n },\n engine: {\n name: engine.name ?? 'Unknown',\n version: engine.version ?? 'Unknown',\n },\n os: {\n name: os.name ?? 'Unknown',\n version: os.version ?? 'Unknown',\n },\n cpu: cpu.architecture ?? 'Unknown',\n };\n\n logger?.debug('Getting ip address');\n const ip =\n (req.raw.headers.get('CF-Connecting-IP') as string) ?? '127.0.0.1';\n\n const clientContent = { ...cf, ...ua, ip };\n\n logger?.debug('Setting client data into context');\n set('client', {\n ...clientContent,\n userAgent,\n securityHash: await SHA.hash({\n input:\n config?.securityHashString?.(clientContent) ??\n `${ip}${city}${country}${userAgent}`,\n algorithm: 'SHA-512',\n pepper: hashSecret,\n }),\n });\n logger?.info('Client data variable set');\n await next();\n });\n","import { init } from '@paralleldrive/cuid2';\nimport type { z, ZodObject } from 'zod';\nimport type { Queue, MessageBatch } from '@cloudflare/workers-types';\nimport type {\n GenericQueueData,\n Variables,\n Handler,\n QueueSendParams,\n MessageHandlers,\n ContextFn,\n} from './types';\n\n/**\n * A type-safe wrapper for Cloudflare Worker Queues using Zod for validation.\n * * This class orchestrates the relationship between incoming queue messages and\n * specific logic handlers, ensuring that data is validated at the edge before\n * reaching your business logic.\n *\n * @template Schema - The Zod schema defining the message shapes.\n * @template Environment - The Cloudflare Worker environment bindings (Env).\n * @template Context - The application context derived from the environment and message.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class QueueHandler<Schema extends ZodObject<any>, Environment, Context> {\n private readonly handlers: Partial<\n MessageHandlers<z.infer<Schema>, Context>\n > = {};\n\n /**\n * @param config - Configuration object.\n * @param config.schema - The Zod schema used to parse and validate incoming message content.\n * @param config.setContext - A factory function to generate context (e.g., DB connections, logging) for each message.\n */\n constructor(\n private readonly config: {\n schema: Schema;\n setContext: ContextFn<Environment, Context>;\n }\n ) {}\n\n /**\n * Registers a specific processing function for a given message type (handlerName).\n * * @template {keyof z.infer<Schema>} HandlerName - A specific key defined in your Zod schema.\n * @param handlerName - The key identifying which handler to use.\n * @param handler - The asynchronous logic to execute when this message type is received.\n * @returns The current instance for fluent chaining.\n */\n public addHandler<HandlerName extends keyof z.infer<Schema>>(\n handlerName: HandlerName,\n handler: Handler<z.infer<Schema>[HandlerName], Variables<Context>>\n ): this {\n this.handlers[handlerName] = handler;\n return this;\n }\n\n /**\n * Generates the Cloudflare Worker queue consumer function.\n * * This handles the batch processing loop, parses the message body against the\n * schema, injects context, and manages concurrency via `Promise.allSettled`.\n * * @returns An async function compatible with the Cloudflare `queue` export.\n */\n public getConsumer() {\n return async (\n batch: MessageBatch<GenericQueueData<z.infer<Schema>>>,\n env: Environment\n ): Promise<void> => {\n await Promise.allSettled(\n batch.messages.map(\n async ({ body: { handler, content, ...restMessage }, ...rest }) => {\n const handlerFunction =\n this.handlers[handler as keyof typeof this.handlers];\n if (!handlerFunction) {\n console.warn(\n `No handler registered for handlerName \"${String(handler)}\"`\n );\n return;\n }\n const parsedContent = this.config.schema.shape[\n handler as keyof typeof this.config.schema.shape\n ].parse(content) as z.infer<Schema>[typeof handler];\n const { retry, ack, ...metadata } = rest;\n const context = this.config.setContext(env, restMessage);\n await handlerFunction(parsedContent, {\n context: { ...context, retry, ack },\n metadata: {\n ...restMessage,\n handler: handler as string,\n ...metadata,\n },\n });\n }\n )\n );\n };\n }\n\n /**\n * Creates a factory function for sending typed messages to a specific queue.\n * * @param queue - The Cloudflare Queue binding (e.g., `env.MY_QUEUE`).\n * @param params - Global parameters to include in every message (e.g., trace IDs).\n * @returns A producer function that accepts a `handler` key and the corresponding typed content.\n */\n public getProducer(queue: Queue<unknown>, params: QueueSendParams) {\n const createId = init({ fingerprint: 'queue' });\n return async <HandlerName extends keyof z.infer<Schema>>(\n handler: HandlerName,\n content: z.infer<Schema>[HandlerName]\n ) => {\n const eventId = createId();\n return queue.send(\n { content, handler, eventId, ...params } as GenericQueueData<\n z.infer<Schema>\n >,\n { contentType: 'json' }\n );\n };\n }\n}\n","export const defaultMessageMap = {\n internalError: 'internal-error',\n notFound: 'not-found',\n};\n","import { HTTPException } from 'hono/http-exception';\nimport type { ErrorHandler, Env } from 'hono';\nimport { defaultMessageMap } from './constants';\n\nexport const onError =\n <Environment extends Env>(\n parseError?: (\n err: Error | HTTPException,\n env: Environment['Bindings'],\n get: <K extends keyof Environment['Variables']>(\n key: K\n ) => Environment['Variables'][K]\n ) => Promise<string>\n ): ErrorHandler<Environment> =>\n async (err: Error | HTTPException, { json, env, get }) => {\n try {\n const status =\n 'status' in err ? (err.status as HTTPException['status']) : 500;\n\n return json(\n {\n message: !parseError\n ? err.message\n : ((await parseError(err, env, get)) ??\n defaultMessageMap.internalError),\n },\n status\n );\n } catch (error) {\n console.error('Failed on error handler:', err);\n console.error('Failed to parse error:', error);\n return json({ message: defaultMessageMap.internalError }, 500);\n }\n };\n","import type { NotFoundHandler } from 'hono';\nimport { defaultMessageMap } from './constants';\n\nexport const onNotFound: NotFoundHandler = async (c) => {\n return c.json(\n {\n message: defaultMessageMap.notFound,\n },\n 404\n );\n};\n","import { hc } from 'hono/client';\nimport { parseResponse } from 'hono/client';\nimport type { ClientResponse } from 'hono/client';\nimport type { Hono } from 'hono';\nimport type { ContentfulStatusCode, StatusCode } from 'hono/utils/http-status';\nimport { HTTPException } from 'hono/http-exception';\n\ntype ErrorBody = Record<string, unknown>;\n\nexport interface TypedClientCallbacks {\n onStart?: () => void;\n onSuccess?: (parsedData: unknown, headers: Headers) => void;\n onError?: (parsedData: unknown, headers: Headers) => void;\n onEnd?: () => void;\n}\n\nexport interface CreateTypedClientOptions {\n url: string;\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n fetch?: typeof fetch;\n errorHandler?: (status: StatusCode | number, body?: ErrorBody) => never;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const createTypedClient = <TApp extends Hono<any, any, any>>() => {\n return (options: CreateTypedClientOptions) =>\n async <TSuccessData>(\n fn: (\n c: ReturnType<typeof hc<TApp>>\n ) => Promise<ClientResponse<TSuccessData>>,\n callbacks?: TypedClientCallbacks\n ): Promise<TSuccessData> => {\n const client = hc<TApp>(options.url, {\n headers: options.headers,\n fetch: options.fetch,\n });\n callbacks?.onStart?.();\n\n // We keep a reference to headers to use them in the catch block if needed\n let responseHeaders: Headers = new Headers();\n\n try {\n // 1. Execute the raw request\n const response = await fn(client);\n responseHeaders = response.headers;\n\n // 2. Use Hono's native parseResponse\n // This automatically:\n // - Checks response.ok (throws DetailedError if false)\n // - Parses JSON/Text based on Content-Type\n const data = await parseResponse(response);\n\n // 3. Handle Success\n callbacks?.onSuccess?.(data, responseHeaders);\n return data as TSuccessData;\n } catch (err) {\n // 4. Handle Errors\n const { detail, statusCode } = err as {\n detail?: TSuccessData;\n statusCode?: ContentfulStatusCode;\n };\n\n const status = statusCode ?? 500;\n\n if (!detail) {\n options.errorHandler?.(status, {\n message: 'Fetch malformed',\n });\n throw new HTTPException(status, { message: 'Fetch malformed' });\n }\n\n const { message } = detail as unknown as {\n message: string;\n };\n\n const eventId = responseHeaders.get('x-event-id') ?? undefined;\n\n options.errorHandler?.(status, { ...detail, eventId, status });\n callbacks?.onError?.(detail, responseHeaders);\n\n throw new HTTPException(status, { message });\n } finally {\n callbacks?.onEnd?.();\n }\n };\n};\n"],"names":["logger","isBot","hash","SHA.hash"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hono-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"prepare": "husky",
|
|
7
|
-
"build": "
|
|
8
|
-
"dev": "
|
|
7
|
+
"build": "unbuild",
|
|
8
|
+
"dev": "unbuild --stub",
|
|
9
9
|
"test": "vitest",
|
|
10
10
|
"test:ui": "vitest --ui",
|
|
11
11
|
"coverage": "vitest --coverage",
|
|
@@ -33,29 +33,28 @@
|
|
|
33
33
|
"node": ">=18.0.0"
|
|
34
34
|
},
|
|
35
35
|
"description": "Hono Utils",
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"@cloudflare/workers-types": "^4.20260124.0",
|
|
38
|
-
"@eslint/js": "^9.0.0",
|
|
39
|
-
"@types/node": "^25.0.10",
|
|
40
|
-
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
41
|
-
"@typescript-eslint/parser": "^8.0.0",
|
|
42
|
-
"@vitest/coverage-v8": "^4.0.18",
|
|
43
|
-
"eslint": "^9.0.0",
|
|
44
|
-
"husky": "^9.1.7",
|
|
45
|
-
"prettier": "^3.8.1",
|
|
46
|
-
"tsup": "^8.5.1",
|
|
47
|
-
"typescript": "^5.0.0",
|
|
48
|
-
"vitest": "^4.0.18",
|
|
49
|
-
"zod": "4.3.6"
|
|
50
|
-
},
|
|
51
36
|
"dependencies": {
|
|
52
37
|
"@paralleldrive/cuid2": "^3.3.0",
|
|
53
|
-
"hierarchical-area-logger": "^0.
|
|
54
|
-
"hono": "^4.
|
|
55
|
-
"ua-parser-js": "^2.0.
|
|
38
|
+
"hierarchical-area-logger": "^0.3.0",
|
|
39
|
+
"hono": "^4.12.30",
|
|
40
|
+
"ua-parser-js": "^2.0.10",
|
|
41
|
+
"zod": "4.4.3"
|
|
56
42
|
},
|
|
57
|
-
"main": "dist/index.
|
|
43
|
+
"main": "dist/index.cjs",
|
|
44
|
+
"module": "dist/index.mjs",
|
|
58
45
|
"types": "dist/index.d.ts",
|
|
46
|
+
"exports": {
|
|
47
|
+
".": {
|
|
48
|
+
"import": {
|
|
49
|
+
"types": "./dist/index.d.ts",
|
|
50
|
+
"default": "./dist/index.mjs"
|
|
51
|
+
},
|
|
52
|
+
"require": {
|
|
53
|
+
"types": "./dist/index.d.ts",
|
|
54
|
+
"default": "./dist/index.cjs"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
59
58
|
"files": [
|
|
60
59
|
"dist"
|
|
61
60
|
],
|
|
@@ -64,5 +63,19 @@
|
|
|
64
63
|
"utils",
|
|
65
64
|
"typescript",
|
|
66
65
|
"nodejs"
|
|
67
|
-
]
|
|
66
|
+
],
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@cloudflare/workers-types": "^5.20260713.1",
|
|
69
|
+
"@eslint/js": "^10.0.1",
|
|
70
|
+
"@types/node": "^26.1.1",
|
|
71
|
+
"@typescript-eslint/eslint-plugin": "^8.63.0",
|
|
72
|
+
"@typescript-eslint/parser": "^8.63.0",
|
|
73
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
74
|
+
"eslint": "^10.7.0",
|
|
75
|
+
"husky": "^9.1.7",
|
|
76
|
+
"prettier": "^3.9.5",
|
|
77
|
+
"unbuild": "^3.6.1",
|
|
78
|
+
"typescript": "^5.9.3",
|
|
79
|
+
"vitest": "^4.1.10"
|
|
80
|
+
}
|
|
68
81
|
}
|