@theholocron/cloudflare-client 1.32.0 → 1.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,184 @@
1
+ import "@theholocron/http-client";
2
+ import { ErrorSink, Logger } from "@theholocron/observability/core";
3
+ //#region ../../../../../src/utils.d.ts
4
+ interface CloudflareClientOptions {
5
+ token: string;
6
+ /** Override base URL for testing. Defaults to https://api.cloudflare.com/client/v4 */
7
+ baseUrl?: string;
8
+ /** Override fetch for testing. Defaults to globalThis.fetch. */
9
+ fetch?: typeof fetch;
10
+ /** Structured logger for request/response diagnostics. Defaults to a no-op. */
11
+ logger?: Logger;
12
+ /** Reports transport failures and unexpected 5xx. Defaults to a no-op. */
13
+ errors?: ErrorSink;
14
+ }
15
+ interface CfEnvelope<T> {
16
+ success: boolean;
17
+ errors: unknown[];
18
+ result: T;
19
+ }
20
+ //#endregion
21
+ //#region ../../../../../src/dns/dns.d.ts
22
+ type CfDnsRecordType = "A" | "AAAA" | "CNAME" | "TXT" | "MX" | "NS" | "SRV" | "CAA";
23
+ interface CfDnsRecord {
24
+ id: string;
25
+ type: CfDnsRecordType;
26
+ name: string;
27
+ content: string;
28
+ ttl: number;
29
+ proxied: boolean;
30
+ }
31
+ interface CfDnsRecordInput {
32
+ type: CfDnsRecordType;
33
+ name: string;
34
+ content: string;
35
+ ttl?: number;
36
+ proxied?: boolean;
37
+ }
38
+ //#endregion
39
+ //#region ../../../../../src/pages/pages.d.ts
40
+ type CfPagesEnvVarType = "plain_text" | "secret_text";
41
+ interface CfPagesEnvVar {
42
+ value: string;
43
+ type: CfPagesEnvVarType;
44
+ }
45
+ interface CfPagesEnvConfig {
46
+ env_vars: Record<string, CfPagesEnvVar>;
47
+ }
48
+ interface CfPagesProject {
49
+ id: string;
50
+ name: string;
51
+ subdomain: string;
52
+ domains: string[];
53
+ production_branch: string;
54
+ deployment_configs: {
55
+ preview: CfPagesEnvConfig;
56
+ production: CfPagesEnvConfig;
57
+ };
58
+ }
59
+ interface CfPagesProjectInput {
60
+ name: string;
61
+ production_branch: string;
62
+ }
63
+ interface CfPagesDeploymentStage {
64
+ name: string;
65
+ status: "idle" | "active" | "canceled" | "success" | "failure";
66
+ }
67
+ interface CfPagesDomain {
68
+ id: string;
69
+ name: string;
70
+ status: "active" | "pending" | "blocked" | "error" | "moved" | "pending_tls";
71
+ }
72
+ interface CfPagesDeployment {
73
+ id: string;
74
+ url: string;
75
+ environment: "preview" | "production";
76
+ deployment_trigger: {
77
+ type: string;
78
+ metadata: {
79
+ branch: string;
80
+ commit_hash: string;
81
+ };
82
+ };
83
+ latest_stage: CfPagesDeploymentStage;
84
+ created_on: string;
85
+ }
86
+ //#endregion
87
+ //#region ../../../../../src/zones/zones.d.ts
88
+ interface CfZone {
89
+ id: string;
90
+ name: string;
91
+ status: string;
92
+ }
93
+ //#endregion
94
+ //#region ../../../../../src/tunnels/tunnels.d.ts
95
+ interface CfTunnel {
96
+ id: string;
97
+ name: string;
98
+ }
99
+ interface CfIngressRule {
100
+ hostname?: string;
101
+ service: string;
102
+ path?: string;
103
+ }
104
+ interface CfTunnelConfig {
105
+ ingress: CfIngressRule[];
106
+ }
107
+ //#endregion
108
+ //#region ../../../../../src/tokens/tokens.d.ts
109
+ interface CfTokenVerification {
110
+ id: string;
111
+ status: "active" | "disabled" | "expired";
112
+ }
113
+ //#endregion
114
+ //#region ../../../../../src/workers/workers.d.ts
115
+ interface CfWorkerRoute {
116
+ id: string;
117
+ pattern: string;
118
+ script: string | null;
119
+ }
120
+ /** https://developers.cloudflare.com/api/operations/worker-script-put-encrypted-secret-binding */
121
+ interface CfWorkerSecret {
122
+ name: string;
123
+ type: "secret_text";
124
+ }
125
+ //#endregion
126
+ //#region ../../../../../src/index.d.ts
127
+ declare function createCloudflareClient(opts: CloudflareClientOptions): {
128
+ dns: {
129
+ list: (zoneId: string, query?: {
130
+ type?: string;
131
+ name?: string;
132
+ }) => Promise<CfDnsRecord[]>;
133
+ create: (zoneId: string, record: CfDnsRecordInput) => Promise<CfDnsRecord>;
134
+ update: (zoneId: string, recordId: string, record: Partial<CfDnsRecordInput>) => Promise<CfDnsRecord>;
135
+ delete: (zoneId: string, recordId: string) => Promise<{
136
+ id: string;
137
+ }>;
138
+ };
139
+ pages: {
140
+ listProjects: (accountId: string) => Promise<CfPagesProject[]>;
141
+ getProject: (accountId: string, name: string) => Promise<CfPagesProject>;
142
+ createProject: (accountId: string, input: CfPagesProjectInput) => Promise<CfPagesProject>;
143
+ updateProject: (accountId: string, name: string, patch: Partial<Pick<CfPagesProject, "deployment_configs" | "production_branch">>) => Promise<CfPagesProject>;
144
+ createDeployment: (accountId: string, projectName: string, branch: string) => Promise<CfPagesDeployment>;
145
+ getDeployment: (accountId: string, projectName: string, deploymentId: string) => Promise<CfPagesDeployment>;
146
+ listDeployments: (accountId: string, projectName: string) => Promise<CfPagesDeployment[]>;
147
+ deleteDeployment: (accountId: string, projectName: string, deploymentId: string) => Promise<void>;
148
+ listDomains: (accountId: string, projectName: string) => Promise<CfPagesDomain[]>;
149
+ addDomain: (accountId: string, projectName: string, hostname: string) => Promise<CfPagesDomain>;
150
+ };
151
+ zones: {
152
+ list: (query?: {
153
+ name?: string;
154
+ per_page?: number;
155
+ }) => Promise<CfZone[]>;
156
+ };
157
+ tunnels: {
158
+ create: (accountId: string, input: {
159
+ name: string;
160
+ }) => Promise<CfTunnel>;
161
+ list: (accountId: string) => Promise<CfTunnel[]>;
162
+ token: (accountId: string, tunnelId: string) => Promise<string>;
163
+ delete: (accountId: string, tunnelId: string) => Promise<void>;
164
+ getConfig: (accountId: string, tunnelId: string) => Promise<{
165
+ config: CfTunnelConfig;
166
+ }>;
167
+ putConfig: (accountId: string, tunnelId: string, config: CfTunnelConfig) => Promise<void>;
168
+ };
169
+ tokens: {
170
+ verify: () => Promise<CfTokenVerification>;
171
+ };
172
+ workers: {
173
+ putScript: (accountId: string, scriptName: string, script: string) => Promise<void>;
174
+ listRoutes: (zoneId: string) => Promise<CfWorkerRoute[]>;
175
+ createRoute: (zoneId: string, pattern: string, script: string) => Promise<CfWorkerRoute>;
176
+ updateRoute: (zoneId: string, routeId: string, pattern: string, script: string) => Promise<CfWorkerRoute>;
177
+ putSecret: (accountId: string, scriptName: string, name: string, value: string) => Promise<CfWorkerSecret>;
178
+ listSecrets: (accountId: string, scriptName: string) => Promise<CfWorkerSecret[]>;
179
+ deleteSecret: (accountId: string, scriptName: string, name: string) => Promise<void>;
180
+ };
181
+ };
182
+ type CloudflareClient = ReturnType<typeof createCloudflareClient>;
183
+ //#endregion
184
+ export { type CfDnsRecord, type CfDnsRecordInput, type CfDnsRecordType, type CfEnvelope, type CfIngressRule, type CfPagesDeployment, type CfPagesDeploymentStage, type CfPagesDomain, type CfPagesEnvConfig, type CfPagesEnvVar, type CfPagesEnvVarType, type CfPagesProject, type CfPagesProjectInput, type CfTokenVerification, type CfTunnel, type CfTunnelConfig, type CfWorkerRoute, type CfWorkerSecret, type CfZone, CloudflareClient, type CloudflareClientOptions, createCloudflareClient };
package/dist/index.mjs ADDED
@@ -0,0 +1,148 @@
1
+ import { ProviderApiError, createRestClient } from "@theholocron/http-client";
2
+ //#region ../../../../../src/utils.ts
3
+ function createCloudflareRestClient(opts) {
4
+ return createRestClient({
5
+ baseUrl: opts.baseUrl ?? "https://api.cloudflare.com/client/v4",
6
+ token: opts.token,
7
+ vendor: "Cloudflare",
8
+ fetch: opts.fetch,
9
+ logger: opts.logger,
10
+ errors: opts.errors
11
+ });
12
+ }
13
+ /**
14
+ * Unwrap Cloudflare's `{ result, success, errors }` envelope.
15
+ * `createRestClient` already throws on HTTP 4xx/5xx; this handles the rare
16
+ * 200 + `success: false` case Cloudflare emits for some validation failures.
17
+ */
18
+ async function cfRequest(rest, method, path, body, query) {
19
+ const envelope = await rest.request(path, {
20
+ method,
21
+ ...body !== void 0 ? { body } : {},
22
+ ...query !== void 0 ? { query } : {}
23
+ });
24
+ if (!envelope) return void 0;
25
+ if (!envelope.success) throw new ProviderApiError(`Cloudflare ${method} ${path} returned success:false`, 0, JSON.stringify(envelope.errors));
26
+ return envelope.result;
27
+ }
28
+ //#endregion
29
+ //#region ../../../../../src/dns/dns.ts
30
+ function dns(rest) {
31
+ return {
32
+ list: (zoneId, query) => cfRequest(rest, "GET", `/zones/${zoneId}/dns_records`, void 0, {
33
+ ...query?.type ? { type: query.type } : {},
34
+ ...query?.name ? { name: query.name } : {},
35
+ per_page: "100"
36
+ }),
37
+ create: (zoneId, record) => cfRequest(rest, "POST", `/zones/${zoneId}/dns_records`, {
38
+ type: record.type,
39
+ name: record.name,
40
+ content: record.content,
41
+ ttl: record.ttl ?? 1,
42
+ proxied: record.proxied ?? false
43
+ }),
44
+ update: (zoneId, recordId, record) => cfRequest(rest, "PATCH", `/zones/${zoneId}/dns_records/${recordId}`, record),
45
+ delete: (zoneId, recordId) => cfRequest(rest, "DELETE", `/zones/${zoneId}/dns_records/${recordId}`)
46
+ };
47
+ }
48
+ //#endregion
49
+ //#region ../../../../../src/pages/pages.ts
50
+ function pages(rest) {
51
+ return {
52
+ listProjects: (accountId) => cfRequest(rest, "GET", `/accounts/${accountId}/pages/projects`, void 0, { per_page: "100" }),
53
+ getProject: (accountId, name) => cfRequest(rest, "GET", `/accounts/${accountId}/pages/projects/${name}`),
54
+ createProject: (accountId, input) => cfRequest(rest, "POST", `/accounts/${accountId}/pages/projects`, input),
55
+ updateProject: (accountId, name, patch) => cfRequest(rest, "PATCH", `/accounts/${accountId}/pages/projects/${name}`, patch),
56
+ createDeployment: (accountId, projectName, branch) => cfRequest(rest, "POST", `/accounts/${accountId}/pages/projects/${projectName}/deployments`, { branch }),
57
+ getDeployment: (accountId, projectName, deploymentId) => cfRequest(rest, "GET", `/accounts/${accountId}/pages/projects/${projectName}/deployments/${deploymentId}`),
58
+ listDeployments: (accountId, projectName) => cfRequest(rest, "GET", `/accounts/${accountId}/pages/projects/${projectName}/deployments`, void 0, { per_page: "25" }),
59
+ deleteDeployment: (accountId, projectName, deploymentId) => cfRequest(rest, "DELETE", `/accounts/${accountId}/pages/projects/${projectName}/deployments/${deploymentId}`, void 0, { force: "true" }),
60
+ listDomains: (accountId, projectName) => cfRequest(rest, "GET", `/accounts/${accountId}/pages/projects/${projectName}/domains`),
61
+ addDomain: (accountId, projectName, hostname) => cfRequest(rest, "POST", `/accounts/${accountId}/pages/projects/${projectName}/domains`, { name: hostname })
62
+ };
63
+ }
64
+ //#endregion
65
+ //#region ../../../../../src/tokens/tokens.ts
66
+ function tokens(rest) {
67
+ return { verify: () => cfRequest(rest, "GET", "/user/tokens/verify") };
68
+ }
69
+ //#endregion
70
+ //#region ../../../../../src/tunnels/tunnels.ts
71
+ function tunnels(rest) {
72
+ return {
73
+ create: (accountId, input) => cfRequest(rest, "POST", `/accounts/${accountId}/cfd_tunnel`, {
74
+ name: input.name,
75
+ config_src: "cloudflare"
76
+ }),
77
+ list: (accountId) => cfRequest(rest, "GET", `/accounts/${accountId}/cfd_tunnel`, void 0, { is_deleted: "false" }),
78
+ token: (accountId, tunnelId) => cfRequest(rest, "GET", `/accounts/${accountId}/cfd_tunnel/${tunnelId}/token`),
79
+ delete: (accountId, tunnelId) => cfRequest(rest, "DELETE", `/accounts/${accountId}/cfd_tunnel/${tunnelId}`, void 0, { cascade: "true" }),
80
+ getConfig: (accountId, tunnelId) => cfRequest(rest, "GET", `/accounts/${accountId}/cfd_tunnel/${tunnelId}/configurations`),
81
+ putConfig: (accountId, tunnelId, config) => cfRequest(rest, "PUT", `/accounts/${accountId}/cfd_tunnel/${tunnelId}/configurations`, { config })
82
+ };
83
+ }
84
+ //#endregion
85
+ //#region ../../../../../src/workers/workers.ts
86
+ function workers(rest, opts) {
87
+ const baseUrl = opts.baseUrl ?? "https://api.cloudflare.com/client/v4";
88
+ const fetchImpl = opts.fetch ?? globalThis.fetch;
89
+ return {
90
+ putScript: async (accountId, scriptName, script) => {
91
+ const form = new FormData();
92
+ form.append("metadata", new Blob([JSON.stringify({
93
+ main_module: "index.js",
94
+ compatibility_date: "2025-08-31"
95
+ })], { type: "application/json" }));
96
+ form.append("index.js", new Blob([script], { type: "application/javascript+module" }), "index.js");
97
+ const path = `/accounts/${accountId}/workers/scripts/${encodeURIComponent(scriptName)}`;
98
+ const res = await fetchImpl(`${baseUrl}${path}`, {
99
+ method: "PUT",
100
+ headers: { authorization: `Bearer ${opts.token}` },
101
+ body: form
102
+ });
103
+ if (!res.ok) {
104
+ const body = await res.text().catch(() => "");
105
+ throw new ProviderApiError(`Cloudflare PUT ${path} → ${res.status}`, res.status, body);
106
+ }
107
+ },
108
+ listRoutes: (zoneId) => cfRequest(rest, "GET", `/zones/${zoneId}/workers/routes`),
109
+ createRoute: (zoneId, pattern, script) => cfRequest(rest, "POST", `/zones/${zoneId}/workers/routes`, {
110
+ pattern,
111
+ script
112
+ }),
113
+ updateRoute: (zoneId, routeId, pattern, script) => cfRequest(rest, "PUT", `/zones/${zoneId}/workers/routes/${routeId}`, {
114
+ pattern,
115
+ script
116
+ }),
117
+ putSecret: (accountId, scriptName, name, value) => cfRequest(rest, "PUT", `/accounts/${accountId}/workers/scripts/${encodeURIComponent(scriptName)}/secrets`, {
118
+ name,
119
+ text: value,
120
+ type: "secret_text"
121
+ }),
122
+ listSecrets: (accountId, scriptName) => cfRequest(rest, "GET", `/accounts/${accountId}/workers/scripts/${encodeURIComponent(scriptName)}/secrets`),
123
+ deleteSecret: (accountId, scriptName, name) => cfRequest(rest, "DELETE", `/accounts/${accountId}/workers/scripts/${encodeURIComponent(scriptName)}/secrets/${encodeURIComponent(name)}`)
124
+ };
125
+ }
126
+ //#endregion
127
+ //#region ../../../../../src/zones/zones.ts
128
+ function zones(rest) {
129
+ return { list: (query) => cfRequest(rest, "GET", "/zones", void 0, {
130
+ ...query?.name ? { name: query.name } : {},
131
+ per_page: String(query?.per_page ?? 100)
132
+ }) };
133
+ }
134
+ //#endregion
135
+ //#region ../../../../../src/index.ts
136
+ function createCloudflareClient(opts) {
137
+ const rest = createCloudflareRestClient(opts);
138
+ return {
139
+ dns: dns(rest),
140
+ pages: pages(rest),
141
+ zones: zones(rest),
142
+ tunnels: tunnels(rest),
143
+ tokens: tokens(rest),
144
+ workers: workers(rest, opts)
145
+ };
146
+ }
147
+ //#endregion
148
+ export { createCloudflareClient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/cloudflare-client",
3
- "version": "1.32.0",
3
+ "version": "1.32.1",
4
4
  "description": "A TypeScript client for the Cloudflare API",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -33,13 +33,13 @@
33
33
  ],
34
34
  "dependencies": {
35
35
  "@theholocron/observability": "^0.3.0",
36
- "@theholocron/http-client": "^1.32.0"
36
+ "@theholocron/http-client": "^1.32.1"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@theholocron/cli": "5.0.0-alpha.79",
40
40
  "@theholocron/eslint-config": "^8.4.3",
41
41
  "@theholocron/tsconfig": "^8.2.0",
42
- "@theholocron/tsdown-config": "^8.5.2",
42
+ "@theholocron/tsdown-config": "^8.6.1",
43
43
  "@theholocron/vitest-config": "^8.2.0",
44
44
  "@types/node": "^26.4.1",
45
45
  "@vitest/coverage-v8": "^5.0.0",