@dual-os/sdk 0.0.0-stage → 0.8.0-next.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/LICENSE +4 -0
- package/README.md +19 -2
- package/dist/Action.d.ts +201 -0
- package/dist/Action.d.ts.map +1 -0
- package/dist/Action.js +172 -0
- package/dist/Action.js.map +1 -0
- package/dist/AgentChannel.d.ts +1717 -0
- package/dist/AgentChannel.d.ts.map +1 -0
- package/dist/AgentChannel.js +708 -0
- package/dist/AgentChannel.js.map +1 -0
- package/dist/AgentChannelConformance.d.ts +1543 -0
- package/dist/AgentChannelConformance.d.ts.map +1 -0
- package/dist/AgentChannelConformance.js +163 -0
- package/dist/AgentChannelConformance.js.map +1 -0
- package/dist/AgentChannelRuntime.d.ts +78 -0
- package/dist/AgentChannelRuntime.d.ts.map +1 -0
- package/dist/AgentChannelRuntime.js +270 -0
- package/dist/AgentChannelRuntime.js.map +1 -0
- package/dist/AiProvider.d.ts +132 -0
- package/dist/AiProvider.d.ts.map +1 -0
- package/dist/AiProvider.js +100 -0
- package/dist/AiProvider.js.map +1 -0
- package/dist/AuthMethod.d.ts +381 -0
- package/dist/AuthMethod.d.ts.map +1 -0
- package/dist/AuthMethod.js +225 -0
- package/dist/AuthMethod.js.map +1 -0
- package/dist/AuthMethodBinding.d.ts +77 -0
- package/dist/AuthMethodBinding.d.ts.map +1 -0
- package/dist/AuthMethodBinding.js +31 -0
- package/dist/AuthMethodBinding.js.map +1 -0
- package/dist/AuthMethodExecution.d.ts +223 -0
- package/dist/AuthMethodExecution.d.ts.map +1 -0
- package/dist/AuthMethodExecution.js +277 -0
- package/dist/AuthMethodExecution.js.map +1 -0
- package/dist/ConnectionResolver.d.ts +219 -0
- package/dist/ConnectionResolver.d.ts.map +1 -0
- package/dist/ConnectionResolver.js +153 -0
- package/dist/ConnectionResolver.js.map +1 -0
- package/dist/DefinitionError.d.ts +67 -0
- package/dist/DefinitionError.d.ts.map +1 -0
- package/dist/DefinitionError.js +82 -0
- package/dist/DefinitionError.js.map +1 -0
- package/dist/DualRegistry.d.ts +71 -0
- package/dist/DualRegistry.d.ts.map +1 -0
- package/dist/DualRegistry.js +96 -0
- package/dist/DualRegistry.js.map +1 -0
- package/dist/DualRuntime.d.ts +279 -0
- package/dist/DualRuntime.d.ts.map +1 -0
- package/dist/DualRuntime.js +311 -0
- package/dist/DualRuntime.js.map +1 -0
- package/dist/Files.d.ts +80 -0
- package/dist/Files.d.ts.map +1 -0
- package/dist/Files.js +66 -0
- package/dist/Files.js.map +1 -0
- package/dist/Group.d.ts +106 -0
- package/dist/Group.d.ts.map +1 -0
- package/dist/Group.js +86 -0
- package/dist/Group.js.map +1 -0
- package/dist/Http.d.ts +342 -0
- package/dist/Http.d.ts.map +1 -0
- package/dist/Http.js +677 -0
- package/dist/Http.js.map +1 -0
- package/dist/Mcp.d.ts +109 -0
- package/dist/Mcp.d.ts.map +1 -0
- package/dist/Mcp.js +176 -0
- package/dist/Mcp.js.map +1 -0
- package/dist/OAuth2TokenRefresher.d.ts +18 -0
- package/dist/OAuth2TokenRefresher.d.ts.map +1 -0
- package/dist/OAuth2TokenRefresher.js +19 -0
- package/dist/OAuth2TokenRefresher.js.map +1 -0
- package/dist/OAuthFlow.d.ts +71 -0
- package/dist/OAuthFlow.d.ts.map +1 -0
- package/dist/OAuthFlow.js +488 -0
- package/dist/OAuthFlow.js.map +1 -0
- package/dist/Plugin.d.ts +383 -0
- package/dist/Plugin.d.ts.map +1 -0
- package/dist/Plugin.js +249 -0
- package/dist/Plugin.js.map +1 -0
- package/dist/PluginRuntime.d.ts +3186 -0
- package/dist/PluginRuntime.d.ts.map +1 -0
- package/dist/PluginRuntime.js +331 -0
- package/dist/PluginRuntime.js.map +1 -0
- package/dist/Runner.d.ts +1 -0
- package/dist/Runner.js +124 -0
- package/dist/Runner.js.map +1 -0
- package/dist/ToolFile.d.ts +23 -0
- package/dist/ToolFile.d.ts.map +1 -0
- package/dist/ToolFile.js +33 -0
- package/dist/ToolFile.js.map +1 -0
- package/dist/Trigger.d.ts +358 -0
- package/dist/Trigger.d.ts.map +1 -0
- package/dist/Trigger.js +287 -0
- package/dist/Trigger.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +24 -0
- package/dist/internal/auth-context.d.ts +23 -0
- package/dist/internal/auth-context.d.ts.map +1 -0
- package/dist/internal/auth-context.js +1 -0
- package/dist/internal/auth-execution.d.ts +27 -0
- package/dist/internal/auth-execution.d.ts.map +1 -0
- package/dist/internal/auth-execution.js +66 -0
- package/dist/internal/auth-execution.js.map +1 -0
- package/dist/internal/auth-runtime.d.ts +5 -0
- package/dist/internal/auth-runtime.d.ts.map +1 -0
- package/dist/internal/auth-runtime.js +16 -0
- package/dist/internal/auth-runtime.js.map +1 -0
- package/dist/internal/capabilities.d.ts +24 -0
- package/dist/internal/capabilities.d.ts.map +1 -0
- package/dist/internal/capabilities.js +22 -0
- package/dist/internal/capabilities.js.map +1 -0
- package/dist/internal/children.d.ts +34 -0
- package/dist/internal/children.d.ts.map +1 -0
- package/dist/internal/children.js +53 -0
- package/dist/internal/children.js.map +1 -0
- package/dist/internal/collection-utils.d.ts +4 -0
- package/dist/internal/collection-utils.d.ts.map +1 -0
- package/dist/internal/collection-utils.js +6 -0
- package/dist/internal/collection-utils.js.map +1 -0
- package/dist/internal/dual/actions.d.ts +33 -0
- package/dist/internal/dual/actions.d.ts.map +1 -0
- package/dist/internal/dual/actions.js +26 -0
- package/dist/internal/dual/actions.js.map +1 -0
- package/dist/internal/dual/invoke.d.ts +63 -0
- package/dist/internal/dual/invoke.d.ts.map +1 -0
- package/dist/internal/dual/invoke.js +75 -0
- package/dist/internal/dual/invoke.js.map +1 -0
- package/dist/internal/dual/registry.d.ts +125 -0
- package/dist/internal/dual/registry.d.ts.map +1 -0
- package/dist/internal/dual/registry.js +172 -0
- package/dist/internal/dual/registry.js.map +1 -0
- package/dist/internal/dual/triggers.d.ts +29 -0
- package/dist/internal/dual/triggers.d.ts.map +1 -0
- package/dist/internal/dual/triggers.js +26 -0
- package/dist/internal/dual/triggers.js.map +1 -0
- package/dist/internal/handler-context.d.ts +9 -0
- package/dist/internal/handler-context.d.ts.map +1 -0
- package/dist/internal/handler-context.js +31 -0
- package/dist/internal/handler-context.js.map +1 -0
- package/dist/internal/identifier.d.ts +6 -0
- package/dist/internal/identifier.d.ts.map +1 -0
- package/dist/internal/identifier.js +28 -0
- package/dist/internal/identifier.js.map +1 -0
- package/dist/internal/plugin-handlers.d.ts +38 -0
- package/dist/internal/plugin-handlers.d.ts.map +1 -0
- package/dist/internal/plugin-handlers.js +52 -0
- package/dist/internal/plugin-handlers.js.map +1 -0
- package/dist/internal/proto.d.ts +7 -0
- package/dist/internal/proto.d.ts.map +1 -0
- package/dist/internal/proto.js +10 -0
- package/dist/internal/proto.js.map +1 -0
- package/dist/internal/schema-runtime.d.ts +6 -0
- package/dist/internal/schema-runtime.d.ts.map +1 -0
- package/dist/internal/schema-runtime.js +10 -0
- package/dist/internal/schema-runtime.js.map +1 -0
- package/dist/internal/trigger-runtime-shared.d.ts +41 -0
- package/dist/internal/trigger-runtime-shared.d.ts.map +1 -0
- package/dist/internal/trigger-runtime-shared.js +132 -0
- package/dist/internal/trigger-runtime-shared.js.map +1 -0
- package/dist/internal/trigger-runtime.d.ts +12 -0
- package/dist/internal/trigger-runtime.d.ts.map +1 -0
- package/dist/internal/trigger-runtime.js +95 -0
- package/dist/internal/trigger-runtime.js.map +1 -0
- package/dist/internal/types.d.ts +7 -0
- package/dist/internal/types.d.ts.map +1 -0
- package/dist/internal/types.js +1 -0
- package/dist/remote-runtime.d.ts +585 -0
- package/dist/remote-runtime.d.ts.map +1 -0
- package/dist/remote-runtime.js +367 -0
- package/dist/remote-runtime.js.map +1 -0
- package/package.json +55 -4
- package/src/Action.ts +370 -0
- package/src/AgentChannel.ts +860 -0
- package/src/AgentChannelConformance.ts +124 -0
- package/src/AgentChannelRuntime.ts +616 -0
- package/src/AiProvider.ts +255 -0
- package/src/AuthMethod.ts +954 -0
- package/src/AuthMethodBinding.ts +112 -0
- package/src/AuthMethodExecution.ts +542 -0
- package/src/ConnectionResolver.ts +265 -0
- package/src/DefinitionError.ts +115 -0
- package/src/DualRegistry.ts +152 -0
- package/src/DualRuntime.ts +1081 -0
- package/src/Files.ts +89 -0
- package/src/Group.ts +292 -0
- package/src/Http.ts +1205 -0
- package/src/Mcp.ts +299 -0
- package/src/OAuth2TokenRefresher.ts +22 -0
- package/src/OAuthFlow.ts +874 -0
- package/src/Plugin.ts +912 -0
- package/src/PluginRuntime.ts +414 -0
- package/src/Runner.ts +248 -0
- package/src/ToolFile.ts +28 -0
- package/src/Trigger.ts +722 -0
- package/src/index.ts +158 -0
- package/src/internal/auth-context.ts +27 -0
- package/src/internal/auth-execution.ts +241 -0
- package/src/internal/auth-runtime.ts +20 -0
- package/src/internal/capabilities.ts +55 -0
- package/src/internal/children.ts +126 -0
- package/src/internal/collection-utils.ts +8 -0
- package/src/internal/dual/actions.ts +116 -0
- package/src/internal/dual/invoke.ts +307 -0
- package/src/internal/dual/registry.ts +340 -0
- package/src/internal/dual/triggers.ts +128 -0
- package/src/internal/handler-context.ts +57 -0
- package/src/internal/identifier.ts +27 -0
- package/src/internal/plugin-handlers.ts +146 -0
- package/src/internal/proto.ts +8 -0
- package/src/internal/schema-runtime.ts +15 -0
- package/src/internal/trigger-runtime-shared.ts +449 -0
- package/src/internal/trigger-runtime.ts +223 -0
- package/src/internal/types.ts +12 -0
- package/src/remote-runtime.ts +469 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { Service as Service$1 } from "./OAuth2TokenRefresher.js";
|
|
3
|
+
import { OAuth2ProviderTokenResponse } from "./AuthMethod.js";
|
|
4
|
+
import "./OAuthFlow.js";
|
|
5
|
+
import { Array, Clock, ConfigProvider, Context, Effect, HashSet, Layer, Predicate, Random, Record, Redacted, Schema, String, pipe } from "effect";
|
|
6
|
+
import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/http";
|
|
7
|
+
import { Base64, Base64Url } from "effect/encoding";
|
|
8
|
+
//#region src/OAuthFlow.ts
|
|
9
|
+
/**
|
|
10
|
+
* OAuth protocol runner used by ConnectionStore.
|
|
11
|
+
*
|
|
12
|
+
* OAuthFlow owns provider-facing protocol mechanics: resolving OAuth client
|
|
13
|
+
* configuration, optional discovery, authorization URL construction, token
|
|
14
|
+
* exchange, token refresh, and token response normalization. It does not persist
|
|
15
|
+
* OAuth sessions or create Connections; ConnectionStore owns those product
|
|
16
|
+
* lifecycle records and calls this service for the protocol steps.
|
|
17
|
+
*/
|
|
18
|
+
var OAuthFlow_exports = /* @__PURE__ */ __exportAll({
|
|
19
|
+
OAuthConnectionValidationFailed: () => OAuthConnectionValidationFailed,
|
|
20
|
+
OAuthFlow: () => OAuthFlow_exports,
|
|
21
|
+
OAuthGrantRejected: () => OAuthGrantRejected,
|
|
22
|
+
OAuthProviderRejected: () => OAuthProviderRejected,
|
|
23
|
+
Service: () => Service,
|
|
24
|
+
layer: () => layer,
|
|
25
|
+
oauth2TokenRefresherLayer: () => oauth2TokenRefresherLayer,
|
|
26
|
+
testing: () => testing
|
|
27
|
+
});
|
|
28
|
+
var OAuthConnectionValidationFailed = class extends Schema.TaggedError()("OAuthConnectionValidationFailed", {
|
|
29
|
+
cause: Schema.optionalKey(Schema.Redacted(Schema.Defect())),
|
|
30
|
+
message: Schema.String
|
|
31
|
+
}) {};
|
|
32
|
+
var OAuthProviderRejected = class extends Schema.TaggedError()("OAuthProviderRejected", {
|
|
33
|
+
error: Schema.String,
|
|
34
|
+
errorDescription: Schema.optional(Schema.String)
|
|
35
|
+
}) {};
|
|
36
|
+
var OAuthGrantRejected = class extends Schema.TaggedError()("OAuthGrantRejected", {
|
|
37
|
+
errorSubtype: Schema.optionalKey(Schema.String),
|
|
38
|
+
message: Schema.String
|
|
39
|
+
}) {};
|
|
40
|
+
const DefaultFlowTtlMs = 6e5;
|
|
41
|
+
Schema.Struct({
|
|
42
|
+
authorizationUrl: Schema.String,
|
|
43
|
+
expectedIssuer: Schema.optional(Schema.String),
|
|
44
|
+
tokenUrl: Schema.String
|
|
45
|
+
});
|
|
46
|
+
const ClientAuthentication = Schema.Union([
|
|
47
|
+
Schema.Literal("basic"),
|
|
48
|
+
Schema.Literal("post"),
|
|
49
|
+
Schema.Literal("none")
|
|
50
|
+
]);
|
|
51
|
+
const ReservedTokenHeaders = HashSet.fromIterable([
|
|
52
|
+
"accept",
|
|
53
|
+
"accept-charset",
|
|
54
|
+
"accept-encoding",
|
|
55
|
+
"access-control-request-headers",
|
|
56
|
+
"access-control-request-method",
|
|
57
|
+
"authorization",
|
|
58
|
+
"connection",
|
|
59
|
+
"content-encoding",
|
|
60
|
+
"content-length",
|
|
61
|
+
"content-type",
|
|
62
|
+
"cookie",
|
|
63
|
+
"cookie2",
|
|
64
|
+
"date",
|
|
65
|
+
"dnt",
|
|
66
|
+
"expect",
|
|
67
|
+
"host",
|
|
68
|
+
"keep-alive",
|
|
69
|
+
"origin",
|
|
70
|
+
"permissions-policy",
|
|
71
|
+
"referer",
|
|
72
|
+
"set-cookie",
|
|
73
|
+
"te",
|
|
74
|
+
"trailer",
|
|
75
|
+
"transfer-encoding",
|
|
76
|
+
"upgrade",
|
|
77
|
+
"user-agent",
|
|
78
|
+
"via"
|
|
79
|
+
]);
|
|
80
|
+
const TokenHeaders = Schema.Record(Schema.String, Schema.String.check(Schema.isPattern(/^[\t\x20-\x7e\x80-\xff]*$/u))).check(Schema.makeFilter((headers) => {
|
|
81
|
+
const names = Array.map(Record.keys(headers), String.toLowerCase);
|
|
82
|
+
return HashSet.size(HashSet.fromIterable(names)) === names.length && Array.every(names, (name) => /^[!#$%&'*+.^_`|~0-9a-z-]+$/u.test(name) && !HashSet.has(ReservedTokenHeaders, name) && !String.startsWith("proxy-")(name) && !String.startsWith("sec-")(name));
|
|
83
|
+
}, { message: "Invalid provider token headers" }));
|
|
84
|
+
Schema.Struct({
|
|
85
|
+
authorizationParams: Schema.Record(Schema.String, Schema.String),
|
|
86
|
+
authorizationUrl: Schema.String,
|
|
87
|
+
clientAuthentication: ClientAuthentication,
|
|
88
|
+
clientId: Schema.String,
|
|
89
|
+
clientSecret: Schema.optional(Schema.String),
|
|
90
|
+
expectedIssuer: Schema.optional(Schema.String),
|
|
91
|
+
pkce: Schema.Boolean,
|
|
92
|
+
scopes: Schema.Array(Schema.String),
|
|
93
|
+
tokenHeaders: TokenHeaders,
|
|
94
|
+
tokenParams: Schema.Record(Schema.String, Schema.String),
|
|
95
|
+
tokenRequestEncoding: Schema.Literals(["form", "json"]),
|
|
96
|
+
tokenUrl: Schema.String
|
|
97
|
+
});
|
|
98
|
+
const ProviderDiscoveryDocument = Schema.Struct({
|
|
99
|
+
authorization_endpoint: Schema.String,
|
|
100
|
+
issuer: Schema.optional(Schema.String),
|
|
101
|
+
token_endpoint: Schema.String
|
|
102
|
+
});
|
|
103
|
+
const decodeDiscoveryDocument = Schema.decodeUnknownSync(ProviderDiscoveryDocument);
|
|
104
|
+
const encodeJsonTokenParams = Schema.encodeSync(Schema.fromJsonString(Schema.Record(Schema.String, Schema.String)));
|
|
105
|
+
const decodeProviderTokenError = Schema.decodeSync(Schema.fromJsonString(Schema.Struct({ error: Schema.Literals([
|
|
106
|
+
"invalid_request",
|
|
107
|
+
"invalid_client",
|
|
108
|
+
"invalid_grant",
|
|
109
|
+
"unauthorized_client",
|
|
110
|
+
"unsupported_grant_type",
|
|
111
|
+
"invalid_scope",
|
|
112
|
+
"server_error",
|
|
113
|
+
"temporarily_unavailable"
|
|
114
|
+
]) })));
|
|
115
|
+
const decodeProviderTokenErrorSubtype = Schema.decodeSync(Schema.fromJsonString(Schema.Struct({ error_subtype: Schema.Literals(["invalid_rapt", "rapt_required"]) })));
|
|
116
|
+
const randomBase64Url = (bytes) => {
|
|
117
|
+
const data = new Uint8Array(bytes);
|
|
118
|
+
crypto.getRandomValues(data);
|
|
119
|
+
return Base64Url.encode(data);
|
|
120
|
+
};
|
|
121
|
+
const sha256Base64Url = (value) => Effect.promise(() => crypto.subtle.digest("SHA-256", new TextEncoder().encode(value))).pipe(Effect.map((digest) => Base64Url.encode(new Uint8Array(digest))));
|
|
122
|
+
const requiredString = (value, message) => typeof value === "string" && value.trim() !== "" ? Effect.succeed(value) : Effect.fail(new OAuthConnectionValidationFailed({ message }));
|
|
123
|
+
const fetchDiscovery = Effect.fn("OAuthFlow.fetchDiscovery")(function* (discoveryUrl, client) {
|
|
124
|
+
const rawDocument = yield* pipe(HttpClient.filterStatusOk(client).get(discoveryUrl, { headers: { Accept: "application/json" } }), Effect.flatMap((response) => response.json), Effect.mapError((cause) => OAuthConnectionValidationFailed.make({
|
|
125
|
+
cause: Redacted.make(cause),
|
|
126
|
+
message: "failed to load oauth discovery document"
|
|
127
|
+
})));
|
|
128
|
+
const document = yield* Effect.try({
|
|
129
|
+
catch: () => new OAuthConnectionValidationFailed({ message: "invalid oauth discovery document" }),
|
|
130
|
+
try: () => decodeDiscoveryDocument(rawDocument)
|
|
131
|
+
});
|
|
132
|
+
const authorizationUrl = yield* requiredString(document.authorization_endpoint, "oauth discovery document missing authorization_endpoint");
|
|
133
|
+
const tokenUrl = yield* requiredString(document.token_endpoint, "oauth discovery document missing token_endpoint");
|
|
134
|
+
const expectedIssuer = typeof document.issuer === "string" && document.issuer.trim() !== "" ? document.issuer : void 0;
|
|
135
|
+
return {
|
|
136
|
+
authorizationUrl,
|
|
137
|
+
tokenUrl,
|
|
138
|
+
...expectedIssuer === void 0 ? {} : { expectedIssuer }
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
const resolveEndpoint = (value, setup) => value === void 0 ? void 0 : typeof value === "function" ? value({ setup }) : value;
|
|
142
|
+
const resolveParams = (value, setup) => typeof value === "function" ? value({ setup }) : value ?? {};
|
|
143
|
+
function invalidTokenHeaders() {
|
|
144
|
+
return OAuthConnectionValidationFailed.make({ message: "invalid oauth token headers" });
|
|
145
|
+
}
|
|
146
|
+
const resolveTokenHeaders = Effect.fnUntraced(function* (input) {
|
|
147
|
+
const raw = yield* Effect.try({
|
|
148
|
+
catch: invalidTokenHeaders,
|
|
149
|
+
try: () => {
|
|
150
|
+
if (Predicate.isFunction(input.value)) return input.value({ setup: input.setup });
|
|
151
|
+
if (input.value === void 0) return {};
|
|
152
|
+
return input.value;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
return yield* pipe(Schema.decodeEffect(TokenHeaders)(raw), Effect.mapError(invalidTokenHeaders));
|
|
156
|
+
});
|
|
157
|
+
const resolveClientValue = Effect.fn("OAuthFlow.resolveClientValue")(function* (input) {
|
|
158
|
+
if (input.value === void 0) return;
|
|
159
|
+
if (typeof input.value === "string") return yield* requiredString(input.value, `${input.field} is empty`);
|
|
160
|
+
return yield* input.value.parse(input.configProvider).pipe(Effect.mapError((cause) => new OAuthConnectionValidationFailed({ message: `failed to resolve ${input.field}: ${cause.message}` })));
|
|
161
|
+
});
|
|
162
|
+
const resolveOAuthConfig = Effect.fn("OAuthFlow.resolveOAuthConfig")(function* (input) {
|
|
163
|
+
const oauth = input.method.oauth2;
|
|
164
|
+
if (oauth === void 0) return yield* new OAuthConnectionValidationFailed({ message: "auth method is not oauth2" });
|
|
165
|
+
const tokenHeaders = yield* resolveTokenHeaders({
|
|
166
|
+
setup: input.setup,
|
|
167
|
+
value: oauth.tokenHeaders
|
|
168
|
+
});
|
|
169
|
+
const client = typeof oauth.client === "function" ? oauth.client({ setup: input.setup }) : oauth.client;
|
|
170
|
+
const clientId = yield* resolveClientValue({
|
|
171
|
+
configProvider: input.configProvider,
|
|
172
|
+
field: "clientId",
|
|
173
|
+
value: client.clientId
|
|
174
|
+
});
|
|
175
|
+
const clientSecret = yield* resolveClientValue({
|
|
176
|
+
configProvider: input.configProvider,
|
|
177
|
+
field: "clientSecret",
|
|
178
|
+
value: client.clientSecret
|
|
179
|
+
});
|
|
180
|
+
if (clientId === void 0) return yield* new OAuthConnectionValidationFailed({ message: "missing oauth client id" });
|
|
181
|
+
const authorizationUrl = resolveEndpoint(oauth.authorizationUrl, input.setup);
|
|
182
|
+
const tokenUrl = resolveEndpoint(oauth.tokenUrl, input.setup);
|
|
183
|
+
const discoveryUrl = resolveEndpoint(oauth.discoveryUrl, input.setup);
|
|
184
|
+
return {
|
|
185
|
+
...authorizationUrl !== void 0 && tokenUrl !== void 0 ? {
|
|
186
|
+
authorizationUrl,
|
|
187
|
+
tokenUrl
|
|
188
|
+
} : discoveryUrl === void 0 ? yield* new OAuthConnectionValidationFailed({ message: "oauth method requires explicit endpoints or discoveryUrl" }) : yield* fetchDiscovery(discoveryUrl, input.client),
|
|
189
|
+
authorizationParams: resolveParams(oauth.authorizationParams, input.setup),
|
|
190
|
+
clientAuthentication: client.authentication ?? "basic",
|
|
191
|
+
clientId,
|
|
192
|
+
clientSecret,
|
|
193
|
+
pkce: oauth.pkce ?? true,
|
|
194
|
+
scopes: oauth.scopes,
|
|
195
|
+
tokenHeaders,
|
|
196
|
+
tokenParams: resolveParams(oauth.tokenParams, input.setup),
|
|
197
|
+
tokenRequestEncoding: oauth.tokenRequestEncoding ?? "form"
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
const buildAuthorizationUrl = (input) => {
|
|
201
|
+
const url = new URL(input.config.authorizationUrl);
|
|
202
|
+
url.searchParams.set("response_type", "code");
|
|
203
|
+
url.searchParams.set("client_id", input.config.clientId);
|
|
204
|
+
url.searchParams.set("redirect_uri", input.redirectUri);
|
|
205
|
+
url.searchParams.set("state", input.state);
|
|
206
|
+
if (input.config.scopes.length > 0) url.searchParams.set("scope", input.config.scopes.join(" "));
|
|
207
|
+
if (input.pkceChallenge !== void 0) {
|
|
208
|
+
url.searchParams.set("code_challenge", input.pkceChallenge);
|
|
209
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
210
|
+
}
|
|
211
|
+
for (const [key, value] of Object.entries(input.config.authorizationParams)) url.searchParams.set(key, value);
|
|
212
|
+
return url.toString();
|
|
213
|
+
};
|
|
214
|
+
const httpStatusClass = (status) => {
|
|
215
|
+
if (status >= 500) return "5xx";
|
|
216
|
+
if (status >= 400) return "4xx";
|
|
217
|
+
return "unexpected";
|
|
218
|
+
};
|
|
219
|
+
const oauthProtocolErrorCode = (body) => {
|
|
220
|
+
try {
|
|
221
|
+
return decodeProviderTokenError(body).error;
|
|
222
|
+
} catch {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const oauthProtocolErrorSubtype = (body) => {
|
|
227
|
+
try {
|
|
228
|
+
return decodeProviderTokenErrorSubtype(body).error_subtype;
|
|
229
|
+
} catch {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
const tokenRequestHeaders = (config) => {
|
|
234
|
+
const headers = {
|
|
235
|
+
...config.tokenHeaders,
|
|
236
|
+
Accept: "application/json",
|
|
237
|
+
"Content-Type": config.tokenRequestEncoding === "json" ? "application/json" : "application/x-www-form-urlencoded"
|
|
238
|
+
};
|
|
239
|
+
if (config.clientAuthentication === "basic") {
|
|
240
|
+
if (config.clientSecret === void 0) return Effect.fail(new OAuthConnectionValidationFailed({ message: "missing client secret for basic token auth" }));
|
|
241
|
+
headers["Authorization"] = `Basic ${Base64.encode(`${config.clientId}:${config.clientSecret}`)}`;
|
|
242
|
+
}
|
|
243
|
+
return Effect.succeed(headers);
|
|
244
|
+
};
|
|
245
|
+
const postTokenRequest = Effect.fn("OAuthFlow.postTokenRequest")(function* (input) {
|
|
246
|
+
if (input.config.clientAuthentication === "post") {
|
|
247
|
+
input.params.set("client_id", input.config.clientId);
|
|
248
|
+
if (input.config.clientSecret !== void 0) input.params.set("client_secret", input.config.clientSecret);
|
|
249
|
+
} else if (input.config.clientAuthentication === "none") input.params.set("client_id", input.config.clientId);
|
|
250
|
+
for (const [key, value] of Object.entries(input.config.tokenParams)) input.params.set(key, value);
|
|
251
|
+
const headers = yield* tokenRequestHeaders(input.config);
|
|
252
|
+
const request = HttpClientRequest.post(input.config.tokenUrl, { headers }).pipe(HttpClientRequest.bodyText(input.config.tokenRequestEncoding === "json" ? encodeJsonTokenParams(Record.fromEntries(input.params)) : input.params.toString(), headers["Content-Type"]));
|
|
253
|
+
const response = yield* pipe(input.client.execute(request), Effect.provideService(FetchHttpClient.RequestInit, Record.isEmptyRecord(input.config.tokenHeaders) ? {} : { redirect: "error" }), Effect.mapError((cause) => OAuthConnectionValidationFailed.make({
|
|
254
|
+
cause: Redacted.make(cause),
|
|
255
|
+
message: `${input.failurePrefix} (network)`
|
|
256
|
+
})));
|
|
257
|
+
if (response.status < 200 || response.status >= 300) {
|
|
258
|
+
const body = yield* pipe(response.text, Effect.mapError((cause) => OAuthConnectionValidationFailed.make({
|
|
259
|
+
cause: Redacted.make(cause),
|
|
260
|
+
message: `${input.failurePrefix} (unreadable response)`
|
|
261
|
+
})));
|
|
262
|
+
const statusClass = httpStatusClass(response.status);
|
|
263
|
+
const protocolError = oauthProtocolErrorCode(body);
|
|
264
|
+
const errorSubtype = oauthProtocolErrorSubtype(body);
|
|
265
|
+
const reason = Array.join(Array.filter([protocolError, errorSubtype], Predicate.isNotUndefined), ", ");
|
|
266
|
+
const message = `${input.failurePrefix} (${Array.join(Array.filter([statusClass, reason], String.isNonEmpty), ": ")})`;
|
|
267
|
+
if (protocolError === "invalid_grant") return yield* OAuthGrantRejected.make({
|
|
268
|
+
message,
|
|
269
|
+
...errorSubtype === void 0 ? {} : { errorSubtype }
|
|
270
|
+
});
|
|
271
|
+
return yield* OAuthConnectionValidationFailed.make({ message });
|
|
272
|
+
}
|
|
273
|
+
return yield* pipe(response.json, Effect.mapError((cause) => OAuthConnectionValidationFailed.make({
|
|
274
|
+
cause: Redacted.make(cause),
|
|
275
|
+
message: `${input.failurePrefix} (network)`
|
|
276
|
+
})), Effect.flatMap((raw) => pipe(Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown))(raw), Effect.mapError(() => OAuthConnectionValidationFailed.make({ message: "invalid oauth token response" })))));
|
|
277
|
+
});
|
|
278
|
+
const numberFromToken = (value) => {
|
|
279
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
280
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
281
|
+
const parsed = Number(value);
|
|
282
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
const normalizeTokenResponse = Effect.fn("OAuthFlow.normalizeTokenResponse")(function* (input) {
|
|
286
|
+
const response = yield* pipe(Schema.decodeUnknownEffect(input.method.oauth2?.tokenResponse ?? OAuth2ProviderTokenResponse)(input.raw), Effect.mapError(() => OAuthConnectionValidationFailed.make({ message: "invalid oauth token response" })));
|
|
287
|
+
const accessToken = yield* requiredString(response.access_token, "oauth token response missing access_token");
|
|
288
|
+
if (response.refresh_token?.trim() === "") return yield* OAuthConnectionValidationFailed.make({ message: "invalid oauth token response" });
|
|
289
|
+
const refreshToken = response.refresh_token ?? input.previousRefreshToken;
|
|
290
|
+
const expiresIn = numberFromToken(response.expires_in);
|
|
291
|
+
if (response.expires_in !== void 0 && (expiresIn === void 0 || expiresIn < 0)) return yield* OAuthConnectionValidationFailed.make({ message: "invalid oauth token response" });
|
|
292
|
+
const now = yield* Clock.currentTimeMillis;
|
|
293
|
+
const absoluteExpiry = response.expires_at === void 0 ? void 0 : Date.parse(response.expires_at);
|
|
294
|
+
if (absoluteExpiry !== void 0 && !Number.isFinite(absoluteExpiry)) return yield* OAuthConnectionValidationFailed.make({ message: "invalid oauth token response" });
|
|
295
|
+
return {
|
|
296
|
+
accessToken,
|
|
297
|
+
...refreshToken === void 0 ? {} : { refreshToken },
|
|
298
|
+
...response.token_type === void 0 ? {} : { tokenType: response.token_type },
|
|
299
|
+
...expiresIn === void 0 ? {} : {
|
|
300
|
+
expiresAt: now + expiresIn * 1e3,
|
|
301
|
+
expiresIn
|
|
302
|
+
},
|
|
303
|
+
...absoluteExpiry === void 0 ? {} : { expiresAt: absoluteExpiry },
|
|
304
|
+
...response.scope === void 0 ? {} : { scope: response.scope },
|
|
305
|
+
raw: input.raw
|
|
306
|
+
};
|
|
307
|
+
});
|
|
308
|
+
var Service = class extends Context.Service()("@dual-os/sdk/OAuthFlow") {};
|
|
309
|
+
/** Deterministic OAuthFlow test layer that avoids external provider traffic. */
|
|
310
|
+
const testing = (options = {}) => Layer.succeed(Service, Service.of({
|
|
311
|
+
complete: Effect.fn("OAuthFlow.testing.complete")(function* (input) {
|
|
312
|
+
if (input.query["state"] !== input.continuation.state) return yield* new OAuthConnectionValidationFailed({ message: "oauth state mismatch" });
|
|
313
|
+
if (input.query["error"] !== void 0) return yield* new OAuthProviderRejected({
|
|
314
|
+
error: input.query["error"],
|
|
315
|
+
errorDescription: input.query["error_description"]
|
|
316
|
+
});
|
|
317
|
+
if (input.query["code"] === void 0 || input.query["code"].trim() === "") return yield* new OAuthConnectionValidationFailed({ message: "missing oauth authorization code" });
|
|
318
|
+
const tokens = {
|
|
319
|
+
accessToken: options.accessToken ?? `test_access_${input.query["code"]}`,
|
|
320
|
+
expiresIn: options.expiresIn ?? 3600,
|
|
321
|
+
raw: {
|
|
322
|
+
code: input.query["code"],
|
|
323
|
+
grantType: "authorization_code",
|
|
324
|
+
testing: true
|
|
325
|
+
},
|
|
326
|
+
refreshToken: options.refreshToken ?? `test_refresh_${input.query["code"]}`,
|
|
327
|
+
tokenType: options.tokenType ?? "Bearer"
|
|
328
|
+
};
|
|
329
|
+
return {
|
|
330
|
+
material: tokens,
|
|
331
|
+
tokens
|
|
332
|
+
};
|
|
333
|
+
}),
|
|
334
|
+
refresh: Effect.fn("OAuthFlow.testing.refresh")(function* (input) {
|
|
335
|
+
const tokens = {
|
|
336
|
+
accessToken: options.refreshedAccessToken ?? `test_refreshed_${input.refreshToken}`,
|
|
337
|
+
expiresIn: options.expiresIn ?? 3600,
|
|
338
|
+
raw: {
|
|
339
|
+
grantType: "refresh_token",
|
|
340
|
+
testing: true
|
|
341
|
+
},
|
|
342
|
+
refreshToken: input.refreshToken,
|
|
343
|
+
tokenType: options.tokenType ?? "Bearer"
|
|
344
|
+
};
|
|
345
|
+
yield* Effect.void;
|
|
346
|
+
return {
|
|
347
|
+
material: tokens,
|
|
348
|
+
tokens
|
|
349
|
+
};
|
|
350
|
+
}),
|
|
351
|
+
start: Effect.fn("OAuthFlow.testing.start")(function* (input) {
|
|
352
|
+
const oauth = input.method.oauth2;
|
|
353
|
+
if (oauth === void 0) return yield* new OAuthConnectionValidationFailed({ message: "auth method is not oauth2" });
|
|
354
|
+
const client = typeof oauth.client === "function" ? oauth.client({ setup: input.setup }) : oauth.client;
|
|
355
|
+
const state = `test_state_${yield* Random.nextInt}`;
|
|
356
|
+
const pkceVerifier = oauth.pkce === false ? null : `test_pkce_${yield* Random.nextInt}`;
|
|
357
|
+
const authorizationUrl = new URL(resolveEndpoint(oauth.authorizationUrl, input.setup) ?? "https://oauth.testing.local/authorize");
|
|
358
|
+
authorizationUrl.searchParams.set("response_type", "code");
|
|
359
|
+
authorizationUrl.searchParams.set("client_id", globalThis.String(client.clientId));
|
|
360
|
+
authorizationUrl.searchParams.set("redirect_uri", input.redirectUri);
|
|
361
|
+
authorizationUrl.searchParams.set("state", state);
|
|
362
|
+
if (oauth.scopes.length > 0) authorizationUrl.searchParams.set("scope", oauth.scopes.join(" "));
|
|
363
|
+
if (pkceVerifier !== null) {
|
|
364
|
+
authorizationUrl.searchParams.set("code_challenge", "testing-code-challenge");
|
|
365
|
+
authorizationUrl.searchParams.set("code_challenge_method", "S256");
|
|
366
|
+
}
|
|
367
|
+
for (const [key, value] of Object.entries(resolveParams(oauth.authorizationParams, input.setup))) authorizationUrl.searchParams.set(key, value);
|
|
368
|
+
return {
|
|
369
|
+
authorizationUrl: authorizationUrl.toString(),
|
|
370
|
+
continuation: {
|
|
371
|
+
expiresAt: new Date((yield* Clock.currentTimeMillis) + (input.ttlMs ?? DefaultFlowTtlMs)),
|
|
372
|
+
pkceVerifier,
|
|
373
|
+
redirectUri: input.redirectUri,
|
|
374
|
+
state
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
})
|
|
378
|
+
}));
|
|
379
|
+
/** Live protocol layer that reads OAuth client config from Effect ConfigProvider. */
|
|
380
|
+
const layer = Layer.effect(Service, Effect.gen(function* () {
|
|
381
|
+
const configProvider = yield* ConfigProvider.ConfigProvider;
|
|
382
|
+
const client = yield* HttpClient.HttpClient;
|
|
383
|
+
const start = Effect.fn("OAuthFlow.start")(function* (input) {
|
|
384
|
+
const config = yield* resolveOAuthConfig({
|
|
385
|
+
client,
|
|
386
|
+
configProvider,
|
|
387
|
+
method: input.method,
|
|
388
|
+
setup: input.setup
|
|
389
|
+
});
|
|
390
|
+
const expiresAt = new Date((yield* Clock.currentTimeMillis) + (input.ttlMs ?? DefaultFlowTtlMs));
|
|
391
|
+
const state = randomBase64Url(32);
|
|
392
|
+
const pkceVerifier = config.pkce ? randomBase64Url(32) : void 0;
|
|
393
|
+
const pkceChallenge = pkceVerifier === void 0 ? void 0 : yield* sha256Base64Url(pkceVerifier);
|
|
394
|
+
return {
|
|
395
|
+
authorizationUrl: buildAuthorizationUrl({
|
|
396
|
+
config,
|
|
397
|
+
pkceChallenge,
|
|
398
|
+
redirectUri: input.redirectUri,
|
|
399
|
+
state
|
|
400
|
+
}),
|
|
401
|
+
continuation: {
|
|
402
|
+
expiresAt,
|
|
403
|
+
pkceVerifier: pkceVerifier ?? null,
|
|
404
|
+
redirectUri: input.redirectUri,
|
|
405
|
+
state
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
});
|
|
409
|
+
const complete = Effect.fn("OAuthFlow.complete")(function* (input) {
|
|
410
|
+
if (input.query["state"] !== input.continuation.state) return yield* new OAuthConnectionValidationFailed({ message: "oauth state mismatch" });
|
|
411
|
+
if (input.query["error"] !== void 0) return yield* new OAuthProviderRejected({
|
|
412
|
+
error: input.query["error"],
|
|
413
|
+
errorDescription: input.query["error_description"]
|
|
414
|
+
});
|
|
415
|
+
const code = input.query["code"];
|
|
416
|
+
if (code === void 0 || code.trim() === "") return yield* new OAuthConnectionValidationFailed({ message: "missing oauth authorization code" });
|
|
417
|
+
const config = yield* resolveOAuthConfig({
|
|
418
|
+
client,
|
|
419
|
+
configProvider,
|
|
420
|
+
method: input.method,
|
|
421
|
+
setup: input.setup
|
|
422
|
+
});
|
|
423
|
+
if (config.expectedIssuer !== void 0 && input.query["iss"] !== void 0 && input.query["iss"] !== config.expectedIssuer) return yield* new OAuthConnectionValidationFailed({ message: "oauth issuer mismatch" });
|
|
424
|
+
const tokenParams = new URLSearchParams({
|
|
425
|
+
code,
|
|
426
|
+
grant_type: "authorization_code",
|
|
427
|
+
redirect_uri: input.continuation.redirectUri
|
|
428
|
+
});
|
|
429
|
+
if (input.continuation.pkceVerifier !== null) tokenParams.set("code_verifier", input.continuation.pkceVerifier);
|
|
430
|
+
const rawTokens = yield* pipe(postTokenRequest({
|
|
431
|
+
client,
|
|
432
|
+
config,
|
|
433
|
+
failurePrefix: "oauth token exchange failed",
|
|
434
|
+
params: tokenParams
|
|
435
|
+
}), Effect.catchTag("OAuthGrantRejected", (error) => Effect.fail(OAuthConnectionValidationFailed.make({ message: error.message }))));
|
|
436
|
+
const tokens = yield* normalizeTokenResponse({
|
|
437
|
+
method: input.method,
|
|
438
|
+
raw: rawTokens
|
|
439
|
+
});
|
|
440
|
+
return {
|
|
441
|
+
material: tokens,
|
|
442
|
+
tokens
|
|
443
|
+
};
|
|
444
|
+
});
|
|
445
|
+
const refresh = Effect.fn("OAuthFlow.refresh")(function* (input) {
|
|
446
|
+
const config = yield* resolveOAuthConfig({
|
|
447
|
+
client,
|
|
448
|
+
configProvider,
|
|
449
|
+
method: input.method,
|
|
450
|
+
setup: input.setup
|
|
451
|
+
});
|
|
452
|
+
const rawTokens = yield* postTokenRequest({
|
|
453
|
+
client,
|
|
454
|
+
config,
|
|
455
|
+
failurePrefix: "oauth token refresh failed",
|
|
456
|
+
params: new URLSearchParams({
|
|
457
|
+
grant_type: "refresh_token",
|
|
458
|
+
refresh_token: input.refreshToken
|
|
459
|
+
})
|
|
460
|
+
});
|
|
461
|
+
const tokens = yield* normalizeTokenResponse({
|
|
462
|
+
method: input.method,
|
|
463
|
+
previousRefreshToken: input.refreshToken,
|
|
464
|
+
raw: rawTokens
|
|
465
|
+
});
|
|
466
|
+
return {
|
|
467
|
+
material: tokens,
|
|
468
|
+
tokens
|
|
469
|
+
};
|
|
470
|
+
});
|
|
471
|
+
return Service.of({
|
|
472
|
+
complete,
|
|
473
|
+
refresh,
|
|
474
|
+
start
|
|
475
|
+
});
|
|
476
|
+
})).pipe(Layer.provide(FetchHttpClient.layer));
|
|
477
|
+
const oauth2TokenRefresherLayer = Layer.effect(Service$1, Effect.gen(function* () {
|
|
478
|
+
const flow = yield* Service;
|
|
479
|
+
return Service$1.of({ refresh: (input) => flow.refresh({
|
|
480
|
+
method: input.method,
|
|
481
|
+
refreshToken: input.refreshToken,
|
|
482
|
+
setup: input.setup
|
|
483
|
+
}).pipe(Effect.map((refreshed) => refreshed.tokens)) });
|
|
484
|
+
}));
|
|
485
|
+
//#endregion
|
|
486
|
+
export { OAuthConnectionValidationFailed, OAuthFlow_exports as OAuthFlow, OAuthFlow_exports, OAuthGrantRejected, OAuthProviderRejected, Service, layer, oauth2TokenRefresherLayer, testing };
|
|
487
|
+
|
|
488
|
+
//# sourceMappingURL=OAuthFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthFlow.js","names":["AuthMethod.OAuth2ProviderTokenResponse","OAuth2TokenRefresher.Service"],"sources":["../src/OAuthFlow.ts"],"sourcesContent":["import type { Config } from \"effect\";\nimport {\n Array,\n Clock,\n ConfigProvider,\n Context,\n Effect,\n HashSet,\n Layer,\n Predicate,\n Random,\n Redacted,\n Record,\n Schema,\n String,\n pipe,\n} from \"effect\";\n\n/**\n * OAuth protocol runner used by ConnectionStore.\n *\n * OAuthFlow owns provider-facing protocol mechanics: resolving OAuth client\n * configuration, optional discovery, authorization URL construction, token\n * exchange, token refresh, and token response normalization. It does not persist\n * OAuth sessions or create Connections; ConnectionStore owns those product\n * lifecycle records and calls this service for the protocol steps.\n */\nimport { Base64, Base64Url } from \"effect/encoding\";\nimport { FetchHttpClient, HttpClient, HttpClientRequest } from \"effect/http\";\n\nimport * as AuthMethod from \"./AuthMethod.ts\";\nimport * as OAuth2TokenRefresher from \"./OAuth2TokenRefresher.ts\";\n\nexport class OAuthConnectionValidationFailed extends Schema.TaggedError<OAuthConnectionValidationFailed>()(\n \"OAuthConnectionValidationFailed\",\n { cause: Schema.optionalKey(Schema.Redacted(Schema.Defect())), message: Schema.String },\n) {}\nexport class OAuthProviderRejected extends Schema.TaggedError<OAuthProviderRejected>()(\n \"OAuthProviderRejected\",\n { error: Schema.String, errorDescription: Schema.optional(Schema.String) },\n) {}\nexport class OAuthGrantRejected extends Schema.TaggedError<OAuthGrantRejected>()(\n \"OAuthGrantRejected\",\n { errorSubtype: Schema.optionalKey(Schema.String), message: Schema.String },\n) {}\n\nconst DefaultFlowTtlMs = 10 * 60 * 1000;\n\nconst OAuthEndpoints = Schema.Struct({\n authorizationUrl: Schema.String,\n expectedIssuer: Schema.optional(Schema.String),\n tokenUrl: Schema.String,\n});\ntype OAuthEndpoints = typeof OAuthEndpoints.Type;\n\nconst ClientAuthentication = Schema.Union([\n Schema.Literal(\"basic\"),\n Schema.Literal(\"post\"),\n Schema.Literal(\"none\"),\n]);\n\nconst ReservedTokenHeaders = HashSet.fromIterable([\n \"accept\",\n \"accept-charset\",\n \"accept-encoding\",\n \"access-control-request-headers\",\n \"access-control-request-method\",\n \"authorization\",\n \"connection\",\n \"content-encoding\",\n \"content-length\",\n \"content-type\",\n \"cookie\",\n \"cookie2\",\n \"date\",\n \"dnt\",\n \"expect\",\n \"host\",\n \"keep-alive\",\n \"origin\",\n \"permissions-policy\",\n \"referer\",\n \"set-cookie\",\n \"te\",\n \"trailer\",\n \"transfer-encoding\",\n \"upgrade\",\n \"user-agent\",\n \"via\",\n]);\n\ntype TokenHeaders = typeof TokenHeaders.Type;\nconst TokenHeaders = Schema.Record(\n Schema.String,\n Schema.String.check(Schema.isPattern(/^[\\t\\x20-\\x7e\\x80-\\xff]*$/u)),\n).check(\n Schema.makeFilter(\n headers => {\n const names = Array.map(Record.keys(headers), String.toLowerCase);\n return (\n HashSet.size(HashSet.fromIterable(names)) === names.length &&\n Array.every(\n names,\n name =>\n /^[!#$%&'*+.^_`|~0-9a-z-]+$/u.test(name) &&\n !HashSet.has(ReservedTokenHeaders, name) &&\n !String.startsWith(\"proxy-\")(name) &&\n !String.startsWith(\"sec-\")(name),\n )\n );\n },\n { message: \"Invalid provider token headers\" },\n ),\n);\n\nconst ResolvedOAuth2Config = Schema.Struct({\n authorizationParams: Schema.Record(Schema.String, Schema.String),\n authorizationUrl: Schema.String,\n clientAuthentication: ClientAuthentication,\n clientId: Schema.String,\n clientSecret: Schema.optional(Schema.String),\n expectedIssuer: Schema.optional(Schema.String),\n pkce: Schema.Boolean,\n scopes: Schema.Array(Schema.String),\n tokenHeaders: TokenHeaders,\n tokenParams: Schema.Record(Schema.String, Schema.String),\n tokenRequestEncoding: Schema.Literals([\"form\", \"json\"]),\n tokenUrl: Schema.String,\n});\ntype ResolvedOAuth2Config = typeof ResolvedOAuth2Config.Type;\n\nconst ProviderDiscoveryDocument = Schema.Struct({\n authorization_endpoint: Schema.String,\n issuer: Schema.optional(Schema.String),\n token_endpoint: Schema.String,\n});\n\nconst decodeDiscoveryDocument = Schema.decodeUnknownSync(ProviderDiscoveryDocument);\nconst encodeJsonTokenParams = Schema.encodeSync(\n Schema.fromJsonString(Schema.Record(Schema.String, Schema.String)),\n);\nconst decodeProviderTokenError = Schema.decodeSync(\n Schema.fromJsonString(\n Schema.Struct({\n error: Schema.Literals([\n \"invalid_request\",\n \"invalid_client\",\n \"invalid_grant\",\n \"unauthorized_client\",\n \"unsupported_grant_type\",\n \"invalid_scope\",\n \"server_error\",\n \"temporarily_unavailable\",\n ]),\n }),\n ),\n);\n// Only Google's documented reauthentication subtypes pass, so an echoed secret never reaches an error.\nconst decodeProviderTokenErrorSubtype = Schema.decodeSync(\n Schema.fromJsonString(\n Schema.Struct({ error_subtype: Schema.Literals([\"invalid_rapt\", \"rapt_required\"]) }),\n ),\n);\n\nconst randomBase64Url = (bytes: number): string => {\n const data = new Uint8Array(bytes);\n crypto.getRandomValues(data);\n return Base64Url.encode(data);\n};\n\nconst sha256Base64Url = (value: string): Effect.Effect<string> =>\n Effect.promise(() => crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(value))).pipe(\n Effect.map(digest => Base64Url.encode(new Uint8Array(digest))),\n );\n\nconst requiredString = (\n value: unknown,\n message: string,\n): Effect.Effect<string, OAuthConnectionValidationFailed> =>\n typeof value === \"string\" && value.trim() !== \"\"\n ? Effect.succeed(value)\n : Effect.fail(new OAuthConnectionValidationFailed({ message }));\n\nconst fetchDiscovery = Effect.fn(\"OAuthFlow.fetchDiscovery\")(function* (\n discoveryUrl: string,\n client: HttpClient.HttpClient,\n): Effect.fn.Return<OAuthEndpoints, OAuthConnectionValidationFailed> {\n const rawDocument = yield* pipe(\n HttpClient.filterStatusOk(client).get(discoveryUrl, {\n headers: { Accept: \"application/json\" },\n }),\n Effect.flatMap(response => response.json),\n Effect.mapError(cause =>\n OAuthConnectionValidationFailed.make({\n cause: Redacted.make(cause),\n message: \"failed to load oauth discovery document\",\n }),\n ),\n );\n\n const document = yield* Effect.try({\n catch: () =>\n new OAuthConnectionValidationFailed({ message: \"invalid oauth discovery document\" }),\n try: () => decodeDiscoveryDocument(rawDocument),\n });\n\n const authorizationUrl = yield* requiredString(\n document.authorization_endpoint,\n \"oauth discovery document missing authorization_endpoint\",\n );\n const tokenUrl = yield* requiredString(\n document.token_endpoint,\n \"oauth discovery document missing token_endpoint\",\n );\n const expectedIssuer =\n typeof document.issuer === \"string\" && document.issuer.trim() !== \"\"\n ? document.issuer\n : undefined;\n\n return {\n authorizationUrl,\n tokenUrl,\n ...(expectedIssuer === undefined ? {} : { expectedIssuer }),\n };\n});\n\nconst resolveEndpoint = (\n value: AuthMethod.OAuth2Endpoint<unknown> | undefined,\n setup: unknown,\n): string | undefined =>\n value === undefined ? undefined : typeof value === \"function\" ? value({ setup }) : value;\n\nconst resolveParams = (\n value: AuthMethod.OAuth2Params<unknown> | undefined,\n setup: unknown,\n): Readonly<Record<string, string>> =>\n typeof value === \"function\" ? value({ setup }) : (value ?? {});\n\nfunction invalidTokenHeaders() {\n return OAuthConnectionValidationFailed.make({ message: \"invalid oauth token headers\" });\n}\n\nconst resolveTokenHeaders = Effect.fnUntraced(function* (input: {\n value: AuthMethod.OAuth2Headers<unknown> | undefined;\n setup: unknown;\n}): Effect.fn.Return<TokenHeaders, OAuthConnectionValidationFailed> {\n const raw = yield* Effect.try({\n catch: invalidTokenHeaders,\n try: () => {\n if (Predicate.isFunction(input.value)) {\n return input.value({ setup: input.setup });\n }\n if (input.value === undefined) {\n return {};\n }\n return input.value;\n },\n });\n return yield* pipe(Schema.decodeEffect(TokenHeaders)(raw), Effect.mapError(invalidTokenHeaders));\n});\n\nconst resolveClientValue = Effect.fn(\"OAuthFlow.resolveClientValue\")(function* (input: {\n readonly value: string | Config.Config<string> | undefined;\n readonly field: string;\n readonly configProvider: ConfigProvider.ConfigProvider;\n}): Effect.fn.Return<string | undefined, OAuthConnectionValidationFailed> {\n if (input.value === undefined) {\n return undefined;\n }\n if (typeof input.value === \"string\") {\n return yield* requiredString(input.value, `${input.field} is empty`);\n }\n\n return yield* input.value\n .parse(input.configProvider)\n .pipe(\n Effect.mapError(\n cause =>\n new OAuthConnectionValidationFailed({\n message: `failed to resolve ${input.field}: ${cause.message}`,\n }),\n ),\n );\n});\n\nconst resolveOAuthConfig = Effect.fn(\"OAuthFlow.resolveOAuthConfig\")(function* (input: {\n readonly method: AuthMethod.Any;\n readonly setup: unknown;\n readonly configProvider: ConfigProvider.ConfigProvider;\n readonly client: HttpClient.HttpClient;\n}): Effect.fn.Return<ResolvedOAuth2Config, OAuthConnectionValidationFailed> {\n const oauth = input.method.oauth2;\n if (oauth === undefined) {\n return yield* new OAuthConnectionValidationFailed({ message: \"auth method is not oauth2\" });\n }\n\n const tokenHeaders = yield* resolveTokenHeaders({\n setup: input.setup,\n value: oauth.tokenHeaders,\n });\n\n const client =\n typeof oauth.client === \"function\" ? oauth.client({ setup: input.setup }) : oauth.client;\n const clientId = yield* resolveClientValue({\n configProvider: input.configProvider,\n field: \"clientId\",\n value: client.clientId,\n });\n const clientSecret = yield* resolveClientValue({\n configProvider: input.configProvider,\n field: \"clientSecret\",\n value: client.clientSecret,\n });\n if (clientId === undefined) {\n return yield* new OAuthConnectionValidationFailed({ message: \"missing oauth client id\" });\n }\n\n const authorizationUrl = resolveEndpoint(oauth.authorizationUrl, input.setup);\n const tokenUrl = resolveEndpoint(oauth.tokenUrl, input.setup);\n const discoveryUrl = resolveEndpoint(oauth.discoveryUrl, input.setup);\n\n const endpoints =\n authorizationUrl !== undefined && tokenUrl !== undefined\n ? { authorizationUrl, tokenUrl }\n : discoveryUrl === undefined\n ? yield* new OAuthConnectionValidationFailed({\n message: \"oauth method requires explicit endpoints or discoveryUrl\",\n })\n : yield* fetchDiscovery(discoveryUrl, input.client);\n\n return {\n ...endpoints,\n authorizationParams: resolveParams(oauth.authorizationParams, input.setup),\n clientAuthentication: client.authentication ?? \"basic\",\n clientId,\n clientSecret,\n pkce: oauth.pkce ?? true,\n scopes: oauth.scopes,\n tokenHeaders,\n tokenParams: resolveParams(oauth.tokenParams, input.setup),\n tokenRequestEncoding: oauth.tokenRequestEncoding ?? \"form\",\n };\n});\n\nconst buildAuthorizationUrl = (input: {\n readonly config: ResolvedOAuth2Config;\n readonly redirectUri: string;\n readonly state: string;\n readonly pkceChallenge: string | undefined;\n}): string => {\n const url = new URL(input.config.authorizationUrl);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", input.config.clientId);\n url.searchParams.set(\"redirect_uri\", input.redirectUri);\n url.searchParams.set(\"state\", input.state);\n if (input.config.scopes.length > 0) {\n url.searchParams.set(\"scope\", input.config.scopes.join(\" \"));\n }\n if (input.pkceChallenge !== undefined) {\n url.searchParams.set(\"code_challenge\", input.pkceChallenge);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n }\n for (const [key, value] of Object.entries(input.config.authorizationParams)) {\n url.searchParams.set(key, value);\n }\n return url.toString();\n};\n\nconst httpStatusClass = (status: number): string => {\n if (status >= 500) {\n return \"5xx\";\n }\n if (status >= 400) {\n return \"4xx\";\n }\n return \"unexpected\";\n};\n\nconst oauthProtocolErrorCode = (body: string): string | undefined => {\n try {\n return decodeProviderTokenError(body).error;\n } catch {\n return undefined;\n }\n};\n\nconst oauthProtocolErrorSubtype = (body: string): string | undefined => {\n try {\n return decodeProviderTokenErrorSubtype(body).error_subtype;\n } catch {\n return undefined;\n }\n};\n\nconst tokenRequestHeaders = (\n config: ResolvedOAuth2Config,\n): Effect.Effect<Record<string, string>, OAuthConnectionValidationFailed> => {\n const headers: Record<string, string> = {\n ...config.tokenHeaders,\n Accept: \"application/json\",\n \"Content-Type\":\n config.tokenRequestEncoding === \"json\"\n ? \"application/json\"\n : \"application/x-www-form-urlencoded\",\n };\n if (config.clientAuthentication === \"basic\") {\n if (config.clientSecret === undefined) {\n return Effect.fail(\n new OAuthConnectionValidationFailed({\n message: \"missing client secret for basic token auth\",\n }),\n );\n }\n headers[\"Authorization\"] =\n `Basic ${Base64.encode(`${config.clientId}:${config.clientSecret}`)}`;\n }\n return Effect.succeed(headers);\n};\n\nconst postTokenRequest = Effect.fn(\"OAuthFlow.postTokenRequest\")(function* (input: {\n readonly config: ResolvedOAuth2Config;\n readonly params: URLSearchParams;\n readonly failurePrefix: string;\n readonly client: HttpClient.HttpClient;\n}): Effect.fn.Return<\n Record<string, unknown>,\n OAuthConnectionValidationFailed | OAuthGrantRejected\n> {\n if (input.config.clientAuthentication === \"post\") {\n input.params.set(\"client_id\", input.config.clientId);\n if (input.config.clientSecret !== undefined) {\n input.params.set(\"client_secret\", input.config.clientSecret);\n }\n } else if (input.config.clientAuthentication === \"none\") {\n input.params.set(\"client_id\", input.config.clientId);\n }\n\n for (const [key, value] of Object.entries(input.config.tokenParams)) {\n input.params.set(key, value);\n }\n\n const headers = yield* tokenRequestHeaders(input.config);\n const request = HttpClientRequest.post(input.config.tokenUrl, { headers }).pipe(\n HttpClientRequest.bodyText(\n input.config.tokenRequestEncoding === \"json\"\n ? encodeJsonTokenParams(Record.fromEntries(input.params))\n : input.params.toString(),\n headers[\"Content-Type\"],\n ),\n );\n const response = yield* pipe(\n input.client.execute(request),\n Effect.provideService(\n FetchHttpClient.RequestInit,\n Record.isEmptyRecord(input.config.tokenHeaders) ? {} : { redirect: \"error\" },\n ),\n Effect.mapError(cause =>\n OAuthConnectionValidationFailed.make({\n cause: Redacted.make(cause),\n message: `${input.failurePrefix} (network)`,\n }),\n ),\n );\n if (response.status < 200 || response.status >= 300) {\n const body = yield* pipe(\n response.text,\n Effect.mapError(cause =>\n OAuthConnectionValidationFailed.make({\n cause: Redacted.make(cause),\n message: `${input.failurePrefix} (unreadable response)`,\n }),\n ),\n );\n const statusClass = httpStatusClass(response.status);\n const protocolError = oauthProtocolErrorCode(body);\n const errorSubtype = oauthProtocolErrorSubtype(body);\n const reason = Array.join(\n Array.filter([protocolError, errorSubtype], Predicate.isNotUndefined),\n \", \",\n );\n const message = `${input.failurePrefix} (${Array.join(Array.filter([statusClass, reason], String.isNonEmpty), \": \")})`;\n if (protocolError === \"invalid_grant\") {\n return yield* OAuthGrantRejected.make({\n message,\n ...(errorSubtype === undefined ? {} : { errorSubtype }),\n });\n }\n return yield* OAuthConnectionValidationFailed.make({ message });\n }\n return yield* pipe(\n response.json,\n Effect.mapError(cause =>\n OAuthConnectionValidationFailed.make({\n cause: Redacted.make(cause),\n message: `${input.failurePrefix} (network)`,\n }),\n ),\n Effect.flatMap(raw =>\n pipe(\n Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown))(raw),\n Effect.mapError(() =>\n OAuthConnectionValidationFailed.make({ message: \"invalid oauth token response\" }),\n ),\n ),\n ),\n );\n});\n\nconst numberFromToken = (value: unknown): number | undefined => {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n }\n return undefined;\n};\n\nconst normalizeTokenResponse = Effect.fn(\"OAuthFlow.normalizeTokenResponse\")(function* (input: {\n readonly raw: Record<string, unknown>;\n readonly method: AuthMethod.Any;\n readonly previousRefreshToken?: string | undefined;\n}): Effect.fn.Return<AuthMethod.OAuth2TokenResponse, OAuthConnectionValidationFailed> {\n const response = yield* pipe(\n Schema.decodeUnknownEffect(\n input.method.oauth2?.tokenResponse ?? AuthMethod.OAuth2ProviderTokenResponse,\n )(input.raw),\n Effect.mapError(() =>\n OAuthConnectionValidationFailed.make({ message: \"invalid oauth token response\" }),\n ),\n );\n const accessToken = yield* requiredString(\n response.access_token,\n \"oauth token response missing access_token\",\n );\n if (response.refresh_token?.trim() === \"\") {\n return yield* OAuthConnectionValidationFailed.make({ message: \"invalid oauth token response\" });\n }\n const refreshToken = response.refresh_token ?? input.previousRefreshToken;\n const expiresIn = numberFromToken(response.expires_in);\n if (response.expires_in !== undefined && (expiresIn === undefined || expiresIn < 0)) {\n return yield* OAuthConnectionValidationFailed.make({ message: \"invalid oauth token response\" });\n }\n const now = yield* Clock.currentTimeMillis;\n const absoluteExpiry =\n response.expires_at === undefined ? undefined : Date.parse(response.expires_at);\n if (absoluteExpiry !== undefined && !Number.isFinite(absoluteExpiry)) {\n return yield* OAuthConnectionValidationFailed.make({ message: \"invalid oauth token response\" });\n }\n\n return {\n accessToken,\n ...(refreshToken === undefined ? {} : { refreshToken }),\n ...(response.token_type === undefined ? {} : { tokenType: response.token_type }),\n ...(expiresIn === undefined ? {} : { expiresAt: now + expiresIn * 1000, expiresIn }),\n ...(absoluteExpiry === undefined ? {} : { expiresAt: absoluteExpiry }),\n ...(response.scope === undefined ? {} : { scope: response.scope }),\n raw: input.raw,\n };\n});\n\nexport type OAuthFlowError = OAuthConnectionValidationFailed | OAuthProviderRejected;\n\nexport interface OAuthFlowContinuation {\n readonly state: string;\n readonly redirectUri: string;\n readonly pkceVerifier: string | null;\n readonly expiresAt: Date;\n}\n\nexport interface OAuthFlowStarted {\n readonly authorizationUrl: string;\n readonly continuation: OAuthFlowContinuation;\n}\n\nexport interface OAuthFlowCompleted<Material> {\n readonly tokens: AuthMethod.OAuth2TokenResponse;\n readonly material: Material;\n}\n\nexport class Service extends Context.Service<\n Service,\n {\n readonly start: <Method extends AuthMethod.Any>(input: {\n readonly method: Method;\n readonly setup: AuthMethod.Setup<Method>;\n readonly redirectUri: string;\n readonly ttlMs?: number;\n }) => Effect.Effect<OAuthFlowStarted, OAuthFlowError>;\n readonly complete: <Method extends AuthMethod.Any>(input: {\n readonly method: Method;\n readonly setup: AuthMethod.Setup<Method>;\n readonly continuation: OAuthFlowContinuation;\n readonly query: Readonly<Record<string, string | undefined>>;\n }) => Effect.Effect<OAuthFlowCompleted<AuthMethod.OAuth2TokenResponse>, OAuthFlowError>;\n readonly refresh: <Method extends AuthMethod.Any>(input: {\n readonly method: Method;\n readonly setup: AuthMethod.Setup<Method>;\n readonly refreshToken: string;\n }) => Effect.Effect<\n OAuthFlowCompleted<AuthMethod.OAuth2TokenResponse>,\n OAuthFlowError | OAuthGrantRejected\n >;\n }\n>()(\"@dual-os/sdk/OAuthFlow\") {}\n\n/** Deterministic OAuthFlow test layer that avoids external provider traffic. */\nexport const testing = (\n options: {\n readonly accessToken?: string;\n readonly refreshToken?: string;\n readonly refreshedAccessToken?: string;\n readonly tokenType?: string;\n readonly expiresIn?: number;\n } = {},\n) =>\n Layer.succeed(\n Service,\n Service.of({\n complete: Effect.fn(\"OAuthFlow.testing.complete\")(function* <\n Method extends AuthMethod.Any,\n >(input: {\n readonly method: Method;\n readonly setup: AuthMethod.Setup<Method>;\n readonly continuation: OAuthFlowContinuation;\n readonly query: Readonly<Record<string, string | undefined>>;\n }) {\n if (input.query[\"state\"] !== input.continuation.state) {\n return yield* new OAuthConnectionValidationFailed({ message: \"oauth state mismatch\" });\n }\n if (input.query[\"error\"] !== undefined) {\n return yield* new OAuthProviderRejected({\n error: input.query[\"error\"],\n errorDescription: input.query[\"error_description\"],\n });\n }\n if (input.query[\"code\"] === undefined || input.query[\"code\"].trim() === \"\") {\n return yield* new OAuthConnectionValidationFailed({\n message: \"missing oauth authorization code\",\n });\n }\n\n const tokens: AuthMethod.OAuth2TokenResponse = {\n accessToken: options.accessToken ?? `test_access_${input.query[\"code\"]}`,\n expiresIn: options.expiresIn ?? 3600,\n raw: { code: input.query[\"code\"], grantType: \"authorization_code\", testing: true },\n refreshToken: options.refreshToken ?? `test_refresh_${input.query[\"code\"]}`,\n tokenType: options.tokenType ?? \"Bearer\",\n };\n return { material: tokens, tokens };\n }),\n refresh: Effect.fn(\"OAuthFlow.testing.refresh\")(function* <\n Method extends AuthMethod.Any,\n >(input: {\n readonly method: Method;\n readonly setup: AuthMethod.Setup<Method>;\n readonly refreshToken: string;\n }) {\n const tokens: AuthMethod.OAuth2TokenResponse = {\n accessToken: options.refreshedAccessToken ?? `test_refreshed_${input.refreshToken}`,\n expiresIn: options.expiresIn ?? 3600,\n raw: { grantType: \"refresh_token\", testing: true },\n refreshToken: input.refreshToken,\n tokenType: options.tokenType ?? \"Bearer\",\n };\n yield* Effect.void;\n return { material: tokens, tokens };\n }),\n start: Effect.fn(\"OAuthFlow.testing.start\")(function* <Method extends AuthMethod.Any>(input: {\n readonly method: Method;\n readonly setup: AuthMethod.Setup<Method>;\n readonly redirectUri: string;\n readonly ttlMs?: number;\n }) {\n const oauth = input.method.oauth2;\n if (oauth === undefined) {\n return yield* new OAuthConnectionValidationFailed({\n message: \"auth method is not oauth2\",\n });\n }\n const client =\n typeof oauth.client === \"function\" ? oauth.client({ setup: input.setup }) : oauth.client;\n const state = `test_state_${yield* Random.nextInt}`;\n const pkceVerifier = oauth.pkce === false ? null : `test_pkce_${yield* Random.nextInt}`;\n const authorizationUrl = new URL(\n resolveEndpoint(oauth.authorizationUrl, input.setup) ??\n \"https://oauth.testing.local/authorize\",\n );\n authorizationUrl.searchParams.set(\"response_type\", \"code\");\n authorizationUrl.searchParams.set(\"client_id\", globalThis.String(client.clientId));\n authorizationUrl.searchParams.set(\"redirect_uri\", input.redirectUri);\n authorizationUrl.searchParams.set(\"state\", state);\n if (oauth.scopes.length > 0) {\n authorizationUrl.searchParams.set(\"scope\", oauth.scopes.join(\" \"));\n }\n if (pkceVerifier !== null) {\n authorizationUrl.searchParams.set(\"code_challenge\", \"testing-code-challenge\");\n authorizationUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n }\n for (const [key, value] of Object.entries(\n resolveParams(oauth.authorizationParams, input.setup),\n )) {\n authorizationUrl.searchParams.set(key, value);\n }\n\n return {\n authorizationUrl: authorizationUrl.toString(),\n continuation: {\n expiresAt: new Date(\n (yield* Clock.currentTimeMillis) + (input.ttlMs ?? DefaultFlowTtlMs),\n ),\n pkceVerifier,\n redirectUri: input.redirectUri,\n state,\n },\n };\n }),\n }),\n );\n\n/** Live protocol layer that reads OAuth client config from Effect ConfigProvider. */\nexport const layer = Layer.effect(\n Service,\n Effect.gen(function* () {\n const configProvider = yield* ConfigProvider.ConfigProvider;\n const client = yield* HttpClient.HttpClient;\n\n const start = Effect.fn(\"OAuthFlow.start\")(function* <Method extends AuthMethod.Any>(input: {\n readonly method: Method;\n readonly setup: AuthMethod.Setup<Method>;\n readonly redirectUri: string;\n readonly ttlMs?: number;\n }) {\n const config = yield* resolveOAuthConfig({\n client,\n configProvider,\n method: input.method,\n setup: input.setup,\n });\n const expiresAt = new Date(\n (yield* Clock.currentTimeMillis) + (input.ttlMs ?? DefaultFlowTtlMs),\n );\n const state = randomBase64Url(32);\n const pkceVerifier = config.pkce ? randomBase64Url(32) : undefined;\n const pkceChallenge =\n pkceVerifier === undefined ? undefined : yield* sha256Base64Url(pkceVerifier);\n\n return {\n authorizationUrl: buildAuthorizationUrl({\n config,\n pkceChallenge,\n redirectUri: input.redirectUri,\n state,\n }),\n continuation: {\n expiresAt,\n pkceVerifier: pkceVerifier ?? null,\n redirectUri: input.redirectUri,\n state,\n },\n };\n });\n\n const complete = Effect.fn(\"OAuthFlow.complete\")(function* <\n Method extends AuthMethod.Any,\n >(input: {\n readonly method: Method;\n readonly setup: AuthMethod.Setup<Method>;\n readonly continuation: OAuthFlowContinuation;\n readonly query: Readonly<Record<string, string | undefined>>;\n }) {\n if (input.query[\"state\"] !== input.continuation.state) {\n return yield* new OAuthConnectionValidationFailed({ message: \"oauth state mismatch\" });\n }\n if (input.query[\"error\"] !== undefined) {\n return yield* new OAuthProviderRejected({\n error: input.query[\"error\"],\n errorDescription: input.query[\"error_description\"],\n });\n }\n const code = input.query[\"code\"];\n if (code === undefined || code.trim() === \"\") {\n return yield* new OAuthConnectionValidationFailed({\n message: \"missing oauth authorization code\",\n });\n }\n\n const config = yield* resolveOAuthConfig({\n client,\n configProvider,\n method: input.method,\n setup: input.setup,\n });\n if (\n config.expectedIssuer !== undefined &&\n input.query[\"iss\"] !== undefined &&\n input.query[\"iss\"] !== config.expectedIssuer\n ) {\n return yield* new OAuthConnectionValidationFailed({ message: \"oauth issuer mismatch\" });\n }\n\n const tokenParams = new URLSearchParams({\n code,\n grant_type: \"authorization_code\",\n redirect_uri: input.continuation.redirectUri,\n });\n if (input.continuation.pkceVerifier !== null) {\n tokenParams.set(\"code_verifier\", input.continuation.pkceVerifier);\n }\n\n const rawTokens = yield* pipe(\n postTokenRequest({\n client,\n config,\n failurePrefix: \"oauth token exchange failed\",\n params: tokenParams,\n }),\n Effect.catchTag(\"OAuthGrantRejected\", error =>\n Effect.fail(OAuthConnectionValidationFailed.make({ message: error.message })),\n ),\n );\n const tokens = yield* normalizeTokenResponse({ method: input.method, raw: rawTokens });\n return { material: tokens, tokens };\n });\n\n const refresh = Effect.fn(\"OAuthFlow.refresh\")(function* <\n Method extends AuthMethod.Any,\n >(input: {\n readonly method: Method;\n readonly setup: AuthMethod.Setup<Method>;\n readonly refreshToken: string;\n }) {\n const config = yield* resolveOAuthConfig({\n client,\n configProvider,\n method: input.method,\n setup: input.setup,\n });\n const rawTokens = yield* postTokenRequest({\n client,\n config,\n failurePrefix: \"oauth token refresh failed\",\n params: new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: input.refreshToken,\n }),\n });\n const tokens = yield* normalizeTokenResponse({\n method: input.method,\n previousRefreshToken: input.refreshToken,\n raw: rawTokens,\n });\n return { material: tokens, tokens };\n });\n\n return Service.of({ complete, refresh, start });\n }),\n).pipe(Layer.provide(FetchHttpClient.layer));\n\nexport const oauth2TokenRefresherLayer = Layer.effect(\n OAuth2TokenRefresher.Service,\n Effect.gen(function* () {\n const flow = yield* Service;\n return OAuth2TokenRefresher.Service.of({\n refresh: input =>\n flow\n .refresh({ method: input.method, refreshToken: input.refreshToken, setup: input.setup })\n .pipe(Effect.map(refreshed => refreshed.tokens)),\n });\n }),\n);\n\nexport * as OAuthFlow from \"./OAuthFlow.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAa,kCAAb,cAAqD,OAAO,YAA6C,CAAC,CACxG,mCACA;CAAE,OAAO,OAAO,YAAY,OAAO,SAAS,OAAO,OAAO,CAAC,CAAC;CAAG,SAAS,OAAO;AAAO,CACxF,CAAC,CAAC,CAAC;AACH,IAAa,wBAAb,cAA2C,OAAO,YAAmC,CAAC,CACpF,yBACA;CAAE,OAAO,OAAO;CAAQ,kBAAkB,OAAO,SAAS,OAAO,MAAM;AAAE,CAC3E,CAAC,CAAC,CAAC;AACH,IAAa,qBAAb,cAAwC,OAAO,YAAgC,CAAC,CAC9E,sBACA;CAAE,cAAc,OAAO,YAAY,OAAO,MAAM;CAAG,SAAS,OAAO;AAAO,CAC5E,CAAC,CAAC,CAAC;AAEH,MAAM,mBAAmB;AAEF,OAAO,OAAO;CACnC,kBAAkB,OAAO;CACzB,gBAAgB,OAAO,SAAS,OAAO,MAAM;CAC7C,UAAU,OAAO;AACnB,CAAC;AAGD,MAAM,uBAAuB,OAAO,MAAM;CACxC,OAAO,QAAQ,OAAO;CACtB,OAAO,QAAQ,MAAM;CACrB,OAAO,QAAQ,MAAM;AACvB,CAAC;AAED,MAAM,uBAAuB,QAAQ,aAAa;CAChD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAGD,MAAM,eAAe,OAAO,OAC1B,OAAO,QACP,OAAO,OAAO,MAAM,OAAO,UAAU,4BAA4B,CAAC,CACpE,CAAC,CAAC,MACA,OAAO,YACL,YAAW;CACT,MAAM,QAAQ,MAAM,IAAI,OAAO,KAAK,OAAO,GAAG,OAAO,WAAW;CAChE,OACE,QAAQ,KAAK,QAAQ,aAAa,KAAK,CAAC,MAAM,MAAM,UACpD,MAAM,MACJ,QACA,SACE,8BAA8B,KAAK,IAAI,KACvC,CAAC,QAAQ,IAAI,sBAAsB,IAAI,KACvC,CAAC,OAAO,WAAW,QAAQ,CAAC,CAAC,IAAI,KACjC,CAAC,OAAO,WAAW,MAAM,CAAC,CAAC,IAAI,CACnC;AAEJ,GACA,EAAE,SAAS,iCAAiC,CAC9C,CACF;AAE6B,OAAO,OAAO;CACzC,qBAAqB,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM;CAC/D,kBAAkB,OAAO;CACzB,sBAAsB;CACtB,UAAU,OAAO;CACjB,cAAc,OAAO,SAAS,OAAO,MAAM;CAC3C,gBAAgB,OAAO,SAAS,OAAO,MAAM;CAC7C,MAAM,OAAO;CACb,QAAQ,OAAO,MAAM,OAAO,MAAM;CAClC,cAAc;CACd,aAAa,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM;CACvD,sBAAsB,OAAO,SAAS,CAAC,QAAQ,MAAM,CAAC;CACtD,UAAU,OAAO;AACnB,CAAC;AAGD,MAAM,4BAA4B,OAAO,OAAO;CAC9C,wBAAwB,OAAO;CAC/B,QAAQ,OAAO,SAAS,OAAO,MAAM;CACrC,gBAAgB,OAAO;AACzB,CAAC;AAED,MAAM,0BAA0B,OAAO,kBAAkB,yBAAyB;AAClF,MAAM,wBAAwB,OAAO,WACnC,OAAO,eAAe,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM,CAAC,CACnE;AACA,MAAM,2BAA2B,OAAO,WACtC,OAAO,eACL,OAAO,OAAO,EACZ,OAAO,OAAO,SAAS;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC,EACH,CAAC,CACH,CACF;AAEA,MAAM,kCAAkC,OAAO,WAC7C,OAAO,eACL,OAAO,OAAO,EAAE,eAAe,OAAO,SAAS,CAAC,gBAAgB,eAAe,CAAC,EAAE,CAAC,CACrF,CACF;AAEA,MAAM,mBAAmB,UAA0B;CACjD,MAAM,OAAO,IAAI,WAAW,KAAK;CACjC,OAAO,gBAAgB,IAAI;CAC3B,OAAO,UAAU,OAAO,IAAI;AAC9B;AAEA,MAAM,mBAAmB,UACvB,OAAO,cAAc,OAAO,OAAO,OAAO,WAAW,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KACrF,OAAO,KAAI,WAAU,UAAU,OAAO,IAAI,WAAW,MAAM,CAAC,CAAC,CAC/D;AAEF,MAAM,kBACJ,OACA,YAEA,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,KAC1C,OAAO,QAAQ,KAAK,IACpB,OAAO,KAAK,IAAI,gCAAgC,EAAE,QAAQ,CAAC,CAAC;AAElE,MAAM,iBAAiB,OAAO,GAAG,0BAA0B,CAAC,CAAC,WAC3D,cACA,QACmE;CACnE,MAAM,cAAc,OAAO,KACzB,WAAW,eAAe,MAAM,CAAC,CAAC,IAAI,cAAc,EAClD,SAAS,EAAE,QAAQ,mBAAmB,EACxC,CAAC,GACD,OAAO,SAAQ,aAAY,SAAS,IAAI,GACxC,OAAO,UAAS,UACd,gCAAgC,KAAK;EACnC,OAAO,SAAS,KAAK,KAAK;EAC1B,SAAS;CACX,CAAC,CACH,CACF;CAEA,MAAM,WAAW,OAAO,OAAO,IAAI;EACjC,aACE,IAAI,gCAAgC,EAAE,SAAS,mCAAmC,CAAC;EACrF,WAAW,wBAAwB,WAAW;CAChD,CAAC;CAED,MAAM,mBAAmB,OAAO,eAC9B,SAAS,wBACT,yDACF;CACA,MAAM,WAAW,OAAO,eACtB,SAAS,gBACT,iDACF;CACA,MAAM,iBACJ,OAAO,SAAS,WAAW,YAAY,SAAS,OAAO,KAAK,MAAM,KAC9D,SAAS,SACT,KAAA;CAEN,OAAO;EACL;EACA;EACA,GAAI,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,eAAe;CAC3D;AACF,CAAC;AAED,MAAM,mBACJ,OACA,UAEA,UAAU,KAAA,IAAY,KAAA,IAAY,OAAO,UAAU,aAAa,MAAM,EAAE,MAAM,CAAC,IAAI;AAErF,MAAM,iBACJ,OACA,UAEA,OAAO,UAAU,aAAa,MAAM,EAAE,MAAM,CAAC,IAAK,SAAS,CAAC;AAE9D,SAAS,sBAAsB;CAC7B,OAAO,gCAAgC,KAAK,EAAE,SAAS,8BAA8B,CAAC;AACxF;AAEA,MAAM,sBAAsB,OAAO,WAAW,WAAW,OAGW;CAClE,MAAM,MAAM,OAAO,OAAO,IAAI;EAC5B,OAAO;EACP,WAAW;GACT,IAAI,UAAU,WAAW,MAAM,KAAK,GAClC,OAAO,MAAM,MAAM,EAAE,OAAO,MAAM,MAAM,CAAC;GAE3C,IAAI,MAAM,UAAU,KAAA,GAClB,OAAO,CAAC;GAEV,OAAO,MAAM;EACf;CACF,CAAC;CACD,OAAO,OAAO,KAAK,OAAO,aAAa,YAAY,CAAC,CAAC,GAAG,GAAG,OAAO,SAAS,mBAAmB,CAAC;AACjG,CAAC;AAED,MAAM,qBAAqB,OAAO,GAAG,8BAA8B,CAAC,CAAC,WAAW,OAIN;CACxE,IAAI,MAAM,UAAU,KAAA,GAClB;CAEF,IAAI,OAAO,MAAM,UAAU,UACzB,OAAO,OAAO,eAAe,MAAM,OAAO,GAAG,MAAM,MAAM,UAAU;CAGrE,OAAO,OAAO,MAAM,MACjB,MAAM,MAAM,cAAc,CAAC,CAC3B,KACC,OAAO,UACL,UACE,IAAI,gCAAgC,EAClC,SAAS,qBAAqB,MAAM,MAAM,IAAI,MAAM,UACtD,CAAC,CACL,CACF;AACJ,CAAC;AAED,MAAM,qBAAqB,OAAO,GAAG,8BAA8B,CAAC,CAAC,WAAW,OAKJ;CAC1E,MAAM,QAAQ,MAAM,OAAO;CAC3B,IAAI,UAAU,KAAA,GACZ,OAAO,OAAO,IAAI,gCAAgC,EAAE,SAAS,4BAA4B,CAAC;CAG5F,MAAM,eAAe,OAAO,oBAAoB;EAC9C,OAAO,MAAM;EACb,OAAO,MAAM;CACf,CAAC;CAED,MAAM,SACJ,OAAO,MAAM,WAAW,aAAa,MAAM,OAAO,EAAE,OAAO,MAAM,MAAM,CAAC,IAAI,MAAM;CACpF,MAAM,WAAW,OAAO,mBAAmB;EACzC,gBAAgB,MAAM;EACtB,OAAO;EACP,OAAO,OAAO;CAChB,CAAC;CACD,MAAM,eAAe,OAAO,mBAAmB;EAC7C,gBAAgB,MAAM;EACtB,OAAO;EACP,OAAO,OAAO;CAChB,CAAC;CACD,IAAI,aAAa,KAAA,GACf,OAAO,OAAO,IAAI,gCAAgC,EAAE,SAAS,0BAA0B,CAAC;CAG1F,MAAM,mBAAmB,gBAAgB,MAAM,kBAAkB,MAAM,KAAK;CAC5E,MAAM,WAAW,gBAAgB,MAAM,UAAU,MAAM,KAAK;CAC5D,MAAM,eAAe,gBAAgB,MAAM,cAAc,MAAM,KAAK;CAWpE,OAAO;EACL,GATA,qBAAqB,KAAA,KAAa,aAAa,KAAA,IAC3C;GAAE;GAAkB;EAAS,IAC7B,iBAAiB,KAAA,IACf,OAAO,IAAI,gCAAgC,EACzC,SAAS,2DACX,CAAC,IACD,OAAO,eAAe,cAAc,MAAM,MAAM;EAItD,qBAAqB,cAAc,MAAM,qBAAqB,MAAM,KAAK;EACzE,sBAAsB,OAAO,kBAAkB;EAC/C;EACA;EACA,MAAM,MAAM,QAAQ;EACpB,QAAQ,MAAM;EACd;EACA,aAAa,cAAc,MAAM,aAAa,MAAM,KAAK;EACzD,sBAAsB,MAAM,wBAAwB;CACtD;AACF,CAAC;AAED,MAAM,yBAAyB,UAKjB;CACZ,MAAM,MAAM,IAAI,IAAI,MAAM,OAAO,gBAAgB;CACjD,IAAI,aAAa,IAAI,iBAAiB,MAAM;CAC5C,IAAI,aAAa,IAAI,aAAa,MAAM,OAAO,QAAQ;CACvD,IAAI,aAAa,IAAI,gBAAgB,MAAM,WAAW;CACtD,IAAI,aAAa,IAAI,SAAS,MAAM,KAAK;CACzC,IAAI,MAAM,OAAO,OAAO,SAAS,GAC/B,IAAI,aAAa,IAAI,SAAS,MAAM,OAAO,OAAO,KAAK,GAAG,CAAC;CAE7D,IAAI,MAAM,kBAAkB,KAAA,GAAW;EACrC,IAAI,aAAa,IAAI,kBAAkB,MAAM,aAAa;EAC1D,IAAI,aAAa,IAAI,yBAAyB,MAAM;CACtD;CACA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,OAAO,mBAAmB,GACxE,IAAI,aAAa,IAAI,KAAK,KAAK;CAEjC,OAAO,IAAI,SAAS;AACtB;AAEA,MAAM,mBAAmB,WAA2B;CAClD,IAAI,UAAU,KACZ,OAAO;CAET,IAAI,UAAU,KACZ,OAAO;CAET,OAAO;AACT;AAEA,MAAM,0BAA0B,SAAqC;CACnE,IAAI;EACF,OAAO,yBAAyB,IAAI,CAAC,CAAC;CACxC,QAAQ;EACN;CACF;AACF;AAEA,MAAM,6BAA6B,SAAqC;CACtE,IAAI;EACF,OAAO,gCAAgC,IAAI,CAAC,CAAC;CAC/C,QAAQ;EACN;CACF;AACF;AAEA,MAAM,uBACJ,WAC2E;CAC3E,MAAM,UAAkC;EACtC,GAAG,OAAO;EACV,QAAQ;EACR,gBACE,OAAO,yBAAyB,SAC5B,qBACA;CACR;CACA,IAAI,OAAO,yBAAyB,SAAS;EAC3C,IAAI,OAAO,iBAAiB,KAAA,GAC1B,OAAO,OAAO,KACZ,IAAI,gCAAgC,EAClC,SAAS,6CACX,CAAC,CACH;EAEF,QAAQ,mBACN,SAAS,OAAO,OAAO,GAAG,OAAO,SAAS,GAAG,OAAO,cAAc;CACtE;CACA,OAAO,OAAO,QAAQ,OAAO;AAC/B;AAEA,MAAM,mBAAmB,OAAO,GAAG,4BAA4B,CAAC,CAAC,WAAW,OAQ1E;CACA,IAAI,MAAM,OAAO,yBAAyB,QAAQ;EAChD,MAAM,OAAO,IAAI,aAAa,MAAM,OAAO,QAAQ;EACnD,IAAI,MAAM,OAAO,iBAAiB,KAAA,GAChC,MAAM,OAAO,IAAI,iBAAiB,MAAM,OAAO,YAAY;CAE/D,OAAO,IAAI,MAAM,OAAO,yBAAyB,QAC/C,MAAM,OAAO,IAAI,aAAa,MAAM,OAAO,QAAQ;CAGrD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,OAAO,WAAW,GAChE,MAAM,OAAO,IAAI,KAAK,KAAK;CAG7B,MAAM,UAAU,OAAO,oBAAoB,MAAM,MAAM;CACvD,MAAM,UAAU,kBAAkB,KAAK,MAAM,OAAO,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,KACzE,kBAAkB,SAChB,MAAM,OAAO,yBAAyB,SAClC,sBAAsB,OAAO,YAAY,MAAM,MAAM,CAAC,IACtD,MAAM,OAAO,SAAS,GAC1B,QAAQ,eACV,CACF;CACA,MAAM,WAAW,OAAO,KACtB,MAAM,OAAO,QAAQ,OAAO,GAC5B,OAAO,eACL,gBAAgB,aAChB,OAAO,cAAc,MAAM,OAAO,YAAY,IAAI,CAAC,IAAI,EAAE,UAAU,QAAQ,CAC7E,GACA,OAAO,UAAS,UACd,gCAAgC,KAAK;EACnC,OAAO,SAAS,KAAK,KAAK;EAC1B,SAAS,GAAG,MAAM,cAAc;CAClC,CAAC,CACH,CACF;CACA,IAAI,SAAS,SAAS,OAAO,SAAS,UAAU,KAAK;EACnD,MAAM,OAAO,OAAO,KAClB,SAAS,MACT,OAAO,UAAS,UACd,gCAAgC,KAAK;GACnC,OAAO,SAAS,KAAK,KAAK;GAC1B,SAAS,GAAG,MAAM,cAAc;EAClC,CAAC,CACH,CACF;EACA,MAAM,cAAc,gBAAgB,SAAS,MAAM;EACnD,MAAM,gBAAgB,uBAAuB,IAAI;EACjD,MAAM,eAAe,0BAA0B,IAAI;EACnD,MAAM,SAAS,MAAM,KACnB,MAAM,OAAO,CAAC,eAAe,YAAY,GAAG,UAAU,cAAc,GACpE,IACF;EACA,MAAM,UAAU,GAAG,MAAM,cAAc,IAAI,MAAM,KAAK,MAAM,OAAO,CAAC,aAAa,MAAM,GAAG,OAAO,UAAU,GAAG,IAAI,EAAE;EACpH,IAAI,kBAAkB,iBACpB,OAAO,OAAO,mBAAmB,KAAK;GACpC;GACA,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;EACvD,CAAC;EAEH,OAAO,OAAO,gCAAgC,KAAK,EAAE,QAAQ,CAAC;CAChE;CACA,OAAO,OAAO,KACZ,SAAS,MACT,OAAO,UAAS,UACd,gCAAgC,KAAK;EACnC,OAAO,SAAS,KAAK,KAAK;EAC1B,SAAS,GAAG,MAAM,cAAc;CAClC,CAAC,CACH,GACA,OAAO,SAAQ,QACb,KACE,OAAO,oBAAoB,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,GAC5E,OAAO,eACL,gCAAgC,KAAK,EAAE,SAAS,+BAA+B,CAAC,CAClF,CACF,CACF,CACF;AACF,CAAC;AAED,MAAM,mBAAmB,UAAuC;CAC9D,IAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,GACpD,OAAO;CAET,IAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,IAAI;EACpD,MAAM,SAAS,OAAO,KAAK;EAC3B,OAAO,OAAO,SAAS,MAAM,IAAI,SAAS,KAAA;CAC5C;AAEF;AAEA,MAAM,yBAAyB,OAAO,GAAG,kCAAkC,CAAC,CAAC,WAAW,OAIF;CACpF,MAAM,WAAW,OAAO,KACtB,OAAO,oBACL,MAAM,OAAO,QAAQ,iBAAiBA,2BACxC,CAAC,CAAC,MAAM,GAAG,GACX,OAAO,eACL,gCAAgC,KAAK,EAAE,SAAS,+BAA+B,CAAC,CAClF,CACF;CACA,MAAM,cAAc,OAAO,eACzB,SAAS,cACT,2CACF;CACA,IAAI,SAAS,eAAe,KAAK,MAAM,IACrC,OAAO,OAAO,gCAAgC,KAAK,EAAE,SAAS,+BAA+B,CAAC;CAEhG,MAAM,eAAe,SAAS,iBAAiB,MAAM;CACrD,MAAM,YAAY,gBAAgB,SAAS,UAAU;CACrD,IAAI,SAAS,eAAe,KAAA,MAAc,cAAc,KAAA,KAAa,YAAY,IAC/E,OAAO,OAAO,gCAAgC,KAAK,EAAE,SAAS,+BAA+B,CAAC;CAEhG,MAAM,MAAM,OAAO,MAAM;CACzB,MAAM,iBACJ,SAAS,eAAe,KAAA,IAAY,KAAA,IAAY,KAAK,MAAM,SAAS,UAAU;CAChF,IAAI,mBAAmB,KAAA,KAAa,CAAC,OAAO,SAAS,cAAc,GACjE,OAAO,OAAO,gCAAgC,KAAK,EAAE,SAAS,+BAA+B,CAAC;CAGhG,OAAO;EACL;EACA,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;EACrD,GAAI,SAAS,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,SAAS,WAAW;EAC9E,GAAI,cAAc,KAAA,IAAY,CAAC,IAAI;GAAE,WAAW,MAAM,YAAY;GAAM;EAAU;EAClF,GAAI,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,eAAe;EACpE,GAAI,SAAS,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,SAAS,MAAM;EAChE,KAAK,MAAM;CACb;AACF,CAAC;AAqBD,IAAa,UAAb,cAA6B,QAAQ,QAwBnC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC;;AAG/B,MAAa,WACX,UAMI,CAAC,MAEL,MAAM,QACJ,SACA,QAAQ,GAAG;CACT,UAAU,OAAO,GAAG,4BAA4B,CAAC,CAAC,WAEhD,OAKC;EACD,IAAI,MAAM,MAAM,aAAa,MAAM,aAAa,OAC9C,OAAO,OAAO,IAAI,gCAAgC,EAAE,SAAS,uBAAuB,CAAC;EAEvF,IAAI,MAAM,MAAM,aAAa,KAAA,GAC3B,OAAO,OAAO,IAAI,sBAAsB;GACtC,OAAO,MAAM,MAAM;GACnB,kBAAkB,MAAM,MAAM;EAChC,CAAC;EAEH,IAAI,MAAM,MAAM,YAAY,KAAA,KAAa,MAAM,MAAM,OAAO,CAAC,KAAK,MAAM,IACtE,OAAO,OAAO,IAAI,gCAAgC,EAChD,SAAS,mCACX,CAAC;EAGH,MAAM,SAAyC;GAC7C,aAAa,QAAQ,eAAe,eAAe,MAAM,MAAM;GAC/D,WAAW,QAAQ,aAAa;GAChC,KAAK;IAAE,MAAM,MAAM,MAAM;IAAS,WAAW;IAAsB,SAAS;GAAK;GACjF,cAAc,QAAQ,gBAAgB,gBAAgB,MAAM,MAAM;GAClE,WAAW,QAAQ,aAAa;EAClC;EACA,OAAO;GAAE,UAAU;GAAQ;EAAO;CACpC,CAAC;CACD,SAAS,OAAO,GAAG,2BAA2B,CAAC,CAAC,WAE9C,OAIC;EACD,MAAM,SAAyC;GAC7C,aAAa,QAAQ,wBAAwB,kBAAkB,MAAM;GACrE,WAAW,QAAQ,aAAa;GAChC,KAAK;IAAE,WAAW;IAAiB,SAAS;GAAK;GACjD,cAAc,MAAM;GACpB,WAAW,QAAQ,aAAa;EAClC;EACA,OAAO,OAAO;EACd,OAAO;GAAE,UAAU;GAAQ;EAAO;CACpC,CAAC;CACD,OAAO,OAAO,GAAG,yBAAyB,CAAC,CAAC,WAA0C,OAKnF;EACD,MAAM,QAAQ,MAAM,OAAO;EAC3B,IAAI,UAAU,KAAA,GACZ,OAAO,OAAO,IAAI,gCAAgC,EAChD,SAAS,4BACX,CAAC;EAEH,MAAM,SACJ,OAAO,MAAM,WAAW,aAAa,MAAM,OAAO,EAAE,OAAO,MAAM,MAAM,CAAC,IAAI,MAAM;EACpF,MAAM,QAAQ,cAAc,OAAO,OAAO;EAC1C,MAAM,eAAe,MAAM,SAAS,QAAQ,OAAO,aAAa,OAAO,OAAO;EAC9E,MAAM,mBAAmB,IAAI,IAC3B,gBAAgB,MAAM,kBAAkB,MAAM,KAAK,KACjD,uCACJ;EACA,iBAAiB,aAAa,IAAI,iBAAiB,MAAM;EACzD,iBAAiB,aAAa,IAAI,aAAa,WAAW,OAAO,OAAO,QAAQ,CAAC;EACjF,iBAAiB,aAAa,IAAI,gBAAgB,MAAM,WAAW;EACnE,iBAAiB,aAAa,IAAI,SAAS,KAAK;EAChD,IAAI,MAAM,OAAO,SAAS,GACxB,iBAAiB,aAAa,IAAI,SAAS,MAAM,OAAO,KAAK,GAAG,CAAC;EAEnE,IAAI,iBAAiB,MAAM;GACzB,iBAAiB,aAAa,IAAI,kBAAkB,wBAAwB;GAC5E,iBAAiB,aAAa,IAAI,yBAAyB,MAAM;EACnE;EACA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAChC,cAAc,MAAM,qBAAqB,MAAM,KAAK,CACtD,GACE,iBAAiB,aAAa,IAAI,KAAK,KAAK;EAG9C,OAAO;GACL,kBAAkB,iBAAiB,SAAS;GAC5C,cAAc;IACZ,WAAW,IAAI,MACZ,OAAO,MAAM,sBAAsB,MAAM,SAAS,iBACrD;IACA;IACA,aAAa,MAAM;IACnB;GACF;EACF;CACF,CAAC;AACH,CAAC,CACH;;AAGF,MAAa,QAAQ,MAAM,OACzB,SACA,OAAO,IAAI,aAAa;CACtB,MAAM,iBAAiB,OAAO,eAAe;CAC7C,MAAM,SAAS,OAAO,WAAW;CAEjC,MAAM,QAAQ,OAAO,GAAG,iBAAiB,CAAC,CAAC,WAA0C,OAKlF;EACD,MAAM,SAAS,OAAO,mBAAmB;GACvC;GACA;GACA,QAAQ,MAAM;GACd,OAAO,MAAM;EACf,CAAC;EACD,MAAM,YAAY,IAAI,MACnB,OAAO,MAAM,sBAAsB,MAAM,SAAS,iBACrD;EACA,MAAM,QAAQ,gBAAgB,EAAE;EAChC,MAAM,eAAe,OAAO,OAAO,gBAAgB,EAAE,IAAI,KAAA;EACzD,MAAM,gBACJ,iBAAiB,KAAA,IAAY,KAAA,IAAY,OAAO,gBAAgB,YAAY;EAE9E,OAAO;GACL,kBAAkB,sBAAsB;IACtC;IACA;IACA,aAAa,MAAM;IACnB;GACF,CAAC;GACD,cAAc;IACZ;IACA,cAAc,gBAAgB;IAC9B,aAAa,MAAM;IACnB;GACF;EACF;CACF,CAAC;CAED,MAAM,WAAW,OAAO,GAAG,oBAAoB,CAAC,CAAC,WAE/C,OAKC;EACD,IAAI,MAAM,MAAM,aAAa,MAAM,aAAa,OAC9C,OAAO,OAAO,IAAI,gCAAgC,EAAE,SAAS,uBAAuB,CAAC;EAEvF,IAAI,MAAM,MAAM,aAAa,KAAA,GAC3B,OAAO,OAAO,IAAI,sBAAsB;GACtC,OAAO,MAAM,MAAM;GACnB,kBAAkB,MAAM,MAAM;EAChC,CAAC;EAEH,MAAM,OAAO,MAAM,MAAM;EACzB,IAAI,SAAS,KAAA,KAAa,KAAK,KAAK,MAAM,IACxC,OAAO,OAAO,IAAI,gCAAgC,EAChD,SAAS,mCACX,CAAC;EAGH,MAAM,SAAS,OAAO,mBAAmB;GACvC;GACA;GACA,QAAQ,MAAM;GACd,OAAO,MAAM;EACf,CAAC;EACD,IACE,OAAO,mBAAmB,KAAA,KAC1B,MAAM,MAAM,WAAW,KAAA,KACvB,MAAM,MAAM,WAAW,OAAO,gBAE9B,OAAO,OAAO,IAAI,gCAAgC,EAAE,SAAS,wBAAwB,CAAC;EAGxF,MAAM,cAAc,IAAI,gBAAgB;GACtC;GACA,YAAY;GACZ,cAAc,MAAM,aAAa;EACnC,CAAC;EACD,IAAI,MAAM,aAAa,iBAAiB,MACtC,YAAY,IAAI,iBAAiB,MAAM,aAAa,YAAY;EAGlE,MAAM,YAAY,OAAO,KACvB,iBAAiB;GACf;GACA;GACA,eAAe;GACf,QAAQ;EACV,CAAC,GACD,OAAO,SAAS,uBAAsB,UACpC,OAAO,KAAK,gCAAgC,KAAK,EAAE,SAAS,MAAM,QAAQ,CAAC,CAAC,CAC9E,CACF;EACA,MAAM,SAAS,OAAO,uBAAuB;GAAE,QAAQ,MAAM;GAAQ,KAAK;EAAU,CAAC;EACrF,OAAO;GAAE,UAAU;GAAQ;EAAO;CACpC,CAAC;CAED,MAAM,UAAU,OAAO,GAAG,mBAAmB,CAAC,CAAC,WAE7C,OAIC;EACD,MAAM,SAAS,OAAO,mBAAmB;GACvC;GACA;GACA,QAAQ,MAAM;GACd,OAAO,MAAM;EACf,CAAC;EACD,MAAM,YAAY,OAAO,iBAAiB;GACxC;GACA;GACA,eAAe;GACf,QAAQ,IAAI,gBAAgB;IAC1B,YAAY;IACZ,eAAe,MAAM;GACvB,CAAC;EACH,CAAC;EACD,MAAM,SAAS,OAAO,uBAAuB;GAC3C,QAAQ,MAAM;GACd,sBAAsB,MAAM;GAC5B,KAAK;EACP,CAAC;EACD,OAAO;GAAE,UAAU;GAAQ;EAAO;CACpC,CAAC;CAED,OAAO,QAAQ,GAAG;EAAE;EAAU;EAAS;CAAM,CAAC;AAChD,CAAC,CACH,CAAC,CAAC,KAAK,MAAM,QAAQ,gBAAgB,KAAK,CAAC;AAE3C,MAAa,4BAA4B,MAAM,OAC7CC,WACA,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO;CACpB,OAAA,UAAoC,GAAG,EACrC,UAAS,UACP,KACG,QAAQ;EAAE,QAAQ,MAAM;EAAQ,cAAc,MAAM;EAAc,OAAO,MAAM;CAAM,CAAC,CAAC,CACvF,KAAK,OAAO,KAAI,cAAa,UAAU,MAAM,CAAC,EACrD,CAAC;AACH,CAAC,CACH"}
|