@executor-js/plugin-openapi 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ZZ7TQ4JC.js → chunk-RBE3CVB4.js} +1233 -998
- package/dist/chunk-RBE3CVB4.js.map +1 -0
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/dist/react/client.d.ts +1 -334
- package/dist/react/plugin-client.d.ts +2 -0
- package/dist/react/source-plugin.d.ts +1 -1
- package/dist/sdk/plugin.d.ts +115 -6
- package/dist/sdk/upstream-failures.test.d.ts +1 -0
- package/package.json +4 -4
- package/dist/chunk-ZZ7TQ4JC.js.map +0 -1
|
@@ -561,934 +561,531 @@ var extract = Effect2.fn("OpenApi.extract")(function* (doc) {
|
|
|
561
561
|
});
|
|
562
562
|
});
|
|
563
563
|
|
|
564
|
-
// src/sdk/
|
|
565
|
-
import { Effect as Effect3,
|
|
566
|
-
import {
|
|
567
|
-
var
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
for (const key of CONTAINER_KEYS[param.location] ?? []) {
|
|
577
|
-
const container = args[key];
|
|
578
|
-
if (typeof container === "object" && container !== null && !Array.isArray(container)) {
|
|
579
|
-
const nested = container[param.name];
|
|
580
|
-
if (nested !== void 0) return nested;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
return void 0;
|
|
564
|
+
// src/sdk/preview.ts
|
|
565
|
+
import { Effect as Effect3, Option as Option3 } from "effect";
|
|
566
|
+
import { Schema as Schema3 } from "effect";
|
|
567
|
+
var OAuth2Scopes = Schema3.Record(Schema3.String, Schema3.String);
|
|
568
|
+
var OAuth2AuthorizationCodeFlow = class extends Schema3.Class(
|
|
569
|
+
"OAuth2AuthorizationCodeFlow"
|
|
570
|
+
)({
|
|
571
|
+
authorizationUrl: Schema3.String,
|
|
572
|
+
tokenUrl: Schema3.String,
|
|
573
|
+
refreshUrl: Schema3.OptionFromOptional(Schema3.String),
|
|
574
|
+
scopes: OAuth2Scopes
|
|
575
|
+
}) {
|
|
584
576
|
};
|
|
585
|
-
var
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return yield* new OpenApiInvocationError({
|
|
593
|
-
message: `Missing required path parameter: ${param.name}`,
|
|
594
|
-
statusCode: Option3.none()
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
continue;
|
|
598
|
-
}
|
|
599
|
-
resolved = resolved.replaceAll(`{${param.name}}`, encodeURIComponent(String(value)));
|
|
600
|
-
}
|
|
601
|
-
const remaining = [...resolved.matchAll(/\{([^{}]+)\}/g)].map((m) => m[1]).filter((v) => typeof v === "string");
|
|
602
|
-
for (const name of remaining) {
|
|
603
|
-
const value = args[name];
|
|
604
|
-
if (value !== void 0 && value !== null) {
|
|
605
|
-
resolved = resolved.replaceAll(`{${name}}`, encodeURIComponent(String(value)));
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
const unresolved = [...resolved.matchAll(/\{([^{}]+)\}/g)].map((m) => m[1]).filter((v) => typeof v === "string");
|
|
609
|
-
if (unresolved.length > 0) {
|
|
610
|
-
return yield* new OpenApiInvocationError({
|
|
611
|
-
message: `Unresolved path parameters: ${[...new Set(unresolved)].join(", ")}`,
|
|
612
|
-
statusCode: Option3.none()
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
return resolved;
|
|
616
|
-
});
|
|
617
|
-
var resolveHeaders = (headers, secrets) => {
|
|
618
|
-
const entries = Object.entries(headers);
|
|
619
|
-
const secretCount = entries.reduce(
|
|
620
|
-
(acc, [, value]) => typeof value === "string" ? acc : acc + 1,
|
|
621
|
-
0
|
|
622
|
-
);
|
|
623
|
-
return Effect3.gen(function* () {
|
|
624
|
-
const values = yield* Effect3.all(
|
|
625
|
-
entries.map(
|
|
626
|
-
([name, value]) => typeof value === "string" ? Effect3.succeed({ name, value }) : secrets.get(value.secretId).pipe(
|
|
627
|
-
Effect3.mapError(
|
|
628
|
-
(err) => "_tag" in err && err._tag === "SecretOwnedByConnectionError" ? new OpenApiInvocationError({
|
|
629
|
-
message: `Failed to resolve secret "${value.secretId}" for header "${name}"`,
|
|
630
|
-
statusCode: Option3.none()
|
|
631
|
-
}) : err
|
|
632
|
-
),
|
|
633
|
-
Effect3.flatMap(
|
|
634
|
-
(secret) => secret === null ? Effect3.fail(
|
|
635
|
-
new OpenApiInvocationError({
|
|
636
|
-
message: `Failed to resolve secret "${value.secretId}" for header "${name}"`,
|
|
637
|
-
statusCode: Option3.none()
|
|
638
|
-
})
|
|
639
|
-
) : Effect3.succeed({
|
|
640
|
-
name,
|
|
641
|
-
value: value.prefix ? `${value.prefix}${secret}` : secret
|
|
642
|
-
})
|
|
643
|
-
)
|
|
644
|
-
)
|
|
645
|
-
),
|
|
646
|
-
{ concurrency: "unbounded" }
|
|
647
|
-
);
|
|
648
|
-
const resolved = {};
|
|
649
|
-
for (const { name, value } of values) resolved[name] = value;
|
|
650
|
-
return resolved;
|
|
651
|
-
}).pipe(
|
|
652
|
-
Effect3.withSpan("plugin.openapi.secret.resolve", {
|
|
653
|
-
attributes: {
|
|
654
|
-
"plugin.openapi.headers.total": entries.length,
|
|
655
|
-
"plugin.openapi.headers.secret_count": secretCount
|
|
656
|
-
}
|
|
657
|
-
})
|
|
658
|
-
);
|
|
577
|
+
var OAuth2ClientCredentialsFlow = class extends Schema3.Class(
|
|
578
|
+
"OAuth2ClientCredentialsFlow"
|
|
579
|
+
)({
|
|
580
|
+
tokenUrl: Schema3.String,
|
|
581
|
+
refreshUrl: Schema3.OptionFromOptional(Schema3.String),
|
|
582
|
+
scopes: OAuth2Scopes
|
|
583
|
+
}) {
|
|
659
584
|
};
|
|
660
|
-
var
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
}
|
|
665
|
-
return req;
|
|
585
|
+
var OAuth2Flows = class extends Schema3.Class("OAuth2Flows")({
|
|
586
|
+
authorizationCode: Schema3.OptionFromOptional(OAuth2AuthorizationCodeFlow),
|
|
587
|
+
clientCredentials: Schema3.OptionFromOptional(OAuth2ClientCredentialsFlow)
|
|
588
|
+
}) {
|
|
666
589
|
};
|
|
667
|
-
var
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
590
|
+
var SecurityScheme = class extends Schema3.Class("SecurityScheme")({
|
|
591
|
+
/** Key name in components.securitySchemes (e.g. "api_token") */
|
|
592
|
+
name: Schema3.String,
|
|
593
|
+
/** OpenAPI security scheme type */
|
|
594
|
+
type: Schema3.Literals(["http", "apiKey", "oauth2", "openIdConnect"]),
|
|
595
|
+
/** For type: "http" — e.g. "bearer", "basic" */
|
|
596
|
+
scheme: Schema3.OptionFromOptional(Schema3.String),
|
|
597
|
+
/** For type: "http" with scheme "bearer" — e.g. "JWT" */
|
|
598
|
+
bearerFormat: Schema3.OptionFromOptional(Schema3.String),
|
|
599
|
+
/** For type: "apiKey" — where the key goes */
|
|
600
|
+
in: Schema3.OptionFromOptional(Schema3.Literals(["header", "query", "cookie"])),
|
|
601
|
+
/** For type: "apiKey" — the header/query/cookie name */
|
|
602
|
+
headerName: Schema3.OptionFromOptional(Schema3.String),
|
|
603
|
+
description: Schema3.OptionFromOptional(Schema3.String),
|
|
604
|
+
/** For type: "oauth2" — declared flows (authorizationCode / clientCredentials only; implicit and password are deprecated). */
|
|
605
|
+
flows: Schema3.OptionFromOptional(OAuth2Flows),
|
|
606
|
+
/** For type: "openIdConnect" — the discovery URL. */
|
|
607
|
+
openIdConnectUrl: Schema3.OptionFromOptional(Schema3.String)
|
|
608
|
+
}) {
|
|
672
609
|
};
|
|
673
|
-
var
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (!normalized) return false;
|
|
678
|
-
return normalized === "application/xml" || normalized === "text/xml" || normalized.endsWith("+xml");
|
|
610
|
+
var AuthStrategy = class extends Schema3.Class("AuthStrategy")({
|
|
611
|
+
/** The security schemes required together for this strategy */
|
|
612
|
+
schemes: Schema3.Array(Schema3.String)
|
|
613
|
+
}) {
|
|
679
614
|
};
|
|
680
|
-
var
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
}
|
|
689
|
-
if (Array.isArray(value) && value.every((v) => typeof v === "number")) {
|
|
690
|
-
return new Uint8Array(value);
|
|
691
|
-
}
|
|
692
|
-
return null;
|
|
615
|
+
var HeaderPreset = class extends Schema3.Class("HeaderPreset")({
|
|
616
|
+
/** Human-readable label for the UI (e.g. "Bearer Token", "API Key + Email") */
|
|
617
|
+
label: Schema3.String,
|
|
618
|
+
/** Headers this strategy needs. Value is null when the user must provide it. */
|
|
619
|
+
headers: Schema3.Record(Schema3.String, Schema3.NullOr(Schema3.String)),
|
|
620
|
+
/** Which headers should be stored as secrets */
|
|
621
|
+
secretHeaders: Schema3.Array(Schema3.String)
|
|
622
|
+
}) {
|
|
693
623
|
};
|
|
694
|
-
var
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
624
|
+
var OAuth2Preset = class extends Schema3.Class("OAuth2Preset")({
|
|
625
|
+
/** Human-readable label for the UI (e.g. "OAuth2 (Authorization Code) — oauth_app") */
|
|
626
|
+
label: Schema3.String,
|
|
627
|
+
/** The source security scheme this preset came from (components.securitySchemes key). */
|
|
628
|
+
securitySchemeName: Schema3.String,
|
|
629
|
+
/** Which OAuth2 flow this preset uses. */
|
|
630
|
+
flow: Schema3.Literals(["authorizationCode", "clientCredentials"]),
|
|
631
|
+
/** For authorizationCode: user-agent redirect URL (from the spec). */
|
|
632
|
+
authorizationUrl: Schema3.OptionFromOptional(Schema3.String),
|
|
633
|
+
/** Token endpoint to exchange the code / refresh. */
|
|
634
|
+
tokenUrl: Schema3.String,
|
|
635
|
+
/** Optional refresh endpoint if the spec declares one separately. */
|
|
636
|
+
refreshUrl: Schema3.OptionFromOptional(Schema3.String),
|
|
637
|
+
/** Declared scopes for this flow: `{ scope: description }`. */
|
|
638
|
+
scopes: Schema3.Record(Schema3.String, Schema3.String)
|
|
639
|
+
}) {
|
|
698
640
|
};
|
|
699
|
-
var
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
641
|
+
var PreviewOperation = class extends Schema3.Class("PreviewOperation")({
|
|
642
|
+
operationId: Schema3.String,
|
|
643
|
+
method: HttpMethod,
|
|
644
|
+
path: Schema3.String,
|
|
645
|
+
summary: Schema3.OptionFromOptional(Schema3.String),
|
|
646
|
+
tags: Schema3.Array(Schema3.String),
|
|
647
|
+
deprecated: Schema3.Boolean
|
|
648
|
+
}) {
|
|
703
649
|
};
|
|
704
|
-
var
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
650
|
+
var SpecPreview = class extends Schema3.Class("SpecPreview")({
|
|
651
|
+
title: Schema3.OptionFromOptional(Schema3.String),
|
|
652
|
+
version: Schema3.OptionFromOptional(Schema3.String),
|
|
653
|
+
/** Reuses ServerInfo from extraction */
|
|
654
|
+
servers: Schema3.Array(ServerInfo),
|
|
655
|
+
operationCount: Schema3.Number,
|
|
656
|
+
/** Lightweight operation list for the add-source UI */
|
|
657
|
+
operations: Schema3.Array(PreviewOperation),
|
|
658
|
+
tags: Schema3.Array(Schema3.String),
|
|
659
|
+
securitySchemes: Schema3.Array(SecurityScheme),
|
|
660
|
+
/** Valid auth strategies (each is a set of schemes used together) */
|
|
661
|
+
authStrategies: Schema3.Array(AuthStrategy),
|
|
662
|
+
/** Pre-built header presets derived from auth strategies */
|
|
663
|
+
headerPresets: Schema3.Array(HeaderPreset),
|
|
664
|
+
/** OAuth2 presets — one per (oauth2 scheme × supported flow) combination */
|
|
665
|
+
oauth2Presets: Schema3.Array(OAuth2Preset)
|
|
666
|
+
}) {
|
|
711
667
|
};
|
|
712
|
-
var
|
|
713
|
-
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
for (const ch of raw) {
|
|
718
|
-
out += RESERVED_UNENCODED_RE.test(ch) ? ch : encodeURIComponent(ch);
|
|
668
|
+
var stringRecord = (value) => {
|
|
669
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
670
|
+
const out = {};
|
|
671
|
+
for (const [k, v] of Object.entries(value)) {
|
|
672
|
+
if (typeof v === "string") out[k] = v;
|
|
719
673
|
}
|
|
720
674
|
return out;
|
|
721
675
|
};
|
|
722
|
-
var
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
)
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
}
|
|
744
|
-
if (typeof raw === "object") {
|
|
745
|
-
const entries = Object.entries(raw).filter(
|
|
746
|
-
([, v]) => v !== void 0 && v !== null
|
|
747
|
-
);
|
|
748
|
-
if (style === "deepObject") {
|
|
749
|
-
for (const [subkey, subval] of entries) {
|
|
750
|
-
parts.push(
|
|
751
|
-
`${encodeURIComponent(`${key}[${subkey}]`)}=${encodeFormValue(
|
|
752
|
-
subval,
|
|
753
|
-
allowReserved
|
|
754
|
-
)}`
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
} else if (explode) {
|
|
758
|
-
for (const [subkey, subval] of entries) {
|
|
759
|
-
parts.push(
|
|
760
|
-
`${encodeURIComponent(subkey)}=${encodeFormValue(subval, allowReserved)}`
|
|
761
|
-
);
|
|
762
|
-
}
|
|
763
|
-
} else {
|
|
764
|
-
const flat = entries.flatMap(([k, v]) => [
|
|
765
|
-
k,
|
|
766
|
-
typeof v === "object" ? JSON.stringify(v) : String(v)
|
|
767
|
-
]);
|
|
768
|
-
parts.push(`${encKey}=${encodeFormValue(flat.join(","), allowReserved)}`);
|
|
769
|
-
}
|
|
770
|
-
continue;
|
|
676
|
+
var extractFlows = (rawFlows) => {
|
|
677
|
+
if (!rawFlows || typeof rawFlows !== "object") return Option3.none();
|
|
678
|
+
const flows = rawFlows;
|
|
679
|
+
const parseFlow = (key) => flows[key];
|
|
680
|
+
let authorizationCode = Option3.none();
|
|
681
|
+
const authCodeRaw = parseFlow("authorizationCode");
|
|
682
|
+
if (authCodeRaw && typeof authCodeRaw === "object") {
|
|
683
|
+
const f = authCodeRaw;
|
|
684
|
+
const authUrl = typeof f.authorizationUrl === "string" ? f.authorizationUrl : null;
|
|
685
|
+
const tokenUrl = typeof f.tokenUrl === "string" ? f.tokenUrl : null;
|
|
686
|
+
if (authUrl && tokenUrl) {
|
|
687
|
+
authorizationCode = Option3.some(
|
|
688
|
+
new OAuth2AuthorizationCodeFlow({
|
|
689
|
+
authorizationUrl: authUrl,
|
|
690
|
+
tokenUrl,
|
|
691
|
+
refreshUrl: Option3.fromNullishOr(
|
|
692
|
+
typeof f.refreshUrl === "string" ? f.refreshUrl : void 0
|
|
693
|
+
),
|
|
694
|
+
scopes: stringRecord(f.scopes)
|
|
695
|
+
})
|
|
696
|
+
);
|
|
771
697
|
}
|
|
772
|
-
parts.push(`${encKey}=${encodeFormValue(raw, allowReserved)}`);
|
|
773
698
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
if (
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
out[key] = raw;
|
|
789
|
-
continue;
|
|
790
|
-
}
|
|
791
|
-
if (Array.isArray(raw)) {
|
|
792
|
-
out[key] = raw.map(
|
|
793
|
-
(v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v instanceof Blob || typeof File !== "undefined" && v instanceof File ? v : JSON.stringify(v)
|
|
699
|
+
let clientCredentials = Option3.none();
|
|
700
|
+
const ccRaw = parseFlow("clientCredentials");
|
|
701
|
+
if (ccRaw && typeof ccRaw === "object") {
|
|
702
|
+
const f = ccRaw;
|
|
703
|
+
const tokenUrl = typeof f.tokenUrl === "string" ? f.tokenUrl : null;
|
|
704
|
+
if (tokenUrl) {
|
|
705
|
+
clientCredentials = Option3.some(
|
|
706
|
+
new OAuth2ClientCredentialsFlow({
|
|
707
|
+
tokenUrl,
|
|
708
|
+
refreshUrl: Option3.fromNullishOr(
|
|
709
|
+
typeof f.refreshUrl === "string" ? f.refreshUrl : void 0
|
|
710
|
+
),
|
|
711
|
+
scopes: stringRecord(f.scopes)
|
|
712
|
+
})
|
|
794
713
|
);
|
|
795
|
-
continue;
|
|
796
|
-
}
|
|
797
|
-
const bytes = toUint8Array(raw);
|
|
798
|
-
if (bytes) {
|
|
799
|
-
out[key] = new Blob([toArrayBuffer(bytes)]);
|
|
800
|
-
continue;
|
|
801
714
|
}
|
|
802
|
-
out[key] = JSON.stringify(raw);
|
|
803
715
|
}
|
|
804
|
-
|
|
716
|
+
if (Option3.isNone(authorizationCode) && Option3.isNone(clientCredentials)) {
|
|
717
|
+
return Option3.none();
|
|
718
|
+
}
|
|
719
|
+
return Option3.some(new OAuth2Flows({ authorizationCode, clientCredentials }));
|
|
805
720
|
};
|
|
806
|
-
var
|
|
807
|
-
if (
|
|
808
|
-
|
|
809
|
-
|
|
721
|
+
var extractSecuritySchemes = (rawSchemes, resolver) => Object.entries(rawSchemes).flatMap(([name, schemeOrRef]) => {
|
|
722
|
+
if (!schemeOrRef || typeof schemeOrRef !== "object") return [];
|
|
723
|
+
const resolved = resolver.resolve(
|
|
724
|
+
schemeOrRef
|
|
725
|
+
);
|
|
726
|
+
if (!resolved || typeof resolved !== "object") return [];
|
|
727
|
+
const scheme = resolved;
|
|
728
|
+
const type = scheme.type;
|
|
729
|
+
if (!["http", "apiKey", "oauth2", "openIdConnect"].includes(type)) return [];
|
|
730
|
+
return [
|
|
731
|
+
new SecurityScheme({
|
|
732
|
+
name,
|
|
733
|
+
type,
|
|
734
|
+
scheme: Option3.fromNullishOr(scheme.scheme),
|
|
735
|
+
bearerFormat: Option3.fromNullishOr(scheme.bearerFormat),
|
|
736
|
+
in: Option3.fromNullishOr(scheme.in),
|
|
737
|
+
headerName: Option3.fromNullishOr(scheme.name),
|
|
738
|
+
description: Option3.fromNullishOr(scheme.description),
|
|
739
|
+
flows: type === "oauth2" ? extractFlows(scheme.flows) : Option3.none(),
|
|
740
|
+
openIdConnectUrl: Option3.fromNullishOr(
|
|
741
|
+
scheme.openIdConnectUrl
|
|
742
|
+
)
|
|
743
|
+
})
|
|
744
|
+
];
|
|
745
|
+
});
|
|
746
|
+
var buildHeaderPresets = (schemes, strategies) => {
|
|
747
|
+
const schemeMap = new Map(schemes.map((s) => [s.name, s]));
|
|
748
|
+
return strategies.flatMap((strategy) => {
|
|
749
|
+
const resolved = strategy.schemes.map((name) => schemeMap.get(name)).filter((s) => s !== void 0);
|
|
750
|
+
if (resolved.length === 0) return [];
|
|
751
|
+
const headers = {};
|
|
752
|
+
const secretHeaders = [];
|
|
753
|
+
const labelParts = [];
|
|
754
|
+
for (const scheme of resolved) {
|
|
755
|
+
if (scheme.type === "http" && Option3.getOrElse(scheme.scheme, () => "") === "bearer") {
|
|
756
|
+
headers["Authorization"] = null;
|
|
757
|
+
secretHeaders.push("Authorization");
|
|
758
|
+
labelParts.push("Bearer Token");
|
|
759
|
+
} else if (scheme.type === "http" && Option3.getOrElse(scheme.scheme, () => "") === "basic") {
|
|
760
|
+
headers["Authorization"] = null;
|
|
761
|
+
secretHeaders.push("Authorization");
|
|
762
|
+
labelParts.push("Basic Auth");
|
|
763
|
+
} else if (scheme.type === "apiKey" && Option3.getOrElse(scheme.in, () => "") === "header") {
|
|
764
|
+
const headerName = Option3.getOrElse(scheme.headerName, () => scheme.name);
|
|
765
|
+
headers[headerName] = null;
|
|
766
|
+
secretHeaders.push(headerName);
|
|
767
|
+
labelParts.push(scheme.name);
|
|
768
|
+
} else if (scheme.type === "apiKey") {
|
|
769
|
+
labelParts.push(`${scheme.name} (${Option3.getOrElse(scheme.in, () => "unknown")})`);
|
|
770
|
+
} else {
|
|
771
|
+
labelParts.push(scheme.name);
|
|
772
|
+
}
|
|
810
773
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
if (isFormUrlEncoded(contentType)) {
|
|
814
|
-
if (typeof bodyValue === "string") {
|
|
815
|
-
return HttpClientRequest2.bodyText(request, bodyValue, contentType);
|
|
774
|
+
if (Object.keys(headers).length === 0 && resolved.length > 0) {
|
|
775
|
+
return [new HeaderPreset({ label: labelParts.join(" + "), headers: {}, secretHeaders: [] })];
|
|
816
776
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
777
|
+
return [new HeaderPreset({ label: labelParts.join(" + "), headers, secretHeaders })];
|
|
778
|
+
});
|
|
779
|
+
};
|
|
780
|
+
var buildOAuth2Presets = (schemes) => {
|
|
781
|
+
const presets = [];
|
|
782
|
+
for (const scheme of schemes) {
|
|
783
|
+
if (scheme.type !== "oauth2") continue;
|
|
784
|
+
if (Option3.isNone(scheme.flows)) continue;
|
|
785
|
+
const flows = scheme.flows.value;
|
|
786
|
+
if (Option3.isSome(flows.authorizationCode)) {
|
|
787
|
+
const flow = flows.authorizationCode.value;
|
|
788
|
+
presets.push(
|
|
789
|
+
new OAuth2Preset({
|
|
790
|
+
label: `OAuth2 Authorization Code \xB7 ${scheme.name}`,
|
|
791
|
+
securitySchemeName: scheme.name,
|
|
792
|
+
flow: "authorizationCode",
|
|
793
|
+
authorizationUrl: Option3.some(flow.authorizationUrl),
|
|
794
|
+
tokenUrl: flow.tokenUrl,
|
|
795
|
+
refreshUrl: flow.refreshUrl,
|
|
796
|
+
scopes: flow.scopes
|
|
797
|
+
})
|
|
821
798
|
);
|
|
822
|
-
return HttpClientRequest2.bodyText(request, serialized, contentType);
|
|
823
|
-
}
|
|
824
|
-
return HttpClientRequest2.bodyUrlParams(
|
|
825
|
-
request,
|
|
826
|
-
bodyValue
|
|
827
|
-
);
|
|
828
|
-
}
|
|
829
|
-
if (isMultipartFormData(contentType)) {
|
|
830
|
-
if (bodyValue instanceof FormData) {
|
|
831
|
-
return HttpClientRequest2.bodyFormData(request, bodyValue);
|
|
832
799
|
}
|
|
833
|
-
if (
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
800
|
+
if (Option3.isSome(flows.clientCredentials)) {
|
|
801
|
+
const flow = flows.clientCredentials.value;
|
|
802
|
+
presets.push(
|
|
803
|
+
new OAuth2Preset({
|
|
804
|
+
label: `OAuth2 Client Credentials \xB7 ${scheme.name}`,
|
|
805
|
+
securitySchemeName: scheme.name,
|
|
806
|
+
flow: "clientCredentials",
|
|
807
|
+
authorizationUrl: Option3.none(),
|
|
808
|
+
tokenUrl: flow.tokenUrl,
|
|
809
|
+
refreshUrl: flow.refreshUrl,
|
|
810
|
+
scopes: flow.scopes
|
|
811
|
+
})
|
|
837
812
|
);
|
|
838
813
|
}
|
|
839
|
-
return HttpClientRequest2.bodyText(request, String(bodyValue), contentType);
|
|
840
|
-
}
|
|
841
|
-
if (isOctetStream(contentType)) {
|
|
842
|
-
const bytes2 = toUint8Array(bodyValue);
|
|
843
|
-
if (bytes2) return HttpClientRequest2.bodyUint8Array(request, bytes2, contentType);
|
|
844
|
-
if (typeof bodyValue === "string") {
|
|
845
|
-
return HttpClientRequest2.bodyText(request, bodyValue, contentType);
|
|
846
|
-
}
|
|
847
|
-
return HttpClientRequest2.bodyText(request, JSON.stringify(bodyValue), contentType);
|
|
848
|
-
}
|
|
849
|
-
if (isXmlContentType(contentType) || isTextContentType(contentType)) {
|
|
850
|
-
if (typeof bodyValue === "string") {
|
|
851
|
-
return HttpClientRequest2.bodyText(request, bodyValue, contentType);
|
|
852
|
-
}
|
|
853
|
-
const bytes2 = toUint8Array(bodyValue);
|
|
854
|
-
if (bytes2) return HttpClientRequest2.bodyUint8Array(request, bytes2, contentType);
|
|
855
|
-
return HttpClientRequest2.bodyText(request, JSON.stringify(bodyValue), contentType);
|
|
856
814
|
}
|
|
857
|
-
|
|
858
|
-
return HttpClientRequest2.bodyText(request, bodyValue, contentType);
|
|
859
|
-
}
|
|
860
|
-
const bytes = toUint8Array(bodyValue);
|
|
861
|
-
if (bytes) return HttpClientRequest2.bodyUint8Array(request, bytes, contentType);
|
|
862
|
-
return HttpClientRequest2.bodyText(request, JSON.stringify(bodyValue), contentType);
|
|
815
|
+
return presets;
|
|
863
816
|
};
|
|
864
|
-
var
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
"http.route": operation.pathTemplate,
|
|
869
|
-
"plugin.openapi.method": operation.method.toUpperCase(),
|
|
870
|
-
"plugin.openapi.path_template": operation.pathTemplate,
|
|
871
|
-
"plugin.openapi.headers.resolved_count": Object.keys(resolvedHeaders).length
|
|
872
|
-
});
|
|
873
|
-
const resolvedPath = yield* resolvePath(operation.pathTemplate, args, operation.parameters);
|
|
874
|
-
const path = resolvedPath.startsWith("/") ? resolvedPath : `/${resolvedPath}`;
|
|
875
|
-
let request = HttpClientRequest2.make(operation.method.toUpperCase())(path);
|
|
876
|
-
for (const [name, value] of Object.entries(sourceQueryParams)) {
|
|
877
|
-
request = HttpClientRequest2.setUrlParam(request, name, value);
|
|
878
|
-
}
|
|
879
|
-
for (const param of operation.parameters) {
|
|
880
|
-
if (param.location !== "query") continue;
|
|
881
|
-
const value = readParamValue(args, param);
|
|
882
|
-
if (value === void 0 || value === null) continue;
|
|
883
|
-
request = HttpClientRequest2.setUrlParam(request, param.name, String(value));
|
|
884
|
-
}
|
|
885
|
-
for (const param of operation.parameters) {
|
|
886
|
-
if (param.location !== "header") continue;
|
|
887
|
-
const value = readParamValue(args, param);
|
|
888
|
-
if (value === void 0 || value === null) continue;
|
|
889
|
-
request = HttpClientRequest2.setHeader(request, param.name, String(value));
|
|
890
|
-
}
|
|
891
|
-
if (Option3.isSome(operation.requestBody)) {
|
|
892
|
-
const rb = operation.requestBody.value;
|
|
893
|
-
const bodyValue = args.body ?? args.input;
|
|
894
|
-
if (bodyValue !== void 0) {
|
|
895
|
-
const contentsOpt = Option3.getOrUndefined(rb.contents);
|
|
896
|
-
const requestedCt = typeof args.contentType === "string" ? args.contentType : void 0;
|
|
897
|
-
const selected = contentsOpt && requestedCt ? contentsOpt.find((c) => c.contentType === requestedCt) : void 0;
|
|
898
|
-
const chosenCt = selected?.contentType ?? rb.contentType;
|
|
899
|
-
const chosenEncoding = selected ? Option3.getOrUndefined(selected.encoding) : contentsOpt && contentsOpt[0] ? Option3.getOrUndefined(contentsOpt[0].encoding) : void 0;
|
|
900
|
-
request = applyRequestBody(request, chosenCt, bodyValue, chosenEncoding);
|
|
901
|
-
}
|
|
817
|
+
var collectTags = (result) => {
|
|
818
|
+
const tagSet = /* @__PURE__ */ new Set();
|
|
819
|
+
for (const op of result.operations) {
|
|
820
|
+
for (const tag of op.tags) tagSet.add(tag);
|
|
902
821
|
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
822
|
+
return [...tagSet].sort();
|
|
823
|
+
};
|
|
824
|
+
var previewSpec = Effect3.fn("OpenApi.previewSpec")(function* (input) {
|
|
825
|
+
const specText = yield* resolveSpecText(input);
|
|
826
|
+
const doc = yield* parse(specText);
|
|
827
|
+
const result = yield* extract(doc);
|
|
828
|
+
const resolver = new DocResolver(doc);
|
|
829
|
+
const securitySchemes = extractSecuritySchemes(
|
|
830
|
+
doc.components?.securitySchemes ?? {},
|
|
831
|
+
resolver
|
|
912
832
|
);
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
});
|
|
917
|
-
const responseHeaders = { ...response.headers };
|
|
918
|
-
const contentType = response.headers["content-type"] ?? null;
|
|
919
|
-
const mapBodyError = Effect3.mapError(
|
|
920
|
-
(err) => new OpenApiInvocationError({
|
|
921
|
-
message: `Failed to read response body: ${err.message ?? String(err)}`,
|
|
922
|
-
statusCode: Option3.some(status),
|
|
923
|
-
cause: err
|
|
924
|
-
})
|
|
833
|
+
const rawSecurity = doc.security ?? [];
|
|
834
|
+
const declaredStrategies = rawSecurity.map(
|
|
835
|
+
(entry) => new AuthStrategy({ schemes: Object.keys(entry) })
|
|
925
836
|
);
|
|
926
|
-
const
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
837
|
+
const authStrategies = declaredStrategies.length > 0 ? declaredStrategies : securitySchemes.map((scheme) => new AuthStrategy({ schemes: [scheme.name] }));
|
|
838
|
+
return new SpecPreview({
|
|
839
|
+
title: result.title,
|
|
840
|
+
version: result.version,
|
|
841
|
+
servers: result.servers,
|
|
842
|
+
operationCount: result.operations.length,
|
|
843
|
+
operations: result.operations.map(
|
|
844
|
+
(op) => new PreviewOperation({
|
|
845
|
+
operationId: op.operationId,
|
|
846
|
+
method: op.method,
|
|
847
|
+
path: op.pathTemplate,
|
|
848
|
+
summary: op.summary,
|
|
849
|
+
tags: op.tags,
|
|
850
|
+
deprecated: op.deprecated
|
|
851
|
+
})
|
|
852
|
+
),
|
|
853
|
+
tags: collectTags(result),
|
|
854
|
+
securitySchemes,
|
|
855
|
+
authStrategies,
|
|
856
|
+
headerPresets: buildHeaderPresets(securitySchemes, authStrategies),
|
|
857
|
+
oauth2Presets: buildOAuth2Presets(securitySchemes)
|
|
936
858
|
});
|
|
937
859
|
});
|
|
938
|
-
var invokeWithLayer = (operation, args, baseUrl, resolvedHeaders, sourceQueryParams, httpClientLayer) => {
|
|
939
|
-
const clientWithBaseUrl = baseUrl ? Layer.effect(
|
|
940
|
-
HttpClient2.HttpClient,
|
|
941
|
-
Effect3.map(
|
|
942
|
-
Effect3.service(HttpClient2.HttpClient),
|
|
943
|
-
HttpClient2.mapRequest(HttpClientRequest2.prependUrl(baseUrl))
|
|
944
|
-
)
|
|
945
|
-
).pipe(Layer.provide(httpClientLayer)) : httpClientLayer;
|
|
946
|
-
return invoke(operation, args, resolvedHeaders, sourceQueryParams).pipe(
|
|
947
|
-
Effect3.provide(clientWithBaseUrl),
|
|
948
|
-
Effect3.withSpan("plugin.openapi.invoke", {
|
|
949
|
-
attributes: {
|
|
950
|
-
"plugin.openapi.method": operation.method.toUpperCase(),
|
|
951
|
-
"plugin.openapi.path_template": operation.pathTemplate,
|
|
952
|
-
"plugin.openapi.base_url": baseUrl
|
|
953
|
-
}
|
|
954
|
-
})
|
|
955
|
-
);
|
|
956
|
-
};
|
|
957
|
-
var REQUIRE_APPROVAL = /* @__PURE__ */ new Set(["post", "put", "patch", "delete"]);
|
|
958
|
-
var annotationsForOperation = (method, pathTemplate) => {
|
|
959
|
-
const m = method.toLowerCase();
|
|
960
|
-
if (!REQUIRE_APPROVAL.has(m)) return {};
|
|
961
|
-
return {
|
|
962
|
-
requiresApproval: true,
|
|
963
|
-
approvalDescription: `${method.toUpperCase()} ${pathTemplate}`
|
|
964
|
-
};
|
|
965
|
-
};
|
|
966
860
|
|
|
967
|
-
// src/sdk/
|
|
968
|
-
import { Effect as Effect4,
|
|
969
|
-
import {
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
|
|
861
|
+
// src/sdk/store.ts
|
|
862
|
+
import { Effect as Effect4, Schema as Schema4 } from "effect";
|
|
863
|
+
import {
|
|
864
|
+
defineSchema,
|
|
865
|
+
ScopeId as ScopeId2,
|
|
866
|
+
StorageError
|
|
867
|
+
} from "@executor-js/sdk/core";
|
|
868
|
+
var openapiSchema = defineSchema({
|
|
869
|
+
openapi_source: {
|
|
870
|
+
fields: {
|
|
871
|
+
id: { type: "string", required: true },
|
|
872
|
+
scope_id: { type: "string", required: true, index: true },
|
|
873
|
+
name: { type: "string", required: true },
|
|
874
|
+
spec: { type: "string", required: true },
|
|
875
|
+
// Origin URL the spec was fetched from. Set when `addSpec` was
|
|
876
|
+
// invoked with an http(s) URL; null when the caller passed raw
|
|
877
|
+
// spec text. Drives `canRefresh` on the core source row and
|
|
878
|
+
// is the address re-fetched on `refreshSource`.
|
|
879
|
+
source_url: { type: "string", required: false },
|
|
880
|
+
base_url: { type: "string", required: false },
|
|
881
|
+
headers: { type: "json", required: false },
|
|
882
|
+
query_params: { type: "json", required: false },
|
|
883
|
+
oauth2: { type: "json", required: false },
|
|
884
|
+
invocation_config: { type: "json", required: true }
|
|
885
|
+
}
|
|
886
|
+
},
|
|
887
|
+
openapi_operation: {
|
|
888
|
+
fields: {
|
|
889
|
+
id: { type: "string", required: true },
|
|
890
|
+
scope_id: { type: "string", required: true, index: true },
|
|
891
|
+
source_id: { type: "string", required: true, index: true },
|
|
892
|
+
binding: { type: "json", required: true }
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
openapi_source_binding: {
|
|
896
|
+
fields: {
|
|
897
|
+
id: { type: "string", required: true },
|
|
898
|
+
source_id: { type: "string", required: true, index: true },
|
|
899
|
+
source_scope_id: { type: "string", required: true, index: true },
|
|
900
|
+
// Intentionally NOT named `scope_id`: this row is visible across
|
|
901
|
+
// scope stacks and is filtered manually by source/target scope.
|
|
902
|
+
// The target scope is credential ownership data, not adapter row
|
|
903
|
+
// ownership. Source owners must be able to delete all descendant
|
|
904
|
+
// bindings when a shared source is removed.
|
|
905
|
+
target_scope_id: { type: "string", required: true, index: true },
|
|
906
|
+
slot: { type: "string", required: true, index: true },
|
|
907
|
+
value: { type: "json", required: true },
|
|
908
|
+
created_at: { type: "date", required: true },
|
|
909
|
+
updated_at: { type: "date", required: true }
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
var StoredSourceSchema = class extends Schema4.Class(
|
|
914
|
+
"OpenApiStoredSource"
|
|
982
915
|
)({
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
916
|
+
namespace: Schema4.String,
|
|
917
|
+
name: Schema4.String,
|
|
918
|
+
config: Schema4.Struct({
|
|
919
|
+
spec: Schema4.String,
|
|
920
|
+
sourceUrl: Schema4.optional(Schema4.String),
|
|
921
|
+
baseUrl: Schema4.optional(Schema4.String),
|
|
922
|
+
namespace: Schema4.optional(Schema4.String),
|
|
923
|
+
headers: Schema4.optional(
|
|
924
|
+
Schema4.Record(Schema4.String, ConfiguredHeaderValue)
|
|
925
|
+
),
|
|
926
|
+
queryParams: Schema4.optional(
|
|
927
|
+
Schema4.Record(Schema4.String, HeaderValue)
|
|
928
|
+
),
|
|
929
|
+
specFetchCredentials: Schema4.optional(
|
|
930
|
+
Schema4.Struct({
|
|
931
|
+
headers: Schema4.optional(
|
|
932
|
+
Schema4.Record(Schema4.String, HeaderValue)
|
|
933
|
+
),
|
|
934
|
+
queryParams: Schema4.optional(
|
|
935
|
+
Schema4.Record(Schema4.String, HeaderValue)
|
|
936
|
+
)
|
|
937
|
+
})
|
|
938
|
+
),
|
|
939
|
+
// Canonical source-owned OAuth config. Concrete client credentials
|
|
940
|
+
// and connection ids live in OpenAPI-owned scoped binding rows.
|
|
941
|
+
oauth2: Schema4.optional(OAuth2SourceConfig)
|
|
942
|
+
})
|
|
991
943
|
}) {
|
|
992
944
|
};
|
|
993
|
-
var
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
description: Schema3.OptionFromOptional(Schema3.String),
|
|
1007
|
-
/** For type: "oauth2" — declared flows (authorizationCode / clientCredentials only; implicit and password are deprecated). */
|
|
1008
|
-
flows: Schema3.OptionFromOptional(OAuth2Flows),
|
|
1009
|
-
/** For type: "openIdConnect" — the discovery URL. */
|
|
1010
|
-
openIdConnectUrl: Schema3.OptionFromOptional(Schema3.String)
|
|
1011
|
-
}) {
|
|
945
|
+
var encodeBinding = Schema4.encodeSync(OperationBinding);
|
|
946
|
+
var decodeBinding = Schema4.decodeUnknownSync(OperationBinding);
|
|
947
|
+
var decodeOAuth2 = Schema4.decodeUnknownSync(OAuth2Auth);
|
|
948
|
+
var encodeOAuth2SourceConfig = Schema4.encodeSync(OAuth2SourceConfig);
|
|
949
|
+
var encodeSourceBindingValue = Schema4.encodeSync(OpenApiSourceBindingValue);
|
|
950
|
+
var decodeSourceBindingValue = Schema4.decodeUnknownSync(
|
|
951
|
+
OpenApiSourceBindingValue
|
|
952
|
+
);
|
|
953
|
+
var asJsonObject = (value) => {
|
|
954
|
+
if (value == null) return {};
|
|
955
|
+
if (typeof value === "string")
|
|
956
|
+
return JSON.parse(value);
|
|
957
|
+
return value;
|
|
1012
958
|
};
|
|
1013
|
-
var
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
959
|
+
var toJsonRecord = (value) => value;
|
|
960
|
+
var toConfiguredHeaderBinding = (value) => new ConfiguredHeaderBinding({
|
|
961
|
+
kind: "binding",
|
|
962
|
+
slot: String(value.slot ?? ""),
|
|
963
|
+
...typeof value.prefix === "string" ? { prefix: value.prefix } : {}
|
|
964
|
+
});
|
|
965
|
+
var decodeHeaders = (value) => {
|
|
966
|
+
if (value == null) return {};
|
|
967
|
+
if (typeof value === "string")
|
|
968
|
+
return JSON.parse(value);
|
|
969
|
+
return value;
|
|
1017
970
|
};
|
|
1018
|
-
var
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
971
|
+
var slugifySlotPart = (value) => value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "default";
|
|
972
|
+
var headerBindingSlot = (headerName) => `header:${slugifySlotPart(headerName)}`;
|
|
973
|
+
var oauth2ClientIdSlot = (securitySchemeName) => `oauth2:${slugifySlotPart(securitySchemeName)}:client-id`;
|
|
974
|
+
var oauth2ClientSecretSlot = (securitySchemeName) => `oauth2:${slugifySlotPart(securitySchemeName)}:client-secret`;
|
|
975
|
+
var oauth2ConnectionSlot = (securitySchemeName) => `oauth2:${slugifySlotPart(securitySchemeName)}:connection`;
|
|
976
|
+
var normalizeStoredHeaders = (value) => {
|
|
977
|
+
const raw = decodeHeaders(value);
|
|
978
|
+
const headers = {};
|
|
979
|
+
const legacy = {};
|
|
980
|
+
for (const [name, header] of Object.entries(raw)) {
|
|
981
|
+
if (typeof header === "string") {
|
|
982
|
+
headers[name] = header;
|
|
983
|
+
legacy[name] = header;
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
if (header && typeof header === "object" && "kind" in header && header.kind === "binding") {
|
|
987
|
+
headers[name] = toConfiguredHeaderBinding(header);
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
legacy[name] = header;
|
|
991
|
+
headers[name] = new ConfiguredHeaderBinding({
|
|
992
|
+
kind: "binding",
|
|
993
|
+
slot: headerBindingSlot(name),
|
|
994
|
+
prefix: header.prefix
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
return { headers, legacy };
|
|
1026
998
|
};
|
|
1027
|
-
var
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
/** For authorizationCode: user-agent redirect URL (from the spec). */
|
|
1035
|
-
authorizationUrl: Schema3.OptionFromOptional(Schema3.String),
|
|
1036
|
-
/** Token endpoint to exchange the code / refresh. */
|
|
1037
|
-
tokenUrl: Schema3.String,
|
|
1038
|
-
/** Optional refresh endpoint if the spec declares one separately. */
|
|
1039
|
-
refreshUrl: Schema3.OptionFromOptional(Schema3.String),
|
|
1040
|
-
/** Declared scopes for this flow: `{ scope: description }`. */
|
|
1041
|
-
scopes: Schema3.Record(Schema3.String, Schema3.String)
|
|
1042
|
-
}) {
|
|
1043
|
-
};
|
|
1044
|
-
var PreviewOperation = class extends Schema3.Class("PreviewOperation")({
|
|
1045
|
-
operationId: Schema3.String,
|
|
1046
|
-
method: HttpMethod,
|
|
1047
|
-
path: Schema3.String,
|
|
1048
|
-
summary: Schema3.OptionFromOptional(Schema3.String),
|
|
1049
|
-
tags: Schema3.Array(Schema3.String),
|
|
1050
|
-
deprecated: Schema3.Boolean
|
|
1051
|
-
}) {
|
|
1052
|
-
};
|
|
1053
|
-
var SpecPreview = class extends Schema3.Class("SpecPreview")({
|
|
1054
|
-
title: Schema3.OptionFromOptional(Schema3.String),
|
|
1055
|
-
version: Schema3.OptionFromOptional(Schema3.String),
|
|
1056
|
-
/** Reuses ServerInfo from extraction */
|
|
1057
|
-
servers: Schema3.Array(ServerInfo),
|
|
1058
|
-
operationCount: Schema3.Number,
|
|
1059
|
-
/** Lightweight operation list for the add-source UI */
|
|
1060
|
-
operations: Schema3.Array(PreviewOperation),
|
|
1061
|
-
tags: Schema3.Array(Schema3.String),
|
|
1062
|
-
securitySchemes: Schema3.Array(SecurityScheme),
|
|
1063
|
-
/** Valid auth strategies (each is a set of schemes used together) */
|
|
1064
|
-
authStrategies: Schema3.Array(AuthStrategy),
|
|
1065
|
-
/** Pre-built header presets derived from auth strategies */
|
|
1066
|
-
headerPresets: Schema3.Array(HeaderPreset),
|
|
1067
|
-
/** OAuth2 presets — one per (oauth2 scheme × supported flow) combination */
|
|
1068
|
-
oauth2Presets: Schema3.Array(OAuth2Preset)
|
|
1069
|
-
}) {
|
|
1070
|
-
};
|
|
1071
|
-
var stringRecord = (value) => {
|
|
1072
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
1073
|
-
const out = {};
|
|
1074
|
-
for (const [k, v] of Object.entries(value)) {
|
|
1075
|
-
if (typeof v === "string") out[k] = v;
|
|
999
|
+
var normalizeStoredOAuth2 = (value) => {
|
|
1000
|
+
if (value == null) return {};
|
|
1001
|
+
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
1002
|
+
if (parsed && typeof parsed === "object" && "connectionSlot" in parsed) {
|
|
1003
|
+
return {
|
|
1004
|
+
oauth2: Schema4.decodeUnknownSync(OAuth2SourceConfig)(parsed)
|
|
1005
|
+
};
|
|
1076
1006
|
}
|
|
1077
|
-
|
|
1007
|
+
const legacy = decodeOAuth2(parsed);
|
|
1008
|
+
return {
|
|
1009
|
+
legacy,
|
|
1010
|
+
oauth2: new OAuth2SourceConfig({
|
|
1011
|
+
kind: "oauth2",
|
|
1012
|
+
securitySchemeName: legacy.securitySchemeName,
|
|
1013
|
+
flow: legacy.flow,
|
|
1014
|
+
tokenUrl: legacy.tokenUrl,
|
|
1015
|
+
authorizationUrl: legacy.authorizationUrl,
|
|
1016
|
+
clientIdSlot: oauth2ClientIdSlot(legacy.securitySchemeName),
|
|
1017
|
+
clientSecretSlot: legacy.clientSecretSecretId ? oauth2ClientSecretSlot(legacy.securitySchemeName) : null,
|
|
1018
|
+
connectionSlot: oauth2ConnectionSlot(legacy.securitySchemeName),
|
|
1019
|
+
scopes: [...legacy.scopes]
|
|
1020
|
+
})
|
|
1021
|
+
};
|
|
1078
1022
|
};
|
|
1079
|
-
var
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
const
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1023
|
+
var makeDefaultOpenapiStore = ({
|
|
1024
|
+
adapter,
|
|
1025
|
+
scopes
|
|
1026
|
+
}) => {
|
|
1027
|
+
const scopeIds = scopes.map((scope) => scope.id);
|
|
1028
|
+
const scopePrecedence = /* @__PURE__ */ new Map();
|
|
1029
|
+
scopeIds.forEach((scope, index) => scopePrecedence.set(scope, index));
|
|
1030
|
+
const scopeRank = (scopeId) => scopePrecedence.get(scopeId) ?? Infinity;
|
|
1031
|
+
const encodeSyntheticRowIdPart = (value) => encodeURIComponent(value);
|
|
1032
|
+
const sourceBindingRowId = (sourceId, sourceScopeId, slot, scopeId) => [
|
|
1033
|
+
"openapi-source-binding",
|
|
1034
|
+
encodeSyntheticRowIdPart(sourceScopeId),
|
|
1035
|
+
encodeSyntheticRowIdPart(sourceId),
|
|
1036
|
+
encodeSyntheticRowIdPart(slot),
|
|
1037
|
+
encodeSyntheticRowIdPart(scopeId)
|
|
1038
|
+
].join("::");
|
|
1039
|
+
const rowToSourceBinding = (row) => new OpenApiSourceBindingRef({
|
|
1040
|
+
sourceId: row.source_id,
|
|
1041
|
+
sourceScopeId: ScopeId2.make(row.source_scope_id),
|
|
1042
|
+
scopeId: ScopeId2.make(row.target_scope_id),
|
|
1043
|
+
slot: row.slot,
|
|
1044
|
+
value: decodeSourceBindingValue(asJsonObject(row.value)),
|
|
1045
|
+
createdAt: row.created_at instanceof Date ? row.created_at : new Date(row.created_at),
|
|
1046
|
+
updatedAt: row.updated_at instanceof Date ? row.updated_at : new Date(row.updated_at)
|
|
1047
|
+
});
|
|
1048
|
+
const validateBindingTarget = (params) => Effect4.gen(function* () {
|
|
1049
|
+
if (!scopeIds.includes(params.sourceScope)) {
|
|
1050
|
+
return yield* Effect4.fail(
|
|
1051
|
+
new StorageError({
|
|
1052
|
+
message: `OpenAPI source binding references source scope "${params.sourceScope}" which is not in the executor's scope stack [${scopeIds.join(", ")}].`,
|
|
1053
|
+
cause: void 0
|
|
1098
1054
|
})
|
|
1099
1055
|
);
|
|
1100
1056
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
const tokenUrl = typeof f.tokenUrl === "string" ? f.tokenUrl : null;
|
|
1107
|
-
if (tokenUrl) {
|
|
1108
|
-
clientCredentials = Option4.some(
|
|
1109
|
-
new OAuth2ClientCredentialsFlow({
|
|
1110
|
-
tokenUrl,
|
|
1111
|
-
refreshUrl: Option4.fromNullishOr(
|
|
1112
|
-
typeof f.refreshUrl === "string" ? f.refreshUrl : void 0
|
|
1113
|
-
),
|
|
1114
|
-
scopes: stringRecord(f.scopes)
|
|
1057
|
+
if (!scopeIds.includes(params.targetScope)) {
|
|
1058
|
+
return yield* Effect4.fail(
|
|
1059
|
+
new StorageError({
|
|
1060
|
+
message: `OpenAPI source binding targets scope "${params.targetScope}" which is not in the executor's scope stack [${scopeIds.join(", ")}].`,
|
|
1061
|
+
cause: void 0
|
|
1115
1062
|
})
|
|
1116
1063
|
);
|
|
1117
1064
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
const scheme = resolved;
|
|
1131
|
-
const type = scheme.type;
|
|
1132
|
-
if (!["http", "apiKey", "oauth2", "openIdConnect"].includes(type)) return [];
|
|
1133
|
-
return [
|
|
1134
|
-
new SecurityScheme({
|
|
1135
|
-
name,
|
|
1136
|
-
type,
|
|
1137
|
-
scheme: Option4.fromNullishOr(scheme.scheme),
|
|
1138
|
-
bearerFormat: Option4.fromNullishOr(scheme.bearerFormat),
|
|
1139
|
-
in: Option4.fromNullishOr(scheme.in),
|
|
1140
|
-
headerName: Option4.fromNullishOr(scheme.name),
|
|
1141
|
-
description: Option4.fromNullishOr(scheme.description),
|
|
1142
|
-
flows: type === "oauth2" ? extractFlows(scheme.flows) : Option4.none(),
|
|
1143
|
-
openIdConnectUrl: Option4.fromNullishOr(
|
|
1144
|
-
scheme.openIdConnectUrl
|
|
1145
|
-
)
|
|
1146
|
-
})
|
|
1147
|
-
];
|
|
1148
|
-
});
|
|
1149
|
-
var buildHeaderPresets = (schemes, strategies) => {
|
|
1150
|
-
const schemeMap = new Map(schemes.map((s) => [s.name, s]));
|
|
1151
|
-
return strategies.flatMap((strategy) => {
|
|
1152
|
-
const resolved = strategy.schemes.map((name) => schemeMap.get(name)).filter((s) => s !== void 0);
|
|
1153
|
-
if (resolved.length === 0) return [];
|
|
1154
|
-
const headers = {};
|
|
1155
|
-
const secretHeaders = [];
|
|
1156
|
-
const labelParts = [];
|
|
1157
|
-
for (const scheme of resolved) {
|
|
1158
|
-
if (scheme.type === "http" && Option4.getOrElse(scheme.scheme, () => "") === "bearer") {
|
|
1159
|
-
headers["Authorization"] = null;
|
|
1160
|
-
secretHeaders.push("Authorization");
|
|
1161
|
-
labelParts.push("Bearer Token");
|
|
1162
|
-
} else if (scheme.type === "http" && Option4.getOrElse(scheme.scheme, () => "") === "basic") {
|
|
1163
|
-
headers["Authorization"] = null;
|
|
1164
|
-
secretHeaders.push("Authorization");
|
|
1165
|
-
labelParts.push("Basic Auth");
|
|
1166
|
-
} else if (scheme.type === "apiKey" && Option4.getOrElse(scheme.in, () => "") === "header") {
|
|
1167
|
-
const headerName = Option4.getOrElse(scheme.headerName, () => scheme.name);
|
|
1168
|
-
headers[headerName] = null;
|
|
1169
|
-
secretHeaders.push(headerName);
|
|
1170
|
-
labelParts.push(scheme.name);
|
|
1171
|
-
} else if (scheme.type === "apiKey") {
|
|
1172
|
-
labelParts.push(`${scheme.name} (${Option4.getOrElse(scheme.in, () => "unknown")})`);
|
|
1173
|
-
} else {
|
|
1174
|
-
labelParts.push(scheme.name);
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
if (Object.keys(headers).length === 0 && resolved.length > 0) {
|
|
1178
|
-
return [new HeaderPreset({ label: labelParts.join(" + "), headers: {}, secretHeaders: [] })];
|
|
1179
|
-
}
|
|
1180
|
-
return [new HeaderPreset({ label: labelParts.join(" + "), headers, secretHeaders })];
|
|
1181
|
-
});
|
|
1182
|
-
};
|
|
1183
|
-
var buildOAuth2Presets = (schemes) => {
|
|
1184
|
-
const presets = [];
|
|
1185
|
-
for (const scheme of schemes) {
|
|
1186
|
-
if (scheme.type !== "oauth2") continue;
|
|
1187
|
-
if (Option4.isNone(scheme.flows)) continue;
|
|
1188
|
-
const flows = scheme.flows.value;
|
|
1189
|
-
if (Option4.isSome(flows.authorizationCode)) {
|
|
1190
|
-
const flow = flows.authorizationCode.value;
|
|
1191
|
-
presets.push(
|
|
1192
|
-
new OAuth2Preset({
|
|
1193
|
-
label: `OAuth2 Authorization Code \xB7 ${scheme.name}`,
|
|
1194
|
-
securitySchemeName: scheme.name,
|
|
1195
|
-
flow: "authorizationCode",
|
|
1196
|
-
authorizationUrl: Option4.some(flow.authorizationUrl),
|
|
1197
|
-
tokenUrl: flow.tokenUrl,
|
|
1198
|
-
refreshUrl: flow.refreshUrl,
|
|
1199
|
-
scopes: flow.scopes
|
|
1065
|
+
const source = yield* adapter.findOne({
|
|
1066
|
+
model: "openapi_source",
|
|
1067
|
+
where: [
|
|
1068
|
+
{ field: "id", value: params.sourceId },
|
|
1069
|
+
{ field: "scope_id", value: params.sourceScope }
|
|
1070
|
+
]
|
|
1071
|
+
});
|
|
1072
|
+
if (!source) {
|
|
1073
|
+
return yield* Effect4.fail(
|
|
1074
|
+
new StorageError({
|
|
1075
|
+
message: `OpenAPI source "${params.sourceId}" does not exist at scope "${params.sourceScope}"`,
|
|
1076
|
+
cause: void 0
|
|
1200
1077
|
})
|
|
1201
1078
|
);
|
|
1202
1079
|
}
|
|
1203
|
-
if (
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
securitySchemeName: scheme.name,
|
|
1209
|
-
flow: "clientCredentials",
|
|
1210
|
-
authorizationUrl: Option4.none(),
|
|
1211
|
-
tokenUrl: flow.tokenUrl,
|
|
1212
|
-
refreshUrl: flow.refreshUrl,
|
|
1213
|
-
scopes: flow.scopes
|
|
1080
|
+
if (scopeRank(params.targetScope) > scopeRank(params.sourceScope)) {
|
|
1081
|
+
return yield* Effect4.fail(
|
|
1082
|
+
new StorageError({
|
|
1083
|
+
message: `OpenAPI source bindings for "${params.sourceId}" cannot be written at outer scope "${params.targetScope}" because the base source lives at "${params.sourceScope}"`,
|
|
1084
|
+
cause: void 0
|
|
1214
1085
|
})
|
|
1215
1086
|
);
|
|
1216
1087
|
}
|
|
1217
|
-
|
|
1218
|
-
return presets;
|
|
1219
|
-
};
|
|
1220
|
-
var collectTags = (result) => {
|
|
1221
|
-
const tagSet = /* @__PURE__ */ new Set();
|
|
1222
|
-
for (const op of result.operations) {
|
|
1223
|
-
for (const tag of op.tags) tagSet.add(tag);
|
|
1224
|
-
}
|
|
1225
|
-
return [...tagSet].sort();
|
|
1226
|
-
};
|
|
1227
|
-
var previewSpec = Effect4.fn("OpenApi.previewSpec")(function* (input) {
|
|
1228
|
-
const specText = yield* resolveSpecText(input);
|
|
1229
|
-
const doc = yield* parse(specText);
|
|
1230
|
-
const result = yield* extract(doc);
|
|
1231
|
-
const resolver = new DocResolver(doc);
|
|
1232
|
-
const securitySchemes = extractSecuritySchemes(
|
|
1233
|
-
doc.components?.securitySchemes ?? {},
|
|
1234
|
-
resolver
|
|
1235
|
-
);
|
|
1236
|
-
const rawSecurity = doc.security ?? [];
|
|
1237
|
-
const declaredStrategies = rawSecurity.map(
|
|
1238
|
-
(entry) => new AuthStrategy({ schemes: Object.keys(entry) })
|
|
1239
|
-
);
|
|
1240
|
-
const authStrategies = declaredStrategies.length > 0 ? declaredStrategies : securitySchemes.map((scheme) => new AuthStrategy({ schemes: [scheme.name] }));
|
|
1241
|
-
return new SpecPreview({
|
|
1242
|
-
title: result.title,
|
|
1243
|
-
version: result.version,
|
|
1244
|
-
servers: result.servers,
|
|
1245
|
-
operationCount: result.operations.length,
|
|
1246
|
-
operations: result.operations.map(
|
|
1247
|
-
(op) => new PreviewOperation({
|
|
1248
|
-
operationId: op.operationId,
|
|
1249
|
-
method: op.method,
|
|
1250
|
-
path: op.pathTemplate,
|
|
1251
|
-
summary: op.summary,
|
|
1252
|
-
tags: op.tags,
|
|
1253
|
-
deprecated: op.deprecated
|
|
1254
|
-
})
|
|
1255
|
-
),
|
|
1256
|
-
tags: collectTags(result),
|
|
1257
|
-
securitySchemes,
|
|
1258
|
-
authStrategies,
|
|
1259
|
-
headerPresets: buildHeaderPresets(securitySchemes, authStrategies),
|
|
1260
|
-
oauth2Presets: buildOAuth2Presets(securitySchemes)
|
|
1261
|
-
});
|
|
1262
|
-
});
|
|
1263
|
-
|
|
1264
|
-
// src/sdk/store.ts
|
|
1265
|
-
import { Effect as Effect5, Schema as Schema4 } from "effect";
|
|
1266
|
-
import {
|
|
1267
|
-
defineSchema,
|
|
1268
|
-
ScopeId as ScopeId2,
|
|
1269
|
-
StorageError
|
|
1270
|
-
} from "@executor-js/sdk/core";
|
|
1271
|
-
var openapiSchema = defineSchema({
|
|
1272
|
-
openapi_source: {
|
|
1273
|
-
fields: {
|
|
1274
|
-
id: { type: "string", required: true },
|
|
1275
|
-
scope_id: { type: "string", required: true, index: true },
|
|
1276
|
-
name: { type: "string", required: true },
|
|
1277
|
-
spec: { type: "string", required: true },
|
|
1278
|
-
// Origin URL the spec was fetched from. Set when `addSpec` was
|
|
1279
|
-
// invoked with an http(s) URL; null when the caller passed raw
|
|
1280
|
-
// spec text. Drives `canRefresh` on the core source row and
|
|
1281
|
-
// is the address re-fetched on `refreshSource`.
|
|
1282
|
-
source_url: { type: "string", required: false },
|
|
1283
|
-
base_url: { type: "string", required: false },
|
|
1284
|
-
headers: { type: "json", required: false },
|
|
1285
|
-
query_params: { type: "json", required: false },
|
|
1286
|
-
oauth2: { type: "json", required: false },
|
|
1287
|
-
invocation_config: { type: "json", required: true }
|
|
1288
|
-
}
|
|
1289
|
-
},
|
|
1290
|
-
openapi_operation: {
|
|
1291
|
-
fields: {
|
|
1292
|
-
id: { type: "string", required: true },
|
|
1293
|
-
scope_id: { type: "string", required: true, index: true },
|
|
1294
|
-
source_id: { type: "string", required: true, index: true },
|
|
1295
|
-
binding: { type: "json", required: true }
|
|
1296
|
-
}
|
|
1297
|
-
},
|
|
1298
|
-
openapi_source_binding: {
|
|
1299
|
-
fields: {
|
|
1300
|
-
id: { type: "string", required: true },
|
|
1301
|
-
source_id: { type: "string", required: true, index: true },
|
|
1302
|
-
source_scope_id: { type: "string", required: true, index: true },
|
|
1303
|
-
// Intentionally NOT named `scope_id`: this row is visible across
|
|
1304
|
-
// scope stacks and is filtered manually by source/target scope.
|
|
1305
|
-
// The target scope is credential ownership data, not adapter row
|
|
1306
|
-
// ownership. Source owners must be able to delete all descendant
|
|
1307
|
-
// bindings when a shared source is removed.
|
|
1308
|
-
target_scope_id: { type: "string", required: true, index: true },
|
|
1309
|
-
slot: { type: "string", required: true, index: true },
|
|
1310
|
-
value: { type: "json", required: true },
|
|
1311
|
-
created_at: { type: "date", required: true },
|
|
1312
|
-
updated_at: { type: "date", required: true }
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
});
|
|
1316
|
-
var StoredSourceSchema = class extends Schema4.Class(
|
|
1317
|
-
"OpenApiStoredSource"
|
|
1318
|
-
)({
|
|
1319
|
-
namespace: Schema4.String,
|
|
1320
|
-
name: Schema4.String,
|
|
1321
|
-
config: Schema4.Struct({
|
|
1322
|
-
spec: Schema4.String,
|
|
1323
|
-
sourceUrl: Schema4.optional(Schema4.String),
|
|
1324
|
-
baseUrl: Schema4.optional(Schema4.String),
|
|
1325
|
-
namespace: Schema4.optional(Schema4.String),
|
|
1326
|
-
headers: Schema4.optional(
|
|
1327
|
-
Schema4.Record(Schema4.String, ConfiguredHeaderValue)
|
|
1328
|
-
),
|
|
1329
|
-
queryParams: Schema4.optional(
|
|
1330
|
-
Schema4.Record(Schema4.String, HeaderValue)
|
|
1331
|
-
),
|
|
1332
|
-
specFetchCredentials: Schema4.optional(
|
|
1333
|
-
Schema4.Struct({
|
|
1334
|
-
headers: Schema4.optional(
|
|
1335
|
-
Schema4.Record(Schema4.String, HeaderValue)
|
|
1336
|
-
),
|
|
1337
|
-
queryParams: Schema4.optional(
|
|
1338
|
-
Schema4.Record(Schema4.String, HeaderValue)
|
|
1339
|
-
)
|
|
1340
|
-
})
|
|
1341
|
-
),
|
|
1342
|
-
// Canonical source-owned OAuth config. Concrete client credentials
|
|
1343
|
-
// and connection ids live in OpenAPI-owned scoped binding rows.
|
|
1344
|
-
oauth2: Schema4.optional(OAuth2SourceConfig)
|
|
1345
|
-
})
|
|
1346
|
-
}) {
|
|
1347
|
-
};
|
|
1348
|
-
var encodeBinding = Schema4.encodeSync(OperationBinding);
|
|
1349
|
-
var decodeBinding = Schema4.decodeUnknownSync(OperationBinding);
|
|
1350
|
-
var decodeOAuth2 = Schema4.decodeUnknownSync(OAuth2Auth);
|
|
1351
|
-
var encodeOAuth2SourceConfig = Schema4.encodeSync(OAuth2SourceConfig);
|
|
1352
|
-
var encodeSourceBindingValue = Schema4.encodeSync(OpenApiSourceBindingValue);
|
|
1353
|
-
var decodeSourceBindingValue = Schema4.decodeUnknownSync(
|
|
1354
|
-
OpenApiSourceBindingValue
|
|
1355
|
-
);
|
|
1356
|
-
var asJsonObject = (value) => {
|
|
1357
|
-
if (value == null) return {};
|
|
1358
|
-
if (typeof value === "string")
|
|
1359
|
-
return JSON.parse(value);
|
|
1360
|
-
return value;
|
|
1361
|
-
};
|
|
1362
|
-
var toJsonRecord = (value) => value;
|
|
1363
|
-
var toConfiguredHeaderBinding = (value) => new ConfiguredHeaderBinding({
|
|
1364
|
-
kind: "binding",
|
|
1365
|
-
slot: String(value.slot ?? ""),
|
|
1366
|
-
...typeof value.prefix === "string" ? { prefix: value.prefix } : {}
|
|
1367
|
-
});
|
|
1368
|
-
var decodeHeaders = (value) => {
|
|
1369
|
-
if (value == null) return {};
|
|
1370
|
-
if (typeof value === "string")
|
|
1371
|
-
return JSON.parse(value);
|
|
1372
|
-
return value;
|
|
1373
|
-
};
|
|
1374
|
-
var slugifySlotPart = (value) => value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "default";
|
|
1375
|
-
var headerBindingSlot = (headerName) => `header:${slugifySlotPart(headerName)}`;
|
|
1376
|
-
var oauth2ClientIdSlot = (securitySchemeName) => `oauth2:${slugifySlotPart(securitySchemeName)}:client-id`;
|
|
1377
|
-
var oauth2ClientSecretSlot = (securitySchemeName) => `oauth2:${slugifySlotPart(securitySchemeName)}:client-secret`;
|
|
1378
|
-
var oauth2ConnectionSlot = (securitySchemeName) => `oauth2:${slugifySlotPart(securitySchemeName)}:connection`;
|
|
1379
|
-
var normalizeStoredHeaders = (value) => {
|
|
1380
|
-
const raw = decodeHeaders(value);
|
|
1381
|
-
const headers = {};
|
|
1382
|
-
const legacy = {};
|
|
1383
|
-
for (const [name, header] of Object.entries(raw)) {
|
|
1384
|
-
if (typeof header === "string") {
|
|
1385
|
-
headers[name] = header;
|
|
1386
|
-
legacy[name] = header;
|
|
1387
|
-
continue;
|
|
1388
|
-
}
|
|
1389
|
-
if (header && typeof header === "object" && "kind" in header && header.kind === "binding") {
|
|
1390
|
-
headers[name] = toConfiguredHeaderBinding(header);
|
|
1391
|
-
continue;
|
|
1392
|
-
}
|
|
1393
|
-
legacy[name] = header;
|
|
1394
|
-
headers[name] = new ConfiguredHeaderBinding({
|
|
1395
|
-
kind: "binding",
|
|
1396
|
-
slot: headerBindingSlot(name),
|
|
1397
|
-
prefix: header.prefix
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
return { headers, legacy };
|
|
1401
|
-
};
|
|
1402
|
-
var normalizeStoredOAuth2 = (value) => {
|
|
1403
|
-
if (value == null) return {};
|
|
1404
|
-
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
1405
|
-
if (parsed && typeof parsed === "object" && "connectionSlot" in parsed) {
|
|
1406
|
-
return {
|
|
1407
|
-
oauth2: Schema4.decodeUnknownSync(OAuth2SourceConfig)(parsed)
|
|
1408
|
-
};
|
|
1409
|
-
}
|
|
1410
|
-
const legacy = decodeOAuth2(parsed);
|
|
1411
|
-
return {
|
|
1412
|
-
legacy,
|
|
1413
|
-
oauth2: new OAuth2SourceConfig({
|
|
1414
|
-
kind: "oauth2",
|
|
1415
|
-
securitySchemeName: legacy.securitySchemeName,
|
|
1416
|
-
flow: legacy.flow,
|
|
1417
|
-
tokenUrl: legacy.tokenUrl,
|
|
1418
|
-
authorizationUrl: legacy.authorizationUrl,
|
|
1419
|
-
clientIdSlot: oauth2ClientIdSlot(legacy.securitySchemeName),
|
|
1420
|
-
clientSecretSlot: legacy.clientSecretSecretId ? oauth2ClientSecretSlot(legacy.securitySchemeName) : null,
|
|
1421
|
-
connectionSlot: oauth2ConnectionSlot(legacy.securitySchemeName),
|
|
1422
|
-
scopes: [...legacy.scopes]
|
|
1423
|
-
})
|
|
1424
|
-
};
|
|
1425
|
-
};
|
|
1426
|
-
var makeDefaultOpenapiStore = ({
|
|
1427
|
-
adapter,
|
|
1428
|
-
scopes
|
|
1429
|
-
}) => {
|
|
1430
|
-
const scopeIds = scopes.map((scope) => scope.id);
|
|
1431
|
-
const scopePrecedence = /* @__PURE__ */ new Map();
|
|
1432
|
-
scopeIds.forEach((scope, index) => scopePrecedence.set(scope, index));
|
|
1433
|
-
const scopeRank = (scopeId) => scopePrecedence.get(scopeId) ?? Infinity;
|
|
1434
|
-
const encodeSyntheticRowIdPart = (value) => encodeURIComponent(value);
|
|
1435
|
-
const sourceBindingRowId = (sourceId, sourceScopeId, slot, scopeId) => [
|
|
1436
|
-
"openapi-source-binding",
|
|
1437
|
-
encodeSyntheticRowIdPart(sourceScopeId),
|
|
1438
|
-
encodeSyntheticRowIdPart(sourceId),
|
|
1439
|
-
encodeSyntheticRowIdPart(slot),
|
|
1440
|
-
encodeSyntheticRowIdPart(scopeId)
|
|
1441
|
-
].join("::");
|
|
1442
|
-
const rowToSourceBinding = (row) => new OpenApiSourceBindingRef({
|
|
1443
|
-
sourceId: row.source_id,
|
|
1444
|
-
sourceScopeId: ScopeId2.make(row.source_scope_id),
|
|
1445
|
-
scopeId: ScopeId2.make(row.target_scope_id),
|
|
1446
|
-
slot: row.slot,
|
|
1447
|
-
value: decodeSourceBindingValue(asJsonObject(row.value)),
|
|
1448
|
-
createdAt: row.created_at instanceof Date ? row.created_at : new Date(row.created_at),
|
|
1449
|
-
updatedAt: row.updated_at instanceof Date ? row.updated_at : new Date(row.updated_at)
|
|
1450
|
-
});
|
|
1451
|
-
const validateBindingTarget = (params) => Effect5.gen(function* () {
|
|
1452
|
-
if (!scopeIds.includes(params.sourceScope)) {
|
|
1453
|
-
return yield* Effect5.fail(
|
|
1454
|
-
new StorageError({
|
|
1455
|
-
message: `OpenAPI source binding references source scope "${params.sourceScope}" which is not in the executor's scope stack [${scopeIds.join(", ")}].`,
|
|
1456
|
-
cause: void 0
|
|
1457
|
-
})
|
|
1458
|
-
);
|
|
1459
|
-
}
|
|
1460
|
-
if (!scopeIds.includes(params.targetScope)) {
|
|
1461
|
-
return yield* Effect5.fail(
|
|
1462
|
-
new StorageError({
|
|
1463
|
-
message: `OpenAPI source binding targets scope "${params.targetScope}" which is not in the executor's scope stack [${scopeIds.join(", ")}].`,
|
|
1464
|
-
cause: void 0
|
|
1465
|
-
})
|
|
1466
|
-
);
|
|
1467
|
-
}
|
|
1468
|
-
const source = yield* adapter.findOne({
|
|
1469
|
-
model: "openapi_source",
|
|
1470
|
-
where: [
|
|
1471
|
-
{ field: "id", value: params.sourceId },
|
|
1472
|
-
{ field: "scope_id", value: params.sourceScope }
|
|
1473
|
-
]
|
|
1474
|
-
});
|
|
1475
|
-
if (!source) {
|
|
1476
|
-
return yield* Effect5.fail(
|
|
1477
|
-
new StorageError({
|
|
1478
|
-
message: `OpenAPI source "${params.sourceId}" does not exist at scope "${params.sourceScope}"`,
|
|
1479
|
-
cause: void 0
|
|
1480
|
-
})
|
|
1481
|
-
);
|
|
1482
|
-
}
|
|
1483
|
-
if (scopeRank(params.targetScope) > scopeRank(params.sourceScope)) {
|
|
1484
|
-
return yield* Effect5.fail(
|
|
1485
|
-
new StorageError({
|
|
1486
|
-
message: `OpenAPI source bindings for "${params.sourceId}" cannot be written at outer scope "${params.targetScope}" because the base source lives at "${params.sourceScope}"`,
|
|
1487
|
-
cause: void 0
|
|
1488
|
-
})
|
|
1489
|
-
);
|
|
1490
|
-
}
|
|
1491
|
-
return source;
|
|
1088
|
+
return source;
|
|
1492
1089
|
});
|
|
1493
1090
|
const rowToSource = (row) => {
|
|
1494
1091
|
const normalizedHeaders = normalizeStoredHeaders(row.headers);
|
|
@@ -1520,7 +1117,7 @@ var makeDefaultOpenapiStore = ({
|
|
|
1520
1117
|
typeof row.binding === "string" ? JSON.parse(row.binding) : row.binding
|
|
1521
1118
|
)
|
|
1522
1119
|
});
|
|
1523
|
-
const deleteSource = (namespace, scope, options) =>
|
|
1120
|
+
const deleteSource = (namespace, scope, options) => Effect4.gen(function* () {
|
|
1524
1121
|
yield* adapter.deleteMany({
|
|
1525
1122
|
model: "openapi_operation",
|
|
1526
1123
|
where: [
|
|
@@ -1546,7 +1143,7 @@ var makeDefaultOpenapiStore = ({
|
|
|
1546
1143
|
}
|
|
1547
1144
|
});
|
|
1548
1145
|
return {
|
|
1549
|
-
upsertSource: (input, operations) =>
|
|
1146
|
+
upsertSource: (input, operations) => Effect4.gen(function* () {
|
|
1550
1147
|
yield* deleteSource(input.namespace, input.scope);
|
|
1551
1148
|
yield* adapter.create({
|
|
1552
1149
|
model: "openapi_source",
|
|
@@ -1590,7 +1187,7 @@ var makeDefaultOpenapiStore = ({
|
|
|
1590
1187
|
});
|
|
1591
1188
|
}
|
|
1592
1189
|
}),
|
|
1593
|
-
updateSourceMeta: (namespace, scope, patch) =>
|
|
1190
|
+
updateSourceMeta: (namespace, scope, patch) => Effect4.gen(function* () {
|
|
1594
1191
|
const existingRow = yield* adapter.findOne({
|
|
1595
1192
|
model: "openapi_source",
|
|
1596
1193
|
where: [
|
|
@@ -1636,24 +1233,24 @@ var makeDefaultOpenapiStore = ({
|
|
|
1636
1233
|
{ field: "id", value: namespace },
|
|
1637
1234
|
{ field: "scope_id", value: scope }
|
|
1638
1235
|
]
|
|
1639
|
-
}).pipe(
|
|
1640
|
-
listSources: () => adapter.findMany({ model: "openapi_source" }).pipe(
|
|
1236
|
+
}).pipe(Effect4.map((row) => row ? rowToSource(row) : null)),
|
|
1237
|
+
listSources: () => adapter.findMany({ model: "openapi_source" }).pipe(Effect4.map((rows) => rows.map(rowToSource))),
|
|
1641
1238
|
getOperationByToolId: (toolId, scope) => adapter.findOne({
|
|
1642
1239
|
model: "openapi_operation",
|
|
1643
1240
|
where: [
|
|
1644
1241
|
{ field: "id", value: toolId },
|
|
1645
1242
|
{ field: "scope_id", value: scope }
|
|
1646
1243
|
]
|
|
1647
|
-
}).pipe(
|
|
1244
|
+
}).pipe(Effect4.map((row) => row ? rowToOperation(row) : null)),
|
|
1648
1245
|
listOperationsBySource: (sourceId, scope) => adapter.findMany({
|
|
1649
1246
|
model: "openapi_operation",
|
|
1650
1247
|
where: [
|
|
1651
1248
|
{ field: "source_id", value: sourceId },
|
|
1652
1249
|
{ field: "scope_id", value: scope }
|
|
1653
1250
|
]
|
|
1654
|
-
}).pipe(
|
|
1251
|
+
}).pipe(Effect4.map((rows) => rows.map(rowToOperation))),
|
|
1655
1252
|
removeSource: (namespace, scope) => deleteSource(namespace, scope, { includeBindings: true }),
|
|
1656
|
-
listSourceBindings: (sourceId, sourceScope) =>
|
|
1253
|
+
listSourceBindings: (sourceId, sourceScope) => Effect4.gen(function* () {
|
|
1657
1254
|
yield* validateBindingTarget({
|
|
1658
1255
|
sourceId,
|
|
1659
1256
|
sourceScope,
|
|
@@ -1673,7 +1270,7 @@ var makeDefaultOpenapiStore = ({
|
|
|
1673
1270
|
(a, b) => scopeRank(a.target_scope_id) - scopeRank(b.target_scope_id)
|
|
1674
1271
|
).map(rowToSourceBinding);
|
|
1675
1272
|
}),
|
|
1676
|
-
resolveSourceBinding: (sourceId, sourceScope, slot) =>
|
|
1273
|
+
resolveSourceBinding: (sourceId, sourceScope, slot) => Effect4.gen(function* () {
|
|
1677
1274
|
yield* validateBindingTarget({
|
|
1678
1275
|
sourceId,
|
|
1679
1276
|
sourceScope,
|
|
@@ -1695,7 +1292,7 @@ var makeDefaultOpenapiStore = ({
|
|
|
1695
1292
|
)[0];
|
|
1696
1293
|
return row ? rowToSourceBinding(row) : null;
|
|
1697
1294
|
}),
|
|
1698
|
-
setSourceBinding: (input) =>
|
|
1295
|
+
setSourceBinding: (input) => Effect4.gen(function* () {
|
|
1699
1296
|
yield* validateBindingTarget({
|
|
1700
1297
|
sourceId: input.sourceId,
|
|
1701
1298
|
sourceScope: input.sourceScope,
|
|
@@ -1707,60 +1304,689 @@ var makeDefaultOpenapiStore = ({
|
|
|
1707
1304
|
input.slot,
|
|
1708
1305
|
input.scope
|
|
1709
1306
|
);
|
|
1710
|
-
const now = /* @__PURE__ */ new Date();
|
|
1711
|
-
yield* adapter.delete({
|
|
1712
|
-
model: "openapi_source_binding",
|
|
1713
|
-
where: [{ field: "id", value: id }]
|
|
1714
|
-
});
|
|
1715
|
-
yield* adapter.create({
|
|
1716
|
-
model: "openapi_source_binding",
|
|
1717
|
-
data: {
|
|
1718
|
-
id,
|
|
1719
|
-
source_id: input.sourceId,
|
|
1720
|
-
source_scope_id: input.sourceScope,
|
|
1721
|
-
target_scope_id: input.scope,
|
|
1722
|
-
slot: input.slot,
|
|
1723
|
-
value: toJsonRecord(encodeSourceBindingValue(input.value)),
|
|
1724
|
-
created_at: now,
|
|
1725
|
-
updated_at: now
|
|
1726
|
-
},
|
|
1727
|
-
forceAllowId: true
|
|
1728
|
-
});
|
|
1729
|
-
return new OpenApiSourceBindingRef({
|
|
1730
|
-
sourceId: input.sourceId,
|
|
1731
|
-
sourceScopeId: input.sourceScope,
|
|
1732
|
-
scopeId: input.scope,
|
|
1733
|
-
slot: input.slot,
|
|
1734
|
-
value: input.value,
|
|
1735
|
-
createdAt: now,
|
|
1736
|
-
updatedAt: now
|
|
1737
|
-
});
|
|
1738
|
-
}),
|
|
1739
|
-
removeSourceBinding: (sourceId, sourceScope, slot, scope) =>
|
|
1740
|
-
yield* validateBindingTarget({
|
|
1741
|
-
sourceId,
|
|
1742
|
-
sourceScope,
|
|
1743
|
-
targetScope: scope
|
|
1744
|
-
});
|
|
1745
|
-
yield* adapter.delete({
|
|
1746
|
-
model: "openapi_source_binding",
|
|
1747
|
-
where: [
|
|
1748
|
-
{
|
|
1749
|
-
field: "id",
|
|
1750
|
-
value: sourceBindingRowId(sourceId, sourceScope, slot, scope)
|
|
1751
|
-
}
|
|
1752
|
-
]
|
|
1753
|
-
});
|
|
1307
|
+
const now = /* @__PURE__ */ new Date();
|
|
1308
|
+
yield* adapter.delete({
|
|
1309
|
+
model: "openapi_source_binding",
|
|
1310
|
+
where: [{ field: "id", value: id }]
|
|
1311
|
+
});
|
|
1312
|
+
yield* adapter.create({
|
|
1313
|
+
model: "openapi_source_binding",
|
|
1314
|
+
data: {
|
|
1315
|
+
id,
|
|
1316
|
+
source_id: input.sourceId,
|
|
1317
|
+
source_scope_id: input.sourceScope,
|
|
1318
|
+
target_scope_id: input.scope,
|
|
1319
|
+
slot: input.slot,
|
|
1320
|
+
value: toJsonRecord(encodeSourceBindingValue(input.value)),
|
|
1321
|
+
created_at: now,
|
|
1322
|
+
updated_at: now
|
|
1323
|
+
},
|
|
1324
|
+
forceAllowId: true
|
|
1325
|
+
});
|
|
1326
|
+
return new OpenApiSourceBindingRef({
|
|
1327
|
+
sourceId: input.sourceId,
|
|
1328
|
+
sourceScopeId: input.sourceScope,
|
|
1329
|
+
scopeId: input.scope,
|
|
1330
|
+
slot: input.slot,
|
|
1331
|
+
value: input.value,
|
|
1332
|
+
createdAt: now,
|
|
1333
|
+
updatedAt: now
|
|
1334
|
+
});
|
|
1335
|
+
}),
|
|
1336
|
+
removeSourceBinding: (sourceId, sourceScope, slot, scope) => Effect4.gen(function* () {
|
|
1337
|
+
yield* validateBindingTarget({
|
|
1338
|
+
sourceId,
|
|
1339
|
+
sourceScope,
|
|
1340
|
+
targetScope: scope
|
|
1341
|
+
});
|
|
1342
|
+
yield* adapter.delete({
|
|
1343
|
+
model: "openapi_source_binding",
|
|
1344
|
+
where: [
|
|
1345
|
+
{
|
|
1346
|
+
field: "id",
|
|
1347
|
+
value: sourceBindingRowId(sourceId, sourceScope, slot, scope)
|
|
1348
|
+
}
|
|
1349
|
+
]
|
|
1350
|
+
});
|
|
1351
|
+
})
|
|
1352
|
+
};
|
|
1353
|
+
};
|
|
1354
|
+
|
|
1355
|
+
// src/sdk/invoke.ts
|
|
1356
|
+
import { Effect as Effect5, Layer, Option as Option4 } from "effect";
|
|
1357
|
+
import { HttpClient as HttpClient2, HttpClientRequest as HttpClientRequest2 } from "effect/unstable/http";
|
|
1358
|
+
var CONTAINER_KEYS = {
|
|
1359
|
+
path: ["path", "pathParams", "params"],
|
|
1360
|
+
query: ["query", "queryParams", "params"],
|
|
1361
|
+
header: ["headers", "header"],
|
|
1362
|
+
cookie: ["cookies", "cookie"]
|
|
1363
|
+
};
|
|
1364
|
+
var readParamValue = (args, param) => {
|
|
1365
|
+
const direct = args[param.name];
|
|
1366
|
+
if (direct !== void 0) return direct;
|
|
1367
|
+
for (const key of CONTAINER_KEYS[param.location] ?? []) {
|
|
1368
|
+
const container = args[key];
|
|
1369
|
+
if (typeof container === "object" && container !== null && !Array.isArray(container)) {
|
|
1370
|
+
const nested = container[param.name];
|
|
1371
|
+
if (nested !== void 0) return nested;
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
return void 0;
|
|
1375
|
+
};
|
|
1376
|
+
var resolvePath = Effect5.fn("OpenApi.resolvePath")(function* (pathTemplate, args, parameters) {
|
|
1377
|
+
let resolved = pathTemplate;
|
|
1378
|
+
for (const param of parameters) {
|
|
1379
|
+
if (param.location !== "path") continue;
|
|
1380
|
+
const value = readParamValue(args, param);
|
|
1381
|
+
if (value === void 0 || value === null) {
|
|
1382
|
+
if (param.required) {
|
|
1383
|
+
return yield* new OpenApiInvocationError({
|
|
1384
|
+
message: `Missing required path parameter: ${param.name}`,
|
|
1385
|
+
statusCode: Option4.none()
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
resolved = resolved.replaceAll(`{${param.name}}`, encodeURIComponent(String(value)));
|
|
1391
|
+
}
|
|
1392
|
+
const remaining = [...resolved.matchAll(/\{([^{}]+)\}/g)].map((m) => m[1]).filter((v) => typeof v === "string");
|
|
1393
|
+
for (const name of remaining) {
|
|
1394
|
+
const value = args[name];
|
|
1395
|
+
if (value !== void 0 && value !== null) {
|
|
1396
|
+
resolved = resolved.replaceAll(`{${name}}`, encodeURIComponent(String(value)));
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
const unresolved = [...resolved.matchAll(/\{([^{}]+)\}/g)].map((m) => m[1]).filter((v) => typeof v === "string");
|
|
1400
|
+
if (unresolved.length > 0) {
|
|
1401
|
+
return yield* new OpenApiInvocationError({
|
|
1402
|
+
message: `Unresolved path parameters: ${[...new Set(unresolved)].join(", ")}`,
|
|
1403
|
+
statusCode: Option4.none()
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
return resolved;
|
|
1407
|
+
});
|
|
1408
|
+
var resolveHeaders = (headers, secrets) => {
|
|
1409
|
+
const entries = Object.entries(headers);
|
|
1410
|
+
const secretCount = entries.reduce(
|
|
1411
|
+
(acc, [, value]) => typeof value === "string" ? acc : acc + 1,
|
|
1412
|
+
0
|
|
1413
|
+
);
|
|
1414
|
+
return Effect5.gen(function* () {
|
|
1415
|
+
const values = yield* Effect5.all(
|
|
1416
|
+
entries.map(
|
|
1417
|
+
([name, value]) => typeof value === "string" ? Effect5.succeed({ name, value }) : secrets.get(value.secretId).pipe(
|
|
1418
|
+
Effect5.mapError(
|
|
1419
|
+
(err) => "_tag" in err && err._tag === "SecretOwnedByConnectionError" ? new OpenApiInvocationError({
|
|
1420
|
+
message: `Failed to resolve secret "${value.secretId}" for header "${name}"`,
|
|
1421
|
+
statusCode: Option4.none()
|
|
1422
|
+
}) : err
|
|
1423
|
+
),
|
|
1424
|
+
Effect5.flatMap(
|
|
1425
|
+
(secret) => secret === null ? Effect5.fail(
|
|
1426
|
+
new OpenApiInvocationError({
|
|
1427
|
+
message: `Failed to resolve secret "${value.secretId}" for header "${name}"`,
|
|
1428
|
+
statusCode: Option4.none()
|
|
1429
|
+
})
|
|
1430
|
+
) : Effect5.succeed({
|
|
1431
|
+
name,
|
|
1432
|
+
value: value.prefix ? `${value.prefix}${secret}` : secret
|
|
1433
|
+
})
|
|
1434
|
+
)
|
|
1435
|
+
)
|
|
1436
|
+
),
|
|
1437
|
+
{ concurrency: "unbounded" }
|
|
1438
|
+
);
|
|
1439
|
+
const resolved = {};
|
|
1440
|
+
for (const { name, value } of values) resolved[name] = value;
|
|
1441
|
+
return resolved;
|
|
1442
|
+
}).pipe(
|
|
1443
|
+
Effect5.withSpan("plugin.openapi.secret.resolve", {
|
|
1444
|
+
attributes: {
|
|
1445
|
+
"plugin.openapi.headers.total": entries.length,
|
|
1446
|
+
"plugin.openapi.headers.secret_count": secretCount
|
|
1447
|
+
}
|
|
1448
|
+
})
|
|
1449
|
+
);
|
|
1450
|
+
};
|
|
1451
|
+
var applyHeaders = (request, headers) => {
|
|
1452
|
+
let req = request;
|
|
1453
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
1454
|
+
req = HttpClientRequest2.setHeader(req, name, value);
|
|
1455
|
+
}
|
|
1456
|
+
return req;
|
|
1457
|
+
};
|
|
1458
|
+
var normalizeContentType = (ct) => ct?.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
1459
|
+
var isJsonContentType = (ct) => {
|
|
1460
|
+
const normalized = normalizeContentType(ct);
|
|
1461
|
+
if (!normalized) return false;
|
|
1462
|
+
return normalized === "application/json" || normalized.includes("+json") || normalized.includes("json");
|
|
1463
|
+
};
|
|
1464
|
+
var isFormUrlEncoded = (ct) => normalizeContentType(ct) === "application/x-www-form-urlencoded";
|
|
1465
|
+
var isMultipartFormData = (ct) => normalizeContentType(ct).startsWith("multipart/form-data");
|
|
1466
|
+
var isXmlContentType = (ct) => {
|
|
1467
|
+
const normalized = normalizeContentType(ct);
|
|
1468
|
+
if (!normalized) return false;
|
|
1469
|
+
return normalized === "application/xml" || normalized === "text/xml" || normalized.endsWith("+xml");
|
|
1470
|
+
};
|
|
1471
|
+
var isTextContentType = (ct) => normalizeContentType(ct).startsWith("text/");
|
|
1472
|
+
var isOctetStream = (ct) => normalizeContentType(ct) === "application/octet-stream";
|
|
1473
|
+
var toUint8Array = (value) => {
|
|
1474
|
+
if (value instanceof Uint8Array) return value;
|
|
1475
|
+
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
1476
|
+
if (ArrayBuffer.isView(value)) {
|
|
1477
|
+
const view = value;
|
|
1478
|
+
return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
1479
|
+
}
|
|
1480
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "number")) {
|
|
1481
|
+
return new Uint8Array(value);
|
|
1482
|
+
}
|
|
1483
|
+
return null;
|
|
1484
|
+
};
|
|
1485
|
+
var toArrayBuffer = (bytes) => {
|
|
1486
|
+
const copy = new ArrayBuffer(bytes.byteLength);
|
|
1487
|
+
new Uint8Array(copy).set(bytes);
|
|
1488
|
+
return copy;
|
|
1489
|
+
};
|
|
1490
|
+
var DEFAULT_FORM_STYLE = {
|
|
1491
|
+
style: "form",
|
|
1492
|
+
explode: true,
|
|
1493
|
+
allowReserved: false
|
|
1494
|
+
};
|
|
1495
|
+
var resolveStyleExplode = (e) => {
|
|
1496
|
+
if (!e) return DEFAULT_FORM_STYLE;
|
|
1497
|
+
return {
|
|
1498
|
+
style: Option4.getOrElse(e.style, () => DEFAULT_FORM_STYLE.style),
|
|
1499
|
+
explode: Option4.getOrElse(e.explode, () => DEFAULT_FORM_STYLE.explode),
|
|
1500
|
+
allowReserved: Option4.getOrElse(e.allowReserved, () => DEFAULT_FORM_STYLE.allowReserved)
|
|
1501
|
+
};
|
|
1502
|
+
};
|
|
1503
|
+
var RESERVED_UNENCODED_RE = /[A-Za-z0-9\-._~:/?#[\]@!$&'()*+,;=]/;
|
|
1504
|
+
var encodeFormValue = (v, allowReserved) => {
|
|
1505
|
+
const raw = typeof v === "object" && v !== null ? JSON.stringify(v) : String(v);
|
|
1506
|
+
if (!allowReserved) return encodeURIComponent(raw);
|
|
1507
|
+
let out = "";
|
|
1508
|
+
for (const ch of raw) {
|
|
1509
|
+
out += RESERVED_UNENCODED_RE.test(ch) ? ch : encodeURIComponent(ch);
|
|
1510
|
+
}
|
|
1511
|
+
return out;
|
|
1512
|
+
};
|
|
1513
|
+
var serializeFormUrlEncoded = (value, encoding) => {
|
|
1514
|
+
const parts = [];
|
|
1515
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
1516
|
+
if (raw === void 0 || raw === null) continue;
|
|
1517
|
+
const { style, explode, allowReserved } = resolveStyleExplode(encoding?.[key]);
|
|
1518
|
+
const encKey = encodeURIComponent(key);
|
|
1519
|
+
if (Array.isArray(raw)) {
|
|
1520
|
+
if (explode) {
|
|
1521
|
+
for (const v of raw) {
|
|
1522
|
+
parts.push(`${encKey}=${encodeFormValue(v, allowReserved)}`);
|
|
1523
|
+
}
|
|
1524
|
+
} else {
|
|
1525
|
+
const sep = style === "spaceDelimited" ? " " : style === "pipeDelimited" ? "|" : ",";
|
|
1526
|
+
parts.push(
|
|
1527
|
+
`${encKey}=${encodeFormValue(
|
|
1528
|
+
raw.map((v) => typeof v === "object" ? JSON.stringify(v) : String(v)).join(sep),
|
|
1529
|
+
allowReserved
|
|
1530
|
+
)}`
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
if (typeof raw === "object") {
|
|
1536
|
+
const entries = Object.entries(raw).filter(
|
|
1537
|
+
([, v]) => v !== void 0 && v !== null
|
|
1538
|
+
);
|
|
1539
|
+
if (style === "deepObject") {
|
|
1540
|
+
for (const [subkey, subval] of entries) {
|
|
1541
|
+
parts.push(
|
|
1542
|
+
`${encodeURIComponent(`${key}[${subkey}]`)}=${encodeFormValue(
|
|
1543
|
+
subval,
|
|
1544
|
+
allowReserved
|
|
1545
|
+
)}`
|
|
1546
|
+
);
|
|
1547
|
+
}
|
|
1548
|
+
} else if (explode) {
|
|
1549
|
+
for (const [subkey, subval] of entries) {
|
|
1550
|
+
parts.push(
|
|
1551
|
+
`${encodeURIComponent(subkey)}=${encodeFormValue(subval, allowReserved)}`
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
} else {
|
|
1555
|
+
const flat = entries.flatMap(([k, v]) => [
|
|
1556
|
+
k,
|
|
1557
|
+
typeof v === "object" ? JSON.stringify(v) : String(v)
|
|
1558
|
+
]);
|
|
1559
|
+
parts.push(`${encKey}=${encodeFormValue(flat.join(","), allowReserved)}`);
|
|
1560
|
+
}
|
|
1561
|
+
continue;
|
|
1562
|
+
}
|
|
1563
|
+
parts.push(`${encKey}=${encodeFormValue(raw, allowReserved)}`);
|
|
1564
|
+
}
|
|
1565
|
+
return parts.join("&");
|
|
1566
|
+
};
|
|
1567
|
+
var coerceFormDataRecord = (value, encoding) => {
|
|
1568
|
+
const out = {};
|
|
1569
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
1570
|
+
if (raw === void 0 || raw === null) continue;
|
|
1571
|
+
const partType = encoding?.[key] ? Option4.getOrUndefined(encoding[key].contentType) : void 0;
|
|
1572
|
+
if (partType) {
|
|
1573
|
+
const isJson = partType.startsWith("application/json") || partType.includes("+json");
|
|
1574
|
+
const serialized = typeof raw === "string" ? raw : isJson ? JSON.stringify(raw) : typeof raw === "object" ? JSON.stringify(raw) : String(raw);
|
|
1575
|
+
out[key] = new Blob([serialized], { type: partType });
|
|
1576
|
+
continue;
|
|
1577
|
+
}
|
|
1578
|
+
if (typeof raw === "string" || typeof raw === "number" || typeof raw === "boolean" || raw instanceof Blob || typeof File !== "undefined" && raw instanceof File) {
|
|
1579
|
+
out[key] = raw;
|
|
1580
|
+
continue;
|
|
1581
|
+
}
|
|
1582
|
+
if (Array.isArray(raw)) {
|
|
1583
|
+
out[key] = raw.map(
|
|
1584
|
+
(v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v instanceof Blob || typeof File !== "undefined" && v instanceof File ? v : JSON.stringify(v)
|
|
1585
|
+
);
|
|
1586
|
+
continue;
|
|
1587
|
+
}
|
|
1588
|
+
const bytes = toUint8Array(raw);
|
|
1589
|
+
if (bytes) {
|
|
1590
|
+
out[key] = new Blob([toArrayBuffer(bytes)]);
|
|
1591
|
+
continue;
|
|
1592
|
+
}
|
|
1593
|
+
out[key] = JSON.stringify(raw);
|
|
1594
|
+
}
|
|
1595
|
+
return out;
|
|
1596
|
+
};
|
|
1597
|
+
var applyRequestBody = (request, contentType, bodyValue, encoding) => {
|
|
1598
|
+
if (isJsonContentType(contentType)) {
|
|
1599
|
+
if (typeof bodyValue === "string") {
|
|
1600
|
+
return HttpClientRequest2.bodyText(request, bodyValue, contentType);
|
|
1601
|
+
}
|
|
1602
|
+
return HttpClientRequest2.bodyJsonUnsafe(request, bodyValue);
|
|
1603
|
+
}
|
|
1604
|
+
if (isFormUrlEncoded(contentType)) {
|
|
1605
|
+
if (typeof bodyValue === "string") {
|
|
1606
|
+
return HttpClientRequest2.bodyText(request, bodyValue, contentType);
|
|
1607
|
+
}
|
|
1608
|
+
if (typeof bodyValue === "object" && bodyValue !== null && !Array.isArray(bodyValue)) {
|
|
1609
|
+
const serialized = serializeFormUrlEncoded(
|
|
1610
|
+
bodyValue,
|
|
1611
|
+
encoding
|
|
1612
|
+
);
|
|
1613
|
+
return HttpClientRequest2.bodyText(request, serialized, contentType);
|
|
1614
|
+
}
|
|
1615
|
+
return HttpClientRequest2.bodyUrlParams(
|
|
1616
|
+
request,
|
|
1617
|
+
bodyValue
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
if (isMultipartFormData(contentType)) {
|
|
1621
|
+
if (bodyValue instanceof FormData) {
|
|
1622
|
+
return HttpClientRequest2.bodyFormData(request, bodyValue);
|
|
1623
|
+
}
|
|
1624
|
+
if (typeof bodyValue === "object" && bodyValue !== null) {
|
|
1625
|
+
return HttpClientRequest2.bodyFormDataRecord(
|
|
1626
|
+
request,
|
|
1627
|
+
coerceFormDataRecord(bodyValue, encoding)
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1630
|
+
return HttpClientRequest2.bodyText(request, String(bodyValue), contentType);
|
|
1631
|
+
}
|
|
1632
|
+
if (isOctetStream(contentType)) {
|
|
1633
|
+
const bytes2 = toUint8Array(bodyValue);
|
|
1634
|
+
if (bytes2) return HttpClientRequest2.bodyUint8Array(request, bytes2, contentType);
|
|
1635
|
+
if (typeof bodyValue === "string") {
|
|
1636
|
+
return HttpClientRequest2.bodyText(request, bodyValue, contentType);
|
|
1637
|
+
}
|
|
1638
|
+
return HttpClientRequest2.bodyText(request, JSON.stringify(bodyValue), contentType);
|
|
1639
|
+
}
|
|
1640
|
+
if (isXmlContentType(contentType) || isTextContentType(contentType)) {
|
|
1641
|
+
if (typeof bodyValue === "string") {
|
|
1642
|
+
return HttpClientRequest2.bodyText(request, bodyValue, contentType);
|
|
1643
|
+
}
|
|
1644
|
+
const bytes2 = toUint8Array(bodyValue);
|
|
1645
|
+
if (bytes2) return HttpClientRequest2.bodyUint8Array(request, bytes2, contentType);
|
|
1646
|
+
return HttpClientRequest2.bodyText(request, JSON.stringify(bodyValue), contentType);
|
|
1647
|
+
}
|
|
1648
|
+
if (typeof bodyValue === "string") {
|
|
1649
|
+
return HttpClientRequest2.bodyText(request, bodyValue, contentType);
|
|
1650
|
+
}
|
|
1651
|
+
const bytes = toUint8Array(bodyValue);
|
|
1652
|
+
if (bytes) return HttpClientRequest2.bodyUint8Array(request, bytes, contentType);
|
|
1653
|
+
return HttpClientRequest2.bodyText(request, JSON.stringify(bodyValue), contentType);
|
|
1654
|
+
};
|
|
1655
|
+
var invoke = Effect5.fn("OpenApi.invoke")(function* (operation, args, resolvedHeaders, sourceQueryParams = {}) {
|
|
1656
|
+
const client = yield* HttpClient2.HttpClient;
|
|
1657
|
+
yield* Effect5.annotateCurrentSpan({
|
|
1658
|
+
"http.method": operation.method.toUpperCase(),
|
|
1659
|
+
"http.route": operation.pathTemplate,
|
|
1660
|
+
"plugin.openapi.method": operation.method.toUpperCase(),
|
|
1661
|
+
"plugin.openapi.path_template": operation.pathTemplate,
|
|
1662
|
+
"plugin.openapi.headers.resolved_count": Object.keys(resolvedHeaders).length
|
|
1663
|
+
});
|
|
1664
|
+
const resolvedPath = yield* resolvePath(operation.pathTemplate, args, operation.parameters);
|
|
1665
|
+
const path = resolvedPath.startsWith("/") ? resolvedPath : `/${resolvedPath}`;
|
|
1666
|
+
let request = HttpClientRequest2.make(operation.method.toUpperCase())(path);
|
|
1667
|
+
for (const [name, value] of Object.entries(sourceQueryParams)) {
|
|
1668
|
+
request = HttpClientRequest2.setUrlParam(request, name, value);
|
|
1669
|
+
}
|
|
1670
|
+
for (const param of operation.parameters) {
|
|
1671
|
+
if (param.location !== "query") continue;
|
|
1672
|
+
const value = readParamValue(args, param);
|
|
1673
|
+
if (value === void 0 || value === null) continue;
|
|
1674
|
+
request = HttpClientRequest2.setUrlParam(request, param.name, String(value));
|
|
1675
|
+
}
|
|
1676
|
+
for (const param of operation.parameters) {
|
|
1677
|
+
if (param.location !== "header") continue;
|
|
1678
|
+
const value = readParamValue(args, param);
|
|
1679
|
+
if (value === void 0 || value === null) continue;
|
|
1680
|
+
request = HttpClientRequest2.setHeader(request, param.name, String(value));
|
|
1681
|
+
}
|
|
1682
|
+
if (Option4.isSome(operation.requestBody)) {
|
|
1683
|
+
const rb = operation.requestBody.value;
|
|
1684
|
+
const bodyValue = args.body ?? args.input;
|
|
1685
|
+
if (bodyValue !== void 0) {
|
|
1686
|
+
const contentsOpt = Option4.getOrUndefined(rb.contents);
|
|
1687
|
+
const requestedCt = typeof args.contentType === "string" ? args.contentType : void 0;
|
|
1688
|
+
const selected = contentsOpt && requestedCt ? contentsOpt.find((c) => c.contentType === requestedCt) : void 0;
|
|
1689
|
+
const chosenCt = selected?.contentType ?? rb.contentType;
|
|
1690
|
+
const chosenEncoding = selected ? Option4.getOrUndefined(selected.encoding) : contentsOpt && contentsOpt[0] ? Option4.getOrUndefined(contentsOpt[0].encoding) : void 0;
|
|
1691
|
+
request = applyRequestBody(request, chosenCt, bodyValue, chosenEncoding);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
request = applyHeaders(request, resolvedHeaders);
|
|
1695
|
+
const response = yield* client.execute(request).pipe(
|
|
1696
|
+
Effect5.mapError(
|
|
1697
|
+
(err) => new OpenApiInvocationError({
|
|
1698
|
+
message: `HTTP request failed: ${err.message}`,
|
|
1699
|
+
statusCode: Option4.none(),
|
|
1700
|
+
cause: err
|
|
1701
|
+
})
|
|
1702
|
+
)
|
|
1703
|
+
);
|
|
1704
|
+
const status = response.status;
|
|
1705
|
+
yield* Effect5.annotateCurrentSpan({
|
|
1706
|
+
"http.status_code": status
|
|
1707
|
+
});
|
|
1708
|
+
const responseHeaders = { ...response.headers };
|
|
1709
|
+
const contentType = response.headers["content-type"] ?? null;
|
|
1710
|
+
const mapBodyError = Effect5.mapError(
|
|
1711
|
+
(err) => new OpenApiInvocationError({
|
|
1712
|
+
message: `Failed to read response body: ${err.message ?? String(err)}`,
|
|
1713
|
+
statusCode: Option4.some(status),
|
|
1714
|
+
cause: err
|
|
1715
|
+
})
|
|
1716
|
+
);
|
|
1717
|
+
const responseBody = status === 204 ? null : isJsonContentType(contentType) ? yield* response.json.pipe(
|
|
1718
|
+
Effect5.catch(() => response.text),
|
|
1719
|
+
mapBodyError
|
|
1720
|
+
) : yield* response.text.pipe(mapBodyError);
|
|
1721
|
+
const ok = status >= 200 && status < 300;
|
|
1722
|
+
return new InvocationResult({
|
|
1723
|
+
status,
|
|
1724
|
+
headers: responseHeaders,
|
|
1725
|
+
data: ok ? responseBody : null,
|
|
1726
|
+
error: ok ? null : responseBody
|
|
1727
|
+
});
|
|
1728
|
+
});
|
|
1729
|
+
var invokeWithLayer = (operation, args, baseUrl, resolvedHeaders, sourceQueryParams, httpClientLayer) => {
|
|
1730
|
+
const clientWithBaseUrl = baseUrl ? Layer.effect(
|
|
1731
|
+
HttpClient2.HttpClient,
|
|
1732
|
+
Effect5.map(
|
|
1733
|
+
Effect5.service(HttpClient2.HttpClient),
|
|
1734
|
+
HttpClient2.mapRequest(HttpClientRequest2.prependUrl(baseUrl))
|
|
1735
|
+
)
|
|
1736
|
+
).pipe(Layer.provide(httpClientLayer)) : httpClientLayer;
|
|
1737
|
+
return invoke(operation, args, resolvedHeaders, sourceQueryParams).pipe(
|
|
1738
|
+
Effect5.provide(clientWithBaseUrl),
|
|
1739
|
+
Effect5.withSpan("plugin.openapi.invoke", {
|
|
1740
|
+
attributes: {
|
|
1741
|
+
"plugin.openapi.method": operation.method.toUpperCase(),
|
|
1742
|
+
"plugin.openapi.path_template": operation.pathTemplate,
|
|
1743
|
+
"plugin.openapi.base_url": baseUrl
|
|
1744
|
+
}
|
|
1754
1745
|
})
|
|
1746
|
+
);
|
|
1747
|
+
};
|
|
1748
|
+
var REQUIRE_APPROVAL = /* @__PURE__ */ new Set(["post", "put", "patch", "delete"]);
|
|
1749
|
+
var annotationsForOperation = (method, pathTemplate) => {
|
|
1750
|
+
const m = method.toLowerCase();
|
|
1751
|
+
if (!REQUIRE_APPROVAL.has(m)) return {};
|
|
1752
|
+
return {
|
|
1753
|
+
requiresApproval: true,
|
|
1754
|
+
approvalDescription: `${method.toUpperCase()} ${pathTemplate}`
|
|
1755
1755
|
};
|
|
1756
1756
|
};
|
|
1757
1757
|
|
|
1758
1758
|
// src/sdk/plugin.ts
|
|
1759
|
-
import { Effect as
|
|
1759
|
+
import { Effect as Effect7, Option as Option5, Schema as Schema6 } from "effect";
|
|
1760
1760
|
import { FetchHttpClient } from "effect/unstable/http";
|
|
1761
|
+
|
|
1762
|
+
// src/api/group.ts
|
|
1763
|
+
import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
|
|
1764
|
+
import { Schema as Schema5 } from "effect";
|
|
1765
|
+
import { ScopeId as ScopeId3, SecretBackedValue as SecretBackedValue2 } from "@executor-js/sdk/core";
|
|
1766
|
+
import { InternalError } from "@executor-js/api";
|
|
1767
|
+
var DomainErrors = [
|
|
1768
|
+
InternalError,
|
|
1769
|
+
OpenApiParseError,
|
|
1770
|
+
OpenApiExtractionError,
|
|
1771
|
+
OpenApiOAuthError
|
|
1772
|
+
];
|
|
1773
|
+
var ScopeIdParam = {
|
|
1774
|
+
scopeId: ScopeId3
|
|
1775
|
+
};
|
|
1776
|
+
var SourceParams = {
|
|
1777
|
+
scopeId: ScopeId3,
|
|
1778
|
+
namespace: Schema5.String
|
|
1779
|
+
};
|
|
1780
|
+
var SourceBindingParams = {
|
|
1781
|
+
scopeId: ScopeId3,
|
|
1782
|
+
namespace: Schema5.String,
|
|
1783
|
+
sourceScopeId: ScopeId3
|
|
1784
|
+
};
|
|
1785
|
+
var SpecFetchCredentialsPayload = Schema5.Struct({
|
|
1786
|
+
headers: Schema5.optional(Schema5.Record(Schema5.String, SecretBackedValue2)),
|
|
1787
|
+
queryParams: Schema5.optional(Schema5.Record(Schema5.String, SecretBackedValue2))
|
|
1788
|
+
});
|
|
1789
|
+
var AddSpecPayload = Schema5.Struct({
|
|
1790
|
+
spec: Schema5.String,
|
|
1791
|
+
specFetchCredentials: Schema5.optional(SpecFetchCredentialsPayload),
|
|
1792
|
+
name: Schema5.optional(Schema5.String),
|
|
1793
|
+
baseUrl: Schema5.optional(Schema5.String),
|
|
1794
|
+
namespace: Schema5.optional(Schema5.String),
|
|
1795
|
+
headers: Schema5.optional(Schema5.Record(Schema5.String, Schema5.Unknown)),
|
|
1796
|
+
queryParams: Schema5.optional(Schema5.Record(Schema5.String, SecretBackedValue2)),
|
|
1797
|
+
oauth2: Schema5.optional(Schema5.Union([OAuth2Auth, OAuth2SourceConfig]))
|
|
1798
|
+
});
|
|
1799
|
+
var PreviewSpecPayload = Schema5.Struct({
|
|
1800
|
+
spec: Schema5.String,
|
|
1801
|
+
specFetchCredentials: Schema5.optional(SpecFetchCredentialsPayload)
|
|
1802
|
+
});
|
|
1803
|
+
var UpdateSourcePayload = Schema5.Struct({
|
|
1804
|
+
name: Schema5.optional(Schema5.String),
|
|
1805
|
+
baseUrl: Schema5.optional(Schema5.String),
|
|
1806
|
+
headers: Schema5.optional(Schema5.Record(Schema5.String, Schema5.Unknown)),
|
|
1807
|
+
queryParams: Schema5.optional(Schema5.Record(Schema5.String, SecretBackedValue2)),
|
|
1808
|
+
// Set after a successful re-authenticate to refresh the source's
|
|
1809
|
+
// stored OAuth2 metadata.
|
|
1810
|
+
oauth2: Schema5.optional(Schema5.Union([OAuth2Auth, OAuth2SourceConfig]))
|
|
1811
|
+
});
|
|
1812
|
+
var UpdateSourceResponse = Schema5.Struct({
|
|
1813
|
+
updated: Schema5.Boolean
|
|
1814
|
+
});
|
|
1815
|
+
var RemoveBindingPayload = Schema5.Struct({
|
|
1816
|
+
sourceId: Schema5.String,
|
|
1817
|
+
sourceScope: ScopeId3,
|
|
1818
|
+
slot: Schema5.String,
|
|
1819
|
+
scope: ScopeId3
|
|
1820
|
+
});
|
|
1821
|
+
var AddSpecResponse = Schema5.Struct({
|
|
1822
|
+
toolCount: Schema5.Number,
|
|
1823
|
+
namespace: Schema5.String
|
|
1824
|
+
});
|
|
1825
|
+
var OpenApiGroup = HttpApiGroup.make("openapi").add(
|
|
1826
|
+
HttpApiEndpoint.post("previewSpec", "/scopes/:scopeId/openapi/preview", {
|
|
1827
|
+
params: ScopeIdParam,
|
|
1828
|
+
payload: PreviewSpecPayload,
|
|
1829
|
+
success: SpecPreview,
|
|
1830
|
+
error: DomainErrors
|
|
1831
|
+
})
|
|
1832
|
+
).add(
|
|
1833
|
+
HttpApiEndpoint.post("addSpec", "/scopes/:scopeId/openapi/specs", {
|
|
1834
|
+
params: ScopeIdParam,
|
|
1835
|
+
payload: AddSpecPayload,
|
|
1836
|
+
success: AddSpecResponse,
|
|
1837
|
+
error: DomainErrors
|
|
1838
|
+
})
|
|
1839
|
+
).add(
|
|
1840
|
+
HttpApiEndpoint.get("getSource", "/scopes/:scopeId/openapi/sources/:namespace", {
|
|
1841
|
+
params: SourceParams,
|
|
1842
|
+
success: Schema5.NullOr(StoredSourceSchema),
|
|
1843
|
+
error: DomainErrors
|
|
1844
|
+
})
|
|
1845
|
+
).add(
|
|
1846
|
+
HttpApiEndpoint.patch("updateSource", "/scopes/:scopeId/openapi/sources/:namespace", {
|
|
1847
|
+
params: SourceParams,
|
|
1848
|
+
payload: UpdateSourcePayload,
|
|
1849
|
+
success: UpdateSourceResponse,
|
|
1850
|
+
error: DomainErrors
|
|
1851
|
+
})
|
|
1852
|
+
).add(
|
|
1853
|
+
HttpApiEndpoint.get(
|
|
1854
|
+
"listSourceBindings",
|
|
1855
|
+
"/scopes/:scopeId/openapi/sources/:namespace/base/:sourceScopeId/bindings",
|
|
1856
|
+
{
|
|
1857
|
+
params: SourceBindingParams,
|
|
1858
|
+
success: Schema5.Array(OpenApiSourceBindingRef),
|
|
1859
|
+
error: DomainErrors
|
|
1860
|
+
}
|
|
1861
|
+
)
|
|
1862
|
+
).add(
|
|
1863
|
+
HttpApiEndpoint.post("setSourceBinding", "/scopes/:scopeId/openapi/source-bindings", {
|
|
1864
|
+
params: ScopeIdParam,
|
|
1865
|
+
payload: OpenApiSourceBindingInputSchema,
|
|
1866
|
+
success: OpenApiSourceBindingRef,
|
|
1867
|
+
error: DomainErrors
|
|
1868
|
+
})
|
|
1869
|
+
).add(
|
|
1870
|
+
HttpApiEndpoint.post(
|
|
1871
|
+
"removeSourceBinding",
|
|
1872
|
+
"/scopes/:scopeId/openapi/source-bindings/remove",
|
|
1873
|
+
{
|
|
1874
|
+
params: ScopeIdParam,
|
|
1875
|
+
payload: RemoveBindingPayload,
|
|
1876
|
+
success: Schema5.Struct({ removed: Schema5.Boolean }),
|
|
1877
|
+
error: DomainErrors
|
|
1878
|
+
}
|
|
1879
|
+
)
|
|
1880
|
+
);
|
|
1881
|
+
|
|
1882
|
+
// src/api/handlers.ts
|
|
1883
|
+
import { HttpApiBuilder } from "effect/unstable/httpapi";
|
|
1884
|
+
import { Context, Effect as Effect6 } from "effect";
|
|
1885
|
+
import { addGroup, capture } from "@executor-js/api";
|
|
1886
|
+
var OpenApiExtensionService = class extends Context.Service()("OpenApiExtensionService") {
|
|
1887
|
+
};
|
|
1888
|
+
var ExecutorApiWithOpenApi = addGroup(OpenApiGroup);
|
|
1889
|
+
var OpenApiHandlers = HttpApiBuilder.group(
|
|
1890
|
+
ExecutorApiWithOpenApi,
|
|
1891
|
+
"openapi",
|
|
1892
|
+
(handlers) => handlers.handle(
|
|
1893
|
+
"previewSpec",
|
|
1894
|
+
({ payload }) => capture(
|
|
1895
|
+
Effect6.gen(function* () {
|
|
1896
|
+
const ext = yield* OpenApiExtensionService;
|
|
1897
|
+
return yield* ext.previewSpec({
|
|
1898
|
+
spec: payload.spec,
|
|
1899
|
+
specFetchCredentials: payload.specFetchCredentials
|
|
1900
|
+
});
|
|
1901
|
+
})
|
|
1902
|
+
)
|
|
1903
|
+
).handle(
|
|
1904
|
+
"addSpec",
|
|
1905
|
+
({ params: path, payload }) => capture(
|
|
1906
|
+
Effect6.gen(function* () {
|
|
1907
|
+
const ext = yield* OpenApiExtensionService;
|
|
1908
|
+
const result = yield* ext.addSpec({
|
|
1909
|
+
spec: payload.spec,
|
|
1910
|
+
specFetchCredentials: payload.specFetchCredentials,
|
|
1911
|
+
scope: path.scopeId,
|
|
1912
|
+
name: payload.name,
|
|
1913
|
+
baseUrl: payload.baseUrl,
|
|
1914
|
+
namespace: payload.namespace,
|
|
1915
|
+
headers: payload.headers,
|
|
1916
|
+
queryParams: payload.queryParams,
|
|
1917
|
+
oauth2: payload.oauth2
|
|
1918
|
+
});
|
|
1919
|
+
return {
|
|
1920
|
+
toolCount: result.toolCount,
|
|
1921
|
+
namespace: result.sourceId
|
|
1922
|
+
};
|
|
1923
|
+
})
|
|
1924
|
+
)
|
|
1925
|
+
).handle(
|
|
1926
|
+
"getSource",
|
|
1927
|
+
({ params: path }) => capture(
|
|
1928
|
+
Effect6.gen(function* () {
|
|
1929
|
+
const ext = yield* OpenApiExtensionService;
|
|
1930
|
+
const source = yield* ext.getSource(path.namespace, path.scopeId);
|
|
1931
|
+
return source ? new StoredSourceSchema({
|
|
1932
|
+
namespace: source.namespace,
|
|
1933
|
+
name: source.name,
|
|
1934
|
+
config: source.config
|
|
1935
|
+
}) : null;
|
|
1936
|
+
})
|
|
1937
|
+
)
|
|
1938
|
+
).handle(
|
|
1939
|
+
"updateSource",
|
|
1940
|
+
({ params: path, payload }) => capture(
|
|
1941
|
+
Effect6.gen(function* () {
|
|
1942
|
+
const ext = yield* OpenApiExtensionService;
|
|
1943
|
+
yield* ext.updateSource(path.namespace, path.scopeId, {
|
|
1944
|
+
name: payload.name,
|
|
1945
|
+
baseUrl: payload.baseUrl,
|
|
1946
|
+
headers: payload.headers,
|
|
1947
|
+
queryParams: payload.queryParams,
|
|
1948
|
+
oauth2: payload.oauth2
|
|
1949
|
+
});
|
|
1950
|
+
return { updated: true };
|
|
1951
|
+
})
|
|
1952
|
+
)
|
|
1953
|
+
).handle(
|
|
1954
|
+
"listSourceBindings",
|
|
1955
|
+
({ params: path }) => capture(
|
|
1956
|
+
Effect6.gen(function* () {
|
|
1957
|
+
const ext = yield* OpenApiExtensionService;
|
|
1958
|
+
return yield* ext.listSourceBindings(path.namespace, path.sourceScopeId);
|
|
1959
|
+
})
|
|
1960
|
+
)
|
|
1961
|
+
).handle(
|
|
1962
|
+
"setSourceBinding",
|
|
1963
|
+
({ payload }) => capture(
|
|
1964
|
+
Effect6.gen(function* () {
|
|
1965
|
+
const ext = yield* OpenApiExtensionService;
|
|
1966
|
+
return yield* ext.setSourceBinding(new OpenApiSourceBindingInput(payload));
|
|
1967
|
+
})
|
|
1968
|
+
)
|
|
1969
|
+
).handle(
|
|
1970
|
+
"removeSourceBinding",
|
|
1971
|
+
({ payload }) => capture(
|
|
1972
|
+
Effect6.gen(function* () {
|
|
1973
|
+
const ext = yield* OpenApiExtensionService;
|
|
1974
|
+
yield* ext.removeSourceBinding(
|
|
1975
|
+
payload.sourceId,
|
|
1976
|
+
payload.sourceScope,
|
|
1977
|
+
payload.slot,
|
|
1978
|
+
payload.scope
|
|
1979
|
+
);
|
|
1980
|
+
return { removed: true };
|
|
1981
|
+
})
|
|
1982
|
+
)
|
|
1983
|
+
)
|
|
1984
|
+
);
|
|
1985
|
+
|
|
1986
|
+
// src/sdk/plugin.ts
|
|
1761
1987
|
import {
|
|
1762
1988
|
ConnectionId as ConnectionId2,
|
|
1763
|
-
ScopeId as
|
|
1989
|
+
ScopeId as ScopeId4,
|
|
1764
1990
|
SecretId as SecretId2,
|
|
1765
1991
|
SourceDetectionResult,
|
|
1766
1992
|
definePlugin,
|
|
@@ -1894,25 +2120,25 @@ var compileToolDefinitions = (operations) => {
|
|
|
1894
2120
|
};
|
|
1895
2121
|
|
|
1896
2122
|
// src/sdk/plugin.ts
|
|
1897
|
-
var PreviewSpecInputSchema =
|
|
1898
|
-
spec:
|
|
1899
|
-
specFetchCredentials:
|
|
1900
|
-
|
|
1901
|
-
headers:
|
|
1902
|
-
queryParams:
|
|
2123
|
+
var PreviewSpecInputSchema = Schema6.Struct({
|
|
2124
|
+
spec: Schema6.String,
|
|
2125
|
+
specFetchCredentials: Schema6.optional(
|
|
2126
|
+
Schema6.Struct({
|
|
2127
|
+
headers: Schema6.optional(Schema6.Record(Schema6.String, HeaderValue)),
|
|
2128
|
+
queryParams: Schema6.optional(Schema6.Record(Schema6.String, HeaderValue))
|
|
1903
2129
|
})
|
|
1904
2130
|
)
|
|
1905
2131
|
});
|
|
1906
|
-
var AddSourceInputSchema =
|
|
1907
|
-
spec:
|
|
1908
|
-
baseUrl:
|
|
1909
|
-
namespace:
|
|
1910
|
-
headers:
|
|
1911
|
-
queryParams:
|
|
1912
|
-
specFetchCredentials:
|
|
1913
|
-
|
|
1914
|
-
headers:
|
|
1915
|
-
queryParams:
|
|
2132
|
+
var AddSourceInputSchema = Schema6.Struct({
|
|
2133
|
+
spec: Schema6.String,
|
|
2134
|
+
baseUrl: Schema6.optional(Schema6.String),
|
|
2135
|
+
namespace: Schema6.optional(Schema6.String),
|
|
2136
|
+
headers: Schema6.optional(Schema6.Record(Schema6.String, HeaderValue)),
|
|
2137
|
+
queryParams: Schema6.optional(Schema6.Record(Schema6.String, HeaderValue)),
|
|
2138
|
+
specFetchCredentials: Schema6.optional(
|
|
2139
|
+
Schema6.Struct({
|
|
2140
|
+
headers: Schema6.optional(Schema6.Record(Schema6.String, HeaderValue)),
|
|
2141
|
+
queryParams: Schema6.optional(Schema6.Record(Schema6.String, HeaderValue))
|
|
1916
2142
|
})
|
|
1917
2143
|
)
|
|
1918
2144
|
});
|
|
@@ -2034,7 +2260,7 @@ var canonicalizeOAuth2 = (oauth2) => {
|
|
|
2034
2260
|
bindings
|
|
2035
2261
|
};
|
|
2036
2262
|
};
|
|
2037
|
-
var resolveEffectiveSourceConfig = (ctx, base) =>
|
|
2263
|
+
var resolveEffectiveSourceConfig = (ctx, base) => Effect7.gen(function* () {
|
|
2038
2264
|
const rank = new Map(ctx.scopes.map((scope, index) => [scope.id, index]));
|
|
2039
2265
|
const baseRank = rank.get(base.scope) ?? Infinity;
|
|
2040
2266
|
let fallback = null;
|
|
@@ -2068,7 +2294,7 @@ var resolveEffectiveSourceConfig = (ctx, base) => Effect6.gen(function* () {
|
|
|
2068
2294
|
oauth2Source: base.config.oauth2 ? base : fallback
|
|
2069
2295
|
};
|
|
2070
2296
|
});
|
|
2071
|
-
var resolveConfiguredHeaders = (ctx, params) =>
|
|
2297
|
+
var resolveConfiguredHeaders = (ctx, params) => Effect7.gen(function* () {
|
|
2072
2298
|
const resolved = {};
|
|
2073
2299
|
for (const [name, value] of Object.entries(params.headers)) {
|
|
2074
2300
|
if (typeof value === "string") {
|
|
@@ -2082,7 +2308,7 @@ var resolveConfiguredHeaders = (ctx, params) => Effect6.gen(function* () {
|
|
|
2082
2308
|
);
|
|
2083
2309
|
if (binding?.value.kind === "secret") {
|
|
2084
2310
|
const secret = yield* ctx.secrets.get(binding.value.secretId).pipe(
|
|
2085
|
-
|
|
2311
|
+
Effect7.mapError(
|
|
2086
2312
|
(err) => "_tag" in err && err._tag === "SecretOwnedByConnectionError" ? new OpenApiOAuthError({
|
|
2087
2313
|
message: `Secret not found for header "${name}"`
|
|
2088
2314
|
}) : err
|
|
@@ -2103,8 +2329,8 @@ var resolveConfiguredHeaders = (ctx, params) => Effect6.gen(function* () {
|
|
|
2103
2329
|
const legacy = params.legacyHeaders?.[name];
|
|
2104
2330
|
if (legacy) {
|
|
2105
2331
|
const fallback = yield* resolveHeaders({ [name]: legacy }, ctx.secrets).pipe(
|
|
2106
|
-
|
|
2107
|
-
|
|
2332
|
+
Effect7.map((headers) => headers[name]),
|
|
2333
|
+
Effect7.mapError(
|
|
2108
2334
|
(err) => err instanceof OpenApiOAuthError ? err : new OpenApiOAuthError({ message: err.message })
|
|
2109
2335
|
)
|
|
2110
2336
|
);
|
|
@@ -2127,12 +2353,12 @@ var resolveHeaderValues = (ctx, values) => resolveSecretBackedMap({
|
|
|
2127
2353
|
message: `Secret not found for "${name}"`
|
|
2128
2354
|
}) : err
|
|
2129
2355
|
}).pipe(
|
|
2130
|
-
|
|
2356
|
+
Effect7.mapError(
|
|
2131
2357
|
(err) => "_tag" in err && err._tag === "SecretOwnedByConnectionError" ? new OpenApiOAuthError({ message: "Secret resolution failed" }) : err
|
|
2132
2358
|
),
|
|
2133
|
-
|
|
2359
|
+
Effect7.map((resolved) => resolved ?? {})
|
|
2134
2360
|
);
|
|
2135
|
-
var resolveOAuthConnectionId = (ctx, params) =>
|
|
2361
|
+
var resolveOAuthConnectionId = (ctx, params) => Effect7.gen(function* () {
|
|
2136
2362
|
const binding = yield* ctx.storage.resolveSourceBinding(
|
|
2137
2363
|
params.sourceId,
|
|
2138
2364
|
params.sourceScope,
|
|
@@ -2147,7 +2373,7 @@ var resolveOAuthConnectionId = (ctx, params) => Effect6.gen(function* () {
|
|
|
2147
2373
|
const legacyConnection = yield* ctx.connections.get(params.legacyOAuth2.connectionId);
|
|
2148
2374
|
return legacyConnection ? params.legacyOAuth2.connectionId : null;
|
|
2149
2375
|
});
|
|
2150
|
-
var resolveSpecFetchCredentials = (ctx, credentials) =>
|
|
2376
|
+
var resolveSpecFetchCredentials = (ctx, credentials) => Effect7.gen(function* () {
|
|
2151
2377
|
if (!credentials) return void 0;
|
|
2152
2378
|
return {
|
|
2153
2379
|
headers: yield* resolveHeaderValues(ctx, credentials.headers),
|
|
@@ -2174,7 +2400,7 @@ var toOpenApiSourceConfig = (namespace, config) => {
|
|
|
2174
2400
|
var isHttpUrl = (s) => s.startsWith("http://") || s.startsWith("https://");
|
|
2175
2401
|
var openApiPlugin = definePlugin((options) => {
|
|
2176
2402
|
const httpClientLayer = options?.httpClientLayer ?? FetchHttpClient.layer;
|
|
2177
|
-
const rebuildSource = (ctx, input) =>
|
|
2403
|
+
const rebuildSource = (ctx, input) => Effect7.gen(function* () {
|
|
2178
2404
|
const doc = yield* parse(input.specText);
|
|
2179
2405
|
const result = yield* extract(doc);
|
|
2180
2406
|
const namespace = input.namespace ?? Option5.getOrElse(result.title, () => "api").toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
@@ -2211,7 +2437,7 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2211
2437
|
binding: toBinding(def)
|
|
2212
2438
|
}));
|
|
2213
2439
|
yield* ctx.transaction(
|
|
2214
|
-
|
|
2440
|
+
Effect7.gen(function* () {
|
|
2215
2441
|
yield* ctx.storage.upsertSource(storedSource, storedOps);
|
|
2216
2442
|
yield* ctx.core.sources.register({
|
|
2217
2443
|
id: namespace,
|
|
@@ -2236,8 +2462,8 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2236
2462
|
yield* ctx.storage.setSourceBinding(
|
|
2237
2463
|
new OpenApiSourceBindingInput({
|
|
2238
2464
|
sourceId: namespace,
|
|
2239
|
-
sourceScope:
|
|
2240
|
-
scope:
|
|
2465
|
+
sourceScope: ScopeId4.make(input.scope),
|
|
2466
|
+
scope: ScopeId4.make(input.scope),
|
|
2241
2467
|
slot: binding.slot,
|
|
2242
2468
|
value: binding.value
|
|
2243
2469
|
})
|
|
@@ -2254,7 +2480,7 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2254
2480
|
);
|
|
2255
2481
|
return { sourceId: namespace, toolCount: definitions.length };
|
|
2256
2482
|
});
|
|
2257
|
-
const refreshSourceInternal = (ctx, sourceId, scope) =>
|
|
2483
|
+
const refreshSourceInternal = (ctx, sourceId, scope) => Effect7.gen(function* () {
|
|
2258
2484
|
const existing = yield* ctx.storage.getSource(sourceId, scope);
|
|
2259
2485
|
if (!existing) return;
|
|
2260
2486
|
const effective = yield* resolveEffectiveSourceConfig(ctx, existing);
|
|
@@ -2266,7 +2492,7 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2266
2492
|
resolvedConfig.specFetchCredentials
|
|
2267
2493
|
);
|
|
2268
2494
|
const specText = yield* resolveSpecText(sourceUrl, credentials).pipe(
|
|
2269
|
-
|
|
2495
|
+
Effect7.provide(httpClientLayer)
|
|
2270
2496
|
);
|
|
2271
2497
|
yield* rebuildSource(ctx, {
|
|
2272
2498
|
specText,
|
|
@@ -2283,13 +2509,14 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2283
2509
|
});
|
|
2284
2510
|
return {
|
|
2285
2511
|
id: "openapi",
|
|
2512
|
+
packageName: "@executor-js/plugin-openapi",
|
|
2286
2513
|
schema: openapiSchema,
|
|
2287
2514
|
storage: (deps) => makeDefaultOpenapiStore(deps),
|
|
2288
2515
|
extension: (ctx) => {
|
|
2289
|
-
const addSpecInternal = (config) =>
|
|
2516
|
+
const addSpecInternal = (config) => Effect7.gen(function* () {
|
|
2290
2517
|
const credentials = yield* resolveSpecFetchCredentials(ctx, config.specFetchCredentials);
|
|
2291
2518
|
const specText = yield* resolveSpecText(config.spec, credentials).pipe(
|
|
2292
|
-
|
|
2519
|
+
Effect7.provide(httpClientLayer)
|
|
2293
2520
|
);
|
|
2294
2521
|
return yield* rebuildSource(ctx, {
|
|
2295
2522
|
specText,
|
|
@@ -2306,27 +2533,27 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2306
2533
|
});
|
|
2307
2534
|
const configFile = options?.configFile;
|
|
2308
2535
|
return {
|
|
2309
|
-
previewSpec: (input) =>
|
|
2536
|
+
previewSpec: (input) => Effect7.gen(function* () {
|
|
2310
2537
|
const previewInput = typeof input === "string" ? { spec: input } : input;
|
|
2311
2538
|
const credentials = yield* resolveSpecFetchCredentials(
|
|
2312
2539
|
ctx,
|
|
2313
2540
|
previewInput.specFetchCredentials
|
|
2314
2541
|
);
|
|
2315
2542
|
const specText = yield* resolveSpecText(previewInput.spec, credentials).pipe(
|
|
2316
|
-
|
|
2543
|
+
Effect7.provide(httpClientLayer)
|
|
2317
2544
|
);
|
|
2318
|
-
return yield* previewSpec(specText).pipe(
|
|
2545
|
+
return yield* previewSpec(specText).pipe(Effect7.provide(httpClientLayer));
|
|
2319
2546
|
}),
|
|
2320
|
-
addSpec: (config) =>
|
|
2547
|
+
addSpec: (config) => Effect7.gen(function* () {
|
|
2321
2548
|
const result = yield* addSpecInternal(config);
|
|
2322
2549
|
if (configFile) {
|
|
2323
2550
|
yield* configFile.upsertSource(toOpenApiSourceConfig(result.sourceId, config));
|
|
2324
2551
|
}
|
|
2325
2552
|
return result;
|
|
2326
2553
|
}),
|
|
2327
|
-
removeSpec: (namespace, scope) =>
|
|
2554
|
+
removeSpec: (namespace, scope) => Effect7.gen(function* () {
|
|
2328
2555
|
yield* ctx.transaction(
|
|
2329
|
-
|
|
2556
|
+
Effect7.gen(function* () {
|
|
2330
2557
|
yield* ctx.storage.removeSource(namespace, scope);
|
|
2331
2558
|
yield* ctx.core.sources.unregister(namespace);
|
|
2332
2559
|
})
|
|
@@ -2335,7 +2562,7 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2335
2562
|
yield* configFile.removeSource(namespace);
|
|
2336
2563
|
}
|
|
2337
2564
|
}),
|
|
2338
|
-
getSource: (namespace, scope) =>
|
|
2565
|
+
getSource: (namespace, scope) => Effect7.gen(function* () {
|
|
2339
2566
|
const source = yield* ctx.storage.getSource(namespace, scope);
|
|
2340
2567
|
if (!source) return null;
|
|
2341
2568
|
const effective = yield* resolveEffectiveSourceConfig(ctx, source);
|
|
@@ -2344,7 +2571,7 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2344
2571
|
config: effective.config
|
|
2345
2572
|
};
|
|
2346
2573
|
}),
|
|
2347
|
-
updateSource: (namespace, scope, input) =>
|
|
2574
|
+
updateSource: (namespace, scope, input) => Effect7.gen(function* () {
|
|
2348
2575
|
const existing = yield* ctx.storage.getSource(namespace, scope);
|
|
2349
2576
|
if (!existing) return;
|
|
2350
2577
|
const canonicalHeaders = input.headers !== void 0 ? canonicalizeHeaders(input.headers) : existing.legacy?.headers ? canonicalizeHeaders(existing.legacy.headers) : null;
|
|
@@ -2361,8 +2588,8 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2361
2588
|
yield* ctx.storage.setSourceBinding(
|
|
2362
2589
|
new OpenApiSourceBindingInput({
|
|
2363
2590
|
sourceId: namespace,
|
|
2364
|
-
sourceScope:
|
|
2365
|
-
scope:
|
|
2591
|
+
sourceScope: ScopeId4.make(scope),
|
|
2592
|
+
scope: ScopeId4.make(scope),
|
|
2366
2593
|
slot: binding.slot,
|
|
2367
2594
|
value: binding.value
|
|
2368
2595
|
})
|
|
@@ -2432,17 +2659,17 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2432
2659
|
]
|
|
2433
2660
|
}
|
|
2434
2661
|
],
|
|
2435
|
-
invokeTool: ({ ctx, toolRow, args }) =>
|
|
2662
|
+
invokeTool: ({ ctx, toolRow, args }) => Effect7.gen(function* () {
|
|
2436
2663
|
const toolScope = toolRow.scope_id;
|
|
2437
2664
|
const op = yield* ctx.storage.getOperationByToolId(toolRow.id, toolScope);
|
|
2438
2665
|
if (!op) {
|
|
2439
|
-
return yield*
|
|
2666
|
+
return yield* Effect7.fail(
|
|
2440
2667
|
new Error(`No OpenAPI operation found for tool "${toolRow.id}"`)
|
|
2441
2668
|
);
|
|
2442
2669
|
}
|
|
2443
2670
|
const source = yield* ctx.storage.getSource(op.sourceId, toolScope);
|
|
2444
2671
|
if (!source) {
|
|
2445
|
-
return yield*
|
|
2672
|
+
return yield* Effect7.fail(new Error(`No OpenAPI source found for "${op.sourceId}"`));
|
|
2446
2673
|
}
|
|
2447
2674
|
const effective = yield* resolveEffectiveSourceConfig(ctx, source);
|
|
2448
2675
|
const config = effective.config;
|
|
@@ -2451,9 +2678,9 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2451
2678
|
sourceScope: effective.headersSource.scope,
|
|
2452
2679
|
headers: config.headers ?? {},
|
|
2453
2680
|
legacyHeaders: effective.headersSource.legacy?.headers
|
|
2454
|
-
}).pipe(
|
|
2681
|
+
}).pipe(Effect7.mapError((err) => new Error(err.message)));
|
|
2455
2682
|
const resolvedQueryParams = yield* resolveHeaderValues(ctx, config.queryParams).pipe(
|
|
2456
|
-
|
|
2683
|
+
Effect7.mapError((err) => new Error(err.message))
|
|
2457
2684
|
);
|
|
2458
2685
|
if (config.oauth2) {
|
|
2459
2686
|
const connectionId = yield* resolveOAuthConnectionId(ctx, {
|
|
@@ -2463,12 +2690,12 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2463
2690
|
legacyOAuth2: effective.oauth2Source.legacy?.oauth2
|
|
2464
2691
|
});
|
|
2465
2692
|
if (!connectionId) {
|
|
2466
|
-
return yield*
|
|
2693
|
+
return yield* Effect7.fail(
|
|
2467
2694
|
new Error(`OAuth configuration for "${op.sourceId}" is missing a connection binding`)
|
|
2468
2695
|
);
|
|
2469
2696
|
}
|
|
2470
2697
|
const accessToken = yield* ctx.connections.accessToken(connectionId).pipe(
|
|
2471
|
-
|
|
2698
|
+
Effect7.mapError(
|
|
2472
2699
|
(err) => new Error(
|
|
2473
2700
|
`OAuth connection resolution failed: ${"message" in err ? err.message : String(err)}`
|
|
2474
2701
|
)
|
|
@@ -2486,14 +2713,14 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2486
2713
|
);
|
|
2487
2714
|
return result;
|
|
2488
2715
|
}),
|
|
2489
|
-
resolveAnnotations: ({ ctx, sourceId, toolRows }) =>
|
|
2716
|
+
resolveAnnotations: ({ ctx, sourceId, toolRows }) => Effect7.gen(function* () {
|
|
2490
2717
|
const scopes = /* @__PURE__ */ new Set();
|
|
2491
2718
|
for (const row of toolRows) {
|
|
2492
2719
|
scopes.add(row.scope_id);
|
|
2493
2720
|
}
|
|
2494
|
-
const entries = yield*
|
|
2721
|
+
const entries = yield* Effect7.forEach(
|
|
2495
2722
|
[...scopes],
|
|
2496
|
-
(scope) =>
|
|
2723
|
+
(scope) => Effect7.gen(function* () {
|
|
2497
2724
|
const ops = yield* ctx.storage.listOperationsBySource(sourceId, scope);
|
|
2498
2725
|
const byId = /* @__PURE__ */ new Map();
|
|
2499
2726
|
for (const op of ops) byId.set(op.toolId, op.binding);
|
|
@@ -2519,22 +2746,22 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2519
2746
|
// when `canRefresh: true`, so a raw-text source reaching here
|
|
2520
2747
|
// means stale UI state, which is worth surfacing to the caller.
|
|
2521
2748
|
refreshSource: ({ ctx, sourceId, scope }) => refreshSourceInternal(ctx, sourceId, scope),
|
|
2522
|
-
detect: ({ url }) =>
|
|
2749
|
+
detect: ({ url }) => Effect7.gen(function* () {
|
|
2523
2750
|
const trimmed = url.trim();
|
|
2524
2751
|
if (!trimmed) return null;
|
|
2525
|
-
const parsed = yield*
|
|
2752
|
+
const parsed = yield* Effect7.try({
|
|
2526
2753
|
try: () => new URL(trimmed),
|
|
2527
2754
|
catch: (error) => error
|
|
2528
|
-
}).pipe(
|
|
2755
|
+
}).pipe(Effect7.option);
|
|
2529
2756
|
if (Option5.isNone(parsed)) return null;
|
|
2530
2757
|
const specText = yield* resolveSpecText(trimmed).pipe(
|
|
2531
|
-
|
|
2532
|
-
|
|
2758
|
+
Effect7.provide(httpClientLayer),
|
|
2759
|
+
Effect7.catch(() => Effect7.succeed(null))
|
|
2533
2760
|
);
|
|
2534
2761
|
if (specText === null) return null;
|
|
2535
|
-
const doc = yield* parse(specText).pipe(
|
|
2762
|
+
const doc = yield* parse(specText).pipe(Effect7.catch(() => Effect7.succeed(null)));
|
|
2536
2763
|
if (!doc) return null;
|
|
2537
|
-
const result = yield* extract(doc).pipe(
|
|
2764
|
+
const result = yield* extract(doc).pipe(Effect7.catch(() => Effect7.succeed(null)));
|
|
2538
2765
|
if (!result) return null;
|
|
2539
2766
|
const namespace = Option5.getOrElse(result.title, () => "api").toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
2540
2767
|
const name = Option5.getOrElse(result.title, () => namespace);
|
|
@@ -2545,7 +2772,15 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
2545
2772
|
name,
|
|
2546
2773
|
namespace
|
|
2547
2774
|
});
|
|
2548
|
-
})
|
|
2775
|
+
}),
|
|
2776
|
+
// HTTP transport. `OpenApiHandlers` is the existing late-binding
|
|
2777
|
+
// Layer that requires `OpenApiExtensionService`; the host satisfies
|
|
2778
|
+
// it via the spec's `extensionService` Tag — at boot for local
|
|
2779
|
+
// (`composePluginHandlers(plugins, executor)`), per-request for
|
|
2780
|
+
// cloud (`providePluginExtensions(plugins)(executor)`).
|
|
2781
|
+
routes: () => OpenApiGroup,
|
|
2782
|
+
handlers: () => OpenApiHandlers,
|
|
2783
|
+
extensionService: OpenApiExtensionService
|
|
2549
2784
|
};
|
|
2550
2785
|
});
|
|
2551
2786
|
|
|
@@ -2581,10 +2816,6 @@ export {
|
|
|
2581
2816
|
InvocationConfig,
|
|
2582
2817
|
InvocationResult,
|
|
2583
2818
|
extract,
|
|
2584
|
-
resolveHeaders,
|
|
2585
|
-
invoke,
|
|
2586
|
-
invokeWithLayer,
|
|
2587
|
-
annotationsForOperation,
|
|
2588
2819
|
OAuth2AuthorizationCodeFlow,
|
|
2589
2820
|
OAuth2ClientCredentialsFlow,
|
|
2590
2821
|
OAuth2Flows,
|
|
@@ -2597,6 +2828,10 @@ export {
|
|
|
2597
2828
|
previewSpec,
|
|
2598
2829
|
openapiSchema,
|
|
2599
2830
|
makeDefaultOpenapiStore,
|
|
2831
|
+
resolveHeaders,
|
|
2832
|
+
invoke,
|
|
2833
|
+
invokeWithLayer,
|
|
2834
|
+
annotationsForOperation,
|
|
2600
2835
|
openApiPlugin
|
|
2601
2836
|
};
|
|
2602
|
-
//# sourceMappingURL=chunk-
|
|
2837
|
+
//# sourceMappingURL=chunk-RBE3CVB4.js.map
|