@openclaw/slack 2026.9.6 → 2026.9.8
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/assets/icon.png +0 -0
- package/dist/.setup/{account-inspect-DP1Bcwj4.mjs → account-inspect-DWBXpER6.mjs} +1 -1
- package/dist/.setup/{accounts-BBHvg0pY.mjs → accounts-D_N9IqNA.mjs} +23 -1
- package/dist/.setup/{action-runtime-B01d85zt.mjs → action-runtime-DfWvuiZd.mjs} +26 -41
- package/dist/.setup/action-runtime.runtime-Ck4bAWOX.mjs +2 -0
- package/dist/.setup/{actions-DKCtBgCI.mjs → actions-Df0CzcDl.mjs} +88 -175
- package/dist/.setup/{approval-handler.runtime-DMszKn_7.mjs → approval-handler.runtime-Ed6Sw5VK.mjs} +37 -47
- package/dist/.setup/{approval-message-updates-Tb_qRokK.mjs → approval-message-updates-BadvtA7R.mjs} +1 -2
- package/dist/.setup/{channel-CErYZ5zw.mjs → channel-C7bgPSTE.mjs} +181 -273
- package/dist/.setup/channel-meta-C443t-lI.mjs +27 -0
- package/dist/.setup/{channel-type-Dxk8VJUb.mjs → channel-type-ClOttrZ5.mjs} +13 -21
- package/dist/.setup/{channel.setup-BVoUtdPn.mjs → channel.setup-Dh2jzAjN.mjs} +35 -42
- package/dist/.setup/{config-schema-CyL279e7.mjs → config-schema-BZCxmHKG.mjs} +24 -21
- package/dist/.setup/{directory-config-CQTSH45W.mjs → directory-config-BOUWH3hR.mjs} +7 -10
- package/dist/.setup/{directory-live-BCcj89QV.mjs → directory-live-B2O4B_Ho.mjs} +9 -21
- package/dist/.setup/doctor-contract-CRN1Z_c7.mjs +342 -0
- package/dist/.setup/{message-tool-api-C0PTFaNV.mjs → message-tool-api-Cu4lwlGy.mjs} +34 -48
- package/dist/.setup/{provider-C-jPaGt2.mjs → monitor-BPVfkumU.mjs} +2181 -2693
- package/dist/.setup/{outbound-adapter-A7Wdn8ZT.mjs → outbound-adapter-Bv48XUjq.mjs} +10 -21
- package/dist/.setup/{pipeline.runtime-BlPOexmJ.mjs → pipeline.runtime-B1H63Frf.mjs} +311 -457
- package/dist/.setup/{plugin-routes-C3X0eVaK.mjs → plugin-routes-B8tooDLj.mjs} +1 -1
- package/dist/.setup/{policy-BApW53pI.mjs → policy-D-jfM174.mjs} +5 -5
- package/dist/.setup/{probe-DiYIMPJo.mjs → probe-B2Y9qc_z.mjs} +101 -39
- package/dist/.setup/{registry-bbjH7IHX.mjs → registry-CIbMfuf8.mjs} +6 -2
- package/dist/.setup/{relay-source-DfOnjJTE.mjs → relay-source-BpKdjsll.mjs} +10 -12
- package/dist/.setup/{replies-BBAJxUOQ.mjs → replies-BAnUtr2f.mjs} +16 -76
- package/dist/.setup/{reply-blocks-Bm8kf6mK.mjs → reply-blocks-SE1kTE3I.mjs} +27 -154
- package/dist/.setup/{resolve-channels-BXbMBbJD.mjs → resolve-channels-XXBn8UU_.mjs} +4 -5
- package/dist/.setup/{resolve-users-65WqvIqD.mjs → resolve-users-DW0ngD03.mjs} +20 -22
- package/dist/.setup/{scopes-h3YB-mMs.mjs → scopes-CasuDvaa.mjs} +6 -17
- package/dist/.setup/{secret-contract-DxmvbxBF.mjs → secret-contract-BaZcu6P_.mjs} +15 -20
- package/dist/.setup/{secret-contract-api-D9UfYujm.mjs → secret-contract-api-Dp6290Vg.mjs} +1 -1
- package/dist/.setup/{security-audit-BypHqcq5.mjs → security-audit-BqDc-6VL.mjs} +1 -1
- package/dist/.setup/{send-Cp6omkOg.mjs → send-BOVLQi3n.mjs} +84 -171
- package/dist/.setup/{session-status-Cf7AvWOt.mjs → session-status-DscajIsd.mjs} +315 -238
- package/dist/.setup/{setup-plugin-api-dLPkL06L.mjs → setup-plugin-api-DTtbTKNk.mjs} +1 -1
- package/dist/.setup/{setup-surface-D3DLcCxu.mjs → setup-surface-Dn7QgWRE.mjs} +24 -56
- package/dist/.setup/{shared-CJ3arAFL.mjs → shared-qau-uDGl.mjs} +30 -56
- package/dist/.setup/{slash-dispatch.runtime-HFy_XvyO.mjs → slash-dispatch.runtime--AEzLVBF.mjs} +1 -1
- package/dist/.setup/{streaming-compat-B-e0mqM0.mjs → streaming-compat-C5Wut626.mjs} +31 -8
- package/dist/.setup/{target-parsing-DEsPjngB.mjs → target-parsing-FFE26kOV.mjs} +1 -7
- package/dist/.setup/{targets-Cku2KCvg.mjs → targets-C6LZQL1Q.mjs} +1 -1
- package/dist/.setup/{thread-ts-CukseRVL.mjs → thread-ts-Cvt7WD6B.mjs} +0 -1
- package/dist/account-inspect-api.js +1 -1
- package/dist/api.js +18 -17
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/doctor-contract-api.js +1 -1
- package/dist/http-routes-api.js +1 -1
- package/dist/index.js +1 -1
- package/dist/message-tool-api.js +1 -1
- package/dist/runtime-api.js +11 -12
- package/dist/secret-contract-api.js +1 -1
- package/dist/security-contract-api.js +1 -1
- package/dist/setup-entry.js +2 -2
- package/dist/setup-plugin-api.js +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/child_process.d.ts +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/crypto.d.ts +20 -8
- package/node_modules/@slack/bolt/node_modules/@types/node/ffi.d.ts +14 -0
- package/node_modules/@slack/bolt/node_modules/@types/node/http2.d.ts +3 -2
- package/node_modules/@slack/bolt/node_modules/@types/node/net.d.ts +16 -4
- package/node_modules/@slack/bolt/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/bolt/node_modules/@types/node/process.d.ts +1 -0
- package/node_modules/@slack/bolt/node_modules/@types/node/quic.d.ts +39 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/stream/iter.d.ts +9 -12
- package/node_modules/@slack/bolt/node_modules/@types/node/test/reporters.d.ts +1 -0
- package/node_modules/@slack/bolt/node_modules/@types/node/test.d.ts +72 -0
- package/node_modules/@slack/bolt/node_modules/@types/node/tls.d.ts +14 -10
- package/node_modules/@slack/bolt/node_modules/@types/node/vm.d.ts +2 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/worker_threads.d.ts +4 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/child_process.d.ts +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/crypto.d.ts +20 -8
- package/node_modules/@slack/web-api/node_modules/@types/node/ffi.d.ts +14 -0
- package/node_modules/@slack/web-api/node_modules/@types/node/http2.d.ts +3 -2
- package/node_modules/@slack/web-api/node_modules/@types/node/net.d.ts +16 -4
- package/node_modules/@slack/web-api/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/web-api/node_modules/@types/node/process.d.ts +1 -0
- package/node_modules/@slack/web-api/node_modules/@types/node/quic.d.ts +39 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/stream/iter.d.ts +9 -12
- package/node_modules/@slack/web-api/node_modules/@types/node/test/reporters.d.ts +1 -0
- package/node_modules/@slack/web-api/node_modules/@types/node/test.d.ts +72 -0
- package/node_modules/@slack/web-api/node_modules/@types/node/tls.d.ts +14 -10
- package/node_modules/@slack/web-api/node_modules/@types/node/vm.d.ts +2 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/worker_threads.d.ts +4 -1
- package/node_modules/get-east-asian-width/index.js +8 -3
- package/node_modules/get-east-asian-width/lookup-data.js +1 -1
- package/node_modules/get-east-asian-width/lookup.js +4 -4
- package/node_modules/get-east-asian-width/package.json +1 -1
- package/node_modules/typebox/build/compile/code.mjs +1 -2
- package/node_modules/typebox/build/compile/validator.mjs +5 -1
- package/node_modules/typebox/build/format/iri.mjs +16 -9
- package/node_modules/typebox/build/guard/emit.mjs +2 -2
- package/node_modules/typebox/build/guard/guard.d.mts +7 -5
- package/node_modules/typebox/build/guard/guard.mjs +14 -9
- package/node_modules/typebox/build/guard/index.mjs +6 -0
- package/node_modules/typebox/build/guard/unicode/unicode.d.mts +6 -0
- package/node_modules/typebox/build/guard/unicode/unicode.mjs +35 -0
- package/node_modules/typebox/build/guard/{string.mjs → unicode/unicode_segment.mjs} +16 -19
- package/node_modules/typebox/build/schema/build.mjs +2 -3
- package/node_modules/typebox/build/schema/engine/_pathing.d.mts +1 -0
- package/node_modules/typebox/build/schema/engine/_pathing.mjs +11 -0
- package/node_modules/typebox/build/schema/engine/_refine.mjs +13 -5
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/dependencies.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/dependentSchemas.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/dynamicRef.d.mts +1 -1
- package/node_modules/typebox/build/schema/engine/dynamicRef.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/format.mjs +9 -2
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +3 -2
- package/node_modules/typebox/build/schema/engine/properties.mjs +3 -2
- package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/recursiveRef.d.mts +1 -1
- package/node_modules/typebox/build/schema/engine/recursiveRef.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/ref.d.mts +1 -1
- package/node_modules/typebox/build/schema/engine/ref.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/schema.mjs +24 -24
- package/node_modules/typebox/build/schema/parse.mjs +3 -0
- package/node_modules/typebox/build/schema/pointer/pointer.d.mts +1 -6
- package/node_modules/typebox/build/schema/pointer/pointer.mjs +47 -64
- package/node_modules/typebox/build/system/settings/internal.d.mts +2 -0
- package/node_modules/typebox/build/system/settings/internal.mjs +20 -0
- package/node_modules/typebox/build/system/settings/settings.d.mts +11 -0
- package/node_modules/typebox/build/system/settings/settings.mjs +14 -0
- package/node_modules/typebox/build/type/extends/inference.d.mts +1 -1
- package/node_modules/typebox/build/type/extends/inference.mjs +15 -2
- package/node_modules/typebox/build/type/extends/tuple.d.mts +2 -1
- package/node_modules/typebox/build/type/extends/tuple.mjs +21 -1
- package/node_modules/typebox/build/value/parse/parse.mjs +10 -4
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +3 -1
- package/node_modules/undici/README.md +81 -25
- package/node_modules/undici/docs/docs/api/Agent.md +249 -42
- package/node_modules/undici/docs/docs/api/BalancedPool.md +246 -48
- package/node_modules/undici/docs/docs/api/CacheStorage.md +171 -13
- package/node_modules/undici/docs/docs/api/CacheStore.md +294 -98
- package/node_modules/undici/docs/docs/api/Client.md +407 -162
- package/node_modules/undici/docs/docs/api/ClientStats.md +80 -14
- package/node_modules/undici/docs/docs/api/Connector.md +118 -35
- package/node_modules/undici/docs/docs/api/ContentType.md +65 -25
- package/node_modules/undici/docs/docs/api/Cookies.md +122 -71
- package/node_modules/undici/docs/docs/api/Debug.md +34 -18
- package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +364 -164
- package/node_modules/undici/docs/docs/api/Dispatcher.md +490 -1055
- package/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +105 -93
- package/node_modules/undici/docs/docs/api/Errors.md +503 -28
- package/node_modules/undici/docs/docs/api/EventSource.md +326 -13
- package/node_modules/undici/docs/docs/api/Fetch.md +681 -30
- package/node_modules/undici/docs/docs/api/GlobalInstallation.md +78 -96
- package/node_modules/undici/docs/docs/api/H2CClient.md +298 -176
- package/node_modules/undici/docs/docs/api/Interceptors.md +418 -0
- package/node_modules/undici/docs/docs/api/MockAgent.md +333 -314
- package/node_modules/undici/docs/docs/api/MockCallHistory.md +272 -98
- package/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +189 -26
- package/node_modules/undici/docs/docs/api/MockClient.md +151 -33
- package/node_modules/undici/docs/docs/api/MockErrors.md +56 -5
- package/node_modules/undici/docs/docs/api/MockPool.md +321 -258
- package/node_modules/undici/docs/docs/api/Pool.md +236 -41
- package/node_modules/undici/docs/docs/api/PoolStats.md +119 -13
- package/node_modules/undici/docs/docs/api/ProxyAgent.md +179 -160
- package/node_modules/undici/docs/docs/api/RedirectHandler.md +238 -55
- package/node_modules/undici/docs/docs/api/RetryAgent.md +113 -27
- package/node_modules/undici/docs/docs/api/RetryHandler.md +161 -79
- package/node_modules/undici/docs/docs/api/RoundRobinPool.md +166 -72
- package/node_modules/undici/docs/docs/api/SnapshotAgent.md +262 -517
- package/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +177 -196
- package/node_modules/undici/docs/docs/api/Util.md +68 -11
- package/node_modules/undici/docs/docs/api/WebSocket.md +475 -80
- package/node_modules/undici/docs/docs/api/api-lifecycle.md +100 -32
- package/node_modules/undici/docs/docs/best-practices/client-certificate.md +2 -0
- package/node_modules/undici/docs/docs/best-practices/crawling.md +3 -1
- package/node_modules/undici/docs/docs/best-practices/migrating-from-v7-to-v8.md +233 -0
- package/node_modules/undici/docs/docs/best-practices/mocking-request.md +9 -7
- package/node_modules/undici/docs/docs/best-practices/proxy.md +3 -1
- package/node_modules/undici/docs/docs/best-practices/undici-vs-builtin-fetch.md +10 -8
- package/node_modules/undici/docs/docs/best-practices/writing-tests.md +2 -0
- package/node_modules/undici/docs/docs/getting-started.md +281 -0
- package/node_modules/undici/docs/docs/index.md +784 -0
- package/node_modules/undici/docs/docs/site.json +125 -0
- package/node_modules/undici/docs/docs/type-map.json +79 -0
- package/node_modules/undici/index.js +4 -2
- package/node_modules/undici/lib/api/api-connect.js +13 -11
- package/node_modules/undici/lib/api/api-pipeline.js +26 -13
- package/node_modules/undici/lib/api/api-request.js +51 -21
- package/node_modules/undici/lib/api/api-stream.js +81 -20
- package/node_modules/undici/lib/api/api-upgrade.js +21 -11
- package/node_modules/undici/lib/api/readable.js +45 -9
- package/node_modules/undici/lib/cache/memory-cache-store.js +3 -3
- package/node_modules/undici/lib/cache/sqlite-cache-store.js +3 -1
- package/node_modules/undici/lib/core/connect.js +46 -7
- package/node_modules/undici/lib/core/constants.js +1 -24
- package/node_modules/undici/lib/core/errors.js +22 -2
- package/node_modules/undici/lib/core/request.js +100 -18
- package/node_modules/undici/lib/core/symbols.js +4 -0
- package/node_modules/undici/lib/core/util.js +105 -48
- package/node_modules/undici/lib/dispatcher/agent.js +51 -32
- package/node_modules/undici/lib/dispatcher/balanced-pool.js +23 -27
- package/node_modules/undici/lib/dispatcher/client-h1.js +86 -24
- package/node_modules/undici/lib/dispatcher/client-h2.js +1091 -361
- package/node_modules/undici/lib/dispatcher/client.js +109 -36
- package/node_modules/undici/lib/dispatcher/dispatcher-base.js +19 -6
- package/node_modules/undici/lib/dispatcher/dispatcher.js +15 -5
- package/node_modules/undici/lib/dispatcher/dispatcher1-wrapper.js +110 -0
- package/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +37 -8
- package/node_modules/undici/lib/dispatcher/h2c-client.js +4 -4
- package/node_modules/undici/lib/dispatcher/pool-base.js +28 -10
- package/node_modules/undici/lib/dispatcher/pool.js +30 -5
- package/node_modules/undici/lib/dispatcher/proxy-agent.js +95 -19
- package/node_modules/undici/lib/dispatcher/retry-agent.js +3 -0
- package/node_modules/undici/lib/dispatcher/round-robin-pool.js +32 -10
- package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +153 -63
- package/node_modules/undici/lib/global.js +37 -14
- package/node_modules/undici/lib/handler/cache-handler.js +49 -7
- package/node_modules/undici/lib/handler/cache-revalidation-handler.js +10 -0
- package/node_modules/undici/lib/handler/decorator-handler.js +8 -3
- package/node_modules/undici/lib/handler/deduplication-handler.js +12 -6
- package/node_modules/undici/lib/handler/redirect-handler.js +66 -66
- package/node_modules/undici/lib/handler/retry-handler.js +191 -50
- package/node_modules/undici/lib/interceptor/cache.js +75 -25
- package/node_modules/undici/lib/interceptor/decompress.js +5 -2
- package/node_modules/undici/lib/interceptor/deduplicate.js +5 -4
- package/node_modules/undici/lib/interceptor/dns.js +12 -1
- package/node_modules/undici/lib/interceptor/redirect.js +3 -3
- package/node_modules/undici/lib/llhttp/llhttp-wasm.js +1 -1
- package/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +1 -1
- package/node_modules/undici/lib/mock/mock-agent.js +21 -9
- package/node_modules/undici/lib/mock/mock-call-history.js +1 -1
- package/node_modules/undici/lib/mock/mock-interceptor.js +25 -7
- package/node_modules/undici/lib/mock/mock-utils.js +281 -47
- package/node_modules/undici/lib/mock/snapshot-agent.js +25 -7
- package/node_modules/undici/lib/mock/snapshot-recorder.js +38 -3
- package/node_modules/undici/lib/util/cache.js +92 -10
- package/node_modules/undici/lib/util/runtime-features.js +3 -34
- package/node_modules/undici/lib/web/cache/cache.js +6 -8
- package/node_modules/undici/lib/web/cookies/parse.js +150 -146
- package/node_modules/undici/lib/web/cookies/util.js +1 -1
- package/node_modules/undici/lib/web/eventsource/eventsource-stream.js +28 -1
- package/node_modules/undici/lib/web/eventsource/eventsource.js +20 -21
- package/node_modules/undici/lib/web/eventsource/util.js +32 -1
- package/node_modules/undici/lib/web/fetch/body.js +48 -10
- package/node_modules/undici/lib/web/fetch/constants.js +1 -1
- package/node_modules/undici/lib/web/fetch/formdata.js +21 -2
- package/node_modules/undici/lib/web/fetch/index.js +242 -223
- package/node_modules/undici/lib/web/fetch/request.js +35 -6
- package/node_modules/undici/lib/web/fetch/response.js +1 -3
- package/node_modules/undici/lib/web/fetch/util.js +10 -2
- package/node_modules/undici/lib/web/webidl/index.js +5 -7
- package/node_modules/undici/lib/web/websocket/frame.js +1 -7
- package/node_modules/undici/lib/web/websocket/receiver.js +1 -1
- package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +10 -10
- package/node_modules/undici/lib/web/websocket/websocket.js +25 -3
- package/node_modules/undici/package.json +10 -10
- package/node_modules/undici/types/agent.d.ts +0 -2
- package/node_modules/undici/types/client.d.ts +69 -24
- package/node_modules/undici/types/connector.d.ts +1 -0
- package/node_modules/undici/types/cookies.d.ts +1 -1
- package/node_modules/undici/types/dispatcher.d.ts +8 -30
- package/node_modules/undici/types/dispatcher1-wrapper.d.ts +7 -0
- package/node_modules/undici/types/errors.d.ts +10 -0
- package/node_modules/undici/types/fetch.d.ts +5 -1
- package/node_modules/undici/types/formdata.d.ts +0 -6
- package/node_modules/undici/types/h2c-client.d.ts +6 -6
- package/node_modules/undici/types/handlers.d.ts +2 -0
- package/node_modules/undici/types/header.d.ts +5 -0
- package/node_modules/undici/types/index.d.ts +3 -1
- package/node_modules/undici/types/interceptors.d.ts +1 -1
- package/node_modules/undici/types/mock-agent.d.ts +11 -10
- package/node_modules/undici/types/mock-interceptor.d.ts +5 -1
- package/node_modules/undici/types/pool.d.ts +0 -2
- package/node_modules/undici/types/proxy-agent.d.ts +9 -2
- package/node_modules/undici/types/round-robin-pool.d.ts +0 -2
- package/node_modules/undici/types/snapshot-agent.d.ts +4 -0
- package/node_modules/undici/types/socks5-proxy-agent.d.ts +4 -2
- package/node_modules/undici/types/webidl.d.ts +0 -1
- package/openclaw.plugin.json +31 -3
- package/package.json +7 -7
- package/dist/.setup/accounts.runtime-DN-PPocC.mjs +0 -2
- package/dist/.setup/action-runtime.runtime-C8R8CUan.mjs +0 -2
- package/dist/.setup/doctor-contract-CtMUqjvk.mjs +0 -186
- package/dist/.setup/monitor-_Sht7Dqy.mjs +0 -13
- package/dist/.setup/rolldown-runtime-D7D4PA-g.mjs +0 -13
- package/dist/.setup/send.runtime-B4caZU6T.mjs +0 -2
- package/dist/.setup/slash-commands.runtime-BpSH0b3q.mjs +0 -2
- package/dist/.setup/slash-skill-commands.runtime-Bi1JWZHY.mjs +0 -2
- package/node_modules/undici/lib/handler/unwrap-handler.js +0 -106
- package/node_modules/undici/lib/handler/wrap-handler.js +0 -105
- package/node_modules/undici/lib/llhttp/.gitkeep +0 -0
- package/node_modules/undici/lib/util/promise.js +0 -28
- /package/node_modules/typebox/build/guard/{string.d.mts → unicode/unicode_segment.d.mts} +0 -0
|
@@ -9,7 +9,7 @@ const {
|
|
|
9
9
|
kGetNetConnect,
|
|
10
10
|
kTotalDispatchCount
|
|
11
11
|
} = require('./mock-symbols')
|
|
12
|
-
const { serializePathWithQuery } = require('../core/util')
|
|
12
|
+
const { serializePathWithQuery, parseHeaders } = require('../core/util')
|
|
13
13
|
const { STATUS_CODES } = require('node:http')
|
|
14
14
|
const {
|
|
15
15
|
types: {
|
|
@@ -17,6 +17,7 @@ const {
|
|
|
17
17
|
}
|
|
18
18
|
} = require('node:util')
|
|
19
19
|
const { InvalidArgumentError } = require('../core/errors')
|
|
20
|
+
const requestAborted = Symbol('request aborted')
|
|
20
21
|
|
|
21
22
|
function matchValue (match, value) {
|
|
22
23
|
if (typeof match === 'string') {
|
|
@@ -153,6 +154,11 @@ function getResponseData (data) {
|
|
|
153
154
|
return data
|
|
154
155
|
} else if (data instanceof ArrayBuffer) {
|
|
155
156
|
return data
|
|
157
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
158
|
+
// A DataView, or any non-Uint8Array typed array, is a byte container
|
|
159
|
+
// rather than a plain object. Buffer.from() cannot read one directly, so
|
|
160
|
+
// expose the bytes it covers instead of letting it reach JSON.stringify.
|
|
161
|
+
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
|
|
156
162
|
} else if (typeof data === 'object') {
|
|
157
163
|
return JSON.stringify(data)
|
|
158
164
|
} else if (data) {
|
|
@@ -225,9 +231,15 @@ function deleteMockDispatch (mockDispatches, key) {
|
|
|
225
231
|
}
|
|
226
232
|
|
|
227
233
|
/**
|
|
228
|
-
* @param {string} path Path to remove trailing slash from
|
|
234
|
+
* @param {string|RegExp|Function} path Path, or path matcher, to remove trailing slash from
|
|
229
235
|
*/
|
|
230
236
|
function removeTrailingSlash (path) {
|
|
237
|
+
// Registered path matchers may be a RegExp or a function, which have no
|
|
238
|
+
// trailing slash to strip; hand those back for matchValue to apply.
|
|
239
|
+
if (typeof path !== 'string') {
|
|
240
|
+
return path
|
|
241
|
+
}
|
|
242
|
+
|
|
231
243
|
while (path.endsWith('/')) {
|
|
232
244
|
path = path.slice(0, -1)
|
|
233
245
|
}
|
|
@@ -292,26 +304,69 @@ function mockDispatch (opts, handler) {
|
|
|
292
304
|
// Get mock dispatch from built key
|
|
293
305
|
const key = buildKey(opts)
|
|
294
306
|
const mockDispatch = getMockDispatch(this[kDispatches], key)
|
|
307
|
+
const mockDispatches = this[kDispatches]
|
|
295
308
|
|
|
296
309
|
mockDispatch.timesInvoked++
|
|
297
310
|
|
|
298
|
-
// Here's where we resolve a callback if a callback is present for the dispatch data.
|
|
299
|
-
if (mockDispatch.data.callback) {
|
|
300
|
-
mockDispatch.data = { ...mockDispatch.data, ...mockDispatch.data.callback(opts) }
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Parse mockDispatch data
|
|
304
|
-
const { data: { statusCode, data, headers, trailers, error }, delay, persist } = mockDispatch
|
|
305
311
|
const { timesInvoked, times } = mockDispatch
|
|
306
312
|
|
|
307
313
|
// If it's used up and not persistent, mark as consumed
|
|
308
|
-
mockDispatch.consumed = !persist && timesInvoked >= times
|
|
314
|
+
mockDispatch.consumed = !mockDispatch.persist && timesInvoked >= times
|
|
309
315
|
mockDispatch.pending = timesInvoked < times
|
|
310
316
|
|
|
317
|
+
const hasBodyHooks = typeof handler.onBodySent === 'function' ||
|
|
318
|
+
typeof handler.onRequestSent === 'function'
|
|
319
|
+
|
|
320
|
+
// Here's where we resolve a callback if a callback is present for the dispatch data.
|
|
321
|
+
if (mockDispatch.data.callback && (!hasBodyHooks || opts.body == null)) {
|
|
322
|
+
const { callback, ...responseDefaults } = mockDispatch.data
|
|
323
|
+
const callbackResult = callback(opts)
|
|
324
|
+
|
|
325
|
+
// An asynchronous reply options callback resolves to the reply data, so
|
|
326
|
+
// the dispatch can only continue once the returned promise settles.
|
|
327
|
+
// A rejection cannot be thrown synchronously from the dispatch at that
|
|
328
|
+
// point, so it is surfaced as a response error instead.
|
|
329
|
+
if (isPromise(callbackResult)) {
|
|
330
|
+
callbackResult.then(
|
|
331
|
+
(resolvedData) => {
|
|
332
|
+
if (resolvedData == null || typeof resolvedData !== 'object') {
|
|
333
|
+
handler.onResponseError(null, new InvalidArgumentError('reply options callback must return an object'))
|
|
334
|
+
return
|
|
335
|
+
}
|
|
336
|
+
dispatchMockReply(mockDispatches, mockDispatch, key, opts, handler, { ...responseDefaults, ...resolvedData })
|
|
337
|
+
},
|
|
338
|
+
(error) => {
|
|
339
|
+
handler.onResponseError(null, error)
|
|
340
|
+
}
|
|
341
|
+
)
|
|
342
|
+
return true
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (callbackResult == null || typeof callbackResult !== 'object') {
|
|
346
|
+
throw new InvalidArgumentError('reply options callback must return an object')
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return dispatchMockReply(mockDispatches, mockDispatch, key, opts, handler, { ...responseDefaults, ...callbackResult })
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return dispatchMockReply(mockDispatches, mockDispatch, key, opts, handler)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Replies to a request once the mock dispatch data is fully resolved
|
|
357
|
+
*/
|
|
358
|
+
function dispatchMockReply (mockDispatches, mockDispatch, key, opts, handler, resolvedResponse) {
|
|
359
|
+
// Parse mockDispatch data. When a reply callback has already been resolved
|
|
360
|
+
// in mockDispatch() (i.e. no body lifecycle hooks are involved), the resolved
|
|
361
|
+
// response is passed in here, leaving mockDispatch.data untouched so the
|
|
362
|
+
// callback can be re-invoked for persistent / times() replies.
|
|
363
|
+
const { data: responseData, delay } = mockDispatch
|
|
364
|
+
const response = resolvedResponse ?? responseData
|
|
365
|
+
|
|
311
366
|
// If specified, trigger dispatch error
|
|
312
|
-
if (error !== null) {
|
|
313
|
-
deleteMockDispatch(
|
|
314
|
-
handler.
|
|
367
|
+
if (response.error !== null) {
|
|
368
|
+
deleteMockDispatch(mockDispatches, key)
|
|
369
|
+
handler.onResponseError(null, response.error)
|
|
315
370
|
return true
|
|
316
371
|
}
|
|
317
372
|
|
|
@@ -319,48 +374,132 @@ function mockDispatch (opts, handler) {
|
|
|
319
374
|
let aborted = false
|
|
320
375
|
let timer = null
|
|
321
376
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
377
|
+
// Create the controller early so abort can use it
|
|
378
|
+
const controller = {
|
|
379
|
+
paused: false,
|
|
380
|
+
rawHeaders: null,
|
|
381
|
+
rawTrailers: null,
|
|
382
|
+
pause () {
|
|
383
|
+
this.paused = true
|
|
384
|
+
},
|
|
385
|
+
resume () {
|
|
386
|
+
this.paused = false
|
|
387
|
+
},
|
|
388
|
+
abort: (reason) => {
|
|
389
|
+
if (aborted) {
|
|
390
|
+
return
|
|
391
|
+
}
|
|
392
|
+
aborted = true
|
|
327
393
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
394
|
+
// Clear the pending delayed response if any
|
|
395
|
+
if (timer !== null) {
|
|
396
|
+
clearTimeout(timer)
|
|
397
|
+
timer = null
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
handler.onResponseError?.(controller, reason)
|
|
332
401
|
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
let replyOpts = opts
|
|
405
|
+
const dispatches = mockDispatches
|
|
333
406
|
|
|
334
|
-
|
|
335
|
-
|
|
407
|
+
// Call onRequestStart to allow the handler to receive the controller
|
|
408
|
+
handler.onRequestStart?.(controller, null)
|
|
409
|
+
|
|
410
|
+
if (aborted) {
|
|
411
|
+
return true
|
|
336
412
|
}
|
|
337
413
|
|
|
338
|
-
|
|
339
|
-
handler.onConnect?.(abort, null)
|
|
414
|
+
const requestBody = dispatchRequestBody(opts.body, handler, controller, () => aborted)
|
|
340
415
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
416
|
+
if (isPromise(requestBody)) {
|
|
417
|
+
requestBody.then((body) => {
|
|
418
|
+
if (body === requestAborted) {
|
|
419
|
+
return
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (body !== opts.body) {
|
|
423
|
+
replyOpts = { ...opts, body }
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
sendReply()
|
|
427
|
+
}, (error) => controller.abort(error))
|
|
428
|
+
return true
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (requestBody === requestAborted) {
|
|
432
|
+
return true
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (requestBody !== opts.body) {
|
|
436
|
+
replyOpts = { ...opts, body: requestBody }
|
|
349
437
|
}
|
|
350
438
|
|
|
351
|
-
|
|
439
|
+
sendReply()
|
|
440
|
+
|
|
441
|
+
function sendReply () {
|
|
442
|
+
if (response.callback) {
|
|
443
|
+
const { callback, ...responseDefaults } = response
|
|
444
|
+
let callbackResult
|
|
445
|
+
try {
|
|
446
|
+
callbackResult = callback(replyOpts)
|
|
447
|
+
} catch (err) {
|
|
448
|
+
deleteMockDispatch(mockDispatches, key)
|
|
449
|
+
handler.onResponseError(null, err)
|
|
450
|
+
return
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (isPromise(callbackResult)) {
|
|
454
|
+
callbackResult.then(
|
|
455
|
+
(resolvedData) => {
|
|
456
|
+
if (resolvedData == null || typeof resolvedData !== 'object') {
|
|
457
|
+
handler.onResponseError(null, new InvalidArgumentError('reply options callback must return an object'))
|
|
458
|
+
return
|
|
459
|
+
}
|
|
460
|
+
handleReply(dispatches, { ...responseDefaults, ...resolvedData })
|
|
461
|
+
},
|
|
462
|
+
(err) => {
|
|
463
|
+
handler.onResponseError(null, err)
|
|
464
|
+
}
|
|
465
|
+
)
|
|
466
|
+
return
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (callbackResult == null || typeof callbackResult !== 'object') {
|
|
470
|
+
throw new InvalidArgumentError('reply options callback must return an object')
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
handleReply(dispatches, { ...responseDefaults, ...callbackResult })
|
|
474
|
+
return
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Handle the request with a delay if necessary
|
|
478
|
+
if (typeof delay === 'number' && delay > 0) {
|
|
479
|
+
timer = setTimeout(() => {
|
|
480
|
+
timer = null
|
|
481
|
+
handleReply(dispatches)
|
|
482
|
+
}, delay)
|
|
483
|
+
} else {
|
|
484
|
+
handleReply(dispatches)
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function handleReply (mockDispatches, _response = response) {
|
|
352
489
|
// Don't send response if the request was aborted
|
|
353
490
|
if (aborted) {
|
|
354
491
|
return
|
|
355
492
|
}
|
|
356
493
|
|
|
494
|
+
const { statusCode, data, headers, trailers } = _response
|
|
495
|
+
|
|
357
496
|
// fetch's HeadersList is a 1D string array
|
|
358
497
|
const optsHeaders = Array.isArray(opts.headers)
|
|
359
498
|
? buildHeadersFromArray(opts.headers)
|
|
360
499
|
: opts.headers
|
|
361
|
-
const body = typeof
|
|
362
|
-
?
|
|
363
|
-
:
|
|
500
|
+
const body = typeof data === 'function'
|
|
501
|
+
? data({ ...replyOpts, headers: optsHeaders })
|
|
502
|
+
: data
|
|
364
503
|
|
|
365
504
|
// util.types.isPromise is likely needed for jest.
|
|
366
505
|
if (isPromise(body)) {
|
|
@@ -369,7 +508,7 @@ function mockDispatch (opts, handler) {
|
|
|
369
508
|
// synchronously throw the error, which breaks some tests.
|
|
370
509
|
// Rather, we wait for the callback to resolve if it is a
|
|
371
510
|
// promise, and then re-run handleReply with the new body.
|
|
372
|
-
return body.then((newData) => handleReply(mockDispatches, newData))
|
|
511
|
+
return body.then((newData) => handleReply(mockDispatches, { ..._response, data: newData }))
|
|
373
512
|
}
|
|
374
513
|
|
|
375
514
|
// Check again if aborted after async body resolution
|
|
@@ -378,20 +517,113 @@ function mockDispatch (opts, handler) {
|
|
|
378
517
|
}
|
|
379
518
|
|
|
380
519
|
const responseData = getResponseData(body)
|
|
381
|
-
const responseHeaders = generateKeyValues(headers)
|
|
382
|
-
const responseTrailers = generateKeyValues(trailers)
|
|
520
|
+
const responseHeaders = generateKeyValues(headers ?? {})
|
|
521
|
+
const responseTrailers = generateKeyValues(trailers ?? {})
|
|
522
|
+
|
|
523
|
+
// Update the controller with response data
|
|
524
|
+
controller.rawHeaders = responseHeaders
|
|
525
|
+
controller.rawTrailers = responseTrailers
|
|
383
526
|
|
|
384
|
-
handler.
|
|
385
|
-
handler.
|
|
386
|
-
handler.
|
|
527
|
+
handler.onResponseStart?.(controller, statusCode, parseHeaders(responseHeaders), getStatusText(statusCode))
|
|
528
|
+
handler.onResponseData?.(controller, Buffer.from(responseData))
|
|
529
|
+
handler.onResponseEnd?.(controller, parseHeaders(responseTrailers))
|
|
387
530
|
deleteMockDispatch(mockDispatches, key)
|
|
388
531
|
}
|
|
389
532
|
|
|
390
|
-
function resume () {}
|
|
391
|
-
|
|
392
533
|
return true
|
|
393
534
|
}
|
|
394
535
|
|
|
536
|
+
function dispatchRequestBody (body, handler, controller, isAborted) {
|
|
537
|
+
if (typeof handler.onBodySent !== 'function' && typeof handler.onRequestSent !== 'function') {
|
|
538
|
+
return body
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (body == null) {
|
|
542
|
+
return callOnRequestSent(handler, controller, isAborted) ? body : requestAborted
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (body && typeof body[Symbol.asyncIterator] === 'function') {
|
|
546
|
+
return dispatchAsyncIterableBody(body, handler, controller, isAborted)
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (isIterableBody(body)) {
|
|
550
|
+
const chunks = []
|
|
551
|
+
|
|
552
|
+
for (const chunk of body) {
|
|
553
|
+
if (isAborted()) {
|
|
554
|
+
return requestAborted
|
|
555
|
+
}
|
|
556
|
+
chunks.push(chunk)
|
|
557
|
+
if (!callOnBodySent(handler, controller, chunk) || isAborted()) {
|
|
558
|
+
return requestAborted
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return callOnRequestSent(handler, controller, isAborted) ? chunks : requestAborted
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (isAborted()) {
|
|
566
|
+
return requestAborted
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (!callOnBodySent(handler, controller, body)) {
|
|
570
|
+
return requestAborted
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return callOnRequestSent(handler, controller, isAborted) ? body : requestAborted
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
async function dispatchAsyncIterableBody (body, handler, controller, isAborted) {
|
|
577
|
+
const chunks = []
|
|
578
|
+
|
|
579
|
+
for await (const chunk of body) {
|
|
580
|
+
if (isAborted()) {
|
|
581
|
+
return requestAborted
|
|
582
|
+
}
|
|
583
|
+
chunks.push(chunk)
|
|
584
|
+
if (!callOnBodySent(handler, controller, chunk) || isAborted()) {
|
|
585
|
+
return requestAborted
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (!callOnRequestSent(handler, controller, isAborted)) {
|
|
590
|
+
return requestAborted
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return {
|
|
594
|
+
async * [Symbol.asyncIterator] () {
|
|
595
|
+
yield * chunks
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function callOnBodySent (handler, controller, chunk) {
|
|
601
|
+
try {
|
|
602
|
+
handler.onBodySent?.(chunk)
|
|
603
|
+
return true
|
|
604
|
+
} catch (error) {
|
|
605
|
+
controller.abort(error)
|
|
606
|
+
return false
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function callOnRequestSent (handler, controller, isAborted) {
|
|
611
|
+
try {
|
|
612
|
+
handler.onRequestSent?.()
|
|
613
|
+
return !isAborted()
|
|
614
|
+
} catch (error) {
|
|
615
|
+
controller.abort(error)
|
|
616
|
+
return false
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function isIterableBody (body) {
|
|
621
|
+
return typeof body !== 'string' &&
|
|
622
|
+
!Buffer.isBuffer(body) &&
|
|
623
|
+
!ArrayBuffer.isView(body) &&
|
|
624
|
+
typeof body[Symbol.iterator] === 'function'
|
|
625
|
+
}
|
|
626
|
+
|
|
395
627
|
function buildMockDispatch () {
|
|
396
628
|
const agent = this[kMockAgent]
|
|
397
629
|
const origin = this[kOrigin]
|
|
@@ -411,7 +643,9 @@ function buildMockDispatch () {
|
|
|
411
643
|
throw new MockNotMatchedError(`${error.message}: subsequent request to origin ${origin} was not allowed (net.connect disabled)${interceptsMessage}`)
|
|
412
644
|
}
|
|
413
645
|
if (checkNetConnect(netConnect, origin)) {
|
|
414
|
-
originalDispatch.call(this, opts
|
|
646
|
+
originalDispatch.call(this, '__mockAgentBodyForDispatch' in opts
|
|
647
|
+
? { ...opts, body: opts.__mockAgentBodyForDispatch }
|
|
648
|
+
: opts, handler)
|
|
415
649
|
} else {
|
|
416
650
|
throw new MockNotMatchedError(`${error.message}: subsequent request to origin ${origin} was not allowed (net.connect is not enabled for this origin)${interceptsMessage}`)
|
|
417
651
|
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
const Agent = require('../dispatcher/agent')
|
|
4
4
|
const MockAgent = require('./mock-agent')
|
|
5
5
|
const { SnapshotRecorder } = require('./snapshot-recorder')
|
|
6
|
-
const WrapHandler = require('../handler/wrap-handler')
|
|
7
6
|
const { InvalidArgumentError, UndiciError } = require('../core/errors')
|
|
7
|
+
const util = require('../core/util')
|
|
8
8
|
const { validateSnapshotMode } = require('./snapshot-utils')
|
|
9
9
|
|
|
10
10
|
const kSnapshotRecorder = Symbol('kSnapshotRecorder')
|
|
@@ -55,7 +55,9 @@ class SnapshotAgent extends MockAgent {
|
|
|
55
55
|
ignoreHeaders: opts.ignoreHeaders,
|
|
56
56
|
excludeHeaders: opts.excludeHeaders,
|
|
57
57
|
matchBody: opts.matchBody,
|
|
58
|
+
normalizeBody: opts.normalizeBody,
|
|
58
59
|
matchQuery: opts.matchQuery,
|
|
60
|
+
normalizeQuery: opts.normalizeQuery,
|
|
59
61
|
caseSensitive: opts.caseSensitive,
|
|
60
62
|
shouldRecord: opts.shouldRecord,
|
|
61
63
|
shouldPlayback: opts.shouldPlayback,
|
|
@@ -79,7 +81,6 @@ class SnapshotAgent extends MockAgent {
|
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
dispatch (opts, handler) {
|
|
82
|
-
handler = WrapHandler.wrap(handler)
|
|
83
84
|
const mode = this[kSnapshotMode]
|
|
84
85
|
|
|
85
86
|
// Check if URL should be excluded (pass through without mocking/recording)
|
|
@@ -107,8 +108,8 @@ class SnapshotAgent extends MockAgent {
|
|
|
107
108
|
} else {
|
|
108
109
|
// Playback mode but no snapshot found
|
|
109
110
|
const error = new UndiciError(`No snapshot found for ${opts.method || 'GET'} ${opts.path}`)
|
|
110
|
-
if (handler.
|
|
111
|
-
handler.
|
|
111
|
+
if (handler.onResponseError) {
|
|
112
|
+
handler.onResponseError(null, error)
|
|
112
113
|
return
|
|
113
114
|
}
|
|
114
115
|
throw error
|
|
@@ -173,6 +174,10 @@ class SnapshotAgent extends MockAgent {
|
|
|
173
174
|
})
|
|
174
175
|
.then(() => handler.onResponseEnd(controller, trailers))
|
|
175
176
|
.catch((error) => handler.onResponseError(controller, error))
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
onResponseError (controller, error) {
|
|
180
|
+
return handler.onResponseError(controller, error)
|
|
176
181
|
}
|
|
177
182
|
}
|
|
178
183
|
|
|
@@ -192,7 +197,12 @@ class SnapshotAgent extends MockAgent {
|
|
|
192
197
|
try {
|
|
193
198
|
const { response } = snapshot
|
|
194
199
|
|
|
200
|
+
const rawHeaders = response.headers ? util.toRawHeaders(response.headers) : []
|
|
201
|
+
const rawTrailers = response.trailers ? util.toRawHeaders(response.trailers) : []
|
|
202
|
+
|
|
195
203
|
const controller = {
|
|
204
|
+
rawHeaders,
|
|
205
|
+
rawTrailers,
|
|
196
206
|
pause () { },
|
|
197
207
|
resume () { },
|
|
198
208
|
abort (reason) {
|
|
@@ -206,7 +216,7 @@ class SnapshotAgent extends MockAgent {
|
|
|
206
216
|
|
|
207
217
|
handler.onRequestStart(controller)
|
|
208
218
|
|
|
209
|
-
handler.onResponseStart(controller, response.statusCode, response.headers)
|
|
219
|
+
handler.onResponseStart(controller, response.statusCode, response.headers, response.statusMessage)
|
|
210
220
|
|
|
211
221
|
// Body is always stored as base64 string
|
|
212
222
|
const body = Buffer.from(response.body, 'base64')
|
|
@@ -214,7 +224,7 @@ class SnapshotAgent extends MockAgent {
|
|
|
214
224
|
|
|
215
225
|
handler.onResponseEnd(controller, response.trailers)
|
|
216
226
|
} catch (error) {
|
|
217
|
-
handler.
|
|
227
|
+
handler.onResponseError?.(null, error)
|
|
218
228
|
}
|
|
219
229
|
}
|
|
220
230
|
|
|
@@ -344,7 +354,15 @@ class SnapshotAgent extends MockAgent {
|
|
|
344
354
|
* @returns {Promise<void>}
|
|
345
355
|
*/
|
|
346
356
|
async close () {
|
|
347
|
-
|
|
357
|
+
// In playback mode the recorder must not persist to disk. findSnapshot()
|
|
358
|
+
// mutates each matched snapshot's callCount, so saving on close would
|
|
359
|
+
// rewrite the snapshot file even though nothing new was recorded. Only
|
|
360
|
+
// record/update modes should write snapshots; playback just cleans up.
|
|
361
|
+
if (this[kSnapshotMode] === 'playback') {
|
|
362
|
+
this[kSnapshotRecorder].destroy()
|
|
363
|
+
} else {
|
|
364
|
+
await this[kSnapshotRecorder].close()
|
|
365
|
+
}
|
|
348
366
|
await this[kRealAgent]?.close()
|
|
349
367
|
await super.close()
|
|
350
368
|
}
|
|
@@ -46,7 +46,9 @@ const { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } =
|
|
|
46
46
|
* @property {Array<string>} [ignoreHeaders=[]] - Headers to ignore for matching
|
|
47
47
|
* @property {Array<string>} [excludeHeaders=[]] - Headers to exclude from matching
|
|
48
48
|
* @property {boolean} [matchBody=true] - Whether to match request body
|
|
49
|
-
* @property {
|
|
49
|
+
* @property {(body: string|Buffer|null|undefined) => string} [normalizeBody] - Function to normalize the body before matching (e.g. strip timestamps)
|
|
50
|
+
* @property {boolean} [matchQuery=true] - Whether to match query parameters
|
|
51
|
+
* @property {(query: URLSearchParams) => string} [normalizeQuery] - Function to normalize query parameters before matching (e.g. strip volatile params)
|
|
50
52
|
* @property {boolean} [caseSensitive=false] - Whether header matching is case-sensitive
|
|
51
53
|
*/
|
|
52
54
|
|
|
@@ -79,6 +81,37 @@ const { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } =
|
|
|
79
81
|
* @property {string} timestamp - ISO timestamp of when the snapshot was created
|
|
80
82
|
*/
|
|
81
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Normalizes the URL string used for request matching.
|
|
86
|
+
*
|
|
87
|
+
* @param {URL} url - Parsed request URL
|
|
88
|
+
* @param {boolean} matchQuery - Whether to include query parameters in matching
|
|
89
|
+
* @param {((query: URLSearchParams) => string)|undefined} normalizeQuery - Optional normalization function
|
|
90
|
+
* @returns {string} - URL string for hashing
|
|
91
|
+
*/
|
|
92
|
+
function normalizeUrlForMatching (url, matchQuery, normalizeQuery) {
|
|
93
|
+
if (matchQuery === false) return `${url.origin}${url.pathname}`
|
|
94
|
+
if (normalizeQuery) {
|
|
95
|
+
const normalized = String(normalizeQuery(url.searchParams) ?? '')
|
|
96
|
+
return normalized ? `${url.origin}${url.pathname}?${normalized}` : `${url.origin}${url.pathname}`
|
|
97
|
+
}
|
|
98
|
+
return url.toString()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Normalizes the body value used for request matching.
|
|
103
|
+
*
|
|
104
|
+
* @param {string|Buffer|null|undefined} body - Raw request body
|
|
105
|
+
* @param {boolean} matchBody - Whether to include the body in matching
|
|
106
|
+
* @param {((body: string|Buffer|null|undefined) => string)|undefined} normalizeBody - Optional normalization function
|
|
107
|
+
* @returns {string} - Body string for hashing
|
|
108
|
+
*/
|
|
109
|
+
function normalizeBodyForMatching (body, matchBody, normalizeBody) {
|
|
110
|
+
if (matchBody === false) return ''
|
|
111
|
+
if (normalizeBody) return String(normalizeBody(body) ?? '')
|
|
112
|
+
return body ? String(body) : ''
|
|
113
|
+
}
|
|
114
|
+
|
|
82
115
|
/**
|
|
83
116
|
* Formats a request for consistent snapshot storage
|
|
84
117
|
* Caches normalized headers to avoid repeated processing
|
|
@@ -99,9 +132,9 @@ function formatRequestKey (opts, headerFilters, matchOptions = {}) {
|
|
|
99
132
|
|
|
100
133
|
return {
|
|
101
134
|
method: opts.method || 'GET',
|
|
102
|
-
url: matchOptions.matchQuery
|
|
135
|
+
url: normalizeUrlForMatching(url, matchOptions.matchQuery, matchOptions.normalizeQuery),
|
|
103
136
|
headers: filterHeadersForMatching(normalized, headerFilters, matchOptions),
|
|
104
|
-
body:
|
|
137
|
+
body: normalizeBodyForMatching(opts.body, matchOptions.matchBody, matchOptions.normalizeBody)
|
|
105
138
|
}
|
|
106
139
|
}
|
|
107
140
|
|
|
@@ -250,7 +283,9 @@ class SnapshotRecorder {
|
|
|
250
283
|
ignoreHeaders: options.ignoreHeaders || [],
|
|
251
284
|
excludeHeaders: options.excludeHeaders || [],
|
|
252
285
|
matchBody: options.matchBody !== false, // default: true
|
|
286
|
+
normalizeBody: options.normalizeBody || undefined,
|
|
253
287
|
matchQuery: options.matchQuery !== false, // default: true
|
|
288
|
+
normalizeQuery: options.normalizeQuery || undefined,
|
|
254
289
|
caseSensitive: options.caseSensitive || false
|
|
255
290
|
}
|
|
256
291
|
|