@frontmcp/utils 0.0.1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +110 -0
  3. package/content/content.d.ts +43 -0
  4. package/content/index.d.ts +1 -0
  5. package/crypto/browser.d.ts +11 -0
  6. package/crypto/encrypted-blob.d.ts +157 -0
  7. package/crypto/index.d.ts +98 -0
  8. package/crypto/jwt-alg.d.ts +8 -0
  9. package/crypto/node.d.ts +59 -0
  10. package/crypto/pkce/index.d.ts +9 -0
  11. package/crypto/pkce/pkce.d.ts +140 -0
  12. package/crypto/runtime.d.ts +18 -0
  13. package/crypto/secret-persistence/index.d.ts +25 -0
  14. package/crypto/secret-persistence/persistence.d.ts +97 -0
  15. package/crypto/secret-persistence/schema.d.ts +34 -0
  16. package/crypto/secret-persistence/types.d.ts +65 -0
  17. package/crypto/types.d.ts +61 -0
  18. package/escape/escape.d.ts +101 -0
  19. package/escape/index.d.ts +1 -0
  20. package/esm/index.mjs +3264 -0
  21. package/esm/package.json +53 -0
  22. package/fs/fs.d.ts +254 -0
  23. package/fs/index.d.ts +1 -0
  24. package/http/http.d.ts +20 -0
  25. package/http/index.d.ts +1 -0
  26. package/index.d.ts +18 -0
  27. package/index.js +3425 -0
  28. package/naming/index.d.ts +1 -0
  29. package/naming/naming.d.ts +79 -0
  30. package/package.json +3 -2
  31. package/path/index.d.ts +1 -0
  32. package/path/path.d.ts +34 -0
  33. package/regex/index.d.ts +24 -0
  34. package/regex/patterns.d.ts +155 -0
  35. package/regex/safe-regex.d.ts +179 -0
  36. package/serialization/index.d.ts +1 -0
  37. package/serialization/serialization.d.ts +33 -0
  38. package/storage/adapters/base.d.ts +90 -0
  39. package/storage/adapters/index.d.ts +10 -0
  40. package/storage/adapters/memory.d.ts +99 -0
  41. package/storage/adapters/redis.d.ts +88 -0
  42. package/storage/adapters/upstash.d.ts +81 -0
  43. package/storage/adapters/vercel-kv.d.ts +69 -0
  44. package/storage/errors.d.ts +117 -0
  45. package/storage/factory.d.ts +70 -0
  46. package/storage/index.d.ts +13 -0
  47. package/storage/namespace.d.ts +88 -0
  48. package/storage/types.d.ts +428 -0
  49. package/storage/utils/index.d.ts +5 -0
  50. package/storage/utils/pattern.d.ts +71 -0
  51. package/storage/utils/ttl.d.ts +54 -0
  52. package/uri/index.d.ts +2 -0
  53. package/uri/uri-template.d.ts +92 -0
  54. package/uri/uri-validation.d.ts +46 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## 0.0.1
