@zackbart/connecta 0.34.0 → 0.35.0
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/CHANGELOG.md +393 -0
- package/README.md +62 -105
- package/SECURITY.md +3 -2
- package/bin/connecta.mjs +41 -16
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.d.ts +2 -1
- package/dist/access-tokens.js +66 -30
- package/dist/activity.d.ts +1 -1
- package/dist/activity.js +2 -0
- package/dist/agent-config.d.ts +23 -0
- package/dist/agent-config.js +59 -0
- package/dist/agent-description.js +16 -0
- package/dist/agent-identity.js +4 -4
- package/dist/agent-observation.js +39 -0
- package/dist/auth/clerk-sdk/client.d.ts +2 -0
- package/dist/auth/clerk-sdk/generated.js +554 -563
- package/dist/auth/clerk-transport.js +70 -3
- package/dist/auth/clerk.d.ts +13 -0
- package/dist/auth/clerk.js +653 -246
- package/dist/auth/cloudflare-access.js +46 -42
- package/dist/auth/downstream-client-metadata.js +2 -2
- package/dist/auth/downstream-oauth.js +2051 -1160
- package/dist/auth/downstream-revocation.js +88 -88
- package/dist/auth/native-call.js +20 -0
- package/dist/auth/native.js +184 -0
- package/dist/auth/oauth-start-reset.js +11 -5
- package/dist/auth/oauth-token-response.js +121 -102
- package/dist/auth/oauth-v2-migration.js +97 -78
- package/dist/auth/renewable-grant.js +10 -0
- package/dist/auth/static-oauth.js +663 -271
- package/dist/auth/vercel-agent.js +266 -0
- package/dist/auth-admission.js +204 -0
- package/dist/auth-elicitation.js +350 -241
- package/dist/authorization-handoff.js +164 -90
- package/dist/call-admission-context.js +3 -10
- package/dist/call-admission.js +2 -0
- package/dist/call-classification.js +9 -0
- package/dist/call-recovery.js +32 -13
- package/dist/canonical-json.js +8 -0
- package/dist/catalog-cache.js +369 -213
- package/dist/catalog-service.js +659 -530
- package/dist/catalog.js +5 -1
- package/dist/concurrency.js +0 -25
- package/dist/config-defaults.d.ts +1 -0
- package/dist/config-defaults.js +1 -0
- package/dist/config-schema.d.ts +3 -1
- package/dist/config-schema.js +13 -3
- package/dist/config.d.ts +40 -6
- package/dist/config.js +40 -26
- package/dist/connector-caller.d.ts +4 -0
- package/dist/connector-caller.js +17 -4
- package/dist/connectors/api-connector.d.ts +15 -7
- package/dist/connectors/api-connector.js +147 -57
- package/dist/connectors/guarded-fetch.d.ts +51 -17
- package/dist/connectors/guarded-fetch.js +215 -121
- package/dist/connectors/negotiation-cache.js +20 -10
- package/dist/connectors/option-shapes.js +1 -1
- package/dist/connectors/remote-mcp.d.ts +12 -30
- package/dist/connectors/remote-mcp.js +960 -564
- package/dist/d1.js +10 -243
- package/dist/derived-context.js +5 -0
- package/dist/describe-config.d.ts +5 -1
- package/dist/describe-config.js +3 -0
- package/dist/downstream-input.js +347 -260
- package/dist/errors.d.ts +87 -6
- package/dist/errors.js +114 -4
- package/dist/execute.js +87 -50
- package/dist/executor-admission.js +3 -0
- package/dist/executors/quickjs.js +163 -112
- package/dist/guarded-transport.js +50 -0
- package/dist/guest-runtime.js +1 -1
- package/dist/http/client.js +439 -0
- package/dist/http/errors.js +558 -0
- package/dist/http/fields.js +49 -0
- package/dist/http/hatch-paging.js +318 -0
- package/dist/http/hatch.js +1279 -0
- package/dist/http/json-schema.js +267 -0
- package/dist/http/node-transport.js +241 -0
- package/dist/http/oauth.js +162 -0
- package/dist/http/paging.js +228 -0
- package/dist/http/provider.js +221 -0
- package/dist/http/safety.js +82 -0
- package/dist/http/services.js +79 -0
- package/dist/http/tools.js +383 -0
- package/dist/http/vendor-index.js +138 -0
- package/dist/http/vendor.js +448 -0
- package/dist/http/write-request.js +66 -0
- package/dist/http-policy.d.ts +11 -0
- package/dist/http-policy.js +45 -0
- package/dist/inbound-credential.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -0
- package/dist/invocation-auth.js +30 -19
- package/dist/invocation.js +221 -87
- package/dist/mcp-result.js +0 -4
- package/dist/membership.d.ts +4 -0
- package/dist/meta-output.js +1 -1
- package/dist/meta-tools.js +534 -408
- package/dist/native-recovery.js +52 -0
- package/dist/native-tools.js +382 -0
- package/dist/node.d.ts +4 -3
- package/dist/node.js +45 -8
- package/dist/oauth-handoff.js +177 -104
- package/dist/oauth-partition.js +6 -20
- package/dist/oauth-sealing.js +6 -7
- package/dist/operation-vocabulary.js +101 -0
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +17 -4
- package/dist/operator-ui/agents.d.ts +5 -4
- package/dist/operator-ui/agents.js +17 -7
- package/dist/operator-ui/config-summary.js +8 -4
- package/dist/operator-ui/connection-map.js +9 -6
- package/dist/operator-ui/contract.d.ts +7 -3
- package/dist/operator-ui/endpoints.js +5 -14
- package/dist/operator-ui/fix-prompts.d.ts +1 -1
- package/dist/operator-ui/fix-prompts.js +0 -8
- package/dist/operator-ui/generated.js +3 -3
- package/dist/operator-ui/health.js +52 -0
- package/dist/operator-ui/model.d.ts +2 -3
- package/dist/operator-ui/overview.js +11 -19
- package/dist/operator-ui/setup-commands.js +0 -4
- package/dist/operator-ui/view.d.ts +5 -29
- package/dist/operator-ui/view.js +3 -59
- package/dist/program-writes.js +47 -26
- package/dist/provider-call-facts.d.ts +115 -0
- package/dist/provider-call-facts.js +182 -0
- package/dist/provider-call.d.ts +112 -0
- package/dist/provider-call.js +271 -0
- package/dist/provider.d.ts +71 -32
- package/dist/provider.js +134 -40
- package/dist/providers/_shared/google/hatch.js +91 -0
- package/dist/providers/_shared/google/kit.js +537 -0
- package/dist/providers/_shared/google/result-size.js +15 -2
- package/dist/providers/_shared/google/service-account.d.ts +2 -1
- package/dist/providers/_shared/google/service-account.js +8 -4
- package/dist/providers/_shared/google/workspace.d.ts +8 -209
- package/dist/providers/_shared/google/workspace.js +7 -426
- package/dist/providers/_shared/rest/compact-index.js +86 -0
- package/dist/providers/_shared/rest/dispatch.js +3 -23
- package/dist/providers/_shared/rest/kit.js +546 -0
- package/dist/providers/_shared/rest/operation-index.js +230 -14
- package/dist/providers/_shared/rest/response-media.js +11 -0
- package/dist/providers/_shared/rest/tools.js +101 -78
- package/dist/providers/_shared/rest/value-safety.js +353 -195
- package/dist/providers/_shared/sensitive-fields.js +27 -0
- package/dist/providers/breeze/api.js +584 -0
- package/dist/providers/breeze/index.d.ts +3 -3
- package/dist/providers/breeze/index.js +67 -1420
- package/dist/providers/breeze/openapi.generated.js +60 -0
- package/dist/providers/breeze/skill.generated.js +2 -6
- package/dist/providers/breeze/tools.js +696 -0
- package/dist/providers/ccb/api.js +496 -0
- package/dist/providers/ccb/index.d.ts +17 -37
- package/dist/providers/ccb/index.js +77 -1702
- package/dist/providers/ccb/lib/constants.d.ts +34 -0
- package/dist/providers/ccb/lib/constants.js +74 -0
- package/dist/providers/ccb/lib/guide.js +9 -0
- package/dist/providers/ccb/lib/verdicts.js +387 -0
- package/dist/providers/ccb/openapi.generated.js +397 -0
- package/dist/providers/ccb/safety.js +295 -0
- package/dist/providers/ccb/skill.generated.js +2 -6
- package/dist/providers/ccb/tools.js +853 -0
- package/dist/providers/cloudflare/api.js +1066 -0
- package/dist/providers/cloudflare/index.d.ts +7 -22
- package/dist/providers/cloudflare/index.js +80 -194
- package/dist/providers/cloudflare/lib/config.d.ts +16 -0
- package/dist/providers/cloudflare/lib/config.js +2 -0
- package/dist/providers/cloudflare/lib/discovery.js +29 -0
- package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
- package/dist/providers/cloudflare/lib/hatch.js +103 -0
- package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
- package/dist/providers/cloudflare/lib/response-media.js +75 -0
- package/dist/providers/cloudflare/openapi.generated.js +3299 -3208
- package/dist/providers/cloudflare/{value-safety.js → safety.js} +538 -18
- package/dist/providers/cloudflare/skill.generated.js +2 -10
- package/dist/providers/cloudflare/tools.js +360 -0
- package/dist/providers/docs/api.js +51 -0
- package/dist/providers/docs/index.d.ts +1 -12
- package/dist/providers/docs/index.js +53 -1165
- package/dist/providers/docs/lib/constants.d.ts +18 -0
- package/dist/providers/docs/lib/constants.js +18 -0
- package/dist/providers/docs/lib/discovery.js +26 -0
- package/dist/providers/docs/lib/requests.js +48 -0
- package/dist/providers/docs/lib/schemas.js +168 -0
- package/dist/providers/docs/openapi.generated.js +16 -0
- package/dist/providers/docs/safety.js +54 -0
- package/dist/providers/docs/skill.generated.js +2 -6
- package/dist/providers/docs/tools.js +802 -0
- package/dist/providers/drive/api.js +177 -0
- package/dist/providers/drive/index.d.ts +1 -12
- package/dist/providers/drive/index.js +38 -1448
- package/dist/providers/drive/lib/constants.d.ts +6 -0
- package/dist/providers/drive/lib/constants.js +6 -0
- package/dist/providers/drive/lib/contracts.js +532 -0
- package/dist/providers/drive/lib/hatch.js +197 -0
- package/dist/providers/drive/openapi.generated.js +77 -0
- package/dist/providers/drive/safety.js +90 -0
- package/dist/providers/drive/skill.generated.js +2 -6
- package/dist/providers/drive/tools.js +1290 -0
- package/dist/providers/forms/api.js +71 -0
- package/dist/providers/forms/index.d.ts +1 -12
- package/dist/providers/forms/index.js +49 -1145
- package/dist/providers/forms/lib/constants.d.ts +14 -0
- package/dist/providers/forms/lib/constants.js +19 -0
- package/dist/providers/forms/lib/hatch.js +72 -0
- package/dist/providers/forms/openapi.generated.js +23 -0
- package/dist/providers/forms/safety.js +34 -0
- package/dist/providers/forms/skill.generated.js +2 -8
- package/dist/providers/forms/tools.js +1024 -0
- package/dist/providers/github/api.js +150 -0
- package/dist/providers/github/index.d.ts +4 -5
- package/dist/providers/github/index.js +30 -87
- package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
- package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
- package/dist/providers/github/lib/discovery.js +132 -0
- package/dist/providers/github/lib/operations.js +58 -0
- package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
- package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
- package/dist/providers/github/openapi.generated.js +1216 -0
- package/dist/providers/github/safety.js +216 -0
- package/dist/providers/github/skill.generated.js +2 -5
- package/dist/providers/github/tools.js +506 -0
- package/dist/providers/gmail/api.js +114 -0
- package/dist/providers/gmail/index.d.ts +1 -12
- package/dist/providers/gmail/index.js +73 -1621
- package/dist/providers/gmail/lib/constants.d.ts +13 -0
- package/dist/providers/gmail/lib/constants.js +16 -0
- package/dist/providers/gmail/lib/index.js +173 -0
- package/dist/providers/gmail/lib/schemas.js +176 -0
- package/dist/providers/gmail/lib/threading.js +120 -0
- package/dist/providers/gmail/openapi.generated.js +92 -0
- package/dist/providers/gmail/safety.js +21 -0
- package/dist/providers/gmail/skill.generated.js +2 -6
- package/dist/providers/gmail/tools.js +1435 -0
- package/dist/providers/overflow/api.js +1008 -0
- package/dist/providers/overflow/index.d.ts +3 -17
- package/dist/providers/overflow/index.js +60 -1488
- package/dist/providers/overflow/skill.generated.js +2 -5
- package/dist/providers/overflow/tools.js +873 -0
- package/dist/providers/planning-center/api.js +467 -0
- package/dist/providers/planning-center/index.d.ts +6 -35
- package/dist/providers/planning-center/index.js +69 -2445
- package/dist/providers/planning-center/lib/constants.d.ts +58 -0
- package/dist/providers/planning-center/lib/constants.js +83 -0
- package/dist/providers/planning-center/lib/discovery.js +40 -0
- package/dist/providers/planning-center/lib/guide.js +39 -0
- package/dist/providers/planning-center/lib/json-api.js +83 -0
- package/dist/providers/planning-center/lib/verdicts.js +1758 -0
- package/dist/providers/planning-center/openapi.generated.js +1764 -0
- package/dist/providers/planning-center/safety.js +188 -0
- package/dist/providers/planning-center/skill.generated.js +2 -27
- package/dist/providers/planning-center/tools.js +1010 -0
- package/dist/providers/sheets/api.js +133 -0
- package/dist/providers/sheets/index.d.ts +4 -16
- package/dist/providers/sheets/index.js +61 -1172
- package/dist/providers/sheets/lib/constants.d.ts +14 -0
- package/dist/providers/sheets/lib/constants.js +14 -0
- package/dist/providers/sheets/lib/hatch.js +163 -0
- package/dist/providers/sheets/lib/requests.js +105 -0
- package/dist/providers/sheets/lib/schemas.js +138 -0
- package/dist/providers/sheets/openapi.generated.js +30 -0
- package/dist/providers/sheets/safety.js +27 -0
- package/dist/providers/sheets/skill.generated.js +2 -7
- package/dist/providers/sheets/tools.js +734 -0
- package/dist/providers/slides/api.js +147 -0
- package/dist/providers/slides/index.d.ts +2 -10
- package/dist/providers/slides/index.js +48 -2555
- package/dist/providers/slides/lib/constants.d.ts +11 -0
- package/dist/providers/slides/lib/constants.js +11 -0
- package/dist/providers/slides/lib/requests.js +60 -0
- package/dist/providers/slides/lib/schemas.js +565 -0
- package/dist/providers/slides/openapi.generated.js +18 -0
- package/dist/providers/slides/skill.generated.js +2 -6
- package/dist/providers/slides/tools.js +1968 -0
- package/dist/providers/stripe/api.js +401 -0
- package/dist/providers/stripe/index.d.ts +7 -27
- package/dist/providers/stripe/index.js +75 -191
- package/dist/providers/stripe/lib/config.d.ts +4 -0
- package/dist/providers/stripe/lib/config.js +2 -0
- package/dist/providers/stripe/openapi.generated.js +4 -3
- package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
- package/dist/providers/stripe/skill.generated.js +2 -10
- package/dist/providers/stripe/tools.js +77 -0
- package/dist/providers/tithely/api.js +531 -0
- package/dist/providers/tithely/index.d.ts +19 -0
- package/dist/providers/tithely/index.js +86 -965
- package/dist/providers/tithely/skill.generated.js +2 -5
- package/dist/providers/tithely/tools.js +426 -0
- package/dist/providers/vercel/api.js +551 -0
- package/dist/providers/vercel/index.d.ts +11 -25
- package/dist/providers/vercel/index.js +67 -261
- package/dist/providers/vercel/lib/config.d.ts +2 -0
- package/dist/providers/vercel/lib/config.js +2 -0
- package/dist/providers/vercel/lib/index.js +117 -0
- package/dist/providers/vercel/lib/logs.js +100 -0
- package/dist/providers/vercel/lib/pin.js +270 -0
- package/dist/providers/vercel/openapi.generated.js +398 -376
- package/dist/providers/vercel/{value-safety.js → safety.js} +261 -42
- package/dist/providers/vercel/skill.generated.js +2 -7
- package/dist/providers/vercel/tools.js +642 -0
- package/dist/redaction-identity.js +47 -0
- package/dist/redaction-marker.js +125 -0
- package/dist/registry.d.ts +30 -28
- package/dist/registry.js +446 -413
- package/dist/routes/access-tokens.js +1 -1
- package/dist/routes/actor-labels.js +1 -1
- package/dist/routes/connect.js +1 -1
- package/dist/routes/dispatch.js +103 -0
- package/dist/routes/mcp.js +170 -79
- package/dist/routes/oauth.js +3 -1
- package/dist/routes/operator.js +2 -2
- package/dist/routes/shared.d.ts +5 -4
- package/dist/routes/shared.js +244 -177
- package/dist/routes/skills.js +10 -8
- package/dist/routes/ui-config.js +27 -3
- package/dist/routes/ui-data.js +29 -22
- package/dist/routes/ui.js +1 -1
- package/dist/runtime/call-admission.js +0 -26
- package/dist/runtime/call-entry.js +30 -0
- package/dist/runtime/call.js +556 -0
- package/dist/runtime/connector-scope.js +7 -0
- package/dist/runtime/credential-probe.js +34 -0
- package/dist/runtime/executor.js +40 -0
- package/dist/runtime/native-storage.js +63 -0
- package/dist/runtime/registry.js +27 -0
- package/dist/runtime/request-scope.js +72 -0
- package/dist/runtime/run.js +15 -7
- package/dist/runtime/shared-read.js +34 -5
- package/dist/runtime/storage.js +34 -21
- package/dist/sent-secrets.d.ts +194 -37
- package/dist/sent-secrets.js +1743 -340
- package/dist/server.js +5 -56
- package/dist/skills.js +330 -224
- package/dist/sqlite.js +15 -14
- package/dist/storage/d1-copy.js +255 -0
- package/dist/storage/d1-driver.js +15 -0
- package/dist/storage/keys.js +28 -0
- package/dist/storage/sql-driver.js +88 -0
- package/dist/storage/sql-native.js +544 -0
- package/dist/storage/sql-schema.d.ts +6 -0
- package/dist/storage/sql-schema.js +15 -0
- package/dist/storage/sql.d.ts +14 -20
- package/dist/storage/sql.js +29 -535
- package/dist/timeout.js +0 -26
- package/dist/tool-safety.js +20 -10
- package/dist/types.d.ts +11 -99
- package/dist/upward.js +3 -0
- package/dist/usage-guide.js +3 -1
- package/dist/validate.d.ts +4 -2
- package/dist/validate.js +86 -9
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +312 -292
- package/dist/workspace-subject.js +5 -1
- package/dist/write-outcome.js +454 -34
- package/documentation/README.md +27 -7
- package/documentation/architecture.md +2550 -186
- package/documentation/auth.md +821 -98
- package/documentation/code-mode.md +56 -21
- package/documentation/deploying.md +164 -20
- package/documentation/hosted-mcp-migration.md +460 -0
- package/documentation/integrating.md +144 -105
- package/documentation/meta-tools.md +411 -34
- package/documentation/operating.md +28 -4
- package/documentation/operator-tests.md +11 -2
- package/documentation/operator-ui.md +103 -33
- package/documentation/provider-auth-migration.md +38 -63
- package/documentation/provider-migration-0.29.md +10 -5
- package/documentation/provider-migration-0.35.md +975 -0
- package/documentation/providers.md +965 -0
- package/examples/worker/AGENTS.md +23 -9
- package/examples/worker/README.md +254 -135
- package/examples/worker/alchemy.run.ts +116 -0
- package/examples/worker/gitignore +10 -0
- package/examples/worker/src/connecta.config.ts +23 -21
- package/examples/worker/wrangler.jsonc +14 -5
- package/guides/connecting-agents.md +157 -0
- package/guides/how-it-works.md +156 -0
- package/guides/safety-and-trust.md +153 -0
- package/package.json +27 -42
- package/templates/node/.env.example +3 -0
- package/templates/node/README.md +22 -2
- package/templates/node/docker-compose.yml +1 -0
- package/templates/node/package.json +3 -2
- package/templates/node/src/connecta.config.ts +8 -0
- package/dist/catalog-drift.d.ts +0 -134
- package/dist/catalog-drift.js +0 -493
- package/dist/providers/_shared/request-token.js +0 -83
- package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
- package/dist/providers/_shared/rest/tools.d.ts +0 -141
- package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
- package/dist/providers/basecamp/index.d.ts +0 -43
- package/dist/providers/basecamp/index.js +0 -997
- package/dist/providers/basecamp/skill.generated.js +0 -9
- package/dist/providers/cloudflare/rest.d.ts +0 -37
- package/dist/providers/cloudflare/rest.js +0 -1078
- package/dist/providers/github/hosted.js +0 -218
- package/dist/providers/github/rest.js +0 -321
- package/dist/providers/infisical/index.d.ts +0 -7
- package/dist/providers/infisical/index.js +0 -713
- package/dist/providers/infisical/skill.generated.js +0 -8
- package/dist/providers/linear/index.d.ts +0 -55
- package/dist/providers/linear/index.js +0 -352
- package/dist/providers/linear/skill.generated.js +0 -8
- package/dist/providers/mixpanel/index.d.ts +0 -57
- package/dist/providers/mixpanel/index.js +0 -468
- package/dist/providers/mixpanel/operations.js +0 -20
- package/dist/providers/mixpanel/rest.js +0 -603
- package/dist/providers/mixpanel/skill.generated.js +0 -10
- package/dist/providers/mixpanel/value-safety.js +0 -16
- package/dist/providers/notion/index.d.ts +0 -65
- package/dist/providers/notion/index.js +0 -368
- package/dist/providers/notion/openapi.generated.js +0 -75
- package/dist/providers/notion/rest.d.ts +0 -24
- package/dist/providers/notion/rest.js +0 -1226
- package/dist/providers/notion/skill.generated.js +0 -11
- package/dist/providers/notion/value-safety.js +0 -56
- package/dist/providers/revenuecat/index.d.ts +0 -49
- package/dist/providers/revenuecat/index.js +0 -557
- package/dist/providers/revenuecat/skill.generated.js +0 -11
- package/dist/providers/stripe/rest.d.ts +0 -14
- package/dist/providers/stripe/rest.js +0 -402
- package/dist/providers/vercel/rest.d.ts +0 -20
- package/dist/providers/vercel/rest.js +0 -1020
- package/dist/providers/wispr-flow/index.d.ts +0 -14
- package/dist/providers/wispr-flow/index.js +0 -117
- package/dist/providers/wispr-flow/skill.generated.js +0 -8
- /package/dist/providers/github/{key.js → lib/key.js} +0 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { runEdge } from "../runtime/run.js";
|
|
2
|
+
import { authCall, authCatch, authFetch, authFlow, authInvoke, authMethod, authProgram, registerAuth, } from "./native.js";
|
|
3
|
+
import { AGENT_NAMESPACE, agentSubjectId, trustAgentProvider } from "../agent-identity.js";
|
|
4
|
+
import { authenticationBusyResponse, withAuthenticationDeadline } from "../auth-admission.js";
|
|
5
|
+
import { authorizationCredential } from "../inbound-credential.js";
|
|
6
|
+
const ISSUER = "https://oidc.vercel.com";
|
|
7
|
+
const JWKS = `${ISSUER}/.well-known/jwks`;
|
|
8
|
+
const CACHE_MS = 300_000;
|
|
9
|
+
const REFRESH_MS = 30_000;
|
|
10
|
+
const SKEW_SECONDS = 30;
|
|
11
|
+
const KEY_FETCH_BUSY = Symbol("key fetch capacity reached");
|
|
12
|
+
const encoder = new TextEncoder();
|
|
13
|
+
const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false });
|
|
14
|
+
function bytes(value) {
|
|
15
|
+
if (!/^[A-Za-z0-9_-]+(?![\s\S])/.test(value))
|
|
16
|
+
throw new Error("Invalid JWT encoding");
|
|
17
|
+
return Uint8Array.from(atob(value.replace(/-/g, "+").replace(/_/g, "/")), (char) => char.charCodeAt(0));
|
|
18
|
+
}
|
|
19
|
+
function record(value) {
|
|
20
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
21
|
+
}
|
|
22
|
+
function part(value) {
|
|
23
|
+
const decoded = JSON.parse(decoder.decode(bytes(value)));
|
|
24
|
+
if (!record(decoded))
|
|
25
|
+
throw new Error("Invalid JWT object");
|
|
26
|
+
return decoded;
|
|
27
|
+
}
|
|
28
|
+
function parsed(request) {
|
|
29
|
+
const credential = authorizationCredential(request);
|
|
30
|
+
if (credential.kind !== "bearer" || credential.token.length > 16_384)
|
|
31
|
+
return undefined;
|
|
32
|
+
const pieces = credential.token.split(".");
|
|
33
|
+
if (pieces.length !== 3)
|
|
34
|
+
return undefined;
|
|
35
|
+
try {
|
|
36
|
+
return { header: part(pieces[0]), claims: part(pieces[1]), pieces };
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Recognition reserves Vercel's issuer namespace; verification still checks exact values. */
|
|
43
|
+
export function isVercelAgentCredential(request) {
|
|
44
|
+
const iss = parsed(request)?.claims.iss;
|
|
45
|
+
return typeof iss === "string" && (iss === ISSUER || iss.startsWith(`${ISSUER}/`));
|
|
46
|
+
}
|
|
47
|
+
function denied() {
|
|
48
|
+
return {
|
|
49
|
+
ok: false,
|
|
50
|
+
response: Response.json({ error: "unauthorized" }, {
|
|
51
|
+
status: 401,
|
|
52
|
+
headers: { "WWW-Authenticate": "Bearer", "Cache-Control": "no-store" },
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function readKeys(signal) {
|
|
57
|
+
return runEdge(authProgram(readKeysNative, [signal]));
|
|
58
|
+
}
|
|
59
|
+
const readKeysNative = /* @__PURE__ */ registerAuth(readKeys, function (signal) {
|
|
60
|
+
return authFlow(function* () {
|
|
61
|
+
let response;
|
|
62
|
+
let reader;
|
|
63
|
+
const cancel = () => {
|
|
64
|
+
void reader?.cancel().catch(() => { });
|
|
65
|
+
};
|
|
66
|
+
try {
|
|
67
|
+
signal.throwIfAborted();
|
|
68
|
+
response = (yield authFetch(JWKS, {
|
|
69
|
+
signal,
|
|
70
|
+
redirect: "manual",
|
|
71
|
+
headers: { Accept: "application/json" },
|
|
72
|
+
}));
|
|
73
|
+
signal.throwIfAborted();
|
|
74
|
+
if (!response.ok || !response.body)
|
|
75
|
+
throw new Error("JWKS unavailable");
|
|
76
|
+
reader = response.body.getReader();
|
|
77
|
+
signal.addEventListener("abort", cancel, { once: true });
|
|
78
|
+
const chunks = [];
|
|
79
|
+
let size = 0;
|
|
80
|
+
for (;;) {
|
|
81
|
+
signal.throwIfAborted();
|
|
82
|
+
const { done, value } = (yield authInvoke(reader, "read", []));
|
|
83
|
+
signal.throwIfAborted();
|
|
84
|
+
if (done)
|
|
85
|
+
break;
|
|
86
|
+
size += value.byteLength;
|
|
87
|
+
if (size > 65536)
|
|
88
|
+
throw new Error("JWKS too large");
|
|
89
|
+
chunks.push(value);
|
|
90
|
+
}
|
|
91
|
+
const body = new Uint8Array(size);
|
|
92
|
+
let offset = 0;
|
|
93
|
+
for (const chunk of chunks) {
|
|
94
|
+
body.set(chunk, offset);
|
|
95
|
+
offset += chunk.length;
|
|
96
|
+
}
|
|
97
|
+
const jwks = JSON.parse(decoder.decode(body));
|
|
98
|
+
if (!record(jwks) || !Array.isArray(jwks.keys) || jwks.keys.length > 16)
|
|
99
|
+
throw new Error("Invalid JWKS");
|
|
100
|
+
const keys = new Map();
|
|
101
|
+
for (const key of jwks.keys) {
|
|
102
|
+
if (!record(key) ||
|
|
103
|
+
key.kty !== "RSA" ||
|
|
104
|
+
(key.alg !== undefined && key.alg !== "RS256") ||
|
|
105
|
+
(key.use !== undefined && key.use !== "sig") ||
|
|
106
|
+
(key.key_ops !== undefined && (!Array.isArray(key.key_ops) || !key.key_ops.includes("verify"))) ||
|
|
107
|
+
typeof key.kid !== "string" ||
|
|
108
|
+
key.kid.length === 0 ||
|
|
109
|
+
key.kid.length > 256 ||
|
|
110
|
+
typeof key.n !== "string" ||
|
|
111
|
+
typeof key.e !== "string" ||
|
|
112
|
+
"d" in key)
|
|
113
|
+
continue;
|
|
114
|
+
const modulus = bytes(key.n);
|
|
115
|
+
if (modulus.length < 256 || modulus.length > 512 || bytes(key.e).length > 8 || keys.has(key.kid)) {
|
|
116
|
+
throw new Error("Invalid RSA key");
|
|
117
|
+
}
|
|
118
|
+
keys.set(key.kid, { kty: "RSA", n: key.n, e: key.e, alg: "RS256", ext: true, key_ops: ["verify"] });
|
|
119
|
+
}
|
|
120
|
+
if (keys.size === 0)
|
|
121
|
+
throw new Error("No signing keys");
|
|
122
|
+
return keys;
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
signal.removeEventListener("abort", cancel);
|
|
126
|
+
(yield authCatch(authInvoke(reader, "cancel", []), () => { }));
|
|
127
|
+
reader?.releaseLock();
|
|
128
|
+
(yield authCatch(authInvoke(response?.body, "cancel", []), () => { }));
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
/** One bounded public-key cache per deployment; never retains request I/O or promises. */
|
|
133
|
+
export function vercelAgentAuth(agents, logger = console) {
|
|
134
|
+
const bindings = agents.filter((agent) => agent.vercel);
|
|
135
|
+
if (bindings.length === 0)
|
|
136
|
+
return undefined;
|
|
137
|
+
let cache;
|
|
138
|
+
let refreshAfter = 0;
|
|
139
|
+
let retryAfter = 0;
|
|
140
|
+
let fetching = 0;
|
|
141
|
+
function load(signal) {
|
|
142
|
+
return runEdge(authProgram(loadNative, [signal]));
|
|
143
|
+
}
|
|
144
|
+
const loadNative = /* @__PURE__ */ registerAuth(load, function (signal) {
|
|
145
|
+
return authFlow(function* () {
|
|
146
|
+
if (fetching >= 2)
|
|
147
|
+
throw KEY_FETCH_BUSY;
|
|
148
|
+
if (Date.now() < retryAfter)
|
|
149
|
+
throw new Error("JWKS unavailable");
|
|
150
|
+
fetching++;
|
|
151
|
+
try {
|
|
152
|
+
const keys = (yield authCall(readKeys, [signal]));
|
|
153
|
+
signal.throwIfAborted();
|
|
154
|
+
cache = { expires: Date.now() + CACHE_MS, keys };
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
if (!signal.aborted)
|
|
158
|
+
retryAfter = Date.now() + REFRESH_MS;
|
|
159
|
+
throw new Error("JWKS unavailable");
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
fetching--;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
return trustAgentProvider({
|
|
167
|
+
kind: "vercel_oidc",
|
|
168
|
+
activityActorNamespace: AGENT_NAMESPACE,
|
|
169
|
+
recognizesCredential: isVercelAgentCredential,
|
|
170
|
+
authorize: authMethod(function (request) {
|
|
171
|
+
return authFlow(function* () {
|
|
172
|
+
try {
|
|
173
|
+
return (yield authCall(withAuthenticationDeadline, [
|
|
174
|
+
request,
|
|
175
|
+
authMethod((signal) => {
|
|
176
|
+
return authFlow(function* () {
|
|
177
|
+
const jwt = parsed(request);
|
|
178
|
+
if (!jwt ||
|
|
179
|
+
jwt.header.alg !== "RS256" ||
|
|
180
|
+
typeof jwt.header.kid !== "string" ||
|
|
181
|
+
jwt.header.kid.length === 0 ||
|
|
182
|
+
jwt.header.kid.length > 256 ||
|
|
183
|
+
jwt.header.crit !== undefined ||
|
|
184
|
+
jwt.header.b64 !== undefined)
|
|
185
|
+
return denied();
|
|
186
|
+
const c = jwt.claims;
|
|
187
|
+
const segment = (value, project = false) => typeof value === "string" &&
|
|
188
|
+
(project ? /^[A-Za-z0-9._-]{1,128}(?![\s\S])/ : /^[A-Za-z0-9_-]{1,128}(?![\s\S])/).test(value);
|
|
189
|
+
if (!segment(c.owner) ||
|
|
190
|
+
!segment(c.project, true) ||
|
|
191
|
+
!segment(c.owner_id) ||
|
|
192
|
+
!segment(c.project_id) ||
|
|
193
|
+
!segment(c.environment) ||
|
|
194
|
+
c.external_sub !== undefined ||
|
|
195
|
+
(c.iss !== ISSUER && c.iss !== `${ISSUER}/${c.owner}`) ||
|
|
196
|
+
c.aud !== `https://vercel.com/${c.owner}` ||
|
|
197
|
+
c.sub !== `owner:${c.owner}:project:${c.project}:environment:${c.environment}`)
|
|
198
|
+
return denied();
|
|
199
|
+
const agent = bindings.find(({ vercel: v }) => v &&
|
|
200
|
+
v.team === c.owner_id &&
|
|
201
|
+
v.project === (v.project.startsWith("prj_") ? c.project_id : c.project) &&
|
|
202
|
+
v.environments.some((environment) => environment === c.environment));
|
|
203
|
+
if (!agent)
|
|
204
|
+
return denied();
|
|
205
|
+
const validTime = () => {
|
|
206
|
+
const now = Date.now() / 1000;
|
|
207
|
+
return (typeof c.exp === "number" &&
|
|
208
|
+
Number.isFinite(c.exp) &&
|
|
209
|
+
typeof c.iat === "number" &&
|
|
210
|
+
Number.isFinite(c.iat) &&
|
|
211
|
+
c.exp > c.iat &&
|
|
212
|
+
c.exp > now - SKEW_SECONDS &&
|
|
213
|
+
c.iat <= now + SKEW_SECONDS &&
|
|
214
|
+
(c.nbf === undefined ||
|
|
215
|
+
(typeof c.nbf === "number" &&
|
|
216
|
+
Number.isFinite(c.nbf) &&
|
|
217
|
+
c.nbf <= now + SKEW_SECONDS &&
|
|
218
|
+
c.nbf < c.exp)));
|
|
219
|
+
};
|
|
220
|
+
if (!validTime())
|
|
221
|
+
return denied();
|
|
222
|
+
const fresh = cache && cache.expires > Date.now();
|
|
223
|
+
if (!fresh)
|
|
224
|
+
(yield authCall(load, [signal]));
|
|
225
|
+
let key = cache?.keys.get(jwt.header.kid);
|
|
226
|
+
if (!key && fresh && Date.now() >= refreshAfter) {
|
|
227
|
+
refreshAfter = Date.now() + REFRESH_MS;
|
|
228
|
+
(yield authCall(load, [signal]));
|
|
229
|
+
key = cache?.keys.get(jwt.header.kid);
|
|
230
|
+
}
|
|
231
|
+
if (!key)
|
|
232
|
+
return denied();
|
|
233
|
+
const imported = (yield authInvoke(crypto.subtle, "importKey", [
|
|
234
|
+
"jwk",
|
|
235
|
+
key,
|
|
236
|
+
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
|
|
237
|
+
false,
|
|
238
|
+
["verify"],
|
|
239
|
+
]));
|
|
240
|
+
signal.throwIfAborted();
|
|
241
|
+
const valid = (yield authInvoke(crypto.subtle, "verify", [
|
|
242
|
+
"RSASSA-PKCS1-v1_5",
|
|
243
|
+
imported,
|
|
244
|
+
bytes(jwt.pieces[2]),
|
|
245
|
+
encoder.encode(`${jwt.pieces[0]}.${jwt.pieces[1]}`),
|
|
246
|
+
]));
|
|
247
|
+
signal.throwIfAborted();
|
|
248
|
+
if (!valid || !validTime())
|
|
249
|
+
return denied();
|
|
250
|
+
return { ok: true, agent: agent.name, subjectId: agentSubjectId(agent.name) };
|
|
251
|
+
});
|
|
252
|
+
}),
|
|
253
|
+
]));
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
if (error === KEY_FETCH_BUSY)
|
|
257
|
+
return {
|
|
258
|
+
ok: false,
|
|
259
|
+
response: authenticationBusyResponse(logger),
|
|
260
|
+
};
|
|
261
|
+
return denied();
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { authCall, AuthenticationWork, authProgram, authValue, hasAuthProgram, registerAuth } from "./auth/native.js";
|
|
3
|
+
import { failureRecord, logFailure } from "./operator-record.js";
|
|
4
|
+
import { runEdge, withDeadlineEffect } from "./runtime/run.js";
|
|
5
|
+
// Shared by human and agent routes, even across deployments constructed in the
|
|
6
|
+
// same loaded core. Isolate-wide state is counters and plain charge records:
|
|
7
|
+
// no queue, requests, promises, signals, or I/O.
|
|
8
|
+
const AUTHENTICATION_CONCURRENCY = 16;
|
|
9
|
+
// Work still unsettled after its hard deadline plus grace leaves the active
|
|
10
|
+
// pool for this allowance. Equal to the active limit: a partial wave of stalls
|
|
11
|
+
// recovers while normal authentication continues, yet total outstanding work
|
|
12
|
+
// never exceeds twice the limit, and a full wave, or a binding that never
|
|
13
|
+
// settles, fills it and fails closed instead of accumulating I/O.
|
|
14
|
+
const ABANDONED_ALLOWANCE = 16;
|
|
15
|
+
const SETTLEMENT_GRACE_MS = 1_000;
|
|
16
|
+
// Clerk owns a ten-second deadline; custom adapters share that threshold.
|
|
17
|
+
const DEFAULT_DEADLINE_MS = 10_000;
|
|
18
|
+
// Native Access lookups and agent-token storage reads.
|
|
19
|
+
const NATIVE_DEADLINE_MS = 5_000;
|
|
20
|
+
let active = 0;
|
|
21
|
+
let abandoned = 0;
|
|
22
|
+
// Charges released by the collector and not yet logged: a plain number.
|
|
23
|
+
let unreachable = 0;
|
|
24
|
+
// Overdue active charges waiting for abandoned room; at most the active limit.
|
|
25
|
+
const overdue = new Set();
|
|
26
|
+
// The one release that is not a settlement. Each charge's token is reachable
|
|
27
|
+
// only through the continuations attached to its adapter promise and native
|
|
28
|
+
// operations, plus its deadline timer and its request's lifetime entry. While
|
|
29
|
+
// any of that work can still settle, whatever would settle it reaches the
|
|
30
|
+
// token. Once the collector frees the token, nothing can ever settle the
|
|
31
|
+
// charge, so it is released. Collection is eventual, never timed. A runtime
|
|
32
|
+
// without FinalizationRegistry (workerd without `enable_weak_ref` before the
|
|
33
|
+
// 2025-05-05 compatibility date) releases on settlement alone.
|
|
34
|
+
const collector = typeof FinalizationRegistry === "function"
|
|
35
|
+
? new FinalizationRegistry((charge) => {
|
|
36
|
+
if (release(charge))
|
|
37
|
+
unreachable++;
|
|
38
|
+
})
|
|
39
|
+
: undefined;
|
|
40
|
+
/** Health counters: plain numbers and one runtime fact, no request facts. */
|
|
41
|
+
export function authenticationSnapshot() {
|
|
42
|
+
return {
|
|
43
|
+
active,
|
|
44
|
+
limit: AUTHENTICATION_CONCURRENCY,
|
|
45
|
+
abandoned,
|
|
46
|
+
abandonedLimit: ABANDONED_ALLOWANCE,
|
|
47
|
+
abandonedSaturated: abandoned >= ABANDONED_ALLOWANCE,
|
|
48
|
+
unreachableRelease: collector !== undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function abandon(charge) {
|
|
52
|
+
if (charge.state !== "active")
|
|
53
|
+
return;
|
|
54
|
+
// A full allowance cannot overflow: the charge keeps its active slot.
|
|
55
|
+
if (abandoned >= ABANDONED_ALLOWANCE) {
|
|
56
|
+
overdue.add(charge);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
charge.state = "abandoned";
|
|
60
|
+
active--;
|
|
61
|
+
abandoned++;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Exactly once per charge: when its underlying work has settled, or when the
|
|
65
|
+
* collector proves it never can. The flag lives in the record, so a second
|
|
66
|
+
* attempt from either path changes nothing.
|
|
67
|
+
*/
|
|
68
|
+
function release(charge) {
|
|
69
|
+
if (charge.state === "released")
|
|
70
|
+
return false;
|
|
71
|
+
overdue.delete(charge);
|
|
72
|
+
if (charge.state === "active")
|
|
73
|
+
active--;
|
|
74
|
+
else
|
|
75
|
+
abandoned--;
|
|
76
|
+
charge.state = "released";
|
|
77
|
+
for (const waiting of overdue) {
|
|
78
|
+
if (abandoned >= ABANDONED_ALLOWANCE)
|
|
79
|
+
break;
|
|
80
|
+
overdue.delete(waiting);
|
|
81
|
+
abandon(waiting);
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
// Lets built-in adapters register work that has no cancellation API with
|
|
86
|
+
// their own invocation, without changing the published adapter contract.
|
|
87
|
+
// Keyed by the exact request `authenticate` hands its adapter; no request
|
|
88
|
+
// ever reads another request's work or settlement. The entry refers to its
|
|
89
|
+
// lifetime weakly: workerd keeps a Request, and whatever a WeakMap keys on it,
|
|
90
|
+
// alive after its invocation ends, so a strong entry would pin every token.
|
|
91
|
+
// While the adapter can still reach withAuthenticationDeadline, its pending
|
|
92
|
+
// promise holds the lifetime through `authenticate`.
|
|
93
|
+
const lifetimes = new WeakMap();
|
|
94
|
+
const weakly = (lifetime) => typeof WeakRef === "function" ? new WeakRef(lifetime) : { deref: () => lifetime };
|
|
95
|
+
/**
|
|
96
|
+
* Race the caller and a five-second deadline for a native operation that
|
|
97
|
+
* cannot be cancelled. The caller gets a prompt answer; the operation itself
|
|
98
|
+
* stays charged to this invocation until it actually settles.
|
|
99
|
+
*/
|
|
100
|
+
export function withAuthenticationDeadline(request, operation) {
|
|
101
|
+
return runEdge(authProgram(withAuthenticationDeadlineNative, [request, operation]));
|
|
102
|
+
}
|
|
103
|
+
const withAuthenticationDeadlineNative = /* @__PURE__ */ registerAuth(withAuthenticationDeadline, function (request, operation) {
|
|
104
|
+
return Effect.suspend(() => {
|
|
105
|
+
const lifetime = lifetimes.get(request)?.deref();
|
|
106
|
+
lifetime?.deadline(NATIVE_DEADLINE_MS);
|
|
107
|
+
const bounded = withDeadlineEffect((signal) => {
|
|
108
|
+
signal.throwIfAborted();
|
|
109
|
+
return authCall(operation, [signal]);
|
|
110
|
+
}, { timeoutMs: NATIVE_DEADLINE_MS, signal: request.signal, timeoutError: new Error("Authentication timed out") });
|
|
111
|
+
// A custom decorator may call the saved Promise shell. Reuse this exact
|
|
112
|
+
// request's charge even when native consumer dispatch cannot select it.
|
|
113
|
+
return lifetime ? bounded.pipe(Effect.provideService(AuthenticationWork, lifetime)) : bounded;
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
/** Shared capacity refusal: one checked log and retryable HTTP contract. */
|
|
117
|
+
export function authenticationBusyResponse(logger, reason = "authentication_busy") {
|
|
118
|
+
logFailure(logger, "Authentication admission refused", failureRecord({ reason }));
|
|
119
|
+
return Response.json({ error: { code: "authentication_busy", message: "Authentication capacity reached.", retryable: true } }, { status: 503, headers: { "Cache-Control": "no-store", "Retry-After": "1" } });
|
|
120
|
+
}
|
|
121
|
+
/** The sole route boundary for invoking an inbound adapter, including its SDK. */
|
|
122
|
+
export function authenticate(provider, request, baseUrl, runtimeContext, logger, settle) {
|
|
123
|
+
return runEdge(authProgram(authenticateNative, [provider, request, baseUrl, runtimeContext, logger, settle]));
|
|
124
|
+
}
|
|
125
|
+
const authenticateNative = /* @__PURE__ */ registerAuth(authenticate, function (provider, request, baseUrl, runtimeContext, logger, settle) {
|
|
126
|
+
return Effect.suspend(() => {
|
|
127
|
+
request.signal.throwIfAborted();
|
|
128
|
+
if (unreachable > 0) {
|
|
129
|
+
unreachable = 0;
|
|
130
|
+
logFailure(logger, "Authentication released after its pending work became unreachable", failureRecord({ reason: "authentication_unreachable" }));
|
|
131
|
+
}
|
|
132
|
+
if (active >= AUTHENTICATION_CONCURRENCY || abandoned >= ABANDONED_ALLOWANCE) {
|
|
133
|
+
// Callers see one retryable contract; the log and health tell an
|
|
134
|
+
// operator whether stalled bindings, not load, are refusing.
|
|
135
|
+
const reason = abandoned >= ABANDONED_ALLOWANCE ? "authentication_abandoned_saturated" : "authentication_busy";
|
|
136
|
+
return Effect.succeed({
|
|
137
|
+
admitted: false,
|
|
138
|
+
response: authenticationBusyResponse(logger, reason),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
active++;
|
|
142
|
+
const charge = { state: "active" };
|
|
143
|
+
// Held only by this scope's closures (see `collector`); the registry keeps
|
|
144
|
+
// the record, and the token, as its own unregister key, only weakly.
|
|
145
|
+
const token = {};
|
|
146
|
+
collector?.register(token, charge, token);
|
|
147
|
+
// The adapter's own program or Promise plus each tracked native operation.
|
|
148
|
+
let remaining = 1;
|
|
149
|
+
let timer;
|
|
150
|
+
let complete;
|
|
151
|
+
const done = new Promise((resolve) => {
|
|
152
|
+
complete = resolve;
|
|
153
|
+
});
|
|
154
|
+
const finish = () => {
|
|
155
|
+
if (--remaining !== 0)
|
|
156
|
+
return;
|
|
157
|
+
clearTimeout(timer);
|
|
158
|
+
collector?.unregister(token);
|
|
159
|
+
if (lifetimes.get(request)?.deref() === lifetime)
|
|
160
|
+
lifetimes.delete(request);
|
|
161
|
+
release(charge);
|
|
162
|
+
complete();
|
|
163
|
+
};
|
|
164
|
+
const lifetime = {
|
|
165
|
+
deadline(ms) {
|
|
166
|
+
clearTimeout(timer);
|
|
167
|
+
timer = setTimeout(() => abandon(charge), ms + SETTLEMENT_GRACE_MS);
|
|
168
|
+
},
|
|
169
|
+
track(pending) {
|
|
170
|
+
remaining++;
|
|
171
|
+
void pending.then(finish, finish);
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
lifetime.deadline(DEFAULT_DEADLINE_MS);
|
|
175
|
+
lifetimes.set(request, weakly(lifetime));
|
|
176
|
+
// Native verification composes in this request and accounts for the
|
|
177
|
+
// real settlement of every uncancellable SDK/platform leaf.
|
|
178
|
+
if (hasAuthProgram(provider.authorize)) {
|
|
179
|
+
settle?.(done);
|
|
180
|
+
return authCall(provider.authorize, [request, baseUrl, runtimeContext], provider).pipe(Effect.provideService(AuthenticationWork, lifetime), Effect.ensuring(Effect.sync(finish)), Effect.map((result) => ({ admitted: true, result })));
|
|
181
|
+
}
|
|
182
|
+
let asynchronous = false;
|
|
183
|
+
try {
|
|
184
|
+
const result = provider.authorize(request, baseUrl, runtimeContext);
|
|
185
|
+
// A synchronous local/cached verdict owns no pending authentication.
|
|
186
|
+
// Release before yielding, rather than holding a slot for a microtask.
|
|
187
|
+
if ("ok" in result)
|
|
188
|
+
return Effect.succeed({ admitted: true, result });
|
|
189
|
+
asynchronous = true;
|
|
190
|
+
const pending = result.finally(finish);
|
|
191
|
+
// Keep just this request's settlement continuation runnable when workerd
|
|
192
|
+
// drops the disconnected caller, including native work that outlived the
|
|
193
|
+
// caller-facing deadline. It neither detaches nor starts work.
|
|
194
|
+
settle?.(done);
|
|
195
|
+
return authValue(() => pending).pipe(Effect.map((result) => ({ admitted: true, result })));
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
// A caller leaving is not proof that its adapter or SDK has stopped.
|
|
199
|
+
// Keep capacity charged until the underlying authorization settles.
|
|
200
|
+
if (!asynchronous)
|
|
201
|
+
finish();
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|