@dragonmastery/tamer 0.1.2 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +570 -18
- package/dist/CFApiClient-DhbyyV71.mjs +868 -0
- package/dist/CFApiClient-DhbyyV71.mjs.map +1 -0
- package/dist/StateManager-DTqtLLVX.mjs +760 -0
- package/dist/StateManager-DTqtLLVX.mjs.map +1 -0
- package/dist/apply-BOABC3UB.mjs +423 -0
- package/dist/apply-BOABC3UB.mjs.map +1 -0
- package/dist/applyTarget-GWDEOXeY.mjs +152 -0
- package/dist/applyTarget-GWDEOXeY.mjs.map +1 -0
- package/dist/bootstrap-BxwxC_2Z.mjs +33 -0
- package/dist/bootstrap-BxwxC_2Z.mjs.map +1 -0
- package/dist/buildDispatchUploadForm-BoUB93b3.mjs +38 -0
- package/dist/buildDispatchUploadForm-BoUB93b3.mjs.map +1 -0
- package/dist/cloudflareSnapshot-DzPuCRTh.mjs +163 -0
- package/dist/cloudflareSnapshot-DzPuCRTh.mjs.map +1 -0
- package/dist/deploy-C0edCpn9.mjs +119 -0
- package/dist/deploy-C0edCpn9.mjs.map +1 -0
- package/dist/destroy-DzgA4lCA.mjs +215 -0
- package/dist/destroy-DzgA4lCA.mjs.map +1 -0
- package/dist/destroy-tenant-U0t7BeJ0.mjs +103 -0
- package/dist/destroy-tenant-U0t7BeJ0.mjs.map +1 -0
- package/dist/dev-CZbKfdFw.mjs +103 -0
- package/dist/dev-CZbKfdFw.mjs.map +1 -0
- package/dist/dns-records.resolve-C2T0m4NG.mjs +3 -0
- package/dist/dns-records.resolve-DwBR_1WI.mjs +47 -0
- package/dist/dns-records.resolve-DwBR_1WI.mjs.map +1 -0
- package/dist/dns-records.sync-Bpzz9H0s.mjs +75 -0
- package/dist/dns-records.sync-Bpzz9H0s.mjs.map +1 -0
- package/dist/doctor-C_hs7k2D.mjs +34 -0
- package/dist/doctor-C_hs7k2D.mjs.map +1 -0
- package/dist/drift-B5bpkI0i.mjs +323 -0
- package/dist/drift-B5bpkI0i.mjs.map +1 -0
- package/dist/drift-BNa92AK5.mjs +10 -0
- package/dist/events-BIznt8Sj.mjs +68 -0
- package/dist/events-BIznt8Sj.mjs.map +1 -0
- package/dist/fetchStackImports-C-1THPYL.mjs +3826 -0
- package/dist/fetchStackImports-C-1THPYL.mjs.map +1 -0
- package/dist/generator-Ba-vqyBG.mjs +77 -0
- package/dist/generator-Ba-vqyBG.mjs.map +1 -0
- package/dist/import-B0dlwKoQ.mjs +164 -0
- package/dist/import-B0dlwKoQ.mjs.map +1 -0
- package/dist/index.d.mts +5673 -1290
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +18 -1
- package/dist/index.mjs.map +1 -0
- package/dist/loader-DAvCKLTT.mjs +518 -0
- package/dist/loader-DAvCKLTT.mjs.map +1 -0
- package/dist/logpush-job-DsRkOORJ.mjs +1106 -0
- package/dist/logpush-job-DsRkOORJ.mjs.map +1 -0
- package/dist/migrate-BpW6JkIg.mjs +87 -0
- package/dist/migrate-BpW6JkIg.mjs.map +1 -0
- package/dist/normalize-DVSTRZhO.mjs +253 -0
- package/dist/normalize-DVSTRZhO.mjs.map +1 -0
- package/dist/plan-Do5rE-c5.mjs +453 -0
- package/dist/plan-Do5rE-c5.mjs.map +1 -0
- package/dist/planFormat-CJw8Kq2s.mjs +119 -0
- package/dist/planFormat-CJw8Kq2s.mjs.map +1 -0
- package/dist/provision-tenant-Wfck-2Oa.mjs +192 -0
- package/dist/provision-tenant-Wfck-2Oa.mjs.map +1 -0
- package/dist/r2S3EmptyBucket-DD81ZWQ7.mjs +92 -0
- package/dist/r2S3EmptyBucket-DD81ZWQ7.mjs.map +1 -0
- package/dist/stackOutputs-CQQHtdPA.mjs +69 -0
- package/dist/stackOutputs-CQQHtdPA.mjs.map +1 -0
- package/dist/status-D5GLpWyn.mjs +198 -0
- package/dist/status-D5GLpWyn.mjs.map +1 -0
- package/dist/sync-B_pyPi7Z.mjs +90 -0
- package/dist/sync-B_pyPi7Z.mjs.map +1 -0
- package/dist/tamer.d.mts +1 -0
- package/dist/tamer.mjs +4553 -0
- package/dist/tamer.mjs.map +1 -0
- package/dist/tamerArtifactsR2-Ccgplu2Q.mjs +52 -0
- package/dist/tamerArtifactsR2-Ccgplu2Q.mjs.map +1 -0
- package/dist/types-JrdlG7Dy.mjs +44 -0
- package/dist/types-JrdlG7Dy.mjs.map +1 -0
- package/dist/verifyPlanFile-ah_4tvTu.mjs +33 -0
- package/dist/verifyPlanFile-ah_4tvTu.mjs.map +1 -0
- package/dist/wfp-delete-BhuUrBUA.mjs +36 -0
- package/dist/wfp-delete-BhuUrBUA.mjs.map +1 -0
- package/dist/wfp-put-DL0mJNNz.mjs +52 -0
- package/dist/wfp-put-DL0mJNNz.mjs.map +1 -0
- package/dist/worker-route-CMbtozNa.mjs +263 -0
- package/dist/worker-route-CMbtozNa.mjs.map +1 -0
- package/dist/workers-C-oeZhdD.mjs +87 -0
- package/dist/workers-C-oeZhdD.mjs.map +1 -0
- package/dist/wranglerSpawn-DmEz0ldT.mjs +24 -0
- package/dist/wranglerSpawn-DmEz0ldT.mjs.map +1 -0
- package/dist/zoneResolver-VoxLHM4N.mjs +32 -0
- package/dist/zoneResolver-VoxLHM4N.mjs.map +1 -0
- package/package.json +38 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CFApiClient-DhbyyV71.mjs","names":["parts: string[]","parsed: unknown","all: Array<{ uuid: string; name: string; created_at: string }>","all: Array<{ name: string; creation_date: string }>","cursor: string | undefined","all: Array<{ id: string; title: string }>","all: Array<{\n queue_id: string;\n queue_name: string;\n created_on?: string;\n modified_on?: string;\n }>","all: Row[]","all: Array<{ id: string; name: string; endpoint?: string }>","all: Array<{ id: string; name: string; type: string }>","all: Item[]","all: Array<{ id: string; name: string; scopes?: string[] }>"],"sources":["../src/core/cloudflareEnv.ts","../src/core/api/pipelinesV1PathId.ts","../src/core/api/CFApiClient.ts"],"sourcesContent":["/**\n * Wrangler-aligned Cloudflare credentials (same names as `wrangler` CLI).\n * @see https://developers.cloudflare.com/workers/wrangler/system-environment-variables/\n */\nexport function cloudflareAccountIdFromEnv(): string | undefined {\n return process.env.CLOUDFLARE_ACCOUNT_ID;\n}\n\nexport function cloudflareApiTokenFromEnv(): string {\n return process.env.CLOUDFLARE_API_TOKEN ?? \"\";\n}\n","/**\n * Pipelines v1 path segments (streams, sinks, SQL pipelines) expect **stream_id**\n * / **sink_id** as **exactly 32 lower-case hex characters** (no hyphens). The API\n * returns 400 if the URL contains a hyphenated UUID (36 chars) — see\n * `stream_id: String must contain exactly 32 character(s)`.\n *\n * State and list APIs may return ids with or without hyphens; we normalize to 32-hex\n * for GET/DELETE paths.\n */\nexport function pipelinesV1IdForPath(id: string): string {\n const c = id.trim().toLowerCase().replace(/-/g, \"\");\n if (!/^[0-9a-f]{32}$/.test(c)) {\n return id.trim();\n }\n return c;\n}\n","import type {\n CFD1ListResponse,\n CFR2ListResponse,\n CFKVListResponse,\n} from \"./CFApiClient.types.js\";\nimport { cloudflareApiTokenFromEnv } from \"../cloudflareEnv.js\";\nimport { pipelinesV1IdForPath } from \"./pipelinesV1PathId.js\";\n\nconst CF_API_BASE = \"https://api.cloudflare.com/client/v4\";\n\n/**\n * Renders the Cloudflare API `errors` / `messages` array into a single string\n * (message, error code, documentation URL, JSON pointer) for operator-friendly logs.\n */\nfunction describeCloudflareErrorItem(e: unknown): string {\n if (e == null) return \"null\";\n if (typeof e === \"string\") return e;\n if (typeof e !== \"object\") return String(e);\n const o = e as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof o.message === \"string\" && o.message) parts.push(o.message);\n if (typeof o.code === \"number\") parts.push(`[code: ${o.code}]`);\n if (typeof o.code === \"string\" && o.code) parts.push(`[code: ${o.code}]`);\n if (typeof o.documentation_url === \"string\" && o.documentation_url) {\n parts.push(o.documentation_url);\n }\n const src = o.source;\n if (src && typeof src === \"object\" && \"pointer\" in src) {\n const p = (src as { pointer?: string }).pointer;\n if (typeof p === \"string\" && p) parts.push(`(field: ${p})`);\n }\n if (Array.isArray(o.error_chain) && o.error_chain.length > 0) {\n parts.push(\n `[chain: ${o.error_chain.map(describeCloudflareErrorItem).join(\" → \")}]`,\n );\n }\n if (parts.length) return parts.join(\" \");\n try {\n return JSON.stringify(e);\n } catch {\n return String(e);\n }\n}\n\nfunction formatCloudflareErrorBody(\n errors: unknown,\n messages: unknown,\n fallback: string,\n): string {\n const errList = formatCloudflareList(errors) || fallback;\n const msgList = formatCloudflareList(messages);\n if (msgList) return `${errList} — messages: ${msgList}`;\n return errList;\n}\n\nfunction formatCloudflareList(v: unknown): string {\n if (!Array.isArray(v) || v.length === 0) return \"\";\n return v.map(describeCloudflareErrorItem).join(\" | \");\n}\n\nexport class CFApiClient {\n private accountId: string;\n private apiToken: string;\n\n constructor(accountId: string, apiToken?: string) {\n this.accountId = accountId;\n this.apiToken = apiToken ?? cloudflareApiTokenFromEnv();\n if (!this.apiToken) {\n throw new Error(\"CLOUDFLARE_API_TOKEN is required\");\n }\n }\n\n getAccountId(): string {\n return this.accountId;\n }\n\n /** Lightweight account read — validates token + account scope. */\n async accountRead(): Promise<{ id: string; name: string }> {\n const data = await this.request<{ result: { id: string; name: string } }>(\n `/accounts/${this.accountId}`,\n );\n return data.result;\n }\n\n /**\n * Get a single Worker script by name (account-scoped, **not** dispatch).\n * Returns `undefined` when Cloudflare responds 404 so callers can treat it\n * as \"not deployed\" without try/catch on the message.\n *\n * @see https://developers.cloudflare.com/api/resources/workers/subresources/scripts/methods/get/\n */\n async workersScriptGet(\n scriptName: string,\n ): Promise<{ id: string; created_on?: string; modified_on?: string } | undefined> {\n const url = `${CF_API_BASE}/accounts/${this.accountId}/workers/scripts/${encodeURIComponent(scriptName)}`;\n const res = await fetch(url, {\n headers: {\n Authorization: `Bearer ${this.apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n });\n if (res.status === 404) return undefined;\n const data = (await res.json()) as {\n success?: boolean;\n errors?: Array<{ message: string }>;\n result?: { id: string; created_on?: string; modified_on?: string };\n };\n if (!res.ok) {\n const msg =\n data?.errors?.map((e) => e.message).join(\"; \") ?? res.statusText;\n throw new Error(`CF API error (workers script get): ${msg}`);\n }\n return data.result;\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {}\n ): Promise<T> {\n const method = (options.method ?? \"GET\").toUpperCase();\n const url = `${CF_API_BASE}${path}`;\n const res = await fetch(url, {\n ...options,\n headers: {\n Authorization: `Bearer ${this.apiToken}`,\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n });\n\n let parsed: unknown;\n try {\n parsed = await res.json();\n } catch {\n throw new Error(\n `CF API error: ${method} \\`${path}\\` → HTTP ${res.status} (response was not valid JSON)`,\n );\n }\n\n /** Some endpoints (e.g. R2 catalog disable) respond `200` with literal JSON `null`. */\n const data = (\n parsed !== null && typeof parsed === \"object\"\n ? parsed\n : {}\n ) as T & {\n success?: boolean;\n errors?: unknown[];\n messages?: unknown[];\n };\n\n const httpError = !res.ok;\n const bodySuccessFalse = data.success === false;\n if (httpError || bodySuccessFalse) {\n const detail = formatCloudflareErrorBody(\n data.errors,\n data.messages,\n res.statusText || \"no error array in body\",\n );\n throw new Error(\n `CF API error: ${method} \\`${path}\\` → HTTP ${res.status}. ${detail}`,\n );\n }\n return data;\n }\n\n async d1ListAll(): Promise<Array<{ uuid: string; name: string; created_at: string }>> {\n const all: Array<{ uuid: string; name: string; created_at: string }> = [];\n let page = 1;\n const perPage = 100;\n\n while (true) {\n const data = await this.request<CFD1ListResponse>(\n `/accounts/${this.accountId}/d1/database?per_page=${perPage}&page=${page}`\n );\n all.push(...data.result);\n const info = data.result_info;\n if (!info || info.count < perPage) break;\n page++;\n }\n return all;\n }\n\n async r2ListAll(): Promise<Array<{ name: string; creation_date: string }>> {\n const all: Array<{ name: string; creation_date: string }> = [];\n const perPage = 100;\n let cursor: string | undefined;\n\n while (true) {\n const qs = new URLSearchParams({ per_page: String(perPage) });\n if (cursor) qs.set(\"cursor\", cursor);\n const data = await this.request<CFR2ListResponse>(\n `/accounts/${this.accountId}/r2/buckets?${qs.toString()}`\n );\n const buckets = data.result?.buckets ?? [];\n all.push(\n ...buckets.map((b) => ({\n name: b.name ?? \"\",\n creation_date: b.creation_date ?? \"\",\n })),\n );\n const next = data.result_info?.cursor;\n if (!next) break;\n cursor = next;\n }\n return all;\n }\n\n async kvListAll(): Promise<Array<{ id: string; title: string }>> {\n const all: Array<{ id: string; title: string }> = [];\n let page = 1;\n const perPage = 100;\n\n while (true) {\n const data = await this.request<CFKVListResponse>(\n `/accounts/${this.accountId}/storage/kv/namespaces?per_page=${perPage}&page=${page}`\n );\n all.push(...data.result);\n const info = data.result_info;\n if (!info || info.count < perPage) break;\n page++;\n }\n return all;\n }\n\n async d1Create(name: string): Promise<{ uuid: string }> {\n const data = await this.request<{ result: { uuid: string } }>(\n `/accounts/${this.accountId}/d1/database`,\n {\n method: \"POST\",\n body: JSON.stringify({ name }),\n }\n );\n return data.result;\n }\n\n async r2Create(name: string, location: string = \"auto\"): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/r2/buckets`,\n {\n method: \"POST\",\n body: JSON.stringify({ name, location }),\n }\n );\n }\n\n async kvCreate(title: string): Promise<{ id: string }> {\n const data = await this.request<{ result: { id: string } }>(\n `/accounts/${this.accountId}/storage/kv/namespaces`,\n {\n method: \"POST\",\n body: JSON.stringify({ title }),\n }\n );\n return data.result;\n }\n\n async d1Delete(databaseId: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/d1/database/${databaseId}`,\n { method: \"DELETE\" }\n );\n }\n\n /**\n * Run SQL against a D1 database (HTTP API).\n * @see https://developers.cloudflare.com/d1/build-with-d1/rest-api/\n */\n async d1Query(\n databaseId: string,\n sql: string,\n params: unknown[] = [],\n ): Promise<{ rows: Array<Record<string, unknown>> }> {\n const data = await this.request<{\n result?: Array<{\n results?: Array<Record<string, unknown>>;\n success?: boolean;\n }>;\n }>(`/accounts/${this.accountId}/d1/database/${databaseId}/query`, {\n method: \"POST\",\n body: JSON.stringify({ sql, params }),\n });\n const rows = data.result?.[0]?.results ?? [];\n return { rows };\n }\n\n async r2Delete(bucketName: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/r2/buckets/${bucketName}`,\n { method: \"DELETE\" }\n );\n }\n\n /**\n * Download an object from R2 (HTTP API).\n * @see https://developers.cloudflare.com/api/resources/r2/subresources/buckets/subresources/objects/methods/get/\n */\n async r2GetObject(bucketName: string, objectKey: string): Promise<ArrayBuffer> {\n const pathEncoded = objectKey\n .split(\"/\")\n .map((seg) => encodeURIComponent(seg))\n .join(\"/\");\n const url = `${CF_API_BASE}/accounts/${this.accountId}/r2/buckets/${encodeURIComponent(bucketName)}/objects/${pathEncoded}`;\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${this.apiToken}` },\n });\n if (!res.ok) {\n let errMsg = res.statusText;\n try {\n const data = (await res.json()) as {\n errors?: Array<{ message: string }>;\n };\n errMsg = data?.errors?.map((e) => e.message).join(\"; \") ?? errMsg;\n } catch {\n /* binary or empty */\n }\n throw new Error(`CF API error (R2 get): ${errMsg}`);\n }\n return res.arrayBuffer();\n }\n\n async kvDelete(namespaceId: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/storage/kv/namespaces/${namespaceId}`,\n { method: \"DELETE\" }\n );\n }\n\n // ── Queues ───────────────────────────────────────────────────────────────\n\n /**\n * List every Cloudflare Queue in the account.\n * @see https://developers.cloudflare.com/api/resources/queues/methods/list/\n */\n async queuesListAll(): Promise<\n Array<{ queue_id: string; queue_name: string; created_on?: string; modified_on?: string }>\n > {\n const all: Array<{\n queue_id: string;\n queue_name: string;\n created_on?: string;\n modified_on?: string;\n }> = [];\n let page = 1;\n const perPage = 100;\n while (true) {\n const data = await this.request<{\n result?: Array<{\n queue_id: string;\n queue_name: string;\n created_on?: string;\n modified_on?: string;\n }>;\n result_info?: { count?: number; total_count?: number };\n }>(\n `/accounts/${this.accountId}/queues?per_page=${perPage}&page=${page}`,\n );\n const batch = data.result ?? [];\n all.push(...batch);\n if (batch.length < perPage) break;\n page++;\n }\n return all;\n }\n\n /**\n * Create a new Cloudflare Queue.\n * @see https://developers.cloudflare.com/api/resources/queues/methods/create/\n */\n async queueCreate(\n queueName: string,\n ): Promise<{ queue_id: string; queue_name: string }> {\n const data = await this.request<{\n result: { queue_id: string; queue_name: string };\n }>(`/accounts/${this.accountId}/queues`, {\n method: \"POST\",\n body: JSON.stringify({ queue_name: queueName }),\n });\n return data.result;\n }\n\n /**\n * Delete a Cloudflare Queue by id.\n * @see https://developers.cloudflare.com/api/resources/queues/methods/delete/\n */\n async queueDelete(queueId: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/queues/${encodeURIComponent(queueId)}`,\n { method: \"DELETE\" },\n );\n }\n\n // ── Hyperdrive ───────────────────────────────────────────────────────────\n\n /**\n * List every Hyperdrive config in the account.\n * @see https://developers.cloudflare.com/api/resources/hyperdrive/subresources/configs/methods/list/\n */\n async hyperdriveListAll(): Promise<\n Array<{\n id: string;\n name: string;\n origin?: { scheme?: string; host?: string; database?: string; port?: number; user?: string };\n caching?: { disabled?: boolean; max_age?: number; stale_while_revalidate?: number };\n }>\n > {\n const data = await this.request<{\n result?: Array<{\n id: string;\n name: string;\n origin?: {\n scheme?: string;\n host?: string;\n database?: string;\n port?: number;\n user?: string;\n };\n caching?: { disabled?: boolean; max_age?: number; stale_while_revalidate?: number };\n }>;\n }>(`/accounts/${this.accountId}/hyperdrive/configs`);\n return data.result ?? [];\n }\n\n /**\n * Create a Hyperdrive config.\n * @see https://developers.cloudflare.com/api/resources/hyperdrive/subresources/configs/methods/create/\n */\n async hyperdriveCreate(body: {\n name: string;\n origin: {\n scheme: string;\n host: string;\n port?: number;\n database: string;\n user: string;\n password: string;\n access_client_id?: string;\n access_client_secret?: string;\n };\n caching?: { disabled?: boolean; max_age?: number; stale_while_revalidate?: number };\n mtls?: { ca_certificate_id?: string; mtls_certificate_id?: string };\n }): Promise<{ id: string; name: string }> {\n const data = await this.request<{ result: { id: string; name: string } }>(\n `/accounts/${this.accountId}/hyperdrive/configs`,\n { method: \"POST\", body: JSON.stringify(body) },\n );\n return data.result;\n }\n\n /**\n * Patch an existing Hyperdrive configuration in place. Cloudflare accepts\n * partial updates here (true `PATCH`), so callers may send only the\n * fields that drifted. Origin patches must include the full origin\n * payload (the password is write-only and never returned, so Tamer\n * reads it back from the resource config every apply).\n * @see https://developers.cloudflare.com/api/resources/hyperdrive/subresources/configs/methods/edit/\n */\n async hyperdrivePatch(\n configId: string,\n body: {\n name?: string;\n origin?: {\n scheme?: string;\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n password?: string;\n access_client_id?: string;\n access_client_secret?: string;\n };\n caching?: { disabled?: boolean; max_age?: number; stale_while_revalidate?: number };\n mtls?: { ca_certificate_id?: string; mtls_certificate_id?: string };\n origin_connection_limit?: number;\n },\n ): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/hyperdrive/configs/${encodeURIComponent(configId)}`,\n { method: \"PATCH\", body: JSON.stringify(body) },\n );\n }\n\n /**\n * Delete a Hyperdrive config by id.\n * @see https://developers.cloudflare.com/api/resources/hyperdrive/subresources/configs/methods/delete/\n */\n async hyperdriveDelete(configId: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/hyperdrive/configs/${encodeURIComponent(configId)}`,\n { method: \"DELETE\" },\n );\n }\n\n // ── Vectorize (v2) ───────────────────────────────────────────────────────\n\n /**\n * List every Vectorize index in the account (v2 storage subsystem).\n * @see https://developers.cloudflare.com/api/resources/vectorize/subresources/indexes/methods/list/\n */\n async vectorizeListAll(): Promise<\n Array<{\n id?: string;\n name: string;\n description?: string;\n config?: { dimensions: number; metric: string };\n created_on?: string;\n modified_on?: string;\n }>\n > {\n const data = await this.request<{\n result?: Array<{\n id?: string;\n name: string;\n description?: string;\n config?: { dimensions: number; metric: string };\n created_on?: string;\n modified_on?: string;\n }>;\n }>(`/accounts/${this.accountId}/vectorize/v2/indexes`);\n return data.result ?? [];\n }\n\n /**\n * Create a Vectorize v2 index. `dimensions` and `metric` are immutable.\n * @see https://developers.cloudflare.com/api/resources/vectorize/subresources/indexes/methods/create/\n */\n async vectorizeCreate(body: {\n name: string;\n description?: string;\n config: { dimensions: number; metric: string };\n }): Promise<{ id?: string; name: string }> {\n const data = await this.request<{ result: { id?: string; name: string } }>(\n `/accounts/${this.accountId}/vectorize/v2/indexes`,\n { method: \"POST\", body: JSON.stringify(body) },\n );\n return data.result;\n }\n\n /**\n * Delete a Vectorize index by name.\n * @see https://developers.cloudflare.com/api/resources/vectorize/subresources/indexes/methods/delete/\n */\n async vectorizeDelete(indexName: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/vectorize/v2/indexes/${encodeURIComponent(indexName)}`,\n { method: \"DELETE\" },\n );\n }\n\n // ── AI Gateway ───────────────────────────────────────────────────────────\n\n /**\n * List every AI Gateway in the account.\n * @see https://developers.cloudflare.com/api/resources/ai_gateway/methods/list/\n */\n async aiGatewayListAll(): Promise<\n Array<{\n id: string;\n cache_ttl?: number;\n cache_invalidate_on_update?: boolean;\n collect_logs?: boolean;\n rate_limiting_interval?: number;\n rate_limiting_limit?: number;\n rate_limiting_technique?: \"fixed\" | \"sliding\";\n authentication?: boolean;\n created_at?: string;\n modified_at?: string;\n }>\n > {\n const data = await this.request<{\n result?: Array<{\n id: string;\n cache_ttl?: number;\n cache_invalidate_on_update?: boolean;\n collect_logs?: boolean;\n rate_limiting_interval?: number;\n rate_limiting_limit?: number;\n rate_limiting_technique?: \"fixed\" | \"sliding\";\n authentication?: boolean;\n created_at?: string;\n modified_at?: string;\n }>;\n }>(`/accounts/${this.accountId}/ai-gateway/gateways?per_page=100`);\n return data.result ?? [];\n }\n\n /**\n * Create an AI Gateway. The gateway `id` is user-supplied and acts as both\n * primary key and routing slug under `https://gateway.ai.cloudflare.com/`.\n * @see https://developers.cloudflare.com/api/resources/ai_gateway/methods/create/\n */\n async aiGatewayCreate(body: {\n id: string;\n cache_ttl: number;\n cache_invalidate_on_update: boolean;\n collect_logs: boolean;\n rate_limiting_interval: number;\n rate_limiting_limit: number;\n rate_limiting_technique?: \"fixed\" | \"sliding\";\n authentication?: boolean;\n }): Promise<{ id: string }> {\n const data = await this.request<{ result: { id: string } }>(\n `/accounts/${this.accountId}/ai-gateway/gateways`,\n { method: \"POST\", body: JSON.stringify(body) },\n );\n return data.result;\n }\n\n /**\n * Update an existing AI Gateway in place. The Cloudflare API uses `PUT`\n * (not PATCH) and is full-replace on the listed fields, so callers must\n * supply the complete desired state — Tamer's `apply` reads the recorded\n * state row and merges declared overrides before calling this.\n * @see https://developers.cloudflare.com/api/resources/ai_gateway/methods/update/\n */\n async aiGatewayUpdate(\n gatewayId: string,\n body: {\n cache_ttl: number;\n cache_invalidate_on_update: boolean;\n collect_logs: boolean;\n rate_limiting_interval: number;\n rate_limiting_limit: number;\n rate_limiting_technique?: \"fixed\" | \"sliding\";\n authentication?: boolean;\n },\n ): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/ai-gateway/gateways/${encodeURIComponent(gatewayId)}`,\n { method: \"PUT\", body: JSON.stringify(body) },\n );\n }\n\n /**\n * Delete an AI Gateway by id.\n * @see https://developers.cloudflare.com/api/resources/ai_gateway/methods/delete/\n */\n async aiGatewayDelete(gatewayId: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/ai-gateway/gateways/${encodeURIComponent(gatewayId)}`,\n { method: \"DELETE\" },\n );\n }\n\n // ── Pipelines (V1, SQL-based) ────────────────────────────────────────────\n\n /**\n * List every Pipeline in the account (paginated, 100/page). Uses the V1\n * SQL pipelines endpoint; the deprecated `/accounts/{id}/pipelines`\n * (HTTP/binding sources) is not used.\n * @see https://developers.cloudflare.com/api/resources/pipelines/methods/list_v1/\n */\n async pipelineListAll(): Promise<\n Array<{\n id: string;\n name: string;\n sql: string;\n status?: string;\n created_at?: string;\n modified_at?: string;\n }>\n > {\n type Row = {\n id: string;\n name: string;\n sql: string;\n status?: string;\n created_at?: string;\n modified_at?: string;\n };\n const all: Row[] = [];\n let page = 1;\n const perPage = 100;\n while (true) {\n const data = await this.request<{\n result?: Row[];\n result_info?: { count?: number; total_count?: number; page?: number };\n }>(\n `/accounts/${this.accountId}/pipelines/v1/pipelines?per_page=${perPage}&page=${page}`,\n );\n const batch = data.result ?? [];\n all.push(...batch);\n if (batch.length < perPage) break;\n page += 1;\n }\n return all;\n }\n\n /**\n * Create a Pipeline. Server assigns the `id`; `name` is user-supplied\n * and uniquely identifies the pipeline within the account.\n * @see https://developers.cloudflare.com/api/resources/pipelines/methods/create_v1/\n */\n async pipelineCreate(body: {\n name: string;\n sql: string;\n }): Promise<{\n id: string;\n name: string;\n sql: string;\n status?: string;\n created_at?: string;\n modified_at?: string;\n }> {\n const data = await this.request<{\n result: {\n id: string;\n name: string;\n sql: string;\n status?: string;\n created_at?: string;\n modified_at?: string;\n };\n }>(`/accounts/${this.accountId}/pipelines/v1/pipelines`, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n return data.result;\n }\n\n /**\n * Delete a Pipeline by server-assigned id.\n * @see https://developers.cloudflare.com/api/resources/pipelines/methods/delete_v1/\n */\n async pipelineDelete(pipelineId: string): Promise<void> {\n const pathId = pipelinesV1IdForPath(pipelineId);\n await this.request(\n `/accounts/${this.accountId}/pipelines/v1/pipelines/${encodeURIComponent(pathId)}`,\n { method: \"DELETE\" },\n );\n }\n\n // ── Pipelines v1 streams / sinks (Iceberg, Logpush) ─────────────────────\n\n /**\n * @see https://developers.cloudflare.com/api/resources/pipelines/subresources/streams/methods/list/\n */\n async pipelinesV1StreamListAll(): Promise<\n Array<{ id: string; name: string; endpoint?: string }>\n > {\n const all: Array<{ id: string; name: string; endpoint?: string }> = [];\n let page = 1;\n const perPage = 100;\n while (true) {\n const data = await this.request<{\n result?: Array<{ id: string; name: string; endpoint?: string }>;\n result_info?: { count?: number; total_count?: number; page?: number };\n }>(\n `/accounts/${this.accountId}/pipelines/v1/streams?per_page=${perPage}&page=${page}`,\n );\n const batch = data.result ?? [];\n all.push(...batch);\n if (batch.length < perPage) break;\n page += 1;\n }\n return all;\n }\n\n /**\n * @see https://developers.cloudflare.com/api/resources/pipelines/subresources/streams/methods/create/\n */\n async pipelinesV1StreamCreate(\n body: Record<string, unknown>,\n ): Promise<{\n id: string;\n name: string;\n endpoint?: string;\n }> {\n const data = await this.request<{\n result: { id: string; name: string; endpoint?: string };\n }>(`/accounts/${this.accountId}/pipelines/v1/streams`, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n return data.result;\n }\n\n /**\n * Path id: 32 lower-case hex chars (no hyphens). The dashboard issues a\n * plain `DELETE` with no query string; pass `{ force: true }` only if the API\n * docs require it for your case.\n *\n * @see https://developers.cloudflare.com/api/resources/pipelines/subresources/streams/methods/delete/\n */\n async pipelinesV1StreamDelete(\n streamId: string,\n query?: { force?: boolean },\n ): Promise<void> {\n const pathId = pipelinesV1IdForPath(streamId);\n const qs = new URLSearchParams();\n if (query?.force) qs.set(\"force\", \"true\");\n const tail = qs.toString() ? `?${qs.toString()}` : \"\";\n await this.request(\n `/accounts/${this.accountId}/pipelines/v1/streams/${encodeURIComponent(pathId)}${tail}`,\n { method: \"DELETE\" },\n );\n }\n\n /**\n * @see https://developers.cloudflare.com/api/resources/pipelines/subresources/sinks/methods/list/\n */\n async pipelinesV1SinkListAll(): Promise<\n Array<{ id: string; name: string; type: string }>\n > {\n const all: Array<{ id: string; name: string; type: string }> = [];\n let page = 1;\n const perPage = 100;\n while (true) {\n const data = await this.request<{\n result?: Array<{ id: string; name: string; type: string }>;\n result_info?: { count?: number };\n }>(\n `/accounts/${this.accountId}/pipelines/v1/sinks?per_page=${perPage}&page=${page}`,\n );\n const batch = data.result ?? [];\n all.push(...batch);\n if (batch.length < perPage) break;\n page += 1;\n }\n return all;\n }\n\n /**\n * R2 Data Catalog sink `config` includes `table_name` and `namespace` as\n * Cloudflare has registered them (may differ from the name sent at create).\n *\n * @see https://developers.cloudflare.com/api/resources/pipelines/subresources/sinks/methods/get/\n */\n async pipelinesV1SinkGet(sinkId: string): Promise<{\n id: string;\n name: string;\n type: string;\n config?: Record<string, unknown>;\n }> {\n const pathId = pipelinesV1IdForPath(sinkId);\n const data = await this.request<{\n result: {\n id: string;\n name: string;\n type: string;\n config?: Record<string, unknown>;\n };\n }>(\n `/accounts/${this.accountId}/pipelines/v1/sinks/${encodeURIComponent(pathId)}`,\n );\n return data.result;\n }\n\n /**\n * @see https://developers.cloudflare.com/api/resources/pipelines/subresources/sinks/methods/create/\n */\n async pipelinesV1SinkCreate(\n body: Record<string, unknown>,\n ): Promise<{ id: string; name: string; type: string }> {\n const data = await this.request<{\n result: { id: string; name: string; type: string };\n }>(`/accounts/${this.accountId}/pipelines/v1/sinks`, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n return data.result;\n }\n\n /**\n * Path id: 32 lower-case hex chars (no hyphens). Optional `{ force: true }`\n * matches `?force=true` when the product requires it (e.g. some sink types).\n *\n * @see https://developers.cloudflare.com/api/resources/pipelines/subresources/sinks/methods/delete/\n */\n async pipelinesV1SinkDelete(\n sinkId: string,\n query?: { force?: boolean },\n ): Promise<void> {\n const pathId = pipelinesV1IdForPath(sinkId);\n const qs = new URLSearchParams();\n if (query?.force) qs.set(\"force\", \"true\");\n const tail = qs.toString() ? `?${qs.toString()}` : \"\";\n await this.request(\n `/accounts/${this.accountId}/pipelines/v1/sinks/${encodeURIComponent(pathId)}${tail}`,\n { method: \"DELETE\" },\n );\n }\n\n // ── R2 Data Catalog (Iceberg) ────────────────────────────────────────────\n\n /**\n * @see https://developers.cloudflare.com/api/resources/r2_data_catalog/methods/list/\n */\n async r2DataCatalogList(): Promise<{\n warehouses: Array<{\n id: string;\n bucket: string;\n name: string;\n status?: string;\n }>;\n }> {\n const data = await this.request<{\n result?: {\n warehouses: Array<{\n id: string;\n bucket: string;\n name: string;\n status?: string;\n }>;\n };\n }>(`/accounts/${this.accountId}/r2-catalog`);\n return { warehouses: data.result?.warehouses ?? [] };\n }\n\n /**\n * @see https://developers.cloudflare.com/api/resources/r2_data_catalog/methods/enable/\n */\n async r2DataCatalogEnable(bucketName: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/r2-catalog/${encodeURIComponent(bucketName)}/enable`,\n { method: \"POST\" },\n );\n }\n\n /**\n * @see https://developers.cloudflare.com/api/resources/r2_data_catalog/methods/update/\n * (Store catalog credentials)\n */\n async r2DataCatalogStoreCredential(\n bucketName: string,\n token: string,\n ): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/r2-catalog/${encodeURIComponent(bucketName)}/credential`,\n {\n method: \"POST\",\n body: JSON.stringify({ token }),\n },\n );\n }\n\n /**\n * Deactivates the R2 Data Catalog for a bucket (metadata files remain in R2\n * until the bucket is emptied or objects are removed). Next `apply` will\n * re-enable the catalog as part of `pipelinesAuto` when needed.\n *\n * @see https://developers.cloudflare.com/api/resources/r2_data_catalog/methods/disable/\n */\n async r2DataCatalogDisable(bucketName: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/r2-catalog/${encodeURIComponent(bucketName)}/disable`,\n { method: \"POST\" },\n );\n }\n\n /**\n * List every Workflow registered on the account (paginated, 100/page).\n * Cloudflare returns the configured class + script binding plus\n * aggregate instance counts; we only consume the registration metadata.\n * @see https://developers.cloudflare.com/api/resources/workflows/methods/list/\n */\n async workflowListAll(): Promise<\n Array<{\n id: string;\n name: string;\n class_name: string;\n script_name: string;\n created_on?: string;\n modified_on?: string;\n triggered_on?: string;\n }>\n > {\n type Item = {\n id: string;\n name: string;\n class_name: string;\n script_name: string;\n created_on?: string;\n modified_on?: string;\n triggered_on?: string;\n };\n const all: Item[] = [];\n let page = 1;\n while (true) {\n const data = await this.request<{\n result?: Item[];\n result_info?: { total_pages?: number; page?: number };\n }>(\n `/accounts/${this.accountId}/workflows?page=${page}&per_page=100`,\n );\n const batch = data.result ?? [];\n all.push(...batch);\n const totalPages = data.result_info?.total_pages ?? 1;\n if (page >= totalPages || batch.length === 0) break;\n page += 1;\n }\n return all;\n }\n\n /**\n * Create or update a Workflow registration. Cloudflare's `PUT` is upsert:\n * if `workflow_name` exists it patches `class_name` / `script_name` /\n * `limits`, otherwise it creates a new one. The bound class must already\n * be deployed in `script_name`'s code (PUT against an unknown class\n * succeeds at registration time but fails at first instance create).\n * @see https://developers.cloudflare.com/api/resources/workflows/methods/update/\n */\n async workflowUpsert(\n name: string,\n body: {\n class_name: string;\n script_name: string;\n limits?: { steps?: number };\n },\n ): Promise<{\n id: string;\n name: string;\n class_name: string;\n script_name: string;\n version_id?: string;\n created_on?: string;\n modified_on?: string;\n }> {\n const data = await this.request<{\n result: {\n id: string;\n name: string;\n class_name: string;\n script_name: string;\n version_id?: string;\n created_on?: string;\n modified_on?: string;\n };\n }>(\n `/accounts/${this.accountId}/workflows/${encodeURIComponent(name)}`,\n { method: \"PUT\", body: JSON.stringify(body) },\n );\n return data.result;\n }\n\n /**\n * Delete a Workflow registration by name. Per Cloudflare's docs this only\n * removes the workflow itself — the worker that hosts the class is\n * untouched and existing in-flight instances continue to run.\n * @see https://developers.cloudflare.com/api/resources/workflows/methods/delete/\n */\n async workflowDelete(name: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/workflows/${encodeURIComponent(name)}`,\n { method: \"DELETE\" },\n );\n }\n\n /**\n * List every Secrets Store store on the account (paginated, 100/page).\n * Stores are account-scoped containers; their internal secret entries\n * are listed via {@link secretsStoreSecretListAll}.\n * @see https://developers.cloudflare.com/api/resources/secrets_store/subresources/stores/methods/list/\n */\n async secretsStoreListAll(): Promise<\n Array<{\n id: string;\n name: string;\n account_id?: string;\n created?: string;\n modified?: string;\n }>\n > {\n type Item = {\n id: string;\n name: string;\n account_id?: string;\n created?: string;\n modified?: string;\n };\n const all: Item[] = [];\n let page = 1;\n while (true) {\n const data = await this.request<{\n result?: Item[];\n result_info?: { total_pages?: number; page?: number };\n }>(\n `/accounts/${this.accountId}/secrets_store/stores?page=${page}&per_page=100`,\n );\n const batch = data.result ?? [];\n all.push(...batch);\n const totalPages = data.result_info?.total_pages ?? 1;\n if (page >= totalPages || batch.length === 0) break;\n page += 1;\n }\n return all;\n }\n\n /**\n * Create a new Secrets Store. Per the API a store name uniquely identifies\n * the container within an account but is **not** itself a primary key —\n * the assigned `id` is. Tamer enforces uniqueness via the derived\n * `sec-{logical}-t-{tenantId}-{env}` name and short-circuits to the\n * existing id when it finds a match in `secretsStoreListAll`.\n * @see https://developers.cloudflare.com/api/resources/secrets_store/subresources/stores/methods/create/\n */\n async secretsStoreCreate(\n name: string,\n ): Promise<{ id: string; name: string }> {\n const data = await this.request<{\n result: { id: string; name: string };\n }>(`/accounts/${this.accountId}/secrets_store/stores`, {\n method: \"POST\",\n body: JSON.stringify({ name }),\n });\n return data.result;\n }\n\n /**\n * Delete a Secrets Store by id. Cloudflare cascades the deletion to all\n * secrets inside the store, so callers should ensure no live worker is\n * still binding into this store before invoking.\n * @see https://developers.cloudflare.com/api/resources/secrets_store/subresources/stores/methods/delete/\n */\n async secretsStoreDelete(storeId: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/secrets_store/stores/${encodeURIComponent(storeId)}`,\n { method: \"DELETE\" },\n );\n }\n\n async dispatchNamespaceListAll(): Promise<\n Array<{ namespace_name: string; script_count?: number }>\n > {\n const data = await this.request<{\n result: Array<{ namespace_name: string; script_count?: number }>;\n }>(`/accounts/${this.accountId}/workers/dispatch/namespaces`);\n return data.result ?? [];\n }\n\n async dispatchNamespaceCreate(namespaceName: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/workers/dispatch/namespaces`,\n {\n method: \"POST\",\n body: JSON.stringify({ name: namespaceName }),\n },\n );\n }\n\n async dispatchNamespaceDelete(namespaceName: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/workers/dispatch/namespaces/${encodeURIComponent(namespaceName)}`,\n { method: \"DELETE\" },\n );\n }\n\n /**\n * Upload a user Worker module to a Workers for Platforms dispatch namespace (multipart).\n * @see https://developers.cloudflare.com/api/operations/namespace-worker-script-upload-worker-module\n */\n async dispatchNamespaceScriptPut(\n dispatchNamespace: string,\n scriptName: string,\n formData: FormData,\n ): Promise<void> {\n const url = `${CF_API_BASE}/accounts/${this.accountId}/workers/dispatch/namespaces/${encodeURIComponent(dispatchNamespace)}/scripts/${encodeURIComponent(scriptName)}`;\n const res = await fetch(url, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${this.apiToken}`,\n },\n body: formData,\n });\n const data = (await res.json()) as { success?: boolean; errors?: Array<{ message: string }> };\n if (!res.ok) {\n const errMsg =\n data?.errors?.map((e) => e.message).join(\"; \") ?? res.statusText;\n throw new Error(`CF API error: ${errMsg}`);\n }\n }\n\n /**\n * List every script inside a dispatch namespace.\n * @see https://developers.cloudflare.com/api/resources/workers_for_platforms/subresources/dispatch/subresources/namespaces/subresources/scripts/methods/list/\n */\n async dispatchNamespaceScriptList(\n dispatchNamespace: string,\n ): Promise<Array<{ id: string }>> {\n const data = await this.request<{\n result?: Array<{ id: string }>;\n }>(\n `/accounts/${this.accountId}/workers/dispatch/namespaces/${encodeURIComponent(dispatchNamespace)}/scripts`,\n );\n return data.result ?? [];\n }\n\n /**\n * Remove a script from a Workers for Platforms dispatch namespace.\n * @see https://developers.cloudflare.com/api/resources/workers_for_platforms/subresources/dispatch/subresources/namespaces/subresources/scripts/methods/delete/\n */\n async dispatchNamespaceScriptDelete(\n dispatchNamespace: string,\n scriptName: string,\n options?: { force?: boolean },\n ): Promise<void> {\n const q = options?.force ? \"?force=true\" : \"\";\n const url = `${CF_API_BASE}/accounts/${this.accountId}/workers/dispatch/namespaces/${encodeURIComponent(dispatchNamespace)}/scripts/${encodeURIComponent(scriptName)}${q}`;\n const res = await fetch(url, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${this.apiToken}`,\n },\n });\n if (!res.ok) {\n let errMsg = res.statusText;\n try {\n const data = (await res.json()) as {\n errors?: Array<{ message: string }>;\n };\n errMsg = data?.errors?.map((e) => e.message).join(\"; \") ?? errMsg;\n } catch {\n /* empty body */\n }\n throw new Error(`CF API error: ${errMsg}`);\n }\n }\n\n /**\n * @see https://developers.cloudflare.com/api/resources/workers/subresources/routes/methods/list/\n */\n async zoneWorkerRoutesList(\n zoneId: string,\n ): Promise<Array<{ id: string; pattern: string; script: string }>> {\n const data = await this.request<{\n result?: Array<{ id: string; pattern: string; script: string }>;\n }>(`/zones/${zoneId}/workers/routes`);\n return data.result ?? [];\n }\n\n /**\n * @see https://developers.cloudflare.com/api/resources/workers/subresources/routes/methods/create/\n */\n async zoneWorkerRouteCreate(\n zoneId: string,\n body: { pattern: string; script: string },\n ): Promise<{ id: string }> {\n const data = await this.request<{ result: { id: string } }>(\n `/zones/${zoneId}/workers/routes`,\n {\n method: \"POST\",\n body: JSON.stringify(body),\n },\n );\n return data.result;\n }\n\n /**\n * @see https://developers.cloudflare.com/api/resources/workers/subresources/routes/methods/delete/\n */\n async zoneWorkerRouteDelete(zoneId: string, routeId: string): Promise<void> {\n await this.request(\n `/zones/${zoneId}/workers/routes/${encodeURIComponent(routeId)}`,\n { method: \"DELETE\" },\n );\n }\n\n /**\n * List every DNS record on a zone (paginated, 100/page). Returns the\n * subset of fields Tamer cares about — the full Cloudflare object also\n * carries `meta`, `proxiable`, `created_on`, `modified_on`, etc., none of\n * which we persist in state.\n *\n * @see https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/list/\n */\n async zoneDnsRecordListAll(\n zoneId: string,\n ): Promise<\n Array<{\n id: string;\n type: string;\n name: string;\n content: string;\n ttl?: number;\n proxied?: boolean;\n priority?: number;\n comment?: string | null;\n }>\n > {\n type Item = {\n id: string;\n type: string;\n name: string;\n content: string;\n ttl?: number;\n proxied?: boolean;\n priority?: number;\n comment?: string | null;\n };\n const all: Item[] = [];\n let page = 1;\n while (true) {\n const data = await this.request<{\n result?: Item[];\n result_info?: { total_pages?: number; page?: number };\n }>(`/zones/${zoneId}/dns_records?page=${page}&per_page=100`);\n const batch = data.result ?? [];\n all.push(...batch);\n const totalPages = data.result_info?.total_pages ?? 1;\n if (page >= totalPages || batch.length === 0) break;\n page += 1;\n }\n return all;\n }\n\n /**\n * Create a DNS record on a zone. The Cloudflare API rejects duplicate\n * `(type, name, content)` triples for record types where that combination\n * is meaningful (CNAME, A, AAAA), so callers should pre-check via\n * {@link zoneDnsRecordListAll}.\n *\n * @see https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/create/\n */\n async zoneDnsRecordCreate(\n zoneId: string,\n body: {\n type: string;\n name: string;\n content: string;\n ttl?: number;\n proxied?: boolean;\n priority?: number;\n comment?: string;\n },\n ): Promise<{ id: string; type: string; name: string; content: string }> {\n const data = await this.request<{\n result: { id: string; type: string; name: string; content: string };\n }>(`/zones/${zoneId}/dns_records`, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n return data.result;\n }\n\n /**\n * Patch a DNS record in place. Cloudflare's PATCH endpoint accepts any\n * subset of mutable fields (`content`, `ttl`, `proxied`, `priority`,\n * `comment`) but rejects `type` changes — Tamer falls back to\n * delete-and-recreate when the type drifts.\n *\n * @see https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/edit/\n */\n async zoneDnsRecordPatch(\n zoneId: string,\n recordId: string,\n body: Partial<{\n type: string;\n name: string;\n content: string;\n ttl: number;\n proxied: boolean;\n priority: number;\n comment: string;\n }>,\n ): Promise<void> {\n await this.request(\n `/zones/${zoneId}/dns_records/${encodeURIComponent(recordId)}`,\n {\n method: \"PATCH\",\n body: JSON.stringify(body),\n },\n );\n }\n\n /**\n * @see https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/delete/\n */\n async zoneDnsRecordDelete(zoneId: string, recordId: string): Promise<void> {\n await this.request(\n `/zones/${zoneId}/dns_records/${encodeURIComponent(recordId)}`,\n { method: \"DELETE\" },\n );\n }\n\n /**\n * Look up zones by exact name.\n *\n * Account-scoped tokens with `Zone Read` see every zone under the account.\n * Returns an array because Cloudflare allows the same name across accounts;\n * the caller normally takes `[0]`.\n *\n * @see https://developers.cloudflare.com/api/operations/zones-get\n */\n async zonesListByName(\n name: string,\n ): Promise<Array<{ id: string; name: string }>> {\n const qs = new URLSearchParams({\n name,\n \"account.id\": this.accountId,\n per_page: \"50\",\n });\n const data = await this.request<{\n result?: Array<{ id: string; name: string }>;\n }>(`/zones?${qs.toString()}`);\n return data.result ?? [];\n }\n\n /**\n * List account-scoped Logpush jobs (includes `workers_trace_events`).\n * @see https://developers.cloudflare.com/api/resources/logpush/subresources/jobs/methods/list/\n */\n async logpushAccountJobsList(): Promise<\n Array<{\n id: number;\n name?: string;\n dataset?: string;\n enabled?: boolean;\n destination_conf?: string;\n filter?: string;\n error_message?: string | null;\n }>\n > {\n const data = await this.request<{\n result?: Array<{\n id: number;\n name?: string;\n dataset?: string;\n enabled?: boolean;\n destination_conf?: string;\n filter?: string;\n error_message?: string | null;\n }>;\n }>(`/accounts/${this.accountId}/logpush/jobs`);\n return data.result ?? [];\n }\n\n /**\n * Fetch one account-scoped Logpush job (includes `destination_conf`).\n * @see https://developers.cloudflare.com/api/resources/logpush/subresources/jobs/methods/get/\n */\n async logpushAccountJobGet(jobId: number): Promise<{\n id: number;\n name?: string;\n dataset?: string;\n enabled?: boolean;\n destination_conf?: string;\n filter?: string;\n output_options?: Record<string, unknown>;\n }> {\n const data = await this.request<{\n result?: {\n id: number;\n name?: string;\n dataset?: string;\n enabled?: boolean;\n destination_conf?: string;\n filter?: string;\n output_options?: Record<string, unknown>;\n };\n }>(`/accounts/${this.accountId}/logpush/jobs/${jobId}`);\n if (!data.result?.id) {\n throw new Error(\n `Logpush job GET ${jobId}: missing result (job may not exist on this account)`,\n );\n }\n return data.result;\n }\n\n /**\n * Create an account Logpush job. Caller supplies `destination_conf` and dataset.\n * @see https://developers.cloudflare.com/api/resources/logpush/subresources/jobs/methods/create/\n */\n async logpushAccountJobCreate(body: {\n name: string;\n dataset: string;\n destination_conf: string;\n enabled?: boolean;\n filter?: string;\n output_options?: Record<string, unknown>;\n }): Promise<{ id: number }> {\n const data = await this.request<{ result: { id: number } }>(\n `/accounts/${this.accountId}/logpush/jobs`,\n { method: \"POST\", body: JSON.stringify(body) },\n );\n return data.result;\n }\n\n /**\n * Update mutable Logpush job fields in place.\n * @see https://developers.cloudflare.com/api/resources/logpush/subresources/jobs/methods/update/\n */\n async logpushAccountJobUpdate(\n jobId: number,\n body: {\n destination_conf: string;\n enabled?: boolean;\n filter?: string;\n output_options?: Record<string, unknown>;\n },\n ): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/logpush/jobs/${jobId}`,\n { method: \"PUT\", body: JSON.stringify(body) },\n );\n }\n\n /**\n * Delete a Logpush job by numeric id.\n * @see https://developers.cloudflare.com/api/resources/logpush/subresources/jobs/methods/delete/\n */\n async logpushAccountJobDelete(jobId: number): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/logpush/jobs/${jobId}`,\n { method: \"DELETE\" },\n );\n }\n\n /**\n * List all permission groups for account-owned API tokens (paginated).\n * @see https://developers.cloudflare.com/api/resources/accounts/subresources/tokens/subresources/permission_groups/methods/list/\n */\n async accountTokenPermissionGroupsListAll(): Promise<\n Array<{ id: string; name: string; scopes?: string[] }>\n > {\n const all: Array<{ id: string; name: string; scopes?: string[] }> = [];\n const seen = new Set<string>();\n let page = 1;\n const perPage = 100;\n /** Failsafe: pagination must always terminate even if the API omits `total_count` or repeats pages. */\n const maxPage = 500;\n while (true) {\n if (page > maxPage) {\n throw new Error(\n `Account token permission groups: exceeded ${maxPage} pages (partial count ${all.length}) — ` +\n `refusing to paginate further (check Cloudflare API or file a bug)`,\n );\n }\n const data = await this.request<{\n result?: Array<{ id: string; name: string; scopes?: string[] }>;\n result_info?: {\n per_page?: number;\n count?: number;\n total_count?: number;\n };\n }>(\n `/accounts/${this.accountId}/tokens/permission_groups?per_page=${perPage}&page=${page}`,\n );\n const batch = data.result ?? [];\n if (batch.length === 0) break;\n\n const newRows = batch.filter((g) => g.id && !seen.has(g.id));\n for (const g of newRows) {\n seen.add(g.id);\n }\n if (newRows.length === 0) {\n break;\n }\n all.push(...newRows);\n\n if (batch.length < perPage) break;\n const total = data.result_info?.total_count;\n if (typeof total === \"number\" && all.length >= total) break;\n page++;\n }\n return all;\n }\n\n /**\n * List all account-owned API tokens (paginated).\n * @see https://developers.cloudflare.com/api/resources/accounts/subresources/tokens/methods/list/\n */\n async accountTokenListAll(): Promise<\n Array<{\n id: string;\n name?: string;\n status?: string;\n }>\n > {\n type Row = { id: string; name?: string; status?: string };\n const all: Row[] = [];\n let page = 1;\n const perPage = 50;\n const maxPage = 500;\n while (page <= maxPage) {\n const data = await this.request<{\n result?: Row[];\n result_info?: {\n page?: number;\n per_page?: number;\n total_count?: number;\n };\n }>(\n `/accounts/${this.accountId}/tokens?per_page=${perPage}&page=${page}`,\n );\n const batch = data.result ?? [];\n all.push(...batch);\n const total = data.result_info?.total_count;\n if (typeof total === \"number\" && all.length >= total) break;\n if (batch.length < perPage) break;\n page += 1;\n }\n return all;\n }\n\n /**\n * Create an account-owned API token. `result.value` is only returned on create.\n * @see https://developers.cloudflare.com/api/resources/accounts/subresources/tokens/methods/create/\n */\n async accountTokenCreate(body: {\n name: string;\n policies: Array<{\n effect: \"allow\" | \"deny\";\n permission_groups: Array<{ id: string }>;\n resources: Record<string, string> | Record<string, Record<string, string>>;\n }>;\n }): Promise<{ id: string; value?: string }> {\n const data = await this.request<{ result: { id: string; value?: string } }>(\n `/accounts/${this.accountId}/tokens`,\n { method: \"POST\", body: JSON.stringify(body) },\n );\n return data.result;\n }\n\n /**\n * Delete (revoke) an account-owned API token.\n * @see https://developers.cloudflare.com/api/resources/accounts/subresources/tokens/methods/delete/\n */\n async accountTokenDelete(tokenId: string): Promise<void> {\n await this.request(\n `/accounts/${this.accountId}/tokens/${encodeURIComponent(tokenId)}`,\n { method: \"DELETE\" },\n );\n }\n}\n"],"mappings":"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"}
|