domainkit 0.21.0 → 0.22.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/README.md +13 -1
- package/dist/CallbackRecovery.mjs +48 -0
- package/dist/CallbackRecovery.mjs.map +1 -0
- package/dist/Connect.mjs +11 -3
- package/dist/Connect.mjs.map +1 -1
- package/dist/Server.mjs +22 -4
- package/dist/Server.mjs.map +1 -1
- package/dist/entry/client.mjs +3 -1
- package/dist/index.mjs +3 -2
- package/dist/index.mjs.map +1 -1
- package/dist/package.mjs +1 -1
- package/dist/types/CallbackRecovery.d.ts +12 -0
- package/dist/types/Connect.d.ts +16 -0
- package/dist/types/Server.d.ts +4 -1
- package/dist/types/entry/client.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -19,6 +19,8 @@ Node.js 24.10 or newer and Effect 4 are required.
|
|
|
19
19
|
|
|
20
20
|
## Plan, approve, apply
|
|
21
21
|
|
|
22
|
+

|
|
23
|
+
|
|
22
24
|
```ts
|
|
23
25
|
import { Effect, Match } from "effect";
|
|
24
26
|
import { DnsRecord, DomainKit, Principal, Provision, Verify } from "domainkit";
|
|
@@ -181,6 +183,15 @@ the group.
|
|
|
181
183
|
`/callback/:provider` is the one route the provider drives the browser to, so `Identity` has to
|
|
182
184
|
recognise a credential the browser sends on a top-level navigation.
|
|
183
185
|
|
|
186
|
+
A live, authenticated cancellation or completion failure redirects to the saved `returnTo` (or
|
|
187
|
+
`defaultReturnTo`) with `domainkit=cancelled|restart|inspect` and `domainkit-provider=<id>`.
|
|
188
|
+
`Connected` redirects after clearing all recovery parameters. `CallbackRecovery` from `domainkit/client`
|
|
189
|
+
reads or clears this display hint, including optional `domainkit-connection=<connectionId>` for
|
|
190
|
+
a reconnect; `Connect.useRecovery()` from `@domainkit/react` reads and
|
|
191
|
+
dismisses it in the browser. A `callback` policy can override the destination with a same-origin
|
|
192
|
+
string or return `undefined` to keep the default. Unknown, expired, spent, or mismatched flows
|
|
193
|
+
remain errors. See [callback destinations](https://domain-kit.dev/docs/reference/server#callback-destinations).
|
|
194
|
+
|
|
184
195
|
`authorize` is optional and runs after `principal` on every request with the route's name, one of
|
|
185
196
|
`Server.EndpointName`. Fail it with reason `Forbidden` for the 403 a UI expects. Omit it and every
|
|
186
197
|
authenticated principal reaches every route, which is right when your own middleware already gates
|
|
@@ -203,7 +214,8 @@ must be HTTP(S) URLs without credentials, query, or fragment; emulator paths rem
|
|
|
203
214
|
`Server.Options.callback` optionally selects a same-origin destination for sanitized `Connected`,
|
|
204
215
|
`Cancelled`, or `Failed` outcomes. Return `Effect.succeed(undefined)` to retain the default response,
|
|
205
216
|
or an Effect of a destination string. The policy receives provider, validated `returnTo`, and
|
|
206
|
-
`connectionId` on success
|
|
217
|
+
`connectionId` on success, optional `connectionId` on cancelled or failed reconnects, and
|
|
218
|
+
`recovery: "restart" | "inspect"` on failure; it receives no request,
|
|
207
219
|
state, code, provider text, secret, or full storage row. Hosts close over acquired services.
|
|
208
220
|
|
|
209
221
|
Only live flows matching the authenticated owner, actor, and provider are classified inside the
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
//#region src/CallbackRecovery.ts
|
|
4
|
+
/** Browser-safe recovery hints for returning from an authenticated provider callback. */
|
|
5
|
+
var CallbackRecovery_exports = /* @__PURE__ */ __exportAll({
|
|
6
|
+
Model: () => Model,
|
|
7
|
+
read: () => read,
|
|
8
|
+
write: () => write
|
|
9
|
+
});
|
|
10
|
+
const Model = Schema.Struct({
|
|
11
|
+
kind: Schema.Literals([
|
|
12
|
+
"cancelled",
|
|
13
|
+
"restart",
|
|
14
|
+
"inspect"
|
|
15
|
+
]),
|
|
16
|
+
provider: Schema.String.check(Schema.isMinLength(1)),
|
|
17
|
+
connectionId: Schema.optionalKey(Schema.String.check(Schema.isMinLength(1)))
|
|
18
|
+
});
|
|
19
|
+
/** Reads a complete recovery hint; ignores missing, duplicate, or invalid metadata. */
|
|
20
|
+
const read = (url) => {
|
|
21
|
+
const kinds = url.searchParams.getAll("domainkit");
|
|
22
|
+
const providers = url.searchParams.getAll("domainkit-provider");
|
|
23
|
+
const connections = url.searchParams.getAll("domainkit-connection");
|
|
24
|
+
if (kinds.length !== 1 || providers.length !== 1 || connections.length > 1) return null;
|
|
25
|
+
const outcome = {
|
|
26
|
+
kind: kinds[0],
|
|
27
|
+
provider: providers[0],
|
|
28
|
+
...connections.length === 0 ? {} : { connectionId: connections[0] }
|
|
29
|
+
};
|
|
30
|
+
return Schema.is(Model)(outcome) ? outcome : null;
|
|
31
|
+
};
|
|
32
|
+
/** Clones the URL and replaces or clears all recovery hints, preserving other parameters and hash. */
|
|
33
|
+
const write = (url, outcome) => {
|
|
34
|
+
const result = new URL(url);
|
|
35
|
+
result.searchParams.delete("domainkit");
|
|
36
|
+
result.searchParams.delete("domainkit-provider");
|
|
37
|
+
result.searchParams.delete("domainkit-connection");
|
|
38
|
+
if (outcome !== null) {
|
|
39
|
+
result.searchParams.set("domainkit", outcome.kind);
|
|
40
|
+
result.searchParams.set("domainkit-provider", outcome.provider);
|
|
41
|
+
if (outcome.connectionId !== void 0) result.searchParams.set("domainkit-connection", outcome.connectionId);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
//#endregion
|
|
46
|
+
export { CallbackRecovery_exports, Model, read, write };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=CallbackRecovery.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CallbackRecovery.mjs","names":[],"sources":["../src/CallbackRecovery.ts"],"sourcesContent":["/** Browser-safe recovery hints for returning from an authenticated provider callback. */\nimport { Schema } from \"effect\";\n\nexport const Model = Schema.Struct({\n kind: Schema.Literals([\"cancelled\", \"restart\", \"inspect\"]),\n provider: Schema.String.check(Schema.isMinLength(1)),\n connectionId: Schema.optionalKey(Schema.String.check(Schema.isMinLength(1))),\n});\nexport type Model = typeof Model.Type;\n\n/** Reads a complete recovery hint; ignores missing, duplicate, or invalid metadata. */\nexport const read = (url: URL): Model | null => {\n const kinds = url.searchParams.getAll(\"domainkit\");\n const providers = url.searchParams.getAll(\"domainkit-provider\");\n const connections = url.searchParams.getAll(\"domainkit-connection\");\n if (kinds.length !== 1 || providers.length !== 1 || connections.length > 1) return null;\n const outcome = {\n kind: kinds[0],\n provider: providers[0],\n ...(connections.length === 0 ? {} : { connectionId: connections[0] }),\n };\n return Schema.is(Model)(outcome) ? outcome : null;\n};\n\n/** Clones the URL and replaces or clears all recovery hints, preserving other parameters and hash. */\nexport const write = (url: URL, outcome: Model | null): URL => {\n const result = new URL(url);\n result.searchParams.delete(\"domainkit\");\n result.searchParams.delete(\"domainkit-provider\");\n result.searchParams.delete(\"domainkit-connection\");\n if (outcome !== null) {\n result.searchParams.set(\"domainkit\", outcome.kind);\n result.searchParams.set(\"domainkit-provider\", outcome.provider);\n if (outcome.connectionId !== undefined)\n result.searchParams.set(\"domainkit-connection\", outcome.connectionId);\n }\n return result;\n};\n"],"mappings":";;;;;;;;;AAGA,MAAa,QAAQ,OAAO,OAAO;CACjC,MAAM,OAAO,SAAS;EAAC;EAAa;EAAW;CAAS,CAAC;CACzD,UAAU,OAAO,OAAO,MAAM,OAAO,YAAY,CAAC,CAAC;CACnD,cAAc,OAAO,YAAY,OAAO,OAAO,MAAM,OAAO,YAAY,CAAC,CAAC,CAAC;AAC7E,CAAC;;AAID,MAAa,QAAQ,QAA2B;CAC9C,MAAM,QAAQ,IAAI,aAAa,OAAO,WAAW;CACjD,MAAM,YAAY,IAAI,aAAa,OAAO,oBAAoB;CAC9D,MAAM,cAAc,IAAI,aAAa,OAAO,sBAAsB;CAClE,IAAI,MAAM,WAAW,KAAK,UAAU,WAAW,KAAK,YAAY,SAAS,GAAG,OAAO;CACnF,MAAM,UAAU;EACd,MAAM,MAAM;EACZ,UAAU,UAAU;EACpB,GAAI,YAAY,WAAW,IAAI,CAAC,IAAI,EAAE,cAAc,YAAY,GAAG;CACrE;CACA,OAAO,OAAO,GAAG,KAAK,CAAC,CAAC,OAAO,IAAI,UAAU;AAC/C;;AAGA,MAAa,SAAS,KAAU,YAA+B;CAC7D,MAAM,SAAS,IAAI,IAAI,GAAG;CAC1B,OAAO,aAAa,OAAO,WAAW;CACtC,OAAO,aAAa,OAAO,oBAAoB;CAC/C,OAAO,aAAa,OAAO,sBAAsB;CACjD,IAAI,YAAY,MAAM;EACpB,OAAO,aAAa,IAAI,aAAa,QAAQ,IAAI;EACjD,OAAO,aAAa,IAAI,sBAAsB,QAAQ,QAAQ;EAC9D,IAAI,QAAQ,iBAAiB,KAAA,GAC3B,OAAO,aAAa,IAAI,wBAAwB,QAAQ,YAAY;CACxE;CACA,OAAO;AACT"}
|
package/dist/Connect.mjs
CHANGED
|
@@ -477,21 +477,28 @@ const make = Effect.gen(function* () {
|
|
|
477
477
|
field: "callbackUrl"
|
|
478
478
|
}) })
|
|
479
479
|
});
|
|
480
|
+
const decoded = yield* decode$1(Payload, continuation.payload, "continuation").pipe(Effect.result);
|
|
481
|
+
const reconnection = decoded._tag === "Success" && decoded.success.connectionId !== null && decoded.success.connectionId.length > 0 ? { connectionId: decoded.success.connectionId } : {};
|
|
480
482
|
const params = Object.fromEntries(callback.searchParams);
|
|
481
483
|
const failed = (message) => Completion.Failed({
|
|
482
484
|
error: new DomainKitError({ reason: new Unauthenticated({ message }) }),
|
|
483
|
-
recovery: "restart"
|
|
485
|
+
recovery: "restart",
|
|
486
|
+
...reconnection
|
|
484
487
|
});
|
|
485
488
|
if (params.state !== input.continuationId) return failed("Callback state does not match the continuation");
|
|
486
489
|
if (params.error !== void 0) {
|
|
487
490
|
const outcome = failed(`Provider returned ${params.error}`);
|
|
488
|
-
return params.error === "access_denied" ? Completion.Cancelled({
|
|
491
|
+
return params.error === "access_denied" ? Completion.Cancelled({
|
|
492
|
+
error: outcome.error,
|
|
493
|
+
...reconnection
|
|
494
|
+
}) : outcome;
|
|
489
495
|
}
|
|
490
496
|
const code = params.code;
|
|
491
497
|
if (code === void 0) return failed("Callback has no code");
|
|
492
498
|
let persisting = false;
|
|
493
499
|
return yield* Effect.gen(function* () {
|
|
494
|
-
|
|
500
|
+
if (decoded._tag === "Failure") return yield* Effect.fail(decoded.failure);
|
|
501
|
+
const payload = decoded.success;
|
|
495
502
|
const definition = yield* providers.get(continuation.provider);
|
|
496
503
|
const issued = payload.method === "oauth" ? yield* Effect.gen(function* () {
|
|
497
504
|
const oauth = definition.auth.oauth;
|
|
@@ -544,6 +551,7 @@ const make = Effect.gen(function* () {
|
|
|
544
551
|
return started(connection, attached);
|
|
545
552
|
}).pipe(Effect.map((result) => Completion.Connected({ started: result })), Effect.catch((error) => Effect.succeed(Completion.Failed({
|
|
546
553
|
error,
|
|
554
|
+
...reconnection,
|
|
547
555
|
recovery: !persisting && error.reason._tag === "Unauthenticated" ? "restart" : "inspect"
|
|
548
556
|
}))));
|
|
549
557
|
}));
|
package/dist/Connect.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Connect.mjs","names":["Errors.fail","Reason.InvalidInput","Storage.Service","Custody.Service","Providers.Service","Resolver.Service","Storage.Credential","Provider.decodeContext","Reason.Reconnect","Principal.Service","Reason.Forbidden","Storage.Authorization","Provider.encodeContext","DomainName.decode","Reason.NotFound","Storage.Attachment","candidates","DomainName.candidates","Provider.describeMethods","pkce","OAuth.pkce","Storage.Continuation","Callback.refuse","Errors.DomainKitError","Reason.Unauthenticated","Errors.decode"],"sources":["../src/Connect.ts"],"sourcesContent":["/**\n * Connect a provider, attach domains to it, and keep the credential alive. Interactive methods\n * (OAuth, integration) return a redirect and complete on callback; token methods connect in one\n * call. Reuse of an existing connection for a new domain is `attach`.\n */\nimport { Context, Data, DateTime, Duration, Effect, Layer, Option, Redacted, Schema } from \"effect\";\n\nimport * as Custody from \"./Custody.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as DomainName from \"./DomainName.ts\";\nimport { fresh } from \"./internal/ids.ts\";\nimport * as OAuth from \"./internal/oauth.ts\";\nimport * as Callback from \"./internal/callback.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Provider from \"./Provider.ts\";\nimport * as Providers from \"./Providers.ts\";\nimport * as Resolver from \"./Resolver.ts\";\nimport * as Storage from \"./Storage.ts\";\n\ntype Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;\n\nexport type Method = Data.TaggedEnum<{\n /** Values keyed by the provider's declared `auth.token.fields`. */\n Token: { readonly values: Provider.TokenValues };\n OAuth: { readonly returnTo?: string };\n Integration: { readonly returnTo?: string };\n}>;\nconst MethodEnum = Data.taggedEnum<Method>();\n/** `token` accepts a lone string (`{ token }`) or a record; every value is wrapped in `Redacted`. */\nexport const Method = {\n ...MethodEnum,\n token: (\n values: string | Readonly<Record<string, Redacted.Redacted<string> | string | undefined>>,\n ): Method =>\n MethodEnum.Token({\n values:\n typeof values === \"string\"\n ? { token: Redacted.make(values) }\n : Object.fromEntries(\n Object.entries(values).map(([name, value]) => [\n name,\n typeof value === \"string\" ? Redacted.make(value) : value,\n ]),\n ),\n }),\n oauth: (options: { readonly returnTo?: string } = {}): Method => MethodEnum.OAuth(options),\n integration: (options: { readonly returnTo?: string } = {}): Method =>\n MethodEnum.Integration(options),\n} as const;\n\n/** Everything the UI needs about a domain's provider state. */\nexport interface Snapshot {\n readonly domain: string;\n readonly attachment: Storage.Attachment | null;\n readonly connection: Storage.Connection | null;\n readonly authorization: Pick<\n Storage.Authorization,\n \"id\" | \"provider\" | \"method\" | \"capabilities\" | \"revocation\" | \"label\"\n > | null;\n readonly lastReceiptId: string | null;\n /** How many domains this domain's connection serves, including this one; `0` without one. */\n readonly connectionDomains: number;\n /** Other connections of this owner that could serve this domain. */\n readonly reusable: ReadonlyArray<{\n readonly connection: Storage.Connection;\n readonly provider: string;\n readonly method: Storage.AuthMethod;\n }>;\n readonly providers: ReadonlyArray<{\n readonly id: string;\n readonly name: string;\n readonly methods: ReadonlyArray<Provider.MethodDescriptor>;\n }>;\n}\n\nexport type Started = Data.TaggedEnum<{\n Connected: {\n readonly connection: Storage.Connection;\n readonly attachment: Storage.Attachment | null;\n };\n Redirect: { readonly authorizationUrl: string; readonly continuationId: string };\n SelectionRequired: {\n readonly connection: Storage.Connection;\n readonly candidates: ReadonlyArray<Provider.Target>;\n };\n}>;\nexport const Started = Data.taggedEnum<Started>();\n\n/** A verified completion result. Errors stay on the server; do not serialize them to a browser. */\nexport type Completion = Data.TaggedEnum<{\n Connected: { readonly started: Exclude<Started, { readonly _tag: \"Redirect\" }> };\n Cancelled: { readonly error: Errors.DomainKitError };\n Failed: { readonly error: Errors.DomainKitError; readonly recovery: \"restart\" | \"inspect\" };\n}>;\nexport const Completion = Data.taggedEnum<Completion>();\n\n/** `attach` either attached the domain or needs the caller to pick a zone. */\nexport type Selection = Data.TaggedEnum<{\n SelectionRequired: { readonly candidates: ReadonlyArray<Provider.Target> };\n}>;\nexport const Selection = Data.taggedEnum<Selection>();\n\nexport type Attached = Storage.Attachment | Selection;\n\nexport type Refreshed = \"current\" | \"refreshed\" | \"reconnect\";\n\n/** Which of the principal's connections serves a domain, from nameserver evidence and zone lists. */\nexport type Discovery = Data.TaggedEnum<{\n Resolved: { readonly connectionId: string; readonly target: Provider.Target };\n SelectionRequired: {\n readonly candidates: ReadonlyArray<{\n readonly connectionId: string;\n readonly target: Provider.Target;\n }>;\n };\n /**\n * No connection reaches the domain. `host` names the one registered provider whose declared\n * nameserver suffixes cover every authoritative nameserver; `null` when none or several do.\n */\n NotFound: {\n readonly nameservers: ReadonlyArray<string>;\n readonly host: { readonly provider: string } | null;\n };\n}>;\nexport const Discovery = Data.taggedEnum<Discovery>();\n\n/** One zone a connection reaches, with the connection that reaches it. */\nexport interface Zone {\n readonly connectionId: string;\n readonly provider: string;\n readonly target: Provider.Target;\n}\n\n/**\n * A connection's standing in a zone listing. `reconnect` is a credential the provider turned down:\n * the connection is still the owner's, and the other connections' zones came back regardless.\n */\nexport interface ZoneConnection {\n readonly connectionId: string;\n readonly provider: string;\n /** What the provider called the account at connect time; `null` when it named none. */\n readonly label: string | null;\n readonly status: \"connected\" | \"reconnect\";\n}\n\n/** Every zone the principal's connections reach, and how each connection is standing. */\nexport interface Zones {\n /** Ordered by zone, then by connection, so two listings of the same state read the same. */\n readonly zones: ReadonlyArray<Zone>;\n readonly connections: ReadonlyArray<ZoneConnection>;\n /** Every provider a customer could connect from here, so an offer needs no second call. */\n readonly providers: Snapshot[\"providers\"];\n}\n\nexport interface Interface {\n readonly inspect: (domain: string) => Fx<Snapshot>;\n /**\n * Every zone the principal's connections can serve, from `Provider.Session.listTargets`. A\n * connection whose credential the provider turned down is marked `reconnect` in `connections`\n * and contributes no zones; the rest still list.\n */\n readonly zones: (options?: { readonly provider?: string }) => Fx<Zones>;\n /**\n * Resolve the domain's authoritative nameservers, then match them against the zones each of\n * the principal's connections can reach: the closest zone wins, a decisive nameserver match\n * breaks ties, and anything else needs a selection.\n */\n readonly discover: (domain: string) => Fx<Discovery>;\n /** Connect a provider and, when `domain` is given, attach it. Token methods finish here. */\n readonly start: (input: {\n readonly provider: string;\n readonly method: Method;\n readonly domain?: string;\n readonly callbackUrl?: string;\n }) => Fx<Started>;\n /**\n * Replace the credential behind a connection the owner already holds, keeping the connection and\n * every domain attached to it. This is what a customer means by reconnecting: starting again\n * would mint a second account and leave the domains on the one the provider turned down. The\n * provider is the connection's own; a credential for a different account is the customer's\n * choice, and the zone check on every session catches a zone it can no longer reach.\n */\n readonly reconnect: (input: {\n readonly connectionId: string;\n readonly method: Method;\n readonly callbackUrl?: string;\n }) => Fx<Started>;\n /** Finish an interactive connection from the provider callback (the full callback URL). */\n readonly complete: (input: {\n readonly continuationId: string;\n readonly callbackUrl: string;\n }) => Fx<Started>;\n /**\n * Classify only after live owner/actor verification inside the completion lock. Optional\n * `provider` binds the flow to a mounted route. Invalid flows fail without an outcome.\n * Cancellation and failure leave the continuation retryable; persistence is not atomic.\n */\n readonly completeOutcome: (input: {\n readonly continuationId: string;\n readonly callbackUrl: string;\n readonly provider?: string;\n }) => Fx<Completion>;\n readonly attach: (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly target?: Provider.Target;\n }) => Fx<Attached>;\n /** Records stay in DNS; DomainKit forgets the domain. Use `Cleanup` first to remove records. */\n readonly detach: (attachmentId: string) => Fx<void>;\n /** Detach every domain and revoke the credential at the provider when the method supports it. */\n readonly disconnect: (connectionId: string) => Fx<void>;\n /** Refresh a near-expiry credential, single-flighted. Called automatically before provider use. */\n readonly refresh: (connectionId: string) => Fx<Refreshed>;\n /** Live provider session for an attachment; used by Provision, Cleanup, Verify. */\n readonly session: (\n attachmentId: string,\n ) => Fx<{ readonly session: Provider.Session; readonly target: Provider.Target }>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Connect\") {}\n\n/** Which owner-scoped reuse is allowed. Default: any connection of the same owner may serve any of its domains. */\nexport interface PolicyShape {\n readonly allowReuse: (input: {\n readonly connection: Storage.Connection;\n readonly domain: string;\n }) => Effect.Effect<boolean, never, Principal.Service>;\n /** Refresh this long before expiry. Default 10 minutes. */\n readonly refreshBeforeMs: number;\n /** Continuation lifetime. Default 15 minutes. */\n readonly continuationTtlMs: number;\n}\nexport const defaults: PolicyShape = {\n allowReuse: () => Effect.succeed(true),\n refreshBeforeMs: 10 * 60_000,\n continuationTtlMs: 15 * 60_000,\n};\nexport class Policy extends Context.Reference<PolicyShape>(\"@domainkit/Connect/Policy\", {\n defaultValue: () => defaults,\n}) {}\n\n// ---------------------------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------------------------\n\nconst Payload = Schema.Struct({\n method: Schema.Literals([\"oauth\", \"integration\"]),\n codeVerifier: Schema.NullOr(Schema.String),\n callbackUrl: Schema.String,\n domain: Schema.NullOr(Schema.String),\n /** Set when the flow is re-crediting a connection rather than making one. */\n connectionId: Schema.NullOr(Schema.String),\n});\n\nconst Target = Schema.Struct({\n zone: Schema.String,\n context: Schema.Unknown,\n label: Schema.String,\n});\n\nconst invalid = (message: string, field?: string) =>\n Errors.fail(new Reason.InvalidInput({ message, ...(field === undefined ? {} : { field }) }));\n\n/** Nameserver hostnames compare case-insensitively and without a trailing dot. */\nconst normalize = (name: string) => name.toLowerCase().replace(/\\.$/, \"\");\n\nexport const make: Effect.Effect<\n Interface,\n never,\n Storage.Service | Custody.Service | Providers.Service | Resolver.Service\n> = Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const custody = yield* Custody.Service;\n const providers = yield* Providers.Service;\n const resolver = yield* Resolver.Service;\n\n const seal = (issued: Provider.IssuedCredential) =>\n Effect.gen(function* () {\n const ciphertext = yield* custody.seal(issued.secret);\n return new Storage.Credential({\n ciphertext,\n expiresAt: issued.expiresAt,\n rotatedAt: yield* DateTime.now,\n });\n });\n\n /** Plaintext credential with the context decoded from its stored envelope. */\n const open = (authorization: Storage.Authorization): Fx<Provider.Credential> =>\n Effect.gen(function* () {\n const definition = yield* providers.get(authorization.provider);\n const row = yield* storage.authorizations.credential(authorization.id);\n const secret = yield* custody.open(row.ciphertext);\n const context = yield* Provider.decodeContext(definition, authorization.context);\n return { secret, context };\n });\n\n const refresherFor = (definition: Provider.Definition, method: Storage.AuthMethod) =>\n method === \"oauth\"\n ? definition.auth.oauth?.refresh\n : method === \"integration\"\n ? definition.auth.integration?.refresh\n : undefined;\n\n const revokerFor = (definition: Provider.Definition, method: Storage.AuthMethod) =>\n method === \"oauth\"\n ? definition.auth.oauth?.revoke\n : method === \"integration\"\n ? definition.auth.integration?.revoke\n : undefined;\n\n const revokeAt = (authorization: Storage.Authorization): Fx<void> =>\n Effect.gen(function* () {\n const definition = yield* providers.get(authorization.provider);\n const revoke = revokerFor(definition, authorization.method);\n if (revoke === undefined) return;\n const credential = yield* open(authorization);\n yield* revoke(credential);\n });\n\n /** Finish revocations a crash interrupted; best effort, never blocks the caller. */\n const recover = storage.authorizations.recoverRevocations(revokeAt).pipe(Effect.ignore);\n\n const refresh: Interface[\"refresh\"] = (connectionId) =>\n Effect.gen(function* () {\n const policy = yield* Policy;\n const connection = yield* storage.connections.get(connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const refresher = refresherFor(definition, authorization.method);\n const dueAt = (row: Storage.Credential, now: DateTime.Utc) =>\n row.expiresAt !== null &&\n DateTime.toEpochMillis(row.expiresAt) - DateTime.toEpochMillis(now) <=\n policy.refreshBeforeMs;\n const expired = (row: Storage.Credential, now: DateTime.Utc) =>\n row.expiresAt !== null &&\n DateTime.toEpochMillis(row.expiresAt) <= DateTime.toEpochMillis(now);\n const current = yield* storage.authorizations.credential(authorization.id);\n const now = yield* DateTime.now;\n if (!dueAt(current, now)) return \"current\" as const;\n if (refresher === undefined)\n return expired(current, now) ? (\"reconnect\" as const) : (\"current\" as const);\n return yield* storage\n .withLock(\n `refresh:${authorization.id}`,\n Effect.gen(function* () {\n // Disconnect's two-phase revocation does not take this lock: re-read the row here\n // so a revocation that started meanwhile skips the refresh instead of racing it.\n const live = yield* storage.authorizations.get(authorization.id).pipe(\n Effect.map(Option.some),\n Effect.catchIf(\n (error) => error.reason._tag === \"NotFound\",\n () => Effect.succeed(Option.none<Storage.Authorization>()),\n ),\n );\n if (Option.isNone(live) || live.value.revocation !== \"active\") {\n return \"reconnect\" as const;\n }\n const latest = yield* storage.authorizations.credential(authorization.id);\n if (!dueAt(latest, yield* DateTime.now)) return \"current\" as const;\n const secret = yield* custody.open(latest.ciphertext);\n const context = yield* Provider.decodeContext(definition, live.value.context);\n const issued = yield* refresher({ secret, context });\n // A revocation that completed while the provider was issuing leaves no row to\n // rotate; revoke the fresh credential so it does not stay live without a home.\n yield* storage.authorizations\n .rotate(authorization.id, yield* seal(issued))\n .pipe(\n Effect.tapError((error) =>\n error.reason._tag === \"NotFound\"\n ? (revokerFor(definition, authorization.method)?.(issued) ?? Effect.void).pipe(\n Effect.ignore,\n )\n : Effect.void,\n ),\n );\n return \"refreshed\" as const;\n }),\n )\n .pipe(\n Effect.catchIf(\n (error) => error.reason._tag === \"Busy\" && !expired(current, now),\n () => Effect.succeed(\"current\" as const),\n ),\n Effect.catchIf(\n (error) => error.reason._tag === \"Unauthenticated\",\n () => Effect.succeed(\"reconnect\" as const),\n ),\n );\n });\n\n const sessionFor = (connection: Storage.Connection): Fx<Provider.Session> =>\n Effect.gen(function* () {\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const reconnect = Errors.fail(\n new Reason.Reconnect({\n provider: authorization.provider,\n connectionId: connection.id,\n }),\n );\n if (authorization.revocation !== \"active\") return yield* reconnect;\n if ((yield* refresh(connection.id)) === \"reconnect\") return yield* reconnect;\n const definition = yield* providers.get(authorization.provider);\n return definition.session(yield* open(authorization));\n });\n\n const connectWith = (input: {\n readonly definition: Provider.Definition;\n readonly method: Storage.AuthMethod;\n readonly issued: Provider.IssuedCredential;\n readonly requiredCapabilities: ReadonlyArray<Storage.Capability>;\n }): Fx<{ readonly connection: Storage.Connection; readonly session: Provider.Session }> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const session = input.definition.session(input.issued);\n const held = yield* session.capabilities();\n const missing = input.requiredCapabilities.filter((capability) => !held.includes(capability));\n if (missing.length > 0) {\n return yield* Errors.fail(\n new Reason.Forbidden({\n message: `${input.definition.name} credential lacks ${missing.join(\", \")}`,\n }),\n );\n }\n const authorization = new Storage.Authorization({\n id: yield* fresh(\"auth\"),\n ownerId: principal.ownerId,\n provider: input.definition.id,\n method: input.method,\n capabilities: held,\n context: yield* Provider.encodeContext(input.definition, input.issued.context),\n label: input.issued.label ?? null,\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: yield* DateTime.now,\n });\n yield* storage.authorizations.upsert({\n authorization,\n credential: yield* seal(input.issued),\n });\n const connection = yield* storage.connections.create(authorization.id);\n return { connection, session };\n });\n\n /**\n * Put a fresh credential behind an authorization the owner already has. The row keeps its id, so\n * every connection over it, and every domain on those connections, stays exactly where it is.\n */\n const replaceWith = (input: {\n readonly definition: Provider.Definition;\n readonly authorizationId: string;\n readonly method: Storage.AuthMethod;\n readonly issued: Provider.IssuedCredential;\n readonly requiredCapabilities: ReadonlyArray<Storage.Capability>;\n }): Fx<void> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const session = input.definition.session(input.issued);\n const held = yield* session.capabilities();\n const missing = input.requiredCapabilities.filter((capability) => !held.includes(capability));\n if (missing.length > 0) {\n return yield* Errors.fail(\n new Reason.Forbidden({\n message: `${input.definition.name} credential lacks ${missing.join(\", \")}`,\n }),\n );\n }\n const authorization = new Storage.Authorization({\n id: input.authorizationId,\n ownerId: principal.ownerId,\n provider: input.definition.id,\n method: input.method,\n capabilities: held,\n context: yield* Provider.encodeContext(input.definition, input.issued.context),\n label: input.issued.label ?? null,\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: yield* DateTime.now,\n });\n yield* storage.authorizations.upsert({\n authorization,\n credential: yield* seal(input.issued),\n expectedId: input.authorizationId,\n });\n });\n\n const attachWith = (input: {\n readonly definition: Provider.Definition;\n readonly connection: Storage.Connection;\n readonly session: Provider.Session;\n readonly domain: string;\n readonly target?: Provider.Target;\n }): Fx<Attached> =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input.domain);\n const existing = yield* storage.attachments.byDomain(domain);\n if (Option.isSome(existing)) {\n if (existing.value.connectionId === input.connection.id) return existing.value;\n return yield* invalid(`${domain} is already attached to another connection`, \"domain\");\n }\n const resolution: Provider.Resolution =\n input.target === undefined\n ? yield* input.session.resolveTarget(domain)\n : Provider.Resolution.Resolved({ target: input.target });\n switch (resolution._tag) {\n case \"SelectionRequired\":\n return Selection.SelectionRequired({ candidates: resolution.candidates });\n case \"NotFound\":\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: domain }));\n case \"Resolved\":\n return yield* storage.attachments.create({\n connectionId: input.connection.id,\n domain,\n zone: resolution.target.zone,\n label: resolution.target.label,\n target: {\n ...resolution.target,\n context: yield* Provider.encodeContext(input.definition, resolution.target.context),\n },\n });\n }\n });\n\n const started = (\n connection: Storage.Connection,\n attached: Attached | null,\n ): Exclude<Started, { readonly _tag: \"Redirect\" }> =>\n attached === null\n ? Started.Connected({ connection, attachment: null })\n : attached instanceof Storage.Attachment\n ? Started.Connected({ connection, attachment: attached })\n : Started.SelectionRequired({ connection, candidates: attached.candidates });\n\n /** Authoritative nameservers for the closest zone that answers an NS query. */\n const authoritative = (candidates: ReadonlyArray<DomainName.Model>) =>\n Effect.gen(function* () {\n for (const zone of candidates) {\n const outcomes = yield* resolver.resolve(zone, \"NS\");\n const names = outcomes.flatMap((outcome) =>\n outcome._tag === \"Answered\"\n ? outcome.answer.records.flatMap((record) =>\n record._tag === \"NS\" ? [record.nameserver] : [],\n )\n : [],\n );\n if (names.length > 0) return [...new Set(names)].sort();\n }\n return [] as ReadonlyArray<string>;\n });\n\n const discover: Interface[\"discover\"] = (input) =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input);\n const candidates = DomainName.candidates(domain);\n const nameservers = yield* authoritative(candidates);\n const reachable: Array<{\n readonly connectionId: string;\n readonly target: Provider.Target;\n }> = [];\n for (const connection of yield* storage.connections.list()) {\n const targets = yield* sessionFor(connection).pipe(\n Effect.flatMap((session) => session.listTargets()),\n Effect.catch(() => Effect.succeed([] as ReadonlyArray<Provider.Target>)),\n );\n for (const target of targets) {\n if (candidates.includes(target.zone as DomainName.Model)) {\n reachable.push({ connectionId: connection.id, target });\n }\n }\n }\n for (const zone of candidates) {\n const matches = reachable\n .filter(({ target }) => target.zone === zone)\n .sort((left, right) => left.connectionId.localeCompare(right.connectionId));\n if (matches.length === 0) continue;\n const [only] = matches;\n if (matches.length === 1 && only !== undefined) return Discovery.Resolved(only);\n const decisive = matches.filter(({ target }) => {\n const configured = new Set((target.nameservers ?? []).map(normalize));\n return nameservers.length > 0 && nameservers.every((name) => configured.has(name));\n });\n const [winner] = decisive;\n if (decisive.length === 1 && winner !== undefined) return Discovery.Resolved(winner);\n return Discovery.SelectionRequired({ candidates: matches });\n }\n return Discovery.NotFound({ nameservers, host: hostOf(nameservers) });\n });\n\n /** The one registered provider whose declared suffixes cover every nameserver, else `null`. */\n const hostOf = (nameservers: ReadonlyArray<string>): { readonly provider: string } | null => {\n if (nameservers.length === 0) return null;\n const names = nameservers.map(normalize);\n const hosts = providers.list().filter((definition) => {\n const suffixes = (definition.nameservers ?? []).map(normalize);\n return (\n suffixes.length > 0 &&\n names.every((name) =>\n suffixes.some((suffix) => name === suffix || name.endsWith(`.${suffix}`)),\n )\n );\n });\n const [host] = hosts;\n return hosts.length === 1 && host !== undefined ? { provider: host.id } : null;\n };\n\n const zones: Interface[\"zones\"] = (options = {}) =>\n Effect.gen(function* () {\n const reachable: Array<Zone> = [];\n const connections: Array<ZoneConnection> = [];\n const filter = options.provider === undefined ? undefined : { provider: options.provider };\n for (const connection of yield* storage.connections.list(filter)) {\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n // A credential the provider turned down is one connection's problem, not the listing's:\n // it is reported as needing a reconnect and every other connection still answers. Any\n // other failure is the listing's own, so it is raised rather than dressed up as an\n // authorization the customer has to grant again.\n const targets = yield* sessionFor(connection).pipe(\n Effect.flatMap((session) => session.listTargets()),\n Effect.map(Option.some),\n Effect.catchIf(\n (error) =>\n error.reason._tag === \"Reconnect\" ||\n error.reason._tag === \"Unauthenticated\" ||\n error.reason._tag === \"Forbidden\",\n () => Effect.succeed(Option.none<ReadonlyArray<Provider.Target>>()),\n ),\n );\n connections.push({\n connectionId: connection.id,\n provider: authorization.provider,\n label: authorization.label,\n status: Option.isNone(targets) ? \"reconnect\" : \"connected\",\n });\n if (Option.isNone(targets)) continue;\n for (const target of targets.value) {\n reachable.push({\n connectionId: connection.id,\n provider: authorization.provider,\n target,\n });\n }\n }\n return {\n zones: reachable.sort(\n (left, right) =>\n left.target.zone.localeCompare(right.target.zone) ||\n left.connectionId.localeCompare(right.connectionId),\n ),\n connections,\n providers: providers.list().map((definition) => ({\n id: definition.id,\n name: definition.name,\n methods: Provider.describeMethods(definition),\n })),\n };\n });\n\n const inspect: Interface[\"inspect\"] = (input) =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input);\n const policy = yield* Policy;\n const attachment = Option.getOrNull(yield* storage.attachments.byDomain(domain));\n const connection =\n attachment === null ? null : yield* storage.connections.get(attachment.connectionId);\n const authorization =\n connection === null ? null : yield* storage.authorizations.get(connection.authorizationId);\n const latest =\n attachment === null\n ? Option.none()\n : yield* storage.attempts.latest(attachment.id, \"provisioning\");\n // Letting a connection go takes every domain on it, so the UI has to be able to say how many.\n const attached = connection === null ? [] : yield* storage.attachments.list(connection.id);\n const reusable: Array<Snapshot[\"reusable\"][number]> = [];\n for (const candidate of yield* storage.connections.list()) {\n if (candidate.id === connection?.id) continue;\n if (!(yield* policy.allowReuse({ connection: candidate, domain }))) continue;\n const candidateAuthorization = yield* storage.authorizations.get(candidate.authorizationId);\n if (candidateAuthorization.revocation !== \"active\") continue;\n reusable.push({\n connection: candidate,\n provider: candidateAuthorization.provider,\n method: candidateAuthorization.method,\n });\n }\n return {\n domain,\n attachment,\n connection,\n authorization:\n authorization === null\n ? null\n : {\n id: authorization.id,\n provider: authorization.provider,\n method: authorization.method,\n capabilities: authorization.capabilities,\n revocation: authorization.revocation,\n label: authorization.label,\n },\n lastReceiptId: Option.isSome(latest) ? (latest.value.receipt?.id ?? null) : null,\n connectionDomains: attached.length,\n reusable,\n providers: providers.list().map((definition) => ({\n id: definition.id,\n name: definition.name,\n methods: Provider.describeMethods(definition),\n })),\n };\n });\n\n const start: Interface[\"start\"] = (input) =>\n Effect.gen(function* () {\n yield* recover;\n const definition = yield* providers.get(input.provider);\n if (input.domain !== undefined) yield* DomainName.decode(input.domain);\n switch (input.method._tag) {\n case \"Token\": {\n const token = definition.auth.token;\n if (token === undefined) {\n return yield* invalid(`${definition.name} does not accept tokens`, \"method\");\n }\n const issued = yield* token.authenticate(input.method.values);\n const { connection, session } = yield* connectWith({\n definition,\n method: \"token\",\n issued,\n requiredCapabilities: token.requiredCapabilities,\n });\n const attached =\n input.domain === undefined\n ? null\n : yield* attachWith({ definition, connection, session, domain: input.domain });\n return started(connection, attached);\n }\n case \"OAuth\":\n case \"Integration\":\n return yield* redirectFor({\n definition,\n method: input.method,\n callbackUrl: input.callbackUrl,\n domain: input.domain ?? null,\n connectionId: null,\n });\n }\n });\n\n /** The redirect an interactive method answers with, and the continuation that finishes it. */\n const redirectFor = (input: {\n readonly definition: Provider.Definition;\n readonly method: Extract<Method, { readonly _tag: \"OAuth\" | \"Integration\" }>;\n readonly callbackUrl: string | undefined;\n readonly domain: string | null;\n readonly connectionId: string | null;\n }): Fx<Started> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const policy = yield* Policy;\n const interactive =\n input.method._tag === \"OAuth\"\n ? input.definition.auth.oauth\n : input.definition.auth.integration;\n if (interactive === undefined) {\n return yield* invalid(\n `${input.definition.name} does not offer ${input.method._tag.toLowerCase()} connections`,\n \"method\",\n );\n }\n if (input.callbackUrl === undefined) {\n return yield* invalid(\"Interactive connections need a callbackUrl\", \"callbackUrl\");\n }\n const callbackUrl = input.callbackUrl;\n const continuationId = yield* fresh(\"cont\");\n const pkce = input.method._tag === \"OAuth\" ? yield* OAuth.pkce() : null;\n const redirect =\n input.method._tag === \"OAuth\"\n ? yield* (interactive as Provider.OAuthAuth).start({\n state: continuationId,\n callbackUrl,\n codeChallenge: pkce?.codeChallenge ?? \"\",\n })\n : yield* (interactive as Provider.IntegrationAuth).start({\n state: continuationId,\n callbackUrl,\n });\n yield* storage.continuations.put(\n new Storage.Continuation({\n id: continuationId,\n ownerId: principal.ownerId,\n actorId: principal.actorId,\n provider: input.definition.id,\n payload: Schema.encodeSync(Payload)({\n method: input.method._tag === \"OAuth\" ? \"oauth\" : \"integration\",\n codeVerifier: pkce?.codeVerifier ?? null,\n callbackUrl,\n domain: input.domain,\n connectionId: input.connectionId,\n }),\n returnTo: input.method.returnTo ?? null,\n expiresAt: DateTime.addDuration(\n yield* DateTime.now,\n Duration.millis(policy.continuationTtlMs),\n ),\n }),\n );\n return Started.Redirect({ authorizationUrl: redirect.authorizationUrl, continuationId });\n });\n\n const reconnect: Interface[\"reconnect\"] = (input) =>\n Effect.gen(function* () {\n yield* recover;\n const connection = yield* storage.connections.get(input.connectionId);\n const existing = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(existing.provider);\n switch (input.method._tag) {\n case \"Token\": {\n const token = definition.auth.token;\n if (token === undefined) {\n return yield* invalid(`${definition.name} does not accept tokens`, \"method\");\n }\n const issued = yield* token.authenticate(input.method.values);\n yield* replaceWith({\n definition,\n authorizationId: existing.id,\n method: \"token\",\n issued,\n requiredCapabilities: token.requiredCapabilities,\n });\n return Started.Connected({ connection, attachment: null });\n }\n case \"OAuth\":\n case \"Integration\":\n return yield* redirectFor({\n definition,\n method: input.method,\n callbackUrl: input.callbackUrl,\n domain: null,\n connectionId: connection.id,\n });\n }\n });\n\n const completeOutcome: Interface[\"completeOutcome\"] = (input) =>\n storage.withLock(\n `continuation:${input.continuationId}`,\n Effect.gen(function* () {\n const continuation = yield* storage.continuations.get(input.continuationId);\n const principal = yield* Principal.Service;\n if (\n continuation.ownerId !== principal.ownerId ||\n continuation.actorId !== principal.actorId ||\n (input.provider !== undefined && continuation.provider !== input.provider)\n ) {\n return yield* Callback.refuse;\n }\n const callback = yield* Effect.try({\n try: () => new URL(input.callbackUrl),\n catch: () =>\n new Errors.DomainKitError({\n reason: new Reason.InvalidInput({\n message: \"callbackUrl is not a URL\",\n field: \"callbackUrl\",\n }),\n }),\n });\n const params = Object.fromEntries(callback.searchParams);\n const failed = (message: string) =>\n Completion.Failed({\n error: new Errors.DomainKitError({ reason: new Reason.Unauthenticated({ message }) }),\n recovery: \"restart\",\n });\n // State verification precedes denial classification, including on the direct Effect API.\n if (params.state !== input.continuationId)\n return failed(\"Callback state does not match the continuation\");\n if (params.error !== undefined) {\n const outcome = failed(`Provider returned ${params.error}`);\n // OAuth's standardized consent denial is the only cancellation code we recognize.\n return params.error === \"access_denied\"\n ? Completion.Cancelled({ error: outcome.error })\n : outcome;\n }\n const code = params.code;\n if (code === undefined) return failed(\"Callback has no code\");\n // Once persistence starts, even a failure may leave a credential or connection behind.\n let persisting = false;\n return yield* Effect.gen(function* () {\n const payload = yield* Errors.decode(Payload, continuation.payload, \"continuation\");\n const definition = yield* providers.get(continuation.provider);\n const issued =\n payload.method === \"oauth\"\n ? yield* Effect.gen(function* () {\n const oauth = definition.auth.oauth;\n if (oauth === undefined || payload.codeVerifier === null) {\n return yield* invalid(\n `${definition.name} does not offer oauth connections`,\n \"method\",\n );\n }\n return yield* oauth.complete({\n code,\n callbackUrl: payload.callbackUrl,\n codeVerifier: payload.codeVerifier,\n params,\n });\n })\n : yield* Effect.gen(function* () {\n const integration = definition.auth.integration;\n if (integration === undefined) {\n return yield* invalid(\n `${definition.name} does not offer integration connections`,\n \"method\",\n );\n }\n return yield* integration.complete({\n code,\n callbackUrl: payload.callbackUrl,\n params,\n });\n });\n persisting = true;\n if (payload.connectionId !== null) {\n const held = yield* storage.connections.get(payload.connectionId);\n const existing = yield* storage.authorizations.get(held.authorizationId);\n yield* replaceWith({\n definition,\n authorizationId: existing.id,\n method: payload.method,\n issued,\n requiredCapabilities: [],\n });\n yield* storage.continuations.consume(input.continuationId).pipe(Effect.ignore);\n return Started.Connected({ connection: held, attachment: null });\n }\n const { connection, session } = yield* connectWith({\n definition,\n method: payload.method,\n issued,\n requiredCapabilities: [],\n });\n const attached =\n payload.domain === null\n ? null\n : yield* attachWith({ definition, connection, session, domain: payload.domain });\n // The connection is durable at this point. Whatever happens to the continuation\n // now (expired, already spent, or a storage failure while spending it) no longer\n // changes the outcome: the row expires on its own and a replayed callback fails at\n // the provider, so the persisted result is returned.\n yield* storage.continuations.consume(input.continuationId).pipe(Effect.ignore);\n return started(connection, attached);\n }).pipe(\n Effect.map((result) => Completion.Connected({ started: result })),\n Effect.catch((error) =>\n Effect.succeed(\n Completion.Failed({\n error,\n recovery:\n !persisting && error.reason._tag === \"Unauthenticated\" ? \"restart\" : \"inspect\",\n }),\n ),\n ),\n );\n }),\n );\n\n const complete: Interface[\"complete\"] = (input) =>\n completeOutcome(input).pipe(\n Effect.flatMap((outcome) =>\n outcome._tag === \"Connected\" ? Effect.succeed(outcome.started) : Effect.fail(outcome.error),\n ),\n );\n\n const attach: Interface[\"attach\"] = (input) =>\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(input.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const session = yield* sessionFor(connection);\n return yield* attachWith({\n definition,\n connection,\n session,\n domain: input.domain,\n ...(input.target === undefined ? {} : { target: input.target }),\n });\n });\n\n const disconnect: Interface[\"disconnect\"] = (connectionId) =>\n Effect.gen(function* () {\n yield* recover;\n const connection = yield* storage.connections.get(connectionId);\n const attachments = yield* storage.attachments.list(connectionId);\n yield* Effect.forEach(attachments, (attachment) => storage.attachments.remove(attachment.id));\n yield* storage.connections.remove(connectionId);\n const remaining = (yield* storage.connections.list()).some(\n (candidate) => candidate.authorizationId === connection.authorizationId,\n );\n if (remaining) return;\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n yield* storage.authorizations.revoke(authorization.id, revokeAt(authorization));\n });\n\n const session: Interface[\"session\"] = (attachmentId) =>\n Effect.gen(function* () {\n const attachment = yield* storage.attachments.get(attachmentId);\n const connection = yield* storage.connections.get(attachment.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const stored = yield* Errors.decode(Target, attachment.target, \"target\");\n const target: Provider.Target = {\n ...stored,\n context: yield* Provider.decodeContext(definition, stored.context),\n };\n const built = yield* sessionFor(connection);\n // The zone must still be reachable by this credential: it may have moved account, been\n // deleted, or become a kind the provider cannot host records in.\n const reachable = yield* built.listTargets();\n if (!reachable.some((candidate) => candidate.zone === target.zone)) {\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: target.zone }));\n }\n return { session: built, target };\n });\n\n return {\n inspect,\n zones,\n discover,\n start,\n reconnect,\n complete,\n completeOutcome,\n attach,\n detach: (attachmentId) => storage.attachments.remove(attachmentId),\n disconnect,\n refresh,\n session,\n };\n});\n\nexport const layer: Layer.Layer<\n Service,\n never,\n Storage.Service | Custody.Service | Providers.Service | Resolver.Service\n> = Layer.effect(Service)(make);\n\nconst accessor =\n <Args extends ReadonlyArray<unknown>, A>(\n pick: (service: Interface) => (...args: Args) => Fx<A>,\n ): ((...args: Args) => Effect.Effect<A, Errors.DomainKitError, Principal.Service | Service>) =>\n (...args) =>\n Effect.flatMap(Service, (service) => pick(service)(...args));\n\nexport const inspect = accessor((service) => service.inspect);\nexport const zones = accessor((service) => service.zones);\nexport const discover = accessor((service) => service.discover);\nexport const start = accessor((service) => service.start);\nexport const reconnect = accessor((service) => service.reconnect);\nexport const complete = accessor((service) => service.complete);\nexport const completeOutcome = accessor((service) => service.completeOutcome);\nexport const attach = accessor((service) => service.attach);\nexport const detach = accessor((service) => service.detach);\nexport const disconnect = accessor((service) => service.disconnect);\nexport const refresh = accessor((service) => service.refresh);\nexport const session = accessor((service) => service.session);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Connect.mjs","names":["Errors.fail","Reason.InvalidInput","Storage.Service","Custody.Service","Providers.Service","Resolver.Service","Storage.Credential","Provider.decodeContext","Reason.Reconnect","Principal.Service","Reason.Forbidden","Storage.Authorization","Provider.encodeContext","DomainName.decode","Reason.NotFound","Storage.Attachment","candidates","DomainName.candidates","Provider.describeMethods","pkce","OAuth.pkce","Storage.Continuation","Callback.refuse","Errors.DomainKitError","Errors.decode","Reason.Unauthenticated"],"sources":["../src/Connect.ts"],"sourcesContent":["/**\n * Connect a provider, attach domains to it, and keep the credential alive. Interactive methods\n * (OAuth, integration) return a redirect and complete on callback; token methods connect in one\n * call. Reuse of an existing connection for a new domain is `attach`.\n */\nimport { Context, Data, DateTime, Duration, Effect, Layer, Option, Redacted, Schema } from \"effect\";\n\nimport * as Custody from \"./Custody.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as DomainName from \"./DomainName.ts\";\nimport { fresh } from \"./internal/ids.ts\";\nimport * as OAuth from \"./internal/oauth.ts\";\nimport * as Callback from \"./internal/callback.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Provider from \"./Provider.ts\";\nimport * as Providers from \"./Providers.ts\";\nimport * as Resolver from \"./Resolver.ts\";\nimport * as Storage from \"./Storage.ts\";\n\ntype Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;\n\nexport type Method = Data.TaggedEnum<{\n /** Values keyed by the provider's declared `auth.token.fields`. */\n Token: { readonly values: Provider.TokenValues };\n OAuth: { readonly returnTo?: string };\n Integration: { readonly returnTo?: string };\n}>;\nconst MethodEnum = Data.taggedEnum<Method>();\n/** `token` accepts a lone string (`{ token }`) or a record; every value is wrapped in `Redacted`. */\nexport const Method = {\n ...MethodEnum,\n token: (\n values: string | Readonly<Record<string, Redacted.Redacted<string> | string | undefined>>,\n ): Method =>\n MethodEnum.Token({\n values:\n typeof values === \"string\"\n ? { token: Redacted.make(values) }\n : Object.fromEntries(\n Object.entries(values).map(([name, value]) => [\n name,\n typeof value === \"string\" ? Redacted.make(value) : value,\n ]),\n ),\n }),\n oauth: (options: { readonly returnTo?: string } = {}): Method => MethodEnum.OAuth(options),\n integration: (options: { readonly returnTo?: string } = {}): Method =>\n MethodEnum.Integration(options),\n} as const;\n\n/** Everything the UI needs about a domain's provider state. */\nexport interface Snapshot {\n readonly domain: string;\n readonly attachment: Storage.Attachment | null;\n readonly connection: Storage.Connection | null;\n readonly authorization: Pick<\n Storage.Authorization,\n \"id\" | \"provider\" | \"method\" | \"capabilities\" | \"revocation\" | \"label\"\n > | null;\n readonly lastReceiptId: string | null;\n /** How many domains this domain's connection serves, including this one; `0` without one. */\n readonly connectionDomains: number;\n /** Other connections of this owner that could serve this domain. */\n readonly reusable: ReadonlyArray<{\n readonly connection: Storage.Connection;\n readonly provider: string;\n readonly method: Storage.AuthMethod;\n }>;\n readonly providers: ReadonlyArray<{\n readonly id: string;\n readonly name: string;\n readonly methods: ReadonlyArray<Provider.MethodDescriptor>;\n }>;\n}\n\nexport type Started = Data.TaggedEnum<{\n Connected: {\n readonly connection: Storage.Connection;\n readonly attachment: Storage.Attachment | null;\n };\n Redirect: { readonly authorizationUrl: string; readonly continuationId: string };\n SelectionRequired: {\n readonly connection: Storage.Connection;\n readonly candidates: ReadonlyArray<Provider.Target>;\n };\n}>;\nexport const Started = Data.taggedEnum<Started>();\n\n/** A verified completion result. Errors stay on the server; do not serialize them to a browser. */\nexport type Completion = Data.TaggedEnum<{\n Connected: { readonly started: Exclude<Started, { readonly _tag: \"Redirect\" }> };\n Cancelled: { readonly error: Errors.DomainKitError; readonly connectionId?: string };\n Failed: {\n readonly error: Errors.DomainKitError;\n readonly recovery: \"restart\" | \"inspect\";\n readonly connectionId?: string;\n };\n}>;\nexport const Completion = Data.taggedEnum<Completion>();\n\n/** `attach` either attached the domain or needs the caller to pick a zone. */\nexport type Selection = Data.TaggedEnum<{\n SelectionRequired: { readonly candidates: ReadonlyArray<Provider.Target> };\n}>;\nexport const Selection = Data.taggedEnum<Selection>();\n\nexport type Attached = Storage.Attachment | Selection;\n\nexport type Refreshed = \"current\" | \"refreshed\" | \"reconnect\";\n\n/** Which of the principal's connections serves a domain, from nameserver evidence and zone lists. */\nexport type Discovery = Data.TaggedEnum<{\n Resolved: { readonly connectionId: string; readonly target: Provider.Target };\n SelectionRequired: {\n readonly candidates: ReadonlyArray<{\n readonly connectionId: string;\n readonly target: Provider.Target;\n }>;\n };\n /**\n * No connection reaches the domain. `host` names the one registered provider whose declared\n * nameserver suffixes cover every authoritative nameserver; `null` when none or several do.\n */\n NotFound: {\n readonly nameservers: ReadonlyArray<string>;\n readonly host: { readonly provider: string } | null;\n };\n}>;\nexport const Discovery = Data.taggedEnum<Discovery>();\n\n/** One zone a connection reaches, with the connection that reaches it. */\nexport interface Zone {\n readonly connectionId: string;\n readonly provider: string;\n readonly target: Provider.Target;\n}\n\n/**\n * A connection's standing in a zone listing. `reconnect` is a credential the provider turned down:\n * the connection is still the owner's, and the other connections' zones came back regardless.\n */\nexport interface ZoneConnection {\n readonly connectionId: string;\n readonly provider: string;\n /** What the provider called the account at connect time; `null` when it named none. */\n readonly label: string | null;\n readonly status: \"connected\" | \"reconnect\";\n}\n\n/** Every zone the principal's connections reach, and how each connection is standing. */\nexport interface Zones {\n /** Ordered by zone, then by connection, so two listings of the same state read the same. */\n readonly zones: ReadonlyArray<Zone>;\n readonly connections: ReadonlyArray<ZoneConnection>;\n /** Every provider a customer could connect from here, so an offer needs no second call. */\n readonly providers: Snapshot[\"providers\"];\n}\n\nexport interface Interface {\n readonly inspect: (domain: string) => Fx<Snapshot>;\n /**\n * Every zone the principal's connections can serve, from `Provider.Session.listTargets`. A\n * connection whose credential the provider turned down is marked `reconnect` in `connections`\n * and contributes no zones; the rest still list.\n */\n readonly zones: (options?: { readonly provider?: string }) => Fx<Zones>;\n /**\n * Resolve the domain's authoritative nameservers, then match them against the zones each of\n * the principal's connections can reach: the closest zone wins, a decisive nameserver match\n * breaks ties, and anything else needs a selection.\n */\n readonly discover: (domain: string) => Fx<Discovery>;\n /** Connect a provider and, when `domain` is given, attach it. Token methods finish here. */\n readonly start: (input: {\n readonly provider: string;\n readonly method: Method;\n readonly domain?: string;\n readonly callbackUrl?: string;\n }) => Fx<Started>;\n /**\n * Replace the credential behind a connection the owner already holds, keeping the connection and\n * every domain attached to it. This is what a customer means by reconnecting: starting again\n * would mint a second account and leave the domains on the one the provider turned down. The\n * provider is the connection's own; a credential for a different account is the customer's\n * choice, and the zone check on every session catches a zone it can no longer reach.\n */\n readonly reconnect: (input: {\n readonly connectionId: string;\n readonly method: Method;\n readonly callbackUrl?: string;\n }) => Fx<Started>;\n /** Finish an interactive connection from the provider callback (the full callback URL). */\n readonly complete: (input: {\n readonly continuationId: string;\n readonly callbackUrl: string;\n }) => Fx<Started>;\n /**\n * Classify only after live owner/actor verification inside the completion lock. Optional\n * `provider` binds the flow to a mounted route. Invalid flows fail without an outcome.\n * Cancellation and failure leave the continuation retryable; persistence is not atomic.\n */\n readonly completeOutcome: (input: {\n readonly continuationId: string;\n readonly callbackUrl: string;\n readonly provider?: string;\n }) => Fx<Completion>;\n readonly attach: (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly target?: Provider.Target;\n }) => Fx<Attached>;\n /** Records stay in DNS; DomainKit forgets the domain. Use `Cleanup` first to remove records. */\n readonly detach: (attachmentId: string) => Fx<void>;\n /** Detach every domain and revoke the credential at the provider when the method supports it. */\n readonly disconnect: (connectionId: string) => Fx<void>;\n /** Refresh a near-expiry credential, single-flighted. Called automatically before provider use. */\n readonly refresh: (connectionId: string) => Fx<Refreshed>;\n /** Live provider session for an attachment; used by Provision, Cleanup, Verify. */\n readonly session: (\n attachmentId: string,\n ) => Fx<{ readonly session: Provider.Session; readonly target: Provider.Target }>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Connect\") {}\n\n/** Which owner-scoped reuse is allowed. Default: any connection of the same owner may serve any of its domains. */\nexport interface PolicyShape {\n readonly allowReuse: (input: {\n readonly connection: Storage.Connection;\n readonly domain: string;\n }) => Effect.Effect<boolean, never, Principal.Service>;\n /** Refresh this long before expiry. Default 10 minutes. */\n readonly refreshBeforeMs: number;\n /** Continuation lifetime. Default 15 minutes. */\n readonly continuationTtlMs: number;\n}\nexport const defaults: PolicyShape = {\n allowReuse: () => Effect.succeed(true),\n refreshBeforeMs: 10 * 60_000,\n continuationTtlMs: 15 * 60_000,\n};\nexport class Policy extends Context.Reference<PolicyShape>(\"@domainkit/Connect/Policy\", {\n defaultValue: () => defaults,\n}) {}\n\n// ---------------------------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------------------------\n\nconst Payload = Schema.Struct({\n method: Schema.Literals([\"oauth\", \"integration\"]),\n codeVerifier: Schema.NullOr(Schema.String),\n callbackUrl: Schema.String,\n domain: Schema.NullOr(Schema.String),\n /** Set when the flow is re-crediting a connection rather than making one. */\n connectionId: Schema.NullOr(Schema.String),\n});\n\nconst Target = Schema.Struct({\n zone: Schema.String,\n context: Schema.Unknown,\n label: Schema.String,\n});\n\nconst invalid = (message: string, field?: string) =>\n Errors.fail(new Reason.InvalidInput({ message, ...(field === undefined ? {} : { field }) }));\n\n/** Nameserver hostnames compare case-insensitively and without a trailing dot. */\nconst normalize = (name: string) => name.toLowerCase().replace(/\\.$/, \"\");\n\nexport const make: Effect.Effect<\n Interface,\n never,\n Storage.Service | Custody.Service | Providers.Service | Resolver.Service\n> = Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const custody = yield* Custody.Service;\n const providers = yield* Providers.Service;\n const resolver = yield* Resolver.Service;\n\n const seal = (issued: Provider.IssuedCredential) =>\n Effect.gen(function* () {\n const ciphertext = yield* custody.seal(issued.secret);\n return new Storage.Credential({\n ciphertext,\n expiresAt: issued.expiresAt,\n rotatedAt: yield* DateTime.now,\n });\n });\n\n /** Plaintext credential with the context decoded from its stored envelope. */\n const open = (authorization: Storage.Authorization): Fx<Provider.Credential> =>\n Effect.gen(function* () {\n const definition = yield* providers.get(authorization.provider);\n const row = yield* storage.authorizations.credential(authorization.id);\n const secret = yield* custody.open(row.ciphertext);\n const context = yield* Provider.decodeContext(definition, authorization.context);\n return { secret, context };\n });\n\n const refresherFor = (definition: Provider.Definition, method: Storage.AuthMethod) =>\n method === \"oauth\"\n ? definition.auth.oauth?.refresh\n : method === \"integration\"\n ? definition.auth.integration?.refresh\n : undefined;\n\n const revokerFor = (definition: Provider.Definition, method: Storage.AuthMethod) =>\n method === \"oauth\"\n ? definition.auth.oauth?.revoke\n : method === \"integration\"\n ? definition.auth.integration?.revoke\n : undefined;\n\n const revokeAt = (authorization: Storage.Authorization): Fx<void> =>\n Effect.gen(function* () {\n const definition = yield* providers.get(authorization.provider);\n const revoke = revokerFor(definition, authorization.method);\n if (revoke === undefined) return;\n const credential = yield* open(authorization);\n yield* revoke(credential);\n });\n\n /** Finish revocations a crash interrupted; best effort, never blocks the caller. */\n const recover = storage.authorizations.recoverRevocations(revokeAt).pipe(Effect.ignore);\n\n const refresh: Interface[\"refresh\"] = (connectionId) =>\n Effect.gen(function* () {\n const policy = yield* Policy;\n const connection = yield* storage.connections.get(connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const refresher = refresherFor(definition, authorization.method);\n const dueAt = (row: Storage.Credential, now: DateTime.Utc) =>\n row.expiresAt !== null &&\n DateTime.toEpochMillis(row.expiresAt) - DateTime.toEpochMillis(now) <=\n policy.refreshBeforeMs;\n const expired = (row: Storage.Credential, now: DateTime.Utc) =>\n row.expiresAt !== null &&\n DateTime.toEpochMillis(row.expiresAt) <= DateTime.toEpochMillis(now);\n const current = yield* storage.authorizations.credential(authorization.id);\n const now = yield* DateTime.now;\n if (!dueAt(current, now)) return \"current\" as const;\n if (refresher === undefined)\n return expired(current, now) ? (\"reconnect\" as const) : (\"current\" as const);\n return yield* storage\n .withLock(\n `refresh:${authorization.id}`,\n Effect.gen(function* () {\n // Disconnect's two-phase revocation does not take this lock: re-read the row here\n // so a revocation that started meanwhile skips the refresh instead of racing it.\n const live = yield* storage.authorizations.get(authorization.id).pipe(\n Effect.map(Option.some),\n Effect.catchIf(\n (error) => error.reason._tag === \"NotFound\",\n () => Effect.succeed(Option.none<Storage.Authorization>()),\n ),\n );\n if (Option.isNone(live) || live.value.revocation !== \"active\") {\n return \"reconnect\" as const;\n }\n const latest = yield* storage.authorizations.credential(authorization.id);\n if (!dueAt(latest, yield* DateTime.now)) return \"current\" as const;\n const secret = yield* custody.open(latest.ciphertext);\n const context = yield* Provider.decodeContext(definition, live.value.context);\n const issued = yield* refresher({ secret, context });\n // A revocation that completed while the provider was issuing leaves no row to\n // rotate; revoke the fresh credential so it does not stay live without a home.\n yield* storage.authorizations\n .rotate(authorization.id, yield* seal(issued))\n .pipe(\n Effect.tapError((error) =>\n error.reason._tag === \"NotFound\"\n ? (revokerFor(definition, authorization.method)?.(issued) ?? Effect.void).pipe(\n Effect.ignore,\n )\n : Effect.void,\n ),\n );\n return \"refreshed\" as const;\n }),\n )\n .pipe(\n Effect.catchIf(\n (error) => error.reason._tag === \"Busy\" && !expired(current, now),\n () => Effect.succeed(\"current\" as const),\n ),\n Effect.catchIf(\n (error) => error.reason._tag === \"Unauthenticated\",\n () => Effect.succeed(\"reconnect\" as const),\n ),\n );\n });\n\n const sessionFor = (connection: Storage.Connection): Fx<Provider.Session> =>\n Effect.gen(function* () {\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const reconnect = Errors.fail(\n new Reason.Reconnect({\n provider: authorization.provider,\n connectionId: connection.id,\n }),\n );\n if (authorization.revocation !== \"active\") return yield* reconnect;\n if ((yield* refresh(connection.id)) === \"reconnect\") return yield* reconnect;\n const definition = yield* providers.get(authorization.provider);\n return definition.session(yield* open(authorization));\n });\n\n const connectWith = (input: {\n readonly definition: Provider.Definition;\n readonly method: Storage.AuthMethod;\n readonly issued: Provider.IssuedCredential;\n readonly requiredCapabilities: ReadonlyArray<Storage.Capability>;\n }): Fx<{ readonly connection: Storage.Connection; readonly session: Provider.Session }> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const session = input.definition.session(input.issued);\n const held = yield* session.capabilities();\n const missing = input.requiredCapabilities.filter((capability) => !held.includes(capability));\n if (missing.length > 0) {\n return yield* Errors.fail(\n new Reason.Forbidden({\n message: `${input.definition.name} credential lacks ${missing.join(\", \")}`,\n }),\n );\n }\n const authorization = new Storage.Authorization({\n id: yield* fresh(\"auth\"),\n ownerId: principal.ownerId,\n provider: input.definition.id,\n method: input.method,\n capabilities: held,\n context: yield* Provider.encodeContext(input.definition, input.issued.context),\n label: input.issued.label ?? null,\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: yield* DateTime.now,\n });\n yield* storage.authorizations.upsert({\n authorization,\n credential: yield* seal(input.issued),\n });\n const connection = yield* storage.connections.create(authorization.id);\n return { connection, session };\n });\n\n /**\n * Put a fresh credential behind an authorization the owner already has. The row keeps its id, so\n * every connection over it, and every domain on those connections, stays exactly where it is.\n */\n const replaceWith = (input: {\n readonly definition: Provider.Definition;\n readonly authorizationId: string;\n readonly method: Storage.AuthMethod;\n readonly issued: Provider.IssuedCredential;\n readonly requiredCapabilities: ReadonlyArray<Storage.Capability>;\n }): Fx<void> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const session = input.definition.session(input.issued);\n const held = yield* session.capabilities();\n const missing = input.requiredCapabilities.filter((capability) => !held.includes(capability));\n if (missing.length > 0) {\n return yield* Errors.fail(\n new Reason.Forbidden({\n message: `${input.definition.name} credential lacks ${missing.join(\", \")}`,\n }),\n );\n }\n const authorization = new Storage.Authorization({\n id: input.authorizationId,\n ownerId: principal.ownerId,\n provider: input.definition.id,\n method: input.method,\n capabilities: held,\n context: yield* Provider.encodeContext(input.definition, input.issued.context),\n label: input.issued.label ?? null,\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: yield* DateTime.now,\n });\n yield* storage.authorizations.upsert({\n authorization,\n credential: yield* seal(input.issued),\n expectedId: input.authorizationId,\n });\n });\n\n const attachWith = (input: {\n readonly definition: Provider.Definition;\n readonly connection: Storage.Connection;\n readonly session: Provider.Session;\n readonly domain: string;\n readonly target?: Provider.Target;\n }): Fx<Attached> =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input.domain);\n const existing = yield* storage.attachments.byDomain(domain);\n if (Option.isSome(existing)) {\n if (existing.value.connectionId === input.connection.id) return existing.value;\n return yield* invalid(`${domain} is already attached to another connection`, \"domain\");\n }\n const resolution: Provider.Resolution =\n input.target === undefined\n ? yield* input.session.resolveTarget(domain)\n : Provider.Resolution.Resolved({ target: input.target });\n switch (resolution._tag) {\n case \"SelectionRequired\":\n return Selection.SelectionRequired({ candidates: resolution.candidates });\n case \"NotFound\":\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: domain }));\n case \"Resolved\":\n return yield* storage.attachments.create({\n connectionId: input.connection.id,\n domain,\n zone: resolution.target.zone,\n label: resolution.target.label,\n target: {\n ...resolution.target,\n context: yield* Provider.encodeContext(input.definition, resolution.target.context),\n },\n });\n }\n });\n\n const started = (\n connection: Storage.Connection,\n attached: Attached | null,\n ): Exclude<Started, { readonly _tag: \"Redirect\" }> =>\n attached === null\n ? Started.Connected({ connection, attachment: null })\n : attached instanceof Storage.Attachment\n ? Started.Connected({ connection, attachment: attached })\n : Started.SelectionRequired({ connection, candidates: attached.candidates });\n\n /** Authoritative nameservers for the closest zone that answers an NS query. */\n const authoritative = (candidates: ReadonlyArray<DomainName.Model>) =>\n Effect.gen(function* () {\n for (const zone of candidates) {\n const outcomes = yield* resolver.resolve(zone, \"NS\");\n const names = outcomes.flatMap((outcome) =>\n outcome._tag === \"Answered\"\n ? outcome.answer.records.flatMap((record) =>\n record._tag === \"NS\" ? [record.nameserver] : [],\n )\n : [],\n );\n if (names.length > 0) return [...new Set(names)].sort();\n }\n return [] as ReadonlyArray<string>;\n });\n\n const discover: Interface[\"discover\"] = (input) =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input);\n const candidates = DomainName.candidates(domain);\n const nameservers = yield* authoritative(candidates);\n const reachable: Array<{\n readonly connectionId: string;\n readonly target: Provider.Target;\n }> = [];\n for (const connection of yield* storage.connections.list()) {\n const targets = yield* sessionFor(connection).pipe(\n Effect.flatMap((session) => session.listTargets()),\n Effect.catch(() => Effect.succeed([] as ReadonlyArray<Provider.Target>)),\n );\n for (const target of targets) {\n if (candidates.includes(target.zone as DomainName.Model)) {\n reachable.push({ connectionId: connection.id, target });\n }\n }\n }\n for (const zone of candidates) {\n const matches = reachable\n .filter(({ target }) => target.zone === zone)\n .sort((left, right) => left.connectionId.localeCompare(right.connectionId));\n if (matches.length === 0) continue;\n const [only] = matches;\n if (matches.length === 1 && only !== undefined) return Discovery.Resolved(only);\n const decisive = matches.filter(({ target }) => {\n const configured = new Set((target.nameservers ?? []).map(normalize));\n return nameservers.length > 0 && nameservers.every((name) => configured.has(name));\n });\n const [winner] = decisive;\n if (decisive.length === 1 && winner !== undefined) return Discovery.Resolved(winner);\n return Discovery.SelectionRequired({ candidates: matches });\n }\n return Discovery.NotFound({ nameservers, host: hostOf(nameservers) });\n });\n\n /** The one registered provider whose declared suffixes cover every nameserver, else `null`. */\n const hostOf = (nameservers: ReadonlyArray<string>): { readonly provider: string } | null => {\n if (nameservers.length === 0) return null;\n const names = nameservers.map(normalize);\n const hosts = providers.list().filter((definition) => {\n const suffixes = (definition.nameservers ?? []).map(normalize);\n return (\n suffixes.length > 0 &&\n names.every((name) =>\n suffixes.some((suffix) => name === suffix || name.endsWith(`.${suffix}`)),\n )\n );\n });\n const [host] = hosts;\n return hosts.length === 1 && host !== undefined ? { provider: host.id } : null;\n };\n\n const zones: Interface[\"zones\"] = (options = {}) =>\n Effect.gen(function* () {\n const reachable: Array<Zone> = [];\n const connections: Array<ZoneConnection> = [];\n const filter = options.provider === undefined ? undefined : { provider: options.provider };\n for (const connection of yield* storage.connections.list(filter)) {\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n // A credential the provider turned down is one connection's problem, not the listing's:\n // it is reported as needing a reconnect and every other connection still answers. Any\n // other failure is the listing's own, so it is raised rather than dressed up as an\n // authorization the customer has to grant again.\n const targets = yield* sessionFor(connection).pipe(\n Effect.flatMap((session) => session.listTargets()),\n Effect.map(Option.some),\n Effect.catchIf(\n (error) =>\n error.reason._tag === \"Reconnect\" ||\n error.reason._tag === \"Unauthenticated\" ||\n error.reason._tag === \"Forbidden\",\n () => Effect.succeed(Option.none<ReadonlyArray<Provider.Target>>()),\n ),\n );\n connections.push({\n connectionId: connection.id,\n provider: authorization.provider,\n label: authorization.label,\n status: Option.isNone(targets) ? \"reconnect\" : \"connected\",\n });\n if (Option.isNone(targets)) continue;\n for (const target of targets.value) {\n reachable.push({\n connectionId: connection.id,\n provider: authorization.provider,\n target,\n });\n }\n }\n return {\n zones: reachable.sort(\n (left, right) =>\n left.target.zone.localeCompare(right.target.zone) ||\n left.connectionId.localeCompare(right.connectionId),\n ),\n connections,\n providers: providers.list().map((definition) => ({\n id: definition.id,\n name: definition.name,\n methods: Provider.describeMethods(definition),\n })),\n };\n });\n\n const inspect: Interface[\"inspect\"] = (input) =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input);\n const policy = yield* Policy;\n const attachment = Option.getOrNull(yield* storage.attachments.byDomain(domain));\n const connection =\n attachment === null ? null : yield* storage.connections.get(attachment.connectionId);\n const authorization =\n connection === null ? null : yield* storage.authorizations.get(connection.authorizationId);\n const latest =\n attachment === null\n ? Option.none()\n : yield* storage.attempts.latest(attachment.id, \"provisioning\");\n // Letting a connection go takes every domain on it, so the UI has to be able to say how many.\n const attached = connection === null ? [] : yield* storage.attachments.list(connection.id);\n const reusable: Array<Snapshot[\"reusable\"][number]> = [];\n for (const candidate of yield* storage.connections.list()) {\n if (candidate.id === connection?.id) continue;\n if (!(yield* policy.allowReuse({ connection: candidate, domain }))) continue;\n const candidateAuthorization = yield* storage.authorizations.get(candidate.authorizationId);\n if (candidateAuthorization.revocation !== \"active\") continue;\n reusable.push({\n connection: candidate,\n provider: candidateAuthorization.provider,\n method: candidateAuthorization.method,\n });\n }\n return {\n domain,\n attachment,\n connection,\n authorization:\n authorization === null\n ? null\n : {\n id: authorization.id,\n provider: authorization.provider,\n method: authorization.method,\n capabilities: authorization.capabilities,\n revocation: authorization.revocation,\n label: authorization.label,\n },\n lastReceiptId: Option.isSome(latest) ? (latest.value.receipt?.id ?? null) : null,\n connectionDomains: attached.length,\n reusable,\n providers: providers.list().map((definition) => ({\n id: definition.id,\n name: definition.name,\n methods: Provider.describeMethods(definition),\n })),\n };\n });\n\n const start: Interface[\"start\"] = (input) =>\n Effect.gen(function* () {\n yield* recover;\n const definition = yield* providers.get(input.provider);\n if (input.domain !== undefined) yield* DomainName.decode(input.domain);\n switch (input.method._tag) {\n case \"Token\": {\n const token = definition.auth.token;\n if (token === undefined) {\n return yield* invalid(`${definition.name} does not accept tokens`, \"method\");\n }\n const issued = yield* token.authenticate(input.method.values);\n const { connection, session } = yield* connectWith({\n definition,\n method: \"token\",\n issued,\n requiredCapabilities: token.requiredCapabilities,\n });\n const attached =\n input.domain === undefined\n ? null\n : yield* attachWith({ definition, connection, session, domain: input.domain });\n return started(connection, attached);\n }\n case \"OAuth\":\n case \"Integration\":\n return yield* redirectFor({\n definition,\n method: input.method,\n callbackUrl: input.callbackUrl,\n domain: input.domain ?? null,\n connectionId: null,\n });\n }\n });\n\n /** The redirect an interactive method answers with, and the continuation that finishes it. */\n const redirectFor = (input: {\n readonly definition: Provider.Definition;\n readonly method: Extract<Method, { readonly _tag: \"OAuth\" | \"Integration\" }>;\n readonly callbackUrl: string | undefined;\n readonly domain: string | null;\n readonly connectionId: string | null;\n }): Fx<Started> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const policy = yield* Policy;\n const interactive =\n input.method._tag === \"OAuth\"\n ? input.definition.auth.oauth\n : input.definition.auth.integration;\n if (interactive === undefined) {\n return yield* invalid(\n `${input.definition.name} does not offer ${input.method._tag.toLowerCase()} connections`,\n \"method\",\n );\n }\n if (input.callbackUrl === undefined) {\n return yield* invalid(\"Interactive connections need a callbackUrl\", \"callbackUrl\");\n }\n const callbackUrl = input.callbackUrl;\n const continuationId = yield* fresh(\"cont\");\n const pkce = input.method._tag === \"OAuth\" ? yield* OAuth.pkce() : null;\n const redirect =\n input.method._tag === \"OAuth\"\n ? yield* (interactive as Provider.OAuthAuth).start({\n state: continuationId,\n callbackUrl,\n codeChallenge: pkce?.codeChallenge ?? \"\",\n })\n : yield* (interactive as Provider.IntegrationAuth).start({\n state: continuationId,\n callbackUrl,\n });\n yield* storage.continuations.put(\n new Storage.Continuation({\n id: continuationId,\n ownerId: principal.ownerId,\n actorId: principal.actorId,\n provider: input.definition.id,\n payload: Schema.encodeSync(Payload)({\n method: input.method._tag === \"OAuth\" ? \"oauth\" : \"integration\",\n codeVerifier: pkce?.codeVerifier ?? null,\n callbackUrl,\n domain: input.domain,\n connectionId: input.connectionId,\n }),\n returnTo: input.method.returnTo ?? null,\n expiresAt: DateTime.addDuration(\n yield* DateTime.now,\n Duration.millis(policy.continuationTtlMs),\n ),\n }),\n );\n return Started.Redirect({ authorizationUrl: redirect.authorizationUrl, continuationId });\n });\n\n const reconnect: Interface[\"reconnect\"] = (input) =>\n Effect.gen(function* () {\n yield* recover;\n const connection = yield* storage.connections.get(input.connectionId);\n const existing = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(existing.provider);\n switch (input.method._tag) {\n case \"Token\": {\n const token = definition.auth.token;\n if (token === undefined) {\n return yield* invalid(`${definition.name} does not accept tokens`, \"method\");\n }\n const issued = yield* token.authenticate(input.method.values);\n yield* replaceWith({\n definition,\n authorizationId: existing.id,\n method: \"token\",\n issued,\n requiredCapabilities: token.requiredCapabilities,\n });\n return Started.Connected({ connection, attachment: null });\n }\n case \"OAuth\":\n case \"Integration\":\n return yield* redirectFor({\n definition,\n method: input.method,\n callbackUrl: input.callbackUrl,\n domain: null,\n connectionId: connection.id,\n });\n }\n });\n\n const completeOutcome: Interface[\"completeOutcome\"] = (input) =>\n storage.withLock(\n `continuation:${input.continuationId}`,\n Effect.gen(function* () {\n const continuation = yield* storage.continuations.get(input.continuationId);\n const principal = yield* Principal.Service;\n if (\n continuation.ownerId !== principal.ownerId ||\n continuation.actorId !== principal.actorId ||\n (input.provider !== undefined && continuation.provider !== input.provider)\n ) {\n return yield* Callback.refuse;\n }\n const callback = yield* Effect.try({\n try: () => new URL(input.callbackUrl),\n catch: () =>\n new Errors.DomainKitError({\n reason: new Reason.InvalidInput({\n message: \"callbackUrl is not a URL\",\n field: \"callbackUrl\",\n }),\n }),\n });\n // Decode once without changing callback-error precedence for malformed stored payloads.\n const decoded = yield* Errors.decode(Payload, continuation.payload, \"continuation\").pipe(\n Effect.result,\n );\n const reconnection =\n decoded._tag === \"Success\" &&\n decoded.success.connectionId !== null &&\n decoded.success.connectionId.length > 0\n ? { connectionId: decoded.success.connectionId }\n : {};\n const params = Object.fromEntries(callback.searchParams);\n const failed = (message: string) =>\n Completion.Failed({\n error: new Errors.DomainKitError({ reason: new Reason.Unauthenticated({ message }) }),\n recovery: \"restart\",\n ...reconnection,\n });\n // State verification precedes denial classification, including on the direct Effect API.\n if (params.state !== input.continuationId)\n return failed(\"Callback state does not match the continuation\");\n if (params.error !== undefined) {\n const outcome = failed(`Provider returned ${params.error}`);\n // OAuth's standardized consent denial is the only cancellation code we recognize.\n return params.error === \"access_denied\"\n ? Completion.Cancelled({ error: outcome.error, ...reconnection })\n : outcome;\n }\n const code = params.code;\n if (code === undefined) return failed(\"Callback has no code\");\n // Once persistence starts, even a failure may leave a credential or connection behind.\n let persisting = false;\n return yield* Effect.gen(function* () {\n if (decoded._tag === \"Failure\") return yield* Effect.fail(decoded.failure);\n const payload = decoded.success;\n const definition = yield* providers.get(continuation.provider);\n const issued =\n payload.method === \"oauth\"\n ? yield* Effect.gen(function* () {\n const oauth = definition.auth.oauth;\n if (oauth === undefined || payload.codeVerifier === null) {\n return yield* invalid(\n `${definition.name} does not offer oauth connections`,\n \"method\",\n );\n }\n return yield* oauth.complete({\n code,\n callbackUrl: payload.callbackUrl,\n codeVerifier: payload.codeVerifier,\n params,\n });\n })\n : yield* Effect.gen(function* () {\n const integration = definition.auth.integration;\n if (integration === undefined) {\n return yield* invalid(\n `${definition.name} does not offer integration connections`,\n \"method\",\n );\n }\n return yield* integration.complete({\n code,\n callbackUrl: payload.callbackUrl,\n params,\n });\n });\n persisting = true;\n if (payload.connectionId !== null) {\n const held = yield* storage.connections.get(payload.connectionId);\n const existing = yield* storage.authorizations.get(held.authorizationId);\n yield* replaceWith({\n definition,\n authorizationId: existing.id,\n method: payload.method,\n issued,\n requiredCapabilities: [],\n });\n yield* storage.continuations.consume(input.continuationId).pipe(Effect.ignore);\n return Started.Connected({ connection: held, attachment: null });\n }\n const { connection, session } = yield* connectWith({\n definition,\n method: payload.method,\n issued,\n requiredCapabilities: [],\n });\n const attached =\n payload.domain === null\n ? null\n : yield* attachWith({ definition, connection, session, domain: payload.domain });\n // The connection is durable at this point. Whatever happens to the continuation\n // now (expired, already spent, or a storage failure while spending it) no longer\n // changes the outcome: the row expires on its own and a replayed callback fails at\n // the provider, so the persisted result is returned.\n yield* storage.continuations.consume(input.continuationId).pipe(Effect.ignore);\n return started(connection, attached);\n }).pipe(\n Effect.map((result) => Completion.Connected({ started: result })),\n Effect.catch((error) =>\n Effect.succeed(\n Completion.Failed({\n error,\n ...reconnection,\n recovery:\n !persisting && error.reason._tag === \"Unauthenticated\" ? \"restart\" : \"inspect\",\n }),\n ),\n ),\n );\n }),\n );\n\n const complete: Interface[\"complete\"] = (input) =>\n completeOutcome(input).pipe(\n Effect.flatMap((outcome) =>\n outcome._tag === \"Connected\" ? Effect.succeed(outcome.started) : Effect.fail(outcome.error),\n ),\n );\n\n const attach: Interface[\"attach\"] = (input) =>\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(input.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const session = yield* sessionFor(connection);\n return yield* attachWith({\n definition,\n connection,\n session,\n domain: input.domain,\n ...(input.target === undefined ? {} : { target: input.target }),\n });\n });\n\n const disconnect: Interface[\"disconnect\"] = (connectionId) =>\n Effect.gen(function* () {\n yield* recover;\n const connection = yield* storage.connections.get(connectionId);\n const attachments = yield* storage.attachments.list(connectionId);\n yield* Effect.forEach(attachments, (attachment) => storage.attachments.remove(attachment.id));\n yield* storage.connections.remove(connectionId);\n const remaining = (yield* storage.connections.list()).some(\n (candidate) => candidate.authorizationId === connection.authorizationId,\n );\n if (remaining) return;\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n yield* storage.authorizations.revoke(authorization.id, revokeAt(authorization));\n });\n\n const session: Interface[\"session\"] = (attachmentId) =>\n Effect.gen(function* () {\n const attachment = yield* storage.attachments.get(attachmentId);\n const connection = yield* storage.connections.get(attachment.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const stored = yield* Errors.decode(Target, attachment.target, \"target\");\n const target: Provider.Target = {\n ...stored,\n context: yield* Provider.decodeContext(definition, stored.context),\n };\n const built = yield* sessionFor(connection);\n // The zone must still be reachable by this credential: it may have moved account, been\n // deleted, or become a kind the provider cannot host records in.\n const reachable = yield* built.listTargets();\n if (!reachable.some((candidate) => candidate.zone === target.zone)) {\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: target.zone }));\n }\n return { session: built, target };\n });\n\n return {\n inspect,\n zones,\n discover,\n start,\n reconnect,\n complete,\n completeOutcome,\n attach,\n detach: (attachmentId) => storage.attachments.remove(attachmentId),\n disconnect,\n refresh,\n session,\n };\n});\n\nexport const layer: Layer.Layer<\n Service,\n never,\n Storage.Service | Custody.Service | Providers.Service | Resolver.Service\n> = Layer.effect(Service)(make);\n\nconst accessor =\n <Args extends ReadonlyArray<unknown>, A>(\n pick: (service: Interface) => (...args: Args) => Fx<A>,\n ): ((...args: Args) => Effect.Effect<A, Errors.DomainKitError, Principal.Service | Service>) =>\n (...args) =>\n Effect.flatMap(Service, (service) => pick(service)(...args));\n\nexport const inspect = accessor((service) => service.inspect);\nexport const zones = accessor((service) => service.zones);\nexport const discover = accessor((service) => service.discover);\nexport const start = accessor((service) => service.start);\nexport const reconnect = accessor((service) => service.reconnect);\nexport const complete = accessor((service) => service.complete);\nexport const completeOutcome = accessor((service) => service.completeOutcome);\nexport const attach = accessor((service) => service.attach);\nexport const detach = accessor((service) => service.detach);\nexport const disconnect = accessor((service) => service.disconnect);\nexport const refresh = accessor((service) => service.refresh);\nexport const session = accessor((service) => service.session);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAM,aAAa,KAAK,WAAmB;;AAE3C,MAAa,SAAS;CACpB,GAAG;CACH,QACE,WAEA,WAAW,MAAM,EACf,QACE,OAAO,WAAW,WACd,EAAE,OAAO,SAAS,KAAK,MAAM,EAAE,IAC/B,OAAO,YACL,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW,CAC5C,MACA,OAAO,UAAU,WAAW,SAAS,KAAK,KAAK,IAAI,KACrD,CAAC,CACH,EACR,CAAC;CACH,QAAQ,UAA0C,CAAC,MAAc,WAAW,MAAM,OAAO;CACzF,cAAc,UAA0C,CAAC,MACvD,WAAW,YAAY,OAAO;AAClC;AAsCA,MAAa,UAAU,KAAK,WAAoB;AAYhD,MAAa,aAAa,KAAK,WAAuB;AAMtD,MAAa,YAAY,KAAK,WAAsB;AAwBpD,MAAa,YAAY,KAAK,WAAsB;AA+FpD,IAAa,UAAb,cAA6B,QAAQ,QAA4B,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;AAa1F,MAAa,WAAwB;CACnC,kBAAkB,OAAO,QAAQ,IAAI;CACrC,iBAAiB;CACjB,mBAAmB;AACrB;AACA,IAAa,SAAb,cAA4B,QAAQ,UAAuB,6BAA6B,EACtF,oBAAoB,SACtB,CAAC,CAAC,CAAC,CAAC;AAMJ,MAAM,UAAU,OAAO,OAAO;CAC5B,QAAQ,OAAO,SAAS,CAAC,SAAS,aAAa,CAAC;CAChD,cAAc,OAAO,OAAO,OAAO,MAAM;CACzC,aAAa,OAAO;CACpB,QAAQ,OAAO,OAAO,OAAO,MAAM;;CAEnC,cAAc,OAAO,OAAO,OAAO,MAAM;AAC3C,CAAC;AAED,MAAM,SAAS,OAAO,OAAO;CAC3B,MAAM,OAAO;CACb,SAAS,OAAO;CAChB,OAAO,OAAO;AAChB,CAAC;AAED,MAAM,WAAW,SAAiB,UAChCA,KAAY,IAAIC,aAAoB;CAAE;CAAS,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;AAAG,CAAC,CAAC;;AAG7F,MAAM,aAAa,SAAiB,KAAK,YAAY,CAAC,CAAC,QAAQ,OAAO,EAAE;AAExE,MAAa,OAIT,OAAO,IAAI,aAAa;CAC1B,MAAM,UAAU,OAAOC;CACvB,MAAM,UAAU,OAAOC;CACvB,MAAM,YAAY,OAAOC;CACzB,MAAM,WAAW,OAAOC;CAExB,MAAM,QAAQ,WACZ,OAAO,IAAI,aAAa;EACtB,MAAM,aAAa,OAAO,QAAQ,KAAK,OAAO,MAAM;EACpD,OAAO,IAAIC,WAAmB;GAC5B;GACA,WAAW,OAAO;GAClB,WAAW,OAAO,SAAS;EAC7B,CAAC;CACH,CAAC;;CAGH,MAAM,QAAQ,kBACZ,OAAO,IAAI,aAAa;EACtB,MAAM,aAAa,OAAO,UAAU,IAAI,cAAc,QAAQ;EAC9D,MAAM,MAAM,OAAO,QAAQ,eAAe,WAAW,cAAc,EAAE;EAGrE,OAAO;GAAE,QAAA,OAFa,QAAQ,KAAK,IAAI,UAAU;GAEhC,SAAA,OADMC,cAAuB,YAAY,cAAc,OAAO;EACtD;CAC3B,CAAC;CAEH,MAAM,gBAAgB,YAAiC,WACrD,WAAW,UACP,WAAW,KAAK,OAAO,UACvB,WAAW,gBACT,WAAW,KAAK,aAAa,UAC7B,KAAA;CAER,MAAM,cAAc,YAAiC,WACnD,WAAW,UACP,WAAW,KAAK,OAAO,SACvB,WAAW,gBACT,WAAW,KAAK,aAAa,SAC7B,KAAA;CAER,MAAM,YAAY,kBAChB,OAAO,IAAI,aAAa;EACtB,MAAM,aAAa,OAAO,UAAU,IAAI,cAAc,QAAQ;EAC9D,MAAM,SAAS,WAAW,YAAY,cAAc,MAAM;EAC1D,IAAI,WAAW,KAAA,GAAW;EAE1B,OAAO,OAAO,OADY,KAAK,aAAa,CACpB;CAC1B,CAAC;;CAGH,MAAM,UAAU,QAAQ,eAAe,mBAAmB,QAAQ,CAAC,CAAC,KAAK,OAAO,MAAM;CAEtF,MAAM,WAAiC,iBACrC,OAAO,IAAI,aAAa;EACtB,MAAM,SAAS,OAAO;EACtB,MAAM,aAAa,OAAO,QAAQ,YAAY,IAAI,YAAY;EAC9D,MAAM,gBAAgB,OAAO,QAAQ,eAAe,IAAI,WAAW,eAAe;EAClF,MAAM,aAAa,OAAO,UAAU,IAAI,cAAc,QAAQ;EAC9D,MAAM,YAAY,aAAa,YAAY,cAAc,MAAM;EAC/D,MAAM,SAAS,KAAyB,QACtC,IAAI,cAAc,QAClB,SAAS,cAAc,IAAI,SAAS,IAAI,SAAS,cAAc,GAAG,KAChE,OAAO;EACX,MAAM,WAAW,KAAyB,QACxC,IAAI,cAAc,QAClB,SAAS,cAAc,IAAI,SAAS,KAAK,SAAS,cAAc,GAAG;EACrE,MAAM,UAAU,OAAO,QAAQ,eAAe,WAAW,cAAc,EAAE;EACzE,MAAM,MAAM,OAAO,SAAS;EAC5B,IAAI,CAAC,MAAM,SAAS,GAAG,GAAG,OAAO;EACjC,IAAI,cAAc,KAAA,GAChB,OAAO,QAAQ,SAAS,GAAG,IAAK,cAAyB;EAC3D,OAAO,OAAO,QACX,SACC,WAAW,cAAc,MACzB,OAAO,IAAI,aAAa;GAGtB,MAAM,OAAO,OAAO,QAAQ,eAAe,IAAI,cAAc,EAAE,CAAC,CAAC,KAC/D,OAAO,IAAI,OAAO,IAAI,GACtB,OAAO,SACJ,UAAU,MAAM,OAAO,SAAS,kBAC3B,OAAO,QAAQ,OAAO,KAA4B,CAAC,CAC3D,CACF;GACA,IAAI,OAAO,OAAO,IAAI,KAAK,KAAK,MAAM,eAAe,UACnD,OAAO;GAET,MAAM,SAAS,OAAO,QAAQ,eAAe,WAAW,cAAc,EAAE;GACxE,IAAI,CAAC,MAAM,QAAQ,OAAO,SAAS,GAAG,GAAG,OAAO;GAChD,MAAM,SAAS,OAAO,QAAQ,KAAK,OAAO,UAAU;GACpD,MAAM,UAAU,OAAOA,cAAuB,YAAY,KAAK,MAAM,OAAO;GAC5E,MAAM,SAAS,OAAO,UAAU;IAAE;IAAQ;GAAQ,CAAC;GAGnD,OAAO,QAAQ,eACZ,OAAO,cAAc,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC,CAC7C,KACC,OAAO,UAAU,UACf,MAAM,OAAO,SAAS,cACjB,WAAW,YAAY,cAAc,MAAM,CAAC,GAAG,MAAM,KAAK,OAAO,KAAA,CAAM,KACtE,OAAO,MACT,IACA,OAAO,IACb,CACF;GACF,OAAO;EACT,CAAC,CACH,CAAC,CACA,KACC,OAAO,SACJ,UAAU,MAAM,OAAO,SAAS,UAAU,CAAC,QAAQ,SAAS,GAAG,SAC1D,OAAO,QAAQ,SAAkB,CACzC,GACA,OAAO,SACJ,UAAU,MAAM,OAAO,SAAS,yBAC3B,OAAO,QAAQ,WAAoB,CAC3C,CACF;CACJ,CAAC;CAEH,MAAM,cAAc,eAClB,OAAO,IAAI,aAAa;EACtB,MAAM,gBAAgB,OAAO,QAAQ,eAAe,IAAI,WAAW,eAAe;EAClF,MAAM,YAAYP,KAChB,IAAIQ,UAAiB;GACnB,UAAU,cAAc;GACxB,cAAc,WAAW;EAC3B,CAAC,CACH;EACA,IAAI,cAAc,eAAe,UAAU,OAAO,OAAO;EACzD,KAAK,OAAO,QAAQ,WAAW,EAAE,OAAO,aAAa,OAAO,OAAO;EAEnE,QAAO,OADmB,UAAU,IAAI,cAAc,QAAQ,EAAA,CAC5C,QAAQ,OAAO,KAAK,aAAa,CAAC;CACtD,CAAC;CAEH,MAAM,eAAe,UAMnB,OAAO,IAAI,aAAa;EACtB,MAAM,YAAY,OAAOC;EACzB,MAAM,UAAU,MAAM,WAAW,QAAQ,MAAM,MAAM;EACrD,MAAM,OAAO,OAAO,QAAQ,aAAa;EACzC,MAAM,UAAU,MAAM,qBAAqB,QAAQ,eAAe,CAAC,KAAK,SAAS,UAAU,CAAC;EAC5F,IAAI,QAAQ,SAAS,GACnB,OAAO,OAAOT,KACZ,IAAIU,UAAiB,EACnB,SAAS,GAAG,MAAM,WAAW,KAAK,oBAAoB,QAAQ,KAAK,IAAI,IACzE,CAAC,CACH;EAEF,MAAM,gBAAgB,IAAIC,cAAsB;GAC9C,IAAI,OAAO,MAAM,MAAM;GACvB,SAAS,UAAU;GACnB,UAAU,MAAM,WAAW;GAC3B,QAAQ,MAAM;GACd,cAAc;GACd,SAAS,OAAOC,cAAuB,MAAM,YAAY,MAAM,OAAO,OAAO;GAC7E,OAAO,MAAM,OAAO,SAAS;GAC7B,YAAY;GACZ,WAAW,UAAU;GACrB,WAAW,OAAO,SAAS;EAC7B,CAAC;EACD,OAAO,QAAQ,eAAe,OAAO;GACnC;GACA,YAAY,OAAO,KAAK,MAAM,MAAM;EACtC,CAAC;EAED,OAAO;GAAE,YAAA,OADiB,QAAQ,YAAY,OAAO,cAAc,EAAE;GAChD;EAAQ;CAC/B,CAAC;;;;;CAMH,MAAM,eAAe,UAOnB,OAAO,IAAI,aAAa;EACtB,MAAM,YAAY,OAAOH;EAEzB,MAAM,OAAO,OADG,MAAM,WAAW,QAAQ,MAAM,MACrB,CAAC,CAAC,aAAa;EACzC,MAAM,UAAU,MAAM,qBAAqB,QAAQ,eAAe,CAAC,KAAK,SAAS,UAAU,CAAC;EAC5F,IAAI,QAAQ,SAAS,GACnB,OAAO,OAAOT,KACZ,IAAIU,UAAiB,EACnB,SAAS,GAAG,MAAM,WAAW,KAAK,oBAAoB,QAAQ,KAAK,IAAI,IACzE,CAAC,CACH;EAEF,MAAM,gBAAgB,IAAIC,cAAsB;GAC9C,IAAI,MAAM;GACV,SAAS,UAAU;GACnB,UAAU,MAAM,WAAW;GAC3B,QAAQ,MAAM;GACd,cAAc;GACd,SAAS,OAAOC,cAAuB,MAAM,YAAY,MAAM,OAAO,OAAO;GAC7E,OAAO,MAAM,OAAO,SAAS;GAC7B,YAAY;GACZ,WAAW,UAAU;GACrB,WAAW,OAAO,SAAS;EAC7B,CAAC;EACD,OAAO,QAAQ,eAAe,OAAO;GACnC;GACA,YAAY,OAAO,KAAK,MAAM,MAAM;GACpC,YAAY,MAAM;EACpB,CAAC;CACH,CAAC;CAEH,MAAM,cAAc,UAOlB,OAAO,IAAI,aAAa;EACtB,MAAM,SAAS,OAAOC,OAAkB,MAAM,MAAM;EACpD,MAAM,WAAW,OAAO,QAAQ,YAAY,SAAS,MAAM;EAC3D,IAAI,OAAO,OAAO,QAAQ,GAAG;GAC3B,IAAI,SAAS,MAAM,iBAAiB,MAAM,WAAW,IAAI,OAAO,SAAS;GACzE,OAAO,OAAO,QAAQ,GAAG,OAAO,6CAA6C,QAAQ;EACvF;EACA,MAAM,aACJ,MAAM,WAAW,KAAA,IACb,OAAO,MAAM,QAAQ,cAAc,MAAM,IAAA,WACrB,SAAS,EAAE,QAAQ,MAAM,OAAO,CAAC;EAC3D,QAAQ,WAAW,MAAnB;GACE,KAAK,qBACH,OAAO,UAAU,kBAAkB,EAAE,YAAY,WAAW,WAAW,CAAC;GAC1E,KAAK,YACH,OAAO,OAAOb,KAAY,IAAIc,SAAgB;IAAE,QAAQ;IAAQ,IAAI;GAAO,CAAC,CAAC;GAC/E,KAAK,YACH,OAAO,OAAO,QAAQ,YAAY,OAAO;IACvC,cAAc,MAAM,WAAW;IAC/B;IACA,MAAM,WAAW,OAAO;IACxB,OAAO,WAAW,OAAO;IACzB,QAAQ;KACN,GAAG,WAAW;KACd,SAAS,OAAOF,cAAuB,MAAM,YAAY,WAAW,OAAO,OAAO;IACpF;GACF,CAAC;EACL;CACF,CAAC;CAEH,MAAM,WACJ,YACA,aAEA,aAAa,OACT,QAAQ,UAAU;EAAE;EAAY,YAAY;CAAK,CAAC,IAClD,oBAAoBG,aAClB,QAAQ,UAAU;EAAE;EAAY,YAAY;CAAS,CAAC,IACtD,QAAQ,kBAAkB;EAAE;EAAY,YAAY,SAAS;CAAW,CAAC;;CAGjF,MAAM,iBAAiB,eACrB,OAAO,IAAI,aAAa;EACtB,KAAK,MAAM,QAAQ,YAAY;GAE7B,MAAM,SAAQ,OADU,SAAS,QAAQ,MAAM,IAAI,EAAA,CAC5B,SAAS,YAC9B,QAAQ,SAAS,aACb,QAAQ,OAAO,QAAQ,SAAS,WAC9B,OAAO,SAAS,OAAO,CAAC,OAAO,UAAU,IAAI,CAAC,CAChD,IACA,CAAC,CACP;GACA,IAAI,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK;EACxD;EACA,OAAO,CAAC;CACV,CAAC;CAEH,MAAM,YAAmC,UACvC,OAAO,IAAI,aAAa;EACtB,MAAM,SAAS,OAAOF,OAAkB,KAAK;EAC7C,MAAMG,eAAaC,WAAsB,MAAM;EAC/C,MAAM,cAAc,OAAO,cAAcD,YAAU;EACnD,MAAM,YAGD,CAAC;EACN,KAAK,MAAM,cAAc,OAAO,QAAQ,YAAY,KAAK,GAAG;GAC1D,MAAM,UAAU,OAAO,WAAW,UAAU,CAAC,CAAC,KAC5C,OAAO,SAAS,YAAY,QAAQ,YAAY,CAAC,GACjD,OAAO,YAAY,OAAO,QAAQ,CAAC,CAAmC,CAAC,CACzE;GACA,KAAK,MAAM,UAAU,SACnB,IAAIA,aAAW,SAAS,OAAO,IAAwB,GACrD,UAAU,KAAK;IAAE,cAAc,WAAW;IAAI;GAAO,CAAC;EAG5D;EACA,KAAK,MAAM,QAAQA,cAAY;GAC7B,MAAM,UAAU,UACb,QAAQ,EAAE,aAAa,OAAO,SAAS,IAAI,CAAC,CAC5C,MAAM,MAAM,UAAU,KAAK,aAAa,cAAc,MAAM,YAAY,CAAC;GAC5E,IAAI,QAAQ,WAAW,GAAG;GAC1B,MAAM,CAAC,QAAQ;GACf,IAAI,QAAQ,WAAW,KAAK,SAAS,KAAA,GAAW,OAAO,UAAU,SAAS,IAAI;GAC9E,MAAM,WAAW,QAAQ,QAAQ,EAAE,aAAa;IAC9C,MAAM,aAAa,IAAI,KAAK,OAAO,eAAe,CAAC,EAAA,CAAG,IAAI,SAAS,CAAC;IACpE,OAAO,YAAY,SAAS,KAAK,YAAY,OAAO,SAAS,WAAW,IAAI,IAAI,CAAC;GACnF,CAAC;GACD,MAAM,CAAC,UAAU;GACjB,IAAI,SAAS,WAAW,KAAK,WAAW,KAAA,GAAW,OAAO,UAAU,SAAS,MAAM;GACnF,OAAO,UAAU,kBAAkB,EAAE,YAAY,QAAQ,CAAC;EAC5D;EACA,OAAO,UAAU,SAAS;GAAE;GAAa,MAAM,OAAO,WAAW;EAAE,CAAC;CACtE,CAAC;;CAGH,MAAM,UAAU,gBAA6E;EAC3F,IAAI,YAAY,WAAW,GAAG,OAAO;EACrC,MAAM,QAAQ,YAAY,IAAI,SAAS;EACvC,MAAM,QAAQ,UAAU,KAAK,CAAC,CAAC,QAAQ,eAAe;GACpD,MAAM,YAAY,WAAW,eAAe,CAAC,EAAA,CAAG,IAAI,SAAS;GAC7D,OACE,SAAS,SAAS,KAClB,MAAM,OAAO,SACX,SAAS,MAAM,WAAW,SAAS,UAAU,KAAK,SAAS,IAAI,QAAQ,CAAC,CAC1E;EAEJ,CAAC;EACD,MAAM,CAAC,QAAQ;EACf,OAAO,MAAM,WAAW,KAAK,SAAS,KAAA,IAAY,EAAE,UAAU,KAAK,GAAG,IAAI;CAC5E;CAEA,MAAM,SAA6B,UAAU,CAAC,MAC5C,OAAO,IAAI,aAAa;EACtB,MAAM,YAAyB,CAAC;EAChC,MAAM,cAAqC,CAAC;EAC5C,MAAM,SAAS,QAAQ,aAAa,KAAA,IAAY,KAAA,IAAY,EAAE,UAAU,QAAQ,SAAS;EACzF,KAAK,MAAM,cAAc,OAAO,QAAQ,YAAY,KAAK,MAAM,GAAG;GAChE,MAAM,gBAAgB,OAAO,QAAQ,eAAe,IAAI,WAAW,eAAe;GAKlF,MAAM,UAAU,OAAO,WAAW,UAAU,CAAC,CAAC,KAC5C,OAAO,SAAS,YAAY,QAAQ,YAAY,CAAC,GACjD,OAAO,IAAI,OAAO,IAAI,GACtB,OAAO,SACJ,UACC,MAAM,OAAO,SAAS,eACtB,MAAM,OAAO,SAAS,qBACtB,MAAM,OAAO,SAAS,mBAClB,OAAO,QAAQ,OAAO,KAAqC,CAAC,CACpE,CACF;GACA,YAAY,KAAK;IACf,cAAc,WAAW;IACzB,UAAU,cAAc;IACxB,OAAO,cAAc;IACrB,QAAQ,OAAO,OAAO,OAAO,IAAI,cAAc;GACjD,CAAC;GACD,IAAI,OAAO,OAAO,OAAO,GAAG;GAC5B,KAAK,MAAM,UAAU,QAAQ,OAC3B,UAAU,KAAK;IACb,cAAc,WAAW;IACzB,UAAU,cAAc;IACxB;GACF,CAAC;EAEL;EACA,OAAO;GACL,OAAO,UAAU,MACd,MAAM,UACL,KAAK,OAAO,KAAK,cAAc,MAAM,OAAO,IAAI,KAChD,KAAK,aAAa,cAAc,MAAM,YAAY,CACtD;GACA;GACA,WAAW,UAAU,KAAK,CAAC,CAAC,KAAK,gBAAgB;IAC/C,IAAI,WAAW;IACf,MAAM,WAAW;IACjB,SAASE,gBAAyB,UAAU;GAC9C,EAAE;EACJ;CACF,CAAC;CAEH,MAAM,WAAiC,UACrC,OAAO,IAAI,aAAa;EACtB,MAAM,SAAS,OAAOL,OAAkB,KAAK;EAC7C,MAAM,SAAS,OAAO;EACtB,MAAM,aAAa,OAAO,UAAU,OAAO,QAAQ,YAAY,SAAS,MAAM,CAAC;EAC/E,MAAM,aACJ,eAAe,OAAO,OAAO,OAAO,QAAQ,YAAY,IAAI,WAAW,YAAY;EACrF,MAAM,gBACJ,eAAe,OAAO,OAAO,OAAO,QAAQ,eAAe,IAAI,WAAW,eAAe;EAC3F,MAAM,SACJ,eAAe,OACX,OAAO,KAAK,IACZ,OAAO,QAAQ,SAAS,OAAO,WAAW,IAAI,cAAc;EAElE,MAAM,WAAW,eAAe,OAAO,CAAC,IAAI,OAAO,QAAQ,YAAY,KAAK,WAAW,EAAE;EACzF,MAAM,WAAgD,CAAC;EACvD,KAAK,MAAM,aAAa,OAAO,QAAQ,YAAY,KAAK,GAAG;GACzD,IAAI,UAAU,OAAO,YAAY,IAAI;GACrC,IAAI,EAAE,OAAO,OAAO,WAAW;IAAE,YAAY;IAAW;GAAO,CAAC,IAAI;GACpE,MAAM,yBAAyB,OAAO,QAAQ,eAAe,IAAI,UAAU,eAAe;GAC1F,IAAI,uBAAuB,eAAe,UAAU;GACpD,SAAS,KAAK;IACZ,YAAY;IACZ,UAAU,uBAAuB;IACjC,QAAQ,uBAAuB;GACjC,CAAC;EACH;EACA,OAAO;GACL;GACA;GACA;GACA,eACE,kBAAkB,OACd,OACA;IACE,IAAI,cAAc;IAClB,UAAU,cAAc;IACxB,QAAQ,cAAc;IACtB,cAAc,cAAc;IAC5B,YAAY,cAAc;IAC1B,OAAO,cAAc;GACvB;GACN,eAAe,OAAO,OAAO,MAAM,IAAK,OAAO,MAAM,SAAS,MAAM,OAAQ;GAC5E,mBAAmB,SAAS;GAC5B;GACA,WAAW,UAAU,KAAK,CAAC,CAAC,KAAK,gBAAgB;IAC/C,IAAI,WAAW;IACf,MAAM,WAAW;IACjB,SAASK,gBAAyB,UAAU;GAC9C,EAAE;EACJ;CACF,CAAC;CAEH,MAAM,SAA6B,UACjC,OAAO,IAAI,aAAa;EACtB,OAAO;EACP,MAAM,aAAa,OAAO,UAAU,IAAI,MAAM,QAAQ;EACtD,IAAI,MAAM,WAAW,KAAA,GAAW,OAAOL,OAAkB,MAAM,MAAM;EACrE,QAAQ,MAAM,OAAO,MAArB;GACE,KAAK,SAAS;IACZ,MAAM,QAAQ,WAAW,KAAK;IAC9B,IAAI,UAAU,KAAA,GACZ,OAAO,OAAO,QAAQ,GAAG,WAAW,KAAK,0BAA0B,QAAQ;IAE7E,MAAM,SAAS,OAAO,MAAM,aAAa,MAAM,OAAO,MAAM;IAC5D,MAAM,EAAE,YAAY,YAAY,OAAO,YAAY;KACjD;KACA,QAAQ;KACR;KACA,sBAAsB,MAAM;IAC9B,CAAC;IACD,MAAM,WACJ,MAAM,WAAW,KAAA,IACb,OACA,OAAO,WAAW;KAAE;KAAY;KAAY;KAAS,QAAQ,MAAM;IAAO,CAAC;IACjF,OAAO,QAAQ,YAAY,QAAQ;GACrC;GACA,KAAK;GACL,KAAK,eACH,OAAO,OAAO,YAAY;IACxB;IACA,QAAQ,MAAM;IACd,aAAa,MAAM;IACnB,QAAQ,MAAM,UAAU;IACxB,cAAc;GAChB,CAAC;EACL;CACF,CAAC;;CAGH,MAAM,eAAe,UAOnB,OAAO,IAAI,aAAa;EACtB,MAAM,YAAY,OAAOJ;EACzB,MAAM,SAAS,OAAO;EACtB,MAAM,cACJ,MAAM,OAAO,SAAS,UAClB,MAAM,WAAW,KAAK,QACtB,MAAM,WAAW,KAAK;EAC5B,IAAI,gBAAgB,KAAA,GAClB,OAAO,OAAO,QACZ,GAAG,MAAM,WAAW,KAAK,kBAAkB,MAAM,OAAO,KAAK,YAAY,EAAE,eAC3E,QACF;EAEF,IAAI,MAAM,gBAAgB,KAAA,GACxB,OAAO,OAAO,QAAQ,8CAA8C,aAAa;EAEnF,MAAM,cAAc,MAAM;EAC1B,MAAM,iBAAiB,OAAO,MAAM,MAAM;EAC1C,MAAMU,SAAO,MAAM,OAAO,SAAS,UAAU,OAAOC,KAAW,IAAI;EACnE,MAAM,WACJ,MAAM,OAAO,SAAS,UAClB,OAAQ,YAAmC,MAAM;GAC/C,OAAO;GACP;GACA,eAAeD,QAAM,iBAAiB;EACxC,CAAC,IACD,OAAQ,YAAyC,MAAM;GACrD,OAAO;GACP;EACF,CAAC;EACP,OAAO,QAAQ,cAAc,IAC3B,IAAIE,aAAqB;GACvB,IAAI;GACJ,SAAS,UAAU;GACnB,SAAS,UAAU;GACnB,UAAU,MAAM,WAAW;GAC3B,SAAS,OAAO,WAAW,OAAO,CAAC,CAAC;IAClC,QAAQ,MAAM,OAAO,SAAS,UAAU,UAAU;IAClD,cAAcF,QAAM,gBAAgB;IACpC;IACA,QAAQ,MAAM;IACd,cAAc,MAAM;GACtB,CAAC;GACD,UAAU,MAAM,OAAO,YAAY;GACnC,WAAW,SAAS,YAClB,OAAO,SAAS,KAChB,SAAS,OAAO,OAAO,iBAAiB,CAC1C;EACF,CAAC,CACH;EACA,OAAO,QAAQ,SAAS;GAAE,kBAAkB,SAAS;GAAkB;EAAe,CAAC;CACzF,CAAC;CAEH,MAAM,aAAqC,UACzC,OAAO,IAAI,aAAa;EACtB,OAAO;EACP,MAAM,aAAa,OAAO,QAAQ,YAAY,IAAI,MAAM,YAAY;EACpE,MAAM,WAAW,OAAO,QAAQ,eAAe,IAAI,WAAW,eAAe;EAC7E,MAAM,aAAa,OAAO,UAAU,IAAI,SAAS,QAAQ;EACzD,QAAQ,MAAM,OAAO,MAArB;GACE,KAAK,SAAS;IACZ,MAAM,QAAQ,WAAW,KAAK;IAC9B,IAAI,UAAU,KAAA,GACZ,OAAO,OAAO,QAAQ,GAAG,WAAW,KAAK,0BAA0B,QAAQ;IAE7E,MAAM,SAAS,OAAO,MAAM,aAAa,MAAM,OAAO,MAAM;IAC5D,OAAO,YAAY;KACjB;KACA,iBAAiB,SAAS;KAC1B,QAAQ;KACR;KACA,sBAAsB,MAAM;IAC9B,CAAC;IACD,OAAO,QAAQ,UAAU;KAAE;KAAY,YAAY;IAAK,CAAC;GAC3D;GACA,KAAK;GACL,KAAK,eACH,OAAO,OAAO,YAAY;IACxB;IACA,QAAQ,MAAM;IACd,aAAa,MAAM;IACnB,QAAQ;IACR,cAAc,WAAW;GAC3B,CAAC;EACL;CACF,CAAC;CAEH,MAAM,mBAAiD,UACrD,QAAQ,SACN,gBAAgB,MAAM,kBACtB,OAAO,IAAI,aAAa;EACtB,MAAM,eAAe,OAAO,QAAQ,cAAc,IAAI,MAAM,cAAc;EAC1E,MAAM,YAAY,OAAOV;EACzB,IACE,aAAa,YAAY,UAAU,WACnC,aAAa,YAAY,UAAU,WAClC,MAAM,aAAa,KAAA,KAAa,aAAa,aAAa,MAAM,UAEjE,OAAO,OAAOa;EAEhB,MAAM,WAAW,OAAO,OAAO,IAAI;GACjC,WAAW,IAAI,IAAI,MAAM,WAAW;GACpC,aACE,IAAIC,eAAsB,EACxB,QAAQ,IAAItB,aAAoB;IAC9B,SAAS;IACT,OAAO;GACT,CAAC,EACH,CAAC;EACL,CAAC;EAED,MAAM,UAAU,OAAOuB,SAAc,SAAS,aAAa,SAAS,cAAc,CAAC,CAAC,KAClF,OAAO,MACT;EACA,MAAM,eACJ,QAAQ,SAAS,aACjB,QAAQ,QAAQ,iBAAiB,QACjC,QAAQ,QAAQ,aAAa,SAAS,IAClC,EAAE,cAAc,QAAQ,QAAQ,aAAa,IAC7C,CAAC;EACP,MAAM,SAAS,OAAO,YAAY,SAAS,YAAY;EACvD,MAAM,UAAU,YACd,WAAW,OAAO;GAChB,OAAO,IAAID,eAAsB,EAAE,QAAQ,IAAIE,gBAAuB,EAAE,QAAQ,CAAC,EAAE,CAAC;GACpF,UAAU;GACV,GAAG;EACL,CAAC;EAEH,IAAI,OAAO,UAAU,MAAM,gBACzB,OAAO,OAAO,gDAAgD;EAChE,IAAI,OAAO,UAAU,KAAA,GAAW;GAC9B,MAAM,UAAU,OAAO,qBAAqB,OAAO,OAAO;GAE1D,OAAO,OAAO,UAAU,kBACpB,WAAW,UAAU;IAAE,OAAO,QAAQ;IAAO,GAAG;GAAa,CAAC,IAC9D;EACN;EACA,MAAM,OAAO,OAAO;EACpB,IAAI,SAAS,KAAA,GAAW,OAAO,OAAO,sBAAsB;EAE5D,IAAI,aAAa;EACjB,OAAO,OAAO,OAAO,IAAI,aAAa;GACpC,IAAI,QAAQ,SAAS,WAAW,OAAO,OAAO,OAAO,KAAK,QAAQ,OAAO;GACzE,MAAM,UAAU,QAAQ;GACxB,MAAM,aAAa,OAAO,UAAU,IAAI,aAAa,QAAQ;GAC7D,MAAM,SACJ,QAAQ,WAAW,UACf,OAAO,OAAO,IAAI,aAAa;IAC7B,MAAM,QAAQ,WAAW,KAAK;IAC9B,IAAI,UAAU,KAAA,KAAa,QAAQ,iBAAiB,MAClD,OAAO,OAAO,QACZ,GAAG,WAAW,KAAK,oCACnB,QACF;IAEF,OAAO,OAAO,MAAM,SAAS;KAC3B;KACA,aAAa,QAAQ;KACrB,cAAc,QAAQ;KACtB;IACF,CAAC;GACH,CAAC,IACD,OAAO,OAAO,IAAI,aAAa;IAC7B,MAAM,cAAc,WAAW,KAAK;IACpC,IAAI,gBAAgB,KAAA,GAClB,OAAO,OAAO,QACZ,GAAG,WAAW,KAAK,0CACnB,QACF;IAEF,OAAO,OAAO,YAAY,SAAS;KACjC;KACA,aAAa,QAAQ;KACrB;IACF,CAAC;GACH,CAAC;GACP,aAAa;GACb,IAAI,QAAQ,iBAAiB,MAAM;IACjC,MAAM,OAAO,OAAO,QAAQ,YAAY,IAAI,QAAQ,YAAY;IAChE,MAAM,WAAW,OAAO,QAAQ,eAAe,IAAI,KAAK,eAAe;IACvE,OAAO,YAAY;KACjB;KACA,iBAAiB,SAAS;KAC1B,QAAQ,QAAQ;KAChB;KACA,sBAAsB,CAAC;IACzB,CAAC;IACD,OAAO,QAAQ,cAAc,QAAQ,MAAM,cAAc,CAAC,CAAC,KAAK,OAAO,MAAM;IAC7E,OAAO,QAAQ,UAAU;KAAE,YAAY;KAAM,YAAY;IAAK,CAAC;GACjE;GACA,MAAM,EAAE,YAAY,YAAY,OAAO,YAAY;IACjD;IACA,QAAQ,QAAQ;IAChB;IACA,sBAAsB,CAAC;GACzB,CAAC;GACD,MAAM,WACJ,QAAQ,WAAW,OACf,OACA,OAAO,WAAW;IAAE;IAAY;IAAY;IAAS,QAAQ,QAAQ;GAAO,CAAC;GAKnF,OAAO,QAAQ,cAAc,QAAQ,MAAM,cAAc,CAAC,CAAC,KAAK,OAAO,MAAM;GAC7E,OAAO,QAAQ,YAAY,QAAQ;EACrC,CAAC,CAAC,CAAC,KACD,OAAO,KAAK,WAAW,WAAW,UAAU,EAAE,SAAS,OAAO,CAAC,CAAC,GAChE,OAAO,OAAO,UACZ,OAAO,QACL,WAAW,OAAO;GAChB;GACA,GAAG;GACH,UACE,CAAC,cAAc,MAAM,OAAO,SAAS,oBAAoB,YAAY;EACzE,CAAC,CACH,CACF,CACF;CACF,CAAC,CACH;CAEF,MAAM,YAAmC,UACvC,gBAAgB,KAAK,CAAC,CAAC,KACrB,OAAO,SAAS,YACd,QAAQ,SAAS,cAAc,OAAO,QAAQ,QAAQ,OAAO,IAAI,OAAO,KAAK,QAAQ,KAAK,CAC5F,CACF;CAEF,MAAM,UAA+B,UACnC,OAAO,IAAI,aAAa;EACtB,MAAM,aAAa,OAAO,QAAQ,YAAY,IAAI,MAAM,YAAY;EACpE,MAAM,gBAAgB,OAAO,QAAQ,eAAe,IAAI,WAAW,eAAe;EAClF,MAAM,aAAa,OAAO,UAAU,IAAI,cAAc,QAAQ;EAC9D,MAAM,UAAU,OAAO,WAAW,UAAU;EAC5C,OAAO,OAAO,WAAW;GACvB;GACA;GACA;GACA,QAAQ,MAAM;GACd,GAAI,MAAM,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,OAAO;EAC/D,CAAC;CACH,CAAC;CAEH,MAAM,cAAuC,iBAC3C,OAAO,IAAI,aAAa;EACtB,OAAO;EACP,MAAM,aAAa,OAAO,QAAQ,YAAY,IAAI,YAAY;EAC9D,MAAM,cAAc,OAAO,QAAQ,YAAY,KAAK,YAAY;EAChE,OAAO,OAAO,QAAQ,cAAc,eAAe,QAAQ,YAAY,OAAO,WAAW,EAAE,CAAC;EAC5F,OAAO,QAAQ,YAAY,OAAO,YAAY;EAI9C,KAHmB,OAAO,QAAQ,YAAY,KAAK,EAAA,CAAG,MACnD,cAAc,UAAU,oBAAoB,WAAW,eAE9C,GAAG;EACf,MAAM,gBAAgB,OAAO,QAAQ,eAAe,IAAI,WAAW,eAAe;EAClF,OAAO,QAAQ,eAAe,OAAO,cAAc,IAAI,SAAS,aAAa,CAAC;CAChF,CAAC;CAEH,MAAM,WAAiC,iBACrC,OAAO,IAAI,aAAa;EACtB,MAAM,aAAa,OAAO,QAAQ,YAAY,IAAI,YAAY;EAC9D,MAAM,aAAa,OAAO,QAAQ,YAAY,IAAI,WAAW,YAAY;EACzE,MAAM,gBAAgB,OAAO,QAAQ,eAAe,IAAI,WAAW,eAAe;EAClF,MAAM,aAAa,OAAO,UAAU,IAAI,cAAc,QAAQ;EAC9D,MAAM,SAAS,OAAOD,SAAc,QAAQ,WAAW,QAAQ,QAAQ;EACvE,MAAM,SAA0B;GAC9B,GAAG;GACH,SAAS,OAAOjB,cAAuB,YAAY,OAAO,OAAO;EACnE;EACA,MAAM,QAAQ,OAAO,WAAW,UAAU;EAI1C,IAAI,EAAC,OADoB,MAAM,YAAY,EAAA,CAC5B,MAAM,cAAc,UAAU,SAAS,OAAO,IAAI,GAC/D,OAAO,OAAOP,KAAY,IAAIc,SAAgB;GAAE,QAAQ;GAAQ,IAAI,OAAO;EAAK,CAAC,CAAC;EAEpF,OAAO;GAAE,SAAS;GAAO;EAAO;CAClC,CAAC;CAEH,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,SAAS,iBAAiB,QAAQ,YAAY,OAAO,YAAY;EACjE;EACA;EACA;CACF;AACF,CAAC;AAED,MAAa,QAIT,MAAM,OAAO,OAAO,CAAC,CAAC,IAAI;AAE9B,MAAM,YAEF,UAED,GAAG,SACF,OAAO,QAAQ,UAAU,YAAY,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;AAE/D,MAAa,UAAU,UAAU,YAAY,QAAQ,OAAO;AAC5D,MAAa,QAAQ,UAAU,YAAY,QAAQ,KAAK;AACxD,MAAa,WAAW,UAAU,YAAY,QAAQ,QAAQ;AAC9D,MAAa,QAAQ,UAAU,YAAY,QAAQ,KAAK;AACxD,MAAa,YAAY,UAAU,YAAY,QAAQ,SAAS;AAChE,MAAa,WAAW,UAAU,YAAY,QAAQ,QAAQ;AAC9D,MAAa,kBAAkB,UAAU,YAAY,QAAQ,eAAe;AAC5E,MAAa,SAAS,UAAU,YAAY,QAAQ,MAAM;AAC1D,MAAa,SAAS,UAAU,YAAY,QAAQ,MAAM;AAC1D,MAAa,aAAa,UAAU,YAAY,QAAQ,UAAU;AAClE,MAAa,UAAU,UAAU,YAAY,QAAQ,OAAO;AAC5D,MAAa,UAAU,UAAU,YAAY,QAAQ,OAAO"}
|
package/dist/Server.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { Digest, Model as Model$1, OperationId, PlanId } from "./Plan.mjs";
|
|
|
4
4
|
import { InvalidInput, NotFound, Unsupported } from "./Reason.mjs";
|
|
5
5
|
import { DomainKitError, fail } from "./internal/error.mjs";
|
|
6
6
|
import { ApprovalId, Model as Model$2 } from "./Approval.mjs";
|
|
7
|
+
import { write } from "./CallbackRecovery.mjs";
|
|
7
8
|
import { missing, refuse } from "./internal/callback.mjs";
|
|
8
9
|
import { Service } from "./Principal.mjs";
|
|
9
10
|
import { Service as Service$1 } from "./Providers.mjs";
|
|
@@ -449,11 +450,13 @@ const CallbackOutcome = Schema.Union([
|
|
|
449
450
|
Schema.Struct({
|
|
450
451
|
_tag: Schema.Literal("Cancelled"),
|
|
451
452
|
provider: Schema.String,
|
|
453
|
+
connectionId: Schema.optionalKey(Schema.String.check(Schema.isMinLength(1))),
|
|
452
454
|
returnTo: Schema.String
|
|
453
455
|
}),
|
|
454
456
|
Schema.Struct({
|
|
455
457
|
_tag: Schema.Literal("Failed"),
|
|
456
458
|
provider: Schema.String,
|
|
459
|
+
connectionId: Schema.optionalKey(Schema.String.check(Schema.isMinLength(1))),
|
|
457
460
|
returnTo: Schema.String,
|
|
458
461
|
recovery: Schema.Literals(["restart", "inspect"])
|
|
459
462
|
})
|
|
@@ -779,17 +782,32 @@ const layer = (api, options = {}) => HttpApiBuilder.group(api, "domainkit", (han
|
|
|
779
782
|
} : outcome._tag === "Cancelled" ? {
|
|
780
783
|
_tag: "Cancelled",
|
|
781
784
|
provider: flow.provider,
|
|
782
|
-
returnTo: destination
|
|
785
|
+
returnTo: destination,
|
|
786
|
+
...outcome.connectionId === void 0 ? {} : { connectionId: outcome.connectionId }
|
|
783
787
|
} : {
|
|
784
788
|
_tag: "Failed",
|
|
785
789
|
provider: flow.provider,
|
|
786
790
|
returnTo: destination,
|
|
787
|
-
recovery: outcome.recovery
|
|
791
|
+
recovery: outcome.recovery,
|
|
792
|
+
...outcome.connectionId === void 0 ? {} : { connectionId: outcome.connectionId }
|
|
788
793
|
};
|
|
794
|
+
if (outcome._tag !== "Connected") {
|
|
795
|
+
const attributes = {
|
|
796
|
+
"domainkit.callback.outcome": outcome._tag,
|
|
797
|
+
"domainkit.callback.provider": flow.provider,
|
|
798
|
+
"domainkit.callback.reason": outcome.error.reason._tag,
|
|
799
|
+
...outcome._tag === "Failed" ? { "domainkit.callback.recovery": outcome.recovery } : {}
|
|
800
|
+
};
|
|
801
|
+
yield* Effect.annotateCurrentSpan(attributes);
|
|
802
|
+
yield* (outcome._tag === "Cancelled" ? Effect.logInfo : Effect.logWarning)("DomainKit callback recovery").pipe(Effect.annotateLogs(attributes));
|
|
803
|
+
}
|
|
789
804
|
const selected = options.callback === void 0 ? void 0 : yield* options.callback(summary);
|
|
790
|
-
const location = selected
|
|
805
|
+
const location = selected !== void 0 ? sameOrigin(selected, base) : summary._tag === "Connected" ? write(new URL(destination), null).toString() : write(new URL(destination), {
|
|
806
|
+
kind: summary._tag === "Cancelled" ? "cancelled" : summary.recovery,
|
|
807
|
+
provider: summary.provider,
|
|
808
|
+
...summary.connectionId === void 0 ? {} : { connectionId: summary.connectionId }
|
|
809
|
+
}).toString();
|
|
791
810
|
if (location === null) return yield* invalid("callback destination leaves this application", "callback");
|
|
792
|
-
if (selected === void 0 && outcome._tag !== "Connected") return yield* Effect.fail(outcome.error);
|
|
793
811
|
return HttpApiSchema.withHeaders({
|
|
794
812
|
body: void 0,
|
|
795
813
|
headers: { location }
|
package/dist/Server.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Server.mjs","names":["Storage.AuthMethod","DnsRecord.Model","Plan.OperationId","Plan.Model","Storage.AttemptStatus","Approval.Model","Receipt.Model","Storage.Rejection","Plan.Digest","Storage.BatchId","Storage.BatchStatus","Storage.BatchApproval","Storage.Overall","Storage.RequirementStatus","Verify.Evidence","Verify.HostEvidence","Plan.PlanId","Approval.ApprovalId","Receipt.ReceiptId","Configuration.endpoint","Errors.fail","Reason.Unsupported","Configuration.scopes","Configuration.requireValue","Reason.InvalidInput","Callback.refuse","Connect.Service","Provision.Service","Cleanup.Service","Verify.Service","Storage.Service","Providers.Service","Principal.Service","Reason.NotFound","Callback.missing"],"sources":["../src/Server.ts"],"sourcesContent":["/**\n * domainkit/server — the whole lifecycle as one HttpApi group the host mounts.\n *\n * const Api = HttpApi.make(\"app\").add(Server.group)\n * const ApiLive = HttpApiBuilder.layer(Api).pipe(\n * Layer.provide([Server.layer(Api), DomainKitLive, IdentityLive]),\n * )\n *\n * The host provides `Identity` (request -> `Principal.Interface`). Everything else comes from\n * `DomainKit.layer`. Non-HttpApi hosts use `toWebHandler`. The group carries no prefix of its\n * own, so `group.prefix(\"/internal/dns\")` or a host router mount is all a different base path\n * takes; callback URLs follow the mount.\n */\nimport { Context, Effect, Layer, Option, Schema } from \"effect\";\nimport * as HttpRouter from \"effect/http/HttpRouter\";\nimport * as HttpServer from \"effect/http/HttpServer\";\nimport * as HttpServerRequest from \"effect/http/HttpServerRequest\";\nimport * as HttpApi from \"effect/http-api/HttpApi\";\nimport * as HttpApiBuilder from \"effect/http-api/HttpApiBuilder\";\nimport * as HttpApiEndpoint from \"effect/http-api/HttpApiEndpoint\";\nimport * as HttpApiGroup from \"effect/http-api/HttpApiGroup\";\nimport * as HttpApiSchema from \"effect/http-api/HttpApiSchema\";\n\nimport * as Approval from \"./Approval.ts\";\nimport * as Cleanup from \"./Cleanup.ts\";\nimport * as Connect from \"./Connect.ts\";\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Plan from \"./Plan.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Configuration from \"./internal/config.ts\";\nimport * as Callback from \"./internal/callback.ts\";\nimport type * as Provider from \"./Provider.ts\";\nimport * as Providers from \"./Providers.ts\";\nimport * as Provision from \"./Provision.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as Receipt from \"./Receipt.ts\";\nimport * as Storage from \"./Storage.ts\";\nimport * as Verify from \"./Verify.ts\";\n\n/**\n * What DomainKit already knows about the request when it asks the host who is making it. Only\n * `/callback/:provider` passes one, because it is the only route that points at a flow DomainKit\n * recorded earlier; every other route calls `principal` with the request alone.\n */\nexport interface IdentityContext {\n /** The route being served. */\n readonly endpoint: EndpointName;\n /** The flow this callback claims to finish, as DomainKit recorded it when the flow started. */\n readonly continuation: Storage.ContinuationHeader;\n}\n\n/** The only service a host must implement for the server. */\nexport interface IdentityService {\n /**\n * `context` is present only on the callback, where a host that keeps per-request tenancy can use\n * `context.continuation.ownerId` to pick the right tenant out of a session that holds several.\n * Ignoring it is safe everywhere: DomainKit checks the principal against the recorded flow on\n * that route regardless, so a host that answers with the wrong one is refused rather than\n * trusted.\n */\n readonly principal: (\n request: HttpServerRequest.HttpServerRequest,\n context?: IdentityContext,\n ) => Effect.Effect<Principal.Interface, Errors.DomainKitError>;\n /**\n * Which routes this principal may reach, checked after `principal` on every request. Fail with\n * reason `Forbidden` for the 403 a UI expects. Without it every authenticated principal reaches\n * every route, which is the right default for a host whose own middleware already gates the\n * mount.\n */\n readonly authorize?: (\n principal: Principal.Interface,\n endpoint: EndpointName,\n ) => Effect.Effect<void, Errors.DomainKitError>;\n}\n\nexport class Identity extends Context.Service<Identity, IdentityService>()(\n \"@domainkit/server/Identity\",\n) {}\n\n// ---------------------------------------------------------------------------------------------\n// Wire schemas (the transport contract `domainkit/client` and `@domainkit/react` consume)\n// ---------------------------------------------------------------------------------------------\n\nexport const ConnectionStatus = Schema.Literals([\"disconnected\", \"connected\", \"reconnect\"]);\nexport type ConnectionStatus = typeof ConnectionStatus.Type;\n\n/** One value a token method needs, so a form renders without knowing the provider. */\nexport const Field = Schema.Struct({\n name: Schema.String,\n required: Schema.Boolean,\n secret: Schema.Boolean,\n});\nexport type Field = typeof Field.Type;\n\n/** How a provider can be connected; `fields` is `null` for the interactive methods. */\nexport const MethodDescriptor = Schema.Struct({\n kind: Storage.AuthMethod,\n label: Schema.String,\n docsUrl: Schema.NullOr(Schema.String),\n fields: Schema.NullOr(Schema.Array(Field)),\n});\nexport type MethodDescriptor = typeof MethodDescriptor.Type;\n\n/** Everything the UI needs about a domain, flattened; provider context never crosses the wire. */\n/** The attachment a domain holds, with the zone label the provider gave when it was created. */\nexport const Attachment = Schema.Struct({\n id: Schema.String,\n label: Schema.String,\n});\nexport type Attachment = typeof Attachment.Type;\n\nexport const Snapshot = Schema.Struct({\n domain: Schema.String,\n attachment: Schema.NullOr(Attachment),\n connectionId: Schema.NullOr(Schema.String),\n provider: Schema.NullOr(Schema.String),\n method: Schema.NullOr(Storage.AuthMethod),\n status: ConnectionStatus,\n lastReceiptId: Schema.NullOr(Schema.String),\n /** How many domains the connection serves, including this one; `0` without a connection. */\n connectionDomains: Schema.Number,\n reusable: Schema.Array(\n Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n method: Storage.AuthMethod,\n }),\n ),\n providers: Schema.Array(\n Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n methods: Schema.Array(MethodDescriptor),\n }),\n ),\n});\nexport type Snapshot = typeof Snapshot.Type;\n\n/**\n * How the client asks to connect. `values` is keyed by the provider's declared token fields, as\n * `Snapshot.providers[].methods[].fields` names them. Secrets are plaintext in transit and never\n * come back out.\n */\nexport class Token extends Schema.TaggedClass<Token>(\"@domainkit/server/Method/Token\")(\"Token\", {\n values: Schema.Record(Schema.String, Schema.String),\n}) {}\nexport class OAuth extends Schema.TaggedClass<OAuth>(\"@domainkit/server/Method/OAuth\")(\"OAuth\", {\n returnTo: Schema.optionalKey(Schema.String),\n}) {}\nexport class Integration extends Schema.TaggedClass<Integration>(\n \"@domainkit/server/Method/Integration\",\n)(\"Integration\", { returnTo: Schema.optionalKey(Schema.String) }) {}\nexport const Method = Schema.Union([Token, OAuth, Integration]);\nexport type Method = typeof Method.Type;\n\n/** A start without a domain connects the account alone; the customer picks a zone afterwards. */\nexport const StartPayload = Schema.Struct({\n domain: Schema.optionalKey(Schema.String),\n provider: Schema.String,\n method: Method,\n});\nexport type StartPayload = typeof StartPayload.Type;\n\n/** How the customer proves the account again. The provider is the connection's own. */\nexport const ReconnectPayload = Schema.Struct({ method: Method });\nexport type ReconnectPayload = typeof ReconnectPayload.Type;\n\n/** A zone the connection can serve, named for a customer to pick from. */\nexport class Candidate extends Schema.Class<Candidate>(\"@domainkit/server/Candidate\")({\n zone: Schema.String,\n label: Schema.String,\n}) {}\n\n/**\n * The connection the start produced. `label` names the account the way the customer will read it:\n * the attached zone's label, or the provider's name when the start carried no domain. `snapshot`\n * is the domain's state, and is null for a start that attached none.\n */\nexport class Connected extends Schema.TaggedClass<Connected>(\"@domainkit/server/Started/Connected\")(\n \"Connected\",\n {\n connectionId: Schema.String,\n provider: Schema.String,\n label: Schema.String,\n snapshot: Schema.NullOr(Snapshot),\n },\n) {}\nexport class Redirect extends Schema.TaggedClass<Redirect>(\"@domainkit/server/Started/Redirect\")(\n \"Redirect\",\n { authorizationUrl: Schema.String },\n) {}\nexport class SelectionRequired extends Schema.TaggedClass<SelectionRequired>(\n \"@domainkit/server/Started/SelectionRequired\",\n)(\"SelectionRequired\", { connectionId: Schema.String, candidates: Schema.Array(Candidate) }) {}\nexport const Started = Schema.Union([Connected, Redirect, SelectionRequired]);\nexport type Started = typeof Started.Type;\n\n/**\n * Which of the principal's connections already reaches a domain. The class names carry a\n * `Discovery` prefix because `Started` owns the unprefixed `SelectionRequired`; the wire tags are\n * the ones `Connect.Discovery` uses.\n */\nexport class DiscoveryResolved extends Schema.TaggedClass<DiscoveryResolved>(\n \"@domainkit/server/Discovery/Resolved\",\n)(\"Resolved\", { connectionId: Schema.String, zone: Schema.String, label: Schema.String }) {}\nexport class DiscoverySelectionRequired extends Schema.TaggedClass<DiscoverySelectionRequired>(\n \"@domainkit/server/Discovery/SelectionRequired\",\n)(\"SelectionRequired\", {\n candidates: Schema.Array(\n Schema.Struct({ connectionId: Schema.String, zone: Schema.String, label: Schema.String }),\n ),\n}) {}\n/** `host` names the registered provider whose nameservers serve the domain, by definition id. */\nexport class DiscoveryNotFound extends Schema.TaggedClass<DiscoveryNotFound>(\n \"@domainkit/server/Discovery/NotFound\",\n)(\"NotFound\", {\n nameservers: Schema.Array(Schema.String),\n host: Schema.NullOr(Schema.Struct({ provider: Schema.String })),\n}) {}\nexport const Discovery = Schema.Union([\n DiscoveryResolved,\n DiscoverySelectionRequired,\n DiscoveryNotFound,\n]);\nexport type Discovery = typeof Discovery.Type;\n\n/** One zone a connection reaches, named for a customer to pick from. */\nexport const Zone = Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n zone: Schema.String,\n label: Schema.String,\n nameservers: Schema.optionalKey(Schema.Array(Schema.String)),\n});\nexport type Zone = typeof Zone.Type;\n\n/**\n * Every zone the principal's connections reach, and where each connection stands. A connection\n * marked `reconnect` contributes no zones and needs the customer to authorize it again.\n */\nexport const Zones = Schema.Struct({\n zones: Schema.Array(Zone),\n connections: Schema.Array(\n Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n /** What the provider called the account at connect time; `null` when it named none. */\n label: Schema.NullOr(Schema.String),\n status: ConnectionStatus,\n }),\n ),\n /** Every provider a customer could connect, so a picker offers one without a second call. */\n providers: Schema.Array(\n Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n methods: Schema.Array(MethodDescriptor),\n }),\n ),\n});\nexport type Zones = typeof Zones.Type;\n\nexport const AttachPayload = Schema.Struct({\n domain: Schema.String,\n /** Which candidate zone to attach when `SelectionRequired` came back. */\n zone: Schema.optionalKey(Schema.String),\n});\nexport type AttachPayload = typeof AttachPayload.Type;\n\nexport const PlanPayload = Schema.Struct({\n requirements: Schema.Array(DnsRecord.Model),\n});\nexport type PlanPayload = typeof PlanPayload.Type;\n\nexport const ObservePayload = Schema.Struct({\n /** Records to observe instead of the attachment's receipt; required for an unattached domain. */\n requirements: Schema.optionalKey(Schema.Array(DnsRecord.Model)),\n});\nexport type ObservePayload = typeof ObservePayload.Type;\n\nexport const ApprovePayload = Schema.Struct({\n /** A subset of the plan's writes; omit to approve every write. */\n operationIds: Schema.optionalKey(Schema.Array(Plan.OperationId)),\n});\nexport type ApprovePayload = typeof ApprovePayload.Type;\n\nexport const RejectPayload = Schema.Struct({\n /** Why the customer declined, for the audit trail. */\n reason: Schema.optionalKey(Schema.String),\n});\nexport type RejectPayload = typeof RejectPayload.Type;\n\n/** One durable plan -> approval -> receipt lifecycle, as `GET /plans/:planId` returns it. */\nexport const Attempt = Schema.Struct({\n plan: Plan.Model,\n status: Storage.AttemptStatus,\n approval: Schema.NullOr(Approval.Model),\n receipt: Schema.NullOr(Receipt.Model),\n rejection: Schema.NullOr(Storage.Rejection),\n});\nexport type Attempt = typeof Attempt.Type;\n\n/** What a batch's create and resume routes take: one domain and its requirements per item. */\nexport const BatchPayload = Schema.Struct({\n items: Schema.Array(\n Schema.Struct({ domain: Schema.String, requirements: Schema.Array(DnsRecord.Model) }),\n ),\n});\nexport type BatchPayload = typeof BatchPayload.Type;\n\n/** The digest the principal read, which the batch's current plans must still produce. */\nexport const BatchApprovePayload = Schema.Struct({ digest: Plan.Digest });\nexport type BatchApprovePayload = typeof BatchApprovePayload.Type;\n\n/** One domain's place in a batch, with whatever its attempt holds. */\nexport const BatchItem = Schema.Struct({\n attachmentId: Schema.String,\n position: Schema.Number,\n /** Null while the item has no plan; `planFailure` then says why. */\n plan: Schema.NullOr(Plan.Model),\n status: Schema.NullOr(Storage.AttemptStatus),\n approval: Schema.NullOr(Approval.Model),\n receipt: Schema.NullOr(Receipt.Model),\n rejection: Schema.NullOr(Storage.Rejection),\n failure: Schema.NullOr(Schema.String),\n planFailure: Schema.NullOr(Schema.String),\n});\nexport type BatchItem = typeof BatchItem.Type;\n\n/** Many domains planned together, as every batch route returns them. */\nexport const Batch = Schema.Struct({\n id: Storage.BatchId,\n status: Storage.BatchStatus,\n /** What `POST /batches/:batchId/approvals` takes; null while any item is unplanned. */\n digest: Schema.NullOr(Plan.Digest),\n approval: Schema.NullOr(Storage.BatchApproval),\n rejection: Schema.NullOr(Storage.Rejection),\n createdAt: Schema.DateTimeUtcFromString,\n updatedAt: Schema.DateTimeUtcFromString,\n completedAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n items: Schema.Array(BatchItem),\n});\nexport type Batch = typeof Batch.Type;\n\n/** A batch without its plans, as the unfinished index returns them. */\nexport const BatchSummary = Schema.Struct({\n id: Storage.BatchId,\n status: Storage.BatchStatus,\n itemCount: Schema.Number,\n createdAt: Schema.DateTimeUtcFromString,\n updatedAt: Schema.DateTimeUtcFromString,\n completedAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n});\nexport type BatchSummary = typeof BatchSummary.Type;\n\n/** `Verify.Readiness` on the wire; timestamps encode as ISO strings. */\nexport const Readiness = Schema.Struct({\n domain: Schema.String,\n attachmentId: Schema.NullOr(Schema.String),\n overall: Storage.Overall,\n requirements: Schema.Array(\n Schema.Struct({\n /** `Verify.requirementKey(record)`, so a client pairs rows by content, not by position. */\n key: Schema.String,\n operationId: Schema.NullOr(Plan.OperationId),\n record: DnsRecord.Model,\n status: Storage.RequirementStatus,\n evidence: Schema.Array(Verify.Evidence),\n }),\n ),\n host: Schema.Array(Verify.HostEvidence),\n checkedAt: Schema.DateTimeUtcFromString,\n nextCheckAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n});\nexport type Readiness = typeof Readiness.Type;\n\nconst Redirected = HttpApiSchema.WithHeaders(HttpApiSchema.Empty(302), {\n location: Schema.String,\n});\n\n// ---------------------------------------------------------------------------------------------\n// Errors: one wire body, the status from `DomainKit.Error.httpStatus`\n// ---------------------------------------------------------------------------------------------\n\n/** `DomainKit.Error` narrowed to the reasons that answer with `status`. */\nconst errorAt = (status: number) =>\n Errors.DomainKitError.check(\n Schema.makeFilter((error: Errors.DomainKitError) => error.httpStatus === status),\n ).pipe(HttpApiSchema.status(status));\n\nconst errors = [400, 401, 403, 404, 409, 500, 501, 502, 503].map(errorAt);\n\n// ---------------------------------------------------------------------------------------------\n// The group\n// ---------------------------------------------------------------------------------------------\n\nexport const group = HttpApiGroup.make(\"domainkit\")\n .add(\n HttpApiEndpoint.get(\"inspect\", \"/domains/:domain\", {\n params: { domain: Schema.String },\n success: Snapshot,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"discover\", \"/domains/:domain/discovery\", {\n params: { domain: Schema.String },\n success: Discovery,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"zones\", \"/zones\", {\n query: { provider: Schema.optionalKey(Schema.String) },\n success: Zones,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"start\", \"/connections\", {\n payload: StartPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"callback\", \"/callback/:provider\", {\n params: { provider: Schema.String },\n // `Connect.complete` reads every callback parameter off the request URL, because a provider\n // may return `error`, `teamId`, or `configurationId` too. These two are declared so the\n // OpenAPI document names what a provider is expected to send back.\n query: { state: Schema.String, code: Schema.optionalKey(Schema.String) },\n success: Redirected,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"reconnect\", \"/connections/:connectionId/reconnections\", {\n params: { connectionId: Schema.String },\n payload: ReconnectPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"attach\", \"/connections/:connectionId/attachments\", {\n params: { connectionId: Schema.String },\n payload: AttachPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.delete(\"detach\", \"/attachments/:attachmentId\", {\n params: { attachmentId: Schema.String },\n success: Schema.Void,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.delete(\"disconnect\", \"/connections/:connectionId\", {\n params: { connectionId: Schema.String },\n success: Schema.Void,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"createPlan\", \"/domains/:domain/plans\", {\n params: { domain: Schema.String },\n payload: PlanPayload,\n success: Plan.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"approve\", \"/plans/:planId/approvals\", {\n params: { planId: Plan.PlanId },\n payload: ApprovePayload,\n success: Approval.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"reject\", \"/plans/:planId/rejections\", {\n params: { planId: Plan.PlanId },\n payload: RejectPayload,\n success: Attempt,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"apply\", \"/approvals/:approvalId/apply\", {\n params: { approvalId: Approval.ApprovalId },\n success: Receipt.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"plan\", \"/plans/:planId\", {\n params: { planId: Plan.PlanId },\n success: Attempt,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"receipt\", \"/receipts/:receiptId\", {\n params: { receiptId: Receipt.ReceiptId },\n success: Receipt.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"observe\", \"/domains/:domain/observations\", {\n params: { domain: Schema.String },\n payload: ObservePayload,\n success: Readiness,\n error: errors,\n }),\n )\n .add(\n // The stored observation, or `null` when nothing has been observed for the domain yet. A\n // domain with no readiness is not a missing route, so it answers 200 with a null body and\n // leaves 404 to name a domain this owner cannot reach.\n HttpApiEndpoint.get(\"readiness\", \"/domains/:domain/readiness\", {\n params: { domain: Schema.String },\n success: Schema.NullOr(Readiness),\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"cleanupPlan\", \"/receipts/:receiptId/cleanup-plans\", {\n params: { receiptId: Receipt.ReceiptId },\n success: Plan.Model,\n error: errors,\n }),\n )\n .add(\n // `Idempotency-Key` is the batch's identity for this owner: a retried create answers with the\n // batch the first call made rather than planning the same domains twice.\n HttpApiEndpoint.post(\"createBatch\", \"/batches\", {\n headers: { \"idempotency-key\": Schema.String },\n payload: BatchPayload,\n success: Batch,\n error: errors,\n }),\n )\n .add(\n // The one listing there is: what this owner still owes a move on. A finished batch is read by\n // its own id.\n HttpApiEndpoint.get(\"batches\", \"/batches\", {\n query: { unfinished: Schema.Literal(\"true\") },\n success: Schema.Array(BatchSummary),\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"batch\", \"/batches/:batchId\", {\n params: { batchId: Storage.BatchId },\n success: Batch,\n error: errors,\n }),\n )\n .add(\n // Plan the items that still have none. The batch stores pointers, not requirements, so the\n // host supplies them again.\n HttpApiEndpoint.post(\"planBatch\", \"/batches/:batchId/plans\", {\n params: { batchId: Storage.BatchId },\n payload: BatchPayload,\n success: Batch,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"approveBatch\", \"/batches/:batchId/approvals\", {\n params: { batchId: Storage.BatchId },\n payload: BatchApprovePayload,\n success: Batch,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"rejectBatch\", \"/batches/:batchId/rejections\", {\n params: { batchId: Storage.BatchId },\n payload: RejectPayload,\n success: Batch,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"applyBatch\", \"/batches/:batchId/apply\", {\n params: { batchId: Storage.BatchId },\n success: Batch,\n error: errors,\n }),\n );\n// Cleanup approve, reject, and apply reuse those routes: the attempt knows its kind.\n\nexport type Group = typeof group;\n\n/** Every route in the group, by name; what `Identity.authorize` is asked about. */\nexport type EndpointName = HttpApiGroup.Endpoints<Group>[\"identifier\"];\n\n/** Browser-safe summaries of an authenticated live callback. Connected is not DNS readiness. */\nexport const CallbackOutcome = Schema.Union([\n Schema.Struct({\n _tag: Schema.Literal(\"Connected\"),\n provider: Schema.String,\n connectionId: Schema.String,\n returnTo: Schema.String,\n }),\n Schema.Struct({\n _tag: Schema.Literal(\"Cancelled\"),\n provider: Schema.String,\n returnTo: Schema.String,\n }),\n Schema.Struct({\n _tag: Schema.Literal(\"Failed\"),\n provider: Schema.String,\n returnTo: Schema.String,\n recovery: Schema.Literals([\"restart\", \"inspect\"]),\n }),\n]);\nexport type CallbackOutcome = typeof CallbackOutcome.Type;\n\nexport interface Options {\n /**\n * Where the callback route is reachable from the provider, without the `/callback/:provider`\n * suffix. Defaults to the incoming request's origin and mount path.\n */\n readonly callbackBaseUrl?: string;\n /** Where interactive flows return when the callback carries no `returnTo`. */\n readonly defaultReturnTo?: string;\n /**\n * Opt in to a same-origin destination, or return undefined to keep default behavior. Invoked\n * only for a verified live flow; no request, state, code, secrets, or storage rows reach this\n * policy. Close over acquired host services; its Effect requires no environment.\n * A policy failure after Connected cannot undo persistence: inspect before restarting.\n */\n readonly callback?: (\n outcome: CallbackOutcome,\n ) => Effect.Effect<string | undefined, Errors.DomainKitError>;\n}\n\n/** Offline registration requirements, not proof of provider console or grant readiness. */\nexport const RegistrationSettings = Schema.Union([\n Schema.Struct({\n _tag: Schema.Literal(\"OAuth\"),\n provider: Schema.String,\n callbackUrl: Schema.String,\n clientAuth: Schema.Literals([\"none\", \"client_secret_basic\", \"client_secret_post\"]),\n pkce: Schema.Literal(\"S256\"),\n scopes: Schema.Array(Schema.String),\n }),\n Schema.Struct({\n _tag: Schema.Literal(\"Integration\"),\n provider: Schema.String,\n callbackUrl: Schema.String,\n clientAuth: Schema.Literal(\"client_secret_post\"),\n }),\n]);\nexport type RegistrationSettings = typeof RegistrationSettings.Type;\n\n/** Derive only public setup values; no credentials, identity, storage, or network are needed. */\nexport const registrationSettings = (input: {\n readonly providers: ReadonlyArray<Provider.Definition>;\n readonly callbackBaseUrl: string;\n}): Effect.Effect<ReadonlyArray<RegistrationSettings>, Errors.DomainKitError> =>\n Effect.gen(function* () {\n yield* Configuration.endpoint(input.callbackBaseUrl, \"callbackBaseUrl\");\n const settings: Array<RegistrationSettings> = [];\n for (const definition of input.providers) {\n const callbackUrl = (yield* callbackUrlAt(input.callbackBaseUrl, definition.id)).toString();\n const oauth = definition.auth.oauth;\n const integration = definition.auth.integration;\n const unsupported = (method: string) =>\n Errors.fail(\n new Reason.Unsupported({\n provider: definition.id,\n operation: \"registrationSettings\",\n message: `${definition.id} ${method} has no registration metadata`,\n }),\n );\n if (oauth !== undefined) {\n if (oauth.registration === undefined) return yield* unsupported(\"OAuth\");\n yield* Configuration.scopes(oauth.scopes, \"oauth.scopes\");\n const { clientAuth, pkce } = oauth.registration;\n yield* Configuration.requireValue(\n clientAuth,\n (v) => v === \"none\" || v === \"client_secret_basic\" || v === \"client_secret_post\",\n \"oauth.clientAuth\",\n \"oauth.clientAuth is unsupported\",\n );\n yield* Configuration.requireValue(\n pkce,\n (v) => v === \"S256\",\n \"oauth.pkce\",\n \"oauth.pkce is unsupported\",\n );\n settings.push({\n _tag: \"OAuth\",\n provider: definition.id,\n callbackUrl,\n clientAuth,\n pkce,\n scopes: [...oauth.scopes],\n });\n }\n if (integration !== undefined) {\n if (integration.registration === undefined) return yield* unsupported(\"Integration\");\n yield* Configuration.requireValue(\n integration.registration.clientAuth,\n (v) => v === \"client_secret_post\",\n \"integration.clientAuth\",\n \"integration.clientAuth is unsupported\",\n );\n settings.push({\n _tag: \"Integration\",\n provider: definition.id,\n callbackUrl,\n clientAuth: integration.registration.clientAuth,\n });\n }\n }\n return settings;\n });\n\n/** Everything the handlers need: the lifecycle services, Storage, and the host's `Identity`. */\nexport type Services =\n | Provision.Service\n | Cleanup.Service\n | Connect.Service\n | Verify.Service\n | Providers.Service\n | Storage.Service\n | Identity;\n\n// ---------------------------------------------------------------------------------------------\n// Handlers\n// ---------------------------------------------------------------------------------------------\n\nconst START_ROUTE = \"/connections\";\nconst reconnectRoute = (connectionId: string) =>\n `/connections/${encodeURIComponent(connectionId)}/reconnections`;\nconst callbackRoute = (provider: string) => `/callback/${encodeURIComponent(provider)}`;\n\nconst invalid = (message: string, field?: string) =>\n Errors.fail(new Reason.InvalidInput({ message, ...(field === undefined ? {} : { field }) }));\n\n/** The request as an absolute URL: the web original when there is one, else scheme plus `Host`. */\nconst absoluteUrl = (\n request: HttpServerRequest.HttpServerRequest,\n): Effect.Effect<URL, Errors.DomainKitError> =>\n Effect.suspend(() => {\n try {\n return Effect.succeed(new URL(request.originalUrl));\n } catch {\n const url = HttpServerRequest.toURL(request);\n return Option.isNone(url)\n ? invalid(\"The request has no usable origin; set callbackBaseUrl\", \"callbackBaseUrl\")\n : Effect.succeed(url.value);\n }\n });\n\n/**\n * The absolute URL the provider redirects back to. `callbackBaseUrl` wins when the host set it,\n * because a proxy that rewrites `Host` leaves the request pointing at an origin the browser never\n * sees; otherwise it follows the prefix the group is mounted at, derived by stripping the handling\n * route's own path off the request.\n */\nconst callbackUrlFor = (input: {\n readonly request: HttpServerRequest.HttpServerRequest;\n readonly provider: string;\n readonly options: Options;\n /** The path of the route serving this request, stripped to find the mount. */\n readonly route: string;\n}): Effect.Effect<URL, Errors.DomainKitError> =>\n Effect.suspend(() => {\n const configured = input.options.callbackBaseUrl;\n if (configured !== undefined) {\n return callbackUrlAt(configured, input.provider);\n }\n return Effect.flatMap(absoluteUrl(input.request), (url) => {\n const path = url.pathname;\n if (!path.endsWith(input.route)) {\n return invalid(\n `The ${input.route} route is not mounted where it says; set callbackBaseUrl`,\n \"callbackBaseUrl\",\n );\n }\n return callbackUrlAt(\n `${url.origin}${path.slice(0, path.length - input.route.length)}`,\n input.provider,\n );\n });\n });\n\n/**\n * One refusal for every way a callback can fail to match a flow: no such `state`, one that has\n * expired, one presented at another provider's route, and one presented by a session that did not\n * start it. The text is constant on purpose — a caller holding a leaked `state` must not be able\n * to tell \"no such flow\" from \"not your flow\".\n */\nconst noSuchFlow = Callback.refuse;\n\nconst callbackUrlAt = (base: string, provider: string): Effect.Effect<URL, Errors.DomainKitError> =>\n Configuration.endpoint(base, \"callbackBaseUrl\").pipe(\n Effect.map((value) => {\n const url = new URL(value);\n url.pathname = `${url.pathname.replace(/\\/+$/, \"\")}${callbackRoute(provider)}`;\n return url;\n }),\n );\n\n/**\n * Where the callback may send the customer: a path under the callback's own base, or an absolute\n * URL on its origin. A destination that leaves the application is an open redirect, whoever\n * supplied it.\n */\nconst sameOrigin = (destination: string, callback: URL): string | null => {\n try {\n // Resolving against the callback is what makes this safe: the URL parser normalizes the forms\n // a browser would follow off-origin (`//host`, `/\\host`, `\\/host`, encoded hosts) into a real\n // origin. Blob URLs can embed that origin, so destinations must also use HTTP(S).\n const url = new URL(destination, callback);\n return (url.protocol === \"http:\" || url.protocol === \"https:\") && url.origin === callback.origin\n ? url.toString()\n : null;\n } catch {\n return null;\n }\n};\n\nconst snapshotOf = (snapshot: Connect.Snapshot): Snapshot => ({\n domain: snapshot.domain,\n attachment:\n snapshot.attachment === null\n ? null\n : { id: snapshot.attachment.id, label: snapshot.attachment.label },\n connectionId: snapshot.connection?.id ?? null,\n provider: snapshot.authorization?.provider ?? null,\n method: snapshot.authorization?.method ?? null,\n status:\n snapshot.connection === null\n ? \"disconnected\"\n : snapshot.authorization?.revocation === \"active\"\n ? \"connected\"\n : \"reconnect\",\n lastReceiptId: snapshot.lastReceiptId,\n connectionDomains: snapshot.connectionDomains,\n reusable: snapshot.reusable.map(({ connection, provider, method }) => ({\n connectionId: connection.id,\n provider,\n method,\n })),\n providers: snapshot.providers.map(({ id, name, methods }) => ({ id, name, methods })),\n});\n\n/** The wire method as `Connect` takes it; interactive cases carry the caller's return destination. */\nconst methodOf = (method: Method): Connect.Method => {\n switch (method._tag) {\n case \"Token\":\n return Connect.Method.token(method.values);\n case \"OAuth\":\n return Connect.Method.oauth(\n method.returnTo === undefined ? {} : { returnTo: method.returnTo },\n );\n case \"Integration\":\n return Connect.Method.integration(\n method.returnTo === undefined ? {} : { returnTo: method.returnTo },\n );\n }\n};\n\nconst candidatesOf = (\n candidates: ReadonlyArray<{ readonly zone: string; readonly label: string }>,\n): ReadonlyArray<Candidate> => candidates.map(({ zone, label }) => new Candidate({ zone, label }));\n\n/**\n * Handlers for `group` inside the host's API. Every handler derives `Principal` from `Identity`\n * for the request it is serving and provides it to the lifecycle services.\n */\nexport const layer = <ApiId extends string, Groups extends HttpApiGroup.Constraint>(\n api: HttpApi.HttpApi<ApiId, Groups>,\n options: Options = {},\n): Layer.Layer<HttpApiGroup.Service<ApiId, \"domainkit\">, never, Services> =>\n // The host's API carries its own groups beside this one; only `\"domainkit\"` is implemented here,\n // and `HttpApiBuilder.group` needs the group's own endpoint types to check the handlers.\n HttpApiBuilder.group(api as unknown as HttpApi.HttpApi<ApiId, Group>, \"domainkit\", (handlers) =>\n Effect.gen(function* () {\n const identity = yield* Identity;\n const connect = yield* Connect.Service;\n const provision = yield* Provision.Service;\n const cleanup = yield* Cleanup.Service;\n const verify = yield* Verify.Service;\n const storage = yield* Storage.Service;\n const providers = yield* Providers.Service;\n\n /**\n * Run a lifecycle effect as the principal the host derives from this request, once the host\n * has allowed that principal onto this route.\n */\n const as = <A>(\n endpoint: EndpointName,\n request: HttpServerRequest.HttpServerRequest,\n effect: Effect.Effect<A, Errors.DomainKitError, Principal.Service>,\n context?: IdentityContext,\n ): Effect.Effect<A, Errors.DomainKitError> =>\n Effect.flatMap(identity.principal(request, context), (principal) =>\n Effect.flatMap(identity.authorize?.(principal, endpoint) ?? Effect.void, () =>\n Effect.provideService(effect, Principal.Service, principal),\n ),\n );\n\n const snapshot = (domain: string) => Effect.map(connect.inspect(domain), snapshotOf);\n\n const discovered = (discovery: Connect.Discovery): Discovery => {\n switch (discovery._tag) {\n case \"Resolved\":\n return new DiscoveryResolved({\n connectionId: discovery.connectionId,\n zone: discovery.target.zone,\n label: discovery.target.label,\n });\n case \"SelectionRequired\":\n return new DiscoverySelectionRequired({\n candidates: discovery.candidates.map(({ connectionId, target }) => ({\n connectionId,\n zone: target.zone,\n label: target.label,\n })),\n });\n case \"NotFound\":\n return new DiscoveryNotFound({\n nameservers: discovery.nameservers,\n host: discovery.host,\n });\n }\n };\n\n /**\n * The connection a start or an attach produced. `label` is the zone the attachment named,\n * or the provider itself where the connection carries no domain yet.\n */\n const connected = (input: {\n readonly connection: Storage.Connection;\n readonly attachment: Storage.Attachment | null;\n readonly domain: string | undefined;\n }): Effect.Effect<Connected, Errors.DomainKitError, Principal.Service> =>\n Effect.gen(function* () {\n const authorization = yield* storage.authorizations.get(input.connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n return new Connected({\n connectionId: input.connection.id,\n provider: authorization.provider,\n label: input.attachment?.label ?? authorization.label ?? definition.name,\n snapshot: input.domain === undefined ? null : yield* snapshot(input.domain),\n });\n });\n\n const startedOf = (\n started: Connect.Started,\n domain: string | undefined,\n ): Effect.Effect<Started, Errors.DomainKitError, Principal.Service> => {\n switch (started._tag) {\n case \"Connected\":\n return connected({\n attachment: started.attachment,\n connection: started.connection,\n domain,\n });\n case \"Redirect\":\n return Effect.succeed(new Redirect({ authorizationUrl: started.authorizationUrl }));\n case \"SelectionRequired\":\n return Effect.succeed(\n new SelectionRequired({\n connectionId: started.connection.id,\n candidates: candidatesOf(started.candidates),\n }),\n );\n }\n };\n\n /**\n * Attach with a zone the client picked out of a `SelectionRequired`. The provider target\n * behind a zone never crosses the wire, so resolve the candidates again and match by zone.\n */\n const attachAt = (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string | undefined;\n }): Effect.Effect<Started, Errors.DomainKitError, Principal.Service> =>\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(input.connectionId);\n const attached = (attachment: Storage.Attachment) =>\n connected({ attachment, connection, domain: input.domain });\n const first = yield* connect.attach({\n connectionId: input.connectionId,\n domain: input.domain,\n });\n if (!(\"_tag\" in first)) return yield* attached(first);\n if (input.zone === undefined) {\n return new SelectionRequired({\n connectionId: input.connectionId,\n candidates: candidatesOf(first.candidates),\n });\n }\n const target = first.candidates.find(({ zone }) => zone === input.zone);\n if (target === undefined) {\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: input.zone }));\n }\n const second = yield* connect.attach({\n connectionId: input.connectionId,\n domain: input.domain,\n target,\n });\n return \"_tag\" in second\n ? yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: input.zone }))\n : yield* attached(second);\n });\n\n /** Provisioning and cleanup share the approve and apply routes; the attempt knows its kind. */\n const kindOf = (attempt: Storage.Attempt) =>\n attempt.kind === \"cleanup\" ? cleanup : provision;\n\n return handlers\n .handle(\"inspect\", ({ params, request }) => as(\"inspect\", request, snapshot(params.domain)))\n .handle(\"discover\", ({ params, request }) =>\n as(\"discover\", request, Effect.map(connect.discover(params.domain), discovered)),\n )\n .handle(\"zones\", ({ query, request }) =>\n as(\n \"zones\",\n request,\n Effect.map(\n connect.zones(query.provider === undefined ? {} : { provider: query.provider }),\n (listing): Zones => ({\n zones: listing.zones.map(({ connectionId, provider, target }) => ({\n connectionId,\n provider,\n zone: target.zone,\n label: target.label,\n ...(target.nameservers === undefined ? {} : { nameservers: target.nameservers }),\n })),\n connections: listing.connections,\n providers: listing.providers,\n }),\n ),\n ),\n )\n .handle(\"start\", ({ payload, request }) =>\n as(\n \"start\",\n request,\n Effect.gen(function* () {\n // Token methods connect in one call; the interactive ones need somewhere to land.\n const callbackUrl =\n payload.method._tag === \"Token\"\n ? undefined\n : (yield* callbackUrlFor({\n request,\n provider: payload.provider,\n options,\n route: START_ROUTE,\n })).toString();\n const started = yield* connect.start({\n provider: payload.provider,\n ...(payload.domain === undefined ? {} : { domain: payload.domain }),\n method: methodOf(payload.method),\n ...(callbackUrl === undefined ? {} : { callbackUrl }),\n });\n return yield* startedOf(started, payload.domain);\n }),\n ),\n )\n .handle(\"reconnect\", ({ params, payload, request }) =>\n as(\n \"reconnect\",\n request,\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(params.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const callbackUrl =\n payload.method._tag === \"Token\"\n ? undefined\n : (yield* callbackUrlFor({\n request,\n provider: authorization.provider,\n options,\n route: reconnectRoute(params.connectionId),\n })).toString();\n const started = yield* connect.reconnect({\n connectionId: params.connectionId,\n method: methodOf(payload.method),\n ...(callbackUrl === undefined ? {} : { callbackUrl }),\n });\n return yield* startedOf(started, undefined);\n }),\n ),\n )\n .handle(\"callback\", ({ params, query, request }) =>\n Effect.gen(function* () {\n // The one route that establishes its identity from something other than the request.\n // A provider callback is a top-level navigation carrying a `state` the provider echoed\n // back and whatever credential the browser attaches by itself, so the flow DomainKit\n // recorded is the only authority on whose it is. Read its header first — a read, never\n // a spend, so a leaked `state` cannot burn the flow it names — tell the host which\n // flow is being finished, and then hold the host's answer against the record.\n const header = yield* storage.continuations\n .header(query.state)\n .pipe(\n Effect.catch((error) =>\n Callback.missing(error) ? Effect.succeed(null) : Effect.fail(error),\n ),\n );\n const continuation =\n header !== null && header.provider === params.provider ? header : null;\n // The host authenticates even when there is nothing to finish, so the status cannot\n // tell a caller whether the `state` it holds names a flow at all.\n return yield* as(\n \"callback\",\n request,\n Effect.gen(function* () {\n // The owner comes from the record and the actor from the host's own session;\n // nothing in the query string contributes either. The scoped `get` below keeps\n // its owner filter, so a host that answers with the wrong principal still fails\n // closed even if this check were ever removed.\n const principal = yield* Principal.Service;\n if (\n continuation === null ||\n principal.ownerId !== continuation.ownerId ||\n principal.actorId !== continuation.actorId\n ) {\n return yield* noSuchFlow;\n }\n const url = yield* absoluteUrl(request);\n // Resolve the destination against the callback's public base, not the request: a\n // proxy that rewrites `Host` leaves the request on an origin the browser never\n // sees.\n const base = yield* callbackUrlFor({\n request,\n provider: params.provider,\n options,\n route: callbackRoute(params.provider),\n });\n // Where the customer lands comes from the continuation this owner started, never\n // from the provider's query string, and it is resolved before the callback is\n // spent: a server with nowhere to send them must not connect the provider and\n // then fail.\n // The host's authentication sits between the header read and this one, so the\n // flow can expire or be spent in that window. It refuses the same way it would\n // have a moment earlier, rather than changing shape because of when it happened.\n // This is also where the owner filter fails closed when a host resolves the wrong\n // principal, and that refusal must look like every other one.\n const flow = yield* storage.continuations\n .get(query.state)\n .pipe(\n Effect.catch((error) =>\n Callback.missing(error) ? noSuchFlow : Effect.fail(error),\n ),\n );\n const requested = flow.returnTo ?? options.defaultReturnTo;\n if (requested === undefined) {\n return yield* invalid(\n \"The flow carried no returnTo and the server has no defaultReturnTo\",\n \"returnTo\",\n );\n }\n const destination = sameOrigin(requested, base);\n if (destination === null) {\n return yield* invalid(`${requested} leaves this application`, \"returnTo\");\n }\n // Re-read within the completion lock before any provider parameters are classified.\n const outcome = yield* connect\n .completeOutcome({\n continuationId: query.state,\n callbackUrl: url.toString(),\n provider: params.provider,\n })\n .pipe(\n Effect.catch((error) =>\n Callback.missing(error) ? noSuchFlow : Effect.fail(error),\n ),\n );\n const summary: CallbackOutcome =\n outcome._tag === \"Connected\"\n ? {\n _tag: \"Connected\",\n provider: flow.provider,\n connectionId: outcome.started.connection.id,\n returnTo: destination,\n }\n : outcome._tag === \"Cancelled\"\n ? { _tag: \"Cancelled\", provider: flow.provider, returnTo: destination }\n : {\n _tag: \"Failed\",\n provider: flow.provider,\n returnTo: destination,\n recovery: outcome.recovery,\n };\n const selected =\n options.callback === undefined ? undefined : yield* options.callback(summary);\n const location = selected === undefined ? destination : sameOrigin(selected, base);\n if (location === null)\n return yield* invalid(\"callback destination leaves this application\", \"callback\");\n if (selected === undefined && outcome._tag !== \"Connected\")\n return yield* Effect.fail(outcome.error);\n return HttpApiSchema.withHeaders({\n body: undefined as void,\n headers: { location },\n });\n }),\n continuation === null ? undefined : { endpoint: \"callback\", continuation },\n );\n }),\n )\n .handle(\"attach\", ({ params, payload, request }) =>\n as(\n \"attach\",\n request,\n attachAt({\n connectionId: params.connectionId,\n domain: payload.domain,\n zone: payload.zone,\n }),\n ),\n )\n .handle(\"detach\", ({ params, request }) =>\n as(\"detach\", request, connect.detach(params.attachmentId)),\n )\n .handle(\"disconnect\", ({ params, request }) =>\n as(\"disconnect\", request, connect.disconnect(params.connectionId)),\n )\n .handle(\"createPlan\", ({ params, payload, request }) =>\n as(\n \"createPlan\",\n request,\n provision.plan({ domain: params.domain, requirements: payload.requirements }),\n ),\n )\n .handle(\"approve\", ({ params, payload, request }) =>\n as(\n \"approve\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.get(params.planId);\n return yield* kindOf(attempt).approve(params.planId, {\n ...(payload.operationIds === undefined\n ? {}\n : { operationIds: payload.operationIds, allowPartial: true }),\n });\n }),\n ),\n )\n .handle(\"reject\", ({ params, payload, request }) =>\n as(\n \"reject\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.get(params.planId);\n return yield* kindOf(attempt).reject(params.planId, {\n ...(payload.reason === undefined ? {} : { reason: payload.reason }),\n });\n }),\n ),\n )\n .handle(\"apply\", ({ params, request }) =>\n as(\n \"apply\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.byApproval(params.approvalId);\n return yield* kindOf(attempt).apply(params.approvalId);\n }),\n ),\n )\n .handle(\"plan\", ({ params, request }) =>\n as(\n \"plan\",\n request,\n Effect.map(storage.attempts.get(params.planId), (attempt) => ({\n plan: attempt.plan,\n status: attempt.status,\n approval: attempt.approval,\n receipt: attempt.receipt,\n rejection: attempt.rejection,\n })),\n ),\n )\n .handle(\"receipt\", ({ params, request }) =>\n as(\n \"receipt\",\n request,\n Effect.flatMap(storage.attempts.byReceipt(params.receiptId), (attempt) =>\n attempt.receipt === null\n ? Errors.fail(new Reason.NotFound({ entity: \"receipt\", id: params.receiptId }))\n : Effect.succeed(attempt.receipt),\n ),\n ),\n )\n .handle(\"observe\", ({ params, payload, request }) =>\n as(\n \"observe\",\n request,\n verify.observe({\n domain: params.domain,\n ...(payload.requirements === undefined ? {} : { requirements: payload.requirements }),\n }),\n ),\n )\n .handle(\"readiness\", ({ params, request }) =>\n as(\"readiness\", request, verify.latest(params.domain)),\n )\n .handle(\"cleanupPlan\", ({ params, request }) =>\n as(\"cleanupPlan\", request, cleanup.plan({ receiptId: params.receiptId })),\n )\n .handle(\"createBatch\", ({ headers, payload, request }) =>\n as(\n \"createBatch\",\n request,\n provision.batch.create({\n items: payload.items,\n idempotencyKey: headers[\"idempotency-key\"],\n }),\n ),\n )\n .handle(\"batches\", ({ request }) =>\n as(\"batches\", request, provision.batch.list({ unfinished: true })),\n )\n .handle(\"batch\", ({ params, request }) =>\n as(\"batch\", request, provision.batch.get(params.batchId)),\n )\n .handle(\"planBatch\", ({ params, payload, request }) =>\n as(\n \"planBatch\",\n request,\n provision.batch.resumePlanning(params.batchId, { items: payload.items }),\n ),\n )\n .handle(\"approveBatch\", ({ params, payload, request }) =>\n as(\n \"approveBatch\",\n request,\n provision.batch.approve(params.batchId, { digest: payload.digest }),\n ),\n )\n .handle(\"rejectBatch\", ({ params, payload, request }) =>\n as(\n \"rejectBatch\",\n request,\n provision.batch.reject(params.batchId, {\n ...(payload.reason === undefined ? {} : { reason: payload.reason }),\n }),\n ),\n )\n .handle(\"applyBatch\", ({ params, request }) =>\n as(\"applyBatch\", request, provision.batch.apply(params.batchId)),\n );\n }),\n ) as Layer.Layer<HttpApiGroup.Service<ApiId, \"domainkit\">, never, Services>;\n\n/** A standalone API for hosts not on HttpApi. Mount at any prefix. */\nexport const api: HttpApi.HttpApi<\"domainkit\", Group> = HttpApi.make(\"domainkit\").add(group);\n\nexport interface WebHandlerOptions extends Options {\n /** Mount every route under this path, e.g. `/api/domainkit`. */\n readonly prefix?: `/${string}`;\n}\n\n/** The Promise edge: a `fetch`-shaped handler over the group for hosts that are not Effect-native. */\nexport const toWebHandler = (\n services: Layer.Layer<Services, Errors.DomainKitError>,\n options: WebHandlerOptions = {},\n): {\n readonly handler: (request: Request) => Promise<Response>;\n readonly dispose: () => Promise<void>;\n} => {\n // `group.prefix` widens the endpoint paths in the type, which `Group` states unprefixed; the\n // handlers are keyed by endpoint identifier, so the routes still line up.\n const prefixed =\n options.prefix === undefined\n ? api\n : (HttpApi.make(\"domainkit\").add(group.prefix(options.prefix)) as unknown as HttpApi.HttpApi<\n \"domainkit\",\n Group\n >);\n const app = HttpApiBuilder.layer(prefixed).pipe(\n Layer.provide(layer(prefixed, options)),\n Layer.provide(services),\n Layer.provide(HttpServer.layerServices),\n );\n return HttpRouter.toWebHandler(app, { disableLogger: true });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Server.mjs","names":["Storage.AuthMethod","DnsRecord.Model","Plan.OperationId","Plan.Model","Storage.AttemptStatus","Approval.Model","Receipt.Model","Storage.Rejection","Plan.Digest","Storage.BatchId","Storage.BatchStatus","Storage.BatchApproval","Storage.Overall","Storage.RequirementStatus","Verify.Evidence","Verify.HostEvidence","Plan.PlanId","Approval.ApprovalId","Receipt.ReceiptId","Configuration.endpoint","Errors.fail","Reason.Unsupported","Configuration.scopes","Configuration.requireValue","Reason.InvalidInput","Callback.refuse","Connect.Service","Provision.Service","Cleanup.Service","Verify.Service","Storage.Service","Providers.Service","Principal.Service","Reason.NotFound","Callback.missing","CallbackRecovery.write"],"sources":["../src/Server.ts"],"sourcesContent":["/**\n * domainkit/server — the whole lifecycle as one HttpApi group the host mounts.\n *\n * const Api = HttpApi.make(\"app\").add(Server.group)\n * const ApiLive = HttpApiBuilder.layer(Api).pipe(\n * Layer.provide([Server.layer(Api), DomainKitLive, IdentityLive]),\n * )\n *\n * The host provides `Identity` (request -> `Principal.Interface`). Everything else comes from\n * `DomainKit.layer`. Non-HttpApi hosts use `toWebHandler`. The group carries no prefix of its\n * own, so `group.prefix(\"/internal/dns\")` or a host router mount is all a different base path\n * takes; callback URLs follow the mount.\n */\nimport { Context, Effect, Layer, Option, Schema } from \"effect\";\nimport * as HttpRouter from \"effect/http/HttpRouter\";\nimport * as HttpServer from \"effect/http/HttpServer\";\nimport * as HttpServerRequest from \"effect/http/HttpServerRequest\";\nimport * as HttpApi from \"effect/http-api/HttpApi\";\nimport * as HttpApiBuilder from \"effect/http-api/HttpApiBuilder\";\nimport * as HttpApiEndpoint from \"effect/http-api/HttpApiEndpoint\";\nimport * as HttpApiGroup from \"effect/http-api/HttpApiGroup\";\nimport * as HttpApiSchema from \"effect/http-api/HttpApiSchema\";\n\nimport * as Approval from \"./Approval.ts\";\nimport * as CallbackRecovery from \"./CallbackRecovery.ts\";\nimport * as Cleanup from \"./Cleanup.ts\";\nimport * as Connect from \"./Connect.ts\";\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Plan from \"./Plan.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Configuration from \"./internal/config.ts\";\nimport * as Callback from \"./internal/callback.ts\";\nimport type * as Provider from \"./Provider.ts\";\nimport * as Providers from \"./Providers.ts\";\nimport * as Provision from \"./Provision.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as Receipt from \"./Receipt.ts\";\nimport * as Storage from \"./Storage.ts\";\nimport * as Verify from \"./Verify.ts\";\n\n/**\n * What DomainKit already knows about the request when it asks the host who is making it. Only\n * `/callback/:provider` passes one, because it is the only route that points at a flow DomainKit\n * recorded earlier; every other route calls `principal` with the request alone.\n */\nexport interface IdentityContext {\n /** The route being served. */\n readonly endpoint: EndpointName;\n /** The flow this callback claims to finish, as DomainKit recorded it when the flow started. */\n readonly continuation: Storage.ContinuationHeader;\n}\n\n/** The only service a host must implement for the server. */\nexport interface IdentityService {\n /**\n * `context` is present only on the callback, where a host that keeps per-request tenancy can use\n * `context.continuation.ownerId` to pick the right tenant out of a session that holds several.\n * Ignoring it is safe everywhere: DomainKit checks the principal against the recorded flow on\n * that route regardless, so a host that answers with the wrong one is refused rather than\n * trusted.\n */\n readonly principal: (\n request: HttpServerRequest.HttpServerRequest,\n context?: IdentityContext,\n ) => Effect.Effect<Principal.Interface, Errors.DomainKitError>;\n /**\n * Which routes this principal may reach, checked after `principal` on every request. Fail with\n * reason `Forbidden` for the 403 a UI expects. Without it every authenticated principal reaches\n * every route, which is the right default for a host whose own middleware already gates the\n * mount.\n */\n readonly authorize?: (\n principal: Principal.Interface,\n endpoint: EndpointName,\n ) => Effect.Effect<void, Errors.DomainKitError>;\n}\n\nexport class Identity extends Context.Service<Identity, IdentityService>()(\n \"@domainkit/server/Identity\",\n) {}\n\n// ---------------------------------------------------------------------------------------------\n// Wire schemas (the transport contract `domainkit/client` and `@domainkit/react` consume)\n// ---------------------------------------------------------------------------------------------\n\nexport const ConnectionStatus = Schema.Literals([\"disconnected\", \"connected\", \"reconnect\"]);\nexport type ConnectionStatus = typeof ConnectionStatus.Type;\n\n/** One value a token method needs, so a form renders without knowing the provider. */\nexport const Field = Schema.Struct({\n name: Schema.String,\n required: Schema.Boolean,\n secret: Schema.Boolean,\n});\nexport type Field = typeof Field.Type;\n\n/** How a provider can be connected; `fields` is `null` for the interactive methods. */\nexport const MethodDescriptor = Schema.Struct({\n kind: Storage.AuthMethod,\n label: Schema.String,\n docsUrl: Schema.NullOr(Schema.String),\n fields: Schema.NullOr(Schema.Array(Field)),\n});\nexport type MethodDescriptor = typeof MethodDescriptor.Type;\n\n/** Everything the UI needs about a domain, flattened; provider context never crosses the wire. */\n/** The attachment a domain holds, with the zone label the provider gave when it was created. */\nexport const Attachment = Schema.Struct({\n id: Schema.String,\n label: Schema.String,\n});\nexport type Attachment = typeof Attachment.Type;\n\nexport const Snapshot = Schema.Struct({\n domain: Schema.String,\n attachment: Schema.NullOr(Attachment),\n connectionId: Schema.NullOr(Schema.String),\n provider: Schema.NullOr(Schema.String),\n method: Schema.NullOr(Storage.AuthMethod),\n status: ConnectionStatus,\n lastReceiptId: Schema.NullOr(Schema.String),\n /** How many domains the connection serves, including this one; `0` without a connection. */\n connectionDomains: Schema.Number,\n reusable: Schema.Array(\n Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n method: Storage.AuthMethod,\n }),\n ),\n providers: Schema.Array(\n Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n methods: Schema.Array(MethodDescriptor),\n }),\n ),\n});\nexport type Snapshot = typeof Snapshot.Type;\n\n/**\n * How the client asks to connect. `values` is keyed by the provider's declared token fields, as\n * `Snapshot.providers[].methods[].fields` names them. Secrets are plaintext in transit and never\n * come back out.\n */\nexport class Token extends Schema.TaggedClass<Token>(\"@domainkit/server/Method/Token\")(\"Token\", {\n values: Schema.Record(Schema.String, Schema.String),\n}) {}\nexport class OAuth extends Schema.TaggedClass<OAuth>(\"@domainkit/server/Method/OAuth\")(\"OAuth\", {\n returnTo: Schema.optionalKey(Schema.String),\n}) {}\nexport class Integration extends Schema.TaggedClass<Integration>(\n \"@domainkit/server/Method/Integration\",\n)(\"Integration\", { returnTo: Schema.optionalKey(Schema.String) }) {}\nexport const Method = Schema.Union([Token, OAuth, Integration]);\nexport type Method = typeof Method.Type;\n\n/** A start without a domain connects the account alone; the customer picks a zone afterwards. */\nexport const StartPayload = Schema.Struct({\n domain: Schema.optionalKey(Schema.String),\n provider: Schema.String,\n method: Method,\n});\nexport type StartPayload = typeof StartPayload.Type;\n\n/** How the customer proves the account again. The provider is the connection's own. */\nexport const ReconnectPayload = Schema.Struct({ method: Method });\nexport type ReconnectPayload = typeof ReconnectPayload.Type;\n\n/** A zone the connection can serve, named for a customer to pick from. */\nexport class Candidate extends Schema.Class<Candidate>(\"@domainkit/server/Candidate\")({\n zone: Schema.String,\n label: Schema.String,\n}) {}\n\n/**\n * The connection the start produced. `label` names the account the way the customer will read it:\n * the attached zone's label, or the provider's name when the start carried no domain. `snapshot`\n * is the domain's state, and is null for a start that attached none.\n */\nexport class Connected extends Schema.TaggedClass<Connected>(\"@domainkit/server/Started/Connected\")(\n \"Connected\",\n {\n connectionId: Schema.String,\n provider: Schema.String,\n label: Schema.String,\n snapshot: Schema.NullOr(Snapshot),\n },\n) {}\nexport class Redirect extends Schema.TaggedClass<Redirect>(\"@domainkit/server/Started/Redirect\")(\n \"Redirect\",\n { authorizationUrl: Schema.String },\n) {}\nexport class SelectionRequired extends Schema.TaggedClass<SelectionRequired>(\n \"@domainkit/server/Started/SelectionRequired\",\n)(\"SelectionRequired\", { connectionId: Schema.String, candidates: Schema.Array(Candidate) }) {}\nexport const Started = Schema.Union([Connected, Redirect, SelectionRequired]);\nexport type Started = typeof Started.Type;\n\n/**\n * Which of the principal's connections already reaches a domain. The class names carry a\n * `Discovery` prefix because `Started` owns the unprefixed `SelectionRequired`; the wire tags are\n * the ones `Connect.Discovery` uses.\n */\nexport class DiscoveryResolved extends Schema.TaggedClass<DiscoveryResolved>(\n \"@domainkit/server/Discovery/Resolved\",\n)(\"Resolved\", { connectionId: Schema.String, zone: Schema.String, label: Schema.String }) {}\nexport class DiscoverySelectionRequired extends Schema.TaggedClass<DiscoverySelectionRequired>(\n \"@domainkit/server/Discovery/SelectionRequired\",\n)(\"SelectionRequired\", {\n candidates: Schema.Array(\n Schema.Struct({ connectionId: Schema.String, zone: Schema.String, label: Schema.String }),\n ),\n}) {}\n/** `host` names the registered provider whose nameservers serve the domain, by definition id. */\nexport class DiscoveryNotFound extends Schema.TaggedClass<DiscoveryNotFound>(\n \"@domainkit/server/Discovery/NotFound\",\n)(\"NotFound\", {\n nameservers: Schema.Array(Schema.String),\n host: Schema.NullOr(Schema.Struct({ provider: Schema.String })),\n}) {}\nexport const Discovery = Schema.Union([\n DiscoveryResolved,\n DiscoverySelectionRequired,\n DiscoveryNotFound,\n]);\nexport type Discovery = typeof Discovery.Type;\n\n/** One zone a connection reaches, named for a customer to pick from. */\nexport const Zone = Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n zone: Schema.String,\n label: Schema.String,\n nameservers: Schema.optionalKey(Schema.Array(Schema.String)),\n});\nexport type Zone = typeof Zone.Type;\n\n/**\n * Every zone the principal's connections reach, and where each connection stands. A connection\n * marked `reconnect` contributes no zones and needs the customer to authorize it again.\n */\nexport const Zones = Schema.Struct({\n zones: Schema.Array(Zone),\n connections: Schema.Array(\n Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n /** What the provider called the account at connect time; `null` when it named none. */\n label: Schema.NullOr(Schema.String),\n status: ConnectionStatus,\n }),\n ),\n /** Every provider a customer could connect, so a picker offers one without a second call. */\n providers: Schema.Array(\n Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n methods: Schema.Array(MethodDescriptor),\n }),\n ),\n});\nexport type Zones = typeof Zones.Type;\n\nexport const AttachPayload = Schema.Struct({\n domain: Schema.String,\n /** Which candidate zone to attach when `SelectionRequired` came back. */\n zone: Schema.optionalKey(Schema.String),\n});\nexport type AttachPayload = typeof AttachPayload.Type;\n\nexport const PlanPayload = Schema.Struct({\n requirements: Schema.Array(DnsRecord.Model),\n});\nexport type PlanPayload = typeof PlanPayload.Type;\n\nexport const ObservePayload = Schema.Struct({\n /** Records to observe instead of the attachment's receipt; required for an unattached domain. */\n requirements: Schema.optionalKey(Schema.Array(DnsRecord.Model)),\n});\nexport type ObservePayload = typeof ObservePayload.Type;\n\nexport const ApprovePayload = Schema.Struct({\n /** A subset of the plan's writes; omit to approve every write. */\n operationIds: Schema.optionalKey(Schema.Array(Plan.OperationId)),\n});\nexport type ApprovePayload = typeof ApprovePayload.Type;\n\nexport const RejectPayload = Schema.Struct({\n /** Why the customer declined, for the audit trail. */\n reason: Schema.optionalKey(Schema.String),\n});\nexport type RejectPayload = typeof RejectPayload.Type;\n\n/** One durable plan -> approval -> receipt lifecycle, as `GET /plans/:planId` returns it. */\nexport const Attempt = Schema.Struct({\n plan: Plan.Model,\n status: Storage.AttemptStatus,\n approval: Schema.NullOr(Approval.Model),\n receipt: Schema.NullOr(Receipt.Model),\n rejection: Schema.NullOr(Storage.Rejection),\n});\nexport type Attempt = typeof Attempt.Type;\n\n/** What a batch's create and resume routes take: one domain and its requirements per item. */\nexport const BatchPayload = Schema.Struct({\n items: Schema.Array(\n Schema.Struct({ domain: Schema.String, requirements: Schema.Array(DnsRecord.Model) }),\n ),\n});\nexport type BatchPayload = typeof BatchPayload.Type;\n\n/** The digest the principal read, which the batch's current plans must still produce. */\nexport const BatchApprovePayload = Schema.Struct({ digest: Plan.Digest });\nexport type BatchApprovePayload = typeof BatchApprovePayload.Type;\n\n/** One domain's place in a batch, with whatever its attempt holds. */\nexport const BatchItem = Schema.Struct({\n attachmentId: Schema.String,\n position: Schema.Number,\n /** Null while the item has no plan; `planFailure` then says why. */\n plan: Schema.NullOr(Plan.Model),\n status: Schema.NullOr(Storage.AttemptStatus),\n approval: Schema.NullOr(Approval.Model),\n receipt: Schema.NullOr(Receipt.Model),\n rejection: Schema.NullOr(Storage.Rejection),\n failure: Schema.NullOr(Schema.String),\n planFailure: Schema.NullOr(Schema.String),\n});\nexport type BatchItem = typeof BatchItem.Type;\n\n/** Many domains planned together, as every batch route returns them. */\nexport const Batch = Schema.Struct({\n id: Storage.BatchId,\n status: Storage.BatchStatus,\n /** What `POST /batches/:batchId/approvals` takes; null while any item is unplanned. */\n digest: Schema.NullOr(Plan.Digest),\n approval: Schema.NullOr(Storage.BatchApproval),\n rejection: Schema.NullOr(Storage.Rejection),\n createdAt: Schema.DateTimeUtcFromString,\n updatedAt: Schema.DateTimeUtcFromString,\n completedAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n items: Schema.Array(BatchItem),\n});\nexport type Batch = typeof Batch.Type;\n\n/** A batch without its plans, as the unfinished index returns them. */\nexport const BatchSummary = Schema.Struct({\n id: Storage.BatchId,\n status: Storage.BatchStatus,\n itemCount: Schema.Number,\n createdAt: Schema.DateTimeUtcFromString,\n updatedAt: Schema.DateTimeUtcFromString,\n completedAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n});\nexport type BatchSummary = typeof BatchSummary.Type;\n\n/** `Verify.Readiness` on the wire; timestamps encode as ISO strings. */\nexport const Readiness = Schema.Struct({\n domain: Schema.String,\n attachmentId: Schema.NullOr(Schema.String),\n overall: Storage.Overall,\n requirements: Schema.Array(\n Schema.Struct({\n /** `Verify.requirementKey(record)`, so a client pairs rows by content, not by position. */\n key: Schema.String,\n operationId: Schema.NullOr(Plan.OperationId),\n record: DnsRecord.Model,\n status: Storage.RequirementStatus,\n evidence: Schema.Array(Verify.Evidence),\n }),\n ),\n host: Schema.Array(Verify.HostEvidence),\n checkedAt: Schema.DateTimeUtcFromString,\n nextCheckAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n});\nexport type Readiness = typeof Readiness.Type;\n\nconst Redirected = HttpApiSchema.WithHeaders(HttpApiSchema.Empty(302), {\n location: Schema.String,\n});\n\n// ---------------------------------------------------------------------------------------------\n// Errors: one wire body, the status from `DomainKit.Error.httpStatus`\n// ---------------------------------------------------------------------------------------------\n\n/** `DomainKit.Error` narrowed to the reasons that answer with `status`. */\nconst errorAt = (status: number) =>\n Errors.DomainKitError.check(\n Schema.makeFilter((error: Errors.DomainKitError) => error.httpStatus === status),\n ).pipe(HttpApiSchema.status(status));\n\nconst errors = [400, 401, 403, 404, 409, 500, 501, 502, 503].map(errorAt);\n\n// ---------------------------------------------------------------------------------------------\n// The group\n// ---------------------------------------------------------------------------------------------\n\nexport const group = HttpApiGroup.make(\"domainkit\")\n .add(\n HttpApiEndpoint.get(\"inspect\", \"/domains/:domain\", {\n params: { domain: Schema.String },\n success: Snapshot,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"discover\", \"/domains/:domain/discovery\", {\n params: { domain: Schema.String },\n success: Discovery,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"zones\", \"/zones\", {\n query: { provider: Schema.optionalKey(Schema.String) },\n success: Zones,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"start\", \"/connections\", {\n payload: StartPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"callback\", \"/callback/:provider\", {\n params: { provider: Schema.String },\n // `Connect.complete` reads every callback parameter off the request URL, because a provider\n // may return `error`, `teamId`, or `configurationId` too. These two are declared so the\n // OpenAPI document names what a provider is expected to send back.\n query: { state: Schema.String, code: Schema.optionalKey(Schema.String) },\n success: Redirected,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"reconnect\", \"/connections/:connectionId/reconnections\", {\n params: { connectionId: Schema.String },\n payload: ReconnectPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"attach\", \"/connections/:connectionId/attachments\", {\n params: { connectionId: Schema.String },\n payload: AttachPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.delete(\"detach\", \"/attachments/:attachmentId\", {\n params: { attachmentId: Schema.String },\n success: Schema.Void,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.delete(\"disconnect\", \"/connections/:connectionId\", {\n params: { connectionId: Schema.String },\n success: Schema.Void,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"createPlan\", \"/domains/:domain/plans\", {\n params: { domain: Schema.String },\n payload: PlanPayload,\n success: Plan.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"approve\", \"/plans/:planId/approvals\", {\n params: { planId: Plan.PlanId },\n payload: ApprovePayload,\n success: Approval.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"reject\", \"/plans/:planId/rejections\", {\n params: { planId: Plan.PlanId },\n payload: RejectPayload,\n success: Attempt,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"apply\", \"/approvals/:approvalId/apply\", {\n params: { approvalId: Approval.ApprovalId },\n success: Receipt.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"plan\", \"/plans/:planId\", {\n params: { planId: Plan.PlanId },\n success: Attempt,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"receipt\", \"/receipts/:receiptId\", {\n params: { receiptId: Receipt.ReceiptId },\n success: Receipt.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"observe\", \"/domains/:domain/observations\", {\n params: { domain: Schema.String },\n payload: ObservePayload,\n success: Readiness,\n error: errors,\n }),\n )\n .add(\n // The stored observation, or `null` when nothing has been observed for the domain yet. A\n // domain with no readiness is not a missing route, so it answers 200 with a null body and\n // leaves 404 to name a domain this owner cannot reach.\n HttpApiEndpoint.get(\"readiness\", \"/domains/:domain/readiness\", {\n params: { domain: Schema.String },\n success: Schema.NullOr(Readiness),\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"cleanupPlan\", \"/receipts/:receiptId/cleanup-plans\", {\n params: { receiptId: Receipt.ReceiptId },\n success: Plan.Model,\n error: errors,\n }),\n )\n .add(\n // `Idempotency-Key` is the batch's identity for this owner: a retried create answers with the\n // batch the first call made rather than planning the same domains twice.\n HttpApiEndpoint.post(\"createBatch\", \"/batches\", {\n headers: { \"idempotency-key\": Schema.String },\n payload: BatchPayload,\n success: Batch,\n error: errors,\n }),\n )\n .add(\n // The one listing there is: what this owner still owes a move on. A finished batch is read by\n // its own id.\n HttpApiEndpoint.get(\"batches\", \"/batches\", {\n query: { unfinished: Schema.Literal(\"true\") },\n success: Schema.Array(BatchSummary),\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"batch\", \"/batches/:batchId\", {\n params: { batchId: Storage.BatchId },\n success: Batch,\n error: errors,\n }),\n )\n .add(\n // Plan the items that still have none. The batch stores pointers, not requirements, so the\n // host supplies them again.\n HttpApiEndpoint.post(\"planBatch\", \"/batches/:batchId/plans\", {\n params: { batchId: Storage.BatchId },\n payload: BatchPayload,\n success: Batch,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"approveBatch\", \"/batches/:batchId/approvals\", {\n params: { batchId: Storage.BatchId },\n payload: BatchApprovePayload,\n success: Batch,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"rejectBatch\", \"/batches/:batchId/rejections\", {\n params: { batchId: Storage.BatchId },\n payload: RejectPayload,\n success: Batch,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"applyBatch\", \"/batches/:batchId/apply\", {\n params: { batchId: Storage.BatchId },\n success: Batch,\n error: errors,\n }),\n );\n// Cleanup approve, reject, and apply reuse those routes: the attempt knows its kind.\n\nexport type Group = typeof group;\n\n/** Every route in the group, by name; what `Identity.authorize` is asked about. */\nexport type EndpointName = HttpApiGroup.Endpoints<Group>[\"identifier\"];\n\n/** Browser-safe summaries of an authenticated live callback. Connected is not DNS readiness. */\nexport const CallbackOutcome = Schema.Union([\n Schema.Struct({\n _tag: Schema.Literal(\"Connected\"),\n provider: Schema.String,\n connectionId: Schema.String,\n returnTo: Schema.String,\n }),\n Schema.Struct({\n _tag: Schema.Literal(\"Cancelled\"),\n provider: Schema.String,\n connectionId: Schema.optionalKey(Schema.String.check(Schema.isMinLength(1))),\n returnTo: Schema.String,\n }),\n Schema.Struct({\n _tag: Schema.Literal(\"Failed\"),\n provider: Schema.String,\n connectionId: Schema.optionalKey(Schema.String.check(Schema.isMinLength(1))),\n returnTo: Schema.String,\n recovery: Schema.Literals([\"restart\", \"inspect\"]),\n }),\n]);\nexport type CallbackOutcome = typeof CallbackOutcome.Type;\n\nexport interface Options {\n /**\n * Where the callback route is reachable from the provider, without the `/callback/:provider`\n * suffix. Defaults to the incoming request's origin and mount path.\n */\n readonly callbackBaseUrl?: string;\n /** Where interactive flows return when the callback carries no `returnTo`. */\n readonly defaultReturnTo?: string;\n /**\n * Select a same-origin destination, or return undefined to use the saved/default returnTo.\n * Default recovery redirects include CallbackRecovery hints; Connected clears those hints. Invoked\n * only for a verified live flow; no request, state, code, secrets, or storage rows reach this\n * policy. Close over acquired host services; its Effect requires no environment.\n * A policy failure after Connected cannot undo persistence: inspect before restarting.\n */\n readonly callback?: (\n outcome: CallbackOutcome,\n ) => Effect.Effect<string | undefined, Errors.DomainKitError>;\n}\n\n/** Offline registration requirements, not proof of provider console or grant readiness. */\nexport const RegistrationSettings = Schema.Union([\n Schema.Struct({\n _tag: Schema.Literal(\"OAuth\"),\n provider: Schema.String,\n callbackUrl: Schema.String,\n clientAuth: Schema.Literals([\"none\", \"client_secret_basic\", \"client_secret_post\"]),\n pkce: Schema.Literal(\"S256\"),\n scopes: Schema.Array(Schema.String),\n }),\n Schema.Struct({\n _tag: Schema.Literal(\"Integration\"),\n provider: Schema.String,\n callbackUrl: Schema.String,\n clientAuth: Schema.Literal(\"client_secret_post\"),\n }),\n]);\nexport type RegistrationSettings = typeof RegistrationSettings.Type;\n\n/** Derive only public setup values; no credentials, identity, storage, or network are needed. */\nexport const registrationSettings = (input: {\n readonly providers: ReadonlyArray<Provider.Definition>;\n readonly callbackBaseUrl: string;\n}): Effect.Effect<ReadonlyArray<RegistrationSettings>, Errors.DomainKitError> =>\n Effect.gen(function* () {\n yield* Configuration.endpoint(input.callbackBaseUrl, \"callbackBaseUrl\");\n const settings: Array<RegistrationSettings> = [];\n for (const definition of input.providers) {\n const callbackUrl = (yield* callbackUrlAt(input.callbackBaseUrl, definition.id)).toString();\n const oauth = definition.auth.oauth;\n const integration = definition.auth.integration;\n const unsupported = (method: string) =>\n Errors.fail(\n new Reason.Unsupported({\n provider: definition.id,\n operation: \"registrationSettings\",\n message: `${definition.id} ${method} has no registration metadata`,\n }),\n );\n if (oauth !== undefined) {\n if (oauth.registration === undefined) return yield* unsupported(\"OAuth\");\n yield* Configuration.scopes(oauth.scopes, \"oauth.scopes\");\n const { clientAuth, pkce } = oauth.registration;\n yield* Configuration.requireValue(\n clientAuth,\n (v) => v === \"none\" || v === \"client_secret_basic\" || v === \"client_secret_post\",\n \"oauth.clientAuth\",\n \"oauth.clientAuth is unsupported\",\n );\n yield* Configuration.requireValue(\n pkce,\n (v) => v === \"S256\",\n \"oauth.pkce\",\n \"oauth.pkce is unsupported\",\n );\n settings.push({\n _tag: \"OAuth\",\n provider: definition.id,\n callbackUrl,\n clientAuth,\n pkce,\n scopes: [...oauth.scopes],\n });\n }\n if (integration !== undefined) {\n if (integration.registration === undefined) return yield* unsupported(\"Integration\");\n yield* Configuration.requireValue(\n integration.registration.clientAuth,\n (v) => v === \"client_secret_post\",\n \"integration.clientAuth\",\n \"integration.clientAuth is unsupported\",\n );\n settings.push({\n _tag: \"Integration\",\n provider: definition.id,\n callbackUrl,\n clientAuth: integration.registration.clientAuth,\n });\n }\n }\n return settings;\n });\n\n/** Everything the handlers need: the lifecycle services, Storage, and the host's `Identity`. */\nexport type Services =\n | Provision.Service\n | Cleanup.Service\n | Connect.Service\n | Verify.Service\n | Providers.Service\n | Storage.Service\n | Identity;\n\n// ---------------------------------------------------------------------------------------------\n// Handlers\n// ---------------------------------------------------------------------------------------------\n\nconst START_ROUTE = \"/connections\";\nconst reconnectRoute = (connectionId: string) =>\n `/connections/${encodeURIComponent(connectionId)}/reconnections`;\nconst callbackRoute = (provider: string) => `/callback/${encodeURIComponent(provider)}`;\n\nconst invalid = (message: string, field?: string) =>\n Errors.fail(new Reason.InvalidInput({ message, ...(field === undefined ? {} : { field }) }));\n\n/** The request as an absolute URL: the web original when there is one, else scheme plus `Host`. */\nconst absoluteUrl = (\n request: HttpServerRequest.HttpServerRequest,\n): Effect.Effect<URL, Errors.DomainKitError> =>\n Effect.suspend(() => {\n try {\n return Effect.succeed(new URL(request.originalUrl));\n } catch {\n const url = HttpServerRequest.toURL(request);\n return Option.isNone(url)\n ? invalid(\"The request has no usable origin; set callbackBaseUrl\", \"callbackBaseUrl\")\n : Effect.succeed(url.value);\n }\n });\n\n/**\n * The absolute URL the provider redirects back to. `callbackBaseUrl` wins when the host set it,\n * because a proxy that rewrites `Host` leaves the request pointing at an origin the browser never\n * sees; otherwise it follows the prefix the group is mounted at, derived by stripping the handling\n * route's own path off the request.\n */\nconst callbackUrlFor = (input: {\n readonly request: HttpServerRequest.HttpServerRequest;\n readonly provider: string;\n readonly options: Options;\n /** The path of the route serving this request, stripped to find the mount. */\n readonly route: string;\n}): Effect.Effect<URL, Errors.DomainKitError> =>\n Effect.suspend(() => {\n const configured = input.options.callbackBaseUrl;\n if (configured !== undefined) {\n return callbackUrlAt(configured, input.provider);\n }\n return Effect.flatMap(absoluteUrl(input.request), (url) => {\n const path = url.pathname;\n if (!path.endsWith(input.route)) {\n return invalid(\n `The ${input.route} route is not mounted where it says; set callbackBaseUrl`,\n \"callbackBaseUrl\",\n );\n }\n return callbackUrlAt(\n `${url.origin}${path.slice(0, path.length - input.route.length)}`,\n input.provider,\n );\n });\n });\n\n/**\n * One refusal for every way a callback can fail to match a flow: no such `state`, one that has\n * expired, one presented at another provider's route, and one presented by a session that did not\n * start it. The text is constant on purpose — a caller holding a leaked `state` must not be able\n * to tell \"no such flow\" from \"not your flow\".\n */\nconst noSuchFlow = Callback.refuse;\n\nconst callbackUrlAt = (base: string, provider: string): Effect.Effect<URL, Errors.DomainKitError> =>\n Configuration.endpoint(base, \"callbackBaseUrl\").pipe(\n Effect.map((value) => {\n const url = new URL(value);\n url.pathname = `${url.pathname.replace(/\\/+$/, \"\")}${callbackRoute(provider)}`;\n return url;\n }),\n );\n\n/**\n * Where the callback may send the customer: a path under the callback's own base, or an absolute\n * URL on its origin. A destination that leaves the application is an open redirect, whoever\n * supplied it.\n */\nconst sameOrigin = (destination: string, callback: URL): string | null => {\n try {\n // Resolving against the callback is what makes this safe: the URL parser normalizes the forms\n // a browser would follow off-origin (`//host`, `/\\host`, `\\/host`, encoded hosts) into a real\n // origin. Blob URLs can embed that origin, so destinations must also use HTTP(S).\n const url = new URL(destination, callback);\n return (url.protocol === \"http:\" || url.protocol === \"https:\") && url.origin === callback.origin\n ? url.toString()\n : null;\n } catch {\n return null;\n }\n};\n\nconst snapshotOf = (snapshot: Connect.Snapshot): Snapshot => ({\n domain: snapshot.domain,\n attachment:\n snapshot.attachment === null\n ? null\n : { id: snapshot.attachment.id, label: snapshot.attachment.label },\n connectionId: snapshot.connection?.id ?? null,\n provider: snapshot.authorization?.provider ?? null,\n method: snapshot.authorization?.method ?? null,\n status:\n snapshot.connection === null\n ? \"disconnected\"\n : snapshot.authorization?.revocation === \"active\"\n ? \"connected\"\n : \"reconnect\",\n lastReceiptId: snapshot.lastReceiptId,\n connectionDomains: snapshot.connectionDomains,\n reusable: snapshot.reusable.map(({ connection, provider, method }) => ({\n connectionId: connection.id,\n provider,\n method,\n })),\n providers: snapshot.providers.map(({ id, name, methods }) => ({ id, name, methods })),\n});\n\n/** The wire method as `Connect` takes it; interactive cases carry the caller's return destination. */\nconst methodOf = (method: Method): Connect.Method => {\n switch (method._tag) {\n case \"Token\":\n return Connect.Method.token(method.values);\n case \"OAuth\":\n return Connect.Method.oauth(\n method.returnTo === undefined ? {} : { returnTo: method.returnTo },\n );\n case \"Integration\":\n return Connect.Method.integration(\n method.returnTo === undefined ? {} : { returnTo: method.returnTo },\n );\n }\n};\n\nconst candidatesOf = (\n candidates: ReadonlyArray<{ readonly zone: string; readonly label: string }>,\n): ReadonlyArray<Candidate> => candidates.map(({ zone, label }) => new Candidate({ zone, label }));\n\n/**\n * Handlers for `group` inside the host's API. Every handler derives `Principal` from `Identity`\n * for the request it is serving and provides it to the lifecycle services.\n */\nexport const layer = <ApiId extends string, Groups extends HttpApiGroup.Constraint>(\n api: HttpApi.HttpApi<ApiId, Groups>,\n options: Options = {},\n): Layer.Layer<HttpApiGroup.Service<ApiId, \"domainkit\">, never, Services> =>\n // The host's API carries its own groups beside this one; only `\"domainkit\"` is implemented here,\n // and `HttpApiBuilder.group` needs the group's own endpoint types to check the handlers.\n HttpApiBuilder.group(api as unknown as HttpApi.HttpApi<ApiId, Group>, \"domainkit\", (handlers) =>\n Effect.gen(function* () {\n const identity = yield* Identity;\n const connect = yield* Connect.Service;\n const provision = yield* Provision.Service;\n const cleanup = yield* Cleanup.Service;\n const verify = yield* Verify.Service;\n const storage = yield* Storage.Service;\n const providers = yield* Providers.Service;\n\n /**\n * Run a lifecycle effect as the principal the host derives from this request, once the host\n * has allowed that principal onto this route.\n */\n const as = <A>(\n endpoint: EndpointName,\n request: HttpServerRequest.HttpServerRequest,\n effect: Effect.Effect<A, Errors.DomainKitError, Principal.Service>,\n context?: IdentityContext,\n ): Effect.Effect<A, Errors.DomainKitError> =>\n Effect.flatMap(identity.principal(request, context), (principal) =>\n Effect.flatMap(identity.authorize?.(principal, endpoint) ?? Effect.void, () =>\n Effect.provideService(effect, Principal.Service, principal),\n ),\n );\n\n const snapshot = (domain: string) => Effect.map(connect.inspect(domain), snapshotOf);\n\n const discovered = (discovery: Connect.Discovery): Discovery => {\n switch (discovery._tag) {\n case \"Resolved\":\n return new DiscoveryResolved({\n connectionId: discovery.connectionId,\n zone: discovery.target.zone,\n label: discovery.target.label,\n });\n case \"SelectionRequired\":\n return new DiscoverySelectionRequired({\n candidates: discovery.candidates.map(({ connectionId, target }) => ({\n connectionId,\n zone: target.zone,\n label: target.label,\n })),\n });\n case \"NotFound\":\n return new DiscoveryNotFound({\n nameservers: discovery.nameservers,\n host: discovery.host,\n });\n }\n };\n\n /**\n * The connection a start or an attach produced. `label` is the zone the attachment named,\n * or the provider itself where the connection carries no domain yet.\n */\n const connected = (input: {\n readonly connection: Storage.Connection;\n readonly attachment: Storage.Attachment | null;\n readonly domain: string | undefined;\n }): Effect.Effect<Connected, Errors.DomainKitError, Principal.Service> =>\n Effect.gen(function* () {\n const authorization = yield* storage.authorizations.get(input.connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n return new Connected({\n connectionId: input.connection.id,\n provider: authorization.provider,\n label: input.attachment?.label ?? authorization.label ?? definition.name,\n snapshot: input.domain === undefined ? null : yield* snapshot(input.domain),\n });\n });\n\n const startedOf = (\n started: Connect.Started,\n domain: string | undefined,\n ): Effect.Effect<Started, Errors.DomainKitError, Principal.Service> => {\n switch (started._tag) {\n case \"Connected\":\n return connected({\n attachment: started.attachment,\n connection: started.connection,\n domain,\n });\n case \"Redirect\":\n return Effect.succeed(new Redirect({ authorizationUrl: started.authorizationUrl }));\n case \"SelectionRequired\":\n return Effect.succeed(\n new SelectionRequired({\n connectionId: started.connection.id,\n candidates: candidatesOf(started.candidates),\n }),\n );\n }\n };\n\n /**\n * Attach with a zone the client picked out of a `SelectionRequired`. The provider target\n * behind a zone never crosses the wire, so resolve the candidates again and match by zone.\n */\n const attachAt = (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string | undefined;\n }): Effect.Effect<Started, Errors.DomainKitError, Principal.Service> =>\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(input.connectionId);\n const attached = (attachment: Storage.Attachment) =>\n connected({ attachment, connection, domain: input.domain });\n const first = yield* connect.attach({\n connectionId: input.connectionId,\n domain: input.domain,\n });\n if (!(\"_tag\" in first)) return yield* attached(first);\n if (input.zone === undefined) {\n return new SelectionRequired({\n connectionId: input.connectionId,\n candidates: candidatesOf(first.candidates),\n });\n }\n const target = first.candidates.find(({ zone }) => zone === input.zone);\n if (target === undefined) {\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: input.zone }));\n }\n const second = yield* connect.attach({\n connectionId: input.connectionId,\n domain: input.domain,\n target,\n });\n return \"_tag\" in second\n ? yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: input.zone }))\n : yield* attached(second);\n });\n\n /** Provisioning and cleanup share the approve and apply routes; the attempt knows its kind. */\n const kindOf = (attempt: Storage.Attempt) =>\n attempt.kind === \"cleanup\" ? cleanup : provision;\n\n return handlers\n .handle(\"inspect\", ({ params, request }) => as(\"inspect\", request, snapshot(params.domain)))\n .handle(\"discover\", ({ params, request }) =>\n as(\"discover\", request, Effect.map(connect.discover(params.domain), discovered)),\n )\n .handle(\"zones\", ({ query, request }) =>\n as(\n \"zones\",\n request,\n Effect.map(\n connect.zones(query.provider === undefined ? {} : { provider: query.provider }),\n (listing): Zones => ({\n zones: listing.zones.map(({ connectionId, provider, target }) => ({\n connectionId,\n provider,\n zone: target.zone,\n label: target.label,\n ...(target.nameservers === undefined ? {} : { nameservers: target.nameservers }),\n })),\n connections: listing.connections,\n providers: listing.providers,\n }),\n ),\n ),\n )\n .handle(\"start\", ({ payload, request }) =>\n as(\n \"start\",\n request,\n Effect.gen(function* () {\n // Token methods connect in one call; the interactive ones need somewhere to land.\n const callbackUrl =\n payload.method._tag === \"Token\"\n ? undefined\n : (yield* callbackUrlFor({\n request,\n provider: payload.provider,\n options,\n route: START_ROUTE,\n })).toString();\n const started = yield* connect.start({\n provider: payload.provider,\n ...(payload.domain === undefined ? {} : { domain: payload.domain }),\n method: methodOf(payload.method),\n ...(callbackUrl === undefined ? {} : { callbackUrl }),\n });\n return yield* startedOf(started, payload.domain);\n }),\n ),\n )\n .handle(\"reconnect\", ({ params, payload, request }) =>\n as(\n \"reconnect\",\n request,\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(params.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const callbackUrl =\n payload.method._tag === \"Token\"\n ? undefined\n : (yield* callbackUrlFor({\n request,\n provider: authorization.provider,\n options,\n route: reconnectRoute(params.connectionId),\n })).toString();\n const started = yield* connect.reconnect({\n connectionId: params.connectionId,\n method: methodOf(payload.method),\n ...(callbackUrl === undefined ? {} : { callbackUrl }),\n });\n return yield* startedOf(started, undefined);\n }),\n ),\n )\n .handle(\"callback\", ({ params, query, request }) =>\n Effect.gen(function* () {\n // The one route that establishes its identity from something other than the request.\n // A provider callback is a top-level navigation carrying a `state` the provider echoed\n // back and whatever credential the browser attaches by itself, so the flow DomainKit\n // recorded is the only authority on whose it is. Read its header first — a read, never\n // a spend, so a leaked `state` cannot burn the flow it names — tell the host which\n // flow is being finished, and then hold the host's answer against the record.\n const header = yield* storage.continuations\n .header(query.state)\n .pipe(\n Effect.catch((error) =>\n Callback.missing(error) ? Effect.succeed(null) : Effect.fail(error),\n ),\n );\n const continuation =\n header !== null && header.provider === params.provider ? header : null;\n // The host authenticates even when there is nothing to finish, so the status cannot\n // tell a caller whether the `state` it holds names a flow at all.\n return yield* as(\n \"callback\",\n request,\n Effect.gen(function* () {\n // The owner comes from the record and the actor from the host's own session;\n // nothing in the query string contributes either. The scoped `get` below keeps\n // its owner filter, so a host that answers with the wrong principal still fails\n // closed even if this check were ever removed.\n const principal = yield* Principal.Service;\n if (\n continuation === null ||\n principal.ownerId !== continuation.ownerId ||\n principal.actorId !== continuation.actorId\n ) {\n return yield* noSuchFlow;\n }\n const url = yield* absoluteUrl(request);\n // Resolve the destination against the callback's public base, not the request: a\n // proxy that rewrites `Host` leaves the request on an origin the browser never\n // sees.\n const base = yield* callbackUrlFor({\n request,\n provider: params.provider,\n options,\n route: callbackRoute(params.provider),\n });\n // Where the customer lands comes from the continuation this owner started, never\n // from the provider's query string, and it is resolved before the callback is\n // spent: a server with nowhere to send them must not connect the provider and\n // then fail.\n // The host's authentication sits between the header read and this one, so the\n // flow can expire or be spent in that window. It refuses the same way it would\n // have a moment earlier, rather than changing shape because of when it happened.\n // This is also where the owner filter fails closed when a host resolves the wrong\n // principal, and that refusal must look like every other one.\n const flow = yield* storage.continuations\n .get(query.state)\n .pipe(\n Effect.catch((error) =>\n Callback.missing(error) ? noSuchFlow : Effect.fail(error),\n ),\n );\n const requested = flow.returnTo ?? options.defaultReturnTo;\n if (requested === undefined) {\n return yield* invalid(\n \"The flow carried no returnTo and the server has no defaultReturnTo\",\n \"returnTo\",\n );\n }\n const destination = sameOrigin(requested, base);\n if (destination === null) {\n return yield* invalid(`${requested} leaves this application`, \"returnTo\");\n }\n // Re-read within the completion lock before any provider parameters are classified.\n const outcome = yield* connect\n .completeOutcome({\n continuationId: query.state,\n callbackUrl: url.toString(),\n provider: params.provider,\n })\n .pipe(\n Effect.catch((error) =>\n Callback.missing(error) ? noSuchFlow : Effect.fail(error),\n ),\n );\n const summary: CallbackOutcome =\n outcome._tag === \"Connected\"\n ? {\n _tag: \"Connected\",\n provider: flow.provider,\n connectionId: outcome.started.connection.id,\n returnTo: destination,\n }\n : outcome._tag === \"Cancelled\"\n ? {\n _tag: \"Cancelled\",\n provider: flow.provider,\n returnTo: destination,\n ...(outcome.connectionId === undefined\n ? {}\n : { connectionId: outcome.connectionId }),\n }\n : {\n _tag: \"Failed\",\n provider: flow.provider,\n returnTo: destination,\n recovery: outcome.recovery,\n ...(outcome.connectionId === undefined\n ? {}\n : { connectionId: outcome.connectionId }),\n };\n if (outcome._tag !== \"Connected\") {\n const attributes = {\n \"domainkit.callback.outcome\": outcome._tag,\n \"domainkit.callback.provider\": flow.provider,\n \"domainkit.callback.reason\": outcome.error.reason._tag,\n ...(outcome._tag === \"Failed\"\n ? { \"domainkit.callback.recovery\": outcome.recovery }\n : {}),\n };\n yield* Effect.annotateCurrentSpan(attributes);\n const log = outcome._tag === \"Cancelled\" ? Effect.logInfo : Effect.logWarning;\n yield* log(\"DomainKit callback recovery\").pipe(Effect.annotateLogs(attributes));\n }\n const selected =\n options.callback === undefined ? undefined : yield* options.callback(summary);\n const location =\n selected !== undefined\n ? sameOrigin(selected, base)\n : summary._tag === \"Connected\"\n ? CallbackRecovery.write(new URL(destination), null).toString()\n : CallbackRecovery.write(new URL(destination), {\n kind: summary._tag === \"Cancelled\" ? \"cancelled\" : summary.recovery,\n provider: summary.provider,\n ...(summary.connectionId === undefined\n ? {}\n : { connectionId: summary.connectionId }),\n }).toString();\n if (location === null)\n return yield* invalid(\"callback destination leaves this application\", \"callback\");\n return HttpApiSchema.withHeaders({\n body: undefined as void,\n headers: { location },\n });\n }),\n continuation === null ? undefined : { endpoint: \"callback\", continuation },\n );\n }),\n )\n .handle(\"attach\", ({ params, payload, request }) =>\n as(\n \"attach\",\n request,\n attachAt({\n connectionId: params.connectionId,\n domain: payload.domain,\n zone: payload.zone,\n }),\n ),\n )\n .handle(\"detach\", ({ params, request }) =>\n as(\"detach\", request, connect.detach(params.attachmentId)),\n )\n .handle(\"disconnect\", ({ params, request }) =>\n as(\"disconnect\", request, connect.disconnect(params.connectionId)),\n )\n .handle(\"createPlan\", ({ params, payload, request }) =>\n as(\n \"createPlan\",\n request,\n provision.plan({ domain: params.domain, requirements: payload.requirements }),\n ),\n )\n .handle(\"approve\", ({ params, payload, request }) =>\n as(\n \"approve\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.get(params.planId);\n return yield* kindOf(attempt).approve(params.planId, {\n ...(payload.operationIds === undefined\n ? {}\n : { operationIds: payload.operationIds, allowPartial: true }),\n });\n }),\n ),\n )\n .handle(\"reject\", ({ params, payload, request }) =>\n as(\n \"reject\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.get(params.planId);\n return yield* kindOf(attempt).reject(params.planId, {\n ...(payload.reason === undefined ? {} : { reason: payload.reason }),\n });\n }),\n ),\n )\n .handle(\"apply\", ({ params, request }) =>\n as(\n \"apply\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.byApproval(params.approvalId);\n return yield* kindOf(attempt).apply(params.approvalId);\n }),\n ),\n )\n .handle(\"plan\", ({ params, request }) =>\n as(\n \"plan\",\n request,\n Effect.map(storage.attempts.get(params.planId), (attempt) => ({\n plan: attempt.plan,\n status: attempt.status,\n approval: attempt.approval,\n receipt: attempt.receipt,\n rejection: attempt.rejection,\n })),\n ),\n )\n .handle(\"receipt\", ({ params, request }) =>\n as(\n \"receipt\",\n request,\n Effect.flatMap(storage.attempts.byReceipt(params.receiptId), (attempt) =>\n attempt.receipt === null\n ? Errors.fail(new Reason.NotFound({ entity: \"receipt\", id: params.receiptId }))\n : Effect.succeed(attempt.receipt),\n ),\n ),\n )\n .handle(\"observe\", ({ params, payload, request }) =>\n as(\n \"observe\",\n request,\n verify.observe({\n domain: params.domain,\n ...(payload.requirements === undefined ? {} : { requirements: payload.requirements }),\n }),\n ),\n )\n .handle(\"readiness\", ({ params, request }) =>\n as(\"readiness\", request, verify.latest(params.domain)),\n )\n .handle(\"cleanupPlan\", ({ params, request }) =>\n as(\"cleanupPlan\", request, cleanup.plan({ receiptId: params.receiptId })),\n )\n .handle(\"createBatch\", ({ headers, payload, request }) =>\n as(\n \"createBatch\",\n request,\n provision.batch.create({\n items: payload.items,\n idempotencyKey: headers[\"idempotency-key\"],\n }),\n ),\n )\n .handle(\"batches\", ({ request }) =>\n as(\"batches\", request, provision.batch.list({ unfinished: true })),\n )\n .handle(\"batch\", ({ params, request }) =>\n as(\"batch\", request, provision.batch.get(params.batchId)),\n )\n .handle(\"planBatch\", ({ params, payload, request }) =>\n as(\n \"planBatch\",\n request,\n provision.batch.resumePlanning(params.batchId, { items: payload.items }),\n ),\n )\n .handle(\"approveBatch\", ({ params, payload, request }) =>\n as(\n \"approveBatch\",\n request,\n provision.batch.approve(params.batchId, { digest: payload.digest }),\n ),\n )\n .handle(\"rejectBatch\", ({ params, payload, request }) =>\n as(\n \"rejectBatch\",\n request,\n provision.batch.reject(params.batchId, {\n ...(payload.reason === undefined ? {} : { reason: payload.reason }),\n }),\n ),\n )\n .handle(\"applyBatch\", ({ params, request }) =>\n as(\"applyBatch\", request, provision.batch.apply(params.batchId)),\n );\n }),\n ) as Layer.Layer<HttpApiGroup.Service<ApiId, \"domainkit\">, never, Services>;\n\n/** A standalone API for hosts not on HttpApi. Mount at any prefix. */\nexport const api: HttpApi.HttpApi<\"domainkit\", Group> = HttpApi.make(\"domainkit\").add(group);\n\nexport interface WebHandlerOptions extends Options {\n /** Mount every route under this path, e.g. `/api/domainkit`. */\n readonly prefix?: `/${string}`;\n}\n\n/** The Promise edge: a `fetch`-shaped handler over the group for hosts that are not Effect-native. */\nexport const toWebHandler = (\n services: Layer.Layer<Services, Errors.DomainKitError>,\n options: WebHandlerOptions = {},\n): {\n readonly handler: (request: Request) => Promise<Response>;\n readonly dispose: () => Promise<void>;\n} => {\n // `group.prefix` widens the endpoint paths in the type, which `Group` states unprefixed; the\n // handlers are keyed by endpoint identifier, so the routes still line up.\n const prefixed =\n options.prefix === undefined\n ? api\n : (HttpApi.make(\"domainkit\").add(group.prefix(options.prefix)) as unknown as HttpApi.HttpApi<\n \"domainkit\",\n Group\n >);\n const app = HttpApiBuilder.layer(prefixed).pipe(\n Layer.provide(layer(prefixed, options)),\n Layer.provide(services),\n Layer.provide(HttpServer.layerServices),\n );\n return HttpRouter.toWebHandler(app, { disableLogger: true });\n};\n"],"mappings":"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"}
|
package/dist/entry/client.mjs
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
import { CallbackRecovery_exports } from "../CallbackRecovery.mjs";
|
|
2
|
+
import { Transport_exports } from "../Transport.mjs";
|
|
3
|
+
export { CallbackRecovery_exports as CallbackRecovery, Transport_exports as Transport };
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { DnsRecord_exports } from "./DnsRecord.mjs";
|
|
|
3
3
|
import { Plan_exports } from "./Plan.mjs";
|
|
4
4
|
import { Reason_exports } from "./Reason.mjs";
|
|
5
5
|
import { Approval_exports } from "./Approval.mjs";
|
|
6
|
+
import { CallbackRecovery_exports } from "./CallbackRecovery.mjs";
|
|
6
7
|
import { Custody_exports } from "./Custody.mjs";
|
|
7
8
|
import { Principal_exports } from "./Principal.mjs";
|
|
8
9
|
import { Provider_exports } from "./Provider.mjs";
|
|
@@ -26,7 +27,7 @@ import { version } from "./package.mjs";
|
|
|
26
27
|
* Lifecycle services: Connect, Provision, Cleanup, Verify.
|
|
27
28
|
* Host seams: Storage, Custody, Principal.
|
|
28
29
|
* Providers: Provider, Providers, Cloudflare, Vercel.
|
|
29
|
-
* Values: DomainName, DnsRecord, Plan, Approval, Receipt.
|
|
30
|
+
* Values: DomainName, DnsRecord, Plan, Approval, Receipt, CallbackRecovery.
|
|
30
31
|
* Everything else: Reason, Resolver, DomainKit (the composed layer and `DomainKit.Error`).
|
|
31
32
|
*
|
|
32
33
|
* Subpath: domainkit/testing.
|
|
@@ -34,6 +35,6 @@ import { version } from "./package.mjs";
|
|
|
34
35
|
/** The current package version from the package manifest. */
|
|
35
36
|
const VERSION = version;
|
|
36
37
|
//#endregion
|
|
37
|
-
export { Approval_exports as Approval, Cleanup_exports as Cleanup, Cloudflare_exports as Cloudflare, Connect_exports as Connect, Custody_exports as Custody, DnsRecord_exports as DnsRecord, DomainKit_exports as DomainKit, DomainName_exports as DomainName, Plan_exports as Plan, Principal_exports as Principal, Provider_exports as Provider, Providers_exports as Providers, Provision_exports as Provision, Reason_exports as Reason, Receipt_exports as Receipt, Resolver_exports as Resolver, Storage_exports as Storage, VERSION, Vercel_exports as Vercel, Verify_exports as Verify };
|
|
38
|
+
export { Approval_exports as Approval, CallbackRecovery_exports as CallbackRecovery, Cleanup_exports as Cleanup, Cloudflare_exports as Cloudflare, Connect_exports as Connect, Custody_exports as Custody, DnsRecord_exports as DnsRecord, DomainKit_exports as DomainKit, DomainName_exports as DomainName, Plan_exports as Plan, Principal_exports as Principal, Provider_exports as Provider, Providers_exports as Providers, Provision_exports as Provision, Reason_exports as Reason, Receipt_exports as Receipt, Resolver_exports as Resolver, Storage_exports as Storage, VERSION, Vercel_exports as Vercel, Verify_exports as Verify };
|
|
38
39
|
|
|
39
40
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["packageJson.version"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * domainkit — the Effect-native root. One module per concept: services expose `Service` (the tag)\n * and `Interface` (its shape), value modules expose `Model` (the schema).\n *\n * Lifecycle services: Connect, Provision, Cleanup, Verify.\n * Host seams: Storage, Custody, Principal.\n * Providers: Provider, Providers, Cloudflare, Vercel.\n * Values: DomainName, DnsRecord, Plan, Approval, Receipt.\n * Everything else: Reason, Resolver, DomainKit (the composed layer and `DomainKit.Error`).\n *\n * Subpath: domainkit/testing.\n */\nimport packageJson from \"../package.json\" with { type: \"json\" };\n\n/** The current package version from the package manifest. */\nexport const VERSION = packageJson.version;\n\nexport * as Approval from \"./Approval.ts\";\nexport * as Cleanup from \"./Cleanup.ts\";\nexport * as Cloudflare from \"./Cloudflare.ts\";\nexport * as Connect from \"./Connect.ts\";\nexport * as Custody from \"./Custody.ts\";\nexport * as DnsRecord from \"./DnsRecord.ts\";\nexport * as DomainKit from \"./DomainKit.ts\";\nexport * as DomainName from \"./DomainName.ts\";\nexport * as Plan from \"./Plan.ts\";\nexport * as Principal from \"./Principal.ts\";\nexport * as Provider from \"./Provider.ts\";\nexport * as Providers from \"./Providers.ts\";\nexport * as Provision from \"./Provision.ts\";\nexport * as Reason from \"./Reason.ts\";\nexport * as Receipt from \"./Receipt.ts\";\nexport * as Resolver from \"./Resolver.ts\";\nexport * as Storage from \"./Storage.ts\";\nexport * as Vercel from \"./Vercel.ts\";\nexport * as Verify from \"./Verify.ts\";\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["packageJson.version"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * domainkit — the Effect-native root. One module per concept: services expose `Service` (the tag)\n * and `Interface` (its shape), value modules expose `Model` (the schema).\n *\n * Lifecycle services: Connect, Provision, Cleanup, Verify.\n * Host seams: Storage, Custody, Principal.\n * Providers: Provider, Providers, Cloudflare, Vercel.\n * Values: DomainName, DnsRecord, Plan, Approval, Receipt, CallbackRecovery.\n * Everything else: Reason, Resolver, DomainKit (the composed layer and `DomainKit.Error`).\n *\n * Subpath: domainkit/testing.\n */\nimport packageJson from \"../package.json\" with { type: \"json\" };\n\n/** The current package version from the package manifest. */\nexport const VERSION = packageJson.version;\n\nexport * as Approval from \"./Approval.ts\";\nexport * as CallbackRecovery from \"./CallbackRecovery.ts\";\nexport * as Cleanup from \"./Cleanup.ts\";\nexport * as Cloudflare from \"./Cloudflare.ts\";\nexport * as Connect from \"./Connect.ts\";\nexport * as Custody from \"./Custody.ts\";\nexport * as DnsRecord from \"./DnsRecord.ts\";\nexport * as DomainKit from \"./DomainKit.ts\";\nexport * as DomainName from \"./DomainName.ts\";\nexport * as Plan from \"./Plan.ts\";\nexport * as Principal from \"./Principal.ts\";\nexport * as Provider from \"./Provider.ts\";\nexport * as Providers from \"./Providers.ts\";\nexport * as Provision from \"./Provision.ts\";\nexport * as Reason from \"./Reason.ts\";\nexport * as Receipt from \"./Receipt.ts\";\nexport * as Resolver from \"./Resolver.ts\";\nexport * as Storage from \"./Storage.ts\";\nexport * as Vercel from \"./Vercel.ts\";\nexport * as Verify from \"./Verify.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,MAAa,UAAUA"}
|
package/dist/package.mjs
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Browser-safe recovery hints for returning from an authenticated provider callback. */
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
export declare const Model: Schema.Struct<{
|
|
4
|
+
readonly kind: Schema.Literals<readonly ["cancelled", "restart", "inspect"]>;
|
|
5
|
+
readonly provider: Schema.String;
|
|
6
|
+
readonly connectionId: Schema.optionalKey<Schema.String>;
|
|
7
|
+
}>;
|
|
8
|
+
export type Model = typeof Model.Type;
|
|
9
|
+
/** Reads a complete recovery hint; ignores missing, duplicate, or invalid metadata. */
|
|
10
|
+
export declare const read: (url: URL) => Model | null;
|
|
11
|
+
/** Clones the URL and replaces or clears all recovery hints, preserving other parameters and hash. */
|
|
12
|
+
export declare const write: (url: URL, outcome: Model | null) => URL;
|
package/dist/types/Connect.d.ts
CHANGED
|
@@ -242,16 +242,19 @@ export type Completion = Data.TaggedEnum<{
|
|
|
242
242
|
};
|
|
243
243
|
Cancelled: {
|
|
244
244
|
readonly error: Errors.DomainKitError;
|
|
245
|
+
readonly connectionId?: string;
|
|
245
246
|
};
|
|
246
247
|
Failed: {
|
|
247
248
|
readonly error: Errors.DomainKitError;
|
|
248
249
|
readonly recovery: "restart" | "inspect";
|
|
250
|
+
readonly connectionId?: string;
|
|
249
251
|
};
|
|
250
252
|
}>;
|
|
251
253
|
export declare const Completion: {
|
|
252
254
|
readonly $is: <Tag extends "Cancelled" | "Connected" | "Failed">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
253
255
|
readonly _tag: "Cancelled";
|
|
254
256
|
readonly error: Errors.DomainKitError;
|
|
257
|
+
readonly connectionId?: string;
|
|
255
258
|
}, {
|
|
256
259
|
readonly _tag: Tag;
|
|
257
260
|
}> | Extract<{
|
|
@@ -265,6 +268,7 @@ export declare const Completion: {
|
|
|
265
268
|
readonly _tag: "Failed";
|
|
266
269
|
readonly error: Errors.DomainKitError;
|
|
267
270
|
readonly recovery: "restart" | "inspect";
|
|
271
|
+
readonly connectionId?: string;
|
|
268
272
|
}, {
|
|
269
273
|
readonly _tag: Tag;
|
|
270
274
|
}>;
|
|
@@ -272,6 +276,7 @@ export declare const Completion: {
|
|
|
272
276
|
<Cases extends { readonly [Tag in "Cancelled" | "Connected" | "Failed"]: (args: Extract<{
|
|
273
277
|
readonly _tag: "Cancelled";
|
|
274
278
|
readonly error: Errors.DomainKitError;
|
|
279
|
+
readonly connectionId?: string;
|
|
275
280
|
} | {
|
|
276
281
|
readonly _tag: "Connected";
|
|
277
282
|
readonly started: Exclude<Started, {
|
|
@@ -281,11 +286,13 @@ export declare const Completion: {
|
|
|
281
286
|
readonly _tag: "Failed";
|
|
282
287
|
readonly error: Errors.DomainKitError;
|
|
283
288
|
readonly recovery: "restart" | "inspect";
|
|
289
|
+
readonly connectionId?: string;
|
|
284
290
|
}, {
|
|
285
291
|
readonly _tag: Tag;
|
|
286
292
|
}>) => any; }>(cases: Cases): (value: {
|
|
287
293
|
readonly _tag: "Cancelled";
|
|
288
294
|
readonly error: Errors.DomainKitError;
|
|
295
|
+
readonly connectionId?: string;
|
|
289
296
|
} | {
|
|
290
297
|
readonly _tag: "Connected";
|
|
291
298
|
readonly started: Exclude<Started, {
|
|
@@ -295,10 +302,12 @@ export declare const Completion: {
|
|
|
295
302
|
readonly _tag: "Failed";
|
|
296
303
|
readonly error: Errors.DomainKitError;
|
|
297
304
|
readonly recovery: "restart" | "inspect";
|
|
305
|
+
readonly connectionId?: string;
|
|
298
306
|
}) => import("effect/Unify").Unify<ReturnType<Cases["Cancelled" | "Connected" | "Failed"]>>;
|
|
299
307
|
<Cases extends { readonly [Tag in "Cancelled" | "Connected" | "Failed"]: (args: Extract<{
|
|
300
308
|
readonly _tag: "Cancelled";
|
|
301
309
|
readonly error: Errors.DomainKitError;
|
|
310
|
+
readonly connectionId?: string;
|
|
302
311
|
} | {
|
|
303
312
|
readonly _tag: "Connected";
|
|
304
313
|
readonly started: Exclude<Started, {
|
|
@@ -308,11 +317,13 @@ export declare const Completion: {
|
|
|
308
317
|
readonly _tag: "Failed";
|
|
309
318
|
readonly error: Errors.DomainKitError;
|
|
310
319
|
readonly recovery: "restart" | "inspect";
|
|
320
|
+
readonly connectionId?: string;
|
|
311
321
|
}, {
|
|
312
322
|
readonly _tag: Tag;
|
|
313
323
|
}>) => any; }>(value: {
|
|
314
324
|
readonly _tag: "Cancelled";
|
|
315
325
|
readonly error: Errors.DomainKitError;
|
|
326
|
+
readonly connectionId?: string;
|
|
316
327
|
} | {
|
|
317
328
|
readonly _tag: "Connected";
|
|
318
329
|
readonly started: Exclude<Started, {
|
|
@@ -322,11 +333,13 @@ export declare const Completion: {
|
|
|
322
333
|
readonly _tag: "Failed";
|
|
323
334
|
readonly error: Errors.DomainKitError;
|
|
324
335
|
readonly recovery: "restart" | "inspect";
|
|
336
|
+
readonly connectionId?: string;
|
|
325
337
|
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Cancelled" | "Connected" | "Failed"]>>;
|
|
326
338
|
};
|
|
327
339
|
readonly Cancelled: Data.TaggedEnum.ConstructorFrom<{
|
|
328
340
|
readonly _tag: "Cancelled";
|
|
329
341
|
readonly error: Errors.DomainKitError;
|
|
342
|
+
readonly connectionId?: string;
|
|
330
343
|
}, "_tag">;
|
|
331
344
|
readonly Connected: Data.TaggedEnum.ConstructorFrom<{
|
|
332
345
|
readonly _tag: "Connected";
|
|
@@ -338,6 +351,7 @@ export declare const Completion: {
|
|
|
338
351
|
readonly _tag: "Failed";
|
|
339
352
|
readonly error: Errors.DomainKitError;
|
|
340
353
|
readonly recovery: "restart" | "inspect";
|
|
354
|
+
readonly connectionId?: string;
|
|
341
355
|
}, "_tag">;
|
|
342
356
|
};
|
|
343
357
|
/** `attach` either attached the domain or needs the caller to pick a zone. */
|
|
@@ -710,6 +724,7 @@ export declare const completeOutcome: (input: {
|
|
|
710
724
|
}) => Effect.Effect<{
|
|
711
725
|
readonly _tag: "Cancelled";
|
|
712
726
|
readonly error: Errors.DomainKitError;
|
|
727
|
+
readonly connectionId?: string;
|
|
713
728
|
} | {
|
|
714
729
|
readonly _tag: "Connected";
|
|
715
730
|
readonly started: Exclude<Started, {
|
|
@@ -719,6 +734,7 @@ export declare const completeOutcome: (input: {
|
|
|
719
734
|
readonly _tag: "Failed";
|
|
720
735
|
readonly error: Errors.DomainKitError;
|
|
721
736
|
readonly recovery: "restart" | "inspect";
|
|
737
|
+
readonly connectionId?: string;
|
|
722
738
|
}, Errors.DomainKitError, Principal.Service | Service>;
|
|
723
739
|
export declare const attach: (input: {
|
|
724
740
|
readonly connectionId: string;
|
package/dist/types/Server.d.ts
CHANGED
|
@@ -11403,10 +11403,12 @@ export declare const CallbackOutcome: Schema.Union<readonly [Schema.Struct<{
|
|
|
11403
11403
|
}>, Schema.Struct<{
|
|
11404
11404
|
readonly _tag: Schema.Literal<"Cancelled">;
|
|
11405
11405
|
readonly provider: Schema.String;
|
|
11406
|
+
readonly connectionId: Schema.optionalKey<Schema.String>;
|
|
11406
11407
|
readonly returnTo: Schema.String;
|
|
11407
11408
|
}>, Schema.Struct<{
|
|
11408
11409
|
readonly _tag: Schema.Literal<"Failed">;
|
|
11409
11410
|
readonly provider: Schema.String;
|
|
11411
|
+
readonly connectionId: Schema.optionalKey<Schema.String>;
|
|
11410
11412
|
readonly returnTo: Schema.String;
|
|
11411
11413
|
readonly recovery: Schema.Literals<readonly ["restart", "inspect"]>;
|
|
11412
11414
|
}>]>;
|
|
@@ -11420,7 +11422,8 @@ export interface Options {
|
|
|
11420
11422
|
/** Where interactive flows return when the callback carries no `returnTo`. */
|
|
11421
11423
|
readonly defaultReturnTo?: string;
|
|
11422
11424
|
/**
|
|
11423
|
-
*
|
|
11425
|
+
* Select a same-origin destination, or return undefined to use the saved/default returnTo.
|
|
11426
|
+
* Default recovery redirects include CallbackRecovery hints; Connected clears those hints. Invoked
|
|
11424
11427
|
* only for a verified live flow; no request, state, code, secrets, or storage rows reach this
|
|
11425
11428
|
* policy. Close over acquired host services; its Effect requires no environment.
|
|
11426
11429
|
* A policy failure after Connected cannot undo persistence: inspect before restarting.
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** The current package version from the package manifest. */
|
|
2
2
|
export declare const VERSION: string;
|
|
3
3
|
export * as Approval from "./Approval.ts";
|
|
4
|
+
export * as CallbackRecovery from "./CallbackRecovery.ts";
|
|
4
5
|
export * as Cleanup from "./Cleanup.ts";
|
|
5
6
|
export * as Cloudflare from "./Cloudflare.ts";
|
|
6
7
|
export * as Connect from "./Connect.ts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domainkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Build reviewable DNS plans for custom domains in TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"custom-domains",
|
|
@@ -49,6 +49,10 @@
|
|
|
49
49
|
"types": "./dist/types/Approval.d.ts",
|
|
50
50
|
"import": "./dist/Approval.mjs"
|
|
51
51
|
},
|
|
52
|
+
"./CallbackRecovery": {
|
|
53
|
+
"types": "./dist/types/CallbackRecovery.d.ts",
|
|
54
|
+
"import": "./dist/CallbackRecovery.mjs"
|
|
55
|
+
},
|
|
52
56
|
"./Cleanup": {
|
|
53
57
|
"types": "./dist/types/Cleanup.d.ts",
|
|
54
58
|
"import": "./dist/Cleanup.mjs"
|
|
@@ -158,5 +162,5 @@
|
|
|
158
162
|
"engines": {
|
|
159
163
|
"node": ">=24.10.0"
|
|
160
164
|
},
|
|
161
|
-
"packageManager": "bun@1.4.
|
|
165
|
+
"packageManager": "bun@1.4.4"
|
|
162
166
|
}
|