@buildinternet/uploads 0.47.0 → 0.48.0
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/client.d.ts +17 -0
- package/dist/commands.d.ts +10 -8
- package/dist/commands.js +22 -4
- package/dist/format-usage.d.ts +5 -0
- package/dist/format-usage.js +14 -4
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -439,6 +439,23 @@ export interface UsageResult {
|
|
|
439
439
|
storageRemainingBytes?: number;
|
|
440
440
|
maxUploadsPerPeriod?: number;
|
|
441
441
|
uploadsRemaining?: number;
|
|
442
|
+
/** Bytes still on hosted storage (shared-lane residue). */
|
|
443
|
+
sharedBytes?: number;
|
|
444
|
+
/** "shared" = BYO bucket active: the storage cap meters only hosted
|
|
445
|
+
* residue; the customer's own bucket is unmetered. */
|
|
446
|
+
storageBudgetBasis?: "total" | "shared";
|
|
447
|
+
/** Bearer-safe lane summary (issue #775; servers ≥ this field's release). */
|
|
448
|
+
storage?: {
|
|
449
|
+
mode: "shared" | "byo";
|
|
450
|
+
/** Demoted former-active lanes that still serve previously uploaded files. */
|
|
451
|
+
fallbackLanes: number;
|
|
452
|
+
health: {
|
|
453
|
+
ok: boolean;
|
|
454
|
+
code?: string;
|
|
455
|
+
message?: string;
|
|
456
|
+
since?: string;
|
|
457
|
+
};
|
|
458
|
+
};
|
|
442
459
|
/** File scopes of the presented token (servers ≥ this field's release). */
|
|
443
460
|
scopes?: Array<TokenScope>;
|
|
444
461
|
/**
|
package/dist/commands.d.ts
CHANGED
|
@@ -615,16 +615,18 @@ export interface DoctorReport {
|
|
|
615
615
|
/** Workspace/token mismatch warning (also present in hints). */
|
|
616
616
|
warning?: string;
|
|
617
617
|
/**
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
*
|
|
621
|
-
*
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
* a fabricated mode.
|
|
618
|
+
* Storage-lane summary (issue #775): the usage endpoint carries a
|
|
619
|
+
* bearer-safe `storage` object (mode + fallback-lane count + health), so
|
|
620
|
+
* doctor reports it from the same call it already makes. `checked` is
|
|
621
|
+
* false when usage failed or the server predates the field — then `note`
|
|
622
|
+
* falls back to the honest "can't check from here" line (the full
|
|
623
|
+
* projection on `GET /me/workspaces/:name/storage` stays session-gated).
|
|
625
624
|
*/
|
|
626
625
|
storage: {
|
|
627
|
-
checked:
|
|
626
|
+
checked: boolean;
|
|
627
|
+
mode?: "shared" | "byo";
|
|
628
|
+
fallbackLanes?: number;
|
|
629
|
+
healthy?: boolean;
|
|
628
630
|
note: string;
|
|
629
631
|
};
|
|
630
632
|
hints: string[];
|
package/dist/commands.js
CHANGED
|
@@ -3483,6 +3483,10 @@ export async function buildDoctorReport(config, client, detectRoots) {
|
|
|
3483
3483
|
}
|
|
3484
3484
|
}
|
|
3485
3485
|
let usage;
|
|
3486
|
+
let storage = {
|
|
3487
|
+
checked: false,
|
|
3488
|
+
note: "not checked from the CLI — this server doesn't report a storage summary on the usage endpoint; sign in on the web (Account → workspace → Settings) to view mode and verification status",
|
|
3489
|
+
};
|
|
3486
3490
|
let scopes;
|
|
3487
3491
|
if (authOk) {
|
|
3488
3492
|
try {
|
|
@@ -3493,6 +3497,23 @@ export async function buildDoctorReport(config, client, detectRoots) {
|
|
|
3493
3497
|
objects: snap.objects,
|
|
3494
3498
|
uploadsInPeriod: snap.uploadsInPeriod,
|
|
3495
3499
|
};
|
|
3500
|
+
if (snap.storage) {
|
|
3501
|
+
const laneNote = snap.storage.fallbackLanes > 0
|
|
3502
|
+
? ` (${snap.storage.fallbackLanes} previous lane${snap.storage.fallbackLanes === 1 ? "" : "s"} still serving old files)`
|
|
3503
|
+
: "";
|
|
3504
|
+
const healthNote = snap.storage.health.ok
|
|
3505
|
+
? ""
|
|
3506
|
+
: " — not working; rotate credentials on the web settings page";
|
|
3507
|
+
storage = {
|
|
3508
|
+
checked: true,
|
|
3509
|
+
mode: snap.storage.mode,
|
|
3510
|
+
fallbackLanes: snap.storage.fallbackLanes,
|
|
3511
|
+
healthy: snap.storage.health.ok,
|
|
3512
|
+
note: (snap.storage.mode === "byo" ? "your bucket" : "hosted storage") +
|
|
3513
|
+
laneNote +
|
|
3514
|
+
healthNote,
|
|
3515
|
+
};
|
|
3516
|
+
}
|
|
3496
3517
|
scopes = snap.scopes;
|
|
3497
3518
|
if (scopes && !scopes.includes("files:delete")) {
|
|
3498
3519
|
hints.push("token lacks files:delete (`uploads delete` will be forbidden) — re-run `uploads login` for a full-scope token");
|
|
@@ -3522,10 +3543,7 @@ export async function buildDoctorReport(config, client, detectRoots) {
|
|
|
3522
3543
|
usage,
|
|
3523
3544
|
scopes,
|
|
3524
3545
|
warning: mismatch,
|
|
3525
|
-
storage
|
|
3526
|
-
checked: false,
|
|
3527
|
-
note: "not checked from the CLI — storage settings (shared vs. bring-your-own-bucket) live behind a signed-in session; sign in on the web (Account → workspace → Settings) to view mode and verification status",
|
|
3528
|
-
},
|
|
3546
|
+
storage,
|
|
3529
3547
|
hints,
|
|
3530
3548
|
browser,
|
|
3531
3549
|
};
|
package/dist/format-usage.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export type UsageSnapshotLike = {
|
|
|
9
9
|
storageRemainingBytes?: number;
|
|
10
10
|
maxUploadsPerPeriod?: number;
|
|
11
11
|
uploadsRemaining?: number;
|
|
12
|
+
/** Bytes still on hosted storage (shared-lane residue). */
|
|
13
|
+
sharedBytes?: number;
|
|
14
|
+
/** "shared" = BYO bucket active: the storage cap meters only hosted
|
|
15
|
+
* residue; bytes in the customer's own bucket are unmetered. */
|
|
16
|
+
storageBudgetBasis?: "total" | "shared";
|
|
12
17
|
/** Catalog plan id when the API reports it (`free` | `pro`). */
|
|
13
18
|
plan?: string;
|
|
14
19
|
};
|
package/dist/format-usage.js
CHANGED
|
@@ -11,9 +11,14 @@ import { formatByteSize, formatMarketedBytes } from "./format-bytes.js";
|
|
|
11
11
|
import { BRAND } from "./cli-brand.js";
|
|
12
12
|
/** True when the API reported any cumulative workspace quota. */
|
|
13
13
|
export function isUsageMetered(result) {
|
|
14
|
-
return (usagePct(result
|
|
14
|
+
return (usagePct(storageMeteredBytes(result), result.maxStorageBytes) !== null ||
|
|
15
15
|
usagePct(result.uploadsInPeriod, result.maxUploadsPerPeriod) !== null);
|
|
16
16
|
}
|
|
17
|
+
/** The usage number the storage cap actually meters — hosted residue only
|
|
18
|
+
* when a BYO bucket is active (`storageBudgetBasis: "shared"`). */
|
|
19
|
+
function storageMeteredBytes(result) {
|
|
20
|
+
return result.storageBudgetBasis === "shared" ? (result.sharedBytes ?? 0) : result.bytes;
|
|
21
|
+
}
|
|
17
22
|
/** 0–100, one decimal. Missing/invalid caps → no bar. Matches web `usagePct`. */
|
|
18
23
|
export function usagePct(value, max) {
|
|
19
24
|
if (typeof max !== "number" || !(max > 0) || !Number.isFinite(value))
|
|
@@ -112,21 +117,26 @@ export function formatUsageHuman(result, opts = {}) {
|
|
|
112
117
|
const label = planLabel(result.plan);
|
|
113
118
|
if (label)
|
|
114
119
|
lines.push(`plan: ${label}`);
|
|
115
|
-
const
|
|
120
|
+
const byoActive = result.storageBudgetBasis === "shared";
|
|
121
|
+
const meteredBytes = storageMeteredBytes(result);
|
|
122
|
+
const storagePct = usagePct(meteredBytes, result.maxStorageBytes);
|
|
116
123
|
if (storagePct !== null && result.maxStorageBytes != null) {
|
|
117
124
|
// Caps (and remaining-against-cap) use SI marketed formatting so Free's
|
|
118
125
|
// 250_000_000 reads as "250 MB", not binary "238.4 MB". Used bytes share
|
|
119
126
|
// the same base on this line so the three numbers stay coherent.
|
|
120
|
-
const detail = `${formatMarketedBytes(
|
|
127
|
+
const detail = `${formatMarketedBytes(meteredBytes)} / ${formatMarketedBytes(result.maxStorageBytes)}` +
|
|
121
128
|
(result.storageRemainingBytes != null
|
|
122
129
|
? ` (${formatMarketedBytes(result.storageRemainingBytes)} free)`
|
|
123
130
|
: "");
|
|
124
131
|
const bar = formatProgressBar(storagePct, { width, color });
|
|
125
|
-
lines.push(`storage: ${bar} ${detail}`);
|
|
132
|
+
lines.push(`storage: ${bar} ${detail}${byoActive ? " on hosted storage" : ""}`);
|
|
126
133
|
}
|
|
127
134
|
else {
|
|
128
135
|
lines.push(`storage: ${formatByteSize(result.bytes)}`);
|
|
129
136
|
}
|
|
137
|
+
if (byoActive) {
|
|
138
|
+
lines.push("note: your own bucket is unmetered — the storage quota only counts files on hosted storage");
|
|
139
|
+
}
|
|
130
140
|
lines.push(`objects: ${formatCount(result.objects)}`);
|
|
131
141
|
const uploadsPct = usagePct(result.uploadsInPeriod, result.maxUploadsPerPeriod);
|
|
132
142
|
if (uploadsPct !== null && result.maxUploadsPerPeriod != null) {
|