@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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":["#body","#init","#eventInitDict","realMakeAddresses","realMakeStrikes","realMakeRewards"],"sources":["../../../node_modules/.pnpm/hono@4.12.27/node_modules/hono/dist/helper/websocket/index.js","../../../node_modules/.pnpm/@hono+node-server@2.0.6_hono@4.12.27/node_modules/@hono/node-server/dist/index.mjs","../../../packages/core/src/admin.ts","../../../packages/core/src/cli.ts","../../../packages/core/src/completion.ts","../src/cli/program.ts","../src/cli/index.ts"],"sourcesContent":["// src/helper/websocket/index.ts\nvar WSContext = class {\n #init;\n constructor(init) {\n this.#init = init;\n this.raw = init.raw;\n this.url = init.url ? new URL(init.url) : null;\n this.protocol = init.protocol ?? null;\n }\n send(source, options) {\n this.#init.send(source, options ?? {});\n }\n raw;\n binaryType = \"arraybuffer\";\n get readyState() {\n return this.#init.readyState;\n }\n url;\n protocol;\n close(code, reason) {\n this.#init.close(code, reason);\n }\n};\nvar createWSMessageEvent = (source) => {\n return new MessageEvent(\"message\", {\n data: source\n });\n};\nvar defineWebSocketHelper = (handler) => {\n return ((...args) => {\n if (typeof args[0] === \"function\") {\n const [createEvents, options] = args;\n return async function upgradeWebSocket(c, next) {\n const events = await createEvents(c);\n const result = await handler(c, events, options);\n if (result) {\n return result;\n }\n await next();\n };\n } else {\n const [c, events, options] = args;\n return (async () => {\n const upgraded = await handler(c, events, options);\n if (!upgraded) {\n throw new Error(\"Failed to upgrade WebSocket\");\n }\n return upgraded;\n })();\n }\n });\n};\nexport {\n WSContext,\n createWSMessageEvent,\n defineWebSocketHelper\n};\n","import { t as X_ALREADY_SENT } from \"./constants-BLSFu_RU.mjs\";\nimport { STATUS_CODES, createServer } from \"node:http\";\nimport { Http2ServerRequest, constants } from \"node:http2\";\nimport { Readable } from \"node:stream\";\nimport { defineWebSocketHelper } from \"hono/ws\";\n\n//#region src/error.ts\nvar RequestError = class extends Error {\n\tconstructor(message, options) {\n\t\tsuper(message, options);\n\t\tthis.name = \"RequestError\";\n\t}\n};\n\n//#endregion\n//#region src/url.ts\nconst reValidRequestUrl = /^\\/[!#$&-;=?-\\[\\]_a-z~]*$/;\nconst reDotSegment = /\\/\\.\\.?(?:[/?#]|$)/;\nconst reValidHost = /^[a-z0-9._-]+(?::(?:[1-5]\\d{3,4}|[6-9]\\d{3}))?$/;\nconst buildUrl = (scheme, host, incomingUrl) => {\n\tconst url = `${scheme}://${host}${incomingUrl}`;\n\tif (!reValidHost.test(host)) {\n\t\tconst urlObj = new URL(url);\n\t\tif (urlObj.hostname.length !== host.length && urlObj.hostname !== (host.includes(\":\") ? host.replace(/:\\d+$/, \"\") : host).toLowerCase()) throw new RequestError(\"Invalid host header\");\n\t\treturn urlObj.href;\n\t} else if (incomingUrl.length === 0) return url + \"/\";\n\telse {\n\t\tif (incomingUrl.charCodeAt(0) !== 47) throw new RequestError(\"Invalid URL\");\n\t\tif (!reValidRequestUrl.test(incomingUrl) || reDotSegment.test(incomingUrl)) return new URL(url).href;\n\t\treturn url;\n\t}\n};\n\n//#endregion\n//#region src/request.ts\nconst toRequestError = (e) => {\n\tif (e instanceof RequestError) return e;\n\treturn new RequestError(e.message, { cause: e });\n};\nconst GlobalRequest = global.Request;\nvar Request$1 = class extends GlobalRequest {\n\tconstructor(input, options) {\n\t\tif (typeof input === \"object\" && getRequestCache in input) {\n\t\t\tconst hasReplacementBody = options !== void 0 && \"body\" in options && options.body != null;\n\t\t\tif (input[bodyConsumedDirectlyKey] && !hasReplacementBody) throw new TypeError(\"Cannot construct a Request with a Request object that has already been used.\");\n\t\t\tinput = input[getRequestCache]();\n\t\t}\n\t\tif (typeof (options?.body)?.getReader !== \"undefined\") options.duplex ??= \"half\";\n\t\tsuper(input, options);\n\t}\n};\nconst newHeadersFromIncoming = (incoming) => {\n\tconst headerRecord = [];\n\tconst rawHeaders = incoming.rawHeaders;\n\tfor (let i = 0, len = rawHeaders.length; i < len; i += 2) {\n\t\tconst key = rawHeaders[i];\n\t\tif (key.charCodeAt(0) !== 58) headerRecord.push([key, rawHeaders[i + 1]]);\n\t}\n\treturn new Headers(headerRecord);\n};\nconst wrapBodyStream = Symbol(\"wrapBodyStream\");\nconst newRequestFromIncoming = (method, url, headers, incoming, abortController) => {\n\tconst init = {\n\t\tmethod,\n\t\theaders,\n\t\tsignal: abortController.signal\n\t};\n\tif (method === \"TRACE\") {\n\t\tinit.method = \"GET\";\n\t\tconst req = new Request$1(url, init);\n\t\tObject.defineProperty(req, \"method\", { get() {\n\t\t\treturn \"TRACE\";\n\t\t} });\n\t\treturn req;\n\t}\n\tif (!(method === \"GET\" || method === \"HEAD\")) if (\"rawBody\" in incoming && incoming.rawBody instanceof Buffer) init.body = new ReadableStream({ start(controller) {\n\t\tcontroller.enqueue(incoming.rawBody);\n\t\tcontroller.close();\n\t} });\n\telse if (incoming[wrapBodyStream]) {\n\t\tlet reader;\n\t\tinit.body = new ReadableStream({ async pull(controller) {\n\t\t\ttry {\n\t\t\t\treader ||= Readable.toWeb(incoming).getReader();\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) controller.close();\n\t\t\t\telse controller.enqueue(value);\n\t\t\t} catch (error) {\n\t\t\t\tcontroller.error(error);\n\t\t\t}\n\t\t} });\n\t} else init.body = Readable.toWeb(incoming);\n\treturn new Request$1(url, init);\n};\nconst getRequestCache = Symbol(\"getRequestCache\");\nconst requestCache = Symbol(\"requestCache\");\nconst incomingKey = Symbol(\"incomingKey\");\nconst urlKey = Symbol(\"urlKey\");\nconst methodKey = Symbol(\"methodKey\");\nconst headersKey = Symbol(\"headersKey\");\nconst abortControllerKey = Symbol(\"abortControllerKey\");\nconst getAbortController = Symbol(\"getAbortController\");\nconst abortRequest = Symbol(\"abortRequest\");\nconst bodyBufferKey = Symbol(\"bodyBuffer\");\nconst bodyReadPromiseKey = Symbol(\"bodyReadPromise\");\nconst bodyConsumedDirectlyKey = Symbol(\"bodyConsumedDirectly\");\nconst bodyLockReaderKey = Symbol(\"bodyLockReader\");\nconst abortReasonKey = Symbol(\"abortReason\");\nconst newBodyUnusableError = () => {\n\treturn /* @__PURE__ */ new TypeError(\"Body is unusable\");\n};\nconst rejectBodyUnusable = () => {\n\treturn Promise.reject(newBodyUnusableError());\n};\nconst textDecoder = new TextDecoder();\nconst consumeBodyDirectOnce = (request) => {\n\tif (request[bodyConsumedDirectlyKey]) return rejectBodyUnusable();\n\trequest[bodyConsumedDirectlyKey] = true;\n};\nconst toArrayBuffer = (buf) => {\n\treturn buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);\n};\nconst contentType = (request) => {\n\treturn (request[headersKey] ||= newHeadersFromIncoming(request[incomingKey])).get(\"content-type\") || \"\";\n};\nconst methodTokenRegExp = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\nconst normalizeIncomingMethod = (method) => {\n\tif (typeof method !== \"string\" || method.length === 0) return \"GET\";\n\tswitch (method) {\n\t\tcase \"DELETE\":\n\t\tcase \"GET\":\n\t\tcase \"HEAD\":\n\t\tcase \"OPTIONS\":\n\t\tcase \"POST\":\n\t\tcase \"PUT\": return method;\n\t}\n\tconst upper = method.toUpperCase();\n\tswitch (upper) {\n\t\tcase \"DELETE\":\n\t\tcase \"GET\":\n\t\tcase \"HEAD\":\n\t\tcase \"OPTIONS\":\n\t\tcase \"POST\":\n\t\tcase \"PUT\": return upper;\n\t\tdefault: return method;\n\t}\n};\nconst validateDirectReadMethod = (method) => {\n\tif (!methodTokenRegExp.test(method)) return /* @__PURE__ */ new TypeError(`'${method}' is not a valid HTTP method.`);\n\tconst normalized = method.toUpperCase();\n\tif (normalized === \"CONNECT\" || normalized === \"TRACK\" || normalized === \"TRACE\" && method !== \"TRACE\") return /* @__PURE__ */ new TypeError(`'${method}' HTTP method is unsupported.`);\n};\nconst readBodyWithFastPath = (request, method, fromBuffer) => {\n\tif (request[bodyConsumedDirectlyKey]) return rejectBodyUnusable();\n\tconst methodName = request.method;\n\tif (methodName === \"GET\" || methodName === \"HEAD\") return request[getRequestCache]()[method]();\n\tconst methodValidationError = validateDirectReadMethod(methodName);\n\tif (methodValidationError) return Promise.reject(methodValidationError);\n\tif (request[requestCache]) {\n\t\tif (methodName !== \"TRACE\") return request[requestCache][method]();\n\t}\n\tconst alreadyUsedError = consumeBodyDirectOnce(request);\n\tif (alreadyUsedError) return alreadyUsedError;\n\tconst raw = readRawBodyIfAvailable(request);\n\tif (raw) {\n\t\tconst result = Promise.resolve(fromBuffer(raw, request));\n\t\trequest[bodyBufferKey] = void 0;\n\t\treturn result;\n\t}\n\treturn readBodyDirect(request).then((buf) => {\n\t\tconst result = fromBuffer(buf, request);\n\t\trequest[bodyBufferKey] = void 0;\n\t\treturn result;\n\t});\n};\nconst readRawBodyIfAvailable = (request) => {\n\tconst incoming = request[incomingKey];\n\tif (\"rawBody\" in incoming && incoming.rawBody instanceof Buffer) return incoming.rawBody;\n};\nconst readBodyDirect = (request) => {\n\tif (request[bodyBufferKey]) return Promise.resolve(request[bodyBufferKey]);\n\tif (request[bodyReadPromiseKey]) return request[bodyReadPromiseKey];\n\tconst incoming = request[incomingKey];\n\tif (Readable.isDisturbed(incoming)) return rejectBodyUnusable();\n\tconst promise = new Promise((resolve, reject) => {\n\t\tconst chunks = [];\n\t\tlet settled = false;\n\t\tconst finish = (callback) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tcallback();\n\t\t};\n\t\tconst onData = (chunk) => {\n\t\t\tchunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n\t\t};\n\t\tconst onEnd = () => {\n\t\t\tfinish(() => {\n\t\t\t\tconst buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);\n\t\t\t\trequest[bodyBufferKey] = buffer;\n\t\t\t\tresolve(buffer);\n\t\t\t});\n\t\t};\n\t\tconst onError = (error) => {\n\t\t\tfinish(() => {\n\t\t\t\treject(error);\n\t\t\t});\n\t\t};\n\t\tconst onClose = () => {\n\t\t\tif (incoming.readableEnded) {\n\t\t\t\tonEnd();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfinish(() => {\n\t\t\t\tif (incoming.errored) {\n\t\t\t\t\treject(incoming.errored);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst reason = request[abortReasonKey];\n\t\t\t\tif (reason !== void 0) {\n\t\t\t\t\treject(reason instanceof Error ? reason : new Error(String(reason)));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\treject(/* @__PURE__ */ new Error(\"Client connection prematurely closed.\"));\n\t\t\t});\n\t\t};\n\t\tconst cleanup = () => {\n\t\t\tincoming.off(\"data\", onData);\n\t\t\tincoming.off(\"end\", onEnd);\n\t\t\tincoming.off(\"error\", onError);\n\t\t\tincoming.off(\"close\", onClose);\n\t\t\trequest[bodyReadPromiseKey] = void 0;\n\t\t};\n\t\tincoming.on(\"data\", onData);\n\t\tincoming.on(\"end\", onEnd);\n\t\tincoming.on(\"error\", onError);\n\t\tincoming.on(\"close\", onClose);\n\t\tqueueMicrotask(() => {\n\t\t\tif (settled) return;\n\t\t\tif (incoming.readableEnded) onEnd();\n\t\t\telse if (incoming.errored) onError(incoming.errored);\n\t\t\telse if (incoming.destroyed) onClose();\n\t\t});\n\t});\n\trequest[bodyReadPromiseKey] = promise;\n\treturn promise;\n};\nconst requestPrototype = {\n\tget method() {\n\t\treturn this[methodKey];\n\t},\n\tget url() {\n\t\treturn this[urlKey];\n\t},\n\tget headers() {\n\t\treturn this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);\n\t},\n\t[abortRequest](reason) {\n\t\tif (this[abortReasonKey] === void 0) this[abortReasonKey] = reason;\n\t\tconst abortController = this[abortControllerKey];\n\t\tif (abortController && !abortController.signal.aborted) abortController.abort(reason);\n\t},\n\t[getAbortController]() {\n\t\tthis[abortControllerKey] ||= new AbortController();\n\t\tif (this[abortReasonKey] !== void 0 && !this[abortControllerKey].signal.aborted) this[abortControllerKey].abort(this[abortReasonKey]);\n\t\treturn this[abortControllerKey];\n\t},\n\t[getRequestCache]() {\n\t\tconst abortController = this[getAbortController]();\n\t\tif (this[requestCache]) return this[requestCache];\n\t\tconst method = this.method;\n\t\tif (this[bodyConsumedDirectlyKey] && !(method === \"GET\" || method === \"HEAD\")) {\n\t\t\tthis[bodyBufferKey] = void 0;\n\t\t\tconst init = {\n\t\t\t\tmethod: method === \"TRACE\" ? \"GET\" : method,\n\t\t\t\theaders: this.headers,\n\t\t\t\tsignal: abortController.signal\n\t\t\t};\n\t\t\tif (method !== \"TRACE\") {\n\t\t\t\tinit.body = new ReadableStream({ start(c) {\n\t\t\t\t\tc.close();\n\t\t\t\t} });\n\t\t\t\tinit.duplex = \"half\";\n\t\t\t}\n\t\t\tconst req = new Request$1(this[urlKey], init);\n\t\t\tif (method === \"TRACE\") Object.defineProperty(req, \"method\", { get() {\n\t\t\t\treturn \"TRACE\";\n\t\t\t} });\n\t\t\treturn this[requestCache] = req;\n\t\t}\n\t\treturn this[requestCache] = newRequestFromIncoming(this.method, this[urlKey], this.headers, this[incomingKey], abortController);\n\t},\n\tget body() {\n\t\tif (!this[bodyConsumedDirectlyKey]) return this[getRequestCache]().body;\n\t\tconst request = this[getRequestCache]();\n\t\tif (!this[bodyLockReaderKey] && request.body) this[bodyLockReaderKey] = request.body.getReader();\n\t\treturn request.body;\n\t},\n\tget bodyUsed() {\n\t\tif (this[bodyConsumedDirectlyKey]) return true;\n\t\tif (this[requestCache]) return this[requestCache].bodyUsed;\n\t\treturn false;\n\t}\n};\nObject.defineProperty(requestPrototype, \"signal\", { get() {\n\treturn this[getAbortController]().signal;\n} });\n[\n\t\"cache\",\n\t\"credentials\",\n\t\"destination\",\n\t\"integrity\",\n\t\"mode\",\n\t\"redirect\",\n\t\"referrer\",\n\t\"referrerPolicy\",\n\t\"keepalive\"\n].forEach((k) => {\n\tObject.defineProperty(requestPrototype, k, { get() {\n\t\treturn this[getRequestCache]()[k];\n\t} });\n});\n[\"clone\", \"formData\"].forEach((k) => {\n\tObject.defineProperty(requestPrototype, k, { value: function() {\n\t\tif (this[bodyConsumedDirectlyKey]) {\n\t\t\tif (k === \"clone\") throw newBodyUnusableError();\n\t\t\treturn rejectBodyUnusable();\n\t\t}\n\t\treturn this[getRequestCache]()[k]();\n\t} });\n});\nObject.defineProperty(requestPrototype, \"text\", { value: function() {\n\treturn readBodyWithFastPath(this, \"text\", (buf) => textDecoder.decode(buf));\n} });\nObject.defineProperty(requestPrototype, \"arrayBuffer\", { value: function() {\n\treturn readBodyWithFastPath(this, \"arrayBuffer\", (buf) => toArrayBuffer(buf));\n} });\nObject.defineProperty(requestPrototype, \"blob\", { value: function() {\n\treturn readBodyWithFastPath(this, \"blob\", (buf, request) => {\n\t\tconst type = contentType(request);\n\t\tconst init = type ? { headers: { \"content-type\": type } } : void 0;\n\t\treturn new Response(buf, init).blob();\n\t});\n} });\nObject.defineProperty(requestPrototype, \"json\", { value: function() {\n\tif (this[bodyConsumedDirectlyKey]) return rejectBodyUnusable();\n\treturn this.text().then(JSON.parse);\n} });\nObject.defineProperty(requestPrototype, Symbol.for(\"nodejs.util.inspect.custom\"), { value: function(depth, options, inspectFn) {\n\treturn `Request (lightweight) ${inspectFn({\n\t\tmethod: this.method,\n\t\turl: this.url,\n\t\theaders: this.headers,\n\t\tnativeRequest: this[requestCache]\n\t}, {\n\t\t...options,\n\t\tdepth: depth == null ? null : depth - 1\n\t})}`;\n} });\nObject.setPrototypeOf(requestPrototype, Request$1.prototype);\nconst newRequest = (incoming, defaultHostname) => {\n\tconst req = Object.create(requestPrototype);\n\treq[incomingKey] = incoming;\n\treq[methodKey] = normalizeIncomingMethod(incoming.method);\n\tconst incomingUrl = incoming.url || \"\";\n\tif (incomingUrl[0] !== \"/\" && (incomingUrl.startsWith(\"http://\") || incomingUrl.startsWith(\"https://\"))) {\n\t\tif (incoming instanceof Http2ServerRequest) throw new RequestError(\"Absolute URL for :path is not allowed in HTTP/2\");\n\t\ttry {\n\t\t\treq[urlKey] = new URL(incomingUrl).href;\n\t\t} catch (e) {\n\t\t\tthrow new RequestError(\"Invalid absolute URL\", { cause: e });\n\t\t}\n\t\treturn req;\n\t}\n\tconst host = (incoming instanceof Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname;\n\tif (!host) throw new RequestError(\"Missing host header\");\n\tlet scheme;\n\tif (incoming instanceof Http2ServerRequest) {\n\t\tscheme = incoming.scheme;\n\t\tif (!(scheme === \"http\" || scheme === \"https\")) throw new RequestError(\"Unsupported scheme\");\n\t} else scheme = incoming.socket && incoming.socket.encrypted ? \"https\" : \"http\";\n\ttry {\n\t\treq[urlKey] = buildUrl(scheme, host, incomingUrl);\n\t} catch (e) {\n\t\tif (e instanceof RequestError) throw e;\n\t\telse throw new RequestError(\"Invalid URL\", { cause: e });\n\t}\n\treturn req;\n};\n\n//#endregion\n//#region src/response.ts\nconst defaultContentType = \"text/plain; charset=UTF-8\";\nconst responseCache = Symbol(\"responseCache\");\nconst getResponseCache = Symbol(\"getResponseCache\");\nconst cacheKey = Symbol(\"cache\");\nconst GlobalResponse = global.Response;\nvar Response$1 = class Response$1 {\n\t#body;\n\t#init;\n\t[getResponseCache]() {\n\t\tconst cache = this[cacheKey];\n\t\tconst liveHeaders = cache && cache[2] instanceof Headers ? cache[2] : void 0;\n\t\tdelete this[cacheKey];\n\t\treturn this[responseCache] ||= new GlobalResponse(this.#body, liveHeaders ? {\n\t\t\tstatus: this.#init?.status,\n\t\t\tstatusText: this.#init?.statusText,\n\t\t\theaders: liveHeaders\n\t\t} : this.#init);\n\t}\n\tconstructor(body, init) {\n\t\tlet headers;\n\t\tthis.#body = body;\n\t\tif (init instanceof Response$1) {\n\t\t\tconst cachedGlobalResponse = init[responseCache];\n\t\t\tif (cachedGlobalResponse) {\n\t\t\t\tthis.#init = cachedGlobalResponse;\n\t\t\t\tthis[getResponseCache]();\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tthis.#init = init.#init;\n\t\t\t\theaders = new Headers(init.headers);\n\t\t\t}\n\t\t} else this.#init = init;\n\t\tif (body == null || typeof body === \"string\" || typeof body?.getReader !== \"undefined\" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [\n\t\t\tinit?.status || 200,\n\t\t\tbody ?? null,\n\t\t\theaders || init?.headers\n\t\t];\n\t}\n\tget headers() {\n\t\tconst cache = this[cacheKey];\n\t\tif (cache) {\n\t\t\tif (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2] || (cache[1] === null ? void 0 : { \"content-type\": defaultContentType }));\n\t\t\treturn cache[2];\n\t\t}\n\t\treturn this[getResponseCache]().headers;\n\t}\n\tget status() {\n\t\treturn this[cacheKey]?.[0] ?? this[getResponseCache]().status;\n\t}\n\tget ok() {\n\t\tconst status = this.status;\n\t\treturn status >= 200 && status < 300;\n\t}\n};\n[\n\t\"body\",\n\t\"bodyUsed\",\n\t\"redirected\",\n\t\"statusText\",\n\t\"trailers\",\n\t\"type\",\n\t\"url\"\n].forEach((k) => {\n\tObject.defineProperty(Response$1.prototype, k, { get() {\n\t\treturn this[getResponseCache]()[k];\n\t} });\n});\n[\n\t\"arrayBuffer\",\n\t\"blob\",\n\t\"clone\",\n\t\"formData\",\n\t\"json\",\n\t\"text\"\n].forEach((k) => {\n\tObject.defineProperty(Response$1.prototype, k, { value: function() {\n\t\treturn this[getResponseCache]()[k]();\n\t} });\n});\nObject.defineProperty(Response$1.prototype, Symbol.for(\"nodejs.util.inspect.custom\"), { value: function(depth, options, inspectFn) {\n\treturn `Response (lightweight) ${inspectFn({\n\t\tstatus: this.status,\n\t\theaders: this.headers,\n\t\tok: this.ok,\n\t\tnativeResponse: this[responseCache]\n\t}, {\n\t\t...options,\n\t\tdepth: depth == null ? null : depth - 1\n\t})}`;\n} });\nObject.setPrototypeOf(Response$1, GlobalResponse);\nObject.setPrototypeOf(Response$1.prototype, GlobalResponse.prototype);\nconst validRedirectUrl = /^https?:\\/\\/[!#-;=?-[\\]_a-z~A-Z]+$/;\nconst parseRedirectUrl = (url) => {\n\tif (url instanceof URL) return url.href;\n\tif (validRedirectUrl.test(url)) return url;\n\treturn new URL(url).href;\n};\nconst validRedirectStatuses = new Set([\n\t301,\n\t302,\n\t303,\n\t307,\n\t308\n]);\nObject.defineProperty(Response$1, \"redirect\", {\n\tvalue: function redirect(url, status = 302) {\n\t\tif (!validRedirectStatuses.has(status)) throw new RangeError(\"Invalid status code\");\n\t\treturn new Response$1(null, {\n\t\t\tstatus,\n\t\t\theaders: { location: parseRedirectUrl(url) }\n\t\t});\n\t},\n\twritable: true,\n\tconfigurable: true\n});\nObject.defineProperty(Response$1, \"json\", {\n\tvalue: function json(data, init) {\n\t\tconst body = JSON.stringify(data);\n\t\tif (body === void 0) throw new TypeError(\"The data is not JSON serializable\");\n\t\tconst initHeaders = init?.headers;\n\t\tlet headers;\n\t\tif (initHeaders) {\n\t\t\theaders = new Headers(initHeaders);\n\t\t\tif (!headers.has(\"content-type\")) headers.set(\"content-type\", \"application/json\");\n\t\t} else headers = { \"content-type\": \"application/json\" };\n\t\treturn new Response$1(body, {\n\t\t\tstatus: init?.status ?? 200,\n\t\t\tstatusText: init?.statusText,\n\t\t\theaders\n\t\t});\n\t},\n\twritable: true,\n\tconfigurable: true\n});\n\n//#endregion\n//#region src/utils.ts\nasync function readWithoutBlocking(readPromise) {\n\treturn Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(void 0))]);\n}\nfunction writeFromReadableStreamDefaultReader(reader, writable, currentReadPromise) {\n\tconst cancel = (error) => {\n\t\treader.cancel(error).catch(() => {});\n\t};\n\twritable.on(\"close\", cancel);\n\twritable.on(\"error\", cancel);\n\t(currentReadPromise ?? reader.read()).then(flow, handleStreamError);\n\treturn reader.closed.finally(() => {\n\t\twritable.off(\"close\", cancel);\n\t\twritable.off(\"error\", cancel);\n\t});\n\tfunction handleStreamError(error) {\n\t\tif (error) writable.destroy(error);\n\t}\n\tfunction onDrain() {\n\t\treader.read().then(flow, handleStreamError);\n\t}\n\tfunction flow({ done, value }) {\n\t\ttry {\n\t\t\tif (done) writable.end();\n\t\t\telse if (!writable.write(value)) writable.once(\"drain\", onDrain);\n\t\t\telse return reader.read().then(flow, handleStreamError);\n\t\t} catch (e) {\n\t\t\thandleStreamError(e);\n\t\t}\n\t}\n}\nfunction writeFromReadableStream(stream, writable) {\n\tif (stream.locked) throw new TypeError(\"ReadableStream is locked.\");\n\telse if (writable.destroyed) return;\n\treturn writeFromReadableStreamDefaultReader(stream.getReader(), writable);\n}\nconst buildOutgoingHttpHeaders = (headers, defaultContentType) => {\n\tconst res = {};\n\tif (!(headers instanceof Headers)) headers = new Headers(headers ?? void 0);\n\tif (headers.has(\"set-cookie\")) {\n\t\tconst cookies = [];\n\t\tfor (const [k, v] of headers) if (k === \"set-cookie\") cookies.push(v);\n\t\telse res[k] = v;\n\t\tif (cookies.length > 0) res[\"set-cookie\"] = cookies;\n\t} else for (const [k, v] of headers) res[k] = v;\n\tif (defaultContentType) res[\"content-type\"] ??= defaultContentType;\n\treturn res;\n};\n\n//#endregion\n//#region src/listener.ts\nconst outgoingEnded = Symbol(\"outgoingEnded\");\nconst incomingDraining = Symbol(\"incomingDraining\");\nconst DRAIN_TIMEOUT_MS = 500;\nconst MAX_DRAIN_BYTES = 64 * 1024 * 1024;\nconst drainIncoming = (incoming) => {\n\tconst incomingWithDrainState = incoming;\n\tif (incoming.destroyed || incomingWithDrainState[incomingDraining]) return;\n\tincomingWithDrainState[incomingDraining] = true;\n\tif (incoming instanceof Http2ServerRequest) {\n\t\ttry {\n\t\t\tincoming.stream?.close?.(constants.NGHTTP2_NO_ERROR);\n\t\t} catch {}\n\t\treturn;\n\t}\n\tlet bytesRead = 0;\n\tconst cleanup = () => {\n\t\tclearTimeout(timer);\n\t\tincoming.off(\"data\", onData);\n\t\tincoming.off(\"end\", cleanup);\n\t\tincoming.off(\"error\", cleanup);\n\t};\n\tconst forceClose = () => {\n\t\tcleanup();\n\t\tconst socket = incoming.socket;\n\t\tif (socket && !socket.destroyed) socket.destroySoon();\n\t};\n\tconst timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);\n\ttimer.unref?.();\n\tconst onData = (chunk) => {\n\t\tbytesRead += chunk.length;\n\t\tif (bytesRead > MAX_DRAIN_BYTES) forceClose();\n\t};\n\tincoming.on(\"data\", onData);\n\tincoming.on(\"end\", cleanup);\n\tincoming.on(\"error\", cleanup);\n\tincoming.resume();\n};\nconst makeCloseHandler = (req, incoming, outgoing, needsBodyCleanup) => () => {\n\tif (incoming.errored) req[abortRequest](incoming.errored.toString());\n\telse if (!outgoing.writableFinished) req[abortRequest](\"Client connection prematurely closed.\");\n\tif (needsBodyCleanup && !incoming.readableEnded) setTimeout(() => {\n\t\tif (!incoming.readableEnded) setTimeout(() => {\n\t\t\tdrainIncoming(incoming);\n\t\t});\n\t});\n};\nconst isImmediateCacheableResponse = (res) => {\n\tif (!(cacheKey in res)) return false;\n\tconst body = res[cacheKey][1];\n\treturn body === null || typeof body === \"string\" || body instanceof Uint8Array;\n};\nconst handleRequestError = () => new Response(null, { status: 400 });\nconst handleFetchError = (e) => new Response(null, { status: e instanceof Error && (e.name === \"TimeoutError\" || e.constructor.name === \"TimeoutError\") ? 504 : 500 });\nconst handleResponseError = (e, outgoing) => {\n\tconst err = e instanceof Error ? e : new Error(\"unknown error\", { cause: e });\n\tif (err.code === \"ERR_STREAM_PREMATURE_CLOSE\") console.info(\"The user aborted a request.\");\n\telse {\n\t\tconsole.error(e);\n\t\tif (!outgoing.headersSent) outgoing.writeHead(500, { \"Content-Type\": \"text/plain\" });\n\t\toutgoing.end(`Error: ${err.message}`);\n\t\toutgoing.destroy(err);\n\t}\n};\nconst flushHeaders = (outgoing) => {\n\tif (\"flushHeaders\" in outgoing && outgoing.writable) outgoing.flushHeaders();\n};\nconst responseViaCache = async (res, outgoing) => {\n\tlet [status, body, header] = res[cacheKey];\n\tif (!header) {\n\t\tif (body === null) {\n\t\t\toutgoing.writeHead(status);\n\t\t\toutgoing.end();\n\t\t} else if (typeof body === \"string\") {\n\t\t\toutgoing.writeHead(status, {\n\t\t\t\t\"Content-Type\": defaultContentType,\n\t\t\t\t\"Content-Length\": Buffer.byteLength(body)\n\t\t\t});\n\t\t\toutgoing.end(body);\n\t\t} else if (body instanceof Uint8Array) {\n\t\t\toutgoing.writeHead(status, {\n\t\t\t\t\"Content-Type\": defaultContentType,\n\t\t\t\t\"Content-Length\": body.byteLength\n\t\t\t});\n\t\t\toutgoing.end(body);\n\t\t} else if (body instanceof Blob) {\n\t\t\toutgoing.writeHead(status, {\n\t\t\t\t\"Content-Type\": defaultContentType,\n\t\t\t\t\"Content-Length\": body.size\n\t\t\t});\n\t\t\toutgoing.end(new Uint8Array(await body.arrayBuffer()));\n\t\t} else {\n\t\t\toutgoing.writeHead(status, { \"Content-Type\": defaultContentType });\n\t\t\tflushHeaders(outgoing);\n\t\t\tawait writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));\n\t\t}\n\t\toutgoing[outgoingEnded]?.();\n\t\treturn;\n\t}\n\tlet hasContentLength = false;\n\tif (header instanceof Headers) {\n\t\thasContentLength = header.has(\"content-length\");\n\t\theader = buildOutgoingHttpHeaders(header, body === null ? void 0 : defaultContentType);\n\t} else if (Array.isArray(header)) {\n\t\tconst headerObj = new Headers(header);\n\t\thasContentLength = headerObj.has(\"content-length\");\n\t\theader = buildOutgoingHttpHeaders(headerObj, body === null ? void 0 : defaultContentType);\n\t} else for (const key in header) if (key.length === 14 && key.toLowerCase() === \"content-length\") {\n\t\thasContentLength = true;\n\t\tbreak;\n\t}\n\tif (!hasContentLength) {\n\t\tif (typeof body === \"string\") header[\"Content-Length\"] = Buffer.byteLength(body);\n\t\telse if (body instanceof Uint8Array) header[\"Content-Length\"] = body.byteLength;\n\t\telse if (body instanceof Blob) header[\"Content-Length\"] = body.size;\n\t}\n\toutgoing.writeHead(status, header);\n\tif (body == null) outgoing.end();\n\telse if (typeof body === \"string\" || body instanceof Uint8Array) outgoing.end(body);\n\telse if (body instanceof Blob) outgoing.end(new Uint8Array(await body.arrayBuffer()));\n\telse {\n\t\tflushHeaders(outgoing);\n\t\tawait writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));\n\t}\n\toutgoing[outgoingEnded]?.();\n};\nconst isPromise = (res) => typeof res.then === \"function\";\nconst responseViaResponseObject = async (res, outgoing, options = {}) => {\n\tif (isPromise(res)) if (options.errorHandler) try {\n\t\tres = await res;\n\t} catch (err) {\n\t\tconst errRes = await options.errorHandler(err);\n\t\tif (!errRes) return;\n\t\tres = errRes;\n\t}\n\telse res = await res.catch(handleFetchError);\n\tif (cacheKey in res) return responseViaCache(res, outgoing);\n\tconst resHeaderRecord = buildOutgoingHttpHeaders(res.headers, res.body === null ? void 0 : defaultContentType);\n\tif (res.body) {\n\t\tconst reader = res.body.getReader();\n\t\tconst values = [];\n\t\tlet done = false;\n\t\tlet currentReadPromise = void 0;\n\t\tif (resHeaderRecord[\"transfer-encoding\"] !== \"chunked\") {\n\t\t\tlet maxReadCount = 2;\n\t\t\tfor (let i = 0; i < maxReadCount; i++) {\n\t\t\t\tcurrentReadPromise ||= reader.read();\n\t\t\t\tconst chunk = await readWithoutBlocking(currentReadPromise).catch((e) => {\n\t\t\t\t\tconsole.error(e);\n\t\t\t\t\tdone = true;\n\t\t\t\t});\n\t\t\t\tif (!chunk) {\n\t\t\t\t\tif (i === 1) {\n\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve));\n\t\t\t\t\t\tmaxReadCount = 3;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrentReadPromise = void 0;\n\t\t\t\tif (chunk.value) values.push(chunk.value);\n\t\t\t\tif (chunk.done) {\n\t\t\t\t\tdone = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (done && !(\"content-length\" in resHeaderRecord)) resHeaderRecord[\"content-length\"] = values.reduce((acc, value) => acc + value.length, 0);\n\t\t}\n\t\toutgoing.writeHead(res.status, resHeaderRecord);\n\t\tvalues.forEach((value) => {\n\t\t\toutgoing.write(value);\n\t\t});\n\t\tif (done) outgoing.end();\n\t\telse {\n\t\t\tif (values.length === 0) flushHeaders(outgoing);\n\t\t\tawait writeFromReadableStreamDefaultReader(reader, outgoing, currentReadPromise);\n\t\t}\n\t} else if (resHeaderRecord[X_ALREADY_SENT]) {} else {\n\t\toutgoing.writeHead(res.status, resHeaderRecord);\n\t\toutgoing.end();\n\t}\n\toutgoing[outgoingEnded]?.();\n};\nconst getRequestListener = (fetchCallback, options = {}) => {\n\tconst autoCleanupIncoming = options.autoCleanupIncoming ?? true;\n\tif (options.overrideGlobalObjects !== false && global.Request !== Request$1) {\n\t\tObject.defineProperty(global, \"Request\", { value: Request$1 });\n\t\tObject.defineProperty(global, \"Response\", { value: Response$1 });\n\t}\n\treturn async (incoming, outgoing) => {\n\t\tlet res, req;\n\t\tlet needsBodyCleanup = false;\n\t\tlet closeHandlerAttached = false;\n\t\tconst ensureCloseHandler = () => {\n\t\t\tif (!req || closeHandlerAttached) return;\n\t\t\tcloseHandlerAttached = true;\n\t\t\toutgoing.on(\"close\", makeCloseHandler(req, incoming, outgoing, needsBodyCleanup));\n\t\t};\n\t\ttry {\n\t\t\treq = newRequest(incoming, options.hostname);\n\t\t\tneedsBodyCleanup = autoCleanupIncoming && !(incoming.method === \"GET\" || incoming.method === \"HEAD\");\n\t\t\tif (needsBodyCleanup) {\n\t\t\t\tincoming[wrapBodyStream] = true;\n\t\t\t\tif (incoming instanceof Http2ServerRequest) outgoing[outgoingEnded] = () => {\n\t\t\t\t\tif (!incoming.readableEnded) setTimeout(() => {\n\t\t\t\t\t\tif (!incoming.readableEnded) setTimeout(() => {\n\t\t\t\t\t\t\tincoming.destroy();\n\t\t\t\t\t\t\toutgoing.destroy();\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t};\n\t\t\t}\n\t\t\tres = fetchCallback(req, {\n\t\t\t\tincoming,\n\t\t\t\toutgoing\n\t\t\t});\n\t\t\tif (!isPromise(res) && isImmediateCacheableResponse(res)) {\n\t\t\t\tif (needsBodyCleanup && !incoming.readableEnded) outgoing.once(\"finish\", () => {\n\t\t\t\t\tif (!incoming.readableEnded) drainIncoming(incoming);\n\t\t\t\t});\n\t\t\t\treturn responseViaCache(res, outgoing);\n\t\t\t}\n\t\t\tensureCloseHandler();\n\t\t} catch (e) {\n\t\t\tif (!res) if (options.errorHandler) {\n\t\t\t\tensureCloseHandler();\n\t\t\t\tres = await options.errorHandler(req ? e : toRequestError(e));\n\t\t\t\tif (!res) return;\n\t\t\t} else if (!req) res = handleRequestError();\n\t\t\telse res = handleFetchError(e);\n\t\t\telse return handleResponseError(e, outgoing);\n\t\t}\n\t\ttry {\n\t\t\treturn await responseViaResponseObject(res, outgoing, options);\n\t\t} catch (e) {\n\t\t\treturn handleResponseError(e, outgoing);\n\t\t}\n\t};\n};\n\n//#endregion\n//#region src/websocket.ts\n/**\n* @link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent\n*/\nconst CloseEvent = globalThis.CloseEvent ?? class extends Event {\n\t#eventInitDict;\n\tconstructor(type, eventInitDict = {}) {\n\t\tsuper(type, eventInitDict);\n\t\tthis.#eventInitDict = eventInitDict;\n\t}\n\tget wasClean() {\n\t\treturn this.#eventInitDict.wasClean ?? false;\n\t}\n\tget code() {\n\t\treturn this.#eventInitDict.code ?? 0;\n\t}\n\tget reason() {\n\t\treturn this.#eventInitDict.reason ?? \"\";\n\t}\n};\nconst generateConnectionSymbol = () => Symbol(\"connection\");\nconst CONNECTION_SYMBOL_KEY = Symbol(\"CONNECTION_SYMBOL_KEY\");\nconst WAIT_FOR_WEBSOCKET_SYMBOL = Symbol(\"WAIT_FOR_WEBSOCKET_SYMBOL\");\nconst responseHeadersToSkip = new Set([\n\t\"connection\",\n\t\"content-length\",\n\t\"keep-alive\",\n\t\"proxy-authenticate\",\n\t\"proxy-authorization\",\n\t\"te\",\n\t\"trailer\",\n\t\"transfer-encoding\",\n\t\"upgrade\",\n\t\"sec-websocket-accept\",\n\t\"sec-websocket-extensions\",\n\t\"sec-websocket-protocol\"\n]);\nconst appendResponseHeaders = (headers, responseHeaders) => {\n\tif (!responseHeaders) return;\n\tresponseHeaders.forEach((value, key) => {\n\t\tif (responseHeadersToSkip.has(key.toLowerCase())) return;\n\t\theaders.push(`${key}: ${value}`);\n\t});\n};\nconst rejectUpgradeRequest = (socket, status, responseHeaders) => {\n\tconst responseLines = [\"Connection: close\", \"Content-Length: 0\"];\n\tappendResponseHeaders(responseLines, responseHeaders);\n\tsocket.end(`HTTP/1.1 ${status.toString()} ${STATUS_CODES[status] ?? \"\"}\\r\\n${responseLines.join(\"\\r\\n\")}\\r\\n\\r\n`);\n};\nconst createUpgradeRequest = (request) => {\n\tconst protocol = request.socket.encrypted ? \"https\" : \"http\";\n\tconst url = new URL(request.url ?? \"/\", `${protocol}://${request.headers.host ?? \"localhost\"}`);\n\tconst headers = new Headers();\n\tfor (const key in request.headers) {\n\t\tconst value = request.headers[key];\n\t\tif (!value) continue;\n\t\theaders.append(key, Array.isArray(value) ? value[0] : value);\n\t}\n\treturn new Request(url, { headers });\n};\nconst setupWebSocket = (options) => {\n\tconst { server, fetchCallback, wss } = options;\n\tconst waiterMap = /* @__PURE__ */ new Map();\n\twss.on(\"connection\", (ws, request) => {\n\t\tconst waiter = waiterMap.get(request);\n\t\tif (waiter) {\n\t\t\twaiter.resolve(ws);\n\t\t\twaiterMap.delete(request);\n\t\t}\n\t});\n\tconst waitForWebSocket = (request, connectionSymbol) => {\n\t\treturn new Promise((resolve) => {\n\t\t\twaiterMap.set(request, {\n\t\t\t\tresolve,\n\t\t\t\tconnectionSymbol\n\t\t\t});\n\t\t});\n\t};\n\tserver.on(\"upgrade\", async (request, socket, head) => {\n\t\tif (request.headers.upgrade?.toLowerCase() !== \"websocket\") return;\n\t\tconst env = {\n\t\t\tincoming: request,\n\t\t\toutgoing: void 0,\n\t\t\twss,\n\t\t\t[WAIT_FOR_WEBSOCKET_SYMBOL]: waitForWebSocket\n\t\t};\n\t\tlet status = 400;\n\t\tlet responseHeaders;\n\t\ttry {\n\t\t\tconst response = await fetchCallback(createUpgradeRequest(request), env);\n\t\t\tif (response instanceof Response) {\n\t\t\t\tstatus = response.status;\n\t\t\t\tresponseHeaders = response.headers;\n\t\t\t}\n\t\t} catch {\n\t\t\tif (server.listenerCount(\"upgrade\") === 1) rejectUpgradeRequest(socket, 500);\n\t\t\treturn;\n\t\t}\n\t\tconst waiter = waiterMap.get(request);\n\t\tif (!waiter || waiter.connectionSymbol !== env[CONNECTION_SYMBOL_KEY]) {\n\t\t\twaiterMap.delete(request);\n\t\t\tif (server.listenerCount(\"upgrade\") === 1) rejectUpgradeRequest(socket, status, responseHeaders);\n\t\t\treturn;\n\t\t}\n\t\tconst addResponseHeaders = (headers) => {\n\t\t\tappendResponseHeaders(headers, responseHeaders);\n\t\t};\n\t\twss.on(\"headers\", addResponseHeaders);\n\t\ttry {\n\t\t\twss.handleUpgrade(request, socket, head, (ws) => {\n\t\t\t\twss.emit(\"connection\", ws, request);\n\t\t\t});\n\t\t} finally {\n\t\t\twss.off(\"headers\", addResponseHeaders);\n\t\t}\n\t});\n\tserver.on(\"close\", () => {\n\t\twss.close();\n\t});\n};\nconst upgradeWebSocket = defineWebSocketHelper(async (c, events, options) => {\n\tif (c.req.header(\"upgrade\")?.toLowerCase() !== \"websocket\") return;\n\tconst env = c.env;\n\tconst waitForWebSocket = env[WAIT_FOR_WEBSOCKET_SYMBOL];\n\tif (!waitForWebSocket || !env.incoming) return new Response(null, { status: 500 });\n\tconst connectionSymbol = generateConnectionSymbol();\n\tenv[CONNECTION_SYMBOL_KEY] = connectionSymbol;\n\t(async () => {\n\t\tconst ws = await waitForWebSocket(env.incoming, connectionSymbol);\n\t\tconst messagesReceivedInStarting = [];\n\t\tconst bufferMessage = (data, isBinary) => {\n\t\t\tmessagesReceivedInStarting.push([data, isBinary]);\n\t\t};\n\t\tws.on(\"message\", bufferMessage);\n\t\tconst ctx = {\n\t\t\tbinaryType: \"arraybuffer\",\n\t\t\tclose(code, reason) {\n\t\t\t\tws.close(code, reason);\n\t\t\t},\n\t\t\tprotocol: ws.protocol,\n\t\t\traw: ws,\n\t\t\tget readyState() {\n\t\t\t\treturn ws.readyState;\n\t\t\t},\n\t\t\tsend(source, opts) {\n\t\t\t\tws.send(source, { compress: opts?.compress });\n\t\t\t},\n\t\t\turl: new URL(c.req.url)\n\t\t};\n\t\ttry {\n\t\t\tevents?.onOpen?.(new Event(\"open\"), ctx);\n\t\t} catch (e) {\n\t\t\t(options?.onError ?? console.error)(e);\n\t\t}\n\t\tconst handleMessage = (data, isBinary) => {\n\t\t\tconst datas = Array.isArray(data) ? data : [data];\n\t\t\tfor (const data of datas) try {\n\t\t\t\tevents?.onMessage?.(new MessageEvent(\"message\", { data: isBinary ? data instanceof ArrayBuffer ? data : data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) : typeof data === \"string\" ? data : Buffer.from(data).toString(\"utf-8\") }), ctx);\n\t\t\t} catch (e) {\n\t\t\t\t(options?.onError ?? console.error)(e);\n\t\t\t}\n\t\t};\n\t\tws.off(\"message\", bufferMessage);\n\t\tfor (const message of messagesReceivedInStarting) handleMessage(...message);\n\t\tws.on(\"message\", (data, isBinary) => {\n\t\t\thandleMessage(data, isBinary);\n\t\t});\n\t\tws.on(\"close\", (code, reason) => {\n\t\t\ttry {\n\t\t\t\tevents?.onClose?.(new CloseEvent(\"close\", {\n\t\t\t\t\tcode,\n\t\t\t\t\treason: reason.toString()\n\t\t\t\t}), ctx);\n\t\t\t} catch (e) {\n\t\t\t\t(options?.onError ?? console.error)(e);\n\t\t\t}\n\t\t});\n\t\tws.on(\"error\", (error) => {\n\t\t\ttry {\n\t\t\t\tevents?.onError?.(new ErrorEvent(\"error\", { error }), ctx);\n\t\t\t} catch (e) {\n\t\t\t\t(options?.onError ?? console.error)(e);\n\t\t\t}\n\t\t});\n\t})();\n\treturn new Response();\n});\n\n//#endregion\n//#region src/server.ts\nconst createAdaptorServer = (options) => {\n\tconst fetchCallback = options.fetch;\n\tconst requestListener = getRequestListener(fetchCallback, {\n\t\thostname: options.hostname,\n\t\toverrideGlobalObjects: options.overrideGlobalObjects,\n\t\tautoCleanupIncoming: options.autoCleanupIncoming\n\t});\n\tconst server = (options.createServer || createServer)(options.serverOptions || {}, requestListener);\n\tif (options.websocket && options.websocket.server) {\n\t\tif (options.websocket.server.options.noServer !== true) throw new Error(\"WebSocket server must be created with { noServer: true } option\");\n\t\tsetupWebSocket({\n\t\t\tserver,\n\t\t\tfetchCallback,\n\t\t\twss: options.websocket.server\n\t\t});\n\t}\n\treturn server;\n};\nconst serve = (options, listeningListener) => {\n\tconst server = createAdaptorServer(options);\n\tserver.listen(options?.port ?? 3e3, options.hostname, () => {\n\t\tconst serverInfo = server.address();\n\t\tlisteningListener && listeningListener(serverInfo);\n\t});\n\treturn server;\n};\n\n//#endregion\nexport { RequestError, createAdaptorServer, getRequestListener, serve, upgradeWebSocket };","import { readFileSync } from 'node:fs';\nimport type { Hono } from 'hono';\n\n// Process start, captured once at module load (bundled into each consumer, so this is\n// effectively the consumer process's start time).\nconst START_TIME = Date.now();\n\nlet shutdownHandler: (() => void) | null = null;\n\n/** Register the graceful-shutdown closure that `POST /admin/shutdown` invokes. Called by startServer. */\nexport function setShutdownHandler(fn: () => void): void {\n shutdownHandler = fn;\n}\n\n/**\n * Read the consuming package's version from its package.json at runtime.\n *\n * Pass `import.meta.url` from the CONSUMER module. core is bundled into each consumer, so\n * after the build this code runs from the consumer's `dist/` and package.json sits one\n * level up (`../package.json`). Never throws — returns 'unknown' on any failure.\n */\nexport function readPackageVersion(metaUrl: string): string {\n try {\n const pkg = JSON.parse(readFileSync(new URL('../package.json', metaUrl), 'utf8')) as {\n version?: string;\n };\n return pkg.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nexport interface BaseStatus {\n ok: true;\n version: string;\n startedAt: string;\n uptimeSeconds: number;\n}\n\nexport interface AdminRoutesOptions {\n /** Server version, typically `readPackageVersion(import.meta.url)`. */\n version: string;\n /** App-specific fields merged into the `/admin/status` payload (e.g. validator/pin counts). */\n getStatus?: () => Record<string, unknown>;\n}\n\n/**\n * Register the admin surface shared by every sm-lab service:\n * GET /admin/status → { ok, version, startedAt, uptimeSeconds, ...getStatus() }\n * POST /admin/shutdown → graceful shutdown (deferred 50ms so the response flushes first)\n */\nexport function registerAdminRoutes(app: Hono, opts: AdminRoutesOptions): void {\n app.get('/admin/status', (c) => {\n const base: BaseStatus = {\n ok: true,\n version: opts.version,\n startedAt: new Date(START_TIME).toISOString(),\n uptimeSeconds: Math.floor((Date.now() - START_TIME) / 1000),\n };\n return c.json({ ...base, ...opts.getStatus?.() });\n });\n\n app.post('/admin/shutdown', (c) => {\n if (shutdownHandler) setTimeout(shutdownHandler, 50);\n return c.json({ message: 'shutting down' });\n });\n}\n","import { Command } from 'commander';\n\n/** Climb to the root program so commands at any nesting depth can read root-level options (e.g. --url). */\nexport function findRoot(cmd: Command): Command {\n let c = cmd;\n while (c.parent) c = c.parent;\n return c;\n}\n\nexport interface ClientTarget {\n /** Env var holding the server URL (e.g. CL_MOCK_URL). */\n envVar: string;\n /** Default port when neither --url nor the env var is set. */\n defaultPort: number;\n}\n\n/** Resolve the target server URL: root `--url` option → env var → `http://127.0.0.1:<defaultPort>`. */\nexport function resolveUrl(cmd: Command, target: ClientTarget): string {\n const opts = findRoot(cmd).opts() as { url?: string };\n return opts.url ?? process.env[target.envVar] ?? `http://127.0.0.1:${target.defaultPort}`;\n}\n\n/** Format seconds as a compact `1h 2m 3s` string (omits leading zero units). */\nexport function formatUptime(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = seconds % 60;\n const parts: string[] = [];\n if (h) parts.push(`${h}h`);\n if (m || h) parts.push(`${m}m`);\n parts.push(`${s}s`);\n return parts.join(' ');\n}\n\nexport interface BaseStatusResponse {\n ok: boolean;\n version: string;\n startedAt: string;\n uptimeSeconds: number;\n}\n\nexport interface StatusCommandOptions<T extends BaseStatusResponse> extends ClientTarget {\n /** App-specific help text; defaults to 'Show status of a running server'. */\n description?: string;\n /** Print app-specific lines after the shared header (URL/Status/Version/Started/Uptime). */\n render?: (data: T, url: string) => void;\n}\n\n/**\n * Build a `status` command: GET /admin/status, print the shared header, then app-specific\n * lines via `render`. `--json` dumps the raw payload. On connect failure prints\n * `Error: <url> offline (<reason>)` to stderr and exits 1 — offline is a legitimate answer, and\n * the `Error:` prefix + exit 1 keep the machine I/O contract uniform with the other commands.\n */\nexport function createStatusCommand<T extends BaseStatusResponse>(\n opts: StatusCommandOptions<T>,\n): Command {\n return new Command('status')\n .description(opts.description ?? 'Show status of a running server')\n .option('--json', 'output raw JSON')\n .action(async (cmdOpts: { json?: boolean }, cmd: Command) => {\n const url = resolveUrl(cmd, opts);\n let res: Response;\n try {\n res = await fetch(`${url}/admin/status`);\n } catch (err) {\n console.error(\n 'Error:',\n `${url} offline (${err instanceof Error ? err.message : String(err)})`,\n );\n process.exit(1);\n }\n if (!res.ok) {\n console.error(`Unexpected response: ${res.status}`);\n process.exit(1);\n }\n const data = (await res.json()) as T;\n if (cmdOpts.json) {\n console.log(JSON.stringify(data, null, 2));\n return;\n }\n console.log(`URL: ${url}`);\n console.log(`Status: ok`);\n console.log(`Version: ${data.version}`);\n console.log(`Started: ${data.startedAt}`);\n console.log(`Uptime: ${formatUptime(data.uptimeSeconds)}`);\n opts.render?.(data, url);\n });\n}\n\nexport interface StopCommandOptions extends ClientTarget {\n /** App-specific help text; defaults to 'Stop a running server'. */\n description?: string;\n}\n\n/** Build a `stop` command: POST /admin/shutdown to the resolved URL. */\nexport function createStopCommand(target: StopCommandOptions): Command {\n return new Command('stop')\n .description(target.description ?? 'Stop a running server')\n .action(async (_cmdOpts: unknown, cmd: Command) => {\n const url = resolveUrl(cmd, target);\n let res: Response;\n try {\n res = await fetch(`${url}/admin/shutdown`, { method: 'POST' });\n } catch (err) {\n console.error(\n `Failed to connect to ${url}: ${err instanceof Error ? err.message : String(err)}`,\n );\n process.exit(1);\n }\n if (res.ok) {\n console.log('Server shutting down');\n } else {\n console.error(`Unexpected response: ${res.status}`);\n process.exit(1);\n }\n });\n}\n","import { Argument, Command } from 'commander';\nimport type { Option } from 'commander';\nimport { findRoot } from './cli';\n\nexport type CompletionShell = 'bash' | 'zsh' | 'fish';\n\nconst SHELLS: readonly CompletionShell[] = ['bash', 'zsh', 'fish'];\n\n// -- Normalised command tree --------------------------------------------------\n// commander@15 has no completion support, so we snapshot the registered tree\n// into plain data and emit a fully static script per shell (no runtime hooks).\n\ninterface OptionNode {\n /** Short flag with dash, e.g. `-c`. */\n short?: string;\n /** Long flag with dashes, e.g. `--count`. */\n long?: string;\n description: string;\n /** True when the option consumes a value (`<x>` or `[x]`). */\n takesValue: boolean;\n choices?: string[];\n}\n\ninterface ArgNode {\n name: string;\n description: string;\n variadic: boolean;\n choices?: string[];\n}\n\ninterface CommandNode {\n name: string;\n aliases: string[];\n description: string;\n /** Canonical subcommand names from the root down to this node ([] for the root itself). */\n path: string[];\n options: OptionNode[];\n args: ArgNode[];\n children: CommandNode[];\n}\n\nfunction firstLine(text: string): string {\n return (text.split('\\n')[0] ?? '').trim();\n}\n\nfunction toNode(cmd: Command, path: string[]): CommandNode {\n return {\n name: cmd.name(),\n aliases: [...cmd.aliases()],\n description: firstLine(cmd.description()),\n path,\n options: cmd.options\n .filter((o: Option) => !o.hidden)\n .map((o: Option) => ({\n short: o.short,\n long: o.long,\n description: firstLine(o.description),\n takesValue: o.required || o.optional,\n choices: o.argChoices ? [...o.argChoices] : undefined,\n })),\n args: cmd.registeredArguments.map((a: Argument) => ({\n name: a.name(),\n description: firstLine(a.description),\n variadic: a.variadic,\n choices: a.argChoices ? [...a.argChoices] : undefined,\n })),\n children: cmd.commands.map((c) => toNode(c, [...path, c.name()])),\n };\n}\n\n/** Flatten in registration order — the emitters iterate this, so output is deterministic. */\nfunction walk(node: CommandNode): CommandNode[] {\n return [node, ...node.children.flatMap(walk)];\n}\n\nfunction namesOf(node: CommandNode): string[] {\n return [node.name, ...node.aliases];\n}\n\n// -- fish ---------------------------------------------------------------------\n\nfunction fishQuote(s: string): string {\n return `'${s.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")}'`;\n}\n\nfunction emitFish(bin: string, root: CommandNode): string {\n const lines = [\n `# fish completion for ${bin}`,\n `# Load with: ${bin} completion fish | source`,\n `# Or install: ${bin} completion fish > ~/.config/fish/completions/${bin}.fish`,\n ];\n for (const node of walk(root)) {\n const seen = node.path.map((p) => `__fish_seen_subcommand_from ${p}`);\n const childNames = node.children.flatMap(namesOf);\n const hereParts = [...seen];\n // `-f` on subcommand candidates suppresses file completion at this position;\n // positions with no matching rule keep fish's default file completion.\n if (childNames.length > 0) {\n hereParts.push(`not __fish_seen_subcommand_from ${childNames.join(' ')}`);\n }\n const here = hereParts.length > 0 ? ` -n ${fishQuote(hereParts.join('; and '))}` : '';\n for (const child of node.children) {\n for (const name of namesOf(child)) {\n lines.push(`complete -c ${bin}${here} -f -a ${name} -d ${fishQuote(child.description)}`);\n }\n }\n for (const opt of node.options) {\n let line = `complete -c ${bin}${here}`;\n if (opt.short) line += ` -s ${opt.short.replace(/^-+/, '')}`;\n if (opt.long) line += ` -l ${opt.long.replace(/^-+/, '')}`;\n if (opt.takesValue) {\n line += opt.choices ? ` -x -a ${fishQuote(opt.choices.join(' '))}` : ' -r';\n }\n line += ` -d ${fishQuote(opt.description)}`;\n lines.push(line);\n }\n const atArgs = seen.length > 0 ? ` -n ${fishQuote(seen.join('; and '))}` : '';\n for (const arg of node.args) {\n if (arg.choices) {\n const desc = arg.description || arg.name;\n lines.push(\n `complete -c ${bin}${atArgs} -f -a ${fishQuote(arg.choices.join(' '))} -d ${fishQuote(desc)}`,\n );\n }\n }\n }\n return `${lines.join('\\n')}\\n`;\n}\n\n// -- bash ---------------------------------------------------------------------\n\nfunction sanitizeIdent(bin: string): string {\n return bin.replace(/[^A-Za-z0-9_]/g, '_');\n}\n\n/**\n * Case arms that descend the known subcommand tree token by token, normalising\n * aliases to the canonical path so a single lookup table serves both spellings.\n */\nfunction descentCases(root: CommandNode, indent: string): string[] {\n return walk(root)\n .filter((node) => node.path.length > 0)\n .map((node) => {\n const parent = node.path.slice(0, -1).join(' ');\n const patterns = namesOf(node)\n .map((name) => `\"${parent ? `${parent} ${name}` : name}\"`)\n .join('|');\n return `${indent}${patterns}) path=\"${node.path.join(' ')}\" ;;`;\n });\n}\n\nfunction emitBash(bin: string, root: CommandNode): string {\n const fn = `_${sanitizeIdent(bin)}_completions`;\n const optCases = walk(root).map((node) => {\n const subs = node.children.flatMap(namesOf).join(' ');\n const flags = node.options\n .flatMap((o) => [o.short, o.long])\n .filter((f): f is string => f !== undefined)\n .join(' ');\n const args = node.args.flatMap((a) => a.choices ?? []).join(' ');\n return ` \"${node.path.join(' ')}\") subs=\"${subs}\"; flags=\"${flags}\"; args=\"${args}\" ;;`;\n });\n return [\n `# bash completion for ${bin}`,\n `# Load with: source <(${bin} completion bash)`,\n `${fn}() {`,\n ' local cur path try w i subs flags args',\n ' cur=\"${COMP_WORDS[COMP_CWORD]}\"',\n ' path=\"\"',\n ' for ((i = 1; i < COMP_CWORD; i++)); do',\n ' w=\"${COMP_WORDS[i]}\"',\n ' [[ \"$w\" == -* ]] && continue',\n ' try=\"${path:+$path }$w\"',\n ' case \"$try\" in',\n ...descentCases(root, ' '),\n ' *) ;;',\n ' esac',\n ' done',\n ' subs=\"\"; flags=\"\"; args=\"\"',\n ' case \"$path\" in',\n ...optCases,\n ' esac',\n ' if [[ \"$cur\" == -* ]]; then',\n ' COMPREPLY=($(compgen -W \"$flags\" -- \"$cur\"))',\n ' elif [[ -n \"$subs\" ]]; then',\n ' COMPREPLY=($(compgen -W \"$subs\" -- \"$cur\"))',\n ' elif [[ -n \"$args\" ]]; then',\n ' COMPREPLY=($(compgen -W \"$args\" -- \"$cur\"))',\n ' else',\n ' COMPREPLY=($(compgen -f -- \"$cur\"))',\n ' fi',\n '}',\n `complete -F ${fn} ${bin}`,\n '',\n ].join('\\n');\n}\n\n// -- zsh ----------------------------------------------------------------------\n\nfunction zshItem(name: string, description: string): string {\n const entry = `${name}:${description}`.replace(/'/g, `'\\\\''`);\n return `'${entry}'`;\n}\n\nfunction emitZsh(bin: string, root: CommandNode): string {\n const fn = `_${sanitizeIdent(bin)}`;\n const optCases = walk(root).map((node) => {\n const subs = node.children\n .flatMap((child) => namesOf(child).map((name) => zshItem(name, child.description)))\n .join(' ');\n const flags = node.options\n .flatMap((o) =>\n [o.short, o.long]\n .filter((f): f is string => f !== undefined)\n .map((f) => zshItem(f, o.description)),\n )\n .join(' ');\n const args = node.args\n .flatMap((a) => (a.choices ?? []).map((c) => zshItem(c, a.description || a.name)))\n .join(' ');\n const body = [\n subs ? `subs=(${subs})` : undefined,\n flags ? `flags=(${flags})` : undefined,\n args ? `argwords=(${args})` : undefined,\n ]\n .filter((p) => p !== undefined)\n .join('; ');\n return ` \"${node.path.join(' ')}\") ${body ? `${body} ` : ''};;`;\n });\n return [\n `#compdef ${bin}`,\n `# zsh completion for ${bin}`,\n `# Install: ${bin} completion zsh > \"\\${fpath[1]}/_${bin}\" (then restart zsh)`,\n `${fn}() {`,\n ' local -a subs flags argwords',\n ' local path=\"\" try w',\n ' for w in \"${(@)words[2,CURRENT-1]}\"; do',\n ' [[ \"$w\" == -* ]] && continue',\n ' try=\"${path:+$path }$w\"',\n ' case \"$try\" in',\n ...descentCases(root, ' '),\n ' *) ;;',\n ' esac',\n ' done',\n ' case \"$path\" in',\n ...optCases,\n ' esac',\n ' if [[ \"$PREFIX\" == -* ]]; then',\n \" (( ${#flags[@]} )) && _describe -t options 'option' flags\",\n ' elif (( ${#subs[@]} )); then',\n \" _describe -t commands 'command' subs\",\n ' elif (( ${#argwords[@]} )); then',\n \" _describe -t values 'value' argwords\",\n ' else',\n ' _files',\n ' fi',\n '}',\n `if [[ \"\\${funcstack[1]}\" == \"${fn}\" ]]; then`,\n ` ${fn} \"$@\"`,\n 'else',\n ` compdef ${fn} ${bin}`,\n 'fi',\n '',\n ].join('\\n');\n}\n\n// -- public API -----------------------------------------------------------------\n\n/**\n * Build a fully static, self-contained shell-completion script for `root`'s command\n * tree (subcommands at any depth, aliases, flags, option/argument choices). Pure:\n * output depends only on the tree, iterated in registration order.\n */\nexport function buildCompletionScript(root: Command, shell: CompletionShell): string {\n const bin = root.name();\n const tree = toNode(root, []);\n switch (shell) {\n case 'fish':\n return emitFish(bin, tree);\n case 'bash':\n return emitBash(bin, tree);\n case 'zsh':\n return emitZsh(bin, tree);\n }\n}\n\n/**\n * Build a `completion <shell>` subcommand that prints the static completion script\n * for the ROOT program (resolved by walking `.parent`) to stdout.\n */\nexport function createCompletionCommand(): Command {\n return new Command('completion')\n .description(\n 'Print a static shell-completion script for bash, zsh or fish. ' +\n 'Load it in your shell, e.g. fish: `sm-cl completion fish | source`',\n )\n .addArgument(new Argument('<shell>', 'target shell').choices(SHELLS))\n .action((shell: CompletionShell, _opts: unknown, cmd: Command) => {\n process.stdout.write(buildCompletionScript(findRoot(cmd), shell));\n });\n}\n","import { Command } from 'commander';\nimport { createCompletionCommand, readPackageVersion } from '@sm-lab/core';\nimport {\n makeAddresses as realMakeAddresses,\n makeStrikes as realMakeStrikes,\n makeRewards as realMakeRewards,\n} from '../pipelines';\nimport { readAddressFile, readJsonFile } from '../io';\nimport type { MakeResult } from '../pipelines';\n\n/** Injectable seam: tests pass fake pipelines so CLI parsing is verified hermetically. */\nexport interface CliDeps {\n makeAddresses: (\n addresses: string[],\n opts: Parameters<typeof realMakeAddresses>[1],\n ) => Promise<MakeResult>;\n makeStrikes: typeof realMakeStrikes;\n makeRewards: typeof realMakeRewards;\n}\n\n/** Wrap an async action so thrown errors print cleanly and exit non-zero. */\nfunction run(fn: () => Promise<void>): void {\n fn().catch((err: unknown) => {\n console.error('Error:', err instanceof Error ? err.message : String(err));\n process.exit(1);\n });\n}\n\nconst bigintReplacer = (_k: string, v: unknown): unknown =>\n typeof v === 'bigint' ? v.toString() : v;\n\nfunction report(label: string, result: MakeResult & { logCid?: string }): void {\n console.log(`${label} tree root: ${result.treeRoot}`);\n console.log(`${label} tree CID: ${result.treeCid ?? '(upload skipped)'}`);\n if (result.logCid) console.log(`${label} log CID: ${result.logCid}`);\n if (result.configPath) console.log(`Wrote ${result.configPath}`);\n}\n\nexport function buildProgram(\n deps: CliDeps = {\n makeAddresses: realMakeAddresses,\n makeStrikes: realMakeStrikes,\n makeRewards: realMakeRewards,\n },\n): Command {\n const program = new Command()\n .name('sm-merkle')\n .description('Lido SM Merkle tree builder — build a tree, pin it to IPFS, print root + CID')\n .version(readPackageVersion(import.meta.url))\n // We ship our own `help` command below; suppress the built-in to avoid a duplicate-command\n // collision. `.helpCommand(false)` is the non-deprecated replacement for `.addHelpCommand(false)`.\n .helpCommand(false);\n\n // ---------------------------------------------------------------------------\n // `addresses` — default command (bare `sm-merkle 0x.. 0x..` routes here)\n // ---------------------------------------------------------------------------\n program\n .command('addresses', { isDefault: true })\n .description('Build the addresses (vetted gate) tree, pin it to IPFS, print root + CID')\n .argument('[addresses...]', 'whitelist addresses (inline, or use --input / --source)')\n .option(\n '--input <addr>',\n 'repeatable: add one address to the list',\n (v: string, acc: string[]) => {\n acc.push(v);\n return acc;\n },\n [] as string[],\n )\n .option('--source <file>', 'load address list from a JSON array or newline-delimited .txt')\n .option('--no-upload', 'build/print root only, skip IPFS pinning')\n .option('-o, --out <path>', 'also write { treeRoot, treeCid } JSON to this path')\n .option('--json', 'print result as JSON to stdout (machine-readable)')\n .action(\n (\n positionals: string[],\n opts: {\n input: string[];\n source?: string;\n upload: boolean;\n out?: string;\n json?: boolean;\n },\n ) => {\n run(async () => {\n const hasInline = positionals.length > 0 || opts.input.length > 0;\n const hasFile = Boolean(opts.source);\n if (hasInline && hasFile) {\n throw new Error(\n 'Cannot combine inline addresses (positionals / --input) with --source <file>. Use one or the other.',\n );\n }\n let addresses: string[];\n if (hasFile) {\n addresses = readAddressFile(opts.source!);\n } else {\n addresses = [...positionals, ...opts.input];\n }\n if (addresses.length === 0) {\n throw new Error(\n 'No addresses supplied. Provide positional addresses, --input <addr>, or --source <file>.',\n );\n }\n const result = await deps.makeAddresses(addresses, {\n noUpload: !opts.upload,\n configPath: opts.out,\n });\n if (opts.json) {\n console.log(JSON.stringify(result, bigintReplacer, 2));\n } else {\n report('Addresses', result);\n }\n });\n },\n );\n\n // ---------------------------------------------------------------------------\n // `strikes`\n // ---------------------------------------------------------------------------\n program\n .command('strikes')\n .description('Build the strikes tree, pin it to IPFS, print root + CID')\n .argument(\n '<strikes>',\n 'path to strikes.json — JSON array [{ nodeOperatorId, pubkey, strikes: number[] }]',\n )\n .option('--source <file>', 'alternative flag for the strikes file path (same as positional)')\n .option('--no-upload', 'build/print root only, skip IPFS pinning')\n .option('-o, --out <path>', 'also write { treeRoot, treeCid } JSON to this path')\n .option('--json', 'print result as JSON to stdout (machine-readable)')\n .action(\n (\n strikesArg: string,\n opts: { source?: string; upload: boolean; out?: string; json?: boolean },\n ) => {\n run(async () => {\n const strikesPath = opts.source ?? strikesArg;\n const result = await deps.makeStrikes(strikesPath, {\n noUpload: !opts.upload,\n configPath: opts.out,\n });\n if (opts.json) {\n console.log(JSON.stringify(result, bigintReplacer, 2));\n } else {\n report('Strikes', result);\n }\n });\n },\n );\n\n // ---------------------------------------------------------------------------\n // `rewards`\n // ---------------------------------------------------------------------------\n program\n .command('rewards')\n .description('Build the rewards tree from [nodeOperatorId, cumulativeShares] pairs, pin it')\n .requiredOption(\n '--source <file>',\n 'JSON array of [nodeOperatorId, cumulativeShares] pairs (number or numeric-string)',\n )\n .option('--no-upload', 'build/print root only, skip IPFS pinning')\n .option('-o, --out <path>', 'also write { treeRoot, treeCid } JSON to this path')\n .option('--json', 'print result as JSON to stdout (machine-readable)')\n .action((opts: { source: string; upload: boolean; out?: string; json?: boolean }) => {\n run(async () => {\n const raw = readJsonFile<[unknown, unknown][]>(opts.source);\n if (raw.length === 0) {\n throw new Error(\n 'No leaves supplied. Provide a non-empty JSON array of [nodeOperatorId, cumulativeShares] pairs.',\n );\n }\n const leaves: [bigint, bigint][] = raw.map(([noId, shares], i) => {\n const toBig = (v: unknown, field: string): bigint => {\n if (typeof v === 'bigint') return v;\n if (typeof v === 'number' || typeof v === 'string') return BigInt(v);\n throw new Error(\n `rewards --source: entry [${i}].${field} must be a number or numeric string, got ${typeof v}`,\n );\n };\n return [toBig(noId, '0'), toBig(shares, '1')];\n });\n const result = await deps.makeRewards(leaves, {\n noUpload: !opts.upload,\n configPath: opts.out,\n });\n if (opts.json) {\n console.log(JSON.stringify(result, bigintReplacer, 2));\n } else {\n report('Rewards', result);\n }\n });\n });\n\n // ---------------------------------------------------------------------------\n // `help` — self-contained cheat sheet\n // ---------------------------------------------------------------------------\n program\n .command('help')\n .description('Print a self-contained usage cheat sheet')\n .action(() => {\n console.log(`sm-merkle — Lido SM Merkle tree builder\n\nWHAT IT DOES\n Build a Merkle tree from input, pin it to IPFS, and print the root + CID.\n Pushing the root/CID on-chain is NOT this tool's job — that's @sm-lab/receipts.\n\nCOMMANDS\n addresses [addresses...] build the addresses (vetted gate) tree (DEFAULT — bare args route here)\n strikes <strikes> build the strikes tree, pin to IPFS, print root + CID\n rewards --source <file> build the rewards tree from [noId, cumulativeShares] pairs\n completion <shell> print a bash|zsh|fish completion script (sm-merkle completion fish | source)\n help print this cheat sheet\n\nFLAGS (all commands)\n --no-upload build/print the root only, skip IPFS pinning\n -o, --out <path> also write { treeRoot, treeCid } JSON to <path>\n --json print result as a single JSON value to stdout (machine-readable)\n\nFLAGS (addresses only)\n --input <addr> repeatable: add one address (can mix with positionals)\n --source <file> load from JSON array [\"0x..\"] or newline-delimited .txt\n (mutually exclusive with inline positionals / --input)\n\nENV\n IPFS_API_URL pinning endpoint; unset → local @sm-lab/ipfs (http://127.0.0.1:5001).\n Pinata used only when PINATA_* creds are set (and IPFS_API_URL is unset).\n PINATA_API_KEY/SECRET Pinata credentials (or PINATA_JWT). Ignored by the mock.\n\nDATA FORMATS\n addresses JSON array [\"0x..\", ...] or newline-delimited text (# comments ok)\n strikes JSON array [{ nodeOperatorId, pubkey, strikes: number[] }]\n rewards JSON array [[nodeOperatorId, cumulativeShares], ...] (numbers or numeric strings)\n\nEXAMPLES\n sm-merkle 0xABC 0xDEF # inline addresses → addresses (vetted gate) tree\n sm-merkle addresses --source addrs.json --json\n sm-merkle addresses --input 0xABC --input 0xDEF --no-upload\n sm-merkle strikes strikes.json --no-upload --json\n sm-merkle rewards --source rewards.json --json\n sm-merkle addresses addrs.json -o config.json`);\n });\n\n program.addCommand(createCompletionCommand());\n\n return program;\n}\n","#!/usr/bin/env node\n\n// eslint-disable-next-line import/no-unassigned-import -- side-effect import: loads .env\nimport 'dotenv/config';\nimport { buildProgram } from './program';\n\nbuildProgram()\n .parseAsync()\n .catch((err: unknown) => {\n console.error('Error:', err instanceof Error ? err.message : String(err));\n process.exit(1);\n });\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
@@ -0,0 +1,178 @@
1
+ import { StandardMerkleTree } from "@openzeppelin/merkle-tree";
2
+
3
+ //#region src/tree.d.ts
4
+ /**
5
+ * Pure, deterministic tree construction. No I/O, no network — given the same inputs these
6
+ * functions always produce the same root, which is exactly what the Vitest suite pins.
7
+ *
8
+ * Three OZ StandardMerkleTree shapes, one per CSM pipeline:
9
+ * - addresses (vetted gate): ["address"] → VettedGate.setTreeParams
10
+ * - strikes: ["uint256","string","uint256[]"] → CSStrikes.processOracleReport
11
+ * - rewards: ["uint256","uint256"] → FeeDistributor cumulative tree
12
+ */
13
+ /** Leaf encoding for the addresses (vetted gate) tree: one address per leaf. */
14
+ declare const ADDRESSES_LEAF_ENCODING: readonly ["address"];
15
+ /** Leaf encoding for the strikes tree: (nodeOperatorId, pubkey, strikes[]). */
16
+ declare const STRIKES_LEAF_ENCODING: readonly ["uint256", "string", "uint256[]"];
17
+ /** Leaf encoding for the rewards tree: (nodeOperatorId, cumulativeShares). */
18
+ declare const REWARDS_LEAF_ENCODING: readonly ["uint256", "uint256"];
19
+ /** A single node-operator strikes record, as stored in strikes.json. */
20
+ interface StrikesEntry {
21
+ nodeOperatorId: number;
22
+ pubkey: string;
23
+ strikes: number[];
24
+ }
25
+ /** The OZ tree dump shape we pin to IPFS (kept structural to avoid leaking OZ internals). */
26
+ type TreeDump = ReturnType<StandardMerkleTree<unknown[]>['dump']>;
27
+ /** Build the addresses (vetted gate) Merkle tree from a list of whitelisted addresses. */
28
+ declare function buildAddressesTree(addresses: string[]): StandardMerkleTree<[string]>;
29
+ /** Build the strikes Merkle tree from node-operator strike records. */
30
+ declare function buildStrikesTree(entries: StrikesEntry[]): StandardMerkleTree<[number, string, number[]]>;
31
+ /**
32
+ * Build the cumulative rewards tree: one [nodeOperatorId, cumulativeShares] leaf per operator.
33
+ *
34
+ * Leaf values are `bigint` (not `number` like buildStrikesTree) because cumulative reward shares
35
+ * are wei amounts that routinely overflow `Number.MAX_SAFE_INTEGER`. OZ serializes them as decimal
36
+ * strings in `dump()` (JSON-safe). Callers shape the leaves — e.g. the FeeDistributor non-empty-proof
37
+ * pad leaf is appended by the caller, not here (same division of labor as the addresses/strikes builders).
38
+ */
39
+ declare function buildRewardsTree(leaves: [bigint, bigint][]): StandardMerkleTree<[bigint, bigint]>;
40
+ //#endregion
41
+ //#region src/io.d.ts
42
+ /**
43
+ * Pure file I/O helpers. Kept small and side-effect-explicit so the deterministic parsing
44
+ * (address-file, strikes.json) can be unit-tested against fixtures.
45
+ */
46
+ /** The `{ treeRoot, treeCid }` shape written by the optional `-o` handoff file. */
47
+ interface TreeConfig {
48
+ treeRoot: string;
49
+ treeCid: string;
50
+ }
51
+ /** Read+parse a JSON file as `T`. Throws (with the path) if missing. */
52
+ declare function readJsonFile<T>(filePath: string): T;
53
+ /**
54
+ * Parse an address list. Accepts either a JSON array (`["0x..", ...]`) or a newline-delimited
55
+ * text file (blank lines and `#` comments ignored) — the original tool supported both shapes.
56
+ */
57
+ declare function parseAddresses(content: string): string[];
58
+ /** Read and parse an address file (JSON array or newline-delimited text). */
59
+ declare function readAddressFile(filePath: string): string[];
60
+ /** Read and parse a strikes.json file. */
61
+ declare function readStrikesFile(filePath: string): StrikesEntry[];
62
+ /** Write a JSON file, creating parent directories as needed. */
63
+ declare function writeJsonFile(filePath: string, data: unknown): void;
64
+ //#endregion
65
+ //#region src/ipfs.d.ts
66
+ /**
67
+ * IPFS pinning client — Pinata-compatible, with an env-switchable endpoint.
68
+ *
69
+ * Why not `@pinata/sdk` directly? The installed v2 SDK hardcodes
70
+ * `baseUrl = 'https://api.pinata.cloud'` (see its `src/constants.ts`); its `PinataConfig`
71
+ * exposes only API keys / JWT, no host override. To target `@sm-lab/ipfs` locally we
72
+ * need a configurable base URL, so this is a thin `fetch` client hitting the exact same
73
+ * `/pinning/pinJSONToIPFS` route the mock implements. Point it at real Pinata in test-infra
74
+ * by supplying PINATA_* credentials (no need to set IPFS_API_URL when using Pinata).
75
+ *
76
+ * Default resolution (local-first):
77
+ * explicit apiUrl → IPFS_API_URL env → Pinata (if PINATA_* set) → local @sm-lab/ipfs
78
+ */
79
+ /** Real Pinata host. Used when PINATA_* credentials are set but IPFS_API_URL is unset. */
80
+ declare const DEFAULT_IPFS_API_URL = "https://api.pinata.cloud";
81
+ /** Local @sm-lab/ipfs default — the fallback when no IPFS_API_URL or Pinata creds are set. */
82
+ declare const LOCAL_IPFS_API_URL = "http://127.0.0.1:5001";
83
+ interface IpfsClientOptions {
84
+ /** Base URL of the pinning service. Defaults to `IPFS_API_URL` env, then real Pinata. */
85
+ apiUrl?: string;
86
+ /** Pinata API key (header `pinata_api_key`). Mocks may ignore it. */
87
+ apiKey?: string;
88
+ /** Pinata API secret (header `pinata_secret_api_key`). Mocks may ignore it. */
89
+ apiSecret?: string;
90
+ /** Pinata JWT (header `Authorization: Bearer …`). Alternative to key/secret. */
91
+ jwt?: string;
92
+ }
93
+ /** Shape of a Pinata `pinJSONToIPFS` success response. */
94
+ interface PinResponse {
95
+ IpfsHash: string;
96
+ PinSize: number;
97
+ Timestamp: string;
98
+ }
99
+ /**
100
+ * Resolve the pinning base URL: explicit apiUrl → IPFS_API_URL env → Pinata (if PINATA_* set)
101
+ * → local @sm-lab/ipfs (http://127.0.0.1:5001).
102
+ */
103
+ declare function resolveIpfsApiUrl(apiUrl?: string): string;
104
+ /** Read pinning config from the environment (credentials + endpoint switch). */
105
+ declare function ipfsOptionsFromEnv(): IpfsClientOptions;
106
+ /** True when enough credentials are present to talk to real Pinata. */
107
+ declare function hasPinataCredentials(opts?: IpfsClientOptions): boolean;
108
+ /**
109
+ * True when a non-default pinning endpoint is configured — i.e. `IPFS_API_URL` points
110
+ * somewhere other than real Pinata (typically a local `@sm-lab/ipfs`). Such endpoints
111
+ * accept unauthenticated pins, so credentials are not required to upload to them.
112
+ */
113
+ declare function hasCustomIpfsEndpoint(opts?: IpfsClientOptions): boolean;
114
+ /**
115
+ * Whether `make`/`tree` should attempt to pin. With the local-first default there is always a
116
+ * usable target, so this returns `true` unless an explicit `IPFS_API_URL` points directly at
117
+ * real Pinata without credentials (that edge case cannot pin, so we still return `false` there).
118
+ * Use the CLI's `--no-upload` / `MakeOptions.noUpload` to explicitly skip pinning.
119
+ */
120
+ declare function shouldAttemptPin(opts?: IpfsClientOptions): boolean;
121
+ /**
122
+ * Pin a JSON object and return its CID. POSTs the Pinata `pinJSONToIPFS` envelope
123
+ * (`{ pinataContent, pinataMetadata }`) so both real Pinata and `@sm-lab/ipfs` accept it.
124
+ */
125
+ declare function pinJsonToIpfs(data: unknown, metadataName: string, opts?: IpfsClientOptions): Promise<string>;
126
+ //#endregion
127
+ //#region src/pipelines.d.ts
128
+ /**
129
+ * merkle's single job: build a Merkle tree from input, pin it to IPFS, and return the
130
+ * root + CID. Pushing those on-chain (and resolving deploy addresses) is out of scope —
131
+ * that belongs to `@sm-lab/receipts`. No `cast`, no `DEPLOY_JSON_PATH` here.
132
+ */
133
+ interface MakeResult {
134
+ treeRoot: string;
135
+ /** undefined when IPFS upload was skipped (--no-upload, or nothing configured). */
136
+ treeCid?: string;
137
+ /** set only when `configPath` was provided and a `{ treeRoot, treeCid }` file was written. */
138
+ configPath?: string;
139
+ }
140
+ interface MakeRewardsResult extends MakeResult {
141
+ logCid?: string;
142
+ /**
143
+ * JSON-safe tree dump (bigint leaf values serialized as decimal strings).
144
+ * Identical shape to OZ `StandardMerkleTree.dump()` but with string values, so
145
+ * `JSON.stringify(treeDump)` is safe without a custom replacer.
146
+ */
147
+ treeDump: TreeDump;
148
+ }
149
+ interface MakeOptions {
150
+ /** Skip pinning to IPFS (build + return root only). */
151
+ noUpload?: boolean;
152
+ /** When set, also write `{ treeRoot, treeCid }` JSON here (a handoff seam for receipts/CI). */
153
+ configPath?: string;
154
+ }
155
+ /**
156
+ * Addresses (vetted gate): build the address tree from a resolved address list, pin it, return
157
+ * `{ treeRoot, treeCid }`. The CLI resolves file/inline/flag inputs to `string[]` before calling
158
+ * this — keeping this function pure so it can be called directly from TS consumers without
159
+ * touching the filesystem.
160
+ */
161
+ declare function makeAddresses(addresses: string[], opts?: MakeOptions): Promise<MakeResult>;
162
+ /** Strikes: build the strikes tree, pin it, return `{ treeRoot, treeCid }`. */
163
+ declare function makeStrikes(strikesPath: string, opts?: MakeOptions): Promise<MakeResult>;
164
+ /**
165
+ * Rewards: build the cumulative rewards tree from `[nodeOperatorId, cumulativeShares]` leaves
166
+ * (bigint values), optionally pin the tree dump and a log object, and return
167
+ * `{ treeRoot, treeCid, logCid?, treeDump }`.
168
+ *
169
+ * Bigints in the tree dump and `opts.log` are serialized to decimal strings before pinning
170
+ * (OZ `dump()` returns leaf values verbatim, which are bigints here). The returned `treeDump`
171
+ * uses the same serialization so it is JSON-safe (string values, no BigInt).
172
+ */
173
+ declare function makeRewards(leaves: [bigint, bigint][], opts?: MakeOptions & {
174
+ log?: unknown;
175
+ }): Promise<MakeRewardsResult>;
176
+ //#endregion
177
+ export { ADDRESSES_LEAF_ENCODING, DEFAULT_IPFS_API_URL, type IpfsClientOptions, LOCAL_IPFS_API_URL, type MakeOptions, type MakeResult, type MakeRewardsResult, type PinResponse, REWARDS_LEAF_ENCODING, STRIKES_LEAF_ENCODING, type StrikesEntry, type TreeConfig, type TreeDump, buildAddressesTree, buildRewardsTree, buildStrikesTree, hasCustomIpfsEndpoint, hasPinataCredentials, ipfsOptionsFromEnv, makeAddresses, makeRewards, makeStrikes, parseAddresses, pinJsonToIpfs, readAddressFile, readJsonFile, readStrikesFile, resolveIpfsApiUrl, shouldAttemptPin, writeJsonFile };
178
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/tree.ts","../src/io.ts","../src/ipfs.ts","../src/pipelines.ts"],"mappings":";;;;;AAaA;;;;AAA2D;AAG3D;;;cAHa,uBAAA;AAGmE;AAAA,cAAnE,qBAAA;;cAGA,qBAAA;;UAGI,YAAA;EACf,cAAA;EACA,MAAA;EACA,OAAA;AAAA;;KAIU,QAAA,GAAW,UAAU,CAAC,kBAAA;;iBAGlB,kBAAA,CAAmB,SAAA,aAAsB,kBAAkB;AAPlE;AAAA,iBAeO,gBAAA,CACd,OAAA,EAAS,YAAA,KACR,kBAAkB;;;;AAb+B;AAGpD;;;;iBAyBgB,gBAAA,CAAiB,MAAA,uBAA6B,kBAAkB;;;;;AA5ChF;;;UCHiB,UAAA;EACf,QAAA;EACA,OAAO;AAAA;;iBAIO,YAAA,IAAgB,QAAA,WAAmB,CAAC;ADA4B;AAGhF;;;AAHgF,iBCWhE,cAAA,CAAe,OAAe;ADRsB;AAAA,iBCoBpD,eAAA,CAAgB,QAAgB;;iBAQhC,eAAA,CAAgB,QAAA,WAAmB,YAAY;;iBAK/C,aAAA,CAAc,QAAA,UAAkB,IAAa;;;;;;ADvC7D;;;;AAA2D;AAG3D;;;;AAAgF;AAGhF;AAAA,cEJa,oBAAA;;cAGA,kBAAA;AAAA,UAEI,iBAAA;EFEA;EEAf,MAAA;;EAEA,MAAA;EFDA;EEGA,SAAA;EFDA;EEGA,GAAA;AAAA;AFCF;AAAA,UEGiB,WAAA;EACf,QAAA;EACA,OAAA;EACA,SAAA;AAAA;;;;AFHyE;iBEU3D,iBAAA,CAAkB,MAAe;;iBASjC,kBAAA,IAAsB,iBAAiB;;iBAUvC,oBAAA,CAAqB,IAA8C,GAAxC,iBAAwC;;;;AFnB9D;AAerB;iBEagB,qBAAA,CAAsB,IAA8C,GAAxC,iBAAwC;;;AFbJ;;;;iBEwBhE,gBAAA,CAAiB,IAA8C,GAAxC,iBAAwC;;;;ADrEtE;iBC6Fa,aAAA,CACpB,IAAA,WACA,YAAA,UACA,IAAA,GAAM,iBAAA,GACL,OAAO;;;;;AFhGV;;;UGDiB,UAAA;EACf,QAAA;EHGW;EGDX,OAAA;;EAEA,UAAA;AAAA;AAAA,UAGe,iBAAA,SAA0B,UAAU;EACnD,MAAA;;;AHFkE;AAGpE;;EGKE,QAAA,EAAU,QAAA;AAAA;AAAA,UAGK,WAAA;EHNf;EGQA,QAAA;EHPO;EGSP,UAAU;AAAA;;;;AHLwC;AAGpD;;iBGkCsB,aAAA,CACpB,SAAA,YACA,IAAA,GAAM,WAAA,GACL,OAAA,CAAQ,UAAA;;iBAOW,WAAA,CACpB,WAAA,UACA,IAAA,GAAM,WAAA,GACL,OAAA,CAAQ,UAAA;AHvCX;;;;;;;;AAEqB;AAFrB,iBGyDsB,WAAA,CACpB,MAAA,sBACA,IAAA,GAAM,WAAA;EAAgB,GAAA;AAAA,IACrB,OAAA,CAAQ,iBAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { S as buildStrikesTree, _ as ADDRESSES_LEAF_ENCODING, a as LOCAL_IPFS_API_URL, b as buildAddressesTree, c as ipfsOptionsFromEnv, d as shouldAttemptPin, f as parseAddresses, g as writeJsonFile, h as readStrikesFile, i as DEFAULT_IPFS_API_URL, l as pinJsonToIpfs, m as readJsonFile, n as makeRewards, o as hasCustomIpfsEndpoint, p as readAddressFile, r as makeStrikes, s as hasPinataCredentials, t as makeAddresses, u as resolveIpfsApiUrl, v as REWARDS_LEAF_ENCODING, x as buildRewardsTree, y as STRIKES_LEAF_ENCODING } from "./pipelines-Dxpjkr0j.mjs";
2
+ export { ADDRESSES_LEAF_ENCODING, DEFAULT_IPFS_API_URL, LOCAL_IPFS_API_URL, REWARDS_LEAF_ENCODING, STRIKES_LEAF_ENCODING, buildAddressesTree, buildRewardsTree, buildStrikesTree, hasCustomIpfsEndpoint, hasPinataCredentials, ipfsOptionsFromEnv, makeAddresses, makeRewards, makeStrikes, parseAddresses, pinJsonToIpfs, readAddressFile, readJsonFile, readStrikesFile, resolveIpfsApiUrl, shouldAttemptPin, writeJsonFile };
@@ -0,0 +1,238 @@
1
+ import { StandardMerkleTree } from "@openzeppelin/merkle-tree";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ //#region src/tree.ts
5
+ /**
6
+ * Pure, deterministic tree construction. No I/O, no network — given the same inputs these
7
+ * functions always produce the same root, which is exactly what the Vitest suite pins.
8
+ *
9
+ * Three OZ StandardMerkleTree shapes, one per CSM pipeline:
10
+ * - addresses (vetted gate): ["address"] → VettedGate.setTreeParams
11
+ * - strikes: ["uint256","string","uint256[]"] → CSStrikes.processOracleReport
12
+ * - rewards: ["uint256","uint256"] → FeeDistributor cumulative tree
13
+ */
14
+ /** Leaf encoding for the addresses (vetted gate) tree: one address per leaf. */
15
+ const ADDRESSES_LEAF_ENCODING = ["address"];
16
+ /** Leaf encoding for the strikes tree: (nodeOperatorId, pubkey, strikes[]). */
17
+ const STRIKES_LEAF_ENCODING = [
18
+ "uint256",
19
+ "string",
20
+ "uint256[]"
21
+ ];
22
+ /** Leaf encoding for the rewards tree: (nodeOperatorId, cumulativeShares). */
23
+ const REWARDS_LEAF_ENCODING = ["uint256", "uint256"];
24
+ /** Build the addresses (vetted gate) Merkle tree from a list of whitelisted addresses. */
25
+ function buildAddressesTree(addresses) {
26
+ return StandardMerkleTree.of(addresses.map((address) => [address]), [...ADDRESSES_LEAF_ENCODING]);
27
+ }
28
+ /** Build the strikes Merkle tree from node-operator strike records. */
29
+ function buildStrikesTree(entries) {
30
+ return StandardMerkleTree.of(entries.map(({ nodeOperatorId, pubkey, strikes }) => [
31
+ nodeOperatorId,
32
+ pubkey,
33
+ strikes
34
+ ]), [...STRIKES_LEAF_ENCODING]);
35
+ }
36
+ /**
37
+ * Build the cumulative rewards tree: one [nodeOperatorId, cumulativeShares] leaf per operator.
38
+ *
39
+ * Leaf values are `bigint` (not `number` like buildStrikesTree) because cumulative reward shares
40
+ * are wei amounts that routinely overflow `Number.MAX_SAFE_INTEGER`. OZ serializes them as decimal
41
+ * strings in `dump()` (JSON-safe). Callers shape the leaves — e.g. the FeeDistributor non-empty-proof
42
+ * pad leaf is appended by the caller, not here (same division of labor as the addresses/strikes builders).
43
+ */
44
+ function buildRewardsTree(leaves) {
45
+ return StandardMerkleTree.of(leaves, [...REWARDS_LEAF_ENCODING]);
46
+ }
47
+ //#endregion
48
+ //#region src/io.ts
49
+ /** Read+parse a JSON file as `T`. Throws (with the path) if missing. */
50
+ function readJsonFile(filePath) {
51
+ if (!fs.existsSync(filePath)) throw new Error(`File not found at ${filePath}`);
52
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
53
+ }
54
+ /**
55
+ * Parse an address list. Accepts either a JSON array (`["0x..", ...]`) or a newline-delimited
56
+ * text file (blank lines and `#` comments ignored) — the original tool supported both shapes.
57
+ */
58
+ function parseAddresses(content) {
59
+ const trimmed = content.trim();
60
+ if (trimmed.startsWith("[")) return JSON.parse(trimmed);
61
+ return trimmed.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
62
+ }
63
+ /** Read and parse an address file (JSON array or newline-delimited text). */
64
+ function readAddressFile(filePath) {
65
+ if (!fs.existsSync(filePath)) throw new Error(`File not found at ${filePath}`);
66
+ return parseAddresses(fs.readFileSync(filePath, "utf-8"));
67
+ }
68
+ /** Read and parse a strikes.json file. */
69
+ function readStrikesFile(filePath) {
70
+ return readJsonFile(filePath);
71
+ }
72
+ /** Write a JSON file, creating parent directories as needed. */
73
+ function writeJsonFile(filePath, data) {
74
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
75
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
76
+ }
77
+ //#endregion
78
+ //#region src/ipfs.ts
79
+ /**
80
+ * IPFS pinning client — Pinata-compatible, with an env-switchable endpoint.
81
+ *
82
+ * Why not `@pinata/sdk` directly? The installed v2 SDK hardcodes
83
+ * `baseUrl = 'https://api.pinata.cloud'` (see its `src/constants.ts`); its `PinataConfig`
84
+ * exposes only API keys / JWT, no host override. To target `@sm-lab/ipfs` locally we
85
+ * need a configurable base URL, so this is a thin `fetch` client hitting the exact same
86
+ * `/pinning/pinJSONToIPFS` route the mock implements. Point it at real Pinata in test-infra
87
+ * by supplying PINATA_* credentials (no need to set IPFS_API_URL when using Pinata).
88
+ *
89
+ * Default resolution (local-first):
90
+ * explicit apiUrl → IPFS_API_URL env → Pinata (if PINATA_* set) → local @sm-lab/ipfs
91
+ */
92
+ /** Real Pinata host. Used when PINATA_* credentials are set but IPFS_API_URL is unset. */
93
+ const DEFAULT_IPFS_API_URL = "https://api.pinata.cloud";
94
+ /** Local @sm-lab/ipfs default — the fallback when no IPFS_API_URL or Pinata creds are set. */
95
+ const LOCAL_IPFS_API_URL = "http://127.0.0.1:5001";
96
+ /**
97
+ * Resolve the pinning base URL: explicit apiUrl → IPFS_API_URL env → Pinata (if PINATA_* set)
98
+ * → local @sm-lab/ipfs (http://127.0.0.1:5001).
99
+ */
100
+ function resolveIpfsApiUrl(apiUrl) {
101
+ const explicit = apiUrl || process.env.IPFS_API_URL;
102
+ if (explicit) return explicit.replace(/\/+$/, "");
103
+ if (hasPinataCredentials()) return DEFAULT_IPFS_API_URL;
104
+ return LOCAL_IPFS_API_URL;
105
+ }
106
+ /** Read pinning config from the environment (credentials + endpoint switch). */
107
+ function ipfsOptionsFromEnv() {
108
+ return {
109
+ apiUrl: process.env.IPFS_API_URL,
110
+ apiKey: process.env.PINATA_API_KEY,
111
+ apiSecret: process.env.PINATA_API_SECRET,
112
+ jwt: process.env.PINATA_JWT
113
+ };
114
+ }
115
+ /** True when enough credentials are present to talk to real Pinata. */
116
+ function hasPinataCredentials(opts = ipfsOptionsFromEnv()) {
117
+ return Boolean(opts.jwt || opts.apiKey && opts.apiSecret);
118
+ }
119
+ /**
120
+ * True when a non-default pinning endpoint is configured — i.e. `IPFS_API_URL` points
121
+ * somewhere other than real Pinata (typically a local `@sm-lab/ipfs`). Such endpoints
122
+ * accept unauthenticated pins, so credentials are not required to upload to them.
123
+ */
124
+ function hasCustomIpfsEndpoint(opts = ipfsOptionsFromEnv()) {
125
+ const raw = (opts.apiUrl ?? "").replace(/\/+$/, "");
126
+ return raw.length > 0 && raw !== "https://api.pinata.cloud";
127
+ }
128
+ /**
129
+ * Whether `make`/`tree` should attempt to pin. With the local-first default there is always a
130
+ * usable target, so this returns `true` unless an explicit `IPFS_API_URL` points directly at
131
+ * real Pinata without credentials (that edge case cannot pin, so we still return `false` there).
132
+ * Use the CLI's `--no-upload` / `MakeOptions.noUpload` to explicitly skip pinning.
133
+ */
134
+ function shouldAttemptPin(opts = ipfsOptionsFromEnv()) {
135
+ if ((opts.apiUrl ?? process.env.IPFS_API_URL ?? "").replace(/\/+$/, "") === "https://api.pinata.cloud" && !hasPinataCredentials(opts)) return false;
136
+ return true;
137
+ }
138
+ function buildAuthHeaders(opts) {
139
+ if (opts.jwt) return { Authorization: `Bearer ${opts.jwt}` };
140
+ if (opts.apiKey && opts.apiSecret) return {
141
+ pinata_api_key: opts.apiKey,
142
+ pinata_secret_api_key: opts.apiSecret
143
+ };
144
+ return {};
145
+ }
146
+ /**
147
+ * Pin a JSON object and return its CID. POSTs the Pinata `pinJSONToIPFS` envelope
148
+ * (`{ pinataContent, pinataMetadata }`) so both real Pinata and `@sm-lab/ipfs` accept it.
149
+ */
150
+ async function pinJsonToIpfs(data, metadataName, opts = ipfsOptionsFromEnv()) {
151
+ const url = `${resolveIpfsApiUrl(opts.apiUrl)}/pinning/pinJSONToIPFS`;
152
+ const body = JSON.stringify({
153
+ pinataContent: data,
154
+ pinataMetadata: { name: metadataName }
155
+ });
156
+ const res = await fetch(url, {
157
+ method: "POST",
158
+ headers: {
159
+ "Content-Type": "application/json",
160
+ ...buildAuthHeaders(opts)
161
+ },
162
+ body
163
+ });
164
+ if (!res.ok) {
165
+ const detail = await res.text().catch(() => "");
166
+ throw new Error(`pinJSONToIPFS failed: ${res.status} ${res.statusText}${detail ? ` — ${detail}` : ""}`);
167
+ }
168
+ const json = await res.json();
169
+ if (!json.IpfsHash) throw new Error(`pinJSONToIPFS: response missing IpfsHash (${JSON.stringify(json)})`);
170
+ return json.IpfsHash;
171
+ }
172
+ //#endregion
173
+ //#region src/pipelines.ts
174
+ async function maybePin(data, name, noUpload) {
175
+ if (noUpload) return void 0;
176
+ if (!shouldAttemptPin()) {
177
+ console.warn("Warning: IPFS upload skipped — set IPFS_API_URL pointing at real Pinata and supply PINATA_API_KEY/SECRET or PINATA_JWT. Unset IPFS_API_URL pins to local @sm-lab/ipfs (http://127.0.0.1:5001).");
178
+ return;
179
+ }
180
+ return pinJsonToIpfs(data, name);
181
+ }
182
+ function finish(treeRoot, treeCid, configPath) {
183
+ if (configPath) writeJsonFile(configPath, {
184
+ treeRoot,
185
+ treeCid: treeCid ?? ""
186
+ });
187
+ return {
188
+ treeRoot,
189
+ treeCid,
190
+ configPath
191
+ };
192
+ }
193
+ /**
194
+ * Addresses (vetted gate): build the address tree from a resolved address list, pin it, return
195
+ * `{ treeRoot, treeCid }`. The CLI resolves file/inline/flag inputs to `string[]` before calling
196
+ * this — keeping this function pure so it can be called directly from TS consumers without
197
+ * touching the filesystem.
198
+ */
199
+ async function makeAddresses(addresses, opts = {}) {
200
+ const tree = buildAddressesTree(addresses);
201
+ const treeCid = await maybePin(tree.dump(), "merkle-tree-addresses", opts.noUpload);
202
+ return finish(tree.root, treeCid, opts.configPath);
203
+ }
204
+ /** Strikes: build the strikes tree, pin it, return `{ treeRoot, treeCid }`. */
205
+ async function makeStrikes(strikesPath, opts = {}) {
206
+ const tree = buildStrikesTree(readStrikesFile(strikesPath));
207
+ const treeCid = await maybePin(tree.dump(), "merkle-tree-strikes", opts.noUpload);
208
+ return finish(tree.root, treeCid, opts.configPath);
209
+ }
210
+ const bigintReplacer = (_k, v) => typeof v === "bigint" ? v.toString() : v;
211
+ /**
212
+ * Rewards: build the cumulative rewards tree from `[nodeOperatorId, cumulativeShares]` leaves
213
+ * (bigint values), optionally pin the tree dump and a log object, and return
214
+ * `{ treeRoot, treeCid, logCid?, treeDump }`.
215
+ *
216
+ * Bigints in the tree dump and `opts.log` are serialized to decimal strings before pinning
217
+ * (OZ `dump()` returns leaf values verbatim, which are bigints here). The returned `treeDump`
218
+ * uses the same serialization so it is JSON-safe (string values, no BigInt).
219
+ */
220
+ async function makeRewards(leaves, opts = {}) {
221
+ const tree = buildRewardsTree(leaves);
222
+ const treeDump = JSON.parse(JSON.stringify(tree.dump(), bigintReplacer));
223
+ const treeCid = await maybePin(treeDump, "merkle-tree-rewards", opts.noUpload);
224
+ const logCid = opts.log !== void 0 && !opts.noUpload ? await maybePin(JSON.parse(JSON.stringify(opts.log, bigintReplacer)), "merkle-rewards-log") : void 0;
225
+ const base = finish(tree.root, treeCid, opts.configPath);
226
+ return logCid !== void 0 ? {
227
+ ...base,
228
+ logCid,
229
+ treeDump
230
+ } : {
231
+ ...base,
232
+ treeDump
233
+ };
234
+ }
235
+ //#endregion
236
+ export { buildStrikesTree as S, ADDRESSES_LEAF_ENCODING as _, LOCAL_IPFS_API_URL as a, buildAddressesTree as b, ipfsOptionsFromEnv as c, shouldAttemptPin as d, parseAddresses as f, writeJsonFile as g, readStrikesFile as h, DEFAULT_IPFS_API_URL as i, pinJsonToIpfs as l, readJsonFile as m, makeRewards as n, hasCustomIpfsEndpoint as o, readAddressFile as p, makeStrikes as r, hasPinataCredentials as s, makeAddresses as t, resolveIpfsApiUrl as u, REWARDS_LEAF_ENCODING as v, buildRewardsTree as x, STRIKES_LEAF_ENCODING as y };
237
+
238
+ //# sourceMappingURL=pipelines-Dxpjkr0j.mjs.map