@fgv/ts-extras 5.1.0-19 → 5.1.0-20
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.browser.js.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/packlets/ai-assist/apiClient.js.map +1 -0
- package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -0
- package/dist/packlets/ai-assist/converters.js.map +1 -0
- package/dist/packlets/ai-assist/index.js.map +1 -0
- package/dist/packlets/ai-assist/model.js.map +1 -0
- package/dist/packlets/ai-assist/registry.js.map +1 -0
- package/dist/packlets/ai-assist/sseParser.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -0
- package/dist/packlets/ai-assist/streamingClient.js.map +1 -0
- package/dist/packlets/ai-assist/toolFormats.js.map +1 -0
- package/dist/packlets/conversion/converters.js.map +1 -0
- package/dist/packlets/conversion/index.js.map +1 -0
- package/dist/packlets/crypto-utils/constants.js.map +1 -0
- package/dist/packlets/crypto-utils/converters.js.map +1 -0
- package/dist/packlets/crypto-utils/directEncryptionProvider.js.map +1 -0
- package/dist/packlets/crypto-utils/encryptedFile.js.map +1 -0
- package/dist/packlets/crypto-utils/index.browser.js.map +1 -0
- package/dist/packlets/crypto-utils/index.js.map +1 -0
- package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js +10 -0
- package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/converters.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/index.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/model.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -0
- package/dist/packlets/crypto-utils/model.js +5 -1
- package/dist/packlets/crypto-utils/model.js.map +1 -0
- package/dist/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -0
- package/dist/packlets/csv/csvFileHelpers.js.map +1 -0
- package/dist/packlets/csv/csvHelpers.js.map +1 -0
- package/dist/packlets/csv/index.browser.js.map +1 -0
- package/dist/packlets/csv/index.js.map +1 -0
- package/dist/packlets/experimental/extendedArray.js.map +1 -0
- package/dist/packlets/experimental/formatter.js.map +1 -0
- package/dist/packlets/experimental/index.js.map +1 -0
- package/dist/packlets/experimental/rangeOf.js.map +1 -0
- package/dist/packlets/hash/index.browser.js.map +1 -0
- package/dist/packlets/hash/index.js.map +1 -0
- package/dist/packlets/hash/index.node.js.map +1 -0
- package/dist/packlets/hash/md5Normalizer.browser.js.map +1 -0
- package/dist/packlets/hash/md5Normalizer.js.map +1 -0
- package/dist/packlets/mustache/index.js.map +1 -0
- package/dist/packlets/mustache/interfaces.js.map +1 -0
- package/dist/packlets/mustache/mustacheTemplate.js.map +1 -0
- package/dist/packlets/record-jar/index.browser.js.map +1 -0
- package/dist/packlets/record-jar/index.js.map +1 -0
- package/dist/packlets/record-jar/recordJarFileHelpers.js.map +1 -0
- package/dist/packlets/record-jar/recordJarHelpers.js.map +1 -0
- package/dist/packlets/yaml/converters.js.map +1 -0
- package/dist/packlets/yaml/index.js.map +1 -0
- package/dist/packlets/yaml/serializers.js.map +1 -0
- package/dist/packlets/zip-file-tree/index.js.map +1 -0
- package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -0
- package/dist/packlets/zip-file-tree/zipFileTreeWriter.js.map +1 -0
- package/dist/ts-extras.d.ts +5 -1
- package/lib/index.browser.d.ts.map +1 -0
- package/lib/index.browser.js.map +1 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js.map +1 -0
- package/lib/packlets/ai-assist/apiClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/apiClient.js.map +1 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts.map +1 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -0
- package/lib/packlets/ai-assist/converters.d.ts.map +1 -0
- package/lib/packlets/ai-assist/converters.js.map +1 -0
- package/lib/packlets/ai-assist/index.d.ts.map +1 -0
- package/lib/packlets/ai-assist/index.js.map +1 -0
- package/lib/packlets/ai-assist/model.d.ts.map +1 -0
- package/lib/packlets/ai-assist/model.js.map +1 -0
- package/lib/packlets/ai-assist/registry.d.ts.map +1 -0
- package/lib/packlets/ai-assist/registry.js.map +1 -0
- package/lib/packlets/ai-assist/sseParser.d.ts.map +1 -0
- package/lib/packlets/ai-assist/sseParser.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -0
- package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingClient.js.map +1 -0
- package/lib/packlets/ai-assist/toolFormats.d.ts.map +1 -0
- package/lib/packlets/ai-assist/toolFormats.js.map +1 -0
- package/lib/packlets/conversion/converters.d.ts.map +1 -0
- package/lib/packlets/conversion/converters.js.map +1 -0
- package/lib/packlets/conversion/index.d.ts.map +1 -0
- package/lib/packlets/conversion/index.js.map +1 -0
- package/lib/packlets/crypto-utils/constants.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/constants.js.map +1 -0
- package/lib/packlets/crypto-utils/converters.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/converters.js.map +1 -0
- package/lib/packlets/crypto-utils/directEncryptionProvider.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/directEncryptionProvider.js.map +1 -0
- package/lib/packlets/crypto-utils/encryptedFile.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/encryptedFile.js.map +1 -0
- package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/index.browser.js.map +1 -0
- package/lib/packlets/crypto-utils/index.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/index.js.map +1 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js +10 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/converters.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/converters.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/model.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -0
- package/lib/packlets/crypto-utils/model.d.ts +5 -1
- package/lib/packlets/crypto-utils/model.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/model.js +5 -1
- package/lib/packlets/crypto-utils/model.js.map +1 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -0
- package/lib/packlets/csv/csvFileHelpers.d.ts.map +1 -0
- package/lib/packlets/csv/csvFileHelpers.js.map +1 -0
- package/lib/packlets/csv/csvHelpers.d.ts.map +1 -0
- package/lib/packlets/csv/csvHelpers.js.map +1 -0
- package/lib/packlets/csv/index.browser.d.ts.map +1 -0
- package/lib/packlets/csv/index.browser.js.map +1 -0
- package/lib/packlets/csv/index.d.ts.map +1 -0
- package/lib/packlets/csv/index.js.map +1 -0
- package/lib/packlets/experimental/extendedArray.d.ts.map +1 -0
- package/lib/packlets/experimental/extendedArray.js.map +1 -0
- package/lib/packlets/experimental/formatter.d.ts.map +1 -0
- package/lib/packlets/experimental/formatter.js.map +1 -0
- package/lib/packlets/experimental/index.d.ts.map +1 -0
- package/lib/packlets/experimental/index.js.map +1 -0
- package/lib/packlets/experimental/rangeOf.d.ts.map +1 -0
- package/lib/packlets/experimental/rangeOf.js.map +1 -0
- package/lib/packlets/hash/index.browser.d.ts.map +1 -0
- package/lib/packlets/hash/index.browser.js.map +1 -0
- package/lib/packlets/hash/index.d.ts.map +1 -0
- package/lib/packlets/hash/index.js.map +1 -0
- package/lib/packlets/hash/index.node.d.ts.map +1 -0
- package/lib/packlets/hash/index.node.js.map +1 -0
- package/lib/packlets/hash/md5Normalizer.browser.d.ts.map +1 -0
- package/lib/packlets/hash/md5Normalizer.browser.js.map +1 -0
- package/lib/packlets/hash/md5Normalizer.d.ts.map +1 -0
- package/lib/packlets/hash/md5Normalizer.js.map +1 -0
- package/lib/packlets/mustache/index.d.ts.map +1 -0
- package/lib/packlets/mustache/index.js.map +1 -0
- package/lib/packlets/mustache/interfaces.d.ts.map +1 -0
- package/lib/packlets/mustache/interfaces.js.map +1 -0
- package/lib/packlets/mustache/mustacheTemplate.d.ts.map +1 -0
- package/lib/packlets/mustache/mustacheTemplate.js.map +1 -0
- package/lib/packlets/record-jar/index.browser.d.ts.map +1 -0
- package/lib/packlets/record-jar/index.browser.js.map +1 -0
- package/lib/packlets/record-jar/index.d.ts.map +1 -0
- package/lib/packlets/record-jar/index.js.map +1 -0
- package/lib/packlets/record-jar/recordJarFileHelpers.d.ts.map +1 -0
- package/lib/packlets/record-jar/recordJarFileHelpers.js.map +1 -0
- package/lib/packlets/record-jar/recordJarHelpers.d.ts.map +1 -0
- package/lib/packlets/record-jar/recordJarHelpers.js.map +1 -0
- package/lib/packlets/yaml/converters.d.ts.map +1 -0
- package/lib/packlets/yaml/converters.js.map +1 -0
- package/lib/packlets/yaml/index.d.ts.map +1 -0
- package/lib/packlets/yaml/index.js.map +1 -0
- package/lib/packlets/yaml/serializers.d.ts.map +1 -0
- package/lib/packlets/yaml/serializers.js.map +1 -0
- package/lib/packlets/zip-file-tree/index.d.ts.map +1 -0
- package/lib/packlets/zip-file-tree/index.js.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeWriter.d.ts.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeWriter.js.map +1 -0
- package/package.json +7 -7
- package/dist/test/unit/crypto/keystore/inMemoryPrivateKeyStorage.js +0 -78
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openaiResponses.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/openaiResponses.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Streaming adapter for the OpenAI / xAI Responses API. This is the format\n * used when server-side tools (e.g. web_search) are requested — Chat\n * Completions doesn't support tool progress events, but the Responses API\n * does.\n *\n * @packageDocumentation\n */\n\nimport { type Logging, Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\n\nimport { buildMessages, buildOpenAiResponsesUserContent } from '../chatRequestBuilders';\nimport { AiPrompt, type AiServerToolConfig, type IAiStreamEvent, type IChatMessage } from '../model';\nimport { parseSseEventJson, readSseEvents } from '../sseParser';\nimport { toResponsesApiTools } from '../toolFormats';\nimport { IStreamApiConfig, openSseConnection, validateEventPayload } from './common';\n\n// ============================================================================\n// Event payload shapes\n// ============================================================================\n\n/**\n * Payload of a `response.output_text.delta` SSE event.\n *\n * @internal\n */\ninterface IResponsesDeltaPayload {\n readonly delta: string;\n}\n\n/**\n * Payload of a `response.completed` SSE event. `status === 'incomplete'`\n * signals the stream was cut short (max output tokens, etc.).\n *\n * @internal\n */\ninterface IResponsesCompletedPayload {\n readonly response: { readonly status?: string };\n}\n\n/**\n * Payload of a `response.failed` or `error` SSE event. Both shapes appear\n * in the wild — sometimes `error.message`, sometimes a top-level `message`.\n *\n * @internal\n */\ninterface IResponsesErrorPayload {\n readonly error?: { readonly message?: string };\n readonly message?: string;\n}\n\nconst responsesDeltaPayload: Validator<IResponsesDeltaPayload> = Validators.object<IResponsesDeltaPayload>({\n delta: Validators.string\n});\n\nconst responsesCompletedPayload: Validator<IResponsesCompletedPayload> =\n Validators.object<IResponsesCompletedPayload>({\n response: Validators.object<{ status?: string }>(\n { status: Validators.string.optional() },\n { options: { optionalFields: ['status'] } }\n )\n });\n\nconst responsesErrorInner: Validator<{ message?: string }> = Validators.object<{ message?: string }>(\n { message: Validators.string.optional() },\n { options: { optionalFields: ['message'] } }\n);\n\nconst responsesErrorPayload: Validator<IResponsesErrorPayload> = Validators.object<IResponsesErrorPayload>(\n {\n error: responsesErrorInner.optional(),\n message: Validators.string.optional()\n },\n { options: { optionalFields: ['error', 'message'] } }\n);\n\n// ============================================================================\n// Stream translator\n// ============================================================================\n\n/**\n * Translates an OpenAI Responses API SSE stream into unified events.\n *\n * @internal\n */\nasync function* translateOpenAiResponsesStream(response: Response): AsyncGenerator<IAiStreamEvent> {\n let fullText = '';\n let truncated = false;\n let completed = false;\n\n try {\n /* c8 ignore next - body is non-null at this point per openSseConnection */\n if (!response.body) return;\n for await (const message of readSseEvents(response.body)) {\n const eventName = message.event;\n if (eventName === 'response.output_text.delta') {\n const payload = validateEventPayload(parseSseEventJson(message.data), responsesDeltaPayload);\n const delta = payload?.delta;\n if (typeof delta === 'string' && delta.length > 0) {\n fullText += delta;\n yield { type: 'text-delta', delta };\n }\n } else if (eventName === 'response.web_search_call.in_progress') {\n yield { type: 'tool-event', toolType: 'web_search', phase: 'started' };\n } else if (eventName === 'response.web_search_call.completed') {\n yield { type: 'tool-event', toolType: 'web_search', phase: 'completed' };\n } else if (eventName === 'response.completed') {\n const payload = validateEventPayload(parseSseEventJson(message.data), responsesCompletedPayload);\n truncated = payload?.response.status === 'incomplete';\n completed = true;\n } else if (eventName === 'response.failed' || eventName === 'error') {\n const payload = validateEventPayload(parseSseEventJson(message.data), responsesErrorPayload);\n const errMsg = payload?.error?.message ?? payload?.message ?? 'Responses API stream failed';\n yield { type: 'error', message: errMsg };\n return;\n }\n }\n } catch (err: unknown) {\n yield { type: 'error', message: err instanceof Error ? err.message : String(err) };\n return;\n }\n\n if (completed) {\n yield { type: 'done', truncated, fullText };\n } else {\n yield { type: 'error', message: 'Responses API stream ended without a completed event' };\n }\n}\n\n// ============================================================================\n// Per-format request caller\n// ============================================================================\n\n/**\n * Issues a streaming Responses API request (with tools) and returns the\n * unified-event iterable on success.\n *\n * @internal\n */\nexport async function callOpenAiResponsesStream(\n config: IStreamApiConfig,\n prompt: AiPrompt,\n tools: ReadonlyArray<AiServerToolConfig>,\n messagesBefore: ReadonlyArray<IChatMessage> | undefined,\n temperature: number,\n logger?: Logging.ILogger,\n signal?: AbortSignal\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const url = `${config.baseUrl}/responses`;\n const input = buildMessages(prompt.system, buildOpenAiResponsesUserContent(prompt), {\n head: messagesBefore\n });\n const body: Record<string, unknown> = {\n model: config.model,\n input,\n tools: toResponsesApiTools(tools),\n temperature,\n stream: true\n };\n const headers: Record<string, string> = { Authorization: `Bearer ${config.apiKey}` };\n /* c8 ignore next 1 - optional logger */\n logger?.info(\n `OpenAI Responses streaming: model=${config.model}, tools=${tools.map((t) => t.type).join(',')}`\n );\n const conn = await openSseConnection(url, headers, body, logger, signal);\n return conn.onSuccess((response) => succeed(translateOpenAiResponsesStream(response)));\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/proxy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Streaming adapter for a caller-provided proxy server. Unlike the\n * provider-specific adapters, the proxy speaks our own unified vocabulary\n * directly: each `data:` line is a JSON-serialized {@link AiAssist.IAiStreamEvent},\n * so this adapter only validates the event-type discriminator and forwards.\n *\n * @packageDocumentation\n */\n\nimport { Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\n\nimport { type IAiStreamEvent } from '../model';\nimport { parseSseEventJson, readSseEvents } from '../sseParser';\nimport { IProviderCompletionStreamParams, openSseConnection, validateEventPayload } from './common';\n\n// ============================================================================\n// Event payload shape — a tagged-event envelope\n// ============================================================================\n\ntype ProxyEventType = 'text-delta' | 'tool-event' | 'done' | 'error';\n\n/**\n * Minimal envelope used to identify and discriminate proxy events. Once the\n * `type` is recognized, the event is forwarded as-is — the unified shape is\n * the proxy contract, so there's no further per-type validation here.\n *\n * @internal\n */\ninterface IProxyEventEnvelope {\n readonly type: ProxyEventType;\n}\n\nconst proxyEventTypes: ReadonlyArray<ProxyEventType> = ['text-delta', 'tool-event', 'done', 'error'];\n\nconst proxyEventEnvelope: Validator<IProxyEventEnvelope> = Validators.object<IProxyEventEnvelope>({\n type: Validators.enumeratedValue<ProxyEventType>(proxyEventTypes)\n});\n\n// ============================================================================\n// Stream translator\n// ============================================================================\n\n/**\n * Translates a proxied SSE stream back into {@link AiAssist.IAiStreamEvent} objects.\n * Validation is limited to the type discriminator; the proxy is contractually\n * required to emit shape-correct unified events.\n *\n * @internal\n */\nasync function* translateProxyStream(response: Response): AsyncGenerator<IAiStreamEvent> {\n try {\n /* c8 ignore next - body is non-null at this point per openSseConnection */\n if (!response.body) return;\n for await (const message of readSseEvents(response.body)) {\n const json = parseSseEventJson(message.data);\n if (json === undefined) {\n continue;\n }\n const envelope = validateEventPayload(json, proxyEventEnvelope);\n if (!envelope) {\n continue;\n }\n const event = json as IAiStreamEvent;\n yield event;\n if (envelope.type === 'done' || envelope.type === 'error') {\n return;\n }\n }\n } catch (err: unknown) {\n yield { type: 'error', message: err instanceof Error ? err.message : String(err) };\n }\n}\n\n// ============================================================================\n// Public entry point\n// ============================================================================\n\n/**\n * Calls the streaming chat endpoint on a proxy server instead of calling\n * the provider directly from the browser.\n *\n * @remarks\n * Proxy contract:\n * - Endpoint: `POST ${proxyUrl}/api/ai/completion-stream`\n * - Request body: same JSON as `/api/ai/completion` plus `\"stream\": true`\n * - Response: `Content-Type: text/event-stream`; body is the unified\n * {@link AiAssist.IAiStreamEvent} JSON-serialized one event per SSE `data:` line\n * (no `event:` line needed since the type discriminator is in the JSON).\n * - Error response (when the proxy can't even start): JSON `{error: string}`\n * with a non-2xx status, surfaced as `proxy: ${error}`.\n *\n * The proxy server is responsible for opening the upstream SSE connection,\n * translating provider-native events to the unified vocabulary, and\n * forwarding events as they arrive (no buffering). The library does not\n * ship a proxy implementation.\n *\n * @public\n */\nexport async function callProxiedCompletionStream(\n proxyUrl: string,\n params: IProviderCompletionStreamParams\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const { descriptor, apiKey, prompt, messagesBefore, temperature, modelOverride, logger, tools, signal } =\n params;\n\n const promptBody: Record<string, unknown> = { system: prompt.system, user: prompt.user };\n if (prompt.attachments.length > 0) {\n promptBody.attachments = prompt.attachments;\n }\n const body: Record<string, unknown> = {\n providerId: descriptor.id,\n apiKey,\n prompt: promptBody,\n temperature: temperature ?? 0.7,\n stream: true\n };\n if (messagesBefore && messagesBefore.length > 0) {\n body.messagesBefore = messagesBefore;\n }\n if (modelOverride !== undefined) {\n body.modelOverride = modelOverride;\n }\n if (tools && tools.length > 0) {\n body.tools = tools;\n }\n\n /* c8 ignore next 1 - optional logger */\n logger?.info(`AI streaming proxy request: provider=${descriptor.id}, proxy=${proxyUrl}`);\n\n const url = `${proxyUrl}/api/ai/completion-stream`;\n const conn = await openSseConnection(url, {}, body, logger, signal);\n return conn.onSuccess((response) => succeed(translateProxyStream(response)));\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamingClient.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/streamingClient.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Streaming chat completion client. Public façade over the per-format\n * adapters under `streamingAdapters/`: provider dispatch and pre-flight\n * validation live here; format-specific request/translation logic and the\n * typed event-payload validators live with each adapter.\n *\n * @packageDocumentation\n */\n\nimport { fail, Result } from '@fgv/ts-utils';\n\nimport { type IAiStreamEvent, resolveModel } from './model';\nimport { callAnthropicStream } from './streamingAdapters/anthropic';\nimport { type IProviderCompletionStreamParams, type IStreamApiConfig } from './streamingAdapters/common';\nimport { callGeminiStream } from './streamingAdapters/gemini';\nimport { callOpenAiChatStream } from './streamingAdapters/openaiChat';\nimport { callOpenAiResponsesStream } from './streamingAdapters/openaiResponses';\n\nexport { callProxiedCompletionStream } from './streamingAdapters/proxy';\nexport type { IProviderCompletionStreamParams } from './streamingAdapters/common';\n\n/**\n * Calls the appropriate streaming chat completion API for a given provider.\n *\n * @remarks\n * Pre-flight rejection: when `descriptor.streamingCorsRestricted === true`\n * and the call isn't being routed through a proxy, this returns\n * `Result.fail` before fetch is invoked. Callers should route through\n * {@link AiAssist.callProxiedCompletionStream} or surface the failure to the user.\n *\n * Connection-time failures (auth, network, non-2xx) surface as the outer\n * `Result.fail`. Once iteration begins, errors mid-stream surface as a\n * terminal error event ({@link AiAssist.IAiStreamError}) followed by the iterable\n * ending. The final successful event is {@link AiAssist.IAiStreamDone}.\n *\n * @param params - Request parameters including descriptor, API key, prompt, and optional tools\n * @returns A streaming iterable of unified events, or a Result.fail\n * @public\n */\nexport async function callProviderCompletionStream(\n params: IProviderCompletionStreamParams\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const {\n descriptor,\n apiKey,\n prompt,\n messagesBefore,\n temperature = 0.7,\n modelOverride,\n logger,\n tools,\n signal\n } = params;\n\n if (!descriptor.baseUrl) {\n return fail(`provider \"${descriptor.id}\" has no API endpoint configured`);\n }\n if (descriptor.streamingCorsRestricted) {\n return fail(`provider \"${descriptor.id}\" requires a proxy for streaming; none is configured`);\n }\n if (prompt.attachments.length > 0 && !descriptor.acceptsImageInput) {\n return fail(`provider \"${descriptor.id}\" does not accept image input`);\n }\n\n const hasTools = tools !== undefined && tools.length > 0;\n const modelContext = hasTools ? 'tools' : undefined;\n\n const config: IStreamApiConfig = {\n baseUrl: descriptor.baseUrl,\n apiKey,\n model: resolveModel(modelOverride ?? descriptor.defaultModel, modelContext)\n };\n\n switch (descriptor.apiFormat) {\n case 'openai':\n if (hasTools) {\n return callOpenAiResponsesStream(config, prompt, tools, messagesBefore, temperature, logger, signal);\n }\n return callOpenAiChatStream(config, prompt, messagesBefore, temperature, logger, signal);\n case 'anthropic':\n return callAnthropicStream(config, prompt, messagesBefore, temperature, tools, logger, signal);\n case 'gemini':\n return callGeminiStream(config, prompt, messagesBefore, temperature, tools, logger, signal);\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = descriptor.apiFormat;\n return fail(`unsupported API format: ${String(_exhaustive)}`);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolFormats.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/toolFormats.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Provider-specific tool format translation and tool resolution logic.\n * @packageDocumentation\n */\n\nimport { type JsonObject } from '@fgv/ts-json-base';\n\nimport {\n type AiServerToolConfig,\n type IAiProviderDescriptor,\n type IAiToolEnablement,\n type IAiWebSearchToolConfig\n} from './model';\n\n// ============================================================================\n// Tool resolution\n// ============================================================================\n\n/**\n * Resolves the effective tools for a completion call.\n *\n * - If per-call tools are provided, they override settings-level tools entirely.\n * - Otherwise, settings-level enabled tools are used.\n * - Only tools supported by the provider are included.\n * - Returns an empty array if no tools are enabled (= no tools sent).\n *\n * @param descriptor - The provider descriptor (used to filter by supported tools)\n * @param settingsTools - Tool enablement from provider settings (optional)\n * @param perCallTools - Per-call tool override (optional)\n * @returns The resolved list of tool configs to include in the request\n * @public\n */\nexport function resolveEffectiveTools(\n descriptor: IAiProviderDescriptor,\n settingsTools?: ReadonlyArray<IAiToolEnablement>,\n perCallTools?: ReadonlyArray<AiServerToolConfig>\n): ReadonlyArray<AiServerToolConfig> {\n const supported = new Set(descriptor.supportedTools);\n\n if (perCallTools !== undefined) {\n return perCallTools.filter((t) => supported.has(t.type));\n }\n\n if (settingsTools === undefined) {\n return [];\n }\n\n return settingsTools\n .filter((e) => e.enabled && supported.has(e.type))\n .map((e): AiServerToolConfig => e.config ?? { type: e.type });\n}\n\n// ============================================================================\n// OpenAI / xAI Responses API format\n// ============================================================================\n\n/**\n * Formats a web search tool config for the xAI/OpenAI Responses API.\n * @internal\n */\nfunction webSearchToResponsesApi(config: IAiWebSearchToolConfig): JsonObject {\n const tool: Record<string, unknown> = { type: 'web_search' };\n\n if (config.allowedDomains || config.blockedDomains) {\n const filters: Record<string, unknown> = {};\n if (config.allowedDomains) {\n filters.allowed_domains = [...config.allowedDomains];\n }\n if (config.blockedDomains) {\n filters.excluded_domains = [...config.blockedDomains];\n }\n tool.filters = filters;\n }\n\n if (config.enableImageUnderstanding) {\n tool.enable_image_understanding = true;\n }\n\n return tool as JsonObject;\n}\n\n/**\n * Formats tool configs for the xAI/OpenAI Responses API.\n * @param tools - The resolved tool configs\n * @returns Provider-native tool objects for the `tools` request field\n * @public\n */\nexport function toResponsesApiTools(tools: ReadonlyArray<AiServerToolConfig>): ReadonlyArray<JsonObject> {\n return tools.map((t) => {\n switch (t.type) {\n case 'web_search':\n return webSearchToResponsesApi(t);\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = t.type;\n return { type: String(_exhaustive) } as JsonObject;\n }\n }\n });\n}\n\n// ============================================================================\n// Anthropic Messages API format\n// ============================================================================\n\n/**\n * Formats a web search tool config for the Anthropic Messages API.\n * @internal\n */\nfunction webSearchToAnthropic(config: IAiWebSearchToolConfig): JsonObject {\n const tool: Record<string, unknown> = {\n type: 'web_search_20250305',\n name: 'web_search'\n };\n\n if (config.maxUses !== undefined) {\n tool.max_uses = config.maxUses;\n }\n if (config.allowedDomains) {\n tool.allowed_domains = [...config.allowedDomains];\n }\n if (config.blockedDomains) {\n tool.blocked_domains = [...config.blockedDomains];\n }\n\n return tool as JsonObject;\n}\n\n/**\n * Formats tool configs for the Anthropic Messages API.\n * @param tools - The resolved tool configs\n * @returns Provider-native tool objects for the `tools` request field\n * @public\n */\nexport function toAnthropicTools(tools: ReadonlyArray<AiServerToolConfig>): ReadonlyArray<JsonObject> {\n return tools.map((t) => {\n switch (t.type) {\n case 'web_search':\n return webSearchToAnthropic(t);\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = t.type;\n return { type: String(_exhaustive) } as JsonObject;\n }\n }\n });\n}\n\n// ============================================================================\n// Gemini generateContent API format\n// ============================================================================\n\n/**\n * Formats tool configs for the Gemini generateContent API.\n * Gemini uses `google_search` for search grounding — no per-tool config options.\n * @param tools - The resolved tool configs\n * @returns Provider-native tool objects for the `tools` request field\n * @public\n */\nexport function toGeminiTools(tools: ReadonlyArray<AiServerToolConfig>): ReadonlyArray<JsonObject> {\n const result: JsonObject[] = [];\n\n for (const t of tools) {\n switch (t.type) {\n case 'web_search':\n result.push({ google_search: {} } as JsonObject);\n break;\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = t.type;\n result.push({ type: String(_exhaustive) } as JsonObject);\n }\n }\n }\n\n return result;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"converters.js","sourceRoot":"","sources":["../../../src/packlets/conversion/converters.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2020 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Conversion, Converter, Converters, Result, captureResult, fail, succeed } from '@fgv/ts-utils';\nimport { DateTime } from 'luxon';\nimport Mustache from 'mustache';\nimport { ExtendedArray, RangeOf, RangeOfProperties } from '../experimental';\n\n/**\n * Helper function to create a `StringConverter` which converts\n * `unknown` to `string`, applying template conversions supplied at construction time or at\n * runtime as context.\n * @remarks\n * Template conversions are applied using `mustache` syntax.\n * @param defaultContext - Optional default context to use for template values.\n * @returns A new `Converter` returning `string`.\n * @public\n */\nexport function templateString(defaultContext?: unknown): Conversion.StringConverter<string, unknown> {\n return new Conversion.StringConverter<string, unknown>(\n defaultContext,\n undefined,\n (from: unknown, __self: Converter<string, unknown>, context?: unknown) => {\n if (typeof from !== 'string') {\n return fail(`Not a string: ${JSON.stringify(from)}`);\n }\n return captureResult(() => Mustache.render(from, context));\n }\n );\n}\n\n/**\n * A `Converter` which converts an iso formatted string, a number or a `Date` object to\n * a `Date` object.\n * @public\n */\nexport const isoDate: Converter<Date, unknown> = new Conversion.BaseConverter<Date>((from: unknown) => {\n if (typeof from === 'string') {\n const dt = DateTime.fromISO(from);\n if (dt.isValid) {\n return succeed(dt.toJSDate());\n }\n return fail(`Invalid date: ${dt.invalidExplanation}`);\n } else if (typeof from === 'number') {\n return succeed(new Date(from));\n } else if (from instanceof Date) {\n return succeed(from);\n } else if (from instanceof DateTime) {\n if (from.isValid) {\n return succeed(from.toJSDate());\n }\n return fail(`Invalid date: ${from.invalidExplanation}`);\n }\n return fail(`Cannot convert ${JSON.stringify(from)} to Date`);\n});\n\n/**\n * A `Converter` which converts an iso formatted string, a number or a `Date` object to\n * a `DateTime` object.\n * @public\n */\nexport const isoDateTime: Converter<DateTime, unknown> = new Conversion.BaseConverter<DateTime>(\n (from: unknown) => {\n if (typeof from === 'string') {\n const dt = DateTime.fromISO(from);\n if (dt.isValid) {\n return succeed(dt);\n }\n return fail(`Invalid date: ${dt.invalidExplanation}`);\n } else if (typeof from === 'number') {\n return succeed(DateTime.fromMillis(from));\n } else if (from instanceof Date) {\n return succeed(DateTime.fromJSDate(from));\n } else if (from instanceof DateTime) {\n if (from.isValid) {\n return succeed(from);\n }\n return fail(`Invalid date: ${from.invalidExplanation}`);\n }\n return fail(`Cannot convert ${JSON.stringify(from)} to DateTime`);\n }\n);\n\n/**\n * A helper function to create a `Converter` which converts `unknown` to {@link Experimental.ExtendedArray | ExtendedArray<T>}.\n * @remarks\n * If `onError` is `'failOnError'` (default), then the entire conversion fails if any element cannot\n * be converted. If `onError` is `'ignoreErrors'`, then failing elements are silently ignored.\n * @param converter - `Converter` used to convert each item in the array\n * @param onError - Specifies treatment of unconvertible elements\n * @beta\n */\nexport function extendedArrayOf<T, TC = undefined>(\n label: string,\n converter: Converter<T, TC>,\n onError: Conversion.OnError = 'failOnError'\n): Converter<ExtendedArray<T>, TC> {\n return Converters.arrayOf(converter, onError).map((items: T[]) => {\n return captureResult(() => new ExtendedArray(label, ...items));\n });\n}\n\n/**\n * A helper wrapper to construct a `Converter` which converts to an arbitrary strongly-typed\n * range of some comparable type.\n * @param converter - `Converter` used to convert `min` and `max` extent of the range.\n * @param constructor - Static constructor to instantiate the object.\n * @public\n */\nexport function rangeTypeOf<T, RT extends RangeOf<T>, TC = unknown>(\n converter: Converter<T, TC>,\n constructor: (init: RangeOfProperties<T>) => Result<RT>\n): Converter<RT, TC> {\n return new Conversion.BaseConverter((from: unknown, __self, context?: TC) => {\n const result = Converters.object(\n {\n min: converter,\n max: converter\n },\n { optionalFields: ['min', 'max'] }\n ).convert(from, context);\n if (result.isSuccess()) {\n return constructor({ min: result.value.min, max: result.value.max });\n }\n return fail(result.message);\n });\n}\n\n/**\n * A helper wrapper to construct a `Converter` which converts to {@link Experimental.RangeOf | RangeOf<T>}\n * where `<T>` is some comparable type.\n * @param converter - `Converter` used to convert `min` and `max` extent of the range.\n * @public\n */\nexport function rangeOf<T, TC = unknown>(converter: Converter<T, TC>): Converter<RangeOf<T>, TC> {\n return rangeTypeOf<T, RangeOf<T>, TC>(converter, RangeOf.createRange);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/conversion/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,CAAC","sourcesContent":["/*\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport * as Converters from './converters';\n\nexport { Converters };\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/constants.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;AAEZ,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,yBAAkC,CAAC;AAExE;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAsB,CAAC;AAExD;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAW,EAAE,CAAC;AAE3C;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAW,EAAE,CAAC;AAEtC;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAW,EAAE,CAAC","sourcesContent":["// Copyright (c) 2024 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * Current format version for encrypted files.\n * @public\n */\nexport const ENCRYPTED_FILE_FORMAT = 'encrypted-collection-v1' as const;\n\n/**\n * Default encryption algorithm.\n * @public\n */\nexport const DEFAULT_ALGORITHM = 'AES-256-GCM' as const;\n\n/**\n * Key size in bytes for AES-256.\n * @public\n */\nexport const AES_256_KEY_SIZE: number = 32;\n\n/**\n * IV size in bytes for GCM mode.\n * @public\n */\nexport const GCM_IV_SIZE: number = 12;\n\n/**\n * Auth tag size in bytes for GCM mode.\n * @public\n */\nexport const GCM_AUTH_TAG_SIZE: number = 16;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"converters.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/converters.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2024 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { Converters as JsonConverters, JsonValue } from '@fgv/ts-json-base';\nimport { Converter, Converters, fail, succeed } from '@fgv/ts-utils';\nimport * as Constants from './constants';\nimport {\n EncryptedFileErrorMode,\n EncryptedFileFormat,\n EncryptionAlgorithm,\n IEncryptedFile,\n IKeyDerivationParams,\n INamedSecret,\n KeyDerivationFunction\n} from './model';\n\n// ============================================================================\n// Base Converters\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.EncryptionAlgorithm | encryption algorithm} values.\n * @public\n */\nexport const encryptionAlgorithm: Converter<EncryptionAlgorithm> =\n Converters.enumeratedValue<EncryptionAlgorithm>([Constants.DEFAULT_ALGORITHM]);\n\n/**\n * Converter for {@link CryptoUtils.EncryptedFileFormat | encrypted file format} version.\n * @public\n */\nexport const encryptedFileFormat: Converter<EncryptedFileFormat> = Converters.enumeratedValue([\n Constants.ENCRYPTED_FILE_FORMAT\n]);\n\n/**\n * Converter for {@link CryptoUtils.EncryptedFileErrorMode | encrypted file error mode}.\n * @public\n */\nexport const encryptedFileErrorMode: Converter<EncryptedFileErrorMode> =\n Converters.enumeratedValue<EncryptedFileErrorMode>(['fail', 'skip', 'warn']);\n\n/**\n * Converter for {@link CryptoUtils.KeyDerivationFunction | key derivation function} type.\n * @public\n */\nexport const keyDerivationFunction: Converter<KeyDerivationFunction> =\n Converters.enumeratedValue<KeyDerivationFunction>(['pbkdf2']);\n\n/**\n * Converter for {@link CryptoUtils.IKeyDerivationParams | key derivation parameters}.\n * @public\n */\nexport const keyDerivationParams: Converter<IKeyDerivationParams> = Converters.object<IKeyDerivationParams>({\n kdf: keyDerivationFunction,\n salt: Converters.string,\n iterations: Converters.number\n});\n\n/**\n * Converter for base64 strings (validates format).\n * @public\n */\nexport const base64String: Converter<string> = Converters.string.withConstraint((value) => {\n // Basic base64 validation - check for valid characters and padding\n const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;\n if (!base64Regex.test(value)) {\n return fail('Invalid base64 encoding');\n }\n return succeed(value);\n});\n\n// ============================================================================\n// Uint8Array Converter\n// ============================================================================\n\n/**\n * Converter which converts a base64 string to a Uint8Array.\n * @public\n */\nexport const uint8ArrayFromBase64: Converter<Uint8Array> = Converters.string.map((base64) => {\n try {\n // Use Buffer in Node.js environment, atob in browser\n if (typeof Buffer !== 'undefined') {\n return succeed(Uint8Array.from(Buffer.from(base64, 'base64')));\n }\n /* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */\n const binaryString = atob(base64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return succeed(bytes);\n } catch (e) {\n // This catch is for browser's atob() which throws on invalid base64\n // Node's Buffer.from() doesn't throw, it ignores invalid characters\n const message = e instanceof Error ? e.message : String(e);\n return fail(`Invalid base64: ${message}`);\n }\n /* c8 ignore stop */\n});\n\n// ============================================================================\n// Named Secret Converter\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.INamedSecret | named secret} from JSON representation.\n * Expects key as base64 string in JSON, converts to Uint8Array.\n * @public\n */\nexport const namedSecret: Converter<INamedSecret> = Converters.object<INamedSecret>({\n name: Converters.string,\n key: uint8ArrayFromBase64\n});\n\n// ============================================================================\n// Encrypted File Converter Factory\n// ============================================================================\n\n/**\n * Base encrypted file structure without metadata typing.\n * Used internally by the converter factory.\n */\ninterface IBaseEncryptedFile {\n readonly format: EncryptedFileFormat;\n readonly secretName: string;\n readonly algorithm: EncryptionAlgorithm;\n readonly iv: string;\n readonly authTag: string;\n readonly encryptedData: string;\n readonly keyDerivation?: IKeyDerivationParams;\n readonly metadata?: JsonValue;\n}\n\n/**\n * Base converter for encrypted file structure (without typed metadata).\n */\nconst baseEncryptedFileConverter: Converter<IBaseEncryptedFile> = Converters.object<IBaseEncryptedFile>(\n {\n format: encryptedFileFormat,\n secretName: Converters.string,\n algorithm: encryptionAlgorithm,\n iv: base64String,\n authTag: base64String,\n encryptedData: base64String,\n keyDerivation: keyDerivationParams,\n metadata: JsonConverters.jsonValue\n },\n { optionalFields: ['keyDerivation', 'metadata'] }\n);\n\n/**\n * Creates a converter for {@link CryptoUtils.IEncryptedFile | encrypted files} with optional typed metadata.\n * @typeParam TMetadata - Type of optional unencrypted metadata\n * @param metadataConverter - Optional converter for validating metadata field\n * @returns A converter that validates and converts encrypted file structures\n * @public\n */\nexport function createEncryptedFileConverter<TMetadata = JsonValue>(\n metadataConverter?: Converter<TMetadata>\n): Converter<IEncryptedFile<TMetadata>> {\n return Converters.generic<IEncryptedFile<TMetadata>>((from: unknown) => {\n // First validate base structure\n const baseResult = baseEncryptedFileConverter.convert(from);\n if (baseResult.isFailure()) {\n return fail(baseResult.message);\n }\n\n const base = baseResult.value;\n\n // Validate metadata with specific converter if provided and metadata exists\n if (metadataConverter !== undefined && base.metadata !== undefined) {\n const metaResult = metadataConverter.convert(base.metadata);\n if (metaResult.isFailure()) {\n return fail(`Invalid metadata: ${metaResult.message}`);\n }\n return succeed({\n ...base,\n metadata: metaResult.value\n } as IEncryptedFile<TMetadata>);\n }\n\n // Return as-is (metadata is either undefined or untyped JsonValue)\n return succeed(base as IEncryptedFile<TMetadata>);\n });\n}\n\n/**\n * Default converter for encrypted files without typed metadata.\n * @public\n */\nexport const encryptedFile: Converter<IEncryptedFile> = createEncryptedFileConverter();\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directEncryptionProvider.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/directEncryptionProvider.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;AAGZ,OAAO,EAAU,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AA0BtD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,wBAAwB;IAKnC,YAAoB,MAAuC;QACzD,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,cAAc,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,eAAe,CAAC;IACjD,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,MAAM,CAAC,MAAuC;QAC1D,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,gCAAgC,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,OAAO,CAAC,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IAED;;;OAGG;IACH,IAAW,eAAe;QACxB,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC/B,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,aAAa,CACxB,UAAkB,EAClB,OAAkB,EAClB,QAAoB;QAEpB,IAAI,IAAI,CAAC,gBAAgB,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAChF,OAAO,IAAI,CACT,oCAAoC,UAAU,+BAA+B,IAAI,CAAC,gBAAgB,GAAG,CACtG,CAAC;QACJ,CAAC;QAED,OAAO,mBAAmB,CAAC;YACzB,OAAO;YACP,UAAU;YACV,GAAG,EAAE,IAAI,CAAC,IAAI;YACd,cAAc,EAAE,IAAI,CAAC,eAAe;YACpC,QAAQ;SACT,CAAC,CAAC;IACL,CAAC;CACF","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { JsonValue } from '@fgv/ts-json-base';\nimport { Result, fail, succeed } from '@fgv/ts-utils';\nimport { createEncryptedFile } from './encryptedFile';\nimport { ICryptoProvider, IEncryptedFile, IEncryptionProvider } from './model';\n\n/**\n * Parameters for creating a {@link DirectEncryptionProvider}.\n * @public\n */\nexport interface IDirectEncryptionProviderParams {\n /**\n * The crypto provider to use for encryption operations.\n */\n readonly cryptoProvider: ICryptoProvider;\n\n /**\n * The encryption key (32 bytes for AES-256).\n */\n readonly key: Uint8Array;\n\n /**\n * Optional bound secret name.\n * When set, `encryptByName` will fail if called with a different name.\n * When unset, any secret name is accepted.\n */\n readonly boundSecretName?: string;\n}\n\n/**\n * An {@link IEncryptionProvider} that uses a pre-supplied key and crypto provider.\n *\n * This is useful when you have the raw encryption key from an external source\n * (e.g. a `SecretProvider` callback, password derivation, or a one-shot\n * operation) and don't want to open a full KeyStore.\n *\n * Optionally bound to a specific secret name for safety: if a `boundSecretName`\n * is provided, calls to `encryptByName` with a different name will fail.\n *\n * @example\n * ```typescript\n * const provider = DirectEncryptionProvider.create({\n * cryptoProvider: nodeCryptoProvider,\n * key: myKey,\n * boundSecretName: 'my-collection'\n * }).orThrow();\n *\n * const encrypted = await provider.encryptByName('my-collection', jsonContent);\n * ```\n *\n * @public\n */\nexport class DirectEncryptionProvider implements IEncryptionProvider {\n private readonly _cryptoProvider: ICryptoProvider;\n private readonly _key: Uint8Array;\n private readonly _boundSecretName: string | undefined;\n\n private constructor(params: IDirectEncryptionProviderParams) {\n this._cryptoProvider = params.cryptoProvider;\n this._key = params.key;\n this._boundSecretName = params.boundSecretName;\n }\n\n /**\n * Creates a new DirectEncryptionProvider.\n * @param params - Provider configuration\n * @returns Success with provider, or Failure if parameters are invalid\n * @public\n */\n public static create(params: IDirectEncryptionProviderParams): Result<DirectEncryptionProvider> {\n if (params.key.length === 0) {\n return fail('Encryption key cannot be empty');\n }\n return succeed(new DirectEncryptionProvider(params));\n }\n\n /**\n * The secret name this provider is bound to, if any.\n * @public\n */\n public get boundSecretName(): string | undefined {\n return this._boundSecretName;\n }\n\n /**\n * {@inheritDoc IEncryptionProvider.encryptByName}\n */\n public async encryptByName<TMetadata = JsonValue>(\n secretName: string,\n content: JsonValue,\n metadata?: TMetadata\n ): Promise<Result<IEncryptedFile<TMetadata>>> {\n if (this._boundSecretName !== undefined && secretName !== this._boundSecretName) {\n return fail(\n `Secret name mismatch: requested '${secretName}' but provider is bound to '${this._boundSecretName}'`\n );\n }\n\n return createEncryptedFile({\n content,\n secretName,\n key: this._key,\n cryptoProvider: this._cryptoProvider,\n metadata\n });\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryptedFile.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/encryptedFile.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2024 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { JsonValue } from '@fgv/ts-json-base';\nimport { captureResult, Converter, fail, Result, succeed } from '@fgv/ts-utils';\nimport * as Constants from './constants';\nimport { createEncryptedFileConverter } from './converters';\nimport { ICryptoProvider, IEncryptedFile, IKeyDerivationParams, isEncryptedFile } from './model';\n\n// ============================================================================\n// Base64 Utilities\n// ============================================================================\n\n/**\n * Encodes a `Uint8Array` to a base64 string.\n * @param bytes - Bytes to encode\n * @returns Base64 string\n * @public\n */\n/* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */\nexport function toBase64(bytes: Uint8Array): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(bytes).toString('base64');\n }\n // Browser fallback\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n/* c8 ignore stop */\n\n/**\n * Decodes a base64 string to a `Uint8Array`.\n * @param base64 - Base64 string to decode\n * @returns Decoded bytes\n * @public\n */\n/* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */\nexport function fromBase64(base64: string): Uint8Array {\n if (typeof Buffer !== 'undefined') {\n return new Uint8Array(Buffer.from(base64, 'base64'));\n }\n // Browser fallback\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n/* c8 ignore stop */\n\n// ============================================================================\n// Create Encrypted File Parameters\n// ============================================================================\n\n/**\n * Parameters for creating an {@link CryptoUtils.IEncryptedFile | encrypted file}.\n * @typeParam TMetadata - Type of optional unencrypted metadata\n * @public\n */\nexport interface ICreateEncryptedFileParams<TMetadata = JsonValue> {\n /**\n * The JSON content to encrypt.\n */\n readonly content: JsonValue;\n\n /**\n * Name of the secret used for encryption.\n */\n readonly secretName: string;\n\n /**\n * The encryption key (32 bytes for AES-256).\n */\n readonly key: Uint8Array;\n\n /**\n * {@link CryptoUtils.ICryptoProvider | Crypto provider} to use for encryption.\n */\n readonly cryptoProvider: ICryptoProvider;\n\n /**\n * Optional metadata to include unencrypted.\n */\n readonly metadata?: TMetadata;\n\n /**\n * Optional converter to validate metadata before including.\n * If provided, metadata will be validated before encryption.\n */\n readonly metadataConverter?: Converter<TMetadata>;\n\n /**\n * Optional {@link CryptoUtils.IKeyDerivationParams | key derivation parameters}.\n * If provided, stores the salt and iterations used to derive the key from a password.\n * This allows decryption using only a password (the salt/iterations are read from the file).\n */\n readonly keyDerivation?: IKeyDerivationParams;\n}\n\n// ============================================================================\n// Encryption Functions\n// ============================================================================\n\n/**\n * Creates an {@link CryptoUtils.IEncryptedFile | encrypted file} from JSON content.\n * @typeParam TMetadata - Type of optional unencrypted metadata\n * @param params - Encryption parameters\n * @returns `Success` with encrypted file structure, or `Failure` with an error.\n * @public\n */\nexport async function createEncryptedFile<TMetadata = JsonValue>(\n params: ICreateEncryptedFileParams<TMetadata>\n): Promise<Result<IEncryptedFile<TMetadata>>> {\n const { content, secretName, key, metadata, metadataConverter, keyDerivation, cryptoProvider } = params;\n\n // Validate metadata if converter provided\n /* c8 ignore next 6 - metadata validation path exercised via higher-level tests */\n if (metadata !== undefined && metadataConverter !== undefined) {\n const metadataResult = metadataConverter.convert(metadata);\n if (metadataResult.isFailure()) {\n return fail(`Invalid metadata: ${metadataResult.message}`);\n }\n }\n\n // Serialize content to JSON string\n const jsonResult = captureResult(() => JSON.stringify(content));\n if (jsonResult.isFailure()) {\n return fail(`Failed to serialize content: ${jsonResult.message}`);\n }\n\n // Encrypt the JSON string\n const encryptResult = await cryptoProvider.encrypt(jsonResult.value, key);\n if (encryptResult.isFailure()) {\n return fail(`Encryption failed: ${encryptResult.message}`);\n }\n\n const { iv, authTag, encryptedData } = encryptResult.value;\n\n // Build the encrypted file structure\n const encryptedFile: IEncryptedFile<TMetadata> = {\n format: Constants.ENCRYPTED_FILE_FORMAT,\n secretName,\n algorithm: Constants.DEFAULT_ALGORITHM,\n iv: toBase64(iv),\n authTag: toBase64(authTag),\n encryptedData: toBase64(encryptedData),\n ...(metadata !== undefined ? { metadata } : {}),\n ...(keyDerivation !== undefined ? { keyDerivation } : {})\n };\n\n return succeed(encryptedFile);\n}\n\n// ============================================================================\n// Decryption Functions\n// ============================================================================\n\n/**\n * Decrypts an {@link CryptoUtils.IEncryptedFile | encrypted file} and returns the JSON content.\n * @typeParam TPayload - Expected type of decrypted content\n * @param file - The encrypted file structure\n * @param key - The decryption key (32 bytes for AES-256)\n * @param cryptoProvider - {@link CryptoUtils.ICryptoProvider | Crypto provider} to use for decryption\n * @param payloadConverter - Optional converter to validate and convert decrypted content\n * @returns `Success` with decrypted content, or `Failure` with an error.\n * @public\n */\nexport async function decryptFile<TPayload extends JsonValue = JsonValue>(\n file: IEncryptedFile<unknown>,\n key: Uint8Array,\n cryptoProvider: ICryptoProvider,\n payloadConverter?: Converter<TPayload>\n): Promise<Result<TPayload>> {\n // Decode base64 values\n const iv = fromBase64(file.iv);\n const authTag = fromBase64(file.authTag);\n const encryptedData = fromBase64(file.encryptedData);\n\n // Decrypt\n const decryptResult = await cryptoProvider.decrypt(encryptedData, key, iv, authTag);\n if (decryptResult.isFailure()) {\n return fail(decryptResult.message);\n }\n\n // Parse JSON\n const parseResult = captureResult(() => JSON.parse(decryptResult.value) as TPayload).withErrorFormat(\n (e) => `Failed to parse decrypted content as JSON: ${e}`\n );\n\n /* c8 ignore next 3 - JSON parse failure only occurs with corrupted encrypted data */\n if (parseResult.isFailure()) {\n return parseResult;\n }\n\n // Validate with converter if provided\n /* c8 ignore next 3 - payload converter path exercised via higher-level tests */\n if (payloadConverter !== undefined) {\n return payloadConverter.convert(parseResult.value);\n }\n\n return parseResult;\n}\n\n/**\n * Attempts to parse and decrypt a JSON object as an {@link CryptoUtils.IEncryptedFile | encrypted file}.\n * @typeParam TPayload - Expected type of decrypted content\n * @typeParam TMetadata - Type of optional unencrypted metadata\n * @param json - JSON object that may be an encrypted file\n * @param key - The decryption key (32 bytes for AES-256)\n * @param cryptoProvider - {@link CryptoUtils.ICryptoProvider | Crypto provider} to use for decryption\n * @param payloadConverter - Optional converter to validate and convert decrypted content\n * @param metadataConverter - Optional converter to validate metadata before decryption\n * @returns `Success` with decrypted content, or `Failure` with an error (including if not encrypted)\n * @public\n */\nexport async function tryDecryptFile<TPayload extends JsonValue = JsonValue, TMetadata = JsonValue>(\n json: JsonValue,\n key: Uint8Array,\n cryptoProvider: ICryptoProvider,\n payloadConverter?: Converter<TPayload>,\n metadataConverter?: Converter<TMetadata>\n): Promise<Result<TPayload>> {\n // Check if it's an encrypted file\n if (!isEncryptedFile(json)) {\n return fail('Not an encrypted file');\n }\n\n // Validate and convert to typed encrypted file\n const fileConverter = createEncryptedFileConverter(metadataConverter);\n const fileResult = fileConverter.convert(json);\n if (fileResult.isFailure()) {\n return fail(`Invalid encrypted file format: ${fileResult.message}`);\n }\n\n return decryptFile(fileResult.value, key, cryptoProvider, payloadConverter);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.browser.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/index.browser.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;AAEZ;;;;GAIG;AAEH,iCAAiC;AACjC,cAAc,SAAS,CAAC;AAExB,YAAY;AACZ,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,EACZ,MAAM,aAAa,CAAC;AAErB,qBAAqB;AACrB,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,CAAC;AAEpB,uBAAuB;AACvB,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,6BAA6B;AAC7B,OAAO,EAAE,wBAAwB,EAAmC,MAAM,4BAA4B,CAAC;AAEvG,8DAA8D;AAC9D,OAAO,EAA2B,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAE3F,8DAA8D;AAC9D,4DAA4D;AAE5D,yBAAyB;AACzB,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,UAAU,EAEV,QAAQ,EACR,cAAc,EACf,MAAM,iBAAiB,CAAC","sourcesContent":["// Copyright (c) 2024 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Crypto utilities for encrypted file handling and key management (browser version).\n * Note: For browser crypto provider, use \\@fgv/ts-web-extras.\n * @packageDocumentation\n */\n\n// Re-export all types from model\nexport * from './model';\n\n// Constants\nexport {\n AES_256_KEY_SIZE,\n DEFAULT_ALGORITHM,\n ENCRYPTED_FILE_FORMAT,\n GCM_AUTH_TAG_SIZE,\n GCM_IV_SIZE\n} from './constants';\n\n// KeyStore namespace\nimport * as KeyStore from './keystore';\nexport { KeyStore };\n\n// Converters namespace\nimport * as Converters from './converters';\nexport { Converters };\n\n// Direct encryption provider\nexport { DirectEncryptionProvider, IDirectEncryptionProviderParams } from './directEncryptionProvider';\n\n// WebCrypto parameter table for asymmetric keypair algorithms\nexport { IKeyPairAlgorithmParams, keyPairAlgorithmParams } from './keyPairAlgorithmParams';\n\n// Note: NodeCryptoProvider is NOT exported in browser version\n// Use BrowserCryptoProvider from @fgv/ts-web-extras instead\n\n// Encrypted file helpers\nexport {\n createEncryptedFile,\n decryptFile,\n fromBase64,\n ICreateEncryptedFileParams,\n toBase64,\n tryDecryptFile\n} from './encryptedFile';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;AAEZ;;;GAGG;AAEH,iCAAiC;AACjC,cAAc,SAAS,CAAC;AAExB,YAAY;AACZ,OAAO,KAAK,SAAS,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,qBAAqB;AACrB,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,CAAC;AAEpB,uBAAuB;AACvB,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,6BAA6B;AAC7B,OAAO,EAAE,wBAAwB,EAAmC,MAAM,4BAA4B,CAAC;AAEvG,8DAA8D;AAC9D,OAAO,EAA2B,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAE3F,qDAAqD;AACrD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE9E,yBAAyB;AACzB,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,UAAU,EAEV,QAAQ,EACR,cAAc,EACf,MAAM,iBAAiB,CAAC","sourcesContent":["// Copyright (c) 2024 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Crypto utilities for encrypted file handling and key management.\n * @packageDocumentation\n */\n\n// Re-export all types from model\nexport * from './model';\n\n// Constants\nimport * as Constants from './constants';\nexport { Constants };\n\n// KeyStore namespace\nimport * as KeyStore from './keystore';\nexport { KeyStore };\n\n// Converters namespace\nimport * as Converters from './converters';\nexport { Converters };\n\n// Direct encryption provider\nexport { DirectEncryptionProvider, IDirectEncryptionProviderParams } from './directEncryptionProvider';\n\n// WebCrypto parameter table for asymmetric keypair algorithms\nexport { IKeyPairAlgorithmParams, keyPairAlgorithmParams } from './keyPairAlgorithmParams';\n\n// Node.js crypto provider (Node.js environment only)\nexport { NodeCryptoProvider, nodeCryptoProvider } from './nodeCryptoProvider';\n\n// Encrypted file helpers\nexport {\n createEncryptedFile,\n decryptFile,\n fromBase64,\n ICreateEncryptedFileParams,\n toBase64,\n tryDecryptFile\n} from './encryptedFile';\n"]}
|
|
@@ -42,6 +42,16 @@ export const keyPairAlgorithmParams = {
|
|
|
42
42
|
importPublicKey: { name: 'RSA-OAEP', hash: 'SHA-256' },
|
|
43
43
|
keyPairUsages: ['encrypt', 'decrypt'],
|
|
44
44
|
publicKeyUsages: ['encrypt']
|
|
45
|
+
},
|
|
46
|
+
'ecdh-p256': {
|
|
47
|
+
generateKey: { name: 'ECDH', namedCurve: 'P-256' },
|
|
48
|
+
importPublicKey: { name: 'ECDH', namedCurve: 'P-256' },
|
|
49
|
+
// WebCrypto filters per-role: the private key takes both derive usages,
|
|
50
|
+
// and the public key gets [] since an ECDH public key alone cannot derive.
|
|
51
|
+
keyPairUsages: ['deriveKey', 'deriveBits'],
|
|
52
|
+
// Importing only the recipient's public key — empty usages because a
|
|
53
|
+
// standalone ECDH public key has no derivation capability.
|
|
54
|
+
publicKeyUsages: []
|
|
45
55
|
}
|
|
46
56
|
};
|
|
47
57
|
//# sourceMappingURL=keyPairAlgorithmParams.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyPairAlgorithmParams.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/keyPairAlgorithmParams.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;AAoCZ;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAgE;IACjG,YAAY,EAAE;QACZ,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE;QACnD,eAAe,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE;QACvD,aAAa,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;QACjC,eAAe,EAAE,CAAC,QAAQ,CAAC;KAC5B;IACD,eAAe,EAAE;QACf,WAAW,EAAE;YACX,IAAI,EAAE,UAAU;YAChB,aAAa,EAAE,IAAI;YACnB,cAAc,EAAE,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;YAClD,IAAI,EAAE,SAAS;SAChB;QACD,eAAe,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE;QACtD,aAAa,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC;QACrC,eAAe,EAAE,CAAC,SAAS,CAAC;KAC7B;IACD,WAAW,EAAE;QACX,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE;QAClD,eAAe,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE;QACtD,wEAAwE;QACxE,2EAA2E;QAC3E,aAAa,EAAE,CAAC,WAAW,EAAE,YAAY,CAAC;QAC1C,qEAAqE;QACrE,2DAA2D;QAC3D,eAAe,EAAE,EAAE;KACpB;CACF,CAAC","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { KeyPairAlgorithm } from './model';\n\n/**\n * WebCrypto parameters for a single {@link CryptoUtils.KeyPairAlgorithm}.\n * Implementations of {@link CryptoUtils.ICryptoProvider} use this table to\n * translate the small public algorithm enum into the WebCrypto algorithm\n * objects and key-usage arrays expected by `crypto.subtle`.\n * @public\n */\nexport interface IKeyPairAlgorithmParams {\n /**\n * Algorithm parameters for `crypto.subtle.generateKey`. Always an asymmetric\n * variant — these algorithms produce a `CryptoKeyPair`, not a single key.\n */\n readonly generateKey: RsaHashedKeyGenParams | EcKeyGenParams;\n\n /**\n * Algorithm parameters for `crypto.subtle.importKey('jwk', ...)` when\n * importing the public half of a keypair.\n */\n readonly importPublicKey: RsaHashedImportParams | EcKeyImportParams;\n\n /**\n * Default key usages for the generated `CryptoKeyPair`. Both halves receive\n * the usages WebCrypto considers valid for their role; the platform filters.\n */\n readonly keyPairUsages: ReadonlyArray<KeyUsage>;\n\n /**\n * Key usages applied when re-importing only the public key.\n */\n readonly publicKeyUsages: ReadonlyArray<KeyUsage>;\n}\n\n/**\n * Lookup table from {@link CryptoUtils.KeyPairAlgorithm} to the WebCrypto\n * parameters needed to drive `crypto.subtle`. Shared between every\n * {@link CryptoUtils.ICryptoProvider} implementation since both Node and\n * browser providers speak the same WebCrypto API. Exposed for downstream\n * provider implementations (e.g. browser-side providers in `@fgv/ts-web-extras`).\n * @public\n */\nexport const keyPairAlgorithmParams: Readonly<Record<KeyPairAlgorithm, IKeyPairAlgorithmParams>> = {\n 'ecdsa-p256': {\n generateKey: { name: 'ECDSA', namedCurve: 'P-256' },\n importPublicKey: { name: 'ECDSA', namedCurve: 'P-256' },\n keyPairUsages: ['sign', 'verify'],\n publicKeyUsages: ['verify']\n },\n 'rsa-oaep-2048': {\n generateKey: {\n name: 'RSA-OAEP',\n modulusLength: 2048,\n publicExponent: new Uint8Array([0x01, 0x00, 0x01]),\n hash: 'SHA-256'\n },\n importPublicKey: { name: 'RSA-OAEP', hash: 'SHA-256' },\n keyPairUsages: ['encrypt', 'decrypt'],\n publicKeyUsages: ['encrypt']\n },\n 'ecdh-p256': {\n generateKey: { name: 'ECDH', namedCurve: 'P-256' },\n importPublicKey: { name: 'ECDH', namedCurve: 'P-256' },\n // WebCrypto filters per-role: the private key takes both derive usages,\n // and the public key gets [] since an ECDH public key alone cannot derive.\n keyPairUsages: ['deriveKey', 'deriveBits'],\n // Importing only the recipient's public key — empty usages because a\n // standalone ECDH public key has no derivation capability.\n publicKeyUsages: []\n }\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"converters.js","sourceRoot":"","sources":["../../../../src/packlets/crypto-utils/keystore/converters.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { Converter, Converters, succeed, Validation, Validator, Validators } from '@fgv/ts-utils';\nimport { base64String, encryptionAlgorithm, keyDerivationParams } from '../converters';\nimport {\n allKeyPairAlgorithms,\n allKeyStoreSecretTypes,\n allKeyStoreSymmetricSecretTypes,\n IKeyStoreAsymmetricEntryJson,\n IKeyStoreEntryJson,\n IKeyStoreFile,\n IKeyStoreSymmetricEntryJson,\n IKeyStoreVaultContents,\n KEYSTORE_FORMAT,\n KeyPairAlgorithm,\n KeyStoreAsymmetricSecretType,\n KeyStoreFormat,\n KeyStoreSecretType,\n KeyStoreSymmetricSecretType,\n allKeyStoreAsymmetricSecretTypes\n} from './model';\n\n// ============================================================================\n// Key Store Format Converter\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.KeyStore.KeyStoreFormat | key store format} version.\n * @public\n */\nexport const keystoreFormat: Converter<KeyStoreFormat> = Converters.enumeratedValue<KeyStoreFormat>([\n KEYSTORE_FORMAT\n]);\n\n// ============================================================================\n// Secret Type Converters\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.KeyStore.KeyStoreSecretType | any key store secret type} discriminator.\n * Accepts both symmetric and asymmetric type values.\n * @public\n */\nexport const keystoreSecretType: Converter<KeyStoreSecretType> =\n Converters.enumeratedValue<KeyStoreSecretType>(allKeyStoreSecretTypes);\n\n/**\n * Converter for {@link CryptoUtils.KeyStore.KeyStoreSymmetricSecretType | symmetric secret type} discriminator.\n * Accepts only `'encryption-key'` and `'api-key'`.\n * @public\n */\nexport const keystoreSymmetricSecretType: Converter<KeyStoreSymmetricSecretType> =\n Converters.enumeratedValue<KeyStoreSymmetricSecretType>(allKeyStoreSymmetricSecretTypes);\n\n/**\n * Converter for {@link CryptoUtils.KeyStore.KeyStoreAsymmetricSecretType | asymmetric secret type} discriminator.\n * Accepts only `'asymmetric-keypair'`.\n * @public\n */\nexport const keystoreAsymmetricSecretType: Converter<KeyStoreAsymmetricSecretType> =\n Converters.enumeratedValue<KeyStoreAsymmetricSecretType>(allKeyStoreAsymmetricSecretTypes);\n\n// ============================================================================\n// Key Pair Algorithm Converter\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.KeyStore.KeyPairAlgorithm | key pair algorithm}.\n * @public\n */\nexport const keyPairAlgorithm: Converter<KeyPairAlgorithm> =\n Converters.enumeratedValue<KeyPairAlgorithm>(allKeyPairAlgorithms);\n\n// ============================================================================\n// JWK Shape Validator\n// ============================================================================\n\n/**\n * In-place shape check for a JSON Web Key. Asserts only that the input is a\n * non-array object whose `kty` discriminator is a string; every other JWK\n * field passes through untouched. This is intentionally **not** a true JWK\n * validator — per-algorithm correctness (RSA `n`/`e`, EC `crv`/`x`/`y`,\n * key-size constraints, etc.) is delegated to `crypto.subtle.importKey` at\n * first use, which is the authoritative checker. The \"shape\" suffix in the\n * name is the warning sign for readers expecting full validation.\n * @remarks\n * Built with `Validators.object` (in-place, non-strict) so unknown JWK fields\n * survive the round-trip; the cast to `FieldValidators<JsonWebKey>` is required\n * only because TypeScript's mapped type demands an entry for every key in\n * `JsonWebKey`. At runtime the `ObjectValidator` only inspects keys present in\n * the field-validators map.\n * @public\n */\nexport const jsonWebKeyShape: Validator<JsonWebKey> = Validators.object<JsonWebKey>({\n kty: Validators.string\n} as Validation.Classes.FieldValidators<JsonWebKey>);\n\n// ============================================================================\n// Symmetric Secret Entry Converter\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntryJson | symmetric secret entry} in JSON form.\n *\n * @remarks\n * Backwards compatibility with vaults written before asymmetric-keypair\n * support: those entries may lack the `type` discriminator on the wire. To\n * keep the model type honest (`type` is required on\n * {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntryJson}, see its docs),\n * we declare `type` in `optionalFields` so the inner `Converters.object` will\n * accept input without it, then `.map()` injects the default\n * `'encryption-key'` when missing. The output therefore always carries the\n * discriminator and downstream code never sees the legacy missing-type form.\n *\n * @public\n */\nexport const keystoreSymmetricEntryJson: Converter<IKeyStoreSymmetricEntryJson> =\n Converters.object<IKeyStoreSymmetricEntryJson>(\n {\n name: Converters.string,\n type: keystoreSymmetricSecretType,\n key: base64String,\n description: Converters.string,\n createdAt: Converters.string\n },\n {\n // `type` is optional at the input layer for legacy-vault compatibility;\n // the .map() below normalizes by injecting the default.\n optionalFields: ['type', 'description']\n }\n ).map((entry) =>\n succeed<IKeyStoreSymmetricEntryJson>({\n ...entry,\n type: entry.type ?? 'encryption-key'\n })\n );\n\n// ============================================================================\n// Asymmetric Keypair Entry Converter\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.KeyStore.IKeyStoreAsymmetricEntryJson | asymmetric keypair entry} in JSON form.\n * The `publicKeyJwk` field passes through {@link CryptoUtils.KeyStore.Converters.jsonWebKeyShape | jsonWebKeyShape}\n * (shape check only — see its docs); cryptographic correctness is enforced by\n * `crypto.subtle.importKey` at use.\n * @public\n */\nexport const keystoreAsymmetricEntryJson: Converter<IKeyStoreAsymmetricEntryJson> =\n Converters.object<IKeyStoreAsymmetricEntryJson>({\n name: Converters.string,\n type: keystoreAsymmetricSecretType,\n id: Converters.string,\n algorithm: keyPairAlgorithm,\n publicKeyJwk: jsonWebKeyShape,\n description: Converters.string.optional(),\n createdAt: Converters.string\n });\n\n// ============================================================================\n// Discriminated-Union Entry Converter\n// ============================================================================\n\n/**\n * Discriminated-union converter for any {@link CryptoUtils.KeyStore.IKeyStoreEntryJson | key store entry} in JSON form.\n * Routes by the `type` field: `'asymmetric-keypair'` is parsed by\n * {@link CryptoUtils.KeyStore.Converters.keystoreAsymmetricEntryJson | keystoreAsymmetricEntryJson},\n * anything else (including a missing `type` field for backwards compatibility) by\n * {@link CryptoUtils.KeyStore.Converters.keystoreSymmetricEntryJson | keystoreSymmetricEntryJson}.\n * @public\n */\nexport const keystoreSecretEntryJson: Converter<IKeyStoreEntryJson> = Converters.oneOf<IKeyStoreEntryJson>([\n keystoreAsymmetricEntryJson,\n keystoreSymmetricEntryJson\n]);\n\n// ============================================================================\n// Vault Contents Converter\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.KeyStore.IKeyStoreVaultContents | key store vault contents} (decrypted state).\n * @public\n */\nexport const keystoreVaultContents: Converter<IKeyStoreVaultContents> =\n Converters.object<IKeyStoreVaultContents>({\n version: keystoreFormat,\n secrets: Converters.recordOf(keystoreSecretEntryJson)\n });\n\n// ============================================================================\n// Key Store File Converter\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.KeyStore.IKeyStoreFile | encrypted key store file}.\n * @public\n */\nexport const keystoreFile: Converter<IKeyStoreFile> = Converters.object<IKeyStoreFile>({\n format: keystoreFormat,\n algorithm: encryptionAlgorithm,\n iv: base64String,\n authTag: base64String,\n encryptedData: base64String,\n keyDerivation: keyDerivationParams\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/packlets/crypto-utils/keystore/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;AAEZ;;;GAGG;AAEH,uBAAuB;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,qBAAqB,CAAC;AAEpC,uBAAuB;AACvB,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,kBAAkB;AAClB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Key store module for password-protected secret management.\n * @packageDocumentation\n */\n\n// Types and interfaces\nexport * from './model';\nexport * from './privateKeyStorage';\n\n// Converters namespace\nimport * as Converters from './converters';\nexport { Converters };\n\n// Key store class\nexport { KeyStore } from './keyStore';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyStore.js","sourceRoot":"","sources":["../../../../src/packlets/crypto-utils/keystore/keyStore.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { JsonValue } from '@fgv/ts-json-base';\nimport { captureResult, fail, Result, succeed } from '@fgv/ts-utils';\nimport * as Constants from '../constants';\nimport { createEncryptedFile } from '../encryptedFile';\nimport {\n ICryptoProvider,\n IEncryptedFile,\n IEncryptionConfig,\n IEncryptionProvider,\n SecretProvider\n} from '../model';\nimport {\n DEFAULT_KEYSTORE_ITERATIONS,\n DEFAULT_SECRET_ITERATIONS,\n IAddKeyPairOptions,\n IAddKeyPairResult,\n IAddSecretFromPasswordOptions,\n IAddSecretFromPasswordResult,\n IAddSecretOptions,\n IAddSecretResult,\n IImportKeyOptions,\n IImportSecretOptions,\n IKeyStoreAsymmetricEntry,\n IKeyStoreCreateParams,\n IKeyStoreEntry,\n IKeyStoreEntryJson,\n IKeyStoreFile,\n IKeyStoreOpenParams,\n IKeyStoreSymmetricEntry,\n IKeyStoreVaultContents,\n IRemoveSecretResult,\n KEYSTORE_FORMAT,\n KeyStoreLockState,\n KeyStoreSecretType,\n MIN_SALT_LENGTH\n} from './model';\nimport { IPrivateKeyStorage } from './privateKeyStorage';\nimport { keystoreFile, keystoreVaultContents } from './converters';\n\n/**\n * Gets the current ISO timestamp.\n */\nfunction getCurrentTimestamp(): string {\n return new Date().toISOString();\n}\n\n// ============================================================================\n// KeyStore Class\n// ============================================================================\n\n/**\n * Password-protected key store for managing encryption secrets.\n *\n * The KeyStore provides a secure vault for storing named encryption keys.\n * The vault is encrypted at rest using a master password via PBKDF2 key derivation.\n *\n * @example\n * ```typescript\n * // Create new key store\n * const keystore = KeyStore.create({ cryptoProvider: nodeCryptoProvider }).orThrow();\n * await keystore.initialize('master-password');\n *\n * // Add secrets\n * await keystore.addSecret('my-key', { description: 'Production key' });\n *\n * // Save to file\n * const fileContent = await keystore.save();\n *\n * // Later: Open existing key store\n * const keystore2 = KeyStore.open({\n * cryptoProvider: nodeCryptoProvider,\n * keystoreFile: fileContent.value\n * }).orThrow();\n * await keystore2.unlock('master-password');\n *\n * // Use as secret provider for encrypted file loading\n * const encryptionConfig = keystore2.getEncryptionConfig().orThrow();\n * ```\n *\n * @public\n */\nexport class KeyStore implements IEncryptionProvider {\n private readonly _cryptoProvider: ICryptoProvider;\n private readonly _privateKeyStorage: IPrivateKeyStorage | undefined;\n private readonly _iterations: number;\n private _keystoreFile: IKeyStoreFile | undefined;\n private _salt: Uint8Array | undefined;\n private _secrets: Map<string, IKeyStoreEntry> | undefined;\n private _state: KeyStoreLockState;\n private _dirty: boolean;\n private _isNew: boolean;\n\n private constructor(\n cryptoProvider: ICryptoProvider,\n iterations: number,\n keystoreFile: IKeyStoreFile | undefined,\n isNew: boolean,\n privateKeyStorage: IPrivateKeyStorage | undefined\n ) {\n this._cryptoProvider = cryptoProvider;\n this._privateKeyStorage = privateKeyStorage;\n this._iterations = iterations;\n this._keystoreFile = keystoreFile;\n this._state = 'locked';\n this._dirty = false;\n this._isNew = isNew;\n }\n\n // ============================================================================\n // Factory Methods\n // ============================================================================\n\n /**\n * Creates a new, empty key store.\n * Call `initialize(password)` to set the master password.\n * @param params - Creation parameters\n * @returns Success with new KeyStore instance, or Failure if parameters invalid\n * @public\n */\n public static create(params: IKeyStoreCreateParams): Result<KeyStore> {\n const iterations = params.iterations ?? DEFAULT_KEYSTORE_ITERATIONS;\n if (iterations < 1) {\n return fail('Iterations must be at least 1');\n }\n return succeed(\n new KeyStore(params.cryptoProvider, iterations, undefined, true, params.privateKeyStorage)\n );\n }\n\n /**\n * Opens an existing encrypted key store.\n * Call `unlock(password)` to decrypt and access secrets.\n * @param params - Open parameters including the encrypted file\n * @returns Success with KeyStore instance, or Failure if file format invalid\n * @public\n */\n public static open(params: IKeyStoreOpenParams): Result<KeyStore> {\n // Validate the file format\n const fileResult = keystoreFile.convert(params.keystoreFile);\n if (fileResult.isFailure()) {\n return fail(`Invalid key store file: ${fileResult.message}`);\n }\n\n const iterations = fileResult.value.keyDerivation.iterations;\n return succeed(\n new KeyStore(params.cryptoProvider, iterations, fileResult.value, false, params.privateKeyStorage)\n );\n }\n\n // ============================================================================\n // Lifecycle Methods\n // ============================================================================\n\n /**\n * Initializes a new key store with the master password.\n * Generates a random salt for key derivation.\n * Only valid for newly created (not opened) key stores.\n * @param password - The master password\n * @returns Success with this instance when initialized, Failure if already initialized or opened\n * @public\n */\n public async initialize(password: string): Promise<Result<KeyStore>> {\n if (!this._isNew) {\n return fail('Cannot initialize an opened key store - use unlock() instead');\n }\n if (this._state === 'unlocked') {\n return fail('Key store is already initialized');\n }\n if (!password || password.length === 0) {\n return fail('Password cannot be empty');\n }\n\n // Generate salt for this key store using crypto provider\n const saltResult = this._cryptoProvider.generateRandomBytes(MIN_SALT_LENGTH);\n /* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */\n if (saltResult.isFailure()) {\n return fail(`Failed to generate salt: ${saltResult.message}`);\n }\n\n this._salt = saltResult.value;\n this._secrets = new Map();\n this._state = 'unlocked';\n this._dirty = true; // New store needs to be saved\n\n return succeed(this);\n }\n\n /**\n * Unlocks an existing key store with the master password.\n * Decrypts the vault and loads secrets into memory.\n * @param password - The master password\n * @returns Success with this instance when unlocked, Failure if password incorrect\n * @public\n */\n public async unlock(password: string): Promise<Result<KeyStore>> {\n if (this._isNew) {\n return fail('Cannot unlock a new key store - use initialize() instead');\n }\n /* c8 ignore next 6 - error paths tested but coverage intermittently missed */\n if (this._state === 'unlocked') {\n return fail('Key store is already unlocked');\n }\n if (!this._keystoreFile) {\n return fail('No key store file to unlock');\n }\n\n const keyDerivation = this._keystoreFile.keyDerivation;\n const saltResult = this._cryptoProvider.fromBase64(keyDerivation.salt);\n /* c8 ignore next 3 - error path tested but coverage intermittently missed */\n if (saltResult.isFailure()) {\n return fail(`Invalid salt in key store file: ${saltResult.message}`);\n }\n const salt = saltResult.value;\n\n // Derive the key from password\n const keyResult = await this._cryptoProvider.deriveKey(password, salt, keyDerivation.iterations);\n /* c8 ignore next 3 - error path tested but coverage intermittently missed */\n if (keyResult.isFailure()) {\n return fail(`Key derivation failed: ${keyResult.message}`);\n }\n\n return this._decryptVault(keyResult.value);\n }\n\n /**\n * Unlocks an existing key store with a pre-derived key, bypassing\n * PBKDF2 key derivation. Use this when the derived key has been\n * stored externally (e.g., in another key store) and the original\n * password is no longer available.\n *\n * The supplied key must have been derived from the correct password\n * using the key store file's own PBKDF2 parameters (salt and\n * iteration count).\n *\n * @param derivedKey - The pre-derived master key (32 bytes for AES-256)\n * @returns Success with this instance when unlocked, Failure if key is incorrect\n * @public\n */\n public async unlockWithKey(derivedKey: Uint8Array): Promise<Result<KeyStore>> {\n if (this._isNew) {\n return fail('Cannot unlock a new key store - use initialize() instead');\n }\n if (this._state === 'unlocked') {\n return fail('Key store is already unlocked');\n }\n if (derivedKey.length !== Constants.AES_256_KEY_SIZE) {\n return fail(`Key must be ${Constants.AES_256_KEY_SIZE} bytes, got ${derivedKey.length}`);\n }\n /* c8 ignore next 3 - defensive coding: unreachable via public API (open sets file, create sets isNew) */\n if (!this._keystoreFile) {\n return fail('No key store file to unlock');\n }\n\n return this._decryptVault(derivedKey);\n }\n\n /**\n * Locks the key store, clearing all secrets from memory.\n * @param force - If true, discards unsaved changes\n * @returns Success when locked, Failure if unsaved changes and !force\n * @public\n */\n public lock(force?: boolean): Result<KeyStore> {\n if (this._state === 'locked') {\n return succeed(this);\n }\n\n if (this._dirty && !force) {\n return fail('Unsaved changes - use force=true to discard or save() first');\n }\n\n // Clear secrets from memory (overwrite for security)\n if (this._secrets) {\n for (const entry of this._secrets.values()) {\n if (entry.type !== 'asymmetric-keypair') {\n entry.key.fill(0);\n }\n }\n this._secrets.clear();\n this._secrets = undefined;\n }\n\n this._state = 'locked';\n this._dirty = false;\n\n return succeed(this);\n }\n\n /**\n * Checks if the key store is unlocked.\n * @public\n */\n public get isUnlocked(): boolean {\n return this._state === 'unlocked';\n }\n\n /**\n * Checks if there are unsaved changes.\n * @public\n */\n public get isDirty(): boolean {\n return this._dirty;\n }\n\n /**\n * Whether this is a newly created key store (not opened from a file).\n * A new key store must be initialized with a password before use.\n * An opened key store must be unlocked with the existing password.\n * @public\n */\n public get isNew(): boolean {\n return this._isNew;\n }\n\n /**\n * Gets the current lock state.\n * @public\n */\n public get state(): KeyStoreLockState {\n return this._state;\n }\n\n /**\n * Gets the crypto provider used by this key store.\n * Available regardless of lock state.\n * @public\n */\n public get cryptoProvider(): ICryptoProvider {\n return this._cryptoProvider;\n }\n\n // ============================================================================\n // Secret Management\n // ============================================================================\n\n /**\n * Lists all secret names in the key store.\n * @returns Success with array of secret names, Failure if locked\n * @public\n */\n public listSecrets(): Result<readonly string[]> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n return succeed(Array.from(this._secrets.keys()));\n }\n\n /**\n * Gets a secret by name. Returns the {@link CryptoUtils.KeyStore.IKeyStoreEntry | discriminated union}\n * — callers must check `entry.type` before accessing `key`/`id` since asymmetric\n * entries carry no raw key material.\n * @param name - Name of the secret\n * @returns Success with secret entry, Failure if not found or locked\n * @public\n */\n public getSecret(name: string): Result<IKeyStoreEntry> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n const entry = this._secrets.get(name);\n if (!entry) {\n return fail(`Secret '${name}' not found`);\n }\n return succeed(entry);\n }\n\n /**\n * Returns the public-key JWK for an asymmetric-keypair entry.\n * Available without {@link CryptoUtils.KeyStore.IPrivateKeyStorage} since the\n * public key lives in the vault metadata directly.\n * @param name - Name of the entry\n * @returns Success with the JWK, Failure if not found, locked, or wrong type\n * @public\n */\n public getPublicKeyJwk(name: string): Result<JsonWebKey> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n const entry = this._secrets.get(name);\n if (!entry) {\n return fail(`Secret '${name}' not found`);\n }\n if (entry.type !== 'asymmetric-keypair') {\n return fail(`Secret '${name}' is not an asymmetric keypair (type: ${entry.type})`);\n }\n return succeed(entry.publicKeyJwk);\n }\n\n /**\n * Checks if a secret exists.\n * @param name - Name of the secret\n * @returns Success with boolean, Failure if locked\n * @public\n */\n public hasSecret(name: string): Result<boolean> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n return succeed(this._secrets.has(name));\n }\n\n /**\n * Adds a new secret with a randomly generated key.\n * @param name - Unique name for the secret\n * @param options - Optional description\n * @returns Success with the generated entry, Failure if locked or name invalid\n * @public\n */\n public async addSecret(name: string, options?: IAddSecretOptions): Promise<Result<IAddSecretResult>> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n if (!name || name.length === 0) {\n return fail('Secret name cannot be empty');\n }\n\n // Generate a new random key\n const keyResult = await this._cryptoProvider.generateKey();\n /* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */\n if (keyResult.isFailure()) {\n return fail(`Failed to generate key: ${keyResult.message}`);\n }\n\n const entry: IKeyStoreSymmetricEntry = {\n name,\n type: 'encryption-key',\n key: keyResult.value,\n description: options?.description,\n createdAt: getCurrentTimestamp()\n };\n\n const existing = this._secrets.get(name);\n const warning = existing ? await this._releaseEntryResources(existing) : undefined;\n\n this._secrets.set(name, entry);\n this._dirty = true;\n\n return succeed({ entry, replaced: existing !== undefined, warning });\n }\n\n /**\n * Imports raw 32-byte key material into the vault.\n *\n * Always validates that the key is exactly 32 bytes (AES-256). The optional\n * `type` field is a classification label stored with the entry; it does not\n * change the validation rules. For importing UTF-8 API key strings (variable\n * length), use {@link KeyStore.importApiKey} instead.\n *\n * @param name - Unique name for the secret\n * @param key - The 32-byte AES-256 key material\n * @param options - Optional type classification, description, whether to replace existing\n * @returns Success with entry, Failure if locked, key invalid, or exists and !replace\n * @public\n */\n public async importSecret(\n name: string,\n key: Uint8Array,\n options?: IImportKeyOptions\n ): Promise<Result<IAddSecretResult>> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n if (!name || name.length === 0) {\n return fail('Secret name cannot be empty');\n }\n if (key.length !== Constants.AES_256_KEY_SIZE) {\n return fail(`Key must be ${Constants.AES_256_KEY_SIZE} bytes, got ${key.length}`);\n }\n\n const existing = this._secrets.get(name);\n if (existing && !options?.replace) {\n return fail(`Secret '${name}' already exists - use replace=true to overwrite`);\n }\n\n const entry: IKeyStoreSymmetricEntry = {\n name,\n type: options?.type ?? 'encryption-key',\n key: new Uint8Array(key), // Copy to prevent external modification\n description: options?.description,\n createdAt: getCurrentTimestamp()\n };\n\n const warning = existing ? await this._releaseEntryResources(existing) : undefined;\n this._secrets.set(name, entry);\n this._dirty = true;\n\n return succeed({ entry, replaced: existing !== undefined, warning });\n }\n\n /**\n * Adds a secret derived from a password using PBKDF2.\n *\n * Generates a random salt, derives a 32-byte AES-256 key from the password,\n * and stores it in the vault. Returns the key derivation parameters so they\n * can be stored alongside encrypted files, enabling decryption with just the\n * password (without unlocking the keystore).\n *\n * @param name - Unique name for the secret\n * @param password - Password to derive the key from\n * @param options - Optional description, iterations, replace flag\n * @returns Success with entry and keyDerivation params, Failure if locked or invalid\n * @public\n */\n public async addSecretFromPassword(\n name: string,\n password: string,\n options?: IAddSecretFromPasswordOptions\n ): Promise<Result<IAddSecretFromPasswordResult>> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n if (!name || name.length === 0) {\n return fail('Secret name cannot be empty');\n }\n if (!password || password.length === 0) {\n return fail('Password cannot be empty');\n }\n\n const existing = this._secrets.get(name);\n if (existing && !options?.replace) {\n return fail(`Secret '${name}' already exists - use replace=true to overwrite`);\n }\n\n const iterations = options?.iterations ?? DEFAULT_SECRET_ITERATIONS;\n\n // Generate a random salt for this secret's key derivation\n const saltResult = this._cryptoProvider.generateRandomBytes(MIN_SALT_LENGTH);\n /* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */\n if (saltResult.isFailure()) {\n return fail(`Failed to generate salt: ${saltResult.message}`);\n }\n\n // Derive the key from password\n const keyResult = await this._cryptoProvider.deriveKey(password, saltResult.value, iterations);\n /* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */\n if (keyResult.isFailure()) {\n return fail(`Key derivation failed: ${keyResult.message}`);\n }\n\n const entry: IKeyStoreSymmetricEntry = {\n name,\n type: 'encryption-key',\n key: keyResult.value,\n description: options?.description,\n createdAt: getCurrentTimestamp()\n };\n\n const warning = existing ? await this._releaseEntryResources(existing) : undefined;\n this._secrets.set(name, entry);\n this._dirty = true;\n\n return succeed({\n entry,\n replaced: existing !== undefined,\n warning,\n keyDerivation: {\n kdf: 'pbkdf2',\n salt: this._cryptoProvider.toBase64(saltResult.value),\n iterations\n }\n });\n }\n\n /**\n * Removes a secret by name. Vault-first: the in-memory vault entry is dropped\n * before any storage cleanup runs. For asymmetric-keypair entries, best-effort\n * calls {@link CryptoUtils.KeyStore.IPrivateKeyStorage}.delete on the entry's\n * `id`; a failure is reported via `warning` on the result but does not roll\n * back the vault removal.\n * @param name - Name of the secret to remove\n * @returns Success with removed entry (and optional warning), Failure if not found or locked\n * @public\n */\n public async removeSecret(name: string): Promise<Result<IRemoveSecretResult>> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n\n const entry = this._secrets.get(name);\n if (!entry) {\n return fail(`Secret '${name}' not found`);\n }\n\n // Vault-first: drop the in-memory entry before touching storage so a\n // storage failure cannot block removal.\n this._secrets.delete(name);\n this._dirty = true;\n\n const warning = await this._releaseEntryResources(entry);\n return succeed({ entry, warning });\n }\n\n /**\n * Imports an API key string into the vault.\n * The string is UTF-8 encoded and stored with type `'api-key'`.\n * @param name - Unique name for the secret\n * @param apiKey - The API key string\n * @param options - Optional description, whether to replace existing\n * @returns Success with entry, Failure if locked, empty, or exists and !replace\n * @public\n */\n public async importApiKey(\n name: string,\n apiKey: string,\n options?: IImportSecretOptions\n ): Promise<Result<IAddSecretResult>> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n if (!name || name.length === 0) {\n return fail('Secret name cannot be empty');\n }\n if (!apiKey || apiKey.length === 0) {\n return fail('API key cannot be empty');\n }\n\n const existing = this._secrets.get(name);\n if (existing && !options?.replace) {\n return fail(`Secret '${name}' already exists - use replace=true to overwrite`);\n }\n\n const encoder = new TextEncoder();\n const entry: IKeyStoreSymmetricEntry = {\n name,\n type: 'api-key',\n key: encoder.encode(apiKey),\n description: options?.description,\n createdAt: getCurrentTimestamp()\n };\n\n const warning = existing ? await this._releaseEntryResources(existing) : undefined;\n this._secrets.set(name, entry);\n this._dirty = true;\n\n return succeed({ entry, replaced: existing !== undefined, warning });\n }\n\n /**\n * Retrieves an API key string by name.\n * Only works for secrets with type `'api-key'`.\n * @param name - Name of the secret\n * @returns Success with the API key string, Failure if not found, locked, or wrong type\n * @public\n */\n public getApiKey(name: string): Result<string> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n const entry = this._secrets.get(name);\n if (!entry) {\n return fail(`Secret '${name}' not found`);\n }\n if (entry.type !== 'api-key') {\n return fail(`Secret '${name}' is not an API key (type: ${entry.type})`);\n }\n const decoder = new TextDecoder();\n return succeed(decoder.decode(entry.key));\n }\n\n // ============================================================================\n // Asymmetric Keypair Management\n // ============================================================================\n\n /**\n * Adds a new asymmetric keypair to the vault. Storage-first: the private key\n * is stored under a freshly-minted `id` before the public-key vault entry is\n * committed. If the storage call fails, no vault entry is written and the\n * operation returns Failure.\n *\n * When `replace: true` displaces an existing entry (asymmetric or symmetric),\n * a fresh `id` is minted; the displaced entry's resources are released\n * best-effort. Failure of the storage delete is reported via `warning` on the\n * result but does not roll back the replacement.\n *\n * Requires a {@link CryptoUtils.KeyStore.IPrivateKeyStorage} backend\n * supplied at construction.\n *\n * @param name - Unique name for the entry\n * @param options - Algorithm, optional description, replace flag\n * @returns Success with the new entry, Failure if locked, no provider, or storage write failed\n * @public\n */\n public async addKeyPair(name: string, options: IAddKeyPairOptions): Promise<Result<IAddKeyPairResult>> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n if (!name || name.length === 0) {\n return fail('Entry name cannot be empty');\n }\n if (!this._privateKeyStorage) {\n return fail('No private key storage configured');\n }\n\n const existing = this._secrets.get(name);\n if (existing && !options.replace) {\n return fail(`Secret '${name}' already exists - use replace=true to overwrite`);\n }\n\n // Generate the keypair before touching storage. extractable=true on backends\n // that round-trip via JWK; extractable=false on backends that hold CryptoKey\n // refs directly.\n const extractable = !this._privateKeyStorage.supportsNonExtractable;\n const keyPairResult = await this._cryptoProvider.generateKeyPair(options.algorithm, extractable);\n /* c8 ignore next 3 - crypto provider errors covered in nodeCryptoProvider tests; cannot be triggered here without mocking */\n if (keyPairResult.isFailure()) {\n return fail(`Failed to generate keypair for '${name}': ${keyPairResult.message}`);\n }\n const { publicKey, privateKey } = keyPairResult.value;\n\n const jwkResult = await this._cryptoProvider.exportPublicKeyJwk(publicKey);\n /* c8 ignore next 3 - export of an extractable freshly-generated public key is hard to fail */\n if (jwkResult.isFailure()) {\n return fail(`Failed to export public key for '${name}': ${jwkResult.message}`);\n }\n\n const idResult = this._generateId();\n /* c8 ignore next 3 - random-bytes failure is hard to trigger with a healthy provider */\n if (idResult.isFailure()) {\n return fail(`Failed to mint storage id for '${name}': ${idResult.message}`);\n }\n const id = idResult.value;\n\n // Storage-first: write the private key before committing the vault entry.\n const storeResult = await this._privateKeyStorage.store(id, privateKey);\n if (storeResult.isFailure()) {\n return fail(`Failed to persist private key for '${name}': ${storeResult.message}`);\n }\n\n const entry: IKeyStoreAsymmetricEntry = {\n name,\n type: 'asymmetric-keypair',\n id,\n algorithm: options.algorithm,\n publicKeyJwk: jwkResult.value,\n description: options.description,\n createdAt: getCurrentTimestamp()\n };\n\n const warning = existing ? await this._releaseEntryResources(existing) : undefined;\n this._secrets.set(name, entry);\n this._dirty = true;\n\n return succeed({ entry, replaced: existing !== undefined, warning });\n }\n\n /**\n * Retrieves the keypair for an asymmetric-keypair entry. The private key is\n * loaded from {@link CryptoUtils.KeyStore.IPrivateKeyStorage} on every call —\n * the keystore never caches private `CryptoKey` references between calls.\n * The public key is re-imported from the vault's JWK so callers always\n * receive a `CryptoKey` rather than the JWK form.\n * @param name - Name of the entry\n * @returns Success with `{ publicKey, privateKey }`, Failure if not found,\n * locked, wrong type, no provider, or storage load failed.\n * @public\n */\n public async getKeyPair(name: string): Promise<Result<{ publicKey: CryptoKey; privateKey: CryptoKey }>> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n const entry = this._secrets.get(name);\n if (!entry) {\n return fail(`Secret '${name}' not found`);\n }\n if (entry.type !== 'asymmetric-keypair') {\n return fail(`Secret '${name}' is not an asymmetric keypair (type: ${entry.type})`);\n }\n if (!this._privateKeyStorage) {\n return fail('No private key storage configured');\n }\n\n const privateResult = await this._privateKeyStorage.load(entry.id);\n if (privateResult.isFailure()) {\n return fail(`Failed to load private key for '${name}': ${privateResult.message}`);\n }\n\n const publicResult = await this._cryptoProvider.importPublicKeyJwk(entry.publicKeyJwk, entry.algorithm);\n /* c8 ignore next 3 - vault JWKs that previously exported cleanly are extremely unlikely to fail re-import */\n if (publicResult.isFailure()) {\n return fail(`Failed to re-import public key for '${name}': ${publicResult.message}`);\n }\n\n return succeed({ publicKey: publicResult.value, privateKey: privateResult.value });\n }\n\n /**\n * Lists secret names filtered by type.\n * @param type - The secret type to filter by\n * @returns Success with array of matching secret names, Failure if locked\n * @public\n */\n public listSecretsByType(type: KeyStoreSecretType): Result<readonly string[]> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n const names: string[] = [];\n for (const [name, entry] of this._secrets) {\n if (entry.type === type) {\n names.push(name);\n }\n }\n return succeed(names);\n }\n\n /**\n * Renames a secret.\n * @param oldName - Current name\n * @param newName - New name\n * @returns Success with updated entry, Failure if source not found, target exists, or locked\n * @public\n */\n public renameSecret(oldName: string, newName: string): Result<IKeyStoreEntry> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n if (!newName || newName.length === 0) {\n return fail('New name cannot be empty');\n }\n\n const entry = this._secrets.get(oldName);\n if (!entry) {\n return fail(`Secret '${oldName}' not found`);\n }\n\n if (oldName !== newName && this._secrets.has(newName)) {\n return fail(`Secret '${newName}' already exists`);\n }\n\n // Create new entry with new name. For asymmetric entries the spread\n // preserves `id` so the storage handle survives the rename.\n const newEntry: IKeyStoreEntry = {\n ...entry,\n name: newName\n };\n\n this._secrets.delete(oldName);\n this._secrets.set(newName, newEntry);\n this._dirty = true;\n\n return succeed(newEntry);\n }\n\n // ============================================================================\n // Persistence\n // ============================================================================\n\n /**\n * Saves the key store, returning the encrypted file content.\n * Requires the master password to encrypt.\n * @param password - The master password\n * @returns Success with IKeyStoreFile, Failure if locked\n * @public\n */\n public async save(password: string): Promise<Result<IKeyStoreFile>> {\n if (!this._secrets || !this._salt) {\n return fail('Key store is locked');\n }\n if (!password || password.length === 0) {\n return fail('Password cannot be empty');\n }\n\n // Derive the encryption key\n const keyResult = await this._cryptoProvider.deriveKey(password, this._salt, this._iterations);\n /* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */\n if (keyResult.isFailure()) {\n return fail(`Key derivation failed: ${keyResult.message}`);\n }\n\n return this._encryptVault(keyResult.value);\n }\n\n /**\n * Saves the key store using a pre-derived key, bypassing PBKDF2 key\n * derivation. Use this when the derived key has been stored externally\n * (e.g., in another key store) and the original password is no longer\n * available.\n *\n * The supplied key must be the same key that was (or would be) derived\n * from the master password using the key store's PBKDF2 parameters.\n *\n * @param derivedKey - The pre-derived master key (32 bytes for AES-256)\n * @returns Success with IKeyStoreFile, Failure if locked or key invalid\n * @public\n */\n public async saveWithKey(derivedKey: Uint8Array): Promise<Result<IKeyStoreFile>> {\n if (!this._secrets || !this._salt) {\n return fail('Key store is locked');\n }\n if (derivedKey.length !== Constants.AES_256_KEY_SIZE) {\n return fail(`Key must be ${Constants.AES_256_KEY_SIZE} bytes, got ${derivedKey.length}`);\n }\n\n return this._encryptVault(derivedKey);\n }\n\n /**\n * Changes the master password.\n * Re-encrypts the vault with the new password-derived key.\n * @param currentPassword - Current master password (for verification)\n * @param newPassword - New master password\n * @returns Success when password changed, Failure if locked or current password incorrect\n * @public\n */\n public async changePassword(currentPassword: string, newPassword: string): Promise<Result<KeyStore>> {\n if (!this._secrets || !this._salt) {\n return fail('Key store is locked');\n }\n if (!newPassword || newPassword.length === 0) {\n return fail('New password cannot be empty');\n }\n\n // Verify current password by trying to derive and re-encrypt\n // (For opened stores, we'd need to verify against the stored file)\n if (this._keystoreFile) {\n const saltResult = this._cryptoProvider.fromBase64(this._keystoreFile.keyDerivation.salt);\n /* c8 ignore next 3 - error path tested but coverage intermittently missed */\n if (saltResult.isFailure()) {\n return fail(`Invalid salt in key store file: ${saltResult.message}`);\n }\n\n const keyResult = await this._cryptoProvider.deriveKey(\n currentPassword,\n saltResult.value,\n this._keystoreFile.keyDerivation.iterations\n );\n /* c8 ignore next 3 - error path tested but coverage intermittently missed */\n if (keyResult.isFailure()) {\n return fail(`Key derivation failed: ${keyResult.message}`);\n }\n\n // Try to decrypt to verify password\n const ivResult = this._cryptoProvider.fromBase64(this._keystoreFile.iv);\n const authTagResult = this._cryptoProvider.fromBase64(this._keystoreFile.authTag);\n const encryptedDataResult = this._cryptoProvider.fromBase64(this._keystoreFile.encryptedData);\n\n /* c8 ignore next 3 - error path tested but coverage intermittently missed */\n if (ivResult.isFailure() || authTagResult.isFailure() || encryptedDataResult.isFailure()) {\n return fail('Invalid key store file format');\n }\n\n const decryptResult = await this._cryptoProvider.decrypt(\n encryptedDataResult.value,\n keyResult.value,\n ivResult.value,\n authTagResult.value\n );\n if (decryptResult.isFailure()) {\n return fail('Current password is incorrect');\n }\n }\n\n // Generate new salt for the new password using crypto provider\n const saltResult = this._cryptoProvider.generateRandomBytes(MIN_SALT_LENGTH);\n /* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */\n if (saltResult.isFailure()) {\n return fail(`Failed to generate salt: ${saltResult.message}`);\n }\n\n this._salt = saltResult.value;\n this._dirty = true;\n\n // Save with new password\n const saveResult = await this.save(newPassword);\n /* c8 ignore next 3 - error path tested but coverage intermittently missed */\n if (saveResult.isFailure()) {\n return fail(saveResult.message);\n }\n return succeed(this);\n }\n\n // ============================================================================\n // IEncryptionProvider\n // ============================================================================\n\n /** {@inheritDoc IEncryptionProvider.encryptByName} */\n public async encryptByName<TMetadata = JsonValue>(\n secretName: string,\n content: JsonValue,\n metadata?: TMetadata\n ): Promise<Result<IEncryptedFile<TMetadata>>> {\n const secretResult = this.getSecret(secretName);\n if (secretResult.isFailure()) {\n return fail(`encryptByName: ${secretResult.message}`);\n }\n if (secretResult.value.type === 'asymmetric-keypair') {\n return fail(\n `encryptByName: secret '${secretName}' is an asymmetric keypair, not symmetric key material`\n );\n }\n\n return createEncryptedFile({\n content,\n secretName,\n key: secretResult.value.key,\n cryptoProvider: this._cryptoProvider,\n metadata\n });\n }\n\n // ============================================================================\n // Integration with IEncryptionConfig\n // ============================================================================\n\n /**\n * Creates a SecretProvider function for use with IEncryptionConfig.\n * The returned function looks up secrets from this key store.\n * @returns Success with SecretProvider, Failure if locked\n * @public\n */\n public getSecretProvider(): Result<SecretProvider> {\n if (!this._secrets) {\n return fail('Key store is locked');\n }\n\n const secrets = this._secrets;\n const provider: SecretProvider = async (secretName: string): Promise<Result<Uint8Array>> => {\n const entry = secrets.get(secretName);\n if (!entry) {\n return fail(`Secret '${secretName}' not found in key store`);\n }\n if (entry.type === 'asymmetric-keypair') {\n return fail(`Secret '${secretName}' is an asymmetric keypair, not symmetric key material`);\n }\n return succeed(entry.key);\n };\n\n return succeed(provider);\n }\n\n /**\n * Creates a partial IEncryptionConfig using this key store as the secret source.\n * @returns Partial config that can be spread into a full IEncryptionConfig\n * @public\n */\n public getEncryptionConfig(): Result<Pick<IEncryptionConfig, 'secretProvider' | 'cryptoProvider'>> {\n const providerResult = this.getSecretProvider();\n if (providerResult.isFailure()) {\n return fail(providerResult.message);\n }\n\n return succeed({\n secretProvider: providerResult.value,\n cryptoProvider: this._cryptoProvider\n });\n }\n\n // ============================================================================\n // Private: Vault Encryption / Decryption\n // ============================================================================\n\n /**\n * Encrypts the vault with a derived key and returns the key store file.\n * Shared by `save()` and `saveWithKey()`.\n */\n private async _encryptVault(derivedKey: Uint8Array): Promise<Result<IKeyStoreFile>> {\n // _secrets and _salt are guaranteed non-undefined by callers\n const secrets = this._secrets!;\n const salt = this._salt!;\n\n // Build vault contents\n const secretEntries: Record<string, IKeyStoreEntryJson> = {};\n for (const [name, entry] of secrets) {\n if (entry.type === 'asymmetric-keypair') {\n secretEntries[name] = {\n name: entry.name,\n type: entry.type,\n id: entry.id,\n algorithm: entry.algorithm,\n publicKeyJwk: entry.publicKeyJwk,\n description: entry.description,\n createdAt: entry.createdAt\n };\n } else {\n secretEntries[name] = {\n name: entry.name,\n type: entry.type,\n key: this._cryptoProvider.toBase64(entry.key),\n description: entry.description,\n createdAt: entry.createdAt\n };\n }\n }\n\n const vaultContents: IKeyStoreVaultContents = {\n version: KEYSTORE_FORMAT,\n secrets: secretEntries\n };\n\n // Serialize and encrypt\n const jsonResult = captureResult(() => JSON.stringify(vaultContents));\n /* c8 ignore next 3 - error path tested but coverage intermittently missed */\n if (jsonResult.isFailure()) {\n return fail(`Failed to serialize vault: ${jsonResult.message}`);\n }\n\n const encryptResult = await this._cryptoProvider.encrypt(jsonResult.value, derivedKey);\n /* c8 ignore next 3 - crypto provider errors tested but coverage intermittently missed */\n if (encryptResult.isFailure()) {\n return fail(`Encryption failed: ${encryptResult.message}`);\n }\n\n const { iv, authTag, encryptedData } = encryptResult.value;\n\n const keystoreFileData: IKeyStoreFile = {\n format: KEYSTORE_FORMAT,\n algorithm: Constants.DEFAULT_ALGORITHM,\n iv: this._cryptoProvider.toBase64(iv),\n authTag: this._cryptoProvider.toBase64(authTag),\n encryptedData: this._cryptoProvider.toBase64(encryptedData),\n keyDerivation: {\n kdf: 'pbkdf2',\n salt: this._cryptoProvider.toBase64(salt),\n iterations: this._iterations\n }\n };\n\n this._keystoreFile = keystoreFileData;\n this._dirty = false;\n this._isNew = false;\n\n return succeed(keystoreFileData);\n }\n\n /**\n * Decrypts the vault with a derived key and loads secrets into memory.\n * Shared by `unlock()` and `unlockWithKey()`.\n */\n private async _decryptVault(derivedKey: Uint8Array): Promise<Result<KeyStore>> {\n const keystoreFile = this._keystoreFile;\n if (keystoreFile === undefined) {\n return fail('No key store file loaded');\n }\n\n const ivResult = this._cryptoProvider.fromBase64(keystoreFile.iv);\n const authTagResult = this._cryptoProvider.fromBase64(keystoreFile.authTag);\n const encryptedDataResult = this._cryptoProvider.fromBase64(keystoreFile.encryptedData);\n\n /* c8 ignore next 9 - base64 decode errors tested but coverage intermittently missed */\n if (ivResult.isFailure()) {\n return fail(`Invalid IV in key store file: ${ivResult.message}`);\n }\n if (authTagResult.isFailure()) {\n return fail(`Invalid auth tag in key store file: ${authTagResult.message}`);\n }\n if (encryptedDataResult.isFailure()) {\n return fail(`Invalid encrypted data in key store file: ${encryptedDataResult.message}`);\n }\n\n const decryptResult = await this._cryptoProvider.decrypt(\n encryptedDataResult.value,\n derivedKey,\n ivResult.value,\n authTagResult.value\n );\n if (decryptResult.isFailure()) {\n return fail('Incorrect password or corrupted key store');\n }\n\n // Parse the vault contents\n const parseResult = captureResult(() => JSON.parse(decryptResult.value) as unknown);\n /* c8 ignore next 3 - error path tested but coverage intermittently missed */\n if (parseResult.isFailure()) {\n return fail(`Failed to parse vault contents: ${parseResult.message}`);\n }\n\n const vaultResult = keystoreVaultContents.convert(parseResult.value);\n /* c8 ignore next 3 - error path tested but coverage intermittently missed */\n if (vaultResult.isFailure()) {\n return fail(`Invalid vault format: ${vaultResult.message}`);\n }\n\n // Build secrets into local variables to avoid partial state on failure\n const saltResult = this._cryptoProvider.fromBase64(keystoreFile.keyDerivation.salt);\n if (saltResult.isFailure()) {\n return fail(`Invalid salt in key store file: ${saltResult.message}`);\n }\n const secrets = new Map<string, IKeyStoreEntry>();\n for (const [name, jsonEntry] of Object.entries(vaultResult.value.secrets)) {\n if (jsonEntry.type === 'asymmetric-keypair') {\n const entry: IKeyStoreAsymmetricEntry = {\n name,\n type: jsonEntry.type,\n id: jsonEntry.id,\n algorithm: jsonEntry.algorithm,\n publicKeyJwk: jsonEntry.publicKeyJwk,\n description: jsonEntry.description,\n createdAt: jsonEntry.createdAt\n };\n secrets.set(name, entry);\n } else {\n const keyBytesResult = this._cryptoProvider.fromBase64(jsonEntry.key);\n /* c8 ignore next 3 - error path tested but coverage intermittently missed */\n if (keyBytesResult.isFailure()) {\n return fail(`Invalid key for secret '${name}': ${keyBytesResult.message}`);\n }\n const entry: IKeyStoreSymmetricEntry = {\n name,\n type: jsonEntry.type,\n key: keyBytesResult.value,\n description: jsonEntry.description,\n createdAt: jsonEntry.createdAt\n };\n secrets.set(name, entry);\n }\n }\n\n // All validation passed — commit state atomically\n this._salt = saltResult.value;\n this._secrets = secrets;\n this._state = 'unlocked';\n this._dirty = false;\n\n return succeed(this);\n }\n\n // ============================================================================\n // Private: Helpers for asymmetric flows\n // ============================================================================\n\n /**\n * Releases the resources held by an entry being displaced from the vault.\n * Symmetric entries get their key buffer zeroed in place. Asymmetric entries\n * have their private-key blob best-effort deleted from\n * {@link CryptoUtils.KeyStore.IPrivateKeyStorage}; if the storage call fails,\n * a warning string is returned but the displacement still proceeds — the\n * orphaned blob is left for consumer-side GC. Without a configured provider,\n * asymmetric cleanup is silently skipped.\n * @returns A warning string if storage cleanup failed, otherwise undefined.\n */\n private async _releaseEntryResources(entry: IKeyStoreEntry): Promise<string | undefined> {\n if (entry.type === 'asymmetric-keypair') {\n if (!this._privateKeyStorage) {\n return undefined;\n }\n const deleteResult = await this._privateKeyStorage.delete(entry.id);\n if (deleteResult.isFailure()) {\n return `Failed to delete prior storage blob for '${entry.name}' (id ${entry.id}): ${deleteResult.message}`;\n }\n return undefined;\n }\n entry.key.fill(0);\n return undefined;\n }\n\n /**\n * Mints a fresh UUID v4 storage handle using the crypto provider's\n * {@link CryptoUtils.ICryptoProvider.generateRandomBytes | generateRandomBytes}.\n * Random-bytes failures propagate as Failure.\n */\n private _generateId(): Result<string> {\n return this._cryptoProvider.generateRandomBytes(16).onSuccess((bytes) => {\n // Per RFC 4122 §4.4: set version (4) and variant (10xx) bits.\n // eslint-disable-next-line no-bitwise\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n // eslint-disable-next-line no-bitwise\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n return succeed(\n `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(\n 20,\n 32\n )}`\n );\n });\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../../src/packlets/crypto-utils/keystore/model.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;AAKZ,2EAA2E;AAC3E,2EAA2E;AAC3E,OAAO,EAAE,oBAAoB,EAAoB,MAAM,UAAU,CAAC;AAYlE;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAmB,aAAa,CAAC;AAE7D;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAW,MAAM,CAAC;AAE1D;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAW,EAAE,CAAC;AAc1C;;;GAGG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAA+C;IACzF,gBAAgB;IAChB,SAAS;CACV,CAAC;AAWF;;;GAGG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAgD;IAC3F,oBAAoB;CACrB,CAAC;AAQF;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsC;IACvE,GAAG,gCAAgC;IACnC,GAAG,+BAA+B;CACnC,CAAC;AAsYF;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAW,MAAM,CAAC;AAoFxD,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAa;IAC1C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,OAAO,GAAG,CAAC,MAAM,KAAK,eAAe,CAAC;AACxC,CAAC","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { EncryptionAlgorithm, ICryptoProvider, IKeyDerivationParams, KeyPairAlgorithm } from '../model';\nimport { IPrivateKeyStorage } from './privateKeyStorage';\n\n// Re-export so consumers can continue to access the algorithm enum via the\n// CryptoUtils.KeyStore namespace alongside the rest of the keystore types.\nexport { allKeyPairAlgorithms, KeyPairAlgorithm } from '../model';\n\n// ============================================================================\n// Key Store Format Types\n// ============================================================================\n\n/**\n * Format version for key store files.\n * @public\n */\nexport type KeyStoreFormat = 'keystore-v1';\n\n/**\n * Current format version constant.\n * @public\n */\nexport const KEYSTORE_FORMAT: KeyStoreFormat = 'keystore-v1';\n\n/**\n * Default PBKDF2 iterations for key store encryption.\n * Higher than regular files since this protects the master key vault.\n * @public\n */\nexport const DEFAULT_KEYSTORE_ITERATIONS: number = 600000;\n\n/**\n * Minimum salt length for key derivation.\n * @public\n */\nexport const MIN_SALT_LENGTH: number = 16;\n\n// ============================================================================\n// Key Store Vault Contents (Decrypted State)\n// ============================================================================\n\n/**\n * Discriminator for symmetric secret types stored in the vault.\n * - `'encryption-key'`: A 32-byte AES-256 encryption key.\n * - `'api-key'`: An arbitrary-length API key string (UTF-8 encoded).\n * @public\n */\nexport type KeyStoreSymmetricSecretType = 'encryption-key' | 'api-key';\n\n/**\n * All valid symmetric secret types.\n * @public\n */\nexport const allKeyStoreSymmetricSecretTypes: ReadonlyArray<KeyStoreSymmetricSecretType> = [\n 'encryption-key',\n 'api-key'\n];\n\n/**\n * Discriminator for asymmetric secret types stored in the vault.\n * - `'asymmetric-keypair'`: A public/private key pair. The public key is held in\n * the vault as a JWK; the private key lives in the supplied\n * {@link CryptoUtils.KeyStore.IPrivateKeyStorage} provider.\n * @public\n */\nexport type KeyStoreAsymmetricSecretType = 'asymmetric-keypair';\n\n/**\n * All valid asymmetric secret types.\n * @public\n */\nexport const allKeyStoreAsymmetricSecretTypes: ReadonlyArray<KeyStoreAsymmetricSecretType> = [\n 'asymmetric-keypair'\n];\n\n/**\n * Discriminator for any secret type stored in the vault.\n * @public\n */\nexport type KeyStoreSecretType = KeyStoreSymmetricSecretType | KeyStoreAsymmetricSecretType;\n\n/**\n * All valid key store secret types.\n * @public\n */\nexport const allKeyStoreSecretTypes: ReadonlyArray<KeyStoreSecretType> = [\n ...allKeyStoreAsymmetricSecretTypes,\n ...allKeyStoreSymmetricSecretTypes\n];\n\n/**\n * A symmetric secret entry stored in the vault (in-memory representation).\n * Holds the raw key material directly — for `'encryption-key'` it is a 32-byte\n * AES-256 key; for `'api-key'` it is the UTF-8 encoded API key string.\n * @public\n */\nexport interface IKeyStoreSymmetricEntry {\n /**\n * Unique name for this secret (used as lookup key).\n */\n readonly name: string;\n\n /**\n * Symmetric secret type discriminator.\n */\n readonly type: KeyStoreSymmetricSecretType;\n\n /**\n * The secret data.\n * - For `'encryption-key'`: 32-byte AES-256 key.\n * - For `'api-key'`: UTF-8 encoded API key string (arbitrary length).\n */\n readonly key: Uint8Array;\n\n /**\n * Optional description for this secret.\n */\n readonly description?: string;\n\n /**\n * When this secret was added (ISO 8601).\n */\n readonly createdAt: string;\n}\n\n/**\n * An asymmetric keypair entry stored in the vault (in-memory representation).\n * Holds only the public key (as a JWK) and a stable handle (`id`) the\n * {@link CryptoUtils.KeyStore.IPrivateKeyStorage} provider uses to fetch the private key.\n * @public\n */\nexport interface IKeyStoreAsymmetricEntry {\n /**\n * Unique name for this entry (used as vault lookup key, renameable).\n */\n readonly name: string;\n\n /**\n * Asymmetric secret type discriminator.\n */\n readonly type: KeyStoreAsymmetricSecretType;\n\n /**\n * Immutable handle used by {@link CryptoUtils.KeyStore.IPrivateKeyStorage} to address the\n * private key. Independent of `name`; survives renames.\n */\n readonly id: string;\n\n /**\n * Algorithm used to generate this keypair.\n */\n readonly algorithm: KeyPairAlgorithm;\n\n /**\n * The public key as a JSON Web Key.\n */\n readonly publicKeyJwk: JsonWebKey;\n\n /**\n * Optional description for this entry.\n */\n readonly description?: string;\n\n /**\n * When this entry was added (ISO 8601).\n */\n readonly createdAt: string;\n}\n\n/**\n * Any vault entry, discriminated by `type`.\n * @public\n */\nexport type IKeyStoreEntry = IKeyStoreSymmetricEntry | IKeyStoreAsymmetricEntry;\n\n/**\n * Backwards-compatible alias for {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntry}.\n * @deprecated Use {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntry} for symmetric\n * entries or {@link CryptoUtils.KeyStore.IKeyStoreEntry} for the discriminated union.\n * @public\n */\nexport type IKeyStoreSecretEntry = IKeyStoreSymmetricEntry;\n\n/**\n * JSON-serializable representation of a symmetric secret entry.\n *\n * @remarks\n * Describes the *normalized* shape after parsing. `type` is required here\n * because the converter (see\n * {@link CryptoUtils.KeyStore.Converters.keystoreSymmetricEntryJson | keystoreSymmetricEntryJson})\n * injects the default `'encryption-key'` when reading vaults written before\n * asymmetric-keypair support added the discriminator. Raw on-wire bytes from\n * a legacy vault may therefore omit `type`; downstream code only ever sees\n * the post-conversion shape declared here.\n *\n * @public\n */\nexport interface IKeyStoreSymmetricEntryJson {\n /**\n * Unique name for this secret.\n */\n readonly name: string;\n\n /**\n * Symmetric secret type discriminator.\n *\n * Required on this normalized model type. Vaults written prior to the\n * asymmetric-keypair support may omit this field on the wire; the\n * converter injects `'encryption-key'` when missing for backwards\n * compatibility, so by the time a value of this type is observed the\n * discriminator is always present.\n */\n readonly type: KeyStoreSymmetricSecretType;\n\n /**\n * Base64-encoded secret data.\n */\n readonly key: string;\n\n /**\n * Optional description.\n */\n readonly description?: string;\n\n /**\n * When this secret was added (ISO 8601).\n */\n readonly createdAt: string;\n}\n\n/**\n * JSON-serializable representation of an asymmetric keypair entry.\n * The private key is not present here — it lives in the\n * {@link CryptoUtils.KeyStore.IPrivateKeyStorage} provider, addressed by `id`.\n * @public\n */\nexport interface IKeyStoreAsymmetricEntryJson {\n /**\n * Unique name for this entry.\n */\n readonly name: string;\n\n /**\n * Asymmetric secret type discriminator.\n */\n readonly type: KeyStoreAsymmetricSecretType;\n\n /**\n * Immutable handle used by {@link CryptoUtils.KeyStore.IPrivateKeyStorage} to address the\n * private key.\n */\n readonly id: string;\n\n /**\n * Algorithm used to generate this keypair.\n */\n readonly algorithm: KeyPairAlgorithm;\n\n /**\n * The public key as a JSON Web Key.\n */\n readonly publicKeyJwk: JsonWebKey;\n\n /**\n * Optional description.\n */\n readonly description?: string;\n\n /**\n * When this entry was added (ISO 8601).\n */\n readonly createdAt: string;\n}\n\n/**\n * Any JSON vault entry, discriminated by `type`.\n * @public\n */\nexport type IKeyStoreEntryJson = IKeyStoreSymmetricEntryJson | IKeyStoreAsymmetricEntryJson;\n\n/**\n * Backwards-compatible alias for {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntryJson}.\n * @deprecated Use {@link CryptoUtils.KeyStore.IKeyStoreSymmetricEntryJson} for\n * symmetric entries or {@link CryptoUtils.KeyStore.IKeyStoreEntryJson} for the\n * discriminated union.\n * @public\n */\nexport type IKeyStoreSecretEntryJson = IKeyStoreSymmetricEntryJson;\n\n/**\n * The decrypted vault contents - a versioned map of entries.\n * @public\n */\nexport interface IKeyStoreVaultContents {\n /**\n * Format version for vault contents.\n */\n readonly version: KeyStoreFormat;\n\n /**\n * Map of entry name to entry (symmetric or asymmetric).\n */\n readonly secrets: Record<string, IKeyStoreEntryJson>;\n}\n\n// ============================================================================\n// Key Store File Format (Encrypted State)\n// ============================================================================\n\n/**\n * The encrypted key store file format.\n * @public\n */\nexport interface IKeyStoreFile {\n /**\n * Format identifier.\n */\n readonly format: KeyStoreFormat;\n\n /**\n * Algorithm used for encryption.\n */\n readonly algorithm: EncryptionAlgorithm;\n\n /**\n * Base64-encoded initialization vector.\n */\n readonly iv: string;\n\n /**\n * Base64-encoded authentication tag.\n */\n readonly authTag: string;\n\n /**\n * Base64-encoded encrypted vault contents.\n */\n readonly encryptedData: string;\n\n /**\n * Key derivation parameters (required for key store - always password-derived).\n */\n readonly keyDerivation: IKeyDerivationParams;\n}\n\n// ============================================================================\n// Key Store State and Configuration\n// ============================================================================\n\n/**\n * Key store lock state.\n * @public\n */\nexport type KeyStoreLockState = 'locked' | 'unlocked';\n\n/**\n * Parameters for creating a new key store.\n * @public\n */\nexport interface IKeyStoreCreateParams {\n /**\n * Crypto provider to use.\n */\n readonly cryptoProvider: ICryptoProvider;\n\n /**\n * PBKDF2 iterations (defaults to DEFAULT_KEYSTORE_ITERATIONS).\n */\n readonly iterations?: number;\n\n /**\n * Optional private-key storage backend. Required to use `addKeyPair` /\n * `getKeyPair`; absent backends still permit opening, listing, and reading\n * public-key metadata for asymmetric entries.\n */\n readonly privateKeyStorage?: IPrivateKeyStorage;\n}\n\n/**\n * Parameters for opening an existing key store.\n * @public\n */\nexport interface IKeyStoreOpenParams {\n /**\n * Crypto provider to use.\n */\n readonly cryptoProvider: ICryptoProvider;\n\n /**\n * The encrypted key store file content.\n */\n readonly keystoreFile: IKeyStoreFile;\n\n /**\n * Optional private-key storage backend. Required to use `addKeyPair` /\n * `getKeyPair`; absent backends still permit opening, listing, and reading\n * public-key metadata for asymmetric entries.\n */\n readonly privateKeyStorage?: IPrivateKeyStorage;\n}\n\n/**\n * Result of adding a secret to the key store.\n * @public\n */\nexport interface IAddSecretResult {\n /**\n * The secret entry that was added.\n */\n readonly entry: IKeyStoreSymmetricEntry;\n\n /**\n * Whether this replaced an existing secret.\n */\n readonly replaced: boolean;\n\n /**\n * Best-effort warning from displaced-resource cleanup. Set when this call\n * replaced an asymmetric-keypair entry but the corresponding\n * {@link CryptoUtils.KeyStore.IPrivateKeyStorage}.delete failed; the new\n * entry is still committed and the orphaned blob is left for consumer-side\n * GC to reconcile.\n */\n readonly warning?: string;\n}\n\n/**\n * Options for adding a secret.\n * @public\n */\nexport interface IAddSecretOptions {\n /**\n * Optional description for the secret.\n */\n readonly description?: string;\n}\n\n/**\n * Options for importing a secret.\n * @public\n */\nexport interface IImportSecretOptions extends IAddSecretOptions {\n /**\n * Whether to replace an existing secret with the same name.\n */\n readonly replace?: boolean;\n}\n\n/**\n * Options for importing raw key material via {@link KeyStore.importSecret}.\n * Extends {@link IImportSecretOptions} with a type classification.\n * @public\n */\nexport interface IImportKeyOptions extends IImportSecretOptions {\n /**\n * Symmetric secret type classification for the imported key material.\n * @defaultValue 'encryption-key'\n */\n readonly type?: KeyStoreSymmetricSecretType;\n}\n\n/**\n * Options for adding a secret derived from a password.\n * @public\n */\nexport interface IAddSecretFromPasswordOptions extends IAddSecretOptions {\n /**\n * Whether to replace an existing secret with the same name.\n */\n readonly replace?: boolean;\n\n /**\n * PBKDF2 iterations for key derivation.\n * @defaultValue DEFAULT_SECRET_ITERATIONS (350000)\n */\n readonly iterations?: number;\n}\n\n/**\n * Default PBKDF2 iterations for secret-level key derivation.\n * Lower than keystore encryption since these are used more frequently.\n * @public\n */\nexport const DEFAULT_SECRET_ITERATIONS: number = 350000;\n\n/**\n * Result of adding a password-derived secret.\n * Extends {@link IAddSecretResult} with key derivation parameters\n * needed to store alongside encrypted files.\n * @public\n */\nexport interface IAddSecretFromPasswordResult extends IAddSecretResult {\n /**\n * Key derivation parameters used to derive the secret key.\n * Store these in encrypted file metadata so the password alone\n * can re-derive the same key for decryption.\n */\n readonly keyDerivation: IKeyDerivationParams;\n}\n\n/**\n * Options for adding an asymmetric keypair to the key store.\n * @public\n */\nexport interface IAddKeyPairOptions {\n /**\n * Algorithm to use for the new keypair.\n */\n readonly algorithm: KeyPairAlgorithm;\n\n /**\n * Optional description for the entry.\n */\n readonly description?: string;\n\n /**\n * Whether to replace an existing entry with the same name.\n * Replacement mints a fresh storage `id` and best-effort deletes the\n * displaced storage blob; see the keystore design doc for details.\n */\n readonly replace?: boolean;\n}\n\n/**\n * Result of adding an asymmetric keypair to the key store.\n * @public\n */\nexport interface IAddKeyPairResult {\n /**\n * The asymmetric entry that was added.\n */\n readonly entry: IKeyStoreAsymmetricEntry;\n\n /**\n * Whether this replaced an existing entry.\n */\n readonly replaced: boolean;\n\n /**\n * Best-effort warning from displaced-resource cleanup. Set when this call\n * replaced a prior entry but the corresponding\n * {@link CryptoUtils.KeyStore.IPrivateKeyStorage}.delete failed; the new\n * keypair is still committed and the orphaned blob is left for consumer-side\n * GC to reconcile.\n */\n readonly warning?: string;\n}\n\n/**\n * Result of removing a secret from the key store.\n * @public\n */\nexport interface IRemoveSecretResult {\n /**\n * The secret entry that was removed from the vault.\n */\n readonly entry: IKeyStoreEntry;\n\n /**\n * Best-effort warning from {@link CryptoUtils.KeyStore.IPrivateKeyStorage}.delete\n * for asymmetric entries when the storage call failed. The vault entry is\n * still considered removed and the orphaned blob is left for consumer-side\n * GC to reconcile.\n */\n readonly warning?: string;\n}\n\n// ============================================================================\n// Detection Helper\n// ============================================================================\n\n/**\n * Checks if a JSON object appears to be a key store file.\n * Uses the format field as a discriminator.\n * @param json - JSON object to check\n * @returns true if the object has the key store format field\n * @public\n */\nexport function isKeyStoreFile(json: unknown): boolean {\n if (typeof json !== 'object' || json === null) {\n return false;\n }\n const obj = json as Record<string, unknown>;\n return obj.format === KEYSTORE_FORMAT;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privateKeyStorage.js","sourceRoot":"","sources":["../../../../src/packlets/crypto-utils/keystore/privateKeyStorage.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { Result } from '@fgv/ts-utils';\n\n/**\n * Pluggable backend that persists raw asymmetric private keys outside of the\n * encrypted keystore vault. Concrete implementations live in platform-specific\n * packages (e.g. an IndexedDB-backed implementation in `@fgv/ts-web-extras` or\n * an encrypted-file implementation in `@fgv/ts-chocolate`).\n *\n * The keystore writes storage-first: a private key is always stored here\n * before the corresponding public-key vault entry is committed. Conversely,\n * deletes hit the vault first and then this storage best-effort. As a result,\n * crashes or skipped saves can leave orphaned blobs here; callers are expected\n * to reconcile via {@link CryptoUtils.KeyStore.IPrivateKeyStorage.list} cross-referenced\n * against the keystore's asymmetric entries.\n *\n * @public\n */\nexport interface IPrivateKeyStorage {\n /**\n * Whether keys generated for this backend may be marked\n * `extractable: false`. `true` on backends that store `CryptoKey`\n * objects directly (e.g. IndexedDB). `false` on backends that must\n * round-trip via JWK (e.g. encrypted-file backends).\n */\n readonly supportsNonExtractable: boolean;\n\n /**\n * Stores `key` under `id`. Returns the stored `id` on success so the\n * call can compose into a Result chain.\n * @param id - Storage handle to write under.\n * @param key - The private `CryptoKey` to persist.\n */\n store(id: string, key: CryptoKey): Promise<Result<string>>;\n\n /**\n * Loads the private key previously stored under `id`.\n * @param id - Storage handle to look up.\n */\n load(id: string): Promise<Result<CryptoKey>>;\n\n /**\n * Deletes the entry stored under `id`. Returns the deleted `id` on\n * success so the call can compose into a Result chain.\n * @param id - Storage handle to remove.\n */\n delete(id: string): Promise<Result<string>>;\n\n /**\n * Lists every `id` currently held by the backend. Used by consumers to\n * garbage-collect orphans left by crashes or aborted sessions; the\n * keystore itself does not invoke this automatically.\n */\n list(): Promise<Result<readonly string[]>>;\n}\n"]}
|