@composio/client 0.1.0-alpha.9 → 2.0.0-rc.2
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/README.md +32 -354
- package/dist/api-key-revocation-BqiqlJh6.mjs +24 -0
- package/dist/auth-ClFXmN5T.mjs +17 -0
- package/dist/auth-configs-COkEdwlP.mjs +48 -0
- package/dist/clanker-1ptspWLR.mjs +23 -0
- package/dist/cli-BWTXVRT4.mjs +42 -0
- package/dist/client-D5v0_f-k.d.mts +22790 -0
- package/dist/config-BLBbhLe1.mjs +10 -0
- package/dist/config-DnyMhILW.d.mts +9 -0
- package/dist/connected-accounts-DK24qYIM.mjs +67 -0
- package/dist/consumer-Do3lg_vI.mjs +33 -0
- package/dist/custom-CH7n4Dyd.mjs +22 -0
- package/dist/files-DoNtYu2Q.mjs +29 -0
- package/dist/files-lfGzAnJ_.mjs +45 -0
- package/dist/generate-B7ClqhN0.mjs +22 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +1219 -0
- package/dist/link-NsFv9L3R.mjs +22 -0
- package/dist/logs-BhCTVSZE.mjs +36 -0
- package/dist/manage-7vQYn-gu.mjs +26 -0
- package/dist/mcp-r-SWyCDh.mjs +73 -0
- package/dist/migration-CBhnoN-Y.mjs +10 -0
- package/dist/migration-CKGKuRO3.d.mts +9 -0
- package/dist/org-C12wx9QM.mjs +44 -0
- package/dist/owner-D4ghEkrK.mjs +17 -0
- package/dist/project-By-OBe-4.mjs +28 -0
- package/dist/project-Cnr9eceg.mjs +24 -0
- package/dist/project-DgdqyoZJ.mjs +22 -0
- package/dist/project-DzrdSIis.mjs +42 -0
- package/dist/realtime-DHJnO2Ye.mjs +26 -0
- package/dist/request-zLX87M09.mjs +1423 -0
- package/dist/resources/api-key-revocation.d.mts +2 -0
- package/dist/resources/api-key-revocation.mjs +3 -0
- package/dist/resources/auth/auth.d.mts +2 -0
- package/dist/resources/auth/auth.mjs +3 -0
- package/dist/resources/auth/index.d.mts +2 -0
- package/dist/resources/auth/index.mjs +4 -0
- package/dist/resources/auth/session.d.mts +2 -0
- package/dist/resources/auth/session.mjs +3 -0
- package/dist/resources/auth-configs.d.mts +2 -0
- package/dist/resources/auth-configs.mjs +3 -0
- package/dist/resources/auth.d.mts +2 -0
- package/dist/resources/auth.mjs +5 -0
- package/dist/resources/cli/cli.d.mts +2 -0
- package/dist/resources/cli/cli.mjs +3 -0
- package/dist/resources/cli/index.d.mts +2 -0
- package/dist/resources/cli/index.mjs +4 -0
- package/dist/resources/cli/realtime.d.mts +2 -0
- package/dist/resources/cli/realtime.mjs +3 -0
- package/dist/resources/cli.d.mts +2 -0
- package/dist/resources/cli.mjs +5 -0
- package/dist/resources/connected-accounts.d.mts +2 -0
- package/dist/resources/connected-accounts.mjs +3 -0
- package/dist/resources/files.d.mts +2 -0
- package/dist/resources/files.mjs +3 -0
- package/dist/resources/index.d.mts +3 -0
- package/dist/resources/index.mjs +16 -0
- package/dist/resources/link.d.mts +2 -0
- package/dist/resources/link.mjs +3 -0
- package/dist/resources/logs/index.d.mts +2 -0
- package/dist/resources/logs/index.mjs +5 -0
- package/dist/resources/logs/logs.d.mts +2 -0
- package/dist/resources/logs/logs.mjs +3 -0
- package/dist/resources/logs/tools.d.mts +2 -0
- package/dist/resources/logs/tools.mjs +3 -0
- package/dist/resources/logs/triggers.d.mts +2 -0
- package/dist/resources/logs/triggers.mjs +3 -0
- package/dist/resources/logs.d.mts +2 -0
- package/dist/resources/logs.mjs +6 -0
- package/dist/resources/mcp/custom.d.mts +2 -0
- package/dist/resources/mcp/custom.mjs +3 -0
- package/dist/resources/mcp/generate.d.mts +2 -0
- package/dist/resources/mcp/generate.mjs +3 -0
- package/dist/resources/mcp/index.d.mts +2 -0
- package/dist/resources/mcp/index.mjs +5 -0
- package/dist/resources/mcp/mcp.d.mts +2 -0
- package/dist/resources/mcp/mcp.mjs +3 -0
- package/dist/resources/mcp.d.mts +2 -0
- package/dist/resources/mcp.mjs +6 -0
- package/dist/resources/migration.d.mts +2 -0
- package/dist/resources/migration.mjs +3 -0
- package/dist/resources/org/clanker.d.mts +2 -0
- package/dist/resources/org/clanker.mjs +3 -0
- package/dist/resources/org/consumer/consumer.d.mts +2 -0
- package/dist/resources/org/consumer/consumer.mjs +3 -0
- package/dist/resources/org/consumer/index.d.mts +2 -0
- package/dist/resources/org/consumer/index.mjs +4 -0
- package/dist/resources/org/consumer/project.d.mts +2 -0
- package/dist/resources/org/consumer/project.mjs +3 -0
- package/dist/resources/org/consumer.d.mts +2 -0
- package/dist/resources/org/consumer.mjs +5 -0
- package/dist/resources/org/index.d.mts +2 -0
- package/dist/resources/org/index.mjs +8 -0
- package/dist/resources/org/org.d.mts +2 -0
- package/dist/resources/org/org.mjs +3 -0
- package/dist/resources/org/owner/index.d.mts +2 -0
- package/dist/resources/org/owner/index.mjs +4 -0
- package/dist/resources/org/owner/owner.d.mts +2 -0
- package/dist/resources/org/owner/owner.mjs +3 -0
- package/dist/resources/org/owner/project.d.mts +2 -0
- package/dist/resources/org/owner/project.mjs +3 -0
- package/dist/resources/org/owner.d.mts +2 -0
- package/dist/resources/org/owner.mjs +5 -0
- package/dist/resources/org/project.d.mts +2 -0
- package/dist/resources/org/project.mjs +3 -0
- package/dist/resources/org/usage.d.mts +2 -0
- package/dist/resources/org/usage.mjs +3 -0
- package/dist/resources/org.d.mts +2 -0
- package/dist/resources/org.mjs +9 -0
- package/dist/resources/project/config.d.mts +2 -0
- package/dist/resources/project/config.mjs +3 -0
- package/dist/resources/project/index.d.mts +3 -0
- package/dist/resources/project/index.mjs +4 -0
- package/dist/resources/project/project.d.mts +2 -0
- package/dist/resources/project/project.mjs +3 -0
- package/dist/resources/project/usage.d.mts +2 -0
- package/dist/resources/project/usage.mjs +3 -0
- package/dist/resources/project.d.mts +3 -0
- package/dist/resources/project.mjs +5 -0
- package/dist/resources/tool-router/index.d.mts +2 -0
- package/dist/resources/tool-router/index.mjs +4 -0
- package/dist/resources/tool-router/session/files.d.mts +2 -0
- package/dist/resources/tool-router/session/files.mjs +3 -0
- package/dist/resources/tool-router/session/index.d.mts +2 -0
- package/dist/resources/tool-router/session/index.mjs +4 -0
- package/dist/resources/tool-router/session/session.d.mts +2 -0
- package/dist/resources/tool-router/session/session.mjs +3 -0
- package/dist/resources/tool-router/session.d.mts +2 -0
- package/dist/resources/tool-router/session.mjs +5 -0
- package/dist/resources/tool-router/tool-router.d.mts +2 -0
- package/dist/resources/tool-router/tool-router.mjs +3 -0
- package/dist/resources/tool-router.d.mts +2 -0
- package/dist/resources/tool-router.mjs +5 -0
- package/dist/resources/toolkits.d.mts +2 -0
- package/dist/resources/toolkits.mjs +3 -0
- package/dist/resources/tools.d.mts +2 -0
- package/dist/resources/tools.mjs +3 -0
- package/dist/resources/trigger-instances/index.d.mts +2 -0
- package/dist/resources/trigger-instances/index.mjs +4 -0
- package/dist/resources/trigger-instances/manage.d.mts +2 -0
- package/dist/resources/trigger-instances/manage.mjs +3 -0
- package/dist/resources/trigger-instances/trigger-instances.d.mts +2 -0
- package/dist/resources/trigger-instances/trigger-instances.mjs +3 -0
- package/dist/resources/trigger-instances.d.mts +2 -0
- package/dist/resources/trigger-instances.mjs +5 -0
- package/dist/resources/triggers-types.d.mts +2 -0
- package/dist/resources/triggers-types.mjs +3 -0
- package/dist/resources/webhook-endpoints.d.mts +2 -0
- package/dist/resources/webhook-endpoints.mjs +3 -0
- package/dist/resources/webhook-subscriptions.d.mts +2 -0
- package/dist/resources/webhook-subscriptions.mjs +3 -0
- package/dist/session-Ci0MoTTn.mjs +21 -0
- package/dist/session-CmqSfXtq.mjs +97 -0
- package/dist/tool-router-DbmIxiU5.mjs +14 -0
- package/dist/toolkits-BUWB3p_C.mjs +43 -0
- package/dist/tools-B4kHPHbe.mjs +30 -0
- package/dist/tools-BO1wTIiE.mjs +60 -0
- package/dist/trigger-instances-CoEohjcv.mjs +33 -0
- package/dist/triggers-BwsETpsM.mjs +26 -0
- package/dist/triggers-types-DmOurxxx.mjs +31 -0
- package/dist/usage-BJsJ4nsd.mjs +29 -0
- package/dist/usage-D6ahTV_Y.mjs +29 -0
- package/dist/webhook-endpoints-CyWYVPMe.mjs +46 -0
- package/dist/webhook-subscriptions-BgnCTUGn.mjs +48 -0
- package/package.json +74 -129
- package/CHANGELOG.md +0 -96
- package/LICENSE +0 -201
- package/api-promise.d.mts +0 -2
- package/api-promise.d.mts.map +0 -1
- package/api-promise.d.ts +0 -2
- package/api-promise.d.ts.map +0 -1
- package/api-promise.js +0 -19
- package/api-promise.js.map +0 -1
- package/api-promise.mjs +0 -2
- package/api-promise.mjs.map +0 -1
- package/client.d.mts +0 -224
- package/client.d.mts.map +0 -1
- package/client.d.ts +0 -224
- package/client.d.ts.map +0 -1
- package/client.js +0 -502
- package/client.js.map +0 -1
- package/client.mjs +0 -475
- package/client.mjs.map +0 -1
- package/core/api-promise.d.mts +0 -47
- package/core/api-promise.d.mts.map +0 -1
- package/core/api-promise.d.ts +0 -47
- package/core/api-promise.d.ts.map +0 -1
- package/core/api-promise.js +0 -84
- package/core/api-promise.js.map +0 -1
- package/core/api-promise.mjs +0 -80
- package/core/api-promise.mjs.map +0 -1
- package/core/error.d.mts +0 -47
- package/core/error.d.mts.map +0 -1
- package/core/error.d.ts +0 -47
- package/core/error.d.ts.map +0 -1
- package/core/error.js +0 -113
- package/core/error.js.map +0 -1
- package/core/error.mjs +0 -97
- package/core/error.mjs.map +0 -1
- package/core/resource.d.mts +0 -6
- package/core/resource.d.mts.map +0 -1
- package/core/resource.d.ts +0 -6
- package/core/resource.d.ts.map +0 -1
- package/core/resource.js +0 -11
- package/core/resource.js.map +0 -1
- package/core/resource.mjs +0 -7
- package/core/resource.mjs.map +0 -1
- package/core/uploads.d.mts +0 -3
- package/core/uploads.d.mts.map +0 -1
- package/core/uploads.d.ts +0 -3
- package/core/uploads.d.ts.map +0 -1
- package/core/uploads.js +0 -6
- package/core/uploads.js.map +0 -1
- package/core/uploads.mjs +0 -2
- package/core/uploads.mjs.map +0 -1
- package/error.d.mts +0 -2
- package/error.d.mts.map +0 -1
- package/error.d.ts +0 -2
- package/error.d.ts.map +0 -1
- package/error.js +0 -19
- package/error.js.map +0 -1
- package/error.mjs +0 -2
- package/error.mjs.map +0 -1
- package/index.d.mts +0 -6
- package/index.d.mts.map +0 -1
- package/index.d.ts +0 -6
- package/index.d.ts.map +0 -1
- package/index.js +0 -30
- package/index.js.map +0 -1
- package/index.mjs +0 -7
- package/index.mjs.map +0 -1
- package/internal/builtin-types.d.mts +0 -75
- package/internal/builtin-types.d.mts.map +0 -1
- package/internal/builtin-types.d.ts +0 -75
- package/internal/builtin-types.d.ts.map +0 -1
- package/internal/builtin-types.js +0 -4
- package/internal/builtin-types.js.map +0 -1
- package/internal/builtin-types.mjs +0 -3
- package/internal/builtin-types.mjs.map +0 -1
- package/internal/detect-platform.d.mts +0 -15
- package/internal/detect-platform.d.mts.map +0 -1
- package/internal/detect-platform.d.ts +0 -15
- package/internal/detect-platform.d.ts.map +0 -1
- package/internal/detect-platform.js +0 -162
- package/internal/detect-platform.js.map +0 -1
- package/internal/detect-platform.mjs +0 -157
- package/internal/detect-platform.mjs.map +0 -1
- package/internal/errors.d.mts +0 -3
- package/internal/errors.d.mts.map +0 -1
- package/internal/errors.d.ts +0 -3
- package/internal/errors.d.ts.map +0 -1
- package/internal/errors.js +0 -41
- package/internal/errors.js.map +0 -1
- package/internal/errors.mjs +0 -36
- package/internal/errors.mjs.map +0 -1
- package/internal/headers.d.mts +0 -21
- package/internal/headers.d.mts.map +0 -1
- package/internal/headers.d.ts +0 -21
- package/internal/headers.d.ts.map +0 -1
- package/internal/headers.js +0 -77
- package/internal/headers.js.map +0 -1
- package/internal/headers.mjs +0 -72
- package/internal/headers.mjs.map +0 -1
- package/internal/parse.d.mts +0 -13
- package/internal/parse.d.mts.map +0 -1
- package/internal/parse.d.ts +0 -13
- package/internal/parse.d.ts.map +0 -1
- package/internal/parse.js +0 -36
- package/internal/parse.js.map +0 -1
- package/internal/parse.mjs +0 -32
- package/internal/parse.mjs.map +0 -1
- package/internal/qs/formats.d.mts +0 -6
- package/internal/qs/formats.d.mts.map +0 -1
- package/internal/qs/formats.d.ts +0 -6
- package/internal/qs/formats.d.ts.map +0 -1
- package/internal/qs/formats.js +0 -11
- package/internal/qs/formats.js.map +0 -1
- package/internal/qs/formats.mjs +0 -8
- package/internal/qs/formats.mjs.map +0 -1
- package/internal/qs/index.d.mts +0 -10
- package/internal/qs/index.d.mts.map +0 -1
- package/internal/qs/index.d.ts +0 -10
- package/internal/qs/index.d.ts.map +0 -1
- package/internal/qs/index.js +0 -14
- package/internal/qs/index.js.map +0 -1
- package/internal/qs/index.mjs +0 -10
- package/internal/qs/index.mjs.map +0 -1
- package/internal/qs/stringify.d.mts +0 -3
- package/internal/qs/stringify.d.mts.map +0 -1
- package/internal/qs/stringify.d.ts +0 -3
- package/internal/qs/stringify.d.ts.map +0 -1
- package/internal/qs/stringify.js +0 -280
- package/internal/qs/stringify.js.map +0 -1
- package/internal/qs/stringify.mjs +0 -276
- package/internal/qs/stringify.mjs.map +0 -1
- package/internal/qs/types.d.mts +0 -57
- package/internal/qs/types.d.mts.map +0 -1
- package/internal/qs/types.d.ts +0 -57
- package/internal/qs/types.d.ts.map +0 -1
- package/internal/qs/types.js +0 -3
- package/internal/qs/types.js.map +0 -1
- package/internal/qs/types.mjs +0 -2
- package/internal/qs/types.mjs.map +0 -1
- package/internal/qs/utils.d.mts +0 -14
- package/internal/qs/utils.d.mts.map +0 -1
- package/internal/qs/utils.d.ts +0 -14
- package/internal/qs/utils.d.ts.map +0 -1
- package/internal/qs/utils.js +0 -229
- package/internal/qs/utils.js.map +0 -1
- package/internal/qs/utils.mjs +0 -217
- package/internal/qs/utils.mjs.map +0 -1
- package/internal/request-options.d.mts +0 -33
- package/internal/request-options.d.mts.map +0 -1
- package/internal/request-options.d.ts +0 -33
- package/internal/request-options.d.ts.map +0 -1
- package/internal/request-options.js +0 -14
- package/internal/request-options.js.map +0 -1
- package/internal/request-options.mjs +0 -10
- package/internal/request-options.mjs.map +0 -1
- package/internal/shim-types.d.mts +0 -28
- package/internal/shim-types.d.ts +0 -28
- package/internal/shims/crypto.d.mts +0 -13
- package/internal/shims/crypto.d.mts.map +0 -1
- package/internal/shims/crypto.d.ts +0 -13
- package/internal/shims/crypto.d.ts.map +0 -1
- package/internal/shims/crypto.js +0 -13
- package/internal/shims/crypto.js.map +0 -1
- package/internal/shims/crypto.mjs +0 -9
- package/internal/shims/crypto.mjs.map +0 -1
- package/internal/shims/file.d.mts +0 -23
- package/internal/shims/file.d.mts.map +0 -1
- package/internal/shims/file.d.ts +0 -23
- package/internal/shims/file.d.ts.map +0 -1
- package/internal/shims/file.js +0 -16
- package/internal/shims/file.js.map +0 -1
- package/internal/shims/file.mjs +0 -12
- package/internal/shims/file.mjs.map +0 -1
- package/internal/shims/getBuiltinModule.d.mts +0 -11
- package/internal/shims/getBuiltinModule.d.mts.map +0 -1
- package/internal/shims/getBuiltinModule.d.ts +0 -11
- package/internal/shims/getBuiltinModule.d.ts.map +0 -1
- package/internal/shims/getBuiltinModule.js +0 -71
- package/internal/shims/getBuiltinModule.js.map +0 -1
- package/internal/shims/getBuiltinModule.mjs +0 -67
- package/internal/shims/getBuiltinModule.mjs.map +0 -1
- package/internal/shims/nullGetBuiltinModule.d.mts +0 -2
- package/internal/shims/nullGetBuiltinModule.d.mts.map +0 -1
- package/internal/shims/nullGetBuiltinModule.d.ts +0 -2
- package/internal/shims/nullGetBuiltinModule.d.ts.map +0 -1
- package/internal/shims/nullGetBuiltinModule.js +0 -5
- package/internal/shims/nullGetBuiltinModule.js.map +0 -1
- package/internal/shims/nullGetBuiltinModule.mjs +0 -2
- package/internal/shims/nullGetBuiltinModule.mjs.map +0 -1
- package/internal/shims.d.mts +0 -20
- package/internal/shims.d.mts.map +0 -1
- package/internal/shims.d.ts +0 -20
- package/internal/shims.d.ts.map +0 -1
- package/internal/shims.js +0 -93
- package/internal/shims.js.map +0 -1
- package/internal/shims.mjs +0 -85
- package/internal/shims.mjs.map +0 -1
- package/internal/to-file.d.mts +0 -46
- package/internal/to-file.d.mts.map +0 -1
- package/internal/to-file.d.ts +0 -46
- package/internal/to-file.d.ts.map +0 -1
- package/internal/to-file.js +0 -91
- package/internal/to-file.js.map +0 -1
- package/internal/to-file.mjs +0 -87
- package/internal/to-file.mjs.map +0 -1
- package/internal/types.d.mts +0 -68
- package/internal/types.d.mts.map +0 -1
- package/internal/types.d.ts +0 -68
- package/internal/types.d.ts.map +0 -1
- package/internal/types.js +0 -4
- package/internal/types.js.map +0 -1
- package/internal/types.mjs +0 -3
- package/internal/types.mjs.map +0 -1
- package/internal/uploads.d.mts +0 -44
- package/internal/uploads.d.mts.map +0 -1
- package/internal/uploads.d.ts +0 -44
- package/internal/uploads.d.ts.map +0 -1
- package/internal/uploads.js +0 -131
- package/internal/uploads.js.map +0 -1
- package/internal/uploads.mjs +0 -122
- package/internal/uploads.mjs.map +0 -1
- package/internal/utils/base64.d.mts +0 -3
- package/internal/utils/base64.d.mts.map +0 -1
- package/internal/utils/base64.d.ts +0 -3
- package/internal/utils/base64.d.ts.map +0 -1
- package/internal/utils/base64.js +0 -37
- package/internal/utils/base64.js.map +0 -1
- package/internal/utils/base64.mjs +0 -32
- package/internal/utils/base64.mjs.map +0 -1
- package/internal/utils/env.d.mts +0 -9
- package/internal/utils/env.d.mts.map +0 -1
- package/internal/utils/env.d.ts +0 -9
- package/internal/utils/env.d.ts.map +0 -1
- package/internal/utils/env.js +0 -22
- package/internal/utils/env.js.map +0 -1
- package/internal/utils/env.mjs +0 -18
- package/internal/utils/env.mjs.map +0 -1
- package/internal/utils/log.d.mts +0 -38
- package/internal/utils/log.d.mts.map +0 -1
- package/internal/utils/log.d.ts +0 -38
- package/internal/utils/log.d.ts.map +0 -1
- package/internal/utils/log.js +0 -86
- package/internal/utils/log.js.map +0 -1
- package/internal/utils/log.mjs +0 -80
- package/internal/utils/log.mjs.map +0 -1
- package/internal/utils/path.d.mts +0 -15
- package/internal/utils/path.d.mts.map +0 -1
- package/internal/utils/path.d.ts +0 -15
- package/internal/utils/path.d.ts.map +0 -1
- package/internal/utils/path.js +0 -58
- package/internal/utils/path.js.map +0 -1
- package/internal/utils/path.mjs +0 -53
- package/internal/utils/path.mjs.map +0 -1
- package/internal/utils/sleep.d.mts +0 -2
- package/internal/utils/sleep.d.mts.map +0 -1
- package/internal/utils/sleep.d.ts +0 -2
- package/internal/utils/sleep.d.ts.map +0 -1
- package/internal/utils/sleep.js +0 -7
- package/internal/utils/sleep.js.map +0 -1
- package/internal/utils/sleep.mjs +0 -3
- package/internal/utils/sleep.mjs.map +0 -1
- package/internal/utils/uuid.d.mts +0 -5
- package/internal/utils/uuid.d.mts.map +0 -1
- package/internal/utils/uuid.d.ts +0 -5
- package/internal/utils/uuid.d.ts.map +0 -1
- package/internal/utils/uuid.js +0 -20
- package/internal/utils/uuid.js.map +0 -1
- package/internal/utils/uuid.mjs +0 -16
- package/internal/utils/uuid.mjs.map +0 -1
- package/internal/utils/values.d.mts +0 -16
- package/internal/utils/values.d.mts.map +0 -1
- package/internal/utils/values.d.ts +0 -16
- package/internal/utils/values.d.ts.map +0 -1
- package/internal/utils/values.js +0 -109
- package/internal/utils/values.js.map +0 -1
- package/internal/utils/values.mjs +0 -92
- package/internal/utils/values.mjs.map +0 -1
- package/internal/utils.d.mts +0 -7
- package/internal/utils.d.mts.map +0 -1
- package/internal/utils.d.ts +0 -7
- package/internal/utils.d.ts.map +0 -1
- package/internal/utils.js +0 -24
- package/internal/utils.js.map +0 -1
- package/internal/utils.mjs +0 -8
- package/internal/utils.mjs.map +0 -1
- package/resource.d.mts +0 -2
- package/resource.d.mts.map +0 -1
- package/resource.d.ts +0 -2
- package/resource.d.ts.map +0 -1
- package/resource.js +0 -19
- package/resource.js.map +0 -1
- package/resource.mjs +0 -2
- package/resource.mjs.map +0 -1
- package/resources/action-execution.d.mts +0 -155
- package/resources/action-execution.d.mts.map +0 -1
- package/resources/action-execution.d.ts +0 -155
- package/resources/action-execution.d.ts.map +0 -1
- package/resources/action-execution.js +0 -19
- package/resources/action-execution.js.map +0 -1
- package/resources/action-execution.mjs +0 -15
- package/resources/action-execution.mjs.map +0 -1
- package/resources/auth/auth.d.mts +0 -10
- package/resources/auth/auth.d.mts.map +0 -1
- package/resources/auth/auth.d.ts +0 -10
- package/resources/auth/auth.d.ts.map +0 -1
- package/resources/auth/auth.js +0 -39
- package/resources/auth/auth.js.map +0 -1
- package/resources/auth/auth.mjs +0 -12
- package/resources/auth/auth.mjs.map +0 -1
- package/resources/auth/index.d.mts +0 -3
- package/resources/auth/index.d.mts.map +0 -1
- package/resources/auth/index.d.ts +0 -3
- package/resources/auth/index.d.ts.map +0 -1
- package/resources/auth/index.js +0 -9
- package/resources/auth/index.js.map +0 -1
- package/resources/auth/index.mjs +0 -4
- package/resources/auth/index.mjs.map +0 -1
- package/resources/auth/session.d.mts +0 -75
- package/resources/auth/session.d.mts.map +0 -1
- package/resources/auth/session.d.ts +0 -75
- package/resources/auth/session.d.ts.map +0 -1
- package/resources/auth/session.js +0 -12
- package/resources/auth/session.js.map +0 -1
- package/resources/auth/session.mjs +0 -8
- package/resources/auth/session.mjs.map +0 -1
- package/resources/auth-configs.d.mts +0 -217
- package/resources/auth-configs.d.mts.map +0 -1
- package/resources/auth-configs.d.ts +0 -217
- package/resources/auth-configs.d.ts.map +0 -1
- package/resources/auth-configs.js +0 -29
- package/resources/auth-configs.js.map +0 -1
- package/resources/auth-configs.mjs +0 -25
- package/resources/auth-configs.mjs.map +0 -1
- package/resources/auth.d.mts +0 -2
- package/resources/auth.d.mts.map +0 -1
- package/resources/auth.d.ts +0 -2
- package/resources/auth.d.ts.map +0 -1
- package/resources/auth.js +0 -19
- package/resources/auth.js.map +0 -1
- package/resources/auth.mjs +0 -3
- package/resources/auth.mjs.map +0 -1
- package/resources/cli.d.mts +0 -124
- package/resources/cli.d.mts.map +0 -1
- package/resources/cli.d.ts +0 -124
- package/resources/cli.d.ts.map +0 -1
- package/resources/cli.js +0 -18
- package/resources/cli.js.map +0 -1
- package/resources/cli.mjs +0 -14
- package/resources/cli.mjs.map +0 -1
- package/resources/connected-accounts.d.mts +0 -449
- package/resources/connected-accounts.d.mts.map +0 -1
- package/resources/connected-accounts.d.ts +0 -449
- package/resources/connected-accounts.d.ts.map +0 -1
- package/resources/connected-accounts.js +0 -28
- package/resources/connected-accounts.js.map +0 -1
- package/resources/connected-accounts.mjs +0 -24
- package/resources/connected-accounts.mjs.map +0 -1
- package/resources/index.d.mts +0 -14
- package/resources/index.d.mts.map +0 -1
- package/resources/index.d.ts +0 -14
- package/resources/index.d.ts.map +0 -1
- package/resources/index.js +0 -31
- package/resources/index.js.map +0 -1
- package/resources/index.mjs +0 -15
- package/resources/index.mjs.map +0 -1
- package/resources/mcp/command.d.mts +0 -34
- package/resources/mcp/command.d.mts.map +0 -1
- package/resources/mcp/command.d.ts +0 -34
- package/resources/mcp/command.d.ts.map +0 -1
- package/resources/mcp/command.js +0 -12
- package/resources/mcp/command.js.map +0 -1
- package/resources/mcp/command.mjs +0 -8
- package/resources/mcp/command.mjs.map +0 -1
- package/resources/mcp/index.d.mts +0 -3
- package/resources/mcp/index.d.mts.map +0 -1
- package/resources/mcp/index.d.ts +0 -3
- package/resources/mcp/index.d.ts.map +0 -1
- package/resources/mcp/index.js +0 -9
- package/resources/mcp/index.js.map +0 -1
- package/resources/mcp/index.mjs +0 -4
- package/resources/mcp/index.mjs.map +0 -1
- package/resources/mcp/mcp.d.mts +0 -378
- package/resources/mcp/mcp.d.mts.map +0 -1
- package/resources/mcp/mcp.d.ts +0 -378
- package/resources/mcp/mcp.d.ts.map +0 -1
- package/resources/mcp/mcp.js +0 -66
- package/resources/mcp/mcp.js.map +0 -1
- package/resources/mcp/mcp.mjs +0 -39
- package/resources/mcp/mcp.mjs.map +0 -1
- package/resources/mcp.d.mts +0 -2
- package/resources/mcp.d.mts.map +0 -1
- package/resources/mcp.d.ts +0 -2
- package/resources/mcp.d.ts.map +0 -1
- package/resources/mcp.js +0 -19
- package/resources/mcp.js.map +0 -1
- package/resources/mcp.mjs +0 -3
- package/resources/mcp.mjs.map +0 -1
- package/resources/org/api-key.d.mts +0 -17
- package/resources/org/api-key.d.mts.map +0 -1
- package/resources/org/api-key.d.ts +0 -17
- package/resources/org/api-key.d.ts.map +0 -1
- package/resources/org/api-key.js +0 -15
- package/resources/org/api-key.js.map +0 -1
- package/resources/org/api-key.mjs +0 -11
- package/resources/org/api-key.mjs.map +0 -1
- package/resources/org/index.d.mts +0 -4
- package/resources/org/index.d.mts.map +0 -1
- package/resources/org/index.d.ts +0 -4
- package/resources/org/index.d.ts.map +0 -1
- package/resources/org/index.js +0 -11
- package/resources/org/index.js.map +0 -1
- package/resources/org/index.mjs +0 -5
- package/resources/org/index.mjs.map +0 -1
- package/resources/org/org.d.mts +0 -14
- package/resources/org/org.d.mts.map +0 -1
- package/resources/org/org.d.ts +0 -14
- package/resources/org/org.d.ts.map +0 -1
- package/resources/org/org.js +0 -43
- package/resources/org/org.js.map +0 -1
- package/resources/org/org.mjs +0 -16
- package/resources/org/org.mjs.map +0 -1
- package/resources/org/project/api-keys.d.mts +0 -109
- package/resources/org/project/api-keys.d.mts.map +0 -1
- package/resources/org/project/api-keys.d.ts +0 -109
- package/resources/org/project/api-keys.d.ts.map +0 -1
- package/resources/org/project/api-keys.js +0 -23
- package/resources/org/project/api-keys.js.map +0 -1
- package/resources/org/project/api-keys.mjs +0 -19
- package/resources/org/project/api-keys.mjs.map +0 -1
- package/resources/org/project/index.d.mts +0 -5
- package/resources/org/project/index.d.mts.map +0 -1
- package/resources/org/project/index.d.ts +0 -5
- package/resources/org/project/index.d.ts.map +0 -1
- package/resources/org/project/index.js +0 -13
- package/resources/org/project/index.js.map +0 -1
- package/resources/org/project/index.mjs +0 -6
- package/resources/org/project/index.mjs.map +0 -1
- package/resources/org/project/project.d.mts +0 -90
- package/resources/org/project/project.d.mts.map +0 -1
- package/resources/org/project/project.d.ts +0 -90
- package/resources/org/project/project.d.ts.map +0 -1
- package/resources/org/project/project.js +0 -60
- package/resources/org/project/project.js.map +0 -1
- package/resources/org/project/project.mjs +0 -33
- package/resources/org/project/project.mjs.map +0 -1
- package/resources/org/project/trigger.d.mts +0 -51
- package/resources/org/project/trigger.d.mts.map +0 -1
- package/resources/org/project/trigger.d.ts +0 -51
- package/resources/org/project/trigger.d.ts.map +0 -1
- package/resources/org/project/trigger.js +0 -16
- package/resources/org/project/trigger.js.map +0 -1
- package/resources/org/project/trigger.mjs +0 -12
- package/resources/org/project/trigger.mjs.map +0 -1
- package/resources/org/project/webhook.d.mts +0 -92
- package/resources/org/project/webhook.d.mts.map +0 -1
- package/resources/org/project/webhook.d.ts +0 -92
- package/resources/org/project/webhook.d.ts.map +0 -1
- package/resources/org/project/webhook.js +0 -24
- package/resources/org/project/webhook.js.map +0 -1
- package/resources/org/project/webhook.mjs +0 -20
- package/resources/org/project/webhook.mjs.map +0 -1
- package/resources/org/project.d.mts +0 -2
- package/resources/org/project.d.mts.map +0 -1
- package/resources/org/project.d.ts +0 -2
- package/resources/org/project.d.ts.map +0 -1
- package/resources/org/project.js +0 -19
- package/resources/org/project.js.map +0 -1
- package/resources/org/project.mjs +0 -3
- package/resources/org/project.mjs.map +0 -1
- package/resources/org.d.mts +0 -2
- package/resources/org.d.mts.map +0 -1
- package/resources/org.d.ts +0 -2
- package/resources/org.d.ts.map +0 -1
- package/resources/org.js +0 -19
- package/resources/org.js.map +0 -1
- package/resources/org.mjs +0 -3
- package/resources/org.mjs.map +0 -1
- package/resources/team-members.d.mts +0 -57
- package/resources/team-members.d.mts.map +0 -1
- package/resources/team-members.d.ts +0 -57
- package/resources/team-members.d.ts.map +0 -1
- package/resources/team-members.js +0 -22
- package/resources/team-members.js.map +0 -1
- package/resources/team-members.mjs +0 -18
- package/resources/team-members.mjs.map +0 -1
- package/resources/toolkits.d.mts +0 -141
- package/resources/toolkits.d.mts.map +0 -1
- package/resources/toolkits.d.ts +0 -141
- package/resources/toolkits.d.ts.map +0 -1
- package/resources/toolkits.js +0 -19
- package/resources/toolkits.js.map +0 -1
- package/resources/toolkits.mjs +0 -15
- package/resources/toolkits.mjs.map +0 -1
- package/resources/tools.d.mts +0 -210
- package/resources/tools.d.mts.map +0 -1
- package/resources/tools.d.ts +0 -210
- package/resources/tools.d.ts.map +0 -1
- package/resources/tools.js +0 -28
- package/resources/tools.js.map +0 -1
- package/resources/tools.mjs +0 -24
- package/resources/tools.mjs.map +0 -1
- package/resources/trigger-instances/handle.d.mts +0 -25
- package/resources/trigger-instances/handle.d.mts.map +0 -1
- package/resources/trigger-instances/handle.d.ts +0 -25
- package/resources/trigger-instances/handle.d.ts.map +0 -1
- package/resources/trigger-instances/handle.js +0 -18
- package/resources/trigger-instances/handle.js.map +0 -1
- package/resources/trigger-instances/handle.mjs +0 -14
- package/resources/trigger-instances/handle.mjs.map +0 -1
- package/resources/trigger-instances/index.d.mts +0 -3
- package/resources/trigger-instances/index.d.mts.map +0 -1
- package/resources/trigger-instances/index.d.ts +0 -3
- package/resources/trigger-instances/index.d.ts.map +0 -1
- package/resources/trigger-instances/index.js +0 -9
- package/resources/trigger-instances/index.js.map +0 -1
- package/resources/trigger-instances/index.mjs +0 -4
- package/resources/trigger-instances/index.mjs.map +0 -1
- package/resources/trigger-instances/trigger-instances.d.mts +0 -126
- package/resources/trigger-instances/trigger-instances.d.mts.map +0 -1
- package/resources/trigger-instances/trigger-instances.d.ts +0 -126
- package/resources/trigger-instances/trigger-instances.d.ts.map +0 -1
- package/resources/trigger-instances/trigger-instances.js +0 -53
- package/resources/trigger-instances/trigger-instances.js.map +0 -1
- package/resources/trigger-instances/trigger-instances.mjs +0 -26
- package/resources/trigger-instances/trigger-instances.mjs.map +0 -1
- package/resources/trigger-instances.d.mts +0 -2
- package/resources/trigger-instances.d.mts.map +0 -1
- package/resources/trigger-instances.d.ts +0 -2
- package/resources/trigger-instances.d.ts.map +0 -1
- package/resources/trigger-instances.js +0 -19
- package/resources/trigger-instances.js.map +0 -1
- package/resources/trigger-instances.mjs +0 -3
- package/resources/trigger-instances.mjs.map +0 -1
- package/resources/trigger.d.mts +0 -73
- package/resources/trigger.d.mts.map +0 -1
- package/resources/trigger.d.ts +0 -73
- package/resources/trigger.d.ts.map +0 -1
- package/resources/trigger.js +0 -12
- package/resources/trigger.js.map +0 -1
- package/resources/trigger.mjs +0 -8
- package/resources/trigger.mjs.map +0 -1
- package/resources/triggers-types.d.mts +0 -59
- package/resources/triggers-types.d.mts.map +0 -1
- package/resources/triggers-types.d.ts +0 -59
- package/resources/triggers-types.d.ts.map +0 -1
- package/resources/triggers-types.js +0 -19
- package/resources/triggers-types.js.map +0 -1
- package/resources/triggers-types.mjs +0 -15
- package/resources/triggers-types.mjs.map +0 -1
- package/resources.d.mts +0 -2
- package/resources.d.mts.map +0 -1
- package/resources.d.ts +0 -2
- package/resources.d.ts.map +0 -1
- package/resources.js +0 -18
- package/resources.js.map +0 -1
- package/resources.mjs +0 -2
- package/resources.mjs.map +0 -1
- package/src/api-promise.ts +0 -2
- package/src/client.ts +0 -938
- package/src/core/README.md +0 -3
- package/src/core/api-promise.ts +0 -92
- package/src/core/error.ts +0 -130
- package/src/core/resource.ts +0 -11
- package/src/core/uploads.ts +0 -2
- package/src/error.ts +0 -2
- package/src/index.ts +0 -22
- package/src/internal/README.md +0 -3
- package/src/internal/builtin-types.ts +0 -93
- package/src/internal/detect-platform.ts +0 -196
- package/src/internal/errors.ts +0 -33
- package/src/internal/headers.ts +0 -96
- package/src/internal/parse.ts +0 -50
- package/src/internal/qs/LICENSE.md +0 -13
- package/src/internal/qs/README.md +0 -3
- package/src/internal/qs/formats.ts +0 -9
- package/src/internal/qs/index.ts +0 -13
- package/src/internal/qs/stringify.ts +0 -388
- package/src/internal/qs/types.ts +0 -71
- package/src/internal/qs/utils.ts +0 -265
- package/src/internal/request-options.ts +0 -37
- package/src/internal/shim-types.d.ts +0 -28
- package/src/internal/shims/crypto.ts +0 -18
- package/src/internal/shims/file.ts +0 -32
- package/src/internal/shims/getBuiltinModule.ts +0 -66
- package/src/internal/shims/nullGetBuiltinModule.ts +0 -1
- package/src/internal/shims.ts +0 -107
- package/src/internal/to-file.ts +0 -152
- package/src/internal/types.ts +0 -92
- package/src/internal/uploads.ts +0 -175
- package/src/internal/utils/base64.ts +0 -39
- package/src/internal/utils/env.ts +0 -18
- package/src/internal/utils/log.ts +0 -127
- package/src/internal/utils/path.ts +0 -63
- package/src/internal/utils/sleep.ts +0 -3
- package/src/internal/utils/uuid.ts +0 -19
- package/src/internal/utils/values.ts +0 -102
- package/src/internal/utils.ts +0 -8
- package/src/lib/.keep +0 -4
- package/src/resource.ts +0 -2
- package/src/resources/action-execution.ts +0 -244
- package/src/resources/auth/auth.ts +0 -15
- package/src/resources/auth/index.ts +0 -4
- package/src/resources/auth/session.ts +0 -127
- package/src/resources/auth-configs.ts +0 -362
- package/src/resources/auth.ts +0 -3
- package/src/resources/cli.ts +0 -166
- package/src/resources/connected-accounts.ts +0 -661
- package/src/resources/index.ts +0 -105
- package/src/resources/mcp/command.ts +0 -47
- package/src/resources/mcp/index.ts +0 -17
- package/src/resources/mcp/mcp.ts +0 -519
- package/src/resources/mcp.ts +0 -3
- package/src/resources/org/api-key.ts +0 -30
- package/src/resources/org/index.ts +0 -12
- package/src/resources/org/org.ts +0 -39
- package/src/resources/org/project/api-keys.ts +0 -156
- package/src/resources/org/project/index.ts +0 -35
- package/src/resources/org/project/project.ts +0 -206
- package/src/resources/org/project/trigger.ts +0 -95
- package/src/resources/org/project/webhook.ts +0 -131
- package/src/resources/org/project.ts +0 -3
- package/src/resources/org.ts +0 -3
- package/src/resources/team-members.ts +0 -111
- package/src/resources/toolkits.ts +0 -232
- package/src/resources/tools.ts +0 -319
- package/src/resources/trigger-instances/handle.ts +0 -45
- package/src/resources/trigger-instances/index.ts +0 -19
- package/src/resources/trigger-instances/trigger-instances.ts +0 -202
- package/src/resources/trigger-instances.ts +0 -3
- package/src/resources/trigger.ts +0 -108
- package/src/resources/triggers-types.ts +0 -105
- package/src/resources.ts +0 -1
- package/src/tsconfig.json +0 -11
- package/src/uploads.ts +0 -2
- package/src/version.ts +0 -1
- package/uploads.d.mts +0 -2
- package/uploads.d.mts.map +0 -1
- package/uploads.d.ts +0 -2
- package/uploads.d.ts.map +0 -1
- package/uploads.js +0 -19
- package/uploads.js.map +0 -1
- package/uploads.mjs +0 -2
- package/uploads.mjs.map +0 -1
- package/version.d.mts +0 -2
- package/version.d.mts.map +0 -1
- package/version.d.ts +0 -2
- package/version.d.ts.map +0 -1
- package/version.js +0 -5
- package/version.js.map +0 -1
- package/version.mjs +0 -2
- package/version.mjs.map +0 -1
|
@@ -0,0 +1,1423 @@
|
|
|
1
|
+
//#region src/compat/error.ts
|
|
2
|
+
/**
|
|
3
|
+
* Stainless-compatible error hierarchy.
|
|
4
|
+
*
|
|
5
|
+
* Downstream code does `instanceof` checks and reads `status` / `error` /
|
|
6
|
+
* `headers` on these classes (see docs/research/drop-in-contract.md §4), so
|
|
7
|
+
* both the class names and the inheritance chain are part of the drop-in
|
|
8
|
+
* contract: `BadRequestError extends APIError extends ComposioError extends Error`.
|
|
9
|
+
*/
|
|
10
|
+
var ComposioError = class extends Error {};
|
|
11
|
+
var APIError = class APIError extends ComposioError {
|
|
12
|
+
/** HTTP status for the response that caused the error. */
|
|
13
|
+
status;
|
|
14
|
+
/** HTTP headers of the response that caused the error. */
|
|
15
|
+
headers;
|
|
16
|
+
/** JSON body of the response that caused the error. */
|
|
17
|
+
error;
|
|
18
|
+
constructor(status, error, message, headers) {
|
|
19
|
+
super(APIError.makeMessage(status, error, message));
|
|
20
|
+
this.status = status;
|
|
21
|
+
this.headers = headers;
|
|
22
|
+
this.error = error;
|
|
23
|
+
}
|
|
24
|
+
static makeMessage(status, error, message) {
|
|
25
|
+
const errorMessage = error?.message;
|
|
26
|
+
const msg = errorMessage ? typeof errorMessage === "string" ? errorMessage : JSON.stringify(errorMessage) : error ? JSON.stringify(error) : message;
|
|
27
|
+
if (status && msg) return `${status} ${msg}`;
|
|
28
|
+
if (status) return `${status} status code (no body)`;
|
|
29
|
+
if (msg) return msg;
|
|
30
|
+
return "(no status code or body)";
|
|
31
|
+
}
|
|
32
|
+
static generate(status, errorResponse, message, headers) {
|
|
33
|
+
if (!status || !headers) return new APIConnectionError({
|
|
34
|
+
message,
|
|
35
|
+
cause: errorResponse
|
|
36
|
+
});
|
|
37
|
+
const error = errorResponse;
|
|
38
|
+
if (status === 400) return new BadRequestError(status, error, message, headers);
|
|
39
|
+
if (status === 401) return new AuthenticationError(status, error, message, headers);
|
|
40
|
+
if (status === 403) return new PermissionDeniedError(status, error, message, headers);
|
|
41
|
+
if (status === 404) return new NotFoundError(status, error, message, headers);
|
|
42
|
+
if (status === 409) return new ConflictError(status, error, message, headers);
|
|
43
|
+
if (status === 422) return new UnprocessableEntityError(status, error, message, headers);
|
|
44
|
+
if (status === 429) return new RateLimitError(status, error, message, headers);
|
|
45
|
+
if (status >= 500) return new InternalServerError(status, error, message, headers);
|
|
46
|
+
return new APIError(status, error, message, headers);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var APIUserAbortError = class extends APIError {
|
|
50
|
+
constructor({ message } = {}) {
|
|
51
|
+
super(void 0, void 0, message ?? "Request was aborted.", void 0);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var APIConnectionError = class extends APIError {
|
|
55
|
+
constructor({ message, cause }) {
|
|
56
|
+
super(void 0, void 0, message ?? "Connection error.", void 0);
|
|
57
|
+
if (cause) this.cause = cause;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var APIConnectionTimeoutError = class extends APIConnectionError {
|
|
61
|
+
constructor({ message } = {}) {
|
|
62
|
+
super({ message: message ?? "Request timed out." });
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var BadRequestError = class extends APIError {};
|
|
66
|
+
var AuthenticationError = class extends APIError {};
|
|
67
|
+
var PermissionDeniedError = class extends APIError {};
|
|
68
|
+
var NotFoundError = class extends APIError {};
|
|
69
|
+
var ConflictError = class extends APIError {};
|
|
70
|
+
var UnprocessableEntityError = class extends APIError {};
|
|
71
|
+
var RateLimitError = class extends APIError {};
|
|
72
|
+
var InternalServerError = class extends APIError {};
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/compat/headers.ts
|
|
76
|
+
const hasEntries = (value) => typeof value === "object" && value !== null && "append" in value && typeof value.append === "function" && "delete" in value && typeof value.delete === "function" && "entries" in value && typeof value.entries === "function" && "get" in value && typeof value.get === "function";
|
|
77
|
+
const isHeaders = (value) => value instanceof Headers || hasEntries(value);
|
|
78
|
+
const isNullableHeadersLike = (headers) => {
|
|
79
|
+
if (!("values" in headers) || !isHeaders(headers.values) || !("nulls" in headers)) return false;
|
|
80
|
+
const nulls = headers.nulls;
|
|
81
|
+
return typeof nulls === "object" && nulls !== null && Symbol.iterator in nulls;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Iterates every supported input while preserving explicit deletions. Plain
|
|
85
|
+
* objects replace an older value for a key; tuple inputs append duplicate
|
|
86
|
+
* values, matching the published client.
|
|
87
|
+
*/
|
|
88
|
+
function* iterateHeaders(headers) {
|
|
89
|
+
if (!headers) return;
|
|
90
|
+
if (isNullableHeadersLike(headers)) {
|
|
91
|
+
yield* headers.values.entries();
|
|
92
|
+
for (const name of headers.nulls) yield [name, null];
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const shouldReplace = !isHeaders(headers) && !Array.isArray(headers);
|
|
96
|
+
const entries = isHeaders(headers) ? headers.entries() : Array.isArray(headers) ? headers : Object.entries(headers);
|
|
97
|
+
for (const entry of entries) {
|
|
98
|
+
const name = entry[0];
|
|
99
|
+
if (typeof name !== "string") throw new TypeError("expected header name to be a string");
|
|
100
|
+
const rawValue = entry[1];
|
|
101
|
+
const values = Array.isArray(rawValue) ? rawValue : [rawValue];
|
|
102
|
+
let replaced = false;
|
|
103
|
+
for (const value of values) {
|
|
104
|
+
if (value === void 0) continue;
|
|
105
|
+
if (shouldReplace && !replaced) {
|
|
106
|
+
replaced = true;
|
|
107
|
+
yield [name, null];
|
|
108
|
+
}
|
|
109
|
+
yield [name, value];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** Merge sources while retaining names explicitly deleted by the last source. */
|
|
114
|
+
const mergeHeaders = (sources) => {
|
|
115
|
+
const values = new Headers();
|
|
116
|
+
const nulls = /* @__PURE__ */ new Set();
|
|
117
|
+
for (const source of sources) {
|
|
118
|
+
const seen = /* @__PURE__ */ new Set();
|
|
119
|
+
for (const [name, value] of iterateHeaders(source)) {
|
|
120
|
+
const lowerName = name.toLowerCase();
|
|
121
|
+
if (!seen.has(lowerName)) {
|
|
122
|
+
values.delete(name);
|
|
123
|
+
seen.add(lowerName);
|
|
124
|
+
}
|
|
125
|
+
if (value === null) {
|
|
126
|
+
values.delete(name);
|
|
127
|
+
nulls.add(lowerName);
|
|
128
|
+
} else {
|
|
129
|
+
values.append(name, value);
|
|
130
|
+
nulls.delete(lowerName);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
values,
|
|
136
|
+
nulls
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
/** Merge sources into the native header collection sent to fetch. */
|
|
140
|
+
const buildHeaders = (sources) => mergeHeaders(sources).values;
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/compat/credentials.ts
|
|
144
|
+
/**
|
|
145
|
+
* The credential transport boundary.
|
|
146
|
+
*
|
|
147
|
+
* The design record makes authentication part of the runtime contract in both
|
|
148
|
+
* languages, with four rules. Three of them are enforced here and in
|
|
149
|
+
* `request.ts`; the fourth is a property of what this client's messages are
|
|
150
|
+
* built from, and is pinned by test rather than by code:
|
|
151
|
+
*
|
|
152
|
+
* 0. **A request carries exactly one credential header**, chosen from the
|
|
153
|
+
* operation's declared security schemes. Not a record rule — it is a
|
|
154
|
+
* measured property of the API (two credential headers on one request is a
|
|
155
|
+
* 401, even where either alone is a 200), so the four rules below are all
|
|
156
|
+
* stated about *the* credential a request carries, and {@link selectScheme}
|
|
157
|
+
* decides which one that is. See wayfinder ticket 48 and §4 of
|
|
158
|
+
* `docs/research/drop-in-contract.md`.
|
|
159
|
+
* 1. **A credentialed API origin must use HTTPS.** Plain `http:` is permitted
|
|
160
|
+
* only for loopback development endpoints — which is exactly what the
|
|
161
|
+
* sanctioned `environment: 'local'` default (`http://localhost:9900`) is.
|
|
162
|
+
* Enforced by {@link assertCredentialTransport} at construction and again
|
|
163
|
+
* by {@link credentialForRequest} at dispatch, because `apiKey` and
|
|
164
|
+
* `baseURL` are both public mutable fields on the client.
|
|
165
|
+
* 2. **The credential is attached only to requests whose origin matches the
|
|
166
|
+
* configured API origin.** {@link credentialForRequest} returns `null`
|
|
167
|
+
* off-origin, and the header is simply not sent.
|
|
168
|
+
* 3. **Credentials are never forwarded across an origin-changing redirect.**
|
|
169
|
+
* The kernel follows redirects itself (`request.ts`) rather than delegating
|
|
170
|
+
* to `fetch`, and every hop re-enters the header build — so the credential
|
|
171
|
+
* decision is re-derived from *that hop's* URL and rule 3 mostly falls out
|
|
172
|
+
* of rule 2. What does not fall out is the caller's own credential headers,
|
|
173
|
+
* which rule 2 deliberately leaves alone: {@link withdrawCredentials}
|
|
174
|
+
* removes those once a chain has changed origin.
|
|
175
|
+
* 4. **Credential-bearing values are redacted from logs and error messages.**
|
|
176
|
+
* Nothing here to do: no message this client constructs ever reads a
|
|
177
|
+
* request header or a request URL. `APIError.makeMessage` is built from the
|
|
178
|
+
* status and the response *body*; the deprecation notice is built from the
|
|
179
|
+
* operation template and parsed public response-header values. Pinned in
|
|
180
|
+
* `test/unit/credential-boundary.test.ts` so it stays true.
|
|
181
|
+
*
|
|
182
|
+
* ### Failure modes, and why they differ
|
|
183
|
+
*
|
|
184
|
+
* Rule 1 **throws**. A plaintext credentialed origin is a configuration
|
|
185
|
+
* mistake with no safe interpretation: dropping the key instead would turn it
|
|
186
|
+
* into a stream of confusing 401s that never names the real cause, and sending
|
|
187
|
+
* it is the thing being prevented. Throwing at construction puts the error on
|
|
188
|
+
* the line that made the mistake.
|
|
189
|
+
*
|
|
190
|
+
* Rule 2 **drops the credential and continues**. The record states it as an
|
|
191
|
+
* attachment rule, not an error, and the runtime genuinely cannot tell an
|
|
192
|
+
* attack from a legitimate off-origin fetch — this API mints presigned upload
|
|
193
|
+
* and download URLs (`files.createPresignedURL`,
|
|
194
|
+
* `session.files.createDownloadURL`) that live on a storage origin, and a
|
|
195
|
+
* caller reaching one through the escape hatch is doing something reasonable.
|
|
196
|
+
* Dropping makes that safe; throwing would make it impossible.
|
|
197
|
+
*
|
|
198
|
+
* Rule 3 **follows the hop without the credential** rather than refusing to
|
|
199
|
+
* hop. Refusing was the first spelling, and it was overruled on a parity
|
|
200
|
+
* argument worth recording: Stainless followed redirects, no differential
|
|
201
|
+
* fixture redirects, so a green suite would have been evidence that the
|
|
202
|
+
* *tests* do not redirect — not that production does not. The rule itself is
|
|
203
|
+
* unchanged; only the remedy is.
|
|
204
|
+
*
|
|
205
|
+
* ### Divergence from Stainless
|
|
206
|
+
*
|
|
207
|
+
* Deliberate, and sanctioned by the design record: the published Stainless
|
|
208
|
+
* client enforces none of this. Redirect *following* is no longer part of the
|
|
209
|
+
* divergence — both clients follow — but what rides along on the hop is. See
|
|
210
|
+
* §4 of `docs/research/drop-in-contract.md` and wayfinder ticket 40.
|
|
211
|
+
*/
|
|
212
|
+
/**
|
|
213
|
+
* The three credential classes this API accepts, and the header each one puts
|
|
214
|
+
* on the wire.
|
|
215
|
+
*
|
|
216
|
+
* These are the `apiKey`-in-header schemes `spec/openapi.json` declares under
|
|
217
|
+
* `components.securitySchemes`. The fourth declared one, `CookieAuth`, is
|
|
218
|
+
* referenced by no operation and is not a thing a client library places.
|
|
219
|
+
*/
|
|
220
|
+
const SCHEME_HEADERS = {
|
|
221
|
+
ApiKeyAuth: "x-api-key",
|
|
222
|
+
UserApiKeyAuth: "x-user-api-key",
|
|
223
|
+
OrgApiKeyAuth: "x-org-api-key"
|
|
224
|
+
};
|
|
225
|
+
/** The scheme a request authenticates with when its operation names none. */
|
|
226
|
+
const DEFAULT_SCHEME = "ApiKeyAuth";
|
|
227
|
+
const valueFor = (scheme, credentials) => scheme === "ApiKeyAuth" ? credentials.apiKey : scheme === "UserApiKeyAuth" ? credentials.userApiKey : credentials.orgApiKey;
|
|
228
|
+
/**
|
|
229
|
+
* Whether anything beyond the project key is configured — the condition that
|
|
230
|
+
* makes per-operation selection able to change an outcome at all.
|
|
231
|
+
*
|
|
232
|
+
* Load-bearing rather than an optimization. With only `apiKey` set, every
|
|
233
|
+
* branch of {@link selectScheme} lands on `ApiKeyAuth`: it wins wherever it is
|
|
234
|
+
* an accepted alternative, and where it is not, the alternative is unconfigured
|
|
235
|
+
* and the fallback is `ApiKeyAuth` again. So the client that every existing
|
|
236
|
+
* caller constructs never consults the operation table, and "an `apiKey`-only
|
|
237
|
+
* client emits byte-identical requests on all 104 operations" is a property of
|
|
238
|
+
* the code shape rather than a claim tests have to keep re-establishing.
|
|
239
|
+
*/
|
|
240
|
+
const hasAlternateCredential = (credentials) => credentials.userApiKey !== null || credentials.orgApiKey !== null;
|
|
241
|
+
/**
|
|
242
|
+
* Which credential a request declaring `schemes` should carry.
|
|
243
|
+
*
|
|
244
|
+
* **Exactly one, always** — and the API says so itself. Measured against
|
|
245
|
+
* production on 2026-08-06, `GET /toolkits` answers 200 for `x-api-key` alone
|
|
246
|
+
* and 200 for `x-user-api-key` alone, and answers both together with
|
|
247
|
+
* `401 HTTP_Unauthorized`:
|
|
248
|
+
*
|
|
249
|
+
* > Multiple authentication modes were provided. Provide exactly one
|
|
250
|
+
* > authentication mode per request. Provided: ApiKeyAuth via x-api-key,
|
|
251
|
+
* > UserApiKeyAuth via x-user-api-key.
|
|
252
|
+
*
|
|
253
|
+
* The platform names the schemes by their OpenAPI names, so this is the same
|
|
254
|
+
* vocabulary `security` is written in rather than a coincidence of headers. It
|
|
255
|
+
* holds for `x-api-key` + `x-org-api-key` too, and on an operation that accepts
|
|
256
|
+
* only one of them. There is therefore no additive "send the user key as well"
|
|
257
|
+
* model available, and the choice has to be made per request rather than
|
|
258
|
+
* accumulated.
|
|
259
|
+
*
|
|
260
|
+
* The precedence exists to make the change free where it must be free:
|
|
261
|
+
*
|
|
262
|
+
* 1. **`ApiKeyAuth` wins wherever the operation accepts it** and an `apiKey` is
|
|
263
|
+
* configured — 85 of the 104 operations, every wire byte preserved.
|
|
264
|
+
* 2. Otherwise the first scheme the operation names that this client both
|
|
265
|
+
* recognizes *and* holds a credential for. That is what makes the 16
|
|
266
|
+
* `UserApiKeyAuth`-only and `OrgApiKeyAuth`-only operations reachable.
|
|
267
|
+
* 3. Otherwise `ApiKeyAuth`, which is what this client did before any of this
|
|
268
|
+
* existed. A `UserApiKeyAuth`-only operation called by a client with no user
|
|
269
|
+
* key still sends `x-api-key` and still fails the same way — which is what
|
|
270
|
+
* keeps `cli.realtime.auth` and `cli.realtime.credentials` pinned against
|
|
271
|
+
* the Stainless oracle in `test/unit/differential-wire.test.ts`.
|
|
272
|
+
*
|
|
273
|
+
* Rule 3 is also where a scheme name this client does not know lands. Three of
|
|
274
|
+
* them exist: `security` references `no_auth` (3 operations) and
|
|
275
|
+
* `workbench_access_key` (1) and `components.securitySchemes` declares neither.
|
|
276
|
+
* Those four operations receive `x-api-key` today and are differentially
|
|
277
|
+
* pinned doing so, so resolving an unknown name to "no credential" would be a
|
|
278
|
+
* wire change made on the strength of a spec defect. A later generator pass
|
|
279
|
+
* must not "fix" that.
|
|
280
|
+
*/
|
|
281
|
+
const selectScheme = (schemes, credentials) => {
|
|
282
|
+
if (schemes === void 0 || schemes.length === 0) return DEFAULT_SCHEME;
|
|
283
|
+
if (schemes.includes(DEFAULT_SCHEME) && credentials.apiKey !== null) return DEFAULT_SCHEME;
|
|
284
|
+
for (const scheme of schemes) {
|
|
285
|
+
if (!Object.hasOwn(SCHEME_HEADERS, scheme)) continue;
|
|
286
|
+
const known = scheme;
|
|
287
|
+
if (valueFor(known, credentials) !== null) return known;
|
|
288
|
+
}
|
|
289
|
+
return DEFAULT_SCHEME;
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* What counts as loopback, pinned rather than assumed. `localhost` is not the
|
|
293
|
+
* whole set:
|
|
294
|
+
*
|
|
295
|
+
* - `localhost`, and any name under it (`api.localhost`). RFC 6761 §6.3
|
|
296
|
+
* reserves the whole `.localhost` TLD to resolve to the loopback interface,
|
|
297
|
+
* and container-era dev setups do use subdomains of it.
|
|
298
|
+
* - The entire IPv4 loopback block `127.0.0.0/8`, not just `127.0.0.1`.
|
|
299
|
+
* - The IPv6 loopback `::1`. `URL` hands hostnames back bracketed and in
|
|
300
|
+
* canonical compressed form, so `http://[0:0:0:0:0:0:0:1]/` arrives here as
|
|
301
|
+
* `[::1]`.
|
|
302
|
+
*
|
|
303
|
+
* Everything else is not loopback, deliberately. `0.0.0.0` in particular is the
|
|
304
|
+
* unspecified address, not a loopback one; it happens to reach the local host
|
|
305
|
+
* on some platforms and fails on others, and a rule that depends on the
|
|
306
|
+
* platform is not a rule. The IPv4-mapped spelling `::ffff:127.0.0.1` is
|
|
307
|
+
* refused for the same reason — Python's `ipaddress` answers it differently
|
|
308
|
+
* across interpreter versions, and nobody configures a development endpoint
|
|
309
|
+
* that way. The error message names the accepted set so a developer who typed
|
|
310
|
+
* `0.0.0.0` sees the fix immediately.
|
|
311
|
+
*/
|
|
312
|
+
const LOOPBACK_IPV4 = /^127(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/;
|
|
313
|
+
const isLoopbackHostname = (hostname) => {
|
|
314
|
+
const host = hostname.trim().toLowerCase().replace(/^\[|\]$/g, "");
|
|
315
|
+
if (host === "localhost" || host.endsWith(".localhost")) return true;
|
|
316
|
+
if (host === "::1") return true;
|
|
317
|
+
return LOOPBACK_IPV4.test(host);
|
|
318
|
+
};
|
|
319
|
+
/** The URL, parsed, when it is absolute and http(s); `null` otherwise. */
|
|
320
|
+
const parseHTTPURL = (value) => {
|
|
321
|
+
try {
|
|
322
|
+
const url = new URL(value);
|
|
323
|
+
return url.protocol === "http:" || url.protocol === "https:" ? url : null;
|
|
324
|
+
} catch {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* The URL `target` names, resolved against `base`, when it lands on http(s);
|
|
330
|
+
* `null` when it cannot be parsed or names a scheme this client does not
|
|
331
|
+
* speak. The one spelling of "resolve against a base, http(s) only" — the
|
|
332
|
+
* redirect follower and the deprecation link resolver both need exactly this.
|
|
333
|
+
*/
|
|
334
|
+
const resolveHTTPURL = (target, base) => {
|
|
335
|
+
try {
|
|
336
|
+
return parseHTTPURL(new URL(target, base).href);
|
|
337
|
+
} catch {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
/** Whether a credential may be sent to this origin at all. */
|
|
342
|
+
const isCredentialSafeOrigin = (url) => url.protocol === "https:" || url.protocol === "http:" && isLoopbackHostname(url.hostname);
|
|
343
|
+
const LOOPBACK_DESCRIPTION = "localhost, *.localhost, 127.0.0.0/8, [::1]";
|
|
344
|
+
const insecureOriginMessage = (baseURL) => `Refusing to send a Composio API credential to ${baseURL}: a credentialed API origin must use HTTPS. Plain http:// is permitted only for loopback development endpoints (${LOOPBACK_DESCRIPTION}). Pass an https:// baseURL, use environment: 'local', or construct the client without credentials.`;
|
|
345
|
+
/** Whether the client holds anything at all that rule 1 has to protect. */
|
|
346
|
+
const anyCredential = (credentials) => credentials.apiKey !== null || hasAlternateCredential(credentials);
|
|
347
|
+
/**
|
|
348
|
+
* Rule 1 at construction time: a client that holds a credential must be
|
|
349
|
+
* pointed at an origin that can carry one. Knowable before any request is
|
|
350
|
+
* made, so it fails on the constructor call rather than on the first dispatch.
|
|
351
|
+
*
|
|
352
|
+
* A `baseURL` that is not an absolute http(s) URL fails the same way — a
|
|
353
|
+
* credential cannot be sent over a scheme this client does not speak, and the
|
|
354
|
+
* alternative is a `TypeError` from `new URL()` one layer down.
|
|
355
|
+
*
|
|
356
|
+
* Every configured credential counts, not just `apiKey`: which one a given
|
|
357
|
+
* request carries is decided per operation, so a client holding *any* of them
|
|
358
|
+
* is a client that can put one on the wire.
|
|
359
|
+
*/
|
|
360
|
+
const assertCredentialTransport = (baseURL, credentials) => {
|
|
361
|
+
if (!anyCredential(credentials)) return;
|
|
362
|
+
const url = parseHTTPURL(baseURL);
|
|
363
|
+
if (url === null || !isCredentialSafeOrigin(url)) throw new ComposioError(insecureOriginMessage(baseURL));
|
|
364
|
+
};
|
|
365
|
+
/**
|
|
366
|
+
* The credential this request may carry, or `null` for none.
|
|
367
|
+
*
|
|
368
|
+
* Rules 1 and 2 at dispatch time. Both re-checks are load-bearing rather than
|
|
369
|
+
* belt-and-braces: `apiKey` and `baseURL` are public mutable fields, so the
|
|
370
|
+
* construction-time check can be invalidated by an assignment, and the request
|
|
371
|
+
* URL is *not* guaranteed to sit on the configured origin. `buildURL`
|
|
372
|
+
* concatenates `baseURL + path` and the escape hatch (`client.get(path)`)
|
|
373
|
+
* takes an arbitrary path, so a path that does not begin with `/` extends the
|
|
374
|
+
* *authority* rather than the path: `client.get('.evil.example/x')` against
|
|
375
|
+
* `https://backend.composio.dev` resolves to
|
|
376
|
+
* `https://backend.composio.dev.evil.example/x`, and `client.get(':8443/x')`
|
|
377
|
+
* changes the port. Both were carrying `x-api-key` before this boundary
|
|
378
|
+
* existed. (The userinfo spelling — `client.get('@evil.example/x')` — reaches
|
|
379
|
+
* a third origin too, but `fetch` refuses to construct a `Request` from a URL
|
|
380
|
+
* with credentials, so that one dies at the transport instead. httpx has no
|
|
381
|
+
* such objection, which is why the Python port checks the same thing.)
|
|
382
|
+
*/
|
|
383
|
+
const credentialForRequest = (baseURL, scheme, credentials, requestURL) => {
|
|
384
|
+
const value = valueFor(scheme, credentials);
|
|
385
|
+
if (value === null) return null;
|
|
386
|
+
const base = parseHTTPURL(baseURL);
|
|
387
|
+
const target = parseHTTPURL(requestURL);
|
|
388
|
+
if (base === null || target === null) return null;
|
|
389
|
+
if (base.origin !== target.origin) return null;
|
|
390
|
+
if (!isCredentialSafeOrigin(target)) throw new ComposioError(insecureOriginMessage(baseURL));
|
|
391
|
+
return {
|
|
392
|
+
name: SCHEME_HEADERS[scheme],
|
|
393
|
+
value
|
|
394
|
+
};
|
|
395
|
+
};
|
|
396
|
+
/**
|
|
397
|
+
* The credential headers a *caller* can place that are not the one this client
|
|
398
|
+
* attaches — `x-api-key` is excluded because a caller setting it is overriding
|
|
399
|
+
* the client's own value in the same slot, which is pinned Stainless behaviour,
|
|
400
|
+
* not a second credential.
|
|
401
|
+
*/
|
|
402
|
+
const FOREIGN_CREDENTIAL_HEADERS = ["x-user-api-key", "x-org-api-key"];
|
|
403
|
+
/**
|
|
404
|
+
* Whether the caller has placed a credential of their own, in which case the
|
|
405
|
+
* client must not add one beside it.
|
|
406
|
+
*
|
|
407
|
+
* Not a courtesy: this API answers a request carrying two credential headers
|
|
408
|
+
* with `401 HTTP_Unauthorized`, so attaching `x-api-key` next to a caller's
|
|
409
|
+
* `x-user-api-key` does not weaken the call, it *breaks* it. Three released
|
|
410
|
+
* methods make that reachable without any `defaultHeaders` at all —
|
|
411
|
+
* `cli.createCodactFailure`, `org.consumer.project.resolve` and
|
|
412
|
+
* `org.consumer.listConnectedToolkits` declare `x-user-api-key` as a `required`
|
|
413
|
+
* header parameter, which the generator folds into their params object. Calling
|
|
414
|
+
* them the way their signature asks for returned 401 until this rule existed.
|
|
415
|
+
*
|
|
416
|
+
* Header merging is replayed rather than approximated, because a later source
|
|
417
|
+
* may carry `null` to delete what an earlier one set (`buildHeaders`).
|
|
418
|
+
*/
|
|
419
|
+
const callerPlacedCredential = (sources) => {
|
|
420
|
+
const headers = buildHeaders(sources);
|
|
421
|
+
return FOREIGN_CREDENTIAL_HEADERS.some((name) => headers.has(name));
|
|
422
|
+
};
|
|
423
|
+
/**
|
|
424
|
+
* Header names that carry a credential and must not survive an origin change.
|
|
425
|
+
*
|
|
426
|
+
* The first three are what `fetch` itself would have stripped had it been
|
|
427
|
+
* following the redirect (Fetch §4.4 "HTTP-redirect fetch" deletes
|
|
428
|
+
* `Authorization` cross-origin; `Cookie` and `Proxy-Authorization` are
|
|
429
|
+
* forbidden header names a page cannot set and a server-side fetch does not
|
|
430
|
+
* carry across origins). Following redirects by hand means taking that job on.
|
|
431
|
+
* `x-api-key` joins them because it is *this* API's credential, and because the
|
|
432
|
+
* Fetch spec would not have stripped it — a custom header rides along a
|
|
433
|
+
* cross-origin redirect, which is the hole this whole module exists to close.
|
|
434
|
+
*
|
|
435
|
+
* `x-user-api-key` and `x-org-api-key` are the spec's other two credential
|
|
436
|
+
* schemes (`UserApiKeyAuth`, `OrgApiKeyAuth`), and they belong here for the same
|
|
437
|
+
* reason `x-api-key` does — the kernel attaches all three (wayfinder ticket 48)
|
|
438
|
+
* and a caller can place the latter two directly besides. They were listed here
|
|
439
|
+
* one ticket *before* the attachment work, because omitting them cost more than
|
|
440
|
+
* a missed withdrawal: {@link carriesCredentials} answered `false` for a request
|
|
441
|
+
* carrying only one of them, so `request.ts` classified it as credential-free
|
|
442
|
+
* and handed the whole chain to `fetch` at `redirect: 'follow'` — the exact leak
|
|
443
|
+
* rule 3 exists to close, arriving through a different header name.
|
|
444
|
+
*
|
|
445
|
+
* This list is the repo's answer to "which header names carry a credential",
|
|
446
|
+
* and the live parity harness needs the same answer: it redacts these values
|
|
447
|
+
* where it renders a captured request into a summary someone reads on a
|
|
448
|
+
* terminal. It cannot import them — the harness runs under `node
|
|
449
|
+
* --experimental-transform-types`, which does not resolve this module's `.js`
|
|
450
|
+
* specifiers — so `packages/parity/src/core/compare.ts` mirrors the list and a
|
|
451
|
+
* test there reads this file to hold the two in step, the same arrangement the
|
|
452
|
+
* Python driver's protocol version already uses.
|
|
453
|
+
*/
|
|
454
|
+
const CREDENTIAL_HEADERS = [
|
|
455
|
+
"authorization",
|
|
456
|
+
"cookie",
|
|
457
|
+
"proxy-authorization",
|
|
458
|
+
"x-api-key",
|
|
459
|
+
"x-user-api-key",
|
|
460
|
+
"x-org-api-key"
|
|
461
|
+
];
|
|
462
|
+
/**
|
|
463
|
+
* Rule 3 for headers the caller placed, applied when a redirect chain changes
|
|
464
|
+
* origin.
|
|
465
|
+
*
|
|
466
|
+
* Rule 2 deliberately leaves a caller's own `defaultHeaders` / per-request
|
|
467
|
+
* headers alone on the *initial* request, even off-origin: the caller chose
|
|
468
|
+
* that URL, and an escape-hatch call to a presigned storage URL carrying its
|
|
469
|
+
* own `Authorization` is a legitimate thing to do. A redirect target is chosen
|
|
470
|
+
* by the *server*, not the caller, so the same consent does not extend to it.
|
|
471
|
+
*
|
|
472
|
+
* Withdrawal is **sticky** for the rest of the chain: once a hop has left the
|
|
473
|
+
* origin it started on, no later hop re-attaches a credential, not even one
|
|
474
|
+
* that lands back on the configured API origin. A real API does not bounce a
|
|
475
|
+
* caller off-site and back, and "no credential after the chain leaves" is one
|
|
476
|
+
* sentence rather than a case analysis.
|
|
477
|
+
*/
|
|
478
|
+
const withdrawCredentials = (headers) => {
|
|
479
|
+
for (const name of CREDENTIAL_HEADERS) headers.delete(name);
|
|
480
|
+
};
|
|
481
|
+
/**
|
|
482
|
+
* Whether these headers carry anything that must not cross an origin change —
|
|
483
|
+
* the exact complement of {@link withdrawCredentials}, sharing its list so the
|
|
484
|
+
* two cannot drift apart.
|
|
485
|
+
*
|
|
486
|
+
* `request.ts` asks this before the *first* dispatch of a chain. A request
|
|
487
|
+
* carrying nothing credential-bearing has nothing to protect, so following it
|
|
488
|
+
* by hand buys nothing the platform's own following does not already give:
|
|
489
|
+
* `fetch` strips exactly these header names across a cross-origin hop, applies
|
|
490
|
+
* the same method rewrite, and enforces the same 20-hop budget. Delegating is
|
|
491
|
+
* therefore provably equivalent there — and it is the only way a *browser* can
|
|
492
|
+
* follow a redirect at all.
|
|
493
|
+
*/
|
|
494
|
+
const carriesCredentials = (headers) => CREDENTIAL_HEADERS.some((name) => headers.has(name));
|
|
495
|
+
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/compat/deprecation.ts
|
|
498
|
+
/**
|
|
499
|
+
* Runtime deprecation notices, read off the response.
|
|
500
|
+
*
|
|
501
|
+
* The server announces a deprecated operation with three standard fields:
|
|
502
|
+
*
|
|
503
|
+
* - `Deprecation` (RFC 9745) — an Item Structured Field whose value is a Date
|
|
504
|
+
* (RFC 9651 §3.3.7): an `@` followed by integer seconds since the Unix
|
|
505
|
+
* epoch, e.g. `Deprecation: @1688169599`.
|
|
506
|
+
* - `Sunset` (RFC 8594) — an HTTP-date, e.g. `Sunset: Sat, 31 Dec 2018
|
|
507
|
+
* 23:59:59 GMT`, naming the date the operation stops being served.
|
|
508
|
+
* - `Link` (RFC 8288) — `rel="deprecation"` points at human-readable notes
|
|
509
|
+
* (RFC 9745 registers that relation); `rel="successor-version"` (RFC 5829)
|
|
510
|
+
* points at the replacement resource, which this module maps back to a
|
|
511
|
+
* public client chain when it can.
|
|
512
|
+
*
|
|
513
|
+
* The authoring-time half of the same contract already exists: an operation
|
|
514
|
+
* the input schema marks `deprecated: true` reaches the generated method as a
|
|
515
|
+
* `@deprecated` annotation. This is the runtime half, and the two are
|
|
516
|
+
* independent — a notice fires because the *server* said so, never because the
|
|
517
|
+
* committed snapshot did, so it stays correct between spec syncs.
|
|
518
|
+
*
|
|
519
|
+
* Everything here is defensive by construction. A deprecation notice is
|
|
520
|
+
* advisory; it must never break a live call, so a field that is oversized,
|
|
521
|
+
* carries control characters, or simply does not parse is dropped rather than
|
|
522
|
+
* raised.
|
|
523
|
+
*/
|
|
524
|
+
/**
|
|
525
|
+
* Field-size ceiling. A deprecation notice is decoration on a response that
|
|
526
|
+
* has already been paid for, so an oversized field is dropped rather than
|
|
527
|
+
* parsed — there is no message worth building out of 2 KiB of `Link`.
|
|
528
|
+
*/
|
|
529
|
+
const MAX_FIELD_LENGTH = 2048;
|
|
530
|
+
/** No message this client emits may smuggle control characters into a log. */
|
|
531
|
+
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
|
|
532
|
+
/**
|
|
533
|
+
* How many distinct (operation, notice) pairs one client instance will ever
|
|
534
|
+
* warn about. Generated methods draw from a fixed table of ~100 and a server
|
|
535
|
+
* revises a notice rarely, so the bound is unreachable in practice; it exists
|
|
536
|
+
* because both halves of the key are ultimately server- or caller-controlled —
|
|
537
|
+
* an escape-hatch caller (`client.get('/api/v3.1/…/{id}')`) keys on the
|
|
538
|
+
* substituted path, and a server rotating `Sunset` or `Link` on every response
|
|
539
|
+
* mints a fresh pair each time. Past the ceiling the client stops warning
|
|
540
|
+
* rather than growing a set forever.
|
|
541
|
+
*/
|
|
542
|
+
const MAX_TRACKED_NOTICES = 1024;
|
|
543
|
+
const readField = (headers, name) => {
|
|
544
|
+
const value = headers.get(name);
|
|
545
|
+
if (value === null) return null;
|
|
546
|
+
if (value.length > MAX_FIELD_LENGTH || CONTROL_CHARACTERS.test(value)) return null;
|
|
547
|
+
return value;
|
|
548
|
+
};
|
|
549
|
+
/**
|
|
550
|
+
* RFC 9651 §3.3.7 sf-date: `@` then integer seconds since the epoch, possibly
|
|
551
|
+
* negative. Anything else — including the pre-RFC `true` and bare HTTP-date
|
|
552
|
+
* spellings the draft years produced — yields no date; the notice still fires,
|
|
553
|
+
* because the server sending the field at all is the signal.
|
|
554
|
+
*/
|
|
555
|
+
const parseStructuredDate = (value) => {
|
|
556
|
+
const match = /^@(-?\d{1,15})$/.exec(value.trim());
|
|
557
|
+
if (match === null) return null;
|
|
558
|
+
const date = /* @__PURE__ */ new Date(Number(match[1]) * 1e3);
|
|
559
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
560
|
+
};
|
|
561
|
+
const MONTHS = [
|
|
562
|
+
"Jan",
|
|
563
|
+
"Feb",
|
|
564
|
+
"Mar",
|
|
565
|
+
"Apr",
|
|
566
|
+
"May",
|
|
567
|
+
"Jun",
|
|
568
|
+
"Jul",
|
|
569
|
+
"Aug",
|
|
570
|
+
"Sep",
|
|
571
|
+
"Oct",
|
|
572
|
+
"Nov",
|
|
573
|
+
"Dec"
|
|
574
|
+
];
|
|
575
|
+
const DAY = "(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)";
|
|
576
|
+
const DAY_LONG = "(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)";
|
|
577
|
+
const MONTH = `(${MONTHS.join("|")})`;
|
|
578
|
+
const TIME = "(\\d{2}):(\\d{2}):(\\d{2})";
|
|
579
|
+
/** `day-name "," SP 2DIGIT SP month SP 4DIGIT SP time-of-day SP GMT` */
|
|
580
|
+
const IMF_FIXDATE = new RegExp(`^${DAY}, (\\d{2}) ${MONTH} (\\d{4}) ${TIME} GMT$`);
|
|
581
|
+
/** `day-name-l "," SP 2DIGIT "-" month "-" 2DIGIT SP time-of-day SP GMT` */
|
|
582
|
+
const RFC850_DATE = new RegExp(`^${DAY_LONG}, (\\d{2})-${MONTH}-(\\d{2}) ${TIME} GMT$`);
|
|
583
|
+
/** `day-name SP month SP ( 2DIGIT / ( SP 1DIGIT ) ) SP time-of-day SP 4DIGIT` */
|
|
584
|
+
const ASCTIME_DATE = new RegExp(`^${DAY} ${MONTH} (\\d{2}| \\d) ${TIME} (\\d{4})$`);
|
|
585
|
+
const isLeapYear = (year) => year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
586
|
+
const daysInMonth = (year, month) => month === 2 ? isLeapYear(year) ? 29 : 28 : month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31;
|
|
587
|
+
/**
|
|
588
|
+
* RFC 8594 `Sunset`: an HTTP-date, which RFC 7231 §7.1.1.1 defines as exactly
|
|
589
|
+
* three forms — IMF-fixdate (the only one a sender may generate), and the
|
|
590
|
+
* obsolete rfc850-date and asctime-date a recipient must still accept. All
|
|
591
|
+
* three denote UTC; only IMF-fixdate and rfc850-date spell the `GMT` literal,
|
|
592
|
+
* and asctime-date carries no zone because it never means anything else.
|
|
593
|
+
*
|
|
594
|
+
* Written out by hand rather than delegated to `Date.parse`, which is not a
|
|
595
|
+
* conformance bug but three:
|
|
596
|
+
*
|
|
597
|
+
* - It **fabricates dates.** `Sat, 31 Feb 2018 23:59:59 GMT` parsed as
|
|
598
|
+
* 2018-03-03 — the client would have told a caller its endpoint disappears
|
|
599
|
+
* on a date the server never sent. The calendar is checked here instead of
|
|
600
|
+
* normalized, so an impossible day yields no date at all.
|
|
601
|
+
* - It is **host-dependent.** For any input outside the ISO-8601 grammar,
|
|
602
|
+
* `Date.parse` is implementation-defined, and V8 falls back to a lenient
|
|
603
|
+
* reader that applies the *local* offset when a value carries no zone. Both
|
|
604
|
+
* `December 31, 2018` and a zone-less `Sat, 31 Dec 2018 23:59:59` therefore
|
|
605
|
+
* produced a different instant on a developer's laptop than in CI — a
|
|
606
|
+
* parsed field with no stable value.
|
|
607
|
+
* - It **disagreed with the Python client**, whose RFC-5322 reader rejected
|
|
608
|
+
* every one of those inputs. The two clients are held to identical parses;
|
|
609
|
+
* `test/fixtures/http-date-vectors.json` is the table both suites read.
|
|
610
|
+
*
|
|
611
|
+
* Rejection is not a silencer: an unparseable `Sunset` leaves the removal date
|
|
612
|
+
* out of the warning, and the notice still fires on the field's presence — the
|
|
613
|
+
* same stance `parseStructuredDate` takes on `Deprecation`.
|
|
614
|
+
*/
|
|
615
|
+
const parseHttpDate = (value) => {
|
|
616
|
+
const trimmed = value.trim();
|
|
617
|
+
let fields = null;
|
|
618
|
+
const imf = IMF_FIXDATE.exec(trimmed);
|
|
619
|
+
if (imf !== null) fields = [
|
|
620
|
+
Number(imf[3]),
|
|
621
|
+
imf[2],
|
|
622
|
+
imf[1],
|
|
623
|
+
imf.slice(4, 7)
|
|
624
|
+
];
|
|
625
|
+
else {
|
|
626
|
+
const rfc850 = RFC850_DATE.exec(trimmed);
|
|
627
|
+
if (rfc850 !== null) {
|
|
628
|
+
const short = Number(rfc850[3]);
|
|
629
|
+
fields = [
|
|
630
|
+
short < 70 ? 2e3 + short : 1900 + short,
|
|
631
|
+
rfc850[2],
|
|
632
|
+
rfc850[1],
|
|
633
|
+
rfc850.slice(4, 7)
|
|
634
|
+
];
|
|
635
|
+
} else {
|
|
636
|
+
const asctime = ASCTIME_DATE.exec(trimmed);
|
|
637
|
+
if (asctime !== null) fields = [
|
|
638
|
+
Number(asctime[6]),
|
|
639
|
+
asctime[1],
|
|
640
|
+
asctime[2].trim(),
|
|
641
|
+
asctime.slice(3, 6)
|
|
642
|
+
];
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (fields === null) return null;
|
|
646
|
+
const [year, month, day, time] = fields;
|
|
647
|
+
const monthIndex = MONTHS.indexOf(month);
|
|
648
|
+
const dayNumber = Number(day);
|
|
649
|
+
const [hour, minute, second] = time.map(Number);
|
|
650
|
+
if (year < 1) return null;
|
|
651
|
+
if (dayNumber < 1 || dayNumber > daysInMonth(year, monthIndex + 1)) return null;
|
|
652
|
+
if (hour > 23 || minute > 59 || second > 59) return null;
|
|
653
|
+
const date = new Date(Date.UTC(year, monthIndex, dayNumber, hour, minute, second));
|
|
654
|
+
if (year < 100) date.setUTCFullYear(year);
|
|
655
|
+
return date;
|
|
656
|
+
};
|
|
657
|
+
/**
|
|
658
|
+
* RFC 8288 `Link`, enough of it: a comma-separated list of `<target>` followed
|
|
659
|
+
* by `;`-separated parameters, where a parameter value may be quoted (and may
|
|
660
|
+
* therefore contain commas and semicolons). Only `rel` and the presence of
|
|
661
|
+
* `anchor` are retained. Malformed input truncates the list instead of failing
|
|
662
|
+
* — whatever parsed before the damage is still usable.
|
|
663
|
+
*
|
|
664
|
+
* Per RFC 8288 §3.3 only the *first* `rel` on a link counts; later occurrences
|
|
665
|
+
* are ignored. Accumulating them instead would let `rel=alternate;
|
|
666
|
+
* rel=deprecation` smuggle an arbitrary target into the relation this client
|
|
667
|
+
* acts on.
|
|
668
|
+
*/
|
|
669
|
+
const parseLinkField = (value) => {
|
|
670
|
+
const links = [];
|
|
671
|
+
let index = 0;
|
|
672
|
+
const isSpace = (char) => char === " " || char === " ";
|
|
673
|
+
while (index < value.length) {
|
|
674
|
+
while (index < value.length && (value[index] === "," || isSpace(value[index]))) index++;
|
|
675
|
+
if (value[index] !== "<") break;
|
|
676
|
+
const close = value.indexOf(">", index);
|
|
677
|
+
if (close < 0) break;
|
|
678
|
+
const target = value.slice(index + 1, close).trim();
|
|
679
|
+
index = close + 1;
|
|
680
|
+
const rels = [];
|
|
681
|
+
let seenRel = false;
|
|
682
|
+
let anchored = false;
|
|
683
|
+
while (index < value.length) {
|
|
684
|
+
while (index < value.length && isSpace(value[index])) index++;
|
|
685
|
+
if (value[index] === ",") {
|
|
686
|
+
index++;
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
if (value[index] !== ";") {
|
|
690
|
+
index++;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
index++;
|
|
694
|
+
const nameStart = index;
|
|
695
|
+
while (index < value.length && !"=;,".includes(value[index] ?? "")) index++;
|
|
696
|
+
const name = value.slice(nameStart, index).trim().toLowerCase();
|
|
697
|
+
let parameter = "";
|
|
698
|
+
if (value[index] === "=") {
|
|
699
|
+
index++;
|
|
700
|
+
while (index < value.length && isSpace(value[index])) index++;
|
|
701
|
+
if (value[index] === "\"") {
|
|
702
|
+
index++;
|
|
703
|
+
while (index < value.length && value[index] !== "\"") {
|
|
704
|
+
if (value[index] === "\\" && index + 1 < value.length) {
|
|
705
|
+
parameter += value[index + 1];
|
|
706
|
+
index += 2;
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
parameter += value[index];
|
|
710
|
+
index++;
|
|
711
|
+
}
|
|
712
|
+
index++;
|
|
713
|
+
} else {
|
|
714
|
+
const start = index;
|
|
715
|
+
while (index < value.length && value[index] !== ";" && value[index] !== ",") index++;
|
|
716
|
+
parameter = value.slice(start, index).trim();
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (name === "rel" && !seenRel) {
|
|
720
|
+
seenRel = true;
|
|
721
|
+
for (const rel of parameter.split(/\s+/)) if (rel !== "") rels.push(rel.toLowerCase());
|
|
722
|
+
}
|
|
723
|
+
if (name === "anchor") anchored = true;
|
|
724
|
+
}
|
|
725
|
+
links.push({
|
|
726
|
+
target,
|
|
727
|
+
rels,
|
|
728
|
+
anchored
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
return links;
|
|
732
|
+
};
|
|
733
|
+
const httpURL = (target, baseURL) => resolveHTTPURL(target, baseURL);
|
|
734
|
+
/**
|
|
735
|
+
* A successor is only followed as far as *naming* it, and only when it lands
|
|
736
|
+
* on the API this client is configured against.
|
|
737
|
+
*
|
|
738
|
+
* Two different URLs are in play and conflating them is a real exposure. The
|
|
739
|
+
* *context* is the URL the response actually came from, which is what RFC 8288
|
|
740
|
+
* §3.1 resolves a relative target against — and after a redirect that is not
|
|
741
|
+
* the URL we asked for. The *origin allowlist* is the client's configured
|
|
742
|
+
* `baseURL`, and it stays the allowlist precisely because a redirected
|
|
743
|
+
* response must not be able to nominate itself: resolving against the context
|
|
744
|
+
* but validating against `baseURL` is what stops a cross-origin hop from
|
|
745
|
+
* emitting a relative link that lands back on our origin and passes the check.
|
|
746
|
+
*/
|
|
747
|
+
const sameOriginURL = (target, contextURL, baseURL) => {
|
|
748
|
+
const url = httpURL(target, contextURL);
|
|
749
|
+
if (url === null) return null;
|
|
750
|
+
const base = httpURL(baseURL, baseURL);
|
|
751
|
+
return base !== null && url.origin === base.origin ? url : null;
|
|
752
|
+
};
|
|
753
|
+
/**
|
|
754
|
+
* The notice a response carries, or `null` when it carries none. The
|
|
755
|
+
* `Deprecation` field's *presence* is what marks the operation deprecated;
|
|
756
|
+
* `Sunset` and `Link` only enrich the message.
|
|
757
|
+
*
|
|
758
|
+
* `contextURL` is the response's own URL (post-redirect), the context RFC 8288
|
|
759
|
+
* resolves relative link targets against; `baseURL` is the configured API
|
|
760
|
+
* origin a successor must land on.
|
|
761
|
+
*/
|
|
762
|
+
const parseDeprecation = (headers, contextURL, baseURL) => {
|
|
763
|
+
const deprecation = readField(headers, "deprecation");
|
|
764
|
+
if (deprecation === null) return null;
|
|
765
|
+
const sunset = readField(headers, "sunset");
|
|
766
|
+
const link = readField(headers, "link");
|
|
767
|
+
const links = link === null ? [] : parseLinkField(link);
|
|
768
|
+
const targetFor = (rel) => links.find((candidate) => !candidate.anchored && candidate.rels.includes(rel))?.target ?? null;
|
|
769
|
+
const documentation = targetFor("deprecation");
|
|
770
|
+
const successor = targetFor("successor-version");
|
|
771
|
+
return {
|
|
772
|
+
deprecatedAt: parseStructuredDate(deprecation),
|
|
773
|
+
sunsetAt: sunset === null ? null : parseHttpDate(sunset),
|
|
774
|
+
documentation: documentation === null ? null : httpURL(documentation, contextURL)?.href ?? null,
|
|
775
|
+
successor: successor === null ? null : sameOriginURL(successor, contextURL, baseURL)
|
|
776
|
+
};
|
|
777
|
+
};
|
|
778
|
+
const pathSegments = (path) => (path.split(/[?#]/, 1)[0] ?? "").split("/").filter((segment) => segment !== "");
|
|
779
|
+
/**
|
|
780
|
+
* Template segments, keyed by descriptor so the cache holds only entries the
|
|
781
|
+
* operations table (or a test's stand-in for it) is still keeping alive. The
|
|
782
|
+
* table is module-level and immutable, so every logical call re-split the same
|
|
783
|
+
* 104 templates for nothing once the match became unconditional.
|
|
784
|
+
*/
|
|
785
|
+
const templateSegments = /* @__PURE__ */ new WeakMap();
|
|
786
|
+
const segmentsOf = (operation) => {
|
|
787
|
+
const cached = templateSegments.get(operation);
|
|
788
|
+
if (cached !== void 0) return cached;
|
|
789
|
+
const segments = pathSegments(operation.path);
|
|
790
|
+
templateSegments.set(operation, segments);
|
|
791
|
+
return segments;
|
|
792
|
+
};
|
|
793
|
+
const isTemplateParam = (segment) => segment.startsWith("{") && segment.endsWith("}");
|
|
794
|
+
/**
|
|
795
|
+
* The operation a dispatched request belongs to, recovered from the path it
|
|
796
|
+
* went out on. Path parameters are percent-encoded on the way out
|
|
797
|
+
* (`encodeURIPath`), which encodes `/` and leaves the unreserved characters
|
|
798
|
+
* every literal template segment is made of — so a substituted path has the
|
|
799
|
+
* same segment count as its template and its literal segments compare byte for
|
|
800
|
+
* byte.
|
|
801
|
+
*
|
|
802
|
+
* Where a parameterized template and a literal one overlap
|
|
803
|
+
* (`/toolkits/{slug}` against `/toolkits/categories`) the literal wins, which
|
|
804
|
+
* is the same most-specific-first rule the server routes by.
|
|
805
|
+
*
|
|
806
|
+
* **Known limit of recovering identity from the path.** Two accessors can emit
|
|
807
|
+
* byte-identical requests — `client.toolkits.retrieve('categories')` and
|
|
808
|
+
* `client.toolkits.retrieveCategories()` both dispatch
|
|
809
|
+
* `GET /api/v3.1/toolkits/categories` — so nothing observable distinguishes
|
|
810
|
+
* them here and the literal sibling is always the one named. The warning text
|
|
811
|
+
* can therefore name the sibling rather than the accessor the caller wrote,
|
|
812
|
+
* and the warn-once record is charged to that same sibling. This is
|
|
813
|
+
* inherent to reverse-matching, not a bug in the matcher: the only fix is to
|
|
814
|
+
* thread operation identity through dispatch, which this design deliberately
|
|
815
|
+
* declined (wayfinder ticket 34). The affected pairs are few and the
|
|
816
|
+
* consequence is a misnamed advisory, never a wrong call.
|
|
817
|
+
*/
|
|
818
|
+
const matchOperation = (method, path, operations) => {
|
|
819
|
+
const segments = pathSegments(path);
|
|
820
|
+
let best = null;
|
|
821
|
+
let bestLiterals = -1;
|
|
822
|
+
for (const operation of operations) {
|
|
823
|
+
if (operation.method !== method) continue;
|
|
824
|
+
const template = segmentsOf(operation);
|
|
825
|
+
if (template.length !== segments.length) continue;
|
|
826
|
+
let literals = 0;
|
|
827
|
+
let matched = true;
|
|
828
|
+
for (const [index, part] of template.entries()) {
|
|
829
|
+
if (isTemplateParam(part)) continue;
|
|
830
|
+
if (part !== segments[index]) {
|
|
831
|
+
matched = false;
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
literals++;
|
|
835
|
+
}
|
|
836
|
+
if (matched && literals > bestLiterals) {
|
|
837
|
+
best = operation;
|
|
838
|
+
bestLiterals = literals;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return best;
|
|
842
|
+
};
|
|
843
|
+
/**
|
|
844
|
+
* The client chain a successor URL names, under the *same* HTTP method.
|
|
845
|
+
*
|
|
846
|
+
* RFC 5829's `successor-version` points at a resource, and an earlier revision
|
|
847
|
+
* of this code fell back to any unambiguous match under a different method
|
|
848
|
+
* when the same one missed. That was wrong in a way that matters: it could
|
|
849
|
+
* answer a deprecated `GET` with "use `client.<resource>.create` instead", telling the
|
|
850
|
+
* caller to replace a read with a write. A successor named under a different
|
|
851
|
+
* method is not a successor for *this* operation, so it is left unnamed and
|
|
852
|
+
* the raw URL is printed instead — accurate and unhelpful beats confident and
|
|
853
|
+
* wrong.
|
|
854
|
+
*/
|
|
855
|
+
const matchSuccessorChain = (method, successor, operations) => matchOperation(method, successor.pathname, operations)?.chain ?? null;
|
|
856
|
+
const chainLabel = (chain) => `\`client.${chain.join(".")}\``;
|
|
857
|
+
const formatDeprecationWarning = (subject, notice, successorChain) => {
|
|
858
|
+
const parts = [`Composio: ${subject} is deprecated.`];
|
|
859
|
+
if (notice.deprecatedAt !== null) parts.push(`Announced ${notice.deprecatedAt.toISOString()}.`);
|
|
860
|
+
if (notice.sunsetAt !== null) parts.push(`Scheduled for removal on ${notice.sunsetAt.toISOString()}.`);
|
|
861
|
+
if (successorChain !== null) parts.push(`Use ${chainLabel(successorChain)} instead.`);
|
|
862
|
+
else if (notice.successor !== null) parts.push(`Successor: ${notice.successor.href}.`);
|
|
863
|
+
if (notice.documentation !== null) parts.push(`See ${notice.documentation}`);
|
|
864
|
+
return parts.join(" ");
|
|
865
|
+
};
|
|
866
|
+
/**
|
|
867
|
+
* NUL joins the halves of a warn-once key. Neither a client chain nor a URL
|
|
868
|
+
* the WHATWG parser produced can carry one, so no pair of distinct components
|
|
869
|
+
* can collide by concatenation.
|
|
870
|
+
*/
|
|
871
|
+
const KEY_SEPARATOR = "\0";
|
|
872
|
+
/**
|
|
873
|
+
* What makes one notice *distinct* from another, for the warn-once record.
|
|
874
|
+
*
|
|
875
|
+
* Derived from the parsed fields rather than the raw header bytes on purpose:
|
|
876
|
+
* a requoted `Link` or a stray space around `Deprecation` reads as the same
|
|
877
|
+
* notice and must not warn a second time. The three fields here are the ones a
|
|
878
|
+
* caller acts on — when the deprecation was announced, when the operation
|
|
879
|
+
* stops being served, and what replaces it. `documentation` is deliberately
|
|
880
|
+
* excluded: it points at prose *about* this notice, so the platform moving its
|
|
881
|
+
* developer notes to another URL is not news worth repeating a warning for.
|
|
882
|
+
*/
|
|
883
|
+
const noticeKey = (notice) => [
|
|
884
|
+
notice.deprecatedAt?.getTime() ?? "",
|
|
885
|
+
notice.sunsetAt?.getTime() ?? "",
|
|
886
|
+
notice.successor?.href ?? ""
|
|
887
|
+
].join(KEY_SEPARATOR);
|
|
888
|
+
/**
|
|
889
|
+
* The interceptor itself: at most one warning per distinct notice per
|
|
890
|
+
* deprecated operation per client instance.
|
|
891
|
+
*
|
|
892
|
+
* It sits at the single response exit in `makeRequest`, after the retry
|
|
893
|
+
* decision has settled, so a response that is about to be discarded and
|
|
894
|
+
* retried never warns and a logical call warns at most once however the caller
|
|
895
|
+
* consumes it — `APIPromise` awaits one `makeRequest` per call and hands the
|
|
896
|
+
* same `Response` to `asResponse()`, `withResponse()` and the parsed path
|
|
897
|
+
* alike.
|
|
898
|
+
*/
|
|
899
|
+
const inspectDeprecation = ({ response, method, path, baseURL, operations, seen, warn }) => {
|
|
900
|
+
const notice = parseDeprecation(response.headers, response.url || baseURL, baseURL);
|
|
901
|
+
if (notice === null) return;
|
|
902
|
+
const operation = matchOperation(method, path, operations);
|
|
903
|
+
const key = `${operation === null ? `${method} ${pathSegments(path).join("/")}` : operation.chain.join(".")}${KEY_SEPARATOR}${noticeKey(notice)}`;
|
|
904
|
+
if (seen.has(key)) return;
|
|
905
|
+
if (seen.size >= MAX_TRACKED_NOTICES) return;
|
|
906
|
+
seen.add(key);
|
|
907
|
+
const successorChain = notice.successor === null ? null : matchSuccessorChain(method, notice.successor, operations);
|
|
908
|
+
const subject = operation === null ? `${method.toUpperCase()} ${path}` : chainLabel(operation.chain);
|
|
909
|
+
warn(formatDeprecationWarning(subject, notice, successorChain));
|
|
910
|
+
};
|
|
911
|
+
/**
|
|
912
|
+
* The keys a value map will actually carry on the wire. Both serializers drop
|
|
913
|
+
* an entry whose value is `undefined` outright — `JSON.stringify` for a body
|
|
914
|
+
* property, `stringifyQuery` for a query entry — while `null` is sent (JSON
|
|
915
|
+
* `null`, or `key=`), so only `undefined` reads as "not sent". `bodyKeys`
|
|
916
|
+
* relies on the same rule for its half of the surface.
|
|
917
|
+
*/
|
|
918
|
+
const presentKeys = (values) => new Set(Object.entries(values).filter(([, value]) => value !== void 0).map(([key]) => key));
|
|
919
|
+
/**
|
|
920
|
+
* The keys `body` will actually carry on the wire, at its top level, when it
|
|
921
|
+
* is a plain object — `presentKeys` over it, narrowed to objects: a resource
|
|
922
|
+
* method always passes an object (even an omitted optional body arrives as
|
|
923
|
+
* `{}`), so this covers every real call; a non-object `body` (the raw escape
|
|
924
|
+
* hatch dispatching something else entirely) has no top-level keys to check.
|
|
925
|
+
*/
|
|
926
|
+
const bodyKeys = (body) => typeof body === "object" && body !== null && !Array.isArray(body) ? presentKeys(body) : /* @__PURE__ */ new Set();
|
|
927
|
+
/**
|
|
928
|
+
* The static parameter-level warning: fires when the client sends an input
|
|
929
|
+
* the committed spec marks `deprecated: true`, decided entirely from the
|
|
930
|
+
* request the caller is about to send. Unlike `inspectDeprecation`, this
|
|
931
|
+
* needs no response — it runs before dispatch, so a connection failure never
|
|
932
|
+
* swallows it.
|
|
933
|
+
*
|
|
934
|
+
* One notice per (operation, sorted set of deprecated wire names present) per
|
|
935
|
+
* client instance, but every input in that set is named in its own `warn`
|
|
936
|
+
* call — the once-only key only suppresses an exact-repeat call, it does not
|
|
937
|
+
* collapse several inputs on one call into a single message.
|
|
938
|
+
*/
|
|
939
|
+
const inspectDeprecatedInputs = ({ operation, query, body, headers, table, seen, warn }) => {
|
|
940
|
+
if (operation === null) return;
|
|
941
|
+
const declared = table.get(operation.chain.join("."));
|
|
942
|
+
if (declared === void 0 || declared.length === 0) return;
|
|
943
|
+
const queryKeys = presentKeys(query);
|
|
944
|
+
const bodyKeySet = bodyKeys(body);
|
|
945
|
+
const present = declared.filter((input) => {
|
|
946
|
+
if (input.in === "query") return queryKeys.has(input.wire);
|
|
947
|
+
if (input.in === "body") return bodyKeySet.has(input.wire);
|
|
948
|
+
return headers.has(input.wire);
|
|
949
|
+
});
|
|
950
|
+
if (present.length === 0) return;
|
|
951
|
+
const wireSet = [...new Set(present.map((input) => input.wire))].sort();
|
|
952
|
+
const key = `param${KEY_SEPARATOR}${operation.chain.join(".")}${KEY_SEPARATOR}${wireSet.join(",")}`;
|
|
953
|
+
if (seen.has(key)) return;
|
|
954
|
+
if (seen.size >= MAX_TRACKED_NOTICES) return;
|
|
955
|
+
seen.add(key);
|
|
956
|
+
const subject = chainLabel(operation.chain);
|
|
957
|
+
for (const input of present) {
|
|
958
|
+
const clause = input.replacementWire === null ? "" : ` Use \`${input.replacementWire}\` instead.`;
|
|
959
|
+
warn(`Composio: the \`${input.wire}\` parameter of ${subject} is deprecated.${clause}`);
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
//#endregion
|
|
964
|
+
//#region src/compat/request.ts
|
|
965
|
+
/** A request label safe to print: dynamic path values and URL secrets are excluded. */
|
|
966
|
+
const printableURL = (url, operation) => `${new URL(url).origin}${operation?.path ?? "/<custom>"}`;
|
|
967
|
+
const emitLog = (config, level, message, metadata) => {
|
|
968
|
+
try {
|
|
969
|
+
config.onLog?.(level, message, metadata);
|
|
970
|
+
} catch {}
|
|
971
|
+
};
|
|
972
|
+
const monotonicMillis = () => globalThis.performance?.now?.() ?? Date.now();
|
|
973
|
+
const elapsedMillis = (startedAt) => Math.max(0, monotonicMillis() - startedAt);
|
|
974
|
+
/**
|
|
975
|
+
* Percent-encode everything that isn't a RFC-3986 pchar, leaving safe chars
|
|
976
|
+
* (unreserved / sub-delims / ":" / "@") readable.
|
|
977
|
+
*/
|
|
978
|
+
const encodeURIPath = (str) => str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
|
|
979
|
+
/**
|
|
980
|
+
* Template tag interpolating path params percent-encoded, rejecting values
|
|
981
|
+
* that would introduce `.` / `..` (or percent-encoded variants) path segments.
|
|
982
|
+
*/
|
|
983
|
+
const path = (statics, ...params) => {
|
|
984
|
+
let result = statics[0] ?? "";
|
|
985
|
+
params.forEach((param, index) => {
|
|
986
|
+
result += encodeURIPath(String(param)) + (statics[index + 1] ?? "");
|
|
987
|
+
});
|
|
988
|
+
const pathOnly = result.split(/[?#]/, 1)[0] ?? "";
|
|
989
|
+
if (/(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/i.test(pathOnly)) throw new ComposioError(`Path parameters result in path with invalid segments: ${result}`);
|
|
990
|
+
return result;
|
|
991
|
+
};
|
|
992
|
+
/**
|
|
993
|
+
* Stainless-flavored query serialization: `arrayFormat: 'comma'` (arrays become
|
|
994
|
+
* one comma-joined param), `undefined` entries dropped, `null` entries kept as
|
|
995
|
+
* empty values, and plain-object values expanded qs-style into bracket
|
|
996
|
+
* notation (`toolkit_versions[github]=v1`, the whole key percent-encoded) —
|
|
997
|
+
* pinned differentially against Stainless via `tools.retrieve`'s object
|
|
998
|
+
* `toolkit_versions` param (wayfinder ticket 23).
|
|
999
|
+
*/
|
|
1000
|
+
const stringifyQuery = (query) => {
|
|
1001
|
+
const parts = [];
|
|
1002
|
+
const append = (key, value) => {
|
|
1003
|
+
if (value === void 0) return;
|
|
1004
|
+
if (value === null) {
|
|
1005
|
+
parts.push(`${encodeURIComponent(key)}=`);
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
if (Array.isArray(value)) {
|
|
1009
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value.join(","))}`);
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
if (typeof value === "object") {
|
|
1013
|
+
for (const [subKey, subValue] of Object.entries(value)) append(`${key}[${subKey}]`, subValue);
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
|
1017
|
+
};
|
|
1018
|
+
for (const [key, value] of Object.entries(query)) append(key, value);
|
|
1019
|
+
return parts.join("&");
|
|
1020
|
+
};
|
|
1021
|
+
const buildURL = (config, options) => {
|
|
1022
|
+
const base = config.baseURL;
|
|
1023
|
+
const joined = base.endsWith("/") && options.path.startsWith("/") ? options.path.slice(1) : options.path;
|
|
1024
|
+
const url = new URL(base + joined);
|
|
1025
|
+
const query = {
|
|
1026
|
+
...Object.fromEntries(url.searchParams),
|
|
1027
|
+
...config.defaultQuery,
|
|
1028
|
+
...options.query
|
|
1029
|
+
};
|
|
1030
|
+
const search = stringifyQuery(query);
|
|
1031
|
+
if (search) url.search = search;
|
|
1032
|
+
return {
|
|
1033
|
+
url: url.toString(),
|
|
1034
|
+
query
|
|
1035
|
+
};
|
|
1036
|
+
};
|
|
1037
|
+
/**
|
|
1038
|
+
* JSON body encoding. A resource method always passes the `body` key when its
|
|
1039
|
+
* operation defines a request body — even an omitted optional body arrives as
|
|
1040
|
+
* `{}` — so `Content-Type: application/json` is kept for those; operations
|
|
1041
|
+
* with no body at all (GET/DELETE) send neither body nor content-type.
|
|
1042
|
+
*/
|
|
1043
|
+
const buildBody = (options) => {
|
|
1044
|
+
if (!("body" in options) || options.body === void 0) return {
|
|
1045
|
+
body: void 0,
|
|
1046
|
+
bodyHeaders: void 0
|
|
1047
|
+
};
|
|
1048
|
+
return {
|
|
1049
|
+
body: JSON.stringify(options.body),
|
|
1050
|
+
bodyHeaders: { "content-type": "application/json" }
|
|
1051
|
+
};
|
|
1052
|
+
};
|
|
1053
|
+
const safeJSON = (text) => {
|
|
1054
|
+
try {
|
|
1055
|
+
return JSON.parse(text);
|
|
1056
|
+
} catch {
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
const castToError = (err) => {
|
|
1061
|
+
if (err instanceof Error) return err;
|
|
1062
|
+
if (typeof err === "object" && err !== null) try {
|
|
1063
|
+
return new Error(JSON.stringify(err));
|
|
1064
|
+
} catch {}
|
|
1065
|
+
return new Error(String(err));
|
|
1066
|
+
};
|
|
1067
|
+
const validatePositiveInteger = (name, value) => {
|
|
1068
|
+
if (!Number.isInteger(value)) throw new ComposioError(`${name} must be an integer`);
|
|
1069
|
+
if (value < 0) throw new ComposioError(`${name} must be a positive integer`);
|
|
1070
|
+
};
|
|
1071
|
+
const RETRYABLE_STATUSES = /* @__PURE__ */ new Set([
|
|
1072
|
+
408,
|
|
1073
|
+
409,
|
|
1074
|
+
429
|
|
1075
|
+
]);
|
|
1076
|
+
const shouldRetryResponse = (response) => {
|
|
1077
|
+
const explicit = response.headers.get("x-should-retry");
|
|
1078
|
+
if (explicit === "true") return true;
|
|
1079
|
+
if (explicit === "false") return false;
|
|
1080
|
+
return RETRYABLE_STATUSES.has(response.status) || response.status >= 500;
|
|
1081
|
+
};
|
|
1082
|
+
/**
|
|
1083
|
+
* Ceiling for server-supplied retry delays. A `retry-after(-ms)` value is
|
|
1084
|
+
* honored only when `0 <= ms < 60_000`; anything negative, non-finite, or a
|
|
1085
|
+
* minute and beyond falls back to exponential backoff instead of collapsing
|
|
1086
|
+
* the backoff to zero or stalling the caller for the server's whim.
|
|
1087
|
+
*/
|
|
1088
|
+
const MAX_RETRY_AFTER_MILLIS = 6e4;
|
|
1089
|
+
/**
|
|
1090
|
+
* How long to wait before retry number `numRetries + 1`: `retry-after-ms`
|
|
1091
|
+
* (milliseconds), then `retry-after` (seconds or HTTP date), then exponential
|
|
1092
|
+
* backoff `min(0.5s · 2^n, 8s)` with up to 25% downward jitter. A
|
|
1093
|
+
* header-derived delay is used only when it is finite and within
|
|
1094
|
+
* `[0, MAX_RETRY_AFTER_MILLIS)`; out-of-range values fall through to the
|
|
1095
|
+
* backoff.
|
|
1096
|
+
*/
|
|
1097
|
+
const retryDelayMillis = (numRetries, headers) => {
|
|
1098
|
+
let headerDelayMillis;
|
|
1099
|
+
const retryAfterMs = parseFloat(headers?.get("retry-after-ms") ?? "");
|
|
1100
|
+
if (!Number.isNaN(retryAfterMs)) headerDelayMillis = retryAfterMs;
|
|
1101
|
+
else {
|
|
1102
|
+
const retryAfter = headers?.get("retry-after");
|
|
1103
|
+
if (retryAfter) {
|
|
1104
|
+
const seconds = parseFloat(retryAfter);
|
|
1105
|
+
if (!Number.isNaN(seconds)) headerDelayMillis = seconds * 1e3;
|
|
1106
|
+
else {
|
|
1107
|
+
const untilDate = Date.parse(retryAfter) - Date.now();
|
|
1108
|
+
if (!Number.isNaN(untilDate)) headerDelayMillis = untilDate;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
if (headerDelayMillis !== void 0 && headerDelayMillis >= 0 && headerDelayMillis < MAX_RETRY_AFTER_MILLIS) return headerDelayMillis;
|
|
1113
|
+
return Math.min(.5 * 2 ** numRetries, 8) * (1 - Math.random() * .25) * 1e3;
|
|
1114
|
+
};
|
|
1115
|
+
const REDIRECT_STATUSES = /* @__PURE__ */ new Set([
|
|
1116
|
+
301,
|
|
1117
|
+
302,
|
|
1118
|
+
303,
|
|
1119
|
+
307,
|
|
1120
|
+
308
|
|
1121
|
+
]);
|
|
1122
|
+
/**
|
|
1123
|
+
* How many hops one attempt will follow before giving up. Twenty is what the
|
|
1124
|
+
* Fetch spec budgets and what undici and every browser enforce, so a chain this
|
|
1125
|
+
* kernel refuses is one the platform would have refused too.
|
|
1126
|
+
*/
|
|
1127
|
+
const MAX_REDIRECTS = 20;
|
|
1128
|
+
const TOO_MANY_REDIRECTS = `The API redirected more than ${MAX_REDIRECTS} times; the chain was abandoned.`;
|
|
1129
|
+
/**
|
|
1130
|
+
* The one environment-dependent behavior in the kernel, stated rather than
|
|
1131
|
+
* degraded into — and narrowed to the case where safety cannot be proved.
|
|
1132
|
+
*
|
|
1133
|
+
* Following redirects by hand requires reading the `Location` off a 3xx, and
|
|
1134
|
+
* only a runtime that hands back the real response can offer that — Node,
|
|
1135
|
+
* Deno and Bun do. A **browser** answers `redirect: 'manual'` with a *filtered*
|
|
1136
|
+
* response (`type: 'opaqueredirect'`, status 0, no headers), so there is
|
|
1137
|
+
* nothing to read and manual following is not implementable there.
|
|
1138
|
+
*
|
|
1139
|
+
* Dispatching at `redirect: 'follow'` instead is exactly what this module
|
|
1140
|
+
* exists to prevent *when a credential is attached*: the Fetch spec deletes
|
|
1141
|
+
* only `Authorization` across a cross-origin redirect, so a custom `x-api-key`
|
|
1142
|
+
* rides along, and an attacker-controlled origin that answers the preflight
|
|
1143
|
+
* with a permissive `Access-Control-Allow-Headers` harvests it. (CORS bounds
|
|
1144
|
+
* that — any request carrying `x-api-key` is preflighted, so the third party
|
|
1145
|
+
* has to cooperate — but bounded is not prevented.)
|
|
1146
|
+
*
|
|
1147
|
+
* When *nothing* credential-bearing is going out that reasoning has no
|
|
1148
|
+
* premise, so the first dispatch of such a chain is delegated (see
|
|
1149
|
+
* `carriesCredentials`) and a browser follows it normally. That is where
|
|
1150
|
+
* real-world redirects on this API actually live: an escape-hatch fetch of a
|
|
1151
|
+
* presigned storage URL, which rule 2 has already stripped our key from, and
|
|
1152
|
+
* which S3 and GCS commonly answer with a 302.
|
|
1153
|
+
*
|
|
1154
|
+
* What is left is a *credentialed* request that redirects in a browser. It
|
|
1155
|
+
* gets a named error rather than a silent leak — and on this API that is very
|
|
1156
|
+
* nearly unreachable: the only redirects the platform serves are same-origin
|
|
1157
|
+
* edge normalizations (wayfinder ticket 40 records the recon), and `buildURL`
|
|
1158
|
+
* cannot construct a URL that triggers one.
|
|
1159
|
+
*/
|
|
1160
|
+
const OPAQUE_REDIRECT = "The API responded with a redirect that this runtime will not let the client read (an opaque redirect, in a browser). Redirects are followed only where the response can be inspected, because a credential must never cross an origin change.";
|
|
1161
|
+
const isOpaqueRedirect = (response) => response.type === "opaqueredirect";
|
|
1162
|
+
/**
|
|
1163
|
+
* The `Location` a hop points at, resolved against the URL it arrived on, or
|
|
1164
|
+
* `null` when there is nothing followable there — unparseable, or a scheme
|
|
1165
|
+
* this client does not speak (`data:`, `file:`, an app link). `null` hands the
|
|
1166
|
+
* 3xx back as the response it is, which is what `fetch` does with a `Location`
|
|
1167
|
+
* it cannot use.
|
|
1168
|
+
*/
|
|
1169
|
+
const resolveLocation = (location, from) => resolveHTTPURL(location, from);
|
|
1170
|
+
/**
|
|
1171
|
+
* Fetch's own method rewrite (§4.4 "HTTP-redirect fetch", step 13): a 303
|
|
1172
|
+
* turns any non-GET into a GET, and a 301/302 does the same to a POST — both
|
|
1173
|
+
* dropping the body. 307 and 308 preserve the method and the body, which is
|
|
1174
|
+
* what they exist for. Following by hand means reproducing this, or a caller's
|
|
1175
|
+
* POST silently becomes a POST to somewhere it was never sent before.
|
|
1176
|
+
*/
|
|
1177
|
+
const rewriteForRedirect = (status, method) => status === 303 && method !== "GET" && method !== "HEAD" || (status === 301 || status === 302) && method === "POST";
|
|
1178
|
+
const sleep = (ms, signal) => {
|
|
1179
|
+
if (signal?.aborted) return Promise.reject(new APIUserAbortError());
|
|
1180
|
+
return new Promise((resolve, reject) => {
|
|
1181
|
+
const onAbort = () => {
|
|
1182
|
+
clearTimeout(timer);
|
|
1183
|
+
reject(new APIUserAbortError());
|
|
1184
|
+
};
|
|
1185
|
+
const timer = setTimeout(() => {
|
|
1186
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1187
|
+
resolve();
|
|
1188
|
+
}, ms);
|
|
1189
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1190
|
+
});
|
|
1191
|
+
};
|
|
1192
|
+
/**
|
|
1193
|
+
* Indirection over `sleep` so kernel tests can capture retry delays instead
|
|
1194
|
+
* of waiting them out on the wall clock. Internal seam only — deliberately
|
|
1195
|
+
* not re-exported from `compat/index.ts`, so the public package surface is
|
|
1196
|
+
* unchanged.
|
|
1197
|
+
*/
|
|
1198
|
+
const kernelInternals = { sleep };
|
|
1199
|
+
/**
|
|
1200
|
+
* The headers for one hop, built fresh from the URL that hop is going to.
|
|
1201
|
+
*
|
|
1202
|
+
* This is where rule 3 mostly stops needing machinery of its own: the
|
|
1203
|
+
* credential is re-derived per hop from the *request* URL rather than carried
|
|
1204
|
+
* along from the last one, so a hop that leaves the configured API origin
|
|
1205
|
+
* simply does not get a key. `withdrawn` covers what that cannot — the
|
|
1206
|
+
* caller's own credential headers, which are legitimate on the URL the caller
|
|
1207
|
+
* chose and are dropped once the *server* has chosen one.
|
|
1208
|
+
*/
|
|
1209
|
+
const buildRequestHeaders = (config, options, requestURL, bodyHeaders, withdrawn, scheme, callerPlaced) => {
|
|
1210
|
+
const credential = withdrawn || callerPlaced ? null : credentialForRequest(config.baseURL, scheme, config.credentials, requestURL);
|
|
1211
|
+
const headers = buildHeaders([
|
|
1212
|
+
{
|
|
1213
|
+
accept: "application/json",
|
|
1214
|
+
"user-agent": config.userAgent
|
|
1215
|
+
},
|
|
1216
|
+
credential === null ? void 0 : { [credential.name]: credential.value },
|
|
1217
|
+
config.defaultHeaders,
|
|
1218
|
+
bodyHeaders,
|
|
1219
|
+
options.headers
|
|
1220
|
+
]);
|
|
1221
|
+
if (withdrawn) withdrawCredentials(headers);
|
|
1222
|
+
return headers;
|
|
1223
|
+
};
|
|
1224
|
+
/**
|
|
1225
|
+
* The credential class this logical request authenticates with, decided once
|
|
1226
|
+
* from the operation the *caller* invoked.
|
|
1227
|
+
*
|
|
1228
|
+
* Deliberately not re-derived per redirect hop. A hop's URL was chosen by the
|
|
1229
|
+
* server, so matching it back to a template would let the server's choice
|
|
1230
|
+
* change which of the caller's credentials goes out; the operation the caller
|
|
1231
|
+
* asked for is the honest answer for the whole chain, and rule 3 already
|
|
1232
|
+
* withdraws everything once a hop leaves the origin.
|
|
1233
|
+
*
|
|
1234
|
+
* `operation` is the match `makeRequest` already computed for the static
|
|
1235
|
+
* deprecated-input check, threaded in rather than re-derived: the two
|
|
1236
|
+
* features need the same fact, so `matchOperation` runs once per logical call
|
|
1237
|
+
* regardless of which of them a given client configuration actually uses.
|
|
1238
|
+
* With only `apiKey` set the scheme answer is still `ApiKeyAuth` down every
|
|
1239
|
+
* branch of {@link selectScheme}, so an existing caller's requests stay
|
|
1240
|
+
* byte-identical by construction; what changed is that the match itself is no
|
|
1241
|
+
* longer skippable, because the deprecated-input warning needs it too.
|
|
1242
|
+
*/
|
|
1243
|
+
const schemeForRequest = (config, operation) => {
|
|
1244
|
+
if (!hasAlternateCredential(config.credentials)) return "ApiKeyAuth";
|
|
1245
|
+
return selectScheme(operation?.schemes, config.credentials);
|
|
1246
|
+
};
|
|
1247
|
+
/**
|
|
1248
|
+
* One attempt: the request, plus however many redirects it takes to land on a
|
|
1249
|
+
* non-3xx response.
|
|
1250
|
+
*
|
|
1251
|
+
* The whole chain sits **inside one attempt** — one entry in the retry budget,
|
|
1252
|
+
* one per-attempt timeout covering every hop. Both follow from what a redirect
|
|
1253
|
+
* is: the caller made one logical request, so a chain must not consume
|
|
1254
|
+
* `maxRetries` (a 3xx is not a failure to retry) and must not be able to
|
|
1255
|
+
* multiply the caller's time budget by the hop limit. A retry re-issues the
|
|
1256
|
+
* *original* request, chain and all.
|
|
1257
|
+
*
|
|
1258
|
+
* Everything the server chose is re-derived rather than carried: the headers
|
|
1259
|
+
* (see `buildRequestHeaders`), the method and body (fetch's own 301/302/303
|
|
1260
|
+
* rewrite), and the query, which is left to the `Location` URL because the
|
|
1261
|
+
* server picked it. `config.defaultQuery` is applied to the URL the caller
|
|
1262
|
+
* asked for, not to the one it was sent to.
|
|
1263
|
+
*/
|
|
1264
|
+
const dispatchWithRedirects = async (config, options, initialURL, initialBody, initialBodyHeaders, signal, scheme) => {
|
|
1265
|
+
let url = initialURL;
|
|
1266
|
+
let method = options.method.toUpperCase();
|
|
1267
|
+
let body = initialBody;
|
|
1268
|
+
let bodyHeaders = initialBodyHeaders;
|
|
1269
|
+
let withdrawn = false;
|
|
1270
|
+
const callerPlaced = callerPlacedCredential([config.defaultHeaders, options.headers]);
|
|
1271
|
+
for (let hop = 0;; hop++) {
|
|
1272
|
+
const headers = buildRequestHeaders(config, options, url, bodyHeaders, withdrawn, scheme, callerPlaced);
|
|
1273
|
+
const delegated = hop === 0 && !carriesCredentials(headers);
|
|
1274
|
+
const response = await config.fetch.call(void 0, url, {
|
|
1275
|
+
method,
|
|
1276
|
+
headers,
|
|
1277
|
+
...body !== void 0 ? { body } : {},
|
|
1278
|
+
...config.fetchOptions,
|
|
1279
|
+
...options.fetchOptions,
|
|
1280
|
+
redirect: delegated ? "follow" : "manual",
|
|
1281
|
+
signal
|
|
1282
|
+
});
|
|
1283
|
+
if (delegated) return response;
|
|
1284
|
+
if (isOpaqueRedirect(response)) throw new APIConnectionError({ message: OPAQUE_REDIRECT });
|
|
1285
|
+
if (!REDIRECT_STATUSES.has(response.status)) return response;
|
|
1286
|
+
const location = response.headers.get("location");
|
|
1287
|
+
if (location === null) return response;
|
|
1288
|
+
const target = resolveLocation(location, url);
|
|
1289
|
+
if (target === null) return response;
|
|
1290
|
+
if (hop >= MAX_REDIRECTS) throw new APIConnectionError({ message: TOO_MANY_REDIRECTS });
|
|
1291
|
+
response.body?.cancel().catch(() => void 0);
|
|
1292
|
+
if (target.origin !== new URL(url).origin) withdrawn = true;
|
|
1293
|
+
if (rewriteForRedirect(response.status, method)) {
|
|
1294
|
+
method = "GET";
|
|
1295
|
+
body = void 0;
|
|
1296
|
+
bodyHeaders = void 0;
|
|
1297
|
+
}
|
|
1298
|
+
url = target.href;
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
/**
|
|
1302
|
+
* The dispatch kernel: one attempt loop with Stainless runtime semantics —
|
|
1303
|
+
* per-attempt timeouts (timeouts and connection errors are retried, like
|
|
1304
|
+
* HTTP-level 408/409/429/5xx and `x-should-retry`), `retry-after(-ms)`
|
|
1305
|
+
* honored, abort → `APIUserAbortError`. Resolves with the raw un-consumed
|
|
1306
|
+
* `Response` regardless of HTTP status; body parsing and error mapping are
|
|
1307
|
+
* strictly deferred to `APIPromise`.
|
|
1308
|
+
*
|
|
1309
|
+
* Redirects are followed inside an attempt by `dispatchWithRedirects`, so the
|
|
1310
|
+
* single response exit below still fires `onResponse` **once per logical
|
|
1311
|
+
* call** rather than once per hop — the once-per-call guarantee ticket 34
|
|
1312
|
+
* built the deprecation interceptor on. The `Response` handed back is the
|
|
1313
|
+
* final hop's, so `response.url` is still "the URL this response came from",
|
|
1314
|
+
* which is what `deprecation.ts` resolves relative `Link` targets against.
|
|
1315
|
+
*/
|
|
1316
|
+
async function makeRequest(config, options) {
|
|
1317
|
+
if (options.timeout !== void 0) validatePositiveInteger("timeout", options.timeout);
|
|
1318
|
+
const maxRetries = options.maxRetries ?? config.maxRetries;
|
|
1319
|
+
validatePositiveInteger("maxRetries", maxRetries);
|
|
1320
|
+
const timeout = options.timeout ?? config.timeout;
|
|
1321
|
+
const { url, query } = buildURL(config, options);
|
|
1322
|
+
const { body, bodyHeaders } = buildBody(options);
|
|
1323
|
+
const userSignal = options.signal ?? void 0;
|
|
1324
|
+
const method = options.method.toUpperCase();
|
|
1325
|
+
const operation = matchOperation(options.method, options.path, config.operations ?? []);
|
|
1326
|
+
const requestLog = config.onLog === void 0 ? void 0 : {
|
|
1327
|
+
method,
|
|
1328
|
+
url: printableURL(url, operation),
|
|
1329
|
+
maxAttempts: maxRetries + 1
|
|
1330
|
+
};
|
|
1331
|
+
const scheme = schemeForRequest(config, operation);
|
|
1332
|
+
config.onRequest?.(operation, query, options.body, buildHeaders([config.defaultHeaders, options.headers]));
|
|
1333
|
+
for (let numRetries = 0;; numRetries++) {
|
|
1334
|
+
if (userSignal?.aborted) throw new APIUserAbortError();
|
|
1335
|
+
const attempt = numRetries + 1;
|
|
1336
|
+
const retriesRemaining = maxRetries - numRetries;
|
|
1337
|
+
const startedAt = requestLog === void 0 ? 0 : monotonicMillis();
|
|
1338
|
+
const attemptLog = requestLog === void 0 ? void 0 : {
|
|
1339
|
+
...requestLog,
|
|
1340
|
+
attempt
|
|
1341
|
+
};
|
|
1342
|
+
if (attemptLog !== void 0) emitLog(config, "debug", "Sending request", attemptLog);
|
|
1343
|
+
const controller = new AbortController();
|
|
1344
|
+
const abort = () => controller.abort();
|
|
1345
|
+
userSignal?.addEventListener("abort", abort, { once: true });
|
|
1346
|
+
const timer = setTimeout(abort, timeout);
|
|
1347
|
+
let result;
|
|
1348
|
+
try {
|
|
1349
|
+
result = await dispatchWithRedirects(config, options, url, body, bodyHeaders, controller.signal, scheme);
|
|
1350
|
+
} catch (cause) {
|
|
1351
|
+
if (cause instanceof ComposioError) {
|
|
1352
|
+
if (attemptLog !== void 0) emitLog(config, "info", "Request failed", {
|
|
1353
|
+
...attemptLog,
|
|
1354
|
+
durationMs: elapsedMillis(startedAt),
|
|
1355
|
+
errorType: cause.constructor.name
|
|
1356
|
+
});
|
|
1357
|
+
throw cause;
|
|
1358
|
+
}
|
|
1359
|
+
result = castToError(cause);
|
|
1360
|
+
} finally {
|
|
1361
|
+
clearTimeout(timer);
|
|
1362
|
+
userSignal?.removeEventListener("abort", abort);
|
|
1363
|
+
}
|
|
1364
|
+
if (result instanceof Error) {
|
|
1365
|
+
if (userSignal?.aborted) {
|
|
1366
|
+
if (attemptLog !== void 0) emitLog(config, "info", "Request failed", {
|
|
1367
|
+
...attemptLog,
|
|
1368
|
+
durationMs: elapsedMillis(startedAt),
|
|
1369
|
+
errorType: APIUserAbortError.name
|
|
1370
|
+
});
|
|
1371
|
+
throw new APIUserAbortError();
|
|
1372
|
+
}
|
|
1373
|
+
const isTimeout = controller.signal.aborted || /timed? ?out/i.test(String(result) + ("cause" in result ? String(result.cause) : ""));
|
|
1374
|
+
const errorType = isTimeout ? APIConnectionTimeoutError.name : APIConnectionError.name;
|
|
1375
|
+
if (attemptLog !== void 0) emitLog(config, "info", "Request failed", {
|
|
1376
|
+
...attemptLog,
|
|
1377
|
+
durationMs: elapsedMillis(startedAt),
|
|
1378
|
+
errorType
|
|
1379
|
+
});
|
|
1380
|
+
if (retriesRemaining > 0) {
|
|
1381
|
+
const delayMs = retryDelayMillis(numRetries);
|
|
1382
|
+
if (attemptLog !== void 0) emitLog(config, "info", "Retrying request", {
|
|
1383
|
+
...attemptLog,
|
|
1384
|
+
nextAttempt: attempt + 1,
|
|
1385
|
+
retriesRemaining,
|
|
1386
|
+
delayMs,
|
|
1387
|
+
reason: isTimeout ? "timeout" : "transport"
|
|
1388
|
+
});
|
|
1389
|
+
await kernelInternals.sleep(delayMs, userSignal);
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
if (isTimeout) throw new APIConnectionTimeoutError();
|
|
1393
|
+
throw new APIConnectionError({ cause: result });
|
|
1394
|
+
}
|
|
1395
|
+
if (attemptLog !== void 0) emitLog(config, "info", "Received response", {
|
|
1396
|
+
...attemptLog,
|
|
1397
|
+
status: result.status,
|
|
1398
|
+
outcome: result.ok ? "success" : "http_error",
|
|
1399
|
+
durationMs: elapsedMillis(startedAt)
|
|
1400
|
+
});
|
|
1401
|
+
if (!result.ok && retriesRemaining > 0 && shouldRetryResponse(result)) {
|
|
1402
|
+
result.body?.cancel().catch(() => void 0);
|
|
1403
|
+
const delayMs = retryDelayMillis(numRetries, result.headers);
|
|
1404
|
+
if (attemptLog !== void 0) emitLog(config, "info", "Retrying request", {
|
|
1405
|
+
...attemptLog,
|
|
1406
|
+
nextAttempt: attempt + 1,
|
|
1407
|
+
retriesRemaining,
|
|
1408
|
+
delayMs,
|
|
1409
|
+
reason: "response",
|
|
1410
|
+
status: result.status
|
|
1411
|
+
});
|
|
1412
|
+
await kernelInternals.sleep(delayMs, userSignal);
|
|
1413
|
+
continue;
|
|
1414
|
+
}
|
|
1415
|
+
if (config.onResponse !== void 0) try {
|
|
1416
|
+
config.onResponse(result, options);
|
|
1417
|
+
} catch {}
|
|
1418
|
+
return result;
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
//#endregion
|
|
1423
|
+
export { InternalServerError as _, inspectDeprecatedInputs as a, RateLimitError as b, mergeHeaders as c, APIError as d, APIUserAbortError as f, ConflictError as g, ComposioError as h, safeJSON as i, APIConnectionError as l, BadRequestError as m, makeRequest as n, inspectDeprecation as o, AuthenticationError as p, path as r, assertCredentialTransport as s, castToError as t, APIConnectionTimeoutError as u, NotFoundError as v, UnprocessableEntityError as x, PermissionDeniedError as y };
|