@needmoretruth/nmts-cli 0.38.1 → 0.40.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.
@@ -1,13 +1,14 @@
1
1
  // `nmts wallet storage split|merge|transfer` — reshaping the storage resources the wallet holds,
2
- // and handing one over. ⛔ EACH SIGNS. The storage-control half a terminal can do: the browser's
3
- // rules (`shared/lib/storage-control/plan.ts`, copied byte-for-byte) say what the contract allows;
4
- // the dry run is the final judge; the review comes before the signature; `--yes` is the answer.
2
+ // and handing one over. ⛔ EACH SIGNS. What the contract allows, what a change would leave behind,
3
+ // the dry run that prices it and the signature are `storage-control.ts`, which the SDK calls as
4
+ // well; this file is the terminal over it: the words typed, the review read, and `--yes`.
5
5
  //
6
- // ⛔ THE ORDER IS THE SAFETY, as in `wallet-send.ts`: ① the resources are READ from the chain and
7
- // the named ones must be this wallet's ② the rules judge the request and say why not ③ the
8
- // exact transaction is dry-run for its fee, and a refusal there is printed and ends the run
9
- // ④ the review ⑤ without `--yes` that is the end ⑥ the wallet unlock is held against the fee —
10
- // scope `storage` for cutting and joining, scope `all` for handing over ⑦ only then the signature.
6
+ // ⛔ THE ORDER IS THE SAFETY, and it is kept over there: ① the resources are READ from the chain and
7
+ // the named ones must be this wallet's ② the rules judge the request and say why not ③ the exact
8
+ // transaction is dry-run for its fee, and a refusal there ends the run the review ⑤ without
9
+ // `--yes` that is the end ⑥ the wallet unlock is held against the fee — scope `storage` for
10
+ // cutting and joining, scope `all` for handing over ⑦ only then the signature. Steps ⑤ and ⑥ are
11
+ // this file's, handed in as the gate.
11
12
  //
12
13
  // ⛔ HANDING OVER MOVES NO FILE. The owner's sentence for the browser holds here: what goes is size
13
14
  // and remaining time; a file's bytes are sealed with keys the NMTS key derives and stay
@@ -18,20 +19,12 @@ import { NmtsError } from "../errors.js";
18
19
  import { resolveNetwork } from "../network.js";
19
20
  import { BINARY_NAME } from "../product.js";
20
21
  import { resolveServer } from "../server.js";
21
- import { canFuse, statusOf } from "../shared/lib/storage-control/plan.js";
22
+ import { reshapeStorage, } from "../storage-control.js";
22
23
  import { explorerTxUrl } from "../shared/lib/wallet/activity.js";
23
- import { isValidSuiAddress } from "../shared/lib/wallet/send-rules.js";
24
- import { storageOpsReads } from "../storage-control-chain.js";
25
24
  import { coinAmount, walletAddress } from "../wallet.js";
26
25
  import { payingWalletIndex } from "../wallet-pay-index.js";
27
26
  import { recordWalletSpend, requireWalletGrant } from "../wallet-grant.js";
28
27
  import { formatBytes } from "./wallet-storage.js";
