UNPKG

@sgnl-ai/set-transmitter

Version:

HTTP transmission library for Security Event Tokens (SET) with CAEP/SSF support

1 lines 22.4 kB
{"version":3,"sources":["../src/types.ts","../src/constants.ts","../src/errors.ts","../src/retry.ts","../src/utils.ts","../src/transmitter.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export interface TransmitOptions {\n authToken?: string;\n headers?: Record<string, string>;\n timeout?: number;\n retry?: RetryConfig;\n parseResponse?: boolean;\n validateStatus?: (status: number) => boolean;\n}\n\nexport interface RetryConfig {\n maxAttempts?: number;\n retryableStatuses?: number[];\n backoffMs?: number;\n maxBackoffMs?: number;\n backoffMultiplier?: number;\n}\n\nexport interface TransmitResult {\n status: 'success' | 'failed';\n statusCode: number;\n body: string | Record<string, unknown>;\n headers: Record<string, string>;\n error?: string;\n retryable?: boolean;\n}\n\nexport interface TransmitterConfig {\n defaultOptions?: TransmitOptions;\n}\n\nexport const DEFAULT_RETRY_CONFIG: Required<RetryConfig> = {\n maxAttempts: 3,\n retryableStatuses: [429, 502, 503, 504],\n backoffMs: 1000,\n maxBackoffMs: 10000,\n backoffMultiplier: 2,\n};\n\nexport const DEFAULT_OPTIONS: TransmitOptions = {\n timeout: 30000,\n parseResponse: true,\n validateStatus: (status) => status < 400,\n retry: DEFAULT_RETRY_CONFIG,\n};\n","export const EventTypes = {\n SESSION_REVOKED: 'https://schemas.openid.net/secevent/caep/event-type/session-revoked',\n TOKEN_CLAIMS_CHANGE: 'https://schemas.openid.net/secevent/caep/event-type/token-claims-change',\n CREDENTIAL_CHANGE: 'https://schemas.openid.net/secevent/caep/event-type/credential-change',\n ASSURANCE_LEVEL_CHANGE:\n 'https://schemas.openid.net/secevent/caep/event-type/assurance-level-change',\n DEVICE_COMPLIANCE_CHANGE:\n 'https://schemas.openid.net/secevent/caep/event-type/device-compliance-change',\n} as const;\n\nexport type EventType = (typeof EventTypes)[keyof typeof EventTypes];\n\nexport const CONTENT_TYPE_SET = 'application/secevent+jwt';\nexport const CONTENT_TYPE_JSON = 'application/json';\nexport const DEFAULT_USER_AGENT = 'SGNL-Action-Framework/1.0';\n","export class TransmissionError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly retryable: boolean = false,\n public readonly responseBody?: string,\n public readonly responseHeaders?: Record<string, string>,\n ) {\n super(message);\n this.name = 'TransmissionError';\n Object.setPrototypeOf(this, TransmissionError.prototype);\n }\n}\n\nexport class TimeoutError extends TransmissionError {\n constructor(message: string, timeout: number) {\n super(`${message} (timeout: ${timeout}ms)`, undefined, true);\n this.name = 'TimeoutError';\n Object.setPrototypeOf(this, TimeoutError.prototype);\n }\n}\n\nexport class NetworkError extends TransmissionError {\n public cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message, undefined, true);\n this.name = 'NetworkError';\n if (cause) {\n this.cause = cause;\n }\n Object.setPrototypeOf(this, NetworkError.prototype);\n }\n}\n\nexport class ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ValidationError';\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n","import { RetryConfig } from './types';\n\nexport function calculateBackoff(\n attempt: number,\n config: Required<RetryConfig>,\n retryAfterMs?: number,\n): number {\n // If Retry-After header is present, use it\n if (retryAfterMs !== undefined && retryAfterMs > 0) {\n return Math.min(retryAfterMs, config.maxBackoffMs);\n }\n\n // Calculate exponential backoff with jitter\n const exponentialDelay = config.backoffMs * Math.pow(config.backoffMultiplier, attempt - 1);\n const clampedDelay = Math.min(exponentialDelay, config.maxBackoffMs);\n\n // Add jitter (±25% randomization)\n const jitter = clampedDelay * 0.25;\n const minDelay = clampedDelay - jitter;\n const maxDelay = clampedDelay + jitter;\n\n return Math.floor(Math.random() * (maxDelay - minDelay) + minDelay);\n}\n\nexport function parseRetryAfter(retryAfterHeader: string | undefined): number | undefined {\n if (!retryAfterHeader) {\n return undefined;\n }\n\n // If it's a number, assume it's delay in seconds\n const delaySeconds = parseInt(retryAfterHeader, 10);\n if (!isNaN(delaySeconds)) {\n return delaySeconds * 1000;\n }\n\n // If it's a date, calculate the delay\n const retryDate = new Date(retryAfterHeader);\n if (!isNaN(retryDate.getTime())) {\n const delayMs = retryDate.getTime() - Date.now();\n return delayMs > 0 ? delayMs : undefined;\n }\n\n return undefined;\n}\n\nexport function isRetryableStatus(statusCode: number, retryableStatuses: number[]): boolean {\n return retryableStatuses.includes(statusCode);\n}\n\nexport function shouldRetry(\n statusCode: number | undefined,\n attempt: number,\n config: Required<RetryConfig>,\n): boolean {\n if (attempt >= config.maxAttempts) {\n return false;\n }\n\n if (statusCode === undefined) {\n // Network errors are retryable\n return true;\n }\n\n return isRetryableStatus(statusCode, config.retryableStatuses);\n}\n\nexport async function delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","export function isValidSET(jwt: string): boolean {\n if (typeof jwt !== 'string') {\n return false;\n }\n\n // Basic JWT format validation: header.payload.signature\n const parts = jwt.split('.');\n if (parts.length !== 3) {\n return false;\n }\n\n // Check that each part is base64url encoded\n const base64urlRegex = /^[A-Za-z0-9_-]+$/;\n return parts.every((part) => base64urlRegex.test(part));\n}\n\nexport function normalizeAuthToken(token?: string): string | undefined {\n if (!token) {\n return undefined;\n }\n\n // If token already starts with \"Bearer \", return as is\n if (token.startsWith('Bearer ')) {\n return token;\n }\n\n // Otherwise, add \"Bearer \" prefix\n return `Bearer ${token}`;\n}\n\nexport function mergeHeaders(\n defaultHeaders: Record<string, string>,\n customHeaders?: Record<string, string>,\n): Record<string, string> {\n return {\n ...defaultHeaders,\n ...customHeaders,\n };\n}\n\nexport function parseResponseHeaders(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n}\n\nexport async function parseResponseBody(\n response: Response,\n parseJson: boolean,\n): Promise<string | Record<string, unknown>> {\n const text = await response.text();\n\n if (!parseJson || !text) {\n return text;\n }\n\n // Try to parse as JSON if content-type indicates JSON\n const contentType = response.headers.get('content-type');\n if (contentType?.includes('application/json')) {\n try {\n return JSON.parse(text) as Record<string, unknown>;\n } catch {\n // If parsing fails, return as text\n return text;\n }\n }\n\n return text;\n}\n","import {\n TransmitOptions,\n TransmitResult,\n DEFAULT_OPTIONS,\n DEFAULT_RETRY_CONFIG,\n RetryConfig,\n} from './types';\nimport { CONTENT_TYPE_SET, CONTENT_TYPE_JSON, DEFAULT_USER_AGENT } from './constants';\nimport { TransmissionError, TimeoutError, NetworkError, ValidationError } from './errors';\nimport { calculateBackoff, parseRetryAfter, shouldRetry, delay } from './retry';\nimport {\n isValidSET,\n normalizeAuthToken,\n mergeHeaders,\n parseResponseHeaders,\n parseResponseBody,\n} from './utils';\n\nexport async function transmitSET(\n jwt: string,\n url: string,\n options: TransmitOptions = {},\n): Promise<TransmitResult> {\n // Validate JWT format\n if (!isValidSET(jwt)) {\n throw new ValidationError('Invalid SET format: JWT must be in format header.payload.signature');\n }\n\n // Validate URL\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(url);\n } catch {\n throw new ValidationError(`Invalid URL: ${url}`);\n }\n\n // Merge options with defaults\n const mergedOptions = {\n authToken: options.authToken,\n headers: options.headers || {},\n timeout: options.timeout ?? DEFAULT_OPTIONS.timeout!,\n parseResponse: options.parseResponse ?? DEFAULT_OPTIONS.parseResponse!,\n validateStatus: options.validateStatus ?? DEFAULT_OPTIONS.validateStatus!,\n retry: {\n ...DEFAULT_RETRY_CONFIG,\n ...(options.retry || {}),\n } as Required<RetryConfig>,\n };\n\n // Prepare headers\n const baseHeaders: Record<string, string> = {\n 'Content-Type': CONTENT_TYPE_SET,\n Accept: CONTENT_TYPE_JSON,\n 'User-Agent': DEFAULT_USER_AGENT,\n };\n\n // Add authorization header if provided\n const authToken = normalizeAuthToken(mergedOptions.authToken);\n if (authToken) {\n baseHeaders['Authorization'] = authToken;\n }\n\n // Merge custom headers\n const headers = mergeHeaders(baseHeaders, mergedOptions.headers);\n\n // Attempt transmission with retry logic\n let lastError: Error | undefined;\n let lastResponse: Response | undefined;\n\n for (let attempt = 1; attempt <= mergedOptions.retry.maxAttempts; attempt++) {\n try {\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), mergedOptions.timeout);\n\n try {\n // Make the request\n const response = await fetch(parsedUrl.toString(), {\n method: 'POST',\n headers,\n body: jwt,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n lastResponse = response;\n\n // Parse response\n const responseHeaders = parseResponseHeaders(response.headers);\n const responseBody = await parseResponseBody(response, mergedOptions.parseResponse);\n\n // Check if status is successful\n const isSuccess = mergedOptions.validateStatus(response.status);\n\n if (isSuccess) {\n return {\n status: 'success',\n statusCode: response.status,\n body: responseBody,\n headers: responseHeaders,\n };\n }\n\n // Check if we should retry\n const canRetry = shouldRetry(response.status, attempt, mergedOptions.retry);\n if (!canRetry) {\n return {\n status: 'failed',\n statusCode: response.status,\n body: responseBody,\n headers: responseHeaders,\n error: `HTTP ${response.status}: ${response.statusText}`,\n retryable: mergedOptions.retry.retryableStatuses.includes(response.status),\n };\n }\n\n // Calculate backoff delay\n const retryAfterMs = parseRetryAfter(responseHeaders['retry-after']);\n const backoffMs = calculateBackoff(attempt, mergedOptions.retry, retryAfterMs);\n\n // Wait before retrying\n await delay(backoffMs);\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n lastError = new TimeoutError('Request timed out', mergedOptions.timeout);\n } else {\n lastError = new NetworkError(`Network error: ${error.message}`, error);\n }\n } else {\n lastError = new NetworkError('Unknown network error');\n }\n\n // Check if we should retry network errors\n if (!shouldRetry(undefined, attempt, mergedOptions.retry)) {\n throw lastError;\n }\n\n // Calculate backoff delay\n const backoffMs = calculateBackoff(attempt, mergedOptions.retry);\n await delay(backoffMs);\n }\n } catch (error) {\n // Re-throw if it's not a retryable error\n if (error instanceof ValidationError) {\n throw error;\n }\n lastError = error instanceof Error ? error : new Error(String(error));\n }\n }\n\n // If we've exhausted all retries\n if (lastResponse) {\n const responseHeaders = parseResponseHeaders(lastResponse.headers);\n // Body may have already been consumed, so handle the error\n let responseBody: string | Record<string, unknown> = '';\n try {\n responseBody = await parseResponseBody(lastResponse, mergedOptions.parseResponse);\n } catch {\n // Body already consumed, use empty string\n responseBody = '';\n }\n\n return {\n status: 'failed',\n statusCode: lastResponse.status,\n body: responseBody,\n headers: responseHeaders,\n error: lastError?.message || `HTTP ${lastResponse.status}: ${lastResponse.statusText}`,\n retryable: true,\n };\n }\n\n // Network error after all retries\n throw (\n lastError ||\n new TransmissionError('Failed to transmit SET after all retry attempts', undefined, true)\n );\n}\n\nexport function createTransmitter(\n defaultOptions?: TransmitOptions,\n): (jwt: string, url: string, options?: TransmitOptions) => Promise<TransmitResult> {\n return (jwt: string, url: string, options?: TransmitOptions) => {\n const mergedOptions = {\n ...defaultOptions,\n ...options,\n headers: {\n ...(defaultOptions?.headers || {}),\n ...(options?.headers || {}),\n },\n retry: {\n ...(defaultOptions?.retry || {}),\n ...(options?.retry || {}),\n },\n };\n return transmitSET(jwt, url, mergedOptions);\n };\n}\n"]}