hono-utils 0.5.0 → 0.5.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/README.md CHANGED
@@ -34,13 +34,13 @@ import { QueueHandler } from 'hono-utils';
34
34
  ### Crypto
35
35
 
36
36
  ```ts
37
- import { pbkdf2, sha } from 'hono-utils';
37
+ import { pbkdf2, sha } from 'hono-utils/crypto';
38
38
  ```
39
39
 
40
40
  ### Client
41
41
 
42
42
  ```ts
43
- import { createTypedClient } from 'hono-utils';
43
+ import { createTypedClient } from 'hono-utils/client';
44
44
  ```
45
45
 
46
46
  ### Router
@@ -67,7 +67,6 @@ import { onError, onNotFound } from 'hono-utils';
67
67
  - [response](docs/middleware/response.md)
68
68
  - [validator](docs/middleware/validator.md)
69
69
  - [withLogger](docs/middleware/withLogger.md)
70
- - [hydrateVariable](docs/middleware/hydrateVariable.md)
71
70
 
72
71
  - Queue
73
72
  - [QueueHandler](docs/queue/QueueHandler.md)
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ const client = require('hono/client');
4
+ const httpException = require('hono/http-exception');
5
+
6
+ const createTypedClient = () => {
7
+ return (options) => async (fn, callbacks) => {
8
+ const client$1 = client.hc(options.url, {
9
+ headers: options.headers,
10
+ fetch: options.fetch
11
+ });
12
+ callbacks?.onStart?.();
13
+ let responseHeaders = new Headers();
14
+ try {
15
+ const response = await fn(client$1);
16
+ responseHeaders = response.headers;
17
+ const data = await client.parseResponse(response);
18
+ callbacks?.onSuccess?.(data, responseHeaders);
19
+ return data;
20
+ } catch (err) {
21
+ const { detail, statusCode } = err;
22
+ const status = statusCode ?? 500;
23
+ if (!detail) {
24
+ options.errorHandler?.(status, {
25
+ message: "Fetch malformed"
26
+ });
27
+ throw new httpException.HTTPException(status, { message: "Fetch malformed" });
28
+ }
29
+ const { message } = detail;
30
+ const eventId = responseHeaders.get("x-event-id") ?? void 0;
31
+ options.errorHandler?.(status, { ...detail, eventId, status });
32
+ callbacks?.onError?.(detail, responseHeaders);
33
+ throw new httpException.HTTPException(status, { message });
34
+ } finally {
35
+ callbacks?.onEnd?.();
36
+ }
37
+ };
38
+ };
39
+
40
+ exports.createTypedClient = createTypedClient;
41
+ //# sourceMappingURL=createTypedClient.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTypedClient.cjs","sources":["../../src/client/createTypedClient.ts"],"sourcesContent":["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":["client","hc","parseResponse","HTTPException"],"mappings":";;;;;AA0BO,MAAM,oBAAoB,MAAwC;AACvE,EAAA,OAAO,CAAC,OAAA,KACN,OACE,EAAA,EAGA,SAAA,KAC0B;AAC1B,IAAA,MAAMA,QAAA,GAASC,SAAA,CAAS,OAAA,CAAQ,GAAA,EAAK;AAAA,MACnC,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,OAAO,OAAA,CAAQ;AAAA,KAChB,CAAA;AACD,IAAA,SAAA,EAAW,OAAA,IAAU;AAGrB,IAAA,IAAI,eAAA,GAA2B,IAAI,OAAA,EAAQ;AAE3C,IAAA,IAAI;AAEF,MAAA,MAAM,QAAA,GAAW,MAAM,EAAA,CAAGD,QAAM,CAAA;AAChC,MAAA,eAAA,GAAkB,QAAA,CAAS,OAAA;AAM3B,MAAA,MAAM,IAAA,GAAO,MAAME,oBAAA,CAAc,QAAQ,CAAA;AAGzC,MAAA,SAAA,EAAW,SAAA,GAAY,MAAM,eAAe,CAAA;AAC5C,MAAA,OAAO,IAAA;AAAA,IACT,SAAS,GAAA,EAAK;AAEZ,MAAA,MAAM,EAAE,MAAA,EAAQ,UAAA,EAAW,GAAI,GAAA;AAK/B,MAAA,MAAM,SAAS,UAAA,IAAc,GAAA;AAE7B,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,OAAA,CAAQ,eAAe,MAAA,EAAQ;AAAA,UAC7B,OAAA,EAAS;AAAA,SACV,CAAA;AACD,QAAA,MAAM,IAAIC,2BAAA,CAAc,MAAA,EAAQ,EAAE,OAAA,EAAS,mBAAmB,CAAA;AAAA,MAChE;AAEA,MAAA,MAAM,EAAE,SAAQ,GAAI,MAAA;AAIpB,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,GAAA,CAAI,YAAY,CAAA,IAAK,MAAA;AAErD,MAAA,OAAA,CAAQ,eAAe,MAAA,EAAQ,EAAE,GAAG,MAAA,EAAQ,OAAA,EAAS,QAAQ,CAAA;AAC7D,MAAA,SAAA,EAAW,OAAA,GAAU,QAAQ,eAAe,CAAA;AAE5C,MAAA,MAAM,IAAIA,2BAAA,CAAc,MAAA,EAAQ,EAAE,SAAS,CAAA;AAAA,IAC7C,CAAA,SAAE;AACA,MAAA,SAAA,EAAW,KAAA,IAAQ;AAAA,IACrB;AAAA,EACF,CAAA;AACJ;;;;"}
@@ -0,0 +1,21 @@
1
+ import { hc, ClientResponse } from 'hono/client';
2
+ import { Hono } from 'hono';
3
+ import { StatusCode } from 'hono/utils/http-status';
4
+
5
+ type ErrorBody = Record<string, unknown>;
6
+ interface TypedClientCallbacks {
7
+ onStart?: () => void;
8
+ onSuccess?: (parsedData: unknown, headers: Headers) => void;
9
+ onError?: (parsedData: unknown, headers: Headers) => void;
10
+ onEnd?: () => void;
11
+ }
12
+ interface CreateTypedClientOptions {
13
+ url: string;
14
+ headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
15
+ fetch?: typeof fetch;
16
+ errorHandler?: (status: StatusCode | number, body?: ErrorBody) => never;
17
+ }
18
+ declare const createTypedClient: <TApp extends Hono<any, any, any>>() => (options: CreateTypedClientOptions) => <TSuccessData>(fn: (c: ReturnType<typeof hc<TApp>>) => Promise<ClientResponse<TSuccessData>>, callbacks?: TypedClientCallbacks) => Promise<TSuccessData>;
19
+
20
+ export { createTypedClient };
21
+ export type { CreateTypedClientOptions, TypedClientCallbacks };
@@ -0,0 +1,21 @@
1
+ import { hc, ClientResponse } from 'hono/client';
2
+ import { Hono } from 'hono';
3
+ import { StatusCode } from 'hono/utils/http-status';
4
+
5
+ type ErrorBody = Record<string, unknown>;
6
+ interface TypedClientCallbacks {
7
+ onStart?: () => void;
8
+ onSuccess?: (parsedData: unknown, headers: Headers) => void;
9
+ onError?: (parsedData: unknown, headers: Headers) => void;
10
+ onEnd?: () => void;
11
+ }
12
+ interface CreateTypedClientOptions {
13
+ url: string;
14
+ headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
15
+ fetch?: typeof fetch;
16
+ errorHandler?: (status: StatusCode | number, body?: ErrorBody) => never;
17
+ }
18
+ declare const createTypedClient: <TApp extends Hono<any, any, any>>() => (options: CreateTypedClientOptions) => <TSuccessData>(fn: (c: ReturnType<typeof hc<TApp>>) => Promise<ClientResponse<TSuccessData>>, callbacks?: TypedClientCallbacks) => Promise<TSuccessData>;
19
+
20
+ export { createTypedClient };
21
+ export type { CreateTypedClientOptions, TypedClientCallbacks };
@@ -0,0 +1,21 @@
1
+ import { hc, ClientResponse } from 'hono/client';
2
+ import { Hono } from 'hono';
3
+ import { StatusCode } from 'hono/utils/http-status';
4
+
5
+ type ErrorBody = Record<string, unknown>;
6
+ interface TypedClientCallbacks {
7
+ onStart?: () => void;
8
+ onSuccess?: (parsedData: unknown, headers: Headers) => void;
9
+ onError?: (parsedData: unknown, headers: Headers) => void;
10
+ onEnd?: () => void;
11
+ }
12
+ interface CreateTypedClientOptions {
13
+ url: string;
14
+ headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
15
+ fetch?: typeof fetch;
16
+ errorHandler?: (status: StatusCode | number, body?: ErrorBody) => never;
17
+ }
18
+ declare const createTypedClient: <TApp extends Hono<any, any, any>>() => (options: CreateTypedClientOptions) => <TSuccessData>(fn: (c: ReturnType<typeof hc<TApp>>) => Promise<ClientResponse<TSuccessData>>, callbacks?: TypedClientCallbacks) => Promise<TSuccessData>;
19
+
20
+ export { createTypedClient };
21
+ export type { CreateTypedClientOptions, TypedClientCallbacks };
@@ -0,0 +1,39 @@
1
+ import { hc, parseResponse } from 'hono/client';
2
+ import { HTTPException } from 'hono/http-exception';
3
+
4
+ const createTypedClient = () => {
5
+ return (options) => async (fn, callbacks) => {
6
+ const client = hc(options.url, {
7
+ headers: options.headers,
8
+ fetch: options.fetch
9
+ });
10
+ callbacks?.onStart?.();
11
+ let responseHeaders = new Headers();
12
+ try {
13
+ const response = await fn(client);
14
+ responseHeaders = response.headers;
15
+ const data = await parseResponse(response);
16
+ callbacks?.onSuccess?.(data, responseHeaders);
17
+ return data;
18
+ } catch (err) {
19
+ const { detail, statusCode } = err;
20
+ const status = statusCode ?? 500;
21
+ if (!detail) {
22
+ options.errorHandler?.(status, {
23
+ message: "Fetch malformed"
24
+ });
25
+ throw new HTTPException(status, { message: "Fetch malformed" });
26
+ }
27
+ const { message } = detail;
28
+ const eventId = responseHeaders.get("x-event-id") ?? void 0;
29
+ options.errorHandler?.(status, { ...detail, eventId, status });
30
+ callbacks?.onError?.(detail, responseHeaders);
31
+ throw new HTTPException(status, { message });
32
+ } finally {
33
+ callbacks?.onEnd?.();
34
+ }
35
+ };
36
+ };
37
+
38
+ export { createTypedClient };
39
+ //# sourceMappingURL=createTypedClient.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTypedClient.mjs","sources":["../../src/client/createTypedClient.ts"],"sourcesContent":["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":[],"mappings":";;;AA0BO,MAAM,oBAAoB,MAAwC;AACvE,EAAA,OAAO,CAAC,OAAA,KACN,OACE,EAAA,EAGA,SAAA,KAC0B;AAC1B,IAAA,MAAM,MAAA,GAAS,EAAA,CAAS,OAAA,CAAQ,GAAA,EAAK;AAAA,MACnC,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,OAAO,OAAA,CAAQ;AAAA,KAChB,CAAA;AACD,IAAA,SAAA,EAAW,OAAA,IAAU;AAGrB,IAAA,IAAI,eAAA,GAA2B,IAAI,OAAA,EAAQ;AAE3C,IAAA,IAAI;AAEF,MAAA,MAAM,QAAA,GAAW,MAAM,EAAA,CAAG,MAAM,CAAA;AAChC,MAAA,eAAA,GAAkB,QAAA,CAAS,OAAA;AAM3B,MAAA,MAAM,IAAA,GAAO,MAAM,aAAA,CAAc,QAAQ,CAAA;AAGzC,MAAA,SAAA,EAAW,SAAA,GAAY,MAAM,eAAe,CAAA;AAC5C,MAAA,OAAO,IAAA;AAAA,IACT,SAAS,GAAA,EAAK;AAEZ,MAAA,MAAM,EAAE,MAAA,EAAQ,UAAA,EAAW,GAAI,GAAA;AAK/B,MAAA,MAAM,SAAS,UAAA,IAAc,GAAA;AAE7B,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,OAAA,CAAQ,eAAe,MAAA,EAAQ;AAAA,UAC7B,OAAA,EAAS;AAAA,SACV,CAAA;AACD,QAAA,MAAM,IAAI,aAAA,CAAc,MAAA,EAAQ,EAAE,OAAA,EAAS,mBAAmB,CAAA;AAAA,MAChE;AAEA,MAAA,MAAM,EAAE,SAAQ,GAAI,MAAA;AAIpB,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,GAAA,CAAI,YAAY,CAAA,IAAK,MAAA;AAErD,MAAA,OAAA,CAAQ,eAAe,MAAA,EAAQ,EAAE,GAAG,MAAA,EAAQ,OAAA,EAAS,QAAQ,CAAA;AAC7D,MAAA,SAAA,EAAW,OAAA,GAAU,QAAQ,eAAe,CAAA;AAE5C,MAAA,MAAM,IAAI,aAAA,CAAc,MAAA,EAAQ,EAAE,SAAS,CAAA;AAAA,IAC7C,CAAA,SAAE;AACA,MAAA,SAAA,EAAW,KAAA,IAAQ;AAAA,IACrB;AAAA,EACF,CAAA;AACJ;;;;"}
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ const sha = require('../shared/hono-utils.DX3fyaCt.cjs');
4
+
5
+ async function deriveRawKey(input, salt, iterations) {
6
+ const encoder = new TextEncoder();
7
+ const baseKey = await crypto.subtle.importKey(
8
+ "raw",
9
+ encoder.encode(input),
10
+ { name: "PBKDF2" },
11
+ false,
12
+ ["deriveBits"]
13
+ );
14
+ const derivedBits = await crypto.subtle.deriveBits(
15
+ {
16
+ name: "PBKDF2",
17
+ salt: salt.buffer,
18
+ iterations,
19
+ hash: "SHA-256"
20
+ },
21
+ baseKey,
22
+ 256
23
+ );
24
+ return new Uint8Array(derivedBits);
25
+ }
26
+ async function hash(input, salt, iterations = 6e5) {
27
+ const encoder = new TextEncoder();
28
+ const saltBuffer = encoder.encode(salt);
29
+ const hashBuffer = await deriveRawKey(input, saltBuffer, iterations);
30
+ return Array.from(hashBuffer).map((b) => b.toString(16).padStart(2, "0")).join("");
31
+ }
32
+ async function verify(input, salt, storedHash, iterations) {
33
+ const encoder = new TextEncoder();
34
+ const saltBuffer = encoder.encode(salt);
35
+ const generatedBuffer = await deriveRawKey(input, saltBuffer, iterations);
36
+ const storedBuffer = new Uint8Array(
37
+ storedHash.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
38
+ );
39
+ if (generatedBuffer.length !== storedBuffer.length) {
40
+ return false;
41
+ }
42
+ let result = 0;
43
+ for (let i = 0; i < generatedBuffer.length; i++) {
44
+ result |= generatedBuffer[i] ^ storedBuffer[i];
45
+ }
46
+ return result === 0;
47
+ }
48
+
49
+ const pbkdf2 = {
50
+ __proto__: null,
51
+ hash: hash,
52
+ verify: verify
53
+ };
54
+
55
+ exports.SHA = sha.sha;
56
+ exports.PBKDF2 = pbkdf2;
57
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../src/crypto/pbkdf2.ts"],"sourcesContent":["/**\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"],"names":[],"mappings":";;;;AASA,eAAe,YAAA,CACb,KAAA,EACA,IAAA,EACA,UAAA,EACqB;AACrB,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAEhC,EAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,MAAA,CAAO,SAAA;AAAA,IAClC,KAAA;AAAA,IACA,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,IACpB,EAAE,MAAM,QAAA,EAAS;AAAA,IACjB,KAAA;AAAA,IACA,CAAC,YAAY;AAAA,GACf;AAEA,EAAA,MAAM,WAAA,GAAc,MAAM,MAAA,CAAO,MAAA,CAAO,UAAA;AAAA,IACtC;AAAA,MACE,IAAA,EAAM,QAAA;AAAA,MACN,MAAM,IAAA,CAAK,MAAA;AAAA,MACX,UAAA;AAAA,MACA,IAAA,EAAM;AAAA,KACR;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO,IAAI,WAAW,WAAW,CAAA;AACnC;AAWA,eAAsB,IAAA,CACpB,KAAA,EACA,IAAA,EACA,UAAA,GAAqB,GAAA,EACJ;AACjB,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,MAAM,YAAA,CAAa,KAAA,EAAO,YAAY,UAAU,CAAA;AAEnE,EAAA,OAAO,MAAM,IAAA,CAAK,UAAU,CAAA,CACzB,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAC1C,KAAK,EAAE,CAAA;AACZ;AAYA,eAAsB,MAAA,CACpB,KAAA,EACA,IAAA,EACA,UAAA,EACA,UAAA,EACkB;AAClB,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAA;AAGtC,EAAA,MAAM,eAAA,GAAkB,MAAM,YAAA,CAAa,KAAA,EAAO,YAAY,UAAU,CAAA;AAGxE,EAAA,MAAM,eAAe,IAAI,UAAA;AAAA,IACvB,UAAA,CAAW,KAAA,CAAM,SAAS,CAAA,CAAG,GAAA,CAAI,CAAC,IAAA,KAAS,QAAA,CAAS,IAAA,EAAM,EAAE,CAAC;AAAA,GAC/D;AAGA,EAAA,IAAI,eAAA,CAAgB,MAAA,KAAW,YAAA,CAAa,MAAA,EAAQ;AAClD,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,MAAA,GAAS,CAAA;AACb,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,eAAA,CAAgB,QAAQ,CAAA,EAAA,EAAK;AAC/C,IAAA,MAAA,IAAU,eAAA,CAAgB,CAAC,CAAA,GAAI,YAAA,CAAa,CAAC,CAAA;AAAA,EAC/C;AAEA,EAAA,OAAO,MAAA,KAAW,CAAA;AACpB;;;;;;;;;;;"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Derives a secure hex-encoded hash from an input string using PBKDF2-HMAC-SHA256.
3
+ * * @example
4
+ * const passwordHash = await hash("myPassword123", "random-salt-string");
5
+ * * @param {string} input - The plain-text string to hash.
6
+ * @param {string} salt - The salt string used to randomize the hash.
7
+ * @param {number} [iterations=600000] - The CPU cost factor. Default is 600,000.
8
+ * @returns {Promise<string>} A hex-encoded string representing the derived key.
9
+ */
10
+ declare function hash$1(input: string, salt: string, iterations?: number): Promise<string>;
11
+ /**
12
+ * Verifies if an input string matches a stored hash using a constant-time comparison.
13
+ * This prevents timing attacks by ensuring the execution time does not reveal
14
+ * how many characters of the hash were correct.
15
+ * * @param {string} input - The plain-text string to verify.
16
+ * @param {string} salt - The salt used during the original hashing.
17
+ * @param {string} storedHash - The hex-encoded hash previously stored in the database.
18
+ * @param {number} iterations - The iteration count used for the original hash.
19
+ * @returns {Promise<boolean>} Resolves to `true` if the input matches the hash, otherwise `false`.
20
+ */
21
+ declare function verify(input: string, salt: string, storedHash: string, iterations: number): Promise<boolean>;
22
+
23
+ declare const pbkdf2_verify: typeof verify;
24
+ declare namespace pbkdf2 {
25
+ export {
26
+ hash$1 as hash,
27
+ pbkdf2_verify as verify,
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Generates a cryptographically strong random salt.
33
+ */
34
+ declare function generateSalt(length?: number): string;
35
+ /**
36
+ * Calculates the SHA hash of the given input string.
37
+ * @param algorithm - The algorithm to be used for hashing. Defaults to 256.
38
+ * @param pepper - An optional string to be prepended to the input before hashing.
39
+ * @param salt - An optional string to be appended to the input before hashing.
40
+ * @returns A Promise that resolves to the hashed string.
41
+ */
42
+ declare function hash({ input, algorithm, pepper, salt, }: {
43
+ input: string;
44
+ algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512';
45
+ pepper?: string;
46
+ salt?: string;
47
+ }): Promise<string>;
48
+
49
+ declare const sha_generateSalt: typeof generateSalt;
50
+ declare const sha_hash: typeof hash;
51
+ declare namespace sha {
52
+ export {
53
+ sha_generateSalt as generateSalt,
54
+ sha_hash as hash,
55
+ };
56
+ }
57
+
58
+ export { pbkdf2 as PBKDF2, sha as SHA };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Derives a secure hex-encoded hash from an input string using PBKDF2-HMAC-SHA256.
3
+ * * @example
4
+ * const passwordHash = await hash("myPassword123", "random-salt-string");
5
+ * * @param {string} input - The plain-text string to hash.
6
+ * @param {string} salt - The salt string used to randomize the hash.
7
+ * @param {number} [iterations=600000] - The CPU cost factor. Default is 600,000.
8
+ * @returns {Promise<string>} A hex-encoded string representing the derived key.
9
+ */
10
+ declare function hash$1(input: string, salt: string, iterations?: number): Promise<string>;
11
+ /**
12
+ * Verifies if an input string matches a stored hash using a constant-time comparison.
13
+ * This prevents timing attacks by ensuring the execution time does not reveal
14
+ * how many characters of the hash were correct.
15
+ * * @param {string} input - The plain-text string to verify.
16
+ * @param {string} salt - The salt used during the original hashing.
17
+ * @param {string} storedHash - The hex-encoded hash previously stored in the database.
18
+ * @param {number} iterations - The iteration count used for the original hash.
19
+ * @returns {Promise<boolean>} Resolves to `true` if the input matches the hash, otherwise `false`.
20
+ */
21
+ declare function verify(input: string, salt: string, storedHash: string, iterations: number): Promise<boolean>;
22
+
23
+ declare const pbkdf2_verify: typeof verify;
24
+ declare namespace pbkdf2 {
25
+ export {
26
+ hash$1 as hash,
27
+ pbkdf2_verify as verify,
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Generates a cryptographically strong random salt.
33
+ */
34
+ declare function generateSalt(length?: number): string;
35
+ /**
36
+ * Calculates the SHA hash of the given input string.
37
+ * @param algorithm - The algorithm to be used for hashing. Defaults to 256.
38
+ * @param pepper - An optional string to be prepended to the input before hashing.
39
+ * @param salt - An optional string to be appended to the input before hashing.
40
+ * @returns A Promise that resolves to the hashed string.
41
+ */
42
+ declare function hash({ input, algorithm, pepper, salt, }: {
43
+ input: string;
44
+ algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512';
45
+ pepper?: string;
46
+ salt?: string;
47
+ }): Promise<string>;
48
+
49
+ declare const sha_generateSalt: typeof generateSalt;
50
+ declare const sha_hash: typeof hash;
51
+ declare namespace sha {
52
+ export {
53
+ sha_generateSalt as generateSalt,
54
+ sha_hash as hash,
55
+ };
56
+ }
57
+
58
+ export { pbkdf2 as PBKDF2, sha as SHA };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Derives a secure hex-encoded hash from an input string using PBKDF2-HMAC-SHA256.
3
+ * * @example
4
+ * const passwordHash = await hash("myPassword123", "random-salt-string");
5
+ * * @param {string} input - The plain-text string to hash.
6
+ * @param {string} salt - The salt string used to randomize the hash.
7
+ * @param {number} [iterations=600000] - The CPU cost factor. Default is 600,000.
8
+ * @returns {Promise<string>} A hex-encoded string representing the derived key.
9
+ */
10
+ declare function hash$1(input: string, salt: string, iterations?: number): Promise<string>;
11
+ /**
12
+ * Verifies if an input string matches a stored hash using a constant-time comparison.
13
+ * This prevents timing attacks by ensuring the execution time does not reveal
14
+ * how many characters of the hash were correct.
15
+ * * @param {string} input - The plain-text string to verify.
16
+ * @param {string} salt - The salt used during the original hashing.
17
+ * @param {string} storedHash - The hex-encoded hash previously stored in the database.
18
+ * @param {number} iterations - The iteration count used for the original hash.
19
+ * @returns {Promise<boolean>} Resolves to `true` if the input matches the hash, otherwise `false`.
20
+ */
21
+ declare function verify(input: string, salt: string, storedHash: string, iterations: number): Promise<boolean>;
22
+
23
+ declare const pbkdf2_verify: typeof verify;
24
+ declare namespace pbkdf2 {
25
+ export {
26
+ hash$1 as hash,
27
+ pbkdf2_verify as verify,
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Generates a cryptographically strong random salt.
33
+ */
34
+ declare function generateSalt(length?: number): string;
35
+ /**
36
+ * Calculates the SHA hash of the given input string.
37
+ * @param algorithm - The algorithm to be used for hashing. Defaults to 256.
38
+ * @param pepper - An optional string to be prepended to the input before hashing.
39
+ * @param salt - An optional string to be appended to the input before hashing.
40
+ * @returns A Promise that resolves to the hashed string.
41
+ */
42
+ declare function hash({ input, algorithm, pepper, salt, }: {
43
+ input: string;
44
+ algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512';
45
+ pepper?: string;
46
+ salt?: string;
47
+ }): Promise<string>;
48
+
49
+ declare const sha_generateSalt: typeof generateSalt;
50
+ declare const sha_hash: typeof hash;
51
+ declare namespace sha {
52
+ export {
53
+ sha_generateSalt as generateSalt,
54
+ sha_hash as hash,
55
+ };
56
+ }
57
+
58
+ export { pbkdf2 as PBKDF2, sha as SHA };
@@ -0,0 +1,54 @@
1
+ export { s as SHA } from '../shared/hono-utils.BOXr1V_a.mjs';
2
+
3
+ async function deriveRawKey(input, salt, iterations) {
4
+ const encoder = new TextEncoder();
5
+ const baseKey = await crypto.subtle.importKey(
6
+ "raw",
7
+ encoder.encode(input),
8
+ { name: "PBKDF2" },
9
+ false,
10
+ ["deriveBits"]
11
+ );
12
+ const derivedBits = await crypto.subtle.deriveBits(
13
+ {
14
+ name: "PBKDF2",
15
+ salt: salt.buffer,
16
+ iterations,
17
+ hash: "SHA-256"
18
+ },
19
+ baseKey,
20
+ 256
21
+ );
22
+ return new Uint8Array(derivedBits);
23
+ }
24
+ async function hash(input, salt, iterations = 6e5) {
25
+ const encoder = new TextEncoder();
26
+ const saltBuffer = encoder.encode(salt);
27
+ const hashBuffer = await deriveRawKey(input, saltBuffer, iterations);
28
+ return Array.from(hashBuffer).map((b) => b.toString(16).padStart(2, "0")).join("");
29
+ }
30
+ async function verify(input, salt, storedHash, iterations) {
31
+ const encoder = new TextEncoder();
32
+ const saltBuffer = encoder.encode(salt);
33
+ const generatedBuffer = await deriveRawKey(input, saltBuffer, iterations);
34
+ const storedBuffer = new Uint8Array(
35
+ storedHash.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
36
+ );
37
+ if (generatedBuffer.length !== storedBuffer.length) {
38
+ return false;
39
+ }
40
+ let result = 0;
41
+ for (let i = 0; i < generatedBuffer.length; i++) {
42
+ result |= generatedBuffer[i] ^ storedBuffer[i];
43
+ }
44
+ return result === 0;
45
+ }
46
+
47
+ const pbkdf2 = {
48
+ __proto__: null,
49
+ hash: hash,
50
+ verify: verify
51
+ };
52
+
53
+ export { pbkdf2 as PBKDF2 };
54
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../../src/crypto/pbkdf2.ts"],"sourcesContent":["/**\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"],"names":[],"mappings":";;AASA,eAAe,YAAA,CACb,KAAA,EACA,IAAA,EACA,UAAA,EACqB;AACrB,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAEhC,EAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,MAAA,CAAO,SAAA;AAAA,IAClC,KAAA;AAAA,IACA,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,IACpB,EAAE,MAAM,QAAA,EAAS;AAAA,IACjB,KAAA;AAAA,IACA,CAAC,YAAY;AAAA,GACf;AAEA,EAAA,MAAM,WAAA,GAAc,MAAM,MAAA,CAAO,MAAA,CAAO,UAAA;AAAA,IACtC;AAAA,MACE,IAAA,EAAM,QAAA;AAAA,MACN,MAAM,IAAA,CAAK,MAAA;AAAA,MACX,UAAA;AAAA,MACA,IAAA,EAAM;AAAA,KACR;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO,IAAI,WAAW,WAAW,CAAA;AACnC;AAWA,eAAsB,IAAA,CACpB,KAAA,EACA,IAAA,EACA,UAAA,GAAqB,GAAA,EACJ;AACjB,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,MAAM,YAAA,CAAa,KAAA,EAAO,YAAY,UAAU,CAAA;AAEnE,EAAA,OAAO,MAAM,IAAA,CAAK,UAAU,CAAA,CACzB,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAC1C,KAAK,EAAE,CAAA;AACZ;AAYA,eAAsB,MAAA,CACpB,KAAA,EACA,IAAA,EACA,UAAA,EACA,UAAA,EACkB;AAClB,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAA;AAGtC,EAAA,MAAM,eAAA,GAAkB,MAAM,YAAA,CAAa,KAAA,EAAO,YAAY,UAAU,CAAA;AAGxE,EAAA,MAAM,eAAe,IAAI,UAAA;AAAA,IACvB,UAAA,CAAW,KAAA,CAAM,SAAS,CAAA,CAAG,GAAA,CAAI,CAAC,IAAA,KAAS,QAAA,CAAS,IAAA,EAAM,EAAE,CAAC;AAAA,GAC/D;AAGA,EAAA,IAAI,eAAA,CAAgB,MAAA,KAAW,YAAA,CAAa,MAAA,EAAQ;AAClD,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,MAAA,GAAS,CAAA;AACb,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,eAAA,CAAgB,QAAQ,CAAA,EAAA,EAAK;AAC/C,IAAA,MAAA,IAAU,eAAA,CAAgB,CAAC,CAAA,GAAI,YAAA,CAAa,CAAC,CAAA;AAAA,EAC/C;AAEA,EAAA,OAAO,MAAA,KAAW,CAAA;AACpB;;;;;;;;;;"}