@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 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
@@ -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;