domainkit 0.0.1 → 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +115 -1
- package/dist/auth-Df9BuC25.mjs +460 -0
- package/dist/auth-Df9BuC25.mjs.map +1 -0
- package/dist/client-DvXfJx_E.mjs +534 -0
- package/dist/client-DvXfJx_E.mjs.map +1 -0
- package/dist/cloudflare-C_ixZsJo.mjs +46 -0
- package/dist/cloudflare-C_ixZsJo.mjs.map +1 -0
- package/dist/cloudflare-DClWuAm8.d.mts +23 -0
- package/dist/cloudflare.d.mts +3 -0
- package/dist/cloudflare.mjs +2 -0
- package/dist/dns-record-Dzo4flM5.mjs +259 -0
- package/dist/dns-record-Dzo4flM5.mjs.map +1 -0
- package/dist/effect-cloudflare.d.mts +2 -0
- package/dist/effect-cloudflare.mjs +2 -0
- package/dist/effect-vercel.d.mts +2 -0
- package/dist/effect-vercel.mjs +2 -0
- package/dist/effect.d.mts +63 -0
- package/dist/effect.mjs +28 -0
- package/dist/effect.mjs.map +1 -0
- package/dist/index-D1KtyjuI.d.mts +71 -0
- package/dist/index-DQAjUrZu.d.mts +94 -0
- package/dist/index.d.mts +128 -0
- package/dist/index.mjs +198 -0
- package/dist/index.mjs.map +1 -0
- package/dist/manifest-DjH3BVB9.d.mts +579 -0
- package/dist/receipt-C7PjnibZ.mjs +28 -0
- package/dist/receipt-C7PjnibZ.mjs.map +1 -0
- package/dist/resolver-BjZwN2JE.d.mts +2327 -0
- package/dist/resolver-D5nVrR10.mjs +153 -0
- package/dist/resolver-D5nVrR10.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/testing.d.mts +47 -0
- package/dist/testing.mjs +117 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/vercel-BWz_9Y1v.mjs +38 -0
- package/dist/vercel-BWz_9Y1v.mjs.map +1 -0
- package/dist/vercel-Vc_K_87s.d.mts +22 -0
- package/dist/vercel.d.mts +3 -0
- package/dist/vercel.mjs +2 -0
- package/dist/verify-CeMW6XtK.mjs +1109 -0
- package/dist/verify-CeMW6XtK.mjs.map +1 -0
- package/dist/verify-DPB1OfZ5.d.mts +979 -0
- package/package.json +94 -6
- package/bun.lock +0 -66
- package/index.ts +0 -1
- package/tsconfig.json +0 -30
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-Df9BuC25.mjs","names":["S","S","decode","DomainName.decode","failure","DnsRecord.decode","DnsRecord.Opaque","DnsProvider.Error","Protocol.ZoneListEnvelope","Protocol.RecordListEnvelope","Records.decode","Records.encode","Protocol.RecordEnvelope","Protocol.TokenEnvelope","DomainName.decode","S","Protocol.BaseEnvelope","DnsProvider.Error","Client.make"],"sources":["../src/providers/cloudflare/protocol.ts","../src/providers/cloudflare/records.ts","../src/providers/cloudflare/client.ts","../src/providers/cloudflare/auth.ts"],"sourcesContent":["import { Schema as S } from \"effect\";\n\nexport const ErrorDetail = S.Struct({\n code: S.Number,\n message: S.String,\n});\nexport interface ErrorDetail extends S.Schema.Type<typeof ErrorDetail> {}\n\nexport const ResultInfo = S.Struct({\n count: S.optionalKey(S.Number),\n page: S.optionalKey(S.Number),\n per_page: S.optionalKey(S.Number),\n total_count: S.optionalKey(S.Number),\n total_pages: S.optionalKey(S.Number),\n});\n\nconst Message = S.Struct({\n code: S.optionalKey(S.Number),\n message: S.String,\n});\n\nexport const BaseEnvelope = S.Struct({\n errors: S.Array(ErrorDetail),\n messages: S.Array(Message),\n success: S.Boolean,\n});\n\nexport const Account = S.Struct({\n id: S.String,\n name: S.String,\n});\nexport interface Account extends S.Schema.Type<typeof Account> {}\n\nexport const Zone = S.Struct({\n account: Account,\n id: S.String,\n name: S.String,\n name_servers: S.Array(S.String),\n status: S.optionalKey(S.String),\n});\nexport interface Zone extends S.Schema.Type<typeof Zone> {}\n\nexport const Record = S.Struct({\n content: S.optionalKey(S.String),\n data: S.optionalKey(S.Unknown),\n id: S.String,\n name: S.String,\n priority: S.optionalKey(S.Number),\n proxied: S.optionalKey(S.Boolean),\n ttl: S.Number,\n type: S.String,\n});\nexport interface Record extends S.Schema.Type<typeof Record> {}\n\nexport const Token = S.Struct({\n expires_on: S.optionalKey(S.String),\n id: S.String,\n not_before: S.optionalKey(S.String),\n status: S.Literals([\"active\", \"disabled\", \"expired\"]),\n});\nexport interface Token extends S.Schema.Type<typeof Token> {}\n\nexport const ZoneListEnvelope = S.Struct({\n errors: S.Array(ErrorDetail),\n messages: S.Array(Message),\n result: S.Array(Zone),\n result_info: S.optionalKey(ResultInfo),\n success: S.Boolean,\n});\nexport const RecordListEnvelope = S.Struct({\n errors: S.Array(ErrorDetail),\n messages: S.Array(Message),\n result: S.Array(Record),\n result_info: S.optionalKey(ResultInfo),\n success: S.Boolean,\n});\nexport const RecordEnvelope = S.Struct({\n errors: S.Array(ErrorDetail),\n messages: S.Array(Message),\n result: Record,\n success: S.Boolean,\n});\nexport const TokenEnvelope = S.Struct({\n errors: S.Array(ErrorDetail),\n messages: S.Array(Message),\n result: Token,\n success: S.Boolean,\n});\n","import { Effect, Schema as S } from \"effect\";\n\nimport * as DomainName from \"../../domain/domain-name.ts\";\nimport * as DnsRecord from \"../../domain/dns-record.ts\";\nimport * as DnsProvider from \"../../provider/provider.ts\";\nimport type * as Protocol from \"./protocol.ts\";\n\nconst Data = S.Struct({\n flags: S.optionalKey(S.Number),\n port: S.optionalKey(S.Number),\n priority: S.optionalKey(S.Number),\n tag: S.optionalKey(S.String),\n target: S.optionalKey(S.String),\n value: S.optionalKey(S.String),\n weight: S.optionalKey(S.Number),\n});\n\nexport type CreateBody = Readonly<Record<string, unknown>>;\n\nexport const decode = Effect.fn(\"CloudflareRecords.decode\")((record: Protocol.Record) =>\n Effect.gen(function* () {\n const name = yield* DomainName.decode(record.name).pipe(\n Effect.match({ onFailure: () => null, onSuccess: (value) => value }),\n );\n if (name === null) return yield* decodeOpaque(record);\n const common = {\n metadata: {\n ownership: \"provider\",\n provenance: record.proxied === true ? \"cloudflare:proxied\" : \"cloudflare\",\n purpose: \"existing DNS record\",\n },\n name,\n policy: record.type === \"CNAME\" ? \"exclusive\" : \"append\",\n ttl: record.ttl === 1 ? null : record.ttl,\n } as const;\n const content = () =>\n record.content === undefined\n ? Effect.fail(failure(\"decodeRecord\", `Cloudflare ${record.type} record has no content`))\n : Effect.succeed(record.content);\n\n switch (record.type) {\n case \"A\":\n case \"AAAA\":\n return yield* DnsRecord.decode({\n ...common,\n _tag: record.type,\n address: yield* content(),\n }).pipe(Effect.mapError((cause) => failure(\"decodeRecord\", cause.message)));\n case \"CNAME\":\n case \"NS\":\n return yield* DnsRecord.decode({\n ...common,\n _tag: record.type,\n target: yield* content(),\n }).pipe(Effect.mapError((cause) => failure(\"decodeRecord\", cause.message)));\n case \"TXT\":\n return yield* DnsRecord.decode({\n ...common,\n _tag: \"TXT\",\n value: yield* content(),\n }).pipe(Effect.mapError((cause) => failure(\"decodeRecord\", cause.message)));\n case \"MX\":\n return yield* DnsRecord.decode({\n ...common,\n _tag: \"MX\",\n exchange: yield* content(),\n priority: record.priority,\n }).pipe(Effect.mapError((cause) => failure(\"decodeRecord\", cause.message)));\n case \"CAA\": {\n const data = yield* decodeData(record);\n return yield* DnsRecord.decode({ ...common, _tag: \"CAA\", ...data }).pipe(\n Effect.mapError((cause) => failure(\"decodeRecord\", cause.message)),\n );\n }\n case \"SRV\": {\n const data = yield* decodeData(record);\n return yield* DnsRecord.decode({ ...common, _tag: \"SRV\", ...data }).pipe(\n Effect.mapError((cause) => failure(\"decodeRecord\", cause.message)),\n );\n }\n default:\n return yield* decodeOpaque(record);\n }\n }),\n);\n\nconst decodeOpaque = Effect.fn(\"CloudflareRecords.decodeOpaque\")((record: Protocol.Record) =>\n S.decodeUnknownEffect(DnsRecord.Opaque)({\n _tag: \"Opaque\",\n name: record.name,\n providerRecordId: record.id,\n providerType: record.type,\n }).pipe(Effect.mapError((cause) => failure(\"decodeRecord\", cause.message))),\n);\n\nexport function encode(record: DnsRecord.DnsRecord): CreateBody {\n const common = {\n name: record.name,\n proxied: false,\n ttl: record.ttl ?? 1,\n type: record._tag,\n };\n switch (record._tag) {\n case \"A\":\n case \"AAAA\":\n return { ...common, content: record.address };\n case \"CNAME\":\n case \"NS\":\n return { ...common, content: record.target };\n case \"TXT\":\n return { ...common, content: record.value };\n case \"MX\":\n return { ...common, content: record.exchange, priority: record.priority };\n case \"CAA\":\n return {\n ...common,\n data: { flags: record.flags, tag: record.tag, value: record.value },\n };\n case \"SRV\":\n return {\n ...common,\n data: {\n port: record.port,\n priority: record.priority,\n target: record.target,\n weight: record.weight,\n },\n };\n }\n}\n\nconst decodeData = Effect.fn(\"CloudflareRecords.decodeData\")((record: Protocol.Record) =>\n S.decodeUnknownEffect(Data)(record.data).pipe(\n Effect.mapError((cause) => failure(\"decodeRecord\", cause.message)),\n ),\n);\n\nfunction failure(\n operation: string,\n message: string,\n fields: Partial<Pick<DnsProvider.Error, \"reason\">> = {},\n): DnsProvider.Error {\n return new DnsProvider.Error({\n message,\n operation,\n providerId: \"cloudflare\",\n reason: fields.reason ?? \"response\",\n });\n}\n","import { Effect, Schema as S } from \"effect\";\n\nimport type * as ProviderAuth from \"../../auth/manifest.ts\";\nimport type * as Secret from \"../../auth/secret.ts\";\nimport * as DomainName from \"../../domain/domain-name.ts\";\nimport type * as DnsRecord from \"../../domain/dns-record.ts\";\nimport * as DnsProvider from \"../../provider/provider.ts\";\nimport * as Protocol from \"./protocol.ts\";\nimport * as Records from \"./records.ts\";\n\nexport type Fetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n/** Configuration for an Effect-native Cloudflare authoritative-DNS client. */\nexport interface Options {\n readonly accountId: string;\n readonly baseUrl?: string;\n /** Capabilities the host required when issuing or authorizing this credential. */\n readonly capabilities: ProviderAuth.TokenValidation[\"capabilities\"];\n readonly fetch?: Fetch;\n readonly token: Secret.Value;\n readonly tokenKind?: \"account\" | \"user\";\n}\n\nexport interface ListZonesInput {\n readonly accountId?: string;\n readonly name?: DomainName.DomainName;\n}\n\nexport type Account = Protocol.Account;\n\n/** A portable Cloudflare authoritative zone with normalized nameserver evidence. */\nexport interface Zone {\n readonly accountId: string;\n readonly id: string;\n readonly name: DomainName.DomainName;\n readonly nameservers: ReadonlyArray<DomainName.DomainName>;\n readonly status: string | null;\n}\n\nexport interface Interface extends DnsProvider.Interface {\n readonly listAccounts: () => Effect.Effect<ReadonlyArray<Account>, DnsProvider.Error>;\n readonly listZones: (\n input?: ListZonesInput,\n ) => Effect.Effect<ReadonlyArray<Zone>, DnsProvider.Error>;\n readonly validateToken: () => Effect.Effect<ProviderAuth.TokenValidation, DnsProvider.Error>;\n}\n\n/** Creates an Effect-native Cloudflare client without owning the credential lifecycle. */\nexport function make(options: Options): Interface {\n const fetch = options.fetch ?? globalThis.fetch;\n const baseUrl = (options.baseUrl ?? \"https://api.cloudflare.com/client/v4\").replace(/\\/$/, \"\");\n\n const request = Effect.fn(\"CloudflareClient.request\")(\n (path: string, operation: string, init?: RequestInit) =>\n Effect.gen(function* () {\n const response = yield* Effect.tryPromise({\n try: () =>\n fetch(`${baseUrl}${path}`, {\n ...init,\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${options.token.expose()}`,\n ...(init?.body === undefined ? {} : { \"Content-Type\": \"application/json\" }),\n ...init?.headers,\n },\n }),\n catch: () =>\n failure(operation, \"Cloudflare request failed\", {\n reason: \"transport\",\n }),\n });\n const body = yield* Effect.tryPromise({\n try: () => response.json() as Promise<unknown>,\n catch: () => {\n const retryAfterMs = retryAfter(response.headers.get(\"retry-after\"));\n return failure(\n operation,\n response.ok\n ? \"Cloudflare returned a non-JSON response\"\n : `Cloudflare request failed with HTTP ${response.status}`,\n {\n reason: response.ok ? \"response\" : reason(response.status),\n ...(retryAfterMs === undefined ? {} : { retryAfterMs }),\n status: response.status,\n },\n );\n },\n });\n return { body, response } as const;\n }),\n );\n\n const listZonesPage = Effect.fn(\"CloudflareClient.listZonesPage\")(\n (accountId: string | undefined, name: DomainName.DomainName | undefined, page: number) =>\n Effect.gen(function* () {\n const query = new URLSearchParams({ page: String(page), per_page: \"50\" });\n query.set(\"type\", \"full,partial,secondary,internal\");\n if (accountId !== undefined) query.set(\"account.id\", accountId);\n if (name !== undefined) query.set(\"name\", name);\n const { body, response } = yield* request(`/zones?${query}`, \"listZones\");\n yield* ensureSuccess(body, \"listZones\", response);\n const envelope = yield* decode(Protocol.ZoneListEnvelope, body, \"listZones\", response);\n return envelope;\n }),\n );\n\n const allZones = Effect.fn(\"CloudflareClient.allZones\")(\n (accountId?: string, name?: DomainName.DomainName) =>\n Effect.gen(function* () {\n const zones: Array<Protocol.Zone> = [];\n let page = 1;\n while (true) {\n const envelope = yield* listZonesPage(accountId, name, page);\n zones.push(...envelope.result);\n const totalPages = envelope.result_info?.total_pages;\n if (\n (totalPages !== undefined && page >= totalPages) ||\n (totalPages === undefined && envelope.result.length < 50)\n ) {\n return zones;\n }\n page += 1;\n }\n }),\n );\n\n const resolveZone = Effect.fn(\"CloudflareClient.resolveZone\")((name: DomainName.DomainName) =>\n Effect.gen(function* () {\n const matches = yield* allZones(options.accountId, name);\n const match = matches[0];\n if (matches.length === 1 && match !== undefined) return match;\n return yield* Effect.fail(\n failure(\n \"resolveZone\",\n matches.length === 0\n ? `Cloudflare zone ${name} was not found in account ${options.accountId}`\n : `Cloudflare returned multiple zones named ${name} in account ${options.accountId}`,\n { reason: matches.length === 0 ? \"not_found\" : \"response\" },\n ),\n );\n }),\n );\n\n const listRecords = Effect.fn(\"CloudflareClient.listRecords\")((zoneName: DomainName.DomainName) =>\n Effect.gen(function* () {\n const zone = yield* resolveZone(zoneName);\n const records: Array<DnsRecord.Observed> = [];\n let page = 1;\n while (true) {\n const query = new URLSearchParams({ page: String(page), per_page: \"100\" });\n const { body, response } = yield* request(\n `/zones/${encodeURIComponent(zone.id)}/dns_records?${query}`,\n \"listRecords\",\n );\n yield* ensureSuccess(body, \"listRecords\", response);\n const envelope = yield* decode(Protocol.RecordListEnvelope, body, \"listRecords\", response);\n records.push(...(yield* Effect.forEach(envelope.result, Records.decode)));\n const totalPages = envelope.result_info?.total_pages;\n if (\n (totalPages !== undefined && page >= totalPages) ||\n (totalPages === undefined && envelope.result.length < 100)\n ) {\n return records;\n }\n page += 1;\n }\n }),\n );\n\n const createRecord = Effect.fn(\"CloudflareClient.createRecord\")(\n (zoneName: DomainName.DomainName, record: DnsRecord.DnsRecord) =>\n Effect.gen(function* () {\n const zone = yield* resolveZone(zoneName);\n const { body, response } = yield* request(\n `/zones/${encodeURIComponent(zone.id)}/dns_records`,\n \"createRecord\",\n { body: JSON.stringify(Records.encode(record)), method: \"POST\" },\n );\n yield* ensureSuccess(body, \"createRecord\", response);\n const envelope = yield* decode(Protocol.RecordEnvelope, body, \"createRecord\", response);\n return { providerRecordId: envelope.result.id };\n }),\n );\n\n const listAccounts = Effect.fn(\"CloudflareClient.listAccounts\")(() =>\n allZones().pipe(\n Effect.map((zones) => {\n const accounts = new Map<string, Protocol.Account>();\n for (const zone of zones) accounts.set(zone.account.id, zone.account);\n return [...accounts.values()].sort((left, right) => left.name.localeCompare(right.name));\n }),\n ),\n );\n\n const listZones = Effect.fn(\"CloudflareClient.listZones\")((input: ListZonesInput = {}) =>\n allZones(input.accountId ?? options.accountId, input.name).pipe(\n Effect.flatMap((zones) => Effect.forEach(zones, projectZone)),\n ),\n );\n\n const validateToken = Effect.fn(\"CloudflareClient.validateToken\")(() =>\n Effect.gen(function* () {\n const path =\n options.tokenKind === \"account\"\n ? `/accounts/${encodeURIComponent(options.accountId)}/tokens/verify`\n : \"/user/tokens/verify\";\n const { body, response } = yield* request(path, \"validateToken\");\n yield* ensureSuccess(body, \"validateToken\", response);\n const envelope = yield* decode(Protocol.TokenEnvelope, body, \"validateToken\", response);\n if (envelope.result.status !== \"active\") {\n return yield* Effect.fail(\n failure(\"validateToken\", `Cloudflare token is ${envelope.result.status}`, {\n reason: \"authentication\",\n status: response.status,\n }),\n );\n }\n yield* allZones(options.accountId);\n const expiresAt =\n envelope.result.expires_on === undefined ? null : new Date(envelope.result.expires_on);\n if (expiresAt !== null && Number.isNaN(expiresAt.valueOf())) {\n return yield* Effect.fail(\n failure(\"validateToken\", \"Cloudflare token expiry is not a valid timestamp\", {\n reason: \"response\",\n status: response.status,\n }),\n );\n }\n return {\n accountId: options.accountId,\n capabilities: options.capabilities,\n expiresAt,\n scopes: [],\n };\n }),\n );\n\n return {\n id: \"cloudflare\",\n createRecord,\n listAccounts,\n listRecords,\n listZones,\n validateToken,\n };\n}\n\nconst projectZone = Effect.fn(\"CloudflareClient.projectZone\")((zone: Protocol.Zone) =>\n Effect.gen(function* () {\n const name = yield* DomainName.decode(zone.name).pipe(\n Effect.mapError((cause) => failure(\"listZones\", cause.message, { reason: \"response\" })),\n );\n const nameservers = yield* Effect.forEach(zone.name_servers, (nameserver) =>\n DomainName.decode(nameserver).pipe(\n Effect.mapError((cause) => failure(\"listZones\", cause.message, { reason: \"response\" })),\n ),\n );\n return {\n accountId: zone.account.id,\n id: zone.id,\n name,\n nameservers,\n status: zone.status ?? null,\n };\n }),\n);\n\nfunction decode<A>(\n schema: S.ConstraintCodec<A, unknown, never, unknown>,\n input: unknown,\n operation: string,\n response: Response,\n): Effect.Effect<A, DnsProvider.Error> {\n return S.decodeUnknownEffect(schema)(input).pipe(\n Effect.mapError((cause) =>\n failure(operation, `Cloudflare response did not match its API contract: ${cause.message}`, {\n reason: \"response\",\n status: response.status,\n }),\n ),\n );\n}\n\nfunction ensureSuccess(\n input: unknown,\n operation: string,\n response: Response,\n): Effect.Effect<void, DnsProvider.Error> {\n return S.decodeUnknownEffect(Protocol.BaseEnvelope)(input).pipe(\n Effect.mapError(() =>\n failure(operation, \"Cloudflare response did not contain a valid API envelope\", {\n reason: \"response\",\n status: response.status,\n }),\n ),\n Effect.flatMap((envelope) => {\n if (response.ok && envelope.success) return Effect.void;\n const detail = envelope.errors[0];\n const retryAfterMs = retryAfter(response.headers.get(\"retry-after\"));\n return Effect.fail(\n failure(\n operation,\n detail?.message ?? `Cloudflare request failed with HTTP ${response.status}`,\n {\n ...(detail === undefined ? {} : { code: detail.code }),\n reason: reason(response.status, detail?.code),\n ...(retryAfterMs === undefined ? {} : { retryAfterMs }),\n status: response.status,\n },\n ),\n );\n }),\n );\n}\n\nfunction reason(status: number, code?: number): DnsProvider.ErrorReason {\n if (status === 401) return \"authentication\";\n if (status === 403) return \"authorization\";\n if (status === 404) return \"not_found\";\n if (status === 409 || code === 81056 || code === 81057 || code === 81058) return \"conflict\";\n if (status === 429) return \"rate_limit\";\n if (status >= 500) return \"response\";\n return \"request\";\n}\n\nfunction retryAfter(value: string | null): number | undefined {\n if (value === null) return undefined;\n const seconds = Number(value);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nfunction failure(\n operation: string,\n message: string,\n fields: Partial<Pick<DnsProvider.Error, \"code\" | \"reason\" | \"retryAfterMs\" | \"status\">> = {},\n): DnsProvider.Error {\n return new DnsProvider.Error({\n ...fields,\n message,\n operation,\n providerId: \"cloudflare\",\n });\n}\n","import type * as oauth from \"oauth4webapi\";\nimport { Clock, Effect } from \"effect\";\n\nimport type * as ProviderAuth from \"../../auth/manifest.ts\";\nimport type * as Secret from \"../../auth/secret.ts\";\nimport * as Client from \"./client.ts\";\n\nconst authorizationServer = {\n authorization_endpoint: \"https://dash.cloudflare.com/oauth2/auth\",\n issuer: \"https://dash.cloudflare.com\",\n revocation_endpoint: \"https://dash.cloudflare.com/oauth2/revoke\",\n token_endpoint: \"https://dash.cloudflare.com/oauth2/token\",\n} as const;\n\n/** Describes Cloudflare's caller-created API-token authorization method. */\nexport function tokenMethod(\n capabilities: ProviderAuth.TokenValidation[\"capabilities\"],\n): Extract<ProviderAuth.Manifest[\"methods\"][number], { readonly _tag: \"token\" }> {\n return {\n _tag: \"token\",\n capabilities: [...capabilities],\n instructionsUrl: \"https://developers.cloudflare.com/fundamentals/api/get-started/create-token/\",\n };\n}\n\nexport interface OAuthMethodOptions {\n /** Scope IDs assigned to the OAuth client by Cloudflare. */\n readonly scopes: ReadonlyArray<string>;\n /** Capabilities represented by those assigned scope IDs. */\n readonly capabilities: ProviderAuth.TokenValidation[\"capabilities\"];\n readonly clientAuth: ProviderAuth.OAuthMethod[\"clientAuth\"];\n}\n\n/** Describes a registered Cloudflare OAuth client and its assigned scope IDs. */\nexport function oauthMethod(options: OAuthMethodOptions): ProviderAuth.OAuthMethod {\n return {\n _tag: \"oauth2\",\n authorizationServer,\n capabilities: [...options.capabilities],\n clientAuth: options.clientAuth,\n scopes: [...options.scopes],\n };\n}\n\n/** Creates the Cloudflare token and OAuth authorization manifest. */\nexport function manifest(options: OAuthMethodOptions): ProviderAuth.Manifest {\n return {\n methods: [tokenMethod(options.capabilities), oauthMethod(options)],\n providerId: \"cloudflare\",\n };\n}\n\nexport interface SubjectResolverOptions extends Omit<Client.Options, \"token\" | \"tokenKind\"> {}\n\n/** Resolves the Cloudflare account selected by a completed OAuth grant. */\nexport function subjectResolver(\n options: SubjectResolverOptions,\n): ProviderAuth.OAuthSubjectResolver {\n return (tokens: oauth.TokenEndpointResponse, accessToken: Secret.Value) =>\n Effect.gen(function* () {\n const client = Client.make({ ...options, token: accessToken, tokenKind: \"user\" });\n yield* client.listZones();\n const now = yield* Clock.currentTimeMillis;\n return {\n accountId: options.accountId,\n expiresAt:\n typeof tokens.expires_in === \"number\" ? new Date(now + tokens.expires_in * 1_000) : null,\n };\n });\n}\n\n/** Creates a host callback that validates a Cloudflare API token. */\nexport function tokenValidator(\n options: Omit<Client.Options, \"token\">,\n): (token: Secret.Value) => ReturnType<Client.Interface[\"validateToken\"]> {\n return (token) => Client.make({ ...options, token }).validateToken();\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { T as decode$2, c as Opaque, h as decode$3, y as Error$1 } from "./dns-record-Dzo4flM5.mjs";
|
|
3
|
+
import { Effect, Schema } from "effect";
|
|
4
|
+
//#region src/auth/secret.ts
|
|
5
|
+
var secret_exports = /* @__PURE__ */ __exportAll({
|
|
6
|
+
Value: () => Value,
|
|
7
|
+
make: () => make$1
|
|
8
|
+
});
|
|
9
|
+
/** A deliberately non-serializable credential value. */
|
|
10
|
+
var Value = class Value {
|
|
11
|
+
#value;
|
|
12
|
+
constructor(value) {
|
|
13
|
+
this.#value = value;
|
|
14
|
+
}
|
|
15
|
+
static from(value) {
|
|
16
|
+
if (value.length === 0) throw new Error("Secret values cannot be empty");
|
|
17
|
+
return new Value(value);
|
|
18
|
+
}
|
|
19
|
+
expose() {
|
|
20
|
+
return this.#value;
|
|
21
|
+
}
|
|
22
|
+
toJSON() {
|
|
23
|
+
return "[REDACTED]";
|
|
24
|
+
}
|
|
25
|
+
toString() {
|
|
26
|
+
return "[REDACTED]";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const make$1 = Value.from;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/providers/vercel/protocol.ts
|
|
32
|
+
const Pagination = Schema.Struct({
|
|
33
|
+
count: Schema.Number,
|
|
34
|
+
next: Schema.NullOr(Schema.Number),
|
|
35
|
+
prev: Schema.NullOr(Schema.Number)
|
|
36
|
+
});
|
|
37
|
+
const ErrorDetail = Schema.Struct({
|
|
38
|
+
code: Schema.String,
|
|
39
|
+
message: Schema.String,
|
|
40
|
+
limit: Schema.optionalKey(Schema.Struct({
|
|
41
|
+
remaining: Schema.Number,
|
|
42
|
+
reset: Schema.optionalKey(Schema.Number),
|
|
43
|
+
resetMs: Schema.optionalKey(Schema.Number),
|
|
44
|
+
total: Schema.Number
|
|
45
|
+
}))
|
|
46
|
+
});
|
|
47
|
+
const ErrorEnvelope = Schema.Struct({ error: ErrorDetail });
|
|
48
|
+
const User = Schema.Struct({
|
|
49
|
+
id: Schema.String,
|
|
50
|
+
name: Schema.NullOr(Schema.String),
|
|
51
|
+
username: Schema.String
|
|
52
|
+
});
|
|
53
|
+
const UserEnvelope = Schema.Struct({ user: User });
|
|
54
|
+
const Team = Schema.Struct({
|
|
55
|
+
id: Schema.String,
|
|
56
|
+
name: Schema.NullOr(Schema.String),
|
|
57
|
+
slug: Schema.String
|
|
58
|
+
});
|
|
59
|
+
const TeamListEnvelope = Schema.Struct({
|
|
60
|
+
pagination: Pagination,
|
|
61
|
+
teams: Schema.Array(Team)
|
|
62
|
+
});
|
|
63
|
+
const Domain = Schema.Struct({
|
|
64
|
+
id: Schema.String,
|
|
65
|
+
intendedNameservers: Schema.Array(Schema.String),
|
|
66
|
+
name: Schema.String,
|
|
67
|
+
nameservers: Schema.Array(Schema.String),
|
|
68
|
+
serviceType: Schema.Literals([
|
|
69
|
+
"zeit.world",
|
|
70
|
+
"external",
|
|
71
|
+
"na"
|
|
72
|
+
]),
|
|
73
|
+
teamId: Schema.NullOr(Schema.String),
|
|
74
|
+
userId: Schema.String,
|
|
75
|
+
verified: Schema.Boolean
|
|
76
|
+
});
|
|
77
|
+
const DomainConfig = Schema.Struct({
|
|
78
|
+
misconfigured: Schema.Boolean,
|
|
79
|
+
serviceType: Schema.Literals([
|
|
80
|
+
"zeit.world",
|
|
81
|
+
"external",
|
|
82
|
+
"na"
|
|
83
|
+
])
|
|
84
|
+
});
|
|
85
|
+
const DomainEnvelope = Schema.Struct({ domain: Domain });
|
|
86
|
+
const DomainListEnvelope = Schema.Struct({
|
|
87
|
+
domains: Schema.Array(Domain),
|
|
88
|
+
pagination: Pagination
|
|
89
|
+
});
|
|
90
|
+
const Record = Schema.Struct({
|
|
91
|
+
id: Schema.String,
|
|
92
|
+
mxPriority: Schema.optionalKey(Schema.Number),
|
|
93
|
+
name: Schema.String,
|
|
94
|
+
priority: Schema.optionalKey(Schema.Number),
|
|
95
|
+
ttl: Schema.optionalKey(Schema.Number),
|
|
96
|
+
type: Schema.String,
|
|
97
|
+
value: Schema.String
|
|
98
|
+
});
|
|
99
|
+
const RecordListEnvelope = Schema.Struct({
|
|
100
|
+
pagination: Schema.optionalKey(Pagination),
|
|
101
|
+
records: Schema.Array(Record)
|
|
102
|
+
});
|
|
103
|
+
const CreateRecordEnvelope = Schema.Struct({
|
|
104
|
+
uid: Schema.String,
|
|
105
|
+
updated: Schema.optionalKey(Schema.Number)
|
|
106
|
+
});
|
|
107
|
+
const IntegrationToken = Schema.Struct({
|
|
108
|
+
access_token: Schema.String,
|
|
109
|
+
installation_id: Schema.optionalKey(Schema.String),
|
|
110
|
+
team_id: Schema.NullOr(Schema.String),
|
|
111
|
+
token_type: Schema.optionalKey(Schema.String),
|
|
112
|
+
user_id: Schema.String
|
|
113
|
+
});
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/providers/vercel/auth.ts
|
|
116
|
+
var auth_exports = /* @__PURE__ */ __exportAll({
|
|
117
|
+
exchangeCode: () => exchangeCode,
|
|
118
|
+
integrationMethod: () => integrationMethod,
|
|
119
|
+
manifest: () => manifest,
|
|
120
|
+
tokenMethod: () => tokenMethod
|
|
121
|
+
});
|
|
122
|
+
/** Describes Vercel's caller-created personal access-token method. */
|
|
123
|
+
function tokenMethod(capabilities) {
|
|
124
|
+
return {
|
|
125
|
+
_tag: "token",
|
|
126
|
+
capabilities: [...capabilities],
|
|
127
|
+
instructionsUrl: "https://vercel.com/account/settings/tokens"
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/** Describes Vercel's provider-specific integration installation flow. */
|
|
131
|
+
function integrationMethod(options) {
|
|
132
|
+
return {
|
|
133
|
+
_tag: "integration",
|
|
134
|
+
capabilities: [...options.capabilities],
|
|
135
|
+
installUrl: `https://vercel.com/integrations/${encodeURIComponent(options.slug)}/new`,
|
|
136
|
+
tokenEndpoint: "https://api.vercel.com/v2/oauth/access_token"
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/** Creates the Vercel token and integration authorization manifest. */
|
|
140
|
+
function manifest(options) {
|
|
141
|
+
return {
|
|
142
|
+
methods: [tokenMethod(options.capabilities), integrationMethod(options)],
|
|
143
|
+
providerId: "vercel"
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** Exchanges Vercel's one-time integration code and preserves its account context. */
|
|
147
|
+
const exchangeCode = Effect.fn("VercelAuth.exchangeCode")((options) => Effect.gen(function* () {
|
|
148
|
+
const fetch = options.fetch ?? globalThis.fetch;
|
|
149
|
+
const baseUrl = (options.baseUrl ?? "https://api.vercel.com").replace(/\/$/, "");
|
|
150
|
+
const body = new URLSearchParams({
|
|
151
|
+
client_id: options.clientId,
|
|
152
|
+
client_secret: options.clientSecret.expose(),
|
|
153
|
+
code: options.code.expose(),
|
|
154
|
+
redirect_uri: options.redirectUri
|
|
155
|
+
});
|
|
156
|
+
const response = yield* Effect.tryPromise({
|
|
157
|
+
try: () => fetch(`${baseUrl}/v2/oauth/access_token`, {
|
|
158
|
+
body,
|
|
159
|
+
headers: {
|
|
160
|
+
Accept: "application/json",
|
|
161
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
162
|
+
},
|
|
163
|
+
method: "POST"
|
|
164
|
+
}),
|
|
165
|
+
catch: () => failure$2("exchangeCode", "Vercel token exchange failed", "transport")
|
|
166
|
+
});
|
|
167
|
+
const input = yield* Effect.tryPromise({
|
|
168
|
+
try: () => response.json(),
|
|
169
|
+
catch: () => failure$2("exchangeCode", response.ok ? "Vercel returned a non-JSON token response" : `Vercel token exchange failed with HTTP ${response.status}`, response.ok ? "response" : response.status === 401 ? "authentication" : "request", response.status)
|
|
170
|
+
});
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
const detail = Schema.decodeUnknownOption(ErrorEnvelope)(input);
|
|
173
|
+
return yield* Effect.fail(failure$2("exchangeCode", detail._tag === "Some" ? detail.value.error.message : `Vercel token exchange failed with HTTP ${response.status}`, response.status === 401 ? "authentication" : "request", response.status, detail._tag === "Some" ? detail.value.error.code : void 0));
|
|
174
|
+
}
|
|
175
|
+
const token = yield* Schema.decodeUnknownEffect(IntegrationToken)(input).pipe(Effect.mapError((cause) => failure$2("exchangeCode", `Vercel token response did not match its API contract: ${cause.message}`, "response", response.status)));
|
|
176
|
+
return {
|
|
177
|
+
accessToken: make$1(token.access_token),
|
|
178
|
+
context: token.team_id === null ? { _tag: "personal" } : {
|
|
179
|
+
_tag: "team",
|
|
180
|
+
teamId: token.team_id
|
|
181
|
+
},
|
|
182
|
+
installationId: token.installation_id ?? null,
|
|
183
|
+
userId: token.user_id
|
|
184
|
+
};
|
|
185
|
+
}));
|
|
186
|
+
function failure$2(operation, message, reason, status, code) {
|
|
187
|
+
return new Error$1({
|
|
188
|
+
...code === void 0 ? {} : { code },
|
|
189
|
+
message,
|
|
190
|
+
operation,
|
|
191
|
+
providerId: "vercel",
|
|
192
|
+
reason,
|
|
193
|
+
...status === void 0 ? {} : { status }
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/providers/vercel/records.ts
|
|
198
|
+
const decode$1 = Effect.fn("VercelRecords.decode")((zone, record) => Effect.gen(function* () {
|
|
199
|
+
const absolute = absoluteNameCandidate(zone, record.name);
|
|
200
|
+
const name = yield* decode$2(absolute).pipe(Effect.match({
|
|
201
|
+
onFailure: () => null,
|
|
202
|
+
onSuccess: (value) => value
|
|
203
|
+
}));
|
|
204
|
+
if (name === null) return yield* decodeOpaque(record, absolute);
|
|
205
|
+
const common = {
|
|
206
|
+
metadata: {
|
|
207
|
+
ownership: "provider",
|
|
208
|
+
provenance: "vercel",
|
|
209
|
+
purpose: "existing DNS record"
|
|
210
|
+
},
|
|
211
|
+
name,
|
|
212
|
+
policy: record.type === "CNAME" ? "exclusive" : "append",
|
|
213
|
+
ttl: record.ttl ?? 60
|
|
214
|
+
};
|
|
215
|
+
const fromRecord = (input) => decode$3(input).pipe(Effect.mapError((cause) => failure$1("decodeRecord", cause.message)));
|
|
216
|
+
switch (record.type) {
|
|
217
|
+
case "A":
|
|
218
|
+
case "AAAA": return yield* fromRecord({
|
|
219
|
+
...common,
|
|
220
|
+
_tag: record.type,
|
|
221
|
+
address: record.value
|
|
222
|
+
});
|
|
223
|
+
case "CNAME":
|
|
224
|
+
case "NS": return yield* fromRecord({
|
|
225
|
+
...common,
|
|
226
|
+
_tag: record.type,
|
|
227
|
+
target: record.value
|
|
228
|
+
});
|
|
229
|
+
case "TXT": return yield* fromRecord({
|
|
230
|
+
...common,
|
|
231
|
+
_tag: "TXT",
|
|
232
|
+
value: record.value
|
|
233
|
+
});
|
|
234
|
+
case "MX": return yield* fromRecord({
|
|
235
|
+
...common,
|
|
236
|
+
_tag: "MX",
|
|
237
|
+
exchange: record.value,
|
|
238
|
+
priority: record.mxPriority ?? record.priority
|
|
239
|
+
});
|
|
240
|
+
case "CAA": {
|
|
241
|
+
const match = record.value.match(/^(\d{1,3})\s+(issue|issuewild|iodef)\s+(?:"((?:\\.|[^"])*)"|(\S.*))$/);
|
|
242
|
+
if (match === null) return yield* Effect.fail(failure$1("decodeRecord", "Vercel CAA record has an invalid value"));
|
|
243
|
+
return yield* fromRecord({
|
|
244
|
+
...common,
|
|
245
|
+
_tag: "CAA",
|
|
246
|
+
flags: Number(match[1]),
|
|
247
|
+
tag: match[2],
|
|
248
|
+
value: unescapeQuoted(match[3] ?? match[4] ?? "")
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
case "SRV": {
|
|
252
|
+
const parts = record.value.trim().split(/\s+/);
|
|
253
|
+
const [priority, weight, port, target] = parts.length === 4 ? parts : [
|
|
254
|
+
String(record.priority ?? ""),
|
|
255
|
+
parts[0],
|
|
256
|
+
parts[1],
|
|
257
|
+
parts[2]
|
|
258
|
+
];
|
|
259
|
+
return yield* fromRecord({
|
|
260
|
+
...common,
|
|
261
|
+
_tag: "SRV",
|
|
262
|
+
port: Number(port),
|
|
263
|
+
priority: Number(priority),
|
|
264
|
+
target,
|
|
265
|
+
weight: Number(weight)
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
default: return yield* decodeOpaque(record, name);
|
|
269
|
+
}
|
|
270
|
+
}));
|
|
271
|
+
const decodeOpaque = Effect.fn("VercelRecords.decodeOpaque")((record, name) => Schema.decodeUnknownEffect(Opaque)({
|
|
272
|
+
_tag: "Opaque",
|
|
273
|
+
name,
|
|
274
|
+
providerRecordId: record.id,
|
|
275
|
+
providerType: record.type
|
|
276
|
+
}).pipe(Effect.mapError((cause) => failure$1("decodeRecord", cause.message))));
|
|
277
|
+
function encode(zone, record) {
|
|
278
|
+
const common = {
|
|
279
|
+
name: relativeName(zone, record.name),
|
|
280
|
+
...record.ttl === null ? {} : { ttl: record.ttl },
|
|
281
|
+
type: record._tag
|
|
282
|
+
};
|
|
283
|
+
switch (record._tag) {
|
|
284
|
+
case "A":
|
|
285
|
+
case "AAAA": return {
|
|
286
|
+
...common,
|
|
287
|
+
value: record.address
|
|
288
|
+
};
|
|
289
|
+
case "CNAME":
|
|
290
|
+
case "NS": return {
|
|
291
|
+
...common,
|
|
292
|
+
value: record.target
|
|
293
|
+
};
|
|
294
|
+
case "TXT": return {
|
|
295
|
+
...common,
|
|
296
|
+
value: record.value
|
|
297
|
+
};
|
|
298
|
+
case "MX": return {
|
|
299
|
+
...common,
|
|
300
|
+
mxPriority: record.priority,
|
|
301
|
+
value: record.exchange
|
|
302
|
+
};
|
|
303
|
+
case "CAA": return {
|
|
304
|
+
...common,
|
|
305
|
+
value: `${record.flags} ${record.tag} "${escapeQuoted(record.value)}"`
|
|
306
|
+
};
|
|
307
|
+
case "SRV": return {
|
|
308
|
+
...common,
|
|
309
|
+
srv: {
|
|
310
|
+
port: record.port,
|
|
311
|
+
priority: record.priority,
|
|
312
|
+
target: record.target,
|
|
313
|
+
weight: record.weight
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function absoluteNameCandidate(zone, name) {
|
|
319
|
+
return name === "" || name === "@" ? zone : name === zone || name.endsWith(`.${zone}`) ? name : `${name}.${zone}`;
|
|
320
|
+
}
|
|
321
|
+
function relativeName(zone, name) {
|
|
322
|
+
if (name === zone) return "";
|
|
323
|
+
const suffix = `.${zone}`;
|
|
324
|
+
if (!name.endsWith(suffix)) throw failure$1("encodeRecord", `DNS record ${name} is outside Vercel zone ${zone}`, { reason: "request" });
|
|
325
|
+
return name.slice(0, -suffix.length);
|
|
326
|
+
}
|
|
327
|
+
function escapeQuoted(value) {
|
|
328
|
+
return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
|
|
329
|
+
}
|
|
330
|
+
function unescapeQuoted(value) {
|
|
331
|
+
return value.replace(/\\([\\"])/g, "$1");
|
|
332
|
+
}
|
|
333
|
+
function failure$1(operation, message, fields = {}) {
|
|
334
|
+
return new Error$1({
|
|
335
|
+
message,
|
|
336
|
+
operation,
|
|
337
|
+
providerId: "vercel",
|
|
338
|
+
reason: fields.reason ?? "response"
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
//#endregion
|
|
342
|
+
//#region src/providers/vercel/client.ts
|
|
343
|
+
var client_exports = /* @__PURE__ */ __exportAll({ make: () => make });
|
|
344
|
+
/** Creates an Effect-native Vercel client without owning the credential lifecycle. */
|
|
345
|
+
function make(options) {
|
|
346
|
+
const fetch = options.fetch ?? globalThis.fetch;
|
|
347
|
+
const baseUrl = (options.baseUrl ?? "https://api.vercel.com").replace(/\/$/, "");
|
|
348
|
+
const withContext = (path, values = {}) => {
|
|
349
|
+
const url = new URL(`${baseUrl}${path}`);
|
|
350
|
+
for (const [key, value] of Object.entries(values)) url.searchParams.set(key, value);
|
|
351
|
+
if (options.context._tag === "team") url.searchParams.set("teamId", options.context.teamId);
|
|
352
|
+
return url;
|
|
353
|
+
};
|
|
354
|
+
const request = Effect.fn("VercelClient.request")((url, operation, init) => Effect.gen(function* () {
|
|
355
|
+
const response = yield* Effect.tryPromise({
|
|
356
|
+
try: () => fetch(url, {
|
|
357
|
+
...init,
|
|
358
|
+
headers: {
|
|
359
|
+
Accept: "application/json",
|
|
360
|
+
Authorization: `Bearer ${options.token.expose()}`,
|
|
361
|
+
...init?.body === void 0 ? {} : { "Content-Type": "application/json" },
|
|
362
|
+
...init?.headers
|
|
363
|
+
}
|
|
364
|
+
}),
|
|
365
|
+
catch: () => failure(operation, "Vercel request failed", { reason: "transport" })
|
|
366
|
+
});
|
|
367
|
+
const body = yield* Effect.tryPromise({
|
|
368
|
+
try: () => response.json(),
|
|
369
|
+
catch: () => failure(operation, response.ok ? "Vercel returned a non-JSON response" : `Vercel request failed with HTTP ${response.status}`, {
|
|
370
|
+
reason: response.ok ? "response" : reason(response.status),
|
|
371
|
+
...rateLimit(response),
|
|
372
|
+
status: response.status
|
|
373
|
+
})
|
|
374
|
+
});
|
|
375
|
+
if (!response.ok) return yield* Effect.fail(apiFailure(body, operation, response));
|
|
376
|
+
return {
|
|
377
|
+
body,
|
|
378
|
+
response
|
|
379
|
+
};
|
|
380
|
+
}));
|
|
381
|
+
const allTeams = Effect.fn("VercelClient.allTeams")(() => Effect.gen(function* () {
|
|
382
|
+
const teams = [];
|
|
383
|
+
let until = null;
|
|
384
|
+
while (true) {
|
|
385
|
+
const url = new URL(`${baseUrl}/v2/teams`);
|
|
386
|
+
url.searchParams.set("limit", "100");
|
|
387
|
+
if (until !== null) url.searchParams.set("until", String(until));
|
|
388
|
+
const { body, response } = yield* request(url, "listAccounts");
|
|
389
|
+
const envelope = yield* decode(TeamListEnvelope, body, "listAccounts", response);
|
|
390
|
+
teams.push(...envelope.teams);
|
|
391
|
+
if (envelope.pagination.next === null) return teams;
|
|
392
|
+
until = envelope.pagination.next;
|
|
393
|
+
}
|
|
394
|
+
}));
|
|
395
|
+
const allDomains = Effect.fn("VercelClient.allDomains")(() => Effect.gen(function* () {
|
|
396
|
+
const domains = [];
|
|
397
|
+
let until = null;
|
|
398
|
+
while (true) {
|
|
399
|
+
const values = { limit: "100" };
|
|
400
|
+
if (until !== null) values.until = String(until);
|
|
401
|
+
const { body, response } = yield* request(withContext("/v5/domains", values), "listZones");
|
|
402
|
+
const envelope = yield* decode(DomainListEnvelope, body, "listZones", response);
|
|
403
|
+
domains.push(...envelope.domains);
|
|
404
|
+
if (envelope.pagination.next === null) return domains;
|
|
405
|
+
until = envelope.pagination.next;
|
|
406
|
+
}
|
|
407
|
+
}));
|
|
408
|
+
const resolveZone = Effect.fn("VercelClient.resolveZone")((name) => Effect.gen(function* () {
|
|
409
|
+
const configResult = yield* request(withContext(`/v6/domains/${encodeURIComponent(name)}/config`), "resolveZone");
|
|
410
|
+
if ((yield* decode(DomainConfig, configResult.body, "resolveZone", configResult.response)).serviceType !== "zeit.world") return yield* Effect.fail(failure("resolveZone", `Vercel zone ${name} is not configured for authoritative DNS in the selected account`, { reason: "not_found" }));
|
|
411
|
+
const domainResult = yield* request(withContext(`/v5/domains/${encodeURIComponent(name)}`), "resolveZone");
|
|
412
|
+
const envelope = yield* decode(DomainEnvelope, domainResult.body, "resolveZone", domainResult.response);
|
|
413
|
+
return yield* decode$2(envelope.domain.name).pipe(Effect.mapError((cause) => failure("resolveZone", `Vercel returned an invalid storage zone: ${cause.message}`)));
|
|
414
|
+
}));
|
|
415
|
+
const listRecords = Effect.fn("VercelClient.listRecords")((zoneName) => Effect.gen(function* () {
|
|
416
|
+
const storageZone = yield* resolveZone(zoneName);
|
|
417
|
+
const records = [];
|
|
418
|
+
let until = null;
|
|
419
|
+
while (true) {
|
|
420
|
+
const values = { limit: "100" };
|
|
421
|
+
if (until !== null) values.until = String(until);
|
|
422
|
+
const { body, response } = yield* request(withContext(`/v5/domains/${encodeURIComponent(storageZone)}/records`, values), "listRecords");
|
|
423
|
+
const envelope = yield* decode(RecordListEnvelope, body, "listRecords", response);
|
|
424
|
+
records.push(...yield* Effect.forEach(envelope.records, (record) => decode$1(storageZone, record)));
|
|
425
|
+
const next = envelope.pagination?.next ?? null;
|
|
426
|
+
if (next === null) return records.filter((record) => record.name === zoneName || record.name.endsWith(`.${zoneName}`));
|
|
427
|
+
until = next;
|
|
428
|
+
}
|
|
429
|
+
}));
|
|
430
|
+
return {
|
|
431
|
+
id: "vercel",
|
|
432
|
+
createRecord: Effect.fn("VercelClient.createRecord")((zoneName, record) => Effect.gen(function* () {
|
|
433
|
+
const storageZone = yield* resolveZone(zoneName);
|
|
434
|
+
const body = yield* Effect.try({
|
|
435
|
+
try: () => encode(storageZone, record),
|
|
436
|
+
catch: (cause) => cause instanceof Error$1 ? cause : failure("createRecord", "Vercel record could not be encoded", { reason: "request" })
|
|
437
|
+
});
|
|
438
|
+
const result = yield* request(withContext(`/v2/domains/${encodeURIComponent(storageZone)}/records`), "createRecord", {
|
|
439
|
+
body: JSON.stringify(body),
|
|
440
|
+
method: "POST"
|
|
441
|
+
});
|
|
442
|
+
return { providerRecordId: (yield* decode(CreateRecordEnvelope, result.body, "createRecord", result.response)).uid };
|
|
443
|
+
})),
|
|
444
|
+
listAccounts: Effect.fn("VercelClient.listAccounts")(() => Effect.gen(function* () {
|
|
445
|
+
const userResult = yield* request(new URL(`${baseUrl}/v2/user`), "listAccounts");
|
|
446
|
+
const { user } = yield* decode(UserEnvelope, userResult.body, "listAccounts", userResult.response);
|
|
447
|
+
const teams = yield* allTeams();
|
|
448
|
+
return [{
|
|
449
|
+
id: user.id,
|
|
450
|
+
name: user.name ?? user.username,
|
|
451
|
+
type: "personal"
|
|
452
|
+
}, ...teams.map((team) => ({
|
|
453
|
+
id: team.id,
|
|
454
|
+
name: team.name ?? team.slug,
|
|
455
|
+
type: "team"
|
|
456
|
+
}))];
|
|
457
|
+
})),
|
|
458
|
+
listRecords,
|
|
459
|
+
listZones: Effect.fn("VercelClient.listZones")((input = {}) => Effect.gen(function* () {
|
|
460
|
+
const domains = (yield* allDomains()).filter((domain) => domain.serviceType === "zeit.world" && (input.name === void 0 || domain.name === input.name));
|
|
461
|
+
return yield* Effect.forEach(domains, projectZone);
|
|
462
|
+
})),
|
|
463
|
+
validateToken: Effect.fn("VercelClient.validateToken")(() => Effect.gen(function* () {
|
|
464
|
+
let accountId;
|
|
465
|
+
if (options.context._tag === "team") {
|
|
466
|
+
yield* allDomains();
|
|
467
|
+
accountId = options.context.teamId;
|
|
468
|
+
} else {
|
|
469
|
+
const result = yield* request(new URL(`${baseUrl}/v2/user`), "validateToken");
|
|
470
|
+
const { user } = yield* decode(UserEnvelope, result.body, "validateToken", result.response);
|
|
471
|
+
accountId = user.id;
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
accountId,
|
|
475
|
+
capabilities: options.capabilities,
|
|
476
|
+
expiresAt: null,
|
|
477
|
+
scopes: []
|
|
478
|
+
};
|
|
479
|
+
}))
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
const projectZone = Effect.fn("VercelClient.projectZone")((domain) => Effect.gen(function* () {
|
|
483
|
+
const name = yield* decode$2(domain.name).pipe(Effect.mapError((cause) => failure("listZones", cause.message, { reason: "response" })));
|
|
484
|
+
const nameservers = yield* Effect.forEach(domain.nameservers, (nameserver) => decode$2(nameserver).pipe(Effect.mapError((cause) => failure("listZones", cause.message, { reason: "response" }))));
|
|
485
|
+
return {
|
|
486
|
+
accountId: domain.teamId ?? domain.userId,
|
|
487
|
+
id: domain.id,
|
|
488
|
+
name,
|
|
489
|
+
nameservers,
|
|
490
|
+
status: domain.verified ? "active" : "pending"
|
|
491
|
+
};
|
|
492
|
+
}));
|
|
493
|
+
function decode(schema, input, operation, response) {
|
|
494
|
+
return Schema.decodeUnknownEffect(schema)(input).pipe(Effect.mapError((cause) => failure(operation, `Vercel response did not match its API contract: ${cause.message}`, {
|
|
495
|
+
reason: "response",
|
|
496
|
+
status: response.status
|
|
497
|
+
})));
|
|
498
|
+
}
|
|
499
|
+
function apiFailure(input, operation, response) {
|
|
500
|
+
const detail = Schema.decodeUnknownOption(ErrorEnvelope)(input);
|
|
501
|
+
return failure(operation, detail._tag === "Some" ? detail.value.error.message : `Vercel request failed with HTTP ${response.status}`, {
|
|
502
|
+
...detail._tag === "Some" ? { code: detail.value.error.code } : {},
|
|
503
|
+
reason: reason(response.status, detail._tag === "Some" ? detail.value.error.code : void 0),
|
|
504
|
+
...rateLimit(response, detail._tag === "Some" ? detail.value.error : void 0),
|
|
505
|
+
status: response.status
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
function reason(status, code) {
|
|
509
|
+
if (status === 401 || code === "invalid_token") return "authentication";
|
|
510
|
+
if (status === 403) return "authorization";
|
|
511
|
+
if (status === 404 || code === "not_found") return "not_found";
|
|
512
|
+
if (status === 409 || code === "conflict") return "conflict";
|
|
513
|
+
if (status === 429 || code === "rate_limited") return "rate_limit";
|
|
514
|
+
if (status >= 500) return "response";
|
|
515
|
+
return "request";
|
|
516
|
+
}
|
|
517
|
+
function rateLimit(response, detail) {
|
|
518
|
+
const resetMs = detail?.limit?.resetMs;
|
|
519
|
+
const header = response.headers.get("x-ratelimit-reset");
|
|
520
|
+
const reset = resetMs ?? (header === null ? void 0 : Number(header) * 1e3);
|
|
521
|
+
return reset === void 0 || !Number.isFinite(reset) ? {} : { retryAfterMs: Math.max(0, reset - Date.now()) };
|
|
522
|
+
}
|
|
523
|
+
function failure(operation, message, fields = {}) {
|
|
524
|
+
return new Error$1({
|
|
525
|
+
...fields,
|
|
526
|
+
message,
|
|
527
|
+
operation,
|
|
528
|
+
providerId: "vercel"
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
//#endregion
|
|
532
|
+
export { integrationMethod as a, Value as c, exchangeCode as i, secret_exports as l, make as n, manifest as o, auth_exports as r, tokenMethod as s, client_exports as t };
|
|
533
|
+
|
|
534
|
+
//# sourceMappingURL=client-DvXfJx_E.mjs.map
|