domainkit 0.8.0 → 0.9.1
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 +213 -41
- package/dist/Approval.mjs +34 -0
- package/dist/Approval.mjs.map +1 -0
- package/dist/Cleanup.mjs +97 -0
- package/dist/Cleanup.mjs.map +1 -0
- package/dist/Cloudflare.mjs +225 -0
- package/dist/Cloudflare.mjs.map +1 -0
- package/dist/Connect.mjs +463 -0
- package/dist/Connect.mjs.map +1 -0
- package/dist/Custody.mjs +64 -0
- package/dist/Custody.mjs.map +1 -0
- package/dist/DnsRecord.mjs +213 -0
- package/dist/DnsRecord.mjs.map +1 -0
- package/dist/DomainKit.mjs +34 -0
- package/dist/DomainKit.mjs.map +1 -0
- package/dist/DomainName.mjs +100 -0
- package/dist/DomainName.mjs.map +1 -0
- package/dist/Plan.mjs +102 -0
- package/dist/Plan.mjs.map +1 -0
- package/dist/Principal.mjs +23 -0
- package/dist/Principal.mjs.map +1 -0
- package/dist/Provider.mjs +130 -0
- package/dist/Provider.mjs.map +1 -0
- package/dist/Providers.mjs +40 -0
- package/dist/Providers.mjs.map +1 -0
- package/dist/Provision.mjs +93 -0
- package/dist/Provision.mjs.map +1 -0
- package/dist/Reason.mjs +162 -0
- package/dist/Reason.mjs.map +1 -0
- package/dist/Receipt.mjs +71 -0
- package/dist/Receipt.mjs.map +1 -0
- package/dist/Resolver.mjs +75 -0
- package/dist/Resolver.mjs.map +1 -0
- package/dist/Server.mjs +516 -0
- package/dist/Server.mjs.map +1 -0
- package/dist/Storage.mjs +236 -0
- package/dist/Storage.mjs.map +1 -0
- package/dist/Testing.mjs +226 -0
- package/dist/Testing.mjs.map +1 -0
- package/dist/Transport.mjs +187 -0
- package/dist/Transport.mjs.map +1 -0
- package/dist/Vercel.mjs +116 -0
- package/dist/Vercel.mjs.map +1 -0
- package/dist/Verify.mjs +292 -0
- package/dist/Verify.mjs.map +1 -0
- package/dist/{rolldown-runtime-D7D4PA-g.mjs → _virtual/_rolldown/runtime.mjs} +1 -1
- package/dist/entry/client.mjs +2 -0
- package/dist/entry/server.mjs +2 -0
- package/dist/entry/testing.mjs +3 -0
- package/dist/index.mjs +33 -462
- package/dist/index.mjs.map +1 -1
- package/dist/internal/aes.mjs +61 -0
- package/dist/internal/aes.mjs.map +1 -0
- package/dist/internal/attempts.mjs +284 -0
- package/dist/internal/attempts.mjs.map +1 -0
- package/dist/internal/cloudflare/client.mjs +100 -0
- package/dist/internal/cloudflare/client.mjs.map +1 -0
- package/dist/internal/cloudflare/protocol.mjs +59 -0
- package/dist/internal/cloudflare/protocol.mjs.map +1 -0
- package/dist/internal/cloudflare/records.mjs +156 -0
- package/dist/internal/cloudflare/records.mjs.map +1 -0
- package/dist/internal/config.mjs +13 -0
- package/dist/internal/config.mjs.map +1 -0
- package/dist/internal/conformance/provider.mjs +207 -0
- package/dist/internal/conformance/provider.mjs.map +1 -0
- package/dist/internal/conformance/storage.mjs +428 -0
- package/dist/internal/conformance/storage.mjs.map +1 -0
- package/dist/internal/digest.mjs +25 -0
- package/dist/internal/digest.mjs.map +1 -0
- package/dist/internal/doh.mjs +153 -0
- package/dist/internal/doh.mjs.map +1 -0
- package/dist/internal/error.mjs +69 -0
- package/dist/internal/error.mjs.map +1 -0
- package/dist/internal/http.mjs +90 -0
- package/dist/internal/http.mjs.map +1 -0
- package/dist/internal/ids.mjs +8 -0
- package/dist/internal/ids.mjs.map +1 -0
- package/dist/internal/oauth.mjs +113 -0
- package/dist/internal/oauth.mjs.map +1 -0
- package/dist/internal/planner.mjs +88 -0
- package/dist/internal/planner.mjs.map +1 -0
- package/dist/internal/provider-async.mjs +83 -0
- package/dist/internal/provider-async.mjs.map +1 -0
- package/dist/internal/storage-memory.mjs +302 -0
- package/dist/internal/storage-memory.mjs.map +1 -0
- package/dist/internal/vercel/client.mjs +122 -0
- package/dist/internal/vercel/client.mjs.map +1 -0
- package/dist/internal/vercel/protocol.mjs +80 -0
- package/dist/internal/vercel/protocol.mjs.map +1 -0
- package/dist/internal/vercel/records.mjs +151 -0
- package/dist/internal/vercel/records.mjs.map +1 -0
- package/dist/package.mjs +6 -0
- package/dist/package.mjs.map +1 -0
- package/dist/types/Approval.d.ts +26 -0
- package/dist/types/Cleanup.d.ts +41 -0
- package/dist/types/Cloudflare.d.ts +36 -0
- package/dist/types/Connect.d.ts +506 -0
- package/dist/types/Custody.d.ts +38 -0
- package/dist/types/DnsRecord.d.ts +158 -0
- package/dist/types/DomainKit.d.ts +31 -0
- package/dist/types/DomainName.d.ts +27 -0
- package/dist/types/Plan.d.ts +76 -0
- package/dist/types/Principal.d.ts +18 -0
- package/dist/types/Provider.d.ts +248 -0
- package/dist/types/Providers.d.ts +16 -0
- package/dist/types/Provision.d.ts +75 -0
- package/dist/types/Reason.d.ts +118 -0
- package/dist/types/Receipt.d.ts +55 -0
- package/dist/types/Resolver.d.ts +135 -0
- package/dist/types/Server.d.ts +6523 -0
- package/dist/types/Storage.d.ts +314 -0
- package/dist/types/Testing.d.ts +84 -0
- package/dist/types/Transport.d.ts +124 -0
- package/dist/types/Vercel.d.ts +25 -0
- package/dist/types/Verify.d.ts +109 -0
- package/dist/types/entry/client.d.ts +2 -0
- package/dist/types/entry/server.d.ts +2 -0
- package/dist/types/entry/testing.d.ts +3 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/internal/aes.d.ts +8 -0
- package/dist/types/internal/attempts.d.ts +40 -0
- package/dist/types/internal/cloudflare/client.d.ts +23 -0
- package/dist/types/internal/cloudflare/protocol.d.ts +71 -0
- package/dist/types/internal/cloudflare/records.d.ts +8 -0
- package/dist/types/internal/config.d.ts +4 -0
- package/dist/types/internal/conformance/provider.d.ts +12 -0
- package/dist/types/internal/conformance/storage.d.ts +14 -0
- package/dist/types/internal/digest.d.ts +10 -0
- package/dist/types/internal/doh.d.ts +19 -0
- package/dist/types/internal/error.d.ts +24 -0
- package/dist/types/internal/http.d.ts +27 -0
- package/dist/types/internal/ids.d.ts +3 -0
- package/dist/types/internal/oauth.d.ts +58 -0
- package/dist/types/internal/planner.d.ts +22 -0
- package/dist/types/internal/provider-async.d.ts +78 -0
- package/dist/types/internal/storage-memory.d.ts +2 -0
- package/dist/types/internal/vercel/client.d.ts +29 -0
- package/dist/types/internal/vercel/protocol.d.ts +124 -0
- package/dist/types/internal/vercel/records.d.ts +7 -0
- package/package.json +88 -12
- package/dist/canonical-json-CQYKsZw7.mjs +0 -310
- package/dist/canonical-json-CQYKsZw7.mjs.map +0 -1
- package/dist/connect-D-X8T9W8.d.mts +0 -1597
- package/dist/connect-oI8hcMxc.mjs +0 -185
- package/dist/connect-oI8hcMxc.mjs.map +0 -1
- package/dist/connection-Dp6ZdSo8.d.mts +0 -186
- package/dist/connection-api-lqG1nBtD.d.mts +0 -26
- package/dist/index.d.mts +0 -1551
- package/dist/managed-dns-connections-C3A_GBaz.mjs +0 -99
- package/dist/managed-dns-connections-C3A_GBaz.mjs.map +0 -1
- package/dist/promise.d.mts +0 -378
- package/dist/promise.mjs +0 -598
- package/dist/promise.mjs.map +0 -1
- package/dist/resolver-BeGz1ukw.mjs +0 -921
- package/dist/resolver-BeGz1ukw.mjs.map +0 -1
- package/dist/resolver-xoSwyXDJ.d.mts +0 -3734
- package/dist/server.d.mts +0 -128
- package/dist/server.mjs +0 -319
- package/dist/server.mjs.map +0 -1
- package/dist/testing.d.mts +0 -62
- package/dist/testing.mjs +0 -507
- package/dist/testing.mjs.map +0 -1
- package/dist/verify-DvGPb5C9.mjs +0 -2402
- package/dist/verify-DvGPb5C9.mjs.map +0 -1
- package/dist/verify-FpunAcM7.d.mts +0 -1335
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { decode } from "./DomainName.mjs";
|
|
3
|
+
import { InvalidInput } from "./Reason.mjs";
|
|
4
|
+
import { fail } from "./internal/error.mjs";
|
|
5
|
+
import { query } from "./internal/doh.mjs";
|
|
6
|
+
import { Context, Data, Effect, Layer } from "effect";
|
|
7
|
+
//#region src/Resolver.ts
|
|
8
|
+
/**
|
|
9
|
+
* Public DNS observation. The default layer fans out to Cloudflare and Google DNS-over-HTTPS and
|
|
10
|
+
* keeps every answer, so verification can show "found at Cloudflare, missing at Google".
|
|
11
|
+
*/
|
|
12
|
+
var Resolver_exports = /* @__PURE__ */ __exportAll({
|
|
13
|
+
Outcome: () => Outcome,
|
|
14
|
+
Service: () => Service,
|
|
15
|
+
cloudflare: () => cloudflare,
|
|
16
|
+
defaults: () => defaults,
|
|
17
|
+
google: () => google,
|
|
18
|
+
layer: () => layer,
|
|
19
|
+
layerWith: () => layerWith,
|
|
20
|
+
make: () => make
|
|
21
|
+
});
|
|
22
|
+
const Outcome = Data.taggedEnum();
|
|
23
|
+
var Service = class extends Context.Service()("@domainkit/Resolver") {};
|
|
24
|
+
const cloudflare = {
|
|
25
|
+
name: "cloudflare",
|
|
26
|
+
url: "https://cloudflare-dns.com/dns-query"
|
|
27
|
+
};
|
|
28
|
+
const google = {
|
|
29
|
+
name: "google",
|
|
30
|
+
url: "https://dns.google/dns-query"
|
|
31
|
+
};
|
|
32
|
+
const defaults = {
|
|
33
|
+
endpoints: [cloudflare, google],
|
|
34
|
+
timeoutMs: 3e3
|
|
35
|
+
};
|
|
36
|
+
const make = (options = {}) => Effect.gen(function* () {
|
|
37
|
+
const endpoints = options.endpoints ?? defaults.endpoints;
|
|
38
|
+
const timeoutMs = options.timeoutMs ?? defaults.timeoutMs;
|
|
39
|
+
const fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
40
|
+
for (const endpoint of endpoints) if (!URL.canParse(endpoint.url)) return yield* fail(new InvalidInput({
|
|
41
|
+
message: `Resolver ${endpoint.name} has an invalid URL`,
|
|
42
|
+
field: "endpoints"
|
|
43
|
+
}));
|
|
44
|
+
const one = (endpoint, name, type, signal) => query({
|
|
45
|
+
resolver: endpoint.name,
|
|
46
|
+
url: endpoint.url,
|
|
47
|
+
fetch,
|
|
48
|
+
name,
|
|
49
|
+
type,
|
|
50
|
+
signal
|
|
51
|
+
}).pipe(Effect.timeout(timeoutMs), Effect.match({
|
|
52
|
+
onSuccess: (answer) => Outcome.Answered({ answer: {
|
|
53
|
+
resolver: endpoint.name,
|
|
54
|
+
...answer
|
|
55
|
+
} }),
|
|
56
|
+
onFailure: (failure) => failure._tag === "TimeoutError" ? Outcome.TimedOut({ resolver: endpoint.name }) : Outcome.Failed({
|
|
57
|
+
resolver: endpoint.name,
|
|
58
|
+
message: failure.message
|
|
59
|
+
})
|
|
60
|
+
}));
|
|
61
|
+
return { resolve: (input, type, query) => decode(input).pipe(Effect.match({
|
|
62
|
+
onFailure: (failure) => endpoints.map((endpoint) => Outcome.Failed({
|
|
63
|
+
resolver: endpoint.name,
|
|
64
|
+
message: failure.message
|
|
65
|
+
})),
|
|
66
|
+
onSuccess: (name) => name
|
|
67
|
+
}), Effect.flatMap((name) => typeof name === "string" ? Effect.forEach(endpoints, (endpoint) => one(endpoint, name, type, query?.signal), { concurrency: "unbounded" }) : Effect.succeed(name))) };
|
|
68
|
+
});
|
|
69
|
+
/** Cloudflare + Google DoH, 3s timeout. */
|
|
70
|
+
const layer = Layer.effect(Service)(make().pipe(Effect.orDie));
|
|
71
|
+
const layerWith = (options) => Layer.effect(Service)(make(options));
|
|
72
|
+
//#endregion
|
|
73
|
+
export { Outcome, Resolver_exports, Service, cloudflare, defaults, google, layer, layerWith, make };
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=Resolver.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Resolver.mjs","names":["Errors.fail","Reason.InvalidInput","Doh.query","DomainName.decode"],"sources":["../src/Resolver.ts"],"sourcesContent":["/**\n * Public DNS observation. The default layer fans out to Cloudflare and Google DNS-over-HTTPS and\n * keeps every answer, so verification can show \"found at Cloudflare, missing at Google\".\n */\nimport { Context, Data, Effect, Layer } from \"effect\";\n\nimport type * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as DomainName from \"./DomainName.ts\";\nimport * as Doh from \"./internal/doh.ts\";\nimport type { Fetch } from \"./internal/http.ts\";\n\nexport interface Answer {\n readonly resolver: string;\n readonly records: ReadonlyArray<DnsRecord.Observed>;\n readonly negative: boolean;\n readonly ttl: number | null;\n}\n\nexport type Outcome = Data.TaggedEnum<{\n Answered: { readonly answer: Answer };\n Failed: { readonly resolver: string; readonly message: string };\n TimedOut: { readonly resolver: string };\n}>;\nexport const Outcome = Data.taggedEnum<Outcome>();\n\nexport interface Interface {\n /** Never fails: each resolver's outcome is preserved. */\n readonly resolve: (\n name: string,\n type: DnsRecord.Type,\n options?: { readonly signal?: AbortSignal | undefined },\n ) => Effect.Effect<ReadonlyArray<Outcome>>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Resolver\") {}\n\nexport interface Endpoint {\n readonly name: string;\n readonly url: string;\n}\n\nexport interface Options {\n readonly endpoints?: ReadonlyArray<Endpoint>;\n readonly timeoutMs?: number;\n readonly fetch?: Fetch;\n}\n\nexport const cloudflare: Endpoint = {\n name: \"cloudflare\",\n url: \"https://cloudflare-dns.com/dns-query\",\n};\nexport const google: Endpoint = { name: \"google\", url: \"https://dns.google/dns-query\" };\n\nexport const defaults = { endpoints: [cloudflare, google], timeoutMs: 3_000 } as const;\n\nexport const make = (options: Options = {}): Effect.Effect<Interface, Errors.DomainKitError> =>\n Effect.gen(function* () {\n const endpoints = options.endpoints ?? defaults.endpoints;\n const timeoutMs = options.timeoutMs ?? defaults.timeoutMs;\n // See Transport.fromFetch: resolved at call time, never invoked as a method.\n const fetch: Fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));\n for (const endpoint of endpoints) {\n if (!URL.canParse(endpoint.url)) {\n return yield* Errors.fail(\n new Reason.InvalidInput({\n message: `Resolver ${endpoint.name} has an invalid URL`,\n field: \"endpoints\",\n }),\n );\n }\n }\n const one = (\n endpoint: Endpoint,\n name: DomainName.Model,\n type: DnsRecord.Type,\n signal: AbortSignal | undefined,\n ) =>\n Doh.query({ resolver: endpoint.name, url: endpoint.url, fetch, name, type, signal }).pipe(\n Effect.timeout(timeoutMs),\n Effect.match({\n onSuccess: (answer): Outcome =>\n Outcome.Answered({ answer: { resolver: endpoint.name, ...answer } }),\n onFailure: (failure): Outcome =>\n failure._tag === \"TimeoutError\"\n ? Outcome.TimedOut({ resolver: endpoint.name })\n : Outcome.Failed({ resolver: endpoint.name, message: failure.message }),\n }),\n );\n return {\n resolve: (input, type, query) =>\n DomainName.decode(input).pipe(\n Effect.match({\n onFailure: (failure): ReadonlyArray<Outcome> =>\n endpoints.map((endpoint) =>\n Outcome.Failed({ resolver: endpoint.name, message: failure.message }),\n ),\n onSuccess: (name) => name,\n }),\n Effect.flatMap((name) =>\n typeof name === \"string\"\n ? Effect.forEach(endpoints, (endpoint) => one(endpoint, name, type, query?.signal), {\n concurrency: \"unbounded\",\n })\n : Effect.succeed(name),\n ),\n ),\n };\n });\n\n/** Cloudflare + Google DoH, 3s timeout. */\nexport const layer: Layer.Layer<Service> = Layer.effect(Service)(make().pipe(Effect.orDie));\n\nexport const layerWith = (options: Options): Layer.Layer<Service, Errors.DomainKitError> =>\n Layer.effect(Service)(make(options));\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,UAAU,KAAK,WAAoB;AAWhD,IAAa,UAAb,cAA6B,QAAQ,QAA4B,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC;AAa3F,MAAa,aAAuB;CAClC,MAAM;CACN,KAAK;AACP;AACA,MAAa,SAAmB;CAAE,MAAM;CAAU,KAAK;AAA+B;AAEtF,MAAa,WAAW;CAAE,WAAW,CAAC,YAAY,MAAM;CAAG,WAAW;AAAM;AAE5E,MAAa,QAAQ,UAAmB,CAAC,MACvC,OAAO,IAAI,aAAa;CACtB,MAAM,YAAY,QAAQ,aAAa,SAAS;CAChD,MAAM,YAAY,QAAQ,aAAa,SAAS;CAEhD,MAAM,QAAe,QAAQ,WAAW,OAAO,SAAS,WAAW,MAAM,OAAO,IAAI;CACpF,KAAK,MAAM,YAAY,WACrB,IAAI,CAAC,IAAI,SAAS,SAAS,GAAG,GAC5B,OAAO,OAAOA,KACZ,IAAIC,aAAoB;EACtB,SAAS,YAAY,SAAS,KAAK;EACnC,OAAO;CACT,CAAC,CACH;CAGJ,MAAM,OACJ,UACA,MACA,MACA,WAEAC,MAAU;EAAE,UAAU,SAAS;EAAM,KAAK,SAAS;EAAK;EAAO;EAAM;EAAM;CAAO,CAAC,CAAC,CAAC,KACnF,OAAO,QAAQ,SAAS,GACxB,OAAO,MAAM;EACX,YAAY,WACV,QAAQ,SAAS,EAAE,QAAQ;GAAE,UAAU,SAAS;GAAM,GAAG;EAAO,EAAE,CAAC;EACrE,YAAY,YACV,QAAQ,SAAS,iBACb,QAAQ,SAAS,EAAE,UAAU,SAAS,KAAK,CAAC,IAC5C,QAAQ,OAAO;GAAE,UAAU,SAAS;GAAM,SAAS,QAAQ;EAAQ,CAAC;CAC5E,CAAC,CACH;CACF,OAAO,EACL,UAAU,OAAO,MAAM,UACrBC,OAAkB,KAAK,CAAC,CAAC,KACvB,OAAO,MAAM;EACX,YAAY,YACV,UAAU,KAAK,aACb,QAAQ,OAAO;GAAE,UAAU,SAAS;GAAM,SAAS,QAAQ;EAAQ,CAAC,CACtE;EACF,YAAY,SAAS;CACvB,CAAC,GACD,OAAO,SAAS,SACd,OAAO,SAAS,WACZ,OAAO,QAAQ,YAAY,aAAa,IAAI,UAAU,MAAM,MAAM,OAAO,MAAM,GAAG,EAChF,aAAa,YACf,CAAC,IACD,OAAO,QAAQ,IAAI,CACzB,CACF,EACJ;AACF,CAAC;;AAGH,MAAa,QAA8B,MAAM,OAAO,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,OAAO,KAAK,CAAC;AAE1F,MAAa,aAAa,YACxB,MAAM,OAAO,OAAO,CAAC,CAAC,KAAK,OAAO,CAAC"}
|
package/dist/Server.mjs
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { Model } from "./DnsRecord.mjs";
|
|
3
|
+
import { Model as Model$1, OperationId, PlanId } from "./Plan.mjs";
|
|
4
|
+
import { InvalidInput, NotFound } from "./Reason.mjs";
|
|
5
|
+
import { DomainKitError, fail } from "./internal/error.mjs";
|
|
6
|
+
import { ApprovalId, Model as Model$2 } from "./Approval.mjs";
|
|
7
|
+
import { Service } from "./Principal.mjs";
|
|
8
|
+
import { Model as Model$3, ReceiptId } from "./Receipt.mjs";
|
|
9
|
+
import { AttemptStatus, AuthMethod, Overall, Rejection, RequirementStatus, Service as Service$1 } from "./Storage.mjs";
|
|
10
|
+
import { Method as Method$1, Service as Service$2 } from "./Connect.mjs";
|
|
11
|
+
import { Service as Service$3 } from "./Provision.mjs";
|
|
12
|
+
import { Service as Service$4 } from "./Cleanup.mjs";
|
|
13
|
+
import { Evidence, HostEvidence, Service as Service$5 } from "./Verify.mjs";
|
|
14
|
+
import { Context, Effect, Layer, Option, Schema } from "effect";
|
|
15
|
+
import * as HttpRouter from "effect/unstable/http/HttpRouter";
|
|
16
|
+
import * as HttpServer from "effect/unstable/http/HttpServer";
|
|
17
|
+
import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
|
|
18
|
+
import * as HttpApi from "effect/unstable/httpapi/HttpApi";
|
|
19
|
+
import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder";
|
|
20
|
+
import * as HttpApiEndpoint from "effect/unstable/httpapi/HttpApiEndpoint";
|
|
21
|
+
import * as HttpApiGroup from "effect/unstable/httpapi/HttpApiGroup";
|
|
22
|
+
import * as HttpApiSchema from "effect/unstable/httpapi/HttpApiSchema";
|
|
23
|
+
//#region src/Server.ts
|
|
24
|
+
/**
|
|
25
|
+
* domainkit/server — the whole lifecycle as one HttpApi group the host mounts.
|
|
26
|
+
*
|
|
27
|
+
* const Api = HttpApi.make("app").add(Server.group)
|
|
28
|
+
* const ApiLive = HttpApiBuilder.layer(Api).pipe(
|
|
29
|
+
* Layer.provide([Server.layer(Api), DomainKitLive, IdentityLive]),
|
|
30
|
+
* )
|
|
31
|
+
*
|
|
32
|
+
* The host provides `Identity` (request -> `Principal.Interface`). Everything else comes from
|
|
33
|
+
* `DomainKit.layer`. Non-HttpApi hosts use `toWebHandler`. The group carries no prefix of its
|
|
34
|
+
* own, so `group.prefix("/internal/dns")` or a host router mount is all a different base path
|
|
35
|
+
* takes; callback URLs follow the mount.
|
|
36
|
+
*/
|
|
37
|
+
var Server_exports = /* @__PURE__ */ __exportAll({
|
|
38
|
+
ApprovePayload: () => ApprovePayload,
|
|
39
|
+
AttachPayload: () => AttachPayload,
|
|
40
|
+
Attempt: () => Attempt,
|
|
41
|
+
Candidate: () => Candidate,
|
|
42
|
+
Connected: () => Connected,
|
|
43
|
+
ConnectionStatus: () => ConnectionStatus,
|
|
44
|
+
Discovery: () => Discovery,
|
|
45
|
+
DiscoveryNotFound: () => DiscoveryNotFound,
|
|
46
|
+
DiscoveryResolved: () => DiscoveryResolved,
|
|
47
|
+
DiscoverySelectionRequired: () => DiscoverySelectionRequired,
|
|
48
|
+
Field: () => Field,
|
|
49
|
+
Identity: () => Identity,
|
|
50
|
+
Integration: () => Integration,
|
|
51
|
+
Method: () => Method,
|
|
52
|
+
MethodDescriptor: () => MethodDescriptor,
|
|
53
|
+
OAuth: () => OAuth,
|
|
54
|
+
ObservePayload: () => ObservePayload,
|
|
55
|
+
PlanPayload: () => PlanPayload,
|
|
56
|
+
Readiness: () => Readiness,
|
|
57
|
+
Redirect: () => Redirect,
|
|
58
|
+
RejectPayload: () => RejectPayload,
|
|
59
|
+
SelectionRequired: () => SelectionRequired,
|
|
60
|
+
Snapshot: () => Snapshot,
|
|
61
|
+
StartPayload: () => StartPayload,
|
|
62
|
+
Started: () => Started,
|
|
63
|
+
Token: () => Token,
|
|
64
|
+
api: () => api,
|
|
65
|
+
group: () => group,
|
|
66
|
+
layer: () => layer,
|
|
67
|
+
toWebHandler: () => toWebHandler
|
|
68
|
+
});
|
|
69
|
+
var Identity = class extends Context.Service()("@domainkit/server/Identity") {};
|
|
70
|
+
const ConnectionStatus = Schema.Literals([
|
|
71
|
+
"disconnected",
|
|
72
|
+
"connected",
|
|
73
|
+
"reconnect"
|
|
74
|
+
]);
|
|
75
|
+
/** One value a token method needs, so a form renders without knowing the provider. */
|
|
76
|
+
const Field = Schema.Struct({
|
|
77
|
+
name: Schema.String,
|
|
78
|
+
required: Schema.Boolean,
|
|
79
|
+
secret: Schema.Boolean
|
|
80
|
+
});
|
|
81
|
+
/** How a provider can be connected; `fields` is `null` for the interactive methods. */
|
|
82
|
+
const MethodDescriptor = Schema.Struct({
|
|
83
|
+
kind: AuthMethod,
|
|
84
|
+
label: Schema.String,
|
|
85
|
+
docsUrl: Schema.NullOr(Schema.String),
|
|
86
|
+
fields: Schema.NullOr(Schema.Array(Field))
|
|
87
|
+
});
|
|
88
|
+
/** Everything the UI needs about a domain, flattened; provider context never crosses the wire. */
|
|
89
|
+
const Snapshot = Schema.Struct({
|
|
90
|
+
domain: Schema.String,
|
|
91
|
+
attachmentId: Schema.NullOr(Schema.String),
|
|
92
|
+
connectionId: Schema.NullOr(Schema.String),
|
|
93
|
+
provider: Schema.NullOr(Schema.String),
|
|
94
|
+
method: Schema.NullOr(AuthMethod),
|
|
95
|
+
status: ConnectionStatus,
|
|
96
|
+
lastReceiptId: Schema.NullOr(Schema.String),
|
|
97
|
+
reusable: Schema.Array(Schema.Struct({
|
|
98
|
+
connectionId: Schema.String,
|
|
99
|
+
provider: Schema.String,
|
|
100
|
+
method: AuthMethod
|
|
101
|
+
})),
|
|
102
|
+
providers: Schema.Array(Schema.Struct({
|
|
103
|
+
id: Schema.String,
|
|
104
|
+
name: Schema.String,
|
|
105
|
+
methods: Schema.Array(MethodDescriptor)
|
|
106
|
+
}))
|
|
107
|
+
});
|
|
108
|
+
/**
|
|
109
|
+
* How the client asks to connect. `values` is keyed by the provider's declared token fields, as
|
|
110
|
+
* `Snapshot.providers[].methods[].fields` names them. Secrets are plaintext in transit and never
|
|
111
|
+
* come back out.
|
|
112
|
+
*/
|
|
113
|
+
var Token = class extends Schema.TaggedClass("@domainkit/server/Method/Token")("Token", { values: Schema.Record(Schema.String, Schema.String) }) {};
|
|
114
|
+
var OAuth = class extends Schema.TaggedClass("@domainkit/server/Method/OAuth")("OAuth", { returnTo: Schema.optionalKey(Schema.String) }) {};
|
|
115
|
+
var Integration = class extends Schema.TaggedClass("@domainkit/server/Method/Integration")("Integration", { returnTo: Schema.optionalKey(Schema.String) }) {};
|
|
116
|
+
const Method = Schema.Union([
|
|
117
|
+
Token,
|
|
118
|
+
OAuth,
|
|
119
|
+
Integration
|
|
120
|
+
]);
|
|
121
|
+
const StartPayload = Schema.Struct({
|
|
122
|
+
domain: Schema.String,
|
|
123
|
+
provider: Schema.String,
|
|
124
|
+
method: Method
|
|
125
|
+
});
|
|
126
|
+
/** A zone the connection can serve, named for a customer to pick from. */
|
|
127
|
+
var Candidate = class extends Schema.Class("@domainkit/server/Candidate")({
|
|
128
|
+
zone: Schema.String,
|
|
129
|
+
label: Schema.String
|
|
130
|
+
}) {};
|
|
131
|
+
var Connected = class extends Schema.TaggedClass("@domainkit/server/Started/Connected")("Connected", { snapshot: Snapshot }) {};
|
|
132
|
+
var Redirect = class extends Schema.TaggedClass("@domainkit/server/Started/Redirect")("Redirect", { authorizationUrl: Schema.String }) {};
|
|
133
|
+
var SelectionRequired = class extends Schema.TaggedClass("@domainkit/server/Started/SelectionRequired")("SelectionRequired", {
|
|
134
|
+
connectionId: Schema.String,
|
|
135
|
+
candidates: Schema.Array(Candidate)
|
|
136
|
+
}) {};
|
|
137
|
+
const Started = Schema.Union([
|
|
138
|
+
Connected,
|
|
139
|
+
Redirect,
|
|
140
|
+
SelectionRequired
|
|
141
|
+
]);
|
|
142
|
+
/**
|
|
143
|
+
* Which of the principal's connections already reaches a domain. The class names carry a
|
|
144
|
+
* `Discovery` prefix because `Started` owns the unprefixed `SelectionRequired`; the wire tags are
|
|
145
|
+
* the ones `Connect.Discovery` uses.
|
|
146
|
+
*/
|
|
147
|
+
var DiscoveryResolved = class extends Schema.TaggedClass("@domainkit/server/Discovery/Resolved")("Resolved", {
|
|
148
|
+
connectionId: Schema.String,
|
|
149
|
+
zone: Schema.String,
|
|
150
|
+
label: Schema.String
|
|
151
|
+
}) {};
|
|
152
|
+
var DiscoverySelectionRequired = class extends Schema.TaggedClass("@domainkit/server/Discovery/SelectionRequired")("SelectionRequired", { candidates: Schema.Array(Schema.Struct({
|
|
153
|
+
connectionId: Schema.String,
|
|
154
|
+
zone: Schema.String,
|
|
155
|
+
label: Schema.String
|
|
156
|
+
})) }) {};
|
|
157
|
+
var DiscoveryNotFound = class extends Schema.TaggedClass("@domainkit/server/Discovery/NotFound")("NotFound", { nameservers: Schema.Array(Schema.String) }) {};
|
|
158
|
+
const Discovery = Schema.Union([
|
|
159
|
+
DiscoveryResolved,
|
|
160
|
+
DiscoverySelectionRequired,
|
|
161
|
+
DiscoveryNotFound
|
|
162
|
+
]);
|
|
163
|
+
const AttachPayload = Schema.Struct({
|
|
164
|
+
domain: Schema.String,
|
|
165
|
+
/** Which candidate zone to attach when `SelectionRequired` came back. */
|
|
166
|
+
zone: Schema.optionalKey(Schema.String)
|
|
167
|
+
});
|
|
168
|
+
const PlanPayload = Schema.Struct({ requirements: Schema.Array(Model) });
|
|
169
|
+
const ObservePayload = Schema.Struct({
|
|
170
|
+
/** Records to observe instead of the attachment's receipt; required for an unattached domain. */
|
|
171
|
+
requirements: Schema.optionalKey(Schema.Array(Model)) });
|
|
172
|
+
const ApprovePayload = Schema.Struct({
|
|
173
|
+
/** A subset of the plan's writes; omit to approve every write. */
|
|
174
|
+
operationIds: Schema.optionalKey(Schema.Array(OperationId)) });
|
|
175
|
+
const RejectPayload = Schema.Struct({
|
|
176
|
+
/** Why the customer declined, for the audit trail. */
|
|
177
|
+
reason: Schema.optionalKey(Schema.String) });
|
|
178
|
+
/** One durable plan -> approval -> receipt lifecycle, as `GET /plans/:planId` returns it. */
|
|
179
|
+
const Attempt = Schema.Struct({
|
|
180
|
+
plan: Model$1,
|
|
181
|
+
status: AttemptStatus,
|
|
182
|
+
approval: Schema.NullOr(Model$2),
|
|
183
|
+
receipt: Schema.NullOr(Model$3),
|
|
184
|
+
rejection: Schema.NullOr(Rejection)
|
|
185
|
+
});
|
|
186
|
+
/** `Verify.Readiness` on the wire; timestamps encode as ISO strings. */
|
|
187
|
+
const Readiness = Schema.Struct({
|
|
188
|
+
domain: Schema.String,
|
|
189
|
+
attachmentId: Schema.NullOr(Schema.String),
|
|
190
|
+
overall: Overall,
|
|
191
|
+
requirements: Schema.Array(Schema.Struct({
|
|
192
|
+
operationId: Schema.NullOr(OperationId),
|
|
193
|
+
record: Model,
|
|
194
|
+
status: RequirementStatus,
|
|
195
|
+
evidence: Schema.Array(Evidence)
|
|
196
|
+
})),
|
|
197
|
+
host: Schema.Array(HostEvidence),
|
|
198
|
+
checkedAt: Schema.DateTimeUtcFromString,
|
|
199
|
+
nextCheckAt: Schema.NullOr(Schema.DateTimeUtcFromString)
|
|
200
|
+
});
|
|
201
|
+
const Redirected = HttpApiSchema.WithHeaders(HttpApiSchema.Empty(302), { location: Schema.String });
|
|
202
|
+
/** `DomainKit.Error` narrowed to the reasons that answer with `status`. */
|
|
203
|
+
const errorAt = (status) => DomainKitError.check(Schema.makeFilter((error) => error.httpStatus === status)).pipe(HttpApiSchema.status(status));
|
|
204
|
+
const errors = [
|
|
205
|
+
400,
|
|
206
|
+
401,
|
|
207
|
+
403,
|
|
208
|
+
404,
|
|
209
|
+
409,
|
|
210
|
+
500,
|
|
211
|
+
501,
|
|
212
|
+
502,
|
|
213
|
+
503
|
|
214
|
+
].map(errorAt);
|
|
215
|
+
const group = HttpApiGroup.make("domainkit").add(HttpApiEndpoint.get("inspect", "/domains/:domain", {
|
|
216
|
+
params: { domain: Schema.String },
|
|
217
|
+
success: Snapshot,
|
|
218
|
+
error: errors
|
|
219
|
+
})).add(HttpApiEndpoint.get("discover", "/domains/:domain/discovery", {
|
|
220
|
+
params: { domain: Schema.String },
|
|
221
|
+
success: Discovery,
|
|
222
|
+
error: errors
|
|
223
|
+
})).add(HttpApiEndpoint.post("start", "/connections", {
|
|
224
|
+
payload: StartPayload,
|
|
225
|
+
success: Started,
|
|
226
|
+
error: errors
|
|
227
|
+
})).add(HttpApiEndpoint.get("callback", "/callback/:provider", {
|
|
228
|
+
params: { provider: Schema.String },
|
|
229
|
+
query: {
|
|
230
|
+
state: Schema.String,
|
|
231
|
+
code: Schema.optionalKey(Schema.String)
|
|
232
|
+
},
|
|
233
|
+
success: Redirected,
|
|
234
|
+
error: errors
|
|
235
|
+
})).add(HttpApiEndpoint.post("attach", "/connections/:connectionId/attachments", {
|
|
236
|
+
params: { connectionId: Schema.String },
|
|
237
|
+
payload: AttachPayload,
|
|
238
|
+
success: Started,
|
|
239
|
+
error: errors
|
|
240
|
+
})).add(HttpApiEndpoint.delete("detach", "/attachments/:attachmentId", {
|
|
241
|
+
params: { attachmentId: Schema.String },
|
|
242
|
+
success: Schema.Void,
|
|
243
|
+
error: errors
|
|
244
|
+
})).add(HttpApiEndpoint.delete("disconnect", "/connections/:connectionId", {
|
|
245
|
+
params: { connectionId: Schema.String },
|
|
246
|
+
success: Schema.Void,
|
|
247
|
+
error: errors
|
|
248
|
+
})).add(HttpApiEndpoint.post("createPlan", "/domains/:domain/plans", {
|
|
249
|
+
params: { domain: Schema.String },
|
|
250
|
+
payload: PlanPayload,
|
|
251
|
+
success: Model$1,
|
|
252
|
+
error: errors
|
|
253
|
+
})).add(HttpApiEndpoint.post("approve", "/plans/:planId/approvals", {
|
|
254
|
+
params: { planId: PlanId },
|
|
255
|
+
payload: ApprovePayload,
|
|
256
|
+
success: Model$2,
|
|
257
|
+
error: errors
|
|
258
|
+
})).add(HttpApiEndpoint.post("reject", "/plans/:planId/rejections", {
|
|
259
|
+
params: { planId: PlanId },
|
|
260
|
+
payload: RejectPayload,
|
|
261
|
+
success: Attempt,
|
|
262
|
+
error: errors
|
|
263
|
+
})).add(HttpApiEndpoint.post("apply", "/approvals/:approvalId/apply", {
|
|
264
|
+
params: { approvalId: ApprovalId },
|
|
265
|
+
success: Model$3,
|
|
266
|
+
error: errors
|
|
267
|
+
})).add(HttpApiEndpoint.get("plan", "/plans/:planId", {
|
|
268
|
+
params: { planId: PlanId },
|
|
269
|
+
success: Attempt,
|
|
270
|
+
error: errors
|
|
271
|
+
})).add(HttpApiEndpoint.get("receipt", "/receipts/:receiptId", {
|
|
272
|
+
params: { receiptId: ReceiptId },
|
|
273
|
+
success: Model$3,
|
|
274
|
+
error: errors
|
|
275
|
+
})).add(HttpApiEndpoint.post("observe", "/domains/:domain/observations", {
|
|
276
|
+
params: { domain: Schema.String },
|
|
277
|
+
payload: ObservePayload,
|
|
278
|
+
success: Readiness,
|
|
279
|
+
error: errors
|
|
280
|
+
})).add(HttpApiEndpoint.post("cleanupPlan", "/receipts/:receiptId/cleanup-plans", {
|
|
281
|
+
params: { receiptId: ReceiptId },
|
|
282
|
+
success: Model$1,
|
|
283
|
+
error: errors
|
|
284
|
+
}));
|
|
285
|
+
const START_ROUTE = "/connections";
|
|
286
|
+
const callbackRoute = (provider) => `/callback/${encodeURIComponent(provider)}`;
|
|
287
|
+
const invalid = (message, field) => fail(new InvalidInput({
|
|
288
|
+
message,
|
|
289
|
+
...field === void 0 ? {} : { field }
|
|
290
|
+
}));
|
|
291
|
+
/** The request as an absolute URL: the web original when there is one, else scheme plus `Host`. */
|
|
292
|
+
const absoluteUrl = (request) => Effect.suspend(() => {
|
|
293
|
+
try {
|
|
294
|
+
return Effect.succeed(new URL(request.originalUrl));
|
|
295
|
+
} catch {
|
|
296
|
+
const url = HttpServerRequest.toURL(request);
|
|
297
|
+
return Option.isNone(url) ? invalid("The request has no usable origin; set callbackBaseUrl", "callbackBaseUrl") : Effect.succeed(url.value);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
/**
|
|
301
|
+
* The absolute URL the provider redirects back to. `callbackBaseUrl` wins when the host set it,
|
|
302
|
+
* because a proxy that rewrites `Host` leaves the request pointing at an origin the browser never
|
|
303
|
+
* sees; otherwise it follows the prefix the group is mounted at, derived by stripping the handling
|
|
304
|
+
* route's own path off the request.
|
|
305
|
+
*/
|
|
306
|
+
const callbackUrlFor = (input) => Effect.suspend(() => {
|
|
307
|
+
const suffix = `/callback/${encodeURIComponent(input.provider)}`;
|
|
308
|
+
const configured = input.options.callbackBaseUrl;
|
|
309
|
+
if (configured !== void 0) return Effect.try({
|
|
310
|
+
try: () => new URL(`${configured.replace(/\/+$/, "")}${suffix}`),
|
|
311
|
+
catch: () => callbackConfigurationError(`${configured} is not a URL`)
|
|
312
|
+
});
|
|
313
|
+
return Effect.flatMap(absoluteUrl(input.request), (url) => {
|
|
314
|
+
const path = url.pathname;
|
|
315
|
+
if (!path.endsWith(input.route)) return invalid(`The ${input.route} route is not mounted where it says; set callbackBaseUrl`, "callbackBaseUrl");
|
|
316
|
+
return Effect.succeed(new URL(`${url.origin}${path.slice(0, path.length - input.route.length)}${suffix}`));
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
const callbackConfigurationError = (message) => new DomainKitError({ reason: new InvalidInput({
|
|
320
|
+
message,
|
|
321
|
+
field: "callbackBaseUrl"
|
|
322
|
+
}) });
|
|
323
|
+
/**
|
|
324
|
+
* Where the callback may send the customer: a path under the callback's own base, or an absolute
|
|
325
|
+
* URL on its origin. A destination that leaves the application is an open redirect, whoever
|
|
326
|
+
* supplied it.
|
|
327
|
+
*/
|
|
328
|
+
const sameOrigin = (destination, callback) => {
|
|
329
|
+
try {
|
|
330
|
+
const url = new URL(destination, callback);
|
|
331
|
+
return url.origin === callback.origin ? url.toString() : null;
|
|
332
|
+
} catch {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
const snapshotOf = (snapshot) => ({
|
|
337
|
+
domain: snapshot.domain,
|
|
338
|
+
attachmentId: snapshot.attachment?.id ?? null,
|
|
339
|
+
connectionId: snapshot.connection?.id ?? null,
|
|
340
|
+
provider: snapshot.authorization?.provider ?? null,
|
|
341
|
+
method: snapshot.authorization?.method ?? null,
|
|
342
|
+
status: snapshot.connection === null ? "disconnected" : snapshot.authorization?.revocation === "active" ? "connected" : "reconnect",
|
|
343
|
+
lastReceiptId: snapshot.lastReceiptId,
|
|
344
|
+
reusable: snapshot.reusable.map(({ connection, provider, method }) => ({
|
|
345
|
+
connectionId: connection.id,
|
|
346
|
+
provider,
|
|
347
|
+
method
|
|
348
|
+
})),
|
|
349
|
+
providers: snapshot.providers.map(({ id, name, methods }) => ({
|
|
350
|
+
id,
|
|
351
|
+
name,
|
|
352
|
+
methods
|
|
353
|
+
}))
|
|
354
|
+
});
|
|
355
|
+
/** The wire method as `Connect` takes it; interactive cases carry the caller's return destination. */
|
|
356
|
+
const methodOf = (method) => {
|
|
357
|
+
switch (method._tag) {
|
|
358
|
+
case "Token": return Method$1.token(method.values);
|
|
359
|
+
case "OAuth": return Method$1.oauth(method.returnTo === void 0 ? {} : { returnTo: method.returnTo });
|
|
360
|
+
case "Integration": return Method$1.integration(method.returnTo === void 0 ? {} : { returnTo: method.returnTo });
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
const candidatesOf = (candidates) => candidates.map(({ zone, label }) => new Candidate({
|
|
364
|
+
zone,
|
|
365
|
+
label
|
|
366
|
+
}));
|
|
367
|
+
/**
|
|
368
|
+
* Handlers for `group` inside the host's API. Every handler derives `Principal` from `Identity`
|
|
369
|
+
* for the request it is serving and provides it to the lifecycle services.
|
|
370
|
+
*/
|
|
371
|
+
const layer = (api, options = {}) => HttpApiBuilder.group(api, "domainkit", (handlers) => Effect.gen(function* () {
|
|
372
|
+
const identity = yield* Identity;
|
|
373
|
+
const connect = yield* Service$2;
|
|
374
|
+
const provision = yield* Service$3;
|
|
375
|
+
const cleanup = yield* Service$4;
|
|
376
|
+
const verify = yield* Service$5;
|
|
377
|
+
const storage = yield* Service$1;
|
|
378
|
+
/**
|
|
379
|
+
* Run a lifecycle effect as the principal the host derives from this request, once the host
|
|
380
|
+
* has allowed that principal onto this route.
|
|
381
|
+
*/
|
|
382
|
+
const as = (endpoint, request, effect) => Effect.flatMap(identity.principal(request), (principal) => Effect.flatMap(identity.authorize?.(principal, endpoint) ?? Effect.void, () => Effect.provideService(effect, Service, principal)));
|
|
383
|
+
const snapshot = (domain) => Effect.map(connect.inspect(domain), snapshotOf);
|
|
384
|
+
const discovered = (discovery) => {
|
|
385
|
+
switch (discovery._tag) {
|
|
386
|
+
case "Resolved": return new DiscoveryResolved({
|
|
387
|
+
connectionId: discovery.connectionId,
|
|
388
|
+
zone: discovery.target.zone,
|
|
389
|
+
label: discovery.target.label
|
|
390
|
+
});
|
|
391
|
+
case "SelectionRequired": return new DiscoverySelectionRequired({ candidates: discovery.candidates.map(({ connectionId, target }) => ({
|
|
392
|
+
connectionId,
|
|
393
|
+
zone: target.zone,
|
|
394
|
+
label: target.label
|
|
395
|
+
})) });
|
|
396
|
+
case "NotFound": return new DiscoveryNotFound({ nameservers: discovery.nameservers });
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const connected = (domain) => Effect.map(snapshot(domain), (value) => new Connected({ snapshot: value }));
|
|
400
|
+
const startedOf = (started, domain) => {
|
|
401
|
+
switch (started._tag) {
|
|
402
|
+
case "Connected": return connected(domain);
|
|
403
|
+
case "Redirect": return Effect.succeed(new Redirect({ authorizationUrl: started.authorizationUrl }));
|
|
404
|
+
case "SelectionRequired": return Effect.succeed(new SelectionRequired({
|
|
405
|
+
connectionId: started.connection.id,
|
|
406
|
+
candidates: candidatesOf(started.candidates)
|
|
407
|
+
}));
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
/**
|
|
411
|
+
* Attach with a zone the client picked out of a `SelectionRequired`. The provider target
|
|
412
|
+
* behind a zone never crosses the wire, so resolve the candidates again and match by zone.
|
|
413
|
+
*/
|
|
414
|
+
const attachAt = (input) => Effect.gen(function* () {
|
|
415
|
+
const first = yield* connect.attach({
|
|
416
|
+
connectionId: input.connectionId,
|
|
417
|
+
domain: input.domain
|
|
418
|
+
});
|
|
419
|
+
if (!("_tag" in first)) return yield* connected(input.domain);
|
|
420
|
+
if (input.zone === void 0) return new SelectionRequired({
|
|
421
|
+
connectionId: input.connectionId,
|
|
422
|
+
candidates: candidatesOf(first.candidates)
|
|
423
|
+
});
|
|
424
|
+
const target = first.candidates.find(({ zone }) => zone === input.zone);
|
|
425
|
+
if (target === void 0) return yield* fail(new NotFound({
|
|
426
|
+
entity: "zone",
|
|
427
|
+
id: input.zone
|
|
428
|
+
}));
|
|
429
|
+
yield* connect.attach({
|
|
430
|
+
connectionId: input.connectionId,
|
|
431
|
+
domain: input.domain,
|
|
432
|
+
target
|
|
433
|
+
});
|
|
434
|
+
return yield* connected(input.domain);
|
|
435
|
+
});
|
|
436
|
+
/** Provisioning and cleanup share the approve and apply routes; the attempt knows its kind. */
|
|
437
|
+
const kindOf = (attempt) => attempt.kind === "cleanup" ? cleanup : provision;
|
|
438
|
+
return handlers.handle("inspect", ({ params, request }) => as("inspect", request, snapshot(params.domain))).handle("discover", ({ params, request }) => as("discover", request, Effect.map(connect.discover(params.domain), discovered))).handle("start", ({ payload, request }) => as("start", request, Effect.gen(function* () {
|
|
439
|
+
const callbackUrl = payload.method._tag === "Token" ? void 0 : (yield* callbackUrlFor({
|
|
440
|
+
request,
|
|
441
|
+
provider: payload.provider,
|
|
442
|
+
options,
|
|
443
|
+
route: START_ROUTE
|
|
444
|
+
})).toString();
|
|
445
|
+
const started = yield* connect.start({
|
|
446
|
+
provider: payload.provider,
|
|
447
|
+
domain: payload.domain,
|
|
448
|
+
method: methodOf(payload.method),
|
|
449
|
+
...callbackUrl === void 0 ? {} : { callbackUrl }
|
|
450
|
+
});
|
|
451
|
+
return yield* startedOf(started, payload.domain);
|
|
452
|
+
}))).handle("callback", ({ params, query, request }) => as("callback", request, Effect.gen(function* () {
|
|
453
|
+
const url = yield* absoluteUrl(request);
|
|
454
|
+
const base = yield* callbackUrlFor({
|
|
455
|
+
request,
|
|
456
|
+
provider: params.provider,
|
|
457
|
+
options,
|
|
458
|
+
route: callbackRoute(params.provider)
|
|
459
|
+
});
|
|
460
|
+
const requested = (yield* storage.continuations.get(query.state)).returnTo ?? options.defaultReturnTo;
|
|
461
|
+
if (requested === void 0) return yield* invalid("The flow carried no returnTo and the server has no defaultReturnTo", "returnTo");
|
|
462
|
+
const destination = sameOrigin(requested, base);
|
|
463
|
+
if (destination === null) return yield* invalid(`${requested} leaves this application`, "returnTo");
|
|
464
|
+
yield* connect.complete({
|
|
465
|
+
continuationId: query.state,
|
|
466
|
+
callbackUrl: url.toString()
|
|
467
|
+
});
|
|
468
|
+
return HttpApiSchema.withHeaders({
|
|
469
|
+
body: void 0,
|
|
470
|
+
headers: { location: destination }
|
|
471
|
+
});
|
|
472
|
+
}))).handle("attach", ({ params, payload, request }) => as("attach", request, attachAt({
|
|
473
|
+
connectionId: params.connectionId,
|
|
474
|
+
domain: payload.domain,
|
|
475
|
+
zone: payload.zone
|
|
476
|
+
}))).handle("detach", ({ params, request }) => as("detach", request, connect.detach(params.attachmentId))).handle("disconnect", ({ params, request }) => as("disconnect", request, connect.disconnect(params.connectionId))).handle("createPlan", ({ params, payload, request }) => as("createPlan", request, provision.plan({
|
|
477
|
+
domain: params.domain,
|
|
478
|
+
requirements: payload.requirements
|
|
479
|
+
}))).handle("approve", ({ params, payload, request }) => as("approve", request, Effect.gen(function* () {
|
|
480
|
+
const attempt = yield* storage.attempts.get(params.planId);
|
|
481
|
+
return yield* kindOf(attempt).approve(params.planId, { ...payload.operationIds === void 0 ? {} : {
|
|
482
|
+
operationIds: payload.operationIds,
|
|
483
|
+
allowPartial: true
|
|
484
|
+
} });
|
|
485
|
+
}))).handle("reject", ({ params, payload, request }) => as("reject", request, Effect.gen(function* () {
|
|
486
|
+
const attempt = yield* storage.attempts.get(params.planId);
|
|
487
|
+
return yield* kindOf(attempt).reject(params.planId, { ...payload.reason === void 0 ? {} : { reason: payload.reason } });
|
|
488
|
+
}))).handle("apply", ({ params, request }) => as("apply", request, Effect.gen(function* () {
|
|
489
|
+
const attempt = yield* storage.attempts.byApproval(params.approvalId);
|
|
490
|
+
return yield* kindOf(attempt).apply(params.approvalId);
|
|
491
|
+
}))).handle("plan", ({ params, request }) => as("plan", request, Effect.map(storage.attempts.get(params.planId), (attempt) => ({
|
|
492
|
+
plan: attempt.plan,
|
|
493
|
+
status: attempt.status,
|
|
494
|
+
approval: attempt.approval,
|
|
495
|
+
receipt: attempt.receipt,
|
|
496
|
+
rejection: attempt.rejection
|
|
497
|
+
})))).handle("receipt", ({ params, request }) => as("receipt", request, Effect.flatMap(storage.attempts.byReceipt(params.receiptId), (attempt) => attempt.receipt === null ? fail(new NotFound({
|
|
498
|
+
entity: "receipt",
|
|
499
|
+
id: params.receiptId
|
|
500
|
+
})) : Effect.succeed(attempt.receipt)))).handle("observe", ({ params, payload, request }) => as("observe", request, verify.observe({
|
|
501
|
+
domain: params.domain,
|
|
502
|
+
...payload.requirements === void 0 ? {} : { requirements: payload.requirements }
|
|
503
|
+
}))).handle("cleanupPlan", ({ params, request }) => as("cleanupPlan", request, cleanup.plan({ receiptId: params.receiptId })));
|
|
504
|
+
}));
|
|
505
|
+
/** A standalone API for hosts not on HttpApi. Mount at any prefix. */
|
|
506
|
+
const api = HttpApi.make("domainkit").add(group);
|
|
507
|
+
/** The Promise edge: a `fetch`-shaped handler over the group for hosts that are not Effect-native. */
|
|
508
|
+
const toWebHandler = (services, options = {}) => {
|
|
509
|
+
const prefixed = options.prefix === void 0 ? api : HttpApi.make("domainkit").add(group.prefix(options.prefix));
|
|
510
|
+
const app = HttpApiBuilder.layer(prefixed).pipe(Layer.provide(layer(prefixed, options)), Layer.provide(services), Layer.provide(HttpServer.layerServices));
|
|
511
|
+
return HttpRouter.toWebHandler(app, { disableLogger: true });
|
|
512
|
+
};
|
|
513
|
+
//#endregion
|
|
514
|
+
export { ApprovePayload, AttachPayload, Attempt, Candidate, Connected, ConnectionStatus, Discovery, DiscoveryNotFound, DiscoveryResolved, DiscoverySelectionRequired, Field, Identity, Integration, Method, MethodDescriptor, OAuth, ObservePayload, PlanPayload, Readiness, Redirect, RejectPayload, SelectionRequired, Server_exports, Snapshot, StartPayload, Started, Token, api, group, layer, toWebHandler };
|
|
515
|
+
|
|
516
|
+
//# sourceMappingURL=Server.mjs.map
|