@apifuse/provider-sdk 2.2.0-beta.3 → 2.2.0-beta.30
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/AUTHORING.md +493 -5
- package/CHANGELOG.md +127 -1
- package/README.md +52 -6
- package/SUBMISSION.md +1 -1
- package/bin/apifuse-check.ts +106 -62
- package/bin/apifuse-create.ts +1 -1
- package/bin/apifuse-dev.ts +63 -55
- package/bin/apifuse-pack-check.ts +22 -2
- package/bin/apifuse-pack-smoke.ts +78 -82
- package/bin/apifuse-pack-types.ts +583 -0
- package/bin/apifuse-perf.ts +59 -140
- package/bin/apifuse-record.ts +698 -113
- package/bin/apifuse-submit-check.ts +517 -44
- package/bin/apifuse-sync-assets.ts +117 -0
- package/bin/apifuse.ts +1 -1
- package/bin/submit-check-delimited-text.ts +50 -0
- package/bin/submit-check-xml.ts +1 -1
- package/dist/auth-turn/index.d.ts +4 -4
- package/dist/auth-turn/index.js +1 -1
- package/dist/auth.d.ts +16 -2
- package/dist/auth.js +76 -18
- package/dist/ceremonies/index.d.ts +9 -1
- package/dist/ceremonies/index.js +117 -29
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +8 -0
- package/dist/cli/create.d.ts +3 -0
- package/dist/cli/create.js +34 -35
- package/dist/cli/prompt-assets.d.ts +80 -0
- package/dist/cli/prompt-assets.js +743 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/dist/cli/templates/provider/README.md.tpl +4 -4
- package/dist/config/loader.d.ts +176 -17
- package/dist/config/loader.js +434 -161
- package/dist/contract-serialization.d.ts +2 -2
- package/dist/contract-serialization.js +7 -14
- package/dist/contract-types.d.ts +3 -2
- package/dist/contract.d.ts +3 -3
- package/dist/contract.js +6 -6
- package/dist/declaration-validation.d.ts +23 -0
- package/dist/declaration-validation.js +159 -0
- package/dist/define.d.ts +13 -1
- package/dist/define.js +391 -122
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/error-resolution.d.ts +4 -0
- package/dist/error-resolution.js +122 -0
- package/dist/errors.d.ts +19 -1
- package/dist/errors.js +41 -3
- package/dist/fixture-sanitization.d.ts +26 -0
- package/dist/fixture-sanitization.js +216 -0
- package/dist/i18n/catalog.d.ts +2 -2
- package/dist/i18n/catalog.js +4 -10
- package/dist/i18n/index.d.ts +2 -2
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/keys.d.ts +2 -2
- package/dist/index.d.ts +50 -42
- package/dist/index.js +41 -37
- package/dist/lint.d.ts +6 -1
- package/dist/lint.js +370 -18
- package/dist/native-address.d.ts +43 -0
- package/dist/native-address.js +281 -0
- package/dist/native-egress-policy.d.ts +31 -0
- package/dist/native-egress-policy.js +288 -0
- package/dist/observability.d.ts +5 -2
- package/dist/observability.js +48 -1
- package/dist/provider.d.ts +13 -11
- package/dist/provider.js +10 -9
- package/dist/public-schema-field-lint.d.ts +1 -1
- package/dist/recipes/gov-api.js +1 -1
- package/dist/runtime/auth-flow.d.ts +3 -1
- package/dist/runtime/auth-flow.js +8 -3
- package/dist/runtime/browser.d.ts +1 -1
- package/dist/runtime/browser.js +138 -40
- package/dist/runtime/cache.d.ts +2 -1
- package/dist/runtime/cache.js +173 -23
- package/dist/runtime/choice-wordlist.d.ts +9 -0
- package/dist/runtime/choice-wordlist.js +138 -0
- package/dist/runtime/choice.d.ts +13 -1
- package/dist/runtime/choice.js +514 -124
- package/dist/runtime/credential.d.ts +1 -1
- package/dist/runtime/credential.js +1 -1
- package/dist/runtime/env.d.ts +1 -1
- package/dist/runtime/executor.d.ts +1 -1
- package/dist/runtime/executor.js +25 -3
- package/dist/runtime/http.d.ts +3 -2
- package/dist/runtime/http.js +517 -55
- package/dist/runtime/insights.d.ts +1 -1
- package/dist/runtime/insights.js +6 -13
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +371 -23
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- package/dist/runtime/native-network-errors.d.ts +33 -0
- package/dist/runtime/native-network-errors.js +69 -0
- package/dist/runtime/native-network.d.ts +96 -0
- package/dist/runtime/native-network.js +1232 -0
- package/dist/runtime/ocr.d.ts +29 -0
- package/dist/runtime/ocr.js +440 -0
- package/dist/runtime/otlp.d.ts +1 -1
- package/dist/runtime/perf.d.ts +1 -1
- package/dist/runtime/provider.d.ts +1 -1
- package/dist/runtime/provider.js +1 -2
- package/dist/runtime/proxy-errors.d.ts +1 -1
- package/dist/runtime/proxy-errors.js +9 -7
- package/dist/runtime/proxy-nodemaven.d.ts +56 -0
- package/dist/runtime/proxy-nodemaven.js +146 -0
- package/dist/runtime/proxy-retry-policy.d.ts +2 -2
- package/dist/runtime/proxy-retry-policy.js +2 -2
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +58 -52
- package/dist/runtime/redirects.d.ts +29 -0
- package/dist/runtime/redirects.js +36 -0
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +5 -5
- package/dist/runtime/request-options.d.ts +68 -1
- package/dist/runtime/request-options.js +548 -0
- package/dist/runtime/resolver-config.d.ts +6 -0
- package/dist/runtime/resolver-config.js +6 -0
- package/dist/runtime/resolver-public.d.ts +1 -0
- package/dist/runtime/resolver-public.js +1 -0
- package/dist/runtime/resolver-shared.d.ts +3 -0
- package/dist/runtime/resolver-shared.js +12 -0
- package/dist/runtime/resolver-vendors/bindings.d.ts +48 -0
- package/dist/runtime/resolver-vendors/bindings.js +40 -0
- package/dist/runtime/resolver-vendors/browser.d.ts +20 -0
- package/dist/runtime/resolver-vendors/browser.js +282 -0
- package/dist/runtime/resolver-vendors/hosts.d.ts +2 -0
- package/dist/runtime/resolver-vendors/hosts.js +33 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +24 -0
- package/dist/runtime/resolver-vendors/twocaptcha.js +368 -0
- package/dist/runtime/resolver-vendors/types.d.ts +83 -0
- package/dist/runtime/resolver-vendors/types.js +69 -0
- package/dist/runtime/resolver.d.ts +59 -0
- package/dist/runtime/resolver.js +705 -0
- package/dist/runtime/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +5 -2
- package/dist/runtime/state.js +280 -74
- package/dist/runtime/stealth-cookies.d.ts +20 -0
- package/dist/runtime/stealth-cookies.js +111 -0
- package/dist/runtime/stealth.d.ts +30 -5
- package/dist/runtime/stealth.js +523 -259
- package/dist/runtime/stt.d.ts +1 -1
- package/dist/runtime/stt.js +12 -27
- package/dist/runtime/timeout.d.ts +5 -0
- package/dist/runtime/timeout.js +12 -0
- package/dist/runtime/trace.d.ts +2 -2
- package/dist/runtime/trace.js +2 -4
- package/dist/runtime/waterfall.d.ts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +7 -15
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/index.d.ts +7 -7
- package/dist/server/index.js +6 -6
- package/dist/server/self-test-input-tokens.d.ts +2 -1
- package/dist/server/self-test-input-tokens.js +18 -14
- package/dist/server/self-test-redaction.d.ts +1 -1
- package/dist/server/self-test-redaction.js +1 -1
- package/dist/server/self-test.d.ts +117 -3
- package/dist/server/self-test.js +787 -151
- package/dist/server/serve-implementation.d.ts +210 -0
- package/dist/server/serve-implementation.js +2077 -0
- package/dist/server/serve.d.ts +1 -70
- package/dist/server/serve.js +1 -1143
- package/dist/server/types.d.ts +34 -9
- package/dist/server/types.js +8 -1
- package/dist/stateful/errors.d.ts +19 -0
- package/dist/stateful/errors.js +24 -0
- package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
- package/dist/stateful/http-provider-event-emitter.js +237 -0
- package/dist/stateful/http-session-owner-registry.d.ts +44 -0
- package/dist/stateful/http-session-owner-registry.js +210 -0
- package/dist/stateful/index.d.ts +18 -0
- package/dist/stateful/index.js +18 -0
- package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
- package/dist/stateful/provider-event-delivery-failures.js +43 -0
- package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
- package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
- package/dist/stateful/provider-event-pipeline.d.ts +50 -0
- package/dist/stateful/provider-event-pipeline.js +1 -0
- package/dist/stateful/provider-events.d.ts +101 -0
- package/dist/stateful/provider-events.js +289 -0
- package/dist/stateful/session-key.d.ts +15 -0
- package/dist/stateful/session-key.js +86 -0
- package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
- package/dist/stateful/stateful-provider-adapter-context.js +42 -0
- package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
- package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
- package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
- package/dist/stateful/stateful-provider-adapter.js +287 -0
- package/dist/stateful/stateful-provider-observability.d.ts +62 -0
- package/dist/stateful/stateful-provider-observability.js +161 -0
- package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
- package/dist/stateful/stateful-provider-runtime-context.js +60 -0
- package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
- package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
- package/dist/stateful/stateful-provider-session-routing.d.ts +67 -0
- package/dist/stateful/stateful-provider-session-routing.js +345 -0
- package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
- package/dist/stateful/stateful-provider-session-runtime.js +245 -0
- package/dist/stateful-signing.d.ts +18 -0
- package/dist/stateful-signing.js +27 -0
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +21 -21
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- package/dist/stream.d.ts +1 -1
- package/dist/stream.js +7 -1
- package/dist/testing/index.d.ts +3 -2
- package/dist/testing/index.js +3 -2
- package/dist/testing/run.d.ts +32 -2
- package/dist/testing/run.js +488 -28
- package/dist/types.d.ts +566 -19
- package/dist/types.js +1 -0
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +42 -7
- package/src/auth-turn/index.ts +2 -2
- package/src/auth.ts +146 -86
- package/src/ceremonies/index.ts +167 -92
- package/src/cli/commands.ts +10 -0
- package/src/cli/create.ts +42 -35
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/src/cli/templates/provider/README.md.tpl +4 -4
- package/src/config/loader.ts +667 -289
- package/src/contract-serialization.ts +10 -18
- package/src/contract-types.ts +3 -2
- package/src/contract.ts +14 -28
- package/src/declaration-validation.ts +202 -0
- package/src/define.ts +631 -495
- package/src/dev.ts +4 -9
- package/src/error-resolution.ts +127 -0
- package/src/errors.ts +56 -11
- package/src/fixture-sanitization.ts +247 -0
- package/src/i18n/catalog.ts +10 -32
- package/src/i18n/index.ts +2 -2
- package/src/i18n/keys.ts +5 -11
- package/src/index.ts +157 -44
- package/src/lint.ts +488 -154
- package/src/native-address.ts +340 -0
- package/src/native-egress-policy.ts +358 -0
- package/src/observability.ts +51 -1
- package/src/provider.ts +65 -11
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +13 -7
- package/src/runtime/browser.ts +252 -207
- package/src/runtime/cache.ts +209 -81
- package/src/runtime/choice-wordlist.ts +145 -0
- package/src/runtime/choice.ts +685 -216
- package/src/runtime/credential.ts +2 -2
- package/src/runtime/env.ts +1 -1
- package/src/runtime/executor.ts +37 -19
- package/src/runtime/http.ts +645 -65
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +530 -67
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- package/src/runtime/native-network-errors.ts +99 -0
- package/src/runtime/native-network.ts +1605 -0
- package/src/runtime/ocr.ts +523 -0
- package/src/runtime/otlp.ts +12 -23
- package/src/runtime/perf.ts +1 -1
- package/src/runtime/provider.ts +4 -9
- package/src/runtime/proxy-errors.ts +29 -42
- package/src/runtime/proxy-nodemaven.ts +221 -0
- package/src/runtime/proxy-retry-policy.ts +3 -3
- package/src/runtime/proxy-telemetry.ts +84 -77
- package/src/runtime/redirects.ts +66 -0
- package/src/runtime/redis.ts +10 -13
- package/src/runtime/request-options.ts +679 -9
- package/src/runtime/resolver-config.ts +6 -0
- package/src/runtime/resolver-public.ts +18 -0
- package/src/runtime/resolver-shared.ts +17 -0
- package/src/runtime/resolver-vendors/bindings.ts +56 -0
- package/src/runtime/resolver-vendors/browser.ts +408 -0
- package/src/runtime/resolver-vendors/hosts.ts +38 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +500 -0
- package/src/runtime/resolver-vendors/types.ts +173 -0
- package/src/runtime/resolver.ts +1060 -0
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +399 -161
- package/src/runtime/stealth-cookies.ts +132 -0
- package/src/runtime/stealth.ts +681 -295
- package/src/runtime/stt.ts +39 -113
- package/src/runtime/timeout.ts +18 -0
- package/src/runtime/trace.ts +14 -44
- package/src/runtime/waterfall.ts +5 -18
- package/src/schema.ts +23 -84
- package/src/serve.ts +6 -1
- package/src/server/index.ts +30 -7
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +1030 -180
- package/src/server/serve-implementation.ts +3060 -0
- package/src/server/serve.ts +1 -1781
- package/src/server/types.ts +12 -13
- package/src/stateful/README.md +146 -0
- package/src/stateful/errors.ts +35 -0
- package/src/stateful/http-provider-event-emitter.ts +314 -0
- package/src/stateful/http-session-owner-registry.ts +306 -0
- package/src/stateful/index.ts +18 -0
- package/src/stateful/provider-event-delivery-failures.ts +80 -0
- package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
- package/src/stateful/provider-event-pipeline.ts +61 -0
- package/src/stateful/provider-events.ts +462 -0
- package/src/stateful/session-key.ts +111 -0
- package/src/stateful/stateful-provider-adapter-context.ts +59 -0
- package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
- package/src/stateful/stateful-provider-adapter.ts +562 -0
- package/src/stateful/stateful-provider-observability.ts +261 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
- package/src/stateful/stateful-provider-runtime-context.ts +92 -0
- package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
- package/src/stateful/stateful-provider-session-routing.ts +546 -0
- package/src/stateful/stateful-provider-session-runtime.ts +403 -0
- package/src/stateful-signing.ts +46 -0
- package/src/stealth/profiles.ts +27 -33
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +16 -20
- package/src/testing/index.ts +11 -2
- package/src/testing/run.ts +668 -74
- package/src/types.ts +665 -35
- package/src/user-input.ts +118 -0
- package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
- package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
- /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
package/dist/types.js
CHANGED
|
@@ -32,6 +32,7 @@ export const HEALTH_CHECK_TIMEOUT_MS_MIN = 1;
|
|
|
32
32
|
export const HEALTH_CHECK_TIMEOUT_MS_MAX = 60_000;
|
|
33
33
|
export const HEALTH_CHECK_DEGRADED_THRESHOLD_MS_MIN = 1;
|
|
34
34
|
export const HEALTH_CHECK_DEGRADED_THRESHOLD_MS_MAX = 60_000;
|
|
35
|
+
export const VALID_OPERATION_ERROR_STATUSES = [400, 401, 404, 409, 410, 422, 429, 500, 502, 503, 504];
|
|
35
36
|
export const HttpRetryPreset = {
|
|
36
37
|
Off: "off",
|
|
37
38
|
TransportTransient: "transport_transient",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const NEEDS_INPUT_STATUS: "needs_input";
|
|
2
|
+
export interface ProviderSelectionOption {
|
|
3
|
+
readonly selection_value: string;
|
|
4
|
+
readonly label: string;
|
|
5
|
+
readonly [extra: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface ProviderRequiredSelection {
|
|
8
|
+
readonly selection_key: string;
|
|
9
|
+
readonly label: string;
|
|
10
|
+
readonly required: boolean;
|
|
11
|
+
readonly selection_type?: string;
|
|
12
|
+
readonly valid_options: readonly ProviderSelectionOption[];
|
|
13
|
+
readonly [extra: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
export interface ProviderSelectedOption {
|
|
16
|
+
readonly selection_key: string;
|
|
17
|
+
readonly selection_value: string;
|
|
18
|
+
/** Free-text answer for text-kind selections. */
|
|
19
|
+
readonly text?: string;
|
|
20
|
+
/** Quantity for quantity-kind selections. */
|
|
21
|
+
readonly quantity?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface ProviderNeedsInputPayload {
|
|
24
|
+
readonly status: typeof NEEDS_INPUT_STATUS;
|
|
25
|
+
readonly required_selections: readonly ProviderRequiredSelection[];
|
|
26
|
+
readonly selected_options?: readonly ProviderSelectedOption[];
|
|
27
|
+
/** Provider-specific fresh state token(s), e.g. `reservation_state`. */
|
|
28
|
+
readonly [extra: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
export declare function isProviderNeedsInputPayload(value: unknown): value is ProviderNeedsInputPayload;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Official cross-provider contract for user-input round-trips.
|
|
2
|
+
//
|
|
3
|
+
// Doctrine (born from the 2026-07-20 CatchTable reserve incident): an
|
|
4
|
+
// operation MUST NOT dead-end on a problem the end user can resolve by
|
|
5
|
+
// choosing among live options. Instead of throwing, the provider returns a
|
|
6
|
+
// SUCCESSFUL payload with `status: "needs_input"` that carries everything a
|
|
7
|
+
// consumer-side agent needs to relay the choice verbatim and retry:
|
|
8
|
+
//
|
|
9
|
+
// - `required_selections`: only the still-pending questions, each with
|
|
10
|
+
// human-readable `label`s and `valid_options` the agent shows the user
|
|
11
|
+
// as-is. The agent never chooses on the user's behalf.
|
|
12
|
+
// - `selected_options`: selections already settled, echoed so the follow-up
|
|
13
|
+
// call keeps them without the agent reconstructing anything.
|
|
14
|
+
// - a fresh provider-specific state token (e.g. `reservation_state`) minted
|
|
15
|
+
// at response time, so the retry never races an expired token.
|
|
16
|
+
//
|
|
17
|
+
// Keeping this success-shaped (instead of error `details`) is deliberate:
|
|
18
|
+
// consumer error-shaping layers routinely strip error metadata, and a model
|
|
19
|
+
// that only sees "error" narrates failure to the user. Complex recovery
|
|
20
|
+
// logic belongs to the system, not the model.
|
|
21
|
+
//
|
|
22
|
+
// Deliberately ABSENT: retry templates, next-action routing, or any other
|
|
23
|
+
// agent choreography. Provider payloads carry upstream-backed data only; how
|
|
24
|
+
// to phrase the ask and shape the retry call is the consumer's contract
|
|
25
|
+
// (apifuse#1030). The retry is fully determined by data: copy the echoed
|
|
26
|
+
// selected_options, add the user's new answers, resend with the fresh state.
|
|
27
|
+
export const NEEDS_INPUT_STATUS = "needs_input";
|
|
28
|
+
function isRecord(value) {
|
|
29
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
function isSelectionOption(value) {
|
|
32
|
+
return (isRecord(value) &&
|
|
33
|
+
typeof value.selection_value === "string" &&
|
|
34
|
+
typeof value.label === "string");
|
|
35
|
+
}
|
|
36
|
+
function isRequiredSelection(value) {
|
|
37
|
+
return (isRecord(value) &&
|
|
38
|
+
typeof value.selection_key === "string" &&
|
|
39
|
+
typeof value.label === "string" &&
|
|
40
|
+
typeof value.required === "boolean" &&
|
|
41
|
+
Array.isArray(value.valid_options) &&
|
|
42
|
+
value.valid_options.every(isSelectionOption));
|
|
43
|
+
}
|
|
44
|
+
function isSelectedOption(value) {
|
|
45
|
+
return (isRecord(value) &&
|
|
46
|
+
typeof value.selection_key === "string" &&
|
|
47
|
+
typeof value.selection_value === "string");
|
|
48
|
+
}
|
|
49
|
+
export function isProviderNeedsInputPayload(value) {
|
|
50
|
+
if (!isRecord(value)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
if (value.status !== NEEDS_INPUT_STATUS ||
|
|
54
|
+
!Array.isArray(value.required_selections) ||
|
|
55
|
+
!value.required_selections.every(isRequiredSelection)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
// A needs_input with nothing to ask AND nothing settled to echo is a
|
|
59
|
+
// no-op dead end — reject it so providers cannot ship it accidentally.
|
|
60
|
+
if (value.required_selections.length > 0) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return (Array.isArray(value.selected_options) &&
|
|
64
|
+
value.selected_options.length > 0 &&
|
|
65
|
+
value.selected_options.every(isSelectedOption));
|
|
66
|
+
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.2.0-beta.
|
|
2
|
+
"version": "2.2.0-beta.30",
|
|
3
3
|
"name": "@apifuse/provider-sdk",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
@@ -61,6 +61,36 @@
|
|
|
61
61
|
"import": "./dist/server/index.js",
|
|
62
62
|
"default": "./dist/server/index.js"
|
|
63
63
|
},
|
|
64
|
+
"./runtime/browser": {
|
|
65
|
+
"types": "./dist/runtime/browser.d.ts",
|
|
66
|
+
"import": "./dist/runtime/browser.js",
|
|
67
|
+
"default": "./dist/runtime/browser.js"
|
|
68
|
+
},
|
|
69
|
+
"./runtime/native-network": {
|
|
70
|
+
"types": "./dist/runtime/native-network.d.ts",
|
|
71
|
+
"import": "./dist/runtime/native-network.js",
|
|
72
|
+
"default": "./dist/runtime/native-network.js"
|
|
73
|
+
},
|
|
74
|
+
"./runtime/prevalidate": {
|
|
75
|
+
"types": "./dist/runtime/prevalidate.d.ts",
|
|
76
|
+
"import": "./dist/runtime/prevalidate.js",
|
|
77
|
+
"default": "./dist/runtime/prevalidate.js"
|
|
78
|
+
},
|
|
79
|
+
"./runtime/resolver": {
|
|
80
|
+
"types": "./dist/runtime/resolver-public.d.ts",
|
|
81
|
+
"import": "./dist/runtime/resolver-public.js",
|
|
82
|
+
"default": "./dist/runtime/resolver-public.js"
|
|
83
|
+
},
|
|
84
|
+
"./runtime/stealth": {
|
|
85
|
+
"types": "./dist/runtime/stealth.d.ts",
|
|
86
|
+
"import": "./dist/runtime/stealth.js",
|
|
87
|
+
"default": "./dist/runtime/stealth.js"
|
|
88
|
+
},
|
|
89
|
+
"./stateful": {
|
|
90
|
+
"types": "./dist/stateful/index.d.ts",
|
|
91
|
+
"import": "./dist/stateful/index.js",
|
|
92
|
+
"default": "./dist/stateful/index.js"
|
|
93
|
+
},
|
|
64
94
|
"./testing": {
|
|
65
95
|
"types": "./dist/testing/index.d.ts",
|
|
66
96
|
"import": "./dist/testing/index.js",
|
|
@@ -75,30 +105,32 @@
|
|
|
75
105
|
"scripts": {
|
|
76
106
|
"lint": "biome lint .",
|
|
77
107
|
"lint:fix": "biome lint --write",
|
|
108
|
+
"lint:deprecated": "bun run scripts/lint-deprecated-usage.ts",
|
|
78
109
|
"format": "biome format --write",
|
|
79
|
-
"type-check": "
|
|
110
|
+
"type-check": "tsc --noEmit",
|
|
80
111
|
"test": "bun test",
|
|
81
|
-
"check": "bun run lint && bun run type-check && bun run build",
|
|
112
|
+
"check": "bun run lint && bun run type-check && bun run lint:deprecated && bun run build",
|
|
82
113
|
"pack:check": "bun run build && bun bin/apifuse-pack-check.ts",
|
|
83
114
|
"pack:smoke": "bun run build && bun bin/apifuse-pack-smoke.ts",
|
|
115
|
+
"pack:types": "bun run build && bun bin/apifuse-pack-types.ts",
|
|
84
116
|
"release:guard": "bun scripts/guard-release-pr.ts",
|
|
85
117
|
"format:check": "biome format .",
|
|
86
|
-
"build": "
|
|
118
|
+
"build": "tsc -p tsconfig.build.json && rm -rf dist/cli/templates && cp -R src/cli/templates dist/cli/templates && rm -rf dist/auth-turn/fixtures && cp -R src/auth-turn/fixtures dist/auth-turn/fixtures && cp src/auth-turn/auth-turn.v1.schema.json dist/auth-turn/auth-turn.v1.schema.json"
|
|
87
119
|
},
|
|
88
120
|
"devDependencies": {
|
|
121
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
89
122
|
"@biomejs/biome": "^2.5.0",
|
|
90
123
|
"@types/bun": "latest",
|
|
91
124
|
"@types/node": "^25.9.3",
|
|
92
|
-
"
|
|
125
|
+
"ajv": "^8.17",
|
|
126
|
+
"typescript": "6.0.3"
|
|
93
127
|
},
|
|
94
128
|
"dependencies": {
|
|
95
129
|
"@clack/prompts": "^1.5.1",
|
|
96
130
|
"@rgrove/parse-xml": "4.2.2",
|
|
97
131
|
"@types/ms": "^2.1.0",
|
|
98
132
|
"acorn": "^8.17.0",
|
|
99
|
-
"ajv": "^8.17",
|
|
100
133
|
"hono": "^4.12.25",
|
|
101
|
-
"impit": "0.14.1",
|
|
102
134
|
"ioredis": "^5.11.1",
|
|
103
135
|
"ms": "^2.1.3",
|
|
104
136
|
"playwright": "^1.55.1",
|
|
@@ -106,6 +138,9 @@
|
|
|
106
138
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
107
139
|
"re2-wasm": "^1.0",
|
|
108
140
|
"safe-regex": "^2.1",
|
|
141
|
+
"socks": "^2.8.9",
|
|
142
|
+
"tough-cookie": "^6.0.2",
|
|
143
|
+
"wreq-js": "3.0.0",
|
|
109
144
|
"zod": "^4.4.3"
|
|
110
145
|
},
|
|
111
146
|
"repository": {
|
package/src/auth-turn/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthTurn } from "../types";
|
|
1
|
+
import type { AuthTurn } from "../types.js";
|
|
2
2
|
|
|
3
3
|
export type { AuthTurn };
|
|
4
4
|
|
|
@@ -24,7 +24,7 @@ export const AUTH_TURN_SCHEMA_ARTIFACT_PATH = "dist/auth-turn/auth-turn.v1.schem
|
|
|
24
24
|
*
|
|
25
25
|
* This is the exact codification of the runtime validation the SDK applies to
|
|
26
26
|
* ceremony outputs (see `validateCeremonyOutput` in `src/ceremonies`), which
|
|
27
|
-
*
|
|
27
|
+
* evaluates this same document. `kind` is an OPEN string on the wire: the known
|
|
28
28
|
* kinds in {@link TURN_KINDS} are tooling metadata, never a wire constraint.
|
|
29
29
|
*
|
|
30
30
|
* The committed artifact at `src/auth-turn/auth-turn.v1.schema.json` (shipped
|
package/src/auth.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import { AuthError, ProviderError } from "./errors";
|
|
1
|
+
import { AuthError, ProviderError } from "./errors.js";
|
|
2
|
+
import {
|
|
3
|
+
declarationInvalidError,
|
|
4
|
+
DECLARATION_RULE_IDS,
|
|
5
|
+
type DeclarationViolation,
|
|
6
|
+
} from "./declaration-validation.js";
|
|
2
7
|
import type {
|
|
3
8
|
AuthAbortData,
|
|
4
9
|
AuthConfig,
|
|
@@ -10,7 +15,7 @@ import type {
|
|
|
10
15
|
CredentialDeclaration,
|
|
11
16
|
FlowContext,
|
|
12
17
|
ProviderLocaleKeyInput,
|
|
13
|
-
} from "./types";
|
|
18
|
+
} from "./types.js";
|
|
14
19
|
|
|
15
20
|
const CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY = "__credentialsAuthChallenge";
|
|
16
21
|
const DEFAULT_COMPLETE_TURN_ID = "auth.complete";
|
|
@@ -33,11 +38,7 @@ function isSensitiveAuthDataKey(key: string): boolean {
|
|
|
33
38
|
return SENSITIVE_ABORT_DATA_KEY_PATTERN.test(normalizedAuthDataKey(key));
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
export type CredentialsAuthFieldType =
|
|
37
|
-
| "string"
|
|
38
|
-
| "email"
|
|
39
|
-
| "password"
|
|
40
|
-
| "otp";
|
|
41
|
+
export type CredentialsAuthFieldType = "string" | "email" | "password" | "otp";
|
|
41
42
|
|
|
42
43
|
export interface CredentialsAuthField {
|
|
43
44
|
type?: CredentialsAuthFieldType;
|
|
@@ -138,10 +139,9 @@ function abortData(options: {
|
|
|
138
139
|
};
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
export function createAuthFlowHelpers(
|
|
142
|
-
readonly signal?: AbortSignal;
|
|
143
|
-
|
|
144
|
-
} = {}): AuthFlowTerminalContext {
|
|
142
|
+
export function createAuthFlowHelpers(
|
|
143
|
+
options: { readonly signal?: AbortSignal; readonly deadline?: string } = {},
|
|
144
|
+
): AuthFlowTerminalContext {
|
|
145
145
|
return {
|
|
146
146
|
...(options.signal ? { signal: options.signal } : {}),
|
|
147
147
|
...(options.deadline ? { deadline: options.deadline } : {}),
|
|
@@ -156,16 +156,7 @@ export function createAuthFlowHelpers(options: {
|
|
|
156
156
|
},
|
|
157
157
|
};
|
|
158
158
|
},
|
|
159
|
-
abort({
|
|
160
|
-
code,
|
|
161
|
-
message,
|
|
162
|
-
retry,
|
|
163
|
-
actionHint,
|
|
164
|
-
fieldErrors,
|
|
165
|
-
data,
|
|
166
|
-
turnId,
|
|
167
|
-
expiresAt,
|
|
168
|
-
}) {
|
|
159
|
+
abort({ code, message, retry, actionHint, fieldErrors, data, turnId, expiresAt }) {
|
|
169
160
|
return {
|
|
170
161
|
kind: "abort",
|
|
171
162
|
...authTurnBase({ turnId, defaultTurnId: DEFAULT_ABORT_TURN_ID, expiresAt }),
|
|
@@ -190,8 +181,7 @@ export function createAuthFlowHelpers(options: {
|
|
|
190
181
|
...(options.hintKey ? { hintKey: options.hintKey } : {}),
|
|
191
182
|
...(options.timing ? { timing: options.timing } : {}),
|
|
192
183
|
...(options.data ? { data: options.data } : {}),
|
|
193
|
-
expectedInput:
|
|
194
|
-
options.expectedInput ?? expectedInputFromFields(options.fields ?? {}),
|
|
184
|
+
expectedInput: options.expectedInput ?? expectedInputFromFields(options.fields ?? {}),
|
|
195
185
|
};
|
|
196
186
|
},
|
|
197
187
|
nextPoll(options = {}) {
|
|
@@ -223,9 +213,7 @@ export class AuthAbortError extends AuthError {
|
|
|
223
213
|
}
|
|
224
214
|
}
|
|
225
215
|
|
|
226
|
-
export interface CredentialsAuthCompleteResult<
|
|
227
|
-
TCredentialKeys extends readonly string[],
|
|
228
|
-
> {
|
|
216
|
+
export interface CredentialsAuthCompleteResult<TCredentialKeys extends readonly string[]> {
|
|
229
217
|
credential: CredentialsAuthCredential<TCredentialKeys>;
|
|
230
218
|
/** Additional non-credential auth-flow data to return alongside credential. */
|
|
231
219
|
data?: Record<string, unknown>;
|
|
@@ -247,9 +235,7 @@ export interface CredentialsAuthChallengeRequest<TChallengeId extends string = s
|
|
|
247
235
|
export type CredentialsAuthLoginResult<
|
|
248
236
|
TCredentialKeys extends readonly string[],
|
|
249
237
|
TChallengeId extends string = string,
|
|
250
|
-
> =
|
|
251
|
-
| CredentialsAuthCompleteResult<TCredentialKeys>
|
|
252
|
-
| CredentialsAuthChallengeRequest<TChallengeId>;
|
|
238
|
+
> = CredentialsAuthCompleteResult<TCredentialKeys> | CredentialsAuthChallengeRequest<TChallengeId>;
|
|
253
239
|
|
|
254
240
|
export interface CredentialsAuthChallengeDefinition<
|
|
255
241
|
TFields extends CredentialsAuthFields,
|
|
@@ -310,6 +296,25 @@ export interface DefineCredentialsAuthOptions<
|
|
|
310
296
|
):
|
|
311
297
|
| CredentialsAuthLoginResult<TCredentialKeys, keyof TChallenges & string>
|
|
312
298
|
| Promise<CredentialsAuthLoginResult<TCredentialKeys, keyof TChallenges & string>>;
|
|
299
|
+
/**
|
|
300
|
+
* Optional re-mint of an expired session from the stored credential, wired
|
|
301
|
+
* to `auth.flow.refresh`.
|
|
302
|
+
*
|
|
303
|
+
* Credential-auth upstreams routinely invalidate a session well before the
|
|
304
|
+
* `expiresAt` the provider advertised, which leaves every operation failing
|
|
305
|
+
* with a reauth error until a human repeats the whole interactive login.
|
|
306
|
+
* Implement this to re-establish the session from what is already stored on
|
|
307
|
+
* the connection; the result is resolved exactly like `login`, so it may
|
|
308
|
+
* also raise a challenge when the upstream demands one. Omit it when the
|
|
309
|
+
* upstream has no non-interactive path and re-authentication genuinely
|
|
310
|
+
* requires the user.
|
|
311
|
+
*/
|
|
312
|
+
refresh?(
|
|
313
|
+
ctx: FlowContext,
|
|
314
|
+
input: Partial<CredentialsAuthInput<TFields>>,
|
|
315
|
+
):
|
|
316
|
+
| CredentialsAuthLoginResult<TCredentialKeys, keyof TChallenges & string>
|
|
317
|
+
| Promise<CredentialsAuthLoginResult<TCredentialKeys, keyof TChallenges & string>>;
|
|
313
318
|
}
|
|
314
319
|
|
|
315
320
|
export interface DefinedCredentialsAuth {
|
|
@@ -351,12 +356,8 @@ function expectedInputFromFields(fields: CredentialsAuthFields): Record<string,
|
|
|
351
356
|
...(field.type === "password" ? { format: "password" } : {}),
|
|
352
357
|
...(field.type === "otp" ? { format: "otp" } : {}),
|
|
353
358
|
...(field.labelKey ? { nameKey: field.labelKey } : {}),
|
|
354
|
-
...(field.descriptionKey
|
|
355
|
-
|
|
356
|
-
: {}),
|
|
357
|
-
...(field.placeholderKey
|
|
358
|
-
? { placeholderKey: field.placeholderKey }
|
|
359
|
-
: {}),
|
|
359
|
+
...(field.descriptionKey ? { descriptionKey: field.descriptionKey } : {}),
|
|
360
|
+
...(field.placeholderKey ? { placeholderKey: field.placeholderKey } : {}),
|
|
360
361
|
...(field.sensitive || field.type === "password" || field.type === "otp"
|
|
361
362
|
? { sensitive: true }
|
|
362
363
|
: {}),
|
|
@@ -394,6 +395,27 @@ function normalizeInput<TFields extends CredentialsAuthFields>(
|
|
|
394
395
|
return result as CredentialsAuthInput<TFields>;
|
|
395
396
|
}
|
|
396
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Refresh variant of {@link normalizeInput}. `login` coerces every declared
|
|
400
|
+
* field to a string because the interactive turn has already enforced that they
|
|
401
|
+
* are present; refresh runs with no user present, so an absent field is omitted
|
|
402
|
+
* rather than turned into an empty string. That keeps "the user did not supply
|
|
403
|
+
* this" distinguishable from "the user supplied an empty value".
|
|
404
|
+
*/
|
|
405
|
+
function normalizePartialInput<TFields extends CredentialsAuthFields>(
|
|
406
|
+
fields: TFields,
|
|
407
|
+
input: Record<string, unknown> | undefined,
|
|
408
|
+
): Partial<CredentialsAuthInput<TFields>> {
|
|
409
|
+
const result: Record<string, string> = {};
|
|
410
|
+
for (const name of Object.keys(fields)) {
|
|
411
|
+
const value = input?.[name];
|
|
412
|
+
if (typeof value === "string") {
|
|
413
|
+
result[name] = value;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return result as Partial<CredentialsAuthInput<TFields>>;
|
|
417
|
+
}
|
|
418
|
+
|
|
397
419
|
function assertCredentialKeys<TCredentialKeys extends readonly string[]>(
|
|
398
420
|
credentialKeys: TCredentialKeys,
|
|
399
421
|
credential: Record<string, unknown>,
|
|
@@ -437,10 +459,7 @@ function getPendingChallenge(ctx: FlowContext): PendingCredentialsAuthChallenge
|
|
|
437
459
|
};
|
|
438
460
|
}
|
|
439
461
|
|
|
440
|
-
function setPendingChallenge(
|
|
441
|
-
ctx: FlowContext,
|
|
442
|
-
challenge: PendingCredentialsAuthChallenge,
|
|
443
|
-
): void {
|
|
462
|
+
function setPendingChallenge(ctx: FlowContext, challenge: PendingCredentialsAuthChallenge): void {
|
|
444
463
|
ctx.context.set(CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY, challenge);
|
|
445
464
|
}
|
|
446
465
|
|
|
@@ -473,9 +492,7 @@ function retryTurn(
|
|
|
473
492
|
turnId: retryTurnId,
|
|
474
493
|
expectedInput,
|
|
475
494
|
data: {
|
|
476
|
-
fieldErrors: Object.fromEntries(
|
|
477
|
-
missing.map((name) => [name, "Required"]),
|
|
478
|
-
),
|
|
495
|
+
fieldErrors: Object.fromEntries(missing.map((name) => [name, "Required"])),
|
|
479
496
|
fieldErrorKeys: Object.fromEntries(
|
|
480
497
|
missing.map((name) => [name, "auth.credentials.fieldRequired"]),
|
|
481
498
|
),
|
|
@@ -499,18 +516,12 @@ function completeTurn<TCredentialKeys extends readonly string[]>(
|
|
|
499
516
|
typeof result.credential !== "object" ||
|
|
500
517
|
Array.isArray(result.credential)
|
|
501
518
|
) {
|
|
502
|
-
throw new ProviderError(
|
|
503
|
-
|
|
504
|
-
{
|
|
505
|
-
|
|
506
|
-
fix: "Return { credential: { ... } } from defineCredentialsAuth handlers. Gateway persists only auth.flow complete data.credential into the connection.",
|
|
507
|
-
},
|
|
508
|
-
);
|
|
519
|
+
throw new ProviderError("Credentials auth login completed without a credential object", {
|
|
520
|
+
code: "credentials_auth_missing_credential",
|
|
521
|
+
fix: "Return { credential: { ... } } from defineCredentialsAuth handlers. Gateway persists only auth.flow complete data.credential into the connection.",
|
|
522
|
+
});
|
|
509
523
|
}
|
|
510
|
-
assertCredentialKeys(
|
|
511
|
-
credentialKeys,
|
|
512
|
-
result.credential as Record<string, unknown>,
|
|
513
|
-
);
|
|
524
|
+
assertCredentialKeys(credentialKeys, result.credential as Record<string, unknown>);
|
|
514
525
|
return {
|
|
515
526
|
kind: "complete",
|
|
516
527
|
turnId: result.turnId ?? defaultTurnId,
|
|
@@ -523,24 +534,18 @@ function completeTurn<TCredentialKeys extends readonly string[]>(
|
|
|
523
534
|
}
|
|
524
535
|
|
|
525
536
|
function challengeTurn(
|
|
526
|
-
definition: CredentialsAuthChallengeDefinition<
|
|
527
|
-
CredentialsAuthFields,
|
|
528
|
-
readonly string[],
|
|
529
|
-
string
|
|
530
|
-
>,
|
|
537
|
+
definition: CredentialsAuthChallengeDefinition<CredentialsAuthFields, readonly string[], string>,
|
|
531
538
|
request: CredentialsAuthChallengeRequest<string>,
|
|
532
539
|
): AuthTurn {
|
|
533
|
-
const expectedInput = definition.fields
|
|
534
|
-
? expectedInputFromFields(definition.fields)
|
|
535
|
-
: undefined;
|
|
540
|
+
const expectedInput = definition.fields ? expectedInputFromFields(definition.fields) : undefined;
|
|
536
541
|
return {
|
|
537
542
|
kind: expectedInput ? "form" : "pending",
|
|
538
543
|
turnId: request.turnId ?? definition.turnId ?? `credentials.${request.challengeId}`,
|
|
539
544
|
...(request.expiresAt ? { expiresAt: request.expiresAt } : {}),
|
|
540
|
-
...(request.hintKey ?? definition.hintKey
|
|
545
|
+
...((request.hintKey ?? definition.hintKey)
|
|
541
546
|
? { hintKey: request.hintKey ?? definition.hintKey }
|
|
542
547
|
: {}),
|
|
543
|
-
...(request.timing ?? definition.timing
|
|
548
|
+
...((request.timing ?? definition.timing)
|
|
544
549
|
? { timing: request.timing ?? definition.timing }
|
|
545
550
|
: {}),
|
|
546
551
|
...(expectedInput ? { expectedInput } : {}),
|
|
@@ -630,13 +635,7 @@ async function continuePendingChallenge<TCredentialKeys extends readonly string[
|
|
|
630
635
|
normalizeInput(definition.fields, rawInput),
|
|
631
636
|
pending.state,
|
|
632
637
|
);
|
|
633
|
-
return await resolveAuthResult(
|
|
634
|
-
ctx,
|
|
635
|
-
credentialKeys,
|
|
636
|
-
challenges,
|
|
637
|
-
result,
|
|
638
|
-
completeTurnId,
|
|
639
|
-
);
|
|
638
|
+
return await resolveAuthResult(ctx, credentialKeys, challenges, result, completeTurnId);
|
|
640
639
|
}
|
|
641
640
|
|
|
642
641
|
async function pollPendingChallenge<TCredentialKeys extends readonly string[]>(
|
|
@@ -669,13 +668,7 @@ async function pollPendingChallenge<TCredentialKeys extends readonly string[]>(
|
|
|
669
668
|
`credentials.${pending.challengeId}.pending`,
|
|
670
669
|
};
|
|
671
670
|
}
|
|
672
|
-
return await resolveAuthResult(
|
|
673
|
-
ctx,
|
|
674
|
-
credentialKeys,
|
|
675
|
-
challenges,
|
|
676
|
-
result,
|
|
677
|
-
completeTurnId,
|
|
678
|
-
);
|
|
671
|
+
return await resolveAuthResult(ctx, credentialKeys, challenges, result, completeTurnId);
|
|
679
672
|
}
|
|
680
673
|
|
|
681
674
|
export function defineCredentialsAuth<
|
|
@@ -697,7 +690,7 @@ export function defineCredentialsAuth<
|
|
|
697
690
|
): DefinedCredentialsAuth {
|
|
698
691
|
if (Object.keys(options.fields).length === 0) {
|
|
699
692
|
throw new ProviderError("defineCredentialsAuth requires at least one field", {
|
|
700
|
-
fix:
|
|
693
|
+
fix: 'Pass fields such as { email: { type: "email" }, password: { type: "password" } }.',
|
|
701
694
|
});
|
|
702
695
|
}
|
|
703
696
|
|
|
@@ -708,6 +701,7 @@ export function defineCredentialsAuth<
|
|
|
708
701
|
string,
|
|
709
702
|
CredentialsAuthChallengeDefinition<CredentialsAuthFields, TCredentialKeys, string>
|
|
710
703
|
>;
|
|
704
|
+
validateCredentialsAuthChallenges(challenges);
|
|
711
705
|
|
|
712
706
|
return {
|
|
713
707
|
auth: {
|
|
@@ -737,10 +731,7 @@ export function defineCredentialsAuth<
|
|
|
737
731
|
return retryTurn(expectedInput, missing, retryTurnId);
|
|
738
732
|
}
|
|
739
733
|
|
|
740
|
-
const result = await options.login(
|
|
741
|
-
ctx,
|
|
742
|
-
normalizeInput(options.fields, rawInput),
|
|
743
|
-
);
|
|
734
|
+
const result = await options.login(ctx, normalizeInput(options.fields, rawInput));
|
|
744
735
|
return await resolveAuthResult(
|
|
745
736
|
ctx,
|
|
746
737
|
options.credentialKeys,
|
|
@@ -765,6 +756,44 @@ export function defineCredentialsAuth<
|
|
|
765
756
|
completeTurnId,
|
|
766
757
|
);
|
|
767
758
|
},
|
|
759
|
+
// Only advertise refresh when the provider implements it: the
|
|
760
|
+
// protocol treats the hook's presence as "this connection can be
|
|
761
|
+
// re-established without the user", and exposing a stub that
|
|
762
|
+
// cannot actually re-mint would turn a recoverable expiry into a
|
|
763
|
+
// silent failure.
|
|
764
|
+
...(options.refresh
|
|
765
|
+
? {
|
|
766
|
+
refresh: async (ctx: FlowContext, rawInput?: Record<string, unknown>) => {
|
|
767
|
+
// A pending challenge belongs to the interactive flow that
|
|
768
|
+
// raised it; finish it there rather than restarting.
|
|
769
|
+
const pending = getPendingChallenge(ctx);
|
|
770
|
+
if (pending) {
|
|
771
|
+
return await continuePendingChallenge(
|
|
772
|
+
ctx,
|
|
773
|
+
options.credentialKeys,
|
|
774
|
+
challenges,
|
|
775
|
+
pending,
|
|
776
|
+
rawInput,
|
|
777
|
+
completeTurnId,
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Refresh runs without user input, so fields are optional
|
|
782
|
+
// here — unlike `continue`, missing ones are not a retry.
|
|
783
|
+
const result = await options.refresh!(
|
|
784
|
+
ctx,
|
|
785
|
+
normalizePartialInput(options.fields, rawInput),
|
|
786
|
+
);
|
|
787
|
+
return await resolveAuthResult(
|
|
788
|
+
ctx,
|
|
789
|
+
options.credentialKeys,
|
|
790
|
+
challenges,
|
|
791
|
+
result,
|
|
792
|
+
completeTurnId,
|
|
793
|
+
);
|
|
794
|
+
},
|
|
795
|
+
}
|
|
796
|
+
: {}),
|
|
768
797
|
},
|
|
769
798
|
},
|
|
770
799
|
credential: {
|
|
@@ -776,11 +805,42 @@ export function defineCredentialsAuth<
|
|
|
776
805
|
},
|
|
777
806
|
context: {
|
|
778
807
|
keys: Array.from(
|
|
779
|
-
new Set([
|
|
780
|
-
CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY,
|
|
781
|
-
...(options.contextKeys ?? []),
|
|
782
|
-
]),
|
|
808
|
+
new Set([CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY, ...(options.contextKeys ?? [])]),
|
|
783
809
|
),
|
|
784
810
|
},
|
|
785
811
|
};
|
|
786
812
|
}
|
|
813
|
+
|
|
814
|
+
function validateCredentialsAuthChallenges(
|
|
815
|
+
challenges: Record<
|
|
816
|
+
string,
|
|
817
|
+
CredentialsAuthChallengeDefinition<CredentialsAuthFields, readonly string[], string>
|
|
818
|
+
>,
|
|
819
|
+
): void {
|
|
820
|
+
const violations: DeclarationViolation[] = [];
|
|
821
|
+
for (const [challengeId, challenge] of Object.entries(challenges)) {
|
|
822
|
+
const fieldCount =
|
|
823
|
+
challenge.fields && typeof challenge.fields === "object"
|
|
824
|
+
? Object.keys(challenge.fields).length
|
|
825
|
+
: 0;
|
|
826
|
+
const fieldsDeclared =
|
|
827
|
+
challenge.fields !== undefined && challenge.fields !== null;
|
|
828
|
+
const hasFields = fieldCount > 0;
|
|
829
|
+
const hasVerify = typeof challenge.verify === "function";
|
|
830
|
+
const hasPoll = typeof challenge.poll === "function";
|
|
831
|
+
const isInteractive = hasFields && hasVerify && !hasPoll;
|
|
832
|
+
const isPolling = !fieldsDeclared && !hasVerify && hasPoll;
|
|
833
|
+
const isHybrid = hasFields && hasVerify && hasPoll;
|
|
834
|
+
const emptyFieldsDeclared = fieldsDeclared && !hasFields;
|
|
835
|
+
if (!emptyFieldsDeclared && (isInteractive || isPolling || isHybrid)) continue;
|
|
836
|
+
|
|
837
|
+
const path = `challenges.${challengeId}`;
|
|
838
|
+
violations.push({
|
|
839
|
+
ruleId: DECLARATION_RULE_IDS.challengeShape,
|
|
840
|
+
path,
|
|
841
|
+
message: "challenge must be interactive, polling, or an explicit hybrid.",
|
|
842
|
+
fix: `Give ${path} non-empty fields plus verify, poll alone, or all three for a hybrid.`,
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
if (violations.length > 0) throw declarationInvalidError(violations);
|
|
846
|
+
}
|