4
+
5
+ - Initial release
6
+ - Extracted utilities from @frontmcp/sdk, @frontmcp/cli, and @frontmcp/adapters
7
+ - Added naming utilities: splitWords, toCase, sepFor, shortHash, ensureMaxLen, idFromString
8
+ - Added URI utilities: isValidMcpUri, extractUriScheme, isValidMcpUriTemplate, parseUriTemplate, matchUriTemplate, expandUriTemplate, extractTemplateParams, isUriTemplate
9
+ - Added path utilities: trimSlashes, joinPath
10
+ - Added content utilities: sanitizeToJson, inferMimeType
11
+ - Added HTTP utilities: validateBaseUrl
12
+ - Added FS utilities: fileExists, readJSON, writeJSON, ensureDir, isDirEmpty, runCmd
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # @frontmcp/utils
2
+
3
+ Shared utility functions for the FrontMCP ecosystem. Provides generic, protocol-neutral utilities for string manipulation, URI handling, path operations, content processing, and more.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @frontmcp/utils
9
+ # or
10
+ yarn add @frontmcp/utils
11
+ ```
12
+
13
+ ## Features
14
+
15
+ ### Naming Utilities
16
+
17
+ ```typescript
18
+ import { splitWords, toCase, shortHash, ensureMaxLen, idFromString } from '@frontmcp/utils';
19
+
20
+ // Split strings into words (handles camelCase, PascalCase, delimiters)
21
+ splitWords('myFunctionName'); // ['my', 'Function', 'Name']
22
+
23
+ // Convert to different cases
24
+ toCase(['my', 'function'], 'snake'); // 'my_function'
25
+ toCase(['my', 'function'], 'kebab'); // 'my-function'
26
+ toCase(['my', 'function'], 'camel'); // 'myFunction'
27
+
28
+ // Generate short hashes
29
+ shortHash('some-string'); // '6-char hex hash'
30
+
31
+ // Truncate with hash for uniqueness
32
+ ensureMaxLen('very-long-name-that-exceeds-limit', 20);
33
+
34
+ // Sanitize to valid ID
35
+ idFromString('My Function Name!'); // 'My-Function-Name'
36
+ ```
37
+
38
+ ### URI Utilities
39
+
40
+ ```typescript
41
+ import {
42
+ isValidMcpUri,
43
+ extractUriScheme,
44
+ parseUriTemplate,
45
+ matchUriTemplate,
46
+ expandUriTemplate,
47
+ } from '@frontmcp/utils';
48
+
49
+ // Validate RFC 3986 URIs
50
+ isValidMcpUri('https://example.com/resource'); // true
51
+ isValidMcpUri('/path/without/scheme'); // false
52
+
53
+ // Extract scheme
54
+ extractUriScheme('https://example.com'); // 'https'
55
+
56
+ // RFC 6570 URI Templates
57
+ const params = matchUriTemplate('users/{userId}/posts/{postId}', 'users/123/posts/456');
58
+ // { userId: '123', postId: '456' }
59
+
60
+ expandUriTemplate('users/{userId}', { userId: '123' }); // 'users/123'
61
+ ```
62
+
63
+ ### Path Utilities
64
+
65
+ ```typescript
66
+ import { trimSlashes, joinPath } from '@frontmcp/utils';
67
+
68
+ trimSlashes('/path/to/resource/'); // 'path/to/resource'
69
+ joinPath('api', 'v1', 'users'); // '/api/v1/users'
70
+ ```
71
+
72
+ ### Content Utilities
73
+
74
+ ```typescript
75
+ import { sanitizeToJson, inferMimeType } from '@frontmcp/utils';
76
+
77
+ // Sanitize values to JSON-safe objects
78
+ sanitizeToJson({ date: new Date(), fn: () => {} }); // { date: '2024-01-01T00:00:00.000Z' }
79
+
80
+ // Infer MIME type from extension
81
+ inferMimeType('document.json'); // 'application/json'
82
+ inferMimeType('image.png'); // 'image/png'
83
+ ```
84
+
85
+ ### HTTP Utilities
86
+
87
+ ```typescript
88
+ import { validateBaseUrl } from '@frontmcp/utils';
89
+
90
+ // Validate and normalize URLs
91
+ const url = validateBaseUrl('https://api.example.com');
92
+ // Throws for invalid URLs or unsupported protocols (file://, javascript:)
93
+ ```
94
+
95
+ ### File System Utilities
96
+
97
+ ```typescript
98
+ import { fileExists, readJSON, writeJSON, ensureDir, isDirEmpty } from '@frontmcp/utils';
99
+
100
+ // Async file operations
101
+ await fileExists('/path/to/file');
102
+ await readJSON<Config>('/path/to/config.json');
103
+ await writeJSON('/path/to/output.json', { key: 'value' });
104
+ await ensureDir('/path/to/directory');
105
+ await isDirEmpty('/path/to/directory');
106
+ ```
107
+
108
+ ## License
109
+
110
+ Apache-2.0
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Content utilities for JSON sanitization and MIME type inference.
3
+ *
4
+ * Provides functions for safely converting JavaScript values to JSON-compatible
5
+ * formats and inferring content types from file extensions.
6
+ */
7
+ /**
8
+ * Sanitize arbitrary JS values into JSON-safe objects.
9
+ *
10
+ * Handles:
11
+ * - Functions and symbols → dropped (undefined)
12
+ * - BigInt → string
13
+ * - Date → ISO string
14
+ * - Error → { name, message, stack }
15
+ * - Map → plain object
16
+ * - Set → array
17
+ * - Circular references → dropped (undefined)
18
+ *
19
+ * @param value - Any JavaScript value
20
+ * @returns JSON-safe version of the value
21
+ *
22
+ * @example
23
+ * sanitizeToJson({ date: new Date(), fn: () => {} })
24
+ * // { date: '2024-01-01T00:00:00.000Z' }
25
+ *
26
+ * sanitizeToJson(new Map([['key', 'value']]))
27
+ * // { key: 'value' }
28
+ */
29
+ export declare function sanitizeToJson(value: unknown): unknown;
30
+ /**
31
+ * Infer MIME type from file extension or content.
32
+ *
33
+ * @param uri - The URI or filename to infer from
34
+ * @param content - Optional content to help infer type
35
+ * @returns Inferred MIME type, defaults to 'text/plain'
36
+ *
37
+ * @example
38
+ * inferMimeType('document.json') // 'application/json'
39
+ * inferMimeType('image.png') // 'image/png'
40
+ * inferMimeType('unknown', '{"key": "value"}') // 'application/json'
41
+ * inferMimeType('unknown', '<html>') // 'text/html'
42
+ */
43
+ export declare function inferMimeType(uri: string, content?: unknown): string;
@@ -0,0 +1 @@
1
+ export { sanitizeToJson, inferMimeType } from './content';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Browser Crypto Provider
3
+ *
4
+ * Implementation using @noble/hashes and @noble/ciphers for cross-platform support.
5
+ * These libraries work in both Node.js and browsers.
6
+ */
7
+ import type { CryptoProvider } from './types';
8
+ /**
9
+ * Browser-compatible crypto provider using @noble libraries.
10
+ */
11
+ export declare const browserCrypto: CryptoProvider;
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Encrypted Blob Utilities
3
+ *
4
+ * Higher-level helpers for encrypting/decrypting JSON-serializable data
5
+ * using AES-256-GCM. All operations are synchronous.
6
+ *
7
+ * @module @frontmcp/utils/crypto
8
+ */
9
+ import type { EncBlob } from './types';
10
+ export type EncryptedBlob = EncBlob;
11
+ /**
12
+ * Error thrown when encrypted blob operations fail.
13
+ */
14
+ export declare class EncryptedBlobError extends Error {
15
+ constructor(message: string);
16
+ }
17
+ /**
18
+ * Encrypt a value using AES-256-GCM.
19
+ *
20
+ * @param data - Value to encrypt (will be JSON serialized)
21
+ * @param key - 32-byte encryption key
22
+ * @returns Encrypted blob with base64url-encoded fields
23
+ * @throws EncryptedBlobError if key is invalid or data cannot be serialized
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const key = hkdfSha256(ikm, salt, info, 32);
28
+ * const blob = encryptValue({ secret: 'data' }, key);
29
+ * // blob.alg === 'A256GCM'
30
+ * // blob.iv, blob.tag, blob.data are base64url strings
31
+ * ```
32
+ */
33
+ export declare function encryptValue<T>(data: T, key: Uint8Array): EncryptedBlob;
34
+ /**
35
+ * Decrypt an encrypted blob and parse the JSON result.
36
+ *
37
+ * @param blob - Encrypted blob to decrypt
38
+ * @param key - 32-byte encryption key (must match encryption key)
39
+ * @returns Decrypted and parsed value
40
+ * @throws EncryptedBlobError if decryption or parsing fails
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const decrypted = decryptValue<MyType>(blob, key);
45
+ * ```
46
+ */
47
+ export declare function decryptValue<T>(blob: EncryptedBlob, key: Uint8Array): T;
48
+ /**
49
+ * Try to decrypt an encrypted blob, returning null on failure.
50
+ *
51
+ * @param blob - Encrypted blob to decrypt
52
+ * @param key - 32-byte encryption key
53
+ * @returns Decrypted value or null if decryption/parsing fails
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const result = tryDecryptValue<MyType>(blob, key);
58
+ * if (result !== null) {
59
+ * // Use decrypted data
60
+ * }
61
+ * ```
62
+ */
63
+ export declare function tryDecryptValue<T>(blob: EncryptedBlob, key: Uint8Array): T | null;
64
+ /**
65
+ * Serialize an encrypted blob to a JSON string for storage.
66
+ *
67
+ * @param blob - Encrypted blob to serialize
68
+ * @returns JSON string representation
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const blob = encryptValue(data, key);
73
+ * const str = serializeBlob(blob);
74
+ * // Store str in database/redis/etc.
75
+ * ```
76
+ */
77
+ export declare function serializeBlob(blob: EncryptedBlob): string;
78
+ /**
79
+ * Deserialize a JSON string back to an encrypted blob.
80
+ *
81
+ * @param str - JSON string to deserialize
82
+ * @returns Encrypted blob
83
+ * @throws EncryptedBlobError if string is not a valid encrypted blob
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const blob = deserializeBlob(storedString);
88
+ * const data = decryptValue(blob, key);
89
+ * ```
90
+ */
91
+ export declare function deserializeBlob(str: string): EncryptedBlob;
92
+ /**
93
+ * Try to deserialize a JSON string to an encrypted blob, returning null on failure.
94
+ *
95
+ * @param str - JSON string to deserialize
96
+ * @returns Encrypted blob or null if invalid
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * const blob = tryDeserializeBlob(storedString);
101
+ * if (blob !== null) {
102
+ * const data = tryDecryptValue(blob, key);
103
+ * }
104
+ * ```
105
+ */
106
+ export declare function tryDeserializeBlob(str: string): EncryptedBlob | null;
107
+ /**
108
+ * Check if a value is a valid encrypted blob structure.
109
+ *
110
+ * @param value - Value to check
111
+ * @returns true if value is a valid EncryptedBlob
112
+ */
113
+ export declare function isValidEncryptedBlob(value: unknown): value is EncryptedBlob;
114
+ /**
115
+ * Encrypt and serialize in one step.
116
+ *
117
+ * @param data - Value to encrypt
118
+ * @param key - 32-byte encryption key
119
+ * @returns JSON string of encrypted blob
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * const encrypted = encryptAndSerialize({ secret: 'data' }, key);
124
+ * // Store encrypted string
125
+ * ```
126
+ */
127
+ export declare function encryptAndSerialize<T>(data: T, key: Uint8Array): string;
128
+ /**
129
+ * Deserialize and decrypt in one step.
130
+ *
131
+ * @param str - JSON string of encrypted blob
132
+ * @param key - 32-byte encryption key
133
+ * @returns Decrypted and parsed value
134
+ * @throws EncryptedBlobError if deserialization or decryption fails
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * const data = deserializeAndDecrypt<MyType>(storedString, key);
139
+ * ```
140
+ */
141
+ export declare function deserializeAndDecrypt<T>(str: string, key: Uint8Array): T;
142
+ /**
143
+ * Try to deserialize and decrypt, returning null on any failure.
144
+ *
145
+ * @param str - JSON string of encrypted blob
146
+ * @param key - 32-byte encryption key
147
+ * @returns Decrypted value or null if any step fails
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * const data = tryDeserializeAndDecrypt<MyType>(storedString, key);
152
+ * if (data !== null) {
153
+ * // Use decrypted data
154
+ * }
155
+ * ```
156
+ */
157
+ export declare function tryDeserializeAndDecrypt<T>(str: string, key: Uint8Array): T | null;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Cross-Platform Crypto Module
3
+ *
4
+ * Provides cryptographic operations that work in both Node.js and browser environments.
5
+ * Uses native crypto in Node.js and @noble/hashes + @noble/ciphers in browsers.
6
+ */
7
+ import type { CryptoProvider, EncBlob } from './types';
8
+ export { isRsaPssAlg, jwtAlgToNodeAlg } from './jwt-alg';
9
+ /**
10
+ * Get the crypto provider for the current runtime environment.
11
+ * Lazily initializes the provider.
12
+ *
13
+ * Note: this module intentionally avoids importing any Node-only crypto modules
14
+ * at module-load time so it can be used in browser builds without pulling them in.
15
+ */
16
+ export declare function getCrypto(): CryptoProvider;
17
+ export declare function rsaVerify(jwtAlg: string, data: Buffer, publicJwk: JsonWebKey, signature: Buffer): boolean;
18
+ /**
19
+ * Generate a UUID v4 string.
20
+ */
21
+ export declare function randomUUID(): string;
22
+ /**
23
+ * Generate cryptographically secure random bytes.
24
+ * @param length - Number of bytes to generate (must be a positive integer)
25
+ * @throws Error if length is not a positive integer
26
+ */
27
+ export declare function randomBytes(length: number): Uint8Array;
28
+ /**
29
+ * Compute SHA-256 hash.
30
+ */
31
+ export declare function sha256(data: string | Uint8Array): Uint8Array;
32
+ /**
33
+ * Compute SHA-256 hash and return as hex string.
34
+ */
35
+ export declare function sha256Hex(data: string | Uint8Array): string;
36
+ /**
37
+ * Compute HMAC-SHA256.
38
+ */
39
+ export declare function hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array;
40
+ /**
41
+ * HKDF-SHA256 key derivation (RFC 5869).
42
+ * @param ikm - Input keying material
43
+ * @param salt - Salt value (can be empty)
44
+ * @param info - Context and application specific information
45
+ * @param length - Length of output keying material in bytes (1 to 8160)
46
+ * @throws Error if length is not a positive integer or exceeds HKDF limits
47
+ */
48
+ export declare function hkdfSha256(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Uint8Array;
49
+ /**
50
+ * Encrypt using AES-256-GCM.
51
+ * @param key - 32-byte encryption key (AES-256)
52
+ * @param plaintext - Data to encrypt
53
+ * @param iv - 12-byte initialization vector (96 bits, recommended for GCM)
54
+ * @throws Error if key is not 32 bytes or IV is not 12 bytes
55
+ */
56
+ export declare function encryptAesGcm(key: Uint8Array, plaintext: Uint8Array, iv: Uint8Array): {
57
+ ciphertext: Uint8Array;
58
+ tag: Uint8Array;
59
+ };
60
+ /**
61
+ * Decrypt using AES-256-GCM.
62
+ * @param key - 32-byte encryption key (AES-256)
63
+ * @param ciphertext - Encrypted data
64
+ * @param iv - 12-byte initialization vector (96 bits)
65
+ * @param tag - 16-byte authentication tag
66
+ * @throws Error if key is not 32 bytes, IV is not 12 bytes, or tag is not 16 bytes
67
+ */
68
+ export declare function decryptAesGcm(key: Uint8Array, ciphertext: Uint8Array, iv: Uint8Array, tag: Uint8Array): Uint8Array;
69
+ /**
70
+ * Constant-time comparison to prevent timing attacks.
71
+ * @param a - First byte array to compare
72
+ * @param b - Second byte array to compare
73
+ * @throws Error if arrays have different lengths
74
+ */
75
+ export declare function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean;
76
+ /**
77
+ * Convert a Uint8Array to hex string.
78
+ */
79
+ export declare function bytesToHex(data: Uint8Array): string;
80
+ /**
81
+ * Encode a Uint8Array to base64url string.
82
+ * RFC 4648 Section 5: Base64 with URL and filename safe alphabet.
83
+ */
84
+ export declare function base64urlEncode(data: Uint8Array): string;
85
+ /**
86
+ * Decode a base64url string to Uint8Array.
87
+ */
88
+ export declare function base64urlDecode(data: string): Uint8Array;
89
+ /**
90
+ * Compute SHA-256 hash and return as base64url string.
91
+ * Commonly used for PKCE code_challenge (S256 method).
92
+ */
93
+ export declare function sha256Base64url(data: string | Uint8Array): string;
94
+ export type { CryptoProvider, EncBlob };
95
+ export { isNode, isBrowser, assertNode } from './runtime';
96
+ export { type EncryptedBlob, EncryptedBlobError, encryptValue, decryptValue, tryDecryptValue, serializeBlob, deserializeBlob, tryDeserializeBlob, isValidEncryptedBlob, encryptAndSerialize, deserializeAndDecrypt, tryDeserializeAndDecrypt, } from './encrypted-blob';
97
+ export { MIN_CODE_VERIFIER_LENGTH, MAX_CODE_VERIFIER_LENGTH, DEFAULT_CODE_VERIFIER_LENGTH, PkceError, generateCodeVerifier, generateCodeChallenge, verifyCodeChallenge, generatePkcePair, isValidCodeVerifier, isValidCodeChallenge, type PkcePair, } from './pkce';
98
+ export { type SecretData, type SecretPersistenceOptions, type SecretValidationResult, secretDataSchema, validateSecretData, parseSecretData, isSecretPersistenceEnabled, resolveSecretPath, loadSecret, saveSecret, deleteSecret, generateSecret, createSecretData, getOrCreateSecret, clearCachedSecret, isSecretCached, } from './secret-persistence';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * JWT algorithm helpers.
3
+ *
4
+ * This module is intentionally dependency-free (no Node-only imports) so it can be safely re-used
5
+ * from both browser-compatible and Node-only code.
6
+ */
7
+ export declare function jwtAlgToNodeAlg(jwtAlg: string): string;
8
+ export declare function isRsaPssAlg(jwtAlg: string): boolean;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Node.js Crypto Provider
3
+ *
4
+ * Implementation using Node.js native crypto module.
5
+ */
6
+ import crypto from 'node:crypto';
7
+ import type { CryptoProvider } from './types';
8
+ export { isRsaPssAlg, jwtAlgToNodeAlg } from './jwt-alg';
9
+ /**
10
+ * Node.js crypto provider implementation.
11
+ */
12
+ export declare const nodeCrypto: CryptoProvider;
13
+ /**
14
+ * RSA JWK structure for public keys
15
+ */
16
+ export interface RsaJwk {
17
+ kty: 'RSA';
18
+ kid: string;
19
+ alg: string;
20
+ use: 'sig';
21
+ n: string;
22
+ e: string;
23
+ }
24
+ /**
25
+ * RSA key pair structure
26
+ */
27
+ export interface RsaKeyPair {
28
+ /** Private key for signing */
29
+ privateKey: crypto.KeyObject;
30
+ /** Public key for verification */
31
+ publicKey: crypto.KeyObject;
32
+ /** Public key in JWK format */
33
+ publicJwk: RsaJwk;
34
+ }
35
+ /**
36
+ * Generate an RSA key pair with the specified modulus length
37
+ *
38
+ * @param modulusLength - Key size in bits (default: 2048, suitable for short-lived OAuth/JWT verification; use 3072+ for longer-term keys)
39
+ * @param alg - JWT algorithm (default: 'RS256')
40
+ * @returns RSA key pair with private, public keys and JWK
41
+ */
42
+ export declare function generateRsaKeyPair(modulusLength?: number, alg?: string): RsaKeyPair;
43
+ /**
44
+ * Sign data using RSA with the specified algorithm.
45
+ *
46
+ * For RSA-PSS (PS256/PS384/PS512), callers must pass appropriate padding/saltLength options.
47
+ */
48
+ export declare function rsaSign(algorithm: string, data: Buffer, privateKey: crypto.KeyObject, options?: Omit<crypto.SignKeyObjectInput, 'key'>): Buffer;
49
+ export declare function rsaVerify(jwtAlg: string, data: Buffer, publicJwk: JsonWebKey, signature: Buffer): boolean;
50
+ /**
51
+ * Create a JWT signed with an RSA key
52
+ *
53
+ * @param payload - JWT payload
54
+ * @param privateKey - RSA private key
55
+ * @param kid - Key ID for the JWT header
56
+ * @param alg - JWT algorithm (default: 'RS256')
57
+ * @returns Signed JWT string
58
+ */
59
+ export declare function createSignedJwt(payload: Record<string, unknown>, privateKey: crypto.KeyObject, kid: string, alg?: string): string;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * PKCE (Proof Key for Code Exchange) utilities
3
+ *
4
+ * Implements RFC 7636 for secure authorization flows.
5
+ * Used for webhook callbacks, OAuth flows, and tool approval.
6
+ *
7
+ * @module @frontmcp/utils/pkce
8
+ */
9
+ export { MIN_CODE_VERIFIER_LENGTH, MAX_CODE_VERIFIER_LENGTH, DEFAULT_CODE_VERIFIER_LENGTH, PkceError, generateCodeVerifier, generateCodeChallenge, verifyCodeChallenge, generatePkcePair, isValidCodeVerifier, isValidCodeChallenge, type PkcePair, } from './pkce';
@@ -0,0 +1,140 @@
1
+ /**
2
+ * PKCE (Proof Key for Code Exchange) utilities per RFC 7636
3
+ *
4
+ * PKCE is used to secure authorization flows by generating a cryptographically
5
+ * random code verifier and deriving a code challenge using SHA-256.
6
+ *
7
+ * @see https://datatracker.ietf.org/doc/html/rfc7636
8
+ */
9
+ /**
10
+ * Minimum length for code verifier per RFC 7636
11
+ */
12
+ export declare const MIN_CODE_VERIFIER_LENGTH = 43;
13
+ /**
14
+ * Maximum length for code verifier per RFC 7636
15
+ */
16
+ export declare const MAX_CODE_VERIFIER_LENGTH = 128;
17
+ /**
18
+ * Default length for code verifier (64 chars provides ~384 bits of entropy)
19
+ */
20
+ export declare const DEFAULT_CODE_VERIFIER_LENGTH = 64;
21
+ /**
22
+ * Error thrown when PKCE parameters are invalid
23
+ */
24
+ export declare class PkceError extends Error {
25
+ constructor(message: string);
26
+ }
27
+ /**
28
+ * Generate a cryptographically random code verifier.
29
+ *
30
+ * The code verifier is a high-entropy cryptographic random string used in PKCE.
31
+ * It should be stored securely and never exposed to external systems.
32
+ *
33
+ * @param length - Length of verifier (43-128 chars per RFC 7636, default 64)
34
+ * @returns Base64url-encoded random string
35
+ * @throws {PkceError} If length is outside allowed range
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const verifier = generateCodeVerifier(); // 64 chars (default)
40
+ * const shortVerifier = generateCodeVerifier(43); // minimum length
41
+ * const longVerifier = generateCodeVerifier(128); // maximum length
42
+ * ```
43
+ */
44
+ export declare function generateCodeVerifier(length?: number): string;
45
+ /**
46
+ * Generate a code challenge from a code verifier using S256 method.
47
+ *
48
+ * The code challenge is derived by applying SHA-256 to the code verifier
49
+ * and base64url encoding the result. This is the only method specified
50
+ * in RFC 7636 for security-sensitive applications.
51
+ *
52
+ * @param codeVerifier - The code verifier string
53
+ * @returns Base64url-encoded SHA-256 hash of the verifier
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const verifier = generateCodeVerifier();
58
+ * const challenge = generateCodeChallenge(verifier);
59
+ * // Send `challenge` to external system, keep `verifier` secret
60
+ * ```
61
+ */
62
+ export declare function generateCodeChallenge(codeVerifier: string): string;
63
+ /**
64
+ * Verify that a code verifier matches a code challenge.
65
+ *
66
+ * This validates that SHA256(base64url(code_verifier)) === code_challenge.
67
+ * Used to verify callback responses in webhook/OAuth flows.
68
+ *
69
+ * @param codeVerifier - The original code verifier
70
+ * @param codeChallenge - The code challenge to verify against
71
+ * @returns true if the verifier produces the expected challenge
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * // When callback arrives with code_verifier
76
+ * const isValid = verifyCodeChallenge(callbackVerifier, storedChallenge);
77
+ * if (!isValid) {
78
+ * throw new Error('PKCE validation failed');
79
+ * }
80
+ * ```
81
+ */
82
+ export declare function verifyCodeChallenge(codeVerifier: string, codeChallenge: string): boolean;
83
+ /**
84
+ * Result of generating a PKCE pair
85
+ */
86
+ export interface PkcePair {
87
+ /** The secret code verifier (keep secure, never expose externally) */
88
+ codeVerifier: string;
89
+ /** The code challenge derived from the verifier (safe to share) */
90
+ codeChallenge: string;
91
+ }
92
+ /**
93
+ * Generate a complete PKCE pair (code verifier + code challenge).
94
+ *
95
+ * This is a convenience function that generates both values at once.
96
+ * The code_verifier should be stored securely (e.g., in Redis) while
97
+ * the code_challenge can be sent to external systems.
98
+ *
99
+ * @param length - Length of code verifier (default 64)
100
+ * @returns Object containing both codeVerifier and codeChallenge
101
+ * @throws {PkceError} If length is outside allowed range
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * const { codeVerifier, codeChallenge } = generatePkcePair();
106
+ *
107
+ * // Store verifier in Redis with TTL
108
+ * await redis.set(`challenge:${codeChallenge}`, {
109
+ * verifier: codeVerifier,
110
+ * sessionId: session.id,
111
+ * expiresAt: Date.now() + 300000, // 5 minutes
112
+ * });
113
+ *
114
+ * // Send challenge to webhook (never send verifier!)
115
+ * await fetch(webhookUrl, {
116
+ * body: JSON.stringify({ code_challenge: codeChallenge, ... })
117
+ * });
118
+ * ```
119
+ */
120
+ export declare function generatePkcePair(length?: number): PkcePair;
121
+ /**
122
+ * Validate that a string is a valid code verifier.
123
+ *
124
+ * Checks that the string:
125
+ * - Has a length between 43 and 128 characters
126
+ * - Contains only unreserved characters per RFC 7636 (A-Z, a-z, 0-9, -, ., _, ~)
127
+ *
128
+ * @param value - The string to validate
129
+ * @returns true if valid, false otherwise
130
+ */
131
+ export declare function isValidCodeVerifier(value: string): boolean;
132
+ /**
133
+ * Validate that a string is a valid code challenge.
134
+ *
135
+ * A valid code challenge is a base64url-encoded SHA-256 hash (43 characters).
136
+ *
137
+ * @param value - The string to validate
138
+ * @returns true if valid, false otherwise
139
+ */
140
+ export declare function isValidCodeChallenge(value: string): boolean;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Runtime Detection
3
+ *
4
+ * Utilities for detecting whether code is running in Node.js or browser environment.
5
+ */
6
+ /**
7
+ * Check if running in Node.js environment.
8
+ */
9
+ export declare function isNode(): boolean;
10
+ /**
11
+ * Check if running in a browser-like environment with Web Crypto API.
12
+ */
13
+ export declare function isBrowser(): boolean;
14
+ /**
15
+ * Assert that code is running in Node.js.
16
+ * @throws Error if not in Node.js environment
17
+ */
18
+ export declare function assertNode(feature: string): void;