@burn0/burn0 0.2.5 → 0.2.6
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/README.md +9 -1
- package/dist/{chunk-H3A5NM5C.mjs → chunk-PPEPVHNM.mjs} +62 -38
- package/dist/cli/index.js +121 -38
- package/dist/index.js +61 -37
- package/dist/index.mjs +1 -1
- package/dist/register.js +61 -37
- package/dist/register.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,14 @@ LLMs, SaaS, infrastructure. See per-request costs in real time.<br><br>
|
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
32
|
+
|
|
33
|
+
### 🎬 See it in action
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
https://github.com/user-attachments/assets/56962fc8-b9cf-49b2-9481-bc10aca6fb56
|
|
37
|
+
|
|
38
|
+
</div>
|
|
39
|
+
|
|
32
40
|
## The Problem
|
|
33
41
|
|
|
34
42
|
You're running OpenAI, Anthropic, Stripe, Supabase, SendGrid, and a dozen other APIs. Your monthly bill is $2,847 and climbing 340% month-over-month.
|
|
@@ -86,7 +94,7 @@ echo 'BURN0_API_KEY=b0_sk_your_key_here' >> .env
|
|
|
86
94
|
# 3. Restart — costs now sync to burn0.dev
|
|
87
95
|
```
|
|
88
96
|
|
|
89
|
-
Now you get a **live event feed**, **cost breakdown by service**, **monthly projections**, and **full request history** — all at [burn0.dev/dashboard](https://burn0.dev/dashboard).
|
|
97
|
+
Now you get a **live event feed**, **cost breakdown by service**, **monthly projections**, and **full request history** — all at [burn0.dev/dashboard](https://burn0## The Problem.dev/dashboard).
|
|
90
98
|
|
|
91
99
|
> burn0 only syncs metadata (service, model, tokens, cost, latency) — never request/response bodies or your API keys.
|
|
92
100
|
|
|
@@ -397,32 +397,10 @@ function createRestorer(deps) {
|
|
|
397
397
|
// src/transport/dispatcher.ts
|
|
398
398
|
function createDispatcher(mode2, deps) {
|
|
399
399
|
return (event) => {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
break;
|
|
405
|
-
case "dev-cloud":
|
|
406
|
-
deps.logEvent?.(event);
|
|
407
|
-
deps.writeLedger?.(event);
|
|
408
|
-
deps.addToBatch?.(event);
|
|
409
|
-
break;
|
|
410
|
-
case "prod-cloud":
|
|
411
|
-
deps.logEvent?.(event);
|
|
412
|
-
deps.writeLedger?.(event);
|
|
413
|
-
deps.addToBatch?.(event);
|
|
414
|
-
break;
|
|
415
|
-
case "prod-local":
|
|
416
|
-
deps.logEvent?.(event);
|
|
417
|
-
break;
|
|
418
|
-
case "test-enabled":
|
|
419
|
-
deps.logEvent?.(event);
|
|
420
|
-
deps.writeLedger?.(event);
|
|
421
|
-
deps.addToBatch?.(event);
|
|
422
|
-
break;
|
|
423
|
-
case "test-disabled":
|
|
424
|
-
break;
|
|
425
|
-
}
|
|
400
|
+
if (mode2 === "test-disabled") return;
|
|
401
|
+
deps.logEvent?.(event);
|
|
402
|
+
deps.writeLedger?.(event);
|
|
403
|
+
deps.addToBatch?.(event);
|
|
426
404
|
};
|
|
427
405
|
}
|
|
428
406
|
|
|
@@ -535,10 +513,17 @@ async function shipEvents(events, apiKey2, baseUrl, fetchFn = globalThis.fetch)
|
|
|
535
513
|
},
|
|
536
514
|
body: JSON.stringify({ events, sdk_version: SDK_VERSION })
|
|
537
515
|
});
|
|
538
|
-
if (response.ok)
|
|
516
|
+
if (response.ok) {
|
|
517
|
+
if (isDebug()) console.log(`[burn0] Shipped ${events.length} events`);
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
if (isDebug()) {
|
|
521
|
+
const body = await response.text().catch(() => "");
|
|
522
|
+
console.warn(`[burn0] Shipping rejected: ${response.status} ${body}`);
|
|
523
|
+
}
|
|
539
524
|
} catch (err) {
|
|
540
525
|
if (isDebug()) {
|
|
541
|
-
console.warn("[burn0]
|
|
526
|
+
console.warn("[burn0] Shipping failed:", err.message);
|
|
542
527
|
}
|
|
543
528
|
}
|
|
544
529
|
if (attempt < maxAttempts - 1) {
|
|
@@ -796,31 +781,70 @@ try {
|
|
|
796
781
|
}
|
|
797
782
|
var ticker = createTicker({ todayCost, todayCalls, perServiceCosts });
|
|
798
783
|
var batch = null;
|
|
799
|
-
|
|
800
|
-
|
|
784
|
+
var lateInitDone = false;
|
|
785
|
+
function createBatch(key) {
|
|
786
|
+
return new BatchBuffer({
|
|
801
787
|
sizeThreshold: 50,
|
|
802
788
|
timeThresholdMs: 1e4,
|
|
803
789
|
maxSize: 500,
|
|
804
790
|
onFlush: (events) => {
|
|
805
|
-
shipEvents(events,
|
|
791
|
+
shipEvents(events, key, BURN0_API_URL, originalFetch2).catch(() => {
|
|
806
792
|
});
|
|
807
793
|
}
|
|
808
794
|
});
|
|
809
795
|
}
|
|
796
|
+
if ((mode === "dev-cloud" || mode === "prod-cloud") && apiKey) {
|
|
797
|
+
batch = createBatch(apiKey);
|
|
798
|
+
}
|
|
799
|
+
var pendingEvents = [];
|
|
800
|
+
function lateInit(event) {
|
|
801
|
+
if (batch) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
const lateKey = getApiKey();
|
|
805
|
+
if (!lateKey) {
|
|
806
|
+
if (event) pendingEvents.push(event);
|
|
807
|
+
if (!lateInitDone) {
|
|
808
|
+
lateInitDone = true;
|
|
809
|
+
setTimeout(() => {
|
|
810
|
+
lateInitDone = false;
|
|
811
|
+
if (pendingEvents.length > 0) {
|
|
812
|
+
const e = pendingEvents.shift();
|
|
813
|
+
lateInit(e);
|
|
814
|
+
} else {
|
|
815
|
+
lateInit();
|
|
816
|
+
}
|
|
817
|
+
}, 0);
|
|
818
|
+
}
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
lateInitDone = true;
|
|
822
|
+
apiKey = lateKey;
|
|
823
|
+
mode = detectMode({ isTTY: isTTY(), apiKey });
|
|
824
|
+
batch = createBatch(lateKey);
|
|
825
|
+
fetchPricing(BURN0_API_URL, originalFetch2).catch(() => {
|
|
826
|
+
});
|
|
827
|
+
for (const e of pendingEvents) {
|
|
828
|
+
batch.add(e);
|
|
829
|
+
}
|
|
830
|
+
pendingEvents.length = 0;
|
|
831
|
+
}
|
|
810
832
|
var shouldWriteLedger = mode !== "test-disabled" && mode !== "prod-local";
|
|
811
833
|
var dispatch = createDispatcher(mode, {
|
|
812
834
|
logEvent: (e) => ticker.tick(e),
|
|
813
835
|
writeLedger: shouldWriteLedger ? (e) => ledger.write(e) : void 0,
|
|
814
|
-
addToBatch:
|
|
836
|
+
addToBatch: (e) => {
|
|
837
|
+
lateInit();
|
|
838
|
+
batch?.add(e);
|
|
839
|
+
}
|
|
815
840
|
});
|
|
816
841
|
var preloaded = checkImportOrder();
|
|
817
842
|
if (preloaded.length > 0) {
|
|
818
|
-
console.warn(
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
console.warn("[burn0] No API key \u2014 costs not tracked. Get one free at burn0.dev/api");
|
|
843
|
+
console.warn(
|
|
844
|
+
`[burn0] Warning: These SDKs were imported before burn0 and may not be tracked: ${preloaded.join(", ")}. Move \`import '@burn0/burn0'\` to the top of your entry file.`
|
|
845
|
+
);
|
|
822
846
|
}
|
|
823
|
-
if (canPatch() && mode !== "test-disabled"
|
|
847
|
+
if (canPatch() && mode !== "test-disabled") {
|
|
824
848
|
const onEvent = (event) => {
|
|
825
849
|
const enriched = enrichEvent(event);
|
|
826
850
|
dispatch(enriched);
|
|
@@ -846,4 +870,4 @@ export {
|
|
|
846
870
|
startSpan,
|
|
847
871
|
restore
|
|
848
872
|
};
|
|
849
|
-
//# sourceMappingURL=chunk-
|
|
873
|
+
//# sourceMappingURL=chunk-PPEPVHNM.mjs.map
|
package/dist/cli/index.js
CHANGED
|
@@ -18080,11 +18080,15 @@ async function _runInit() {
|
|
|
18080
18080
|
}
|
|
18081
18081
|
const serviceConfigs = [];
|
|
18082
18082
|
if (allDetected.length > 0) {
|
|
18083
|
-
console.log(
|
|
18084
|
-
`
|
|
18083
|
+
console.log(
|
|
18084
|
+
source_default.bold(` Auto-detected ${allDetected.length} services:
|
|
18085
|
+
`)
|
|
18086
|
+
);
|
|
18085
18087
|
for (const svc of allDetected) {
|
|
18086
18088
|
const tag = svc.autopriced ? source_default.dim("auto-priced") : source_default.yellow("needs plan");
|
|
18087
|
-
console.log(
|
|
18089
|
+
console.log(
|
|
18090
|
+
` ${source_default.green(" \u2713")} ${svc.displayName.padEnd(20)} ${tag}`
|
|
18091
|
+
);
|
|
18088
18092
|
}
|
|
18089
18093
|
console.log();
|
|
18090
18094
|
const fixedTier = allDetected.filter((s) => !s.autopriced);
|
|
@@ -18101,7 +18105,11 @@ async function _runInit() {
|
|
|
18101
18105
|
});
|
|
18102
18106
|
if (plan !== "skip") {
|
|
18103
18107
|
const selected = entry.plans.find((p) => p.value === plan);
|
|
18104
|
-
serviceConfigs.push({
|
|
18108
|
+
serviceConfigs.push({
|
|
18109
|
+
name: svc.name,
|
|
18110
|
+
plan,
|
|
18111
|
+
monthlyCost: selected?.monthly
|
|
18112
|
+
});
|
|
18105
18113
|
} else {
|
|
18106
18114
|
serviceConfigs.push({ name: svc.name });
|
|
18107
18115
|
}
|
|
@@ -18120,18 +18128,38 @@ async function _runInit() {
|
|
|
18120
18128
|
});
|
|
18121
18129
|
if (addMore) {
|
|
18122
18130
|
const alreadyAdded = new Set(serviceConfigs.map((s) => s.name));
|
|
18123
|
-
const additionalServices = SERVICE_CATALOG.filter(
|
|
18131
|
+
const additionalServices = SERVICE_CATALOG.filter(
|
|
18132
|
+
(s) => !alreadyAdded.has(s.name)
|
|
18133
|
+
);
|
|
18124
18134
|
const llmChoices = additionalServices.filter((s) => s.category === "llm").map((s) => ({ name: s.displayName, value: s.name }));
|
|
18125
18135
|
const apiChoices = additionalServices.filter((s) => s.category === "api").map((s) => ({ name: s.displayName, value: s.name }));
|
|
18126
18136
|
const infraChoices = additionalServices.filter((s) => s.category === "infra").map((s) => ({ name: s.displayName, value: s.name }));
|
|
18127
18137
|
const additional = await esm_default2({
|
|
18128
18138
|
message: "Select services:",
|
|
18129
18139
|
choices: [
|
|
18130
|
-
...llmChoices.length ? [
|
|
18140
|
+
...llmChoices.length ? [
|
|
18141
|
+
{
|
|
18142
|
+
name: source_default.bold.blue("\u2500\u2500 LLM Providers \u2500\u2500"),
|
|
18143
|
+
value: "__sep",
|
|
18144
|
+
disabled: true
|
|
18145
|
+
}
|
|
18146
|
+
] : [],
|
|
18131
18147
|
...llmChoices,
|
|
18132
|
-
...apiChoices.length ? [
|
|
18148
|
+
...apiChoices.length ? [
|
|
18149
|
+
{
|
|
18150
|
+
name: source_default.bold.magenta("\u2500\u2500 API Services \u2500\u2500"),
|
|
18151
|
+
value: "__sep2",
|
|
18152
|
+
disabled: true
|
|
18153
|
+
}
|
|
18154
|
+
] : [],
|
|
18133
18155
|
...apiChoices,
|
|
18134
|
-
...infraChoices.length ? [
|
|
18156
|
+
...infraChoices.length ? [
|
|
18157
|
+
{
|
|
18158
|
+
name: source_default.bold.yellow("\u2500\u2500 Infrastructure \u2500\u2500"),
|
|
18159
|
+
value: "__sep3",
|
|
18160
|
+
disabled: true
|
|
18161
|
+
}
|
|
18162
|
+
] : [],
|
|
18135
18163
|
...infraChoices
|
|
18136
18164
|
]
|
|
18137
18165
|
});
|
|
@@ -18159,7 +18187,9 @@ async function _runInit() {
|
|
|
18159
18187
|
}
|
|
18160
18188
|
let projectName = "my-project";
|
|
18161
18189
|
try {
|
|
18162
|
-
const pkg = JSON.parse(
|
|
18190
|
+
const pkg = JSON.parse(
|
|
18191
|
+
import_node_fs5.default.readFileSync(import_node_path6.default.join(cwd, "package.json"), "utf-8")
|
|
18192
|
+
);
|
|
18163
18193
|
if (pkg.name) projectName = pkg.name;
|
|
18164
18194
|
} catch {
|
|
18165
18195
|
}
|
|
@@ -18174,12 +18204,12 @@ async function _runInit() {
|
|
|
18174
18204
|
});
|
|
18175
18205
|
if (apiKey) {
|
|
18176
18206
|
try {
|
|
18177
|
-
const apiUrl =
|
|
18207
|
+
const apiUrl = "https://burn0-server-production.up.railway.app";
|
|
18178
18208
|
const res = await fetch(`${apiUrl}/v1/projects/config`, {
|
|
18179
18209
|
method: "POST",
|
|
18180
18210
|
headers: {
|
|
18181
18211
|
"Content-Type": "application/json",
|
|
18182
|
-
|
|
18212
|
+
Authorization: `Bearer ${apiKey}`
|
|
18183
18213
|
},
|
|
18184
18214
|
body: JSON.stringify({
|
|
18185
18215
|
services: serviceConfigs.map((s) => ({
|
|
@@ -18206,7 +18236,9 @@ async function _runInit() {
|
|
|
18206
18236
|
const gitignoreLines = gitignoreContent.split("\n").map((l) => l.trim());
|
|
18207
18237
|
if (!gitignoreLines.includes(".env")) {
|
|
18208
18238
|
ensureGitignore(cwd, ".env");
|
|
18209
|
-
console.log(
|
|
18239
|
+
console.log(
|
|
18240
|
+
source_default.green(" \u2713 Added .env to .gitignore (protects your API keys)")
|
|
18241
|
+
);
|
|
18210
18242
|
}
|
|
18211
18243
|
console.log("");
|
|
18212
18244
|
console.log(source_default.green(" \u2713 Setup complete"));
|
|
@@ -18483,7 +18515,20 @@ function formatCost(cost) {
|
|
|
18483
18515
|
}
|
|
18484
18516
|
function formatDateLabel(dateStr) {
|
|
18485
18517
|
const d = /* @__PURE__ */ new Date(dateStr + "T12:00:00");
|
|
18486
|
-
const months = [
|
|
18518
|
+
const months = [
|
|
18519
|
+
"Jan",
|
|
18520
|
+
"Feb",
|
|
18521
|
+
"Mar",
|
|
18522
|
+
"Apr",
|
|
18523
|
+
"May",
|
|
18524
|
+
"Jun",
|
|
18525
|
+
"Jul",
|
|
18526
|
+
"Aug",
|
|
18527
|
+
"Sep",
|
|
18528
|
+
"Oct",
|
|
18529
|
+
"Nov",
|
|
18530
|
+
"Dec"
|
|
18531
|
+
];
|
|
18487
18532
|
return `${months[d.getMonth()]} ${String(d.getDate()).padStart(2, " ")}`;
|
|
18488
18533
|
}
|
|
18489
18534
|
function makeBar(value, max, width) {
|
|
@@ -18512,10 +18557,12 @@ function aggregateLocal(events, days) {
|
|
|
18512
18557
|
const estimate = estimateLocalCost(event);
|
|
18513
18558
|
if (estimate.type === "priced" && estimate.cost > 0) {
|
|
18514
18559
|
totalCost += estimate.cost;
|
|
18515
|
-
if (!serviceCosts[event.service])
|
|
18560
|
+
if (!serviceCosts[event.service])
|
|
18561
|
+
serviceCosts[event.service] = { cost: 0, calls: 0 };
|
|
18516
18562
|
serviceCosts[event.service].cost += estimate.cost;
|
|
18517
18563
|
serviceCosts[event.service].calls++;
|
|
18518
|
-
if (!dayCosts[eventDateStr])
|
|
18564
|
+
if (!dayCosts[eventDateStr])
|
|
18565
|
+
dayCosts[eventDateStr] = { cost: 0, calls: 0, services: {} };
|
|
18519
18566
|
dayCosts[eventDateStr].cost += estimate.cost;
|
|
18520
18567
|
dayCosts[eventDateStr].calls++;
|
|
18521
18568
|
dayCosts[eventDateStr].services[event.service] = (dayCosts[eventDateStr].services[event.service] ?? 0) + estimate.cost;
|
|
@@ -18543,17 +18590,24 @@ function aggregateLocal(events, days) {
|
|
|
18543
18590
|
}
|
|
18544
18591
|
function renderCallCountOnly(data) {
|
|
18545
18592
|
const maxCalls = data.allServiceCalls.length > 0 ? data.allServiceCalls[0].calls : 0;
|
|
18546
|
-
const maxNameLen = Math.max(
|
|
18593
|
+
const maxNameLen = Math.max(
|
|
18594
|
+
...data.allServiceCalls.map((s) => s.name.length),
|
|
18595
|
+
8
|
|
18596
|
+
);
|
|
18547
18597
|
for (const svc of data.allServiceCalls) {
|
|
18548
18598
|
const bar = makeBar(svc.calls, maxCalls, 20);
|
|
18549
|
-
console.log(
|
|
18599
|
+
console.log(
|
|
18600
|
+
` ${svc.name.padEnd(maxNameLen)} ${source_default.gray(`${String(svc.calls).padStart(5)} calls`)} ${source_default.cyan(bar)}`
|
|
18601
|
+
);
|
|
18550
18602
|
}
|
|
18551
18603
|
console.log();
|
|
18552
18604
|
}
|
|
18553
18605
|
function renderCostReport(data, label, showDaily, isToday) {
|
|
18554
|
-
console.log(
|
|
18606
|
+
console.log(
|
|
18607
|
+
`
|
|
18555
18608
|
${source_default.hex("#FA5D19").bold("burn0 report")} ${source_default.gray(`\u2500\u2500 ${label}`)}
|
|
18556
|
-
`
|
|
18609
|
+
`
|
|
18610
|
+
);
|
|
18557
18611
|
if (data.total.calls === 0) {
|
|
18558
18612
|
const msg = isToday ? "No calls today." : `No cost data yet. Run your app with \`import '@burn0/burn0'\` to start tracking.`;
|
|
18559
18613
|
console.log(source_default.dim(` ${msg}
|
|
@@ -18561,50 +18615,70 @@ function renderCostReport(data, label, showDaily, isToday) {
|
|
|
18561
18615
|
return;
|
|
18562
18616
|
}
|
|
18563
18617
|
if (!data.pricingAvailable) {
|
|
18564
|
-
console.log(
|
|
18565
|
-
|
|
18618
|
+
console.log(
|
|
18619
|
+
source_default.dim(
|
|
18620
|
+
` ${data.total.calls} calls tracked (pricing data not available)
|
|
18621
|
+
`
|
|
18622
|
+
)
|
|
18623
|
+
);
|
|
18566
18624
|
renderCallCountOnly(data);
|
|
18567
18625
|
return;
|
|
18568
18626
|
}
|
|
18569
18627
|
if (data.total.cost === 0 && data.total.calls > 0) {
|
|
18570
|
-
console.log(
|
|
18571
|
-
|
|
18628
|
+
console.log(
|
|
18629
|
+
source_default.dim(
|
|
18630
|
+
` ${data.total.calls} calls tracked (no pricing data available)
|
|
18631
|
+
`
|
|
18632
|
+
)
|
|
18633
|
+
);
|
|
18572
18634
|
renderCallCountOnly(data);
|
|
18573
18635
|
return;
|
|
18574
18636
|
}
|
|
18575
|
-
console.log(
|
|
18576
|
-
`)
|
|
18637
|
+
console.log(
|
|
18638
|
+
` ${source_default.bold("Total:")} ${source_default.green(formatCost(data.total.cost))} ${source_default.gray(`(${data.total.calls} calls)`)}
|
|
18639
|
+
`
|
|
18640
|
+
);
|
|
18577
18641
|
const maxCost = data.byService.length > 0 ? data.byService[0].cost : 0;
|
|
18578
18642
|
const maxNameLen = Math.max(...data.byService.map((s) => s.name.length), 8);
|
|
18579
18643
|
for (const svc of data.byService) {
|
|
18580
18644
|
const pct = data.total.cost > 0 ? Math.round(svc.cost / data.total.cost * 100) : 0;
|
|
18581
18645
|
const bar = makeBar(svc.cost, maxCost, 20);
|
|
18582
|
-
console.log(
|
|
18646
|
+
console.log(
|
|
18647
|
+
` ${svc.name.padEnd(maxNameLen)} ${source_default.green(formatCost(svc.cost).padStart(10))} ${source_default.cyan(bar)} ${source_default.gray(`${String(pct).padStart(3)}%`)}`
|
|
18648
|
+
);
|
|
18583
18649
|
}
|
|
18584
18650
|
if (data.unpricedCount > 0) {
|
|
18585
18651
|
console.log(source_default.dim(`
|
|
18586
18652
|
+ ${data.unpricedCount} calls not priced`));
|
|
18587
18653
|
}
|
|
18588
18654
|
if (showDaily && data.byDay.length > 0) {
|
|
18589
|
-
console.log(
|
|
18655
|
+
console.log(
|
|
18656
|
+
`
|
|
18590
18657
|
${source_default.gray("\u2500\u2500 daily \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
|
|
18591
|
-
`
|
|
18658
|
+
`
|
|
18659
|
+
);
|
|
18592
18660
|
const maxDayCost = Math.max(...data.byDay.map((d) => d.cost));
|
|
18593
18661
|
for (const day of data.byDay) {
|
|
18594
18662
|
const dateLabel = formatDateLabel(day.date);
|
|
18595
18663
|
const bar = makeBar(day.cost, maxDayCost, 12);
|
|
18596
18664
|
const top2 = day.topServices.slice(0, 2).map((s) => `${s.name} ${formatCost(s.cost)}`).join(" \xB7 ");
|
|
18597
18665
|
const more = day.topServices.length > 2 ? ` +${day.topServices.length - 2} more` : "";
|
|
18598
|
-
console.log(
|
|
18666
|
+
console.log(
|
|
18667
|
+
` ${source_default.gray(dateLabel)} ${source_default.green(formatCost(day.cost).padStart(10))} ${source_default.cyan(bar)} ${source_default.dim(top2 + more)}`
|
|
18668
|
+
);
|
|
18599
18669
|
}
|
|
18600
18670
|
}
|
|
18601
18671
|
if (data.total.cost > 0) {
|
|
18602
18672
|
const daysInPeriod = showDaily ? 7 : 1;
|
|
18603
18673
|
const dailyRate = data.total.cost / daysInPeriod;
|
|
18604
18674
|
const monthly = dailyRate * 30;
|
|
18605
|
-
console.log(
|
|
18606
|
-
|
|
18607
|
-
|
|
18675
|
+
console.log(
|
|
18676
|
+
`
|
|
18677
|
+
${source_default.gray("\u2500\u2500 projection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`
|
|
18678
|
+
);
|
|
18679
|
+
console.log(
|
|
18680
|
+
` ${source_default.gray("~")}${source_default.green(formatCost(monthly))}${source_default.gray("/mo estimated")} ${source_default.dim(`(based on ${isToday ? "today" : "last 7 days"})`)}`
|
|
18681
|
+
);
|
|
18608
18682
|
}
|
|
18609
18683
|
console.log();
|
|
18610
18684
|
}
|
|
@@ -18612,10 +18686,16 @@ async function fetchBackendReport(apiKey, days) {
|
|
|
18612
18686
|
try {
|
|
18613
18687
|
const controller = new AbortController();
|
|
18614
18688
|
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
18615
|
-
const response = await globalThis.fetch(
|
|
18616
|
-
|
|
18617
|
-
|
|
18618
|
-
|
|
18689
|
+
const response = await globalThis.fetch(
|
|
18690
|
+
`${BURN0_API_URL}/v1/report?days=${days}`,
|
|
18691
|
+
{
|
|
18692
|
+
headers: {
|
|
18693
|
+
Accept: "application/json",
|
|
18694
|
+
Authorization: `Bearer ${apiKey}`
|
|
18695
|
+
},
|
|
18696
|
+
signal: controller.signal
|
|
18697
|
+
}
|
|
18698
|
+
);
|
|
18619
18699
|
clearTimeout(timeout);
|
|
18620
18700
|
if (!response.ok) return null;
|
|
18621
18701
|
const data = await response.json();
|
|
@@ -18623,7 +18703,10 @@ async function fetchBackendReport(apiKey, days) {
|
|
|
18623
18703
|
total: data.total ?? { cost: 0, calls: 0 },
|
|
18624
18704
|
byService: data.byService ?? [],
|
|
18625
18705
|
byDay: data.byDay ?? [],
|
|
18626
|
-
allServiceCalls: (data.byService ?? []).map((s) => ({
|
|
18706
|
+
allServiceCalls: (data.byService ?? []).map((s) => ({
|
|
18707
|
+
name: s.name,
|
|
18708
|
+
calls: s.calls
|
|
18709
|
+
})),
|
|
18627
18710
|
unpricedCount: 0,
|
|
18628
18711
|
pricingAvailable: true
|
|
18629
18712
|
};
|
|
@@ -18657,7 +18740,7 @@ var init_report = __esm({
|
|
|
18657
18740
|
init_local();
|
|
18658
18741
|
init_local_pricing();
|
|
18659
18742
|
init_env();
|
|
18660
|
-
BURN0_API_URL =
|
|
18743
|
+
BURN0_API_URL = "https://burn0-server-production.up.railway.app";
|
|
18661
18744
|
}
|
|
18662
18745
|
});
|
|
18663
18746
|
|
package/dist/index.js
CHANGED
|
@@ -458,32 +458,10 @@ function createRestorer(deps) {
|
|
|
458
458
|
// src/transport/dispatcher.ts
|
|
459
459
|
function createDispatcher(mode2, deps) {
|
|
460
460
|
return (event) => {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
break;
|
|
466
|
-
case "dev-cloud":
|
|
467
|
-
deps.logEvent?.(event);
|
|
468
|
-
deps.writeLedger?.(event);
|
|
469
|
-
deps.addToBatch?.(event);
|
|
470
|
-
break;
|
|
471
|
-
case "prod-cloud":
|
|
472
|
-
deps.logEvent?.(event);
|
|
473
|
-
deps.writeLedger?.(event);
|
|
474
|
-
deps.addToBatch?.(event);
|
|
475
|
-
break;
|
|
476
|
-
case "prod-local":
|
|
477
|
-
deps.logEvent?.(event);
|
|
478
|
-
break;
|
|
479
|
-
case "test-enabled":
|
|
480
|
-
deps.logEvent?.(event);
|
|
481
|
-
deps.writeLedger?.(event);
|
|
482
|
-
deps.addToBatch?.(event);
|
|
483
|
-
break;
|
|
484
|
-
case "test-disabled":
|
|
485
|
-
break;
|
|
486
|
-
}
|
|
461
|
+
if (mode2 === "test-disabled") return;
|
|
462
|
+
deps.logEvent?.(event);
|
|
463
|
+
deps.writeLedger?.(event);
|
|
464
|
+
deps.addToBatch?.(event);
|
|
487
465
|
};
|
|
488
466
|
}
|
|
489
467
|
|
|
@@ -596,10 +574,17 @@ async function shipEvents(events, apiKey2, baseUrl, fetchFn = globalThis.fetch)
|
|
|
596
574
|
},
|
|
597
575
|
body: JSON.stringify({ events, sdk_version: SDK_VERSION })
|
|
598
576
|
});
|
|
599
|
-
if (response.ok)
|
|
577
|
+
if (response.ok) {
|
|
578
|
+
if (isDebug()) console.log(`[burn0] Shipped ${events.length} events`);
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
if (isDebug()) {
|
|
582
|
+
const body = await response.text().catch(() => "");
|
|
583
|
+
console.warn(`[burn0] Shipping rejected: ${response.status} ${body}`);
|
|
584
|
+
}
|
|
600
585
|
} catch (err) {
|
|
601
586
|
if (isDebug()) {
|
|
602
|
-
console.warn("[burn0]
|
|
587
|
+
console.warn("[burn0] Shipping failed:", err.message);
|
|
603
588
|
}
|
|
604
589
|
}
|
|
605
590
|
if (attempt < maxAttempts - 1) {
|
|
@@ -857,31 +842,70 @@ try {
|
|
|
857
842
|
}
|
|
858
843
|
var ticker = createTicker({ todayCost, todayCalls, perServiceCosts });
|
|
859
844
|
var batch = null;
|
|
860
|
-
|
|
861
|
-
|
|
845
|
+
var lateInitDone = false;
|
|
846
|
+
function createBatch(key) {
|
|
847
|
+
return new BatchBuffer({
|
|
862
848
|
sizeThreshold: 50,
|
|
863
849
|
timeThresholdMs: 1e4,
|
|
864
850
|
maxSize: 500,
|
|
865
851
|
onFlush: (events) => {
|
|
866
|
-
shipEvents(events,
|
|
852
|
+
shipEvents(events, key, BURN0_API_URL, originalFetch2).catch(() => {
|
|
867
853
|
});
|
|
868
854
|
}
|
|
869
855
|
});
|
|
870
856
|
}
|
|
857
|
+
if ((mode === "dev-cloud" || mode === "prod-cloud") && apiKey) {
|
|
858
|
+
batch = createBatch(apiKey);
|
|
859
|
+
}
|
|
860
|
+
var pendingEvents = [];
|
|
861
|
+
function lateInit(event) {
|
|
862
|
+
if (batch) {
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
const lateKey = getApiKey();
|
|
866
|
+
if (!lateKey) {
|
|
867
|
+
if (event) pendingEvents.push(event);
|
|
868
|
+
if (!lateInitDone) {
|
|
869
|
+
lateInitDone = true;
|
|
870
|
+
setTimeout(() => {
|
|
871
|
+
lateInitDone = false;
|
|
872
|
+
if (pendingEvents.length > 0) {
|
|
873
|
+
const e = pendingEvents.shift();
|
|
874
|
+
lateInit(e);
|
|
875
|
+
} else {
|
|
876
|
+
lateInit();
|
|
877
|
+
}
|
|
878
|
+
}, 0);
|
|
879
|
+
}
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
lateInitDone = true;
|
|
883
|
+
apiKey = lateKey;
|
|
884
|
+
mode = detectMode({ isTTY: isTTY(), apiKey });
|
|
885
|
+
batch = createBatch(lateKey);
|
|
886
|
+
fetchPricing(BURN0_API_URL, originalFetch2).catch(() => {
|
|
887
|
+
});
|
|
888
|
+
for (const e of pendingEvents) {
|
|
889
|
+
batch.add(e);
|
|
890
|
+
}
|
|
891
|
+
pendingEvents.length = 0;
|
|
892
|
+
}
|
|
871
893
|
var shouldWriteLedger = mode !== "test-disabled" && mode !== "prod-local";
|
|
872
894
|
var dispatch = createDispatcher(mode, {
|
|
873
895
|
logEvent: (e) => ticker.tick(e),
|
|
874
896
|
writeLedger: shouldWriteLedger ? (e) => ledger.write(e) : void 0,
|
|
875
|
-
addToBatch:
|
|
897
|
+
addToBatch: (e) => {
|
|
898
|
+
lateInit();
|
|
899
|
+
batch?.add(e);
|
|
900
|
+
}
|
|
876
901
|
});
|
|
877
902
|
var preloaded = checkImportOrder();
|
|
878
903
|
if (preloaded.length > 0) {
|
|
879
|
-
console.warn(
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
console.warn("[burn0] No API key \u2014 costs not tracked. Get one free at burn0.dev/api");
|
|
904
|
+
console.warn(
|
|
905
|
+
`[burn0] Warning: These SDKs were imported before burn0 and may not be tracked: ${preloaded.join(", ")}. Move \`import '@burn0/burn0'\` to the top of your entry file.`
|
|
906
|
+
);
|
|
883
907
|
}
|
|
884
|
-
if (canPatch() && mode !== "test-disabled"
|
|
908
|
+
if (canPatch() && mode !== "test-disabled") {
|
|
885
909
|
const onEvent = (event) => {
|
|
886
910
|
const enriched = enrichEvent(event);
|
|
887
911
|
dispatch(enriched);
|
package/dist/index.mjs
CHANGED
package/dist/register.js
CHANGED
|
@@ -444,32 +444,10 @@ function createRestorer(deps) {
|
|
|
444
444
|
// src/transport/dispatcher.ts
|
|
445
445
|
function createDispatcher(mode2, deps) {
|
|
446
446
|
return (event) => {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
break;
|
|
452
|
-
case "dev-cloud":
|
|
453
|
-
deps.logEvent?.(event);
|
|
454
|
-
deps.writeLedger?.(event);
|
|
455
|
-
deps.addToBatch?.(event);
|
|
456
|
-
break;
|
|
457
|
-
case "prod-cloud":
|
|
458
|
-
deps.logEvent?.(event);
|
|
459
|
-
deps.writeLedger?.(event);
|
|
460
|
-
deps.addToBatch?.(event);
|
|
461
|
-
break;
|
|
462
|
-
case "prod-local":
|
|
463
|
-
deps.logEvent?.(event);
|
|
464
|
-
break;
|
|
465
|
-
case "test-enabled":
|
|
466
|
-
deps.logEvent?.(event);
|
|
467
|
-
deps.writeLedger?.(event);
|
|
468
|
-
deps.addToBatch?.(event);
|
|
469
|
-
break;
|
|
470
|
-
case "test-disabled":
|
|
471
|
-
break;
|
|
472
|
-
}
|
|
447
|
+
if (mode2 === "test-disabled") return;
|
|
448
|
+
deps.logEvent?.(event);
|
|
449
|
+
deps.writeLedger?.(event);
|
|
450
|
+
deps.addToBatch?.(event);
|
|
473
451
|
};
|
|
474
452
|
}
|
|
475
453
|
|
|
@@ -582,10 +560,17 @@ async function shipEvents(events, apiKey2, baseUrl, fetchFn = globalThis.fetch)
|
|
|
582
560
|
},
|
|
583
561
|
body: JSON.stringify({ events, sdk_version: SDK_VERSION })
|
|
584
562
|
});
|
|
585
|
-
if (response.ok)
|
|
563
|
+
if (response.ok) {
|
|
564
|
+
if (isDebug()) console.log(`[burn0] Shipped ${events.length} events`);
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
567
|
+
if (isDebug()) {
|
|
568
|
+
const body = await response.text().catch(() => "");
|
|
569
|
+
console.warn(`[burn0] Shipping rejected: ${response.status} ${body}`);
|
|
570
|
+
}
|
|
586
571
|
} catch (err) {
|
|
587
572
|
if (isDebug()) {
|
|
588
|
-
console.warn("[burn0]
|
|
573
|
+
console.warn("[burn0] Shipping failed:", err.message);
|
|
589
574
|
}
|
|
590
575
|
}
|
|
591
576
|
if (attempt < maxAttempts - 1) {
|
|
@@ -843,31 +828,70 @@ try {
|
|
|
843
828
|
}
|
|
844
829
|
var ticker = createTicker({ todayCost, todayCalls, perServiceCosts });
|
|
845
830
|
var batch = null;
|
|
846
|
-
|
|
847
|
-
|
|
831
|
+
var lateInitDone = false;
|
|
832
|
+
function createBatch(key) {
|
|
833
|
+
return new BatchBuffer({
|
|
848
834
|
sizeThreshold: 50,
|
|
849
835
|
timeThresholdMs: 1e4,
|
|
850
836
|
maxSize: 500,
|
|
851
837
|
onFlush: (events) => {
|
|
852
|
-
shipEvents(events,
|
|
838
|
+
shipEvents(events, key, BURN0_API_URL, originalFetch2).catch(() => {
|
|
853
839
|
});
|
|
854
840
|
}
|
|
855
841
|
});
|
|
856
842
|
}
|
|
843
|
+
if ((mode === "dev-cloud" || mode === "prod-cloud") && apiKey) {
|
|
844
|
+
batch = createBatch(apiKey);
|
|
845
|
+
}
|
|
846
|
+
var pendingEvents = [];
|
|
847
|
+
function lateInit(event) {
|
|
848
|
+
if (batch) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
const lateKey = getApiKey();
|
|
852
|
+
if (!lateKey) {
|
|
853
|
+
if (event) pendingEvents.push(event);
|
|
854
|
+
if (!lateInitDone) {
|
|
855
|
+
lateInitDone = true;
|
|
856
|
+
setTimeout(() => {
|
|
857
|
+
lateInitDone = false;
|
|
858
|
+
if (pendingEvents.length > 0) {
|
|
859
|
+
const e = pendingEvents.shift();
|
|
860
|
+
lateInit(e);
|
|
861
|
+
} else {
|
|
862
|
+
lateInit();
|
|
863
|
+
}
|
|
864
|
+
}, 0);
|
|
865
|
+
}
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
lateInitDone = true;
|
|
869
|
+
apiKey = lateKey;
|
|
870
|
+
mode = detectMode({ isTTY: isTTY(), apiKey });
|
|
871
|
+
batch = createBatch(lateKey);
|
|
872
|
+
fetchPricing(BURN0_API_URL, originalFetch2).catch(() => {
|
|
873
|
+
});
|
|
874
|
+
for (const e of pendingEvents) {
|
|
875
|
+
batch.add(e);
|
|
876
|
+
}
|
|
877
|
+
pendingEvents.length = 0;
|
|
878
|
+
}
|
|
857
879
|
var shouldWriteLedger = mode !== "test-disabled" && mode !== "prod-local";
|
|
858
880
|
var dispatch = createDispatcher(mode, {
|
|
859
881
|
logEvent: (e) => ticker.tick(e),
|
|
860
882
|
writeLedger: shouldWriteLedger ? (e) => ledger.write(e) : void 0,
|
|
861
|
-
addToBatch:
|
|
883
|
+
addToBatch: (e) => {
|
|
884
|
+
lateInit();
|
|
885
|
+
batch?.add(e);
|
|
886
|
+
}
|
|
862
887
|
});
|
|
863
888
|
var preloaded = checkImportOrder();
|
|
864
889
|
if (preloaded.length > 0) {
|
|
865
|
-
console.warn(
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
console.warn("[burn0] No API key \u2014 costs not tracked. Get one free at burn0.dev/api");
|
|
890
|
+
console.warn(
|
|
891
|
+
`[burn0] Warning: These SDKs were imported before burn0 and may not be tracked: ${preloaded.join(", ")}. Move \`import '@burn0/burn0'\` to the top of your entry file.`
|
|
892
|
+
);
|
|
869
893
|
}
|
|
870
|
-
if (canPatch() && mode !== "test-disabled"
|
|
894
|
+
if (canPatch() && mode !== "test-disabled") {
|
|
871
895
|
const onEvent = (event) => {
|
|
872
896
|
const enriched = enrichEvent(event);
|
|
873
897
|
dispatch(enriched);
|
package/dist/register.mjs
CHANGED