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 @@
|
|
|
1
|
+
{"version":3,"file":"Testing.mjs","names":["Storage.layerMemory","Principal.make","Context","DomainName.fromStringUnsafe","Errors.fail","Reason.NotFound","Provider.make","Provider.tokenAuth","Reason.Unauthenticated","DomainName.decode","Provider.resolveAmong","Reason.ProviderUnavailable","DomainName.isWithin","Reason.ProviderRejected","DnsRecord.ns","Resolver.Service","DomainName.fromString","Server.toWebHandler","DomainKit.layerMemory","Server.Identity","Transport.fromFetch","Transport.capabilities","storageCases","providerCases"],"sources":["../src/Testing.ts"],"sourcesContent":["/**\n * domainkit/testing — fakes and conformance runners so hosts test against the seam instead of\n * stubbing global fetch.\n */\nimport { DateTime, Effect, Layer, Redacted, Schema } from \"effect\";\n\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as DomainKit from \"./DomainKit.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as DomainName from \"./DomainName.ts\";\nimport { provider as providerCases } from \"./internal/conformance/provider.ts\";\nimport { storage as storageCases } from \"./internal/conformance/storage.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Provider from \"./Provider.ts\";\nimport * as Resolver from \"./Resolver.ts\";\nimport * as Server from \"./Server.ts\";\nimport * as Storage from \"./Storage.ts\";\nimport * as Transport from \"./Transport.ts\";\n\n/** In-memory Storage. Same as `Storage.layerMemory`; re-exported for discoverability. */\nexport const storage: Layer.Layer<Storage.Service> = Storage.layerMemory;\n\nexport const principal: Principal.Interface = Principal.make({\n ownerId: \"org_test\",\n actorId: \"user_test\",\n});\n\nexport interface FakeProviderOptions {\n readonly id?: string;\n /** Zones the fake credential can reach. Default: `example.com`. */\n readonly zones?: ReadonlyArray<string>;\n /** Pre-existing records, to produce `Noop` and `Conflict` operations. */\n readonly records?: ReadonlyArray<{ readonly zone: string; readonly record: DnsRecord.Observed }>;\n /** Offer OAuth in addition to tokens; the fake redirects to `callbackUrl` immediately. */\n readonly oauth?: boolean;\n /** Fail the write at this zero-based index, to exercise partial receipts. */\n readonly failWrite?: (index: number) => boolean;\n /** Nameservers per zone; default `ns1.<zone>`, `ns2.<zone>`. `resolver()` answers NS queries from them. */\n readonly nameservers?: Readonly<Record<string, ReadonlyArray<string>>>;\n}\n\nexport interface FakeProvider extends Provider.Definition<FakeContext> {\n readonly records: (zone: string) => ReadonlyArray<DnsRecord.Observed>;\n /** Every credential the fake issued, oldest first. */\n readonly issued: () => ReadonlyArray<string>;\n /** How many credentials were revoked at the fake. */\n readonly revoked: () => number;\n}\n\ninterface Zone {\n readonly name: string;\n readonly nameservers: ReadonlyArray<string>;\n readonly rows: Array<{ readonly id: string; readonly record: DnsRecord.Observed }>;\n}\n\n/** Every fake zone created in this process, so `resolver()` can answer from them. */\nconst registry = new Set<Zone>();\n\n/** Account context carries `account`; target context carries `zone`. One schema covers both. */\nconst Context = Schema.Struct({\n account: Schema.optionalKey(Schema.String),\n zone: Schema.optionalKey(Schema.String),\n});\ntype FakeContext = typeof Context.Type;\nconst TargetContext = Schema.Struct({ zone: Schema.String });\n\n/** A provider definition with a token method (and optional OAuth) over in-memory zones. */\nexport const provider = (options: FakeProviderOptions = {}): FakeProvider => {\n const id = options.id ?? \"fake\";\n const zones = new Map<string, Zone>();\n let nextId = 1;\n let writes = 0;\n let revokedCount = 0;\n const issued: Array<string> = [];\n for (const name of options.zones ?? [\"example.com\"]) {\n const normalized = DomainName.fromStringUnsafe(name);\n const zone: Zone = {\n name: normalized,\n nameservers: options.nameservers?.[name] ??\n options.nameservers?.[normalized] ?? [`ns1.${normalized}`, `ns2.${normalized}`],\n rows: [],\n };\n zones.set(zone.name, zone);\n registry.add(zone);\n }\n for (const { zone: name, record } of options.records ?? []) {\n const zone = zones.get(DomainName.fromStringUnsafe(name));\n if (zone === undefined) throw new Error(`Fake provider ${id} has no zone ${name}`);\n zone.rows.push({ id: `${id}-${nextId++}`, record });\n }\n\n const issue = (prefix: string): Provider.IssuedCredential => {\n const secret = `${prefix}-${issued.length + 1}`;\n issued.push(secret);\n return { secret: Redacted.make(secret), context: { account: id }, expiresAt: null };\n };\n\n const zoneOf = (target: Provider.Target) =>\n Effect.suspend(() => {\n const decoded = Schema.decodeUnknownOption(TargetContext)(target.context);\n const zone = decoded._tag === \"Some\" ? zones.get(decoded.value.zone) : undefined;\n return zone === undefined\n ? Errors.fail(new Reason.NotFound({ entity: \"zone\", id: target.zone }))\n : Effect.succeed(zone);\n });\n\n const targets = () =>\n [...zones.values()].map((zone): Provider.Target => ({\n zone: zone.name,\n context: { zone: zone.name },\n label: zone.name,\n nameservers: zone.nameservers,\n }));\n\n const oauth: Provider.OAuthAuth = {\n label: \"Sign in (fake)\",\n scopes: [\"dns\"],\n start: (input) =>\n Effect.succeed({\n authorizationUrl: `${input.callbackUrl}?${new URLSearchParams({\n state: input.state,\n code: \"fake-code\",\n })}`,\n }),\n complete: (input) =>\n input.code === \"fake-code\"\n ? Effect.map(DateTime.now, (now) => ({\n ...issue(\"oauth\"),\n expiresAt: DateTime.add(now, { hours: 1 }),\n }))\n : Errors.fail(new Reason.Unauthenticated({ message: \"fake provider rejected the code\" })),\n refresh: (credential) =>\n Redacted.value(credential.secret).startsWith(\"revoked\")\n ? Errors.fail(new Reason.Unauthenticated({ message: \"fake refresh token was revoked\" }))\n : Effect.map(DateTime.now, (now) => ({\n ...issue(\"oauth\"),\n expiresAt: DateTime.add(now, { hours: 1 }),\n })),\n revoke: () => Effect.sync(() => void (revokedCount += 1)),\n };\n\n const definition = Provider.make<FakeContext>({\n id,\n name: `Fake ${id}`,\n context: Context,\n contextVersion: \"fake.v1\",\n auth: {\n token: Provider.tokenAuth({\n label: \"Token (fake)\",\n requiredCapabilities: [\"dns:read\", \"dns:write\"],\n fields: Schema.Struct({ token: Schema.RedactedFromValue(Schema.String) }),\n authenticate: ({ token }) =>\n Redacted.value(token).length === 0\n ? Errors.fail(\n new Reason.Unauthenticated({\n message: \"fake provider rejected an empty token\",\n }),\n )\n : Effect.succeed({ secret: token, context: { account: id }, expiresAt: null }),\n }),\n ...(options.oauth === true ? { oauth } : {}),\n },\n session: () => ({\n capabilities: () => Effect.succeed([\"dns:read\", \"dns:write\"]),\n listTargets: () => Effect.succeed(targets()),\n resolveTarget: (domain) =>\n Effect.map(DomainName.decode(domain), (name) => Provider.resolveAmong(name, targets())),\n dns: (target) => ({\n list: () =>\n Effect.map(zoneOf(target), (zone) =>\n zone.rows.map(({ id: providerRecordId, record }) => ({ record, providerRecordId })),\n ),\n create: (_zone, record) =>\n Effect.gen(function* () {\n const zone = yield* zoneOf(target);\n const index = writes;\n writes += 1;\n if (options.failWrite?.(index) === true) {\n return yield* Errors.fail(\n new Reason.ProviderUnavailable({\n provider: id,\n message: `fake provider failed write ${index}`,\n }),\n );\n }\n if (!DomainName.isWithin(record.name, zone.name)) {\n return yield* Errors.fail(\n new Reason.ProviderRejected({\n provider: id,\n message: `${record.name} is outside ${zone.name}`,\n }),\n );\n }\n const providerRecordId = `${id}-${nextId++}`;\n zone.rows.push({ id: providerRecordId, record });\n return { providerRecordId };\n }),\n get: (_zone, providerRecordId) =>\n Effect.map(\n zoneOf(target),\n (zone) => zone.rows.find((row) => row.id === providerRecordId)?.record ?? null,\n ),\n delete: (_zone, providerRecordId) =>\n Effect.map(zoneOf(target), (zone) => {\n const index = zone.rows.findIndex((row) => row.id === providerRecordId);\n if (index >= 0) zone.rows.splice(index, 1);\n }),\n }),\n }),\n });\n\n return {\n ...definition,\n records: (zone) =>\n zones.get(DomainName.fromStringUnsafe(zone))?.rows.map(({ record }) => record) ?? [],\n issued: () => issued,\n revoked: () => revokedCount,\n };\n};\n\n/** NS records for every fake zone named `name`, so `resolver()` can answer authority queries. */\nconst fakeNameservers = (name: string): ReadonlyArray<DnsRecord.Observed> =>\n [...registry]\n .filter((zone) => zone.name === name)\n .flatMap((zone) =>\n zone.nameservers.map((nameserver) => DnsRecord.ns({ name: zone.name, nameserver })),\n );\n\n/** Records every fake provider currently holds for `name`, across all fake zones. */\nexport const fakeRecords = (name: string): ReadonlyArray<DnsRecord.Observed> =>\n [...registry]\n .flatMap((zone) => zone.rows.map(({ record }) => record))\n .filter((record) => record.name === name);\n\n/**\n * A resolver answering from the fake providers' zones, or from an explicit table. Every answer\n * is attributed to the resolver named `fake`.\n */\nexport const resolver = (\n answers?: ReadonlyArray<{\n readonly name: string;\n readonly records: ReadonlyArray<DnsRecord.Observed>;\n }>,\n): Layer.Layer<Resolver.Service> =>\n Layer.succeed(Resolver.Service)({\n resolve: (name, type) =>\n Effect.sync(() => {\n const normalized = DomainName.fromString(name);\n const lookup = normalized._tag === \"Some\" ? normalized.value : name;\n const table =\n answers === undefined\n ? [...fakeRecords(lookup), ...fakeNameservers(lookup)]\n : answers.filter((entry) => entry.name === lookup).flatMap((entry) => entry.records);\n const records = table.filter(\n (record) => (record._tag === \"Opaque\" ? record.type : record._tag) === type,\n );\n return [\n Resolver.Outcome.Answered({\n answer: { resolver: \"fake\", records, negative: records.length === 0, ttl: 60 },\n }),\n ];\n }),\n });\n\nexport interface TransportOptions {\n /** Which groups the fake server exposes, so a UI can be tested against a partial host. */\n readonly capabilities?: ReadonlyArray<Transport.Capability>;\n readonly provider?: FakeProviderOptions;\n}\n\nexport interface RecordedCall {\n /** `connection.inspect`, `provisioning.approve`, ... */\n readonly method: string;\n /** The call's only argument, or the argument list when the method takes more than one. */\n readonly input: unknown;\n}\n\ntype TransportMethod = (\n ...args: ReadonlyArray<never>\n) => Effect.Effect<unknown, Errors.DomainKitError>;\n\nexport interface RecordingTransport extends Transport.Interface {\n readonly calls: ReadonlyArray<RecordedCall>;\n}\n\n/**\n * A transport over an in-memory `domainkit/server` and memory Storage, recording every call. What\n * `@domainkit/react` tests render against, instead of stubbing global `fetch`.\n */\nexport const transport = (options: TransportOptions = {}): RecordingTransport => {\n const fake = provider(options.provider);\n // The handler's layer holds memory Storage and a throwaway custody key, so there is nothing to\n // release; a test that wants the server disposed builds `Server.toWebHandler` itself.\n const { handler } = Server.toWebHandler(\n DomainKit.layerMemory({ providers: [fake], resolver: resolver() }).pipe(\n Layer.merge(Layer.succeed(Server.Identity)({ principal: () => Effect.succeed(principal) })),\n ),\n );\n const live = Transport.fromFetch(\"http://domainkit.test\", {\n fetch: (input, init) => handler(new Request(input, init)),\n ...(options.capabilities === undefined ? {} : { capabilities: options.capabilities }),\n });\n const calls: Array<RecordedCall> = [];\n const recorded = Object.fromEntries(\n Transport.capabilities(live).map((capability) => [\n capability,\n Object.fromEntries(\n Object.entries(live[capability] as unknown as Record<string, TransportMethod>).map(\n ([name, method]) => [\n name,\n (...args: ReadonlyArray<never>) =>\n Effect.suspend(() => {\n calls.push({\n method: `${capability}.${name}`,\n input: args.length > 1 ? args : args[0],\n });\n return method(...args);\n }),\n ],\n ),\n ),\n ]),\n ) as Transport.Interface;\n return { ...recorded, calls };\n};\n\nexport type { Case as ProviderCase } from \"./internal/conformance/provider.ts\";\nexport type { Case as StorageCase } from \"./internal/conformance/storage.ts\";\n\nexport const conformance = {\n /** Runs every Storage invariant (tenant isolation, leases, exactly-once continuations, revocation recovery). */\n storage: storageCases,\n /** Runs create/readback/cleanup, exact-noop, conflict, stale-plan, partial-apply against a real provider definition. */\n provider: providerCases,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { Model } from "./Plan.mjs";
|
|
3
|
+
import { ProviderUnavailable } from "./Reason.mjs";
|
|
4
|
+
import { DomainKitError, decode, isDomainKitError } from "./internal/error.mjs";
|
|
5
|
+
import { Model as Model$1 } from "./Approval.mjs";
|
|
6
|
+
import { Model as Model$2 } from "./Receipt.mjs";
|
|
7
|
+
import { classify, headersFrom, requestJson } from "./internal/http.mjs";
|
|
8
|
+
import { ApprovePayload, AttachPayload, Attempt, Discovery, Integration, OAuth, ObservePayload, PlanPayload, Readiness, RejectPayload, Snapshot, StartPayload, Started, Token } from "./Server.mjs";
|
|
9
|
+
import { Effect, Schema } from "effect";
|
|
10
|
+
//#region src/Transport.ts
|
|
11
|
+
/**
|
|
12
|
+
* domainkit/client — the browser-side transport `@domainkit/react` consumes. Capability groups are
|
|
13
|
+
* optional: a host that only exposes connection routes declares just `connection`, and the parts
|
|
14
|
+
* of the UI that plan or clean up do not render.
|
|
15
|
+
*
|
|
16
|
+
* const transport = Transport.fromFetch("/api/domainkit")
|
|
17
|
+
*
|
|
18
|
+
* Every method decodes the same wire schemas `domainkit/server` encodes, so a failure arrives as
|
|
19
|
+
* the `DomainKit.Error` the lifecycle raised, reason intact.
|
|
20
|
+
*/
|
|
21
|
+
var Transport_exports = /* @__PURE__ */ __exportAll({
|
|
22
|
+
Method: () => Method,
|
|
23
|
+
allCapabilities: () => allCapabilities,
|
|
24
|
+
capabilities: () => capabilities,
|
|
25
|
+
fromAsync: () => fromAsync,
|
|
26
|
+
fromFetch: () => fromFetch,
|
|
27
|
+
toAsync: () => toAsync
|
|
28
|
+
});
|
|
29
|
+
/** How a client asks to connect. Mirrors `Connect.Method`, over the wire. */
|
|
30
|
+
const Method = {
|
|
31
|
+
/** A lone string for the common one-field provider, or the values its descriptor names. */
|
|
32
|
+
token: (values) => new Token({ values: typeof values === "string" ? { token: values } : values }),
|
|
33
|
+
oauth: (options = {}) => new OAuth(options),
|
|
34
|
+
integration: (options = {}) => new Integration(options)
|
|
35
|
+
};
|
|
36
|
+
const allCapabilities = [
|
|
37
|
+
"connection",
|
|
38
|
+
"provisioning",
|
|
39
|
+
"verification",
|
|
40
|
+
"cleanup"
|
|
41
|
+
];
|
|
42
|
+
/** Which groups this transport actually carries, in declaration order. */
|
|
43
|
+
const capabilities = (transport) => allCapabilities.filter((capability) => transport[capability] !== void 0);
|
|
44
|
+
/** A transport over `domainkit/server` routes mounted at `baseUrl`. */
|
|
45
|
+
const fromFetch = (baseUrl, options = {}) => {
|
|
46
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
47
|
+
const source = safeOrigin(base);
|
|
48
|
+
const call = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
49
|
+
const declared = options.capabilities ?? allCapabilities;
|
|
50
|
+
const request = (input) => Effect.gen(function* () {
|
|
51
|
+
const headers = yield* headersFrom(options.headers);
|
|
52
|
+
if (input.body !== void 0) headers.set("content-type", "application/json");
|
|
53
|
+
const reply = yield* requestJson({
|
|
54
|
+
fetch: call,
|
|
55
|
+
provider: source,
|
|
56
|
+
url: `${base}${input.path}`,
|
|
57
|
+
init: {
|
|
58
|
+
method: input.method,
|
|
59
|
+
headers,
|
|
60
|
+
...input.body === void 0 ? {} : { body: JSON.stringify(input.body) }
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
if (!reply.ok) {
|
|
64
|
+
const decoded = yield* Effect.result(decode(DomainKitError, reply.body, "response"));
|
|
65
|
+
return yield* decoded._tag === "Success" ? Effect.fail(decoded.success) : Effect.fail(new DomainKitError({ reason: classify(source, reply.status, reply.headers, `${input.method} ${input.path} answered ${reply.status}`) }));
|
|
66
|
+
}
|
|
67
|
+
if (input.success === null) return void 0;
|
|
68
|
+
return yield* decode(input.success, reply.body, "response");
|
|
69
|
+
});
|
|
70
|
+
/** Provisioning and cleanup share these routes; the attempt knows its kind. */
|
|
71
|
+
const approve = (input) => request({
|
|
72
|
+
method: "POST",
|
|
73
|
+
path: `/plans/${encodeURIComponent(input.planId)}/approvals`,
|
|
74
|
+
body: Schema.encodeSync(ApprovePayload)(input.operationIds === void 0 ? {} : { operationIds: input.operationIds }),
|
|
75
|
+
success: Model$1
|
|
76
|
+
});
|
|
77
|
+
const reject = (input) => request({
|
|
78
|
+
method: "POST",
|
|
79
|
+
path: `/plans/${encodeURIComponent(input.planId)}/rejections`,
|
|
80
|
+
body: Schema.encodeSync(RejectPayload)(input.reason === void 0 ? {} : { reason: input.reason }),
|
|
81
|
+
success: Attempt
|
|
82
|
+
});
|
|
83
|
+
const apply = (approvalId) => request({
|
|
84
|
+
method: "POST",
|
|
85
|
+
path: `/approvals/${encodeURIComponent(approvalId)}/apply`,
|
|
86
|
+
success: Model$2
|
|
87
|
+
});
|
|
88
|
+
const groups = {
|
|
89
|
+
connection: {
|
|
90
|
+
inspect: (domain) => request({
|
|
91
|
+
method: "GET",
|
|
92
|
+
path: `/domains/${encodeURIComponent(domain)}`,
|
|
93
|
+
success: Snapshot
|
|
94
|
+
}),
|
|
95
|
+
discover: (domain) => request({
|
|
96
|
+
method: "GET",
|
|
97
|
+
path: `/domains/${encodeURIComponent(domain)}/discovery`,
|
|
98
|
+
success: Discovery
|
|
99
|
+
}),
|
|
100
|
+
start: (input) => request({
|
|
101
|
+
method: "POST",
|
|
102
|
+
path: "/connections",
|
|
103
|
+
body: Schema.encodeSync(StartPayload)(input),
|
|
104
|
+
success: Started
|
|
105
|
+
}),
|
|
106
|
+
attach: (input) => request({
|
|
107
|
+
method: "POST",
|
|
108
|
+
path: `/connections/${encodeURIComponent(input.connectionId)}/attachments`,
|
|
109
|
+
body: Schema.encodeSync(AttachPayload)(input.zone === void 0 ? { domain: input.domain } : {
|
|
110
|
+
domain: input.domain,
|
|
111
|
+
zone: input.zone
|
|
112
|
+
}),
|
|
113
|
+
success: Started
|
|
114
|
+
}),
|
|
115
|
+
detach: (attachmentId) => request({
|
|
116
|
+
method: "DELETE",
|
|
117
|
+
path: `/attachments/${encodeURIComponent(attachmentId)}`,
|
|
118
|
+
success: null
|
|
119
|
+
}),
|
|
120
|
+
disconnect: (connectionId) => request({
|
|
121
|
+
method: "DELETE",
|
|
122
|
+
path: `/connections/${encodeURIComponent(connectionId)}`,
|
|
123
|
+
success: null
|
|
124
|
+
})
|
|
125
|
+
},
|
|
126
|
+
provisioning: {
|
|
127
|
+
plan: (input) => request({
|
|
128
|
+
method: "POST",
|
|
129
|
+
path: `/domains/${encodeURIComponent(input.domain)}/plans`,
|
|
130
|
+
body: Schema.encodeSync(PlanPayload)({ requirements: input.requirements }),
|
|
131
|
+
success: Model
|
|
132
|
+
}),
|
|
133
|
+
approve,
|
|
134
|
+
reject,
|
|
135
|
+
apply,
|
|
136
|
+
attempt: (planId) => request({
|
|
137
|
+
method: "GET",
|
|
138
|
+
path: `/plans/${encodeURIComponent(planId)}`,
|
|
139
|
+
success: Attempt
|
|
140
|
+
})
|
|
141
|
+
},
|
|
142
|
+
verification: { observe: (domain, input) => request({
|
|
143
|
+
method: "POST",
|
|
144
|
+
path: `/domains/${encodeURIComponent(domain)}/observations`,
|
|
145
|
+
body: Schema.encodeSync(ObservePayload)(input?.requirements === void 0 ? {} : { requirements: input.requirements }),
|
|
146
|
+
success: Readiness
|
|
147
|
+
}) },
|
|
148
|
+
cleanup: {
|
|
149
|
+
plan: (receiptId) => request({
|
|
150
|
+
method: "POST",
|
|
151
|
+
path: `/receipts/${encodeURIComponent(receiptId)}/cleanup-plans`,
|
|
152
|
+
success: Model
|
|
153
|
+
}),
|
|
154
|
+
approve,
|
|
155
|
+
reject,
|
|
156
|
+
apply
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return Object.fromEntries(allCapabilities.filter((capability) => declared.includes(capability)).map((capability) => [capability, groups[capability]]));
|
|
160
|
+
};
|
|
161
|
+
/** Adapt a Promise-shaped transport; a rejection that is not a `DomainKit.Error` becomes one. */
|
|
162
|
+
const fromAsync = (transport) => mapGroups(transport, (method) => (...args) => Effect.tryPromise({
|
|
163
|
+
try: () => method(...args),
|
|
164
|
+
catch: (cause) => isDomainKitError(cause) ? cause : new DomainKitError({ reason: new ProviderUnavailable({
|
|
165
|
+
provider: "domainkit",
|
|
166
|
+
message: `The transport rejected: ${String(cause)}`
|
|
167
|
+
}) })
|
|
168
|
+
}));
|
|
169
|
+
/** Adapt to Promises; every rejection is the `DomainKit.Error` the lifecycle raised. */
|
|
170
|
+
const toAsync = (transport) => mapGroups(transport, (method) => (...args) => Effect.runPromise(method(...args)));
|
|
171
|
+
/** Rebuild every declared group with each method wrapped; the two adapters differ only in `wrap`. */
|
|
172
|
+
const mapGroups = (source, wrap) => Object.fromEntries(allCapabilities.flatMap((capability) => {
|
|
173
|
+
const group = source[capability];
|
|
174
|
+
if (group === void 0) return [];
|
|
175
|
+
return [[capability, Object.fromEntries(Object.entries(group).map(([name, method]) => [name, wrap(method)]))]];
|
|
176
|
+
}));
|
|
177
|
+
const safeOrigin = (base) => {
|
|
178
|
+
try {
|
|
179
|
+
return new URL(base).origin;
|
|
180
|
+
} catch {
|
|
181
|
+
return base === "" ? "domainkit" : base;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
//#endregion
|
|
185
|
+
export { Method, Transport_exports, allCapabilities, capabilities, fromAsync, fromFetch, toAsync };
|
|
186
|
+
|
|
187
|
+
//# sourceMappingURL=Transport.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transport.mjs","names":["Server.Token","Server.OAuth","Server.Integration","Http.headersFrom","Http.requestJson","Errors.decode","Errors.DomainKitError","Http.classify","Server.ApprovePayload","Approval.Model","Server.RejectPayload","Server.Attempt","Receipt.Model","Server.Snapshot","Server.Discovery","Server.StartPayload","Server.Started","Server.AttachPayload","Server.PlanPayload","Plan.Model","Server.ObservePayload","Server.Readiness","Errors.isDomainKitError","Reason.ProviderUnavailable"],"sources":["../src/Transport.ts"],"sourcesContent":["/**\n * domainkit/client — the browser-side transport `@domainkit/react` consumes. Capability groups are\n * optional: a host that only exposes connection routes declares just `connection`, and the parts\n * of the UI that plan or clean up do not render.\n *\n * const transport = Transport.fromFetch(\"/api/domainkit\")\n *\n * Every method decodes the same wire schemas `domainkit/server` encodes, so a failure arrives as\n * the `DomainKit.Error` the lifecycle raised, reason intact.\n */\nimport { Effect, Schema } from \"effect\";\n\nimport * as Approval from \"./Approval.ts\";\nimport type * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Http from \"./internal/http.ts\";\nimport * as Plan from \"./Plan.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as Receipt from \"./Receipt.ts\";\nimport * as Server from \"./Server.ts\";\n\ntype Fx<A> = Effect.Effect<A, Errors.DomainKitError>;\n\nexport type Snapshot = Server.Snapshot;\nexport type Started = Server.Started;\nexport type Readiness = Server.Readiness;\nexport type Attempt = Server.Attempt;\nexport type Candidate = Server.Candidate;\nexport type Discovery = Server.Discovery;\nexport type Field = Server.Field;\nexport type MethodDescriptor = Server.MethodDescriptor;\nexport type Method = Server.Method;\n\n/** How a client asks to connect. Mirrors `Connect.Method`, over the wire. */\nexport const Method = {\n /** A lone string for the common one-field provider, or the values its descriptor names. */\n token: (values: string | Readonly<Record<string, string>>): Method =>\n new Server.Token({ values: typeof values === \"string\" ? { token: values } : values }),\n oauth: (options: { readonly returnTo?: string } = {}): Method => new Server.OAuth(options),\n integration: (options: { readonly returnTo?: string } = {}): Method =>\n new Server.Integration(options),\n} as const;\n\nexport interface ConnectionGroup {\n readonly inspect: (domain: string) => Fx<Snapshot>;\n /** Which of this owner's existing connections already reaches the domain. */\n readonly discover: (domain: string) => Fx<Discovery>;\n readonly start: (input: {\n readonly domain: string;\n readonly provider: string;\n readonly method: Method;\n }) => Fx<Started>;\n readonly attach: (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone?: string;\n }) => Fx<Started>;\n readonly detach: (attachmentId: string) => Fx<void>;\n readonly disconnect: (connectionId: string) => Fx<void>;\n}\n\nexport interface ProvisioningGroup {\n readonly plan: (input: {\n readonly domain: string;\n readonly requirements: ReadonlyArray<DnsRecord.Model>;\n }) => Fx<Plan.Model>;\n readonly approve: (input: {\n readonly planId: Plan.PlanId;\n readonly operationIds?: ReadonlyArray<Plan.OperationId>;\n }) => Fx<Approval.Model>;\n /** Decline the plan; terminal, and approving it afterwards fails `Stale`. */\n readonly reject: (input: {\n readonly planId: Plan.PlanId;\n readonly reason?: string;\n }) => Fx<Attempt>;\n readonly apply: (approvalId: Approval.ApprovalId) => Fx<Receipt.Model>;\n /** The stored plan with its status, approval, receipt, and rejection. */\n readonly attempt: (planId: Plan.PlanId) => Fx<Attempt>;\n}\n\nexport interface VerificationGroup {\n /**\n * Observe the domain's provisioning receipt, or `options.requirements` when supplied (required\n * for a domain with no attachment).\n */\n readonly observe: (\n domain: string,\n options?: { readonly requirements?: ReadonlyArray<DnsRecord.Model> },\n ) => Fx<Readiness>;\n}\n\nexport interface CleanupGroup {\n readonly plan: (receiptId: Receipt.ReceiptId) => Fx<Plan.Model>;\n readonly approve: (input: {\n readonly planId: Plan.PlanId;\n readonly operationIds?: ReadonlyArray<Plan.OperationId>;\n }) => Fx<Approval.Model>;\n readonly reject: (input: {\n readonly planId: Plan.PlanId;\n readonly reason?: string;\n }) => Fx<Attempt>;\n readonly apply: (approvalId: Approval.ApprovalId) => Fx<Receipt.Model>;\n}\n\nexport interface Interface {\n readonly connection?: ConnectionGroup;\n readonly provisioning?: ProvisioningGroup;\n readonly verification?: VerificationGroup;\n readonly cleanup?: CleanupGroup;\n}\n\nexport type Capability = keyof Interface;\n\nexport const allCapabilities: ReadonlyArray<Capability> = [\n \"connection\",\n \"provisioning\",\n \"verification\",\n \"cleanup\",\n];\n\n/** Which groups this transport actually carries, in declaration order. */\nexport const capabilities = (transport: Interface): ReadonlyArray<Capability> =>\n allCapabilities.filter((capability) => transport[capability] !== undefined);\n\nexport interface FetchOptions {\n readonly fetch?: Http.Fetch;\n readonly headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);\n /** Which groups the server exposes. Default: all. */\n readonly capabilities?: ReadonlyArray<Capability>;\n}\n\n// ---------------------------------------------------------------------------------------------\n// fetch\n// ---------------------------------------------------------------------------------------------\n\n/** A transport over `domainkit/server` routes mounted at `baseUrl`. */\nexport const fromFetch = (baseUrl: string, options: FetchOptions = {}): Interface => {\n const base = baseUrl.replace(/\\/+$/, \"\");\n const source = safeOrigin(base);\n // Resolved at call time and invoked as a free function: a browser `fetch` throws \"Illegal\n // invocation\" when called as a method of anything but `window`, and a host may polyfill later.\n const call: Http.Fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));\n const declared = options.capabilities ?? allCapabilities;\n\n const request = <A>(input: {\n readonly method: \"GET\" | \"POST\" | \"DELETE\";\n readonly path: string;\n readonly body?: unknown;\n readonly success: Schema.Codec<A, unknown> | null;\n }): Fx<A> =>\n Effect.gen(function* () {\n const headers = yield* Http.headersFrom(options.headers);\n if (input.body !== undefined) headers.set(\"content-type\", \"application/json\");\n const reply = yield* Http.requestJson({\n fetch: call,\n provider: source,\n url: `${base}${input.path}`,\n init: {\n method: input.method,\n headers,\n ...(input.body === undefined ? {} : { body: JSON.stringify(input.body) }),\n },\n });\n if (!reply.ok) {\n // The server answers failures with the `DomainKit.Error` value itself. Anything else came\n // from in front of it: a proxy, a login page, a maintenance window.\n const decoded = yield* Effect.result(\n Errors.decode(Errors.DomainKitError, reply.body, \"response\"),\n );\n return yield* decoded._tag === \"Success\"\n ? Effect.fail(decoded.success)\n : Effect.fail(\n new Errors.DomainKitError({\n reason: Http.classify(\n source,\n reply.status,\n reply.headers,\n `${input.method} ${input.path} answered ${reply.status}`,\n ),\n }),\n );\n }\n if (input.success === null) return undefined as A;\n return yield* Errors.decode(input.success, reply.body, \"response\");\n });\n\n /** Provisioning and cleanup share these routes; the attempt knows its kind. */\n const approve = (input: {\n readonly planId: Plan.PlanId;\n readonly operationIds?: ReadonlyArray<Plan.OperationId>;\n }) =>\n request({\n method: \"POST\",\n path: `/plans/${encodeURIComponent(input.planId)}/approvals`,\n body: Schema.encodeSync(Server.ApprovePayload)(\n input.operationIds === undefined ? {} : { operationIds: input.operationIds },\n ),\n success: Approval.Model,\n });\n\n const reject = (input: { readonly planId: Plan.PlanId; readonly reason?: string }) =>\n request({\n method: \"POST\",\n path: `/plans/${encodeURIComponent(input.planId)}/rejections`,\n body: Schema.encodeSync(Server.RejectPayload)(\n input.reason === undefined ? {} : { reason: input.reason },\n ),\n success: Server.Attempt,\n });\n\n const apply = (approvalId: Approval.ApprovalId) =>\n request({\n method: \"POST\",\n path: `/approvals/${encodeURIComponent(approvalId)}/apply`,\n success: Receipt.Model,\n });\n\n const connection: ConnectionGroup = {\n inspect: (domain) =>\n request({\n method: \"GET\",\n path: `/domains/${encodeURIComponent(domain)}`,\n success: Server.Snapshot,\n }),\n discover: (domain) =>\n request({\n method: \"GET\",\n path: `/domains/${encodeURIComponent(domain)}/discovery`,\n success: Server.Discovery,\n }),\n start: (input) =>\n request({\n method: \"POST\",\n path: \"/connections\",\n body: Schema.encodeSync(Server.StartPayload)(input),\n success: Server.Started,\n }),\n attach: (input) =>\n request({\n method: \"POST\",\n path: `/connections/${encodeURIComponent(input.connectionId)}/attachments`,\n body: Schema.encodeSync(Server.AttachPayload)(\n input.zone === undefined\n ? { domain: input.domain }\n : { domain: input.domain, zone: input.zone },\n ),\n success: Server.Started,\n }),\n detach: (attachmentId) =>\n request({\n method: \"DELETE\",\n path: `/attachments/${encodeURIComponent(attachmentId)}`,\n success: null,\n }),\n disconnect: (connectionId) =>\n request({\n method: \"DELETE\",\n path: `/connections/${encodeURIComponent(connectionId)}`,\n success: null,\n }),\n };\n\n const provisioning: ProvisioningGroup = {\n plan: (input) =>\n request({\n method: \"POST\",\n path: `/domains/${encodeURIComponent(input.domain)}/plans`,\n body: Schema.encodeSync(Server.PlanPayload)({ requirements: input.requirements }),\n success: Plan.Model,\n }),\n approve,\n reject,\n apply,\n attempt: (planId) =>\n request({\n method: \"GET\",\n path: `/plans/${encodeURIComponent(planId)}`,\n success: Server.Attempt,\n }),\n };\n\n const verification: VerificationGroup = {\n observe: (domain, input) =>\n request({\n method: \"POST\",\n path: `/domains/${encodeURIComponent(domain)}/observations`,\n body: Schema.encodeSync(Server.ObservePayload)(\n input?.requirements === undefined ? {} : { requirements: input.requirements },\n ),\n success: Server.Readiness,\n }),\n };\n\n const cleanup: CleanupGroup = {\n plan: (receiptId) =>\n request({\n method: \"POST\",\n path: `/receipts/${encodeURIComponent(receiptId)}/cleanup-plans`,\n success: Plan.Model,\n }),\n approve,\n reject,\n apply,\n };\n\n const groups = { connection, provisioning, verification, cleanup };\n return Object.fromEntries(\n allCapabilities\n .filter((capability) => declared.includes(capability))\n .map((capability) => [capability, groups[capability]]),\n );\n};\n\n// ---------------------------------------------------------------------------------------------\n// Promise adapters\n// ---------------------------------------------------------------------------------------------\n\n/** The same transport in Promises, for hosts and tests that do not run Effect. */\nexport type AsyncTransport = {\n readonly [K in keyof Interface]?: {\n readonly [M in keyof NonNullable<Interface[K]>]: NonNullable<Interface[K]>[M] extends (\n ...args: infer A\n ) => Effect.Effect<infer R, Errors.DomainKitError>\n ? (...args: A) => Promise<R>\n : never;\n };\n};\n\n/** Adapt a Promise-shaped transport; a rejection that is not a `DomainKit.Error` becomes one. */\nexport const fromAsync = (transport: AsyncTransport): Interface =>\n mapGroups(\n transport,\n (method) =>\n (...args: ReadonlyArray<never>) =>\n Effect.tryPromise({\n try: () => method(...args) as Promise<unknown>,\n catch: (cause) =>\n Errors.isDomainKitError(cause)\n ? cause\n : new Errors.DomainKitError({\n reason: new Reason.ProviderUnavailable({\n provider: \"domainkit\",\n message: `The transport rejected: ${String(cause)}`,\n }),\n }),\n }),\n ) as Interface;\n\n/** Adapt to Promises; every rejection is the `DomainKit.Error` the lifecycle raised. */\nexport const toAsync = (transport: Interface): AsyncTransport =>\n mapGroups(\n transport,\n (method) =>\n (...args: ReadonlyArray<never>) =>\n Effect.runPromise(method(...args) as Effect.Effect<unknown, Errors.DomainKitError>),\n ) as AsyncTransport;\n\ntype AnyMethod = (...args: ReadonlyArray<never>) => unknown;\ntype AnyGroups = { readonly [key: string]: Record<string, AnyMethod> | undefined };\n\n/** Rebuild every declared group with each method wrapped; the two adapters differ only in `wrap`. */\nconst mapGroups = (source: object, wrap: (method: AnyMethod) => AnyMethod): unknown =>\n Object.fromEntries(\n allCapabilities.flatMap((capability) => {\n const group = (source as AnyGroups)[capability];\n if (group === undefined) return [];\n return [\n [\n capability,\n Object.fromEntries(Object.entries(group).map(([name, method]) => [name, wrap(method)])),\n ],\n ];\n }),\n );\n\n// ---------------------------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------------------------\n\nconst safeOrigin = (base: string): string => {\n try {\n return new URL(base).origin;\n } catch {\n return base === \"\" ? \"domainkit\" : base;\n }\n};\n"],"mappings":"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"}
|
package/dist/Vercel.mjs
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { decode } from "./DomainName.mjs";
|
|
3
|
+
import { Unauthenticated } from "./Reason.mjs";
|
|
4
|
+
import { fail } from "./internal/error.mjs";
|
|
5
|
+
import { make, resolveAmong, tokenAuth } from "./Provider.mjs";
|
|
6
|
+
import { resolve } from "./internal/config.mjs";
|
|
7
|
+
import { dns, exchangeCode, teams, user, zones } from "./internal/vercel/client.mjs";
|
|
8
|
+
import { Effect, Redacted, Schema } from "effect";
|
|
9
|
+
//#region src/Vercel.ts
|
|
10
|
+
/** Vercel: personal tokens and marketplace integrations. */
|
|
11
|
+
var Vercel_exports = /* @__PURE__ */ __exportAll({
|
|
12
|
+
TeamContext: () => TeamContext,
|
|
13
|
+
TokenFields: () => TokenFields,
|
|
14
|
+
provider: () => provider
|
|
15
|
+
});
|
|
16
|
+
/** `teamId` is `null` for a personal account; targets carry the team that owns the zone. */
|
|
17
|
+
const TeamContext = Schema.Struct({ teamId: Schema.NullOr(Schema.String) });
|
|
18
|
+
/** Token-method input: the access token, and a team id to scope the connection to one team. */
|
|
19
|
+
const TokenFields = Schema.Struct({
|
|
20
|
+
token: Schema.RedactedFromValue(Schema.String),
|
|
21
|
+
teamId: Schema.optionalKey(Schema.String)
|
|
22
|
+
});
|
|
23
|
+
const capabilities = ["dns:read", "dns:write"];
|
|
24
|
+
const provider = (options = {}) => {
|
|
25
|
+
const fetch = options.fetch ?? globalThis.fetch;
|
|
26
|
+
const baseUrl = (options.baseUrl ?? "https://api.vercel.com").replace(/\/$/, "");
|
|
27
|
+
const client = (token) => ({
|
|
28
|
+
token,
|
|
29
|
+
fetch,
|
|
30
|
+
baseUrl
|
|
31
|
+
});
|
|
32
|
+
const targetOf = (domain, label) => ({
|
|
33
|
+
zone: domain.name,
|
|
34
|
+
context: { teamId: domain.teamId },
|
|
35
|
+
label: `${domain.name} (${label})`,
|
|
36
|
+
nameservers: domain.nameservers.length > 0 ? domain.nameservers : domain.intendedNameservers
|
|
37
|
+
});
|
|
38
|
+
const integration = options.integration === void 0 ? void 0 : integrationAuth(options.integration);
|
|
39
|
+
function integrationAuth(settings) {
|
|
40
|
+
return {
|
|
41
|
+
label: "Install the Vercel integration",
|
|
42
|
+
start: (input) => {
|
|
43
|
+
const authorizationUrl = new URL(`https://vercel.com/integrations/${encodeURIComponent(settings.slug)}/new`);
|
|
44
|
+
authorizationUrl.searchParams.set("source", "external");
|
|
45
|
+
authorizationUrl.searchParams.set("state", input.state);
|
|
46
|
+
return Effect.succeed({ authorizationUrl: authorizationUrl.toString() });
|
|
47
|
+
},
|
|
48
|
+
complete: (input) => Effect.gen(function* () {
|
|
49
|
+
const clientId = yield* resolve(settings.clientId, "integration.clientId");
|
|
50
|
+
const clientSecret = yield* resolve(settings.clientSecret, "integration.clientSecret");
|
|
51
|
+
const token = yield* exchangeCode({
|
|
52
|
+
options: client(""),
|
|
53
|
+
clientId,
|
|
54
|
+
clientSecret: Redacted.value(clientSecret),
|
|
55
|
+
code: input.code,
|
|
56
|
+
callbackUrl: input.callbackUrl
|
|
57
|
+
});
|
|
58
|
+
const callbackTeam = input.params.teamId;
|
|
59
|
+
if (callbackTeam !== void 0 && callbackTeam !== token.team_id) return yield* fail(new Unauthenticated({ message: "Vercel integration callback team does not match the installed team" }));
|
|
60
|
+
return {
|
|
61
|
+
secret: Redacted.make(token.access_token),
|
|
62
|
+
context: { teamId: token.team_id },
|
|
63
|
+
expiresAt: null
|
|
64
|
+
};
|
|
65
|
+
})
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return make({
|
|
69
|
+
id: "vercel",
|
|
70
|
+
name: "Vercel",
|
|
71
|
+
context: TeamContext,
|
|
72
|
+
contextVersion: "vercel.v1",
|
|
73
|
+
auth: {
|
|
74
|
+
token: tokenAuth({
|
|
75
|
+
label: "Access token",
|
|
76
|
+
docsUrl: "https://vercel.com/account/settings/tokens",
|
|
77
|
+
requiredCapabilities: capabilities,
|
|
78
|
+
fields: TokenFields,
|
|
79
|
+
authenticate: ({ token, teamId }) => user(client(Redacted.value(token))).pipe(Effect.map(() => ({
|
|
80
|
+
secret: token,
|
|
81
|
+
context: { teamId: teamId ?? null },
|
|
82
|
+
expiresAt: null
|
|
83
|
+
})))
|
|
84
|
+
}),
|
|
85
|
+
...integration === void 0 ? {} : { integration }
|
|
86
|
+
},
|
|
87
|
+
session: (credential) => {
|
|
88
|
+
const clientOptions = client(Redacted.value(credential.secret));
|
|
89
|
+
const context = Schema.decodeUnknownOption(TeamContext)(credential.context);
|
|
90
|
+
const teamId = context._tag === "Some" ? context.value.teamId : null;
|
|
91
|
+
const listTargets = () => teamId !== null ? zones(clientOptions, teamId).pipe(Effect.map((domains) => domains.map((domain) => targetOf(domain, teamId)))) : Effect.gen(function* () {
|
|
92
|
+
const personal = yield* zones(clientOptions, null);
|
|
93
|
+
const teams$1 = yield* teams(clientOptions).pipe(Effect.catchIf((error) => error.reason._tag !== "ProviderUnavailable", () => Effect.succeed([])));
|
|
94
|
+
const perTeam = yield* Effect.forEach(teams$1, (team) => zones(clientOptions, team.id).pipe(Effect.map((domains) => domains.map((domain) => targetOf(domain, team.name ?? team.slug)))));
|
|
95
|
+
return [...personal.map((domain) => targetOf(domain, "personal")), ...perTeam.flat()];
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
capabilities: () => Effect.succeed(capabilities),
|
|
99
|
+
listTargets,
|
|
100
|
+
resolveTarget: (domain) => Effect.gen(function* () {
|
|
101
|
+
const name = yield* decode(domain);
|
|
102
|
+
const targets = yield* listTargets();
|
|
103
|
+
return resolveAmong(name, targets);
|
|
104
|
+
}),
|
|
105
|
+
dns: (target) => {
|
|
106
|
+
const scope = Schema.decodeUnknownOption(TeamContext)(target.context);
|
|
107
|
+
return dns(clientOptions, scope._tag === "Some" ? scope.value.teamId : teamId);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
//#endregion
|
|
114
|
+
export { TeamContext, TokenFields, Vercel_exports, provider };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=Vercel.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Vercel.mjs","names":["Client.exchangeCode","Errors.fail","Reason.Unauthenticated","Provider.make","Provider.tokenAuth","Client.user","Client.zones","teams","Client.teams","DomainName.decode","Provider.resolveAmong","Client.dns"],"sources":["../src/Vercel.ts"],"sourcesContent":["/** Vercel: personal tokens and marketplace integrations. */\nimport { type Config, Effect, Redacted, Schema } from \"effect\";\n\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as DomainName from \"./DomainName.ts\";\nimport { resolve } from \"./internal/config.ts\";\nimport type { Fetch } from \"./internal/http.ts\";\nimport * as Client from \"./internal/vercel/client.ts\";\nimport type * as Protocol from \"./internal/vercel/protocol.ts\";\nimport * as Provider from \"./Provider.ts\";\n\n/** `teamId` is `null` for a personal account; targets carry the team that owns the zone. */\nexport const TeamContext = Schema.Struct({ teamId: Schema.NullOr(Schema.String) });\nexport type TeamContext = typeof TeamContext.Type;\n\n/** Token-method input: the access token, and a team id to scope the connection to one team. */\nexport const TokenFields = Schema.Struct({\n token: Schema.RedactedFromValue(Schema.String),\n teamId: Schema.optionalKey(Schema.String),\n});\n\nexport interface Options {\n /** Omit to offer tokens only. */\n readonly integration?: {\n readonly clientId: string | Config.Config<string>;\n readonly clientSecret: Redacted.Redacted<string> | Config.Config<Redacted.Redacted<string>>;\n readonly slug: string;\n };\n readonly fetch?: Fetch;\n readonly baseUrl?: string;\n}\n\nconst capabilities = [\"dns:read\", \"dns:write\"] as const;\n\nexport const provider = (options: Options = {}): Provider.Definition<TeamContext> => {\n const fetch = options.fetch ?? globalThis.fetch;\n const baseUrl = (options.baseUrl ?? \"https://api.vercel.com\").replace(/\\/$/, \"\");\n const client = (token: string): Client.Options => ({ token, fetch, baseUrl });\n\n const targetOf = (domain: Protocol.Domain, label: string): Provider.Target => ({\n zone: domain.name,\n context: { teamId: domain.teamId } satisfies TeamContext,\n label: `${domain.name} (${label})`,\n nameservers: domain.nameservers.length > 0 ? domain.nameservers : domain.intendedNameservers,\n });\n\n const integration =\n options.integration === undefined ? undefined : integrationAuth(options.integration);\n\n function integrationAuth(\n settings: NonNullable<Options[\"integration\"]>,\n ): Provider.IntegrationAuth {\n return {\n label: \"Install the Vercel integration\",\n start: (input) => {\n const authorizationUrl = new URL(\n `https://vercel.com/integrations/${encodeURIComponent(settings.slug)}/new`,\n );\n authorizationUrl.searchParams.set(\"source\", \"external\");\n authorizationUrl.searchParams.set(\"state\", input.state);\n return Effect.succeed({ authorizationUrl: authorizationUrl.toString() });\n },\n complete: (input) =>\n Effect.gen(function* () {\n const clientId = yield* resolve(settings.clientId, \"integration.clientId\");\n const clientSecret = yield* resolve(settings.clientSecret, \"integration.clientSecret\");\n const token = yield* Client.exchangeCode({\n options: client(\"\"),\n clientId,\n clientSecret: Redacted.value(clientSecret),\n code: input.code,\n callbackUrl: input.callbackUrl,\n });\n const callbackTeam = input.params.teamId;\n if (callbackTeam !== undefined && callbackTeam !== token.team_id) {\n return yield* Errors.fail(\n new Reason.Unauthenticated({\n message: \"Vercel integration callback team does not match the installed team\",\n }),\n );\n }\n return {\n secret: Redacted.make(token.access_token),\n context: { teamId: token.team_id } satisfies TeamContext,\n expiresAt: null,\n };\n }),\n };\n }\n\n return Provider.make<TeamContext>({\n id: \"vercel\",\n name: \"Vercel\",\n context: TeamContext,\n contextVersion: \"vercel.v1\",\n auth: {\n token: Provider.tokenAuth({\n label: \"Access token\",\n docsUrl: \"https://vercel.com/account/settings/tokens\",\n requiredCapabilities: capabilities,\n fields: TokenFields,\n authenticate: ({ token, teamId }) =>\n Client.user(client(Redacted.value(token))).pipe(\n Effect.map(() => ({\n secret: token,\n context: { teamId: teamId ?? null } satisfies TeamContext,\n expiresAt: null,\n })),\n ),\n }),\n ...(integration === undefined ? {} : { integration }),\n },\n session: (credential) => {\n const clientOptions = client(Redacted.value(credential.secret));\n const context = Schema.decodeUnknownOption(TeamContext)(credential.context);\n const teamId = context._tag === \"Some\" ? context.value.teamId : null;\n const listTargets = (): Effect.Effect<\n ReadonlyArray<Provider.Target>,\n Errors.DomainKitError\n > =>\n teamId !== null\n ? Client.zones(clientOptions, teamId).pipe(\n Effect.map((domains) => domains.map((domain) => targetOf(domain, teamId))),\n )\n : Effect.gen(function* () {\n const personal = yield* Client.zones(clientOptions, null);\n const teams = yield* Client.teams(clientOptions).pipe(\n Effect.catchIf(\n (error) => error.reason._tag !== \"ProviderUnavailable\",\n () => Effect.succeed([]),\n ),\n );\n const perTeam = yield* Effect.forEach(teams, (team) =>\n Client.zones(clientOptions, team.id).pipe(\n Effect.map((domains) =>\n domains.map((domain) => targetOf(domain, team.name ?? team.slug)),\n ),\n ),\n );\n return [...personal.map((domain) => targetOf(domain, \"personal\")), ...perTeam.flat()];\n });\n return {\n capabilities: () => Effect.succeed(capabilities),\n listTargets,\n resolveTarget: (domain) =>\n Effect.gen(function* () {\n const name = yield* DomainName.decode(domain);\n const targets = yield* listTargets();\n return Provider.resolveAmong(name, targets);\n }),\n dns: (target) => {\n const scope = Schema.decodeUnknownOption(TeamContext)(target.context);\n return Client.dns(clientOptions, scope._tag === \"Some\" ? scope.value.teamId : teamId);\n },\n };\n },\n });\n};\n"],"mappings":"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"}
|