@fgv/ts-extras 5.1.0-5 → 5.1.0-50
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/LICENSE +1 -1
- package/README.md +90 -0
- package/dist/index.browser.js +6 -2
- package/dist/index.browser.js.map +1 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -0
- package/dist/packlets/ai-assist/chatRequestBuilders.js +282 -0
- package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -0
- package/dist/packlets/ai-assist/{apiClient.js → completionClient.js} +165 -214
- package/dist/packlets/ai-assist/completionClient.js.map +1 -0
- package/dist/packlets/ai-assist/converters.js +47 -3
- package/dist/packlets/ai-assist/converters.js.map +1 -0
- package/dist/packlets/ai-assist/embeddingClient.js +345 -0
- package/dist/packlets/ai-assist/embeddingClient.js.map +1 -0
- package/dist/packlets/ai-assist/endpoint.js +107 -0
- package/dist/packlets/ai-assist/endpoint.js.map +1 -0
- package/dist/packlets/ai-assist/http.js +104 -0
- package/dist/packlets/ai-assist/http.js.map +1 -0
- package/dist/packlets/ai-assist/imageGenerationClient.js +454 -0
- package/dist/packlets/ai-assist/imageGenerationClient.js.map +1 -0
- package/dist/packlets/ai-assist/imageOptionsResolver.js +193 -0
- package/dist/packlets/ai-assist/imageOptionsResolver.js.map +1 -0
- package/dist/packlets/ai-assist/index.js +12 -4
- package/dist/packlets/ai-assist/index.js.map +1 -0
- package/dist/packlets/ai-assist/jsonCompletion.js +93 -0
- package/dist/packlets/ai-assist/jsonCompletion.js.map +1 -0
- package/dist/packlets/ai-assist/jsonResponse.js +423 -0
- package/dist/packlets/ai-assist/jsonResponse.js.map +1 -0
- package/dist/packlets/ai-assist/listModelsClient.js +294 -0
- package/dist/packlets/ai-assist/listModelsClient.js.map +1 -0
- package/dist/packlets/ai-assist/model.js +301 -9
- package/dist/packlets/ai-assist/model.js.map +1 -0
- package/dist/packlets/ai-assist/registry.js +376 -12
- package/dist/packlets/ai-assist/registry.js.map +1 -0
- package/dist/packlets/ai-assist/sseParser.js +123 -0
- package/dist/packlets/ai-assist/sseParser.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +376 -0
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +687 -0
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/common.js +187 -0
- package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js +208 -0
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +175 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +390 -0
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js +178 -0
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -0
- package/dist/packlets/ai-assist/streamingClient.js +140 -0
- package/dist/packlets/ai-assist/streamingClient.js.map +1 -0
- package/dist/packlets/ai-assist/thinkingOptionsResolver.js +288 -0
- package/dist/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -0
- package/dist/packlets/ai-assist/toolFormats.js +106 -10
- package/dist/packlets/ai-assist/toolFormats.js.map +1 -0
- package/dist/packlets/conversion/converters.js +35 -1
- 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 +42 -4
- 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 +37 -0
- package/dist/packlets/crypto-utils/encryptedFile.js.map +1 -0
- package/dist/packlets/crypto-utils/hpkeProvider.js +355 -0
- package/dist/packlets/crypto-utils/hpkeProvider.js.map +1 -0
- package/dist/packlets/crypto-utils/index.browser.js +13 -2
- package/dist/packlets/crypto-utils/index.browser.js.map +1 -0
- package/dist/packlets/crypto-utils/index.js +9 -1
- package/dist/packlets/crypto-utils/index.js.map +1 -0
- package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js +71 -0
- package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/converters.js +111 -16
- package/dist/packlets/crypto-utils/keystore/converters.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js +287 -0
- package/dist/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/index.browser.js +36 -0
- package/dist/packlets/crypto-utils/keystore/index.browser.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/index.js +3 -0
- package/dist/packlets/crypto-utils/keystore/index.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/keyStore.js +872 -119
- package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/model.js +35 -4
- package/dist/packlets/crypto-utils/keystore/model.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js +21 -0
- package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -0
- package/dist/packlets/crypto-utils/model.js +32 -0
- package/dist/packlets/crypto-utils/model.js.map +1 -0
- package/dist/packlets/crypto-utils/nodeCryptoProvider.js +347 -1
- package/dist/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -0
- package/dist/packlets/crypto-utils/seedDerivedKeyPair.js +120 -0
- package/dist/packlets/crypto-utils/seedDerivedKeyPair.js.map +1 -0
- package/dist/packlets/crypto-utils/spkiHelpers.js +284 -0
- package/dist/packlets/crypto-utils/spkiHelpers.js.map +1 -0
- package/dist/packlets/csv/csvFileHelpers.js +0 -14
- package/dist/packlets/csv/csvFileHelpers.js.map +1 -0
- package/dist/packlets/csv/csvHelpers.js +14 -0
- package/dist/packlets/csv/csvHelpers.js.map +1 -0
- package/dist/packlets/csv/index.browser.js +1 -3
- 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 +42 -4
- package/dist/packlets/mustache/mustacheTemplate.js.map +1 -0
- package/dist/packlets/record-jar/index.browser.js +1 -3
- 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 +0 -18
- package/dist/packlets/record-jar/recordJarFileHelpers.js.map +1 -0
- package/dist/packlets/record-jar/recordJarHelpers.js +18 -0
- package/dist/packlets/record-jar/recordJarHelpers.js.map +1 -0
- package/dist/packlets/safer-fetch/addressClassification.js +438 -0
- package/dist/packlets/safer-fetch/addressClassification.js.map +1 -0
- package/dist/packlets/safer-fetch/addressPolicy.js +119 -0
- package/dist/packlets/safer-fetch/addressPolicy.js.map +1 -0
- package/dist/packlets/safer-fetch/contentType.js +120 -0
- package/dist/packlets/safer-fetch/contentType.js.map +1 -0
- package/dist/packlets/safer-fetch/deadline.js +254 -0
- package/dist/packlets/safer-fetch/deadline.js.map +1 -0
- package/dist/packlets/safer-fetch/defaults.js +131 -0
- package/dist/packlets/safer-fetch/defaults.js.map +1 -0
- package/dist/packlets/safer-fetch/failureReason.js +21 -0
- package/dist/packlets/safer-fetch/failureReason.js.map +1 -0
- package/dist/packlets/safer-fetch/guards.js +148 -0
- package/dist/packlets/safer-fetch/guards.js.map +1 -0
- package/dist/packlets/safer-fetch/index.browser.js +58 -0
- package/dist/packlets/safer-fetch/index.browser.js.map +1 -0
- package/dist/packlets/safer-fetch/index.js +52 -0
- package/dist/packlets/safer-fetch/index.js.map +1 -0
- package/dist/packlets/safer-fetch/model.js +21 -0
- package/dist/packlets/safer-fetch/model.js.map +1 -0
- package/dist/packlets/safer-fetch/nodeAddressGuard.js +191 -0
- package/dist/packlets/safer-fetch/nodeAddressGuard.js.map +1 -0
- package/dist/packlets/safer-fetch/redirect.js +93 -0
- package/dist/packlets/safer-fetch/redirect.js.map +1 -0
- package/dist/packlets/safer-fetch/retry.js +188 -0
- package/dist/packlets/safer-fetch/retry.js.map +1 -0
- package/dist/packlets/safer-fetch/saferFetch.js +755 -0
- package/dist/packlets/safer-fetch/saferFetch.js.map +1 -0
- package/dist/packlets/safer-fetch/transport.js +50 -0
- package/dist/packlets/safer-fetch/transport.js.map +1 -0
- package/dist/packlets/yaml/converters.js.map +1 -0
- package/dist/packlets/yaml/index.js +1 -0
- package/dist/packlets/yaml/index.js.map +1 -0
- package/dist/packlets/yaml/serializers.js +48 -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 +99 -9
- package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -0
- package/dist/packlets/zip-file-tree/zipFileTreeWriter.js +16 -1
- package/dist/packlets/zip-file-tree/zipFileTreeWriter.js.map +1 -0
- package/dist/ts-extras.d.ts +6648 -553
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/index.browser.d.ts +5 -2
- package/lib/index.browser.d.ts.map +1 -0
- package/lib/index.browser.js +11 -3
- package/lib/index.browser.js.map +1 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +149 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts.map +1 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.js +293 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -0
- package/lib/packlets/ai-assist/completionClient.d.ts +74 -0
- package/lib/packlets/ai-assist/completionClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/{apiClient.js → completionClient.js} +164 -213
- package/lib/packlets/ai-assist/completionClient.js.map +1 -0
- package/lib/packlets/ai-assist/converters.d.ts +15 -1
- package/lib/packlets/ai-assist/converters.d.ts.map +1 -0
- package/lib/packlets/ai-assist/converters.js +47 -3
- package/lib/packlets/ai-assist/converters.js.map +1 -0
- package/lib/packlets/ai-assist/embeddingClient.d.ts +69 -0
- package/lib/packlets/ai-assist/embeddingClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/embeddingClient.js +349 -0
- package/lib/packlets/ai-assist/embeddingClient.js.map +1 -0
- package/lib/packlets/ai-assist/endpoint.d.ts +49 -0
- package/lib/packlets/ai-assist/endpoint.d.ts.map +1 -0
- package/lib/packlets/ai-assist/endpoint.js +113 -0
- package/lib/packlets/ai-assist/endpoint.js.map +1 -0
- package/lib/packlets/ai-assist/http.d.ts +34 -0
- package/lib/packlets/ai-assist/http.d.ts.map +1 -0
- package/lib/packlets/ai-assist/http.js +109 -0
- package/lib/packlets/ai-assist/http.js.map +1 -0
- package/lib/packlets/ai-assist/imageGenerationClient.d.ts +46 -0
- package/lib/packlets/ai-assist/imageGenerationClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/imageGenerationClient.js +458 -0
- package/lib/packlets/ai-assist/imageGenerationClient.js.map +1 -0
- package/lib/packlets/ai-assist/imageOptionsResolver.d.ts +66 -0
- package/lib/packlets/ai-assist/imageOptionsResolver.d.ts.map +1 -0
- package/lib/packlets/ai-assist/imageOptionsResolver.js +197 -0
- package/lib/packlets/ai-assist/imageOptionsResolver.js.map +1 -0
- package/lib/packlets/ai-assist/index.d.ts +12 -4
- package/lib/packlets/ai-assist/index.d.ts.map +1 -0
- package/lib/packlets/ai-assist/index.js +47 -4
- package/lib/packlets/ai-assist/index.js.map +1 -0
- package/lib/packlets/ai-assist/jsonCompletion.d.ts +93 -0
- package/lib/packlets/ai-assist/jsonCompletion.d.ts.map +1 -0
- package/lib/packlets/ai-assist/jsonCompletion.js +97 -0
- package/lib/packlets/ai-assist/jsonCompletion.js.map +1 -0
- package/lib/packlets/ai-assist/jsonResponse.d.ts +194 -0
- package/lib/packlets/ai-assist/jsonResponse.d.ts.map +1 -0
- package/lib/packlets/ai-assist/jsonResponse.js +429 -0
- package/lib/packlets/ai-assist/jsonResponse.js.map +1 -0
- package/lib/packlets/ai-assist/listModelsClient.d.ts +46 -0
- package/lib/packlets/ai-assist/listModelsClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/listModelsClient.js +298 -0
- package/lib/packlets/ai-assist/listModelsClient.js.map +1 -0
- package/lib/packlets/ai-assist/model.d.ts +1346 -12
- package/lib/packlets/ai-assist/model.d.ts.map +1 -0
- package/lib/packlets/ai-assist/model.js +309 -10
- package/lib/packlets/ai-assist/model.js.map +1 -0
- package/lib/packlets/ai-assist/registry.d.ts +76 -1
- package/lib/packlets/ai-assist/registry.d.ts.map +1 -0
- package/lib/packlets/ai-assist/registry.js +381 -13
- package/lib/packlets/ai-assist/registry.js.map +1 -0
- package/lib/packlets/ai-assist/sseParser.d.ts +45 -0
- package/lib/packlets/ai-assist/sseParser.d.ts.map +1 -0
- package/lib/packlets/ai-assist/sseParser.js +128 -0
- package/lib/packlets/ai-assist/sseParser.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +72 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +379 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +269 -0
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +694 -0
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +163 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.js +193 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +43 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js +211 -0
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +19 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +178 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +33 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +393 -0
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts +34 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js +181 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -0
- package/lib/packlets/ai-assist/streamingClient.d.ts +50 -0
- package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingClient.js +146 -0
- package/lib/packlets/ai-assist/streamingClient.js.map +1 -0
- package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts +87 -0
- package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts.map +1 -0
- package/lib/packlets/ai-assist/thinkingOptionsResolver.js +294 -0
- package/lib/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -0
- package/lib/packlets/ai-assist/toolFormats.d.ts +40 -9
- package/lib/packlets/ai-assist/toolFormats.d.ts.map +1 -0
- package/lib/packlets/ai-assist/toolFormats.js +107 -10
- package/lib/packlets/ai-assist/toolFormats.js.map +1 -0
- package/lib/packlets/conversion/converters.d.ts +8 -1
- package/lib/packlets/conversion/converters.d.ts.map +1 -0
- package/lib/packlets/conversion/converters.js +36 -2
- 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 +20 -1
- package/lib/packlets/crypto-utils/converters.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/converters.js +43 -5
- 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 +25 -0
- package/lib/packlets/crypto-utils/encryptedFile.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/encryptedFile.js +38 -0
- package/lib/packlets/crypto-utils/encryptedFile.js.map +1 -0
- package/lib/packlets/crypto-utils/hpkeProvider.d.ts +150 -0
- package/lib/packlets/crypto-utils/hpkeProvider.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/hpkeProvider.js +359 -0
- package/lib/packlets/crypto-utils/hpkeProvider.js.map +1 -0
- package/lib/packlets/crypto-utils/index.browser.d.ts +5 -1
- package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/index.browser.js +28 -3
- package/lib/packlets/crypto-utils/index.browser.js.map +1 -0
- package/lib/packlets/crypto-utils/index.d.ts +5 -1
- package/lib/packlets/crypto-utils/index.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/index.js +24 -1
- package/lib/packlets/crypto-utils/index.js.map +1 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts +54 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js +74 -0
- package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/converters.d.ts +68 -6
- package/lib/packlets/crypto-utils/keystore/converters.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/converters.js +109 -14
- package/lib/packlets/crypto-utils/keystore/converters.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.d.ts +148 -0
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js +324 -0
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.d.ts +10 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.js +76 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.d.ts +2 -0
- package/lib/packlets/crypto-utils/keystore/index.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.js +5 -1
- package/lib/packlets/crypto-utils/keystore/index.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +314 -13
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/keyStore.js +877 -124
- package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/model.d.ts +404 -21
- package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/model.js +37 -5
- package/lib/packlets/crypto-utils/keystore/model.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts +53 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js +22 -0
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -0
- package/lib/packlets/crypto-utils/model.d.ts +518 -10
- package/lib/packlets/crypto-utils/model.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/model.js +33 -1
- package/lib/packlets/crypto-utils/model.js.map +1 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +142 -2
- package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.js +346 -0
- package/lib/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -0
- package/lib/packlets/crypto-utils/seedDerivedKeyPair.d.ts +42 -0
- package/lib/packlets/crypto-utils/seedDerivedKeyPair.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/seedDerivedKeyPair.js +123 -0
- package/lib/packlets/crypto-utils/seedDerivedKeyPair.js.map +1 -0
- package/lib/packlets/crypto-utils/spkiHelpers.d.ts +151 -0
- package/lib/packlets/crypto-utils/spkiHelpers.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/spkiHelpers.js +297 -0
- package/lib/packlets/crypto-utils/spkiHelpers.js.map +1 -0
- package/lib/packlets/csv/csvFileHelpers.d.ts +0 -10
- package/lib/packlets/csv/csvFileHelpers.d.ts.map +1 -0
- package/lib/packlets/csv/csvFileHelpers.js +0 -15
- package/lib/packlets/csv/csvFileHelpers.js.map +1 -0
- package/lib/packlets/csv/csvHelpers.d.ts +10 -0
- package/lib/packlets/csv/csvHelpers.d.ts.map +1 -0
- package/lib/packlets/csv/csvHelpers.js +15 -0
- package/lib/packlets/csv/csvHelpers.js.map +1 -0
- package/lib/packlets/csv/index.browser.d.ts +0 -1
- package/lib/packlets/csv/index.browser.d.ts.map +1 -0
- package/lib/packlets/csv/index.browser.js +1 -5
- 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 +1 -1
- 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 +34 -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 +2 -0
- package/lib/packlets/mustache/mustacheTemplate.d.ts.map +1 -0
- package/lib/packlets/mustache/mustacheTemplate.js +42 -4
- package/lib/packlets/mustache/mustacheTemplate.js.map +1 -0
- package/lib/packlets/record-jar/index.browser.d.ts +0 -1
- package/lib/packlets/record-jar/index.browser.d.ts.map +1 -0
- package/lib/packlets/record-jar/index.browser.js +1 -5
- 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 +0 -11
- package/lib/packlets/record-jar/recordJarFileHelpers.d.ts.map +1 -0
- package/lib/packlets/record-jar/recordJarFileHelpers.js +0 -19
- package/lib/packlets/record-jar/recordJarFileHelpers.js.map +1 -0
- package/lib/packlets/record-jar/recordJarHelpers.d.ts +11 -0
- package/lib/packlets/record-jar/recordJarHelpers.d.ts.map +1 -0
- package/lib/packlets/record-jar/recordJarHelpers.js +19 -0
- package/lib/packlets/record-jar/recordJarHelpers.js.map +1 -0
- package/lib/packlets/safer-fetch/addressClassification.d.ts +144 -0
- package/lib/packlets/safer-fetch/addressClassification.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/addressClassification.js +441 -0
- package/lib/packlets/safer-fetch/addressClassification.js.map +1 -0
- package/lib/packlets/safer-fetch/addressPolicy.d.ts +113 -0
- package/lib/packlets/safer-fetch/addressPolicy.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/addressPolicy.js +123 -0
- package/lib/packlets/safer-fetch/addressPolicy.js.map +1 -0
- package/lib/packlets/safer-fetch/contentType.d.ts +51 -0
- package/lib/packlets/safer-fetch/contentType.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/contentType.js +127 -0
- package/lib/packlets/safer-fetch/contentType.js.map +1 -0
- package/lib/packlets/safer-fetch/deadline.d.ts +138 -0
- package/lib/packlets/safer-fetch/deadline.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/deadline.js +258 -0
- package/lib/packlets/safer-fetch/deadline.js.map +1 -0
- package/lib/packlets/safer-fetch/defaults.d.ts +108 -0
- package/lib/packlets/safer-fetch/defaults.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/defaults.js +134 -0
- package/lib/packlets/safer-fetch/defaults.js.map +1 -0
- package/lib/packlets/safer-fetch/failureReason.d.ts +148 -0
- package/lib/packlets/safer-fetch/failureReason.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/failureReason.js +22 -0
- package/lib/packlets/safer-fetch/failureReason.js.map +1 -0
- package/lib/packlets/safer-fetch/guards.d.ts +62 -0
- package/lib/packlets/safer-fetch/guards.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/guards.js +153 -0
- package/lib/packlets/safer-fetch/guards.js.map +1 -0
- package/lib/packlets/safer-fetch/index.browser.d.ts +33 -0
- package/lib/packlets/safer-fetch/index.browser.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/index.browser.js +82 -0
- package/lib/packlets/safer-fetch/index.browser.js.map +1 -0
- package/lib/packlets/safer-fetch/index.d.ts +27 -0
- package/lib/packlets/safer-fetch/index.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/index.js +78 -0
- package/lib/packlets/safer-fetch/index.js.map +1 -0
- package/lib/packlets/safer-fetch/model.d.ts +373 -0
- package/lib/packlets/safer-fetch/model.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/model.js +22 -0
- package/lib/packlets/safer-fetch/model.js.map +1 -0
- package/lib/packlets/safer-fetch/nodeAddressGuard.d.ts +129 -0
- package/lib/packlets/safer-fetch/nodeAddressGuard.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/nodeAddressGuard.js +196 -0
- package/lib/packlets/safer-fetch/nodeAddressGuard.js.map +1 -0
- package/lib/packlets/safer-fetch/redirect.d.ts +62 -0
- package/lib/packlets/safer-fetch/redirect.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/redirect.js +98 -0
- package/lib/packlets/safer-fetch/redirect.js.map +1 -0
- package/lib/packlets/safer-fetch/retry.d.ts +162 -0
- package/lib/packlets/safer-fetch/retry.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/retry.js +197 -0
- package/lib/packlets/safer-fetch/retry.js.map +1 -0
- package/lib/packlets/safer-fetch/saferFetch.d.ts +108 -0
- package/lib/packlets/safer-fetch/saferFetch.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/saferFetch.js +760 -0
- package/lib/packlets/safer-fetch/saferFetch.js.map +1 -0
- package/lib/packlets/safer-fetch/transport.d.ts +18 -0
- package/lib/packlets/safer-fetch/transport.d.ts.map +1 -0
- package/lib/packlets/safer-fetch/transport.js +53 -0
- package/lib/packlets/safer-fetch/transport.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 +1 -0
- package/lib/packlets/yaml/index.d.ts.map +1 -0
- package/lib/packlets/yaml/index.js +1 -0
- package/lib/packlets/yaml/index.js.map +1 -0
- package/lib/packlets/yaml/serializers.d.ts +45 -0
- package/lib/packlets/yaml/serializers.d.ts.map +1 -0
- package/lib/packlets/yaml/serializers.js +84 -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 +84 -8
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js +99 -9
- package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeWriter.d.ts +26 -1
- package/lib/packlets/zip-file-tree/zipFileTreeWriter.d.ts.map +1 -0
- package/lib/packlets/zip-file-tree/zipFileTreeWriter.js +17 -1
- package/lib/packlets/zip-file-tree/zipFileTreeWriter.js.map +1 -0
- package/package.json +30 -15
- package/lib/packlets/ai-assist/apiClient.d.ts +0 -60
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2026 Erik Fortune
|
|
3
|
+
//
|
|
4
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
// in the Software without restriction, including without limitation the rights
|
|
7
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
// furnished to do so, subject to the following conditions:
|
|
10
|
+
//
|
|
11
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
// copies or substantial portions of the Software.
|
|
13
|
+
//
|
|
14
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
// SOFTWARE.
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.IDEMPOTENT_METHODS = exports.RETRY_AFTER_STATUSES = exports.RETRYABLE_HTTP_STATUSES = exports.DEFAULT_RETRY_MAX_DELAY_MS = exports.DEFAULT_RETRY_BASE_DELAY_MS = exports.SUPPORTED_SCHEMES = exports.ALWAYS_STRIPPED_HEADERS = exports.DEFAULT_MAX_REDIRECTS = exports.REDIRECT_STATUSES = exports.DEFAULT_MAX_RESPONSE_BYTES = exports.DEFAULT_HEADERS_TIMEOUT_MS = exports.DEFAULT_TIMEOUT_MS = void 0;
|
|
23
|
+
/**
|
|
24
|
+
* Default overall deadline, in milliseconds — comfortably above a slow-but-real API and far
|
|
25
|
+
* below "a hung request pins a socket for minutes". Streaming LLM calls should not use this
|
|
26
|
+
* primitive at all; that is what `AiAssist` is for.
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
exports.DEFAULT_TIMEOUT_MS = 30000;
|
|
30
|
+
/**
|
|
31
|
+
* Default deadline to first response headers, in milliseconds. A host that has not sent
|
|
32
|
+
* headers in ten seconds is not answering.
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
35
|
+
exports.DEFAULT_HEADERS_TIMEOUT_MS = 10000;
|
|
36
|
+
/**
|
|
37
|
+
* Default cap on decoded response bytes (5 MiB) — roughly an order of magnitude above a
|
|
38
|
+
* realistic JSON API response and well below a size that threatens a Node heap.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* Raising it is a per-call option, not a construction-time setting, precisely because real
|
|
42
|
+
* documents clear this. See `ISaferFetchOptions.maxResponseBytes`.
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
exports.DEFAULT_MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
46
|
+
/**
|
|
47
|
+
* Redirect statuses this primitive recognizes as redirects. Other 3xx statuses (`300`, `304`,
|
|
48
|
+
* …) do not direct the client at a new URL and are reported as ordinary non-2xx responses.
|
|
49
|
+
* @public
|
|
50
|
+
*/
|
|
51
|
+
exports.REDIRECT_STATUSES = [301, 302, 303, 307, 308];
|
|
52
|
+
/**
|
|
53
|
+
* Default cap on redirect hops followed under `'validate-each-hop'`.
|
|
54
|
+
*
|
|
55
|
+
* @remarks
|
|
56
|
+
* Five is enough for the ordinary shortener-then-canonicalize-then-CDN chains real services
|
|
57
|
+
* produce and small enough that a chain designed to burn budget is refused quickly. Every hop
|
|
58
|
+
* costs a full guard evaluation — including a DNS resolution — against the same overall
|
|
59
|
+
* deadline, so the cap bounds work, not just politeness.
|
|
60
|
+
* @public
|
|
61
|
+
*/
|
|
62
|
+
exports.DEFAULT_MAX_REDIRECTS = 5;
|
|
63
|
+
/**
|
|
64
|
+
* Header names dropped on every cross-origin redirect hop, whatever the caller configured.
|
|
65
|
+
*
|
|
66
|
+
* @remarks
|
|
67
|
+
* Turning on manual redirects makes this primitive responsible for a rule the platform was
|
|
68
|
+
* applying for free: browsers and `curl` both strip credential headers when a redirect leaves
|
|
69
|
+
* the origin, and a hand-rolled loop that replays them hands `Authorization: Bearer …` to
|
|
70
|
+
* whatever host the redirect names. Callers add their own names with
|
|
71
|
+
* `ISaferFetchOptions.sensitiveHeaders`; these three are not removable.
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
74
|
+
exports.ALWAYS_STRIPPED_HEADERS = [
|
|
75
|
+
'authorization',
|
|
76
|
+
'cookie',
|
|
77
|
+
'proxy-authorization'
|
|
78
|
+
];
|
|
79
|
+
/**
|
|
80
|
+
* URL schemes this primitive will ever request.
|
|
81
|
+
*
|
|
82
|
+
* @remarks
|
|
83
|
+
* Core rejects everything else — `file:`, `data:`, `blob:`, `ftp:`, `gopher:`, `ws:` — outright
|
|
84
|
+
* and at every hop, because none of them is a legitimate network fetch and each is a standard
|
|
85
|
+
* SSRF payload. Deciding between `http:` and `https:`, and which ports are acceptable, belongs
|
|
86
|
+
* to the address guard, which is why core does not narrow this further.
|
|
87
|
+
* @public
|
|
88
|
+
*/
|
|
89
|
+
exports.SUPPORTED_SCHEMES = ['http:', 'https:'];
|
|
90
|
+
/**
|
|
91
|
+
* Default base delay for {@link SaferFetch.IRetryPolicy | retry} backoff, in milliseconds.
|
|
92
|
+
* @public
|
|
93
|
+
*/
|
|
94
|
+
exports.DEFAULT_RETRY_BASE_DELAY_MS = 250;
|
|
95
|
+
/**
|
|
96
|
+
* Default ceiling on a single {@link SaferFetch.IRetryPolicy | retry} delay, in milliseconds.
|
|
97
|
+
*
|
|
98
|
+
* @remarks
|
|
99
|
+
* It bounds the computed backoff **and** a server-supplied `Retry-After`. That second role is
|
|
100
|
+
* the security-relevant one: `Retry-After` is a header an attacker-controlled server chooses
|
|
101
|
+
* freely, so an unclamped `Retry-After: 86400` is a denial of service on the caller.
|
|
102
|
+
* @public
|
|
103
|
+
*/
|
|
104
|
+
exports.DEFAULT_RETRY_MAX_DELAY_MS = 5000;
|
|
105
|
+
/**
|
|
106
|
+
* HTTP statuses a {@link SaferFetch.IRetryPolicy | retry policy} will retry.
|
|
107
|
+
*
|
|
108
|
+
* @remarks
|
|
109
|
+
* Every other status — including every other 4xx — is terminal. A `401`, a `403` and a `404`
|
|
110
|
+
* mean the same thing on the second attempt as on the first, and retrying them turns a client
|
|
111
|
+
* bug into load against a service that already said no.
|
|
112
|
+
* @public
|
|
113
|
+
*/
|
|
114
|
+
exports.RETRYABLE_HTTP_STATUSES = [408, 429, 500, 502, 503, 504];
|
|
115
|
+
/**
|
|
116
|
+
* Statuses whose `Retry-After` header is honored.
|
|
117
|
+
*
|
|
118
|
+
* @remarks
|
|
119
|
+
* `429` and `503` are the two the header is specified for and the two where it carries real
|
|
120
|
+
* scheduling information. Honoring it on a `500` would let any failing endpoint dictate the
|
|
121
|
+
* caller's schedule for no benefit.
|
|
122
|
+
* @public
|
|
123
|
+
*/
|
|
124
|
+
exports.RETRY_AFTER_STATUSES = [429, 503];
|
|
125
|
+
/**
|
|
126
|
+
* Methods retried without an explicit opt-in.
|
|
127
|
+
*
|
|
128
|
+
* @remarks
|
|
129
|
+
* A timeout does not tell you whether the server processed the request, so a retried `POST`
|
|
130
|
+
* can double-charge. `retryNonIdempotent` opts out, per call, visibly.
|
|
131
|
+
* @public
|
|
132
|
+
*/
|
|
133
|
+
exports.IDEMPOTENT_METHODS = ['GET', 'HEAD'];
|
|
134
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../../src/packlets/safer-fetch/defaults.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;;;;;GAKG;AACU,QAAA,kBAAkB,GAAW,KAAM,CAAC;AAEjD;;;;GAIG;AACU,QAAA,0BAA0B,GAAW,KAAM,CAAC;AAEzD;;;;;;;;GAQG;AACU,QAAA,0BAA0B,GAAW,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAElE;;;;GAIG;AACU,QAAA,iBAAiB,GAA0B,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAElF;;;;;;;;;GASG;AACU,QAAA,qBAAqB,GAAW,CAAC,CAAC;AAE/C;;;;;;;;;;GAUG;AACU,QAAA,uBAAuB,GAA0B;IAC5D,eAAe;IACf,QAAQ;IACR,qBAAqB;CACtB,CAAC;AAEF;;;;;;;;;GASG;AACU,QAAA,iBAAiB,GAA0B,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAE5E;;;GAGG;AACU,QAAA,2BAA2B,GAAW,GAAG,CAAC;AAEvD;;;;;;;;GAQG;AACU,QAAA,0BAA0B,GAAW,IAAK,CAAC;AAExD;;;;;;;;GAQG;AACU,QAAA,uBAAuB,GAA0B,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAE7F;;;;;;;;GAQG;AACU,QAAA,oBAAoB,GAA0B,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAEtE;;;;;;;GAOG;AACU,QAAA,kBAAkB,GAA0B,CAAC,KAAK,EAAE,MAAM,CAAC,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 * Default overall deadline, in milliseconds — comfortably above a slow-but-real API and far\n * below \"a hung request pins a socket for minutes\". Streaming LLM calls should not use this\n * primitive at all; that is what `AiAssist` is for.\n * @public\n */\nexport const DEFAULT_TIMEOUT_MS: number = 30_000;\n\n/**\n * Default deadline to first response headers, in milliseconds. A host that has not sent\n * headers in ten seconds is not answering.\n * @public\n */\nexport const DEFAULT_HEADERS_TIMEOUT_MS: number = 10_000;\n\n/**\n * Default cap on decoded response bytes (5 MiB) — roughly an order of magnitude above a\n * realistic JSON API response and well below a size that threatens a Node heap.\n *\n * @remarks\n * Raising it is a per-call option, not a construction-time setting, precisely because real\n * documents clear this. See `ISaferFetchOptions.maxResponseBytes`.\n * @public\n */\nexport const DEFAULT_MAX_RESPONSE_BYTES: number = 5 * 1024 * 1024;\n\n/**\n * Redirect statuses this primitive recognizes as redirects. Other 3xx statuses (`300`, `304`,\n * …) do not direct the client at a new URL and are reported as ordinary non-2xx responses.\n * @public\n */\nexport const REDIRECT_STATUSES: ReadonlyArray<number> = [301, 302, 303, 307, 308];\n\n/**\n * Default cap on redirect hops followed under `'validate-each-hop'`.\n *\n * @remarks\n * Five is enough for the ordinary shortener-then-canonicalize-then-CDN chains real services\n * produce and small enough that a chain designed to burn budget is refused quickly. Every hop\n * costs a full guard evaluation — including a DNS resolution — against the same overall\n * deadline, so the cap bounds work, not just politeness.\n * @public\n */\nexport const DEFAULT_MAX_REDIRECTS: number = 5;\n\n/**\n * Header names dropped on every cross-origin redirect hop, whatever the caller configured.\n *\n * @remarks\n * Turning on manual redirects makes this primitive responsible for a rule the platform was\n * applying for free: browsers and `curl` both strip credential headers when a redirect leaves\n * the origin, and a hand-rolled loop that replays them hands `Authorization: Bearer …` to\n * whatever host the redirect names. Callers add their own names with\n * `ISaferFetchOptions.sensitiveHeaders`; these three are not removable.\n * @public\n */\nexport const ALWAYS_STRIPPED_HEADERS: ReadonlyArray<string> = [\n 'authorization',\n 'cookie',\n 'proxy-authorization'\n];\n\n/**\n * URL schemes this primitive will ever request.\n *\n * @remarks\n * Core rejects everything else — `file:`, `data:`, `blob:`, `ftp:`, `gopher:`, `ws:` — outright\n * and at every hop, because none of them is a legitimate network fetch and each is a standard\n * SSRF payload. Deciding between `http:` and `https:`, and which ports are acceptable, belongs\n * to the address guard, which is why core does not narrow this further.\n * @public\n */\nexport const SUPPORTED_SCHEMES: ReadonlyArray<string> = ['http:', 'https:'];\n\n/**\n * Default base delay for {@link SaferFetch.IRetryPolicy | retry} backoff, in milliseconds.\n * @public\n */\nexport const DEFAULT_RETRY_BASE_DELAY_MS: number = 250;\n\n/**\n * Default ceiling on a single {@link SaferFetch.IRetryPolicy | retry} delay, in milliseconds.\n *\n * @remarks\n * It bounds the computed backoff **and** a server-supplied `Retry-After`. That second role is\n * the security-relevant one: `Retry-After` is a header an attacker-controlled server chooses\n * freely, so an unclamped `Retry-After: 86400` is a denial of service on the caller.\n * @public\n */\nexport const DEFAULT_RETRY_MAX_DELAY_MS: number = 5_000;\n\n/**\n * HTTP statuses a {@link SaferFetch.IRetryPolicy | retry policy} will retry.\n *\n * @remarks\n * Every other status — including every other 4xx — is terminal. A `401`, a `403` and a `404`\n * mean the same thing on the second attempt as on the first, and retrying them turns a client\n * bug into load against a service that already said no.\n * @public\n */\nexport const RETRYABLE_HTTP_STATUSES: ReadonlyArray<number> = [408, 429, 500, 502, 503, 504];\n\n/**\n * Statuses whose `Retry-After` header is honored.\n *\n * @remarks\n * `429` and `503` are the two the header is specified for and the two where it carries real\n * scheduling information. Honoring it on a `500` would let any failing endpoint dictate the\n * caller's schedule for no benefit.\n * @public\n */\nexport const RETRY_AFTER_STATUSES: ReadonlyArray<number> = [429, 503];\n\n/**\n * Methods retried without an explicit opt-in.\n *\n * @remarks\n * A timeout does not tell you whether the server processed the request, so a retried `POST`\n * can double-charge. `retryNonIdempotent` opts out, per call, visibly.\n * @public\n */\nexport const IDEMPOTENT_METHODS: ReadonlyArray<string> = ['GET', 'HEAD'];\n"]}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which deadline a {@link SaferFetch.FetchFailureReason | timeout failure} belongs to.
|
|
3
|
+
*
|
|
4
|
+
* - `'headers'`: the per-attempt headers deadline elapsed before the server sent
|
|
5
|
+
* response headers — the host is not answering.
|
|
6
|
+
* - `'body'`: the overall deadline elapsed while the response body was being read
|
|
7
|
+
* — the host answered and then trickled (the slowloris shape).
|
|
8
|
+
* - `'overall'`: the overall deadline elapsed before response headers arrived.
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export type FetchTimeoutPhase = 'headers' | 'body' | 'overall';
|
|
12
|
+
/**
|
|
13
|
+
* Machine-readable reason a safer-fetch call failed, carried as the detail of the
|
|
14
|
+
* `DetailedResult` every entry point returns.
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* **This value is an internal-network scanning oracle. Do not echo it, or any string
|
|
18
|
+
* derived from it, to an untrusted caller.** A `'blocked-by-guard'` detail names the
|
|
19
|
+
* URL, the hop, and the guard that rejected it; an attacker who can influence the
|
|
20
|
+
* request URL and observe the error learns whether an internal host exists. Log the
|
|
21
|
+
* detail; return a coarse code (the `kind` alone, or a generic message) to callers.
|
|
22
|
+
*
|
|
23
|
+
* Following the `AiAssist.JsonParseFailureReason` precedent in this package: a `kind`
|
|
24
|
+
* discriminant with per-kind payload fields, and `'unknown'` as an honest catch-all
|
|
25
|
+
* rather than a guess.
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export type FetchFailureReason =
|
|
29
|
+
/** The supplied URL could not be parsed, or its scheme is not `http:` or `https:`. */
|
|
30
|
+
{
|
|
31
|
+
readonly kind: 'invalid-url';
|
|
32
|
+
readonly url: string;
|
|
33
|
+
readonly detail: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A guard refused. `hop` is 0-based; hop 0 is the caller's URL, and `guard` names which
|
|
37
|
+
* guard rejected — which is what makes "did the address check run, and was it the thing that
|
|
38
|
+
* said no?" answerable from the failure alone. A content-type rejection is reported as
|
|
39
|
+
* `'unsupported-content-type'` instead, because that case carries a more useful payload.
|
|
40
|
+
*/
|
|
41
|
+
| {
|
|
42
|
+
readonly kind: 'blocked-by-guard';
|
|
43
|
+
readonly url: string;
|
|
44
|
+
readonly hop: number;
|
|
45
|
+
readonly guard: string;
|
|
46
|
+
readonly detail: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A redirect status was received that this call would not follow: the policy is `'reject'`,
|
|
50
|
+
* or the response carried no usable `Location`, or following it would revisit a URL already
|
|
51
|
+
* in the chain.
|
|
52
|
+
*
|
|
53
|
+
* @remarks
|
|
54
|
+
* The three are deliberately one kind. Splitting them would buy a caller almost nothing and
|
|
55
|
+
* would widen the scanning oracle this taxonomy already is — and the two follow-time cases
|
|
56
|
+
* are facts about the chain the redirecting server produced, not about the network behind
|
|
57
|
+
* this process. "The chain got too long" is a different question and stays
|
|
58
|
+
* `'too-many-redirects'`.
|
|
59
|
+
*
|
|
60
|
+
* `url` is always the URL that **issued** the rejected redirect — the hop this call actually
|
|
61
|
+
* requested and got a 3xx back from — never the `Location` target it pointed at. That holds
|
|
62
|
+
* for all three cases, including the revisit case, where the target is the URL already in the
|
|
63
|
+
* chain and naming it here would make the same field mean two different things. The target is
|
|
64
|
+
* named in the message instead. `status` is the redirect status that was received.
|
|
65
|
+
*/
|
|
66
|
+
| {
|
|
67
|
+
readonly kind: 'redirect-rejected';
|
|
68
|
+
readonly url: string;
|
|
69
|
+
readonly status: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The platform returned an opaque redirect, whose `Location` is not readable. This is
|
|
73
|
+
* what a browser yields for `redirect: 'manual'`; the hop cannot be inspected at all.
|
|
74
|
+
*/
|
|
75
|
+
| {
|
|
76
|
+
readonly kind: 'redirect-opaque';
|
|
77
|
+
}
|
|
78
|
+
/** The redirect hop budget was exhausted. */
|
|
79
|
+
| {
|
|
80
|
+
readonly kind: 'too-many-redirects';
|
|
81
|
+
readonly hops: number;
|
|
82
|
+
readonly limit: number;
|
|
83
|
+
}
|
|
84
|
+
/** One of the deadlines elapsed. Distinct from `'aborted'`, which is the caller's signal. */
|
|
85
|
+
| {
|
|
86
|
+
readonly kind: 'timeout';
|
|
87
|
+
readonly phase: FetchTimeoutPhase;
|
|
88
|
+
readonly elapsedMs: number;
|
|
89
|
+
readonly limitMs: number;
|
|
90
|
+
}
|
|
91
|
+
/** The caller's `AbortSignal` fired. Distinct from `'timeout'`, which is our deadline. */
|
|
92
|
+
| {
|
|
93
|
+
readonly kind: 'aborted';
|
|
94
|
+
}
|
|
95
|
+
/** The transport could not complete the request. */
|
|
96
|
+
| {
|
|
97
|
+
readonly kind: 'network';
|
|
98
|
+
readonly detail: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* A non-2xx response. `bodyPreview` is **never populated in this release** — error bodies
|
|
102
|
+
* routinely echo request content, including credentials, so surfacing one has to be an
|
|
103
|
+
* explicit, length-capped opt-in rather than a default. The field is declared so that adding
|
|
104
|
+
* that opt-in later is additive.
|
|
105
|
+
*/
|
|
106
|
+
| {
|
|
107
|
+
readonly kind: 'http-status';
|
|
108
|
+
readonly status: number;
|
|
109
|
+
readonly statusText: string;
|
|
110
|
+
readonly bodyPreview?: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The response exceeded the byte cap. `declared` is what `Content-Length` claimed, present
|
|
114
|
+
* only when the header was sent — and it counts *encoded* bytes where `bytesRead` counts
|
|
115
|
+
* *decoded* bytes, so a `declared` far below `bytesRead` is evidence of a compression bomb
|
|
116
|
+
* or a lying server rather than an arithmetic error.
|
|
117
|
+
*/
|
|
118
|
+
| {
|
|
119
|
+
readonly kind: 'too-large';
|
|
120
|
+
readonly bytesRead: number;
|
|
121
|
+
readonly limit: number;
|
|
122
|
+
readonly declared?: number;
|
|
123
|
+
}
|
|
124
|
+
/** A response-headers guard rejected the response's content type. */
|
|
125
|
+
| {
|
|
126
|
+
readonly kind: 'unsupported-content-type';
|
|
127
|
+
readonly contentType?: string;
|
|
128
|
+
readonly accepted: ReadonlyArray<string>;
|
|
129
|
+
}
|
|
130
|
+
/** The response bytes could not be decoded to text with the indicated charset. */
|
|
131
|
+
| {
|
|
132
|
+
readonly kind: 'decode';
|
|
133
|
+
readonly detail: string;
|
|
134
|
+
}
|
|
135
|
+
/** The decoded text could not be parsed, or failed the caller's converter. */
|
|
136
|
+
| {
|
|
137
|
+
readonly kind: 'parse';
|
|
138
|
+
readonly detail: string;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Anything else — including invalid options and a guard or transport that violated its
|
|
142
|
+
* contract. Reports what it knows rather than guessing at a more specific kind.
|
|
143
|
+
*/
|
|
144
|
+
| {
|
|
145
|
+
readonly kind: 'unknown';
|
|
146
|
+
readonly detail: string;
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=failureReason.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failureReason.d.ts","sourceRoot":"","sources":["../../../src/packlets/safer-fetch/failureReason.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,kBAAkB;AAC5B,sFAAsF;AACpF;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACjF;;;;;GAKG;GACD;IACE,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AACH;;;;;;;;;;;;;;;;;GAiBG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACvF;;;GAGG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE;AACtC,6CAA6C;GAC3C;IAAE,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AACxF,6FAA6F;GAC3F;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AACH,0FAA0F;GACxF;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE;AAC9B,oDAAoD;GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACvD;;;;;GAKG;GACD;IACE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AACH;;;;;GAKG;GACD;IACE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AACH,qEAAqE;GACnE;IACE,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC1C;AACH,kFAAkF;GAChF;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACtD,8EAA8E;GAC5E;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACrD;;;GAGG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2026 Erik Fortune
|
|
3
|
+
//
|
|
4
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
// in the Software without restriction, including without limitation the rights
|
|
7
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
// furnished to do so, subject to the following conditions:
|
|
10
|
+
//
|
|
11
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
// copies or substantial portions of the Software.
|
|
13
|
+
//
|
|
14
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
// SOFTWARE.
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
//# sourceMappingURL=failureReason.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failureReason.js","sourceRoot":"","sources":["../../../src/packlets/safer-fetch/failureReason.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\n/**\n * Which deadline a {@link SaferFetch.FetchFailureReason | timeout failure} belongs to.\n *\n * - `'headers'`: the per-attempt headers deadline elapsed before the server sent\n * response headers — the host is not answering.\n * - `'body'`: the overall deadline elapsed while the response body was being read\n * — the host answered and then trickled (the slowloris shape).\n * - `'overall'`: the overall deadline elapsed before response headers arrived.\n * @public\n */\nexport type FetchTimeoutPhase = 'headers' | 'body' | 'overall';\n\n/**\n * Machine-readable reason a safer-fetch call failed, carried as the detail of the\n * `DetailedResult` every entry point returns.\n *\n * @remarks\n * **This value is an internal-network scanning oracle. Do not echo it, or any string\n * derived from it, to an untrusted caller.** A `'blocked-by-guard'` detail names the\n * URL, the hop, and the guard that rejected it; an attacker who can influence the\n * request URL and observe the error learns whether an internal host exists. Log the\n * detail; return a coarse code (the `kind` alone, or a generic message) to callers.\n *\n * Following the `AiAssist.JsonParseFailureReason` precedent in this package: a `kind`\n * discriminant with per-kind payload fields, and `'unknown'` as an honest catch-all\n * rather than a guess.\n * @public\n */\nexport type FetchFailureReason =\n /** The supplied URL could not be parsed, or its scheme is not `http:` or `https:`. */\n | { readonly kind: 'invalid-url'; readonly url: string; readonly detail: string }\n /**\n * A guard refused. `hop` is 0-based; hop 0 is the caller's URL, and `guard` names which\n * guard rejected — which is what makes \"did the address check run, and was it the thing that\n * said no?\" answerable from the failure alone. A content-type rejection is reported as\n * `'unsupported-content-type'` instead, because that case carries a more useful payload.\n */\n | {\n readonly kind: 'blocked-by-guard';\n readonly url: string;\n readonly hop: number;\n readonly guard: string;\n readonly detail: string;\n }\n /**\n * A redirect status was received that this call would not follow: the policy is `'reject'`,\n * or the response carried no usable `Location`, or following it would revisit a URL already\n * in the chain.\n *\n * @remarks\n * The three are deliberately one kind. Splitting them would buy a caller almost nothing and\n * would widen the scanning oracle this taxonomy already is — and the two follow-time cases\n * are facts about the chain the redirecting server produced, not about the network behind\n * this process. \"The chain got too long\" is a different question and stays\n * `'too-many-redirects'`.\n *\n * `url` is always the URL that **issued** the rejected redirect — the hop this call actually\n * requested and got a 3xx back from — never the `Location` target it pointed at. That holds\n * for all three cases, including the revisit case, where the target is the URL already in the\n * chain and naming it here would make the same field mean two different things. The target is\n * named in the message instead. `status` is the redirect status that was received.\n */\n | { readonly kind: 'redirect-rejected'; readonly url: string; readonly status: number }\n /**\n * The platform returned an opaque redirect, whose `Location` is not readable. This is\n * what a browser yields for `redirect: 'manual'`; the hop cannot be inspected at all.\n */\n | { readonly kind: 'redirect-opaque' }\n /** The redirect hop budget was exhausted. */\n | { readonly kind: 'too-many-redirects'; readonly hops: number; readonly limit: number }\n /** One of the deadlines elapsed. Distinct from `'aborted'`, which is the caller's signal. */\n | {\n readonly kind: 'timeout';\n readonly phase: FetchTimeoutPhase;\n readonly elapsedMs: number;\n readonly limitMs: number;\n }\n /** The caller's `AbortSignal` fired. Distinct from `'timeout'`, which is our deadline. */\n | { readonly kind: 'aborted' }\n /** The transport could not complete the request. */\n | { readonly kind: 'network'; readonly detail: string }\n /**\n * A non-2xx response. `bodyPreview` is **never populated in this release** — error bodies\n * routinely echo request content, including credentials, so surfacing one has to be an\n * explicit, length-capped opt-in rather than a default. The field is declared so that adding\n * that opt-in later is additive.\n */\n | {\n readonly kind: 'http-status';\n readonly status: number;\n readonly statusText: string;\n readonly bodyPreview?: string;\n }\n /**\n * The response exceeded the byte cap. `declared` is what `Content-Length` claimed, present\n * only when the header was sent — and it counts *encoded* bytes where `bytesRead` counts\n * *decoded* bytes, so a `declared` far below `bytesRead` is evidence of a compression bomb\n * or a lying server rather than an arithmetic error.\n */\n | {\n readonly kind: 'too-large';\n readonly bytesRead: number;\n readonly limit: number;\n readonly declared?: number;\n }\n /** A response-headers guard rejected the response's content type. */\n | {\n readonly kind: 'unsupported-content-type';\n readonly contentType?: string;\n readonly accepted: ReadonlyArray<string>;\n }\n /** The response bytes could not be decoded to text with the indicated charset. */\n | { readonly kind: 'decode'; readonly detail: string }\n /** The decoded text could not be parsed, or failed the caller's converter. */\n | { readonly kind: 'parse'; readonly detail: string }\n /**\n * Anything else — including invalid options and a guard or transport that violated its\n * contract. Reports what it knows rather than guessing at a more specific kind.\n */\n | { readonly kind: 'unknown'; readonly detail: string };\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Result } from '@fgv/ts-utils';
|
|
2
|
+
import type { IAddressGuard, IResolvedGuards, IResponseHeadersGuard, ISaferFetchOptions } from './model';
|
|
3
|
+
/**
|
|
4
|
+
* An address guard that permits every address.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* **This is the absence of the SSRF guarantee, given a name.** It performs no address
|
|
8
|
+
* classification, no DNS resolution, and no scheme or port narrowing beyond the core's
|
|
9
|
+
* refusal of non-`http(s)` schemes. A call site using it is reachable at any address the
|
|
10
|
+
* process can route to, including cloud metadata endpoints, loopback admin ports, and
|
|
11
|
+
* RFC-1918 hosts.
|
|
12
|
+
*
|
|
13
|
+
* It ships anyway, for two reasons. It is the honest and only possible choice in a browser,
|
|
14
|
+
* where neither DNS resolution nor redirect interposition exists. And omitting it would not
|
|
15
|
+
* make anyone safer — it would make consumers hand-roll something worse. It is deliberately
|
|
16
|
+
* named to be uncomfortable in review and to grep as a distinct posture.
|
|
17
|
+
*
|
|
18
|
+
* This is the **guard-layer** factory: it returns an {@link SaferFetch.IAddressGuard}, which is
|
|
19
|
+
* what an entry point's `addressGuard` option takes. A pure address *policy* — the synchronous,
|
|
20
|
+
* address-list-in classifier a resolving guard delegates to — is a different layer and needs a
|
|
21
|
+
* distinct name.
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
export declare function allowAnyAddress(): IAddressGuard;
|
|
25
|
+
/**
|
|
26
|
+
* A response-headers guard that accepts only the listed media types.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* Rejecting on `Content-Type` is strictly cheaper than capping mid-read — a header comparison
|
|
30
|
+
* instead of a partial body transfer — so a consumer ingesting URLs wants it on every call. It
|
|
31
|
+
* is also what keeps an HTML error page served with a `200` from being parsed as JSON twenty
|
|
32
|
+
* frames away from where it went wrong.
|
|
33
|
+
*
|
|
34
|
+
* Entries are media types, optionally with a wildcard subtype (`text/*`); a wildcard type
|
|
35
|
+
* requires a wildcard subtype. Matching is case-insensitive and ignores parameters, so
|
|
36
|
+
* `text/html` accepts `text/html; charset=utf-8`. A response with **no** `Content-Type` is
|
|
37
|
+
* rejected: an untyped response has not satisfied a content-type allowlist.
|
|
38
|
+
*
|
|
39
|
+
* Construction is fallible because a malformed entry in a security-adjacent allowlist must be
|
|
40
|
+
* reported to its author rather than silently compiled into a pattern that never matches.
|
|
41
|
+
*
|
|
42
|
+
* @param types - Accepted media types. Must be non-empty.
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
export declare function allowContentTypes(types: ReadonlyArray<string>): Result<IResponseHeadersGuard>;
|
|
46
|
+
/**
|
|
47
|
+
* Resolves the guards a call will use, applying defaults once at the boundary so that no
|
|
48
|
+
* downstream code path branches on a guard's absence.
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* Every optional guard resolves with `??`, uniformly. That uniformity is the point: a
|
|
52
|
+
* `!== undefined` test in one slot where its siblings use `??` lets a `null` — from a
|
|
53
|
+
* JavaScript caller, or through an `unknown` escape hatch — pass straight through and install
|
|
54
|
+
* itself as a guard, which is a real defect that has shipped in this repo before. A `null`
|
|
55
|
+
* guard here is treated as *absent*, never as *installed*.
|
|
56
|
+
*
|
|
57
|
+
* `addressGuard` has no default, so a `null` or missing value is a hard failure rather than a
|
|
58
|
+
* silent passthrough.
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveGuards(options: ISaferFetchOptions): Result<IResolvedGuards>;
|
|
62
|
+
//# sourceMappingURL=guards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../../src/packlets/safer-fetch/guards.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAoB,MAAM,EAAW,MAAM,eAAe,CAAC;AAGlE,OAAO,KAAK,EACV,aAAa,EAIb,eAAe,EAEf,qBAAqB,EACrB,kBAAkB,EAGnB,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,IAAI,aAAa,CAa/C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,qBAAqB,CAAC,CA6B7F;AAiBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAAC,eAAe,CAAC,CAclF"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2026 Erik Fortune
|
|
3
|
+
//
|
|
4
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
// in the Software without restriction, including without limitation the rights
|
|
7
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
// furnished to do so, subject to the following conditions:
|
|
10
|
+
//
|
|
11
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
// copies or substantial portions of the Software.
|
|
13
|
+
//
|
|
14
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
// SOFTWARE.
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.allowAnyAddress = allowAnyAddress;
|
|
23
|
+
exports.allowContentTypes = allowContentTypes;
|
|
24
|
+
exports.resolveGuards = resolveGuards;
|
|
25
|
+
const ts_utils_1 = require("@fgv/ts-utils");
|
|
26
|
+
const contentType_1 = require("./contentType");
|
|
27
|
+
/**
|
|
28
|
+
* An address guard that permits every address.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* **This is the absence of the SSRF guarantee, given a name.** It performs no address
|
|
32
|
+
* classification, no DNS resolution, and no scheme or port narrowing beyond the core's
|
|
33
|
+
* refusal of non-`http(s)` schemes. A call site using it is reachable at any address the
|
|
34
|
+
* process can route to, including cloud metadata endpoints, loopback admin ports, and
|
|
35
|
+
* RFC-1918 hosts.
|
|
36
|
+
*
|
|
37
|
+
* It ships anyway, for two reasons. It is the honest and only possible choice in a browser,
|
|
38
|
+
* where neither DNS resolution nor redirect interposition exists. And omitting it would not
|
|
39
|
+
* make anyone safer — it would make consumers hand-roll something worse. It is deliberately
|
|
40
|
+
* named to be uncomfortable in review and to grep as a distinct posture.
|
|
41
|
+
*
|
|
42
|
+
* This is the **guard-layer** factory: it returns an {@link SaferFetch.IAddressGuard}, which is
|
|
43
|
+
* what an entry point's `addressGuard` option takes. A pure address *policy* — the synchronous,
|
|
44
|
+
* address-list-in classifier a resolving guard delegates to — is a different layer and needs a
|
|
45
|
+
* distinct name.
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
function allowAnyAddress() {
|
|
49
|
+
return {
|
|
50
|
+
name: 'allowAnyAddress',
|
|
51
|
+
check: async (chain) => {
|
|
52
|
+
var _a;
|
|
53
|
+
const hop = (_a = chain[chain.length - 1]) !== null && _a !== void 0 ? _a : undefined;
|
|
54
|
+
if (hop === undefined) {
|
|
55
|
+
return (0, ts_utils_1.fail)('allowAnyAddress: hop chain is empty.');
|
|
56
|
+
}
|
|
57
|
+
// pinnedAddress is deliberately left undefined: this guard validates nothing, so it has
|
|
58
|
+
// no address to pin, and a transport must never be told otherwise.
|
|
59
|
+
return (0, ts_utils_1.succeed)({ url: hop.url });
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A response-headers guard that accepts only the listed media types.
|
|
65
|
+
*
|
|
66
|
+
* @remarks
|
|
67
|
+
* Rejecting on `Content-Type` is strictly cheaper than capping mid-read — a header comparison
|
|
68
|
+
* instead of a partial body transfer — so a consumer ingesting URLs wants it on every call. It
|
|
69
|
+
* is also what keeps an HTML error page served with a `200` from being parsed as JSON twenty
|
|
70
|
+
* frames away from where it went wrong.
|
|
71
|
+
*
|
|
72
|
+
* Entries are media types, optionally with a wildcard subtype (`text/*`); a wildcard type
|
|
73
|
+
* requires a wildcard subtype. Matching is case-insensitive and ignores parameters, so
|
|
74
|
+
* `text/html` accepts `text/html; charset=utf-8`. A response with **no** `Content-Type` is
|
|
75
|
+
* rejected: an untyped response has not satisfied a content-type allowlist.
|
|
76
|
+
*
|
|
77
|
+
* Construction is fallible because a malformed entry in a security-adjacent allowlist must be
|
|
78
|
+
* reported to its author rather than silently compiled into a pattern that never matches.
|
|
79
|
+
*
|
|
80
|
+
* @param types - Accepted media types. Must be non-empty.
|
|
81
|
+
* @public
|
|
82
|
+
*/
|
|
83
|
+
function allowContentTypes(types) {
|
|
84
|
+
if (types.length === 0) {
|
|
85
|
+
return (0, ts_utils_1.fail)('allowContentTypes: at least one media type is required.');
|
|
86
|
+
}
|
|
87
|
+
return (0, ts_utils_1.mapResults)(types.map((t) => (0, contentType_1.parseMediaTypePattern)(t)))
|
|
88
|
+
.withErrorFormat((message) => `allowContentTypes: ${message}`)
|
|
89
|
+
.onSuccess((patterns) => {
|
|
90
|
+
const accepted = patterns.map((p) => `${p.type}/${p.subtype}`);
|
|
91
|
+
return (0, ts_utils_1.succeed)({
|
|
92
|
+
name: 'allowContentTypes',
|
|
93
|
+
acceptedContentTypes: accepted,
|
|
94
|
+
check: async (head) => {
|
|
95
|
+
var _a;
|
|
96
|
+
const contentType = (_a = head.contentType) !== null && _a !== void 0 ? _a : undefined;
|
|
97
|
+
if (contentType === undefined) {
|
|
98
|
+
return (0, ts_utils_1.fail)(`response has no content-type; expected one of [${accepted.join(', ')}].`);
|
|
99
|
+
}
|
|
100
|
+
return (0, contentType_1.parseMediaType)(contentType)
|
|
101
|
+
.withErrorFormat((message) => `content-type ${message}`)
|
|
102
|
+
.onSuccess((actual) => {
|
|
103
|
+
if (patterns.some((p) => (0, contentType_1.mediaTypeMatches)(actual, p))) {
|
|
104
|
+
return (0, ts_utils_1.succeed)(true);
|
|
105
|
+
}
|
|
106
|
+
return (0, ts_utils_1.fail)(`content-type "${contentType}" is not accepted; expected one of [${accepted.join(', ')}].`);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const passthroughRequestGuard = {
|
|
113
|
+
name: 'passthrough',
|
|
114
|
+
check: async (request) => (0, ts_utils_1.succeed)(request)
|
|
115
|
+
};
|
|
116
|
+
const passthroughResponseHeadersGuard = {
|
|
117
|
+
name: 'passthrough',
|
|
118
|
+
check: async () => (0, ts_utils_1.succeed)(true)
|
|
119
|
+
};
|
|
120
|
+
const passthroughResponseBodyGuard = {
|
|
121
|
+
name: 'passthrough',
|
|
122
|
+
check: async () => (0, ts_utils_1.succeed)(true)
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Resolves the guards a call will use, applying defaults once at the boundary so that no
|
|
126
|
+
* downstream code path branches on a guard's absence.
|
|
127
|
+
*
|
|
128
|
+
* @remarks
|
|
129
|
+
* Every optional guard resolves with `??`, uniformly. That uniformity is the point: a
|
|
130
|
+
* `!== undefined` test in one slot where its siblings use `??` lets a `null` — from a
|
|
131
|
+
* JavaScript caller, or through an `unknown` escape hatch — pass straight through and install
|
|
132
|
+
* itself as a guard, which is a real defect that has shipped in this repo before. A `null`
|
|
133
|
+
* guard here is treated as *absent*, never as *installed*.
|
|
134
|
+
*
|
|
135
|
+
* `addressGuard` has no default, so a `null` or missing value is a hard failure rather than a
|
|
136
|
+
* silent passthrough.
|
|
137
|
+
* @internal
|
|
138
|
+
*/
|
|
139
|
+
function resolveGuards(options) {
|
|
140
|
+
var _a, _b, _c, _d;
|
|
141
|
+
const address = (_a = options.addressGuard) !== null && _a !== void 0 ? _a : undefined;
|
|
142
|
+
if (address === undefined) {
|
|
143
|
+
return (0, ts_utils_1.fail)('addressGuard is required and has no default. Supply a named factory — ' +
|
|
144
|
+
'allowAnyAddress() is the explicit opt-out.');
|
|
145
|
+
}
|
|
146
|
+
return (0, ts_utils_1.succeed)({
|
|
147
|
+
address,
|
|
148
|
+
request: (_b = options.requestGuard) !== null && _b !== void 0 ? _b : passthroughRequestGuard,
|
|
149
|
+
responseHeaders: (_c = options.responseHeadersGuard) !== null && _c !== void 0 ? _c : passthroughResponseHeadersGuard,
|
|
150
|
+
responseBody: (_d = options.responseBodyGuard) !== null && _d !== void 0 ? _d : passthroughResponseBodyGuard
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=guards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guards.js","sourceRoot":"","sources":["../../../src/packlets/safer-fetch/guards.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;;AAuCZ,0CAaC;AAsBD,8CA6BC;AAgCD,sCAcC;AAnJD,4CAAkE;AAElE,+CAAoG;AAcpG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAgB,eAAe;IAC7B,OAAO;QACL,IAAI,EAAE,iBAAiB;QACvB,KAAK,EAAE,KAAK,EAAE,KAAiC,EAAkC,EAAE;;YACjF,MAAM,GAAG,GAAG,MAAA,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,mCAAI,SAAS,CAAC;YACjD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,OAAO,IAAA,eAAI,EAAC,sCAAsC,CAAC,CAAC;YACtD,CAAC;YACD,wFAAwF;YACxF,mEAAmE;YACnE,OAAO,IAAA,kBAAO,EAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QACnC,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,iBAAiB,CAAC,KAA4B;IAC5D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAA,eAAI,EAAC,yDAAyD,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,IAAA,qBAAU,EAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,mCAAqB,EAAC,CAAC,CAAC,CAAC,CAAC;SAC1D,eAAe,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,sBAAsB,OAAO,EAAE,CAAC;SAC7D,SAAS,CAAC,CAAC,QAAsB,EAAE,EAAE;QACpC,MAAM,QAAQ,GAA0B,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACtF,OAAO,IAAA,kBAAO,EAAC;YACb,IAAI,EAAE,mBAAmB;YACzB,oBAAoB,EAAE,QAAQ;YAC9B,KAAK,EAAE,KAAK,EAAE,IAA6B,EAAyB,EAAE;;gBACpE,MAAM,WAAW,GAAG,MAAA,IAAI,CAAC,WAAW,mCAAI,SAAS,CAAC;gBAClD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;oBAC9B,OAAO,IAAA,eAAI,EAAC,kDAAkD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACzF,CAAC;gBACD,OAAO,IAAA,4BAAc,EAAC,WAAW,CAAC;qBAC/B,eAAe,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,gBAAgB,OAAO,EAAE,CAAC;qBACvD,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE;oBACpB,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,8BAAgB,EAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;wBACtD,OAAO,IAAA,kBAAO,EAAC,IAAa,CAAC,CAAC;oBAChC,CAAC;oBACD,OAAO,IAAA,eAAI,EACT,iBAAiB,WAAW,uCAAuC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAC3F,CAAC;gBACJ,CAAC,CAAC,CAAC;YACP,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC;AAED,MAAM,uBAAuB,GAAkB;IAC7C,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,KAAK,EAAE,OAA2B,EAAuC,EAAE,CAAC,IAAA,kBAAO,EAAC,OAAO,CAAC;CACpG,CAAC;AAEF,MAAM,+BAA+B,GAA0B;IAC7D,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,KAAK,IAA2B,EAAE,CAAC,IAAA,kBAAO,EAAC,IAAa,CAAC;CACjE,CAAC;AAEF,MAAM,4BAA4B,GAAuB;IACvD,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,KAAK,IAA2B,EAAE,CAAC,IAAA,kBAAO,EAAC,IAAa,CAAC;CACjE,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,SAAgB,aAAa,CAAC,OAA2B;;IACvD,MAAM,OAAO,GAAG,MAAA,OAAO,CAAC,YAAY,mCAAI,SAAS,CAAC;IAClD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,IAAA,eAAI,EACT,wEAAwE;YACtE,4CAA4C,CAC/C,CAAC;IACJ,CAAC;IACD,OAAO,IAAA,kBAAO,EAAC;QACb,OAAO;QACP,OAAO,EAAE,MAAA,OAAO,CAAC,YAAY,mCAAI,uBAAuB;QACxD,eAAe,EAAE,MAAA,OAAO,CAAC,oBAAoB,mCAAI,+BAA+B;QAChF,YAAY,EAAE,MAAA,OAAO,CAAC,iBAAiB,mCAAI,4BAA4B;KACxE,CAAC,CAAC;AACL,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 { fail, mapResults, Result, succeed } from '@fgv/ts-utils';\n\nimport { IMediaType, mediaTypeMatches, parseMediaType, parseMediaTypePattern } from './contentType';\nimport type {\n IAddressGuard,\n IGuardVerdict,\n IRequestGuard,\n IRequestHop,\n IResolvedGuards,\n IResponseBodyGuard,\n IResponseHeadersGuard,\n ISaferFetchOptions,\n ISaferFetchRequest,\n ISaferFetchResponseHead\n} from './model';\n\n/**\n * An address guard that permits every address.\n *\n * @remarks\n * **This is the absence of the SSRF guarantee, given a name.** It performs no address\n * classification, no DNS resolution, and no scheme or port narrowing beyond the core's\n * refusal of non-`http(s)` schemes. A call site using it is reachable at any address the\n * process can route to, including cloud metadata endpoints, loopback admin ports, and\n * RFC-1918 hosts.\n *\n * It ships anyway, for two reasons. It is the honest and only possible choice in a browser,\n * where neither DNS resolution nor redirect interposition exists. And omitting it would not\n * make anyone safer — it would make consumers hand-roll something worse. It is deliberately\n * named to be uncomfortable in review and to grep as a distinct posture.\n *\n * This is the **guard-layer** factory: it returns an {@link SaferFetch.IAddressGuard}, which is\n * what an entry point's `addressGuard` option takes. A pure address *policy* — the synchronous,\n * address-list-in classifier a resolving guard delegates to — is a different layer and needs a\n * distinct name.\n * @public\n */\nexport function allowAnyAddress(): IAddressGuard {\n return {\n name: 'allowAnyAddress',\n check: async (chain: ReadonlyArray<IRequestHop>): Promise<Result<IGuardVerdict>> => {\n const hop = chain[chain.length - 1] ?? undefined;\n if (hop === undefined) {\n return fail('allowAnyAddress: hop chain is empty.');\n }\n // pinnedAddress is deliberately left undefined: this guard validates nothing, so it has\n // no address to pin, and a transport must never be told otherwise.\n return succeed({ url: hop.url });\n }\n };\n}\n\n/**\n * A response-headers guard that accepts only the listed media types.\n *\n * @remarks\n * Rejecting on `Content-Type` is strictly cheaper than capping mid-read — a header comparison\n * instead of a partial body transfer — so a consumer ingesting URLs wants it on every call. It\n * is also what keeps an HTML error page served with a `200` from being parsed as JSON twenty\n * frames away from where it went wrong.\n *\n * Entries are media types, optionally with a wildcard subtype (`text/*`); a wildcard type\n * requires a wildcard subtype. Matching is case-insensitive and ignores parameters, so\n * `text/html` accepts `text/html; charset=utf-8`. A response with **no** `Content-Type` is\n * rejected: an untyped response has not satisfied a content-type allowlist.\n *\n * Construction is fallible because a malformed entry in a security-adjacent allowlist must be\n * reported to its author rather than silently compiled into a pattern that never matches.\n *\n * @param types - Accepted media types. Must be non-empty.\n * @public\n */\nexport function allowContentTypes(types: ReadonlyArray<string>): Result<IResponseHeadersGuard> {\n if (types.length === 0) {\n return fail('allowContentTypes: at least one media type is required.');\n }\n return mapResults(types.map((t) => parseMediaTypePattern(t)))\n .withErrorFormat((message) => `allowContentTypes: ${message}`)\n .onSuccess((patterns: IMediaType[]) => {\n const accepted: ReadonlyArray<string> = patterns.map((p) => `${p.type}/${p.subtype}`);\n return succeed({\n name: 'allowContentTypes',\n acceptedContentTypes: accepted,\n check: async (head: ISaferFetchResponseHead): Promise<Result<true>> => {\n const contentType = head.contentType ?? undefined;\n if (contentType === undefined) {\n return fail(`response has no content-type; expected one of [${accepted.join(', ')}].`);\n }\n return parseMediaType(contentType)\n .withErrorFormat((message) => `content-type ${message}`)\n .onSuccess((actual) => {\n if (patterns.some((p) => mediaTypeMatches(actual, p))) {\n return succeed(true as const);\n }\n return fail(\n `content-type \"${contentType}\" is not accepted; expected one of [${accepted.join(', ')}].`\n );\n });\n }\n });\n });\n}\n\nconst passthroughRequestGuard: IRequestGuard = {\n name: 'passthrough',\n check: async (request: ISaferFetchRequest): Promise<Result<ISaferFetchRequest>> => succeed(request)\n};\n\nconst passthroughResponseHeadersGuard: IResponseHeadersGuard = {\n name: 'passthrough',\n check: async (): Promise<Result<true>> => succeed(true as const)\n};\n\nconst passthroughResponseBodyGuard: IResponseBodyGuard = {\n name: 'passthrough',\n check: async (): Promise<Result<true>> => succeed(true as const)\n};\n\n/**\n * Resolves the guards a call will use, applying defaults once at the boundary so that no\n * downstream code path branches on a guard's absence.\n *\n * @remarks\n * Every optional guard resolves with `??`, uniformly. That uniformity is the point: a\n * `!== undefined` test in one slot where its siblings use `??` lets a `null` — from a\n * JavaScript caller, or through an `unknown` escape hatch — pass straight through and install\n * itself as a guard, which is a real defect that has shipped in this repo before. A `null`\n * guard here is treated as *absent*, never as *installed*.\n *\n * `addressGuard` has no default, so a `null` or missing value is a hard failure rather than a\n * silent passthrough.\n * @internal\n */\nexport function resolveGuards(options: ISaferFetchOptions): Result<IResolvedGuards> {\n const address = options.addressGuard ?? undefined;\n if (address === undefined) {\n return fail(\n 'addressGuard is required and has no default. Supply a named factory — ' +\n 'allowAnyAddress() is the explicit opt-out.'\n );\n }\n return succeed({\n address,\n request: options.requestGuard ?? passthroughRequestGuard,\n responseHeaders: options.responseHeadersGuard ?? passthroughResponseHeadersGuard,\n responseBody: options.responseBodyGuard ?? passthroughResponseBodyGuard\n });\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An HTTP fetch primitive with an explicit threat model (browser version).
|
|
3
|
+
*
|
|
4
|
+
* This barrel deliberately omits `blockPrivateNetworks` and the resolver seam beneath it: that
|
|
5
|
+
* module imports `node:dns/promises`, and exporting it here would pull a Node builtin into a
|
|
6
|
+
* browser bundle.
|
|
7
|
+
*
|
|
8
|
+
* The address-classification layer — `classifyAddress`, and the pure synchronous policies
|
|
9
|
+
* `allowAnyAddressPolicy` / `blockPrivateNetworksPolicy` — **is** exported here: it is pure
|
|
10
|
+
* arithmetic over parsed octets, it works identically in a browser, and it is useful for a
|
|
11
|
+
* URL-zero check. It is not a substitute for the guard, and cannot be: it classifies an address
|
|
12
|
+
* a caller already holds, while what the browser lacks is any way to learn the address a
|
|
13
|
+
* hostname resolves to.
|
|
14
|
+
*
|
|
15
|
+
* Note: the resolved-address (private-IP) guard and per-hop redirect revalidation are NOT
|
|
16
|
+
* available in a browser, and not for want of implementation. There is no browser API that
|
|
17
|
+
* returns a hostname's A/AAAA records, nothing in `fetch` or `Response` exposes the peer
|
|
18
|
+
* address, and `redirect: 'manual'` yields an opaque response whose `Location` is not
|
|
19
|
+
* readable — a `'validate-each-hop'` call there fails as `'redirect-opaque'` rather than
|
|
20
|
+
* quietly following anything. `allowAnyAddress()` is the honest choice, and its name says so.
|
|
21
|
+
*
|
|
22
|
+
* @packageDocumentation
|
|
23
|
+
*/
|
|
24
|
+
export { ALWAYS_STRIPPED_HEADERS, DEFAULT_HEADERS_TIMEOUT_MS, DEFAULT_MAX_REDIRECTS, DEFAULT_MAX_RESPONSE_BYTES, DEFAULT_RETRY_BASE_DELAY_MS, DEFAULT_RETRY_MAX_DELAY_MS, DEFAULT_TIMEOUT_MS, IDEMPOTENT_METHODS, REDIRECT_STATUSES, RETRY_AFTER_STATUSES, RETRYABLE_HTTP_STATUSES, SUPPORTED_SCHEMES } from './defaults';
|
|
25
|
+
export type { FetchFailureReason, FetchTimeoutPhase } from './failureReason';
|
|
26
|
+
export type { IRetryPolicy } from './retry';
|
|
27
|
+
export type { IAddressGuard, IFetchTransport, IFetchTransportHints, IGuardVerdict, IRequestGuard, IRequestHop, IResolvedGuards, IResponseBodyGuard, IResponseHeadersGuard, ISaferFetchOptions, ISaferFetchRequest, ISaferFetchResponse, ISaferFetchResponseHead, SaferFetchMethod, SaferFetchRedirectPolicy } from './model';
|
|
28
|
+
export { allowAnyAddress, allowContentTypes } from './guards';
|
|
29
|
+
export { platformFetchTransport } from './transport';
|
|
30
|
+
export { saferFetchBytes, saferFetchJson, saferFetchText, type ISaferFetchJsonOptions } from './saferFetch';
|
|
31
|
+
export { allowAnyAddressPolicy, blockPrivateNetworksPolicy, type IAddressCheckVerdict, type IAddressPolicy, type IBlockPrivateNetworksOptions } from './addressPolicy';
|
|
32
|
+
export { classifyAddress, type AddressClassification, type AddressFamily, type IClassifiedAddress, type IEmbeddedIpv4, type Ipv4EmbeddingKind } from './addressClassification';
|
|
33
|
+
//# sourceMappingURL=index.browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../../../src/packlets/safer-fetch/index.browser.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,EACrB,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,EAC1B,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,YAAY,EACV,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,gBAAgB,EAChB,wBAAwB,EACzB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAW5G,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,4BAA4B,EAClC,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAC"}
|