@apifuse/provider-sdk 2.2.0-beta.1 → 2.2.0-beta.10
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 +106 -0
- package/CHANGELOG.md +37 -0
- package/README.md +5 -1
- package/SUBMISSION.md +1 -1
- package/bin/apifuse-check.ts +44 -59
- package/bin/apifuse-create.ts +1 -1
- package/bin/apifuse-dev.ts +27 -52
- package/bin/apifuse-pack-check.ts +14 -0
- package/bin/apifuse-pack-smoke.ts +22 -81
- package/bin/apifuse-pack-types.ts +266 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +53 -70
- package/bin/apifuse-submit-check.ts +510 -45
- 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-semantics.ts +204 -0
- package/bin/submit-check-xml.ts +134 -0
- package/dist/auth-turn/index.d.ts +3 -3
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +9 -18
- package/dist/ceremonies/index.d.ts +1 -1
- package/dist/ceremonies/index.js +8 -18
- 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/config/loader.d.ts +149 -8
- package/dist/config/loader.js +378 -89
- package/dist/contract-serialization.d.ts +2 -2
- package/dist/contract-serialization.js +3 -6
- package/dist/contract-types.d.ts +2 -2
- package/dist/contract.d.ts +3 -3
- package/dist/contract.js +4 -6
- package/dist/define.d.ts +8 -1
- package/dist/define.js +119 -122
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/errors.d.ts +4 -1
- package/dist/errors.js +48 -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 +44 -42
- package/dist/index.js +39 -37
- package/dist/lint.js +8 -15
- package/dist/provider.d.ts +11 -11
- package/dist/provider.js +9 -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 +1 -1
- package/dist/runtime/auth-flow.js +3 -3
- package/dist/runtime/browser.d.ts +1 -1
- package/dist/runtime/browser.js +15 -29
- package/dist/runtime/cache.d.ts +1 -1
- package/dist/runtime/cache.js +4 -8
- package/dist/runtime/choice.d.ts +1 -1
- package/dist/runtime/choice.js +31 -35
- 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 +15 -3
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +144 -348
- 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 +56 -19
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- 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 +35 -0
- package/dist/runtime/proxy-nodemaven.js +128 -0
- package/dist/runtime/proxy-retry-policy.d.ts +40 -0
- package/dist/runtime/proxy-retry-policy.js +326 -0
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +55 -52
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +1 -1
- package/dist/runtime/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +2 -2
- package/dist/runtime/state.js +15 -4
- package/dist/runtime/stealth.d.ts +2 -2
- package/dist/runtime/stealth.js +108 -212
- package/dist/runtime/stt.d.ts +1 -1
- package/dist/runtime/stt.js +11 -15
- 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-redaction.d.ts +1 -1
- package/dist/server/self-test-redaction.js +1 -1
- package/dist/server/self-test.d.ts +104 -3
- package/dist/server/self-test.js +673 -115
- package/dist/server/serve.d.ts +8 -3
- package/dist/server/serve.js +105 -59
- package/dist/server/types.d.ts +10 -9
- package/dist/server/types.js +3 -7
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- package/dist/stream.d.ts +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/testing/run.d.ts +1 -1
- package/dist/testing/run.js +7 -14
- package/dist/types.d.ts +87 -1
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +9 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +28 -86
- package/src/ceremonies/index.ts +24 -78
- 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/config/loader.ts +543 -208
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +12 -28
- package/src/define.ts +247 -505
- package/src/dev.ts +4 -9
- package/src/errors.ts +58 -5
- 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 +49 -42
- package/src/lint.ts +89 -159
- package/src/provider.ts +15 -11
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +5 -7
- package/src/runtime/browser.ts +64 -187
- package/src/runtime/cache.ts +20 -67
- package/src/runtime/choice.ts +79 -132
- package/src/runtime/credential.ts +2 -2
- package/src/runtime/env.ts +1 -1
- package/src/runtime/executor.ts +23 -20
- package/src/runtime/http.ts +195 -559
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +78 -65
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- 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 +178 -0
- package/src/runtime/proxy-retry-policy.ts +469 -0
- package/src/runtime/proxy-telemetry.ts +79 -77
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +4 -13
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +41 -110
- package/src/runtime/stealth.ts +162 -377
- package/src/runtime/stt.ts +38 -94
- package/src/runtime/trace.ts +14 -44
- package/src/runtime/waterfall.ts +5 -18
- package/src/schema.ts +23 -84
- package/src/serve.ts +1 -1
- package/src/server/index.ts +12 -7
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +857 -132
- package/src/server/serve.ts +172 -245
- package/src/server/types.ts +7 -19
- package/src/stealth/profiles.ts +10 -26
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +2 -2
- package/src/testing/run.ts +19 -67
- package/src/types.ts +88 -1
- 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.d.ts
CHANGED
|
@@ -650,7 +650,25 @@ export type ConnectionMode = AuthMode;
|
|
|
650
650
|
export type ProviderReviewed = "first-party" | "community" | "staging";
|
|
651
651
|
export type ProviderAccessVisibility = "public" | "early_access";
|
|
652
652
|
export type ProviderProxyMode = "disabled" | "optional" | "required";
|
|
653
|
-
|
|
653
|
+
/**
|
|
654
|
+
* Proxy egress vendors. These are FOUR DISTINCT services — do not conflate them
|
|
655
|
+
* (a common mistake because the names collide with a well-known rebrand):
|
|
656
|
+
*
|
|
657
|
+
* - `smartproxy` — **api.smartproxy.org**, a residential proxy with an IP
|
|
658
|
+
* *extraction/allocation* API (app_key → a pool of raw `ip:port` CONNECT
|
|
659
|
+
* endpoints). This is our own vendor. It is NOT the company formerly named
|
|
660
|
+
* "Smartproxy". Credentials: `APIFUSE__PROXY__SMARTPROXY_APP_KEY`.
|
|
661
|
+
* - `nodemaven` — **gate.nodemaven.com**, a *gateway* proxy with static
|
|
662
|
+
* credentials; geo/session encoded in the username, no allocation API.
|
|
663
|
+
* - `decodo` — **decodo.com**, the *gateway* proxy that was named "Smartproxy"
|
|
664
|
+
* (smartproxy.com) before its 2025 rebrand to Decodo. Sticky sessions via
|
|
665
|
+
* username params. A different company from `smartproxy` above.
|
|
666
|
+
* **@deprecated** — unused; no managed adapter. Use `smartproxy`/`nodemaven`,
|
|
667
|
+
* or the `APIFUSE__PROXY__URL` bring-your-own escape hatch.
|
|
668
|
+
* - `custom` — **@deprecated** bring-your-own static proxy URL marker. The
|
|
669
|
+
* `APIFUSE__PROXY__URL` env still works without declaring this value.
|
|
670
|
+
*/
|
|
671
|
+
export type ProviderProxyProvider = "smartproxy" | "nodemaven" | "decodo" | "custom";
|
|
654
672
|
export type ProviderProxySessionAffinity = "request" | "operation" | "auth-flow" | "connection";
|
|
655
673
|
export interface ProviderProxyPolicy {
|
|
656
674
|
/**
|
|
@@ -658,7 +676,18 @@ export interface ProviderProxyPolicy {
|
|
|
658
676
|
* certificate verification, and vendor allocator endpoints are SDK-owned.
|
|
659
677
|
*/
|
|
660
678
|
mode: ProviderProxyMode;
|
|
679
|
+
/**
|
|
680
|
+
* @deprecated Use `providers: [...]` to declare an ordered vendor fallback
|
|
681
|
+
* chain. A single-element `providers` list is equivalent to this field.
|
|
682
|
+
*/
|
|
661
683
|
provider?: ProviderProxyProvider;
|
|
684
|
+
/**
|
|
685
|
+
* Ordered proxy-vendor fallback chain. The SDK tries each vendor in order and
|
|
686
|
+
* fails over to the next when a vendor lacks credentials or its allocation /
|
|
687
|
+
* transport is exhausted. When omitted, `provider` (or the platform default)
|
|
688
|
+
* is used as a single-vendor chain.
|
|
689
|
+
*/
|
|
690
|
+
providers?: ProviderProxyProvider[];
|
|
662
691
|
geo?: {
|
|
663
692
|
/** ISO 3166-1 alpha-2 country code, for example KR or US. */
|
|
664
693
|
country?: Iso3166Alpha2CountryCode;
|
|
@@ -1418,6 +1447,19 @@ export interface OperationContractMetadata {
|
|
|
1418
1447
|
deprecation?: OperationDeprecationMetadata;
|
|
1419
1448
|
}
|
|
1420
1449
|
export interface OperationDefinition<TInput extends SchemaLike = SchemaLike, TOutput extends SchemaLike = SchemaLike> {
|
|
1450
|
+
/**
|
|
1451
|
+
* Short English display title for the operation. The SDK passes it through
|
|
1452
|
+
* verbatim; the APIFuse registry derives the operation's en locale title
|
|
1453
|
+
* baseline from it (`docs.titleKey` remains available as an explicit key
|
|
1454
|
+
* override).
|
|
1455
|
+
*/
|
|
1456
|
+
title?: string;
|
|
1457
|
+
/**
|
|
1458
|
+
* Raw English operation description. The SDK passes it through verbatim;
|
|
1459
|
+
* keyed descriptions via `descriptionKey` remain the primary flow, and
|
|
1460
|
+
* provider lint rules for raw prose descriptions still apply.
|
|
1461
|
+
*/
|
|
1462
|
+
description?: string;
|
|
1421
1463
|
descriptionKey?: ProviderLocaleKeyInput;
|
|
1422
1464
|
docs?: OperationDocMeta;
|
|
1423
1465
|
whenToUseKeys?: readonly ProviderLocaleKeyInput[];
|
|
@@ -1447,10 +1489,54 @@ export interface OperationDefinition<TInput extends SchemaLike = SchemaLike, TOu
|
|
|
1447
1489
|
healthCheck?: HealthCheckSuite<InferSchemaOutput<TInput>, InferSchemaOutput<TOutput>>;
|
|
1448
1490
|
healthCheckUnsupported?: HealthCheckUnsupported;
|
|
1449
1491
|
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Author-declared deployment overrides accepted as the optional top-level
|
|
1494
|
+
* `deployment` key on `defineProvider()`.
|
|
1495
|
+
*
|
|
1496
|
+
* The SDK passes this object through VERBATIM onto the returned provider
|
|
1497
|
+
* definition — it is typed here but deliberately not deep-validated: the
|
|
1498
|
+
* APIFuse registry builder owns deployment validation and resolves omitted
|
|
1499
|
+
* fields against the runtime deployment profiles. The shape mirrors the
|
|
1500
|
+
* registry's `ProviderDeploymentOverrides` contract; every field is
|
|
1501
|
+
* optional. Note that the deployment `runtime` axis
|
|
1502
|
+
* (`shared`/`dedicated`/`browser`) is distinct from the provider execution
|
|
1503
|
+
* `runtime` (`standard`/`shared`/`browser`).
|
|
1504
|
+
*/
|
|
1505
|
+
export interface ProviderDeploymentOverrides {
|
|
1506
|
+
runtime?: "shared" | "dedicated" | "browser";
|
|
1507
|
+
language?: "typescript" | "python";
|
|
1508
|
+
replicas?: number;
|
|
1509
|
+
hpa?: {
|
|
1510
|
+
enabled: boolean;
|
|
1511
|
+
minReplicas?: number;
|
|
1512
|
+
maxReplicas?: number;
|
|
1513
|
+
targetCPUUtilizationPercentage?: number;
|
|
1514
|
+
};
|
|
1515
|
+
resources?: {
|
|
1516
|
+
cpu: string;
|
|
1517
|
+
memory: string;
|
|
1518
|
+
};
|
|
1519
|
+
cache?: {
|
|
1520
|
+
redis?: {
|
|
1521
|
+
enabled: boolean;
|
|
1522
|
+
url?: string;
|
|
1523
|
+
};
|
|
1524
|
+
};
|
|
1525
|
+
network?: {
|
|
1526
|
+
additionalTcpPorts?: number[];
|
|
1527
|
+
};
|
|
1528
|
+
buildContext?: string;
|
|
1529
|
+
}
|
|
1450
1530
|
export interface ProviderDefinition {
|
|
1451
1531
|
id: string;
|
|
1452
1532
|
version: string;
|
|
1453
1533
|
runtime: "standard" | "shared" | "browser";
|
|
1534
|
+
/**
|
|
1535
|
+
* Optional deployment overrides, passed through verbatim from
|
|
1536
|
+
* `defineProvider({ deployment })`. Validation and profile resolution are
|
|
1537
|
+
* owned by the APIFuse registry builder, not the SDK.
|
|
1538
|
+
*/
|
|
1539
|
+
deployment?: ProviderDeploymentOverrides;
|
|
1454
1540
|
allowedHosts?: string[];
|
|
1455
1541
|
stealth?: {
|
|
1456
1542
|
profile: string;
|
|
@@ -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.10",
|
|
3
3
|
"name": "@apifuse/provider-sdk",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
@@ -75,24 +75,28 @@
|
|
|
75
75
|
"scripts": {
|
|
76
76
|
"lint": "biome lint .",
|
|
77
77
|
"lint:fix": "biome lint --write",
|
|
78
|
+
"lint:deprecated": "bun run scripts/lint-deprecated-usage.ts",
|
|
78
79
|
"format": "biome format --write",
|
|
79
|
-
"type-check": "
|
|
80
|
+
"type-check": "tsc --noEmit",
|
|
80
81
|
"test": "bun test",
|
|
81
|
-
"check": "bun run lint && bun run type-check && bun run build",
|
|
82
|
+
"check": "bun run lint && bun run type-check && bun run lint:deprecated && bun run build",
|
|
82
83
|
"pack:check": "bun run build && bun bin/apifuse-pack-check.ts",
|
|
83
84
|
"pack:smoke": "bun run build && bun bin/apifuse-pack-smoke.ts",
|
|
85
|
+
"pack:types": "bun run build && bun bin/apifuse-pack-types.ts",
|
|
84
86
|
"release:guard": "bun scripts/guard-release-pr.ts",
|
|
85
87
|
"format:check": "biome format .",
|
|
86
|
-
"build": "
|
|
88
|
+
"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
89
|
},
|
|
88
90
|
"devDependencies": {
|
|
91
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
89
92
|
"@biomejs/biome": "^2.5.0",
|
|
90
93
|
"@types/bun": "latest",
|
|
91
94
|
"@types/node": "^25.9.3",
|
|
92
|
-
"
|
|
95
|
+
"typescript": "6.0.3"
|
|
93
96
|
},
|
|
94
97
|
"dependencies": {
|
|
95
98
|
"@clack/prompts": "^1.5.1",
|
|
99
|
+
"@rgrove/parse-xml": "4.2.2",
|
|
96
100
|
"@types/ms": "^2.1.0",
|
|
97
101
|
"acorn": "^8.17.0",
|
|
98
102
|
"ajv": "^8.17",
|
package/src/auth-turn/index.ts
CHANGED
package/src/auth.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthError, ProviderError } from "./errors";
|
|
1
|
+
import { AuthError, ProviderError } from "./errors.js";
|
|
2
2
|
import type {
|
|
3
3
|
AuthAbortData,
|
|
4
4
|
AuthConfig,
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
CredentialDeclaration,
|
|
11
11
|
FlowContext,
|
|
12
12
|
ProviderLocaleKeyInput,
|
|
13
|
-
} from "./types";
|
|
13
|
+
} from "./types.js";
|
|
14
14
|
|
|
15
15
|
const CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY = "__credentialsAuthChallenge";
|
|
16
16
|
const DEFAULT_COMPLETE_TURN_ID = "auth.complete";
|
|
@@ -33,11 +33,7 @@ function isSensitiveAuthDataKey(key: string): boolean {
|
|
|
33
33
|
return SENSITIVE_ABORT_DATA_KEY_PATTERN.test(normalizedAuthDataKey(key));
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export type CredentialsAuthFieldType =
|
|
37
|
-
| "string"
|
|
38
|
-
| "email"
|
|
39
|
-
| "password"
|
|
40
|
-
| "otp";
|
|
36
|
+
export type CredentialsAuthFieldType = "string" | "email" | "password" | "otp";
|
|
41
37
|
|
|
42
38
|
export interface CredentialsAuthField {
|
|
43
39
|
type?: CredentialsAuthFieldType;
|
|
@@ -138,10 +134,9 @@ function abortData(options: {
|
|
|
138
134
|
};
|
|
139
135
|
}
|
|
140
136
|
|
|
141
|
-
export function createAuthFlowHelpers(
|
|
142
|
-
readonly signal?: AbortSignal;
|
|
143
|
-
|
|
144
|
-
} = {}): AuthFlowTerminalContext {
|
|
137
|
+
export function createAuthFlowHelpers(
|
|
138
|
+
options: { readonly signal?: AbortSignal; readonly deadline?: string } = {},
|
|
139
|
+
): AuthFlowTerminalContext {
|
|
145
140
|
return {
|
|
146
141
|
...(options.signal ? { signal: options.signal } : {}),
|
|
147
142
|
...(options.deadline ? { deadline: options.deadline } : {}),
|
|
@@ -156,16 +151,7 @@ export function createAuthFlowHelpers(options: {
|
|
|
156
151
|
},
|
|
157
152
|
};
|
|
158
153
|
},
|
|
159
|
-
abort({
|
|
160
|
-
code,
|
|
161
|
-
message,
|
|
162
|
-
retry,
|
|
163
|
-
actionHint,
|
|
164
|
-
fieldErrors,
|
|
165
|
-
data,
|
|
166
|
-
turnId,
|
|
167
|
-
expiresAt,
|
|
168
|
-
}) {
|
|
154
|
+
abort({ code, message, retry, actionHint, fieldErrors, data, turnId, expiresAt }) {
|
|
169
155
|
return {
|
|
170
156
|
kind: "abort",
|
|
171
157
|
...authTurnBase({ turnId, defaultTurnId: DEFAULT_ABORT_TURN_ID, expiresAt }),
|
|
@@ -190,8 +176,7 @@ export function createAuthFlowHelpers(options: {
|
|
|
190
176
|
...(options.hintKey ? { hintKey: options.hintKey } : {}),
|
|
191
177
|
...(options.timing ? { timing: options.timing } : {}),
|
|
192
178
|
...(options.data ? { data: options.data } : {}),
|
|
193
|
-
expectedInput:
|
|
194
|
-
options.expectedInput ?? expectedInputFromFields(options.fields ?? {}),
|
|
179
|
+
expectedInput: options.expectedInput ?? expectedInputFromFields(options.fields ?? {}),
|
|
195
180
|
};
|
|
196
181
|
},
|
|
197
182
|
nextPoll(options = {}) {
|
|
@@ -223,9 +208,7 @@ export class AuthAbortError extends AuthError {
|
|
|
223
208
|
}
|
|
224
209
|
}
|
|
225
210
|
|
|
226
|
-
export interface CredentialsAuthCompleteResult<
|
|
227
|
-
TCredentialKeys extends readonly string[],
|
|
228
|
-
> {
|
|
211
|
+
export interface CredentialsAuthCompleteResult<TCredentialKeys extends readonly string[]> {
|
|
229
212
|
credential: CredentialsAuthCredential<TCredentialKeys>;
|
|
230
213
|
/** Additional non-credential auth-flow data to return alongside credential. */
|
|
231
214
|
data?: Record<string, unknown>;
|
|
@@ -247,9 +230,7 @@ export interface CredentialsAuthChallengeRequest<TChallengeId extends string = s
|
|
|
247
230
|
export type CredentialsAuthLoginResult<
|
|
248
231
|
TCredentialKeys extends readonly string[],
|
|
249
232
|
TChallengeId extends string = string,
|
|
250
|
-
> =
|
|
251
|
-
| CredentialsAuthCompleteResult<TCredentialKeys>
|
|
252
|
-
| CredentialsAuthChallengeRequest<TChallengeId>;
|
|
233
|
+
> = CredentialsAuthCompleteResult<TCredentialKeys> | CredentialsAuthChallengeRequest<TChallengeId>;
|
|
253
234
|
|
|
254
235
|
export interface CredentialsAuthChallengeDefinition<
|
|
255
236
|
TFields extends CredentialsAuthFields,
|
|
@@ -351,12 +332,8 @@ function expectedInputFromFields(fields: CredentialsAuthFields): Record<string,
|
|
|
351
332
|
...(field.type === "password" ? { format: "password" } : {}),
|
|
352
333
|
...(field.type === "otp" ? { format: "otp" } : {}),
|
|
353
334
|
...(field.labelKey ? { nameKey: field.labelKey } : {}),
|
|
354
|
-
...(field.descriptionKey
|
|
355
|
-
|
|
356
|
-
: {}),
|
|
357
|
-
...(field.placeholderKey
|
|
358
|
-
? { placeholderKey: field.placeholderKey }
|
|
359
|
-
: {}),
|
|
335
|
+
...(field.descriptionKey ? { descriptionKey: field.descriptionKey } : {}),
|
|
336
|
+
...(field.placeholderKey ? { placeholderKey: field.placeholderKey } : {}),
|
|
360
337
|
...(field.sensitive || field.type === "password" || field.type === "otp"
|
|
361
338
|
? { sensitive: true }
|
|
362
339
|
: {}),
|
|
@@ -437,10 +414,7 @@ function getPendingChallenge(ctx: FlowContext): PendingCredentialsAuthChallenge
|
|
|
437
414
|
};
|
|
438
415
|
}
|
|
439
416
|
|
|
440
|
-
function setPendingChallenge(
|
|
441
|
-
ctx: FlowContext,
|
|
442
|
-
challenge: PendingCredentialsAuthChallenge,
|
|
443
|
-
): void {
|
|
417
|
+
function setPendingChallenge(ctx: FlowContext, challenge: PendingCredentialsAuthChallenge): void {
|
|
444
418
|
ctx.context.set(CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY, challenge);
|
|
445
419
|
}
|
|
446
420
|
|
|
@@ -473,9 +447,7 @@ function retryTurn(
|
|
|
473
447
|
turnId: retryTurnId,
|
|
474
448
|
expectedInput,
|
|
475
449
|
data: {
|
|
476
|
-
fieldErrors: Object.fromEntries(
|
|
477
|
-
missing.map((name) => [name, "Required"]),
|
|
478
|
-
),
|
|
450
|
+
fieldErrors: Object.fromEntries(missing.map((name) => [name, "Required"])),
|
|
479
451
|
fieldErrorKeys: Object.fromEntries(
|
|
480
452
|
missing.map((name) => [name, "auth.credentials.fieldRequired"]),
|
|
481
453
|
),
|
|
@@ -499,18 +471,12 @@ function completeTurn<TCredentialKeys extends readonly string[]>(
|
|
|
499
471
|
typeof result.credential !== "object" ||
|
|
500
472
|
Array.isArray(result.credential)
|
|
501
473
|
) {
|
|
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
|
-
);
|
|
474
|
+
throw new ProviderError("Credentials auth login completed without a credential object", {
|
|
475
|
+
code: "credentials_auth_missing_credential",
|
|
476
|
+
fix: "Return { credential: { ... } } from defineCredentialsAuth handlers. Gateway persists only auth.flow complete data.credential into the connection.",
|
|
477
|
+
});
|
|
509
478
|
}
|
|
510
|
-
assertCredentialKeys(
|
|
511
|
-
credentialKeys,
|
|
512
|
-
result.credential as Record<string, unknown>,
|
|
513
|
-
);
|
|
479
|
+
assertCredentialKeys(credentialKeys, result.credential as Record<string, unknown>);
|
|
514
480
|
return {
|
|
515
481
|
kind: "complete",
|
|
516
482
|
turnId: result.turnId ?? defaultTurnId,
|
|
@@ -523,24 +489,18 @@ function completeTurn<TCredentialKeys extends readonly string[]>(
|
|
|
523
489
|
}
|
|
524
490
|
|
|
525
491
|
function challengeTurn(
|
|
526
|
-
definition: CredentialsAuthChallengeDefinition<
|
|
527
|
-
CredentialsAuthFields,
|
|
528
|
-
readonly string[],
|
|
529
|
-
string
|
|
530
|
-
>,
|
|
492
|
+
definition: CredentialsAuthChallengeDefinition<CredentialsAuthFields, readonly string[], string>,
|
|
531
493
|
request: CredentialsAuthChallengeRequest<string>,
|
|
532
494
|
): AuthTurn {
|
|
533
|
-
const expectedInput = definition.fields
|
|
534
|
-
? expectedInputFromFields(definition.fields)
|
|
535
|
-
: undefined;
|
|
495
|
+
const expectedInput = definition.fields ? expectedInputFromFields(definition.fields) : undefined;
|
|
536
496
|
return {
|
|
537
497
|
kind: expectedInput ? "form" : "pending",
|
|
538
498
|
turnId: request.turnId ?? definition.turnId ?? `credentials.${request.challengeId}`,
|
|
539
499
|
...(request.expiresAt ? { expiresAt: request.expiresAt } : {}),
|
|
540
|
-
...(request.hintKey ?? definition.hintKey
|
|
500
|
+
...((request.hintKey ?? definition.hintKey)
|
|
541
501
|
? { hintKey: request.hintKey ?? definition.hintKey }
|
|
542
502
|
: {}),
|
|
543
|
-
...(request.timing ?? definition.timing
|
|
503
|
+
...((request.timing ?? definition.timing)
|
|
544
504
|
? { timing: request.timing ?? definition.timing }
|
|
545
505
|
: {}),
|
|
546
506
|
...(expectedInput ? { expectedInput } : {}),
|
|
@@ -630,13 +590,7 @@ async function continuePendingChallenge<TCredentialKeys extends readonly string[
|
|
|
630
590
|
normalizeInput(definition.fields, rawInput),
|
|
631
591
|
pending.state,
|
|
632
592
|
);
|
|
633
|
-
return await resolveAuthResult(
|
|
634
|
-
ctx,
|
|
635
|
-
credentialKeys,
|
|
636
|
-
challenges,
|
|
637
|
-
result,
|
|
638
|
-
completeTurnId,
|
|
639
|
-
);
|
|
593
|
+
return await resolveAuthResult(ctx, credentialKeys, challenges, result, completeTurnId);
|
|
640
594
|
}
|
|
641
595
|
|
|
642
596
|
async function pollPendingChallenge<TCredentialKeys extends readonly string[]>(
|
|
@@ -669,13 +623,7 @@ async function pollPendingChallenge<TCredentialKeys extends readonly string[]>(
|
|
|
669
623
|
`credentials.${pending.challengeId}.pending`,
|
|
670
624
|
};
|
|
671
625
|
}
|
|
672
|
-
return await resolveAuthResult(
|
|
673
|
-
ctx,
|
|
674
|
-
credentialKeys,
|
|
675
|
-
challenges,
|
|
676
|
-
result,
|
|
677
|
-
completeTurnId,
|
|
678
|
-
);
|
|
626
|
+
return await resolveAuthResult(ctx, credentialKeys, challenges, result, completeTurnId);
|
|
679
627
|
}
|
|
680
628
|
|
|
681
629
|
export function defineCredentialsAuth<
|
|
@@ -697,7 +645,7 @@ export function defineCredentialsAuth<
|
|
|
697
645
|
): DefinedCredentialsAuth {
|
|
698
646
|
if (Object.keys(options.fields).length === 0) {
|
|
699
647
|
throw new ProviderError("defineCredentialsAuth requires at least one field", {
|
|
700
|
-
fix:
|
|
648
|
+
fix: 'Pass fields such as { email: { type: "email" }, password: { type: "password" } }.',
|
|
701
649
|
});
|
|
702
650
|
}
|
|
703
651
|
|
|
@@ -737,10 +685,7 @@ export function defineCredentialsAuth<
|
|
|
737
685
|
return retryTurn(expectedInput, missing, retryTurnId);
|
|
738
686
|
}
|
|
739
687
|
|
|
740
|
-
const result = await options.login(
|
|
741
|
-
ctx,
|
|
742
|
-
normalizeInput(options.fields, rawInput),
|
|
743
|
-
);
|
|
688
|
+
const result = await options.login(ctx, normalizeInput(options.fields, rawInput));
|
|
744
689
|
return await resolveAuthResult(
|
|
745
690
|
ctx,
|
|
746
691
|
options.credentialKeys,
|
|
@@ -776,10 +721,7 @@ export function defineCredentialsAuth<
|
|
|
776
721
|
},
|
|
777
722
|
context: {
|
|
778
723
|
keys: Array.from(
|
|
779
|
-
new Set([
|
|
780
|
-
CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY,
|
|
781
|
-
...(options.contextKeys ?? []),
|
|
782
|
-
]),
|
|
724
|
+
new Set([CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY, ...(options.contextKeys ?? [])]),
|
|
783
725
|
),
|
|
784
726
|
},
|
|
785
727
|
};
|