@apifuse/provider-sdk 2.2.0-beta.37 → 2.2.0-beta.39
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 +8 -0
- package/dist/declaration-validation.d.ts +9 -0
- package/dist/declaration-validation.js +51 -3
- package/dist/define.d.ts +2 -2
- package/dist/define.js +37 -22
- package/dist/health-scenario.d.ts +1842 -0
- package/dist/health-scenario.js +624 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -0
- package/dist/provider.d.ts +3 -0
- package/dist/provider.js +1 -0
- package/dist/runtime/executor.d.ts +2 -2
- package/dist/runtime/executor.js +3 -1
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/proxy-telemetry.d.ts +41 -1
- package/dist/runtime/proxy-telemetry.js +59 -56
- package/dist/server/index.d.ts +2 -0
- package/dist/server/serve-implementation.d.ts +3 -1
- package/dist/server/serve-implementation.js +81 -30
- package/dist/server/types.d.ts +4 -4
- package/dist/types.d.ts +10 -8
- package/package.json +1 -1
- package/src/declaration-validation.ts +71 -7
- package/src/define.ts +61 -38
- package/src/health-scenario.ts +875 -0
- package/src/index.ts +78 -0
- package/src/provider.ts +75 -0
- package/src/runtime/executor.ts +11 -6
- package/src/runtime/instrumentation.ts +5 -2
- package/src/runtime/proxy-telemetry.ts +100 -99
- package/src/server/index.ts +8 -0
- package/src/server/serve-implementation.ts +90 -8
- package/src/types.ts +11 -8
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describeSchema } from "./contract-serialization.js";
|
|
2
2
|
import { ProviderError } from "./errors.js";
|
|
3
|
+
import { HealthScenarioSchema } from "./health-scenario.js";
|
|
3
4
|
import type {
|
|
4
5
|
HealthJourneyDefinition,
|
|
5
6
|
ProviderDefinition,
|
|
@@ -12,6 +13,8 @@ export const DECLARATION_INVALID_CODE = "DECLARATION_INVALID";
|
|
|
12
13
|
export const DECLARATION_RULE_IDS = {
|
|
13
14
|
challengeShape: "credentials-challenge-shape",
|
|
14
15
|
journeyExecutable: "health-journey-executable",
|
|
16
|
+
journeyRunScenarioExclusive: "health-journey-run-scenario-exclusive",
|
|
17
|
+
journeyScenarioValid: "health-journey-scenario-valid",
|
|
15
18
|
schemaSerializable: "operation-schema-serializable",
|
|
16
19
|
proxyExplicitPolicy: "proxy-explicit-policy",
|
|
17
20
|
proxyVendorExclusive: "proxy-vendor-fields-exclusive",
|
|
@@ -56,21 +59,82 @@ export function validateFailClosedDeclaration(provider: ProviderDefinition): voi
|
|
|
56
59
|
if (violations.length > 0) throw declarationInvalidError(violations);
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
type ProviderDeclarationRulesInput = Pick<ProviderDefinition, "healthJourneys" | "proxy">;
|
|
63
|
+
type OperationDeclarationRulesInput = Pick<ProviderDefinition, "operations">;
|
|
64
|
+
|
|
65
|
+
/** Enforces fail-closed rules that only depend on the provider declaration. */
|
|
66
|
+
export function validateFailClosedProviderDeclaration(
|
|
67
|
+
provider: ProviderDeclarationRulesInput,
|
|
68
|
+
): void {
|
|
69
|
+
const violations: DeclarationViolation[] = [];
|
|
70
|
+
collectProviderDeclarationViolations(provider, violations);
|
|
71
|
+
if (violations.length > 0) throw declarationInvalidError(violations);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Enforces fail-closed rules that depend on the operation implementation. */
|
|
75
|
+
export function validateFailClosedOperationDeclaration(
|
|
76
|
+
provider: OperationDeclarationRulesInput,
|
|
77
|
+
): void {
|
|
78
|
+
const violations: DeclarationViolation[] = [];
|
|
79
|
+
collectOperationDeclarationViolations(provider, violations);
|
|
80
|
+
if (violations.length > 0) throw declarationInvalidError(violations);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function collectProviderDeclarationViolations(
|
|
84
|
+
provider: ProviderDeclarationRulesInput,
|
|
85
|
+
violations: DeclarationViolation[],
|
|
86
|
+
): void {
|
|
87
|
+
validateHealthDeclaration(provider, violations);
|
|
88
|
+
validateProxyDeclaration(provider, violations);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function collectOperationDeclarationViolations(
|
|
92
|
+
provider: OperationDeclarationRulesInput,
|
|
93
|
+
violations: DeclarationViolation[],
|
|
94
|
+
): void {
|
|
95
|
+
validateSchemaDeclaration(provider, violations);
|
|
96
|
+
validateOperationDeclaration(provider, violations);
|
|
97
|
+
}
|
|
98
|
+
|
|
59
99
|
function validateHealthDeclaration(
|
|
60
|
-
provider:
|
|
100
|
+
provider: ProviderDeclarationRulesInput,
|
|
61
101
|
violations: DeclarationViolation[],
|
|
62
102
|
): void {
|
|
63
103
|
for (const [index, journey] of (provider.healthJourneys ?? []).entries()) {
|
|
64
104
|
if (!journey || typeof journey !== "object") continue;
|
|
65
|
-
|
|
105
|
+
const hasRun = journey.run !== undefined;
|
|
106
|
+
const hasScenario = journey.scenario !== undefined;
|
|
107
|
+
if (hasRun && hasScenario) {
|
|
108
|
+
const journeyPath = healthJourneyPath(journey, index);
|
|
109
|
+
violations.push({
|
|
110
|
+
ruleId: DECLARATION_RULE_IDS.journeyRunScenarioExclusive,
|
|
111
|
+
path: journeyPath,
|
|
112
|
+
message: "A health journey must declare exactly one of run or scenario, not both.",
|
|
113
|
+
fix: `Remove either ${journeyPath}.run or ${journeyPath}.scenario.`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (!hasRun && !hasScenario) {
|
|
66
117
|
const journeyPath = healthJourneyPath(journey, index);
|
|
67
118
|
violations.push({
|
|
68
119
|
ruleId: DECLARATION_RULE_IDS.journeyExecutable,
|
|
69
120
|
path: `${journeyPath}.run`,
|
|
70
|
-
message:
|
|
71
|
-
|
|
121
|
+
message:
|
|
122
|
+
"coversOperations cannot provide health coverage without run or a declarative scenario.",
|
|
123
|
+
fix: `Add an async run(ctx) implementation or a valid scenario to ${journeyPath}.`,
|
|
72
124
|
});
|
|
73
125
|
}
|
|
126
|
+
if (hasScenario) {
|
|
127
|
+
const parsed = HealthScenarioSchema.safeParse(journey.scenario);
|
|
128
|
+
if (!parsed.success) {
|
|
129
|
+
const journeyPath = healthJourneyPath(journey, index);
|
|
130
|
+
violations.push({
|
|
131
|
+
ruleId: DECLARATION_RULE_IDS.journeyScenarioValid,
|
|
132
|
+
path: `${journeyPath}.scenario`,
|
|
133
|
+
message: "scenario must conform to HealthScenario.",
|
|
134
|
+
fix: "Build the scenario with defineHealthScenario().",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
74
138
|
}
|
|
75
139
|
|
|
76
140
|
// NOTE: healthCheck.cases[].enabled is intentionally NOT validated here.
|
|
@@ -86,7 +150,7 @@ function healthJourneyPath(journey: HealthJourneyDefinition, index: number): str
|
|
|
86
150
|
}
|
|
87
151
|
|
|
88
152
|
function validateSchemaDeclaration(
|
|
89
|
-
provider:
|
|
153
|
+
provider: OperationDeclarationRulesInput,
|
|
90
154
|
violations: DeclarationViolation[],
|
|
91
155
|
): void {
|
|
92
156
|
for (const [operationId, operation] of Object.entries(provider.operations ?? {})) {
|
|
@@ -126,7 +190,7 @@ const MANAGED_PROXY_VENDORS = new Set<ProviderProxyProvider>(["smartproxy", "nod
|
|
|
126
190
|
const STATIC_PROXY_VENDORS = new Set<ProviderProxyProvider>(["custom", "decodo"]);
|
|
127
191
|
|
|
128
192
|
function validateProxyDeclaration(
|
|
129
|
-
provider:
|
|
193
|
+
provider: ProviderDeclarationRulesInput,
|
|
130
194
|
violations: DeclarationViolation[],
|
|
131
195
|
): void {
|
|
132
196
|
if (provider.proxy === true) {
|
|
@@ -186,7 +250,7 @@ function declaredProxyVendors(policy: ProviderProxyPolicy): ProviderProxyProvide
|
|
|
186
250
|
}
|
|
187
251
|
|
|
188
252
|
function validateOperationDeclaration(
|
|
189
|
-
provider:
|
|
253
|
+
provider: OperationDeclarationRulesInput,
|
|
190
254
|
violations: DeclarationViolation[],
|
|
191
255
|
): void {
|
|
192
256
|
for (const [operationId, operation] of Object.entries(provider.operations ?? {})) {
|
package/src/define.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import ms from "ms";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
validateFailClosedOperationDeclaration,
|
|
5
|
+
validateFailClosedProviderDeclaration,
|
|
6
|
+
} from "./declaration-validation.js";
|
|
4
7
|
import { SDK_RUNTIME_OWNED_ERROR_CODES } from "./error-resolution.js";
|
|
5
8
|
import { ProviderError, ValidationError } from "./errors.js";
|
|
6
9
|
import {
|
|
@@ -30,12 +33,12 @@ import type {
|
|
|
30
33
|
OperationWebSocketTransport,
|
|
31
34
|
ProviderAccessConfig,
|
|
32
35
|
ProviderChallengeKind,
|
|
33
|
-
ProviderDefinition,
|
|
34
36
|
ProviderContext,
|
|
35
37
|
ProviderContextFor,
|
|
36
|
-
|
|
38
|
+
ProviderDefinition,
|
|
37
39
|
ProviderDeploymentOverrides,
|
|
38
40
|
ProviderHealthMonitorConfig,
|
|
41
|
+
ProviderOcrConfig,
|
|
39
42
|
ProviderProxyConfig,
|
|
40
43
|
ProviderProxyProvider,
|
|
41
44
|
ProviderPublicProfile,
|
|
@@ -806,13 +809,12 @@ function validateProxiedOAuthAuth(auth: Record<string, unknown>, providerId: str
|
|
|
806
809
|
validateProxiedOAuthParams(config.tokenParams, "tokenParams", providerId);
|
|
807
810
|
}
|
|
808
811
|
|
|
809
|
-
function
|
|
812
|
+
function validateProviderDeclarationShape(config: unknown): void {
|
|
810
813
|
assertObjectConfig(config);
|
|
811
814
|
assertRequiredField(config, "id");
|
|
812
815
|
assertRequiredField(config, "version", String(config.id));
|
|
813
816
|
assertRequiredField(config, "runtime", String(config.id));
|
|
814
817
|
assertRequiredField(config, "meta", String(config.id));
|
|
815
|
-
assertRequiredField(config, "operations", String(config.id));
|
|
816
818
|
if (typeof config.runtime === "string")
|
|
817
819
|
assertLiteralField(config.runtime, "runtime", VALID_RUNTIMES, String(config.id));
|
|
818
820
|
if (config.native !== undefined && config.runtime === "browser") {
|
|
@@ -910,6 +912,11 @@ function validateProviderShape(config: unknown): void {
|
|
|
910
912
|
}
|
|
911
913
|
}
|
|
912
914
|
|
|
915
|
+
function validateProviderImplementationShape(config: { id: string }): void {
|
|
916
|
+
const configRecord = config as unknown as Record<string, unknown>;
|
|
917
|
+
assertRequiredField(configRecord, "operations", String(config.id));
|
|
918
|
+
}
|
|
919
|
+
|
|
913
920
|
function validateProviderProxy(config: {
|
|
914
921
|
id: string;
|
|
915
922
|
proxy?: ProviderProxyConfig;
|
|
@@ -2132,6 +2139,7 @@ const HEALTH_JOURNEY_FIELDS = new Set([
|
|
|
2132
2139
|
"manualTrigger",
|
|
2133
2140
|
"steps",
|
|
2134
2141
|
"run",
|
|
2142
|
+
"scenario",
|
|
2135
2143
|
]);
|
|
2136
2144
|
const HEALTH_JOURNEY_SCHEDULE_FIELDS = new Set(["kind", "interval", "jitter", "randomize"]);
|
|
2137
2145
|
const HEALTH_JOURNEY_STEP_FIELDS = new Set([
|
|
@@ -2667,6 +2675,14 @@ function validateHealthJourneys(
|
|
|
2667
2675
|
}
|
|
2668
2676
|
if (journey.manualTrigger !== undefined)
|
|
2669
2677
|
validateHealthJourneyManualTrigger(providerId, journey.id, journey.manualTrigger);
|
|
2678
|
+
if (journey.scenario !== undefined && journey.smsMatchers !== undefined)
|
|
2679
|
+
throw new ValidationError(
|
|
2680
|
+
`Provider "${providerId}" healthJourneys.${journey.id}.smsMatchers is not allowed on declarative scenarios.`,
|
|
2681
|
+
);
|
|
2682
|
+
if (journey.scenario !== undefined && journey.requiredSecrets !== undefined)
|
|
2683
|
+
throw new ValidationError(
|
|
2684
|
+
`Provider "${providerId}" healthJourneys.${journey.id}.requiredSecrets is not allowed on declarative scenarios.`,
|
|
2685
|
+
);
|
|
2670
2686
|
if (journey.timeout !== undefined)
|
|
2671
2687
|
assertIsoDuration(
|
|
2672
2688
|
journey.timeout,
|
|
@@ -2835,7 +2851,7 @@ export type ProviderBuilder<TDeclaration extends ProviderDeclaration> = <
|
|
|
2835
2851
|
implementation: {
|
|
2836
2852
|
operations: OperationMapConfig<TOperations, ProviderContextFor<TDeclaration>>;
|
|
2837
2853
|
},
|
|
2838
|
-
) => ProviderDefinition & {
|
|
2854
|
+
) => Omit<ProviderDefinition, "operations"> & {
|
|
2839
2855
|
operations: OperationMapConfig<TOperations, ProviderContextFor<TDeclaration>>;
|
|
2840
2856
|
};
|
|
2841
2857
|
|
|
@@ -2850,6 +2866,7 @@ export function defineProvider<const TDeclaration extends ProviderDeclaration>(
|
|
|
2850
2866
|
Record<Exclude<keyof TDeclaration, keyof ProviderDeclaration>, never> &
|
|
2851
2867
|
AuthStartNoInputGuard<TDeclaration>,
|
|
2852
2868
|
): ProviderBuilder<TDeclaration> {
|
|
2869
|
+
validateProviderDeclaration(declaration);
|
|
2853
2870
|
const buildProvider = <TOperations extends Record<string, ProviderOperation>>(
|
|
2854
2871
|
implementation: {
|
|
2855
2872
|
operations: OperationMapConfig<TOperations, ProviderContextFor<TDeclaration>>;
|
|
@@ -2862,37 +2879,12 @@ export function defineProvider<const TDeclaration extends ProviderDeclaration>(
|
|
|
2862
2879
|
return buildProvider as ProviderBuilder<TDeclaration>;
|
|
2863
2880
|
}
|
|
2864
2881
|
|
|
2865
|
-
function
|
|
2866
|
-
|
|
2867
|
-
TContext,
|
|
2868
|
-
>(
|
|
2869
|
-
config: ProviderConfig<TOperations, TContext>,
|
|
2870
|
-
): ProviderDefinition & {
|
|
2871
|
-
operations: OperationMapConfig<TOperations, TContext>;
|
|
2872
|
-
} {
|
|
2873
|
-
validateProviderShape(config);
|
|
2874
|
-
const operations = resolveOperationFixtureRequests(config.operations);
|
|
2882
|
+
function validateProviderDeclaration(config: ProviderDeclaration): void {
|
|
2883
|
+
validateProviderDeclarationShape(config);
|
|
2875
2884
|
if (!CONNECTOR_ID_REGEX.test(config.id))
|
|
2876
2885
|
throw new ProviderError(`Invalid provider id: "${config.id}"`, {
|
|
2877
2886
|
fix: 'Use lowercase alphanumeric with dashes, e.g., "korea-air-quality"',
|
|
2878
2887
|
});
|
|
2879
|
-
if (Object.keys(config.operations).length === 0)
|
|
2880
|
-
throw new ProviderError(`Provider "${config.id}" must define at least one operation`, {
|
|
2881
|
-
fix: "Add at least one operation to the operations object",
|
|
2882
|
-
});
|
|
2883
|
-
validateOperationIds(config.id, config.operations);
|
|
2884
|
-
validateOperationAnnotations(config.id, config.operations);
|
|
2885
|
-
validateOperationObservability(config.id, config.operations);
|
|
2886
|
-
validateOperationErrorCodes(config.id, config.operations);
|
|
2887
|
-
validateOperationTransports(config.id, config.operations);
|
|
2888
|
-
validateOperationContracts(config.id, config.operations);
|
|
2889
|
-
validateToolRouterMetadata(config.id, config.operations);
|
|
2890
|
-
const journeyCoveredOperations = validateHealthJourneys(
|
|
2891
|
-
config.id,
|
|
2892
|
-
config.operations,
|
|
2893
|
-
config.healthJourneys,
|
|
2894
|
-
);
|
|
2895
|
-
validateOperationHealthChecks(config.id, config.operations, journeyCoveredOperations);
|
|
2896
2888
|
if (config.healthMonitor !== undefined && config.healthProbe !== undefined)
|
|
2897
2889
|
throw new ValidationError(
|
|
2898
2890
|
`Provider "${config.id}" declares both healthMonitor and healthProbe. They are aliases; declare exactly one.`,
|
|
@@ -2905,7 +2897,6 @@ function finalizeProvider<
|
|
|
2905
2897
|
config.healthProbe ?? config.healthMonitor,
|
|
2906
2898
|
config.healthProbe !== undefined ? "healthProbe" : "healthMonitor",
|
|
2907
2899
|
);
|
|
2908
|
-
validateOperationFixtures(config.id, operations);
|
|
2909
2900
|
validateProviderDeployment(config.id, config.deployment);
|
|
2910
2901
|
try {
|
|
2911
2902
|
validateNativeProviderConfig(config.native);
|
|
@@ -2930,7 +2921,38 @@ function finalizeProvider<
|
|
|
2930
2921
|
`Provider "${config.id}" cannot define browser config unless runtime is "browser"`,
|
|
2931
2922
|
{ fix: 'Set runtime: "browser" or remove the browser config' },
|
|
2932
2923
|
);
|
|
2933
|
-
|
|
2924
|
+
validateFailClosedProviderDeclaration(config);
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
function finalizeProvider<
|
|
2928
|
+
TOperations extends Record<string, ProviderOperation>,
|
|
2929
|
+
TContext,
|
|
2930
|
+
>(
|
|
2931
|
+
config: ProviderConfig<TOperations, TContext>,
|
|
2932
|
+
): Omit<ProviderDefinition, "operations"> & {
|
|
2933
|
+
operations: OperationMapConfig<TOperations, TContext>;
|
|
2934
|
+
} {
|
|
2935
|
+
validateProviderImplementationShape(config);
|
|
2936
|
+
const operations = resolveOperationFixtureRequests(config.operations);
|
|
2937
|
+
if (Object.keys(config.operations).length === 0)
|
|
2938
|
+
throw new ProviderError(`Provider "${config.id}" must define at least one operation`, {
|
|
2939
|
+
fix: "Add at least one operation to the operations object",
|
|
2940
|
+
});
|
|
2941
|
+
validateOperationIds(config.id, config.operations);
|
|
2942
|
+
validateOperationAnnotations(config.id, config.operations);
|
|
2943
|
+
validateOperationObservability(config.id, config.operations);
|
|
2944
|
+
validateOperationErrorCodes(config.id, config.operations);
|
|
2945
|
+
validateOperationTransports(config.id, config.operations);
|
|
2946
|
+
validateOperationContracts(config.id, config.operations);
|
|
2947
|
+
validateToolRouterMetadata(config.id, config.operations);
|
|
2948
|
+
const journeyCoveredOperations = validateHealthJourneys(
|
|
2949
|
+
config.id,
|
|
2950
|
+
config.operations,
|
|
2951
|
+
config.healthJourneys,
|
|
2952
|
+
);
|
|
2953
|
+
validateOperationHealthChecks(config.id, config.operations, journeyCoveredOperations);
|
|
2954
|
+
validateOperationFixtures(config.id, operations);
|
|
2955
|
+
const provider: Omit<ProviderDefinition, "operations"> & {
|
|
2934
2956
|
operations: OperationMapConfig<TOperations, TContext>;
|
|
2935
2957
|
} = {
|
|
2936
2958
|
id: config.id,
|
|
@@ -2954,14 +2976,15 @@ function finalizeProvider<
|
|
|
2954
2976
|
credential: config.credential,
|
|
2955
2977
|
context: config.context,
|
|
2956
2978
|
meta: config.meta,
|
|
2957
|
-
operations
|
|
2958
|
-
OperationMapConfig<TOperations, TContext>,
|
|
2979
|
+
operations,
|
|
2959
2980
|
// Transitional healthMonitor → healthProbe alias: mirror whichever field
|
|
2960
2981
|
// was declared onto both so old and new consumers keep working.
|
|
2961
2982
|
healthMonitor: config.healthMonitor ?? config.healthProbe,
|
|
2962
2983
|
healthProbe: config.healthProbe ?? config.healthMonitor,
|
|
2963
2984
|
healthJourneys: config.healthJourneys,
|
|
2964
2985
|
};
|
|
2965
|
-
|
|
2986
|
+
// Declaration validation never invokes handlers, so their declaration-bound
|
|
2987
|
+
// context parameter is irrelevant to the runtime ProviderDefinition shape.
|
|
2988
|
+
validateFailClosedOperationDeclaration(provider as unknown as ProviderDefinition);
|
|
2966
2989
|
return provider;
|
|
2967
2990
|
}
|