@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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @apifuse/provider-sdk Changelog
2
2
 
3
+ ## 2.2.0-beta.39
4
+
5
+ - Release candidate for main commit ecbe349c44e15c873b7c58b42e14610db029eb5f.
6
+
7
+ ## 2.2.0-beta.38
8
+
9
+ - Release candidate for main commit 448775077dbc9ab907209f1feee4e6e4e5ef4283.
10
+
3
11
  ## 2.2.0-beta.37
4
12
 
5
13
  - Release candidate for main commit aa14b268dffe1196f25c2b6b314598fe0896edec.
@@ -4,6 +4,8 @@ export declare const DECLARATION_INVALID_CODE = "DECLARATION_INVALID";
4
4
  export declare const DECLARATION_RULE_IDS: {
5
5
  readonly challengeShape: "credentials-challenge-shape";
6
6
  readonly journeyExecutable: "health-journey-executable";
7
+ readonly journeyRunScenarioExclusive: "health-journey-run-scenario-exclusive";
8
+ readonly journeyScenarioValid: "health-journey-scenario-valid";
7
9
  readonly schemaSerializable: "operation-schema-serializable";
8
10
  readonly proxyExplicitPolicy: "proxy-explicit-policy";
9
11
  readonly proxyVendorExclusive: "proxy-vendor-fields-exclusive";
@@ -21,3 +23,10 @@ export type DeclarationViolation = {
21
23
  export declare function declarationInvalidError(violations: readonly DeclarationViolation[]): ProviderError;
22
24
  /** Enforces declaration rules whose runtime behavior would otherwise fail open. */
23
25
  export declare function validateFailClosedDeclaration(provider: ProviderDefinition): void;
26
+ type ProviderDeclarationRulesInput = Pick<ProviderDefinition, "healthJourneys" | "proxy">;
27
+ type OperationDeclarationRulesInput = Pick<ProviderDefinition, "operations">;
28
+ /** Enforces fail-closed rules that only depend on the provider declaration. */
29
+ export declare function validateFailClosedProviderDeclaration(provider: ProviderDeclarationRulesInput): void;
30
+ /** Enforces fail-closed rules that depend on the operation implementation. */
31
+ export declare function validateFailClosedOperationDeclaration(provider: OperationDeclarationRulesInput): void;
32
+ export {};
@@ -1,9 +1,12 @@
1
1
  import { describeSchema } from "./contract-serialization.js";
2
2
  import { ProviderError } from "./errors.js";
3
+ import { HealthScenarioSchema } from "./health-scenario.js";
3
4
  export const DECLARATION_INVALID_CODE = "DECLARATION_INVALID";
4
5
  export const DECLARATION_RULE_IDS = {
5
6
  challengeShape: "credentials-challenge-shape",
6
7
  journeyExecutable: "health-journey-executable",
8
+ journeyRunScenarioExclusive: "health-journey-run-scenario-exclusive",
9
+ journeyScenarioValid: "health-journey-scenario-valid",
7
10
  schemaSerializable: "operation-schema-serializable",
8
11
  proxyExplicitPolicy: "proxy-explicit-policy",
9
12
  proxyVendorExclusive: "proxy-vendor-fields-exclusive",
@@ -31,19 +34,64 @@ export function validateFailClosedDeclaration(provider) {
31
34
  if (violations.length > 0)
32
35
  throw declarationInvalidError(violations);
33
36
  }
37
+ /** Enforces fail-closed rules that only depend on the provider declaration. */
38
+ export function validateFailClosedProviderDeclaration(provider) {
39
+ const violations = [];
40
+ collectProviderDeclarationViolations(provider, violations);
41
+ if (violations.length > 0)
42
+ throw declarationInvalidError(violations);
43
+ }
44
+ /** Enforces fail-closed rules that depend on the operation implementation. */
45
+ export function validateFailClosedOperationDeclaration(provider) {
46
+ const violations = [];
47
+ collectOperationDeclarationViolations(provider, violations);
48
+ if (violations.length > 0)
49
+ throw declarationInvalidError(violations);
50
+ }
51
+ function collectProviderDeclarationViolations(provider, violations) {
52
+ validateHealthDeclaration(provider, violations);
53
+ validateProxyDeclaration(provider, violations);
54
+ }
55
+ function collectOperationDeclarationViolations(provider, violations) {
56
+ validateSchemaDeclaration(provider, violations);
57
+ validateOperationDeclaration(provider, violations);
58
+ }
34
59
  function validateHealthDeclaration(provider, violations) {
35
60
  for (const [index, journey] of (provider.healthJourneys ?? []).entries()) {
36
61
  if (!journey || typeof journey !== "object")
37
62
  continue;
38
- if (typeof journey.run !== "function") {
63
+ const hasRun = journey.run !== undefined;
64
+ const hasScenario = journey.scenario !== undefined;
65
+ if (hasRun && hasScenario) {
66
+ const journeyPath = healthJourneyPath(journey, index);
67
+ violations.push({
68
+ ruleId: DECLARATION_RULE_IDS.journeyRunScenarioExclusive,
69
+ path: journeyPath,
70
+ message: "A health journey must declare exactly one of run or scenario, not both.",
71
+ fix: `Remove either ${journeyPath}.run or ${journeyPath}.scenario.`,
72
+ });
73
+ }
74
+ if (!hasRun && !hasScenario) {
39
75
  const journeyPath = healthJourneyPath(journey, index);
40
76
  violations.push({
41
77
  ruleId: DECLARATION_RULE_IDS.journeyExecutable,
42
78
  path: `${journeyPath}.run`,
43
- message: "coversOperations cannot provide health coverage without executable run logic.",
44
- fix: `Add an async run(ctx) implementation to ${journeyPath}.`,
79
+ message: "coversOperations cannot provide health coverage without run or a declarative scenario.",
80
+ fix: `Add an async run(ctx) implementation or a valid scenario to ${journeyPath}.`,
45
81
  });
46
82
  }
83
+ if (hasScenario) {
84
+ const parsed = HealthScenarioSchema.safeParse(journey.scenario);
85
+ if (!parsed.success) {
86
+ const journeyPath = healthJourneyPath(journey, index);
87
+ violations.push({
88
+ ruleId: DECLARATION_RULE_IDS.journeyScenarioValid,
89
+ path: `${journeyPath}.scenario`,
90
+ message: "scenario must conform to HealthScenario.",
91
+ fix: "Build the scenario with defineHealthScenario().",
92
+ });
93
+ }
94
+ }
47
95
  }
48
96
  // NOTE: healthCheck.cases[].enabled is intentionally NOT validated here.
49
97
  // self-test.ts reports a gated case as status "skipped" with skipReason
package/dist/define.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AuthConfig, BrowserEngine, ContextDeclaration, CredentialDeclaration, HealthJourneyDefinition, HealthJourneySchedule, HealthScheduleRandomization, InferSchemaOutput, NativeProviderConfig, OperationDefinition, OperationHandlerResult, OperationHttpStreamTransport, OperationSseTransport, OperationWebSocketTransport, ProviderAccessConfig, ProviderDefinition, ProviderContext, ProviderContextFor, ProviderOcrConfig, ProviderDeploymentOverrides, ProviderHealthMonitorConfig, ProviderProxyConfig, ProviderPublicProfile, ProviderResolverConfig, ProviderReviewed, ProviderSecretDeclaration, ProviderStreamEvent, ProviderSttConfig, SchemaLike, SmsOtpMatcherDefinition, StealthPlatform } from "./types.js";
1
+ import type { AuthConfig, BrowserEngine, ContextDeclaration, CredentialDeclaration, HealthJourneyDefinition, HealthJourneySchedule, HealthScheduleRandomization, InferSchemaOutput, NativeProviderConfig, OperationDefinition, OperationHandlerResult, OperationHttpStreamTransport, OperationSseTransport, OperationWebSocketTransport, ProviderAccessConfig, ProviderContext, ProviderContextFor, ProviderDefinition, ProviderDeploymentOverrides, ProviderHealthMonitorConfig, ProviderOcrConfig, ProviderProxyConfig, ProviderPublicProfile, ProviderResolverConfig, ProviderReviewed, ProviderSecretDeclaration, ProviderStreamEvent, ProviderSttConfig, SchemaLike, SmsOtpMatcherDefinition, StealthPlatform } from "./types.js";
2
2
  type ProviderImplementationSourceAccess = "official_api" | "private_api" | "browser_flow" | "hybrid";
3
3
  type ProviderImplementationCredentialStrategy = "apifuse_managed" | "workspace_secret" | "user_oauth" | "user_session" | "none";
4
4
  interface ProviderImplementationProfile {
@@ -125,7 +125,7 @@ export declare function defineHealthJourney(config: HealthJourneyDefinition): He
125
125
  /** The second authoring phase for a declaration established by defineProvider. */
126
126
  export type ProviderBuilder<TDeclaration extends ProviderDeclaration> = <TOperations extends Record<string, ProviderOperation>>(implementation: {
127
127
  operations: OperationMapConfig<TOperations, ProviderContextFor<TDeclaration>>;
128
- }) => ProviderDefinition & {
128
+ }) => Omit<ProviderDefinition, "operations"> & {
129
129
  operations: OperationMapConfig<TOperations, ProviderContextFor<TDeclaration>>;
130
130
  };
131
131
  /** Extract the declaration-derived operation context from a provider builder. */
package/dist/define.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import ms from "ms";
2
- import { validateFailClosedDeclaration } from "./declaration-validation.js";
2
+ import { validateFailClosedOperationDeclaration, validateFailClosedProviderDeclaration, } from "./declaration-validation.js";
3
3
  import { SDK_RUNTIME_OWNED_ERROR_CODES } from "./error-resolution.js";
4
4
  import { ProviderError, ValidationError } from "./errors.js";
5
5
  import { NativeEgressPolicyValidationError, validateNativeProviderConfig, } from "./native-egress-policy.js";
@@ -553,13 +553,12 @@ function validateProxiedOAuthAuth(auth, providerId) {
553
553
  validateProxiedOAuthParams(config.authorizeParams, "authorizeParams", providerId);
554
554
  validateProxiedOAuthParams(config.tokenParams, "tokenParams", providerId);
555
555
  }
556
- function validateProviderShape(config) {
556
+ function validateProviderDeclarationShape(config) {
557
557
  assertObjectConfig(config);
558
558
  assertRequiredField(config, "id");
559
559
  assertRequiredField(config, "version", String(config.id));
560
560
  assertRequiredField(config, "runtime", String(config.id));
561
561
  assertRequiredField(config, "meta", String(config.id));
562
- assertRequiredField(config, "operations", String(config.id));
563
562
  if (typeof config.runtime === "string")
564
563
  assertLiteralField(config.runtime, "runtime", VALID_RUNTIMES, String(config.id));
565
564
  if (config.native !== undefined && config.runtime === "browser") {
@@ -629,6 +628,10 @@ function validateProviderShape(config) {
629
628
  }
630
629
  }
631
630
  }
631
+ function validateProviderImplementationShape(config) {
632
+ const configRecord = config;
633
+ assertRequiredField(configRecord, "operations", String(config.id));
634
+ }
632
635
  function validateProviderProxy(config) {
633
636
  const proxy = config.proxy;
634
637
  if (proxy === undefined || typeof proxy === "boolean") {
@@ -1413,6 +1416,7 @@ const HEALTH_JOURNEY_FIELDS = new Set([
1413
1416
  "manualTrigger",
1414
1417
  "steps",
1415
1418
  "run",
1419
+ "scenario",
1416
1420
  ]);
1417
1421
  const HEALTH_JOURNEY_SCHEDULE_FIELDS = new Set(["kind", "interval", "jitter", "randomize"]);
1418
1422
  const HEALTH_JOURNEY_STEP_FIELDS = new Set([
@@ -1835,6 +1839,10 @@ function validateHealthJourneys(providerId, operations, healthJourneys) {
1835
1839
  }
1836
1840
  if (journey.manualTrigger !== undefined)
1837
1841
  validateHealthJourneyManualTrigger(providerId, journey.id, journey.manualTrigger);
1842
+ if (journey.scenario !== undefined && journey.smsMatchers !== undefined)
1843
+ throw new ValidationError(`Provider "${providerId}" healthJourneys.${journey.id}.smsMatchers is not allowed on declarative scenarios.`);
1844
+ if (journey.scenario !== undefined && journey.requiredSecrets !== undefined)
1845
+ throw new ValidationError(`Provider "${providerId}" healthJourneys.${journey.id}.requiredSecrets is not allowed on declarative scenarios.`);
1838
1846
  if (journey.timeout !== undefined)
1839
1847
  assertIsoDuration(journey.timeout, `Provider "${providerId}" healthJourneys.${journey.id}.timeout`);
1840
1848
  if (journey.cooldown !== undefined)
@@ -1946,38 +1954,24 @@ function validateProviderDeployment(providerId, deployment) {
1946
1954
  }
1947
1955
  /** Establish a provider declaration before its operations are contextually typed. */
1948
1956
  export function defineProvider(declaration) {
1957
+ validateProviderDeclaration(declaration);
1949
1958
  const buildProvider = (implementation) => finalizeProvider({
1950
1959
  ...declaration,
1951
1960
  ...implementation,
1952
1961
  });
1953
1962
  return buildProvider;
1954
1963
  }
1955
- function finalizeProvider(config) {
1956
- validateProviderShape(config);
1957
- const operations = resolveOperationFixtureRequests(config.operations);
1964
+ function validateProviderDeclaration(config) {
1965
+ validateProviderDeclarationShape(config);
1958
1966
  if (!CONNECTOR_ID_REGEX.test(config.id))
1959
1967
  throw new ProviderError(`Invalid provider id: "${config.id}"`, {
1960
1968
  fix: 'Use lowercase alphanumeric with dashes, e.g., "korea-air-quality"',
1961
1969
  });
1962
- if (Object.keys(config.operations).length === 0)
1963
- throw new ProviderError(`Provider "${config.id}" must define at least one operation`, {
1964
- fix: "Add at least one operation to the operations object",
1965
- });
1966
- validateOperationIds(config.id, config.operations);
1967
- validateOperationAnnotations(config.id, config.operations);
1968
- validateOperationObservability(config.id, config.operations);
1969
- validateOperationErrorCodes(config.id, config.operations);
1970
- validateOperationTransports(config.id, config.operations);
1971
- validateOperationContracts(config.id, config.operations);
1972
- validateToolRouterMetadata(config.id, config.operations);
1973
- const journeyCoveredOperations = validateHealthJourneys(config.id, config.operations, config.healthJourneys);
1974
- validateOperationHealthChecks(config.id, config.operations, journeyCoveredOperations);
1975
1970
  if (config.healthMonitor !== undefined && config.healthProbe !== undefined)
1976
1971
  throw new ValidationError(`Provider "${config.id}" declares both healthMonitor and healthProbe. They are aliases; declare exactly one.`, {
1977
1972
  fix: "Keep healthProbe (the new name) and delete the healthMonitor block.",
1978
1973
  });
1979
1974
  validateProviderHealthMonitor(config.id, config.healthProbe ?? config.healthMonitor, config.healthProbe !== undefined ? "healthProbe" : "healthMonitor");
1980
- validateOperationFixtures(config.id, operations);
1981
1975
  validateProviderDeployment(config.id, config.deployment);
1982
1976
  try {
1983
1977
  validateNativeProviderConfig(config.native);
@@ -1997,6 +1991,25 @@ function finalizeProvider(config) {
1997
1991
  });
1998
1992
  if (config.browser && config.runtime !== "browser")
1999
1993
  throw new ProviderError(`Provider "${config.id}" cannot define browser config unless runtime is "browser"`, { fix: 'Set runtime: "browser" or remove the browser config' });
1994
+ validateFailClosedProviderDeclaration(config);
1995
+ }
1996
+ function finalizeProvider(config) {
1997
+ validateProviderImplementationShape(config);
1998
+ const operations = resolveOperationFixtureRequests(config.operations);
1999
+ if (Object.keys(config.operations).length === 0)
2000
+ throw new ProviderError(`Provider "${config.id}" must define at least one operation`, {
2001
+ fix: "Add at least one operation to the operations object",
2002
+ });
2003
+ validateOperationIds(config.id, config.operations);
2004
+ validateOperationAnnotations(config.id, config.operations);
2005
+ validateOperationObservability(config.id, config.operations);
2006
+ validateOperationErrorCodes(config.id, config.operations);
2007
+ validateOperationTransports(config.id, config.operations);
2008
+ validateOperationContracts(config.id, config.operations);
2009
+ validateToolRouterMetadata(config.id, config.operations);
2010
+ const journeyCoveredOperations = validateHealthJourneys(config.id, config.operations, config.healthJourneys);
2011
+ validateOperationHealthChecks(config.id, config.operations, journeyCoveredOperations);
2012
+ validateOperationFixtures(config.id, operations);
2000
2013
  const provider = {
2001
2014
  id: config.id,
2002
2015
  version: config.version,
@@ -2019,13 +2032,15 @@ function finalizeProvider(config) {
2019
2032
  credential: config.credential,
2020
2033
  context: config.context,
2021
2034
  meta: config.meta,
2022
- operations: operations,
2035
+ operations,
2023
2036
  // Transitional healthMonitor → healthProbe alias: mirror whichever field
2024
2037
  // was declared onto both so old and new consumers keep working.
2025
2038
  healthMonitor: config.healthMonitor ?? config.healthProbe,
2026
2039
  healthProbe: config.healthProbe ?? config.healthMonitor,
2027
2040
  healthJourneys: config.healthJourneys,
2028
2041
  };
2029
- validateFailClosedDeclaration(provider);
2042
+ // Declaration validation never invokes handlers, so their declaration-bound
2043
+ // context parameter is irrelevant to the runtime ProviderDefinition shape.
2044
+ validateFailClosedOperationDeclaration(provider);
2030
2045
  return provider;
2031
2046
  }