@sm-lab/merkle 1.2.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/cli.mjs ADDED
@@ -0,0 +1,796 @@
1
+ #!/usr/bin/env node
2
+ import { m as readJsonFile, n as makeRewards, p as readAddressFile, r as makeStrikes, t as makeAddresses } from "./pipelines-Dxpjkr0j.mjs";
3
+ import { readFileSync } from "node:fs";
4
+ import "dotenv/config";
5
+ import { Argument, Command } from "commander";
6
+ import { Readable } from "node:stream";
7
+ //#endregion
8
+ //#region ../../node_modules/.pnpm/@hono+node-server@2.0.6_hono@4.12.27/node_modules/@hono/node-server/dist/index.mjs
9
+ const GlobalRequest = global.Request;
10
+ var Request$1 = class extends GlobalRequest {
11
+ constructor(input, options) {
12
+ if (typeof input === "object" && getRequestCache in input) {
13
+ const hasReplacementBody = options !== void 0 && "body" in options && options.body != null;
14
+ if (input[bodyConsumedDirectlyKey] && !hasReplacementBody) throw new TypeError("Cannot construct a Request with a Request object that has already been used.");
15
+ input = input[getRequestCache]();
16
+ }
17
+ if (typeof (options?.body)?.getReader !== "undefined") options.duplex ??= "half";
18
+ super(input, options);
19
+ }
20
+ };
21
+ const newHeadersFromIncoming = (incoming) => {
22
+ const headerRecord = [];
23
+ const rawHeaders = incoming.rawHeaders;
24
+ for (let i = 0, len = rawHeaders.length; i < len; i += 2) {
25
+ const key = rawHeaders[i];
26
+ if (key.charCodeAt(0) !== 58) headerRecord.push([key, rawHeaders[i + 1]]);
27
+ }
28
+ return new Headers(headerRecord);
29
+ };
30
+ const wrapBodyStream = Symbol("wrapBodyStream");
31
+ const newRequestFromIncoming = (method, url, headers, incoming, abortController) => {
32
+ const init = {
33
+ method,
34
+ headers,
35
+ signal: abortController.signal
36
+ };
37
+ if (method === "TRACE") {
38
+ init.method = "GET";
39
+ const req = new Request$1(url, init);
40
+ Object.defineProperty(req, "method", { get() {
41
+ return "TRACE";
42
+ } });
43
+ return req;
44
+ }
45
+ if (!(method === "GET" || method === "HEAD")) if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) init.body = new ReadableStream({ start(controller) {
46
+ controller.enqueue(incoming.rawBody);
47
+ controller.close();
48
+ } });
49
+ else if (incoming[wrapBodyStream]) {
50
+ let reader;
51
+ init.body = new ReadableStream({ async pull(controller) {
52
+ try {
53
+ reader ||= Readable.toWeb(incoming).getReader();
54
+ const { done, value } = await reader.read();
55
+ if (done) controller.close();
56
+ else controller.enqueue(value);
57
+ } catch (error) {
58
+ controller.error(error);
59
+ }
60
+ } });
61
+ } else init.body = Readable.toWeb(incoming);
62
+ return new Request$1(url, init);
63
+ };
64
+ const getRequestCache = Symbol("getRequestCache");
65
+ const requestCache = Symbol("requestCache");
66
+ const incomingKey = Symbol("incomingKey");
67
+ const urlKey = Symbol("urlKey");
68
+ const methodKey = Symbol("methodKey");
69
+ const headersKey = Symbol("headersKey");
70
+ const abortControllerKey = Symbol("abortControllerKey");
71
+ const getAbortController = Symbol("getAbortController");
72
+ const abortRequest = Symbol("abortRequest");
73
+ const bodyBufferKey = Symbol("bodyBuffer");
74
+ const bodyReadPromiseKey = Symbol("bodyReadPromise");
75
+ const bodyConsumedDirectlyKey = Symbol("bodyConsumedDirectly");
76
+ const bodyLockReaderKey = Symbol("bodyLockReader");
77
+ const abortReasonKey = Symbol("abortReason");
78
+ const newBodyUnusableError = () => {
79
+ return /* @__PURE__ */ new TypeError("Body is unusable");
80
+ };
81
+ const rejectBodyUnusable = () => {
82
+ return Promise.reject(newBodyUnusableError());
83
+ };
84
+ const textDecoder = new TextDecoder();
85
+ const consumeBodyDirectOnce = (request) => {
86
+ if (request[bodyConsumedDirectlyKey]) return rejectBodyUnusable();
87
+ request[bodyConsumedDirectlyKey] = true;
88
+ };
89
+ const toArrayBuffer = (buf) => {
90
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
91
+ };
92
+ const contentType = (request) => {
93
+ return (request[headersKey] ||= newHeadersFromIncoming(request[incomingKey])).get("content-type") || "";
94
+ };
95
+ const methodTokenRegExp = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
96
+ const validateDirectReadMethod = (method) => {
97
+ if (!methodTokenRegExp.test(method)) return /* @__PURE__ */ new TypeError(`'${method}' is not a valid HTTP method.`);
98
+ const normalized = method.toUpperCase();
99
+ if (normalized === "CONNECT" || normalized === "TRACK" || normalized === "TRACE" && method !== "TRACE") return /* @__PURE__ */ new TypeError(`'${method}' HTTP method is unsupported.`);
100
+ };
101
+ const readBodyWithFastPath = (request, method, fromBuffer) => {
102
+ if (request[bodyConsumedDirectlyKey]) return rejectBodyUnusable();
103
+ const methodName = request.method;
104
+ if (methodName === "GET" || methodName === "HEAD") return request[getRequestCache]()[method]();
105
+ const methodValidationError = validateDirectReadMethod(methodName);
106
+ if (methodValidationError) return Promise.reject(methodValidationError);
107
+ if (request[requestCache]) {
108
+ if (methodName !== "TRACE") return request[requestCache][method]();
109
+ }
110
+ const alreadyUsedError = consumeBodyDirectOnce(request);
111
+ if (alreadyUsedError) return alreadyUsedError;
112
+ const raw = readRawBodyIfAvailable(request);
113
+ if (raw) {
114
+ const result = Promise.resolve(fromBuffer(raw, request));
115
+ request[bodyBufferKey] = void 0;
116
+ return result;
117
+ }
118
+ return readBodyDirect(request).then((buf) => {
119
+ const result = fromBuffer(buf, request);
120
+ request[bodyBufferKey] = void 0;
121
+ return result;
122
+ });
123
+ };
124
+ const readRawBodyIfAvailable = (request) => {
125
+ const incoming = request[incomingKey];
126
+ if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) return incoming.rawBody;
127
+ };
128
+ const readBodyDirect = (request) => {
129
+ if (request[bodyBufferKey]) return Promise.resolve(request[bodyBufferKey]);
130
+ if (request[bodyReadPromiseKey]) return request[bodyReadPromiseKey];
131
+ const incoming = request[incomingKey];
132
+ if (Readable.isDisturbed(incoming)) return rejectBodyUnusable();
133
+ const promise = new Promise((resolve, reject) => {
134
+ const chunks = [];
135
+ let settled = false;
136
+ const finish = (callback) => {
137
+ if (settled) return;
138
+ settled = true;
139
+ cleanup();
140
+ callback();
141
+ };
142
+ const onData = (chunk) => {
143
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
144
+ };
145
+ const onEnd = () => {
146
+ finish(() => {
147
+ const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
148
+ request[bodyBufferKey] = buffer;
149
+ resolve(buffer);
150
+ });
151
+ };
152
+ const onError = (error) => {
153
+ finish(() => {
154
+ reject(error);
155
+ });
156
+ };
157
+ const onClose = () => {
158
+ if (incoming.readableEnded) {
159
+ onEnd();
160
+ return;
161
+ }
162
+ finish(() => {
163
+ if (incoming.errored) {
164
+ reject(incoming.errored);
165
+ return;
166
+ }
167
+ const reason = request[abortReasonKey];
168
+ if (reason !== void 0) {
169
+ reject(reason instanceof Error ? reason : new Error(String(reason)));
170
+ return;
171
+ }
172
+ reject(/* @__PURE__ */ new Error("Client connection prematurely closed."));
173
+ });
174
+ };
175
+ const cleanup = () => {
176
+ incoming.off("data", onData);
177
+ incoming.off("end", onEnd);
178
+ incoming.off("error", onError);
179
+ incoming.off("close", onClose);
180
+ request[bodyReadPromiseKey] = void 0;
181
+ };
182
+ incoming.on("data", onData);
183
+ incoming.on("end", onEnd);
184
+ incoming.on("error", onError);
185
+ incoming.on("close", onClose);
186
+ queueMicrotask(() => {
187
+ if (settled) return;
188
+ if (incoming.readableEnded) onEnd();
189
+ else if (incoming.errored) onError(incoming.errored);
190
+ else if (incoming.destroyed) onClose();
191
+ });
192
+ });
193
+ request[bodyReadPromiseKey] = promise;
194
+ return promise;
195
+ };
196
+ const requestPrototype = {
197
+ get method() {
198
+ return this[methodKey];
199
+ },
200
+ get url() {
201
+ return this[urlKey];
202
+ },
203
+ get headers() {
204
+ return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);
205
+ },
206
+ [abortRequest](reason) {
207
+ if (this[abortReasonKey] === void 0) this[abortReasonKey] = reason;
208
+ const abortController = this[abortControllerKey];
209
+ if (abortController && !abortController.signal.aborted) abortController.abort(reason);
210
+ },
211
+ [getAbortController]() {
212
+ this[abortControllerKey] ||= new AbortController();
213
+ if (this[abortReasonKey] !== void 0 && !this[abortControllerKey].signal.aborted) this[abortControllerKey].abort(this[abortReasonKey]);
214
+ return this[abortControllerKey];
215
+ },
216
+ [getRequestCache]() {
217
+ const abortController = this[getAbortController]();
218
+ if (this[requestCache]) return this[requestCache];
219
+ const method = this.method;
220
+ if (this[bodyConsumedDirectlyKey] && !(method === "GET" || method === "HEAD")) {
221
+ this[bodyBufferKey] = void 0;
222
+ const init = {
223
+ method: method === "TRACE" ? "GET" : method,
224
+ headers: this.headers,
225
+ signal: abortController.signal
226
+ };
227
+ if (method !== "TRACE") {
228
+ init.body = new ReadableStream({ start(c) {
229
+ c.close();
230
+ } });
231
+ init.duplex = "half";
232
+ }
233
+ const req = new Request$1(this[urlKey], init);
234
+ if (method === "TRACE") Object.defineProperty(req, "method", { get() {
235
+ return "TRACE";
236
+ } });
237
+ return this[requestCache] = req;
238
+ }
239
+ return this[requestCache] = newRequestFromIncoming(this.method, this[urlKey], this.headers, this[incomingKey], abortController);
240
+ },
241
+ get body() {
242
+ if (!this[bodyConsumedDirectlyKey]) return this[getRequestCache]().body;
243
+ const request = this[getRequestCache]();
244
+ if (!this[bodyLockReaderKey] && request.body) this[bodyLockReaderKey] = request.body.getReader();
245
+ return request.body;
246
+ },
247
+ get bodyUsed() {
248
+ if (this[bodyConsumedDirectlyKey]) return true;
249
+ if (this[requestCache]) return this[requestCache].bodyUsed;
250
+ return false;
251
+ }
252
+ };
253
+ Object.defineProperty(requestPrototype, "signal", { get() {
254
+ return this[getAbortController]().signal;
255
+ } });
256
+ [
257
+ "cache",
258
+ "credentials",
259
+ "destination",
260
+ "integrity",
261
+ "mode",
262
+ "redirect",
263
+ "referrer",
264
+ "referrerPolicy",
265
+ "keepalive"
266
+ ].forEach((k) => {
267
+ Object.defineProperty(requestPrototype, k, { get() {
268
+ return this[getRequestCache]()[k];
269
+ } });
270
+ });
271
+ ["clone", "formData"].forEach((k) => {
272
+ Object.defineProperty(requestPrototype, k, { value: function() {
273
+ if (this[bodyConsumedDirectlyKey]) {
274
+ if (k === "clone") throw newBodyUnusableError();
275
+ return rejectBodyUnusable();
276
+ }
277
+ return this[getRequestCache]()[k]();
278
+ } });
279
+ });
280
+ Object.defineProperty(requestPrototype, "text", { value: function() {
281
+ return readBodyWithFastPath(this, "text", (buf) => textDecoder.decode(buf));
282
+ } });
283
+ Object.defineProperty(requestPrototype, "arrayBuffer", { value: function() {
284
+ return readBodyWithFastPath(this, "arrayBuffer", (buf) => toArrayBuffer(buf));
285
+ } });
286
+ Object.defineProperty(requestPrototype, "blob", { value: function() {
287
+ return readBodyWithFastPath(this, "blob", (buf, request) => {
288
+ const type = contentType(request);
289
+ return new Response(buf, type ? { headers: { "content-type": type } } : void 0).blob();
290
+ });
291
+ } });
292
+ Object.defineProperty(requestPrototype, "json", { value: function() {
293
+ if (this[bodyConsumedDirectlyKey]) return rejectBodyUnusable();
294
+ return this.text().then(JSON.parse);
295
+ } });
296
+ Object.defineProperty(requestPrototype, Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) {
297
+ return `Request (lightweight) ${inspectFn({
298
+ method: this.method,
299
+ url: this.url,
300
+ headers: this.headers,
301
+ nativeRequest: this[requestCache]
302
+ }, {
303
+ ...options,
304
+ depth: depth == null ? null : depth - 1
305
+ })}`;
306
+ } });
307
+ Object.setPrototypeOf(requestPrototype, Request$1.prototype);
308
+ const defaultContentType = "text/plain; charset=UTF-8";
309
+ const responseCache = Symbol("responseCache");
310
+ const getResponseCache = Symbol("getResponseCache");
311
+ const cacheKey = Symbol("cache");
312
+ const GlobalResponse = global.Response;
313
+ var Response$1 = class Response$1 {
314
+ #body;
315
+ #init;
316
+ [getResponseCache]() {
317
+ const cache = this[cacheKey];
318
+ const liveHeaders = cache && cache[2] instanceof Headers ? cache[2] : void 0;
319
+ delete this[cacheKey];
320
+ return this[responseCache] ||= new GlobalResponse(this.#body, liveHeaders ? {
321
+ status: this.#init?.status,
322
+ statusText: this.#init?.statusText,
323
+ headers: liveHeaders
324
+ } : this.#init);
325
+ }
326
+ constructor(body, init) {
327
+ let headers;
328
+ this.#body = body;
329
+ if (init instanceof Response$1) {
330
+ const cachedGlobalResponse = init[responseCache];
331
+ if (cachedGlobalResponse) {
332
+ this.#init = cachedGlobalResponse;
333
+ this[getResponseCache]();
334
+ return;
335
+ } else {
336
+ this.#init = init.#init;
337
+ headers = new Headers(init.headers);
338
+ }
339
+ } else this.#init = init;
340
+ if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
341
+ init?.status || 200,
342
+ body ?? null,
343
+ headers || init?.headers
344
+ ];
345
+ }
346
+ get headers() {
347
+ const cache = this[cacheKey];
348
+ if (cache) {
349
+ if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2] || (cache[1] === null ? void 0 : { "content-type": defaultContentType }));
350
+ return cache[2];
351
+ }
352
+ return this[getResponseCache]().headers;
353
+ }
354
+ get status() {
355
+ return this[cacheKey]?.[0] ?? this[getResponseCache]().status;
356
+ }
357
+ get ok() {
358
+ const status = this.status;
359
+ return status >= 200 && status < 300;
360
+ }
361
+ };
362
+ [
363
+ "body",
364
+ "bodyUsed",
365
+ "redirected",
366
+ "statusText",
367
+ "trailers",
368
+ "type",
369
+ "url"
370
+ ].forEach((k) => {
371
+ Object.defineProperty(Response$1.prototype, k, { get() {
372
+ return this[getResponseCache]()[k];
373
+ } });
374
+ });
375
+ [
376
+ "arrayBuffer",
377
+ "blob",
378
+ "clone",
379
+ "formData",
380
+ "json",
381
+ "text"
382
+ ].forEach((k) => {
383
+ Object.defineProperty(Response$1.prototype, k, { value: function() {
384
+ return this[getResponseCache]()[k]();
385
+ } });
386
+ });
387
+ Object.defineProperty(Response$1.prototype, Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) {
388
+ return `Response (lightweight) ${inspectFn({
389
+ status: this.status,
390
+ headers: this.headers,
391
+ ok: this.ok,
392
+ nativeResponse: this[responseCache]
393
+ }, {
394
+ ...options,
395
+ depth: depth == null ? null : depth - 1
396
+ })}`;
397
+ } });
398
+ Object.setPrototypeOf(Response$1, GlobalResponse);
399
+ Object.setPrototypeOf(Response$1.prototype, GlobalResponse.prototype);
400
+ const validRedirectUrl = /^https?:\/\/[!#-;=?-[\]_a-z~A-Z]+$/;
401
+ const parseRedirectUrl = (url) => {
402
+ if (url instanceof URL) return url.href;
403
+ if (validRedirectUrl.test(url)) return url;
404
+ return new URL(url).href;
405
+ };
406
+ const validRedirectStatuses = /* @__PURE__ */ new Set([
407
+ 301,
408
+ 302,
409
+ 303,
410
+ 307,
411
+ 308
412
+ ]);
413
+ Object.defineProperty(Response$1, "redirect", {
414
+ value: function redirect(url, status = 302) {
415
+ if (!validRedirectStatuses.has(status)) throw new RangeError("Invalid status code");
416
+ return new Response$1(null, {
417
+ status,
418
+ headers: { location: parseRedirectUrl(url) }
419
+ });
420
+ },
421
+ writable: true,
422
+ configurable: true
423
+ });
424
+ Object.defineProperty(Response$1, "json", {
425
+ value: function json(data, init) {
426
+ const body = JSON.stringify(data);
427
+ if (body === void 0) throw new TypeError("The data is not JSON serializable");
428
+ const initHeaders = init?.headers;
429
+ let headers;
430
+ if (initHeaders) {
431
+ headers = new Headers(initHeaders);
432
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
433
+ } else headers = { "content-type": "application/json" };
434
+ return new Response$1(body, {
435
+ status: init?.status ?? 200,
436
+ statusText: init?.statusText,
437
+ headers
438
+ });
439
+ },
440
+ writable: true,
441
+ configurable: true
442
+ });
443
+ globalThis.CloseEvent;
444
+ //#endregion
445
+ //#region ../../packages/core/src/admin.ts
446
+ /**
447
+ * Read the consuming package's version from its package.json at runtime.
448
+ *
449
+ * Pass `import.meta.url` from the CONSUMER module. core is bundled into each consumer, so
450
+ * after the build this code runs from the consumer's `dist/` and package.json sits one
451
+ * level up (`../package.json`). Never throws — returns 'unknown' on any failure.
452
+ */
453
+ function readPackageVersion(metaUrl) {
454
+ try {
455
+ return JSON.parse(readFileSync(new URL("../package.json", metaUrl), "utf8")).version ?? "unknown";
456
+ } catch {
457
+ return "unknown";
458
+ }
459
+ }
460
+ //#endregion
461
+ //#region ../../packages/core/src/cli.ts
462
+ /** Climb to the root program so commands at any nesting depth can read root-level options (e.g. --url). */
463
+ function findRoot(cmd) {
464
+ let c = cmd;
465
+ while (c.parent) c = c.parent;
466
+ return c;
467
+ }
468
+ //#endregion
469
+ //#region ../../packages/core/src/completion.ts
470
+ const SHELLS = [
471
+ "bash",
472
+ "zsh",
473
+ "fish"
474
+ ];
475
+ function firstLine(text) {
476
+ return (text.split("\n")[0] ?? "").trim();
477
+ }
478
+ function toNode(cmd, path) {
479
+ return {
480
+ name: cmd.name(),
481
+ aliases: [...cmd.aliases()],
482
+ description: firstLine(cmd.description()),
483
+ path,
484
+ options: cmd.options.filter((o) => !o.hidden).map((o) => ({
485
+ short: o.short,
486
+ long: o.long,
487
+ description: firstLine(o.description),
488
+ takesValue: o.required || o.optional,
489
+ choices: o.argChoices ? [...o.argChoices] : void 0
490
+ })),
491
+ args: cmd.registeredArguments.map((a) => ({
492
+ name: a.name(),
493
+ description: firstLine(a.description),
494
+ variadic: a.variadic,
495
+ choices: a.argChoices ? [...a.argChoices] : void 0
496
+ })),
497
+ children: cmd.commands.map((c) => toNode(c, [...path, c.name()]))
498
+ };
499
+ }
500
+ /** Flatten in registration order — the emitters iterate this, so output is deterministic. */
501
+ function walk(node) {
502
+ return [node, ...node.children.flatMap(walk)];
503
+ }
504
+ function namesOf(node) {
505
+ return [node.name, ...node.aliases];
506
+ }
507
+ function fishQuote(s) {
508
+ return `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
509
+ }
510
+ function emitFish(bin, root) {
511
+ const lines = [
512
+ `# fish completion for ${bin}`,
513
+ `# Load with: ${bin} completion fish | source`,
514
+ `# Or install: ${bin} completion fish > ~/.config/fish/completions/${bin}.fish`
515
+ ];
516
+ for (const node of walk(root)) {
517
+ const seen = node.path.map((p) => `__fish_seen_subcommand_from ${p}`);
518
+ const childNames = node.children.flatMap(namesOf);
519
+ const hereParts = [...seen];
520
+ if (childNames.length > 0) hereParts.push(`not __fish_seen_subcommand_from ${childNames.join(" ")}`);
521
+ const here = hereParts.length > 0 ? ` -n ${fishQuote(hereParts.join("; and "))}` : "";
522
+ for (const child of node.children) for (const name of namesOf(child)) lines.push(`complete -c ${bin}${here} -f -a ${name} -d ${fishQuote(child.description)}`);
523
+ for (const opt of node.options) {
524
+ let line = `complete -c ${bin}${here}`;
525
+ if (opt.short) line += ` -s ${opt.short.replace(/^-+/, "")}`;
526
+ if (opt.long) line += ` -l ${opt.long.replace(/^-+/, "")}`;
527
+ if (opt.takesValue) line += opt.choices ? ` -x -a ${fishQuote(opt.choices.join(" "))}` : " -r";
528
+ line += ` -d ${fishQuote(opt.description)}`;
529
+ lines.push(line);
530
+ }
531
+ const atArgs = seen.length > 0 ? ` -n ${fishQuote(seen.join("; and "))}` : "";
532
+ for (const arg of node.args) if (arg.choices) {
533
+ const desc = arg.description || arg.name;
534
+ lines.push(`complete -c ${bin}${atArgs} -f -a ${fishQuote(arg.choices.join(" "))} -d ${fishQuote(desc)}`);
535
+ }
536
+ }
537
+ return `${lines.join("\n")}\n`;
538
+ }
539
+ function sanitizeIdent(bin) {
540
+ return bin.replace(/[^A-Za-z0-9_]/g, "_");
541
+ }
542
+ /**
543
+ * Case arms that descend the known subcommand tree token by token, normalising
544
+ * aliases to the canonical path so a single lookup table serves both spellings.
545
+ */
546
+ function descentCases(root, indent) {
547
+ return walk(root).filter((node) => node.path.length > 0).map((node) => {
548
+ const parent = node.path.slice(0, -1).join(" ");
549
+ return `${indent}${namesOf(node).map((name) => `"${parent ? `${parent} ${name}` : name}"`).join("|")}) path="${node.path.join(" ")}" ;;`;
550
+ });
551
+ }
552
+ function emitBash(bin, root) {
553
+ const fn = `_${sanitizeIdent(bin)}_completions`;
554
+ const optCases = walk(root).map((node) => {
555
+ const subs = node.children.flatMap(namesOf).join(" ");
556
+ const flags = node.options.flatMap((o) => [o.short, o.long]).filter((f) => f !== void 0).join(" ");
557
+ const args = node.args.flatMap((a) => a.choices ?? []).join(" ");
558
+ return ` "${node.path.join(" ")}") subs="${subs}"; flags="${flags}"; args="${args}" ;;`;
559
+ });
560
+ return [
561
+ `# bash completion for ${bin}`,
562
+ `# Load with: source <(${bin} completion bash)`,
563
+ `${fn}() {`,
564
+ " local cur path try w i subs flags args",
565
+ " cur=\"${COMP_WORDS[COMP_CWORD]}\"",
566
+ " path=\"\"",
567
+ " for ((i = 1; i < COMP_CWORD; i++)); do",
568
+ " w=\"${COMP_WORDS[i]}\"",
569
+ " [[ \"$w\" == -* ]] && continue",
570
+ " try=\"${path:+$path }$w\"",
571
+ " case \"$try\" in",
572
+ ...descentCases(root, " "),
573
+ " *) ;;",
574
+ " esac",
575
+ " done",
576
+ " subs=\"\"; flags=\"\"; args=\"\"",
577
+ " case \"$path\" in",
578
+ ...optCases,
579
+ " esac",
580
+ " if [[ \"$cur\" == -* ]]; then",
581
+ " COMPREPLY=($(compgen -W \"$flags\" -- \"$cur\"))",
582
+ " elif [[ -n \"$subs\" ]]; then",
583
+ " COMPREPLY=($(compgen -W \"$subs\" -- \"$cur\"))",
584
+ " elif [[ -n \"$args\" ]]; then",
585
+ " COMPREPLY=($(compgen -W \"$args\" -- \"$cur\"))",
586
+ " else",
587
+ " COMPREPLY=($(compgen -f -- \"$cur\"))",
588
+ " fi",
589
+ "}",
590
+ `complete -F ${fn} ${bin}`,
591
+ ""
592
+ ].join("\n");
593
+ }
594
+ function zshItem(name, description) {
595
+ return `'${`${name}:${description}`.replace(/'/g, `'\\''`)}'`;
596
+ }
597
+ function emitZsh(bin, root) {
598
+ const fn = `_${sanitizeIdent(bin)}`;
599
+ const optCases = walk(root).map((node) => {
600
+ const subs = node.children.flatMap((child) => namesOf(child).map((name) => zshItem(name, child.description))).join(" ");
601
+ const flags = node.options.flatMap((o) => [o.short, o.long].filter((f) => f !== void 0).map((f) => zshItem(f, o.description))).join(" ");
602
+ const args = node.args.flatMap((a) => (a.choices ?? []).map((c) => zshItem(c, a.description || a.name))).join(" ");
603
+ const body = [
604
+ subs ? `subs=(${subs})` : void 0,
605
+ flags ? `flags=(${flags})` : void 0,
606
+ args ? `argwords=(${args})` : void 0
607
+ ].filter((p) => p !== void 0).join("; ");
608
+ return ` "${node.path.join(" ")}") ${body ? `${body} ` : ""};;`;
609
+ });
610
+ return [
611
+ `#compdef ${bin}`,
612
+ `# zsh completion for ${bin}`,
613
+ `# Install: ${bin} completion zsh > "\${fpath[1]}/_${bin}" (then restart zsh)`,
614
+ `${fn}() {`,
615
+ " local -a subs flags argwords",
616
+ " local path=\"\" try w",
617
+ " for w in \"${(@)words[2,CURRENT-1]}\"; do",
618
+ " [[ \"$w\" == -* ]] && continue",
619
+ " try=\"${path:+$path }$w\"",
620
+ " case \"$try\" in",
621
+ ...descentCases(root, " "),
622
+ " *) ;;",
623
+ " esac",
624
+ " done",
625
+ " case \"$path\" in",
626
+ ...optCases,
627
+ " esac",
628
+ " if [[ \"$PREFIX\" == -* ]]; then",
629
+ " (( ${#flags[@]} )) && _describe -t options 'option' flags",
630
+ " elif (( ${#subs[@]} )); then",
631
+ " _describe -t commands 'command' subs",
632
+ " elif (( ${#argwords[@]} )); then",
633
+ " _describe -t values 'value' argwords",
634
+ " else",
635
+ " _files",
636
+ " fi",
637
+ "}",
638
+ `if [[ "\${funcstack[1]}" == "${fn}" ]]; then`,
639
+ ` ${fn} "$@"`,
640
+ "else",
641
+ ` compdef ${fn} ${bin}`,
642
+ "fi",
643
+ ""
644
+ ].join("\n");
645
+ }
646
+ /**
647
+ * Build a fully static, self-contained shell-completion script for `root`'s command
648
+ * tree (subcommands at any depth, aliases, flags, option/argument choices). Pure:
649
+ * output depends only on the tree, iterated in registration order.
650
+ */
651
+ function buildCompletionScript(root, shell) {
652
+ const bin = root.name();
653
+ const tree = toNode(root, []);
654
+ switch (shell) {
655
+ case "fish": return emitFish(bin, tree);
656
+ case "bash": return emitBash(bin, tree);
657
+ case "zsh": return emitZsh(bin, tree);
658
+ }
659
+ }
660
+ /**
661
+ * Build a `completion <shell>` subcommand that prints the static completion script
662
+ * for the ROOT program (resolved by walking `.parent`) to stdout.
663
+ */
664
+ function createCompletionCommand() {
665
+ return new Command("completion").description("Print a static shell-completion script for bash, zsh or fish. Load it in your shell, e.g. fish: `sm-cl completion fish | source`").addArgument(new Argument("<shell>", "target shell").choices(SHELLS)).action((shell, _opts, cmd) => {
666
+ process.stdout.write(buildCompletionScript(findRoot(cmd), shell));
667
+ });
668
+ }
669
+ //#endregion
670
+ //#region src/cli/program.ts
671
+ /** Wrap an async action so thrown errors print cleanly and exit non-zero. */
672
+ function run(fn) {
673
+ fn().catch((err) => {
674
+ console.error("Error:", err instanceof Error ? err.message : String(err));
675
+ process.exit(1);
676
+ });
677
+ }
678
+ const bigintReplacer = (_k, v) => typeof v === "bigint" ? v.toString() : v;
679
+ function report(label, result) {
680
+ console.log(`${label} tree root: ${result.treeRoot}`);
681
+ console.log(`${label} tree CID: ${result.treeCid ?? "(upload skipped)"}`);
682
+ if (result.logCid) console.log(`${label} log CID: ${result.logCid}`);
683
+ if (result.configPath) console.log(`Wrote ${result.configPath}`);
684
+ }
685
+ function buildProgram(deps = {
686
+ makeAddresses,
687
+ makeStrikes,
688
+ makeRewards
689
+ }) {
690
+ const program = new Command().name("sm-merkle").description("Lido SM Merkle tree builder — build a tree, pin it to IPFS, print root + CID").version(readPackageVersion(import.meta.url)).helpCommand(false);
691
+ program.command("addresses", { isDefault: true }).description("Build the addresses (vetted gate) tree, pin it to IPFS, print root + CID").argument("[addresses...]", "whitelist addresses (inline, or use --input / --source)").option("--input <addr>", "repeatable: add one address to the list", (v, acc) => {
692
+ acc.push(v);
693
+ return acc;
694
+ }, []).option("--source <file>", "load address list from a JSON array or newline-delimited .txt").option("--no-upload", "build/print root only, skip IPFS pinning").option("-o, --out <path>", "also write { treeRoot, treeCid } JSON to this path").option("--json", "print result as JSON to stdout (machine-readable)").action((positionals, opts) => {
695
+ run(async () => {
696
+ const hasInline = positionals.length > 0 || opts.input.length > 0;
697
+ const hasFile = Boolean(opts.source);
698
+ if (hasInline && hasFile) throw new Error("Cannot combine inline addresses (positionals / --input) with --source <file>. Use one or the other.");
699
+ let addresses;
700
+ if (hasFile) addresses = readAddressFile(opts.source);
701
+ else addresses = [...positionals, ...opts.input];
702
+ if (addresses.length === 0) throw new Error("No addresses supplied. Provide positional addresses, --input <addr>, or --source <file>.");
703
+ const result = await deps.makeAddresses(addresses, {
704
+ noUpload: !opts.upload,
705
+ configPath: opts.out
706
+ });
707
+ if (opts.json) console.log(JSON.stringify(result, bigintReplacer, 2));
708
+ else report("Addresses", result);
709
+ });
710
+ });
711
+ program.command("strikes").description("Build the strikes tree, pin it to IPFS, print root + CID").argument("<strikes>", "path to strikes.json — JSON array [{ nodeOperatorId, pubkey, strikes: number[] }]").option("--source <file>", "alternative flag for the strikes file path (same as positional)").option("--no-upload", "build/print root only, skip IPFS pinning").option("-o, --out <path>", "also write { treeRoot, treeCid } JSON to this path").option("--json", "print result as JSON to stdout (machine-readable)").action((strikesArg, opts) => {
712
+ run(async () => {
713
+ const strikesPath = opts.source ?? strikesArg;
714
+ const result = await deps.makeStrikes(strikesPath, {
715
+ noUpload: !opts.upload,
716
+ configPath: opts.out
717
+ });
718
+ if (opts.json) console.log(JSON.stringify(result, bigintReplacer, 2));
719
+ else report("Strikes", result);
720
+ });
721
+ });
722
+ program.command("rewards").description("Build the rewards tree from [nodeOperatorId, cumulativeShares] pairs, pin it").requiredOption("--source <file>", "JSON array of [nodeOperatorId, cumulativeShares] pairs (number or numeric-string)").option("--no-upload", "build/print root only, skip IPFS pinning").option("-o, --out <path>", "also write { treeRoot, treeCid } JSON to this path").option("--json", "print result as JSON to stdout (machine-readable)").action((opts) => {
723
+ run(async () => {
724
+ const raw = readJsonFile(opts.source);
725
+ if (raw.length === 0) throw new Error("No leaves supplied. Provide a non-empty JSON array of [nodeOperatorId, cumulativeShares] pairs.");
726
+ const leaves = raw.map(([noId, shares], i) => {
727
+ const toBig = (v, field) => {
728
+ if (typeof v === "bigint") return v;
729
+ if (typeof v === "number" || typeof v === "string") return BigInt(v);
730
+ throw new Error(`rewards --source: entry [${i}].${field} must be a number or numeric string, got ${typeof v}`);
731
+ };
732
+ return [toBig(noId, "0"), toBig(shares, "1")];
733
+ });
734
+ const result = await deps.makeRewards(leaves, {
735
+ noUpload: !opts.upload,
736
+ configPath: opts.out
737
+ });
738
+ if (opts.json) console.log(JSON.stringify(result, bigintReplacer, 2));
739
+ else report("Rewards", result);
740
+ });
741
+ });
742
+ program.command("help").description("Print a self-contained usage cheat sheet").action(() => {
743
+ console.log(`sm-merkle — Lido SM Merkle tree builder
744
+
745
+ WHAT IT DOES
746
+ Build a Merkle tree from input, pin it to IPFS, and print the root + CID.
747
+ Pushing the root/CID on-chain is NOT this tool's job — that's @sm-lab/receipts.
748
+
749
+ COMMANDS
750
+ addresses [addresses...] build the addresses (vetted gate) tree (DEFAULT — bare args route here)
751
+ strikes <strikes> build the strikes tree, pin to IPFS, print root + CID
752
+ rewards --source <file> build the rewards tree from [noId, cumulativeShares] pairs
753
+ completion <shell> print a bash|zsh|fish completion script (sm-merkle completion fish | source)
754
+ help print this cheat sheet
755
+
756
+ FLAGS (all commands)
757
+ --no-upload build/print the root only, skip IPFS pinning
758
+ -o, --out <path> also write { treeRoot, treeCid } JSON to <path>
759
+ --json print result as a single JSON value to stdout (machine-readable)
760
+
761
+ FLAGS (addresses only)
762
+ --input <addr> repeatable: add one address (can mix with positionals)
763
+ --source <file> load from JSON array ["0x.."] or newline-delimited .txt
764
+ (mutually exclusive with inline positionals / --input)
765
+
766
+ ENV
767
+ IPFS_API_URL pinning endpoint; unset → local @sm-lab/ipfs (http://127.0.0.1:5001).
768
+ Pinata used only when PINATA_* creds are set (and IPFS_API_URL is unset).
769
+ PINATA_API_KEY/SECRET Pinata credentials (or PINATA_JWT). Ignored by the mock.
770
+
771
+ DATA FORMATS
772
+ addresses JSON array ["0x..", ...] or newline-delimited text (# comments ok)
773
+ strikes JSON array [{ nodeOperatorId, pubkey, strikes: number[] }]
774
+ rewards JSON array [[nodeOperatorId, cumulativeShares], ...] (numbers or numeric strings)
775
+
776
+ EXAMPLES
777
+ sm-merkle 0xABC 0xDEF # inline addresses → addresses (vetted gate) tree
778
+ sm-merkle addresses --source addrs.json --json
779
+ sm-merkle addresses --input 0xABC --input 0xDEF --no-upload
780
+ sm-merkle strikes strikes.json --no-upload --json
781
+ sm-merkle rewards --source rewards.json --json
782
+ sm-merkle addresses addrs.json -o config.json`);
783
+ });
784
+ program.addCommand(createCompletionCommand());
785
+ return program;
786
+ }
787
+ //#endregion
788
+ //#region src/cli/index.ts
789
+ buildProgram().parseAsync().catch((err) => {
790
+ console.error("Error:", err instanceof Error ? err.message : String(err));
791
+ process.exit(1);
792
+ });
793
+ //#endregion
794
+ export {};
795
+
796
+ //# sourceMappingURL=cli.mjs.map