@theholocron/holocron-plugin-cloudflare 3.45.0 → 3.45.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +33 -3
- package/dist/index.mjs +131 -2
- package/package.json +7 -7
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AuthError, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, ResolveTokenInput } from "@theholocron/cli";
|
|
2
|
-
import { CloudflareClient, CloudflareClientOptions, createCloudflareClient } from "@theholocron/cloudflare-client";
|
|
1
|
+
import { AuthError, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, ResolveTokenInput, WikiProxyConfig, Workers } from "@theholocron/cli";
|
|
2
|
+
import { CloudflareClient, CloudflareClientOptions, CloudflareClientOptions as CloudflareClientOptions$1, createCloudflareClient } from "@theholocron/cloudflare-client";
|
|
3
3
|
//#region src/auth.d.ts
|
|
4
4
|
declare const resolveToken: (input?: ResolveTokenInput) => string;
|
|
5
5
|
//#endregion
|
|
@@ -52,6 +52,34 @@ declare class CloudflareDns implements Dns {
|
|
|
52
52
|
private resolveZone;
|
|
53
53
|
}
|
|
54
54
|
//#endregion
|
|
55
|
+
//#region src/capabilities/workers.d.ts
|
|
56
|
+
interface ZonesClient {
|
|
57
|
+
list(query?: {
|
|
58
|
+
name?: string;
|
|
59
|
+
per_page?: number;
|
|
60
|
+
}): Promise<Array<{
|
|
61
|
+
id: string;
|
|
62
|
+
}>>;
|
|
63
|
+
}
|
|
64
|
+
declare class CloudflareWorkers implements Workers {
|
|
65
|
+
private readonly zones;
|
|
66
|
+
private readonly accountId;
|
|
67
|
+
readonly key: "workers";
|
|
68
|
+
readonly providerName = "cloudflare";
|
|
69
|
+
private readonly zoneCache;
|
|
70
|
+
private readonly baseUrl;
|
|
71
|
+
private readonly fetchImpl;
|
|
72
|
+
private readonly token;
|
|
73
|
+
constructor(zones: ZonesClient, accountId: string, opts: Pick<CloudflareClientOptions$1, "token" | "baseUrl" | "fetch">);
|
|
74
|
+
upsertProxy(hostname: string, config: WikiProxyConfig): Promise<void>;
|
|
75
|
+
private putScript;
|
|
76
|
+
private listRoutes;
|
|
77
|
+
private createRoute;
|
|
78
|
+
private updateRoute;
|
|
79
|
+
private cfRequest;
|
|
80
|
+
private resolveZone;
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
55
83
|
//#region src/verify-token.d.ts
|
|
56
84
|
interface VerifyTokenSuccess {
|
|
57
85
|
ok: true;
|
|
@@ -86,13 +114,15 @@ interface PluginContext {
|
|
|
86
114
|
declare function createContext(options?: CloudflarePluginOptions): PluginContext;
|
|
87
115
|
declare function dns(ctx: PluginContext): Dns;
|
|
88
116
|
declare function deployment(ctx: PluginContext): Deployment;
|
|
117
|
+
declare function workersCapability(ctx: PluginContext): Workers;
|
|
89
118
|
declare function createPlugin(options?: CloudflarePluginOptions): {
|
|
90
119
|
name: string;
|
|
91
120
|
capabilities: {
|
|
92
121
|
deployment?: (() => Deployment) | undefined;
|
|
122
|
+
workers?: (() => Workers) | undefined;
|
|
93
123
|
dns: () => Dns;
|
|
94
124
|
};
|
|
95
125
|
};
|
|
96
126
|
declare const AUTH_HINT: string;
|
|
97
127
|
//#endregion
|
|
98
|
-
export { AUTH_HINT, AuthError, type CloudflareClient, type CloudflareClientOptions, CloudflareDeployment, CloudflareDns, CloudflarePluginOptions, PluginContext, type ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, createCloudflareClient, createContext, createPlugin, deployment, dns, resolveToken, verifyToken };
|
|
128
|
+
export { AUTH_HINT, AuthError, type CloudflareClient, type CloudflareClientOptions, CloudflareDeployment, CloudflareDns, CloudflarePluginOptions, CloudflareWorkers, PluginContext, type ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, createCloudflareClient, createContext, createPlugin, deployment, dns, resolveToken, verifyToken, workersCapability };
|
package/dist/index.mjs
CHANGED
|
@@ -182,6 +182,123 @@ function mapRecord(raw) {
|
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
184
|
//#endregion
|
|
185
|
+
//#region src/capabilities/workers.ts
|
|
186
|
+
var CloudflareWorkers = class {
|
|
187
|
+
zones;
|
|
188
|
+
accountId;
|
|
189
|
+
key = "workers";
|
|
190
|
+
providerName = "cloudflare";
|
|
191
|
+
zoneCache = /* @__PURE__ */ new Map();
|
|
192
|
+
baseUrl;
|
|
193
|
+
fetchImpl;
|
|
194
|
+
token;
|
|
195
|
+
constructor(zones, accountId, opts) {
|
|
196
|
+
this.zones = zones;
|
|
197
|
+
this.accountId = accountId;
|
|
198
|
+
this.token = opts.token;
|
|
199
|
+
this.baseUrl = opts.baseUrl ?? "https://api.cloudflare.com/client/v4";
|
|
200
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
201
|
+
}
|
|
202
|
+
async upsertProxy(hostname, config) {
|
|
203
|
+
const scriptName = hostnameToScriptName(hostname);
|
|
204
|
+
const script = generateProxyScript(config);
|
|
205
|
+
await this.putScript(scriptName, script);
|
|
206
|
+
const zoneId = await this.resolveZone(hostname);
|
|
207
|
+
const pattern = `${hostname}/*`;
|
|
208
|
+
const existing = (await this.listRoutes(zoneId)).find((r) => r.pattern === pattern);
|
|
209
|
+
if (existing) {
|
|
210
|
+
if (existing.script !== scriptName) await this.updateRoute(zoneId, existing.id, pattern, scriptName);
|
|
211
|
+
} else await this.createRoute(zoneId, pattern, scriptName);
|
|
212
|
+
}
|
|
213
|
+
async putScript(scriptName, script) {
|
|
214
|
+
const form = new FormData();
|
|
215
|
+
form.append("metadata", new Blob([JSON.stringify({
|
|
216
|
+
main_module: "index.js",
|
|
217
|
+
compatibility_date: "2025-08-31"
|
|
218
|
+
})], { type: "application/json" }));
|
|
219
|
+
form.append("index.js", new Blob([script], { type: "application/javascript+module" }), "index.js");
|
|
220
|
+
const path = `/accounts/${this.accountId}/workers/scripts/${encodeURIComponent(scriptName)}`;
|
|
221
|
+
const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
222
|
+
method: "PUT",
|
|
223
|
+
headers: { authorization: `Bearer ${this.token}` },
|
|
224
|
+
body: form
|
|
225
|
+
});
|
|
226
|
+
if (!res.ok) {
|
|
227
|
+
const body = await res.text().catch(() => "");
|
|
228
|
+
throw new ProviderApiError(`Cloudflare PUT ${path} → ${res.status}`, res.status, body);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
listRoutes(zoneId) {
|
|
232
|
+
return this.cfRequest("GET", `/zones/${zoneId}/workers/routes`);
|
|
233
|
+
}
|
|
234
|
+
createRoute(zoneId, pattern, script) {
|
|
235
|
+
return this.cfRequest("POST", `/zones/${zoneId}/workers/routes`, {
|
|
236
|
+
pattern,
|
|
237
|
+
script
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
updateRoute(zoneId, routeId, pattern, script) {
|
|
241
|
+
return this.cfRequest("PUT", `/zones/${zoneId}/workers/routes/${routeId}`, {
|
|
242
|
+
pattern,
|
|
243
|
+
script
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
async cfRequest(method, path, body) {
|
|
247
|
+
const headers = {
|
|
248
|
+
authorization: `Bearer ${this.token}`,
|
|
249
|
+
accept: "application/json"
|
|
250
|
+
};
|
|
251
|
+
const init = {
|
|
252
|
+
method,
|
|
253
|
+
headers
|
|
254
|
+
};
|
|
255
|
+
if (body !== void 0) {
|
|
256
|
+
headers["content-type"] = "application/json";
|
|
257
|
+
init.body = JSON.stringify(body);
|
|
258
|
+
}
|
|
259
|
+
const res = await this.fetchImpl(`${this.baseUrl}${path}`, init);
|
|
260
|
+
if (!res.ok) {
|
|
261
|
+
const text = await res.text().catch(() => "");
|
|
262
|
+
throw new ProviderApiError(`Cloudflare ${method} ${path} → ${res.status}`, res.status, text);
|
|
263
|
+
}
|
|
264
|
+
const envelope = await res.json();
|
|
265
|
+
if (!envelope.success) throw new ProviderApiError(`Cloudflare ${method} ${path} returned success:false`, 0, JSON.stringify(envelope.errors));
|
|
266
|
+
return envelope.result;
|
|
267
|
+
}
|
|
268
|
+
async resolveZone(domain) {
|
|
269
|
+
const cached = this.zoneCache.get(domain);
|
|
270
|
+
if (cached) return cached;
|
|
271
|
+
const parts = domain.split(".");
|
|
272
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
273
|
+
const candidate = parts.slice(i).join(".");
|
|
274
|
+
const zones = await this.zones.list({ name: candidate });
|
|
275
|
+
if (zones.length > 0) {
|
|
276
|
+
this.zoneCache.set(domain, zones[0].id);
|
|
277
|
+
return zones[0].id;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
throw new ProviderApiError(`No Cloudflare zone found for domain: ${domain}`, 404, void 0);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
function hostnameToScriptName(hostname) {
|
|
284
|
+
return hostname.replace(/\./g, "-") + "-proxy";
|
|
285
|
+
}
|
|
286
|
+
function generateProxyScript(config) {
|
|
287
|
+
const targetJson = JSON.stringify(config.target);
|
|
288
|
+
const headersJson = JSON.stringify(config.headers);
|
|
289
|
+
return [
|
|
290
|
+
`export default {`,
|
|
291
|
+
` async fetch(request) {`,
|
|
292
|
+
` const url = new URL(request.url);`,
|
|
293
|
+
` const target = new URL(url.pathname + url.search, ${targetJson});`,
|
|
294
|
+
` const headers = new Headers(request.headers);`,
|
|
295
|
+
` for (const [k, v] of Object.entries(${headersJson})) headers.set(k, v);`,
|
|
296
|
+
` return fetch(target, { method: request.method, headers, body: request.body, redirect: "follow" });`,
|
|
297
|
+
` },`,
|
|
298
|
+
`};`
|
|
299
|
+
].join("\n");
|
|
300
|
+
}
|
|
301
|
+
//#endregion
|
|
185
302
|
//#region src/verify-token.ts
|
|
186
303
|
async function verifyToken(token, opts = {}) {
|
|
187
304
|
const client = createCloudflareClient({
|
|
@@ -226,16 +343,28 @@ function deployment(ctx) {
|
|
|
226
343
|
if (!ctx.options.accountId) throw new Error("Cloudflare accountId is required for Pages deployments — set accountId in the plugin options or CLOUDFLARE_ACCOUNT_ID env var");
|
|
227
344
|
return new CloudflareDeployment(ctx.client, ctx.options.accountId);
|
|
228
345
|
}
|
|
346
|
+
function workersCapability(ctx) {
|
|
347
|
+
if (!ctx.options.accountId) throw new Error("Cloudflare accountId is required for Workers — set accountId in the plugin options or CLOUDFLARE_ACCOUNT_ID env var");
|
|
348
|
+
const token = resolveToken(ctx.options);
|
|
349
|
+
return new CloudflareWorkers(ctx.client.zones, ctx.options.accountId, {
|
|
350
|
+
token,
|
|
351
|
+
baseUrl: ctx.options.baseUrl,
|
|
352
|
+
fetch: ctx.options.fetch
|
|
353
|
+
});
|
|
354
|
+
}
|
|
229
355
|
function createPlugin(options = {}) {
|
|
230
356
|
const ctx = createContext(options);
|
|
231
357
|
return {
|
|
232
358
|
name: "@theholocron/holocron-plugin-cloudflare",
|
|
233
359
|
capabilities: {
|
|
234
360
|
dns: () => dns(ctx),
|
|
235
|
-
...ctx.options.accountId ? {
|
|
361
|
+
...ctx.options.accountId ? {
|
|
362
|
+
deployment: () => deployment(ctx),
|
|
363
|
+
workers: () => workersCapability(ctx)
|
|
364
|
+
} : {}
|
|
236
365
|
}
|
|
237
366
|
};
|
|
238
367
|
}
|
|
239
368
|
const AUTH_HINT = "create an API token at https://dash.cloudflare.com/profile/api-tokens with Zone:Read, DNS:Edit, and Cloudflare Pages:Edit permissions, then run: holocron auth set cloudflare <TOKEN>";
|
|
240
369
|
//#endregion
|
|
241
|
-
export { AUTH_HINT, AuthError, CloudflareDeployment, CloudflareDns, createCloudflareClient, createContext, createPlugin, deployment, dns, resolveToken, verifyToken };
|
|
370
|
+
export { AUTH_HINT, AuthError, CloudflareDeployment, CloudflareDns, CloudflareWorkers, createCloudflareClient, createContext, createPlugin, deployment, dns, resolveToken, verifyToken, workersCapability };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/holocron-plugin-cloudflare",
|
|
3
|
-
"version": "3.45.
|
|
3
|
+
"version": "3.45.1",
|
|
4
4
|
"description": "Holocron plugin for Cloudflare. Implements the dns capability against Cloudflare's REST API — zone resolution, DNS record management.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
],
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@theholocron/cloudflare-client": "^1.12.0",
|
|
34
|
-
"@theholocron/eslint-config": "^7.
|
|
35
|
-
"@theholocron/tsconfig": "^7.
|
|
36
|
-
"@theholocron/tsdown-config": "^7.
|
|
37
|
-
"@theholocron/vitest-config": "^7.
|
|
34
|
+
"@theholocron/eslint-config": "^7.29.0",
|
|
35
|
+
"@theholocron/tsconfig": "^7.29.0",
|
|
36
|
+
"@theholocron/tsdown-config": "^7.29.0",
|
|
37
|
+
"@theholocron/vitest-config": "^7.29.0",
|
|
38
38
|
"@types/node": "^26",
|
|
39
39
|
"@vitest/coverage-v8": "^4.1.10",
|
|
40
40
|
"@vitest/eslint-plugin": "^1.6.27",
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"tsx": "4.23.12",
|
|
47
47
|
"typescript": "^5.9.3",
|
|
48
48
|
"vitest": "^4.1.10",
|
|
49
|
-
"@theholocron/cli": "3.45.
|
|
49
|
+
"@theholocron/cli": "3.45.1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"@theholocron/cloudflare-client": "^1.12.0",
|
|
53
|
-
"@theholocron/cli": "3.45.
|
|
53
|
+
"@theholocron/cli": "3.45.1"
|
|
54
54
|
},
|
|
55
55
|
"peerDependenciesMeta": {
|
|
56
56
|
"@theholocron/cloudflare-client": {}
|