@aigne/afs-cloudflare-cost 1.11.0-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +26 -0
- package/dist/adapter.cjs +668 -0
- package/dist/adapter.d.cts +42 -0
- package/dist/adapter.d.cts.map +1 -0
- package/dist/adapter.d.mts +42 -0
- package/dist/adapter.d.mts.map +1 -0
- package/dist/adapter.mjs +669 -0
- package/dist/adapter.mjs.map +1 -0
- package/dist/index.cjs +100 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +33 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +94 -0
- package/dist/index.mjs.map +1 -0
- package/dist/pricing.cjs +93 -0
- package/dist/pricing.mjs +89 -0
- package/dist/pricing.mjs.map +1 -0
- package/package.json +58 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 ArcBlock, Inc. All Rights Reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are proprietary
|
|
6
|
+
and confidential. Unauthorized copying, modification, distribution, or use of
|
|
7
|
+
this Software, via any medium, is strictly prohibited.
|
|
8
|
+
|
|
9
|
+
The Software is provided for internal use only within ArcBlock, Inc. and its
|
|
10
|
+
authorized affiliates.
|
|
11
|
+
|
|
12
|
+
## No License Granted
|
|
13
|
+
|
|
14
|
+
No license, express or implied, is granted to any party for any purpose.
|
|
15
|
+
All rights are reserved by ArcBlock, Inc.
|
|
16
|
+
|
|
17
|
+
## Public Artifact Distribution
|
|
18
|
+
|
|
19
|
+
Portions of this Software may be released publicly under separate open-source
|
|
20
|
+
licenses (such as MIT License) through designated public repositories. Such
|
|
21
|
+
public releases are governed by their respective licenses and do not affect
|
|
22
|
+
the proprietary nature of this repository.
|
|
23
|
+
|
|
24
|
+
## Contact
|
|
25
|
+
|
|
26
|
+
For licensing inquiries, contact: legal@arcblock.io
|
package/dist/adapter.cjs
ADDED
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
const require_pricing = require('./pricing.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/adapter.ts
|
|
4
|
+
const GRAPHQL_ENDPOINT = "https://api.cloudflare.com/client/v4/graphql";
|
|
5
|
+
function buildWorkersQuery(accountTag, startDate, endDate) {
|
|
6
|
+
return `{
|
|
7
|
+
viewer {
|
|
8
|
+
accounts(filter: {accountTag: "${accountTag}"}) {
|
|
9
|
+
workersInvocationsAdaptive(
|
|
10
|
+
filter: {datetime_geq: "${startDate}T00:00:00Z", datetime_leq: "${endDate}T23:59:59Z"}
|
|
11
|
+
limit: 10000
|
|
12
|
+
) {
|
|
13
|
+
dimensions { datetime scriptName }
|
|
14
|
+
sum { requests errors subrequests cpuTimeUs }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}`;
|
|
19
|
+
}
|
|
20
|
+
function buildPagesFunctionsQuery(accountTag, startDate, endDate) {
|
|
21
|
+
return `{
|
|
22
|
+
viewer {
|
|
23
|
+
accounts(filter: {accountTag: "${accountTag}"}) {
|
|
24
|
+
pagesFunctionsInvocationsAdaptiveGroups(
|
|
25
|
+
filter: {datetime_geq: "${startDate}T00:00:00Z", datetime_leq: "${endDate}T23:59:59Z"}
|
|
26
|
+
limit: 10000
|
|
27
|
+
) {
|
|
28
|
+
dimensions { datetime scriptName }
|
|
29
|
+
sum { requests errors }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}`;
|
|
34
|
+
}
|
|
35
|
+
function buildR2StorageQuery(accountTag, startDate, endDate) {
|
|
36
|
+
return `{
|
|
37
|
+
viewer {
|
|
38
|
+
accounts(filter: {accountTag: "${accountTag}"}) {
|
|
39
|
+
r2StorageAdaptiveGroups(
|
|
40
|
+
filter: {datetime_geq: "${startDate}T00:00:00Z", datetime_leq: "${endDate}T23:59:59Z"}
|
|
41
|
+
limit: 10000
|
|
42
|
+
) {
|
|
43
|
+
dimensions { datetime }
|
|
44
|
+
max { payloadSize }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}`;
|
|
49
|
+
}
|
|
50
|
+
function buildR2OpsQuery(accountTag, startDate, endDate) {
|
|
51
|
+
return `{
|
|
52
|
+
viewer {
|
|
53
|
+
accounts(filter: {accountTag: "${accountTag}"}) {
|
|
54
|
+
r2OperationsAdaptiveGroups(
|
|
55
|
+
filter: {datetime_geq: "${startDate}T00:00:00Z", datetime_leq: "${endDate}T23:59:59Z"}
|
|
56
|
+
limit: 10000
|
|
57
|
+
) {
|
|
58
|
+
dimensions { datetime actionType }
|
|
59
|
+
sum { requests }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}`;
|
|
64
|
+
}
|
|
65
|
+
function buildD1AnalyticsQuery(accountTag, startDate, endDate) {
|
|
66
|
+
return `{
|
|
67
|
+
viewer {
|
|
68
|
+
accounts(filter: {accountTag: "${accountTag}"}) {
|
|
69
|
+
d1AnalyticsAdaptiveGroups(
|
|
70
|
+
filter: {datetime_geq: "${startDate}T00:00:00Z", datetime_leq: "${endDate}T23:59:59Z"}
|
|
71
|
+
limit: 10000
|
|
72
|
+
) {
|
|
73
|
+
dimensions { datetime }
|
|
74
|
+
sum { readQueries writeQueries }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}`;
|
|
79
|
+
}
|
|
80
|
+
function buildD1StorageQuery(accountTag, startDate, endDate) {
|
|
81
|
+
return `{
|
|
82
|
+
viewer {
|
|
83
|
+
accounts(filter: {accountTag: "${accountTag}"}) {
|
|
84
|
+
d1StorageAdaptiveGroups(
|
|
85
|
+
filter: {date_geq: "${startDate}", date_leq: "${endDate}"}
|
|
86
|
+
limit: 10000
|
|
87
|
+
) {
|
|
88
|
+
dimensions { date }
|
|
89
|
+
max { databaseSizeBytes }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}`;
|
|
94
|
+
}
|
|
95
|
+
function buildKVOpsQuery(accountTag, startDate, endDate) {
|
|
96
|
+
return `{
|
|
97
|
+
viewer {
|
|
98
|
+
accounts(filter: {accountTag: "${accountTag}"}) {
|
|
99
|
+
kvOperationsAdaptiveGroups(
|
|
100
|
+
filter: {datetime_geq: "${startDate}T00:00:00Z", datetime_leq: "${endDate}T23:59:59Z"}
|
|
101
|
+
limit: 10000
|
|
102
|
+
) {
|
|
103
|
+
dimensions { datetime actionType }
|
|
104
|
+
sum { requests }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}`;
|
|
109
|
+
}
|
|
110
|
+
function buildKVStorageQuery(accountTag, startDate, endDate) {
|
|
111
|
+
return `{
|
|
112
|
+
viewer {
|
|
113
|
+
accounts(filter: {accountTag: "${accountTag}"}) {
|
|
114
|
+
kvStorageAdaptiveGroups(
|
|
115
|
+
filter: {datetime_geq: "${startDate}T00:00:00Z", datetime_leq: "${endDate}T23:59:59Z"}
|
|
116
|
+
limit: 10000
|
|
117
|
+
) {
|
|
118
|
+
dimensions { datetime }
|
|
119
|
+
max { byteCount }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}`;
|
|
124
|
+
}
|
|
125
|
+
function buildDOInvocationsQuery(accountTag, startDate, endDate) {
|
|
126
|
+
return `{
|
|
127
|
+
viewer {
|
|
128
|
+
accounts(filter: {accountTag: "${accountTag}"}) {
|
|
129
|
+
durableObjectsInvocationsAdaptiveGroups(
|
|
130
|
+
filter: {datetime_geq: "${startDate}T00:00:00Z", datetime_leq: "${endDate}T23:59:59Z"}
|
|
131
|
+
limit: 10000
|
|
132
|
+
) {
|
|
133
|
+
dimensions { datetime }
|
|
134
|
+
sum { requests wallTime }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}`;
|
|
139
|
+
}
|
|
140
|
+
function buildDOStorageQuery(accountTag, startDate, endDate) {
|
|
141
|
+
return `{
|
|
142
|
+
viewer {
|
|
143
|
+
accounts(filter: {accountTag: "${accountTag}"}) {
|
|
144
|
+
durableObjectsStorageGroups(
|
|
145
|
+
filter: {date_geq: "${startDate}", date_leq: "${endDate}"}
|
|
146
|
+
limit: 10000
|
|
147
|
+
) {
|
|
148
|
+
dimensions { date }
|
|
149
|
+
max { storedBytes }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}`;
|
|
154
|
+
}
|
|
155
|
+
/** Extract date (YYYY-MM-DD) from a datetime string that may be ISO 8601 or just a date */
|
|
156
|
+
function extractDate(datetime) {
|
|
157
|
+
return datetime ? datetime.slice(0, 10) : "";
|
|
158
|
+
}
|
|
159
|
+
/** Helper to traverse viewer.accounts[0].{datasetName} */
|
|
160
|
+
function getDatasetItems(data, datasetName) {
|
|
161
|
+
const accounts = data.viewer?.accounts ?? [];
|
|
162
|
+
for (const account of accounts) {
|
|
163
|
+
const items = account[datasetName];
|
|
164
|
+
if (items) return items;
|
|
165
|
+
}
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
function extractWorkersUsage(data) {
|
|
169
|
+
const entries = [];
|
|
170
|
+
for (const item of getDatasetItems(data, "workersInvocationsAdaptive")) {
|
|
171
|
+
const dims = item.dimensions;
|
|
172
|
+
const sum = item.sum;
|
|
173
|
+
const date = extractDate(dims?.datetime ?? dims?.date ?? "");
|
|
174
|
+
const scriptName = dims?.scriptName ?? "workers";
|
|
175
|
+
entries.push({
|
|
176
|
+
date,
|
|
177
|
+
service: `workers:${scriptName}`,
|
|
178
|
+
usage: {
|
|
179
|
+
value: sum?.requests ?? 0,
|
|
180
|
+
unit: "requests"
|
|
181
|
+
},
|
|
182
|
+
metrics: {
|
|
183
|
+
requests: sum?.requests ?? 0,
|
|
184
|
+
cpuTimeUs: sum?.cpuTimeUs ?? 0
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return entries;
|
|
189
|
+
}
|
|
190
|
+
function extractPagesFunctionsUsage(data) {
|
|
191
|
+
const entries = [];
|
|
192
|
+
for (const item of getDatasetItems(data, "pagesFunctionsInvocationsAdaptiveGroups")) {
|
|
193
|
+
const dims = item.dimensions;
|
|
194
|
+
const sum = item.sum;
|
|
195
|
+
const date = extractDate(dims?.datetime ?? dims?.date ?? "");
|
|
196
|
+
const scriptName = dims?.scriptName ?? "pages";
|
|
197
|
+
entries.push({
|
|
198
|
+
date,
|
|
199
|
+
service: `workers:${scriptName}`,
|
|
200
|
+
usage: {
|
|
201
|
+
value: sum?.requests ?? 0,
|
|
202
|
+
unit: "requests"
|
|
203
|
+
},
|
|
204
|
+
metrics: {
|
|
205
|
+
requests: sum?.requests ?? 0,
|
|
206
|
+
cpuTimeUs: 0
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return entries;
|
|
211
|
+
}
|
|
212
|
+
function extractR2StorageUsage(data) {
|
|
213
|
+
const entries = [];
|
|
214
|
+
for (const item of getDatasetItems(data, "r2StorageAdaptiveGroups")) {
|
|
215
|
+
const dims = item.dimensions;
|
|
216
|
+
const max = item.max;
|
|
217
|
+
const date = extractDate(dims?.datetime ?? dims?.date ?? "");
|
|
218
|
+
const bytes = max?.payloadSize ?? 0;
|
|
219
|
+
entries.push({
|
|
220
|
+
date,
|
|
221
|
+
service: "r2",
|
|
222
|
+
usage: {
|
|
223
|
+
value: bytes,
|
|
224
|
+
unit: "bytes"
|
|
225
|
+
},
|
|
226
|
+
metrics: { storageBytes: bytes }
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return entries;
|
|
230
|
+
}
|
|
231
|
+
const R2_CLASS_A_ACTIONS = new Set([
|
|
232
|
+
"PutObject",
|
|
233
|
+
"CopyObject",
|
|
234
|
+
"CreateMultipartUpload",
|
|
235
|
+
"CompleteMultipartUpload",
|
|
236
|
+
"UploadPart",
|
|
237
|
+
"ListParts",
|
|
238
|
+
"CreateBucket",
|
|
239
|
+
"ListMultipartUploads",
|
|
240
|
+
"UploadPartCopy",
|
|
241
|
+
"AbortMultipartUpload",
|
|
242
|
+
"DeleteObject",
|
|
243
|
+
"DeleteBucket"
|
|
244
|
+
]);
|
|
245
|
+
function extractR2OpsUsage(data) {
|
|
246
|
+
let classAOps = 0;
|
|
247
|
+
let classBOps = 0;
|
|
248
|
+
let latestDate = "";
|
|
249
|
+
for (const item of getDatasetItems(data, "r2OperationsAdaptiveGroups")) {
|
|
250
|
+
const dims = item.dimensions;
|
|
251
|
+
const sum = item.sum;
|
|
252
|
+
const date = extractDate(dims?.datetime ?? dims?.date ?? "");
|
|
253
|
+
if (date > latestDate) latestDate = date;
|
|
254
|
+
const reqs = sum?.requests ?? 0;
|
|
255
|
+
if (R2_CLASS_A_ACTIONS.has(dims?.actionType ?? "")) classAOps += reqs;
|
|
256
|
+
else classBOps += reqs;
|
|
257
|
+
}
|
|
258
|
+
if (classAOps === 0 && classBOps === 0) return [];
|
|
259
|
+
return [{
|
|
260
|
+
date: latestDate,
|
|
261
|
+
service: "r2",
|
|
262
|
+
usage: {
|
|
263
|
+
value: classAOps + classBOps,
|
|
264
|
+
unit: "operations"
|
|
265
|
+
},
|
|
266
|
+
metrics: {
|
|
267
|
+
classAOps,
|
|
268
|
+
classBOps
|
|
269
|
+
}
|
|
270
|
+
}];
|
|
271
|
+
}
|
|
272
|
+
function extractD1AnalyticsUsage(data) {
|
|
273
|
+
const entries = [];
|
|
274
|
+
for (const item of getDatasetItems(data, "d1AnalyticsAdaptiveGroups")) {
|
|
275
|
+
const dims = item.dimensions;
|
|
276
|
+
const sum = item.sum;
|
|
277
|
+
const date = extractDate(dims?.datetime ?? dims?.date ?? "");
|
|
278
|
+
const reads = sum?.readQueries ?? 0;
|
|
279
|
+
const writes = sum?.writeQueries ?? 0;
|
|
280
|
+
entries.push({
|
|
281
|
+
date,
|
|
282
|
+
service: "d1",
|
|
283
|
+
usage: {
|
|
284
|
+
value: reads + writes,
|
|
285
|
+
unit: "rows"
|
|
286
|
+
},
|
|
287
|
+
metrics: {
|
|
288
|
+
rowsRead: reads,
|
|
289
|
+
rowsWritten: writes
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return entries;
|
|
294
|
+
}
|
|
295
|
+
function extractD1StorageUsage(data) {
|
|
296
|
+
let maxBytes = 0;
|
|
297
|
+
let latestDate = "";
|
|
298
|
+
for (const item of getDatasetItems(data, "d1StorageAdaptiveGroups")) {
|
|
299
|
+
const dims = item.dimensions;
|
|
300
|
+
const max = item.max;
|
|
301
|
+
const date = extractDate(dims?.datetime ?? dims?.date ?? "");
|
|
302
|
+
if (date > latestDate) latestDate = date;
|
|
303
|
+
const bytes = max?.databaseSizeBytes ?? 0;
|
|
304
|
+
if (bytes > maxBytes) maxBytes = bytes;
|
|
305
|
+
}
|
|
306
|
+
if (maxBytes === 0 && !latestDate) return [];
|
|
307
|
+
return [{
|
|
308
|
+
date: latestDate,
|
|
309
|
+
service: "d1",
|
|
310
|
+
usage: {
|
|
311
|
+
value: maxBytes,
|
|
312
|
+
unit: "storage-bytes"
|
|
313
|
+
},
|
|
314
|
+
metrics: { storageBytes: maxBytes }
|
|
315
|
+
}];
|
|
316
|
+
}
|
|
317
|
+
function extractKVOpsUsage(data) {
|
|
318
|
+
let reads = 0;
|
|
319
|
+
let writes = 0;
|
|
320
|
+
let deletes = 0;
|
|
321
|
+
let lists = 0;
|
|
322
|
+
let latestDate = "";
|
|
323
|
+
for (const item of getDatasetItems(data, "kvOperationsAdaptiveGroups")) {
|
|
324
|
+
const dims = item.dimensions;
|
|
325
|
+
const sum = item.sum;
|
|
326
|
+
const date = extractDate(dims?.datetime ?? dims?.date ?? "");
|
|
327
|
+
if (date > latestDate) latestDate = date;
|
|
328
|
+
const reqs = sum?.requests ?? 0;
|
|
329
|
+
const action = dims?.actionType ?? "read";
|
|
330
|
+
if (action === "write") writes += reqs;
|
|
331
|
+
else if (action === "delete") deletes += reqs;
|
|
332
|
+
else if (action === "list") lists += reqs;
|
|
333
|
+
else reads += reqs;
|
|
334
|
+
}
|
|
335
|
+
const total = reads + writes + deletes + lists;
|
|
336
|
+
if (total === 0) return [];
|
|
337
|
+
return [{
|
|
338
|
+
date: latestDate,
|
|
339
|
+
service: "kv",
|
|
340
|
+
usage: {
|
|
341
|
+
value: total,
|
|
342
|
+
unit: "operations"
|
|
343
|
+
},
|
|
344
|
+
metrics: {
|
|
345
|
+
reads,
|
|
346
|
+
writes,
|
|
347
|
+
deletes,
|
|
348
|
+
lists
|
|
349
|
+
}
|
|
350
|
+
}];
|
|
351
|
+
}
|
|
352
|
+
function extractKVStorageUsage(data) {
|
|
353
|
+
let maxBytes = 0;
|
|
354
|
+
let latestDate = "";
|
|
355
|
+
for (const item of getDatasetItems(data, "kvStorageAdaptiveGroups")) {
|
|
356
|
+
const dims = item.dimensions;
|
|
357
|
+
const max = item.max;
|
|
358
|
+
const date = extractDate(dims?.datetime ?? dims?.date ?? "");
|
|
359
|
+
if (date > latestDate) latestDate = date;
|
|
360
|
+
const bytes = max?.byteCount ?? 0;
|
|
361
|
+
if (bytes > maxBytes) maxBytes = bytes;
|
|
362
|
+
}
|
|
363
|
+
if (maxBytes === 0 && !latestDate) return [];
|
|
364
|
+
return [{
|
|
365
|
+
date: latestDate,
|
|
366
|
+
service: "kv",
|
|
367
|
+
usage: {
|
|
368
|
+
value: maxBytes,
|
|
369
|
+
unit: "storage-bytes"
|
|
370
|
+
},
|
|
371
|
+
metrics: { storageBytes: maxBytes }
|
|
372
|
+
}];
|
|
373
|
+
}
|
|
374
|
+
function extractDOInvocationsUsage(data) {
|
|
375
|
+
let totalRequests = 0;
|
|
376
|
+
let totalWallTime = 0;
|
|
377
|
+
let latestDate = "";
|
|
378
|
+
for (const item of getDatasetItems(data, "durableObjectsInvocationsAdaptiveGroups")) {
|
|
379
|
+
const dims = item.dimensions;
|
|
380
|
+
const sum = item.sum;
|
|
381
|
+
const date = extractDate(dims?.datetime ?? dims?.date ?? "");
|
|
382
|
+
if (date > latestDate) latestDate = date;
|
|
383
|
+
totalRequests += sum?.requests ?? 0;
|
|
384
|
+
totalWallTime += sum?.wallTime ?? 0;
|
|
385
|
+
}
|
|
386
|
+
if (totalRequests === 0 && totalWallTime === 0) return [];
|
|
387
|
+
return [{
|
|
388
|
+
date: latestDate,
|
|
389
|
+
service: "durable-objects",
|
|
390
|
+
usage: {
|
|
391
|
+
value: totalRequests,
|
|
392
|
+
unit: "requests"
|
|
393
|
+
},
|
|
394
|
+
metrics: {
|
|
395
|
+
requests: totalRequests,
|
|
396
|
+
wallTimeUs: totalWallTime
|
|
397
|
+
}
|
|
398
|
+
}];
|
|
399
|
+
}
|
|
400
|
+
function extractDOStorageUsage(data) {
|
|
401
|
+
let maxBytes = 0;
|
|
402
|
+
let latestDate = "";
|
|
403
|
+
for (const item of getDatasetItems(data, "durableObjectsStorageGroups")) {
|
|
404
|
+
const dims = item.dimensions;
|
|
405
|
+
const max = item.max;
|
|
406
|
+
const date = extractDate(dims?.datetime ?? dims?.date ?? "");
|
|
407
|
+
if (date > latestDate) latestDate = date;
|
|
408
|
+
const bytes = max?.storedBytes ?? 0;
|
|
409
|
+
if (bytes > maxBytes) maxBytes = bytes;
|
|
410
|
+
}
|
|
411
|
+
if (maxBytes === 0 && !latestDate) return [];
|
|
412
|
+
return [{
|
|
413
|
+
date: latestDate,
|
|
414
|
+
service: "durable-objects",
|
|
415
|
+
usage: {
|
|
416
|
+
value: maxBytes,
|
|
417
|
+
unit: "storage-bytes"
|
|
418
|
+
},
|
|
419
|
+
metrics: { storageBytes: maxBytes }
|
|
420
|
+
}];
|
|
421
|
+
}
|
|
422
|
+
function getProductQueries(accountTag, startDate, endDate) {
|
|
423
|
+
return [
|
|
424
|
+
{
|
|
425
|
+
product: "workers",
|
|
426
|
+
query: buildWorkersQuery(accountTag, startDate, endDate),
|
|
427
|
+
extractUsage: extractWorkersUsage
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
product: "pages",
|
|
431
|
+
query: buildPagesFunctionsQuery(accountTag, startDate, endDate),
|
|
432
|
+
extractUsage: extractPagesFunctionsUsage
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
product: "r2-storage",
|
|
436
|
+
query: buildR2StorageQuery(accountTag, startDate, endDate),
|
|
437
|
+
extractUsage: extractR2StorageUsage
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
product: "r2-ops",
|
|
441
|
+
query: buildR2OpsQuery(accountTag, startDate, endDate),
|
|
442
|
+
extractUsage: extractR2OpsUsage
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
product: "d1-analytics",
|
|
446
|
+
query: buildD1AnalyticsQuery(accountTag, startDate, endDate),
|
|
447
|
+
extractUsage: extractD1AnalyticsUsage
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
product: "d1-storage",
|
|
451
|
+
query: buildD1StorageQuery(accountTag, startDate, endDate),
|
|
452
|
+
extractUsage: extractD1StorageUsage
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
product: "kv-ops",
|
|
456
|
+
query: buildKVOpsQuery(accountTag, startDate, endDate),
|
|
457
|
+
extractUsage: extractKVOpsUsage
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
product: "kv-storage",
|
|
461
|
+
query: buildKVStorageQuery(accountTag, startDate, endDate),
|
|
462
|
+
extractUsage: extractKVStorageUsage
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
product: "do-invocations",
|
|
466
|
+
query: buildDOInvocationsQuery(accountTag, startDate, endDate),
|
|
467
|
+
extractUsage: extractDOInvocationsUsage
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
product: "do-storage",
|
|
471
|
+
query: buildDOStorageQuery(accountTag, startDate, endDate),
|
|
472
|
+
extractUsage: extractDOStorageUsage
|
|
473
|
+
}
|
|
474
|
+
];
|
|
475
|
+
}
|
|
476
|
+
function aggregateMetrics(entries) {
|
|
477
|
+
const m = {
|
|
478
|
+
workers: {
|
|
479
|
+
requests: 0,
|
|
480
|
+
cpuTimeUs: 0
|
|
481
|
+
},
|
|
482
|
+
r2: {
|
|
483
|
+
storageBytes: 0,
|
|
484
|
+
classAOps: 0,
|
|
485
|
+
classBOps: 0
|
|
486
|
+
},
|
|
487
|
+
d1: {
|
|
488
|
+
rowsRead: 0,
|
|
489
|
+
rowsWritten: 0,
|
|
490
|
+
storageBytes: 0
|
|
491
|
+
},
|
|
492
|
+
kv: {
|
|
493
|
+
reads: 0,
|
|
494
|
+
writes: 0,
|
|
495
|
+
deletes: 0,
|
|
496
|
+
lists: 0,
|
|
497
|
+
storageBytes: 0
|
|
498
|
+
},
|
|
499
|
+
do: {
|
|
500
|
+
requests: 0,
|
|
501
|
+
wallTimeUs: 0,
|
|
502
|
+
storageBytes: 0
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
for (const e of entries) if (e.service.startsWith("workers:")) {
|
|
506
|
+
m.workers.requests += e.metrics.requests ?? 0;
|
|
507
|
+
m.workers.cpuTimeUs += e.metrics.cpuTimeUs ?? 0;
|
|
508
|
+
} else if (e.service === "r2") {
|
|
509
|
+
if (e.metrics.storageBytes !== void 0) m.r2.storageBytes = Math.max(m.r2.storageBytes, e.metrics.storageBytes);
|
|
510
|
+
m.r2.classAOps += e.metrics.classAOps ?? 0;
|
|
511
|
+
m.r2.classBOps += e.metrics.classBOps ?? 0;
|
|
512
|
+
} else if (e.service === "d1") {
|
|
513
|
+
m.d1.rowsRead += e.metrics.rowsRead ?? 0;
|
|
514
|
+
m.d1.rowsWritten += e.metrics.rowsWritten ?? 0;
|
|
515
|
+
if (e.metrics.storageBytes !== void 0) m.d1.storageBytes = Math.max(m.d1.storageBytes, e.metrics.storageBytes);
|
|
516
|
+
} else if (e.service === "kv") {
|
|
517
|
+
m.kv.reads += e.metrics.reads ?? 0;
|
|
518
|
+
m.kv.writes += e.metrics.writes ?? 0;
|
|
519
|
+
m.kv.deletes += e.metrics.deletes ?? 0;
|
|
520
|
+
m.kv.lists += e.metrics.lists ?? 0;
|
|
521
|
+
if (e.metrics.storageBytes !== void 0) m.kv.storageBytes = Math.max(m.kv.storageBytes, e.metrics.storageBytes);
|
|
522
|
+
} else if (e.service === "durable-objects") {
|
|
523
|
+
m.do.requests += e.metrics.requests ?? 0;
|
|
524
|
+
m.do.wallTimeUs += e.metrics.wallTimeUs ?? 0;
|
|
525
|
+
if (e.metrics.storageBytes !== void 0) m.do.storageBytes = Math.max(m.do.storageBytes, e.metrics.storageBytes);
|
|
526
|
+
}
|
|
527
|
+
return m;
|
|
528
|
+
}
|
|
529
|
+
function estimateTotalCost(metrics) {
|
|
530
|
+
return {
|
|
531
|
+
workers: require_pricing.estimateWorkersCost(metrics.workers.requests, metrics.workers.cpuTimeUs),
|
|
532
|
+
r2: require_pricing.estimateR2Cost(metrics.r2.storageBytes, metrics.r2.classAOps, metrics.r2.classBOps),
|
|
533
|
+
d1: require_pricing.estimateD1Cost(metrics.d1.rowsRead, metrics.d1.rowsWritten, metrics.d1.storageBytes),
|
|
534
|
+
kv: require_pricing.estimateKVCost(metrics.kv.reads, metrics.kv.writes, metrics.kv.deletes, metrics.kv.lists, metrics.kv.storageBytes),
|
|
535
|
+
"durable-objects": require_pricing.estimateDOCost(metrics.do.requests, metrics.do.wallTimeUs, metrics.do.storageBytes)
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
/** Map a service name to its product cost key */
|
|
539
|
+
function serviceToProduct(service) {
|
|
540
|
+
if (service.startsWith("workers:")) return "workers";
|
|
541
|
+
return service;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Distribute monthly product costs proportionally across daily usage entries.
|
|
545
|
+
* Each entry gets: monthlyCost × (entryUsage / totalUsage) for its product.
|
|
546
|
+
*/
|
|
547
|
+
function distributeEstimates(entries, productCosts) {
|
|
548
|
+
const estimates = /* @__PURE__ */ new Map();
|
|
549
|
+
const productTotals = /* @__PURE__ */ new Map();
|
|
550
|
+
for (const e of entries) {
|
|
551
|
+
const product = serviceToProduct(e.service);
|
|
552
|
+
productTotals.set(product, (productTotals.get(product) ?? 0) + e.usage.value);
|
|
553
|
+
}
|
|
554
|
+
for (const e of entries) {
|
|
555
|
+
const product = serviceToProduct(e.service);
|
|
556
|
+
const monthlyCost = productCosts[product] ?? 0;
|
|
557
|
+
const totalUsage = productTotals.get(product) ?? 0;
|
|
558
|
+
if (monthlyCost === 0 || totalUsage === 0) estimates.set(e, 0);
|
|
559
|
+
else {
|
|
560
|
+
const share = Math.round(monthlyCost * (e.usage.value / totalUsage) * 100) / 100;
|
|
561
|
+
estimates.set(e, share);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return estimates;
|
|
565
|
+
}
|
|
566
|
+
var CloudflareCostAdapter = class {
|
|
567
|
+
cloud = "cloudflare";
|
|
568
|
+
config;
|
|
569
|
+
client;
|
|
570
|
+
cachedServices = null;
|
|
571
|
+
constructor(config = {}, client) {
|
|
572
|
+
this.config = config;
|
|
573
|
+
this.client = client;
|
|
574
|
+
}
|
|
575
|
+
async isAvailable() {
|
|
576
|
+
if (!this.config.apiToken || !this.config.accountId) return false;
|
|
577
|
+
try {
|
|
578
|
+
const client = this.getClient();
|
|
579
|
+
if (!client) return false;
|
|
580
|
+
const response = await client.post(GRAPHQL_ENDPOINT, {
|
|
581
|
+
headers: this.buildHeaders(),
|
|
582
|
+
body: JSON.stringify({ query: "{ viewer { accounts { accountTag } } }" })
|
|
583
|
+
});
|
|
584
|
+
return response.status === 200 && !response.data.errors;
|
|
585
|
+
} catch {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async fetchCosts(options) {
|
|
590
|
+
const client = this.getClient();
|
|
591
|
+
if (!client) throw new Error("Cloudflare HTTP client not available");
|
|
592
|
+
const accountId = this.config.accountId;
|
|
593
|
+
if (!accountId) throw new Error("Missing accountId in Cloudflare config");
|
|
594
|
+
const productQueries = getProductQueries(accountId, options.startDate, options.endDate);
|
|
595
|
+
const allEntries = [];
|
|
596
|
+
const headers = this.buildHeaders();
|
|
597
|
+
for (const pq of productQueries) try {
|
|
598
|
+
const response = await client.post(GRAPHQL_ENDPOINT, {
|
|
599
|
+
headers,
|
|
600
|
+
body: JSON.stringify({ query: pq.query })
|
|
601
|
+
});
|
|
602
|
+
if (response.status === 429) throw new Error("Cloudflare API rate limit exceeded");
|
|
603
|
+
if (response.data.errors && response.data.errors.length > 0) continue;
|
|
604
|
+
const data = response.data.data;
|
|
605
|
+
if (!data) continue;
|
|
606
|
+
const usageEntries = pq.extractUsage(data);
|
|
607
|
+
allEntries.push(...usageEntries);
|
|
608
|
+
} catch (error) {
|
|
609
|
+
if (error instanceof Error && error.message.includes("rate limit")) throw error;
|
|
610
|
+
}
|
|
611
|
+
const estimates = distributeEstimates(allEntries, estimateTotalCost(aggregateMetrics(allEntries)));
|
|
612
|
+
const records = [];
|
|
613
|
+
for (const entry of allEntries) {
|
|
614
|
+
if (entry.usage.unit === "storage-bytes") continue;
|
|
615
|
+
const amount = estimates.get(entry) ?? 0;
|
|
616
|
+
records.push({
|
|
617
|
+
cloud: "cloudflare",
|
|
618
|
+
service: entry.service,
|
|
619
|
+
date: entry.date,
|
|
620
|
+
amount: Math.round(amount * 100) / 100,
|
|
621
|
+
currency: "USD",
|
|
622
|
+
usage: entry.usage,
|
|
623
|
+
isEstimate: true
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
this.cachedServices = [...new Set(records.map((r) => r.service))];
|
|
627
|
+
return records;
|
|
628
|
+
}
|
|
629
|
+
async listServices() {
|
|
630
|
+
if (this.cachedServices) return this.cachedServices;
|
|
631
|
+
return [];
|
|
632
|
+
}
|
|
633
|
+
capabilities() {
|
|
634
|
+
return {
|
|
635
|
+
supportsAmount: true,
|
|
636
|
+
supportsUsage: true,
|
|
637
|
+
supportsTags: false,
|
|
638
|
+
supportsRegion: false,
|
|
639
|
+
supportsAccount: false,
|
|
640
|
+
supportsForecast: false
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
getClient() {
|
|
644
|
+
if (this.client) return this.client;
|
|
645
|
+
this.client = { async post(url, options) {
|
|
646
|
+
const resp = await fetch(url, {
|
|
647
|
+
method: "POST",
|
|
648
|
+
headers: options.headers,
|
|
649
|
+
body: options.body
|
|
650
|
+
});
|
|
651
|
+
const data = await resp.json();
|
|
652
|
+
return {
|
|
653
|
+
status: resp.status,
|
|
654
|
+
data
|
|
655
|
+
};
|
|
656
|
+
} };
|
|
657
|
+
return this.client;
|
|
658
|
+
}
|
|
659
|
+
buildHeaders() {
|
|
660
|
+
return {
|
|
661
|
+
Authorization: `Bearer ${this.config.apiToken}`,
|
|
662
|
+
"Content-Type": "application/json"
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
//#endregion
|
|
668
|
+
exports.CloudflareCostAdapter = CloudflareCostAdapter;
|