29
- const FUSE_WHY = {
30
- same: "those are the same resource.",
31
- differentSize: "their periods differ and so do their sizes — the contract joins sizes only over an identical period, and periods only at an identical size.",
32
- differentPeriod: "their periods differ.",
33
- notAdjacent: "their sizes are equal but their periods do not touch — one has to end where the other starts.",
34
- };
35
28
  export async function walletStorageOps(op, rest, options = {}) {
36
29
  const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
37
30
  const resolved = await requireAccountCode();
@@ -42,57 +35,43 @@ export async function walletStorageOps(op, rest, options = {}) {
42
35
  const stored = resolved.source === "file" || resolved.source === "file-locked" ? readCredentialsFile() : null;
43
36
  const server = resolveServer(options.server ?? stored?.server);
44
37
  const network = resolveNetwork(server, options.network ?? stored?.network);
45
- const reads = (options.storageReads ?? storageOpsReads)(network);
46
- // The resources, and which epoch it is.
47
- let read;
48
- try {
49
- read = await reads.readStorage(address);
50
- }
51
- catch (error) {
52
- throw new NmtsError("The storage resources could not be read from the chain.", {
53
- exitCode: 1,
54
- nextStep: `Nothing was signed. Cause: ${error instanceof Error ? error.message : String(error)}`,
55
- });
56
- }
57
- const held = (id) => {
58
- const found = read.items.find((r) => r.objectId === id);
59
- if (found === undefined) {
60
- throw new NmtsError(`This wallet holds no free storage resource ${id}.`, {
61
- exitCode: 4,
62
- nextStep: `\`${BINARY_NAME} wallet storage\` lists the ones it holds. A resource bound inside a file is not free.`,
63
- });
64
- }
65
- return found;
38
+ // What was typed, as values. Everything past this is judged against the chain's own answer.
39
+ const { ask, command } = asked(op, rest, options);
40
+ // ⛔ WITHOUT `--yes` THE RUN STOPS AT THE REVIEW, exactly as `--dry-run` does — the two differ
41
+ // only in what is said afterwards and in the exit code, which is this file's business.
42
+ const stopAtReview = options.dryRun === true || options.yes !== true;
43
+ const outcome = await reshapeStorage({ network, code: resolved.code, wallet, address }, ask, {
44
+ reads: options.storageReads,
45
+ sign: options.signStorage,
46
+ dryRun: stopAtReview,
47
+ // THE SENTENCES A PERSON READS ARE THIS FILE'S. The library judges; what a refusal calls
48
+ // the thing that was wrong — a flag, another command — belongs where somebody typed it.
49
+ hints: {
50
+ cannotRead: "Nothing was signed.",
51
+ notHeld: `\`${BINARY_NAME} wallet storage\` lists the ones it holds. A resource bound inside a file is not free.`,
52
+ cut: (what, limit) => new NmtsError(what === "size"
53
+ ? `--size must be above 0 and below the resource's ${limit}.`
54
+ : `--epochs must be a whole number above 0 and below the resource's ${limit} epochs.`, { exitCode: 2 }),
55
+ },
56
+ onReview: (review) => {
57
+ if (!options.json)
58
+ describe(say, review, wallet);
59
+ },
60
+ // ⑥ The unlock, held against the fee this review measured. ⛔ Throwing here signs nothing.
61
+ agree: (review) => {
62
+ requireWalletGrant(review.plan.action, spendOf(review), new Date(options.now ?? Date.now()));
63
+ },
64
+ });
65
+ const { review } = outcome;
66
+ const facts = {
67
+ op,
68
+ network,
69
+ address,
70
+ shape: review.plan.shape,
71
+ feeSui: review.feeMist === null ? null : coinAmount(review.feeMist),
72
+ currentEpoch: review.currentEpoch,
66
73
  };
67
- // The request, judged.
68
- const { shape, action, lines, command } = plan(op, rest, options, held);
69
- const walrusPackageId = await reads.walrusPackageId();
70
- // ③ The dry run: the fee, or the contract's own refusal.
71
- const verdict = await reads.dryRun(shape, address);
72
- if (verdict.refusal !== null) {
73
- throw new NmtsError(`The chain would refuse this: ${verdict.refusal}`, {
74
- exitCode: 4,
75
- nextStep: "Nothing was signed and no fee was spent.",
76
- });
77
- }
78
- const feeMist = verdict.feeMist;
79
- const facts = { op, network, address, shape, feeSui: feeMist === null ? null : coinAmount(feeMist), currentEpoch: read.currentEpoch };
80
- // ④ The review.
81
- if (!options.json) {
82
- say(`Would ${lines.what}`);
83
- say(` from ${address} (wallet ${wallet})`);
84
- for (const l of lines.detail)
85
- say(` ${l}`);
86
- say(feeMist === null
87
- ? ` Chain fee (SUI): could not be measured just now; it is charged with the signature.`
88
- : ` Chain fee about ${facts.feeSui} SUI, measured by a dry run just now.`);
89
- if (op === "transfer") {
90
- say(` ⛔ No file goes with it. What goes is size and remaining time; the files stay sealed with this`);
91
- say(` account's keys. It cannot be undone, and NMTS cannot recall it. Check the address once more.`);
92
- }
93
- }
94
- // ⑤ Without --yes, that is the end.
95
- if (options.dryRun === true || options.yes !== true) {
74
+ if (outcome.kind === "review") {
96
75
  if (options.json) {
97
76
  say(JSON.stringify({ ...facts, signed: false, dryRun: options.dryRun === true }));
98
77
  return options.dryRun === true ? 0 : 4;
@@ -107,61 +86,86 @@ export async function walletStorageOps(op, rest, options = {}) {
107
86
  nextStep: `Read the review above, then: \`${command} --yes\``,
108
87
  });
109
88
  }
110
- // The unlock, held against the fee. The signature.
111
- const spend = { walFrost: 0n, suiMist: feeMist ?? 0n };
112
- requireWalletGrant(action, spend, new Date(options.now ?? Date.now()));
113
- const sign = options.signStorage ?? (await import("../wallet-sign.js")).signStorageOp;
114
- const digest = await sign({ network, code: resolved.code, wallet, shape, walrusPackageId });
115
- recordWalletSpend(spend);
89
+ // What left the wallet is added to this machine's ledger as the dry run measured it.
90
+ recordWalletSpend(spendOf(review));
116
91
  if (options.json) {
117
- say(JSON.stringify({ ...facts, signed: true, digest, explorerUrl: explorerTxUrl(digest, network) }));
92
+ say(JSON.stringify({ ...facts, signed: true, digest: outcome.digest, explorerUrl: explorerTxUrl(outcome.digest, network) }));
118
93
  return 0;
119
94
  }
120
95
  say(``);
121
- say(` Done. Transaction ${digest}`);
122
- say(` ${explorerTxUrl(digest, network)}`);
96
+ say(` Done. Transaction ${outcome.digest}`);
97
+ say(` ${explorerTxUrl(outcome.digest, network)}`);
123
98
  say(` \`${BINARY_NAME} wallet storage\` lists what the wallet holds now.`);
124
99
  return 0;
125
100
  }
126
- /** The shape one request is, the unlock action it needs, and the review's words. */
127
- function plan(op, rest, options, held) {
101
+ /** What one change costs this wallet: no WAL, and the fee the dry run measured. */
102
+ function spendOf(review) {
103
+ return { walFrost: 0n, suiMist: review.feeMist ?? 0n };
104
+ }
105
+ /** The review a person reads, in the order somebody deciding needs it. */
106
+ function describe(say, review, wallet) {
107
+ const { plan } = review;
108
+ say(`Would ${what(plan)}`);
109
+ say(` from ${review.address} (wallet ${wallet})`);
110
+ for (const line of detail(plan))
111
+ say(` ${line}`);
112
+ say(review.feeMist === null
113
+ ? ` Chain fee (SUI): could not be measured just now; it is charged with the signature.`
114
+ : ` Chain fee about ${coinAmount(review.feeMist)} SUI, measured by a dry run just now.`);
115
+ if (plan.op === "transfer") {
116
+ say(` ⛔ No file goes with it. What goes is size and remaining time; the files stay sealed with this`);
117
+ say(` account's keys. It cannot be undone, and NMTS cannot recall it. Check the address once more.`);
118
+ }
119
+ }
120
+ /** The one-line "what this would do", from the resources as the chain has them. */
121
+ function what(plan) {
122
+ const named = (at) => {
123
+ const r = plan.named[at];
124
+ return r === undefined ? "" : `${formatBytes(r.sizeBytes)} · epoch ${r.startEpoch} to ${r.endEpoch} (${r.objectId})`;
125
+ };
126
+ const result = plan.result;
127
+ if (result.kind === "split")
128
+ return `cut ${named(0)} by ${plan.shape.kind === "splitSize" ? "size" : "period"}.`;
129
+ if (result.kind === "merge")
130
+ return `join ${named(1)} into ${named(0)}.`;
131
+ return `hand ${named(0)} to ${result.to}.`;
132
+ }
133
+ /** What it would leave behind, said from the numbers the plan worked out. */
134
+ function detail(plan) {
135
+ const result = plan.result;
136
+ if (result.kind === "split") {
137
+ const { keeps, creates } = result;
138
+ return plan.shape.kind === "splitSize"
139
+ ? [`It keeps ${formatBytes(keeps.sizeBytes)}; a new resource of ${formatBytes(creates.sizeBytes)} over the same period appears in this wallet.`]
140
+ : [`It keeps epoch ${keeps.startEpoch} to ${keeps.endEpoch}; a new resource of the same size over epoch ${creates.startEpoch} to ${creates.endEpoch} appears in this wallet.`];
141
+ }
142
+ if (result.kind === "merge") {
143
+ const b = result.becomes;
144
+ return [
145
+ result.how === "amount"
146
+ ? `Same period, so the sizes add: the first becomes ${formatBytes(b.sizeBytes)} and the second is gone.`
147
+ : `Same size and touching periods, so the periods join: the first spans epoch ${b.startEpoch} to ${b.endEpoch} and the second is gone.`,
148
+ ];
149
+ }
150
+ return [`Afterwards this wallet no longer holds it.`];
151
+ }
152
+ /** What was typed, as the values the library judges — and the line to run to go through with it. */
153
+ function asked(op, rest, options) {
128
154
  const [a, b] = rest;
129
- const resource = (r) => `${formatBytes(r.sizeBytes)} · epoch ${r.startEpoch} to ${r.endEpoch} (${r.objectId})`;
130
155
  if (op === "split") {
131
156
  if (a === undefined)
132
157
  throw usage("split <resource id> --size <bytes> | --epochs <n>");
133
- const r = held(a);
134
158
  if (options.size !== undefined && options.epochs !== undefined)
135
159
  throw usage("split takes --size OR --epochs, not both");
136
160
  if (options.size !== undefined) {
137
- const keep = parseBytes(options.size);
138
- if (keep <= 0 || keep >= r.sizeBytes) {
139
- throw new NmtsError(`--size must be above 0 and below the resource's ${formatBytes(r.sizeBytes)}.`, { exitCode: 2 });
140
- }
141
161
  return {
142
- shape: { kind: "splitSize", objectId: a, keepBytes: keep },
143
- action: "reshape",
144
- lines: {
145
- what: `cut ${resource(r)} by size.`,
146
- detail: [`It keeps ${formatBytes(keep)}; a new resource of ${formatBytes(r.sizeBytes - keep)} over the same period appears in this wallet.`],
147
- },
162
+ ask: { kind: "splitSize", objectId: a, keepBytes: parseBytes(options.size) },
148
163
  command: `${BINARY_NAME} wallet storage split ${a} --size ${options.size}`,
149
164
  };
150
165
  }
151
166
  if (options.epochs !== undefined) {
152
- const n = Number(options.epochs);
153
- const span = r.endEpoch - r.startEpoch;
154
- if (!Number.isSafeInteger(n) || n <= 0 || n >= span) {
155
- throw new NmtsError(`--epochs must be a whole number above 0 and below the resource's ${span} epochs.`, { exitCode: 2 });
156
- }
157
- const at = r.startEpoch + n;
158
167
  return {
159
- shape: { kind: "splitEpoch", objectId: a, splitEpoch: at },
160
- action: "reshape",
161
- lines: {
162
- what: `cut ${resource(r)} by period.`,
163
- detail: [`It keeps epoch ${r.startEpoch} to ${at}; a new resource of the same size over epoch ${at} to ${r.endEpoch} appears in this wallet.`],
164
- },
168
+ ask: { kind: "splitEpochs", objectId: a, keepEpochs: Number(options.epochs) },
165
169
  command: `${BINARY_NAME} wallet storage split ${a} --epochs ${options.epochs}`,
166
170
  };
167
171
  }
@@ -170,41 +174,11 @@ function plan(op, rest, options, held) {
170
174
  if (op === "merge") {
171
175
  if (a === undefined || b === undefined)
172
176
  throw usage("merge <resource id> <resource id>");
173
- const first = held(a);
174
- const second = held(b);
175
- const verdict = canFuse(first, second);
176
- if (!verdict.can) {
177
- throw new NmtsError(`These two cannot be joined: ${FUSE_WHY[verdict.why]}`, {
178
- exitCode: 4,
179
- nextStep: `Nothing was signed. \`${BINARY_NAME} wallet storage\` shows each one's size and period.`,
180
- });
181
- }
182
- return {
183
- shape: { kind: "fuse", first: a, second: b, how: verdict.kind },
184
- action: "reshape",
185
- lines: {
186
- what: `join ${resource(second)} into ${resource(first)}.`,
187
- detail: [
188
- verdict.kind === "amount"
189
- ? `Same period, so the sizes add: the first becomes ${formatBytes(first.sizeBytes + second.sizeBytes)} and the second is gone.`
190
- : `Same size and touching periods, so the periods join: the first spans epoch ${Math.min(first.startEpoch, second.startEpoch)} to ${Math.max(first.endEpoch, second.endEpoch)} and the second is gone.`,
191
- ],
192
- },
193
- command: `${BINARY_NAME} wallet storage merge ${a} ${b}`,
194
- };
177
+ return { ask: { kind: "merge", first: a, second: b }, command: `${BINARY_NAME} wallet storage merge ${a} ${b}` };
195
178
  }
196
179
  if (a === undefined || b === undefined)
197
180
  throw usage("transfer <resource id> <address>");
198
- const r = held(a);
199
- if (!isValidSuiAddress(b)) {
200
- throw new NmtsError("That is not a Sui address: it is 0x followed by 64 hexadecimal characters.", { exitCode: 2 });
201
- }
202
- return {
203
- shape: { kind: "transfer", objectId: a, to: b },
204
- action: "give",
205
- lines: { what: `hand ${resource(r)} to ${b}.`, detail: [`Afterwards this wallet no longer holds it.`] },
206
- command: `${BINARY_NAME} wallet storage transfer ${a} ${b}`,
207
- };
181
+ return { ask: { kind: "transfer", objectId: a, to: b }, command: `${BINARY_NAME} wallet storage transfer ${a} ${b}` };
208
182
  }
209
183
  function usage(form) {
210
184
  return new NmtsError(`\`${BINARY_NAME} wallet storage ${form}\``, { exitCode: 2 });
@@ -1,10 +1,7 @@
1
1
  import { type Network } from "../network.ts";
2
- import type { StorageResource } from "../shared/lib/storage-control/chain.ts";
3
- /** What the chain said: the resources, and which epoch it is (null when the clock could not be read). */
4
- export interface StorageRead {
5
- items: readonly StorageResource[];
6
- currentEpoch: number | null;
7
- }
2
+ import { type StorageRead } from "../storage-control.ts";
3
+ export type { StorageRead } from "../storage-control.ts";
4
+ export { formatBytes } from "../storage-control.ts";
8
5
  export interface WalletStorageOptions {
9
6
  server?: string | undefined;
10
7
  network?: string | undefined;
@@ -14,5 +11,3 @@ export interface WalletStorageOptions {
14
11
  readStorage?: (network: Network, address: string) => Promise<StorageRead>;
15
12
  }
16
13
  export declare function walletStorage(options?: WalletStorageOptions): Promise<number>;
17
- /** Bytes for a person: binary units, two decimals, whole bytes below a KiB. */
18
- export declare function formatBytes(bytes: number): string;
@@ -6,16 +6,16 @@
6
6
  // fusing and spending a resource need a signature and are not here.
7
7
  //
8
8
  // ⛔ "NONE" AND "COULD NOT READ" ARE DIFFERENT SENTENCES — holding no resource is normal; failing to
9
- // read is a reason to look again. The reader (`shared/lib/storage-control/chain.ts`, the
10
- // browser's own, copied byte-for-byte) throws rather than answering an empty list.
9
+ // read is a reason to look again. The reading, the ordering and that refusal are
10
+ // `storage-control.ts`, which the SDK calls as well; this file is the terminal over it.
11
11
  import { requireAccountCode } from "../code-access.js";
12
12
  import { readCredentialsFile } from "../credentials.js";
13
- import { NmtsError } from "../errors.js";
14
13
  import { resolveNetwork } from "../network.js";
15
14
  import { BINARY_NAME } from "../product.js";
15
+ import { formatBytes, listStorage, } from "../storage-control.js";
16
16
  import { resolveServer } from "../server.js";
17
- import { statusOf, totalUsableBytes, usableFirst } from "../shared/lib/storage-control/plan.js";
18
17
  import { walletAddress } from "../wallet.js";
18
+ export { formatBytes } from "../storage-control.js";
19
19
  export async function walletStorage(options = {}) {
20
20
  const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
21
21
  const resolved = await requireAccountCode();
@@ -23,33 +23,25 @@ export async function walletStorage(options = {}) {
23
23
  const stored = resolved.source === "file" || resolved.source === "file-locked" ? readCredentialsFile() : null;
24
24
  const server = resolveServer(options.server ?? stored?.server);
25
25
  const network = resolveNetwork(server, options.network ?? stored?.network);
26
- const read = options.readStorage ??
27
- (async (net, addr) => (await import("../wallet-storage-chain.js")).readWalletStorage(net, addr));
28
- let got;
29
- try {
30
- got = await read(network, address);
31
- }
32
- catch (error) {
33
- throw new NmtsError("The storage resources could not be read from the chain.", {
34
- exitCode: 1,
35
- nextStep: `That is not the same as holding none. \`${BINARY_NAME} env\` says which network was asked. ` +
36
- `Cause: ${error instanceof Error ? error.message : String(error)}`,
37
- });
38
- }
39
- const epoch = got.currentEpoch;
40
- const items = epoch === null ? [...got.items] : usableFirst(got.items, epoch);
26
+ // THE SENTENCE A PERSON READS IS THIS FILE'S, not the library's: `nmts env` is a command at a
27
+ // prompt and means nothing inside somebody else's program.
28
+ const listed = await listStorage({ network, address }, options.readStorage, {
29
+ cannotRead: `That is not the same as holding none. \`${BINARY_NAME} env\` says which network was asked.`,
30
+ });
31
+ const epoch = listed.currentEpoch;
32
+ const items = listed.items;
41
33
  if (options.json) {
42
34
  say(JSON.stringify({
43
35
  address,
44
36
  network,
45
37
  currentEpoch: epoch,
46
- usableBytes: epoch === null ? null : totalUsableBytes(items, epoch),
38
+ usableBytes: listed.usableBytes,
47
39
  resources: items.map((r) => ({
48
40
  objectId: r.objectId,
49
41
  sizeBytes: r.sizeBytes,
50
42
  startEpoch: r.startEpoch,
51
43
  endEpoch: r.endEpoch,
52
- status: epoch === null ? null : statusOf(r, epoch),
44
+ status: r.status,
53
45
  })),
54
46
  }));
55
47
  return 0;
@@ -61,10 +53,10 @@ export async function walletStorage(options = {}) {
61
53
  say(` This wallet holds no free storage resource. Anything bound inside a file is not listed here.`);
62
54
  }
63
55
  else {
64
- if (epoch !== null)
65
- say(` ${formatBytes(totalUsableBytes(items, epoch))} usable now, in ${items.length} resource${items.length === 1 ? "" : "s"}.`);
56
+ if (listed.usableBytes !== null)
57
+ say(` ${formatBytes(listed.usableBytes)} usable now, in ${items.length} resource${items.length === 1 ? "" : "s"}.`);
66
58
  for (const r of items) {
67
- const status = epoch === null ? "" : ` ${statusWord(statusOf(r, epoch))}`;
59
+ const status = r.status === null ? "" : ` ${statusWord(r.status)}`;
68
60
  say(` ${formatBytes(r.sizeBytes).padStart(11)} · epoch ${r.startEpoch} to ${r.endEpoch}${status}`);
69
61
  say(` ${r.objectId}`);
70
62
  }
@@ -80,16 +72,3 @@ export async function walletStorage(options = {}) {
80
72
  function statusWord(status) {
81
73
  return status === "usable" ? "usable now" : status === "notYet" ? "not started" : "ended";
82
74
  }
83
- /** Bytes for a person: binary units, two decimals, whole bytes below a KiB. */
84
- export function formatBytes(bytes) {
85
- const units = ["KiB", "MiB", "GiB", "TiB"];
86
- let value = bytes;
87
- let unit = "B";
88
- for (const next of units) {
89
- if (value < 1024)
90
- break;
91
- value /= 1024;
92
- unit = next;
93
- }
94
- return unit === "B" ? `${bytes} B` : `${value.toFixed(2)} ${unit}`;
95
- }
@@ -0,0 +1,83 @@
1
+ import { type ListEditInput } from "./manifest-write.ts";
2
+ /** What the erasing underneath takes: where to talk, what opens the list, and whose list it is. */
3
+ export interface EraseInput extends ListEditInput {
4
+ /**
5
+ * The account code's own proof for this one run, base64url — what the two permanent doors ask
6
+ * for beside the credential (`x-nmts-account-proof`).
7
+ *
8
+ * ⛔ BUILT BY THE CALLER AND KEPT BY NOBODY. It is not the NMTS key and opens no file; what it
9
+ * proves is possession of the code, which is exactly the question these two doors ask.
10
+ */
11
+ accountProof: string;
12
+ }
13
+ export interface EraseOptions {
14
+ /** Also destroy the treasury's storage under credit-paid files, before erasing them. */
15
+ releaseStorage?: boolean;
16
+ }
17
+ /** One thing this run acts on: its id, and where it sits in the list as it was read. */
18
+ export interface ErasePath {
19
+ id: string;
20
+ path: string;
21
+ }
22
+ /** What one run will destroy, worked out before anything is sent. */
23
+ export interface ErasePlan {
24
+ /** Every FILE going: the ones named, and every file under a folder that was named. */
25
+ readonly files: readonly ErasePath[];
26
+ /**
27
+ * The ids leaving the sealed list — the files above and the folders that were named.
28
+ *
29
+ * ⚠ WIDER THAN `files` ON PURPOSE. A named folder has no server row of its own, so nothing is
30
+ * erased for it; its entry still has to go, or the list keeps a folder whose contents are gone.
31
+ */
32
+ readonly going: readonly string[];
33
+ }
34
+ /** What one file's storage release came back with. */
35
+ export interface StorageRelease {
36
+ path: string;
37
+ released: number;
38
+ alreadyReleased: number;
39
+ failed: number;
40
+ /**
41
+ * Credits the release actually cost, and where they came from.
42
+ *
43
+ * ⛔ TWO FIELDS, NOT ONE, because "it cost nothing" and "it cost nothing OUT OF THE BALANCE" are
44
+ * different answers and only one of them is true. A release paid out of the file's own
45
+ * deposit charges the balance nothing; a file with no deposit pays twice the fee from the
46
+ * balance, and somebody watching their credits needs to be able to tell which happened.
47
+ */
48
+ feeCredits: number;
49
+ fromDeposit: boolean;
50
+ /** The server's typed refusal when the storage was not the treasury's to destroy. */
51
+ refused: string | null;
52
+ }
53
+ /** What one run did. */
54
+ export interface EraseOutcome {
55
+ /** How many server rows went. Lower than what was asked for when one was already gone. */
56
+ erased: number;
57
+ /** The files it acted on, with the paths they had. */
58
+ files: ErasePath[];
59
+ /** One per file whose storage was asked about — empty unless `releaseStorage` was asked for. */
60
+ releases: StorageRelease[];
61
+ /** The version of the sealed list after the entries left it. */
62
+ seq: number;
63
+ }
64
+ /**
65
+ * Work out what erasing these paths would destroy, without destroying anything.
66
+ *
67
+ * ⛔ ITS OWN STEP BECAUSE ONE CALLER HAS TO SHOW THE LIST BEFORE IT ASKS. `nmts erase` prints every
68
+ * file it is about to destroy and then waits for a typed sentence; folding this into the act
69
+ * would leave the terminal with nothing to print, and reading the list twice would leave the
70
+ * two reads free to disagree about what is in it.
71
+ */
72
+ export declare function planErase(input: ListEditInput, paths: readonly string[]): Promise<ErasePlan>;
73
+ /**
74
+ * Destroy what the plan names. ⛔ Irreversible, and nothing below asks whether the caller meant it.
75
+ */
76
+ export declare function eraseFiles(input: EraseInput, plan: ErasePlan, options?: EraseOptions): Promise<EraseOutcome>;
77
+ /**
78
+ * Resolve these paths and destroy what they name, in one call.
79
+ *
80
+ * ⚠ FOR A CALLER THAT HAS ALREADY DECIDED. Anything that shows a person what is about to go should
81
+ * use `planErase` first, so what it shows is what it then destroys.
82
+ */
83
+ export declare function erasePaths(input: EraseInput, paths: readonly string[], options?: EraseOptions): Promise<EraseOutcome>;