openwrangler 0.0.2 → 0.0.3
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.mjs +390 -3
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,398 @@
|
|
|
1
|
+
class CloudflareAPIClient {
|
|
2
|
+
baseUrl = "https://api.cloudflare.com/client/v4";
|
|
3
|
+
accountId;
|
|
4
|
+
apiToken;
|
|
5
|
+
constructor(accountId, apiToken) {
|
|
6
|
+
this.accountId = accountId;
|
|
7
|
+
this.apiToken = apiToken;
|
|
8
|
+
}
|
|
9
|
+
async request(method, endpoint, body, headers) {
|
|
10
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
11
|
+
const response = await fetch(url, {
|
|
12
|
+
method,
|
|
13
|
+
headers: {
|
|
14
|
+
"Authorization": `Bearer ${this.apiToken}`,
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
...headers
|
|
17
|
+
},
|
|
18
|
+
body: body ? JSON.stringify(body) : void 0
|
|
19
|
+
});
|
|
20
|
+
const data = await response.json();
|
|
21
|
+
if (!response.ok || !data.success) {
|
|
22
|
+
const errorMessage = data.errors?.[0]?.message || `HTTP ${response.status}`;
|
|
23
|
+
throw new Error(`Cloudflare API Error: ${errorMessage}`);
|
|
24
|
+
}
|
|
25
|
+
return data.result;
|
|
26
|
+
}
|
|
27
|
+
async get(endpoint, headers) {
|
|
28
|
+
return this.request("GET", endpoint, void 0, headers);
|
|
29
|
+
}
|
|
30
|
+
async post(endpoint, body, headers) {
|
|
31
|
+
return this.request("POST", endpoint, body, headers);
|
|
32
|
+
}
|
|
33
|
+
async put(endpoint, body, headers) {
|
|
34
|
+
return this.request("PUT", endpoint, body, headers);
|
|
35
|
+
}
|
|
36
|
+
async delete(endpoint, body, headers) {
|
|
37
|
+
return this.request("DELETE", endpoint, body, headers);
|
|
38
|
+
}
|
|
39
|
+
getAccountId() {
|
|
40
|
+
return this.accountId;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function createKVBinding$1(client, namespaceId) {
|
|
45
|
+
const baseEndpoint = `/accounts/${client.getAccountId()}/storage/kv/namespaces/${namespaceId}`;
|
|
46
|
+
async function getValue(key, type, cacheTtl) {
|
|
47
|
+
try {
|
|
48
|
+
const url = `${baseEndpoint}/values/${encodeURIComponent(key)}`;
|
|
49
|
+
const headers = {};
|
|
50
|
+
if (cacheTtl !== void 0) {
|
|
51
|
+
headers["Cache-Control"] = `max-age=${cacheTtl}`;
|
|
52
|
+
}
|
|
53
|
+
const response = await fetch(`https://api.cloudflare.com/client/v4${url}`, {
|
|
54
|
+
method: "GET",
|
|
55
|
+
headers: {
|
|
56
|
+
"Authorization": `Bearer ${client.apiToken}`,
|
|
57
|
+
...headers
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
if (response.status === 404) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(`KV GET failed: ${response.status}`);
|
|
65
|
+
}
|
|
66
|
+
switch (type) {
|
|
67
|
+
case "json":
|
|
68
|
+
return await response.json();
|
|
69
|
+
case "arrayBuffer":
|
|
70
|
+
return await response.arrayBuffer();
|
|
71
|
+
case "stream":
|
|
72
|
+
return response.body;
|
|
73
|
+
case "text":
|
|
74
|
+
default:
|
|
75
|
+
return await response.text();
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error?.message?.includes("404")) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function getValueWithMetadata(key, type) {
|
|
85
|
+
try {
|
|
86
|
+
const url = `${baseEndpoint}/values/${encodeURIComponent(key)}`;
|
|
87
|
+
const response = await fetch(`https://api.cloudflare.com/client/v4${url}`, {
|
|
88
|
+
method: "GET",
|
|
89
|
+
headers: {
|
|
90
|
+
"Authorization": `Bearer ${client.apiToken}`
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
if (response.status === 404) {
|
|
94
|
+
return { value: null, metadata: null, cacheStatus: null };
|
|
95
|
+
}
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
throw new Error(`KV GET failed: ${response.status}`);
|
|
98
|
+
}
|
|
99
|
+
const metadata = response.headers.get("cf-kv-metadata");
|
|
100
|
+
const parsedMetadata = metadata ? JSON.parse(metadata) : null;
|
|
101
|
+
let value;
|
|
102
|
+
switch (type) {
|
|
103
|
+
case "json":
|
|
104
|
+
value = await response.json();
|
|
105
|
+
break;
|
|
106
|
+
case "arrayBuffer":
|
|
107
|
+
value = await response.arrayBuffer();
|
|
108
|
+
break;
|
|
109
|
+
case "stream":
|
|
110
|
+
value = response.body;
|
|
111
|
+
break;
|
|
112
|
+
case "text":
|
|
113
|
+
default:
|
|
114
|
+
value = await response.text();
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
value,
|
|
118
|
+
metadata: parsedMetadata,
|
|
119
|
+
cacheStatus: response.headers.get("cf-cache-status")
|
|
120
|
+
};
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (error?.message?.includes("404")) {
|
|
123
|
+
return { value: null, metadata: null, cacheStatus: null };
|
|
124
|
+
}
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const kvNamespace = {
|
|
129
|
+
async get(key, options) {
|
|
130
|
+
if (Array.isArray(key)) {
|
|
131
|
+
const type2 = typeof options === "string" ? options : options?.type || "text";
|
|
132
|
+
const results = /* @__PURE__ */ new Map();
|
|
133
|
+
for (const k of key) {
|
|
134
|
+
const value = await getValue(k, type2, options?.cacheTtl);
|
|
135
|
+
results.set(k, value);
|
|
136
|
+
}
|
|
137
|
+
return results;
|
|
138
|
+
}
|
|
139
|
+
const type = typeof options === "string" ? options : options?.type;
|
|
140
|
+
return getValue(key, type, options?.cacheTtl);
|
|
141
|
+
},
|
|
142
|
+
async put(key, value, options) {
|
|
143
|
+
const url = `${baseEndpoint}/values/${encodeURIComponent(key)}`;
|
|
144
|
+
const headers = {
|
|
145
|
+
"Authorization": `Bearer ${client.apiToken}`
|
|
146
|
+
};
|
|
147
|
+
let body;
|
|
148
|
+
if (typeof value === "string") {
|
|
149
|
+
body = value;
|
|
150
|
+
} else if (value instanceof ArrayBuffer) {
|
|
151
|
+
body = value;
|
|
152
|
+
} else if (ArrayBuffer.isView(value)) {
|
|
153
|
+
body = value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
|
|
154
|
+
} else if (value instanceof ReadableStream) {
|
|
155
|
+
const reader = value.getReader();
|
|
156
|
+
const chunks = [];
|
|
157
|
+
while (true) {
|
|
158
|
+
const { done, value: chunk } = await reader.read();
|
|
159
|
+
if (done) break;
|
|
160
|
+
chunks.push(chunk);
|
|
161
|
+
}
|
|
162
|
+
const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
163
|
+
const result = new Uint8Array(totalLength);
|
|
164
|
+
let offset = 0;
|
|
165
|
+
for (const chunk of chunks) {
|
|
166
|
+
result.set(chunk, offset);
|
|
167
|
+
offset += chunk.length;
|
|
168
|
+
}
|
|
169
|
+
body = result.buffer;
|
|
170
|
+
} else {
|
|
171
|
+
throw new Error("Unsupported value type");
|
|
172
|
+
}
|
|
173
|
+
const queryParams = new URLSearchParams();
|
|
174
|
+
if (options?.expiration) {
|
|
175
|
+
queryParams.set("expiration", options.expiration.toString());
|
|
176
|
+
}
|
|
177
|
+
if (options?.expirationTtl) {
|
|
178
|
+
queryParams.set("expiration_ttl", options.expirationTtl.toString());
|
|
179
|
+
}
|
|
180
|
+
const queryString = queryParams.toString();
|
|
181
|
+
const fullUrl = queryString ? `${url}?${queryString}` : url;
|
|
182
|
+
const response = await fetch(`https://api.cloudflare.com/client/v4${fullUrl}`, {
|
|
183
|
+
method: "PUT",
|
|
184
|
+
headers,
|
|
185
|
+
body
|
|
186
|
+
});
|
|
187
|
+
if (!response.ok) {
|
|
188
|
+
throw new Error(`KV PUT failed: ${response.status}`);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
async delete(key) {
|
|
192
|
+
const url = `${baseEndpoint}/values/${encodeURIComponent(key)}`;
|
|
193
|
+
const response = await fetch(`https://api.cloudflare.com/client/v4${url}`, {
|
|
194
|
+
method: "DELETE",
|
|
195
|
+
headers: {
|
|
196
|
+
"Authorization": `Bearer ${client.apiToken}`
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
if (!response.ok && response.status !== 404) {
|
|
200
|
+
throw new Error(`KV DELETE failed: ${response.status}`);
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
async deleteBulk(keys) {
|
|
204
|
+
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
205
|
+
const url = `${baseEndpoint}/bulk`;
|
|
206
|
+
const response = await fetch(`https://api.cloudflare.com/client/v4${url}`, {
|
|
207
|
+
method: "DELETE",
|
|
208
|
+
headers: {
|
|
209
|
+
"Authorization": `Bearer ${client.apiToken}`,
|
|
210
|
+
"Content-Type": "application/json"
|
|
211
|
+
},
|
|
212
|
+
body: JSON.stringify(keyArray)
|
|
213
|
+
});
|
|
214
|
+
if (!response.ok) {
|
|
215
|
+
throw new Error(`KV DELETE BULK failed: ${response.status}`);
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
async list(options) {
|
|
219
|
+
const queryParams = new URLSearchParams();
|
|
220
|
+
if (options?.limit) {
|
|
221
|
+
queryParams.set("limit", options.limit.toString());
|
|
222
|
+
}
|
|
223
|
+
if (options?.prefix) {
|
|
224
|
+
queryParams.set("prefix", options.prefix);
|
|
225
|
+
}
|
|
226
|
+
if (options?.cursor) {
|
|
227
|
+
queryParams.set("cursor", options.cursor);
|
|
228
|
+
}
|
|
229
|
+
const queryString = queryParams.toString();
|
|
230
|
+
const url = queryString ? `${baseEndpoint}/keys?${queryString}` : `${baseEndpoint}/keys`;
|
|
231
|
+
const data = await client.get(url);
|
|
232
|
+
if (data.list_complete) {
|
|
233
|
+
return {
|
|
234
|
+
list_complete: true,
|
|
235
|
+
keys: data.keys.map((k) => ({
|
|
236
|
+
name: k.name,
|
|
237
|
+
expiration: k.expiration,
|
|
238
|
+
metadata: k.metadata
|
|
239
|
+
})),
|
|
240
|
+
cacheStatus: null
|
|
241
|
+
};
|
|
242
|
+
} else {
|
|
243
|
+
return {
|
|
244
|
+
list_complete: false,
|
|
245
|
+
keys: data.keys.map((k) => ({
|
|
246
|
+
name: k.name,
|
|
247
|
+
expiration: k.expiration,
|
|
248
|
+
metadata: k.metadata
|
|
249
|
+
})),
|
|
250
|
+
cursor: data.cursor,
|
|
251
|
+
cacheStatus: null
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
async getWithMetadata(key, options) {
|
|
256
|
+
if (Array.isArray(key)) {
|
|
257
|
+
const type2 = typeof options === "string" ? options : options?.type || "text";
|
|
258
|
+
const results = /* @__PURE__ */ new Map();
|
|
259
|
+
for (const k of key) {
|
|
260
|
+
const result = await getValueWithMetadata(k, type2);
|
|
261
|
+
results.set(k, result);
|
|
262
|
+
}
|
|
263
|
+
return results;
|
|
264
|
+
}
|
|
265
|
+
const type = typeof options === "string" ? options : options?.type;
|
|
266
|
+
return getValueWithMetadata(key, type);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
return kvNamespace;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
class D1PreparedStatementImpl {
|
|
273
|
+
constructor(query, client, databaseId) {
|
|
274
|
+
this.query = query;
|
|
275
|
+
this.client = client;
|
|
276
|
+
this.databaseId = databaseId;
|
|
277
|
+
}
|
|
278
|
+
bindings = [];
|
|
279
|
+
bind(...values) {
|
|
280
|
+
this.bindings = values;
|
|
281
|
+
return this;
|
|
282
|
+
}
|
|
283
|
+
async run() {
|
|
284
|
+
const endpoint = `/accounts/${this.client.getAccountId()}/d1/database/${this.databaseId}/query`;
|
|
285
|
+
const data = await this.client.post(endpoint, {
|
|
286
|
+
sql: this.query,
|
|
287
|
+
params: this.bindings
|
|
288
|
+
});
|
|
289
|
+
const result = data[0];
|
|
290
|
+
return {
|
|
291
|
+
success: result.success,
|
|
292
|
+
results: result.results,
|
|
293
|
+
meta: {
|
|
294
|
+
served_by: result.meta.served_by || "",
|
|
295
|
+
duration: result.meta.duration || 0,
|
|
296
|
+
changes: result.meta.changes || 0,
|
|
297
|
+
last_row_id: result.meta.last_row_id || 0,
|
|
298
|
+
changed_db: result.meta.changed_db || false,
|
|
299
|
+
size_after: result.meta.size_after || 0,
|
|
300
|
+
rows_read: result.meta.rows_read || 0,
|
|
301
|
+
rows_written: result.meta.rows_written || 0
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
async all() {
|
|
306
|
+
return this.run();
|
|
307
|
+
}
|
|
308
|
+
async first(colName) {
|
|
309
|
+
const result = await this.run();
|
|
310
|
+
if (!result.results || result.results.length === 0) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
const firstRow = result.results[0];
|
|
314
|
+
if (colName && typeof firstRow === "object" && firstRow !== null) {
|
|
315
|
+
return firstRow[colName] ?? null;
|
|
316
|
+
}
|
|
317
|
+
return firstRow;
|
|
318
|
+
}
|
|
319
|
+
async raw(options) {
|
|
320
|
+
const result = await this.run();
|
|
321
|
+
if (!result.results || result.results.length === 0) {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
const raw = result.results.map((row) => {
|
|
325
|
+
if (typeof row === "object" && row !== null) {
|
|
326
|
+
return Object.values(row);
|
|
327
|
+
}
|
|
328
|
+
return row;
|
|
329
|
+
});
|
|
330
|
+
if (options?.columnNames && result.results.length > 0) {
|
|
331
|
+
const firstRow = result.results[0];
|
|
332
|
+
if (typeof firstRow === "object" && firstRow !== null) {
|
|
333
|
+
const columnNames = Object.keys(firstRow);
|
|
334
|
+
return [columnNames, ...raw];
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return raw;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function createD1Binding$1(client, databaseId) {
|
|
341
|
+
return {
|
|
342
|
+
prepare(query) {
|
|
343
|
+
return new D1PreparedStatementImpl(query, client, databaseId);
|
|
344
|
+
},
|
|
345
|
+
async batch(statements) {
|
|
346
|
+
const endpoint = `/accounts/${client.getAccountId()}/d1/database/${databaseId}/query`;
|
|
347
|
+
const queries = statements.map((stmt) => {
|
|
348
|
+
const impl = stmt;
|
|
349
|
+
return {
|
|
350
|
+
sql: impl.query,
|
|
351
|
+
params: impl.bindings
|
|
352
|
+
};
|
|
353
|
+
});
|
|
354
|
+
const data = await client.post(endpoint, queries);
|
|
355
|
+
return data.map((result) => ({
|
|
356
|
+
success: result.success,
|
|
357
|
+
results: result.results,
|
|
358
|
+
meta: {
|
|
359
|
+
served_by: result.meta.served_by || "",
|
|
360
|
+
duration: result.meta.duration || 0,
|
|
361
|
+
changes: result.meta.changes || 0,
|
|
362
|
+
last_row_id: result.meta.last_row_id || 0,
|
|
363
|
+
changed_db: result.meta.changed_db || false,
|
|
364
|
+
size_after: result.meta.size_after || 0,
|
|
365
|
+
rows_read: result.meta.rows_read || 0,
|
|
366
|
+
rows_written: result.meta.rows_written || 0
|
|
367
|
+
}
|
|
368
|
+
}));
|
|
369
|
+
},
|
|
370
|
+
async exec(query) {
|
|
371
|
+
const endpoint = `/accounts/${client.getAccountId()}/d1/database/${databaseId}/query`;
|
|
372
|
+
const data = await client.post(endpoint, {
|
|
373
|
+
sql: query
|
|
374
|
+
});
|
|
375
|
+
return {
|
|
376
|
+
count: data.length,
|
|
377
|
+
duration: data.reduce((acc, r) => acc + (r.meta.duration || 0), 0)
|
|
378
|
+
};
|
|
379
|
+
},
|
|
380
|
+
dump() {
|
|
381
|
+
throw new Error("D1 dump() is deprecated and not supported");
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
1
386
|
function createR2Binding(config, bucketName) {
|
|
2
|
-
throw new Error("
|
|
387
|
+
throw new Error("R2 binding not yet implemented. Use KV or D1 instead.");
|
|
3
388
|
}
|
|
4
389
|
function createKVBinding(config, namespaceId) {
|
|
5
|
-
|
|
390
|
+
const client = new CloudflareAPIClient(config.accountId, config.apiToken);
|
|
391
|
+
return createKVBinding$1(client, namespaceId);
|
|
6
392
|
}
|
|
7
393
|
function createD1Binding(config, databaseId) {
|
|
8
|
-
|
|
394
|
+
const client = new CloudflareAPIClient(config.accountId, config.apiToken);
|
|
395
|
+
return createD1Binding$1(client, databaseId);
|
|
9
396
|
}
|
|
10
397
|
|
|
11
398
|
export { createD1Binding, createKVBinding, createR2Binding };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openwrangler",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"typescript": "^5.7.2",
|
|
20
20
|
"unbuild": "^3.3.1",
|
|
21
21
|
"wrangler": "^3.99.0",
|
|
22
|
-
"@binochoi/nitro-cloudflare-dev": "npm:@bino0216/nitro-cloudflare-dev@0.2.
|
|
22
|
+
"@binochoi/nitro-cloudflare-dev": "npm:@bino0216/nitro-cloudflare-dev@0.2.5"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"dev": "nuxi dev playground",
|