/** * NDK (Nostr Development Kit) - Browser Bundle * Version: 3.0.0-beta.64 * * IMPORTANT: This bundle requires nostr.bundle.js to be loaded first! * * Usage: * * * * Then access via: window.NDK * * Generated: 2026-03-06T18:29:23.331Z */ var NDK = (() => { var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __require = /* @__PURE__ */ ((x2) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x2, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x2)(function(x2) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x2 + '" is not supported'); }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __commonJS = (cb, mod3) => function __require2() { return mod3 || (0, cb[__getOwnPropNames(cb)[0]])((mod3 = { exports: {} }).exports, mod3), mod3.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __reExport = (target, mod3, secondTarget) => (__copyProps(target, mod3, "default"), secondTarget && __copyProps(secondTarget, mod3, "default")); var __toESM = (mod3, isNodeMode, target) => (target = mod3 != null ? __create(__getProtoOf(mod3)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod3 || !mod3.__esModule ? __defProp(target, "default", { value: mod3, enumerable: true }) : target, mod3 )); var __toCommonJS = (mod3) => __copyProps(__defProp({}, "__esModule", { value: true }), mod3); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); // ndk/node_modules/tseep/lib/types.js var require_types = __commonJS({ "ndk/node_modules/tseep/lib/types.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); } }); // ndk/node_modules/tseep/lib/task-collection/utils.js var require_utils = __commonJS({ "ndk/node_modules/tseep/lib/task-collection/utils.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); exports2._fast_remove_single = void 0; function _fast_remove_single(arr, index) { if (index === -1) return; if (index === 0) arr.shift(); else if (index === arr.length - 1) arr.length = arr.length - 1; else arr.splice(index, 1); } exports2._fast_remove_single = _fast_remove_single; } }); // ndk/node_modules/tseep/lib/task-collection/bake-collection.js var require_bake_collection = __commonJS({ "ndk/node_modules/tseep/lib/task-collection/bake-collection.js"(exports, module) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.bakeCollectionVariadic = exports.bakeCollectionAwait = exports.bakeCollection = exports.BAKED_EMPTY_FUNC = void 0; exports.BAKED_EMPTY_FUNC = (function() { }); var FORLOOP_FALLBACK = 1500; function generateArgsDefCode(numArgs) { var argsDefCode2 = ""; if (numArgs === 0) return argsDefCode2; for (var i3 = 0; i3 < numArgs - 1; ++i3) { argsDefCode2 += "arg" + String(i3) + ", "; } argsDefCode2 += "arg" + String(numArgs - 1); return argsDefCode2; } function generateBodyPartsCode(argsDefCode2, collectionLength) { var funcDefCode2 = "", funcCallCode2 = ""; for (var i3 = 0; i3 < collectionLength; ++i3) { funcDefCode2 += "var f".concat(i3, " = collection[").concat(i3, "];\n"); funcCallCode2 += "f".concat(i3, "(").concat(argsDefCode2, ")\n"); } return { funcDefCode: funcDefCode2, funcCallCode: funcCallCode2 }; } function generateBodyPartsVariadicCode(collectionLength) { var funcDefCode2 = "", funcCallCode2 = ""; for (var i3 = 0; i3 < collectionLength; ++i3) { funcDefCode2 += "var f".concat(i3, " = collection[").concat(i3, "];\n"); funcCallCode2 += "f".concat(i3, ".apply(undefined, arguments)\n"); } return { funcDefCode: funcDefCode2, funcCallCode: funcCallCode2 }; } function bakeCollection(collection, fixedArgsNum) { if (collection.length === 0) return exports.BAKED_EMPTY_FUNC; else if (collection.length === 1) return collection[0]; var funcFactoryCode; if (collection.length < FORLOOP_FALLBACK) { var argsDefCode = generateArgsDefCode(fixedArgsNum); var _a = generateBodyPartsCode(argsDefCode, collection.length), funcDefCode = _a.funcDefCode, funcCallCode = _a.funcCallCode; funcFactoryCode = "(function(collection) {\n ".concat(funcDefCode, "\n collection = undefined;\n return (function(").concat(argsDefCode, ") {\n ").concat(funcCallCode, "\n });\n })"); } else { var argsDefCode = generateArgsDefCode(fixedArgsNum); if (collection.length % 10 === 0) { funcFactoryCode = "(function(collection) {\n return (function(".concat(argsDefCode, ") {\n for (var i = 0; i < collection.length; i += 10) {\n collection[i](").concat(argsDefCode, ");\n collection[i+1](").concat(argsDefCode, ");\n collection[i+2](").concat(argsDefCode, ");\n collection[i+3](").concat(argsDefCode, ");\n collection[i+4](").concat(argsDefCode, ");\n collection[i+5](").concat(argsDefCode, ");\n collection[i+6](").concat(argsDefCode, ");\n collection[i+7](").concat(argsDefCode, ");\n collection[i+8](").concat(argsDefCode, ");\n collection[i+9](").concat(argsDefCode, ");\n }\n });\n })"); } else if (collection.length % 4 === 0) { funcFactoryCode = "(function(collection) {\n return (function(".concat(argsDefCode, ") {\n for (var i = 0; i < collection.length; i += 4) {\n collection[i](").concat(argsDefCode, ");\n collection[i+1](").concat(argsDefCode, ");\n collection[i+2](").concat(argsDefCode, ");\n collection[i+3](").concat(argsDefCode, ");\n }\n });\n })"); } else if (collection.length % 3 === 0) { funcFactoryCode = "(function(collection) {\n return (function(".concat(argsDefCode, ") {\n for (var i = 0; i < collection.length; i += 3) {\n collection[i](").concat(argsDefCode, ");\n collection[i+1](").concat(argsDefCode, ");\n collection[i+2](").concat(argsDefCode, ");\n }\n });\n })"); } else { funcFactoryCode = "(function(collection) {\n return (function(".concat(argsDefCode, ") {\n for (var i = 0; i < collection.length; ++i) {\n collection[i](").concat(argsDefCode, ");\n }\n });\n })"); } } { var bakeCollection_1 = void 0; var fixedArgsNum_1 = void 0; var bakeCollectionVariadic_1 = void 0; var bakeCollectionAwait_1 = void 0; var funcFactory = eval(funcFactoryCode); return funcFactory(collection); } } exports.bakeCollection = bakeCollection; function bakeCollectionAwait(collection, fixedArgsNum) { if (collection.length === 0) return exports.BAKED_EMPTY_FUNC; else if (collection.length === 1) return collection[0]; var funcFactoryCode; if (collection.length < FORLOOP_FALLBACK) { var argsDefCode = generateArgsDefCode(fixedArgsNum); var _a = generateBodyPartsCode(argsDefCode, collection.length), funcDefCode = _a.funcDefCode, funcCallCode = _a.funcCallCode; funcFactoryCode = "(function(collection) {\n ".concat(funcDefCode, "\n collection = undefined;\n return (function(").concat(argsDefCode, ") {\n return Promise.all([ ").concat(funcCallCode, " ]);\n });\n })"); } else { var argsDefCode = generateArgsDefCode(fixedArgsNum); funcFactoryCode = "(function(collection) {\n return (function(".concat(argsDefCode, ") {\n var promises = Array(collection.length);\n for (var i = 0; i < collection.length; ++i) {\n promises[i] = collection[i](").concat(argsDefCode, ");\n }\n return Promise.all(promises);\n });\n })"); } { var bakeCollection_2 = void 0; var fixedArgsNum_2 = void 0; var bakeCollectionVariadic_2 = void 0; var bakeCollectionAwait_2 = void 0; var funcFactory = eval(funcFactoryCode); return funcFactory(collection); } } exports.bakeCollectionAwait = bakeCollectionAwait; function bakeCollectionVariadic(collection) { if (collection.length === 0) return exports.BAKED_EMPTY_FUNC; else if (collection.length === 1) return collection[0]; var funcFactoryCode; if (collection.length < FORLOOP_FALLBACK) { var _a = generateBodyPartsVariadicCode(collection.length), funcDefCode = _a.funcDefCode, funcCallCode = _a.funcCallCode; funcFactoryCode = "(function(collection) {\n ".concat(funcDefCode, "\n collection = undefined;\n return (function() {\n ").concat(funcCallCode, "\n });\n })"); } else { funcFactoryCode = "(function(collection) {\n return (function() {\n for (var i = 0; i < collection.length; ++i) {\n collection[i].apply(undefined, arguments);\n }\n });\n })"; } { var bakeCollection_3 = void 0; var fixedArgsNum = void 0; var bakeCollectionVariadic_3 = void 0; var bakeCollectionAwait_3 = void 0; var funcFactory = eval(funcFactoryCode); return funcFactory(collection); } } exports.bakeCollectionVariadic = bakeCollectionVariadic; } }); // ndk/node_modules/tseep/lib/task-collection/task-collection.js var require_task_collection = __commonJS({ "ndk/node_modules/tseep/lib/task-collection/task-collection.js"(exports2) { "use strict"; var __spreadArray = exports2 && exports2.__spreadArray || function(to, from, pack) { if (pack || arguments.length === 2) for (var i3 = 0, l3 = from.length, ar; i3 < l3; i3++) { if (ar || !(i3 in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i3); ar[i3] = from[i3]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.TaskCollection = void 0; var utils_1 = require_utils(); var bake_collection_1 = require_bake_collection(); function push_norebuild(a, b) { var len = this.length; if (len > 1) { if (b) { var _a72; (_a72 = this._tasks).push.apply(_a72, arguments); this.length += arguments.length; } else { this._tasks.push(a); this.length++; } } else { if (b) { if (len === 1) { var newAr = Array(1 + arguments.length); newAr.push(newAr); newAr.push.apply(newAr, arguments); this._tasks = newAr; } else { var newAr = Array(arguments.length); newAr.push.apply(newAr, arguments); this._tasks = newAr; } this.length += arguments.length; } else { if (len === 1) this._tasks = [this._tasks, a]; else this._tasks = a; this.length++; } } } function push_rebuild(a, b) { var len = this.length; if (len > 1) { if (b) { var _a72; (_a72 = this._tasks).push.apply(_a72, arguments); this.length += arguments.length; } else { this._tasks.push(a); this.length++; } } else { if (b) { if (len === 1) { var newAr = Array(1 + arguments.length); newAr.push(newAr); newAr.push.apply(newAr, arguments); this._tasks = newAr; } else { var newAr = Array(arguments.length); newAr.push.apply(newAr, arguments); this._tasks = newAr; } this.length += arguments.length; } else { if (len === 1) this._tasks = [this._tasks, a]; else this._tasks = a; this.length++; } } if (this.firstEmitBuildStrategy) this.call = rebuild_on_first_call; else this.rebuild(); } function removeLast_norebuild(a) { if (this.length === 0) return; if (this.length === 1) { if (this._tasks === a) { this.length = 0; } } else { (0, utils_1._fast_remove_single)(this._tasks, this._tasks.lastIndexOf(a)); if (this._tasks.length === 1) { this._tasks = this._tasks[0]; this.length = 1; } else this.length = this._tasks.length; } } function removeLast_rebuild(a) { if (this.length === 0) return; if (this.length === 1) { if (this._tasks === a) { this.length = 0; } if (this.firstEmitBuildStrategy) { this.call = bake_collection_1.BAKED_EMPTY_FUNC; return; } else { this.rebuild(); return; } } else { (0, utils_1._fast_remove_single)(this._tasks, this._tasks.lastIndexOf(a)); if (this._tasks.length === 1) { this._tasks = this._tasks[0]; this.length = 1; } else this.length = this._tasks.length; } if (this.firstEmitBuildStrategy) this.call = rebuild_on_first_call; else this.rebuild(); } function insert_norebuild(index) { var _b; var func = []; for (var _i = 1; _i < arguments.length; _i++) { func[_i - 1] = arguments[_i]; } if (this.length === 0) { this._tasks = func; this.length = 1; } else if (this.length === 1) { func.unshift(this._tasks); this._tasks = func; this.length = this._tasks.length; } else { (_b = this._tasks).splice.apply(_b, __spreadArray([index, 0], func, false)); this.length = this._tasks.length; } } function insert_rebuild(index) { var _b; var func = []; for (var _i = 1; _i < arguments.length; _i++) { func[_i - 1] = arguments[_i]; } if (this.length === 0) { this._tasks = func; this.length = 1; } else if (this.length === 1) { func.unshift(this._tasks); this._tasks = func; this.length = this._tasks.length; } else { (_b = this._tasks).splice.apply(_b, __spreadArray([index, 0], func, false)); this.length = this._tasks.length; } if (this.firstEmitBuildStrategy) this.call = rebuild_on_first_call; else this.rebuild(); } function rebuild_noawait() { if (this.length === 0) this.call = bake_collection_1.BAKED_EMPTY_FUNC; else if (this.length === 1) this.call = this._tasks; else this.call = (0, bake_collection_1.bakeCollection)(this._tasks, this.argsNum); } function rebuild_await() { if (this.length === 0) this.call = bake_collection_1.BAKED_EMPTY_FUNC; else if (this.length === 1) this.call = this._tasks; else this.call = (0, bake_collection_1.bakeCollectionAwait)(this._tasks, this.argsNum); } function rebuild_on_first_call() { this.rebuild(); this.call.apply(void 0, arguments); } var TaskCollection = ( /** @class */ /* @__PURE__ */ (function() { function TaskCollection2(argsNum, autoRebuild, initialTasks, awaitTasks) { if (autoRebuild === void 0) { autoRebuild = true; } if (initialTasks === void 0) { initialTasks = null; } if (awaitTasks === void 0) { awaitTasks = false; } this.awaitTasks = awaitTasks; this.call = bake_collection_1.BAKED_EMPTY_FUNC; this.argsNum = argsNum; this.firstEmitBuildStrategy = true; if (awaitTasks) this.rebuild = rebuild_await.bind(this); else this.rebuild = rebuild_noawait.bind(this); this.setAutoRebuild(autoRebuild); if (initialTasks) { if (typeof initialTasks === "function") { this._tasks = initialTasks; this.length = 1; } else { this._tasks = initialTasks; this.length = initialTasks.length; } } else { this._tasks = null; this.length = 0; } if (autoRebuild) this.rebuild(); } return TaskCollection2; })() ); exports2.TaskCollection = TaskCollection; function fastClear() { this._tasks = null; this.length = 0; this.call = bake_collection_1.BAKED_EMPTY_FUNC; } function clear() { this._tasks = null; this.length = 0; this.call = bake_collection_1.BAKED_EMPTY_FUNC; } function growArgsNum(argsNum) { if (this.argsNum < argsNum) { this.argsNum = argsNum; if (this.firstEmitBuildStrategy) this.call = rebuild_on_first_call; else this.rebuild(); } } function setAutoRebuild(newVal) { if (newVal) { this.push = push_rebuild.bind(this); this.insert = insert_rebuild.bind(this); this.removeLast = removeLast_rebuild.bind(this); } else { this.push = push_norebuild.bind(this); this.insert = insert_norebuild.bind(this); this.removeLast = removeLast_norebuild.bind(this); } } function tasksAsArray() { if (this.length === 0) return []; if (this.length === 1) return [this._tasks]; return this._tasks; } function setTasks(tasks) { if (tasks.length === 0) { this.length = 0; this.call = bake_collection_1.BAKED_EMPTY_FUNC; } else if (tasks.length === 1) { this.length = 1; this.call = tasks[0]; this._tasks = tasks[0]; } else { this.length = tasks.length; this._tasks = tasks; if (this.firstEmitBuildStrategy) this.call = rebuild_on_first_call; else this.rebuild(); } } TaskCollection.prototype.fastClear = fastClear; TaskCollection.prototype.clear = clear; TaskCollection.prototype.growArgsNum = growArgsNum; TaskCollection.prototype.setAutoRebuild = setAutoRebuild; TaskCollection.prototype.tasksAsArray = tasksAsArray; TaskCollection.prototype.setTasks = setTasks; } }); // ndk/node_modules/tseep/lib/task-collection/index.js var require_task_collection2 = __commonJS({ "ndk/node_modules/tseep/lib/task-collection/index.js"(exports2) { "use strict"; var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; var desc = Object.getOwnPropertyDescriptor(m, k2); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k2]; } }; } Object.defineProperty(o, k22, desc); }) : (function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; o[k22] = m[k2]; })); var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { for (var p5 in m) if (p5 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p5)) __createBinding(exports3, m, p5); }; Object.defineProperty(exports2, "__esModule", { value: true }); __exportStar(require_task_collection(), exports2); } }); // ndk/node_modules/tseep/lib/utils.js var require_utils2 = __commonJS({ "ndk/node_modules/tseep/lib/utils.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.nullObj = void 0; function nullObj() { var x2 = {}; x2.__proto__ = null; return x2; } exports2.nullObj = nullObj; } }); // ndk/node_modules/tseep/lib/ee.js var require_ee = __commonJS({ "ndk/node_modules/tseep/lib/ee.js"(exports2) { "use strict"; var __spreadArray = exports2 && exports2.__spreadArray || function(to, from, pack) { if (pack || arguments.length === 2) for (var i3 = 0, l3 = from.length, ar; i3 < l3; i3++) { if (ar || !(i3 in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i3); ar[i3] = from[i3]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.EventEmitter = void 0; var task_collection_1 = require_task_collection2(); var utils_1 = require_utils(); var utils_2 = require_utils2(); function emit(event, a, b, c, d17, e2) { var ev = this.events[event]; if (ev) { if (ev.length === 0) return false; if (ev.argsNum < 6) { ev.call(a, b, c, d17, e2); } else { var arr = new Array(ev.argsNum); for (var i3 = 0, len = arr.length; i3 < len; ++i3) { arr[i3] = arguments[i3 + 1]; } ev.call.apply(void 0, arr); } return true; } return false; } function emitHasOnce(event, a, b, c, d17, e2) { var ev = this.events[event]; var argsArr; if (ev !== void 0) { if (ev.length === 0) return false; if (ev.argsNum < 6) { ev.call(a, b, c, d17, e2); } else { argsArr = new Array(ev.argsNum); for (var i3 = 0, len = argsArr.length; i3 < len; ++i3) { argsArr[i3] = arguments[i3 + 1]; } ev.call.apply(void 0, argsArr); } } var oev = this.onceEvents[event]; if (oev) { if (typeof oev === "function") { this.onceEvents[event] = void 0; if (arguments.length < 6) { oev(a, b, c, d17, e2); } else { if (argsArr === void 0) { argsArr = new Array(arguments.length - 1); for (var i3 = 0, len = argsArr.length; i3 < len; ++i3) { argsArr[i3] = arguments[i3 + 1]; } } oev.apply(void 0, argsArr); } } else { var fncs = oev; this.onceEvents[event] = void 0; if (arguments.length < 6) { for (var i3 = 0; i3 < fncs.length; ++i3) { fncs[i3](a, b, c, d17, e2); } } else { if (argsArr === void 0) { argsArr = new Array(arguments.length - 1); for (var i3 = 0, len = argsArr.length; i3 < len; ++i3) { argsArr[i3] = arguments[i3 + 1]; } } for (var i3 = 0; i3 < fncs.length; ++i3) { fncs[i3].apply(void 0, argsArr); } } } return true; } return ev !== void 0; } var EventEmitter18 = ( /** @class */ (function() { function EventEmitter19() { this.events = (0, utils_2.nullObj)(); this.onceEvents = (0, utils_2.nullObj)(); this._symbolKeys = /* @__PURE__ */ new Set(); this.maxListeners = Infinity; } Object.defineProperty(EventEmitter19.prototype, "_eventsCount", { get: function() { return this.eventNames().length; }, enumerable: false, configurable: true }); return EventEmitter19; })() ); exports2.EventEmitter = EventEmitter18; function once(event, listener) { if (this.emit === emit) { this.emit = emitHasOnce; } switch (typeof this.onceEvents[event]) { case "undefined": this.onceEvents[event] = listener; if (typeof event === "symbol") this._symbolKeys.add(event); break; case "function": this.onceEvents[event] = [this.onceEvents[event], listener]; break; case "object": this.onceEvents[event].push(listener); } return this; } function addListener(event, listener, argsNum) { if (argsNum === void 0) { argsNum = listener.length; } if (typeof listener !== "function") throw new TypeError("The listener must be a function"); var evtmap = this.events[event]; if (!evtmap) { this.events[event] = new task_collection_1.TaskCollection(argsNum, true, listener, false); if (typeof event === "symbol") this._symbolKeys.add(event); } else { evtmap.push(listener); evtmap.growArgsNum(argsNum); if (this.maxListeners !== Infinity && this.maxListeners <= evtmap.length) console.warn('Maximum event listeners for "'.concat(String(event), '" event!')); } return this; } function removeListener(event, listener) { var evt = this.events[event]; if (evt) { evt.removeLast(listener); } var evto = this.onceEvents[event]; if (evto) { if (typeof evto === "function") { this.onceEvents[event] = void 0; } else if (typeof evto === "object") { if (evto.length === 1 && evto[0] === listener) { this.onceEvents[event] = void 0; } else { (0, utils_1._fast_remove_single)(evto, evto.lastIndexOf(listener)); } } } return this; } function addListenerBound(event, listener, bindTo, argsNum) { if (bindTo === void 0) { bindTo = this; } if (argsNum === void 0) { argsNum = listener.length; } if (!this.boundFuncs) this.boundFuncs = /* @__PURE__ */ new Map(); var bound = listener.bind(bindTo); this.boundFuncs.set(listener, bound); return this.addListener(event, bound, argsNum); } function removeListenerBound(event, listener) { var _a72, _b; var bound = (_a72 = this.boundFuncs) === null || _a72 === void 0 ? void 0 : _a72.get(listener); (_b = this.boundFuncs) === null || _b === void 0 ? void 0 : _b.delete(listener); return this.removeListener(event, bound); } function hasListeners(event) { return this.events[event] && !!this.events[event].length; } function prependListener(event, listener, argsNum) { if (argsNum === void 0) { argsNum = listener.length; } if (typeof listener !== "function") throw new TypeError("The listener must be a function"); var evtmap = this.events[event]; if (!evtmap || !(evtmap instanceof task_collection_1.TaskCollection)) { evtmap = this.events[event] = new task_collection_1.TaskCollection(argsNum, true, listener, false); if (typeof event === "symbol") this._symbolKeys.add(event); } else { evtmap.insert(0, listener); evtmap.growArgsNum(argsNum); if (this.maxListeners !== Infinity && this.maxListeners <= evtmap.length) console.warn('Maximum event listeners for "'.concat(String(event), '" event!')); } return this; } function prependOnceListener(event, listener) { if (this.emit === emit) { this.emit = emitHasOnce; } var evtmap = this.onceEvents[event]; if (!evtmap) { this.onceEvents[event] = [listener]; if (typeof event === "symbol") this._symbolKeys.add(event); } else if (typeof evtmap !== "object") { this.onceEvents[event] = [listener, evtmap]; if (typeof event === "symbol") this._symbolKeys.add(event); } else { evtmap.unshift(listener); if (this.maxListeners !== Infinity && this.maxListeners <= evtmap.length) { console.warn('Maximum event listeners for "'.concat(String(event), '" once event!')); } } return this; } function removeAllListeners(event) { if (event === void 0) { this.events = (0, utils_2.nullObj)(); this.onceEvents = (0, utils_2.nullObj)(); this._symbolKeys = /* @__PURE__ */ new Set(); } else { this.events[event] = void 0; this.onceEvents[event] = void 0; if (typeof event === "symbol") this._symbolKeys.delete(event); } return this; } function setMaxListeners(n) { this.maxListeners = n; return this; } function getMaxListeners() { return this.maxListeners; } function listeners(event) { if (this.emit === emit) return this.events[event] ? this.events[event].tasksAsArray().slice() : []; else { if (this.events[event] && this.onceEvents[event]) { return __spreadArray(__spreadArray([], this.events[event].tasksAsArray(), true), typeof this.onceEvents[event] === "function" ? [this.onceEvents[event]] : this.onceEvents[event], true); } else if (this.events[event]) return this.events[event].tasksAsArray(); else if (this.onceEvents[event]) return typeof this.onceEvents[event] === "function" ? [this.onceEvents[event]] : this.onceEvents[event]; else return []; } } function eventNames() { var _this = this; if (this.emit === emit) { var keys = Object.keys(this.events); return __spreadArray(__spreadArray([], keys, true), Array.from(this._symbolKeys), true).filter(function(x2) { return x2 in _this.events && _this.events[x2] && _this.events[x2].length; }); } else { var keys = Object.keys(this.events).filter(function(x2) { return _this.events[x2] && _this.events[x2].length; }); var keysO = Object.keys(this.onceEvents).filter(function(x2) { return _this.onceEvents[x2] && _this.onceEvents[x2].length; }); return __spreadArray(__spreadArray(__spreadArray([], keys, true), keysO, true), Array.from(this._symbolKeys).filter(function(x2) { return x2 in _this.events && _this.events[x2] && _this.events[x2].length || x2 in _this.onceEvents && _this.onceEvents[x2] && _this.onceEvents[x2].length; }), true); } } function listenerCount(type) { if (this.emit === emit) return this.events[type] && this.events[type].length || 0; else return (this.events[type] && this.events[type].length || 0) + (this.onceEvents[type] && this.onceEvents[type].length || 0); } EventEmitter18.prototype.emit = emit; EventEmitter18.prototype.on = addListener; EventEmitter18.prototype.once = once; EventEmitter18.prototype.addListener = addListener; EventEmitter18.prototype.removeListener = removeListener; EventEmitter18.prototype.addListenerBound = addListenerBound; EventEmitter18.prototype.removeListenerBound = removeListenerBound; EventEmitter18.prototype.hasListeners = hasListeners; EventEmitter18.prototype.prependListener = prependListener; EventEmitter18.prototype.prependOnceListener = prependOnceListener; EventEmitter18.prototype.off = removeListener; EventEmitter18.prototype.removeAllListeners = removeAllListeners; EventEmitter18.prototype.setMaxListeners = setMaxListeners; EventEmitter18.prototype.getMaxListeners = getMaxListeners; EventEmitter18.prototype.listeners = listeners; EventEmitter18.prototype.eventNames = eventNames; EventEmitter18.prototype.listenerCount = listenerCount; } }); // ndk/node_modules/tseep/lib/index.js var require_lib = __commonJS({ "ndk/node_modules/tseep/lib/index.js"(exports2) { "use strict"; var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; var desc = Object.getOwnPropertyDescriptor(m, k2); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k2]; } }; } Object.defineProperty(o, k22, desc); }) : (function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; o[k22] = m[k2]; })); var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { for (var p5 in m) if (p5 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p5)) __createBinding(exports3, m, p5); }; Object.defineProperty(exports2, "__esModule", { value: true }); __exportStar(require_types(), exports2); __exportStar(require_ee(), exports2); } }); // ndk/node_modules/ms/index.js var require_ms = __commonJS({ "ndk/node_modules/ms/index.js"(exports2, module2) { var s = 1e3; var m = s * 60; var h2 = m * 60; var d17 = h2 * 24; var w2 = d17 * 7; var y2 = d17 * 365.25; module2.exports = function(val, options) { options = options || {}; var type = typeof val; if (type === "string" && val.length > 0) { return parse4(val); } else if (type === "number" && isFinite(val)) { return options.long ? fmtLong(val) : fmtShort(val); } throw new Error( "val is not a non-empty string or a valid number. val=" + JSON.stringify(val) ); }; function parse4(str) { str = String(str); if (str.length > 100) { return; } var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec( str ); if (!match) { return; } var n = parseFloat(match[1]); var type = (match[2] || "ms").toLowerCase(); switch (type) { case "years": case "year": case "yrs": case "yr": case "y": return n * y2; case "weeks": case "week": case "w": return n * w2; case "days": case "day": case "d": return n * d17; case "hours": case "hour": case "hrs": case "hr": case "h": return n * h2; case "minutes": case "minute": case "mins": case "min": case "m": return n * m; case "seconds": case "second": case "secs": case "sec": case "s": return n * s; case "milliseconds": case "millisecond": case "msecs": case "msec": case "ms": return n; default: return void 0; } } function fmtShort(ms) { var msAbs = Math.abs(ms); if (msAbs >= d17) { return Math.round(ms / d17) + "d"; } if (msAbs >= h2) { return Math.round(ms / h2) + "h"; } if (msAbs >= m) { return Math.round(ms / m) + "m"; } if (msAbs >= s) { return Math.round(ms / s) + "s"; } return ms + "ms"; } function fmtLong(ms) { var msAbs = Math.abs(ms); if (msAbs >= d17) { return plural(ms, msAbs, d17, "day"); } if (msAbs >= h2) { return plural(ms, msAbs, h2, "hour"); } if (msAbs >= m) { return plural(ms, msAbs, m, "minute"); } if (msAbs >= s) { return plural(ms, msAbs, s, "second"); } return ms + " ms"; } function plural(ms, msAbs, n, name) { var isPlural = msAbs >= n * 1.5; return Math.round(ms / n) + " " + name + (isPlural ? "s" : ""); } } }); // ndk/node_modules/debug/src/common.js var require_common = __commonJS({ "ndk/node_modules/debug/src/common.js"(exports2, module2) { function setup(env) { createDebug19.debug = createDebug19; createDebug19.default = createDebug19; createDebug19.coerce = coerce; createDebug19.disable = disable; createDebug19.enable = enable; createDebug19.enabled = enabled; createDebug19.humanize = require_ms(); createDebug19.destroy = destroy; Object.keys(env).forEach((key) => { createDebug19[key] = env[key]; }); createDebug19.names = []; createDebug19.skips = []; createDebug19.formatters = {}; function selectColor(namespace) { let hash3 = 0; for (let i3 = 0; i3 < namespace.length; i3++) { hash3 = (hash3 << 5) - hash3 + namespace.charCodeAt(i3); hash3 |= 0; } return createDebug19.colors[Math.abs(hash3) % createDebug19.colors.length]; } createDebug19.selectColor = selectColor; function createDebug19(namespace) { let prevTime; let enableOverride = null; let namespacesCache; let enabledCache; function debug15(...args) { if (!debug15.enabled) { return; } const self2 = debug15; const curr = Number(/* @__PURE__ */ new Date()); const ms = curr - (prevTime || curr); self2.diff = ms; self2.prev = prevTime; self2.curr = curr; prevTime = curr; args[0] = createDebug19.coerce(args[0]); if (typeof args[0] !== "string") { args.unshift("%O"); } let index = 0; args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { if (match === "%%") { return "%"; } index++; const formatter = createDebug19.formatters[format]; if (typeof formatter === "function") { const val = args[index]; match = formatter.call(self2, val); args.splice(index, 1); index--; } return match; }); createDebug19.formatArgs.call(self2, args); const logFn = self2.log || createDebug19.log; logFn.apply(self2, args); } debug15.namespace = namespace; debug15.useColors = createDebug19.useColors(); debug15.color = createDebug19.selectColor(namespace); debug15.extend = extend; debug15.destroy = createDebug19.destroy; Object.defineProperty(debug15, "enabled", { enumerable: true, configurable: false, get: () => { if (enableOverride !== null) { return enableOverride; } if (namespacesCache !== createDebug19.namespaces) { namespacesCache = createDebug19.namespaces; enabledCache = createDebug19.enabled(namespace); } return enabledCache; }, set: (v6) => { enableOverride = v6; } }); if (typeof createDebug19.init === "function") { createDebug19.init(debug15); } return debug15; } function extend(namespace, delimiter) { const newDebug = createDebug19(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace); newDebug.log = this.log; return newDebug; } function enable(namespaces) { createDebug19.save(namespaces); createDebug19.namespaces = namespaces; createDebug19.names = []; createDebug19.skips = []; const split2 = (typeof namespaces === "string" ? namespaces : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean); for (const ns of split2) { if (ns[0] === "-") { createDebug19.skips.push(ns.slice(1)); } else { createDebug19.names.push(ns); } } } function matchesTemplate(search, template) { let searchIndex = 0; let templateIndex = 0; let starIndex = -1; let matchIndex = 0; while (searchIndex < search.length) { if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === "*")) { if (template[templateIndex] === "*") { starIndex = templateIndex; matchIndex = searchIndex; templateIndex++; } else { searchIndex++; templateIndex++; } } else if (starIndex !== -1) { templateIndex = starIndex + 1; matchIndex++; searchIndex = matchIndex; } else { return false; } } while (templateIndex < template.length && template[templateIndex] === "*") { templateIndex++; } return templateIndex === template.length; } function disable() { const namespaces = [ ...createDebug19.names, ...createDebug19.skips.map((namespace) => "-" + namespace) ].join(","); createDebug19.enable(""); return namespaces; } function enabled(name) { for (const skip of createDebug19.skips) { if (matchesTemplate(name, skip)) { return false; } } for (const ns of createDebug19.names) { if (matchesTemplate(name, ns)) { return true; } } return false; } function coerce(val) { if (val instanceof Error) { return val.stack || val.message; } return val; } function destroy() { console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."); } createDebug19.enable(createDebug19.load()); return createDebug19; } module2.exports = setup; } }); // ndk/node_modules/debug/src/browser.js var require_browser = __commonJS({ "ndk/node_modules/debug/src/browser.js"(exports2, module2) { exports2.formatArgs = formatArgs; exports2.save = save; exports2.load = load; exports2.useColors = useColors; exports2.storage = localstorage(); exports2.destroy = /* @__PURE__ */ (() => { let warned = false; return () => { if (!warned) { warned = true; console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."); } }; })(); exports2.colors = [ "#0000CC", "#0000FF", "#0033CC", "#0033FF", "#0066CC", "#0066FF", "#0099CC", "#0099FF", "#00CC00", "#00CC33", "#00CC66", "#00CC99", "#00CCCC", "#00CCFF", "#3300CC", "#3300FF", "#3333CC", "#3333FF", "#3366CC", "#3366FF", "#3399CC", "#3399FF", "#33CC00", "#33CC33", "#33CC66", "#33CC99", "#33CCCC", "#33CCFF", "#6600CC", "#6600FF", "#6633CC", "#6633FF", "#66CC00", "#66CC33", "#9900CC", "#9900FF", "#9933CC", "#9933FF", "#99CC00", "#99CC33", "#CC0000", "#CC0033", "#CC0066", "#CC0099", "#CC00CC", "#CC00FF", "#CC3300", "#CC3333", "#CC3366", "#CC3399", "#CC33CC", "#CC33FF", "#CC6600", "#CC6633", "#CC9900", "#CC9933", "#CCCC00", "#CCCC33", "#FF0000", "#FF0033", "#FF0066", "#FF0099", "#FF00CC", "#FF00FF", "#FF3300", "#FF3333", "#FF3366", "#FF3399", "#FF33CC", "#FF33FF", "#FF6600", "#FF6633", "#FF9900", "#FF9933", "#FFCC00", "#FFCC33" ]; function useColors() { if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) { return true; } if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { return false; } let m; return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773 typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31? // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/); } function formatArgs(args) { args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module2.exports.humanize(this.diff); if (!this.useColors) { return; } const c = "color: " + this.color; args.splice(1, 0, c, "color: inherit"); let index = 0; let lastC = 0; args[0].replace(/%[a-zA-Z%]/g, (match) => { if (match === "%%") { return; } index++; if (match === "%c") { lastC = index; } }); args.splice(lastC, 0, c); } exports2.log = console.debug || console.log || (() => { }); function save(namespaces) { try { if (namespaces) { exports2.storage.setItem("debug", namespaces); } else { exports2.storage.removeItem("debug"); } } catch (error) { } } function load() { let r; try { r = exports2.storage.getItem("debug") || exports2.storage.getItem("DEBUG"); } catch (error) { } if (!r && typeof process !== "undefined" && "env" in process) { r = process.env.DEBUG; } return r; } function localstorage() { try { return localStorage; } catch (error) { } } module2.exports = require_common()(exports2); var { formatters } = module2.exports; formatters.j = function(v6) { try { return JSON.stringify(v6); } catch (error) { return "[UnexpectedJSONParseError]: " + error.message; } }; } }); // nostr-tools-external:nostr-tools var require_nostr_tools = __commonJS({ "nostr-tools-external:nostr-tools"(exports2, module2) { if (typeof window === "undefined" || !window.NostrTools) { throw new Error("NDK: nostr.bundle.js must be loaded before ndk-core.bundle.js"); } module2.exports = window.NostrTools; } }); // ndk/node_modules/@noble/hashes/esm/crypto.js var crypto2; var init_crypto = __esm({ "ndk/node_modules/@noble/hashes/esm/crypto.js"() { crypto2 = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : void 0; } }); // ndk/node_modules/@noble/hashes/esm/utils.js function isBytes(a) { return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array"; } function anumber(n) { if (!Number.isSafeInteger(n) || n < 0) throw new Error("positive integer expected, got " + n); } function abytes(b, ...lengths) { if (!isBytes(b)) throw new Error("Uint8Array expected"); if (lengths.length > 0 && !lengths.includes(b.length)) throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length); } function ahash(h2) { if (typeof h2 !== "function" || typeof h2.create !== "function") throw new Error("Hash should be wrapped by utils.createHasher"); anumber(h2.outputLen); anumber(h2.blockLen); } function aexists(instance, checkFinished = true) { if (instance.destroyed) throw new Error("Hash instance has been destroyed"); if (checkFinished && instance.finished) throw new Error("Hash#digest() has already been called"); } function aoutput(out, instance) { abytes(out); const min = instance.outputLen; if (out.length < min) { throw new Error("digestInto() expects output buffer of length at least " + min); } } function clean(...arrays) { for (let i3 = 0; i3 < arrays.length; i3++) { arrays[i3].fill(0); } } function createView(arr) { return new DataView(arr.buffer, arr.byteOffset, arr.byteLength); } function rotr(word, shift) { return word << 32 - shift | word >>> shift; } function rotl(word, shift) { return word << shift | word >>> 32 - shift >>> 0; } function bytesToHex(bytes4) { abytes(bytes4); if (hasHexBuiltin) return bytes4.toHex(); let hex2 = ""; for (let i3 = 0; i3 < bytes4.length; i3++) { hex2 += hexes[bytes4[i3]]; } return hex2; } function asciiToBase16(ch) { if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); return; } function hexToBytes(hex2) { if (typeof hex2 !== "string") throw new Error("hex string expected, got " + typeof hex2); if (hasHexBuiltin) return Uint8Array.fromHex(hex2); const hl = hex2.length; const al = hl / 2; if (hl % 2) throw new Error("hex string expected, got unpadded hex of length " + hl); const array = new Uint8Array(al); for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { const n1 = asciiToBase16(hex2.charCodeAt(hi)); const n2 = asciiToBase16(hex2.charCodeAt(hi + 1)); if (n1 === void 0 || n2 === void 0) { const char = hex2[hi] + hex2[hi + 1]; throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi); } array[ai] = n1 * 16 + n2; } return array; } function utf8ToBytes(str) { if (typeof str !== "string") throw new Error("string expected"); return new Uint8Array(new TextEncoder().encode(str)); } function toBytes(data) { if (typeof data === "string") data = utf8ToBytes(data); abytes(data); return data; } function concatBytes(...arrays) { let sum = 0; for (let i3 = 0; i3 < arrays.length; i3++) { const a = arrays[i3]; abytes(a); sum += a.length; } const res = new Uint8Array(sum); for (let i3 = 0, pad2 = 0; i3 < arrays.length; i3++) { const a = arrays[i3]; res.set(a, pad2); pad2 += a.length; } return res; } function createHasher(hashCons) { const hashC = (msg) => hashCons().update(toBytes(msg)).digest(); const tmp = hashCons(); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.create = () => hashCons(); return hashC; } function randomBytes(bytesLength = 32) { if (crypto2 && typeof crypto2.getRandomValues === "function") { return crypto2.getRandomValues(new Uint8Array(bytesLength)); } if (crypto2 && typeof crypto2.randomBytes === "function") { return Uint8Array.from(crypto2.randomBytes(bytesLength)); } throw new Error("crypto.getRandomValues must be defined"); } var hasHexBuiltin, hexes, asciis, Hash; var init_utils = __esm({ "ndk/node_modules/@noble/hashes/esm/utils.js"() { init_crypto(); hasHexBuiltin = /* @__PURE__ */ (() => ( // @ts-ignore typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function" ))(); hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_2, i3) => i3.toString(16).padStart(2, "0")); asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; Hash = class { }; } }); // ndk/node_modules/@noble/hashes/esm/_md.js function setBigUint64(view, byteOffset, value, isLE4) { if (typeof view.setBigUint64 === "function") return view.setBigUint64(byteOffset, value, isLE4); const _32n2 = BigInt(32); const _u32_max = BigInt(4294967295); const wh = Number(value >> _32n2 & _u32_max); const wl = Number(value & _u32_max); const h2 = isLE4 ? 4 : 0; const l3 = isLE4 ? 0 : 4; view.setUint32(byteOffset + h2, wh, isLE4); view.setUint32(byteOffset + l3, wl, isLE4); } function Chi(a, b, c) { return a & b ^ ~a & c; } function Maj(a, b, c) { return a & b ^ a & c ^ b & c; } var HashMD, SHA256_IV, SHA512_IV; var init_md = __esm({ "ndk/node_modules/@noble/hashes/esm/_md.js"() { init_utils(); HashMD = class extends Hash { constructor(blockLen, outputLen, padOffset, isLE4) { super(); this.finished = false; this.length = 0; this.pos = 0; this.destroyed = false; this.blockLen = blockLen; this.outputLen = outputLen; this.padOffset = padOffset; this.isLE = isLE4; this.buffer = new Uint8Array(blockLen); this.view = createView(this.buffer); } update(data) { aexists(this); data = toBytes(data); abytes(data); const { view, buffer, blockLen } = this; const len = data.length; for (let pos = 0; pos < len; ) { const take = Math.min(blockLen - this.pos, len - pos); if (take === blockLen) { const dataView = createView(data); for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos); continue; } buffer.set(data.subarray(pos, pos + take), this.pos); this.pos += take; pos += take; if (this.pos === blockLen) { this.process(view, 0); this.pos = 0; } } this.length += data.length; this.roundClean(); return this; } digestInto(out) { aexists(this); aoutput(out, this); this.finished = true; const { buffer, view, blockLen, isLE: isLE4 } = this; let { pos } = this; buffer[pos++] = 128; clean(this.buffer.subarray(pos)); if (this.padOffset > blockLen - pos) { this.process(view, 0); pos = 0; } for (let i3 = pos; i3 < blockLen; i3++) buffer[i3] = 0; setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE4); this.process(view, 0); const oview = createView(out); const len = this.outputLen; if (len % 4) throw new Error("_sha2: outputLen should be aligned to 32bit"); const outLen = len / 4; const state = this.get(); if (outLen > state.length) throw new Error("_sha2: outputLen bigger than state"); for (let i3 = 0; i3 < outLen; i3++) oview.setUint32(4 * i3, state[i3], isLE4); } digest() { const { buffer, outputLen } = this; this.digestInto(buffer); const res = buffer.slice(0, outputLen); this.destroy(); return res; } _cloneInto(to) { to || (to = new this.constructor()); to.set(...this.get()); const { blockLen, buffer, length, finished, destroyed, pos } = this; to.destroyed = destroyed; to.finished = finished; to.length = length; to.pos = pos; if (length % blockLen) to.buffer.set(buffer); return to; } clone() { return this._cloneInto(); } }; SHA256_IV = /* @__PURE__ */ Uint32Array.from([ 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225 ]); SHA512_IV = /* @__PURE__ */ Uint32Array.from([ 1779033703, 4089235720, 3144134277, 2227873595, 1013904242, 4271175723, 2773480762, 1595750129, 1359893119, 2917565137, 2600822924, 725511199, 528734635, 4215389547, 1541459225, 327033209 ]); } }); // ndk/node_modules/@noble/hashes/esm/_u64.js function fromBig(n, le2 = false) { if (le2) return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) }; return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 }; } function split(lst, le2 = false) { const len = lst.length; let Ah = new Uint32Array(len); let Al = new Uint32Array(len); for (let i3 = 0; i3 < len; i3++) { const { h: h2, l: l3 } = fromBig(lst[i3], le2); [Ah[i3], Al[i3]] = [h2, l3]; } return [Ah, Al]; } function add(Ah, Al, Bh, Bl) { const l3 = (Al >>> 0) + (Bl >>> 0); return { h: Ah + Bh + (l3 / 2 ** 32 | 0) | 0, l: l3 | 0 }; } var U32_MASK64, _32n, shrSH, shrSL, rotrSH, rotrSL, rotrBH, rotrBL, add3L, add3H, add4L, add4H, add5L, add5H; var init_u64 = __esm({ "ndk/node_modules/@noble/hashes/esm/_u64.js"() { U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1); _32n = /* @__PURE__ */ BigInt(32); shrSH = (h2, _l, s) => h2 >>> s; shrSL = (h2, l3, s) => h2 << 32 - s | l3 >>> s; rotrSH = (h2, l3, s) => h2 >>> s | l3 << 32 - s; rotrSL = (h2, l3, s) => h2 << 32 - s | l3 >>> s; rotrBH = (h2, l3, s) => h2 << 64 - s | l3 >>> s - 32; rotrBL = (h2, l3, s) => h2 >>> s - 32 | l3 << 64 - s; add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0); add3H = (low, Ah, Bh, Ch) => Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0; add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0); add4H = (low, Ah, Bh, Ch, Dh) => Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0; add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0); add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0; } }); // ndk/node_modules/@noble/hashes/esm/sha2.js var SHA256_K, SHA256_W, SHA256, K512, SHA512_Kh, SHA512_Kl, SHA512_W_H, SHA512_W_L, SHA512, sha256, sha512; var init_sha2 = __esm({ "ndk/node_modules/@noble/hashes/esm/sha2.js"() { init_md(); init_u64(); init_utils(); SHA256_K = /* @__PURE__ */ Uint32Array.from([ 1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298 ]); SHA256_W = /* @__PURE__ */ new Uint32Array(64); SHA256 = class extends HashMD { constructor(outputLen = 32) { super(64, outputLen, 8, false); this.A = SHA256_IV[0] | 0; this.B = SHA256_IV[1] | 0; this.C = SHA256_IV[2] | 0; this.D = SHA256_IV[3] | 0; this.E = SHA256_IV[4] | 0; this.F = SHA256_IV[5] | 0; this.G = SHA256_IV[6] | 0; this.H = SHA256_IV[7] | 0; } get() { const { A, B, C: C2, D: D3, E: E2, F: F2, G: G2, H: H2 } = this; return [A, B, C2, D3, E2, F2, G2, H2]; } // prettier-ignore set(A, B, C2, D3, E2, F2, G2, H2) { this.A = A | 0; this.B = B | 0; this.C = C2 | 0; this.D = D3 | 0; this.E = E2 | 0; this.F = F2 | 0; this.G = G2 | 0; this.H = H2 | 0; } process(view, offset) { for (let i3 = 0; i3 < 16; i3++, offset += 4) SHA256_W[i3] = view.getUint32(offset, false); for (let i3 = 16; i3 < 64; i3++) { const W15 = SHA256_W[i3 - 15]; const W22 = SHA256_W[i3 - 2]; const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3; const s1 = rotr(W22, 17) ^ rotr(W22, 19) ^ W22 >>> 10; SHA256_W[i3] = s1 + SHA256_W[i3 - 7] + s0 + SHA256_W[i3 - 16] | 0; } let { A, B, C: C2, D: D3, E: E2, F: F2, G: G2, H: H2 } = this; for (let i3 = 0; i3 < 64; i3++) { const sigma1 = rotr(E2, 6) ^ rotr(E2, 11) ^ rotr(E2, 25); const T1 = H2 + sigma1 + Chi(E2, F2, G2) + SHA256_K[i3] + SHA256_W[i3] | 0; const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22); const T22 = sigma0 + Maj(A, B, C2) | 0; H2 = G2; G2 = F2; F2 = E2; E2 = D3 + T1 | 0; D3 = C2; C2 = B; B = A; A = T1 + T22 | 0; } A = A + this.A | 0; B = B + this.B | 0; C2 = C2 + this.C | 0; D3 = D3 + this.D | 0; E2 = E2 + this.E | 0; F2 = F2 + this.F | 0; G2 = G2 + this.G | 0; H2 = H2 + this.H | 0; this.set(A, B, C2, D3, E2, F2, G2, H2); } roundClean() { clean(SHA256_W); } destroy() { this.set(0, 0, 0, 0, 0, 0, 0, 0); clean(this.buffer); } }; K512 = /* @__PURE__ */ (() => split([ "0x428a2f98d728ae22", "0x7137449123ef65cd", "0xb5c0fbcfec4d3b2f", "0xe9b5dba58189dbbc", "0x3956c25bf348b538", "0x59f111f1b605d019", "0x923f82a4af194f9b", "0xab1c5ed5da6d8118", "0xd807aa98a3030242", "0x12835b0145706fbe", "0x243185be4ee4b28c", "0x550c7dc3d5ffb4e2", "0x72be5d74f27b896f", "0x80deb1fe3b1696b1", "0x9bdc06a725c71235", "0xc19bf174cf692694", "0xe49b69c19ef14ad2", "0xefbe4786384f25e3", "0x0fc19dc68b8cd5b5", "0x240ca1cc77ac9c65", "0x2de92c6f592b0275", "0x4a7484aa6ea6e483", "0x5cb0a9dcbd41fbd4", "0x76f988da831153b5", "0x983e5152ee66dfab", "0xa831c66d2db43210", "0xb00327c898fb213f", "0xbf597fc7beef0ee4", "0xc6e00bf33da88fc2", "0xd5a79147930aa725", "0x06ca6351e003826f", "0x142929670a0e6e70", "0x27b70a8546d22ffc", "0x2e1b21385c26c926", "0x4d2c6dfc5ac42aed", "0x53380d139d95b3df", "0x650a73548baf63de", "0x766a0abb3c77b2a8", "0x81c2c92e47edaee6", "0x92722c851482353b", "0xa2bfe8a14cf10364", "0xa81a664bbc423001", "0xc24b8b70d0f89791", "0xc76c51a30654be30", "0xd192e819d6ef5218", "0xd69906245565a910", "0xf40e35855771202a", "0x106aa07032bbd1b8", "0x19a4c116b8d2d0c8", "0x1e376c085141ab53", "0x2748774cdf8eeb99", "0x34b0bcb5e19b48a8", "0x391c0cb3c5c95a63", "0x4ed8aa4ae3418acb", "0x5b9cca4f7763e373", "0x682e6ff3d6b2b8a3", "0x748f82ee5defb2fc", "0x78a5636f43172f60", "0x84c87814a1f0ab72", "0x8cc702081a6439ec", "0x90befffa23631e28", "0xa4506cebde82bde9", "0xbef9a3f7b2c67915", "0xc67178f2e372532b", "0xca273eceea26619c", "0xd186b8c721c0c207", "0xeada7dd6cde0eb1e", "0xf57d4f7fee6ed178", "0x06f067aa72176fba", "0x0a637dc5a2c898a6", "0x113f9804bef90dae", "0x1b710b35131c471b", "0x28db77f523047d84", "0x32caab7b40c72493", "0x3c9ebe0a15c9bebc", "0x431d67c49c100d4c", "0x4cc5d4becb3e42b6", "0x597f299cfc657e2a", "0x5fcb6fab3ad6faec", "0x6c44198c4a475817" ].map((n) => BigInt(n))))(); SHA512_Kh = /* @__PURE__ */ (() => K512[0])(); SHA512_Kl = /* @__PURE__ */ (() => K512[1])(); SHA512_W_H = /* @__PURE__ */ new Uint32Array(80); SHA512_W_L = /* @__PURE__ */ new Uint32Array(80); SHA512 = class extends HashMD { constructor(outputLen = 64) { super(128, outputLen, 16, false); this.Ah = SHA512_IV[0] | 0; this.Al = SHA512_IV[1] | 0; this.Bh = SHA512_IV[2] | 0; this.Bl = SHA512_IV[3] | 0; this.Ch = SHA512_IV[4] | 0; this.Cl = SHA512_IV[5] | 0; this.Dh = SHA512_IV[6] | 0; this.Dl = SHA512_IV[7] | 0; this.Eh = SHA512_IV[8] | 0; this.El = SHA512_IV[9] | 0; this.Fh = SHA512_IV[10] | 0; this.Fl = SHA512_IV[11] | 0; this.Gh = SHA512_IV[12] | 0; this.Gl = SHA512_IV[13] | 0; this.Hh = SHA512_IV[14] | 0; this.Hl = SHA512_IV[15] | 0; } // prettier-ignore get() { const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl]; } // prettier-ignore set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) { this.Ah = Ah | 0; this.Al = Al | 0; this.Bh = Bh | 0; this.Bl = Bl | 0; this.Ch = Ch | 0; this.Cl = Cl | 0; this.Dh = Dh | 0; this.Dl = Dl | 0; this.Eh = Eh | 0; this.El = El | 0; this.Fh = Fh | 0; this.Fl = Fl | 0; this.Gh = Gh | 0; this.Gl = Gl | 0; this.Hh = Hh | 0; this.Hl = Hl | 0; } process(view, offset) { for (let i3 = 0; i3 < 16; i3++, offset += 4) { SHA512_W_H[i3] = view.getUint32(offset); SHA512_W_L[i3] = view.getUint32(offset += 4); } for (let i3 = 16; i3 < 80; i3++) { const W15h = SHA512_W_H[i3 - 15] | 0; const W15l = SHA512_W_L[i3 - 15] | 0; const s0h = rotrSH(W15h, W15l, 1) ^ rotrSH(W15h, W15l, 8) ^ shrSH(W15h, W15l, 7); const s0l = rotrSL(W15h, W15l, 1) ^ rotrSL(W15h, W15l, 8) ^ shrSL(W15h, W15l, 7); const W2h = SHA512_W_H[i3 - 2] | 0; const W2l = SHA512_W_L[i3 - 2] | 0; const s1h = rotrSH(W2h, W2l, 19) ^ rotrBH(W2h, W2l, 61) ^ shrSH(W2h, W2l, 6); const s1l = rotrSL(W2h, W2l, 19) ^ rotrBL(W2h, W2l, 61) ^ shrSL(W2h, W2l, 6); const SUMl = add4L(s0l, s1l, SHA512_W_L[i3 - 7], SHA512_W_L[i3 - 16]); const SUMh = add4H(SUMl, s0h, s1h, SHA512_W_H[i3 - 7], SHA512_W_H[i3 - 16]); SHA512_W_H[i3] = SUMh | 0; SHA512_W_L[i3] = SUMl | 0; } let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; for (let i3 = 0; i3 < 80; i3++) { const sigma1h = rotrSH(Eh, El, 14) ^ rotrSH(Eh, El, 18) ^ rotrBH(Eh, El, 41); const sigma1l = rotrSL(Eh, El, 14) ^ rotrSL(Eh, El, 18) ^ rotrBL(Eh, El, 41); const CHIh = Eh & Fh ^ ~Eh & Gh; const CHIl = El & Fl ^ ~El & Gl; const T1ll = add5L(Hl, sigma1l, CHIl, SHA512_Kl[i3], SHA512_W_L[i3]); const T1h = add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i3], SHA512_W_H[i3]); const T1l = T1ll | 0; const sigma0h = rotrSH(Ah, Al, 28) ^ rotrBH(Ah, Al, 34) ^ rotrBH(Ah, Al, 39); const sigma0l = rotrSL(Ah, Al, 28) ^ rotrBL(Ah, Al, 34) ^ rotrBL(Ah, Al, 39); const MAJh = Ah & Bh ^ Ah & Ch ^ Bh & Ch; const MAJl = Al & Bl ^ Al & Cl ^ Bl & Cl; Hh = Gh | 0; Hl = Gl | 0; Gh = Fh | 0; Gl = Fl | 0; Fh = Eh | 0; Fl = El | 0; ({ h: Eh, l: El } = add(Dh | 0, Dl | 0, T1h | 0, T1l | 0)); Dh = Ch | 0; Dl = Cl | 0; Ch = Bh | 0; Cl = Bl | 0; Bh = Ah | 0; Bl = Al | 0; const All = add3L(T1l, sigma0l, MAJl); Ah = add3H(All, T1h, sigma0h, MAJh); Al = All | 0; } ({ h: Ah, l: Al } = add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0)); ({ h: Bh, l: Bl } = add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0)); ({ h: Ch, l: Cl } = add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0)); ({ h: Dh, l: Dl } = add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0)); ({ h: Eh, l: El } = add(this.Eh | 0, this.El | 0, Eh | 0, El | 0)); ({ h: Fh, l: Fl } = add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0)); ({ h: Gh, l: Gl } = add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0)); ({ h: Hh, l: Hl } = add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0)); this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl); } roundClean() { clean(SHA512_W_H, SHA512_W_L); } destroy() { clean(this.buffer); this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } }; sha256 = /* @__PURE__ */ createHasher(() => new SHA256()); sha512 = /* @__PURE__ */ createHasher(() => new SHA512()); } }); // ndk/node_modules/@noble/hashes/esm/hmac.js var HMAC, hmac; var init_hmac = __esm({ "ndk/node_modules/@noble/hashes/esm/hmac.js"() { init_utils(); HMAC = class extends Hash { constructor(hash3, _key) { super(); this.finished = false; this.destroyed = false; ahash(hash3); const key = toBytes(_key); this.iHash = hash3.create(); if (typeof this.iHash.update !== "function") throw new Error("Expected instance of class which extends utils.Hash"); this.blockLen = this.iHash.blockLen; this.outputLen = this.iHash.outputLen; const blockLen = this.blockLen; const pad2 = new Uint8Array(blockLen); pad2.set(key.length > blockLen ? hash3.create().update(key).digest() : key); for (let i3 = 0; i3 < pad2.length; i3++) pad2[i3] ^= 54; this.iHash.update(pad2); this.oHash = hash3.create(); for (let i3 = 0; i3 < pad2.length; i3++) pad2[i3] ^= 54 ^ 92; this.oHash.update(pad2); clean(pad2); } update(buf) { aexists(this); this.iHash.update(buf); return this; } digestInto(out) { aexists(this); abytes(out, this.outputLen); this.finished = true; this.iHash.digestInto(out); this.oHash.update(out); this.oHash.digestInto(out); this.destroy(); } digest() { const out = new Uint8Array(this.oHash.outputLen); this.digestInto(out); return out; } _cloneInto(to) { to || (to = Object.create(Object.getPrototypeOf(this), {})); const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; to = to; to.finished = finished; to.destroyed = destroyed; to.blockLen = blockLen; to.outputLen = outputLen; to.oHash = oHash._cloneInto(to.oHash); to.iHash = iHash._cloneInto(to.iHash); return to; } clone() { return this._cloneInto(); } destroy() { this.destroyed = true; this.oHash.destroy(); this.iHash.destroy(); } }; hmac = (hash3, key, message) => new HMAC(hash3, key).update(message).digest(); hmac.create = (hash3, key) => new HMAC(hash3, key); } }); // ndk/node_modules/@noble/curves/esm/utils.js function _abool2(value, title = "") { if (typeof value !== "boolean") { const prefix = title && `"${title}"`; throw new Error(prefix + "expected boolean, got type=" + typeof value); } return value; } function _abytes2(value, length, title = "") { const bytes4 = isBytes(value); const len = value?.length; const needsLen = length !== void 0; if (!bytes4 || needsLen && len !== length) { const prefix = title && `"${title}" `; const ofLen = needsLen ? ` of length ${length}` : ""; const got = bytes4 ? `length=${len}` : `type=${typeof value}`; throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got); } return value; } function numberToHexUnpadded(num3) { const hex2 = num3.toString(16); return hex2.length & 1 ? "0" + hex2 : hex2; } function hexToNumber(hex2) { if (typeof hex2 !== "string") throw new Error("hex string expected, got " + typeof hex2); return hex2 === "" ? _0n : BigInt("0x" + hex2); } function bytesToNumberBE(bytes4) { return hexToNumber(bytesToHex(bytes4)); } function bytesToNumberLE(bytes4) { abytes(bytes4); return hexToNumber(bytesToHex(Uint8Array.from(bytes4).reverse())); } function numberToBytesBE(n, len) { return hexToBytes(n.toString(16).padStart(len * 2, "0")); } function numberToBytesLE(n, len) { return numberToBytesBE(n, len).reverse(); } function ensureBytes(title, hex2, expectedLength) { let res; if (typeof hex2 === "string") { try { res = hexToBytes(hex2); } catch (e2) { throw new Error(title + " must be hex string or Uint8Array, cause: " + e2); } } else if (isBytes(hex2)) { res = Uint8Array.from(hex2); } else { throw new Error(title + " must be hex string or Uint8Array"); } const len = res.length; if (typeof expectedLength === "number" && len !== expectedLength) throw new Error(title + " of length " + expectedLength + " expected, got " + len); return res; } function inRange(n, min, max) { return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max; } function aInRange(title, n, min, max) { if (!inRange(n, min, max)) throw new Error("expected valid " + title + ": " + min + " <= n < " + max + ", got " + n); } function bitLen(n) { let len; for (len = 0; n > _0n; n >>= _1n, len += 1) ; return len; } function createHmacDrbg(hashLen, qByteLen, hmacFn) { if (typeof hashLen !== "number" || hashLen < 2) throw new Error("hashLen must be a number"); if (typeof qByteLen !== "number" || qByteLen < 2) throw new Error("qByteLen must be a number"); if (typeof hmacFn !== "function") throw new Error("hmacFn must be a function"); const u8n2 = (len) => new Uint8Array(len); const u8of = (byte) => Uint8Array.of(byte); let v6 = u8n2(hashLen); let k2 = u8n2(hashLen); let i3 = 0; const reset = () => { v6.fill(1); k2.fill(0); i3 = 0; }; const h2 = (...b) => hmacFn(k2, v6, ...b); const reseed = (seed = u8n2(0)) => { k2 = h2(u8of(0), seed); v6 = h2(); if (seed.length === 0) return; k2 = h2(u8of(1), seed); v6 = h2(); }; const gen = () => { if (i3++ >= 1e3) throw new Error("drbg: tried 1000 values"); let len = 0; const out = []; while (len < qByteLen) { v6 = h2(); const sl = v6.slice(); out.push(sl); len += v6.length; } return concatBytes(...out); }; const genUntil = (seed, pred) => { reset(); reseed(seed); let res = void 0; while (!(res = pred(gen()))) reseed(); reset(); return res; }; return genUntil; } function _validateObject(object, fields, optFields = {}) { if (!object || typeof object !== "object") throw new Error("expected valid options object"); function checkField(fieldName, expectedType, isOpt) { const val = object[fieldName]; if (isOpt && val === void 0) return; const current = typeof val; if (current !== expectedType || val === null) throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`); } Object.entries(fields).forEach(([k2, v6]) => checkField(k2, v6, false)); Object.entries(optFields).forEach(([k2, v6]) => checkField(k2, v6, true)); } function memoized(fn) { const map = /* @__PURE__ */ new WeakMap(); return (arg, ...args) => { const val = map.get(arg); if (val !== void 0) return val; const computed = fn(arg, ...args); map.set(arg, computed); return computed; }; } var _0n, _1n, isPosBig, bitMask; var init_utils2 = __esm({ "ndk/node_modules/@noble/curves/esm/utils.js"() { init_utils(); init_utils(); _0n = /* @__PURE__ */ BigInt(0); _1n = /* @__PURE__ */ BigInt(1); isPosBig = (n) => typeof n === "bigint" && _0n <= n; bitMask = (n) => (_1n << BigInt(n)) - _1n; } }); // ndk/node_modules/@noble/curves/esm/abstract/modular.js function mod(a, b) { const result = a % b; return result >= _0n2 ? result : b + result; } function pow2(x2, power, modulo) { let res = x2; while (power-- > _0n2) { res *= res; res %= modulo; } return res; } function invert(number4, modulo) { if (number4 === _0n2) throw new Error("invert: expected non-zero number"); if (modulo <= _0n2) throw new Error("invert: expected positive modulus, got " + modulo); let a = mod(number4, modulo); let b = modulo; let x2 = _0n2, y2 = _1n2, u3 = _1n2, v6 = _0n2; while (a !== _0n2) { const q2 = b / a; const r = b % a; const m = x2 - u3 * q2; const n = y2 - v6 * q2; b = a, a = r, x2 = u3, y2 = v6, u3 = m, v6 = n; } const gcd3 = b; if (gcd3 !== _1n2) throw new Error("invert: does not exist"); return mod(x2, modulo); } function assertIsSquare(Fp2, root, n) { if (!Fp2.eql(Fp2.sqr(root), n)) throw new Error("Cannot find square root"); } function sqrt3mod4(Fp2, n) { const p1div4 = (Fp2.ORDER + _1n2) / _4n; const root = Fp2.pow(n, p1div4); assertIsSquare(Fp2, root, n); return root; } function sqrt5mod8(Fp2, n) { const p5div8 = (Fp2.ORDER - _5n) / _8n; const n2 = Fp2.mul(n, _2n); const v6 = Fp2.pow(n2, p5div8); const nv = Fp2.mul(n, v6); const i3 = Fp2.mul(Fp2.mul(nv, _2n), v6); const root = Fp2.mul(nv, Fp2.sub(i3, Fp2.ONE)); assertIsSquare(Fp2, root, n); return root; } function sqrt9mod16(P) { const Fp_ = Field(P); const tn = tonelliShanks(P); const c1 = tn(Fp_, Fp_.neg(Fp_.ONE)); const c2 = tn(Fp_, c1); const c3 = tn(Fp_, Fp_.neg(c1)); const c4 = (P + _7n) / _16n; return (Fp2, n) => { let tv1 = Fp2.pow(n, c4); let tv2 = Fp2.mul(tv1, c1); const tv3 = Fp2.mul(tv1, c2); const tv4 = Fp2.mul(tv1, c3); const e1 = Fp2.eql(Fp2.sqr(tv2), n); const e2 = Fp2.eql(Fp2.sqr(tv3), n); tv1 = Fp2.cmov(tv1, tv2, e1); tv2 = Fp2.cmov(tv4, tv3, e2); const e3 = Fp2.eql(Fp2.sqr(tv2), n); const root = Fp2.cmov(tv1, tv2, e3); assertIsSquare(Fp2, root, n); return root; }; } function tonelliShanks(P) { if (P < _3n) throw new Error("sqrt is not defined for small field"); let Q2 = P - _1n2; let S4 = 0; while (Q2 % _2n === _0n2) { Q2 /= _2n; S4++; } let Z = _2n; const _Fp = Field(P); while (FpLegendre(_Fp, Z) === 1) { if (Z++ > 1e3) throw new Error("Cannot find square root: probably non-prime P"); } if (S4 === 1) return sqrt3mod4; let cc = _Fp.pow(Z, Q2); const Q1div2 = (Q2 + _1n2) / _2n; return function tonelliSlow(Fp2, n) { if (Fp2.is0(n)) return n; if (FpLegendre(Fp2, n) !== 1) throw new Error("Cannot find square root"); let M2 = S4; let c = Fp2.mul(Fp2.ONE, cc); let t = Fp2.pow(n, Q2); let R2 = Fp2.pow(n, Q1div2); while (!Fp2.eql(t, Fp2.ONE)) { if (Fp2.is0(t)) return Fp2.ZERO; let i3 = 1; let t_tmp = Fp2.sqr(t); while (!Fp2.eql(t_tmp, Fp2.ONE)) { i3++; t_tmp = Fp2.sqr(t_tmp); if (i3 === M2) throw new Error("Cannot find square root"); } const exponent = _1n2 << BigInt(M2 - i3 - 1); const b = Fp2.pow(c, exponent); M2 = i3; c = Fp2.sqr(b); t = Fp2.mul(t, c); R2 = Fp2.mul(R2, b); } return R2; }; } function FpSqrt(P) { if (P % _4n === _3n) return sqrt3mod4; if (P % _8n === _5n) return sqrt5mod8; if (P % _16n === _9n) return sqrt9mod16(P); return tonelliShanks(P); } function validateField(field) { const initial = { ORDER: "bigint", MASK: "bigint", BYTES: "number", BITS: "number" }; const opts = FIELD_FIELDS.reduce((map, val) => { map[val] = "function"; return map; }, initial); _validateObject(field, opts); return field; } function FpPow(Fp2, num3, power) { if (power < _0n2) throw new Error("invalid exponent, negatives unsupported"); if (power === _0n2) return Fp2.ONE; if (power === _1n2) return num3; let p5 = Fp2.ONE; let d17 = num3; while (power > _0n2) { if (power & _1n2) p5 = Fp2.mul(p5, d17); d17 = Fp2.sqr(d17); power >>= _1n2; } return p5; } function FpInvertBatch(Fp2, nums, passZero = false) { const inverted = new Array(nums.length).fill(passZero ? Fp2.ZERO : void 0); const multipliedAcc = nums.reduce((acc, num3, i3) => { if (Fp2.is0(num3)) return acc; inverted[i3] = acc; return Fp2.mul(acc, num3); }, Fp2.ONE); const invertedAcc = Fp2.inv(multipliedAcc); nums.reduceRight((acc, num3, i3) => { if (Fp2.is0(num3)) return acc; inverted[i3] = Fp2.mul(acc, inverted[i3]); return Fp2.mul(acc, num3); }, invertedAcc); return inverted; } function FpLegendre(Fp2, n) { const p1mod2 = (Fp2.ORDER - _1n2) / _2n; const powered = Fp2.pow(n, p1mod2); const yes = Fp2.eql(powered, Fp2.ONE); const zero = Fp2.eql(powered, Fp2.ZERO); const no = Fp2.eql(powered, Fp2.neg(Fp2.ONE)); if (!yes && !zero && !no) throw new Error("invalid Legendre symbol result"); return yes ? 1 : zero ? 0 : -1; } function nLength(n, nBitLength) { if (nBitLength !== void 0) anumber(nBitLength); const _nBitLength = nBitLength !== void 0 ? nBitLength : n.toString(2).length; const nByteLength = Math.ceil(_nBitLength / 8); return { nBitLength: _nBitLength, nByteLength }; } function Field(ORDER, bitLenOrOpts, isLE4 = false, opts = {}) { if (ORDER <= _0n2) throw new Error("invalid field: expected ORDER > 0, got " + ORDER); let _nbitLength = void 0; let _sqrt = void 0; let modFromBytes = false; let allowedLengths = void 0; if (typeof bitLenOrOpts === "object" && bitLenOrOpts != null) { if (opts.sqrt || isLE4) throw new Error("cannot specify opts in two arguments"); const _opts = bitLenOrOpts; if (_opts.BITS) _nbitLength = _opts.BITS; if (_opts.sqrt) _sqrt = _opts.sqrt; if (typeof _opts.isLE === "boolean") isLE4 = _opts.isLE; if (typeof _opts.modFromBytes === "boolean") modFromBytes = _opts.modFromBytes; allowedLengths = _opts.allowedLengths; } else { if (typeof bitLenOrOpts === "number") _nbitLength = bitLenOrOpts; if (opts.sqrt) _sqrt = opts.sqrt; } const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, _nbitLength); if (BYTES > 2048) throw new Error("invalid field: expected ORDER of <= 2048 bytes"); let sqrtP; const f = Object.freeze({ ORDER, isLE: isLE4, BITS, BYTES, MASK: bitMask(BITS), ZERO: _0n2, ONE: _1n2, allowedLengths, create: (num3) => mod(num3, ORDER), isValid: (num3) => { if (typeof num3 !== "bigint") throw new Error("invalid field element: expected bigint, got " + typeof num3); return _0n2 <= num3 && num3 < ORDER; }, is0: (num3) => num3 === _0n2, // is valid and invertible isValidNot0: (num3) => !f.is0(num3) && f.isValid(num3), isOdd: (num3) => (num3 & _1n2) === _1n2, neg: (num3) => mod(-num3, ORDER), eql: (lhs, rhs) => lhs === rhs, sqr: (num3) => mod(num3 * num3, ORDER), add: (lhs, rhs) => mod(lhs + rhs, ORDER), sub: (lhs, rhs) => mod(lhs - rhs, ORDER), mul: (lhs, rhs) => mod(lhs * rhs, ORDER), pow: (num3, power) => FpPow(f, num3, power), div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER), // Same as above, but doesn't normalize sqrN: (num3) => num3 * num3, addN: (lhs, rhs) => lhs + rhs, subN: (lhs, rhs) => lhs - rhs, mulN: (lhs, rhs) => lhs * rhs, inv: (num3) => invert(num3, ORDER), sqrt: _sqrt || ((n) => { if (!sqrtP) sqrtP = FpSqrt(ORDER); return sqrtP(f, n); }), toBytes: (num3) => isLE4 ? numberToBytesLE(num3, BYTES) : numberToBytesBE(num3, BYTES), fromBytes: (bytes4, skipValidation = true) => { if (allowedLengths) { if (!allowedLengths.includes(bytes4.length) || bytes4.length > BYTES) { throw new Error("Field.fromBytes: expected " + allowedLengths + " bytes, got " + bytes4.length); } const padded = new Uint8Array(BYTES); padded.set(bytes4, isLE4 ? 0 : padded.length - bytes4.length); bytes4 = padded; } if (bytes4.length !== BYTES) throw new Error("Field.fromBytes: expected " + BYTES + " bytes, got " + bytes4.length); let scalar = isLE4 ? bytesToNumberLE(bytes4) : bytesToNumberBE(bytes4); if (modFromBytes) scalar = mod(scalar, ORDER); if (!skipValidation) { if (!f.isValid(scalar)) throw new Error("invalid field element: outside of range 0..ORDER"); } return scalar; }, // TODO: we don't need it here, move out to separate fn invertBatch: (lst) => FpInvertBatch(f, lst), // We can't move this out because Fp6, Fp12 implement it // and it's unclear what to return in there. cmov: (a, b, c) => c ? b : a }); return Object.freeze(f); } function getFieldBytesLength(fieldOrder) { if (typeof fieldOrder !== "bigint") throw new Error("field order must be bigint"); const bitLength = fieldOrder.toString(2).length; return Math.ceil(bitLength / 8); } function getMinHashLength(fieldOrder) { const length = getFieldBytesLength(fieldOrder); return length + Math.ceil(length / 2); } function mapHashToField(key, fieldOrder, isLE4 = false) { const len = key.length; const fieldLen = getFieldBytesLength(fieldOrder); const minLen = getMinHashLength(fieldOrder); if (len < 16 || len < minLen || len > 1024) throw new Error("expected " + minLen + "-1024 bytes of input, got " + len); const num3 = isLE4 ? bytesToNumberLE(key) : bytesToNumberBE(key); const reduced = mod(num3, fieldOrder - _1n2) + _1n2; return isLE4 ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen); } var _0n2, _1n2, _2n, _3n, _4n, _5n, _7n, _8n, _9n, _16n, FIELD_FIELDS; var init_modular = __esm({ "ndk/node_modules/@noble/curves/esm/abstract/modular.js"() { init_utils2(); _0n2 = BigInt(0); _1n2 = BigInt(1); _2n = /* @__PURE__ */ BigInt(2); _3n = /* @__PURE__ */ BigInt(3); _4n = /* @__PURE__ */ BigInt(4); _5n = /* @__PURE__ */ BigInt(5); _7n = /* @__PURE__ */ BigInt(7); _8n = /* @__PURE__ */ BigInt(8); _9n = /* @__PURE__ */ BigInt(9); _16n = /* @__PURE__ */ BigInt(16); FIELD_FIELDS = [ "create", "isValid", "is0", "neg", "inv", "sqrt", "sqr", "eql", "add", "sub", "mul", "pow", "div", "addN", "subN", "mulN", "sqrN" ]; } }); // ndk/node_modules/@noble/curves/esm/abstract/curve.js function negateCt(condition, item) { const neg = item.negate(); return condition ? neg : item; } function normalizeZ(c, points) { const invertedZs = FpInvertBatch(c.Fp, points.map((p5) => p5.Z)); return points.map((p5, i3) => c.fromAffine(p5.toAffine(invertedZs[i3]))); } function validateW(W3, bits) { if (!Number.isSafeInteger(W3) || W3 <= 0 || W3 > bits) throw new Error("invalid window size, expected [1.." + bits + "], got W=" + W3); } function calcWOpts(W3, scalarBits) { validateW(W3, scalarBits); const windows = Math.ceil(scalarBits / W3) + 1; const windowSize = 2 ** (W3 - 1); const maxNumber = 2 ** W3; const mask = bitMask(W3); const shiftBy = BigInt(W3); return { windows, windowSize, mask, maxNumber, shiftBy }; } function calcOffsets(n, window2, wOpts) { const { windowSize, mask, maxNumber, shiftBy } = wOpts; let wbits = Number(n & mask); let nextN = n >> shiftBy; if (wbits > windowSize) { wbits -= maxNumber; nextN += _1n3; } const offsetStart = window2 * windowSize; const offset = offsetStart + Math.abs(wbits) - 1; const isZero = wbits === 0; const isNeg = wbits < 0; const isNegF = window2 % 2 !== 0; const offsetF = offsetStart; return { nextN, offset, isZero, isNeg, isNegF, offsetF }; } function validateMSMPoints(points, c) { if (!Array.isArray(points)) throw new Error("array expected"); points.forEach((p5, i3) => { if (!(p5 instanceof c)) throw new Error("invalid point at index " + i3); }); } function validateMSMScalars(scalars, field) { if (!Array.isArray(scalars)) throw new Error("array of scalars expected"); scalars.forEach((s, i3) => { if (!field.isValid(s)) throw new Error("invalid scalar at index " + i3); }); } function getW(P) { return pointWindowSizes.get(P) || 1; } function assert0(n) { if (n !== _0n3) throw new Error("invalid wNAF"); } function mulEndoUnsafe(Point3, point, k1, k2) { let acc = point; let p1 = Point3.ZERO; let p22 = Point3.ZERO; while (k1 > _0n3 || k2 > _0n3) { if (k1 & _1n3) p1 = p1.add(acc); if (k2 & _1n3) p22 = p22.add(acc); acc = acc.double(); k1 >>= _1n3; k2 >>= _1n3; } return { p1, p2: p22 }; } function pippenger(c, fieldN, points, scalars) { validateMSMPoints(points, c); validateMSMScalars(scalars, fieldN); const plength = points.length; const slength = scalars.length; if (plength !== slength) throw new Error("arrays of points and scalars must have equal length"); const zero = c.ZERO; const wbits = bitLen(BigInt(plength)); let windowSize = 1; if (wbits > 12) windowSize = wbits - 3; else if (wbits > 4) windowSize = wbits - 2; else if (wbits > 0) windowSize = 2; const MASK = bitMask(windowSize); const buckets = new Array(Number(MASK) + 1).fill(zero); const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize; let sum = zero; for (let i3 = lastBits; i3 >= 0; i3 -= windowSize) { buckets.fill(zero); for (let j2 = 0; j2 < slength; j2++) { const scalar = scalars[j2]; const wbits2 = Number(scalar >> BigInt(i3) & MASK); buckets[wbits2] = buckets[wbits2].add(points[j2]); } let resI = zero; for (let j2 = buckets.length - 1, sumI = zero; j2 > 0; j2--) { sumI = sumI.add(buckets[j2]); resI = resI.add(sumI); } sum = sum.add(resI); if (i3 !== 0) for (let j2 = 0; j2 < windowSize; j2++) sum = sum.double(); } return sum; } function createField(order, field, isLE4) { if (field) { if (field.ORDER !== order) throw new Error("Field.ORDER must match order: Fp == p, Fn == n"); validateField(field); return field; } else { return Field(order, { isLE: isLE4 }); } } function _createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) { if (FpFnLE === void 0) FpFnLE = type === "edwards"; if (!CURVE || typeof CURVE !== "object") throw new Error(`expected valid ${type} CURVE object`); for (const p5 of ["p", "n", "h"]) { const val = CURVE[p5]; if (!(typeof val === "bigint" && val > _0n3)) throw new Error(`CURVE.${p5} must be positive bigint`); } const Fp2 = createField(CURVE.p, curveOpts.Fp, FpFnLE); const Fn = createField(CURVE.n, curveOpts.Fn, FpFnLE); const _b = type === "weierstrass" ? "b" : "d"; const params = ["Gx", "Gy", "a", _b]; for (const p5 of params) { if (!Fp2.isValid(CURVE[p5])) throw new Error(`CURVE.${p5} must be valid field element of CURVE.Fp`); } CURVE = Object.freeze(Object.assign({}, CURVE)); return { CURVE, Fp: Fp2, Fn }; } var _0n3, _1n3, pointPrecomputes, pointWindowSizes, wNAF; var init_curve = __esm({ "ndk/node_modules/@noble/curves/esm/abstract/curve.js"() { init_utils2(); init_modular(); _0n3 = BigInt(0); _1n3 = BigInt(1); pointPrecomputes = /* @__PURE__ */ new WeakMap(); pointWindowSizes = /* @__PURE__ */ new WeakMap(); wNAF = class { // Parametrized with a given Point class (not individual point) constructor(Point3, bits) { this.BASE = Point3.BASE; this.ZERO = Point3.ZERO; this.Fn = Point3.Fn; this.bits = bits; } // non-const time multiplication ladder _unsafeLadder(elm, n, p5 = this.ZERO) { let d17 = elm; while (n > _0n3) { if (n & _1n3) p5 = p5.add(d17); d17 = d17.double(); n >>= _1n3; } return p5; } /** * Creates a wNAF precomputation window. Used for caching. * Default window size is set by `utils.precompute()` and is equal to 8. * Number of precomputed points depends on the curve size: * 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where: * - 𝑊 is the window size * - 𝑛 is the bitlength of the curve order. * For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224. * @param point Point instance * @param W window size * @returns precomputed point tables flattened to a single array */ precomputeWindow(point, W3) { const { windows, windowSize } = calcWOpts(W3, this.bits); const points = []; let p5 = point; let base = p5; for (let window2 = 0; window2 < windows; window2++) { base = p5; points.push(base); for (let i3 = 1; i3 < windowSize; i3++) { base = base.add(p5); points.push(base); } p5 = base.double(); } return points; } /** * Implements ec multiplication using precomputed tables and w-ary non-adjacent form. * More compact implementation: * https://github.com/paulmillr/noble-secp256k1/blob/47cb1669b6e506ad66b35fe7d76132ae97465da2/index.ts#L502-L541 * @returns real and fake (for const-time) points */ wNAF(W3, precomputes, n) { if (!this.Fn.isValid(n)) throw new Error("invalid scalar"); let p5 = this.ZERO; let f = this.BASE; const wo = calcWOpts(W3, this.bits); for (let window2 = 0; window2 < wo.windows; window2++) { const { nextN, offset, isZero, isNeg, isNegF, offsetF } = calcOffsets(n, window2, wo); n = nextN; if (isZero) { f = f.add(negateCt(isNegF, precomputes[offsetF])); } else { p5 = p5.add(negateCt(isNeg, precomputes[offset])); } } assert0(n); return { p: p5, f }; } /** * Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form. * @param acc accumulator point to add result of multiplication * @returns point */ wNAFUnsafe(W3, precomputes, n, acc = this.ZERO) { const wo = calcWOpts(W3, this.bits); for (let window2 = 0; window2 < wo.windows; window2++) { if (n === _0n3) break; const { nextN, offset, isZero, isNeg } = calcOffsets(n, window2, wo); n = nextN; if (isZero) { continue; } else { const item = precomputes[offset]; acc = acc.add(isNeg ? item.negate() : item); } } assert0(n); return acc; } getPrecomputes(W3, point, transform) { let comp = pointPrecomputes.get(point); if (!comp) { comp = this.precomputeWindow(point, W3); if (W3 !== 1) { if (typeof transform === "function") comp = transform(comp); pointPrecomputes.set(point, comp); } } return comp; } cached(point, scalar, transform) { const W3 = getW(point); return this.wNAF(W3, this.getPrecomputes(W3, point, transform), scalar); } unsafe(point, scalar, transform, prev) { const W3 = getW(point); if (W3 === 1) return this._unsafeLadder(point, scalar, prev); return this.wNAFUnsafe(W3, this.getPrecomputes(W3, point, transform), scalar, prev); } // We calculate precomputes for elliptic curve point multiplication // using windowed method. This specifies window size and // stores precomputed values. Usually only base point would be precomputed. createCache(P, W3) { validateW(W3, this.bits); pointWindowSizes.set(P, W3); pointPrecomputes.delete(P); } hasCache(elm) { return getW(elm) !== 1; } }; } }); // ndk/node_modules/@noble/curves/esm/abstract/weierstrass.js function _splitEndoScalar(k2, basis, n) { const [[a1, b1], [a2, b2]] = basis; const c1 = divNearest(b2 * k2, n); const c2 = divNearest(-b1 * k2, n); let k1 = k2 - c1 * a1 - c2 * a2; let k22 = -c1 * b1 - c2 * b2; const k1neg = k1 < _0n4; const k2neg = k22 < _0n4; if (k1neg) k1 = -k1; if (k2neg) k22 = -k22; const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n4; if (k1 < _0n4 || k1 >= MAX_NUM || k22 < _0n4 || k22 >= MAX_NUM) { throw new Error("splitScalar (endomorphism): failed, k=" + k2); } return { k1neg, k1, k2neg, k2: k22 }; } function validateSigFormat(format) { if (!["compact", "recovered", "der"].includes(format)) throw new Error('Signature format must be "compact", "recovered", or "der"'); return format; } function validateSigOpts(opts, def) { const optsn = {}; for (let optName of Object.keys(def)) { optsn[optName] = opts[optName] === void 0 ? def[optName] : opts[optName]; } _abool2(optsn.lowS, "lowS"); _abool2(optsn.prehash, "prehash"); if (optsn.format !== void 0) validateSigFormat(optsn.format); return optsn; } function _normFnElement(Fn, key) { const { BYTES: expected } = Fn; let num3; if (typeof key === "bigint") { num3 = key; } else { let bytes4 = ensureBytes("private key", key); try { num3 = Fn.fromBytes(bytes4); } catch (error) { throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`); } } if (!Fn.isValidNot0(num3)) throw new Error("invalid private key: out of range [1..N-1]"); return num3; } function weierstrassN(params, extraOpts = {}) { const validated = _createCurveFields("weierstrass", params, extraOpts); const { Fp: Fp2, Fn } = validated; let CURVE = validated.CURVE; const { h: cofactor, n: CURVE_ORDER } = CURVE; _validateObject(extraOpts, {}, { allowInfinityPoint: "boolean", clearCofactor: "function", isTorsionFree: "function", fromBytes: "function", toBytes: "function", endo: "object", wrapPrivateKey: "boolean" }); const { endo } = extraOpts; if (endo) { if (!Fp2.is0(CURVE.a) || typeof endo.beta !== "bigint" || !Array.isArray(endo.basises)) { throw new Error('invalid endo: expected "beta": bigint and "basises": array'); } } const lengths = getWLengths(Fp2, Fn); function assertCompressionIsSupported() { if (!Fp2.isOdd) throw new Error("compression is not supported: Field does not have .isOdd()"); } function pointToBytes3(_c, point, isCompressed) { const { x: x2, y: y2 } = point.toAffine(); const bx = Fp2.toBytes(x2); _abool2(isCompressed, "isCompressed"); if (isCompressed) { assertCompressionIsSupported(); const hasEvenY = !Fp2.isOdd(y2); return concatBytes(pprefix(hasEvenY), bx); } else { return concatBytes(Uint8Array.of(4), bx, Fp2.toBytes(y2)); } } function pointFromBytes(bytes4) { _abytes2(bytes4, void 0, "Point"); const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; const length = bytes4.length; const head = bytes4[0]; const tail = bytes4.subarray(1); if (length === comp && (head === 2 || head === 3)) { const x2 = Fp2.fromBytes(tail); if (!Fp2.isValid(x2)) throw new Error("bad point: is not on curve, wrong x"); const y2 = weierstrassEquation(x2); let y3; try { y3 = Fp2.sqrt(y2); } catch (sqrtError) { const err = sqrtError instanceof Error ? ": " + sqrtError.message : ""; throw new Error("bad point: is not on curve, sqrt error" + err); } assertCompressionIsSupported(); const isYOdd = Fp2.isOdd(y3); const isHeadOdd = (head & 1) === 1; if (isHeadOdd !== isYOdd) y3 = Fp2.neg(y3); return { x: x2, y: y3 }; } else if (length === uncomp && head === 4) { const L = Fp2.BYTES; const x2 = Fp2.fromBytes(tail.subarray(0, L)); const y2 = Fp2.fromBytes(tail.subarray(L, L * 2)); if (!isValidXY(x2, y2)) throw new Error("bad point: is not on curve"); return { x: x2, y: y2 }; } else { throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`); } } const encodePoint = extraOpts.toBytes || pointToBytes3; const decodePoint = extraOpts.fromBytes || pointFromBytes; function weierstrassEquation(x2) { const x22 = Fp2.sqr(x2); const x3 = Fp2.mul(x22, x2); return Fp2.add(Fp2.add(x3, Fp2.mul(x2, CURVE.a)), CURVE.b); } function isValidXY(x2, y2) { const left = Fp2.sqr(y2); const right = weierstrassEquation(x2); return Fp2.eql(left, right); } if (!isValidXY(CURVE.Gx, CURVE.Gy)) throw new Error("bad curve params: generator point"); const _4a3 = Fp2.mul(Fp2.pow(CURVE.a, _3n2), _4n2); const _27b2 = Fp2.mul(Fp2.sqr(CURVE.b), BigInt(27)); if (Fp2.is0(Fp2.add(_4a3, _27b2))) throw new Error("bad curve params: a or b"); function acoord(title, n, banZero = false) { if (!Fp2.isValid(n) || banZero && Fp2.is0(n)) throw new Error(`bad point coordinate ${title}`); return n; } function aprjpoint(other) { if (!(other instanceof Point3)) throw new Error("ProjectivePoint expected"); } function splitEndoScalarN(k2) { if (!endo || !endo.basises) throw new Error("no endo"); return _splitEndoScalar(k2, endo.basises, Fn.ORDER); } const toAffineMemo = memoized((p5, iz) => { const { X: X2, Y, Z } = p5; if (Fp2.eql(Z, Fp2.ONE)) return { x: X2, y: Y }; const is0 = p5.is0(); if (iz == null) iz = is0 ? Fp2.ONE : Fp2.inv(Z); const x2 = Fp2.mul(X2, iz); const y2 = Fp2.mul(Y, iz); const zz = Fp2.mul(Z, iz); if (is0) return { x: Fp2.ZERO, y: Fp2.ZERO }; if (!Fp2.eql(zz, Fp2.ONE)) throw new Error("invZ was invalid"); return { x: x2, y: y2 }; }); const assertValidMemo = memoized((p5) => { if (p5.is0()) { if (extraOpts.allowInfinityPoint && !Fp2.is0(p5.Y)) return; throw new Error("bad point: ZERO"); } const { x: x2, y: y2 } = p5.toAffine(); if (!Fp2.isValid(x2) || !Fp2.isValid(y2)) throw new Error("bad point: x or y not field elements"); if (!isValidXY(x2, y2)) throw new Error("bad point: equation left != right"); if (!p5.isTorsionFree()) throw new Error("bad point: not in prime-order subgroup"); return true; }); function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) { k2p = new Point3(Fp2.mul(k2p.X, endoBeta), k2p.Y, k2p.Z); k1p = negateCt(k1neg, k1p); k2p = negateCt(k2neg, k2p); return k1p.add(k2p); } class Point3 { /** Does NOT validate if the point is valid. Use `.assertValidity()`. */ constructor(X2, Y, Z) { this.X = acoord("x", X2); this.Y = acoord("y", Y, true); this.Z = acoord("z", Z); Object.freeze(this); } static CURVE() { return CURVE; } /** Does NOT validate if the point is valid. Use `.assertValidity()`. */ static fromAffine(p5) { const { x: x2, y: y2 } = p5 || {}; if (!p5 || !Fp2.isValid(x2) || !Fp2.isValid(y2)) throw new Error("invalid affine point"); if (p5 instanceof Point3) throw new Error("projective point not allowed"); if (Fp2.is0(x2) && Fp2.is0(y2)) return Point3.ZERO; return new Point3(x2, y2, Fp2.ONE); } static fromBytes(bytes4) { const P = Point3.fromAffine(decodePoint(_abytes2(bytes4, void 0, "point"))); P.assertValidity(); return P; } static fromHex(hex2) { return Point3.fromBytes(ensureBytes("pointHex", hex2)); } get x() { return this.toAffine().x; } get y() { return this.toAffine().y; } /** * * @param windowSize * @param isLazy true will defer table computation until the first multiplication * @returns */ precompute(windowSize = 8, isLazy = true) { wnaf.createCache(this, windowSize); if (!isLazy) this.multiply(_3n2); return this; } // TODO: return `this` /** A point on curve is valid if it conforms to equation. */ assertValidity() { assertValidMemo(this); } hasEvenY() { const { y: y2 } = this.toAffine(); if (!Fp2.isOdd) throw new Error("Field doesn't support isOdd"); return !Fp2.isOdd(y2); } /** Compare one point to another. */ equals(other) { aprjpoint(other); const { X: X1, Y: Y1, Z: Z1 } = this; const { X: X2, Y: Y2, Z: Z2 } = other; const U1 = Fp2.eql(Fp2.mul(X1, Z2), Fp2.mul(X2, Z1)); const U2 = Fp2.eql(Fp2.mul(Y1, Z2), Fp2.mul(Y2, Z1)); return U1 && U2; } /** Flips point to one corresponding to (x, -y) in Affine coordinates. */ negate() { return new Point3(this.X, Fp2.neg(this.Y), this.Z); } // Renes-Costello-Batina exception-free doubling formula. // There is 30% faster Jacobian formula, but it is not complete. // https://eprint.iacr.org/2015/1060, algorithm 3 // Cost: 8M + 3S + 3*a + 2*b3 + 15add. double() { const { a, b } = CURVE; const b3 = Fp2.mul(b, _3n2); const { X: X1, Y: Y1, Z: Z1 } = this; let X3 = Fp2.ZERO, Y3 = Fp2.ZERO, Z3 = Fp2.ZERO; let t0 = Fp2.mul(X1, X1); let t1 = Fp2.mul(Y1, Y1); let t2 = Fp2.mul(Z1, Z1); let t3 = Fp2.mul(X1, Y1); t3 = Fp2.add(t3, t3); Z3 = Fp2.mul(X1, Z1); Z3 = Fp2.add(Z3, Z3); X3 = Fp2.mul(a, Z3); Y3 = Fp2.mul(b3, t2); Y3 = Fp2.add(X3, Y3); X3 = Fp2.sub(t1, Y3); Y3 = Fp2.add(t1, Y3); Y3 = Fp2.mul(X3, Y3); X3 = Fp2.mul(t3, X3); Z3 = Fp2.mul(b3, Z3); t2 = Fp2.mul(a, t2); t3 = Fp2.sub(t0, t2); t3 = Fp2.mul(a, t3); t3 = Fp2.add(t3, Z3); Z3 = Fp2.add(t0, t0); t0 = Fp2.add(Z3, t0); t0 = Fp2.add(t0, t2); t0 = Fp2.mul(t0, t3); Y3 = Fp2.add(Y3, t0); t2 = Fp2.mul(Y1, Z1); t2 = Fp2.add(t2, t2); t0 = Fp2.mul(t2, t3); X3 = Fp2.sub(X3, t0); Z3 = Fp2.mul(t2, t1); Z3 = Fp2.add(Z3, Z3); Z3 = Fp2.add(Z3, Z3); return new Point3(X3, Y3, Z3); } // Renes-Costello-Batina exception-free addition formula. // There is 30% faster Jacobian formula, but it is not complete. // https://eprint.iacr.org/2015/1060, algorithm 1 // Cost: 12M + 0S + 3*a + 3*b3 + 23add. add(other) { aprjpoint(other); const { X: X1, Y: Y1, Z: Z1 } = this; const { X: X2, Y: Y2, Z: Z2 } = other; let X3 = Fp2.ZERO, Y3 = Fp2.ZERO, Z3 = Fp2.ZERO; const a = CURVE.a; const b3 = Fp2.mul(CURVE.b, _3n2); let t0 = Fp2.mul(X1, X2); let t1 = Fp2.mul(Y1, Y2); let t2 = Fp2.mul(Z1, Z2); let t3 = Fp2.add(X1, Y1); let t4 = Fp2.add(X2, Y2); t3 = Fp2.mul(t3, t4); t4 = Fp2.add(t0, t1); t3 = Fp2.sub(t3, t4); t4 = Fp2.add(X1, Z1); let t5 = Fp2.add(X2, Z2); t4 = Fp2.mul(t4, t5); t5 = Fp2.add(t0, t2); t4 = Fp2.sub(t4, t5); t5 = Fp2.add(Y1, Z1); X3 = Fp2.add(Y2, Z2); t5 = Fp2.mul(t5, X3); X3 = Fp2.add(t1, t2); t5 = Fp2.sub(t5, X3); Z3 = Fp2.mul(a, t4); X3 = Fp2.mul(b3, t2); Z3 = Fp2.add(X3, Z3); X3 = Fp2.sub(t1, Z3); Z3 = Fp2.add(t1, Z3); Y3 = Fp2.mul(X3, Z3); t1 = Fp2.add(t0, t0); t1 = Fp2.add(t1, t0); t2 = Fp2.mul(a, t2); t4 = Fp2.mul(b3, t4); t1 = Fp2.add(t1, t2); t2 = Fp2.sub(t0, t2); t2 = Fp2.mul(a, t2); t4 = Fp2.add(t4, t2); t0 = Fp2.mul(t1, t4); Y3 = Fp2.add(Y3, t0); t0 = Fp2.mul(t5, t4); X3 = Fp2.mul(t3, X3); X3 = Fp2.sub(X3, t0); t0 = Fp2.mul(t3, t1); Z3 = Fp2.mul(t5, Z3); Z3 = Fp2.add(Z3, t0); return new Point3(X3, Y3, Z3); } subtract(other) { return this.add(other.negate()); } is0() { return this.equals(Point3.ZERO); } /** * Constant time multiplication. * Uses wNAF method. Windowed method may be 10% faster, * but takes 2x longer to generate and consumes 2x memory. * Uses precomputes when available. * Uses endomorphism for Koblitz curves. * @param scalar by which the point would be multiplied * @returns New point */ multiply(scalar) { const { endo: endo2 } = extraOpts; if (!Fn.isValidNot0(scalar)) throw new Error("invalid scalar: out of range"); let point, fake; const mul3 = (n) => wnaf.cached(this, n, (p5) => normalizeZ(Point3, p5)); if (endo2) { const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar); const { p: k1p, f: k1f } = mul3(k1); const { p: k2p, f: k2f } = mul3(k2); fake = k1f.add(k2f); point = finishEndo(endo2.beta, k1p, k2p, k1neg, k2neg); } else { const { p: p5, f } = mul3(scalar); point = p5; fake = f; } return normalizeZ(Point3, [point, fake])[0]; } /** * Non-constant-time multiplication. Uses double-and-add algorithm. * It's faster, but should only be used when you don't care about * an exposed secret key e.g. sig verification, which works over *public* keys. */ multiplyUnsafe(sc) { const { endo: endo2 } = extraOpts; const p5 = this; if (!Fn.isValid(sc)) throw new Error("invalid scalar: out of range"); if (sc === _0n4 || p5.is0()) return Point3.ZERO; if (sc === _1n4) return p5; if (wnaf.hasCache(this)) return this.multiply(sc); if (endo2) { const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc); const { p1, p2: p22 } = mulEndoUnsafe(Point3, p5, k1, k2); return finishEndo(endo2.beta, p1, p22, k1neg, k2neg); } else { return wnaf.unsafe(p5, sc); } } multiplyAndAddUnsafe(Q2, a, b) { const sum = this.multiplyUnsafe(a).add(Q2.multiplyUnsafe(b)); return sum.is0() ? void 0 : sum; } /** * Converts Projective point to affine (x, y) coordinates. * @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch */ toAffine(invertedZ) { return toAffineMemo(this, invertedZ); } /** * Checks whether Point is free of torsion elements (is in prime subgroup). * Always torsion-free for cofactor=1 curves. */ isTorsionFree() { const { isTorsionFree } = extraOpts; if (cofactor === _1n4) return true; if (isTorsionFree) return isTorsionFree(Point3, this); return wnaf.unsafe(this, CURVE_ORDER).is0(); } clearCofactor() { const { clearCofactor } = extraOpts; if (cofactor === _1n4) return this; if (clearCofactor) return clearCofactor(Point3, this); return this.multiplyUnsafe(cofactor); } isSmallOrder() { return this.multiplyUnsafe(cofactor).is0(); } toBytes(isCompressed = true) { _abool2(isCompressed, "isCompressed"); this.assertValidity(); return encodePoint(Point3, this, isCompressed); } toHex(isCompressed = true) { return bytesToHex(this.toBytes(isCompressed)); } toString() { return ``; } // TODO: remove get px() { return this.X; } get py() { return this.X; } get pz() { return this.Z; } toRawBytes(isCompressed = true) { return this.toBytes(isCompressed); } _setWindowSize(windowSize) { this.precompute(windowSize); } static normalizeZ(points) { return normalizeZ(Point3, points); } static msm(points, scalars) { return pippenger(Point3, Fn, points, scalars); } static fromPrivateKey(privateKey) { return Point3.BASE.multiply(_normFnElement(Fn, privateKey)); } } Point3.BASE = new Point3(CURVE.Gx, CURVE.Gy, Fp2.ONE); Point3.ZERO = new Point3(Fp2.ZERO, Fp2.ONE, Fp2.ZERO); Point3.Fp = Fp2; Point3.Fn = Fn; const bits = Fn.BITS; const wnaf = new wNAF(Point3, extraOpts.endo ? Math.ceil(bits / 2) : bits); Point3.BASE.precompute(8); return Point3; } function pprefix(hasEvenY) { return Uint8Array.of(hasEvenY ? 2 : 3); } function getWLengths(Fp2, Fn) { return { secretKey: Fn.BYTES, publicKey: 1 + Fp2.BYTES, publicKeyUncompressed: 1 + 2 * Fp2.BYTES, publicKeyHasPrefix: true, signature: 2 * Fn.BYTES }; } function ecdh(Point3, ecdhOpts = {}) { const { Fn } = Point3; const randomBytes_ = ecdhOpts.randomBytes || randomBytes; const lengths = Object.assign(getWLengths(Point3.Fp, Fn), { seed: getMinHashLength(Fn.ORDER) }); function isValidSecretKey(secretKey) { try { return !!_normFnElement(Fn, secretKey); } catch (error) { return false; } } function isValidPublicKey(publicKey, isCompressed) { const { publicKey: comp, publicKeyUncompressed } = lengths; try { const l3 = publicKey.length; if (isCompressed === true && l3 !== comp) return false; if (isCompressed === false && l3 !== publicKeyUncompressed) return false; return !!Point3.fromBytes(publicKey); } catch (error) { return false; } } function randomSecretKey(seed = randomBytes_(lengths.seed)) { return mapHashToField(_abytes2(seed, lengths.seed, "seed"), Fn.ORDER); } function getPublicKey4(secretKey, isCompressed = true) { return Point3.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed); } function keygen(seed) { const secretKey = randomSecretKey(seed); return { secretKey, publicKey: getPublicKey4(secretKey) }; } function isProbPub(item) { if (typeof item === "bigint") return false; if (item instanceof Point3) return true; const { secretKey, publicKey, publicKeyUncompressed } = lengths; if (Fn.allowedLengths || secretKey === publicKey) return void 0; const l3 = ensureBytes("key", item).length; return l3 === publicKey || l3 === publicKeyUncompressed; } function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) { if (isProbPub(secretKeyA) === true) throw new Error("first arg must be private key"); if (isProbPub(publicKeyB) === false) throw new Error("second arg must be public key"); const s = _normFnElement(Fn, secretKeyA); const b = Point3.fromHex(publicKeyB); return b.multiply(s).toBytes(isCompressed); } const utils = { isValidSecretKey, isValidPublicKey, randomSecretKey, // TODO: remove isValidPrivateKey: isValidSecretKey, randomPrivateKey: randomSecretKey, normPrivateKeyToScalar: (key) => _normFnElement(Fn, key), precompute(windowSize = 8, point = Point3.BASE) { return point.precompute(windowSize, false); } }; return Object.freeze({ getPublicKey: getPublicKey4, getSharedSecret, keygen, Point: Point3, utils, lengths }); } function ecdsa(Point3, hash3, ecdsaOpts = {}) { ahash(hash3); _validateObject(ecdsaOpts, {}, { hmac: "function", lowS: "boolean", randomBytes: "function", bits2int: "function", bits2int_modN: "function" }); const randomBytes4 = ecdsaOpts.randomBytes || randomBytes; const hmac4 = ecdsaOpts.hmac || ((key, ...msgs) => hmac(hash3, key, concatBytes(...msgs))); const { Fp: Fp2, Fn } = Point3; const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn; const { keygen, getPublicKey: getPublicKey4, getSharedSecret, utils, lengths } = ecdh(Point3, ecdsaOpts); const defaultSigOpts = { prehash: false, lowS: typeof ecdsaOpts.lowS === "boolean" ? ecdsaOpts.lowS : false, format: void 0, //'compact' as ECDSASigFormat, extraEntropy: false }; const defaultSigOpts_format = "compact"; function isBiggerThanHalfOrder(number4) { const HALF = CURVE_ORDER >> _1n4; return number4 > HALF; } function validateRS(title, num3) { if (!Fn.isValidNot0(num3)) throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`); return num3; } function validateSigLength(bytes4, format) { validateSigFormat(format); const size = lengths.signature; const sizer = format === "compact" ? size : format === "recovered" ? size + 1 : void 0; return _abytes2(bytes4, sizer, `${format} signature`); } class Signature { constructor(r, s, recovery) { this.r = validateRS("r", r); this.s = validateRS("s", s); if (recovery != null) this.recovery = recovery; Object.freeze(this); } static fromBytes(bytes4, format = defaultSigOpts_format) { validateSigLength(bytes4, format); let recid; if (format === "der") { const { r: r2, s: s2 } = DER.toSig(_abytes2(bytes4)); return new Signature(r2, s2); } if (format === "recovered") { recid = bytes4[0]; format = "compact"; bytes4 = bytes4.subarray(1); } const L = Fn.BYTES; const r = bytes4.subarray(0, L); const s = bytes4.subarray(L, L * 2); return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid); } static fromHex(hex2, format) { return this.fromBytes(hexToBytes(hex2), format); } addRecoveryBit(recovery) { return new Signature(this.r, this.s, recovery); } recoverPublicKey(messageHash) { const FIELD_ORDER = Fp2.ORDER; const { r, s, recovery: rec } = this; if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error("recovery id invalid"); const hasCofactor = CURVE_ORDER * _2n2 < FIELD_ORDER; if (hasCofactor && rec > 1) throw new Error("recovery id is ambiguous for h>1 curve"); const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r; if (!Fp2.isValid(radj)) throw new Error("recovery id 2 or 3 invalid"); const x2 = Fp2.toBytes(radj); const R2 = Point3.fromBytes(concatBytes(pprefix((rec & 1) === 0), x2)); const ir = Fn.inv(radj); const h2 = bits2int_modN(ensureBytes("msgHash", messageHash)); const u1 = Fn.create(-h2 * ir); const u22 = Fn.create(s * ir); const Q2 = Point3.BASE.multiplyUnsafe(u1).add(R2.multiplyUnsafe(u22)); if (Q2.is0()) throw new Error("point at infinify"); Q2.assertValidity(); return Q2; } // Signatures should be low-s, to prevent malleability. hasHighS() { return isBiggerThanHalfOrder(this.s); } toBytes(format = defaultSigOpts_format) { validateSigFormat(format); if (format === "der") return hexToBytes(DER.hexFromSig(this)); const r = Fn.toBytes(this.r); const s = Fn.toBytes(this.s); if (format === "recovered") { if (this.recovery == null) throw new Error("recovery bit must be present"); return concatBytes(Uint8Array.of(this.recovery), r, s); } return concatBytes(r, s); } toHex(format) { return bytesToHex(this.toBytes(format)); } // TODO: remove assertValidity() { } static fromCompact(hex2) { return Signature.fromBytes(ensureBytes("sig", hex2), "compact"); } static fromDER(hex2) { return Signature.fromBytes(ensureBytes("sig", hex2), "der"); } normalizeS() { return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this; } toDERRawBytes() { return this.toBytes("der"); } toDERHex() { return bytesToHex(this.toBytes("der")); } toCompactRawBytes() { return this.toBytes("compact"); } toCompactHex() { return bytesToHex(this.toBytes("compact")); } } const bits2int = ecdsaOpts.bits2int || function bits2int_def(bytes4) { if (bytes4.length > 8192) throw new Error("input is too large"); const num3 = bytesToNumberBE(bytes4); const delta = bytes4.length * 8 - fnBits; return delta > 0 ? num3 >> BigInt(delta) : num3; }; const bits2int_modN = ecdsaOpts.bits2int_modN || function bits2int_modN_def(bytes4) { return Fn.create(bits2int(bytes4)); }; const ORDER_MASK = bitMask(fnBits); function int2octets(num3) { aInRange("num < 2^" + fnBits, num3, _0n4, ORDER_MASK); return Fn.toBytes(num3); } function validateMsgAndHash(message, prehash) { _abytes2(message, void 0, "message"); return prehash ? _abytes2(hash3(message), void 0, "prehashed message") : message; } function prepSig(message, privateKey, opts) { if (["recovered", "canonical"].some((k2) => k2 in opts)) throw new Error("sign() legacy options not supported"); const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts); message = validateMsgAndHash(message, prehash); const h1int = bits2int_modN(message); const d17 = _normFnElement(Fn, privateKey); const seedArgs = [int2octets(d17), int2octets(h1int)]; if (extraEntropy != null && extraEntropy !== false) { const e2 = extraEntropy === true ? randomBytes4(lengths.secretKey) : extraEntropy; seedArgs.push(ensureBytes("extraEntropy", e2)); } const seed = concatBytes(...seedArgs); const m = h1int; function k2sig(kBytes) { const k2 = bits2int(kBytes); if (!Fn.isValidNot0(k2)) return; const ik = Fn.inv(k2); const q2 = Point3.BASE.multiply(k2).toAffine(); const r = Fn.create(q2.x); if (r === _0n4) return; const s = Fn.create(ik * Fn.create(m + r * d17)); if (s === _0n4) return; let recovery = (q2.x === r ? 0 : 2) | Number(q2.y & _1n4); let normS = s; if (lowS && isBiggerThanHalfOrder(s)) { normS = Fn.neg(s); recovery ^= 1; } return new Signature(r, normS, recovery); } return { seed, k2sig }; } function sign(message, secretKey, opts = {}) { message = ensureBytes("message", message); const { seed, k2sig } = prepSig(message, secretKey, opts); const drbg = createHmacDrbg(hash3.outputLen, Fn.BYTES, hmac4); const sig = drbg(seed, k2sig); return sig; } function tryParsingSig(sg) { let sig = void 0; const isHex = typeof sg === "string" || isBytes(sg); const isObj = !isHex && sg !== null && typeof sg === "object" && typeof sg.r === "bigint" && typeof sg.s === "bigint"; if (!isHex && !isObj) throw new Error("invalid signature, expected Uint8Array, hex string or Signature instance"); if (isObj) { sig = new Signature(sg.r, sg.s); } else if (isHex) { try { sig = Signature.fromBytes(ensureBytes("sig", sg), "der"); } catch (derError) { if (!(derError instanceof DER.Err)) throw derError; } if (!sig) { try { sig = Signature.fromBytes(ensureBytes("sig", sg), "compact"); } catch (error) { return false; } } } if (!sig) return false; return sig; } function verify(signature, message, publicKey, opts = {}) { const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts); publicKey = ensureBytes("publicKey", publicKey); message = validateMsgAndHash(ensureBytes("message", message), prehash); if ("strict" in opts) throw new Error("options.strict was renamed to lowS"); const sig = format === void 0 ? tryParsingSig(signature) : Signature.fromBytes(ensureBytes("sig", signature), format); if (sig === false) return false; try { const P = Point3.fromBytes(publicKey); if (lowS && sig.hasHighS()) return false; const { r, s } = sig; const h2 = bits2int_modN(message); const is = Fn.inv(s); const u1 = Fn.create(h2 * is); const u22 = Fn.create(r * is); const R2 = Point3.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u22)); if (R2.is0()) return false; const v6 = Fn.create(R2.x); return v6 === r; } catch (e2) { return false; } } function recoverPublicKey(signature, message, opts = {}) { const { prehash } = validateSigOpts(opts, defaultSigOpts); message = validateMsgAndHash(message, prehash); return Signature.fromBytes(signature, "recovered").recoverPublicKey(message).toBytes(); } return Object.freeze({ keygen, getPublicKey: getPublicKey4, getSharedSecret, utils, lengths, Point: Point3, sign, verify, recoverPublicKey, Signature, hash: hash3 }); } function _weierstrass_legacy_opts_to_new(c) { const CURVE = { a: c.a, b: c.b, p: c.Fp.ORDER, n: c.n, h: c.h, Gx: c.Gx, Gy: c.Gy }; const Fp2 = c.Fp; let allowedLengths = c.allowedPrivateKeyLengths ? Array.from(new Set(c.allowedPrivateKeyLengths.map((l3) => Math.ceil(l3 / 2)))) : void 0; const Fn = Field(CURVE.n, { BITS: c.nBitLength, allowedLengths, modFromBytes: c.wrapPrivateKey }); const curveOpts = { Fp: Fp2, Fn, allowInfinityPoint: c.allowInfinityPoint, endo: c.endo, isTorsionFree: c.isTorsionFree, clearCofactor: c.clearCofactor, fromBytes: c.fromBytes, toBytes: c.toBytes }; return { CURVE, curveOpts }; } function _ecdsa_legacy_opts_to_new(c) { const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c); const ecdsaOpts = { hmac: c.hmac, randomBytes: c.randomBytes, lowS: c.lowS, bits2int: c.bits2int, bits2int_modN: c.bits2int_modN }; return { CURVE, curveOpts, hash: c.hash, ecdsaOpts }; } function _ecdsa_new_output_to_legacy(c, _ecdsa) { const Point3 = _ecdsa.Point; return Object.assign({}, _ecdsa, { ProjectivePoint: Point3, CURVE: Object.assign({}, c, nLength(Point3.Fn.ORDER, Point3.Fn.BITS)) }); } function weierstrass(c) { const { CURVE, curveOpts, hash: hash3, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c); const Point3 = weierstrassN(CURVE, curveOpts); const signs = ecdsa(Point3, hash3, ecdsaOpts); return _ecdsa_new_output_to_legacy(c, signs); } var divNearest, DERErr, DER, _0n4, _1n4, _2n2, _3n2, _4n2; var init_weierstrass = __esm({ "ndk/node_modules/@noble/curves/esm/abstract/weierstrass.js"() { init_hmac(); init_utils(); init_utils2(); init_curve(); init_modular(); divNearest = (num3, den) => (num3 + (num3 >= 0 ? den : -den) / _2n2) / den; DERErr = class extends Error { constructor(m = "") { super(m); } }; DER = { // asn.1 DER encoding utils Err: DERErr, // Basic building block is TLV (Tag-Length-Value) _tlv: { encode: (tag, data) => { const { Err: E2 } = DER; if (tag < 0 || tag > 256) throw new E2("tlv.encode: wrong tag"); if (data.length & 1) throw new E2("tlv.encode: unpadded data"); const dataLen = data.length / 2; const len = numberToHexUnpadded(dataLen); if (len.length / 2 & 128) throw new E2("tlv.encode: long form length too big"); const lenLen = dataLen > 127 ? numberToHexUnpadded(len.length / 2 | 128) : ""; const t = numberToHexUnpadded(tag); return t + lenLen + len + data; }, // v - value, l - left bytes (unparsed) decode(tag, data) { const { Err: E2 } = DER; let pos = 0; if (tag < 0 || tag > 256) throw new E2("tlv.encode: wrong tag"); if (data.length < 2 || data[pos++] !== tag) throw new E2("tlv.decode: wrong tlv"); const first = data[pos++]; const isLong = !!(first & 128); let length = 0; if (!isLong) length = first; else { const lenLen = first & 127; if (!lenLen) throw new E2("tlv.decode(long): indefinite length not supported"); if (lenLen > 4) throw new E2("tlv.decode(long): byte length is too big"); const lengthBytes = data.subarray(pos, pos + lenLen); if (lengthBytes.length !== lenLen) throw new E2("tlv.decode: length bytes not complete"); if (lengthBytes[0] === 0) throw new E2("tlv.decode(long): zero leftmost byte"); for (const b of lengthBytes) length = length << 8 | b; pos += lenLen; if (length < 128) throw new E2("tlv.decode(long): not minimal encoding"); } const v6 = data.subarray(pos, pos + length); if (v6.length !== length) throw new E2("tlv.decode: wrong value length"); return { v: v6, l: data.subarray(pos + length) }; } }, // https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag, // since we always use positive integers here. It must always be empty: // - add zero byte if exists // - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding) _int: { encode(num3) { const { Err: E2 } = DER; if (num3 < _0n4) throw new E2("integer: negative integers are not allowed"); let hex2 = numberToHexUnpadded(num3); if (Number.parseInt(hex2[0], 16) & 8) hex2 = "00" + hex2; if (hex2.length & 1) throw new E2("unexpected DER parsing assertion: unpadded hex"); return hex2; }, decode(data) { const { Err: E2 } = DER; if (data[0] & 128) throw new E2("invalid signature integer: negative"); if (data[0] === 0 && !(data[1] & 128)) throw new E2("invalid signature integer: unnecessary leading zero"); return bytesToNumberBE(data); } }, toSig(hex2) { const { Err: E2, _int: int, _tlv: tlv } = DER; const data = ensureBytes("signature", hex2); const { v: seqBytes, l: seqLeftBytes } = tlv.decode(48, data); if (seqLeftBytes.length) throw new E2("invalid signature: left bytes after parsing"); const { v: rBytes, l: rLeftBytes } = tlv.decode(2, seqBytes); const { v: sBytes, l: sLeftBytes } = tlv.decode(2, rLeftBytes); if (sLeftBytes.length) throw new E2("invalid signature: left bytes after parsing"); return { r: int.decode(rBytes), s: int.decode(sBytes) }; }, hexFromSig(sig) { const { _tlv: tlv, _int: int } = DER; const rs = tlv.encode(2, int.encode(sig.r)); const ss = tlv.encode(2, int.encode(sig.s)); const seq = rs + ss; return tlv.encode(48, seq); } }; _0n4 = BigInt(0); _1n4 = BigInt(1); _2n2 = BigInt(2); _3n2 = BigInt(3); _4n2 = BigInt(4); } }); // ndk/node_modules/@noble/curves/esm/_shortw_utils.js function createCurve(curveDef, defHash) { const create = (hash3) => weierstrass({ ...curveDef, hash: hash3 }); return { ...create(defHash), create }; } var init_shortw_utils = __esm({ "ndk/node_modules/@noble/curves/esm/_shortw_utils.js"() { init_weierstrass(); } }); // ndk/node_modules/@noble/curves/esm/secp256k1.js function sqrtMod(y2) { const P = secp256k1_CURVE.p; const _3n5 = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22); const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88); const b2 = y2 * y2 * y2 % P; const b3 = b2 * b2 * y2 % P; const b6 = pow2(b3, _3n5, P) * b3 % P; const b9 = pow2(b6, _3n5, P) * b3 % P; const b11 = pow2(b9, _2n3, P) * b2 % P; const b22 = pow2(b11, _11n, P) * b11 % P; const b44 = pow2(b22, _22n, P) * b22 % P; const b88 = pow2(b44, _44n, P) * b44 % P; const b176 = pow2(b88, _88n, P) * b88 % P; const b220 = pow2(b176, _44n, P) * b44 % P; const b223 = pow2(b220, _3n5, P) * b3 % P; const t1 = pow2(b223, _23n, P) * b22 % P; const t2 = pow2(t1, _6n, P) * b2 % P; const root = pow2(t2, _2n3, P); if (!Fpk1.eql(Fpk1.sqr(root), y2)) throw new Error("Cannot find square root"); return root; } function taggedHash(tag, ...messages) { let tagP = TAGGED_HASH_PREFIXES[tag]; if (tagP === void 0) { const tagH = sha256(utf8ToBytes(tag)); tagP = concatBytes(tagH, tagH); TAGGED_HASH_PREFIXES[tag] = tagP; } return sha256(concatBytes(tagP, ...messages)); } function schnorrGetExtPubKey(priv) { const { Fn, BASE } = Pointk1; const d_ = _normFnElement(Fn, priv); const p5 = BASE.multiply(d_); const scalar = hasEven(p5.y) ? d_ : Fn.neg(d_); return { scalar, bytes: pointToBytes(p5) }; } function lift_x(x2) { const Fp2 = Fpk1; if (!Fp2.isValidNot0(x2)) throw new Error("invalid x: Fail if x \u2265 p"); const xx = Fp2.create(x2 * x2); const c = Fp2.create(xx * x2 + BigInt(7)); let y2 = Fp2.sqrt(c); if (!hasEven(y2)) y2 = Fp2.neg(y2); const p5 = Pointk1.fromAffine({ x: x2, y: y2 }); p5.assertValidity(); return p5; } function challenge(...args) { return Pointk1.Fn.create(num(taggedHash("BIP0340/challenge", ...args))); } function schnorrGetPublicKey(secretKey) { return schnorrGetExtPubKey(secretKey).bytes; } function schnorrSign(message, secretKey, auxRand = randomBytes(32)) { const { Fn } = Pointk1; const m = ensureBytes("message", message); const { bytes: px, scalar: d17 } = schnorrGetExtPubKey(secretKey); const a = ensureBytes("auxRand", auxRand, 32); const t = Fn.toBytes(d17 ^ num(taggedHash("BIP0340/aux", a))); const rand = taggedHash("BIP0340/nonce", t, px, m); const { bytes: rx, scalar: k2 } = schnorrGetExtPubKey(rand); const e2 = challenge(rx, px, m); const sig = new Uint8Array(64); sig.set(rx, 0); sig.set(Fn.toBytes(Fn.create(k2 + e2 * d17)), 32); if (!schnorrVerify(sig, m, px)) throw new Error("sign: Invalid signature produced"); return sig; } function schnorrVerify(signature, message, publicKey) { const { Fn, BASE } = Pointk1; const sig = ensureBytes("signature", signature, 64); const m = ensureBytes("message", message); const pub = ensureBytes("publicKey", publicKey, 32); try { const P = lift_x(num(pub)); const r = num(sig.subarray(0, 32)); if (!inRange(r, _1n5, secp256k1_CURVE.p)) return false; const s = num(sig.subarray(32, 64)); if (!inRange(s, _1n5, secp256k1_CURVE.n)) return false; const e2 = challenge(Fn.toBytes(r), pointToBytes(P), m); const R2 = BASE.multiplyUnsafe(s).add(P.multiplyUnsafe(Fn.neg(e2))); const { x: x2, y: y2 } = R2.toAffine(); if (R2.is0() || !hasEven(y2) || x2 !== r) return false; return true; } catch (error) { return false; } } var secp256k1_CURVE, secp256k1_ENDO, _0n5, _1n5, _2n3, Fpk1, secp256k1, TAGGED_HASH_PREFIXES, pointToBytes, Pointk1, hasEven, num, schnorr; var init_secp256k1 = __esm({ "ndk/node_modules/@noble/curves/esm/secp256k1.js"() { init_sha2(); init_utils(); init_shortw_utils(); init_modular(); init_weierstrass(); init_utils2(); secp256k1_CURVE = { p: BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"), n: BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"), h: BigInt(1), a: BigInt(0), b: BigInt(7), Gx: BigInt("0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"), Gy: BigInt("0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8") }; secp256k1_ENDO = { beta: BigInt("0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"), basises: [ [BigInt("0x3086d221a7d46bcde86c90e49284eb15"), -BigInt("0xe4437ed6010e88286f547fa90abfe4c3")], [BigInt("0x114ca50f7a8e2f3f657c1108d9d44cfd8"), BigInt("0x3086d221a7d46bcde86c90e49284eb15")] ] }; _0n5 = /* @__PURE__ */ BigInt(0); _1n5 = /* @__PURE__ */ BigInt(1); _2n3 = /* @__PURE__ */ BigInt(2); Fpk1 = Field(secp256k1_CURVE.p, { sqrt: sqrtMod }); secp256k1 = createCurve({ ...secp256k1_CURVE, Fp: Fpk1, lowS: true, endo: secp256k1_ENDO }, sha256); TAGGED_HASH_PREFIXES = {}; pointToBytes = (point) => point.toBytes(true).slice(1); Pointk1 = /* @__PURE__ */ (() => secp256k1.Point)(); hasEven = (y2) => y2 % _2n3 === _0n5; num = bytesToNumberBE; schnorr = /* @__PURE__ */ (() => { const size = 32; const seedLength = 48; const randomSecretKey = (seed = randomBytes(seedLength)) => { return mapHashToField(seed, secp256k1_CURVE.n); }; secp256k1.utils.randomSecretKey; function keygen(seed) { const secretKey = randomSecretKey(seed); return { secretKey, publicKey: schnorrGetPublicKey(secretKey) }; } return { keygen, getPublicKey: schnorrGetPublicKey, sign: schnorrSign, verify: schnorrVerify, Point: Pointk1, utils: { randomSecretKey, randomPrivateKey: randomSecretKey, taggedHash, // TODO: remove lift_x, pointToBytes, numberToBytesBE, bytesToNumberBE, mod }, lengths: { secretKey: size, publicKey: size, publicKeyHasPrefix: false, signature: size * 2, seed: seedLength } }; })(); } }); // ndk/node_modules/@noble/hashes/esm/sha256.js var sha2562; var init_sha256 = __esm({ "ndk/node_modules/@noble/hashes/esm/sha256.js"() { init_sha2(); sha2562 = sha256; } }); // ndk/node_modules/typescript-lru-cache/dist/LRUCacheNode.js var require_LRUCacheNode = __commonJS({ "ndk/node_modules/typescript-lru-cache/dist/LRUCacheNode.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.LRUCacheNode = void 0; var LRUCacheNode = class { constructor(key, value, options) { const { entryExpirationTimeInMS = null, next = null, prev = null, onEntryEvicted, onEntryMarkedAsMostRecentlyUsed, clone, cloneFn } = options !== null && options !== void 0 ? options : {}; if (typeof entryExpirationTimeInMS === "number" && (entryExpirationTimeInMS <= 0 || Number.isNaN(entryExpirationTimeInMS))) { throw new Error("entryExpirationTimeInMS must either be null (no expiry) or greater than 0"); } this.clone = clone !== null && clone !== void 0 ? clone : false; this.cloneFn = cloneFn !== null && cloneFn !== void 0 ? cloneFn : this.defaultClone; this.key = key; this.internalValue = this.clone ? this.cloneFn(value) : value; this.created = Date.now(); this.entryExpirationTimeInMS = entryExpirationTimeInMS; this.next = next; this.prev = prev; this.onEntryEvicted = onEntryEvicted; this.onEntryMarkedAsMostRecentlyUsed = onEntryMarkedAsMostRecentlyUsed; } get value() { return this.clone ? this.cloneFn(this.internalValue) : this.internalValue; } get isExpired() { return typeof this.entryExpirationTimeInMS === "number" && Date.now() - this.created > this.entryExpirationTimeInMS; } invokeOnEvicted() { if (this.onEntryEvicted) { const { key, value, isExpired } = this; this.onEntryEvicted({ key, value, isExpired }); } } invokeOnEntryMarkedAsMostRecentlyUsed() { if (this.onEntryMarkedAsMostRecentlyUsed) { const { key, value } = this; this.onEntryMarkedAsMostRecentlyUsed({ key, value }); } } defaultClone(value) { if (typeof value === "boolean" || typeof value === "string" || typeof value === "number") { return value; } return JSON.parse(JSON.stringify(value)); } }; exports2.LRUCacheNode = LRUCacheNode; } }); // ndk/node_modules/typescript-lru-cache/dist/LRUCache.js var require_LRUCache = __commonJS({ "ndk/node_modules/typescript-lru-cache/dist/LRUCache.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.LRUCache = void 0; var LRUCacheNode_1 = require_LRUCacheNode(); var LRUCache7 = class { /** * Creates a new instance of the LRUCache. * * @param options Additional configuration options for the LRUCache. * * @example * ```typescript * // No options. * const cache = new LRUCache(); * * // With options. * const cache = new LRUCache({ * entryExpirationTimeInMS: 10000 * }); * ``` */ constructor(options) { this.lookupTable = /* @__PURE__ */ new Map(); this.head = null; this.tail = null; const { maxSize = 25, entryExpirationTimeInMS = null, onEntryEvicted, onEntryMarkedAsMostRecentlyUsed, cloneFn, clone } = options !== null && options !== void 0 ? options : {}; if (Number.isNaN(maxSize) || maxSize <= 0) { throw new Error("maxSize must be greater than 0."); } if (typeof entryExpirationTimeInMS === "number" && (entryExpirationTimeInMS <= 0 || Number.isNaN(entryExpirationTimeInMS))) { throw new Error("entryExpirationTimeInMS must either be null (no expiry) or greater than 0"); } this.maxSizeInternal = maxSize; this.entryExpirationTimeInMS = entryExpirationTimeInMS; this.onEntryEvicted = onEntryEvicted; this.onEntryMarkedAsMostRecentlyUsed = onEntryMarkedAsMostRecentlyUsed; this.clone = clone; this.cloneFn = cloneFn; } /** * Returns the number of entries in the LRUCache object. * If the cache has entryExpirationTimeInMS set, expired entries will be removed before the size is returned. * * @returns The number of entries in the cache. * * @example * ```typescript * const cache = new LRUCache(); * * cache.set('testKey', 'testValue'); * * const size = cache.size; * * // Will log 1 * console.log(size); * ``` */ get size() { this.cleanCache(); return this.lookupTable.size; } /** * Returns the number of entries that can still be added to the LRUCache without evicting existing entries. * * @returns The number of entries that can still be added without evicting existing entries. * * @example * ```typescript * const cache = new LRUCache({ maxSize: 10 }); * * cache.set('testKey', 'testValue'); * * const remainingSize = cache.remainingSize; * * // Will log 9 due to 9 spots remaining before reaching maxSize of 10. * console.log(remainingSize); * ``` */ get remainingSize() { return this.maxSizeInternal - this.size; } /** * Returns the most recently used (newest) entry in the cache. * This will not mark the entry as recently used. * If the newest node is expired, it will be removed. * * @returns The most recently used (newest) entry in the cache. * * @example * ```typescript * const cache = new LRUCache({ maxSize: 10 }); * * cache.set('testKey', 'testValue'); * * const newest = cache.newest; * * // Will log testValue * console.log(newest.value); * * // Will log testKey * console.log(newest.key); * ``` */ get newest() { if (!this.head) { return null; } if (this.head.isExpired) { this.removeNodeFromListAndLookupTable(this.head); return this.newest; } return this.mapNodeToEntry(this.head); } /** * Returns the least recently used (oldest) entry in the cache. * This will not mark the entry as recently used. * If the oldest node is expired, it will be removed. * * @returns The least recently used (oldest) entry in the cache. * * @example * ```typescript * const cache = new LRUCache({ maxSize: 10 }); * * cache.set('testKey', 'testValue'); * * const oldest = cache.oldest; * * // Will log testValue * console.log(oldest.value); * * // Will log testKey * console.log(oldest.key); * ``` */ get oldest() { if (!this.tail) { return null; } if (this.tail.isExpired) { this.removeNodeFromListAndLookupTable(this.tail); return this.oldest; } return this.mapNodeToEntry(this.tail); } /** * Gets or sets the maxSize of the cache. * This will evict the least recently used entries if needed to reach new maxSize. * * @param value The new value for maxSize. Must be greater than 0. * * @example * ```typescript * const cache = new LRUCache({ maxSize: 10 }); * * cache.set('testKey', 'testValue'); * * // Will be 10 * const maxSize = cache.maxSize; * * // Set new maxSize to 5. If there are more than 5 items in the cache, the least recently used entries will be removed until cache size is 5. * cache.maxSize = 5; * ``` */ get maxSize() { return this.maxSizeInternal; } set maxSize(value) { if (Number.isNaN(value) || value <= 0) { throw new Error("maxSize must be greater than 0."); } this.maxSizeInternal = value; this.enforceSizeLimit(); } /** * Sets the value for the key in the LRUCache object. Returns the LRUCache object. * This marks the newly added entry as the most recently used entry. * If adding the new entry makes the cache size go above maxSize, * this will evict the least recently used entries until size is equal to maxSize. * * @param key The key of the entry. * @param value The value to set for the key. * @param entryOptions Additional configuration options for the cache entry. * * @returns The LRUCache instance. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * // Set the key key2 to value value2. Pass in optional options. * cache.set('key2', 'value2', { entryExpirationTimeInMS: 10 }); * ``` */ set(key, value, entryOptions) { const currentNodeForKey = this.lookupTable.get(key); if (currentNodeForKey) { this.removeNodeFromListAndLookupTable(currentNodeForKey); } const node = new LRUCacheNode_1.LRUCacheNode(key, value, { entryExpirationTimeInMS: this.entryExpirationTimeInMS, onEntryEvicted: this.onEntryEvicted, onEntryMarkedAsMostRecentlyUsed: this.onEntryMarkedAsMostRecentlyUsed, clone: this.clone, cloneFn: this.cloneFn, ...entryOptions }); this.setNodeAsHead(node); this.lookupTable.set(key, node); this.enforceSizeLimit(); return this; } /** * Returns the value associated to the key, or null if there is none or if the entry is expired. * If an entry is returned, this marks the returned entry as the most recently used entry. * * @param key The key of the entry to get. * * @returns The cached value or null. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * // Will be 'testValue'. Entry will now be most recently used. * const item1 = cache.get('testKey'); * * // Will be null * const item2 = cache.get('keyNotInCache'); * ``` */ get(key) { const node = this.lookupTable.get(key); if (!node) { return null; } if (node.isExpired) { this.removeNodeFromListAndLookupTable(node); return null; } this.setNodeAsHead(node); return node.value; } /** * Returns the value associated to the key, or null if there is none or if the entry is expired. * If an entry is returned, this will not mark the entry as most recently accessed. * Useful if a value is needed but the order of the cache should not be changed. * * @param key The key of the entry to get. * * @returns The cached value or null. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * // Will be 'testValue' * const item1 = cache.peek('testKey'); * * // Will be null * const item2 = cache.peek('keyNotInCache'); * ``` */ peek(key) { const node = this.lookupTable.get(key); if (!node) { return null; } if (node.isExpired) { this.removeNodeFromListAndLookupTable(node); return null; } return node.value; } /** * Deletes the entry for the passed in key. * * @param key The key of the entry to delete * * @returns True if an element in the LRUCache object existed and has been removed, * or false if the element does not exist. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * // Will be true * const wasDeleted = cache.delete('testKey'); * * // Will be false * const wasDeleted2 = cache.delete('keyNotInCache'); * ``` */ delete(key) { const node = this.lookupTable.get(key); if (!node) { return false; } return this.removeNodeFromListAndLookupTable(node); } /** * Returns a boolean asserting whether a value has been associated to the key in the LRUCache object or not. * This does not mark the entry as recently used. * If the cache has a key but the entry is expired, it will be removed and false will be returned. * * @param key The key of the entry to check if exists * * @returns true if the cache contains the supplied key. False if not. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * // Will be true * const wasDeleted = cache.has('testKey'); * * // Will be false * const wasDeleted2 = cache.has('keyNotInCache'); * ``` */ has(key) { const node = this.lookupTable.get(key); if (!node) { return false; } if (node.isExpired) { this.removeNodeFromListAndLookupTable(node); return false; } return true; } /** * Removes all entries in the cache. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * // Clear cache. * cache.clear(); * ``` */ clear() { this.head = null; this.tail = null; this.lookupTable.clear(); } /** * Searches the cache for an entry matching the passed in condition. * Expired entries will be skipped (and removed). * If multiply entries in the cache match the condition, the most recently used entry will be returned. * If an entry is returned, this marks the returned entry as the most recently used entry. * * @param condition The condition to apply to each entry in the * * @returns The first cache entry to match the condition. Null if none match. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * // item will be { key: 'testKey', value: 'testValue } * const item = cache.find(entry => { * const { key, value } = entry; * * if (key === 'testKey' || value === 'something') { * return true; * } * * return false; * }); * * // item2 will be null * const item2 = cache.find(entry => entry.key === 'notInCache'); * ``` */ find(condition) { let node = this.head; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } const entry = this.mapNodeToEntry(node); if (condition(entry)) { this.setNodeAsHead(node); return entry; } node = node.next; } return null; } /** * Iterates over and applies the callback function to each entry in the cache. * Iterates in order from most recently accessed entry to least recently. * Expired entries will be skipped (and removed). * No entry will be marked as recently used. * * @param callback the callback function to apply to the entry * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * cache.forEach((key, value, index) => { * // do something with key, value, and/or index * }); * ``` */ forEach(callback) { let node = this.head; let index = 0; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } callback(node.value, node.key, index); node = node.next; index++; } } /** * Creates a Generator which can be used with for ... of ... to iterate over the cache values. * Iterates in order from most recently accessed entry to least recently. * Expired entries will be skipped (and removed). * No entry will be marked as accessed. * * @returns A Generator for the cache values. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * for (const value of cache.values()) { * // do something with the value * } * ``` */ *values() { let node = this.head; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } yield node.value; node = node.next; } } /** * Creates a Generator which can be used with for ... of ... to iterate over the cache keys. * Iterates in order from most recently accessed entry to least recently. * Expired entries will be skipped (and removed). * No entry will be marked as accessed. * * @returns A Generator for the cache keys. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * for (const key of cache.keys()) { * // do something with the key * } * ``` */ *keys() { let node = this.head; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } yield node.key; node = node.next; } } /** * Creates a Generator which can be used with for ... of ... to iterate over the cache entries. * Iterates in order from most recently accessed entry to least recently. * Expired entries will be skipped (and removed). * No entry will be marked as accessed. * * @returns A Generator for the cache entries. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * for (const entry of cache.entries()) { * const { key, value } = entry; * // do something with the entry * } * ``` */ *entries() { let node = this.head; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } yield this.mapNodeToEntry(node); node = node.next; } } /** * Creates a Generator which can be used with for ... of ... to iterate over the cache entries. * Iterates in order from most recently accessed entry to least recently. * Expired entries will be skipped (and removed). * No entry will be marked as accessed. * * @returns A Generator for the cache entries. * * @example * ```typescript * const cache = new LRUCache(); * * // Set the key testKey to value testValue * cache.set('testKey', 'testValue'); * * for (const entry of cache) { * const { key, value } = entry; * // do something with the entry * } * ``` */ *[Symbol.iterator]() { let node = this.head; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } yield this.mapNodeToEntry(node); node = node.next; } } enforceSizeLimit() { let node = this.tail; while (node !== null && this.size > this.maxSizeInternal) { const prev = node.prev; this.removeNodeFromListAndLookupTable(node); node = prev; } } mapNodeToEntry({ key, value }) { return { key, value }; } setNodeAsHead(node) { this.removeNodeFromList(node); if (!this.head) { this.head = node; this.tail = node; } else { node.next = this.head; this.head.prev = node; this.head = node; } node.invokeOnEntryMarkedAsMostRecentlyUsed(); } removeNodeFromList(node) { if (node.prev !== null) { node.prev.next = node.next; } if (node.next !== null) { node.next.prev = node.prev; } if (this.head === node) { this.head = node.next; } if (this.tail === node) { this.tail = node.prev; } node.next = null; node.prev = null; } removeNodeFromListAndLookupTable(node) { node.invokeOnEvicted(); this.removeNodeFromList(node); return this.lookupTable.delete(node.key); } cleanCache() { if (!this.entryExpirationTimeInMS) { return; } const expiredNodes = []; for (const node of this.lookupTable.values()) { if (node.isExpired) { expiredNodes.push(node); } } expiredNodes.forEach((node) => this.removeNodeFromListAndLookupTable(node)); } }; exports2.LRUCache = LRUCache7; } }); // ndk/node_modules/typescript-lru-cache/dist/index.js var require_dist = __commonJS({ "ndk/node_modules/typescript-lru-cache/dist/index.js"(exports2) { "use strict"; var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; var desc = Object.getOwnPropertyDescriptor(m, k2); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k2]; } }; } Object.defineProperty(o, k22, desc); }) : (function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; o[k22] = m[k2]; })); var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { for (var p5 in m) if (p5 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p5)) __createBinding(exports3, m, p5); }; Object.defineProperty(exports2, "__esModule", { value: true }); __exportStar(require_LRUCache(), exports2); } }); // nostr-tools-external:nostr-tools/nip49 var require_nip49 = __commonJS({ "nostr-tools-external:nostr-tools/nip49"(exports2, module2) { if (typeof window === "undefined" || !window.NostrTools) { throw new Error("NDK: nostr.bundle.js must be loaded before ndk-core.bundle.js"); } if (!window.NostrTools.nip49) { console.warn("nostr-tools/nip49 not found in window.NostrTools"); module2.exports = {}; } else { module2.exports = window.NostrTools.nip49; } } }); // ndk/node_modules/light-bolt11-decoder/node_modules/@scure/base/lib/index.js var require_lib2 = __commonJS({ "ndk/node_modules/light-bolt11-decoder/node_modules/@scure/base/lib/index.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.bytes = exports2.stringToBytes = exports2.str = exports2.bytesToString = exports2.hex = exports2.utf8 = exports2.bech32m = exports2.bech32 = exports2.base58check = exports2.base58xmr = exports2.base58xrp = exports2.base58flickr = exports2.base58 = exports2.base64url = exports2.base64 = exports2.base32crockford = exports2.base32hex = exports2.base32 = exports2.base16 = exports2.utils = exports2.assertNumber = void 0; function assertNumber2(n) { if (!Number.isSafeInteger(n)) throw new Error(`Wrong integer: ${n}`); } exports2.assertNumber = assertNumber2; function chain3(...args) { const wrap = (a, b) => (c) => a(b(c)); const encode4 = Array.from(args).reverse().reduce((acc, i3) => acc ? wrap(acc, i3.encode) : i3.encode, void 0); const decode5 = args.reduce((acc, i3) => acc ? wrap(acc, i3.decode) : i3.decode, void 0); return { encode: encode4, decode: decode5 }; } function alphabet3(alphabet4) { return { encode: (digits) => { if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number") throw new Error("alphabet.encode input should be an array of numbers"); return digits.map((i3) => { assertNumber2(i3); if (i3 < 0 || i3 >= alphabet4.length) throw new Error(`Digit index outside alphabet: ${i3} (alphabet: ${alphabet4.length})`); return alphabet4[i3]; }); }, decode: (input) => { if (!Array.isArray(input) || input.length && typeof input[0] !== "string") throw new Error("alphabet.decode input should be array of strings"); return input.map((letter) => { if (typeof letter !== "string") throw new Error(`alphabet.decode: not string element=${letter}`); const index = alphabet4.indexOf(letter); if (index === -1) throw new Error(`Unknown letter: "${letter}". Allowed: ${alphabet4}`); return index; }); } }; } function join3(separator = "") { if (typeof separator !== "string") throw new Error("join separator should be string"); return { encode: (from) => { if (!Array.isArray(from) || from.length && typeof from[0] !== "string") throw new Error("join.encode input should be array of strings"); for (let i3 of from) if (typeof i3 !== "string") throw new Error(`join.encode: non-string input=${i3}`); return from.join(separator); }, decode: (to) => { if (typeof to !== "string") throw new Error("join.decode input should be string"); return to.split(separator); } }; } function padding2(bits, chr = "=") { assertNumber2(bits); if (typeof chr !== "string") throw new Error("padding chr should be string"); return { encode(data) { if (!Array.isArray(data) || data.length && typeof data[0] !== "string") throw new Error("padding.encode input should be array of strings"); for (let i3 of data) if (typeof i3 !== "string") throw new Error(`padding.encode: non-string input=${i3}`); while (data.length * bits % 8) data.push(chr); return data; }, decode(input) { if (!Array.isArray(input) || input.length && typeof input[0] !== "string") throw new Error("padding.encode input should be array of strings"); for (let i3 of input) if (typeof i3 !== "string") throw new Error(`padding.decode: non-string input=${i3}`); let end = input.length; if (end * bits % 8) throw new Error("Invalid padding: string should have whole number of bytes"); for (; end > 0 && input[end - 1] === chr; end--) { if (!((end - 1) * bits % 8)) throw new Error("Invalid padding: string has too much padding"); } return input.slice(0, end); } }; } function normalize3(fn) { if (typeof fn !== "function") throw new Error("normalize fn should be function"); return { encode: (from) => from, decode: (to) => fn(to) }; } function convertRadix4(data, from, to) { if (from < 2) throw new Error(`convertRadix: wrong from=${from}, base cannot be less than 2`); if (to < 2) throw new Error(`convertRadix: wrong to=${to}, base cannot be less than 2`); if (!Array.isArray(data)) throw new Error("convertRadix: data should be array"); if (!data.length) return []; let pos = 0; const res = []; const digits = Array.from(data); digits.forEach((d17) => { assertNumber2(d17); if (d17 < 0 || d17 >= from) throw new Error(`Wrong integer: ${d17}`); }); while (true) { let carry = 0; let done = true; for (let i3 = pos; i3 < digits.length; i3++) { const digit = digits[i3]; const digitBase = from * carry + digit; if (!Number.isSafeInteger(digitBase) || from * carry / from !== carry || digitBase - digit !== from * carry) { throw new Error("convertRadix: carry overflow"); } carry = digitBase % to; digits[i3] = Math.floor(digitBase / to); if (!Number.isSafeInteger(digits[i3]) || digits[i3] * to + carry !== digitBase) throw new Error("convertRadix: carry overflow"); if (!done) continue; else if (!digits[i3]) pos = i3; else done = false; } res.push(carry); if (done) break; } for (let i3 = 0; i3 < data.length - 1 && data[i3] === 0; i3++) res.push(0); return res.reverse(); } var gcd3 = (a, b) => !b ? a : gcd3(b, a % b); var radix2carry3 = (from, to) => from + (to - gcd3(from, to)); function convertRadix23(data, from, to, padding3) { if (!Array.isArray(data)) throw new Error("convertRadix2: data should be array"); if (from <= 0 || from > 32) throw new Error(`convertRadix2: wrong from=${from}`); if (to <= 0 || to > 32) throw new Error(`convertRadix2: wrong to=${to}`); if (radix2carry3(from, to) > 32) { throw new Error(`convertRadix2: carry overflow from=${from} to=${to} carryBits=${radix2carry3(from, to)}`); } let carry = 0; let pos = 0; const mask = 2 ** to - 1; const res = []; for (const n of data) { assertNumber2(n); if (n >= 2 ** from) throw new Error(`convertRadix2: invalid data word=${n} from=${from}`); carry = carry << from | n; if (pos + from > 32) throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from}`); pos += from; for (; pos >= to; pos -= to) res.push((carry >> pos - to & mask) >>> 0); carry &= 2 ** pos - 1; } carry = carry << to - pos & mask; if (!padding3 && pos >= from) throw new Error("Excess padding"); if (!padding3 && carry) throw new Error(`Non-zero padding: ${carry}`); if (padding3 && pos > 0) res.push(carry >>> 0); return res; } function radix4(num3) { assertNumber2(num3); return { encode: (bytes4) => { if (!(bytes4 instanceof Uint8Array)) throw new Error("radix.encode input should be Uint8Array"); return convertRadix4(Array.from(bytes4), 2 ** 8, num3); }, decode: (digits) => { if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number") throw new Error("radix.decode input should be array of strings"); return Uint8Array.from(convertRadix4(digits, num3, 2 ** 8)); } }; } function radix23(bits, revPadding = false) { assertNumber2(bits); if (bits <= 0 || bits > 32) throw new Error("radix2: bits should be in (0..32]"); if (radix2carry3(8, bits) > 32 || radix2carry3(bits, 8) > 32) throw new Error("radix2: carry overflow"); return { encode: (bytes4) => { if (!(bytes4 instanceof Uint8Array)) throw new Error("radix2.encode input should be Uint8Array"); return convertRadix23(Array.from(bytes4), 8, bits, !revPadding); }, decode: (digits) => { if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number") throw new Error("radix2.decode input should be array of strings"); return Uint8Array.from(convertRadix23(digits, bits, 8, revPadding)); } }; } function unsafeWrapper3(fn) { if (typeof fn !== "function") throw new Error("unsafeWrapper fn should be function"); return function(...args) { try { return fn.apply(null, args); } catch (e2) { } }; } function checksum2(len, fn) { assertNumber2(len); if (typeof fn !== "function") throw new Error("checksum fn should be function"); return { encode(data) { if (!(data instanceof Uint8Array)) throw new Error("checksum.encode: input should be Uint8Array"); const checksum3 = fn(data).slice(0, len); const res = new Uint8Array(data.length + len); res.set(data); res.set(checksum3, data.length); return res; }, decode(data) { if (!(data instanceof Uint8Array)) throw new Error("checksum.decode: input should be Uint8Array"); const payload = data.slice(0, -len); const newChecksum = fn(payload).slice(0, len); const oldChecksum = data.slice(-len); for (let i3 = 0; i3 < len; i3++) if (newChecksum[i3] !== oldChecksum[i3]) throw new Error("Invalid checksum"); return payload; } }; } exports2.utils = { alphabet: alphabet3, chain: chain3, checksum: checksum2, radix: radix4, radix2: radix23, join: join3, padding: padding2 }; exports2.base16 = chain3(radix23(4), alphabet3("0123456789ABCDEF"), join3("")); exports2.base32 = chain3(radix23(5), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding2(5), join3("")); exports2.base32hex = chain3(radix23(5), alphabet3("0123456789ABCDEFGHIJKLMNOPQRSTUV"), padding2(5), join3("")); exports2.base32crockford = chain3(radix23(5), alphabet3("0123456789ABCDEFGHJKMNPQRSTVWXYZ"), join3(""), normalize3((s) => s.toUpperCase().replace(/O/g, "0").replace(/[IL]/g, "1"))); exports2.base64 = chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), padding2(6), join3("")); exports2.base64url = chain3(radix23(6), alphabet3("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), padding2(6), join3("")); var genBase583 = (abc) => chain3(radix4(58), alphabet3(abc), join3("")); exports2.base58 = genBase583("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"); exports2.base58flickr = genBase583("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"); exports2.base58xrp = genBase583("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"); var XMR_BLOCK_LEN2 = [0, 2, 3, 5, 6, 7, 9, 10, 11]; exports2.base58xmr = { encode(data) { let res = ""; for (let i3 = 0; i3 < data.length; i3 += 8) { const block = data.subarray(i3, i3 + 8); res += exports2.base58.encode(block).padStart(XMR_BLOCK_LEN2[block.length], "1"); } return res; }, decode(str) { let res = []; for (let i3 = 0; i3 < str.length; i3 += 11) { const slice = str.slice(i3, i3 + 11); const blockLen = XMR_BLOCK_LEN2.indexOf(slice.length); const block = exports2.base58.decode(slice); for (let j2 = 0; j2 < block.length - blockLen; j2++) { if (block[j2] !== 0) throw new Error("base58xmr: wrong padding"); } res = res.concat(Array.from(block.slice(block.length - blockLen))); } return Uint8Array.from(res); } }; var base58check2 = (sha2565) => chain3(checksum2(4, (data) => sha2565(sha2565(data))), exports2.base58); exports2.base58check = base58check2; var BECH_ALPHABET3 = chain3(alphabet3("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), join3("")); var POLYMOD_GENERATORS3 = [996825010, 642813549, 513874426, 1027748829, 705979059]; function bech32Polymod3(pre) { const b = pre >> 25; let chk = (pre & 33554431) << 5; for (let i3 = 0; i3 < POLYMOD_GENERATORS3.length; i3++) { if ((b >> i3 & 1) === 1) chk ^= POLYMOD_GENERATORS3[i3]; } return chk; } function bechChecksum3(prefix, words, encodingConst = 1) { const len = prefix.length; let chk = 1; for (let i3 = 0; i3 < len; i3++) { const c = prefix.charCodeAt(i3); if (c < 33 || c > 126) throw new Error(`Invalid prefix (${prefix})`); chk = bech32Polymod3(chk) ^ c >> 5; } chk = bech32Polymod3(chk); for (let i3 = 0; i3 < len; i3++) chk = bech32Polymod3(chk) ^ prefix.charCodeAt(i3) & 31; for (let v6 of words) chk = bech32Polymod3(chk) ^ v6; for (let i3 = 0; i3 < 6; i3++) chk = bech32Polymod3(chk); chk ^= encodingConst; return BECH_ALPHABET3.encode(convertRadix23([chk % 2 ** 30], 30, 5, false)); } function genBech323(encoding) { const ENCODING_CONST = encoding === "bech32" ? 1 : 734539939; const _words = radix23(5); const fromWords = _words.decode; const toWords = _words.encode; const fromWordsUnsafe = unsafeWrapper3(fromWords); function encode4(prefix, words, limit2 = 90) { if (typeof prefix !== "string") throw new Error(`bech32.encode prefix should be string, not ${typeof prefix}`); if (!Array.isArray(words) || words.length && typeof words[0] !== "number") throw new Error(`bech32.encode words should be array of numbers, not ${typeof words}`); const actualLength = prefix.length + 7 + words.length; if (limit2 !== false && actualLength > limit2) throw new TypeError(`Length ${actualLength} exceeds limit ${limit2}`); prefix = prefix.toLowerCase(); return `${prefix}1${BECH_ALPHABET3.encode(words)}${bechChecksum3(prefix, words, ENCODING_CONST)}`; } function decode5(str, limit2 = 90) { if (typeof str !== "string") throw new Error(`bech32.decode input should be string, not ${typeof str}`); if (str.length < 8 || limit2 !== false && str.length > limit2) throw new TypeError(`Wrong string length: ${str.length} (${str}). Expected (8..${limit2})`); const lowered = str.toLowerCase(); if (str !== lowered && str !== str.toUpperCase()) throw new Error(`String must be lowercase or uppercase`); str = lowered; const sepIndex = str.lastIndexOf("1"); if (sepIndex === 0 || sepIndex === -1) throw new Error(`Letter "1" must be present between prefix and data only`); const prefix = str.slice(0, sepIndex); const _words2 = str.slice(sepIndex + 1); if (_words2.length < 6) throw new Error("Data must be at least 6 characters long"); const words = BECH_ALPHABET3.decode(_words2).slice(0, -6); const sum = bechChecksum3(prefix, words, ENCODING_CONST); if (!_words2.endsWith(sum)) throw new Error(`Invalid checksum in ${str}: expected "${sum}"`); return { prefix, words }; } const decodeUnsafe = unsafeWrapper3(decode5); function decodeToBytes(str) { const { prefix, words } = decode5(str, false); return { prefix, words, bytes: fromWords(words) }; } return { encode: encode4, decode: decode5, decodeToBytes, decodeUnsafe, fromWords, fromWordsUnsafe, toWords }; } exports2.bech32 = genBech323("bech32"); exports2.bech32m = genBech323("bech32m"); exports2.utf8 = { encode: (data) => new TextDecoder().decode(data), decode: (str) => new TextEncoder().encode(str) }; exports2.hex = chain3(radix23(4), alphabet3("0123456789abcdef"), join3(""), normalize3((s) => { if (typeof s !== "string" || s.length % 2) throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`); return s.toLowerCase(); })); var CODERS2 = { utf8: exports2.utf8, hex: exports2.hex, base16: exports2.base16, base32: exports2.base32, base64: exports2.base64, base64url: exports2.base64url, base58: exports2.base58, base58xmr: exports2.base58xmr }; var coderTypeError2 = `Invalid encoding type. Available types: ${Object.keys(CODERS2).join(", ")}`; var bytesToString = (type, bytes4) => { if (typeof type !== "string" || !CODERS2.hasOwnProperty(type)) throw new TypeError(coderTypeError2); if (!(bytes4 instanceof Uint8Array)) throw new TypeError("bytesToString() expects Uint8Array"); return CODERS2[type].encode(bytes4); }; exports2.bytesToString = bytesToString; exports2.str = exports2.bytesToString; var stringToBytes = (type, str) => { if (!CODERS2.hasOwnProperty(type)) throw new TypeError(coderTypeError2); if (typeof str !== "string") throw new TypeError("stringToBytes() expects string"); return CODERS2[type].decode(str); }; exports2.stringToBytes = stringToBytes; exports2.bytes = exports2.stringToBytes; } }); // ndk/node_modules/light-bolt11-decoder/bolt11.js var require_bolt11 = __commonJS({ "ndk/node_modules/light-bolt11-decoder/bolt11.js"(exports2, module2) { var { bech32: bech323, hex: hex2, utf8: utf82 } = require_lib2(); var DEFAULTNETWORK = { // default network is bitcoin bech32: "bc", pubKeyHash: 0, scriptHash: 5, validWitnessVersions: [0] }; var TESTNETWORK = { bech32: "tb", pubKeyHash: 111, scriptHash: 196, validWitnessVersions: [0] }; var SIGNETNETWORK = { bech32: "tbs", pubKeyHash: 111, scriptHash: 196, validWitnessVersions: [0] }; var REGTESTNETWORK = { bech32: "bcrt", pubKeyHash: 111, scriptHash: 196, validWitnessVersions: [0] }; var SIMNETWORK = { bech32: "sb", pubKeyHash: 63, scriptHash: 123, validWitnessVersions: [0] }; var FEATUREBIT_ORDER = [ "option_data_loss_protect", "initial_routing_sync", "option_upfront_shutdown_script", "gossip_queries", "var_onion_optin", "gossip_queries_ex", "option_static_remotekey", "payment_secret", "basic_mpp", "option_support_large_channel" ]; var DIVISORS = { m: BigInt(1e3), u: BigInt(1e6), n: BigInt(1e9), p: BigInt(1e12) }; var MAX_MILLISATS = BigInt("2100000000000000000"); var MILLISATS_PER_BTC = BigInt(1e11); var TAGCODES = { payment_hash: 1, payment_secret: 16, description: 13, payee: 19, description_hash: 23, // commit to longer descriptions (used by lnurl-pay) expiry: 6, // default: 3600 (1 hour) min_final_cltv_expiry: 24, // default: 9 fallback_address: 9, route_hint: 3, // for extra routing info (private etc.) feature_bits: 5, metadata: 27 }; var TAGNAMES = {}; for (let i3 = 0, keys = Object.keys(TAGCODES); i3 < keys.length; i3++) { const currentName = keys[i3]; const currentCode = TAGCODES[keys[i3]].toString(); TAGNAMES[currentCode] = currentName; } var TAGPARSERS = { 1: (words) => hex2.encode(bech323.fromWordsUnsafe(words)), // 256 bits 16: (words) => hex2.encode(bech323.fromWordsUnsafe(words)), // 256 bits 13: (words) => utf82.encode(bech323.fromWordsUnsafe(words)), // string variable length 19: (words) => hex2.encode(bech323.fromWordsUnsafe(words)), // 264 bits 23: (words) => hex2.encode(bech323.fromWordsUnsafe(words)), // 256 bits 27: (words) => hex2.encode(bech323.fromWordsUnsafe(words)), // variable 6: wordsToIntBE, // default: 3600 (1 hour) 24: wordsToIntBE, // default: 9 3: routingInfoParser, // for extra routing info (private etc.) 5: featureBitsParser // keep feature bits as array of 5 bit words }; function getUnknownParser(tagCode) { return (words) => ({ tagCode: parseInt(tagCode), words: bech323.encode("unknown", words, Number.MAX_SAFE_INTEGER) }); } function wordsToIntBE(words) { return words.reverse().reduce((total, item, index) => { return total + item * Math.pow(32, index); }, 0); } function routingInfoParser(words) { const routes = []; let pubkey, shortChannelId, feeBaseMSats, feeProportionalMillionths, cltvExpiryDelta; let routesBuffer = bech323.fromWordsUnsafe(words); while (routesBuffer.length > 0) { pubkey = hex2.encode(routesBuffer.slice(0, 33)); shortChannelId = hex2.encode(routesBuffer.slice(33, 41)); feeBaseMSats = parseInt(hex2.encode(routesBuffer.slice(41, 45)), 16); feeProportionalMillionths = parseInt( hex2.encode(routesBuffer.slice(45, 49)), 16 ); cltvExpiryDelta = parseInt(hex2.encode(routesBuffer.slice(49, 51)), 16); routesBuffer = routesBuffer.slice(51); routes.push({ pubkey, short_channel_id: shortChannelId, fee_base_msat: feeBaseMSats, fee_proportional_millionths: feeProportionalMillionths, cltv_expiry_delta: cltvExpiryDelta }); } return routes; } function featureBitsParser(words) { const bools = words.slice().reverse().map((word) => [ !!(word & 1), !!(word & 2), !!(word & 4), !!(word & 8), !!(word & 16) ]).reduce((finalArr, itemArr) => finalArr.concat(itemArr), []); while (bools.length < FEATUREBIT_ORDER.length * 2) { bools.push(false); } const featureBits = {}; FEATUREBIT_ORDER.forEach((featureName, index) => { let status; if (bools[index * 2]) { status = "required"; } else if (bools[index * 2 + 1]) { status = "supported"; } else { status = "unsupported"; } featureBits[featureName] = status; }); const extraBits = bools.slice(FEATUREBIT_ORDER.length * 2); featureBits.extra_bits = { start_bit: FEATUREBIT_ORDER.length * 2, bits: extraBits, has_required: extraBits.reduce( (result, bit, index) => index % 2 !== 0 ? result || false : result || bit, false ) }; return featureBits; } function hrpToMillisat(hrpString, outputString) { let divisor, value; if (hrpString.slice(-1).match(/^[munp]$/)) { divisor = hrpString.slice(-1); value = hrpString.slice(0, -1); } else if (hrpString.slice(-1).match(/^[^munp0-9]$/)) { throw new Error("Not a valid multiplier for the amount"); } else { value = hrpString; } if (!value.match(/^\d+$/)) throw new Error("Not a valid human readable amount"); const valueBN = BigInt(value); const millisatoshisBN = divisor ? valueBN * MILLISATS_PER_BTC / DIVISORS[divisor] : valueBN * MILLISATS_PER_BTC; if (divisor === "p" && !(valueBN % BigInt(10) === BigInt(0)) || millisatoshisBN > MAX_MILLISATS) { throw new Error("Amount is outside of valid range"); } return outputString ? millisatoshisBN.toString() : millisatoshisBN; } function decode5(paymentRequest, network) { if (typeof paymentRequest !== "string") throw new Error("Lightning Payment Request must be string"); if (paymentRequest.slice(0, 2).toLowerCase() !== "ln") throw new Error("Not a proper lightning payment request"); const sections = []; const decoded = bech323.decode(paymentRequest, Number.MAX_SAFE_INTEGER); paymentRequest = paymentRequest.toLowerCase(); const prefix = decoded.prefix; let words = decoded.words; let letters = paymentRequest.slice(prefix.length + 1); let sigWords = words.slice(-104); words = words.slice(0, -104); let prefixMatches = prefix.match(/^ln(\S+?)(\d*)([a-zA-Z]?)$/); if (prefixMatches && !prefixMatches[2]) prefixMatches = prefix.match(/^ln(\S+)$/); if (!prefixMatches) { throw new Error("Not a proper lightning payment request"); } sections.push({ name: "lightning_network", letters: "ln" }); const bech32Prefix = prefixMatches[1]; let coinNetwork; if (!network) { switch (bech32Prefix) { case DEFAULTNETWORK.bech32: coinNetwork = DEFAULTNETWORK; break; case TESTNETWORK.bech32: coinNetwork = TESTNETWORK; break; case SIGNETNETWORK.bech32: coinNetwork = SIGNETNETWORK; break; case REGTESTNETWORK.bech32: coinNetwork = REGTESTNETWORK; break; case SIMNETWORK.bech32: coinNetwork = SIMNETWORK; break; } } else { if (network.bech32 === void 0 || network.pubKeyHash === void 0 || network.scriptHash === void 0 || !Array.isArray(network.validWitnessVersions)) throw new Error("Invalid network"); coinNetwork = network; } if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix) { throw new Error("Unknown coin bech32 prefix"); } sections.push({ name: "coin_network", letters: bech32Prefix, value: coinNetwork }); const value = prefixMatches[2]; let millisatoshis; if (value) { const divisor = prefixMatches[3]; millisatoshis = hrpToMillisat(value + divisor, true); sections.push({ name: "amount", letters: prefixMatches[2] + prefixMatches[3], value: millisatoshis }); } else { millisatoshis = null; } sections.push({ name: "separator", letters: "1" }); const timestamp = wordsToIntBE(words.slice(0, 7)); words = words.slice(7); sections.push({ name: "timestamp", letters: letters.slice(0, 7), value: timestamp }); letters = letters.slice(7); let tagName, parser, tagLength, tagWords; while (words.length > 0) { const tagCode = words[0].toString(); tagName = TAGNAMES[tagCode] || "unknown_tag"; parser = TAGPARSERS[tagCode] || getUnknownParser(tagCode); words = words.slice(1); tagLength = wordsToIntBE(words.slice(0, 2)); words = words.slice(2); tagWords = words.slice(0, tagLength); words = words.slice(tagLength); sections.push({ name: tagName, tag: letters[0], letters: letters.slice(0, 1 + 2 + tagLength), value: parser(tagWords) // see: parsers for more comments }); letters = letters.slice(1 + 2 + tagLength); } sections.push({ name: "signature", letters: letters.slice(0, 104), value: hex2.encode(bech323.fromWordsUnsafe(sigWords)) }); letters = letters.slice(104); sections.push({ name: "checksum", letters }); let result = { paymentRequest, sections, get expiry() { let exp = sections.find((s) => s.name === "expiry"); if (exp) return getValue("timestamp") + exp.value; }, get route_hints() { return sections.filter((s) => s.name === "route_hint").map((s) => s.value); } }; for (let name in TAGCODES) { if (name === "route_hint") { continue; } Object.defineProperty(result, name, { get() { return getValue(name); } }); } return result; function getValue(name) { let section = sections.find((s) => s.name === name); return section ? section.value : void 0; } } module2.exports = { decode: decode5, hrpToMillisat }; } }); // nostr-tools-external:nostr-tools/nip19 var require_nip19 = __commonJS({ "nostr-tools-external:nostr-tools/nip19"(exports2, module2) { if (typeof window === "undefined" || !window.NostrTools) { throw new Error("NDK: nostr.bundle.js must be loaded before ndk-core.bundle.js"); } if (!window.NostrTools.nip19) { console.warn("nostr-tools/nip19 not found in window.NostrTools"); module2.exports = {}; } else { module2.exports = window.NostrTools.nip19; } } }); // ndk/node_modules/@scure/base/lib/esm/index.js function isBytes2(a) { return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array"; } function isArrayOf(isString, arr) { if (!Array.isArray(arr)) return false; if (arr.length === 0) return true; if (isString) { return arr.every((item) => typeof item === "string"); } else { return arr.every((item) => Number.isSafeInteger(item)); } } function afn(input) { if (typeof input !== "function") throw new Error("function expected"); return true; } function astr(label, input) { if (typeof input !== "string") throw new Error(`${label}: string expected`); return true; } function anumber2(n) { if (!Number.isSafeInteger(n)) throw new Error(`invalid integer: ${n}`); } function aArr(input) { if (!Array.isArray(input)) throw new Error("array expected"); } function astrArr(label, input) { if (!isArrayOf(true, input)) throw new Error(`${label}: array of strings expected`); } function anumArr(label, input) { if (!isArrayOf(false, input)) throw new Error(`${label}: array of numbers expected`); } // @__NO_SIDE_EFFECTS__ function chain(...args) { const id = (a) => a; const wrap = (a, b) => (c) => a(b(c)); const encode4 = args.map((x2) => x2.encode).reduceRight(wrap, id); const decode5 = args.map((x2) => x2.decode).reduce(wrap, id); return { encode: encode4, decode: decode5 }; } // @__NO_SIDE_EFFECTS__ function alphabet(letters) { const lettersA = typeof letters === "string" ? letters.split("") : letters; const len = lettersA.length; astrArr("alphabet", lettersA); const indexes = new Map(lettersA.map((l3, i3) => [l3, i3])); return { encode: (digits) => { aArr(digits); return digits.map((i3) => { if (!Number.isSafeInteger(i3) || i3 < 0 || i3 >= len) throw new Error(`alphabet.encode: digit index outside alphabet "${i3}". Allowed: ${letters}`); return lettersA[i3]; }); }, decode: (input) => { aArr(input); return input.map((letter) => { astr("alphabet.decode", letter); const i3 = indexes.get(letter); if (i3 === void 0) throw new Error(`Unknown letter: "${letter}". Allowed: ${letters}`); return i3; }); } }; } // @__NO_SIDE_EFFECTS__ function join(separator = "") { astr("join", separator); return { encode: (from) => { astrArr("join.decode", from); return from.join(separator); }, decode: (to) => { astr("join.decode", to); return to.split(separator); } }; } function convertRadix(data, from, to) { if (from < 2) throw new Error(`convertRadix: invalid from=${from}, base cannot be less than 2`); if (to < 2) throw new Error(`convertRadix: invalid to=${to}, base cannot be less than 2`); aArr(data); if (!data.length) return []; let pos = 0; const res = []; const digits = Array.from(data, (d17) => { anumber2(d17); if (d17 < 0 || d17 >= from) throw new Error(`invalid integer: ${d17}`); return d17; }); const dlen = digits.length; while (true) { let carry = 0; let done = true; for (let i3 = pos; i3 < dlen; i3++) { const digit = digits[i3]; const fromCarry = from * carry; const digitBase = fromCarry + digit; if (!Number.isSafeInteger(digitBase) || fromCarry / from !== carry || digitBase - digit !== fromCarry) { throw new Error("convertRadix: carry overflow"); } const div = digitBase / to; carry = digitBase % to; const rounded = Math.floor(div); digits[i3] = rounded; if (!Number.isSafeInteger(rounded) || rounded * to + carry !== digitBase) throw new Error("convertRadix: carry overflow"); if (!done) continue; else if (!rounded) pos = i3; else done = false; } res.push(carry); if (done) break; } for (let i3 = 0; i3 < data.length - 1 && data[i3] === 0; i3++) res.push(0); return res.reverse(); } function convertRadix2(data, from, to, padding2) { aArr(data); if (from <= 0 || from > 32) throw new Error(`convertRadix2: wrong from=${from}`); if (to <= 0 || to > 32) throw new Error(`convertRadix2: wrong to=${to}`); if (/* @__PURE__ */ radix2carry(from, to) > 32) { throw new Error(`convertRadix2: carry overflow from=${from} to=${to} carryBits=${/* @__PURE__ */ radix2carry(from, to)}`); } let carry = 0; let pos = 0; const max = powers[from]; const mask = powers[to] - 1; const res = []; for (const n of data) { anumber2(n); if (n >= max) throw new Error(`convertRadix2: invalid data word=${n} from=${from}`); carry = carry << from | n; if (pos + from > 32) throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from}`); pos += from; for (; pos >= to; pos -= to) res.push((carry >> pos - to & mask) >>> 0); const pow3 = powers[pos]; if (pow3 === void 0) throw new Error("invalid carry"); carry &= pow3 - 1; } carry = carry << to - pos & mask; if (!padding2 && pos >= from) throw new Error("Excess padding"); if (!padding2 && carry > 0) throw new Error(`Non-zero padding: ${carry}`); if (padding2 && pos > 0) res.push(carry >>> 0); return res; } // @__NO_SIDE_EFFECTS__ function radix(num3) { anumber2(num3); const _256 = 2 ** 8; return { encode: (bytes4) => { if (!isBytes2(bytes4)) throw new Error("radix.encode input should be Uint8Array"); return convertRadix(Array.from(bytes4), _256, num3); }, decode: (digits) => { anumArr("radix.decode", digits); return Uint8Array.from(convertRadix(digits, num3, _256)); } }; } // @__NO_SIDE_EFFECTS__ function radix2(bits, revPadding = false) { anumber2(bits); if (bits <= 0 || bits > 32) throw new Error("radix2: bits should be in (0..32]"); if (/* @__PURE__ */ radix2carry(8, bits) > 32 || /* @__PURE__ */ radix2carry(bits, 8) > 32) throw new Error("radix2: carry overflow"); return { encode: (bytes4) => { if (!isBytes2(bytes4)) throw new Error("radix2.encode input should be Uint8Array"); return convertRadix2(Array.from(bytes4), 8, bits, !revPadding); }, decode: (digits) => { anumArr("radix2.decode", digits); return Uint8Array.from(convertRadix2(digits, bits, 8, revPadding)); } }; } function unsafeWrapper(fn) { afn(fn); return function(...args) { try { return fn.apply(null, args); } catch (e2) { } }; } function checksum(len, fn) { anumber2(len); afn(fn); return { encode(data) { if (!isBytes2(data)) throw new Error("checksum.encode: input should be Uint8Array"); const sum = fn(data).slice(0, len); const res = new Uint8Array(data.length + len); res.set(data); res.set(sum, data.length); return res; }, decode(data) { if (!isBytes2(data)) throw new Error("checksum.decode: input should be Uint8Array"); const payload = data.slice(0, -len); const oldChecksum = data.slice(-len); const newChecksum = fn(payload).slice(0, len); for (let i3 = 0; i3 < len; i3++) if (newChecksum[i3] !== oldChecksum[i3]) throw new Error("Invalid checksum"); return payload; } }; } function bech32Polymod(pre) { const b = pre >> 25; let chk = (pre & 33554431) << 5; for (let i3 = 0; i3 < POLYMOD_GENERATORS.length; i3++) { if ((b >> i3 & 1) === 1) chk ^= POLYMOD_GENERATORS[i3]; } return chk; } function bechChecksum(prefix, words, encodingConst = 1) { const len = prefix.length; let chk = 1; for (let i3 = 0; i3 < len; i3++) { const c = prefix.charCodeAt(i3); if (c < 33 || c > 126) throw new Error(`Invalid prefix (${prefix})`); chk = bech32Polymod(chk) ^ c >> 5; } chk = bech32Polymod(chk); for (let i3 = 0; i3 < len; i3++) chk = bech32Polymod(chk) ^ prefix.charCodeAt(i3) & 31; for (let v6 of words) chk = bech32Polymod(chk) ^ v6; for (let i3 = 0; i3 < 6; i3++) chk = bech32Polymod(chk); chk ^= encodingConst; return BECH_ALPHABET.encode(convertRadix2([chk % powers[30]], 30, 5, false)); } // @__NO_SIDE_EFFECTS__ function genBech32(encoding) { const ENCODING_CONST = encoding === "bech32" ? 1 : 734539939; const _words = /* @__PURE__ */ radix2(5); const fromWords = _words.decode; const toWords = _words.encode; const fromWordsUnsafe = unsafeWrapper(fromWords); function encode4(prefix, words, limit2 = 90) { astr("bech32.encode prefix", prefix); if (isBytes2(words)) words = Array.from(words); anumArr("bech32.encode", words); const plen = prefix.length; if (plen === 0) throw new TypeError(`Invalid prefix length ${plen}`); const actualLength = plen + 7 + words.length; if (limit2 !== false && actualLength > limit2) throw new TypeError(`Length ${actualLength} exceeds limit ${limit2}`); const lowered = prefix.toLowerCase(); const sum = bechChecksum(lowered, words, ENCODING_CONST); return `${lowered}1${BECH_ALPHABET.encode(words)}${sum}`; } function decode5(str, limit2 = 90) { astr("bech32.decode input", str); const slen = str.length; if (slen < 8 || limit2 !== false && slen > limit2) throw new TypeError(`invalid string length: ${slen} (${str}). Expected (8..${limit2})`); const lowered = str.toLowerCase(); if (str !== lowered && str !== str.toUpperCase()) throw new Error(`String must be lowercase or uppercase`); const sepIndex = lowered.lastIndexOf("1"); if (sepIndex === 0 || sepIndex === -1) throw new Error(`Letter "1" must be present between prefix and data only`); const prefix = lowered.slice(0, sepIndex); const data = lowered.slice(sepIndex + 1); if (data.length < 6) throw new Error("Data must be at least 6 characters long"); const words = BECH_ALPHABET.decode(data).slice(0, -6); const sum = bechChecksum(prefix, words, ENCODING_CONST); if (!data.endsWith(sum)) throw new Error(`Invalid checksum in ${str}: expected "${sum}"`); return { prefix, words }; } const decodeUnsafe = unsafeWrapper(decode5); function decodeToBytes(str) { const { prefix, words } = decode5(str, false); return { prefix, words, bytes: fromWords(words) }; } function encodeFromBytes(prefix, bytes4) { return encode4(prefix, toWords(bytes4)); } return { encode: encode4, decode: decode5, encodeFromBytes, decodeToBytes, decodeUnsafe, fromWords, fromWordsUnsafe, toWords }; } var gcd, radix2carry, powers, genBase58, base58, createBase58check, BECH_ALPHABET, POLYMOD_GENERATORS, bech32; var init_esm = __esm({ "ndk/node_modules/@scure/base/lib/esm/index.js"() { gcd = (a, b) => b === 0 ? a : gcd(b, a % b); radix2carry = /* @__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to)); powers = /* @__PURE__ */ (() => { let res = []; for (let i3 = 0; i3 < 40; i3++) res.push(2 ** i3); return res; })(); genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => /* @__PURE__ */ chain(/* @__PURE__ */ radix(58), /* @__PURE__ */ alphabet(abc), /* @__PURE__ */ join("")); base58 = /* @__PURE__ */ genBase58("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"); createBase58check = (sha2565) => /* @__PURE__ */ chain(checksum(4, (data) => sha2565(sha2565(data))), base58); BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join("")); POLYMOD_GENERATORS = [996825010, 642813549, 513874426, 1027748829, 705979059]; bech32 = /* @__PURE__ */ genBech32("bech32"); } }); // ndk/core/dist/index.mjs function getRelaysForSync2(ndk, author, type = "write") { if (!ndk.outboxTracker) return void 0; const item = ndk.outboxTracker.data.get(author); if (!item) return void 0; if (type === "write") { return item.writeRelays; } return item.readRelays; } async function getWriteRelaysFor2(ndk, author, type = "write") { if (!ndk.outboxTracker) return void 0; if (!ndk.outboxTracker.data.has(author)) { await ndk.outboxTracker.trackUsers([author]); } return getRelaysForSync2(ndk, author, type); } function getTopRelaysForAuthors2(ndk, authors) { const relaysWithCount = /* @__PURE__ */ new Map(); authors.forEach((author) => { const writeRelays = getRelaysForSync2(ndk, author); if (writeRelays) { writeRelays.forEach((relay) => { const count = relaysWithCount.get(relay) || 0; relaysWithCount.set(relay, count + 1); }); } }); const sortedRelays = Array.from(relaysWithCount.entries()).sort((a, b) => b[1] - a[1]); return sortedRelays.map((entry) => entry[0]); } function getAllRelaysForAllPubkeys3(ndk, pubkeys, type = "read") { const pubkeysToRelays = /* @__PURE__ */ new Map(); const authorsMissingRelays = /* @__PURE__ */ new Set(); pubkeys.forEach((pubkey) => { const relays = getRelaysForSync2(ndk, pubkey, type); if (relays && relays.size > 0) { relays.forEach((relay) => { const pubkeysInRelay = pubkeysToRelays.get(relay) || /* @__PURE__ */ new Set(); pubkeysInRelay.add(pubkey); }); pubkeysToRelays.set(pubkey, relays); } else { authorsMissingRelays.add(pubkey); } }); return { pubkeysToRelays, authorsMissingRelays }; } function chooseRelayCombinationForPubkeys2(ndk, pubkeys, type, { count, preferredRelays } = {}) { count ?? (count = 2); preferredRelays ?? (preferredRelays = /* @__PURE__ */ new Set()); const pool = ndk.pool; const connectedRelays = pool.connectedRelays(); connectedRelays.forEach((relay) => { preferredRelays?.add(relay.url); }); const relayToAuthorsMap = /* @__PURE__ */ new Map(); const { pubkeysToRelays, authorsMissingRelays } = getAllRelaysForAllPubkeys3(ndk, pubkeys, type); const sortedRelays = getTopRelaysForAuthors2(ndk, pubkeys); const addAuthorToRelay = (author, relay) => { const authorsInRelay = relayToAuthorsMap.get(relay) || []; authorsInRelay.push(author); relayToAuthorsMap.set(relay, authorsInRelay); }; for (const [author, authorRelays] of pubkeysToRelays.entries()) { let missingRelayCount = count; const addedRelaysForAuthor = /* @__PURE__ */ new Set(); for (const relay of connectedRelays) { if (authorRelays.has(relay.url)) { addAuthorToRelay(author, relay.url); addedRelaysForAuthor.add(relay.url); missingRelayCount--; } } for (const authorRelay of authorRelays) { if (addedRelaysForAuthor.has(authorRelay)) continue; if (relayToAuthorsMap.has(authorRelay)) { addAuthorToRelay(author, authorRelay); addedRelaysForAuthor.add(authorRelay); missingRelayCount--; } } if (missingRelayCount <= 0) continue; for (const relay of sortedRelays) { if (missingRelayCount <= 0) break; if (addedRelaysForAuthor.has(relay)) continue; if (authorRelays.has(relay)) { addAuthorToRelay(author, relay); addedRelaysForAuthor.add(relay); missingRelayCount--; } } } for (const author of authorsMissingRelays) { pool.permanentAndConnectedRelays().forEach((relay) => { const authorsInRelay = relayToAuthorsMap.get(relay.url) || []; authorsInRelay.push(author); relayToAuthorsMap.set(relay.url, authorsInRelay); }); } return relayToAuthorsMap; } function tryNormalizeRelayUrl2(url) { try { return normalizeRelayUrl2(url); } catch { return void 0; } } function normalizeRelayUrl2(url) { let r = normalizeUrl2(url, { stripAuthentication: false, stripWWW: false, stripHash: true }); if (!r.endsWith("/")) { r += "/"; } return r; } function normalizeUrl2(urlString, options = {}) { options = { defaultProtocol: "http", normalizeProtocol: true, forceHttp: false, forceHttps: false, stripAuthentication: true, stripHash: false, stripTextFragment: true, stripWWW: true, removeQueryParameters: [/^utm_\w+/i], removeTrailingSlash: true, removeSingleSlash: true, removeDirectoryIndex: false, removeExplicitPort: false, sortQueryParameters: true, ...options }; if (typeof options.defaultProtocol === "string" && !options.defaultProtocol.endsWith(":")) { options.defaultProtocol = `${options.defaultProtocol}:`; } urlString = urlString.trim(); if (/^data:/i.test(urlString)) { return normalizeDataURL2(urlString, options); } if (hasCustomProtocol2(urlString)) { return urlString; } const hasRelativeProtocol = urlString.startsWith("//"); const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString); if (!isRelativeUrl) { urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol); } const urlObject = new URL(urlString); urlObject.hostname = urlObject.hostname.toLowerCase(); if (options.forceHttp && options.forceHttps) { throw new Error("The `forceHttp` and `forceHttps` options cannot be used together"); } if (options.forceHttp && urlObject.protocol === "https:") { urlObject.protocol = "http:"; } if (options.forceHttps && urlObject.protocol === "http:") { urlObject.protocol = "https:"; } if (options.stripAuthentication) { urlObject.username = ""; urlObject.password = ""; } if (options.stripHash) { urlObject.hash = ""; } else if (options.stripTextFragment) { urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, ""); } if (urlObject.pathname) { const protocolRegex = /\b[a-z][a-z\d+\-.]{1,50}:\/\//g; let lastIndex = 0; let result = ""; for (; ; ) { const match = protocolRegex.exec(urlObject.pathname); if (!match) { break; } const protocol = match[0]; const protocolAtIndex = match.index; const intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex); result += intermediate.replace(/\/{2,}/g, "/"); result += protocol; lastIndex = protocolAtIndex + protocol.length; } const remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length); result += remnant.replace(/\/{2,}/g, "/"); urlObject.pathname = result; } if (urlObject.pathname) { try { urlObject.pathname = decodeURI(urlObject.pathname); } catch { } } if (options.removeDirectoryIndex === true) { options.removeDirectoryIndex = [/^index\.[a-z]+$/]; } if (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) { let pathComponents = urlObject.pathname.split("/"); const lastComponent = pathComponents[pathComponents.length - 1]; if (testParameter2(lastComponent, options.removeDirectoryIndex)) { pathComponents = pathComponents.slice(0, -1); urlObject.pathname = `${pathComponents.slice(1).join("/")}/`; } } if (urlObject.hostname) { urlObject.hostname = urlObject.hostname.replace(/\.$/, ""); if (options.stripWWW && /^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(urlObject.hostname)) { urlObject.hostname = urlObject.hostname.replace(/^www\./, ""); } } if (Array.isArray(options.removeQueryParameters)) { for (const key of [...urlObject.searchParams.keys()]) { if (testParameter2(key, options.removeQueryParameters)) { urlObject.searchParams.delete(key); } } } if (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) { urlObject.search = ""; } if (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) { for (const key of [...urlObject.searchParams.keys()]) { if (!testParameter2(key, options.keepQueryParameters)) { urlObject.searchParams.delete(key); } } } if (options.sortQueryParameters) { urlObject.searchParams.sort(); try { urlObject.search = decodeURIComponent(urlObject.search); } catch { } } if (options.removeTrailingSlash) { urlObject.pathname = urlObject.pathname.replace(/\/$/, ""); } if (options.removeExplicitPort && urlObject.port) { urlObject.port = ""; } const oldUrlString = urlString; urlString = urlObject.toString(); if (!options.removeSingleSlash && urlObject.pathname === "/" && !oldUrlString.endsWith("/") && urlObject.hash === "") { urlString = urlString.replace(/\/$/, ""); } if ((options.removeTrailingSlash || urlObject.pathname === "/") && urlObject.hash === "" && options.removeSingleSlash) { urlString = urlString.replace(/\/$/, ""); } if (hasRelativeProtocol && !options.normalizeProtocol) { urlString = urlString.replace(/^http:\/\//, "//"); } if (options.stripProtocol) { urlString = urlString.replace(/^(?:https?:)?\/\//, ""); } return urlString; } async function probeRelayConnection2(relay) { const probeId = `probe-${Math.random().toString(36).substring(7)}`; return new Promise((resolve) => { let responded = false; const timeout = setTimeout(() => { if (!responded) { responded = true; relay.send(["CLOSE", probeId]); resolve(false); } }, 5e3); const handler = () => { if (!responded) { responded = true; clearTimeout(timeout); relay.send(["CLOSE", probeId]); resolve(true); } }; relay.once("message", handler); relay.send([ "REQ", probeId, { kinds: [99999], limit: 0 } ]); }); } async function fetchRelayInformation2(relayUrl) { const httpUrl = relayUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://"); const response = await fetch(httpUrl, { headers: { Accept: "application/nostr+json" } }); if (!response.ok) { throw new Error(`Failed to fetch relay information: ${response.status} ${response.statusText}`); } const data = await response.json(); return data; } function filterFingerprint2(filters, closeOnEose) { const elements = []; for (const filter of filters) { const keys = Object.entries(filter || {}).map(([key, values]) => { if (["since", "until"].includes(key)) { return `${key}:${values}`; } return key; }).sort().join("-"); elements.push(keys); } let id = closeOnEose ? "+" : ""; id += elements.join("|"); return id; } function mergeFilters2(filters) { const result = []; const lastResult = {}; filters.filter((f) => !!f.limit).forEach((filterWithLimit) => result.push(filterWithLimit)); filters = filters.filter((f) => !f.limit); if (filters.length === 0) return result; filters.forEach((filter) => { Object.entries(filter).forEach(([key, value]) => { if (Array.isArray(value)) { if (lastResult[key] === void 0) { lastResult[key] = [...value]; } else { lastResult[key] = Array.from(/* @__PURE__ */ new Set([...lastResult[key], ...value])); } } else { lastResult[key] = value; } }); }); return [...result, lastResult]; } function formatArray2(items, formatter) { const formatted = formatter ? items.slice(0, MAX_ITEMS2).map(formatter) : items.slice(0, MAX_ITEMS2); const display = formatted.join(","); return items.length > MAX_ITEMS2 ? `${display}+${items.length - MAX_ITEMS2}` : display; } function formatFilters2(filters) { return filters.map((f) => { const parts = []; if (f.ids?.length) { parts.push(`ids:[${formatArray2(f.ids, (id) => String(id).slice(0, 8))}]`); } if (f.kinds?.length) { parts.push(`kinds:[${formatArray2(f.kinds)}]`); } if (f.authors?.length) { parts.push(`authors:[${formatArray2(f.authors, (a) => String(a).slice(0, 8))}]`); } if (f.since) { parts.push(`since:${f.since}`); } if (f.until) { parts.push(`until:${f.until}`); } if (f.limit) { parts.push(`limit:${f.limit}`); } if (f.search) { parts.push(`search:"${String(f.search).slice(0, 20)}"`); } for (const [key, value] of Object.entries(f)) { if (key.startsWith("#") && Array.isArray(value) && value.length > 0) { parts.push(`${key}:[${formatArray2(value, (v6) => String(v6).slice(0, 8))}]`); } } return `{${parts.join(" ")}}`; }).join(", "); } async function calculateRelaySetFromEvent2(ndk, event, requiredRelayCount) { const relays = /* @__PURE__ */ new Set(); const authorWriteRelays = await getWriteRelaysFor2(ndk, event.pubkey); if (authorWriteRelays) { authorWriteRelays.forEach((relayUrl) => { const relay = ndk.pool?.getRelay(relayUrl); if (relay) relays.add(relay); }); } let relayHints = event.tags.filter((tag) => ["a", "e"].includes(tag[0])).map((tag) => tag[2]).filter((url) => url?.startsWith("wss://")).filter((url) => { try { new URL(url); return true; } catch { return false; } }).map((url) => normalizeRelayUrl2(url)); relayHints = Array.from(new Set(relayHints)).slice(0, 5); relayHints.forEach((relayUrl) => { const relay = ndk.pool?.getRelay(relayUrl, true, true); if (relay) { d4("Adding relay hint %s", relayUrl); relays.add(relay); } }); const pTags = event.getMatchingTags("p").map((tag) => tag[1]); if (pTags.length < 5) { const pTaggedRelays = Array.from( chooseRelayCombinationForPubkeys2(ndk, pTags, "read", { preferredRelays: new Set(authorWriteRelays) }).keys() ); pTaggedRelays.forEach((relayUrl) => { const relay = ndk.pool?.getRelay(relayUrl, false, true); if (relay) { d4("Adding p-tagged relay %s", relayUrl); relays.add(relay); } }); } else { d4("Too many p-tags to consider %d", pTags.length); } ndk.pool?.permanentAndConnectedRelays().forEach((relay) => relays.add(relay)); if (requiredRelayCount && relays.size < requiredRelayCount) { const explicitRelays = ndk.explicitRelayUrls?.filter((url) => !Array.from(relays).some((r) => r.url === url)).slice(0, requiredRelayCount - relays.size); explicitRelays?.forEach((url) => { const relay = ndk.pool?.getRelay(url, false, true); if (relay) { d4("Adding explicit relay %s", url); relays.add(relay); } }); } return new NDKRelaySet2(relays, ndk); } function isValidHex642(value) { if (typeof value !== "string" || value.length !== 64) { return false; } for (let i3 = 0; i3 < 64; i3++) { const c = value.charCodeAt(i3); if (!(c >= 48 && c <= 57 || c >= 97 && c <= 102 || c >= 65 && c <= 70)) { return false; } } return true; } function isValidPubkey2(pubkey) { return isValidHex642(pubkey); } function mergeTags2(tags1, tags2) { const tagMap = /* @__PURE__ */ new Map(); const generateKey = (tag) => tag.join(","); const isContained = (smaller, larger) => { return smaller.every((value, index) => value === larger[index]); }; const processTag = (tag) => { for (const [key, existingTag] of tagMap) { if (isContained(existingTag, tag) || isContained(tag, existingTag)) { if (tag.length >= existingTag.length) { tagMap.set(key, tag); } return; } } tagMap.set(generateKey(tag), tag); }; tags1.concat(tags2).forEach(processTag); return Array.from(tagMap.values()); } function generateHashtags2(content) { const hashtags = content.match(hashtagRegex2); const tagIds = /* @__PURE__ */ new Set(); const tag = /* @__PURE__ */ new Set(); if (hashtags) { for (const hashtag of hashtags) { if (tagIds.has(hashtag.slice(1))) continue; tag.add(hashtag.slice(1)); tagIds.add(hashtag.slice(1)); } } return Array.from(tag); } async function generateContentTags2(content, tags = [], opts, ctx) { if (opts?.skipContentTagging) { return { content, tags }; } const tagRegex = /(@|nostr:)(npub|nprofile|note|nevent|naddr)[a-zA-Z0-9]+/g; const promises = []; const addTagIfNew = (t) => { if (!tags.find((t2) => ["q", t[0]].includes(t2[0]) && t2[1] === t[1])) { tags.push(t); } }; content = content.replace(tagRegex, (tag) => { try { const entity = tag.split(/(@|nostr:)/)[2]; const { type, data } = import_nostr_tools11.nip19.decode(entity); let t; if (opts?.filters) { const shouldInclude = !opts.filters.includeTypes || opts.filters.includeTypes.includes(type); const shouldExclude = opts.filters.excludeTypes?.includes(type); if (!shouldInclude || shouldExclude) { return tag; } } switch (type) { case "npub": if (opts?.pTags !== false) { t = ["p", data]; } break; case "nprofile": if (opts?.pTags !== false) { t = ["p", data.pubkey]; } break; case "note": promises.push( new Promise(async (resolve) => { const relay = await maybeGetEventRelayUrl2(entity); addTagIfNew(["q", data, relay]); resolve(); }) ); break; case "nevent": promises.push( new Promise(async (resolve) => { const { id, author } = data; let { relays } = data; if (!relays || relays.length === 0) { relays = [await maybeGetEventRelayUrl2(entity)]; } addTagIfNew(["q", id, relays[0]]); if (author && opts?.pTags !== false && opts?.pTagOnQTags !== false) addTagIfNew(["p", author]); resolve(); }) ); break; case "naddr": promises.push( new Promise(async (resolve) => { const id = [data.kind, data.pubkey, data.identifier].join(":"); let relays = data.relays ?? []; if (relays.length === 0) { relays = [await maybeGetEventRelayUrl2(entity)]; } addTagIfNew(["q", id, relays[0]]); if (opts?.pTags !== false && opts?.pTagOnQTags !== false && opts?.pTagOnATags !== false) addTagIfNew(["p", data.pubkey]); resolve(); }) ); break; default: return tag; } if (t) addTagIfNew(t); return `nostr:${entity}`; } catch (_error) { return tag; } }); await Promise.all(promises); if (!opts?.filters?.excludeTypes?.includes("hashtag")) { const newTags = generateHashtags2(content).map((hashtag) => ["t", hashtag]); tags = mergeTags2(tags, newTags); } if (opts?.pTags !== false && opts?.copyPTagsFromTarget && ctx) { const pTags = ctx.getMatchingTags("p"); for (const pTag of pTags) { if (!pTag[1] || !isValidPubkey2(pTag[1])) continue; if (!tags.find((t) => t[0] === "p" && t[1] === pTag[1])) { tags.push(pTag); } } } return { content, tags }; } async function maybeGetEventRelayUrl2(_nip19Id) { return ""; } async function encrypt6(recipient, signer, scheme = "nip44") { let encrypted; if (!this.ndk) throw new Error("No NDK instance found!"); let currentSigner = signer; if (!currentSigner) { this.ndk.assertSigner(); currentSigner = this.ndk.signer; } if (!currentSigner) throw new Error("no NDK signer"); const currentRecipient = recipient || (() => { const pTags = this.getMatchingTags("p"); if (pTags.length !== 1) { throw new Error("No recipient could be determined and no explicit recipient was provided"); } return this.ndk.getUser({ pubkey: pTags[0][1] }); })(); if (scheme === "nip44" && await isEncryptionEnabled2(currentSigner, "nip44")) { encrypted = await currentSigner.encrypt(currentRecipient, this.content, "nip44"); } if ((!encrypted || scheme === "nip04") && await isEncryptionEnabled2(currentSigner, "nip04")) { encrypted = await currentSigner.encrypt(currentRecipient, this.content, "nip04"); } if (!encrypted) throw new Error("Failed to encrypt event."); this.content = encrypted; } async function decrypt6(sender, signer, scheme) { if (this.ndk?.cacheAdapter?.getDecryptedEvent) { const cachedEvent = await this.ndk.cacheAdapter.getDecryptedEvent(this.id); if (cachedEvent) { this.content = cachedEvent.content; return; } } let decrypted; if (!this.ndk) throw new Error("No NDK instance found!"); let currentSigner = signer; if (!currentSigner) { this.ndk.assertSigner(); currentSigner = this.ndk.signer; } if (!currentSigner) throw new Error("no NDK signer"); const currentSender = sender || this.author; if (!currentSender) throw new Error("No sender provided and no author available"); const currentScheme = scheme || (this.content.match(/\\?iv=/) ? "nip04" : "nip44"); if ((currentScheme === "nip04" || this.kind === 4) && await isEncryptionEnabled2(currentSigner, "nip04") && this.content.search("\\?iv=")) { decrypted = await currentSigner.decrypt(currentSender, this.content, "nip04"); } if (!decrypted && currentScheme === "nip44" && await isEncryptionEnabled2(currentSigner, "nip44")) { decrypted = await currentSigner.decrypt(currentSender, this.content, "nip44"); } if (!decrypted) throw new Error("Failed to decrypt event."); this.content = decrypted; if (this.ndk?.cacheAdapter?.addDecryptedEvent) { this.ndk.cacheAdapter.addDecryptedEvent(this.id, this); } } async function isEncryptionEnabled2(signer, scheme) { if (!signer.encryptionEnabled) return false; if (!scheme) return true; return Boolean(await signer.encryptionEnabled(scheme)); } function eventHasETagMarkers2(event) { for (const tag of event.tags) { if (tag[0] === "e" && (tag[3] ?? "").length > 0) return true; } return false; } function getRootTag2(event, searchTag) { searchTag ?? (searchTag = event.tagType()); const rootEventTag = event.tags.find(isTagRootTag2); if (!rootEventTag) { if (eventHasETagMarkers2(event)) return; const matchingTags = event.getMatchingTags(searchTag); if (matchingTags.length < 3) return matchingTags[0]; } return rootEventTag; } function getReplyTag2(event, searchTag) { if (event.kind === 1111) { let replyTag2; for (const tag of event.tags) { if (nip22RootTags2.has(tag[0])) replyTag2 = tag; else if (nip22ReplyTags2.has(tag[0])) { replyTag2 = tag; break; } } return replyTag2; } searchTag ?? (searchTag = event.tagType()); let hasMarkers2 = false; let replyTag; for (const tag of event.tags) { if (tag[0] !== searchTag) continue; if ((tag[3] ?? "").length > 0) hasMarkers2 = true; if (hasMarkers2 && tag[3] === "reply") return tag; if (hasMarkers2 && tag[3] === "root") replyTag = tag; if (!hasMarkers2) replyTag = tag; } return replyTag; } function isTagRootTag2(tag) { return tag[0] === "E" || tag[3] === "root"; } async function fetchTaggedEvent2(tag, marker) { if (!this.ndk) throw new Error("NDK instance not found"); const t = this.getMatchingTags(tag, marker); if (t.length === 0) return void 0; const [_2, id, hint] = t[0]; const relay = hint !== "" ? this.ndk.pool.getRelay(hint) : void 0; const event = await this.ndk.fetchEvent(id, {}, relay); return event; } async function fetchRootEvent2(subOpts) { if (!this.ndk) throw new Error("NDK instance not found"); const rootTag = getRootTag2(this); if (!rootTag) return void 0; return this.ndk.fetchEventFromTag(rootTag, this, subOpts); } async function fetchReplyEvent2(subOpts) { if (!this.ndk) throw new Error("NDK instance not found"); const replyTag = getReplyTag2(this); if (!replyTag) return void 0; return this.ndk.fetchEventFromTag(replyTag, this, subOpts); } function isReplaceable2() { if (this.kind === void 0) throw new Error("Kind not set"); return [0, 3].includes(this.kind) || this.kind >= 1e4 && this.kind < 2e4 || this.kind >= 3e4 && this.kind < 4e4; } function isEphemeral2() { if (this.kind === void 0) throw new Error("Kind not set"); return this.kind >= 2e4 && this.kind < 3e4; } function isParamReplaceable2() { if (this.kind === void 0) throw new Error("Kind not set"); return this.kind >= 3e4 && this.kind < 4e4; } function encode2(maxRelayCount = DEFAULT_RELAY_COUNT2) { let relays = []; if (this.onRelays.length > 0) { relays = this.onRelays.map((relay) => relay.url); } else if (this.relay) { relays = [this.relay.url]; } if (relays.length > maxRelayCount) { relays = relays.slice(0, maxRelayCount); } if (this.isParamReplaceable()) { return import_nostr_tools12.nip19.naddrEncode({ kind: this.kind, pubkey: this.pubkey, identifier: this.replaceableDTag(), relays }); } if (relays.length > 0) { return import_nostr_tools12.nip19.neventEncode({ id: this.tagId(), relays, author: this.pubkey }); } return import_nostr_tools12.nip19.noteEncode(this.tagId()); } async function repost2(publish = true, signer) { if (!signer && publish) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); signer = this.ndk.signer; } const e2 = new NDKEvent2(this.ndk, { kind: getKind2(this) }); if (!this.isProtected) e2.content = JSON.stringify(this.rawEvent()); e2.tag(this); if (this.kind !== 1) { e2.tags.push(["k", `${this.kind}`]); } if (signer) await e2.sign(signer); if (publish) await e2.publish(); return e2; } function getKind2(event) { if (event.kind === 1) { return 6; } return 16; } function getEventDetails2(event) { if ("inspect" in event && typeof event.inspect === "string") { return event.inspect; } return JSON.stringify(event); } function validateForSerialization2(event) { if (typeof event.kind !== "number") { throw new Error( `Can't serialize event with invalid properties: kind (must be number, got ${typeof event.kind}). Event: ${getEventDetails2(event)}` ); } if (typeof event.content !== "string") { throw new Error( `Can't serialize event with invalid properties: content (must be string, got ${typeof event.content}). Event: ${getEventDetails2(event)}` ); } if (typeof event.created_at !== "number") { throw new Error( `Can't serialize event with invalid properties: created_at (must be number, got ${typeof event.created_at}). Event: ${getEventDetails2(event)}` ); } if (typeof event.pubkey !== "string") { throw new Error( `Can't serialize event with invalid properties: pubkey (must be string, got ${typeof event.pubkey}). Event: ${getEventDetails2(event)}` ); } if (!Array.isArray(event.tags)) { throw new Error( `Can't serialize event with invalid properties: tags (must be array, got ${typeof event.tags}). Event: ${getEventDetails2(event)}` ); } for (let i3 = 0; i3 < event.tags.length; i3++) { const tag = event.tags[i3]; if (!Array.isArray(tag)) { throw new Error( `Can't serialize event with invalid properties: tags[${i3}] (must be array, got ${typeof tag}). Event: ${getEventDetails2(event)}` ); } for (let j2 = 0; j2 < tag.length; j2++) { if (typeof tag[j2] !== "string") { throw new Error( `Can't serialize event with invalid properties: tags[${i3}][${j2}] (must be string, got ${typeof tag[j2]}). Event: ${getEventDetails2(event)}` ); } } } } function serialize2(includeSig = false, includeId = false) { validateForSerialization2(this); const payload = [0, this.pubkey, this.created_at, this.kind, this.tags, this.content]; if (includeSig) payload.push(this.sig); if (includeId) payload.push(this.id); return JSON.stringify(payload); } function deserialize2(serializedEvent) { const eventArray = JSON.parse(serializedEvent); const ret = { pubkey: eventArray[1], created_at: eventArray[2], kind: eventArray[3], tags: eventArray[4], content: eventArray[5] }; if (eventArray.length >= 7) { const first = eventArray[6]; const second = eventArray[7]; if (first && first.length === 128) { ret.sig = first; if (second && second.length === 64) { ret.id = second; } } else if (first && first.length === 64) { ret.id = first; if (second && second.length === 128) { ret.sig = second; } } } return ret; } async function verifySignatureAsync2(event, _persist, relay) { const ndkInstance = event.ndk; const start = Date.now(); let result; if (ndkInstance.signatureVerificationFunction) { result = await ndkInstance.signatureVerificationFunction(event); } else { result = await new Promise((resolve) => { const serialized = event.serialize(); let enqueue = false; if (!processingQueue2[event.id]) { processingQueue2[event.id] = { event, resolves: [], relay }; enqueue = true; } processingQueue2[event.id].resolves.push(resolve); if (!enqueue) return; worker2?.postMessage({ serialized, id: event.id, sig: event.sig, pubkey: event.pubkey }); }); } ndkInstance.signatureVerificationTimeMs += Date.now() - start; return result; } function validate2() { if (typeof this.kind !== "number") return false; if (typeof this.content !== "string") return false; if (typeof this.created_at !== "number") return false; if (typeof this.pubkey !== "string") return false; if (!this.pubkey.match(PUBKEY_REGEX2)) return false; if (!Array.isArray(this.tags)) return false; for (let i3 = 0; i3 < this.tags.length; i3++) { const tag = this.tags[i3]; if (!Array.isArray(tag)) return false; for (let j2 = 0; j2 < tag.length; j2++) { if (typeof tag[j2] === "object") return false; } } return true; } function verifySignature2(persist) { if (typeof this.signatureVerified === "boolean") return this.signatureVerified; const prevVerification = verifiedSignatures2.get(this.id); if (prevVerification !== null) { this.signatureVerified = !!prevVerification; return this.signatureVerified; } try { if (this.ndk?.asyncSigVerification) { const relayForVerification = this.relay; verifySignatureAsync2(this, persist, relayForVerification).then((result) => { if (persist) { this.signatureVerified = result; if (result) verifiedSignatures2.set(this.id, this.sig); } if (!result) { if (relayForVerification) { this.ndk?.reportInvalidSignature(this, relayForVerification); } else { this.ndk?.reportInvalidSignature(this); } verifiedSignatures2.set(this.id, false); } else { if (relayForVerification) { relayForVerification.addValidatedEvent(); } } }).catch((err) => { console.error("signature verification error", this.id, err); }); } else { const hash3 = sha2562(new TextEncoder().encode(this.serialize())); const res = schnorr.verify(this.sig, hash3, this.pubkey); if (res) verifiedSignatures2.set(this.id, this.sig); else verifiedSignatures2.set(this.id, false); this.signatureVerified = res; return res; } } catch (_err) { this.signatureVerified = false; return false; } } function getEventHash3() { return getEventHashFromSerializedEvent2(this.serialize()); } function getEventHashFromSerializedEvent2(serializedEvent) { const eventHash = sha2562(new TextEncoder().encode(serializedEvent)); return bytesToHex(eventHash); } function shouldTrackUnpublishedEvent2(event) { return !untrackedUnpublishedEvents2.has(event.kind); } function mapImetaTag2(tag) { const data = {}; if (tag.length === 2) { const parts = tag[1].split(" "); for (let i3 = 0; i3 < parts.length; i3 += 2) { const key = parts[i3]; const value = parts[i3 + 1]; if (key === "fallback") { if (!data.fallback) data.fallback = []; data.fallback.push(value); } else { data[key] = value; } } return data; } const tags = tag.slice(1); for (const val of tags) { const parts = val.split(" "); const key = parts[0]; const value = parts.slice(1).join(" "); if (key === "fallback") { if (!data.fallback) data.fallback = []; data.fallback.push(value); } else { data[key] = value; } } return data; } function imetaTagToTag2(imeta) { const tag = ["imeta"]; for (const [key, value] of Object.entries(imeta)) { if (Array.isArray(value)) { for (const v6 of value) { tag.push(`${key} ${v6}`); } } else if (value) { tag.push(`${key} ${value}`); } } return tag; } function createValidationIssue2(code, proofIndex) { return { code, severity: SEVERITY_MAP2[code], message: ERROR_MESSAGES2[code], proofIndex }; } function proofP2pk2(proof) { try { const secret = JSON.parse(proof.secret); let payload = {}; if (typeof secret === "string") { payload = JSON.parse(secret); } else if (typeof secret === "object") { payload = secret; } const isP2PKLocked = payload[0] === "P2PK" && payload[1]?.data; if (isP2PKLocked) { return payload[1].data; } } catch (e2) { console.error("error parsing p2pk pubkey", e2, proof); } } function cashuPubkeyToNostrPubkey2(cashuPubkey) { if (cashuPubkey.startsWith("02") && cashuPubkey.length === 66) return cashuPubkey.slice(2); return void 0; } function filterForId2(id) { if (id.match(/:/)) { const [kind, pubkey, identifier] = id.split(":"); return { kinds: [Number.parseInt(kind)], authors: [pubkey], "#d": [identifier] }; } return { ids: [id] }; } function strToPosition2(positionStr) { const [x2, y2] = positionStr.split(",").map(Number); return { x: x2, y: y2 }; } function strToDimension2(dimensionStr) { const [width, height] = dimensionStr.split("x").map(Number); return { width, height }; } function newAmount2(amount, currency, term) { return ["amount", amount.toString(), currency, term]; } function parseTagToSubscriptionAmount2(tag) { const amount = Number.parseInt(tag[1]); if (Number.isNaN(amount) || amount === void 0 || amount === null || amount <= 0) return void 0; const currency = tag[2]; if (currency === void 0 || currency === "") return void 0; const term = tag[3]; if (term === void 0) return void 0; if (!possibleIntervalFrequencies2.includes(term)) return void 0; return { amount, currency, term }; } function wrapEvent2(event) { const eventWrappingMap = /* @__PURE__ */ new Map(); const builtInClasses = [ NDKImage2, NDKVideo2, NDKCashuMintList2, NDKArticle2, NDKHighlight2, NDKDraft2, NDKWiki2, NDKWikiMergeRequest2, NDKNutzap2, NDKProject2, NDKTask2, NDKProjectTemplate2, NDKSimpleGroupMemberList2, NDKSimpleGroupMetadata2, NDKSubscriptionTier2, NDKSubscriptionStart2, NDKSubscriptionReceipt2, NDKList2, NDKRelayList2, NDKRelayFeedList2, NDKStory2, NDKBlossomList2, NDKFollowPack2, NDKThread2, NDKRepost2, NDKClassified2, NDKAppHandlerEvent2, NDKDVMJobFeedback2, NDKCashuMintAnnouncement2, NDKFedimintMint2, NDKMintRecommendation2 ]; const allClasses = [...builtInClasses, ...registeredEventClasses2]; for (const klass2 of allClasses) { for (const kind of klass2.kinds) { eventWrappingMap.set(kind, klass2); } } const klass = eventWrappingMap.get(event.kind); if (klass) return klass.from(event); return event; } async function follows2(opts, outbox, kind = 3) { if (!this.ndk) throw new Error("NDK not set"); const contactListEvent = await this.ndk.fetchEvent( { kinds: [kind], authors: [this.pubkey] }, opts || { groupable: false } ); if (contactListEvent) { const pubkeys = /* @__PURE__ */ new Set(); contactListEvent.tags.forEach((tag) => { if (tag[0] === "p" && tag[1] && isValidPubkey2(tag[1])) { pubkeys.add(tag[1]); } }); if (outbox) { this.ndk?.outboxTracker?.trackUsers(Array.from(pubkeys)); } return [...pubkeys].reduce((acc, pubkey) => { const user = new NDKUser2({ pubkey }); user.ndk = this.ndk; acc.add(user); return acc; }, /* @__PURE__ */ new Set()); } return /* @__PURE__ */ new Set(); } async function getNip05For2(ndk, fullname, _fetch5 = fetch, fetchOpts = {}) { return await ndk.queuesNip05.add({ id: fullname, func: async () => { if (ndk.cacheAdapter?.loadNip05) { const profile = await ndk.cacheAdapter.loadNip05(fullname); if (profile !== "missing") { if (profile) { const user = new NDKUser2({ pubkey: profile.pubkey, relayUrls: profile.relays, nip46Urls: profile.nip46 }); user.ndk = ndk; return user; } if (fetchOpts.cache !== "no-cache") { return null; } } } const match = fullname.match(NIP05_REGEX2); if (!match) return null; const [_2, name = "_", domain] = match; try { const res = await _fetch5(`https://${domain}/.well-known/nostr.json?name=${name}`, fetchOpts); const { names, relays, nip46 } = parseNIP05Result2(await res.json()); const pubkey = names[name.toLowerCase()]; let profile = null; if (pubkey) { profile = { pubkey, relays: relays?.[pubkey], nip46: nip46?.[pubkey] }; } if (ndk?.cacheAdapter?.saveNip05) { ndk.cacheAdapter.saveNip05(fullname, profile); } return profile; } catch (_e2) { if (ndk?.cacheAdapter?.saveNip05) { ndk?.cacheAdapter.saveNip05(fullname, null); } console.error("Failed to fetch NIP05 for", fullname, _e2); return null; } } }); } function parseNIP05Result2(json) { const result = { names: {} }; for (const [name, pubkey] of Object.entries(json.names)) { if (typeof name === "string" && typeof pubkey === "string") { result.names[name.toLowerCase()] = pubkey; } } if (json.relays) { result.relays = {}; for (const [pubkey, relays] of Object.entries(json.relays)) { if (typeof pubkey === "string" && Array.isArray(relays)) { result.relays[pubkey] = relays.filter((relay) => typeof relay === "string"); } } } if (json.nip46) { result.nip46 = {}; for (const [pubkey, nip46] of Object.entries(json.nip46)) { if (typeof pubkey === "string" && Array.isArray(nip46)) { result.nip46[pubkey] = nip46.filter((relay) => typeof relay === "string"); } } } return result; } function profileFromEvent2(event) { const profile = {}; let payload; try { payload = JSON.parse(event.content); } catch (error) { throw new Error(`Failed to parse profile event: ${error}`); } profile.profileEvent = JSON.stringify(event.rawEvent()); for (const key of Object.keys(payload)) { switch (key) { case "name": profile.name = payload.name; break; case "display_name": profile.displayName = payload.display_name; break; case "image": case "picture": profile.picture = payload.picture || payload.image; profile.image = profile.picture; break; case "banner": profile.banner = payload.banner; break; case "bio": profile.bio = payload.bio; break; case "nip05": profile.nip05 = payload.nip05; break; case "lud06": profile.lud06 = payload.lud06; break; case "lud16": profile.lud16 = payload.lud16; break; case "about": profile.about = payload.about; break; case "website": profile.website = payload.website; break; default: profile[key] = payload[key]; break; } } profile.created_at = event.created_at; return profile; } function serializeProfile2(profile) { const payload = {}; for (const [key, val] of Object.entries(profile)) { switch (key) { case "username": case "name": payload.name = val; break; case "displayName": payload.display_name = val; break; case "image": case "picture": payload.picture = val; break; case "bio": case "about": payload.about = val; break; default: payload[key] = val; break; } } return JSON.stringify(payload); } function registerSigner2(type, signerClass) { signerRegistry2.set(type, signerClass); } async function giftWrap2(event, recipient, signer, params = {}) { let _signer = signer; params.scheme ?? (params.scheme = "nip44"); if (!_signer) { if (!event.ndk) throw new Error("no signer available for giftWrap"); _signer = event.ndk.signer; } if (!_signer) throw new Error("no signer"); if (!_signer.encryptionEnabled || !_signer.encryptionEnabled(params.scheme)) throw new Error("signer is not able to giftWrap"); if (!event.pubkey) { const sender = await _signer.user(); event.pubkey = sender.pubkey; } if (event.sig) { console.warn( "\u26A0\uFE0F NIP-17 Warning: Rumor event should not be signed. The signature will be removed during gift wrapping." ); } const rumor = getRumorEvent2(event, params?.rumorKind); const seal = await getSealEvent2(rumor, recipient, _signer, params.scheme); const wrap = await getWrapEvent2(seal, recipient, params); return new NDKEvent2(event.ndk, wrap); } async function giftUnwrap2(event, sender, signer, scheme = "nip44") { if (event.ndk?.cacheAdapter?.getDecryptedEvent) { const cached = await event.ndk.cacheAdapter.getDecryptedEvent(event.id); if (cached) { return cached; } } const _sender = sender || new NDKUser2({ pubkey: event.pubkey }); const _signer = signer || event.ndk?.signer; if (!_signer) throw new Error("no signer"); try { const seal = JSON.parse(await _signer.decrypt(_sender, event.content, scheme)); if (!seal) throw new Error("Failed to decrypt wrapper"); if (!new NDKEvent2(void 0, seal).verifySignature(false)) throw new Error("GiftSeal signature verification failed!"); const rumorSender = new NDKUser2({ pubkey: seal.pubkey }); const rumor = JSON.parse(await _signer.decrypt(rumorSender, seal.content, scheme)); if (!rumor) throw new Error("Failed to decrypt seal"); if (rumor.pubkey !== seal.pubkey) throw new Error("Invalid GiftWrap, sender validation failed!"); const rumorEvent = new NDKEvent2(event.ndk, rumor); if (event.ndk?.cacheAdapter?.addDecryptedEvent) { await event.ndk.cacheAdapter.addDecryptedEvent(event.id, rumorEvent); } return rumorEvent; } catch (_e2) { return Promise.reject("Got error unwrapping event! See console log."); } } function getRumorEvent2(event, kind) { const rumor = event.rawEvent(); rumor.kind = kind || rumor.kind || 14; rumor.sig = void 0; rumor.id = (0, import_nostr_tools13.getEventHash)(rumor); return new NDKEvent2(event.ndk, rumor); } async function getSealEvent2(rumor, recipient, signer, scheme = "nip44") { const seal = new NDKEvent2(rumor.ndk); seal.kind = 13; seal.created_at = approximateNow2(5); seal.content = JSON.stringify(rumor.rawEvent()); await seal.encrypt(recipient, signer, scheme); await seal.sign(signer); return seal; } async function getWrapEvent2(sealed, recipient, params, scheme = "nip44") { const signer = NDKPrivateKeySigner2.generate(); const wrap = new NDKEvent2(sealed.ndk); wrap.kind = 1059; wrap.created_at = approximateNow2(5); if (params?.wrapTags) wrap.tags = params.wrapTags; wrap.tag(recipient); wrap.content = JSON.stringify(sealed.rawEvent()); await wrap.encrypt(recipient, signer, scheme); await wrap.sign(signer); return wrap; } function approximateNow2(drift = 0) { return Math.round(Date.now() / 1e3 - Math.random() * 10 ** drift); } function proofsTotalBalance2(proofs) { return proofs.reduce((acc, proof) => { if (proof.amount < 0) { throw new Error("proof amount is negative"); } return acc + proof.amount; }, 0); } function disconnect2(pool, debug92) { debug92 ?? (debug92 = (0, import_debug21.default)("ndk:relay:auth-policies:disconnect")); return async (relay) => { debug92?.(`Relay ${relay.url} requested authentication, disconnecting`); pool.removeRelay(relay.url); }; } async function signAndAuth2(event, relay, signer, debug92, resolve, reject) { try { await event.sign(signer); resolve(event); } catch (e2) { debug92?.(`Failed to publish auth event to relay ${relay.url}`, e2); reject(event); } } function signIn2({ ndk, signer, debug: debug92 } = {}) { debug92 ?? (debug92 = (0, import_debug21.default)("ndk:auth-policies:signIn")); return async (relay, challenge3) => { debug92?.(`Relay ${relay.url} requested authentication, signing in`); const event = new NDKEvent2(ndk); event.kind = 22242; event.tags = [ ["relay", relay.url], ["challenge", challenge3] ]; signer ?? (signer = ndk?.signer); return new Promise(async (resolve, reject) => { if (signer) { await signAndAuth2(event, relay, signer, debug92, resolve, reject); } else { ndk?.once("signer:ready", async (signer2) => { await signAndAuth2(event, relay, signer2, debug92, resolve, reject); }); } }); }; } async function ndkSignerFromPayload2(payloadString, ndk) { let parsed; try { parsed = JSON.parse(payloadString); } catch (e2) { console.error("Failed to parse signer payload string", payloadString, e2); return void 0; } if (!parsed || typeof parsed.type !== "string") { console.error("Failed to parse signer payload string", payloadString, new Error("Missing type field")); return void 0; } const SignerClass = signerRegistry2.get(parsed.type); if (!SignerClass) { throw new Error(`Unknown signer type: ${parsed.type}`); } try { return await SignerClass.fromPayload(payloadString, ndk); } catch (e2) { const errorMsg = e2 instanceof Error ? e2.message : String(e2); throw new Error(`Failed to deserialize signer type ${parsed.type}: ${errorMsg}`); } } function nostrConnectGenerateSecret2() { return Math.random().toString(36).substring(2, 15); } function generateNostrConnectUri2(pubkey, secret, relay, options) { const meta = { name: options?.name ? encodeURIComponent(options.name) : "", url: options?.url ? encodeURIComponent(options.url) : "", image: options?.image ? encodeURIComponent(options.image) : "", perms: options?.perms ? encodeURIComponent(options.perms) : "" }; let uri = `nostrconnect://${pubkey}?image=${meta.image}&url=${meta.url}&name=${meta.name}&perms=${meta.perms}&secret=${encodeURIComponent(secret)}`; if (relay) { uri += `&relay=${encodeURIComponent(relay)}`; } return uri; } function matchFilter2(filter, event) { if (filter.ids && filter.ids.indexOf(event.id) === -1) { return false; } if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) { return false; } if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) { return false; } for (const f in filter) { if (f[0] === "#") { const tagName = f.slice(1); if (tagName === "t") { const values = filter[`#${tagName}`]?.map((v6) => v6.toLowerCase()); if (values && !event.tags.find(([t, v6]) => t === tagName && values?.indexOf(v6.toLowerCase()) !== -1)) return false; } else { const values = filter[`#${tagName}`]; if (values && !event.tags.find(([t, v6]) => t === tagName && values?.indexOf(v6) !== -1)) return false; } } } if (filter.since && event.created_at < filter.since) return false; if (filter.until && event.created_at > filter.until) return false; return true; } var import_tseep10, import_debug13, import_debug14, import_tseep11, import_debug15, import_nostr_tools11, import_nostr_tools12, import_typescript_lru_cache4, import_tseep12, import_nostr_tools13, import_nostr_tools14, nip492, import_nostr_tools15, import_tseep13, import_nostr_tools16, import_debug16, import_debug17, import_debug18, import_nostr_tools17, import_light_bolt11_decoder2, import_debug19, import_nostr_tools18, import_tseep14, import_tseep15, import_typescript_lru_cache5, import_nostr_tools19, import_typescript_lru_cache6, import_debug20, import_nostr_tools20, nip19_star2, nip49_star2, import_debug21, import_debug22, import_tseep16, import_tseep17, import_debug23, import_tseep18, import_debug24, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __copyProps2, __reExport2, NDKKind2, NdkNutzapStatus2, DATA_URL_DEFAULT_MIME_TYPE2, DATA_URL_DEFAULT_CHARSET2, testParameter2, supportedProtocols2, hasCustomProtocol2, normalizeDataURL2, NDKRelayKeepalive2, MAX_RECONNECT_ATTEMPTS2, FLAPPING_THRESHOLD_MS2, NDKRelayConnectivity2, NDKRelayPublisher2, MAX_ITEMS2, NDKRelaySubscription2, NDKRelaySubscriptionManager2, _a2, NDKRelay2, NDKPublishError2, NDKRelaySet2, d4, hashtagRegex2, nip22RootTags2, nip22ReplyTags2, DEFAULT_RELAY_COUNT2, worker2, processingQueue2, PUBKEY_REGEX2, verifiedSignatures2, skipClientTagOnKinds2, NDKEvent2, untrackedUnpublishedEvents2, NDKPool2, _a3, NDKDVMJobFeedback2, _a4, NDKCashuMintList2, _a5, NDKArticle2, _a6, NDKBlossomList2, _a7, NDKFedimintMint2, _a8, NDKCashuMintAnnouncement2, _a9, NDKMintRecommendation2, _a10, NDKClassified2, _a11, NDKDraft2, _a12, NDKFollowPack2, _a13, NDKHighlight2, _a14, NDKImage2, _a15, NDKList2, _a16, NDKAppHandlerEvent2, SEVERITY_MAP2, ERROR_MESSAGES2, _a17, NDKNutzap2, _a18, NDKProject2, _a19, NDKProjectTemplate2, READ_MARKER2, WRITE_MARKER2, _a20, NDKRelayList2, _a21, NDKRelayFeedList2, _a22, NDKRepost2, _a23, NDKSimpleGroupMemberList2, _a24, NDKSimpleGroupMetadata2, _a25, NDKStorySticker2, _a26, NDKStory2, coordinates2, dimension2, _a27, NDKSubscriptionReceipt2, possibleIntervalFrequencies2, _a28, NDKSubscriptionTier2, _a29, NDKSubscriptionStart2, _a30, NDKTask2, _a31, NDKThread2, _a32, NDKVideo2, _a33, NDKWiki2, _a34, NDKWikiMergeRequest2, registeredEventClasses2, NDKSubscriptionCacheUsage2, NIP05_REGEX2, NDKUser2, signerRegistry2, NDKPrivateKeySigner2, _a35, NDKCashuToken2, MARKERS2, _a36, NDKCashuWalletTx2, debug62, nip19_exports2, nip49_exports2, NDKRelayAuthPolicies2, NDKNip07Signer2, NDKNostrRpc2, NDKNip46Signer2, d22, d32; var init_dist = __esm({ "ndk/core/dist/index.mjs"() { "use strict"; import_tseep10 = __toESM(require_lib(), 1); import_debug13 = __toESM(require_browser(), 1); import_debug14 = __toESM(require_browser(), 1); import_tseep11 = __toESM(require_lib(), 1); import_debug15 = __toESM(require_browser(), 1); import_nostr_tools11 = __toESM(require_nostr_tools(), 1); import_nostr_tools12 = __toESM(require_nostr_tools(), 1); init_secp256k1(); init_sha256(); init_utils(); import_typescript_lru_cache4 = __toESM(require_dist(), 1); import_tseep12 = __toESM(require_lib(), 1); import_nostr_tools13 = __toESM(require_nostr_tools(), 1); init_utils(); import_nostr_tools14 = __toESM(require_nostr_tools(), 1); nip492 = __toESM(require_nip49(), 1); import_nostr_tools15 = __toESM(require_nostr_tools(), 1); import_tseep13 = __toESM(require_lib(), 1); import_nostr_tools16 = __toESM(require_nostr_tools(), 1); import_debug16 = __toESM(require_browser(), 1); import_debug17 = __toESM(require_browser(), 1); import_debug18 = __toESM(require_browser(), 1); import_nostr_tools17 = __toESM(require_nostr_tools(), 1); import_light_bolt11_decoder2 = __toESM(require_bolt11(), 1); import_debug19 = __toESM(require_browser(), 1); import_nostr_tools18 = __toESM(require_nostr_tools(), 1); import_tseep14 = __toESM(require_lib(), 1); import_tseep15 = __toESM(require_lib(), 1); import_typescript_lru_cache5 = __toESM(require_dist(), 1); import_nostr_tools19 = __toESM(require_nostr_tools(), 1); import_typescript_lru_cache6 = __toESM(require_dist(), 1); import_debug20 = __toESM(require_browser(), 1); import_nostr_tools20 = __toESM(require_nostr_tools(), 1); nip19_star2 = __toESM(require_nip19(), 1); nip49_star2 = __toESM(require_nip49(), 1); import_debug21 = __toESM(require_browser(), 1); import_debug22 = __toESM(require_browser(), 1); import_tseep16 = __toESM(require_lib(), 1); import_tseep17 = __toESM(require_lib(), 1); import_debug23 = __toESM(require_browser(), 1); import_tseep18 = __toESM(require_lib(), 1); import_debug24 = __toESM(require_browser(), 1); __defProp2 = Object.defineProperty; __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; __getOwnPropNames2 = Object.getOwnPropertyNames; __hasOwnProp2 = Object.prototype.hasOwnProperty; __copyProps2 = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames2(from)) if (!__hasOwnProp2.call(to, key) && key !== except) __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable }); } return to; }; __reExport2 = (target, mod3, secondTarget) => (__copyProps2(target, mod3, "default"), secondTarget && __copyProps2(secondTarget, mod3, "default")); NDKKind2 = /* @__PURE__ */ ((NDKKind22) => { NDKKind22[NDKKind22["Metadata"] = 0] = "Metadata"; NDKKind22[NDKKind22["Text"] = 1] = "Text"; NDKKind22[NDKKind22["RecommendRelay"] = 2] = "RecommendRelay"; NDKKind22[NDKKind22["Contacts"] = 3] = "Contacts"; NDKKind22[NDKKind22["EncryptedDirectMessage"] = 4] = "EncryptedDirectMessage"; NDKKind22[NDKKind22["EventDeletion"] = 5] = "EventDeletion"; NDKKind22[NDKKind22["Repost"] = 6] = "Repost"; NDKKind22[NDKKind22["Reaction"] = 7] = "Reaction"; NDKKind22[NDKKind22["BadgeAward"] = 8] = "BadgeAward"; NDKKind22[NDKKind22["GroupChat"] = 9] = "GroupChat"; NDKKind22[NDKKind22["Thread"] = 11] = "Thread"; NDKKind22[NDKKind22["GroupReply"] = 12] = "GroupReply"; NDKKind22[NDKKind22["GiftWrapSeal"] = 13] = "GiftWrapSeal"; NDKKind22[NDKKind22["PrivateDirectMessage"] = 14] = "PrivateDirectMessage"; NDKKind22[NDKKind22["Image"] = 20] = "Image"; NDKKind22[NDKKind22["Video"] = 21] = "Video"; NDKKind22[NDKKind22["ShortVideo"] = 22] = "ShortVideo"; NDKKind22[NDKKind22["Story"] = 23] = "Story"; NDKKind22[NDKKind22["Vanish"] = 62] = "Vanish"; NDKKind22[NDKKind22["CashuWalletBackup"] = 375] = "CashuWalletBackup"; NDKKind22[NDKKind22["GiftWrap"] = 1059] = "GiftWrap"; NDKKind22[NDKKind22["GenericRepost"] = 16] = "GenericRepost"; NDKKind22[NDKKind22["ChannelCreation"] = 40] = "ChannelCreation"; NDKKind22[NDKKind22["ChannelMetadata"] = 41] = "ChannelMetadata"; NDKKind22[NDKKind22["ChannelMessage"] = 42] = "ChannelMessage"; NDKKind22[NDKKind22["ChannelHideMessage"] = 43] = "ChannelHideMessage"; NDKKind22[NDKKind22["ChannelMuteUser"] = 44] = "ChannelMuteUser"; NDKKind22[NDKKind22["WikiMergeRequest"] = 818] = "WikiMergeRequest"; NDKKind22[NDKKind22["GenericReply"] = 1111] = "GenericReply"; NDKKind22[NDKKind22["Media"] = 1063] = "Media"; NDKKind22[NDKKind22["VoiceMessage"] = 1222] = "VoiceMessage"; NDKKind22[NDKKind22["VoiceReply"] = 1244] = "VoiceReply"; NDKKind22[NDKKind22["DraftCheckpoint"] = 1234] = "DraftCheckpoint"; NDKKind22[NDKKind22["Task"] = 1934] = "Task"; NDKKind22[NDKKind22["Report"] = 1984] = "Report"; NDKKind22[NDKKind22["Label"] = 1985] = "Label"; NDKKind22[NDKKind22["DVMReqTextExtraction"] = 5e3] = "DVMReqTextExtraction"; NDKKind22[NDKKind22["DVMReqTextSummarization"] = 5001] = "DVMReqTextSummarization"; NDKKind22[NDKKind22["DVMReqTextTranslation"] = 5002] = "DVMReqTextTranslation"; NDKKind22[NDKKind22["DVMReqTextGeneration"] = 5050] = "DVMReqTextGeneration"; NDKKind22[NDKKind22["DVMReqImageGeneration"] = 5100] = "DVMReqImageGeneration"; NDKKind22[NDKKind22["DVMReqTextToSpeech"] = 5250] = "DVMReqTextToSpeech"; NDKKind22[NDKKind22["DVMReqDiscoveryNostrContent"] = 5300] = "DVMReqDiscoveryNostrContent"; NDKKind22[NDKKind22["DVMReqDiscoveryNostrPeople"] = 5301] = "DVMReqDiscoveryNostrPeople"; NDKKind22[NDKKind22["DVMReqTimestamping"] = 5900] = "DVMReqTimestamping"; NDKKind22[NDKKind22["DVMEventSchedule"] = 5905] = "DVMEventSchedule"; NDKKind22[NDKKind22["DVMJobFeedback"] = 7e3] = "DVMJobFeedback"; NDKKind22[NDKKind22["Subscribe"] = 7001] = "Subscribe"; NDKKind22[NDKKind22["Unsubscribe"] = 7002] = "Unsubscribe"; NDKKind22[NDKKind22["SubscriptionReceipt"] = 7003] = "SubscriptionReceipt"; NDKKind22[NDKKind22["CashuReserve"] = 7373] = "CashuReserve"; NDKKind22[NDKKind22["CashuQuote"] = 7374] = "CashuQuote"; NDKKind22[NDKKind22["CashuToken"] = 7375] = "CashuToken"; NDKKind22[NDKKind22["CashuWalletTx"] = 7376] = "CashuWalletTx"; NDKKind22[NDKKind22["GroupAdminAddUser"] = 9e3] = "GroupAdminAddUser"; NDKKind22[NDKKind22["GroupAdminRemoveUser"] = 9001] = "GroupAdminRemoveUser"; NDKKind22[NDKKind22["GroupAdminEditMetadata"] = 9002] = "GroupAdminEditMetadata"; NDKKind22[NDKKind22["GroupAdminEditStatus"] = 9006] = "GroupAdminEditStatus"; NDKKind22[NDKKind22["GroupAdminCreateGroup"] = 9007] = "GroupAdminCreateGroup"; NDKKind22[NDKKind22["GroupAdminRequestJoin"] = 9021] = "GroupAdminRequestJoin"; NDKKind22[NDKKind22["MuteList"] = 1e4] = "MuteList"; NDKKind22[NDKKind22["PinList"] = 10001] = "PinList"; NDKKind22[NDKKind22["RelayList"] = 10002] = "RelayList"; NDKKind22[NDKKind22["BookmarkList"] = 10003] = "BookmarkList"; NDKKind22[NDKKind22["CommunityList"] = 10004] = "CommunityList"; NDKKind22[NDKKind22["PublicChatList"] = 10005] = "PublicChatList"; NDKKind22[NDKKind22["BlockRelayList"] = 10006] = "BlockRelayList"; NDKKind22[NDKKind22["SearchRelayList"] = 10007] = "SearchRelayList"; NDKKind22[NDKKind22["SimpleGroupList"] = 10009] = "SimpleGroupList"; NDKKind22[NDKKind22["RelayFeedList"] = 10012] = "RelayFeedList"; NDKKind22[NDKKind22["InterestList"] = 10015] = "InterestList"; NDKKind22[NDKKind22["CashuMintList"] = 10019] = "CashuMintList"; NDKKind22[NDKKind22["EmojiList"] = 10030] = "EmojiList"; NDKKind22[NDKKind22["DirectMessageReceiveRelayList"] = 10050] = "DirectMessageReceiveRelayList"; NDKKind22[NDKKind22["BlossomList"] = 10063] = "BlossomList"; NDKKind22[NDKKind22["NostrWaletConnectInfo"] = 13194] = "NostrWaletConnectInfo"; NDKKind22[NDKKind22["TierList"] = 17e3] = "TierList"; NDKKind22[NDKKind22["CashuWallet"] = 17375] = "CashuWallet"; NDKKind22[NDKKind22["FollowSet"] = 3e4] = "FollowSet"; NDKKind22[ NDKKind22["CategorizedPeopleList"] = 3e4 /* FollowSet */ ] = "CategorizedPeopleList"; NDKKind22[NDKKind22["CategorizedBookmarkList"] = 30001] = "CategorizedBookmarkList"; NDKKind22[NDKKind22["RelaySet"] = 30002] = "RelaySet"; NDKKind22[ NDKKind22["CategorizedRelayList"] = 30002 /* RelaySet */ ] = "CategorizedRelayList"; NDKKind22[NDKKind22["BookmarkSet"] = 30003] = "BookmarkSet"; NDKKind22[NDKKind22["CurationSet"] = 30004] = "CurationSet"; NDKKind22[NDKKind22["ArticleCurationSet"] = 30004] = "ArticleCurationSet"; NDKKind22[NDKKind22["VideoCurationSet"] = 30005] = "VideoCurationSet"; NDKKind22[NDKKind22["ImageCurationSet"] = 30006] = "ImageCurationSet"; NDKKind22[NDKKind22["InterestSet"] = 30015] = "InterestSet"; NDKKind22[ NDKKind22["InterestsList"] = 30015 /* InterestSet */ ] = "InterestsList"; NDKKind22[NDKKind22["ProjectTemplate"] = 30717] = "ProjectTemplate"; NDKKind22[NDKKind22["EmojiSet"] = 30030] = "EmojiSet"; NDKKind22[NDKKind22["ModularArticle"] = 30040] = "ModularArticle"; NDKKind22[NDKKind22["ModularArticleItem"] = 30041] = "ModularArticleItem"; NDKKind22[NDKKind22["Wiki"] = 30818] = "Wiki"; NDKKind22[NDKKind22["Draft"] = 31234] = "Draft"; NDKKind22[NDKKind22["Project"] = 31933] = "Project"; NDKKind22[NDKKind22["SubscriptionTier"] = 37001] = "SubscriptionTier"; NDKKind22[NDKKind22["EcashMintRecommendation"] = 38e3] = "EcashMintRecommendation"; NDKKind22[NDKKind22["CashuMintAnnouncement"] = 38172] = "CashuMintAnnouncement"; NDKKind22[NDKKind22["FedimintMintAnnouncement"] = 38173] = "FedimintMintAnnouncement"; NDKKind22[NDKKind22["P2POrder"] = 38383] = "P2POrder"; NDKKind22[NDKKind22["HighlightSet"] = 39802] = "HighlightSet"; NDKKind22[ NDKKind22["CategorizedHighlightList"] = 39802 /* HighlightSet */ ] = "CategorizedHighlightList"; NDKKind22[NDKKind22["Nutzap"] = 9321] = "Nutzap"; NDKKind22[NDKKind22["ZapRequest"] = 9734] = "ZapRequest"; NDKKind22[NDKKind22["Zap"] = 9735] = "Zap"; NDKKind22[NDKKind22["Highlight"] = 9802] = "Highlight"; NDKKind22[NDKKind22["ClientAuth"] = 22242] = "ClientAuth"; NDKKind22[NDKKind22["NostrWalletConnectReq"] = 23194] = "NostrWalletConnectReq"; NDKKind22[NDKKind22["NostrWalletConnectRes"] = 23195] = "NostrWalletConnectRes"; NDKKind22[NDKKind22["NostrConnect"] = 24133] = "NostrConnect"; NDKKind22[NDKKind22["BlossomUpload"] = 24242] = "BlossomUpload"; NDKKind22[NDKKind22["HttpAuth"] = 27235] = "HttpAuth"; NDKKind22[NDKKind22["ProfileBadge"] = 30008] = "ProfileBadge"; NDKKind22[NDKKind22["BadgeDefinition"] = 30009] = "BadgeDefinition"; NDKKind22[NDKKind22["MarketStall"] = 30017] = "MarketStall"; NDKKind22[NDKKind22["MarketProduct"] = 30018] = "MarketProduct"; NDKKind22[NDKKind22["Article"] = 30023] = "Article"; NDKKind22[NDKKind22["AppSpecificData"] = 30078] = "AppSpecificData"; NDKKind22[NDKKind22["Classified"] = 30402] = "Classified"; NDKKind22[NDKKind22["HorizontalVideo"] = 34235] = "HorizontalVideo"; NDKKind22[NDKKind22["VerticalVideo"] = 34236] = "VerticalVideo"; NDKKind22[NDKKind22["GroupMetadata"] = 39e3] = "GroupMetadata"; NDKKind22[NDKKind22["GroupAdmins"] = 39001] = "GroupAdmins"; NDKKind22[NDKKind22["GroupMembers"] = 39002] = "GroupMembers"; NDKKind22[NDKKind22["FollowPack"] = 39089] = "FollowPack"; NDKKind22[NDKKind22["MediaFollowPack"] = 39092] = "MediaFollowPack"; NDKKind22[NDKKind22["AppRecommendation"] = 31989] = "AppRecommendation"; NDKKind22[NDKKind22["AppHandler"] = 31990] = "AppHandler"; return NDKKind22; })(NDKKind2 || {}); NdkNutzapStatus2 = /* @__PURE__ */ ((NdkNutzapStatus22) => { NdkNutzapStatus22["INITIAL"] = "initial"; NdkNutzapStatus22["PROCESSING"] = "processing"; NdkNutzapStatus22["REDEEMED"] = "redeemed"; NdkNutzapStatus22["SPENT"] = "spent"; NdkNutzapStatus22["MISSING_PRIVKEY"] = "missing_privkey"; NdkNutzapStatus22["TEMPORARY_ERROR"] = "temporary_error"; NdkNutzapStatus22["PERMANENT_ERROR"] = "permanent_error"; NdkNutzapStatus22["INVALID_NUTZAP"] = "invalid_nutzap"; return NdkNutzapStatus22; })(NdkNutzapStatus2 || {}); DATA_URL_DEFAULT_MIME_TYPE2 = "text/plain"; DATA_URL_DEFAULT_CHARSET2 = "us-ascii"; testParameter2 = (name, filters) => filters.some((filter) => filter instanceof RegExp ? filter.test(name) : filter === name); supportedProtocols2 = /* @__PURE__ */ new Set(["https:", "http:", "file:"]); hasCustomProtocol2 = (urlString) => { try { const { protocol } = new URL(urlString); return protocol.endsWith(":") && !protocol.includes(".") && !supportedProtocols2.has(protocol); } catch { return false; } }; normalizeDataURL2 = (urlString, { stripHash }) => { const match = /^data:(?[^,]*?),(?[^#]*?)(?:#(?.*))?$/.exec(urlString); if (!match) { throw new Error(`Invalid URL: ${urlString}`); } const type = match.groups?.type ?? ""; const data = match.groups?.data ?? ""; let hash3 = match.groups?.hash ?? ""; const mediaType = type.split(";"); hash3 = stripHash ? "" : hash3; let isBase64 = false; if (mediaType[mediaType.length - 1] === "base64") { mediaType.pop(); isBase64 = true; } const mimeType = mediaType.shift()?.toLowerCase() ?? ""; const attributes = mediaType.map((attribute) => { let [key, value = ""] = attribute.split("=").map((string) => string.trim()); if (key === "charset") { value = value.toLowerCase(); if (value === DATA_URL_DEFAULT_CHARSET2) { return ""; } } return `${key}${value ? `=${value}` : ""}`; }).filter(Boolean); const normalizedMediaType = [...attributes]; if (isBase64) { normalizedMediaType.push("base64"); } if (normalizedMediaType.length > 0 || mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE2) { normalizedMediaType.unshift(mimeType); } return `data:${normalizedMediaType.join(";")},${isBase64 ? data.trim() : data}${hash3 ? `#${hash3}` : ""}`; }; NDKRelayKeepalive2 = class { /** * @param timeout - Time in milliseconds to wait before considering connection stale (default 30s) * @param onSilenceDetected - Callback when silence is detected */ constructor(timeout = 3e4, onSilenceDetected) { __publicField(this, "lastActivity", Date.now()); __publicField(this, "timer"); __publicField(this, "timeout"); __publicField(this, "isRunning", false); this.onSilenceDetected = onSilenceDetected; this.timeout = timeout; } /** * Records activity from the relay, resetting the silence timer */ recordActivity() { this.lastActivity = Date.now(); if (this.isRunning) { this.resetTimer(); } } /** * Starts monitoring for relay silence */ start() { if (this.isRunning) return; this.isRunning = true; this.lastActivity = Date.now(); this.resetTimer(); } /** * Stops monitoring for relay silence */ stop() { this.isRunning = false; if (this.timer) { clearTimeout(this.timer); this.timer = void 0; } } resetTimer() { if (this.timer) { clearTimeout(this.timer); } this.timer = setTimeout(() => { const silenceTime = Date.now() - this.lastActivity; if (silenceTime >= this.timeout) { this.onSilenceDetected(); } else { const remainingTime = this.timeout - silenceTime; this.timer = setTimeout(() => { this.onSilenceDetected(); }, remainingTime); } }, this.timeout); } }; MAX_RECONNECT_ATTEMPTS2 = 5; FLAPPING_THRESHOLD_MS2 = 1e3; NDKRelayConnectivity2 = class { constructor(ndkRelay, ndk) { __publicField(this, "ndkRelay"); __publicField(this, "ws"); __publicField(this, "_status"); __publicField(this, "timeoutMs"); __publicField(this, "connectedAt"); __publicField(this, "_connectionStats", { attempts: 0, success: 0, durations: [] }); __publicField(this, "debug"); __publicField(this, "netDebug"); __publicField(this, "connectTimeout"); __publicField(this, "reconnectTimeout"); __publicField(this, "ndk"); __publicField(this, "openSubs", /* @__PURE__ */ new Map()); __publicField(this, "openCountRequests", /* @__PURE__ */ new Map()); __publicField(this, "openEventPublishes", /* @__PURE__ */ new Map()); __publicField(this, "pendingAuthPublishes", /* @__PURE__ */ new Map()); __publicField(this, "serial", 0); __publicField(this, "baseEoseTimeout", 4400); // Keepalive and monitoring __publicField(this, "keepalive"); __publicField(this, "wsStateMonitor"); __publicField(this, "sleepDetector"); __publicField(this, "lastSleepCheck", Date.now()); __publicField(this, "lastMessageSent", Date.now()); __publicField(this, "wasIdle", false); /** * Utility functions to update the connection stats. */ __publicField(this, "updateConnectionStats", { connected: () => { this._connectionStats.success++; this._connectionStats.connectedAt = Date.now(); }, disconnected: () => { if (this._connectionStats.connectedAt) { this._connectionStats.durations.push(Date.now() - this._connectionStats.connectedAt); if (this._connectionStats.durations.length > 100) { this._connectionStats.durations.shift(); } } this._connectionStats.connectedAt = void 0; }, attempt: () => { this._connectionStats.attempts++; this._connectionStats.connectedAt = Date.now(); } }); this.ndkRelay = ndkRelay; this._status = 1; const rand = Math.floor(Math.random() * 1e3); this.debug = this.ndkRelay.debug.extend(`connectivity${rand}`); this.ndk = ndk; this.setupMonitoring(); } /** * Sets up keepalive, WebSocket state monitoring, and sleep detection */ setupMonitoring() { this.keepalive = new NDKRelayKeepalive2(12e4, async () => { this.debug("Relay silence detected, probing connection"); const isAlive = await probeRelayConnection2({ send: (msg) => this.send(JSON.stringify(msg)), once: (event, handler) => { const messageHandler = (e2) => { try { const data = JSON.parse(e2.data); if (data[0] === "EOSE" || data[0] === "EVENT" || data[0] === "NOTICE") { handler(); this.ws?.removeEventListener("message", messageHandler); } } catch { } }; this.ws?.addEventListener("message", messageHandler); } }); if (!isAlive) { this.debug("Probe failed, connection is stale"); this.handleStaleConnection(); } }); this.wsStateMonitor = setInterval(() => { if (this._status === 5) { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.debug("WebSocket died silently, reconnecting"); this.handleStaleConnection(); } } }, 5e3); this.sleepDetector = setInterval(() => { const now2 = Date.now(); const elapsed = now2 - this.lastSleepCheck; if (elapsed > 15e3) { this.debug(`Detected possible sleep/wake (${elapsed}ms gap)`); this.handlePossibleWake(); } this.lastSleepCheck = now2; }, 1e4); } /** * Handles detection of a stale connection */ handleStaleConnection() { this._status = 1; this.wasIdle = true; this.onDisconnect(); } /** * Handles possible system wake event */ handlePossibleWake() { this.debug("System wake detected, checking all connections"); this.wasIdle = true; if (this._status >= 5) { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.handleStaleConnection(); } else { probeRelayConnection2({ send: (msg) => this.send(JSON.stringify(msg)), once: (event, handler) => { const messageHandler = (e2) => { try { const data = JSON.parse(e2.data); if (data[0] === "EOSE" || data[0] === "EVENT" || data[0] === "NOTICE") { handler(); this.ws?.removeEventListener("message", messageHandler); } } catch { } }; this.ws?.addEventListener("message", messageHandler); } }).then((isAlive) => { if (!isAlive) { this.handleStaleConnection(); } }); } } } /** * Resets the reconnection state for system-wide events * Used by NDKPool when detecting system sleep/wake */ resetReconnectionState() { this.wasIdle = true; if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = void 0; } } /** * Connects to the NDK relay and handles the connection lifecycle. * * This method attempts to establish a WebSocket connection to the NDK relay specified in the `ndkRelay` object. * If the connection is successful, it updates the connection statistics, sets the connection status to `CONNECTED`, * and emits `connect` and `ready` events on the `ndkRelay` object. * * If the connection attempt fails, it handles the error by either initiating a reconnection attempt or emitting a * `delayed-connect` event on the `ndkRelay` object, depending on the `reconnect` parameter. * * @param timeoutMs - The timeout in milliseconds for the connection attempt. If not provided, the default timeout from the `ndkRelay` object is used. * @param reconnect - Indicates whether a reconnection should be attempted if the connection fails. Defaults to `true`. * @returns A Promise that resolves when the connection is established, or rejects if the connection fails. */ async connect(timeoutMs, reconnect = true) { if (this.ws && this.ws.readyState !== WebSocket.OPEN && this.ws.readyState !== WebSocket.CONNECTING) { this.debug("Cleaning up stale WebSocket connection"); try { this.ws.close(); } catch (e2) { } this.ws = void 0; this._status = 1; } if (this._status !== 2 && this._status !== 1 || this.reconnectTimeout) { this.debug( "Relay requested to be connected but was in state %s or it had a reconnect timeout", this._status ); return; } if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = void 0; } if (this.connectTimeout) { clearTimeout(this.connectTimeout); this.connectTimeout = void 0; } timeoutMs ?? (timeoutMs = this.timeoutMs); if (!this.timeoutMs && timeoutMs) this.timeoutMs = timeoutMs; if (this.timeoutMs) this.connectTimeout = setTimeout(() => this.onConnectionError(reconnect), this.timeoutMs); try { this.updateConnectionStats.attempt(); if (this._status === 1) this._status = 4; else this._status = 2; this.ws = new WebSocket(this.ndkRelay.url); this.ws.onopen = this.onConnect.bind(this); this.ws.onclose = this.onDisconnect.bind(this); this.ws.onmessage = this.onMessage.bind(this); this.ws.onerror = this.onError.bind(this); } catch (e2) { this.debug(`Failed to connect to ${this.ndkRelay.url}`, e2); this._status = 1; if (reconnect) this.handleReconnection(); else this.ndkRelay.emit("delayed-connect", 2 * 24 * 60 * 60 * 1e3); throw e2; } } /** * Disconnects the WebSocket connection to the NDK relay. * This method sets the connection status to `NDKRelayStatus.DISCONNECTING`, * attempts to close the WebSocket connection, and sets the status to * `NDKRelayStatus.DISCONNECTED` if the disconnect operation fails. */ disconnect() { this._status = 0; this.keepalive?.stop(); if (this.wsStateMonitor) { clearInterval(this.wsStateMonitor); this.wsStateMonitor = void 0; } if (this.sleepDetector) { clearInterval(this.sleepDetector); this.sleepDetector = void 0; } try { this.ws?.close(); } catch (e2) { this.debug("Failed to disconnect", e2); this._status = 1; } } /** * Handles the error that occurred when attempting to connect to the NDK relay. * If `reconnect` is `true`, this method will initiate a reconnection attempt. * Otherwise, it will emit a `delayed-connect` event on the `ndkRelay` object, * indicating that a reconnection should be attempted after a delay. * * @param reconnect - Indicates whether a reconnection should be attempted. */ onConnectionError(reconnect) { this.debug(`Error connecting to ${this.ndkRelay.url}`, this.timeoutMs); if (reconnect && !this.reconnectTimeout) { this.handleReconnection(); } } /** * Handles the connection event when the WebSocket connection is established. * This method is called when the WebSocket connection is successfully opened. * It clears any existing connection and reconnection timeouts, updates the connection statistics, * sets the connection status to `CONNECTED`, and emits `connect` and `ready` events on the `ndkRelay` object. */ onConnect() { this.netDebug?.("connected", this.ndkRelay); if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = void 0; } if (this.connectTimeout) { clearTimeout(this.connectTimeout); this.connectTimeout = void 0; } this.updateConnectionStats.connected(); this._status = 5; this.keepalive?.start(); this.wasIdle = false; this.ndkRelay.emit("connect"); this.ndkRelay.emit("ready"); } /** * Handles the disconnection event when the WebSocket connection is closed. * This method is called when the WebSocket connection is successfully closed. * It updates the connection statistics, sets the connection status to `DISCONNECTED`, * initiates a reconnection attempt if we didn't disconnect ourselves, * and emits a `disconnect` event on the `ndkRelay` object. */ onDisconnect() { this.netDebug?.("disconnected", this.ndkRelay); this.updateConnectionStats.disconnected(); this.keepalive?.stop(); this.clearPendingPublishes(new Error(`Relay ${this.ndkRelay.url} disconnected`)); if (this._status === 5) { this.handleReconnection(); } this._status = 1; this.ndkRelay.emit("disconnect"); } /** * Handles incoming messages from the NDK relay WebSocket connection. * This method is called whenever a message is received from the relay. * It parses the message data and dispatches the appropriate handling logic based on the message type. * * @param event - The MessageEvent containing the received message data. */ /** * Fast extraction of event ID from JSON string without parsing. * Returns event ID if message is EVENT type, null otherwise. * This optimization avoids expensive JSON.parse() for duplicate events. */ getEventIdFromMessage(msg) { if (msg.charCodeAt(2) !== 69 || msg.charCodeAt(3) !== 86) { return null; } const idPos = msg.indexOf('"id":"'); if (idPos === -1) { return null; } return msg.substring(idPos + 6, idPos + 70); } onMessage(event) { this.netDebug?.(event.data, this.ndkRelay, "recv"); this.keepalive?.recordActivity(); const msg = event.data; const eventId = this.getEventIdFromMessage(msg); if (eventId && this.ndk) { const seenRelays = this.ndk.subManager.seenEvents.get(eventId); if (seenRelays && seenRelays.length > 0) { this.ndk.subManager.seenEvent(eventId, this.ndkRelay); return; } } try { const data = JSON.parse(event.data); const [cmd, id, ..._rest] = data; const handler = this.ndkRelay.getProtocolHandler(cmd); if (handler) { handler(this.ndkRelay, data); return; } switch (cmd) { case "EVENT": { const so = this.openSubs.get(id); const event2 = data[2]; if (!so) { this.debug(`Received event for unknown subscription ${id}`); return; } so.onevent(event2); return; } case "COUNT": { const payload = data[2]; const cr = this.openCountRequests.get(id); if (cr) { cr.resolve(payload.count); this.openCountRequests.delete(id); } return; } case "EOSE": { const so = this.openSubs.get(id); if (!so) return; so.oneose(id); return; } case "OK": { const ok = data[2]; const reason = data[3]; const ep = this.openEventPublishes.get(id); const firstEp = ep?.pop(); if (!ep || !firstEp) { this.debug("Received OK for unknown event publish", id); return; } if (ok) { firstEp.resolve(reason); this.pendingAuthPublishes.delete(id); } else { const isAuthRequired = reason && (reason.toLowerCase().includes("auth-required") || reason.toLowerCase().includes("not authorized") || reason.toLowerCase().includes("blocked: not authorized")); if (isAuthRequired) { const event2 = this.pendingAuthPublishes.get(id); if (event2) { this.debug("Publish failed due to auth-required, will retry after auth", id); ep.push(firstEp); this.openEventPublishes.set(id, ep); } else { firstEp.reject(new Error(reason)); } } else { firstEp.reject(new Error(reason)); this.pendingAuthPublishes.delete(id); } } if (ep.length === 0) { this.openEventPublishes.delete(id); } else if (!ok && !(reason?.toLowerCase().includes("auth-required") || reason?.toLowerCase().includes("not authorized") || reason?.toLowerCase().includes("blocked: not authorized"))) { this.openEventPublishes.set(id, ep); } return; } case "CLOSED": { const so = this.openSubs.get(id); if (!so) return; so.onclosed(data[2]); return; } case "NOTICE": this.onNotice(data[1]); return; case "AUTH": { this.onAuthRequested(data[1]); return; } } } catch (error) { this.debug(`Error parsing message from ${this.ndkRelay.url}: ${error.message}`, error?.stack); return; } } /** * Handles an authentication request from the NDK relay. * * If an authentication policy is configured, it will be used to authenticate the connection. * Otherwise, the `auth` event will be emitted to allow the application to handle the authentication. * * @param challenge - The authentication challenge provided by the NDK relay. */ async onAuthRequested(challenge3) { const authPolicy = this.ndkRelay.authPolicy ?? this.ndk?.relayAuthDefaultPolicy; this.debug("Relay requested authentication", { havePolicy: !!authPolicy }); if (this._status === 7) { this.debug("Already authenticating, ignoring"); return; } this._status = 6; if (authPolicy) { if (this._status >= 5) { this._status = 7; let res; try { res = await authPolicy(this.ndkRelay, challenge3); } catch (e2) { this.debug("Authentication policy threw an error", e2); res = false; } this.debug("Authentication policy returned", !!res); if (res instanceof NDKEvent2 || res === true) { if (res instanceof NDKEvent2) { await this.auth(res); } const authenticate = async () => { if (this._status >= 5 && this._status < 8) { const event = new NDKEvent2(this.ndk); event.kind = 22242; event.tags = [ ["relay", this.ndkRelay.url], ["challenge", challenge3] ]; await event.sign(); this.auth(event).then(() => { this._status = 8; this.ndkRelay.emit("authed"); this.debug("Authentication successful"); this.retryPendingAuthPublishes(); }).catch((e2) => { this._status = 6; this.ndkRelay.emit("auth:failed", e2); this.debug("Authentication failed", e2); this.rejectPendingAuthPublishes(e2); }); } else { this.debug("Authentication failed, it changed status, status is %d", this._status); } }; if (res === true) { if (!this.ndk?.signer) { this.debug("No signer available for authentication localhost"); this.ndk?.once("signer:ready", authenticate); } else { authenticate().catch((e2) => { console.error("Error authenticating", e2); }); } } this._status = 5; this.ndkRelay.emit("authed"); } } } else { this.ndkRelay.emit("auth", challenge3); } } /** * Handles errors that occur on the WebSocket connection to the relay. * @param error - The error or event that occurred. */ onError(error) { this.debug(`WebSocket error on ${this.ndkRelay.url}:`, error); } /** * Gets the current status of the NDK relay connection. * @returns {NDKRelayStatus} The current status of the NDK relay connection. */ get status() { return this._status; } /** * Checks if the NDK relay connection is currently available. * @returns {boolean} `true` if the relay connection is in the `CONNECTED` status, `false` otherwise. */ isAvailable() { return this._status === 5; } /** * Checks if the NDK relay connection is flapping, which means the connection is rapidly * disconnecting and reconnecting. This is determined by analyzing the durations of the * last three connection attempts. If the standard deviation of the durations is less * than 1000 milliseconds, the connection is considered to be flapping. * * @returns {boolean} `true` if the connection is flapping, `false` otherwise. */ isFlapping() { const durations = this._connectionStats.durations; if (durations.length % 3 !== 0) return false; const sum = durations.reduce((a, b) => a + b, 0); const avg = sum / durations.length; const variance = durations.map((x2) => (x2 - avg) ** 2).reduce((a, b) => a + b, 0) / durations.length; const stdDev = Math.sqrt(variance); const isFlapping = stdDev < FLAPPING_THRESHOLD_MS2; return isFlapping; } /** * Handles a notice received from the NDK relay. * If the notice indicates the relay is complaining (e.g. "too many" or "maximum"), * the method disconnects from the relay and attempts to reconnect after a 2-second delay. * A debug message is logged with the relay URL and the notice text. * The "notice" event is emitted on the ndkRelay instance with the notice text. * * @param notice - The notice text received from the NDK relay. */ async onNotice(notice) { this.ndkRelay.emit("notice", notice); } /** * Attempts to reconnect to the NDK relay after a connection is lost. * This function is called recursively to handle multiple reconnection attempts. * It checks if the relay is flapping and emits a "flapping" event if so. * It then calculates a delay before the next reconnection attempt based on the number of previous attempts. * The function sets a timeout to execute the next reconnection attempt after the calculated delay. * If the maximum number of reconnection attempts is reached, a debug message is logged. * * @param attempt - The current attempt number (default is 0). */ handleReconnection(attempt = 0) { if (this.reconnectTimeout) return; if (this.isFlapping()) { this.ndkRelay.emit("flapping", this._connectionStats); this._status = 3; return; } let reconnectDelay; if (this.wasIdle) { const aggressiveDelays = [0, 1e3, 2e3, 5e3, 1e4, 3e4]; reconnectDelay = aggressiveDelays[Math.min(attempt, aggressiveDelays.length - 1)]; this.debug(`Using aggressive reconnect after idle, attempt ${attempt}, delay ${reconnectDelay}ms`); } else if (this.connectedAt) { reconnectDelay = Math.max(0, 6e4 - (Date.now() - this.connectedAt)); } else { reconnectDelay = Math.min(1e3 * 2 ** attempt, 3e4); this.debug(`Using standard backoff, attempt ${attempt}, delay ${reconnectDelay}ms`); } this.reconnectTimeout = setTimeout(() => { this.reconnectTimeout = void 0; this._status = 2; this.connect().catch((_err) => { if (attempt < MAX_RECONNECT_ATTEMPTS2) { this.handleReconnection(attempt + 1); } else { this.debug("Max reconnect attempts reached"); this.wasIdle = false; } }); }, reconnectDelay); this.ndkRelay.emit("delayed-connect", reconnectDelay); this.debug("Reconnecting in", reconnectDelay); this._connectionStats.nextReconnectAt = Date.now() + reconnectDelay; } /** * Sends a message to the NDK relay if the connection is in the CONNECTED state and the WebSocket is open. * If the connection is not in the CONNECTED state or the WebSocket is not open, logs a debug message and throws an error. * * @param message - The message to send to the NDK relay. * @throws {Error} If attempting to send on a closed relay connection. */ async send(message) { const idleTime = Date.now() - this.lastMessageSent; if (idleTime > 12e4) { this.wasIdle = true; } if (this._status >= 5 && this.ws?.readyState === WebSocket.OPEN) { this.ws?.send(message); this.netDebug?.(message, this.ndkRelay, "send"); this.lastMessageSent = Date.now(); } else { this.debug(`Not connected to ${this.ndkRelay.url} (%d), not sending message ${message}`, this._status); if (this._status >= 5 && this.ws?.readyState !== WebSocket.OPEN) { this.debug(`Stale connection detected, WebSocket state: ${this.ws?.readyState}`); this.handleStaleConnection(); } } } /** * Authenticates the NDK event by sending it to the NDK relay and returning a promise that resolves with the result. * * @param event - The NDK event to authenticate. * @returns A promise that resolves with the authentication result. */ async auth(event) { const ret = new Promise((resolve, reject) => { const val = this.openEventPublishes.get(event.id) ?? []; val.push({ resolve, reject }); this.openEventPublishes.set(event.id, val); }); this.send(`["AUTH",${JSON.stringify(event.rawEvent())}]`); return ret; } /** * Clears all pending publish promises by rejecting them with the provided error. * This is called on disconnection to prevent memory leaks and ensure promises * don't hang indefinitely. * @param error The error to reject the promises with */ clearPendingPublishes(error) { this.rejectPendingAuthPublishes(error); for (const [eventId, resolvers] of this.openEventPublishes.entries()) { while (resolvers.length > 0) { const resolver = resolvers.shift(); if (resolver) { resolver.reject(error); } } this.openEventPublishes.delete(eventId); } } /** * Retries all pending publishes that failed due to auth-required. * Called after successful authentication. */ retryPendingAuthPublishes() { if (this.pendingAuthPublishes.size === 0) return; this.debug(`Retrying ${this.pendingAuthPublishes.size} pending publishes after auth`); for (const [eventId, event] of this.pendingAuthPublishes.entries()) { this.debug(`Retrying publish for event ${eventId}`); this.send(`["EVENT",${JSON.stringify(event)}]`); } this.pendingAuthPublishes.clear(); } /** * Rejects all pending publishes that failed due to auth-required. * Called when authentication fails. */ rejectPendingAuthPublishes(error) { if (this.pendingAuthPublishes.size === 0) return; this.debug(`Rejecting ${this.pendingAuthPublishes.size} pending publishes due to auth failure`); for (const [eventId] of this.pendingAuthPublishes.entries()) { const ep = this.openEventPublishes.get(eventId); if (ep && ep.length > 0) { const resolver = ep.pop(); if (resolver) { resolver.reject(new Error(`Authentication failed: ${error.message}`)); } if (ep.length === 0) { this.openEventPublishes.delete(eventId); } } } this.pendingAuthPublishes.clear(); } /** * Publishes an NDK event to the relay and returns a promise that resolves with the result. * * @param event - The NDK event to publish. * @returns A promise that resolves with the result of the event publication. * @throws {Error} If attempting to publish on a closed relay connection. */ async publish(event) { const ret = new Promise((resolve, reject) => { const val = this.openEventPublishes.get(event.id) ?? []; if (val.length > 0) { console.warn(`Duplicate event publishing detected, you are publishing event ${event.id} twice`); } val.push({ resolve, reject }); this.openEventPublishes.set(event.id, val); }); this.pendingAuthPublishes.set(event.id, event); this.send(`["EVENT",${JSON.stringify(event)}]`); return ret; } /** * Counts the number of events that match the provided filters. * * @param filters - The filters to apply to the count request. * @param params - An optional object containing a custom id for the count request. * @returns A promise that resolves with the number of matching events. * @throws {Error} If attempting to send the count request on a closed relay connection. */ async count(filters, params) { this.serial++; const id = params?.id || `count:${this.serial}`; const ret = new Promise((resolve, reject) => { this.openCountRequests.set(id, { resolve, reject }); }); this.send(`["COUNT","${id}",${JSON.stringify(filters).substring(1)}`); return ret; } close(subId, reason) { this.send(`["CLOSE","${subId}"]`); const sub = this.openSubs.get(subId); this.openSubs.delete(subId); if (sub) sub.onclose(reason); } /** * Subscribes to the NDK relay with the provided filters and parameters. * * @param filters - The filters to apply to the subscription. * @param params - The subscription parameters, including an optional custom id. * @returns A new NDKRelaySubscription instance. */ req(relaySub) { `${this.send(`["REQ","${relaySub.subId}",${JSON.stringify(relaySub.executeFilters).substring(1)}`)}]`; this.openSubs.set(relaySub.subId, relaySub); } /** Returns the connection stats. */ get connectionStats() { return this._connectionStats; } /** Returns the relay URL */ get url() { return this.ndkRelay.url; } get connected() { return this._status >= 5 && this.ws?.readyState === WebSocket.OPEN; } }; NDKRelayPublisher2 = class { constructor(ndkRelay) { __publicField(this, "ndkRelay"); __publicField(this, "debug"); this.ndkRelay = ndkRelay; this.debug = ndkRelay.debug.extend("publisher"); } /** * Published an event to the relay; if the relay is not connected, it will * wait for the relay to connect before publishing the event. * * If the relay does not connect within the timeout, the publish operation * will fail. * @param event The event to publish * @param timeoutMs The timeout for the publish operation in milliseconds * @returns A promise that resolves when the event has been published or rejects if the operation times out */ async publish(event, timeoutMs = 2500) { let timeout; const publishConnected = () => { return new Promise((resolve, reject) => { try { this.publishEvent(event).then((_result) => { this.ndkRelay.emit("published", event); event.emit("relay:published", this.ndkRelay); resolve(true); }).catch(reject); } catch (err) { reject(err); } }); }; const timeoutPromise = new Promise((_2, reject) => { timeout = setTimeout(() => { timeout = void 0; reject(new Error(`Timeout: ${timeoutMs}ms`)); }, timeoutMs); }); const onConnectHandler = () => { publishConnected().then((result) => connectResolve(result)).catch((err) => connectReject(err)); }; let connectResolve; let connectReject; const onError = (err) => { this.ndkRelay.debug("Publish failed", err, event.id); this.ndkRelay.emit("publish:failed", event, err); event.emit("relay:publish:failed", this.ndkRelay, err); throw err; }; const onFinally = () => { if (timeout) clearTimeout(timeout); this.ndkRelay.removeListener("connect", onConnectHandler); }; if (this.ndkRelay.status >= 5) { return Promise.race([publishConnected(), timeoutPromise]).catch(onError).finally(onFinally); } if (this.ndkRelay.status <= 1) { console.warn("Relay is disconnected, trying to connect to publish an event", this.ndkRelay.url); this.ndkRelay.connect(); } else { console.warn("Relay not connected, waiting for connection to publish an event", this.ndkRelay.url); } return Promise.race([ new Promise((resolve, reject) => { connectResolve = resolve; connectReject = reject; this.ndkRelay.on("connect", onConnectHandler); }), timeoutPromise ]).catch(onError).finally(onFinally); } async publishEvent(event) { return this.ndkRelay.connectivity.publish(event.rawEvent()); } }; MAX_ITEMS2 = 3; NDKRelaySubscription2 = class { /** * * @param fingerprint The fingerprint of this subscription. */ constructor(relay, fingerprint, topSubManager) { __publicField(this, "fingerprint"); __publicField(this, "items", /* @__PURE__ */ new Map()); __publicField(this, "topSubManager"); __publicField(this, "debug"); /** * Tracks the status of this REQ. */ __publicField(this, "status", 0); __publicField(this, "onClose"); __publicField(this, "relay"); /** * Whether this subscription has reached EOSE. */ __publicField(this, "eosed", false); /** * Timeout at which this subscription will * start executing. */ __publicField(this, "executionTimer"); /** * Track the time at which this subscription will fire. */ __publicField(this, "fireTime"); /** * The delay type that the current fireTime was calculated with. */ __publicField(this, "delayType"); /** * The filters that have been executed. */ __publicField(this, "executeFilters"); __publicField(this, "id", Math.random().toString(36).substring(7)); __publicField(this, "_subId"); __publicField(this, "subIdParts", /* @__PURE__ */ new Set()); __publicField(this, "executeOnRelayReady", () => { if (this.status !== 2) return; if (this.items.size === 0) { this.debug( "No items to execute; this relay was probably too slow to respond and the caller gave up", { status: this.status, fingerprint: this.fingerprint, id: this.id, subId: this.subId } ); this.cleanup(); return; } this.debug("Executing on relay ready", { status: this.status, fingerprint: this.fingerprint, itemsSize: this.items.size, filters: formatFilters2(this.compileFilters()) }); this.status = 1; this.execute(); }); // we do it this way so that we can remove the listener __publicField(this, "reExecuteAfterAuth", (() => { const oldSubId = this.subId; this.debug("Re-executing after auth", this.items.size); if (this.eosed) { this.relay.close(this.subId); } else { this.debug( "We are abandoning an opened subscription, once it EOSE's, the handler will close it", { oldSubId } ); } this._subId = void 0; this.status = 1; this.execute(); this.debug("Re-executed after auth %s \u{1F449} %s", oldSubId, this.subId); }).bind(this)); this.relay = relay; this.topSubManager = topSubManager; this.debug = relay.debug.extend(`sub[${this.id}]`); this.fingerprint = fingerprint || Math.random().toString(36).substring(7); } get subId() { if (this._subId) return this._subId; this._subId = this.fingerprint.slice(0, 15); return this._subId; } addSubIdPart(part) { this.subIdParts.add(part); } addItem(subscription, filters) { if (this.items.has(subscription.internalId)) { return; } subscription.on("close", this.removeItem.bind(this, subscription)); this.items.set(subscription.internalId, { subscription, filters }); if (this.status !== 3) { if (subscription.subId && (!this._subId || this._subId.length < 25)) { if (this.status === 0 || this.status === 1) { this.addSubIdPart(subscription.subId); } } } switch (this.status) { case 0: this.evaluateExecutionPlan(subscription); break; case 3: break; case 1: this.evaluateExecutionPlan(subscription); break; case 4: this.debug("Subscription is closed, cannot add new items", { filters: formatFilters2(filters), subId: subscription.subId, internalId: subscription.internalId }); throw new Error("Cannot add new items to a closed subscription"); } } /** * A subscription has been closed, remove it from the list of items. * @param subscription */ removeItem(subscription) { this.items.delete(subscription.internalId); if (this.items.size === 0) { if (this.status === 0 || this.status === 1) { this.status = 4; this.cleanup(); return; } if (!this.eosed) return; this.close(); this.cleanup(); } } close() { if (this.status === 4) return; const prevStatus = this.status; this.status = 4; if (prevStatus === 3) { try { this.relay.close(this.subId); } catch (e2) { this.debug("Error closing subscription", e2, this); } } else { this.debug("Subscription wanted to close but it wasn't running, this is probably ok", { subId: this.subId, prevStatus, sub: this }); } this.cleanup(); } cleanup() { if (this.executionTimer) clearTimeout(this.executionTimer); this.relay.off("ready", this.executeOnRelayReady); this.relay.off("authed", this.reExecuteAfterAuth); if (this.onClose) this.onClose(this); } evaluateExecutionPlan(subscription) { if (!subscription.isGroupable()) { this.status = 1; this.execute(); return; } if (subscription.filters.find((filter) => !!filter.limit)) { this.executeFilters = this.compileFilters(); if (this.executeFilters.length >= 10) { this.status = 1; this.execute(); return; } } const delay = subscription.groupableDelay; const delayType = subscription.groupableDelayType; if (!delay) throw new Error("Cannot group a subscription without a delay"); if (this.status === 0) { this.schedule(delay, delayType); } else { const existingDelayType = this.delayType; const timeUntilFire = this.fireTime - Date.now(); if (existingDelayType === "at-least" && delayType === "at-least") { if (timeUntilFire < delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else if (existingDelayType === "at-least" && delayType === "at-most") { if (timeUntilFire > delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else if (existingDelayType === "at-most" && delayType === "at-most") { if (timeUntilFire > delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else if (existingDelayType === "at-most" && delayType === "at-least") { if (timeUntilFire > delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else { throw new Error(`Unknown delay type combination ${existingDelayType} ${delayType}`); } } } schedule(delay, delayType) { this.status = 1; const currentTime = Date.now(); this.fireTime = currentTime + delay; this.delayType = delayType; const timer = setTimeout(() => { this.execute(); }, delay); if (delayType === "at-least") { this.executionTimer = timer; } } finalizeSubId() { if (this.subIdParts.size > 0) { const parts = Array.from(this.subIdParts).map((part) => part.substring(0, 10)); let joined = parts.join("-"); if (joined.length > 20) { joined = joined.substring(0, 20); } this._subId = joined; } else { this._subId = this.fingerprint.slice(0, 15); } this._subId += `-${Math.random().toString(36).substring(2, 7)}`; } execute() { if (this.status !== 1) { return; } if (!this.relay.connected) { this.status = 2; this.debug("Waiting for relay to be ready", { status: this.status, id: this.subId, fingerprint: this.fingerprint, itemsSize: this.items.size }); this.relay.once("ready", this.executeOnRelayReady); return; } if (this.relay.status < 8) { this.relay.once("authed", this.reExecuteAfterAuth); } this.status = 3; this.finalizeSubId(); this.executeFilters = this.compileFilters(); this.relay.req(this); } onstart() { } onevent(event) { this.topSubManager.dispatchEvent(event, this.relay); } oneose(subId) { this.eosed = true; if (subId !== this.subId) { this.debug("Received EOSE for an abandoned subscription", subId, this.subId); this.relay.close(subId); return; } if (this.items.size === 0) { this.close(); } for (const { subscription } of this.items.values()) { subscription.eoseReceived(this.relay); if (subscription.closeOnEose) { this.removeItem(subscription); } } } onclose(_reason) { this.status = 4; } onclosed(reason) { if (!reason) return; for (const { subscription } of this.items.values()) { subscription.closedReceived(this.relay, reason); } } /** * Grabs the filters from all the subscriptions * and merges them into a single filter. */ compileFilters() { const mergedFilters = []; const filters = Array.from(this.items.values()).map((item) => item.filters); if (!filters[0]) { this.debug("\u{1F440} No filters to merge", { itemsSize: this.items.size }); return []; } const filterCount = filters[0].length; for (let i3 = 0; i3 < filterCount; i3++) { const allFiltersAtIndex = filters.map((filter) => filter[i3]); const merged = mergeFilters2(allFiltersAtIndex); mergedFilters.push(...merged); } return mergedFilters; } }; NDKRelaySubscriptionManager2 = class { /** * @param relay - The relay instance. * @param generalSubManager - The subscription manager instance. */ constructor(relay, generalSubManager) { __publicField(this, "relay"); __publicField(this, "subscriptions"); __publicField(this, "generalSubManager"); this.relay = relay; this.subscriptions = /* @__PURE__ */ new Map(); this.generalSubManager = generalSubManager; } /** * Adds a subscription to the manager. */ addSubscription(sub, filters) { let relaySub; if (!sub.isGroupable()) { relaySub = this.createSubscription(sub, filters); } else { const filterFp = filterFingerprint2(filters, sub.closeOnEose); if (filterFp) { const existingSubs = this.subscriptions.get(filterFp); relaySub = (existingSubs || []).find( (sub2) => sub2.status < 3 /* RUNNING */ ); } relaySub ?? (relaySub = this.createSubscription(sub, filters, filterFp)); } relaySub.addItem(sub, filters); } createSubscription(_sub, _filters, fingerprint) { const relaySub = new NDKRelaySubscription2(this.relay, fingerprint || null, this.generalSubManager); relaySub.onClose = this.onRelaySubscriptionClose.bind(this); const currentVal = this.subscriptions.get(relaySub.fingerprint) ?? []; this.subscriptions.set(relaySub.fingerprint, [...currentVal, relaySub]); return relaySub; } onRelaySubscriptionClose(sub) { let currentVal = this.subscriptions.get(sub.fingerprint) ?? []; if (!currentVal) { console.warn("Unexpectedly did not find a subscription with fingerprint", sub.fingerprint); } else if (currentVal.length === 1) { this.subscriptions.delete(sub.fingerprint); } else { currentVal = currentVal.filter((s) => s.id !== sub.id); this.subscriptions.set(sub.fingerprint, currentVal); } } }; NDKRelay2 = (_a2 = class extends import_tseep11.EventEmitter { constructor(url, authPolicy, ndk) { super(); __publicField(this, "url"); __publicField(this, "scores"); __publicField(this, "connectivity"); __publicField(this, "subs"); __publicField(this, "publisher"); __publicField(this, "authPolicy"); /** * Protocol handlers for custom relay message types (e.g., NEG-OPEN, NEG-MSG). * Allows external packages to handle non-standard relay messages. */ __publicField(this, "protocolHandlers", /* @__PURE__ */ new Map()); /** * Cached relay information from NIP-11. */ __publicField(this, "_relayInfo"); /** * The lowest validation ratio this relay can reach. */ __publicField(this, "lowestValidationRatio"); /** * Current validation ratio this relay is targeting. */ __publicField(this, "targetValidationRatio"); __publicField(this, "validationRatioFn"); /** * This tracks events that have been seen by this relay * with a valid signature. */ __publicField(this, "validatedEventCount", 0); /** * This tracks events that have been seen by this relay * but have not been validated. */ __publicField(this, "nonValidatedEventCount", 0); /** * Whether this relay is trusted. * * Trusted relay's events do not get their signature verified. */ __publicField(this, "trusted", false); __publicField(this, "complaining", false); __publicField(this, "debug"); __publicField(this, "req"); __publicField(this, "close"); this.url = normalizeRelayUrl2(url); this.scores = /* @__PURE__ */ new Map(); this.debug = (0, import_debug14.default)(`ndk:relay:${url}`); this.connectivity = new NDKRelayConnectivity2(this, ndk); this.connectivity.netDebug = ndk?.netDebug; this.req = this.connectivity.req.bind(this.connectivity); this.close = this.connectivity.close.bind(this.connectivity); this.subs = new NDKRelaySubscriptionManager2(this, ndk.subManager); this.publisher = new NDKRelayPublisher2(this); this.authPolicy = authPolicy; this.targetValidationRatio = ndk?.initialValidationRatio; this.lowestValidationRatio = ndk?.lowestValidationRatio; this.validationRatioFn = (ndk?.validationRatioFn ?? _a2.defaultValidationRatioUpdateFn).bind(this); this.updateValidationRatio(); if (!ndk) { console.trace("relay created without ndk"); } } updateValidationRatio() { if (this.validationRatioFn && this.validatedEventCount > 0) { const newRatio = this.validationRatioFn(this, this.validatedEventCount, this.nonValidatedEventCount); this.targetValidationRatio = newRatio; } setTimeout(() => { this.updateValidationRatio(); }, 3e4); } get status() { return this.connectivity.status; } get connectionStats() { return this.connectivity.connectionStats; } /** * Connects to the relay. */ async connect(timeoutMs, reconnect = true) { return this.connectivity.connect(timeoutMs, reconnect); } /** * Disconnects from the relay. */ disconnect() { if (this.status === 1) { return; } this.connectivity.disconnect(); } /** * Queues or executes the subscription of a specific set of filters * within this relay. * * @param subscription NDKSubscription this filters belong to. * @param filters Filters to execute */ subscribe(subscription, filters) { this.subs.addSubscription(subscription, filters); } /** * Publishes an event to the relay with an optional timeout. * * If the relay is not connected, the event will be published when the relay connects, * unless the timeout is reached before the relay connects. * * @param event The event to publish * @param timeoutMs The timeout for the publish operation in milliseconds * @returns A promise that resolves when the event has been published or rejects if the operation times out */ async publish(event, timeoutMs = 2500) { return this.publisher.publish(event, timeoutMs); } referenceTags() { return [["r", this.url]]; } addValidatedEvent() { this.validatedEventCount++; } addNonValidatedEvent() { this.nonValidatedEventCount++; } /** * The current validation ratio this relay has achieved. */ get validationRatio() { if (this.nonValidatedEventCount === 0) { return 1; } return this.validatedEventCount / (this.validatedEventCount + this.nonValidatedEventCount); } shouldValidateEvent() { if (this.trusted) { return false; } if (this.targetValidationRatio === void 0) { return true; } if (this.targetValidationRatio >= 1) return true; return Math.random() < this.targetValidationRatio; } get connected() { return this.connectivity.connected; } /** * Registers a protocol handler for a specific message type. * This allows external packages to handle custom relay messages (e.g., NIP-77 NEG-* messages). * * @param messageType The message type to handle (e.g., "NEG-OPEN", "NEG-MSG") * @param handler The function to call when a message of this type is received * * @example * ```typescript * relay.registerProtocolHandler('NEG-MSG', (relay, message) => { * console.log('Received NEG-MSG:', message); * }); * ``` */ registerProtocolHandler(messageType, handler) { this.protocolHandlers.set(messageType, handler); } /** * Unregisters a protocol handler for a specific message type. * * @param messageType The message type to stop handling */ unregisterProtocolHandler(messageType) { this.protocolHandlers.delete(messageType); } /** * Checks if a protocol handler is registered for a message type. * This is used internally by the connectivity layer to route messages. * * @internal * @param messageType The message type to check * @returns The handler function if registered, undefined otherwise */ getProtocolHandler(messageType) { return this.protocolHandlers.get(messageType); } /** * Fetches relay information (NIP-11) from the relay. * Results are cached in persistent storage when cache adapter is available (24-hour TTL). * Falls back to in-memory cache. Pass force=true to bypass all caches. * * @param force Force a fresh fetch, bypassing all caches * @returns The relay information document * @throws Error if the fetch fails * * @example * ```typescript * const info = await relay.fetchInfo(); * console.log(`Relay: ${info.name}`); * console.log(`Supported NIPs: ${info.supported_nips?.join(', ')}`); * ``` */ async fetchInfo(force = false) { const MAX_AGE = 864e5; const ndk = this.connectivity.ndk; if (!force && ndk?.cacheAdapter?.getRelayStatus) { const cached = await ndk.cacheAdapter.getRelayStatus(this.url); if (cached?.nip11 && Date.now() - cached.nip11.fetchedAt < MAX_AGE) { this._relayInfo = cached.nip11.data; return cached.nip11.data; } } if (!force && this._relayInfo) { return this._relayInfo; } this._relayInfo = await fetchRelayInformation2(this.url); if (ndk?.cacheAdapter?.updateRelayStatus) { await ndk.cacheAdapter.updateRelayStatus(this.url, { nip11: { data: this._relayInfo, fetchedAt: Date.now() } }); } return this._relayInfo; } /** * Returns cached relay information if available, undefined otherwise. * Use fetchInfo() to retrieve fresh information. */ get info() { return this._relayInfo; } }, __publicField(_a2, "defaultValidationRatioUpdateFn", (relay, validatedCount, _nonValidatedCount) => { if (relay.lowestValidationRatio === void 0 || relay.targetValidationRatio === void 0) return 1; let newRatio = relay.validationRatio; if (relay.validationRatio > relay.targetValidationRatio) { const factor = validatedCount / 100; newRatio = Math.max(relay.lowestValidationRatio, relay.validationRatio - factor); } if (newRatio < relay.validationRatio) { return newRatio; } return relay.validationRatio; }), _a2); NDKPublishError2 = class extends Error { constructor(message, errors, publishedToRelays, intendedRelaySet) { super(message); __publicField(this, "errors"); __publicField(this, "publishedToRelays"); /** * Intended relay set where the publishing was intended to happen. */ __publicField(this, "intendedRelaySet"); this.errors = errors; this.publishedToRelays = publishedToRelays; this.intendedRelaySet = intendedRelaySet; } get relayErrors() { const errors = []; for (const [relay, err] of this.errors) { errors.push(`${relay.url}: ${err}`); } return errors.join("\n"); } }; NDKRelaySet2 = class _NDKRelaySet { constructor(relays, ndk, pool) { __publicField(this, "relays"); __publicField(this, "debug"); __publicField(this, "ndk"); __publicField(this, "pool"); this.relays = relays; this.ndk = ndk; this.pool = pool ?? ndk.pool; this.debug = ndk.debug.extend("relayset"); } /** * Adds a relay to this set. */ addRelay(relay) { this.relays.add(relay); } get relayUrls() { return Array.from(this.relays).map((r) => r.url); } /** * Creates a relay set from a list of relay URLs. * * If no connection to the relay is found in the pool it will temporarily * connect to it. * * @param relayUrls - list of relay URLs to include in this set * @param ndk * @param connect - whether to connect to the relay immediately if it was already in the pool but not connected * @returns NDKRelaySet */ static fromRelayUrls(relayUrls, ndk, connect = true, pool) { pool = pool ?? ndk.pool; if (!pool) throw new Error("No pool provided"); const relays = /* @__PURE__ */ new Set(); for (const url of relayUrls) { const relay = pool.relays.get(normalizeRelayUrl2(url)); if (relay) { if (relay.status < 5 && connect) { relay.connect(); } relays.add(relay); } else { const temporaryRelay = new NDKRelay2(normalizeRelayUrl2(url), ndk?.relayAuthDefaultPolicy, ndk); pool.useTemporaryRelay(temporaryRelay, void 0, `requested from fromRelayUrls ${relayUrls}`); relays.add(temporaryRelay); } } return new _NDKRelaySet(new Set(relays), ndk, pool); } /** * Publish an event to all relays in this relay set. * * This method implements a robust mechanism for publishing events to multiple relays with * built-in handling for race conditions, timeouts, and partial failures. The implementation * uses a dual-tracking mechanism to ensure accurate reporting of which relays successfully * received an event. * * Key aspects of this implementation: * * 1. DUAL-TRACKING MECHANISM: * - Promise-based tracking: Records successes/failures from the promises returned by relay.publish() * - Event-based tracking: Listens for 'relay:published' events that indicate successful publishing * This approach ensures we don't miss successful publishes even if there are subsequent errors in * the promise chain. * * 2. RACE CONDITION HANDLING: * - If a relay emits a success event but later fails in the promise chain, we still count it as a success * - If a relay times out after successfully publishing, we still count it as a success * - All relay operations happen in parallel, with proper tracking regardless of completion order * * 3. TIMEOUT MANAGEMENT: * - Individual timeouts for each relay operation * - Proper cleanup of timeouts to prevent memory leaks * - Clear timeout error reporting * * 4. ERROR HANDLING: * - Detailed tracking of specific errors for each failed relay * - Special handling for ephemeral events (which don't expect acknowledgement) * - RequiredRelayCount parameter to control the minimum success threshold * * @param event Event to publish * @param timeoutMs Timeout in milliseconds for each relay publish operation * @param requiredRelayCount The minimum number of relays we expect the event to be published to * @returns A set of relays the event was published to * @throws {NDKPublishError} If the event could not be published to at least `requiredRelayCount` relays * @example * ```typescript * const relaySet = new NDKRelaySet(new Set([relay1, relay2]), ndk); * const publishedToRelays = await relaySet.publish(event); * // publishedToRelays can contain relay1, relay2, both, or none * // depending on which relays the event was successfully published to * if (publishedToRelays.size > 0) { * console.log("Event published to at least one relay"); * } * ``` */ async publish(event, timeoutMs, requiredRelayCount = 1) { const publishedToRelays = /* @__PURE__ */ new Set(); const errors = /* @__PURE__ */ new Map(); const isEphemeral22 = event.isEphemeral(); event.publishStatus = "pending"; const relayPublishedHandler = (relay) => { publishedToRelays.add(relay); }; event.on("relay:published", relayPublishedHandler); try { const promises = Array.from(this.relays).map((relay) => { return new Promise((resolve) => { const timeoutId = timeoutMs ? setTimeout(() => { if (!publishedToRelays.has(relay)) { errors.set(relay, new Error(`Publish timeout after ${timeoutMs}ms`)); resolve(false); } }, timeoutMs) : null; relay.publish(event, timeoutMs).then((success) => { if (timeoutId) clearTimeout(timeoutId); if (success) { publishedToRelays.add(relay); resolve(true); } else { resolve(false); } }).catch((err) => { if (timeoutId) clearTimeout(timeoutId); if (!isEphemeral22) { errors.set(relay, err); } resolve(false); }); }); }); await Promise.all(promises); if (publishedToRelays.size < requiredRelayCount) { if (!isEphemeral22) { const error = new NDKPublishError2( "Not enough relays received the event (" + publishedToRelays.size + " published, " + requiredRelayCount + " required)", errors, publishedToRelays, this ); event.publishStatus = "error"; event.publishError = error; this.ndk?.emit("event:publish-failed", event, error, this.relayUrls); throw error; } } else { event.publishStatus = "success"; event.emit("published", { relaySet: this, publishedToRelays }); } return publishedToRelays; } finally { event.off("relay:published", relayPublishedHandler); } } get size() { return this.relays.size; } }; d4 = (0, import_debug13.default)("ndk:outbox:calculate"); hashtagRegex2 = /(?<=\s|^)(#[^\s!@#$%^&*()=+./,[{\]};:'"?><]+)/g; nip22RootTags2 = /* @__PURE__ */ new Set(["A", "E", "I"]); nip22ReplyTags2 = /* @__PURE__ */ new Set(["a", "e", "i"]); DEFAULT_RELAY_COUNT2 = 2; processingQueue2 = {}; PUBKEY_REGEX2 = /^[a-f0-9]{64}$/; verifiedSignatures2 = new import_typescript_lru_cache4.LRUCache({ maxSize: 1e3, entryExpirationTimeInMS: 6e4 }); skipClientTagOnKinds2 = /* @__PURE__ */ new Set([ 0, 4, 1059, 13, 3, 9734, 5 /* EventDeletion */ ]); NDKEvent2 = class _NDKEvent extends import_tseep10.EventEmitter { constructor(ndk, event) { super(); __publicField(this, "ndk"); __publicField(this, "created_at"); __publicField(this, "content", ""); __publicField(this, "tags", []); __publicField(this, "kind"); __publicField(this, "id", ""); __publicField(this, "sig"); __publicField(this, "pubkey", ""); __publicField(this, "signatureVerified"); __publicField(this, "_author"); /** * The relay that this event was first received from. */ __publicField(this, "relay"); /** * The status of the publish operation. */ __publicField(this, "publishStatus", "success"); __publicField(this, "publishError"); __publicField(this, "serialize", serialize2.bind(this)); __publicField(this, "getEventHash", getEventHash3.bind(this)); __publicField(this, "validate", validate2.bind(this)); __publicField(this, "verifySignature", verifySignature2.bind(this)); /** * Is this event replaceable (whether parameterized or not)? * * This will return true for kind 0, 3, 10k-20k and 30k-40k */ __publicField(this, "isReplaceable", isReplaceable2.bind(this)); __publicField(this, "isEphemeral", isEphemeral2.bind(this)); __publicField(this, "isDvm", () => this.kind && this.kind >= 5e3 && this.kind <= 7e3); /** * Is this event parameterized replaceable? * * This will return true for kind 30k-40k */ __publicField(this, "isParamReplaceable", isParamReplaceable2.bind(this)); /** * Encodes a bech32 id. * * @param relays {string[]} The relays to encode in the id * @returns {string} - Encoded naddr, note or nevent. */ __publicField(this, "encode", encode2.bind(this)); __publicField(this, "encrypt", encrypt6.bind(this)); __publicField(this, "decrypt", decrypt6.bind(this)); /** * Fetch an event tagged with the given tag following relay hints if provided. * @param tag The tag to search for * @param marker The marker to use in the tag (e.g. "root") * @returns The fetched event or null if no event was found, undefined if no matching tag was found in the event * * @example * const replyEvent = await ndk.fetchEvent("nevent1qqs8x8vnycyha73grv380gmvlury4wtmx0nr9a5ds2dngqwgu87wn6gpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgq3ql2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqz4cwjd") * const originalEvent = await replyEvent.fetchTaggedEvent("e", "reply"); * console.log(replyEvent.encode() + " is a reply to event " + originalEvent?.encode()); */ __publicField(this, "fetchTaggedEvent", fetchTaggedEvent2.bind(this)); /** * Fetch the root event of the current event. * @returns The fetched root event or null if no event was found * @example * const replyEvent = await ndk.fetchEvent("nevent1qqs8x8vnycyha73grv380gmvlury4wtmx0nr9a5ds2dngqwgu87wn6gpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgq3ql2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqz4cwjd") * const rootEvent = await replyEvent.fetchRootEvent(); * console.log(replyEvent.encode() + " is a reply in the thread " + rootEvent?.encode()); */ __publicField(this, "fetchRootEvent", fetchRootEvent2.bind(this)); /** * Fetch the event the current event is replying to. * @returns The fetched reply event or null if no event was found */ __publicField(this, "fetchReplyEvent", fetchReplyEvent2.bind(this)); /** * NIP-18 reposting event. * * @param publish Whether to publish the reposted event automatically @default true * @param signer The signer to use for signing the reposted event * @returns The reposted event * * @function */ __publicField(this, "repost", repost2.bind(this)); this.ndk = ndk; this.created_at = event?.created_at; this.content = event?.content || ""; this.tags = event?.tags || []; this.id = event?.id || ""; this.sig = event?.sig; this.pubkey = event?.pubkey || ""; this.kind = event?.kind; if (event instanceof _NDKEvent) { if (this.relay) { this.relay = event.relay; this.ndk?.subManager.seenEvent(event.id, this.relay); } this.publishStatus = event.publishStatus; this.publishError = event.publishError; } } /** * The relays that this event was received from and/or successfully published to. */ get onRelays() { let res = []; if (!this.ndk) { if (this.relay) res.push(this.relay); } else { res = this.ndk.subManager.seenEvents.get(this.id) || []; } return res; } /** * Deserialize an NDKEvent from a serialized payload. * @param ndk * @param event * @returns */ static deserialize(ndk, event) { return new _NDKEvent(ndk, deserialize2(event)); } /** * Returns the event as is. */ rawEvent() { return { created_at: this.created_at, content: this.content, tags: this.tags, kind: this.kind, pubkey: this.pubkey, id: this.id, sig: this.sig }; } set author(user) { var _a72; this.pubkey = user.pubkey; this._author = user; (_a72 = this._author).ndk ?? (_a72.ndk = this.ndk); } /** * Returns an NDKUser for the author of the event. */ get author() { if (this._author) return this._author; if (!this.ndk) throw new Error("No NDK instance found"); const user = this.ndk.getUser({ pubkey: this.pubkey }); this._author = user; return user; } /** * NIP-73 tagging of external entities * @param entity to be tagged * @param type of the entity * @param markerUrl to be used as the marker URL * * @example * ```typescript * event.tagExternal("https://example.com/article/123#nostr", "url"); * event.tags => [["i", "https://example.com/123"], ["k", "https://example.com"]] * ``` * * @example tag a podcast:item:guid * ```typescript * event.tagExternal("e32b4890-b9ea-4aef-a0bf-54b787833dc5", "podcast:item:guid"); * event.tags => [["i", "podcast:item:guid:e32b4890-b9ea-4aef-a0bf-54b787833dc5"], ["k", "podcast:item:guid"]] * ``` * * @see https://github.com/nostr-protocol/nips/blob/master/73.md */ tagExternal(entity, type, markerUrl) { const iTag = ["i"]; const kTag = ["k"]; switch (type) { case "url": { const url = new URL(entity); url.hash = ""; iTag.push(url.toString()); kTag.push(`${url.protocol}//${url.host}`); break; } case "hashtag": iTag.push(`#${entity.toLowerCase()}`); kTag.push("#"); break; case "geohash": iTag.push(`geo:${entity.toLowerCase()}`); kTag.push("geo"); break; case "isbn": iTag.push(`isbn:${entity.replace(/-/g, "")}`); kTag.push("isbn"); break; case "podcast:guid": iTag.push(`podcast:guid:${entity}`); kTag.push("podcast:guid"); break; case "podcast:item:guid": iTag.push(`podcast:item:guid:${entity}`); kTag.push("podcast:item:guid"); break; case "podcast:publisher:guid": iTag.push(`podcast:publisher:guid:${entity}`); kTag.push("podcast:publisher:guid"); break; case "isan": iTag.push(`isan:${entity.split("-").slice(0, 4).join("-")}`); kTag.push("isan"); break; case "doi": iTag.push(`doi:${entity.toLowerCase()}`); kTag.push("doi"); break; default: throw new Error(`Unsupported NIP-73 entity type: ${type}`); } if (markerUrl) { iTag.push(markerUrl); } this.tags.push(iTag); this.tags.push(kTag); } /** * Tag a user with an optional marker. * @param target What is to be tagged. Can be an NDKUser, NDKEvent, or an NDKTag. * @param marker The marker to use in the tag. * @param skipAuthorTag Whether to explicitly skip adding the author tag of the event. * @param forceTag Force a specific tag to be used instead of the default "e" or "a" tag. * @param opts Optional content tagging options to control p tag behavior. * @example * ```typescript * reply.tag(opEvent, "reply"); * // reply.tags => [["e", , , "reply"]] * ``` */ tag(target, marker, skipAuthorTag, forceTag, opts) { let tags = []; const isNDKUser = target.fetchProfile !== void 0; if (isNDKUser) { forceTag ?? (forceTag = "p"); if (forceTag === "p" && opts?.pTags === false) { return; } const tag = [forceTag, target.pubkey]; if (marker) tag.push(...["", marker]); tags.push(tag); } else if (target instanceof _NDKEvent) { const event = target; skipAuthorTag ?? (skipAuthorTag = event?.pubkey === this.pubkey); tags = event.referenceTags(marker, skipAuthorTag, forceTag, opts); if (opts?.pTags !== false) { for (const pTag of event.getMatchingTags("p")) { if (!pTag[1] || !isValidPubkey2(pTag[1])) continue; if (pTag[1] === this.pubkey) continue; if (this.tags.find((t) => t[0] === "p" && t[1] === pTag[1])) continue; this.tags.push(["p", pTag[1]]); } } } else if (Array.isArray(target)) { tags = [target]; } else { throw new Error("Invalid argument", target); } this.tags = mergeTags2(this.tags, tags); } /** * Return a NostrEvent object, trying to fill in missing fields * when possible, adding tags when necessary. * @param pubkey {string} The pubkey of the user who the event belongs to. * @param opts {ContentTaggingOptions} Options for content tagging. * @returns {Promise} A promise that resolves to a NostrEvent. */ async toNostrEvent(pubkey, opts) { if (!pubkey && this.pubkey === "") { const user = await this.ndk?.signer?.user(); this.pubkey = user?.pubkey || ""; } if (!this.created_at) { this.created_at = Math.floor(Date.now() / 1e3); } const { content, tags } = await this.generateTags(opts); this.content = content || ""; this.tags = tags; try { this.id = this.getEventHash(); } catch (_e2) { } return this.rawEvent(); } /** * Get all tags with the given name * @param tagName {string} The name of the tag to search for * @returns {NDKTag[]} An array of the matching tags */ getMatchingTags(tagName, marker) { const t = this.tags.filter((tag) => tag[0] === tagName); if (marker === void 0) return t; return t.filter((tag) => tag[3] === marker); } /** * Check if the event has a tag with the given name * @param tagName * @param marker * @returns */ hasTag(tagName, marker) { return this.tags.some((tag) => tag[0] === tagName && (!marker || tag[3] === marker)); } /** * Get the first tag with the given name * @param tagName Tag name to search for * @returns The value of the first tag with the given name, or undefined if no such tag exists */ tagValue(tagName, marker) { const tags = this.getMatchingTags(tagName, marker); if (tags.length === 0) return void 0; return tags[0][1]; } /** * Gets the NIP-31 "alt" tag of the event. */ get alt() { return this.tagValue("alt"); } /** * Sets the NIP-31 "alt" tag of the event. Use this to set an alt tag so * clients that don't handle a particular event kind can display something * useful for users. */ set alt(alt) { this.removeTag("alt"); if (alt) this.tags.push(["alt", alt]); } /** * Gets the NIP-33 "d" tag of the event. */ get dTag() { return this.tagValue("d"); } /** * Sets the NIP-33 "d" tag of the event. */ set dTag(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } /** * Remove all tags with the given name (e.g. "d", "a", "p") * @param tagName Tag name(s) to search for and remove * @param marker Optional marker to check for too * * @example * Remove a tags with a "defer" marker * ```typescript * event.tags = [ * ["a", "....", "defer"], * ["a", "....", "no-defer"], * ] * * event.removeTag("a", "defer"); * * // event.tags => [["a", "....", "no-defer"]] * * @returns {void} */ removeTag(tagName, marker) { const tagNames = Array.isArray(tagName) ? tagName : [tagName]; this.tags = this.tags.filter((tag) => { const include = tagNames.includes(tag[0]); const hasMarker = marker ? tag[3] === marker : true; return !(include && hasMarker); }); } /** * Replace a tag with a new value. If not found, it will be added. * @param tag The tag to replace. * @param value The new value for the tag. */ replaceTag(tag) { this.removeTag(tag[0]); this.tags.push(tag); } /** * Sign the event if a signer is present. * * It will generate tags. * Repleacable events will have their created_at field set to the current time. * @param signer {NDKSigner} The NDKSigner to use to sign the event * @param opts {ContentTaggingOptions} Options for content tagging. * @returns {Promise} A Promise that resolves to the signature of the signed event. */ async sign(signer, opts) { this.ndk?.aiGuardrails?.event?.signing(this); if (!signer) { this.ndk?.assertSigner(); signer = this.ndk?.signer; } else { this.author = await signer.user(); } const nostrEvent = await this.toNostrEvent(void 0, opts); this.sig = await signer.sign(nostrEvent); return this.sig; } /** * * @param relaySet * @param timeoutMs * @param requiredRelayCount * @returns */ async publishReplaceable(relaySet, timeoutMs, requiredRelayCount) { this.id = ""; this.created_at = Math.floor(Date.now() / 1e3); this.sig = ""; return this.publish(relaySet, timeoutMs, requiredRelayCount); } /** * Attempt to sign and then publish an NDKEvent to a given relaySet. * If no relaySet is provided, the relaySet will be calculated by NDK. * @param relaySet {NDKRelaySet} The relaySet to publish the even to. * @param timeoutM {number} The timeout for the publish operation in milliseconds. * @param requiredRelayCount The number of relays that must receive the event for the publish to be considered successful. * @param opts {ContentTaggingOptions} Options for content tagging. * @returns A promise that resolves to the relays the event was published to. */ async publish(relaySet, timeoutMs, requiredRelayCount, opts) { if (!requiredRelayCount) requiredRelayCount = 1; if (!this.sig) await this.sign(void 0, opts); if (!this.ndk) throw new Error("NDKEvent must be associated with an NDK instance to publish"); this.ndk.aiGuardrails?.event?.publishing(this); if (!relaySet || relaySet.size === 0) { relaySet = this.ndk.devWriteRelaySet || await calculateRelaySetFromEvent2(this.ndk, this, requiredRelayCount); } if (this.kind === 5 && this.ndk.cacheAdapter?.deleteEventIds) { const eTags = this.getMatchingTags("e").map((tag) => tag[1]); this.ndk.cacheAdapter.deleteEventIds(eTags); } const rawEvent = this.rawEvent(); if (this.ndk.cacheAdapter?.addUnpublishedEvent && shouldTrackUnpublishedEvent2(this)) { try { this.ndk.cacheAdapter.addUnpublishedEvent(this, relaySet.relayUrls); } catch (e2) { console.error("Error adding unpublished event to cache", e2); } } if (this.kind === 5 && this.ndk.cacheAdapter?.deleteEventIds) { this.ndk.cacheAdapter.deleteEventIds(this.getMatchingTags("e").map((tag) => tag[1])); } this.ndk.subManager.dispatchEvent(rawEvent, void 0, true); const relays = await relaySet.publish(this, timeoutMs, requiredRelayCount); relays.forEach((relay) => this.ndk?.subManager.seenEvent(this.id, relay)); return relays; } /** * Generates tags for users, notes, and other events tagged in content. * Will also generate random "d" tag for parameterized replaceable events where needed. * @param opts {ContentTaggingOptions} Options for content tagging. * @returns {ContentTag} The tags and content of the event. */ async generateTags(opts) { let tags = []; const g = await generateContentTags2(this.content, this.tags, opts, this); const content = g.content; tags = g.tags; if (this.kind && this.isParamReplaceable()) { const dTag = this.getMatchingTags("d")[0]; if (!dTag) { const title = this.tagValue("title"); const randLength = title ? 6 : 16; let str = [...Array(randLength)].map(() => Math.random().toString(36)[2]).join(""); if (title && title.length > 0) { str = `${title.replace(/[^a-z0-9]+/gi, "-").replace(/^-|-$/g, "")}-${str}`; } tags.push(["d", str]); } } if (this.shouldAddClientTag) { const clientTag = ["client", this.ndk?.clientName ?? ""]; if (this.ndk?.clientNip89) clientTag.push(this.ndk?.clientNip89); tags.push(clientTag); } else if (this.shouldStripClientTag) { tags = tags.filter((tag) => tag[0] !== "client"); } return { content: content || "", tags }; } get shouldAddClientTag() { if (!this.ndk?.clientName && !this.ndk?.clientNip89) return false; if (skipClientTagOnKinds2.has(this.kind)) return false; if (this.isEphemeral()) return false; if (this.isReplaceable() && !this.isParamReplaceable()) return false; if (this.isDvm()) return false; if (this.hasTag("client")) return false; return true; } get shouldStripClientTag() { return skipClientTagOnKinds2.has(this.kind); } muted() { if (this.ndk?.muteFilter && this.ndk.muteFilter(this)) { return "muted"; } return null; } /** * Returns the "d" tag of a parameterized replaceable event or throws an error if the event isn't * a parameterized replaceable event. * @returns {string} the "d" tag of the event. * * @deprecated Use `dTag` instead. */ replaceableDTag() { if (this.kind && this.kind >= 3e4 && this.kind <= 4e4) { const dTag = this.getMatchingTags("d")[0]; const dTagId = dTag ? dTag[1] : ""; return dTagId; } throw new Error("Event is not a parameterized replaceable event"); } /** * Provides a deduplication key for the event. * * For kinds 0, 3, 10k-20k this will be the event : * For kinds 30k-40k this will be the event :: * For all other kinds this will be the event id */ deduplicationKey() { if (this.kind === 0 || this.kind === 3 || this.kind && this.kind >= 1e4 && this.kind < 2e4) { return `${this.kind}:${this.pubkey}`; } return this.tagId(); } /** * Returns the id of the event or, if it's a parameterized event, the generated id of the event using "d" tag, pubkey, and kind. * @returns {string} The id */ tagId() { if (this.isParamReplaceable()) { return this.tagAddress(); } return this.id; } /** * Returns a stable reference value for a replaceable event. * * Param replaceable events are returned in the expected format of `::`. * Kind-replaceable events are returned in the format of `::`. * * @returns {string} A stable reference value for replaceable events */ tagAddress() { if (this.isParamReplaceable()) { const dTagId = this.dTag ?? ""; return `${this.kind}:${this.pubkey}:${dTagId}`; } if (this.isReplaceable()) { return `${this.kind}:${this.pubkey}:`; } throw new Error("Event is not a replaceable event"); } /** * Determines the type of tag that can be used to reference this event from another event. * @returns {string} The tag type * @example * event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] }); * event.tagType(); // "a" */ tagType() { return this.isParamReplaceable() ? "a" : "e"; } /** * Get the tag that can be used to reference this event from another event. * * Consider using referenceTags() instead (unless you have a good reason to use this) * * @example * event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] }); * event.tagReference(); // ["a", "30000:pubkey:d-code"] * * event = new NDKEvent(ndk, { kind: 1, pubkey: 'pubkey', id: "eventid" }); * event.tagReference(); // ["e", "eventid"] * @returns {NDKTag} The NDKTag object referencing this event */ tagReference(marker) { let tag; if (this.isParamReplaceable()) { tag = ["a", this.tagAddress()]; } else { tag = ["e", this.tagId()]; } if (this.relay) { tag.push(this.relay.url); } else { tag.push(""); } tag.push(marker ?? ""); if (!this.isParamReplaceable()) { tag.push(this.pubkey); } return tag; } /** * Get the tags that can be used to reference this event from another event * @param marker The marker to use in the tag * @param skipAuthorTag Whether to explicitly skip adding the author tag of the event * @param forceTag Force a specific tag to be used instead of the default "e" or "a" tag * @example * event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] }); * event.referenceTags(); // [["a", "30000:pubkey:d-code"], ["e", "parent-id"]] * * event = new NDKEvent(ndk, { kind: 1, pubkey: 'pubkey', id: "eventid" }); * event.referenceTags(); // [["e", "parent-id"]] * @returns {NDKTag} The NDKTag object referencing this event */ referenceTags(marker, skipAuthorTag, forceTag, opts) { let tags = []; if (this.isParamReplaceable()) { tags = [ [forceTag ?? "a", this.tagAddress()], [forceTag ?? "e", this.id] ]; } else { tags = [[forceTag ?? "e", this.id]]; } tags = tags.map((tag) => { if (tag[0] === "e" || marker) { tag.push(this.relay?.url ?? ""); } else if (this.relay?.url) { tag.push(this.relay?.url); } return tag; }); tags.forEach((tag) => { if (tag[0] === "e") { tag.push(marker ?? ""); tag.push(this.pubkey); } else if (marker) { tag.push(marker); } }); tags = [...tags, ...this.getMatchingTags("h")]; if (!skipAuthorTag && opts?.pTags !== false) tags.push(...this.author.referenceTags()); return tags; } /** * Provides the filter that will return matching events for this event. * * @example * event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] }); * event.filter(); // { "#a": ["30000:pubkey:d-code"] } * @example * event = new NDKEvent(ndk, { kind: 1, pubkey: 'pubkey', id: "eventid" }); * event.filter(); // { "#e": ["eventid"] } * * @returns The filter that will return matching events for this event */ filter() { if (this.isParamReplaceable()) { return { "#a": [this.tagId()] }; } return { "#e": [this.tagId()] }; } nip22Filter() { if (this.isParamReplaceable()) { return { "#A": [this.tagId()] }; } return { "#E": [this.tagId()] }; } /** * Generates a deletion event of the current event * * @param reason The reason for the deletion * @param publish Whether to publish the deletion event automatically * @returns The deletion event */ async delete(reason, publish = true) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); const e2 = new _NDKEvent(this.ndk, { kind: 5, content: reason || "" }); e2.tag(this, void 0, true); e2.tags.push(["k", this.kind?.toString()]); if (publish) { this.emit("deleted"); await e2.publish(); } return e2; } /** * Establishes whether this is a NIP-70-protectede event. * @@satisfies NIP-70 */ set isProtected(val) { this.removeTag("-"); if (val) this.tags.push(["-"]); } /** * Whether this is a NIP-70-protected event. * @@satisfies NIP-70 */ get isProtected() { return this.hasTag("-"); } /** * React to an existing event * * @param content The content of the reaction */ async react(content, publish = true) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); const e2 = new _NDKEvent(this.ndk, { kind: 7, content }); e2.tag(this); if (this.kind !== 1) { e2.tags.push(["k", `${this.kind}`]); } if (publish) await e2.publish(); return e2; } /** * Checks whether the event is valid per underlying NIPs. * * This method is meant to be overridden by subclasses that implement specific NIPs * to allow the enforcement of NIP-specific validation rules. * * Otherwise, it will only check for basic event properties. * */ get isValid() { return this.validate(); } get inspect() { return JSON.stringify(this.rawEvent(), null, 4); } /** * Dump the event to console for debugging purposes. * Prints a JSON stringified version of rawEvent() with indentation * and also lists all relay URLs for onRelays. */ dump() { console.debug(JSON.stringify(this.rawEvent(), null, 4)); console.debug("Event on relays:", this.onRelays.map((relay) => relay.url).join(", ")); } /** * Creates a reply event for the current event. * * This function will use NIP-22 when appropriate (i.e. replies to non-kind:1 events). * This function does not have side-effects; it will just return an event with the appropriate tags * to generate the reply event; the caller is responsible for publishing the event. * * @param forceNip22 - Optional flag to force NIP-22 style replies (kind 1111) regardless of the original event's kind * @param opts - Optional content tagging options */ reply(forceNip22, opts) { const reply = new _NDKEvent(this.ndk); this.ndk?.aiGuardrails?.event?.creatingReply(reply); if (this.kind === 1 && !forceNip22) { reply.kind = 1; const opHasETag = this.hasTag("e"); if (opHasETag) { reply.tags = [ ...reply.tags, ...this.getMatchingTags("e"), ...this.getMatchingTags("p"), ...this.getMatchingTags("a"), ...this.referenceTags("reply", false, void 0, opts) ]; } else { reply.tag(this, "root", false, void 0, opts); } } else { reply.kind = 1111; const carryOverTags = ["A", "E", "I", "P"]; const rootTags = this.tags.filter((tag) => carryOverTags.includes(tag[0])); if (rootTags.length > 0) { const rootKind = this.tagValue("K"); reply.tags.push(...rootTags); if (rootKind) reply.tags.push(["K", rootKind]); let tag; if (this.isParamReplaceable()) { tag = ["a", this.tagAddress()]; const relayHint = this.relay?.url ?? ""; if (relayHint) tag.push(relayHint); } else { tag = ["e", this.tagId()]; const relayHint = this.relay?.url ?? ""; tag.push(relayHint); tag.push(this.pubkey); } reply.tags.push(tag); } else { let lowerTag; let upperTag; const relayHint = this.relay?.url ?? ""; if (this.isParamReplaceable()) { lowerTag = ["a", this.tagAddress(), relayHint]; upperTag = ["A", this.tagAddress(), relayHint]; } else { lowerTag = ["e", this.tagId(), relayHint, this.pubkey]; upperTag = ["E", this.tagId(), relayHint, this.pubkey]; } reply.tags.push(lowerTag); reply.tags.push(upperTag); reply.tags.push(["K", this.kind?.toString()]); if (opts?.pTags !== false && opts?.pTagOnATags !== false) { reply.tags.push(["P", this.pubkey]); } } reply.tags.push(["k", this.kind?.toString()]); if (opts?.pTags !== false) { reply.tags.push(...this.getMatchingTags("p")); reply.tags.push(["p", this.pubkey]); } } return reply; } }; untrackedUnpublishedEvents2 = /* @__PURE__ */ new Set([ 24133, 13194, 23194, 23195 /* NostrWalletConnectRes */ ]); NDKPool2 = class extends import_tseep12.EventEmitter { /** * @param relayUrls - The URLs of the relays to connect to. * @param ndk - The NDK instance. * @param opts - Options for the pool. */ constructor(relayUrls, ndk, { debug: debug92, name } = {}) { super(); // TODO: This should probably be an LRU cache __publicField(this, "_relays", /* @__PURE__ */ new Map()); __publicField(this, "status", "idle"); __publicField(this, "autoConnectRelays", /* @__PURE__ */ new Set()); __publicField(this, "debug"); __publicField(this, "temporaryRelayTimers", /* @__PURE__ */ new Map()); __publicField(this, "flappingRelays", /* @__PURE__ */ new Set()); // A map to store timeouts for each flapping relay. __publicField(this, "backoffTimes", /* @__PURE__ */ new Map()); __publicField(this, "ndk"); // System-wide disconnection detection __publicField(this, "disconnectionTimes", /* @__PURE__ */ new Map()); __publicField(this, "systemEventDetector"); __publicField(this, "_name", "unnamed"); this.debug = debug92 ?? ndk.debug.extend("pool"); if (name) this._name = name; this.ndk = ndk; this.relayUrls = relayUrls; if (this.ndk.pools) { this.ndk.pools.push(this); } } get relays() { return this._relays; } set relayUrls(urls) { this._relays.clear(); for (const relayUrl of urls) { const relay = new NDKRelay2(relayUrl, void 0, this.ndk); relay.connectivity.netDebug = this.ndk.netDebug; this.addRelay(relay); } } get name() { return this._name; } set name(name) { this._name = name; this.debug = this.debug.extend(name); } /** * Adds a relay to the pool, and sets a timer to remove it if it is not used within the specified time. * @param relay - The relay to add to the pool. * @param removeIfUnusedAfter - The time in milliseconds to wait before removing the relay from the pool after it is no longer used. */ useTemporaryRelay(relay, removeIfUnusedAfter = 3e4, filters) { const relayAlreadyInPool = this.relays.has(relay.url); if (!relayAlreadyInPool) { this.addRelay(relay); this.debug("Adding temporary relay %s for filters %o", relay.url, filters); } const existingTimer = this.temporaryRelayTimers.get(relay.url); if (existingTimer) { clearTimeout(existingTimer); } if (!relayAlreadyInPool || existingTimer) { const timer = setTimeout(() => { if (this.ndk.explicitRelayUrls?.includes(relay.url)) return; this.removeRelay(relay.url); }, removeIfUnusedAfter); this.temporaryRelayTimers.set(relay.url, timer); } } /** * Adds a relay to the pool. * * @param relay - The relay to add to the pool. * @param connect - Whether or not to connect to the relay. */ addRelay(relay, connect = true) { const isAlreadyInPool = this.relays.has(relay.url); const isCustomRelayUrl = relay.url.includes("/npub1"); let reconnect = true; const relayUrl = relay.url; if (isAlreadyInPool) return; if (this.ndk.relayConnectionFilter && !this.ndk.relayConnectionFilter(relayUrl)) { this.debug(`Refusing to add relay ${relayUrl}: blocked by relayConnectionFilter`); return; } if (isCustomRelayUrl) { this.debug(`Refusing to add relay ${relayUrl}: is a filter relay`); return; } if (this.ndk.cacheAdapter?.getRelayStatus) { const infoOrPromise = this.ndk.cacheAdapter.getRelayStatus(relayUrl); const info = infoOrPromise instanceof Promise ? void 0 : infoOrPromise; if (info?.dontConnectBefore) { if (info.dontConnectBefore > Date.now()) { const delay = info.dontConnectBefore - Date.now(); this.debug(`Refusing to add relay ${relayUrl}: delayed connect for ${delay}ms`); setTimeout(() => { this.addRelay(relay, connect); }, delay); return; } reconnect = false; } } const noticeHandler = (notice) => this.emit("notice", relay, notice); const connectHandler = () => this.handleRelayConnect(relayUrl); const readyHandler = () => this.handleRelayReady(relay); const disconnectHandler = () => { this.recordDisconnection(relay); this.emit("relay:disconnect", relay); }; const flappingHandler = () => this.handleFlapping(relay); const authHandler = (challenge3) => this.emit("relay:auth", relay, challenge3); const authedHandler = () => this.emit("relay:authed", relay); relay.off("notice", noticeHandler); relay.off("connect", connectHandler); relay.off("ready", readyHandler); relay.off("disconnect", disconnectHandler); relay.off("flapping", flappingHandler); relay.off("auth", authHandler); relay.off("authed", authedHandler); relay.on("notice", noticeHandler); relay.on("connect", connectHandler); relay.on("ready", readyHandler); relay.on("disconnect", disconnectHandler); relay.on("flapping", flappingHandler); relay.on("auth", authHandler); relay.on("authed", authedHandler); relay.on("delayed-connect", (delay) => { if (this.ndk.cacheAdapter?.updateRelayStatus) { this.ndk.cacheAdapter.updateRelayStatus(relay.url, { dontConnectBefore: Date.now() + delay }); } }); this._relays.set(relayUrl, relay); if (connect) this.autoConnectRelays.add(relayUrl); if (connect && this.status === "active") { this.emit("relay:connecting", relay); relay.connect(void 0, reconnect).catch((e2) => { this.debug(`Failed to connect to relay ${relayUrl}`, e2); }); } } /** * Removes a relay from the pool. * @param relayUrl - The URL of the relay to remove. * @returns {boolean} True if the relay was removed, false if it was not found. */ removeRelay(relayUrl) { const relay = this.relays.get(relayUrl); if (relay) { relay.disconnect(); this.relays.delete(relayUrl); this.autoConnectRelays.delete(relayUrl); this.emit("relay:disconnect", relay); return true; } const existingTimer = this.temporaryRelayTimers.get(relayUrl); if (existingTimer) { clearTimeout(existingTimer); this.temporaryRelayTimers.delete(relayUrl); } return false; } /** * Checks whether a relay is already connected in the pool. */ isRelayConnected(url) { const normalizedUrl = normalizeRelayUrl2(url); const relay = this.relays.get(normalizedUrl); if (!relay) return false; return relay.status === 5; } /** * Fetches a relay from the pool, or creates a new one if it does not exist. * * New relays will be attempted to be connected. */ getRelay(url, connect = true, temporary = false, filters) { let relay = this.relays.get(normalizeRelayUrl2(url)); if (!relay) { relay = new NDKRelay2(url, void 0, this.ndk); relay.connectivity.netDebug = this.ndk.netDebug; if (temporary) { this.useTemporaryRelay(relay, 3e4, filters); } else { this.addRelay(relay, connect); } } return relay; } handleRelayConnect(relayUrl) { const relay = this.relays.get(relayUrl); if (!relay) { console.error("NDK BUG: relay not found in pool", { relayUrl }); return; } this.emit("relay:connect", relay); if (this.stats().connected === this.relays.size) { this.emit("connect"); } } handleRelayReady(relay) { this.emit("relay:ready", relay); } /** * Attempts to establish a connection to each relay in the pool. * * @async * @param {number} [timeoutMs] - Optional timeout in milliseconds for each connection attempt. * @returns {Promise} A promise that resolves when all connection attempts have completed. * @throws {Error} If any of the connection attempts result in an error or timeout. */ async connect(timeoutMs) { this.status = "active"; this.debug(`Connecting to ${this.relays.size} relays${timeoutMs ? `, timeout ${timeoutMs}ms` : ""}...`); const relaysToConnect = Array.from(this.autoConnectRelays.keys()).map((url) => this.relays.get(url)).filter((relay) => !!relay); for (const relay of relaysToConnect) { if (relay.status !== 5 && relay.status !== 4) { this.emit("relay:connecting", relay); relay.connect().catch((e2) => { this.debug(`Failed to connect to relay ${relay.url}: ${e2 ?? "No reason specified"}`); }); } } const allConnected = () => relaysToConnect.every( (r) => r.status === 5 /* CONNECTED */ ); const allConnectedPromise = new Promise((resolve) => { if (allConnected()) { resolve(); return; } const listeners = []; for (const relay of relaysToConnect) { const handler = () => { if (allConnected()) { for (let i3 = 0; i3 < relaysToConnect.length; i3++) { relaysToConnect[i3].off("connect", listeners[i3]); } resolve(); } }; listeners.push(handler); relay.on("connect", handler); } }); const timeoutPromise = typeof timeoutMs === "number" ? new Promise((resolve) => setTimeout(resolve, timeoutMs)) : new Promise(() => { }); await Promise.race([allConnectedPromise, timeoutPromise]); } checkOnFlappingRelays() { const flappingRelaysCount = this.flappingRelays.size; const totalRelays = this.relays.size; if (flappingRelaysCount / totalRelays >= 0.8) { for (const relayUrl of this.flappingRelays) { this.backoffTimes.set(relayUrl, 0); } } } /** * Records when a relay disconnects to detect system-wide events */ recordDisconnection(relay) { const now2 = Date.now(); this.disconnectionTimes.set(relay.url, now2); for (const [url, time] of this.disconnectionTimes.entries()) { if (now2 - time > 1e4) { this.disconnectionTimes.delete(url); } } this.checkForSystemWideDisconnection(); } /** * Checks if multiple relays disconnected simultaneously, indicating a system event */ checkForSystemWideDisconnection() { const now2 = Date.now(); const recentDisconnections = []; for (const time of this.disconnectionTimes.values()) { if (now2 - time < 5e3) { recentDisconnections.push(time); } } if (recentDisconnections.length > this.relays.size / 2 && this.relays.size > 1) { this.debug( `System-wide disconnection detected: ${recentDisconnections.length}/${this.relays.size} relays disconnected` ); this.handleSystemWideReconnection(); } } /** * Handles system-wide reconnection (e.g., after sleep/wake or network change) */ handleSystemWideReconnection() { if (this.systemEventDetector) { this.debug("System-wide reconnection already in progress, skipping"); return; } this.debug("Initiating system-wide reconnection with reset backoff"); this.systemEventDetector = setTimeout(() => { this.systemEventDetector = void 0; }, 1e4); for (const relay of this.relays.values()) { if (relay.connectivity) { relay.connectivity.resetReconnectionState(); if (relay.status !== 5 && relay.status !== 4) { relay.connect().catch((e2) => { this.debug(`Failed to reconnect relay ${relay.url} after system event: ${e2}`); }); } } } this.disconnectionTimes.clear(); } handleFlapping(relay) { this.debug(`Relay ${relay.url} is flapping`); let currentBackoff = this.backoffTimes.get(relay.url) || 5e3; currentBackoff = currentBackoff * 2; this.backoffTimes.set(relay.url, currentBackoff); this.debug(`Backoff time for ${relay.url} is ${currentBackoff}ms`); setTimeout(() => { this.debug(`Attempting to reconnect to ${relay.url}`); this.emit("relay:connecting", relay); relay.connect(); this.checkOnFlappingRelays(); }, currentBackoff); relay.disconnect(); this.emit("flapping", relay); } size() { return this.relays.size; } /** * Returns the status of each relay in the pool. * @returns {NDKPoolStats} An object containing the number of relays in each status. */ stats() { const stats = { total: 0, connected: 0, disconnected: 0, connecting: 0 }; for (const relay of this.relays.values()) { stats.total++; if (relay.status === 5) { stats.connected++; } else if (relay.status === 1) { stats.disconnected++; } else if (relay.status === 4) { stats.connecting++; } } return stats; } connectedRelays() { return Array.from(this.relays.values()).filter( (relay) => relay.status >= 5 /* CONNECTED */ ); } permanentAndConnectedRelays() { return Array.from(this.relays.values()).filter( (relay) => relay.status >= 5 && !this.temporaryRelayTimers.has(relay.url) ); } /** * Get a list of all relay urls in the pool. */ urls() { return Array.from(this.relays.keys()); } }; NDKDVMJobFeedback2 = (_a3 = class extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 7e3); } static async from(event) { const e2 = new _a3(event.ndk, event.rawEvent()); if (e2.encrypted) await e2.dvmDecrypt(); return e2; } get status() { return this.tagValue("status"); } set status(status) { this.removeTag("status"); if (status !== void 0) { this.tags.push(["status", status]); } } get encrypted() { return !!this.getMatchingTags("encrypted")[0]; } async dvmDecrypt() { await this.decrypt(); const decryptedContent = JSON.parse(this.content); this.tags.push(...decryptedContent); } }, __publicField(_a3, "kind", 7e3), __publicField(_a3, "kinds", [ 7e3 /* DVMJobFeedback */ ]), _a3); NDKCashuMintList2 = (_a4 = class extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); __publicField(this, "_p2pk"); this.kind ?? (this.kind = 10019); } static from(event) { return new _a4(event.ndk, event); } set relays(urls) { this.tags = this.tags.filter((t) => t[0] !== "relay"); for (const url of urls) { this.tags.push(["relay", url]); } } get relays() { const r = []; for (const tag of this.tags) { if (tag[0] === "relay") { r.push(tag[1]); } } return r; } set mints(urls) { this.tags = this.tags.filter((t) => t[0] !== "mint"); for (const url of urls) { this.tags.push(["mint", url]); } } get mints() { const r = []; for (const tag of this.tags) { if (tag[0] === "mint") { r.push(tag[1]); } } return Array.from(new Set(r)); } get p2pk() { if (this._p2pk) { return this._p2pk; } this._p2pk = this.tagValue("pubkey") ?? this.pubkey; return this._p2pk; } set p2pk(pubkey) { this._p2pk = pubkey; this.removeTag("pubkey"); if (pubkey) { this.tags.push(["pubkey", pubkey]); } } get relaySet() { return NDKRelaySet2.fromRelayUrls(this.relays, this.ndk); } }, __publicField(_a4, "kind", 10019), __publicField(_a4, "kinds", [ 10019 /* CashuMintList */ ]), _a4); NDKArticle2 = (_a5 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 30023); } /** * Creates a NDKArticle from an existing NDKEvent. * * @param event NDKEvent to create the NDKArticle from. * @returns NDKArticle */ static from(event) { return new _a5(event.ndk, event); } /** * Getter for the article title. * * @returns {string | undefined} - The article title if available, otherwise undefined. */ get title() { return this.tagValue("title"); } /** * Setter for the article title. * * @param {string | undefined} title - The title to set for the article. */ set title(title) { this.removeTag("title"); if (title) this.tags.push(["title", title]); } /** * Getter for the article image. * * @returns {string | undefined} - The article image if available, otherwise undefined. */ get image() { return this.tagValue("image"); } /** * Setter for the article image. * * @param {string | undefined} image - The image to set for the article. */ set image(image) { this.removeTag("image"); if (image) this.tags.push(["image", image]); } get summary() { return this.tagValue("summary"); } set summary(summary) { this.removeTag("summary"); if (summary) this.tags.push(["summary", summary]); } /** * Getter for the article's publication timestamp. * * @returns {number | undefined} - The Unix timestamp of when the article was published or undefined. */ get published_at() { const tag = this.tagValue("published_at"); if (tag) { let val = Number.parseInt(tag); if (val > 1e12) { val = Math.floor(val / 1e3); } return val; } return void 0; } /** * Setter for the article's publication timestamp. * * @param {number | undefined} timestamp - The Unix timestamp to set for the article's publication date. */ set published_at(timestamp) { this.removeTag("published_at"); if (timestamp !== void 0) { this.tags.push(["published_at", timestamp.toString()]); } } /** * Generates content tags for the article. * * This method first checks and sets the publication date if not available, * and then generates content tags based on the base NDKEvent class. * * @returns {ContentTag} - The generated content tags. */ async generateTags() { super.generateTags(); if (!this.published_at) { this.published_at = this.created_at; } return super.generateTags(); } /** * Getter for the article's URL. * * @returns {string | undefined} - The article's URL if available, otherwise undefined. */ get url() { return this.tagValue("url"); } /** * Setter for the article's URL. * * @param {string | undefined} url - The URL to set for the article. */ set url(url) { if (url) { this.tags.push(["url", url]); } else { this.removeTag("url"); } } }, __publicField(_a5, "kind", 30023), __publicField(_a5, "kinds", [ 30023 /* Article */ ]), _a5); NDKBlossomList2 = (_a6 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 10063); } static from(ndkEvent) { return new _a6(ndkEvent.ndk, ndkEvent.rawEvent()); } /** * Returns all Blossom servers in the list */ get servers() { return this.tags.filter((tag) => tag[0] === "server").map((tag) => tag[1]); } /** * Sets the list of Blossom servers */ set servers(servers) { this.tags = this.tags.filter((tag) => tag[0] !== "server"); for (const server of servers) { this.tags.push(["server", server]); } } /** * Returns the default Blossom server (first in the list) */ get default() { const servers = this.servers; return servers.length > 0 ? servers[0] : void 0; } /** * Sets the default Blossom server by moving it to the beginning of the list */ set default(server) { if (!server) return; const currentServers = this.servers; const filteredServers = currentServers.filter((s) => s !== server); this.servers = [server, ...filteredServers]; } /** * Adds a server to the list if it doesn't already exist */ addServer(server) { if (!server) return; const currentServers = this.servers; if (!currentServers.includes(server)) { this.servers = [...currentServers, server]; } } /** * Removes a server from the list */ removeServer(server) { if (!server) return; const currentServers = this.servers; this.servers = currentServers.filter((s) => s !== server); } }, __publicField(_a6, "kind", 10063), __publicField(_a6, "kinds", [ 10063 /* BlossomList */ ]), _a6); NDKFedimintMint2 = (_a7 = class extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 38173); } static async from(event) { const mint = new _a7(event.ndk, event); return mint; } /** * The federation ID */ get identifier() { return this.tagValue("d"); } set identifier(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } /** * Invite codes (multiple allowed) */ get inviteCodes() { return this.getMatchingTags("u").map((t) => t[1]); } set inviteCodes(values) { this.removeTag("u"); for (const value of values) { this.tags.push(["u", value]); } } /** * Supported modules */ get modules() { return this.getMatchingTags("modules").map((t) => t[1]); } set modules(values) { this.removeTag("modules"); for (const value of values) { this.tags.push(["modules", value]); } } /** * Network (mainnet/testnet/signet/regtest) */ get network() { return this.tagValue("n"); } set network(value) { this.removeTag("n"); if (value) this.tags.push(["n", value]); } /** * Optional metadata */ get metadata() { if (!this.content) return void 0; try { return JSON.parse(this.content); } catch { return void 0; } } set metadata(value) { if (value) { this.content = JSON.stringify(value); } else { this.content = ""; } } }, __publicField(_a7, "kind", 38173), __publicField(_a7, "kinds", [ 38173 /* FedimintMintAnnouncement */ ]), _a7); NDKCashuMintAnnouncement2 = (_a8 = class extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 38172); } static async from(event) { const mint = new _a8(event.ndk, event); return mint; } /** * The mint's identifier (pubkey) */ get identifier() { return this.tagValue("d"); } set identifier(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } /** * The mint URL */ get url() { return this.tagValue("u"); } set url(value) { this.removeTag("u"); if (value) this.tags.push(["u", value]); } /** * Supported NUT protocols */ get nuts() { return this.getMatchingTags("nuts").map((t) => t[1]); } set nuts(values) { this.removeTag("nuts"); for (const value of values) { this.tags.push(["nuts", value]); } } /** * Network (mainnet/testnet/signet/regtest) */ get network() { return this.tagValue("n"); } set network(value) { this.removeTag("n"); if (value) this.tags.push(["n", value]); } /** * Optional metadata */ get metadata() { if (!this.content) return void 0; try { return JSON.parse(this.content); } catch { return void 0; } } set metadata(value) { if (value) { this.content = JSON.stringify(value); } else { this.content = ""; } } }, __publicField(_a8, "kind", 38172), __publicField(_a8, "kinds", [ 38172 /* CashuMintAnnouncement */ ]), _a8); NDKMintRecommendation2 = (_a9 = class extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 38e3); } static async from(event) { const recommendation = new _a9(event.ndk, event); return recommendation; } /** * Event kind being recommended (38173 for Fedimint or 38172 for Cashu) */ get recommendedKind() { const value = this.tagValue("k"); return value ? Number(value) : void 0; } set recommendedKind(value) { this.removeTag("k"); if (value) this.tags.push(["k", value.toString()]); } /** * Identifier for the recommended mint event */ get identifier() { return this.tagValue("d"); } set identifier(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } /** * Mint connection URLs/invite codes (multiple allowed) */ get urls() { return this.getMatchingTags("u").map((t) => t[1]); } set urls(values) { this.removeTag("u"); for (const value of values) { this.tags.push(["u", value]); } } /** * Pointers to specific mint events * Returns array of {kind, identifier, relay} objects */ get mintEventPointers() { return this.getMatchingTags("a").map((t) => ({ kind: Number(t[1].split(":")[0]), identifier: t[1].split(":")[2], relay: t[2] })); } /** * Add a pointer to a specific mint event */ addMintEventPointer(kind, pubkey, identifier, relay) { const aTag = [`a`, `${kind}:${pubkey}:${identifier}`]; if (relay) aTag.push(relay); this.tags.push(aTag); } /** * Review/recommendation text */ get review() { return this.content; } set review(value) { this.content = value; } }, __publicField(_a9, "kind", 38e3), __publicField(_a9, "kinds", [ 38e3 /* EcashMintRecommendation */ ]), _a9); NDKClassified2 = (_a10 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 30402); } /** * Creates a NDKClassified from an existing NDKEvent. * * @param event NDKEvent to create the NDKClassified from. * @returns NDKClassified */ static from(event) { return new _a10(event.ndk, event); } /** * Getter for the classified title. * * @returns {string | undefined} - The classified title if available, otherwise undefined. */ get title() { return this.tagValue("title"); } /** * Setter for the classified title. * * @param {string | undefined} title - The title to set for the classified. */ set title(title) { this.removeTag("title"); if (title) this.tags.push(["title", title]); } /** * Getter for the classified summary. * * @returns {string | undefined} - The classified summary if available, otherwise undefined. */ get summary() { return this.tagValue("summary"); } /** * Setter for the classified summary. * * @param {string | undefined} summary - The summary to set for the classified. */ set summary(summary) { this.removeTag("summary"); if (summary) this.tags.push(["summary", summary]); } /** * Getter for the classified's publication timestamp. * * @returns {number | undefined} - The Unix timestamp of when the classified was published or undefined. */ get published_at() { const tag = this.tagValue("published_at"); if (tag) { return Number.parseInt(tag); } return void 0; } /** * Setter for the classified's publication timestamp. * * @param {number | undefined} timestamp - The Unix timestamp to set for the classified's publication date. */ set published_at(timestamp) { this.removeTag("published_at"); if (timestamp !== void 0) { this.tags.push(["published_at", timestamp.toString()]); } } /** * Getter for the classified location. * * @returns {string | undefined} - The classified location if available, otherwise undefined. */ get location() { return this.tagValue("location"); } /** * Setter for the classified location. * * @param {string | undefined} location - The location to set for the classified. */ set location(location2) { this.removeTag("location"); if (location2) this.tags.push(["location", location2]); } /** * Getter for the classified price. * * @returns {NDKClassifiedPriceTag | undefined} - The classified price if available, otherwise undefined. */ get price() { const priceTag = this.tags.find((tag) => tag[0] === "price"); if (priceTag) { return { amount: Number.parseFloat(priceTag[1]), currency: priceTag[2], frequency: priceTag[3] }; } return void 0; } /** * Setter for the classified price. * * @param price - The price to set for the classified. */ set price(priceTag) { if (typeof priceTag === "string") { priceTag = { amount: Number.parseFloat(priceTag) }; } if (priceTag?.amount) { const tag = ["price", priceTag.amount.toString()]; if (priceTag.currency) tag.push(priceTag.currency); if (priceTag.frequency) tag.push(priceTag.frequency); this.tags.push(tag); } else { this.removeTag("price"); } } /** * Generates content tags for the classified. * * This method first checks and sets the publication date if not available, * and then generates content tags based on the base NDKEvent class. * * @returns {ContentTag} - The generated content tags. */ async generateTags() { super.generateTags(); if (!this.published_at) { this.published_at = this.created_at; } return super.generateTags(); } }, __publicField(_a10, "kind", 30402), __publicField(_a10, "kinds", [ 30402 /* Classified */ ]), _a10); NDKDraft2 = (_a11 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_event"); /** * Can be used to include a different pubkey as part of the draft. * This is useful when we want to make the draft a proposal for a different user to publish. */ __publicField(this, "counterparty"); this.kind ?? (this.kind = 31234); } static from(event) { return new _a11(event.ndk, event); } /** * Sets an identifier (i.e. d-tag) */ set identifier(id) { this.removeTag("d"); this.tags.push(["d", id]); } get identifier() { return this.dTag; } /** * Event that is to be saved. */ set event(e2) { if (!(e2 instanceof NDKEvent2)) this._event = new NDKEvent2(void 0, e2); else this._event = e2; this.prepareEvent(); } /** * Marks the event as a checkpoint for another draft event. */ set checkpoint(parent) { if (parent) { this.tags.push(parent.tagReference()); this.kind = 1234; } else { this.removeTag("a"); this.kind = 31234; } } get isCheckpoint() { return this.kind === 1234; } get isProposal() { const pTag = this.tagValue("p"); return !!pTag && pTag !== this.pubkey; } /** * Gets the event. * @param param0 * @returns NDKEvent of the draft event or null if the draft event has been deleted (emptied). */ async getEvent(signer) { if (this._event) return this._event; signer ?? (signer = this.ndk?.signer); if (!signer) throw new Error("No signer available"); if (this.content && this.content.length > 0) { try { const ownPubkey = signer.pubkey; const pubkeys = [this.tagValue("p"), this.pubkey].filter(Boolean); const counterpartyPubkey = pubkeys.find((pubkey) => pubkey !== ownPubkey); let user; user = new NDKUser2({ pubkey: counterpartyPubkey ?? ownPubkey }); await this.decrypt(user, signer); const payload = JSON.parse(this.content); this._event = await wrapEvent2(new NDKEvent2(this.ndk, payload)); return this._event; } catch (e2) { console.error(e2); return void 0; } } else { return null; } } prepareEvent() { if (!this._event) throw new Error("No event has been provided"); this.removeTag("k"); if (this._event.kind) this.tags.push(["k", this._event.kind.toString()]); this.content = JSON.stringify(this._event.rawEvent()); } /** * Generates draft event. * * @param signer: Optional signer to encrypt with * @param publish: Whether to publish, optionally specifying relaySet to publish to */ async save({ signer, publish, relaySet }) { signer ?? (signer = this.ndk?.signer); if (!signer) throw new Error("No signer available"); const user = this.counterparty || await signer.user(); await this.encrypt(user, signer); if (this.counterparty) { const pubkey = this.counterparty.pubkey; this.removeTag("p"); this.tags.push(["p", pubkey]); } if (publish === false) return; return this.publishReplaceable(relaySet); } }, __publicField(_a11, "kind", 31234), __publicField(_a11, "kinds", [ 31234, 1234 /* DraftCheckpoint */ ]), _a11); NDKFollowPack2 = (_a12 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 39089); } /** * Converts a generic NDKEvent to an NDKFollowPack. */ static from(ndkEvent) { return new _a12(ndkEvent.ndk, ndkEvent); } /** * Gets the title from the tags. */ get title() { return this.tagValue("title"); } /** * Sets the title tag. */ set title(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } /** * Gets the image URL from the tags. */ /** * Gets the image URL from the tags. * Looks for an imeta tag first (returns its url), then falls back to the image tag. */ get image() { const imetaTag = this.tags.find((tag) => tag[0] === "imeta"); if (imetaTag) { const imeta = mapImetaTag2(imetaTag); if (imeta.url) return imeta.url; } return this.tagValue("image"); } /** * Sets the image URL tag. */ /** * Sets the image tag. * Accepts a string (URL) or an NDKImetaTag. * If given an NDKImetaTag, sets both the imeta tag and the image tag (using the url). * If undefined, removes both tags. */ set image(value) { this.tags = this.tags.filter((tag) => tag[0] !== "imeta" && tag[0] !== "image"); if (typeof value === "string") { if (value !== void 0) { this.tags.push(["image", value]); } } else if (value && typeof value === "object") { this.tags.push(imetaTagToTag2(value)); if (value.url) { this.tags.push(["image", value.url]); } } } /** * Gets all pubkeys from p tags. */ get pubkeys() { return Array.from( new Set(this.tags.filter((tag) => tag[0] === "p" && tag[1] && isValidPubkey2(tag[1])).map((tag) => tag[1])) ); } /** * Sets the pubkeys (replaces all p tags). */ set pubkeys(pubkeys) { this.tags = this.tags.filter((tag) => tag[0] !== "p"); for (const pubkey of pubkeys) { this.tags.push(["p", pubkey]); } } /** * Gets the description from the tags. */ get description() { return this.tagValue("description"); } /** * Sets the description tag. */ set description(value) { this.removeTag("description"); if (value) this.tags.push(["description", value]); } }, __publicField(_a12, "kind", 39089), __publicField(_a12, "kinds", [ 39089, 39092 /* MediaFollowPack */ ]), _a12); NDKHighlight2 = (_a13 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_article"); this.kind ?? (this.kind = 9802); } static from(event) { return new _a13(event.ndk, event); } get url() { return this.tagValue("r"); } /** * Context tag. */ set context(context) { if (context === void 0) { this.tags = this.tags.filter(([tag, _value]) => tag !== "context"); } else { this.tags = this.tags.filter(([tag, _value]) => tag !== "context"); this.tags.push(["context", context]); } } get context() { return this.tags.find(([tag, _value]) => tag === "context")?.[1] ?? void 0; } /** * Will return the article URL or NDKEvent if they have already been * set (it won't attempt to load remote events) */ get article() { return this._article; } /** * Article the highlight is coming from. * * @param article Article URL or NDKEvent. */ set article(article) { this._article = article; if (typeof article === "string") { this.tags.push(["r", article]); } else { this.tag(article); } } getArticleTag() { return this.getMatchingTags("a")[0] || this.getMatchingTags("e")[0] || this.getMatchingTags("r")[0]; } async getArticle() { if (this._article !== void 0) return this._article; let taggedBech32; const articleTag = this.getArticleTag(); if (!articleTag) return void 0; switch (articleTag[0]) { case "a": { const [kind, pubkey, identifier] = articleTag[1].split(":"); taggedBech32 = import_nostr_tools16.nip19.naddrEncode({ kind: Number.parseInt(kind), pubkey, identifier }); break; } case "e": taggedBech32 = import_nostr_tools16.nip19.noteEncode(articleTag[1]); break; case "r": this._article = articleTag[1]; break; } if (taggedBech32) { let a = await this.ndk?.fetchEvent(taggedBech32); if (a) { if (a.kind === 30023) { a = NDKArticle2.from(a); } this._article = a; } } return this._article; } }, __publicField(_a13, "kind", 9802), __publicField(_a13, "kinds", [ 9802 /* Highlight */ ]), _a13); NDKImage2 = (_a14 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_imetas"); this.kind ?? (this.kind = 20); } /** * Creates a NDKImage from an existing NDKEvent. * * @param event NDKEvent to create the NDKImage from. * @returns NDKImage */ static from(event) { return new _a14(event.ndk, event.rawEvent()); } get isValid() { return this.imetas.length > 0; } get imetas() { if (this._imetas) return this._imetas; this._imetas = this.tags.filter((tag) => tag[0] === "imeta").map(mapImetaTag2).filter((imeta) => !!imeta.url); return this._imetas; } set imetas(tags) { this._imetas = tags; this.tags = this.tags.filter((tag) => tag[0] !== "imeta"); this.tags.push(...tags.map(imetaTagToTag2)); } }, __publicField(_a14, "kind", 20), __publicField(_a14, "kinds", [ 20 /* Image */ ]), _a14); NDKList2 = (_a15 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_encryptedTags"); /** * Stores the number of bytes the content was before decryption * to expire the cache when the content changes. */ __publicField(this, "encryptedTagsLength"); this.kind ?? (this.kind = 30001); } /** * Wrap a NDKEvent into a NDKList */ static from(ndkEvent) { return new _a15(ndkEvent.ndk, ndkEvent); } /** * Returns the title of the list. Falls back on fetching the name tag value. */ get title() { const titleTag = this.tagValue("title") || this.tagValue("name"); if (titleTag) return titleTag; if (this.kind === 3) { return "Contacts"; } if (this.kind === 1e4) { return "Mute"; } if (this.kind === 10001) { return "Pinned Notes"; } if (this.kind === 10002) { return "Relay Metadata"; } if (this.kind === 10003) { return "Bookmarks"; } if (this.kind === 10004) { return "Communities"; } if (this.kind === 10005) { return "Public Chats"; } if (this.kind === 10006) { return "Blocked Relays"; } if (this.kind === 10007) { return "Search Relays"; } if (this.kind === 10050) { return "Direct Message Receive Relays"; } if (this.kind === 10012) { return "Relay Feeds"; } if (this.kind === 10015) { return "Interests"; } if (this.kind === 10030) { return "Emojis"; } return this.tagValue("d"); } /** * Sets the title of the list. */ set title(title) { this.removeTag(["title", "name"]); if (title) this.tags.push(["title", title]); } /** * Returns the name of the list. * @deprecated Please use "title" instead. */ get name() { return this.title; } /** * Sets the name of the list. * @deprecated Please use "title" instead. This method will use the `title` tag instead. */ set name(name) { this.title = name; } /** * Returns the description of the list. */ get description() { return this.tagValue("description"); } /** * Sets the description of the list. */ set description(name) { this.removeTag("description"); if (name) this.tags.push(["description", name]); } /** * Returns the image of the list. */ get image() { return this.tagValue("image"); } /** * Sets the image of the list. */ set image(name) { this.removeTag("image"); if (name) this.tags.push(["image", name]); } isEncryptedTagsCacheValid() { return !!(this._encryptedTags && this.encryptedTagsLength === this.content.length); } /** * Returns the decrypted content of the list. */ async encryptedTags(useCache = true) { if (useCache && this.isEncryptedTagsCacheValid()) return this._encryptedTags; if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); const user = await this.ndk.signer.user(); try { if (this.content.length > 0) { try { const decryptedContent = await this.ndk.signer.decrypt(user, this.content); const a = JSON.parse(decryptedContent); if (a?.[0]) { this.encryptedTagsLength = this.content.length; return this._encryptedTags = a; } this.encryptedTagsLength = this.content.length; return this._encryptedTags = []; } catch (_e2) { } } } catch (_e2) { } return []; } /** * This method can be overriden to validate that a tag is valid for this list. * * (i.e. the NDKPersonList can validate that items are NDKUser instances) */ validateTag(_tagValue) { return true; } getItems(type) { return this.tags.filter((tag) => tag[0] === type); } /** * Returns the unecrypted items in this list. */ get items() { return this.tags.filter((t) => { return ![ "d", "L", "l", "title", "name", "description", "published_at", "summary", "image", "thumb", "alt", "expiration", "subject", "client" ].includes(t[0]); }); } /** * Adds a new item to the list. * @param relay Relay to add * @param mark Optional mark to add to the item * @param encrypted Whether to encrypt the item * @param position Where to add the item in the list (top or bottom) */ async addItem(item, mark = void 0, encrypted = false, position = "bottom") { if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); let tags; if (item instanceof NDKEvent2) { tags = [item.tagReference(mark)]; } else if (item instanceof NDKUser2) { tags = item.referenceTags(); } else if (item instanceof NDKRelay2) { tags = item.referenceTags(); } else if (Array.isArray(item)) { tags = [item]; } else { throw new Error("Invalid object type"); } if (mark) tags[0].push(mark); if (encrypted) { const user = await this.ndk.signer.user(); const currentList = await this.encryptedTags(); if (position === "top") currentList.unshift(...tags); else currentList.push(...tags); this._encryptedTags = currentList; this.encryptedTagsLength = this.content.length; this.content = JSON.stringify(currentList); await this.encrypt(user); } else { if (position === "top") this.tags.unshift(...tags); else this.tags.push(...tags); } this.created_at = Math.floor(Date.now() / 1e3); this.emit("change"); } /** * Removes an item from the list from both the encrypted and unencrypted lists. * @param value value of item to remove from the list * @param publish whether to publish the change * @returns */ async removeItemByValue(value, publish = true) { if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); const index = this.tags.findIndex((tag) => tag[1] === value); if (index >= 0) { this.tags.splice(index, 1); } const user = await this.ndk.signer.user(); const encryptedTags = await this.encryptedTags(); const encryptedIndex = encryptedTags.findIndex((tag) => tag[1] === value); if (encryptedIndex >= 0) { encryptedTags.splice(encryptedIndex, 1); this._encryptedTags = encryptedTags; this.encryptedTagsLength = this.content.length; this.content = JSON.stringify(encryptedTags); await this.encrypt(user); } if (publish) { return this.publishReplaceable(); } this.created_at = Math.floor(Date.now() / 1e3); this.emit("change"); } /** * Removes an item from the list. * * @param index The index of the item to remove. * @param encrypted Whether to remove from the encrypted list or not. */ async removeItem(index, encrypted) { if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); if (encrypted) { const user = await this.ndk.signer.user(); const currentList = await this.encryptedTags(); currentList.splice(index, 1); this._encryptedTags = currentList; this.encryptedTagsLength = this.content.length; this.content = JSON.stringify(currentList); await this.encrypt(user); } else { this.tags.splice(index, 1); } this.created_at = Math.floor(Date.now() / 1e3); this.emit("change"); return this; } has(item) { return this.items.some((tag) => tag[1] === item); } /** * Creates a filter that will result in fetching * the items of this list * @example * const list = new NDKList(...); * const filters = list.filterForItems(); * const events = await ndk.fetchEvents(filters); */ filterForItems() { const ids = /* @__PURE__ */ new Set(); const nip33Queries = /* @__PURE__ */ new Map(); const filters = []; for (const tag of this.items) { if (tag[0] === "e" && tag[1]) { ids.add(tag[1]); } else if (tag[0] === "a" && tag[1]) { const [kind, pubkey, dTag] = tag[1].split(":"); if (!kind || !pubkey) continue; const key = `${kind}:${pubkey}`; const item = nip33Queries.get(key) || []; item.push(dTag || ""); nip33Queries.set(key, item); } } if (ids.size > 0) { filters.push({ ids: Array.from(ids) }); } if (nip33Queries.size > 0) { for (const [key, values] of nip33Queries.entries()) { const [kind, pubkey] = key.split(":"); filters.push({ kinds: [Number.parseInt(kind)], authors: [pubkey], "#d": values }); } } return filters; } }, __publicField(_a15, "kind", 30001), __publicField(_a15, "kinds", [ 30001, 10004, 10050, 10030, 10015, 10001, 10002, 10007, 10006, 10003, 10012 /* RelayFeedList */ ]), _a15); NDKAppHandlerEvent2 = (_a16 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "profile"); this.kind ?? (this.kind = 31990); } static from(ndkEvent) { const event = new _a16(ndkEvent.ndk, ndkEvent.rawEvent()); if (event.isValid) { return event; } return null; } get isValid() { const combinations = /* @__PURE__ */ new Map(); const combinationFromTag = (tag) => [tag[0], tag[2]].join(":").toLowerCase(); const tagsToInspect = ["web", "android", "ios"]; for (const tag of this.tags) { if (tagsToInspect.includes(tag[0])) { const combination = combinationFromTag(tag); if (combinations.has(combination)) { if (combinations.get(combination) !== tag[1].toLowerCase()) { return false; } } combinations.set(combination, tag[1].toLowerCase()); } } return true; } /** * Fetches app handler information * If no app information is available on the kind:31990, * we fetch the event's author's profile and return that instead. */ async fetchProfile() { if (this.profile === void 0 && this.content.length > 0) { try { const profile = JSON.parse(this.content); if (profile?.name) { return profile; } this.profile = null; } catch (_e2) { this.profile = null; } } return new Promise((resolve, reject) => { const author = this.author; author.fetchProfile().then(() => { resolve(author.profile); }).catch(reject); }); } }, __publicField(_a16, "kind", 31990), __publicField(_a16, "kinds", [ 31990 /* AppHandler */ ]), _a16); SEVERITY_MAP2 = { [ "NO_PROOFS" /* NO_PROOFS */ ]: "ERROR", [ "INVALID_PROOF_COUNT" /* INVALID_PROOF_COUNT */ ]: "ERROR", [ "MULTIPLE_RECIPIENTS" /* MULTIPLE_RECIPIENTS */ ]: "ERROR", [ "NO_RECIPIENT" /* NO_RECIPIENT */ ]: "ERROR", [ "MULTIPLE_MINTS" /* MULTIPLE_MINTS */ ]: "ERROR", [ "NO_MINT" /* NO_MINT */ ]: "ERROR", [ "MULTIPLE_EVENT_TAGS" /* MULTIPLE_EVENT_TAGS */ ]: "ERROR", [ "MALFORMED_PROOF_SECRET" /* MALFORMED_PROOF_SECRET */ ]: "ERROR", [ "MISSING_EVENT_TAG_IN_PROOF" /* MISSING_EVENT_TAG_IN_PROOF */ ]: "WARNING", [ "MISMATCHED_EVENT_TAG_IN_PROOF" /* MISMATCHED_EVENT_TAG_IN_PROOF */ ]: "WARNING", [ "MISSING_SENDER_TAG_IN_PROOF" /* MISSING_SENDER_TAG_IN_PROOF */ ]: "WARNING", [ "MISMATCHED_SENDER_TAG_IN_PROOF" /* MISMATCHED_SENDER_TAG_IN_PROOF */ ]: "WARNING", [ "NO_EVENT_TAG_IN_EVENT" /* NO_EVENT_TAG_IN_EVENT */ ]: "WARNING" /* WARNING */ }; ERROR_MESSAGES2 = { [ "NO_PROOFS" /* NO_PROOFS */ ]: "Nutzap must contain at least one proof", [ "INVALID_PROOF_COUNT" /* INVALID_PROOF_COUNT */ ]: "Invalid proof count", [ "MULTIPLE_RECIPIENTS" /* MULTIPLE_RECIPIENTS */ ]: "Nutzap must have exactly one recipient (p tag)", [ "NO_RECIPIENT" /* NO_RECIPIENT */ ]: "Nutzap must have a recipient (p tag)", [ "MULTIPLE_MINTS" /* MULTIPLE_MINTS */ ]: "Nutzap must specify exactly one mint (u tag)", [ "NO_MINT" /* NO_MINT */ ]: "Nutzap must specify a mint (u tag)", [ "MULTIPLE_EVENT_TAGS" /* MULTIPLE_EVENT_TAGS */ ]: "Nutzap must have at most one event tag (e tag)", [ "MALFORMED_PROOF_SECRET" /* MALFORMED_PROOF_SECRET */ ]: "Proof secret is malformed and cannot be parsed", [ "MISSING_EVENT_TAG_IN_PROOF" /* MISSING_EVENT_TAG_IN_PROOF */ ]: "Proof secret missing 'e' tag for replay protection", [ "MISMATCHED_EVENT_TAG_IN_PROOF" /* MISMATCHED_EVENT_TAG_IN_PROOF */ ]: "Proof secret 'e' tag does not match event being zapped", [ "MISSING_SENDER_TAG_IN_PROOF" /* MISSING_SENDER_TAG_IN_PROOF */ ]: "Proof secret missing 'P' tag for sender verification", [ "MISMATCHED_SENDER_TAG_IN_PROOF" /* MISMATCHED_SENDER_TAG_IN_PROOF */ ]: "Proof secret 'P' tag does not match sender pubkey", [ "NO_EVENT_TAG_IN_EVENT" /* NO_EVENT_TAG_IN_EVENT */ ]: "Nutzap event missing 'e' tag (recommended for replay protection)" }; NDKNutzap2 = (_a17 = class extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); __publicField(this, "debug"); __publicField(this, "_proofs", []); __publicField(this, "sender", this.author); this.kind ?? (this.kind = 9321); this.debug = ndk?.debug.extend("nutzap") ?? (0, import_debug16.default)("ndk:nutzap"); if (!this.alt) this.alt = "This is a nutzap"; try { const proofTags = this.getMatchingTags("proof"); if (proofTags.length) { this._proofs = proofTags.map((tag) => JSON.parse(tag[1])); } else { this._proofs = JSON.parse(this.content); } } catch { return; } } static from(event) { const e2 = new _a17(event.ndk, event); if (!e2._proofs || !e2._proofs.length) return; return e2; } set comment(comment) { this.content = comment ?? ""; } get comment() { const c = this.tagValue("comment"); if (c) return c; return this.content; } set proofs(proofs) { this._proofs = proofs; this.tags = this.tags.filter((tag) => tag[0] !== "proof"); for (const proof of proofs) { this.tags.push(["proof", JSON.stringify(proof)]); } } get proofs() { return this._proofs; } get rawP2pk() { const firstProof = this.proofs[0]; try { const secret = JSON.parse(firstProof.secret); let payload; if (typeof secret === "string") { payload = JSON.parse(secret); this.debug("stringified payload", firstProof.secret); } else if (typeof secret === "object") { payload = secret; } if (Array.isArray(payload) && payload[0] === "P2PK" && payload.length > 1 && typeof payload[1] === "object" && payload[1] !== null) { return payload[1].data; } if (typeof payload === "object" && payload !== null && typeof payload[1]?.data === "string") { return payload[1].data; } } catch (e2) { this.debug("error parsing p2pk pubkey", e2, this.proofs[0]); } return void 0; } /** * Gets the p2pk pubkey that is embedded in the first proof. * * Note that this returns a nostr pubkey, not a cashu pubkey (no "02" prefix) */ get p2pk() { const rawP2pk = this.rawP2pk; if (!rawP2pk) return; return rawP2pk.startsWith("02") ? rawP2pk.slice(2) : rawP2pk; } /** * Get the mint where this nutzap proofs exist */ get mint() { return this.tagValue("u"); } set mint(value) { this.replaceTag(["u", value]); } get unit() { let _unit = this.tagValue("unit") ?? "sat"; if (_unit?.startsWith("msat")) _unit = "sat"; return _unit; } set unit(value) { this.removeTag("unit"); if (value?.startsWith("msat")) throw new Error("msat is not allowed, use sat denomination instead"); if (value) this.tag(["unit", value]); } get amount() { const amount = this.proofs.reduce((total, proof) => total + proof.amount, 0); return amount; } /** * Set the target of the nutzap * @param target The target of the nutzap (a user or an event) */ set target(target) { this.tags = this.tags.filter((t) => t[0] !== "p"); if (target instanceof NDKEvent2) { this.tags.push(target.tagReference()); } } set recipientPubkey(pubkey) { this.removeTag("p"); this.tag(["p", pubkey]); } get recipientPubkey() { return this.tagValue("p"); } get recipient() { const pubkey = this.recipientPubkey; if (this.ndk) return this.ndk.getUser({ pubkey }); return new NDKUser2({ pubkey }); } async toNostrEvent() { if (this.unit === "msat") { this.unit = "sat"; } this.removeTag("amount"); this.tags.push(["amount", this.amount.toString()]); const event = await super.toNostrEvent(); event.content = this.comment; return event; } /** * Validates that the nutzap conforms to NIP-61 * @deprecated Use validateNIP61() instead for detailed validation results */ get isValid() { const result = this.validateNIP61(); return result.valid; } /** * Performs comprehensive validation of the nutzap according to NIP-61. * Returns detailed validation results including errors and warnings. * * Errors make the nutzap invalid, warnings are recommendations for best practices. */ validateNIP61() { const issues = []; let eTagCount = 0; let pTagCount = 0; let mintTagCount = 0; for (const tag of this.tags) { if (tag[0] === "e") eTagCount++; if (tag[0] === "p") pTagCount++; if (tag[0] === "u") mintTagCount++; } if (this.proofs.length === 0) { issues.push(createValidationIssue2( "NO_PROOFS" /* NO_PROOFS */ )); } if (pTagCount === 0) { issues.push(createValidationIssue2( "NO_RECIPIENT" /* NO_RECIPIENT */ )); } else if (pTagCount > 1) { issues.push(createValidationIssue2( "MULTIPLE_RECIPIENTS" /* MULTIPLE_RECIPIENTS */ )); } if (mintTagCount === 0) { issues.push(createValidationIssue2( "NO_MINT" /* NO_MINT */ )); } else if (mintTagCount > 1) { issues.push(createValidationIssue2( "MULTIPLE_MINTS" /* MULTIPLE_MINTS */ )); } if (eTagCount > 1) { issues.push(createValidationIssue2( "MULTIPLE_EVENT_TAGS" /* MULTIPLE_EVENT_TAGS */ )); } const eventId = this.tagValue("e"); const senderPubkey = this.pubkey; for (let i3 = 0; i3 < this.proofs.length; i3++) { const proof = this.proofs[i3]; try { const secret = JSON.parse(proof.secret); const payload = typeof secret === "string" ? JSON.parse(secret) : secret; if (Array.isArray(payload) && payload[0] === "P2PK" && payload[1]) { const tags = payload[1].tags; if (eventId) { if (!tags) { issues.push( createValidationIssue2( "MISSING_EVENT_TAG_IN_PROOF", i3 ) ); } else { const eTag = tags.find((t) => t[0] === "e"); if (!eTag) { issues.push( createValidationIssue2( "MISSING_EVENT_TAG_IN_PROOF", i3 ) ); } else if (eTag[1] !== eventId) { issues.push( createValidationIssue2( "MISMATCHED_EVENT_TAG_IN_PROOF", i3 ) ); } } } if (!tags) { issues.push( createValidationIssue2("MISSING_SENDER_TAG_IN_PROOF", i3) ); } else { const PTag = tags.find((t) => t[0] === "P"); if (!PTag) { issues.push( createValidationIssue2( "MISSING_SENDER_TAG_IN_PROOF", i3 ) ); } else if (PTag[1] !== senderPubkey) { issues.push( createValidationIssue2( "MISMATCHED_SENDER_TAG_IN_PROOF", i3 ) ); } } } } catch { issues.push( createValidationIssue2("MALFORMED_PROOF_SECRET", i3) ); } } if (!eventId && this.proofs.length > 0) { issues.push(createValidationIssue2( "NO_EVENT_TAG_IN_EVENT" /* NO_EVENT_TAG_IN_EVENT */ )); } const hasErrors = issues.some( (issue) => issue.severity === "ERROR" /* ERROR */ ); return { valid: !hasErrors, issues }; } }, __publicField(_a17, "kind", 9321), __publicField(_a17, "kinds", [_a17.kind]), _a17); NDKProject2 = (_a18 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_signer"); this.kind = 31933; } static from(event) { return new _a18(event.ndk, event.rawEvent()); } set repo(value) { this.removeTag("repo"); if (value) this.tags.push(["repo", value]); } set hashtags(values) { this.removeTag("hashtags"); if (values.filter((t) => t.length > 0).length) this.tags.push(["hashtags", ...values]); } get hashtags() { const tag = this.tags.find((tag2) => tag2[0] === "hashtags"); return tag ? tag.slice(1) : []; } get repo() { return this.tagValue("repo"); } get title() { return this.tagValue("title"); } set title(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } get picture() { return this.tagValue("picture"); } set picture(value) { this.removeTag("picture"); if (value) this.tags.push(["picture", value]); } set description(value) { this.content = value; } get description() { return this.content; } /** * The project slug, derived from the 'd' tag. */ get slug() { return this.dTag ?? "empty-dtag"; } async getSigner() { if (this._signer) return this._signer; const encryptedKey = this.tagValue("key"); if (!encryptedKey) { this._signer = NDKPrivateKeySigner2.generate(); await this.encryptAndSaveNsec(); } else { const decryptedKey = await this.ndk?.signer?.decrypt(this.ndk.activeUser, encryptedKey); if (!decryptedKey) { throw new Error("Failed to decrypt project key or missing signer context."); } this._signer = new NDKPrivateKeySigner2(decryptedKey); } return this._signer; } async getNsec() { const signer = await this.getSigner(); return signer.privateKey; } async setNsec(value) { this._signer = new NDKPrivateKeySigner2(value); await this.encryptAndSaveNsec(); } async encryptAndSaveNsec() { if (!this._signer) throw new Error("Signer is not set."); const key = this._signer.privateKey; const encryptedKey = await this.ndk?.signer?.encrypt(this.ndk.activeUser, key); if (encryptedKey) { this.removeTag("key"); this.tags.push(["key", encryptedKey]); } } }, __publicField(_a18, "kind", 31933), __publicField(_a18, "kinds", [ 31933 /* Project */ ]), _a18); NDKProjectTemplate2 = (_a19 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind = 30717; } static from(event) { return new _a19(event.ndk, event.rawEvent()); } /** * Template identifier from 'd' tag */ get templateId() { return this.dTag ?? ""; } set templateId(value) { this.dTag = value; } /** * Template name from 'title' tag */ get name() { return this.tagValue("title") ?? ""; } set name(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } /** * Template description from 'description' tag */ get description() { return this.tagValue("description") ?? ""; } set description(value) { this.removeTag("description"); if (value) this.tags.push(["description", value]); } /** * Git repository URL from 'uri' tag */ get repoUrl() { return this.tagValue("uri") ?? ""; } set repoUrl(value) { this.removeTag("uri"); if (value) this.tags.push(["uri", value]); } /** * Template preview image URL from 'image' tag */ get image() { return this.tagValue("image"); } set image(value) { this.removeTag("image"); if (value) this.tags.push(["image", value]); } /** * Command to run from 'command' tag */ get command() { return this.tagValue("command"); } set command(value) { this.removeTag("command"); if (value) this.tags.push(["command", value]); } /** * Agent configuration from 'agent' tag */ get agentConfig() { const agentTag = this.tagValue("agent"); if (!agentTag) return void 0; try { return JSON.parse(agentTag); } catch { return void 0; } } set agentConfig(value) { this.removeTag("agent"); if (value) { this.tags.push(["agent", JSON.stringify(value)]); } } /** * Template tags from 't' tags */ get templateTags() { return this.getMatchingTags("t").map((tag) => tag[1]).filter(Boolean); } set templateTags(values) { this.tags = this.tags.filter((tag) => tag[0] !== "t"); values.forEach((value) => { if (value) this.tags.push(["t", value]); }); } }, __publicField(_a19, "kind", 30717), __publicField(_a19, "kinds", [ 30717 /* ProjectTemplate */ ]), _a19); READ_MARKER2 = "read"; WRITE_MARKER2 = "write"; NDKRelayList2 = (_a20 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 10002); } static from(ndkEvent) { return new _a20(ndkEvent.ndk, ndkEvent.rawEvent()); } get readRelayUrls() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").filter((tag) => !tag[2] || tag[2] && tag[2] === READ_MARKER2).map((tag) => tryNormalizeRelayUrl2(tag[1])).filter((url) => !!url); } set readRelayUrls(relays) { for (const relay of relays) { this.tags.push(["r", relay, READ_MARKER2]); } } get writeRelayUrls() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").filter((tag) => !tag[2] || tag[2] && tag[2] === WRITE_MARKER2).map((tag) => tryNormalizeRelayUrl2(tag[1])).filter((url) => !!url); } set writeRelayUrls(relays) { for (const relay of relays) { this.tags.push(["r", relay, WRITE_MARKER2]); } } get bothRelayUrls() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").filter((tag) => !tag[2]).map((tag) => tag[1]); } set bothRelayUrls(relays) { for (const relay of relays) { this.tags.push(["r", relay]); } } get relays() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").map((tag) => tag[1]); } /** * Provides a relaySet for the relays in this list. */ get relaySet() { if (!this.ndk) throw new Error("NDKRelayList has no NDK instance"); return new NDKRelaySet2( new Set(this.relays.map((u3) => this.ndk?.pool.getRelay(u3)).filter((r) => !!r)), this.ndk ); } }, __publicField(_a20, "kind", 10002), __publicField(_a20, "kinds", [ 10002 /* RelayList */ ]), _a20); NDKRelayFeedList2 = (_a21 = class extends NDKList2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); if (!rawEvent?.kind) { this.kind = 10012; } } static from(ndkEvent) { return new _a21(ndkEvent.ndk, ndkEvent); } /** * Gets all relay URLs from the list. */ get relayUrls() { return this.getMatchingTags("relay").map((tag) => tag[1]); } /** * Gets all relay set references (kind:30002 naddr) from the list. * Returns them in the format "kind:pubkey:dtag". */ get relaySets() { return this.getMatchingTags("a").map((tag) => tag[1]); } /** * Adds a relay URL to the list. * @param relayUrl - WebSocket URL of the relay * @param mark - Optional mark to add to the relay tag * @param encrypted - Whether to encrypt the item * @param position - Where to add the item in the list */ async addRelay(relayUrl, mark, encrypted = false, position = "bottom") { const tag = ["relay", relayUrl]; if (mark) tag.push(mark); await this.addItem(tag, void 0, encrypted, position); } /** * Adds a relay set reference to the list. * @param relaySetNaddr - NIP-33 address in format "kind:pubkey:dtag" (kind should be 30002) * @param mark - Optional mark to add to the relay set tag * @param encrypted - Whether to encrypt the item * @param position - Where to add the item in the list */ async addRelaySet(relaySetNaddr, mark, encrypted = false, position = "bottom") { const tag = ["a", relaySetNaddr]; if (mark) tag.push(mark); await this.addItem(tag, void 0, encrypted, position); } /** * Removes a relay URL from the list. * @param relayUrl - The relay URL to remove * @param publish - Whether to publish the change */ async removeRelay(relayUrl, publish = true) { await this.removeItemByValue(relayUrl, publish); } /** * Removes a relay set from the list. * @param relaySetNaddr - The relay set naddr to remove * @param publish - Whether to publish the change */ async removeRelaySet(relaySetNaddr, publish = true) { await this.removeItemByValue(relaySetNaddr, publish); } }, __publicField(_a21, "kind", 10012), __publicField(_a21, "kinds", [ 10012 /* RelayFeedList */ ]), _a21); NDKRepost2 = (_a22 = class extends NDKEvent2 { constructor() { super(...arguments); __publicField(this, "_repostedEvents"); } static from(event) { return new _a22(event.ndk, event.rawEvent()); } /** * Returns all reposted events by the current event. * * @param klass Optional class to convert the events to. * @returns */ async repostedEvents(klass, opts) { const items = []; if (!this.ndk) throw new Error("NDK instance not set"); if (this._repostedEvents !== void 0) return this._repostedEvents; for (const eventId of this.repostedEventIds()) { const filter = filterForId2(eventId); const event = await this.ndk.fetchEvent(filter, opts); if (event) { items.push(klass ? klass.from(event) : event); } } return items; } /** * Returns the reposted event IDs. */ repostedEventIds() { return this.tags.filter((t) => t[0] === "e" || t[0] === "a").map((t) => t[1]); } }, __publicField(_a22, "kind", 6), __publicField(_a22, "kinds", [ 6, 16 /* GenericRepost */ ]), _a22); NDKSimpleGroupMemberList2 = (_a23 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "relaySet"); __publicField(this, "memberSet", /* @__PURE__ */ new Set()); this.kind ?? (this.kind = 39002); this.memberSet = new Set(this.members); } static from(event) { return new _a23(event.ndk, event); } get members() { return this.getMatchingTags("p").map((tag) => tag[1]); } hasMember(member) { return this.memberSet.has(member); } async publish(relaySet, timeoutMs, requiredRelayCount) { relaySet ?? (relaySet = this.relaySet); return super.publishReplaceable(relaySet, timeoutMs, requiredRelayCount); } }, __publicField(_a23, "kind", 39002), __publicField(_a23, "kinds", [ 39002 /* GroupMembers */ ]), _a23); NDKSimpleGroupMetadata2 = (_a24 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 39e3); } static from(event) { return new _a24(event.ndk, event); } get name() { return this.tagValue("name"); } get picture() { return this.tagValue("picture"); } get about() { return this.tagValue("about"); } get scope() { if (this.getMatchingTags("public").length > 0) return "public"; if (this.getMatchingTags("public").length > 0) return "private"; return void 0; } set scope(scope) { this.removeTag("public"); this.removeTag("private"); if (scope === "public") { this.tags.push(["public", ""]); } else if (scope === "private") { this.tags.push(["private", ""]); } } get access() { if (this.getMatchingTags("open").length > 0) return "open"; if (this.getMatchingTags("closed").length > 0) return "closed"; return void 0; } set access(access) { this.removeTag("open"); this.removeTag("closed"); if (access === "open") { this.tags.push(["open", ""]); } else if (access === "closed") { this.tags.push(["closed", ""]); } } }, __publicField(_a24, "kind", 39e3), __publicField(_a24, "kinds", [ 39e3 /* GroupMetadata */ ]), _a24); NDKStorySticker2 = (_a25 = class { constructor(arg) { __publicField(this, "type"); __publicField(this, "value"); __publicField(this, "position"); __publicField(this, "dimension"); __publicField(this, "properties"); __publicField(this, "hasValidDimensions", () => { return typeof this.dimension.width === "number" && typeof this.dimension.height === "number" && !Number.isNaN(this.dimension.width) && !Number.isNaN(this.dimension.height); }); __publicField(this, "hasValidPosition", () => { return typeof this.position.x === "number" && typeof this.position.y === "number" && !Number.isNaN(this.position.x) && !Number.isNaN(this.position.y); }); if (Array.isArray(arg)) { const tag = arg; if (tag[0] !== "sticker" || tag.length < 5) { throw new Error("Invalid sticker tag"); } this.type = tag[1]; this.value = tag[2]; this.position = strToPosition2(tag[3]); this.dimension = strToDimension2(tag[4]); const props = {}; for (let i3 = 5; i3 < tag.length; i3++) { const [key, ...rest] = tag[i3].split(" "); props[key] = rest.join(" "); } if (Object.keys(props).length > 0) { this.properties = props; } } else { this.type = arg; this.value = void 0; this.position = { x: 0, y: 0 }; this.dimension = { width: 0, height: 0 }; } } static fromTag(tag) { try { return new _a25(tag); } catch { return null; } } get style() { return this.properties?.style; } set style(style) { if (style) this.properties = { ...this.properties, style }; else delete this.properties?.style; } get rotation() { return this.properties?.rot ? Number.parseFloat(this.properties.rot) : void 0; } set rotation(rotation) { if (rotation !== void 0) { this.properties = { ...this.properties, rot: rotation.toString() }; } else { delete this.properties?.rot; } } /** * Checks if the sticker is valid. * * @returns {boolean} - True if the sticker is valid, false otherwise. */ get isValid() { return this.hasValidDimensions() && this.hasValidPosition(); } toTag() { if (!this.isValid) { const errors = [ !this.hasValidDimensions() ? "dimensions is invalid" : void 0, !this.hasValidPosition() ? "position is invalid" : void 0 ].filter(Boolean); throw new Error(`Invalid sticker: ${errors.join(", ")}`); } let value; switch (this.type) { case "event": value = this.value.tagId(); break; case "pubkey": value = this.value.pubkey; break; default: value = this.value; } const tag = ["sticker", this.type, value, coordinates2(this.position), dimension2(this.dimension)]; if (this.properties) { for (const [key, propValue] of Object.entries(this.properties)) { tag.push(`${key} ${propValue}`); } } return tag; } }, __publicField(_a25, "Text", "text"), __publicField(_a25, "Pubkey", "pubkey"), __publicField(_a25, "Event", "event"), __publicField(_a25, "Prompt", "prompt"), __publicField(_a25, "Countdown", "countdown"), _a25); NDKStory2 = (_a26 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_imeta"); __publicField(this, "_dimensions"); this.kind ?? (this.kind = 23); if (rawEvent) { for (const tag of rawEvent.tags) { switch (tag[0]) { case "imeta": this._imeta = mapImetaTag2(tag); break; case "dim": this.dimensions = strToDimension2(tag[1]); break; } } } } /** * Creates a NDKStory from an existing NDKEvent. * * @param event NDKEvent to create the NDKStory from. * @returns NDKStory */ static from(event) { return new _a26(event.ndk, event); } /** * Checks if the story is valid (has exactly one imeta tag). */ get isValid() { return !!this.imeta; } /** * Gets the first imeta tag (there should only be one). */ get imeta() { return this._imeta; } /** * Sets a single imeta tag, replacing any existing ones. */ set imeta(tag) { this._imeta = tag; this.tags = this.tags.filter((t) => t[0] !== "imeta"); if (tag) { this.tags.push(imetaTagToTag2(tag)); } } /** * Getter for the story dimensions. * * @returns {NDKStoryDimension | undefined} - The story dimensions if available, otherwise undefined. */ get dimensions() { const dimTag = this.tagValue("dim"); if (!dimTag) return void 0; return strToDimension2(dimTag); } /** * Setter for the story dimensions. * * @param {NDKStoryDimension | undefined} dimensions - The dimensions to set for the story. */ set dimensions(dimensions) { this.removeTag("dim"); if (dimensions) { this.tags.push(["dim", `${dimensions.width}x${dimensions.height}`]); } } /** * Getter for the story duration. * * @returns {number | undefined} - The story duration in seconds if available, otherwise undefined. */ get duration() { const durTag = this.tagValue("dur"); if (!durTag) return void 0; return Number.parseInt(durTag); } /** * Setter for the story duration. * * @param {number | undefined} duration - The duration in seconds to set for the story. */ set duration(duration) { this.removeTag("dur"); if (duration !== void 0) { this.tags.push(["dur", duration.toString()]); } } /** * Gets all stickers from the story. * * @returns {NDKStorySticker[]} - Array of stickers in the story. */ get stickers() { const stickers = []; for (const tag of this.tags) { if (tag[0] !== "sticker" || tag.length < 5) continue; const sticker = NDKStorySticker2.fromTag(tag); if (sticker) stickers.push(sticker); } return stickers; } /** * Adds a sticker to the story. * * @param {NDKStorySticker|StorySticker} sticker - The sticker to add. */ addSticker(sticker) { let stickerToAdd; if (sticker instanceof NDKStorySticker2) { stickerToAdd = sticker; } else { const tag = [ "sticker", sticker.type, typeof sticker.value === "string" ? sticker.value : "", coordinates2(sticker.position), dimension2(sticker.dimension) ]; if (sticker.properties) { for (const [key, value] of Object.entries(sticker.properties)) { tag.push(`${key} ${value}`); } } stickerToAdd = new NDKStorySticker2(tag); stickerToAdd.value = sticker.value; } if (stickerToAdd.type === "pubkey") { this.tag(stickerToAdd.value); } else if (stickerToAdd.type === "event") { this.tag(stickerToAdd.value); } this.tags.push(stickerToAdd.toTag()); } /** * Removes a sticker from the story. * * @param {number} index - The index of the sticker to remove. */ removeSticker(index) { const stickers = this.stickers; if (index < 0 || index >= stickers.length) return; let stickerCount = 0; for (let i3 = 0; i3 < this.tags.length; i3++) { if (this.tags[i3][0] === "sticker") { if (stickerCount === index) { this.tags.splice(i3, 1); break; } stickerCount++; } } } }, __publicField(_a26, "kind", 23), __publicField(_a26, "kinds", [ 23 /* Story */ ]), _a26); coordinates2 = (position) => `${position.x},${position.y}`; dimension2 = (dimension22) => `${dimension22.width}x${dimension22.height}`; NDKSubscriptionReceipt2 = (_a27 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "debug"); this.kind ?? (this.kind = 7003); this.debug = ndk?.debug.extend("subscription-start") ?? (0, import_debug17.default)("ndk:subscription-start"); } static from(event) { return new _a27(event.ndk, event.rawEvent()); } /** * This is the person being subscribed to */ get recipient() { const pTag = this.getMatchingTags("p")?.[0]; if (!pTag) return void 0; const user = new NDKUser2({ pubkey: pTag[1] }); return user; } set recipient(user) { this.removeTag("p"); if (!user) return; this.tags.push(["p", user.pubkey]); } /** * This is the person subscribing */ get subscriber() { const PTag = this.getMatchingTags("P")?.[0]; if (!PTag) return void 0; const user = new NDKUser2({ pubkey: PTag[1] }); return user; } set subscriber(user) { this.removeTag("P"); if (!user) return; this.tags.push(["P", user.pubkey]); } set subscriptionStart(event) { this.debug(`before setting subscription start: ${this.rawEvent}`); this.removeTag("e"); this.tag(event, "subscription", true); this.debug(`after setting subscription start: ${this.rawEvent}`); } get tierName() { const tag = this.getMatchingTags("tier")?.[0]; return tag?.[1]; } get isValid() { const period = this.validPeriod; if (!period) { return false; } if (period.start > period.end) { return false; } const pTags = this.getMatchingTags("p"); const PTags = this.getMatchingTags("P"); if (pTags.length !== 1 || PTags.length !== 1) { return false; } return true; } get validPeriod() { const tag = this.getMatchingTags("valid")?.[0]; if (!tag) return void 0; try { return { start: new Date(Number.parseInt(tag[1]) * 1e3), end: new Date(Number.parseInt(tag[2]) * 1e3) }; } catch { return void 0; } } set validPeriod(period) { this.removeTag("valid"); if (!period) return; this.tags.push([ "valid", Math.floor(period.start.getTime() / 1e3).toString(), Math.floor(period.end.getTime() / 1e3).toString() ]); } get startPeriod() { return this.validPeriod?.start; } get endPeriod() { return this.validPeriod?.end; } /** * Whether the subscription is currently active */ isActive(time) { time ?? (time = /* @__PURE__ */ new Date()); const period = this.validPeriod; if (!period) return false; if (time < period.start) return false; if (time > period.end) return false; return true; } }, __publicField(_a27, "kind", 7003), __publicField(_a27, "kinds", [ 7003 /* SubscriptionReceipt */ ]), _a27); possibleIntervalFrequencies2 = [ "daily", "weekly", "monthly", "quarterly", "yearly" ]; NDKSubscriptionTier2 = (_a28 = class extends NDKArticle2 { constructor(ndk, rawEvent) { const k2 = rawEvent?.kind ?? 37001; super(ndk, rawEvent); this.kind = k2; } /** * Creates a new NDKSubscriptionTier from an event * @param event * @returns NDKSubscriptionTier */ static from(event) { return new _a28(event.ndk, event); } /** * Returns perks for this tier */ get perks() { return this.getMatchingTags("perk").map((tag) => tag[1]).filter((perk) => perk !== void 0); } /** * Adds a perk to this tier */ addPerk(perk) { this.tags.push(["perk", perk]); } /** * Returns the amount for this tier */ get amounts() { return this.getMatchingTags("amount").map((tag) => parseTagToSubscriptionAmount2(tag)).filter((a) => a !== void 0); } /** * Adds an amount to this tier * @param amount Amount in the smallest unit of the currency (e.g. cents, msats) * @param currency Currency code. Use msat for millisatoshis * @param term One of daily, weekly, monthly, quarterly, yearly */ addAmount(amount, currency, term) { this.tags.push(newAmount2(amount, currency, term)); } /** * Sets a relay where content related to this tier can be found * @param relayUrl URL of the relay */ set relayUrl(relayUrl) { this.tags.push(["r", relayUrl]); } /** * Returns the relay URLs for this tier */ get relayUrls() { return this.getMatchingTags("r").map((tag) => tag[1]).filter((relay) => relay !== void 0); } /** * Gets the verifier pubkey for this tier. This is the pubkey that will generate * subscription payment receipts */ get verifierPubkey() { return this.tagValue("p"); } /** * Sets the verifier pubkey for this tier. */ set verifierPubkey(pubkey) { this.removeTag("p"); if (pubkey) this.tags.push(["p", pubkey]); } /** * Checks if this tier is valid */ get isValid() { return this.title !== void 0 && // Must have a title this.amounts.length > 0; } }, __publicField(_a28, "kind", 37001), __publicField(_a28, "kinds", [ 37001 /* SubscriptionTier */ ]), _a28); NDKSubscriptionStart2 = (_a29 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "debug"); this.kind ?? (this.kind = 7001); this.debug = ndk?.debug.extend("subscription-start") ?? (0, import_debug18.default)("ndk:subscription-start"); } static from(event) { return new _a29(event.ndk, event.rawEvent()); } /** * Recipient of the subscription. I.e. The author of this event subscribes to this user. */ get recipient() { const pTag = this.getMatchingTags("p")?.[0]; if (!pTag) return void 0; const user = new NDKUser2({ pubkey: pTag[1] }); return user; } set recipient(user) { this.removeTag("p"); if (!user) return; this.tags.push(["p", user.pubkey]); } /** * The amount of the subscription. */ get amount() { const amountTag = this.getMatchingTags("amount")?.[0]; if (!amountTag) return void 0; return parseTagToSubscriptionAmount2(amountTag); } set amount(amount) { this.removeTag("amount"); if (!amount) return; this.tags.push(newAmount2(amount.amount, amount.currency, amount.term)); } /** * The event id or NIP-33 tag id of the tier that the user is subscribing to. */ get tierId() { const eTag = this.getMatchingTags("e")?.[0]; const aTag = this.getMatchingTags("a")?.[0]; if (!eTag || !aTag) return void 0; return eTag[1] ?? aTag[1]; } set tier(tier) { this.removeTag("e"); this.removeTag("a"); this.removeTag("event"); if (!tier) return; this.tag(tier); this.removeTag("p"); this.tags.push(["p", tier.pubkey]); this.tags.push(["event", JSON.stringify(tier.rawEvent())]); } /** * Fetches the tier that the user is subscribing to. */ async fetchTier() { const eventTag = this.tagValue("event"); if (eventTag) { try { const parsedEvent = JSON.parse(eventTag); return new NDKSubscriptionTier2(this.ndk, parsedEvent); } catch { this.debug("Failed to parse event tag"); } } const tierId = this.tierId; if (!tierId) return void 0; const e2 = await this.ndk?.fetchEvent(tierId); if (!e2) return void 0; return NDKSubscriptionTier2.from(e2); } get isValid() { if (this.getMatchingTags("amount").length !== 1) { this.debug("Invalid # of amount tag"); return false; } if (!this.amount) { this.debug("Invalid amount tag"); return false; } if (this.getMatchingTags("p").length !== 1) { this.debug("Invalid # of p tag"); return false; } if (!this.recipient) { this.debug("Invalid p tag"); return false; } return true; } }, __publicField(_a29, "kind", 7001), __publicField(_a29, "kinds", [ 7001 /* Subscribe */ ]), _a29); NDKTask2 = (_a30 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind = 1934; } static from(event) { return new _a30(event.ndk, event.rawEvent()); } set title(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } get title() { return this.tagValue("title"); } set project(project) { this.removeTag("a"); this.tags.push(project.tagReference()); } get projectSlug() { const tag = this.getMatchingTags("a")[0]; return tag ? tag[1].split(/:/)?.[2] : void 0; } }, __publicField(_a30, "kind", 1934), __publicField(_a30, "kinds", [ 1934 /* Task */ ]), _a30); NDKThread2 = (_a31 = class extends NDKEvent2 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 11); } /** * Creates an NDKThread from an existing NDKEvent. * * @param event NDKEvent to create the NDKThread from. * @returns NDKThread */ static from(event) { return new _a31(event.ndk, event); } /** * Gets the title of the thread. */ get title() { return this.tagValue("title"); } /** * Sets the title of the thread. */ set title(title) { this.removeTag("title"); if (title) { this.tags.push(["title", title]); } } }, __publicField(_a31, "kind", 11), __publicField(_a31, "kinds", [ 11 /* Thread */ ]), _a31); NDKVideo2 = (_a32 = class extends NDKEvent2 { constructor() { super(...arguments); __publicField(this, "_imetas"); } /** * Creates a NDKArticle from an existing NDKEvent. * * @param event NDKEvent to create the NDKArticle from. * @returns NDKArticle */ static from(event) { return new _a32(event.ndk, event.rawEvent()); } /** * Getter for the article title. * * @returns {string | undefined} - The article title if available, otherwise undefined. */ get title() { return this.tagValue("title"); } /** * Setter for the article title. * * @param {string | undefined} title - The title to set for the article. */ set title(title) { this.removeTag("title"); if (title) this.tags.push(["title", title]); } /** * Getter for the article thumbnail. * * @returns {string | undefined} - The article thumbnail if available, otherwise undefined. */ get thumbnail() { let thumbnail; if (this.imetas && this.imetas.length > 0) { thumbnail = this.imetas[0].image?.[0]; } return thumbnail ?? this.tagValue("thumb"); } get imetas() { if (this._imetas) return this._imetas; this._imetas = this.tags.filter((tag) => tag[0] === "imeta").map(mapImetaTag2); return this._imetas; } set imetas(tags) { this._imetas = tags; this.tags = this.tags.filter((tag) => tag[0] !== "imeta"); this.tags.push(...tags.map(imetaTagToTag2)); } get url() { if (this.imetas && this.imetas.length > 0) { return this.imetas[0].url; } return this.tagValue("url"); } /** * Getter for the article's publication timestamp. * * @returns {number | undefined} - The Unix timestamp of when the article was published or undefined. */ get published_at() { const tag = this.tagValue("published_at"); if (tag) { return Number.parseInt(tag); } return void 0; } /** * Generates content tags for the article. * * This method first checks and sets the publication date if not available, * and then generates content tags based on the base NDKEvent class. * * @returns {ContentTag} - The generated content tags. */ async generateTags() { super.generateTags(); if (!this.kind) { if (this.imetas?.[0]?.dim) { const [width, height] = this.imetas[0].dim.split("x"); const isPortrait = width && height && Number.parseInt(width) < Number.parseInt(height); const isShort = this.duration && this.duration < 120; if (isShort && isPortrait) this.kind = 22; else this.kind = 21; } } return super.generateTags(); } get duration() { const tag = this.tagValue("duration"); if (tag) { return Number.parseInt(tag); } return void 0; } /** * Setter for the video's duration * * @param {number | undefined} duration - The duration to set for the video (in seconds) */ set duration(dur) { this.removeTag("duration"); if (dur !== void 0) { this.tags.push(["duration", Math.floor(dur).toString()]); } } }, __publicField(_a32, "kind", 21), __publicField(_a32, "kinds", [ 34235, 34236, 22, 21 /* Video */ ]), _a32); NDKWiki2 = (_a33 = class extends NDKArticle2 { static from(event) { return new _a33(event.ndk, event.rawEvent()); } get isDefered() { return this.hasTag("a", "defer"); } get deferedId() { return this.tagValue("a", "defer"); } /** * Defers the author's wiki event to another wiki event. * * Wiki-events can tag other wiki-events with a `defer` marker to indicate that it considers someone else's entry as a "better" version of itself. If using a `defer` marker both `a` and `e` tags SHOULD be used. * * @example * myWiki.defer = betterWikiEntryOnTheSameTopic; * myWiki.publishReplaceable() */ set defer(deferedTo) { this.removeTag("a", "defer"); this.tag(deferedTo, "defer"); } }, __publicField(_a33, "kind", 30818), __publicField(_a33, "kinds", [ 30818 /* Wiki */ ]), _a33); NDKWikiMergeRequest2 = (_a34 = class extends NDKEvent2 { static from(event) { return new _a34(event.ndk, event.rawEvent()); } /** * The target ID () of the wiki event to merge into. */ get targetId() { return this.tagValue("a"); } /** * Sets the target ID () of the wiki event to merge into. */ set target(targetEvent) { this.tags = this.tags.filter((tag) => { if (tag[0] === "a") return true; if (tag[0] === "e" && tag[3] !== "source") return true; }); this.tag(targetEvent); } /** * The source ID of the wiki event to merge from. */ get sourceId() { return this.tagValue("e", "source"); } /** * Sets the event we are asking to get merged into the target. */ set source(sourceEvent) { this.removeTag("e", "source"); this.tag(sourceEvent, "source", false, "e"); } }, __publicField(_a34, "kind", 818), __publicField(_a34, "kinds", [ 818 /* WikiMergeRequest */ ]), _a34); registeredEventClasses2 = /* @__PURE__ */ new Set(); NDKSubscriptionCacheUsage2 = /* @__PURE__ */ ((NDKSubscriptionCacheUsage22) => { NDKSubscriptionCacheUsage22["ONLY_CACHE"] = "ONLY_CACHE"; NDKSubscriptionCacheUsage22["CACHE_FIRST"] = "CACHE_FIRST"; NDKSubscriptionCacheUsage22["PARALLEL"] = "PARALLEL"; NDKSubscriptionCacheUsage22["ONLY_RELAY"] = "ONLY_RELAY"; return NDKSubscriptionCacheUsage22; })(NDKSubscriptionCacheUsage2 || {}); NIP05_REGEX2 = /^(?:([\w.+-]+)@)?([\w.-]+)$/; NDKUser2 = class _NDKUser { constructor(opts) { __publicField(this, "ndk"); __publicField(this, "profile"); __publicField(this, "profileEvent"); __publicField(this, "_npub"); __publicField(this, "_pubkey"); __publicField(this, "relayUrls", []); __publicField(this, "nip46Urls", []); /** * Returns a set of users that this user follows. * * @deprecated Use followSet instead */ __publicField(this, "follows", follows2.bind(this)); if (opts.npub) this._npub = opts.npub; if (opts.hexpubkey) this._pubkey = opts.hexpubkey; if (opts.pubkey) this._pubkey = opts.pubkey; if (opts.relayUrls) this.relayUrls = opts.relayUrls; if (opts.nip46Urls) this.nip46Urls = opts.nip46Urls; if (opts.nprofile) { try { const decoded = import_nostr_tools15.nip19.decode(opts.nprofile); if (decoded.type === "nprofile") { this._pubkey = decoded.data.pubkey; if (decoded.data.relays && decoded.data.relays.length > 0) { this.relayUrls.push(...decoded.data.relays); } } } catch (e2) { console.error("Failed to decode nprofile", e2); } } } get npub() { if (!this._npub) { if (!this._pubkey) throw new Error("pubkey not set"); this._npub = import_nostr_tools15.nip19.npubEncode(this.pubkey); } return this._npub; } get nprofile() { const relays = this.profileEvent?.onRelays?.map((r) => r.url); return import_nostr_tools15.nip19.nprofileEncode({ pubkey: this.pubkey, relays }); } set npub(npub22) { this._npub = npub22; } /** * Get the user's pubkey * @returns {string} The user's pubkey */ get pubkey() { if (!this._pubkey) { if (!this._npub) throw new Error("npub not set"); this._pubkey = import_nostr_tools15.nip19.decode(this.npub).data; } return this._pubkey; } /** * Set the user's pubkey * @param pubkey {string} The user's pubkey */ set pubkey(pubkey) { this._pubkey = pubkey; } /** * Equivalent to NDKEvent.filters(). * @returns {NDKFilter} */ filter() { return { "#p": [this.pubkey] }; } /** * Gets NIP-57 and NIP-61 information that this user has signaled * * @param getAll {boolean} Whether to get all zap info or just the first one */ async getZapInfo(timeoutMs) { if (!this.ndk) throw new Error("No NDK instance found"); const promiseWithTimeout = async (promise) => { if (!timeoutMs) return promise; let timeoutId; const timeoutPromise = new Promise((_2, reject) => { timeoutId = setTimeout(() => reject(new Error("Timeout")), timeoutMs); }); try { const result = await Promise.race([promise, timeoutPromise]); if (timeoutId) clearTimeout(timeoutId); return result; } catch (e2) { if (e2 instanceof Error && e2.message === "Timeout") { try { const result = await promise; return result; } catch (_originalError) { return void 0; } } return void 0; } }; const [userProfile, mintListEvent] = await Promise.all([ promiseWithTimeout(this.fetchProfile()), promiseWithTimeout( this.ndk.fetchEvent({ kinds: [ 10019 /* CashuMintList */ ], authors: [this.pubkey] }) ) ]); const res = /* @__PURE__ */ new Map(); if (mintListEvent) { const mintList = NDKCashuMintList2.from(mintListEvent); if (mintList.mints.length > 0) { res.set("nip61", { mints: mintList.mints, relays: mintList.relays, p2pk: mintList.p2pk }); } } if (userProfile) { const { lud06, lud16 } = userProfile; res.set("nip57", { lud06, lud16 }); } return res; } /** * Instantiate an NDKUser from a NIP-05 string * @param nip05Id {string} The user's NIP-05 * @param ndk {NDK} An NDK instance * @param skipCache {boolean} Whether to skip the cache or not * @returns {NDKUser | undefined} An NDKUser if one is found for the given NIP-05, undefined otherwise. */ static async fromNip05(nip05Id, ndk, skipCache = false) { if (!ndk) throw new Error("No NDK instance found"); const opts = {}; if (skipCache) opts.cache = "no-cache"; const profile = await getNip05For2(ndk, nip05Id, ndk?.httpFetch, opts); if (profile) { const user = new _NDKUser({ pubkey: profile.pubkey, relayUrls: profile.relays, nip46Urls: profile.nip46 }); user.ndk = ndk; return user; } } /** * Fetch a user's profile * @param opts {NDKSubscriptionOptions} A set of NDKSubscriptionOptions * @param storeProfileEvent {boolean} Whether to store the profile event or not * @returns User Profile */ async fetchProfile(opts, storeProfileEvent = false) { if (!this.ndk) throw new Error("NDK not set"); let setMetadataEvent = null; if (this.ndk.cacheAdapter && (this.ndk.cacheAdapter.fetchProfile || this.ndk.cacheAdapter.fetchProfileSync) && opts?.cacheUsage !== "ONLY_RELAY") { let profile = null; if (this.ndk.cacheAdapter.fetchProfileSync) { profile = this.ndk.cacheAdapter.fetchProfileSync(this.pubkey); } else if (this.ndk.cacheAdapter.fetchProfile) { profile = await this.ndk.cacheAdapter.fetchProfile(this.pubkey); } if (profile) { this.profile = profile; return profile; } } opts ?? (opts = {}); opts.cacheUsage ?? (opts.cacheUsage = "ONLY_RELAY"); opts.closeOnEose ?? (opts.closeOnEose = true); opts.groupable ?? (opts.groupable = true); opts.groupableDelay ?? (opts.groupableDelay = 25); if (!setMetadataEvent) { setMetadataEvent = await this.ndk.fetchEvent( { kinds: [0], authors: [this.pubkey] }, opts ); } if (!setMetadataEvent) return null; this.profile = profileFromEvent2(setMetadataEvent); if (storeProfileEvent && this.profile && this.ndk.cacheAdapter && this.ndk.cacheAdapter.saveProfile) { this.ndk.cacheAdapter.saveProfile(this.pubkey, this.profile); } return this.profile; } /** * Returns a set of pubkeys that this user follows. * * @param opts - NDKSubscriptionOptions * @param outbox - boolean * @param kind - number */ async followSet(opts, outbox, kind = 3) { const follows22 = await this.follows(opts, outbox, kind); return new Set(Array.from(follows22).map((f) => f.pubkey)); } /** @deprecated Use referenceTags instead. */ /** * Get the tag that can be used to reference this user in an event * @returns {NDKTag} an NDKTag */ tagReference() { return ["p", this.pubkey]; } /** * Get the tags that can be used to reference this user in an event * @returns {NDKTag[]} an array of NDKTag */ referenceTags(marker) { const tag = [["p", this.pubkey]]; if (!marker) return tag; tag[0].push("", marker); return tag; } /** * Publishes the current profile. */ async publish() { if (!this.ndk) throw new Error("No NDK instance found"); if (!this.profile) throw new Error("No profile available"); this.ndk.assertSigner(); const event = new NDKEvent2(this.ndk, { kind: 0, content: serializeProfile2(this.profile) }); await event.publish(); } /** * Add one or more follows to this user's contact list * * @param newFollow {NDKUser | Hexpubkey | Array} The user(s) to follow * @param currentFollowList {Set} The current follow list * @param kind {NDKKind} The kind to use for this contact list (defaults to `3`) * @returns {Promise} True if any follows were added, false if all already exist */ async follow(newFollow, currentFollowList, kind = 3) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); if (!currentFollowList) { currentFollowList = await this.follows(void 0, void 0, kind); } const followsToAdd = Array.isArray(newFollow) ? newFollow : [newFollow]; let anyAdded = false; for (const follow of followsToAdd) { const followPubkey = typeof follow === "string" ? follow : follow.pubkey; const isAlreadyFollowing = Array.from(currentFollowList).some( (item) => typeof item === "string" ? item === followPubkey : item.pubkey === followPubkey ); if (!isAlreadyFollowing) { currentFollowList.add(follow); anyAdded = true; } } if (!anyAdded) { return false; } const event = new NDKEvent2(this.ndk, { kind }); for (const follow of currentFollowList) { if (typeof follow === "string") { event.tags.push(["p", follow]); } else { event.tag(follow); } } await event.publish(); return true; } /** * Remove one or more follows from this user's contact list * * @param user {NDKUser | Hexpubkey | Array} The user(s) to unfollow * @param currentFollowList {Set} The current follow list * @param kind {NDKKind} The kind to use for this contact list (defaults to `3`) * @returns The relays where the follow list was published or false if none were found */ async unfollow(user, currentFollowList, kind = 3) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); if (!currentFollowList) { currentFollowList = await this.follows(void 0, void 0, kind); } const usersToUnfollow = Array.isArray(user) ? user : [user]; const unfollowPubkeys = new Set( usersToUnfollow.map((u3) => typeof u3 === "string" ? u3 : u3.pubkey) ); const newUserFollowList = /* @__PURE__ */ new Set(); let foundAny = false; for (const follow of currentFollowList) { const followPubkey = typeof follow === "string" ? follow : follow.pubkey; if (!unfollowPubkeys.has(followPubkey)) { newUserFollowList.add(follow); } else { foundAny = true; } } if (!foundAny) return false; const event = new NDKEvent2(this.ndk, { kind }); for (const follow of newUserFollowList) { if (typeof follow === "string") { event.tags.push(["p", follow]); } else { event.tag(follow); } } return await event.publish(); } /** * Validate a user's NIP-05 identifier (usually fetched from their kind:0 profile data) * * @param nip05Id The NIP-05 string to validate * @returns {Promise} True if the NIP-05 is found and matches this user's pubkey, * False if the NIP-05 is found but doesn't match this user's pubkey, * null if the NIP-05 isn't found on the domain or we're unable to verify (because of network issues, etc.) */ async validateNip05(nip05Id) { if (!this.ndk) throw new Error("No NDK instance found"); const profilePointer = await getNip05For2(this.ndk, nip05Id); if (profilePointer === null) return null; return profilePointer.pubkey === this.pubkey; } }; signerRegistry2 = /* @__PURE__ */ new Map(); NDKPrivateKeySigner2 = class _NDKPrivateKeySigner { /** * Create a new signer from a private key. * @param privateKey - The private key to use in hex form or nsec. * @param ndk - The NDK instance to use. * * @ai-guardrail * If you have an nsec (bech32-encoded private key starting with "nsec1"), you can pass it directly * to this constructor without decoding it first. The constructor handles both hex and nsec formats automatically. * DO NOT use nip19.decode() to convert nsec to hex before passing it here - just pass the nsec string directly. */ constructor(privateKeyOrNsec, ndk) { __publicField(this, "_user"); __publicField(this, "_privateKey"); __publicField(this, "_pubkey"); if (typeof privateKeyOrNsec === "string") { if (privateKeyOrNsec.startsWith("nsec1")) { const { type, data } = import_nostr_tools14.nip19.decode(privateKeyOrNsec); if (type === "nsec") this._privateKey = data; else throw new Error("Invalid private key provided."); } else if (privateKeyOrNsec.length === 64) { this._privateKey = hexToBytes(privateKeyOrNsec); } else { throw new Error("Invalid private key provided."); } } else { this._privateKey = privateKeyOrNsec; } this._pubkey = (0, import_nostr_tools14.getPublicKey)(this._privateKey); if (ndk) this._user = ndk.getUser({ pubkey: this._pubkey }); this._user ?? (this._user = new NDKUser2({ pubkey: this._pubkey })); } /** * Get the private key in hex form. */ get privateKey() { if (!this._privateKey) throw new Error("Not ready"); return bytesToHex(this._privateKey); } /** * Get the public key in hex form. */ get pubkey() { if (!this._pubkey) throw new Error("Not ready"); return this._pubkey; } /** * Get the private key in nsec form. */ get nsec() { if (!this._privateKey) throw new Error("Not ready"); return import_nostr_tools14.nip19.nsecEncode(this._privateKey); } /** * Get the public key in npub form. */ get npub() { if (!this._pubkey) throw new Error("Not ready"); return import_nostr_tools14.nip19.npubEncode(this._pubkey); } /** * Encrypt the private key with a password to ncryptsec format. * @param password - The password to encrypt the private key. * @param logn - The log2 of the scrypt N parameter (default: 16). * @param ksb - The key security byte (0x00, 0x01, or 0x02, default: 0x02). * @returns The encrypted private key in ncryptsec format. * * @example * ```ts * const signer = new NDKPrivateKeySigner(nsec); * const ncryptsec = signer.encryptToNcryptsec("my-password"); * console.log('encrypted key:', ncryptsec); * ``` */ encryptToNcryptsec(password, logn = 16, ksb = 2) { if (!this._privateKey) throw new Error("Private key not available"); return nip492.encrypt(this._privateKey, password, logn, ksb); } /** * Generate a new private key. */ static generate() { const privateKey = (0, import_nostr_tools14.generateSecretKey)(); return new _NDKPrivateKeySigner(privateKey); } /** * Create a signer from an encrypted private key (ncryptsec) using a password. * @param ncryptsec - The encrypted private key in ncryptsec format. * @param password - The password to decrypt the private key. * @param ndk - Optional NDK instance. * @returns A new NDKPrivateKeySigner instance. * * @example * ```ts * const signer = NDKPrivateKeySigner.fromNcryptsec( * "ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p", * "my-password" * ); * console.log('your pubkey is', signer.pubkey); * ``` */ static fromNcryptsec(ncryptsec, password, ndk) { const privateKeyBytes = nip492.decrypt(ncryptsec, password); return new _NDKPrivateKeySigner(privateKeyBytes, ndk); } /** * Noop in NDKPrivateKeySigner. */ async blockUntilReady() { return this._user; } /** * Get the user. */ async user() { return this._user; } /** * Get the user. */ get userSync() { return this._user; } async sign(event) { if (!this._privateKey) { throw Error("Attempted to sign without a private key"); } return (0, import_nostr_tools14.finalizeEvent)(event, this._privateKey).sig; } async encryptionEnabled(scheme) { const enabled = []; if (!scheme || scheme === "nip04") enabled.push("nip04"); if (!scheme || scheme === "nip44") enabled.push("nip44"); return enabled; } async encrypt(recipient, value, scheme) { if (!this._privateKey || !this.privateKey) { throw Error("Attempted to encrypt without a private key"); } const recipientHexPubKey = recipient.pubkey; if (scheme === "nip44") { const conversationKey = import_nostr_tools14.nip44.v2.utils.getConversationKey(this._privateKey, recipientHexPubKey); return await import_nostr_tools14.nip44.v2.encrypt(value, conversationKey); } return await import_nostr_tools14.nip04.encrypt(this._privateKey, recipientHexPubKey, value); } async decrypt(sender, value, scheme) { if (!this._privateKey || !this.privateKey) { throw Error("Attempted to decrypt without a private key"); } const senderHexPubKey = sender.pubkey; if (scheme === "nip44") { const conversationKey = import_nostr_tools14.nip44.v2.utils.getConversationKey(this._privateKey, senderHexPubKey); return await import_nostr_tools14.nip44.v2.decrypt(value, conversationKey); } return await import_nostr_tools14.nip04.decrypt(this._privateKey, senderHexPubKey, value); } /** * Serializes the signer's private key into a storable format. * @returns A JSON string containing the type and the hex private key. */ toPayload() { if (!this._privateKey) throw new Error("Private key not available"); const payload = { type: "private-key", payload: this.privateKey // Use the hex private key }; return JSON.stringify(payload); } /** * Deserializes the signer from a payload string. * @param payloadString The JSON string obtained from toPayload(). * @param ndk Optional NDK instance. * @returns An instance of NDKPrivateKeySigner. */ static async fromPayload(payloadString, ndk) { const payload = JSON.parse(payloadString); if (payload.type !== "private-key") { throw new Error(`Invalid payload type: expected 'private-key', got ${payload.type}`); } if (!payload.payload || typeof payload.payload !== "string") { throw new Error("Invalid payload content for private-key signer"); } return new _NDKPrivateKeySigner(payload.payload, ndk); } }; registerSigner2("private-key", NDKPrivateKeySigner2); NDKCashuToken2 = (_a35 = class extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); __publicField(this, "_proofs", []); __publicField(this, "_mint"); /** * Tokens that this token superseeds */ __publicField(this, "_deletes", []); __publicField(this, "original"); this.kind ?? (this.kind = 7375); } static async from(event) { const token = new _a35(event.ndk, event); token.original = event; try { await token.decrypt(); } catch { token.content = token.original.content; } try { const content = JSON.parse(token.content); token.proofs = content.proofs; token.mint = content.mint ?? token.tagValue("mint"); token.deletedTokens = content.del ?? []; if (!Array.isArray(token.proofs)) return; } catch (_e2) { return; } return token; } get proofs() { return this._proofs; } set proofs(proofs) { const cs = /* @__PURE__ */ new Set(); this._proofs = proofs.filter((proof) => { if (cs.has(proof.C)) { console.warn("Passed in proofs had duplicates, ignoring", proof.C); return false; } if (proof.amount < 0) { console.warn("Invalid proof with negative amount", proof); return false; } cs.add(proof.C); return true; }).map(this.cleanProof); } /** * Returns a minimal proof object with only essential properties */ cleanProof(proof) { return { id: proof.id, amount: proof.amount, C: proof.C, secret: proof.secret }; } async toNostrEvent(pubkey) { if (!this.ndk) throw new Error("no ndk"); if (!this.ndk.signer) throw new Error("no signer"); const payload = { proofs: this.proofs.map(this.cleanProof), mint: this.mint, del: this.deletedTokens ?? [] }; this.content = JSON.stringify(payload); const user = await this.ndk.signer.user(); await this.encrypt(user, void 0, "nip44"); return super.toNostrEvent(pubkey); } set mint(mint) { this._mint = mint; } get mint() { return this._mint; } /** * Tokens that were deleted by the creation of this token. */ get deletedTokens() { return this._deletes; } /** * Marks tokens that were deleted by the creation of this token. */ set deletedTokens(tokenIds) { this._deletes = tokenIds; } get amount() { return proofsTotalBalance2(this.proofs); } async publish(relaySet, timeoutMs, requiredRelayCount) { if (this.original) { return this.original.publish(relaySet, timeoutMs, requiredRelayCount); } return super.publish(relaySet, timeoutMs, requiredRelayCount); } }, __publicField(_a35, "kind", 7375), __publicField(_a35, "kinds", [ 7375 /* CashuToken */ ]), _a35); MARKERS2 = { REDEEMED: "redeemed", CREATED: "created", DESTROYED: "destroyed", RESERVED: "reserved" }; NDKCashuWalletTx2 = (_a36 = class extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 7376); } static async from(event) { const walletChange = new _a36(event.ndk, event); const prevContent = walletChange.content; try { await walletChange.decrypt(); } catch (_e2) { walletChange.content ?? (walletChange.content = prevContent); } try { const contentTags = JSON.parse(walletChange.content); walletChange.tags = [...contentTags, ...walletChange.tags]; } catch (_e2) { return; } return walletChange; } set direction(direction) { this.removeTag("direction"); if (direction) this.tags.push(["direction", direction]); } get direction() { return this.tagValue("direction"); } set amount(amount) { this.removeTag("amount"); this.tags.push(["amount", amount.toString()]); } get amount() { const val = this.tagValue("amount"); if (val === void 0) return void 0; return Number(val); } set fee(fee) { this.removeTag("fee"); this.tags.push(["fee", fee.toString()]); } get fee() { const val = this.tagValue("fee"); if (val === void 0) return void 0; return Number(val); } set unit(unit) { this.removeTag("unit"); if (unit) this.tags.push(["unit", unit.toString()]); } get unit() { return this.tagValue("unit"); } set description(description) { this.removeTag("description"); if (description) this.tags.push(["description", description.toString()]); } get description() { return this.tagValue("description"); } set mint(mint) { this.removeTag("mint"); if (mint) this.tags.push(["mint", mint.toString()]); } get mint() { return this.tagValue("mint"); } /** * Tags tokens that were created in this history event */ set destroyedTokens(events) { for (const event of events) { this.tags.push(event.tagReference(MARKERS2.DESTROYED)); } } set destroyedTokenIds(ids) { for (const id of ids) { this.tags.push(["e", id, "", MARKERS2.DESTROYED]); } } /** * Tags tokens that were created in this history event */ set createdTokens(events) { for (const event of events) { this.tags.push(event.tagReference(MARKERS2.CREATED)); } } set reservedTokens(events) { for (const event of events) { this.tags.push(event.tagReference(MARKERS2.RESERVED)); } } addRedeemedNutzap(event) { this.tag(event, MARKERS2.REDEEMED); } async toNostrEvent(pubkey) { const encryptedTags = []; const unencryptedTags = []; for (const tag of this.tags) { if (!this.shouldEncryptTag(tag)) { unencryptedTags.push(tag); } else { encryptedTags.push(tag); } } this.tags = unencryptedTags.filter((t) => t[0] !== "client"); this.content = JSON.stringify(encryptedTags); const user = await this.ndk?.signer?.user(); if (user) { const ownPubkey = user.pubkey; this.tags = this.tags.filter((t) => t[0] !== "p" || t[1] !== ownPubkey); } await this.encrypt(user, void 0, "nip44"); return super.toNostrEvent(pubkey); } /** * Whether this entry includes a redemption of a Nutzap */ get hasNutzapRedemption() { return this.getMatchingTags("e", MARKERS2.REDEEMED).length > 0; } shouldEncryptTag(tag) { const unencryptedTagNames = ["client"]; if (unencryptedTagNames.includes(tag[0])) { return false; } if (tag[0] === "e" && tag[3] === MARKERS2.REDEEMED) { return false; } if (tag[0] === "p") return false; return true; } }, __publicField(_a36, "MARKERS", MARKERS2), __publicField(_a36, "kind", 7376), __publicField(_a36, "kinds", [ 7376 /* CashuWalletTx */ ]), _a36); debug62 = (0, import_debug20.default)("ndk:active-user"); nip19_exports2 = {}; __reExport2(nip19_exports2, nip19_star2); nip49_exports2 = {}; __reExport2(nip49_exports2, nip49_star2); NDKRelayAuthPolicies2 = { disconnect: disconnect2, signIn: signIn2 }; NDKNip07Signer2 = class _NDKNip07Signer { /** * @param waitTimeout - The timeout in milliseconds to wait for the NIP-07 to become available */ constructor(waitTimeout = 1e3, ndk) { __publicField(this, "_userPromise"); __publicField(this, "encryptionQueue", []); __publicField(this, "encryptionProcessing", false); __publicField(this, "debug"); __publicField(this, "waitTimeout"); __publicField(this, "_pubkey"); __publicField(this, "ndk"); __publicField(this, "_user"); this.debug = (0, import_debug22.default)("ndk:nip07"); this.waitTimeout = waitTimeout; this.ndk = ndk; } get pubkey() { if (!this._pubkey) throw new Error("Not ready"); return this._pubkey; } async blockUntilReady() { await this.waitForExtension(); const pubkey = await window.nostr?.getPublicKey(); if (!pubkey) { throw new Error("User rejected access"); } this._pubkey = pubkey; let user; if (this.ndk) user = this.ndk.getUser({ pubkey }); else user = new NDKUser2({ pubkey }); this._user = user; return user; } /** * Getter for the user property. * @returns The NDKUser instance. */ async user() { if (!this._userPromise) { this._userPromise = this.blockUntilReady(); } return this._userPromise; } get userSync() { if (!this._user) throw new Error("User not ready"); return this._user; } /** * Signs the given Nostr event. * @param event - The Nostr event to be signed. * @returns The signature of the signed event. * @throws Error if the NIP-07 is not available on the window object. */ async sign(event) { await this.waitForExtension(); const signedEvent = await window.nostr?.signEvent(event); if (!signedEvent) throw new Error("Failed to sign event"); return signedEvent.sig; } async relays(ndk) { await this.waitForExtension(); const relays = await window.nostr?.getRelays?.() || {}; const activeRelays = []; for (const url of Object.keys(relays)) { if (relays[url].read && relays[url].write) { activeRelays.push(url); } } return activeRelays.map((url) => new NDKRelay2(url, ndk?.relayAuthDefaultPolicy, ndk)); } async encryptionEnabled(nip) { const enabled = []; if ((!nip || nip === "nip04") && Boolean(window.nostr?.nip04)) enabled.push("nip04"); if ((!nip || nip === "nip44") && Boolean(window.nostr?.nip44)) enabled.push("nip44"); return enabled; } async encrypt(recipient, value, nip = "nip04") { if (!await this.encryptionEnabled(nip)) throw new Error(`${nip}encryption is not available from your browser extension`); await this.waitForExtension(); const recipientHexPubKey = recipient.pubkey; return this.queueEncryption(nip, "encrypt", recipientHexPubKey, value); } async decrypt(sender, value, nip = "nip04") { if (!await this.encryptionEnabled(nip)) throw new Error(`${nip}encryption is not available from your browser extension`); await this.waitForExtension(); const senderHexPubKey = sender.pubkey; return this.queueEncryption(nip, "decrypt", senderHexPubKey, value); } async queueEncryption(scheme, method, counterpartyHexpubkey, value) { return new Promise((resolve, reject) => { this.encryptionQueue.push({ scheme, method, counterpartyHexpubkey, value, resolve, reject }); if (!this.encryptionProcessing) { this.processEncryptionQueue(); } }); } async processEncryptionQueue(item, retries = 0) { if (!item && this.encryptionQueue.length === 0) { this.encryptionProcessing = false; return; } this.encryptionProcessing = true; const currentItem = item || this.encryptionQueue.shift(); if (!currentItem) { this.encryptionProcessing = false; return; } const { scheme, method, counterpartyHexpubkey, value, resolve, reject } = currentItem; this.debug("Processing encryption queue item", { method, counterpartyHexpubkey, value }); try { const result = await window.nostr?.[scheme]?.[method](counterpartyHexpubkey, value); if (!result) throw new Error("Failed to encrypt/decrypt"); resolve(result); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); if (errorMessage.includes("call already executing") && retries < 5) { this.debug("Retrying encryption queue item", { method, counterpartyHexpubkey, value, retries }); setTimeout(() => { this.processEncryptionQueue(currentItem, retries + 1); }, 50 * retries); return; } reject(error instanceof Error ? error : new Error(errorMessage)); } this.processEncryptionQueue(); } waitForExtension() { return new Promise((resolve, reject) => { if (window.nostr) { resolve(); return; } let timerId; const intervalId = setInterval(() => { if (window.nostr) { clearTimeout(timerId); clearInterval(intervalId); resolve(); } }, 100); timerId = setTimeout(() => { clearInterval(intervalId); reject(new Error("NIP-07 extension not available")); }, this.waitTimeout); }); } /** * Serializes the signer type into a storable format. * NIP-07 signers don't have persistent state to serialize beyond their type. * @returns A JSON string containing the type. */ toPayload() { const payload = { type: "nip07", payload: "" // No specific payload needed for NIP-07 }; return JSON.stringify(payload); } /** * Deserializes the signer from a payload string. * Creates a new NDKNip07Signer instance. * @param payloadString The JSON string obtained from toPayload(). * @param ndk Optional NDK instance. * @returns An instance of NDKNip07Signer. */ static async fromPayload(payloadString, ndk) { const payload = JSON.parse(payloadString); if (payload.type !== "nip07") { throw new Error(`Invalid payload type: expected 'nip07', got ${payload.type}`); } return new _NDKNip07Signer(void 0, ndk); } }; registerSigner2("nip07", NDKNip07Signer2); NDKNostrRpc2 = class extends import_tseep16.EventEmitter { constructor(ndk, signer, debug92, relayUrls) { super(); __publicField(this, "ndk"); __publicField(this, "signer"); __publicField(this, "relaySet"); __publicField(this, "debug"); __publicField(this, "encryptionType", "nip44"); __publicField(this, "pool"); this.ndk = ndk; this.signer = signer; if (relayUrls) { this.pool = new NDKPool2(relayUrls, ndk, { debug: debug92.extend("rpc-pool"), name: "Nostr RPC" }); this.relaySet = new NDKRelaySet2(/* @__PURE__ */ new Set(), ndk, this.pool); for (const url of relayUrls) { const relay = this.pool.getRelay(url, false, false); relay.authPolicy = NDKRelayAuthPolicies2.signIn({ ndk, signer, debug: debug92 }); this.relaySet.addRelay(relay); relay.connect(); } } this.debug = debug92.extend("rpc"); } /** * Subscribe to a filter. This function will resolve once the subscription is ready. */ subscribe(filter) { return new Promise((resolve) => { const sub = this.ndk.subscribe(filter, { closeOnEose: false, groupable: false, cacheUsage: "ONLY_RELAY", pool: this.pool, relaySet: this.relaySet, onEvent: async (event) => { try { const parsedEvent = await this.parseEvent(event); if (parsedEvent.method) { this.emit("request", parsedEvent); } else { this.emit(`response-${parsedEvent.id}`, parsedEvent); this.emit("response", parsedEvent); } } catch (e2) { this.debug("error parsing event", e2, event.rawEvent()); } }, onEose: () => { this.debug("eosed"); resolve(sub); } }); }); } async parseEvent(event) { if (this.encryptionType === "nip44" && event.content.includes("?iv=")) { this.encryptionType = "nip04"; } else if (this.encryptionType === "nip04" && !event.content.includes("?iv=")) { this.encryptionType = "nip44"; } const remoteUser = this.ndk.getUser({ pubkey: event.pubkey }); remoteUser.ndk = this.ndk; let decryptedContent; try { decryptedContent = await this.signer.decrypt(remoteUser, event.content, this.encryptionType); } catch (_e2) { const otherEncryptionType = this.encryptionType === "nip04" ? "nip44" : "nip04"; decryptedContent = await this.signer.decrypt(remoteUser, event.content, otherEncryptionType); this.encryptionType = otherEncryptionType; } const parsedContent = JSON.parse(decryptedContent); const { id, method, params, result, error } = parsedContent; if (method) { return { id, pubkey: event.pubkey, method, params, event }; } return { id, result, error, event }; } async sendResponse(id, remotePubkey, result, kind = 24133, error) { const res = { id, result }; if (error) { res.error = error; } const localUser = await this.signer.user(); const remoteUser = this.ndk.getUser({ pubkey: remotePubkey }); const event = new NDKEvent2(this.ndk, { kind, content: JSON.stringify(res), tags: [["p", remotePubkey]], pubkey: localUser.pubkey }); event.content = await this.signer.encrypt(remoteUser, event.content, this.encryptionType); await event.sign(this.signer); await event.publish(this.relaySet); } /** * Sends a request. * @param remotePubkey * @param method * @param params * @param kind * @param id */ async sendRequest(remotePubkey, method, params = [], kind = 24133, cb) { const id = Math.random().toString(36).substring(7); const localUser = await this.signer.user(); const remoteUser = this.ndk.getUser({ pubkey: remotePubkey }); const request = { id, method, params }; const promise = new Promise(() => { const responseHandler = (response) => { if (response.result === "auth_url") { this.once(`response-${id}`, responseHandler); this.emit("authUrl", response.error); } else if (cb) { cb(response); } }; this.once(`response-${id}`, responseHandler); }); const event = new NDKEvent2(this.ndk, { kind, content: JSON.stringify(request), tags: [["p", remotePubkey]], pubkey: localUser.pubkey }); event.content = await this.signer.encrypt(remoteUser, event.content, this.encryptionType); await event.sign(this.signer); await event.publish(this.relaySet); return promise; } }; NDKNip46Signer2 = class _NDKNip46Signer extends import_tseep17.EventEmitter { /** * * Don't instantiate this directly. Use the static methods instead. * * @example: * // for bunker:// flow * const signer = NDKNip46Signer.bunker(ndk, "bunker://") * const signer = NDKNip46Signer.bunker(ndk, ""); // with nip05 flow * // for nostrconnect:// flow * const signer = NDKNip46Signer.nostrconnect(ndk, "wss://relay.example.com") * * @param ndk - The NDK instance to use * @param userOrConnectionToken - The public key, or a connection token, of the npub that wants to be published as * @param localSigner - The signer that will be used to request events to be signed */ constructor(ndk, userOrConnectionToken, localSigner, relayUrls, nostrConnectOptions) { super(); __publicField(this, "ndk"); __publicField(this, "_user"); /** * The pubkey of the bunker that will be providing signatures */ __publicField(this, "bunkerPubkey"); /** * The pubkey of the user that events will be published as */ __publicField(this, "userPubkey"); /** * An optional secret value provided to connect to the bunker */ __publicField(this, "secret"); __publicField(this, "localSigner"); __publicField(this, "nip05"); __publicField(this, "rpc"); __publicField(this, "debug"); __publicField(this, "relayUrls"); __publicField(this, "subscription"); /** * If using nostrconnect://, stores the nostrConnectURI */ __publicField(this, "nostrConnectUri"); /** * The random secret used for nostrconnect:// flows. */ __publicField(this, "nostrConnectSecret"); this.ndk = ndk; this.debug = ndk.debug.extend("nip46:signer"); this.relayUrls = relayUrls; if (!localSigner) { this.localSigner = NDKPrivateKeySigner2.generate(); } else { if (typeof localSigner === "string") { this.localSigner = new NDKPrivateKeySigner2(localSigner); } else { this.localSigner = localSigner; } } if (userOrConnectionToken === false) { } else if (!userOrConnectionToken) { this.nostrconnectFlowInit(nostrConnectOptions); } else if (userOrConnectionToken.startsWith("bunker://")) { this.bunkerFlowInit(userOrConnectionToken); } else { this.nip05Init(userOrConnectionToken); } this.rpc = new NDKNostrRpc2(this.ndk, this.localSigner, this.debug, this.relayUrls); } get pubkey() { if (!this.userPubkey) throw new Error("Not ready"); return this.userPubkey; } /** * Connnect with a bunker:// flow * @param ndk * @param userOrConnectionToken bunker:// connection string * @param localSigner If you have previously authenticated with this signer, you can restore the session by providing the previously authenticated key */ static bunker(ndk, userOrConnectionToken, localSigner) { return new _NDKNip46Signer(ndk, userOrConnectionToken, localSigner); } /** * Connect with a nostrconnect:// flow * @param ndk * @param relay - Relay used to connect with the signer * @param localSigner If you have previously authenticated with this signer, you can restore the session by providing the previously authenticated key */ static nostrconnect(ndk, relay, localSigner, nostrConnectOptions) { return new _NDKNip46Signer(ndk, void 0, localSigner, [relay], nostrConnectOptions); } nostrconnectFlowInit(nostrConnectOptions) { this.nostrConnectSecret = nostrConnectGenerateSecret2(); const pubkey = this.localSigner.pubkey; this.nostrConnectUri = generateNostrConnectUri2( pubkey, this.nostrConnectSecret, this.relayUrls?.[0], nostrConnectOptions ); } bunkerFlowInit(connectionToken) { const bunkerUrl = new URL(connectionToken); const bunkerPubkey = bunkerUrl.hostname || bunkerUrl.pathname.replace(/^\/\//, ""); const userPubkey = bunkerUrl.searchParams.get("pubkey"); const relayUrls = bunkerUrl.searchParams.getAll("relay"); const secret = bunkerUrl.searchParams.get("secret"); this.bunkerPubkey = bunkerPubkey; this.userPubkey = userPubkey; this.relayUrls = relayUrls; this.secret = secret; } nip05Init(nip05) { this.nip05 = nip05; } /** * We start listening for events from the bunker */ async startListening() { if (this.subscription) return; const localUser = await this.localSigner.user(); if (!localUser) throw new Error("Local signer not ready"); this.subscription = await this.rpc.subscribe({ kinds: [ 24133 /* NostrConnect */ ], "#p": [localUser.pubkey] }); } /** * Get the user that is being published as */ async user() { if (this._user) return this._user; return this.blockUntilReady(); } get userSync() { if (!this._user) throw new Error("Remote user not ready synchronously"); return this._user; } async blockUntilReadyNostrConnect() { return new Promise((resolve, reject) => { const connect = (response) => { if (response.result === this.nostrConnectSecret) { this._user = response.event.author; this.userPubkey = response.event.pubkey; this.bunkerPubkey = response.event.pubkey; this.rpc.off("response", connect); resolve(this._user); } }; this.startListening(); this.rpc.on("response", connect); }); } async blockUntilReady() { if (!this.bunkerPubkey && !this.nostrConnectSecret && !this.nip05) { throw new Error("Bunker pubkey not set"); } if (this.nostrConnectSecret) return this.blockUntilReadyNostrConnect(); if (this.nip05 && !this.userPubkey) { const user = await NDKUser2.fromNip05(this.nip05, this.ndk); if (user) { this._user = user; this.userPubkey = user.pubkey; this.relayUrls = user.nip46Urls; this.rpc = new NDKNostrRpc2(this.ndk, this.localSigner, this.debug, this.relayUrls); } } if (!this.bunkerPubkey && this.userPubkey) { this.bunkerPubkey = this.userPubkey; } else if (!this.bunkerPubkey) { throw new Error("Bunker pubkey not set"); } await this.startListening(); this.rpc.on("authUrl", (...props) => { this.emit("authUrl", ...props); }); return new Promise((resolve, reject) => { const connectParams = [this.userPubkey ?? ""]; if (this.secret) connectParams.push(this.secret); if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest(this.bunkerPubkey, "connect", connectParams, 24133, (response) => { if (response.result === "ack") { this.getPublicKey().then((pubkey) => { this.userPubkey = pubkey; this._user = this.ndk.getUser({ pubkey }); resolve(this._user); }); } else { reject(response.error); } }); }); } stop() { this.subscription?.stop(); this.subscription = void 0; } async getPublicKey() { if (this.userPubkey) return this.userPubkey; return new Promise((resolve, _reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest(this.bunkerPubkey, "get_public_key", [], 24133, (response) => { resolve(response.result); }); }); } async encryptionEnabled(scheme) { if (scheme) return [scheme]; return Promise.resolve(["nip04", "nip44"]); } async encrypt(recipient, value, scheme = "nip04") { return this.encryption(recipient, value, scheme, "encrypt"); } async decrypt(sender, value, scheme = "nip04") { return this.encryption(sender, value, scheme, "decrypt"); } async encryption(peer, value, scheme, method) { const promise = new Promise((resolve, reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest( this.bunkerPubkey, `${scheme}_${method}`, [peer.pubkey, value], 24133, (response) => { if (!response.error) { resolve(response.result); } else { reject(response.error); } } ); }); return promise; } async sign(event) { const promise = new Promise((resolve, reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest( this.bunkerPubkey, "sign_event", [JSON.stringify(event)], 24133, (response) => { if (!response.error) { const json = JSON.parse(response.result); resolve(json.sig); } else { reject(response.error); } } ); }); return promise; } /** * Allows creating a new account on the remote server. * @param username Desired username for the NIP-05 * @param domain Desired domain for the NIP-05 * @param email Email address to associate with this account -- Remote servers may use this for recovery * @returns The public key of the newly created account */ async createAccount(username, domain, email) { await this.startListening(); const req = []; if (username) req.push(username); if (domain) req.push(domain); if (email) req.push(email); return new Promise((resolve, reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest( this.bunkerPubkey, "create_account", req, 24133, (response) => { if (!response.error) { const pubkey = response.result; resolve(pubkey); } else { reject(response.error); } } ); }); } /** * Serializes the signer's connection details and local signer state. * @returns A JSON string containing the type, connection info, and local signer payload. */ toPayload() { if (!this.bunkerPubkey || !this.userPubkey) { throw new Error("NIP-46 signer is not fully initialized for serialization"); } const payload = { type: "nip46", payload: { bunkerPubkey: this.bunkerPubkey, userPubkey: this.userPubkey, relayUrls: this.relayUrls, secret: this.secret, localSignerPayload: this.localSigner.toPayload(), // Store nip05 if it was used for initialization, otherwise null nip05: this.nip05 || null } }; return JSON.stringify(payload); } /** * Deserializes the signer from a payload string. * @param payloadString The JSON string obtained from toPayload(). * @param ndk The NDK instance, required for NIP-46. * @returns An instance of NDKNip46Signer. */ static async fromPayload(payloadString, ndk) { if (!ndk) { throw new Error("NDK instance is required to deserialize NIP-46 signer"); } const parsed = JSON.parse(payloadString); if (parsed.type !== "nip46") { throw new Error(`Invalid payload type: expected 'nip46', got ${parsed.type}`); } const payload = parsed.payload; if (!payload || typeof payload !== "object" || !payload.localSignerPayload) { throw new Error("Invalid payload content for nip46 signer"); } const localSigner = await ndkSignerFromPayload2(payload.localSignerPayload, ndk); if (!localSigner) { throw new Error("Failed to deserialize local signer for NIP-46"); } if (!(localSigner instanceof NDKPrivateKeySigner2)) { throw new Error("Local signer must be an instance of NDKPrivateKeySigner"); } let signer; signer = new _NDKNip46Signer(ndk, false, localSigner, payload.relayUrls); signer.userPubkey = payload.userPubkey; signer.bunkerPubkey = payload.bunkerPubkey; signer.relayUrls = payload.relayUrls; signer.secret = payload.secret; if (payload.userPubkey) { signer._user = new NDKUser2({ pubkey: payload.userPubkey }); if (signer._user) signer._user.ndk = ndk; } return signer; } }; registerSigner2("nip46", NDKNip46Signer2); d22 = (0, import_debug24.default)("ndk:zapper:ln"); d32 = (0, import_debug23.default)("ndk:zapper"); } }); // ndk/node_modules/dexie/dist/dexie.js var require_dexie = __commonJS({ "ndk/node_modules/dexie/dist/dexie.js"(exports2, module2) { (function(global2, factory) { typeof exports2 === "object" && typeof module2 !== "undefined" ? module2.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global2 = typeof globalThis !== "undefined" ? globalThis : global2 || self, global2.Dexie = factory()); })(exports2, (function() { "use strict"; var extendStatics = function(d17, b) { extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d18, b2) { d18.__proto__ = b2; } || function(d18, b2) { for (var p5 in b2) if (Object.prototype.hasOwnProperty.call(b2, p5)) d18[p5] = b2[p5]; }; return extendStatics(d17, b); }; function __extends(d17, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d17, b); function __() { this.constructor = d17; } d17.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign2(t) { for (var s, i3 = 1, n = arguments.length; i3 < n; i3++) { s = arguments[i3]; for (var p5 in s) if (Object.prototype.hasOwnProperty.call(s, p5)) t[p5] = s[p5]; } return t; }; return __assign.apply(this, arguments); }; function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i3 = 0, l3 = from.length, ar; i3 < l3; i3++) { if (ar || !(i3 in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i3); ar[i3] = from[i3]; } } return to.concat(ar || Array.prototype.slice.call(from)); } var _global = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : window; var keys = Object.keys; var isArray = Array.isArray; if (typeof Promise !== "undefined" && !_global.Promise) { _global.Promise = Promise; } function extend(obj, extension) { if (typeof extension !== "object") return obj; keys(extension).forEach(function(key) { obj[key] = extension[key]; }); return obj; } var getProto = Object.getPrototypeOf; var _hasOwn = {}.hasOwnProperty; function hasOwn(obj, prop) { return _hasOwn.call(obj, prop); } function props(proto, extension) { if (typeof extension === "function") extension = extension(getProto(proto)); (typeof Reflect === "undefined" ? keys : Reflect.ownKeys)(extension).forEach(function(key) { setProp(proto, key, extension[key]); }); } var defineProperty = Object.defineProperty; function setProp(obj, prop, functionOrGetSet, options) { defineProperty(obj, prop, extend(functionOrGetSet && hasOwn(functionOrGetSet, "get") && typeof functionOrGetSet.get === "function" ? { get: functionOrGetSet.get, set: functionOrGetSet.set, configurable: true } : { value: functionOrGetSet, configurable: true, writable: true }, options)); } function derive(Child) { return { from: function(Parent) { Child.prototype = Object.create(Parent.prototype); setProp(Child.prototype, "constructor", Child); return { extend: props.bind(null, Child.prototype) }; } }; } var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; function getPropertyDescriptor(obj, prop) { var pd = getOwnPropertyDescriptor(obj, prop); var proto; return pd || (proto = getProto(obj)) && getPropertyDescriptor(proto, prop); } var _slice = [].slice; function slice(args, start, end) { return _slice.call(args, start, end); } function override(origFunc, overridedFactory) { return overridedFactory(origFunc); } function assert2(b) { if (!b) throw new Error("Assertion Failed"); } function asap$1(fn) { if (_global.setImmediate) setImmediate(fn); else setTimeout(fn, 0); } function arrayToObject(array, extractor) { return array.reduce(function(result, item, i3) { var nameAndValue = extractor(item, i3); if (nameAndValue) result[nameAndValue[0]] = nameAndValue[1]; return result; }, {}); } function getByKeyPath(obj, keyPath) { if (typeof keyPath === "string" && hasOwn(obj, keyPath)) return obj[keyPath]; if (!keyPath) return obj; if (typeof keyPath !== "string") { var rv = []; for (var i3 = 0, l3 = keyPath.length; i3 < l3; ++i3) { var val = getByKeyPath(obj, keyPath[i3]); rv.push(val); } return rv; } var period = keyPath.indexOf("."); if (period !== -1) { var innerObj = obj[keyPath.substr(0, period)]; return innerObj == null ? void 0 : getByKeyPath(innerObj, keyPath.substr(period + 1)); } return void 0; } function setByKeyPath(obj, keyPath, value) { if (!obj || keyPath === void 0) return; if ("isFrozen" in Object && Object.isFrozen(obj)) return; if (typeof keyPath !== "string" && "length" in keyPath) { assert2(typeof value !== "string" && "length" in value); for (var i3 = 0, l3 = keyPath.length; i3 < l3; ++i3) { setByKeyPath(obj, keyPath[i3], value[i3]); } } else { var period = keyPath.indexOf("."); if (period !== -1) { var currentKeyPath = keyPath.substr(0, period); var remainingKeyPath = keyPath.substr(period + 1); if (remainingKeyPath === "") if (value === void 0) { if (isArray(obj) && !isNaN(parseInt(currentKeyPath))) obj.splice(currentKeyPath, 1); else delete obj[currentKeyPath]; } else obj[currentKeyPath] = value; else { var innerObj = obj[currentKeyPath]; if (!innerObj || !hasOwn(obj, currentKeyPath)) innerObj = obj[currentKeyPath] = {}; setByKeyPath(innerObj, remainingKeyPath, value); } } else { if (value === void 0) { if (isArray(obj) && !isNaN(parseInt(keyPath))) obj.splice(keyPath, 1); else delete obj[keyPath]; } else obj[keyPath] = value; } } } function delByKeyPath(obj, keyPath) { if (typeof keyPath === "string") setByKeyPath(obj, keyPath, void 0); else if ("length" in keyPath) [].map.call(keyPath, function(kp) { setByKeyPath(obj, kp, void 0); }); } function shallowClone(obj) { var rv = {}; for (var m in obj) { if (hasOwn(obj, m)) rv[m] = obj[m]; } return rv; } var concat = [].concat; function flatten(a) { return concat.apply([], a); } var intrinsicTypeNames = "BigUint64Array,BigInt64Array,Array,Boolean,String,Date,RegExp,Blob,File,FileList,FileSystemFileHandle,FileSystemDirectoryHandle,ArrayBuffer,DataView,Uint8ClampedArray,ImageBitmap,ImageData,Map,Set,CryptoKey".split(",").concat(flatten([8, 16, 32, 64].map(function(num3) { return ["Int", "Uint", "Float"].map(function(t) { return t + num3 + "Array"; }); }))).filter(function(t) { return _global[t]; }); var intrinsicTypes = new Set(intrinsicTypeNames.map(function(t) { return _global[t]; })); function cloneSimpleObjectTree(o) { var rv = {}; for (var k2 in o) if (hasOwn(o, k2)) { var v6 = o[k2]; rv[k2] = !v6 || typeof v6 !== "object" || intrinsicTypes.has(v6.constructor) ? v6 : cloneSimpleObjectTree(v6); } return rv; } function objectIsEmpty(o) { for (var k2 in o) if (hasOwn(o, k2)) return false; return true; } var circularRefs = null; function deepClone(any) { circularRefs = /* @__PURE__ */ new WeakMap(); var rv = innerDeepClone(any); circularRefs = null; return rv; } function innerDeepClone(x2) { if (!x2 || typeof x2 !== "object") return x2; var rv = circularRefs.get(x2); if (rv) return rv; if (isArray(x2)) { rv = []; circularRefs.set(x2, rv); for (var i3 = 0, l3 = x2.length; i3 < l3; ++i3) { rv.push(innerDeepClone(x2[i3])); } } else if (intrinsicTypes.has(x2.constructor)) { rv = x2; } else { var proto = getProto(x2); rv = proto === Object.prototype ? {} : Object.create(proto); circularRefs.set(x2, rv); for (var prop in x2) { if (hasOwn(x2, prop)) { rv[prop] = innerDeepClone(x2[prop]); } } } return rv; } var toString = {}.toString; function toStringTag(o) { return toString.call(o).slice(8, -1); } var iteratorSymbol = typeof Symbol !== "undefined" ? Symbol.iterator : "@@iterator"; var getIteratorOf = typeof iteratorSymbol === "symbol" ? function(x2) { var i3; return x2 != null && (i3 = x2[iteratorSymbol]) && i3.apply(x2); } : function() { return null; }; function delArrayItem(a, x2) { var i3 = a.indexOf(x2); if (i3 >= 0) a.splice(i3, 1); return i3 >= 0; } var NO_CHAR_ARRAY = {}; function getArrayOf(arrayLike) { var i3, a, x2, it2; if (arguments.length === 1) { if (isArray(arrayLike)) return arrayLike.slice(); if (this === NO_CHAR_ARRAY && typeof arrayLike === "string") return [arrayLike]; if (it2 = getIteratorOf(arrayLike)) { a = []; while (x2 = it2.next(), !x2.done) a.push(x2.value); return a; } if (arrayLike == null) return [arrayLike]; i3 = arrayLike.length; if (typeof i3 === "number") { a = new Array(i3); while (i3--) a[i3] = arrayLike[i3]; return a; } return [arrayLike]; } i3 = arguments.length; a = new Array(i3); while (i3--) a[i3] = arguments[i3]; return a; } var isAsyncFunction = typeof Symbol !== "undefined" ? function(fn) { return fn[Symbol.toStringTag] === "AsyncFunction"; } : function() { return false; }; var dexieErrorNames = [ "Modify", "Bulk", "OpenFailed", "VersionChange", "Schema", "Upgrade", "InvalidTable", "MissingAPI", "NoSuchDatabase", "InvalidArgument", "SubTransaction", "Unsupported", "Internal", "DatabaseClosed", "PrematureCommit", "ForeignAwait" ]; var idbDomErrorNames = [ "Unknown", "Constraint", "Data", "TransactionInactive", "ReadOnly", "Version", "NotFound", "InvalidState", "InvalidAccess", "Abort", "Timeout", "QuotaExceeded", "Syntax", "DataClone" ]; var errorList = dexieErrorNames.concat(idbDomErrorNames); var defaultTexts = { VersionChanged: "Database version changed by other database connection", DatabaseClosed: "Database has been closed", Abort: "Transaction aborted", TransactionInactive: "Transaction has already completed or failed", MissingAPI: "IndexedDB API missing. Please visit https://tinyurl.com/y2uuvskb" }; function DexieError(name, msg) { this.name = name; this.message = msg; } derive(DexieError).from(Error).extend({ toString: function() { return this.name + ": " + this.message; } }); function getMultiErrorMessage(msg, failures) { return msg + ". Errors: " + Object.keys(failures).map(function(key) { return failures[key].toString(); }).filter(function(v6, i3, s) { return s.indexOf(v6) === i3; }).join("\n"); } function ModifyError(msg, failures, successCount, failedKeys) { this.failures = failures; this.failedKeys = failedKeys; this.successCount = successCount; this.message = getMultiErrorMessage(msg, failures); } derive(ModifyError).from(DexieError); function BulkError(msg, failures) { this.name = "BulkError"; this.failures = Object.keys(failures).map(function(pos) { return failures[pos]; }); this.failuresByPos = failures; this.message = getMultiErrorMessage(msg, this.failures); } derive(BulkError).from(DexieError); var errnames = errorList.reduce(function(obj, name) { return obj[name] = name + "Error", obj; }, {}); var BaseException = DexieError; var exceptions = errorList.reduce(function(obj, name) { var fullName = name + "Error"; function DexieError2(msgOrInner, inner) { this.name = fullName; if (!msgOrInner) { this.message = defaultTexts[name] || fullName; this.inner = null; } else if (typeof msgOrInner === "string") { this.message = "".concat(msgOrInner).concat(!inner ? "" : "\n " + inner); this.inner = inner || null; } else if (typeof msgOrInner === "object") { this.message = "".concat(msgOrInner.name, " ").concat(msgOrInner.message); this.inner = msgOrInner; } } derive(DexieError2).from(BaseException); obj[name] = DexieError2; return obj; }, {}); exceptions.Syntax = SyntaxError; exceptions.Type = TypeError; exceptions.Range = RangeError; var exceptionMap = idbDomErrorNames.reduce(function(obj, name) { obj[name + "Error"] = exceptions[name]; return obj; }, {}); function mapError(domError, message) { if (!domError || domError instanceof DexieError || domError instanceof TypeError || domError instanceof SyntaxError || !domError.name || !exceptionMap[domError.name]) return domError; var rv = new exceptionMap[domError.name](message || domError.message, domError); if ("stack" in domError) { setProp(rv, "stack", { get: function() { return this.inner.stack; } }); } return rv; } var fullNameExceptions = errorList.reduce(function(obj, name) { if (["Syntax", "Type", "Range"].indexOf(name) === -1) obj[name + "Error"] = exceptions[name]; return obj; }, {}); fullNameExceptions.ModifyError = ModifyError; fullNameExceptions.DexieError = DexieError; fullNameExceptions.BulkError = BulkError; function nop() { } function mirror(val) { return val; } function pureFunctionChain(f1, f2) { if (f1 == null || f1 === mirror) return f2; return function(val) { return f2(f1(val)); }; } function callBoth(on1, on2) { return function() { on1.apply(this, arguments); on2.apply(this, arguments); }; } function hookCreatingChain(f1, f2) { if (f1 === nop) return f2; return function() { var res = f1.apply(this, arguments); if (res !== void 0) arguments[0] = res; var onsuccess = this.onsuccess, onerror = this.onerror; this.onsuccess = null; this.onerror = null; var res2 = f2.apply(this, arguments); if (onsuccess) this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; if (onerror) this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; return res2 !== void 0 ? res2 : res; }; } function hookDeletingChain(f1, f2) { if (f1 === nop) return f2; return function() { f1.apply(this, arguments); var onsuccess = this.onsuccess, onerror = this.onerror; this.onsuccess = this.onerror = null; f2.apply(this, arguments); if (onsuccess) this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; if (onerror) this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; }; } function hookUpdatingChain(f1, f2) { if (f1 === nop) return f2; return function(modifications) { var res = f1.apply(this, arguments); extend(modifications, res); var onsuccess = this.onsuccess, onerror = this.onerror; this.onsuccess = null; this.onerror = null; var res2 = f2.apply(this, arguments); if (onsuccess) this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; if (onerror) this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; return res === void 0 ? res2 === void 0 ? void 0 : res2 : extend(res, res2); }; } function reverseStoppableEventChain(f1, f2) { if (f1 === nop) return f2; return function() { if (f2.apply(this, arguments) === false) return false; return f1.apply(this, arguments); }; } function promisableChain(f1, f2) { if (f1 === nop) return f2; return function() { var res = f1.apply(this, arguments); if (res && typeof res.then === "function") { var thiz = this, i3 = arguments.length, args = new Array(i3); while (i3--) args[i3] = arguments[i3]; return res.then(function() { return f2.apply(thiz, args); }); } return f2.apply(this, arguments); }; } var debug15 = typeof location !== "undefined" && /^(http|https):\/\/(localhost|127\.0\.0\.1)/.test(location.href); function setDebug(value, filter) { debug15 = value; } var INTERNAL = {}; var ZONE_ECHO_LIMIT = 100, _a$1 = typeof Promise === "undefined" ? [] : (function() { var globalP = Promise.resolve(); if (typeof crypto === "undefined" || !crypto.subtle) return [globalP, getProto(globalP), globalP]; var nativeP = crypto.subtle.digest("SHA-512", new Uint8Array([0])); return [ nativeP, getProto(nativeP), globalP ]; })(), resolvedNativePromise = _a$1[0], nativePromiseProto = _a$1[1], resolvedGlobalPromise = _a$1[2], nativePromiseThen = nativePromiseProto && nativePromiseProto.then; var NativePromise = resolvedNativePromise && resolvedNativePromise.constructor; var patchGlobalPromise = !!resolvedGlobalPromise; function schedulePhysicalTick() { queueMicrotask(physicalTick); } var asap = function(callback, args) { microtickQueue.push([callback, args]); if (needsNewPhysicalTick) { schedulePhysicalTick(); needsNewPhysicalTick = false; } }; var isOutsideMicroTick = true, needsNewPhysicalTick = true, unhandledErrors = [], rejectingErrors = [], rejectionMapper = mirror; var globalPSD = { id: "global", global: true, ref: 0, unhandleds: [], onunhandled: nop, pgp: false, env: {}, finalize: nop }; var PSD = globalPSD; var microtickQueue = []; var numScheduledCalls = 0; var tickFinalizers = []; function DexiePromise(fn) { if (typeof this !== "object") throw new TypeError("Promises must be constructed via new"); this._listeners = []; this._lib = false; var psd = this._PSD = PSD; if (typeof fn !== "function") { if (fn !== INTERNAL) throw new TypeError("Not a function"); this._state = arguments[1]; this._value = arguments[2]; if (this._state === false) handleRejection(this, this._value); return; } this._state = null; this._value = null; ++psd.ref; executePromiseTask(this, fn); } var thenProp = { get: function() { var psd = PSD, microTaskId = totalEchoes; function then(onFulfilled, onRejected) { var _this = this; var possibleAwait = !psd.global && (psd !== PSD || microTaskId !== totalEchoes); var cleanup = possibleAwait && !decrementExpectedAwaits(); var rv = new DexiePromise(function(resolve, reject) { propagateToListener(_this, new Listener(nativeAwaitCompatibleWrap(onFulfilled, psd, possibleAwait, cleanup), nativeAwaitCompatibleWrap(onRejected, psd, possibleAwait, cleanup), resolve, reject, psd)); }); if (this._consoleTask) rv._consoleTask = this._consoleTask; return rv; } then.prototype = INTERNAL; return then; }, set: function(value) { setProp(this, "then", value && value.prototype === INTERNAL ? thenProp : { get: function() { return value; }, set: thenProp.set }); } }; props(DexiePromise.prototype, { then: thenProp, _then: function(onFulfilled, onRejected) { propagateToListener(this, new Listener(null, null, onFulfilled, onRejected, PSD)); }, catch: function(onRejected) { if (arguments.length === 1) return this.then(null, onRejected); var type2 = arguments[0], handler = arguments[1]; return typeof type2 === "function" ? this.then(null, function(err) { return err instanceof type2 ? handler(err) : PromiseReject(err); }) : this.then(null, function(err) { return err && err.name === type2 ? handler(err) : PromiseReject(err); }); }, finally: function(onFinally) { return this.then(function(value) { return DexiePromise.resolve(onFinally()).then(function() { return value; }); }, function(err) { return DexiePromise.resolve(onFinally()).then(function() { return PromiseReject(err); }); }); }, timeout: function(ms, msg) { var _this = this; return ms < Infinity ? new DexiePromise(function(resolve, reject) { var handle = setTimeout(function() { return reject(new exceptions.Timeout(msg)); }, ms); _this.then(resolve, reject).finally(clearTimeout.bind(null, handle)); }) : this; } }); if (typeof Symbol !== "undefined" && Symbol.toStringTag) setProp(DexiePromise.prototype, Symbol.toStringTag, "Dexie.Promise"); globalPSD.env = snapShot(); function Listener(onFulfilled, onRejected, resolve, reject, zone) { this.onFulfilled = typeof onFulfilled === "function" ? onFulfilled : null; this.onRejected = typeof onRejected === "function" ? onRejected : null; this.resolve = resolve; this.reject = reject; this.psd = zone; } props(DexiePromise, { all: function() { var values = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); return new DexiePromise(function(resolve, reject) { if (values.length === 0) resolve([]); var remaining = values.length; values.forEach(function(a, i3) { return DexiePromise.resolve(a).then(function(x2) { values[i3] = x2; if (!--remaining) resolve(values); }, reject); }); }); }, resolve: function(value) { if (value instanceof DexiePromise) return value; if (value && typeof value.then === "function") return new DexiePromise(function(resolve, reject) { value.then(resolve, reject); }); var rv = new DexiePromise(INTERNAL, true, value); return rv; }, reject: PromiseReject, race: function() { var values = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); return new DexiePromise(function(resolve, reject) { values.map(function(value) { return DexiePromise.resolve(value).then(resolve, reject); }); }); }, PSD: { get: function() { return PSD; }, set: function(value) { return PSD = value; } }, totalEchoes: { get: function() { return totalEchoes; } }, newPSD: newScope, usePSD, scheduler: { get: function() { return asap; }, set: function(value) { asap = value; } }, rejectionMapper: { get: function() { return rejectionMapper; }, set: function(value) { rejectionMapper = value; } }, follow: function(fn, zoneProps) { return new DexiePromise(function(resolve, reject) { return newScope(function(resolve2, reject2) { var psd = PSD; psd.unhandleds = []; psd.onunhandled = reject2; psd.finalize = callBoth(function() { var _this = this; run_at_end_of_this_or_next_physical_tick(function() { _this.unhandleds.length === 0 ? resolve2() : reject2(_this.unhandleds[0]); }); }, psd.finalize); fn(); }, zoneProps, resolve, reject); }); } }); if (NativePromise) { if (NativePromise.allSettled) setProp(DexiePromise, "allSettled", function() { var possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); return new DexiePromise(function(resolve) { if (possiblePromises.length === 0) resolve([]); var remaining = possiblePromises.length; var results = new Array(remaining); possiblePromises.forEach(function(p5, i3) { return DexiePromise.resolve(p5).then(function(value) { return results[i3] = { status: "fulfilled", value }; }, function(reason) { return results[i3] = { status: "rejected", reason }; }).then(function() { return --remaining || resolve(results); }); }); }); }); if (NativePromise.any && typeof AggregateError !== "undefined") setProp(DexiePromise, "any", function() { var possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); return new DexiePromise(function(resolve, reject) { if (possiblePromises.length === 0) reject(new AggregateError([])); var remaining = possiblePromises.length; var failures = new Array(remaining); possiblePromises.forEach(function(p5, i3) { return DexiePromise.resolve(p5).then(function(value) { return resolve(value); }, function(failure) { failures[i3] = failure; if (!--remaining) reject(new AggregateError(failures)); }); }); }); }); if (NativePromise.withResolvers) DexiePromise.withResolvers = NativePromise.withResolvers; } function executePromiseTask(promise, fn) { try { fn(function(value) { if (promise._state !== null) return; if (value === promise) throw new TypeError("A promise cannot be resolved with itself."); var shouldExecuteTick = promise._lib && beginMicroTickScope(); if (value && typeof value.then === "function") { executePromiseTask(promise, function(resolve, reject) { value instanceof DexiePromise ? value._then(resolve, reject) : value.then(resolve, reject); }); } else { promise._state = true; promise._value = value; propagateAllListeners(promise); } if (shouldExecuteTick) endMicroTickScope(); }, handleRejection.bind(null, promise)); } catch (ex) { handleRejection(promise, ex); } } function handleRejection(promise, reason) { rejectingErrors.push(reason); if (promise._state !== null) return; var shouldExecuteTick = promise._lib && beginMicroTickScope(); reason = rejectionMapper(reason); promise._state = false; promise._value = reason; addPossiblyUnhandledError(promise); propagateAllListeners(promise); if (shouldExecuteTick) endMicroTickScope(); } function propagateAllListeners(promise) { var listeners = promise._listeners; promise._listeners = []; for (var i3 = 0, len = listeners.length; i3 < len; ++i3) { propagateToListener(promise, listeners[i3]); } var psd = promise._PSD; --psd.ref || psd.finalize(); if (numScheduledCalls === 0) { ++numScheduledCalls; asap(function() { if (--numScheduledCalls === 0) finalizePhysicalTick(); }, []); } } function propagateToListener(promise, listener) { if (promise._state === null) { promise._listeners.push(listener); return; } var cb = promise._state ? listener.onFulfilled : listener.onRejected; if (cb === null) { return (promise._state ? listener.resolve : listener.reject)(promise._value); } ++listener.psd.ref; ++numScheduledCalls; asap(callListener, [cb, promise, listener]); } function callListener(cb, promise, listener) { try { var ret, value = promise._value; if (!promise._state && rejectingErrors.length) rejectingErrors = []; ret = debug15 && promise._consoleTask ? promise._consoleTask.run(function() { return cb(value); }) : cb(value); if (!promise._state && rejectingErrors.indexOf(value) === -1) { markErrorAsHandled(promise); } listener.resolve(ret); } catch (e2) { listener.reject(e2); } finally { if (--numScheduledCalls === 0) finalizePhysicalTick(); --listener.psd.ref || listener.psd.finalize(); } } function physicalTick() { usePSD(globalPSD, function() { beginMicroTickScope() && endMicroTickScope(); }); } function beginMicroTickScope() { var wasRootExec = isOutsideMicroTick; isOutsideMicroTick = false; needsNewPhysicalTick = false; return wasRootExec; } function endMicroTickScope() { var callbacks, i3, l3; do { while (microtickQueue.length > 0) { callbacks = microtickQueue; microtickQueue = []; l3 = callbacks.length; for (i3 = 0; i3 < l3; ++i3) { var item = callbacks[i3]; item[0].apply(null, item[1]); } } } while (microtickQueue.length > 0); isOutsideMicroTick = true; needsNewPhysicalTick = true; } function finalizePhysicalTick() { var unhandledErrs = unhandledErrors; unhandledErrors = []; unhandledErrs.forEach(function(p5) { p5._PSD.onunhandled.call(null, p5._value, p5); }); var finalizers = tickFinalizers.slice(0); var i3 = finalizers.length; while (i3) finalizers[--i3](); } function run_at_end_of_this_or_next_physical_tick(fn) { function finalizer() { fn(); tickFinalizers.splice(tickFinalizers.indexOf(finalizer), 1); } tickFinalizers.push(finalizer); ++numScheduledCalls; asap(function() { if (--numScheduledCalls === 0) finalizePhysicalTick(); }, []); } function addPossiblyUnhandledError(promise) { if (!unhandledErrors.some(function(p5) { return p5._value === promise._value; })) unhandledErrors.push(promise); } function markErrorAsHandled(promise) { var i3 = unhandledErrors.length; while (i3) if (unhandledErrors[--i3]._value === promise._value) { unhandledErrors.splice(i3, 1); return; } } function PromiseReject(reason) { return new DexiePromise(INTERNAL, false, reason); } function wrap(fn, errorCatcher) { var psd = PSD; return function() { var wasRootExec = beginMicroTickScope(), outerScope = PSD; try { switchToZone(psd, true); return fn.apply(this, arguments); } catch (e2) { errorCatcher && errorCatcher(e2); } finally { switchToZone(outerScope, false); if (wasRootExec) endMicroTickScope(); } }; } var task = { awaits: 0, echoes: 0, id: 0 }; var taskCounter = 0; var zoneStack = []; var zoneEchoes = 0; var totalEchoes = 0; var zone_id_counter = 0; function newScope(fn, props2, a1, a2) { var parent = PSD, psd = Object.create(parent); psd.parent = parent; psd.ref = 0; psd.global = false; psd.id = ++zone_id_counter; globalPSD.env; psd.env = patchGlobalPromise ? { Promise: DexiePromise, PromiseProp: { value: DexiePromise, configurable: true, writable: true }, all: DexiePromise.all, race: DexiePromise.race, allSettled: DexiePromise.allSettled, any: DexiePromise.any, resolve: DexiePromise.resolve, reject: DexiePromise.reject } : {}; if (props2) extend(psd, props2); ++parent.ref; psd.finalize = function() { --this.parent.ref || this.parent.finalize(); }; var rv = usePSD(psd, fn, a1, a2); if (psd.ref === 0) psd.finalize(); return rv; } function incrementExpectedAwaits() { if (!task.id) task.id = ++taskCounter; ++task.awaits; task.echoes += ZONE_ECHO_LIMIT; return task.id; } function decrementExpectedAwaits() { if (!task.awaits) return false; if (--task.awaits === 0) task.id = 0; task.echoes = task.awaits * ZONE_ECHO_LIMIT; return true; } if (("" + nativePromiseThen).indexOf("[native code]") === -1) { incrementExpectedAwaits = decrementExpectedAwaits = nop; } function onPossibleParallellAsync(possiblePromise) { if (task.echoes && possiblePromise && possiblePromise.constructor === NativePromise) { incrementExpectedAwaits(); return possiblePromise.then(function(x2) { decrementExpectedAwaits(); return x2; }, function(e2) { decrementExpectedAwaits(); return rejection(e2); }); } return possiblePromise; } function zoneEnterEcho(targetZone) { ++totalEchoes; if (!task.echoes || --task.echoes === 0) { task.echoes = task.awaits = task.id = 0; } zoneStack.push(PSD); switchToZone(targetZone, true); } function zoneLeaveEcho() { var zone = zoneStack[zoneStack.length - 1]; zoneStack.pop(); switchToZone(zone, false); } function switchToZone(targetZone, bEnteringZone) { var currentZone = PSD; if (bEnteringZone ? task.echoes && (!zoneEchoes++ || targetZone !== PSD) : zoneEchoes && (!--zoneEchoes || targetZone !== PSD)) { queueMicrotask(bEnteringZone ? zoneEnterEcho.bind(null, targetZone) : zoneLeaveEcho); } if (targetZone === PSD) return; PSD = targetZone; if (currentZone === globalPSD) globalPSD.env = snapShot(); if (patchGlobalPromise) { var GlobalPromise = globalPSD.env.Promise; var targetEnv = targetZone.env; if (currentZone.global || targetZone.global) { Object.defineProperty(_global, "Promise", targetEnv.PromiseProp); GlobalPromise.all = targetEnv.all; GlobalPromise.race = targetEnv.race; GlobalPromise.resolve = targetEnv.resolve; GlobalPromise.reject = targetEnv.reject; if (targetEnv.allSettled) GlobalPromise.allSettled = targetEnv.allSettled; if (targetEnv.any) GlobalPromise.any = targetEnv.any; } } } function snapShot() { var GlobalPromise = _global.Promise; return patchGlobalPromise ? { Promise: GlobalPromise, PromiseProp: Object.getOwnPropertyDescriptor(_global, "Promise"), all: GlobalPromise.all, race: GlobalPromise.race, allSettled: GlobalPromise.allSettled, any: GlobalPromise.any, resolve: GlobalPromise.resolve, reject: GlobalPromise.reject } : {}; } function usePSD(psd, fn, a1, a2, a3) { var outerScope = PSD; try { switchToZone(psd, true); return fn(a1, a2, a3); } finally { switchToZone(outerScope, false); } } function nativeAwaitCompatibleWrap(fn, zone, possibleAwait, cleanup) { return typeof fn !== "function" ? fn : function() { var outerZone = PSD; if (possibleAwait) incrementExpectedAwaits(); switchToZone(zone, true); try { return fn.apply(this, arguments); } finally { switchToZone(outerZone, false); if (cleanup) queueMicrotask(decrementExpectedAwaits); } }; } function execInGlobalContext(cb) { if (Promise === NativePromise && task.echoes === 0) { if (zoneEchoes === 0) { cb(); } else { enqueueNativeMicroTask(cb); } } else { setTimeout(cb, 0); } } var rejection = DexiePromise.reject; function tempTransaction(db3, mode, storeNames, fn) { if (!db3.idbdb || !db3._state.openComplete && (!PSD.letThrough && !db3._vip)) { if (db3._state.openComplete) { return rejection(new exceptions.DatabaseClosed(db3._state.dbOpenError)); } if (!db3._state.isBeingOpened) { if (!db3._state.autoOpen) return rejection(new exceptions.DatabaseClosed()); db3.open().catch(nop); } return db3._state.dbReadyPromise.then(function() { return tempTransaction(db3, mode, storeNames, fn); }); } else { var trans = db3._createTransaction(mode, storeNames, db3._dbSchema); try { trans.create(); db3._state.PR1398_maxLoop = 3; } catch (ex) { if (ex.name === errnames.InvalidState && db3.isOpen() && --db3._state.PR1398_maxLoop > 0) { console.warn("Dexie: Need to reopen db"); db3.close({ disableAutoOpen: false }); return db3.open().then(function() { return tempTransaction(db3, mode, storeNames, fn); }); } return rejection(ex); } return trans._promise(mode, function(resolve, reject) { return newScope(function() { PSD.trans = trans; return fn(resolve, reject, trans); }); }).then(function(result) { if (mode === "readwrite") try { trans.idbtrans.commit(); } catch (_a73) { } return mode === "readonly" ? result : trans._completion.then(function() { return result; }); }); } } var DEXIE_VERSION = "4.2.1"; var maxString = String.fromCharCode(65535); var minKey = -Infinity; var INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array."; var STRING_EXPECTED = "String expected."; var connections = []; var DBNAMES_DB = "__dbnames"; var READONLY = "readonly"; var READWRITE = "readwrite"; function combine(filter1, filter2) { return filter1 ? filter2 ? function() { return filter1.apply(this, arguments) && filter2.apply(this, arguments); } : filter1 : filter2; } var AnyRange = { type: 3, lower: -Infinity, lowerOpen: false, upper: [[]], upperOpen: false }; function workaroundForUndefinedPrimKey(keyPath) { return typeof keyPath === "string" && !/\./.test(keyPath) ? function(obj) { if (obj[keyPath] === void 0 && keyPath in obj) { obj = deepClone(obj); delete obj[keyPath]; } return obj; } : function(obj) { return obj; }; } function Entity2() { throw exceptions.Type("Entity instances must never be new:ed. Instances are generated by the framework bypassing the constructor."); } function cmp2(a, b) { try { var ta = type(a); var tb = type(b); if (ta !== tb) { if (ta === "Array") return 1; if (tb === "Array") return -1; if (ta === "binary") return 1; if (tb === "binary") return -1; if (ta === "string") return 1; if (tb === "string") return -1; if (ta === "Date") return 1; if (tb !== "Date") return NaN; return -1; } switch (ta) { case "number": case "Date": case "string": return a > b ? 1 : a < b ? -1 : 0; case "binary": { return compareUint8Arrays(getUint8Array(a), getUint8Array(b)); } case "Array": return compareArrays(a, b); } } catch (_a73) { } return NaN; } function compareArrays(a, b) { var al = a.length; var bl = b.length; var l3 = al < bl ? al : bl; for (var i3 = 0; i3 < l3; ++i3) { var res = cmp2(a[i3], b[i3]); if (res !== 0) return res; } return al === bl ? 0 : al < bl ? -1 : 1; } function compareUint8Arrays(a, b) { var al = a.length; var bl = b.length; var l3 = al < bl ? al : bl; for (var i3 = 0; i3 < l3; ++i3) { if (a[i3] !== b[i3]) return a[i3] < b[i3] ? -1 : 1; } return al === bl ? 0 : al < bl ? -1 : 1; } function type(x2) { var t = typeof x2; if (t !== "object") return t; if (ArrayBuffer.isView(x2)) return "binary"; var tsTag = toStringTag(x2); return tsTag === "ArrayBuffer" ? "binary" : tsTag; } function getUint8Array(a) { if (a instanceof Uint8Array) return a; if (ArrayBuffer.isView(a)) return new Uint8Array(a.buffer, a.byteOffset, a.byteLength); return new Uint8Array(a); } function builtInDeletionTrigger(table, keys2, res) { var yProps = table.schema.yProps; if (!yProps) return res; if (keys2 && res.numFailures > 0) keys2 = keys2.filter(function(_2, i3) { return !res.failures[i3]; }); return Promise.all(yProps.map(function(_a73) { var updatesTable = _a73.updatesTable; return keys2 ? table.db.table(updatesTable).where("k").anyOf(keys2).delete() : table.db.table(updatesTable).clear(); })).then(function() { return res; }); } var PropModification2 = (function() { function PropModification3(spec) { this["@@propmod"] = spec; } PropModification3.prototype.execute = function(value) { var _a73; var spec = this["@@propmod"]; if (spec.add !== void 0) { var term = spec.add; if (isArray(term)) { return __spreadArray(__spreadArray([], isArray(value) ? value : [], true), term, true).sort(); } if (typeof term === "number") return (Number(value) || 0) + term; if (typeof term === "bigint") { try { return BigInt(value) + term; } catch (_b) { return BigInt(0) + term; } } throw new TypeError("Invalid term ".concat(term)); } if (spec.remove !== void 0) { var subtrahend_1 = spec.remove; if (isArray(subtrahend_1)) { return isArray(value) ? value.filter(function(item) { return !subtrahend_1.includes(item); }).sort() : []; } if (typeof subtrahend_1 === "number") return Number(value) - subtrahend_1; if (typeof subtrahend_1 === "bigint") { try { return BigInt(value) - subtrahend_1; } catch (_c) { return BigInt(0) - subtrahend_1; } } throw new TypeError("Invalid subtrahend ".concat(subtrahend_1)); } var prefixToReplace = (_a73 = spec.replacePrefix) === null || _a73 === void 0 ? void 0 : _a73[0]; if (prefixToReplace && typeof value === "string" && value.startsWith(prefixToReplace)) { return spec.replacePrefix[1] + value.substring(prefixToReplace.length); } return value; }; return PropModification3; })(); function applyUpdateSpec(obj, changes) { var keyPaths = keys(changes); var numKeys = keyPaths.length; var anythingModified = false; for (var i3 = 0; i3 < numKeys; ++i3) { var keyPath = keyPaths[i3]; var value = changes[keyPath]; var origValue = getByKeyPath(obj, keyPath); if (value instanceof PropModification2) { setByKeyPath(obj, keyPath, value.execute(origValue)); anythingModified = true; } else if (origValue !== value) { setByKeyPath(obj, keyPath, value); anythingModified = true; } } return anythingModified; } var Table = (function() { function Table2() { } Table2.prototype._trans = function(mode, fn, writeLocked) { var trans = this._tx || PSD.trans; var tableName = this.name; var task2 = debug15 && typeof console !== "undefined" && console.createTask && console.createTask("Dexie: ".concat(mode === "readonly" ? "read" : "write", " ").concat(this.name)); function checkTableInTransaction(resolve, reject, trans2) { if (!trans2.schema[tableName]) throw new exceptions.NotFound("Table " + tableName + " not part of transaction"); return fn(trans2.idbtrans, trans2); } var wasRootExec = beginMicroTickScope(); try { var p5 = trans && trans.db._novip === this.db._novip ? trans === PSD.trans ? trans._promise(mode, checkTableInTransaction, writeLocked) : newScope(function() { return trans._promise(mode, checkTableInTransaction, writeLocked); }, { trans, transless: PSD.transless || PSD }) : tempTransaction(this.db, mode, [this.name], checkTableInTransaction); if (task2) { p5._consoleTask = task2; p5 = p5.catch(function(err) { console.trace(err); return rejection(err); }); } return p5; } finally { if (wasRootExec) endMicroTickScope(); } }; Table2.prototype.get = function(keyOrCrit, cb) { var _this = this; if (keyOrCrit && keyOrCrit.constructor === Object) return this.where(keyOrCrit).first(cb); if (keyOrCrit == null) return rejection(new exceptions.Type("Invalid argument to Table.get()")); return this._trans("readonly", function(trans) { return _this.core.get({ trans, key: keyOrCrit }).then(function(res) { return _this.hook.reading.fire(res); }); }).then(cb); }; Table2.prototype.where = function(indexOrCrit) { if (typeof indexOrCrit === "string") return new this.db.WhereClause(this, indexOrCrit); if (isArray(indexOrCrit)) return new this.db.WhereClause(this, "[".concat(indexOrCrit.join("+"), "]")); var keyPaths = keys(indexOrCrit); if (keyPaths.length === 1) return this.where(keyPaths[0]).equals(indexOrCrit[keyPaths[0]]); var compoundIndex = this.schema.indexes.concat(this.schema.primKey).filter(function(ix) { if (ix.compound && keyPaths.every(function(keyPath) { return ix.keyPath.indexOf(keyPath) >= 0; })) { for (var i3 = 0; i3 < keyPaths.length; ++i3) { if (keyPaths.indexOf(ix.keyPath[i3]) === -1) return false; } return true; } return false; }).sort(function(a, b) { return a.keyPath.length - b.keyPath.length; })[0]; if (compoundIndex && this.db._maxKey !== maxString) { var keyPathsInValidOrder = compoundIndex.keyPath.slice(0, keyPaths.length); return this.where(keyPathsInValidOrder).equals(keyPathsInValidOrder.map(function(kp) { return indexOrCrit[kp]; })); } if (!compoundIndex && debug15) console.warn("The query ".concat(JSON.stringify(indexOrCrit), " on ").concat(this.name, " would benefit from a ") + "compound index [".concat(keyPaths.join("+"), "]")); var idxByName = this.schema.idxByName; function equals(a, b) { return cmp2(a, b) === 0; } var _a73 = keyPaths.reduce(function(_a74, keyPath) { var prevIndex = _a74[0], prevFilterFn = _a74[1]; var index = idxByName[keyPath]; var value = indexOrCrit[keyPath]; return [ prevIndex || index, prevIndex || !index ? combine(prevFilterFn, index && index.multi ? function(x2) { var prop = getByKeyPath(x2, keyPath); return isArray(prop) && prop.some(function(item) { return equals(value, item); }); } : function(x2) { return equals(value, getByKeyPath(x2, keyPath)); }) : prevFilterFn ]; }, [null, null]), idx = _a73[0], filterFunction = _a73[1]; return idx ? this.where(idx.name).equals(indexOrCrit[idx.keyPath]).filter(filterFunction) : compoundIndex ? this.filter(filterFunction) : this.where(keyPaths).equals(""); }; Table2.prototype.filter = function(filterFunction) { return this.toCollection().and(filterFunction); }; Table2.prototype.count = function(thenShortcut) { return this.toCollection().count(thenShortcut); }; Table2.prototype.offset = function(offset) { return this.toCollection().offset(offset); }; Table2.prototype.limit = function(numRows) { return this.toCollection().limit(numRows); }; Table2.prototype.each = function(callback) { return this.toCollection().each(callback); }; Table2.prototype.toArray = function(thenShortcut) { return this.toCollection().toArray(thenShortcut); }; Table2.prototype.toCollection = function() { return new this.db.Collection(new this.db.WhereClause(this)); }; Table2.prototype.orderBy = function(index) { return new this.db.Collection(new this.db.WhereClause(this, isArray(index) ? "[".concat(index.join("+"), "]") : index)); }; Table2.prototype.reverse = function() { return this.toCollection().reverse(); }; Table2.prototype.mapToClass = function(constructor) { var _a73 = this, db3 = _a73.db, tableName = _a73.name; this.schema.mappedClass = constructor; if (constructor.prototype instanceof Entity2) { constructor = (function(_super) { __extends(class_1, _super); function class_1() { return _super !== null && _super.apply(this, arguments) || this; } Object.defineProperty(class_1.prototype, "db", { get: function() { return db3; }, enumerable: false, configurable: true }); class_1.prototype.table = function() { return tableName; }; return class_1; })(constructor); } var inheritedProps = /* @__PURE__ */ new Set(); for (var proto = constructor.prototype; proto; proto = getProto(proto)) { Object.getOwnPropertyNames(proto).forEach(function(propName) { return inheritedProps.add(propName); }); } var readHook = function(obj) { if (!obj) return obj; var res = Object.create(constructor.prototype); for (var m in obj) if (!inheritedProps.has(m)) try { res[m] = obj[m]; } catch (_2) { } return res; }; if (this.schema.readHook) { this.hook.reading.unsubscribe(this.schema.readHook); } this.schema.readHook = readHook; this.hook("reading", readHook); return constructor; }; Table2.prototype.defineClass = function() { function Class(content) { extend(this, content); } return this.mapToClass(Class); }; Table2.prototype.add = function(obj, key) { var _this = this; var _a73 = this.schema.primKey, auto = _a73.auto, keyPath = _a73.keyPath; var objToAdd = obj; if (keyPath && auto) { objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj); } return this._trans("readwrite", function(trans) { return _this.core.mutate({ trans, type: "add", keys: key != null ? [key] : null, values: [objToAdd] }); }).then(function(res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult; }).then(function(lastResult) { if (keyPath) { try { setByKeyPath(obj, keyPath, lastResult); } catch (_2) { } } return lastResult; }); }; Table2.prototype.upsert = function(key, modifications) { var _this = this; var keyPath = this.schema.primKey.keyPath; return this._trans("readwrite", function(trans) { return _this.core.get({ trans, key }).then(function(existing) { var obj = existing !== null && existing !== void 0 ? existing : {}; applyUpdateSpec(obj, modifications); if (keyPath) setByKeyPath(obj, keyPath, key); return _this.core.mutate({ trans, type: "put", values: [obj], keys: [key], upsert: true, updates: { keys: [key], changeSpecs: [modifications] } }).then(function(res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : !!existing; }); }); }); }; Table2.prototype.update = function(keyOrObject, modifications) { if (typeof keyOrObject === "object" && !isArray(keyOrObject)) { var key = getByKeyPath(keyOrObject, this.schema.primKey.keyPath); if (key === void 0) return rejection(new exceptions.InvalidArgument("Given object does not contain its primary key")); return this.where(":id").equals(key).modify(modifications); } else { return this.where(":id").equals(keyOrObject).modify(modifications); } }; Table2.prototype.put = function(obj, key) { var _this = this; var _a73 = this.schema.primKey, auto = _a73.auto, keyPath = _a73.keyPath; var objToAdd = obj; if (keyPath && auto) { objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj); } return this._trans("readwrite", function(trans) { return _this.core.mutate({ trans, type: "put", values: [objToAdd], keys: key != null ? [key] : null }); }).then(function(res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult; }).then(function(lastResult) { if (keyPath) { try { setByKeyPath(obj, keyPath, lastResult); } catch (_2) { } } return lastResult; }); }; Table2.prototype.delete = function(key) { var _this = this; return this._trans("readwrite", function(trans) { return _this.core.mutate({ trans, type: "delete", keys: [key] }).then(function(res) { return builtInDeletionTrigger(_this, [key], res); }).then(function(res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : void 0; }); }); }; Table2.prototype.clear = function() { var _this = this; return this._trans("readwrite", function(trans) { return _this.core.mutate({ trans, type: "deleteRange", range: AnyRange }).then(function(res) { return builtInDeletionTrigger(_this, null, res); }); }).then(function(res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : void 0; }); }; Table2.prototype.bulkGet = function(keys2) { var _this = this; return this._trans("readonly", function(trans) { return _this.core.getMany({ keys: keys2, trans }).then(function(result) { return result.map(function(res) { return _this.hook.reading.fire(res); }); }); }); }; Table2.prototype.bulkAdd = function(objects, keysOrOptions, options) { var _this = this; var keys2 = Array.isArray(keysOrOptions) ? keysOrOptions : void 0; options = options || (keys2 ? void 0 : keysOrOptions); var wantResults = options ? options.allKeys : void 0; return this._trans("readwrite", function(trans) { var _a73 = _this.schema.primKey, auto = _a73.auto, keyPath = _a73.keyPath; if (keyPath && keys2) throw new exceptions.InvalidArgument("bulkAdd(): keys argument invalid on tables with inbound keys"); if (keys2 && keys2.length !== objects.length) throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length"); var numObjects = objects.length; var objectsToAdd = keyPath && auto ? objects.map(workaroundForUndefinedPrimKey(keyPath)) : objects; return _this.core.mutate({ trans, type: "add", keys: keys2, values: objectsToAdd, wantResults }).then(function(_a74) { var numFailures = _a74.numFailures, results = _a74.results, lastResult = _a74.lastResult, failures = _a74.failures; var result = wantResults ? results : lastResult; if (numFailures === 0) return result; throw new BulkError("".concat(_this.name, ".bulkAdd(): ").concat(numFailures, " of ").concat(numObjects, " operations failed"), failures); }); }); }; Table2.prototype.bulkPut = function(objects, keysOrOptions, options) { var _this = this; var keys2 = Array.isArray(keysOrOptions) ? keysOrOptions : void 0; options = options || (keys2 ? void 0 : keysOrOptions); var wantResults = options ? options.allKeys : void 0; return this._trans("readwrite", function(trans) { var _a73 = _this.schema.primKey, auto = _a73.auto, keyPath = _a73.keyPath; if (keyPath && keys2) throw new exceptions.InvalidArgument("bulkPut(): keys argument invalid on tables with inbound keys"); if (keys2 && keys2.length !== objects.length) throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length"); var numObjects = objects.length; var objectsToPut = keyPath && auto ? objects.map(workaroundForUndefinedPrimKey(keyPath)) : objects; return _this.core.mutate({ trans, type: "put", keys: keys2, values: objectsToPut, wantResults }).then(function(_a74) { var numFailures = _a74.numFailures, results = _a74.results, lastResult = _a74.lastResult, failures = _a74.failures; var result = wantResults ? results : lastResult; if (numFailures === 0) return result; throw new BulkError("".concat(_this.name, ".bulkPut(): ").concat(numFailures, " of ").concat(numObjects, " operations failed"), failures); }); }); }; Table2.prototype.bulkUpdate = function(keysAndChanges) { var _this = this; var coreTable = this.core; var keys2 = keysAndChanges.map(function(entry) { return entry.key; }); var changeSpecs = keysAndChanges.map(function(entry) { return entry.changes; }); var offsetMap = []; return this._trans("readwrite", function(trans) { return coreTable.getMany({ trans, keys: keys2, cache: "clone" }).then(function(objs) { var resultKeys = []; var resultObjs = []; keysAndChanges.forEach(function(_a73, idx) { var key = _a73.key, changes = _a73.changes; var obj = objs[idx]; if (obj) { for (var _i = 0, _b = Object.keys(changes); _i < _b.length; _i++) { var keyPath = _b[_i]; var value = changes[keyPath]; if (keyPath === _this.schema.primKey.keyPath) { if (cmp2(value, key) !== 0) { throw new exceptions.Constraint("Cannot update primary key in bulkUpdate()"); } } else { setByKeyPath(obj, keyPath, value); } } offsetMap.push(idx); resultKeys.push(key); resultObjs.push(obj); } }); var numEntries = resultKeys.length; return coreTable.mutate({ trans, type: "put", keys: resultKeys, values: resultObjs, updates: { keys: keys2, changeSpecs } }).then(function(_a73) { var numFailures = _a73.numFailures, failures = _a73.failures; if (numFailures === 0) return numEntries; for (var _i = 0, _b = Object.keys(failures); _i < _b.length; _i++) { var offset = _b[_i]; var mappedOffset = offsetMap[Number(offset)]; if (mappedOffset != null) { var failure = failures[offset]; delete failures[offset]; failures[mappedOffset] = failure; } } throw new BulkError("".concat(_this.name, ".bulkUpdate(): ").concat(numFailures, " of ").concat(numEntries, " operations failed"), failures); }); }); }); }; Table2.prototype.bulkDelete = function(keys2) { var _this = this; var numKeys = keys2.length; return this._trans("readwrite", function(trans) { return _this.core.mutate({ trans, type: "delete", keys: keys2 }).then(function(res) { return builtInDeletionTrigger(_this, keys2, res); }); }).then(function(_a73) { var numFailures = _a73.numFailures, lastResult = _a73.lastResult, failures = _a73.failures; if (numFailures === 0) return lastResult; throw new BulkError("".concat(_this.name, ".bulkDelete(): ").concat(numFailures, " of ").concat(numKeys, " operations failed"), failures); }); }; return Table2; })(); function Events(ctx) { var evs = {}; var rv = function(eventName, subscriber) { if (subscriber) { var i4 = arguments.length, args = new Array(i4 - 1); while (--i4) args[i4 - 1] = arguments[i4]; evs[eventName].subscribe.apply(null, args); return ctx; } else if (typeof eventName === "string") { return evs[eventName]; } }; rv.addEventType = add4; for (var i3 = 1, l3 = arguments.length; i3 < l3; ++i3) { add4(arguments[i3]); } return rv; function add4(eventName, chainFunction, defaultFunction) { if (typeof eventName === "object") return addConfiguredEvents(eventName); if (!chainFunction) chainFunction = reverseStoppableEventChain; if (!defaultFunction) defaultFunction = nop; var context = { subscribers: [], fire: defaultFunction, subscribe: function(cb) { if (context.subscribers.indexOf(cb) === -1) { context.subscribers.push(cb); context.fire = chainFunction(context.fire, cb); } }, unsubscribe: function(cb) { context.subscribers = context.subscribers.filter(function(fn) { return fn !== cb; }); context.fire = context.subscribers.reduce(chainFunction, defaultFunction); } }; evs[eventName] = rv[eventName] = context; return context; } function addConfiguredEvents(cfg) { keys(cfg).forEach(function(eventName) { var args = cfg[eventName]; if (isArray(args)) { add4(eventName, cfg[eventName][0], cfg[eventName][1]); } else if (args === "asap") { var context = add4(eventName, mirror, function fire() { var i4 = arguments.length, args2 = new Array(i4); while (i4--) args2[i4] = arguments[i4]; context.subscribers.forEach(function(fn) { asap$1(function fireEvent() { fn.apply(null, args2); }); }); }); } else throw new exceptions.InvalidArgument("Invalid event config"); }); } } function makeClassConstructor(prototype, constructor) { derive(constructor).from({ prototype }); return constructor; } function createTableConstructor(db3) { return makeClassConstructor(Table.prototype, function Table2(name, tableSchema, trans) { this.db = db3; this._tx = trans; this.name = name; this.schema = tableSchema; this.hook = db3._allTables[name] ? db3._allTables[name].hook : Events(null, { "creating": [hookCreatingChain, nop], "reading": [pureFunctionChain, mirror], "updating": [hookUpdatingChain, nop], "deleting": [hookDeletingChain, nop] }); }); } function isPlainKeyRange(ctx, ignoreLimitFilter) { return !(ctx.filter || ctx.algorithm || ctx.or) && (ignoreLimitFilter ? ctx.justLimit : !ctx.replayFilter); } function addFilter(ctx, fn) { ctx.filter = combine(ctx.filter, fn); } function addReplayFilter(ctx, factory, isLimitFilter) { var curr = ctx.replayFilter; ctx.replayFilter = curr ? function() { return combine(curr(), factory()); } : factory; ctx.justLimit = isLimitFilter && !curr; } function addMatchFilter(ctx, fn) { ctx.isMatch = combine(ctx.isMatch, fn); } function getIndexOrStore(ctx, coreSchema) { if (ctx.isPrimKey) return coreSchema.primaryKey; var index = coreSchema.getIndexByKeyPath(ctx.index); if (!index) throw new exceptions.Schema("KeyPath " + ctx.index + " on object store " + coreSchema.name + " is not indexed"); return index; } function openCursor(ctx, coreTable, trans) { var index = getIndexOrStore(ctx, coreTable.schema); return coreTable.openCursor({ trans, values: !ctx.keysOnly, reverse: ctx.dir === "prev", unique: !!ctx.unique, query: { index, range: ctx.range } }); } function iter(ctx, fn, coreTrans, coreTable) { var filter = ctx.replayFilter ? combine(ctx.filter, ctx.replayFilter()) : ctx.filter; if (!ctx.or) { return iterate(openCursor(ctx, coreTable, coreTrans), combine(ctx.algorithm, filter), fn, !ctx.keysOnly && ctx.valueMapper); } else { var set_1 = {}; var union = function(item, cursor, advance) { if (!filter || filter(cursor, advance, function(result) { return cursor.stop(result); }, function(err) { return cursor.fail(err); })) { var primaryKey = cursor.primaryKey; var key = "" + primaryKey; if (key === "[object ArrayBuffer]") key = "" + new Uint8Array(primaryKey); if (!hasOwn(set_1, key)) { set_1[key] = true; fn(item, cursor, advance); } } }; return Promise.all([ ctx.or._iterate(union, coreTrans), iterate(openCursor(ctx, coreTable, coreTrans), ctx.algorithm, union, !ctx.keysOnly && ctx.valueMapper) ]); } } function iterate(cursorPromise, filter, fn, valueMapper) { var mappedFn = valueMapper ? function(x2, c, a) { return fn(valueMapper(x2), c, a); } : fn; var wrappedFn = wrap(mappedFn); return cursorPromise.then(function(cursor) { if (cursor) { return cursor.start(function() { var c = function() { return cursor.continue(); }; if (!filter || filter(cursor, function(advancer) { return c = advancer; }, function(val) { cursor.stop(val); c = nop; }, function(e2) { cursor.fail(e2); c = nop; })) wrappedFn(cursor.value, cursor, function(advancer) { return c = advancer; }); c(); }); } }); } var Collection = (function() { function Collection2() { } Collection2.prototype._read = function(fn, cb) { var ctx = this._ctx; return ctx.error ? ctx.table._trans(null, rejection.bind(null, ctx.error)) : ctx.table._trans("readonly", fn).then(cb); }; Collection2.prototype._write = function(fn) { var ctx = this._ctx; return ctx.error ? ctx.table._trans(null, rejection.bind(null, ctx.error)) : ctx.table._trans("readwrite", fn, "locked"); }; Collection2.prototype._addAlgorithm = function(fn) { var ctx = this._ctx; ctx.algorithm = combine(ctx.algorithm, fn); }; Collection2.prototype._iterate = function(fn, coreTrans) { return iter(this._ctx, fn, coreTrans, this._ctx.table.core); }; Collection2.prototype.clone = function(props2) { var rv = Object.create(this.constructor.prototype), ctx = Object.create(this._ctx); if (props2) extend(ctx, props2); rv._ctx = ctx; return rv; }; Collection2.prototype.raw = function() { this._ctx.valueMapper = null; return this; }; Collection2.prototype.each = function(fn) { var ctx = this._ctx; return this._read(function(trans) { return iter(ctx, fn, trans, ctx.table.core); }); }; Collection2.prototype.count = function(cb) { var _this = this; return this._read(function(trans) { var ctx = _this._ctx; var coreTable = ctx.table.core; if (isPlainKeyRange(ctx, true)) { return coreTable.count({ trans, query: { index: getIndexOrStore(ctx, coreTable.schema), range: ctx.range } }).then(function(count2) { return Math.min(count2, ctx.limit); }); } else { var count = 0; return iter(ctx, function() { ++count; return false; }, trans, coreTable).then(function() { return count; }); } }).then(cb); }; Collection2.prototype.sortBy = function(keyPath, cb) { var parts = keyPath.split(".").reverse(), lastPart = parts[0], lastIndex = parts.length - 1; function getval(obj, i3) { if (i3) return getval(obj[parts[i3]], i3 - 1); return obj[lastPart]; } var order = this._ctx.dir === "next" ? 1 : -1; function sorter(a, b) { var aVal = getval(a, lastIndex), bVal = getval(b, lastIndex); return cmp2(aVal, bVal) * order; } return this.toArray(function(a) { return a.sort(sorter); }).then(cb); }; Collection2.prototype.toArray = function(cb) { var _this = this; return this._read(function(trans) { var ctx = _this._ctx; if (ctx.dir === "next" && isPlainKeyRange(ctx, true) && ctx.limit > 0) { var valueMapper_1 = ctx.valueMapper; var index = getIndexOrStore(ctx, ctx.table.core.schema); return ctx.table.core.query({ trans, limit: ctx.limit, values: true, query: { index, range: ctx.range } }).then(function(_a73) { var result = _a73.result; return valueMapper_1 ? result.map(valueMapper_1) : result; }); } else { var a_1 = []; return iter(ctx, function(item) { return a_1.push(item); }, trans, ctx.table.core).then(function() { return a_1; }); } }, cb); }; Collection2.prototype.offset = function(offset) { var ctx = this._ctx; if (offset <= 0) return this; ctx.offset += offset; if (isPlainKeyRange(ctx)) { addReplayFilter(ctx, function() { var offsetLeft = offset; return function(cursor, advance) { if (offsetLeft === 0) return true; if (offsetLeft === 1) { --offsetLeft; return false; } advance(function() { cursor.advance(offsetLeft); offsetLeft = 0; }); return false; }; }); } else { addReplayFilter(ctx, function() { var offsetLeft = offset; return function() { return --offsetLeft < 0; }; }); } return this; }; Collection2.prototype.limit = function(numRows) { this._ctx.limit = Math.min(this._ctx.limit, numRows); addReplayFilter(this._ctx, function() { var rowsLeft = numRows; return function(cursor, advance, resolve) { if (--rowsLeft <= 0) advance(resolve); return rowsLeft >= 0; }; }, true); return this; }; Collection2.prototype.until = function(filterFunction, bIncludeStopEntry) { addFilter(this._ctx, function(cursor, advance, resolve) { if (filterFunction(cursor.value)) { advance(resolve); return bIncludeStopEntry; } else { return true; } }); return this; }; Collection2.prototype.first = function(cb) { return this.limit(1).toArray(function(a) { return a[0]; }).then(cb); }; Collection2.prototype.last = function(cb) { return this.reverse().first(cb); }; Collection2.prototype.filter = function(filterFunction) { addFilter(this._ctx, function(cursor) { return filterFunction(cursor.value); }); addMatchFilter(this._ctx, filterFunction); return this; }; Collection2.prototype.and = function(filter) { return this.filter(filter); }; Collection2.prototype.or = function(indexName) { return new this.db.WhereClause(this._ctx.table, indexName, this); }; Collection2.prototype.reverse = function() { this._ctx.dir = this._ctx.dir === "prev" ? "next" : "prev"; if (this._ondirectionchange) this._ondirectionchange(this._ctx.dir); return this; }; Collection2.prototype.desc = function() { return this.reverse(); }; Collection2.prototype.eachKey = function(cb) { var ctx = this._ctx; ctx.keysOnly = !ctx.isMatch; return this.each(function(val, cursor) { cb(cursor.key, cursor); }); }; Collection2.prototype.eachUniqueKey = function(cb) { this._ctx.unique = "unique"; return this.eachKey(cb); }; Collection2.prototype.eachPrimaryKey = function(cb) { var ctx = this._ctx; ctx.keysOnly = !ctx.isMatch; return this.each(function(val, cursor) { cb(cursor.primaryKey, cursor); }); }; Collection2.prototype.keys = function(cb) { var ctx = this._ctx; ctx.keysOnly = !ctx.isMatch; var a = []; return this.each(function(item, cursor) { a.push(cursor.key); }).then(function() { return a; }).then(cb); }; Collection2.prototype.primaryKeys = function(cb) { var ctx = this._ctx; if (ctx.dir === "next" && isPlainKeyRange(ctx, true) && ctx.limit > 0) { return this._read(function(trans) { var index = getIndexOrStore(ctx, ctx.table.core.schema); return ctx.table.core.query({ trans, values: false, limit: ctx.limit, query: { index, range: ctx.range } }); }).then(function(_a73) { var result = _a73.result; return result; }).then(cb); } ctx.keysOnly = !ctx.isMatch; var a = []; return this.each(function(item, cursor) { a.push(cursor.primaryKey); }).then(function() { return a; }).then(cb); }; Collection2.prototype.uniqueKeys = function(cb) { this._ctx.unique = "unique"; return this.keys(cb); }; Collection2.prototype.firstKey = function(cb) { return this.limit(1).keys(function(a) { return a[0]; }).then(cb); }; Collection2.prototype.lastKey = function(cb) { return this.reverse().firstKey(cb); }; Collection2.prototype.distinct = function() { var ctx = this._ctx, idx = ctx.index && ctx.table.schema.idxByName[ctx.index]; if (!idx || !idx.multi) return this; var set = {}; addFilter(this._ctx, function(cursor) { var strKey = cursor.primaryKey.toString(); var found = hasOwn(set, strKey); set[strKey] = true; return !found; }); return this; }; Collection2.prototype.modify = function(changes) { var _this = this; var ctx = this._ctx; return this._write(function(trans) { var modifyer; if (typeof changes === "function") { modifyer = changes; } else { modifyer = function(item) { return applyUpdateSpec(item, changes); }; } var coreTable = ctx.table.core; var _a73 = coreTable.schema.primaryKey, outbound = _a73.outbound, extractKey = _a73.extractKey; var limit2 = 200; var modifyChunkSize = _this.db._options.modifyChunkSize; if (modifyChunkSize) { if (typeof modifyChunkSize == "object") { limit2 = modifyChunkSize[coreTable.name] || modifyChunkSize["*"] || 200; } else { limit2 = modifyChunkSize; } } var totalFailures = []; var successCount = 0; var failedKeys = []; var applyMutateResult = function(expectedCount, res) { var failures = res.failures, numFailures = res.numFailures; successCount += expectedCount - numFailures; for (var _i = 0, _a74 = keys(failures); _i < _a74.length; _i++) { var pos = _a74[_i]; totalFailures.push(failures[pos]); } }; var isUnconditionalDelete = changes === deleteCallback; return _this.clone().primaryKeys().then(function(keys2) { var criteria = isPlainKeyRange(ctx) && ctx.limit === Infinity && (typeof changes !== "function" || isUnconditionalDelete) && { index: ctx.index, range: ctx.range }; var nextChunk = function(offset) { var count = Math.min(limit2, keys2.length - offset); var keysInChunk = keys2.slice(offset, offset + count); return (isUnconditionalDelete ? Promise.resolve([]) : coreTable.getMany({ trans, keys: keysInChunk, cache: "immutable" })).then(function(values) { var addValues = []; var putValues = []; var putKeys = outbound ? [] : null; var deleteKeys = isUnconditionalDelete ? keysInChunk : []; if (!isUnconditionalDelete) for (var i3 = 0; i3 < count; ++i3) { var origValue = values[i3]; var ctx_1 = { value: deepClone(origValue), primKey: keys2[offset + i3] }; if (modifyer.call(ctx_1, ctx_1.value, ctx_1) !== false) { if (ctx_1.value == null) { deleteKeys.push(keys2[offset + i3]); } else if (!outbound && cmp2(extractKey(origValue), extractKey(ctx_1.value)) !== 0) { deleteKeys.push(keys2[offset + i3]); addValues.push(ctx_1.value); } else { putValues.push(ctx_1.value); if (outbound) putKeys.push(keys2[offset + i3]); } } } return Promise.resolve(addValues.length > 0 && coreTable.mutate({ trans, type: "add", values: addValues }).then(function(res) { for (var pos in res.failures) { deleteKeys.splice(parseInt(pos), 1); } applyMutateResult(addValues.length, res); })).then(function() { return (putValues.length > 0 || criteria && typeof changes === "object") && coreTable.mutate({ trans, type: "put", keys: putKeys, values: putValues, criteria, changeSpec: typeof changes !== "function" && changes, isAdditionalChunk: offset > 0 }).then(function(res) { return applyMutateResult(putValues.length, res); }); }).then(function() { return (deleteKeys.length > 0 || criteria && isUnconditionalDelete) && coreTable.mutate({ trans, type: "delete", keys: deleteKeys, criteria, isAdditionalChunk: offset > 0 }).then(function(res) { return builtInDeletionTrigger(ctx.table, deleteKeys, res); }).then(function(res) { return applyMutateResult(deleteKeys.length, res); }); }).then(function() { return keys2.length > offset + count && nextChunk(offset + limit2); }); }); }; return nextChunk(0).then(function() { if (totalFailures.length > 0) throw new ModifyError("Error modifying one or more objects", totalFailures, successCount, failedKeys); return keys2.length; }); }); }); }; Collection2.prototype.delete = function() { var ctx = this._ctx, range = ctx.range; if (isPlainKeyRange(ctx) && !ctx.table.schema.yProps && (ctx.isPrimKey || range.type === 3)) { return this._write(function(trans) { var primaryKey = ctx.table.core.schema.primaryKey; var coreRange = range; return ctx.table.core.count({ trans, query: { index: primaryKey, range: coreRange } }).then(function(count) { return ctx.table.core.mutate({ trans, type: "deleteRange", range: coreRange }).then(function(_a73) { var failures = _a73.failures, numFailures = _a73.numFailures; if (numFailures) throw new ModifyError("Could not delete some values", Object.keys(failures).map(function(pos) { return failures[pos]; }), count - numFailures); return count - numFailures; }); }); }); } return this.modify(deleteCallback); }; return Collection2; })(); var deleteCallback = function(value, ctx) { return ctx.value = null; }; function createCollectionConstructor(db3) { return makeClassConstructor(Collection.prototype, function Collection2(whereClause, keyRangeGenerator) { this.db = db3; var keyRange = AnyRange, error = null; if (keyRangeGenerator) try { keyRange = keyRangeGenerator(); } catch (ex) { error = ex; } var whereCtx = whereClause._ctx; var table = whereCtx.table; var readingHook = table.hook.reading.fire; this._ctx = { table, index: whereCtx.index, isPrimKey: !whereCtx.index || table.schema.primKey.keyPath && whereCtx.index === table.schema.primKey.name, range: keyRange, keysOnly: false, dir: "next", unique: "", algorithm: null, filter: null, replayFilter: null, justLimit: true, isMatch: null, offset: 0, limit: Infinity, error, or: whereCtx.or, valueMapper: readingHook !== mirror ? readingHook : null }; }); } function simpleCompare(a, b) { return a < b ? -1 : a === b ? 0 : 1; } function simpleCompareReverse(a, b) { return a > b ? -1 : a === b ? 0 : 1; } function fail(collectionOrWhereClause, err, T4) { var collection2 = collectionOrWhereClause instanceof WhereClause ? new collectionOrWhereClause.Collection(collectionOrWhereClause) : collectionOrWhereClause; collection2._ctx.error = T4 ? new T4(err) : new TypeError(err); return collection2; } function emptyCollection(whereClause) { return new whereClause.Collection(whereClause, function() { return rangeEqual(""); }).limit(0); } function upperFactory(dir) { return dir === "next" ? function(s) { return s.toUpperCase(); } : function(s) { return s.toLowerCase(); }; } function lowerFactory(dir) { return dir === "next" ? function(s) { return s.toLowerCase(); } : function(s) { return s.toUpperCase(); }; } function nextCasing(key, lowerKey, upperNeedle, lowerNeedle, cmp3, dir) { var length = Math.min(key.length, lowerNeedle.length); var llp = -1; for (var i3 = 0; i3 < length; ++i3) { var lwrKeyChar = lowerKey[i3]; if (lwrKeyChar !== lowerNeedle[i3]) { if (cmp3(key[i3], upperNeedle[i3]) < 0) return key.substr(0, i3) + upperNeedle[i3] + upperNeedle.substr(i3 + 1); if (cmp3(key[i3], lowerNeedle[i3]) < 0) return key.substr(0, i3) + lowerNeedle[i3] + upperNeedle.substr(i3 + 1); if (llp >= 0) return key.substr(0, llp) + lowerKey[llp] + upperNeedle.substr(llp + 1); return null; } if (cmp3(key[i3], lwrKeyChar) < 0) llp = i3; } if (length < lowerNeedle.length && dir === "next") return key + upperNeedle.substr(key.length); if (length < key.length && dir === "prev") return key.substr(0, upperNeedle.length); return llp < 0 ? null : key.substr(0, llp) + lowerNeedle[llp] + upperNeedle.substr(llp + 1); } function addIgnoreCaseAlgorithm(whereClause, match, needles, suffix) { var upper, lower, compare, upperNeedles, lowerNeedles, direction, nextKeySuffix, needlesLen = needles.length; if (!needles.every(function(s) { return typeof s === "string"; })) { return fail(whereClause, STRING_EXPECTED); } function initDirection(dir) { upper = upperFactory(dir); lower = lowerFactory(dir); compare = dir === "next" ? simpleCompare : simpleCompareReverse; var needleBounds = needles.map(function(needle) { return { lower: lower(needle), upper: upper(needle) }; }).sort(function(a, b) { return compare(a.lower, b.lower); }); upperNeedles = needleBounds.map(function(nb) { return nb.upper; }); lowerNeedles = needleBounds.map(function(nb) { return nb.lower; }); direction = dir; nextKeySuffix = dir === "next" ? "" : suffix; } initDirection("next"); var c = new whereClause.Collection(whereClause, function() { return createRange(upperNeedles[0], lowerNeedles[needlesLen - 1] + suffix); }); c._ondirectionchange = function(direction2) { initDirection(direction2); }; var firstPossibleNeedle = 0; c._addAlgorithm(function(cursor, advance, resolve) { var key = cursor.key; if (typeof key !== "string") return false; var lowerKey = lower(key); if (match(lowerKey, lowerNeedles, firstPossibleNeedle)) { return true; } else { var lowestPossibleCasing = null; for (var i3 = firstPossibleNeedle; i3 < needlesLen; ++i3) { var casing = nextCasing(key, lowerKey, upperNeedles[i3], lowerNeedles[i3], compare, direction); if (casing === null && lowestPossibleCasing === null) firstPossibleNeedle = i3 + 1; else if (lowestPossibleCasing === null || compare(lowestPossibleCasing, casing) > 0) { lowestPossibleCasing = casing; } } if (lowestPossibleCasing !== null) { advance(function() { cursor.continue(lowestPossibleCasing + nextKeySuffix); }); } else { advance(resolve); } return false; } }); return c; } function createRange(lower, upper, lowerOpen, upperOpen) { return { type: 2, lower, upper, lowerOpen, upperOpen }; } function rangeEqual(value) { return { type: 1, lower: value, upper: value }; } var WhereClause = (function() { function WhereClause2() { } Object.defineProperty(WhereClause2.prototype, "Collection", { get: function() { return this._ctx.table.db.Collection; }, enumerable: false, configurable: true }); WhereClause2.prototype.between = function(lower, upper, includeLower, includeUpper) { includeLower = includeLower !== false; includeUpper = includeUpper === true; try { if (this._cmp(lower, upper) > 0 || this._cmp(lower, upper) === 0 && (includeLower || includeUpper) && !(includeLower && includeUpper)) return emptyCollection(this); return new this.Collection(this, function() { return createRange(lower, upper, !includeLower, !includeUpper); }); } catch (e2) { return fail(this, INVALID_KEY_ARGUMENT); } }; WhereClause2.prototype.equals = function(value) { if (value == null) return fail(this, INVALID_KEY_ARGUMENT); return new this.Collection(this, function() { return rangeEqual(value); }); }; WhereClause2.prototype.above = function(value) { if (value == null) return fail(this, INVALID_KEY_ARGUMENT); return new this.Collection(this, function() { return createRange(value, void 0, true); }); }; WhereClause2.prototype.aboveOrEqual = function(value) { if (value == null) return fail(this, INVALID_KEY_ARGUMENT); return new this.Collection(this, function() { return createRange(value, void 0, false); }); }; WhereClause2.prototype.below = function(value) { if (value == null) return fail(this, INVALID_KEY_ARGUMENT); return new this.Collection(this, function() { return createRange(void 0, value, false, true); }); }; WhereClause2.prototype.belowOrEqual = function(value) { if (value == null) return fail(this, INVALID_KEY_ARGUMENT); return new this.Collection(this, function() { return createRange(void 0, value); }); }; WhereClause2.prototype.startsWith = function(str) { if (typeof str !== "string") return fail(this, STRING_EXPECTED); return this.between(str, str + maxString, true, true); }; WhereClause2.prototype.startsWithIgnoreCase = function(str) { if (str === "") return this.startsWith(str); return addIgnoreCaseAlgorithm(this, function(x2, a) { return x2.indexOf(a[0]) === 0; }, [str], maxString); }; WhereClause2.prototype.equalsIgnoreCase = function(str) { return addIgnoreCaseAlgorithm(this, function(x2, a) { return x2 === a[0]; }, [str], ""); }; WhereClause2.prototype.anyOfIgnoreCase = function() { var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); if (set.length === 0) return emptyCollection(this); return addIgnoreCaseAlgorithm(this, function(x2, a) { return a.indexOf(x2) !== -1; }, set, ""); }; WhereClause2.prototype.startsWithAnyOfIgnoreCase = function() { var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); if (set.length === 0) return emptyCollection(this); return addIgnoreCaseAlgorithm(this, function(x2, a) { return a.some(function(n) { return x2.indexOf(n) === 0; }); }, set, maxString); }; WhereClause2.prototype.anyOf = function() { var _this = this; var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); var compare = this._cmp; try { set.sort(compare); } catch (e2) { return fail(this, INVALID_KEY_ARGUMENT); } if (set.length === 0) return emptyCollection(this); var c = new this.Collection(this, function() { return createRange(set[0], set[set.length - 1]); }); c._ondirectionchange = function(direction) { compare = direction === "next" ? _this._ascending : _this._descending; set.sort(compare); }; var i3 = 0; c._addAlgorithm(function(cursor, advance, resolve) { var key = cursor.key; while (compare(key, set[i3]) > 0) { ++i3; if (i3 === set.length) { advance(resolve); return false; } } if (compare(key, set[i3]) === 0) { return true; } else { advance(function() { cursor.continue(set[i3]); }); return false; } }); return c; }; WhereClause2.prototype.notEqual = function(value) { return this.inAnyRange([[minKey, value], [value, this.db._maxKey]], { includeLowers: false, includeUppers: false }); }; WhereClause2.prototype.noneOf = function() { var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); if (set.length === 0) return new this.Collection(this); try { set.sort(this._ascending); } catch (e2) { return fail(this, INVALID_KEY_ARGUMENT); } var ranges = set.reduce(function(res, val) { return res ? res.concat([[res[res.length - 1][1], val]]) : [[minKey, val]]; }, null); ranges.push([set[set.length - 1], this.db._maxKey]); return this.inAnyRange(ranges, { includeLowers: false, includeUppers: false }); }; WhereClause2.prototype.inAnyRange = function(ranges, options) { var _this = this; var cmp3 = this._cmp, ascending = this._ascending, descending = this._descending, min = this._min, max = this._max; if (ranges.length === 0) return emptyCollection(this); if (!ranges.every(function(range) { return range[0] !== void 0 && range[1] !== void 0 && ascending(range[0], range[1]) <= 0; })) { return fail(this, "First argument to inAnyRange() must be an Array of two-value Arrays [lower,upper] where upper must not be lower than lower", exceptions.InvalidArgument); } var includeLowers = !options || options.includeLowers !== false; var includeUppers = options && options.includeUppers === true; function addRange2(ranges2, newRange) { var i3 = 0, l3 = ranges2.length; for (; i3 < l3; ++i3) { var range = ranges2[i3]; if (cmp3(newRange[0], range[1]) < 0 && cmp3(newRange[1], range[0]) > 0) { range[0] = min(range[0], newRange[0]); range[1] = max(range[1], newRange[1]); break; } } if (i3 === l3) ranges2.push(newRange); return ranges2; } var sortDirection = ascending; function rangeSorter(a, b) { return sortDirection(a[0], b[0]); } var set; try { set = ranges.reduce(addRange2, []); set.sort(rangeSorter); } catch (ex) { return fail(this, INVALID_KEY_ARGUMENT); } var rangePos = 0; var keyIsBeyondCurrentEntry = includeUppers ? function(key) { return ascending(key, set[rangePos][1]) > 0; } : function(key) { return ascending(key, set[rangePos][1]) >= 0; }; var keyIsBeforeCurrentEntry = includeLowers ? function(key) { return descending(key, set[rangePos][0]) > 0; } : function(key) { return descending(key, set[rangePos][0]) >= 0; }; function keyWithinCurrentRange(key) { return !keyIsBeyondCurrentEntry(key) && !keyIsBeforeCurrentEntry(key); } var checkKey = keyIsBeyondCurrentEntry; var c = new this.Collection(this, function() { return createRange(set[0][0], set[set.length - 1][1], !includeLowers, !includeUppers); }); c._ondirectionchange = function(direction) { if (direction === "next") { checkKey = keyIsBeyondCurrentEntry; sortDirection = ascending; } else { checkKey = keyIsBeforeCurrentEntry; sortDirection = descending; } set.sort(rangeSorter); }; c._addAlgorithm(function(cursor, advance, resolve) { var key = cursor.key; while (checkKey(key)) { ++rangePos; if (rangePos === set.length) { advance(resolve); return false; } } if (keyWithinCurrentRange(key)) { return true; } else if (_this._cmp(key, set[rangePos][1]) === 0 || _this._cmp(key, set[rangePos][0]) === 0) { return false; } else { advance(function() { if (sortDirection === ascending) cursor.continue(set[rangePos][0]); else cursor.continue(set[rangePos][1]); }); return false; } }); return c; }; WhereClause2.prototype.startsWithAnyOf = function() { var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); if (!set.every(function(s) { return typeof s === "string"; })) { return fail(this, "startsWithAnyOf() only works with strings"); } if (set.length === 0) return emptyCollection(this); return this.inAnyRange(set.map(function(str) { return [str, str + maxString]; })); }; return WhereClause2; })(); function createWhereClauseConstructor(db3) { return makeClassConstructor(WhereClause.prototype, function WhereClause2(table, index, orCollection) { this.db = db3; this._ctx = { table, index: index === ":id" ? null : index, or: orCollection }; this._cmp = this._ascending = cmp2; this._descending = function(a, b) { return cmp2(b, a); }; this._max = function(a, b) { return cmp2(a, b) > 0 ? a : b; }; this._min = function(a, b) { return cmp2(a, b) < 0 ? a : b; }; this._IDBKeyRange = db3._deps.IDBKeyRange; if (!this._IDBKeyRange) throw new exceptions.MissingAPI(); }); } function eventRejectHandler(reject) { return wrap(function(event) { preventDefault(event); reject(event.target.error); return false; }); } function preventDefault(event) { if (event.stopPropagation) event.stopPropagation(); if (event.preventDefault) event.preventDefault(); } var DEXIE_STORAGE_MUTATED_EVENT_NAME = "storagemutated"; var STORAGE_MUTATED_DOM_EVENT_NAME = "x-storagemutated-1"; var globalEvents = Events(null, DEXIE_STORAGE_MUTATED_EVENT_NAME); var Transaction = (function() { function Transaction2() { } Transaction2.prototype._lock = function() { assert2(!PSD.global); ++this._reculock; if (this._reculock === 1 && !PSD.global) PSD.lockOwnerFor = this; return this; }; Transaction2.prototype._unlock = function() { assert2(!PSD.global); if (--this._reculock === 0) { if (!PSD.global) PSD.lockOwnerFor = null; while (this._blockedFuncs.length > 0 && !this._locked()) { var fnAndPSD = this._blockedFuncs.shift(); try { usePSD(fnAndPSD[1], fnAndPSD[0]); } catch (e2) { } } } return this; }; Transaction2.prototype._locked = function() { return this._reculock && PSD.lockOwnerFor !== this; }; Transaction2.prototype.create = function(idbtrans) { var _this = this; if (!this.mode) return this; var idbdb = this.db.idbdb; var dbOpenError = this.db._state.dbOpenError; assert2(!this.idbtrans); if (!idbtrans && !idbdb) { switch (dbOpenError && dbOpenError.name) { case "DatabaseClosedError": throw new exceptions.DatabaseClosed(dbOpenError); case "MissingAPIError": throw new exceptions.MissingAPI(dbOpenError.message, dbOpenError); default: throw new exceptions.OpenFailed(dbOpenError); } } if (!this.active) throw new exceptions.TransactionInactive(); assert2(this._completion._state === null); idbtrans = this.idbtrans = idbtrans || (this.db.core ? this.db.core.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability }) : idbdb.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability })); idbtrans.onerror = wrap(function(ev) { preventDefault(ev); _this._reject(idbtrans.error); }); idbtrans.onabort = wrap(function(ev) { preventDefault(ev); _this.active && _this._reject(new exceptions.Abort(idbtrans.error)); _this.active = false; _this.on("abort").fire(ev); }); idbtrans.oncomplete = wrap(function() { _this.active = false; _this._resolve(); if ("mutatedParts" in idbtrans) { globalEvents.storagemutated.fire(idbtrans["mutatedParts"]); } }); return this; }; Transaction2.prototype._promise = function(mode, fn, bWriteLock) { var _this = this; if (mode === "readwrite" && this.mode !== "readwrite") return rejection(new exceptions.ReadOnly("Transaction is readonly")); if (!this.active) return rejection(new exceptions.TransactionInactive()); if (this._locked()) { return new DexiePromise(function(resolve, reject) { _this._blockedFuncs.push([function() { _this._promise(mode, fn, bWriteLock).then(resolve, reject); }, PSD]); }); } else if (bWriteLock) { return newScope(function() { var p6 = new DexiePromise(function(resolve, reject) { _this._lock(); var rv = fn(resolve, reject, _this); if (rv && rv.then) rv.then(resolve, reject); }); p6.finally(function() { return _this._unlock(); }); p6._lib = true; return p6; }); } else { var p5 = new DexiePromise(function(resolve, reject) { var rv = fn(resolve, reject, _this); if (rv && rv.then) rv.then(resolve, reject); }); p5._lib = true; return p5; } }; Transaction2.prototype._root = function() { return this.parent ? this.parent._root() : this; }; Transaction2.prototype.waitFor = function(promiseLike) { var root = this._root(); var promise = DexiePromise.resolve(promiseLike); if (root._waitingFor) { root._waitingFor = root._waitingFor.then(function() { return promise; }); } else { root._waitingFor = promise; root._waitingQueue = []; var store = root.idbtrans.objectStore(root.storeNames[0]); (function spin() { ++root._spinCount; while (root._waitingQueue.length) root._waitingQueue.shift()(); if (root._waitingFor) store.get(-Infinity).onsuccess = spin; })(); } var currentWaitPromise = root._waitingFor; return new DexiePromise(function(resolve, reject) { promise.then(function(res) { return root._waitingQueue.push(wrap(resolve.bind(null, res))); }, function(err) { return root._waitingQueue.push(wrap(reject.bind(null, err))); }).finally(function() { if (root._waitingFor === currentWaitPromise) { root._waitingFor = null; } }); }); }; Transaction2.prototype.abort = function() { if (this.active) { this.active = false; if (this.idbtrans) this.idbtrans.abort(); this._reject(new exceptions.Abort()); } }; Transaction2.prototype.table = function(tableName) { var memoizedTables = this._memoizedTables || (this._memoizedTables = {}); if (hasOwn(memoizedTables, tableName)) return memoizedTables[tableName]; var tableSchema = this.schema[tableName]; if (!tableSchema) { throw new exceptions.NotFound("Table " + tableName + " not part of transaction"); } var transactionBoundTable = new this.db.Table(tableName, tableSchema, this); transactionBoundTable.core = this.db.core.table(tableName); memoizedTables[tableName] = transactionBoundTable; return transactionBoundTable; }; return Transaction2; })(); function createTransactionConstructor(db3) { return makeClassConstructor(Transaction.prototype, function Transaction2(mode, storeNames, dbschema, chromeTransactionDurability, parent) { var _this = this; if (mode !== "readonly") storeNames.forEach(function(storeName) { var _a73; var yProps = (_a73 = dbschema[storeName]) === null || _a73 === void 0 ? void 0 : _a73.yProps; if (yProps) storeNames = storeNames.concat(yProps.map(function(p5) { return p5.updatesTable; })); }); this.db = db3; this.mode = mode; this.storeNames = storeNames; this.schema = dbschema; this.chromeTransactionDurability = chromeTransactionDurability; this.idbtrans = null; this.on = Events(this, "complete", "error", "abort"); this.parent = parent || null; this.active = true; this._reculock = 0; this._blockedFuncs = []; this._resolve = null; this._reject = null; this._waitingFor = null; this._waitingQueue = null; this._spinCount = 0; this._completion = new DexiePromise(function(resolve, reject) { _this._resolve = resolve; _this._reject = reject; }); this._completion.then(function() { _this.active = false; _this.on.complete.fire(); }, function(e2) { var wasActive = _this.active; _this.active = false; _this.on.error.fire(e2); _this.parent ? _this.parent._reject(e2) : wasActive && _this.idbtrans && _this.idbtrans.abort(); return rejection(e2); }); }); } function createIndexSpec(name, keyPath, unique, multi, auto, compound, isPrimKey, type2) { return { name, keyPath, unique, multi, auto, compound, src: (unique && !isPrimKey ? "&" : "") + (multi ? "*" : "") + (auto ? "++" : "") + nameFromKeyPath(keyPath), type: type2 }; } function nameFromKeyPath(keyPath) { return typeof keyPath === "string" ? keyPath : keyPath ? "[" + [].join.call(keyPath, "+") + "]" : ""; } function createTableSchema(name, primKey, indexes) { return { name, primKey, indexes, mappedClass: null, idxByName: arrayToObject(indexes, function(index) { return [index.name, index]; }) }; } function safariMultiStoreFix(storeNames) { return storeNames.length === 1 ? storeNames[0] : storeNames; } var getMaxKey = function(IdbKeyRange) { try { IdbKeyRange.only([[]]); getMaxKey = function() { return [[]]; }; return [[]]; } catch (e2) { getMaxKey = function() { return maxString; }; return maxString; } }; function getKeyExtractor(keyPath) { if (keyPath == null) { return function() { return void 0; }; } else if (typeof keyPath === "string") { return getSinglePathKeyExtractor(keyPath); } else { return function(obj) { return getByKeyPath(obj, keyPath); }; } } function getSinglePathKeyExtractor(keyPath) { var split2 = keyPath.split("."); if (split2.length === 1) { return function(obj) { return obj[keyPath]; }; } else { return function(obj) { return getByKeyPath(obj, keyPath); }; } } function arrayify(arrayLike) { return [].slice.call(arrayLike); } var _id_counter = 0; function getKeyPathAlias(keyPath) { return keyPath == null ? ":id" : typeof keyPath === "string" ? keyPath : "[".concat(keyPath.join("+"), "]"); } function createDBCore(db3, IdbKeyRange, tmpTrans) { function extractSchema(db4, trans) { var tables2 = arrayify(db4.objectStoreNames); return { schema: { name: db4.name, tables: tables2.map(function(table) { return trans.objectStore(table); }).map(function(store) { var keyPath = store.keyPath, autoIncrement = store.autoIncrement; var compound = isArray(keyPath); var outbound = keyPath == null; var indexByKeyPath = {}; var result = { name: store.name, primaryKey: { name: null, isPrimaryKey: true, outbound, compound, keyPath, autoIncrement, unique: true, extractKey: getKeyExtractor(keyPath) }, indexes: arrayify(store.indexNames).map(function(indexName) { return store.index(indexName); }).map(function(index) { var name = index.name, unique = index.unique, multiEntry = index.multiEntry, keyPath2 = index.keyPath; var compound2 = isArray(keyPath2); var result2 = { name, compound: compound2, keyPath: keyPath2, unique, multiEntry, extractKey: getKeyExtractor(keyPath2) }; indexByKeyPath[getKeyPathAlias(keyPath2)] = result2; return result2; }), getIndexByKeyPath: function(keyPath2) { return indexByKeyPath[getKeyPathAlias(keyPath2)]; } }; indexByKeyPath[":id"] = result.primaryKey; if (keyPath != null) { indexByKeyPath[getKeyPathAlias(keyPath)] = result.primaryKey; } return result; }) }, hasGetAll: tables2.length > 0 && "getAll" in trans.objectStore(tables2[0]) && !(typeof navigator !== "undefined" && /Safari/.test(navigator.userAgent) && !/(Chrome\/|Edge\/)/.test(navigator.userAgent) && [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604) }; } function makeIDBKeyRange(range) { if (range.type === 3) return null; if (range.type === 4) throw new Error("Cannot convert never type to IDBKeyRange"); var lower = range.lower, upper = range.upper, lowerOpen = range.lowerOpen, upperOpen = range.upperOpen; var idbRange = lower === void 0 ? upper === void 0 ? null : IdbKeyRange.upperBound(upper, !!upperOpen) : upper === void 0 ? IdbKeyRange.lowerBound(lower, !!lowerOpen) : IdbKeyRange.bound(lower, upper, !!lowerOpen, !!upperOpen); return idbRange; } function createDbCoreTable(tableSchema) { var tableName = tableSchema.name; function mutate(_a74) { var trans = _a74.trans, type2 = _a74.type, keys2 = _a74.keys, values = _a74.values, range = _a74.range; return new Promise(function(resolve, reject) { resolve = wrap(resolve); var store = trans.objectStore(tableName); var outbound = store.keyPath == null; var isAddOrPut = type2 === "put" || type2 === "add"; if (!isAddOrPut && type2 !== "delete" && type2 !== "deleteRange") throw new Error("Invalid operation type: " + type2); var length = (keys2 || values || { length: 1 }).length; if (keys2 && values && keys2.length !== values.length) { throw new Error("Given keys array must have same length as given values array."); } if (length === 0) return resolve({ numFailures: 0, failures: {}, results: [], lastResult: void 0 }); var req; var reqs = []; var failures = []; var numFailures = 0; var errorHandler = function(event) { ++numFailures; preventDefault(event); }; if (type2 === "deleteRange") { if (range.type === 4) return resolve({ numFailures, failures, results: [], lastResult: void 0 }); if (range.type === 3) reqs.push(req = store.clear()); else reqs.push(req = store.delete(makeIDBKeyRange(range))); } else { var _a75 = isAddOrPut ? outbound ? [values, keys2] : [values, null] : [keys2, null], args1 = _a75[0], args2 = _a75[1]; if (isAddOrPut) { for (var i3 = 0; i3 < length; ++i3) { reqs.push(req = args2 && args2[i3] !== void 0 ? store[type2](args1[i3], args2[i3]) : store[type2](args1[i3])); req.onerror = errorHandler; } } else { for (var i3 = 0; i3 < length; ++i3) { reqs.push(req = store[type2](args1[i3])); req.onerror = errorHandler; } } } var done = function(event) { var lastResult = event.target.result; reqs.forEach(function(req2, i4) { return req2.error != null && (failures[i4] = req2.error); }); resolve({ numFailures, failures, results: type2 === "delete" ? keys2 : reqs.map(function(req2) { return req2.result; }), lastResult }); }; req.onerror = function(event) { errorHandler(event); done(event); }; req.onsuccess = done; }); } function openCursor2(_a74) { var trans = _a74.trans, values = _a74.values, query4 = _a74.query, reverse = _a74.reverse, unique = _a74.unique; return new Promise(function(resolve, reject) { resolve = wrap(resolve); var index = query4.index, range = query4.range; var store = trans.objectStore(tableName); var source = index.isPrimaryKey ? store : store.index(index.name); var direction = reverse ? unique ? "prevunique" : "prev" : unique ? "nextunique" : "next"; var req = values || !("openKeyCursor" in source) ? source.openCursor(makeIDBKeyRange(range), direction) : source.openKeyCursor(makeIDBKeyRange(range), direction); req.onerror = eventRejectHandler(reject); req.onsuccess = wrap(function(ev) { var cursor = req.result; if (!cursor) { resolve(null); return; } cursor.___id = ++_id_counter; cursor.done = false; var _cursorContinue = cursor.continue.bind(cursor); var _cursorContinuePrimaryKey = cursor.continuePrimaryKey; if (_cursorContinuePrimaryKey) _cursorContinuePrimaryKey = _cursorContinuePrimaryKey.bind(cursor); var _cursorAdvance = cursor.advance.bind(cursor); var doThrowCursorIsNotStarted = function() { throw new Error("Cursor not started"); }; var doThrowCursorIsStopped = function() { throw new Error("Cursor not stopped"); }; cursor.trans = trans; cursor.stop = cursor.continue = cursor.continuePrimaryKey = cursor.advance = doThrowCursorIsNotStarted; cursor.fail = wrap(reject); cursor.next = function() { var _this = this; var gotOne = 1; return this.start(function() { return gotOne-- ? _this.continue() : _this.stop(); }).then(function() { return _this; }); }; cursor.start = function(callback) { var iterationPromise = new Promise(function(resolveIteration, rejectIteration) { resolveIteration = wrap(resolveIteration); req.onerror = eventRejectHandler(rejectIteration); cursor.fail = rejectIteration; cursor.stop = function(value) { cursor.stop = cursor.continue = cursor.continuePrimaryKey = cursor.advance = doThrowCursorIsStopped; resolveIteration(value); }; }); var guardedCallback = function() { if (req.result) { try { callback(); } catch (err) { cursor.fail(err); } } else { cursor.done = true; cursor.start = function() { throw new Error("Cursor behind last entry"); }; cursor.stop(); } }; req.onsuccess = wrap(function(ev2) { req.onsuccess = guardedCallback; guardedCallback(); }); cursor.continue = _cursorContinue; cursor.continuePrimaryKey = _cursorContinuePrimaryKey; cursor.advance = _cursorAdvance; guardedCallback(); return iterationPromise; }; resolve(cursor); }, reject); }); } function query3(hasGetAll2) { return function(request) { return new Promise(function(resolve, reject) { resolve = wrap(resolve); var trans = request.trans, values = request.values, limit2 = request.limit, query4 = request.query; var nonInfinitLimit = limit2 === Infinity ? void 0 : limit2; var index = query4.index, range = query4.range; var store = trans.objectStore(tableName); var source = index.isPrimaryKey ? store : store.index(index.name); var idbKeyRange = makeIDBKeyRange(range); if (limit2 === 0) return resolve({ result: [] }); if (hasGetAll2) { var req = values ? source.getAll(idbKeyRange, nonInfinitLimit) : source.getAllKeys(idbKeyRange, nonInfinitLimit); req.onsuccess = function(event) { return resolve({ result: event.target.result }); }; req.onerror = eventRejectHandler(reject); } else { var count_1 = 0; var req_1 = values || !("openKeyCursor" in source) ? source.openCursor(idbKeyRange) : source.openKeyCursor(idbKeyRange); var result_1 = []; req_1.onsuccess = function(event) { var cursor = req_1.result; if (!cursor) return resolve({ result: result_1 }); result_1.push(values ? cursor.value : cursor.primaryKey); if (++count_1 === limit2) return resolve({ result: result_1 }); cursor.continue(); }; req_1.onerror = eventRejectHandler(reject); } }); }; } return { name: tableName, schema: tableSchema, mutate, getMany: function(_a74) { var trans = _a74.trans, keys2 = _a74.keys; return new Promise(function(resolve, reject) { resolve = wrap(resolve); var store = trans.objectStore(tableName); var length = keys2.length; var result = new Array(length); var keyCount = 0; var callbackCount = 0; var req; var successHandler = function(event) { var req2 = event.target; if ((result[req2._pos] = req2.result) != null) ; if (++callbackCount === keyCount) resolve(result); }; var errorHandler = eventRejectHandler(reject); for (var i3 = 0; i3 < length; ++i3) { var key = keys2[i3]; if (key != null) { req = store.get(keys2[i3]); req._pos = i3; req.onsuccess = successHandler; req.onerror = errorHandler; ++keyCount; } } if (keyCount === 0) resolve(result); }); }, get: function(_a74) { var trans = _a74.trans, key = _a74.key; return new Promise(function(resolve, reject) { resolve = wrap(resolve); var store = trans.objectStore(tableName); var req = store.get(key); req.onsuccess = function(event) { return resolve(event.target.result); }; req.onerror = eventRejectHandler(reject); }); }, query: query3(hasGetAll), openCursor: openCursor2, count: function(_a74) { var query4 = _a74.query, trans = _a74.trans; var index = query4.index, range = query4.range; return new Promise(function(resolve, reject) { var store = trans.objectStore(tableName); var source = index.isPrimaryKey ? store : store.index(index.name); var idbKeyRange = makeIDBKeyRange(range); var req = idbKeyRange ? source.count(idbKeyRange) : source.count(); req.onsuccess = wrap(function(ev) { return resolve(ev.target.result); }); req.onerror = eventRejectHandler(reject); }); } }; } var _a73 = extractSchema(db3, tmpTrans), schema = _a73.schema, hasGetAll = _a73.hasGetAll; var tables = schema.tables.map(function(tableSchema) { return createDbCoreTable(tableSchema); }); var tableMap = {}; tables.forEach(function(table) { return tableMap[table.name] = table; }); return { stack: "dbcore", transaction: db3.transaction.bind(db3), table: function(name) { var result = tableMap[name]; if (!result) throw new Error("Table '".concat(name, "' not found")); return tableMap[name]; }, MIN_KEY: -Infinity, MAX_KEY: getMaxKey(IdbKeyRange), schema }; } function createMiddlewareStack(stackImpl, middlewares) { return middlewares.reduce(function(down, _a73) { var create = _a73.create; return __assign(__assign({}, down), create(down)); }, stackImpl); } function createMiddlewareStacks(middlewares, idbdb, _a73, tmpTrans) { var IDBKeyRange = _a73.IDBKeyRange; _a73.indexedDB; var dbcore = createMiddlewareStack(createDBCore(idbdb, IDBKeyRange, tmpTrans), middlewares.dbcore); return { dbcore }; } function generateMiddlewareStacks(db3, tmpTrans) { var idbdb = tmpTrans.db; var stacks = createMiddlewareStacks(db3._middlewares, idbdb, db3._deps, tmpTrans); db3.core = stacks.dbcore; db3.tables.forEach(function(table) { var tableName = table.name; if (db3.core.schema.tables.some(function(tbl) { return tbl.name === tableName; })) { table.core = db3.core.table(tableName); if (db3[tableName] instanceof db3.Table) { db3[tableName].core = table.core; } } }); } function setApiOnPlace(db3, objs, tableNames, dbschema) { tableNames.forEach(function(tableName) { var schema = dbschema[tableName]; objs.forEach(function(obj) { var propDesc = getPropertyDescriptor(obj, tableName); if (!propDesc || "value" in propDesc && propDesc.value === void 0) { if (obj === db3.Transaction.prototype || obj instanceof db3.Transaction) { setProp(obj, tableName, { get: function() { return this.table(tableName); }, set: function(value) { defineProperty(this, tableName, { value, writable: true, configurable: true, enumerable: true }); } }); } else { obj[tableName] = new db3.Table(tableName, schema); } } }); }); } function removeTablesApi(db3, objs) { objs.forEach(function(obj) { for (var key in obj) { if (obj[key] instanceof db3.Table) delete obj[key]; } }); } function lowerVersionFirst(a, b) { return a._cfg.version - b._cfg.version; } function runUpgraders(db3, oldVersion, idbUpgradeTrans, reject) { var globalSchema = db3._dbSchema; if (idbUpgradeTrans.objectStoreNames.contains("$meta") && !globalSchema.$meta) { globalSchema.$meta = createTableSchema("$meta", parseIndexSyntax("")[0], []); db3._storeNames.push("$meta"); } var trans = db3._createTransaction("readwrite", db3._storeNames, globalSchema); trans.create(idbUpgradeTrans); trans._completion.catch(reject); var rejectTransaction = trans._reject.bind(trans); var transless = PSD.transless || PSD; newScope(function() { PSD.trans = trans; PSD.transless = transless; if (oldVersion === 0) { keys(globalSchema).forEach(function(tableName) { createTable(idbUpgradeTrans, tableName, globalSchema[tableName].primKey, globalSchema[tableName].indexes); }); generateMiddlewareStacks(db3, idbUpgradeTrans); DexiePromise.follow(function() { return db3.on.populate.fire(trans); }).catch(rejectTransaction); } else { generateMiddlewareStacks(db3, idbUpgradeTrans); return getExistingVersion(db3, trans, oldVersion).then(function(oldVersion2) { return updateTablesAndIndexes(db3, oldVersion2, trans, idbUpgradeTrans); }).catch(rejectTransaction); } }); } function patchCurrentVersion(db3, idbUpgradeTrans) { createMissingTables(db3._dbSchema, idbUpgradeTrans); if (idbUpgradeTrans.db.version % 10 === 0 && !idbUpgradeTrans.objectStoreNames.contains("$meta")) { idbUpgradeTrans.db.createObjectStore("$meta").add(Math.ceil(idbUpgradeTrans.db.version / 10 - 1), "version"); } var globalSchema = buildGlobalSchema(db3, db3.idbdb, idbUpgradeTrans); adjustToExistingIndexNames(db3, db3._dbSchema, idbUpgradeTrans); var diff = getSchemaDiff(globalSchema, db3._dbSchema); var _loop_1 = function(tableChange2) { if (tableChange2.change.length || tableChange2.recreate) { console.warn("Unable to patch indexes of table ".concat(tableChange2.name, " because it has changes on the type of index or primary key.")); return { value: void 0 }; } var store = idbUpgradeTrans.objectStore(tableChange2.name); tableChange2.add.forEach(function(idx) { if (debug15) console.debug("Dexie upgrade patch: Creating missing index ".concat(tableChange2.name, ".").concat(idx.src)); addIndex(store, idx); }); }; for (var _i = 0, _a73 = diff.change; _i < _a73.length; _i++) { var tableChange = _a73[_i]; var state_1 = _loop_1(tableChange); if (typeof state_1 === "object") return state_1.value; } } function getExistingVersion(db3, trans, oldVersion) { if (trans.storeNames.includes("$meta")) { return trans.table("$meta").get("version").then(function(metaVersion) { return metaVersion != null ? metaVersion : oldVersion; }); } else { return DexiePromise.resolve(oldVersion); } } function updateTablesAndIndexes(db3, oldVersion, trans, idbUpgradeTrans) { var queue = []; var versions = db3._versions; var globalSchema = db3._dbSchema = buildGlobalSchema(db3, db3.idbdb, idbUpgradeTrans); var versToRun = versions.filter(function(v6) { return v6._cfg.version >= oldVersion; }); if (versToRun.length === 0) { return DexiePromise.resolve(); } versToRun.forEach(function(version) { queue.push(function() { var oldSchema = globalSchema; var newSchema = version._cfg.dbschema; adjustToExistingIndexNames(db3, oldSchema, idbUpgradeTrans); adjustToExistingIndexNames(db3, newSchema, idbUpgradeTrans); globalSchema = db3._dbSchema = newSchema; var diff = getSchemaDiff(oldSchema, newSchema); diff.add.forEach(function(tuple) { createTable(idbUpgradeTrans, tuple[0], tuple[1].primKey, tuple[1].indexes); }); diff.change.forEach(function(change) { if (change.recreate) { throw new exceptions.Upgrade("Not yet support for changing primary key"); } else { var store_1 = idbUpgradeTrans.objectStore(change.name); change.add.forEach(function(idx) { return addIndex(store_1, idx); }); change.change.forEach(function(idx) { store_1.deleteIndex(idx.name); addIndex(store_1, idx); }); change.del.forEach(function(idxName) { return store_1.deleteIndex(idxName); }); } }); var contentUpgrade = version._cfg.contentUpgrade; if (contentUpgrade && version._cfg.version > oldVersion) { generateMiddlewareStacks(db3, idbUpgradeTrans); trans._memoizedTables = {}; var upgradeSchema_1 = shallowClone(newSchema); diff.del.forEach(function(table) { upgradeSchema_1[table] = oldSchema[table]; }); removeTablesApi(db3, [db3.Transaction.prototype]); setApiOnPlace(db3, [db3.Transaction.prototype], keys(upgradeSchema_1), upgradeSchema_1); trans.schema = upgradeSchema_1; var contentUpgradeIsAsync_1 = isAsyncFunction(contentUpgrade); if (contentUpgradeIsAsync_1) { incrementExpectedAwaits(); } var returnValue_1; var promiseFollowed = DexiePromise.follow(function() { returnValue_1 = contentUpgrade(trans); if (returnValue_1) { if (contentUpgradeIsAsync_1) { var decrementor = decrementExpectedAwaits.bind(null, null); returnValue_1.then(decrementor, decrementor); } } }); return returnValue_1 && typeof returnValue_1.then === "function" ? DexiePromise.resolve(returnValue_1) : promiseFollowed.then(function() { return returnValue_1; }); } }); queue.push(function(idbtrans) { var newSchema = version._cfg.dbschema; deleteRemovedTables(newSchema, idbtrans); removeTablesApi(db3, [db3.Transaction.prototype]); setApiOnPlace(db3, [db3.Transaction.prototype], db3._storeNames, db3._dbSchema); trans.schema = db3._dbSchema; }); queue.push(function(idbtrans) { if (db3.idbdb.objectStoreNames.contains("$meta")) { if (Math.ceil(db3.idbdb.version / 10) === version._cfg.version) { db3.idbdb.deleteObjectStore("$meta"); delete db3._dbSchema.$meta; db3._storeNames = db3._storeNames.filter(function(name) { return name !== "$meta"; }); } else { idbtrans.objectStore("$meta").put(version._cfg.version, "version"); } } }); }); function runQueue() { return queue.length ? DexiePromise.resolve(queue.shift()(trans.idbtrans)).then(runQueue) : DexiePromise.resolve(); } return runQueue().then(function() { createMissingTables(globalSchema, idbUpgradeTrans); }); } function getSchemaDiff(oldSchema, newSchema) { var diff = { del: [], add: [], change: [] }; var table; for (table in oldSchema) { if (!newSchema[table]) diff.del.push(table); } for (table in newSchema) { var oldDef = oldSchema[table], newDef = newSchema[table]; if (!oldDef) { diff.add.push([table, newDef]); } else { var change = { name: table, def: newDef, recreate: false, del: [], add: [], change: [] }; if ("" + (oldDef.primKey.keyPath || "") !== "" + (newDef.primKey.keyPath || "") || oldDef.primKey.auto !== newDef.primKey.auto) { change.recreate = true; diff.change.push(change); } else { var oldIndexes = oldDef.idxByName; var newIndexes = newDef.idxByName; var idxName = void 0; for (idxName in oldIndexes) { if (!newIndexes[idxName]) change.del.push(idxName); } for (idxName in newIndexes) { var oldIdx = oldIndexes[idxName], newIdx = newIndexes[idxName]; if (!oldIdx) change.add.push(newIdx); else if (oldIdx.src !== newIdx.src) change.change.push(newIdx); } if (change.del.length > 0 || change.add.length > 0 || change.change.length > 0) { diff.change.push(change); } } } } return diff; } function createTable(idbtrans, tableName, primKey, indexes) { var store = idbtrans.db.createObjectStore(tableName, primKey.keyPath ? { keyPath: primKey.keyPath, autoIncrement: primKey.auto } : { autoIncrement: primKey.auto }); indexes.forEach(function(idx) { return addIndex(store, idx); }); return store; } function createMissingTables(newSchema, idbtrans) { keys(newSchema).forEach(function(tableName) { if (!idbtrans.db.objectStoreNames.contains(tableName)) { if (debug15) console.debug("Dexie: Creating missing table", tableName); createTable(idbtrans, tableName, newSchema[tableName].primKey, newSchema[tableName].indexes); } }); } function deleteRemovedTables(newSchema, idbtrans) { [].slice.call(idbtrans.db.objectStoreNames).forEach(function(storeName) { return newSchema[storeName] == null && idbtrans.db.deleteObjectStore(storeName); }); } function addIndex(store, idx) { store.createIndex(idx.name, idx.keyPath, { unique: idx.unique, multiEntry: idx.multi }); } function buildGlobalSchema(db3, idbdb, tmpTrans) { var globalSchema = {}; var dbStoreNames = slice(idbdb.objectStoreNames, 0); dbStoreNames.forEach(function(storeName) { var store = tmpTrans.objectStore(storeName); var keyPath = store.keyPath; var primKey = createIndexSpec(nameFromKeyPath(keyPath), keyPath || "", true, false, !!store.autoIncrement, keyPath && typeof keyPath !== "string", true); var indexes = []; for (var j2 = 0; j2 < store.indexNames.length; ++j2) { var idbindex = store.index(store.indexNames[j2]); keyPath = idbindex.keyPath; var index = createIndexSpec(idbindex.name, keyPath, !!idbindex.unique, !!idbindex.multiEntry, false, keyPath && typeof keyPath !== "string", false); indexes.push(index); } globalSchema[storeName] = createTableSchema(storeName, primKey, indexes); }); return globalSchema; } function readGlobalSchema(db3, idbdb, tmpTrans) { db3.verno = idbdb.version / 10; var globalSchema = db3._dbSchema = buildGlobalSchema(db3, idbdb, tmpTrans); db3._storeNames = slice(idbdb.objectStoreNames, 0); setApiOnPlace(db3, [db3._allTables], keys(globalSchema), globalSchema); } function verifyInstalledSchema(db3, tmpTrans) { var installedSchema = buildGlobalSchema(db3, db3.idbdb, tmpTrans); var diff = getSchemaDiff(installedSchema, db3._dbSchema); return !(diff.add.length || diff.change.some(function(ch) { return ch.add.length || ch.change.length; })); } function adjustToExistingIndexNames(db3, schema, idbtrans) { var storeNames = idbtrans.db.objectStoreNames; for (var i3 = 0; i3 < storeNames.length; ++i3) { var storeName = storeNames[i3]; var store = idbtrans.objectStore(storeName); db3._hasGetAll = "getAll" in store; for (var j2 = 0; j2 < store.indexNames.length; ++j2) { var indexName = store.indexNames[j2]; var keyPath = store.index(indexName).keyPath; var dexieName = typeof keyPath === "string" ? keyPath : "[" + slice(keyPath).join("+") + "]"; if (schema[storeName]) { var indexSpec = schema[storeName].idxByName[dexieName]; if (indexSpec) { indexSpec.name = indexName; delete schema[storeName].idxByName[dexieName]; schema[storeName].idxByName[indexName] = indexSpec; } } } } if (typeof navigator !== "undefined" && /Safari/.test(navigator.userAgent) && !/(Chrome\/|Edge\/)/.test(navigator.userAgent) && _global.WorkerGlobalScope && _global instanceof _global.WorkerGlobalScope && [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604) { db3._hasGetAll = false; } } function parseIndexSyntax(primKeyAndIndexes) { return primKeyAndIndexes.split(",").map(function(index, indexNum) { var _a73; var typeSplit = index.split(":"); var type2 = (_a73 = typeSplit[1]) === null || _a73 === void 0 ? void 0 : _a73.trim(); index = typeSplit[0].trim(); var name = index.replace(/([&*]|\+\+)/g, ""); var keyPath = /^\[/.test(name) ? name.match(/^\[(.*)\]$/)[1].split("+") : name; return createIndexSpec(name, keyPath || null, /\&/.test(index), /\*/.test(index), /\+\+/.test(index), isArray(keyPath), indexNum === 0, type2); }); } var Version = (function() { function Version2() { } Version2.prototype._createTableSchema = function(name, primKey, indexes) { return createTableSchema(name, primKey, indexes); }; Version2.prototype._parseIndexSyntax = function(primKeyAndIndexes) { return parseIndexSyntax(primKeyAndIndexes); }; Version2.prototype._parseStoresSpec = function(stores, outSchema) { var _this = this; keys(stores).forEach(function(tableName) { if (stores[tableName] !== null) { var indexes = _this._parseIndexSyntax(stores[tableName]); var primKey = indexes.shift(); if (!primKey) { throw new exceptions.Schema("Invalid schema for table " + tableName + ": " + stores[tableName]); } primKey.unique = true; if (primKey.multi) throw new exceptions.Schema("Primary key cannot be multiEntry*"); indexes.forEach(function(idx) { if (idx.auto) throw new exceptions.Schema("Only primary key can be marked as autoIncrement (++)"); if (!idx.keyPath) throw new exceptions.Schema("Index must have a name and cannot be an empty string"); }); var tblSchema = _this._createTableSchema(tableName, primKey, indexes); outSchema[tableName] = tblSchema; } }); }; Version2.prototype.stores = function(stores) { var db3 = this.db; this._cfg.storesSource = this._cfg.storesSource ? extend(this._cfg.storesSource, stores) : stores; var versions = db3._versions; var storesSpec = {}; var dbschema = {}; versions.forEach(function(version) { extend(storesSpec, version._cfg.storesSource); dbschema = version._cfg.dbschema = {}; version._parseStoresSpec(storesSpec, dbschema); }); db3._dbSchema = dbschema; removeTablesApi(db3, [db3._allTables, db3, db3.Transaction.prototype]); setApiOnPlace(db3, [db3._allTables, db3, db3.Transaction.prototype, this._cfg.tables], keys(dbschema), dbschema); db3._storeNames = keys(dbschema); return this; }; Version2.prototype.upgrade = function(upgradeFunction) { this._cfg.contentUpgrade = promisableChain(this._cfg.contentUpgrade || nop, upgradeFunction); return this; }; return Version2; })(); function createVersionConstructor(db3) { return makeClassConstructor(Version.prototype, function Version2(versionNumber) { this.db = db3; this._cfg = { version: versionNumber, storesSource: null, dbschema: {}, tables: {}, contentUpgrade: null }; }); } function getDbNamesTable(indexedDB2, IDBKeyRange) { var dbNamesDB = indexedDB2["_dbNamesDB"]; if (!dbNamesDB) { dbNamesDB = indexedDB2["_dbNamesDB"] = new Dexie$1(DBNAMES_DB, { addons: [], indexedDB: indexedDB2, IDBKeyRange }); dbNamesDB.version(1).stores({ dbnames: "name" }); } return dbNamesDB.table("dbnames"); } function hasDatabasesNative(indexedDB2) { return indexedDB2 && typeof indexedDB2.databases === "function"; } function getDatabaseNames(_a73) { var indexedDB2 = _a73.indexedDB, IDBKeyRange = _a73.IDBKeyRange; return hasDatabasesNative(indexedDB2) ? Promise.resolve(indexedDB2.databases()).then(function(infos) { return infos.map(function(info) { return info.name; }).filter(function(name) { return name !== DBNAMES_DB; }); }) : getDbNamesTable(indexedDB2, IDBKeyRange).toCollection().primaryKeys(); } function _onDatabaseCreated(_a73, name) { var indexedDB2 = _a73.indexedDB, IDBKeyRange = _a73.IDBKeyRange; !hasDatabasesNative(indexedDB2) && name !== DBNAMES_DB && getDbNamesTable(indexedDB2, IDBKeyRange).put({ name }).catch(nop); } function _onDatabaseDeleted(_a73, name) { var indexedDB2 = _a73.indexedDB, IDBKeyRange = _a73.IDBKeyRange; !hasDatabasesNative(indexedDB2) && name !== DBNAMES_DB && getDbNamesTable(indexedDB2, IDBKeyRange).delete(name).catch(nop); } function vip(fn) { return newScope(function() { PSD.letThrough = true; return fn(); }); } function idbReady() { var isSafari = !navigator.userAgentData && /Safari\//.test(navigator.userAgent) && !/Chrom(e|ium)\//.test(navigator.userAgent); if (!isSafari || !indexedDB.databases) return Promise.resolve(); var intervalId; return new Promise(function(resolve) { var tryIdb = function() { return indexedDB.databases().finally(resolve); }; intervalId = setInterval(tryIdb, 100); tryIdb(); }).finally(function() { return clearInterval(intervalId); }); } var _a72; function isEmptyRange(node) { return !("from" in node); } var RangeSet2 = function(fromOrTree, to) { if (this) { extend(this, arguments.length ? { d: 1, from: fromOrTree, to: arguments.length > 1 ? to : fromOrTree } : { d: 0 }); } else { var rv = new RangeSet2(); if (fromOrTree && "d" in fromOrTree) { extend(rv, fromOrTree); } return rv; } }; props(RangeSet2.prototype, (_a72 = { add: function(rangeSet) { mergeRanges2(this, rangeSet); return this; }, addKey: function(key) { addRange(this, key, key); return this; }, addKeys: function(keys2) { var _this = this; keys2.forEach(function(key) { return addRange(_this, key, key); }); return this; }, hasKey: function(key) { var node = getRangeSetIterator(this).next(key).value; return node && cmp2(node.from, key) <= 0 && cmp2(node.to, key) >= 0; } }, _a72[iteratorSymbol] = function() { return getRangeSetIterator(this); }, _a72)); function addRange(target, from, to) { var diff = cmp2(from, to); if (isNaN(diff)) return; if (diff > 0) throw RangeError(); if (isEmptyRange(target)) return extend(target, { from, to, d: 1 }); var left = target.l; var right = target.r; if (cmp2(to, target.from) < 0) { left ? addRange(left, from, to) : target.l = { from, to, d: 1, l: null, r: null }; return rebalance(target); } if (cmp2(from, target.to) > 0) { right ? addRange(right, from, to) : target.r = { from, to, d: 1, l: null, r: null }; return rebalance(target); } if (cmp2(from, target.from) < 0) { target.from = from; target.l = null; target.d = right ? right.d + 1 : 1; } if (cmp2(to, target.to) > 0) { target.to = to; target.r = null; target.d = target.l ? target.l.d + 1 : 1; } var rightWasCutOff = !target.r; if (left && !target.l) { mergeRanges2(target, left); } if (right && rightWasCutOff) { mergeRanges2(target, right); } } function mergeRanges2(target, newSet) { function _addRangeSet(target2, _a73) { var from = _a73.from, to = _a73.to, l3 = _a73.l, r = _a73.r; addRange(target2, from, to); if (l3) _addRangeSet(target2, l3); if (r) _addRangeSet(target2, r); } if (!isEmptyRange(newSet)) _addRangeSet(target, newSet); } function rangesOverlap2(rangeSet1, rangeSet2) { var i1 = getRangeSetIterator(rangeSet2); var nextResult1 = i1.next(); if (nextResult1.done) return false; var a = nextResult1.value; var i22 = getRangeSetIterator(rangeSet1); var nextResult2 = i22.next(a.from); var b = nextResult2.value; while (!nextResult1.done && !nextResult2.done) { if (cmp2(b.from, a.to) <= 0 && cmp2(b.to, a.from) >= 0) return true; cmp2(a.from, b.from) < 0 ? a = (nextResult1 = i1.next(b.from)).value : b = (nextResult2 = i22.next(a.from)).value; } return false; } function getRangeSetIterator(node) { var state = isEmptyRange(node) ? null : { s: 0, n: node }; return { next: function(key) { var keyProvided = arguments.length > 0; while (state) { switch (state.s) { case 0: state.s = 1; if (keyProvided) { while (state.n.l && cmp2(key, state.n.from) < 0) state = { up: state, n: state.n.l, s: 1 }; } else { while (state.n.l) state = { up: state, n: state.n.l, s: 1 }; } case 1: state.s = 2; if (!keyProvided || cmp2(key, state.n.to) <= 0) return { value: state.n, done: false }; case 2: if (state.n.r) { state.s = 3; state = { up: state, n: state.n.r, s: 0 }; continue; } case 3: state = state.up; } } return { done: true }; } }; } function rebalance(target) { var _a73, _b; var diff = (((_a73 = target.r) === null || _a73 === void 0 ? void 0 : _a73.d) || 0) - (((_b = target.l) === null || _b === void 0 ? void 0 : _b.d) || 0); var r = diff > 1 ? "r" : diff < -1 ? "l" : ""; if (r) { var l3 = r === "r" ? "l" : "r"; var rootClone = __assign({}, target); var oldRootRight = target[r]; target.from = oldRootRight.from; target.to = oldRootRight.to; target[r] = oldRootRight[r]; rootClone[r] = oldRootRight[l3]; target[l3] = rootClone; rootClone.d = computeDepth(rootClone); } target.d = computeDepth(target); } function computeDepth(_a73) { var r = _a73.r, l3 = _a73.l; return (r ? l3 ? Math.max(r.d, l3.d) : r.d : l3 ? l3.d : 0) + 1; } function extendObservabilitySet(target, newSet) { keys(newSet).forEach(function(part) { if (target[part]) mergeRanges2(target[part], newSet[part]); else target[part] = cloneSimpleObjectTree(newSet[part]); }); return target; } function obsSetsOverlap(os1, os2) { return os1.all || os2.all || Object.keys(os1).some(function(key) { return os2[key] && rangesOverlap2(os2[key], os1[key]); }); } var cache = {}; var unsignaledParts = {}; var isTaskEnqueued = false; function signalSubscribersLazily(part, optimistic) { extendObservabilitySet(unsignaledParts, part); if (!isTaskEnqueued) { isTaskEnqueued = true; setTimeout(function() { isTaskEnqueued = false; var parts = unsignaledParts; unsignaledParts = {}; signalSubscribersNow(parts, false); }, 0); } } function signalSubscribersNow(updatedParts, deleteAffectedCacheEntries) { if (deleteAffectedCacheEntries === void 0) { deleteAffectedCacheEntries = false; } var queriesToSignal = /* @__PURE__ */ new Set(); if (updatedParts.all) { for (var _i = 0, _a73 = Object.values(cache); _i < _a73.length; _i++) { var tblCache = _a73[_i]; collectTableSubscribers(tblCache, updatedParts, queriesToSignal, deleteAffectedCacheEntries); } } else { for (var key in updatedParts) { var parts = /^idb\:\/\/(.*)\/(.*)\//.exec(key); if (parts) { var dbName = parts[1], tableName = parts[2]; var tblCache = cache["idb://".concat(dbName, "/").concat(tableName)]; if (tblCache) collectTableSubscribers(tblCache, updatedParts, queriesToSignal, deleteAffectedCacheEntries); } } } queriesToSignal.forEach(function(requery) { return requery(); }); } function collectTableSubscribers(tblCache, updatedParts, outQueriesToSignal, deleteAffectedCacheEntries) { var updatedEntryLists = []; for (var _i = 0, _a73 = Object.entries(tblCache.queries.query); _i < _a73.length; _i++) { var _b = _a73[_i], indexName = _b[0], entries = _b[1]; var filteredEntries = []; for (var _c = 0, entries_1 = entries; _c < entries_1.length; _c++) { var entry = entries_1[_c]; if (obsSetsOverlap(updatedParts, entry.obsSet)) { entry.subscribers.forEach(function(requery) { return outQueriesToSignal.add(requery); }); } else if (deleteAffectedCacheEntries) { filteredEntries.push(entry); } } if (deleteAffectedCacheEntries) updatedEntryLists.push([indexName, filteredEntries]); } if (deleteAffectedCacheEntries) { for (var _d = 0, updatedEntryLists_1 = updatedEntryLists; _d < updatedEntryLists_1.length; _d++) { var _e2 = updatedEntryLists_1[_d], indexName = _e2[0], filteredEntries = _e2[1]; tblCache.queries.query[indexName] = filteredEntries; } } } function dexieOpen(db3) { var state = db3._state; var indexedDB2 = db3._deps.indexedDB; if (state.isBeingOpened || db3.idbdb) return state.dbReadyPromise.then(function() { return state.dbOpenError ? rejection(state.dbOpenError) : db3; }); state.isBeingOpened = true; state.dbOpenError = null; state.openComplete = false; var openCanceller = state.openCanceller; var nativeVerToOpen = Math.round(db3.verno * 10); var schemaPatchMode = false; function throwIfCancelled() { if (state.openCanceller !== openCanceller) throw new exceptions.DatabaseClosed("db.open() was cancelled"); } var resolveDbReady = state.dbReadyResolve, upgradeTransaction = null, wasCreated = false; var tryOpenDB = function() { return new DexiePromise(function(resolve, reject) { throwIfCancelled(); if (!indexedDB2) throw new exceptions.MissingAPI(); var dbName = db3.name; var req = state.autoSchema || !nativeVerToOpen ? indexedDB2.open(dbName) : indexedDB2.open(dbName, nativeVerToOpen); if (!req) throw new exceptions.MissingAPI(); req.onerror = eventRejectHandler(reject); req.onblocked = wrap(db3._fireOnBlocked); req.onupgradeneeded = wrap(function(e2) { upgradeTransaction = req.transaction; if (state.autoSchema && !db3._options.allowEmptyDB) { req.onerror = preventDefault; upgradeTransaction.abort(); req.result.close(); var delreq = indexedDB2.deleteDatabase(dbName); delreq.onsuccess = delreq.onerror = wrap(function() { reject(new exceptions.NoSuchDatabase("Database ".concat(dbName, " doesnt exist"))); }); } else { upgradeTransaction.onerror = eventRejectHandler(reject); var oldVer = e2.oldVersion > Math.pow(2, 62) ? 0 : e2.oldVersion; wasCreated = oldVer < 1; db3.idbdb = req.result; if (schemaPatchMode) { patchCurrentVersion(db3, upgradeTransaction); } runUpgraders(db3, oldVer / 10, upgradeTransaction, reject); } }, reject); req.onsuccess = wrap(function() { upgradeTransaction = null; var idbdb = db3.idbdb = req.result; var objectStoreNames = slice(idbdb.objectStoreNames); if (objectStoreNames.length > 0) try { var tmpTrans = idbdb.transaction(safariMultiStoreFix(objectStoreNames), "readonly"); if (state.autoSchema) readGlobalSchema(db3, idbdb, tmpTrans); else { adjustToExistingIndexNames(db3, db3._dbSchema, tmpTrans); if (!verifyInstalledSchema(db3, tmpTrans) && !schemaPatchMode) { console.warn("Dexie SchemaDiff: Schema was extended without increasing the number passed to db.version(). Dexie will add missing parts and increment native version number to workaround this."); idbdb.close(); nativeVerToOpen = idbdb.version + 1; schemaPatchMode = true; return resolve(tryOpenDB()); } } generateMiddlewareStacks(db3, tmpTrans); } catch (e2) { } connections.push(db3); idbdb.onversionchange = wrap(function(ev) { state.vcFired = true; db3.on("versionchange").fire(ev); }); idbdb.onclose = wrap(function() { db3.close({ disableAutoOpen: false }); }); if (wasCreated) _onDatabaseCreated(db3._deps, dbName); resolve(); }, reject); }).catch(function(err) { switch (err === null || err === void 0 ? void 0 : err.name) { case "UnknownError": if (state.PR1398_maxLoop > 0) { state.PR1398_maxLoop--; console.warn("Dexie: Workaround for Chrome UnknownError on open()"); return tryOpenDB(); } break; case "VersionError": if (nativeVerToOpen > 0) { nativeVerToOpen = 0; return tryOpenDB(); } break; } return DexiePromise.reject(err); }); }; return DexiePromise.race([ openCanceller, (typeof navigator === "undefined" ? DexiePromise.resolve() : idbReady()).then(tryOpenDB) ]).then(function() { throwIfCancelled(); state.onReadyBeingFired = []; return DexiePromise.resolve(vip(function() { return db3.on.ready.fire(db3.vip); })).then(function fireRemainders() { if (state.onReadyBeingFired.length > 0) { var remainders_1 = state.onReadyBeingFired.reduce(promisableChain, nop); state.onReadyBeingFired = []; return DexiePromise.resolve(vip(function() { return remainders_1(db3.vip); })).then(fireRemainders); } }); }).finally(function() { if (state.openCanceller === openCanceller) { state.onReadyBeingFired = null; state.isBeingOpened = false; } }).catch(function(err) { state.dbOpenError = err; try { upgradeTransaction && upgradeTransaction.abort(); } catch (_a73) { } if (openCanceller === state.openCanceller) { db3._close(); } return rejection(err); }).finally(function() { state.openComplete = true; resolveDbReady(); }).then(function() { if (wasCreated) { var everything_1 = {}; db3.tables.forEach(function(table) { table.schema.indexes.forEach(function(idx) { if (idx.name) everything_1["idb://".concat(db3.name, "/").concat(table.name, "/").concat(idx.name)] = new RangeSet2(-Infinity, [[[]]]); }); everything_1["idb://".concat(db3.name, "/").concat(table.name, "/")] = everything_1["idb://".concat(db3.name, "/").concat(table.name, "/:dels")] = new RangeSet2(-Infinity, [[[]]]); }); globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME).fire(everything_1); signalSubscribersNow(everything_1, true); } return db3; }); } function awaitIterator(iterator) { var callNext = function(result) { return iterator.next(result); }, doThrow = function(error) { return iterator.throw(error); }, onSuccess = step(callNext), onError = step(doThrow); function step(getNext) { return function(val) { var next = getNext(val), value = next.value; return next.done ? value : !value || typeof value.then !== "function" ? isArray(value) ? Promise.all(value).then(onSuccess, onError) : onSuccess(value) : value.then(onSuccess, onError); }; } return step(callNext)(); } function extractTransactionArgs(mode, _tableArgs_, scopeFunc) { var i3 = arguments.length; if (i3 < 2) throw new exceptions.InvalidArgument("Too few arguments"); var args = new Array(i3 - 1); while (--i3) args[i3 - 1] = arguments[i3]; scopeFunc = args.pop(); var tables = flatten(args); return [mode, tables, scopeFunc]; } function enterTransactionScope(db3, mode, storeNames, parentTransaction, scopeFunc) { return DexiePromise.resolve().then(function() { var transless = PSD.transless || PSD; var trans = db3._createTransaction(mode, storeNames, db3._dbSchema, parentTransaction); trans.explicit = true; var zoneProps = { trans, transless }; if (parentTransaction) { trans.idbtrans = parentTransaction.idbtrans; } else { try { trans.create(); trans.idbtrans._explicit = true; db3._state.PR1398_maxLoop = 3; } catch (ex) { if (ex.name === errnames.InvalidState && db3.isOpen() && --db3._state.PR1398_maxLoop > 0) { console.warn("Dexie: Need to reopen db"); db3.close({ disableAutoOpen: false }); return db3.open().then(function() { return enterTransactionScope(db3, mode, storeNames, null, scopeFunc); }); } return rejection(ex); } } var scopeFuncIsAsync = isAsyncFunction(scopeFunc); if (scopeFuncIsAsync) { incrementExpectedAwaits(); } var returnValue; var promiseFollowed = DexiePromise.follow(function() { returnValue = scopeFunc.call(trans, trans); if (returnValue) { if (scopeFuncIsAsync) { var decrementor = decrementExpectedAwaits.bind(null, null); returnValue.then(decrementor, decrementor); } else if (typeof returnValue.next === "function" && typeof returnValue.throw === "function") { returnValue = awaitIterator(returnValue); } } }, zoneProps); return (returnValue && typeof returnValue.then === "function" ? DexiePromise.resolve(returnValue).then(function(x2) { return trans.active ? x2 : rejection(new exceptions.PrematureCommit("Transaction committed too early. See http://bit.ly/2kdckMn")); }) : promiseFollowed.then(function() { return returnValue; })).then(function(x2) { if (parentTransaction) trans._resolve(); return trans._completion.then(function() { return x2; }); }).catch(function(e2) { trans._reject(e2); return rejection(e2); }); }); } function pad2(a, value, count) { var result = isArray(a) ? a.slice() : [a]; for (var i3 = 0; i3 < count; ++i3) result.push(value); return result; } function createVirtualIndexMiddleware(down) { return __assign(__assign({}, down), { table: function(tableName) { var table = down.table(tableName); var schema = table.schema; var indexLookup = {}; var allVirtualIndexes = []; function addVirtualIndexes(keyPath, keyTail, lowLevelIndex) { var keyPathAlias = getKeyPathAlias(keyPath); var indexList = indexLookup[keyPathAlias] = indexLookup[keyPathAlias] || []; var keyLength = keyPath == null ? 0 : typeof keyPath === "string" ? 1 : keyPath.length; var isVirtual = keyTail > 0; var virtualIndex = __assign(__assign({}, lowLevelIndex), { name: isVirtual ? "".concat(keyPathAlias, "(virtual-from:").concat(lowLevelIndex.name, ")") : lowLevelIndex.name, lowLevelIndex, isVirtual, keyTail, keyLength, extractKey: getKeyExtractor(keyPath), unique: !isVirtual && lowLevelIndex.unique }); indexList.push(virtualIndex); if (!virtualIndex.isPrimaryKey) { allVirtualIndexes.push(virtualIndex); } if (keyLength > 1) { var virtualKeyPath = keyLength === 2 ? keyPath[0] : keyPath.slice(0, keyLength - 1); addVirtualIndexes(virtualKeyPath, keyTail + 1, lowLevelIndex); } indexList.sort(function(a, b) { return a.keyTail - b.keyTail; }); return virtualIndex; } var primaryKey = addVirtualIndexes(schema.primaryKey.keyPath, 0, schema.primaryKey); indexLookup[":id"] = [primaryKey]; for (var _i = 0, _a73 = schema.indexes; _i < _a73.length; _i++) { var index = _a73[_i]; addVirtualIndexes(index.keyPath, 0, index); } function findBestIndex(keyPath) { var result2 = indexLookup[getKeyPathAlias(keyPath)]; return result2 && result2[0]; } function translateRange(range, keyTail) { return { type: range.type === 1 ? 2 : range.type, lower: pad2(range.lower, range.lowerOpen ? down.MAX_KEY : down.MIN_KEY, keyTail), lowerOpen: true, upper: pad2(range.upper, range.upperOpen ? down.MIN_KEY : down.MAX_KEY, keyTail), upperOpen: true }; } function translateRequest(req) { var index2 = req.query.index; return index2.isVirtual ? __assign(__assign({}, req), { query: { index: index2.lowLevelIndex, range: translateRange(req.query.range, index2.keyTail) } }) : req; } var result = __assign(__assign({}, table), { schema: __assign(__assign({}, schema), { primaryKey, indexes: allVirtualIndexes, getIndexByKeyPath: findBestIndex }), count: function(req) { return table.count(translateRequest(req)); }, query: function(req) { return table.query(translateRequest(req)); }, openCursor: function(req) { var _a74 = req.query.index, keyTail = _a74.keyTail, isVirtual = _a74.isVirtual, keyLength = _a74.keyLength; if (!isVirtual) return table.openCursor(req); function createVirtualCursor(cursor) { function _continue(key) { key != null ? cursor.continue(pad2(key, req.reverse ? down.MAX_KEY : down.MIN_KEY, keyTail)) : req.unique ? cursor.continue(cursor.key.slice(0, keyLength).concat(req.reverse ? down.MIN_KEY : down.MAX_KEY, keyTail)) : cursor.continue(); } var virtualCursor = Object.create(cursor, { continue: { value: _continue }, continuePrimaryKey: { value: function(key, primaryKey2) { cursor.continuePrimaryKey(pad2(key, down.MAX_KEY, keyTail), primaryKey2); } }, primaryKey: { get: function() { return cursor.primaryKey; } }, key: { get: function() { var key = cursor.key; return keyLength === 1 ? key[0] : key.slice(0, keyLength); } }, value: { get: function() { return cursor.value; } } }); return virtualCursor; } return table.openCursor(translateRequest(req)).then(function(cursor) { return cursor && createVirtualCursor(cursor); }); } }); return result; } }); } var virtualIndexMiddleware = { stack: "dbcore", name: "VirtualIndexMiddleware", level: 1, create: createVirtualIndexMiddleware }; function getObjectDiff(a, b, rv, prfx) { rv = rv || {}; prfx = prfx || ""; keys(a).forEach(function(prop) { if (!hasOwn(b, prop)) { rv[prfx + prop] = void 0; } else { var ap = a[prop], bp = b[prop]; if (typeof ap === "object" && typeof bp === "object" && ap && bp) { var apTypeName = toStringTag(ap); var bpTypeName = toStringTag(bp); if (apTypeName !== bpTypeName) { rv[prfx + prop] = b[prop]; } else if (apTypeName === "Object") { getObjectDiff(ap, bp, rv, prfx + prop + "."); } else if (ap !== bp) { rv[prfx + prop] = b[prop]; } } else if (ap !== bp) rv[prfx + prop] = b[prop]; } }); keys(b).forEach(function(prop) { if (!hasOwn(a, prop)) { rv[prfx + prop] = b[prop]; } }); return rv; } function getEffectiveKeys(primaryKey, req) { if (req.type === "delete") return req.keys; return req.keys || req.values.map(primaryKey.extractKey); } var hooksMiddleware = { stack: "dbcore", name: "HooksMiddleware", level: 2, create: function(downCore) { return __assign(__assign({}, downCore), { table: function(tableName) { var downTable = downCore.table(tableName); var primaryKey = downTable.schema.primaryKey; var tableMiddleware = __assign(__assign({}, downTable), { mutate: function(req) { var dxTrans = PSD.trans; var _a73 = dxTrans.table(tableName).hook, deleting = _a73.deleting, creating = _a73.creating, updating = _a73.updating; switch (req.type) { case "add": if (creating.fire === nop) break; return dxTrans._promise("readwrite", function() { return addPutOrDelete(req); }, true); case "put": if (creating.fire === nop && updating.fire === nop) break; return dxTrans._promise("readwrite", function() { return addPutOrDelete(req); }, true); case "delete": if (deleting.fire === nop) break; return dxTrans._promise("readwrite", function() { return addPutOrDelete(req); }, true); case "deleteRange": if (deleting.fire === nop) break; return dxTrans._promise("readwrite", function() { return deleteRange(req); }, true); } return downTable.mutate(req); function addPutOrDelete(req2) { var dxTrans2 = PSD.trans; var keys2 = req2.keys || getEffectiveKeys(primaryKey, req2); if (!keys2) throw new Error("Keys missing"); req2 = req2.type === "add" || req2.type === "put" ? __assign(__assign({}, req2), { keys: keys2 }) : __assign({}, req2); if (req2.type !== "delete") req2.values = __spreadArray([], req2.values, true); if (req2.keys) req2.keys = __spreadArray([], req2.keys, true); return getExistingValues(downTable, req2, keys2).then(function(existingValues) { var contexts = keys2.map(function(key, i3) { var existingValue = existingValues[i3]; var ctx = { onerror: null, onsuccess: null }; if (req2.type === "delete") { deleting.fire.call(ctx, key, existingValue, dxTrans2); } else if (req2.type === "add" || existingValue === void 0) { var generatedPrimaryKey = creating.fire.call(ctx, key, req2.values[i3], dxTrans2); if (key == null && generatedPrimaryKey != null) { key = generatedPrimaryKey; req2.keys[i3] = key; if (!primaryKey.outbound) { setByKeyPath(req2.values[i3], primaryKey.keyPath, key); } } } else { var objectDiff = getObjectDiff(existingValue, req2.values[i3]); var additionalChanges_1 = updating.fire.call(ctx, objectDiff, key, existingValue, dxTrans2); if (additionalChanges_1) { var requestedValue_1 = req2.values[i3]; Object.keys(additionalChanges_1).forEach(function(keyPath) { if (hasOwn(requestedValue_1, keyPath)) { requestedValue_1[keyPath] = additionalChanges_1[keyPath]; } else { setByKeyPath(requestedValue_1, keyPath, additionalChanges_1[keyPath]); } }); } } return ctx; }); return downTable.mutate(req2).then(function(_a74) { var failures = _a74.failures, results = _a74.results, numFailures = _a74.numFailures, lastResult = _a74.lastResult; for (var i3 = 0; i3 < keys2.length; ++i3) { var primKey = results ? results[i3] : keys2[i3]; var ctx = contexts[i3]; if (primKey == null) { ctx.onerror && ctx.onerror(failures[i3]); } else { ctx.onsuccess && ctx.onsuccess( req2.type === "put" && existingValues[i3] ? req2.values[i3] : primKey ); } } return { failures, results, numFailures, lastResult }; }).catch(function(error) { contexts.forEach(function(ctx) { return ctx.onerror && ctx.onerror(error); }); return Promise.reject(error); }); }); } function deleteRange(req2) { return deleteNextChunk(req2.trans, req2.range, 1e4); } function deleteNextChunk(trans, range, limit2) { return downTable.query({ trans, values: false, query: { index: primaryKey, range }, limit: limit2 }).then(function(_a74) { var result = _a74.result; return addPutOrDelete({ type: "delete", keys: result, trans }).then(function(res) { if (res.numFailures > 0) return Promise.reject(res.failures[0]); if (result.length < limit2) { return { failures: [], numFailures: 0, lastResult: void 0 }; } else { return deleteNextChunk(trans, __assign(__assign({}, range), { lower: result[result.length - 1], lowerOpen: true }), limit2); } }); }); } } }); return tableMiddleware; } }); } }; function getExistingValues(table, req, effectiveKeys) { return req.type === "add" ? Promise.resolve([]) : table.getMany({ trans: req.trans, keys: effectiveKeys, cache: "immutable" }); } function getFromTransactionCache(keys2, cache2, clone) { try { if (!cache2) return null; if (cache2.keys.length < keys2.length) return null; var result = []; for (var i3 = 0, j2 = 0; i3 < cache2.keys.length && j2 < keys2.length; ++i3) { if (cmp2(cache2.keys[i3], keys2[j2]) !== 0) continue; result.push(clone ? deepClone(cache2.values[i3]) : cache2.values[i3]); ++j2; } return result.length === keys2.length ? result : null; } catch (_a73) { return null; } } var cacheExistingValuesMiddleware = { stack: "dbcore", level: -1, create: function(core) { return { table: function(tableName) { var table = core.table(tableName); return __assign(__assign({}, table), { getMany: function(req) { if (!req.cache) { return table.getMany(req); } var cachedResult = getFromTransactionCache(req.keys, req.trans["_cache"], req.cache === "clone"); if (cachedResult) { return DexiePromise.resolve(cachedResult); } return table.getMany(req).then(function(res) { req.trans["_cache"] = { keys: req.keys, values: req.cache === "clone" ? deepClone(res) : res }; return res; }); }, mutate: function(req) { if (req.type !== "add") req.trans["_cache"] = null; return table.mutate(req); } }); } }; } }; function isCachableContext(ctx, table) { return ctx.trans.mode === "readonly" && !!ctx.subscr && !ctx.trans.explicit && ctx.trans.db._options.cache !== "disabled" && !table.schema.primaryKey.outbound; } function isCachableRequest(type2, req) { switch (type2) { case "query": return req.values && !req.unique; case "get": return false; case "getMany": return false; case "count": return false; case "openCursor": return false; } } var observabilityMiddleware = { stack: "dbcore", level: 0, name: "Observability", create: function(core) { var dbName = core.schema.name; var FULL_RANGE = new RangeSet2(core.MIN_KEY, core.MAX_KEY); return __assign(__assign({}, core), { transaction: function(stores, mode, options) { if (PSD.subscr && mode !== "readonly") { throw new exceptions.ReadOnly("Readwrite transaction in liveQuery context. Querier source: ".concat(PSD.querier)); } return core.transaction(stores, mode, options); }, table: function(tableName) { var table = core.table(tableName); var schema = table.schema; var primaryKey = schema.primaryKey, indexes = schema.indexes; var extractKey = primaryKey.extractKey, outbound = primaryKey.outbound; var indexesWithAutoIncPK = primaryKey.autoIncrement && indexes.filter(function(index) { return index.compound && index.keyPath.includes(primaryKey.keyPath); }); var tableClone = __assign(__assign({}, table), { mutate: function(req) { var _a73, _b; var trans = req.trans; var mutatedParts = req.mutatedParts || (req.mutatedParts = {}); var getRangeSet = function(indexName) { var part = "idb://".concat(dbName, "/").concat(tableName, "/").concat(indexName); return mutatedParts[part] || (mutatedParts[part] = new RangeSet2()); }; var pkRangeSet = getRangeSet(""); var delsRangeSet = getRangeSet(":dels"); var type2 = req.type; var _c = req.type === "deleteRange" ? [req.range] : req.type === "delete" ? [req.keys] : req.values.length < 50 ? [getEffectiveKeys(primaryKey, req).filter(function(id) { return id; }), req.values] : [], keys2 = _c[0], newObjs = _c[1]; var oldCache = req.trans["_cache"]; if (isArray(keys2)) { pkRangeSet.addKeys(keys2); var oldObjs = type2 === "delete" || keys2.length === newObjs.length ? getFromTransactionCache(keys2, oldCache) : null; if (!oldObjs) { delsRangeSet.addKeys(keys2); } if (oldObjs || newObjs) { trackAffectedIndexes(getRangeSet, schema, oldObjs, newObjs); } } else if (keys2) { var range = { from: (_a73 = keys2.lower) !== null && _a73 !== void 0 ? _a73 : core.MIN_KEY, to: (_b = keys2.upper) !== null && _b !== void 0 ? _b : core.MAX_KEY }; delsRangeSet.add(range); pkRangeSet.add(range); } else { pkRangeSet.add(FULL_RANGE); delsRangeSet.add(FULL_RANGE); schema.indexes.forEach(function(idx) { return getRangeSet(idx.name).add(FULL_RANGE); }); } return table.mutate(req).then(function(res) { if (keys2 && (req.type === "add" || req.type === "put")) { pkRangeSet.addKeys(res.results); if (indexesWithAutoIncPK) { indexesWithAutoIncPK.forEach(function(idx) { var idxVals = req.values.map(function(v6) { return idx.extractKey(v6); }); var pkPos = idx.keyPath.findIndex(function(prop) { return prop === primaryKey.keyPath; }); for (var i3 = 0, len = res.results.length; i3 < len; ++i3) { idxVals[i3][pkPos] = res.results[i3]; } getRangeSet(idx.name).addKeys(idxVals); }); } } trans.mutatedParts = extendObservabilitySet(trans.mutatedParts || {}, mutatedParts); return res; }); } }); var getRange = function(_a73) { var _b, _c; var _d = _a73.query, index = _d.index, range = _d.range; return [ index, new RangeSet2((_b = range.lower) !== null && _b !== void 0 ? _b : core.MIN_KEY, (_c = range.upper) !== null && _c !== void 0 ? _c : core.MAX_KEY) ]; }; var readSubscribers = { get: function(req) { return [primaryKey, new RangeSet2(req.key)]; }, getMany: function(req) { return [primaryKey, new RangeSet2().addKeys(req.keys)]; }, count: getRange, query: getRange, openCursor: getRange }; keys(readSubscribers).forEach(function(method) { tableClone[method] = function(req) { var subscr = PSD.subscr; var isLiveQuery = !!subscr; var cachable = isCachableContext(PSD, table) && isCachableRequest(method, req); var obsSet = cachable ? req.obsSet = {} : subscr; if (isLiveQuery) { var getRangeSet = function(indexName) { var part = "idb://".concat(dbName, "/").concat(tableName, "/").concat(indexName); return obsSet[part] || (obsSet[part] = new RangeSet2()); }; var pkRangeSet_1 = getRangeSet(""); var delsRangeSet_1 = getRangeSet(":dels"); var _a73 = readSubscribers[method](req), queriedIndex = _a73[0], queriedRanges = _a73[1]; if (method === "query" && queriedIndex.isPrimaryKey && !req.values) { delsRangeSet_1.add(queriedRanges); } else { getRangeSet(queriedIndex.name || "").add(queriedRanges); } if (!queriedIndex.isPrimaryKey) { if (method === "count") { delsRangeSet_1.add(FULL_RANGE); } else { var keysPromise_1 = method === "query" && outbound && req.values && table.query(__assign(__assign({}, req), { values: false })); return table[method].apply(this, arguments).then(function(res) { if (method === "query") { if (outbound && req.values) { return keysPromise_1.then(function(_a74) { var resultingKeys = _a74.result; pkRangeSet_1.addKeys(resultingKeys); return res; }); } var pKeys = req.values ? res.result.map(extractKey) : res.result; if (req.values) { pkRangeSet_1.addKeys(pKeys); } else { delsRangeSet_1.addKeys(pKeys); } } else if (method === "openCursor") { var cursor_1 = res; var wantValues_1 = req.values; return cursor_1 && Object.create(cursor_1, { key: { get: function() { delsRangeSet_1.addKey(cursor_1.primaryKey); return cursor_1.key; } }, primaryKey: { get: function() { var pkey = cursor_1.primaryKey; delsRangeSet_1.addKey(pkey); return pkey; } }, value: { get: function() { wantValues_1 && pkRangeSet_1.addKey(cursor_1.primaryKey); return cursor_1.value; } } }); } return res; }); } } } return table[method].apply(this, arguments); }; }); return tableClone; } }); } }; function trackAffectedIndexes(getRangeSet, schema, oldObjs, newObjs) { function addAffectedIndex(ix) { var rangeSet = getRangeSet(ix.name || ""); function extractKey(obj) { return obj != null ? ix.extractKey(obj) : null; } var addKeyOrKeys = function(key) { return ix.multiEntry && isArray(key) ? key.forEach(function(key2) { return rangeSet.addKey(key2); }) : rangeSet.addKey(key); }; (oldObjs || newObjs).forEach(function(_2, i3) { var oldKey = oldObjs && extractKey(oldObjs[i3]); var newKey = newObjs && extractKey(newObjs[i3]); if (cmp2(oldKey, newKey) !== 0) { if (oldKey != null) addKeyOrKeys(oldKey); if (newKey != null) addKeyOrKeys(newKey); } }); } schema.indexes.forEach(addAffectedIndex); } function adjustOptimisticFromFailures(tblCache, req, res) { if (res.numFailures === 0) return req; if (req.type === "deleteRange") { return null; } var numBulkOps = req.keys ? req.keys.length : "values" in req && req.values ? req.values.length : 1; if (res.numFailures === numBulkOps) { return null; } var clone = __assign({}, req); if (isArray(clone.keys)) { clone.keys = clone.keys.filter(function(_2, i3) { return !(i3 in res.failures); }); } if ("values" in clone && isArray(clone.values)) { clone.values = clone.values.filter(function(_2, i3) { return !(i3 in res.failures); }); } return clone; } function isAboveLower(key, range) { return range.lower === void 0 ? true : range.lowerOpen ? cmp2(key, range.lower) > 0 : cmp2(key, range.lower) >= 0; } function isBelowUpper(key, range) { return range.upper === void 0 ? true : range.upperOpen ? cmp2(key, range.upper) < 0 : cmp2(key, range.upper) <= 0; } function isWithinRange(key, range) { return isAboveLower(key, range) && isBelowUpper(key, range); } function applyOptimisticOps(result, req, ops, table, cacheEntry, immutable) { if (!ops || ops.length === 0) return result; var index = req.query.index; var multiEntry = index.multiEntry; var queryRange = req.query.range; var primaryKey = table.schema.primaryKey; var extractPrimKey = primaryKey.extractKey; var extractIndex = index.extractKey; var extractLowLevelIndex = (index.lowLevelIndex || index).extractKey; var finalResult = ops.reduce(function(result2, op) { var modifedResult = result2; var includedValues = []; if (op.type === "add" || op.type === "put") { var includedPKs = new RangeSet2(); for (var i3 = op.values.length - 1; i3 >= 0; --i3) { var value = op.values[i3]; var pk = extractPrimKey(value); if (includedPKs.hasKey(pk)) continue; var key = extractIndex(value); if (multiEntry && isArray(key) ? key.some(function(k2) { return isWithinRange(k2, queryRange); }) : isWithinRange(key, queryRange)) { includedPKs.addKey(pk); includedValues.push(value); } } } switch (op.type) { case "add": { var existingKeys_1 = new RangeSet2().addKeys(req.values ? result2.map(function(v6) { return extractPrimKey(v6); }) : result2); modifedResult = result2.concat(req.values ? includedValues.filter(function(v6) { var key2 = extractPrimKey(v6); if (existingKeys_1.hasKey(key2)) return false; existingKeys_1.addKey(key2); return true; }) : includedValues.map(function(v6) { return extractPrimKey(v6); }).filter(function(k2) { if (existingKeys_1.hasKey(k2)) return false; existingKeys_1.addKey(k2); return true; })); break; } case "put": { var keySet_1 = new RangeSet2().addKeys(op.values.map(function(v6) { return extractPrimKey(v6); })); modifedResult = result2.filter( function(item) { return !keySet_1.hasKey(req.values ? extractPrimKey(item) : item); } ).concat( req.values ? includedValues : includedValues.map(function(v6) { return extractPrimKey(v6); }) ); break; } case "delete": var keysToDelete_1 = new RangeSet2().addKeys(op.keys); modifedResult = result2.filter(function(item) { return !keysToDelete_1.hasKey(req.values ? extractPrimKey(item) : item); }); break; case "deleteRange": var range_1 = op.range; modifedResult = result2.filter(function(item) { return !isWithinRange(extractPrimKey(item), range_1); }); break; } return modifedResult; }, result); if (finalResult === result) return result; finalResult.sort(function(a, b) { return cmp2(extractLowLevelIndex(a), extractLowLevelIndex(b)) || cmp2(extractPrimKey(a), extractPrimKey(b)); }); if (req.limit && req.limit < Infinity) { if (finalResult.length > req.limit) { finalResult.length = req.limit; } else if (result.length === req.limit && finalResult.length < req.limit) { cacheEntry.dirty = true; } } return immutable ? Object.freeze(finalResult) : finalResult; } function areRangesEqual(r1, r2) { return cmp2(r1.lower, r2.lower) === 0 && cmp2(r1.upper, r2.upper) === 0 && !!r1.lowerOpen === !!r2.lowerOpen && !!r1.upperOpen === !!r2.upperOpen; } function compareLowers(lower1, lower2, lowerOpen1, lowerOpen2) { if (lower1 === void 0) return lower2 !== void 0 ? -1 : 0; if (lower2 === void 0) return 1; var c = cmp2(lower1, lower2); if (c === 0) { if (lowerOpen1 && lowerOpen2) return 0; if (lowerOpen1) return 1; if (lowerOpen2) return -1; } return c; } function compareUppers(upper1, upper2, upperOpen1, upperOpen2) { if (upper1 === void 0) return upper2 !== void 0 ? 1 : 0; if (upper2 === void 0) return -1; var c = cmp2(upper1, upper2); if (c === 0) { if (upperOpen1 && upperOpen2) return 0; if (upperOpen1) return -1; if (upperOpen2) return 1; } return c; } function isSuperRange(r1, r2) { return compareLowers(r1.lower, r2.lower, r1.lowerOpen, r2.lowerOpen) <= 0 && compareUppers(r1.upper, r2.upper, r1.upperOpen, r2.upperOpen) >= 0; } function findCompatibleQuery(dbName, tableName, type2, req) { var tblCache = cache["idb://".concat(dbName, "/").concat(tableName)]; if (!tblCache) return []; var queries = tblCache.queries[type2]; if (!queries) return [null, false, tblCache, null]; var indexName = req.query ? req.query.index.name : null; var entries = queries[indexName || ""]; if (!entries) return [null, false, tblCache, null]; switch (type2) { case "query": var equalEntry = entries.find(function(entry) { return entry.req.limit === req.limit && entry.req.values === req.values && areRangesEqual(entry.req.query.range, req.query.range); }); if (equalEntry) return [ equalEntry, true, tblCache, entries ]; var superEntry = entries.find(function(entry) { var limit2 = "limit" in entry.req ? entry.req.limit : Infinity; return limit2 >= req.limit && (req.values ? entry.req.values : true) && isSuperRange(entry.req.query.range, req.query.range); }); return [superEntry, false, tblCache, entries]; case "count": var countQuery = entries.find(function(entry) { return areRangesEqual(entry.req.query.range, req.query.range); }); return [countQuery, !!countQuery, tblCache, entries]; } } function subscribeToCacheEntry(cacheEntry, container, requery, signal) { cacheEntry.subscribers.add(requery); signal.addEventListener("abort", function() { cacheEntry.subscribers.delete(requery); if (cacheEntry.subscribers.size === 0) { enqueForDeletion(cacheEntry, container); } }); } function enqueForDeletion(cacheEntry, container) { setTimeout(function() { if (cacheEntry.subscribers.size === 0) { delArrayItem(container, cacheEntry); } }, 3e3); } var cacheMiddleware = { stack: "dbcore", level: 0, name: "Cache", create: function(core) { var dbName = core.schema.name; var coreMW = __assign(__assign({}, core), { transaction: function(stores, mode, options) { var idbtrans = core.transaction(stores, mode, options); if (mode === "readwrite") { var ac_1 = new AbortController(); var signal = ac_1.signal; var endTransaction = function(wasCommitted) { return function() { ac_1.abort(); if (mode === "readwrite") { var affectedSubscribers_1 = /* @__PURE__ */ new Set(); for (var _i = 0, stores_1 = stores; _i < stores_1.length; _i++) { var storeName = stores_1[_i]; var tblCache = cache["idb://".concat(dbName, "/").concat(storeName)]; if (tblCache) { var table = core.table(storeName); var ops = tblCache.optimisticOps.filter(function(op) { return op.trans === idbtrans; }); if (idbtrans._explicit && wasCommitted && idbtrans.mutatedParts) { for (var _a73 = 0, _b = Object.values(tblCache.queries.query); _a73 < _b.length; _a73++) { var entries = _b[_a73]; for (var _c = 0, _d = entries.slice(); _c < _d.length; _c++) { var entry = _d[_c]; if (obsSetsOverlap(entry.obsSet, idbtrans.mutatedParts)) { delArrayItem(entries, entry); entry.subscribers.forEach(function(requery) { return affectedSubscribers_1.add(requery); }); } } } } else if (ops.length > 0) { tblCache.optimisticOps = tblCache.optimisticOps.filter(function(op) { return op.trans !== idbtrans; }); for (var _e2 = 0, _f = Object.values(tblCache.queries.query); _e2 < _f.length; _e2++) { var entries = _f[_e2]; for (var _g = 0, _h = entries.slice(); _g < _h.length; _g++) { var entry = _h[_g]; if (entry.res != null && idbtrans.mutatedParts) { if (wasCommitted && !entry.dirty) { var freezeResults = Object.isFrozen(entry.res); var modRes = applyOptimisticOps(entry.res, entry.req, ops, table, entry, freezeResults); if (entry.dirty) { delArrayItem(entries, entry); entry.subscribers.forEach(function(requery) { return affectedSubscribers_1.add(requery); }); } else if (modRes !== entry.res) { entry.res = modRes; entry.promise = DexiePromise.resolve({ result: modRes }); } } else { if (entry.dirty) { delArrayItem(entries, entry); } entry.subscribers.forEach(function(requery) { return affectedSubscribers_1.add(requery); }); } } } } } } } affectedSubscribers_1.forEach(function(requery) { return requery(); }); } }; }; idbtrans.addEventListener("abort", endTransaction(false), { signal }); idbtrans.addEventListener("error", endTransaction(false), { signal }); idbtrans.addEventListener("complete", endTransaction(true), { signal }); } return idbtrans; }, table: function(tableName) { var downTable = core.table(tableName); var primKey = downTable.schema.primaryKey; var tableMW = __assign(__assign({}, downTable), { mutate: function(req) { var trans = PSD.trans; if (primKey.outbound || trans.db._options.cache === "disabled" || trans.explicit || trans.idbtrans.mode !== "readwrite") { return downTable.mutate(req); } var tblCache = cache["idb://".concat(dbName, "/").concat(tableName)]; if (!tblCache) return downTable.mutate(req); var promise = downTable.mutate(req); if ((req.type === "add" || req.type === "put") && (req.values.length >= 50 || getEffectiveKeys(primKey, req).some(function(key) { return key == null; }))) { promise.then(function(res) { var reqWithResolvedKeys = __assign(__assign({}, req), { values: req.values.map(function(value, i3) { var _a73; if (res.failures[i3]) return value; var valueWithKey = ((_a73 = primKey.keyPath) === null || _a73 === void 0 ? void 0 : _a73.includes(".")) ? deepClone(value) : __assign({}, value); setByKeyPath(valueWithKey, primKey.keyPath, res.results[i3]); return valueWithKey; }) }); var adjustedReq = adjustOptimisticFromFailures(tblCache, reqWithResolvedKeys, res); tblCache.optimisticOps.push(adjustedReq); queueMicrotask(function() { return req.mutatedParts && signalSubscribersLazily(req.mutatedParts); }); }); } else { tblCache.optimisticOps.push(req); req.mutatedParts && signalSubscribersLazily(req.mutatedParts); promise.then(function(res) { if (res.numFailures > 0) { delArrayItem(tblCache.optimisticOps, req); var adjustedReq = adjustOptimisticFromFailures(tblCache, req, res); if (adjustedReq) { tblCache.optimisticOps.push(adjustedReq); } req.mutatedParts && signalSubscribersLazily(req.mutatedParts); } }); promise.catch(function() { delArrayItem(tblCache.optimisticOps, req); req.mutatedParts && signalSubscribersLazily(req.mutatedParts); }); } return promise; }, query: function(req) { var _a73; if (!isCachableContext(PSD, downTable) || !isCachableRequest("query", req)) return downTable.query(req); var freezeResults = ((_a73 = PSD.trans) === null || _a73 === void 0 ? void 0 : _a73.db._options.cache) === "immutable"; var _b = PSD, requery = _b.requery, signal = _b.signal; var _c = findCompatibleQuery(dbName, tableName, "query", req), cacheEntry = _c[0], exactMatch = _c[1], tblCache = _c[2], container = _c[3]; if (cacheEntry && exactMatch) { cacheEntry.obsSet = req.obsSet; } else { var promise = downTable.query(req).then(function(res) { var result = res.result; if (cacheEntry) cacheEntry.res = result; if (freezeResults) { for (var i3 = 0, l3 = result.length; i3 < l3; ++i3) { Object.freeze(result[i3]); } Object.freeze(result); } else { res.result = deepClone(result); } return res; }).catch(function(error) { if (container && cacheEntry) delArrayItem(container, cacheEntry); return Promise.reject(error); }); cacheEntry = { obsSet: req.obsSet, promise, subscribers: /* @__PURE__ */ new Set(), type: "query", req, dirty: false }; if (container) { container.push(cacheEntry); } else { container = [cacheEntry]; if (!tblCache) { tblCache = cache["idb://".concat(dbName, "/").concat(tableName)] = { queries: { query: {}, count: {} }, objs: /* @__PURE__ */ new Map(), optimisticOps: [], unsignaledParts: {} }; } tblCache.queries.query[req.query.index.name || ""] = container; } } subscribeToCacheEntry(cacheEntry, container, requery, signal); return cacheEntry.promise.then(function(res) { return { result: applyOptimisticOps(res.result, req, tblCache === null || tblCache === void 0 ? void 0 : tblCache.optimisticOps, downTable, cacheEntry, freezeResults) }; }); } }); return tableMW; } }); return coreMW; } }; function vipify(target, vipDb) { return new Proxy(target, { get: function(target2, prop, receiver) { if (prop === "db") return vipDb; return Reflect.get(target2, prop, receiver); } }); } var Dexie$1 = (function() { function Dexie3(name, options) { var _this = this; this._middlewares = {}; this.verno = 0; var deps = Dexie3.dependencies; this._options = options = __assign({ addons: Dexie3.addons, autoOpen: true, indexedDB: deps.indexedDB, IDBKeyRange: deps.IDBKeyRange, cache: "cloned" }, options); this._deps = { indexedDB: options.indexedDB, IDBKeyRange: options.IDBKeyRange }; var addons = options.addons; this._dbSchema = {}; this._versions = []; this._storeNames = []; this._allTables = {}; this.idbdb = null; this._novip = this; var state = { dbOpenError: null, isBeingOpened: false, onReadyBeingFired: null, openComplete: false, dbReadyResolve: nop, dbReadyPromise: null, cancelOpen: nop, openCanceller: null, autoSchema: true, PR1398_maxLoop: 3, autoOpen: options.autoOpen }; state.dbReadyPromise = new DexiePromise(function(resolve) { state.dbReadyResolve = resolve; }); state.openCanceller = new DexiePromise(function(_2, reject) { state.cancelOpen = reject; }); this._state = state; this.name = name; this.on = Events(this, "populate", "blocked", "versionchange", "close", { ready: [promisableChain, nop] }); this.once = function(event, callback) { var fn = function() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } _this.on(event).unsubscribe(fn); callback.apply(_this, args); }; return _this.on(event, fn); }; this.on.ready.subscribe = override(this.on.ready.subscribe, function(subscribe) { return function(subscriber, bSticky) { Dexie3.vip(function() { var state2 = _this._state; if (state2.openComplete) { if (!state2.dbOpenError) DexiePromise.resolve().then(subscriber); if (bSticky) subscribe(subscriber); } else if (state2.onReadyBeingFired) { state2.onReadyBeingFired.push(subscriber); if (bSticky) subscribe(subscriber); } else { subscribe(subscriber); var db_1 = _this; if (!bSticky) subscribe(function unsubscribe() { db_1.on.ready.unsubscribe(subscriber); db_1.on.ready.unsubscribe(unsubscribe); }); } }); }; }); this.Collection = createCollectionConstructor(this); this.Table = createTableConstructor(this); this.Transaction = createTransactionConstructor(this); this.Version = createVersionConstructor(this); this.WhereClause = createWhereClauseConstructor(this); this.on("versionchange", function(ev) { if (ev.newVersion > 0) console.warn("Another connection wants to upgrade database '".concat(_this.name, "'. Closing db now to resume the upgrade.")); else console.warn("Another connection wants to delete database '".concat(_this.name, "'. Closing db now to resume the delete request.")); _this.close({ disableAutoOpen: false }); }); this.on("blocked", function(ev) { if (!ev.newVersion || ev.newVersion < ev.oldVersion) console.warn("Dexie.delete('".concat(_this.name, "') was blocked")); else console.warn("Upgrade '".concat(_this.name, "' blocked by other connection holding version ").concat(ev.oldVersion / 10)); }); this._maxKey = getMaxKey(options.IDBKeyRange); this._createTransaction = function(mode, storeNames, dbschema, parentTransaction) { return new _this.Transaction(mode, storeNames, dbschema, _this._options.chromeTransactionDurability, parentTransaction); }; this._fireOnBlocked = function(ev) { _this.on("blocked").fire(ev); connections.filter(function(c) { return c.name === _this.name && c !== _this && !c._state.vcFired; }).map(function(c) { return c.on("versionchange").fire(ev); }); }; this.use(cacheExistingValuesMiddleware); this.use(cacheMiddleware); this.use(observabilityMiddleware); this.use(virtualIndexMiddleware); this.use(hooksMiddleware); var vipDB = new Proxy(this, { get: function(_2, prop, receiver) { if (prop === "_vip") return true; if (prop === "table") return function(tableName) { return vipify(_this.table(tableName), vipDB); }; var rv = Reflect.get(_2, prop, receiver); if (rv instanceof Table) return vipify(rv, vipDB); if (prop === "tables") return rv.map(function(t) { return vipify(t, vipDB); }); if (prop === "_createTransaction") return function() { var tx = rv.apply(this, arguments); return vipify(tx, vipDB); }; return rv; } }); this.vip = vipDB; addons.forEach(function(addon) { return addon(_this); }); } Dexie3.prototype.version = function(versionNumber) { if (isNaN(versionNumber) || versionNumber < 0.1) throw new exceptions.Type("Given version is not a positive number"); versionNumber = Math.round(versionNumber * 10) / 10; if (this.idbdb || this._state.isBeingOpened) throw new exceptions.Schema("Cannot add version when database is open"); this.verno = Math.max(this.verno, versionNumber); var versions = this._versions; var versionInstance = versions.filter(function(v6) { return v6._cfg.version === versionNumber; })[0]; if (versionInstance) return versionInstance; versionInstance = new this.Version(versionNumber); versions.push(versionInstance); versions.sort(lowerVersionFirst); versionInstance.stores({}); this._state.autoSchema = false; return versionInstance; }; Dexie3.prototype._whenReady = function(fn) { var _this = this; return this.idbdb && (this._state.openComplete || PSD.letThrough || this._vip) ? fn() : new DexiePromise(function(resolve, reject) { if (_this._state.openComplete) { return reject(new exceptions.DatabaseClosed(_this._state.dbOpenError)); } if (!_this._state.isBeingOpened) { if (!_this._state.autoOpen) { reject(new exceptions.DatabaseClosed()); return; } _this.open().catch(nop); } _this._state.dbReadyPromise.then(resolve, reject); }).then(fn); }; Dexie3.prototype.use = function(_a73) { var stack = _a73.stack, create = _a73.create, level = _a73.level, name = _a73.name; if (name) this.unuse({ stack, name }); var middlewares = this._middlewares[stack] || (this._middlewares[stack] = []); middlewares.push({ stack, create, level: level == null ? 10 : level, name }); middlewares.sort(function(a, b) { return a.level - b.level; }); return this; }; Dexie3.prototype.unuse = function(_a73) { var stack = _a73.stack, name = _a73.name, create = _a73.create; if (stack && this._middlewares[stack]) { this._middlewares[stack] = this._middlewares[stack].filter(function(mw) { return create ? mw.create !== create : name ? mw.name !== name : false; }); } return this; }; Dexie3.prototype.open = function() { var _this = this; return usePSD( globalPSD, function() { return dexieOpen(_this); } ); }; Dexie3.prototype._close = function() { this.on.close.fire(new CustomEvent("close")); var state = this._state; var idx = connections.indexOf(this); if (idx >= 0) connections.splice(idx, 1); if (this.idbdb) { try { this.idbdb.close(); } catch (e2) { } this.idbdb = null; } if (!state.isBeingOpened) { state.dbReadyPromise = new DexiePromise(function(resolve) { state.dbReadyResolve = resolve; }); state.openCanceller = new DexiePromise(function(_2, reject) { state.cancelOpen = reject; }); } }; Dexie3.prototype.close = function(_a73) { var _b = _a73 === void 0 ? { disableAutoOpen: true } : _a73, disableAutoOpen = _b.disableAutoOpen; var state = this._state; if (disableAutoOpen) { if (state.isBeingOpened) { state.cancelOpen(new exceptions.DatabaseClosed()); } this._close(); state.autoOpen = false; state.dbOpenError = new exceptions.DatabaseClosed(); } else { this._close(); state.autoOpen = this._options.autoOpen || state.isBeingOpened; state.openComplete = false; state.dbOpenError = null; } }; Dexie3.prototype.delete = function(closeOptions) { var _this = this; if (closeOptions === void 0) { closeOptions = { disableAutoOpen: true }; } var hasInvalidArguments = arguments.length > 0 && typeof arguments[0] !== "object"; var state = this._state; return new DexiePromise(function(resolve, reject) { var doDelete = function() { _this.close(closeOptions); var req = _this._deps.indexedDB.deleteDatabase(_this.name); req.onsuccess = wrap(function() { _onDatabaseDeleted(_this._deps, _this.name); resolve(); }); req.onerror = eventRejectHandler(reject); req.onblocked = _this._fireOnBlocked; }; if (hasInvalidArguments) throw new exceptions.InvalidArgument("Invalid closeOptions argument to db.delete()"); if (state.isBeingOpened) { state.dbReadyPromise.then(doDelete); } else { doDelete(); } }); }; Dexie3.prototype.backendDB = function() { return this.idbdb; }; Dexie3.prototype.isOpen = function() { return this.idbdb !== null; }; Dexie3.prototype.hasBeenClosed = function() { var dbOpenError = this._state.dbOpenError; return dbOpenError && dbOpenError.name === "DatabaseClosed"; }; Dexie3.prototype.hasFailed = function() { return this._state.dbOpenError !== null; }; Dexie3.prototype.dynamicallyOpened = function() { return this._state.autoSchema; }; Object.defineProperty(Dexie3.prototype, "tables", { get: function() { var _this = this; return keys(this._allTables).map(function(name) { return _this._allTables[name]; }); }, enumerable: false, configurable: true }); Dexie3.prototype.transaction = function() { var args = extractTransactionArgs.apply(this, arguments); return this._transaction.apply(this, args); }; Dexie3.prototype._transaction = function(mode, tables, scopeFunc) { var _this = this; var parentTransaction = PSD.trans; if (!parentTransaction || parentTransaction.db !== this || mode.indexOf("!") !== -1) parentTransaction = null; var onlyIfCompatible = mode.indexOf("?") !== -1; mode = mode.replace("!", "").replace("?", ""); var idbMode, storeNames; try { storeNames = tables.map(function(table) { var storeName = table instanceof _this.Table ? table.name : table; if (typeof storeName !== "string") throw new TypeError("Invalid table argument to Dexie.transaction(). Only Table or String are allowed"); return storeName; }); if (mode == "r" || mode === READONLY) idbMode = READONLY; else if (mode == "rw" || mode == READWRITE) idbMode = READWRITE; else throw new exceptions.InvalidArgument("Invalid transaction mode: " + mode); if (parentTransaction) { if (parentTransaction.mode === READONLY && idbMode === READWRITE) { if (onlyIfCompatible) { parentTransaction = null; } else throw new exceptions.SubTransaction("Cannot enter a sub-transaction with READWRITE mode when parent transaction is READONLY"); } if (parentTransaction) { storeNames.forEach(function(storeName) { if (parentTransaction && parentTransaction.storeNames.indexOf(storeName) === -1) { if (onlyIfCompatible) { parentTransaction = null; } else throw new exceptions.SubTransaction("Table " + storeName + " not included in parent transaction."); } }); } if (onlyIfCompatible && parentTransaction && !parentTransaction.active) { parentTransaction = null; } } } catch (e2) { return parentTransaction ? parentTransaction._promise(null, function(_2, reject) { reject(e2); }) : rejection(e2); } var enterTransaction = enterTransactionScope.bind(null, this, idbMode, storeNames, parentTransaction, scopeFunc); return parentTransaction ? parentTransaction._promise(idbMode, enterTransaction, "lock") : PSD.trans ? usePSD(PSD.transless, function() { return _this._whenReady(enterTransaction); }) : this._whenReady(enterTransaction); }; Dexie3.prototype.table = function(tableName) { if (!hasOwn(this._allTables, tableName)) { throw new exceptions.InvalidTable("Table ".concat(tableName, " does not exist")); } return this._allTables[tableName]; }; return Dexie3; })(); var symbolObservable = typeof Symbol !== "undefined" && "observable" in Symbol ? Symbol.observable : "@@observable"; var Observable = (function() { function Observable2(subscribe) { this._subscribe = subscribe; } Observable2.prototype.subscribe = function(x2, error, complete) { return this._subscribe(!x2 || typeof x2 === "function" ? { next: x2, error, complete } : x2); }; Observable2.prototype[symbolObservable] = function() { return this; }; return Observable2; })(); var domDeps; try { domDeps = { indexedDB: _global.indexedDB || _global.mozIndexedDB || _global.webkitIndexedDB || _global.msIndexedDB, IDBKeyRange: _global.IDBKeyRange || _global.webkitIDBKeyRange }; } catch (e2) { domDeps = { indexedDB: null, IDBKeyRange: null }; } function liveQuery2(querier) { var hasValue = false; var currentValue; var observable = new Observable(function(observer) { var scopeFuncIsAsync = isAsyncFunction(querier); function execute(ctx) { var wasRootExec = beginMicroTickScope(); try { if (scopeFuncIsAsync) { incrementExpectedAwaits(); } var rv = newScope(querier, ctx); if (scopeFuncIsAsync) { rv = rv.finally(decrementExpectedAwaits); } return rv; } finally { wasRootExec && endMicroTickScope(); } } var closed = false; var abortController; var accumMuts = {}; var currentObs = {}; var subscription = { get closed() { return closed; }, unsubscribe: function() { if (closed) return; closed = true; if (abortController) abortController.abort(); if (startedListening) globalEvents.storagemutated.unsubscribe(mutationListener); } }; observer.start && observer.start(subscription); var startedListening = false; var doQuery = function() { return execInGlobalContext(_doQuery); }; function shouldNotify() { return obsSetsOverlap(currentObs, accumMuts); } var mutationListener = function(parts) { extendObservabilitySet(accumMuts, parts); if (shouldNotify()) { doQuery(); } }; var _doQuery = function() { if (closed || !domDeps.indexedDB) { return; } accumMuts = {}; var subscr = {}; if (abortController) abortController.abort(); abortController = new AbortController(); var ctx = { subscr, signal: abortController.signal, requery: doQuery, querier, trans: null }; var ret = execute(ctx); Promise.resolve(ret).then(function(result) { hasValue = true; currentValue = result; if (closed || ctx.signal.aborted) { return; } accumMuts = {}; currentObs = subscr; if (!objectIsEmpty(currentObs) && !startedListening) { globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, mutationListener); startedListening = true; } execInGlobalContext(function() { return !closed && observer.next && observer.next(result); }); }, function(err) { hasValue = false; if (!["DatabaseClosedError", "AbortError"].includes(err === null || err === void 0 ? void 0 : err.name)) { if (!closed) execInGlobalContext(function() { if (closed) return; observer.error && observer.error(err); }); } }); }; setTimeout(doQuery, 0); return subscription; }); observable.hasValue = function() { return hasValue; }; observable.getValue = function() { return currentValue; }; return observable; } var Dexie2 = Dexie$1; props(Dexie2, __assign(__assign({}, fullNameExceptions), { delete: function(databaseName) { var db3 = new Dexie2(databaseName, { addons: [] }); return db3.delete(); }, exists: function(name) { return new Dexie2(name, { addons: [] }).open().then(function(db3) { db3.close(); return true; }).catch("NoSuchDatabaseError", function() { return false; }); }, getDatabaseNames: function(cb) { try { return getDatabaseNames(Dexie2.dependencies).then(cb); } catch (_a73) { return rejection(new exceptions.MissingAPI()); } }, defineClass: function() { function Class(content) { extend(this, content); } return Class; }, ignoreTransaction: function(scopeFunc) { return PSD.trans ? usePSD(PSD.transless, scopeFunc) : scopeFunc(); }, vip, async: function(generatorFn) { return function() { try { var rv = awaitIterator(generatorFn.apply(this, arguments)); if (!rv || typeof rv.then !== "function") return DexiePromise.resolve(rv); return rv; } catch (e2) { return rejection(e2); } }; }, spawn: function(generatorFn, args, thiz) { try { var rv = awaitIterator(generatorFn.apply(thiz, args || [])); if (!rv || typeof rv.then !== "function") return DexiePromise.resolve(rv); return rv; } catch (e2) { return rejection(e2); } }, currentTransaction: { get: function() { return PSD.trans || null; } }, waitFor: function(promiseOrFunction, optionalTimeout) { var promise = DexiePromise.resolve(typeof promiseOrFunction === "function" ? Dexie2.ignoreTransaction(promiseOrFunction) : promiseOrFunction).timeout(optionalTimeout || 6e4); return PSD.trans ? PSD.trans.waitFor(promise) : promise; }, Promise: DexiePromise, debug: { get: function() { return debug15; }, set: function(value) { setDebug(value); } }, derive, extend, props, override, Events, on: globalEvents, liveQuery: liveQuery2, extendObservabilitySet, getByKeyPath, setByKeyPath, delByKeyPath, shallowClone, deepClone, getObjectDiff, cmp: cmp2, asap: asap$1, minKey, addons: [], connections, errnames, dependencies: domDeps, cache, semVer: DEXIE_VERSION, version: DEXIE_VERSION.split(".").map(function(n) { return parseInt(n); }).reduce(function(p5, c, i3) { return p5 + c / Math.pow(10, i3 * 2); }) })); Dexie2.maxKey = getMaxKey(Dexie2.dependencies.IDBKeyRange); if (typeof dispatchEvent !== "undefined" && typeof addEventListener !== "undefined") { globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, function(updatedParts) { if (!propagatingLocally) { var event_1; event_1 = new CustomEvent(STORAGE_MUTATED_DOM_EVENT_NAME, { detail: updatedParts }); propagatingLocally = true; dispatchEvent(event_1); propagatingLocally = false; } }); addEventListener(STORAGE_MUTATED_DOM_EVENT_NAME, function(_a73) { var detail = _a73.detail; if (!propagatingLocally) { propagateLocally(detail); } }); } function propagateLocally(updateParts) { var wasMe = propagatingLocally; try { propagatingLocally = true; globalEvents.storagemutated.fire(updateParts); signalSubscribersNow(updateParts, true); } finally { propagatingLocally = wasMe; } } var propagatingLocally = false; var bc; var createBC = function() { }; if (typeof BroadcastChannel !== "undefined") { createBC = function() { bc = new BroadcastChannel(STORAGE_MUTATED_DOM_EVENT_NAME); bc.onmessage = function(ev) { return ev.data && propagateLocally(ev.data); }; }; createBC(); if (typeof bc.unref === "function") { bc.unref(); } globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, function(changedParts) { if (!propagatingLocally) { bc.postMessage(changedParts); } }); } if (typeof addEventListener !== "undefined") { addEventListener("pagehide", function(event) { if (!Dexie$1.disableBfCache && event.persisted) { if (debug15) console.debug("Dexie: handling persisted pagehide"); bc === null || bc === void 0 ? void 0 : bc.close(); for (var _i = 0, connections_1 = connections; _i < connections_1.length; _i++) { var db3 = connections_1[_i]; db3.close({ disableAutoOpen: false }); } } }); addEventListener("pageshow", function(event) { if (!Dexie$1.disableBfCache && event.persisted) { if (debug15) console.debug("Dexie: handling persisted pageshow"); createBC(); propagateLocally({ all: new RangeSet2(-Infinity, [[]]) }); } }); } function add3(value) { return new PropModification2({ add: value }); } function remove2(value) { return new PropModification2({ remove: value }); } function replacePrefix2(a, b) { return new PropModification2({ replacePrefix: [a, b] }); } DexiePromise.rejectionMapper = mapError; setDebug(debug15); var namedExports = /* @__PURE__ */ Object.freeze({ __proto__: null, Dexie: Dexie$1, liveQuery: liveQuery2, Entity: Entity2, cmp: cmp2, PropModification: PropModification2, replacePrefix: replacePrefix2, add: add3, remove: remove2, "default": Dexie$1, RangeSet: RangeSet2, mergeRanges: mergeRanges2, rangesOverlap: rangesOverlap2 }); __assign(Dexie$1, namedExports, { default: Dexie$1 }); return Dexie$1; })); } }); // ndk/cache-sqlite-wasm/src/binary/decoder.ts var decoder_exports = {}; __export(decoder_exports, { decodeEvents: () => decodeEvents2, decodeSingleEvent: () => decodeSingleEvent2, looksLikeBinaryFormat: () => looksLikeBinaryFormat2 }); function bytesToHex6(bytes4) { let hex2 = ""; for (let i3 = 0; i3 < bytes4.length; i3++) { const byte = bytes4[i3]; hex2 += HEX_CHARS2[byte >> 4] + HEX_CHARS2[byte & 15]; } return hex2; } function decodeString2(bytes4) { return textDecoder2.decode(bytes4); } function decodeEvent2(buffer, offset) { const view = new DataView(buffer); const uint8 = new Uint8Array(buffer); let pos = offset; if (pos + 4 > buffer.byteLength) { throw new Error(`Buffer overflow: trying to read event size at offset ${pos}, buffer length is ${buffer.byteLength}`); } const eventSize = view.getUint32(pos, true); pos += 4; const eventEndPos = offset + eventSize; if (eventEndPos > buffer.byteLength) { throw new Error(`Invalid event size: event claims to be ${eventSize} bytes but only ${buffer.byteLength - offset} bytes available`); } const idBytes = uint8.slice(pos, pos + 32); const id = bytesToHex6(idBytes); pos += 32; const pubkeyBytes = uint8.slice(pos, pos + 32); const pubkey = bytesToHex6(pubkeyBytes); pos += 32; const created_at = view.getUint32(pos, true); pos += 4; const kind = view.getUint16(pos, true); pos += 2; const sigBytes = uint8.slice(pos, pos + 64); const sig = bytesToHex6(sigBytes); pos += 64; const contentLength = view.getUint32(pos, true); pos += 4; const contentBytes = uint8.slice(pos, pos + contentLength); const content = decodeString2(contentBytes); pos += contentLength; const tagsCount = view.getUint16(pos, true); pos += 2; const tags = []; for (let i3 = 0; i3 < tagsCount; i3++) { const tagItemsCount = view.getUint8(pos); pos += 1; const tag = []; for (let j2 = 0; j2 < tagItemsCount; j2++) { const itemLength = view.getUint16(pos, true); pos += 2; const itemBytes = uint8.slice(pos, pos + itemLength); const item = decodeString2(itemBytes); tag.push(item); pos += itemLength; } tags.push(tag); } let relay_url = null; const hasRelayUrl = view.getUint8(pos); pos += 1; if (hasRelayUrl === 1) { const relayLength = view.getUint16(pos, true); pos += 2; const relayBytes = uint8.slice(pos, pos + relayLength); relay_url = decodeString2(relayBytes); pos += relayLength; } const event = { id, pubkey, created_at, kind, sig, content, tags, relay_url }; return { event, nextOffset: eventEndPos }; } function decodeEvents2(buffer) { if (buffer.byteLength === 0) { return []; } if (buffer.byteLength < 9) { throw new Error(`Buffer too small: ${buffer.byteLength} bytes. Need at least 9 bytes for header.`); } const view = new DataView(buffer); let offset = 0; const magic = view.getUint32(offset, true); if (magic !== MAGIC_NUMBER2) { throw new Error(`Invalid magic number. Expected ${MAGIC_NUMBER2.toString(16)}, got ${magic.toString(16)}`); } offset += 4; const version = view.getUint8(offset); if (version !== SUPPORTED_VERSION2) { throw new Error(`Unsupported version ${version}. Only version ${SUPPORTED_VERSION2} is supported`); } offset += 1; const eventCount = view.getUint32(offset, true); offset += 4; const events = []; for (let i3 = 0; i3 < eventCount; i3++) { const { event, nextOffset } = decodeEvent2(buffer, offset); events.push(event); offset = nextOffset; } return events; } function decodeSingleEvent2(buffer) { const events = decodeEvents2(buffer); if (events.length !== 1) { throw new Error(`Expected 1 event, got ${events.length}`); } return events[0]; } function looksLikeBinaryFormat2(buffer) { if (buffer.byteLength < 9) return false; const view = new DataView(buffer); const magic = view.getUint32(0, true); return magic === MAGIC_NUMBER2; } var MAGIC_NUMBER2, SUPPORTED_VERSION2, textDecoder2, HEX_CHARS2; var init_decoder2 = __esm({ "ndk/cache-sqlite-wasm/src/binary/decoder.ts"() { "use strict"; MAGIC_NUMBER2 = 1313821524; SUPPORTED_VERSION2 = 1; textDecoder2 = new TextDecoder(); HEX_CHARS2 = "0123456789abcdef"; } }); // ndk/node_modules/@noble/curves/esm/abstract/utils.js var bytesToHex7, hexToBytes5; var init_utils3 = __esm({ "ndk/node_modules/@noble/curves/esm/abstract/utils.js"() { init_utils2(); bytesToHex7 = bytesToHex; hexToBytes5 = hexToBytes; } }); // ndk/node_modules/@cashu/cashu-ts/lib/utils-CrQNeCaC.js function pr(s) { var c = s.length; if (c % 4 > 0) throw new Error("Invalid string. Length must be a multiple of 4"); var f = s.indexOf("="); f === -1 && (f = c); var w2 = f === c ? 0 : 4 - f % 4; return [f, w2]; } function Dr(s) { var c = pr(s), f = c[0], w2 = c[1]; return (f + w2) * 3 / 4 - w2; } function $r(s, c, f) { return (c + f) * 3 / 4 - f; } function Pr(s) { var c, f = pr(s), w2 = f[0], l3 = f[1], p5 = new br($r(s, w2, l3)), a = 0, m = l3 > 0 ? w2 - 4 : w2, B; for (B = 0; B < m; B += 4) c = R[s.charCodeAt(B)] << 18 | R[s.charCodeAt(B + 1)] << 12 | R[s.charCodeAt(B + 2)] << 6 | R[s.charCodeAt(B + 3)], p5[a++] = c >> 16 & 255, p5[a++] = c >> 8 & 255, p5[a++] = c & 255; return l3 === 2 && (c = R[s.charCodeAt(B)] << 2 | R[s.charCodeAt(B + 1)] >> 4, p5[a++] = c & 255), l3 === 1 && (c = R[s.charCodeAt(B)] << 10 | R[s.charCodeAt(B + 1)] << 4 | R[s.charCodeAt(B + 2)] >> 2, p5[a++] = c >> 8 & 255, p5[a++] = c & 255), p5; } function Or(s) { return S[s >> 18 & 63] + S[s >> 12 & 63] + S[s >> 6 & 63] + S[s & 63]; } function Gr(s, c, f) { for (var w2, l3 = [], p5 = c; p5 < f; p5 += 3) w2 = (s[p5] << 16 & 16711680) + (s[p5 + 1] << 8 & 65280) + (s[p5 + 2] & 255), l3.push(Or(w2)); return l3.join(""); } function Wr(s) { for (var c, f = s.length, w2 = f % 3, l3 = [], p5 = 16383, a = 0, m = f - w2; a < m; a += p5) l3.push(Gr(s, a, a + p5 > m ? m : a + p5)); return w2 === 1 ? (c = s[f - 1], l3.push( S[c >> 2] + S[c << 4 & 63] + "==" )) : w2 === 2 && (c = (s[f - 2] << 8) + s[f - 1], l3.push( S[c >> 10] + S[c >> 4 & 63] + S[c << 2 & 63] + "=" )), l3.join(""); } function Vr(s) { return Hr(bytesToHex7(s)); } function Hr(s) { return BigInt(`0x${s}`); } function Xr(s) { return Yr.fromBase64(s); } var fr, G, S, R, br, z, D3, kr, J, O, Yr; var init_utils_CrQNeCaC = __esm({ "ndk/node_modules/@cashu/cashu-ts/lib/utils-CrQNeCaC.js"() { init_utils3(); fr = {}; G = {}; G.byteLength = Dr; G.toByteArray = Pr; G.fromByteArray = Wr; S = []; R = []; br = typeof Uint8Array < "u" ? Uint8Array : Array; z = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; for (D3 = 0, kr = z.length; D3 < kr; ++D3) S[D3] = z[D3], R[z.charCodeAt(D3)] = D3; R[45] = 62; R[95] = 63; J = {}; J.read = function(s, c, f, w2, l3) { var p5, a, m = l3 * 8 - w2 - 1, B = (1 << m) - 1, F2 = B >> 1, o = -7, A = f ? l3 - 1 : 0, _2 = f ? -1 : 1, T4 = s[c + A]; for (A += _2, p5 = T4 & (1 << -o) - 1, T4 >>= -o, o += m; o > 0; p5 = p5 * 256 + s[c + A], A += _2, o -= 8) ; for (a = p5 & (1 << -o) - 1, p5 >>= -o, o += w2; o > 0; a = a * 256 + s[c + A], A += _2, o -= 8) ; if (p5 === 0) p5 = 1 - F2; else { if (p5 === B) return a ? NaN : (T4 ? -1 : 1) * (1 / 0); a = a + Math.pow(2, w2), p5 = p5 - F2; } return (T4 ? -1 : 1) * a * Math.pow(2, p5 - w2); }; J.write = function(s, c, f, w2, l3, p5) { var a, m, B, F2 = p5 * 8 - l3 - 1, o = (1 << F2) - 1, A = o >> 1, _2 = l3 === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0, T4 = w2 ? 0 : p5 - 1, N = w2 ? 1 : -1, W3 = c < 0 || c === 0 && 1 / c < 0 ? 1 : 0; for (c = Math.abs(c), isNaN(c) || c === 1 / 0 ? (m = isNaN(c) ? 1 : 0, a = o) : (a = Math.floor(Math.log(c) / Math.LN2), c * (B = Math.pow(2, -a)) < 1 && (a--, B *= 2), a + A >= 1 ? c += _2 / B : c += _2 * Math.pow(2, 1 - A), c * B >= 2 && (a++, B /= 2), a + A >= o ? (m = 0, a = o) : a + A >= 1 ? (m = (c * B - 1) * Math.pow(2, l3), a = a + A) : (m = c * Math.pow(2, A - 1) * Math.pow(2, l3), a = 0)); l3 >= 8; s[f + T4] = m & 255, T4 += N, m /= 256, l3 -= 8) ; for (a = a << l3 | m, F2 += l3; F2 > 0; s[f + T4] = a & 255, T4 += N, a /= 256, F2 -= 8) ; s[f + T4 - N] |= W3 * 128; }; (function(s) { const c = G, f = J, w2 = typeof Symbol == "function" && typeof Symbol.for == "function" ? /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom") : null; s.Buffer = o, s.SlowBuffer = ar, s.INSPECT_MAX_BYTES = 50; const l3 = 2147483647; s.kMaxLength = l3; const { Uint8Array: p5, ArrayBuffer: a, SharedArrayBuffer: m } = globalThis; o.TYPED_ARRAY_SUPPORT = B(), !o.TYPED_ARRAY_SUPPORT && typeof console < "u" && typeof console.error == "function" && console.error( "This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support." ); function B() { try { const n = new p5(1), r = { foo: function() { return 42; } }; return Object.setPrototypeOf(r, p5.prototype), Object.setPrototypeOf(n, r), n.foo() === 42; } catch { return false; } } Object.defineProperty(o.prototype, "parent", { enumerable: true, get: function() { if (o.isBuffer(this)) return this.buffer; } }), Object.defineProperty(o.prototype, "offset", { enumerable: true, get: function() { if (o.isBuffer(this)) return this.byteOffset; } }); function F2(n) { if (n > l3) throw new RangeError('The value "' + n + '" is invalid for option "size"'); const r = new p5(n); return Object.setPrototypeOf(r, o.prototype), r; } function o(n, r, t) { if (typeof n == "number") { if (typeof r == "string") throw new TypeError( 'The "string" argument must be of type string. Received type number' ); return N(n); } return A(n, r, t); } o.poolSize = 8192; function A(n, r, t) { if (typeof n == "string") return W3(n, r); if (a.isView(n)) return sr(n); if (n == null) throw new TypeError( "The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type " + typeof n ); if (C2(n, a) || n && C2(n.buffer, a) || typeof m < "u" && (C2(n, m) || n && C2(n.buffer, m))) return H2(n, r, t); if (typeof n == "number") throw new TypeError( 'The "value" argument must not be of type number. Received type number' ); const i3 = n.valueOf && n.valueOf(); if (i3 != null && i3 !== n) return o.from(i3, r, t); const e2 = lr(n); if (e2) return e2; if (typeof Symbol < "u" && Symbol.toPrimitive != null && typeof n[Symbol.toPrimitive] == "function") return o.from(n[Symbol.toPrimitive]("string"), r, t); throw new TypeError( "The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type " + typeof n ); } o.from = function(n, r, t) { return A(n, r, t); }, Object.setPrototypeOf(o.prototype, p5.prototype), Object.setPrototypeOf(o, p5); function _2(n) { if (typeof n != "number") throw new TypeError('"size" argument must be of type number'); if (n < 0) throw new RangeError('The value "' + n + '" is invalid for option "size"'); } function T4(n, r, t) { return _2(n), n <= 0 ? F2(n) : r !== void 0 ? typeof t == "string" ? F2(n).fill(r, t) : F2(n).fill(r) : F2(n); } o.alloc = function(n, r, t) { return T4(n, r, t); }; function N(n) { return _2(n), F2(n < 0 ? 0 : j2(n) | 0); } o.allocUnsafe = function(n) { return N(n); }, o.allocUnsafeSlow = function(n) { return N(n); }; function W3(n, r) { if ((typeof r != "string" || r === "") && (r = "utf8"), !o.isEncoding(r)) throw new TypeError("Unknown encoding: " + r); const t = K3(n, r) | 0; let i3 = F2(t); const e2 = i3.write(n, r); return e2 !== t && (i3 = i3.slice(0, e2)), i3; } function Y(n) { const r = n.length < 0 ? 0 : j2(n.length) | 0, t = F2(r); for (let i3 = 0; i3 < r; i3 += 1) t[i3] = n[i3] & 255; return t; } function sr(n) { if (C2(n, p5)) { const r = new p5(n); return H2(r.buffer, r.byteOffset, r.byteLength); } return Y(n); } function H2(n, r, t) { if (r < 0 || n.byteLength < r) throw new RangeError('"offset" is outside of buffer bounds'); if (n.byteLength < r + (t || 0)) throw new RangeError('"length" is outside of buffer bounds'); let i3; return r === void 0 && t === void 0 ? i3 = new p5(n) : t === void 0 ? i3 = new p5(n, r) : i3 = new p5(n, r, t), Object.setPrototypeOf(i3, o.prototype), i3; } function lr(n) { if (o.isBuffer(n)) { const r = j2(n.length) | 0, t = F2(r); return t.length === 0 || n.copy(t, 0, 0, r), t; } if (n.length !== void 0) return typeof n.length != "number" || X2(n.length) ? F2(0) : Y(n); if (n.type === "Buffer" && Array.isArray(n.data)) return Y(n.data); } function j2(n) { if (n >= l3) throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x" + l3.toString(16) + " bytes"); return n | 0; } function ar(n) { return +n != n && (n = 0), o.alloc(+n); } o.isBuffer = function(r) { return r != null && r._isBuffer === true && r !== o.prototype; }, o.compare = function(r, t) { if (C2(r, p5) && (r = o.from(r, r.offset, r.byteLength)), C2(t, p5) && (t = o.from(t, t.offset, t.byteLength)), !o.isBuffer(r) || !o.isBuffer(t)) throw new TypeError( 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' ); if (r === t) return 0; let i3 = r.length, e2 = t.length; for (let u3 = 0, h2 = Math.min(i3, e2); u3 < h2; ++u3) if (r[u3] !== t[u3]) { i3 = r[u3], e2 = t[u3]; break; } return i3 < e2 ? -1 : e2 < i3 ? 1 : 0; }, o.isEncoding = function(r) { switch (String(r).toLowerCase()) { case "hex": case "utf8": case "utf-8": case "ascii": case "latin1": case "binary": case "base64": case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return true; default: return false; } }, o.concat = function(r, t) { if (!Array.isArray(r)) throw new TypeError('"list" argument must be an Array of Buffers'); if (r.length === 0) return o.alloc(0); let i3; if (t === void 0) for (t = 0, i3 = 0; i3 < r.length; ++i3) t += r[i3].length; const e2 = o.allocUnsafe(t); let u3 = 0; for (i3 = 0; i3 < r.length; ++i3) { let h2 = r[i3]; if (C2(h2, p5)) u3 + h2.length > e2.length ? (o.isBuffer(h2) || (h2 = o.from(h2)), h2.copy(e2, u3)) : p5.prototype.set.call( e2, h2, u3 ); else if (o.isBuffer(h2)) h2.copy(e2, u3); else throw new TypeError('"list" argument must be an Array of Buffers'); u3 += h2.length; } return e2; }; function K3(n, r) { if (o.isBuffer(n)) return n.length; if (a.isView(n) || C2(n, a)) return n.byteLength; if (typeof n != "string") throw new TypeError( 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type ' + typeof n ); const t = n.length, i3 = arguments.length > 2 && arguments[2] === true; if (!i3 && t === 0) return 0; let e2 = false; for (; ; ) switch (r) { case "ascii": case "latin1": case "binary": return t; case "utf8": case "utf-8": return V2(n).length; case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return t * 2; case "hex": return t >>> 1; case "base64": return cr(n).length; default: if (e2) return i3 ? -1 : V2(n).length; r = ("" + r).toLowerCase(), e2 = true; } } o.byteLength = K3; function wr(n, r, t) { let i3 = false; if ((r === void 0 || r < 0) && (r = 0), r > this.length || ((t === void 0 || t > this.length) && (t = this.length), t <= 0) || (t >>>= 0, r >>>= 0, t <= r)) return ""; for (n || (n = "utf8"); ; ) switch (n) { case "hex": return Ar(this, r, t); case "utf8": case "utf-8": return v6(this, r, t); case "ascii": return Ir(this, r, t); case "latin1": case "binary": return Fr(this, r, t); case "base64": return dr(this, r, t); case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return Ur(this, r, t); default: if (i3) throw new TypeError("Unknown encoding: " + n); n = (n + "").toLowerCase(), i3 = true; } } o.prototype._isBuffer = true; function M2(n, r, t) { const i3 = n[r]; n[r] = n[t], n[t] = i3; } o.prototype.swap16 = function() { const r = this.length; if (r % 2 !== 0) throw new RangeError("Buffer size must be a multiple of 16-bits"); for (let t = 0; t < r; t += 2) M2(this, t, t + 1); return this; }, o.prototype.swap32 = function() { const r = this.length; if (r % 4 !== 0) throw new RangeError("Buffer size must be a multiple of 32-bits"); for (let t = 0; t < r; t += 4) M2(this, t, t + 3), M2(this, t + 1, t + 2); return this; }, o.prototype.swap64 = function() { const r = this.length; if (r % 8 !== 0) throw new RangeError("Buffer size must be a multiple of 64-bits"); for (let t = 0; t < r; t += 8) M2(this, t, t + 7), M2(this, t + 1, t + 6), M2(this, t + 2, t + 5), M2(this, t + 3, t + 4); return this; }, o.prototype.toString = function() { const r = this.length; return r === 0 ? "" : arguments.length === 0 ? v6(this, 0, r) : wr.apply(this, arguments); }, o.prototype.toLocaleString = o.prototype.toString, o.prototype.equals = function(r) { if (!o.isBuffer(r)) throw new TypeError("Argument must be a Buffer"); return this === r ? true : o.compare(this, r) === 0; }, o.prototype.inspect = function() { let r = ""; const t = s.INSPECT_MAX_BYTES; return r = this.toString("hex", 0, t).replace(/(.{2})/g, "$1 ").trim(), this.length > t && (r += " ... "), ""; }, w2 && (o.prototype[w2] = o.prototype.inspect), o.prototype.compare = function(r, t, i3, e2, u3) { if (C2(r, p5) && (r = o.from(r, r.offset, r.byteLength)), !o.isBuffer(r)) throw new TypeError( 'The "target" argument must be one of type Buffer or Uint8Array. Received type ' + typeof r ); if (t === void 0 && (t = 0), i3 === void 0 && (i3 = r ? r.length : 0), e2 === void 0 && (e2 = 0), u3 === void 0 && (u3 = this.length), t < 0 || i3 > r.length || e2 < 0 || u3 > this.length) throw new RangeError("out of range index"); if (e2 >= u3 && t >= i3) return 0; if (e2 >= u3) return -1; if (t >= i3) return 1; if (t >>>= 0, i3 >>>= 0, e2 >>>= 0, u3 >>>= 0, this === r) return 0; let h2 = u3 - e2, y2 = i3 - t; const E2 = Math.min(h2, y2), g = this.slice(e2, u3), d17 = r.slice(t, i3); for (let x2 = 0; x2 < E2; ++x2) if (g[x2] !== d17[x2]) { h2 = g[x2], y2 = d17[x2]; break; } return h2 < y2 ? -1 : y2 < h2 ? 1 : 0; }; function Z(n, r, t, i3, e2) { if (n.length === 0) return -1; if (typeof t == "string" ? (i3 = t, t = 0) : t > 2147483647 ? t = 2147483647 : t < -2147483648 && (t = -2147483648), t = +t, X2(t) && (t = e2 ? 0 : n.length - 1), t < 0 && (t = n.length + t), t >= n.length) { if (e2) return -1; t = n.length - 1; } else if (t < 0) if (e2) t = 0; else return -1; if (typeof r == "string" && (r = o.from(r, i3)), o.isBuffer(r)) return r.length === 0 ? -1 : Q2(n, r, t, i3, e2); if (typeof r == "number") return r = r & 255, typeof p5.prototype.indexOf == "function" ? e2 ? p5.prototype.indexOf.call(n, r, t) : p5.prototype.lastIndexOf.call(n, r, t) : Q2(n, [r], t, i3, e2); throw new TypeError("val must be string, number or Buffer"); } function Q2(n, r, t, i3, e2) { let u3 = 1, h2 = n.length, y2 = r.length; if (i3 !== void 0 && (i3 = String(i3).toLowerCase(), i3 === "ucs2" || i3 === "ucs-2" || i3 === "utf16le" || i3 === "utf-16le")) { if (n.length < 2 || r.length < 2) return -1; u3 = 2, h2 /= 2, y2 /= 2, t /= 2; } function E2(d17, x2) { return u3 === 1 ? d17[x2] : d17.readUInt16BE(x2 * u3); } let g; if (e2) { let d17 = -1; for (g = t; g < h2; g++) if (E2(n, g) === E2(r, d17 === -1 ? 0 : g - d17)) { if (d17 === -1 && (d17 = g), g - d17 + 1 === y2) return d17 * u3; } else d17 !== -1 && (g -= g - d17), d17 = -1; } else for (t + y2 > h2 && (t = h2 - y2), g = t; g >= 0; g--) { let d17 = true; for (let x2 = 0; x2 < y2; x2++) if (E2(n, g + x2) !== E2(r, x2)) { d17 = false; break; } if (d17) return g; } return -1; } o.prototype.includes = function(r, t, i3) { return this.indexOf(r, t, i3) !== -1; }, o.prototype.indexOf = function(r, t, i3) { return Z(this, r, t, i3, true); }, o.prototype.lastIndexOf = function(r, t, i3) { return Z(this, r, t, i3, false); }; function yr(n, r, t, i3) { t = Number(t) || 0; const e2 = n.length - t; i3 ? (i3 = Number(i3), i3 > e2 && (i3 = e2)) : i3 = e2; const u3 = r.length; i3 > u3 / 2 && (i3 = u3 / 2); let h2; for (h2 = 0; h2 < i3; ++h2) { const y2 = parseInt(r.substr(h2 * 2, 2), 16); if (X2(y2)) return h2; n[t + h2] = y2; } return h2; } function xr(n, r, t, i3) { return P(V2(r, n.length - t), n, t, i3); } function Br(n, r, t, i3) { return P(Sr(r), n, t, i3); } function gr(n, r, t, i3) { return P(cr(r), n, t, i3); } function Er(n, r, t, i3) { return P(_r(r, n.length - t), n, t, i3); } o.prototype.write = function(r, t, i3, e2) { if (t === void 0) e2 = "utf8", i3 = this.length, t = 0; else if (i3 === void 0 && typeof t == "string") e2 = t, i3 = this.length, t = 0; else if (isFinite(t)) t = t >>> 0, isFinite(i3) ? (i3 = i3 >>> 0, e2 === void 0 && (e2 = "utf8")) : (e2 = i3, i3 = void 0); else throw new Error( "Buffer.write(string, encoding, offset[, length]) is no longer supported" ); const u3 = this.length - t; if ((i3 === void 0 || i3 > u3) && (i3 = u3), r.length > 0 && (i3 < 0 || t < 0) || t > this.length) throw new RangeError("Attempt to write outside buffer bounds"); e2 || (e2 = "utf8"); let h2 = false; for (; ; ) switch (e2) { case "hex": return yr(this, r, t, i3); case "utf8": case "utf-8": return xr(this, r, t, i3); case "ascii": case "latin1": case "binary": return Br(this, r, t, i3); case "base64": return gr(this, r, t, i3); case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return Er(this, r, t, i3); default: if (h2) throw new TypeError("Unknown encoding: " + e2); e2 = ("" + e2).toLowerCase(), h2 = true; } }, o.prototype.toJSON = function() { return { type: "Buffer", data: Array.prototype.slice.call(this._arr || this, 0) }; }; function dr(n, r, t) { return r === 0 && t === n.length ? c.fromByteArray(n) : c.fromByteArray(n.slice(r, t)); } function v6(n, r, t) { t = Math.min(n.length, t); const i3 = []; let e2 = r; for (; e2 < t; ) { const u3 = n[e2]; let h2 = null, y2 = u3 > 239 ? 4 : u3 > 223 ? 3 : u3 > 191 ? 2 : 1; if (e2 + y2 <= t) { let E2, g, d17, x2; switch (y2) { case 1: u3 < 128 && (h2 = u3); break; case 2: E2 = n[e2 + 1], (E2 & 192) === 128 && (x2 = (u3 & 31) << 6 | E2 & 63, x2 > 127 && (h2 = x2)); break; case 3: E2 = n[e2 + 1], g = n[e2 + 2], (E2 & 192) === 128 && (g & 192) === 128 && (x2 = (u3 & 15) << 12 | (E2 & 63) << 6 | g & 63, x2 > 2047 && (x2 < 55296 || x2 > 57343) && (h2 = x2)); break; case 4: E2 = n[e2 + 1], g = n[e2 + 2], d17 = n[e2 + 3], (E2 & 192) === 128 && (g & 192) === 128 && (d17 & 192) === 128 && (x2 = (u3 & 15) << 18 | (E2 & 63) << 12 | (g & 63) << 6 | d17 & 63, x2 > 65535 && x2 < 1114112 && (h2 = x2)); } } h2 === null ? (h2 = 65533, y2 = 1) : h2 > 65535 && (h2 -= 65536, i3.push(h2 >>> 10 & 1023 | 55296), h2 = 56320 | h2 & 1023), i3.push(h2), e2 += y2; } return mr(i3); } const rr = 4096; function mr(n) { const r = n.length; if (r <= rr) return String.fromCharCode.apply(String, n); let t = "", i3 = 0; for (; i3 < r; ) t += String.fromCharCode.apply( String, n.slice(i3, i3 += rr) ); return t; } function Ir(n, r, t) { let i3 = ""; t = Math.min(n.length, t); for (let e2 = r; e2 < t; ++e2) i3 += String.fromCharCode(n[e2] & 127); return i3; } function Fr(n, r, t) { let i3 = ""; t = Math.min(n.length, t); for (let e2 = r; e2 < t; ++e2) i3 += String.fromCharCode(n[e2]); return i3; } function Ar(n, r, t) { const i3 = n.length; (!r || r < 0) && (r = 0), (!t || t < 0 || t > i3) && (t = i3); let e2 = ""; for (let u3 = r; u3 < t; ++u3) e2 += Lr[n[u3]]; return e2; } function Ur(n, r, t) { const i3 = n.slice(r, t); let e2 = ""; for (let u3 = 0; u3 < i3.length - 1; u3 += 2) e2 += String.fromCharCode(i3[u3] + i3[u3 + 1] * 256); return e2; } o.prototype.slice = function(r, t) { const i3 = this.length; r = ~~r, t = t === void 0 ? i3 : ~~t, r < 0 ? (r += i3, r < 0 && (r = 0)) : r > i3 && (r = i3), t < 0 ? (t += i3, t < 0 && (t = 0)) : t > i3 && (t = i3), t < r && (t = r); const e2 = this.subarray(r, t); return Object.setPrototypeOf(e2, o.prototype), e2; }; function I2(n, r, t) { if (n % 1 !== 0 || n < 0) throw new RangeError("offset is not uint"); if (n + r > t) throw new RangeError("Trying to access beyond buffer length"); } o.prototype.readUintLE = o.prototype.readUIntLE = function(r, t, i3) { r = r >>> 0, t = t >>> 0, i3 || I2(r, t, this.length); let e2 = this[r], u3 = 1, h2 = 0; for (; ++h2 < t && (u3 *= 256); ) e2 += this[r + h2] * u3; return e2; }, o.prototype.readUintBE = o.prototype.readUIntBE = function(r, t, i3) { r = r >>> 0, t = t >>> 0, i3 || I2(r, t, this.length); let e2 = this[r + --t], u3 = 1; for (; t > 0 && (u3 *= 256); ) e2 += this[r + --t] * u3; return e2; }, o.prototype.readUint8 = o.prototype.readUInt8 = function(r, t) { return r = r >>> 0, t || I2(r, 1, this.length), this[r]; }, o.prototype.readUint16LE = o.prototype.readUInt16LE = function(r, t) { return r = r >>> 0, t || I2(r, 2, this.length), this[r] | this[r + 1] << 8; }, o.prototype.readUint16BE = o.prototype.readUInt16BE = function(r, t) { return r = r >>> 0, t || I2(r, 2, this.length), this[r] << 8 | this[r + 1]; }, o.prototype.readUint32LE = o.prototype.readUInt32LE = function(r, t) { return r = r >>> 0, t || I2(r, 4, this.length), (this[r] | this[r + 1] << 8 | this[r + 2] << 16) + this[r + 3] * 16777216; }, o.prototype.readUint32BE = o.prototype.readUInt32BE = function(r, t) { return r = r >>> 0, t || I2(r, 4, this.length), this[r] * 16777216 + (this[r + 1] << 16 | this[r + 2] << 8 | this[r + 3]); }, o.prototype.readBigUInt64LE = L(function(r) { r = r >>> 0, k2(r, "offset"); const t = this[r], i3 = this[r + 7]; (t === void 0 || i3 === void 0) && $2(r, this.length - 8); const e2 = t + this[++r] * 2 ** 8 + this[++r] * 2 ** 16 + this[++r] * 2 ** 24, u3 = this[++r] + this[++r] * 2 ** 8 + this[++r] * 2 ** 16 + i3 * 2 ** 24; return BigInt(e2) + (BigInt(u3) << BigInt(32)); }), o.prototype.readBigUInt64BE = L(function(r) { r = r >>> 0, k2(r, "offset"); const t = this[r], i3 = this[r + 7]; (t === void 0 || i3 === void 0) && $2(r, this.length - 8); const e2 = t * 2 ** 24 + this[++r] * 2 ** 16 + this[++r] * 2 ** 8 + this[++r], u3 = this[++r] * 2 ** 24 + this[++r] * 2 ** 16 + this[++r] * 2 ** 8 + i3; return (BigInt(e2) << BigInt(32)) + BigInt(u3); }), o.prototype.readIntLE = function(r, t, i3) { r = r >>> 0, t = t >>> 0, i3 || I2(r, t, this.length); let e2 = this[r], u3 = 1, h2 = 0; for (; ++h2 < t && (u3 *= 256); ) e2 += this[r + h2] * u3; return u3 *= 128, e2 >= u3 && (e2 -= Math.pow(2, 8 * t)), e2; }, o.prototype.readIntBE = function(r, t, i3) { r = r >>> 0, t = t >>> 0, i3 || I2(r, t, this.length); let e2 = t, u3 = 1, h2 = this[r + --e2]; for (; e2 > 0 && (u3 *= 256); ) h2 += this[r + --e2] * u3; return u3 *= 128, h2 >= u3 && (h2 -= Math.pow(2, 8 * t)), h2; }, o.prototype.readInt8 = function(r, t) { return r = r >>> 0, t || I2(r, 1, this.length), this[r] & 128 ? (255 - this[r] + 1) * -1 : this[r]; }, o.prototype.readInt16LE = function(r, t) { r = r >>> 0, t || I2(r, 2, this.length); const i3 = this[r] | this[r + 1] << 8; return i3 & 32768 ? i3 | 4294901760 : i3; }, o.prototype.readInt16BE = function(r, t) { r = r >>> 0, t || I2(r, 2, this.length); const i3 = this[r + 1] | this[r] << 8; return i3 & 32768 ? i3 | 4294901760 : i3; }, o.prototype.readInt32LE = function(r, t) { return r = r >>> 0, t || I2(r, 4, this.length), this[r] | this[r + 1] << 8 | this[r + 2] << 16 | this[r + 3] << 24; }, o.prototype.readInt32BE = function(r, t) { return r = r >>> 0, t || I2(r, 4, this.length), this[r] << 24 | this[r + 1] << 16 | this[r + 2] << 8 | this[r + 3]; }, o.prototype.readBigInt64LE = L(function(r) { r = r >>> 0, k2(r, "offset"); const t = this[r], i3 = this[r + 7]; (t === void 0 || i3 === void 0) && $2(r, this.length - 8); const e2 = this[r + 4] + this[r + 5] * 2 ** 8 + this[r + 6] * 2 ** 16 + (i3 << 24); return (BigInt(e2) << BigInt(32)) + BigInt(t + this[++r] * 2 ** 8 + this[++r] * 2 ** 16 + this[++r] * 2 ** 24); }), o.prototype.readBigInt64BE = L(function(r) { r = r >>> 0, k2(r, "offset"); const t = this[r], i3 = this[r + 7]; (t === void 0 || i3 === void 0) && $2(r, this.length - 8); const e2 = (t << 24) + // Overflow this[++r] * 2 ** 16 + this[++r] * 2 ** 8 + this[++r]; return (BigInt(e2) << BigInt(32)) + BigInt(this[++r] * 2 ** 24 + this[++r] * 2 ** 16 + this[++r] * 2 ** 8 + i3); }), o.prototype.readFloatLE = function(r, t) { return r = r >>> 0, t || I2(r, 4, this.length), f.read(this, r, true, 23, 4); }, o.prototype.readFloatBE = function(r, t) { return r = r >>> 0, t || I2(r, 4, this.length), f.read(this, r, false, 23, 4); }, o.prototype.readDoubleLE = function(r, t) { return r = r >>> 0, t || I2(r, 8, this.length), f.read(this, r, true, 52, 8); }, o.prototype.readDoubleBE = function(r, t) { return r = r >>> 0, t || I2(r, 8, this.length), f.read(this, r, false, 52, 8); }; function U2(n, r, t, i3, e2, u3) { if (!o.isBuffer(n)) throw new TypeError('"buffer" argument must be a Buffer instance'); if (r > e2 || r < u3) throw new RangeError('"value" argument is out of bounds'); if (t + i3 > n.length) throw new RangeError("Index out of range"); } o.prototype.writeUintLE = o.prototype.writeUIntLE = function(r, t, i3, e2) { if (r = +r, t = t >>> 0, i3 = i3 >>> 0, !e2) { const y2 = Math.pow(2, 8 * i3) - 1; U2(this, r, t, i3, y2, 0); } let u3 = 1, h2 = 0; for (this[t] = r & 255; ++h2 < i3 && (u3 *= 256); ) this[t + h2] = r / u3 & 255; return t + i3; }, o.prototype.writeUintBE = o.prototype.writeUIntBE = function(r, t, i3, e2) { if (r = +r, t = t >>> 0, i3 = i3 >>> 0, !e2) { const y2 = Math.pow(2, 8 * i3) - 1; U2(this, r, t, i3, y2, 0); } let u3 = i3 - 1, h2 = 1; for (this[t + u3] = r & 255; --u3 >= 0 && (h2 *= 256); ) this[t + u3] = r / h2 & 255; return t + i3; }, o.prototype.writeUint8 = o.prototype.writeUInt8 = function(r, t, i3) { return r = +r, t = t >>> 0, i3 || U2(this, r, t, 1, 255, 0), this[t] = r & 255, t + 1; }, o.prototype.writeUint16LE = o.prototype.writeUInt16LE = function(r, t, i3) { return r = +r, t = t >>> 0, i3 || U2(this, r, t, 2, 65535, 0), this[t] = r & 255, this[t + 1] = r >>> 8, t + 2; }, o.prototype.writeUint16BE = o.prototype.writeUInt16BE = function(r, t, i3) { return r = +r, t = t >>> 0, i3 || U2(this, r, t, 2, 65535, 0), this[t] = r >>> 8, this[t + 1] = r & 255, t + 2; }, o.prototype.writeUint32LE = o.prototype.writeUInt32LE = function(r, t, i3) { return r = +r, t = t >>> 0, i3 || U2(this, r, t, 4, 4294967295, 0), this[t + 3] = r >>> 24, this[t + 2] = r >>> 16, this[t + 1] = r >>> 8, this[t] = r & 255, t + 4; }, o.prototype.writeUint32BE = o.prototype.writeUInt32BE = function(r, t, i3) { return r = +r, t = t >>> 0, i3 || U2(this, r, t, 4, 4294967295, 0), this[t] = r >>> 24, this[t + 1] = r >>> 16, this[t + 2] = r >>> 8, this[t + 3] = r & 255, t + 4; }; function tr(n, r, t, i3, e2) { hr(r, i3, e2, n, t, 7); let u3 = Number(r & BigInt(4294967295)); n[t++] = u3, u3 = u3 >> 8, n[t++] = u3, u3 = u3 >> 8, n[t++] = u3, u3 = u3 >> 8, n[t++] = u3; let h2 = Number(r >> BigInt(32) & BigInt(4294967295)); return n[t++] = h2, h2 = h2 >> 8, n[t++] = h2, h2 = h2 >> 8, n[t++] = h2, h2 = h2 >> 8, n[t++] = h2, t; } function nr(n, r, t, i3, e2) { hr(r, i3, e2, n, t, 7); let u3 = Number(r & BigInt(4294967295)); n[t + 7] = u3, u3 = u3 >> 8, n[t + 6] = u3, u3 = u3 >> 8, n[t + 5] = u3, u3 = u3 >> 8, n[t + 4] = u3; let h2 = Number(r >> BigInt(32) & BigInt(4294967295)); return n[t + 3] = h2, h2 = h2 >> 8, n[t + 2] = h2, h2 = h2 >> 8, n[t + 1] = h2, h2 = h2 >> 8, n[t] = h2, t + 8; } o.prototype.writeBigUInt64LE = L(function(r, t = 0) { return tr(this, r, t, BigInt(0), BigInt("0xffffffffffffffff")); }), o.prototype.writeBigUInt64BE = L(function(r, t = 0) { return nr(this, r, t, BigInt(0), BigInt("0xffffffffffffffff")); }), o.prototype.writeIntLE = function(r, t, i3, e2) { if (r = +r, t = t >>> 0, !e2) { const E2 = Math.pow(2, 8 * i3 - 1); U2(this, r, t, i3, E2 - 1, -E2); } let u3 = 0, h2 = 1, y2 = 0; for (this[t] = r & 255; ++u3 < i3 && (h2 *= 256); ) r < 0 && y2 === 0 && this[t + u3 - 1] !== 0 && (y2 = 1), this[t + u3] = (r / h2 >> 0) - y2 & 255; return t + i3; }, o.prototype.writeIntBE = function(r, t, i3, e2) { if (r = +r, t = t >>> 0, !e2) { const E2 = Math.pow(2, 8 * i3 - 1); U2(this, r, t, i3, E2 - 1, -E2); } let u3 = i3 - 1, h2 = 1, y2 = 0; for (this[t + u3] = r & 255; --u3 >= 0 && (h2 *= 256); ) r < 0 && y2 === 0 && this[t + u3 + 1] !== 0 && (y2 = 1), this[t + u3] = (r / h2 >> 0) - y2 & 255; return t + i3; }, o.prototype.writeInt8 = function(r, t, i3) { return r = +r, t = t >>> 0, i3 || U2(this, r, t, 1, 127, -128), r < 0 && (r = 255 + r + 1), this[t] = r & 255, t + 1; }, o.prototype.writeInt16LE = function(r, t, i3) { return r = +r, t = t >>> 0, i3 || U2(this, r, t, 2, 32767, -32768), this[t] = r & 255, this[t + 1] = r >>> 8, t + 2; }, o.prototype.writeInt16BE = function(r, t, i3) { return r = +r, t = t >>> 0, i3 || U2(this, r, t, 2, 32767, -32768), this[t] = r >>> 8, this[t + 1] = r & 255, t + 2; }, o.prototype.writeInt32LE = function(r, t, i3) { return r = +r, t = t >>> 0, i3 || U2(this, r, t, 4, 2147483647, -2147483648), this[t] = r & 255, this[t + 1] = r >>> 8, this[t + 2] = r >>> 16, this[t + 3] = r >>> 24, t + 4; }, o.prototype.writeInt32BE = function(r, t, i3) { return r = +r, t = t >>> 0, i3 || U2(this, r, t, 4, 2147483647, -2147483648), r < 0 && (r = 4294967295 + r + 1), this[t] = r >>> 24, this[t + 1] = r >>> 16, this[t + 2] = r >>> 8, this[t + 3] = r & 255, t + 4; }, o.prototype.writeBigInt64LE = L(function(r, t = 0) { return tr(this, r, t, -BigInt("0x8000000000000000"), BigInt("0x7fffffffffffffff")); }), o.prototype.writeBigInt64BE = L(function(r, t = 0) { return nr(this, r, t, -BigInt("0x8000000000000000"), BigInt("0x7fffffffffffffff")); }); function ir(n, r, t, i3, e2, u3) { if (t + i3 > n.length) throw new RangeError("Index out of range"); if (t < 0) throw new RangeError("Index out of range"); } function er(n, r, t, i3, e2) { return r = +r, t = t >>> 0, e2 || ir(n, r, t, 4), f.write(n, r, t, i3, 23, 4), t + 4; } o.prototype.writeFloatLE = function(r, t, i3) { return er(this, r, t, true, i3); }, o.prototype.writeFloatBE = function(r, t, i3) { return er(this, r, t, false, i3); }; function or(n, r, t, i3, e2) { return r = +r, t = t >>> 0, e2 || ir(n, r, t, 8), f.write(n, r, t, i3, 52, 8), t + 8; } o.prototype.writeDoubleLE = function(r, t, i3) { return or(this, r, t, true, i3); }, o.prototype.writeDoubleBE = function(r, t, i3) { return or(this, r, t, false, i3); }, o.prototype.copy = function(r, t, i3, e2) { if (!o.isBuffer(r)) throw new TypeError("argument should be a Buffer"); if (i3 || (i3 = 0), !e2 && e2 !== 0 && (e2 = this.length), t >= r.length && (t = r.length), t || (t = 0), e2 > 0 && e2 < i3 && (e2 = i3), e2 === i3 || r.length === 0 || this.length === 0) return 0; if (t < 0) throw new RangeError("targetStart out of bounds"); if (i3 < 0 || i3 >= this.length) throw new RangeError("Index out of range"); if (e2 < 0) throw new RangeError("sourceEnd out of bounds"); e2 > this.length && (e2 = this.length), r.length - t < e2 - i3 && (e2 = r.length - t + i3); const u3 = e2 - i3; return this === r && typeof p5.prototype.copyWithin == "function" ? this.copyWithin(t, i3, e2) : p5.prototype.set.call( r, this.subarray(i3, e2), t ), u3; }, o.prototype.fill = function(r, t, i3, e2) { if (typeof r == "string") { if (typeof t == "string" ? (e2 = t, t = 0, i3 = this.length) : typeof i3 == "string" && (e2 = i3, i3 = this.length), e2 !== void 0 && typeof e2 != "string") throw new TypeError("encoding must be a string"); if (typeof e2 == "string" && !o.isEncoding(e2)) throw new TypeError("Unknown encoding: " + e2); if (r.length === 1) { const h2 = r.charCodeAt(0); (e2 === "utf8" && h2 < 128 || e2 === "latin1") && (r = h2); } } else typeof r == "number" ? r = r & 255 : typeof r == "boolean" && (r = Number(r)); if (t < 0 || this.length < t || this.length < i3) throw new RangeError("Out of range index"); if (i3 <= t) return this; t = t >>> 0, i3 = i3 === void 0 ? this.length : i3 >>> 0, r || (r = 0); let u3; if (typeof r == "number") for (u3 = t; u3 < i3; ++u3) this[u3] = r; else { const h2 = o.isBuffer(r) ? r : o.from(r, e2), y2 = h2.length; if (y2 === 0) throw new TypeError('The value "' + r + '" is invalid for argument "value"'); for (u3 = 0; u3 < i3 - t; ++u3) this[u3 + t] = h2[u3 % y2]; } return this; }; const b = {}; function q2(n, r, t) { b[n] = class extends t { constructor() { super(), Object.defineProperty(this, "message", { value: r.apply(this, arguments), writable: true, configurable: true }), this.name = `${this.name} [${n}]`, this.stack, delete this.name; } get code() { return n; } set code(e2) { Object.defineProperty(this, "code", { configurable: true, enumerable: true, value: e2, writable: true }); } toString() { return `${this.name} [${n}]: ${this.message}`; } }; } q2( "ERR_BUFFER_OUT_OF_BOUNDS", function(n) { return n ? `${n} is outside of buffer bounds` : "Attempt to access memory outside buffer bounds"; }, RangeError ), q2( "ERR_INVALID_ARG_TYPE", function(n, r) { return `The "${n}" argument must be of type number. Received type ${typeof r}`; }, TypeError ), q2( "ERR_OUT_OF_RANGE", function(n, r, t) { let i3 = `The value of "${n}" is out of range.`, e2 = t; return Number.isInteger(t) && Math.abs(t) > 2 ** 32 ? e2 = ur(String(t)) : typeof t == "bigint" && (e2 = String(t), (t > BigInt(2) ** BigInt(32) || t < -(BigInt(2) ** BigInt(32))) && (e2 = ur(e2)), e2 += "n"), i3 += ` It must be ${r}. Received ${e2}`, i3; }, RangeError ); function ur(n) { let r = "", t = n.length; const i3 = n[0] === "-" ? 1 : 0; for (; t >= i3 + 4; t -= 3) r = `_${n.slice(t - 3, t)}${r}`; return `${n.slice(0, t)}${r}`; } function Tr(n, r, t) { k2(r, "offset"), (n[r] === void 0 || n[r + t] === void 0) && $2(r, n.length - (t + 1)); } function hr(n, r, t, i3, e2, u3) { if (n > t || n < r) { const h2 = typeof r == "bigint" ? "n" : ""; let y2; throw r === 0 || r === BigInt(0) ? y2 = `>= 0${h2} and < 2${h2} ** ${(u3 + 1) * 8}${h2}` : y2 = `>= -(2${h2} ** ${(u3 + 1) * 8 - 1}${h2}) and < 2 ** ${(u3 + 1) * 8 - 1}${h2}`, new b.ERR_OUT_OF_RANGE("value", y2, n); } Tr(i3, e2, u3); } function k2(n, r) { if (typeof n != "number") throw new b.ERR_INVALID_ARG_TYPE(r, "number", n); } function $2(n, r, t) { throw Math.floor(n) !== n ? (k2(n, t), new b.ERR_OUT_OF_RANGE("offset", "an integer", n)) : r < 0 ? new b.ERR_BUFFER_OUT_OF_BOUNDS() : new b.ERR_OUT_OF_RANGE( "offset", `>= 0 and <= ${r}`, n ); } const Rr = /[^+/0-9A-Za-z-_]/g; function Cr(n) { if (n = n.split("=")[0], n = n.trim().replace(Rr, ""), n.length < 2) return ""; for (; n.length % 4 !== 0; ) n = n + "="; return n; } function V2(n, r) { r = r || 1 / 0; let t; const i3 = n.length; let e2 = null; const u3 = []; for (let h2 = 0; h2 < i3; ++h2) { if (t = n.charCodeAt(h2), t > 55295 && t < 57344) { if (!e2) { if (t > 56319) { (r -= 3) > -1 && u3.push(239, 191, 189); continue; } else if (h2 + 1 === i3) { (r -= 3) > -1 && u3.push(239, 191, 189); continue; } e2 = t; continue; } if (t < 56320) { (r -= 3) > -1 && u3.push(239, 191, 189), e2 = t; continue; } t = (e2 - 55296 << 10 | t - 56320) + 65536; } else e2 && (r -= 3) > -1 && u3.push(239, 191, 189); if (e2 = null, t < 128) { if ((r -= 1) < 0) break; u3.push(t); } else if (t < 2048) { if ((r -= 2) < 0) break; u3.push( t >> 6 | 192, t & 63 | 128 ); } else if (t < 65536) { if ((r -= 3) < 0) break; u3.push( t >> 12 | 224, t >> 6 & 63 | 128, t & 63 | 128 ); } else if (t < 1114112) { if ((r -= 4) < 0) break; u3.push( t >> 18 | 240, t >> 12 & 63 | 128, t >> 6 & 63 | 128, t & 63 | 128 ); } else throw new Error("Invalid code point"); } return u3; } function Sr(n) { const r = []; for (let t = 0; t < n.length; ++t) r.push(n.charCodeAt(t) & 255); return r; } function _r(n, r) { let t, i3, e2; const u3 = []; for (let h2 = 0; h2 < n.length && !((r -= 2) < 0); ++h2) t = n.charCodeAt(h2), i3 = t >> 8, e2 = t % 256, u3.push(e2), u3.push(i3); return u3; } function cr(n) { return c.toByteArray(Cr(n)); } function P(n, r, t, i3) { let e2; for (e2 = 0; e2 < i3 && !(e2 + t >= r.length || e2 >= n.length); ++e2) r[e2 + t] = n[e2]; return e2; } function C2(n, r) { return n instanceof r || n != null && n.constructor != null && n.constructor.name != null && n.constructor.name === r.name; } function X2(n) { return n !== n; } const Lr = (function() { const n = "0123456789abcdef", r = new Array(256); for (let t = 0; t < 16; ++t) { const i3 = t * 16; for (let e2 = 0; e2 < 16; ++e2) r[i3 + e2] = n[t] + n[e2]; } return r; })(); function L(n) { return typeof BigInt > "u" ? Nr : n; } function Nr() { throw new Error("BigInt not supported"); } })(fr); O = fr.Buffer; Yr = class { static fromHex(c) { if (c = c.trim(), c.length === 0) return new Uint8Array(0); if (c.length < 2 || c.length & 1) throw new Error("Invalid hex string: odd length."); if ((c.startsWith("0x") || c.startsWith("0X")) && (c = c.slice(2)), !c.match(/^[0-9a-fA-F]*$/)) throw new Error("Invalid hex string: contains non-hex characters"); const w2 = c.match(/.{1,2}/g); if (!w2) throw new Error("Invalid hex string"); return new Uint8Array(w2.map((l3) => parseInt(l3, 16))); } static toHex(c) { return Array.from(c, (f) => f.toString(16).padStart(2, "0")).join(""); } static fromString(c) { return c = c.trim(), new TextEncoder().encode(c); } static toString(c) { return new TextDecoder("utf-8").decode(c); } static concat(...c) { const f = c.reduce((p5, a) => p5 + a.length, 0), w2 = new Uint8Array(f); let l3 = 0; for (const p5 of c) w2.set(p5, l3), l3 += p5.length; return w2; } static alloc(c) { return new Uint8Array(c); } static writeBigUint64BE(c) { const f = new ArrayBuffer(8); return new DataView(f).setBigUint64(0, c, false), new Uint8Array(f); } static toBase64(c) { if (typeof O < "u") return O.from(c).toString("base64"); if (c.length > 32768) { let f = ""; for (let w2 = 0; w2 < c.length; w2 += 32768) { const l3 = c.slice(w2, w2 + 32768); f += btoa(String.fromCharCode(...l3)); } return f; } return btoa(String.fromCharCode(...c)); } static fromBase64(c) { if (c = c.trim(), typeof O < "u") return new Uint8Array(O.from(c, "base64")); let f = c.replace(/-/g, "+").replace(/_/g, "/"); for (; f.length % 4; ) f += "="; return new Uint8Array([...atob(f)].map((w2) => w2.charCodeAt(0))); } static equals(c, f) { if (c.length !== f.length) return false; let w2 = 0; for (let l3 = 0; l3 < c.length; l3++) w2 |= c[l3] ^ f[l3]; return w2 === 0; } static compare(c, f) { const w2 = Math.min(c.length, f.length); for (let l3 = 0; l3 < w2; l3++) { if (c[l3] < f[l3]) return -1; if (c[l3] > f[l3]) return 1; } return c.length - f.length; } }; } }); // ndk/node_modules/@cashu/cashu-ts/lib/crypto/common.es.js function T(t) { const e2 = sha2562(Yr.concat(d8, t)), n = new Uint32Array(1), o = 2 ** 16; for (let s = 0; s < o; s++) { const m = new Uint8Array(n.buffer), r = sha2562(Yr.concat(e2, m)); try { return l(bytesToHex7(Yr.concat(new Uint8Array([2]), r))); } catch { n[0]++; } } throw new Error("No valid point found"); } function H(t) { const n = t.map((o) => o.toHex(false)).join(""); return sha2562(new TextEncoder().encode(n)); } function l(t) { return secp256k1.ProjectivePoint.fromHex(t); } var d8, K; var init_common_es = __esm({ "ndk/node_modules/@cashu/cashu-ts/lib/crypto/common.es.js"() { init_secp256k1(); init_sha256(); init_utils3(); init_utils_CrQNeCaC(); d8 = hexToBytes5("536563703235366b315f48617368546f43757276655f43617368755f"); K = (t) => { let e2; return /^[a-fA-F0-9]+$/.test(t) ? e2 = Hr(t) % BigInt(2 ** 31 - 1) : e2 = Vr(Xr(t)) % BigInt(2 ** 31 - 1), e2; }; } }); // ndk/node_modules/@cashu/cashu-ts/lib/crypto/client/NUT12.es.js function p(t, o) { if (t.length !== o.length) return false; for (let r = 0; r < t.length; r++) if (t[r] !== o[r]) return false; return true; } var v, d9; var init_NUT12_es = __esm({ "ndk/node_modules/@cashu/cashu-ts/lib/crypto/client/NUT12.es.js"() { init_common_es(); init_utils3(); init_secp256k1(); init_utils_CrQNeCaC(); v = (t, o, r, e2) => { const n = secp256k1.ProjectivePoint.fromPrivateKey(bytesToHex7(t.s)), s = e2.multiply(Vr(t.e)), i3 = o.multiply(Vr(t.s)), f = r.multiply(Vr(t.e)), m = n.subtract(s), a = i3.subtract(f), y2 = H([m, a, e2, r]); return p(y2, t.e); }; d9 = (t, o, r, e2) => { if (o.r === void 0) throw new Error("verifyDLEQProof_reblind: Undefined blinding factor"); const n = T(t), s = r.add(e2.multiply(o.r)), i3 = secp256k1.ProjectivePoint.fromPrivateKey(o.r), f = n.add(i3); return v(o, f, s, e2); }; } }); // ndk/node_modules/@noble/hashes/esm/legacy.js function ripemd_f(group, x2, y2, z3) { if (group === 0) return x2 ^ y2 ^ z3; if (group === 1) return x2 & y2 | ~x2 & z3; if (group === 2) return (x2 | ~y2) ^ z3; if (group === 3) return x2 & z3 | y2 & ~z3; return x2 ^ (y2 | ~z3); } var Rho160, Id160, Pi160, idxLR, idxL, idxR, shifts160, shiftsL160, shiftsR160, Kl160, Kr160, BUF_160, RIPEMD160, ripemd160; var init_legacy = __esm({ "ndk/node_modules/@noble/hashes/esm/legacy.js"() { init_md(); init_utils(); Rho160 = /* @__PURE__ */ Uint8Array.from([ 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8 ]); Id160 = /* @__PURE__ */ (() => Uint8Array.from(new Array(16).fill(0).map((_2, i3) => i3)))(); Pi160 = /* @__PURE__ */ (() => Id160.map((i3) => (9 * i3 + 5) % 16))(); idxLR = /* @__PURE__ */ (() => { const L = [Id160]; const R2 = [Pi160]; const res = [L, R2]; for (let i3 = 0; i3 < 4; i3++) for (let j2 of res) j2.push(j2[i3].map((k2) => Rho160[k2])); return res; })(); idxL = /* @__PURE__ */ (() => idxLR[0])(); idxR = /* @__PURE__ */ (() => idxLR[1])(); shifts160 = /* @__PURE__ */ [ [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8], [12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7], [13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9], [14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6], [15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5] ].map((i3) => Uint8Array.from(i3)); shiftsL160 = /* @__PURE__ */ idxL.map((idx, i3) => idx.map((j2) => shifts160[i3][j2])); shiftsR160 = /* @__PURE__ */ idxR.map((idx, i3) => idx.map((j2) => shifts160[i3][j2])); Kl160 = /* @__PURE__ */ Uint32Array.from([ 0, 1518500249, 1859775393, 2400959708, 2840853838 ]); Kr160 = /* @__PURE__ */ Uint32Array.from([ 1352829926, 1548603684, 1836072691, 2053994217, 0 ]); BUF_160 = /* @__PURE__ */ new Uint32Array(16); RIPEMD160 = class extends HashMD { constructor() { super(64, 20, 8, true); this.h0 = 1732584193 | 0; this.h1 = 4023233417 | 0; this.h2 = 2562383102 | 0; this.h3 = 271733878 | 0; this.h4 = 3285377520 | 0; } get() { const { h0, h1, h2, h3, h4 } = this; return [h0, h1, h2, h3, h4]; } set(h0, h1, h2, h3, h4) { this.h0 = h0 | 0; this.h1 = h1 | 0; this.h2 = h2 | 0; this.h3 = h3 | 0; this.h4 = h4 | 0; } process(view, offset) { for (let i3 = 0; i3 < 16; i3++, offset += 4) BUF_160[i3] = view.getUint32(offset, true); let al = this.h0 | 0, ar = al, bl = this.h1 | 0, br2 = bl, cl = this.h2 | 0, cr = cl, dl = this.h3 | 0, dr = dl, el = this.h4 | 0, er = el; for (let group = 0; group < 5; group++) { const rGroup = 4 - group; const hbl = Kl160[group], hbr = Kr160[group]; const rl = idxL[group], rr = idxR[group]; const sl = shiftsL160[group], sr = shiftsR160[group]; for (let i3 = 0; i3 < 16; i3++) { const tl = rotl(al + ripemd_f(group, bl, cl, dl) + BUF_160[rl[i3]] + hbl, sl[i3]) + el | 0; al = el, el = dl, dl = rotl(cl, 10) | 0, cl = bl, bl = tl; } for (let i3 = 0; i3 < 16; i3++) { const tr = rotl(ar + ripemd_f(rGroup, br2, cr, dr) + BUF_160[rr[i3]] + hbr, sr[i3]) + er | 0; ar = er, er = dr, dr = rotl(cr, 10) | 0, cr = br2, br2 = tr; } } this.set(this.h1 + cl + dr | 0, this.h2 + dl + er | 0, this.h3 + el + ar | 0, this.h4 + al + br2 | 0, this.h0 + bl + cr | 0); } roundClean() { clean(BUF_160); } destroy() { this.destroyed = true; clean(this.buffer); this.set(0, 0, 0, 0, 0); } }; ripemd160 = /* @__PURE__ */ createHasher(() => new RIPEMD160()); } }); // ndk/node_modules/@cashu/cashu-ts/node_modules/@scure/bip32/lib/esm/index.js function bytesToNumber(bytes4) { abytes(bytes4); const h2 = bytes4.length === 0 ? "0" : bytesToHex(bytes4); return BigInt("0x" + h2); } function numberToBytes(num3) { if (typeof num3 !== "bigint") throw new Error("bigint expected"); return hexToBytes(num3.toString(16).padStart(64, "0")); } var Point2, base58check, MASTER_SECRET, BITCOIN_VERSIONS, HARDENED_OFFSET, hash160, fromU32, toU32, HDKey; var init_esm2 = __esm({ "ndk/node_modules/@cashu/cashu-ts/node_modules/@scure/bip32/lib/esm/index.js"() { init_modular(); init_secp256k1(); init_hmac(); init_legacy(); init_sha2(); init_utils(); init_esm(); Point2 = secp256k1.ProjectivePoint; base58check = createBase58check(sha256); MASTER_SECRET = utf8ToBytes("Bitcoin seed"); BITCOIN_VERSIONS = { private: 76066276, public: 76067358 }; HARDENED_OFFSET = 2147483648; hash160 = (data) => ripemd160(sha256(data)); fromU32 = (data) => createView(data).getUint32(0, false); toU32 = (n) => { if (!Number.isSafeInteger(n) || n < 0 || n > 2 ** 32 - 1) { throw new Error("invalid number, should be from 0 to 2**32-1, got " + n); } const buf = new Uint8Array(4); createView(buf).setUint32(0, n, false); return buf; }; HDKey = class _HDKey { get fingerprint() { if (!this.pubHash) { throw new Error("No publicKey set!"); } return fromU32(this.pubHash); } get identifier() { return this.pubHash; } get pubKeyHash() { return this.pubHash; } get privateKey() { return this.privKeyBytes || null; } get publicKey() { return this.pubKey || null; } get privateExtendedKey() { const priv = this.privateKey; if (!priv) { throw new Error("No private key"); } return base58check.encode(this.serialize(this.versions.private, concatBytes(new Uint8Array([0]), priv))); } get publicExtendedKey() { if (!this.pubKey) { throw new Error("No public key"); } return base58check.encode(this.serialize(this.versions.public, this.pubKey)); } static fromMasterSeed(seed, versions = BITCOIN_VERSIONS) { abytes(seed); if (8 * seed.length < 128 || 8 * seed.length > 512) { throw new Error("HDKey: seed length must be between 128 and 512 bits; 256 bits is advised, got " + seed.length); } const I2 = hmac(sha512, MASTER_SECRET, seed); return new _HDKey({ versions, chainCode: I2.slice(32), privateKey: I2.slice(0, 32) }); } static fromExtendedKey(base58key, versions = BITCOIN_VERSIONS) { const keyBuffer = base58check.decode(base58key); const keyView = createView(keyBuffer); const version = keyView.getUint32(0, false); const opt = { versions, depth: keyBuffer[4], parentFingerprint: keyView.getUint32(5, false), index: keyView.getUint32(9, false), chainCode: keyBuffer.slice(13, 45) }; const key = keyBuffer.slice(45); const isPriv = key[0] === 0; if (version !== versions[isPriv ? "private" : "public"]) { throw new Error("Version mismatch"); } if (isPriv) { return new _HDKey({ ...opt, privateKey: key.slice(1) }); } else { return new _HDKey({ ...opt, publicKey: key }); } } static fromJSON(json) { return _HDKey.fromExtendedKey(json.xpriv); } constructor(opt) { this.depth = 0; this.index = 0; this.chainCode = null; this.parentFingerprint = 0; if (!opt || typeof opt !== "object") { throw new Error("HDKey.constructor must not be called directly"); } this.versions = opt.versions || BITCOIN_VERSIONS; this.depth = opt.depth || 0; this.chainCode = opt.chainCode || null; this.index = opt.index || 0; this.parentFingerprint = opt.parentFingerprint || 0; if (!this.depth) { if (this.parentFingerprint || this.index) { throw new Error("HDKey: zero depth with non-zero index/parent fingerprint"); } } if (opt.publicKey && opt.privateKey) { throw new Error("HDKey: publicKey and privateKey at same time."); } if (opt.privateKey) { if (!secp256k1.utils.isValidPrivateKey(opt.privateKey)) { throw new Error("Invalid private key"); } this.privKey = typeof opt.privateKey === "bigint" ? opt.privateKey : bytesToNumber(opt.privateKey); this.privKeyBytes = numberToBytes(this.privKey); this.pubKey = secp256k1.getPublicKey(opt.privateKey, true); } else if (opt.publicKey) { this.pubKey = Point2.fromHex(opt.publicKey).toRawBytes(true); } else { throw new Error("HDKey: no public or private key provided"); } this.pubHash = hash160(this.pubKey); } derive(path) { if (!/^[mM]'?/.test(path)) { throw new Error('Path must start with "m" or "M"'); } if (/^[mM]'?$/.test(path)) { return this; } const parts = path.replace(/^[mM]'?\//, "").split("/"); let child = this; for (const c of parts) { const m = /^(\d+)('?)$/.exec(c); const m1 = m && m[1]; if (!m || m.length !== 3 || typeof m1 !== "string") throw new Error("invalid child index: " + c); let idx = +m1; if (!Number.isSafeInteger(idx) || idx >= HARDENED_OFFSET) { throw new Error("Invalid index"); } if (m[2] === "'") { idx += HARDENED_OFFSET; } child = child.deriveChild(idx); } return child; } deriveChild(index) { if (!this.pubKey || !this.chainCode) { throw new Error("No publicKey or chainCode set"); } let data = toU32(index); if (index >= HARDENED_OFFSET) { const priv = this.privateKey; if (!priv) { throw new Error("Could not derive hardened child key"); } data = concatBytes(new Uint8Array([0]), priv, data); } else { data = concatBytes(this.pubKey, data); } const I2 = hmac(sha512, this.chainCode, data); const childTweak = bytesToNumber(I2.slice(0, 32)); const chainCode = I2.slice(32); if (!secp256k1.utils.isValidPrivateKey(childTweak)) { throw new Error("Tweak bigger than curve order"); } const opt = { versions: this.versions, chainCode, depth: this.depth + 1, parentFingerprint: this.fingerprint, index }; try { if (this.privateKey) { const added = mod(this.privKey + childTweak, secp256k1.CURVE.n); if (!secp256k1.utils.isValidPrivateKey(added)) { throw new Error("The tweak was out of range or the resulted private key is invalid"); } opt.privateKey = added; } else { const added = Point2.fromHex(this.pubKey).add(Point2.fromPrivateKey(childTweak)); if (added.equals(Point2.ZERO)) { throw new Error("The tweak was equal to negative P, which made the result key invalid"); } opt.publicKey = added.toRawBytes(true); } return new _HDKey(opt); } catch (err) { return this.deriveChild(index + 1); } } sign(hash3) { if (!this.privateKey) { throw new Error("No privateKey set!"); } abytes(hash3, 32); return secp256k1.sign(hash3, this.privKey).toCompactRawBytes(); } verify(hash3, signature) { abytes(hash3, 32); abytes(signature, 64); if (!this.publicKey) { throw new Error("No publicKey set!"); } let sig; try { sig = secp256k1.Signature.fromCompact(signature); } catch (error) { return false; } return secp256k1.verify(sig, hash3, this.publicKey); } wipePrivateData() { this.privKey = void 0; if (this.privKeyBytes) { this.privKeyBytes.fill(0); this.privKeyBytes = void 0; } return this; } toJSON() { return { xpriv: this.privateExtendedKey, xpub: this.publicExtendedKey }; } serialize(version, key) { if (!this.chainCode) { throw new Error("No chainCode set"); } abytes(key, 33); return concatBytes(toU32(version), new Uint8Array([this.depth]), toU32(this.parentFingerprint), toU32(this.index), this.chainCode, key); } }; } }); // ndk/node_modules/@cashu/cashu-ts/lib/NUT09-BsylB_jy.js function D(r) { return Yr.toBase64(r).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } function U(r) { return Yr.fromBase64(r); } function j(r) { const e2 = JSON.stringify(r); return v3(Yr.toBase64(Yr.fromString(e2))); } function x(r) { const e2 = Yr.toString(Yr.fromBase64($(r))); return JSON.parse(e2); } function $(r) { return r.replace(/-/g, "+").replace(/_/g, "/").split("=")[0]; } function v3(r) { return r.replace(/\+/g, "-").replace(/\//g, "_").split("=")[0]; } function u(r) { if (typeof r != "string" || r.length === 0) return false; const e2 = /^[A-Za-z0-9\-_]+={0,2}$/, o = /^[A-Za-z0-9+/]+={0,2}$/; if (!e2.test(r) && !o.test(r)) return false; const n = r.replace(/-/g, "+").replace(/_/g, "/"), a = (4 - n.length % 4) % 4; if (a > 2) return false; const c = n + "=".repeat(a); try { const f = Yr.fromBase64(c), s = Yr.toBase64(f), g = s.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""), l3 = n.replace(/=+$/, ""); return s.replace(/=+$/, "") === l3 || g === l3; } catch { return false; } } var S2, T2, z2, p2, i2; var init_NUT09_BsylB_jy = __esm({ "ndk/node_modules/@cashu/cashu-ts/lib/NUT09-BsylB_jy.js"() { init_hmac(); init_sha2(); init_common_es(); init_esm2(); init_utils_CrQNeCaC(); S2 = "m/129372'/0'"; T2 = (r, e2, o) => { const n = /^[a-fA-F0-9]+$/.test(e2); if (!n && u(e2) || n && e2.startsWith("00")) return i2( r, e2, o, 0 /* SECRET */ ); if (n && e2.startsWith("01")) return p2( r, e2, o, 0 /* SECRET */ ); throw new Error(`Unrecognized keyset ID version ${e2.slice(0, 2)}`); }; z2 = (r, e2, o) => { const n = /^[a-fA-F0-9]+$/.test(e2); if (!n && u(e2) || n && e2.startsWith("00")) return i2( r, e2, o, 1 /* BLINDING_FACTOR */ ); if (n && e2.startsWith("01")) return p2( r, e2, o, 1 /* BLINDING_FACTOR */ ); throw new Error(`Unrecognized keyset ID version ${e2.slice(0, 2)}`); }; p2 = (r, e2, o, n) => { let a = Yr.concat( Yr.fromString("Cashu_KDF_HMAC_SHA256"), Yr.fromHex(e2), Yr.writeBigUint64BE(BigInt(o)) ); switch (n) { case 0: a = Yr.concat(a, Yr.fromHex("00")); break; case 1: a = Yr.concat(a, Yr.fromHex("01")); } return hmac(sha256, r, a); }; i2 = (r, e2, o, n) => { const a = HDKey.fromMasterSeed(r), c = K(e2), f = `${S2}/${c}'/${o}'/${n}`, s = a.derive(f); if (s.privateKey === null) throw new Error("Could not derive private key"); return s.privateKey; }; } }); // ndk/node_modules/@cashu/cashu-ts/lib/crypto/common/NUT11.es.js var e; var init_NUT11_es = __esm({ "ndk/node_modules/@cashu/cashu-ts/lib/crypto/common/NUT11.es.js"() { e = (r) => { try { return r instanceof Uint8Array && (r = new TextDecoder().decode(r)), JSON.parse(r); } catch { throw new Error("can't parse secret"); } }; } }); // ndk/node_modules/@cashu/cashu-ts/lib/crypto/client/NUT11.es.js function d10(t) { try { const n = typeof t == "string" ? e(t) : t; if (n[0] !== "P2PK") throw new Error('Invalid P2PK secret: must start with "P2PK"'); const r = Math.floor(Date.now() / 1e3); return w(n) > r ? E(n) : I(n); } catch { } return []; } function E(t) { const n = typeof t == "string" ? e(t) : t; if (n[0] !== "P2PK") throw new Error('Invalid P2PK secret: must start with "P2PK"'); const { data: r, tags: e2 } = n[1], s = e2 && e2.find((o) => o[0] === "pubkeys"), i3 = s && s.length > 1 ? s.slice(1) : []; return [r, ...i3].filter(Boolean); } function I(t) { const n = typeof t == "string" ? e(t) : t; if (n[0] !== "P2PK") throw new Error('Invalid P2PK secret: must start with "P2PK"'); const { tags: r } = n[1], e2 = r && r.find((s) => s[0] === "refund"); return e2 && e2.length > 1 ? e2.slice(1).filter(Boolean) : []; } function w(t) { const n = typeof t == "string" ? e(t) : t; if (n[0] !== "P2PK") throw new Error('Invalid P2PK secret: must start with "P2PK"'); const { tags: r } = n[1], e2 = r && r.find((s) => s[0] === "locktime"); return e2 && e2.length > 1 ? parseInt(e2[1], 10) : 1 / 0; } var p3, k, h, K2, W, T3, _; var init_NUT11_es2 = __esm({ "ndk/node_modules/@cashu/cashu-ts/lib/crypto/client/NUT11.es.js"() { init_utils3(); init_sha256(); init_secp256k1(); init_NUT11_es(); p3 = (t, n) => { const r = sha2562(t), e2 = schnorr.sign(r, n); return bytesToHex7(e2); }; k = (t, n) => { const r = sha2562(t), e2 = schnorr.sign(r, n); return bytesToHex7(e2); }; h = (t, n, r) => { try { const e2 = sha2562(n), s = r.length === 66 ? r.slice(2) : r; if (schnorr.verify(t, e2, hexToBytes5(s))) return true; } catch (e2) { console.error("verifyP2PKsecret error:", e2); } return false; }; K2 = (t) => { if (!t) return []; if (typeof t == "string") try { return JSON.parse(t).signatures || []; } catch (n) { return console.error("Failed to parse witness string:", n), []; } return t.signatures || []; }; W = (t, n, r = false) => { const e2 = Array.isArray(n) ? n : [n]; return t.map((s, i3) => { let o = s; for (const c of e2) try { o = T3(o, c); } catch (a) { const l3 = a instanceof Error ? a.message : "Unknown error"; if (r) throw new Error(`Failed signing proof #${i3 + 1}: ${l3}`); console.warn(`Proof #${i3 + 1}: ${l3}`); } return o; }); }; T3 = (t, n) => { const r = e(t.secret); if (r[0] !== "P2PK") throw new Error("not a P2PK secret"); const e2 = bytesToHex7(schnorr.getPublicKey(n)), s = d10(r); if (!s.length || !s.some((a) => a.includes(e2))) throw new Error(`Signature not required from [02|03]${e2}`); const i3 = K2(t.witness); if (i3.some((a) => { try { return h(a, t.secret, e2); } catch { return false; } })) throw new Error(`Proof already signed by [02|03]${e2}`); const c = p3(t.secret, n); return i3.push(c), { ...t, witness: { signatures: i3 } }; }; _ = (t, n) => { const r = t.B_.toHex(true), e2 = k(r, n); return t.witness = { signatures: [e2] }, t; }; } }); // ndk/node_modules/@cashu/cashu-ts/lib/crypto/client/NUT20.es.js function u2(s, n) { let e2 = s; for (const r of n) e2 += r.B_; const o = new TextEncoder().encode(e2); return sha2562(o); } function p4(s, n, e2) { const o = u2(n, e2), r = hexToBytes(s), t = schnorr.sign(o, r); return bytesToHex(t); } var init_NUT20_es = __esm({ "ndk/node_modules/@cashu/cashu-ts/lib/crypto/client/NUT20.es.js"() { init_secp256k1(); init_utils(); init_sha256(); } }); // ndk/node_modules/@cashu/cashu-ts/lib/crypto/client.es.js function l2(t, e2, n) { const i3 = T(t); e2 || (e2 = Vr(secp256k1.utils.randomPrivateKey())); const o = secp256k1.ProjectivePoint.BASE.multiply(e2), r = i3.add(o); return n !== void 0 ? _({ B_: r, r: e2, secret: t }, n) : { B_: r, r: e2, secret: t }; } function C(t, e2, n) { return t.subtract(n.multiply(e2)); } function y(t, e2, n, i3) { const o = i3, r = C(t.C_, e2, o); return { id: t.id, amount: t.amount, secret: n, C: r }; } var v4; var init_client_es = __esm({ "ndk/node_modules/@cashu/cashu-ts/lib/crypto/client.es.js"() { init_secp256k1(); init_utils_CrQNeCaC(); init_common_es(); init_NUT11_es2(); v4 = (t) => ({ amount: t.amount, C: t.C.toHex(true), id: t.id, secret: new TextDecoder().decode(t.secret), witness: JSON.stringify(t.witness) }); } }); // ndk/node_modules/@cashu/cashu-ts/lib/cashu-ts.es.js function ue(n) { return typeof n == "number" || typeof n == "string"; } function kt(n) { const t = []; return bt(n, t), new Uint8Array(t); } function bt(n, t) { if (n === null) t.push(246); else if (n === void 0) t.push(247); else if (typeof n == "boolean") t.push(n ? 245 : 244); else if (typeof n == "number") Nt(n, t); else if (typeof n == "string") Qt(n, t); else if (Array.isArray(n)) le(n, t); else if (n instanceof Uint8Array) he(n, t); else if ( // Defensive: POJO only (null/array handled above) typeof n == "object" && n !== null && !Array.isArray(n) ) de(n, t); else throw new Error("Unsupported type"); } function Nt(n, t) { if (n < 24) t.push(n); else if (n < 256) t.push(24, n); else if (n < 65536) t.push(25, n >> 8, n & 255); else if (n < 4294967296) t.push(26, n >> 24, n >> 16 & 255, n >> 8 & 255, n & 255); else throw new Error("Unsupported integer size"); } function he(n, t) { const e2 = n.length; if (e2 < 24) t.push(64 + e2); else if (e2 < 256) t.push(88, e2); else if (e2 < 65536) t.push(89, e2 >> 8 & 255, e2 & 255); else if (e2 < 4294967296) t.push( 90, e2 >> 24 & 255, e2 >> 16 & 255, e2 >> 8 & 255, e2 & 255 ); else throw new Error("Byte string too long to encode"); for (let s = 0; s < n.length; s++) t.push(n[s]); } function Qt(n, t) { const e2 = new TextEncoder().encode(n), s = e2.length; if (s < 24) t.push(96 + s); else if (s < 256) t.push(120, s); else if (s < 65536) t.push(121, s >> 8 & 255, s & 255); else if (s < 4294967296) t.push( 122, s >> 24 & 255, s >> 16 & 255, s >> 8 & 255, s & 255 ); else throw new Error("String too long to encode"); for (let o = 0; o < e2.length; o++) t.push(e2[o]); } function le(n, t) { const e2 = n.length; if (e2 < 24) t.push(128 | e2); else if (e2 < 256) t.push(152, e2); else if (e2 < 65536) t.push(153, e2 >> 8, e2 & 255); else throw new Error("Unsupported array length"); for (const s of n) bt(s, t); } function de(n, t) { const e2 = Object.keys(n); Nt(e2.length, t), t[t.length - 1] |= 160; for (const s of e2) Qt(s, t), bt(n[s], t); } function _t(n) { const t = new DataView(n.buffer, n.byteOffset, n.byteLength); return it(t, 0).value; } function it(n, t) { if (t >= n.byteLength) throw new Error("Unexpected end of data"); const e2 = n.getUint8(t++), s = e2 >> 5, o = e2 & 31; switch (s) { case 0: return fe2(n, t, o); case 1: return me(n, t, o); case 2: return pe(n, t, o); case 3: return ye(n, t, o); case 4: return ge2(n, t, o); case 5: return we(n, t, o); case 7: return be(n, t, o); default: throw new Error(`Unsupported major type: ${s}`); } } function X(n, t, e2) { if (e2 < 24) return { value: e2, offset: t }; if (e2 === 24) return { value: n.getUint8(t++), offset: t }; if (e2 === 25) { const s = n.getUint16(t, false); return t += 2, { value: s, offset: t }; } if (e2 === 26) { const s = n.getUint32(t, false); return t += 4, { value: s, offset: t }; } if (e2 === 27) { const s = n.getUint32(t, false), o = n.getUint32(t + 4, false); return t += 8, { value: s * 2 ** 32 + o, offset: t }; } throw new Error(`Unsupported length: ${e2}`); } function fe2(n, t, e2) { const { value: s, offset: o } = X(n, t, e2); return { value: s, offset: o }; } function me(n, t, e2) { const { value: s, offset: o } = X(n, t, e2); return { value: -1 - s, offset: o }; } function pe(n, t, e2) { const { value: s, offset: o } = X(n, t, e2); if (o + s > n.byteLength) throw new Error("Byte string length exceeds data length"); return { value: new Uint8Array(n.buffer, n.byteOffset + o, s), offset: o + s }; } function ye(n, t, e2) { const { value: s, offset: o } = X(n, t, e2); if (o + s > n.byteLength) throw new Error("String length exceeds data length"); const r = new Uint8Array(n.buffer, n.byteOffset + o, s); return { value: new TextDecoder().decode(r), offset: o + s }; } function ge2(n, t, e2) { const { value: s, offset: o } = X(n, t, e2), r = []; let a = o; for (let i3 = 0; i3 < s; i3++) { const c = it(n, a); r.push(c.value), a = c.offset; } return { value: r, offset: a }; } function we(n, t, e2) { const { value: s, offset: o } = X(n, t, e2), r = {}; let a = o; for (let i3 = 0; i3 < s; i3++) { const c = it(n, a); if (!ue(c.value)) throw new Error("Invalid key type"); const u3 = it(n, c.offset); r[c.value] = u3.value, a = u3.offset; } return { value: r, offset: a }; } function ke(n) { const t = (n & 31744) >> 10, e2 = n & 1023, s = n & 32768 ? -1 : 1; return t === 0 ? s * 2 ** -14 * (e2 / 1024) : t === 31 ? e2 ? NaN : s * (1 / 0) : s * 2 ** (t - 15) * (1 + e2 / 1024); } function be(n, t, e2) { if (e2 < 24) switch (e2) { case 20: return { value: false, offset: t }; case 21: return { value: true, offset: t }; case 22: return { value: null, offset: t }; case 23: return { value: void 0, offset: t }; default: throw new Error(`Unknown simple value: ${e2}`); } if (e2 === 24) return { value: n.getUint8(t++), offset: t }; if (e2 === 25) { const s = ke(n.getUint16(t, false)); return t += 2, { value: s, offset: t }; } if (e2 === 26) { const s = n.getFloat32(t, false); return t += 4, { value: s, offset: t }; } if (e2 === 27) { const s = n.getFloat64(t, false); return t += 8, { value: s, offset: t }; } throw new Error(`Unknown simple or float value: ${e2}`); } function D2(n, t, e2, s) { if (e2) { const r = Dt(e2); if (n === 0 && r === 0) return e2; const a = e2.filter((c) => c > 0), i3 = Dt(a); if (i3 > n) throw new Error(`Split is greater than total amount: ${i3} > ${n}`); if (a.some((c) => !Ct(c, t))) throw new Error("Provided amount preferences do not match the amounts of the mint keyset."); if (i3 === n) return a; e2 = a, n -= i3; } else e2 = []; const o = Lt(t, "desc"); if (!o || o.length === 0) throw new Error("Cannot split amount, keyset is inactive or contains no keys"); if (o.forEach((r) => { if (n <= 0 || r <= 0) return; const a = Math.floor(n / r); for (let i3 = 0; i3 < a; ++i3) e2.push(r); n %= r; }), n !== 0) throw new Error(`Unable to split remaining amount: ${n}`); return e2.sort((r, a) => r - a); } function Tt(n, t, e2, s) { const o = [], r = n.map((c) => c.amount); Lt(e2, "asc").forEach((c) => { const u3 = r.filter((l3) => l3 === c).length, h2 = Math.max(s - u3, 0); for (let l3 = 0; l3 < h2 && !(o.reduce((f, d17) => f + d17, 0) + c > t); ++l3) o.push(c); }); const i3 = t - o.reduce((c, u3) => c + u3, 0); return i3 && D2(i3, e2).forEach((u3) => { o.push(u3); }), o.sort((c, u3) => c - u3); } function Lt(n, t = "desc") { return t == "desc" ? Object.keys(n).map((e2) => parseInt(e2)).sort((e2, s) => s - e2) : Object.keys(n).map((e2) => parseInt(e2)).sort((e2, s) => e2 - s); } function Ct(n, t) { return n in t; } function Ae(n) { return Wt(bytesToHex7(n)); } function Wt(n) { return BigInt(`0x${n}`); } function Pe(n) { return n.toString(16).padStart(64, "0"); } function yt(n) { return /^[a-f0-9]*$/i.test(n); } function At(n) { return Array.isArray(n) ? n.some((t) => !yt(t.id)) : !yt(n.id); } function Se(n, t) { At(n.proofs) || (n.proofs = $t(n.proofs)), t && (n.proofs = at(n.proofs)); const e2 = { token: [{ mint: n.mint, proofs: n.proofs }] }; return n.unit && (e2.unit = n.unit), n.memo && (e2.memo = n.memo), Ee + _e + j(e2); } function $t(n) { return n.map((t) => { const e2 = { ...t }; return e2.id = e2.id.slice(0, 16), e2; }); } function es(n, t) { if (At(n.proofs) || t?.version === 3) { if (t?.version === 4) throw new Error("can not encode to v4 token if proofs contain non-hex keyset id"); return Se(n, t?.removeDleq); } return Ie(n, t?.removeDleq); } function Ie(n, t) { if (t && (n.proofs = at(n.proofs)), n.proofs.forEach((c) => { if (c.dleq && c.dleq.r == null) throw new Error("Missing blinding factor in included DLEQ proof"); }), At(n.proofs)) throw new Error("can not encode to v4 token if proofs contain non-hex keyset id"); n.proofs = $t(n.proofs); const s = jt(n), o = kt(s), r = "cashu", a = "B", i3 = D(o); return r + a + i3; } function jt(n) { const t = {}, e2 = n.mint; for (let o = 0; o < n.proofs.length; o++) { const r = n.proofs[o]; t[r.id] ? t[r.id].push(r) : t[r.id] = [r]; } const s = { m: e2, u: n.unit || "sat", t: Object.keys(t).map( (o) => ({ i: hexToBytes5(o), p: t[o].map( (r) => ({ a: r.amount, s: r.secret, c: hexToBytes5(r.C), ...r.dleq && { d: { e: hexToBytes5(r.dleq.e), s: hexToBytes5(r.dleq.s), r: hexToBytes5(r.dleq.r ?? "00") } }, ...r.witness && { w: JSON.stringify(r.witness) } }) ) }) ) }; return n.memo && (s.d = n.memo), s; } function zt(n) { const t = []; n.t.forEach( (s) => s.p.forEach((o) => { t.push({ secret: o.s, C: bytesToHex7(o.c), amount: o.a, id: bytesToHex7(s.i), ...o.d && { dleq: { r: bytesToHex7(o.d.r), s: bytesToHex7(o.d.s), e: bytesToHex7(o.d.e) } }, ...o.w && { witness: o.w } }); }) ); const e2 = { mint: n.m, proofs: t, unit: n.u || "sat" }; return n.d && (e2.memo = n.d), e2; } function Me(n, t) { n = Xt(n); const e2 = Ht(n); return e2.proofs = Te(e2.proofs, t), e2; } function Ht(n) { const t = n.slice(0, 1), e2 = n.slice(1); if (t === "A") { const s = x(e2); if (s.token.length > 1) throw new Error("Multi entry token are not supported"); const o = s.token[0], r = { mint: o.mint, proofs: o.proofs, unit: s.unit || "sat" }; return s.memo && (r.memo = s.memo), r; } else if (t === "B") { const s = U(e2), o = _t(s); return zt(o); } throw new Error("Token version is not supported"); } function qe(n, t, e2, s = 0, o = false) { if (o) { const c = Object.entries(n).sort((l3, f) => +l3[0] - +f[0]).map(([, l3]) => l3).reduce((l3, f) => l3 + f, ""), u3 = sha256(c); return Yr.toBase64(u3).slice(0, 12); } let r = Object.entries(n).sort((c, u3) => +c[0] - +u3[0]).map(([, c]) => hexToBytes5(c)).reduce((c, u3) => dt(c, u3), new Uint8Array()), a, i3; switch (s) { case 0: return a = sha256(r), i3 = Yr.toHex(a).slice(0, 14), "00" + i3; case 1: if (!t) throw new Error("Cannot compute keyset ID version 01: unit is required."); return r = dt(r, Yr.fromString("unit:" + t)), e2 && (r = dt( r, Yr.fromString("final_expiry:" + e2.toString()) )), a = sha256(r), i3 = Yr.toHex(a), "01" + i3; default: throw new Error(`Unrecognized keyset ID version: ${s}`); } } function dt(n, t) { const e2 = new Uint8Array(n.length + t.length); return e2.set(n), e2.set(t, n.length), e2; } function O2(n) { return typeof n == "object"; } function S3(...n) { return n.map((t) => t.replace(/(^\/+|\/+$)/g, "")).join("/"); } function Gt(n) { return n.replace(/\/$/, ""); } function W2(n) { return n.reduce((t, e2) => t + e2.amount, 0); } function at(n) { return n.map((t) => { const e2 = { ...t }; return delete e2.dleq, e2; }); } function Rt(n) { const t = u(n.id), e2 = /^[a-fA-F0-9]+$/.test(n.id), s = e2 ? hexToBytes5(n.id)[0] : 0; return qe( n.keys, n.unit, n.final_expiry, s, t && !e2 ) === n.id; } function Te(n, t) { const e2 = []; for (const s of n) { let o; try { o = hexToBytes5(s.id); } catch { e2.push(s); continue; } if (o[0] === 0) e2.push(s); else if (o[0] === 1) { if (!t) throw new Error("A short keyset ID v2 was encountered, but got no keysets to map it to."); let r = false; for (const a of t) if (s.id === a.id.slice(0, s.id.length)) { s.id = a.id, e2.push(s), r = true; break; } if (!r) throw new Error( `Couldn't map short keyset ID ${s.id} to any known keysets of the current Mint` ); } else throw new Error(`Unknown keyset ID version: ${o[0]}`); } return e2; } function Vt(n, t) { if (n.dleq == null) return false; const e2 = { e: hexToBytes5(n.dleq.e), s: hexToBytes5(n.dleq.s), r: Wt(n.dleq.r ?? "00") }; if (!Ct(n.amount, t.keys)) throw new Error(`undefined key for amount ${n.amount}`); const s = t.keys[n.amount]; return d9( new TextEncoder().encode(n.secret), e2, l(n.C), l(s) ); } function Dt(n) { return n.reduce((t, e2) => t + e2, 0); } function Xt(n) { return ["web+cashu://", "cashu://", "cashu:", "cashu"].forEach((e2) => { n.startsWith(e2) && (n = n.slice(e2.length)); }), n; } function De() { if (ct === void 0) throw new Error("WebSocket implementation not initialized"); return ct; } function Ue() { const n = Date.now(); return { elapsed: () => Date.now() - n }; } function Oe(n) { Yt = n; } async function Fe({ endpoint: n, requestBody: t, headers: e2, ...s }) { const o = t ? JSON.stringify(t) : void 0, r = { Accept: "application/json, text/plain, */*", ...o ? { "Content-Type": "application/json" } : void 0, ...e2 }; let a; try { a = await fetch(n, { body: o, headers: r, ...s }); } catch (i3) { throw new Pt(i3 instanceof Error ? i3.message : "Network request failed"); } if (!a.ok) { let i3; try { i3 = await a.json(); } catch { i3 = { error: "bad response" }; } if (a.status === 400 && "code" in i3 && typeof i3.code == "number" && "detail" in i3 && typeof i3.detail == "string") throw new St(i3.code, i3.detail); let c = "HTTP request failed"; throw "error" in i3 && typeof i3.error == "string" ? c = i3.error : "detail" in i3 && typeof i3.detail == "string" && (c = i3.detail), new st(c, a.status); } try { return await a.json(); } catch (i3) { throw Yt.error("Failed to parse HTTP response", { err: i3 }), new st("bad response", a.status); } } async function M(n) { return await Fe({ ...n, ...Jt }); } function ft(n, t) { return n.state || (t.warn( "Field 'state' not found in MeltQuoteResponse. Update NUT-05 of mint: https://github.com/cashubtc/nuts/pull/136)" ), typeof n.paid == "boolean" && (n.state = n.paid ? et.PAID : et.UNPAID)), n; } function Kt(n, t) { return n.state || (t.warn( "Field 'state' not found in MintQuoteResponse. Update NUT-04 of mint: https://github.com/cashubtc/nuts/pull/141)" ), typeof n.paid == "boolean" && (n.state = n.paid ? gt.PAID : gt.UNPAID)), n; } function Ne(n, t) { return Array.isArray(n?.contact) && n?.contact.length > 0 && (n.contact = n.contact.map((e2) => Array.isArray(e2) && e2.length === 2 && typeof e2[0] == "string" && typeof e2[1] == "string" ? (t.warn( "Mint returned deprecated 'contact' field: Update NUT-06: https://github.com/cashubtc/nuts/pull/117" ), { method: e2[0], info: e2[1] }) : e2)), n; } function pt(n) { return typeof n == "function"; } var _e, Ee, Be, ve, ct, v5, F, tt, V, Ke, as, et, gt, st, Pt, St, Jt, Yt, wt, q, mt, Qe, xt, Q, Le, Ce, us; var init_cashu_ts_es = __esm({ "ndk/node_modules/@cashu/cashu-ts/lib/cashu-ts.es.js"() { init_NUT12_es(); init_common_es(); init_utils3(); init_sha2(); init_NUT09_BsylB_jy(); init_utils_CrQNeCaC(); init_NUT11_es2(); init_NUT20_es(); init_client_es(); init_utils(); _e = "A"; Ee = "cashu"; Be = class { get value() { return this._value; } set value(t) { this._value = t; } get next() { return this._next; } set next(t) { this._next = t; } constructor(t) { this._value = t, this._next = null; } }; ve = class { get first() { return this._first; } set first(t) { this._first = t; } get last() { return this._last; } set last(t) { this._last = t; } get size() { return this._size; } set size(t) { this._size = t; } constructor() { this._first = null, this._last = null, this._size = 0; } enqueue(t) { const e2 = new Be(t); return this._size === 0 || !this._last ? (this._first = e2, this._last = e2) : (this._last.next = e2, this._last = e2), this._size++, true; } dequeue() { if (this._size === 0 || !this._first) return null; const t = this._first; return this._first = t.next, t.next = null, this._size--, t.value; } }; typeof WebSocket < "u" && (ct = WebSocket); v5 = { FATAL: "FATAL", ERROR: "ERROR", WARN: "WARN", INFO: "INFO", DEBUG: "DEBUG", TRACE: "TRACE" }; F = { fatal() { }, error() { }, warn() { }, info() { }, debug() { }, trace() { }, log() { } }; tt = class tt2 { constructor(t = v5.INFO) { this.minLevel = t; } logToConsole(t, e2, s) { if (tt2.SEVERITY[t] > tt2.SEVERITY[this.minLevel]) return; const o = `[${t}] `; let r = e2; const a = /* @__PURE__ */ new Set(); if (s) { const i3 = Object.fromEntries( Object.entries(s).map(([h2, l3]) => [ h2, l3 instanceof Error ? { message: l3.message, stack: l3.stack } : l3 ]) ); r = e2.replace(/\{(\w+)\}/g, (h2, l3) => { if (l3 in i3 && i3[l3] !== void 0) { a.add(l3); const f = i3[l3]; return typeof f == "string" ? f : typeof f == "number" || typeof f == "boolean" ? f.toString() : f == null ? "" : JSON.stringify(f); } return h2; }); const c = Object.fromEntries( Object.entries(i3).filter(([h2]) => !a.has(h2)) ), u3 = this.getConsoleMethod(t); Object.keys(c).length > 0 ? u3(o + r, c) : u3(o + r); } else this.getConsoleMethod(t)(o + r); } // Note: NOT static as test suite needs to spy on the output getConsoleMethod(t) { switch (t) { case v5.FATAL: case v5.ERROR: return console.error; case v5.WARN: return console.warn; case v5.INFO: return console.info; case v5.DEBUG: return console.debug; case v5.TRACE: return console.trace; default: return console.log; } } // Interface methods fatal(t, e2) { this.logToConsole(v5.FATAL, t, e2); } error(t, e2) { this.logToConsole(v5.ERROR, t, e2); } warn(t, e2) { this.logToConsole(v5.WARN, t, e2); } info(t, e2) { this.logToConsole(v5.INFO, t, e2); } debug(t, e2) { this.logToConsole(v5.DEBUG, t, e2); } trace(t, e2) { this.logToConsole(v5.TRACE, t, e2); } log(t, e2, s) { this.logToConsole(t, e2, s); } }; tt.SEVERITY = { [v5.FATAL]: 0, [v5.ERROR]: 1, [v5.WARN]: 2, [v5.INFO]: 3, [v5.DEBUG]: 4, [v5.TRACE]: 5 }; V = class _V { constructor() { this.connectionMap = /* @__PURE__ */ new Map(); } static getInstance() { return _V.instance || (_V.instance = new _V()), _V.instance; } getConnection(t, e2) { if (this.connectionMap.has(t)) return this.connectionMap.get(t); const s = new Ke(t, e2); return this.connectionMap.set(t, s), s; } }; Ke = class { constructor(t, e2) { this.subListeners = {}, this.rpcListeners = {}, this.rpcId = 0, this.onCloseCallbacks = [], this._WS = De(), this.url = new URL(t), this.messageQueue = new ve(), this._logger = e2 ?? F; } connect() { return this.connectionPromise || (this.connectionPromise = new Promise((t, e2) => { try { this.ws = new this._WS(this.url.toString()), this.onCloseCallbacks = []; } catch (s) { e2(s instanceof Error ? s : new Error(String(s))); return; } this.ws.onopen = () => { t(); }, this.ws.onerror = () => { e2(new Error("Failed to open WebSocket")); }, this.ws.onmessage = (s) => { this.messageQueue.enqueue(s.data), this.handlingInterval || (this.handlingInterval = setInterval( this.handleNextMessage.bind(this), 0 )); }, this.ws.onclose = (s) => { this.connectionPromise = void 0, this.onCloseCallbacks.forEach((o) => o(s)); }; })), this.connectionPromise; } sendRequest(t, e2) { if (this.ws?.readyState !== 1) { if (t === "unsubscribe") return; throw this._logger.error("Attempted sendRequest, but socket was not open"), new Error("Socket not open"); } const s = this.rpcId; this.rpcId++; const o = JSON.stringify({ jsonrpc: "2.0", method: t, params: e2, id: s }); this.ws?.send(o); } /** * @deprecated Use cancelSubscription for JSONRPC compliance. */ closeSubscription(t) { this.ws?.send(JSON.stringify(["CLOSE", t])); } addSubListener(t, e2) { (this.subListeners[t] = this.subListeners[t] || []).push( e2 ); } addRpcListener(t, e2, s) { this.rpcListeners[s] = { callback: t, errorCallback: e2 }; } removeRpcListener(t) { delete this.rpcListeners[t]; } removeListener(t, e2) { if (this.subListeners[t]) { if (this.subListeners[t].length === 1) { delete this.subListeners[t]; return; } this.subListeners[t] = this.subListeners[t].filter( (s) => s !== e2 ); } } async ensureConnection() { this.ws?.readyState !== 1 && await this.connect(); } handleNextMessage() { if (this.messageQueue.size === 0) { clearInterval(this.handlingInterval), this.handlingInterval = void 0; return; } const t = this.messageQueue.dequeue(); let e2; try { if (e2 = JSON.parse(t), "result" in e2 && e2.id != null) this.rpcListeners[e2.id] && (this.rpcListeners[e2.id].callback(), this.removeRpcListener(e2.id)); else if ("error" in e2 && e2.id != null) this.rpcListeners[e2.id] && (this.rpcListeners[e2.id].errorCallback(new Error(e2.error.message)), this.removeRpcListener(e2.id)); else if ("method" in e2 && !("id" in e2)) { const s = e2.params?.subId; if (!s) return; if (this.subListeners[s]?.length > 0) { const o = e2; this.subListeners[s].forEach((r) => r(o.params?.payload)); } } } catch (s) { this._logger.error("Error doing handleNextMessage", { e: s }); return; } } createSubscription(t, e2, s) { if (this.ws?.readyState !== 1) throw this._logger.error("Attempted createSubscription, but socket was not open"), new Error("Socket is not open"); const o = (Math.random() + 1).toString(36).substring(7); return this.addRpcListener( () => { this.addSubListener(o, e2); }, s, this.rpcId ), this.sendRequest("subscribe", { ...t, subId: o }), this.rpcId++, o; } /** * Cancels a subscription, sending an unsubscribe request and handling responses. * * @param subId The subscription ID to cancel. * @param callback The original payload callback to remove. * @param errorCallback Optional callback for unsubscribe errors (defaults to logging). */ cancelSubscription(t, e2, s) { this.removeListener(t, e2), this.addRpcListener( () => { this._logger.info("Unsubscribed {subId}", { subId: t }); }, s || ((o) => this._logger.error("Unsubscribe failed", { e: o })), this.rpcId ), this.sendRequest("unsubscribe", { subId: t }); } get activeSubscriptions() { return Object.keys(this.subListeners); } close() { this.ws && this.ws?.close(); } onClose(t) { this.onCloseCallbacks.push(t); } }; as = { UNSPENT: "UNSPENT", PENDING: "PENDING", SPENT: "SPENT" }; et = { UNPAID: "UNPAID", PENDING: "PENDING", PAID: "PAID" }; gt = { UNPAID: "UNPAID", PAID: "PAID", ISSUED: "ISSUED" }; st = class _st extends Error { constructor(t, e2) { super(t), this.status = e2, this.name = "HttpResponseError", Object.setPrototypeOf(this, _st.prototype); } }; Pt = class _Pt extends Error { constructor(t) { super(t), this.name = "NetworkError", Object.setPrototypeOf(this, _Pt.prototype); } }; St = class _St extends st { constructor(t, e2) { super(e2 || "Unknown mint operation error", 400), this.code = t, this.name = "MintOperationError", Object.setPrototypeOf(this, _St.prototype); } }; Jt = {}; Yt = F; wt = class { constructor(t) { this._mintInfo = t, t.nuts[22] && (this._protectedEnpoints = { cache: {}, apiReturn: t.nuts[22].protected_endpoints.map((e2) => ({ method: e2.method, regex: new RegExp(e2.path) })) }); } isSupported(t) { switch (t) { case 4: case 5: return this.checkMintMelt(t); case 7: case 8: case 9: case 10: case 11: case 12: case 14: case 20: return this.checkGenericNut(t); case 17: return this.checkNut17(); case 15: return this.checkNut15(); default: throw new Error("nut is not supported by cashu-ts"); } } requiresBlindAuthToken(t) { if (!this._protectedEnpoints) return false; if (typeof this._protectedEnpoints.cache[t] == "boolean") return this._protectedEnpoints.cache[t]; const e2 = this._protectedEnpoints.apiReturn.some((s) => s.regex.test(t)); return this._protectedEnpoints.cache[t] = e2, e2; } checkGenericNut(t) { return this._mintInfo.nuts[t]?.supported ? { supported: true } : { supported: false }; } checkMintMelt(t) { const e2 = this._mintInfo.nuts[t]; return e2 && e2.methods.length > 0 && !e2.disabled ? { disabled: false, params: e2.methods } : { disabled: true, params: e2.methods }; } checkNut17() { return this._mintInfo.nuts[17] && this._mintInfo.nuts[17].supported.length > 0 ? { supported: true, params: this._mintInfo.nuts[17].supported } : { supported: false }; } checkNut15() { return this._mintInfo.nuts[15] && this._mintInfo.nuts[15].methods.length > 0 ? { supported: true, params: this._mintInfo.nuts[15].methods } : { supported: false }; } get contact() { return this._mintInfo.contact; } get description() { return this._mintInfo.description; } get description_long() { return this._mintInfo.description_long; } get name() { return this._mintInfo.name; } get pubkey() { return this._mintInfo.pubkey; } get nuts() { return this._mintInfo.nuts; } get version() { return this._mintInfo.version; } get motd() { return this._mintInfo.motd; } /** * Checks if the mint supports creating BOLT12 offers with a description. * * @returns True if the mint supports offers with a description, false otherwise. */ get supportsBolt12Description() { return this._mintInfo.nuts[4]?.methods.some( (t) => t.method === "bolt12" && t.options?.description === true ); } }; q = class _q { /** * @param _mintUrl Requires mint URL to create this object. * @param _customRequest If passed, use custom request implementation for network communication * with the mint. * @param [authTokenGetter] A function that is called by the CashuMint instance to obtain a NUT-22 * BlindedAuthToken (e.g. from a database or localstorage) */ constructor(t, e2, s, o) { this._mintUrl = t, this._customRequest = e2, this._checkNut22 = false, this._mintUrl = Gt(t), this._customRequest = e2, s && (this._checkNut22 = true, this._authTokenGetter = s), this._logger = o?.logger ?? F, Oe(this._logger); } //TODO: v3 - refactor CashuMint to take two or less args. get mintUrl() { return this._mintUrl; } /** * Fetches mints info at the /info endpoint. * * @param mintUrl * @param customRequest */ static async getInfo(t, e2, s) { const o = s ?? F, a = await (e2 || M)({ endpoint: S3(t, "/v1/info") }); return Ne(a, o); } /** * Fetches mints info at the /info endpoint. */ async getInfo() { return _q.getInfo(this._mintUrl, this._customRequest, this._logger); } async getLazyMintInfo() { if (this._mintInfo) return this._mintInfo; const t = await _q.getInfo(this._mintUrl, this._customRequest); return this._mintInfo = new wt(t), this._mintInfo; } /** * Performs a swap operation with ecash inputs and outputs. * * @param mintUrl * @param swapPayload Payload containing inputs and outputs. * @param customRequest * @returns Signed outputs. */ static async swap(t, e2, s, o) { const r = s || M, a = o ? { "Blind-auth": o } : {}, i3 = await r({ endpoint: S3(t, "/v1/swap"), method: "POST", requestBody: e2, headers: a }); if (!O2(i3) || !Array.isArray(i3?.signatures)) throw new Error(i3.detail ?? "bad response"); return i3; } /** * Performs a swap operation with ecash inputs and outputs. * * @param swapPayload Payload containing inputs and outputs. * @returns Signed outputs. */ async swap(t) { const e2 = await this.handleBlindAuth("/v1/swap"); return _q.swap(this._mintUrl, t, this._customRequest, e2); } /** * Requests a new mint quote from the mint. * * @param mintUrl * @param mintQuotePayload Payload for creating a new mint quote. * @param customRequest * @returns The mint will create and return a new mint quote containing a payment request for the * specified amount and unit. */ static async createMintQuote(t, e2, s, o, r) { const a = r ?? F, i3 = s || M, c = o ? { "Blind-auth": o } : {}, u3 = await i3({ endpoint: S3(t, "/v1/mint/quote/bolt11"), method: "POST", requestBody: e2, headers: c }); return Kt(u3, a); } /** * Requests a new mint quote from the mint. * * @param mintQuotePayload Payload for creating a new mint quote. * @returns The mint will create and return a new mint quote containing a payment request for the * specified amount and unit. */ async createMintQuote(t) { const e2 = await this.handleBlindAuth("/v1/mint/quote/bolt11"); return _q.createMintQuote( this._mintUrl, t, this._customRequest, e2 ); } /** * Requests a new BOLT12 mint quote from the mint using Lightning Network offers. * * @param mintUrl The mint's base URL. * @param mintQuotePayload Payload containing amount, unit, optional description, and required * pubkey. * @param customRequest Optional custom request implementation. * @param blindAuthToken Optional authentication token for NUT-22. * @returns A mint quote containing a BOLT12 offer. */ static async createMintQuoteBolt12(t, e2, s, o) { const r = s || M, a = o ? { "Blind-auth": o } : {}; return await r({ endpoint: S3(t, "/v1/mint/quote/bolt12"), method: "POST", requestBody: e2, headers: a }); } /** * Requests a new BOLT12 mint quote from the mint using Lightning Network offers. * * @param mintQuotePayload Payload containing amount, unit, optional description, and required * pubkey. * @returns A mint quote containing a BOLT12 offer. */ async createMintQuoteBolt12(t) { const e2 = await this.handleBlindAuth("/v1/mint/quote/bolt12"); return _q.createMintQuoteBolt12( this._mintUrl, t, this._customRequest, e2 ); } /** * Gets an existing mint quote from the mint. * * @param mintUrl * @param quote Quote ID. * @param customRequest * @returns The mint will create and return a Lightning invoice for the specified amount. */ static async checkMintQuote(t, e2, s, o, r) { const a = r ?? F, i3 = s || M, c = o ? { "Blind-auth": o } : {}, u3 = await i3({ endpoint: S3(t, "/v1/mint/quote/bolt11", e2), method: "GET", headers: c }); return Kt(u3, a); } /** * Gets an existing mint quote from the mint. * * @param quote Quote ID. * @returns The mint will create and return a Lightning invoice for the specified amount. */ async checkMintQuote(t) { const e2 = await this.handleBlindAuth(`/v1/mint/quote/bolt11/${t}`); return _q.checkMintQuote(this._mintUrl, t, this._customRequest, e2); } /** * Gets an existing BOLT12 mint quote from the mint. * * @param mintUrl The mint's base URL. * @param quote Quote ID to check. * @param customRequest Optional custom request implementation. * @param blindAuthToken Optional authentication token for NUT-22. * @returns Updated quote with current payment and issuance amounts. */ static async checkMintQuoteBolt12(t, e2, s, o) { const r = s || M, a = o ? { "Blind-auth": o } : {}; return await r({ endpoint: S3(t, "/v1/mint/quote/bolt12", e2), method: "GET", headers: a }); } /** * Gets an existing BOLT12 mint quote from the mint. * * @param quote Quote ID to check. * @returns Updated quote with current payment and issuance amounts. */ async checkMintQuoteBolt12(t) { const e2 = await this.handleBlindAuth(`/v1/mint/quote/bolt12/${t}`); return _q.checkMintQuoteBolt12( this._mintUrl, t, this._customRequest, e2 ); } /** * Mints new tokens by requesting blind signatures on the provided outputs. * * @param mintUrl * @param mintPayload Payload containing the outputs to get blind signatures on. * @param customRequest * @returns Serialized blinded signatures. */ static async mint(t, e2, s, o) { const r = s || M, a = o ? { "Blind-auth": o } : {}, i3 = await r({ endpoint: S3(t, "/v1/mint/bolt11"), method: "POST", requestBody: e2, headers: a }); if (!O2(i3) || !Array.isArray(i3?.signatures)) throw new Error("bad response"); return i3; } /** * Mints new tokens by requesting blind signatures on the provided outputs. * * @param mintPayload Payload containing the outputs to get blind signatures on. * @returns Serialized blinded signatures. */ async mint(t) { const e2 = await this.handleBlindAuth("/v1/mint/bolt11"); return _q.mint(this._mintUrl, t, this._customRequest, e2); } /** * Mints new tokens using a BOLT12 quote by requesting blind signatures on the provided outputs. * * @param mintUrl The mint's base URL. * @param mintPayload Payload containing the quote ID and outputs to get blind signatures on. * @param customRequest Optional custom request implementation. * @param blindAuthToken Optional authentication token for NUT-22. * @returns Serialized blinded signatures for the requested outputs. */ static async mintBolt12(t, e2, s, o) { const r = s || M, a = o ? { "Blind-auth": o } : {}, i3 = await r({ endpoint: S3(t, "/v1/mint/bolt12"), method: "POST", requestBody: e2, headers: a }); if (!O2(i3) || !Array.isArray(i3?.signatures)) throw new Error("bad response"); return i3; } /** * Mints new tokens using a BOLT12 quote by requesting blind signatures on the provided outputs. * * @param mintPayload Payload containing the quote ID and outputs to get blind signatures on. * @returns Serialized blinded signatures for the requested outputs. */ async mintBolt12(t) { const e2 = await this.handleBlindAuth("/v1/mint/bolt12"); return _q.mintBolt12(this._mintUrl, t, this._customRequest, e2); } /** * Requests a new melt quote from the mint. * * @param mintUrl * @param MeltQuotePayload * @returns */ static async createMeltQuote(t, e2, s, o, r) { const a = r ?? F, i3 = s || M, c = o ? { "Blind-auth": o } : {}, u3 = await i3({ endpoint: S3(t, "/v1/melt/quote/bolt11"), method: "POST", requestBody: e2, headers: c }), h2 = ft(u3, a); if (!O2(h2) || typeof h2?.amount != "number" || typeof h2?.fee_reserve != "number" || typeof h2?.quote != "string") throw new Error("bad response"); return h2; } /** * Requests a new melt quote from the mint. * * @param MeltQuotePayload * @returns */ async createMeltQuote(t) { const e2 = await this.handleBlindAuth("/v1/melt/quote/bolt11"); return _q.createMeltQuote( this._mintUrl, t, this._customRequest, e2 ); } /** * Requests a new BOLT12 melt quote from the mint for paying a Lightning Network offer. For * amount-less offers, specify the amount in options.amountless.amount_msat. * * @param mintUrl The mint's base URL. * @param meltQuotePayload Payload containing the BOLT12 offer to pay and unit. * @param customRequest Optional custom request implementation. * @param blindAuthToken Optional authentication token for NUT-22. * @returns Melt quote with amount, fee reserve, and payment state. */ static async createMeltQuoteBolt12(t, e2, s, o) { const r = s || M, a = o ? { "Blind-auth": o } : {}; return await r({ endpoint: S3(t, "/v1/melt/quote/bolt12"), method: "POST", requestBody: e2, headers: a }); } /** * Requests a new BOLT12 melt quote from the mint for paying a Lightning Network offer. For * amount-less offers, specify the amount in options.amountless.amount_msat. * * @param meltQuotePayload Payload containing the BOLT12 offer to pay and unit. * @returns Melt quote with amount, fee reserve, and payment state. */ async createMeltQuoteBolt12(t) { const e2 = await this.handleBlindAuth("/v1/melt/quote/bolt12"); return _q.createMeltQuoteBolt12( this._mintUrl, t, this._customRequest, e2 ); } /** * Gets an existing melt quote. * * @param mintUrl * @param quote Quote ID. * @returns */ static async checkMeltQuote(t, e2, s, o, r) { const a = r ?? F, i3 = s || M, c = o ? { "Blind-auth": o } : {}, u3 = await i3({ endpoint: S3(t, "/v1/melt/quote/bolt11", e2), method: "GET", headers: c }), h2 = ft(u3, a); if (!O2(h2) || typeof h2?.amount != "number" || typeof h2?.fee_reserve != "number" || typeof h2?.quote != "string" || typeof h2?.state != "string" || !Object.values(et).includes(h2.state)) throw new Error("bad response"); return h2; } /** * Gets an existing melt quote. * * @param quote Quote ID. * @returns */ async checkMeltQuote(t) { const e2 = await this.handleBlindAuth(`/v1/melt/quote/bolt11/${t}`); return _q.checkMeltQuote(this._mintUrl, t, this._customRequest, e2); } /** * Gets an existing BOLT12 melt quote from the mint. Returns current payment state (UNPAID, * PENDING, or PAID) and payment preimage if paid. * * @param mintUrl The mint's base URL. * @param quote Quote ID to check. * @param customRequest Optional custom request implementation. * @param blindAuthToken Optional authentication token for NUT-22. * @returns Updated quote with current payment state and preimage if available. */ static async checkMeltQuoteBolt12(t, e2, s, o) { const r = s || M, a = o ? { "Blind-auth": o } : {}; return await r({ endpoint: S3(t, "/v1/melt/quote/bolt12", e2), method: "GET", headers: a }); } /** * Gets an existing BOLT12 melt quote from the mint. Returns current payment state (UNPAID, * PENDING, or PAID) and payment preimage if paid. * * @param quote Quote ID to check. * @returns Updated quote with current payment state and preimage if available. */ async checkMeltQuoteBolt12(t) { const e2 = await this.handleBlindAuth(`/v1/melt/quote/bolt12/${t}`); return _q.checkMeltQuoteBolt12( this._mintUrl, t, this._customRequest, e2 ); } /** * Requests the mint to pay for a Bolt11 payment request by providing ecash as inputs to be spent. * The inputs contain the amount and the fee_reserves for a Lightning payment. The payload can * also contain blank outputs in order to receive back overpaid Lightning fees. * * @param mintUrl * @param meltPayload * @param customRequest * @returns */ static async melt(t, e2, s, o, r) { const a = r ?? F, i3 = s || M, c = o ? { "Blind-auth": o } : {}, u3 = await i3({ endpoint: S3(t, "/v1/melt/bolt11"), method: "POST", requestBody: e2, headers: c }), h2 = ft(u3, a); if (!O2(h2) || typeof h2?.state != "string" || !Object.values(et).includes(h2.state)) throw new Error("bad response"); return h2; } /** * Ask mint to perform a melt operation. This pays a lightning invoice and destroys tokens * matching its amount + fees. * * @param meltPayload * @returns */ async melt(t) { const e2 = await this.handleBlindAuth("/v1/melt/bolt11"); return _q.melt(this._mintUrl, t, this._customRequest, e2); } /** * Requests the mint to pay a BOLT12 offer by providing ecash inputs to be spent. The inputs must * cover the amount plus fee reserves. Optional outputs can be included to receive change for * overpaid Lightning fees. * * @param mintUrl The mint's base URL. * @param meltPayload Payload containing quote ID, inputs, and optional outputs for change. * @param customRequest Optional custom request implementation. * @param blindAuthToken Optional authentication token for NUT-22. * @returns Payment result with state and optional change signatures. */ static async meltBolt12(t, e2, s, o) { const r = s || M, a = o ? { "Blind-auth": o } : {}; return await r({ endpoint: S3(t, "/v1/melt/bolt12"), method: "POST", requestBody: e2, headers: a }); } /** * Requests the mint to pay a BOLT12 offer by providing ecash inputs to be spent. The inputs must * cover the amount plus fee reserves. Optional outputs can be included to receive change for * overpaid Lightning fees. * * @param meltPayload Payload containing quote ID, inputs, and optional outputs for change. * @returns Payment result with state and optional change signatures. */ async meltBolt12(t) { const e2 = await this.handleBlindAuth("/v1/melt/bolt12"); return _q.meltBolt12(this._mintUrl, t, this._customRequest, e2); } /** * Checks if specific proofs have already been redeemed. * * @param mintUrl * @param checkPayload * @param customRequest * @returns Redeemed and unredeemed ordered list of booleans. */ static async check(t, e2, s) { const r = await (s || M)({ endpoint: S3(t, "/v1/checkstate"), method: "POST", requestBody: e2 }); if (!O2(r) || !Array.isArray(r?.states)) throw new Error("bad response"); return r; } /** * Get the mints public keys. * * @param mintUrl * @param keysetId Optional param to get the keys for a specific keyset. If not specified, the * keys from all active keysets are fetched. * @param customRequest * @returns */ static async getKeys(t, e2, s) { e2 && (e2 = e2.replace(/\//g, "_").replace(/\+/g, "-")); const r = await (s || M)({ endpoint: e2 ? S3(t, "/v1/keys", e2) : S3(t, "/v1/keys") }); if (!O2(r) || !Array.isArray(r.keysets)) throw new Error("bad response"); return r; } /** * Get the mints public keys. * * @param keysetId Optional param to get the keys for a specific keyset. If not specified, the * keys from all active keysets are fetched. * @returns The mints public keys. */ async getKeys(t, e2) { return await _q.getKeys( e2 || this._mintUrl, t, this._customRequest ); } /** * Get the mints keysets in no specific order. * * @param mintUrl * @param customRequest * @returns All the mints past and current keysets. */ static async getKeySets(t, e2) { return (e2 || M)({ endpoint: S3(t, "/v1/keysets") }); } /** * Get the mints keysets in no specific order. * * @returns All the mints past and current keysets. */ async getKeySets() { return _q.getKeySets(this._mintUrl, this._customRequest); } /** * Checks if specific proofs have already been redeemed. * * @param checkPayload * @returns Redeemed and unredeemed ordered list of booleans. */ async check(t) { return _q.check(this._mintUrl, t, this._customRequest); } static async restore(t, e2, s) { const r = await (s || M)({ endpoint: S3(t, "/v1/restore"), method: "POST", requestBody: e2 }); if (!O2(r) || !Array.isArray(r?.outputs) || !Array.isArray(r?.signatures)) throw new Error("bad response"); return r; } async restore(t) { return _q.restore(this._mintUrl, t, this._customRequest); } /** * Tries to establish a websocket connection with the websocket mint url according to NUT-17. */ async connectWebSocket() { if (this.ws) await this.ws.ensureConnection(); else { const t = new URL(this._mintUrl), e2 = "v1/ws"; t.pathname && (t.pathname.endsWith("/") ? t.pathname += e2 : t.pathname += "/" + e2), this.ws = V.getInstance().getConnection( `${t.protocol === "https:" ? "wss" : "ws"}://${t.host}${t.pathname}` ); try { await this.ws.connect(); } catch (s) { throw this._logger.error("Failed to connect to WebSocket...", { e: s }), new Error("Failed to connect to WebSocket..."); } } } /** * Closes a websocket connection. */ disconnectWebSocket() { this.ws && this.ws.close(); } get webSocketConnection() { return this.ws; } async handleBlindAuth(t) { if (!this._checkNut22) return; if ((await this.getLazyMintInfo()).requiresBlindAuthToken(t)) { if (!this._authTokenGetter) throw new Error("Can not call a protected endpoint without authProofGetter"); return this._authTokenGetter(); } } }; mt = class { constructor(t, e2, s) { this.amount = t, this.B_ = e2, this.id = s; } getSerializedBlindedMessage() { return { amount: this.amount, B_: this.B_.toHex(true), id: this.id }; } }; Qe = /* @__PURE__ */ new Set(["locktime", "pubkeys", "n_sigs", "refund", "n_sigs_refund"]); xt = 1024; Q = class _Q { constructor(t, e2, s) { this.secret = s, this.blindingFactor = e2, this.blindedMessage = t; } toProof(t, e2) { let s; t.dleq && (s = { s: hexToBytes(t.dleq.s), e: hexToBytes(t.dleq.e), r: this.blindingFactor }); const o = { id: t.id, amount: t.amount, C_: l(t.C_) }, r = l(e2.keys[t.amount]), a = y(o, this.blindingFactor, this.secret, r); return { ...v4(a), ...s && { dleq: { s: bytesToHex(s.s), e: bytesToHex(s.e), r: Pe(s.r ?? BigInt(0)) } } }; } static createP2PKData(t, e2, s, o) { return D2(e2, s.keys, o).map((a) => this.createSingleP2PKData(t, a, s.id)); } static createSingleP2PKData(t, e2, s) { const o = Array.isArray(t.pubkey) ? t.pubkey : [t.pubkey], r = t.refundKeys ?? [], a = Math.max(1, Math.min(t.requiredSignatures ?? 1, o.length)), i3 = Math.max( 1, Math.min(t.requiredRefundSignatures ?? 1, r.length || 1) ), c = o[0], u3 = o.slice(1), h2 = r, l3 = [], f = t.locktime ?? NaN; if (Number.isSafeInteger(f) && f >= 0 && l3.push(["locktime", String(f)]), u3.length > 0 && (l3.push(["pubkeys", ...u3]), a > 1 && l3.push(["n_sigs", String(a)])), h2.length > 0 && (l3.push(["refund", ...h2]), i3 > 1 && l3.push(["n_sigs_refund", String(i3)])), t.additionalTags?.length) { const m = t.additionalTags.map(([E2, ...x2], $2) => { if (typeof E2 != "string" || !E2) throw new Error(`additionalTags[${$2}][0] must be a non empty string`); if (Qe.has(E2)) throw new Error(`additionalTags must not use reserved key "${E2}"`); return [E2, ...x2.map(String)]; }); l3.push(...m); } const d17 = [ "P2PK", { nonce: bytesToHex(randomBytes(32)), data: c, tags: l3 } ], y2 = JSON.stringify(d17), _2 = [...y2].length; if (_2 > xt) throw new Error(`Secret too long (${_2} characters), maximum is ${xt}`); const I2 = new TextEncoder().encode(y2), { r: T4, B_: b } = l2(I2); return new _Q( new mt(e2, b, s).getSerializedBlindedMessage(), T4, I2 ); } static createRandomData(t, e2, s) { return D2(t, e2.keys, s).map((r) => this.createSingleRandomData(r, e2.id)); } static createSingleRandomData(t, e2) { const s = bytesToHex(randomBytes(32)), o = new TextEncoder().encode(s), { r, B_: a } = l2(o); return new _Q( new mt(t, a, e2).getSerializedBlindedMessage(), r, o ); } static createDeterministicData(t, e2, s, o, r) { return D2(t, o.keys, r).map( (i3, c) => this.createSingleDeterministicData(i3, e2, s + c, o.id) ); } static createSingleDeterministicData(t, e2, s, o) { const r = T2(e2, o, s), a = bytesToHex(r), i3 = new TextEncoder().encode(a), c = Ae(z2(e2, o, s)), { r: u3, B_: h2 } = l2(i3, c); return new _Q( new mt(t, h2, o).getSerializedBlindedMessage(), u3, i3 ); } }; Le = 3; Ce = "sat"; us = class { /** * @param mint Cashu mint instance is used to make api calls. * @param options.unit Optionally set unit (default is 'sat') * @param options.keys Public keys from the mint (will be fetched from mint if not provided) * @param options.keysets Keysets from the mint (will be fetched from mint if not provided) * @param options.mintInfo Mint info from the mint (will be fetched from mint if not provided) * @param options.denominationTarget Target number proofs per denomination (default: see @constant * DEFAULT_DENOMINATION_TARGET) * @param options.bip39seed BIP39 seed for deterministic secrets. * @param options.keepFactory A function that will be used by all parts of the library that * produce proofs to be kept (change, etc.). This can lead to poor performance, in which case * the seed should be directly provided. */ constructor(t, e2) { this._keys = /* @__PURE__ */ new Map(), this._keysets = [], this._seed = void 0, this._unit = Ce, this._mintInfo = void 0, this._denominationTarget = Le, this.mint = t, this._logger = e2?.logger ?? F, this._logger.warn( "cashu-ts v3 has been released. Please upgrade to access the latest features. v2 is now in minimal maintenance mode." ); let s = []; if (e2?.keys && !Array.isArray(e2.keys) ? s = [e2.keys] : e2?.keys && Array.isArray(e2?.keys) && (s = e2?.keys), s && s.forEach((o) => this._keys.set(o.id, o)), e2?.unit && (this._unit = e2?.unit), e2?.keysets && (this._keysets = e2.keysets), e2?.mintInfo && (this._mintInfo = new wt(e2.mintInfo)), e2?.denominationTarget && (this._denominationTarget = e2.denominationTarget), e2?.bip39seed) { if (e2.bip39seed instanceof Uint8Array) { this._seed = e2.bip39seed; return; } throw new Error("bip39seed must be a valid UInt8Array"); } e2?.keepFactory && (this._keepFactory = e2.keepFactory); } get unit() { return this._unit; } get keys() { return this._keys; } get keysetId() { if (!this._keysetId) throw new Error("No keysetId set"); return this._keysetId; } set keysetId(t) { this._keysetId = t; } get keysets() { return this._keysets; } get mintInfo() { if (!this._mintInfo) throw new Error("Mint info not loaded"); return this._mintInfo; } /** * Get information about the mint. * * @returns Mint info. */ async getMintInfo() { const t = await this.mint.getInfo(); return this._mintInfo = new wt(t), this._mintInfo; } /** * Get stored information about the mint or request it if not loaded. * * @returns Mint info. */ async lazyGetMintInfo() { return this._mintInfo ? this._mintInfo : await this.getMintInfo(); } /** * Load mint information, keysets and keys. This function can be called if no keysets are passed * in the constructor. */ async loadMint() { await Promise.all([ this.getMintInfo(), this.getKeys() // NB: also runs getKeySets() ]); } /** * Choose a keyset to activate based on the lowest input fee. * * Note: this function will filter out deprecated base64 keysets. * * @param keysets Keysets to choose from. * @returns Active keyset. */ getActiveKeyset(t) { let e2 = t.filter((o) => o.active && o.unit === this._unit); e2 = e2.filter((o) => yt(o.id)); const s = e2.sort( (o, r) => (o.input_fee_ppk ?? 0) - (r.input_fee_ppk ?? 0) )[0]; if (!s) throw new Error("No active keyset found"); return s; } /** * Get keysets from the mint with the unit of the wallet. * * @returns Keysets with wallet's unit. */ async getKeySets() { const e2 = (await this.mint.getKeySets()).keysets.filter((s) => s.unit === this._unit); return this._keysets = e2, this._keysets; } /** * Get all active keys from the mint and set the keyset with the lowest fees as the active wallet * keyset. * * @returns Keyset. */ async getAllKeys() { const t = await this.mint.getKeys(); return t.keysets.forEach((e2) => { if (!Rt(e2)) throw new Error(`Couldn't verify keyset ID ${e2.id}`); }), this._keys = new Map(t.keysets.map((e2) => [e2.id, e2])), this.keysetId = this.getActiveKeyset(this._keysets).id, t.keysets; } /** * Get public keys from the mint. If keys were already fetched, it will return those. * * If `keysetId` is set, it will fetch and return that specific keyset. Otherwise, we select an * active keyset with the unit of the wallet. * * @param keysetId Optional keysetId to get keys for. * @param forceRefresh? If set to true, it will force refresh the keyset from the mint. * @returns Keyset. */ async getKeys(t, e2) { if ((!(this._keysets.length > 0) || e2) && await this.getKeySets(), t || (t = this.getActiveKeyset(this._keysets).id), !this._keysets.find((s) => s.id === t) && (await this.getKeySets(), !this._keysets.find((s) => s.id === t))) throw new Error(`could not initialize keys. No keyset with id '${t}' found`); if (!this._keys.get(t)) { const s = await this.mint.getKeys(t); if (!Rt(s.keysets[0])) throw new Error(`Couldn't verify keyset ID ${s.keysets[0].id}`); this._keys.set(t, s.keysets[0]); } return this.keysetId = t, this._keys.get(t); } /** * Receive an encoded or raw Cashu token (only supports single tokens. It will only process the * first token in the token array) * * @param {string | Token} token - Cashu token, either as string or decoded. * @param {ReceiveOptions} [options] - Optional configuration for token processing. * @returns New token with newly created proofs, token entries that had errors. */ async receive(t, e2) { const { requireDleq: s, keysetId: o, outputAmounts: r, counter: a, pubkey: i3, privkey: c, outputData: u3, p2pk: h2 } = e2 || {}; this._keysets.length === 0 && await this.getKeySets(), typeof t == "string" && (t = Me(t, this._keysets)); const l3 = await this.getKeys(o); if (s && t.proofs.some((b) => !Vt(b, l3))) throw new Error("Token contains proofs with invalid DLEQ"); const f = W2(t.proofs) - this.getFeesForProofs(t.proofs); let d17; u3 ? d17 = { send: u3 } : this._keepFactory && (d17 = { send: this._keepFactory }); const y2 = this.createSwapPayload( f, t.proofs, l3, r, a, i3, c, d17, h2 ), { signatures: _2 } = await this.mint.swap(y2.payload), I2 = y2.outputData.map((b, m) => b.toProof(_2[m], l3)), T4 = []; return y2.sortedIndices.forEach((b, m) => { T4[b] = I2[m]; }), T4; } /** * Send proofs of a given amount, by providing at least the required amount of proofs. * * @param amount Amount to send. * @param proofs Array of proofs (accumulated amount of proofs must be >= than amount) * @param {SendOptions} [options] - Optional parameters for configuring the send operation. * @returns {SendResponse} */ async send(t, e2, s) { const { offline: o, includeFees: r, includeDleq: a, keysetId: i3, outputAmounts: c, pubkey: u3, privkey: h2, outputData: l3 } = s || {}; if (a && (e2 = e2.filter((_2) => _2.dleq != null)), W2(e2) < t) throw new Error("Not enough funds available to send"); const { keep: f, send: d17 } = this.selectProofsToSend( e2, t, s?.includeFees ), y2 = r ? this.getFeesForProofs(d17) : 0; if (!o && (W2(d17) != t + y2 || // if the exact amount cannot be selected c || u3 || h2 || i3 || l3)) { const _2 = await this.swap(t, e2, s), { keep: I2, send: T4 } = _2, b = _2.serialized; return { keep: I2, send: T4, serialized: b }; } if (W2(d17) < t + y2) throw new Error("Not enough funds available to send"); return { keep: f, send: d17 }; } /** * Selects proofs to send based on amount and fee inclusion. * * @remarks * Uses an adapted Randomized Greedy with Local Improvement (RGLI) algorithm, which has a time * complexity O(n log n) and space complexity O(n). * @param proofs Array of Proof objects available to select from. * @param amountToSend The target amount to send. * @param includeFees Optional boolean to include fees; Default: false. * @returns SendResponse containing proofs to keep and proofs to send. * @see https://crypto.ethz.ch/publications/files/Przyda02.pdf */ selectProofsToSend(t, e2, s = false) { const h2 = Ue(); let l3 = null, f = 1 / 0, d17 = 0, y2 = 0; const _2 = (g, p5) => g - (s ? Math.ceil(p5 / 1e3) : 0), I2 = (g) => { const p5 = [...g]; for (let k2 = p5.length - 1; k2 > 0; k2--) { const w2 = Math.floor(Math.random() * (k2 + 1)); [p5[k2], p5[w2]] = [p5[w2], p5[k2]]; } return p5; }, T4 = (g, p5, k2) => { let w2 = 0, A = g.length - 1, P = null; for (; w2 <= A; ) { const L = Math.floor((w2 + A) / 2), j2 = g[L].exFee; (k2 ? j2 <= p5 : j2 >= p5) ? (P = L, k2 ? w2 = L + 1 : A = L - 1) : k2 ? A = L - 1 : w2 = L + 1; } return k2 ? P : w2 < g.length ? w2 : null; }, b = (g, p5) => { const k2 = p5.exFee; let w2 = 0, A = g.length; for (; w2 < A; ) { const P = Math.floor((w2 + A) / 2); g[P].exFee < k2 ? w2 = P + 1 : A = P; } g.splice(w2, 0, p5); }, m = (g, p5) => _2(g, p5) < e2 ? 1 / 0 : g + p5 / 1e3 - e2; let E2 = 0, x2 = 0; const $2 = t.map((g) => { const p5 = this.getProofFeePPK(g), k2 = s ? g.amount - p5 / 1e3 : g.amount, w2 = { proof: g, exFee: k2, ppkfee: p5 }; return (!s || k2 > 0) && (E2 += g.amount, x2 += p5), w2; }); let B = s ? $2.filter((g) => g.exFee > 0) : $2; if (B.sort((g, p5) => g.exFee - p5.exFee), B.length > 0) { let g; { const p5 = T4(B, e2, false); if (p5 !== null) { const k2 = B[p5].exFee, w2 = T4(B, k2, true); if (w2 === null) throw new Error("Unexpected null rightIndex in binary search"); g = w2 + 1; } else g = B.length; } for (let p5 = g; p5 < B.length; p5++) E2 -= B[p5].proof.amount, x2 -= B[p5].ppkfee; B = B.slice(0, g); } const nt = _2(E2, x2); if (e2 <= 0 || e2 > nt) return { keep: t, send: [] }; const J2 = Math.min( Math.ceil(e2 * (1 + 0 / 100)), e2 + 0, nt ); for (let g = 0; g < 60; g++) { const p5 = []; let k2 = 0, w2 = 0; for (const R2 of I2(B)) { const U2 = k2 + R2.proof.amount, K3 = w2 + R2.ppkfee, C2 = _2(U2, K3); if (p5.push(R2), k2 = U2, w2 = K3, C2 >= e2) break; } const A = new Set(p5), P = B.filter((R2) => !A.has(R2)), L = I2(Array.from({ length: p5.length }, (R2, U2) => U2)).slice( 0, 5e3 ); for (const R2 of L) { const U2 = _2(k2, w2); if (U2 === e2 || U2 >= e2 && U2 <= J2) break; const K3 = p5[R2], C2 = k2 - K3.proof.amount, z3 = w2 - K3.ppkfee, Zt = _2(C2, z3), It = e2 - Zt, ut = T4(P, It, false); if (ut !== null) { const ot = P[ut]; (It >= 0 || ot.exFee <= K3.exFee) && (p5[R2] = ot, k2 = C2 + ot.proof.amount, w2 = z3 + ot.ppkfee, P.splice(ut, 1), b(P, K3)); } } const j2 = m(k2, w2); if (j2 < f) { this._logger.debug( "selectProofsToSend: best solution found in trial #{trial} - amount: {amount}, delta: {delta}", { trial: g, amount: k2, delta: j2 } ), l3 = [...p5].sort((U2, K3) => K3.exFee - U2.exFee), f = j2, d17 = k2, y2 = w2; const R2 = [...l3]; for (; R2.length > 1 && f > 0; ) { const U2 = R2.pop(), K3 = k2 - U2.proof.amount, C2 = w2 - U2.ppkfee, z3 = m(K3, C2); if (z3 == 1 / 0) break; z3 < f && (l3 = [...R2], f = z3, d17 = K3, y2 = C2, k2 = K3, w2 = C2); } } if (l3 && f < 1 / 0) { const R2 = _2(d17, y2); if (R2 === e2 || R2 >= e2 && R2 <= J2) break; } if (h2.elapsed() > 1e3) { this._logger.warn("Proof selection took too long. Returning best selection so far."); break; } } if (l3 && f < 1 / 0) { const g = l3.map((w2) => w2.proof), p5 = new Set(g), k2 = t.filter((w2) => !p5.has(w2)); return this._logger.info("Proof selection took {time}ms", { time: h2.elapsed() }), { keep: k2, send: g }; } return { keep: t, send: [] }; } /** * Calculates the fees based on inputs (proofs) * * @param proofs Input proofs to calculate fees for. * @returns Fee amount. * @throws Throws an error if the proofs keyset is unknown. */ getFeesForProofs(t) { const e2 = t.reduce((s, o) => s + this.getProofFeePPK(o), 0); return Math.ceil(e2 / 1e3); } /** * Returns the current fee PPK for a proof according to the cached keyset. * * @param proof {Proof} A single proof. * @returns FeePPK {number} The feePPK for the selected proof. * @throws Throws an error if the proofs keyset is unknown. */ getProofFeePPK(t) { const e2 = this._keysets.find((s) => s.id === t.id); if (!e2) throw new Error(`Could not get fee. No keyset found for keyset id: ${t.id}`); return e2?.input_fee_ppk || 0; } /** * Calculates the fees based on inputs for a given keyset. * * @param nInputs Number of inputs. * @param keysetId KeysetId used to lookup `input_fee_ppk` * @returns Fee amount. */ getFeesForKeyset(t, e2) { return Math.floor( Math.max( (t * (this._keysets.find((o) => o.id === e2)?.input_fee_ppk || 0) + 999) / 1e3, 0 ) ); } /** * Splits and creates sendable tokens if no amount is specified, the amount is implied by the * cumulative amount of all proofs if both amount and preference are set, but the preference * cannot fulfill the amount, then we use the default split. * * @param {SwapOptions} [options] - Optional parameters for configuring the swap operation. * @returns Promise of the change- and send-proofs. */ async swap(t, e2, s) { let { outputAmounts: o } = s || {}; const { includeFees: r, keysetId: a, counter: i3, pubkey: c, privkey: u3, proofsWeHave: h2, outputData: l3, p2pk: f } = s || {}, d17 = await this.getKeys(a); let y2 = t; const _2 = W2(e2); let I2 = o?.sendAmounts || D2(y2, d17.keys); if (r) { let A = this.getFeesForKeyset(I2.length, d17.id), P = D2(A, d17.keys); for (; this.getFeesForKeyset(I2.concat(P).length, d17.id) > A; ) A++, P = D2(A, d17.keys); I2 = I2.concat(P), y2 += A; } const { keep: T4, send: b } = this.selectProofsToSend( e2, y2, true // inc. fees ), m = W2(b) - this.getFeesForProofs(b) - y2; if (m < 0) throw new Error("Not enough balance to send"); let E2; if (!o?.keepAmounts && !h2) E2 = D2(m, d17.keys); else if (!o?.keepAmounts && h2) E2 = Tt( h2, m, d17.keys, this._denominationTarget ); else if (o) { if (o.keepAmounts?.reduce((A, P) => A + P, 0) != m) throw new Error("Keep amounts do not match amount to keep"); E2 = o.keepAmounts; } if (y2 + this.getFeesForProofs(b) > _2) throw this._logger.error( `Not enough funds available (${_2}) for swap amountToSend: ${y2} + fee: ${this.getFeesForProofs( b )} | length: ${b.length}` ), new Error("Not enough funds available for swap"); o = { keepAmounts: E2, sendAmounts: I2 }; const x2 = l3?.keep || this._keepFactory, $2 = l3?.send, B = this.createSwapPayload( y2, b, d17, o, i3, c, u3, { keep: x2, send: $2 }, f ), { signatures: nt } = await this.mint.swap(B.payload), J2 = B.outputData.map((A, P) => A.toProof(nt[P], d17)), g = [], p5 = [], k2 = Array(B.keepVector.length), w2 = Array(J2.length); return B.sortedIndices.forEach((A, P) => { k2[A] = B.keepVector[P], w2[A] = J2[P]; }), w2.forEach((A, P) => { k2[P] ? g.push(A) : p5.push(A); }), { keep: [...g, ...T4], send: p5 }; } /** * Restores batches of deterministic proofs until no more signatures are returned from the mint. * * @param [gapLimit=300] The amount of empty counters that should be returned before restoring * ends (defaults to 300). Default is `300` * @param [batchSize=100] The amount of proofs that should be restored at a time (defaults to * 100). Default is `100` * @param [counter=0] The counter that should be used as a starting point (defaults to 0). Default * is `0` * @param [keysetId] Which keysetId to use for the restoration. If none is passed the instance's * default one will be used. */ async batchRestore(t = 300, e2 = 100, s = 0, o) { const r = Math.ceil(t / e2), a = []; let i3, c = 0; for (; c < r; ) { const u3 = await this.restore(s, e2, { keysetId: o }); u3.proofs.length > 0 ? (c = 0, a.push(...u3.proofs), i3 = u3.lastCounterWithSignature) : c++, s += e2; } return { proofs: a, lastCounterWithSignature: i3 }; } /** * Regenerates. * * @param start Set starting point for count (first cycle for each keyset should usually be 0) * @param count Set number of blinded messages that should be generated. * @param options.keysetId Set a custom keysetId to restore from. keysetIds can be loaded with * `CashuMint.getKeySets()` */ async restore(t, e2, s) { const { keysetId: o } = s || {}, r = await this.getKeys(o); if (!this._seed) throw new Error("CashuWallet must be initialized with a seed to use restore"); const a = Array(e2).fill(0), i3 = Q.createDeterministicData(0, this._seed, t, r, a), { outputs: c, signatures: u3 } = await this.mint.restore({ outputs: i3.map((d17) => d17.blindedMessage) }), h2 = {}; c.forEach((d17, y2) => h2[d17.B_] = u3[y2]); const l3 = []; let f; for (let d17 = 0; d17 < i3.length; d17++) { const y2 = h2[i3[d17].blindedMessage.B_]; y2 && (f = t + d17, i3[d17].blindedMessage.amount = y2.amount, l3.push(i3[d17].toProof(y2, r))); } return { proofs: l3, lastCounterWithSignature: f }; } /** * Requests a mint quote from the mint. Response returns a Lightning payment request for the * requested given amount and unit. * * @param amount Amount requesting for mint. * @param description Optional description for the mint quote. * @param pubkey Optional public key to lock the quote to. * @returns The mint will return a mint quote with a Lightning invoice for minting tokens of the * specified amount and unit. */ async createMintQuote(t, e2) { const s = { unit: this._unit, amount: t, description: e2 }, o = await this.mint.createMintQuote(s); return { ...o, amount: o.amount || t, unit: o.unit || this.unit }; } /** * Requests a mint quote from the mint that is locked to a public key. * * @param amount Amount requesting for mint. * @param pubkey Public key to lock the quote to. * @param description Optional description for the mint quote. * @returns The mint will return a mint quote with a Lightning invoice for minting tokens of the * specified amount and unit. The quote will be locked to the specified `pubkey`. */ async createLockedMintQuote(t, e2, s) { const { supported: o } = (await this.lazyGetMintInfo()).isSupported(20); if (!o) throw new Error("Mint does not support NUT-20"); const r = { unit: this._unit, amount: t, description: s, pubkey: e2 }, a = await this.mint.createMintQuote(r); if (typeof a.pubkey != "string") throw new Error("Mint returned unlocked mint quote"); { const i3 = a.pubkey; return { ...a, pubkey: i3, amount: a.amount || t, unit: a.unit || this.unit }; } } /** * Requests a mint quote from the mint. Response returns a Lightning BOLT12 offer for the * requested given amount and unit. * * @param pubkey Public key to lock the quote to. * @param options.amount BOLT12 offer amount requesting for mint. If not specified, the offer will * be amountless. * @param options.description Description for the mint quote. * @returns The mint will return a mint quote with a Lightning invoice for minting tokens of the * specified amount and unit. */ async createMintQuoteBolt12(t, e2) { const s = await this.lazyGetMintInfo(); if (e2?.description && !s.supportsBolt12Description) throw new Error("Mint does not support description for bolt12"); const o = { pubkey: t, unit: this._unit, amount: e2?.amount, description: e2?.description }; return this.mint.createMintQuoteBolt12(o); } async checkMintQuote(t) { const e2 = typeof t == "string" ? t : t.quote, s = await this.mint.checkMintQuote(e2); return typeof t == "string" ? s : { ...s, amount: s.amount || t.amount, unit: s.unit || t.unit }; } /** * Gets an existing BOLT12 mint quote from the mint. * * @param quote Quote ID. * @returns The latest mint quote for the given quote ID. */ async checkMintQuoteBolt12(t) { return this.mint.checkMintQuoteBolt12(t); } async mintProofs(t, e2, s) { return this._mintProofs("bolt11", t, e2, s); } /** * Mint proofs for a given mint quote. * * @param amount Amount to request. This must be less than or equal to the `quote.amountPaid - * quote.amountIssued` * @param {string} quote - ID of mint quote. * @param {string} privateKey - Private key to unlock the quote. * @param {MintProofOptions} [options] - Optional parameters for configuring the Mint Proof * operation. * @returns Proofs. */ async mintProofsBolt12(t, e2, s, o) { return this._mintProofs("bolt12", t, e2, { ...o, privateKey: s }); } /** * Requests a melt quote from the mint. Response returns amount and fees for a given unit in order * to pay a Lightning invoice. * * @param invoice LN invoice that needs to get a fee estimate. * @returns The mint will create and return a melt quote for the invoice with an amount and fee * reserve. */ async createMeltQuote(t) { const e2 = { unit: this._unit, request: t }, s = await this.mint.createMeltQuote(e2); return { ...s, unit: s.unit || this.unit, request: s.request || t }; } /** * Requests a melt quote from the mint. Response returns amount and fees for a given unit in order * to pay a BOLT12 offer. * * @param offer BOLT12 offer that needs to get a fee estimate. * @param amountMsat Amount in millisatoshis for amount-less offers. If this is defined and the * offer has an amount, they **MUST** be equal. * @returns The mint will create and return a melt quote for the offer with an amount and fee * reserve. */ async createMeltQuoteBolt12(t, e2) { return this.mint.createMeltQuoteBolt12({ unit: this._unit, request: t, options: e2 ? { amountless: { amount_msat: e2 } } : void 0 }); } /** * Requests a multi path melt quote from the mint. * * @param invoice LN invoice that needs to get a fee estimate. * @param partialAmount The partial amount of the invoice's total to be paid by this instance. * @returns The mint will create and return a melt quote for the invoice with an amount and fee * reserve. */ async createMultiPathMeltQuote(t, e2) { const { supported: s, params: o } = (await this.lazyGetMintInfo()).isSupported(15); if (!s) throw new Error("Mint does not support NUT-15"); if (!o?.some((u3) => u3.method === "bolt11" && u3.unit === this.unit)) throw new Error(`Mint does not support MPP for bolt11 and ${this.unit}`); const a = { mpp: { amount: e2 } }, i3 = { unit: this._unit, request: t, options: a }; return { ...await this.mint.createMeltQuote(i3), request: t, unit: this._unit }; } async checkMeltQuote(t) { const e2 = typeof t == "string" ? t : t.quote, s = await this.mint.checkMeltQuote(e2); return typeof t == "string" ? s : { ...s, request: t.request, unit: t.unit }; } async checkMeltQuoteBolt12(t) { return this.mint.checkMeltQuoteBolt12(t); } /** * Melt proofs for a melt quote. proofsToSend must be at least amount+fee_reserve form the melt * quote. This function does not perform coin selection!. Returns melt quote and change proofs. * * @param meltQuote ID of the melt quote. * @param proofsToSend Proofs to melt. * @param {MeltProofOptions} [options] - Optional parameters for configuring the Melting Proof * operation. * @returns */ async meltProofs(t, e2, s) { return this._meltProofs("bolt11", t, e2, s); } /** * Melt proofs for a melt quote. proofsToSend must be at least amount+fee_reserve form the melt * quote. This function does not perform coin selection!. Returns melt quote and change proofs. * * @param meltQuote ID of the melt quote. * @param proofsToSend Proofs to melt. * @param {MeltProofOptions} [options] - Optional parameters for configuring the Melting Proof * operation. * @returns */ async meltProofsBolt12(t, e2, s) { return this._meltProofs("bolt12", t, e2, s); } /** * Creates a split payload. * * @param amount Amount to send. * @param proofsToSend Proofs to split* * @param outputAmounts? Optionally specify the output's amounts to keep and to send. * @param counter? Optionally set counter to derive secret deterministically. CashuWallet class * must be initialized with seed phrase to take effect. * @param pubkey? Optionally locks ecash to pubkey. Will not be deterministic, even if counter is * set! * @param privkey? Will create a signature on the @param proofsToSend secrets if set. * @param customOutputData? Optionally specify your own OutputData (blinded messages) * @param p2pk? Optionally specify options to lock the proofs according to NUT-11. * @returns */ createSwapPayload(t, e2, s, o, r, a, i3, c, u3) { const h2 = e2.reduce((m, E2) => m + E2.amount, 0); o && o.sendAmounts && !o.keepAmounts && (o.keepAmounts = D2( h2 - t - this.getFeesForProofs(e2), s.keys )); const l3 = h2 - t - this.getFeesForProofs(e2); let f = [], d17 = []; if (c?.keep) if (pt(c.keep)) { const m = c.keep; D2(l3, s.keys).forEach((x2) => { f.push(m(x2, s)); }); } else f = c.keep; else f = this.createOutputData( l3, s, r, void 0, o?.keepAmounts, void 0, this._keepFactory ); if (c?.send) if (pt(c.send)) { const m = c.send; D2(t, s.keys).forEach((x2) => { d17.push(m(x2, s)); }); } else d17 = c.send; else d17 = this.createOutputData( t, s, r ? r + f.length : void 0, a, o?.sendAmounts, u3 ); i3 && (e2 = W(e2, i3)), e2 = at(e2), e2 = e2.map((m) => { const E2 = m.witness && typeof m.witness != "string" ? JSON.stringify(m.witness) : m.witness; return { ...m, witness: E2 }; }); const y2 = [...f, ...d17], _2 = y2.map((m, E2) => E2).sort( (m, E2) => y2[m].blindedMessage.amount - y2[E2].blindedMessage.amount ), I2 = [ ...Array.from({ length: f.length }, () => true), ...Array.from({ length: d17.length }, () => false) ], T4 = _2.map((m) => y2[m]), b = _2.map((m) => I2[m]); return { payload: { inputs: e2, outputs: T4.map((m) => m.blindedMessage) }, outputData: T4, keepVector: b, sortedIndices: _2 }; } /** * Get an array of the states of proofs from the mint (as an array of CheckStateEnum's) * * @param proofs (only the `secret` field is required) * @returns */ async checkProofsStates(t) { const e2 = new TextEncoder(), s = t.map((a) => T(e2.encode(a.secret)).toHex(true)), o = 100, r = []; for (let a = 0; a < s.length; a += o) { const i3 = s.slice(a, a + o), { states: c } = await this.mint.check({ Ys: i3 }), u3 = {}; c.forEach((h2) => { u3[h2.Y] = h2; }); for (let h2 = 0; h2 < i3.length; h2++) { const l3 = u3[i3[h2]]; if (!l3) throw new Error("Could not find state for proof with Y: " + i3[h2]); r.push(l3); } } return r; } /** * Register a callback to be called whenever a mint quote's state changes. * * @param quoteIds List of mint quote IDs that should be subscribed to. * @param callback Callback function that will be called whenever a mint quote state changes. * @param errorCallback * @returns */ async onMintQuoteUpdates(t, e2, s) { if (await this.mint.connectWebSocket(), !this.mint.webSocketConnection) throw new Error("failed to establish WebSocket connection."); const o = this.mint.webSocketConnection.createSubscription( { kind: "bolt11_mint_quote", filters: t }, e2, s ); return () => { this.mint.webSocketConnection?.cancelSubscription(o, e2); }; } /** * Register a callback to be called whenever a melt quote's state changes. * * @param quoteIds List of melt quote IDs that should be subscribed to. * @param callback Callback function that will be called whenever a melt quote state changes. * @param errorCallback * @returns */ async onMeltQuotePaid(t, e2, s) { return this.onMeltQuoteUpdates( [t], (o) => { o.state === et.PAID && e2(o); }, s ); } /** * Register a callback to be called when a single mint quote gets paid. * * @param quoteId Mint quote id that should be subscribed to. * @param callback Callback function that will be called when this mint quote gets paid. * @param errorCallback * @returns */ async onMintQuotePaid(t, e2, s) { return this.onMintQuoteUpdates( [t], (o) => { o.state === gt.PAID && e2(o); }, s ); } /** * Register a callback to be called when a single melt quote gets paid. * * @param quoteId Melt quote id that should be subscribed to. * @param callback Callback function that will be called when this melt quote gets paid. * @param errorCallback * @returns */ async onMeltQuoteUpdates(t, e2, s) { if (await this.mint.connectWebSocket(), !this.mint.webSocketConnection) throw new Error("failed to establish WebSocket connection."); const o = this.mint.webSocketConnection.createSubscription( { kind: "bolt11_melt_quote", filters: t }, e2, s ); return () => { this.mint.webSocketConnection?.cancelSubscription(o, e2); }; } /** * Register a callback to be called whenever a subscribed proof state changes. * * @param proofs List of proofs that should be subscribed to. * @param callback Callback function that will be called whenever a proof's state changes. * @param errorCallback * @returns */ async onProofStateUpdates(t, e2, s) { if (await this.mint.connectWebSocket(), !this.mint.webSocketConnection) throw new Error("failed to establish WebSocket connection."); const o = new TextEncoder(), r = {}; for (let c = 0; c < t.length; c++) { const u3 = T(o.encode(t[c].secret)).toHex(true); r[u3] = t[c]; } const a = Object.keys(r), i3 = this.mint.webSocketConnection.createSubscription( { kind: "proof_state", filters: a }, (c) => { e2({ ...c, proof: r[c.Y] }); }, s ); return () => { this.mint.webSocketConnection?.cancelSubscription(i3, e2); }; } /** * Creates blinded messages for a according to @param amounts. * * @param amount Array of amounts to create blinded messages for. * @param counter? Optionally set counter to derive secret deterministically. CashuWallet class * must be initialized with seed phrase to take effect. * @param pubkey? Optionally locks ecash to pubkey. Will not be deterministic, even if counter is * set! * @param outputAmounts? Optionally specify the output's amounts to keep and to send. * @param p2pk? Optionally specify options to lock the proofs according to NUT-11. * @param factory? Optionally specify a custom function that produces OutputData (blinded * messages) * @returns Blinded messages, secrets, rs, and amounts. */ createOutputData(t, e2, s, o, r, a, i3) { let c; if (o) c = Q.createP2PKData( { pubkey: o, additionalTags: a?.additionalTags }, t, e2, r ); else if (s || s === 0) { if (!this._seed) throw new Error("cannot create deterministic messages without seed"); c = Q.createDeterministicData( t, this._seed, s, e2, r ); } else a ? c = Q.createP2PKData(a, t, e2, r) : i3 ? c = D2(t, e2.keys).map((h2) => i3(h2, e2)) : c = Q.createRandomData(t, e2, r); return c; } /** * Creates NUT-08 blank outputs (fee returns) for a given fee reserve See: * https://github.com/cashubtc/nuts/blob/main/08.md. * * @param amount Amount to cover with blank outputs. * @param keysetId Mint keysetId. * @param counter? Optionally set counter to derive secret deterministically. CashuWallet class * must be initialized with seed phrase to take effect. * @returns Blinded messages, secrets, and rs. */ createBlankOutputs(t, e2, s, o) { let r = Math.ceil(Math.log2(t)) || 1; r < 0 && (r = 0); const a = r ? Array(r).fill(0) : []; return this.createOutputData(0, e2, s, void 0, a, void 0, o); } /** * Mints proofs for a given mint quote created with the bolt11 or bolt12 method. * * @param method Payment method of the quote. * @param amount Amount to mint. * @param quote The bolt11 or bolt12 mint quote. * @param options Optional parameters for configuring the Mint Proof operation. * @returns Proofs. */ async _mintProofs(t, e2, s, o) { let { outputAmounts: r } = o || {}; const { counter: a, pubkey: i3, p2pk: c, keysetId: u3, proofsWeHave: h2, outputData: l3, privateKey: f } = o || {}, d17 = await this.getKeys(u3); !r && h2 && (r = { keepAmounts: Tt(h2, e2, d17.keys, this._denominationTarget), sendAmounts: [] }); let y2 = []; if (l3) if (pt(l3)) { const b = D2(e2, d17.keys, r?.keepAmounts); for (let m = 0; m < b.length; m++) y2.push(l3(b[m], d17)); } else y2 = l3; else if (this._keepFactory) { const b = D2(e2, d17.keys, r?.keepAmounts); for (let m = 0; m < b.length; m++) y2.push(this._keepFactory(b[m], d17)); } else y2 = this.createOutputData( e2, d17, a, i3, r?.keepAmounts, c ); const _2 = y2.map((b) => b.blindedMessage), I2 = { outputs: _2, quote: typeof s == "string" ? s : s.quote }; if (typeof s != "string" && s.pubkey) { if (!f) throw new Error("Can not sign locked quote without private key"); I2.signature = p4(f, s.quote, _2); } if (t === "bolt12") { const { signatures: b } = await this.mint.mintBolt12(I2); return y2.map((m, E2) => m.toProof(b[E2], d17)); } const { signatures: T4 } = await this.mint.mint(I2); return y2.map((b, m) => b.toProof(T4[m], d17)); } /** * Melt proofs for a given melt quote created with the bolt11 or bolt12 method. * * @param method Payment method of the quote. * @param meltQuote The bolt11 or bolt12 melt quote. * @param proofsToSend Proofs to melt. * @param options Optional parameters for configuring the Melting Proof operation. * @returns Melt quote and change proofs. */ async _meltProofs(t, e2, s, o) { const { keysetId: r, counter: a, privkey: i3 } = o || {}, c = await this.getKeys(r), u3 = this.createBlankOutputs( W2(s) - e2.amount, c, a, this._keepFactory ); i3 != null && (s = W(s, i3)), s = at(s), s = s.map((f) => { const d17 = f.witness && typeof f.witness != "string" ? JSON.stringify(f.witness) : f.witness; return { ...f, witness: d17 }; }); const h2 = { quote: e2.quote, inputs: s, outputs: u3.map((f) => f.blindedMessage) }; if (t === "bolt12") { const f = await this.mint.meltBolt12(h2); return { quote: { ...f, unit: e2.unit, request: e2.request }, change: f.change?.map((d17, y2) => u3[y2].toProof(d17, c)) ?? [] }; } const l3 = await this.mint.melt(h2); return { quote: { ...l3, unit: e2.unit, request: e2.request }, change: l3.change?.map((f, d17) => u3[d17].toProof(f, c)) ?? [] }; } }; } }); // ndk/node_modules/zustand/esm/vanilla.mjs var createStoreImpl, createStore; var init_vanilla = __esm({ "ndk/node_modules/zustand/esm/vanilla.mjs"() { createStoreImpl = (createState) => { let state; const listeners = /* @__PURE__ */ new Set(); const setState = (partial, replace) => { const nextState = typeof partial === "function" ? partial(state) : partial; if (!Object.is(nextState, state)) { const previousState = state; state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState); listeners.forEach((listener) => listener(state, previousState)); } }; const getState = () => state; const getInitialState = () => initialState; const subscribe = (listener) => { listeners.add(listener); return () => listeners.delete(listener); }; const api = { setState, getState, getInitialState, subscribe }; const initialState = state = createState(setState, getState, api); return api; }; createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl); } }); // ndk/sync/dist/index.js function isWrappedBuffer(buf) { return buf instanceof WrappedBuffer2; } function isUint8Array(buf) { return buf instanceof Uint8Array && !isWrappedBuffer(buf); } function encodeVarInt2(n) { if (n === 0) return new Uint8Array([0]); const bytes4 = []; while (n !== 0) { bytes4.push(n & 127); n >>>= 7; } bytes4.reverse(); for (let i3 = 0; i3 < bytes4.length - 1; i3++) { bytes4[i3] |= 128; } return new Uint8Array(bytes4); } function decodeVarInt2(buf) { if (!isWrappedBuffer(buf) && !isUint8Array(buf)) { throw new Error("Invalid buffer type: expected Uint8Array or WrappedBuffer"); } let res = 0; while (true) { if (buf.length === 0) { throw new Error("VarInt decoding: unexpected end of buffer"); } const byte = shiftByte(buf); res = res << 7 | byte & 127; if ((byte & 128) === 0) break; } return res; } function getByte2(buf) { return getBytes2(buf, 1)[0]; } function getBytes2(buf, n) { if (!isWrappedBuffer(buf) && !isUint8Array(buf)) { throw new Error("Invalid buffer type: expected Uint8Array or WrappedBuffer"); } if (buf.length < n) { throw new Error("getBytes: unexpected end of buffer"); } if (isWrappedBuffer(buf)) { return buf.shiftN(n); } const result = buf.slice(0, n); return result; } function shiftByte(buf) { if (!isWrappedBuffer(buf) && !isUint8Array(buf)) { throw new Error("Invalid buffer type: expected Uint8Array or WrappedBuffer"); } if (isWrappedBuffer(buf)) { return buf.shift(); } return buf[0]; } function compareUint8Array2(a, b) { const minLength = Math.min(a.length, b.length); for (let i3 = 0; i3 < minLength; i3++) { if (a[i3] < b[i3]) return -1; if (a[i3] > b[i3]) return 1; } if (a.length < b.length) return -1; if (a.length > b.length) return 1; return 0; } function hexToUint8Array(hex2) { if (hex2.startsWith("0x")) hex2 = hex2.slice(2); if (hex2.length % 2 !== 0) { throw new Error("Hex string has odd length"); } const arr = new Uint8Array(hex2.length / 2); for (let i3 = 0; i3 < arr.length; i3++) { arr[i3] = Number.parseInt(hex2.slice(i3 * 2, i3 * 2 + 2), 16); } return arr; } function uint8ArrayToHex(arr) { let out = ""; for (let i3 = 0; i3 < arr.length; i3++) { out += hexLookupTable[arr[i3]]; } return out; } function itemCompare2(a, b) { if (a.timestamp !== b.timestamp) { return a.timestamp - b.timestamp; } return compareUint8Array2(a.id, b.id); } function isNegMessage(message) { return Array.isArray(message) && message.length >= 2 && typeof message[0] === "string" && typeof message[1] === "string"; } function isNegMsgWithPayload(message) { return isNegMessage(message) && message.length === 3 && typeof message[2] === "string"; } function hasWebSocketConnectivity(relay) { return "connectivity" in relay && relay.connectivity && typeof relay.connectivity.send === "function"; } async function ndkSync(filters, opts = {}) { if (!this.cacheAdapter) { throw new Error("NDK sync requires a cache adapter. Configure NDK with cacheAdapter option."); } const filterArray = Array.isArray(filters) ? filters : [filters]; const relaySet = getRelaySet.call(this, opts); const relays = Array.from(relaySet.relays); const result = { events: [], need: /* @__PURE__ */ new Set(), have: /* @__PURE__ */ new Set() }; const syncPromises = relays.map(async (relay) => { if (!relay.connected) { await new Promise((resolve) => { const onReady = () => { relay.off("ready", onReady); resolve(); }; relay.once("ready", onReady); setTimeout(() => { relay.off("ready", onReady); resolve(); }, TIMEOUTS.RELAY_CONNECTION); }); } if (!relay.connected) { console.warn(`[NDK Sync] Relay ${relay.url} did not connect in time, skipping`); return; } try { await syncWithRelay.call(this, relay, filterArray, opts, result); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`[NDK Sync] Failed to sync with relay ${relay.url}: ${errorMessage}`); if (opts.onRelayError) { await opts.onRelayError(relay, error instanceof Error ? error : new Error(String(error))); } } }); await Promise.all(syncPromises); return result; } function getRelaySet(opts) { if (opts.relaySet) { return opts.relaySet; } if (opts.relayUrls) { return NDKRelaySet2.fromRelayUrls(opts.relayUrls, this); } const poolRelays = this.pool?.relays; if (!poolRelays || poolRelays.size === 0) { throw new Error("No relays available for sync"); } const relaySet_ = new Set(poolRelays.values()); return new NDKRelaySet2(relaySet_, this); } async function syncWithRelay(relay, filterArray, opts, result) { try { const cachedEvents = await queryCache.call(this, filterArray); const storage = NegentropyStorage.fromEvents(cachedEvents); const session = new SyncSession(relay, filterArray, storage, opts); if (opts.onNegotiationProgress) { session.on("progress", (progress) => { opts.onNegotiationProgress?.(relay, progress); }); } const { need, have } = await session.start(); for (const id of need) result.need.add(id); for (const id of have) result.have.add(id); if (opts.autoFetch !== false && need.size > 0) { if (opts.onNegotiationProgress) { opts.onNegotiationProgress(relay, { phase: "fetching", round: 0, needCount: need.size, haveCount: have.size, messageSize: 0, timestamp: Date.now() }); } const events = await fetchNeededEvents.call(this, relay, need); result.events.push(...events); if (this.cacheAdapter) { await saveFetchedEventsToCache.call(this, events, filterArray, relay); } } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Sync failed with relay ${relay.url}: ${errorMessage}`); } } async function saveFetchedEventsToCache(events, filterArray, relay) { if (!this.cacheAdapter) return; for (const event of events) { await this.cacheAdapter.setEvent(event, filterArray, relay); } } async function queryCache(filters) { if (!this.cacheAdapter) return []; const events = []; return new Promise((resolve) => { const sub = this.subscribe(filters, { cacheUsage: NDKSubscriptionCacheUsage2.ONLY_CACHE, closeOnEose: true, // Batch handler for cached events (O(n) performance) onEvents: (cachedEvents) => { events.push(...cachedEvents); }, // Individual handler for any stragglers onEvent: (event) => { events.push(event); }, onEose: () => { resolve(events); } }); setTimeout(() => { sub.stop(); resolve(events); }, TIMEOUTS.CACHE_QUERY); }); } async function fetchNeededEvents(relay, need) { const events = []; const relaySet = new NDKRelaySet2(/* @__PURE__ */ new Set([relay]), this); return new Promise((resolve) => { const sub = this.subscribe( { ids: Array.from(need) }, { closeOnEose: true, relaySet, exclusiveRelay: true, groupable: false, onEvent: (event) => { events.push(event); }, onEose: () => { resolve(events); } } ); setTimeout(() => { sub.stop(); resolve(events); }, TIMEOUTS.EVENT_FETCH); }); } async function supportsNegentropy(relay) { try { const info = typeof relay === "string" ? await fetchRelayInformation2(relay) : await relay.fetchInfo(); return info.supported_nips?.includes(77) ?? false; } catch (_error) { return false; } } var import_tseep20, FRAME_SIZE_LIMITS, TIMEOUTS, RANGE_SPLITTING, BUFFER_SIZES, PROTOCOL_VERSION2, ID_SIZE2, FINGERPRINT_SIZE2, hexLookupTable, WrappedBuffer2, Accumulator2, NegentropyStorage, Negentropy2, SyncSession, NDKSync; var init_dist2 = __esm({ "ndk/sync/dist/index.js"() { "use strict"; init_dist(); init_dist(); import_tseep20 = __toESM(require_lib(), 1); init_dist(); init_dist(); FRAME_SIZE_LIMITS = { /** Minimum allowed frame size limit (4KB) */ MINIMUM: 4096, /** Default frame size limit (50KB) */ DEFAULT: 5e4 }; TIMEOUTS = { /** * Default sync session timeout (5 seconds). * Most relays respond immediately with NOTICE if they don't support negentropy, * so we only need a short timeout for relays that silently ignore unknown messages. */ SYNC_SESSION: 5e3, /** Cache query timeout (5 seconds) */ CACHE_QUERY: 5e3, /** Event fetch timeout (10 seconds) */ EVENT_FETCH: 1e4, /** Relay connection timeout (30 seconds) */ RELAY_CONNECTION: 3e4 }; RANGE_SPLITTING = { /** Number of buckets to split large ranges into */ BUCKET_COUNT: 16, /** Minimum elements per bucket before switching to ID list mode */ MIN_ELEMENTS_FOR_BUCKETS: 32 // buckets * 2 }; BUFFER_SIZES = { /** Default initial size for WrappedBuffer (512 bytes) */ DEFAULT_WRAPPED_BUFFER_SIZE: 512, /** Frame size limit safety margin (200 bytes) */ FRAME_SIZE_SAFETY_MARGIN: 200 }; PROTOCOL_VERSION2 = 97; ID_SIZE2 = 32; FINGERPRINT_SIZE2 = 16; hexLookupTable = new Array(256); { const hexAlphabet = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]; for (let i3 = 0; i3 < 256; i3++) { hexLookupTable[i3] = hexAlphabet[i3 >>> 4 & 15] + hexAlphabet[i3 & 15]; } } WrappedBuffer2 = class _WrappedBuffer { constructor(buffer) { if (typeof buffer === "number") { this._raw = new Uint8Array(buffer); this.length = 0; } else if (buffer) { this._raw = new Uint8Array(buffer); this.length = buffer.length; } else { this._raw = new Uint8Array(BUFFER_SIZES.DEFAULT_WRAPPED_BUFFER_SIZE); this.length = 0; } } /** * Get the underlying buffer (sliced to actual length). */ unwrap() { return this._raw.subarray(0, this.length); } /** * Get the capacity of the internal buffer. */ get capacity() { return this._raw.byteLength; } /** * Append data to the buffer, growing if necessary. */ append(buf) { const data = buf instanceof _WrappedBuffer ? buf.unwrap() : buf; const targetSize = data.length + this.length; if (this.capacity < targetSize) { const oldRaw = this._raw; const newCapacity = Math.max(this.capacity * 2, targetSize); this._raw = new Uint8Array(newCapacity); this._raw.set(oldRaw); } this._raw.set(data, this.length); this.length += data.length; } /** * Set data at the beginning, replacing current content. */ set(data) { const arr = data instanceof Uint8Array ? data : new Uint8Array(data); if (this.capacity < arr.length) { this._raw = new Uint8Array(Math.max(this.capacity * 2, arr.length)); } this._raw.set(arr); this.length = arr.length; } /** * Remove and return the first byte. */ shift() { if (this.length === 0) { throw new Error("Cannot shift from empty buffer"); } const first = this._raw[0]; this._raw = this._raw.subarray(1); this.length--; return first; } /** * Remove and return the first N bytes. */ shiftN(n) { if (this.length < n) { throw new Error("Cannot shift more bytes than available"); } const result = this._raw.subarray(0, n); this._raw = this._raw.subarray(n); this.length -= n; return result; } /** * Clear the buffer. */ clear() { this.length = 0; } }; Accumulator2 = class { constructor() { this.buf = new Uint8Array(ID_SIZE2); this.setToZero(); } /** * Set the accumulator to zero. */ setToZero() { this.buf = new Uint8Array(ID_SIZE2); } /** * Add another buffer to this accumulator. * This is a 256-bit addition with carry. */ add(otherBuf) { let currCarry = 0; let nextCarry = 0; const p5 = new DataView(this.buf.buffer); const po = new DataView(otherBuf.buffer); for (let i3 = 0; i3 < 8; i3++) { const offset = i3 * 4; const orig = p5.getUint32(offset, true); const otherV = po.getUint32(offset, true); let next = orig; next += currCarry; next += otherV; if (next > 4294967295) nextCarry = 1; p5.setUint32(offset, next & 4294967295, true); currCarry = nextCarry; nextCarry = 0; } } /** * Negate the accumulator (two's complement). */ negate() { const p5 = new DataView(this.buf.buffer); for (let i3 = 0; i3 < 8; i3++) { const offset = i3 * 4; p5.setUint32(offset, ~p5.getUint32(offset, true), true); } const one = new Uint8Array(ID_SIZE2); one[0] = 1; this.add(one); } /** * Get the fingerprint from this accumulator. * The fingerprint is SHA256(accumulator || n) truncated to FINGERPRINT_SIZE. * * @param n The number of items this fingerprint represents * @returns The fingerprint bytes */ async getFingerprint(n) { const input = new Uint8Array(this.buf.length + encodeVarInt2(n).length); input.set(this.buf); input.set(encodeVarInt2(n), this.buf.length); const hash3 = await this.sha256(input); return hash3.subarray(0, FINGERPRINT_SIZE2); } /** * SHA256 hash function that works in both Node.js and browser. */ async sha256(data) { if (crypto?.subtle) { const hashBuffer = await crypto.subtle.digest("SHA-256", data.buffer); return new Uint8Array(hashBuffer); } try { const nodeCrypto = await import("crypto"); return new Uint8Array(nodeCrypto.createHash("sha256").update(data).digest()); } catch { throw new Error("No SHA256 implementation available"); } } }; NegentropyStorage = class _NegentropyStorage { constructor() { this.items = []; this.sealed = false; } /** * Creates storage from an array of NDK events. */ static fromEvents(events) { const storage = new _NegentropyStorage(); for (const event of events) { storage.insert(event.created_at || 0, event.id); } storage.seal(); return storage; } /** * Insert an item into the storage. * @param timestamp Unix timestamp * @param id Event ID (32-byte hex string or Uint8Array) */ insert(timestamp, id) { if (this.sealed) { throw new Error("Storage is sealed, cannot insert"); } const idBytes = typeof id === "string" ? hexToUint8Array(id) : id; if (idBytes.length !== ID_SIZE2) { throw new Error(`Invalid ID size: expected ${ID_SIZE2}, got ${idBytes.length}`); } this.items.push({ timestamp, id: idBytes }); } /** * Seal the storage. * This sorts the items and checks for duplicates. * After sealing, no more items can be inserted. */ seal() { if (this.sealed) { throw new Error("Storage is already sealed"); } this.sealed = true; this.items.sort(itemCompare2); for (let i3 = 1; i3 < this.items.length; i3++) { if (itemCompare2(this.items[i3 - 1], this.items[i3]) === 0) { throw new Error("Duplicate item in storage"); } } } /** * Unseal the storage to allow modifications. */ unseal() { this.sealed = false; } /** * Get the number of items in storage. */ size() { this.checkSealed(); return this.items.length; } /** * Get an item at a specific index. */ getItem(i3) { this.checkSealed(); if (i3 >= this.items.length) { throw new Error("Index out of range"); } return this.items[i3]; } /** * Iterate over items in a range. * @param begin Start index (inclusive) * @param end End index (exclusive) * @param cb Callback for each item, return false to stop iteration */ iterate(begin, end, cb) { this.checkSealed(); this.checkBounds(begin, end); for (let i3 = begin; i3 < end; i3++) { if (!cb(this.items[i3], i3)) break; } } /** * Find the lower bound index for a given bound. * Returns the index of the first item >= bound. */ findLowerBound(begin, end, bound) { this.checkSealed(); this.checkBounds(begin, end); return this.binarySearch(this.items, begin, end, (a) => itemCompare2(a, bound) < 0); } /** * Compute a fingerprint for a range of items. * @param begin Start index (inclusive) * @param end End index (exclusive) * @returns Fingerprint bytes */ async fingerprint(begin, end) { const accumulator = new Accumulator2(); accumulator.setToZero(); this.iterate(begin, end, (item) => { accumulator.add(item.id); return true; }); return await accumulator.getFingerprint(end - begin); } /** * Check that storage is sealed. */ checkSealed() { if (!this.sealed) { throw new Error("Storage is not sealed"); } } /** * Check that begin/end are valid. */ checkBounds(begin, end) { if (begin > end || end > this.items.length) { throw new Error("Invalid range"); } } /** * Binary search to find first index where predicate is false. */ binarySearch(arr, first, last, cmp2) { let count = last - first; while (count > 0) { let it2 = first; const step = Math.floor(count / 2); it2 += step; if (cmp2(arr[it2])) { first = ++it2; count -= step + 1; } else { count = step; } } return first; } }; Negentropy2 = class { constructor(storage, frameSizeLimit = 0) { this.lastTimestampIn = 0; this.lastTimestampOut = 0; this.isInitiator = false; if (frameSizeLimit !== 0 && frameSizeLimit < FRAME_SIZE_LIMITS.MINIMUM) { throw new Error(`frameSizeLimit too small (minimum ${FRAME_SIZE_LIMITS.MINIMUM} bytes)`); } this.storage = storage; this.frameSizeLimit = frameSizeLimit; } /** * Create a bound object. */ bound(timestamp, id) { return { timestamp, id: id || new Uint8Array(0) }; } /** * Initiate a sync session. * Returns the initial message to send to the server. */ async initiate() { if (this.isInitiator) { throw new Error("Already initiated"); } this.isInitiator = true; const output4 = new WrappedBuffer2(); output4.set([PROTOCOL_VERSION2]); await this.splitRange(0, this.storage.size(), this.bound(Number.MAX_VALUE), output4); return output4.unwrap(); } /** * Set this instance as the initiator (client). */ setInitiator() { this.isInitiator = true; } /** * Process a message from the server. * Returns the next message to send, or undefined if sync is complete. * Also returns arrays of IDs we have and need. */ async reconcile(query3) { const haveIds = []; const needIds = []; const queryBuf = new WrappedBuffer2(query3); this.lastTimestampIn = this.lastTimestampOut = 0; const fullOutput = new WrappedBuffer2(); fullOutput.set([PROTOCOL_VERSION2]); const versionCheckResult = this.validateProtocolVersion(queryBuf); if (!versionCheckResult.isValid) { return { nextMessage: versionCheckResult.output.unwrap(), have: haveIds, need: needIds }; } const storageSize = this.storage.size(); let prevBound = this.bound(0); let prevIndex = 0; let skip = false; while (queryBuf.length !== 0) { const o = new WrappedBuffer2(); const doSkip = () => { if (skip) { skip = false; o.append(this.encodeBound(prevBound)); o.append(encodeVarInt2( 0 /* Skip */ )); } }; const currBound = this.decodeBound(queryBuf); const mode = queryBuf.length === 0 ? 0 : decodeVarInt2(queryBuf); const lower = prevIndex; const upper = this.storage.findLowerBound(prevIndex, storageSize, currBound); const modeResult = await this.processByMode( mode, lower, upper, currBound, queryBuf, o, fullOutput, doSkip, haveIds, needIds ); if (modeResult.shouldSkip) { skip = true; } if (this.exceededFrameSizeLimit(fullOutput.length + o.length)) { const remainingFingerprint = await this.storage.fingerprint(upper, storageSize); fullOutput.append(this.encodeBound(this.bound(Number.MAX_VALUE))); fullOutput.append(encodeVarInt2( 1 /* Fingerprint */ )); fullOutput.append(remainingFingerprint); break; } if (!modeResult.outputAlreadyAppended) { fullOutput.append(o.unwrap()); } prevIndex = upper; prevBound = currBound; } return { nextMessage: fullOutput.length === 1 && this.isInitiator ? void 0 : fullOutput.unwrap(), have: haveIds, need: needIds }; } /** * Validate the protocol version from the query buffer. */ validateProtocolVersion(queryBuf) { const output4 = new WrappedBuffer2(); output4.set([PROTOCOL_VERSION2]); const protocolVersion = getByte2(queryBuf); if (protocolVersion < 96 || protocolVersion > 111) { throw new Error("Invalid negentropy protocol version byte"); } if (protocolVersion !== PROTOCOL_VERSION2) { if (this.isInitiator) { throw new Error(`Unsupported negentropy protocol version requested: ${protocolVersion - 96}`); } return { isValid: false, output: output4 }; } return { isValid: true, output: output4 }; } /** * Process a range based on the operation mode. */ async processByMode(mode, lower, upper, currBound, queryBuf, o, fullOutput, doSkip, haveIds, needIds) { if (mode === 0) { return this.handleModeSkip(); } else if (mode === 1) { return await this.handleModeFingerprint(lower, upper, currBound, queryBuf, o, doSkip); } else if (mode === 2) { return await this.handleModeIdList( lower, upper, currBound, queryBuf, o, fullOutput, doSkip, haveIds, needIds ); } else { throw new Error("Unexpected mode"); } } /** * Handle Skip mode operation. */ handleModeSkip() { return { shouldSkip: true, outputAlreadyAppended: false }; } /** * Handle Fingerprint mode operation. */ async handleModeFingerprint(lower, upper, currBound, queryBuf, o, doSkip) { const theirFingerprint = getBytes2(queryBuf, FINGERPRINT_SIZE2); const ourFingerprint = await this.storage.fingerprint(lower, upper); if (compareUint8Array2(theirFingerprint, ourFingerprint) !== 0) { doSkip(); await this.splitRange(lower, upper, currBound, o); return { shouldSkip: false, outputAlreadyAppended: false }; } else { return { shouldSkip: true, outputAlreadyAppended: false }; } } /** * Handle IdList mode operation. */ async handleModeIdList(lower, upper, currBound, queryBuf, o, fullOutput, doSkip, haveIds, needIds) { const numIds = decodeVarInt2(queryBuf); const theirElems = /* @__PURE__ */ new Map(); for (let i3 = 0; i3 < numIds; i3++) { const e2 = getBytes2(queryBuf, ID_SIZE2); const key = Array.from(e2).join(","); theirElems.set(key, e2); } if (this.isInitiator) { return this.handleIdListAsInitiator(lower, upper, theirElems, haveIds, needIds); } else { return await this.handleIdListAsResponder(lower, upper, currBound, o, fullOutput, doSkip); } } /** * Handle IdList mode when we are the initiator. */ handleIdListAsInitiator(lower, upper, theirElems, haveIds, needIds) { this.storage.iterate(lower, upper, (item) => { const k2 = Array.from(item.id).join(","); if (!theirElems.has(k2)) { haveIds.push(item.id); } else { theirElems.delete(k2); } return true; }); for (const v6 of theirElems.values()) { needIds.push(v6); } return { shouldSkip: true, outputAlreadyAppended: false }; } /** * Handle IdList mode when we are the responder. */ async handleIdListAsResponder(lower, upper, currBound, o, fullOutput, doSkip) { doSkip(); const responseIds = new WrappedBuffer2(); let numResponseIds = 0; let endBound = currBound; this.storage.iterate(lower, upper, (item, _index) => { if (this.exceededFrameSizeLimit(fullOutput.length + responseIds.length)) { endBound = item; return false; } responseIds.append(item.id); numResponseIds++; return true; }); o.append(this.encodeBound(endBound)); o.append(encodeVarInt2( 2 /* IdList */ )); o.append(encodeVarInt2(numResponseIds)); o.append(responseIds.unwrap()); fullOutput.append(o.unwrap()); o.clear(); return { shouldSkip: false, outputAlreadyAppended: true }; } /** * Split a range into sub-ranges. * Either send fingerprints for buckets or an ID list if small enough. */ async splitRange(lower, upper, upperBound, o) { const numElems = upper - lower; const buckets = RANGE_SPLITTING.BUCKET_COUNT; if (numElems < RANGE_SPLITTING.MIN_ELEMENTS_FOR_BUCKETS) { o.append(this.encodeBound(upperBound)); o.append(encodeVarInt2( 2 /* IdList */ )); o.append(encodeVarInt2(numElems)); this.storage.iterate(lower, upper, (item) => { o.append(item.id); return true; }); } else { const itemsPerBucket = Math.floor(numElems / buckets); const bucketsWithExtra = numElems % buckets; let curr = lower; for (let i3 = 0; i3 < buckets; i3++) { const bucketSize = itemsPerBucket + (i3 < bucketsWithExtra ? 1 : 0); const ourFingerprint = await this.storage.fingerprint(curr, curr + bucketSize); curr += bucketSize; let nextBound; if (curr === upper) { nextBound = upperBound; } else { let prevItem; let currItem; this.storage.iterate(curr - 1, curr + 1, (item, index) => { if (index === curr - 1) prevItem = item; else currItem = item; return true; }); if (!prevItem || !currItem) { throw new Error(`Failed to get items at index ${curr - 1} and ${curr} for bound calculation`); } nextBound = this.getMinimalBound(prevItem, currItem); } o.append(this.encodeBound(nextBound)); o.append(encodeVarInt2( 1 /* Fingerprint */ )); o.append(ourFingerprint); } } } /** * Check if we've exceeded the frame size limit. */ exceededFrameSizeLimit(n) { return this.frameSizeLimit > 0 && n > this.frameSizeLimit - BUFFER_SIZES.FRAME_SIZE_SAFETY_MARGIN; } // Decoding decodeTimestampIn(encoded) { let timestamp = decodeVarInt2(encoded); timestamp = timestamp === 0 ? Number.MAX_VALUE : timestamp - 1; if (this.lastTimestampIn === Number.MAX_VALUE || timestamp === Number.MAX_VALUE) { this.lastTimestampIn = Number.MAX_VALUE; return Number.MAX_VALUE; } timestamp += this.lastTimestampIn; this.lastTimestampIn = timestamp; return timestamp; } decodeBound(encoded) { const timestamp = this.decodeTimestampIn(encoded); const len = decodeVarInt2(encoded); if (len > ID_SIZE2) { throw new Error("Bound key too long"); } const id = new Uint8Array(ID_SIZE2); const encodedId = getBytes2(encoded, Math.min(len, encoded.length)); id.set(encodedId); return { timestamp, id }; } // Encoding encodeTimestampOut(timestamp) { if (timestamp === Number.MAX_VALUE) { this.lastTimestampOut = Number.MAX_VALUE; return encodeVarInt2(0); } const temp = timestamp; timestamp -= this.lastTimestampOut; this.lastTimestampOut = temp; return encodeVarInt2(timestamp + 1); } encodeBound(key) { const tsBytes = this.encodeTimestampOut(key.timestamp); const idLenBytes = encodeVarInt2(key.id.length); const output4 = new Uint8Array(tsBytes.length + idLenBytes.length + key.id.length); output4.set(tsBytes); output4.set(idLenBytes, tsBytes.length); output4.set(key.id, tsBytes.length + idLenBytes.length); return output4; } getMinimalBound(prev, curr) { if (curr.timestamp !== prev.timestamp) { return this.bound(curr.timestamp); } let sharedPrefixBytes = 0; for (let i3 = 0; i3 < ID_SIZE2; i3++) { if (curr.id[i3] !== prev.id[i3]) break; sharedPrefixBytes++; } return this.bound(curr.timestamp, curr.id.subarray(0, sharedPrefixBytes + 1)); } }; SyncSession = class extends import_tseep20.EventEmitter { constructor(relay, filters, storage, opts) { super(); this.need = /* @__PURE__ */ new Set(); this.have = /* @__PURE__ */ new Set(); this.active = false; this.roundNumber = 0; this.relay = relay; this.filters = filters; this.sessionId = this.generateSessionId(); this.opts = opts; this.negentropy = new Negentropy2(storage, opts.frameSizeLimit || FRAME_SIZE_LIMITS.DEFAULT); this.setupRelayMonitoring(); } /** * Start the sync session. */ async start() { if (this.active) { throw new Error("Sync session already active"); } this.active = true; this.relay.registerProtocolHandler("NEG-MSG", this.handleNegMsg.bind(this)); this.relay.registerProtocolHandler("NEG-ERR", this.handleNegErr.bind(this)); this.relay.registerProtocolHandler("NEG-CLOSE", this.handleNegClose.bind(this)); this.relay.on("notice", this.handleNotice.bind(this)); try { this.emitProgress("initiating", 0); const initialMsg = await this.negentropy.initiate(); const message = JSON.stringify(["NEG-OPEN", this.sessionId, this.filters, uint8ArrayToHex(initialMsg)]); await this.sendRaw(message); this.roundNumber++; return await new Promise((resolve, reject) => { this.once("complete", resolve); this.once("error", reject); setTimeout(() => { if (this.active) { this.cleanup(); reject(new Error("Sync session timeout")); } }, this.opts.timeout || TIMEOUTS.SYNC_SESSION); }); } catch (error) { this.cleanup(); throw error; } } /** * Handle NEG-MSG message from relay. */ async handleNegMsg(_relay, message) { try { if (!isNegMsgWithPayload(message)) { throw new Error("Invalid NEG-MSG format: expected [string, string, string]"); } const [, id, payload] = message; if (id !== this.sessionId) return; const query3 = hexToUint8Array(payload); const querySize = query3.length; const result = await this.negentropy.reconcile(query3); for (const id2 of result.need) { this.need.add(uint8ArrayToHex(id2)); } for (const id2 of result.have) { this.have.add(uint8ArrayToHex(id2)); } this.emitProgress("reconciling", querySize); if (result.nextMessage) { this.roundNumber++; const msg = JSON.stringify(["NEG-MSG", this.sessionId, uint8ArrayToHex(result.nextMessage)]); await this.sendRaw(msg); } else { this.emitProgress("closing", 0); const closeMsg = JSON.stringify(["NEG-CLOSE", this.sessionId]); await this.sendRaw(closeMsg); this.complete(); } } catch (error) { this.error(error instanceof Error ? error : new Error(String(error))); } } /** * Handle NEG-ERR message from relay. */ handleNegErr(_relay, message) { if (!isNegMsgWithPayload(message)) { this.error(new Error("Invalid NEG-ERR format: expected [string, string, string]")); return; } const [, id, errorMsg] = message; if (id !== this.sessionId) return; this.error(new Error(`Relay sync error: ${errorMsg}`)); } /** * Handle NEG-CLOSE message from relay. */ handleNegClose(_relay, message) { if (!isNegMessage(message)) { this.error(new Error("Invalid NEG-CLOSE format: expected [string, string]")); return; } const [, id] = message; if (id !== this.sessionId) return; this.complete(); } /** * Handle NOTICE message from relay. * Relays often send NOTICE for unsupported protocol messages, including negentropy. */ handleNotice(noticeText) { if (typeof noticeText !== "string") { return; } const lowerNotice = noticeText.toLowerCase(); const isNegentropyError = lowerNotice.includes("negentropy") || lowerNotice.includes("bad msg") || lowerNotice.includes("bad message") || lowerNotice.includes("unknown") && lowerNotice.includes("msg") || lowerNotice.includes("unsupported") && lowerNotice.includes("protocol"); if (isNegentropyError) { this.error(new Error(`Relay does not support negentropy: ${noticeText}`)); } } /** * Complete the sync session successfully. */ complete() { if (!this.active) return; this.cleanup(); this.emit("complete", { need: this.need, have: this.have }); } /** * Error out the sync session. */ error(error) { if (!this.active) return; this.cleanup(); this.emit("error", error); } /** * Clean up the session. */ cleanup() { this.active = false; this.relay.unregisterProtocolHandler("NEG-MSG"); this.relay.unregisterProtocolHandler("NEG-ERR"); this.relay.unregisterProtocolHandler("NEG-CLOSE"); this.relay.off("notice", this.handleNotice); this.relay.off("disconnect", this.handleRelayDisconnect); } /** * Set up monitoring for relay disconnections. */ setupRelayMonitoring() { this.relay.once("disconnect", this.handleRelayDisconnect.bind(this)); } /** * Handle relay disconnection during sync. */ handleRelayDisconnect() { if (this.active) { this.error(new Error("Relay disconnected during sync session")); } } /** * Send a raw message to the relay. */ async sendRaw(message) { const relayUrl = this.relay.url; if (!this.relay.connected) { throw new Error(`Relay ${relayUrl} is not connected`); } if (!hasWebSocketConnectivity(this.relay)) { throw new Error(`Relay ${relayUrl} does not support direct message sending`); } try { await this.relay.connectivity.send(message); } catch (error) { throw new Error( `Failed to send message to relay ${relayUrl}: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Generate a unique session ID. */ generateSessionId() { return `neg-${Math.random().toString(36).substring(2, 15)}`; } /** * Emit progress update */ emitProgress(phase, messageSize) { const progress = { phase, round: this.roundNumber, needCount: this.need.size, haveCount: this.have.size, messageSize, timestamp: Date.now() }; this.emit("progress", progress); } }; NDKSync = class _NDKSync { // 1 hour constructor(ndk) { this.CAPABILITY_CACHE_TTL = 36e5; this.ndk = ndk; } /** * Check if a relay supports Negentropy * Uses persistent cache if available */ async checkRelaySupport(relay) { const status = await this.ndk.cacheAdapter?.getRelayStatus?.(relay.url); const syncMeta = status?.metadata?.sync; const now2 = Date.now(); if (syncMeta?.lastChecked && now2 - syncMeta.lastChecked < this.CAPABILITY_CACHE_TTL) { return syncMeta.supportsNegentropy ?? false; } try { const supports = await supportsNegentropy(relay); await this.ndk.cacheAdapter?.updateRelayStatus?.(relay.url, { metadata: { sync: { supportsNegentropy: supports, lastChecked: now2 } } }); return supports; } catch (error) { await this.ndk.cacheAdapter?.updateRelayStatus?.(relay.url, { metadata: { sync: { supportsNegentropy: false, lastChecked: now2, lastError: error instanceof Error ? error.message : "Unknown error" } } }); return false; } } /** * Get all relays that support Negentropy */ async getNegentropyRelays(relays) { const relaysToCheck = relays || Array.from(this.ndk.pool?.relays?.values() || []); const results = await Promise.all( relaysToCheck.map(async (relay) => ({ relay, supports: await this.checkRelaySupport(relay) })) ); return results.filter((r) => r.supports).map((r) => r.relay); } /** * Get relay capability info from persistent cache */ async getRelayCapability(relayUrl) { const status = await this.ndk.cacheAdapter?.getRelayStatus?.(relayUrl); return status?.metadata?.sync; } /** * Clear capability cache for a specific relay or all relays * Useful for testing or after relay updates */ async clearCapabilityCache(relayUrl) { if (relayUrl) { await this.ndk.cacheAdapter?.updateRelayStatus?.(relayUrl, { metadata: { sync: void 0 } }); } else { console.warn("clearCapabilityCache() without relayUrl is not supported with persistent cache"); } } /** * Mark a relay as not supporting negentropy in the cache * @private */ async markRelayAsNotSupporting(relayUrl, error) { await this.ndk.cacheAdapter?.updateRelayStatus?.(relayUrl, { metadata: { sync: { supportsNegentropy: false, lastChecked: Date.now(), lastError: error.message } } }); } /** * Create an onRelayError handler that updates cache and calls user callback * @private */ createErrorHandler(userCallback) { return async (relay, error) => { await userCallback?.(relay, error); await this.markRelayAsNotSupporting(relay.url, error); }; } /** * Sync with a single relay, with automatic fallback to fetchEvents if negentropy not supported * @private */ async syncSingleRelay(relay, filters, opts = {}) { const supportsNeg = await this.checkRelaySupport(relay); if (supportsNeg) { return await ndkSync.call(this.ndk, filters, { ...opts, relaySet: new NDKRelaySet2(/* @__PURE__ */ new Set([relay]), this.ndk), onNegotiationProgress: opts.onNegotiationProgress }); } const events = await this.ndk.guardrailOff("fetch-events-usage").fetchEvents(filters, { relaySet: new NDKRelaySet2(/* @__PURE__ */ new Set([relay]), this.ndk), subId: "sync-fetch-fallback", groupable: false }); return { events: Array.from(events), need: /* @__PURE__ */ new Set(), have: /* @__PURE__ */ new Set() }; } /** * Perform NIP-77 Negentropy sync with relays * * @param filters - Filters to sync * @param opts - Sync options * @returns Sync result with events, need, and have sets */ async sync(filters, opts) { const filterArray = Array.isArray(filters) ? filters : [filters]; const relaySet = opts?.relaySet || (opts?.relayUrls ? NDKRelaySet2.fromRelayUrls(opts.relayUrls, this.ndk) : void 0); const relays = relaySet ? Array.from(relaySet.relays) : Array.from(this.ndk.pool?.relays?.values() || []); if (relays.length === 0) { console.warn("[NDK Sync] No relays available for sync"); return { events: [], need: /* @__PURE__ */ new Set(), have: /* @__PURE__ */ new Set() }; } const result = { events: [], need: /* @__PURE__ */ new Set(), have: /* @__PURE__ */ new Set() }; const mergedOpts = { ...opts, onRelayError: this.createErrorHandler(opts?.onRelayError) }; await Promise.all( relays.map(async (relay) => { try { const relayResult = await this.syncSingleRelay(relay, filterArray, mergedOpts); result.events.push(...relayResult.events); for (const id of relayResult.need) result.need.add(id); for (const id of relayResult.have) result.have.add(id); } catch (error) { console.error(`[NDK Sync] Failed to sync with relay ${relay.url}:`, error); } }) ); return result; } /** * Subscribe and sync - ensures complete event coverage without missing events * * This method: * 1. Immediately starts a live subscription with limit: 0 to catch new events * 2. Returns the subscription right away (non-blocking) * 3. In the background, syncs historical events from each relay: * - Uses Negentropy sync where available (tracked via capability cache) * - Falls back to fetchEvents for relays without Negentropy * 4. All synced events automatically flow to the subscription * * @param filters - NDK filter(s) to sync and subscribe to * @param opts - Subscription options with sync callbacks * @returns NDKSubscription that receives both live and historical events */ async syncAndSubscribe(filters, opts = {}) { if (!this.ndk.cacheAdapter) { console.warn("[NDKSync] No cache adapter - sync will not work, using subscription only"); } const filterArray = Array.isArray(filters) ? filters : [filters]; const relaySet = opts.relaySet || (opts.relayUrls ? NDKRelaySet2.fromRelayUrls(opts.relayUrls, this.ndk) : void 0); const relays = relaySet ? Array.from(relaySet.relays) : Array.from(this.ndk.pool?.relays?.values() || []); if (relays.length === 0) { throw new Error("No relays available for syncAndSubscribe"); } const subFilters = filterArray.map((f) => ({ ...f, limit: 0 })); const sub = this.ndk.subscribe(subFilters, { ...opts, relaySet, closeOnEose: false }); if (this.ndk.cacheAdapter) { let completedCount = 0; const totalRelays = relays.length; const syncWithRelay2 = async (relay) => { try { const result = await this.syncSingleRelay(relay, filterArray, { autoFetch: true, onRelayError: this.createErrorHandler(opts.onRelayError), onNegotiationProgress: opts.onNegotiationProgress }); opts.onRelaySynced?.(relay, result.events.length); } catch (error) { console.error(`[NDKSync] Failed to sync from ${relay.url}:`, error); } finally { completedCount++; if (completedCount === totalRelays) { opts.onSyncComplete?.(); } } }; for (const relay of relays) { if (relay.connected) { syncWithRelay2(relay); } else { let completed = false; const onReady = () => { if (completed) return; completed = true; relay.off("ready", onReady); syncWithRelay2(relay); }; relay.once("ready", onReady); setTimeout(() => { if (completed) return; completed = true; relay.off("ready", onReady); completedCount++; if (completedCount === totalRelays) { opts.onSyncComplete?.(); } }, TIMEOUTS.RELAY_CONNECTION); } } } else { setTimeout(() => opts.onSyncComplete?.(), 0); } return sub; } /** * Static factory methods for backwards compatibility */ static sync(ndk, filters, opts) { const sync = new _NDKSync(ndk); return sync.sync(filters, opts); } static syncAndSubscribe(ndk, filters, opts) { const sync = new _NDKSync(ndk); return sync.syncAndSubscribe(filters, opts); } }; } }); // ndk/node_modules/webln/lib/errors.js var require_errors = __commonJS({ "ndk/node_modules/webln/lib/errors.js"(exports2) { "use strict"; var __extends = exports2 && exports2.__extends || /* @__PURE__ */ (function() { var extendStatics = function(d17, b) { extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d18, b2) { d18.__proto__ = b2; } || function(d18, b2) { for (var p5 in b2) if (Object.prototype.hasOwnProperty.call(b2, p5)) d18[p5] = b2[p5]; }; return extendStatics(d17, b); }; return function(d17, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d17, b); function __() { this.constructor = d17; } d17.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports2, "__esModule", { value: true }); exports2.InternalError = exports2.InvalidDataError = exports2.RoutingError = exports2.UnsupportedMethodError = exports2.ConnectionError = exports2.RejectionError = exports2.MissingProviderError = void 0; function fixError(error, newTarget, errorType) { Object.setPrototypeOf(error, errorType.prototype); if (newTarget === errorType) { error.name = newTarget.name; if (Error.captureStackTrace) { Error.captureStackTrace(error, errorType); } else { var stack = new Error(error.message).stack; if (stack) { error.stack = fixStack(stack, "new ".concat(newTarget.name)); } } } } function fixStack(stack, functionName) { if (!stack) return stack; if (!functionName) return stack; var exclusion = new RegExp("\\s+at\\s".concat(functionName, "\\s")); var lines = stack.split("\n"); var resultLines = lines.filter(function(line) { return !line.match(exclusion); }); return resultLines.join("\n"); } var MissingProviderError = ( /** @class */ (function(_super) { __extends(MissingProviderError2, _super); function MissingProviderError2(message) { var _newTarget = this.constructor; var _this = _super.call(this, message) || this; fixError(_this, _newTarget, MissingProviderError2); return _this; } return MissingProviderError2; })(Error) ); exports2.MissingProviderError = MissingProviderError; var RejectionError = ( /** @class */ (function(_super) { __extends(RejectionError2, _super); function RejectionError2(message) { var _newTarget = this.constructor; var _this = _super.call(this, message) || this; fixError(_this, _newTarget, RejectionError2); return _this; } return RejectionError2; })(Error) ); exports2.RejectionError = RejectionError; var ConnectionError = ( /** @class */ (function(_super) { __extends(ConnectionError2, _super); function ConnectionError2(message) { var _newTarget = this.constructor; var _this = _super.call(this, message) || this; fixError(_this, _newTarget, ConnectionError2); return _this; } return ConnectionError2; })(Error) ); exports2.ConnectionError = ConnectionError; var UnsupportedMethodError = ( /** @class */ (function(_super) { __extends(UnsupportedMethodError2, _super); function UnsupportedMethodError2(message) { var _newTarget = this.constructor; var _this = _super.call(this, message) || this; fixError(_this, _newTarget, UnsupportedMethodError2); return _this; } return UnsupportedMethodError2; })(Error) ); exports2.UnsupportedMethodError = UnsupportedMethodError; var RoutingError = ( /** @class */ (function(_super) { __extends(RoutingError2, _super); function RoutingError2(message) { var _newTarget = this.constructor; var _this = _super.call(this, message) || this; fixError(_this, _newTarget, RoutingError2); return _this; } return RoutingError2; })(Error) ); exports2.RoutingError = RoutingError; var InvalidDataError = ( /** @class */ (function(_super) { __extends(InvalidDataError2, _super); function InvalidDataError2(message) { var _newTarget = this.constructor; var _this = _super.call(this, message) || this; fixError(_this, _newTarget, InvalidDataError2); return _this; } return InvalidDataError2; })(Error) ); exports2.InvalidDataError = InvalidDataError; var InternalError = ( /** @class */ (function(_super) { __extends(InternalError2, _super); function InternalError2(message) { var _newTarget = this.constructor; var _this = _super.call(this, message) || this; fixError(_this, _newTarget, InternalError2); return _this; } return InternalError2; })(Error) ); exports2.InternalError = InternalError; } }); // ndk/node_modules/webln/lib/client.js var require_client = __commonJS({ "ndk/node_modules/webln/lib/client.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.requestProvider = void 0; var errors_1 = require_errors(); function requestProvider3(_2) { if (_2 === void 0) { _2 = {}; } return new Promise(function(resolve, reject) { if (typeof window === "undefined") { return reject(new Error("Must be called in a browser context")); } var webln = window.webln; if (!webln) { return reject(new errors_1.MissingProviderError("Your browser has no WebLN provider")); } webln.enable().then(function() { return resolve(webln); }).catch(function(err) { return reject(err); }); }); } exports2.requestProvider = requestProvider3; } }); // ndk/node_modules/webln/lib/provider.js var require_provider = __commonJS({ "ndk/node_modules/webln/lib/provider.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); } }); // ndk/node_modules/webln/lib/index.js var require_lib4 = __commonJS({ "ndk/node_modules/webln/lib/index.js"(exports2) { "use strict"; var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; var desc = Object.getOwnPropertyDescriptor(m, k2); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k2]; } }; } Object.defineProperty(o, k22, desc); }) : (function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; o[k22] = m[k2]; })); var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { for (var p5 in m) if (p5 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p5)) __createBinding(exports3, m, p5); }; Object.defineProperty(exports2, "__esModule", { value: true }); __exportStar(require_client(), exports2); __exportStar(require_provider(), exports2); __exportStar(require_errors(), exports2); } }); // ndk/wallet/dist/index.js var dist_exports = {}; __export(dist_exports, { NDKCashuDeposit: () => NDKCashuDeposit, NDKCashuWallet: () => NDKCashuWallet, NDKCashuWalletBackup: () => NDKCashuWalletBackup, NDKNWCWallet: () => NDKNWCWallet, NDKNutzapMonitor: () => NDKNutzapMonitor, NDKWallet: () => NDKWallet, NDKWalletStatus: () => NDKWalletStatus, NDKWebLNWallet: () => NDKWebLNWallet, WalletState: () => WalletState, calculateNewState: () => calculateNewState, consolidateMintTokens: () => consolidateMintTokens, consolidateTokens: () => consolidateTokens, createMintCacheCallbacks: () => createMintCacheCallbacks, createMintDiscoveryStore: () => createMintDiscoveryStore, getBolt11Amount: () => getBolt11Amount, getBolt11Description: () => getBolt11Description, getBolt11ExpiresAt: () => getBolt11ExpiresAt, getCashuMintRecommendations: () => getCashuMintRecommendations, update: () => update }); async function fetchMintInfo(url, ndk) { if (ndk.cacheAdapter?.getCacheData) { try { const cached = await ndk.cacheAdapter.getCacheData("wallet:mint:info", url); if (cached) { return { isOnline: true, info: cached }; } } catch (e2) { console.error("Error reading mint info from cache:", e2); } } try { const response = await fetch(`${url}/v1/info`); if (response.ok) { const info = await response.json(); if (ndk.cacheAdapter?.setCacheData) { try { await ndk.cacheAdapter.setCacheData("wallet:mint:info", url, info); } catch (e2) { console.error("Error caching mint info:", e2); } } return { isOnline: true, info }; } return { isOnline: false }; } catch { return { isOnline: false }; } } function createMintDiscoveryStore(ndk, options = {}) { const { network = "mainnet", timeout = 1e4, followUsers } = options; let mintSub; let recSub; let timeoutId; const mintsMap = /* @__PURE__ */ new Map(); const store = createStore()((set, get) => ({ mints: [], progress: { announcementsFound: 0, recommendationsFound: 0 }, getMint: (url) => mintsMap.get(url), getTopMints: (limit2 = 10, minRecommendations = 0) => { let filtered = get().mints; if (minRecommendations > 0) { filtered = filtered.filter((m) => m.recommendations.length >= minRecommendations); } return filtered.sort((a, b) => b.score - a.score).slice(0, limit2); }, searchMints: (query3) => { const lowerQuery = query3.toLowerCase(); return get().mints.filter( (mint) => mint.url.toLowerCase().includes(lowerQuery) || mint.name?.toLowerCase().includes(lowerQuery) || mint.description?.toLowerCase().includes(lowerQuery) ); }, recommendMint: async (url, review) => { const rec = new NDKMintRecommendation2(ndk); rec.recommendedKind = NDKKind2.CashuMintAnnouncement; rec.urls = [url]; rec.review = review; await rec.sign(); await rec.publish(); }, stop: () => { mintSub?.stop(); recSub?.stop(); if (timeoutId) { clearTimeout(timeoutId); } } })); mintSub = ndk.subscribe( { kinds: [NDKKind2.CashuMintAnnouncement], limit: 100 }, { closeOnEose: false, onEvent: async (event) => { const mint = await NDKCashuMintAnnouncement2.from(event); if (!mint) return; if (network && mint.network !== network) return; const url = mint.url; if (!url) return; const existing = mintsMap.get(url); const mintData = { url, identifier: mint.identifier, network: mint.network, nuts: mint.nuts || [], name: mint.metadata?.name, description: mint.metadata?.description, icon: mint.metadata?.icon, longDescription: mint.metadata?.longDescription, contact: mint.metadata?.contact, motd: mint.metadata?.motd, recommendations: existing?.recommendations || [], score: existing?.score || 0, lastUpdated: Date.now() }; mintsMap.set(url, mintData); store.setState((state) => ({ mints: Array.from(mintsMap.values()), progress: { ...state.progress, announcementsFound: state.progress.announcementsFound + 1 } })); fetchMintInfo(url, ndk).then(({ isOnline, info }) => { const existing2 = mintsMap.get(url); if (!existing2) return; mintsMap.set(url, { ...existing2, isOnline, name: info?.name || existing2.name, description: info?.description || existing2.description, icon: info?.icon || existing2.icon, longDescription: info?.longDescription || existing2.longDescription, contact: info?.contact || existing2.contact, motd: info?.motd || existing2.motd, lastUpdated: Date.now() }); store.setState({ mints: Array.from(mintsMap.values()) }); }); } } ); const recFilter = { kinds: [NDKKind2.EcashMintRecommendation], "#k": [NDKKind2.CashuMintAnnouncement.toString()], limit: 500 }; if (followUsers && followUsers.length > 0) { recFilter.authors = followUsers; } recSub = ndk.subscribe(recFilter, { closeOnEose: false, onEvent: async (event) => { const rec = await NDKMintRecommendation2.from(event); if (!rec) return; const urls = rec.urls; for (const url of urls) { const mint = mintsMap.get(url); if (mint) { mint.recommendations.push(rec); mint.score = mint.recommendations.length; mintsMap.set(url, { ...mint }); } else { mintsMap.set(url, { url, nuts: [], recommendations: [rec], score: 1, lastUpdated: Date.now() }); } store.setState((state) => ({ mints: Array.from(mintsMap.values()), progress: { ...state.progress, recommendationsFound: state.progress.recommendationsFound + 1 } })); } } }); if (timeout > 0) { timeoutId = setTimeout(() => { store.getState().stop(); }, timeout); } return store; } function mintKey(mint, unit, pk) { if (pk) { const pkStr = new TextDecoder().decode(pk); return `${mint}-${unit}-${pkStr}`; } return `${mint}-${unit}`; } async function walletForMint(mint, { pk, timeout = 5e3, mintInfo, mintKeys, onMintInfoNeeded, onMintInfoLoaded, onMintKeysNeeded, onMintKeysLoaded } = {}) { const startTime = Date.now(); const ts = () => `+${Date.now() - startTime}ms`; if (onMintInfoNeeded) { console.log(`[MINT-CACHE ${ts()}] Querying cache for mint info: ${mint}`); const cacheStartTime = Date.now(); mintInfo ?? (mintInfo = await onMintInfoNeeded(mint)); const cacheTime = Date.now() - cacheStartTime; if (mintInfo) { console.log(`[MINT-CACHE ${ts()}] \u2713 Cache HIT for mint info: ${mint} (${cacheTime}ms)`, { name: mintInfo.name }); } else { console.log(`[MINT-CACHE ${ts()}] \u2717 Cache MISS for mint info: ${mint} (${cacheTime}ms)`); } } if (onMintKeysNeeded) { console.log(`[MINT-CACHE ${ts()}] Querying cache for mint keys: ${mint}`); const cacheStartTime = Date.now(); mintKeys ?? (mintKeys = await onMintKeysNeeded(mint)); const cacheTime = Date.now() - cacheStartTime; if (mintKeys) { console.log(`[MINT-CACHE ${ts()}] \u2713 Cache HIT for mint keys: ${mint} (${cacheTime}ms)`, { count: mintKeys.length }); } else { console.log(`[MINT-CACHE ${ts()}] \u2717 Cache MISS for mint keys: ${mint} (${cacheTime}ms)`); } } if (!mintInfo && onMintInfoLoaded) { console.log(`[MINT-CACHE ${ts()}] Fetching mint info from ${mint}/v1/info`); const fetchStartTime = Date.now(); mintInfo = await q.getInfo(mint); const fetchTime = Date.now() - fetchStartTime; console.log(`[MINT-CACHE ${ts()}] Caching mint info: ${mint} (fetched in ${fetchTime}ms)`, { name: mintInfo.name }); onMintInfoLoaded?.(mint, mintInfo); } const unit = "sat"; const key = mintKey(mint, unit, pk); if (mintWallets.has(key)) { console.log(`[MINT-CACHE ${ts()}] Returning cached wallet instance: ${mint}`); return mintWallets.get(key); } if (mintWalletPromises.has(key)) { console.log(`[MINT-CACHE ${ts()}] Wallet loading in progress, returning existing promise: ${mint}`); return mintWalletPromises.get(key); } if (!mintInfo) { if (onMintInfoNeeded) { console.log(`[MINT-CACHE ${ts()}] Querying cache for mint info (second check): ${mint}`); const cacheStartTime = Date.now(); mintInfo = await onMintInfoNeeded(mint); const cacheTime = Date.now() - cacheStartTime; if (mintInfo) { console.log(`[MINT-CACHE ${ts()}] \u2713 Cache HIT for mint info (second check): ${mint} (${cacheTime}ms)`, { name: mintInfo.name }); } else { console.log(`[MINT-CACHE ${ts()}] \u2717 Cache MISS for mint info (second check): ${mint} (${cacheTime}ms)`); } } if (!mintInfo && onMintInfoLoaded) { console.log(`[MINT-CACHE ${ts()}] Fetching mint info from ${mint}/v1/info (second check)`); const fetchStartTime = Date.now(); mintInfo = await q.getInfo(mint); const fetchTime = Date.now() - fetchStartTime; console.log(`[MINT-CACHE ${ts()}] Caching mint info (second check): ${mint} (fetched in ${fetchTime}ms)`, { name: mintInfo.name }); onMintInfoLoaded(mint, mintInfo); } } if (!mintKeys && onMintKeysNeeded) { console.log(`[MINT-CACHE ${ts()}] Querying cache for mint keys (second check): ${mint}`); const cacheStartTime = Date.now(); mintKeys = await onMintKeysNeeded(mint); const cacheTime = Date.now() - cacheStartTime; if (mintKeys) { console.log(`[MINT-CACHE ${ts()}] \u2713 Cache HIT for mint keys (second check): ${mint} (${cacheTime}ms)`, { count: mintKeys.length }); } else { console.log(`[MINT-CACHE ${ts()}] \u2717 Cache MISS for mint keys (second check): ${mint} (${cacheTime}ms)`); } } const wallet = new us(new q(mint), { unit, bip39seed: pk, mintInfo, keys: mintKeys }); const loadPromise = new Promise(async (resolve) => { try { console.log(`[MINT-CACHE ${ts()}] Loading mint wallet: ${mint}`); const loadStartTime = Date.now(); const timeoutPromise = new Promise((_2, rejectTimeout) => { setTimeout(() => { rejectTimeout(new Error("timeout loading mint")); }, timeout); }); await Promise.race([wallet.loadMint(), timeoutPromise]); const loadTime = Date.now() - loadStartTime; console.log(`[MINT-CACHE ${ts()}] Mint wallet loaded: ${mint} (${loadTime}ms)`); mintWallets.set(key, wallet); mintWalletPromises.delete(key); if (wallet.keys) { console.log(`[MINT-CACHE ${ts()}] Caching mint keys after loadMint: ${mint}`, { count: wallet.keys.size }); onMintKeysLoaded?.(mint, wallet.keys); } resolve(wallet); } catch (e2) { console.error(`[WALLET ${ts()}] error loading mint`, mint, e2.message); mintWalletPromises.delete(key); resolve(null); } }); mintWalletPromises.set(key, loadPromise); return loadPromise; } function createMintCacheCallbacks(adapter) { return { onMintInfoNeeded: async (mint) => { if (!adapter.getCacheData) return void 0; return adapter.getCacheData("wallet:mint:info", mint); }, onMintInfoLoaded: async (mint, info) => { if (!adapter.setCacheData) return; await adapter.setCacheData("wallet:mint:info", mint, info); }, onMintKeysNeeded: async (mint) => { if (!adapter.getCacheData) return void 0; return adapter.getCacheData("wallet:mint:keys", mint); }, onMintKeysLoaded: async (mint, keysets) => { if (!adapter.setCacheData) return; const keysArray = Array.from(keysets.values()); await adapter.setCacheData("wallet:mint:keys", mint, keysArray); } }; } async function getCashuWallet(mint) { if (this.cashuWallets.has(mint)) return this.cashuWallets.get(mint); const w2 = await walletForMint(mint, { onMintInfoNeeded: this.onMintInfoNeeded, onMintInfoLoaded: this.onMintInfoLoaded, onMintKeysNeeded: this.onMintKeysNeeded, onMintKeysLoaded: this.onMintKeysLoaded }); if (!w2) throw new Error(`unable to load wallet for mint ${mint}`); this.cashuWallets.set(mint, w2); return w2; } function getBolt11ExpiresAt(bolt11) { const decoded = (0, import_light_bolt11_decoder4.decode)(bolt11); const expiry = decoded.expiry; const timestamp = decoded.sections.find((section) => section.name === "timestamp").value; if (typeof expiry === "number" && typeof timestamp === "number") { return expiry + timestamp; } return void 0; } function getBolt11Amount(bolt11) { const decoded = (0, import_light_bolt11_decoder4.decode)(bolt11); const section = decoded.sections.find((section2) => section2.name === "amount"); const val = section?.value; return Number(val); } function getBolt11Description(bolt11) { const decoded = (0, import_light_bolt11_decoder4.decode)(bolt11); const section = decoded.sections.find((section2) => section2.name === "description"); const val = section?.value; return val; } async function createOutTxEvent(ndk, paymentRequest, paymentResult, relaySet, { nutzaps } = {}) { let description = paymentRequest.paymentDescription; let amount; if (paymentRequest.pr) { amount = getBolt11Amount(paymentRequest.pr); description ?? (description = getBolt11Description(paymentRequest.pr)); if (amount) amount /= 1e3; } else { amount = paymentRequest.amount; } if (!amount) { console.error("BUG: Unable to find amount for paymentRequest", paymentRequest); } const txEvent = new NDKCashuWalletTx2(ndk); txEvent.direction = "out"; txEvent.amount = amount ?? 0; txEvent.mint = paymentResult.mint; txEvent.description = description; if (paymentResult.fee) txEvent.fee = paymentResult.fee; if (paymentRequest.target) { txEvent.tags.push(paymentRequest.target.tagReference()); if (!(paymentRequest.target instanceof NDKUser2)) { txEvent.tags.push(["p", paymentRequest.target.pubkey]); } } if (nutzaps) { txEvent.description ?? (txEvent.description = "nutzap redeem"); for (const nutzap of nutzaps) txEvent.addRedeemedNutzap(nutzap); } if (paymentResult.stateUpdate?.created) txEvent.createdTokens = [paymentResult.stateUpdate.created]; if (paymentResult.stateUpdate?.deleted) txEvent.destroyedTokenIds = paymentResult.stateUpdate.deleted; if (paymentResult.stateUpdate?.reserved) txEvent.reservedTokens = [paymentResult.stateUpdate.reserved]; await txEvent.sign(); txEvent.publish(relaySet); return txEvent; } async function createInTxEvent(ndk, proofs, mint, updateStateResult, { nutzaps, fee, description }, relaySet) { const txEvent = new NDKCashuWalletTx2(ndk); const amount = proofsTotalBalance2(proofs); txEvent.direction = "in"; txEvent.amount = amount; txEvent.mint = mint; txEvent.description = description; if (updateStateResult.created) txEvent.createdTokens = [updateStateResult.created]; if (updateStateResult.deleted) txEvent.destroyedTokenIds = updateStateResult.deleted; if (updateStateResult.reserved) txEvent.reservedTokens = [updateStateResult.reserved]; if (nutzaps) for (const nutzap of nutzaps) txEvent.addRedeemedNutzap(nutzap); if (fee) txEvent.fee = fee; await txEvent.sign(); txEvent.publish(relaySet); return txEvent; } function randomMint(wallet) { const mints = wallet.mints; const mint = mints[Math.floor(Math.random() * mints.length)]; return mint; } async function handleEventDeletion(event) { const deletedIds = event.getMatchingTags("e").map((tag) => tag[1]); for (const deletedId of deletedIds) { this.state.removeTokenId(deletedId); } } async function handleQuote(event) { const quote = await NDKCashuQuote.from(event); if (!quote) return; const oneHourAgo = Date.now() / 1e3 - 3600; if (event.created_at && event.created_at < oneHourAgo) { return; } const deposit = NDKCashuDeposit.fromQuoteEvent(this, quote); if (this.depositMonitor.addDeposit(deposit)) { deposit.finalize(); } } async function handleToken(event) { if (this.state.tokens.has(event.id)) return; const token = await NDKCashuToken2.from(event); if (!token) { return; } for (const deletedTokenId of token.deletedTokens) { this.state.removeTokenId(deletedTokenId); } this.state.addToken(token); } async function eventHandler(event) { const handler = handlers[event.kind]; if (handler) { if (balanceUpdateTimer) clearTimeout(balanceUpdateTimer); await handler.call(this, event); balanceUpdateTimer = setTimeout(() => { this.emit("balance_updated"); }, 100); } } async function eventDupHandler(_event, _relay, _timeSinceFirstSeen, _sub, _fromCache) { } async function consolidateTokens() { d24("checking %d tokens for spent proofs", this.state.tokens.size); const mints = new Set( this.state.getMintsProofs({ validStates: /* @__PURE__ */ new Set(["available", "reserved", "deleted"]) }).keys() ); d24("found %d mints", mints.size); mints.forEach((mint) => { consolidateMintTokens(mint, this); }); } async function consolidateMintTokens(mint, wallet, allProofs, onResult, onFailure) { allProofs ?? (allProofs = wallet.state.getProofs({ mint, includeDeleted: true, onlyAvailable: false })); const _wallet = await walletForMint(mint); if (!_wallet) { return; } let proofStates = []; try { proofStates = await _wallet.checkProofsStates(allProofs); } catch (e2) { onFailure?.(e2.message); return; } const spentProofs = []; const unspentProofs = []; const pendingProofs = []; allProofs.forEach((proof, index) => { const { state } = proofStates[index]; if (state === as.SPENT) { spentProofs.push(proof); } else if (state === as.UNSPENT) { unspentProofs.push(proof); } else { pendingProofs.push(proof); } }); const walletChange = { mint, store: unspentProofs, destroy: spentProofs }; onResult?.(walletChange); spentProofs.reduce((acc, proof) => acc + proof.amount, 0); if (walletChange.destroy?.length === 0) return; walletChange.store?.push(...pendingProofs); const totalPendingProofs = pendingProofs.reduce((acc, proof) => acc + proof.amount, 0); wallet.state.reserveProofs(pendingProofs, totalPendingProofs); return wallet.state.update(walletChange, "Consolidate"); } function calculateFee(intendedAmount, providedProofs, returnedProofs) { const totalProvided = providedProofs.reduce((acc, p5) => acc + p5.amount, 0); const totalReturned = returnedProofs.reduce((acc, p5) => acc + p5.amount, 0); const totalFee = totalProvided - intendedAmount - totalReturned; if (totalFee < 0) { throw new Error("Invalid fee calculation: received more proofs than sent to mint"); } return totalFee; } async function withProofReserve(wallet, cashuWallet, mint, amountWithFees, amountWithoutFees, cb) { cashuWallet ?? (cashuWallet = await wallet.getCashuWallet(mint)); const availableMintProofs = wallet.state.getProofs({ mint, onlyAvailable: true }); const proofs = cashuWallet.selectProofsToSend(availableMintProofs, amountWithFees); const fetchedAmount = proofs.send.reduce((a, b) => a + b.amount, 0); if (fetchedAmount < amountWithFees) return null; wallet.state.reserveProofs(proofs.send, amountWithFees); let cbResult = null; let proofsChange = null; let updateRes = null; try { cbResult = await cb(proofs.send, availableMintProofs); if (!cbResult) return null; proofsChange = { mint, store: cbResult.change, destroy: proofs.send }; updateRes = await wallet.state.update(proofsChange); } catch (e2) { wallet.state.unreserveProofs(proofs.send, amountWithFees, "available"); throw e2; } if (!cbResult) return null; return { result: cbResult.result, proofsChange, stateUpdate: updateRes, mint, fee: calculateFee(amountWithoutFees, proofs.send, cbResult.change) }; } async function payLn(wallet, pr2, { amount, unit } = {}) { let invoiceAmount = getBolt11Amount(pr2); if (!invoiceAmount) throw new Error("invoice amount is required"); invoiceAmount = invoiceAmount / 1e3; if (amount && unit) { if (unit === "msat") { amount = amount / 1e3; } } const eligibleMints = wallet.getMintsWithBalance(invoiceAmount + 3); if (!eligibleMints.length) { return null; } for (const mint of eligibleMints) { try { const result = await executePayment(mint, pr2, amount ?? invoiceAmount, wallet); if (result) { if (amount) { result.fee = calculateFee( amount, result.proofsChange?.destroy ?? [], result.proofsChange?.store ?? [] ); } return result; } } catch (error) { wallet.warn(`Failed to execute payment with min ${mint}: ${error}`); } } return null; } async function executePayment(mint, pr2, amountWithoutFees, wallet) { const cashuWallet = await wallet.getCashuWallet(mint); try { const meltQuote = await cashuWallet.createMeltQuote(pr2); const amountToSend = meltQuote.amount + meltQuote.fee_reserve; const result = await withProofReserve( wallet, cashuWallet, mint, amountToSend, amountWithoutFees, async (proofsToUse, _allOurProofs) => { const meltResult = await cashuWallet.meltProofs(meltQuote, proofsToUse); if (meltResult.quote.state === et.PAID) { return { result: { preimage: meltResult.quote.payment_preimage ?? "" }, change: meltResult.change }; } return null; } ); return result; } catch (e2) { if (e2 instanceof Error) { if (e2.message.match(/already spent/i)) { setTimeout(() => { consolidateMintTokens(mint, wallet); }, 2500); } else { throw e2; } } return null; } } function ensureIsCashuPubkey(pubkey) { if (!pubkey) return; let _pubkey = pubkey; if (_pubkey.length === 64) _pubkey = `02${_pubkey}`; if (_pubkey.length !== 66) throw new Error("Invalid pubkey"); return _pubkey; } async function mintProofs(wallet, quote, amount, mint, p2pk, proofTags) { const mintTokenAttempt = (resolve, reject, attempt) => { const pubkey = ensureIsCashuPubkey(p2pk); wallet.mintProofs(amount, quote.quote, { pubkey, ...proofTags && proofTags.length > 0 ? { tags: proofTags } : {} }).then((mintProofs22) => { console.debug("minted tokens", mintProofs22); resolve({ proofs: mintProofs22, mint }); }).catch((e2) => { attempt++; if (attempt <= 3) { console.error("error minting tokens", e2); setTimeout(() => mintTokenAttempt(resolve, reject, attempt), attempt * 1500); } else { reject(e2); } }); }; return new Promise((resolve, reject) => { mintTokenAttempt(resolve, reject, 0); }); } async function createToken(wallet, amount, recipientMints, p2pk, proofTags) { console.log("[createToken] Starting token creation", { amount, recipientMints, p2pk }); p2pk = ensureIsCashuPubkey(p2pk); const myMintsWithEnoughBalance = wallet.getMintsWithBalance(amount); console.log("[createToken] My mints with enough balance", myMintsWithEnoughBalance); const hasRecipientMints = recipientMints && recipientMints.length > 0; const mintsInCommon = hasRecipientMints ? findMintsInCommon([recipientMints, myMintsWithEnoughBalance]) : myMintsWithEnoughBalance; console.log("[createToken] Mints in common", { hasRecipientMints, mintsInCommon }); for (const mint of mintsInCommon) { console.log("[createToken] Attempting to create token in mint", mint); try { const res = await createTokenInMint(wallet, mint, amount, p2pk, proofTags); if (res) { console.log("[createToken] Successfully created token in mint", mint); return res; } console.log("[createToken] Failed to create token in mint", mint); } catch (e2) { console.error("[createToken] Error creating token in mint", mint, e2); } } if (hasRecipientMints) { console.log("[createToken] Attempting cross-mint transfer"); return await createTokenWithMintTransfer(wallet, amount, recipientMints, p2pk, proofTags); } console.error("[createToken] All token creation attempts failed"); return null; } async function createTokenInMint(wallet, mint, amount, p2pk, proofTags) { console.log("[createTokenInMint] Starting", { mint, amount, p2pk }); const cashuWallet = await wallet.getCashuWallet(mint); console.log("[createTokenInMint] Got cashu wallet for mint", mint); try { const result = await withProofReserve( wallet, cashuWallet, mint, amount, amount, async (proofsToUse, allOurProofs) => { console.log("[createTokenInMint] Inside withProofReserve callback", { proofsToUseCount: proofsToUse.length, allOurProofsCount: allOurProofs.length }); const sendResult = await cashuWallet.send(amount, proofsToUse, { pubkey: p2pk, proofsWeHave: allOurProofs, ...proofTags && proofTags.length > 0 ? { tags: proofTags } : {} }); console.log("[createTokenInMint] Send result", { sendCount: sendResult.send.length, keepCount: sendResult.keep.length }); return { result: { proofs: sendResult.send, mint }, change: sendResult.keep, mint }; } ); console.log("[createTokenInMint] Success", result); return result; } catch (e2) { console.error("[createTokenInMint] Error", { mint, error: e2.message, stack: e2.stack }); } return null; } async function createTokenWithMintTransfer(wallet, amount, recipientMints, p2pk, proofTags) { const generateQuote = async () => { const generateQuoteFromSomeMint = async (mint3) => { const targetMintWallet3 = await walletForMint(mint3); if (!targetMintWallet3) throw new Error(`unable to load wallet for mint ${mint3}`); const quote3 = await targetMintWallet3.createMintQuote(amount); return { quote: quote3, mint: mint3, targetMintWallet: targetMintWallet3 }; }; const quotesPromises = recipientMints.map(generateQuoteFromSomeMint); const { quote: quote2, mint: mint2, targetMintWallet: targetMintWallet2 } = await Promise.any(quotesPromises); if (!quote2) { throw new Error("failed to get quote from any mint"); } return { quote: quote2, mint: mint2, targetMintWallet: targetMintWallet2 }; }; const { quote, mint: targetMint, targetMintWallet } = await generateQuote(); if (!quote) { return null; } const invoiceAmount = getBolt11Amount(quote.request); if (!invoiceAmount) throw new Error("invoice amount is required"); const invoiceAmountInSat = invoiceAmount / 1e3; if (invoiceAmountInSat > amount) throw new Error(`invoice amount is more than the amount passed in (${invoiceAmountInSat} vs ${amount})`); const payLNResult = await payLn(wallet, quote.request, { amount }); if (!payLNResult) { return null; } const { proofs, mint } = await mintProofs(targetMintWallet, quote, amount, targetMint, p2pk, proofTags); return { ...payLNResult, result: { proofs, mint }, fee: payLNResult.fee }; } function findMintsInCommon(mintCollections) { const mintCounts = /* @__PURE__ */ new Map(); for (const mints of mintCollections) { for (const mint of mints) { const normalizedMint = normalizeUrl2(mint); if (!mintCounts.has(normalizedMint)) { mintCounts.set(normalizedMint, 1); } else { mintCounts.set(normalizedMint, mintCounts.get(normalizedMint) + 1); } } } const commonMints = []; for (const [mint, count] of mintCounts.entries()) { if (count === mintCollections.length) { commonMints.push(mint); } } return commonMints; } function getBalance(opts) { const proofs = this.getProofEntries(opts); return proofs.reduce((sum, proof) => sum + proof.proof.amount, 0); } function getMintsBalances({ onlyAvailable } = { onlyAvailable: true }) { var _a72; const balances = {}; const proofs = this.getProofEntries({ onlyAvailable }); for (const proof of proofs) { if (!proof.mint) continue; balances[_a72 = proof.mint] ?? (balances[_a72] = 0); balances[proof.mint] += proof.proof.amount; } return balances; } function addProof(proofEntry) { this.proofs.set(proofEntry.proof.C, proofEntry); this.journal.push({ memo: "Added proof", timestamp: Date.now(), metadata: { type: "proof", id: proofEntry.proof.C, amount: proofEntry.proof.amount, mint: proofEntry.mint } }); } function reserveProofs(proofs, amount) { for (const proof of proofs) { this.updateProof(proof, { state: "reserved" }); } this.reserveAmounts.push(amount); } function unreserveProofs(proofs, amount, newState) { for (const proof of proofs) { this.updateProof(proof, { state: newState }); } const index = this.reserveAmounts.indexOf(amount); if (index !== -1) { this.reserveAmounts.splice(index, 1); } else { throw new Error(`BUG: Amount ${amount} not found in reserveAmounts`); } } function getProofEntries(opts = {}) { const proofs = /* @__PURE__ */ new Map(); const validStates = /* @__PURE__ */ new Set(["available"]); let { mint, onlyAvailable, includeDeleted } = opts; onlyAvailable ?? (onlyAvailable = true); if (!onlyAvailable) validStates.add("reserved"); if (includeDeleted) validStates.add("deleted"); for (const proofEntry of this.proofs.values()) { if (mint && proofEntry.mint !== mint) continue; if (!validStates.has(proofEntry.state)) continue; if (!proofEntry.proof) continue; proofs.set(proofEntry.proof.C, proofEntry); } return Array.from(proofs.values()); } function updateProof(proof, state) { const proofC = proof.C; const currentState = this.proofs.get(proofC); if (!currentState) throw new Error("Proof not found"); const newState = { ...currentState, ...state }; this.proofs.set(proofC, newState); this.journal.push({ memo: `Updated proof state: ${JSON.stringify(state)}`, timestamp: Date.now(), metadata: { type: "proof", id: proofC, amount: proof.amount, mint: currentState.mint } }); } function addToken(token) { if (!token.mint) throw new Error("BUG: Token has no mint"); const currentEntry = this.tokens.get(token.id); const state = currentEntry?.state ?? "available"; this.tokens.set(token.id, { token, state }); for (const proof of token.proofs) { maybeAssociateProofWithToken(this, proof, token, state); } } function maybeAssociateProofWithToken(walletState, proof, token, state) { const proofC = proof.C; const proofEntry = walletState.proofs.get(proofC); if (!proofEntry) { walletState.addProof({ mint: token.mint, state, tokenId: token.id, timestamp: token.created_at, proof }); return true; } if (proofEntry.tokenId) { if (proofEntry.tokenId === token.id) { return null; } const existingTokenEntry = walletState.tokens.get(proofEntry.tokenId); if (!existingTokenEntry) { throw new Error( `BUG: Token id ${proofEntry.tokenId} not found, was expected to be associated with proof ${proofC}` ); } const existingToken = existingTokenEntry.token; if (existingToken) { if (existingToken.created_at && (!token.created_at || token.created_at < existingToken.created_at)) { return false; } } walletState.updateProof(proof, { tokenId: token.id, state }); return true; } walletState.updateProof(proof, { tokenId: token.id, state }); return true; } function removeTokenId(tokenId) { const currentEntry = this.tokens.get(tokenId) || {}; this.tokens.set(tokenId, { ...currentEntry, state: "deleted" }); for (const proofEntry of this.proofs.values()) { const { proof } = proofEntry; if (proofEntry.tokenId === tokenId) { if (!proof) { throw new Error("BUG: Proof entry has no proof"); } this.updateProof(proof, { state: "deleted" }); } } } async function update(stateChange, _memo) { updateInternalState(this, stateChange); this.wallet.emit("balance_updated"); return updateExternalState(this, stateChange); } function updateInternalState(walletState, stateChange) { if (stateChange.store && stateChange.store.length > 0) { for (const proof of stateChange.store) { walletState.addProof({ mint: stateChange.mint, state: "available", proof, timestamp: Date.now() }); } } if (stateChange.destroy && stateChange.destroy.length > 0) { for (const proof of stateChange.destroy) { walletState.updateProof(proof, { state: "deleted" }); } } if (stateChange.reserve && stateChange.reserve.length > 0) { throw new Error("BUG: Proofs should not be reserved via update"); } } async function updateExternalState(walletState, stateChange) { const newState = calculateNewState(walletState, stateChange); if (newState.deletedTokenIds.size > 0) { const deleteEvent = new NDKEvent2(walletState.wallet.ndk, { kind: NDKKind2.EventDeletion, tags: [ ["k", NDKKind2.CashuToken.toString()], ...Array.from(newState.deletedTokenIds).map((id) => ["e", id]) ] }); await deleteEvent.sign(); publishWithRetry(walletState, deleteEvent, walletState.wallet.relaySet); for (const tokenId of newState.deletedTokenIds) { walletState.removeTokenId(tokenId); } } const res = {}; if (newState.saveProofs.length > 0) { const newToken = await createTokenEvent(walletState, stateChange.mint, newState); res.created = newToken; } return res; } async function publishWithRetry(walletState, event, relaySet, retryTimeout = 10 * 1e3) { let publishResult; publishResult = await event.publish(relaySet); let type; if (event.kind === NDKKind2.EventDeletion) type = "deletion"; if (event.kind === NDKKind2.CashuToken) type = "token"; if (event.kind === NDKKind2.CashuWallet) type = "wallet"; const journalEntryMetadata = { type, id: event.id, relayUrl: relaySet?.relayUrls.join(",") }; if (publishResult) { walletState.journal.push({ memo: `Publish kind:${event.kind} succeesfully`, timestamp: Date.now(), metadata: journalEntryMetadata }); return publishResult; } walletState.journal.push({ memo: "Publish failed", timestamp: Date.now(), metadata: journalEntryMetadata }); setTimeout(() => { publishWithRetry(walletState, event, relaySet, retryTimeout); }, retryTimeout); } async function createTokenEvent(walletState, mint, newState) { const newToken = new NDKCashuToken2(walletState.wallet.ndk); newToken.mint = mint; newToken.proofs = newState.saveProofs; await newToken.toNostrEvent(); walletState.addToken(newToken); newToken.deletedTokens = Array.from(newState.deletedTokenIds); await newToken.sign(); walletState.addToken(newToken); publishWithRetry(walletState, newToken, walletState.wallet.relaySet); return newToken; } function calculateNewState(walletState, stateChange) { const destroyProofs = /* @__PURE__ */ new Set(); for (const proof of stateChange.destroy || []) destroyProofs.add(proof.C); const proofsToStore = /* @__PURE__ */ new Map(); let tokensToDelete; for (const proof of stateChange.store || []) proofsToStore.set(proof.C, proof); tokensToDelete = getAffectedTokens(walletState, stateChange); for (const token of tokensToDelete.values()) { for (const proof of token.proofs) { if (destroyProofs.has(proof.C)) continue; proofsToStore.set(proof.C, proof); } } return { deletedTokenIds: new Set(tokensToDelete.keys()), deletedProofs: destroyProofs, reserveProofs: [], saveProofs: Array.from(proofsToStore.values()) }; } function getAffectedTokens(walletState, stateChange) { const tokens = /* @__PURE__ */ new Map(); for (const proof of stateChange.destroy || []) { const proofEntry = walletState.proofs.get(proof.C); if (!proofEntry) { continue; } const tokenId = proofEntry.tokenId; if (!tokenId) { continue; } const tokenEntry = walletState.tokens.get(tokenId); if (!tokenEntry?.token) { continue; } tokens.set(tokenId, tokenEntry.token); } return tokens; } function payloadForEvent(privkeys, mints) { if (privkeys.length === 0) throw new Error("privkey not set"); const payload = [ ...mints.map((mint) => ["mint", mint]), ...privkeys.map((privkey) => ["privkey", privkey]) ]; return payload; } async function fetchPage(ndk, filter, _knownNutzaps, relaySet) { const events = await ndk.fetchEvents( filter, { cacheUsage: NDKSubscriptionCacheUsage2.ONLY_RELAY, groupable: false, subId: "recent-nutzap" }, relaySet ); return Array.from(events).map((e2) => NDKNutzap2.from(e2)).filter((n) => !!n); } function groupNutzaps(nutzaps, monitor) { const result = /* @__PURE__ */ new Map(); const getKey = (mint, p2pk = "no-key") => `${mint}:${p2pk}`; for (const nutzap of nutzaps) { if (!monitor.shouldTryRedeem(nutzap)) continue; const mint = nutzap.mint; for (const proof of nutzap.proofs) { const cashuPubkey = proofP2pk2(proof) ?? "no-key"; const key = getKey(mint, cashuPubkey); const group = result.get(key) ?? { mint, cashuPubkey, nostrPubkey: cashuPubkeyToNostrPubkey2(cashuPubkey), nutzaps: [] }; group.nutzaps.push(nutzap); result.set(key, group); } } return Array.from(result.values()); } async function getProofSpendState(wallet, nutzaps) { const result = { unspentProofs: [], spentProofs: [], nutzapsWithUnspentProofs: [], nutzapsWithSpentProofs: [] }; const proofCs = /* @__PURE__ */ new Set(); const proofs = []; const nutzapMap = /* @__PURE__ */ new Map(); for (const nutzap of nutzaps) { for (const proof of nutzap.proofs) { if (proofCs.has(proof.C)) continue; proofCs.add(proof.C); proofs.push(proof); nutzapMap.set(proof.C, nutzap); } } const states = await wallet.checkProofsStates(proofs); for (let i3 = 0; i3 < states.length; i3++) { const state = states[i3]; const proof = proofs[i3]; const nutzap = nutzapMap.get(proof.C); if (!nutzap) continue; if (state.state === as.SPENT) { result.spentProofs.push(proof); if (!result.nutzapsWithSpentProofs.some((n) => n.id === nutzap.id)) { result.nutzapsWithSpentProofs.push(nutzap); } } else if (state.state === as.UNSPENT) { result.unspentProofs.push(proof); if (!result.nutzapsWithUnspentProofs.some((n) => n.id === nutzap.id)) { result.nutzapsWithUnspentProofs.push(nutzap); } } } return result; } function log(_msg) { } function proofsIntersection(proofs1, proofs2) { const proofs2Cs = new Set(proofs2.map((p5) => p5.C)); return proofs1.filter((p5) => proofs2Cs.has(p5.C)); } function proofsTotal(proofs) { return proofs.reduce((acc, proof) => acc + proof.amount, 0); } async function getCashuMintRecommendations(ndk, filter) { const f = [ { kinds: [NDKKind2.EcashMintRecommendation], "#k": ["38002"], ...filter || {} }, { kinds: [NDKKind2.CashuMintList], ...filter || {} } ]; const res = {}; const recommendations = await ndk.fetchEvents(f); for (const event of recommendations) { switch (event.kind) { case NDKKind2.EcashMintRecommendation: for (const uTag of event.getMatchingTags("u")) { if (uTag[2] && uTag[2] !== "cashu") continue; const url = uTag[1]; if (!url) continue; const entry = res[url] || { events: [], pubkeys: /* @__PURE__ */ new Set() }; entry.events.push(event); entry.pubkeys.add(event.pubkey); res[url] = entry; } break; case NDKKind2.CashuMintList: for (const mintTag of event.getMatchingTags("mint")) { const url = mintTag[1]; if (!url) continue; const entry = res[url] || { events: [], pubkeys: /* @__PURE__ */ new Set() }; entry.events.push(event); entry.pubkeys.add(event.pubkey); res[url] = entry; } break; } } return res; } async function redeemNutzaps(nutzaps, privkey, { cashuWallet, proofs, mint }) { proofs ?? (proofs = nutzaps.flatMap((n) => n.proofs)); if (!cashuWallet) { if (!mint) throw new Error("No mint provided"); cashuWallet = await this.getCashuWallet(mint); } else { mint = cashuWallet.mint.mintUrl; } const info = await this.getInfo(); if (!info.methods.includes("make_invoice")) throw new Error("This NWC wallet does not support making invoices"); const totalAvailable = proofs.reduce((acc, proof) => acc + proof.amount, 0); let sweepAmount = totalAvailable; while (sweepAmount > 0) { const invoice = await this.makeInvoice(sweepAmount * 1e3, "Nutzap redemption"); const meltQuote = await cashuWallet.createMeltQuote(invoice.invoice); const totalRequired = meltQuote.amount + meltQuote.fee_reserve; if (totalRequired > totalAvailable) { sweepAmount -= meltQuote.fee_reserve; continue; } const result = await cashuWallet.meltProofs(meltQuote, proofs, { privkey }); let change; if (result.change.length > 0) change = await saveChange(this.ndk, mint, result.change); const description = `Nutzap redemption to external wallet (${this.walletId})`; createOutTxEvent( this.ndk, { pr: invoice.invoice, paymentDescription: description }, { result: { preimage: invoice.preimage }, mint, fee: meltQuote.fee_reserve, proofsChange: { store: change?.proofs }, stateUpdate: { created: change } }, this.relaySet, { nutzaps } ); return sweepAmount; } throw new Error("Failed to redeem nutzaps"); } async function saveChange(ndk, mint, change) { const totalChange = change.reduce((acc, proof) => acc + proof.amount, 0); if (totalChange === 0) return; const token = new NDKCashuToken2(ndk); token.mint = mint; token.proofs = change; token.publish(); return token; } async function waitForResponse(request) { if (!this.pool) throw new Error("Wallet not initialized"); const sendRequest = () => { if (waitForEoseTimeout) clearTimeout(waitForEoseTimeout); request.publish(this.relaySet); }; const waitForEoseTimeout = setTimeout(sendRequest, 2500); return new Promise((resolve, reject) => { const sub = this.ndk.subscribe( { kinds: [NDKKind2.NostrWalletConnectRes], "#e": [request.id], limit: 1 }, { groupable: false, pool: this.pool, relaySet: this.relaySet, onEvent: async (event) => { try { await event.decrypt(event.author, this.signer); const content = JSON.parse(event.content); if (content.error) { reject(content); } else { resolve(content); } } catch (e2) { console.error("error decrypting event", e2); reject({ result_type: "error", error: { code: "failed_to_parse_response", message: e2.message } }); } finally { sub.stop(); } }, onEose: () => { sendRequest(); } } ); }); } async function sendReq(method, params) { if (!this.walletService || !this.signer) { throw new Error("Wallet not initialized"); } const event = new NDKEvent2(this.ndk, { kind: NDKKind2.NostrWalletConnectReq, tags: [["p", this.walletService.pubkey]], content: JSON.stringify({ method, params }) }); await event.encrypt(this.walletService, this.signer, "nip04"); await event.sign(this.signer); const responsePromise = new Promise((resolve, reject) => { waitForResponse.call(this, event).then(resolve).catch(reject); }); if (this.timeout) { const timeoutPromise = new Promise( (_2, reject) => setTimeout(() => { this.emit("timeout", method); reject(new Error(`Request timed out after ${this.timeout}ms`)); }, this.timeout) ); return Promise.race([responsePromise, timeoutPromise]); } return responsePromise; } function toWalletTransaction(tx) { return { id: tx.payment_hash, direction: tx.type === "incoming" ? "in" : "out", amount: Math.floor(tx.amount / 1e3), timestamp: tx.created_at, description: tx.description, fee: tx.fees_paid ? Math.floor(tx.fees_paid / 1e3) : void 0, invoice: tx.invoice }; } var import_tseep21, import_debug36, import_light_bolt11_decoder4, import_webln, mintWallets, mintWalletPromises, NDKWalletStatus, NDKWallet, _a70, NDKCashuQuote, d11, NDKCashuDeposit, NDKCashuDepositMonitor, handlers, balanceUpdateTimer, d24, PaymentHandler, WalletState, _a71, NDKCashuWallet, NDKCashuWalletBackup, NDKNutzapMonitor, d34, TX_POLL_INTERVAL, NDKNWCWallet, NDKLnPay, NDKWebLNWallet; var init_dist3 = __esm({ "ndk/wallet/dist/index.js"() { "use strict"; init_dist(); init_vanilla(); import_tseep21 = __toESM(require_lib(), 1); init_cashu_ts_es(); init_dist2(); import_debug36 = __toESM(require_browser(), 1); import_light_bolt11_decoder4 = __toESM(require_bolt11(), 1); import_webln = __toESM(require_lib4(), 1); mintWallets = /* @__PURE__ */ new Map(); mintWalletPromises = /* @__PURE__ */ new Map(); NDKWalletStatus = /* @__PURE__ */ ((NDKWalletStatus22) => { NDKWalletStatus22["INITIAL"] = "initial"; NDKWalletStatus22["LOADING"] = "loading"; NDKWalletStatus22["READY"] = "ready"; NDKWalletStatus22["FAILED"] = "failed"; return NDKWalletStatus22; })(NDKWalletStatus || {}); NDKWallet = class extends import_tseep21.EventEmitter { constructor(ndk) { super(); __publicField(this, "cashuWallets", /* @__PURE__ */ new Map()); __publicField(this, "onMintInfoNeeded"); __publicField(this, "onMintInfoLoaded"); __publicField(this, "onMintKeysNeeded"); __publicField(this, "onMintKeysLoaded"); __publicField(this, "getCashuWallet", getCashuWallet.bind(this)); __publicField(this, "ndk"); __publicField(this, "status", "initial"); /** * An ID of this wallet */ __publicField(this, "walletId", "unknown"); this.ndk = ndk; } get type() { throw new Error("Not implemented"); } /** * Get the balance of this wallet */ get balance() { throw new Error("Not implemented"); } /** * Fetch transaction history */ async fetchTransactions() { return []; } /** * Subscribe to transaction updates. * - Cashu: real-time via relay subscription * - NWC: polls slowly + triggers on wallet activity */ subscribeTransactions(_callback) { return () => { }; } /** * Redeem a set of nutzaps into an NWC wallet. * * This function gets an invoice from the NWC wallet until the total amount of the nutzaps is enough to pay for the invoice * when accounting for fees. * * @param cashuWallet - The cashu wallet to redeem the nutzaps into * @param nutzapIds - The IDs of the nutzaps to redeem * @param proofs - The proofs to redeem * @param privkey - The private key needed to redeem p2pk proofs. */ redeemNutzaps(_nutzaps, _privkey, _opts) { throw new Error("Not implemented"); } }; NDKCashuQuote = (_a70 = class extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); __publicField(this, "quoteId"); __publicField(this, "mint"); __publicField(this, "amount"); __publicField(this, "unit"); __publicField(this, "_wallet"); this.kind ?? (this.kind = NDKKind2.CashuQuote); } static async from(event) { const quote = new _a70(event.ndk, event); const original = event; try { await quote.decrypt(); } catch { quote.content = original.content; } try { const content = JSON.parse(quote.content); quote.quoteId = content.quoteId; quote.mint = content.mint; quote.amount = content.amount; quote.unit = content.unit; } catch (_e2) { return; } return quote; } set wallet(wallet) { this._wallet = wallet; } set invoice(invoice) { const bolt11Expiry = getBolt11ExpiresAt(invoice); if (bolt11Expiry) this.tags.push(["expiration", bolt11Expiry.toString()]); } async save() { if (!this.ndk) throw new Error("NDK is required"); this.content = JSON.stringify({ quoteId: this.quoteId, mint: this.mint, amount: this.amount, unit: this.unit }); await this.encrypt(this.ndk.activeUser, void 0, "nip44"); await this.sign(); await this.publish(this._wallet?.relaySet); } }, __publicField(_a70, "kind", NDKKind2.CashuQuote), _a70); d11 = (0, import_debug36.default)("ndk-wallet:cashu:deposit"); NDKCashuDeposit = class _NDKCashuDeposit extends import_tseep21.EventEmitter { constructor(wallet, amount, mint) { super(); __publicField(this, "mint"); __publicField(this, "amount"); __publicField(this, "quoteId"); __publicField(this, "wallet"); __publicField(this, "checkTimeout"); __publicField(this, "checkIntervalLength", 2500); __publicField(this, "finalized", false); __publicField(this, "quoteEvent"); this.wallet = wallet; this.mint = mint || randomMint(wallet); this.amount = amount; } static fromQuoteEvent(wallet, quote) { if (!quote.amount) throw new Error("quote has no amount"); if (!quote.mint) throw new Error("quote has no mint"); const deposit = new _NDKCashuDeposit(wallet, quote.amount, quote.mint); deposit.quoteId = quote.quoteId; return deposit; } /** * Creates a quote ID and start monitoring for payment. * * Once a payment is received, the deposit will emit a "success" event. * * @param pollTime - time in milliseconds between checks * @returns */ async start(pollTime = 2500) { const cashuWallet = await this.wallet.getCashuWallet(this.mint); const quote = await cashuWallet.createMintQuote(this.amount); d11("created quote %s for %d %s", quote.quote, this.amount, this.mint); this.quoteId = quote.quote; this.wallet.depositMonitor.addDeposit(this); setTimeout(this.check.bind(this, pollTime), pollTime); this.createQuoteEvent(quote.quote, quote.request).then((event) => this.quoteEvent = event); return quote.request; } /** * This generates a 7374 event containing the quote ID * with an optional expiration set to the bolt11 expiry (if there is one) */ async createQuoteEvent(quoteId, bolt11) { const { ndk } = this.wallet; const quoteEvent = new NDKCashuQuote(ndk); quoteEvent.quoteId = quoteId; quoteEvent.mint = this.mint; quoteEvent.amount = this.amount; quoteEvent.wallet = this.wallet; quoteEvent.invoice = bolt11; try { await quoteEvent.save(); d11("saved quote on event %s", quoteEvent.rawEvent()); } catch (e2) { d11("error saving quote on event %s", e2.relayErrors); } return quoteEvent; } async runCheck() { if (!this.finalized) await this.finalize(); if (!this.finalized) this.delayCheck(); } delayCheck() { setTimeout(() => { this.runCheck(); this.checkIntervalLength += 500; }, this.checkIntervalLength); } /** * Check if the deposit has been finalized. * @param timeout A timeout in milliseconds to wait before giving up. */ async check(timeout) { this.runCheck(); if (timeout) { setTimeout(() => { clearTimeout(this.checkTimeout); }, timeout); } } async finalize() { if (!this.quoteId) throw new Error("No quoteId set."); let proofs; try { d11("Checking for minting status of %s", this.quoteId); const cashuWallet = await this.wallet.getCashuWallet(this.mint); const proofsWeHave = await this.wallet.state.getProofs({ mint: this.mint }); proofs = await cashuWallet.mintProofs(this.amount, this.quoteId, { proofsWeHave }); if (proofs.length === 0) return; } catch (e2) { if (e2.message.match(/not paid/i)) return; if (e2.message.match(/already issued/i)) { d11("Mint is saying the quote has already been issued, destroying quote event: %s", e2.message); this.destroyQuoteEvent(); this.finalized = true; return; } if (e2.message.match(/rate limit/i)) { d11("Mint seems to be rate limiting, lowering check interval"); this.checkIntervalLength += 5e3; return; } d11(e2.message); return; } try { this.finalized = true; const updateRes = await this.wallet.state.update( { store: proofs, mint: this.mint }, "Deposit" ); const tokenEvent = updateRes.created; if (!tokenEvent) throw new Error("no token event created"); createInTxEvent( this.wallet.ndk, proofs, this.mint, updateRes, { description: "Deposit" }, this.wallet.relaySet ); this.emit("success", tokenEvent); this.destroyQuoteEvent(); } catch (e2) { this.emit("error", e2.message); console.error(e2); } } async destroyQuoteEvent() { if (!this.quoteEvent) return; const deleteEvent = await this.quoteEvent.delete(void 0, false); deleteEvent.publish(this.wallet.relaySet); } }; NDKCashuDepositMonitor = class extends import_tseep21.EventEmitter { constructor() { super(...arguments); __publicField(this, "deposits", /* @__PURE__ */ new Map()); } addDeposit(deposit) { const { quoteId } = deposit; if (!quoteId) throw new Error("deposit has no quote ID"); if (this.deposits.has(quoteId)) return false; deposit.once("success", (_token) => { this.removeDeposit(quoteId); }); this.deposits.set(quoteId, deposit); this.emit("change"); return true; } removeDeposit(quoteId) { this.deposits.delete(quoteId); this.emit("change"); } }; setInterval(() => { }, 5e3); handlers = { [NDKKind2.CashuToken]: handleToken, [NDKKind2.CashuQuote]: handleQuote, [NDKKind2.EventDeletion]: handleEventDeletion }; balanceUpdateTimer = null; d24 = (0, import_debug36.default)("ndk-wallet:cashu:validate"); PaymentHandler = class { constructor(wallet) { __publicField(this, "wallet"); this.wallet = wallet; } /** * Pay a LN invoice with this wallet. This will used cashu proofs to pay a bolt11. */ async lnPay(payment, createTxEvent = true) { if (!payment.pr) throw new Error("pr is required"); const invoiceAmount = getBolt11Amount(payment.pr); if (!invoiceAmount) throw new Error("invoice amount is required"); if (payment.amount && invoiceAmount > payment.amount) { throw new Error("invoice amount is more than the amount passed in"); } const res = await payLn(this.wallet, payment.pr, { amount: payment.amount, unit: payment.unit }); if (!res?.result?.preimage) return; if (createTxEvent) { createOutTxEvent(this.wallet.ndk, payment, res, this.wallet.relaySet); } return res.result; } /** * Swaps tokens to a specific amount, optionally locking to a p2pk. */ async cashuPay(payment) { console.log("[PaymentHandler.cashuPay] Starting cashu payment", { originalAmount: payment.amount, unit: payment.unit, mints: payment.mints, p2pk: payment.p2pk, allowIntramintFallback: payment.allowIntramintFallback }); const satPayment = { ...payment }; if (satPayment.unit?.startsWith("msat")) { satPayment.amount = satPayment.amount / 1e3; satPayment.unit = "sat"; console.log("[PaymentHandler.cashuPay] Converted msat to sat", { newAmount: satPayment.amount, newUnit: satPayment.unit }); } console.log("[PaymentHandler.cashuPay] Creating token with mints", payment.mints); let createResult = await createToken(this.wallet, satPayment.amount, payment.mints, payment.p2pk, payment.proofTags); if (!createResult?.result) { console.log("[PaymentHandler.cashuPay] Token creation failed with specified mints"); if (payment.allowIntramintFallback) { console.log("[PaymentHandler.cashuPay] Attempting intramint fallback"); createResult = await createToken(this.wallet, satPayment.amount, void 0, payment.p2pk, payment.proofTags); } if (!createResult?.result) { console.error("[PaymentHandler.cashuPay] Token creation failed completely"); return; } } console.log("[PaymentHandler.cashuPay] Token created successfully", { proofsCount: createResult.result.proofs.length, mint: createResult.result.mint }); createOutTxEvent(this.wallet.ndk, satPayment, createResult, this.wallet.relaySet); return createResult.result; } }; WalletState = class { constructor(wallet, reservedProofCs = /* @__PURE__ */ new Set()) { /** * the amounts that are intended to be reserved * this is the net amount we are trying to pay out, * excluding fees and coin sizes * e.g. we might want to pay 5 sats, have 2 sats in fees * and we're using 2 inputs that add up to 8, the reserve amount is 5 * while the reserve proofs add up to 8 */ __publicField(this, "reserveAmounts", []); /** * Source of truth of the proofs this wallet has/had. */ __publicField(this, "proofs", /* @__PURE__ */ new Map()); /** * The tokens that are known to this wallet. */ __publicField(this, "tokens", /* @__PURE__ */ new Map()); __publicField(this, "journal", []); /*************************** * Tokens ***************************/ __publicField(this, "addToken", addToken.bind(this)); __publicField(this, "removeTokenId", removeTokenId.bind(this)); /*************************** * Proof management ***************************/ __publicField(this, "addProof", addProof.bind(this)); /** * Reserves a number of selected proofs and a specific amount. * * The amount and total of the proofs don't need to match. We * might want to use 5 sats and have 2 proofs of 4 sats each. * In that case, the reserve amount is 5, while the reserve proofs * add up to 8. */ __publicField(this, "reserveProofs", reserveProofs.bind(this)); /** * Unreserves a number of selected proofs and a specific amount. */ __publicField(this, "unreserveProofs", unreserveProofs.bind(this)); /** * Returns all proof entries, optionally filtered by mint and state */ __publicField(this, "getProofEntries", getProofEntries.bind(this)); /** * Updates information about a proof */ __publicField(this, "updateProof", updateProof.bind(this)); /*************************** * Balance ***************************/ /** * Returns the balance of the wallet, optionally filtered by mint and state * * @params opts.mint - optional mint to filter by * @params opts.onlyAvailable - only include available proofs @default true */ __publicField(this, "getBalance", getBalance.bind(this)); /** * Returns the balances of the different mints * * @params opts.onlyAvailable - only include available proofs @default true */ __publicField(this, "getMintsBalance", getMintsBalances.bind(this)); /*************************** * State update ***************************/ __publicField(this, "update", update.bind(this)); this.wallet = wallet; this.reservedProofCs = reservedProofCs; } /** This is a debugging function that dumps the state of the wallet */ dump() { const res = { proofs: Array.from(this.proofs.values()), balances: this.getMintsBalance(), totalBalance: this.getBalance(), tokens: Array.from(this.tokens.values()) }; return res; } /** * Returns all proofs, optionally filtered by mint and state * @param opts.mint - optional mint to filter by * @param opts.onlyAvailable - only include available proofs @default true * @param opts.includeDeleted - include deleted proofs @default false */ getProofs(opts) { return this.getProofEntries(opts).map((entry) => entry.proof); } getTokens(opts = { onlyAvailable: true }) { const proofEntries = this.getProofEntries(opts); const tokens = /* @__PURE__ */ new Map(); for (const proofEntry of proofEntries) { const tokenId = proofEntry.tokenId ?? null; const current = tokens.get(tokenId) ?? { tokenId, mint: proofEntry.mint, proofEntries: [] }; current.token ?? (current.token = tokenId ? this.tokens.get(tokenId)?.token : void 0); current.proofEntries.push(proofEntry); tokens.set(tokenId, current); } return tokens; } /** * Gets a list of proofs for each mint * @returns */ getMintsProofs({ validStates = /* @__PURE__ */ new Set(["available"]) } = {}) { const mints = /* @__PURE__ */ new Map(); for (const entry of this.proofs.values()) { if (!entry.mint || !entry.proof) continue; if (!validStates.has(entry.state)) continue; const current = mints.get(entry.mint) || []; current.push(entry.proof); mints.set(entry.mint, current); } return mints; } }; NDKCashuWallet = (_a71 = class extends NDKWallet { constructor(ndk) { super(ndk); __publicField(this, "_p2pk"); __publicField(this, "sub"); __publicField(this, "status", "initial"); /** * List of mint URLs configured for this wallet. * Modify directly to add/remove mints, then call publish() to save. * * @example * // Add a mint * wallet.mints = [...wallet.mints, 'https://mint.example.com']; * await wallet.publish(); * * @example * // Remove a mint * wallet.mints = wallet.mints.filter(url => url !== 'https://old-mint.com'); * await wallet.publish(); */ __publicField(this, "mints", []); __publicField(this, "privkeys", /* @__PURE__ */ new Map()); __publicField(this, "signer"); __publicField(this, "walletId", "nip-60"); __publicField(this, "depositMonitor", new NDKCashuDepositMonitor()); /** * Warnings that have been raised */ __publicField(this, "warnings", []); __publicField(this, "paymentHandler"); __publicField(this, "state"); /** * Relay set for wallet events (kinds 7374, 7375, 7376). * Modify directly to add/remove relays, then call publish() to save. * If undefined, falls back to NIP-65 relay list. * * @example * // Set relays * wallet.relaySet = NDKRelaySet.fromRelayUrls(['wss://relay1.com', 'wss://relay2.com'], ndk); * await wallet.publish(); * * @example * // Clear relays (use NIP-65 fallback) * wallet.relaySet = undefined; * await wallet.publish(); */ __publicField(this, "relaySet"); __publicField(this, "_walletRelays", []); __publicField(this, "consolidateTokens", consolidateTokens.bind(this)); __publicField(this, "wallets", /* @__PURE__ */ new Map()); this.ndk = ndk; this.paymentHandler = new PaymentHandler(this); this.state = new WalletState(this); if (ndk.cacheAdapter?.getCacheData && ndk.cacheAdapter?.setCacheData) { const callbacks = createMintCacheCallbacks(ndk.cacheAdapter); this.onMintInfoNeeded = callbacks.onMintInfoNeeded; this.onMintInfoLoaded = callbacks.onMintInfoLoaded; this.onMintKeysNeeded = callbacks.onMintKeysNeeded; this.onMintKeysLoaded = callbacks.onMintKeysLoaded; } } get type() { return "nip-60"; } /** * Generates a backup event for this wallet */ async backup(publish = true) { if (this.privkeys.size === 0) throw new Error("no privkey to backup"); const backup = new NDKCashuWalletBackup(this.ndk); const privkeys = []; for (const [_pubkey, signer] of this.privkeys.entries()) { privkeys.push(signer.privateKey); } backup.privkeys = privkeys; backup.mints = this.mints; if (publish) backup.save(this.relaySet); return backup; } /** * Generates nuts that can be used to send to someone. * * Note that this function does not send anything, it just generates a specific amount of proofs. * @param amounts * @returns */ async mintNuts(amounts) { let result; const totalAmount = amounts.reduce((acc, amount) => acc + amount, 0); for (const mint of this.mints) { const wallet = await this.getCashuWallet(mint); const mintProofs22 = await this.state.getProofs({ mint }); result = await wallet.send(totalAmount, mintProofs22, { proofsWeHave: mintProofs22, includeFees: true, outputAmounts: { sendAmounts: amounts } }); if (result.send.length > 0) { const change = { store: result?.keep ?? [], destroy: result.send, mint }; const updateRes = await this.state.update(change); createOutTxEvent( this.ndk, { paymentDescription: "minted nuts", amount: amounts.reduce((acc, amount) => acc + amount, 0) }, { result: { proofs: result.send }, stateUpdate: updateRes, mint, fee: 0 }, this.relaySet ); this.emit("balance_updated"); return result; } } } /** * Creates a cashu token that can be sent to someone. * This method mints the specified amount and returns an encoded token string. * * @param amount - Amount in satoshis to send * @param memo - Optional memo to include in the token * @returns Encoded cashu token string * * @example * const token = await wallet.send(1000, "Coffee payment"); * // token is a cashu token string that can be shared */ async send(amount, memo) { if (this.mints.length === 0) throw new Error("No mints configured"); const result = await this.mintNuts([amount]); if (!result) throw new Error("Failed to create token"); return es({ mint: this.mints[0], proofs: result.send, memo }); } /** * Loads a wallet information from an event * @param event */ async loadFromEvent(event) { const _event = new NDKEvent2(event.ndk, event.rawEvent()); await _event.decrypt(); const content = JSON.parse(_event.content); for (const tag of content) { if (tag[0] === "mint") { this.mints.push(tag[1]); } else if (tag[0] === "privkey") { await this.addPrivkey(tag[1]); } else if (tag[0] === "relay") { this._walletRelays.push(tag[1]); } } await this.getP2pk(); } static async from(event) { if (!event.ndk) throw new Error("no ndk instance on event"); const wallet = new _a71(event.ndk); await wallet.loadFromEvent(event); return wallet; } /** * Creates a new NIP-60 wallet with the specified configuration. * Generates a private key, publishes the wallet event (kind 17375), and creates a backup (kind 375). * * @param ndk - NDK instance * @param mints - Array of mint URLs to configure * @param relays - Optional array of relay URLs for wallet events * @returns The newly created and published wallet * * @example * const wallet = await NDKCashuWallet.create( * ndk, * ['https://mint.example.com'], * ['wss://relay.example.com'] * ); */ static async create(ndk, mints, relays) { const wallet = new _a71(ndk); const signer = NDKPrivateKeySigner2.generate(); await wallet.addPrivkey(signer.privateKey); wallet.mints = mints; if (relays && relays.length > 0) { wallet.relaySet = NDKRelaySet2.fromRelayUrls(relays, ndk); } await wallet.publish(); await wallet.backup(true); return wallet; } /** * Fetches relay configuration for the wallet according to NIP-60. * First tries to get relays from encrypted wallet relays, * falls back to NIP-65 (kind 10002) relays if not found. */ async fetchWalletRelays(pubkey) { if (this._walletRelays.length > 0) { return NDKRelaySet2.fromRelayUrls(this._walletRelays, this.ndk); } const relayListEvent = await this.ndk.fetchEvent( { kinds: [NDKKind2.RelayList], authors: [pubkey] }, { cacheUsage: NDKSubscriptionCacheUsage2.PARALLEL } ); if (relayListEvent) { return NDKRelayList2.from(relayListEvent).relaySet; } return void 0; } /** * Starts monitoring the wallet. * * Use `since` to start syncing state from a specific timestamp. This should be * used by storing at the app level a time in which we know we were able to communicate * with the relays, for example, by saving the time the wallet has emitted a "ready" event. */ async start(opts) { const activeUser = this.ndk?.activeUser; if (this.status === "ready") return Promise.resolve(); this.setStatus( "loading" /* LOADING */ ); const pubkey = opts?.pubkey ?? activeUser?.pubkey; if (!pubkey) throw new Error("no pubkey"); if (!this.relaySet) { this.relaySet = await this.fetchWalletRelays(pubkey); } const filters = [ { kinds: [NDKKind2.CashuToken], authors: [pubkey] }, { kinds: [NDKKind2.CashuQuote], authors: [pubkey] }, { kinds: [NDKKind2.EventDeletion], authors: [pubkey], "#k": [NDKKind2.CashuToken.toString()] } ]; if (opts?.since) { filters[0].since = opts.since; filters[1].since = opts.since; filters[2].since = opts.since; } if (this.ndk.cacheAdapter) { const cacheEvents = []; const events = await this.ndk.fetchEvents([{ kinds: [NDKKind2.CashuToken], authors: [pubkey] }], { cacheUsage: NDKSubscriptionCacheUsage2.ONLY_CACHE }); cacheEvents.push(...events); for (const event of cacheEvents) { eventHandler.call(this, event); } this.emit("balance_updated"); } if (this.ndk.cacheAdapter) { try { const syncResult = await NDKSync.sync(this.ndk, filters, { relaySet: this.relaySet, autoFetch: true }); for (const event of syncResult.events) { eventHandler.call(this, event); } const subOpts = opts ?? {}; subOpts.subId ?? (subOpts.subId = "cashu-wallet-state"); const liveFilters = filters.map((f) => ({ ...f, since: Math.floor(Date.now() / 1e3) - 60 })); this.sub = this.ndk.subscribe(liveFilters, { ...subOpts, relaySet: this.relaySet, closeOnEose: false, onEvent: (event) => { eventHandler.call(this, event); }, onEventDup: eventDupHandler.bind(this) }); this.emit("ready"); this.setStatus( "ready" /* READY */ ); } catch (error) { console.error(`[NDKCashuWallet] Sync failed, falling back to subscription:`, error); await this.startWithSubscription(filters, opts); } } else { await this.startWithSubscription(filters, opts); } } /** * Starts wallet monitoring using traditional subscription (fallback when sync unavailable) */ async startWithSubscription(filters, opts) { const subOpts = opts ?? {}; subOpts.subId ?? (subOpts.subId = "cashu-wallet-state"); return new Promise((resolve) => { this.sub = this.ndk.subscribe(filters, { ...subOpts, relaySet: this.relaySet, onEvent: (event) => { eventHandler.call(this, event); }, onEose: async () => { this.emit("ready"); this.setStatus( "ready" /* READY */ ); resolve(); }, onEventDup: eventDupHandler.bind(this) }); }); } stop() { this.sub?.stop(); this.setStatus( "initial" /* INITIAL */ ); } setStatus(status) { if (this.status !== status) { this.status = status; this.emit("status_changed", status); } } /** * Returns the p2pk of this wallet or generates a new one if we don't have one */ async getP2pk() { if (this._p2pk) return this._p2pk; if (this.privkeys.size === 0) { const signer = NDKPrivateKeySigner2.generate(); await this.addPrivkey(signer.privateKey); } return this.p2pk; } /** * If this wallet has access to more than one privkey, this will return all of them. */ get p2pks() { return Array.from(this.privkeys.keys()); } async addPrivkey(privkey) { const signer = new NDKPrivateKeySigner2(privkey); const user = await signer.user(); this.privkeys.set(user.pubkey, signer); this._p2pk ?? (this._p2pk = user.pubkey); return this._p2pk; } get p2pk() { if (!this._p2pk) throw new Error("p2pk not set"); return this._p2pk; } set p2pk(pubkey) { if (this.privkeys.has(pubkey)) { this.signer = this.privkeys.get(pubkey); this.p2pk = pubkey; } else { throw new Error(`privkey for ${pubkey} not found`); } } /** * Generates the payload for a wallet event */ walletPayload() { const privkeys = Array.from(this.privkeys.values()).map((signer) => signer.privateKey); const payload = payloadForEvent(privkeys, this.mints); if (this._walletRelays.length > 0) { payload.push(...this._walletRelays.map((relay) => ["relay", relay])); } return payload; } /** * Publishes the wallet configuration (kind 17375) to save changes. * Call this after modifying mints or relaySet to persist the configuration. * * The wallet event contains encrypted mint URLs, private keys, and relay URLs. * * @example * // Add a mint and save * wallet.mints.push('https://mint.example.com'); * await wallet.publish(); * * @example * // Update relays and save * wallet.relaySet = NDKRelaySet.fromRelayUrls(['wss://relay.example.com'], ndk); * await wallet.publish(); */ async publish() { if (this.relaySet) { this._walletRelays = Array.from(this.relaySet.relays).map((relay) => relay.url); } const event = new NDKEvent2(this.ndk, { content: JSON.stringify(this.walletPayload()), kind: NDKKind2.CashuWallet }); const user = await this.ndk?.signer?.user(); await event.encrypt(user, void 0, "nip44"); return event.publish(this.relaySet); } /** * Publishes the CashuMintList (kind 10019) for nutzap reception. * This public event tells others which mints and relays to use when sending nutzaps. * * @example * await wallet.publishMintList(); */ async publishMintList() { const mintList = new NDKCashuMintList2(this.ndk); mintList.mints = this.mints; if (this.relaySet) { mintList.relays = Array.from(this.relaySet.relays).map((relay) => relay.url); } mintList.p2pk = this.p2pk; return mintList.publishReplaceable(this.relaySet); } /** * Updates wallet configuration (mints and relays) and publishes the changes. * Uses publishReplaceable to ensure the event replaces the previous wallet configuration. * * @param config - Configuration object with mints and optional relays * * @example * // Update mints only * await wallet.update({ mints: ['https://mint.example.com'] }); * * @example * // Update both mints and relays * await wallet.update({ * mints: ['https://mint.example.com'], * relays: ['wss://relay.example.com'] * }); */ async update(config) { this.mints = config.mints; if (config.relays && config.relays.length > 0) { this.relaySet = NDKRelaySet2.fromRelayUrls(config.relays, this.ndk); this._walletRelays = config.relays; } else { this.relaySet = void 0; this._walletRelays = []; } const event = new NDKEvent2(this.ndk, { content: JSON.stringify(this.walletPayload()), kind: NDKKind2.CashuWallet }); const user = await this.ndk?.signer?.user(); await event.encrypt(user, void 0, "nip44"); return event.publishReplaceable(this.relaySet); } /** * Prepares a deposit * @param amount * @param mint * * @example * const wallet = new NDKCashuWallet(...); * const deposit = wallet.deposit(1000, "https://mint.example.com", "sats"); * deposit.on("success", (token) => { * }); * deposit.on("error", (error) => { * }); * * // start monitoring the deposit * deposit.start(); */ deposit(amount, mint) { const deposit = new NDKCashuDeposit(this, amount, mint); deposit.on("success", (token) => { this.state.addToken(token); }); return deposit; } /** * Receives a token and adds it to the wallet * @param token * @returns the token event that was created */ async receiveToken(token, description) { const { mint } = Me(token); const wallet = await this.getCashuWallet(mint); const proofs = await wallet.receive(token); const updateRes = await this.state.update({ store: proofs, mint }); const tokenEvent = updateRes.created; createInTxEvent(this.ndk, proofs, mint, updateRes, { description }, this.relaySet); return tokenEvent; } /** * Pay a LN invoice with this wallet */ async lnPay(payment, createTxEvent = true) { return this.paymentHandler.lnPay(payment, createTxEvent); } /** * Swaps tokens to a specific amount, optionally locking to a p2pk. * * This function has side effects: * - It swaps tokens at the mint * - It updates the wallet state (deletes affected tokens, might create new ones) * - It creates a wallet transaction event * * This function returns the proofs that need to be sent to the recipient. * @param amount */ async cashuPay(payment) { return this.paymentHandler.cashuPay(payment); } async redeemNutzaps(nutzaps, privkey, { mint, proofs, cashuWallet }) { if (cashuWallet) { mint ?? (mint = cashuWallet.mint.mintUrl); } else { if (!mint) throw new Error("mint not set"); cashuWallet = await this.getCashuWallet(mint); } if (!mint) throw new Error("mint not set"); if (!proofs) throw new Error("proofs not set"); try { const proofsWeHave = this.state.getProofs({ mint }); const res = await cashuWallet.receive({ proofs, mint }, { proofsWeHave, privkey }); const receivedAmount = proofs.reduce((acc, proof) => acc + proof.amount, 0); const redeemedAmount = res.reduce((acc, proof) => acc + proof.amount, 0); const fee = receivedAmount - redeemedAmount; const updateRes = await this.state.update({ store: res, mint }); createInTxEvent(this.ndk, res, mint, updateRes, { nutzaps, fee }, this.relaySet); return receivedAmount; } catch (e2) { console.error( "error redeeming nutzaps", nutzaps.map((n) => n.encode()), e2 ); throw e2; } } warn(msg, event, relays) { relays ?? (relays = event?.onRelays); this.warnings.push({ msg, event, relays }); this.emit("warning", { msg, event, relays }); } get balance() { return { amount: this.state.getBalance({ onlyAvailable: true }) }; } /** * Gets the total balance for a specific mint, including reserved proofs */ mintBalance(mint) { return this.mintBalances[mint] || 0; } /** * Gets all tokens, grouped by mint with their total balances */ get mintBalances() { return this.state.getMintsBalance({ onlyAvailable: true }); } /** * Returns a list of mints that have enough available balance (excluding reserved proofs) * to cover the specified amount */ getMintsWithBalance(amount) { const availableBalances = this.state.getMintsBalance({ onlyAvailable: true }); return Object.entries(availableBalances).filter(([_2, balance]) => balance >= amount).map(([mint]) => mint); } /** * Gets mint information for a specific mint URL. * Returns cached info if available, otherwise fetches from the mint. */ async getMintInfo(mintUrl) { const cashuWallet = await this.getCashuWallet(mintUrl); return await cashuWallet.mint.getInfo(); } /** * Fetches transaction history (kind 7376) for this wallet. */ async fetchTransactions() { const user = await this.ndk.signer?.user(); if (!user) return []; const events = await this.ndk.fetchEvents( { kinds: [NDKKind2.CashuWalletTx], authors: [user.pubkey] }, { cacheUsage: NDKSubscriptionCacheUsage2.PARALLEL }, this.relaySet ); const transactions = []; for (const event of events) { const tx = await NDKCashuWalletTx2.from(event); if (tx) { transactions.push(this.txEventToTransaction(tx)); } } return transactions.sort((a, b) => b.timestamp - a.timestamp); } /** * Subscribes to transaction updates (kind 7376) for real-time updates. */ subscribeTransactions(callback) { const user = this.ndk.activeUser; if (!user) return () => { }; const seenIds = /* @__PURE__ */ new Set(); this.fetchTransactions().then((txs) => { for (const tx of txs) { if (!seenIds.has(tx.id)) { seenIds.add(tx.id); callback(tx); } } }); const sub = this.ndk.subscribe( { kinds: [NDKKind2.CashuWalletTx], authors: [user.pubkey] }, { closeOnEose: false, relaySet: this.relaySet, onEvent: async (event) => { if (seenIds.has(event.id)) return; seenIds.add(event.id); const tx = await NDKCashuWalletTx2.from(event); if (tx) { callback(this.txEventToTransaction(tx)); } } } ); return () => sub.stop(); } txEventToTransaction(tx) { return { id: tx.id, direction: tx.direction ?? "out", amount: tx.amount ?? 0, timestamp: tx.created_at ?? 0, description: tx.description, fee: tx.fee, mint: tx.mint }; } }, __publicField(_a71, "kind", NDKKind2.CashuWallet), __publicField(_a71, "kinds", [NDKKind2.CashuWallet]), _a71); NDKCashuWalletBackup = class _NDKCashuWalletBackup extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); __publicField(this, "privkeys", []); __publicField(this, "mints", []); this.kind ?? (this.kind = NDKKind2.CashuWalletBackup); } static async from(event) { if (!event.ndk) throw new Error("no ndk instance on event"); const backup = new _NDKCashuWalletBackup(event.ndk, event); try { await backup.decrypt(); const content = JSON.parse(backup.content); for (const tag of content) { if (tag[0] === "mint") { backup.mints.push(tag[1]); } else if (tag[0] === "privkey") { backup.privkeys.push(tag[1]); } } } catch (e2) { console.error("error decrypting backup event", backup.encode(), e2); return; } return backup; } async save(relaySet) { if (!this.ndk) throw new Error("no ndk instance"); if (!this.privkeys.length) throw new Error("no privkeys"); this.content = JSON.stringify(payloadForEvent(this.privkeys, this.mints)); await this.encrypt(this.ndk.activeUser, void 0, "nip44"); return this.publish(relaySet); } }; NDKNutzapMonitor = class extends import_tseep21.EventEmitter { /** * Create a new nutzap monitor. * @param ndk - The NDK instance. * @param user - The user to monitor. * @param mintList - An optional mint list to monitor zaps on, if one is not provided, the monitor will use the relay set from the mint list, which is the correct default behavior of NIP-61 zaps. * @param store - An optional store to save and load nutzap states to. */ constructor(ndk, user, { mintList, store }) { super(); __publicField(this, "store"); __publicField(this, "ndk"); __publicField(this, "user"); __publicField(this, "relaySet"); __publicField(this, "sub"); __publicField(this, "nutzapStates", /* @__PURE__ */ new Map()); __publicField(this, "_wallet"); __publicField(this, "mintList"); __publicField(this, "privkeys", /* @__PURE__ */ new Map()); __publicField(this, "cashuWallets", /* @__PURE__ */ new Map()); __publicField(this, "getCashuWallet", getCashuWallet.bind(this)); __publicField(this, "onMintInfoNeeded"); __publicField(this, "onMintInfoLoaded"); __publicField(this, "onMintKeysNeeded"); __publicField(this, "onMintKeysLoaded"); this.ndk = ndk; this.user = user; this.mintList = mintList; this.relaySet = mintList?.relaySet; this.store = store; } set wallet(wallet) { this._wallet = wallet; if (wallet) { this.onMintInfoNeeded ?? (this.onMintInfoNeeded = wallet.onMintInfoNeeded); this.onMintInfoLoaded ?? (this.onMintInfoLoaded = wallet.onMintInfoLoaded); this.onMintKeysNeeded ?? (this.onMintKeysNeeded = wallet.onMintKeysNeeded); this.onMintKeysLoaded ?? (this.onMintKeysLoaded = wallet.onMintKeysLoaded); if (wallet instanceof NDKCashuWallet && wallet?.privkeys) { for (const [pubkey, signer] of wallet.privkeys.entries()) { try { this.addPrivkey(signer); } catch (e2) { console.error("failed to add privkey from wallet with pubkey", pubkey, e2); } } } } } get wallet() { return this._wallet; } /** * Provide private keys that can be used to redeem nutzaps. * * This is particularly useful when a NWC wallet is used to receive the nutzaps, * since it doesn't have a private key, this allows keeping the private key in a separate * place (ideally a NIP-60 wallet event). * * Multiple keys can be added, and the monitor will use the correct key for the nutzap. */ async addPrivkey(signer) { const pubkey = (await signer.user()).pubkey; if (this.privkeys.has(pubkey)) return; this.privkeys.set(pubkey, signer); if (!this.sub) return; const inMssingPrivKeyState = (state) => state.status === NdkNutzapStatus2.MISSING_PRIVKEY; const ensureIsCashuPubkey22 = (state) => state.nutzap?.p2pk === pubkey; const candidateNutzaps = Array.from(this.nutzapStates.values()).filter(inMssingPrivKeyState).filter(ensureIsCashuPubkey22); if (candidateNutzaps.length > 0) { const nutzaps = candidateNutzaps.map((c) => c.nutzap).filter((n) => !!n); const groupedNutzaps = groupNutzaps(nutzaps, this); for (const group of groupedNutzaps) { await this.checkAndRedeemGroup(group); } } } async addUserPrivKey() { const { signer } = this.ndk; if (signer instanceof NDKPrivateKeySigner2) { const user = await signer.user(); const pubkey = user.pubkey; this.privkeys.set(pubkey, signer); } } /** * Loads kind:375 backup events and kind:17375 wallet config events from this user * to find all backup keys this user might have used. */ async getBackupKeys() { const backupEvents = await this.ndk.fetchEvents( [{ kinds: [NDKKind2.CashuWalletBackup, NDKKind2.CashuWallet], authors: [this.user.pubkey] }], void 0, this.relaySet ); const keys = Array.from(this.privkeys.values()); const keysNotFound = new Set(keys.map((signer) => signer.privateKey)); for (const event of backupEvents) { if (event.kind === NDKKind2.CashuWalletBackup) { const backup = await NDKCashuWalletBackup.from(event); if (!backup) continue; for (const privkey of backup.privkeys) { if (keysNotFound.has(privkey)) keysNotFound.delete(privkey); try { const signer = new NDKPrivateKeySigner2(privkey); this.addPrivkey(signer); } catch (e2) { console.error("failed to add privkey", privkey, e2); } } } else if (event.kind === NDKKind2.CashuWallet) { try { await event.decrypt(); const content = JSON.parse(event.content); for (const tag of content) { if (tag[0] === "privkey") { const privkey = tag[1]; if (keysNotFound.has(privkey)) keysNotFound.delete(privkey); try { const signer = new NDKPrivateKeySigner2(privkey); this.addPrivkey(signer); } catch (e2) { console.error("failed to add privkey from wallet config", privkey, e2); } } } } catch (e2) { console.error("failed to decrypt wallet config event", event.encode(), e2); } } } if (keysNotFound.size > 0) { const backup = new NDKCashuWalletBackup(this.ndk); backup.privkeys = Array.from(keysNotFound); await backup.save(this.relaySet); } } /** * Fetches the wallet's mint list from relays. * This is used for checking if incoming nutzaps match advertised preferences. */ async fetchMintList() { const event = await this.ndk.fetchEvent( { kinds: [NDKKind2.CashuMintList], authors: [this.user.pubkey] }, { cacheUsage: NDKSubscriptionCacheUsage2.PARALLEL, subId: "cashu-mint-list" } ); if (event) { this.mintList = NDKCashuMintList2.from(event); return this.mintList; } return void 0; } /** * Start the nutzap monitor. The monitor will initially look back * for nutzaps it doesn't know about and will try to redeem them. * * @param knownNutzaps - An optional set of nutzaps the app knows about. This is an optimization so that we don't try to redeem nutzaps we know have already been redeemed. * @param pageSize - The number of nutzaps to fetch per page. * */ async start({ filter, opts }) { if (this.sub) this.sub.stop(); if (!this.mintList) { try { const mintList = await this.fetchMintList(); log(`Fetched mint list with ${mintList?.mints.length ?? 0} mints`); } catch (e2) { console.error("\u274C Failed to fetch mint list", e2); } } try { await this.getBackupKeys(); log(`Got backup keys ${this.privkeys.size}`); } catch (e2) { console.error("\u274C Failed to get backup keys", e2); } await this.addUserPrivKey(); log(`Added user privkey ${this.privkeys.size}`); const since = Math.floor(Date.now() / 1e3); const monitorFilter = { kinds: [NDKKind2.Nutzap], "#p": [this.user.pubkey], since }; if (this.store) { try { const nutzaps = await this.store.getAllNutzaps(); log(`Loaded ${nutzaps.size} nutzaps`); for (const [id, state] of nutzaps.entries()) { this.nutzapStates.set(id, state); } log(`Changed the state of ${nutzaps.size} nutzaps`); } catch (e2) { console.error("\u274C Failed to load nutzaps from store", e2); } } try { log("Will start processing redeemable nutzaps from store"); await this.processRedeemableNutzapsFromStore(); log("Finished processing redeemable nutzaps from store"); } catch (e2) { console.error("\u274C Failed to process redeemable nutzaps from store", e2); } try { log("Will start processing accumulated nutzaps"); await this.processAccumulatedNutzaps(filter, opts); log(`Finished processing accumulated nutzaps ${this.nutzapStates.size}`); } catch (e2) { console.error("\u274C Failed to process nutzaps", e2); } log(`Running filter ${JSON.stringify(monitorFilter)}`); const subscribeOpts = { subId: "ndk-wallet:nutzap-monitor", cacheUsage: NDKSubscriptionCacheUsage2.ONLY_RELAY, wrap: false, // We skip validation so the user knows about nutzaps that were sent but are not valid // this way tooling can be more comprehensive and include nutzaps that were not valid skipValidation: true, ...opts, relaySet: this.relaySet // Pass relaySet via options }; this.sub = this.ndk.subscribe( monitorFilter, subscribeOpts, // this.relaySet, // Removed: Passed via opts { // autoStart handlers (now 3rd argument) onEvent: (event) => this.eventHandler(event) // Added NDKEvent type } ); return true; } /** * Checks if the group of nutzaps can be redeemed and redeems the ones that can be. */ async checkAndRedeemGroup(group, oldestUnspentNutzapTime) { const cashuWallet = await this.getCashuWallet(group.mint); const spendStates = await getProofSpendState(cashuWallet, group.nutzaps); for (const nutzap of spendStates.nutzapsWithSpentProofs) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.SPENT, nutzap }); } for (const nutzap of spendStates.nutzapsWithUnspentProofs) { this.emit("seen", nutzap); this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INITIAL, nutzap }); } if (spendStates.unspentProofs.length > 0) { for (const nutzap of spendStates.nutzapsWithUnspentProofs) { if (!oldestUnspentNutzapTime || oldestUnspentNutzapTime > nutzap.created_at) { oldestUnspentNutzapTime = nutzap.created_at; } } await this.redeemNutzaps(group.mint, spendStates.nutzapsWithUnspentProofs, spendStates.unspentProofs); } } /** * Processes nutzaps that have been accumulated while the monitor was offline. * @param startOpts * @param opts */ async processAccumulatedNutzaps(filter = {}, opts) { let oldestUnspentNutzapTime; const _filter = { ...filter }; _filter.kinds = [NDKKind2.Nutzap]; _filter["#p"] = [this.user.pubkey]; const knownNutzapIds = new Set(this.nutzapStates.keys()); const nutzaps = await fetchPage(this.ndk, _filter, knownNutzapIds, this.relaySet); log(`We loaded ${nutzaps.length} nutzaps from relays`); oldestUnspentNutzapTime = await this.processNutzaps(nutzaps, oldestUnspentNutzapTime); if (oldestUnspentNutzapTime) { _filter.since = oldestUnspentNutzapTime - 1; await this.processAccumulatedNutzaps(_filter, opts); } } stop() { this.sub?.stop(); } updateNutzapState(id, state) { const currentState = this.nutzapStates.get(id) ?? {}; if (!currentState.status) state.status ?? (state.status = NdkNutzapStatus2.INITIAL); const stateIsUnchanged = Object.entries(state).every(([key, value]) => { if (key === "nutzap" && currentState.nutzap && value) { return currentState.nutzap.id === value.id; } return currentState[key] === value; }); if (stateIsUnchanged) return; this.nutzapStates.set(id, { ...currentState, ...state }); this.emit("state_changed", id, currentState.status); const serializedState = (state2) => { const res = { ...state2 }; if (res.nutzap) res.nutzap = res.nutzap.id; return JSON.stringify(res); }; const currentStatusStr = serializedState(currentState); const newStatusStr = serializedState(state); log(`[${id.substring(0, 6)}] ${currentStatusStr} changed to \u{1F449} ${newStatusStr}`); this.store?.setNutzapState(id, state); } async eventHandler(event) { if (this.nutzapStates.has(event.id)) return; const nutzap = await NDKNutzap2.from(event); if (!nutzap) { this.updateNutzapState(event.id, { status: NdkNutzapStatus2.PERMANENT_ERROR, errorMessage: "Failed to parse nutzap" }); return; } if (this.mintList && !this.mintList.mints.includes(nutzap.mint)) { this.emit("seen_in_unknown_mint", nutzap); } this.redeemNutzap(nutzap); } /** * Gathers the necessary information to redeem a nutzap and then redeems it. * @param nutzap */ async redeemNutzap(nutzap) { if (!this.nutzapStates.has(nutzap.id)) this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INITIAL, nutzap }); const rawP2pk = nutzap.rawP2pk; if (rawP2pk) { const cashuPubkey = proofP2pk2(nutzap.proofs[0]); if (cashuPubkey) { const nostrPubkey = cashuPubkeyToNostrPubkey2(cashuPubkey); if (nostrPubkey && !this.privkeys.has(nostrPubkey)) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.MISSING_PRIVKEY, errorMessage: "No privkey found for p2pk" }); return this.nutzapStates.get(nutzap.id); } } } await this.redeemNutzaps(nutzap.mint, [nutzap], nutzap.proofs); return this.nutzapStates.get(nutzap.id); } /** * This function redeems a list of proofs. * * Proofs will be attempted to be redeemed in a single call, so they will all work or none will. * Either call this function with proofs that have been verified to be redeemable or don't group them, * and provide a single nutzap per call. * * All nutzaps MUST be p2pked to the same pubkey. * * @param mint * @param nutzaps * @param proofs * @param privkey Private key that is needed to redeem the nutzaps. * @returns */ async redeemNutzaps(mint, nutzaps, proofs) { if (!this.wallet) throw new Error("wallet not set"); if (!this.wallet.redeemNutzaps) throw new Error("wallet does not support redeeming nutzaps"); const cashuWallet = await this.getCashuWallet(mint); const validNutzaps = []; if (proofs.length > 0) { const cashuPubkey2 = proofP2pk2(proofs[0]); if (!cashuPubkey2) { for (const nutzap of nutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INVALID_NUTZAP, errorMessage: "Invalid nutzap: proof is not p2pk" }); } return; } const nostrPubkey2 = cashuPubkeyToNostrPubkey2(cashuPubkey2); if (!nostrPubkey2) { for (const nutzap of nutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INVALID_NUTZAP, errorMessage: "Invalid nutzap: locked to an invalid public key (not a nostr key)" }); } return; } const privkey2 = this.privkeys.get(nostrPubkey2); if (!privkey2) { for (const nutzap of nutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.MISSING_PRIVKEY, errorMessage: "No privkey found for p2pk" }); } return; } } for (const nutzap of nutzaps) { if (!nutzap.isValid) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INVALID_NUTZAP, errorMessage: "Invalid nutzap" }); continue; } const rawP2pk = nutzap.rawP2pk; if (!rawP2pk) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INVALID_NUTZAP, errorMessage: "Invalid nutzap: locked to an invalid public key (no p2pk)" }); continue; } if (rawP2pk.length !== 66) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INVALID_NUTZAP, errorMessage: `Invalid nutzap: locked to an invalid public key (length ${rawP2pk.length})` }); continue; } validNutzaps.push(nutzap); } if (validNutzaps.length === 0) return; const cashuPubkey = proofP2pk2(proofs[0]); if (!cashuPubkey) return; const nostrPubkey = cashuPubkeyToNostrPubkey2(cashuPubkey); if (!nostrPubkey) return; const privkey = this.privkeys.get(nostrPubkey); if (!privkey) { for (const nutzap of validNutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.MISSING_PRIVKEY, errorMessage: "No privkey found for p2pk" }); } return; } for (const nutzap of validNutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.PROCESSING }); } try { const totalAmount = await this.wallet.redeemNutzaps(nutzaps, privkey.privateKey, { cashuWallet, proofs, mint }); this.emit("redeemed", nutzaps, totalAmount); for (const nutzap of nutzaps) { const nutzapTotalAmount = proofsTotal(proofsIntersection(proofs, nutzap.proofs)); this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.REDEEMED, redeemedAmount: nutzapTotalAmount }); } } catch (e2) { console.error("\u274C Failed to redeem nutzaps", e2.message); if (e2.message?.includes("unknown public key size")) { for (const nutzap of nutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.PERMANENT_ERROR, errorMessage: "Invalid p2pk: unknown public key size" }); this.emit("failed", nutzap, "Invalid p2pk: unknown public key size"); } } else { for (const nutzap of nutzaps) { this.emit("failed", nutzap, e2.message); } } } } shouldTryRedeem(nutzap) { const state = this.nutzapStates.get(nutzap.id); if (!state) return true; if ([NdkNutzapStatus2.INITIAL].includes(state.status)) return true; if (state.status === NdkNutzapStatus2.MISSING_PRIVKEY) { const p2pk = state.nutzap?.p2pk; if (p2pk && this.privkeys.has(p2pk)) return true; return false; } if ([NdkNutzapStatus2.SPENT, NdkNutzapStatus2.REDEEMED].includes(state.status)) return false; if ([NdkNutzapStatus2.PERMANENT_ERROR, NdkNutzapStatus2.INVALID_NUTZAP].includes(state.status)) return false; return false; } /** * Process nutzaps from the store that are in a redeemable state. * This includes nutzaps in INITIAL state and those in MISSING_PRIVKEY state * for which we now have the private key. */ async processRedeemableNutzapsFromStore() { const redeemableNutzaps = []; for (const [_id, state] of this.nutzapStates.entries()) { if (!state.nutzap) continue; if (this.shouldTryRedeem(state.nutzap)) { redeemableNutzaps.push(state.nutzap); } } if (redeemableNutzaps.length === 0) return; log(`We found ${redeemableNutzaps.length} redeemable nutzaps in the store`); await this.processNutzaps(redeemableNutzaps); } /** * Common method to process a collection of nutzaps: * - Group them by mint * - Check and redeem each group * * @param nutzaps The nutzaps to process * @param oldestUnspentNutzapTime Optional timestamp to track the oldest unspent nutzap * @returns The updated oldestUnspentNutzapTime if any nutzaps were processed */ async processNutzaps(nutzaps, oldestUnspentNutzapTime) { const groupedNutzaps = groupNutzaps(nutzaps, this); for (const group of groupedNutzaps) { log(`Processing group ${group.mint} with ${group.nutzaps.length} nutzaps`); try { await this.checkAndRedeemGroup(group, oldestUnspentNutzapTime); log(`Finished processing group ${group.mint}`); } catch (e2) { log(`Failed to process group ${group.mint}`); console.error(`\u274C Failed to process group ${group.mint}`, e2); } } return oldestUnspentNutzapTime; } }; d34 = (0, import_debug36.default)("ndk-wallet:nwc"); TX_POLL_INTERVAL = 6e4; NDKNWCWallet = class extends NDKWallet { /** * * @param ndk * @param timeout A timeeout to use for all operations. */ constructor(ndk, { timeout, pairingCode, pubkey, relayUrls, secret }) { super(ndk); __publicField(this, "status", "initial"); __publicField(this, "walletId", "nwc"); __publicField(this, "pairingCode"); __publicField(this, "walletService"); __publicField(this, "relaySet"); __publicField(this, "signer"); __publicField(this, "_balance"); __publicField(this, "cachedInfo"); __publicField(this, "pool"); __publicField(this, "timeout"); /** * Redeem a set of nutzaps into an NWC wallet. * * This function gets an invoice from the NWC wallet until the total amount of the nutzaps is enough to pay for the invoice * when accounting for fees. * * @param cashuWallet - The cashu wallet to redeem the nutzaps into * @param nutzaps - The nutzaps to redeem * @param proofs - The proofs to redeem * @param mint - The mint to redeem the nutzaps into * @param privkey - The private key needed to redeem p2pk proofs. */ __publicField(this, "redeemNutzaps", redeemNutzaps.bind(this)); __publicField(this, "req", sendReq.bind(this)); if (pairingCode) { const u3 = new URL(pairingCode); pubkey = u3.host ?? u3.pathname; relayUrls = u3.searchParams.getAll("relay"); secret = u3.searchParams.get("secret"); this.pairingCode = pairingCode; } if (!pubkey || !relayUrls || !secret) throw new Error("Incomplete initialization parameters"); this.timeout = timeout; this.walletService = this.ndk.getUser({ pubkey }); this.pool = this.getPool(relayUrls); this.relaySet = NDKRelaySet2.fromRelayUrls(relayUrls, this.ndk, true, this.pool); this.signer = new NDKPrivateKeySigner2(secret); this.pool.on("connect", () => { this.status = "ready"; this.emit("ready"); }); this.pool.on( "relay:disconnect", () => this.status = "loading" /* LOADING */ ); this.pool.connect(); if (this.pool.connectedRelays().length > 0) { this.status = "ready"; this.emit("ready"); } } get type() { return "nwc"; } getPool(relayUrls) { for (const pool of this.ndk.pools) if (pool.name === "NWC") return pool; return new NDKPool2(relayUrls, this.ndk, { name: "NWC" }); } async lnPay(payment) { if (!this.signer) throw new Error("Wallet not initialized"); d34("lnPay", payment.pr); const res = await this.req("pay_invoice", { invoice: payment.pr }); d34("lnPay res", res); if (res.result) { return { preimage: res.result.preimage }; } this.updateBalance(); throw new Error(res.error?.message || "Payment failed"); } /** * Pay by minting tokens. * * This creates a quote on a mint, pays it using NWC and then mints the tokens. * * @param payment - The payment to pay * @param onLnPayment - A callback that is called when an LN payment will be processed * @returns The payment confirmation */ async cashuPay(payment, onLnInvoice, onLnPayment) { if (!payment.mints) throw new Error("No mints provided"); for (const mint of payment.mints) { let amount = payment.amount; amount = amount / 1e3; const wallet = new us(new q(mint), { unit: "sat" }); let quote; try { quote = await wallet.createMintQuote(amount); d34("cashuPay quote", quote); onLnInvoice?.(quote.request); } catch (e2) { console.error("error creating mint quote", e2); throw e2; } if (!quote) throw new Error("Didnt receive a mint quote"); try { const res = await this.req("pay_invoice", { invoice: quote.request }); if (res.result?.preimage) { onLnPayment?.(mint, res.result.preimage); } d34("cashuPay res", res); } catch (e2) { const message = e2?.error?.message || e2?.message || "unknown error"; console.error("error paying invoice", e2, { message }); throw new Error(message); } this.updateBalance(); return mintProofs(wallet, quote, amount, mint, payment.p2pk); } } /** * Fetch the balance of this wallet */ async updateBalance() { const res = await this.req("get_balance", {}); if (!res.result) throw new Error("Failed to get balance"); if (res.error) throw new Error(res.error.message); this._balance = { amount: res.result?.balance ?? 0 }; this._balance.amount /= 1e3; this.emit("balance_updated"); } /** * Get the balance of this wallet */ get balance() { return this._balance; } async getInfo(refetch = false) { if (refetch) { this.cachedInfo = void 0; } if (this.cachedInfo) return this.cachedInfo; const res = await this.req("get_info", {}); d34("info", res); if (!res.result) throw new Error("Failed to get info"); if (res.error) throw new Error(res.error.message); this.cachedInfo = res.result; if (res.result.alias) this.walletId = res.result.alias; return res.result; } async fetchTransactions() { const res = await this.req("list_transactions", {}); if (!res.result) return []; return res.result.transactions.map(toWalletTransaction); } subscribeTransactions(callback) { const knownIds = /* @__PURE__ */ new Set(); const poll = async () => { try { const txs = await this.fetchTransactions(); for (const tx of txs) { if (!knownIds.has(tx.id)) { knownIds.add(tx.id); callback(tx); } } } catch (e2) { d34("Error polling transactions", e2); } }; poll(); const interval = setInterval(poll, TX_POLL_INTERVAL); const boundPoll = () => { poll(); }; this.on("balance_updated", boundPoll); return () => { clearInterval(interval); this.off("balance_updated", boundPoll); }; } async makeInvoice(amount, description) { const res = await this.req("make_invoice", { amount, description }); if (!res.result) throw new Error("Failed to make invoice"); return res.result; } }; NDKLnPay = class { constructor(wallet, info) { __publicField(this, "wallet"); __publicField(this, "info"); __publicField(this, "type", "ln"); this.wallet = wallet; this.info = info; } async pay() { if (this.type === "ln") { return this.payLn(); } return this.payNut(); } /** * Uses LN balance to pay to a mint */ async payNut() { const { mints, p2pk } = this.info; let { amount, unit } = this.info; if (!mints) throw new Error("No mints provided"); if (unit === "msat") { amount /= 1e3; unit = "sat"; } const quotesPromises = mints.map(async (mint2) => { const wallet2 = new us(new q(mint2), { unit }); const quote2 = await wallet2.createMintQuote(amount); return { quote: quote2, mint: mint2 }; }); const { quote, mint } = await Promise.any(quotesPromises); if (!quote) { console.warn("failed to get quote from any mint"); throw new Error("failed to get quote from any mint"); } const res = await this.wallet.pay({ pr: quote.request }); if (!res) { console.warn("payment failed"); throw new Error("payment failed"); } const wallet = new us(new q(mint), { unit }); const proofs = await wallet.mintProofs(amount, quote.quote, { pubkey: p2pk }); console.warn("minted tokens with proofs %o", proofs); return { proofs, mint }; } /** * Straightforward; uses LN balance to pay a LN invoice */ async payLn() { const data = this.info; if (!data.pr) throw new Error("missing pr"); const ret = await this.wallet.pay(data); return ret ? ret.preimage : void 0; } }; NDKWebLNWallet = class extends NDKWallet { constructor(ndk) { super(ndk); __publicField(this, "walletId", "webln"); __publicField(this, "status", "initial"); __publicField(this, "provider"); __publicField(this, "_balance"); (0, import_webln.requestProvider)().then((p5) => { if (p5) { this.provider = p5; this.status = "ready"; this.emit("ready"); } else { this.status = "failed"; } }).catch( () => this.status = "failed" /* FAILED */ ); } get type() { return "webln"; } async pay(payment) { if (!this.provider) throw new Error("Provider not ready"); return this.provider.sendPayment(payment.pr); } async lnPay(payment) { const pay = new NDKLnPay(this, payment); const preimage = await pay.payLn(); if (!preimage) return; return { preimage }; } async cashuPay(payment) { const pay = new NDKLnPay(this, payment); return pay.payNut(); } async updateBalance() { if (!this.provider) { return new Promise((resolve) => { this.once("ready", () => { resolve(); }); }); } const b = await this.provider.getBalance?.(); if (b) this._balance = { amount: b.balance }; return; } get balance() { if (!this.provider) { return void 0; } return this._balance; } }; } }); // ndk/node_modules/eventemitter3/index.js var require_eventemitter3 = __commonJS({ "ndk/node_modules/eventemitter3/index.js"(exports2, module2) { "use strict"; var has = Object.prototype.hasOwnProperty; var prefix = "~"; function Events() { } if (Object.create) { Events.prototype = /* @__PURE__ */ Object.create(null); if (!new Events().__proto__) prefix = false; } function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } function addListener(emitter, event, fn, context, once) { if (typeof fn !== "function") { throw new TypeError("The listener must be a function"); } var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event; if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); else emitter._events[evt] = [emitter._events[evt], listener]; return emitter; } function clearEvent(emitter, evt) { if (--emitter._eventsCount === 0) emitter._events = new Events(); else delete emitter._events[evt]; } function EventEmitter18() { this._events = new Events(); this._eventsCount = 0; } EventEmitter18.prototype.eventNames = function eventNames() { var names = [], events, name; if (this._eventsCount === 0) return names; for (name in events = this._events) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }; EventEmitter18.prototype.listeners = function listeners(event) { var evt = prefix ? prefix + event : event, handlers3 = this._events[evt]; if (!handlers3) return []; if (handlers3.fn) return [handlers3.fn]; for (var i3 = 0, l3 = handlers3.length, ee = new Array(l3); i3 < l3; i3++) { ee[i3] = handlers3[i3].fn; } return ee; }; EventEmitter18.prototype.listenerCount = function listenerCount(event) { var evt = prefix ? prefix + event : event, listeners = this._events[evt]; if (!listeners) return 0; if (listeners.fn) return 1; return listeners.length; }; EventEmitter18.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return false; var listeners = this._events[evt], len = arguments.length, args, i3; if (listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, void 0, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i3 = 1, args = new Array(len - 1); i3 < len; i3++) { args[i3 - 1] = arguments[i3]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length, j2; for (i3 = 0; i3 < length; i3++) { if (listeners[i3].once) this.removeListener(event, listeners[i3].fn, void 0, true); switch (len) { case 1: listeners[i3].fn.call(listeners[i3].context); break; case 2: listeners[i3].fn.call(listeners[i3].context, a1); break; case 3: listeners[i3].fn.call(listeners[i3].context, a1, a2); break; case 4: listeners[i3].fn.call(listeners[i3].context, a1, a2, a3); break; default: if (!args) for (j2 = 1, args = new Array(len - 1); j2 < len; j2++) { args[j2 - 1] = arguments[j2]; } listeners[i3].fn.apply(listeners[i3].context, args); } } } return true; }; EventEmitter18.prototype.on = function on(event, fn, context) { return addListener(this, event, fn, context, false); }; EventEmitter18.prototype.once = function once(event, fn, context) { return addListener(this, event, fn, context, true); }; EventEmitter18.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return this; if (!fn) { clearEvent(this, evt); return this; } var listeners = this._events[evt]; if (listeners.fn) { if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) { clearEvent(this, evt); } } else { for (var i3 = 0, events = [], length = listeners.length; i3 < length; i3++) { if (listeners[i3].fn !== fn || once && !listeners[i3].once || context && listeners[i3].context !== context) { events.push(listeners[i3]); } } if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; else clearEvent(this, evt); } return this; }; EventEmitter18.prototype.removeAllListeners = function removeAllListeners(event) { var evt; if (event) { evt = prefix ? prefix + event : event; if (this._events[evt]) clearEvent(this, evt); } else { this._events = new Events(); this._eventsCount = 0; } return this; }; EventEmitter18.prototype.off = EventEmitter18.prototype.removeListener; EventEmitter18.prototype.addListener = EventEmitter18.prototype.on; EventEmitter18.prefixed = prefix; EventEmitter18.EventEmitter = EventEmitter18; if ("undefined" !== typeof module2) { module2.exports = EventEmitter18; } } }); // nostr-tools-external:nostr-tools/pure var require_pure = __commonJS({ "nostr-tools-external:nostr-tools/pure"(exports2, module2) { if (typeof window === "undefined" || !window.NostrTools) { throw new Error("NDK: nostr.bundle.js must be loaded before ndk-core.bundle.js"); } if (!window.NostrTools.pure) { console.warn("nostr-tools/pure not found in window.NostrTools"); module2.exports = {}; } else { module2.exports = window.NostrTools.pure; } } }); // ndk/blossom/src/types/index.ts var ErrorCodes; var init_types = __esm({ "ndk/blossom/src/types/index.ts"() { "use strict"; ErrorCodes = { // Server Errors SERVER_UNAVAILABLE: "SERVER_UNAVAILABLE", SERVER_ERROR: "SERVER_ERROR", SERVER_REJECTED: "SERVER_REJECTED", SERVER_TIMEOUT: "SERVER_TIMEOUT", SERVER_LIST_EMPTY: "SERVER_LIST_EMPTY", SERVER_INVALID_RESPONSE: "SERVER_INVALID_RESPONSE", // Auth Errors NO_SIGNER: "NO_SIGNER", AUTH_REQUIRED: "AUTH_REQUIRED", AUTH_INVALID: "AUTH_INVALID", AUTH_EXPIRED: "AUTH_EXPIRED", AUTH_REJECTED: "AUTH_REJECTED", // Upload Errors UPLOAD_TOO_LARGE: "UPLOAD_TOO_LARGE", UPLOAD_INVALID_TYPE: "UPLOAD_INVALID_TYPE", UPLOAD_FAILED: "UPLOAD_FAILED", ALL_SERVERS_FAILED: "ALL_SERVERS_FAILED", // Not Found Errors BLOB_NOT_FOUND: "BLOB_NOT_FOUND", USER_SERVER_LIST_NOT_FOUND: "USER_SERVER_LIST_NOT_FOUND", // Optimization Errors SERVER_UNSUPPORTED: "SERVER_UNSUPPORTED", FORMAT_UNSUPPORTED: "FORMAT_UNSUPPORTED", // SHA256 Calculator Errors NO_SHA256_CALCULATOR: "NO_SHA256_CALCULATOR" }; } }); // ndk/blossom/src/utils/constants.ts var BLOSSOM_AUTH_EVENT_KIND, DEFAULT_RETRY_OPTIONS, DEFAULT_HEADERS, DEBUG_NAMESPACE, SERVER_ERROR_STATUS_CODES; var init_constants = __esm({ "ndk/blossom/src/utils/constants.ts"() { "use strict"; BLOSSOM_AUTH_EVENT_KIND = 24242; DEFAULT_RETRY_OPTIONS = { maxRetries: 3, retryDelay: 1e3, backoffFactor: 1.5, retryableStatusCodes: [408, 429, 500, 502, 503, 504] }; DEFAULT_HEADERS = { Accept: "application/json" }; DEBUG_NAMESPACE = "ndk:blossom"; SERVER_ERROR_STATUS_CODES = [500, 501, 502, 503, 504, 505]; } }); // ndk/blossom/src/utils/errors.ts var NDKBlossomError, NDKBlossomUploadError, NDKBlossomServerError, NDKBlossomAuthError, NDKBlossomNotFoundError, NDKBlossomOptimizationError; var init_errors = __esm({ "ndk/blossom/src/utils/errors.ts"() { "use strict"; NDKBlossomError = class extends Error { constructor(message, code, serverUrl, cause) { super(message); this.name = "NDKBlossomError"; this.code = code; this.serverUrl = serverUrl; this.cause = cause; } }; NDKBlossomUploadError = class extends NDKBlossomError { constructor(message, code, serverUrl, cause) { super(message, code, serverUrl, cause); this.name = "NDKBlossomUploadError"; } }; NDKBlossomServerError = class extends NDKBlossomError { constructor(message, code, serverUrl, status, cause) { super(message, code, serverUrl, cause); this.name = "NDKBlossomServerError"; this.status = status; } }; NDKBlossomAuthError = class extends NDKBlossomError { constructor(message, code, serverUrl, cause) { super(message, code, serverUrl, cause); this.name = "NDKBlossomAuthError"; } }; NDKBlossomNotFoundError = class extends NDKBlossomError { constructor(message, code, serverUrl, cause) { super(message, code, serverUrl, cause); this.name = "NDKBlossomNotFoundError"; } }; NDKBlossomOptimizationError = class extends NDKBlossomError { constructor(message, code, serverUrl, cause) { super(message, code, serverUrl, cause); this.name = "NDKBlossomOptimizationError"; } }; } }); // ndk/blossom/src/utils/logger.ts var import_debug39, DebugLogger, CustomLogger; var init_logger = __esm({ "ndk/blossom/src/utils/logger.ts"() { "use strict"; import_debug39 = __toESM(require_browser(), 1); init_constants(); DebugLogger = class { constructor(namespace = DEBUG_NAMESPACE) { this.debugger = (0, import_debug39.default)(namespace); } error(message, data) { this.log("error", message, data); } warn(message, data) { this.log("warn", message, data); } info(message, data) { this.log("info", message, data); } debug(message, data) { this.log("debug", message, data); } log(level, message, data) { const formattedMessage = `[${level.toUpperCase()}] ${message}`; if (data !== void 0) { this.debugger(formattedMessage, data); } else { this.debugger(formattedMessage); } } }; CustomLogger = class { constructor(logFunction) { this.logFunction = logFunction; } error(message, data) { this.logFunction("error", message, data); } warn(message, data) { this.logFunction("warn", message, data); } info(message, data) { this.logFunction("info", message, data); } debug(message, data) { this.logFunction("debug", message, data); } }; } }); // ndk/blossom/src/utils/auth.ts var auth_exports = {}; __export(auth_exports, { addAuthHeaders: () => addAuthHeaders, createAuthEvent: () => createAuthEvent, createAuthenticatedFetchOptions: () => createAuthenticatedFetchOptions }); async function createAuthEvent(ndk, action, options = {}) { try { const authEvent = new NDKEvent2(ndk); authEvent.kind = BLOSSOM_AUTH_EVENT_KIND; authEvent.created_at = Math.floor(Date.now() / 1e3); authEvent.content = options.content || `${action.charAt(0).toUpperCase() + action.slice(1)} blob`; const tags = [ ["t", action] // Action tag (required) ]; if (options.sha256) { const hashes = Array.isArray(options.sha256) ? options.sha256 : [options.sha256]; hashes.forEach((hash3) => { tags.push(["x", hash3]); }); } const expirationSeconds = options.expirationSeconds || 3600; const expiration = Math.floor(Date.now() / 1e3) + expirationSeconds; tags.push(["expiration", expiration.toString()]); authEvent.tags = tags; const signer = options.signer ?? ndk.signer; await authEvent.sign(signer); logger.debug(`Created Blossom auth event for action: ${action}`); return authEvent; } catch (error) { if (error instanceof NDKBlossomAuthError) { throw error; } throw new NDKBlossomAuthError( `Failed to create auth event: ${error.message}`, ErrorCodes.AUTH_REQUIRED, void 0, error ); } } function addAuthHeaders(headers, authEvent) { const serializedEvent = JSON.stringify(authEvent.rawEvent()); const encodedEvent = btoa(serializedEvent); return { ...headers, Authorization: `Nostr ${encodedEvent}` }; } async function createAuthenticatedFetchOptions(ndk, action, options = {}) { const authEvent = await createAuthEvent(ndk, action, { sha256: options.sha256, content: options.content, expirationSeconds: options.expirationSeconds, signer: options.signer }); const headers = addAuthHeaders(options.fetchOptions?.headers || {}, authEvent); return { ...options.fetchOptions || {}, headers }; } var logger; var init_auth = __esm({ "ndk/blossom/src/utils/auth.ts"() { "use strict"; init_dist(); init_types(); init_constants(); init_errors(); init_logger(); logger = new DebugLogger("ndk:blossom:auth"); } }); // build/ndk-entry.js var ndk_entry_exports = {}; __export(ndk_entry_exports, { AuthManager: () => AuthManager, BECH32_REGEX: () => BECH32_REGEX, CacheModuleStorage: () => CacheModuleStorage, CashuMint: () => q, CashuWallet: () => us, DefaultSHA256Calculator: () => DefaultSHA256Calculator, FileStorage: () => FileStorage, LocalStorage: () => LocalStorage, MemoryAdapter: () => MemoryAdapter, MemoryStorage: () => MemoryStorage, MetadataLRUCache: () => MetadataLRUCache2, NDKAppHandlerEvent: () => NDKAppHandlerEvent, NDKAppSettings: () => NDKAppSettings, NDKArticle: () => NDKArticle, NDKBlossom: () => blossom_default, NDKBlossomAuthError: () => NDKBlossomAuthError, NDKBlossomError: () => NDKBlossomError, NDKBlossomList: () => NDKBlossomList, NDKBlossomNotFoundError: () => NDKBlossomNotFoundError, NDKBlossomOptimizationError: () => NDKBlossomOptimizationError, NDKBlossomServerError: () => NDKBlossomServerError, NDKBlossomUploadError: () => NDKBlossomUploadError, NDKCacheAdapterDexie: () => NDKCacheAdapterDexie2, NDKCacheAdapterSqliteWasm: () => src_default2, NDKCacheBrowser: () => NDKCacheBrowser, NDKCashuDeposit: () => NDKCashuDeposit2, NDKCashuMintAnnouncement: () => NDKCashuMintAnnouncement, NDKCashuMintList: () => NDKCashuMintList, NDKCashuToken: () => NDKCashuToken, NDKCashuWallet: () => NDKCashuWallet2, NDKCashuWalletBackup: () => NDKCashuWalletBackup2, NDKCashuWalletTx: () => NDKCashuWalletTx, NDKClassified: () => NDKClassified, NDKConversation: () => NDKConversation, NDKDVMJobFeedback: () => NDKDVMJobFeedback, NDKDVMJobResult: () => NDKDVMJobResult, NDKDVMRequest: () => NDKDVMRequest, NDKDraft: () => NDKDraft, NDKDvmJobFeedbackStatus: () => NDKDvmJobFeedbackStatus, NDKEvent: () => NDKEvent, NDKFedimintMint: () => NDKFedimintMint, NDKFilterValidationMode: () => NDKFilterValidationMode, NDKFollowPack: () => NDKFollowPack, NDKHighlight: () => NDKHighlight, NDKImage: () => NDKImage, NDKInterestList: () => NDKInterestList, NDKKind: () => NDKKind, NDKList: () => NDKList, NDKListKinds: () => NDKListKinds, NDKMessenger: () => NDKMessenger, NDKMintRecommendation: () => NDKMintRecommendation, NDKNWCWallet: () => NDKNWCWallet2, NDKNip07Signer: () => NDKNip07Signer, NDKNip46Backend: () => NDKNip46Backend, NDKNip46Signer: () => NDKNip46Signer, NDKNostrRpc: () => NDKNostrRpc, NDKNotInitializedError: () => NDKNotInitializedError, NDKNutzap: () => NDKNutzap, NDKNutzapMonitor: () => NDKNutzapMonitor2, NDKPool: () => NDKPool, NDKPrivateKeySigner: () => NDKPrivateKeySigner, NDKProject: () => NDKProject, NDKProjectTemplate: () => NDKProjectTemplate, NDKPublishError: () => NDKPublishError, NDKRelay: () => NDKRelay, NDKRelayAuthPolicies: () => NDKRelayAuthPolicies, NDKRelayFeedList: () => NDKRelayFeedList, NDKRelayList: () => NDKRelayList, NDKRelaySet: () => NDKRelaySet, NDKRelayStatus: () => NDKRelayStatus, NDKRepost: () => NDKRepost, NDKSessionManager: () => NDKSessionManager, NDKSimpleGroup: () => NDKSimpleGroup, NDKSimpleGroupMemberList: () => NDKSimpleGroupMemberList, NDKSimpleGroupMetadata: () => NDKSimpleGroupMetadata, NDKStory: () => NDKStory, NDKStorySticker: () => NDKStorySticker, NDKStoryStickerType: () => NDKStoryStickerType, NDKSubscription: () => NDKSubscription, NDKSubscriptionCacheUsage: () => NDKSubscriptionCacheUsage, NDKSubscriptionReceipt: () => NDKSubscriptionReceipt, NDKSubscriptionStart: () => NDKSubscriptionStart, NDKSubscriptionTier: () => NDKSubscriptionTier, NDKTask: () => NDKTask, NDKThread: () => NDKThread, NDKTranscriptionDVM: () => NDKTranscriptionDVM, NDKUser: () => NDKUser, NDKVideo: () => NDKVideo, NDKVoiceMessage: () => NDKVoiceMessage, NDKVoiceReply: () => NDKVoiceReply, NDKWallet: () => NDKWallet2, NDKWalletStatus: () => NDKWalletStatus2, NDKWebLNWallet: () => NDKWebLNWallet2, NDKWiki: () => NDKWiki, NDKWikiMergeRequest: () => NDKWikiMergeRequest, NDKWoT: () => NDKWoT, NDKZap: () => NDKZap, NDKZapper: () => NDKZapper, NIP17Protocol: () => NIP17Protocol, NIP33_A_REGEX: () => NIP33_A_REGEX, NdkNutzapStatus: () => NdkNutzapStatus, NoActiveSessionError: () => NoActiveSessionError, NutzapValidationCode: () => NutzapValidationCode, NutzapValidationSeverity: () => NutzapValidationSeverity, PersistenceManager: () => PersistenceManager, SessionError: () => SessionError, SessionNotFoundError: () => SessionNotFoundError, SignatureVerificationStats: () => SignatureVerificationStats, SignerDeserializationError: () => SignerDeserializationError, StorageError: () => StorageError, WalletState: () => WalletState2, assertSignedEvent: () => assertSignedEvent, calculateNewState: () => calculateNewState2, calculateRelaySetFromEvent: () => calculateRelaySetFromEvent, calculateTermDurationInSeconds: () => calculateTermDurationInSeconds, cashuPubkeyToNostrPubkey: () => cashuPubkeyToNostrPubkey, clearPreferredAdapter: () => clearPreferredAdapter, compareFilter: () => compareFilter, consolidateMintTokens: () => consolidateMintTokens2, consolidateTokens: () => consolidateTokens2, createMintCacheCallbacks: () => createMintCacheCallbacks2, createMintDiscoveryStore: () => createMintDiscoveryStore2, createSessionStore: () => createSessionStore, createSignedEvent: () => createSignedEvent, createValidationIssue: () => createValidationIssue, createWoTComparator: () => createWoTComparator, db: () => db2, default: () => ndk_entry_default, defaultOpts: () => defaultOpts, defaultSHA256Calculator: () => defaultSHA256Calculator, deserialize: () => deserialize, dvmSchedule: () => dvmSchedule, eventHasETagMarkers: () => eventHasETagMarkers, eventIsPartOfThread: () => eventIsPartOfThread, eventIsReply: () => eventIsReply, eventReplies: () => eventReplies, eventThreadIds: () => eventThreadIds, eventThreads: () => eventThreads, eventsBySameAuthor: () => eventsBySameAuthor, extractHashFromUrl: () => extractHashFromUrl, fetchRelayInformation: () => fetchRelayInformation, filterAndRelaySetFromBech32: () => filterAndRelaySetFromBech32, filterByWoT: () => filterByWoT, filterFingerprint: () => filterFingerprint, filterForEventsTaggingId: () => filterForEventsTaggingId, filterFromId: () => filterFromId, foundEvent: () => foundEvent3, foundEvents: () => foundEvents3, generateContentTags: () => generateContentTags, generateHashtags: () => generateHashtags, generateSubId: () => generateSubId, generateZapRequest: () => generateZapRequest, getBolt11Amount: () => getBolt11Amount2, getBolt11Description: () => getBolt11Description2, getBolt11ExpiresAt: () => getBolt11ExpiresAt2, getCashuMintRecommendations: () => getCashuMintRecommendations2, getDecodedToken: () => Me, getEncodedTokenV4: () => Ie, getEventReplyId: () => getEventReplyId, getNip57ZapSpecFromLud: () => getNip57ZapSpecFromLud, getPreferredAdapter: () => getPreferredAdapter, getRegisteredEventClasses: () => getRegisteredEventClasses, getRelayListForUser: () => getRelayListForUser, getRelayListForUsers: () => getRelayListForUsers, getReplyTag: () => getReplyTag, getRootEventId: () => getRootEventId, getRootTag: () => getRootTag, giftUnwrap: () => giftUnwrap, giftWrap: () => giftWrap, imetaTagToTag: () => imetaTagToTag, isEventOriginalPost: () => isEventOriginalPost, isNip33AValue: () => isNip33AValue, isSignedEvent: () => isSignedEvent, isUnsignedEvent: () => isUnsignedEvent, isValidEventId: () => isValidEventId, isValidHex64: () => isValidHex64, isValidNip05: () => isValidNip05, isValidPubkey: () => isValidPubkey, mapImetaTag: () => mapImetaTag, matchFilter: () => matchFilter, mergeFilters: () => mergeFilters, mergeTags: () => mergeTags, messagesCacheModule: () => messagesCacheModule, ndkSignerFromPayload: () => ndkSignerFromPayload, newAmount: () => newAmount, nip19: () => nip19_exports, nip49: () => nip49_exports, normalize: () => normalize, normalizeRelayUrl: () => normalizeRelayUrl, normalizeUrl: () => normalizeUrl, parseTagToSubscriptionAmount: () => parseTagToSubscriptionAmount, pinEvent: () => pinEvent, possibleIntervalFrequencies: () => possibleIntervalFrequencies, processFilters: () => processFilters, profileFromEvent: () => profileFromEvent, proofP2pk: () => proofP2pk, proofP2pkNostr: () => proofP2pkNostr, proofsTotalBalance: () => proofsTotalBalance, queryFullyFilled: () => queryFullyFilled, rankByWoT: () => rankByWoT, registerEventClass: () => registerEventClass, registerSigner: () => registerSigner, relayListFromKind3: () => relayListFromKind3, relaysFromBech32: () => relaysFromBech32, serialize: () => serialize, serializeProfile: () => serializeProfile, setPreferredAdapter: () => setPreferredAdapter, startSignatureVerificationStats: () => startSignatureVerificationStats, strToDimension: () => strToDimension, strToPosition: () => strToPosition, tryNormalizeRelayUrl: () => tryNormalizeRelayUrl, uniqueTag: () => uniqueTag, unregisterEventClass: () => unregisterEventClass, update: () => update2, wrapEvent: () => wrapEvent, zapInvoiceFromEvent: () => zapInvoiceFromEvent }); // ndk/core/src/events/kinds/index.ts var NDKKind = /* @__PURE__ */ ((NDKKind3) => { NDKKind3[NDKKind3["Metadata"] = 0] = "Metadata"; NDKKind3[NDKKind3["Text"] = 1] = "Text"; NDKKind3[NDKKind3["RecommendRelay"] = 2] = "RecommendRelay"; NDKKind3[NDKKind3["Contacts"] = 3] = "Contacts"; NDKKind3[NDKKind3["EncryptedDirectMessage"] = 4] = "EncryptedDirectMessage"; NDKKind3[NDKKind3["EventDeletion"] = 5] = "EventDeletion"; NDKKind3[NDKKind3["Repost"] = 6] = "Repost"; NDKKind3[NDKKind3["Reaction"] = 7] = "Reaction"; NDKKind3[NDKKind3["BadgeAward"] = 8] = "BadgeAward"; NDKKind3[NDKKind3["GroupChat"] = 9] = "GroupChat"; NDKKind3[NDKKind3["Thread"] = 11] = "Thread"; NDKKind3[NDKKind3["GroupReply"] = 12] = "GroupReply"; NDKKind3[NDKKind3["GiftWrapSeal"] = 13] = "GiftWrapSeal"; NDKKind3[NDKKind3["PrivateDirectMessage"] = 14] = "PrivateDirectMessage"; NDKKind3[NDKKind3["Image"] = 20] = "Image"; NDKKind3[NDKKind3["Video"] = 21] = "Video"; NDKKind3[NDKKind3["ShortVideo"] = 22] = "ShortVideo"; NDKKind3[NDKKind3["Story"] = 23] = "Story"; NDKKind3[NDKKind3["Vanish"] = 62] = "Vanish"; NDKKind3[NDKKind3["CashuWalletBackup"] = 375] = "CashuWalletBackup"; NDKKind3[NDKKind3["GiftWrap"] = 1059] = "GiftWrap"; NDKKind3[NDKKind3["GenericRepost"] = 16] = "GenericRepost"; NDKKind3[NDKKind3["ChannelCreation"] = 40] = "ChannelCreation"; NDKKind3[NDKKind3["ChannelMetadata"] = 41] = "ChannelMetadata"; NDKKind3[NDKKind3["ChannelMessage"] = 42] = "ChannelMessage"; NDKKind3[NDKKind3["ChannelHideMessage"] = 43] = "ChannelHideMessage"; NDKKind3[NDKKind3["ChannelMuteUser"] = 44] = "ChannelMuteUser"; NDKKind3[NDKKind3["WikiMergeRequest"] = 818] = "WikiMergeRequest"; NDKKind3[NDKKind3["GenericReply"] = 1111] = "GenericReply"; NDKKind3[NDKKind3["Media"] = 1063] = "Media"; NDKKind3[NDKKind3["VoiceMessage"] = 1222] = "VoiceMessage"; NDKKind3[NDKKind3["VoiceReply"] = 1244] = "VoiceReply"; NDKKind3[NDKKind3["DraftCheckpoint"] = 1234] = "DraftCheckpoint"; NDKKind3[NDKKind3["Task"] = 1934] = "Task"; NDKKind3[NDKKind3["Report"] = 1984] = "Report"; NDKKind3[NDKKind3["Label"] = 1985] = "Label"; NDKKind3[NDKKind3["DVMReqTextExtraction"] = 5e3] = "DVMReqTextExtraction"; NDKKind3[NDKKind3["DVMReqTextSummarization"] = 5001] = "DVMReqTextSummarization"; NDKKind3[NDKKind3["DVMReqTextTranslation"] = 5002] = "DVMReqTextTranslation"; NDKKind3[NDKKind3["DVMReqTextGeneration"] = 5050] = "DVMReqTextGeneration"; NDKKind3[NDKKind3["DVMReqImageGeneration"] = 5100] = "DVMReqImageGeneration"; NDKKind3[NDKKind3["DVMReqTextToSpeech"] = 5250] = "DVMReqTextToSpeech"; NDKKind3[NDKKind3["DVMReqDiscoveryNostrContent"] = 5300] = "DVMReqDiscoveryNostrContent"; NDKKind3[NDKKind3["DVMReqDiscoveryNostrPeople"] = 5301] = "DVMReqDiscoveryNostrPeople"; NDKKind3[NDKKind3["DVMReqTimestamping"] = 5900] = "DVMReqTimestamping"; NDKKind3[NDKKind3["DVMEventSchedule"] = 5905] = "DVMEventSchedule"; NDKKind3[NDKKind3["DVMJobFeedback"] = 7e3] = "DVMJobFeedback"; NDKKind3[NDKKind3["Subscribe"] = 7001] = "Subscribe"; NDKKind3[NDKKind3["Unsubscribe"] = 7002] = "Unsubscribe"; NDKKind3[NDKKind3["SubscriptionReceipt"] = 7003] = "SubscriptionReceipt"; NDKKind3[NDKKind3["CashuReserve"] = 7373] = "CashuReserve"; NDKKind3[NDKKind3["CashuQuote"] = 7374] = "CashuQuote"; NDKKind3[NDKKind3["CashuToken"] = 7375] = "CashuToken"; NDKKind3[NDKKind3["CashuWalletTx"] = 7376] = "CashuWalletTx"; NDKKind3[NDKKind3["GroupAdminAddUser"] = 9e3] = "GroupAdminAddUser"; NDKKind3[NDKKind3["GroupAdminRemoveUser"] = 9001] = "GroupAdminRemoveUser"; NDKKind3[NDKKind3["GroupAdminEditMetadata"] = 9002] = "GroupAdminEditMetadata"; NDKKind3[NDKKind3["GroupAdminEditStatus"] = 9006] = "GroupAdminEditStatus"; NDKKind3[NDKKind3["GroupAdminCreateGroup"] = 9007] = "GroupAdminCreateGroup"; NDKKind3[NDKKind3["GroupAdminRequestJoin"] = 9021] = "GroupAdminRequestJoin"; NDKKind3[NDKKind3["MuteList"] = 1e4] = "MuteList"; NDKKind3[NDKKind3["PinList"] = 10001] = "PinList"; NDKKind3[NDKKind3["RelayList"] = 10002] = "RelayList"; NDKKind3[NDKKind3["BookmarkList"] = 10003] = "BookmarkList"; NDKKind3[NDKKind3["CommunityList"] = 10004] = "CommunityList"; NDKKind3[NDKKind3["PublicChatList"] = 10005] = "PublicChatList"; NDKKind3[NDKKind3["BlockRelayList"] = 10006] = "BlockRelayList"; NDKKind3[NDKKind3["SearchRelayList"] = 10007] = "SearchRelayList"; NDKKind3[NDKKind3["SimpleGroupList"] = 10009] = "SimpleGroupList"; NDKKind3[NDKKind3["RelayFeedList"] = 10012] = "RelayFeedList"; NDKKind3[NDKKind3["InterestList"] = 10015] = "InterestList"; NDKKind3[NDKKind3["CashuMintList"] = 10019] = "CashuMintList"; NDKKind3[NDKKind3["EmojiList"] = 10030] = "EmojiList"; NDKKind3[NDKKind3["DirectMessageReceiveRelayList"] = 10050] = "DirectMessageReceiveRelayList"; NDKKind3[NDKKind3["BlossomList"] = 10063] = "BlossomList"; NDKKind3[NDKKind3["NostrWaletConnectInfo"] = 13194] = "NostrWaletConnectInfo"; NDKKind3[NDKKind3["TierList"] = 17e3] = "TierList"; NDKKind3[NDKKind3["CashuWallet"] = 17375] = "CashuWallet"; NDKKind3[NDKKind3["FollowSet"] = 3e4] = "FollowSet"; NDKKind3[NDKKind3["CategorizedPeopleList"] = 3e4 /* FollowSet */] = "CategorizedPeopleList"; NDKKind3[NDKKind3["CategorizedBookmarkList"] = 30001] = "CategorizedBookmarkList"; NDKKind3[NDKKind3["RelaySet"] = 30002] = "RelaySet"; NDKKind3[NDKKind3["CategorizedRelayList"] = 30002 /* RelaySet */] = "CategorizedRelayList"; NDKKind3[NDKKind3["BookmarkSet"] = 30003] = "BookmarkSet"; NDKKind3[NDKKind3["CurationSet"] = 30004] = "CurationSet"; NDKKind3[NDKKind3["ArticleCurationSet"] = 30004] = "ArticleCurationSet"; NDKKind3[NDKKind3["VideoCurationSet"] = 30005] = "VideoCurationSet"; NDKKind3[NDKKind3["ImageCurationSet"] = 30006] = "ImageCurationSet"; NDKKind3[NDKKind3["InterestSet"] = 30015] = "InterestSet"; NDKKind3[NDKKind3["InterestsList"] = 30015 /* InterestSet */] = "InterestsList"; NDKKind3[NDKKind3["ProjectTemplate"] = 30717] = "ProjectTemplate"; NDKKind3[NDKKind3["EmojiSet"] = 30030] = "EmojiSet"; NDKKind3[NDKKind3["ModularArticle"] = 30040] = "ModularArticle"; NDKKind3[NDKKind3["ModularArticleItem"] = 30041] = "ModularArticleItem"; NDKKind3[NDKKind3["Wiki"] = 30818] = "Wiki"; NDKKind3[NDKKind3["Draft"] = 31234] = "Draft"; NDKKind3[NDKKind3["Project"] = 31933] = "Project"; NDKKind3[NDKKind3["SubscriptionTier"] = 37001] = "SubscriptionTier"; NDKKind3[NDKKind3["EcashMintRecommendation"] = 38e3] = "EcashMintRecommendation"; NDKKind3[NDKKind3["CashuMintAnnouncement"] = 38172] = "CashuMintAnnouncement"; NDKKind3[NDKKind3["FedimintMintAnnouncement"] = 38173] = "FedimintMintAnnouncement"; NDKKind3[NDKKind3["P2POrder"] = 38383] = "P2POrder"; NDKKind3[NDKKind3["HighlightSet"] = 39802] = "HighlightSet"; NDKKind3[NDKKind3["CategorizedHighlightList"] = 39802 /* HighlightSet */] = "CategorizedHighlightList"; NDKKind3[NDKKind3["Nutzap"] = 9321] = "Nutzap"; NDKKind3[NDKKind3["ZapRequest"] = 9734] = "ZapRequest"; NDKKind3[NDKKind3["Zap"] = 9735] = "Zap"; NDKKind3[NDKKind3["Highlight"] = 9802] = "Highlight"; NDKKind3[NDKKind3["ClientAuth"] = 22242] = "ClientAuth"; NDKKind3[NDKKind3["NostrWalletConnectReq"] = 23194] = "NostrWalletConnectReq"; NDKKind3[NDKKind3["NostrWalletConnectRes"] = 23195] = "NostrWalletConnectRes"; NDKKind3[NDKKind3["NostrConnect"] = 24133] = "NostrConnect"; NDKKind3[NDKKind3["BlossomUpload"] = 24242] = "BlossomUpload"; NDKKind3[NDKKind3["HttpAuth"] = 27235] = "HttpAuth"; NDKKind3[NDKKind3["ProfileBadge"] = 30008] = "ProfileBadge"; NDKKind3[NDKKind3["BadgeDefinition"] = 30009] = "BadgeDefinition"; NDKKind3[NDKKind3["MarketStall"] = 30017] = "MarketStall"; NDKKind3[NDKKind3["MarketProduct"] = 30018] = "MarketProduct"; NDKKind3[NDKKind3["Article"] = 30023] = "Article"; NDKKind3[NDKKind3["AppSpecificData"] = 30078] = "AppSpecificData"; NDKKind3[NDKKind3["Classified"] = 30402] = "Classified"; NDKKind3[NDKKind3["HorizontalVideo"] = 34235] = "HorizontalVideo"; NDKKind3[NDKKind3["VerticalVideo"] = 34236] = "VerticalVideo"; NDKKind3[NDKKind3["GroupMetadata"] = 39e3] = "GroupMetadata"; NDKKind3[NDKKind3["GroupAdmins"] = 39001] = "GroupAdmins"; NDKKind3[NDKKind3["GroupMembers"] = 39002] = "GroupMembers"; NDKKind3[NDKKind3["FollowPack"] = 39089] = "FollowPack"; NDKKind3[NDKKind3["MediaFollowPack"] = 39092] = "MediaFollowPack"; NDKKind3[NDKKind3["AppRecommendation"] = 31989] = "AppRecommendation"; NDKKind3[NDKKind3["AppHandler"] = 31990] = "AppHandler"; return NDKKind3; })(NDKKind || {}); var NDKListKinds = [ 1e4 /* MuteList */, 10001 /* PinList */, 10002 /* RelayList */, 10003 /* BookmarkList */, 10004 /* CommunityList */, 10005 /* PublicChatList */, 10006 /* BlockRelayList */, 10007 /* SearchRelayList */, 10012 /* RelayFeedList */, 10015 /* InterestList */, 10030 /* EmojiList */, 10050 /* DirectMessageReceiveRelayList */, 3e4 /* FollowSet */, 30003 /* BookmarkSet */, 30001 /* CategorizedBookmarkList */, // Backwards compatibility 30002 /* RelaySet */, 30004 /* ArticleCurationSet */, 30005 /* VideoCurationSet */, 30015 /* InterestSet */, 30030 /* EmojiSet */, 39802 /* HighlightSet */ ]; // ndk/core/src/types.ts var NdkNutzapStatus = /* @__PURE__ */ ((NdkNutzapStatus3) => { NdkNutzapStatus3["INITIAL"] = "initial"; NdkNutzapStatus3["PROCESSING"] = "processing"; NdkNutzapStatus3["REDEEMED"] = "redeemed"; NdkNutzapStatus3["SPENT"] = "spent"; NdkNutzapStatus3["MISSING_PRIVKEY"] = "missing_privkey"; NdkNutzapStatus3["TEMPORARY_ERROR"] = "temporary_error"; NdkNutzapStatus3["PERMANENT_ERROR"] = "permanent_error"; NdkNutzapStatus3["INVALID_NUTZAP"] = "invalid_nutzap"; return NdkNutzapStatus3; })(NdkNutzapStatus || {}); // ndk/core/src/events/index.ts var import_tseep2 = __toESM(require_lib()); // ndk/core/src/relay/sets/calculate.ts var import_debug3 = __toESM(require_browser()); // ndk/core/src/outbox/write.ts function getRelaysForSync(ndk, author, type = "write") { if (!ndk.outboxTracker) return void 0; const item = ndk.outboxTracker.data.get(author); if (!item) return void 0; if (type === "write") { return item.writeRelays; } return item.readRelays; } async function getWriteRelaysFor(ndk, author, type = "write") { if (!ndk.outboxTracker) return void 0; if (!ndk.outboxTracker.data.has(author)) { await ndk.outboxTracker.trackUsers([author]); } return getRelaysForSync(ndk, author, type); } // ndk/core/src/outbox/relay-ranking.ts function getTopRelaysForAuthors(ndk, authors) { const relaysWithCount = /* @__PURE__ */ new Map(); authors.forEach((author) => { const writeRelays = getRelaysForSync(ndk, author); if (writeRelays) { writeRelays.forEach((relay) => { const count = relaysWithCount.get(relay) || 0; relaysWithCount.set(relay, count + 1); }); } }); const sortedRelays = Array.from(relaysWithCount.entries()).sort((a, b) => b[1] - a[1]); return sortedRelays.map((entry) => entry[0]); } // ndk/core/src/outbox/index.ts function getAllRelaysForAllPubkeys(ndk, pubkeys, type = "read") { const pubkeysToRelays = /* @__PURE__ */ new Map(); const authorsMissingRelays = /* @__PURE__ */ new Set(); pubkeys.forEach((pubkey) => { const relays = getRelaysForSync(ndk, pubkey, type); if (relays && relays.size > 0) { relays.forEach((relay) => { const pubkeysInRelay = pubkeysToRelays.get(relay) || /* @__PURE__ */ new Set(); pubkeysInRelay.add(pubkey); }); pubkeysToRelays.set(pubkey, relays); } else { authorsMissingRelays.add(pubkey); } }); return { pubkeysToRelays, authorsMissingRelays }; } function chooseRelayCombinationForPubkeys(ndk, pubkeys, type, { count, preferredRelays } = {}) { count ?? (count = 2); preferredRelays ?? (preferredRelays = /* @__PURE__ */ new Set()); const pool = ndk.pool; const connectedRelays = pool.connectedRelays(); connectedRelays.forEach((relay) => { preferredRelays?.add(relay.url); }); const relayToAuthorsMap = /* @__PURE__ */ new Map(); const { pubkeysToRelays, authorsMissingRelays } = getAllRelaysForAllPubkeys(ndk, pubkeys, type); const sortedRelays = getTopRelaysForAuthors(ndk, pubkeys); const addAuthorToRelay = (author, relay) => { const authorsInRelay = relayToAuthorsMap.get(relay) || []; authorsInRelay.push(author); relayToAuthorsMap.set(relay, authorsInRelay); }; for (const [author, authorRelays] of pubkeysToRelays.entries()) { let missingRelayCount = count; const addedRelaysForAuthor = /* @__PURE__ */ new Set(); for (const relay of connectedRelays) { if (authorRelays.has(relay.url)) { addAuthorToRelay(author, relay.url); addedRelaysForAuthor.add(relay.url); missingRelayCount--; } } for (const authorRelay of authorRelays) { if (addedRelaysForAuthor.has(authorRelay)) continue; if (relayToAuthorsMap.has(authorRelay)) { addAuthorToRelay(author, authorRelay); addedRelaysForAuthor.add(authorRelay); missingRelayCount--; } } if (missingRelayCount <= 0) continue; for (const relay of sortedRelays) { if (missingRelayCount <= 0) break; if (addedRelaysForAuthor.has(relay)) continue; if (authorRelays.has(relay)) { addAuthorToRelay(author, relay); addedRelaysForAuthor.add(relay); missingRelayCount--; } } } for (const author of authorsMissingRelays) { pool.permanentAndConnectedRelays().forEach((relay) => { const authorsInRelay = relayToAuthorsMap.get(relay.url) || []; authorsInRelay.push(author); relayToAuthorsMap.set(relay.url, authorsInRelay); }); } return relayToAuthorsMap; } // ndk/core/src/outbox/read/with-authors.ts function getRelaysForFilterWithAuthors(ndk, authors, relayGoalPerAuthor = 2) { return chooseRelayCombinationForPubkeys(ndk, authors, "write", { count: relayGoalPerAuthor }); } // ndk/core/src/utils/normalize-url.ts function tryNormalizeRelayUrl(url) { try { return normalizeRelayUrl(url); } catch { return void 0; } } function normalizeRelayUrl(url) { let r = normalizeUrl(url, { stripAuthentication: false, stripWWW: false, stripHash: true }); if (!r.endsWith("/")) { r += "/"; } return r; } function normalize(urls) { const normalized = /* @__PURE__ */ new Set(); for (const url of urls) { try { normalized.add(normalizeRelayUrl(url)); } catch { } } return Array.from(normalized); } var DATA_URL_DEFAULT_MIME_TYPE = "text/plain"; var DATA_URL_DEFAULT_CHARSET = "us-ascii"; var testParameter = (name, filters) => filters.some((filter) => filter instanceof RegExp ? filter.test(name) : filter === name); var supportedProtocols = /* @__PURE__ */ new Set(["https:", "http:", "file:"]); var hasCustomProtocol = (urlString) => { try { const { protocol } = new URL(urlString); return protocol.endsWith(":") && !protocol.includes(".") && !supportedProtocols.has(protocol); } catch { return false; } }; var normalizeDataURL = (urlString, { stripHash }) => { const match = /^data:(?[^,]*?),(?[^#]*?)(?:#(?.*))?$/.exec(urlString); if (!match) { throw new Error(`Invalid URL: ${urlString}`); } const type = match.groups?.type ?? ""; const data = match.groups?.data ?? ""; let hash3 = match.groups?.hash ?? ""; const mediaType = type.split(";"); hash3 = stripHash ? "" : hash3; let isBase64 = false; if (mediaType[mediaType.length - 1] === "base64") { mediaType.pop(); isBase64 = true; } const mimeType = mediaType.shift()?.toLowerCase() ?? ""; const attributes = mediaType.map((attribute) => { let [key, value = ""] = attribute.split("=").map((string) => string.trim()); if (key === "charset") { value = value.toLowerCase(); if (value === DATA_URL_DEFAULT_CHARSET) { return ""; } } return `${key}${value ? `=${value}` : ""}`; }).filter(Boolean); const normalizedMediaType = [...attributes]; if (isBase64) { normalizedMediaType.push("base64"); } if (normalizedMediaType.length > 0 || mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE) { normalizedMediaType.unshift(mimeType); } return `data:${normalizedMediaType.join(";")},${isBase64 ? data.trim() : data}${hash3 ? `#${hash3}` : ""}`; }; function normalizeUrl(urlString, options = {}) { options = { defaultProtocol: "http", normalizeProtocol: true, forceHttp: false, forceHttps: false, stripAuthentication: true, stripHash: false, stripTextFragment: true, stripWWW: true, removeQueryParameters: [/^utm_\w+/i], removeTrailingSlash: true, removeSingleSlash: true, removeDirectoryIndex: false, removeExplicitPort: false, sortQueryParameters: true, ...options }; if (typeof options.defaultProtocol === "string" && !options.defaultProtocol.endsWith(":")) { options.defaultProtocol = `${options.defaultProtocol}:`; } urlString = urlString.trim(); if (/^data:/i.test(urlString)) { return normalizeDataURL(urlString, options); } if (hasCustomProtocol(urlString)) { return urlString; } const hasRelativeProtocol = urlString.startsWith("//"); const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString); if (!isRelativeUrl) { urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol); } const urlObject = new URL(urlString); urlObject.hostname = urlObject.hostname.toLowerCase(); if (options.forceHttp && options.forceHttps) { throw new Error("The `forceHttp` and `forceHttps` options cannot be used together"); } if (options.forceHttp && urlObject.protocol === "https:") { urlObject.protocol = "http:"; } if (options.forceHttps && urlObject.protocol === "http:") { urlObject.protocol = "https:"; } if (options.stripAuthentication) { urlObject.username = ""; urlObject.password = ""; } if (options.stripHash) { urlObject.hash = ""; } else if (options.stripTextFragment) { urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, ""); } if (urlObject.pathname) { const protocolRegex = /\b[a-z][a-z\d+\-.]{1,50}:\/\//g; let lastIndex = 0; let result = ""; for (; ; ) { const match = protocolRegex.exec(urlObject.pathname); if (!match) { break; } const protocol = match[0]; const protocolAtIndex = match.index; const intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex); result += intermediate.replace(/\/{2,}/g, "/"); result += protocol; lastIndex = protocolAtIndex + protocol.length; } const remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length); result += remnant.replace(/\/{2,}/g, "/"); urlObject.pathname = result; } if (urlObject.pathname) { try { urlObject.pathname = decodeURI(urlObject.pathname); } catch { } } if (options.removeDirectoryIndex === true) { options.removeDirectoryIndex = [/^index\.[a-z]+$/]; } if (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) { let pathComponents = urlObject.pathname.split("/"); const lastComponent = pathComponents[pathComponents.length - 1]; if (testParameter(lastComponent, options.removeDirectoryIndex)) { pathComponents = pathComponents.slice(0, -1); urlObject.pathname = `${pathComponents.slice(1).join("/")}/`; } } if (urlObject.hostname) { urlObject.hostname = urlObject.hostname.replace(/\.$/, ""); if (options.stripWWW && /^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(urlObject.hostname)) { urlObject.hostname = urlObject.hostname.replace(/^www\./, ""); } } if (Array.isArray(options.removeQueryParameters)) { for (const key of [...urlObject.searchParams.keys()]) { if (testParameter(key, options.removeQueryParameters)) { urlObject.searchParams.delete(key); } } } if (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) { urlObject.search = ""; } if (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) { for (const key of [...urlObject.searchParams.keys()]) { if (!testParameter(key, options.keepQueryParameters)) { urlObject.searchParams.delete(key); } } } if (options.sortQueryParameters) { urlObject.searchParams.sort(); try { urlObject.search = decodeURIComponent(urlObject.search); } catch { } } if (options.removeTrailingSlash) { urlObject.pathname = urlObject.pathname.replace(/\/$/, ""); } if (options.removeExplicitPort && urlObject.port) { urlObject.port = ""; } const oldUrlString = urlString; urlString = urlObject.toString(); if (!options.removeSingleSlash && urlObject.pathname === "/" && !oldUrlString.endsWith("/") && urlObject.hash === "") { urlString = urlString.replace(/\/$/, ""); } if ((options.removeTrailingSlash || urlObject.pathname === "/") && urlObject.hash === "" && options.removeSingleSlash) { urlString = urlString.replace(/\/$/, ""); } if (hasRelativeProtocol && !options.normalizeProtocol) { urlString = urlString.replace(/^http:\/\//, "//"); } if (options.stripProtocol) { urlString = urlString.replace(/^(?:https?:)?\/\//, ""); } return urlString; } // ndk/core/src/relay/index.ts var import_debug2 = __toESM(require_browser()); var import_tseep = __toESM(require_lib()); // ndk/core/src/relay/keepalive.ts var NDKRelayKeepalive = class { /** * @param timeout - Time in milliseconds to wait before considering connection stale (default 30s) * @param onSilenceDetected - Callback when silence is detected */ constructor(timeout = 3e4, onSilenceDetected) { this.onSilenceDetected = onSilenceDetected; __publicField(this, "lastActivity", Date.now()); __publicField(this, "timer"); __publicField(this, "timeout"); __publicField(this, "isRunning", false); this.timeout = timeout; } /** * Records activity from the relay, resetting the silence timer */ recordActivity() { this.lastActivity = Date.now(); if (this.isRunning) { this.resetTimer(); } } /** * Starts monitoring for relay silence */ start() { if (this.isRunning) return; this.isRunning = true; this.lastActivity = Date.now(); this.resetTimer(); } /** * Stops monitoring for relay silence */ stop() { this.isRunning = false; if (this.timer) { clearTimeout(this.timer); this.timer = void 0; } } resetTimer() { if (this.timer) { clearTimeout(this.timer); } this.timer = setTimeout(() => { const silenceTime = Date.now() - this.lastActivity; if (silenceTime >= this.timeout) { this.onSilenceDetected(); } else { const remainingTime = this.timeout - silenceTime; this.timer = setTimeout(() => { this.onSilenceDetected(); }, remainingTime); } }, this.timeout); } }; async function probeRelayConnection(relay) { const probeId = `probe-${Math.random().toString(36).substring(7)}`; return new Promise((resolve) => { let responded = false; const timeout = setTimeout(() => { if (!responded) { responded = true; relay.send(["CLOSE", probeId]); resolve(false); } }, 5e3); const handler = () => { if (!responded) { responded = true; clearTimeout(timeout); relay.send(["CLOSE", probeId]); resolve(true); } }; relay.once("message", handler); relay.send([ "REQ", probeId, { kinds: [1], limit: 0 } ]); }); } // ndk/core/src/relay/connectivity.ts var MAX_RECONNECT_ATTEMPTS = 5; var FLAPPING_THRESHOLD_MS = 1e3; var NDKRelayConnectivity = class { constructor(ndkRelay, ndk) { __publicField(this, "ndkRelay"); __publicField(this, "ws"); __publicField(this, "_status"); __publicField(this, "timeoutMs"); __publicField(this, "connectedAt"); __publicField(this, "_connectionStats", { attempts: 0, success: 0, durations: [] }); __publicField(this, "debug"); __publicField(this, "netDebug"); __publicField(this, "connectTimeout"); __publicField(this, "reconnectTimeout"); __publicField(this, "ndk"); __publicField(this, "openSubs", /* @__PURE__ */ new Map()); __publicField(this, "openCountRequests", /* @__PURE__ */ new Map()); __publicField(this, "openEventPublishes", /* @__PURE__ */ new Map()); __publicField(this, "pendingAuthPublishes", /* @__PURE__ */ new Map()); __publicField(this, "serial", 0); __publicField(this, "baseEoseTimeout", 4400); // Keepalive and monitoring __publicField(this, "keepalive"); __publicField(this, "wsStateMonitor"); __publicField(this, "sleepDetector"); __publicField(this, "lastSleepCheck", Date.now()); __publicField(this, "lastMessageSent", Date.now()); __publicField(this, "wasIdle", false); /** * Utility functions to update the connection stats. */ __publicField(this, "updateConnectionStats", { connected: () => { this._connectionStats.success++; this._connectionStats.connectedAt = Date.now(); }, disconnected: () => { if (this._connectionStats.connectedAt) { this._connectionStats.durations.push(Date.now() - this._connectionStats.connectedAt); if (this._connectionStats.durations.length > 100) { this._connectionStats.durations.shift(); } } this._connectionStats.connectedAt = void 0; }, attempt: () => { this._connectionStats.attempts++; this._connectionStats.connectedAt = Date.now(); } }); this.ndkRelay = ndkRelay; this._status = 1 /* DISCONNECTED */; const rand = Math.floor(Math.random() * 1e3); this.debug = this.ndkRelay.debug.extend(`connectivity${rand}`); this.ndk = ndk; this.setupMonitoring(); } /** * Sets up keepalive, WebSocket state monitoring, and sleep detection */ setupMonitoring() { this.keepalive = new NDKRelayKeepalive(12e4, async () => { this.debug("Relay silence detected, probing connection"); const isAlive = await probeRelayConnection({ send: (msg) => this.send(JSON.stringify(msg)), once: (event, handler) => { const messageHandler = (e2) => { try { const data = JSON.parse(e2.data); if (data[0] === "EOSE" || data[0] === "EVENT" || data[0] === "NOTICE") { handler(); this.ws?.removeEventListener("message", messageHandler); } } catch { } }; this.ws?.addEventListener("message", messageHandler); } }); if (!isAlive) { this.debug("Probe failed, connection is stale"); this.handleStaleConnection(); } }); this.wsStateMonitor = setInterval(() => { if (this._status === 5 /* CONNECTED */) { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.debug("WebSocket died silently, reconnecting"); this.handleStaleConnection(); } } }, 5e3); this.sleepDetector = setInterval(() => { const now2 = Date.now(); const elapsed = now2 - this.lastSleepCheck; if (elapsed > 15e3) { this.debug(`Detected possible sleep/wake (${elapsed}ms gap)`); this.handlePossibleWake(); } this.lastSleepCheck = now2; }, 1e4); } /** * Handles detection of a stale connection */ handleStaleConnection() { this._status = 1 /* DISCONNECTED */; this.wasIdle = true; this.onDisconnect(); } /** * Handles possible system wake event */ handlePossibleWake() { this.debug("System wake detected, checking all connections"); this.wasIdle = true; if (this._status >= 5 /* CONNECTED */) { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.handleStaleConnection(); } else { probeRelayConnection({ send: (msg) => this.send(JSON.stringify(msg)), once: (event, handler) => { const messageHandler = (e2) => { try { const data = JSON.parse(e2.data); if (data[0] === "EOSE" || data[0] === "EVENT" || data[0] === "NOTICE") { handler(); this.ws?.removeEventListener("message", messageHandler); } } catch { } }; this.ws?.addEventListener("message", messageHandler); } }).then((isAlive) => { if (!isAlive) { this.handleStaleConnection(); } }); } } } /** * Resets the reconnection state for system-wide events * Used by NDKPool when detecting system sleep/wake */ resetReconnectionState() { this.wasIdle = true; if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = void 0; } } /** * Connects to the NDK relay and handles the connection lifecycle. * * This method attempts to establish a WebSocket connection to the NDK relay specified in the `ndkRelay` object. * If the connection is successful, it updates the connection statistics, sets the connection status to `CONNECTED`, * and emits `connect` and `ready` events on the `ndkRelay` object. * * If the connection attempt fails, it handles the error by either initiating a reconnection attempt or emitting a * `delayed-connect` event on the `ndkRelay` object, depending on the `reconnect` parameter. * * @param timeoutMs - The timeout in milliseconds for the connection attempt. If not provided, the default timeout from the `ndkRelay` object is used. * @param reconnect - Indicates whether a reconnection should be attempted if the connection fails. Defaults to `true`. * @returns A Promise that resolves when the connection is established, or rejects if the connection fails. */ async connect(timeoutMs, reconnect = true) { if (this.ws && this.ws.readyState !== WebSocket.OPEN && this.ws.readyState !== WebSocket.CONNECTING) { this.debug("Cleaning up stale WebSocket connection"); try { this.ws.close(); } catch (e2) { } this.ws = void 0; this._status = 1 /* DISCONNECTED */; } if (this._status !== 2 /* RECONNECTING */ && this._status !== 1 /* DISCONNECTED */ || this.reconnectTimeout) { this.debug( "Relay requested to be connected but was in state %s or it had a reconnect timeout", this._status ); return; } if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = void 0; } if (this.connectTimeout) { clearTimeout(this.connectTimeout); this.connectTimeout = void 0; } timeoutMs ?? (timeoutMs = this.timeoutMs); if (!this.timeoutMs && timeoutMs) this.timeoutMs = timeoutMs; if (this.timeoutMs) this.connectTimeout = setTimeout(() => this.onConnectionError(reconnect), this.timeoutMs); try { this.updateConnectionStats.attempt(); if (this._status === 1 /* DISCONNECTED */) this._status = 4 /* CONNECTING */; else this._status = 2 /* RECONNECTING */; this.ws = new WebSocket(this.ndkRelay.url); this.ws.onopen = this.onConnect.bind(this); this.ws.onclose = this.onDisconnect.bind(this); this.ws.onmessage = this.onMessage.bind(this); this.ws.onerror = this.onError.bind(this); } catch (e2) { this.debug(`Failed to connect to ${this.ndkRelay.url}`, e2); this._status = 1 /* DISCONNECTED */; if (reconnect) this.handleReconnection(); else this.ndkRelay.emit("delayed-connect", 2 * 24 * 60 * 60 * 1e3); throw e2; } } /** * Disconnects the WebSocket connection to the NDK relay. * This method sets the connection status to `NDKRelayStatus.DISCONNECTING`, * attempts to close the WebSocket connection, and sets the status to * `NDKRelayStatus.DISCONNECTED` if the disconnect operation fails. */ disconnect() { this._status = 0 /* DISCONNECTING */; this.keepalive?.stop(); if (this.wsStateMonitor) { clearInterval(this.wsStateMonitor); this.wsStateMonitor = void 0; } if (this.sleepDetector) { clearInterval(this.sleepDetector); this.sleepDetector = void 0; } try { this.ws?.close(); } catch (e2) { this.debug("Failed to disconnect", e2); this._status = 1 /* DISCONNECTED */; } } /** * Handles the error that occurred when attempting to connect to the NDK relay. * If `reconnect` is `true`, this method will initiate a reconnection attempt. * Otherwise, it will emit a `delayed-connect` event on the `ndkRelay` object, * indicating that a reconnection should be attempted after a delay. * * @param reconnect - Indicates whether a reconnection should be attempted. */ onConnectionError(reconnect) { this.debug(`Error connecting to ${this.ndkRelay.url}`, this.timeoutMs); if (reconnect && !this.reconnectTimeout) { this.handleReconnection(); } } /** * Handles the connection event when the WebSocket connection is established. * This method is called when the WebSocket connection is successfully opened. * It clears any existing connection and reconnection timeouts, updates the connection statistics, * sets the connection status to `CONNECTED`, and emits `connect` and `ready` events on the `ndkRelay` object. */ onConnect() { this.netDebug?.("connected", this.ndkRelay); if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = void 0; } if (this.connectTimeout) { clearTimeout(this.connectTimeout); this.connectTimeout = void 0; } this.updateConnectionStats.connected(); this._status = 5 /* CONNECTED */; this.keepalive?.start(); this.wasIdle = false; this.ndkRelay.emit("connect"); this.ndkRelay.emit("ready"); } /** * Handles the disconnection event when the WebSocket connection is closed. * This method is called when the WebSocket connection is successfully closed. * It updates the connection statistics, sets the connection status to `DISCONNECTED`, * initiates a reconnection attempt if we didn't disconnect ourselves, * and emits a `disconnect` event on the `ndkRelay` object. */ onDisconnect() { this.netDebug?.("disconnected", this.ndkRelay); this.updateConnectionStats.disconnected(); this.keepalive?.stop(); this.clearPendingPublishes(new Error(`Relay ${this.ndkRelay.url} disconnected`)); if (this._status === 5 /* CONNECTED */) { this.handleReconnection(); } this._status = 1 /* DISCONNECTED */; this.ndkRelay.emit("disconnect"); } /** * Handles incoming messages from the NDK relay WebSocket connection. * This method is called whenever a message is received from the relay. * It parses the message data and dispatches the appropriate handling logic based on the message type. * * @param event - The MessageEvent containing the received message data. */ /** * Fast extraction of event ID from JSON string without parsing. * Returns event ID if message is EVENT type, null otherwise. * This optimization avoids expensive JSON.parse() for duplicate events. */ getEventIdFromMessage(msg) { if (msg.charCodeAt(2) !== 69 || msg.charCodeAt(3) !== 86) { return null; } const idPos = msg.indexOf('"id":"'); if (idPos === -1) { return null; } return msg.substring(idPos + 6, idPos + 70); } onMessage(event) { this.netDebug?.(event.data, this.ndkRelay, "recv"); this.keepalive?.recordActivity(); const msg = event.data; const eventId = this.getEventIdFromMessage(msg); if (eventId && this.ndk) { const seenRelays = this.ndk.subManager.seenEvents.get(eventId); if (seenRelays && seenRelays.length > 0) { this.ndk.subManager.seenEvent(eventId, this.ndkRelay); return; } } try { const data = JSON.parse(event.data); const [cmd, id, ..._rest] = data; const handler = this.ndkRelay.getProtocolHandler(cmd); if (handler) { handler(this.ndkRelay, data); return; } switch (cmd) { case "EVENT": { const so = this.openSubs.get(id); const event2 = data[2]; if (!so) { this.debug(`Received event for unknown subscription ${id}`); return; } so.onevent(event2); return; } case "COUNT": { const payload = data[2]; const cr = this.openCountRequests.get(id); if (cr) { cr.resolve(payload.count); this.openCountRequests.delete(id); } return; } case "EOSE": { const so = this.openSubs.get(id); if (!so) return; so.oneose(id); return; } case "OK": { const ok = data[2]; const reason = data[3]; const ep = this.openEventPublishes.get(id); const firstEp = ep?.pop(); if (!ep || !firstEp) { this.debug("Received OK for unknown event publish", id); return; } if (ok) { firstEp.resolve(reason); this.pendingAuthPublishes.delete(id); } else { const isAuthRequired = reason && (reason.toLowerCase().includes("auth-required") || reason.toLowerCase().includes("not authorized") || reason.toLowerCase().includes("blocked: not authorized")); if (isAuthRequired) { const event2 = this.pendingAuthPublishes.get(id); if (event2) { this.debug("Publish failed due to auth-required, will retry after auth", id); ep.push(firstEp); this.openEventPublishes.set(id, ep); } else { firstEp.reject(new Error(reason)); } } else { firstEp.reject(new Error(reason)); this.pendingAuthPublishes.delete(id); } } if (ep.length === 0) { this.openEventPublishes.delete(id); } else if (!ok && !(reason?.toLowerCase().includes("auth-required") || reason?.toLowerCase().includes("not authorized") || reason?.toLowerCase().includes("blocked: not authorized"))) { this.openEventPublishes.set(id, ep); } return; } case "CLOSED": { const so = this.openSubs.get(id); if (!so) return; so.onclosed(data[2]); return; } case "NOTICE": this.onNotice(data[1]); return; case "AUTH": { this.onAuthRequested(data[1]); return; } } } catch (error) { this.debug(`Error parsing message from ${this.ndkRelay.url}: ${error.message}`, error?.stack); return; } } /** * Handles an authentication request from the NDK relay. * * If an authentication policy is configured, it will be used to authenticate the connection. * Otherwise, the `auth` event will be emitted to allow the application to handle the authentication. * * @param challenge - The authentication challenge provided by the NDK relay. */ async onAuthRequested(challenge3) { const authPolicy = this.ndkRelay.authPolicy ?? this.ndk?.relayAuthDefaultPolicy; this.debug("Relay requested authentication", { havePolicy: !!authPolicy }); if (this._status === 7 /* AUTHENTICATING */) { this.debug("Already authenticating, ignoring"); return; } this._status = 6 /* AUTH_REQUESTED */; if (authPolicy) { if (this._status >= 5 /* CONNECTED */) { this._status = 7 /* AUTHENTICATING */; let res; try { res = await authPolicy(this.ndkRelay, challenge3); } catch (e2) { this.debug("Authentication policy threw an error", e2); res = false; } this.debug("Authentication policy returned", !!res); if (res instanceof NDKEvent || res === true) { if (res instanceof NDKEvent) { await this.auth(res); } const authenticate = async () => { if (this._status >= 5 /* CONNECTED */ && this._status < 8 /* AUTHENTICATED */) { const event = new NDKEvent(this.ndk); event.kind = 22242 /* ClientAuth */; event.tags = [ ["relay", this.ndkRelay.url], ["challenge", challenge3] ]; await event.sign(); this.auth(event).then(() => { this._status = 8 /* AUTHENTICATED */; this.ndkRelay.emit("authed"); this.debug("Authentication successful"); this.retryPendingAuthPublishes(); }).catch((e2) => { this._status = 6 /* AUTH_REQUESTED */; this.ndkRelay.emit("auth:failed", e2); this.debug("Authentication failed", e2); this.rejectPendingAuthPublishes(e2); }); } else { this.debug("Authentication failed, it changed status, status is %d", this._status); } }; if (res === true) { if (!this.ndk?.signer) { this.debug("No signer available for authentication localhost"); this.ndk?.once("signer:ready", authenticate); } else { authenticate().catch((e2) => { console.error("Error authenticating", e2); }); } } this._status = 5 /* CONNECTED */; this.ndkRelay.emit("authed"); } } } else { this.ndkRelay.emit("auth", challenge3); } } /** * Handles errors that occur on the WebSocket connection to the relay. * @param error - The error or event that occurred. */ onError(error) { this.debug(`WebSocket error on ${this.ndkRelay.url}:`, error); } /** * Gets the current status of the NDK relay connection. * @returns {NDKRelayStatus} The current status of the NDK relay connection. */ get status() { return this._status; } /** * Checks if the NDK relay connection is currently available. * @returns {boolean} `true` if the relay connection is in the `CONNECTED` status, `false` otherwise. */ isAvailable() { return this._status === 5 /* CONNECTED */; } /** * Checks if the NDK relay connection is flapping, which means the connection is rapidly * disconnecting and reconnecting. This is determined by analyzing the durations of the * last three connection attempts. If the standard deviation of the durations is less * than 1000 milliseconds, the connection is considered to be flapping. * * @returns {boolean} `true` if the connection is flapping, `false` otherwise. */ isFlapping() { const durations = this._connectionStats.durations; if (durations.length % 3 !== 0) return false; const sum = durations.reduce((a, b) => a + b, 0); const avg = sum / durations.length; const variance = durations.map((x2) => (x2 - avg) ** 2).reduce((a, b) => a + b, 0) / durations.length; const stdDev = Math.sqrt(variance); const isFlapping = stdDev < FLAPPING_THRESHOLD_MS; return isFlapping; } /** * Handles a notice received from the NDK relay. * If the notice indicates the relay is complaining (e.g. "too many" or "maximum"), * the method disconnects from the relay and attempts to reconnect after a 2-second delay. * A debug message is logged with the relay URL and the notice text. * The "notice" event is emitted on the ndkRelay instance with the notice text. * * @param notice - The notice text received from the NDK relay. */ async onNotice(notice) { this.ndkRelay.emit("notice", notice); } /** * Attempts to reconnect to the NDK relay after a connection is lost. * This function is called recursively to handle multiple reconnection attempts. * It checks if the relay is flapping and emits a "flapping" event if so. * It then calculates a delay before the next reconnection attempt based on the number of previous attempts. * The function sets a timeout to execute the next reconnection attempt after the calculated delay. * If the maximum number of reconnection attempts is reached, a debug message is logged. * * @param attempt - The current attempt number (default is 0). */ handleReconnection(attempt = 0) { if (this.reconnectTimeout) return; if (this.isFlapping()) { this.ndkRelay.emit("flapping", this._connectionStats); this._status = 3 /* FLAPPING */; return; } let reconnectDelay; if (this.wasIdle) { const aggressiveDelays = [0, 1e3, 2e3, 5e3, 1e4, 3e4]; reconnectDelay = aggressiveDelays[Math.min(attempt, aggressiveDelays.length - 1)]; this.debug(`Using aggressive reconnect after idle, attempt ${attempt}, delay ${reconnectDelay}ms`); } else if (this.connectedAt) { reconnectDelay = Math.max(0, 6e4 - (Date.now() - this.connectedAt)); } else { reconnectDelay = Math.min(1e3 * 2 ** attempt, 3e4); this.debug(`Using standard backoff, attempt ${attempt}, delay ${reconnectDelay}ms`); } this.reconnectTimeout = setTimeout(() => { this.reconnectTimeout = void 0; this._status = 2 /* RECONNECTING */; this.connect().catch((_err) => { if (attempt < MAX_RECONNECT_ATTEMPTS) { this.handleReconnection(attempt + 1); } else { this.debug("Max reconnect attempts reached"); this.wasIdle = false; } }); }, reconnectDelay); this.ndkRelay.emit("delayed-connect", reconnectDelay); this.debug("Reconnecting in", reconnectDelay); this._connectionStats.nextReconnectAt = Date.now() + reconnectDelay; } /** * Sends a message to the NDK relay if the connection is in the CONNECTED state and the WebSocket is open. * If the connection is not in the CONNECTED state or the WebSocket is not open, logs a debug message and throws an error. * * @param message - The message to send to the NDK relay. * @throws {Error} If attempting to send on a closed relay connection. */ async send(message) { const idleTime = Date.now() - this.lastMessageSent; if (idleTime > 12e4) { this.wasIdle = true; } if (this._status >= 5 /* CONNECTED */ && this.ws?.readyState === WebSocket.OPEN) { this.ws?.send(message); this.netDebug?.(message, this.ndkRelay, "send"); this.lastMessageSent = Date.now(); } else { this.debug(`Not connected to ${this.ndkRelay.url} (%d), not sending message ${message}`, this._status); if (this._status >= 5 /* CONNECTED */ && this.ws?.readyState !== WebSocket.OPEN) { this.debug(`Stale connection detected, WebSocket state: ${this.ws?.readyState}`); this.handleStaleConnection(); } } } /** * Authenticates the NDK event by sending it to the NDK relay and returning a promise that resolves with the result. * * @param event - The NDK event to authenticate. * @returns A promise that resolves with the authentication result. */ async auth(event) { const ret = new Promise((resolve, reject) => { const val = this.openEventPublishes.get(event.id) ?? []; val.push({ resolve, reject }); this.openEventPublishes.set(event.id, val); }); this.send(`["AUTH",${JSON.stringify(event.rawEvent())}]`); return ret; } /** * Clears all pending publish promises by rejecting them with the provided error. * This is called on disconnection to prevent memory leaks and ensure promises * don't hang indefinitely. * @param error The error to reject the promises with */ clearPendingPublishes(error) { this.rejectPendingAuthPublishes(error); for (const [eventId, resolvers] of this.openEventPublishes.entries()) { while (resolvers.length > 0) { const resolver = resolvers.shift(); if (resolver) { resolver.reject(error); } } this.openEventPublishes.delete(eventId); } } /** * Retries all pending publishes that failed due to auth-required. * Called after successful authentication. */ retryPendingAuthPublishes() { if (this.pendingAuthPublishes.size === 0) return; this.debug(`Retrying ${this.pendingAuthPublishes.size} pending publishes after auth`); for (const [eventId, event] of this.pendingAuthPublishes.entries()) { this.debug(`Retrying publish for event ${eventId}`); this.send(`["EVENT",${JSON.stringify(event)}]`); } this.pendingAuthPublishes.clear(); } /** * Rejects all pending publishes that failed due to auth-required. * Called when authentication fails. */ rejectPendingAuthPublishes(error) { if (this.pendingAuthPublishes.size === 0) return; this.debug(`Rejecting ${this.pendingAuthPublishes.size} pending publishes due to auth failure`); for (const [eventId] of this.pendingAuthPublishes.entries()) { const ep = this.openEventPublishes.get(eventId); if (ep && ep.length > 0) { const resolver = ep.pop(); if (resolver) { resolver.reject(new Error(`Authentication failed: ${error.message}`)); } if (ep.length === 0) { this.openEventPublishes.delete(eventId); } } } this.pendingAuthPublishes.clear(); } /** * Publishes an NDK event to the relay and returns a promise that resolves with the result. * * @param event - The NDK event to publish. * @returns A promise that resolves with the result of the event publication. * @throws {Error} If attempting to publish on a closed relay connection. */ async publish(event) { const ret = new Promise((resolve, reject) => { const val = this.openEventPublishes.get(event.id) ?? []; if (val.length > 0) { console.warn(`Duplicate event publishing detected, you are publishing event ${event.id} twice`); } val.push({ resolve, reject }); this.openEventPublishes.set(event.id, val); }); this.pendingAuthPublishes.set(event.id, event); this.send(`["EVENT",${JSON.stringify(event)}]`); return ret; } /** * Counts the number of events that match the provided filters. * * @param filters - The filters to apply to the count request. * @param params - An optional object containing a custom id for the count request. * @returns A promise that resolves with the number of matching events. * @throws {Error} If attempting to send the count request on a closed relay connection. */ async count(filters, params) { this.serial++; const id = params?.id || `count:${this.serial}`; const ret = new Promise((resolve, reject) => { this.openCountRequests.set(id, { resolve, reject }); }); this.send(`["COUNT","${id}",${JSON.stringify(filters).substring(1)}`); return ret; } close(subId, reason) { this.send(`["CLOSE","${subId}"]`); const sub = this.openSubs.get(subId); this.openSubs.delete(subId); if (sub) sub.onclose(reason); } /** * Subscribes to the NDK relay with the provided filters and parameters. * * @param filters - The filters to apply to the subscription. * @param params - The subscription parameters, including an optional custom id. * @returns A new NDKRelaySubscription instance. */ req(relaySub) { `${this.send(`["REQ","${relaySub.subId}",${JSON.stringify(relaySub.executeFilters).substring(1)}`)}]`; this.openSubs.set(relaySub.subId, relaySub); } /** Returns the connection stats. */ get connectionStats() { return this._connectionStats; } /** Returns the relay URL */ get url() { return this.ndkRelay.url; } get connected() { return this._status >= 5 /* CONNECTED */ && this.ws?.readyState === WebSocket.OPEN; } }; // ndk/core/src/relay/nip11.ts async function fetchRelayInformation(relayUrl) { const httpUrl = relayUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://"); const response = await fetch(httpUrl, { headers: { Accept: "application/nostr+json" } }); if (!response.ok) { throw new Error(`Failed to fetch relay information: ${response.status} ${response.statusText}`); } const data = await response.json(); return data; } // ndk/core/src/relay/publisher.ts var NDKRelayPublisher = class { constructor(ndkRelay) { __publicField(this, "ndkRelay"); __publicField(this, "debug"); this.ndkRelay = ndkRelay; this.debug = ndkRelay.debug.extend("publisher"); } /** * Published an event to the relay; if the relay is not connected, it will * wait for the relay to connect before publishing the event. * * If the relay does not connect within the timeout, the publish operation * will fail. * @param event The event to publish * @param timeoutMs The timeout for the publish operation in milliseconds * @returns A promise that resolves when the event has been published or rejects if the operation times out */ async publish(event, timeoutMs = 2500) { let timeout; const publishConnected = () => { return new Promise((resolve, reject) => { try { this.publishEvent(event).then((_result) => { this.ndkRelay.emit("published", event); event.emit("relay:published", this.ndkRelay); resolve(true); }).catch(reject); } catch (err) { reject(err); } }); }; const timeoutPromise = new Promise((_2, reject) => { timeout = setTimeout(() => { timeout = void 0; reject(new Error(`Timeout: ${timeoutMs}ms`)); }, timeoutMs); }); const onConnectHandler = () => { publishConnected().then((result) => connectResolve(result)).catch((err) => connectReject(err)); }; let connectResolve; let connectReject; const onError = (err) => { this.ndkRelay.debug("Publish failed", err, event.id); this.ndkRelay.emit("publish:failed", event, err); event.emit("relay:publish:failed", this.ndkRelay, err); throw err; }; const onFinally = () => { if (timeout) clearTimeout(timeout); this.ndkRelay.removeListener("connect", onConnectHandler); }; if (this.ndkRelay.status >= 5 /* CONNECTED */) { return Promise.race([publishConnected(), timeoutPromise]).catch(onError).finally(onFinally); } if (this.ndkRelay.status <= 1 /* DISCONNECTED */) { console.warn("Relay is disconnected, trying to connect to publish an event", this.ndkRelay.url); this.ndkRelay.connect(); } else { console.warn("Relay not connected, waiting for connection to publish an event", this.ndkRelay.url); } return Promise.race([ new Promise((resolve, reject) => { connectResolve = resolve; connectReject = reject; this.ndkRelay.on("connect", onConnectHandler); }), timeoutPromise ]).catch(onError).finally(onFinally); } async publishEvent(event) { return this.ndkRelay.connectivity.publish(event.rawEvent()); } }; // ndk/core/src/relay/signature-verification-stats.ts var import_debug = __toESM(require_browser()); var SignatureVerificationStats = class { /** * Creates a new SignatureVerificationStats instance * * @param ndk - The NDK instance to track stats for * @param intervalMs - How often to print stats (in milliseconds) */ constructor(ndk, intervalMs = 1e4) { __publicField(this, "ndk"); __publicField(this, "debug"); __publicField(this, "intervalId", null); __publicField(this, "intervalMs"); this.ndk = ndk; this.debug = (0, import_debug.default)("ndk:signature-verification-stats"); this.intervalMs = intervalMs; } /** * Start tracking and reporting signature verification statistics */ start() { if (this.intervalId) { this.debug("Stats tracking already started"); return; } this.debug(`Starting signature verification stats reporting every ${this.intervalMs}ms`); this.intervalId = setInterval(() => { this.reportStats(); }, this.intervalMs); } /** * Stop tracking and reporting signature verification statistics */ stop() { if (!this.intervalId) { this.debug("Stats tracking not started"); return; } clearInterval(this.intervalId); this.intervalId = null; this.debug("Stopped signature verification stats reporting"); } /** * Report current signature verification statistics for all relays */ reportStats() { const stats = this.collectStats(); console.log("\n=== Signature Verification Sampling Stats ==="); console.log(`Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}`); console.log(`Total Relays: ${stats.totalRelays}`); console.log(`Connected Relays: ${stats.connectedRelays}`); if (stats.relayStats.length === 0) { console.log("No relay statistics available"); } else { console.log("\nRelay Statistics:"); stats.relayStats.sort((a, b) => a.url.localeCompare(b.url)); stats.relayStats.forEach((relayStat) => { console.log(` ${relayStat.url} ${relayStat.connected ? "(connected)" : "(disconnected)"}`); console.log(` Validated Events: ${relayStat.validatedCount}`); console.log(` Non-validated Events: ${relayStat.nonValidatedCount}`); console.log(` Total Events: ${relayStat.totalEvents}`); console.log( ` Current Validation Ratio: ${relayStat.validationRatio.toFixed(4)} (${(relayStat.validationRatio * 100).toFixed(2)}%)` ); console.log( ` Target Validation Ratio: ${relayStat.targetValidationRatio?.toFixed(4) || "N/A"} (${relayStat.targetValidationRatio ? (relayStat.targetValidationRatio * 100).toFixed(2) + "%" : "N/A"})` ); console.log(` Trusted: ${relayStat.trusted ? "Yes" : "No"}`); }); } console.log("\nGlobal Settings:"); console.log( ` Initial Validation Ratio: ${stats.initialValidationRatio.toFixed(4)} (${(stats.initialValidationRatio * 100).toFixed(2)}%)` ); console.log( ` Lowest Validation Ratio: ${stats.lowestValidationRatio.toFixed(4)} (${(stats.lowestValidationRatio * 100).toFixed(2)}%)` ); console.log("===========================================\n"); } /** * Collect statistics from all relays */ collectStats() { const relayStats = []; for (const relay of this.ndk.pool.relays.values()) { relayStats.push({ url: relay.url, connected: relay.connected, validatedCount: relay.validatedEventCount, nonValidatedCount: relay.nonValidatedEventCount, totalEvents: relay.validatedEventCount + relay.nonValidatedEventCount, validationRatio: relay.validationRatio, targetValidationRatio: relay.targetValidationRatio, trusted: relay.trusted }); } return { totalRelays: this.ndk.pool.relays.size, connectedRelays: this.ndk.pool.connectedRelays().length, relayStats, initialValidationRatio: this.ndk.initialValidationRatio, lowestValidationRatio: this.ndk.lowestValidationRatio }; } }; function startSignatureVerificationStats(ndk, intervalMs = 1e4) { const stats = new SignatureVerificationStats(ndk, intervalMs); stats.start(); return stats; } // ndk/core/src/subscription/grouping.ts function filterFingerprint(filters, closeOnEose) { const elements = []; for (const filter of filters) { const keys = Object.entries(filter || {}).map(([key, values]) => { if (["since", "until"].includes(key)) { return `${key}:${values}`; } return key; }).sort().join("-"); elements.push(keys); } let id = closeOnEose ? "+" : ""; id += elements.join("|"); return id; } function mergeFilters(filters) { const result = []; const lastResult = {}; filters.filter((f) => !!f.limit).forEach((filterWithLimit) => result.push(filterWithLimit)); filters = filters.filter((f) => !f.limit); if (filters.length === 0) return result; filters.forEach((filter) => { Object.entries(filter).forEach(([key, value]) => { if (Array.isArray(value)) { if (lastResult[key] === void 0) { lastResult[key] = [...value]; } else { lastResult[key] = Array.from(/* @__PURE__ */ new Set([...lastResult[key], ...value])); } } else { lastResult[key] = value; } }); }); return [...result, lastResult]; } // ndk/core/src/subscription/utils/format-filters.ts var MAX_ITEMS = 3; function formatArray(items, formatter) { const formatted = formatter ? items.slice(0, MAX_ITEMS).map(formatter) : items.slice(0, MAX_ITEMS); const display = formatted.join(","); return items.length > MAX_ITEMS ? `${display}+${items.length - MAX_ITEMS}` : display; } function formatFilters(filters) { return filters.map((f) => { const parts = []; if (f.ids?.length) { parts.push(`ids:[${formatArray(f.ids, (id) => String(id).slice(0, 8))}]`); } if (f.kinds?.length) { parts.push(`kinds:[${formatArray(f.kinds)}]`); } if (f.authors?.length) { parts.push(`authors:[${formatArray(f.authors, (a) => String(a).slice(0, 8))}]`); } if (f.since) { parts.push(`since:${f.since}`); } if (f.until) { parts.push(`until:${f.until}`); } if (f.limit) { parts.push(`limit:${f.limit}`); } if (f.search) { parts.push(`search:"${String(f.search).slice(0, 20)}"`); } for (const [key, value] of Object.entries(f)) { if (key.startsWith("#") && Array.isArray(value) && value.length > 0) { parts.push(`${key}:[${formatArray(value, (v6) => String(v6).slice(0, 8))}]`); } } return `{${parts.join(" ")}}`; }).join(", "); } // ndk/core/src/relay/subscription.ts var NDKRelaySubscription = class { /** * * @param fingerprint The fingerprint of this subscription. */ constructor(relay, fingerprint, topSubManager) { __publicField(this, "fingerprint"); __publicField(this, "items", /* @__PURE__ */ new Map()); __publicField(this, "topSubManager"); __publicField(this, "debug"); /** * Tracks the status of this REQ. */ __publicField(this, "status", 0 /* INITIAL */); __publicField(this, "onClose"); __publicField(this, "relay"); /** * Whether this subscription has reached EOSE. */ __publicField(this, "eosed", false); /** * Timeout at which this subscription will * start executing. */ __publicField(this, "executionTimer"); /** * Track the time at which this subscription will fire. */ __publicField(this, "fireTime"); /** * The delay type that the current fireTime was calculated with. */ __publicField(this, "delayType"); /** * The filters that have been executed. */ __publicField(this, "executeFilters"); __publicField(this, "id", Math.random().toString(36).substring(7)); __publicField(this, "_subId"); __publicField(this, "subIdParts", /* @__PURE__ */ new Set()); __publicField(this, "executeOnRelayReady", () => { if (this.status !== 2 /* WAITING */) return; if (this.items.size === 0) { this.debug( "No items to execute; this relay was probably too slow to respond and the caller gave up", { status: this.status, fingerprint: this.fingerprint, id: this.id, subId: this.subId } ); this.cleanup(); return; } this.debug("Executing on relay ready", { status: this.status, fingerprint: this.fingerprint, itemsSize: this.items.size, filters: formatFilters(this.compileFilters()) }); this.status = 1 /* PENDING */; this.execute(); }); // we do it this way so that we can remove the listener __publicField(this, "reExecuteAfterAuth", (() => { const oldSubId = this.subId; this.debug("Re-executing after auth", this.items.size); if (this.eosed) { this.relay.close(this.subId); } else { this.debug( "We are abandoning an opened subscription, once it EOSE's, the handler will close it", { oldSubId } ); } this._subId = void 0; this.status = 1 /* PENDING */; this.execute(); this.debug("Re-executed after auth %s \u{1F449} %s", oldSubId, this.subId); }).bind(this)); this.relay = relay; this.topSubManager = topSubManager; this.debug = relay.debug.extend(`sub[${this.id}]`); this.fingerprint = fingerprint || Math.random().toString(36).substring(7); } get subId() { if (this._subId) return this._subId; this._subId = this.fingerprint.slice(0, 15); return this._subId; } addSubIdPart(part) { this.subIdParts.add(part); } addItem(subscription, filters) { if (this.items.has(subscription.internalId)) { return; } subscription.on("close", this.removeItem.bind(this, subscription)); this.items.set(subscription.internalId, { subscription, filters }); if (this.status !== 3 /* RUNNING */) { if (subscription.subId && (!this._subId || this._subId.length < 25)) { if (this.status === 0 /* INITIAL */ || this.status === 1 /* PENDING */) { this.addSubIdPart(subscription.subId); } } } switch (this.status) { case 0 /* INITIAL */: this.evaluateExecutionPlan(subscription); break; case 3 /* RUNNING */: break; case 1 /* PENDING */: this.evaluateExecutionPlan(subscription); break; case 4 /* CLOSED */: this.debug("Subscription is closed, cannot add new items", { filters: formatFilters(filters), subId: subscription.subId, internalId: subscription.internalId }); throw new Error("Cannot add new items to a closed subscription"); } } /** * A subscription has been closed, remove it from the list of items. * @param subscription */ removeItem(subscription) { this.items.delete(subscription.internalId); if (this.items.size === 0) { if (this.status === 0 /* INITIAL */ || this.status === 1 /* PENDING */) { this.status = 4 /* CLOSED */; this.cleanup(); return; } if (!this.eosed) return; this.close(); this.cleanup(); } } close() { if (this.status === 4 /* CLOSED */) return; const prevStatus = this.status; this.status = 4 /* CLOSED */; if (prevStatus === 3 /* RUNNING */) { try { this.relay.close(this.subId); } catch (e2) { this.debug("Error closing subscription", e2, this); } } else { this.debug("Subscription wanted to close but it wasn't running, this is probably ok", { subId: this.subId, prevStatus, sub: this }); } this.cleanup(); } cleanup() { if (this.executionTimer) clearTimeout(this.executionTimer); this.relay.off("ready", this.executeOnRelayReady); this.relay.off("authed", this.reExecuteAfterAuth); if (this.onClose) this.onClose(this); } evaluateExecutionPlan(subscription) { if (!subscription.isGroupable()) { this.status = 1 /* PENDING */; this.execute(); return; } if (subscription.filters.find((filter) => !!filter.limit)) { this.executeFilters = this.compileFilters(); if (this.executeFilters.length >= 10) { this.status = 1 /* PENDING */; this.execute(); return; } } const delay = subscription.groupableDelay; const delayType = subscription.groupableDelayType; if (!delay) throw new Error("Cannot group a subscription without a delay"); if (this.status === 0 /* INITIAL */) { this.schedule(delay, delayType); } else { const existingDelayType = this.delayType; const timeUntilFire = this.fireTime - Date.now(); if (existingDelayType === "at-least" && delayType === "at-least") { if (timeUntilFire < delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else if (existingDelayType === "at-least" && delayType === "at-most") { if (timeUntilFire > delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else if (existingDelayType === "at-most" && delayType === "at-most") { if (timeUntilFire > delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else if (existingDelayType === "at-most" && delayType === "at-least") { if (timeUntilFire > delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else { throw new Error(`Unknown delay type combination ${existingDelayType} ${delayType}`); } } } schedule(delay, delayType) { this.status = 1 /* PENDING */; const currentTime = Date.now(); this.fireTime = currentTime + delay; this.delayType = delayType; const timer = setTimeout(() => { this.execute(); }, delay); if (delayType === "at-least") { this.executionTimer = timer; } } finalizeSubId() { if (this.subIdParts.size > 0) { const parts = Array.from(this.subIdParts).map((part) => part.substring(0, 10)); let joined = parts.join("-"); if (joined.length > 20) { joined = joined.substring(0, 20); } this._subId = joined; } else { this._subId = this.fingerprint.slice(0, 15); } this._subId += `-${Math.random().toString(36).substring(2, 7)}`; } execute() { if (this.status !== 1 /* PENDING */) { return; } if (!this.relay.connected) { this.status = 2 /* WAITING */; this.debug("Waiting for relay to be ready", { status: this.status, id: this.subId, fingerprint: this.fingerprint, itemsSize: this.items.size }); this.relay.once("ready", this.executeOnRelayReady); return; } if (this.relay.status < 8 /* AUTHENTICATED */) { this.relay.once("authed", this.reExecuteAfterAuth); } this.status = 3 /* RUNNING */; this.finalizeSubId(); this.executeFilters = this.compileFilters(); this.relay.req(this); } onstart() { } onevent(event) { this.topSubManager.dispatchEvent(event, this.relay); } oneose(subId) { this.eosed = true; if (subId !== this.subId) { this.debug("Received EOSE for an abandoned subscription", subId, this.subId); this.relay.close(subId); return; } if (this.items.size === 0) { this.close(); } for (const { subscription } of this.items.values()) { subscription.eoseReceived(this.relay); if (subscription.closeOnEose) { this.removeItem(subscription); } } } onclose(_reason) { this.status = 4 /* CLOSED */; } onclosed(reason) { if (!reason) return; for (const { subscription } of this.items.values()) { subscription.closedReceived(this.relay, reason); } } /** * Grabs the filters from all the subscriptions * and merges them into a single filter. */ compileFilters() { const mergedFilters = []; const filters = Array.from(this.items.values()).map((item) => item.filters); if (!filters[0]) { this.debug("\u{1F440} No filters to merge", { itemsSize: this.items.size }); return []; } const filterCount = filters[0].length; for (let i3 = 0; i3 < filterCount; i3++) { const allFiltersAtIndex = filters.map((filter) => filter[i3]); const merged = mergeFilters(allFiltersAtIndex); mergedFilters.push(...merged); } return mergedFilters; } }; // ndk/core/src/relay/sub-manager.ts var NDKRelaySubscriptionManager = class { /** * @param relay - The relay instance. * @param generalSubManager - The subscription manager instance. */ constructor(relay, generalSubManager) { __publicField(this, "relay"); __publicField(this, "subscriptions"); __publicField(this, "generalSubManager"); this.relay = relay; this.subscriptions = /* @__PURE__ */ new Map(); this.generalSubManager = generalSubManager; } /** * Adds a subscription to the manager. */ addSubscription(sub, filters) { let relaySub; if (!sub.isGroupable()) { relaySub = this.createSubscription(sub, filters); } else { const filterFp = filterFingerprint(filters, sub.closeOnEose); if (filterFp) { const existingSubs = this.subscriptions.get(filterFp); relaySub = (existingSubs || []).find((sub2) => sub2.status < 3 /* RUNNING */); } relaySub ?? (relaySub = this.createSubscription(sub, filters, filterFp)); } relaySub.addItem(sub, filters); } createSubscription(_sub, _filters, fingerprint) { const relaySub = new NDKRelaySubscription(this.relay, fingerprint || null, this.generalSubManager); relaySub.onClose = this.onRelaySubscriptionClose.bind(this); const currentVal = this.subscriptions.get(relaySub.fingerprint) ?? []; this.subscriptions.set(relaySub.fingerprint, [...currentVal, relaySub]); return relaySub; } onRelaySubscriptionClose(sub) { let currentVal = this.subscriptions.get(sub.fingerprint) ?? []; if (!currentVal) { console.warn("Unexpectedly did not find a subscription with fingerprint", sub.fingerprint); } else if (currentVal.length === 1) { this.subscriptions.delete(sub.fingerprint); } else { currentVal = currentVal.filter((s) => s.id !== sub.id); this.subscriptions.set(sub.fingerprint, currentVal); } } }; // ndk/core/src/relay/index.ts var NDKRelayStatus = /* @__PURE__ */ ((NDKRelayStatus2) => { NDKRelayStatus2[NDKRelayStatus2["DISCONNECTING"] = 0] = "DISCONNECTING"; NDKRelayStatus2[NDKRelayStatus2["DISCONNECTED"] = 1] = "DISCONNECTED"; NDKRelayStatus2[NDKRelayStatus2["RECONNECTING"] = 2] = "RECONNECTING"; NDKRelayStatus2[NDKRelayStatus2["FLAPPING"] = 3] = "FLAPPING"; NDKRelayStatus2[NDKRelayStatus2["CONNECTING"] = 4] = "CONNECTING"; NDKRelayStatus2[NDKRelayStatus2["CONNECTED"] = 5] = "CONNECTED"; NDKRelayStatus2[NDKRelayStatus2["AUTH_REQUESTED"] = 6] = "AUTH_REQUESTED"; NDKRelayStatus2[NDKRelayStatus2["AUTHENTICATING"] = 7] = "AUTHENTICATING"; NDKRelayStatus2[NDKRelayStatus2["AUTHENTICATED"] = 8] = "AUTHENTICATED"; return NDKRelayStatus2; })(NDKRelayStatus || {}); var _NDKRelay = class _NDKRelay extends import_tseep.EventEmitter { constructor(url, authPolicy, ndk) { super(); __publicField(this, "url"); __publicField(this, "scores"); __publicField(this, "connectivity"); __publicField(this, "subs"); __publicField(this, "publisher"); __publicField(this, "authPolicy"); /** * Protocol handlers for custom relay message types (e.g., NEG-OPEN, NEG-MSG). * Allows external packages to handle non-standard relay messages. */ __publicField(this, "protocolHandlers", /* @__PURE__ */ new Map()); /** * Cached relay information from NIP-11. */ __publicField(this, "_relayInfo"); /** * The lowest validation ratio this relay can reach. */ __publicField(this, "lowestValidationRatio"); /** * Current validation ratio this relay is targeting. */ __publicField(this, "targetValidationRatio"); __publicField(this, "validationRatioFn"); /** * This tracks events that have been seen by this relay * with a valid signature. */ __publicField(this, "validatedEventCount", 0); /** * This tracks events that have been seen by this relay * but have not been validated. */ __publicField(this, "nonValidatedEventCount", 0); /** * Whether this relay is trusted. * * Trusted relay's events do not get their signature verified. */ __publicField(this, "trusted", false); __publicField(this, "complaining", false); __publicField(this, "debug"); __publicField(this, "req"); __publicField(this, "close"); this.url = normalizeRelayUrl(url); this.scores = /* @__PURE__ */ new Map(); this.debug = (0, import_debug2.default)(`ndk:relay:${url}`); this.connectivity = new NDKRelayConnectivity(this, ndk); this.connectivity.netDebug = ndk?.netDebug; this.req = this.connectivity.req.bind(this.connectivity); this.close = this.connectivity.close.bind(this.connectivity); this.subs = new NDKRelaySubscriptionManager(this, ndk.subManager); this.publisher = new NDKRelayPublisher(this); this.authPolicy = authPolicy; this.targetValidationRatio = ndk?.initialValidationRatio; this.lowestValidationRatio = ndk?.lowestValidationRatio; this.validationRatioFn = (ndk?.validationRatioFn ?? _NDKRelay.defaultValidationRatioUpdateFn).bind(this); this.updateValidationRatio(); if (!ndk) { console.trace("relay created without ndk"); } } updateValidationRatio() { if (this.validationRatioFn && this.validatedEventCount > 0) { const newRatio = this.validationRatioFn(this, this.validatedEventCount, this.nonValidatedEventCount); this.targetValidationRatio = newRatio; } setTimeout(() => { this.updateValidationRatio(); }, 3e4); } get status() { return this.connectivity.status; } get connectionStats() { return this.connectivity.connectionStats; } /** * Connects to the relay. */ async connect(timeoutMs, reconnect = true) { return this.connectivity.connect(timeoutMs, reconnect); } /** * Disconnects from the relay. */ disconnect() { if (this.status === 1 /* DISCONNECTED */) { return; } this.connectivity.disconnect(); } /** * Queues or executes the subscription of a specific set of filters * within this relay. * * @param subscription NDKSubscription this filters belong to. * @param filters Filters to execute */ subscribe(subscription, filters) { this.subs.addSubscription(subscription, filters); } /** * Publishes an event to the relay with an optional timeout. * * If the relay is not connected, the event will be published when the relay connects, * unless the timeout is reached before the relay connects. * * @param event The event to publish * @param timeoutMs The timeout for the publish operation in milliseconds * @returns A promise that resolves when the event has been published or rejects if the operation times out */ async publish(event, timeoutMs = 2500) { return this.publisher.publish(event, timeoutMs); } referenceTags() { return [["r", this.url]]; } addValidatedEvent() { this.validatedEventCount++; } addNonValidatedEvent() { this.nonValidatedEventCount++; } /** * The current validation ratio this relay has achieved. */ get validationRatio() { if (this.nonValidatedEventCount === 0) { return 1; } return this.validatedEventCount / (this.validatedEventCount + this.nonValidatedEventCount); } shouldValidateEvent() { if (this.trusted) { return false; } if (this.targetValidationRatio === void 0) { return true; } if (this.targetValidationRatio >= 1) return true; return Math.random() < this.targetValidationRatio; } get connected() { return this.connectivity.connected; } /** * Registers a protocol handler for a specific message type. * This allows external packages to handle custom relay messages (e.g., NIP-77 NEG-* messages). * * @param messageType The message type to handle (e.g., "NEG-OPEN", "NEG-MSG") * @param handler The function to call when a message of this type is received * * @example * ```typescript * relay.registerProtocolHandler('NEG-MSG', (relay, message) => { * console.log('Received NEG-MSG:', message); * }); * ``` */ registerProtocolHandler(messageType, handler) { this.protocolHandlers.set(messageType, handler); } /** * Unregisters a protocol handler for a specific message type. * * @param messageType The message type to stop handling */ unregisterProtocolHandler(messageType) { this.protocolHandlers.delete(messageType); } /** * Checks if a protocol handler is registered for a message type. * This is used internally by the connectivity layer to route messages. * * @internal * @param messageType The message type to check * @returns The handler function if registered, undefined otherwise */ getProtocolHandler(messageType) { return this.protocolHandlers.get(messageType); } /** * Fetches relay information (NIP-11) from the relay. * Results are cached in persistent storage when cache adapter is available (24-hour TTL). * Falls back to in-memory cache. Pass force=true to bypass all caches. * * @param force Force a fresh fetch, bypassing all caches * @returns The relay information document * @throws Error if the fetch fails * * @example * ```typescript * const info = await relay.fetchInfo(); * console.log(`Relay: ${info.name}`); * console.log(`Supported NIPs: ${info.supported_nips?.join(', ')}`); * ``` */ async fetchInfo(force = false) { const MAX_AGE = 864e5; const ndk = this.connectivity.ndk; if (!force && ndk?.cacheAdapter?.getRelayStatus) { const cached = await ndk.cacheAdapter.getRelayStatus(this.url); if (cached?.nip11 && Date.now() - cached.nip11.fetchedAt < MAX_AGE) { this._relayInfo = cached.nip11.data; return cached.nip11.data; } } if (!force && this._relayInfo) { return this._relayInfo; } this._relayInfo = await fetchRelayInformation(this.url); if (ndk?.cacheAdapter?.updateRelayStatus) { await ndk.cacheAdapter.updateRelayStatus(this.url, { nip11: { data: this._relayInfo, fetchedAt: Date.now() } }); } return this._relayInfo; } /** * Returns cached relay information if available, undefined otherwise. * Use fetchInfo() to retrieve fresh information. */ get info() { return this._relayInfo; } }; __publicField(_NDKRelay, "defaultValidationRatioUpdateFn", (relay, validatedCount, _nonValidatedCount) => { if (relay.lowestValidationRatio === void 0 || relay.targetValidationRatio === void 0) return 1; let newRatio = relay.validationRatio; if (relay.validationRatio > relay.targetValidationRatio) { const factor = validatedCount / 100; newRatio = Math.max(relay.lowestValidationRatio, relay.validationRatio - factor); } if (newRatio < relay.validationRatio) { return newRatio; } return relay.validationRatio; }); var NDKRelay = _NDKRelay; // ndk/core/src/relay/sets/index.ts var NDKPublishError = class extends Error { constructor(message, errors, publishedToRelays, intendedRelaySet) { super(message); __publicField(this, "errors"); __publicField(this, "publishedToRelays"); /** * Intended relay set where the publishing was intended to happen. */ __publicField(this, "intendedRelaySet"); this.errors = errors; this.publishedToRelays = publishedToRelays; this.intendedRelaySet = intendedRelaySet; } get relayErrors() { const errors = []; for (const [relay, err] of this.errors) { errors.push(`${relay.url}: ${err}`); } return errors.join("\n"); } }; var NDKRelaySet = class _NDKRelaySet3 { constructor(relays, ndk, pool) { __publicField(this, "relays"); __publicField(this, "debug"); __publicField(this, "ndk"); __publicField(this, "pool"); this.relays = relays; this.ndk = ndk; this.pool = pool ?? ndk.pool; this.debug = ndk.debug.extend("relayset"); } /** * Adds a relay to this set. */ addRelay(relay) { this.relays.add(relay); } get relayUrls() { return Array.from(this.relays).map((r) => r.url); } /** * Creates a relay set from a list of relay URLs. * * If no connection to the relay is found in the pool it will temporarily * connect to it. * * @param relayUrls - list of relay URLs to include in this set * @param ndk * @param connect - whether to connect to the relay immediately if it was already in the pool but not connected * @returns NDKRelaySet */ static fromRelayUrls(relayUrls, ndk, connect = true, pool) { pool = pool ?? ndk.pool; if (!pool) throw new Error("No pool provided"); const relays = /* @__PURE__ */ new Set(); for (const url of relayUrls) { const relay = pool.relays.get(normalizeRelayUrl(url)); if (relay) { if (relay.status < 5 /* CONNECTED */ && connect) { relay.connect(); } relays.add(relay); } else { const temporaryRelay = new NDKRelay(normalizeRelayUrl(url), ndk?.relayAuthDefaultPolicy, ndk); pool.useTemporaryRelay(temporaryRelay, void 0, `requested from fromRelayUrls ${relayUrls}`); relays.add(temporaryRelay); } } return new _NDKRelaySet3(new Set(relays), ndk, pool); } /** * Publish an event to all relays in this relay set. * * This method implements a robust mechanism for publishing events to multiple relays with * built-in handling for race conditions, timeouts, and partial failures. The implementation * uses a dual-tracking mechanism to ensure accurate reporting of which relays successfully * received an event. * * Key aspects of this implementation: * * 1. DUAL-TRACKING MECHANISM: * - Promise-based tracking: Records successes/failures from the promises returned by relay.publish() * - Event-based tracking: Listens for 'relay:published' events that indicate successful publishing * This approach ensures we don't miss successful publishes even if there are subsequent errors in * the promise chain. * * 2. RACE CONDITION HANDLING: * - If a relay emits a success event but later fails in the promise chain, we still count it as a success * - If a relay times out after successfully publishing, we still count it as a success * - All relay operations happen in parallel, with proper tracking regardless of completion order * * 3. TIMEOUT MANAGEMENT: * - Individual timeouts for each relay operation * - Proper cleanup of timeouts to prevent memory leaks * - Clear timeout error reporting * * 4. ERROR HANDLING: * - Detailed tracking of specific errors for each failed relay * - Special handling for ephemeral events (which don't expect acknowledgement) * - RequiredRelayCount parameter to control the minimum success threshold * * @param event Event to publish * @param timeoutMs Timeout in milliseconds for each relay publish operation * @param requiredRelayCount The minimum number of relays we expect the event to be published to * @returns A set of relays the event was published to * @throws {NDKPublishError} If the event could not be published to at least `requiredRelayCount` relays * @example * ```typescript * const relaySet = new NDKRelaySet(new Set([relay1, relay2]), ndk); * const publishedToRelays = await relaySet.publish(event); * // publishedToRelays can contain relay1, relay2, both, or none * // depending on which relays the event was successfully published to * if (publishedToRelays.size > 0) { * console.log("Event published to at least one relay"); * } * ``` */ async publish(event, timeoutMs, requiredRelayCount = 1) { const publishedToRelays = /* @__PURE__ */ new Set(); const errors = /* @__PURE__ */ new Map(); const isEphemeral4 = event.isEphemeral(); event.publishStatus = "pending"; const relayPublishedHandler = (relay) => { publishedToRelays.add(relay); }; event.on("relay:published", relayPublishedHandler); try { const promises = Array.from(this.relays).map((relay) => { return new Promise((resolve) => { const timeoutId = timeoutMs ? setTimeout(() => { if (!publishedToRelays.has(relay)) { errors.set(relay, new Error(`Publish timeout after ${timeoutMs}ms`)); resolve(false); } }, timeoutMs) : null; relay.publish(event, timeoutMs).then((success) => { if (timeoutId) clearTimeout(timeoutId); if (success) { publishedToRelays.add(relay); resolve(true); } else { resolve(false); } }).catch((err) => { if (timeoutId) clearTimeout(timeoutId); if (!isEphemeral4) { errors.set(relay, err); } resolve(false); }); }); }); await Promise.all(promises); if (publishedToRelays.size < requiredRelayCount) { if (!isEphemeral4) { const error = new NDKPublishError( "Not enough relays received the event (" + publishedToRelays.size + " published, " + requiredRelayCount + " required)", errors, publishedToRelays, this ); event.publishStatus = "error"; event.publishError = error; this.ndk?.emit("event:publish-failed", event, error, this.relayUrls); throw error; } } else { event.publishStatus = "success"; event.emit("published", { relaySet: this, publishedToRelays }); } return publishedToRelays; } finally { event.off("relay:published", relayPublishedHandler); } } get size() { return this.relays.size; } }; // ndk/core/src/relay/sets/calculate.ts var d = (0, import_debug3.default)("ndk:outbox:calculate"); async function calculateRelaySetFromEvent(ndk, event, requiredRelayCount) { const relays = /* @__PURE__ */ new Set(); const authorWriteRelays = await getWriteRelaysFor(ndk, event.pubkey); if (authorWriteRelays) { authorWriteRelays.forEach((relayUrl) => { const relay = ndk.pool?.getRelay(relayUrl); if (relay) relays.add(relay); }); } let relayHints = event.tags.filter((tag) => ["a", "e"].includes(tag[0])).map((tag) => tag[2]).filter((url) => url?.startsWith("wss://")).filter((url) => { try { new URL(url); return true; } catch { return false; } }).map((url) => normalizeRelayUrl(url)); relayHints = Array.from(new Set(relayHints)).slice(0, 5); relayHints.forEach((relayUrl) => { const relay = ndk.pool?.getRelay(relayUrl, true, true); if (relay) { d("Adding relay hint %s", relayUrl); relays.add(relay); } }); const pTags = event.getMatchingTags("p").map((tag) => tag[1]); if (pTags.length < 5) { const pTaggedRelays = Array.from( chooseRelayCombinationForPubkeys(ndk, pTags, "read", { preferredRelays: new Set(authorWriteRelays) }).keys() ); pTaggedRelays.forEach((relayUrl) => { const relay = ndk.pool?.getRelay(relayUrl, false, true); if (relay) { d("Adding p-tagged relay %s", relayUrl); relays.add(relay); } }); } else { d("Too many p-tags to consider %d", pTags.length); } ndk.pool?.permanentAndConnectedRelays().forEach((relay) => relays.add(relay)); if (requiredRelayCount && relays.size < requiredRelayCount) { const explicitRelays = ndk.explicitRelayUrls?.filter((url) => !Array.from(relays).some((r) => r.url === url)).slice(0, requiredRelayCount - relays.size); explicitRelays?.forEach((url) => { const relay = ndk.pool?.getRelay(url, false, true); if (relay) { d("Adding explicit relay %s", url); relays.add(relay); } }); } return new NDKRelaySet(relays, ndk); } function calculateRelaySetsFromFilter(ndk, filters, pool, relayGoalPerAuthor) { const result = /* @__PURE__ */ new Map(); const authors = /* @__PURE__ */ new Set(); filters.forEach((filter) => { if (filter.authors) { filter.authors.forEach((author) => authors.add(author)); } }); if (authors.size > 0) { const authorToRelaysMap = getRelaysForFilterWithAuthors(ndk, Array.from(authors), relayGoalPerAuthor); for (const relayUrl of authorToRelaysMap.keys()) { result.set(relayUrl, []); } for (const filter of filters) { if (filter.authors) { for (const [relayUrl, authors2] of authorToRelaysMap.entries()) { const authorFilterAndRelayPubkeyIntersection = filter.authors.filter( (author) => authors2.includes(author) ); result.set(relayUrl, [ ...result.get(relayUrl), { ...filter, // Overwrite authors sent to this relay with the authors that were // present in the filter and are also present in the relay authors: authorFilterAndRelayPubkeyIntersection } ]); } } else { for (const relayUrl of authorToRelaysMap.keys()) { result.set(relayUrl, [...result.get(relayUrl), filter]); } } } } else { if (ndk.explicitRelayUrls) { ndk.explicitRelayUrls.forEach((relayUrl) => { result.set(relayUrl, filters); }); } } if (result.size === 0) { pool.permanentAndConnectedRelays().slice(0, 5).forEach((relay) => { result.set(relay.url, filters); }); } return result; } function calculateRelaySetsFromFilters(ndk, filters, pool, relayGoalPerAuthor) { const a = calculateRelaySetsFromFilter(ndk, filters, pool, relayGoalPerAuthor); return a; } // ndk/core/src/utils/validation.ts function isValidHex64(value) { if (typeof value !== "string" || value.length !== 64) { return false; } for (let i3 = 0; i3 < 64; i3++) { const c = value.charCodeAt(i3); if (!(c >= 48 && c <= 57 || c >= 97 && c <= 102 || c >= 65 && c <= 70)) { return false; } } return true; } function isValidPubkey(pubkey) { return isValidHex64(pubkey); } function isValidEventId(id) { return isValidHex64(id); } function isValidNip05(input) { if (typeof input !== "string") { return false; } for (let i3 = 0; i3 < input.length; i3++) { if (input.charCodeAt(i3) === 46) { return true; } } return false; } // ndk/core/src/events/content-tagger.ts var import_nostr_tools = __toESM(require_nostr_tools()); function mergeTags(tags1, tags2) { const tagMap = /* @__PURE__ */ new Map(); const generateKey = (tag) => tag.join(","); const isContained = (smaller, larger) => { return smaller.every((value, index) => value === larger[index]); }; const processTag = (tag) => { for (const [key, existingTag] of tagMap) { if (isContained(existingTag, tag) || isContained(tag, existingTag)) { if (tag.length >= existingTag.length) { tagMap.set(key, tag); } return; } } tagMap.set(generateKey(tag), tag); }; tags1.concat(tags2).forEach(processTag); return Array.from(tagMap.values()); } function uniqueTag(a, b) { const aLength = a.length; const bLength = b.length; const sameLength = aLength === bLength; if (sameLength) { if (a.every((v6, i3) => v6 === b[i3])) { return [a]; } return [a, b]; } if (aLength > bLength && a.every((v6, i3) => v6 === b[i3])) { return [a]; } if (bLength > aLength && b.every((v6, i3) => v6 === a[i3])) { return [b]; } return [a, b]; } var hashtagRegex = /(?<=\s|^)(#[^\s!@#$%^&*()=+./,[{\]};:'"?><]+)/g; function generateHashtags(content) { const hashtags = content.match(hashtagRegex); const tagIds = /* @__PURE__ */ new Set(); const tag = /* @__PURE__ */ new Set(); if (hashtags) { for (const hashtag of hashtags) { if (tagIds.has(hashtag.slice(1))) continue; tag.add(hashtag.slice(1)); tagIds.add(hashtag.slice(1)); } } return Array.from(tag); } async function generateContentTags(content, tags = [], opts, ctx) { if (opts?.skipContentTagging) { return { content, tags }; } const tagRegex = /(@|nostr:)(npub|nprofile|note|nevent|naddr)[a-zA-Z0-9]+/g; const promises = []; const addTagIfNew = (t) => { if (!tags.find((t2) => ["q", t[0]].includes(t2[0]) && t2[1] === t[1])) { tags.push(t); } }; content = content.replace(tagRegex, (tag) => { try { const entity = tag.split(/(@|nostr:)/)[2]; const { type, data } = import_nostr_tools.nip19.decode(entity); let t; if (opts?.filters) { const shouldInclude = !opts.filters.includeTypes || opts.filters.includeTypes.includes(type); const shouldExclude = opts.filters.excludeTypes?.includes(type); if (!shouldInclude || shouldExclude) { return tag; } } switch (type) { case "npub": if (opts?.pTags !== false) { t = ["p", data]; } break; case "nprofile": if (opts?.pTags !== false) { t = ["p", data.pubkey]; } break; case "note": promises.push( new Promise(async (resolve) => { const relay = await maybeGetEventRelayUrl(entity); addTagIfNew(["q", data, relay]); resolve(); }) ); break; case "nevent": promises.push( new Promise(async (resolve) => { const { id, author } = data; let { relays } = data; if (!relays || relays.length === 0) { relays = [await maybeGetEventRelayUrl(entity)]; } addTagIfNew(["q", id, relays[0]]); if (author && opts?.pTags !== false && opts?.pTagOnQTags !== false) addTagIfNew(["p", author]); resolve(); }) ); break; case "naddr": promises.push( new Promise(async (resolve) => { const id = [data.kind, data.pubkey, data.identifier].join(":"); let relays = data.relays ?? []; if (relays.length === 0) { relays = [await maybeGetEventRelayUrl(entity)]; } addTagIfNew(["q", id, relays[0]]); if (opts?.pTags !== false && opts?.pTagOnQTags !== false && opts?.pTagOnATags !== false) addTagIfNew(["p", data.pubkey]); resolve(); }) ); break; default: return tag; } if (t) addTagIfNew(t); return `nostr:${entity}`; } catch (_error) { return tag; } }); await Promise.all(promises); if (!opts?.filters?.excludeTypes?.includes("hashtag")) { const newTags = generateHashtags(content).map((hashtag) => ["t", hashtag]); tags = mergeTags(tags, newTags); } if (opts?.pTags !== false && opts?.copyPTagsFromTarget && ctx) { const pTags = ctx.getMatchingTags("p"); for (const pTag of pTags) { if (!pTag[1] || !isValidPubkey(pTag[1])) continue; if (!tags.find((t) => t[0] === "p" && t[1] === pTag[1])) { tags.push(pTag); } } } return { content, tags }; } async function maybeGetEventRelayUrl(_nip19Id) { return ""; } // ndk/core/src/events/encryption.ts async function encrypt(recipient, signer, scheme = "nip44") { let encrypted; if (!this.ndk) throw new Error("No NDK instance found!"); let currentSigner = signer; if (!currentSigner) { this.ndk.assertSigner(); currentSigner = this.ndk.signer; } if (!currentSigner) throw new Error("no NDK signer"); const currentRecipient = recipient || (() => { const pTags = this.getMatchingTags("p"); if (pTags.length !== 1) { throw new Error("No recipient could be determined and no explicit recipient was provided"); } return this.ndk.getUser({ pubkey: pTags[0][1] }); })(); if (scheme === "nip44" && await isEncryptionEnabled(currentSigner, "nip44")) { encrypted = await currentSigner.encrypt(currentRecipient, this.content, "nip44"); } if ((!encrypted || scheme === "nip04") && await isEncryptionEnabled(currentSigner, "nip04")) { encrypted = await currentSigner.encrypt(currentRecipient, this.content, "nip04"); } if (!encrypted) throw new Error("Failed to encrypt event."); this.content = encrypted; } async function decrypt(sender, signer, scheme) { if (this.ndk?.cacheAdapter?.getDecryptedEvent) { const cachedEvent = await this.ndk.cacheAdapter.getDecryptedEvent(this.id); if (cachedEvent) { this.content = cachedEvent.content; return; } } let decrypted; if (!this.ndk) throw new Error("No NDK instance found!"); let currentSigner = signer; if (!currentSigner) { this.ndk.assertSigner(); currentSigner = this.ndk.signer; } if (!currentSigner) throw new Error("no NDK signer"); const currentSender = sender || this.author; if (!currentSender) throw new Error("No sender provided and no author available"); const currentScheme = scheme || (this.content.match(/\\?iv=/) ? "nip04" : "nip44"); if ((currentScheme === "nip04" || this.kind === 4) && await isEncryptionEnabled(currentSigner, "nip04") && this.content.search("\\?iv=")) { decrypted = await currentSigner.decrypt(currentSender, this.content, "nip04"); } if (!decrypted && currentScheme === "nip44" && await isEncryptionEnabled(currentSigner, "nip44")) { decrypted = await currentSigner.decrypt(currentSender, this.content, "nip44"); } if (!decrypted) throw new Error("Failed to decrypt event."); this.content = decrypted; if (this.ndk?.cacheAdapter?.addDecryptedEvent) { this.ndk.cacheAdapter.addDecryptedEvent(this.id, this); } } async function isEncryptionEnabled(signer, scheme) { if (!signer.encryptionEnabled) return false; if (!scheme) return true; return Boolean(await signer.encryptionEnabled(scheme)); } // ndk/core/src/thread/index.ts function eventsBySameAuthor(op, events) { const eventsByAuthor = /* @__PURE__ */ new Map(); eventsByAuthor.set(op.id, op); events.forEach((event) => { if (event.pubkey === op.pubkey) { eventsByAuthor.set(event.id, event); } }); return eventsByAuthor; } var hasMarkers = (event, tagType) => { return event.getMatchingTags(tagType).some((tag) => tag[3] && tag[3] !== ""); }; function eventIsReply(op, event, threadIds = /* @__PURE__ */ new Set(), tagType) { tagType ?? (tagType = op.tagType()); const tags = event.getMatchingTags(tagType); threadIds.add(op.tagId()); if (threadIds.has(event.tagId())) return false; const heedExplicitReplyMarker = () => { let eventIsTagged = false; for (const tag of tags) { if (tag[3] === "reply") return threadIds.has(tag[1]); const markerIsEmpty = tag[3] === "" || tag[3] === void 0; const markerIsRoot = tag[3] === "root"; if (tag[1] === op.tagId() && (markerIsEmpty || markerIsRoot)) { eventIsTagged = markerIsRoot ? "root" : true; } } if (!eventIsTagged) return false; if (eventIsTagged === "root") return true; }; const explicitReplyMarker = heedExplicitReplyMarker(); if (explicitReplyMarker !== void 0) return explicitReplyMarker; if (hasMarkers(event, tagType)) return false; const expectedTags = op.getMatchingTags("e").map((tag) => tag[1]); expectedTags.push(op.id); return event.getMatchingTags("e").every((tag) => expectedTags.includes(tag[1])); } function eventThreads(op, events) { const eventsByAuthor = eventsBySameAuthor(op, events); const threadEvents = events.filter((event) => eventIsPartOfThread(op, event, eventsByAuthor)); return threadEvents.sort((a, b) => a.created_at - b.created_at); } function getEventReplyId(event) { const replyTag = getReplyTag(event); if (replyTag) return replyTag[1]; const rootTag = getRootTag(event); if (rootTag) return rootTag[1]; } function isEventOriginalPost(event) { return getEventReplyId(event) === void 0; } function eventThreadIds(op, events) { const threadIds = /* @__PURE__ */ new Map(); const threadEvents = eventThreads(op, events); threadEvents.forEach((event) => threadIds.set(event.id, event)); return threadIds; } function eventReplies(op, events, threadEventIds) { threadEventIds ?? (threadEventIds = new Set(eventThreadIds(op, events).keys())); return events.filter((event) => eventIsReply(op, event, threadEventIds)); } function eventIsPartOfThread(op, event, eventsByAuthor) { if (op.pubkey !== event.pubkey) return false; const taggedEventIds = event.getMatchingTags("e").map((tag) => tag[1]); const allTaggedEventsAreByOriginalAuthor = taggedEventIds.every((id) => eventsByAuthor.has(id)); return allTaggedEventsAreByOriginalAuthor; } function eventHasETagMarkers(event) { for (const tag of event.tags) { if (tag[0] === "e" && (tag[3] ?? "").length > 0) return true; } return false; } function getRootEventId(event, searchTag) { searchTag ?? (searchTag = event.tagType()); const rootEventTag = getRootTag(event, searchTag); if (rootEventTag) return rootEventTag[1]; const replyTag = getReplyTag(event, searchTag); return replyTag?.[1]; } function getRootTag(event, searchTag) { searchTag ?? (searchTag = event.tagType()); const rootEventTag = event.tags.find(isTagRootTag); if (!rootEventTag) { if (eventHasETagMarkers(event)) return; const matchingTags = event.getMatchingTags(searchTag); if (matchingTags.length < 3) return matchingTags[0]; } return rootEventTag; } var nip22RootTags = /* @__PURE__ */ new Set(["A", "E", "I"]); var nip22ReplyTags = /* @__PURE__ */ new Set(["a", "e", "i"]); function getReplyTag(event, searchTag) { if (event.kind === 1111 /* GenericReply */) { let replyTag2; for (const tag of event.tags) { if (nip22RootTags.has(tag[0])) replyTag2 = tag; else if (nip22ReplyTags.has(tag[0])) { replyTag2 = tag; break; } } return replyTag2; } searchTag ?? (searchTag = event.tagType()); let hasMarkers2 = false; let replyTag; for (const tag of event.tags) { if (tag[0] !== searchTag) continue; if ((tag[3] ?? "").length > 0) hasMarkers2 = true; if (hasMarkers2 && tag[3] === "reply") return tag; if (hasMarkers2 && tag[3] === "root") replyTag = tag; if (!hasMarkers2) replyTag = tag; } return replyTag; } function isTagRootTag(tag) { return tag[0] === "E" || tag[3] === "root"; } // ndk/core/src/events/fetch-tagged-event.ts async function fetchTaggedEvent(tag, marker) { if (!this.ndk) throw new Error("NDK instance not found"); const t = this.getMatchingTags(tag, marker); if (t.length === 0) return void 0; const [_2, id, hint] = t[0]; const relay = hint !== "" ? this.ndk.pool.getRelay(hint) : void 0; const event = await this.ndk.fetchEvent(id, {}, relay); return event; } async function fetchRootEvent(subOpts) { if (!this.ndk) throw new Error("NDK instance not found"); const rootTag = getRootTag(this); if (!rootTag) return void 0; return this.ndk.fetchEventFromTag(rootTag, this, subOpts); } async function fetchReplyEvent(subOpts) { if (!this.ndk) throw new Error("NDK instance not found"); const replyTag = getReplyTag(this); if (!replyTag) return void 0; return this.ndk.fetchEventFromTag(replyTag, this, subOpts); } // ndk/core/src/events/kind.ts function isReplaceable() { if (this.kind === void 0) throw new Error("Kind not set"); return [0, 3].includes(this.kind) || this.kind >= 1e4 && this.kind < 2e4 || this.kind >= 3e4 && this.kind < 4e4; } function isEphemeral() { if (this.kind === void 0) throw new Error("Kind not set"); return this.kind >= 2e4 && this.kind < 3e4; } function isParamReplaceable() { if (this.kind === void 0) throw new Error("Kind not set"); return this.kind >= 3e4 && this.kind < 4e4; } // ndk/core/src/events/nip19.ts var import_nostr_tools2 = __toESM(require_nostr_tools()); var DEFAULT_RELAY_COUNT = 2; function encode(maxRelayCount = DEFAULT_RELAY_COUNT) { let relays = []; if (this.onRelays.length > 0) { relays = this.onRelays.map((relay) => relay.url); } else if (this.relay) { relays = [this.relay.url]; } if (relays.length > maxRelayCount) { relays = relays.slice(0, maxRelayCount); } if (this.isParamReplaceable()) { return import_nostr_tools2.nip19.naddrEncode({ kind: this.kind, pubkey: this.pubkey, identifier: this.replaceableDTag(), relays }); } if (relays.length > 0) { return import_nostr_tools2.nip19.neventEncode({ id: this.tagId(), relays, author: this.pubkey }); } return import_nostr_tools2.nip19.noteEncode(this.tagId()); } // ndk/core/src/events/repost.ts async function repost(publish = true, signer) { if (!signer && publish) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); signer = this.ndk.signer; } const e2 = new NDKEvent(this.ndk, { kind: getKind(this) }); if (!this.isProtected) e2.content = JSON.stringify(this.rawEvent()); e2.tag(this); if (this.kind !== 1 /* Text */) { e2.tags.push(["k", `${this.kind}`]); } if (signer) await e2.sign(signer); if (publish) await e2.publish(); return e2; } function getKind(event) { if (event.kind === 1) { return 6 /* Repost */; } return 16 /* GenericRepost */; } // ndk/core/src/events/serializer.ts function getEventDetails(event) { if ("inspect" in event && typeof event.inspect === "string") { return event.inspect; } return JSON.stringify(event); } function validateForSerialization(event) { if (typeof event.kind !== "number") { throw new Error( `Can't serialize event with invalid properties: kind (must be number, got ${typeof event.kind}). Event: ${getEventDetails(event)}` ); } if (typeof event.content !== "string") { throw new Error( `Can't serialize event with invalid properties: content (must be string, got ${typeof event.content}). Event: ${getEventDetails(event)}` ); } if (typeof event.created_at !== "number") { throw new Error( `Can't serialize event with invalid properties: created_at (must be number, got ${typeof event.created_at}). Event: ${getEventDetails(event)}` ); } if (typeof event.pubkey !== "string") { throw new Error( `Can't serialize event with invalid properties: pubkey (must be string, got ${typeof event.pubkey}). Event: ${getEventDetails(event)}` ); } if (!Array.isArray(event.tags)) { throw new Error( `Can't serialize event with invalid properties: tags (must be array, got ${typeof event.tags}). Event: ${getEventDetails(event)}` ); } for (let i3 = 0; i3 < event.tags.length; i3++) { const tag = event.tags[i3]; if (!Array.isArray(tag)) { throw new Error( `Can't serialize event with invalid properties: tags[${i3}] (must be array, got ${typeof tag}). Event: ${getEventDetails(event)}` ); } for (let j2 = 0; j2 < tag.length; j2++) { if (typeof tag[j2] !== "string") { throw new Error( `Can't serialize event with invalid properties: tags[${i3}][${j2}] (must be string, got ${typeof tag[j2]}). Event: ${getEventDetails(event)}` ); } } } } function serialize(includeSig = false, includeId = false) { validateForSerialization(this); const payload = [0, this.pubkey, this.created_at, this.kind, this.tags, this.content]; if (includeSig) payload.push(this.sig); if (includeId) payload.push(this.id); return JSON.stringify(payload); } function deserialize(serializedEvent) { const eventArray = JSON.parse(serializedEvent); const ret = { pubkey: eventArray[1], created_at: eventArray[2], kind: eventArray[3], tags: eventArray[4], content: eventArray[5] }; if (eventArray.length >= 7) { const first = eventArray[6]; const second = eventArray[7]; if (first && first.length === 128) { ret.sig = first; if (second && second.length === 64) { ret.id = second; } } else if (first && first.length === 64) { ret.id = first; if (second && second.length === 128) { ret.sig = second; } } } return ret; } // ndk/core/src/events/validation.ts init_secp256k1(); init_sha256(); init_utils(); var import_typescript_lru_cache = __toESM(require_dist()); // ndk/core/src/events/signature.ts var worker; var processingQueue = {}; function signatureVerificationInit(w2) { worker = w2; worker.onmessage = (msg) => { if (!Array.isArray(msg.data) || msg.data.length !== 2) { console.error( "[NDK] \u274C Signature verification worker received incompatible message format.", "\n\n\u{1F4CB} Expected format: [eventId, boolean]", "\n\u{1F4E6} Received:", msg.data, "\n\n\u{1F50D} This likely means:", "\n 1. You have a STALE worker.js file that needs updating", "\n 2. Version mismatch between @nostr-dev-kit/ndk and deployed worker", "\n 3. Wrong worker is being used for signature verification", "\n\n\u2705 Solution: Update your worker files:", "\n cp node_modules/@nostr-dev-kit/ndk/dist/workers/sig-verification.js public/", "\n cp node_modules/@nostr-dev-kit/cache-sqlite-wasm/dist/worker.js public/", "\n\n\u{1F4A1} Or use Vite/bundler imports instead of static files:", '\n import SigWorker from "@nostr-dev-kit/ndk/workers/sig-verification?worker"' ); return; } const [eventId, result] = msg.data; const record = processingQueue[eventId]; if (!record) { console.error("No record found for event", eventId); return; } delete processingQueue[eventId]; for (const resolve of record.resolves) { resolve(result); } }; } async function verifySignatureAsync(event, _persist, relay) { const ndkInstance = event.ndk; const start = Date.now(); let result; if (ndkInstance.signatureVerificationFunction) { result = await ndkInstance.signatureVerificationFunction(event); } else { result = await new Promise((resolve) => { const serialized = event.serialize(); let enqueue = false; if (!processingQueue[event.id]) { processingQueue[event.id] = { event, resolves: [], relay }; enqueue = true; } processingQueue[event.id].resolves.push(resolve); if (!enqueue) return; worker?.postMessage({ serialized, id: event.id, sig: event.sig, pubkey: event.pubkey }); }); } ndkInstance.signatureVerificationTimeMs += Date.now() - start; return result; } // ndk/core/src/events/validation.ts var PUBKEY_REGEX = /^[a-f0-9]{64}$/; function validate() { if (typeof this.kind !== "number") return false; if (typeof this.content !== "string") return false; if (typeof this.created_at !== "number") return false; if (typeof this.pubkey !== "string") return false; if (!this.pubkey.match(PUBKEY_REGEX)) return false; if (!Array.isArray(this.tags)) return false; for (let i3 = 0; i3 < this.tags.length; i3++) { const tag = this.tags[i3]; if (!Array.isArray(tag)) return false; for (let j2 = 0; j2 < tag.length; j2++) { if (typeof tag[j2] === "object") return false; } } return true; } var verifiedSignatures = new import_typescript_lru_cache.LRUCache({ maxSize: 1e3, entryExpirationTimeInMS: 6e4 }); function verifySignature(persist) { if (typeof this.signatureVerified === "boolean") return this.signatureVerified; const prevVerification = verifiedSignatures.get(this.id); if (prevVerification !== null) { this.signatureVerified = !!prevVerification; return this.signatureVerified; } try { if (this.ndk?.asyncSigVerification) { const relayForVerification = this.relay; verifySignatureAsync(this, persist, relayForVerification).then((result) => { if (persist) { this.signatureVerified = result; if (result) verifiedSignatures.set(this.id, this.sig); } if (!result) { if (relayForVerification) { this.ndk?.reportInvalidSignature(this, relayForVerification); } else { this.ndk?.reportInvalidSignature(this); } verifiedSignatures.set(this.id, false); } else { if (relayForVerification) { relayForVerification.addValidatedEvent(); } } }).catch((err) => { console.error("signature verification error", this.id, err); }); } else { const hash3 = sha2562(new TextEncoder().encode(this.serialize())); const res = schnorr.verify(this.sig, hash3, this.pubkey); if (res) verifiedSignatures.set(this.id, this.sig); else verifiedSignatures.set(this.id, false); this.signatureVerified = res; return res; } } catch (_err) { this.signatureVerified = false; return false; } } function getEventHash() { return getEventHashFromSerializedEvent(this.serialize()); } function getEventHashFromSerializedEvent(serializedEvent) { const eventHash = sha2562(new TextEncoder().encode(serializedEvent)); return bytesToHex(eventHash); } // ndk/core/src/events/index.ts var skipClientTagOnKinds = /* @__PURE__ */ new Set([ 0 /* Metadata */, 4 /* EncryptedDirectMessage */, 1059 /* GiftWrap */, 13 /* GiftWrapSeal */, 3 /* Contacts */, 9734 /* ZapRequest */, 5 /* EventDeletion */ ]); var NDKEvent = class _NDKEvent3 extends import_tseep2.EventEmitter { constructor(ndk, event) { super(); __publicField(this, "ndk"); __publicField(this, "created_at"); __publicField(this, "content", ""); __publicField(this, "tags", []); __publicField(this, "kind"); __publicField(this, "id", ""); __publicField(this, "sig"); __publicField(this, "pubkey", ""); __publicField(this, "signatureVerified"); __publicField(this, "_author"); /** * The relay that this event was first received from. */ __publicField(this, "relay"); /** * The status of the publish operation. */ __publicField(this, "publishStatus", "success"); __publicField(this, "publishError"); __publicField(this, "serialize", serialize.bind(this)); __publicField(this, "getEventHash", getEventHash.bind(this)); __publicField(this, "validate", validate.bind(this)); __publicField(this, "verifySignature", verifySignature.bind(this)); /** * Is this event replaceable (whether parameterized or not)? * * This will return true for kind 0, 3, 10k-20k and 30k-40k */ __publicField(this, "isReplaceable", isReplaceable.bind(this)); __publicField(this, "isEphemeral", isEphemeral.bind(this)); __publicField(this, "isDvm", () => this.kind && this.kind >= 5e3 && this.kind <= 7e3); /** * Is this event parameterized replaceable? * * This will return true for kind 30k-40k */ __publicField(this, "isParamReplaceable", isParamReplaceable.bind(this)); /** * Encodes a bech32 id. * * @param relays {string[]} The relays to encode in the id * @returns {string} - Encoded naddr, note or nevent. */ __publicField(this, "encode", encode.bind(this)); __publicField(this, "encrypt", encrypt.bind(this)); __publicField(this, "decrypt", decrypt.bind(this)); /** * Fetch an event tagged with the given tag following relay hints if provided. * @param tag The tag to search for * @param marker The marker to use in the tag (e.g. "root") * @returns The fetched event or null if no event was found, undefined if no matching tag was found in the event * * @example * const replyEvent = await ndk.fetchEvent("nevent1qqs8x8vnycyha73grv380gmvlury4wtmx0nr9a5ds2dngqwgu87wn6gpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgq3ql2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqz4cwjd") * const originalEvent = await replyEvent.fetchTaggedEvent("e", "reply"); * console.log(replyEvent.encode() + " is a reply to event " + originalEvent?.encode()); */ __publicField(this, "fetchTaggedEvent", fetchTaggedEvent.bind(this)); /** * Fetch the root event of the current event. * @returns The fetched root event or null if no event was found * @example * const replyEvent = await ndk.fetchEvent("nevent1qqs8x8vnycyha73grv380gmvlury4wtmx0nr9a5ds2dngqwgu87wn6gpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgq3ql2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqz4cwjd") * const rootEvent = await replyEvent.fetchRootEvent(); * console.log(replyEvent.encode() + " is a reply in the thread " + rootEvent?.encode()); */ __publicField(this, "fetchRootEvent", fetchRootEvent.bind(this)); /** * Fetch the event the current event is replying to. * @returns The fetched reply event or null if no event was found */ __publicField(this, "fetchReplyEvent", fetchReplyEvent.bind(this)); /** * NIP-18 reposting event. * * @param publish Whether to publish the reposted event automatically @default true * @param signer The signer to use for signing the reposted event * @returns The reposted event * * @function */ __publicField(this, "repost", repost.bind(this)); this.ndk = ndk; this.created_at = event?.created_at; this.content = event?.content || ""; this.tags = event?.tags || []; this.id = event?.id || ""; this.sig = event?.sig; this.pubkey = event?.pubkey || ""; this.kind = event?.kind; if (event instanceof _NDKEvent3) { if (this.relay) { this.relay = event.relay; this.ndk?.subManager.seenEvent(event.id, this.relay); } this.publishStatus = event.publishStatus; this.publishError = event.publishError; } } /** * The relays that this event was received from and/or successfully published to. */ get onRelays() { let res = []; if (!this.ndk) { if (this.relay) res.push(this.relay); } else { res = this.ndk.subManager.seenEvents.get(this.id) || []; } return res; } /** * Deserialize an NDKEvent from a serialized payload. * @param ndk * @param event * @returns */ static deserialize(ndk, event) { return new _NDKEvent3(ndk, deserialize(event)); } /** * Returns the event as is. */ rawEvent() { return { created_at: this.created_at, content: this.content, tags: this.tags, kind: this.kind, pubkey: this.pubkey, id: this.id, sig: this.sig }; } set author(user) { var _a72; this.pubkey = user.pubkey; this._author = user; (_a72 = this._author).ndk ?? (_a72.ndk = this.ndk); } /** * Returns an NDKUser for the author of the event. */ get author() { if (this._author) return this._author; if (!this.ndk) throw new Error("No NDK instance found"); const user = this.ndk.getUser({ pubkey: this.pubkey }); this._author = user; return user; } /** * NIP-73 tagging of external entities * @param entity to be tagged * @param type of the entity * @param markerUrl to be used as the marker URL * * @example * ```typescript * event.tagExternal("https://example.com/article/123#nostr", "url"); * event.tags => [["i", "https://example.com/123"], ["k", "https://example.com"]] * ``` * * @example tag a podcast:item:guid * ```typescript * event.tagExternal("e32b4890-b9ea-4aef-a0bf-54b787833dc5", "podcast:item:guid"); * event.tags => [["i", "podcast:item:guid:e32b4890-b9ea-4aef-a0bf-54b787833dc5"], ["k", "podcast:item:guid"]] * ``` * * @see https://github.com/nostr-protocol/nips/blob/master/73.md */ tagExternal(entity, type, markerUrl) { const iTag = ["i"]; const kTag = ["k"]; switch (type) { case "url": { const url = new URL(entity); url.hash = ""; iTag.push(url.toString()); kTag.push(`${url.protocol}//${url.host}`); break; } case "hashtag": iTag.push(`#${entity.toLowerCase()}`); kTag.push("#"); break; case "geohash": iTag.push(`geo:${entity.toLowerCase()}`); kTag.push("geo"); break; case "isbn": iTag.push(`isbn:${entity.replace(/-/g, "")}`); kTag.push("isbn"); break; case "podcast:guid": iTag.push(`podcast:guid:${entity}`); kTag.push("podcast:guid"); break; case "podcast:item:guid": iTag.push(`podcast:item:guid:${entity}`); kTag.push("podcast:item:guid"); break; case "podcast:publisher:guid": iTag.push(`podcast:publisher:guid:${entity}`); kTag.push("podcast:publisher:guid"); break; case "isan": iTag.push(`isan:${entity.split("-").slice(0, 4).join("-")}`); kTag.push("isan"); break; case "doi": iTag.push(`doi:${entity.toLowerCase()}`); kTag.push("doi"); break; default: throw new Error(`Unsupported NIP-73 entity type: ${type}`); } if (markerUrl) { iTag.push(markerUrl); } this.tags.push(iTag); this.tags.push(kTag); } /** * Tag a user with an optional marker. * @param target What is to be tagged. Can be an NDKUser, NDKEvent, or an NDKTag. * @param marker The marker to use in the tag. * @param skipAuthorTag Whether to explicitly skip adding the author tag of the event. * @param forceTag Force a specific tag to be used instead of the default "e" or "a" tag. * @param opts Optional content tagging options to control p tag behavior. * @example * ```typescript * reply.tag(opEvent, "reply"); * // reply.tags => [["e", , , "reply"]] * ``` */ tag(target, marker, skipAuthorTag, forceTag, opts) { let tags = []; const isNDKUser = target.fetchProfile !== void 0; if (isNDKUser) { forceTag ?? (forceTag = "p"); if (forceTag === "p" && opts?.pTags === false) { return; } const tag = [forceTag, target.pubkey]; if (marker) tag.push(...["", marker]); tags.push(tag); } else if (target instanceof _NDKEvent3) { const event = target; skipAuthorTag ?? (skipAuthorTag = event?.pubkey === this.pubkey); tags = event.referenceTags(marker, skipAuthorTag, forceTag, opts); if (opts?.pTags !== false) { for (const pTag of event.getMatchingTags("p")) { if (!pTag[1] || !isValidPubkey(pTag[1])) continue; if (pTag[1] === this.pubkey) continue; if (this.tags.find((t) => t[0] === "p" && t[1] === pTag[1])) continue; this.tags.push(["p", pTag[1]]); } } } else if (Array.isArray(target)) { tags = [target]; } else { throw new Error("Invalid argument", target); } this.tags = mergeTags(this.tags, tags); } /** * Return a NostrEvent object, trying to fill in missing fields * when possible, adding tags when necessary. * @param pubkey {string} The pubkey of the user who the event belongs to. * @param opts {ContentTaggingOptions} Options for content tagging. * @returns {Promise} A promise that resolves to a NostrEvent. */ async toNostrEvent(pubkey, opts) { if (!pubkey && this.pubkey === "") { const user = await this.ndk?.signer?.user(); this.pubkey = user?.pubkey || ""; } if (!this.created_at) { this.created_at = Math.floor(Date.now() / 1e3); } const { content, tags } = await this.generateTags(opts); this.content = content || ""; this.tags = tags; try { this.id = this.getEventHash(); } catch (_e2) { } return this.rawEvent(); } /** * Get all tags with the given name * @param tagName {string} The name of the tag to search for * @returns {NDKTag[]} An array of the matching tags */ getMatchingTags(tagName, marker) { const t = this.tags.filter((tag) => tag[0] === tagName); if (marker === void 0) return t; return t.filter((tag) => tag[3] === marker); } /** * Check if the event has a tag with the given name * @param tagName * @param marker * @returns */ hasTag(tagName, marker) { return this.tags.some((tag) => tag[0] === tagName && (!marker || tag[3] === marker)); } /** * Get the first tag with the given name * @param tagName Tag name to search for * @returns The value of the first tag with the given name, or undefined if no such tag exists */ tagValue(tagName, marker) { const tags = this.getMatchingTags(tagName, marker); if (tags.length === 0) return void 0; return tags[0][1]; } /** * Gets the NIP-31 "alt" tag of the event. */ get alt() { return this.tagValue("alt"); } /** * Sets the NIP-31 "alt" tag of the event. Use this to set an alt tag so * clients that don't handle a particular event kind can display something * useful for users. */ set alt(alt) { this.removeTag("alt"); if (alt) this.tags.push(["alt", alt]); } /** * Gets the NIP-33 "d" tag of the event. */ get dTag() { return this.tagValue("d"); } /** * Sets the NIP-33 "d" tag of the event. */ set dTag(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } /** * Remove all tags with the given name (e.g. "d", "a", "p") * @param tagName Tag name(s) to search for and remove * @param marker Optional marker to check for too * * @example * Remove a tags with a "defer" marker * ```typescript * event.tags = [ * ["a", "....", "defer"], * ["a", "....", "no-defer"], * ] * * event.removeTag("a", "defer"); * * // event.tags => [["a", "....", "no-defer"]] * * @returns {void} */ removeTag(tagName, marker) { const tagNames = Array.isArray(tagName) ? tagName : [tagName]; this.tags = this.tags.filter((tag) => { const include = tagNames.includes(tag[0]); const hasMarker = marker ? tag[3] === marker : true; return !(include && hasMarker); }); } /** * Replace a tag with a new value. If not found, it will be added. * @param tag The tag to replace. * @param value The new value for the tag. */ replaceTag(tag) { this.removeTag(tag[0]); this.tags.push(tag); } /** * Sign the event if a signer is present. * * It will generate tags. * Repleacable events will have their created_at field set to the current time. * @param signer {NDKSigner} The NDKSigner to use to sign the event * @param opts {ContentTaggingOptions} Options for content tagging. * @returns {Promise} A Promise that resolves to the signature of the signed event. */ async sign(signer, opts) { this.ndk?.aiGuardrails?.event?.signing(this); if (!signer) { this.ndk?.assertSigner(); signer = this.ndk?.signer; } else { this.author = await signer.user(); } const nostrEvent = await this.toNostrEvent(void 0, opts); this.sig = await signer.sign(nostrEvent); return this.sig; } /** * * @param relaySet * @param timeoutMs * @param requiredRelayCount * @returns */ async publishReplaceable(relaySet, timeoutMs, requiredRelayCount) { this.id = ""; this.created_at = Math.floor(Date.now() / 1e3); this.sig = ""; return this.publish(relaySet, timeoutMs, requiredRelayCount); } /** * Attempt to sign and then publish an NDKEvent to a given relaySet. * If no relaySet is provided, the relaySet will be calculated by NDK. * @param relaySet {NDKRelaySet} The relaySet to publish the even to. * @param timeoutM {number} The timeout for the publish operation in milliseconds. * @param requiredRelayCount The number of relays that must receive the event for the publish to be considered successful. * @param opts {ContentTaggingOptions} Options for content tagging. * @returns A promise that resolves to the relays the event was published to. */ async publish(relaySet, timeoutMs, requiredRelayCount, opts) { if (!requiredRelayCount) requiredRelayCount = 1; if (!this.sig) await this.sign(void 0, opts); if (!this.ndk) throw new Error("NDKEvent must be associated with an NDK instance to publish"); this.ndk.aiGuardrails?.event?.publishing(this); if (!relaySet || relaySet.size === 0) { relaySet = this.ndk.devWriteRelaySet || await calculateRelaySetFromEvent(this.ndk, this, requiredRelayCount); } if (this.kind === 5 /* EventDeletion */ && this.ndk.cacheAdapter?.deleteEventIds) { const eTags = this.getMatchingTags("e").map((tag) => tag[1]); this.ndk.cacheAdapter.deleteEventIds(eTags); } const rawEvent = this.rawEvent(); if (this.ndk.cacheAdapter?.addUnpublishedEvent && shouldTrackUnpublishedEvent(this)) { try { this.ndk.cacheAdapter.addUnpublishedEvent(this, relaySet.relayUrls); } catch (e2) { console.error("Error adding unpublished event to cache", e2); } } if (this.kind === 5 /* EventDeletion */ && this.ndk.cacheAdapter?.deleteEventIds) { this.ndk.cacheAdapter.deleteEventIds(this.getMatchingTags("e").map((tag) => tag[1])); } this.ndk.subManager.dispatchEvent(rawEvent, void 0, true); const relays = await relaySet.publish(this, timeoutMs, requiredRelayCount); relays.forEach((relay) => this.ndk?.subManager.seenEvent(this.id, relay)); return relays; } /** * Generates tags for users, notes, and other events tagged in content. * Will also generate random "d" tag for parameterized replaceable events where needed. * @param opts {ContentTaggingOptions} Options for content tagging. * @returns {ContentTag} The tags and content of the event. */ async generateTags(opts) { let tags = []; const g = await generateContentTags(this.content, this.tags, opts, this); const content = g.content; tags = g.tags; if (this.kind && this.isParamReplaceable()) { const dTag = this.getMatchingTags("d")[0]; if (!dTag) { const title = this.tagValue("title"); const randLength = title ? 6 : 16; let str = [...Array(randLength)].map(() => Math.random().toString(36)[2]).join(""); if (title && title.length > 0) { str = `${title.replace(/[^a-z0-9]+/gi, "-").replace(/^-|-$/g, "")}-${str}`; } tags.push(["d", str]); } } if (this.shouldAddClientTag) { const clientTag = ["client", this.ndk?.clientName ?? ""]; if (this.ndk?.clientNip89) clientTag.push(this.ndk?.clientNip89); tags.push(clientTag); } else if (this.shouldStripClientTag) { tags = tags.filter((tag) => tag[0] !== "client"); } return { content: content || "", tags }; } get shouldAddClientTag() { if (!this.ndk?.clientName && !this.ndk?.clientNip89) return false; if (skipClientTagOnKinds.has(this.kind)) return false; if (this.isEphemeral()) return false; if (this.isReplaceable() && !this.isParamReplaceable()) return false; if (this.isDvm()) return false; if (this.hasTag("client")) return false; return true; } get shouldStripClientTag() { return skipClientTagOnKinds.has(this.kind); } muted() { if (this.ndk?.muteFilter && this.ndk.muteFilter(this)) { return "muted"; } return null; } /** * Returns the "d" tag of a parameterized replaceable event or throws an error if the event isn't * a parameterized replaceable event. * @returns {string} the "d" tag of the event. * * @deprecated Use `dTag` instead. */ replaceableDTag() { if (this.kind && this.kind >= 3e4 && this.kind <= 4e4) { const dTag = this.getMatchingTags("d")[0]; const dTagId = dTag ? dTag[1] : ""; return dTagId; } throw new Error("Event is not a parameterized replaceable event"); } /** * Provides a deduplication key for the event. * * For kinds 0, 3, 10k-20k this will be the event : * For kinds 30k-40k this will be the event :: * For all other kinds this will be the event id */ deduplicationKey() { if (this.kind === 0 || this.kind === 3 || this.kind && this.kind >= 1e4 && this.kind < 2e4) { return `${this.kind}:${this.pubkey}`; } return this.tagId(); } /** * Returns the id of the event or, if it's a parameterized event, the generated id of the event using "d" tag, pubkey, and kind. * @returns {string} The id */ tagId() { if (this.isParamReplaceable()) { return this.tagAddress(); } return this.id; } /** * Returns a stable reference value for a replaceable event. * * Param replaceable events are returned in the expected format of `::`. * Kind-replaceable events are returned in the format of `::`. * * @returns {string} A stable reference value for replaceable events */ tagAddress() { if (this.isParamReplaceable()) { const dTagId = this.dTag ?? ""; return `${this.kind}:${this.pubkey}:${dTagId}`; } if (this.isReplaceable()) { return `${this.kind}:${this.pubkey}:`; } throw new Error("Event is not a replaceable event"); } /** * Determines the type of tag that can be used to reference this event from another event. * @returns {string} The tag type * @example * event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] }); * event.tagType(); // "a" */ tagType() { return this.isParamReplaceable() ? "a" : "e"; } /** * Get the tag that can be used to reference this event from another event. * * Consider using referenceTags() instead (unless you have a good reason to use this) * * @example * event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] }); * event.tagReference(); // ["a", "30000:pubkey:d-code"] * * event = new NDKEvent(ndk, { kind: 1, pubkey: 'pubkey', id: "eventid" }); * event.tagReference(); // ["e", "eventid"] * @returns {NDKTag} The NDKTag object referencing this event */ tagReference(marker) { let tag; if (this.isParamReplaceable()) { tag = ["a", this.tagAddress()]; } else { tag = ["e", this.tagId()]; } if (this.relay) { tag.push(this.relay.url); } else { tag.push(""); } tag.push(marker ?? ""); if (!this.isParamReplaceable()) { tag.push(this.pubkey); } return tag; } /** * Get the tags that can be used to reference this event from another event * @param marker The marker to use in the tag * @param skipAuthorTag Whether to explicitly skip adding the author tag of the event * @param forceTag Force a specific tag to be used instead of the default "e" or "a" tag * @example * event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] }); * event.referenceTags(); // [["a", "30000:pubkey:d-code"], ["e", "parent-id"]] * * event = new NDKEvent(ndk, { kind: 1, pubkey: 'pubkey', id: "eventid" }); * event.referenceTags(); // [["e", "parent-id"]] * @returns {NDKTag} The NDKTag object referencing this event */ referenceTags(marker, skipAuthorTag, forceTag, opts) { let tags = []; if (this.isParamReplaceable()) { tags = [ [forceTag ?? "a", this.tagAddress()], [forceTag ?? "e", this.id] ]; } else { tags = [[forceTag ?? "e", this.id]]; } tags = tags.map((tag) => { if (tag[0] === "e" || marker) { tag.push(this.relay?.url ?? ""); } else if (this.relay?.url) { tag.push(this.relay?.url); } return tag; }); tags.forEach((tag) => { if (tag[0] === "e") { tag.push(marker ?? ""); tag.push(this.pubkey); } else if (marker) { tag.push(marker); } }); tags = [...tags, ...this.getMatchingTags("h")]; if (!skipAuthorTag && opts?.pTags !== false) tags.push(...this.author.referenceTags()); return tags; } /** * Provides the filter that will return matching events for this event. * * @example * event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] }); * event.filter(); // { "#a": ["30000:pubkey:d-code"] } * @example * event = new NDKEvent(ndk, { kind: 1, pubkey: 'pubkey', id: "eventid" }); * event.filter(); // { "#e": ["eventid"] } * * @returns The filter that will return matching events for this event */ filter() { if (this.isParamReplaceable()) { return { "#a": [this.tagId()] }; } return { "#e": [this.tagId()] }; } nip22Filter() { if (this.isParamReplaceable()) { return { "#A": [this.tagId()] }; } return { "#E": [this.tagId()] }; } /** * Generates a deletion event of the current event * * @param reason The reason for the deletion * @param publish Whether to publish the deletion event automatically * @returns The deletion event */ async delete(reason, publish = true) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); const e2 = new _NDKEvent3(this.ndk, { kind: 5 /* EventDeletion */, content: reason || "" }); e2.tag(this, void 0, true); e2.tags.push(["k", this.kind?.toString()]); if (publish) { this.emit("deleted"); await e2.publish(); } return e2; } /** * Establishes whether this is a NIP-70-protectede event. * @@satisfies NIP-70 */ set isProtected(val) { this.removeTag("-"); if (val) this.tags.push(["-"]); } /** * Whether this is a NIP-70-protected event. * @@satisfies NIP-70 */ get isProtected() { return this.hasTag("-"); } /** * React to an existing event * * @param content The content of the reaction */ async react(content, publish = true) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); const e2 = new _NDKEvent3(this.ndk, { kind: 7 /* Reaction */, content }); e2.tag(this); if (this.kind !== 1 /* Text */) { e2.tags.push(["k", `${this.kind}`]); } if (publish) await e2.publish(); return e2; } /** * Checks whether the event is valid per underlying NIPs. * * This method is meant to be overridden by subclasses that implement specific NIPs * to allow the enforcement of NIP-specific validation rules. * * Otherwise, it will only check for basic event properties. * */ get isValid() { return this.validate(); } get inspect() { return JSON.stringify(this.rawEvent(), null, 4); } /** * Dump the event to console for debugging purposes. * Prints a JSON stringified version of rawEvent() with indentation * and also lists all relay URLs for onRelays. */ dump() { console.debug(JSON.stringify(this.rawEvent(), null, 4)); console.debug("Event on relays:", this.onRelays.map((relay) => relay.url).join(", ")); } /** * Creates a reply event for the current event. * * This function will use NIP-22 when appropriate (i.e. replies to non-kind:1 events). * This function does not have side-effects; it will just return an event with the appropriate tags * to generate the reply event; the caller is responsible for publishing the event. * * @param forceNip22 - Optional flag to force NIP-22 style replies (kind 1111) regardless of the original event's kind * @param opts - Optional content tagging options */ reply(forceNip22, opts) { const reply = new _NDKEvent3(this.ndk); this.ndk?.aiGuardrails?.event?.creatingReply(reply); if (this.kind === 1 && !forceNip22) { reply.kind = 1; const opHasETag = this.hasTag("e"); if (opHasETag) { reply.tags = [ ...reply.tags, ...this.getMatchingTags("e"), ...this.getMatchingTags("p"), ...this.getMatchingTags("a"), ...this.referenceTags("reply", false, void 0, opts) ]; } else { reply.tag(this, "root", false, void 0, opts); } } else { reply.kind = 1111 /* GenericReply */; const carryOverTags = ["A", "E", "I", "P"]; const rootTags = this.tags.filter((tag) => carryOverTags.includes(tag[0])); if (rootTags.length > 0) { const rootKind = this.tagValue("K"); reply.tags.push(...rootTags); if (rootKind) reply.tags.push(["K", rootKind]); let tag; if (this.isParamReplaceable()) { tag = ["a", this.tagAddress()]; const relayHint = this.relay?.url ?? ""; if (relayHint) tag.push(relayHint); } else { tag = ["e", this.tagId()]; const relayHint = this.relay?.url ?? ""; tag.push(relayHint); tag.push(this.pubkey); } reply.tags.push(tag); } else { let lowerTag; let upperTag; const relayHint = this.relay?.url ?? ""; if (this.isParamReplaceable()) { lowerTag = ["a", this.tagAddress(), relayHint]; upperTag = ["A", this.tagAddress(), relayHint]; } else { lowerTag = ["e", this.tagId(), relayHint, this.pubkey]; upperTag = ["E", this.tagId(), relayHint, this.pubkey]; } reply.tags.push(lowerTag); reply.tags.push(upperTag); reply.tags.push(["K", this.kind?.toString()]); if (opts?.pTags !== false && opts?.pTagOnATags !== false) { reply.tags.push(["P", this.pubkey]); } } reply.tags.push(["k", this.kind?.toString()]); if (opts?.pTags !== false) { reply.tags.push(...this.getMatchingTags("p")); reply.tags.push(["p", this.pubkey]); } } return reply; } }; var untrackedUnpublishedEvents = /* @__PURE__ */ new Set([ 24133 /* NostrConnect */, 13194 /* NostrWaletConnectInfo */, 23194 /* NostrWalletConnectReq */, 23195 /* NostrWalletConnectRes */ ]); function shouldTrackUnpublishedEvent(event) { return !untrackedUnpublishedEvents.has(event.kind); } function isSignedEvent(event) { return !!(event.sig && event.id && event.created_at && event.created_at > 0); } function isUnsignedEvent(event) { return !isSignedEvent(event); } function assertSignedEvent(event) { if (!isSignedEvent(event)) { throw new Error("Expected signed event but event is not signed"); } } function createSignedEvent(event) { if (!isSignedEvent(event)) { throw new Error("Cannot create signed event from unsigned event"); } Object.defineProperty(event, "signed", { value: true, writable: false, enumerable: false }); return event; } // ndk/core/src/relay/pool/index.ts var import_tseep3 = __toESM(require_lib()); var NDKPool = class extends import_tseep3.EventEmitter { /** * @param relayUrls - The URLs of the relays to connect to. * @param ndk - The NDK instance. * @param opts - Options for the pool. */ constructor(relayUrls, ndk, { debug: debug15, name } = {}) { super(); // TODO: This should probably be an LRU cache __publicField(this, "_relays", /* @__PURE__ */ new Map()); __publicField(this, "status", "idle"); __publicField(this, "autoConnectRelays", /* @__PURE__ */ new Set()); __publicField(this, "debug"); __publicField(this, "temporaryRelayTimers", /* @__PURE__ */ new Map()); __publicField(this, "flappingRelays", /* @__PURE__ */ new Set()); // A map to store timeouts for each flapping relay. __publicField(this, "backoffTimes", /* @__PURE__ */ new Map()); __publicField(this, "ndk"); // System-wide disconnection detection __publicField(this, "disconnectionTimes", /* @__PURE__ */ new Map()); __publicField(this, "systemEventDetector"); __publicField(this, "_name", "unnamed"); this.debug = debug15 ?? ndk.debug.extend("pool"); if (name) this._name = name; this.ndk = ndk; this.relayUrls = relayUrls; if (this.ndk.pools) { this.ndk.pools.push(this); } } get relays() { return this._relays; } set relayUrls(urls) { this._relays.clear(); for (const relayUrl of urls) { const relay = new NDKRelay(relayUrl, void 0, this.ndk); relay.connectivity.netDebug = this.ndk.netDebug; this.addRelay(relay); } } get name() { return this._name; } set name(name) { this._name = name; this.debug = this.debug.extend(name); } /** * Adds a relay to the pool, and sets a timer to remove it if it is not used within the specified time. * @param relay - The relay to add to the pool. * @param removeIfUnusedAfter - The time in milliseconds to wait before removing the relay from the pool after it is no longer used. */ useTemporaryRelay(relay, removeIfUnusedAfter = 3e4, filters) { const relayAlreadyInPool = this.relays.has(relay.url); if (!relayAlreadyInPool) { this.addRelay(relay); this.debug("Adding temporary relay %s for filters %o", relay.url, filters); } const existingTimer = this.temporaryRelayTimers.get(relay.url); if (existingTimer) { clearTimeout(existingTimer); } if (!relayAlreadyInPool || existingTimer) { const timer = setTimeout(() => { if (this.ndk.explicitRelayUrls?.includes(relay.url)) return; this.removeRelay(relay.url); }, removeIfUnusedAfter); this.temporaryRelayTimers.set(relay.url, timer); } } /** * Adds a relay to the pool. * * @param relay - The relay to add to the pool. * @param connect - Whether or not to connect to the relay. */ addRelay(relay, connect = true) { const isAlreadyInPool = this.relays.has(relay.url); const isCustomRelayUrl = relay.url.includes("/npub1"); let reconnect = true; const relayUrl = relay.url; if (isAlreadyInPool) return; if (this.ndk.relayConnectionFilter && !this.ndk.relayConnectionFilter(relayUrl)) { this.debug(`Refusing to add relay ${relayUrl}: blocked by relayConnectionFilter`); return; } if (isCustomRelayUrl) { this.debug(`Refusing to add relay ${relayUrl}: is a filter relay`); return; } if (this.ndk.cacheAdapter?.getRelayStatus) { const infoOrPromise = this.ndk.cacheAdapter.getRelayStatus(relayUrl); const info = infoOrPromise instanceof Promise ? void 0 : infoOrPromise; if (info?.dontConnectBefore) { if (info.dontConnectBefore > Date.now()) { const delay = info.dontConnectBefore - Date.now(); this.debug(`Refusing to add relay ${relayUrl}: delayed connect for ${delay}ms`); setTimeout(() => { this.addRelay(relay, connect); }, delay); return; } reconnect = false; } } const noticeHandler = (notice) => this.emit("notice", relay, notice); const connectHandler = () => this.handleRelayConnect(relayUrl); const readyHandler = () => this.handleRelayReady(relay); const disconnectHandler = () => { this.recordDisconnection(relay); this.emit("relay:disconnect", relay); }; const flappingHandler = () => this.handleFlapping(relay); const authHandler = (challenge3) => this.emit("relay:auth", relay, challenge3); const authedHandler = () => this.emit("relay:authed", relay); relay.off("notice", noticeHandler); relay.off("connect", connectHandler); relay.off("ready", readyHandler); relay.off("disconnect", disconnectHandler); relay.off("flapping", flappingHandler); relay.off("auth", authHandler); relay.off("authed", authedHandler); relay.on("notice", noticeHandler); relay.on("connect", connectHandler); relay.on("ready", readyHandler); relay.on("disconnect", disconnectHandler); relay.on("flapping", flappingHandler); relay.on("auth", authHandler); relay.on("authed", authedHandler); relay.on("delayed-connect", (delay) => { if (this.ndk.cacheAdapter?.updateRelayStatus) { this.ndk.cacheAdapter.updateRelayStatus(relay.url, { dontConnectBefore: Date.now() + delay }); } }); this._relays.set(relayUrl, relay); if (connect) this.autoConnectRelays.add(relayUrl); if (connect && this.status === "active") { this.emit("relay:connecting", relay); relay.connect(void 0, reconnect).catch((e2) => { this.debug(`Failed to connect to relay ${relayUrl}`, e2); }); } } /** * Removes a relay from the pool. * @param relayUrl - The URL of the relay to remove. * @returns {boolean} True if the relay was removed, false if it was not found. */ removeRelay(relayUrl) { const relay = this.relays.get(relayUrl); if (relay) { relay.disconnect(); this.relays.delete(relayUrl); this.autoConnectRelays.delete(relayUrl); this.emit("relay:disconnect", relay); return true; } const existingTimer = this.temporaryRelayTimers.get(relayUrl); if (existingTimer) { clearTimeout(existingTimer); this.temporaryRelayTimers.delete(relayUrl); } return false; } /** * Checks whether a relay is already connected in the pool. */ isRelayConnected(url) { const normalizedUrl = normalizeRelayUrl(url); const relay = this.relays.get(normalizedUrl); if (!relay) return false; return relay.status === 5 /* CONNECTED */; } /** * Fetches a relay from the pool, or creates a new one if it does not exist. * * New relays will be attempted to be connected. */ getRelay(url, connect = true, temporary = false, filters) { let relay = this.relays.get(normalizeRelayUrl(url)); if (!relay) { relay = new NDKRelay(url, void 0, this.ndk); relay.connectivity.netDebug = this.ndk.netDebug; if (temporary) { this.useTemporaryRelay(relay, 3e4, filters); } else { this.addRelay(relay, connect); } } return relay; } handleRelayConnect(relayUrl) { const relay = this.relays.get(relayUrl); if (!relay) { console.error("NDK BUG: relay not found in pool", { relayUrl }); return; } this.emit("relay:connect", relay); if (this.stats().connected === this.relays.size) { this.emit("connect"); } } handleRelayReady(relay) { this.emit("relay:ready", relay); } /** * Attempts to establish a connection to each relay in the pool. * * @async * @param {number} [timeoutMs] - Optional timeout in milliseconds for each connection attempt. * @returns {Promise} A promise that resolves when all connection attempts have completed. * @throws {Error} If any of the connection attempts result in an error or timeout. */ async connect(timeoutMs) { this.status = "active"; this.debug(`Connecting to ${this.relays.size} relays${timeoutMs ? `, timeout ${timeoutMs}ms` : ""}...`); const relaysToConnect = Array.from(this.autoConnectRelays.keys()).map((url) => this.relays.get(url)).filter((relay) => !!relay); for (const relay of relaysToConnect) { if (relay.status !== 5 /* CONNECTED */ && relay.status !== 4 /* CONNECTING */) { this.emit("relay:connecting", relay); relay.connect().catch((e2) => { this.debug(`Failed to connect to relay ${relay.url}: ${e2 ?? "No reason specified"}`); }); } } const allConnected = () => relaysToConnect.every((r) => r.status === 5 /* CONNECTED */); const allConnectedPromise = new Promise((resolve) => { if (allConnected()) { resolve(); return; } const listeners = []; for (const relay of relaysToConnect) { const handler = () => { if (allConnected()) { for (let i3 = 0; i3 < relaysToConnect.length; i3++) { relaysToConnect[i3].off("connect", listeners[i3]); } resolve(); } }; listeners.push(handler); relay.on("connect", handler); } }); const timeoutPromise = typeof timeoutMs === "number" ? new Promise((resolve) => setTimeout(resolve, timeoutMs)) : new Promise(() => { }); await Promise.race([allConnectedPromise, timeoutPromise]); } checkOnFlappingRelays() { const flappingRelaysCount = this.flappingRelays.size; const totalRelays = this.relays.size; if (flappingRelaysCount / totalRelays >= 0.8) { for (const relayUrl of this.flappingRelays) { this.backoffTimes.set(relayUrl, 0); } } } /** * Records when a relay disconnects to detect system-wide events */ recordDisconnection(relay) { const now2 = Date.now(); this.disconnectionTimes.set(relay.url, now2); for (const [url, time] of this.disconnectionTimes.entries()) { if (now2 - time > 1e4) { this.disconnectionTimes.delete(url); } } this.checkForSystemWideDisconnection(); } /** * Checks if multiple relays disconnected simultaneously, indicating a system event */ checkForSystemWideDisconnection() { const now2 = Date.now(); const recentDisconnections = []; for (const time of this.disconnectionTimes.values()) { if (now2 - time < 5e3) { recentDisconnections.push(time); } } if (recentDisconnections.length > this.relays.size / 2 && this.relays.size > 1) { this.debug( `System-wide disconnection detected: ${recentDisconnections.length}/${this.relays.size} relays disconnected` ); this.handleSystemWideReconnection(); } } /** * Handles system-wide reconnection (e.g., after sleep/wake or network change) */ handleSystemWideReconnection() { if (this.systemEventDetector) { this.debug("System-wide reconnection already in progress, skipping"); return; } this.debug("Initiating system-wide reconnection with reset backoff"); this.systemEventDetector = setTimeout(() => { this.systemEventDetector = void 0; }, 1e4); for (const relay of this.relays.values()) { if (relay.connectivity) { relay.connectivity.resetReconnectionState(); if (relay.status !== 5 /* CONNECTED */ && relay.status !== 4 /* CONNECTING */) { relay.connect().catch((e2) => { this.debug(`Failed to reconnect relay ${relay.url} after system event: ${e2}`); }); } } } this.disconnectionTimes.clear(); } handleFlapping(relay) { this.debug(`Relay ${relay.url} is flapping`); let currentBackoff = this.backoffTimes.get(relay.url) || 5e3; currentBackoff = currentBackoff * 2; this.backoffTimes.set(relay.url, currentBackoff); this.debug(`Backoff time for ${relay.url} is ${currentBackoff}ms`); setTimeout(() => { this.debug(`Attempting to reconnect to ${relay.url}`); this.emit("relay:connecting", relay); relay.connect(); this.checkOnFlappingRelays(); }, currentBackoff); relay.disconnect(); this.emit("flapping", relay); } size() { return this.relays.size; } /** * Returns the status of each relay in the pool. * @returns {NDKPoolStats} An object containing the number of relays in each status. */ stats() { const stats = { total: 0, connected: 0, disconnected: 0, connecting: 0 }; for (const relay of this.relays.values()) { stats.total++; if (relay.status === 5 /* CONNECTED */) { stats.connected++; } else if (relay.status === 1 /* DISCONNECTED */) { stats.disconnected++; } else if (relay.status === 4 /* CONNECTING */) { stats.connecting++; } } return stats; } connectedRelays() { return Array.from(this.relays.values()).filter((relay) => relay.status >= 5 /* CONNECTED */); } permanentAndConnectedRelays() { return Array.from(this.relays.values()).filter( (relay) => relay.status >= 5 /* CONNECTED */ && !this.temporaryRelayTimers.has(relay.url) ); } /** * Get a list of all relay urls in the pool. */ urls() { return Array.from(this.relays.keys()); } }; // ndk/core/src/app-settings/index.ts var NDKAppSettings = class _NDKAppSettings extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "appName"); __publicField(this, "settings", {}); this.kind ?? (this.kind = 30078 /* AppSpecificData */); this.dTag ?? (this.dTag = this.appName); if (this.content.length > 0) { try { this.settings = JSON.parse(this.content); } catch (error) { console.error("Error parsing app settings", error); } } } static from(event) { return new _NDKAppSettings(event.ndk, event); } /** * Set a value for a given key. * * @param key * @param value */ set(key, value) { this.settings[key] = value; } /** * Get a value for a given key. * * @param key * @returns */ get(key) { return this.settings[key]; } async publishReplaceable(relaySet, timeoutMs, requiredRelayCount) { this.content = JSON.stringify(this.settings); return super.publishReplaceable(relaySet, timeoutMs, requiredRelayCount); } }; // ndk/core/src/events/kinds/dvm/feedback.ts var NDKDvmJobFeedbackStatus = /* @__PURE__ */ ((NDKDvmJobFeedbackStatus2) => { NDKDvmJobFeedbackStatus2["Processing"] = "processing"; NDKDvmJobFeedbackStatus2["Success"] = "success"; NDKDvmJobFeedbackStatus2["Scheduled"] = "scheduled"; NDKDvmJobFeedbackStatus2["PayReq"] = "payment_required"; return NDKDvmJobFeedbackStatus2; })(NDKDvmJobFeedbackStatus || {}); var _NDKDVMJobFeedback = class _NDKDVMJobFeedback extends NDKEvent { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 7e3 /* DVMJobFeedback */); } static async from(event) { const e2 = new _NDKDVMJobFeedback(event.ndk, event.rawEvent()); if (e2.encrypted) await e2.dvmDecrypt(); return e2; } get status() { return this.tagValue("status"); } set status(status) { this.removeTag("status"); if (status !== void 0) { this.tags.push(["status", status]); } } get encrypted() { return !!this.getMatchingTags("encrypted")[0]; } async dvmDecrypt() { await this.decrypt(); const decryptedContent = JSON.parse(this.content); this.tags.push(...decryptedContent); } }; __publicField(_NDKDVMJobFeedback, "kind", 7e3 /* DVMJobFeedback */); __publicField(_NDKDVMJobFeedback, "kinds", [7e3 /* DVMJobFeedback */]); var NDKDVMJobFeedback = _NDKDVMJobFeedback; // ndk/core/src/events/kinds/dvm/request.ts var _NDKDVMRequest = class _NDKDVMRequest extends NDKEvent { static from(event) { return new _NDKDVMRequest(event.ndk, event.rawEvent()); } set bid(msatAmount) { if (msatAmount === void 0) { this.removeTag("bid"); } else { this.tags.push(["bid", msatAmount.toString()]); } } get bid() { const v6 = this.tagValue("bid"); if (v6 === void 0) return void 0; return Number.parseInt(v6); } /** * Adds a new input to the job * @param args The arguments to the input */ addInput(...args) { this.tags.push(["i", ...args]); } /** * Adds a new parameter to the job */ addParam(...args) { this.tags.push(["param", ...args]); } set output(output4) { if (output4 === void 0) { this.removeTag("output"); } else { if (typeof output4 === "string") output4 = [output4]; this.tags.push(["output", ...output4]); } } get output() { const outputTag = this.getMatchingTags("output")[0]; return outputTag ? outputTag.slice(1) : void 0; } get params() { const paramTags = this.getMatchingTags("param"); return paramTags.map((t) => t.slice(1)); } getParam(name) { const paramTag = this.getMatchingTags("param").find((t) => t[1] === name); return paramTag ? paramTag[2] : void 0; } createFeedback(status) { const feedback = new NDKDVMJobFeedback(this.ndk); feedback.tag(this, "job"); feedback.status = status; return feedback; } /** * Enables job encryption for this event * @param dvm DVM that will receive the event * @param signer Signer to use for encryption */ async encryption(dvm, signer) { const dvmTags = ["i", "param", "output", "relays", "bid"]; const tags = this.tags.filter((t) => dvmTags.includes(t[0])); this.tags = this.tags.filter((t) => !dvmTags.includes(t[0])); this.content = JSON.stringify(tags); this.tag(dvm); this.tags.push(["encrypted"]); await this.encrypt(dvm, signer); } /** * Sets the DVM that will receive the event */ set dvm(dvm) { this.removeTag("p"); if (dvm) this.tag(dvm); } }; __publicField(_NDKDVMRequest, "kind", 5e3 /* DVMReqTextExtraction */); __publicField(_NDKDVMRequest, "kinds", [ 5e3 /* DVMReqTextExtraction */, 5001 /* DVMReqTextSummarization */, 5002 /* DVMReqTextTranslation */, 5050 /* DVMReqTextGeneration */, 5100 /* DVMReqImageGeneration */, 5250 /* DVMReqTextToSpeech */, 5300 /* DVMReqDiscoveryNostrContent */, 5301 /* DVMReqDiscoveryNostrPeople */, 5900 /* DVMReqTimestamping */, 5905 /* DVMEventSchedule */ ]); var NDKDVMRequest = _NDKDVMRequest; // ndk/core/src/events/kinds/dvm/NDKTranscriptionDVM.ts var NDKTranscriptionDVM = class _NDKTranscriptionDVM extends NDKDVMRequest { constructor(ndk, event) { super(ndk, event); this.kind = 5e3 /* DVMReqTextExtraction */; } static from(event) { return new _NDKTranscriptionDVM(event.ndk, event.rawEvent()); } /** * Returns the original source of the transcription */ get url() { const inputTags = this.getMatchingTags("i"); if (inputTags.length !== 1) { return void 0; } return inputTags[0][1]; } /** * Getter for the title tag */ get title() { return this.tagValue("title"); } /** * Setter for the title tag */ set title(value) { this.removeTag("title"); if (value) { this.tags.push(["title", value]); } } /** * Getter for the image tag */ get image() { return this.tagValue("image"); } /** * Setter for the image tag */ set image(value) { this.removeTag("image"); if (value) { this.tags.push(["image", value]); } } }; // ndk/core/src/events/kinds/dvm/result.ts var _NDKDVMJobResult = class _NDKDVMJobResult extends NDKEvent { static from(event) { return new _NDKDVMJobResult(event.ndk, event.rawEvent()); } setAmount(msat, invoice) { this.removeTag("amount"); const tag = ["amount", msat.toString()]; if (invoice) tag.push(invoice); this.tags.push(tag); } set result(result) { if (result === void 0) { this.content = ""; } else { this.content = result; } } get result() { if (this.content === "") { return void 0; } return this.content; } set status(status) { this.removeTag("status"); if (status !== void 0) { this.tags.push(["status", status]); } } get status() { return this.tagValue("status"); } get jobRequestId() { for (const eTag of this.getMatchingTags("e")) { if (eTag[2] === "job") return eTag[1]; } if (this.jobRequest) return this.jobRequest.id; return this.tagValue("e"); } set jobRequest(event) { this.removeTag("request"); if (event) { this.kind = event.kind + 1e3; this.tags.push(["request", JSON.stringify(event.rawEvent())]); this.tag(event); } } get jobRequest() { const tag = this.tagValue("request"); if (tag === void 0) { return void 0; } return new NDKEvent(this.ndk, JSON.parse(tag)); } }; __publicField(_NDKDVMJobResult, "kind", 6e3); __publicField(_NDKDVMJobResult, "kinds", [ 6e3, // DVMReqTextExtraction result 6001, // DVMReqTextSummarization result 6002, // DVMReqTextTranslation result 6050, // DVMReqTextGeneration result 6100, // DVMReqImageGeneration result 6250, // DVMReqTextToSpeech result 6300, // DVMReqDiscoveryNostrContent result 6301, // DVMReqDiscoveryNostrPeople result 6900, // DVMReqTimestamping result 6905 // DVMEventSchedule result ]); var NDKDVMJobResult = _NDKDVMJobResult; // ndk/core/src/dvm/schedule.ts function addRelays(event, relays) { const tags = []; if (!relays || relays.length === 0) { const poolRelays = event.ndk?.pool.relays; relays = poolRelays ? Object.keys(poolRelays) : void 0; } if (relays && relays.length > 0) tags.push(["relays", ...relays]); return tags; } async function dvmSchedule(events, dvm, relays, encrypted = true, waitForConfirmationForMs) { if (!Array.isArray(events)) { events = [events]; } const ndk = events[0].ndk; if (!ndk) throw new Error("NDK not set"); for (const event of events) { if (!event.sig) throw new Error("Event not signed"); if (!event.created_at) throw new Error("Event has no date"); if (!dvm) throw new Error("No DVM specified"); if (event.created_at <= Date.now() / 1e3) throw new Error("Event needs to be in the future"); } const scheduleEvent = new NDKDVMRequest(ndk, { kind: 5905 /* DVMEventSchedule */ }); for (const event of events) { scheduleEvent.addInput(JSON.stringify(event.rawEvent()), "text"); } scheduleEvent.tags.push(...addRelays(events[0], relays)); if (encrypted) { await scheduleEvent.encryption(dvm); } else { scheduleEvent.dvm = dvm; } await scheduleEvent.sign(); let res; const schedulePromise = new Promise((resolve, reject) => { if (waitForConfirmationForMs) { res = ndk.subscribe( { kinds: [5905 /* DVMEventSchedule */ + 1e3, 7e3 /* DVMJobFeedback */], ...scheduleEvent.filter() }, { groupable: false, closeOnEose: false, onEvent: async (e2) => { res?.stop(); if (e2.kind === 7e3 /* DVMJobFeedback */) { const feedback = await NDKDVMJobFeedback.from(e2); if (feedback.status === "error") { const statusTag = feedback.getMatchingTags("status"); reject(statusTag?.[2] ?? feedback); } else { resolve(feedback); } } resolve(e2); } } ); } scheduleEvent.publish().then(() => { if (!waitForConfirmationForMs) resolve(void 0); }); }); const timeoutPromise = new Promise((reject) => { setTimeout(() => { res?.stop(); reject("Timeout waiting for an answer from the DVM"); }, waitForConfirmationForMs); }); return new Promise((resolve, reject) => { if (waitForConfirmationForMs) { Promise.race([timeoutPromise, schedulePromise]).then((e2) => { resolve(e2); }).catch(reject); } else { schedulePromise.then(resolve); } }); } // ndk/core/src/events/gift-wrapping.ts var import_nostr_tools7 = __toESM(require_nostr_tools()); // ndk/core/src/signers/private-key/index.ts init_utils(); var import_nostr_tools6 = __toESM(require_nostr_tools()); var nip49 = __toESM(require_nip49()); // ndk/core/src/user/index.ts var import_nostr_tools5 = __toESM(require_nostr_tools()); // ndk/core/src/events/kinds/nutzap/mint-list.ts var _NDKCashuMintList = class _NDKCashuMintList extends NDKEvent { constructor(ndk, event) { super(ndk, event); __publicField(this, "_p2pk"); this.kind ?? (this.kind = 10019 /* CashuMintList */); } static from(event) { return new _NDKCashuMintList(event.ndk, event); } set relays(urls) { this.tags = this.tags.filter((t) => t[0] !== "relay"); for (const url of urls) { this.tags.push(["relay", url]); } } get relays() { const r = []; for (const tag of this.tags) { if (tag[0] === "relay") { r.push(tag[1]); } } return r; } set mints(urls) { this.tags = this.tags.filter((t) => t[0] !== "mint"); for (const url of urls) { this.tags.push(["mint", url]); } } get mints() { const r = []; for (const tag of this.tags) { if (tag[0] === "mint") { r.push(tag[1]); } } return Array.from(new Set(r)); } get p2pk() { if (this._p2pk) { return this._p2pk; } this._p2pk = this.tagValue("pubkey") ?? this.pubkey; return this._p2pk; } set p2pk(pubkey) { this._p2pk = pubkey; this.removeTag("pubkey"); if (pubkey) { this.tags.push(["pubkey", pubkey]); } } get relaySet() { return NDKRelaySet.fromRelayUrls(this.relays, this.ndk); } }; __publicField(_NDKCashuMintList, "kind", 10019 /* CashuMintList */); __publicField(_NDKCashuMintList, "kinds", [10019 /* CashuMintList */]); var NDKCashuMintList = _NDKCashuMintList; // ndk/core/src/subscription/index.ts var import_tseep4 = __toESM(require_lib()); // ndk/core/src/events/kinds/article.ts var _NDKArticle = class _NDKArticle extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 30023 /* Article */); } /** * Creates a NDKArticle from an existing NDKEvent. * * @param event NDKEvent to create the NDKArticle from. * @returns NDKArticle */ static from(event) { return new _NDKArticle(event.ndk, event); } /** * Getter for the article title. * * @returns {string | undefined} - The article title if available, otherwise undefined. */ get title() { return this.tagValue("title"); } /** * Setter for the article title. * * @param {string | undefined} title - The title to set for the article. */ set title(title) { this.removeTag("title"); if (title) this.tags.push(["title", title]); } /** * Getter for the article image. * * @returns {string | undefined} - The article image if available, otherwise undefined. */ get image() { return this.tagValue("image"); } /** * Setter for the article image. * * @param {string | undefined} image - The image to set for the article. */ set image(image) { this.removeTag("image"); if (image) this.tags.push(["image", image]); } get summary() { return this.tagValue("summary"); } set summary(summary) { this.removeTag("summary"); if (summary) this.tags.push(["summary", summary]); } /** * Getter for the article's publication timestamp. * * @returns {number | undefined} - The Unix timestamp of when the article was published or undefined. */ get published_at() { const tag = this.tagValue("published_at"); if (tag) { let val = Number.parseInt(tag); if (val > 1e12) { val = Math.floor(val / 1e3); } return val; } return void 0; } /** * Setter for the article's publication timestamp. * * @param {number | undefined} timestamp - The Unix timestamp to set for the article's publication date. */ set published_at(timestamp) { this.removeTag("published_at"); if (timestamp !== void 0) { this.tags.push(["published_at", timestamp.toString()]); } } /** * Generates content tags for the article. * * This method first checks and sets the publication date if not available, * and then generates content tags based on the base NDKEvent class. * * @returns {ContentTag} - The generated content tags. */ async generateTags() { super.generateTags(); if (!this.published_at) { this.published_at = this.created_at; } return super.generateTags(); } /** * Getter for the article's URL. * * @returns {string | undefined} - The article's URL if available, otherwise undefined. */ get url() { return this.tagValue("url"); } /** * Setter for the article's URL. * * @param {string | undefined} url - The URL to set for the article. */ set url(url) { if (url) { this.tags.push(["url", url]); } else { this.removeTag("url"); } } }; __publicField(_NDKArticle, "kind", 30023 /* Article */); __publicField(_NDKArticle, "kinds", [30023 /* Article */]); var NDKArticle = _NDKArticle; // ndk/core/src/events/kinds/blossom-list.ts var _NDKBlossomList = class _NDKBlossomList extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 10063 /* BlossomList */); } static from(ndkEvent) { return new _NDKBlossomList(ndkEvent.ndk, ndkEvent.rawEvent()); } /** * Returns all Blossom servers in the list */ get servers() { return this.tags.filter((tag) => tag[0] === "server").map((tag) => tag[1]); } /** * Sets the list of Blossom servers */ set servers(servers) { this.tags = this.tags.filter((tag) => tag[0] !== "server"); for (const server of servers) { this.tags.push(["server", server]); } } /** * Returns the default Blossom server (first in the list) */ get default() { const servers = this.servers; return servers.length > 0 ? servers[0] : void 0; } /** * Sets the default Blossom server by moving it to the beginning of the list */ set default(server) { if (!server) return; const currentServers = this.servers; const filteredServers = currentServers.filter((s) => s !== server); this.servers = [server, ...filteredServers]; } /** * Adds a server to the list if it doesn't already exist */ addServer(server) { if (!server) return; const currentServers = this.servers; if (!currentServers.includes(server)) { this.servers = [...currentServers, server]; } } /** * Removes a server from the list */ removeServer(server) { if (!server) return; const currentServers = this.servers; this.servers = currentServers.filter((s) => s !== server); } }; __publicField(_NDKBlossomList, "kind", 10063 /* BlossomList */); __publicField(_NDKBlossomList, "kinds", [10063 /* BlossomList */]); var NDKBlossomList = _NDKBlossomList; // ndk/core/src/events/kinds/cashu/fedimint.ts var _NDKFedimintMint = class _NDKFedimintMint extends NDKEvent { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 38173 /* FedimintMintAnnouncement */); } static async from(event) { const mint = new _NDKFedimintMint(event.ndk, event); return mint; } /** * The federation ID */ get identifier() { return this.tagValue("d"); } set identifier(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } /** * Invite codes (multiple allowed) */ get inviteCodes() { return this.getMatchingTags("u").map((t) => t[1]); } set inviteCodes(values) { this.removeTag("u"); for (const value of values) { this.tags.push(["u", value]); } } /** * Supported modules */ get modules() { return this.getMatchingTags("modules").map((t) => t[1]); } set modules(values) { this.removeTag("modules"); for (const value of values) { this.tags.push(["modules", value]); } } /** * Network (mainnet/testnet/signet/regtest) */ get network() { return this.tagValue("n"); } set network(value) { this.removeTag("n"); if (value) this.tags.push(["n", value]); } /** * Optional metadata */ get metadata() { if (!this.content) return void 0; try { return JSON.parse(this.content); } catch { return void 0; } } set metadata(value) { if (value) { this.content = JSON.stringify(value); } else { this.content = ""; } } }; __publicField(_NDKFedimintMint, "kind", 38173 /* FedimintMintAnnouncement */); __publicField(_NDKFedimintMint, "kinds", [38173 /* FedimintMintAnnouncement */]); var NDKFedimintMint = _NDKFedimintMint; // ndk/core/src/events/kinds/cashu/mint.ts var _NDKCashuMintAnnouncement = class _NDKCashuMintAnnouncement extends NDKEvent { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 38172 /* CashuMintAnnouncement */); } static async from(event) { const mint = new _NDKCashuMintAnnouncement(event.ndk, event); return mint; } /** * The mint's identifier (pubkey) */ get identifier() { return this.tagValue("d"); } set identifier(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } /** * The mint URL */ get url() { return this.tagValue("u"); } set url(value) { this.removeTag("u"); if (value) this.tags.push(["u", value]); } /** * Supported NUT protocols */ get nuts() { return this.getMatchingTags("nuts").map((t) => t[1]); } set nuts(values) { this.removeTag("nuts"); for (const value of values) { this.tags.push(["nuts", value]); } } /** * Network (mainnet/testnet/signet/regtest) */ get network() { return this.tagValue("n"); } set network(value) { this.removeTag("n"); if (value) this.tags.push(["n", value]); } /** * Optional metadata */ get metadata() { if (!this.content) return void 0; try { return JSON.parse(this.content); } catch { return void 0; } } set metadata(value) { if (value) { this.content = JSON.stringify(value); } else { this.content = ""; } } }; __publicField(_NDKCashuMintAnnouncement, "kind", 38172 /* CashuMintAnnouncement */); __publicField(_NDKCashuMintAnnouncement, "kinds", [38172 /* CashuMintAnnouncement */]); var NDKCashuMintAnnouncement = _NDKCashuMintAnnouncement; // ndk/core/src/events/kinds/cashu/mint-recommendation.ts var _NDKMintRecommendation = class _NDKMintRecommendation extends NDKEvent { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 38e3 /* EcashMintRecommendation */); } static async from(event) { const recommendation = new _NDKMintRecommendation(event.ndk, event); return recommendation; } /** * Event kind being recommended (38173 for Fedimint or 38172 for Cashu) */ get recommendedKind() { const value = this.tagValue("k"); return value ? Number(value) : void 0; } set recommendedKind(value) { this.removeTag("k"); if (value) this.tags.push(["k", value.toString()]); } /** * Identifier for the recommended mint event */ get identifier() { return this.tagValue("d"); } set identifier(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } /** * Mint connection URLs/invite codes (multiple allowed) */ get urls() { return this.getMatchingTags("u").map((t) => t[1]); } set urls(values) { this.removeTag("u"); for (const value of values) { this.tags.push(["u", value]); } } /** * Pointers to specific mint events * Returns array of {kind, identifier, relay} objects */ get mintEventPointers() { return this.getMatchingTags("a").map((t) => ({ kind: Number(t[1].split(":")[0]), identifier: t[1].split(":")[2], relay: t[2] })); } /** * Add a pointer to a specific mint event */ addMintEventPointer(kind, pubkey, identifier, relay) { const aTag = [`a`, `${kind}:${pubkey}:${identifier}`]; if (relay) aTag.push(relay); this.tags.push(aTag); } /** * Review/recommendation text */ get review() { return this.content; } set review(value) { this.content = value; } }; __publicField(_NDKMintRecommendation, "kind", 38e3 /* EcashMintRecommendation */); __publicField(_NDKMintRecommendation, "kinds", [38e3 /* EcashMintRecommendation */]); var NDKMintRecommendation = _NDKMintRecommendation; // ndk/core/src/events/kinds/classified.ts var _NDKClassified = class _NDKClassified extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 30402 /* Classified */); } /** * Creates a NDKClassified from an existing NDKEvent. * * @param event NDKEvent to create the NDKClassified from. * @returns NDKClassified */ static from(event) { return new _NDKClassified(event.ndk, event); } /** * Getter for the classified title. * * @returns {string | undefined} - The classified title if available, otherwise undefined. */ get title() { return this.tagValue("title"); } /** * Setter for the classified title. * * @param {string | undefined} title - The title to set for the classified. */ set title(title) { this.removeTag("title"); if (title) this.tags.push(["title", title]); } /** * Getter for the classified summary. * * @returns {string | undefined} - The classified summary if available, otherwise undefined. */ get summary() { return this.tagValue("summary"); } /** * Setter for the classified summary. * * @param {string | undefined} summary - The summary to set for the classified. */ set summary(summary) { this.removeTag("summary"); if (summary) this.tags.push(["summary", summary]); } /** * Getter for the classified's publication timestamp. * * @returns {number | undefined} - The Unix timestamp of when the classified was published or undefined. */ get published_at() { const tag = this.tagValue("published_at"); if (tag) { return Number.parseInt(tag); } return void 0; } /** * Setter for the classified's publication timestamp. * * @param {number | undefined} timestamp - The Unix timestamp to set for the classified's publication date. */ set published_at(timestamp) { this.removeTag("published_at"); if (timestamp !== void 0) { this.tags.push(["published_at", timestamp.toString()]); } } /** * Getter for the classified location. * * @returns {string | undefined} - The classified location if available, otherwise undefined. */ get location() { return this.tagValue("location"); } /** * Setter for the classified location. * * @param {string | undefined} location - The location to set for the classified. */ set location(location2) { this.removeTag("location"); if (location2) this.tags.push(["location", location2]); } /** * Getter for the classified price. * * @returns {NDKClassifiedPriceTag | undefined} - The classified price if available, otherwise undefined. */ get price() { const priceTag = this.tags.find((tag) => tag[0] === "price"); if (priceTag) { return { amount: Number.parseFloat(priceTag[1]), currency: priceTag[2], frequency: priceTag[3] }; } return void 0; } /** * Setter for the classified price. * * @param price - The price to set for the classified. */ set price(priceTag) { if (typeof priceTag === "string") { priceTag = { amount: Number.parseFloat(priceTag) }; } if (priceTag?.amount) { const tag = ["price", priceTag.amount.toString()]; if (priceTag.currency) tag.push(priceTag.currency); if (priceTag.frequency) tag.push(priceTag.frequency); this.tags.push(tag); } else { this.removeTag("price"); } } /** * Generates content tags for the classified. * * This method first checks and sets the publication date if not available, * and then generates content tags based on the base NDKEvent class. * * @returns {ContentTag} - The generated content tags. */ async generateTags() { super.generateTags(); if (!this.published_at) { this.published_at = this.created_at; } return super.generateTags(); } }; __publicField(_NDKClassified, "kind", 30402 /* Classified */); __publicField(_NDKClassified, "kinds", [30402 /* Classified */]); var NDKClassified = _NDKClassified; // ndk/core/src/events/kinds/drafts.ts var _NDKDraft = class _NDKDraft extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_event"); /** * Can be used to include a different pubkey as part of the draft. * This is useful when we want to make the draft a proposal for a different user to publish. */ __publicField(this, "counterparty"); this.kind ?? (this.kind = 31234 /* Draft */); } static from(event) { return new _NDKDraft(event.ndk, event); } /** * Sets an identifier (i.e. d-tag) */ set identifier(id) { this.removeTag("d"); this.tags.push(["d", id]); } get identifier() { return this.dTag; } /** * Event that is to be saved. */ set event(e2) { if (!(e2 instanceof NDKEvent)) this._event = new NDKEvent(void 0, e2); else this._event = e2; this.prepareEvent(); } /** * Marks the event as a checkpoint for another draft event. */ set checkpoint(parent) { if (parent) { this.tags.push(parent.tagReference()); this.kind = 1234 /* DraftCheckpoint */; } else { this.removeTag("a"); this.kind = 31234 /* Draft */; } } get isCheckpoint() { return this.kind === 1234 /* DraftCheckpoint */; } get isProposal() { const pTag = this.tagValue("p"); return !!pTag && pTag !== this.pubkey; } /** * Gets the event. * @param param0 * @returns NDKEvent of the draft event or null if the draft event has been deleted (emptied). */ async getEvent(signer) { if (this._event) return this._event; signer ?? (signer = this.ndk?.signer); if (!signer) throw new Error("No signer available"); if (this.content && this.content.length > 0) { try { const ownPubkey = signer.pubkey; const pubkeys = [this.tagValue("p"), this.pubkey].filter(Boolean); const counterpartyPubkey = pubkeys.find((pubkey) => pubkey !== ownPubkey); let user; user = new NDKUser({ pubkey: counterpartyPubkey ?? ownPubkey }); await this.decrypt(user, signer); const payload = JSON.parse(this.content); this._event = await wrapEvent(new NDKEvent(this.ndk, payload)); return this._event; } catch (e2) { console.error(e2); return void 0; } } else { return null; } } prepareEvent() { if (!this._event) throw new Error("No event has been provided"); this.removeTag("k"); if (this._event.kind) this.tags.push(["k", this._event.kind.toString()]); this.content = JSON.stringify(this._event.rawEvent()); } /** * Generates draft event. * * @param signer: Optional signer to encrypt with * @param publish: Whether to publish, optionally specifying relaySet to publish to */ async save({ signer, publish, relaySet }) { signer ?? (signer = this.ndk?.signer); if (!signer) throw new Error("No signer available"); const user = this.counterparty || await signer.user(); await this.encrypt(user, signer); if (this.counterparty) { const pubkey = this.counterparty.pubkey; this.removeTag("p"); this.tags.push(["p", pubkey]); } if (publish === false) return; return this.publishReplaceable(relaySet); } }; __publicField(_NDKDraft, "kind", 31234 /* Draft */); __publicField(_NDKDraft, "kinds", [31234 /* Draft */, 1234 /* DraftCheckpoint */]); var NDKDraft = _NDKDraft; // ndk/core/src/utils/imeta.ts function mapImetaTag(tag) { const data = {}; if (tag.length === 2) { const parts = tag[1].split(" "); for (let i3 = 0; i3 < parts.length; i3 += 2) { const key = parts[i3]; const value = parts[i3 + 1]; if (key === "fallback") { if (!data.fallback) data.fallback = []; data.fallback.push(value); } else { data[key] = value; } } return data; } const tags = tag.slice(1); for (const val of tags) { const parts = val.split(" "); const key = parts[0]; const value = parts.slice(1).join(" "); if (key === "fallback") { if (!data.fallback) data.fallback = []; data.fallback.push(value); } else { data[key] = value; } } return data; } function imetaTagToTag(imeta) { const tag = ["imeta"]; for (const [key, value] of Object.entries(imeta)) { if (Array.isArray(value)) { for (const v6 of value) { tag.push(`${key} ${v6}`); } } else if (value) { tag.push(`${key} ${value}`); } } return tag; } // ndk/core/src/events/kinds/follow-pack.ts var _NDKFollowPack = class _NDKFollowPack extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 39089 /* FollowPack */); } /** * Converts a generic NDKEvent to an NDKFollowPack. */ static from(ndkEvent) { return new _NDKFollowPack(ndkEvent.ndk, ndkEvent); } /** * Gets the title from the tags. */ get title() { return this.tagValue("title"); } /** * Sets the title tag. */ set title(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } /** * Gets the image URL from the tags. */ /** * Gets the image URL from the tags. * Looks for an imeta tag first (returns its url), then falls back to the image tag. */ get image() { const imetaTag = this.tags.find((tag) => tag[0] === "imeta"); if (imetaTag) { const imeta = mapImetaTag(imetaTag); if (imeta.url) return imeta.url; } return this.tagValue("image"); } /** * Sets the image URL tag. */ /** * Sets the image tag. * Accepts a string (URL) or an NDKImetaTag. * If given an NDKImetaTag, sets both the imeta tag and the image tag (using the url). * If undefined, removes both tags. */ set image(value) { this.tags = this.tags.filter((tag) => tag[0] !== "imeta" && tag[0] !== "image"); if (typeof value === "string") { if (value !== void 0) { this.tags.push(["image", value]); } } else if (value && typeof value === "object") { this.tags.push(imetaTagToTag(value)); if (value.url) { this.tags.push(["image", value.url]); } } } /** * Gets all pubkeys from p tags. */ get pubkeys() { return Array.from( new Set(this.tags.filter((tag) => tag[0] === "p" && tag[1] && isValidPubkey(tag[1])).map((tag) => tag[1])) ); } /** * Sets the pubkeys (replaces all p tags). */ set pubkeys(pubkeys) { this.tags = this.tags.filter((tag) => tag[0] !== "p"); for (const pubkey of pubkeys) { this.tags.push(["p", pubkey]); } } /** * Gets the description from the tags. */ get description() { return this.tagValue("description"); } /** * Sets the description tag. */ set description(value) { this.removeTag("description"); if (value) this.tags.push(["description", value]); } }; __publicField(_NDKFollowPack, "kind", 39089 /* FollowPack */); __publicField(_NDKFollowPack, "kinds", [39089 /* FollowPack */, 39092 /* MediaFollowPack */]); var NDKFollowPack = _NDKFollowPack; // ndk/core/src/events/kinds/highlight.ts var import_nostr_tools3 = __toESM(require_nostr_tools()); var _NDKHighlight = class _NDKHighlight extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_article"); this.kind ?? (this.kind = 9802 /* Highlight */); } static from(event) { return new _NDKHighlight(event.ndk, event); } get url() { return this.tagValue("r"); } /** * Context tag. */ set context(context) { if (context === void 0) { this.tags = this.tags.filter(([tag, _value]) => tag !== "context"); } else { this.tags = this.tags.filter(([tag, _value]) => tag !== "context"); this.tags.push(["context", context]); } } get context() { return this.tags.find(([tag, _value]) => tag === "context")?.[1] ?? void 0; } /** * Will return the article URL or NDKEvent if they have already been * set (it won't attempt to load remote events) */ get article() { return this._article; } /** * Article the highlight is coming from. * * @param article Article URL or NDKEvent. */ set article(article) { this._article = article; if (typeof article === "string") { this.tags.push(["r", article]); } else { this.tag(article); } } getArticleTag() { return this.getMatchingTags("a")[0] || this.getMatchingTags("e")[0] || this.getMatchingTags("r")[0]; } async getArticle() { if (this._article !== void 0) return this._article; let taggedBech32; const articleTag = this.getArticleTag(); if (!articleTag) return void 0; switch (articleTag[0]) { case "a": { const [kind, pubkey, identifier] = articleTag[1].split(":"); taggedBech32 = import_nostr_tools3.nip19.naddrEncode({ kind: Number.parseInt(kind), pubkey, identifier }); break; } case "e": taggedBech32 = import_nostr_tools3.nip19.noteEncode(articleTag[1]); break; case "r": this._article = articleTag[1]; break; } if (taggedBech32) { let a = await this.ndk?.fetchEvent(taggedBech32); if (a) { if (a.kind === 30023 /* Article */) { a = NDKArticle.from(a); } this._article = a; } } return this._article; } }; __publicField(_NDKHighlight, "kind", 9802 /* Highlight */); __publicField(_NDKHighlight, "kinds", [9802 /* Highlight */]); var NDKHighlight = _NDKHighlight; // ndk/core/src/events/kinds/image.ts var _NDKImage = class _NDKImage extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_imetas"); this.kind ?? (this.kind = 20 /* Image */); } /** * Creates a NDKImage from an existing NDKEvent. * * @param event NDKEvent to create the NDKImage from. * @returns NDKImage */ static from(event) { return new _NDKImage(event.ndk, event.rawEvent()); } get isValid() { return this.imetas.length > 0; } get imetas() { if (this._imetas) return this._imetas; this._imetas = this.tags.filter((tag) => tag[0] === "imeta").map(mapImetaTag).filter((imeta) => !!imeta.url); return this._imetas; } set imetas(tags) { this._imetas = tags; this.tags = this.tags.filter((tag) => tag[0] !== "imeta"); this.tags.push(...tags.map(imetaTagToTag)); } }; __publicField(_NDKImage, "kind", 20 /* Image */); __publicField(_NDKImage, "kinds", [20 /* Image */]); var NDKImage = _NDKImage; // ndk/core/src/events/kinds/lists/index.ts var _NDKList = class _NDKList extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_encryptedTags"); /** * Stores the number of bytes the content was before decryption * to expire the cache when the content changes. */ __publicField(this, "encryptedTagsLength"); this.kind ?? (this.kind = 30001 /* CategorizedBookmarkList */); } /** * Wrap a NDKEvent into a NDKList */ static from(ndkEvent) { return new _NDKList(ndkEvent.ndk, ndkEvent); } /** * Returns the title of the list. Falls back on fetching the name tag value. */ get title() { const titleTag = this.tagValue("title") || this.tagValue("name"); if (titleTag) return titleTag; if (this.kind === 3 /* Contacts */) { return "Contacts"; } if (this.kind === 1e4 /* MuteList */) { return "Mute"; } if (this.kind === 10001 /* PinList */) { return "Pinned Notes"; } if (this.kind === 10002 /* RelayList */) { return "Relay Metadata"; } if (this.kind === 10003 /* BookmarkList */) { return "Bookmarks"; } if (this.kind === 10004 /* CommunityList */) { return "Communities"; } if (this.kind === 10005 /* PublicChatList */) { return "Public Chats"; } if (this.kind === 10006 /* BlockRelayList */) { return "Blocked Relays"; } if (this.kind === 10007 /* SearchRelayList */) { return "Search Relays"; } if (this.kind === 10050 /* DirectMessageReceiveRelayList */) { return "Direct Message Receive Relays"; } if (this.kind === 10012 /* RelayFeedList */) { return "Relay Feeds"; } if (this.kind === 10015 /* InterestList */) { return "Interests"; } if (this.kind === 10030 /* EmojiList */) { return "Emojis"; } return this.tagValue("d"); } /** * Sets the title of the list. */ set title(title) { this.removeTag(["title", "name"]); if (title) this.tags.push(["title", title]); } /** * Returns the name of the list. * @deprecated Please use "title" instead. */ get name() { return this.title; } /** * Sets the name of the list. * @deprecated Please use "title" instead. This method will use the `title` tag instead. */ set name(name) { this.title = name; } /** * Returns the description of the list. */ get description() { return this.tagValue("description"); } /** * Sets the description of the list. */ set description(name) { this.removeTag("description"); if (name) this.tags.push(["description", name]); } /** * Returns the image of the list. */ get image() { return this.tagValue("image"); } /** * Sets the image of the list. */ set image(name) { this.removeTag("image"); if (name) this.tags.push(["image", name]); } isEncryptedTagsCacheValid() { return !!(this._encryptedTags && this.encryptedTagsLength === this.content.length); } /** * Returns the decrypted content of the list. */ async encryptedTags(useCache = true) { if (useCache && this.isEncryptedTagsCacheValid()) return this._encryptedTags; if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); const user = await this.ndk.signer.user(); try { if (this.content.length > 0) { try { const decryptedContent = await this.ndk.signer.decrypt(user, this.content); const a = JSON.parse(decryptedContent); if (a?.[0]) { this.encryptedTagsLength = this.content.length; return this._encryptedTags = a; } this.encryptedTagsLength = this.content.length; return this._encryptedTags = []; } catch (_e2) { } } } catch (_e2) { } return []; } /** * This method can be overriden to validate that a tag is valid for this list. * * (i.e. the NDKPersonList can validate that items are NDKUser instances) */ validateTag(_tagValue) { return true; } getItems(type) { return this.tags.filter((tag) => tag[0] === type); } /** * Returns the unecrypted items in this list. */ get items() { return this.tags.filter((t) => { return ![ "d", "L", "l", "title", "name", "description", "published_at", "summary", "image", "thumb", "alt", "expiration", "subject", "client" ].includes(t[0]); }); } /** * Adds a new item to the list. * @param relay Relay to add * @param mark Optional mark to add to the item * @param encrypted Whether to encrypt the item * @param position Where to add the item in the list (top or bottom) */ async addItem(item, mark = void 0, encrypted = false, position = "bottom") { if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); let tags; if (item instanceof NDKEvent) { tags = [item.tagReference(mark)]; } else if (item instanceof NDKUser) { tags = item.referenceTags(); } else if (item instanceof NDKRelay) { tags = item.referenceTags(); } else if (Array.isArray(item)) { tags = [item]; } else { throw new Error("Invalid object type"); } if (mark) tags[0].push(mark); if (encrypted) { const user = await this.ndk.signer.user(); const currentList = await this.encryptedTags(); if (position === "top") currentList.unshift(...tags); else currentList.push(...tags); this._encryptedTags = currentList; this.encryptedTagsLength = this.content.length; this.content = JSON.stringify(currentList); await this.encrypt(user); } else { if (position === "top") this.tags.unshift(...tags); else this.tags.push(...tags); } this.created_at = Math.floor(Date.now() / 1e3); this.emit("change"); } /** * Removes an item from the list from both the encrypted and unencrypted lists. * @param value value of item to remove from the list * @param publish whether to publish the change * @returns */ async removeItemByValue(value, publish = true) { if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); const index = this.tags.findIndex((tag) => tag[1] === value); if (index >= 0) { this.tags.splice(index, 1); } const user = await this.ndk.signer.user(); const encryptedTags = await this.encryptedTags(); const encryptedIndex = encryptedTags.findIndex((tag) => tag[1] === value); if (encryptedIndex >= 0) { encryptedTags.splice(encryptedIndex, 1); this._encryptedTags = encryptedTags; this.encryptedTagsLength = this.content.length; this.content = JSON.stringify(encryptedTags); await this.encrypt(user); } if (publish) { return this.publishReplaceable(); } this.created_at = Math.floor(Date.now() / 1e3); this.emit("change"); } /** * Removes an item from the list. * * @param index The index of the item to remove. * @param encrypted Whether to remove from the encrypted list or not. */ async removeItem(index, encrypted) { if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); if (encrypted) { const user = await this.ndk.signer.user(); const currentList = await this.encryptedTags(); currentList.splice(index, 1); this._encryptedTags = currentList; this.encryptedTagsLength = this.content.length; this.content = JSON.stringify(currentList); await this.encrypt(user); } else { this.tags.splice(index, 1); } this.created_at = Math.floor(Date.now() / 1e3); this.emit("change"); return this; } has(item) { return this.items.some((tag) => tag[1] === item); } /** * Creates a filter that will result in fetching * the items of this list * @example * const list = new NDKList(...); * const filters = list.filterForItems(); * const events = await ndk.fetchEvents(filters); */ filterForItems() { const ids = /* @__PURE__ */ new Set(); const nip33Queries = /* @__PURE__ */ new Map(); const filters = []; for (const tag of this.items) { if (tag[0] === "e" && tag[1]) { ids.add(tag[1]); } else if (tag[0] === "a" && tag[1]) { const [kind, pubkey, dTag] = tag[1].split(":"); if (!kind || !pubkey) continue; const key = `${kind}:${pubkey}`; const item = nip33Queries.get(key) || []; item.push(dTag || ""); nip33Queries.set(key, item); } } if (ids.size > 0) { filters.push({ ids: Array.from(ids) }); } if (nip33Queries.size > 0) { for (const [key, values] of nip33Queries.entries()) { const [kind, pubkey] = key.split(":"); filters.push({ kinds: [Number.parseInt(kind)], authors: [pubkey], "#d": values }); } } return filters; } }; __publicField(_NDKList, "kind", 30001 /* CategorizedBookmarkList */); __publicField(_NDKList, "kinds", [ 30001 /* CategorizedBookmarkList */, 10004 /* CommunityList */, 10050 /* DirectMessageReceiveRelayList */, 10030 /* EmojiList */, 10015 /* InterestList */, 10001 /* PinList */, 10002 /* RelayList */, 10007 /* SearchRelayList */, 10006 /* BlockRelayList */, 10003 /* BookmarkList */, 10012 /* RelayFeedList */ ]); var NDKList = _NDKList; var lists_default = NDKList; // ndk/core/src/events/kinds/nip89/app-handler.ts var _NDKAppHandlerEvent = class _NDKAppHandlerEvent extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "profile"); this.kind ?? (this.kind = 31990 /* AppHandler */); } static from(ndkEvent) { const event = new _NDKAppHandlerEvent(ndkEvent.ndk, ndkEvent.rawEvent()); if (event.isValid) { return event; } return null; } get isValid() { const combinations = /* @__PURE__ */ new Map(); const combinationFromTag = (tag) => [tag[0], tag[2]].join(":").toLowerCase(); const tagsToInspect = ["web", "android", "ios"]; for (const tag of this.tags) { if (tagsToInspect.includes(tag[0])) { const combination = combinationFromTag(tag); if (combinations.has(combination)) { if (combinations.get(combination) !== tag[1].toLowerCase()) { return false; } } combinations.set(combination, tag[1].toLowerCase()); } } return true; } /** * Fetches app handler information * If no app information is available on the kind:31990, * we fetch the event's author's profile and return that instead. */ async fetchProfile() { if (this.profile === void 0 && this.content.length > 0) { try { const profile = JSON.parse(this.content); if (profile?.name) { return profile; } this.profile = null; } catch (_e2) { this.profile = null; } } return new Promise((resolve, reject) => { const author = this.author; author.fetchProfile().then(() => { resolve(author.profile); }).catch(reject); }); } }; __publicField(_NDKAppHandlerEvent, "kind", 31990 /* AppHandler */); __publicField(_NDKAppHandlerEvent, "kinds", [31990 /* AppHandler */]); var NDKAppHandlerEvent = _NDKAppHandlerEvent; // ndk/core/src/events/kinds/nutzap/index.ts var import_debug4 = __toESM(require_browser()); // ndk/core/src/events/kinds/nutzap/validation.ts var NutzapValidationCode = /* @__PURE__ */ ((NutzapValidationCode2) => { NutzapValidationCode2["NO_PROOFS"] = "NO_PROOFS"; NutzapValidationCode2["INVALID_PROOF_COUNT"] = "INVALID_PROOF_COUNT"; NutzapValidationCode2["MULTIPLE_RECIPIENTS"] = "MULTIPLE_RECIPIENTS"; NutzapValidationCode2["NO_RECIPIENT"] = "NO_RECIPIENT"; NutzapValidationCode2["MULTIPLE_MINTS"] = "MULTIPLE_MINTS"; NutzapValidationCode2["NO_MINT"] = "NO_MINT"; NutzapValidationCode2["MULTIPLE_EVENT_TAGS"] = "MULTIPLE_EVENT_TAGS"; NutzapValidationCode2["MALFORMED_PROOF_SECRET"] = "MALFORMED_PROOF_SECRET"; NutzapValidationCode2["MISSING_EVENT_TAG_IN_PROOF"] = "MISSING_EVENT_TAG_IN_PROOF"; NutzapValidationCode2["MISMATCHED_EVENT_TAG_IN_PROOF"] = "MISMATCHED_EVENT_TAG_IN_PROOF"; NutzapValidationCode2["MISSING_SENDER_TAG_IN_PROOF"] = "MISSING_SENDER_TAG_IN_PROOF"; NutzapValidationCode2["MISMATCHED_SENDER_TAG_IN_PROOF"] = "MISMATCHED_SENDER_TAG_IN_PROOF"; NutzapValidationCode2["NO_EVENT_TAG_IN_EVENT"] = "NO_EVENT_TAG_IN_EVENT"; return NutzapValidationCode2; })(NutzapValidationCode || {}); var NutzapValidationSeverity = /* @__PURE__ */ ((NutzapValidationSeverity2) => { NutzapValidationSeverity2["ERROR"] = "ERROR"; NutzapValidationSeverity2["WARNING"] = "WARNING"; return NutzapValidationSeverity2; })(NutzapValidationSeverity || {}); var SEVERITY_MAP = { ["NO_PROOFS" /* NO_PROOFS */]: "ERROR" /* ERROR */, ["INVALID_PROOF_COUNT" /* INVALID_PROOF_COUNT */]: "ERROR" /* ERROR */, ["MULTIPLE_RECIPIENTS" /* MULTIPLE_RECIPIENTS */]: "ERROR" /* ERROR */, ["NO_RECIPIENT" /* NO_RECIPIENT */]: "ERROR" /* ERROR */, ["MULTIPLE_MINTS" /* MULTIPLE_MINTS */]: "ERROR" /* ERROR */, ["NO_MINT" /* NO_MINT */]: "ERROR" /* ERROR */, ["MULTIPLE_EVENT_TAGS" /* MULTIPLE_EVENT_TAGS */]: "ERROR" /* ERROR */, ["MALFORMED_PROOF_SECRET" /* MALFORMED_PROOF_SECRET */]: "ERROR" /* ERROR */, ["MISSING_EVENT_TAG_IN_PROOF" /* MISSING_EVENT_TAG_IN_PROOF */]: "WARNING" /* WARNING */, ["MISMATCHED_EVENT_TAG_IN_PROOF" /* MISMATCHED_EVENT_TAG_IN_PROOF */]: "WARNING" /* WARNING */, ["MISSING_SENDER_TAG_IN_PROOF" /* MISSING_SENDER_TAG_IN_PROOF */]: "WARNING" /* WARNING */, ["MISMATCHED_SENDER_TAG_IN_PROOF" /* MISMATCHED_SENDER_TAG_IN_PROOF */]: "WARNING" /* WARNING */, ["NO_EVENT_TAG_IN_EVENT" /* NO_EVENT_TAG_IN_EVENT */]: "WARNING" /* WARNING */ }; var ERROR_MESSAGES = { ["NO_PROOFS" /* NO_PROOFS */]: "Nutzap must contain at least one proof", ["INVALID_PROOF_COUNT" /* INVALID_PROOF_COUNT */]: "Invalid proof count", ["MULTIPLE_RECIPIENTS" /* MULTIPLE_RECIPIENTS */]: "Nutzap must have exactly one recipient (p tag)", ["NO_RECIPIENT" /* NO_RECIPIENT */]: "Nutzap must have a recipient (p tag)", ["MULTIPLE_MINTS" /* MULTIPLE_MINTS */]: "Nutzap must specify exactly one mint (u tag)", ["NO_MINT" /* NO_MINT */]: "Nutzap must specify a mint (u tag)", ["MULTIPLE_EVENT_TAGS" /* MULTIPLE_EVENT_TAGS */]: "Nutzap must have at most one event tag (e tag)", ["MALFORMED_PROOF_SECRET" /* MALFORMED_PROOF_SECRET */]: "Proof secret is malformed and cannot be parsed", ["MISSING_EVENT_TAG_IN_PROOF" /* MISSING_EVENT_TAG_IN_PROOF */]: "Proof secret missing 'e' tag for replay protection", ["MISMATCHED_EVENT_TAG_IN_PROOF" /* MISMATCHED_EVENT_TAG_IN_PROOF */]: "Proof secret 'e' tag does not match event being zapped", ["MISSING_SENDER_TAG_IN_PROOF" /* MISSING_SENDER_TAG_IN_PROOF */]: "Proof secret missing 'P' tag for sender verification", ["MISMATCHED_SENDER_TAG_IN_PROOF" /* MISMATCHED_SENDER_TAG_IN_PROOF */]: "Proof secret 'P' tag does not match sender pubkey", ["NO_EVENT_TAG_IN_EVENT" /* NO_EVENT_TAG_IN_EVENT */]: "Nutzap event missing 'e' tag (recommended for replay protection)" }; function createValidationIssue(code, proofIndex) { return { code, severity: SEVERITY_MAP[code], message: ERROR_MESSAGES[code], proofIndex }; } // ndk/core/src/events/kinds/nutzap/index.ts var _NDKNutzap = class _NDKNutzap extends NDKEvent { constructor(ndk, event) { super(ndk, event); __publicField(this, "debug"); __publicField(this, "_proofs", []); __publicField(this, "sender", this.author); this.kind ?? (this.kind = 9321 /* Nutzap */); this.debug = ndk?.debug.extend("nutzap") ?? (0, import_debug4.default)("ndk:nutzap"); if (!this.alt) this.alt = "This is a nutzap"; try { const proofTags = this.getMatchingTags("proof"); if (proofTags.length) { this._proofs = proofTags.map((tag) => JSON.parse(tag[1])); } else { this._proofs = JSON.parse(this.content); } } catch { return; } } static from(event) { const e2 = new _NDKNutzap(event.ndk, event); if (!e2._proofs || !e2._proofs.length) return; return e2; } set comment(comment) { this.content = comment ?? ""; } get comment() { const c = this.tagValue("comment"); if (c) return c; return this.content; } set proofs(proofs) { this._proofs = proofs; this.tags = this.tags.filter((tag) => tag[0] !== "proof"); for (const proof of proofs) { this.tags.push(["proof", JSON.stringify(proof)]); } } get proofs() { return this._proofs; } get rawP2pk() { const firstProof = this.proofs[0]; try { const secret = JSON.parse(firstProof.secret); let payload; if (typeof secret === "string") { payload = JSON.parse(secret); this.debug("stringified payload", firstProof.secret); } else if (typeof secret === "object") { payload = secret; } if (Array.isArray(payload) && payload[0] === "P2PK" && payload.length > 1 && typeof payload[1] === "object" && payload[1] !== null) { return payload[1].data; } if (typeof payload === "object" && payload !== null && typeof payload[1]?.data === "string") { return payload[1].data; } } catch (e2) { this.debug("error parsing p2pk pubkey", e2, this.proofs[0]); } return void 0; } /** * Gets the p2pk pubkey that is embedded in the first proof. * * Note that this returns a nostr pubkey, not a cashu pubkey (no "02" prefix) */ get p2pk() { const rawP2pk = this.rawP2pk; if (!rawP2pk) return; return rawP2pk.startsWith("02") ? rawP2pk.slice(2) : rawP2pk; } /** * Get the mint where this nutzap proofs exist */ get mint() { return this.tagValue("u"); } set mint(value) { this.replaceTag(["u", value]); } get unit() { let _unit = this.tagValue("unit") ?? "sat"; if (_unit?.startsWith("msat")) _unit = "sat"; return _unit; } set unit(value) { this.removeTag("unit"); if (value?.startsWith("msat")) throw new Error("msat is not allowed, use sat denomination instead"); if (value) this.tag(["unit", value]); } get amount() { const amount = this.proofs.reduce((total, proof) => total + proof.amount, 0); return amount; } /** * Set the target of the nutzap * @param target The target of the nutzap (a user or an event) */ set target(target) { this.tags = this.tags.filter((t) => t[0] !== "p"); if (target instanceof NDKEvent) { this.tags.push(target.tagReference()); } } set recipientPubkey(pubkey) { this.removeTag("p"); this.tag(["p", pubkey]); } get recipientPubkey() { return this.tagValue("p"); } get recipient() { const pubkey = this.recipientPubkey; if (this.ndk) return this.ndk.getUser({ pubkey }); return new NDKUser({ pubkey }); } async toNostrEvent() { if (this.unit === "msat") { this.unit = "sat"; } this.removeTag("amount"); this.tags.push(["amount", this.amount.toString()]); const event = await super.toNostrEvent(); event.content = this.comment; return event; } /** * Validates that the nutzap conforms to NIP-61 * @deprecated Use validateNIP61() instead for detailed validation results */ get isValid() { const result = this.validateNIP61(); return result.valid; } /** * Performs comprehensive validation of the nutzap according to NIP-61. * Returns detailed validation results including errors and warnings. * * Errors make the nutzap invalid, warnings are recommendations for best practices. */ validateNIP61() { const issues = []; let eTagCount = 0; let pTagCount = 0; let mintTagCount = 0; for (const tag of this.tags) { if (tag[0] === "e") eTagCount++; if (tag[0] === "p") pTagCount++; if (tag[0] === "u") mintTagCount++; } if (this.proofs.length === 0) { issues.push(createValidationIssue("NO_PROOFS" /* NO_PROOFS */)); } if (pTagCount === 0) { issues.push(createValidationIssue("NO_RECIPIENT" /* NO_RECIPIENT */)); } else if (pTagCount > 1) { issues.push(createValidationIssue("MULTIPLE_RECIPIENTS" /* MULTIPLE_RECIPIENTS */)); } if (mintTagCount === 0) { issues.push(createValidationIssue("NO_MINT" /* NO_MINT */)); } else if (mintTagCount > 1) { issues.push(createValidationIssue("MULTIPLE_MINTS" /* MULTIPLE_MINTS */)); } if (eTagCount > 1) { issues.push(createValidationIssue("MULTIPLE_EVENT_TAGS" /* MULTIPLE_EVENT_TAGS */)); } const eventId = this.tagValue("e"); const senderPubkey = this.pubkey; for (let i3 = 0; i3 < this.proofs.length; i3++) { const proof = this.proofs[i3]; try { const secret = JSON.parse(proof.secret); const payload = typeof secret === "string" ? JSON.parse(secret) : secret; if (Array.isArray(payload) && payload[0] === "P2PK" && payload[1]) { const tags = payload[1].tags; if (eventId) { if (!tags) { issues.push( createValidationIssue( "MISSING_EVENT_TAG_IN_PROOF" /* MISSING_EVENT_TAG_IN_PROOF */, i3 ) ); } else { const eTag = tags.find((t) => t[0] === "e"); if (!eTag) { issues.push( createValidationIssue( "MISSING_EVENT_TAG_IN_PROOF" /* MISSING_EVENT_TAG_IN_PROOF */, i3 ) ); } else if (eTag[1] !== eventId) { issues.push( createValidationIssue( "MISMATCHED_EVENT_TAG_IN_PROOF" /* MISMATCHED_EVENT_TAG_IN_PROOF */, i3 ) ); } } } if (!tags) { issues.push( createValidationIssue("MISSING_SENDER_TAG_IN_PROOF" /* MISSING_SENDER_TAG_IN_PROOF */, i3) ); } else { const PTag = tags.find((t) => t[0] === "P"); if (!PTag) { issues.push( createValidationIssue( "MISSING_SENDER_TAG_IN_PROOF" /* MISSING_SENDER_TAG_IN_PROOF */, i3 ) ); } else if (PTag[1] !== senderPubkey) { issues.push( createValidationIssue( "MISMATCHED_SENDER_TAG_IN_PROOF" /* MISMATCHED_SENDER_TAG_IN_PROOF */, i3 ) ); } } } } catch { issues.push( createValidationIssue("MALFORMED_PROOF_SECRET" /* MALFORMED_PROOF_SECRET */, i3) ); } } if (!eventId && this.proofs.length > 0) { issues.push(createValidationIssue("NO_EVENT_TAG_IN_EVENT" /* NO_EVENT_TAG_IN_EVENT */)); } const hasErrors = issues.some((issue) => issue.severity === "ERROR" /* ERROR */); return { valid: !hasErrors, issues }; } }; __publicField(_NDKNutzap, "kind", 9321 /* Nutzap */); __publicField(_NDKNutzap, "kinds", [_NDKNutzap.kind]); var NDKNutzap = _NDKNutzap; function proofP2pk(proof) { try { const secret = JSON.parse(proof.secret); let payload = {}; if (typeof secret === "string") { payload = JSON.parse(secret); } else if (typeof secret === "object") { payload = secret; } const isP2PKLocked = payload[0] === "P2PK" && payload[1]?.data; if (isP2PKLocked) { return payload[1].data; } } catch (e2) { console.error("error parsing p2pk pubkey", e2, proof); } } function proofP2pkNostr(proof) { const p2pk = proofP2pk(proof); if (!p2pk) return; if (p2pk.startsWith("02") && p2pk.length === 66) return p2pk.slice(2); return p2pk; } function cashuPubkeyToNostrPubkey(cashuPubkey) { if (cashuPubkey.startsWith("02") && cashuPubkey.length === 66) return cashuPubkey.slice(2); return void 0; } // ndk/core/src/events/kinds/project.ts var _NDKProject = class _NDKProject extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_signer"); this.kind = 31933 /* Project */; } static from(event) { return new _NDKProject(event.ndk, event.rawEvent()); } set repo(value) { this.removeTag("repo"); if (value) this.tags.push(["repo", value]); } set hashtags(values) { this.removeTag("hashtags"); if (values.filter((t) => t.length > 0).length) this.tags.push(["hashtags", ...values]); } get hashtags() { const tag = this.tags.find((tag2) => tag2[0] === "hashtags"); return tag ? tag.slice(1) : []; } get repo() { return this.tagValue("repo"); } get title() { return this.tagValue("title"); } set title(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } get picture() { return this.tagValue("picture"); } set picture(value) { this.removeTag("picture"); if (value) this.tags.push(["picture", value]); } set description(value) { this.content = value; } get description() { return this.content; } /** * The project slug, derived from the 'd' tag. */ get slug() { return this.dTag ?? "empty-dtag"; } async getSigner() { if (this._signer) return this._signer; const encryptedKey = this.tagValue("key"); if (!encryptedKey) { this._signer = NDKPrivateKeySigner.generate(); await this.encryptAndSaveNsec(); } else { const decryptedKey = await this.ndk?.signer?.decrypt(this.ndk.activeUser, encryptedKey); if (!decryptedKey) { throw new Error("Failed to decrypt project key or missing signer context."); } this._signer = new NDKPrivateKeySigner(decryptedKey); } return this._signer; } async getNsec() { const signer = await this.getSigner(); return signer.privateKey; } async setNsec(value) { this._signer = new NDKPrivateKeySigner(value); await this.encryptAndSaveNsec(); } async encryptAndSaveNsec() { if (!this._signer) throw new Error("Signer is not set."); const key = this._signer.privateKey; const encryptedKey = await this.ndk?.signer?.encrypt(this.ndk.activeUser, key); if (encryptedKey) { this.removeTag("key"); this.tags.push(["key", encryptedKey]); } } }; __publicField(_NDKProject, "kind", 31933 /* Project */); __publicField(_NDKProject, "kinds", [31933 /* Project */]); var NDKProject = _NDKProject; // ndk/core/src/events/kinds/project-template.ts var _NDKProjectTemplate = class _NDKProjectTemplate extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind = 30717 /* ProjectTemplate */; } static from(event) { return new _NDKProjectTemplate(event.ndk, event.rawEvent()); } /** * Template identifier from 'd' tag */ get templateId() { return this.dTag ?? ""; } set templateId(value) { this.dTag = value; } /** * Template name from 'title' tag */ get name() { return this.tagValue("title") ?? ""; } set name(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } /** * Template description from 'description' tag */ get description() { return this.tagValue("description") ?? ""; } set description(value) { this.removeTag("description"); if (value) this.tags.push(["description", value]); } /** * Git repository URL from 'uri' tag */ get repoUrl() { return this.tagValue("uri") ?? ""; } set repoUrl(value) { this.removeTag("uri"); if (value) this.tags.push(["uri", value]); } /** * Template preview image URL from 'image' tag */ get image() { return this.tagValue("image"); } set image(value) { this.removeTag("image"); if (value) this.tags.push(["image", value]); } /** * Command to run from 'command' tag */ get command() { return this.tagValue("command"); } set command(value) { this.removeTag("command"); if (value) this.tags.push(["command", value]); } /** * Agent configuration from 'agent' tag */ get agentConfig() { const agentTag = this.tagValue("agent"); if (!agentTag) return void 0; try { return JSON.parse(agentTag); } catch { return void 0; } } set agentConfig(value) { this.removeTag("agent"); if (value) { this.tags.push(["agent", JSON.stringify(value)]); } } /** * Template tags from 't' tags */ get templateTags() { return this.getMatchingTags("t").map((tag) => tag[1]).filter(Boolean); } set templateTags(values) { this.tags = this.tags.filter((tag) => tag[0] !== "t"); values.forEach((value) => { if (value) this.tags.push(["t", value]); }); } }; __publicField(_NDKProjectTemplate, "kind", 30717 /* ProjectTemplate */); __publicField(_NDKProjectTemplate, "kinds", [30717 /* ProjectTemplate */]); var NDKProjectTemplate = _NDKProjectTemplate; // ndk/core/src/events/kinds/relay-list.ts var READ_MARKER = "read"; var WRITE_MARKER = "write"; var _NDKRelayList = class _NDKRelayList extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 10002 /* RelayList */); } static from(ndkEvent) { return new _NDKRelayList(ndkEvent.ndk, ndkEvent.rawEvent()); } get readRelayUrls() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").filter((tag) => !tag[2] || tag[2] && tag[2] === READ_MARKER).map((tag) => tryNormalizeRelayUrl(tag[1])).filter((url) => !!url); } set readRelayUrls(relays) { for (const relay of relays) { this.tags.push(["r", relay, READ_MARKER]); } } get writeRelayUrls() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").filter((tag) => !tag[2] || tag[2] && tag[2] === WRITE_MARKER).map((tag) => tryNormalizeRelayUrl(tag[1])).filter((url) => !!url); } set writeRelayUrls(relays) { for (const relay of relays) { this.tags.push(["r", relay, WRITE_MARKER]); } } get bothRelayUrls() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").filter((tag) => !tag[2]).map((tag) => tag[1]); } set bothRelayUrls(relays) { for (const relay of relays) { this.tags.push(["r", relay]); } } get relays() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").map((tag) => tag[1]); } /** * Provides a relaySet for the relays in this list. */ get relaySet() { if (!this.ndk) throw new Error("NDKRelayList has no NDK instance"); return new NDKRelaySet( new Set(this.relays.map((u3) => this.ndk?.pool.getRelay(u3)).filter((r) => !!r)), this.ndk ); } }; __publicField(_NDKRelayList, "kind", 10002 /* RelayList */); __publicField(_NDKRelayList, "kinds", [10002 /* RelayList */]); var NDKRelayList = _NDKRelayList; function relayListFromKind3(ndk, contactList) { try { const content = JSON.parse(contactList.content); const relayList = new NDKRelayList(ndk); const readRelays = /* @__PURE__ */ new Set(); const writeRelays = /* @__PURE__ */ new Set(); for (let [key, config] of Object.entries(content)) { try { key = normalizeRelayUrl(key); } catch { continue; } if (!config) { readRelays.add(key); writeRelays.add(key); } else { const relayConfig = config; if (relayConfig.write) writeRelays.add(key); if (relayConfig.read) readRelays.add(key); } } relayList.readRelayUrls = Array.from(readRelays); relayList.writeRelayUrls = Array.from(writeRelays); return relayList; } catch { } return void 0; } // ndk/core/src/events/kinds/relay-feed-list.ts var _NDKRelayFeedList = class _NDKRelayFeedList extends NDKList { constructor(ndk, rawEvent) { super(ndk, rawEvent); if (!rawEvent?.kind) { this.kind = 10012 /* RelayFeedList */; } } static from(ndkEvent) { return new _NDKRelayFeedList(ndkEvent.ndk, ndkEvent); } /** * Gets all relay URLs from the list. */ get relayUrls() { return this.getMatchingTags("relay").map((tag) => tag[1]); } /** * Gets all relay set references (kind:30002 naddr) from the list. * Returns them in the format "kind:pubkey:dtag". */ get relaySets() { return this.getMatchingTags("a").map((tag) => tag[1]); } /** * Adds a relay URL to the list. * @param relayUrl - WebSocket URL of the relay * @param mark - Optional mark to add to the relay tag * @param encrypted - Whether to encrypt the item * @param position - Where to add the item in the list */ async addRelay(relayUrl, mark, encrypted = false, position = "bottom") { const tag = ["relay", relayUrl]; if (mark) tag.push(mark); await this.addItem(tag, void 0, encrypted, position); } /** * Adds a relay set reference to the list. * @param relaySetNaddr - NIP-33 address in format "kind:pubkey:dtag" (kind should be 30002) * @param mark - Optional mark to add to the relay set tag * @param encrypted - Whether to encrypt the item * @param position - Where to add the item in the list */ async addRelaySet(relaySetNaddr, mark, encrypted = false, position = "bottom") { const tag = ["a", relaySetNaddr]; if (mark) tag.push(mark); await this.addItem(tag, void 0, encrypted, position); } /** * Removes a relay URL from the list. * @param relayUrl - The relay URL to remove * @param publish - Whether to publish the change */ async removeRelay(relayUrl, publish = true) { await this.removeItemByValue(relayUrl, publish); } /** * Removes a relay set from the list. * @param relaySetNaddr - The relay set naddr to remove * @param publish - Whether to publish the change */ async removeRelaySet(relaySetNaddr, publish = true) { await this.removeItemByValue(relaySetNaddr, publish); } }; __publicField(_NDKRelayFeedList, "kind", 10012 /* RelayFeedList */); __publicField(_NDKRelayFeedList, "kinds", [10012 /* RelayFeedList */]); var NDKRelayFeedList = _NDKRelayFeedList; // ndk/core/src/events/kinds/repost.ts var _NDKRepost = class _NDKRepost extends NDKEvent { constructor() { super(...arguments); __publicField(this, "_repostedEvents"); } static from(event) { return new _NDKRepost(event.ndk, event.rawEvent()); } /** * Returns all reposted events by the current event. * * @param klass Optional class to convert the events to. * @returns */ async repostedEvents(klass, opts) { const items = []; if (!this.ndk) throw new Error("NDK instance not set"); if (this._repostedEvents !== void 0) return this._repostedEvents; for (const eventId of this.repostedEventIds()) { const filter = filterForId(eventId); const event = await this.ndk.fetchEvent(filter, opts); if (event) { items.push(klass ? klass.from(event) : event); } } return items; } /** * Returns the reposted event IDs. */ repostedEventIds() { return this.tags.filter((t) => t[0] === "e" || t[0] === "a").map((t) => t[1]); } }; __publicField(_NDKRepost, "kind", 6 /* Repost */); __publicField(_NDKRepost, "kinds", [6 /* Repost */, 16 /* GenericRepost */]); var NDKRepost = _NDKRepost; function filterForId(id) { if (id.match(/:/)) { const [kind, pubkey, identifier] = id.split(":"); return { kinds: [Number.parseInt(kind)], authors: [pubkey], "#d": [identifier] }; } return { ids: [id] }; } // ndk/core/src/events/kinds/simple-group/member-list.ts var _NDKSimpleGroupMemberList = class _NDKSimpleGroupMemberList extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "relaySet"); __publicField(this, "memberSet", /* @__PURE__ */ new Set()); this.kind ?? (this.kind = 39002 /* GroupMembers */); this.memberSet = new Set(this.members); } static from(event) { return new _NDKSimpleGroupMemberList(event.ndk, event); } get members() { return this.getMatchingTags("p").map((tag) => tag[1]); } hasMember(member) { return this.memberSet.has(member); } async publish(relaySet, timeoutMs, requiredRelayCount) { relaySet ?? (relaySet = this.relaySet); return super.publishReplaceable(relaySet, timeoutMs, requiredRelayCount); } }; __publicField(_NDKSimpleGroupMemberList, "kind", 39002 /* GroupMembers */); __publicField(_NDKSimpleGroupMemberList, "kinds", [39002 /* GroupMembers */]); var NDKSimpleGroupMemberList = _NDKSimpleGroupMemberList; // ndk/core/src/events/kinds/simple-group/metadata.ts var _NDKSimpleGroupMetadata = class _NDKSimpleGroupMetadata extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 39e3 /* GroupMetadata */); } static from(event) { return new _NDKSimpleGroupMetadata(event.ndk, event); } get name() { return this.tagValue("name"); } get picture() { return this.tagValue("picture"); } get about() { return this.tagValue("about"); } get scope() { if (this.getMatchingTags("public").length > 0) return "public"; if (this.getMatchingTags("public").length > 0) return "private"; return void 0; } set scope(scope) { this.removeTag("public"); this.removeTag("private"); if (scope === "public") { this.tags.push(["public", ""]); } else if (scope === "private") { this.tags.push(["private", ""]); } } get access() { if (this.getMatchingTags("open").length > 0) return "open"; if (this.getMatchingTags("closed").length > 0) return "closed"; return void 0; } set access(access) { this.removeTag("open"); this.removeTag("closed"); if (access === "open") { this.tags.push(["open", ""]); } else if (access === "closed") { this.tags.push(["closed", ""]); } } }; __publicField(_NDKSimpleGroupMetadata, "kind", 39e3 /* GroupMetadata */); __publicField(_NDKSimpleGroupMetadata, "kinds", [39e3 /* GroupMetadata */]); var NDKSimpleGroupMetadata = _NDKSimpleGroupMetadata; // ndk/core/src/events/kinds/story.ts var NDKStoryStickerType = /* @__PURE__ */ ((NDKStoryStickerType2) => { NDKStoryStickerType2["Pubkey"] = "pubkey"; NDKStoryStickerType2["Event"] = "event"; NDKStoryStickerType2["Prompt"] = "prompt"; NDKStoryStickerType2["Text"] = "text"; NDKStoryStickerType2["Countdown"] = "countdown"; return NDKStoryStickerType2; })(NDKStoryStickerType || {}); function strToPosition(positionStr) { const [x2, y2] = positionStr.split(",").map(Number); return { x: x2, y: y2 }; } function strToDimension(dimensionStr) { const [width, height] = dimensionStr.split("x").map(Number); return { width, height }; } var _NDKStorySticker = class _NDKStorySticker { constructor(arg) { __publicField(this, "type"); __publicField(this, "value"); __publicField(this, "position"); __publicField(this, "dimension"); __publicField(this, "properties"); __publicField(this, "hasValidDimensions", () => { return typeof this.dimension.width === "number" && typeof this.dimension.height === "number" && !Number.isNaN(this.dimension.width) && !Number.isNaN(this.dimension.height); }); __publicField(this, "hasValidPosition", () => { return typeof this.position.x === "number" && typeof this.position.y === "number" && !Number.isNaN(this.position.x) && !Number.isNaN(this.position.y); }); if (Array.isArray(arg)) { const tag = arg; if (tag[0] !== "sticker" || tag.length < 5) { throw new Error("Invalid sticker tag"); } this.type = tag[1]; this.value = tag[2]; this.position = strToPosition(tag[3]); this.dimension = strToDimension(tag[4]); const props = {}; for (let i3 = 5; i3 < tag.length; i3++) { const [key, ...rest] = tag[i3].split(" "); props[key] = rest.join(" "); } if (Object.keys(props).length > 0) { this.properties = props; } } else { this.type = arg; this.value = void 0; this.position = { x: 0, y: 0 }; this.dimension = { width: 0, height: 0 }; } } static fromTag(tag) { try { return new _NDKStorySticker(tag); } catch { return null; } } get style() { return this.properties?.style; } set style(style) { if (style) this.properties = { ...this.properties, style }; else delete this.properties?.style; } get rotation() { return this.properties?.rot ? Number.parseFloat(this.properties.rot) : void 0; } set rotation(rotation) { if (rotation !== void 0) { this.properties = { ...this.properties, rot: rotation.toString() }; } else { delete this.properties?.rot; } } /** * Checks if the sticker is valid. * * @returns {boolean} - True if the sticker is valid, false otherwise. */ get isValid() { return this.hasValidDimensions() && this.hasValidPosition(); } toTag() { if (!this.isValid) { const errors = [ !this.hasValidDimensions() ? "dimensions is invalid" : void 0, !this.hasValidPosition() ? "position is invalid" : void 0 ].filter(Boolean); throw new Error(`Invalid sticker: ${errors.join(", ")}`); } let value; switch (this.type) { case "event" /* Event */: value = this.value.tagId(); break; case "pubkey" /* Pubkey */: value = this.value.pubkey; break; default: value = this.value; } const tag = ["sticker", this.type, value, coordinates(this.position), dimension(this.dimension)]; if (this.properties) { for (const [key, propValue] of Object.entries(this.properties)) { tag.push(`${key} ${propValue}`); } } return tag; } }; __publicField(_NDKStorySticker, "Text", "text" /* Text */); __publicField(_NDKStorySticker, "Pubkey", "pubkey" /* Pubkey */); __publicField(_NDKStorySticker, "Event", "event" /* Event */); __publicField(_NDKStorySticker, "Prompt", "prompt" /* Prompt */); __publicField(_NDKStorySticker, "Countdown", "countdown" /* Countdown */); var NDKStorySticker = _NDKStorySticker; var _NDKStory = class _NDKStory extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_imeta"); __publicField(this, "_dimensions"); this.kind ?? (this.kind = 23 /* Story */); if (rawEvent) { for (const tag of rawEvent.tags) { switch (tag[0]) { case "imeta": this._imeta = mapImetaTag(tag); break; case "dim": this.dimensions = strToDimension(tag[1]); break; } } } } /** * Creates a NDKStory from an existing NDKEvent. * * @param event NDKEvent to create the NDKStory from. * @returns NDKStory */ static from(event) { return new _NDKStory(event.ndk, event); } /** * Checks if the story is valid (has exactly one imeta tag). */ get isValid() { return !!this.imeta; } /** * Gets the first imeta tag (there should only be one). */ get imeta() { return this._imeta; } /** * Sets a single imeta tag, replacing any existing ones. */ set imeta(tag) { this._imeta = tag; this.tags = this.tags.filter((t) => t[0] !== "imeta"); if (tag) { this.tags.push(imetaTagToTag(tag)); } } /** * Getter for the story dimensions. * * @returns {NDKStoryDimension | undefined} - The story dimensions if available, otherwise undefined. */ get dimensions() { const dimTag = this.tagValue("dim"); if (!dimTag) return void 0; return strToDimension(dimTag); } /** * Setter for the story dimensions. * * @param {NDKStoryDimension | undefined} dimensions - The dimensions to set for the story. */ set dimensions(dimensions) { this.removeTag("dim"); if (dimensions) { this.tags.push(["dim", `${dimensions.width}x${dimensions.height}`]); } } /** * Getter for the story duration. * * @returns {number | undefined} - The story duration in seconds if available, otherwise undefined. */ get duration() { const durTag = this.tagValue("dur"); if (!durTag) return void 0; return Number.parseInt(durTag); } /** * Setter for the story duration. * * @param {number | undefined} duration - The duration in seconds to set for the story. */ set duration(duration) { this.removeTag("dur"); if (duration !== void 0) { this.tags.push(["dur", duration.toString()]); } } /** * Gets all stickers from the story. * * @returns {NDKStorySticker[]} - Array of stickers in the story. */ get stickers() { const stickers = []; for (const tag of this.tags) { if (tag[0] !== "sticker" || tag.length < 5) continue; const sticker = NDKStorySticker.fromTag(tag); if (sticker) stickers.push(sticker); } return stickers; } /** * Adds a sticker to the story. * * @param {NDKStorySticker|StorySticker} sticker - The sticker to add. */ addSticker(sticker) { let stickerToAdd; if (sticker instanceof NDKStorySticker) { stickerToAdd = sticker; } else { const tag = [ "sticker", sticker.type, typeof sticker.value === "string" ? sticker.value : "", coordinates(sticker.position), dimension(sticker.dimension) ]; if (sticker.properties) { for (const [key, value] of Object.entries(sticker.properties)) { tag.push(`${key} ${value}`); } } stickerToAdd = new NDKStorySticker(tag); stickerToAdd.value = sticker.value; } if (stickerToAdd.type === "pubkey" /* Pubkey */) { this.tag(stickerToAdd.value); } else if (stickerToAdd.type === "event" /* Event */) { this.tag(stickerToAdd.value); } this.tags.push(stickerToAdd.toTag()); } /** * Removes a sticker from the story. * * @param {number} index - The index of the sticker to remove. */ removeSticker(index) { const stickers = this.stickers; if (index < 0 || index >= stickers.length) return; let stickerCount = 0; for (let i3 = 0; i3 < this.tags.length; i3++) { if (this.tags[i3][0] === "sticker") { if (stickerCount === index) { this.tags.splice(i3, 1); break; } stickerCount++; } } } }; __publicField(_NDKStory, "kind", 23 /* Story */); __publicField(_NDKStory, "kinds", [23 /* Story */]); var NDKStory = _NDKStory; var coordinates = (position) => `${position.x},${position.y}`; var dimension = (dimension4) => `${dimension4.width}x${dimension4.height}`; // ndk/core/src/events/kinds/subscriptions/receipt.ts var import_debug5 = __toESM(require_browser()); var _NDKSubscriptionReceipt = class _NDKSubscriptionReceipt extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "debug"); this.kind ?? (this.kind = 7003 /* SubscriptionReceipt */); this.debug = ndk?.debug.extend("subscription-start") ?? (0, import_debug5.default)("ndk:subscription-start"); } static from(event) { return new _NDKSubscriptionReceipt(event.ndk, event.rawEvent()); } /** * This is the person being subscribed to */ get recipient() { const pTag = this.getMatchingTags("p")?.[0]; if (!pTag) return void 0; const user = new NDKUser({ pubkey: pTag[1] }); return user; } set recipient(user) { this.removeTag("p"); if (!user) return; this.tags.push(["p", user.pubkey]); } /** * This is the person subscribing */ get subscriber() { const PTag = this.getMatchingTags("P")?.[0]; if (!PTag) return void 0; const user = new NDKUser({ pubkey: PTag[1] }); return user; } set subscriber(user) { this.removeTag("P"); if (!user) return; this.tags.push(["P", user.pubkey]); } set subscriptionStart(event) { this.debug(`before setting subscription start: ${this.rawEvent}`); this.removeTag("e"); this.tag(event, "subscription", true); this.debug(`after setting subscription start: ${this.rawEvent}`); } get tierName() { const tag = this.getMatchingTags("tier")?.[0]; return tag?.[1]; } get isValid() { const period = this.validPeriod; if (!period) { return false; } if (period.start > period.end) { return false; } const pTags = this.getMatchingTags("p"); const PTags = this.getMatchingTags("P"); if (pTags.length !== 1 || PTags.length !== 1) { return false; } return true; } get validPeriod() { const tag = this.getMatchingTags("valid")?.[0]; if (!tag) return void 0; try { return { start: new Date(Number.parseInt(tag[1]) * 1e3), end: new Date(Number.parseInt(tag[2]) * 1e3) }; } catch { return void 0; } } set validPeriod(period) { this.removeTag("valid"); if (!period) return; this.tags.push([ "valid", Math.floor(period.start.getTime() / 1e3).toString(), Math.floor(period.end.getTime() / 1e3).toString() ]); } get startPeriod() { return this.validPeriod?.start; } get endPeriod() { return this.validPeriod?.end; } /** * Whether the subscription is currently active */ isActive(time) { time ?? (time = /* @__PURE__ */ new Date()); const period = this.validPeriod; if (!period) return false; if (time < period.start) return false; if (time > period.end) return false; return true; } }; __publicField(_NDKSubscriptionReceipt, "kind", 7003 /* SubscriptionReceipt */); __publicField(_NDKSubscriptionReceipt, "kinds", [7003 /* SubscriptionReceipt */]); var NDKSubscriptionReceipt = _NDKSubscriptionReceipt; // ndk/core/src/events/kinds/subscriptions/subscription-start.ts var import_debug6 = __toESM(require_browser()); // ndk/core/src/events/kinds/subscriptions/amount.ts var possibleIntervalFrequencies = [ "daily", "weekly", "monthly", "quarterly", "yearly" ]; function calculateTermDurationInSeconds(term) { switch (term) { case "daily": return 24 * 60 * 60; case "weekly": return 7 * 24 * 60 * 60; case "monthly": return 30 * 24 * 60 * 60; case "quarterly": return 3 * 30 * 24 * 60 * 60; case "yearly": return 365 * 24 * 60 * 60; } } function newAmount(amount, currency, term) { return ["amount", amount.toString(), currency, term]; } function parseTagToSubscriptionAmount(tag) { const amount = Number.parseInt(tag[1]); if (Number.isNaN(amount) || amount === void 0 || amount === null || amount <= 0) return void 0; const currency = tag[2]; if (currency === void 0 || currency === "") return void 0; const term = tag[3]; if (term === void 0) return void 0; if (!possibleIntervalFrequencies.includes(term)) return void 0; return { amount, currency, term }; } // ndk/core/src/events/kinds/subscriptions/tier.ts var _NDKSubscriptionTier = class _NDKSubscriptionTier extends NDKArticle { constructor(ndk, rawEvent) { const k2 = rawEvent?.kind ?? 37001 /* SubscriptionTier */; super(ndk, rawEvent); this.kind = k2; } /** * Creates a new NDKSubscriptionTier from an event * @param event * @returns NDKSubscriptionTier */ static from(event) { return new _NDKSubscriptionTier(event.ndk, event); } /** * Returns perks for this tier */ get perks() { return this.getMatchingTags("perk").map((tag) => tag[1]).filter((perk) => perk !== void 0); } /** * Adds a perk to this tier */ addPerk(perk) { this.tags.push(["perk", perk]); } /** * Returns the amount for this tier */ get amounts() { return this.getMatchingTags("amount").map((tag) => parseTagToSubscriptionAmount(tag)).filter((a) => a !== void 0); } /** * Adds an amount to this tier * @param amount Amount in the smallest unit of the currency (e.g. cents, msats) * @param currency Currency code. Use msat for millisatoshis * @param term One of daily, weekly, monthly, quarterly, yearly */ addAmount(amount, currency, term) { this.tags.push(newAmount(amount, currency, term)); } /** * Sets a relay where content related to this tier can be found * @param relayUrl URL of the relay */ set relayUrl(relayUrl) { this.tags.push(["r", relayUrl]); } /** * Returns the relay URLs for this tier */ get relayUrls() { return this.getMatchingTags("r").map((tag) => tag[1]).filter((relay) => relay !== void 0); } /** * Gets the verifier pubkey for this tier. This is the pubkey that will generate * subscription payment receipts */ get verifierPubkey() { return this.tagValue("p"); } /** * Sets the verifier pubkey for this tier. */ set verifierPubkey(pubkey) { this.removeTag("p"); if (pubkey) this.tags.push(["p", pubkey]); } /** * Checks if this tier is valid */ get isValid() { return this.title !== void 0 && // Must have a title this.amounts.length > 0; } }; __publicField(_NDKSubscriptionTier, "kind", 37001 /* SubscriptionTier */); __publicField(_NDKSubscriptionTier, "kinds", [37001 /* SubscriptionTier */]); var NDKSubscriptionTier = _NDKSubscriptionTier; // ndk/core/src/events/kinds/subscriptions/subscription-start.ts var _NDKSubscriptionStart = class _NDKSubscriptionStart extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "debug"); this.kind ?? (this.kind = 7001 /* Subscribe */); this.debug = ndk?.debug.extend("subscription-start") ?? (0, import_debug6.default)("ndk:subscription-start"); } static from(event) { return new _NDKSubscriptionStart(event.ndk, event.rawEvent()); } /** * Recipient of the subscription. I.e. The author of this event subscribes to this user. */ get recipient() { const pTag = this.getMatchingTags("p")?.[0]; if (!pTag) return void 0; const user = new NDKUser({ pubkey: pTag[1] }); return user; } set recipient(user) { this.removeTag("p"); if (!user) return; this.tags.push(["p", user.pubkey]); } /** * The amount of the subscription. */ get amount() { const amountTag = this.getMatchingTags("amount")?.[0]; if (!amountTag) return void 0; return parseTagToSubscriptionAmount(amountTag); } set amount(amount) { this.removeTag("amount"); if (!amount) return; this.tags.push(newAmount(amount.amount, amount.currency, amount.term)); } /** * The event id or NIP-33 tag id of the tier that the user is subscribing to. */ get tierId() { const eTag = this.getMatchingTags("e")?.[0]; const aTag = this.getMatchingTags("a")?.[0]; if (!eTag || !aTag) return void 0; return eTag[1] ?? aTag[1]; } set tier(tier) { this.removeTag("e"); this.removeTag("a"); this.removeTag("event"); if (!tier) return; this.tag(tier); this.removeTag("p"); this.tags.push(["p", tier.pubkey]); this.tags.push(["event", JSON.stringify(tier.rawEvent())]); } /** * Fetches the tier that the user is subscribing to. */ async fetchTier() { const eventTag = this.tagValue("event"); if (eventTag) { try { const parsedEvent = JSON.parse(eventTag); return new NDKSubscriptionTier(this.ndk, parsedEvent); } catch { this.debug("Failed to parse event tag"); } } const tierId = this.tierId; if (!tierId) return void 0; const e2 = await this.ndk?.fetchEvent(tierId); if (!e2) return void 0; return NDKSubscriptionTier.from(e2); } get isValid() { if (this.getMatchingTags("amount").length !== 1) { this.debug("Invalid # of amount tag"); return false; } if (!this.amount) { this.debug("Invalid amount tag"); return false; } if (this.getMatchingTags("p").length !== 1) { this.debug("Invalid # of p tag"); return false; } if (!this.recipient) { this.debug("Invalid p tag"); return false; } return true; } }; __publicField(_NDKSubscriptionStart, "kind", 7001 /* Subscribe */); __publicField(_NDKSubscriptionStart, "kinds", [7001 /* Subscribe */]); var NDKSubscriptionStart = _NDKSubscriptionStart; // ndk/core/src/events/kinds/task.ts var _NDKTask = class _NDKTask extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind = 1934 /* Task */; } static from(event) { return new _NDKTask(event.ndk, event.rawEvent()); } set title(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } get title() { return this.tagValue("title"); } set project(project) { this.removeTag("a"); this.tags.push(project.tagReference()); } get projectSlug() { const tag = this.getMatchingTags("a")[0]; return tag ? tag[1].split(/:/)?.[2] : void 0; } }; __publicField(_NDKTask, "kind", 1934 /* Task */); __publicField(_NDKTask, "kinds", [1934 /* Task */]); var NDKTask = _NDKTask; // ndk/core/src/events/kinds/thread.ts var _NDKThread = class _NDKThread extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 11 /* Thread */); } /** * Creates an NDKThread from an existing NDKEvent. * * @param event NDKEvent to create the NDKThread from. * @returns NDKThread */ static from(event) { return new _NDKThread(event.ndk, event); } /** * Gets the title of the thread. */ get title() { return this.tagValue("title"); } /** * Sets the title of the thread. */ set title(title) { this.removeTag("title"); if (title) { this.tags.push(["title", title]); } } }; __publicField(_NDKThread, "kind", 11 /* Thread */); __publicField(_NDKThread, "kinds", [11 /* Thread */]); var NDKThread = _NDKThread; // ndk/core/src/events/kinds/video.ts var _NDKVideo = class _NDKVideo extends NDKEvent { constructor() { super(...arguments); __publicField(this, "_imetas"); } /** * Creates a NDKArticle from an existing NDKEvent. * * @param event NDKEvent to create the NDKArticle from. * @returns NDKArticle */ static from(event) { return new _NDKVideo(event.ndk, event.rawEvent()); } /** * Getter for the article title. * * @returns {string | undefined} - The article title if available, otherwise undefined. */ get title() { return this.tagValue("title"); } /** * Setter for the article title. * * @param {string | undefined} title - The title to set for the article. */ set title(title) { this.removeTag("title"); if (title) this.tags.push(["title", title]); } /** * Getter for the article thumbnail. * * @returns {string | undefined} - The article thumbnail if available, otherwise undefined. */ get thumbnail() { let thumbnail; if (this.imetas && this.imetas.length > 0) { thumbnail = this.imetas[0].image?.[0]; } return thumbnail ?? this.tagValue("thumb"); } get imetas() { if (this._imetas) return this._imetas; this._imetas = this.tags.filter((tag) => tag[0] === "imeta").map(mapImetaTag); return this._imetas; } set imetas(tags) { this._imetas = tags; this.tags = this.tags.filter((tag) => tag[0] !== "imeta"); this.tags.push(...tags.map(imetaTagToTag)); } get url() { if (this.imetas && this.imetas.length > 0) { return this.imetas[0].url; } return this.tagValue("url"); } /** * Getter for the article's publication timestamp. * * @returns {number | undefined} - The Unix timestamp of when the article was published or undefined. */ get published_at() { const tag = this.tagValue("published_at"); if (tag) { return Number.parseInt(tag); } return void 0; } /** * Generates content tags for the article. * * This method first checks and sets the publication date if not available, * and then generates content tags based on the base NDKEvent class. * * @returns {ContentTag} - The generated content tags. */ async generateTags() { super.generateTags(); if (!this.kind) { if (this.imetas?.[0]?.dim) { const [width, height] = this.imetas[0].dim.split("x"); const isPortrait = width && height && Number.parseInt(width) < Number.parseInt(height); const isShort = this.duration && this.duration < 120; if (isShort && isPortrait) this.kind = 22 /* ShortVideo */; else this.kind = 21 /* Video */; } } return super.generateTags(); } get duration() { const tag = this.tagValue("duration"); if (tag) { return Number.parseInt(tag); } return void 0; } /** * Setter for the video's duration * * @param {number | undefined} duration - The duration to set for the video (in seconds) */ set duration(dur) { this.removeTag("duration"); if (dur !== void 0) { this.tags.push(["duration", Math.floor(dur).toString()]); } } }; __publicField(_NDKVideo, "kind", 21 /* Video */); __publicField(_NDKVideo, "kinds", [34235 /* HorizontalVideo */, 34236 /* VerticalVideo */, 22 /* ShortVideo */, 21 /* Video */]); var NDKVideo = _NDKVideo; // ndk/core/src/events/kinds/wiki.ts var _NDKWiki = class _NDKWiki extends NDKArticle { static from(event) { return new _NDKWiki(event.ndk, event.rawEvent()); } get isDefered() { return this.hasTag("a", "defer"); } get deferedId() { return this.tagValue("a", "defer"); } /** * Defers the author's wiki event to another wiki event. * * Wiki-events can tag other wiki-events with a `defer` marker to indicate that it considers someone else's entry as a "better" version of itself. If using a `defer` marker both `a` and `e` tags SHOULD be used. * * @example * myWiki.defer = betterWikiEntryOnTheSameTopic; * myWiki.publishReplaceable() */ set defer(deferedTo) { this.removeTag("a", "defer"); this.tag(deferedTo, "defer"); } }; __publicField(_NDKWiki, "kind", 30818 /* Wiki */); __publicField(_NDKWiki, "kinds", [30818 /* Wiki */]); var NDKWiki = _NDKWiki; var _NDKWikiMergeRequest = class _NDKWikiMergeRequest extends NDKEvent { static from(event) { return new _NDKWikiMergeRequest(event.ndk, event.rawEvent()); } /** * The target ID () of the wiki event to merge into. */ get targetId() { return this.tagValue("a"); } /** * Sets the target ID () of the wiki event to merge into. */ set target(targetEvent) { this.tags = this.tags.filter((tag) => { if (tag[0] === "a") return true; if (tag[0] === "e" && tag[3] !== "source") return true; }); this.tag(targetEvent); } /** * The source ID of the wiki event to merge from. */ get sourceId() { return this.tagValue("e", "source"); } /** * Sets the event we are asking to get merged into the target. */ set source(sourceEvent) { this.removeTag("e", "source"); this.tag(sourceEvent, "source", false, "e"); } }; __publicField(_NDKWikiMergeRequest, "kind", 818 /* WikiMergeRequest */); __publicField(_NDKWikiMergeRequest, "kinds", [818 /* WikiMergeRequest */]); var NDKWikiMergeRequest = _NDKWikiMergeRequest; // ndk/core/src/events/wrap.ts var registeredEventClasses = /* @__PURE__ */ new Set(); function registerEventClass(eventClass) { registeredEventClasses.add(eventClass); } function unregisterEventClass(eventClass) { registeredEventClasses.delete(eventClass); } function getRegisteredEventClasses() { return new Set(registeredEventClasses); } function wrapEvent(event) { const eventWrappingMap = /* @__PURE__ */ new Map(); const builtInClasses = [ NDKImage, NDKVideo, NDKCashuMintList, NDKArticle, NDKHighlight, NDKDraft, NDKWiki, NDKWikiMergeRequest, NDKNutzap, NDKProject, NDKTask, NDKProjectTemplate, NDKSimpleGroupMemberList, NDKSimpleGroupMetadata, NDKSubscriptionTier, NDKSubscriptionStart, NDKSubscriptionReceipt, NDKList, NDKRelayList, NDKRelayFeedList, NDKStory, NDKBlossomList, NDKFollowPack, NDKThread, NDKRepost, NDKClassified, NDKAppHandlerEvent, NDKDVMJobFeedback, NDKCashuMintAnnouncement, NDKFedimintMint, NDKMintRecommendation ]; const allClasses = [...builtInClasses, ...registeredEventClasses]; for (const klass2 of allClasses) { for (const kind of klass2.kinds) { eventWrappingMap.set(kind, klass2); } } const klass = eventWrappingMap.get(event.kind); if (klass) return klass.from(event); return event; } // ndk/core/src/ai-guardrails/event/signing.ts function checkMissingKind(event, error) { if (event.kind === void 0 || event.kind === null) { error( "event-missing-kind", `Cannot sign event without 'kind'. \u{1F4E6} Event data: \u2022 content: ${event.content ? `"${event.content.substring(0, 50)}${event.content.length > 50 ? "..." : ""}"` : "(empty)"} \u2022 tags: ${event.tags.length} tag${event.tags.length !== 1 ? "s" : ""} \u2022 kind: ${event.kind} \u274C Set event.kind before signing.`, "Example: event.kind = 1; // for text note", false // Fatal error - cannot be disabled ); } } function checkContentIsObject(event, error) { if (typeof event.content === "object") { const contentPreview = JSON.stringify(event.content, null, 2).substring(0, 200); error( "event-content-is-object", `Event content is an object. Content must be a string. \u{1F4E6} Your content (${typeof event.content}): ${contentPreview}${JSON.stringify(event.content).length > 200 ? "..." : ""} \u274C event.content = { ... } // WRONG \u2705 event.content = JSON.stringify({ ... }) // CORRECT`, "Use JSON.stringify() for structured data: event.content = JSON.stringify(data)", false // Fatal error - cannot be disabled ); } } function checkCreatedAtMilliseconds(event, error) { if (event.created_at && event.created_at > 1e10) { const correctValue = Math.floor(event.created_at / 1e3); const dateString = new Date(event.created_at).toISOString(); error( "event-created-at-milliseconds", `Event created_at is in milliseconds, not seconds. \u{1F4E6} Your value: \u2022 created_at: ${event.created_at} \u274C \u2022 Interpreted as: ${dateString} \u2022 Should be: ${correctValue} \u2705 Nostr timestamps MUST be in seconds since Unix epoch.`, "Use Math.floor(Date.now() / 1000) instead of Date.now()", false // Fatal error - cannot be disabled ); } } function checkInvalidPTags(event, error) { const pTags = event.getMatchingTags("p"); pTags.forEach((tag, idx) => { if (tag[1] && !/^[0-9a-f]{64}$/i.test(tag[1])) { const tagPreview = JSON.stringify(tag); error( "tag-invalid-p-tag", `p-tag[${idx}] has invalid pubkey. \u{1F4E6} Your tag: ${tagPreview} \u274C Invalid value: "${tag[1]}" \u2022 Length: ${tag[1].length} (expected 64) \u2022 Format: ${tag[1].startsWith("npub") ? "bech32 (npub)" : "unknown"} p-tags MUST contain 64-character hex pubkeys.`, tag[1].startsWith("npub") ? "Use ndkUser.pubkey instead of npub:\n \u2705 event.tags.push(['p', ndkUser.pubkey])\n \u274C event.tags.push(['p', 'npub1...'])" : "p-tags must contain valid hex pubkeys (64 characters, 0-9a-f)", false // Fatal error - cannot be disabled ); } }); } function checkInvalidETags(event, error) { const eTags = event.getMatchingTags("e"); eTags.forEach((tag, idx) => { if (tag[1] && !/^[0-9a-f]{64}$/i.test(tag[1])) { const tagPreview = JSON.stringify(tag); const isBech32 = tag[1].startsWith("note") || tag[1].startsWith("nevent"); error( "tag-invalid-e-tag", `e-tag[${idx}] has invalid event ID. \u{1F4E6} Your tag: ${tagPreview} \u274C Invalid value: "${tag[1]}" \u2022 Length: ${tag[1].length} (expected 64) \u2022 Format: ${isBech32 ? "bech32 (note/nevent)" : "unknown"} e-tags MUST contain 64-character hex event IDs.`, isBech32 ? "Use event.id instead of bech32:\n \u2705 event.tags.push(['e', referencedEvent.id])\n \u274C event.tags.push(['e', 'note1...'])" : "e-tags must contain valid hex event IDs (64 characters, 0-9a-f)", false // Fatal error - cannot be disabled ); } }); } function checkManualReplyMarkers(event, warn, replyEvents) { if (event.kind !== 1) return; if (replyEvents.has(event)) return; const eTagsWithMarkers = event.tags.filter((tag) => tag[0] === "e" && (tag[3] === "reply" || tag[3] === "root")); if (eTagsWithMarkers.length > 0) { const tagList = eTagsWithMarkers.map((tag, idx) => ` ${idx + 1}. ${JSON.stringify(tag)}`).join("\n"); warn( "event-manual-reply-markers", `Event has ${eTagsWithMarkers.length} e-tag(s) with manual reply/root markers. \u{1F4E6} Your tags with markers: ${tagList} \u26A0\uFE0F Manual reply markers detected! This will cause incorrect threading.`, `Reply events MUST be created using .reply(): \u2705 CORRECT: const replyEvent = originalEvent.reply(); replyEvent.content = 'good point!'; await replyEvent.publish(); \u274C WRONG: event.tags.push(['e', eventId, '', 'reply']); NDK handles all reply threading automatically - never add reply/root markers manually.` ); } } function checkHashtagsWithPrefix(event, error) { const tTags = event.getMatchingTags("t"); tTags.forEach((tag, idx) => { if (tag[1] && tag[1].startsWith("#")) { const tagPreview = JSON.stringify(tag); error( "tag-hashtag-with-prefix", `t-tag[${idx}] contains hashtag with # prefix. \u{1F4E6} Your tag: ${tagPreview} \u274C Invalid value: "${tag[1]}" Hashtag tags should NOT include the # symbol.`, `Remove the # prefix from hashtag tags: \u2705 event.tags.push(['t', 'nostr']) \u274C event.tags.push(['t', '#nostr'])`, false // Fatal error - cannot be disabled ); } }); } function checkReplaceableWithOldTimestamp(event, warn) { if (event.kind === void 0 || event.kind === null || !event.created_at) return; if (!event.isReplaceable()) return; const nowSeconds = Math.floor(Date.now() / 1e3); const ageSeconds = nowSeconds - event.created_at; const TEN_SECONDS = 10; if (ageSeconds > TEN_SECONDS) { const ageMinutes = Math.floor(ageSeconds / 60); const ageDescription = ageMinutes > 0 ? `${ageMinutes} minute${ageMinutes !== 1 ? "s" : ""}` : `${ageSeconds} seconds`; warn( "event-replaceable-old-timestamp", `Publishing a replaceable event with an old created_at timestamp. \u{1F4E6} Event details: \u2022 kind: ${event.kind} (replaceable) \u2022 created_at: ${event.created_at} \u2022 age: ${ageDescription} old \u2022 current time: ${nowSeconds} \u26A0\uFE0F This is wrong and will be rejected by relays.`, `For replaceable events, use publishReplaceable(): \u2705 CORRECT: await event.publishReplaceable(); // Automatically updates created_at to now \u274C WRONG: await event.publish(); // Uses old created_at` ); } } function signing(event, error, warn, replyEvents) { checkMissingKind(event, error); checkContentIsObject(event, error); checkCreatedAtMilliseconds(event, error); checkInvalidPTags(event, error); checkInvalidETags(event, error); checkHashtagsWithPrefix(event, error); checkManualReplyMarkers(event, warn, replyEvents); } function publishing(event, warn) { checkReplaceableWithOldTimestamp(event, warn); } // ndk/core/src/ai-guardrails/ndk/fetch-events.ts function isNip33Pattern(filters) { const filterArray = Array.isArray(filters) ? filters : [filters]; if (filterArray.length !== 1) return false; const filter = filterArray[0]; return filter.kinds && Array.isArray(filter.kinds) && filter.kinds.length === 1 && filter.authors && Array.isArray(filter.authors) && filter.authors.length === 1 && filter["#d"] && Array.isArray(filter["#d"]) && filter["#d"].length === 1; } function isReplaceableEventFilter(filters) { const filterArray = Array.isArray(filters) ? filters : [filters]; if (filterArray.length === 0) { return false; } return filterArray.every((filter) => { if (!filter.kinds || !Array.isArray(filter.kinds) || filter.kinds.length === 0) { return false; } if (!filter.authors || !Array.isArray(filter.authors) || filter.authors.length === 0) { return false; } const allKindsReplaceable = filter.kinds.every((kind) => { return kind === 0 || kind === 3 || kind >= 1e4 && kind <= 19999; }); return allKindsReplaceable; }); } function formatFilter(filter) { const formatted = JSON.stringify(filter, null, 2); return formatted.split("\n").map((line, idx) => idx === 0 ? line : ` ${line}`).join("\n"); } function fetchingEvents(filters, opts, warn, shouldWarnRatio, incrementCount) { incrementCount(); if (opts?.cacheUsage === "ONLY_CACHE") { return; } const filterArray = Array.isArray(filters) ? filters : [filters]; const formattedFilters = filterArray.map(formatFilter).join("\n\n ---\n\n "); if (isNip33Pattern(filters)) { const filter = filterArray[0]; warn( "fetch-events-usage", "For fetching a NIP-33 addressable event, use fetchEvent() with the naddr directly.\n\n\u{1F4E6} Your filter:\n " + formattedFilters + ` \u274C BAD: const decoded = nip19.decode(naddr); const events = await ndk.fetchEvents({ kinds: [decoded.data.kind], authors: [decoded.data.pubkey], "#d": [decoded.data.identifier] }); const event = Array.from(events)[0]; \u2705 GOOD: const event = await ndk.fetchEvent(naddr); \u2705 GOOD: const event = await ndk.fetchEvent('naddr1...'); fetchEvent() handles naddr decoding automatically and returns the event directly.` ); } else if (isReplaceableEventFilter(filters)) { return; } else { if (!shouldWarnRatio()) { return; } let filterAnalysis = ""; const hasLimit = filterArray.some((f) => f.limit !== void 0); const totalKinds = new Set(filterArray.flatMap((f) => f.kinds || [])).size; const totalAuthors = new Set(filterArray.flatMap((f) => f.authors || [])).size; if (hasLimit) { const maxLimit = Math.max(...filterArray.map((f) => f.limit || 0)); filterAnalysis += ` \u2022 Limit: ${maxLimit} event${maxLimit !== 1 ? "s" : ""}`; } if (totalKinds > 0) { filterAnalysis += ` \u2022 Kinds: ${totalKinds} type${totalKinds !== 1 ? "s" : ""}`; } if (totalAuthors > 0) { filterAnalysis += ` \u2022 Authors: ${totalAuthors} author${totalAuthors !== 1 ? "s" : ""}`; } warn( "fetch-events-usage", "fetchEvents() is a BLOCKING operation that waits for EOSE.\nIn most cases, you should use subscribe() instead.\n\n\u{1F4E6} Your filter" + (filterArray.length > 1 ? "s" : "") + ":\n " + formattedFilters + (filterAnalysis ? "\n\n\u{1F4CA} Filter analysis:" + filterAnalysis : "") + "\n\n \u274C BAD: const events = await ndk.fetchEvents(filter);\n \u2705 GOOD: ndk.subscribe(filter, { onEvent: (e) => ... });\n\nOnly use fetchEvents() when you MUST block until data arrives.", "For one-time queries, use fetchEvent() instead of fetchEvents() when expecting a single result." ); } } // ndk/core/src/ai-guardrails/types.ts var GuardrailCheckId = { // NDK lifecycle NDK_NO_CACHE: "ndk-no-cache", // Filter-related FILTER_BECH32_IN_ARRAY: "filter-bech32-in-array", FILTER_INVALID_HEX: "filter-invalid-hex", FILTER_ONLY_LIMIT: "filter-only-limit", FILTER_LARGE_LIMIT: "filter-large-limit", FILTER_EMPTY: "filter-empty", FILTER_SINCE_AFTER_UNTIL: "filter-since-after-until", FILTER_INVALID_A_TAG: "filter-invalid-a-tag", FILTER_HASHTAG_WITH_PREFIX: "filter-hashtag-with-prefix", // fetchEvents anti-pattern FETCH_EVENTS_USAGE: "fetch-events-usage", // Event construction EVENT_MISSING_KIND: "event-missing-kind", EVENT_PARAM_REPLACEABLE_NO_DTAG: "event-param-replaceable-no-dtag", EVENT_CREATED_AT_MILLISECONDS: "event-created-at-milliseconds", EVENT_NO_NDK_INSTANCE: "event-no-ndk-instance", EVENT_CONTENT_IS_OBJECT: "event-content-is-object", EVENT_MODIFIED_AFTER_SIGNING: "event-modified-after-signing", EVENT_MANUAL_REPLY_MARKERS: "event-manual-reply-markers", // Tag construction TAG_E_FOR_PARAM_REPLACEABLE: "tag-e-for-param-replaceable", TAG_BECH32_VALUE: "tag-bech32-value", TAG_DUPLICATE: "tag-duplicate", TAG_INVALID_P_TAG: "tag-invalid-p-tag", TAG_INVALID_E_TAG: "tag-invalid-e-tag", TAG_HASHTAG_WITH_PREFIX: "tag-hashtag-with-prefix", // Subscription SUBSCRIBE_NOT_STARTED: "subscribe-not-started", SUBSCRIBE_CLOSE_ON_EOSE_NO_HANDLER: "subscribe-close-on-eose-no-handler", SUBSCRIBE_PASSED_EVENT_NOT_FILTER: "subscribe-passed-event-not-filter", SUBSCRIBE_AWAITED: "subscribe-awaited", // Relay RELAY_INVALID_URL: "relay-invalid-url", RELAY_HTTP_INSTEAD_OF_WS: "relay-http-instead-of-ws", RELAY_NO_ERROR_HANDLERS: "relay-no-error-handlers", // Validation VALIDATION_PUBKEY_IS_NPUB: "validation-pubkey-is-npub", VALIDATION_PUBKEY_WRONG_LENGTH: "validation-pubkey-wrong-length", VALIDATION_EVENT_ID_IS_BECH32: "validation-event-id-is-bech32", VALIDATION_EVENT_ID_WRONG_LENGTH: "validation-event-id-wrong-length" }; // ndk/core/src/ai-guardrails/ndk.ts function checkCachePresence(ndk, shouldCheck) { if (!shouldCheck(GuardrailCheckId.NDK_NO_CACHE)) return; setTimeout(() => { if (!ndk.cacheAdapter) { const isBrowser = typeof window !== "undefined"; const suggestion = isBrowser ? "Consider using @nostr-dev-kit/ndk-cache-dexie or @nostr-dev-kit/ndk-cache-sqlite-wasm" : "Consider using @nostr-dev-kit/ndk-cache-redis or @nostr-dev-kit/ndk-cache-sqlite"; const message = ` \u{1F916} AI_GUARDRAILS WARNING: NDK initialized without a cache adapter. Apps perform significantly better with caching. \u{1F4A1} ${suggestion} \u{1F507} To disable this check: ndk.aiGuardrails.skip('${GuardrailCheckId.NDK_NO_CACHE}') or set: ndk.aiGuardrails = { skip: new Set(['${GuardrailCheckId.NDK_NO_CACHE}']) }`; console.warn(message); } }, 2500); } // ndk/core/src/ai-guardrails/index.ts var AIGuardrails = class { constructor(mode = false) { __publicField(this, "enabled", false); __publicField(this, "skipSet", /* @__PURE__ */ new Set()); __publicField(this, "extensions", /* @__PURE__ */ new Map()); __publicField(this, "_nextCallDisabled", null); __publicField(this, "_replyEvents", /* @__PURE__ */ new WeakSet()); __publicField(this, "_fetchEventsCount", 0); __publicField(this, "_subscribeCount", 0); /** * NDK-related guardrails */ __publicField(this, "ndk", { /** * Called when fetchEvents is about to be called */ fetchingEvents: (filters, opts) => { if (!this.enabled) return; fetchingEvents( filters, opts, this.warn.bind(this), this.shouldWarnAboutFetchEventsRatio.bind(this), this.incrementFetchEventsCount.bind(this) ); } }); /** * Event-related guardrails */ __publicField(this, "event", { /** * Called when an event is about to be signed */ signing: (event) => { if (!this.enabled) return; signing(event, this.error.bind(this), this.warn.bind(this), this._replyEvents); }, /** * Called before an event is published */ publishing: (event) => { if (!this.enabled) return; publishing(event, this.warn.bind(this)); }, /** * Called when an event is received from a relay */ received: (_event, _relay) => { if (!this.enabled) return; }, /** * Called when a reply event is being created via .reply() * This allows guardrails to track legitimate reply events */ creatingReply: (event) => { if (!this.enabled) return; this._replyEvents.add(event); } }); /** * Subscription-related guardrails */ __publicField(this, "subscription", { /** * Called when a subscription is created */ created: (_filters, _opts) => { if (!this.enabled) return; this.incrementSubscribeCount(); } }); /** * Relay-related guardrails */ __publicField(this, "relay", { /** * Called when a relay connection is established */ connected: (_relay) => { if (!this.enabled) return; } }); this.setMode(mode); } /** * Register an extension namespace with custom guardrail hooks. * This allows external packages to add their own guardrails. * * @example * ```typescript * // In NDKSvelte package: * ndk.aiGuardrails.register('ndkSvelte', { * constructing: (params) => { * if (!params.session) { * warn('ndksvelte-no-session', 'NDKSvelte instantiated without session parameter...'); * } * } * }); * * // In NDKSvelte constructor: * this.ndk.aiGuardrails?.ndkSvelte?.constructing(params); * ``` */ register(namespace, hooks) { if (this.extensions.has(namespace)) { console.warn(`AIGuardrails: Extension '${namespace}' already registered, overwriting`); } const wrappedHooks = {}; for (const [key, fn] of Object.entries(hooks)) { if (typeof fn === "function") { wrappedHooks[key] = (...args) => { if (!this.enabled) return; fn(...args, this.shouldCheck.bind(this), this.error.bind(this), this.warn.bind(this)); }; } } this.extensions.set(namespace, wrappedHooks); this[namespace] = wrappedHooks; } /** * Set the guardrails mode. */ setMode(mode) { if (typeof mode === "boolean") { this.enabled = mode; this.skipSet.clear(); } else if (mode && typeof mode === "object") { this.enabled = true; this.skipSet = mode.skip || /* @__PURE__ */ new Set(); } } /** * Check if guardrails are enabled at all. */ isEnabled() { return this.enabled; } /** * Check if a specific guardrail check should run. */ shouldCheck(id) { if (!this.enabled) return false; if (this.skipSet.has(id)) return false; if (this._nextCallDisabled === "all") return false; if (this._nextCallDisabled && this._nextCallDisabled.has(id)) return false; return true; } /** * Disable a specific guardrail check. */ skip(id) { this.skipSet.add(id); } /** * Re-enable a specific guardrail check. */ enable(id) { this.skipSet.delete(id); } /** * Get all currently skipped guardrails. */ getSkipped() { return Array.from(this.skipSet); } /** * Capture the current _nextCallDisabled set and clear it atomically. * This is used by hook methods to handle one-time guardrail disabling. */ captureAndClearNextCallDisabled() { const captured = this._nextCallDisabled; this._nextCallDisabled = null; return captured; } /** * Increment fetchEvents call counter for ratio tracking. */ incrementFetchEventsCount() { this._fetchEventsCount++; } /** * Increment subscribe call counter for ratio tracking. */ incrementSubscribeCount() { this._subscribeCount++; } /** * Check if fetchEvents usage ratio exceeds the threshold. * Returns true if more than 50% of calls are fetchEvents AND total calls > 6. */ shouldWarnAboutFetchEventsRatio() { const totalCalls = this._fetchEventsCount + this._subscribeCount; if (totalCalls <= 6) { return false; } const ratio = this._fetchEventsCount / totalCalls; return ratio > 0.5; } /** * Throw an error if the check should run. * Also logs to console.error in case the throw gets swallowed. * @param canDisable - If false, this is a fatal error that cannot be disabled (default: true) */ error(id, message, hint, canDisable = true) { if (!this.shouldCheck(id)) return; const fullMessage = this.formatMessage(id, "ERROR", message, hint, canDisable); console.error(fullMessage); throw new Error(fullMessage); } /** * Throw a warning if the check should run. * Also logs to console.error in case the throw gets swallowed. * Warnings can always be disabled. */ warn(id, message, hint) { if (!this.shouldCheck(id)) return; const fullMessage = this.formatMessage(id, "WARNING", message, hint, true); console.error(fullMessage); throw new Error(fullMessage); } /** * Format a guardrail message with helpful metadata. */ formatMessage(id, level, message, hint, canDisable = true) { let output4 = ` \u{1F916} AI_GUARDRAILS ${level}: ${message}`; if (hint) { output4 += ` \u{1F4A1} ${hint}`; } if (canDisable) { output4 += ` \u{1F507} To disable this check: ndk.guardrailOff('${id}').yourMethod() // For one call`; output4 += ` ndk.aiGuardrails.skip('${id}') // Permanently`; output4 += ` or set: ndk.aiGuardrails = { skip: new Set(['${id}']) }`; } return output4; } // ============================================================================ // Hook Methods - Type-safe, domain-organized insertion points // ============================================================================ /** * Called when NDK instance is created. * Checks for cache presence and other initialization concerns. */ ndkInstantiated(ndk) { if (!this.enabled) return; checkCachePresence(ndk, this.shouldCheck.bind(this)); } }; // ndk/core/src/utils/filter-validation.ts var NDKFilterValidationMode = /* @__PURE__ */ ((NDKFilterValidationMode2) => { NDKFilterValidationMode2["VALIDATE"] = "validate"; NDKFilterValidationMode2["FIX"] = "fix"; NDKFilterValidationMode2["IGNORE"] = "ignore"; return NDKFilterValidationMode2; })(NDKFilterValidationMode || {}); function processFilters(filters, mode = "validate" /* VALIDATE */, debug15, ndk) { if (mode === "ignore" /* IGNORE */) { return filters; } const issues = []; const processedFilters = filters.map((filter, index) => { if (ndk?.aiGuardrails.isEnabled()) { runAIGuardrailsForFilter(filter, index, ndk); } const result = processFilter(filter, mode, index, issues, debug15); return result; }); if (mode === "validate" /* VALIDATE */ && issues.length > 0) { throw new Error(`Invalid filter(s) detected: ${issues.join("\n")}`); } return processedFilters; } function processFilter(filter, mode, filterIndex, issues, debug15) { const isValidating = mode === "validate" /* VALIDATE */; const cleanedFilter = isValidating ? filter : { ...filter }; if (filter.ids) { const validIds = []; filter.ids.forEach((id, idx) => { if (id === void 0) { if (isValidating) { issues.push(`Filter[${filterIndex}].ids[${idx}] is undefined`); } else { debug15?.(`Fixed: Removed undefined value at ids[${idx}]`); } } else if (typeof id !== "string") { if (isValidating) { issues.push(`Filter[${filterIndex}].ids[${idx}] is not a string (got ${typeof id})`); } else { debug15?.(`Fixed: Removed non-string value at ids[${idx}] (was ${typeof id})`); } } else if (!isValidHex64(id)) { if (isValidating) { issues.push(`Filter[${filterIndex}].ids[${idx}] is not a valid 64-char hex string: "${id}"`); } else { debug15?.(`Fixed: Removed invalid hex string at ids[${idx}]`); } } else { validIds.push(id); } }); if (!isValidating) { cleanedFilter.ids = validIds.length > 0 ? validIds : void 0; } } if (filter.authors) { const validAuthors = []; filter.authors.forEach((author, idx) => { if (author === void 0) { if (isValidating) { issues.push(`Filter[${filterIndex}].authors[${idx}] is undefined`); } else { debug15?.(`Fixed: Removed undefined value at authors[${idx}]`); } } else if (typeof author !== "string") { if (isValidating) { issues.push(`Filter[${filterIndex}].authors[${idx}] is not a string (got ${typeof author})`); } else { debug15?.(`Fixed: Removed non-string value at authors[${idx}] (was ${typeof author})`); } } else if (!isValidHex64(author)) { if (isValidating) { issues.push( `Filter[${filterIndex}].authors[${idx}] is not a valid 64-char hex pubkey: "${author}"` ); } else { debug15?.(`Fixed: Removed invalid hex pubkey at authors[${idx}]`); } } else { validAuthors.push(author); } }); if (!isValidating) { cleanedFilter.authors = validAuthors.length > 0 ? validAuthors : void 0; } } if (filter.kinds) { const validKinds = []; filter.kinds.forEach((kind, idx) => { if (kind === void 0) { if (isValidating) { issues.push(`Filter[${filterIndex}].kinds[${idx}] is undefined`); } else { debug15?.(`Fixed: Removed undefined value at kinds[${idx}]`); } } else if (typeof kind !== "number") { if (isValidating) { issues.push(`Filter[${filterIndex}].kinds[${idx}] is not a number (got ${typeof kind})`); } else { debug15?.(`Fixed: Removed non-number value at kinds[${idx}] (was ${typeof kind})`); } } else if (!Number.isInteger(kind)) { if (isValidating) { issues.push(`Filter[${filterIndex}].kinds[${idx}] is not an integer: ${kind}`); } else { debug15?.(`Fixed: Removed non-integer value at kinds[${idx}]: ${kind}`); } } else if (kind < 0 || kind > 65535) { if (isValidating) { issues.push(`Filter[${filterIndex}].kinds[${idx}] is out of valid range (0-65535): ${kind}`); } else { debug15?.(`Fixed: Removed out-of-range kind at kinds[${idx}]: ${kind}`); } } else { validKinds.push(kind); } }); if (!isValidating) { cleanedFilter.kinds = validKinds.length > 0 ? validKinds : void 0; } } for (const key in filter) { if (key.startsWith("#") && key.length === 2) { const tagValues = filter[key]; if (Array.isArray(tagValues)) { const validValues = []; tagValues.forEach((value, idx) => { if (value === void 0) { if (isValidating) { issues.push(`Filter[${filterIndex}].${key}[${idx}] is undefined`); } else { debug15?.(`Fixed: Removed undefined value at ${key}[${idx}]`); } } else if (typeof value !== "string") { if (isValidating) { issues.push(`Filter[${filterIndex}].${key}[${idx}] is not a string (got ${typeof value})`); } else { debug15?.(`Fixed: Removed non-string value at ${key}[${idx}] (was ${typeof value})`); } } else { if ((key === "#e" || key === "#p") && !isValidHex64(value)) { if (isValidating) { issues.push( `Filter[${filterIndex}].${key}[${idx}] is not a valid 64-char hex string: "${value}"` ); } else { debug15?.(`Fixed: Removed invalid hex string at ${key}[${idx}]`); } } else { validValues.push(value); } } }); if (!isValidating) { cleanedFilter[key] = validValues.length > 0 ? validValues : void 0; } } } } if (!isValidating) { Object.keys(cleanedFilter).forEach((key) => { if (cleanedFilter[key] === void 0) { delete cleanedFilter[key]; } }); } return cleanedFilter; } function runAIGuardrailsForFilter(filter, filterIndex, ndk) { const guards = ndk.aiGuardrails; const filterPreview = JSON.stringify(filter, null, 2); if (Object.keys(filter).length === 1 && filter.limit !== void 0) { guards.error( GuardrailCheckId.FILTER_ONLY_LIMIT, `Filter[${filterIndex}] contains only 'limit' without any filtering criteria. \u{1F4E6} Your filter: ${filterPreview} \u26A0\uFE0F This will fetch random events from relays without any criteria.`, `Add filtering criteria: \u2705 { kinds: [1], limit: 10 } \u2705 { authors: [pubkey], limit: 10 } \u274C { limit: 10 }` ); } if (Object.keys(filter).length === 0) { guards.error( GuardrailCheckId.FILTER_EMPTY, `Filter[${filterIndex}] is empty. \u{1F4E6} Your filter: ${filterPreview} \u26A0\uFE0F This will request ALL events from relays, which is never what you want.`, `Add filtering criteria like 'kinds', 'authors', or tags.`, false // Fatal error - cannot be disabled ); } if (filter.since !== void 0 && filter.until !== void 0 && filter.since > filter.until) { const sinceDate = new Date(filter.since * 1e3).toISOString(); const untilDate = new Date(filter.until * 1e3).toISOString(); guards.error( GuardrailCheckId.FILTER_SINCE_AFTER_UNTIL, `Filter[${filterIndex}] has 'since' AFTER 'until'. \u{1F4E6} Your filter: ${filterPreview} \u274C since: ${filter.since} (${sinceDate}) \u274C until: ${filter.until} (${untilDate}) No events can match this time range!`, `'since' must be BEFORE 'until'. Both are Unix timestamps in seconds.`, false // Fatal error - cannot be disabled ); } const bech32Regex = /^n(addr|event|ote|pub|profile)1/; if (filter.ids) { filter.ids.forEach((id, idx) => { if (typeof id === "string") { if (bech32Regex.test(id)) { guards.error( GuardrailCheckId.FILTER_BECH32_IN_ARRAY, `Filter[${filterIndex}].ids[${idx}] contains bech32: "${id}". IDs must be hex, not bech32.`, `Use filterFromId() to decode bech32 first: import { filterFromId } from "@nostr-dev-kit/ndk"`, false // Fatal error - cannot be disabled ); } else if (!isValidHex64(id)) { guards.error( GuardrailCheckId.FILTER_INVALID_HEX, `Filter[${filterIndex}].ids[${idx}] is not a valid 64-char hex string: "${id}"`, `Event IDs must be 64-character hexadecimal strings. Invalid IDs often come from corrupted data in user-generated lists. Always validate hex strings before using them in filters: const validIds = ids.filter(id => /^[0-9a-f]{64}$/i.test(id));`, false // Fatal error - cannot be disabled ); } } }); } if (filter.authors) { filter.authors.forEach((author, idx) => { if (typeof author === "string") { if (bech32Regex.test(author)) { guards.error( GuardrailCheckId.FILTER_BECH32_IN_ARRAY, `Filter[${filterIndex}].authors[${idx}] contains bech32: "${author}". Authors must be hex pubkeys, not npub.`, `Use ndkUser.pubkey instead. Example: { authors: [ndkUser.pubkey] }`, false // Fatal error - cannot be disabled ); } else if (!isValidHex64(author)) { guards.error( GuardrailCheckId.FILTER_INVALID_HEX, `Filter[${filterIndex}].authors[${idx}] is not a valid 64-char hex pubkey: "${author}"`, `Kind:3 follow lists can contain invalid entries like labels ("Follow List"), partial strings ("highlig"), or other corrupted data. You MUST validate all pubkeys before using them in filters. Example: const validPubkeys = pubkeys.filter(p => /^[0-9a-f]{64}$/i.test(p)); ndk.subscribe({ authors: validPubkeys, kinds: [1] });`, false // Fatal error - cannot be disabled ); } } }); } for (const key in filter) { if (key.startsWith("#") && key.length === 2) { const tagValues = filter[key]; if (Array.isArray(tagValues)) { tagValues.forEach((value, idx) => { if (typeof value === "string") { if (key === "#e" || key === "#p") { if (bech32Regex.test(value)) { guards.error( GuardrailCheckId.FILTER_BECH32_IN_ARRAY, `Filter[${filterIndex}].${key}[${idx}] contains bech32: "${value}". Tag values must be decoded.`, `Use filterFromId() or nip19.decode() to get the hex value first.`, false // Fatal error - cannot be disabled ); } else if (!isValidHex64(value)) { guards.error( GuardrailCheckId.FILTER_INVALID_HEX, `Filter[${filterIndex}].${key}[${idx}] is not a valid 64-char hex string: "${value}"`, `${key === "#e" ? "Event IDs" : "Public keys"} in tag filters must be 64-character hexadecimal strings. Kind:3 follow lists and other user-generated content can contain invalid data. Always filter before using: const validValues = values.filter(v => /^[0-9a-f]{64}$/i.test(v));`, false // Fatal error - cannot be disabled ); } } } }); } } } if (filter["#a"]) { const aTags = filter["#a"]; aTags?.forEach((aTag, idx) => { if (typeof aTag === "string") { if (!/^\d+:[0-9a-f]{64}:.*$/.test(aTag)) { guards.error( GuardrailCheckId.FILTER_INVALID_A_TAG, `Filter[${filterIndex}].#a[${idx}] has invalid format: "${aTag}". Must be "kind:pubkey:d-tag".`, `Example: "30023:fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52:my-article"`, false // Fatal error - cannot be disabled ); } else { const kind = Number.parseInt(aTag.split(":")[0], 10); if (kind < 3e4 || kind > 39999) { guards.error( GuardrailCheckId.FILTER_INVALID_A_TAG, `Filter[${filterIndex}].#a[${idx}] uses non-addressable kind ${kind}: "${aTag}". #a filters are only for addressable events (kinds 30000-39999).`, `Addressable events include: \u2022 30000-30039: Parameterized Replaceable Events (profiles, settings, etc.) \u2022 30040-39999: Other addressable events For regular events (kind ${kind}), use: \u2022 #e filter for specific event IDs \u2022 kinds + authors filters for event queries`, false // Fatal error - cannot be disabled ); } } } }); } if (filter["#t"]) { const tTags = filter["#t"]; tTags?.forEach((tag, idx) => { if (typeof tag === "string" && tag.startsWith("#")) { guards.error( GuardrailCheckId.FILTER_HASHTAG_WITH_PREFIX, `Filter[${filterIndex}].#t[${idx}] contains hashtag with # prefix: "${tag}". Hashtag values should NOT include the # symbol.`, `Remove the # prefix from hashtag filters: \u2705 { "#t": ["nostr"] } \u274C { "#t": ["#nostr"] }`, false // Fatal error - cannot be disabled ); } }); } } // ndk/core/src/subscription/utils.ts var import_nostr_tools4 = __toESM(require_nostr_tools()); var MAX_SUBID_LENGTH = 20; function queryFullyFilled(subscription) { if (filterIncludesIds(subscription.filter)) { if (resultHasAllRequestedIds(subscription)) { return true; } } return false; } function compareFilter(filter1, filter2) { if (Object.keys(filter1).length !== Object.keys(filter2).length) return false; for (const [key, value] of Object.entries(filter1)) { const valuesInFilter2 = filter2[key]; if (!valuesInFilter2) return false; if (Array.isArray(value) && Array.isArray(valuesInFilter2)) { const v6 = value; for (const valueInFilter2 of valuesInFilter2) { const val = valueInFilter2; if (!v6.includes(val)) { return false; } } } else { if (valuesInFilter2 !== value) return false; } } return true; } function filterIncludesIds(filter) { return !!filter.ids; } function resultHasAllRequestedIds(subscription) { const ids = subscription.filter.ids; return !!ids && ids.length === subscription.eventFirstSeen.size; } function generateSubId(subscriptions, filters) { const subIds = subscriptions.map((sub) => sub.subId).filter(Boolean); const subIdParts = []; const filterNonKindKeys = /* @__PURE__ */ new Set(); const filterKinds = /* @__PURE__ */ new Set(); if (subIds.length > 0) { subIdParts.push(Array.from(new Set(subIds)).join(",")); } else { for (const filter of filters) { for (const key of Object.keys(filter)) { if (key === "kinds") { filter.kinds?.forEach((k2) => filterKinds.add(k2)); } else { filterNonKindKeys.add(key); } } } if (filterKinds.size > 0) { subIdParts.push(`kinds:${Array.from(filterKinds).join(",")}`); } if (filterNonKindKeys.size > 0) { subIdParts.push(Array.from(filterNonKindKeys).join(",")); } } let subId = subIdParts.join("-"); if (subId.length > MAX_SUBID_LENGTH) subId = subId.substring(0, MAX_SUBID_LENGTH); subId += `-${Math.floor(Math.random() * 999).toString()}`; return subId; } function filterForEventsTaggingId(id) { try { const decoded = import_nostr_tools4.nip19.decode(id); switch (decoded.type) { case "naddr": return { "#a": [`${decoded.data.kind}:${decoded.data.pubkey}:${decoded.data.identifier}`] }; case "nevent": return { "#e": [decoded.data.id] }; case "note": return { "#e": [decoded.data] }; case "nprofile": return { "#p": [decoded.data.pubkey] }; case "npub": return { "#p": [decoded.data] }; } } catch { } } function filterAndRelaySetFromBech32(bech323, ndk) { const filter = filterFromId(bech323); const relays = relaysFromBech32(bech323, ndk); if (relays.length === 0) return { filter }; return { filter, relaySet: new NDKRelaySet(new Set(relays), ndk) }; } function filterFromId(id) { let decoded; if (id.match(NIP33_A_REGEX)) { const [kind, pubkey, identifier] = id.split(":"); const filter = { authors: [pubkey], kinds: [Number.parseInt(kind)] }; if (identifier) { filter["#d"] = [identifier]; } return filter; } if (id.match(BECH32_REGEX)) { try { decoded = import_nostr_tools4.nip19.decode(id); switch (decoded.type) { case "nevent": { const filter = { ids: [decoded.data.id] }; if (decoded.data.author) filter.authors = [decoded.data.author]; if (decoded.data.kind) filter.kinds = [decoded.data.kind]; return filter; } case "note": return { ids: [decoded.data] }; case "naddr": { const filter = { authors: [decoded.data.pubkey], kinds: [decoded.data.kind] }; if (decoded.data.identifier) filter["#d"] = [decoded.data.identifier]; return filter; } } } catch (e2) { console.error("Error decoding", id, e2); } } return { ids: [id] }; } function isNip33AValue(value) { return value.match(NIP33_A_REGEX) !== null; } var NIP33_A_REGEX = /^(\d+):([0-9A-Fa-f]+)(?::(.*))?$/; var BECH32_REGEX = /^n(event|ote|profile|pub|addr)1[\d\w]+$/; function relaysFromBech32(bech323, ndk) { try { const decoded = import_nostr_tools4.nip19.decode(bech323); if (["naddr", "nevent"].includes(decoded?.type)) { const data = decoded.data; if (data?.relays) { return data.relays.map((r) => new NDKRelay(r, ndk.relayAuthDefaultPolicy, ndk)); } } } catch (_e2) { } return []; } // ndk/core/src/subscription/index.ts var NDKSubscriptionCacheUsage = /* @__PURE__ */ ((NDKSubscriptionCacheUsage3) => { NDKSubscriptionCacheUsage3["ONLY_CACHE"] = "ONLY_CACHE"; NDKSubscriptionCacheUsage3["CACHE_FIRST"] = "CACHE_FIRST"; NDKSubscriptionCacheUsage3["PARALLEL"] = "PARALLEL"; NDKSubscriptionCacheUsage3["ONLY_RELAY"] = "ONLY_RELAY"; return NDKSubscriptionCacheUsage3; })(NDKSubscriptionCacheUsage || {}); var defaultOpts = { closeOnEose: false, cacheUsage: "CACHE_FIRST" /* CACHE_FIRST */, dontSaveToCache: false, groupable: true, groupableDelay: 10, groupableDelayType: "at-most", cacheUnconstrainFilter: ["limit", "since", "until"], includeMuted: false }; var NDKSubscription = class extends import_tseep4.EventEmitter { constructor(ndk, filters, opts, subId) { super(); __publicField(this, "subId"); __publicField(this, "filters"); __publicField(this, "opts"); __publicField(this, "pool"); __publicField(this, "skipVerification", false); __publicField(this, "skipValidation", false); __publicField(this, "exclusiveRelay", false); /** * Tracks the filters as they are executed on each relay */ __publicField(this, "relayFilters"); __publicField(this, "relaySet"); __publicField(this, "ndk"); __publicField(this, "debug"); /** * Events that have been seen by the subscription, with the time they were first seen. */ __publicField(this, "eventFirstSeen", /* @__PURE__ */ new Map()); /** * Relays that have sent an EOSE. */ __publicField(this, "eosesSeen", /* @__PURE__ */ new Set()); /** * The time the last event was received by the subscription. * This is used to calculate when EOSE should be emitted. */ __publicField(this, "lastEventReceivedAt"); /** * The most recent event timestamp from cache results. * This is used for addSinceFromCache functionality. */ __publicField(this, "mostRecentCacheEventTimestamp"); __publicField(this, "internalId"); /** * Whether the subscription should close when all relays have reached the end of the event stream. */ __publicField(this, "closeOnEose"); /** * Pool monitor callback */ __publicField(this, "poolMonitor"); __publicField(this, "skipOptimisticPublishEvent", false); /** * Filters to remove when querying the cache. */ __publicField(this, "cacheUnconstrainFilter"); __publicField(this, "onStopped"); // EOSE handling __publicField(this, "eoseTimeout"); __publicField(this, "eosed", false); this.ndk = ndk; this.opts = { ...defaultOpts, ...opts || {} }; this.pool = this.opts.pool || ndk.pool; const rawFilters = Array.isArray(filters) ? filters : [filters]; const validationMode = ndk.filterValidationMode === "validate" ? "validate" /* VALIDATE */ : ndk.filterValidationMode === "fix" ? "fix" /* FIX */ : "ignore" /* IGNORE */; this.filters = processFilters(rawFilters, validationMode, ndk.debug, ndk); if (this.filters.length === 0) { throw new Error("Subscription must have at least one filter"); } this.subId = subId || this.opts.subId; this.internalId = Math.random().toString(36).substring(7); this.debug = ndk.debug.extend(`subscription[${this.opts.subId ?? this.internalId}]`); if (this.opts.relaySet) { this.relaySet = this.opts.relaySet; } else if (this.opts.relayUrls) { this.relaySet = NDKRelaySet.fromRelayUrls(this.opts.relayUrls, this.ndk); } this.skipVerification = this.opts.skipVerification || false; this.skipValidation = this.opts.skipValidation || false; this.closeOnEose = this.opts.closeOnEose || false; this.skipOptimisticPublishEvent = this.opts.skipOptimisticPublishEvent || false; this.cacheUnconstrainFilter = this.opts.cacheUnconstrainFilter; this.exclusiveRelay = this.opts.exclusiveRelay || false; if (this.opts.onEvent) { this.on("event", this.opts.onEvent); } if (this.opts.onEose) { this.on("eose", this.opts.onEose); } if (this.opts.onClose) { this.on("close", this.opts.onClose); } } /** * Returns the relays that have not yet sent an EOSE. */ relaysMissingEose() { if (!this.relayFilters) return []; const relaysMissingEose = Array.from(this.relayFilters?.keys()).filter( (url) => !this.eosesSeen.has(this.pool.getRelay(url, false, false)) ); return relaysMissingEose; } /** * Provides access to the first filter of the subscription for * backwards compatibility. */ get filter() { return this.filters[0]; } get groupableDelay() { if (!this.isGroupable()) return void 0; return this.opts?.groupableDelay; } get groupableDelayType() { return this.opts?.groupableDelayType || "at-most"; } isGroupable() { return this.opts?.groupable || false; } shouldQueryCache() { if (this.opts.addSinceFromCache) return true; if (this.opts?.cacheUsage === "ONLY_RELAY" /* ONLY_RELAY */) return false; const hasNonEphemeralKind = this.filters.some((f) => f.kinds?.some((k2) => kindIsEphemeral(k2))); if (hasNonEphemeralKind) return true; return true; } shouldQueryRelays() { return this.opts?.cacheUsage !== "ONLY_CACHE" /* ONLY_CACHE */; } shouldWaitForCache() { if (this.opts.addSinceFromCache) return true; return ( // Must want to close on EOSE; subscriptions // that want to receive further updates must // always hit the relay !!this.opts.closeOnEose && // Cache adapter must claim to be fast !!this.ndk.cacheAdapter?.locking && // If explicitly told to run in parallel, then // we should not wait for the cache this.opts.cacheUsage !== "PARALLEL" /* PARALLEL */ ); } /** * Start the subscription. This is the main method that should be called * after creating a subscription. * * @param emitCachedEvents - Whether to emit events coming from a synchronous cache * * When using a synchronous cache, the events will be returned immediately * by this function. If you will use those returned events, you should * set emitCachedEvents to false to prevent seeing them as duplicate events. */ start(emitCachedEvents = true) { let cacheResult; const updateStateFromCacheResults = (events) => { if (events.length === 0) { if (!emitCachedEvents) cacheResult = events; return; } if (!emitCachedEvents) { let maxTimestamp2 = this.mostRecentCacheEventTimestamp || 0; for (const event of events) { event.ndk = this.ndk; if (event.created_at && event.created_at > maxTimestamp2) { maxTimestamp2 = event.created_at; } } this.mostRecentCacheEventTimestamp = maxTimestamp2; cacheResult = events; return; } let maxTimestamp = this.mostRecentCacheEventTimestamp || 0; for (const event of events) { if (event.created_at && event.created_at > maxTimestamp) { maxTimestamp = event.created_at; } } this.mostRecentCacheEventTimestamp = maxTimestamp; for (const event of events) { this.eventReceived(event, void 0, true, false); } }; const loadFromRelays = () => { if (this.shouldQueryRelays()) { this.startWithRelays(); this.startPoolMonitor(); } else { this.emit("eose", this); } }; if (this.shouldQueryCache()) { cacheResult = this.startWithCache(); if (cacheResult instanceof Promise) { if (this.shouldWaitForCache()) { cacheResult.then((events) => { if (this.opts.onEvents) { let maxTimestamp = this.mostRecentCacheEventTimestamp || 0; for (const event of events) { event.ndk = this.ndk; if (event.created_at && event.created_at > maxTimestamp) { maxTimestamp = event.created_at; } } this.mostRecentCacheEventTimestamp = maxTimestamp; this.opts.onEvents(events); } else { updateStateFromCacheResults(events); } if (queryFullyFilled(this)) { this.emit("eose", this); return; } loadFromRelays(); }); return null; } cacheResult.then((events) => { if (this.opts.onEvents) { let maxTimestamp = this.mostRecentCacheEventTimestamp || 0; for (const event of events) { event.ndk = this.ndk; if (event.created_at && event.created_at > maxTimestamp) { maxTimestamp = event.created_at; } } this.mostRecentCacheEventTimestamp = maxTimestamp; this.opts.onEvents(events); } else { updateStateFromCacheResults(events); } if (!this.shouldQueryRelays()) { this.emit("eose", this); } }); if (this.shouldQueryRelays()) { loadFromRelays(); } return null; } updateStateFromCacheResults(cacheResult); if (queryFullyFilled(this)) { this.emit("eose", this); } else { loadFromRelays(); } return cacheResult; } loadFromRelays(); return null; } /** * We want to monitor for new relays that are coming online, in case * they should be part of this subscription. */ startPoolMonitor() { const _d = this.debug.extend("pool-monitor"); this.poolMonitor = (relay) => { if (this.relayFilters?.has(relay.url)) return; const calc = calculateRelaySetsFromFilters(this.ndk, this.filters, this.pool, this.opts.relayGoalPerAuthor); if (calc.get(relay.url)) { this.relayFilters?.set(relay.url, this.filters); relay.subscribe(this, this.filters); } }; this.pool.on("relay:connect", this.poolMonitor); } stop() { this.emit("close", this); this.poolMonitor && this.pool.off("relay:connect", this.poolMonitor); this.onStopped?.(); } /** * @returns Whether the subscription has an authors filter. */ hasAuthorsFilter() { return this.filters.some((f) => f.authors?.length); } startWithCache() { if (this.ndk.cacheAdapter?.query) { return this.ndk.cacheAdapter.query(this); } return []; } /** * Find available relays that should be part of this subscription and execute in them. * * Note that this is executed in addition to using the pool monitor, so even if the relay set * that is computed (i.e. we don't have any relays available), when relays come online, we will * check if we need to execute in them. */ startWithRelays() { let filters = this.filters; if (this.opts.addSinceFromCache && this.mostRecentCacheEventTimestamp) { const sinceTimestamp = this.mostRecentCacheEventTimestamp + 1; filters = filters.map((filter) => ({ ...filter, since: Math.max(filter.since || 0, sinceTimestamp) })); } if (!this.relaySet || this.relaySet.relays.size === 0) { this.relayFilters = calculateRelaySetsFromFilters( this.ndk, filters, this.pool, this.opts.relayGoalPerAuthor ); } else { this.relayFilters = /* @__PURE__ */ new Map(); for (const relay of this.relaySet.relays) { this.relayFilters.set(relay.url, filters); } } for (const [relayUrl, filters2] of this.relayFilters) { const relay = this.pool.getRelay(relayUrl, true, true, filters2); relay.subscribe(this, filters2); } } /** * Refresh relay connections when outbox data becomes available. * This recalculates which relays should receive this subscription and * connects to any newly discovered relays. */ refreshRelayConnections() { if (this.relaySet && this.relaySet.relays.size > 0) { return; } const updatedRelaySets = calculateRelaySetsFromFilters( this.ndk, this.filters, this.pool, this.opts.relayGoalPerAuthor ); for (const [relayUrl, filters] of updatedRelaySets) { if (!this.relayFilters?.has(relayUrl)) { this.relayFilters?.set(relayUrl, filters); const relay = this.pool.getRelay(relayUrl, true, true, filters); relay.subscribe(this, filters); } } } // EVENT handling /** * Called when an event is received from a relay or the cache * @param event * @param relay * @param fromCache Whether the event was received from the cache * @param optimisticPublish Whether this event is coming from an optimistic publish */ eventReceived(event, relay, fromCache = false, optimisticPublish = false) { const eventId = event.id; const eventAlreadySeen = this.eventFirstSeen.has(eventId); let ndkEvent; if (event instanceof NDKEvent) ndkEvent = event; if (!eventAlreadySeen) { if (this.ndk.futureTimestampGrace !== void 0 && event.created_at) { const currentTime = Math.floor(Date.now() / 1e3); const timeDifference = event.created_at - currentTime; if (timeDifference > this.ndk.futureTimestampGrace) { this.debug( "Event discarded: timestamp %d is %d seconds in the future (grace: %d seconds)", event.created_at, timeDifference, this.ndk.futureTimestampGrace ); return; } } ndkEvent ?? (ndkEvent = new NDKEvent(this.ndk, event)); ndkEvent.ndk = this.ndk; ndkEvent.relay = relay; if (!fromCache && !optimisticPublish) { if (!this.skipValidation) { if (!ndkEvent.isValid) { this.debug("Event failed validation %s from relay %s", eventId, relay?.url); return; } } if (relay) { const shouldVerify = relay.shouldValidateEvent(); if (shouldVerify && !this.skipVerification) { ndkEvent.relay = relay; if (this.ndk.asyncSigVerification) { ndkEvent.verifySignature(true); } else { if (!ndkEvent.verifySignature(true)) { this.debug("Event failed signature validation", event); this.ndk.reportInvalidSignature(ndkEvent, relay); return; } relay.addValidatedEvent(); } } else { relay.addNonValidatedEvent(); } } if (this.ndk.cacheAdapter && !this.opts.dontSaveToCache && !kindIsEphemeral(ndkEvent.kind) && !fromCache) { this.ndk.cacheAdapter.setEvent(ndkEvent, this.filters, relay); } } if (!this.opts.includeMuted && this.ndk.muteFilter && this.ndk.muteFilter(ndkEvent)) { this.debug("Event muted, skipping"); return; } if (!optimisticPublish || this.skipOptimisticPublishEvent !== true) { this.emitEvent(this.opts?.wrap ?? false, ndkEvent, relay, fromCache, optimisticPublish); this.eventFirstSeen.set(eventId, Date.now()); } } else { const timeSinceFirstSeen = Date.now() - (this.eventFirstSeen.get(eventId) || 0); this.emit("event:dup", event, relay, timeSinceFirstSeen, this, fromCache, optimisticPublish); if (this.opts?.onEventDup) { this.opts.onEventDup(event, relay, timeSinceFirstSeen, this, fromCache, optimisticPublish); } if (!fromCache && !optimisticPublish && relay && this.ndk.cacheAdapter?.setEventDup && !this.opts.dontSaveToCache) { ndkEvent ?? (ndkEvent = event instanceof NDKEvent ? event : new NDKEvent(this.ndk, event)); this.ndk.cacheAdapter.setEventDup(ndkEvent, relay); } if (relay) { const signature = verifiedSignatures.get(eventId); if (signature && typeof signature === "string") { if (event.sig === signature) { relay.addValidatedEvent(); } else { const eventToReport = event instanceof NDKEvent ? event : new NDKEvent(this.ndk, event); this.ndk.reportInvalidSignature(eventToReport, relay); } } } } this.lastEventReceivedAt = Date.now(); } /** * Optionally wraps, sync or async, and emits the event (if one comes back from the wrapper) */ emitEvent(wrap, evt, relay, fromCache, optimisticPublish) { const wrapped = wrap ? wrapEvent(evt) : evt; if (wrapped instanceof Promise) { wrapped.then((e2) => this.emitEvent(false, e2, relay, fromCache, optimisticPublish)); } else if (wrapped) { this.emit("event", wrapped, relay, this, fromCache, optimisticPublish); } } closedReceived(relay, reason) { this.emit("closed", relay, reason); } eoseReceived(relay) { this.eosesSeen.add(relay); let lastEventSeen = this.lastEventReceivedAt ? Date.now() - this.lastEventReceivedAt : void 0; const hasSeenAllEoses = this.eosesSeen.size === this.relayFilters?.size; const queryFilled = queryFullyFilled(this); const performEose = (reason) => { if (this.eosed) return; if (this.eoseTimeout) clearTimeout(this.eoseTimeout); this.emit("eose", this); this.eosed = true; if (this.opts?.closeOnEose) this.stop(); }; if (queryFilled || hasSeenAllEoses) { performEose("query filled or seen all"); } else if (this.relayFilters) { let timeToWaitForNextEose = 1e3; const connectedRelays = new Set(this.pool.connectedRelays().map((r) => r.url)); const connectedRelaysWithFilters = Array.from(this.relayFilters.keys()).filter( (url) => connectedRelays.has(url) ); if (connectedRelaysWithFilters.length === 0) { this.debug( "No connected relays, waiting for all relays to connect", Array.from(this.relayFilters.keys()).join(", ") ); return; } const percentageOfRelaysThatHaveSentEose = this.eosesSeen.size / connectedRelaysWithFilters.length; if (this.eosesSeen.size >= 2 && percentageOfRelaysThatHaveSentEose >= 0.5) { timeToWaitForNextEose = timeToWaitForNextEose * (1 - percentageOfRelaysThatHaveSentEose); if (timeToWaitForNextEose === 0) { performEose("time to wait was 0"); return; } if (this.eoseTimeout) clearTimeout(this.eoseTimeout); const sendEoseTimeout = () => { lastEventSeen = this.lastEventReceivedAt ? Date.now() - this.lastEventReceivedAt : void 0; if (lastEventSeen !== void 0 && lastEventSeen < 20) { this.eoseTimeout = setTimeout(sendEoseTimeout, timeToWaitForNextEose); } else { performEose(`send eose timeout: ${timeToWaitForNextEose}`); } }; this.eoseTimeout = setTimeout(sendEoseTimeout, timeToWaitForNextEose); } } } }; var kindIsEphemeral = (kind) => kind >= 2e4 && kind < 3e4; // ndk/core/src/user/follows.ts async function follows(opts, outbox, kind = 3 /* Contacts */) { if (!this.ndk) throw new Error("NDK not set"); const contactListEvent = await this.ndk.fetchEvent( { kinds: [kind], authors: [this.pubkey] }, opts || { groupable: false } ); if (contactListEvent) { const pubkeys = /* @__PURE__ */ new Set(); contactListEvent.tags.forEach((tag) => { if (tag[0] === "p" && tag[1] && isValidPubkey(tag[1])) { pubkeys.add(tag[1]); } }); if (outbox) { this.ndk?.outboxTracker?.trackUsers(Array.from(pubkeys)); } return [...pubkeys].reduce((acc, pubkey) => { const user = new NDKUser({ pubkey }); user.ndk = this.ndk; acc.add(user); return acc; }, /* @__PURE__ */ new Set()); } return /* @__PURE__ */ new Set(); } // ndk/core/src/user/nip05.ts var NIP05_REGEX = /^(?:([\w.+-]+)@)?([\w.-]+)$/; async function getNip05For(ndk, fullname, _fetch5 = fetch, fetchOpts = {}) { return await ndk.queuesNip05.add({ id: fullname, func: async () => { if (ndk.cacheAdapter?.loadNip05) { const profile = await ndk.cacheAdapter.loadNip05(fullname); if (profile !== "missing") { if (profile) { const user = new NDKUser({ pubkey: profile.pubkey, relayUrls: profile.relays, nip46Urls: profile.nip46 }); user.ndk = ndk; return user; } if (fetchOpts.cache !== "no-cache") { return null; } } } const match = fullname.match(NIP05_REGEX); if (!match) return null; const [_2, name = "_", domain] = match; try { const res = await _fetch5(`https://${domain}/.well-known/nostr.json?name=${name}`, fetchOpts); const { names, relays, nip46 } = parseNIP05Result(await res.json()); const pubkey = names[name.toLowerCase()]; let profile = null; if (pubkey) { profile = { pubkey, relays: relays?.[pubkey], nip46: nip46?.[pubkey] }; } if (ndk?.cacheAdapter?.saveNip05) { ndk.cacheAdapter.saveNip05(fullname, profile); } return profile; } catch (_e2) { if (ndk?.cacheAdapter?.saveNip05) { ndk?.cacheAdapter.saveNip05(fullname, null); } console.error("Failed to fetch NIP05 for", fullname, _e2); return null; } } }); } function parseNIP05Result(json) { const result = { names: {} }; for (const [name, pubkey] of Object.entries(json.names)) { if (typeof name === "string" && typeof pubkey === "string") { result.names[name.toLowerCase()] = pubkey; } } if (json.relays) { result.relays = {}; for (const [pubkey, relays] of Object.entries(json.relays)) { if (typeof pubkey === "string" && Array.isArray(relays)) { result.relays[pubkey] = relays.filter((relay) => typeof relay === "string"); } } } if (json.nip46) { result.nip46 = {}; for (const [pubkey, nip46] of Object.entries(json.nip46)) { if (typeof pubkey === "string" && Array.isArray(nip46)) { result.nip46[pubkey] = nip46.filter((relay) => typeof relay === "string"); } } } return result; } // ndk/core/src/user/profile.ts function profileFromEvent(event) { const profile = {}; let payload; try { payload = JSON.parse(event.content); } catch (error) { throw new Error(`Failed to parse profile event: ${error}`); } profile.profileEvent = JSON.stringify(event.rawEvent()); for (const key of Object.keys(payload)) { switch (key) { case "name": profile.name = payload.name; break; case "display_name": profile.displayName = payload.display_name; break; case "image": case "picture": profile.picture = payload.picture || payload.image; profile.image = profile.picture; break; case "banner": profile.banner = payload.banner; break; case "bio": profile.bio = payload.bio; break; case "nip05": profile.nip05 = payload.nip05; break; case "lud06": profile.lud06 = payload.lud06; break; case "lud16": profile.lud16 = payload.lud16; break; case "about": profile.about = payload.about; break; case "website": profile.website = payload.website; break; default: profile[key] = payload[key]; break; } } profile.created_at = event.created_at; return profile; } function serializeProfile(profile) { const payload = {}; for (const [key, val] of Object.entries(profile)) { switch (key) { case "username": case "name": payload.name = val; break; case "displayName": payload.display_name = val; break; case "image": case "picture": payload.picture = val; break; case "bio": case "about": payload.about = val; break; default: payload[key] = val; break; } } return JSON.stringify(payload); } // ndk/core/src/user/index.ts var NDKUser = class _NDKUser3 { constructor(opts) { __publicField(this, "ndk"); __publicField(this, "profile"); __publicField(this, "profileEvent"); __publicField(this, "_npub"); __publicField(this, "_pubkey"); __publicField(this, "relayUrls", []); __publicField(this, "nip46Urls", []); /** * Returns a set of users that this user follows. * * @deprecated Use followSet instead */ __publicField(this, "follows", follows.bind(this)); if (opts.npub) this._npub = opts.npub; if (opts.hexpubkey) this._pubkey = opts.hexpubkey; if (opts.pubkey) this._pubkey = opts.pubkey; if (opts.relayUrls) this.relayUrls = opts.relayUrls; if (opts.nip46Urls) this.nip46Urls = opts.nip46Urls; if (opts.nprofile) { try { const decoded = import_nostr_tools5.nip19.decode(opts.nprofile); if (decoded.type === "nprofile") { this._pubkey = decoded.data.pubkey; if (decoded.data.relays && decoded.data.relays.length > 0) { this.relayUrls.push(...decoded.data.relays); } } } catch (e2) { console.error("Failed to decode nprofile", e2); } } } get npub() { if (!this._npub) { if (!this._pubkey) throw new Error("pubkey not set"); this._npub = import_nostr_tools5.nip19.npubEncode(this.pubkey); } return this._npub; } get nprofile() { const relays = this.profileEvent?.onRelays?.map((r) => r.url); return import_nostr_tools5.nip19.nprofileEncode({ pubkey: this.pubkey, relays }); } set npub(npub3) { this._npub = npub3; } /** * Get the user's pubkey * @returns {string} The user's pubkey */ get pubkey() { if (!this._pubkey) { if (!this._npub) throw new Error("npub not set"); this._pubkey = import_nostr_tools5.nip19.decode(this.npub).data; } return this._pubkey; } /** * Set the user's pubkey * @param pubkey {string} The user's pubkey */ set pubkey(pubkey) { this._pubkey = pubkey; } /** * Equivalent to NDKEvent.filters(). * @returns {NDKFilter} */ filter() { return { "#p": [this.pubkey] }; } /** * Gets NIP-57 and NIP-61 information that this user has signaled * * @param getAll {boolean} Whether to get all zap info or just the first one */ async getZapInfo(timeoutMs) { if (!this.ndk) throw new Error("No NDK instance found"); const promiseWithTimeout = async (promise) => { if (!timeoutMs) return promise; let timeoutId; const timeoutPromise = new Promise((_2, reject) => { timeoutId = setTimeout(() => reject(new Error("Timeout")), timeoutMs); }); try { const result = await Promise.race([promise, timeoutPromise]); if (timeoutId) clearTimeout(timeoutId); return result; } catch (e2) { if (e2 instanceof Error && e2.message === "Timeout") { try { const result = await promise; return result; } catch (_originalError) { return void 0; } } return void 0; } }; const [userProfile, mintListEvent] = await Promise.all([ promiseWithTimeout(this.fetchProfile()), promiseWithTimeout( this.ndk.fetchEvent({ kinds: [10019 /* CashuMintList */], authors: [this.pubkey] }) ) ]); const res = /* @__PURE__ */ new Map(); if (mintListEvent) { const mintList = NDKCashuMintList.from(mintListEvent); if (mintList.mints.length > 0) { res.set("nip61", { mints: mintList.mints, relays: mintList.relays, p2pk: mintList.p2pk }); } } if (userProfile) { const { lud06, lud16 } = userProfile; res.set("nip57", { lud06, lud16 }); } return res; } /** * Instantiate an NDKUser from a NIP-05 string * @param nip05Id {string} The user's NIP-05 * @param ndk {NDK} An NDK instance * @param skipCache {boolean} Whether to skip the cache or not * @returns {NDKUser | undefined} An NDKUser if one is found for the given NIP-05, undefined otherwise. */ static async fromNip05(nip05Id, ndk, skipCache = false) { if (!ndk) throw new Error("No NDK instance found"); const opts = {}; if (skipCache) opts.cache = "no-cache"; const profile = await getNip05For(ndk, nip05Id, ndk?.httpFetch, opts); if (profile) { const user = new _NDKUser3({ pubkey: profile.pubkey, relayUrls: profile.relays, nip46Urls: profile.nip46 }); user.ndk = ndk; return user; } } /** * Fetch a user's profile * @param opts {NDKSubscriptionOptions} A set of NDKSubscriptionOptions * @param storeProfileEvent {boolean} Whether to store the profile event or not * @returns User Profile */ async fetchProfile(opts, storeProfileEvent = false) { if (!this.ndk) throw new Error("NDK not set"); let setMetadataEvent = null; if (this.ndk.cacheAdapter && (this.ndk.cacheAdapter.fetchProfile || this.ndk.cacheAdapter.fetchProfileSync) && opts?.cacheUsage !== "ONLY_RELAY" /* ONLY_RELAY */) { let profile = null; if (this.ndk.cacheAdapter.fetchProfileSync) { profile = this.ndk.cacheAdapter.fetchProfileSync(this.pubkey); } else if (this.ndk.cacheAdapter.fetchProfile) { profile = await this.ndk.cacheAdapter.fetchProfile(this.pubkey); } if (profile) { this.profile = profile; return profile; } } opts ?? (opts = {}); opts.cacheUsage ?? (opts.cacheUsage = "ONLY_RELAY" /* ONLY_RELAY */); opts.closeOnEose ?? (opts.closeOnEose = true); opts.groupable ?? (opts.groupable = true); opts.groupableDelay ?? (opts.groupableDelay = 25); if (!setMetadataEvent) { setMetadataEvent = await this.ndk.fetchEvent( { kinds: [0], authors: [this.pubkey] }, opts ); } if (!setMetadataEvent) return null; this.profile = profileFromEvent(setMetadataEvent); if (storeProfileEvent && this.profile && this.ndk.cacheAdapter && this.ndk.cacheAdapter.saveProfile) { this.ndk.cacheAdapter.saveProfile(this.pubkey, this.profile); } return this.profile; } /** * Returns a set of pubkeys that this user follows. * * @param opts - NDKSubscriptionOptions * @param outbox - boolean * @param kind - number */ async followSet(opts, outbox, kind = 3 /* Contacts */) { const follows4 = await this.follows(opts, outbox, kind); return new Set(Array.from(follows4).map((f) => f.pubkey)); } /** @deprecated Use referenceTags instead. */ /** * Get the tag that can be used to reference this user in an event * @returns {NDKTag} an NDKTag */ tagReference() { return ["p", this.pubkey]; } /** * Get the tags that can be used to reference this user in an event * @returns {NDKTag[]} an array of NDKTag */ referenceTags(marker) { const tag = [["p", this.pubkey]]; if (!marker) return tag; tag[0].push("", marker); return tag; } /** * Publishes the current profile. */ async publish() { if (!this.ndk) throw new Error("No NDK instance found"); if (!this.profile) throw new Error("No profile available"); this.ndk.assertSigner(); const event = new NDKEvent(this.ndk, { kind: 0, content: serializeProfile(this.profile) }); await event.publish(); } /** * Add one or more follows to this user's contact list * * @param newFollow {NDKUser | Hexpubkey | Array} The user(s) to follow * @param currentFollowList {Set} The current follow list * @param kind {NDKKind} The kind to use for this contact list (defaults to `3`) * @returns {Promise} True if any follows were added, false if all already exist */ async follow(newFollow, currentFollowList, kind = 3 /* Contacts */) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); if (!currentFollowList) { currentFollowList = await this.follows(void 0, void 0, kind); } const followsToAdd = Array.isArray(newFollow) ? newFollow : [newFollow]; let anyAdded = false; for (const follow of followsToAdd) { const followPubkey = typeof follow === "string" ? follow : follow.pubkey; const isAlreadyFollowing = Array.from(currentFollowList).some( (item) => typeof item === "string" ? item === followPubkey : item.pubkey === followPubkey ); if (!isAlreadyFollowing) { currentFollowList.add(follow); anyAdded = true; } } if (!anyAdded) { return false; } const event = new NDKEvent(this.ndk, { kind }); for (const follow of currentFollowList) { if (typeof follow === "string") { event.tags.push(["p", follow]); } else { event.tag(follow); } } await event.publish(); return true; } /** * Remove one or more follows from this user's contact list * * @param user {NDKUser | Hexpubkey | Array} The user(s) to unfollow * @param currentFollowList {Set} The current follow list * @param kind {NDKKind} The kind to use for this contact list (defaults to `3`) * @returns The relays where the follow list was published or false if none were found */ async unfollow(user, currentFollowList, kind = 3 /* Contacts */) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); if (!currentFollowList) { currentFollowList = await this.follows(void 0, void 0, kind); } const usersToUnfollow = Array.isArray(user) ? user : [user]; const unfollowPubkeys = new Set( usersToUnfollow.map((u3) => typeof u3 === "string" ? u3 : u3.pubkey) ); const newUserFollowList = /* @__PURE__ */ new Set(); let foundAny = false; for (const follow of currentFollowList) { const followPubkey = typeof follow === "string" ? follow : follow.pubkey; if (!unfollowPubkeys.has(followPubkey)) { newUserFollowList.add(follow); } else { foundAny = true; } } if (!foundAny) return false; const event = new NDKEvent(this.ndk, { kind }); for (const follow of newUserFollowList) { if (typeof follow === "string") { event.tags.push(["p", follow]); } else { event.tag(follow); } } return await event.publish(); } /** * Validate a user's NIP-05 identifier (usually fetched from their kind:0 profile data) * * @param nip05Id The NIP-05 string to validate * @returns {Promise} True if the NIP-05 is found and matches this user's pubkey, * False if the NIP-05 is found but doesn't match this user's pubkey, * null if the NIP-05 isn't found on the domain or we're unable to verify (because of network issues, etc.) */ async validateNip05(nip05Id) { if (!this.ndk) throw new Error("No NDK instance found"); const profilePointer = await getNip05For(this.ndk, nip05Id); if (profilePointer === null) return null; return profilePointer.pubkey === this.pubkey; } }; // ndk/core/src/signers/registry.ts var signerRegistry = /* @__PURE__ */ new Map(); function registerSigner(type, signerClass) { signerRegistry.set(type, signerClass); } // ndk/core/src/signers/private-key/index.ts var NDKPrivateKeySigner = class _NDKPrivateKeySigner3 { /** * Create a new signer from a private key. * @param privateKey - The private key to use in hex form or nsec. * @param ndk - The NDK instance to use. * * @ai-guardrail * If you have an nsec (bech32-encoded private key starting with "nsec1"), you can pass it directly * to this constructor without decoding it first. The constructor handles both hex and nsec formats automatically. * DO NOT use nip19.decode() to convert nsec to hex before passing it here - just pass the nsec string directly. */ constructor(privateKeyOrNsec, ndk) { __publicField(this, "_user"); __publicField(this, "_privateKey"); __publicField(this, "_pubkey"); if (typeof privateKeyOrNsec === "string") { if (privateKeyOrNsec.startsWith("nsec1")) { const { type, data } = import_nostr_tools6.nip19.decode(privateKeyOrNsec); if (type === "nsec") this._privateKey = data; else throw new Error("Invalid private key provided."); } else if (privateKeyOrNsec.length === 64) { this._privateKey = hexToBytes(privateKeyOrNsec); } else { throw new Error("Invalid private key provided."); } } else { this._privateKey = privateKeyOrNsec; } this._pubkey = (0, import_nostr_tools6.getPublicKey)(this._privateKey); if (ndk) this._user = ndk.getUser({ pubkey: this._pubkey }); this._user ?? (this._user = new NDKUser({ pubkey: this._pubkey })); } /** * Get the private key in hex form. */ get privateKey() { if (!this._privateKey) throw new Error("Not ready"); return bytesToHex(this._privateKey); } /** * Get the public key in hex form. */ get pubkey() { if (!this._pubkey) throw new Error("Not ready"); return this._pubkey; } /** * Get the private key in nsec form. */ get nsec() { if (!this._privateKey) throw new Error("Not ready"); return import_nostr_tools6.nip19.nsecEncode(this._privateKey); } /** * Get the public key in npub form. */ get npub() { if (!this._pubkey) throw new Error("Not ready"); return import_nostr_tools6.nip19.npubEncode(this._pubkey); } /** * Encrypt the private key with a password to ncryptsec format. * @param password - The password to encrypt the private key. * @param logn - The log2 of the scrypt N parameter (default: 16). * @param ksb - The key security byte (0x00, 0x01, or 0x02, default: 0x02). * @returns The encrypted private key in ncryptsec format. * * @example * ```ts * const signer = new NDKPrivateKeySigner(nsec); * const ncryptsec = signer.encryptToNcryptsec("my-password"); * console.log('encrypted key:', ncryptsec); * ``` */ encryptToNcryptsec(password, logn = 16, ksb = 2) { if (!this._privateKey) throw new Error("Private key not available"); return nip49.encrypt(this._privateKey, password, logn, ksb); } /** * Generate a new private key. */ static generate() { const privateKey = (0, import_nostr_tools6.generateSecretKey)(); return new _NDKPrivateKeySigner3(privateKey); } /** * Create a signer from an encrypted private key (ncryptsec) using a password. * @param ncryptsec - The encrypted private key in ncryptsec format. * @param password - The password to decrypt the private key. * @param ndk - Optional NDK instance. * @returns A new NDKPrivateKeySigner instance. * * @example * ```ts * const signer = NDKPrivateKeySigner.fromNcryptsec( * "ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p", * "my-password" * ); * console.log('your pubkey is', signer.pubkey); * ``` */ static fromNcryptsec(ncryptsec, password, ndk) { const privateKeyBytes = nip49.decrypt(ncryptsec, password); return new _NDKPrivateKeySigner3(privateKeyBytes, ndk); } /** * Noop in NDKPrivateKeySigner. */ async blockUntilReady() { return this._user; } /** * Get the user. */ async user() { return this._user; } /** * Get the user. */ get userSync() { return this._user; } async sign(event) { if (!this._privateKey) { throw Error("Attempted to sign without a private key"); } return (0, import_nostr_tools6.finalizeEvent)(event, this._privateKey).sig; } async encryptionEnabled(scheme) { const enabled = []; if (!scheme || scheme === "nip04") enabled.push("nip04"); if (!scheme || scheme === "nip44") enabled.push("nip44"); return enabled; } async encrypt(recipient, value, scheme) { if (!this._privateKey || !this.privateKey) { throw Error("Attempted to encrypt without a private key"); } const recipientHexPubKey = recipient.pubkey; if (scheme === "nip44") { const conversationKey = import_nostr_tools6.nip44.v2.utils.getConversationKey(this._privateKey, recipientHexPubKey); return await import_nostr_tools6.nip44.v2.encrypt(value, conversationKey); } return await import_nostr_tools6.nip04.encrypt(this._privateKey, recipientHexPubKey, value); } async decrypt(sender, value, scheme) { if (!this._privateKey || !this.privateKey) { throw Error("Attempted to decrypt without a private key"); } const senderHexPubKey = sender.pubkey; if (scheme === "nip44") { const conversationKey = import_nostr_tools6.nip44.v2.utils.getConversationKey(this._privateKey, senderHexPubKey); return await import_nostr_tools6.nip44.v2.decrypt(value, conversationKey); } return await import_nostr_tools6.nip04.decrypt(this._privateKey, senderHexPubKey, value); } /** * Serializes the signer's private key into a storable format. * @returns A JSON string containing the type and the hex private key. */ toPayload() { if (!this._privateKey) throw new Error("Private key not available"); const payload = { type: "private-key", payload: this.privateKey // Use the hex private key }; return JSON.stringify(payload); } /** * Deserializes the signer from a payload string. * @param payloadString The JSON string obtained from toPayload(). * @param ndk Optional NDK instance. * @returns An instance of NDKPrivateKeySigner. */ static async fromPayload(payloadString, ndk) { const payload = JSON.parse(payloadString); if (payload.type !== "private-key") { throw new Error(`Invalid payload type: expected 'private-key', got ${payload.type}`); } if (!payload.payload || typeof payload.payload !== "string") { throw new Error("Invalid payload content for private-key signer"); } return new _NDKPrivateKeySigner3(payload.payload, ndk); } }; registerSigner("private-key", NDKPrivateKeySigner); // ndk/core/src/events/gift-wrapping.ts async function giftWrap(event, recipient, signer, params = {}) { let _signer = signer; params.scheme ?? (params.scheme = "nip44"); if (!_signer) { if (!event.ndk) throw new Error("no signer available for giftWrap"); _signer = event.ndk.signer; } if (!_signer) throw new Error("no signer"); if (!_signer.encryptionEnabled || !_signer.encryptionEnabled(params.scheme)) throw new Error("signer is not able to giftWrap"); if (!event.pubkey) { const sender = await _signer.user(); event.pubkey = sender.pubkey; } if (event.sig) { console.warn( "\u26A0\uFE0F NIP-17 Warning: Rumor event should not be signed. The signature will be removed during gift wrapping." ); } const rumor = getRumorEvent(event, params?.rumorKind); const seal = await getSealEvent(rumor, recipient, _signer, params.scheme); const wrap = await getWrapEvent(seal, recipient, params); return new NDKEvent(event.ndk, wrap); } async function giftUnwrap(event, sender, signer, scheme = "nip44") { if (event.ndk?.cacheAdapter?.getDecryptedEvent) { const cached = await event.ndk.cacheAdapter.getDecryptedEvent(event.id); if (cached) { return cached; } } const _sender = sender || new NDKUser({ pubkey: event.pubkey }); const _signer = signer || event.ndk?.signer; if (!_signer) throw new Error("no signer"); try { const seal = JSON.parse(await _signer.decrypt(_sender, event.content, scheme)); if (!seal) throw new Error("Failed to decrypt wrapper"); if (!new NDKEvent(void 0, seal).verifySignature(false)) throw new Error("GiftSeal signature verification failed!"); const rumorSender = new NDKUser({ pubkey: seal.pubkey }); const rumor = JSON.parse(await _signer.decrypt(rumorSender, seal.content, scheme)); if (!rumor) throw new Error("Failed to decrypt seal"); if (rumor.pubkey !== seal.pubkey) throw new Error("Invalid GiftWrap, sender validation failed!"); const rumorEvent = new NDKEvent(event.ndk, rumor); if (event.ndk?.cacheAdapter?.addDecryptedEvent) { await event.ndk.cacheAdapter.addDecryptedEvent(event.id, rumorEvent); } return rumorEvent; } catch (_e2) { return Promise.reject("Got error unwrapping event! See console log."); } } function getRumorEvent(event, kind) { const rumor = event.rawEvent(); rumor.kind = kind || rumor.kind || 14 /* PrivateDirectMessage */; rumor.sig = void 0; rumor.id = (0, import_nostr_tools7.getEventHash)(rumor); return new NDKEvent(event.ndk, rumor); } async function getSealEvent(rumor, recipient, signer, scheme = "nip44") { const seal = new NDKEvent(rumor.ndk); seal.kind = 13 /* GiftWrapSeal */; seal.created_at = approximateNow(5); seal.content = JSON.stringify(rumor.rawEvent()); await seal.encrypt(recipient, signer, scheme); await seal.sign(signer); return seal; } async function getWrapEvent(sealed, recipient, params, scheme = "nip44") { const signer = NDKPrivateKeySigner.generate(); const wrap = new NDKEvent(sealed.ndk); wrap.kind = 1059 /* GiftWrap */; wrap.created_at = approximateNow(5); if (params?.wrapTags) wrap.tags = params.wrapTags; wrap.tag(recipient); wrap.content = JSON.stringify(sealed.rawEvent()); await wrap.encrypt(recipient, signer, scheme); await wrap.sign(signer); return wrap; } function approximateNow(drift = 0) { return Math.round(Date.now() / 1e3 - Math.random() * 10 ** drift); } // ndk/core/src/events/kinds/cashu/token.ts function proofsTotalBalance(proofs) { return proofs.reduce((acc, proof) => { if (proof.amount < 0) { throw new Error("proof amount is negative"); } return acc + proof.amount; }, 0); } var _NDKCashuToken = class _NDKCashuToken extends NDKEvent { constructor(ndk, event) { super(ndk, event); __publicField(this, "_proofs", []); __publicField(this, "_mint"); /** * Tokens that this token superseeds */ __publicField(this, "_deletes", []); __publicField(this, "original"); this.kind ?? (this.kind = 7375 /* CashuToken */); } static async from(event) { const token = new _NDKCashuToken(event.ndk, event); token.original = event; try { await token.decrypt(); } catch { token.content = token.original.content; } try { const content = JSON.parse(token.content); token.proofs = content.proofs; token.mint = content.mint ?? token.tagValue("mint"); token.deletedTokens = content.del ?? []; if (!Array.isArray(token.proofs)) return; } catch (_e2) { return; } return token; } get proofs() { return this._proofs; } set proofs(proofs) { const cs = /* @__PURE__ */ new Set(); this._proofs = proofs.filter((proof) => { if (cs.has(proof.C)) { console.warn("Passed in proofs had duplicates, ignoring", proof.C); return false; } if (proof.amount < 0) { console.warn("Invalid proof with negative amount", proof); return false; } cs.add(proof.C); return true; }).map(this.cleanProof); } /** * Returns a minimal proof object with only essential properties */ cleanProof(proof) { return { id: proof.id, amount: proof.amount, C: proof.C, secret: proof.secret }; } async toNostrEvent(pubkey) { if (!this.ndk) throw new Error("no ndk"); if (!this.ndk.signer) throw new Error("no signer"); const payload = { proofs: this.proofs.map(this.cleanProof), mint: this.mint, del: this.deletedTokens ?? [] }; this.content = JSON.stringify(payload); const user = await this.ndk.signer.user(); await this.encrypt(user, void 0, "nip44"); return super.toNostrEvent(pubkey); } set mint(mint) { this._mint = mint; } get mint() { return this._mint; } /** * Tokens that were deleted by the creation of this token. */ get deletedTokens() { return this._deletes; } /** * Marks tokens that were deleted by the creation of this token. */ set deletedTokens(tokenIds) { this._deletes = tokenIds; } get amount() { return proofsTotalBalance(this.proofs); } async publish(relaySet, timeoutMs, requiredRelayCount) { if (this.original) { return this.original.publish(relaySet, timeoutMs, requiredRelayCount); } return super.publish(relaySet, timeoutMs, requiredRelayCount); } }; __publicField(_NDKCashuToken, "kind", 7375 /* CashuToken */); __publicField(_NDKCashuToken, "kinds", [7375 /* CashuToken */]); var NDKCashuToken = _NDKCashuToken; // ndk/core/src/events/kinds/cashu/tx.ts var MARKERS = { REDEEMED: "redeemed", CREATED: "created", DESTROYED: "destroyed", RESERVED: "reserved" }; var _NDKCashuWalletTx = class _NDKCashuWalletTx extends NDKEvent { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 7376 /* CashuWalletTx */); } static async from(event) { const walletChange = new _NDKCashuWalletTx(event.ndk, event); const prevContent = walletChange.content; try { await walletChange.decrypt(); } catch (_e2) { walletChange.content ?? (walletChange.content = prevContent); } try { const contentTags = JSON.parse(walletChange.content); walletChange.tags = [...contentTags, ...walletChange.tags]; } catch (_e2) { return; } return walletChange; } set direction(direction) { this.removeTag("direction"); if (direction) this.tags.push(["direction", direction]); } get direction() { return this.tagValue("direction"); } set amount(amount) { this.removeTag("amount"); this.tags.push(["amount", amount.toString()]); } get amount() { const val = this.tagValue("amount"); if (val === void 0) return void 0; return Number(val); } set fee(fee) { this.removeTag("fee"); this.tags.push(["fee", fee.toString()]); } get fee() { const val = this.tagValue("fee"); if (val === void 0) return void 0; return Number(val); } set unit(unit) { this.removeTag("unit"); if (unit) this.tags.push(["unit", unit.toString()]); } get unit() { return this.tagValue("unit"); } set description(description) { this.removeTag("description"); if (description) this.tags.push(["description", description.toString()]); } get description() { return this.tagValue("description"); } set mint(mint) { this.removeTag("mint"); if (mint) this.tags.push(["mint", mint.toString()]); } get mint() { return this.tagValue("mint"); } /** * Tags tokens that were created in this history event */ set destroyedTokens(events) { for (const event of events) { this.tags.push(event.tagReference(MARKERS.DESTROYED)); } } set destroyedTokenIds(ids) { for (const id of ids) { this.tags.push(["e", id, "", MARKERS.DESTROYED]); } } /** * Tags tokens that were created in this history event */ set createdTokens(events) { for (const event of events) { this.tags.push(event.tagReference(MARKERS.CREATED)); } } set reservedTokens(events) { for (const event of events) { this.tags.push(event.tagReference(MARKERS.RESERVED)); } } addRedeemedNutzap(event) { this.tag(event, MARKERS.REDEEMED); } async toNostrEvent(pubkey) { const encryptedTags = []; const unencryptedTags = []; for (const tag of this.tags) { if (!this.shouldEncryptTag(tag)) { unencryptedTags.push(tag); } else { encryptedTags.push(tag); } } this.tags = unencryptedTags.filter((t) => t[0] !== "client"); this.content = JSON.stringify(encryptedTags); const user = await this.ndk?.signer?.user(); if (user) { const ownPubkey = user.pubkey; this.tags = this.tags.filter((t) => t[0] !== "p" || t[1] !== ownPubkey); } await this.encrypt(user, void 0, "nip44"); return super.toNostrEvent(pubkey); } /** * Whether this entry includes a redemption of a Nutzap */ get hasNutzapRedemption() { return this.getMatchingTags("e", MARKERS.REDEEMED).length > 0; } shouldEncryptTag(tag) { const unencryptedTagNames = ["client"]; if (unencryptedTagNames.includes(tag[0])) { return false; } if (tag[0] === "e" && tag[3] === MARKERS.REDEEMED) { return false; } if (tag[0] === "p") return false; return true; } }; __publicField(_NDKCashuWalletTx, "MARKERS", MARKERS); __publicField(_NDKCashuWalletTx, "kind", 7376 /* CashuWalletTx */); __publicField(_NDKCashuWalletTx, "kinds", [7376 /* CashuWalletTx */]); var NDKCashuWalletTx = _NDKCashuWalletTx; // ndk/core/src/events/kinds/interest-list.ts var _NDKInterestList = class _NDKInterestList extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 10015 /* InterestList */); } static from(ndkEvent) { return new _NDKInterestList(ndkEvent.ndk, ndkEvent.rawEvent()); } /** * Get all interest hashtags from the list. */ get interests() { return this.tags.filter((tag) => tag[0] === "t").map((tag) => tag[1]).filter(Boolean); } /** * Set interest hashtags, replacing all existing ones. */ set interests(hashtags) { this.tags = this.tags.filter((tag) => tag[0] !== "t"); for (const hashtag of hashtags) { this.tags.push(["t", hashtag]); } } /** * Add a single interest hashtag to the list. * @param hashtag The hashtag to add (without the # symbol) */ addInterest(hashtag) { if (!this.hasInterest(hashtag)) { this.tags.push(["t", hashtag]); } } /** * Remove an interest hashtag from the list. * @param hashtag The hashtag to remove */ removeInterest(hashtag) { const index = this.tags.findIndex((tag) => tag[0] === "t" && tag[1] === hashtag); if (index >= 0) { this.tags.splice(index, 1); } } /** * Check if the list contains a specific interest hashtag. * @param hashtag The hashtag to check for */ hasInterest(hashtag) { return this.tags.some((tag) => tag[0] === "t" && tag[1] === hashtag); } /** * Get interest set references (kind:30015) from "a" tags. */ get interestSetReferences() { return this.tags.filter((tag) => tag[0] === "a").map((tag) => tag[1]).filter((ref) => ref?.startsWith("30015:")); } }; __publicField(_NDKInterestList, "kind", 10015 /* InterestList */); __publicField(_NDKInterestList, "kinds", [10015 /* InterestList */]); var NDKInterestList = _NDKInterestList; // ndk/core/src/zap/invoice.ts var import_light_bolt11_decoder = __toESM(require_bolt11()); function zapInvoiceFromEvent(event) { const description = event.getMatchingTags("description")[0]; const bolt11 = event.getMatchingTags("bolt11")[0]; let decodedInvoice; let zapRequest; if (!description || !bolt11 || !bolt11[1]) { return null; } try { let zapRequestPayload = description[1]; if (zapRequestPayload.startsWith("%")) { zapRequestPayload = decodeURIComponent(zapRequestPayload); } if (zapRequestPayload === "") { return null; } zapRequest = JSON.parse(zapRequestPayload); decodedInvoice = (0, import_light_bolt11_decoder.decode)(bolt11[1]); } catch (_e2) { return null; } const amountSection = decodedInvoice.sections.find((s) => s.name === "amount"); if (!amountSection) { return null; } const amount = Number.parseInt(amountSection.value); if (!amount) { return null; } const content = zapRequest.content; const sender = zapRequest.pubkey; const recipientTag = event.getMatchingTags("p")[0]; const recipient = recipientTag[1]; let zappedEvent = event.getMatchingTags("e")[0]; if (!zappedEvent) { zappedEvent = event.getMatchingTags("a")[0]; } const zappedEventId = zappedEvent ? zappedEvent[1] : void 0; const zapInvoice = { id: event.id, zapper: event.pubkey, zappee: sender, zapped: recipient, zappedEvent: zappedEventId, amount, comment: content }; return zapInvoice; } // ndk/core/src/events/kinds/zap.ts var _NDKZap = class _NDKZap extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_invoice"); this.kind ?? (this.kind = 9735 /* Zap */); } /** * Creates an NDKZap instance from an NDKEvent * * @param event The event to convert * @returns NDKZap instance or null if invalid */ static from(event) { if (event.kind !== 9735 /* Zap */) return null; return new _NDKZap(event.ndk, event.rawEvent()); } /** * Get the parsed zap invoice (lazy loaded, cached) * Returns null if the zap event is invalid or malformed */ get invoice() { if (this._invoice !== void 0) return this._invoice; this._invoice = zapInvoiceFromEvent(this); return this._invoice; } /** * Amount in sats (converted from millisats in the invoice) * Returns 0 if invoice is invalid */ get amount() { return this.invoice ? Math.floor(this.invoice.amount / 1e3) : 0; } /** * The user who sent the zap (zappee) * * @throws Error if zap is invalid or NDK instance is not available */ get sender() { const pubkey = this.invoice?.zappee; if (!pubkey) throw new Error("Invalid zap - no sender"); if (!this.ndk) throw new Error("No NDK instance"); return this.ndk.getUser({ pubkey }); } /** * The user who received the zap (zapped) * * @throws Error if zap is invalid or NDK instance is not available */ get recipient() { const pubkey = this.invoice?.zapped; if (!pubkey) throw new Error("Invalid zap - no recipient"); if (!this.ndk) throw new Error("No NDK instance"); return this.ndk.getUser({ pubkey }); } /** * Zap comment/message from the zap request */ get comment() { return this.invoice?.comment; } /** * The event that was zapped (if any) * Can be an event ID (e tag) or address (a tag) */ get zappedEventId() { return this.invoice?.zappedEvent; } /** * The zapper service pubkey that processed this zap */ get zapper() { return this.invoice?.zapper; } /** * Check if this is a valid zap */ get isValid() { return this.invoice !== null; } }; __publicField(_NDKZap, "kind", 9735 /* Zap */); __publicField(_NDKZap, "kinds", [9735 /* Zap */]); var NDKZap = _NDKZap; // ndk/core/src/events/kinds/simple-group/index.ts var NDKSimpleGroup = class _NDKSimpleGroup { constructor(ndk, relaySet, groupId) { __publicField(this, "ndk"); __publicField(this, "groupId"); __publicField(this, "relaySet"); __publicField(this, "fetchingMetadata"); __publicField(this, "metadata"); __publicField(this, "memberList"); __publicField(this, "adminList"); this.ndk = ndk; this.groupId = groupId ?? randomId(24); this.relaySet = relaySet; } get id() { return this.groupId; } relayUrls() { return this.relaySet?.relayUrls; } get name() { return this.metadata?.name; } get about() { return this.metadata?.about; } get picture() { return this.metadata?.picture; } get members() { return this.memberList?.members ?? []; } get admins() { return this.adminList?.members ?? []; } async getMetadata() { await this.ensureMetadataEvent(); return this.metadata; } /** * Creates the group by publishing a kind:9007 event. * @param signer * @returns */ async createGroup(signer) { signer ?? (signer = this.ndk.signer); if (!signer) throw new Error("No signer available"); const user = await signer.user(); if (!user) throw new Error("No user available"); const event = new NDKEvent(this.ndk); event.kind = 9007 /* GroupAdminCreateGroup */; event.tags.push(["h", this.groupId]); await event.sign(signer); return event.publish(this.relaySet); } async setMetadata({ name, about, picture }) { const event = new NDKEvent(this.ndk); event.kind = 9002 /* GroupAdminEditMetadata */; event.tags.push(["h", this.groupId]); if (name) event.tags.push(["name", name]); if (about) event.tags.push(["about", about]); if (picture) event.tags.push(["picture", picture]); await event.sign(); return event.publish(this.relaySet); } /** * Adds a user to the group using a kind:9000 event * @param user user to add * @param opts options */ async addUser(user) { const addUserEvent = _NDKSimpleGroup.generateAddUserEvent(user.pubkey, this.groupId); addUserEvent.ndk = this.ndk; return addUserEvent; } async getMemberListEvent() { const memberList = await this.ndk.fetchEvent( { kinds: [39002 /* GroupMembers */], "#d": [this.groupId] }, void 0, this.relaySet ); if (!memberList) return null; return NDKSimpleGroupMemberList.from(memberList); } /** * Gets a list of users that belong to this group */ async getMembers() { const members = []; const memberPubkeys = /* @__PURE__ */ new Set(); const memberListEvent = await this.getMemberListEvent(); if (!memberListEvent) return []; for (const pTag of memberListEvent.getMatchingTags("p")) { const pubkey = pTag[1]; if (!pubkey || !isValidPubkey(pubkey)) continue; if (memberPubkeys.has(pubkey)) continue; memberPubkeys.add(pubkey); try { members.push(this.ndk.getUser({ pubkey })); } catch { } } return members; } /** * Generates an event that lists the members of a group. * @param groupId * @returns */ static generateUserListEvent(groupId) { const event = new NDKEvent(void 0, { kind: 39002 /* GroupMembers */, tags: [ ["h", groupId], ["alt", "Group Member List"] ] }); return event; } /** * Generates an event that adds a user to a group. * @param userPubkey pubkey of the user to add * @param groupId group to add the user to * @returns */ static generateAddUserEvent(userPubkey, groupId) { const event = new NDKEvent(void 0, { kind: 9e3 /* GroupAdminAddUser */, tags: [["h", groupId]] }); event.tags.push(["p", userPubkey]); return event; } async requestToJoin(_pubkey, content) { const event = new NDKEvent(this.ndk, { kind: 9021 /* GroupAdminRequestJoin */, content: content ?? "", tags: [["h", this.groupId]] }); return event.publish(this.relaySet); } /** * Makes sure that a metadata event exists locally */ async ensureMetadataEvent() { if (this.metadata) return; if (this.fetchingMetadata) return this.fetchingMetadata; this.fetchingMetadata = this.ndk.fetchEvent( { kinds: [39e3 /* GroupMetadata */], "#d": [this.groupId] }, void 0, this.relaySet ).then((event) => { if (event) { this.metadata = NDKSimpleGroupMetadata.from(event); } else { this.metadata = new NDKSimpleGroupMetadata(this.ndk); this.metadata.dTag = this.groupId; } }).finally(() => { this.fetchingMetadata = void 0; }).catch(() => { throw new Error(`Failed to fetch metadata for group ${this.groupId}`); }); return this.fetchingMetadata; } }; function randomId(length) { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const charsLength = chars.length; let result = ""; for (let i3 = 0; i3 < length; i3++) { result += chars.charAt(Math.floor(Math.random() * charsLength)); } return result; } // ndk/core/src/events/kinds/voice-message.ts var _NDKVoiceMessage = class _NDKVoiceMessage extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 1222 /* VoiceMessage */); } /** * Creates a NDKVoiceMessage from an existing NDKEvent. * * @param event NDKEvent to create the NDKVoiceMessage from. * @returns NDKVoiceMessage */ static from(event) { return new _NDKVoiceMessage(event.ndk, event); } /** * Getter for the audio URL. * * @returns {string | undefined} - The audio file URL if available, otherwise undefined. */ get url() { return this.content || void 0; } /** * Setter for the audio URL. * * @param {string | undefined} url - The audio URL to set for the voice message. */ set url(url) { this.content = url || ""; } /** * Getter for the waveform data from imeta tag. * * @returns {number[] | undefined} - Array of amplitude values if available, otherwise undefined. */ get waveform() { const imetaTag = this.tags.find((tag) => tag[0] === "imeta"); if (!imetaTag) return void 0; const waveformValue = imetaTag.find((value) => value.startsWith("waveform ")); if (!waveformValue) return void 0; const waveformStr = waveformValue.replace("waveform ", ""); return waveformStr.split(" ").map((v6) => parseInt(v6, 10)); } /** * Setter for the waveform data in imeta tag. * * @param {number[] | undefined} waveform - Array of amplitude values (0-100). */ set waveform(waveform) { this.removeTag("imeta"); if (waveform && waveform.length > 0) { const imetaTag = ["imeta", `url ${this.content}`]; imetaTag.push(`waveform ${waveform.join(" ")}`); const duration = this.duration; if (duration !== void 0) { imetaTag.push(`duration ${duration}`); } this.tags.push(imetaTag); } } /** * Getter for the audio duration in seconds from imeta tag. * * @returns {number | undefined} - The audio duration in seconds if available, otherwise undefined. */ get duration() { const imetaTag = this.tags.find((tag) => tag[0] === "imeta"); if (!imetaTag) return void 0; const durationValue = imetaTag.find((value) => value.startsWith("duration ")); if (!durationValue) return void 0; const durationStr = durationValue.replace("duration ", ""); return parseInt(durationStr, 10); } /** * Setter for the audio duration in imeta tag. * * @param {number | undefined} duration - The audio duration in seconds. */ set duration(duration) { const existingImeta = this.tags.find((tag) => tag[0] === "imeta"); if (duration !== void 0) { if (existingImeta) { const durationIndex = existingImeta.findIndex((v6) => v6.startsWith("duration ")); if (durationIndex > 0) { existingImeta[durationIndex] = `duration ${duration}`; } else { existingImeta.push(`duration ${duration}`); } } else { const imetaTag = ["imeta", `url ${this.content}`, `duration ${duration}`]; this.tags.push(imetaTag); } } else if (existingImeta) { const filtered = existingImeta.filter((v6) => !v6.startsWith("duration ")); if (filtered.length <= 1) { this.removeTag("imeta"); } else { const index = this.tags.indexOf(existingImeta); this.tags[index] = filtered; } } } }; __publicField(_NDKVoiceMessage, "kind", 1222 /* VoiceMessage */); __publicField(_NDKVoiceMessage, "kinds", [1222 /* VoiceMessage */]); var NDKVoiceMessage = _NDKVoiceMessage; var _NDKVoiceReply = class _NDKVoiceReply extends NDKEvent { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 1244 /* VoiceReply */); } /** * Creates a NDKVoiceReply from an existing NDKEvent. * * @param event NDKEvent to create the NDKVoiceReply from. * @returns NDKVoiceReply */ static from(event) { return new _NDKVoiceReply(event.ndk, event); } /** * Getter for the audio URL. * * @returns {string | undefined} - The audio file URL if available, otherwise undefined. */ get url() { return this.content || void 0; } /** * Setter for the audio URL. * * @param {string | undefined} url - The audio URL to set for the voice reply. */ set url(url) { this.content = url || ""; } /** * Getter for the waveform data from imeta tag. * * @returns {number[] | undefined} - Array of amplitude values if available, otherwise undefined. */ get waveform() { const imetaTag = this.tags.find((tag) => tag[0] === "imeta"); if (!imetaTag) return void 0; const waveformValue = imetaTag.find((value) => value.startsWith("waveform ")); if (!waveformValue) return void 0; const waveformStr = waveformValue.replace("waveform ", ""); return waveformStr.split(" ").map((v6) => parseInt(v6, 10)); } /** * Setter for the waveform data in imeta tag. * * @param {number[] | undefined} waveform - Array of amplitude values (0-100). */ set waveform(waveform) { this.removeTag("imeta"); if (waveform && waveform.length > 0) { const imetaTag = ["imeta", `url ${this.content}`]; imetaTag.push(`waveform ${waveform.join(" ")}`); const duration = this.duration; if (duration !== void 0) { imetaTag.push(`duration ${duration}`); } this.tags.push(imetaTag); } } /** * Getter for the audio duration in seconds from imeta tag. * * @returns {number | undefined} - The audio duration in seconds if available, otherwise undefined. */ get duration() { const imetaTag = this.tags.find((tag) => tag[0] === "imeta"); if (!imetaTag) return void 0; const durationValue = imetaTag.find((value) => value.startsWith("duration ")); if (!durationValue) return void 0; const durationStr = durationValue.replace("duration ", ""); return parseInt(durationStr, 10); } /** * Setter for the audio duration in imeta tag. * * @param {number | undefined} duration - The audio duration in seconds. */ set duration(duration) { const existingImeta = this.tags.find((tag) => tag[0] === "imeta"); if (duration !== void 0) { if (existingImeta) { const durationIndex = existingImeta.findIndex((v6) => v6.startsWith("duration ")); if (durationIndex > 0) { existingImeta[durationIndex] = `duration ${duration}`; } else { existingImeta.push(`duration ${duration}`); } } else { const imetaTag = ["imeta", `url ${this.content}`, `duration ${duration}`]; this.tags.push(imetaTag); } } else if (existingImeta) { const filtered = existingImeta.filter((v6) => !v6.startsWith("duration ")); if (filtered.length <= 1) { this.removeTag("imeta"); } else { const index = this.tags.indexOf(existingImeta); this.tags[index] = filtered; } } } }; __publicField(_NDKVoiceReply, "kind", 1244 /* VoiceReply */); __publicField(_NDKVoiceReply, "kinds", [1244 /* VoiceReply */]); var NDKVoiceReply = _NDKVoiceReply; // ndk/core/src/ndk/index.ts var import_debug8 = __toESM(require_browser()); var import_nostr_tools10 = __toESM(require_nostr_tools()); var import_tseep6 = __toESM(require_lib()); // ndk/core/src/events/dedup.ts function dedup(event1, event2) { if (event1.created_at > event2.created_at) { return event1; } return event2; } // ndk/core/src/outbox/tracker.ts var import_tseep5 = __toESM(require_lib()); var import_typescript_lru_cache2 = __toESM(require_dist()); // ndk/core/src/utils/get-users-relay-list.ts async function getRelayListForUser(pubkey, ndk) { const list = await getRelayListForUsers([pubkey], ndk); return list.get(pubkey); } async function getRelayListForUsers(pubkeys, ndk, skipCache = false, timeout = 1e3, relayHints) { const pool = ndk.outboxPool || ndk.pool; const set = /* @__PURE__ */ new Set(); for (const relay of pool.relays.values()) set.add(relay); if (relayHints) { for (const hints of relayHints.values()) { for (const url of hints) { const relay = pool.getRelay(url, true, true); if (relay) set.add(relay); } } } const relayLists = /* @__PURE__ */ new Map(); const fromContactList = /* @__PURE__ */ new Map(); const relaySet = new NDKRelaySet(set, ndk); if (ndk.cacheAdapter?.locking && !skipCache) { const cachedList = await ndk.fetchEvents( { kinds: [3, 10002], authors: Array.from(new Set(pubkeys)) }, { cacheUsage: "ONLY_CACHE" /* ONLY_CACHE */, subId: "ndk-relay-list-fetch" } ); for (const relayList of cachedList) { if (relayList.kind === 10002) relayLists.set(relayList.pubkey, NDKRelayList.from(relayList)); } for (const relayList of cachedList) { if (relayList.kind === 3) { if (relayLists.has(relayList.pubkey)) continue; const list = relayListFromKind3(ndk, relayList); if (list) fromContactList.set(relayList.pubkey, list); } } pubkeys = pubkeys.filter((pubkey) => !relayLists.has(pubkey) && !fromContactList.has(pubkey)); } if (pubkeys.length === 0) return relayLists; const relayListEvents = /* @__PURE__ */ new Map(); const contactListEvents = /* @__PURE__ */ new Map(); return new Promise((resolve) => { let resolved = false; const handleSubscription = async () => { const subscribeOpts = { closeOnEose: true, pool, groupable: true, subId: "ndk-relay-list-fetch", addSinceFromCache: true, relaySet }; if (relaySet) subscribeOpts.relaySet = relaySet; const sub = ndk.subscribe({ kinds: [3, 10002], authors: pubkeys }, subscribeOpts, { onEvent: (event) => { if (event.kind === 10002 /* RelayList */) { const existingEvent = relayListEvents.get(event.pubkey); if (existingEvent && existingEvent.created_at > event.created_at) return; relayListEvents.set(event.pubkey, event); } else if (event.kind === 3 /* Contacts */) { const existingEvent = contactListEvents.get(event.pubkey); if (existingEvent && existingEvent.created_at > event.created_at) return; contactListEvents.set(event.pubkey, event); } }, onEose: () => { if (resolved) return; resolved = true; ndk.debug( `[getRelayListForUsers] EOSE - relayListEvents: ${relayListEvents.size}, contactListEvents: ${contactListEvents.size}` ); for (const event of relayListEvents.values()) { relayLists.set(event.pubkey, NDKRelayList.from(event)); } for (const pubkey of pubkeys) { if (relayLists.has(pubkey)) continue; const contactList = contactListEvents.get(pubkey); if (!contactList) continue; const list = relayListFromKind3(ndk, contactList); if (list) relayLists.set(pubkey, list); } ndk.debug( `[getRelayListForUsers] Returning ${relayLists.size} relay lists for ${pubkeys.length} pubkeys` ); resolve(relayLists); } }); const hasDisconnectedRelays = Array.from(set).some( (relay) => relay.status <= 2 // DISCONNECTING, DISCONNECTED, or RECONNECTING ); const hasConnectingRelays = Array.from(set).some( (relay) => relay.status === 4 // CONNECTING ); let effectiveTimeout = timeout; if (hasDisconnectedRelays || hasConnectingRelays) { effectiveTimeout = timeout + 3e3; } ndk.debug( `[getRelayListForUsers] Setting fallback timeout to ${effectiveTimeout}ms (disconnected: ${hasDisconnectedRelays}, connecting: ${hasConnectingRelays})`, { pubkeys } ); setTimeout(() => { if (!resolved) { resolved = true; ndk.debug(`[getRelayListForUsers] Timeout reached, returning ${relayLists.size} relay lists`); resolve(relayLists); } }, effectiveTimeout); }; handleSubscription(); }); } // ndk/core/src/outbox/tracker.ts var OutboxItem = class { constructor(type) { /** * Type of item */ __publicField(this, "type"); /** * The relay URLs that are of interest to this item */ __publicField(this, "relayUrlScores"); __publicField(this, "readRelays"); __publicField(this, "writeRelays"); this.type = type; this.relayUrlScores = /* @__PURE__ */ new Map(); this.readRelays = /* @__PURE__ */ new Set(); this.writeRelays = /* @__PURE__ */ new Set(); } }; var OutboxTracker = class extends import_tseep5.EventEmitter { constructor(ndk) { super(); __publicField(this, "data"); __publicField(this, "ndk"); __publicField(this, "debug"); this.ndk = ndk; this.debug = ndk.debug.extend("outbox-tracker"); this.data = new import_typescript_lru_cache2.LRUCache({ maxSize: 1e5, entryExpirationTimeInMS: 2 * 60 * 1e3 }); } /** * Adds a list of users to the tracker. * @param items * @param skipCache */ async trackUsers(items, skipCache = false) { const promises = []; for (let i3 = 0; i3 < items.length; i3 += 400) { const slice = items.slice(i3, i3 + 400); const pubkeys = slice.map((item) => getKeyFromItem(item)).filter((pubkey) => !this.data.has(pubkey)); if (pubkeys.length === 0) continue; for (const pubkey of pubkeys) { this.data.set(pubkey, new OutboxItem("user")); } const relayHints = /* @__PURE__ */ new Map(); for (const item of slice) { if (item instanceof NDKUser && item.relayUrls.length > 0) { relayHints.set(item.pubkey, item.relayUrls); } } promises.push( new Promise((resolve) => { getRelayListForUsers(pubkeys, this.ndk, skipCache, 1e3, relayHints).then((relayLists) => { this.debug( `Received relay lists for ${relayLists.size} pubkeys out of ${pubkeys.length} requested` ); for (const [pubkey, relayList] of relayLists) { let outboxItem = this.data.get(pubkey); outboxItem ?? (outboxItem = new OutboxItem("user")); if (relayList) { outboxItem.readRelays = new Set(normalize(relayList.readRelayUrls)); outboxItem.writeRelays = new Set(normalize(relayList.writeRelayUrls)); if (this.ndk.relayConnectionFilter) { for (const relayUrl of outboxItem.readRelays) { if (!this.ndk.relayConnectionFilter(relayUrl)) { outboxItem.readRelays.delete(relayUrl); } } for (const relayUrl of outboxItem.writeRelays) { if (!this.ndk.relayConnectionFilter(relayUrl)) { outboxItem.writeRelays.delete(relayUrl); } } } this.data.set(pubkey, outboxItem); this.emit("user:relay-list-updated", pubkey, outboxItem); this.debug( `Adding ${outboxItem.readRelays.size} read relays and ${outboxItem.writeRelays.size} write relays for ${pubkey}`, relayList?.rawEvent() ); } } }).finally(resolve); }) ); } return Promise.all(promises); } /** * * @param key * @param score */ track(item, type, _skipCache = true) { const key = getKeyFromItem(item); type ?? (type = getTypeFromItem(item)); let outboxItem = this.data.get(key); if (!outboxItem) { outboxItem = new OutboxItem(type); if (item instanceof NDKUser) { this.trackUsers([item]); } } return outboxItem; } }; function getKeyFromItem(item) { if (item instanceof NDKUser) { return item.pubkey; } return item; } function getTypeFromItem(item) { if (item instanceof NDKUser) { return "user"; } return "kind"; } // ndk/core/src/relay/sets/utils.ts function correctRelaySet(relaySet, pool) { const connectedRelays = pool.connectedRelays(); const includesConnectedRelay = Array.from(relaySet.relays).some((relay) => { return connectedRelays.map((r) => r.url).includes(relay.url); }); if (!includesConnectedRelay) { for (const relay of connectedRelays) { relaySet.addRelay(relay); } } if (connectedRelays.length === 0) { for (const relay of pool.relays.values()) { relaySet.addRelay(relay); } } return relaySet; } // ndk/core/src/subscription/manager.ts var import_nostr_tools8 = __toESM(require_nostr_tools()); var import_typescript_lru_cache3 = __toESM(require_dist()); var NDKSubscriptionManager = class { constructor() { __publicField(this, "subscriptions"); // Use LRU cache instead of unbounded Map to prevent memory leaks __publicField(this, "seenEvents", new import_typescript_lru_cache3.LRUCache({ maxSize: 1e4, // Keep last 10k events entryExpirationTimeInMS: 5 * 60 * 1e3 // 5 minutes })); this.subscriptions = /* @__PURE__ */ new Map(); } add(sub) { this.subscriptions.set(sub.internalId, sub); if (sub.onStopped) { } sub.onStopped = () => { this.subscriptions.delete(sub.internalId); }; sub.on("close", () => { this.subscriptions.delete(sub.internalId); }); } seenEvent(eventId, relay) { const current = this.seenEvents.get(eventId) || []; if (!current.some((r) => r.url === relay.url)) { current.push(relay); } this.seenEvents.set(eventId, current); } /** * Whenever an event comes in, this function is called. * This function matches the received event against all the * known (i.e. active) NDKSubscriptions, and if it matches, * it sends the event to the subscription. * * This is the single place in the codebase that matches * incoming events with parties interested in the event. * * This is also what allows for reactivity in NDK apps, such that * whenever an active subscription receives an event that some * other active subscription would want to receive, both receive it. * * TODO This also allows for subscriptions that overlap in meaning * to be collapsed into one. * * I.e. if a subscription with filter: kinds: [1], authors: [alice] * is created and EOSEs, and then a subsequent subscription with * kinds: [1], authors: [alice] is created, once the second subscription * EOSEs we can safely close it, increment its refCount and close it, * and when the first subscription receives a new event from Alice this * code will make the second subscription receive the event even though * it has no active subscription on a relay. * @param event Raw event received from a relay * @param relay Relay that sent the event * @param optimisticPublish Whether the event is coming from an optimistic publish */ dispatchEvent(event, relay, optimisticPublish = false) { if (relay) this.seenEvent(event.id, relay); const subscriptions = this.subscriptions.values(); const matchingSubs = []; for (const sub of subscriptions) { if ((0, import_nostr_tools8.matchFilters)(sub.filters, event)) { matchingSubs.push(sub); } } for (const sub of matchingSubs) { if (sub.exclusiveRelay && sub.relaySet) { let shouldAccept = false; if (optimisticPublish) { shouldAccept = !sub.skipOptimisticPublishEvent; } else if (!relay) { const eventOnRelays = this.seenEvents.get(event.id) || []; shouldAccept = eventOnRelays.some((r) => sub.relaySet.relays.has(r)); } else { shouldAccept = sub.relaySet.relays.has(relay); } if (!shouldAccept) { sub.debug.extend("exclusive-relay")( "Rejected event %s from %s (relay not in exclusive set)", event.id, relay?.url || (optimisticPublish ? "optimistic" : "cache") ); continue; } } sub.eventReceived(event, relay, false, optimisticPublish); } } }; // ndk/core/src/ndk/active-user.ts var import_debug7 = __toESM(require_browser()); var debug6 = (0, import_debug7.default)("ndk:active-user"); async function getUserRelayList(user) { if (!this.autoConnectUserRelays) return; const userRelays = await getRelayListForUser(user.pubkey, this); if (!userRelays) return; for (const url of userRelays.relays) { let relay = this.pool.relays.get(url); if (!relay) { relay = new NDKRelay(url, this.relayAuthDefaultPolicy, this); this.pool.addRelay(relay); } } debug6("Connected to %d user relays", userRelays.relays.length); return userRelays; } async function setActiveUser(user) { if (!this.autoConnectUserRelays) return; const pool = this.outboxPool || this.pool; if (pool.connectedRelays.length > 0) { await getUserRelayList.call(this, user); } else { pool.once("connect", async () => { await getUserRelayList.call(this, user); }); } } // ndk/core/src/ndk/entity.ts var import_nostr_tools9 = __toESM(require_nostr_tools()); function getEntity(entity) { try { const decoded = import_nostr_tools9.nip19.decode(entity); if (decoded.type === "npub") return npub(this, decoded.data); if (decoded.type === "nprofile") return nprofile(this, decoded.data); return decoded; } catch (_e2) { return null; } } function npub(ndk, pubkey) { return ndk.getUser({ pubkey }); } function nprofile(ndk, profile) { const user = ndk.getUser({ pubkey: profile.pubkey }); if (profile.relays) user.relayUrls = profile.relays; return user; } // ndk/core/src/ndk/fetch-event-from-tag.ts function isValidHint(hint) { if (!hint || hint === "") return false; try { new URL(hint); return true; } catch (_e2) { return false; } } async function fetchEventFromTag(tag, originalEvent, subOpts, fallback = { type: "timeout" }) { const d17 = this.debug.extend("fetch-event-from-tag"); const [_2, id, hint] = tag; subOpts = {}; d17("fetching event from tag", tag, subOpts, fallback); const authorRelays = getRelaysForSync(this, originalEvent.pubkey); if (authorRelays && authorRelays.size > 0) { d17("fetching event from author relays %o", Array.from(authorRelays)); const relaySet2 = NDKRelaySet.fromRelayUrls(Array.from(authorRelays), this); const event2 = await this.fetchEvent(id, subOpts, relaySet2); if (event2) return event2; } else { d17("no author relays found for %s", originalEvent.pubkey, originalEvent); } const relaySet = calculateRelaySetsFromFilters(this, [{ ids: [id] }], this.pool); d17("fetching event without relay hint", relaySet); const event = await this.fetchEvent(id, subOpts); if (event) return event; if (hint && hint !== "") { const event2 = await this.fetchEvent(id, subOpts, this.pool.getRelay(hint, true, true, [{ ids: [id] }])); if (event2) return event2; } let result; const relay = isValidHint(hint) ? this.pool.getRelay(hint, false, true, [{ ids: [id] }]) : void 0; const fetchMaybeWithRelayHint = new Promise((resolve) => { this.fetchEvent(id, subOpts, relay).then(resolve); }); if (!isValidHint(hint) || fallback.type === "none") { return fetchMaybeWithRelayHint; } const fallbackFetchPromise = new Promise(async (resolve) => { const fallbackRelaySet = fallback.relaySet; const timeout = fallback.timeout ?? 1500; const timeoutPromise = new Promise((resolve2) => setTimeout(resolve2, timeout)); if (fallback.type === "timeout") await timeoutPromise; if (result) { resolve(result); } else { d17("fallback fetch triggered"); const fallbackEvent = await this.fetchEvent(id, subOpts, fallbackRelaySet); resolve(fallbackEvent); } }); switch (fallback.type) { case "timeout": return Promise.race([fetchMaybeWithRelayHint, fallbackFetchPromise]); case "eose": result = await fetchMaybeWithRelayHint; if (result) return result; return fallbackFetchPromise; } } // ndk/core/src/ndk/queue/index.ts var Queue = class { constructor(_name, maxConcurrency) { __publicField(this, "queue", []); __publicField(this, "maxConcurrency"); __publicField(this, "processing", /* @__PURE__ */ new Set()); __publicField(this, "promises", /* @__PURE__ */ new Map()); this.maxConcurrency = maxConcurrency; } add(item) { if (this.promises.has(item.id)) { return this.promises.get(item.id); } const promise = new Promise((resolve, reject) => { this.queue.push({ ...item, func: () => item.func().then( (result) => { resolve(result); return result; }, (error) => { reject(error); throw error; } ) }); this.process(); }); this.promises.set(item.id, promise); promise.finally(() => { this.promises.delete(item.id); this.processing.delete(item.id); this.process(); }); return promise; } process() { if (this.processing.size >= this.maxConcurrency || this.queue.length === 0) { return; } const item = this.queue.shift(); if (!item || this.processing.has(item.id)) { return; } this.processing.add(item.id); item.func(); } clear() { this.queue = []; } clearProcessing() { this.processing.clear(); } clearAll() { this.clear(); this.clearProcessing(); } length() { return this.queue.length; } }; // ndk/core/src/ndk/index.ts var DEFAULT_OUTBOX_RELAYS = ["wss://purplepag.es/", "wss://nos.lol/"]; var NDK = class extends import_tseep6.EventEmitter { constructor(opts = {}) { super(); __publicField(this, "_explicitRelayUrls"); __publicField(this, "pool"); __publicField(this, "outboxPool"); __publicField(this, "_signer"); __publicField(this, "_activeUser"); __publicField(this, "cacheAdapter"); __publicField(this, "debug"); __publicField(this, "devWriteRelaySet"); __publicField(this, "outboxTracker"); __publicField(this, "muteFilter"); __publicField(this, "relayConnectionFilter"); __publicField(this, "clientName"); __publicField(this, "clientNip89"); __publicField(this, "queuesZapConfig"); __publicField(this, "queuesNip05"); __publicField(this, "asyncSigVerification", false); __publicField(this, "initialValidationRatio", 1); __publicField(this, "lowestValidationRatio", 0.1); __publicField(this, "validationRatioFn"); __publicField(this, "filterValidationMode", "validate"); __publicField(this, "subManager"); __publicField(this, "aiGuardrails"); __publicField(this, "futureTimestampGrace"); /** * Private storage for the signature verification function */ __publicField(this, "_signatureVerificationFunction"); /** * Private storage for the signature verification worker */ __publicField(this, "_signatureVerificationWorker"); /** * Rolling total of time spent (in ms) performing signature verifications. * Users can read this to monitor or display aggregate verification cost. */ __publicField(this, "signatureVerificationTimeMs", 0); __publicField(this, "publishingFailureHandled", false); __publicField(this, "pools", []); /** * Default relay-auth policy that will be used when a relay requests authentication, * if no other policy is specified for that relay. * * @example Disconnect from relays that request authentication: * ```typescript * ndk.relayAuthDefaultPolicy = NDKAuthPolicies.disconnect(ndk.pool); * ``` * * @example Sign in to relays that request authentication: * ```typescript * ndk.relayAuthDefaultPolicy = NDKAuthPolicies.signIn({ndk}) * ``` * * @example Sign in to relays that request authentication, asking the user for confirmation: * ```typescript * ndk.relayAuthDefaultPolicy = (relay: NDKRelay) => { * const signIn = NDKAuthPolicies.signIn({ndk}); * if (confirm(`Relay ${relay.url} is requesting authentication, do you want to sign in?`)) { * signIn(relay); * } * } * ``` */ __publicField(this, "relayAuthDefaultPolicy"); /** * Fetch function to use for HTTP requests. * * @example * ```typescript * import fetch from "node-fetch"; * * ndk.httpFetch = fetch; * ``` */ __publicField(this, "httpFetch"); /** * Provide a caller function to receive all networking traffic from relays */ __publicField(this, "netDebug"); __publicField(this, "autoConnectUserRelays", true); __publicField(this, "_wallet"); __publicField(this, "walletConfig"); /** * Attempts to fetch an event from a tag, following relay hints and * other best practices. * @param tag Tag to fetch the event from * @param originalEvent Event where the tag came from * @param subOpts Subscription options to use when fetching the event * @param fallback Fallback options to use when the hint relay doesn't respond * @returns */ __publicField(this, "fetchEventFromTag", fetchEventFromTag.bind(this)); __publicField(this, "getEntity", getEntity.bind(this)); this.debug = opts.debug || (0, import_debug8.default)("ndk"); this.netDebug = opts.netDebug; this._explicitRelayUrls = opts.explicitRelayUrls || []; this.subManager = new NDKSubscriptionManager(); this.pool = new NDKPool(opts.explicitRelayUrls || [], this); this.pool.name = "Main"; this.pool.on("relay:auth", async (relay, challenge3) => { if (this.relayAuthDefaultPolicy) { await this.relayAuthDefaultPolicy(relay, challenge3); } }); this.autoConnectUserRelays = opts.autoConnectUserRelays ?? true; this.clientName = opts.clientName; this.clientNip89 = opts.clientNip89; this.relayAuthDefaultPolicy = opts.relayAuthDefaultPolicy; if (!(opts.enableOutboxModel === false)) { this.outboxPool = new NDKPool(opts.outboxRelayUrls || DEFAULT_OUTBOX_RELAYS, this, { debug: this.debug.extend("outbox-pool"), name: "Outbox Pool" }); this.outboxTracker = new OutboxTracker(this); this.outboxTracker.on("user:relay-list-updated", (pubkey, _outboxItem) => { this.debug(`Outbox relay list updated for ${pubkey}`); for (const subscription of this.subManager.subscriptions.values()) { const isRelevant = subscription.filters.some((filter) => filter.authors?.includes(pubkey)); if (isRelevant && typeof subscription.refreshRelayConnections === "function") { this.debug(`Refreshing relay connections for subscription ${subscription.internalId}`); subscription.refreshRelayConnections(); } } }); } this.signer = opts.signer; this.cacheAdapter = opts.cacheAdapter; this.muteFilter = opts.muteFilter; this.relayConnectionFilter = opts.relayConnectionFilter; if (opts.devWriteRelayUrls) { this.devWriteRelaySet = NDKRelaySet.fromRelayUrls(opts.devWriteRelayUrls, this); } this.queuesZapConfig = new Queue("zaps", 3); this.queuesNip05 = new Queue("nip05", 10); if (opts.signatureVerificationWorker) { this.signatureVerificationWorker = opts.signatureVerificationWorker; } if (opts.signatureVerificationFunction) { this.signatureVerificationFunction = opts.signatureVerificationFunction; } this.initialValidationRatio = opts.initialValidationRatio || 1; this.lowestValidationRatio = opts.lowestValidationRatio || 0.1; this.validationRatioFn = opts.validationRatioFn || this.defaultValidationRatioFn; this.filterValidationMode = opts.filterValidationMode || "validate"; this.aiGuardrails = new AIGuardrails(opts.aiGuardrails || false); this.futureTimestampGrace = opts.futureTimestampGrace; this.aiGuardrails.ndkInstantiated(this); try { this.httpFetch = fetch; } catch { } } set explicitRelayUrls(urls) { this._explicitRelayUrls = urls.map(normalizeRelayUrl); this.pool.relayUrls = urls; } get explicitRelayUrls() { return this._explicitRelayUrls || []; } /** * Set a Web Worker for signature verification. * * This method initializes the worker and sets the asyncSigVerification flag. * The actual verification is handled by the verifySignatureAsync function in signature.ts, * which will use the worker if available. */ set signatureVerificationWorker(worker4) { this._signatureVerificationWorker = worker4; if (worker4) { signatureVerificationInit(worker4); this.asyncSigVerification = true; } else { this.asyncSigVerification = false; } } /** * Set a custom signature verification function. * * This method is particularly useful for platforms that don't support Web Workers, * such as React Native. * * When a function is provided, it will be used for signature verification * instead of the default worker-based verification. This enables signature * verification on platforms where Web Workers are not available. * * @example * ```typescript * import { verifySignatureAsync } from "@nostr-dev-kit/mobile"; * * ndk.signatureVerificationFunction = verifySignatureAsync; * ``` */ set signatureVerificationFunction(fn) { this._signatureVerificationFunction = fn; this.asyncSigVerification = !!fn; } /** * Get the custom signature verification function */ get signatureVerificationFunction() { return this._signatureVerificationFunction; } /** * Adds an explicit relay to the pool. * @param url * @param relayAuthPolicy Authentication policy to use if different from the default * @param connect Whether to connect to the relay automatically * @returns */ addExplicitRelay(urlOrRelay, relayAuthPolicy, connect = true) { let relay; if (typeof urlOrRelay === "string") { relay = new NDKRelay(urlOrRelay, relayAuthPolicy, this); } else { relay = urlOrRelay; } this.pool.addRelay(relay, connect); this.explicitRelayUrls?.push(relay.url); return relay; } toJSON() { return { relayCount: this.pool.relays.size }.toString(); } get activeUser() { return this._activeUser; } /** * Sets the active user for this NDK instance, typically this will be * called when assigning a signer to the NDK instance. * * This function will automatically connect to the user's relays if * `autoConnectUserRelays` is set to true. */ set activeUser(user) { const differentUser = this._activeUser?.pubkey !== user?.pubkey; this._activeUser = user; if (differentUser) { this.emit("activeUser:change", user); } if (user && differentUser) { setActiveUser.call(this, user); } } get signer() { return this._signer; } set signer(newSigner) { this._signer = newSigner; if (newSigner) this.emit("signer:ready", newSigner); newSigner?.user().then((user) => { user.ndk = this; this.activeUser = user; }); } /** * Connect to relays with optional timeout. * If the timeout is reached, the connection will be continued to be established in the background. */ async connect(timeoutMs) { if (this._signer && this.autoConnectUserRelays) { this.debug( "Attempting to connect to user relays specified by signer %o", await this._signer.relays?.(this) ); if (this._signer.relays) { const relays = await this._signer.relays(this); relays.forEach((relay) => this.pool.addRelay(relay)); } } const connections = [this.pool.connect(timeoutMs)]; if (this.outboxPool) { connections.push(this.outboxPool.connect(timeoutMs)); } if (this.cacheAdapter?.initializeAsync) { connections.push(this.cacheAdapter.initializeAsync(this)); } return Promise.allSettled(connections).then(() => { }); } /** * Centralized method to report an invalid signature, identifying the relay that provided it. * A single invalid signature means the relay is considered malicious. * All invalid signature detections (synchronous or asynchronous) should delegate to this method. * * @param event The event with an invalid signature * @param relay The relay that provided the invalid signature */ reportInvalidSignature(event, relay) { this.debug(`Invalid signature detected for event ${event.id}${relay ? ` from relay ${relay.url}` : ""}`); this.emit("event:invalid-sig", event, relay); } /** * Default function to calculate validation ratio based on historical validation results. * The more events validated successfully, the lower the ratio goes (down to the minimum). */ defaultValidationRatioFn(_relay, validatedCount, _nonValidatedCount) { if (validatedCount < 10) return this.initialValidationRatio; const trustFactor = Math.min(validatedCount / 100, 1); const calculatedRatio = this.initialValidationRatio * (1 - trustFactor) + this.lowestValidationRatio * trustFactor; return Math.max(calculatedRatio, this.lowestValidationRatio); } /** * Get a NDKUser object * * @deprecated Use `fetchUser` instead - this method will be removed in the next major version * @param opts - User parameters object or a string (npub, nprofile, or hex pubkey) * @returns NDKUser instance * * @example * ```typescript * // Using parameters object * const user1 = ndk.getUser({ pubkey: "hex..." }); * * // Using npub string * const user2 = ndk.getUser("npub1..."); * * // Using nprofile string (includes relay hints) * const user3 = ndk.getUser("nprofile1..."); * * // Using hex pubkey directly * const user4 = ndk.getUser("deadbeef..."); * ``` */ getUser(opts) { if (typeof opts === "string") { if (opts.startsWith("npub1")) { const { type, data } = import_nostr_tools10.nip19.decode(opts); if (type !== "npub") throw new Error(`Invalid npub: ${opts}`); return this.getUser({ pubkey: data }); } else if (opts.startsWith("nprofile1")) { const { type, data } = import_nostr_tools10.nip19.decode(opts); if (type !== "nprofile") throw new Error(`Invalid nprofile: ${opts}`); return this.getUser({ pubkey: data.pubkey, relayUrls: data.relays }); } else { return this.getUser({ pubkey: opts }); } } const user = new NDKUser(opts); user.ndk = this; return user; } /** * Get a NDKUser from a NIP05 * @deprecated Use `fetchUser` instead - this method will be removed in the next major version * @param nip05 NIP-05 ID * @param skipCache Skip cache * @returns */ async getUserFromNip05(nip05, skipCache = false) { return NDKUser.fromNip05(nip05, this, skipCache); } /** * Fetch a NDKUser from a string identifier * * Supports multiple input formats: * - NIP-05 identifiers (e.g., "pablo@test.com" or "test.com") * - npub (NIP-19 encoded public key) * - nprofile (NIP-19 encoded profile with optional relay hints) * - Hex public key * * @param input - String identifier for the user (NIP-05, npub, nprofile, or hex pubkey) * @param skipCache - Skip cache when resolving NIP-05 (only applies to NIP-05 lookups) * @returns Promise resolving to NDKUser or undefined if not found * * @example * ```typescript * // Using NIP-05 * const user1 = await ndk.fetchUser("pablo@test.com"); * const user2 = await ndk.fetchUser("test.com"); // defaults to _@test.com * * // Using npub * const user3 = await ndk.fetchUser("npub1..."); * * // Using nprofile (includes relay hints) * const user4 = await ndk.fetchUser("nprofile1..."); * * // Using hex pubkey * const user5 = await ndk.fetchUser("deadbeef..."); * ``` */ async fetchUser(input, skipCache = false) { if (isValidNip05(input)) { return NDKUser.fromNip05(input, this, skipCache); } else if (input.startsWith("npub1")) { const { type, data } = import_nostr_tools10.nip19.decode(input); if (type !== "npub") throw new Error(`Invalid npub: ${input}`); const user = new NDKUser({ pubkey: data }); user.ndk = this; return user; } else if (input.startsWith("nprofile1")) { const { type, data } = import_nostr_tools10.nip19.decode(input); if (type !== "nprofile") throw new Error(`Invalid nprofile: ${input}`); const user = new NDKUser({ pubkey: data.pubkey, relayUrls: data.relays }); user.ndk = this; return user; } else { const user = new NDKUser({ pubkey: input }); user.ndk = this; return user; } } /** * Creates and starts a new subscription. * * Subscriptions automatically start unless `autoStart` is set to `false`. * You can control automatic closing on EOSE via `opts.closeOnEose`. * * @param filters - A single NDKFilter object or an array of filters. * @param opts - Optional NDKSubscriptionOptions to customize behavior (e.g., caching, grouping). * @param handlers - Optional handlers for subscription events. Passing handlers is the preferred method of using ndk.subscribe. * - `onEvent`: Called for each event received. * - `onEvents`: Called once with an array of events when the subscription starts (from the cache). * - `onEose`: Called when the subscription receives EOSE. * For backwards compatibility, this third parameter also accepts a relaySet, the relaySet should be passed via `opts.relaySet`. * * @param _autoStart - For backwards compatibility, this can be a boolean indicating whether to start the subscription immediately. * This parameter is deprecated and will be removed in a future version. * - `false`: Creates the subscription but does not start it (call `subscription.start()` manually). * @returns The created NDKSubscription instance. * * @example Basic subscription * ```typescript * const sub = ndk.subscribe( * { kinds: [1], authors: [pubkey] }, * { * onEvent: (event) => console.log("Kind 1 event:", event.content) * } * ); * ``` * * @example Subscription with options and direct handlers * ```typescript * const sub = ndk.subscribe( * { kinds: [0], authors: [pubkey] }, * { * closeOnEose: true, * cacheUsage: NDKSubscriptionCacheUsage.PARALLEL, * onEvents: (events) => console.log(`Got ${events.length} profile events from cache:`, events[0].content), * onEvent: (event) => console.log("Got profile update from relay:", event.content), * onEose: () => console.log("Profile subscription finished.") * } * ); * ``` * * @since 2.13.0 `relaySet` parameter removed; pass `relaySet` or `relayUrls` via `opts`. */ subscribe(filters, opts, autoStartOrRelaySet = true, _autoStart = true) { let _relaySet = opts?.relaySet; let autoStart = _autoStart; if (autoStartOrRelaySet instanceof NDKRelaySet) { console.warn("relaySet is deprecated, use opts.relaySet instead. This will be removed in version v2.14.0"); _relaySet = autoStartOrRelaySet; autoStart = _autoStart; } else if (typeof autoStartOrRelaySet === "boolean" || typeof autoStartOrRelaySet === "object") { autoStart = autoStartOrRelaySet; } const finalOpts = { relaySet: _relaySet, ...opts }; if (autoStart && typeof autoStart === "object") { if (autoStart.onEvent) finalOpts.onEvent = autoStart.onEvent; if (autoStart.onEose) finalOpts.onEose = autoStart.onEose; if (autoStart.onClose) finalOpts.onClose = autoStart.onClose; if (autoStart.onEvents) finalOpts.onEvents = autoStart.onEvents; } const subscription = new NDKSubscription(this, filters, finalOpts); this.subManager.add(subscription); this.aiGuardrails?.subscription?.created(Array.isArray(filters) ? filters : [filters], finalOpts); const pool = subscription.pool; if (subscription.relaySet) { for (const relay of subscription.relaySet.relays) { pool.useTemporaryRelay(relay, void 0, subscription.filters); } } if (this.outboxPool && subscription.hasAuthorsFilter()) { const authors = subscription.filters.filter((filter) => filter.authors && filter.authors?.length > 0).flatMap((filter) => filter.authors); this.outboxTracker?.trackUsers(authors); } if (autoStart) { setTimeout(async () => { if (this.cacheAdapter?.initializeAsync && !this.cacheAdapter.ready) { await this.cacheAdapter.initializeAsync(this); } subscription.start(); }, 0); } return subscription; } /** * Fetch an event from the cache synchronously. * @param idOrFilter event id in bech32 format or filter * @returns events from the cache or null if the cache is empty */ fetchEventSync(idOrFilter) { if (!this.cacheAdapter) throw new Error("Cache adapter not set"); let filters; if (typeof idOrFilter === "string") filters = [filterFromId(idOrFilter)]; else filters = idOrFilter; const sub = new NDKSubscription(this, filters); const events = this.cacheAdapter.query(sub); if (events instanceof Promise) throw new Error("Cache adapter is async"); return events.map((e2) => { e2.ndk = this; return e2; }); } /** * Fetch a single event. * * @param idOrFilter event id in bech32 format or filter * @param opts subscription options * @param relaySetOrRelay explicit relay set to use */ async fetchEvent(idOrFilter, opts, relaySetOrRelay) { let filters; let relaySet; if (relaySetOrRelay instanceof NDKRelay) { relaySet = new NDKRelaySet(/* @__PURE__ */ new Set([relaySetOrRelay]), this); } else if (relaySetOrRelay instanceof NDKRelaySet) { relaySet = relaySetOrRelay; } if (!relaySetOrRelay && typeof idOrFilter === "string") { if (!isNip33AValue(idOrFilter)) { const relays = relaysFromBech32(idOrFilter, this); if (relays.length > 0) { relaySet = new NDKRelaySet(new Set(relays), this); relaySet = correctRelaySet(relaySet, this.pool); } } } if (typeof idOrFilter === "string") { filters = [filterFromId(idOrFilter)]; } else if (Array.isArray(idOrFilter)) { filters = idOrFilter; } else { filters = [idOrFilter]; } if (typeof idOrFilter !== "string") { this.aiGuardrails?.ndk?.fetchingEvents(filters); } if (filters.length === 0) { throw new Error(`Invalid filter: ${JSON.stringify(idOrFilter)}`); } return new Promise((resolve, reject) => { let fetchedEvent = null; const processEvent = (event) => { event.ndk = this; if (!event.isReplaceable()) { clearTimeout(t2); s?.stop(); this.aiGuardrails["_nextCallDisabled"] = null; resolve(event); } else if (!fetchedEvent || fetchedEvent.created_at < event.created_at) { fetchedEvent = event; } }; const subscribeOpts = { ...opts || {}, closeOnEose: true, // Batch handler for cached events onEvents: (cachedEvents) => { for (const event of cachedEvents) { processEvent(event); } }, // Individual handler for relay events onEvent: (event) => { processEvent(event); }, onEose: () => { clearTimeout(t2); this.aiGuardrails["_nextCallDisabled"] = null; resolve(fetchedEvent); } }; if (relaySet) subscribeOpts.relaySet = relaySet; let s; const t2 = setTimeout(() => { s?.stop(); this.aiGuardrails["_nextCallDisabled"] = null; resolve(fetchedEvent); }, 1e4); s = this.subscribe(filters, subscribeOpts); }); } /** * Fetch events */ async fetchEvents(filters, opts, relaySet) { this.aiGuardrails?.ndk?.fetchingEvents(filters, opts); return new Promise((resolve) => { const events = /* @__PURE__ */ new Map(); const processEvent = (event) => { let _event; if (!(event instanceof NDKEvent)) _event = new NDKEvent(void 0, event); else _event = event; const dedupKey = _event.deduplicationKey(); const existingEvent = events.get(dedupKey); if (existingEvent) { _event = dedup(existingEvent, _event); } _event.ndk = this; events.set(dedupKey, _event); }; const subscribeOpts = { ...opts || {}, closeOnEose: true, onEvents: (cachedEvents) => { for (const event of cachedEvents) { processEvent(event); } }, onEvent: processEvent, onEose: () => { this.aiGuardrails["_nextCallDisabled"] = null; resolve(new Set(events.values())); } }; if (relaySet) subscribeOpts.relaySet = relaySet; const _relaySetSubscription = this.subscribe(filters, subscribeOpts); }); } /** * Ensures that a signer is available to sign an event. */ assertSigner() { if (!this.signer) { this.emit("signer:required"); throw new Error("Signer required"); } } /** * Temporarily disable AI guardrails for the next method call. * * @param ids - Optional guardrail IDs to disable. If omitted, all guardrails are disabled for the next call. * Can be a single string or an array of strings. * @returns This NDK instance for method chaining * * @example Disable all guardrails for one call * ```typescript * ndk.guardrailOff().fetchEvents({ kinds: [1] }); * ``` * * @example Disable specific guardrail * ```typescript * ndk.guardrailOff('fetch-events-usage').fetchEvents({ kinds: [1] }); * ``` * * @example Disable multiple guardrails * ```typescript * ndk.guardrailOff(['fetch-events-usage', 'filter-large-limit']).fetchEvents({ kinds: [1], limit: 5000 }); * ``` */ guardrailOff(ids) { if (!ids) { this.aiGuardrails["_nextCallDisabled"] = "all"; } else if (typeof ids === "string") { this.aiGuardrails["_nextCallDisabled"] = /* @__PURE__ */ new Set([ids]); } else { this.aiGuardrails["_nextCallDisabled"] = new Set(ids); } return this; } set wallet(wallet) { if (!wallet) { this._wallet = void 0; this.walletConfig = void 0; return; } this._wallet = wallet; this.walletConfig ?? (this.walletConfig = {}); this.walletConfig.lnPay = wallet?.lnPay?.bind(wallet); this.walletConfig.cashuPay = wallet?.cashuPay?.bind(wallet); } get wallet() { return this._wallet; } }; // ndk/core/src/nip19/index.ts var nip19_exports = {}; __reExport(nip19_exports, __toESM(require_nip19())); // ndk/core/src/nip49/index.ts var nip49_exports = {}; __reExport(nip49_exports, __toESM(require_nip49())); // ndk/core/src/relay/auth-policies.ts var import_debug9 = __toESM(require_browser()); function disconnect(pool, debug15) { debug15 ?? (debug15 = (0, import_debug9.default)("ndk:relay:auth-policies:disconnect")); return async (relay) => { debug15?.(`Relay ${relay.url} requested authentication, disconnecting`); pool.removeRelay(relay.url); }; } async function signAndAuth(event, relay, signer, debug15, resolve, reject) { try { await event.sign(signer); resolve(event); } catch (e2) { debug15?.(`Failed to publish auth event to relay ${relay.url}`, e2); reject(event); } } function signIn({ ndk, signer, debug: debug15 } = {}) { debug15 ?? (debug15 = (0, import_debug9.default)("ndk:auth-policies:signIn")); return async (relay, challenge3) => { debug15?.(`Relay ${relay.url} requested authentication, signing in`); const event = new NDKEvent(ndk); event.kind = 22242 /* ClientAuth */; event.tags = [ ["relay", relay.url], ["challenge", challenge3] ]; signer ?? (signer = ndk?.signer); return new Promise(async (resolve, reject) => { if (signer) { await signAndAuth(event, relay, signer, debug15, resolve, reject); } else { ndk?.once("signer:ready", async (signer2) => { await signAndAuth(event, relay, signer2, debug15, resolve, reject); }); } }); }; } var NDKRelayAuthPolicies = { disconnect, signIn }; // ndk/core/src/signers/deserialization.ts async function ndkSignerFromPayload(payloadString, ndk) { let parsed; try { parsed = JSON.parse(payloadString); } catch (e2) { console.error("Failed to parse signer payload string", payloadString, e2); return void 0; } if (!parsed || typeof parsed.type !== "string") { console.error("Failed to parse signer payload string", payloadString, new Error("Missing type field")); return void 0; } const SignerClass = signerRegistry.get(parsed.type); if (!SignerClass) { throw new Error(`Unknown signer type: ${parsed.type}`); } try { return await SignerClass.fromPayload(payloadString, ndk); } catch (e2) { const errorMsg = e2 instanceof Error ? e2.message : String(e2); throw new Error(`Failed to deserialize signer type ${parsed.type}: ${errorMsg}`); } } // ndk/core/src/signers/nip07/index.ts var import_debug10 = __toESM(require_browser()); var NDKNip07Signer = class _NDKNip07Signer3 { /** * @param waitTimeout - The timeout in milliseconds to wait for the NIP-07 to become available */ constructor(waitTimeout = 1e3, ndk) { __publicField(this, "_userPromise"); __publicField(this, "encryptionQueue", []); __publicField(this, "encryptionProcessing", false); __publicField(this, "debug"); __publicField(this, "waitTimeout"); __publicField(this, "_pubkey"); __publicField(this, "ndk"); __publicField(this, "_user"); this.debug = (0, import_debug10.default)("ndk:nip07"); this.waitTimeout = waitTimeout; this.ndk = ndk; } get pubkey() { if (!this._pubkey) throw new Error("Not ready"); return this._pubkey; } async blockUntilReady() { await this.waitForExtension(); const pubkey = await window.nostr?.getPublicKey(); if (!pubkey) { throw new Error("User rejected access"); } this._pubkey = pubkey; let user; if (this.ndk) user = this.ndk.getUser({ pubkey }); else user = new NDKUser({ pubkey }); this._user = user; return user; } /** * Getter for the user property. * @returns The NDKUser instance. */ async user() { if (!this._userPromise) { this._userPromise = this.blockUntilReady(); } return this._userPromise; } get userSync() { if (!this._user) throw new Error("User not ready"); return this._user; } /** * Signs the given Nostr event. * @param event - The Nostr event to be signed. * @returns The signature of the signed event. * @throws Error if the NIP-07 is not available on the window object. */ async sign(event) { await this.waitForExtension(); const signedEvent = await window.nostr?.signEvent(event); if (!signedEvent) throw new Error("Failed to sign event"); return signedEvent.sig; } async relays(ndk) { await this.waitForExtension(); const relays = await window.nostr?.getRelays?.() || {}; const activeRelays = []; for (const url of Object.keys(relays)) { if (relays[url].read && relays[url].write) { activeRelays.push(url); } } return activeRelays.map((url) => new NDKRelay(url, ndk?.relayAuthDefaultPolicy, ndk)); } async encryptionEnabled(nip) { const enabled = []; if ((!nip || nip === "nip04") && Boolean(window.nostr?.nip04)) enabled.push("nip04"); if ((!nip || nip === "nip44") && Boolean(window.nostr?.nip44)) enabled.push("nip44"); return enabled; } async encrypt(recipient, value, nip = "nip04") { if (!await this.encryptionEnabled(nip)) throw new Error(`${nip}encryption is not available from your browser extension`); await this.waitForExtension(); const recipientHexPubKey = recipient.pubkey; return this.queueEncryption(nip, "encrypt", recipientHexPubKey, value); } async decrypt(sender, value, nip = "nip04") { if (!await this.encryptionEnabled(nip)) throw new Error(`${nip}encryption is not available from your browser extension`); await this.waitForExtension(); const senderHexPubKey = sender.pubkey; return this.queueEncryption(nip, "decrypt", senderHexPubKey, value); } async queueEncryption(scheme, method, counterpartyHexpubkey, value) { return new Promise((resolve, reject) => { this.encryptionQueue.push({ scheme, method, counterpartyHexpubkey, value, resolve, reject }); if (!this.encryptionProcessing) { this.processEncryptionQueue(); } }); } async processEncryptionQueue(item, retries = 0) { if (!item && this.encryptionQueue.length === 0) { this.encryptionProcessing = false; return; } this.encryptionProcessing = true; const currentItem = item || this.encryptionQueue.shift(); if (!currentItem) { this.encryptionProcessing = false; return; } const { scheme, method, counterpartyHexpubkey, value, resolve, reject } = currentItem; this.debug("Processing encryption queue item", { method, counterpartyHexpubkey, value }); try { const result = await window.nostr?.[scheme]?.[method](counterpartyHexpubkey, value); if (!result) throw new Error("Failed to encrypt/decrypt"); resolve(result); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); if (errorMessage.includes("call already executing") && retries < 5) { this.debug("Retrying encryption queue item", { method, counterpartyHexpubkey, value, retries }); setTimeout(() => { this.processEncryptionQueue(currentItem, retries + 1); }, 50 * retries); return; } reject(error instanceof Error ? error : new Error(errorMessage)); } this.processEncryptionQueue(); } waitForExtension() { return new Promise((resolve, reject) => { if (window.nostr) { resolve(); return; } let timerId; const intervalId = setInterval(() => { if (window.nostr) { clearTimeout(timerId); clearInterval(intervalId); resolve(); } }, 100); timerId = setTimeout(() => { clearInterval(intervalId); reject(new Error("NIP-07 extension not available")); }, this.waitTimeout); }); } /** * Serializes the signer type into a storable format. * NIP-07 signers don't have persistent state to serialize beyond their type. * @returns A JSON string containing the type. */ toPayload() { const payload = { type: "nip07", payload: "" // No specific payload needed for NIP-07 }; return JSON.stringify(payload); } /** * Deserializes the signer from a payload string. * Creates a new NDKNip07Signer instance. * @param payloadString The JSON string obtained from toPayload(). * @param ndk Optional NDK instance. * @returns An instance of NDKNip07Signer. */ static async fromPayload(payloadString, ndk) { const payload = JSON.parse(payloadString); if (payload.type !== "nip07") { throw new Error(`Invalid payload type: expected 'nip07', got ${payload.type}`); } return new _NDKNip07Signer3(void 0, ndk); } }; registerSigner("nip07", NDKNip07Signer); // ndk/core/src/signers/nip46/backend/index.ts init_utils(); // ndk/core/src/signers/nip46/rpc.ts var import_tseep7 = __toESM(require_lib()); var NDKNostrRpc = class extends import_tseep7.EventEmitter { constructor(ndk, signer, debug15, relayUrls) { super(); __publicField(this, "ndk"); __publicField(this, "signer"); __publicField(this, "relaySet"); __publicField(this, "debug"); __publicField(this, "encryptionType", "nip44"); __publicField(this, "pool"); this.ndk = ndk; this.signer = signer; if (relayUrls) { this.pool = new NDKPool(relayUrls, ndk, { debug: debug15.extend("rpc-pool"), name: "Nostr RPC" }); this.relaySet = new NDKRelaySet(/* @__PURE__ */ new Set(), ndk, this.pool); for (const url of relayUrls) { const relay = this.pool.getRelay(url, false, false); relay.authPolicy = NDKRelayAuthPolicies.signIn({ ndk, signer, debug: debug15 }); this.relaySet.addRelay(relay); relay.connect(); } } this.debug = debug15.extend("rpc"); } /** * Subscribe to a filter. This function will resolve once the subscription is ready. */ subscribe(filter) { return new Promise((resolve) => { const sub = this.ndk.subscribe(filter, { closeOnEose: false, groupable: false, cacheUsage: "ONLY_RELAY" /* ONLY_RELAY */, pool: this.pool, relaySet: this.relaySet, onEvent: async (event) => { try { const parsedEvent = await this.parseEvent(event); if (parsedEvent.method) { this.emit("request", parsedEvent); } else { this.emit(`response-${parsedEvent.id}`, parsedEvent); this.emit("response", parsedEvent); } } catch (e2) { this.debug("error parsing event", e2, event.rawEvent()); } }, onEose: () => { this.debug("eosed"); resolve(sub); } }); }); } async parseEvent(event) { if (this.encryptionType === "nip44" && event.content.includes("?iv=")) { this.encryptionType = "nip04"; } else if (this.encryptionType === "nip04" && !event.content.includes("?iv=")) { this.encryptionType = "nip44"; } const remoteUser = this.ndk.getUser({ pubkey: event.pubkey }); remoteUser.ndk = this.ndk; let decryptedContent; try { decryptedContent = await this.signer.decrypt(remoteUser, event.content, this.encryptionType); } catch (_e2) { const otherEncryptionType = this.encryptionType === "nip04" ? "nip44" : "nip04"; decryptedContent = await this.signer.decrypt(remoteUser, event.content, otherEncryptionType); this.encryptionType = otherEncryptionType; } const parsedContent = JSON.parse(decryptedContent); const { id, method, params, result, error } = parsedContent; if (method) { return { id, pubkey: event.pubkey, method, params, event }; } return { id, result, error, event }; } async sendResponse(id, remotePubkey, result, kind = 24133 /* NostrConnect */, error) { const res = { id, result }; if (error) { res.error = error; } const localUser = await this.signer.user(); const remoteUser = this.ndk.getUser({ pubkey: remotePubkey }); const event = new NDKEvent(this.ndk, { kind, content: JSON.stringify(res), tags: [["p", remotePubkey]], pubkey: localUser.pubkey }); event.content = await this.signer.encrypt(remoteUser, event.content, this.encryptionType); await event.sign(this.signer); await event.publish(this.relaySet); } /** * Sends a request. * @param remotePubkey * @param method * @param params * @param kind * @param id */ async sendRequest(remotePubkey, method, params = [], kind = 24133, cb) { const id = Math.random().toString(36).substring(7); const localUser = await this.signer.user(); const remoteUser = this.ndk.getUser({ pubkey: remotePubkey }); const request = { id, method, params }; const promise = new Promise(() => { const responseHandler = (response) => { if (response.result === "auth_url") { this.once(`response-${id}`, responseHandler); this.emit("authUrl", response.error); } else if (cb) { cb(response); } }; this.once(`response-${id}`, responseHandler); }); const event = new NDKEvent(this.ndk, { kind, content: JSON.stringify(request), tags: [["p", remotePubkey]], pubkey: localUser.pubkey }); event.content = await this.signer.encrypt(remoteUser, event.content, this.encryptionType); await event.sign(this.signer); await event.publish(this.relaySet); return promise; } }; // ndk/core/src/signers/nip46/backend/connect.ts var ConnectEventHandlingStrategy = class { async handle(backend, id, remotePubkey, params) { const [_2, token] = params; const debug15 = backend.debug.extend("connect"); debug15(`connection request from ${remotePubkey}`); if (token && backend.applyToken) { debug15("applying token"); await backend.applyToken(remotePubkey, token); } if (await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "connect", params: token })) { debug15(`connection request from ${remotePubkey} allowed`); return "ack"; } debug15(`connection request from ${remotePubkey} rejected`); return void 0; } }; // ndk/core/src/signers/nip46/backend/get-public-key.ts var GetPublicKeyHandlingStrategy = class { async handle(backend, _id, _remotePubkey, _params) { return backend.localUser?.pubkey; } }; // ndk/core/src/signers/nip46/backend/nip04-decrypt.ts var Nip04DecryptHandlingStrategy = class { async handle(backend, id, remotePubkey, params) { const [senderPubkey, payload] = params; const senderUser = new NDKUser({ pubkey: senderPubkey }); const decryptedPayload = await decrypt3(backend, id, remotePubkey, senderUser, payload); return decryptedPayload; } }; async function decrypt3(backend, id, remotePubkey, senderUser, payload) { if (!await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "nip04_decrypt", params: payload })) { backend.debug(`decrypt request from ${remotePubkey} rejected`); return void 0; } return await backend.signer.decrypt(senderUser, payload, "nip04"); } // ndk/core/src/signers/nip46/backend/nip04-encrypt.ts var Nip04EncryptHandlingStrategy = class { async handle(backend, id, remotePubkey, params) { const [recipientPubkey, payload] = params; const recipientUser = new NDKUser({ pubkey: recipientPubkey }); const encryptedPayload = await encrypt3(backend, id, remotePubkey, recipientUser, payload); return encryptedPayload; } }; async function encrypt3(backend, id, remotePubkey, recipientUser, payload) { if (!await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "nip04_encrypt", params: payload })) { backend.debug(`encrypt request from ${remotePubkey} rejected`); return void 0; } return await backend.signer.encrypt(recipientUser, payload, "nip04"); } // ndk/core/src/signers/nip46/backend/nip44-decrypt.ts var Nip44DecryptHandlingStrategy = class { async handle(backend, id, remotePubkey, params) { const [senderPubkey, payload] = params; const senderUser = new NDKUser({ pubkey: senderPubkey }); const decryptedPayload = await decrypt4(backend, id, remotePubkey, senderUser, payload); return decryptedPayload; } }; async function decrypt4(backend, id, remotePubkey, senderUser, payload) { if (!await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "nip44_decrypt", params: payload })) { backend.debug(`decrypt request from ${remotePubkey} rejected`); return void 0; } return await backend.signer.decrypt(senderUser, payload, "nip44"); } // ndk/core/src/signers/nip46/backend/nip44-encrypt.ts var Nip44EncryptHandlingStrategy = class { async handle(backend, id, remotePubkey, params) { const [recipientPubkey, payload] = params; const recipientUser = new NDKUser({ pubkey: recipientPubkey }); const encryptedPayload = await encrypt4(backend, id, remotePubkey, recipientUser, payload); return encryptedPayload; } }; async function encrypt4(backend, id, remotePubkey, recipientUser, payload) { if (!await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "nip44_encrypt", params: payload })) { backend.debug(`encrypt request from ${remotePubkey} rejected`); return void 0; } return await backend.signer.encrypt(recipientUser, payload, "nip44"); } // ndk/core/src/signers/nip46/backend/ping.ts var PingEventHandlingStrategy = class { async handle(backend, id, remotePubkey, _params) { const debug15 = backend.debug.extend("ping"); debug15(`ping request from ${remotePubkey}`); if (await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "ping" })) { debug15(`connection request from ${remotePubkey} allowed`); return "pong"; } debug15(`connection request from ${remotePubkey} rejected`); return void 0; } }; // ndk/core/src/signers/nip46/backend/sign-event.ts var SignEventHandlingStrategy = class { async handle(backend, id, remotePubkey, params) { const event = await signEvent(backend, id, remotePubkey, params); if (!event) return void 0; return JSON.stringify(await event.toNostrEvent()); } }; async function signEvent(backend, id, remotePubkey, params) { const [eventString] = params; backend.debug(`sign event request from ${remotePubkey}`); const event = new NDKEvent(backend.ndk, JSON.parse(eventString)); backend.debug("event to sign", event.rawEvent()); if (!await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "sign_event", params: event })) { backend.debug(`sign event request from ${remotePubkey} rejected`); return void 0; } backend.debug(`sign event request from ${remotePubkey} allowed`); await event.sign(backend.signer); return event; } // ndk/core/src/signers/nip46/backend/index.ts var NDKNip46Backend = class { /** * @param ndk The NDK instance to use * @param privateKeyOrSigner The private key or signer of the npub that wants to be published as * @param permitCallback Callback executed when permission is requested */ constructor(ndk, privateKeyOrSigner, permitCallback, relayUrls) { __publicField(this, "ndk"); __publicField(this, "signer"); __publicField(this, "localUser"); __publicField(this, "debug"); __publicField(this, "rpc"); __publicField(this, "permitCallback"); __publicField(this, "relayUrls"); __publicField(this, "handlers", { connect: new ConnectEventHandlingStrategy(), sign_event: new SignEventHandlingStrategy(), nip04_encrypt: new Nip04EncryptHandlingStrategy(), nip04_decrypt: new Nip04DecryptHandlingStrategy(), nip44_encrypt: new Nip44EncryptHandlingStrategy(), nip44_decrypt: new Nip44DecryptHandlingStrategy(), get_public_key: new GetPublicKeyHandlingStrategy(), ping: new PingEventHandlingStrategy() }); this.ndk = ndk; if (privateKeyOrSigner instanceof Uint8Array) { this.signer = new NDKPrivateKeySigner(privateKeyOrSigner); } else if (privateKeyOrSigner instanceof String) { this.signer = new NDKPrivateKeySigner(hexToBytes(privateKeyOrSigner)); } else if (privateKeyOrSigner instanceof NDKPrivateKeySigner) { this.signer = privateKeyOrSigner; } else { throw new Error("Invalid signer"); } this.debug = ndk.debug.extend("nip46:backend"); this.relayUrls = relayUrls ?? Array.from(ndk.pool.relays.keys()); this.rpc = new NDKNostrRpc(ndk, this.signer, this.debug, this.relayUrls); this.permitCallback = permitCallback; } /** * This method starts the backend, which will start listening for incoming * requests. */ async start() { this.localUser = await this.signer.user(); this.ndk.subscribe( { kinds: [24133], "#p": [this.localUser.pubkey] }, { closeOnEose: false, onEvent: (e2) => this.handleIncomingEvent(e2) } ); } /** * Enables the user to set a custom strategy for handling incoming events. * @param method - The method to set the strategy for * @param strategy - The strategy to set */ setStrategy(method, strategy) { this.handlers[method] = strategy; } /** * Overload this method to apply tokens, which can * wrap permission sets to be applied to a pubkey. * @param pubkey public key to apply token to * @param token token to apply */ async applyToken(_pubkey, _token) { throw new Error("connection token not supported"); } async handleIncomingEvent(event) { const { id, method, params } = await this.rpc.parseEvent(event); const remotePubkey = event.pubkey; let response; let errorHandled = false; this.debug("incoming event", { id, method, params }); if (!event.verifySignature(false)) { this.debug("invalid signature", event.rawEvent()); return; } const strategy = this.handlers[method]; if (strategy) { try { response = await strategy.handle(this, id, remotePubkey, params); } catch (e2) { this.debug("error handling event", e2, { id, method, params }); errorHandled = true; try { await this.rpc.sendResponse(id, remotePubkey, "error", void 0, e2.message); } catch (sendError) { this.debug("failed to send error response", sendError); } } } else { this.debug("unsupported method", { method, params }); } if (!errorHandled) { try { if (response) { this.debug(`sending response to ${remotePubkey}`, response); await this.rpc.sendResponse(id, remotePubkey, response); } else { await this.rpc.sendResponse(id, remotePubkey, "error", void 0, "Not authorized"); } } catch (sendError) { this.debug("failed to send response", sendError); } } } /** * This method should be overriden by the user to allow or reject incoming * connections. */ async pubkeyAllowed(params) { return this.permitCallback(params); } }; // ndk/core/src/signers/nip46/index.ts var import_tseep8 = __toESM(require_lib()); // ndk/core/src/signers/nip46/nostrconnect.ts function nostrConnectGenerateSecret() { return Math.random().toString(36).substring(2, 15); } function generateNostrConnectUri(pubkey, secret, relay, options) { const meta = { name: options?.name ? encodeURIComponent(options.name) : "", url: options?.url ? encodeURIComponent(options.url) : "", image: options?.image ? encodeURIComponent(options.image) : "", perms: options?.perms ? encodeURIComponent(options.perms) : "" }; let uri = `nostrconnect://${pubkey}?image=${meta.image}&url=${meta.url}&name=${meta.name}&perms=${meta.perms}&secret=${encodeURIComponent(secret)}`; if (relay) { uri += `&relay=${encodeURIComponent(relay)}`; } return uri; } // ndk/core/src/signers/nip46/index.ts var NDKNip46Signer = class _NDKNip46Signer3 extends import_tseep8.EventEmitter { /** * * Don't instantiate this directly. Use the static methods instead. * * @example: * // for bunker:// flow * const signer = NDKNip46Signer.bunker(ndk, "bunker://") * const signer = NDKNip46Signer.bunker(ndk, ""); // with nip05 flow * // for nostrconnect:// flow * const signer = NDKNip46Signer.nostrconnect(ndk, "wss://relay.example.com") * * @param ndk - The NDK instance to use * @param userOrConnectionToken - The public key, or a connection token, of the npub that wants to be published as * @param localSigner - The signer that will be used to request events to be signed */ constructor(ndk, userOrConnectionToken, localSigner, relayUrls, nostrConnectOptions) { super(); __publicField(this, "ndk"); __publicField(this, "_user"); /** * The pubkey of the bunker that will be providing signatures */ __publicField(this, "bunkerPubkey"); /** * The pubkey of the user that events will be published as */ __publicField(this, "userPubkey"); /** * An optional secret value provided to connect to the bunker */ __publicField(this, "secret"); __publicField(this, "localSigner"); __publicField(this, "nip05"); __publicField(this, "rpc"); __publicField(this, "debug"); __publicField(this, "relayUrls"); __publicField(this, "subscription"); /** * If using nostrconnect://, stores the nostrConnectURI */ __publicField(this, "nostrConnectUri"); /** * The random secret used for nostrconnect:// flows. */ __publicField(this, "nostrConnectSecret"); this.ndk = ndk; this.debug = ndk.debug.extend("nip46:signer"); this.relayUrls = relayUrls; if (!localSigner) { this.localSigner = NDKPrivateKeySigner.generate(); } else { if (typeof localSigner === "string") { this.localSigner = new NDKPrivateKeySigner(localSigner); } else { this.localSigner = localSigner; } } if (userOrConnectionToken === false) { } else if (!userOrConnectionToken) { this.nostrconnectFlowInit(nostrConnectOptions); } else if (userOrConnectionToken.startsWith("bunker://")) { this.bunkerFlowInit(userOrConnectionToken); } else { this.nip05Init(userOrConnectionToken); } this.rpc = new NDKNostrRpc(this.ndk, this.localSigner, this.debug, this.relayUrls); } get pubkey() { if (!this.userPubkey) throw new Error("Not ready"); return this.userPubkey; } /** * Connnect with a bunker:// flow * @param ndk * @param userOrConnectionToken bunker:// connection string * @param localSigner If you have previously authenticated with this signer, you can restore the session by providing the previously authenticated key */ static bunker(ndk, userOrConnectionToken, localSigner) { return new _NDKNip46Signer3(ndk, userOrConnectionToken, localSigner); } /** * Connect with a nostrconnect:// flow * @param ndk * @param relay - Relay used to connect with the signer * @param localSigner If you have previously authenticated with this signer, you can restore the session by providing the previously authenticated key */ static nostrconnect(ndk, relay, localSigner, nostrConnectOptions) { return new _NDKNip46Signer3(ndk, void 0, localSigner, [relay], nostrConnectOptions); } nostrconnectFlowInit(nostrConnectOptions) { this.nostrConnectSecret = nostrConnectGenerateSecret(); const pubkey = this.localSigner.pubkey; this.nostrConnectUri = generateNostrConnectUri( pubkey, this.nostrConnectSecret, this.relayUrls?.[0], nostrConnectOptions ); } bunkerFlowInit(connectionToken) { const bunkerUrl = new URL(connectionToken); const bunkerPubkey = bunkerUrl.hostname || bunkerUrl.pathname.replace(/^\/\//, ""); const userPubkey = bunkerUrl.searchParams.get("pubkey"); const relayUrls = bunkerUrl.searchParams.getAll("relay"); const secret = bunkerUrl.searchParams.get("secret"); this.bunkerPubkey = bunkerPubkey; this.userPubkey = userPubkey; this.relayUrls = relayUrls; this.secret = secret; } nip05Init(nip05) { this.nip05 = nip05; } /** * We start listening for events from the bunker */ async startListening() { if (this.subscription) return; const localUser = await this.localSigner.user(); if (!localUser) throw new Error("Local signer not ready"); this.subscription = await this.rpc.subscribe({ kinds: [24133 /* NostrConnect */], "#p": [localUser.pubkey] }); } /** * Get the user that is being published as */ async user() { if (this._user) return this._user; return this.blockUntilReady(); } get userSync() { if (!this._user) throw new Error("Remote user not ready synchronously"); return this._user; } async blockUntilReadyNostrConnect() { return new Promise((resolve, reject) => { const connect = (response) => { if (response.result === this.nostrConnectSecret) { this._user = response.event.author; this.userPubkey = response.event.pubkey; this.bunkerPubkey = response.event.pubkey; this.rpc.off("response", connect); resolve(this._user); } }; this.startListening(); this.rpc.on("response", connect); }); } async blockUntilReady() { if (!this.bunkerPubkey && !this.nostrConnectSecret && !this.nip05) { throw new Error("Bunker pubkey not set"); } if (this.nostrConnectSecret) return this.blockUntilReadyNostrConnect(); if (this.nip05 && !this.userPubkey) { const user = await NDKUser.fromNip05(this.nip05, this.ndk); if (user) { this._user = user; this.userPubkey = user.pubkey; this.relayUrls = user.nip46Urls; this.rpc = new NDKNostrRpc(this.ndk, this.localSigner, this.debug, this.relayUrls); } } if (!this.bunkerPubkey && this.userPubkey) { this.bunkerPubkey = this.userPubkey; } else if (!this.bunkerPubkey) { throw new Error("Bunker pubkey not set"); } await this.startListening(); this.rpc.on("authUrl", (...props) => { this.emit("authUrl", ...props); }); return new Promise((resolve, reject) => { const connectParams = [this.userPubkey ?? ""]; if (this.secret) connectParams.push(this.secret); if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest(this.bunkerPubkey, "connect", connectParams, 24133, (response) => { if (response.result === "ack") { this.getPublicKey().then((pubkey) => { this.userPubkey = pubkey; this._user = this.ndk.getUser({ pubkey }); resolve(this._user); }); } else { reject(response.error); } }); }); } stop() { this.subscription?.stop(); this.subscription = void 0; } async getPublicKey() { if (this.userPubkey) return this.userPubkey; return new Promise((resolve, _reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest(this.bunkerPubkey, "get_public_key", [], 24133, (response) => { resolve(response.result); }); }); } async encryptionEnabled(scheme) { if (scheme) return [scheme]; return Promise.resolve(["nip04", "nip44"]); } async encrypt(recipient, value, scheme = "nip04") { return this.encryption(recipient, value, scheme, "encrypt"); } async decrypt(sender, value, scheme = "nip04") { return this.encryption(sender, value, scheme, "decrypt"); } async encryption(peer, value, scheme, method) { const promise = new Promise((resolve, reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest( this.bunkerPubkey, `${scheme}_${method}`, [peer.pubkey, value], 24133, (response) => { if (!response.error) { resolve(response.result); } else { reject(response.error); } } ); }); return promise; } async sign(event) { const promise = new Promise((resolve, reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest( this.bunkerPubkey, "sign_event", [JSON.stringify(event)], 24133, (response) => { if (!response.error) { const json = JSON.parse(response.result); resolve(json.sig); } else { reject(response.error); } } ); }); return promise; } /** * Allows creating a new account on the remote server. * @param username Desired username for the NIP-05 * @param domain Desired domain for the NIP-05 * @param email Email address to associate with this account -- Remote servers may use this for recovery * @returns The public key of the newly created account */ async createAccount(username, domain, email) { await this.startListening(); const req = []; if (username) req.push(username); if (domain) req.push(domain); if (email) req.push(email); return new Promise((resolve, reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest( this.bunkerPubkey, "create_account", req, 24133 /* NostrConnect */, (response) => { if (!response.error) { const pubkey = response.result; resolve(pubkey); } else { reject(response.error); } } ); }); } /** * Serializes the signer's connection details and local signer state. * @returns A JSON string containing the type, connection info, and local signer payload. */ toPayload() { if (!this.bunkerPubkey || !this.userPubkey) { throw new Error("NIP-46 signer is not fully initialized for serialization"); } const payload = { type: "nip46", payload: { bunkerPubkey: this.bunkerPubkey, userPubkey: this.userPubkey, relayUrls: this.relayUrls, secret: this.secret, localSignerPayload: this.localSigner.toPayload(), // Store nip05 if it was used for initialization, otherwise null nip05: this.nip05 || null } }; return JSON.stringify(payload); } /** * Deserializes the signer from a payload string. * @param payloadString The JSON string obtained from toPayload(). * @param ndk The NDK instance, required for NIP-46. * @returns An instance of NDKNip46Signer. */ static async fromPayload(payloadString, ndk) { if (!ndk) { throw new Error("NDK instance is required to deserialize NIP-46 signer"); } const parsed = JSON.parse(payloadString); if (parsed.type !== "nip46") { throw new Error(`Invalid payload type: expected 'nip46', got ${parsed.type}`); } const payload = parsed.payload; if (!payload || typeof payload !== "object" || !payload.localSignerPayload) { throw new Error("Invalid payload content for nip46 signer"); } const localSigner = await ndkSignerFromPayload(payload.localSignerPayload, ndk); if (!localSigner) { throw new Error("Failed to deserialize local signer for NIP-46"); } if (!(localSigner instanceof NDKPrivateKeySigner)) { throw new Error("Local signer must be an instance of NDKPrivateKeySigner"); } let signer; signer = new _NDKNip46Signer3(ndk, false, localSigner, payload.relayUrls); signer.userPubkey = payload.userPubkey; signer.bunkerPubkey = payload.bunkerPubkey; signer.relayUrls = payload.relayUrls; signer.secret = payload.secret; if (payload.userPubkey) { signer._user = new NDKUser({ pubkey: payload.userPubkey }); if (signer._user) signer._user.ndk = ndk; } return signer; } }; registerSigner("nip46", NDKNip46Signer); // ndk/core/src/user/pin.ts async function pinEvent(user, event, pinEvent2, publish) { const kind = 10001 /* PinList */; if (!user.ndk) throw new Error("No NDK instance found"); user.ndk.assertSigner(); if (!pinEvent2) { const events = await user.ndk.fetchEvents( { kinds: [kind], authors: [user.pubkey] }, { cacheUsage: "ONLY_RELAY" /* ONLY_RELAY */ } ); if (events.size > 0) { pinEvent2 = lists_default.from(Array.from(events)[0]); } else { pinEvent2 = new NDKEvent(user.ndk, { kind }); } } pinEvent2.tag(event); if (publish) { await pinEvent2.publish(); } return pinEvent2; } // ndk/core/src/utils/filter.ts function matchFilter(filter, event) { if (filter.ids && filter.ids.indexOf(event.id) === -1) { return false; } if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) { return false; } if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) { return false; } for (const f in filter) { if (f[0] === "#") { const tagName = f.slice(1); if (tagName === "t") { const values = filter[`#${tagName}`]?.map((v6) => v6.toLowerCase()); if (values && !event.tags.find(([t, v6]) => t === tagName && values?.indexOf(v6.toLowerCase()) !== -1)) return false; } else { const values = filter[`#${tagName}`]; if (values && !event.tags.find(([t, v6]) => t === tagName && values?.indexOf(v6) !== -1)) return false; } } } if (filter.since && event.created_at < filter.since) return false; if (filter.until && event.created_at > filter.until) return false; return true; } // ndk/core/src/zapper/index.ts var import_debug12 = __toESM(require_browser()); var import_tseep9 = __toESM(require_lib()); // ndk/core/src/zapper/ln.ts init_esm(); var import_debug11 = __toESM(require_browser()); var d2 = (0, import_debug11.default)("ndk:zapper:ln"); async function getNip57ZapSpecFromLud({ lud06, lud16 }, ndk) { let zapEndpoint; if (lud16 && !lud16.startsWith("LNURL")) { const [name, domain] = lud16.split("@"); zapEndpoint = `https://${domain}/.well-known/lnurlp/${name}`; } else if (lud06) { const { words } = bech32.decode(lud06, 1e3); const data = bech32.fromWords(words); const utf8Decoder3 = new TextDecoder("utf-8"); zapEndpoint = utf8Decoder3.decode(data); } if (!zapEndpoint) { d2("No zap endpoint found %o", { lud06, lud16 }); throw new Error("No zap endpoint found"); } try { const _fetch5 = ndk.httpFetch || fetch; const response = await _fetch5(zapEndpoint); if (response.status !== 200) { const text = await response.text(); throw new Error(`Unable to fetch zap endpoint ${zapEndpoint}: ${text}`); } return await response.json(); } catch (e2) { throw new Error(`Unable to fetch zap endpoint ${zapEndpoint}: ${e2}`); } } // ndk/core/src/zapper/nip57.ts async function generateZapRequest(target, ndk, data, pubkey, amount, relays, comment, tags, signer) { const zapEndpoint = data.callback; const event = new NDKEvent(ndk); event.kind = 9734; event.content = comment || ""; event.tags = [ ["relays", ...relays.slice(0, 4)], ["amount", amount.toString()], ["lnurl", zapEndpoint], ["p", pubkey] ]; if (target instanceof NDKEvent) { const referenceTags = target.referenceTags(); const nonPTags = referenceTags.filter((tag) => tag[0] !== "p"); event.tags.push(...nonPTags); if (target.kind !== void 0) { event.tags.push(["k", target.kind.toString()]); } } if (tags) { event.tags = event.tags.concat(tags); } const eTaggedEvents = /* @__PURE__ */ new Set(); const aTaggedEvents = /* @__PURE__ */ new Set(); for (const tag of event.tags) { if (tag[0] === "e") { eTaggedEvents.add(tag[1]); } else if (tag[0] === "a") { aTaggedEvents.add(tag[1]); } } if (eTaggedEvents.size > 1) throw new Error("Only one e-tag is allowed"); if (aTaggedEvents.size > 1) throw new Error("Only one a-tag is allowed"); event.tags = event.tags.filter((tag) => tag[0] !== "p"); event.tags.push(["p", pubkey]); await event.sign(signer); return event; } // ndk/core/src/zapper/index.ts var d3 = (0, import_debug12.default)("ndk:zapper"); var NDKZapper = class extends import_tseep9.EventEmitter { /** * * @param target The target of the zap * @param amount The amount to send indicated in the unit * @param unit The unit of the amount * @param opts Options for the zap */ constructor(target, amount, unit = "msat", opts = {}) { super(); __publicField(this, "target"); __publicField(this, "ndk"); __publicField(this, "comment"); __publicField(this, "amount"); __publicField(this, "unit"); __publicField(this, "tags"); __publicField(this, "signer"); __publicField(this, "zapMethod"); __publicField(this, "nutzapAsFallback"); __publicField(this, "lnPay"); /** * Called when a cashu payment is to be made. * This function should swap/mint proofs for the required amount, in the required unit, * in any of the provided mints and return the proofs and mint used. */ __publicField(this, "cashuPay"); __publicField(this, "onComplete"); __publicField(this, "maxRelays", 3); this.target = target; this.ndk = opts.ndk || target.ndk; if (!this.ndk) { throw new Error("No NDK instance provided"); } this.amount = amount; this.comment = opts.comment; this.unit = unit; this.tags = opts.tags; this.signer = opts.signer; this.nutzapAsFallback = opts.nutzapAsFallback ?? false; this.lnPay = opts.lnPay || this.ndk.walletConfig?.lnPay; this.cashuPay = opts.cashuPay || this.ndk.walletConfig?.cashuPay; this.onComplete = opts.onComplete || this.ndk.walletConfig?.onPaymentComplete; } /** * Initiate zapping process * * This function will calculate the splits for this zap and initiate each zap split. */ async zap(methods) { d3("Starting zap process", { target: this.target, amount: this.amount, unit: this.unit, methods, nutzapAsFallback: this.nutzapAsFallback }); const splits = this.getZapSplits(); d3("Calculated zap splits", splits); const results = /* @__PURE__ */ new Map(); await Promise.all( splits.map(async (split2) => { let result; d3("Processing split", split2); try { result = await this.zapSplit(split2, methods); d3("Split completed successfully", { split: split2, result }); } catch (e2) { d3("Split failed", { split: split2, error: e2.message }); result = new Error(e2.message); } this.emit("split:complete", split2, result); results.set(split2, result); }) ); d3("All splits completed", results); const allFailed = Array.from(results.values()).every( (result) => result === void 0 || result instanceof Error ); const anyFailed = Array.from(results.values()).some((result) => result instanceof Error); this.emit("complete", results); if (this.onComplete) this.onComplete(results); if (allFailed) { const errors = Array.from(results.values()).filter((r) => r instanceof Error).map((e2) => e2.message).join(", "); const errorMessage = errors || "All zap attempts failed"; d3("All splits failed", errorMessage); throw new Error(errorMessage); } if (anyFailed) { d3("Some splits failed, but at least one succeeded"); } return results; } async zapNip57(split2, data) { if (!this.lnPay) throw new Error("No lnPay function available"); const zapSpec = await getNip57ZapSpecFromLud(data, this.ndk); if (!zapSpec) throw new Error("No zap spec available for recipient"); const relays = await this.relays(split2.pubkey); const zapRequest = await generateZapRequest( this.target, this.ndk, zapSpec, split2.pubkey, split2.amount, relays, this.comment, this.tags, this.signer ); if (!zapRequest) { d3("Unable to generate zap request"); throw new Error("Unable to generate zap request"); } const pr2 = await this.getLnInvoice(zapRequest, split2.amount, zapSpec); if (!pr2) { d3("Unable to get payment request"); throw new Error("Unable to get payment request"); } this.emit("ln_invoice", { amount: split2.amount, recipientPubkey: split2.pubkey, unit: this.unit, nip57ZapRequest: zapRequest, pr: pr2, type: "nip57" }); const res = await this.lnPay({ target: this.target, recipientPubkey: split2.pubkey, paymentDescription: "NIP-57 Zap", pr: pr2, amount: split2.amount, unit: this.unit, nip57ZapRequest: zapRequest }); if (res?.preimage) { this.emit("ln_payment", { preimage: res.preimage, amount: split2.amount, recipientPubkey: split2.pubkey, pr: pr2, unit: this.unit, nip57ZapRequest: zapRequest, type: "nip57" }); } return res; } /** * Fetches information about a NIP-61 zap and asks the caller to create cashu proofs for the zap. * * (note that the cashuPay function can use any method to create the proofs, including using lightning * to mint proofs in the specified mint, the responsibility of minting the proofs is delegated to the caller (e.g. ndk-wallet)) */ async zapNip61(split2, data) { d3("Starting NIP-61 zap", { split: split2, data }); if (!this.cashuPay) { d3("No cashuPay function available"); throw new Error("No cashuPay function available"); } const proofTags = []; if (this.target instanceof NDKEvent) { proofTags.push(["e", this.target.id]); } const signer = this.signer || this.ndk.signer; if (signer) { const user = await signer.user(); proofTags.push(["P", user.pubkey]); } d3("Calling cashuPay function", { target: this.target, recipientPubkey: split2.pubkey, amount: split2.amount, unit: this.unit, proofTags, data }); let ret; ret = await this.cashuPay( { target: this.target, recipientPubkey: split2.pubkey, paymentDescription: "NIP-61 Zap", amount: split2.amount, unit: this.unit, proofTags, ...data ?? {} }, (pr2) => { d3("LN invoice generated for NIP-61", pr2); this.emit("ln_invoice", { pr: pr2, amount: split2.amount, recipientPubkey: split2.pubkey, unit: this.unit, type: "nip61" }); } ); d3("NIP-61 Zap result: %o", ret); if (ret instanceof Error) { d3("cashuPay returned error", ret); return ret; } if (ret) { const { proofs, mint } = ret; if (!proofs || !mint) { d3("Invalid zap confirmation: missing proofs or mint", ret); throw new Error(`Invalid zap confirmation: missing proofs or mint: ${ret}`); } d3("Creating nutzap event", { proofsCount: proofs.length, mint }); const relays = await this.relays(split2.pubkey); d3("Publishing to relays", relays); const relaySet = NDKRelaySet.fromRelayUrls(relays, this.ndk); const nutzap = new NDKNutzap(this.ndk); nutzap.tags = [...nutzap.tags, ...this.tags || []]; nutzap.proofs = proofs; nutzap.mint = mint; nutzap.target = this.target; nutzap.comment = this.comment; nutzap.unit = "sat"; nutzap.recipientPubkey = split2.pubkey; await nutzap.sign(this.signer); d3("Nutzap signed, publishing", nutzap.id); nutzap.publish(relaySet); return nutzap; } d3("cashuPay returned undefined"); } /** * Get the zap methods available for the recipient and initiates the zap * in the desired method. * @param split * @param methods - The methods to try, if not provided, all methods will be tried. * @returns */ async zapSplit(split2, methods) { d3("Starting zapSplit", { split: split2, methods }); const recipient = this.ndk.getUser({ pubkey: split2.pubkey }); d3("Fetching zap info for recipient", recipient.pubkey); const zapMethods = await recipient.getZapInfo(2500); d3("Recipient zap methods", { methods: Array.from(zapMethods.keys()), nip61Data: zapMethods.get("nip61"), nip57Data: zapMethods.get("nip57") }); let retVal; const canFallbackToNip61 = this.nutzapAsFallback && this.cashuPay; d3("Fallback configuration", { canFallbackToNip61, nutzapAsFallback: this.nutzapAsFallback, hasCashuPay: !!this.cashuPay }); if (zapMethods.size === 0 && !canFallbackToNip61) { d3("No zap methods available and fallback disabled"); throw new Error("No zap method available for recipient and NIP-61 fallback is disabled"); } const nip61Fallback = async () => { d3("Executing NIP-61 fallback"); if (!this.nutzapAsFallback) return; const relayLists = await getRelayListForUsers([split2.pubkey], this.ndk); let relayUrls = relayLists.get(split2.pubkey)?.readRelayUrls; relayUrls = this.ndk.pool.connectedRelays().map((r) => r.url); d3("NIP-61 fallback relay URLs", relayUrls); return await this.zapNip61(split2, { // use the user's relay list relays: relayUrls, // lock to the user's actual pubkey p2pk: split2.pubkey, // allow intramint fallback allowIntramintFallback: !!canFallbackToNip61 }); }; const canUseNip61 = !methods || methods.includes("nip61"); const canUseNip57 = !methods || methods.includes("nip57"); d3("Method filters", { canUseNip61, canUseNip57 }); const nip61Method = zapMethods.get("nip61"); if (nip61Method && canUseNip61) { d3("Attempting NIP-61 zap", nip61Method); try { retVal = await this.zapNip61(split2, nip61Method); if (retVal instanceof NDKNutzap) { d3("NIP-61 zap succeeded", retVal); return retVal; } } catch (e2) { d3("NIP-61 attempt failed", e2); this.emit("notice", `NIP-61 attempt failed: ${e2.message}`); } } const nip57Method = zapMethods.get("nip57"); if (nip57Method && canUseNip57) { d3("Attempting NIP-57 zap", nip57Method); try { retVal = await this.zapNip57(split2, nip57Method); if (!(retVal instanceof Error)) { d3("NIP-57 zap succeeded", retVal); return retVal; } } catch (e2) { d3("NIP-57 attempt failed", e2); this.emit("notice", `NIP-57 attempt failed: ${e2.message}`); } } if (canFallbackToNip61) { d3("Attempting NIP-61 fallback"); retVal = await nip61Fallback(); if (retVal instanceof Error) { d3("NIP-61 fallback failed", retVal); throw retVal; } d3("NIP-61 fallback succeeded", retVal); return retVal; } d3("All zap methods exhausted"); this.emit("notice", "Zap methods exhausted and there was no fallback to NIP-61"); if (retVal instanceof Error) throw retVal; return retVal; } /** * Gets a bolt11 for a nip57 zap * @param event * @param amount * @param zapEndpoint * @returns */ async getLnInvoice(zapRequest, amount, data) { const zapEndpoint = data.callback; const eventPayload = JSON.stringify(zapRequest.rawEvent()); d3( `Fetching invoice from ${zapEndpoint}?${new URLSearchParams({ amount: amount.toString(), nostr: eventPayload })}` ); const url = new URL(zapEndpoint); url.searchParams.append("amount", amount.toString()); url.searchParams.append("nostr", eventPayload); d3(`Fetching invoice from ${url.toString()}`); const response = await fetch(url.toString()); d3(`Got response from zap endpoint: ${zapEndpoint}`, { status: response.status }); if (response.status !== 200) { d3(`Received non-200 status from zap endpoint: ${zapEndpoint}`, { status: response.status, amount, nostr: eventPayload }); const text = await response.text(); throw new Error(`Unable to fetch zap endpoint ${zapEndpoint}: ${text}`); } const body = await response.json(); return body.pr; } getZapSplits() { if (this.target instanceof NDKUser) { return [ { pubkey: this.target.pubkey, amount: this.amount } ]; } const zapTags = this.target.getMatchingTags("zap"); if (zapTags.length === 0) { return [ { pubkey: this.target.pubkey, amount: this.amount } ]; } const splits = []; const total = zapTags.reduce((acc, tag) => acc + Number.parseInt(tag[2]), 0); for (const tag of zapTags) { const pubkey = tag[1]; const amount = Math.floor(Number.parseInt(tag[2]) / total * this.amount); splits.push({ pubkey, amount }); } return splits; } /** * Get the zap methods available for all recipients (all splits) * Returns a map of pubkey -> zap methods for that recipient * * @example * ```ts * const zapper = new NDKZapper(event, 1000, "msat"); * const methods = await zapper.getRecipientZapMethods(); * for (const [pubkey, zapMethods] of methods) { * console.log(`${pubkey} accepts:`, Array.from(zapMethods.keys())); * } * ``` */ async getRecipientZapMethods(timeout = 2500) { const splits = this.getZapSplits(); const results = /* @__PURE__ */ new Map(); await Promise.all( splits.map(async (split2) => { const user = this.ndk.getUser({ pubkey: split2.pubkey }); const zapMethods = await user.getZapInfo(timeout); results.set(split2.pubkey, zapMethods); }) ); return results; } /** * Gets the zap method that should be used to zap a pubbkey * @param ndk * @param pubkey * @returns */ async getZapMethods(ndk, recipient, timeout = 2500) { const user = ndk.getUser({ pubkey: recipient }); return await user.getZapInfo(timeout); } /** * @returns the relays to use for the zap request */ async relays(pubkey) { let r = []; if (this.ndk?.activeUser) { const relayLists = await getRelayListForUsers([this.ndk.activeUser.pubkey, pubkey], this.ndk); const relayScores = /* @__PURE__ */ new Map(); for (const relayList of relayLists.values()) { for (const url of relayList.readRelayUrls) { const score = relayScores.get(url) || 0; relayScores.set(url, score + 1); } } r = Array.from(relayScores.entries()).sort((a, b) => b[1] - a[1]).map(([url]) => url).slice(0, this.maxRelays); } if (this.ndk?.pool?.permanentAndConnectedRelays().length) { r = this.ndk.pool.permanentAndConnectedRelays().map((relay) => relay.url); } if (!r.length) { r = []; } return r; } }; // ndk/cache-dexie/dist/index.mjs init_dist(); var import_debug25 = __toESM(require_browser(), 1); var import_nostr_tools21 = __toESM(require_nostr_tools(), 1); var import_debug26 = __toESM(require_browser(), 1); // ndk/node_modules/dexie/import-wrapper.mjs var import_dexie = __toESM(require_dexie(), 1); var DexieSymbol = /* @__PURE__ */ Symbol.for("Dexie"); var Dexie = globalThis[DexieSymbol] || (globalThis[DexieSymbol] = import_dexie.default); if (import_dexie.default.semVer !== Dexie.semVer) { throw new Error(`Two different versions of Dexie loaded in the same app: ${import_dexie.default.semVer} and ${Dexie.semVer}`); } var { liveQuery, mergeRanges, rangesOverlap, RangeSet, cmp, Entity, PropModification, replacePrefix, add: add2, remove, DexieYProvider } = Dexie; var import_wrapper_default = Dexie; // ndk/cache-dexie/dist/index.mjs var import_debug27 = __toESM(require_browser(), 1); init_dist(); var import_typescript_lru_cache7 = __toESM(require_dist(), 1); var debug10 = (0, import_debug26.default)("ndk:dexie-adapter:modules"); var DexieModuleCollection = class { constructor(db22, tableName) { this.db = db22; this.tableName = tableName; } get table() { return this.db[this.tableName]; } async get(id) { const result = await this.table.get(id); return result || null; } async getMany(ids) { const results = await this.table.where(":id").anyOf(ids).toArray(); return results; } async save(item) { await this.table.put(item); } async saveMany(items) { await this.table.bulkPut(items); } async delete(id) { await this.table.delete(id); } async deleteMany(ids) { await this.table.where(":id").anyOf(ids).delete(); } async findBy(field, value) { return await this.table.where(field).equals(value).toArray(); } async where(conditions) { let collection2 = this.table.toCollection(); for (const [field, value] of Object.entries(conditions)) { collection2 = collection2.and((item) => item[field] === value); } return await collection2.toArray(); } async all() { return await this.table.toArray(); } async count(conditions) { if (!conditions) { return await this.table.count(); } let collection2 = this.table.toCollection(); for (const [field, value] of Object.entries(conditions)) { collection2 = collection2.and((item) => item[field] === value); } return await collection2.count(); } async clear() { await this.table.clear(); } }; var DexieCacheModuleManager = class { constructor(dbName) { __publicField(this, "modules", /* @__PURE__ */ new Map()); __publicField(this, "moduleDb"); __publicField(this, "initialized", false); this.dbName = dbName; this.moduleDb = new import_wrapper_default(`${dbName}_modules`); this.setupDatabase(); } setupDatabase() { this.moduleDb.version(1).stores({ moduleMetadata: "&namespace" }); } /** * Register a cache module */ async registerModule(module2) { if (!this.moduleDb.isOpen()) { await this.moduleDb.open(); } const metadataTable = this.moduleDb.table("moduleMetadata"); const existingMetadata = await metadataTable.get(module2.namespace); const currentVersion = existingMetadata?.version || 0; if (currentVersion >= module2.version) { debug10(`Module ${module2.namespace} is already at version ${currentVersion}`); return; } const currentDbVersion = this.moduleDb.verno; const newDbVersion = currentDbVersion + 1; this.moduleDb.close(); const stores = { moduleMetadata: "&namespace" }; for (const [collName, collDef] of Object.entries(module2.collections)) { const tableName = `${module2.namespace}_${collName}`; let indexString = `&${collDef.primaryKey}`; if (collDef.indexes) { indexString += `, ${collDef.indexes.join(", ")}`; } if (collDef.compoundIndexes) { const compounds = collDef.compoundIndexes.map((fields) => `[${fields.join("+")}]`); indexString += `, ${compounds.join(", ")}`; } stores[tableName] = indexString; } this.moduleDb.version(newDbVersion).stores(stores); await this.moduleDb.open(); for (let version = currentVersion + 1; version <= module2.version; version++) { if (module2.migrations[version]) { debug10(`Running migration ${version} for module ${module2.namespace}`); const context = { fromVersion: currentVersion, toVersion: version, async getCollection(name) { return new DexieModuleCollection(this.moduleDb, `${module2.namespace}_${name}`); }, async createCollection(name, definition) { debug10(`Collection ${name} created during schema update`); }, async deleteCollection(name) { debug10(`Collection deletion requires database recreation`); }, async addIndex(collection2, field) { debug10(`Index addition requires database recreation`); } }; await module2.migrations[version](context); } } await metadataTable.put({ namespace: module2.namespace, version: module2.version, lastMigration: Date.now(), collections: Object.keys(module2.collections) }); this.modules.set(module2.namespace, module2); debug10(`Module ${module2.namespace} registered at version ${module2.version}`); } /** * Get a collection from a module */ async getModuleCollection(namespace, collection2) { if (!this.moduleDb.isOpen()) { await this.moduleDb.open(); } const tableName = `${namespace}_${collection2}`; const table = this.moduleDb[tableName]; if (!table) { const metadata = await this.moduleDb.table("moduleMetadata").get(namespace); if (!metadata) { throw new Error(`Module ${namespace} not registered`); } throw new Error(`Collection ${collection2} not found in module ${namespace}`); } return new DexieModuleCollection(this.moduleDb, tableName); } /** * Check if a module is registered */ hasModule(namespace) { return this.modules.has(namespace); } /** * Get the current version of a module */ async getModuleVersion(namespace) { if (!this.moduleDb.isOpen()) { await this.moduleDb.open(); } const metadata = await this.moduleDb.table("moduleMetadata").get(namespace); return metadata?.version || 0; } }; async function eventTagsWarmUp(cacheHandler, eventTags) { const array = await eventTags.limit(cacheHandler.maxSize).toArray(); for (const event of array) { cacheHandler.add(event.tagValue, event.eventId, false); } } var eventTagsDump = (eventTags, debug23) => { return async (dirtyKeys, cache) => { const entries = []; for (const tagValue of dirtyKeys) { const eventIds = cache.get(tagValue); if (eventIds) { for (const eventId of eventIds) entries.push({ tagValue, eventId }); } } if (entries.length > 0) { debug23(`Saving ${entries.length} events cache entries to database`); await eventTags.bulkPut(entries); } dirtyKeys.clear(); }; }; async function eventsWarmUp(cacheHandler, events) { const array = await events.limit(cacheHandler.maxSize).toArray(); for (const event of array) { cacheHandler.set(event.id, event, false); } } var eventsDump = (events, debug23) => { return async (dirtyKeys, cache) => { const entries = []; for (const event of dirtyKeys) { const entry = cache.get(event); if (entry) entries.push(entry); } if (entries.length > 0) { debug23(`Saving ${entries.length} events cache entries to database`); await events.bulkPut(entries); } dirtyKeys.clear(); }; }; async function nip05WarmUp(cacheHandler, nip05s) { const array = await nip05s.limit(cacheHandler.maxSize).toArray(); for (const nip05 of array) { cacheHandler.set(nip05.nip05, nip05, false); } } var nip05Dump = (nip05s, debug23) => { return async (dirtyKeys, cache) => { const entries = []; for (const nip05 of dirtyKeys) { const entry = cache.get(nip05); if (entry) { entries.push({ nip05, ...entry }); } } if (entries.length) { debug23(`Saving ${entries.length} NIP-05 cache entries to database`); await nip05s.bulkPut(entries); } dirtyKeys.clear(); }; }; var Database = class extends import_wrapper_default { constructor(name) { super(name); __publicField(this, "profiles"); __publicField(this, "events"); __publicField(this, "eventTags"); __publicField(this, "nip05"); __publicField(this, "lnurl"); __publicField(this, "relayStatus"); __publicField(this, "unpublishedEvents"); __publicField(this, "eventRelays"); __publicField(this, "decryptedEvents"); this.version(18).stores({ profiles: "&pubkey", events: "&id, kind", eventTags: "&tagValue", nip05: "&nip05", lnurl: "&pubkey", relayStatus: "&url", unpublishedEvents: "&id", eventRelays: "[eventId+relayUrl], eventId", decryptedEvents: "&id" }); } }; var db; function createDatabase(name) { db = new Database(name); } var d5 = (0, import_debug27.default)("ndk:dexie-adapter:profiles"); async function profilesWarmUp(cacheHandler, profiles) { const array = await profiles.limit(cacheHandler.maxSize).toArray(); for (const user of array) { const obj = user; cacheHandler.set(user.pubkey, obj, false); } d5("Loaded %d profiles from database", cacheHandler.size()); } var profilesDump = (profiles, debug23) => { return async (dirtyKeys, cache) => { const entries = []; for (const pubkey of dirtyKeys) { const entry = cache.get(pubkey); if (entry) { entries.push(entry); } } if (entries.length) { debug23(`Saving ${entries.length} users to database`); await profiles.bulkPut(entries); } dirtyKeys.clear(); }; }; async function relayInfoWarmUp(cacheHandler, relayStatus) { const array = await relayStatus.limit(cacheHandler.maxSize).toArray(); for (const entry of array) { cacheHandler.set( entry.url, { url: entry.url, updatedAt: entry.updatedAt, lastConnectedAt: entry.lastConnectedAt, dontConnectBefore: entry.dontConnectBefore }, false ); } } var relayInfoDump = (relayStatus, debug23) => { return async (dirtyKeys, cache) => { const entries = []; for (const url of dirtyKeys) { const info = cache.get(url); if (info) { entries.push({ url, updatedAt: info.updatedAt, lastConnectedAt: info.lastConnectedAt, dontConnectBefore: info.dontConnectBefore }); } } if (entries.length > 0) { debug23(`Saving ${entries.length} relay status cache entries to database`); await relayStatus.bulkPut(entries); } dirtyKeys.clear(); }; }; var WRITE_STATUS_THRESHOLD = 3; async function unpublishedEventsWarmUp(cacheHandler, unpublishedEvents) { await unpublishedEvents.each((unpublishedEvent) => { cacheHandler.set(unpublishedEvent.event.id, unpublishedEvent, false); }); } function unpublishedEventsDump(unpublishedEvents, debug23) { return async (dirtyKeys, cache) => { const entries = []; for (const eventId of dirtyKeys) { const entry = cache.get(eventId); if (entry) { entries.push(entry); } } if (entries.length > 0) { debug23(`Saving ${entries.length} unpublished events cache entries to database`); await unpublishedEvents.bulkPut(entries); } dirtyKeys.clear(); }; } async function discardUnpublishedEvent(unpublishedEvents, eventId) { await unpublishedEvents.delete(eventId); } async function getUnpublishedEvents(unpublishedEvents) { const events = []; await unpublishedEvents.each((unpublishedEvent) => { events.push({ event: new NDKEvent2(void 0, unpublishedEvent.event), relays: Object.keys(unpublishedEvent.relays), lastTryAt: unpublishedEvent.lastTryAt }); }); return events; } function addUnpublishedEvent(event, relays) { const r = {}; relays.forEach((url) => r[url] = false); this.unpublishedEvents.set(event.id, { id: event.id, event: event.rawEvent(), relays: r }); this.setEvent(event, [], void 0).catch((e2) => { console.error("[addUnpublishedEvent] Failed to store event in main table:", e2); }); const onPublished = (relay) => { const url = relay.url; const existingEntry = this.unpublishedEvents.get(event.id); if (!existingEntry) { event.off("publushed", onPublished); return; } existingEntry.relays[url] = true; this.unpublishedEvents.set(event.id, existingEntry); const successWrites = Object.values(existingEntry.relays).filter((v6) => v6).length; const unsuccessWrites = Object.values(existingEntry.relays).length - successWrites; if (successWrites >= WRITE_STATUS_THRESHOLD || unsuccessWrites === 0) { this.unpublishedEvents.delete(event.id); event.off("published", onPublished); } }; event.on("published", onPublished); } async function zapperWarmUp(cacheHandler, lnurls) { const array = await lnurls.limit(cacheHandler.maxSize).toArray(); for (const lnurl of array) { cacheHandler.set(lnurl.pubkey, { document: lnurl.document, fetchedAt: lnurl.fetchedAt }, false); } } var zapperDump = (lnurls, debug23) => { return async (dirtyKeys, cache) => { const entries = []; for (const pubkey of dirtyKeys) { const entry = cache.get(pubkey); if (entry) { entries.push({ pubkey, ...entry }); } } if (entries.length) { debug23(`Saving ${entries.length} zapper cache entries to database`); await lnurls.bulkPut(entries); } dirtyKeys.clear(); }; }; var CacheHandler = class { constructor(options) { __publicField(this, "cache"); __publicField(this, "dirtyKeys", /* @__PURE__ */ new Set()); __publicField(this, "options"); __publicField(this, "debug"); __publicField(this, "indexes"); __publicField(this, "isSet", false); __publicField(this, "maxSize", 0); this.debug = options.debug; this.options = options; this.maxSize = options.maxSize; if (options.maxSize > 0) { this.cache = new import_typescript_lru_cache7.LRUCache({ maxSize: options.maxSize }); setInterval(() => this.dump().catch(console.error), 1e3 * 10); } this.indexes = /* @__PURE__ */ new Map(); } getSet(key) { return this.cache?.get(key); } /** * Get all entries that match the filter. */ getAllWithFilter(filter) { const ret = /* @__PURE__ */ new Map(); this.cache?.forEach((val, key) => { if (filter(key, val)) { ret.set(key, val); } }); return ret; } get(key) { return this.cache?.get(key); } async getWithFallback(key, table) { let entry = this.get(key); if (!entry) { entry = await table.get(key); if (entry) { this.set(key, entry); } } return entry; } async getManyWithFallback(keys, table) { const entries = []; const missingKeys = []; for (const key of keys) { const entry = this.get(key); if (entry) entries.push(entry); else missingKeys.push(key); } if (entries.length > 0) { this.debug(`Cache hit for keys ${entries.length} and miss for ${missingKeys.length} keys`); } if (missingKeys.length > 0) { const startTime = Date.now(); const missingEntries = await table.bulkGet(missingKeys); const endTime = Date.now(); let foundKeys = 0; for (const entry of missingEntries) { if (entry) { this.set(entry.id, entry); entries.push(entry); foundKeys++; } } this.debug( `Time spent querying database: ${endTime - startTime}ms for ${missingKeys.length} keys, which added ${foundKeys} entries to the cache` ); } return entries; } add(key, value, dirty = true) { const existing = this.get(key) ?? /* @__PURE__ */ new Set(); existing.add(value); this.cache?.set(key, existing); if (dirty) this.dirtyKeys.add(key); } set(key, value, dirty = true) { this.cache?.set(key, value); if (dirty) this.dirtyKeys.add(key); for (const [attribute, index] of this.indexes.entries()) { const indexKey = value[attribute]; if (indexKey) { const indexValue = index.get(indexKey) || /* @__PURE__ */ new Set(); indexValue.add(key); index.set(indexKey, indexValue); } } } size() { return this.cache?.size || 0; } delete(key) { this.cache?.delete(key); this.dirtyKeys.add(key); } async dump() { if (this.dirtyKeys.size > 0 && this.cache) { await this.options.dump(this.dirtyKeys, this.cache); this.dirtyKeys.clear(); } } addIndex(attribute) { this.indexes.set(attribute, new import_typescript_lru_cache7.LRUCache({ maxSize: this.options.maxSize })); } getFromIndex(index, key) { const ret = /* @__PURE__ */ new Set(); const indexValues = this.indexes.get(index); if (indexValues) { const values = indexValues.get(key); if (values) { for (const key2 of values.values()) { const entry = this.get(key2); if (entry) ret.add(entry); } } } return ret; } }; var INDEXABLE_TAGS_LIMIT = 10; var NDKCacheAdapterDexie = class { constructor(opts = {}) { __publicField(this, "debug"); __publicField(this, "locking", false); __publicField(this, "ready", false); __publicField(this, "profiles"); __publicField(this, "zappers"); __publicField(this, "nip05s"); __publicField(this, "events"); __publicField(this, "eventTags"); __publicField(this, "relayInfo"); __publicField(this, "unpublishedEvents"); __publicField(this, "warmedUp", false); __publicField(this, "warmUpPromise"); __publicField(this, "devMode", false); __publicField(this, "saveSig"); __publicField(this, "_onReady"); __publicField(this, "moduleManager"); __publicField(this, "addUnpublishedEvent", addUnpublishedEvent.bind(this)); __publicField(this, "getUnpublishedEvents", () => getUnpublishedEvents(db.unpublishedEvents)); __publicField(this, "discardUnpublishedEvent", (id) => discardUnpublishedEvent(db.unpublishedEvents, id)); const dbName = opts.dbName || "ndk"; createDatabase(dbName); this.debug = opts.debug || (0, import_debug25.default)("ndk:dexie-adapter"); this.saveSig = opts.saveSig || false; this.moduleManager = new DexieCacheModuleManager(dbName); this.profiles = new CacheHandler({ maxSize: opts.profileCacheSize || 1e5, dump: profilesDump(db.profiles, this.debug), debug: this.debug }); this.zappers = new CacheHandler({ maxSize: opts.zapperCacheSize || 200, dump: zapperDump(db.lnurl, this.debug), debug: this.debug }); this.nip05s = new CacheHandler({ maxSize: opts.nip05CacheSize || 1e3, dump: nip05Dump(db.nip05, this.debug), debug: this.debug }); this.events = new CacheHandler({ maxSize: opts.eventCacheSize || 5e4, dump: eventsDump(db.events, this.debug), debug: this.debug }); this.events.addIndex("pubkey"); this.events.addIndex("kind"); this.eventTags = new CacheHandler({ maxSize: opts.eventTagsCacheSize || 1e5, dump: eventTagsDump(db.eventTags, this.debug), debug: this.debug }); this.relayInfo = new CacheHandler({ maxSize: 500, debug: this.debug, dump: relayInfoDump(db.relayStatus, this.debug) }); this.unpublishedEvents = new CacheHandler({ maxSize: 5e3, debug: this.debug, dump: unpublishedEventsDump(db.unpublishedEvents, this.debug) }); const profile = (label, fn) => { const start = Date.now(); return fn().then(() => { const end = Date.now(); this.debug(label, "took", end - start, "ms"); }); }; const startTime = Date.now(); this.warmUpPromise = Promise.allSettled([ profile("profilesWarmUp", () => profilesWarmUp(this.profiles, db.profiles)), profile("zapperWarmUp", () => zapperWarmUp(this.zappers, db.lnurl)), profile("nip05WarmUp", () => nip05WarmUp(this.nip05s, db.nip05)), profile("relayInfoWarmUp", () => relayInfoWarmUp(this.relayInfo, db.relayStatus)), profile( "unpublishedEventsWarmUp", () => unpublishedEventsWarmUp(this.unpublishedEvents, db.unpublishedEvents) ), profile("eventsWarmUp", () => eventsWarmUp(this.events, db.events)), profile("eventTagsWarmUp", () => eventTagsWarmUp(this.eventTags, db.eventTags)) ]); this.warmUpPromise.then(() => { const endTime = Date.now(); this.warmedUp = true; this.ready = true; this.locking = true; this.debug("Warm up completed, time", endTime - startTime, "ms"); if (this._onReady) this._onReady(); }); } onReady(callback) { this._onReady = callback; } async query(subscription) { if (!this.warmedUp) { const startTime2 = Date.now(); await this.warmUpPromise; this.debug("froze query for", Date.now() - startTime2, "ms", subscription.filters); } const startTime = Date.now(); subscription.filters.map((filter) => this.processFilter(filter, subscription)); const dur = Date.now() - startTime; if (dur > 100) this.debug("query took", dur, "ms", subscription.filter); return []; } async fetchProfile(pubkey) { if (!this.profiles) return null; const user = await this.profiles.getWithFallback(pubkey, db.profiles); return user; } fetchProfileSync(pubkey) { if (!this.profiles) return null; const user = this.profiles.get(pubkey); return user; } async getProfiles(filter) { if (!this.profiles) return; const filterFn = typeof filter === "function" ? filter : (pubkey, profile) => { const searchLower = filter.contains.toLowerCase(); const fields = filter.fields || (filter.field ? [filter.field] : ["name", "displayName", "nip05"]); return fields.some((field) => { const value = profile[field]; return typeof value === "string" && value.toLowerCase().includes(searchLower); }); }; return this.profiles.getAllWithFilter(filterFn); } saveProfile(pubkey, profile) { const existingValue = this.profiles.get(pubkey); if (existingValue?.created_at && profile.created_at && existingValue.created_at >= profile.created_at) { return; } const cachedAt = Math.floor(Date.now() / 1e3); this.profiles.set(pubkey, { pubkey, ...profile, cachedAt }); this.debug("Saved profile for pubkey", pubkey, profile); } async loadNip05(nip05, maxAgeForMissing = 3600) { const cache = this.nip05s?.get(nip05); if (cache) { if (cache.profile === null) { if (cache.fetchedAt + maxAgeForMissing * 1e3 < Date.now()) return "missing"; return null; } try { return JSON.parse(cache.profile); } catch (_e2) { return "missing"; } } const nip = await db.nip05.get({ nip05 }); if (!nip) return "missing"; const now2 = Date.now(); if (nip.profile === null) { if (nip.fetchedAt + maxAgeForMissing * 1e3 < now2) return "missing"; return null; } try { return JSON.parse(nip.profile); } catch (_e2) { return "missing"; } } async saveNip05(nip05, profile) { try { const document2 = profile ? JSON.stringify(profile) : null; this.nip05s.set(nip05, { profile: document2, fetchedAt: Date.now() }); } catch (error) { console.error("Failed to save NIP-05 profile for nip05:", nip05, error); } } async loadUsersLNURLDoc(pubkey, maxAgeInSecs = 86400, maxAgeForMissing = 3600) { const cache = this.zappers?.get(pubkey); if (cache) { if (cache.document === null) { if (cache.fetchedAt + maxAgeForMissing * 1e3 < Date.now()) return "missing"; return null; } try { return JSON.parse(cache.document); } catch (_e2) { return "missing"; } } const lnurl = await db.lnurl.get({ pubkey }); if (!lnurl) return "missing"; const now2 = Date.now(); if (lnurl.fetchedAt + maxAgeInSecs * 1e3 < now2) return "missing"; if (lnurl.document === null) { if (lnurl.fetchedAt + maxAgeForMissing * 1e3 < now2) return "missing"; return null; } try { return JSON.parse(lnurl.document); } catch (_e2) { return "missing"; } } async saveUsersLNURLDoc(pubkey, doc) { try { const document2 = doc ? JSON.stringify(doc) : null; this.zappers?.set(pubkey, { document: document2, fetchedAt: Date.now() }); } catch (error) { console.error("Failed to save LNURL document for pubkey:", pubkey, error); } } processFilter(filter, subscription) { const _filter = { ...filter }; _filter.limit = void 0; const filterKeys = new Set(Object.keys(_filter || {})); filterKeys.delete("since"); filterKeys.delete("limit"); filterKeys.delete("until"); try { if (this.byNip33Query(filterKeys, filter, subscription)) return; if (this.byAuthors(filter, subscription)) return; if (this.byIdsQuery(filter, subscription)) return; if (this.byTags(filter, subscription)) return; if (this.byKinds(filterKeys, filter, subscription)) return; } catch (error) { console.error(error); } } async deleteEventIds(eventIds) { eventIds.forEach((id) => this.events.delete(id)); await db.events.where({ id: eventIds }).delete(); } async setEvent(event, _filters, relay) { if (event.kind === 0) { if (!this.profiles) return; try { const profile = profileFromEvent2(event); this.saveProfile(event.pubkey, profile); } catch { this.debug(`Failed to save profile for pubkey: ${event.pubkey}`); } } let addEvent = true; if (event.isParamReplaceable()) { const existingEvent = this.events.get(event.tagId()); if (existingEvent && event.created_at && existingEvent.createdAt > event.created_at) { addEvent = false; } } if (addEvent) { const eventData = { id: event.tagId(), pubkey: event.pubkey, kind: event.kind, createdAt: event.created_at ?? Date.now(), relay: relay?.url, event: event.serialize(this.saveSig, true) }; if (this.saveSig && event.sig) { eventData.sig = event.sig; } this.events.set(event.tagId(), eventData); const indexableTags = getIndexableTags(event); for (const tag of indexableTags) { this.eventTags.add(tag[0] + tag[1], event.tagId()); } if (relay?.url) { db.eventRelays.put({ eventId: event.id, relayUrl: relay.url, seenAt: Date.now() }).catch((e2) => { this.debug("Failed to store relay provenance", e2); }); } } } setEventDup(event, relay) { if (relay?.url) { db.eventRelays.put({ eventId: event.id, relayUrl: relay.url, seenAt: Date.now() }).catch((e2) => { this.debug("Failed to store relay provenance for duplicate event", e2); }); } } updateRelayStatus(url, info) { const existing = this.relayInfo.get(url); const merged = { url, updatedAt: Date.now(), ...existing, ...info, metadata: { ...existing?.metadata, ...info.metadata } }; this.relayInfo.set(url, merged); } getRelayStatus(url) { const a = this.relayInfo.get(url); if (a) { return { lastConnectedAt: a.lastConnectedAt, dontConnectBefore: a.dontConnectBefore, consecutiveFailures: a.consecutiveFailures, lastFailureAt: a.lastFailureAt, nip11: a.nip11, metadata: a.metadata }; } } /** * Searches by authors */ byAuthors(filter, subscription) { if (!filter.authors) return false; let _total = 0; for (const pubkey of filter.authors) { let events = Array.from(this.events.getFromIndex("pubkey", pubkey)); if (filter.kinds) events = events.filter((e2) => filter.kinds?.includes(e2.kind)); foundEvents(subscription, events, filter); _total += events.length; } return true; } /** * Searches by ids */ byIdsQuery(filter, subscription) { if (filter.ids) { for (const id of filter.ids) { const event = this.events.get(id); if (event) foundEvent(subscription, event, event.relay, filter); } return true; } return false; } /** * Searches by NIP-33 */ byNip33Query(filterKeys, filter, subscription) { const f = ["#d", "authors", "kinds"]; const hasAllKeys = filterKeys.size === f.length && f.every((k2) => filterKeys.has(k2)); if (hasAllKeys && filter.kinds && filter.authors) { for (const kind of filter.kinds) { const replaceableKind = kind >= 3e4 && kind < 4e4; if (!replaceableKind) continue; for (const author of filter.authors) { for (const dTag of filter["#d"]) { const replaceableId = `${kind}:${author}:${dTag}`; const event = this.events.get(replaceableId); if (event) foundEvent(subscription, event, event.relay, filter); } } } return true; } return false; } /** * Searches by tags and optionally filters by tags */ byTags(filter, subscription) { const tagFilters = Object.entries(filter).filter(([filter2]) => filter2.startsWith("#") && filter2.length === 2).map(([filter2, values]) => [filter2[1], values]); if (tagFilters.length === 0) return false; for (const [tag, values] of tagFilters) { for (const value of values) { const tagValue = tag + value; const eventIds = this.eventTags.getSet(tagValue); if (!eventIds) continue; eventIds.forEach((id) => { const event = this.events.get(id); if (!event) return; if (!filter.kinds || filter.kinds.includes(event.kind)) { foundEvent(subscription, event, event.relay, filter); } }); } } return true; } byKinds(filterKeys, filter, subscription) { if (!filter.kinds || filterKeys.size !== 1 || !filterKeys.has("kinds")) return false; const limit2 = filter.limit || 500; let totalEvents = 0; const processedEventIds = /* @__PURE__ */ new Set(); const sortedKinds = [...filter.kinds].sort( (a, b) => (this.events.indexes.get("kind")?.get(a)?.size || 0) - (this.events.indexes.get("kind")?.get(b)?.size || 0) ); for (const kind of sortedKinds) { const events = this.events.getFromIndex("kind", kind); for (const event of events) { if (processedEventIds.has(event.id)) continue; processedEventIds.add(event.id); foundEvent(subscription, event, event.relay, filter); totalEvents++; if (totalEvents >= limit2) break; } if (totalEvents >= limit2) break; } return true; } /** * Register a cache module with its schema and migrations */ async registerModule(module2) { await this.moduleManager.registerModule(module2); } /** * Get a collection from a registered module */ async getModuleCollection(namespace, collection2) { return await this.moduleManager.getModuleCollection(namespace, collection2); } /** * Get a decrypted event from the cache by its wrapper ID */ async getDecryptedEvent(wrapperId) { try { const decrypted = await db.decryptedEvents.get(wrapperId); if (decrypted) { const nostrEvent = JSON.parse(decrypted.event); return new NDKEvent2(void 0, nostrEvent); } return null; } catch (e2) { console.error(`[cache-dexie] Error getting decrypted event for wrapper ${wrapperId}:`, e2); return null; } } /** * Add a decrypted event to the cache */ async addDecryptedEvent(wrapperId, decryptedEvent) { try { await db.decryptedEvents.put({ id: wrapperId, event: JSON.stringify(decryptedEvent.rawEvent()) }); } catch (e2) { console.error(`[cache-dexie] Error adding decrypted event for wrapper ${wrapperId}:`, e2); } } }; function foundEvents(subscription, events, filter) { if (filter?.limit && events.length > filter.limit) { events = events.sort((a, b) => b.createdAt - a.createdAt).slice(0, filter.limit); } for (const event of events) { foundEvent(subscription, event, event.relay, filter); } } function foundEvent(subscription, event, relayUrl, filter) { try { const deserializedEvent = deserialize2(event.event); if (filter && !(0, import_nostr_tools21.matchFilter)(filter, deserializedEvent)) return; const ndkEvent = new NDKEvent2(void 0, deserializedEvent); const relay = relayUrl ? subscription.pool.getRelay(relayUrl, false) : void 0; ndkEvent.relay = relay; subscription.eventReceived(ndkEvent, relay, true); } catch (e2) { console.error("failed to deserialize event", e2); } } function getIndexableTags(event) { const indexableTags = []; if (event.kind === 3) return []; for (const tag of event.tags) { if (tag[0].length !== 1) continue; indexableTags.push(tag); if (indexableTags.length >= INDEXABLE_TAGS_LIMIT) return []; } return indexableTags; } // ndk/cache-sqlite-wasm/dist/index.mjs var import_meta = {}; var __create2 = Object.create; var __getProtoOf2 = Object.getPrototypeOf; var __defProp3 = Object.defineProperty; var __getOwnPropNames3 = Object.getOwnPropertyNames; var __hasOwnProp3 = Object.prototype.hasOwnProperty; var __toESM2 = (mod3, isNodeMode, target) => { target = mod3 != null ? __create2(__getProtoOf2(mod3)) : {}; const to = isNodeMode || !mod3 || !mod3.__esModule ? __defProp3(target, "default", { value: mod3, enumerable: true }) : target; for (let key of __getOwnPropNames3(mod3)) if (!__hasOwnProp3.call(to, key)) __defProp3(to, key, { get: () => mod3[key], enumerable: true }); return to; }; var __commonJS2 = (cb, mod3) => () => (mod3 || cb((mod3 = { exports: {} }).exports, mod3), mod3.exports); var __export2 = (target, all) => { for (var name in all) __defProp3(target, name, { get: all[name], enumerable: true, configurable: true, set: (newValue) => all[name] = () => newValue }); }; var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res); var require_types2 = __commonJS2((exports2) => { Object.defineProperty(exports2, "__esModule", { value: true }); }); var require_utils3 = __commonJS2((exports2) => { Object.defineProperty(exports2, "__esModule", { value: true }); exports2._fast_remove_single = void 0; function _fast_remove_single(arr, index) { if (index === -1) return; if (index === 0) arr.shift(); else if (index === arr.length - 1) arr.length = arr.length - 1; else arr.splice(index, 1); } exports2._fast_remove_single = _fast_remove_single; }); var require_bake_collection2 = __commonJS2((exports, module) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.bakeCollectionVariadic = exports.bakeCollectionAwait = exports.bakeCollection = exports.BAKED_EMPTY_FUNC = void 0; exports.BAKED_EMPTY_FUNC = function() { }; var FORLOOP_FALLBACK = 1500; function generateArgsDefCode(numArgs) { var argsDefCode2 = ""; if (numArgs === 0) return argsDefCode2; for (var i3 = 0; i3 < numArgs - 1; ++i3) { argsDefCode2 += "arg" + String(i3) + ", "; } argsDefCode2 += "arg" + String(numArgs - 1); return argsDefCode2; } function generateBodyPartsCode(argsDefCode2, collectionLength) { var funcDefCode2 = "", funcCallCode2 = ""; for (var i3 = 0; i3 < collectionLength; ++i3) { funcDefCode2 += "var f".concat(i3, " = collection[").concat(i3, `]; `); funcCallCode2 += "f".concat(i3, "(").concat(argsDefCode2, `) `); } return { funcDefCode: funcDefCode2, funcCallCode: funcCallCode2 }; } function generateBodyPartsVariadicCode(collectionLength) { var funcDefCode2 = "", funcCallCode2 = ""; for (var i3 = 0; i3 < collectionLength; ++i3) { funcDefCode2 += "var f".concat(i3, " = collection[").concat(i3, `]; `); funcCallCode2 += "f".concat(i3, `.apply(undefined, arguments) `); } return { funcDefCode: funcDefCode2, funcCallCode: funcCallCode2 }; } function bakeCollection(collection, fixedArgsNum) { if (collection.length === 0) return exports.BAKED_EMPTY_FUNC; else if (collection.length === 1) return collection[0]; var funcFactoryCode; if (collection.length < FORLOOP_FALLBACK) { var argsDefCode = generateArgsDefCode(fixedArgsNum); var _a = generateBodyPartsCode(argsDefCode, collection.length), funcDefCode = _a.funcDefCode, funcCallCode = _a.funcCallCode; funcFactoryCode = `(function(collection) { `.concat(funcDefCode, ` collection = undefined; return (function(`).concat(argsDefCode, `) { `).concat(funcCallCode, ` }); })`); } else { var argsDefCode = generateArgsDefCode(fixedArgsNum); if (collection.length % 10 === 0) { funcFactoryCode = `(function(collection) { return (function(`.concat(argsDefCode, `) { for (var i = 0; i < collection.length; i += 10) { collection[i](`).concat(argsDefCode, `); collection[i+1](`).concat(argsDefCode, `); collection[i+2](`).concat(argsDefCode, `); collection[i+3](`).concat(argsDefCode, `); collection[i+4](`).concat(argsDefCode, `); collection[i+5](`).concat(argsDefCode, `); collection[i+6](`).concat(argsDefCode, `); collection[i+7](`).concat(argsDefCode, `); collection[i+8](`).concat(argsDefCode, `); collection[i+9](`).concat(argsDefCode, `); } }); })`); } else if (collection.length % 4 === 0) { funcFactoryCode = `(function(collection) { return (function(`.concat(argsDefCode, `) { for (var i = 0; i < collection.length; i += 4) { collection[i](`).concat(argsDefCode, `); collection[i+1](`).concat(argsDefCode, `); collection[i+2](`).concat(argsDefCode, `); collection[i+3](`).concat(argsDefCode, `); } }); })`); } else if (collection.length % 3 === 0) { funcFactoryCode = `(function(collection) { return (function(`.concat(argsDefCode, `) { for (var i = 0; i < collection.length; i += 3) { collection[i](`).concat(argsDefCode, `); collection[i+1](`).concat(argsDefCode, `); collection[i+2](`).concat(argsDefCode, `); } }); })`); } else { funcFactoryCode = `(function(collection) { return (function(`.concat(argsDefCode, `) { for (var i = 0; i < collection.length; ++i) { collection[i](`).concat(argsDefCode, `); } }); })`); } } { var bakeCollection_1 = void 0; var fixedArgsNum_1 = void 0; var bakeCollectionVariadic_1 = void 0; var bakeCollectionAwait_1 = void 0; var funcFactory = eval(funcFactoryCode); return funcFactory(collection); } } exports.bakeCollection = bakeCollection; function bakeCollectionAwait(collection, fixedArgsNum) { if (collection.length === 0) return exports.BAKED_EMPTY_FUNC; else if (collection.length === 1) return collection[0]; var funcFactoryCode; if (collection.length < FORLOOP_FALLBACK) { var argsDefCode = generateArgsDefCode(fixedArgsNum); var _a = generateBodyPartsCode(argsDefCode, collection.length), funcDefCode = _a.funcDefCode, funcCallCode = _a.funcCallCode; funcFactoryCode = `(function(collection) { `.concat(funcDefCode, ` collection = undefined; return (function(`).concat(argsDefCode, `) { return Promise.all([ `).concat(funcCallCode, ` ]); }); })`); } else { var argsDefCode = generateArgsDefCode(fixedArgsNum); funcFactoryCode = `(function(collection) { return (function(`.concat(argsDefCode, `) { var promises = Array(collection.length); for (var i = 0; i < collection.length; ++i) { promises[i] = collection[i](`).concat(argsDefCode, `); } return Promise.all(promises); }); })`); } { var bakeCollection_2 = void 0; var fixedArgsNum_2 = void 0; var bakeCollectionVariadic_2 = void 0; var bakeCollectionAwait_2 = void 0; var funcFactory = eval(funcFactoryCode); return funcFactory(collection); } } exports.bakeCollectionAwait = bakeCollectionAwait; function bakeCollectionVariadic(collection) { if (collection.length === 0) return exports.BAKED_EMPTY_FUNC; else if (collection.length === 1) return collection[0]; var funcFactoryCode; if (collection.length < FORLOOP_FALLBACK) { var _a = generateBodyPartsVariadicCode(collection.length), funcDefCode = _a.funcDefCode, funcCallCode = _a.funcCallCode; funcFactoryCode = `(function(collection) { `.concat(funcDefCode, ` collection = undefined; return (function() { `).concat(funcCallCode, ` }); })`); } else { funcFactoryCode = `(function(collection) { return (function() { for (var i = 0; i < collection.length; ++i) { collection[i].apply(undefined, arguments); } }); })`; } { var bakeCollection_3 = void 0; var fixedArgsNum = void 0; var bakeCollectionVariadic_3 = void 0; var bakeCollectionAwait_3 = void 0; var funcFactory = eval(funcFactoryCode); return funcFactory(collection); } } exports.bakeCollectionVariadic = bakeCollectionVariadic; }); var require_task_collection3 = __commonJS2((exports2) => { var __spreadArray = exports2 && exports2.__spreadArray || function(to, from, pack) { if (pack || arguments.length === 2) for (var i3 = 0, l3 = from.length, ar; i3 < l3; i3++) { if (ar || !(i3 in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i3); ar[i3] = from[i3]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.TaskCollection = void 0; var utils_1 = require_utils3(); var bake_collection_1 = require_bake_collection2(); function push_norebuild(a, b) { var len = this.length; if (len > 1) { if (b) { var _a210; (_a210 = this._tasks).push.apply(_a210, arguments); this.length += arguments.length; } else { this._tasks.push(a); this.length++; } } else { if (b) { if (len === 1) { var newAr = Array(1 + arguments.length); newAr.push(newAr); newAr.push.apply(newAr, arguments); this._tasks = newAr; } else { var newAr = Array(arguments.length); newAr.push.apply(newAr, arguments); this._tasks = newAr; } this.length += arguments.length; } else { if (len === 1) this._tasks = [this._tasks, a]; else this._tasks = a; this.length++; } } } function push_rebuild(a, b) { var len = this.length; if (len > 1) { if (b) { var _a210; (_a210 = this._tasks).push.apply(_a210, arguments); this.length += arguments.length; } else { this._tasks.push(a); this.length++; } } else { if (b) { if (len === 1) { var newAr = Array(1 + arguments.length); newAr.push(newAr); newAr.push.apply(newAr, arguments); this._tasks = newAr; } else { var newAr = Array(arguments.length); newAr.push.apply(newAr, arguments); this._tasks = newAr; } this.length += arguments.length; } else { if (len === 1) this._tasks = [this._tasks, a]; else this._tasks = a; this.length++; } } if (this.firstEmitBuildStrategy) this.call = rebuild_on_first_call; else this.rebuild(); } function removeLast_norebuild(a) { if (this.length === 0) return; if (this.length === 1) { if (this._tasks === a) { this.length = 0; } } else { (0, utils_1._fast_remove_single)(this._tasks, this._tasks.lastIndexOf(a)); if (this._tasks.length === 1) { this._tasks = this._tasks[0]; this.length = 1; } else this.length = this._tasks.length; } } function removeLast_rebuild(a) { if (this.length === 0) return; if (this.length === 1) { if (this._tasks === a) { this.length = 0; } if (this.firstEmitBuildStrategy) { this.call = bake_collection_1.BAKED_EMPTY_FUNC; return; } else { this.rebuild(); return; } } else { (0, utils_1._fast_remove_single)(this._tasks, this._tasks.lastIndexOf(a)); if (this._tasks.length === 1) { this._tasks = this._tasks[0]; this.length = 1; } else this.length = this._tasks.length; } if (this.firstEmitBuildStrategy) this.call = rebuild_on_first_call; else this.rebuild(); } function insert_norebuild(index) { var _b; var func = []; for (var _i = 1; _i < arguments.length; _i++) { func[_i - 1] = arguments[_i]; } if (this.length === 0) { this._tasks = func; this.length = 1; } else if (this.length === 1) { func.unshift(this._tasks); this._tasks = func; this.length = this._tasks.length; } else { (_b = this._tasks).splice.apply(_b, __spreadArray([index, 0], func, false)); this.length = this._tasks.length; } } function insert_rebuild(index) { var _b; var func = []; for (var _i = 1; _i < arguments.length; _i++) { func[_i - 1] = arguments[_i]; } if (this.length === 0) { this._tasks = func; this.length = 1; } else if (this.length === 1) { func.unshift(this._tasks); this._tasks = func; this.length = this._tasks.length; } else { (_b = this._tasks).splice.apply(_b, __spreadArray([index, 0], func, false)); this.length = this._tasks.length; } if (this.firstEmitBuildStrategy) this.call = rebuild_on_first_call; else this.rebuild(); } function rebuild_noawait() { if (this.length === 0) this.call = bake_collection_1.BAKED_EMPTY_FUNC; else if (this.length === 1) this.call = this._tasks; else this.call = (0, bake_collection_1.bakeCollection)(this._tasks, this.argsNum); } function rebuild_await() { if (this.length === 0) this.call = bake_collection_1.BAKED_EMPTY_FUNC; else if (this.length === 1) this.call = this._tasks; else this.call = (0, bake_collection_1.bakeCollectionAwait)(this._tasks, this.argsNum); } function rebuild_on_first_call() { this.rebuild(); this.call.apply(void 0, arguments); } var TaskCollection = /* @__PURE__ */ (function() { function TaskCollection2(argsNum, autoRebuild, initialTasks, awaitTasks) { if (autoRebuild === void 0) { autoRebuild = true; } if (initialTasks === void 0) { initialTasks = null; } if (awaitTasks === void 0) { awaitTasks = false; } this.awaitTasks = awaitTasks; this.call = bake_collection_1.BAKED_EMPTY_FUNC; this.argsNum = argsNum; this.firstEmitBuildStrategy = true; if (awaitTasks) this.rebuild = rebuild_await.bind(this); else this.rebuild = rebuild_noawait.bind(this); this.setAutoRebuild(autoRebuild); if (initialTasks) { if (typeof initialTasks === "function") { this._tasks = initialTasks; this.length = 1; } else { this._tasks = initialTasks; this.length = initialTasks.length; } } else { this._tasks = null; this.length = 0; } if (autoRebuild) this.rebuild(); } return TaskCollection2; })(); exports2.TaskCollection = TaskCollection; function fastClear() { this._tasks = null; this.length = 0; this.call = bake_collection_1.BAKED_EMPTY_FUNC; } function clear() { this._tasks = null; this.length = 0; this.call = bake_collection_1.BAKED_EMPTY_FUNC; } function growArgsNum(argsNum) { if (this.argsNum < argsNum) { this.argsNum = argsNum; if (this.firstEmitBuildStrategy) this.call = rebuild_on_first_call; else this.rebuild(); } } function setAutoRebuild(newVal) { if (newVal) { this.push = push_rebuild.bind(this); this.insert = insert_rebuild.bind(this); this.removeLast = removeLast_rebuild.bind(this); } else { this.push = push_norebuild.bind(this); this.insert = insert_norebuild.bind(this); this.removeLast = removeLast_norebuild.bind(this); } } function tasksAsArray() { if (this.length === 0) return []; if (this.length === 1) return [this._tasks]; return this._tasks; } function setTasks(tasks) { if (tasks.length === 0) { this.length = 0; this.call = bake_collection_1.BAKED_EMPTY_FUNC; } else if (tasks.length === 1) { this.length = 1; this.call = tasks[0]; this._tasks = tasks[0]; } else { this.length = tasks.length; this._tasks = tasks; if (this.firstEmitBuildStrategy) this.call = rebuild_on_first_call; else this.rebuild(); } } TaskCollection.prototype.fastClear = fastClear; TaskCollection.prototype.clear = clear; TaskCollection.prototype.growArgsNum = growArgsNum; TaskCollection.prototype.setAutoRebuild = setAutoRebuild; TaskCollection.prototype.tasksAsArray = tasksAsArray; TaskCollection.prototype.setTasks = setTasks; }); var require_task_collection22 = __commonJS2((exports2) => { var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; var desc = Object.getOwnPropertyDescriptor(m, k2); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k2]; } }; } Object.defineProperty(o, k22, desc); } : function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; o[k22] = m[k2]; }); var __exportStar = exports2 && exports2.__exportStar || function(m, exports22) { for (var p5 in m) if (p5 !== "default" && !Object.prototype.hasOwnProperty.call(exports22, p5)) __createBinding(exports22, m, p5); }; Object.defineProperty(exports2, "__esModule", { value: true }); __exportStar(require_task_collection3(), exports2); }); var require_utils22 = __commonJS2((exports2) => { Object.defineProperty(exports2, "__esModule", { value: true }); exports2.nullObj = void 0; function nullObj() { var x2 = {}; x2.__proto__ = null; return x2; } exports2.nullObj = nullObj; }); var require_ee2 = __commonJS2((exports2) => { var __spreadArray = exports2 && exports2.__spreadArray || function(to, from, pack) { if (pack || arguments.length === 2) for (var i3 = 0, l3 = from.length, ar; i3 < l3; i3++) { if (ar || !(i3 in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i3); ar[i3] = from[i3]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.EventEmitter = void 0; var task_collection_1 = require_task_collection22(); var utils_1 = require_utils3(); var utils_2 = require_utils22(); function emit(event, a, b, c, d17, e2) { var ev = this.events[event]; if (ev) { if (ev.length === 0) return false; if (ev.argsNum < 6) { ev.call(a, b, c, d17, e2); } else { var arr = new Array(ev.argsNum); for (var i3 = 0, len = arr.length; i3 < len; ++i3) { arr[i3] = arguments[i3 + 1]; } ev.call.apply(void 0, arr); } return true; } return false; } function emitHasOnce(event, a, b, c, d17, e2) { var ev = this.events[event]; var argsArr; if (ev !== void 0) { if (ev.length === 0) return false; if (ev.argsNum < 6) { ev.call(a, b, c, d17, e2); } else { argsArr = new Array(ev.argsNum); for (var i3 = 0, len = argsArr.length; i3 < len; ++i3) { argsArr[i3] = arguments[i3 + 1]; } ev.call.apply(void 0, argsArr); } } var oev = this.onceEvents[event]; if (oev) { if (typeof oev === "function") { this.onceEvents[event] = void 0; if (arguments.length < 6) { oev(a, b, c, d17, e2); } else { if (argsArr === void 0) { argsArr = new Array(arguments.length - 1); for (var i3 = 0, len = argsArr.length; i3 < len; ++i3) { argsArr[i3] = arguments[i3 + 1]; } } oev.apply(void 0, argsArr); } } else { var fncs = oev; this.onceEvents[event] = void 0; if (arguments.length < 6) { for (var i3 = 0; i3 < fncs.length; ++i3) { fncs[i3](a, b, c, d17, e2); } } else { if (argsArr === void 0) { argsArr = new Array(arguments.length - 1); for (var i3 = 0, len = argsArr.length; i3 < len; ++i3) { argsArr[i3] = arguments[i3 + 1]; } } for (var i3 = 0; i3 < fncs.length; ++i3) { fncs[i3].apply(void 0, argsArr); } } } return true; } return ev !== void 0; } var EventEmitter18 = (function() { function EventEmitter23() { this.events = (0, utils_2.nullObj)(); this.onceEvents = (0, utils_2.nullObj)(); this._symbolKeys = /* @__PURE__ */ new Set(); this.maxListeners = Infinity; } Object.defineProperty(EventEmitter23.prototype, "_eventsCount", { get: function() { return this.eventNames().length; }, enumerable: false, configurable: true }); return EventEmitter23; })(); exports2.EventEmitter = EventEmitter18; function once(event, listener) { if (this.emit === emit) { this.emit = emitHasOnce; } switch (typeof this.onceEvents[event]) { case "undefined": this.onceEvents[event] = listener; if (typeof event === "symbol") this._symbolKeys.add(event); break; case "function": this.onceEvents[event] = [this.onceEvents[event], listener]; break; case "object": this.onceEvents[event].push(listener); } return this; } function addListener(event, listener, argsNum) { if (argsNum === void 0) { argsNum = listener.length; } if (typeof listener !== "function") throw new TypeError("The listener must be a function"); var evtmap = this.events[event]; if (!evtmap) { this.events[event] = new task_collection_1.TaskCollection(argsNum, true, listener, false); if (typeof event === "symbol") this._symbolKeys.add(event); } else { evtmap.push(listener); evtmap.growArgsNum(argsNum); if (this.maxListeners !== Infinity && this.maxListeners <= evtmap.length) console.warn('Maximum event listeners for "'.concat(String(event), '" event!')); } return this; } function removeListener(event, listener) { var evt = this.events[event]; if (evt) { evt.removeLast(listener); } var evto = this.onceEvents[event]; if (evto) { if (typeof evto === "function") { this.onceEvents[event] = void 0; } else if (typeof evto === "object") { if (evto.length === 1 && evto[0] === listener) { this.onceEvents[event] = void 0; } else { (0, utils_1._fast_remove_single)(evto, evto.lastIndexOf(listener)); } } } return this; } function addListenerBound(event, listener, bindTo, argsNum) { if (bindTo === void 0) { bindTo = this; } if (argsNum === void 0) { argsNum = listener.length; } if (!this.boundFuncs) this.boundFuncs = /* @__PURE__ */ new Map(); var bound = listener.bind(bindTo); this.boundFuncs.set(listener, bound); return this.addListener(event, bound, argsNum); } function removeListenerBound(event, listener) { var _a210, _b; var bound = (_a210 = this.boundFuncs) === null || _a210 === void 0 ? void 0 : _a210.get(listener); (_b = this.boundFuncs) === null || _b === void 0 || _b.delete(listener); return this.removeListener(event, bound); } function hasListeners(event) { return this.events[event] && !!this.events[event].length; } function prependListener(event, listener, argsNum) { if (argsNum === void 0) { argsNum = listener.length; } if (typeof listener !== "function") throw new TypeError("The listener must be a function"); var evtmap = this.events[event]; if (!evtmap || !(evtmap instanceof task_collection_1.TaskCollection)) { evtmap = this.events[event] = new task_collection_1.TaskCollection(argsNum, true, listener, false); if (typeof event === "symbol") this._symbolKeys.add(event); } else { evtmap.insert(0, listener); evtmap.growArgsNum(argsNum); if (this.maxListeners !== Infinity && this.maxListeners <= evtmap.length) console.warn('Maximum event listeners for "'.concat(String(event), '" event!')); } return this; } function prependOnceListener(event, listener) { if (this.emit === emit) { this.emit = emitHasOnce; } var evtmap = this.onceEvents[event]; if (!evtmap) { this.onceEvents[event] = [listener]; if (typeof event === "symbol") this._symbolKeys.add(event); } else if (typeof evtmap !== "object") { this.onceEvents[event] = [listener, evtmap]; if (typeof event === "symbol") this._symbolKeys.add(event); } else { evtmap.unshift(listener); if (this.maxListeners !== Infinity && this.maxListeners <= evtmap.length) { console.warn('Maximum event listeners for "'.concat(String(event), '" once event!')); } } return this; } function removeAllListeners(event) { if (event === void 0) { this.events = (0, utils_2.nullObj)(); this.onceEvents = (0, utils_2.nullObj)(); this._symbolKeys = /* @__PURE__ */ new Set(); } else { this.events[event] = void 0; this.onceEvents[event] = void 0; if (typeof event === "symbol") this._symbolKeys.delete(event); } return this; } function setMaxListeners(n) { this.maxListeners = n; return this; } function getMaxListeners() { return this.maxListeners; } function listeners(event) { if (this.emit === emit) return this.events[event] ? this.events[event].tasksAsArray().slice() : []; else { if (this.events[event] && this.onceEvents[event]) { return __spreadArray(__spreadArray([], this.events[event].tasksAsArray(), true), typeof this.onceEvents[event] === "function" ? [this.onceEvents[event]] : this.onceEvents[event], true); } else if (this.events[event]) return this.events[event].tasksAsArray(); else if (this.onceEvents[event]) return typeof this.onceEvents[event] === "function" ? [this.onceEvents[event]] : this.onceEvents[event]; else return []; } } function eventNames() { var _this = this; if (this.emit === emit) { var keys = Object.keys(this.events); return __spreadArray(__spreadArray([], keys, true), Array.from(this._symbolKeys), true).filter(function(x2) { return x2 in _this.events && _this.events[x2] && _this.events[x2].length; }); } else { var keys = Object.keys(this.events).filter(function(x2) { return _this.events[x2] && _this.events[x2].length; }); var keysO = Object.keys(this.onceEvents).filter(function(x2) { return _this.onceEvents[x2] && _this.onceEvents[x2].length; }); return __spreadArray(__spreadArray(__spreadArray([], keys, true), keysO, true), Array.from(this._symbolKeys).filter(function(x2) { return x2 in _this.events && _this.events[x2] && _this.events[x2].length || x2 in _this.onceEvents && _this.onceEvents[x2] && _this.onceEvents[x2].length; }), true); } } function listenerCount(type) { if (this.emit === emit) return this.events[type] && this.events[type].length || 0; else return (this.events[type] && this.events[type].length || 0) + (this.onceEvents[type] && this.onceEvents[type].length || 0); } EventEmitter18.prototype.emit = emit; EventEmitter18.prototype.on = addListener; EventEmitter18.prototype.once = once; EventEmitter18.prototype.addListener = addListener; EventEmitter18.prototype.removeListener = removeListener; EventEmitter18.prototype.addListenerBound = addListenerBound; EventEmitter18.prototype.removeListenerBound = removeListenerBound; EventEmitter18.prototype.hasListeners = hasListeners; EventEmitter18.prototype.prependListener = prependListener; EventEmitter18.prototype.prependOnceListener = prependOnceListener; EventEmitter18.prototype.off = removeListener; EventEmitter18.prototype.removeAllListeners = removeAllListeners; EventEmitter18.prototype.setMaxListeners = setMaxListeners; EventEmitter18.prototype.getMaxListeners = getMaxListeners; EventEmitter18.prototype.listeners = listeners; EventEmitter18.prototype.eventNames = eventNames; EventEmitter18.prototype.listenerCount = listenerCount; }); var require_lib3 = __commonJS2((exports2) => { var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; var desc = Object.getOwnPropertyDescriptor(m, k2); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k2]; } }; } Object.defineProperty(o, k22, desc); } : function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; o[k22] = m[k2]; }); var __exportStar = exports2 && exports2.__exportStar || function(m, exports22) { for (var p5 in m) if (p5 !== "default" && !Object.prototype.hasOwnProperty.call(exports22, p5)) __createBinding(exports22, m, p5); }; Object.defineProperty(exports2, "__esModule", { value: true }); __exportStar(require_types2(), exports2); __exportStar(require_ee2(), exports2); }); var require_ms2 = __commonJS2((exports2, module2) => { var s = 1e3; var m = s * 60; var h2 = m * 60; var d17 = h2 * 24; var w2 = d17 * 7; var y2 = d17 * 365.25; module2.exports = function(val, options) { options = options || {}; var type = typeof val; if (type === "string" && val.length > 0) { return parse4(val); } else if (type === "number" && isFinite(val)) { return options.long ? fmtLong(val) : fmtShort(val); } throw new Error("val is not a non-empty string or a valid number. val=" + JSON.stringify(val)); }; function parse4(str) { str = String(str); if (str.length > 100) { return; } var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(str); if (!match) { return; } var n = parseFloat(match[1]); var type = (match[2] || "ms").toLowerCase(); switch (type) { case "years": case "year": case "yrs": case "yr": case "y": return n * y2; case "weeks": case "week": case "w": return n * w2; case "days": case "day": case "d": return n * d17; case "hours": case "hour": case "hrs": case "hr": case "h": return n * h2; case "minutes": case "minute": case "mins": case "min": case "m": return n * m; case "seconds": case "second": case "secs": case "sec": case "s": return n * s; case "milliseconds": case "millisecond": case "msecs": case "msec": case "ms": return n; default: return; } } function fmtShort(ms) { var msAbs = Math.abs(ms); if (msAbs >= d17) { return Math.round(ms / d17) + "d"; } if (msAbs >= h2) { return Math.round(ms / h2) + "h"; } if (msAbs >= m) { return Math.round(ms / m) + "m"; } if (msAbs >= s) { return Math.round(ms / s) + "s"; } return ms + "ms"; } function fmtLong(ms) { var msAbs = Math.abs(ms); if (msAbs >= d17) { return plural(ms, msAbs, d17, "day"); } if (msAbs >= h2) { return plural(ms, msAbs, h2, "hour"); } if (msAbs >= m) { return plural(ms, msAbs, m, "minute"); } if (msAbs >= s) { return plural(ms, msAbs, s, "second"); } return ms + " ms"; } function plural(ms, msAbs, n, name) { var isPlural = msAbs >= n * 1.5; return Math.round(ms / n) + " " + name + (isPlural ? "s" : ""); } }); var require_common2 = __commonJS2((exports2, module2) => { function setup(env) { createDebug19.debug = createDebug19; createDebug19.default = createDebug19; createDebug19.coerce = coerce; createDebug19.disable = disable; createDebug19.enable = enable; createDebug19.enabled = enabled; createDebug19.humanize = require_ms2(); createDebug19.destroy = destroy; Object.keys(env).forEach((key) => { createDebug19[key] = env[key]; }); createDebug19.names = []; createDebug19.skips = []; createDebug19.formatters = {}; function selectColor(namespace) { let hash3 = 0; for (let i3 = 0; i3 < namespace.length; i3++) { hash3 = (hash3 << 5) - hash3 + namespace.charCodeAt(i3); hash3 |= 0; } return createDebug19.colors[Math.abs(hash3) % createDebug19.colors.length]; } createDebug19.selectColor = selectColor; function createDebug19(namespace) { let prevTime; let enableOverride = null; let namespacesCache; let enabledCache; function debug15(...args) { if (!debug15.enabled) { return; } const self2 = debug15; const curr = Number(/* @__PURE__ */ new Date()); const ms = curr - (prevTime || curr); self2.diff = ms; self2.prev = prevTime; self2.curr = curr; prevTime = curr; args[0] = createDebug19.coerce(args[0]); if (typeof args[0] !== "string") { args.unshift("%O"); } let index = 0; args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { if (match === "%%") { return "%"; } index++; const formatter = createDebug19.formatters[format]; if (typeof formatter === "function") { const val = args[index]; match = formatter.call(self2, val); args.splice(index, 1); index--; } return match; }); createDebug19.formatArgs.call(self2, args); const logFn = self2.log || createDebug19.log; logFn.apply(self2, args); } debug15.namespace = namespace; debug15.useColors = createDebug19.useColors(); debug15.color = createDebug19.selectColor(namespace); debug15.extend = extend; debug15.destroy = createDebug19.destroy; Object.defineProperty(debug15, "enabled", { enumerable: true, configurable: false, get: () => { if (enableOverride !== null) { return enableOverride; } if (namespacesCache !== createDebug19.namespaces) { namespacesCache = createDebug19.namespaces; enabledCache = createDebug19.enabled(namespace); } return enabledCache; }, set: (v6) => { enableOverride = v6; } }); if (typeof createDebug19.init === "function") { createDebug19.init(debug15); } return debug15; } function extend(namespace, delimiter) { const newDebug = createDebug19(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace); newDebug.log = this.log; return newDebug; } function enable(namespaces) { createDebug19.save(namespaces); createDebug19.namespaces = namespaces; createDebug19.names = []; createDebug19.skips = []; const split2 = (typeof namespaces === "string" ? namespaces : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean); for (const ns of split2) { if (ns[0] === "-") { createDebug19.skips.push(ns.slice(1)); } else { createDebug19.names.push(ns); } } } function matchesTemplate(search, template) { let searchIndex = 0; let templateIndex = 0; let starIndex = -1; let matchIndex = 0; while (searchIndex < search.length) { if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === "*")) { if (template[templateIndex] === "*") { starIndex = templateIndex; matchIndex = searchIndex; templateIndex++; } else { searchIndex++; templateIndex++; } } else if (starIndex !== -1) { templateIndex = starIndex + 1; matchIndex++; searchIndex = matchIndex; } else { return false; } } while (templateIndex < template.length && template[templateIndex] === "*") { templateIndex++; } return templateIndex === template.length; } function disable() { const namespaces = [ ...createDebug19.names, ...createDebug19.skips.map((namespace) => "-" + namespace) ].join(","); createDebug19.enable(""); return namespaces; } function enabled(name) { for (const skip of createDebug19.skips) { if (matchesTemplate(name, skip)) { return false; } } for (const ns of createDebug19.names) { if (matchesTemplate(name, ns)) { return true; } } return false; } function coerce(val) { if (val instanceof Error) { return val.stack || val.message; } return val; } function destroy() { console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."); } createDebug19.enable(createDebug19.load()); return createDebug19; } module2.exports = setup; }); var require_browser2 = __commonJS2((exports2, module2) => { exports2.formatArgs = formatArgs; exports2.save = save; exports2.load = load; exports2.useColors = useColors; exports2.storage = localstorage(); exports2.destroy = /* @__PURE__ */ (() => { let warned = false; return () => { if (!warned) { warned = true; console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."); } }; })(); exports2.colors = [ "#0000CC", "#0000FF", "#0033CC", "#0033FF", "#0066CC", "#0066FF", "#0099CC", "#0099FF", "#00CC00", "#00CC33", "#00CC66", "#00CC99", "#00CCCC", "#00CCFF", "#3300CC", "#3300FF", "#3333CC", "#3333FF", "#3366CC", "#3366FF", "#3399CC", "#3399FF", "#33CC00", "#33CC33", "#33CC66", "#33CC99", "#33CCCC", "#33CCFF", "#6600CC", "#6600FF", "#6633CC", "#6633FF", "#66CC00", "#66CC33", "#9900CC", "#9900FF", "#9933CC", "#9933FF", "#99CC00", "#99CC33", "#CC0000", "#CC0033", "#CC0066", "#CC0099", "#CC00CC", "#CC00FF", "#CC3300", "#CC3333", "#CC3366", "#CC3399", "#CC33CC", "#CC33FF", "#CC6600", "#CC6633", "#CC9900", "#CC9933", "#CCCC00", "#CCCC33", "#FF0000", "#FF0033", "#FF0066", "#FF0099", "#FF00CC", "#FF00FF", "#FF3300", "#FF3333", "#FF3366", "#FF3399", "#FF33CC", "#FF33FF", "#FF6600", "#FF6633", "#FF9900", "#FF9933", "#FFCC00", "#FFCC33" ]; function useColors() { if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) { return true; } if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { return false; } let m; return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/); } function formatArgs(args) { args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module2.exports.humanize(this.diff); if (!this.useColors) { return; } const c = "color: " + this.color; args.splice(1, 0, c, "color: inherit"); let index = 0; let lastC = 0; args[0].replace(/%[a-zA-Z%]/g, (match) => { if (match === "%%") { return; } index++; if (match === "%c") { lastC = index; } }); args.splice(lastC, 0, c); } exports2.log = console.debug || console.log || (() => { }); function save(namespaces) { try { if (namespaces) { exports2.storage.setItem("debug", namespaces); } else { exports2.storage.removeItem("debug"); } } catch (error) { } } function load() { let r; try { r = exports2.storage.getItem("debug") || exports2.storage.getItem("DEBUG"); } catch (error) { } if (!r && typeof process !== "undefined" && "env" in process) { r = process.env.DEBUG; } return r; } function localstorage() { try { return localStorage; } catch (error) { } } module2.exports = require_common2()(exports2); var { formatters } = module2.exports; formatters.j = function(v6) { try { return JSON.stringify(v6); } catch (error) { return "[UnexpectedJSONParseError]: " + error.message; } }; }); var require_LRUCacheNode2 = __commonJS2((exports2) => { Object.defineProperty(exports2, "__esModule", { value: true }); exports2.LRUCacheNode = void 0; class LRUCacheNode { constructor(key, value, options) { const { entryExpirationTimeInMS = null, next = null, prev = null, onEntryEvicted, onEntryMarkedAsMostRecentlyUsed, clone, cloneFn } = options !== null && options !== void 0 ? options : {}; if (typeof entryExpirationTimeInMS === "number" && (entryExpirationTimeInMS <= 0 || Number.isNaN(entryExpirationTimeInMS))) { throw new Error("entryExpirationTimeInMS must either be null (no expiry) or greater than 0"); } this.clone = clone !== null && clone !== void 0 ? clone : false; this.cloneFn = cloneFn !== null && cloneFn !== void 0 ? cloneFn : this.defaultClone; this.key = key; this.internalValue = this.clone ? this.cloneFn(value) : value; this.created = Date.now(); this.entryExpirationTimeInMS = entryExpirationTimeInMS; this.next = next; this.prev = prev; this.onEntryEvicted = onEntryEvicted; this.onEntryMarkedAsMostRecentlyUsed = onEntryMarkedAsMostRecentlyUsed; } get value() { return this.clone ? this.cloneFn(this.internalValue) : this.internalValue; } get isExpired() { return typeof this.entryExpirationTimeInMS === "number" && Date.now() - this.created > this.entryExpirationTimeInMS; } invokeOnEvicted() { if (this.onEntryEvicted) { const { key, value, isExpired } = this; this.onEntryEvicted({ key, value, isExpired }); } } invokeOnEntryMarkedAsMostRecentlyUsed() { if (this.onEntryMarkedAsMostRecentlyUsed) { const { key, value } = this; this.onEntryMarkedAsMostRecentlyUsed({ key, value }); } } defaultClone(value) { if (typeof value === "boolean" || typeof value === "string" || typeof value === "number") { return value; } return JSON.parse(JSON.stringify(value)); } } exports2.LRUCacheNode = LRUCacheNode; }); var require_LRUCache2 = __commonJS2((exports2) => { Object.defineProperty(exports2, "__esModule", { value: true }); exports2.LRUCache = void 0; var LRUCacheNode_1 = require_LRUCacheNode2(); class LRUCache7 { constructor(options) { this.lookupTable = /* @__PURE__ */ new Map(); this.head = null; this.tail = null; const { maxSize = 25, entryExpirationTimeInMS = null, onEntryEvicted, onEntryMarkedAsMostRecentlyUsed, cloneFn, clone } = options !== null && options !== void 0 ? options : {}; if (Number.isNaN(maxSize) || maxSize <= 0) { throw new Error("maxSize must be greater than 0."); } if (typeof entryExpirationTimeInMS === "number" && (entryExpirationTimeInMS <= 0 || Number.isNaN(entryExpirationTimeInMS))) { throw new Error("entryExpirationTimeInMS must either be null (no expiry) or greater than 0"); } this.maxSizeInternal = maxSize; this.entryExpirationTimeInMS = entryExpirationTimeInMS; this.onEntryEvicted = onEntryEvicted; this.onEntryMarkedAsMostRecentlyUsed = onEntryMarkedAsMostRecentlyUsed; this.clone = clone; this.cloneFn = cloneFn; } get size() { this.cleanCache(); return this.lookupTable.size; } get remainingSize() { return this.maxSizeInternal - this.size; } get newest() { if (!this.head) { return null; } if (this.head.isExpired) { this.removeNodeFromListAndLookupTable(this.head); return this.newest; } return this.mapNodeToEntry(this.head); } get oldest() { if (!this.tail) { return null; } if (this.tail.isExpired) { this.removeNodeFromListAndLookupTable(this.tail); return this.oldest; } return this.mapNodeToEntry(this.tail); } get maxSize() { return this.maxSizeInternal; } set maxSize(value) { if (Number.isNaN(value) || value <= 0) { throw new Error("maxSize must be greater than 0."); } this.maxSizeInternal = value; this.enforceSizeLimit(); } set(key, value, entryOptions) { const currentNodeForKey = this.lookupTable.get(key); if (currentNodeForKey) { this.removeNodeFromListAndLookupTable(currentNodeForKey); } const node = new LRUCacheNode_1.LRUCacheNode(key, value, { entryExpirationTimeInMS: this.entryExpirationTimeInMS, onEntryEvicted: this.onEntryEvicted, onEntryMarkedAsMostRecentlyUsed: this.onEntryMarkedAsMostRecentlyUsed, clone: this.clone, cloneFn: this.cloneFn, ...entryOptions }); this.setNodeAsHead(node); this.lookupTable.set(key, node); this.enforceSizeLimit(); return this; } get(key) { const node = this.lookupTable.get(key); if (!node) { return null; } if (node.isExpired) { this.removeNodeFromListAndLookupTable(node); return null; } this.setNodeAsHead(node); return node.value; } peek(key) { const node = this.lookupTable.get(key); if (!node) { return null; } if (node.isExpired) { this.removeNodeFromListAndLookupTable(node); return null; } return node.value; } delete(key) { const node = this.lookupTable.get(key); if (!node) { return false; } return this.removeNodeFromListAndLookupTable(node); } has(key) { const node = this.lookupTable.get(key); if (!node) { return false; } if (node.isExpired) { this.removeNodeFromListAndLookupTable(node); return false; } return true; } clear() { this.head = null; this.tail = null; this.lookupTable.clear(); } find(condition) { let node = this.head; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } const entry = this.mapNodeToEntry(node); if (condition(entry)) { this.setNodeAsHead(node); return entry; } node = node.next; } return null; } forEach(callback) { let node = this.head; let index = 0; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } callback(node.value, node.key, index); node = node.next; index++; } } *values() { let node = this.head; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } yield node.value; node = node.next; } } *keys() { let node = this.head; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } yield node.key; node = node.next; } } *entries() { let node = this.head; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } yield this.mapNodeToEntry(node); node = node.next; } } *[Symbol.iterator]() { let node = this.head; while (node) { if (node.isExpired) { const next = node.next; this.removeNodeFromListAndLookupTable(node); node = next; continue; } yield this.mapNodeToEntry(node); node = node.next; } } enforceSizeLimit() { let node = this.tail; while (node !== null && this.size > this.maxSizeInternal) { const prev = node.prev; this.removeNodeFromListAndLookupTable(node); node = prev; } } mapNodeToEntry({ key, value }) { return { key, value }; } setNodeAsHead(node) { this.removeNodeFromList(node); if (!this.head) { this.head = node; this.tail = node; } else { node.next = this.head; this.head.prev = node; this.head = node; } node.invokeOnEntryMarkedAsMostRecentlyUsed(); } removeNodeFromList(node) { if (node.prev !== null) { node.prev.next = node.next; } if (node.next !== null) { node.next.prev = node.prev; } if (this.head === node) { this.head = node.next; } if (this.tail === node) { this.tail = node.prev; } node.next = null; node.prev = null; } removeNodeFromListAndLookupTable(node) { node.invokeOnEvicted(); this.removeNodeFromList(node); return this.lookupTable.delete(node.key); } cleanCache() { if (!this.entryExpirationTimeInMS) { return; } const expiredNodes = []; for (const node of this.lookupTable.values()) { if (node.isExpired) { expiredNodes.push(node); } } expiredNodes.forEach((node) => this.removeNodeFromListAndLookupTable(node)); } } exports2.LRUCache = LRUCache7; }); var require_dist2 = __commonJS2((exports2) => { var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; var desc = Object.getOwnPropertyDescriptor(m, k2); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k2]; } }; } Object.defineProperty(o, k22, desc); } : function(o, m, k2, k22) { if (k22 === void 0) k22 = k2; o[k22] = m[k2]; }); var __exportStar = exports2 && exports2.__exportStar || function(m, exports22) { for (var p5 in m) if (p5 !== "default" && !Object.prototype.hasOwnProperty.call(exports22, p5)) __createBinding(exports22, m, p5); }; Object.defineProperty(exports2, "__esModule", { value: true }); __exportStar(require_LRUCache2(), exports2); }); var require_lib22 = __commonJS2((exports2) => { Object.defineProperty(exports2, "__esModule", { value: true }); exports2.bytes = exports2.stringToBytes = exports2.str = exports2.bytesToString = exports2.hex = exports2.utf8 = exports2.bech32m = exports2.bech32 = exports2.base58check = exports2.base58xmr = exports2.base58xrp = exports2.base58flickr = exports2.base58 = exports2.base64url = exports2.base64 = exports2.base32crockford = exports2.base32hex = exports2.base32 = exports2.base16 = exports2.utils = exports2.assertNumber = void 0; function assertNumber2(n) { if (!Number.isSafeInteger(n)) throw new Error(`Wrong integer: ${n}`); } exports2.assertNumber = assertNumber2; function chain22(...args) { const wrap = (a, b) => (c) => a(b(c)); const encode4 = Array.from(args).reverse().reduce((acc, i22) => acc ? wrap(acc, i22.encode) : i22.encode, void 0); const decode23 = args.reduce((acc, i22) => acc ? wrap(acc, i22.decode) : i22.decode, void 0); return { encode: encode4, decode: decode23 }; } function alphabet22(alphabet3) { return { encode: (digits) => { if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number") throw new Error("alphabet.encode input should be an array of numbers"); return digits.map((i22) => { assertNumber2(i22); if (i22 < 0 || i22 >= alphabet3.length) throw new Error(`Digit index outside alphabet: ${i22} (alphabet: ${alphabet3.length})`); return alphabet3[i22]; }); }, decode: (input) => { if (!Array.isArray(input) || input.length && typeof input[0] !== "string") throw new Error("alphabet.decode input should be array of strings"); return input.map((letter) => { if (typeof letter !== "string") throw new Error(`alphabet.decode: not string element=${letter}`); const index = alphabet3.indexOf(letter); if (index === -1) throw new Error(`Unknown letter: "${letter}". Allowed: ${alphabet3}`); return index; }); } }; } function join22(separator = "") { if (typeof separator !== "string") throw new Error("join separator should be string"); return { encode: (from) => { if (!Array.isArray(from) || from.length && typeof from[0] !== "string") throw new Error("join.encode input should be array of strings"); for (let i22 of from) if (typeof i22 !== "string") throw new Error(`join.encode: non-string input=${i22}`); return from.join(separator); }, decode: (to) => { if (typeof to !== "string") throw new Error("join.decode input should be string"); return to.split(separator); } }; } function padding2(bits, chr = "=") { assertNumber2(bits); if (typeof chr !== "string") throw new Error("padding chr should be string"); return { encode(data) { if (!Array.isArray(data) || data.length && typeof data[0] !== "string") throw new Error("padding.encode input should be array of strings"); for (let i22 of data) if (typeof i22 !== "string") throw new Error(`padding.encode: non-string input=${i22}`); while (data.length * bits % 8) data.push(chr); return data; }, decode(input) { if (!Array.isArray(input) || input.length && typeof input[0] !== "string") throw new Error("padding.encode input should be array of strings"); for (let i22 of input) if (typeof i22 !== "string") throw new Error(`padding.decode: non-string input=${i22}`); let end = input.length; if (end * bits % 8) throw new Error("Invalid padding: string should have whole number of bytes"); for (; end > 0 && input[end - 1] === chr; end--) { if (!((end - 1) * bits % 8)) throw new Error("Invalid padding: string has too much padding"); } return input.slice(0, end); } }; } function normalize23(fn) { if (typeof fn !== "function") throw new Error("normalize fn should be function"); return { encode: (from) => from, decode: (to) => fn(to) }; } function convertRadix32(data, from, to) { if (from < 2) throw new Error(`convertRadix: wrong from=${from}, base cannot be less than 2`); if (to < 2) throw new Error(`convertRadix: wrong to=${to}, base cannot be less than 2`); if (!Array.isArray(data)) throw new Error("convertRadix: data should be array"); if (!data.length) return []; let pos = 0; const res = []; const digits = Array.from(data); digits.forEach((d17) => { assertNumber2(d17); if (d17 < 0 || d17 >= from) throw new Error(`Wrong integer: ${d17}`); }); while (true) { let carry = 0; let done = true; for (let i22 = pos; i22 < digits.length; i22++) { const digit = digits[i22]; const digitBase = from * carry + digit; if (!Number.isSafeInteger(digitBase) || from * carry / from !== carry || digitBase - digit !== from * carry) { throw new Error("convertRadix: carry overflow"); } carry = digitBase % to; digits[i22] = Math.floor(digitBase / to); if (!Number.isSafeInteger(digits[i22]) || digits[i22] * to + carry !== digitBase) throw new Error("convertRadix: carry overflow"); if (!done) continue; else if (!digits[i22]) pos = i22; else done = false; } res.push(carry); if (done) break; } for (let i22 = 0; i22 < data.length - 1 && data[i22] === 0; i22++) res.push(0); return res.reverse(); } var gcd22 = (a, b) => !b ? a : gcd22(b, a % b); var radix2carry22 = (from, to) => from + (to - gcd22(from, to)); function convertRadix222(data, from, to, padding3) { if (!Array.isArray(data)) throw new Error("convertRadix2: data should be array"); if (from <= 0 || from > 32) throw new Error(`convertRadix2: wrong from=${from}`); if (to <= 0 || to > 32) throw new Error(`convertRadix2: wrong to=${to}`); if (radix2carry22(from, to) > 32) { throw new Error(`convertRadix2: carry overflow from=${from} to=${to} carryBits=${radix2carry22(from, to)}`); } let carry = 0; let pos = 0; const mask = 2 ** to - 1; const res = []; for (const n of data) { assertNumber2(n); if (n >= 2 ** from) throw new Error(`convertRadix2: invalid data word=${n} from=${from}`); carry = carry << from | n; if (pos + from > 32) throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from}`); pos += from; for (; pos >= to; pos -= to) res.push((carry >> pos - to & mask) >>> 0); carry &= 2 ** pos - 1; } carry = carry << to - pos & mask; if (!padding3 && pos >= from) throw new Error("Excess padding"); if (!padding3 && carry) throw new Error(`Non-zero padding: ${carry}`); if (padding3 && pos > 0) res.push(carry >>> 0); return res; } function radix32(num22) { assertNumber2(num22); return { encode: (bytes4) => { if (!(bytes4 instanceof Uint8Array)) throw new Error("radix.encode input should be Uint8Array"); return convertRadix32(Array.from(bytes4), 2 ** 8, num22); }, decode: (digits) => { if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number") throw new Error("radix.decode input should be array of strings"); return Uint8Array.from(convertRadix32(digits, num22, 2 ** 8)); } }; } function radix222(bits, revPadding = false) { assertNumber2(bits); if (bits <= 0 || bits > 32) throw new Error("radix2: bits should be in (0..32]"); if (radix2carry22(8, bits) > 32 || radix2carry22(bits, 8) > 32) throw new Error("radix2: carry overflow"); return { encode: (bytes4) => { if (!(bytes4 instanceof Uint8Array)) throw new Error("radix2.encode input should be Uint8Array"); return convertRadix222(Array.from(bytes4), 8, bits, !revPadding); }, decode: (digits) => { if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number") throw new Error("radix2.decode input should be array of strings"); return Uint8Array.from(convertRadix222(digits, bits, 8, revPadding)); } }; } function unsafeWrapper22(fn) { if (typeof fn !== "function") throw new Error("unsafeWrapper fn should be function"); return function(...args) { try { return fn.apply(null, args); } catch (e2) { } }; } function checksum2(len, fn) { assertNumber2(len); if (typeof fn !== "function") throw new Error("checksum fn should be function"); return { encode(data) { if (!(data instanceof Uint8Array)) throw new Error("checksum.encode: input should be Uint8Array"); const checksum22 = fn(data).slice(0, len); const res = new Uint8Array(data.length + len); res.set(data); res.set(checksum22, data.length); return res; }, decode(data) { if (!(data instanceof Uint8Array)) throw new Error("checksum.decode: input should be Uint8Array"); const payload = data.slice(0, -len); const newChecksum = fn(payload).slice(0, len); const oldChecksum = data.slice(-len); for (let i22 = 0; i22 < len; i22++) if (newChecksum[i22] !== oldChecksum[i22]) throw new Error("Invalid checksum"); return payload; } }; } exports2.utils = { alphabet: alphabet22, chain: chain22, checksum: checksum2, radix: radix32, radix2: radix222, join: join22, padding: padding2 }; exports2.base16 = chain22(radix222(4), alphabet22("0123456789ABCDEF"), join22("")); exports2.base32 = chain22(radix222(5), alphabet22("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding2(5), join22("")); exports2.base32hex = chain22(radix222(5), alphabet22("0123456789ABCDEFGHIJKLMNOPQRSTUV"), padding2(5), join22("")); exports2.base32crockford = chain22(radix222(5), alphabet22("0123456789ABCDEFGHJKMNPQRSTVWXYZ"), join22(""), normalize23((s) => s.toUpperCase().replace(/O/g, "0").replace(/[IL]/g, "1"))); exports2.base64 = chain22(radix222(6), alphabet22("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), padding2(6), join22("")); exports2.base64url = chain22(radix222(6), alphabet22("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), padding2(6), join22("")); var genBase5822 = (abc) => chain22(radix32(58), alphabet22(abc), join22("")); exports2.base58 = genBase5822("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"); exports2.base58flickr = genBase5822("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"); exports2.base58xrp = genBase5822("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"); var XMR_BLOCK_LEN2 = [0, 2, 3, 5, 6, 7, 9, 10, 11]; exports2.base58xmr = { encode(data) { let res = ""; for (let i22 = 0; i22 < data.length; i22 += 8) { const block = data.subarray(i22, i22 + 8); res += exports2.base58.encode(block).padStart(XMR_BLOCK_LEN2[block.length], "1"); } return res; }, decode(str) { let res = []; for (let i22 = 0; i22 < str.length; i22 += 11) { const slice = str.slice(i22, i22 + 11); const blockLen = XMR_BLOCK_LEN2.indexOf(slice.length); const block = exports2.base58.decode(slice); for (let j2 = 0; j2 < block.length - blockLen; j2++) { if (block[j2] !== 0) throw new Error("base58xmr: wrong padding"); } res = res.concat(Array.from(block.slice(block.length - blockLen))); } return Uint8Array.from(res); } }; var base58check2 = (sha2565) => chain22(checksum2(4, (data) => sha2565(sha2565(data))), exports2.base58); exports2.base58check = base58check2; var BECH_ALPHABET22 = chain22(alphabet22("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), join22("")); var POLYMOD_GENERATORS22 = [996825010, 642813549, 513874426, 1027748829, 705979059]; function bech32Polymod22(pre) { const b = pre >> 25; let chk = (pre & 33554431) << 5; for (let i22 = 0; i22 < POLYMOD_GENERATORS22.length; i22++) { if ((b >> i22 & 1) === 1) chk ^= POLYMOD_GENERATORS22[i22]; } return chk; } function bechChecksum22(prefix, words, encodingConst = 1) { const len = prefix.length; let chk = 1; for (let i22 = 0; i22 < len; i22++) { const c = prefix.charCodeAt(i22); if (c < 33 || c > 126) throw new Error(`Invalid prefix (${prefix})`); chk = bech32Polymod22(chk) ^ c >> 5; } chk = bech32Polymod22(chk); for (let i22 = 0; i22 < len; i22++) chk = bech32Polymod22(chk) ^ prefix.charCodeAt(i22) & 31; for (let v6 of words) chk = bech32Polymod22(chk) ^ v6; for (let i22 = 0; i22 < 6; i22++) chk = bech32Polymod22(chk); chk ^= encodingConst; return BECH_ALPHABET22.encode(convertRadix222([chk % 2 ** 30], 30, 5, false)); } function genBech3222(encoding) { const ENCODING_CONST = encoding === "bech32" ? 1 : 734539939; const _words = radix222(5); const fromWords = _words.decode; const toWords = _words.encode; const fromWordsUnsafe = unsafeWrapper22(fromWords); function encode4(prefix, words, limit2 = 90) { if (typeof prefix !== "string") throw new Error(`bech32.encode prefix should be string, not ${typeof prefix}`); if (!Array.isArray(words) || words.length && typeof words[0] !== "number") throw new Error(`bech32.encode words should be array of numbers, not ${typeof words}`); const actualLength = prefix.length + 7 + words.length; if (limit2 !== false && actualLength > limit2) throw new TypeError(`Length ${actualLength} exceeds limit ${limit2}`); prefix = prefix.toLowerCase(); return `${prefix}1${BECH_ALPHABET22.encode(words)}${bechChecksum22(prefix, words, ENCODING_CONST)}`; } function decode23(str, limit2 = 90) { if (typeof str !== "string") throw new Error(`bech32.decode input should be string, not ${typeof str}`); if (str.length < 8 || limit2 !== false && str.length > limit2) throw new TypeError(`Wrong string length: ${str.length} (${str}). Expected (8..${limit2})`); const lowered = str.toLowerCase(); if (str !== lowered && str !== str.toUpperCase()) throw new Error(`String must be lowercase or uppercase`); str = lowered; const sepIndex = str.lastIndexOf("1"); if (sepIndex === 0 || sepIndex === -1) throw new Error(`Letter "1" must be present between prefix and data only`); const prefix = str.slice(0, sepIndex); const _words2 = str.slice(sepIndex + 1); if (_words2.length < 6) throw new Error("Data must be at least 6 characters long"); const words = BECH_ALPHABET22.decode(_words2).slice(0, -6); const sum = bechChecksum22(prefix, words, ENCODING_CONST); if (!_words2.endsWith(sum)) throw new Error(`Invalid checksum in ${str}: expected "${sum}"`); return { prefix, words }; } const decodeUnsafe = unsafeWrapper22(decode23); function decodeToBytes(str) { const { prefix, words } = decode23(str, false); return { prefix, words, bytes: fromWords(words) }; } return { encode: encode4, decode: decode23, decodeToBytes, decodeUnsafe, fromWords, fromWordsUnsafe, toWords }; } exports2.bech32 = genBech3222("bech32"); exports2.bech32m = genBech3222("bech32m"); exports2.utf8 = { encode: (data) => new TextDecoder().decode(data), decode: (str) => new TextEncoder().encode(str) }; exports2.hex = chain22(radix222(4), alphabet22("0123456789abcdef"), join22(""), normalize23((s) => { if (typeof s !== "string" || s.length % 2) throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`); return s.toLowerCase(); })); var CODERS2 = { utf8: exports2.utf8, hex: exports2.hex, base16: exports2.base16, base32: exports2.base32, base64: exports2.base64, base64url: exports2.base64url, base58: exports2.base58, base58xmr: exports2.base58xmr }; var coderTypeError2 = `Invalid encoding type. Available types: ${Object.keys(CODERS2).join(", ")}`; var bytesToString = (type, bytes4) => { if (typeof type !== "string" || !CODERS2.hasOwnProperty(type)) throw new TypeError(coderTypeError2); if (!(bytes4 instanceof Uint8Array)) throw new TypeError("bytesToString() expects Uint8Array"); return CODERS2[type].encode(bytes4); }; exports2.bytesToString = bytesToString; exports2.str = exports2.bytesToString; var stringToBytes = (type, str) => { if (!CODERS2.hasOwnProperty(type)) throw new TypeError(coderTypeError2); if (typeof str !== "string") throw new TypeError("stringToBytes() expects string"); return CODERS2[type].decode(str); }; exports2.stringToBytes = stringToBytes; exports2.bytes = exports2.stringToBytes; }); var require_bolt112 = __commonJS2((exports2, module2) => { var { bech32: bech3222, hex: hex2, utf8: utf82 } = require_lib22(); var DEFAULTNETWORK = { bech32: "bc", pubKeyHash: 0, scriptHash: 5, validWitnessVersions: [0] }; var TESTNETWORK = { bech32: "tb", pubKeyHash: 111, scriptHash: 196, validWitnessVersions: [0] }; var SIGNETNETWORK = { bech32: "tbs", pubKeyHash: 111, scriptHash: 196, validWitnessVersions: [0] }; var REGTESTNETWORK = { bech32: "bcrt", pubKeyHash: 111, scriptHash: 196, validWitnessVersions: [0] }; var SIMNETWORK = { bech32: "sb", pubKeyHash: 63, scriptHash: 123, validWitnessVersions: [0] }; var FEATUREBIT_ORDER = [ "option_data_loss_protect", "initial_routing_sync", "option_upfront_shutdown_script", "gossip_queries", "var_onion_optin", "gossip_queries_ex", "option_static_remotekey", "payment_secret", "basic_mpp", "option_support_large_channel" ]; var DIVISORS = { m: BigInt(1e3), u: BigInt(1e6), n: BigInt(1e9), p: BigInt(1e12) }; var MAX_MILLISATS = BigInt("2100000000000000000"); var MILLISATS_PER_BTC = BigInt(1e11); var TAGCODES = { payment_hash: 1, payment_secret: 16, description: 13, payee: 19, description_hash: 23, expiry: 6, min_final_cltv_expiry: 24, fallback_address: 9, route_hint: 3, feature_bits: 5, metadata: 27 }; var TAGNAMES = {}; for (let i22 = 0, keys = Object.keys(TAGCODES); i22 < keys.length; i22++) { const currentName = keys[i22]; const currentCode = TAGCODES[keys[i22]].toString(); TAGNAMES[currentCode] = currentName; } var TAGPARSERS = { 1: (words) => hex2.encode(bech3222.fromWordsUnsafe(words)), 16: (words) => hex2.encode(bech3222.fromWordsUnsafe(words)), 13: (words) => utf82.encode(bech3222.fromWordsUnsafe(words)), 19: (words) => hex2.encode(bech3222.fromWordsUnsafe(words)), 23: (words) => hex2.encode(bech3222.fromWordsUnsafe(words)), 27: (words) => hex2.encode(bech3222.fromWordsUnsafe(words)), 6: wordsToIntBE, 24: wordsToIntBE, 3: routingInfoParser, 5: featureBitsParser }; function getUnknownParser(tagCode) { return (words) => ({ tagCode: parseInt(tagCode), words: bech3222.encode("unknown", words, Number.MAX_SAFE_INTEGER) }); } function wordsToIntBE(words) { return words.reverse().reduce((total, item, index) => { return total + item * Math.pow(32, index); }, 0); } function routingInfoParser(words) { const routes = []; let pubkey, shortChannelId, feeBaseMSats, feeProportionalMillionths, cltvExpiryDelta; let routesBuffer = bech3222.fromWordsUnsafe(words); while (routesBuffer.length > 0) { pubkey = hex2.encode(routesBuffer.slice(0, 33)); shortChannelId = hex2.encode(routesBuffer.slice(33, 41)); feeBaseMSats = parseInt(hex2.encode(routesBuffer.slice(41, 45)), 16); feeProportionalMillionths = parseInt(hex2.encode(routesBuffer.slice(45, 49)), 16); cltvExpiryDelta = parseInt(hex2.encode(routesBuffer.slice(49, 51)), 16); routesBuffer = routesBuffer.slice(51); routes.push({ pubkey, short_channel_id: shortChannelId, fee_base_msat: feeBaseMSats, fee_proportional_millionths: feeProportionalMillionths, cltv_expiry_delta: cltvExpiryDelta }); } return routes; } function featureBitsParser(words) { const bools = words.slice().reverse().map((word) => [ !!(word & 1), !!(word & 2), !!(word & 4), !!(word & 8), !!(word & 16) ]).reduce((finalArr, itemArr) => finalArr.concat(itemArr), []); while (bools.length < FEATUREBIT_ORDER.length * 2) { bools.push(false); } const featureBits = {}; FEATUREBIT_ORDER.forEach((featureName, index) => { let status; if (bools[index * 2]) { status = "required"; } else if (bools[index * 2 + 1]) { status = "supported"; } else { status = "unsupported"; } featureBits[featureName] = status; }); const extraBits = bools.slice(FEATUREBIT_ORDER.length * 2); featureBits.extra_bits = { start_bit: FEATUREBIT_ORDER.length * 2, bits: extraBits, has_required: extraBits.reduce((result, bit, index) => index % 2 !== 0 ? result || false : result || bit, false) }; return featureBits; } function hrpToMillisat(hrpString, outputString) { let divisor, value; if (hrpString.slice(-1).match(/^[munp]$/)) { divisor = hrpString.slice(-1); value = hrpString.slice(0, -1); } else if (hrpString.slice(-1).match(/^[^munp0-9]$/)) { throw new Error("Not a valid multiplier for the amount"); } else { value = hrpString; } if (!value.match(/^\d+$/)) throw new Error("Not a valid human readable amount"); const valueBN = BigInt(value); const millisatoshisBN = divisor ? valueBN * MILLISATS_PER_BTC / DIVISORS[divisor] : valueBN * MILLISATS_PER_BTC; if (divisor === "p" && !(valueBN % BigInt(10) === BigInt(0)) || millisatoshisBN > MAX_MILLISATS) { throw new Error("Amount is outside of valid range"); } return outputString ? millisatoshisBN.toString() : millisatoshisBN; } function decode23(paymentRequest, network) { if (typeof paymentRequest !== "string") throw new Error("Lightning Payment Request must be string"); if (paymentRequest.slice(0, 2).toLowerCase() !== "ln") throw new Error("Not a proper lightning payment request"); const sections = []; const decoded = bech3222.decode(paymentRequest, Number.MAX_SAFE_INTEGER); paymentRequest = paymentRequest.toLowerCase(); const prefix = decoded.prefix; let words = decoded.words; let letters = paymentRequest.slice(prefix.length + 1); let sigWords = words.slice(-104); words = words.slice(0, -104); let prefixMatches = prefix.match(/^ln(\S+?)(\d*)([a-zA-Z]?)$/); if (prefixMatches && !prefixMatches[2]) prefixMatches = prefix.match(/^ln(\S+)$/); if (!prefixMatches) { throw new Error("Not a proper lightning payment request"); } sections.push({ name: "lightning_network", letters: "ln" }); const bech32Prefix = prefixMatches[1]; let coinNetwork; if (!network) { switch (bech32Prefix) { case DEFAULTNETWORK.bech32: coinNetwork = DEFAULTNETWORK; break; case TESTNETWORK.bech32: coinNetwork = TESTNETWORK; break; case SIGNETNETWORK.bech32: coinNetwork = SIGNETNETWORK; break; case REGTESTNETWORK.bech32: coinNetwork = REGTESTNETWORK; break; case SIMNETWORK.bech32: coinNetwork = SIMNETWORK; break; } } else { if (network.bech32 === void 0 || network.pubKeyHash === void 0 || network.scriptHash === void 0 || !Array.isArray(network.validWitnessVersions)) throw new Error("Invalid network"); coinNetwork = network; } if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix) { throw new Error("Unknown coin bech32 prefix"); } sections.push({ name: "coin_network", letters: bech32Prefix, value: coinNetwork }); const value = prefixMatches[2]; let millisatoshis; if (value) { const divisor = prefixMatches[3]; millisatoshis = hrpToMillisat(value + divisor, true); sections.push({ name: "amount", letters: prefixMatches[2] + prefixMatches[3], value: millisatoshis }); } else { millisatoshis = null; } sections.push({ name: "separator", letters: "1" }); const timestamp = wordsToIntBE(words.slice(0, 7)); words = words.slice(7); sections.push({ name: "timestamp", letters: letters.slice(0, 7), value: timestamp }); letters = letters.slice(7); let tagName, parser, tagLength, tagWords; while (words.length > 0) { const tagCode = words[0].toString(); tagName = TAGNAMES[tagCode] || "unknown_tag"; parser = TAGPARSERS[tagCode] || getUnknownParser(tagCode); words = words.slice(1); tagLength = wordsToIntBE(words.slice(0, 2)); words = words.slice(2); tagWords = words.slice(0, tagLength); words = words.slice(tagLength); sections.push({ name: tagName, tag: letters[0], letters: letters.slice(0, 1 + 2 + tagLength), value: parser(tagWords) }); letters = letters.slice(1 + 2 + tagLength); } sections.push({ name: "signature", letters: letters.slice(0, 104), value: hex2.encode(bech3222.fromWordsUnsafe(sigWords)) }); letters = letters.slice(104); sections.push({ name: "checksum", letters }); let result = { paymentRequest, sections, get expiry() { let exp = sections.find((s) => s.name === "expiry"); if (exp) return getValue("timestamp") + exp.value; }, get route_hints() { return sections.filter((s) => s.name === "route_hint").map((s) => s.value); } }; for (let name in TAGCODES) { if (name === "route_hint") { continue; } Object.defineProperty(result, name, { get() { return getValue(name); } }); } return result; function getValue(name) { let section = sections.find((s) => s.name === name); return section ? section.value : void 0; } } module2.exports = { decode: decode23, hrpToMillisat }; }); var exports_decoder = {}; __export2(exports_decoder, { looksLikeBinaryFormat: () => looksLikeBinaryFormat, decodeSingleEvent: () => decodeSingleEvent, decodeEvents: () => decodeEvents }); function bytesToHex5(bytes4) { let hex2 = ""; for (let i22 = 0; i22 < bytes4.length; i22++) { const byte = bytes4[i22]; hex2 += HEX_CHARS[byte >> 4] + HEX_CHARS[byte & 15]; } return hex2; } function decodeString(bytes4) { return textDecoder.decode(bytes4); } function decodeEvent(buffer, offset) { const view = new DataView(buffer); const uint8 = new Uint8Array(buffer); let pos = offset; if (pos + 4 > buffer.byteLength) { throw new Error(`Buffer overflow: trying to read event size at offset ${pos}, buffer length is ${buffer.byteLength}`); } const eventSize = view.getUint32(pos, true); pos += 4; const eventEndPos = offset + eventSize; if (eventEndPos > buffer.byteLength) { throw new Error(`Invalid event size: event claims to be ${eventSize} bytes but only ${buffer.byteLength - offset} bytes available`); } const idBytes = uint8.slice(pos, pos + 32); const id = bytesToHex5(idBytes); pos += 32; const pubkeyBytes = uint8.slice(pos, pos + 32); const pubkey = bytesToHex5(pubkeyBytes); pos += 32; const created_at = view.getUint32(pos, true); pos += 4; const kind = view.getUint16(pos, true); pos += 2; const sigBytes = uint8.slice(pos, pos + 64); const sig = bytesToHex5(sigBytes); pos += 64; const contentLength = view.getUint32(pos, true); pos += 4; const contentBytes = uint8.slice(pos, pos + contentLength); const content = decodeString(contentBytes); pos += contentLength; const tagsCount = view.getUint16(pos, true); pos += 2; const tags = []; for (let i22 = 0; i22 < tagsCount; i22++) { const tagItemsCount = view.getUint8(pos); pos += 1; const tag = []; for (let j2 = 0; j2 < tagItemsCount; j2++) { const itemLength = view.getUint16(pos, true); pos += 2; const itemBytes = uint8.slice(pos, pos + itemLength); const item = decodeString(itemBytes); tag.push(item); pos += itemLength; } tags.push(tag); } let relay_url = null; const hasRelayUrl = view.getUint8(pos); pos += 1; if (hasRelayUrl === 1) { const relayLength = view.getUint16(pos, true); pos += 2; const relayBytes = uint8.slice(pos, pos + relayLength); relay_url = decodeString(relayBytes); pos += relayLength; } const event = { id, pubkey, created_at, kind, sig, content, tags, relay_url }; return { event, nextOffset: eventEndPos }; } function decodeEvents(buffer) { if (buffer.byteLength === 0) { return []; } if (buffer.byteLength < 9) { throw new Error(`Buffer too small: ${buffer.byteLength} bytes. Need at least 9 bytes for header.`); } const view = new DataView(buffer); let offset = 0; const magic = view.getUint32(offset, true); if (magic !== MAGIC_NUMBER) { throw new Error(`Invalid magic number. Expected ${MAGIC_NUMBER.toString(16)}, got ${magic.toString(16)}`); } offset += 4; const version = view.getUint8(offset); if (version !== SUPPORTED_VERSION) { throw new Error(`Unsupported version ${version}. Only version ${SUPPORTED_VERSION} is supported`); } offset += 1; const eventCount = view.getUint32(offset, true); offset += 4; const events = []; for (let i22 = 0; i22 < eventCount; i22++) { const { event, nextOffset } = decodeEvent(buffer, offset); events.push(event); offset = nextOffset; } return events; } function decodeSingleEvent(buffer) { const events = decodeEvents(buffer); if (events.length !== 1) { throw new Error(`Expected 1 event, got ${events.length}`); } return events[0]; } function looksLikeBinaryFormat(buffer) { if (buffer.byteLength < 9) return false; const view = new DataView(buffer); const magic = view.getUint32(0, true); return magic === MAGIC_NUMBER; } var MAGIC_NUMBER = 1313821524; var SUPPORTED_VERSION = 1; var textDecoder; var HEX_CHARS = "0123456789abcdef"; var init_decoder = __esm2(() => { textDecoder = new TextDecoder(); }); async function addDecryptedEvent(wrapperId, decryptedEvent) { await this.ensureInitialized(); const serialized = decryptedEvent.serialize(true, true); await this.postWorkerMessage({ type: "addDecryptedEvent", payload: { wrapperId, serialized } }); } async function addUnpublishedEvent2(event, relayUrls, lastTryAt = Date.now()) { await this.ensureInitialized(); await this.postWorkerMessage({ type: "addUnpublishedEvent", payload: { id: event.id, event: event.serialize(true, true), relays: JSON.stringify(relayUrls) } }); } async function discardUnpublishedEvent2(eventId) { await this.ensureInitialized(); await this.postWorkerMessage({ type: "discardUnpublishedEvent", payload: { id: eventId } }); } async function fetchProfile(pubkey) { await this.ensureInitialized(); const cached = this.metadataCache?.getProfile(pubkey); if (cached) { return cached; } if (this.degradedMode) return null; const result = await this.postWorkerMessage({ type: "fetchProfile", payload: { pubkey } }); if (result && result.profile) { try { const profile = JSON.parse(result.profile); const entry = { ...profile, cachedAt: result.updated_at }; this.metadataCache?.setProfile(pubkey, entry); return entry; } catch { return null; } } return null; } async function getCacheStats() { await this.ensureInitialized(); return this.postWorkerMessage({ type: "getCacheStats" }); } var import_tseep19 = __toESM2(require_lib3(), 1); var import_debug28 = __toESM2(require_browser2(), 1); var import_debug29 = __toESM2(require_browser2(), 1); var import_tseep22 = __toESM2(require_lib3(), 1); var import_debug32 = __toESM2(require_browser2(), 1); function number(n) { if (!Number.isSafeInteger(n) || n < 0) throw new Error(`Wrong positive integer: ${n}`); } function bytes(b, ...lengths) { if (!(b instanceof Uint8Array)) throw new Error("Expected Uint8Array"); if (lengths.length > 0 && !lengths.includes(b.length)) throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`); } function hash(hash22) { if (typeof hash22 !== "function" || typeof hash22.create !== "function") throw new Error("Hash should be wrapped by utils.wrapConstructor"); number(hash22.outputLen); number(hash22.blockLen); } function exists(instance, checkFinished = true) { if (instance.destroyed) throw new Error("Hash instance has been destroyed"); if (checkFinished && instance.finished) throw new Error("Hash#digest() has already been called"); } function output(out, instance) { bytes(out); const min = instance.outputLen; if (out.length < min) { throw new Error(`digestInto() expects output buffer of length at least ${min}`); } } var crypto3 = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : void 0; var u8a = (a) => a instanceof Uint8Array; var createView2 = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength); var rotr2 = (word, shift) => word << 32 - shift | word >>> shift; var isLE = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68; if (!isLE) throw new Error("Non little-endian hardware is not supported"); function utf8ToBytes2(str) { if (typeof str !== "string") throw new Error(`utf8ToBytes expected string, got ${typeof str}`); return new Uint8Array(new TextEncoder().encode(str)); } function toBytes2(data) { if (typeof data === "string") data = utf8ToBytes2(data); if (!u8a(data)) throw new Error(`expected Uint8Array, got ${typeof data}`); return data; } function concatBytes2(...arrays) { const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0)); let pad2 = 0; arrays.forEach((a) => { if (!u8a(a)) throw new Error("Uint8Array expected"); r.set(a, pad2); pad2 += a.length; }); return r; } var Hash2 = class { clone() { return this._cloneInto(); } }; var toStr = {}.toString; function wrapConstructor(hashCons) { const hashC = (msg) => hashCons().update(toBytes2(msg)).digest(); const tmp = hashCons(); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.create = () => hashCons(); return hashC; } function randomBytes2(bytesLength = 32) { if (crypto3 && typeof crypto3.getRandomValues === "function") { return crypto3.getRandomValues(new Uint8Array(bytesLength)); } throw new Error("crypto.getRandomValues must be defined"); } function setBigUint642(view, byteOffset, value, isLE22) { if (typeof view.setBigUint64 === "function") return view.setBigUint64(byteOffset, value, isLE22); const _32n2 = BigInt(32); const _u32_max = BigInt(4294967295); const wh = Number(value >> _32n2 & _u32_max); const wl = Number(value & _u32_max); const h2 = isLE22 ? 4 : 0; const l3 = isLE22 ? 0 : 4; view.setUint32(byteOffset + h2, wh, isLE22); view.setUint32(byteOffset + l3, wl, isLE22); } var SHA2 = class extends Hash2 { constructor(blockLen, outputLen, padOffset, isLE22) { super(); this.blockLen = blockLen; this.outputLen = outputLen; this.padOffset = padOffset; this.isLE = isLE22; this.finished = false; this.length = 0; this.pos = 0; this.destroyed = false; this.buffer = new Uint8Array(blockLen); this.view = createView2(this.buffer); } update(data) { exists(this); const { view, buffer, blockLen } = this; data = toBytes2(data); const len = data.length; for (let pos = 0; pos < len; ) { const take = Math.min(blockLen - this.pos, len - pos); if (take === blockLen) { const dataView = createView2(data); for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos); continue; } buffer.set(data.subarray(pos, pos + take), this.pos); this.pos += take; pos += take; if (this.pos === blockLen) { this.process(view, 0); this.pos = 0; } } this.length += data.length; this.roundClean(); return this; } digestInto(out) { exists(this); output(out, this); this.finished = true; const { buffer, view, blockLen, isLE: isLE22 } = this; let { pos } = this; buffer[pos++] = 128; this.buffer.subarray(pos).fill(0); if (this.padOffset > blockLen - pos) { this.process(view, 0); pos = 0; } for (let i3 = pos; i3 < blockLen; i3++) buffer[i3] = 0; setBigUint642(view, blockLen - 8, BigInt(this.length * 8), isLE22); this.process(view, 0); const oview = createView2(out); const len = this.outputLen; if (len % 4) throw new Error("_sha2: outputLen should be aligned to 32bit"); const outLen = len / 4; const state = this.get(); if (outLen > state.length) throw new Error("_sha2: outputLen bigger than state"); for (let i3 = 0; i3 < outLen; i3++) oview.setUint32(4 * i3, state[i3], isLE22); } digest() { const { buffer, outputLen } = this; this.digestInto(buffer); const res = buffer.slice(0, outputLen); this.destroy(); return res; } _cloneInto(to) { to || (to = new this.constructor()); to.set(...this.get()); const { blockLen, buffer, length, finished, destroyed, pos } = this; to.length = length; to.pos = pos; to.finished = finished; to.destroyed = destroyed; if (length % blockLen) to.buffer.set(buffer); return to; } }; var Chi2 = (a, b, c) => a & b ^ ~a & c; var Maj2 = (a, b, c) => a & b ^ a & c ^ b & c; var SHA256_K2 = /* @__PURE__ */ new Uint32Array([ 1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298 ]); var IV = /* @__PURE__ */ new Uint32Array([ 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225 ]); var SHA256_W2 = /* @__PURE__ */ new Uint32Array(64); var SHA2562 = class extends SHA2 { constructor() { super(64, 32, 8, false); this.A = IV[0] | 0; this.B = IV[1] | 0; this.C = IV[2] | 0; this.D = IV[3] | 0; this.E = IV[4] | 0; this.F = IV[5] | 0; this.G = IV[6] | 0; this.H = IV[7] | 0; } get() { const { A, B, C: C2, D: D3, E: E2, F: F2, G: G2, H: H2 } = this; return [A, B, C2, D3, E2, F2, G2, H2]; } set(A, B, C2, D3, E2, F2, G2, H2) { this.A = A | 0; this.B = B | 0; this.C = C2 | 0; this.D = D3 | 0; this.E = E2 | 0; this.F = F2 | 0; this.G = G2 | 0; this.H = H2 | 0; } process(view, offset) { for (let i3 = 0; i3 < 16; i3++, offset += 4) SHA256_W2[i3] = view.getUint32(offset, false); for (let i3 = 16; i3 < 64; i3++) { const W15 = SHA256_W2[i3 - 15]; const W22 = SHA256_W2[i3 - 2]; const s0 = rotr2(W15, 7) ^ rotr2(W15, 18) ^ W15 >>> 3; const s1 = rotr2(W22, 17) ^ rotr2(W22, 19) ^ W22 >>> 10; SHA256_W2[i3] = s1 + SHA256_W2[i3 - 7] + s0 + SHA256_W2[i3 - 16] | 0; } let { A, B, C: C2, D: D3, E: E2, F: F2, G: G2, H: H2 } = this; for (let i3 = 0; i3 < 64; i3++) { const sigma1 = rotr2(E2, 6) ^ rotr2(E2, 11) ^ rotr2(E2, 25); const T1 = H2 + sigma1 + Chi2(E2, F2, G2) + SHA256_K2[i3] + SHA256_W2[i3] | 0; const sigma0 = rotr2(A, 2) ^ rotr2(A, 13) ^ rotr2(A, 22); const T22 = sigma0 + Maj2(A, B, C2) | 0; H2 = G2; G2 = F2; F2 = E2; E2 = D3 + T1 | 0; D3 = C2; C2 = B; B = A; A = T1 + T22 | 0; } A = A + this.A | 0; B = B + this.B | 0; C2 = C2 + this.C | 0; D3 = D3 + this.D | 0; E2 = E2 + this.E | 0; F2 = F2 + this.F | 0; G2 = G2 + this.G | 0; H2 = H2 + this.H | 0; this.set(A, B, C2, D3, E2, F2, G2, H2); } roundClean() { SHA256_W2.fill(0); } destroy() { this.set(0, 0, 0, 0, 0, 0, 0, 0); this.buffer.fill(0); } }; var sha2563 = /* @__PURE__ */ wrapConstructor(() => new SHA2562()); var exports_utils = {}; __export2(exports_utils, { validateObject: () => validateObject2, utf8ToBytes: () => utf8ToBytes22, numberToVarBytesBE: () => numberToVarBytesBE, numberToHexUnpadded: () => numberToHexUnpadded2, numberToBytesLE: () => numberToBytesLE2, numberToBytesBE: () => numberToBytesBE2, hexToNumber: () => hexToNumber2, hexToBytes: () => hexToBytes2, equalBytes: () => equalBytes, ensureBytes: () => ensureBytes2, createHmacDrbg: () => createHmacDrbg2, concatBytes: () => concatBytes22, bytesToNumberLE: () => bytesToNumberLE2, bytesToNumberBE: () => bytesToNumberBE2, bytesToHex: () => bytesToHex2, bitSet: () => bitSet, bitMask: () => bitMask2, bitLen: () => bitLen2, bitGet: () => bitGet }); var _0n6 = BigInt(0); var _1n6 = BigInt(1); var _2n4 = BigInt(2); var u8a2 = (a) => a instanceof Uint8Array; var hexes2 = /* @__PURE__ */ Array.from({ length: 256 }, (_2, i3) => i3.toString(16).padStart(2, "0")); function bytesToHex2(bytes22) { if (!u8a2(bytes22)) throw new Error("Uint8Array expected"); let hex2 = ""; for (let i3 = 0; i3 < bytes22.length; i3++) { hex2 += hexes2[bytes22[i3]]; } return hex2; } function numberToHexUnpadded2(num3) { const hex2 = num3.toString(16); return hex2.length & 1 ? `0${hex2}` : hex2; } function hexToNumber2(hex2) { if (typeof hex2 !== "string") throw new Error("hex string expected, got " + typeof hex2); return BigInt(hex2 === "" ? "0" : `0x${hex2}`); } function hexToBytes2(hex2) { if (typeof hex2 !== "string") throw new Error("hex string expected, got " + typeof hex2); const len = hex2.length; if (len % 2) throw new Error("padded hex string expected, got unpadded hex of length " + len); const array = new Uint8Array(len / 2); for (let i3 = 0; i3 < array.length; i3++) { const j2 = i3 * 2; const hexByte = hex2.slice(j2, j2 + 2); const byte = Number.parseInt(hexByte, 16); if (Number.isNaN(byte) || byte < 0) throw new Error("Invalid byte sequence"); array[i3] = byte; } return array; } function bytesToNumberBE2(bytes22) { return hexToNumber2(bytesToHex2(bytes22)); } function bytesToNumberLE2(bytes22) { if (!u8a2(bytes22)) throw new Error("Uint8Array expected"); return hexToNumber2(bytesToHex2(Uint8Array.from(bytes22).reverse())); } function numberToBytesBE2(n, len) { return hexToBytes2(n.toString(16).padStart(len * 2, "0")); } function numberToBytesLE2(n, len) { return numberToBytesBE2(n, len).reverse(); } function numberToVarBytesBE(n) { return hexToBytes2(numberToHexUnpadded2(n)); } function ensureBytes2(title, hex2, expectedLength) { let res; if (typeof hex2 === "string") { try { res = hexToBytes2(hex2); } catch (e2) { throw new Error(`${title} must be valid hex string, got "${hex2}". Cause: ${e2}`); } } else if (u8a2(hex2)) { res = Uint8Array.from(hex2); } else { throw new Error(`${title} must be hex string or Uint8Array`); } const len = res.length; if (typeof expectedLength === "number" && len !== expectedLength) throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`); return res; } function concatBytes22(...arrays) { const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0)); let pad2 = 0; arrays.forEach((a) => { if (!u8a2(a)) throw new Error("Uint8Array expected"); r.set(a, pad2); pad2 += a.length; }); return r; } function equalBytes(b1, b2) { if (b1.length !== b2.length) return false; for (let i3 = 0; i3 < b1.length; i3++) if (b1[i3] !== b2[i3]) return false; return true; } function utf8ToBytes22(str) { if (typeof str !== "string") throw new Error(`utf8ToBytes expected string, got ${typeof str}`); return new Uint8Array(new TextEncoder().encode(str)); } function bitLen2(n) { let len; for (len = 0; n > _0n6; n >>= _1n6, len += 1) ; return len; } function bitGet(n, pos) { return n >> BigInt(pos) & _1n6; } var bitSet = (n, pos, value) => { return n | (value ? _1n6 : _0n6) << BigInt(pos); }; var bitMask2 = (n) => (_2n4 << BigInt(n - 1)) - _1n6; var u8n = (data) => new Uint8Array(data); var u8fr = (arr) => Uint8Array.from(arr); function createHmacDrbg2(hashLen, qByteLen, hmacFn) { if (typeof hashLen !== "number" || hashLen < 2) throw new Error("hashLen must be a number"); if (typeof qByteLen !== "number" || qByteLen < 2) throw new Error("qByteLen must be a number"); if (typeof hmacFn !== "function") throw new Error("hmacFn must be a function"); let v6 = u8n(hashLen); let k2 = u8n(hashLen); let i3 = 0; const reset = () => { v6.fill(1); k2.fill(0); i3 = 0; }; const h2 = (...b) => hmacFn(k2, v6, ...b); const reseed = (seed = u8n()) => { k2 = h2(u8fr([0]), seed); v6 = h2(); if (seed.length === 0) return; k2 = h2(u8fr([1]), seed); v6 = h2(); }; const gen = () => { if (i3++ >= 1e3) throw new Error("drbg: tried 1000 values"); let len = 0; const out = []; while (len < qByteLen) { v6 = h2(); const sl = v6.slice(); out.push(sl); len += v6.length; } return concatBytes22(...out); }; const genUntil = (seed, pred) => { reset(); reseed(seed); let res = void 0; while (!(res = pred(gen()))) reseed(); reset(); return res; }; return genUntil; } var validatorFns = { bigint: (val) => typeof val === "bigint", function: (val) => typeof val === "function", boolean: (val) => typeof val === "boolean", string: (val) => typeof val === "string", stringOrUint8Array: (val) => typeof val === "string" || val instanceof Uint8Array, isSafeInteger: (val) => Number.isSafeInteger(val), array: (val) => Array.isArray(val), field: (val, object) => object.Fp.isValid(val), hash: (val) => typeof val === "function" && Number.isSafeInteger(val.outputLen) }; function validateObject2(object, validators, optValidators = {}) { const checkField = (fieldName, type, isOptional) => { const checkVal = validatorFns[type]; if (typeof checkVal !== "function") throw new Error(`Invalid validator "${type}", expected function`); const val = object[fieldName]; if (isOptional && val === void 0) return; if (!checkVal(val, object)) { throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`); } }; for (const [fieldName, type] of Object.entries(validators)) checkField(fieldName, type, false); for (const [fieldName, type] of Object.entries(optValidators)) checkField(fieldName, type, true); return object; } var _0n22 = BigInt(0); var _1n22 = BigInt(1); var _2n22 = BigInt(2); var _3n3 = BigInt(3); var _4n3 = BigInt(4); var _5n2 = BigInt(5); var _8n2 = BigInt(8); var _9n2 = BigInt(9); var _16n2 = BigInt(16); function mod2(a, b) { const result = a % b; return result >= _0n22 ? result : b + result; } function pow(num3, power, modulo) { if (modulo <= _0n22 || power < _0n22) throw new Error("Expected power/modulo > 0"); if (modulo === _1n22) return _0n22; let res = _1n22; while (power > _0n22) { if (power & _1n22) res = res * num3 % modulo; num3 = num3 * num3 % modulo; power >>= _1n22; } return res; } function pow22(x2, power, modulo) { let res = x2; while (power-- > _0n22) { res *= res; res %= modulo; } return res; } function invert2(number22, modulo) { if (number22 === _0n22 || modulo <= _0n22) { throw new Error(`invert: expected positive integers, got n=${number22} mod=${modulo}`); } let a = mod2(number22, modulo); let b = modulo; let x2 = _0n22, y2 = _1n22, u3 = _1n22, v6 = _0n22; while (a !== _0n22) { const q2 = b / a; const r = b % a; const m = x2 - u3 * q2; const n = y2 - v6 * q2; b = a, a = r, x2 = u3, y2 = v6, u3 = m, v6 = n; } const gcd3 = b; if (gcd3 !== _1n22) throw new Error("invert: does not exist"); return mod2(x2, modulo); } function tonelliShanks2(P) { const legendreC = (P - _1n22) / _2n22; let Q2, S4, Z; for (Q2 = P - _1n22, S4 = 0; Q2 % _2n22 === _0n22; Q2 /= _2n22, S4++) ; for (Z = _2n22; Z < P && pow(Z, legendreC, P) !== P - _1n22; Z++) ; if (S4 === 1) { const p1div4 = (P + _1n22) / _4n3; return function tonelliFast(Fp2, n) { const root = Fp2.pow(n, p1div4); if (!Fp2.eql(Fp2.sqr(root), n)) throw new Error("Cannot find square root"); return root; }; } const Q1div2 = (Q2 + _1n22) / _2n22; return function tonelliSlow(Fp2, n) { if (Fp2.pow(n, legendreC) === Fp2.neg(Fp2.ONE)) throw new Error("Cannot find square root"); let r = S4; let g = Fp2.pow(Fp2.mul(Fp2.ONE, Z), Q2); let x2 = Fp2.pow(n, Q1div2); let b = Fp2.pow(n, Q2); while (!Fp2.eql(b, Fp2.ONE)) { if (Fp2.eql(b, Fp2.ZERO)) return Fp2.ZERO; let m = 1; for (let t2 = Fp2.sqr(b); m < r; m++) { if (Fp2.eql(t2, Fp2.ONE)) break; t2 = Fp2.sqr(t2); } const ge3 = Fp2.pow(g, _1n22 << BigInt(r - m - 1)); g = Fp2.sqr(ge3); x2 = Fp2.mul(x2, ge3); b = Fp2.mul(b, g); r = m; } return x2; }; } function FpSqrt2(P) { if (P % _4n3 === _3n3) { const p1div4 = (P + _1n22) / _4n3; return function sqrt3mod43(Fp2, n) { const root = Fp2.pow(n, p1div4); if (!Fp2.eql(Fp2.sqr(root), n)) throw new Error("Cannot find square root"); return root; }; } if (P % _8n2 === _5n2) { const c1 = (P - _5n2) / _8n2; return function sqrt5mod83(Fp2, n) { const n2 = Fp2.mul(n, _2n22); const v6 = Fp2.pow(n2, c1); const nv = Fp2.mul(n, v6); const i3 = Fp2.mul(Fp2.mul(nv, _2n22), v6); const root = Fp2.mul(nv, Fp2.sub(i3, Fp2.ONE)); if (!Fp2.eql(Fp2.sqr(root), n)) throw new Error("Cannot find square root"); return root; }; } if (P % _16n2 === _9n2) { } return tonelliShanks2(P); } var FIELD_FIELDS2 = [ "create", "isValid", "is0", "neg", "inv", "sqrt", "sqr", "eql", "add", "sub", "mul", "pow", "div", "addN", "subN", "mulN", "sqrN" ]; function validateField2(field) { const initial = { ORDER: "bigint", MASK: "bigint", BYTES: "isSafeInteger", BITS: "isSafeInteger" }; const opts = FIELD_FIELDS2.reduce((map, val) => { map[val] = "function"; return map; }, initial); return validateObject2(field, opts); } function FpPow2(f, num3, power) { if (power < _0n22) throw new Error("Expected power > 0"); if (power === _0n22) return f.ONE; if (power === _1n22) return num3; let p5 = f.ONE; let d17 = num3; while (power > _0n22) { if (power & _1n22) p5 = f.mul(p5, d17); d17 = f.sqr(d17); power >>= _1n22; } return p5; } function FpInvertBatch2(f, nums) { const tmp = new Array(nums.length); const lastMultiplied = nums.reduce((acc, num3, i3) => { if (f.is0(num3)) return acc; tmp[i3] = acc; return f.mul(acc, num3); }, f.ONE); const inverted = f.inv(lastMultiplied); nums.reduceRight((acc, num3, i3) => { if (f.is0(num3)) return acc; tmp[i3] = f.mul(acc, tmp[i3]); return f.mul(acc, num3); }, inverted); return tmp; } function nLength2(n, nBitLength) { const _nBitLength = nBitLength !== void 0 ? nBitLength : n.toString(2).length; const nByteLength = Math.ceil(_nBitLength / 8); return { nBitLength: _nBitLength, nByteLength }; } function Field2(ORDER, bitLen23, isLE22 = false, redef = {}) { if (ORDER <= _0n22) throw new Error(`Expected Field ORDER > 0, got ${ORDER}`); const { nBitLength: BITS, nByteLength: BYTES } = nLength2(ORDER, bitLen23); if (BYTES > 2048) throw new Error("Field lengths over 2048 bytes are not supported"); const sqrtP = FpSqrt2(ORDER); const f = Object.freeze({ ORDER, BITS, BYTES, MASK: bitMask2(BITS), ZERO: _0n22, ONE: _1n22, create: (num3) => mod2(num3, ORDER), isValid: (num3) => { if (typeof num3 !== "bigint") throw new Error(`Invalid field element: expected bigint, got ${typeof num3}`); return _0n22 <= num3 && num3 < ORDER; }, is0: (num3) => num3 === _0n22, isOdd: (num3) => (num3 & _1n22) === _1n22, neg: (num3) => mod2(-num3, ORDER), eql: (lhs, rhs) => lhs === rhs, sqr: (num3) => mod2(num3 * num3, ORDER), add: (lhs, rhs) => mod2(lhs + rhs, ORDER), sub: (lhs, rhs) => mod2(lhs - rhs, ORDER), mul: (lhs, rhs) => mod2(lhs * rhs, ORDER), pow: (num3, power) => FpPow2(f, num3, power), div: (lhs, rhs) => mod2(lhs * invert2(rhs, ORDER), ORDER), sqrN: (num3) => num3 * num3, addN: (lhs, rhs) => lhs + rhs, subN: (lhs, rhs) => lhs - rhs, mulN: (lhs, rhs) => lhs * rhs, inv: (num3) => invert2(num3, ORDER), sqrt: redef.sqrt || ((n) => sqrtP(f, n)), invertBatch: (lst) => FpInvertBatch2(f, lst), cmov: (a, b, c) => c ? b : a, toBytes: (num3) => isLE22 ? numberToBytesLE2(num3, BYTES) : numberToBytesBE2(num3, BYTES), fromBytes: (bytes22) => { if (bytes22.length !== BYTES) throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes22.length}`); return isLE22 ? bytesToNumberLE2(bytes22) : bytesToNumberBE2(bytes22); } }); return Object.freeze(f); } function getFieldBytesLength2(fieldOrder) { if (typeof fieldOrder !== "bigint") throw new Error("field order must be bigint"); const bitLength = fieldOrder.toString(2).length; return Math.ceil(bitLength / 8); } function getMinHashLength2(fieldOrder) { const length = getFieldBytesLength2(fieldOrder); return length + Math.ceil(length / 2); } function mapHashToField2(key, fieldOrder, isLE22 = false) { const len = key.length; const fieldLen = getFieldBytesLength2(fieldOrder); const minLen = getMinHashLength2(fieldOrder); if (len < 16 || len < minLen || len > 1024) throw new Error(`expected ${minLen}-1024 bytes of input, got ${len}`); const num3 = isLE22 ? bytesToNumberBE2(key) : bytesToNumberLE2(key); const reduced = mod2(num3, fieldOrder - _1n22) + _1n22; return isLE22 ? numberToBytesLE2(reduced, fieldLen) : numberToBytesBE2(reduced, fieldLen); } var _0n32 = BigInt(0); var _1n32 = BigInt(1); function wNAF2(c, bits) { const constTimeNegate = (condition, item) => { const neg = item.negate(); return condition ? neg : item; }; const opts = (W3) => { const windows = Math.ceil(bits / W3) + 1; const windowSize = 2 ** (W3 - 1); return { windows, windowSize }; }; return { constTimeNegate, unsafeLadder(elm, n) { let p5 = c.ZERO; let d17 = elm; while (n > _0n32) { if (n & _1n32) p5 = p5.add(d17); d17 = d17.double(); n >>= _1n32; } return p5; }, precomputeWindow(elm, W3) { const { windows, windowSize } = opts(W3); const points = []; let p5 = elm; let base = p5; for (let window2 = 0; window2 < windows; window2++) { base = p5; points.push(base); for (let i3 = 1; i3 < windowSize; i3++) { base = base.add(p5); points.push(base); } p5 = base.double(); } return points; }, wNAF(W3, precomputes, n) { const { windows, windowSize } = opts(W3); let p5 = c.ZERO; let f = c.BASE; const mask = BigInt(2 ** W3 - 1); const maxNumber = 2 ** W3; const shiftBy = BigInt(W3); for (let window2 = 0; window2 < windows; window2++) { const offset = window2 * windowSize; let wbits = Number(n & mask); n >>= shiftBy; if (wbits > windowSize) { wbits -= maxNumber; n += _1n32; } const offset1 = offset; const offset2 = offset + Math.abs(wbits) - 1; const cond1 = window2 % 2 !== 0; const cond2 = wbits < 0; if (wbits === 0) { f = f.add(constTimeNegate(cond1, precomputes[offset1])); } else { p5 = p5.add(constTimeNegate(cond2, precomputes[offset2])); } } return { p: p5, f }; }, wNAFCached(P, precomputesMap, n, transform) { const W3 = P._WINDOW_SIZE || 1; let comp = precomputesMap.get(P); if (!comp) { comp = this.precomputeWindow(P, W3); if (W3 !== 1) { precomputesMap.set(P, transform(comp)); } } return this.wNAF(W3, comp, n); } }; } function validateBasic(curve) { validateField2(curve.Fp); validateObject2(curve, { n: "bigint", h: "bigint", Gx: "field", Gy: "field" }, { nBitLength: "isSafeInteger", nByteLength: "isSafeInteger" }); return Object.freeze({ ...nLength2(curve.n, curve.nBitLength), ...curve, ...{ p: curve.Fp.ORDER } }); } function validatePointOpts(curve) { const opts = validateBasic(curve); validateObject2(opts, { a: "field", b: "field" }, { allowedPrivateKeyLengths: "array", wrapPrivateKey: "boolean", isTorsionFree: "function", clearCofactor: "function", allowInfinityPoint: "boolean", fromBytes: "function", toBytes: "function" }); const { endo, Fp: Fp2, a } = opts; if (endo) { if (!Fp2.eql(a, Fp2.ZERO)) { throw new Error("Endomorphism can only be defined for Koblitz curves that have a=0"); } if (typeof endo !== "object" || typeof endo.beta !== "bigint" || typeof endo.splitScalar !== "function") { throw new Error("Expected endomorphism with beta: bigint and splitScalar: function"); } } return Object.freeze({ ...opts }); } var { bytesToNumberBE: b2n, hexToBytes: h2b } = exports_utils; var DER2 = { Err: class DERErr2 extends Error { constructor(m = "") { super(m); } }, _parseInt(data) { const { Err: E2 } = DER2; if (data.length < 2 || data[0] !== 2) throw new E2("Invalid signature integer tag"); const len = data[1]; const res = data.subarray(2, len + 2); if (!len || res.length !== len) throw new E2("Invalid signature integer: wrong length"); if (res[0] & 128) throw new E2("Invalid signature integer: negative"); if (res[0] === 0 && !(res[1] & 128)) throw new E2("Invalid signature integer: unnecessary leading zero"); return { d: b2n(res), l: data.subarray(len + 2) }; }, toSig(hex2) { const { Err: E2 } = DER2; const data = typeof hex2 === "string" ? h2b(hex2) : hex2; if (!(data instanceof Uint8Array)) throw new Error("ui8a expected"); let l3 = data.length; if (l3 < 2 || data[0] != 48) throw new E2("Invalid signature tag"); if (data[1] !== l3 - 2) throw new E2("Invalid signature: incorrect length"); const { d: r, l: sBytes } = DER2._parseInt(data.subarray(2)); const { d: s, l: rBytesLeft } = DER2._parseInt(sBytes); if (rBytesLeft.length) throw new E2("Invalid signature: left bytes after parsing"); return { r, s }; }, hexFromSig(sig) { const slice = (s2) => Number.parseInt(s2[0], 16) & 8 ? "00" + s2 : s2; const h2 = (num3) => { const hex2 = num3.toString(16); return hex2.length & 1 ? `0${hex2}` : hex2; }; const s = slice(h2(sig.s)); const r = slice(h2(sig.r)); const shl = s.length / 2; const rhl = r.length / 2; const sl = h2(shl); const rl = h2(rhl); return `30${h2(rhl + shl + 4)}02${rl}${r}02${sl}${s}`; } }; var _0n42 = BigInt(0); var _1n42 = BigInt(1); var _2n32 = BigInt(2); var _3n22 = BigInt(3); var _4n22 = BigInt(4); function weierstrassPoints(opts) { const CURVE = validatePointOpts(opts); const { Fp: Fp2 } = CURVE; const toBytes23 = CURVE.toBytes || ((_c, point, _isCompressed) => { const a = point.toAffine(); return concatBytes22(Uint8Array.from([4]), Fp2.toBytes(a.x), Fp2.toBytes(a.y)); }); const fromBytes = CURVE.fromBytes || ((bytes22) => { const tail = bytes22.subarray(1); const x2 = Fp2.fromBytes(tail.subarray(0, Fp2.BYTES)); const y2 = Fp2.fromBytes(tail.subarray(Fp2.BYTES, 2 * Fp2.BYTES)); return { x: x2, y: y2 }; }); function weierstrassEquation(x2) { const { a, b } = CURVE; const x22 = Fp2.sqr(x2); const x3 = Fp2.mul(x22, x2); return Fp2.add(Fp2.add(x3, Fp2.mul(x2, a)), b); } if (!Fp2.eql(Fp2.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx))) throw new Error("bad generator point: equation left != right"); function isWithinCurveOrder(num3) { return typeof num3 === "bigint" && _0n42 < num3 && num3 < CURVE.n; } function assertGE(num3) { if (!isWithinCurveOrder(num3)) throw new Error("Expected valid bigint: 0 < bigint < curve.n"); } function normPrivateKeyToScalar(key) { const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE; if (lengths && typeof key !== "bigint") { if (key instanceof Uint8Array) key = bytesToHex2(key); if (typeof key !== "string" || !lengths.includes(key.length)) throw new Error("Invalid key"); key = key.padStart(nByteLength * 2, "0"); } let num3; try { num3 = typeof key === "bigint" ? key : bytesToNumberBE2(ensureBytes2("private key", key, nByteLength)); } catch (error) { throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`); } if (wrapPrivateKey) num3 = mod2(num3, n); assertGE(num3); return num3; } const pointPrecomputes3 = /* @__PURE__ */ new Map(); function assertPrjPoint(other) { if (!(other instanceof Point3)) throw new Error("ProjectivePoint expected"); } class Point3 { constructor(px, py, pz) { this.px = px; this.py = py; this.pz = pz; if (px == null || !Fp2.isValid(px)) throw new Error("x required"); if (py == null || !Fp2.isValid(py)) throw new Error("y required"); if (pz == null || !Fp2.isValid(pz)) throw new Error("z required"); } static fromAffine(p5) { const { x: x2, y: y2 } = p5 || {}; if (!p5 || !Fp2.isValid(x2) || !Fp2.isValid(y2)) throw new Error("invalid affine point"); if (p5 instanceof Point3) throw new Error("projective point not allowed"); const is0 = (i3) => Fp2.eql(i3, Fp2.ZERO); if (is0(x2) && is0(y2)) return Point3.ZERO; return new Point3(x2, y2, Fp2.ONE); } get x() { return this.toAffine().x; } get y() { return this.toAffine().y; } static normalizeZ(points) { const toInv = Fp2.invertBatch(points.map((p5) => p5.pz)); return points.map((p5, i3) => p5.toAffine(toInv[i3])).map(Point3.fromAffine); } static fromHex(hex2) { const P = Point3.fromAffine(fromBytes(ensureBytes2("pointHex", hex2))); P.assertValidity(); return P; } static fromPrivateKey(privateKey) { return Point3.BASE.multiply(normPrivateKeyToScalar(privateKey)); } _setWindowSize(windowSize) { this._WINDOW_SIZE = windowSize; pointPrecomputes3.delete(this); } assertValidity() { if (this.is0()) { if (CURVE.allowInfinityPoint && !Fp2.is0(this.py)) return; throw new Error("bad point: ZERO"); } const { x: x2, y: y2 } = this.toAffine(); if (!Fp2.isValid(x2) || !Fp2.isValid(y2)) throw new Error("bad point: x or y not FE"); const left = Fp2.sqr(y2); const right = weierstrassEquation(x2); if (!Fp2.eql(left, right)) throw new Error("bad point: equation left != right"); if (!this.isTorsionFree()) throw new Error("bad point: not in prime-order subgroup"); } hasEvenY() { const { y: y2 } = this.toAffine(); if (Fp2.isOdd) return !Fp2.isOdd(y2); throw new Error("Field doesn't support isOdd"); } equals(other) { assertPrjPoint(other); const { px: X1, py: Y1, pz: Z1 } = this; const { px: X2, py: Y2, pz: Z2 } = other; const U1 = Fp2.eql(Fp2.mul(X1, Z2), Fp2.mul(X2, Z1)); const U2 = Fp2.eql(Fp2.mul(Y1, Z2), Fp2.mul(Y2, Z1)); return U1 && U2; } negate() { return new Point3(this.px, Fp2.neg(this.py), this.pz); } double() { const { a, b } = CURVE; const b3 = Fp2.mul(b, _3n22); const { px: X1, py: Y1, pz: Z1 } = this; let { ZERO: X3, ZERO: Y3, ZERO: Z3 } = Fp2; let t0 = Fp2.mul(X1, X1); let t1 = Fp2.mul(Y1, Y1); let t2 = Fp2.mul(Z1, Z1); let t3 = Fp2.mul(X1, Y1); t3 = Fp2.add(t3, t3); Z3 = Fp2.mul(X1, Z1); Z3 = Fp2.add(Z3, Z3); X3 = Fp2.mul(a, Z3); Y3 = Fp2.mul(b3, t2); Y3 = Fp2.add(X3, Y3); X3 = Fp2.sub(t1, Y3); Y3 = Fp2.add(t1, Y3); Y3 = Fp2.mul(X3, Y3); X3 = Fp2.mul(t3, X3); Z3 = Fp2.mul(b3, Z3); t2 = Fp2.mul(a, t2); t3 = Fp2.sub(t0, t2); t3 = Fp2.mul(a, t3); t3 = Fp2.add(t3, Z3); Z3 = Fp2.add(t0, t0); t0 = Fp2.add(Z3, t0); t0 = Fp2.add(t0, t2); t0 = Fp2.mul(t0, t3); Y3 = Fp2.add(Y3, t0); t2 = Fp2.mul(Y1, Z1); t2 = Fp2.add(t2, t2); t0 = Fp2.mul(t2, t3); X3 = Fp2.sub(X3, t0); Z3 = Fp2.mul(t2, t1); Z3 = Fp2.add(Z3, Z3); Z3 = Fp2.add(Z3, Z3); return new Point3(X3, Y3, Z3); } add(other) { assertPrjPoint(other); const { px: X1, py: Y1, pz: Z1 } = this; const { px: X2, py: Y2, pz: Z2 } = other; let { ZERO: X3, ZERO: Y3, ZERO: Z3 } = Fp2; const a = CURVE.a; const b3 = Fp2.mul(CURVE.b, _3n22); let t0 = Fp2.mul(X1, X2); let t1 = Fp2.mul(Y1, Y2); let t2 = Fp2.mul(Z1, Z2); let t3 = Fp2.add(X1, Y1); let t4 = Fp2.add(X2, Y2); t3 = Fp2.mul(t3, t4); t4 = Fp2.add(t0, t1); t3 = Fp2.sub(t3, t4); t4 = Fp2.add(X1, Z1); let t5 = Fp2.add(X2, Z2); t4 = Fp2.mul(t4, t5); t5 = Fp2.add(t0, t2); t4 = Fp2.sub(t4, t5); t5 = Fp2.add(Y1, Z1); X3 = Fp2.add(Y2, Z2); t5 = Fp2.mul(t5, X3); X3 = Fp2.add(t1, t2); t5 = Fp2.sub(t5, X3); Z3 = Fp2.mul(a, t4); X3 = Fp2.mul(b3, t2); Z3 = Fp2.add(X3, Z3); X3 = Fp2.sub(t1, Z3); Z3 = Fp2.add(t1, Z3); Y3 = Fp2.mul(X3, Z3); t1 = Fp2.add(t0, t0); t1 = Fp2.add(t1, t0); t2 = Fp2.mul(a, t2); t4 = Fp2.mul(b3, t4); t1 = Fp2.add(t1, t2); t2 = Fp2.sub(t0, t2); t2 = Fp2.mul(a, t2); t4 = Fp2.add(t4, t2); t0 = Fp2.mul(t1, t4); Y3 = Fp2.add(Y3, t0); t0 = Fp2.mul(t5, t4); X3 = Fp2.mul(t3, X3); X3 = Fp2.sub(X3, t0); t0 = Fp2.mul(t3, t1); Z3 = Fp2.mul(t5, Z3); Z3 = Fp2.add(Z3, t0); return new Point3(X3, Y3, Z3); } subtract(other) { return this.add(other.negate()); } is0() { return this.equals(Point3.ZERO); } wNAF(n) { return wnaf.wNAFCached(this, pointPrecomputes3, n, (comp) => { const toInv = Fp2.invertBatch(comp.map((p5) => p5.pz)); return comp.map((p5, i3) => p5.toAffine(toInv[i3])).map(Point3.fromAffine); }); } multiplyUnsafe(n) { const I2 = Point3.ZERO; if (n === _0n42) return I2; assertGE(n); if (n === _1n42) return this; const { endo } = CURVE; if (!endo) return wnaf.unsafeLadder(this, n); let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n); let k1p = I2; let k2p = I2; let d17 = this; while (k1 > _0n42 || k2 > _0n42) { if (k1 & _1n42) k1p = k1p.add(d17); if (k2 & _1n42) k2p = k2p.add(d17); d17 = d17.double(); k1 >>= _1n42; k2 >>= _1n42; } if (k1neg) k1p = k1p.negate(); if (k2neg) k2p = k2p.negate(); k2p = new Point3(Fp2.mul(k2p.px, endo.beta), k2p.py, k2p.pz); return k1p.add(k2p); } multiply(scalar) { assertGE(scalar); let n = scalar; let point, fake; const { endo } = CURVE; if (endo) { const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n); let { p: k1p, f: f1p } = this.wNAF(k1); let { p: k2p, f: f2p } = this.wNAF(k2); k1p = wnaf.constTimeNegate(k1neg, k1p); k2p = wnaf.constTimeNegate(k2neg, k2p); k2p = new Point3(Fp2.mul(k2p.px, endo.beta), k2p.py, k2p.pz); point = k1p.add(k2p); fake = f1p.add(f2p); } else { const { p: p5, f } = this.wNAF(n); point = p5; fake = f; } return Point3.normalizeZ([point, fake])[0]; } multiplyAndAddUnsafe(Q2, a, b) { const G2 = Point3.BASE; const mul3 = (P, a2) => a2 === _0n42 || a2 === _1n42 || !P.equals(G2) ? P.multiplyUnsafe(a2) : P.multiply(a2); const sum = mul3(this, a).add(mul3(Q2, b)); return sum.is0() ? void 0 : sum; } toAffine(iz) { const { px: x2, py: y2, pz: z3 } = this; const is0 = this.is0(); if (iz == null) iz = is0 ? Fp2.ONE : Fp2.inv(z3); const ax = Fp2.mul(x2, iz); const ay = Fp2.mul(y2, iz); const zz = Fp2.mul(z3, iz); if (is0) return { x: Fp2.ZERO, y: Fp2.ZERO }; if (!Fp2.eql(zz, Fp2.ONE)) throw new Error("invZ was invalid"); return { x: ax, y: ay }; } isTorsionFree() { const { h: cofactor, isTorsionFree } = CURVE; if (cofactor === _1n42) return true; if (isTorsionFree) return isTorsionFree(Point3, this); throw new Error("isTorsionFree() has not been declared for the elliptic curve"); } clearCofactor() { const { h: cofactor, clearCofactor } = CURVE; if (cofactor === _1n42) return this; if (clearCofactor) return clearCofactor(Point3, this); return this.multiplyUnsafe(CURVE.h); } toRawBytes(isCompressed = true) { this.assertValidity(); return toBytes23(Point3, this, isCompressed); } toHex(isCompressed = true) { return bytesToHex2(this.toRawBytes(isCompressed)); } } Point3.BASE = new Point3(CURVE.Gx, CURVE.Gy, Fp2.ONE); Point3.ZERO = new Point3(Fp2.ZERO, Fp2.ONE, Fp2.ZERO); const _bits = CURVE.nBitLength; const wnaf = wNAF2(Point3, CURVE.endo ? Math.ceil(_bits / 2) : _bits); return { CURVE, ProjectivePoint: Point3, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder }; } function validateOpts(curve) { const opts = validateBasic(curve); validateObject2(opts, { hash: "hash", hmac: "function", randomBytes: "function" }, { bits2int: "function", bits2int_modN: "function", lowS: "boolean" }); return Object.freeze({ lowS: true, ...opts }); } function weierstrass2(curveDef) { const CURVE = validateOpts(curveDef); const { Fp: Fp2, n: CURVE_ORDER } = CURVE; const compressedLen = Fp2.BYTES + 1; const uncompressedLen = 2 * Fp2.BYTES + 1; function isValidFieldElement(num3) { return _0n42 < num3 && num3 < Fp2.ORDER; } function modN2(a) { return mod2(a, CURVE_ORDER); } function invN(a) { return invert2(a, CURVE_ORDER); } const { ProjectivePoint: Point3, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder } = weierstrassPoints({ ...CURVE, toBytes(_c, point, isCompressed) { const a = point.toAffine(); const x2 = Fp2.toBytes(a.x); const cat = concatBytes22; if (isCompressed) { return cat(Uint8Array.from([point.hasEvenY() ? 2 : 3]), x2); } else { return cat(Uint8Array.from([4]), x2, Fp2.toBytes(a.y)); } }, fromBytes(bytes22) { const len = bytes22.length; const head = bytes22[0]; const tail = bytes22.subarray(1); if (len === compressedLen && (head === 2 || head === 3)) { const x2 = bytesToNumberBE2(tail); if (!isValidFieldElement(x2)) throw new Error("Point is not on curve"); const y2 = weierstrassEquation(x2); let y3 = Fp2.sqrt(y2); const isYOdd = (y3 & _1n42) === _1n42; const isHeadOdd = (head & 1) === 1; if (isHeadOdd !== isYOdd) y3 = Fp2.neg(y3); return { x: x2, y: y3 }; } else if (len === uncompressedLen && head === 4) { const x2 = Fp2.fromBytes(tail.subarray(0, Fp2.BYTES)); const y2 = Fp2.fromBytes(tail.subarray(Fp2.BYTES, 2 * Fp2.BYTES)); return { x: x2, y: y2 }; } else { throw new Error(`Point of length ${len} was invalid. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes`); } } }); const numToNByteStr = (num3) => bytesToHex2(numberToBytesBE2(num3, CURVE.nByteLength)); function isBiggerThanHalfOrder(number22) { const HALF = CURVE_ORDER >> _1n42; return number22 > HALF; } function normalizeS(s) { return isBiggerThanHalfOrder(s) ? modN2(-s) : s; } const slcNum = (b, from, to) => bytesToNumberBE2(b.slice(from, to)); class Signature { constructor(r, s, recovery) { this.r = r; this.s = s; this.recovery = recovery; this.assertValidity(); } static fromCompact(hex2) { const l3 = CURVE.nByteLength; hex2 = ensureBytes2("compactSignature", hex2, l3 * 2); return new Signature(slcNum(hex2, 0, l3), slcNum(hex2, l3, 2 * l3)); } static fromDER(hex2) { const { r, s } = DER2.toSig(ensureBytes2("DER", hex2)); return new Signature(r, s); } assertValidity() { if (!isWithinCurveOrder(this.r)) throw new Error("r must be 0 < r < CURVE.n"); if (!isWithinCurveOrder(this.s)) throw new Error("s must be 0 < s < CURVE.n"); } addRecoveryBit(recovery) { return new Signature(this.r, this.s, recovery); } recoverPublicKey(msgHash) { const { r, s, recovery: rec } = this; const h2 = bits2int_modN(ensureBytes2("msgHash", msgHash)); if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error("recovery id invalid"); const radj = rec === 2 || rec === 3 ? r + CURVE.n : r; if (radj >= Fp2.ORDER) throw new Error("recovery id 2 or 3 invalid"); const prefix = (rec & 1) === 0 ? "02" : "03"; const R2 = Point3.fromHex(prefix + numToNByteStr(radj)); const ir = invN(radj); const u1 = modN2(-h2 * ir); const u22 = modN2(s * ir); const Q2 = Point3.BASE.multiplyAndAddUnsafe(R2, u1, u22); if (!Q2) throw new Error("point at infinify"); Q2.assertValidity(); return Q2; } hasHighS() { return isBiggerThanHalfOrder(this.s); } normalizeS() { return this.hasHighS() ? new Signature(this.r, modN2(-this.s), this.recovery) : this; } toDERRawBytes() { return hexToBytes2(this.toDERHex()); } toDERHex() { return DER2.hexFromSig({ r: this.r, s: this.s }); } toCompactRawBytes() { return hexToBytes2(this.toCompactHex()); } toCompactHex() { return numToNByteStr(this.r) + numToNByteStr(this.s); } } const utils = { isValidPrivateKey(privateKey) { try { normPrivateKeyToScalar(privateKey); return true; } catch (error) { return false; } }, normPrivateKeyToScalar, randomPrivateKey: () => { const length = getMinHashLength2(CURVE.n); return mapHashToField2(CURVE.randomBytes(length), CURVE.n); }, precompute(windowSize = 8, point = Point3.BASE) { point._setWindowSize(windowSize); point.multiply(BigInt(3)); return point; } }; function getPublicKey4(privateKey, isCompressed = true) { return Point3.fromPrivateKey(privateKey).toRawBytes(isCompressed); } function isProbPub(item) { const arr = item instanceof Uint8Array; const str = typeof item === "string"; const len = (arr || str) && item.length; if (arr) return len === compressedLen || len === uncompressedLen; if (str) return len === 2 * compressedLen || len === 2 * uncompressedLen; if (item instanceof Point3) return true; return false; } function getSharedSecret(privateA, publicB, isCompressed = true) { if (isProbPub(privateA)) throw new Error("first arg must be private key"); if (!isProbPub(publicB)) throw new Error("second arg must be public key"); const b = Point3.fromHex(publicB); return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed); } const bits2int = CURVE.bits2int || function(bytes22) { const num3 = bytesToNumberBE2(bytes22); const delta = bytes22.length * 8 - CURVE.nBitLength; return delta > 0 ? num3 >> BigInt(delta) : num3; }; const bits2int_modN = CURVE.bits2int_modN || function(bytes22) { return modN2(bits2int(bytes22)); }; const ORDER_MASK = bitMask2(CURVE.nBitLength); function int2octets(num3) { if (typeof num3 !== "bigint") throw new Error("bigint expected"); if (!(_0n42 <= num3 && num3 < ORDER_MASK)) throw new Error(`bigint expected < 2^${CURVE.nBitLength}`); return numberToBytesBE2(num3, CURVE.nByteLength); } function prepSig(msgHash, privateKey, opts = defaultSigOpts) { if (["recovered", "canonical"].some((k2) => k2 in opts)) throw new Error("sign() legacy options not supported"); const { hash: hash22, randomBytes: randomBytes23 } = CURVE; let { lowS, prehash, extraEntropy: ent } = opts; if (lowS == null) lowS = true; msgHash = ensureBytes2("msgHash", msgHash); if (prehash) msgHash = ensureBytes2("prehashed msgHash", hash22(msgHash)); const h1int = bits2int_modN(msgHash); const d17 = normPrivateKeyToScalar(privateKey); const seedArgs = [int2octets(d17), int2octets(h1int)]; if (ent != null) { const e2 = ent === true ? randomBytes23(Fp2.BYTES) : ent; seedArgs.push(ensureBytes2("extraEntropy", e2)); } const seed = concatBytes22(...seedArgs); const m = h1int; function k2sig(kBytes) { const k2 = bits2int(kBytes); if (!isWithinCurveOrder(k2)) return; const ik = invN(k2); const q2 = Point3.BASE.multiply(k2).toAffine(); const r = modN2(q2.x); if (r === _0n42) return; const s = modN2(ik * modN2(m + r * d17)); if (s === _0n42) return; let recovery = (q2.x === r ? 0 : 2) | Number(q2.y & _1n42); let normS = s; if (lowS && isBiggerThanHalfOrder(s)) { normS = normalizeS(s); recovery ^= 1; } return new Signature(r, normS, recovery); } return { seed, k2sig }; } const defaultSigOpts = { lowS: CURVE.lowS, prehash: false }; const defaultVerOpts = { lowS: CURVE.lowS, prehash: false }; function sign(msgHash, privKey, opts = defaultSigOpts) { const { seed, k2sig } = prepSig(msgHash, privKey, opts); const C2 = CURVE; const drbg = createHmacDrbg2(C2.hash.outputLen, C2.nByteLength, C2.hmac); return drbg(seed, k2sig); } Point3.BASE._setWindowSize(8); function verify(signature, msgHash, publicKey, opts = defaultVerOpts) { const sg = signature; msgHash = ensureBytes2("msgHash", msgHash); publicKey = ensureBytes2("publicKey", publicKey); if ("strict" in opts) throw new Error("options.strict was renamed to lowS"); const { lowS, prehash } = opts; let _sig = void 0; let P; try { if (typeof sg === "string" || sg instanceof Uint8Array) { try { _sig = Signature.fromDER(sg); } catch (derError) { if (!(derError instanceof DER2.Err)) throw derError; _sig = Signature.fromCompact(sg); } } else if (typeof sg === "object" && typeof sg.r === "bigint" && typeof sg.s === "bigint") { const { r: r2, s: s2 } = sg; _sig = new Signature(r2, s2); } else { throw new Error("PARSE"); } P = Point3.fromHex(publicKey); } catch (error) { if (error.message === "PARSE") throw new Error(`signature must be Signature instance, Uint8Array or hex string`); return false; } if (lowS && _sig.hasHighS()) return false; if (prehash) msgHash = CURVE.hash(msgHash); const { r, s } = _sig; const h2 = bits2int_modN(msgHash); const is = invN(s); const u1 = modN2(h2 * is); const u22 = modN2(r * is); const R2 = Point3.BASE.multiplyAndAddUnsafe(P, u1, u22)?.toAffine(); if (!R2) return false; const v6 = modN2(R2.x); return v6 === r; } return { CURVE, getPublicKey: getPublicKey4, getSharedSecret, sign, verify, ProjectivePoint: Point3, Signature, utils }; } var HMAC2 = class extends Hash2 { constructor(hash22, _key) { super(); this.finished = false; this.destroyed = false; hash(hash22); const key = toBytes2(_key); this.iHash = hash22.create(); if (typeof this.iHash.update !== "function") throw new Error("Expected instance of class which extends utils.Hash"); this.blockLen = this.iHash.blockLen; this.outputLen = this.iHash.outputLen; const blockLen = this.blockLen; const pad2 = new Uint8Array(blockLen); pad2.set(key.length > blockLen ? hash22.create().update(key).digest() : key); for (let i3 = 0; i3 < pad2.length; i3++) pad2[i3] ^= 54; this.iHash.update(pad2); this.oHash = hash22.create(); for (let i3 = 0; i3 < pad2.length; i3++) pad2[i3] ^= 54 ^ 92; this.oHash.update(pad2); pad2.fill(0); } update(buf) { exists(this); this.iHash.update(buf); return this; } digestInto(out) { exists(this); bytes(out, this.outputLen); this.finished = true; this.iHash.digestInto(out); this.oHash.update(out); this.oHash.digestInto(out); this.destroy(); } digest() { const out = new Uint8Array(this.oHash.outputLen); this.digestInto(out); return out; } _cloneInto(to) { to || (to = Object.create(Object.getPrototypeOf(this), {})); const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; to = to; to.finished = finished; to.destroyed = destroyed; to.blockLen = blockLen; to.outputLen = outputLen; to.oHash = oHash._cloneInto(to.oHash); to.iHash = iHash._cloneInto(to.iHash); return to; } destroy() { this.destroyed = true; this.oHash.destroy(); this.iHash.destroy(); } }; var hmac2 = (hash22, key, message) => new HMAC2(hash22, key).update(message).digest(); hmac2.create = (hash22, key) => new HMAC2(hash22, key); function getHash(hash22) { return { hash: hash22, hmac: (key, ...msgs) => hmac2(hash22, key, concatBytes2(...msgs)), randomBytes: randomBytes2 }; } function createCurve2(curveDef, defHash) { const create = (hash22) => weierstrass2({ ...curveDef, ...getHash(hash22) }); return Object.freeze({ ...create(defHash), create }); } var secp256k1P = BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); var secp256k1N = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); var _1n52 = BigInt(1); var _2n42 = BigInt(2); var divNearest2 = (a, b) => (a + b / _2n42) / b; function sqrtMod2(y2) { const P = secp256k1P; const _3n33 = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22); const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88); const b2 = y2 * y2 * y2 % P; const b3 = b2 * b2 * y2 % P; const b6 = pow22(b3, _3n33, P) * b3 % P; const b9 = pow22(b6, _3n33, P) * b3 % P; const b11 = pow22(b9, _2n42, P) * b2 % P; const b22 = pow22(b11, _11n, P) * b11 % P; const b44 = pow22(b22, _22n, P) * b22 % P; const b88 = pow22(b44, _44n, P) * b44 % P; const b176 = pow22(b88, _88n, P) * b88 % P; const b220 = pow22(b176, _44n, P) * b44 % P; const b223 = pow22(b220, _3n33, P) * b3 % P; const t1 = pow22(b223, _23n, P) * b22 % P; const t2 = pow22(t1, _6n, P) * b2 % P; const root = pow22(t2, _2n42, P); if (!Fp.eql(Fp.sqr(root), y2)) throw new Error("Cannot find square root"); return root; } var Fp = Field2(secp256k1P, void 0, void 0, { sqrt: sqrtMod2 }); var secp256k12 = createCurve2({ a: BigInt(0), b: BigInt(7), Fp, n: secp256k1N, Gx: BigInt("55066263022277343669578718895168534326250603453777594175500187360389116729240"), Gy: BigInt("32670510020758816978083085130507043184471273380659243275938904335757337482424"), h: BigInt(1), lowS: true, endo: { beta: BigInt("0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"), splitScalar: (k2) => { const n = secp256k1N; const a1 = BigInt("0x3086d221a7d46bcde86c90e49284eb15"); const b1 = -_1n52 * BigInt("0xe4437ed6010e88286f547fa90abfe4c3"); const a2 = BigInt("0x114ca50f7a8e2f3f657c1108d9d44cfd8"); const b2 = a1; const POW_2_128 = BigInt("0x100000000000000000000000000000000"); const c1 = divNearest2(b2 * k2, n); const c2 = divNearest2(-b1 * k2, n); let k1 = mod2(k2 - c1 * a1 - c2 * a2, n); let k22 = mod2(-c1 * b1 - c2 * b2, n); const k1neg = k1 > POW_2_128; const k2neg = k22 > POW_2_128; if (k1neg) k1 = n - k1; if (k2neg) k22 = n - k22; if (k1 > POW_2_128 || k22 > POW_2_128) { throw new Error("splitScalar: Endomorphism failed, k=" + k2); } return { k1neg, k1, k2neg, k2: k22 }; } } }, sha2563); var _0n52 = BigInt(0); var fe = (x2) => typeof x2 === "bigint" && _0n52 < x2 && x2 < secp256k1P; var ge = (x2) => typeof x2 === "bigint" && _0n52 < x2 && x2 < secp256k1N; var TAGGED_HASH_PREFIXES2 = {}; function taggedHash2(tag, ...messages) { let tagP = TAGGED_HASH_PREFIXES2[tag]; if (tagP === void 0) { const tagH = sha2563(Uint8Array.from(tag, (c) => c.charCodeAt(0))); tagP = concatBytes22(tagH, tagH); TAGGED_HASH_PREFIXES2[tag] = tagP; } return sha2563(concatBytes22(tagP, ...messages)); } var pointToBytes2 = (point) => point.toRawBytes(true).slice(1); var numTo32b = (n) => numberToBytesBE2(n, 32); var modP = (x2) => mod2(x2, secp256k1P); var modN = (x2) => mod2(x2, secp256k1N); var Point = secp256k12.ProjectivePoint; var GmulAdd = (Q2, a, b) => Point.BASE.multiplyAndAddUnsafe(Q2, a, b); function schnorrGetExtPubKey2(priv) { let d_ = secp256k12.utils.normPrivateKeyToScalar(priv); let p5 = Point.fromPrivateKey(d_); const scalar = p5.hasEvenY() ? d_ : modN(-d_); return { scalar, bytes: pointToBytes2(p5) }; } function lift_x2(x2) { if (!fe(x2)) throw new Error("bad x: need 0 < x < p"); const xx = modP(x2 * x2); const c = modP(xx * x2 + BigInt(7)); let y2 = sqrtMod2(c); if (y2 % _2n42 !== _0n52) y2 = modP(-y2); const p5 = new Point(x2, y2, _1n52); p5.assertValidity(); return p5; } function challenge2(...args) { return modN(bytesToNumberBE2(taggedHash2("BIP0340/challenge", ...args))); } function schnorrGetPublicKey2(privateKey) { return schnorrGetExtPubKey2(privateKey).bytes; } function schnorrSign2(message, privateKey, auxRand = randomBytes2(32)) { const m = ensureBytes2("message", message); const { bytes: px, scalar: d17 } = schnorrGetExtPubKey2(privateKey); const a = ensureBytes2("auxRand", auxRand, 32); const t = numTo32b(d17 ^ bytesToNumberBE2(taggedHash2("BIP0340/aux", a))); const rand = taggedHash2("BIP0340/nonce", t, px, m); const k_ = modN(bytesToNumberBE2(rand)); if (k_ === _0n52) throw new Error("sign failed: k is zero"); const { bytes: rx, scalar: k2 } = schnorrGetExtPubKey2(k_); const e2 = challenge2(rx, px, m); const sig = new Uint8Array(64); sig.set(rx, 0); sig.set(numTo32b(modN(k2 + e2 * d17)), 32); if (!schnorrVerify2(sig, m, px)) throw new Error("sign: Invalid signature produced"); return sig; } function schnorrVerify2(signature, message, publicKey) { const sig = ensureBytes2("signature", signature, 64); const m = ensureBytes2("message", message); const pub = ensureBytes2("publicKey", publicKey, 32); try { const P = lift_x2(bytesToNumberBE2(pub)); const r = bytesToNumberBE2(sig.subarray(0, 32)); if (!fe(r)) return false; const s = bytesToNumberBE2(sig.subarray(32, 64)); if (!ge(s)) return false; const e2 = challenge2(numTo32b(r), pointToBytes2(P), m); const R2 = GmulAdd(P, s, modN(-e2)); if (!R2 || !R2.hasEvenY() || R2.toAffine().x !== r) return false; return true; } catch (error) { return false; } } var schnorr2 = /* @__PURE__ */ (() => ({ getPublicKey: schnorrGetPublicKey2, sign: schnorrSign2, verify: schnorrVerify2, utils: { randomPrivateKey: secp256k12.utils.randomPrivateKey, lift_x: lift_x2, pointToBytes: pointToBytes2, numberToBytesBE: numberToBytesBE2, bytesToNumberBE: bytesToNumberBE2, taggedHash: taggedHash2, mod: mod2 } }))(); var crypto22 = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : void 0; var u8a3 = (a) => a instanceof Uint8Array; var u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4)); var createView22 = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength); var rotr22 = (word, shift) => word << 32 - shift | word >>> shift; var isLE2 = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68; if (!isLE2) throw new Error("Non little-endian hardware is not supported"); var hexes22 = Array.from({ length: 256 }, (v6, i3) => i3.toString(16).padStart(2, "0")); function bytesToHex22(bytes22) { if (!u8a3(bytes22)) throw new Error("Uint8Array expected"); let hex2 = ""; for (let i3 = 0; i3 < bytes22.length; i3++) { hex2 += hexes22[bytes22[i3]]; } return hex2; } function hexToBytes22(hex2) { if (typeof hex2 !== "string") throw new Error("hex string expected, got " + typeof hex2); const len = hex2.length; if (len % 2) throw new Error("padded hex string expected, got unpadded hex of length " + len); const array = new Uint8Array(len / 2); for (let i3 = 0; i3 < array.length; i3++) { const j2 = i3 * 2; const hexByte = hex2.slice(j2, j2 + 2); const byte = Number.parseInt(hexByte, 16); if (Number.isNaN(byte) || byte < 0) throw new Error("Invalid byte sequence"); array[i3] = byte; } return array; } function utf8ToBytes3(str) { if (typeof str !== "string") throw new Error(`utf8ToBytes expected string, got ${typeof str}`); return new Uint8Array(new TextEncoder().encode(str)); } function toBytes22(data) { if (typeof data === "string") data = utf8ToBytes3(data); if (!u8a3(data)) throw new Error(`expected Uint8Array, got ${typeof data}`); return data; } function concatBytes3(...arrays) { const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0)); let pad2 = 0; arrays.forEach((a) => { if (!u8a3(a)) throw new Error("Uint8Array expected"); r.set(a, pad2); pad2 += a.length; }); return r; } var Hash22 = class { clone() { return this._cloneInto(); } }; var isPlainObject = (obj) => Object.prototype.toString.call(obj) === "[object Object]" && obj.constructor === Object; function checkOpts(defaults, opts) { if (opts !== void 0 && (typeof opts !== "object" || !isPlainObject(opts))) throw new Error("Options should be object or undefined"); const merged = Object.assign(defaults, opts); return merged; } function wrapConstructor2(hashCons) { const hashC = (msg) => hashCons().update(toBytes22(msg)).digest(); const tmp = hashCons(); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.create = () => hashCons(); return hashC; } function randomBytes22(bytesLength = 32) { if (crypto22 && typeof crypto22.getRandomValues === "function") { return crypto22.getRandomValues(new Uint8Array(bytesLength)); } throw new Error("crypto.getRandomValues must be defined"); } function number2(n) { if (!Number.isSafeInteger(n) || n < 0) throw new Error(`Wrong positive integer: ${n}`); } function bool(b) { if (typeof b !== "boolean") throw new Error(`Expected boolean, not ${b}`); } function bytes2(b, ...lengths) { if (!(b instanceof Uint8Array)) throw new Error("Expected Uint8Array"); if (lengths.length > 0 && !lengths.includes(b.length)) throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`); } function hash2(hash3) { if (typeof hash3 !== "function" || typeof hash3.create !== "function") throw new Error("Hash should be wrapped by utils.wrapConstructor"); number2(hash3.outputLen); number2(hash3.blockLen); } function exists2(instance, checkFinished = true) { if (instance.destroyed) throw new Error("Hash instance has been destroyed"); if (checkFinished && instance.finished) throw new Error("Hash#digest() has already been called"); } function output2(out, instance) { bytes2(out); const min = instance.outputLen; if (out.length < min) { throw new Error(`digestInto() expects output buffer of length at least ${min}`); } } var assert = { number: number2, bool, bytes: bytes2, hash: hash2, exists: exists2, output: output2 }; var _assert_default = assert; function setBigUint6422(view, byteOffset, value, isLE32) { if (typeof view.setBigUint64 === "function") return view.setBigUint64(byteOffset, value, isLE32); const _32n2 = BigInt(32); const _u32_max = BigInt(4294967295); const wh = Number(value >> _32n2 & _u32_max); const wl = Number(value & _u32_max); const h2 = isLE32 ? 4 : 0; const l3 = isLE32 ? 0 : 4; view.setUint32(byteOffset + h2, wh, isLE32); view.setUint32(byteOffset + l3, wl, isLE32); } var SHA22 = class extends Hash22 { constructor(blockLen, outputLen, padOffset, isLE32) { super(); this.blockLen = blockLen; this.outputLen = outputLen; this.padOffset = padOffset; this.isLE = isLE32; this.finished = false; this.length = 0; this.pos = 0; this.destroyed = false; this.buffer = new Uint8Array(blockLen); this.view = createView22(this.buffer); } update(data) { _assert_default.exists(this); const { view, buffer, blockLen } = this; data = toBytes22(data); const len = data.length; for (let pos = 0; pos < len; ) { const take = Math.min(blockLen - this.pos, len - pos); if (take === blockLen) { const dataView = createView22(data); for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos); continue; } buffer.set(data.subarray(pos, pos + take), this.pos); this.pos += take; pos += take; if (this.pos === blockLen) { this.process(view, 0); this.pos = 0; } } this.length += data.length; this.roundClean(); return this; } digestInto(out) { _assert_default.exists(this); _assert_default.output(out, this); this.finished = true; const { buffer, view, blockLen, isLE: isLE32 } = this; let { pos } = this; buffer[pos++] = 128; this.buffer.subarray(pos).fill(0); if (this.padOffset > blockLen - pos) { this.process(view, 0); pos = 0; } for (let i3 = pos; i3 < blockLen; i3++) buffer[i3] = 0; setBigUint6422(view, blockLen - 8, BigInt(this.length * 8), isLE32); this.process(view, 0); const oview = createView22(out); const len = this.outputLen; if (len % 4) throw new Error("_sha2: outputLen should be aligned to 32bit"); const outLen = len / 4; const state = this.get(); if (outLen > state.length) throw new Error("_sha2: outputLen bigger than state"); for (let i3 = 0; i3 < outLen; i3++) oview.setUint32(4 * i3, state[i3], isLE32); } digest() { const { buffer, outputLen } = this; this.digestInto(buffer); const res = buffer.slice(0, outputLen); this.destroy(); return res; } _cloneInto(to) { to || (to = new this.constructor()); to.set(...this.get()); const { blockLen, buffer, length, finished, destroyed, pos } = this; to.length = length; to.pos = pos; to.finished = finished; to.destroyed = destroyed; if (length % blockLen) to.buffer.set(buffer); return to; } }; var Chi22 = (a, b, c) => a & b ^ ~a & c; var Maj22 = (a, b, c) => a & b ^ a & c ^ b & c; var SHA256_K22 = new Uint32Array([ 1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298 ]); var IV2 = new Uint32Array([ 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225 ]); var SHA256_W22 = new Uint32Array(64); var SHA25622 = class extends SHA22 { constructor() { super(64, 32, 8, false); this.A = IV2[0] | 0; this.B = IV2[1] | 0; this.C = IV2[2] | 0; this.D = IV2[3] | 0; this.E = IV2[4] | 0; this.F = IV2[5] | 0; this.G = IV2[6] | 0; this.H = IV2[7] | 0; } get() { const { A, B, C: C2, D: D3, E: E2, F: F2, G: G2, H: H2 } = this; return [A, B, C2, D3, E2, F2, G2, H2]; } set(A, B, C2, D3, E2, F2, G2, H2) { this.A = A | 0; this.B = B | 0; this.C = C2 | 0; this.D = D3 | 0; this.E = E2 | 0; this.F = F2 | 0; this.G = G2 | 0; this.H = H2 | 0; } process(view, offset) { for (let i3 = 0; i3 < 16; i3++, offset += 4) SHA256_W22[i3] = view.getUint32(offset, false); for (let i3 = 16; i3 < 64; i3++) { const W15 = SHA256_W22[i3 - 15]; const W22 = SHA256_W22[i3 - 2]; const s0 = rotr22(W15, 7) ^ rotr22(W15, 18) ^ W15 >>> 3; const s1 = rotr22(W22, 17) ^ rotr22(W22, 19) ^ W22 >>> 10; SHA256_W22[i3] = s1 + SHA256_W22[i3 - 7] + s0 + SHA256_W22[i3 - 16] | 0; } let { A, B, C: C2, D: D3, E: E2, F: F2, G: G2, H: H2 } = this; for (let i3 = 0; i3 < 64; i3++) { const sigma1 = rotr22(E2, 6) ^ rotr22(E2, 11) ^ rotr22(E2, 25); const T1 = H2 + sigma1 + Chi22(E2, F2, G2) + SHA256_K22[i3] + SHA256_W22[i3] | 0; const sigma0 = rotr22(A, 2) ^ rotr22(A, 13) ^ rotr22(A, 22); const T22 = sigma0 + Maj22(A, B, C2) | 0; H2 = G2; G2 = F2; F2 = E2; E2 = D3 + T1 | 0; D3 = C2; C2 = B; B = A; A = T1 + T22 | 0; } A = A + this.A | 0; B = B + this.B | 0; C2 = C2 + this.C | 0; D3 = D3 + this.D | 0; E2 = E2 + this.E | 0; F2 = F2 + this.F | 0; G2 = G2 + this.G | 0; H2 = H2 + this.H | 0; this.set(A, B, C2, D3, E2, F2, G2, H2); } roundClean() { SHA256_W22.fill(0); } destroy() { this.set(0, 0, 0, 0, 0, 0, 0, 0); this.buffer.fill(0); } }; var SHA2242 = class extends SHA25622 { constructor() { super(); this.A = 3238371032 | 0; this.B = 914150663 | 0; this.C = 812702999 | 0; this.D = 4144912697 | 0; this.E = 4290775857 | 0; this.F = 1750603025 | 0; this.G = 1694076839 | 0; this.H = 3204075428 | 0; this.outputLen = 28; } }; var sha25622 = wrapConstructor2(() => new SHA25622()); var sha2242 = wrapConstructor2(() => new SHA2242()); function assertNumber(n) { if (!Number.isSafeInteger(n)) throw new Error(`Wrong integer: ${n}`); } function chain2(...args) { const wrap = (a, b) => (c) => a(b(c)); const encode4 = Array.from(args).reverse().reduce((acc, i3) => acc ? wrap(acc, i3.encode) : i3.encode, void 0); const decode5 = args.reduce((acc, i3) => acc ? wrap(acc, i3.decode) : i3.decode, void 0); return { encode: encode4, decode: decode5 }; } function alphabet2(alphabet22) { return { encode: (digits) => { if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number") throw new Error("alphabet.encode input should be an array of numbers"); return digits.map((i3) => { assertNumber(i3); if (i3 < 0 || i3 >= alphabet22.length) throw new Error(`Digit index outside alphabet: ${i3} (alphabet: ${alphabet22.length})`); return alphabet22[i3]; }); }, decode: (input) => { if (!Array.isArray(input) || input.length && typeof input[0] !== "string") throw new Error("alphabet.decode input should be array of strings"); return input.map((letter) => { if (typeof letter !== "string") throw new Error(`alphabet.decode: not string element=${letter}`); const index = alphabet22.indexOf(letter); if (index === -1) throw new Error(`Unknown letter: "${letter}". Allowed: ${alphabet22}`); return index; }); } }; } function join2(separator = "") { if (typeof separator !== "string") throw new Error("join separator should be string"); return { encode: (from) => { if (!Array.isArray(from) || from.length && typeof from[0] !== "string") throw new Error("join.encode input should be array of strings"); for (let i3 of from) if (typeof i3 !== "string") throw new Error(`join.encode: non-string input=${i3}`); return from.join(separator); }, decode: (to) => { if (typeof to !== "string") throw new Error("join.decode input should be string"); return to.split(separator); } }; } function padding(bits, chr = "=") { assertNumber(bits); if (typeof chr !== "string") throw new Error("padding chr should be string"); return { encode(data) { if (!Array.isArray(data) || data.length && typeof data[0] !== "string") throw new Error("padding.encode input should be array of strings"); for (let i3 of data) if (typeof i3 !== "string") throw new Error(`padding.encode: non-string input=${i3}`); while (data.length * bits % 8) data.push(chr); return data; }, decode(input) { if (!Array.isArray(input) || input.length && typeof input[0] !== "string") throw new Error("padding.encode input should be array of strings"); for (let i3 of input) if (typeof i3 !== "string") throw new Error(`padding.decode: non-string input=${i3}`); let end = input.length; if (end * bits % 8) throw new Error("Invalid padding: string should have whole number of bytes"); for (; end > 0 && input[end - 1] === chr; end--) { if (!((end - 1) * bits % 8)) throw new Error("Invalid padding: string has too much padding"); } return input.slice(0, end); } }; } function normalize2(fn) { if (typeof fn !== "function") throw new Error("normalize fn should be function"); return { encode: (from) => from, decode: (to) => fn(to) }; } function convertRadix3(data, from, to) { if (from < 2) throw new Error(`convertRadix: wrong from=${from}, base cannot be less than 2`); if (to < 2) throw new Error(`convertRadix: wrong to=${to}, base cannot be less than 2`); if (!Array.isArray(data)) throw new Error("convertRadix: data should be array"); if (!data.length) return []; let pos = 0; const res = []; const digits = Array.from(data); digits.forEach((d17) => { assertNumber(d17); if (d17 < 0 || d17 >= from) throw new Error(`Wrong integer: ${d17}`); }); while (true) { let carry = 0; let done = true; for (let i3 = pos; i3 < digits.length; i3++) { const digit = digits[i3]; const digitBase = from * carry + digit; if (!Number.isSafeInteger(digitBase) || from * carry / from !== carry || digitBase - digit !== from * carry) { throw new Error("convertRadix: carry overflow"); } carry = digitBase % to; digits[i3] = Math.floor(digitBase / to); if (!Number.isSafeInteger(digits[i3]) || digits[i3] * to + carry !== digitBase) throw new Error("convertRadix: carry overflow"); if (!done) continue; else if (!digits[i3]) pos = i3; else done = false; } res.push(carry); if (done) break; } for (let i3 = 0; i3 < data.length - 1 && data[i3] === 0; i3++) res.push(0); return res.reverse(); } var gcd2 = (a, b) => !b ? a : gcd2(b, a % b); var radix2carry2 = (from, to) => from + (to - gcd2(from, to)); function convertRadix22(data, from, to, padding2) { if (!Array.isArray(data)) throw new Error("convertRadix2: data should be array"); if (from <= 0 || from > 32) throw new Error(`convertRadix2: wrong from=${from}`); if (to <= 0 || to > 32) throw new Error(`convertRadix2: wrong to=${to}`); if (radix2carry2(from, to) > 32) { throw new Error(`convertRadix2: carry overflow from=${from} to=${to} carryBits=${radix2carry2(from, to)}`); } let carry = 0; let pos = 0; const mask = 2 ** to - 1; const res = []; for (const n of data) { assertNumber(n); if (n >= 2 ** from) throw new Error(`convertRadix2: invalid data word=${n} from=${from}`); carry = carry << from | n; if (pos + from > 32) throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from}`); pos += from; for (; pos >= to; pos -= to) res.push((carry >> pos - to & mask) >>> 0); carry &= 2 ** pos - 1; } carry = carry << to - pos & mask; if (!padding2 && pos >= from) throw new Error("Excess padding"); if (!padding2 && carry) throw new Error(`Non-zero padding: ${carry}`); if (padding2 && pos > 0) res.push(carry >>> 0); return res; } function radix3(num3) { assertNumber(num3); return { encode: (bytes32) => { if (!(bytes32 instanceof Uint8Array)) throw new Error("radix.encode input should be Uint8Array"); return convertRadix3(Array.from(bytes32), 2 ** 8, num3); }, decode: (digits) => { if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number") throw new Error("radix.decode input should be array of strings"); return Uint8Array.from(convertRadix3(digits, num3, 2 ** 8)); } }; } function radix22(bits, revPadding = false) { assertNumber(bits); if (bits <= 0 || bits > 32) throw new Error("radix2: bits should be in (0..32]"); if (radix2carry2(8, bits) > 32 || radix2carry2(bits, 8) > 32) throw new Error("radix2: carry overflow"); return { encode: (bytes32) => { if (!(bytes32 instanceof Uint8Array)) throw new Error("radix2.encode input should be Uint8Array"); return convertRadix22(Array.from(bytes32), 8, bits, !revPadding); }, decode: (digits) => { if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number") throw new Error("radix2.decode input should be array of strings"); return Uint8Array.from(convertRadix22(digits, bits, 8, revPadding)); } }; } function unsafeWrapper2(fn) { if (typeof fn !== "function") throw new Error("unsafeWrapper fn should be function"); return function(...args) { try { return fn.apply(null, args); } catch (e2) { } }; } var base16 = chain2(radix22(4), alphabet2("0123456789ABCDEF"), join2("")); var base32 = chain2(radix22(5), alphabet2("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding(5), join2("")); var base32hex = chain2(radix22(5), alphabet2("0123456789ABCDEFGHIJKLMNOPQRSTUV"), padding(5), join2("")); var base32crockford = chain2(radix22(5), alphabet2("0123456789ABCDEFGHJKMNPQRSTVWXYZ"), join2(""), normalize2((s) => s.toUpperCase().replace(/O/g, "0").replace(/[IL]/g, "1"))); var base64 = chain2(radix22(6), alphabet2("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), padding(6), join2("")); var base64url = chain2(radix22(6), alphabet2("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), padding(6), join2("")); var genBase582 = (abc) => chain2(radix3(58), alphabet2(abc), join2("")); var base582 = genBase582("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"); var base58flickr = genBase582("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"); var base58xrp = genBase582("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"); var XMR_BLOCK_LEN = [0, 2, 3, 5, 6, 7, 9, 10, 11]; var base58xmr = { encode(data) { let res = ""; for (let i3 = 0; i3 < data.length; i3 += 8) { const block = data.subarray(i3, i3 + 8); res += base582.encode(block).padStart(XMR_BLOCK_LEN[block.length], "1"); } return res; }, decode(str) { let res = []; for (let i3 = 0; i3 < str.length; i3 += 11) { const slice = str.slice(i3, i3 + 11); const blockLen = XMR_BLOCK_LEN.indexOf(slice.length); const block = base582.decode(slice); for (let j2 = 0; j2 < block.length - blockLen; j2++) { if (block[j2] !== 0) throw new Error("base58xmr: wrong padding"); } res = res.concat(Array.from(block.slice(block.length - blockLen))); } return Uint8Array.from(res); } }; var BECH_ALPHABET2 = chain2(alphabet2("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), join2("")); var POLYMOD_GENERATORS2 = [996825010, 642813549, 513874426, 1027748829, 705979059]; function bech32Polymod2(pre) { const b = pre >> 25; let chk = (pre & 33554431) << 5; for (let i3 = 0; i3 < POLYMOD_GENERATORS2.length; i3++) { if ((b >> i3 & 1) === 1) chk ^= POLYMOD_GENERATORS2[i3]; } return chk; } function bechChecksum2(prefix, words, encodingConst = 1) { const len = prefix.length; let chk = 1; for (let i3 = 0; i3 < len; i3++) { const c = prefix.charCodeAt(i3); if (c < 33 || c > 126) throw new Error(`Invalid prefix (${prefix})`); chk = bech32Polymod2(chk) ^ c >> 5; } chk = bech32Polymod2(chk); for (let i3 = 0; i3 < len; i3++) chk = bech32Polymod2(chk) ^ prefix.charCodeAt(i3) & 31; for (let v6 of words) chk = bech32Polymod2(chk) ^ v6; for (let i3 = 0; i3 < 6; i3++) chk = bech32Polymod2(chk); chk ^= encodingConst; return BECH_ALPHABET2.encode(convertRadix22([chk % 2 ** 30], 30, 5, false)); } function genBech322(encoding) { const ENCODING_CONST = encoding === "bech32" ? 1 : 734539939; const _words = radix22(5); const fromWords = _words.decode; const toWords = _words.encode; const fromWordsUnsafe = unsafeWrapper2(fromWords); function encode4(prefix, words, limit2 = 90) { if (typeof prefix !== "string") throw new Error(`bech32.encode prefix should be string, not ${typeof prefix}`); if (!Array.isArray(words) || words.length && typeof words[0] !== "number") throw new Error(`bech32.encode words should be array of numbers, not ${typeof words}`); const actualLength = prefix.length + 7 + words.length; if (limit2 !== false && actualLength > limit2) throw new TypeError(`Length ${actualLength} exceeds limit ${limit2}`); prefix = prefix.toLowerCase(); return `${prefix}1${BECH_ALPHABET2.encode(words)}${bechChecksum2(prefix, words, ENCODING_CONST)}`; } function decode5(str, limit2 = 90) { if (typeof str !== "string") throw new Error(`bech32.decode input should be string, not ${typeof str}`); if (str.length < 8 || limit2 !== false && str.length > limit2) throw new TypeError(`Wrong string length: ${str.length} (${str}). Expected (8..${limit2})`); const lowered = str.toLowerCase(); if (str !== lowered && str !== str.toUpperCase()) throw new Error(`String must be lowercase or uppercase`); str = lowered; const sepIndex = str.lastIndexOf("1"); if (sepIndex === 0 || sepIndex === -1) throw new Error(`Letter "1" must be present between prefix and data only`); const prefix = str.slice(0, sepIndex); const _words2 = str.slice(sepIndex + 1); if (_words2.length < 6) throw new Error("Data must be at least 6 characters long"); const words = BECH_ALPHABET2.decode(_words2).slice(0, -6); const sum = bechChecksum2(prefix, words, ENCODING_CONST); if (!_words2.endsWith(sum)) throw new Error(`Invalid checksum in ${str}: expected "${sum}"`); return { prefix, words }; } const decodeUnsafe = unsafeWrapper2(decode5); function decodeToBytes(str) { const { prefix, words } = decode5(str, false); return { prefix, words, bytes: fromWords(words) }; } return { encode: encode4, decode: decode5, decodeToBytes, decodeUnsafe, fromWords, fromWordsUnsafe, toWords }; } var bech322 = genBech322("bech32"); var bech32m = genBech322("bech32m"); var utf8 = { encode: (data) => new TextDecoder().decode(data), decode: (str) => new TextEncoder().encode(str) }; var hex = chain2(radix22(4), alphabet2("0123456789abcdef"), join2(""), normalize2((s) => { if (typeof s !== "string" || s.length % 2) throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`); return s.toLowerCase(); })); var CODERS = { utf8, hex, base16, base32, base64, base64url, base58: base582, base58xmr }; var coderTypeError = `Invalid encoding type. Available types: ${Object.keys(CODERS).join(", ")}`; function number3(n) { if (!Number.isSafeInteger(n) || n < 0) throw new Error(`positive integer expected, not ${n}`); } function bool2(b) { if (typeof b !== "boolean") throw new Error(`boolean expected, not ${b}`); } function isBytes3(a) { return a instanceof Uint8Array || a != null && typeof a === "object" && a.constructor.name === "Uint8Array"; } function bytes3(b, ...lengths) { if (!isBytes3(b)) throw new Error("Uint8Array expected"); if (lengths.length > 0 && !lengths.includes(b.length)) throw new Error(`Uint8Array expected of length ${lengths}, not of length=${b.length}`); } function exists3(instance, checkFinished = true) { if (instance.destroyed) throw new Error("Hash instance has been destroyed"); if (checkFinished && instance.finished) throw new Error("Hash#digest() has already been called"); } function output3(out, instance) { bytes3(out); const min = instance.outputLen; if (out.length < min) { throw new Error(`digestInto() expects output buffer of length at least ${min}`); } } var u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength); var u322 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4)); var createView3 = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength); var isLE3 = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68; if (!isLE3) throw new Error("Non little-endian hardware is not supported"); var hexes3 = /* @__PURE__ */ Array.from({ length: 256 }, (_2, i3) => i3.toString(16).padStart(2, "0")); function bytesToHex3(bytes4) { bytes3(bytes4); let hex2 = ""; for (let i3 = 0; i3 < bytes4.length; i3++) { hex2 += hexes3[bytes4[i3]]; } return hex2; } var asciis2 = { _0: 48, _9: 57, _A: 65, _F: 70, _a: 97, _f: 102 }; function asciiToBase162(char) { if (char >= asciis2._0 && char <= asciis2._9) return char - asciis2._0; if (char >= asciis2._A && char <= asciis2._F) return char - (asciis2._A - 10); if (char >= asciis2._a && char <= asciis2._f) return char - (asciis2._a - 10); return; } function hexToBytes3(hex2) { if (typeof hex2 !== "string") throw new Error("hex string expected, got " + typeof hex2); const hl = hex2.length; const al = hl / 2; if (hl % 2) throw new Error("padded hex string expected, got unpadded hex of length " + hl); const array = new Uint8Array(al); for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { const n1 = asciiToBase162(hex2.charCodeAt(hi)); const n2 = asciiToBase162(hex2.charCodeAt(hi + 1)); if (n1 === void 0 || n2 === void 0) { const char = hex2[hi] + hex2[hi + 1]; throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi); } array[ai] = n1 * 16 + n2; } return array; } function utf8ToBytes4(str) { if (typeof str !== "string") throw new Error(`string expected, got ${typeof str}`); return new Uint8Array(new TextEncoder().encode(str)); } function toBytes3(data) { if (typeof data === "string") data = utf8ToBytes4(data); else if (isBytes3(data)) data = data.slice(); else throw new Error(`Uint8Array expected, got ${typeof data}`); return data; } function checkOpts2(defaults, opts) { if (opts == null || typeof opts !== "object") throw new Error("options must be defined"); const merged = Object.assign(defaults, opts); return merged; } function equalBytes2(a, b) { if (a.length !== b.length) return false; let diff = 0; for (let i3 = 0; i3 < a.length; i3++) diff |= a[i3] ^ b[i3]; return diff === 0; } var wrapCipher = (params, c) => { Object.assign(c, params); return c; }; function setBigUint643(view, byteOffset, value, isLE4) { if (typeof view.setBigUint64 === "function") return view.setBigUint64(byteOffset, value, isLE4); const _32n2 = BigInt(32); const _u32_max = BigInt(4294967295); const wh = Number(value >> _32n2 & _u32_max); const wl = Number(value & _u32_max); const h2 = isLE4 ? 4 : 0; const l3 = isLE4 ? 0 : 4; view.setUint32(byteOffset + h2, wh, isLE4); view.setUint32(byteOffset + l3, wl, isLE4); } var BLOCK_SIZE = 16; var ZEROS16 = /* @__PURE__ */ new Uint8Array(16); var ZEROS32 = u322(ZEROS16); var POLY = 225; var mul2 = (s0, s1, s2, s3) => { const hiBit = s3 & 1; return { s3: s2 << 31 | s3 >>> 1, s2: s1 << 31 | s2 >>> 1, s1: s0 << 31 | s1 >>> 1, s0: s0 >>> 1 ^ POLY << 24 & -(hiBit & 1) }; }; var swapLE = (n) => (n >>> 0 & 255) << 24 | (n >>> 8 & 255) << 16 | (n >>> 16 & 255) << 8 | n >>> 24 & 255 | 0; function _toGHASHKey(k2) { k2.reverse(); const hiBit = k2[15] & 1; let carry = 0; for (let i3 = 0; i3 < k2.length; i3++) { const t = k2[i3]; k2[i3] = t >>> 1 | carry; carry = (t & 1) << 7; } k2[0] ^= -hiBit & 225; return k2; } var estimateWindow = (bytes4) => { if (bytes4 > 64 * 1024) return 8; if (bytes4 > 1024) return 4; return 2; }; var GHASH = class { constructor(key, expectedLength) { this.blockLen = BLOCK_SIZE; this.outputLen = BLOCK_SIZE; this.s0 = 0; this.s1 = 0; this.s2 = 0; this.s3 = 0; this.finished = false; key = toBytes3(key); bytes3(key, 16); const kView = createView3(key); let k0 = kView.getUint32(0, false); let k1 = kView.getUint32(4, false); let k2 = kView.getUint32(8, false); let k3 = kView.getUint32(12, false); const doubles = []; for (let i3 = 0; i3 < 128; i3++) { doubles.push({ s0: swapLE(k0), s1: swapLE(k1), s2: swapLE(k2), s3: swapLE(k3) }); ({ s0: k0, s1: k1, s2: k2, s3: k3 } = mul2(k0, k1, k2, k3)); } const W3 = estimateWindow(expectedLength || 1024); if (![1, 2, 4, 8].includes(W3)) throw new Error(`ghash: wrong window size=${W3}, should be 2, 4 or 8`); this.W = W3; const bits = 128; const windows = bits / W3; const windowSize = this.windowSize = 2 ** W3; const items = []; for (let w2 = 0; w2 < windows; w2++) { for (let byte = 0; byte < windowSize; byte++) { let s0 = 0, s1 = 0, s2 = 0, s3 = 0; for (let j2 = 0; j2 < W3; j2++) { const bit = byte >>> W3 - j2 - 1 & 1; if (!bit) continue; const { s0: d0, s1: d1, s2: d25, s3: d35 } = doubles[W3 * w2 + j2]; s0 ^= d0, s1 ^= d1, s2 ^= d25, s3 ^= d35; } items.push({ s0, s1, s2, s3 }); } } this.t = items; } _updateBlock(s0, s1, s2, s3) { s0 ^= this.s0, s1 ^= this.s1, s2 ^= this.s2, s3 ^= this.s3; const { W: W3, t, windowSize } = this; let o0 = 0, o1 = 0, o2 = 0, o3 = 0; const mask = (1 << W3) - 1; let w2 = 0; for (const num3 of [s0, s1, s2, s3]) { for (let bytePos = 0; bytePos < 4; bytePos++) { const byte = num3 >>> 8 * bytePos & 255; for (let bitPos = 8 / W3 - 1; bitPos >= 0; bitPos--) { const bit = byte >>> W3 * bitPos & mask; const { s0: e0, s1: e1, s2: e2, s3: e3 } = t[w2 * windowSize + bit]; o0 ^= e0, o1 ^= e1, o2 ^= e2, o3 ^= e3; w2 += 1; } } } this.s0 = o0; this.s1 = o1; this.s2 = o2; this.s3 = o3; } update(data) { data = toBytes3(data); exists3(this); const b32 = u322(data); const blocks = Math.floor(data.length / BLOCK_SIZE); const left = data.length % BLOCK_SIZE; for (let i3 = 0; i3 < blocks; i3++) { this._updateBlock(b32[i3 * 4 + 0], b32[i3 * 4 + 1], b32[i3 * 4 + 2], b32[i3 * 4 + 3]); } if (left) { ZEROS16.set(data.subarray(blocks * BLOCK_SIZE)); this._updateBlock(ZEROS32[0], ZEROS32[1], ZEROS32[2], ZEROS32[3]); ZEROS32.fill(0); } return this; } destroy() { const { t } = this; for (const elm of t) { elm.s0 = 0, elm.s1 = 0, elm.s2 = 0, elm.s3 = 0; } } digestInto(out) { exists3(this); output3(out, this); this.finished = true; const { s0, s1, s2, s3 } = this; const o32 = u322(out); o32[0] = s0; o32[1] = s1; o32[2] = s2; o32[3] = s3; return out; } digest() { const res = new Uint8Array(BLOCK_SIZE); this.digestInto(res); this.destroy(); return res; } }; var Polyval = class extends GHASH { constructor(key, expectedLength) { key = toBytes3(key); const ghKey = _toGHASHKey(key.slice()); super(ghKey, expectedLength); ghKey.fill(0); } update(data) { data = toBytes3(data); exists3(this); const b32 = u322(data); const left = data.length % BLOCK_SIZE; const blocks = Math.floor(data.length / BLOCK_SIZE); for (let i3 = 0; i3 < blocks; i3++) { this._updateBlock(swapLE(b32[i3 * 4 + 3]), swapLE(b32[i3 * 4 + 2]), swapLE(b32[i3 * 4 + 1]), swapLE(b32[i3 * 4 + 0])); } if (left) { ZEROS16.set(data.subarray(blocks * BLOCK_SIZE)); this._updateBlock(swapLE(ZEROS32[3]), swapLE(ZEROS32[2]), swapLE(ZEROS32[1]), swapLE(ZEROS32[0])); ZEROS32.fill(0); } return this; } digestInto(out) { exists3(this); output3(out, this); this.finished = true; const { s0, s1, s2, s3 } = this; const o32 = u322(out); o32[0] = s0; o32[1] = s1; o32[2] = s2; o32[3] = s3; return out.reverse(); } }; function wrapConstructorWithKey(hashCons) { const hashC = (msg, key) => hashCons(key, msg.length).update(toBytes3(msg)).digest(); const tmp = hashCons(new Uint8Array(16), 0); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.create = (key, expectedLength) => hashCons(key, expectedLength); return hashC; } var ghash = wrapConstructorWithKey((key, expectedLength) => new GHASH(key, expectedLength)); var polyval = wrapConstructorWithKey((key, expectedLength) => new Polyval(key, expectedLength)); var BLOCK_SIZE2 = 16; var BLOCK_SIZE32 = 4; var EMPTY_BLOCK = new Uint8Array(BLOCK_SIZE2); var POLY2 = 283; function mul22(n) { return n << 1 ^ POLY2 & -(n >> 7); } function mul(a, b) { let res = 0; for (; b > 0; b >>= 1) { res ^= a & -(b & 1); a = mul22(a); } return res; } var sbox = /* @__PURE__ */ (() => { let t = new Uint8Array(256); for (let i3 = 0, x2 = 1; i3 < 256; i3++, x2 ^= mul22(x2)) t[i3] = x2; const box = new Uint8Array(256); box[0] = 99; for (let i3 = 0; i3 < 255; i3++) { let x2 = t[255 - i3]; x2 |= x2 << 8; box[t[i3]] = (x2 ^ x2 >> 4 ^ x2 >> 5 ^ x2 >> 6 ^ x2 >> 7 ^ 99) & 255; } return box; })(); var invSbox = /* @__PURE__ */ sbox.map((_2, j2) => sbox.indexOf(j2)); var rotr32_8 = (n) => n << 24 | n >>> 8; var rotl32_8 = (n) => n << 8 | n >>> 24; function genTtable(sbox2, fn) { if (sbox2.length !== 256) throw new Error("Wrong sbox length"); const T0 = new Uint32Array(256).map((_2, j2) => fn(sbox2[j2])); const T1 = T0.map(rotl32_8); const T22 = T1.map(rotl32_8); const T32 = T22.map(rotl32_8); const T01 = new Uint32Array(256 * 256); const T23 = new Uint32Array(256 * 256); const sbox22 = new Uint16Array(256 * 256); for (let i3 = 0; i3 < 256; i3++) { for (let j2 = 0; j2 < 256; j2++) { const idx = i3 * 256 + j2; T01[idx] = T0[i3] ^ T1[j2]; T23[idx] = T22[i3] ^ T32[j2]; sbox22[idx] = sbox2[i3] << 8 | sbox2[j2]; } } return { sbox: sbox2, sbox2: sbox22, T0, T1, T2: T22, T3: T32, T01, T23 }; } var tableEncoding = /* @__PURE__ */ genTtable(sbox, (s) => mul(s, 3) << 24 | s << 16 | s << 8 | mul(s, 2)); var tableDecoding = /* @__PURE__ */ genTtable(invSbox, (s) => mul(s, 11) << 24 | mul(s, 13) << 16 | mul(s, 9) << 8 | mul(s, 14)); var xPowers = /* @__PURE__ */ (() => { const p5 = new Uint8Array(16); for (let i3 = 0, x2 = 1; i3 < 16; i3++, x2 = mul22(x2)) p5[i3] = x2; return p5; })(); function expandKeyLE(key) { bytes3(key); const len = key.length; if (![16, 24, 32].includes(len)) throw new Error(`aes: wrong key size: should be 16, 24 or 32, got: ${len}`); const { sbox2 } = tableEncoding; const k32 = u322(key); const Nk = k32.length; const subByte = (n) => applySbox(sbox2, n, n, n, n); const xk = new Uint32Array(len + 28); xk.set(k32); for (let i3 = Nk; i3 < xk.length; i3++) { let t = xk[i3 - 1]; if (i3 % Nk === 0) t = subByte(rotr32_8(t)) ^ xPowers[i3 / Nk - 1]; else if (Nk > 6 && i3 % Nk === 4) t = subByte(t); xk[i3] = xk[i3 - Nk] ^ t; } return xk; } function expandKeyDecLE(key) { const encKey = expandKeyLE(key); const xk = encKey.slice(); const Nk = encKey.length; const { sbox2 } = tableEncoding; const { T0, T1, T2: T22, T3: T32 } = tableDecoding; for (let i3 = 0; i3 < Nk; i3 += 4) { for (let j2 = 0; j2 < 4; j2++) xk[i3 + j2] = encKey[Nk - i3 - 4 + j2]; } encKey.fill(0); for (let i3 = 4; i3 < Nk - 4; i3++) { const x2 = xk[i3]; const w2 = applySbox(sbox2, x2, x2, x2, x2); xk[i3] = T0[w2 & 255] ^ T1[w2 >>> 8 & 255] ^ T22[w2 >>> 16 & 255] ^ T32[w2 >>> 24]; } return xk; } function apply0123(T01, T23, s0, s1, s2, s3) { return T01[s0 << 8 & 65280 | s1 >>> 8 & 255] ^ T23[s2 >>> 8 & 65280 | s3 >>> 24 & 255]; } function applySbox(sbox2, s0, s1, s2, s3) { return sbox2[s0 & 255 | s1 & 65280] | sbox2[s2 >>> 16 & 255 | s3 >>> 16 & 65280] << 16; } function encrypt7(xk, s0, s1, s2, s3) { const { sbox2, T01, T23 } = tableEncoding; let k2 = 0; s0 ^= xk[k2++], s1 ^= xk[k2++], s2 ^= xk[k2++], s3 ^= xk[k2++]; const rounds = xk.length / 4 - 2; for (let i3 = 0; i3 < rounds; i3++) { const t02 = xk[k2++] ^ apply0123(T01, T23, s0, s1, s2, s3); const t12 = xk[k2++] ^ apply0123(T01, T23, s1, s2, s3, s0); const t22 = xk[k2++] ^ apply0123(T01, T23, s2, s3, s0, s1); const t32 = xk[k2++] ^ apply0123(T01, T23, s3, s0, s1, s2); s0 = t02, s1 = t12, s2 = t22, s3 = t32; } const t0 = xk[k2++] ^ applySbox(sbox2, s0, s1, s2, s3); const t1 = xk[k2++] ^ applySbox(sbox2, s1, s2, s3, s0); const t2 = xk[k2++] ^ applySbox(sbox2, s2, s3, s0, s1); const t3 = xk[k2++] ^ applySbox(sbox2, s3, s0, s1, s2); return { s0: t0, s1: t1, s2: t2, s3: t3 }; } function decrypt7(xk, s0, s1, s2, s3) { const { sbox2, T01, T23 } = tableDecoding; let k2 = 0; s0 ^= xk[k2++], s1 ^= xk[k2++], s2 ^= xk[k2++], s3 ^= xk[k2++]; const rounds = xk.length / 4 - 2; for (let i3 = 0; i3 < rounds; i3++) { const t02 = xk[k2++] ^ apply0123(T01, T23, s0, s3, s2, s1); const t12 = xk[k2++] ^ apply0123(T01, T23, s1, s0, s3, s2); const t22 = xk[k2++] ^ apply0123(T01, T23, s2, s1, s0, s3); const t32 = xk[k2++] ^ apply0123(T01, T23, s3, s2, s1, s0); s0 = t02, s1 = t12, s2 = t22, s3 = t32; } const t0 = xk[k2++] ^ applySbox(sbox2, s0, s3, s2, s1); const t1 = xk[k2++] ^ applySbox(sbox2, s1, s0, s3, s2); const t2 = xk[k2++] ^ applySbox(sbox2, s2, s1, s0, s3); const t3 = xk[k2++] ^ applySbox(sbox2, s3, s2, s1, s0); return { s0: t0, s1: t1, s2: t2, s3: t3 }; } function getDst(len, dst) { if (!dst) return new Uint8Array(len); bytes3(dst); if (dst.length < len) throw new Error(`aes: wrong destination length, expected at least ${len}, got: ${dst.length}`); return dst; } function ctrCounter(xk, nonce, src, dst) { bytes3(nonce, BLOCK_SIZE2); bytes3(src); const srcLen = src.length; dst = getDst(srcLen, dst); const ctr3 = nonce; const c32 = u322(ctr3); let { s0, s1, s2, s3 } = encrypt7(xk, c32[0], c32[1], c32[2], c32[3]); const src32 = u322(src); const dst32 = u322(dst); for (let i3 = 0; i3 + 4 <= src32.length; i3 += 4) { dst32[i3 + 0] = src32[i3 + 0] ^ s0; dst32[i3 + 1] = src32[i3 + 1] ^ s1; dst32[i3 + 2] = src32[i3 + 2] ^ s2; dst32[i3 + 3] = src32[i3 + 3] ^ s3; let carry = 1; for (let i22 = ctr3.length - 1; i22 >= 0; i22--) { carry = carry + (ctr3[i22] & 255) | 0; ctr3[i22] = carry & 255; carry >>>= 8; } ({ s0, s1, s2, s3 } = encrypt7(xk, c32[0], c32[1], c32[2], c32[3])); } const start = BLOCK_SIZE2 * Math.floor(src32.length / BLOCK_SIZE32); if (start < srcLen) { const b32 = new Uint32Array([s0, s1, s2, s3]); const buf = u8(b32); for (let i3 = start, pos = 0; i3 < srcLen; i3++, pos++) dst[i3] = src[i3] ^ buf[pos]; } return dst; } function ctr32(xk, isLE4, nonce, src, dst) { bytes3(nonce, BLOCK_SIZE2); bytes3(src); dst = getDst(src.length, dst); const ctr3 = nonce; const c32 = u322(ctr3); const view = createView3(ctr3); const src32 = u322(src); const dst32 = u322(dst); const ctrPos = isLE4 ? 0 : 12; const srcLen = src.length; let ctrNum = view.getUint32(ctrPos, isLE4); let { s0, s1, s2, s3 } = encrypt7(xk, c32[0], c32[1], c32[2], c32[3]); for (let i3 = 0; i3 + 4 <= src32.length; i3 += 4) { dst32[i3 + 0] = src32[i3 + 0] ^ s0; dst32[i3 + 1] = src32[i3 + 1] ^ s1; dst32[i3 + 2] = src32[i3 + 2] ^ s2; dst32[i3 + 3] = src32[i3 + 3] ^ s3; ctrNum = ctrNum + 1 >>> 0; view.setUint32(ctrPos, ctrNum, isLE4); ({ s0, s1, s2, s3 } = encrypt7(xk, c32[0], c32[1], c32[2], c32[3])); } const start = BLOCK_SIZE2 * Math.floor(src32.length / BLOCK_SIZE32); if (start < srcLen) { const b32 = new Uint32Array([s0, s1, s2, s3]); const buf = u8(b32); for (let i3 = start, pos = 0; i3 < srcLen; i3++, pos++) dst[i3] = src[i3] ^ buf[pos]; } return dst; } var ctr = wrapCipher({ blockSize: 16, nonceLength: 16 }, function ctr2(key, nonce) { bytes3(key); bytes3(nonce, BLOCK_SIZE2); function processCtr(buf, dst) { const xk = expandKeyLE(key); const n = nonce.slice(); const out = ctrCounter(xk, n, buf, dst); xk.fill(0); n.fill(0); return out; } return { encrypt: (plaintext, dst) => processCtr(plaintext, dst), decrypt: (ciphertext, dst) => processCtr(ciphertext, dst) }; }); function validateBlockDecrypt(data) { bytes3(data); if (data.length % BLOCK_SIZE2 !== 0) { throw new Error(`aes/(cbc-ecb).decrypt ciphertext should consist of blocks with size ${BLOCK_SIZE2}`); } } function validateBlockEncrypt(plaintext, pcks5, dst) { let outLen = plaintext.length; const remaining = outLen % BLOCK_SIZE2; if (!pcks5 && remaining !== 0) throw new Error("aec/(cbc-ecb): unpadded plaintext with disabled padding"); const b = u322(plaintext); if (pcks5) { let left = BLOCK_SIZE2 - remaining; if (!left) left = BLOCK_SIZE2; outLen = outLen + left; } const out = getDst(outLen, dst); const o = u322(out); return { b, o, out }; } function validatePCKS(data, pcks5) { if (!pcks5) return data; const len = data.length; if (!len) throw new Error(`aes/pcks5: empty ciphertext not allowed`); const lastByte = data[len - 1]; if (lastByte <= 0 || lastByte > 16) throw new Error(`aes/pcks5: wrong padding byte: ${lastByte}`); const out = data.subarray(0, -lastByte); for (let i3 = 0; i3 < lastByte; i3++) if (data[len - i3 - 1] !== lastByte) throw new Error(`aes/pcks5: wrong padding`); return out; } function padPCKS(left) { const tmp = new Uint8Array(16); const tmp32 = u322(tmp); tmp.set(left); const paddingByte = BLOCK_SIZE2 - left.length; for (let i3 = BLOCK_SIZE2 - paddingByte; i3 < BLOCK_SIZE2; i3++) tmp[i3] = paddingByte; return tmp32; } var ecb = wrapCipher({ blockSize: 16 }, function ecb2(key, opts = {}) { bytes3(key); const pcks5 = !opts.disablePadding; return { encrypt: (plaintext, dst) => { bytes3(plaintext); const { b, o, out: _out } = validateBlockEncrypt(plaintext, pcks5, dst); const xk = expandKeyLE(key); let i3 = 0; for (; i3 + 4 <= b.length; ) { const { s0, s1, s2, s3 } = encrypt7(xk, b[i3 + 0], b[i3 + 1], b[i3 + 2], b[i3 + 3]); o[i3++] = s0, o[i3++] = s1, o[i3++] = s2, o[i3++] = s3; } if (pcks5) { const tmp32 = padPCKS(plaintext.subarray(i3 * 4)); const { s0, s1, s2, s3 } = encrypt7(xk, tmp32[0], tmp32[1], tmp32[2], tmp32[3]); o[i3++] = s0, o[i3++] = s1, o[i3++] = s2, o[i3++] = s3; } xk.fill(0); return _out; }, decrypt: (ciphertext, dst) => { validateBlockDecrypt(ciphertext); const xk = expandKeyDecLE(key); const out = getDst(ciphertext.length, dst); const b = u322(ciphertext); const o = u322(out); for (let i3 = 0; i3 + 4 <= b.length; ) { const { s0, s1, s2, s3 } = decrypt7(xk, b[i3 + 0], b[i3 + 1], b[i3 + 2], b[i3 + 3]); o[i3++] = s0, o[i3++] = s1, o[i3++] = s2, o[i3++] = s3; } xk.fill(0); return validatePCKS(out, pcks5); } }; }); var cbc = wrapCipher({ blockSize: 16, nonceLength: 16 }, function cbc2(key, iv, opts = {}) { bytes3(key); bytes3(iv, 16); const pcks5 = !opts.disablePadding; return { encrypt: (plaintext, dst) => { const xk = expandKeyLE(key); const { b, o, out: _out } = validateBlockEncrypt(plaintext, pcks5, dst); const n32 = u322(iv); let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3]; let i3 = 0; for (; i3 + 4 <= b.length; ) { s0 ^= b[i3 + 0], s1 ^= b[i3 + 1], s2 ^= b[i3 + 2], s3 ^= b[i3 + 3]; ({ s0, s1, s2, s3 } = encrypt7(xk, s0, s1, s2, s3)); o[i3++] = s0, o[i3++] = s1, o[i3++] = s2, o[i3++] = s3; } if (pcks5) { const tmp32 = padPCKS(plaintext.subarray(i3 * 4)); s0 ^= tmp32[0], s1 ^= tmp32[1], s2 ^= tmp32[2], s3 ^= tmp32[3]; ({ s0, s1, s2, s3 } = encrypt7(xk, s0, s1, s2, s3)); o[i3++] = s0, o[i3++] = s1, o[i3++] = s2, o[i3++] = s3; } xk.fill(0); return _out; }, decrypt: (ciphertext, dst) => { validateBlockDecrypt(ciphertext); const xk = expandKeyDecLE(key); const n32 = u322(iv); const out = getDst(ciphertext.length, dst); const b = u322(ciphertext); const o = u322(out); let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3]; for (let i3 = 0; i3 + 4 <= b.length; ) { const ps0 = s0, ps1 = s1, ps2 = s2, ps3 = s3; s0 = b[i3 + 0], s1 = b[i3 + 1], s2 = b[i3 + 2], s3 = b[i3 + 3]; const { s0: o0, s1: o1, s2: o2, s3: o3 } = decrypt7(xk, s0, s1, s2, s3); o[i3++] = o0 ^ ps0, o[i3++] = o1 ^ ps1, o[i3++] = o2 ^ ps2, o[i3++] = o3 ^ ps3; } xk.fill(0); return validatePCKS(out, pcks5); } }; }); var cfb = wrapCipher({ blockSize: 16, nonceLength: 16 }, function cfb2(key, iv) { bytes3(key); bytes3(iv, 16); function processCfb(src, isEncrypt, dst) { const xk = expandKeyLE(key); const srcLen = src.length; dst = getDst(srcLen, dst); const src32 = u322(src); const dst32 = u322(dst); const next32 = isEncrypt ? dst32 : src32; const n32 = u322(iv); let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3]; for (let i3 = 0; i3 + 4 <= src32.length; ) { const { s0: e0, s1: e1, s2: e2, s3: e3 } = encrypt7(xk, s0, s1, s2, s3); dst32[i3 + 0] = src32[i3 + 0] ^ e0; dst32[i3 + 1] = src32[i3 + 1] ^ e1; dst32[i3 + 2] = src32[i3 + 2] ^ e2; dst32[i3 + 3] = src32[i3 + 3] ^ e3; s0 = next32[i3++], s1 = next32[i3++], s2 = next32[i3++], s3 = next32[i3++]; } const start = BLOCK_SIZE2 * Math.floor(src32.length / BLOCK_SIZE32); if (start < srcLen) { ({ s0, s1, s2, s3 } = encrypt7(xk, s0, s1, s2, s3)); const buf = u8(new Uint32Array([s0, s1, s2, s3])); for (let i3 = start, pos = 0; i3 < srcLen; i3++, pos++) dst[i3] = src[i3] ^ buf[pos]; buf.fill(0); } xk.fill(0); return dst; } return { encrypt: (plaintext, dst) => processCfb(plaintext, true, dst), decrypt: (ciphertext, dst) => processCfb(ciphertext, false, dst) }; }); function computeTag(fn, isLE4, key, data, AAD) { const h2 = fn.create(key, data.length + (AAD?.length || 0)); if (AAD) h2.update(AAD); h2.update(data); const num3 = new Uint8Array(16); const view = createView3(num3); if (AAD) setBigUint643(view, 0, BigInt(AAD.length * 8), isLE4); setBigUint643(view, 8, BigInt(data.length * 8), isLE4); h2.update(num3); return h2.digest(); } var gcm = wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function gcm2(key, nonce, AAD) { bytes3(nonce); if (nonce.length === 0) throw new Error("aes/gcm: empty nonce"); const tagLength = 16; function _computeTag(authKey, tagMask, data) { const tag = computeTag(ghash, false, authKey, data, AAD); for (let i3 = 0; i3 < tagMask.length; i3++) tag[i3] ^= tagMask[i3]; return tag; } function deriveKeys() { const xk = expandKeyLE(key); const authKey = EMPTY_BLOCK.slice(); const counter = EMPTY_BLOCK.slice(); ctr32(xk, false, counter, counter, authKey); if (nonce.length === 12) { counter.set(nonce); } else { const nonceLen = EMPTY_BLOCK.slice(); const view = createView3(nonceLen); setBigUint643(view, 8, BigInt(nonce.length * 8), false); ghash.create(authKey).update(nonce).update(nonceLen).digestInto(counter); } const tagMask = ctr32(xk, false, counter, EMPTY_BLOCK); return { xk, authKey, counter, tagMask }; } return { encrypt: (plaintext) => { bytes3(plaintext); const { xk, authKey, counter, tagMask } = deriveKeys(); const out = new Uint8Array(plaintext.length + tagLength); ctr32(xk, false, counter, plaintext, out); const tag = _computeTag(authKey, tagMask, out.subarray(0, out.length - tagLength)); out.set(tag, plaintext.length); xk.fill(0); return out; }, decrypt: (ciphertext) => { bytes3(ciphertext); if (ciphertext.length < tagLength) throw new Error(`aes/gcm: ciphertext less than tagLen (${tagLength})`); const { xk, authKey, counter, tagMask } = deriveKeys(); const data = ciphertext.subarray(0, -tagLength); const passedTag = ciphertext.subarray(-tagLength); const tag = _computeTag(authKey, tagMask, data); if (!equalBytes2(tag, passedTag)) throw new Error("aes/gcm: invalid ghash tag"); const out = ctr32(xk, false, counter, data); authKey.fill(0); tagMask.fill(0); xk.fill(0); return out; } }; }); var limit = (name, min, max) => (value) => { if (!Number.isSafeInteger(value) || min > value || value > max) throw new Error(`${name}: invalid value=${value}, must be [${min}..${max}]`); }; var siv = wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function siv2(key, nonce, AAD) { const tagLength = 16; const AAD_LIMIT = limit("AAD", 0, 2 ** 36); const PLAIN_LIMIT = limit("plaintext", 0, 2 ** 36); const NONCE_LIMIT = limit("nonce", 12, 12); const CIPHER_LIMIT = limit("ciphertext", 16, 2 ** 36 + 16); bytes3(nonce); NONCE_LIMIT(nonce.length); if (AAD) { bytes3(AAD); AAD_LIMIT(AAD.length); } function deriveKeys() { const len = key.length; if (len !== 16 && len !== 24 && len !== 32) throw new Error(`key length must be 16, 24 or 32 bytes, got: ${len} bytes`); const xk = expandKeyLE(key); const encKey = new Uint8Array(len); const authKey = new Uint8Array(16); const n32 = u322(nonce); let s0 = 0, s1 = n32[0], s2 = n32[1], s3 = n32[2]; let counter = 0; for (const derivedKey of [authKey, encKey].map(u322)) { const d322 = u322(derivedKey); for (let i3 = 0; i3 < d322.length; i3 += 2) { const { s0: o0, s1: o1 } = encrypt7(xk, s0, s1, s2, s3); d322[i3 + 0] = o0; d322[i3 + 1] = o1; s0 = ++counter; } } xk.fill(0); return { authKey, encKey: expandKeyLE(encKey) }; } function _computeTag(encKey, authKey, data) { const tag = computeTag(polyval, true, authKey, data, AAD); for (let i3 = 0; i3 < 12; i3++) tag[i3] ^= nonce[i3]; tag[15] &= 127; const t32 = u322(tag); let s0 = t32[0], s1 = t32[1], s2 = t32[2], s3 = t32[3]; ({ s0, s1, s2, s3 } = encrypt7(encKey, s0, s1, s2, s3)); t32[0] = s0, t32[1] = s1, t32[2] = s2, t32[3] = s3; return tag; } function processSiv(encKey, tag, input) { let block = tag.slice(); block[15] |= 128; return ctr32(encKey, true, block, input); } return { encrypt: (plaintext) => { bytes3(plaintext); PLAIN_LIMIT(plaintext.length); const { encKey, authKey } = deriveKeys(); const tag = _computeTag(encKey, authKey, plaintext); const out = new Uint8Array(plaintext.length + tagLength); out.set(tag, plaintext.length); out.set(processSiv(encKey, tag, plaintext)); encKey.fill(0); authKey.fill(0); return out; }, decrypt: (ciphertext) => { bytes3(ciphertext); CIPHER_LIMIT(ciphertext.length); const tag = ciphertext.subarray(-tagLength); const { encKey, authKey } = deriveKeys(); const plaintext = processSiv(encKey, tag, ciphertext.subarray(0, -tagLength)); const expectedTag = _computeTag(encKey, authKey, plaintext); encKey.fill(0); authKey.fill(0); if (!equalBytes2(tag, expectedTag)) throw new Error("invalid polyval tag"); return plaintext; } }; }); var u8to16 = (a, i3) => a[i3++] & 255 | (a[i3++] & 255) << 8; var Poly1305 = class { constructor(key) { this.blockLen = 16; this.outputLen = 16; this.buffer = new Uint8Array(16); this.r = new Uint16Array(10); this.h = new Uint16Array(10); this.pad = new Uint16Array(8); this.pos = 0; this.finished = false; key = toBytes3(key); bytes3(key, 32); const t0 = u8to16(key, 0); const t1 = u8to16(key, 2); const t2 = u8to16(key, 4); const t3 = u8to16(key, 6); const t4 = u8to16(key, 8); const t5 = u8to16(key, 10); const t6 = u8to16(key, 12); const t7 = u8to16(key, 14); this.r[0] = t0 & 8191; this.r[1] = (t0 >>> 13 | t1 << 3) & 8191; this.r[2] = (t1 >>> 10 | t2 << 6) & 7939; this.r[3] = (t2 >>> 7 | t3 << 9) & 8191; this.r[4] = (t3 >>> 4 | t4 << 12) & 255; this.r[5] = t4 >>> 1 & 8190; this.r[6] = (t4 >>> 14 | t5 << 2) & 8191; this.r[7] = (t5 >>> 11 | t6 << 5) & 8065; this.r[8] = (t6 >>> 8 | t7 << 8) & 8191; this.r[9] = t7 >>> 5 & 127; for (let i3 = 0; i3 < 8; i3++) this.pad[i3] = u8to16(key, 16 + 2 * i3); } process(data, offset, isLast = false) { const hibit = isLast ? 0 : 1 << 11; const { h: h2, r } = this; const r0 = r[0]; const r1 = r[1]; const r2 = r[2]; const r3 = r[3]; const r4 = r[4]; const r5 = r[5]; const r6 = r[6]; const r7 = r[7]; const r8 = r[8]; const r9 = r[9]; const t0 = u8to16(data, offset + 0); const t1 = u8to16(data, offset + 2); const t2 = u8to16(data, offset + 4); const t3 = u8to16(data, offset + 6); const t4 = u8to16(data, offset + 8); const t5 = u8to16(data, offset + 10); const t6 = u8to16(data, offset + 12); const t7 = u8to16(data, offset + 14); let h0 = h2[0] + (t0 & 8191); let h1 = h2[1] + ((t0 >>> 13 | t1 << 3) & 8191); let h22 = h2[2] + ((t1 >>> 10 | t2 << 6) & 8191); let h3 = h2[3] + ((t2 >>> 7 | t3 << 9) & 8191); let h4 = h2[4] + ((t3 >>> 4 | t4 << 12) & 8191); let h5 = h2[5] + (t4 >>> 1 & 8191); let h6 = h2[6] + ((t4 >>> 14 | t5 << 2) & 8191); let h7 = h2[7] + ((t5 >>> 11 | t6 << 5) & 8191); let h8 = h2[8] + ((t6 >>> 8 | t7 << 8) & 8191); let h9 = h2[9] + (t7 >>> 5 | hibit); let c = 0; let d0 = c + h0 * r0 + h1 * (5 * r9) + h22 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6); c = d0 >>> 13; d0 &= 8191; d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1); c += d0 >>> 13; d0 &= 8191; let d1 = c + h0 * r1 + h1 * r0 + h22 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7); c = d1 >>> 13; d1 &= 8191; d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2); c += d1 >>> 13; d1 &= 8191; let d25 = c + h0 * r2 + h1 * r1 + h22 * r0 + h3 * (5 * r9) + h4 * (5 * r8); c = d25 >>> 13; d25 &= 8191; d25 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3); c += d25 >>> 13; d25 &= 8191; let d35 = c + h0 * r3 + h1 * r2 + h22 * r1 + h3 * r0 + h4 * (5 * r9); c = d35 >>> 13; d35 &= 8191; d35 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4); c += d35 >>> 13; d35 &= 8191; let d42 = c + h0 * r4 + h1 * r3 + h22 * r2 + h3 * r1 + h4 * r0; c = d42 >>> 13; d42 &= 8191; d42 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5); c += d42 >>> 13; d42 &= 8191; let d52 = c + h0 * r5 + h1 * r4 + h22 * r3 + h3 * r2 + h4 * r1; c = d52 >>> 13; d52 &= 8191; d52 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6); c += d52 >>> 13; d52 &= 8191; let d62 = c + h0 * r6 + h1 * r5 + h22 * r4 + h3 * r3 + h4 * r2; c = d62 >>> 13; d62 &= 8191; d62 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7); c += d62 >>> 13; d62 &= 8191; let d72 = c + h0 * r7 + h1 * r6 + h22 * r5 + h3 * r4 + h4 * r3; c = d72 >>> 13; d72 &= 8191; d72 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8); c += d72 >>> 13; d72 &= 8191; let d82 = c + h0 * r8 + h1 * r7 + h22 * r6 + h3 * r5 + h4 * r4; c = d82 >>> 13; d82 &= 8191; d82 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9); c += d82 >>> 13; d82 &= 8191; let d92 = c + h0 * r9 + h1 * r8 + h22 * r7 + h3 * r6 + h4 * r5; c = d92 >>> 13; d92 &= 8191; d92 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0; c += d92 >>> 13; d92 &= 8191; c = (c << 2) + c | 0; c = c + d0 | 0; d0 = c & 8191; c = c >>> 13; d1 += c; h2[0] = d0; h2[1] = d1; h2[2] = d25; h2[3] = d35; h2[4] = d42; h2[5] = d52; h2[6] = d62; h2[7] = d72; h2[8] = d82; h2[9] = d92; } finalize() { const { h: h2, pad: pad2 } = this; const g = new Uint16Array(10); let c = h2[1] >>> 13; h2[1] &= 8191; for (let i3 = 2; i3 < 10; i3++) { h2[i3] += c; c = h2[i3] >>> 13; h2[i3] &= 8191; } h2[0] += c * 5; c = h2[0] >>> 13; h2[0] &= 8191; h2[1] += c; c = h2[1] >>> 13; h2[1] &= 8191; h2[2] += c; g[0] = h2[0] + 5; c = g[0] >>> 13; g[0] &= 8191; for (let i3 = 1; i3 < 10; i3++) { g[i3] = h2[i3] + c; c = g[i3] >>> 13; g[i3] &= 8191; } g[9] -= 1 << 13; let mask = (c ^ 1) - 1; for (let i3 = 0; i3 < 10; i3++) g[i3] &= mask; mask = ~mask; for (let i3 = 0; i3 < 10; i3++) h2[i3] = h2[i3] & mask | g[i3]; h2[0] = (h2[0] | h2[1] << 13) & 65535; h2[1] = (h2[1] >>> 3 | h2[2] << 10) & 65535; h2[2] = (h2[2] >>> 6 | h2[3] << 7) & 65535; h2[3] = (h2[3] >>> 9 | h2[4] << 4) & 65535; h2[4] = (h2[4] >>> 12 | h2[5] << 1 | h2[6] << 14) & 65535; h2[5] = (h2[6] >>> 2 | h2[7] << 11) & 65535; h2[6] = (h2[7] >>> 5 | h2[8] << 8) & 65535; h2[7] = (h2[8] >>> 8 | h2[9] << 5) & 65535; let f = h2[0] + pad2[0]; h2[0] = f & 65535; for (let i3 = 1; i3 < 8; i3++) { f = (h2[i3] + pad2[i3] | 0) + (f >>> 16) | 0; h2[i3] = f & 65535; } } update(data) { exists3(this); const { buffer, blockLen } = this; data = toBytes3(data); const len = data.length; for (let pos = 0; pos < len; ) { const take = Math.min(blockLen - this.pos, len - pos); if (take === blockLen) { for (; blockLen <= len - pos; pos += blockLen) this.process(data, pos); continue; } buffer.set(data.subarray(pos, pos + take), this.pos); this.pos += take; pos += take; if (this.pos === blockLen) { this.process(buffer, 0, false); this.pos = 0; } } return this; } destroy() { this.h.fill(0); this.r.fill(0); this.buffer.fill(0); this.pad.fill(0); } digestInto(out) { exists3(this); output3(out, this); this.finished = true; const { buffer, h: h2 } = this; let { pos } = this; if (pos) { buffer[pos++] = 1; for (; pos < 16; pos++) buffer[pos] = 0; this.process(buffer, 0, true); } this.finalize(); let opos = 0; for (let i3 = 0; i3 < 8; i3++) { out[opos++] = h2[i3] >>> 0; out[opos++] = h2[i3] >>> 8; } return out; } digest() { const { buffer, outputLen } = this; this.digestInto(buffer); const res = buffer.slice(0, outputLen); this.destroy(); return res; } }; function wrapConstructorWithKey2(hashCons) { const hashC = (msg, key) => hashCons(key).update(toBytes3(msg)).digest(); const tmp = hashCons(new Uint8Array(32)); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.create = (key) => hashCons(key); return hashC; } var poly1305 = wrapConstructorWithKey2((key) => new Poly1305(key)); var _utf8ToBytes = (str) => Uint8Array.from(str.split("").map((c) => c.charCodeAt(0))); var sigma16 = _utf8ToBytes("expand 16-byte k"); var sigma32 = _utf8ToBytes("expand 32-byte k"); var sigma16_32 = u322(sigma16); var sigma32_32 = u322(sigma32); var sigma = sigma32_32.slice(); function rotl2(a, b) { return a << b | a >>> 32 - b; } function isAligned32(b) { return b.byteOffset % 4 === 0; } var BLOCK_LEN = 64; var BLOCK_LEN32 = 16; var MAX_COUNTER = 2 ** 32 - 1; var U32_EMPTY = new Uint32Array(); function runCipher(core, sigma2, key, nonce, data, output4, counter, rounds) { const len = data.length; const block = new Uint8Array(BLOCK_LEN); const b32 = u322(block); const isAligned = isAligned32(data) && isAligned32(output4); const d322 = isAligned ? u322(data) : U32_EMPTY; const o32 = isAligned ? u322(output4) : U32_EMPTY; for (let pos = 0; pos < len; counter++) { core(sigma2, key, nonce, b32, counter, rounds); if (counter >= MAX_COUNTER) throw new Error("arx: counter overflow"); const take = Math.min(BLOCK_LEN, len - pos); if (isAligned && take === BLOCK_LEN) { const pos32 = pos / 4; if (pos % 4 !== 0) throw new Error("arx: invalid block position"); for (let j2 = 0, posj; j2 < BLOCK_LEN32; j2++) { posj = pos32 + j2; o32[posj] = d322[posj] ^ b32[j2]; } pos += BLOCK_LEN; continue; } for (let j2 = 0, posj; j2 < take; j2++) { posj = pos + j2; output4[posj] = data[posj] ^ block[j2]; } pos += take; } } function createCipher(core, opts) { const { allowShortKeys, extendNonceFn, counterLength, counterRight, rounds } = checkOpts2({ allowShortKeys: false, counterLength: 8, counterRight: false, rounds: 20 }, opts); if (typeof core !== "function") throw new Error("core must be a function"); number3(counterLength); number3(rounds); bool2(counterRight); bool2(allowShortKeys); return (key, nonce, data, output4, counter = 0) => { bytes3(key); bytes3(nonce); bytes3(data); const len = data.length; if (!output4) output4 = new Uint8Array(len); bytes3(output4); number3(counter); if (counter < 0 || counter >= MAX_COUNTER) throw new Error("arx: counter overflow"); if (output4.length < len) throw new Error(`arx: output (${output4.length}) is shorter than data (${len})`); const toClean = []; let l3 = key.length, k2, sigma2; if (l3 === 32) { k2 = key.slice(); toClean.push(k2); sigma2 = sigma32_32; } else if (l3 === 16 && allowShortKeys) { k2 = new Uint8Array(32); k2.set(key); k2.set(key, 16); sigma2 = sigma16_32; toClean.push(k2); } else { throw new Error(`arx: invalid 32-byte key, got length=${l3}`); } if (!isAligned32(nonce)) { nonce = nonce.slice(); toClean.push(nonce); } const k32 = u322(k2); if (extendNonceFn) { if (nonce.length !== 24) throw new Error(`arx: extended nonce must be 24 bytes`); extendNonceFn(sigma2, k32, u322(nonce.subarray(0, 16)), k32); nonce = nonce.subarray(16); } const nonceNcLen = 16 - counterLength; if (nonceNcLen !== nonce.length) throw new Error(`arx: nonce must be ${nonceNcLen} or 16 bytes`); if (nonceNcLen !== 12) { const nc = new Uint8Array(12); nc.set(nonce, counterRight ? 0 : 12 - nonce.length); nonce = nc; toClean.push(nonce); } const n32 = u322(nonce); runCipher(core, sigma2, k32, n32, data, output4, counter, rounds); while (toClean.length > 0) toClean.pop().fill(0); return output4; }; } function chachaCore(s, k2, n, out, cnt, rounds = 20) { let y00 = s[0], y01 = s[1], y02 = s[2], y03 = s[3], y04 = k2[0], y05 = k2[1], y06 = k2[2], y07 = k2[3], y08 = k2[4], y09 = k2[5], y10 = k2[6], y11 = k2[7], y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2]; let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15; for (let r = 0; r < rounds; r += 2) { x00 = x00 + x04 | 0; x12 = rotl2(x12 ^ x00, 16); x08 = x08 + x12 | 0; x04 = rotl2(x04 ^ x08, 12); x00 = x00 + x04 | 0; x12 = rotl2(x12 ^ x00, 8); x08 = x08 + x12 | 0; x04 = rotl2(x04 ^ x08, 7); x01 = x01 + x05 | 0; x13 = rotl2(x13 ^ x01, 16); x09 = x09 + x13 | 0; x05 = rotl2(x05 ^ x09, 12); x01 = x01 + x05 | 0; x13 = rotl2(x13 ^ x01, 8); x09 = x09 + x13 | 0; x05 = rotl2(x05 ^ x09, 7); x02 = x02 + x06 | 0; x14 = rotl2(x14 ^ x02, 16); x10 = x10 + x14 | 0; x06 = rotl2(x06 ^ x10, 12); x02 = x02 + x06 | 0; x14 = rotl2(x14 ^ x02, 8); x10 = x10 + x14 | 0; x06 = rotl2(x06 ^ x10, 7); x03 = x03 + x07 | 0; x15 = rotl2(x15 ^ x03, 16); x11 = x11 + x15 | 0; x07 = rotl2(x07 ^ x11, 12); x03 = x03 + x07 | 0; x15 = rotl2(x15 ^ x03, 8); x11 = x11 + x15 | 0; x07 = rotl2(x07 ^ x11, 7); x00 = x00 + x05 | 0; x15 = rotl2(x15 ^ x00, 16); x10 = x10 + x15 | 0; x05 = rotl2(x05 ^ x10, 12); x00 = x00 + x05 | 0; x15 = rotl2(x15 ^ x00, 8); x10 = x10 + x15 | 0; x05 = rotl2(x05 ^ x10, 7); x01 = x01 + x06 | 0; x12 = rotl2(x12 ^ x01, 16); x11 = x11 + x12 | 0; x06 = rotl2(x06 ^ x11, 12); x01 = x01 + x06 | 0; x12 = rotl2(x12 ^ x01, 8); x11 = x11 + x12 | 0; x06 = rotl2(x06 ^ x11, 7); x02 = x02 + x07 | 0; x13 = rotl2(x13 ^ x02, 16); x08 = x08 + x13 | 0; x07 = rotl2(x07 ^ x08, 12); x02 = x02 + x07 | 0; x13 = rotl2(x13 ^ x02, 8); x08 = x08 + x13 | 0; x07 = rotl2(x07 ^ x08, 7); x03 = x03 + x04 | 0; x14 = rotl2(x14 ^ x03, 16); x09 = x09 + x14 | 0; x04 = rotl2(x04 ^ x09, 12); x03 = x03 + x04 | 0; x14 = rotl2(x14 ^ x03, 8); x09 = x09 + x14 | 0; x04 = rotl2(x04 ^ x09, 7); } let oi = 0; out[oi++] = y00 + x00 | 0; out[oi++] = y01 + x01 | 0; out[oi++] = y02 + x02 | 0; out[oi++] = y03 + x03 | 0; out[oi++] = y04 + x04 | 0; out[oi++] = y05 + x05 | 0; out[oi++] = y06 + x06 | 0; out[oi++] = y07 + x07 | 0; out[oi++] = y08 + x08 | 0; out[oi++] = y09 + x09 | 0; out[oi++] = y10 + x10 | 0; out[oi++] = y11 + x11 | 0; out[oi++] = y12 + x12 | 0; out[oi++] = y13 + x13 | 0; out[oi++] = y14 + x14 | 0; out[oi++] = y15 + x15 | 0; } function hchacha(s, k2, i3, o32) { let x00 = s[0], x01 = s[1], x02 = s[2], x03 = s[3], x04 = k2[0], x05 = k2[1], x06 = k2[2], x07 = k2[3], x08 = k2[4], x09 = k2[5], x10 = k2[6], x11 = k2[7], x12 = i3[0], x13 = i3[1], x14 = i3[2], x15 = i3[3]; for (let r = 0; r < 20; r += 2) { x00 = x00 + x04 | 0; x12 = rotl2(x12 ^ x00, 16); x08 = x08 + x12 | 0; x04 = rotl2(x04 ^ x08, 12); x00 = x00 + x04 | 0; x12 = rotl2(x12 ^ x00, 8); x08 = x08 + x12 | 0; x04 = rotl2(x04 ^ x08, 7); x01 = x01 + x05 | 0; x13 = rotl2(x13 ^ x01, 16); x09 = x09 + x13 | 0; x05 = rotl2(x05 ^ x09, 12); x01 = x01 + x05 | 0; x13 = rotl2(x13 ^ x01, 8); x09 = x09 + x13 | 0; x05 = rotl2(x05 ^ x09, 7); x02 = x02 + x06 | 0; x14 = rotl2(x14 ^ x02, 16); x10 = x10 + x14 | 0; x06 = rotl2(x06 ^ x10, 12); x02 = x02 + x06 | 0; x14 = rotl2(x14 ^ x02, 8); x10 = x10 + x14 | 0; x06 = rotl2(x06 ^ x10, 7); x03 = x03 + x07 | 0; x15 = rotl2(x15 ^ x03, 16); x11 = x11 + x15 | 0; x07 = rotl2(x07 ^ x11, 12); x03 = x03 + x07 | 0; x15 = rotl2(x15 ^ x03, 8); x11 = x11 + x15 | 0; x07 = rotl2(x07 ^ x11, 7); x00 = x00 + x05 | 0; x15 = rotl2(x15 ^ x00, 16); x10 = x10 + x15 | 0; x05 = rotl2(x05 ^ x10, 12); x00 = x00 + x05 | 0; x15 = rotl2(x15 ^ x00, 8); x10 = x10 + x15 | 0; x05 = rotl2(x05 ^ x10, 7); x01 = x01 + x06 | 0; x12 = rotl2(x12 ^ x01, 16); x11 = x11 + x12 | 0; x06 = rotl2(x06 ^ x11, 12); x01 = x01 + x06 | 0; x12 = rotl2(x12 ^ x01, 8); x11 = x11 + x12 | 0; x06 = rotl2(x06 ^ x11, 7); x02 = x02 + x07 | 0; x13 = rotl2(x13 ^ x02, 16); x08 = x08 + x13 | 0; x07 = rotl2(x07 ^ x08, 12); x02 = x02 + x07 | 0; x13 = rotl2(x13 ^ x02, 8); x08 = x08 + x13 | 0; x07 = rotl2(x07 ^ x08, 7); x03 = x03 + x04 | 0; x14 = rotl2(x14 ^ x03, 16); x09 = x09 + x14 | 0; x04 = rotl2(x04 ^ x09, 12); x03 = x03 + x04 | 0; x14 = rotl2(x14 ^ x03, 8); x09 = x09 + x14 | 0; x04 = rotl2(x04 ^ x09, 7); } let oi = 0; o32[oi++] = x00; o32[oi++] = x01; o32[oi++] = x02; o32[oi++] = x03; o32[oi++] = x12; o32[oi++] = x13; o32[oi++] = x14; o32[oi++] = x15; } var chacha20 = /* @__PURE__ */ createCipher(chachaCore, { counterRight: false, counterLength: 4, allowShortKeys: false }); var xchacha20 = /* @__PURE__ */ createCipher(chachaCore, { counterRight: false, counterLength: 8, extendNonceFn: hchacha, allowShortKeys: false }); var ZEROS162 = /* @__PURE__ */ new Uint8Array(16); var updatePadded = (h2, msg) => { h2.update(msg); const left = msg.length % 16; if (left) h2.update(ZEROS162.subarray(left)); }; var ZEROS322 = /* @__PURE__ */ new Uint8Array(32); function computeTag2(fn, key, nonce, data, AAD) { const authKey = fn(key, nonce, ZEROS322); const h2 = poly1305.create(authKey); if (AAD) updatePadded(h2, AAD); updatePadded(h2, data); const num3 = new Uint8Array(16); const view = createView3(num3); setBigUint643(view, 0, BigInt(AAD ? AAD.length : 0), true); setBigUint643(view, 8, BigInt(data.length), true); h2.update(num3); const res = h2.digest(); authKey.fill(0); return res; } var _poly1305_aead = (xorStream) => (key, nonce, AAD) => { const tagLength = 16; bytes3(key, 32); bytes3(nonce); return { encrypt: (plaintext, output4) => { const plength = plaintext.length; const clength = plength + tagLength; if (output4) { bytes3(output4, clength); } else { output4 = new Uint8Array(clength); } xorStream(key, nonce, plaintext, output4, 1); const tag = computeTag2(xorStream, key, nonce, output4.subarray(0, -tagLength), AAD); output4.set(tag, plength); return output4; }, decrypt: (ciphertext, output4) => { const clength = ciphertext.length; const plength = clength - tagLength; if (clength < tagLength) throw new Error(`encrypted data must be at least ${tagLength} bytes`); if (output4) { bytes3(output4, plength); } else { output4 = new Uint8Array(plength); } const data = ciphertext.subarray(0, -tagLength); const passedTag = ciphertext.subarray(-tagLength); const tag = computeTag2(xorStream, key, nonce, data, AAD); if (!equalBytes2(passedTag, tag)) throw new Error("invalid tag"); xorStream(key, nonce, data, output4, 1); return output4; } }; }; var chacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 12, tagLength: 16 }, _poly1305_aead(chacha20)); var xchacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16 }, _poly1305_aead(xchacha20)); var HMAC22 = class extends Hash22 { constructor(hash3, _key) { super(); this.finished = false; this.destroyed = false; _assert_default.hash(hash3); const key = toBytes22(_key); this.iHash = hash3.create(); if (typeof this.iHash.update !== "function") throw new Error("Expected instance of class which extends utils.Hash"); this.blockLen = this.iHash.blockLen; this.outputLen = this.iHash.outputLen; const blockLen = this.blockLen; const pad2 = new Uint8Array(blockLen); pad2.set(key.length > blockLen ? hash3.create().update(key).digest() : key); for (let i3 = 0; i3 < pad2.length; i3++) pad2[i3] ^= 54; this.iHash.update(pad2); this.oHash = hash3.create(); for (let i3 = 0; i3 < pad2.length; i3++) pad2[i3] ^= 54 ^ 92; this.oHash.update(pad2); pad2.fill(0); } update(buf) { _assert_default.exists(this); this.iHash.update(buf); return this; } digestInto(out) { _assert_default.exists(this); _assert_default.bytes(out, this.outputLen); this.finished = true; this.iHash.digestInto(out); this.oHash.update(out); this.oHash.digestInto(out); this.destroy(); } digest() { const out = new Uint8Array(this.oHash.outputLen); this.digestInto(out); return out; } _cloneInto(to) { to || (to = Object.create(Object.getPrototypeOf(this), {})); const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; to = to; to.finished = finished; to.destroyed = destroyed; to.blockLen = blockLen; to.outputLen = outputLen; to.oHash = oHash._cloneInto(to.oHash); to.iHash = iHash._cloneInto(to.iHash); return to; } destroy() { this.destroyed = true; this.oHash.destroy(); this.iHash.destroy(); } }; var hmac22 = (hash3, key, message) => new HMAC22(hash3, key).update(message).digest(); hmac22.create = (hash3, key) => new HMAC22(hash3, key); function extract(hash3, ikm, salt) { _assert_default.hash(hash3); if (salt === void 0) salt = new Uint8Array(hash3.outputLen); return hmac22(hash3, toBytes22(salt), toBytes22(ikm)); } var HKDF_COUNTER = new Uint8Array([0]); var EMPTY_BUFFER = new Uint8Array(); function expand(hash3, prk, info, length = 32) { _assert_default.hash(hash3); _assert_default.number(length); if (length > 255 * hash3.outputLen) throw new Error("Length should be <= 255*HashLen"); const blocks = Math.ceil(length / hash3.outputLen); if (info === void 0) info = EMPTY_BUFFER; const okm = new Uint8Array(blocks * hash3.outputLen); const HMAC32 = hmac22.create(hash3, prk); const HMACTmp = HMAC32._cloneInto(); const T4 = new Uint8Array(HMAC32.outputLen); for (let counter = 0; counter < blocks; counter++) { HKDF_COUNTER[0] = counter + 1; HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T4).update(info).update(HKDF_COUNTER).digestInto(T4); okm.set(T4, hash3.outputLen * counter); HMAC32._cloneInto(HMACTmp); } HMAC32.destroy(); HMACTmp.destroy(); T4.fill(0); HKDF_COUNTER.fill(0); return okm.slice(0, length); } var __defProp22 = Object.defineProperty; var __export22 = (target, all) => { for (var name in all) __defProp22(target, name, { get: all[name], enumerable: true }); }; var verifiedSymbol = /* @__PURE__ */ Symbol("verified"); var isRecord = (obj) => obj instanceof Object; function validateEvent(event) { if (!isRecord(event)) return false; if (typeof event.kind !== "number") return false; if (typeof event.content !== "string") return false; if (typeof event.created_at !== "number") return false; if (typeof event.pubkey !== "string") return false; if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false; if (!Array.isArray(event.tags)) return false; for (let i22 = 0; i22 < event.tags.length; i22++) { let tag = event.tags[i22]; if (!Array.isArray(tag)) return false; for (let j2 = 0; j2 < tag.length; j2++) { if (typeof tag[j2] !== "string") return false; } } return true; } var utils_exports = {}; __export22(utils_exports, { Queue: () => Queue2, QueueNode: () => QueueNode, binarySearch: () => binarySearch, bytesToHex: () => bytesToHex22, hexToBytes: () => hexToBytes22, insertEventIntoAscendingList: () => insertEventIntoAscendingList, insertEventIntoDescendingList: () => insertEventIntoDescendingList, normalizeURL: () => normalizeURL, utf8Decoder: () => utf8Decoder, utf8Encoder: () => utf8Encoder }); var utf8Decoder = new TextDecoder("utf-8"); var utf8Encoder = new TextEncoder(); function normalizeURL(url) { try { if (url.indexOf("://") === -1) url = "wss://" + url; let p5 = new URL(url); if (p5.protocol === "http:") p5.protocol = "ws:"; else if (p5.protocol === "https:") p5.protocol = "wss:"; p5.pathname = p5.pathname.replace(/\/+/g, "/"); if (p5.pathname.endsWith("/")) p5.pathname = p5.pathname.slice(0, -1); if (p5.port === "80" && p5.protocol === "ws:" || p5.port === "443" && p5.protocol === "wss:") p5.port = ""; p5.searchParams.sort(); p5.hash = ""; return p5.toString(); } catch (e2) { throw new Error(`Invalid URL: ${url}`); } } function insertEventIntoDescendingList(sortedArray, event) { const [idx, found] = binarySearch(sortedArray, (b) => { if (event.id === b.id) return 0; if (event.created_at === b.created_at) return -1; return b.created_at - event.created_at; }); if (!found) { sortedArray.splice(idx, 0, event); } return sortedArray; } function insertEventIntoAscendingList(sortedArray, event) { const [idx, found] = binarySearch(sortedArray, (b) => { if (event.id === b.id) return 0; if (event.created_at === b.created_at) return -1; return event.created_at - b.created_at; }); if (!found) { sortedArray.splice(idx, 0, event); } return sortedArray; } function binarySearch(arr, compare) { let start = 0; let end = arr.length - 1; while (start <= end) { const mid = Math.floor((start + end) / 2); const cmp2 = compare(arr[mid]); if (cmp2 === 0) { return [mid, true]; } if (cmp2 < 0) { end = mid - 1; } else { start = mid + 1; } } return [start, false]; } var QueueNode = class { constructor(message) { __publicField(this, "value"); __publicField(this, "next", null); __publicField(this, "prev", null); this.value = message; } }; var Queue2 = class { constructor() { __publicField(this, "first"); __publicField(this, "last"); this.first = null; this.last = null; } enqueue(value) { const newNode = new QueueNode(value); if (!this.last) { this.first = newNode; this.last = newNode; } else if (this.last === this.first) { this.last = newNode; this.last.prev = this.first; this.first.next = newNode; } else { newNode.prev = this.last; this.last.next = newNode; this.last = newNode; } return true; } dequeue() { if (!this.first) return null; if (this.first === this.last) { const target2 = this.first; this.first = null; this.last = null; return target2.value; } const target = this.first; this.first = target.next; if (this.first) { this.first.prev = null; } return target.value; } }; var JS = class { generateSecretKey() { return schnorr2.utils.randomPrivateKey(); } getPublicKey(secretKey) { return bytesToHex22(schnorr2.getPublicKey(secretKey)); } finalizeEvent(t, secretKey) { const event = t; event.pubkey = bytesToHex22(schnorr2.getPublicKey(secretKey)); event.id = getEventHash4(event); event.sig = bytesToHex22(schnorr2.sign(getEventHash4(event), secretKey)); event[verifiedSymbol] = true; return event; } verifyEvent(event) { if (typeof event[verifiedSymbol] === "boolean") return event[verifiedSymbol]; const hash3 = getEventHash4(event); if (hash3 !== event.id) { event[verifiedSymbol] = false; return false; } try { const valid = schnorr2.verify(event.sig, hash3, event.pubkey); event[verifiedSymbol] = valid; return valid; } catch (err) { event[verifiedSymbol] = false; return false; } } }; function serializeEvent(evt) { if (!validateEvent(evt)) throw new Error("can't serialize event with wrong or missing properties"); return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]); } function getEventHash4(event) { let eventHash = sha25622(utf8Encoder.encode(serializeEvent(event))); return bytesToHex22(eventHash); } var i = new JS(); var generateSecretKey3 = i.generateSecretKey; var getPublicKey3 = i.getPublicKey; var finalizeEvent3 = i.finalizeEvent; var verifyEvent = i.verifyEvent; var kinds_exports = {}; __export22(kinds_exports, { Application: () => Application, BadgeAward: () => BadgeAward, BadgeDefinition: () => BadgeDefinition, BlockedRelaysList: () => BlockedRelaysList, BlossomServerList: () => BlossomServerList, BookmarkList: () => BookmarkList, Bookmarksets: () => Bookmarksets, Calendar: () => Calendar, CalendarEventRSVP: () => CalendarEventRSVP, ChannelCreation: () => ChannelCreation, ChannelHideMessage: () => ChannelHideMessage, ChannelMessage: () => ChannelMessage, ChannelMetadata: () => ChannelMetadata, ChannelMuteUser: () => ChannelMuteUser, ChatMessage: () => ChatMessage, ClassifiedListing: () => ClassifiedListing, ClientAuth: () => ClientAuth, Comment: () => Comment, CommunitiesList: () => CommunitiesList, CommunityDefinition: () => CommunityDefinition, CommunityPostApproval: () => CommunityPostApproval, Contacts: () => Contacts, CreateOrUpdateProduct: () => CreateOrUpdateProduct, CreateOrUpdateStall: () => CreateOrUpdateStall, Curationsets: () => Curationsets, Date: () => Date2, DirectMessageRelaysList: () => DirectMessageRelaysList, DraftClassifiedListing: () => DraftClassifiedListing, DraftLong: () => DraftLong, Emojisets: () => Emojisets, EncryptedDirectMessage: () => EncryptedDirectMessage, EventDeletion: () => EventDeletion, FavoriteRelays: () => FavoriteRelays, FileMessage: () => FileMessage, FileMetadata: () => FileMetadata, FileServerPreference: () => FileServerPreference, Followsets: () => Followsets, ForumThread: () => ForumThread, GenericRepost: () => GenericRepost, Genericlists: () => Genericlists, GiftWrap: () => GiftWrap, GroupMetadata: () => GroupMetadata, HTTPAuth: () => HTTPAuth, Handlerinformation: () => Handlerinformation, Handlerrecommendation: () => Handlerrecommendation, Highlights: () => Highlights, InterestsList: () => InterestsList, Interestsets: () => Interestsets, JobFeedback: () => JobFeedback, JobRequest: () => JobRequest, JobResult: () => JobResult, Label: () => Label, LightningPubRPC: () => LightningPubRPC, LiveChatMessage: () => LiveChatMessage, LiveEvent: () => LiveEvent, LongFormArticle: () => LongFormArticle, Metadata: () => Metadata, Mutelist: () => Mutelist, NWCWalletInfo: () => NWCWalletInfo, NWCWalletRequest: () => NWCWalletRequest, NWCWalletResponse: () => NWCWalletResponse, NormalVideo: () => NormalVideo, NostrConnect: () => NostrConnect, OpenTimestamps: () => OpenTimestamps, Photo: () => Photo, Pinlist: () => Pinlist, Poll: () => Poll, PollResponse: () => PollResponse, PrivateDirectMessage: () => PrivateDirectMessage, ProblemTracker: () => ProblemTracker, ProfileBadges: () => ProfileBadges, PublicChatsList: () => PublicChatsList, Reaction: () => Reaction, RecommendRelay: () => RecommendRelay, RelayList: () => RelayList, RelayReview: () => RelayReview, Relaysets: () => Relaysets, Report: () => Report, Reporting: () => Reporting, Repost: () => Repost, Seal: () => Seal, SearchRelaysList: () => SearchRelaysList, ShortTextNote: () => ShortTextNote, ShortVideo: () => ShortVideo, Time: () => Time, UserEmojiList: () => UserEmojiList, UserStatuses: () => UserStatuses, Voice: () => Voice, VoiceComment: () => VoiceComment, Zap: () => Zap, ZapGoal: () => ZapGoal, ZapRequest: () => ZapRequest, classifyKind: () => classifyKind, isAddressableKind: () => isAddressableKind, isEphemeralKind: () => isEphemeralKind, isKind: () => isKind, isRegularKind: () => isRegularKind, isReplaceableKind: () => isReplaceableKind }); function isRegularKind(kind) { return kind < 1e4 && kind !== 0 && kind !== 3; } function isReplaceableKind(kind) { return kind === 0 || kind === 3 || 1e4 <= kind && kind < 2e4; } function isEphemeralKind(kind) { return 2e4 <= kind && kind < 3e4; } function isAddressableKind(kind) { return 3e4 <= kind && kind < 4e4; } function classifyKind(kind) { if (isRegularKind(kind)) return "regular"; if (isReplaceableKind(kind)) return "replaceable"; if (isEphemeralKind(kind)) return "ephemeral"; if (isAddressableKind(kind)) return "parameterized"; return "unknown"; } function isKind(event, kind) { const kindAsArray = kind instanceof Array ? kind : [kind]; return validateEvent(event) && kindAsArray.includes(event.kind) || false; } var Metadata = 0; var ShortTextNote = 1; var RecommendRelay = 2; var Contacts = 3; var EncryptedDirectMessage = 4; var EventDeletion = 5; var Repost = 6; var Reaction = 7; var BadgeAward = 8; var ChatMessage = 9; var ForumThread = 11; var Seal = 13; var PrivateDirectMessage = 14; var FileMessage = 15; var GenericRepost = 16; var Photo = 20; var NormalVideo = 21; var ShortVideo = 22; var ChannelCreation = 40; var ChannelMetadata = 41; var ChannelMessage = 42; var ChannelHideMessage = 43; var ChannelMuteUser = 44; var OpenTimestamps = 1040; var GiftWrap = 1059; var Poll = 1068; var FileMetadata = 1063; var Comment = 1111; var LiveChatMessage = 1311; var Voice = 1222; var VoiceComment = 1244; var ProblemTracker = 1971; var Report = 1984; var Reporting = 1984; var Label = 1985; var CommunityPostApproval = 4550; var JobRequest = 5999; var JobResult = 6999; var JobFeedback = 7e3; var ZapGoal = 9041; var ZapRequest = 9734; var Zap = 9735; var Highlights = 9802; var PollResponse = 1018; var Mutelist = 1e4; var Pinlist = 10001; var RelayList = 10002; var BookmarkList = 10003; var CommunitiesList = 10004; var PublicChatsList = 10005; var BlockedRelaysList = 10006; var SearchRelaysList = 10007; var FavoriteRelays = 10012; var InterestsList = 10015; var UserEmojiList = 10030; var DirectMessageRelaysList = 10050; var FileServerPreference = 10096; var BlossomServerList = 10063; var NWCWalletInfo = 13194; var LightningPubRPC = 21e3; var ClientAuth = 22242; var NWCWalletRequest = 23194; var NWCWalletResponse = 23195; var NostrConnect = 24133; var HTTPAuth = 27235; var Followsets = 3e4; var Genericlists = 30001; var Relaysets = 30002; var Bookmarksets = 30003; var Curationsets = 30004; var ProfileBadges = 30008; var BadgeDefinition = 30009; var Interestsets = 30015; var CreateOrUpdateStall = 30017; var CreateOrUpdateProduct = 30018; var LongFormArticle = 30023; var DraftLong = 30024; var Emojisets = 30030; var Application = 30078; var LiveEvent = 30311; var UserStatuses = 30315; var ClassifiedListing = 30402; var DraftClassifiedListing = 30403; var Date2 = 31922; var Time = 31923; var Calendar = 31924; var CalendarEventRSVP = 31925; var RelayReview = 31987; var Handlerrecommendation = 31989; var Handlerinformation = 31990; var CommunityDefinition = 34550; var GroupMetadata = 39e3; function matchFilter4(filter, event) { if (filter.ids && filter.ids.indexOf(event.id) === -1) { return false; } if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) { return false; } if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) { return false; } for (let f in filter) { if (f[0] === "#") { let tagName = f.slice(1); let values = filter[`#${tagName}`]; if (values && !event.tags.find(([t, v6]) => t === f.slice(1) && values.indexOf(v6) !== -1)) return false; } } if (filter.since && event.created_at < filter.since) return false; if (filter.until && event.created_at > filter.until) return false; return true; } function matchFilters3(filters, event) { for (let i22 = 0; i22 < filters.length; i22++) { if (matchFilter4(filters[i22], event)) { return true; } } return false; } var fakejson_exports = {}; __export22(fakejson_exports, { getHex64: () => getHex64, getInt: () => getInt, getSubscriptionId: () => getSubscriptionId, matchEventId: () => matchEventId, matchEventKind: () => matchEventKind, matchEventPubkey: () => matchEventPubkey }); function getHex64(json, field) { let len = field.length + 3; let idx = json.indexOf(`"${field}":`) + len; let s = json.slice(idx).indexOf(`"`) + idx + 1; return json.slice(s, s + 64); } function getInt(json, field) { let len = field.length; let idx = json.indexOf(`"${field}":`) + len + 3; let sliced = json.slice(idx); let end = Math.min(sliced.indexOf(","), sliced.indexOf("}")); return parseInt(sliced.slice(0, end), 10); } function getSubscriptionId(json) { let idx = json.slice(0, 22).indexOf(`"EVENT"`); if (idx === -1) return null; let pstart = json.slice(idx + 7 + 1).indexOf(`"`); if (pstart === -1) return null; let start = idx + 7 + 1 + pstart; let pend = json.slice(start + 1, 80).indexOf(`"`); if (pend === -1) return null; let end = start + 1 + pend; return json.slice(start + 1, end); } function matchEventId(json, id) { return id === getHex64(json, "id"); } function matchEventPubkey(json, pubkey) { return pubkey === getHex64(json, "pubkey"); } function matchEventKind(json, kind) { return kind === getInt(json, "kind"); } var nip42_exports = {}; __export22(nip42_exports, { makeAuthEvent: () => makeAuthEvent }); function makeAuthEvent(relayURL, challenge23) { return { kind: ClientAuth, created_at: Math.floor(Date.now() / 1e3), tags: [ ["relay", relayURL], ["challenge", challenge23] ], content: "" }; } async function yieldThread() { return new Promise((resolve, reject) => { try { if (typeof MessageChannel !== "undefined") { const ch = new MessageChannel(); const handler = () => { ch.port1.removeEventListener("message", handler); resolve(); }; ch.port1.addEventListener("message", handler); ch.port2.postMessage(0); ch.port1.start(); } else { if (typeof setImmediate !== "undefined") { setImmediate(resolve); } else if (typeof setTimeout !== "undefined") { setTimeout(resolve, 0); } else { resolve(); } } } catch (e2) { console.error("during yield: ", e2); reject(e2); } }); } var SendingOnClosedConnection = class extends Error { constructor(message, relay) { super(`Tried to send message '${message} on a closed connection to ${relay}.`); this.name = "SendingOnClosedConnection"; } }; var AbstractRelay = class { constructor(url, opts) { __publicField(this, "url"); __publicField(this, "_connected", false); __publicField(this, "onclose", null); __publicField(this, "onnotice", (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`)); __publicField(this, "onauth"); __publicField(this, "baseEoseTimeout", 4400); __publicField(this, "connectionTimeout", 4400); __publicField(this, "publishTimeout", 4400); __publicField(this, "pingFrequency", 29e3); __publicField(this, "pingTimeout", 2e4); __publicField(this, "resubscribeBackoff", [1e4, 1e4, 1e4, 2e4, 2e4, 3e4, 6e4]); __publicField(this, "openSubs", /* @__PURE__ */ new Map()); __publicField(this, "enablePing"); __publicField(this, "enableReconnect"); __publicField(this, "connectionTimeoutHandle"); __publicField(this, "reconnectTimeoutHandle"); __publicField(this, "pingIntervalHandle"); __publicField(this, "reconnectAttempts", 0); __publicField(this, "closedIntentionally", false); __publicField(this, "connectionPromise"); __publicField(this, "openCountRequests", /* @__PURE__ */ new Map()); __publicField(this, "openEventPublishes", /* @__PURE__ */ new Map()); __publicField(this, "ws"); __publicField(this, "incomingMessageQueue", new Queue2()); __publicField(this, "queueRunning", false); __publicField(this, "challenge"); __publicField(this, "authPromise"); __publicField(this, "serial", 0); __publicField(this, "verifyEvent"); __publicField(this, "_WebSocket"); this.url = normalizeURL(url); this.verifyEvent = opts.verifyEvent; this._WebSocket = opts.websocketImplementation || WebSocket; this.enablePing = opts.enablePing; this.enableReconnect = opts.enableReconnect || false; } static async connect(url, opts) { const relay = new AbstractRelay(url, opts); await relay.connect(); return relay; } closeAllSubscriptions(reason) { for (let [_2, sub] of this.openSubs) { sub.close(reason); } this.openSubs.clear(); for (let [_2, ep] of this.openEventPublishes) { ep.reject(new Error(reason)); } this.openEventPublishes.clear(); for (let [_2, cr] of this.openCountRequests) { cr.reject(new Error(reason)); } this.openCountRequests.clear(); } get connected() { return this._connected; } async reconnect() { const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]; this.reconnectAttempts++; this.reconnectTimeoutHandle = setTimeout(async () => { try { await this.connect(); } catch (err) { } }, backoff); } handleHardClose(reason) { if (this.pingIntervalHandle) { clearInterval(this.pingIntervalHandle); this.pingIntervalHandle = void 0; } this._connected = false; this.connectionPromise = void 0; const wasIntentional = this.closedIntentionally; this.closedIntentionally = false; this.onclose?.(); if (this.enableReconnect && !wasIntentional) { this.reconnect(); } else { this.closeAllSubscriptions(reason); } } async connect() { if (this.connectionPromise) return this.connectionPromise; this.challenge = void 0; this.authPromise = void 0; this.connectionPromise = new Promise((resolve, reject) => { this.connectionTimeoutHandle = setTimeout(() => { reject("connection timed out"); this.connectionPromise = void 0; this.onclose?.(); this.closeAllSubscriptions("relay connection timed out"); }, this.connectionTimeout); try { this.ws = new this._WebSocket(this.url); } catch (err) { clearTimeout(this.connectionTimeoutHandle); reject(err); return; } this.ws.onopen = () => { if (this.reconnectTimeoutHandle) { clearTimeout(this.reconnectTimeoutHandle); this.reconnectTimeoutHandle = void 0; } clearTimeout(this.connectionTimeoutHandle); this._connected = true; const isReconnection = this.reconnectAttempts > 0; this.reconnectAttempts = 0; for (const sub of this.openSubs.values()) { sub.eosed = false; if (isReconnection) { for (let f = 0; f < sub.filters.length; f++) { if (sub.lastEmitted) { sub.filters[f].since = sub.lastEmitted + 1; } } } sub.fire(); } if (this.enablePing) { this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency); } resolve(); }; this.ws.onerror = (ev) => { clearTimeout(this.connectionTimeoutHandle); reject(ev.message || "websocket error"); this.handleHardClose("relay connection errored"); }; this.ws.onclose = (ev) => { clearTimeout(this.connectionTimeoutHandle); reject(ev.message || "websocket closed"); this.handleHardClose("relay connection closed"); }; this.ws.onmessage = this._onmessage.bind(this); }); return this.connectionPromise; } waitForPingPong() { return new Promise((resolve) => { this.ws.once("pong", () => resolve(true)); this.ws.ping(); }); } waitForDummyReq() { return new Promise((resolve, reject) => { if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`)); try { const sub = this.subscribe([{ ids: ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], limit: 0 }], { label: "forced-ping", oneose: () => { resolve(true); sub.close(); }, onclose() { resolve(true); }, eoseTimeout: this.pingTimeout + 1e3 }); } catch (err) { reject(err); } }); } async pingpong() { if (this.ws?.readyState === 1) { const result = await Promise.any([ this.ws && this.ws.ping && this.ws.once ? this.waitForPingPong() : this.waitForDummyReq(), new Promise((res) => setTimeout(() => res(false), this.pingTimeout)) ]); if (!result) { if (this.ws?.readyState === this._WebSocket.OPEN) { this.ws?.close(); } } } } async runQueue() { this.queueRunning = true; while (true) { if (this.handleNext() === false) { break; } await yieldThread(); } this.queueRunning = false; } handleNext() { const json = this.incomingMessageQueue.dequeue(); if (!json) { return false; } const subid = getSubscriptionId(json); if (subid) { const so = this.openSubs.get(subid); if (!so) { return; } const id = getHex64(json, "id"); const alreadyHave = so.alreadyHaveEvent?.(id); so.receivedEvent?.(this, id); if (alreadyHave) { return; } } try { let data = JSON.parse(json); switch (data[0]) { case "EVENT": { const so = this.openSubs.get(data[1]); const event = data[2]; if (this.verifyEvent(event) && matchFilters3(so.filters, event)) { so.onevent(event); } if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at; return; } case "COUNT": { const id = data[1]; const payload = data[2]; const cr = this.openCountRequests.get(id); if (cr) { cr.resolve(payload.count); this.openCountRequests.delete(id); } return; } case "EOSE": { const so = this.openSubs.get(data[1]); if (!so) return; so.receivedEose(); return; } case "OK": { const id = data[1]; const ok = data[2]; const reason = data[3]; const ep = this.openEventPublishes.get(id); if (ep) { clearTimeout(ep.timeout); if (ok) ep.resolve(reason); else ep.reject(new Error(reason)); this.openEventPublishes.delete(id); } return; } case "CLOSED": { const id = data[1]; const so = this.openSubs.get(id); if (!so) return; so.closed = true; so.close(data[2]); return; } case "NOTICE": { this.onnotice(data[1]); return; } case "AUTH": { this.challenge = data[1]; if (this.onauth) { this.auth(this.onauth); } return; } default: { const so = this.openSubs.get(data[1]); so?.oncustom?.(data); return; } } } catch (err) { return; } } async send(message) { if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url); this.connectionPromise.then(() => { this.ws?.send(message); }); } async auth(signAuthEvent) { const challenge23 = this.challenge; if (!challenge23) throw new Error("can't perform auth, no challenge was received"); if (this.authPromise) return this.authPromise; this.authPromise = new Promise(async (resolve, reject) => { try { let evt = await signAuthEvent(makeAuthEvent(this.url, challenge23)); let timeout = setTimeout(() => { let ep = this.openEventPublishes.get(evt.id); if (ep) { ep.reject(new Error("auth timed out")); this.openEventPublishes.delete(evt.id); } }, this.publishTimeout); this.openEventPublishes.set(evt.id, { resolve, reject, timeout }); this.send('["AUTH",' + JSON.stringify(evt) + "]"); } catch (err) { console.warn("subscribe auth function failed:", err); } }); return this.authPromise; } async publish(event) { const ret = new Promise((resolve, reject) => { const timeout = setTimeout(() => { const ep = this.openEventPublishes.get(event.id); if (ep) { ep.reject(new Error("publish timed out")); this.openEventPublishes.delete(event.id); } }, this.publishTimeout); this.openEventPublishes.set(event.id, { resolve, reject, timeout }); }); this.send('["EVENT",' + JSON.stringify(event) + "]"); return ret; } async count(filters, params) { this.serial++; const id = params?.id || "count:" + this.serial; const ret = new Promise((resolve, reject) => { this.openCountRequests.set(id, { resolve, reject }); }); this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1)); return ret; } subscribe(filters, params) { const sub = this.prepareSubscription(filters, params); sub.fire(); return sub; } prepareSubscription(filters, params) { this.serial++; const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial; const subscription = new Subscription(this, id, filters, params); this.openSubs.set(id, subscription); return subscription; } close() { this.closedIntentionally = true; if (this.reconnectTimeoutHandle) { clearTimeout(this.reconnectTimeoutHandle); this.reconnectTimeoutHandle = void 0; } if (this.pingIntervalHandle) { clearInterval(this.pingIntervalHandle); this.pingIntervalHandle = void 0; } this.closeAllSubscriptions("relay connection closed by us"); this._connected = false; this.onclose?.(); if (this.ws?.readyState === this._WebSocket.OPEN) { this.ws?.close(); } } _onmessage(ev) { this.incomingMessageQueue.enqueue(ev.data); if (!this.queueRunning) { this.runQueue(); } } }; var Subscription = class { constructor(relay, id, filters, params) { __publicField(this, "relay"); __publicField(this, "id"); __publicField(this, "lastEmitted"); __publicField(this, "closed", false); __publicField(this, "eosed", false); __publicField(this, "filters"); __publicField(this, "alreadyHaveEvent"); __publicField(this, "receivedEvent"); __publicField(this, "onevent"); __publicField(this, "oneose"); __publicField(this, "onclose"); __publicField(this, "oncustom"); __publicField(this, "eoseTimeout"); __publicField(this, "eoseTimeoutHandle"); if (filters.length === 0) throw new Error("subscription can't be created with zero filters"); this.relay = relay; this.filters = filters; this.id = id; this.alreadyHaveEvent = params.alreadyHaveEvent; this.receivedEvent = params.receivedEvent; this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout; this.oneose = params.oneose; this.onclose = params.onclose; this.onevent = params.onevent || ((event) => { console.warn(`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`, event); }); } fire() { this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1)); this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout); } receivedEose() { if (this.eosed) return; clearTimeout(this.eoseTimeoutHandle); this.eosed = true; this.oneose?.(); } close(reason = "closed by caller") { if (!this.closed && this.relay.connected) { try { this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]"); } catch (err) { if (err instanceof SendingOnClosedConnection) { } else { throw err; } } this.closed = true; } this.relay.openSubs.delete(this.id); this.onclose?.(reason); } }; var _WebSocket; try { _WebSocket = WebSocket; } catch { } var _WebSocket2; try { _WebSocket2 = WebSocket; } catch { } var nip19_exports3 = {}; __export22(nip19_exports3, { BECH32_REGEX: () => BECH32_REGEX2, Bech32MaxSize: () => Bech32MaxSize, NostrTypeGuard: () => NostrTypeGuard, decode: () => decode3, decodeNostrURI: () => decodeNostrURI, encodeBytes: () => encodeBytes, naddrEncode: () => naddrEncode, neventEncode: () => neventEncode, noteEncode: () => noteEncode, nprofileEncode: () => nprofileEncode, npubEncode: () => npubEncode, nsecEncode: () => nsecEncode }); var NostrTypeGuard = { isNProfile: (value) => /^nprofile1[a-z\d]+$/.test(value || ""), isNEvent: (value) => /^nevent1[a-z\d]+$/.test(value || ""), isNAddr: (value) => /^naddr1[a-z\d]+$/.test(value || ""), isNSec: (value) => /^nsec1[a-z\d]{58}$/.test(value || ""), isNPub: (value) => /^npub1[a-z\d]{58}$/.test(value || ""), isNote: (value) => /^note1[a-z\d]+$/.test(value || ""), isNcryptsec: (value) => /^ncryptsec1[a-z\d]+$/.test(value || "") }; var Bech32MaxSize = 5e3; var BECH32_REGEX2 = /[\x21-\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/; function integerToUint8Array(number4) { const uint8Array = new Uint8Array(4); uint8Array[0] = number4 >> 24 & 255; uint8Array[1] = number4 >> 16 & 255; uint8Array[2] = number4 >> 8 & 255; uint8Array[3] = number4 & 255; return uint8Array; } function decodeNostrURI(nip19code) { try { if (nip19code.startsWith("nostr:")) nip19code = nip19code.substring(6); return decode3(nip19code); } catch (_err) { return { type: "invalid", data: null }; } } function decode3(code) { let { prefix, words } = bech322.decode(code, Bech32MaxSize); let data = new Uint8Array(bech322.fromWords(words)); switch (prefix) { case "nprofile": { let tlv = parseTLV(data); if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for nprofile"); if (tlv[0][0].length !== 32) throw new Error("TLV 0 should be 32 bytes"); return { type: "nprofile", data: { pubkey: bytesToHex22(tlv[0][0]), relays: tlv[1] ? tlv[1].map((d17) => utf8Decoder.decode(d17)) : [] } }; } case "nevent": { let tlv = parseTLV(data); if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for nevent"); if (tlv[0][0].length !== 32) throw new Error("TLV 0 should be 32 bytes"); if (tlv[2] && tlv[2][0].length !== 32) throw new Error("TLV 2 should be 32 bytes"); if (tlv[3] && tlv[3][0].length !== 4) throw new Error("TLV 3 should be 4 bytes"); return { type: "nevent", data: { id: bytesToHex22(tlv[0][0]), relays: tlv[1] ? tlv[1].map((d17) => utf8Decoder.decode(d17)) : [], author: tlv[2]?.[0] ? bytesToHex22(tlv[2][0]) : void 0, kind: tlv[3]?.[0] ? parseInt(bytesToHex22(tlv[3][0]), 16) : void 0 } }; } case "naddr": { let tlv = parseTLV(data); if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for naddr"); if (!tlv[2]?.[0]) throw new Error("missing TLV 2 for naddr"); if (tlv[2][0].length !== 32) throw new Error("TLV 2 should be 32 bytes"); if (!tlv[3]?.[0]) throw new Error("missing TLV 3 for naddr"); if (tlv[3][0].length !== 4) throw new Error("TLV 3 should be 4 bytes"); return { type: "naddr", data: { identifier: utf8Decoder.decode(tlv[0][0]), pubkey: bytesToHex22(tlv[2][0]), kind: parseInt(bytesToHex22(tlv[3][0]), 16), relays: tlv[1] ? tlv[1].map((d17) => utf8Decoder.decode(d17)) : [] } }; } case "nsec": return { type: prefix, data }; case "npub": case "note": return { type: prefix, data: bytesToHex22(data) }; default: throw new Error(`unknown prefix ${prefix}`); } } function parseTLV(data) { let result = {}; let rest = data; while (rest.length > 0) { let t = rest[0]; let l3 = rest[1]; let v6 = rest.slice(2, 2 + l3); rest = rest.slice(2 + l3); if (v6.length < l3) throw new Error(`not enough data to read on TLV ${t}`); result[t] = result[t] || []; result[t].push(v6); } return result; } function nsecEncode(key) { return encodeBytes("nsec", key); } function npubEncode(hex2) { return encodeBytes("npub", hexToBytes22(hex2)); } function noteEncode(hex2) { return encodeBytes("note", hexToBytes22(hex2)); } function encodeBech32(prefix, data) { let words = bech322.toWords(data); return bech322.encode(prefix, words, Bech32MaxSize); } function encodeBytes(prefix, bytes4) { return encodeBech32(prefix, bytes4); } function nprofileEncode(profile) { let data = encodeTLV({ 0: [hexToBytes22(profile.pubkey)], 1: (profile.relays || []).map((url) => utf8Encoder.encode(url)) }); return encodeBech32("nprofile", data); } function neventEncode(event) { let kindArray; if (event.kind !== void 0) { kindArray = integerToUint8Array(event.kind); } let data = encodeTLV({ 0: [hexToBytes22(event.id)], 1: (event.relays || []).map((url) => utf8Encoder.encode(url)), 2: event.author ? [hexToBytes22(event.author)] : [], 3: kindArray ? [new Uint8Array(kindArray)] : [] }); return encodeBech32("nevent", data); } function naddrEncode(addr) { let kind = new ArrayBuffer(4); new DataView(kind).setUint32(0, addr.kind, false); let data = encodeTLV({ 0: [utf8Encoder.encode(addr.identifier)], 1: (addr.relays || []).map((url) => utf8Encoder.encode(url)), 2: [hexToBytes22(addr.pubkey)], 3: [new Uint8Array(kind)] }); return encodeBech32("naddr", data); } function encodeTLV(tlv) { let entries = []; Object.entries(tlv).reverse().forEach(([t, vs]) => { vs.forEach((v6) => { let entry = new Uint8Array(v6.length + 2); entry.set([parseInt(t)], 0); entry.set([v6.length], 1); entry.set(v6, 2); entries.push(entry); }); }); return concatBytes3(...entries); } var nip04_exports = {}; __export22(nip04_exports, { decrypt: () => decrypt22, encrypt: () => encrypt22 }); function encrypt22(secretKey, pubkey, text) { const privkey = secretKey instanceof Uint8Array ? bytesToHex22(secretKey) : secretKey; const key = secp256k12.getSharedSecret(privkey, "02" + pubkey); const normalizedKey = getNormalizedX(key); let iv = Uint8Array.from(randomBytes22(16)); let plaintext = utf8Encoder.encode(text); let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext); let ctb64 = base64.encode(new Uint8Array(ciphertext)); let ivb64 = base64.encode(new Uint8Array(iv.buffer)); return `${ctb64}?iv=${ivb64}`; } function decrypt22(secretKey, pubkey, data) { const privkey = secretKey instanceof Uint8Array ? bytesToHex22(secretKey) : secretKey; let [ctb64, ivb64] = data.split("?iv="); let key = secp256k12.getSharedSecret(privkey, "02" + pubkey); let normalizedKey = getNormalizedX(key); let iv = base64.decode(ivb64); let ciphertext = base64.decode(ctb64); let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext); return utf8Decoder.decode(plaintext); } function getNormalizedX(key) { return key.slice(1, 33); } var nip05_exports = {}; __export22(nip05_exports, { NIP05_REGEX: () => NIP05_REGEX3, isNip05: () => isNip05, isValid: () => isValid, queryProfile: () => queryProfile, searchDomain: () => searchDomain, useFetchImplementation: () => useFetchImplementation }); var NIP05_REGEX3 = /^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$/; var isNip05 = (value) => NIP05_REGEX3.test(value || ""); var _fetch; try { _fetch = fetch; } catch (_2) { } function useFetchImplementation(fetchImplementation) { _fetch = fetchImplementation; } async function searchDomain(domain, query3 = "") { try { const url = `https://${domain}/.well-known/nostr.json?name=${query3}`; const res = await _fetch(url, { redirect: "manual" }); if (res.status !== 200) { throw Error("Wrong response code"); } const json = await res.json(); return json.names; } catch (_2) { return {}; } } async function queryProfile(fullname) { const match = fullname.match(NIP05_REGEX3); if (!match) return null; const [, name = "_", domain] = match; try { const url = `https://${domain}/.well-known/nostr.json?name=${name}`; const res = await _fetch(url, { redirect: "manual" }); if (res.status !== 200) { throw Error("Wrong response code"); } const json = await res.json(); const pubkey = json.names[name]; return pubkey ? { pubkey, relays: json.relays?.[pubkey] } : null; } catch (_e2) { return null; } } async function isValid(pubkey, nip05) { const res = await queryProfile(nip05); return res ? res.pubkey === pubkey : false; } var nip10_exports = {}; __export22(nip10_exports, { parse: () => parse }); function parse(event) { const result = { reply: void 0, root: void 0, mentions: [], profiles: [], quotes: [] }; let maybeParent; let maybeRoot; for (let i22 = event.tags.length - 1; i22 >= 0; i22--) { const tag = event.tags[i22]; if (tag[0] === "e" && tag[1]) { const [_2, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag; const eventPointer = { id: eTagEventId, relays: eTagRelayUrl ? [eTagRelayUrl] : [], author: eTagAuthor }; if (eTagMarker === "root") { result.root = eventPointer; continue; } if (eTagMarker === "reply") { result.reply = eventPointer; continue; } if (eTagMarker === "mention") { result.mentions.push(eventPointer); continue; } if (!maybeParent) { maybeParent = eventPointer; } else { maybeRoot = eventPointer; } result.mentions.push(eventPointer); continue; } if (tag[0] === "q" && tag[1]) { const [_2, eTagEventId, eTagRelayUrl] = tag; result.quotes.push({ id: eTagEventId, relays: eTagRelayUrl ? [eTagRelayUrl] : [] }); } if (tag[0] === "p" && tag[1]) { result.profiles.push({ pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] }); continue; } } if (!result.root) { result.root = maybeRoot || maybeParent || result.reply; } if (!result.reply) { result.reply = maybeParent || result.root; } [result.reply, result.root].forEach((ref) => { if (!ref) return; let idx = result.mentions.indexOf(ref); if (idx !== -1) { result.mentions.splice(idx, 1); } if (ref.author) { let author = result.profiles.find((p5) => p5.pubkey === ref.author); if (author && author.relays) { if (!ref.relays) { ref.relays = []; } author.relays.forEach((url) => { if (ref.relays?.indexOf(url) === -1) ref.relays.push(url); }); author.relays = ref.relays; } } }); result.mentions.forEach((ref) => { if (ref.author) { let author = result.profiles.find((p5) => p5.pubkey === ref.author); if (author && author.relays) { if (!ref.relays) { ref.relays = []; } author.relays.forEach((url) => { if (ref.relays.indexOf(url) === -1) ref.relays.push(url); }); author.relays = ref.relays; } } }); return result; } var nip11_exports = {}; __export22(nip11_exports, { fetchRelayInformation: () => fetchRelayInformation3, useFetchImplementation: () => useFetchImplementation2 }); var _fetch2; try { _fetch2 = fetch; } catch { } function useFetchImplementation2(fetchImplementation) { _fetch2 = fetchImplementation; } async function fetchRelayInformation3(url) { return await (await fetch(url.replace("ws://", "http://").replace("wss://", "https://"), { headers: { Accept: "application/nostr+json" } })).json(); } var nip13_exports = {}; __export22(nip13_exports, { fastEventHash: () => fastEventHash, getPow: () => getPow, minePow: () => minePow }); function getPow(hex2) { let count = 0; for (let i22 = 0; i22 < 64; i22 += 8) { const nibble = parseInt(hex2.substring(i22, i22 + 8), 16); if (nibble === 0) { count += 32; } else { count += Math.clz32(nibble); break; } } return count; } function minePow(unsigned, difficulty) { let count = 0; const event = unsigned; const tag = ["nonce", count.toString(), difficulty.toString()]; event.tags.push(tag); while (true) { const now2 = Math.floor((/* @__PURE__ */ new Date()).getTime() / 1e3); if (now2 !== event.created_at) { count = 0; event.created_at = now2; } tag[1] = (++count).toString(); event.id = fastEventHash(event); if (getPow(event.id) >= difficulty) { break; } } return event; } function fastEventHash(evt) { return bytesToHex22(sha25622(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])))); } var nip17_exports = {}; __export22(nip17_exports, { unwrapEvent: () => unwrapEvent2, unwrapManyEvents: () => unwrapManyEvents2, wrapEvent: () => wrapEvent22, wrapManyEvents: () => wrapManyEvents2 }); var nip59_exports = {}; __export22(nip59_exports, { createRumor: () => createRumor, createSeal: () => createSeal, createWrap: () => createWrap, unwrapEvent: () => unwrapEvent, unwrapManyEvents: () => unwrapManyEvents, wrapEvent: () => wrapEvent3, wrapManyEvents: () => wrapManyEvents }); var nip44_exports = {}; __export22(nip44_exports, { decrypt: () => decrypt222, encrypt: () => encrypt222, getConversationKey: () => getConversationKey, v2: () => v2 }); var minPlaintextSize = 1; var maxPlaintextSize = 65535; function getConversationKey(privkeyA, pubkeyB) { const sharedX = secp256k12.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33); return extract(sha25622, sharedX, "nip44-v2"); } function getMessageKeys(conversationKey, nonce) { const keys = expand(sha25622, conversationKey, nonce, 76); return { chacha_key: keys.subarray(0, 32), chacha_nonce: keys.subarray(32, 44), hmac_key: keys.subarray(44, 76) }; } function calcPaddedLen(len) { if (!Number.isSafeInteger(len) || len < 1) throw new Error("expected positive integer"); if (len <= 32) return 32; const nextPower = 1 << Math.floor(Math.log2(len - 1)) + 1; const chunk = nextPower <= 256 ? 32 : nextPower / 8; return chunk * (Math.floor((len - 1) / chunk) + 1); } function writeU16BE(num3) { if (!Number.isSafeInteger(num3) || num3 < minPlaintextSize || num3 > maxPlaintextSize) throw new Error("invalid plaintext size: must be between 1 and 65535 bytes"); const arr = new Uint8Array(2); new DataView(arr.buffer).setUint16(0, num3, false); return arr; } function pad(plaintext) { const unpadded = utf8Encoder.encode(plaintext); const unpaddedLen = unpadded.length; const prefix = writeU16BE(unpaddedLen); const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen); return concatBytes3(prefix, unpadded, suffix); } function unpad(padded) { const unpaddedLen = new DataView(padded.buffer).getUint16(0); const unpadded = padded.subarray(2, 2 + unpaddedLen); if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize || unpadded.length !== unpaddedLen || padded.length !== 2 + calcPaddedLen(unpaddedLen)) throw new Error("invalid padding"); return utf8Decoder.decode(unpadded); } function hmacAad(key, message, aad) { if (aad.length !== 32) throw new Error("AAD associated data must be 32 bytes"); const combined = concatBytes3(aad, message); return hmac22(sha25622, key, combined); } function decodePayload(payload) { if (typeof payload !== "string") throw new Error("payload must be a valid string"); const plen = payload.length; if (plen < 132 || plen > 87472) throw new Error("invalid payload length: " + plen); if (payload[0] === "#") throw new Error("unknown encryption version"); let data; try { data = base64.decode(payload); } catch (error) { throw new Error("invalid base64: " + error.message); } const dlen = data.length; if (dlen < 99 || dlen > 65603) throw new Error("invalid data length: " + dlen); const vers = data[0]; if (vers !== 2) throw new Error("unknown encryption version " + vers); return { nonce: data.subarray(1, 33), ciphertext: data.subarray(33, -32), mac: data.subarray(-32) }; } function encrypt222(plaintext, conversationKey, nonce = randomBytes22(32)) { const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce); const padded = pad(plaintext); const ciphertext = chacha20(chacha_key, chacha_nonce, padded); const mac = hmacAad(hmac_key, ciphertext, nonce); return base64.encode(concatBytes3(new Uint8Array([2]), nonce, ciphertext, mac)); } function decrypt222(payload, conversationKey) { const { nonce, ciphertext, mac } = decodePayload(payload); const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce); const calculatedMac = hmacAad(hmac_key, ciphertext, nonce); if (!equalBytes2(calculatedMac, mac)) throw new Error("invalid MAC"); const padded = chacha20(chacha_key, chacha_nonce, ciphertext); return unpad(padded); } var v2 = { utils: { getConversationKey, calcPaddedLen }, encrypt: encrypt222, decrypt: decrypt222 }; var TWO_DAYS = 2 * 24 * 60 * 60; var now = () => Math.round(Date.now() / 1e3); var randomNow = () => Math.round(now() - Math.random() * TWO_DAYS); var nip44ConversationKey = (privateKey, publicKey) => getConversationKey(privateKey, publicKey); var nip44Encrypt = (data, privateKey, publicKey) => encrypt222(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey)); var nip44Decrypt = (data, privateKey) => JSON.parse(decrypt222(data.content, nip44ConversationKey(privateKey, data.pubkey))); function createRumor(event, privateKey) { const rumor = { created_at: now(), content: "", tags: [], ...event, pubkey: getPublicKey3(privateKey) }; rumor.id = getEventHash4(rumor); return rumor; } function createSeal(rumor, privateKey, recipientPublicKey) { return finalizeEvent3({ kind: Seal, content: nip44Encrypt(rumor, privateKey, recipientPublicKey), created_at: randomNow(), tags: [] }, privateKey); } function createWrap(seal, recipientPublicKey) { const randomKey = generateSecretKey3(); return finalizeEvent3({ kind: GiftWrap, content: nip44Encrypt(seal, randomKey, recipientPublicKey), created_at: randomNow(), tags: [["p", recipientPublicKey]] }, randomKey); } function wrapEvent3(event, senderPrivateKey, recipientPublicKey) { const rumor = createRumor(event, senderPrivateKey); const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey); return createWrap(seal, recipientPublicKey); } function wrapManyEvents(event, senderPrivateKey, recipientsPublicKeys) { if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) { throw new Error("At least one recipient is required."); } const senderPublicKey = getPublicKey3(senderPrivateKey); const wrappeds = [wrapEvent3(event, senderPrivateKey, senderPublicKey)]; recipientsPublicKeys.forEach((recipientPublicKey) => { wrappeds.push(wrapEvent3(event, senderPrivateKey, recipientPublicKey)); }); return wrappeds; } function unwrapEvent(wrap, recipientPrivateKey) { const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey); return nip44Decrypt(unwrappedSeal, recipientPrivateKey); } function unwrapManyEvents(wrappedEvents, recipientPrivateKey) { let unwrappedEvents = []; wrappedEvents.forEach((e2) => { unwrappedEvents.push(unwrapEvent(e2, recipientPrivateKey)); }); unwrappedEvents.sort((a, b) => a.created_at - b.created_at); return unwrappedEvents; } function createEvent(recipients, message, conversationTitle, replyTo) { const baseEvent = { created_at: Math.ceil(Date.now() / 1e3), kind: PrivateDirectMessage, tags: [], content: message }; const recipientsArray = Array.isArray(recipients) ? recipients : [recipients]; recipientsArray.forEach(({ publicKey, relayUrl }) => { baseEvent.tags.push(relayUrl ? ["p", publicKey, relayUrl] : ["p", publicKey]); }); if (replyTo) { baseEvent.tags.push(["e", replyTo.eventId, replyTo.relayUrl || "", "reply"]); } if (conversationTitle) { baseEvent.tags.push(["subject", conversationTitle]); } return baseEvent; } function wrapEvent22(senderPrivateKey, recipient, message, conversationTitle, replyTo) { const event = createEvent(recipient, message, conversationTitle, replyTo); return wrapEvent3(event, senderPrivateKey, recipient.publicKey); } function wrapManyEvents2(senderPrivateKey, recipients, message, conversationTitle, replyTo) { if (!recipients || recipients.length === 0) { throw new Error("At least one recipient is required."); } const senderPublicKey = getPublicKey3(senderPrivateKey); return [{ publicKey: senderPublicKey }, ...recipients].map((recipient) => wrapEvent22(senderPrivateKey, recipient, message, conversationTitle, replyTo)); } var unwrapEvent2 = unwrapEvent; var unwrapManyEvents2 = unwrapManyEvents; var nip18_exports = {}; __export22(nip18_exports, { finishRepostEvent: () => finishRepostEvent, getRepostedEvent: () => getRepostedEvent, getRepostedEventPointer: () => getRepostedEventPointer }); function finishRepostEvent(t, reposted, relayUrl, privateKey) { let kind; const tags = [...t.tags ?? [], ["e", reposted.id, relayUrl], ["p", reposted.pubkey]]; if (reposted.kind === ShortTextNote) { kind = Repost; } else { kind = GenericRepost; tags.push(["k", String(reposted.kind)]); } return finalizeEvent3({ kind, tags, content: t.content === "" || reposted.tags?.find((tag) => tag[0] === "-") ? "" : JSON.stringify(reposted), created_at: t.created_at }, privateKey); } function getRepostedEventPointer(event) { if (![Repost, GenericRepost].includes(event.kind)) { return; } let lastETag; let lastPTag; for (let i22 = event.tags.length - 1; i22 >= 0 && (lastETag === void 0 || lastPTag === void 0); i22--) { const tag = event.tags[i22]; if (tag.length >= 2) { if (tag[0] === "e" && lastETag === void 0) { lastETag = tag; } else if (tag[0] === "p" && lastPTag === void 0) { lastPTag = tag; } } } if (lastETag === void 0) { return; } return { id: lastETag[1], relays: [lastETag[2], lastPTag?.[2]].filter((x2) => typeof x2 === "string"), author: lastPTag?.[1] }; } function getRepostedEvent(event, { skipVerification } = {}) { const pointer = getRepostedEventPointer(event); if (pointer === void 0 || event.content === "") { return; } let repostedEvent; try { repostedEvent = JSON.parse(event.content); } catch (error) { return; } if (repostedEvent.id !== pointer.id) { return; } if (!skipVerification && !verifyEvent(repostedEvent)) { return; } return repostedEvent; } var nip21_exports = {}; __export22(nip21_exports, { NOSTR_URI_REGEX: () => NOSTR_URI_REGEX, parse: () => parse2, test: () => test }); var NOSTR_URI_REGEX = new RegExp(`nostr:(${BECH32_REGEX2.source})`); function test(value) { return typeof value === "string" && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value); } function parse2(uri) { const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`)); if (!match) throw new Error(`Invalid Nostr URI: ${uri}`); return { uri: match[0], value: match[1], decoded: decode3(match[1]) }; } var nip25_exports = {}; __export22(nip25_exports, { finishReactionEvent: () => finishReactionEvent, getReactedEventPointer: () => getReactedEventPointer }); function finishReactionEvent(t, reacted, privateKey) { const inheritedTags = reacted.tags.filter((tag) => tag.length >= 2 && (tag[0] === "e" || tag[0] === "p")); return finalizeEvent3({ ...t, kind: Reaction, tags: [...t.tags ?? [], ...inheritedTags, ["e", reacted.id], ["p", reacted.pubkey]], content: t.content ?? "+" }, privateKey); } function getReactedEventPointer(event) { if (event.kind !== Reaction) { return; } let lastETag; let lastPTag; for (let i22 = event.tags.length - 1; i22 >= 0 && (lastETag === void 0 || lastPTag === void 0); i22--) { const tag = event.tags[i22]; if (tag.length >= 2) { if (tag[0] === "e" && lastETag === void 0) { lastETag = tag; } else if (tag[0] === "p" && lastPTag === void 0) { lastPTag = tag; } } } if (lastETag === void 0 || lastPTag === void 0) { return; } return { id: lastETag[1], relays: [lastETag[2], lastPTag[2]].filter((x2) => x2 !== void 0), author: lastPTag[1] }; } var nip27_exports = {}; __export22(nip27_exports, { parse: () => parse3 }); var noCharacter = /\W/m; var noURLCharacter = /[^\w\/] |[^\w\/]$|$|,| /m; var MAX_HASHTAG_LENGTH = 42; function* parse3(content) { let emojis = []; if (typeof content !== "string") { for (let i22 = 0; i22 < content.tags.length; i22++) { const tag = content.tags[i22]; if (tag[0] === "emoji" && tag.length >= 3) { emojis.push({ type: "emoji", shortcode: tag[1], url: tag[2] }); } } content = content.content; } const max = content.length; let prevIndex = 0; let index = 0; mainloop: while (index < max) { const u3 = content.indexOf(":", index); const h2 = content.indexOf("#", index); if (u3 === -1 && h2 === -1) { break mainloop; } if (u3 === -1 || h2 >= 0 && h2 < u3) { if (h2 === 0 || content[h2 - 1] === " ") { const m = content.slice(h2 + 1, h2 + MAX_HASHTAG_LENGTH).match(noCharacter); const end = m ? h2 + 1 + m.index : max; yield { type: "text", text: content.slice(prevIndex, h2) }; yield { type: "hashtag", value: content.slice(h2 + 1, end) }; index = end; prevIndex = index; continue mainloop; } index = h2 + 1; continue mainloop; } if (content.slice(u3 - 5, u3) === "nostr") { const m = content.slice(u3 + 60).match(noCharacter); const end = m ? u3 + 60 + m.index : max; try { let pointer; let { data, type } = decode3(content.slice(u3 + 1, end)); switch (type) { case "npub": pointer = { pubkey: data }; break; case "note": pointer = { id: data }; break; case "nsec": index = end + 1; continue; default: pointer = data; } if (prevIndex !== u3 - 5) { yield { type: "text", text: content.slice(prevIndex, u3 - 5) }; } yield { type: "reference", pointer }; index = end; prevIndex = index; continue mainloop; } catch (_err) { index = u3 + 1; continue mainloop; } } else if (content.slice(u3 - 5, u3) === "https" || content.slice(u3 - 4, u3) === "http") { const m = content.slice(u3 + 4).match(noURLCharacter); const end = m ? u3 + 4 + m.index : max; const prefixLen = content[u3 - 1] === "s" ? 5 : 4; try { let url = new URL(content.slice(u3 - prefixLen, end)); if (url.hostname.indexOf(".") === -1) { throw new Error("invalid url"); } if (prevIndex !== u3 - prefixLen) { yield { type: "text", text: content.slice(prevIndex, u3 - prefixLen) }; } if (/\.(png|jpe?g|gif|webp|heic|svg)$/i.test(url.pathname)) { yield { type: "image", url: url.toString() }; index = end; prevIndex = index; continue mainloop; } if (/\.(mp4|avi|webm|mkv|mov)$/i.test(url.pathname)) { yield { type: "video", url: url.toString() }; index = end; prevIndex = index; continue mainloop; } if (/\.(mp3|aac|ogg|opus|wav|flac)$/i.test(url.pathname)) { yield { type: "audio", url: url.toString() }; index = end; prevIndex = index; continue mainloop; } yield { type: "url", url: url.toString() }; index = end; prevIndex = index; continue mainloop; } catch (_err) { index = end + 1; continue mainloop; } } else if (content.slice(u3 - 3, u3) === "wss" || content.slice(u3 - 2, u3) === "ws") { const m = content.slice(u3 + 4).match(noURLCharacter); const end = m ? u3 + 4 + m.index : max; const prefixLen = content[u3 - 1] === "s" ? 3 : 2; try { let url = new URL(content.slice(u3 - prefixLen, end)); if (url.hostname.indexOf(".") === -1) { throw new Error("invalid ws url"); } if (prevIndex !== u3 - prefixLen) { yield { type: "text", text: content.slice(prevIndex, u3 - prefixLen) }; } yield { type: "relay", url: url.toString() }; index = end; prevIndex = index; continue mainloop; } catch (_err) { index = end + 1; continue mainloop; } } else { for (let e2 = 0; e2 < emojis.length; e2++) { const emoji = emojis[e2]; if (content[u3 + emoji.shortcode.length + 1] === ":" && content.slice(u3 + 1, u3 + emoji.shortcode.length + 1) === emoji.shortcode) { if (prevIndex !== u3) { yield { type: "text", text: content.slice(prevIndex, u3) }; } yield emoji; index = u3 + emoji.shortcode.length + 2; prevIndex = index; continue mainloop; } } index = u3 + 1; continue mainloop; } } if (prevIndex !== max) { yield { type: "text", text: content.slice(prevIndex) }; } } var nip28_exports = {}; __export22(nip28_exports, { channelCreateEvent: () => channelCreateEvent, channelHideMessageEvent: () => channelHideMessageEvent, channelMessageEvent: () => channelMessageEvent, channelMetadataEvent: () => channelMetadataEvent, channelMuteUserEvent: () => channelMuteUserEvent }); var channelCreateEvent = (t, privateKey) => { let content; if (typeof t.content === "object") { content = JSON.stringify(t.content); } else if (typeof t.content === "string") { content = t.content; } else { return; } return finalizeEvent3({ kind: ChannelCreation, tags: [...t.tags ?? []], content, created_at: t.created_at }, privateKey); }; var channelMetadataEvent = (t, privateKey) => { let content; if (typeof t.content === "object") { content = JSON.stringify(t.content); } else if (typeof t.content === "string") { content = t.content; } else { return; } return finalizeEvent3({ kind: ChannelMetadata, tags: [["e", t.channel_create_event_id], ...t.tags ?? []], content, created_at: t.created_at }, privateKey); }; var channelMessageEvent = (t, privateKey) => { const tags = [["e", t.channel_create_event_id, t.relay_url, "root"]]; if (t.reply_to_channel_message_event_id) { tags.push(["e", t.reply_to_channel_message_event_id, t.relay_url, "reply"]); } return finalizeEvent3({ kind: ChannelMessage, tags: [...tags, ...t.tags ?? []], content: t.content, created_at: t.created_at }, privateKey); }; var channelHideMessageEvent = (t, privateKey) => { let content; if (typeof t.content === "object") { content = JSON.stringify(t.content); } else if (typeof t.content === "string") { content = t.content; } else { return; } return finalizeEvent3({ kind: ChannelHideMessage, tags: [["e", t.channel_message_event_id], ...t.tags ?? []], content, created_at: t.created_at }, privateKey); }; var channelMuteUserEvent = (t, privateKey) => { let content; if (typeof t.content === "object") { content = JSON.stringify(t.content); } else if (typeof t.content === "string") { content = t.content; } else { return; } return finalizeEvent3({ kind: ChannelMuteUser, tags: [["p", t.pubkey_to_mute], ...t.tags ?? []], content, created_at: t.created_at }, privateKey); }; var nip30_exports = {}; __export22(nip30_exports, { EMOJI_SHORTCODE_REGEX: () => EMOJI_SHORTCODE_REGEX, matchAll: () => matchAll, regex: () => regex, replaceAll: () => replaceAll }); var EMOJI_SHORTCODE_REGEX = /:(\w+):/; var regex = () => new RegExp(`\\B${EMOJI_SHORTCODE_REGEX.source}\\B`, "g"); function* matchAll(content) { const matches = content.matchAll(regex()); for (const match of matches) { try { const [shortcode, name] = match; yield { shortcode, name, start: match.index, end: match.index + shortcode.length }; } catch (_e2) { } } } function replaceAll(content, replacer) { return content.replaceAll(regex(), (shortcode, name) => { return replacer({ shortcode, name }); }); } var nip39_exports = {}; __export22(nip39_exports, { useFetchImplementation: () => useFetchImplementation3, validateGithub: () => validateGithub }); var _fetch3; try { _fetch3 = fetch; } catch { } function useFetchImplementation3(fetchImplementation) { _fetch3 = fetchImplementation; } async function validateGithub(pubkey, username, proof) { try { let res = await (await _fetch3(`https://gist.github.com/${username}/${proof}/raw`)).text(); return res === `Verifying that I control the following Nostr public key: ${pubkey}`; } catch (_2) { return false; } } var nip47_exports = {}; __export22(nip47_exports, { makeNwcRequestEvent: () => makeNwcRequestEvent, parseConnectionString: () => parseConnectionString }); function parseConnectionString(connectionString) { const { host, pathname, searchParams } = new URL(connectionString); const pubkey = pathname || host; const relay = searchParams.get("relay"); const secret = searchParams.get("secret"); if (!pubkey || !relay || !secret) { throw new Error("invalid connection string"); } return { pubkey, relay, secret }; } async function makeNwcRequestEvent(pubkey, secretKey, invoice) { const content = { method: "pay_invoice", params: { invoice } }; const encryptedContent = encrypt22(secretKey, pubkey, JSON.stringify(content)); const eventTemplate = { kind: NWCWalletRequest, created_at: Math.round(Date.now() / 1e3), content: encryptedContent, tags: [["p", pubkey]] }; return finalizeEvent3(eventTemplate, secretKey); } var nip54_exports = {}; __export22(nip54_exports, { normalizeIdentifier: () => normalizeIdentifier }); function normalizeIdentifier(name) { name = name.trim().toLowerCase(); name = name.normalize("NFKC"); return Array.from(name).map((char) => { if (/\p{Letter}/u.test(char) || /\p{Number}/u.test(char)) { return char; } return "-"; }).join(""); } var nip57_exports = {}; __export22(nip57_exports, { getSatoshisAmountFromBolt11: () => getSatoshisAmountFromBolt11, getZapEndpoint: () => getZapEndpoint, makeZapReceipt: () => makeZapReceipt, makeZapRequest: () => makeZapRequest, useFetchImplementation: () => useFetchImplementation4, validateZapRequest: () => validateZapRequest }); var _fetch4; try { _fetch4 = fetch; } catch { } function useFetchImplementation4(fetchImplementation) { _fetch4 = fetchImplementation; } async function getZapEndpoint(metadata) { try { let lnurl = ""; let { lud06, lud16 } = JSON.parse(metadata.content); if (lud16) { let [name, domain] = lud16.split("@"); lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString(); } else if (lud06) { let { words } = bech322.decode(lud06, 1e3); let data = bech322.fromWords(words); lnurl = utf8Decoder.decode(data); } else { return null; } let res = await _fetch4(lnurl); let body = await res.json(); if (body.allowsNostr && body.nostrPubkey) { return body.callback; } } catch (err) { } return null; } function makeZapRequest(params) { let zr = { kind: 9734, created_at: Math.round(Date.now() / 1e3), content: params.comment || "", tags: [ ["p", "pubkey" in params ? params.pubkey : params.event.pubkey], ["amount", params.amount.toString()], ["relays", ...params.relays] ] }; if ("event" in params) { zr.tags.push(["e", params.event.id]); if (isReplaceableKind(params.event.kind)) { const a = ["a", `${params.event.kind}:${params.event.pubkey}:`]; zr.tags.push(a); } else if (isAddressableKind(params.event.kind)) { let d17 = params.event.tags.find(([t, v6]) => t === "d" && v6); if (!d17) throw new Error("d tag not found or is empty"); const a = ["a", `${params.event.kind}:${params.event.pubkey}:${d17[1]}`]; zr.tags.push(a); } zr.tags.push(["k", params.event.kind.toString()]); } return zr; } function validateZapRequest(zapRequestString) { let zapRequest; try { zapRequest = JSON.parse(zapRequestString); } catch (err) { return "Invalid zap request JSON."; } if (!validateEvent(zapRequest)) return "Zap request is not a valid Nostr event."; if (!verifyEvent(zapRequest)) return "Invalid signature on zap request."; let p5 = zapRequest.tags.find(([t, v6]) => t === "p" && v6); if (!p5) return "Zap request doesn't have a 'p' tag."; if (!p5[1].match(/^[a-f0-9]{64}$/)) return "Zap request 'p' tag is not valid hex."; let e2 = zapRequest.tags.find(([t, v6]) => t === "e" && v6); if (e2 && !e2[1].match(/^[a-f0-9]{64}$/)) return "Zap request 'e' tag is not valid hex."; let relays = zapRequest.tags.find(([t, v6]) => t === "relays" && v6); if (!relays) return "Zap request doesn't have a 'relays' tag."; return null; } function makeZapReceipt({ zapRequest, preimage, bolt11, paidAt }) { let zr = JSON.parse(zapRequest); let tagsFromZapRequest = zr.tags.filter(([t]) => t === "e" || t === "p" || t === "a"); let zap = { kind: 9735, created_at: Math.round(paidAt.getTime() / 1e3), content: "", tags: [...tagsFromZapRequest, ["P", zr.pubkey], ["bolt11", bolt11], ["description", zapRequest]] }; if (preimage) { zap.tags.push(["preimage", preimage]); } return zap; } function getSatoshisAmountFromBolt11(bolt11) { if (bolt11.length < 50) { return 0; } bolt11 = bolt11.substring(0, 50); const idx = bolt11.lastIndexOf("1"); if (idx === -1) { return 0; } const hrp = bolt11.substring(0, idx); if (!hrp.startsWith("lnbc")) { return 0; } const amount = hrp.substring(4); if (amount.length < 1) { return 0; } const char = amount[amount.length - 1]; const digit = char.charCodeAt(0) - 48; const isDigit = digit >= 0 && digit <= 9; let cutPoint = amount.length - 1; if (isDigit) { cutPoint++; } if (cutPoint < 1) { return 0; } const num3 = parseInt(amount.substring(0, cutPoint)); switch (char) { case "m": return num3 * 1e5; case "u": return num3 * 100; case "n": return num3 / 10; case "p": return num3 / 1e4; default: return num3 * 1e8; } } var nip77_exports = {}; __export22(nip77_exports, { Negentropy: () => Negentropy, NegentropyStorageVector: () => NegentropyStorageVector, NegentropySync: () => NegentropySync }); var PROTOCOL_VERSION = 97; var ID_SIZE = 32; var FINGERPRINT_SIZE = 16; var Mode = { Skip: 0, Fingerprint: 1, IdList: 2 }; var WrappedBuffer = class { constructor(buffer) { __publicField(this, "_raw"); __publicField(this, "length"); if (typeof buffer === "number") { this._raw = new Uint8Array(buffer); this.length = 0; } else if (buffer instanceof Uint8Array) { this._raw = new Uint8Array(buffer); this.length = buffer.length; } else { this._raw = new Uint8Array(512); this.length = 0; } } unwrap() { return this._raw.subarray(0, this.length); } get capacity() { return this._raw.byteLength; } extend(buf) { if (buf instanceof WrappedBuffer) buf = buf.unwrap(); if (typeof buf.length !== "number") throw Error("bad length"); const targetSize = buf.length + this.length; if (this.capacity < targetSize) { const oldRaw = this._raw; const newCapacity = Math.max(this.capacity * 2, targetSize); this._raw = new Uint8Array(newCapacity); this._raw.set(oldRaw); } this._raw.set(buf, this.length); this.length += buf.length; } shift() { const first = this._raw[0]; this._raw = this._raw.subarray(1); this.length--; return first; } shiftN(n = 1) { const firstSubarray = this._raw.subarray(0, n); this._raw = this._raw.subarray(n); this.length -= n; return firstSubarray; } }; function decodeVarInt(buf) { let res = 0; while (true) { if (buf.length === 0) throw Error("parse ends prematurely"); let byte = buf.shift(); res = res << 7 | byte & 127; if ((byte & 128) === 0) break; } return res; } function encodeVarInt(n) { if (n === 0) return new WrappedBuffer(new Uint8Array([0])); let o = []; while (n !== 0) { o.push(n & 127); n >>>= 7; } o.reverse(); for (let i22 = 0; i22 < o.length - 1; i22++) o[i22] |= 128; return new WrappedBuffer(new Uint8Array(o)); } function getByte(buf) { return getBytes(buf, 1)[0]; } function getBytes(buf, n) { if (buf.length < n) throw Error("parse ends prematurely"); return buf.shiftN(n); } var Accumulator = class { constructor() { __publicField(this, "buf"); this.setToZero(); } setToZero() { this.buf = new Uint8Array(ID_SIZE); } add(otherBuf) { let currCarry = 0, nextCarry = 0; let p5 = new DataView(this.buf.buffer); let po = new DataView(otherBuf.buffer); for (let i22 = 0; i22 < 8; i22++) { let offset = i22 * 4; let orig = p5.getUint32(offset, true); let otherV = po.getUint32(offset, true); let next = orig; next += currCarry; next += otherV; if (next > 4294967295) nextCarry = 1; p5.setUint32(offset, next & 4294967295, true); currCarry = nextCarry; nextCarry = 0; } } negate() { let p5 = new DataView(this.buf.buffer); for (let i22 = 0; i22 < 8; i22++) { let offset = i22 * 4; p5.setUint32(offset, ~p5.getUint32(offset, true)); } let one = new Uint8Array(ID_SIZE); one[0] = 1; this.add(one); } getFingerprint(n) { let input = new WrappedBuffer(); input.extend(this.buf); input.extend(encodeVarInt(n)); let hash3 = sha25622(input.unwrap()); return hash3.subarray(0, FINGERPRINT_SIZE); } }; var NegentropyStorageVector = class { constructor() { __publicField(this, "items"); __publicField(this, "sealed"); this.items = []; this.sealed = false; } insert(timestamp, id) { if (this.sealed) throw Error("already sealed"); const idb = hexToBytes3(id); if (idb.byteLength !== ID_SIZE) throw Error("bad id size for added item"); this.items.push({ timestamp, id: idb }); } seal() { if (this.sealed) throw Error("already sealed"); this.sealed = true; this.items.sort(itemCompare); for (let i22 = 1; i22 < this.items.length; i22++) { if (itemCompare(this.items[i22 - 1], this.items[i22]) === 0) throw Error("duplicate item inserted"); } } unseal() { this.sealed = false; } size() { this._checkSealed(); return this.items.length; } getItem(i22) { this._checkSealed(); if (i22 >= this.items.length) throw Error("out of range"); return this.items[i22]; } iterate(begin, end, cb) { this._checkSealed(); this._checkBounds(begin, end); for (let i22 = begin; i22 < end; ++i22) { if (!cb(this.items[i22], i22)) break; } } findLowerBound(begin, end, bound) { this._checkSealed(); this._checkBounds(begin, end); return this._binarySearch(this.items, begin, end, (a) => itemCompare(a, bound) < 0); } fingerprint(begin, end) { let out = new Accumulator(); out.setToZero(); this.iterate(begin, end, (item) => { out.add(item.id); return true; }); return out.getFingerprint(end - begin); } _checkSealed() { if (!this.sealed) throw Error("not sealed"); } _checkBounds(begin, end) { if (begin > end || end > this.items.length) throw Error("bad range"); } _binarySearch(arr, first, last, cmp2) { let count = last - first; while (count > 0) { let it2 = first; let step = Math.floor(count / 2); it2 += step; if (cmp2(arr[it2])) { first = ++it2; count -= step + 1; } else { count = step; } } return first; } }; var Negentropy = class { constructor(storage, frameSizeLimit = 6e4) { __publicField(this, "storage"); __publicField(this, "frameSizeLimit"); __publicField(this, "lastTimestampIn"); __publicField(this, "lastTimestampOut"); if (frameSizeLimit < 4096) throw Error("frameSizeLimit too small"); this.storage = storage; this.frameSizeLimit = frameSizeLimit; this.lastTimestampIn = 0; this.lastTimestampOut = 0; } _bound(timestamp, id) { return { timestamp, id: id || new Uint8Array(0) }; } initiate() { let output4 = new WrappedBuffer(); output4.extend(new Uint8Array([PROTOCOL_VERSION])); this.splitRange(0, this.storage.size(), this._bound(Number.MAX_VALUE), output4); return bytesToHex3(output4.unwrap()); } reconcile(queryMsg, onhave, onneed) { const query3 = new WrappedBuffer(hexToBytes3(queryMsg)); this.lastTimestampIn = this.lastTimestampOut = 0; let fullOutput = new WrappedBuffer(); fullOutput.extend(new Uint8Array([PROTOCOL_VERSION])); let protocolVersion = getByte(query3); if (protocolVersion < 96 || protocolVersion > 111) throw Error("invalid negentropy protocol version byte"); if (protocolVersion !== PROTOCOL_VERSION) { throw Error("unsupported negentropy protocol version requested: " + (protocolVersion - 96)); } let storageSize = this.storage.size(); let prevBound = this._bound(0); let prevIndex = 0; let skip = false; while (query3.length !== 0) { let o = new WrappedBuffer(); let doSkip = () => { if (skip) { skip = false; o.extend(this.encodeBound(prevBound)); o.extend(encodeVarInt(Mode.Skip)); } }; let currBound = this.decodeBound(query3); let mode = decodeVarInt(query3); let lower = prevIndex; let upper = this.storage.findLowerBound(prevIndex, storageSize, currBound); if (mode === Mode.Skip) { skip = true; } else if (mode === Mode.Fingerprint) { let theirFingerprint = getBytes(query3, FINGERPRINT_SIZE); let ourFingerprint = this.storage.fingerprint(lower, upper); if (compareUint8Array(theirFingerprint, ourFingerprint) !== 0) { doSkip(); this.splitRange(lower, upper, currBound, o); } else { skip = true; } } else if (mode === Mode.IdList) { let numIds = decodeVarInt(query3); let theirElems = {}; for (let i22 = 0; i22 < numIds; i22++) { let e2 = getBytes(query3, ID_SIZE); theirElems[bytesToHex3(e2)] = e2; } skip = true; this.storage.iterate(lower, upper, (item) => { let k2 = item.id; const id = bytesToHex3(k2); if (!theirElems[id]) { onhave?.(id); } else { delete theirElems[bytesToHex3(k2)]; } return true; }); if (onneed) { for (let v6 of Object.values(theirElems)) { onneed(bytesToHex3(v6)); } } } else { throw Error("unexpected mode"); } if (this.exceededFrameSizeLimit(fullOutput.length + o.length)) { let remainingFingerprint = this.storage.fingerprint(upper, storageSize); fullOutput.extend(this.encodeBound(this._bound(Number.MAX_VALUE))); fullOutput.extend(encodeVarInt(Mode.Fingerprint)); fullOutput.extend(remainingFingerprint); break; } else { fullOutput.extend(o); } prevIndex = upper; prevBound = currBound; } return fullOutput.length === 1 ? null : bytesToHex3(fullOutput.unwrap()); } splitRange(lower, upper, upperBound, o) { let numElems = upper - lower; let buckets = 16; if (numElems < buckets * 2) { o.extend(this.encodeBound(upperBound)); o.extend(encodeVarInt(Mode.IdList)); o.extend(encodeVarInt(numElems)); this.storage.iterate(lower, upper, (item) => { o.extend(item.id); return true; }); } else { let itemsPerBucket = Math.floor(numElems / buckets); let bucketsWithExtra = numElems % buckets; let curr = lower; for (let i22 = 0; i22 < buckets; i22++) { let bucketSize = itemsPerBucket + (i22 < bucketsWithExtra ? 1 : 0); let ourFingerprint = this.storage.fingerprint(curr, curr + bucketSize); curr += bucketSize; let nextBound; if (curr === upper) { nextBound = upperBound; } else { let prevItem; let currItem; this.storage.iterate(curr - 1, curr + 1, (item, index) => { if (index === curr - 1) prevItem = item; else currItem = item; return true; }); nextBound = this.getMinimalBound(prevItem, currItem); } o.extend(this.encodeBound(nextBound)); o.extend(encodeVarInt(Mode.Fingerprint)); o.extend(ourFingerprint); } } } exceededFrameSizeLimit(n) { return n > this.frameSizeLimit - 200; } decodeTimestampIn(encoded) { let timestamp = decodeVarInt(encoded); timestamp = timestamp === 0 ? Number.MAX_VALUE : timestamp - 1; if (this.lastTimestampIn === Number.MAX_VALUE || timestamp === Number.MAX_VALUE) { this.lastTimestampIn = Number.MAX_VALUE; return Number.MAX_VALUE; } timestamp += this.lastTimestampIn; this.lastTimestampIn = timestamp; return timestamp; } decodeBound(encoded) { let timestamp = this.decodeTimestampIn(encoded); let len = decodeVarInt(encoded); if (len > ID_SIZE) throw Error("bound key too long"); let id = getBytes(encoded, len); return { timestamp, id }; } encodeTimestampOut(timestamp) { if (timestamp === Number.MAX_VALUE) { this.lastTimestampOut = Number.MAX_VALUE; return encodeVarInt(0); } let temp = timestamp; timestamp -= this.lastTimestampOut; this.lastTimestampOut = temp; return encodeVarInt(timestamp + 1); } encodeBound(key) { let output4 = new WrappedBuffer(); output4.extend(this.encodeTimestampOut(key.timestamp)); output4.extend(encodeVarInt(key.id.length)); output4.extend(key.id); return output4; } getMinimalBound(prev, curr) { if (curr.timestamp !== prev.timestamp) { return this._bound(curr.timestamp); } else { let sharedPrefixBytes = 0; let currKey = curr.id; let prevKey = prev.id; for (let i22 = 0; i22 < ID_SIZE; i22++) { if (currKey[i22] !== prevKey[i22]) break; sharedPrefixBytes++; } return this._bound(curr.timestamp, curr.id.subarray(0, sharedPrefixBytes + 1)); } } }; function compareUint8Array(a, b) { for (let i22 = 0; i22 < a.byteLength; i22++) { if (a[i22] < b[i22]) return -1; if (a[i22] > b[i22]) return 1; } if (a.byteLength > b.byteLength) return 1; if (a.byteLength < b.byteLength) return -1; return 0; } function itemCompare(a, b) { if (a.timestamp === b.timestamp) { return compareUint8Array(a.id, b.id); } return a.timestamp - b.timestamp; } var NegentropySync = class { constructor(relay, storage, filter, params = {}) { __publicField(this, "relay"); __publicField(this, "storage"); __publicField(this, "neg"); __publicField(this, "filter"); __publicField(this, "subscription"); __publicField(this, "onhave"); __publicField(this, "onneed"); this.relay = relay; this.storage = storage; this.neg = new Negentropy(storage); this.onhave = params.onhave; this.onneed = params.onneed; this.filter = filter; this.subscription = this.relay.prepareSubscription([{}], { label: params.label || "negentropy" }); this.subscription.oncustom = (data) => { switch (data[0]) { case "NEG-MSG": { if (data.length < 3) { console.warn(`got invalid NEG-MSG from ${this.relay.url}: ${data}`); } try { const response = this.neg.reconcile(data[2], this.onhave, this.onneed); if (response) { this.relay.send(`["NEG-MSG", "${this.subscription.id}", "${response}"]`); } else { this.close(); params.onclose?.(); } } catch (error) { console.error("negentropy reconcile error:", error); params?.onclose?.(`reconcile error: ${error}`); } break; } case "NEG-CLOSE": { const reason = data[2]; console.warn("negentropy error:", reason); params.onclose?.(reason); break; } case "NEG-ERR": { params.onclose?.(); } } }; } async start() { const initMsg = this.neg.initiate(); this.relay.send(`["NEG-OPEN","${this.subscription.id}",${JSON.stringify(this.filter)},"${initMsg}"]`); } close() { this.relay.send(`["NEG-CLOSE","${this.subscription.id}"]`); this.subscription.close(); } }; var nip98_exports = {}; __export22(nip98_exports, { getToken: () => getToken, hashPayload: () => hashPayload, unpackEventFromToken: () => unpackEventFromToken, validateEvent: () => validateEvent2, validateEventKind: () => validateEventKind, validateEventMethodTag: () => validateEventMethodTag, validateEventPayloadTag: () => validateEventPayloadTag, validateEventTimestamp: () => validateEventTimestamp, validateEventUrlTag: () => validateEventUrlTag, validateToken: () => validateToken }); var _authorizationScheme = "Nostr "; async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false, payload) { const event = { kind: HTTPAuth, tags: [ ["u", loginUrl], ["method", httpMethod] ], created_at: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3), content: "" }; if (payload) { event.tags.push(["payload", hashPayload(payload)]); } const signedEvent = await sign(event); const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : ""; return authorizationScheme + base64.encode(utf8Encoder.encode(JSON.stringify(signedEvent))); } async function validateToken(token, url, method) { const event = await unpackEventFromToken(token).catch((error) => { throw error; }); const valid = await validateEvent2(event, url, method).catch((error) => { throw error; }); return valid; } async function unpackEventFromToken(token) { if (!token) { throw new Error("Missing token"); } token = token.replace(_authorizationScheme, ""); const eventB64 = utf8Decoder.decode(base64.decode(token)); if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith("{")) { throw new Error("Invalid token"); } const event = JSON.parse(eventB64); return event; } function validateEventTimestamp(event) { if (!event.created_at) { return false; } return Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3) - event.created_at < 60; } function validateEventKind(event) { return event.kind === HTTPAuth; } function validateEventUrlTag(event, url) { const urlTag = event.tags.find((t) => t[0] === "u"); if (!urlTag) { return false; } return urlTag.length > 0 && urlTag[1] === url; } function validateEventMethodTag(event, method) { const methodTag = event.tags.find((t) => t[0] === "method"); if (!methodTag) { return false; } return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase(); } function hashPayload(payload) { const hash3 = sha25622(utf8Encoder.encode(JSON.stringify(payload))); return bytesToHex22(hash3); } function validateEventPayloadTag(event, payload) { const payloadTag = event.tags.find((t) => t[0] === "payload"); if (!payloadTag) { return false; } const payloadHash = hashPayload(payload); return payloadTag.length > 0 && payloadTag[1] === payloadHash; } async function validateEvent2(event, url, method, body) { if (!verifyEvent(event)) { throw new Error("Invalid nostr event, signature invalid"); } if (!validateEventKind(event)) { throw new Error("Invalid nostr event, kind invalid"); } if (!validateEventTimestamp(event)) { throw new Error("Invalid nostr event, created_at timestamp invalid"); } if (!validateEventUrlTag(event, url)) { throw new Error("Invalid nostr event, url tag invalid"); } if (!validateEventMethodTag(event, method)) { throw new Error("Invalid nostr event, method tag invalid"); } if (Boolean(body) && typeof body === "object" && Object.keys(body).length > 0) { if (!validateEventPayloadTag(event, body)) { throw new Error("Invalid nostr event, payload tag does not match request body hash"); } } return true; } var crypto32 = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : void 0; function isBytes22(a) { return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array"; } function anumber3(n) { if (!Number.isSafeInteger(n) || n < 0) throw new Error("positive integer expected, got " + n); } function abytes2(b, ...lengths) { if (!isBytes22(b)) throw new Error("Uint8Array expected"); if (lengths.length > 0 && !lengths.includes(b.length)) throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length); } function ahash2(h2) { if (typeof h2 !== "function" || typeof h2.create !== "function") throw new Error("Hash should be wrapped by utils.createHasher"); anumber3(h2.outputLen); anumber3(h2.blockLen); } function aexists2(instance, checkFinished = true) { if (instance.destroyed) throw new Error("Hash instance has been destroyed"); if (checkFinished && instance.finished) throw new Error("Hash#digest() has already been called"); } function aoutput2(out, instance) { abytes2(out); const min = instance.outputLen; if (out.length < min) { throw new Error("digestInto() expects output buffer of length at least " + min); } } function clean2(...arrays) { for (let i22 = 0; i22 < arrays.length; i22++) { arrays[i22].fill(0); } } function createView4(arr) { return new DataView(arr.buffer, arr.byteOffset, arr.byteLength); } function rotr3(word, shift) { return word << 32 - shift | word >>> shift; } var hasHexBuiltin2 = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function")(); var hexes4 = /* @__PURE__ */ Array.from({ length: 256 }, (_2, i22) => i22.toString(16).padStart(2, "0")); function bytesToHex4(bytes4) { abytes2(bytes4); if (hasHexBuiltin2) return bytes4.toHex(); let hex2 = ""; for (let i22 = 0; i22 < bytes4.length; i22++) { hex2 += hexes4[bytes4[i22]]; } return hex2; } var asciis22 = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; function asciiToBase1622(ch) { if (ch >= asciis22._0 && ch <= asciis22._9) return ch - asciis22._0; if (ch >= asciis22.A && ch <= asciis22.F) return ch - (asciis22.A - 10); if (ch >= asciis22.a && ch <= asciis22.f) return ch - (asciis22.a - 10); return; } function hexToBytes4(hex2) { if (typeof hex2 !== "string") throw new Error("hex string expected, got " + typeof hex2); if (hasHexBuiltin2) return Uint8Array.fromHex(hex2); const hl = hex2.length; const al = hl / 2; if (hl % 2) throw new Error("hex string expected, got unpadded hex of length " + hl); const array = new Uint8Array(al); for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { const n1 = asciiToBase1622(hex2.charCodeAt(hi)); const n2 = asciiToBase1622(hex2.charCodeAt(hi + 1)); if (n1 === void 0 || n2 === void 0) { const char = hex2[hi] + hex2[hi + 1]; throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi); } array[ai] = n1 * 16 + n2; } return array; } function utf8ToBytes5(str) { if (typeof str !== "string") throw new Error("string expected"); return new Uint8Array(new TextEncoder().encode(str)); } function toBytes4(data) { if (typeof data === "string") data = utf8ToBytes5(data); abytes2(data); return data; } function concatBytes4(...arrays) { let sum = 0; for (let i22 = 0; i22 < arrays.length; i22++) { const a = arrays[i22]; abytes2(a); sum += a.length; } const res = new Uint8Array(sum); for (let i22 = 0, pad2 = 0; i22 < arrays.length; i22++) { const a = arrays[i22]; res.set(a, pad2); pad2 += a.length; } return res; } var Hash3 = class { }; function createHasher2(hashCons) { const hashC = (msg) => hashCons().update(toBytes4(msg)).digest(); const tmp = hashCons(); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.create = () => hashCons(); return hashC; } function randomBytes3(bytesLength = 32) { if (crypto32 && typeof crypto32.getRandomValues === "function") { return crypto32.getRandomValues(new Uint8Array(bytesLength)); } if (crypto32 && typeof crypto32.randomBytes === "function") { return Uint8Array.from(crypto32.randomBytes(bytesLength)); } throw new Error("crypto.getRandomValues must be defined"); } function setBigUint644(view, byteOffset, value, isLE4) { if (typeof view.setBigUint64 === "function") return view.setBigUint64(byteOffset, value, isLE4); const _32n2 = BigInt(32); const _u32_max = BigInt(4294967295); const wh = Number(value >> _32n2 & _u32_max); const wl = Number(value & _u32_max); const h2 = isLE4 ? 4 : 0; const l3 = isLE4 ? 0 : 4; view.setUint32(byteOffset + h2, wh, isLE4); view.setUint32(byteOffset + l3, wl, isLE4); } function Chi3(a, b, c) { return a & b ^ ~a & c; } function Maj3(a, b, c) { return a & b ^ a & c ^ b & c; } var HashMD2 = class extends Hash3 { constructor(blockLen, outputLen, padOffset, isLE4) { super(); this.finished = false; this.length = 0; this.pos = 0; this.destroyed = false; this.blockLen = blockLen; this.outputLen = outputLen; this.padOffset = padOffset; this.isLE = isLE4; this.buffer = new Uint8Array(blockLen); this.view = createView4(this.buffer); } update(data) { aexists2(this); data = toBytes4(data); abytes2(data); const { view, buffer, blockLen } = this; const len = data.length; for (let pos = 0; pos < len; ) { const take = Math.min(blockLen - this.pos, len - pos); if (take === blockLen) { const dataView = createView4(data); for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos); continue; } buffer.set(data.subarray(pos, pos + take), this.pos); this.pos += take; pos += take; if (this.pos === blockLen) { this.process(view, 0); this.pos = 0; } } this.length += data.length; this.roundClean(); return this; } digestInto(out) { aexists2(this); aoutput2(out, this); this.finished = true; const { buffer, view, blockLen, isLE: isLE4 } = this; let { pos } = this; buffer[pos++] = 128; clean2(this.buffer.subarray(pos)); if (this.padOffset > blockLen - pos) { this.process(view, 0); pos = 0; } for (let i22 = pos; i22 < blockLen; i22++) buffer[i22] = 0; setBigUint644(view, blockLen - 8, BigInt(this.length * 8), isLE4); this.process(view, 0); const oview = createView4(out); const len = this.outputLen; if (len % 4) throw new Error("_sha2: outputLen should be aligned to 32bit"); const outLen = len / 4; const state = this.get(); if (outLen > state.length) throw new Error("_sha2: outputLen bigger than state"); for (let i22 = 0; i22 < outLen; i22++) oview.setUint32(4 * i22, state[i22], isLE4); } digest() { const { buffer, outputLen } = this; this.digestInto(buffer); const res = buffer.slice(0, outputLen); this.destroy(); return res; } _cloneInto(to) { to || (to = new this.constructor()); to.set(...this.get()); const { blockLen, buffer, length, finished, destroyed, pos } = this; to.destroyed = destroyed; to.finished = finished; to.length = length; to.pos = pos; if (length % blockLen) to.buffer.set(buffer); return to; } clone() { return this._cloneInto(); } }; var SHA256_IV2 = /* @__PURE__ */ Uint32Array.from([ 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225 ]); var SHA256_K3 = /* @__PURE__ */ Uint32Array.from([ 1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298 ]); var SHA256_W3 = /* @__PURE__ */ new Uint32Array(64); var SHA2563 = class extends HashMD2 { constructor(outputLen = 32) { super(64, outputLen, 8, false); this.A = SHA256_IV2[0] | 0; this.B = SHA256_IV2[1] | 0; this.C = SHA256_IV2[2] | 0; this.D = SHA256_IV2[3] | 0; this.E = SHA256_IV2[4] | 0; this.F = SHA256_IV2[5] | 0; this.G = SHA256_IV2[6] | 0; this.H = SHA256_IV2[7] | 0; } get() { const { A, B, C: C2, D: D3, E: E2, F: F2, G: G2, H: H2 } = this; return [A, B, C2, D3, E2, F2, G2, H2]; } set(A, B, C2, D3, E2, F2, G2, H2) { this.A = A | 0; this.B = B | 0; this.C = C2 | 0; this.D = D3 | 0; this.E = E2 | 0; this.F = F2 | 0; this.G = G2 | 0; this.H = H2 | 0; } process(view, offset) { for (let i22 = 0; i22 < 16; i22++, offset += 4) SHA256_W3[i22] = view.getUint32(offset, false); for (let i22 = 16; i22 < 64; i22++) { const W15 = SHA256_W3[i22 - 15]; const W22 = SHA256_W3[i22 - 2]; const s0 = rotr3(W15, 7) ^ rotr3(W15, 18) ^ W15 >>> 3; const s1 = rotr3(W22, 17) ^ rotr3(W22, 19) ^ W22 >>> 10; SHA256_W3[i22] = s1 + SHA256_W3[i22 - 7] + s0 + SHA256_W3[i22 - 16] | 0; } let { A, B, C: C2, D: D3, E: E2, F: F2, G: G2, H: H2 } = this; for (let i22 = 0; i22 < 64; i22++) { const sigma1 = rotr3(E2, 6) ^ rotr3(E2, 11) ^ rotr3(E2, 25); const T1 = H2 + sigma1 + Chi3(E2, F2, G2) + SHA256_K3[i22] + SHA256_W3[i22] | 0; const sigma0 = rotr3(A, 2) ^ rotr3(A, 13) ^ rotr3(A, 22); const T22 = sigma0 + Maj3(A, B, C2) | 0; H2 = G2; G2 = F2; F2 = E2; E2 = D3 + T1 | 0; D3 = C2; C2 = B; B = A; A = T1 + T22 | 0; } A = A + this.A | 0; B = B + this.B | 0; C2 = C2 + this.C | 0; D3 = D3 + this.D | 0; E2 = E2 + this.E | 0; F2 = F2 + this.F | 0; G2 = G2 + this.G | 0; H2 = H2 + this.H | 0; this.set(A, B, C2, D3, E2, F2, G2, H2); } roundClean() { clean2(SHA256_W3); } destroy() { this.set(0, 0, 0, 0, 0, 0, 0, 0); clean2(this.buffer); } }; var sha25632 = /* @__PURE__ */ createHasher2(() => new SHA2563()); var HMAC3 = class extends Hash3 { constructor(hash3, _key) { super(); this.finished = false; this.destroyed = false; ahash2(hash3); const key = toBytes4(_key); this.iHash = hash3.create(); if (typeof this.iHash.update !== "function") throw new Error("Expected instance of class which extends utils.Hash"); this.blockLen = this.iHash.blockLen; this.outputLen = this.iHash.outputLen; const blockLen = this.blockLen; const pad2 = new Uint8Array(blockLen); pad2.set(key.length > blockLen ? hash3.create().update(key).digest() : key); for (let i22 = 0; i22 < pad2.length; i22++) pad2[i22] ^= 54; this.iHash.update(pad2); this.oHash = hash3.create(); for (let i22 = 0; i22 < pad2.length; i22++) pad2[i22] ^= 54 ^ 92; this.oHash.update(pad2); clean2(pad2); } update(buf) { aexists2(this); this.iHash.update(buf); return this; } digestInto(out) { aexists2(this); abytes2(out, this.outputLen); this.finished = true; this.iHash.digestInto(out); this.oHash.update(out); this.oHash.digestInto(out); this.destroy(); } digest() { const out = new Uint8Array(this.oHash.outputLen); this.digestInto(out); return out; } _cloneInto(to) { to || (to = Object.create(Object.getPrototypeOf(this), {})); const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; to = to; to.finished = finished; to.destroyed = destroyed; to.blockLen = blockLen; to.outputLen = outputLen; to.oHash = oHash._cloneInto(to.oHash); to.iHash = iHash._cloneInto(to.iHash); return to; } clone() { return this._cloneInto(); } destroy() { this.destroyed = true; this.oHash.destroy(); this.iHash.destroy(); } }; var hmac3 = (hash3, key, message) => new HMAC3(hash3, key).update(message).digest(); hmac3.create = (hash3, key) => new HMAC3(hash3, key); var _0n62 = /* @__PURE__ */ BigInt(0); var _1n62 = /* @__PURE__ */ BigInt(1); function _abool22(value, title = "") { if (typeof value !== "boolean") { const prefix = title && `"${title}"`; throw new Error(prefix + "expected boolean, got type=" + typeof value); } return value; } function _abytes22(value, length, title = "") { const bytes4 = isBytes22(value); const len = value?.length; const needsLen = length !== void 0; if (!bytes4 || needsLen && len !== length) { const prefix = title && `"${title}" `; const ofLen = needsLen ? ` of length ${length}` : ""; const got = bytes4 ? `length=${len}` : `type=${typeof value}`; throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got); } return value; } function numberToHexUnpadded22(num3) { const hex2 = num3.toString(16); return hex2.length & 1 ? "0" + hex2 : hex2; } function hexToNumber22(hex2) { if (typeof hex2 !== "string") throw new Error("hex string expected, got " + typeof hex2); return hex2 === "" ? _0n62 : BigInt("0x" + hex2); } function bytesToNumberBE22(bytes4) { return hexToNumber22(bytesToHex4(bytes4)); } function bytesToNumberLE22(bytes4) { abytes2(bytes4); return hexToNumber22(bytesToHex4(Uint8Array.from(bytes4).reverse())); } function numberToBytesBE22(n, len) { return hexToBytes4(n.toString(16).padStart(len * 2, "0")); } function numberToBytesLE22(n, len) { return numberToBytesBE22(n, len).reverse(); } function ensureBytes22(title, hex2, expectedLength) { let res; if (typeof hex2 === "string") { try { res = hexToBytes4(hex2); } catch (e2) { throw new Error(title + " must be hex string or Uint8Array, cause: " + e2); } } else if (isBytes22(hex2)) { res = Uint8Array.from(hex2); } else { throw new Error(title + " must be hex string or Uint8Array"); } const len = res.length; if (typeof expectedLength === "number" && len !== expectedLength) throw new Error(title + " of length " + expectedLength + " expected, got " + len); return res; } var isPosBig2 = (n) => typeof n === "bigint" && _0n62 <= n; function inRange2(n, min, max) { return isPosBig2(n) && isPosBig2(min) && isPosBig2(max) && min <= n && n < max; } function aInRange2(title, n, min, max) { if (!inRange2(n, min, max)) throw new Error("expected valid " + title + ": " + min + " <= n < " + max + ", got " + n); } function bitLen22(n) { let len; for (len = 0; n > _0n62; n >>= _1n62, len += 1) ; return len; } var bitMask22 = (n) => (_1n62 << BigInt(n)) - _1n62; function createHmacDrbg22(hashLen, qByteLen, hmacFn) { if (typeof hashLen !== "number" || hashLen < 2) throw new Error("hashLen must be a number"); if (typeof qByteLen !== "number" || qByteLen < 2) throw new Error("qByteLen must be a number"); if (typeof hmacFn !== "function") throw new Error("hmacFn must be a function"); const u8n2 = (len) => new Uint8Array(len); const u8of = (byte) => Uint8Array.of(byte); let v6 = u8n2(hashLen); let k2 = u8n2(hashLen); let i22 = 0; const reset = () => { v6.fill(1); k2.fill(0); i22 = 0; }; const h2 = (...b) => hmacFn(k2, v6, ...b); const reseed = (seed = u8n2(0)) => { k2 = h2(u8of(0), seed); v6 = h2(); if (seed.length === 0) return; k2 = h2(u8of(1), seed); v6 = h2(); }; const gen = () => { if (i22++ >= 1e3) throw new Error("drbg: tried 1000 values"); let len = 0; const out = []; while (len < qByteLen) { v6 = h2(); const sl = v6.slice(); out.push(sl); len += v6.length; } return concatBytes4(...out); }; const genUntil = (seed, pred) => { reset(); reseed(seed); let res = void 0; while (!(res = pred(gen()))) reseed(); reset(); return res; }; return genUntil; } function _validateObject2(object, fields, optFields = {}) { if (!object || typeof object !== "object") throw new Error("expected valid options object"); function checkField(fieldName, expectedType, isOpt) { const val = object[fieldName]; if (isOpt && val === void 0) return; const current = typeof val; if (current !== expectedType || val === null) throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`); } Object.entries(fields).forEach(([k2, v6]) => checkField(k2, v6, false)); Object.entries(optFields).forEach(([k2, v6]) => checkField(k2, v6, true)); } function memoized2(fn) { const map = /* @__PURE__ */ new WeakMap(); return (arg, ...args) => { const val = map.get(arg); if (val !== void 0) return val; const computed = fn(arg, ...args); map.set(arg, computed); return computed; }; } var _0n7 = BigInt(0); var _1n7 = BigInt(1); var _2n5 = /* @__PURE__ */ BigInt(2); var _3n32 = /* @__PURE__ */ BigInt(3); var _4n32 = /* @__PURE__ */ BigInt(4); var _5n22 = /* @__PURE__ */ BigInt(5); var _7n2 = /* @__PURE__ */ BigInt(7); var _8n22 = /* @__PURE__ */ BigInt(8); var _9n22 = /* @__PURE__ */ BigInt(9); var _16n22 = /* @__PURE__ */ BigInt(16); function mod22(a, b) { const result = a % b; return result >= _0n7 ? result : b + result; } function pow222(x2, power, modulo) { let res = x2; while (power-- > _0n7) { res *= res; res %= modulo; } return res; } function invert22(number4, modulo) { if (number4 === _0n7) throw new Error("invert: expected non-zero number"); if (modulo <= _0n7) throw new Error("invert: expected positive modulus, got " + modulo); let a = mod22(number4, modulo); let b = modulo; let x2 = _0n7, y2 = _1n7, u3 = _1n7, v6 = _0n7; while (a !== _0n7) { const q2 = b / a; const r = b % a; const m = x2 - u3 * q2; const n = y2 - v6 * q2; b = a, a = r, x2 = u3, y2 = v6, u3 = m, v6 = n; } const gcd22 = b; if (gcd22 !== _1n7) throw new Error("invert: does not exist"); return mod22(x2, modulo); } function assertIsSquare2(Fp2, root, n) { if (!Fp2.eql(Fp2.sqr(root), n)) throw new Error("Cannot find square root"); } function sqrt3mod42(Fp2, n) { const p1div4 = (Fp2.ORDER + _1n7) / _4n32; const root = Fp2.pow(n, p1div4); assertIsSquare2(Fp2, root, n); return root; } function sqrt5mod82(Fp2, n) { const p5div8 = (Fp2.ORDER - _5n22) / _8n22; const n2 = Fp2.mul(n, _2n5); const v6 = Fp2.pow(n2, p5div8); const nv = Fp2.mul(n, v6); const i22 = Fp2.mul(Fp2.mul(nv, _2n5), v6); const root = Fp2.mul(nv, Fp2.sub(i22, Fp2.ONE)); assertIsSquare2(Fp2, root, n); return root; } function sqrt9mod162(P) { const Fp_ = Field22(P); const tn = tonelliShanks22(P); const c1 = tn(Fp_, Fp_.neg(Fp_.ONE)); const c2 = tn(Fp_, c1); const c3 = tn(Fp_, Fp_.neg(c1)); const c4 = (P + _7n2) / _16n22; return (Fp2, n) => { let tv1 = Fp2.pow(n, c4); let tv2 = Fp2.mul(tv1, c1); const tv3 = Fp2.mul(tv1, c2); const tv4 = Fp2.mul(tv1, c3); const e1 = Fp2.eql(Fp2.sqr(tv2), n); const e2 = Fp2.eql(Fp2.sqr(tv3), n); tv1 = Fp2.cmov(tv1, tv2, e1); tv2 = Fp2.cmov(tv4, tv3, e2); const e3 = Fp2.eql(Fp2.sqr(tv2), n); const root = Fp2.cmov(tv1, tv2, e3); assertIsSquare2(Fp2, root, n); return root; }; } function tonelliShanks22(P) { if (P < _3n32) throw new Error("sqrt is not defined for small field"); let Q2 = P - _1n7; let S4 = 0; while (Q2 % _2n5 === _0n7) { Q2 /= _2n5; S4++; } let Z = _2n5; const _Fp = Field22(P); while (FpLegendre2(_Fp, Z) === 1) { if (Z++ > 1e3) throw new Error("Cannot find square root: probably non-prime P"); } if (S4 === 1) return sqrt3mod42; let cc = _Fp.pow(Z, Q2); const Q1div2 = (Q2 + _1n7) / _2n5; return function tonelliSlow(Fp2, n) { if (Fp2.is0(n)) return n; if (FpLegendre2(Fp2, n) !== 1) throw new Error("Cannot find square root"); let M2 = S4; let c = Fp2.mul(Fp2.ONE, cc); let t = Fp2.pow(n, Q2); let R2 = Fp2.pow(n, Q1div2); while (!Fp2.eql(t, Fp2.ONE)) { if (Fp2.is0(t)) return Fp2.ZERO; let i22 = 1; let t_tmp = Fp2.sqr(t); while (!Fp2.eql(t_tmp, Fp2.ONE)) { i22++; t_tmp = Fp2.sqr(t_tmp); if (i22 === M2) throw new Error("Cannot find square root"); } const exponent = _1n7 << BigInt(M2 - i22 - 1); const b = Fp2.pow(c, exponent); M2 = i22; c = Fp2.sqr(b); t = Fp2.mul(t, c); R2 = Fp2.mul(R2, b); } return R2; }; } function FpSqrt22(P) { if (P % _4n32 === _3n32) return sqrt3mod42; if (P % _8n22 === _5n22) return sqrt5mod82; if (P % _16n22 === _9n22) return sqrt9mod162(P); return tonelliShanks22(P); } var FIELD_FIELDS22 = [ "create", "isValid", "is0", "neg", "inv", "sqrt", "sqr", "eql", "add", "sub", "mul", "pow", "div", "addN", "subN", "mulN", "sqrN" ]; function validateField22(field) { const initial = { ORDER: "bigint", MASK: "bigint", BYTES: "number", BITS: "number" }; const opts = FIELD_FIELDS22.reduce((map, val) => { map[val] = "function"; return map; }, initial); _validateObject2(field, opts); return field; } function FpPow22(Fp2, num3, power) { if (power < _0n7) throw new Error("invalid exponent, negatives unsupported"); if (power === _0n7) return Fp2.ONE; if (power === _1n7) return num3; let p5 = Fp2.ONE; let d17 = num3; while (power > _0n7) { if (power & _1n7) p5 = Fp2.mul(p5, d17); d17 = Fp2.sqr(d17); power >>= _1n7; } return p5; } function FpInvertBatch22(Fp2, nums, passZero = false) { const inverted = new Array(nums.length).fill(passZero ? Fp2.ZERO : void 0); const multipliedAcc = nums.reduce((acc, num3, i22) => { if (Fp2.is0(num3)) return acc; inverted[i22] = acc; return Fp2.mul(acc, num3); }, Fp2.ONE); const invertedAcc = Fp2.inv(multipliedAcc); nums.reduceRight((acc, num3, i22) => { if (Fp2.is0(num3)) return acc; inverted[i22] = Fp2.mul(acc, inverted[i22]); return Fp2.mul(acc, num3); }, invertedAcc); return inverted; } function FpLegendre2(Fp2, n) { const p1mod2 = (Fp2.ORDER - _1n7) / _2n5; const powered = Fp2.pow(n, p1mod2); const yes = Fp2.eql(powered, Fp2.ONE); const zero = Fp2.eql(powered, Fp2.ZERO); const no = Fp2.eql(powered, Fp2.neg(Fp2.ONE)); if (!yes && !zero && !no) throw new Error("invalid Legendre symbol result"); return yes ? 1 : zero ? 0 : -1; } function nLength22(n, nBitLength) { if (nBitLength !== void 0) anumber3(nBitLength); const _nBitLength = nBitLength !== void 0 ? nBitLength : n.toString(2).length; const nByteLength = Math.ceil(_nBitLength / 8); return { nBitLength: _nBitLength, nByteLength }; } function Field22(ORDER, bitLenOrOpts, isLE4 = false, opts = {}) { if (ORDER <= _0n7) throw new Error("invalid field: expected ORDER > 0, got " + ORDER); let _nbitLength = void 0; let _sqrt = void 0; let modFromBytes = false; let allowedLengths = void 0; if (typeof bitLenOrOpts === "object" && bitLenOrOpts != null) { if (opts.sqrt || isLE4) throw new Error("cannot specify opts in two arguments"); const _opts = bitLenOrOpts; if (_opts.BITS) _nbitLength = _opts.BITS; if (_opts.sqrt) _sqrt = _opts.sqrt; if (typeof _opts.isLE === "boolean") isLE4 = _opts.isLE; if (typeof _opts.modFromBytes === "boolean") modFromBytes = _opts.modFromBytes; allowedLengths = _opts.allowedLengths; } else { if (typeof bitLenOrOpts === "number") _nbitLength = bitLenOrOpts; if (opts.sqrt) _sqrt = opts.sqrt; } const { nBitLength: BITS, nByteLength: BYTES } = nLength22(ORDER, _nbitLength); if (BYTES > 2048) throw new Error("invalid field: expected ORDER of <= 2048 bytes"); let sqrtP; const f = Object.freeze({ ORDER, isLE: isLE4, BITS, BYTES, MASK: bitMask22(BITS), ZERO: _0n7, ONE: _1n7, allowedLengths, create: (num3) => mod22(num3, ORDER), isValid: (num3) => { if (typeof num3 !== "bigint") throw new Error("invalid field element: expected bigint, got " + typeof num3); return _0n7 <= num3 && num3 < ORDER; }, is0: (num3) => num3 === _0n7, isValidNot0: (num3) => !f.is0(num3) && f.isValid(num3), isOdd: (num3) => (num3 & _1n7) === _1n7, neg: (num3) => mod22(-num3, ORDER), eql: (lhs, rhs) => lhs === rhs, sqr: (num3) => mod22(num3 * num3, ORDER), add: (lhs, rhs) => mod22(lhs + rhs, ORDER), sub: (lhs, rhs) => mod22(lhs - rhs, ORDER), mul: (lhs, rhs) => mod22(lhs * rhs, ORDER), pow: (num3, power) => FpPow22(f, num3, power), div: (lhs, rhs) => mod22(lhs * invert22(rhs, ORDER), ORDER), sqrN: (num3) => num3 * num3, addN: (lhs, rhs) => lhs + rhs, subN: (lhs, rhs) => lhs - rhs, mulN: (lhs, rhs) => lhs * rhs, inv: (num3) => invert22(num3, ORDER), sqrt: _sqrt || ((n) => { if (!sqrtP) sqrtP = FpSqrt22(ORDER); return sqrtP(f, n); }), toBytes: (num3) => isLE4 ? numberToBytesLE22(num3, BYTES) : numberToBytesBE22(num3, BYTES), fromBytes: (bytes4, skipValidation = true) => { if (allowedLengths) { if (!allowedLengths.includes(bytes4.length) || bytes4.length > BYTES) { throw new Error("Field.fromBytes: expected " + allowedLengths + " bytes, got " + bytes4.length); } const padded = new Uint8Array(BYTES); padded.set(bytes4, isLE4 ? 0 : padded.length - bytes4.length); bytes4 = padded; } if (bytes4.length !== BYTES) throw new Error("Field.fromBytes: expected " + BYTES + " bytes, got " + bytes4.length); let scalar = isLE4 ? bytesToNumberLE22(bytes4) : bytesToNumberBE22(bytes4); if (modFromBytes) scalar = mod22(scalar, ORDER); if (!skipValidation) { if (!f.isValid(scalar)) throw new Error("invalid field element: outside of range 0..ORDER"); } return scalar; }, invertBatch: (lst) => FpInvertBatch22(f, lst), cmov: (a, b, c) => c ? b : a }); return Object.freeze(f); } function getFieldBytesLength22(fieldOrder) { if (typeof fieldOrder !== "bigint") throw new Error("field order must be bigint"); const bitLength = fieldOrder.toString(2).length; return Math.ceil(bitLength / 8); } function getMinHashLength22(fieldOrder) { const length = getFieldBytesLength22(fieldOrder); return length + Math.ceil(length / 2); } function mapHashToField22(key, fieldOrder, isLE4 = false) { const len = key.length; const fieldLen = getFieldBytesLength22(fieldOrder); const minLen = getMinHashLength22(fieldOrder); if (len < 16 || len < minLen || len > 1024) throw new Error("expected " + minLen + "-1024 bytes of input, got " + len); const num3 = isLE4 ? bytesToNumberLE22(key) : bytesToNumberBE22(key); const reduced = mod22(num3, fieldOrder - _1n7) + _1n7; return isLE4 ? numberToBytesLE22(reduced, fieldLen) : numberToBytesBE22(reduced, fieldLen); } var _0n8 = BigInt(0); var _1n8 = BigInt(1); function negateCt2(condition, item) { const neg = item.negate(); return condition ? neg : item; } function normalizeZ2(c, points) { const invertedZs = FpInvertBatch22(c.Fp, points.map((p5) => p5.Z)); return points.map((p5, i22) => c.fromAffine(p5.toAffine(invertedZs[i22]))); } function validateW2(W3, bits) { if (!Number.isSafeInteger(W3) || W3 <= 0 || W3 > bits) throw new Error("invalid window size, expected [1.." + bits + "], got W=" + W3); } function calcWOpts2(W3, scalarBits) { validateW2(W3, scalarBits); const windows = Math.ceil(scalarBits / W3) + 1; const windowSize = 2 ** (W3 - 1); const maxNumber = 2 ** W3; const mask = bitMask22(W3); const shiftBy = BigInt(W3); return { windows, windowSize, mask, maxNumber, shiftBy }; } function calcOffsets2(n, window2, wOpts) { const { windowSize, mask, maxNumber, shiftBy } = wOpts; let wbits = Number(n & mask); let nextN = n >> shiftBy; if (wbits > windowSize) { wbits -= maxNumber; nextN += _1n8; } const offsetStart = window2 * windowSize; const offset = offsetStart + Math.abs(wbits) - 1; const isZero = wbits === 0; const isNeg = wbits < 0; const isNegF = window2 % 2 !== 0; const offsetF = offsetStart; return { nextN, offset, isZero, isNeg, isNegF, offsetF }; } function validateMSMPoints2(points, c) { if (!Array.isArray(points)) throw new Error("array expected"); points.forEach((p5, i22) => { if (!(p5 instanceof c)) throw new Error("invalid point at index " + i22); }); } function validateMSMScalars2(scalars, field) { if (!Array.isArray(scalars)) throw new Error("array of scalars expected"); scalars.forEach((s, i22) => { if (!field.isValid(s)) throw new Error("invalid scalar at index " + i22); }); } var pointPrecomputes2 = /* @__PURE__ */ new WeakMap(); var pointWindowSizes2 = /* @__PURE__ */ new WeakMap(); function getW2(P) { return pointWindowSizes2.get(P) || 1; } function assert02(n) { if (n !== _0n8) throw new Error("invalid wNAF"); } var wNAF22 = class { constructor(Point22, bits) { this.BASE = Point22.BASE; this.ZERO = Point22.ZERO; this.Fn = Point22.Fn; this.bits = bits; } _unsafeLadder(elm, n, p5 = this.ZERO) { let d17 = elm; while (n > _0n8) { if (n & _1n8) p5 = p5.add(d17); d17 = d17.double(); n >>= _1n8; } return p5; } precomputeWindow(point, W3) { const { windows, windowSize } = calcWOpts2(W3, this.bits); const points = []; let p5 = point; let base = p5; for (let window2 = 0; window2 < windows; window2++) { base = p5; points.push(base); for (let i22 = 1; i22 < windowSize; i22++) { base = base.add(p5); points.push(base); } p5 = base.double(); } return points; } wNAF(W3, precomputes, n) { if (!this.Fn.isValid(n)) throw new Error("invalid scalar"); let p5 = this.ZERO; let f = this.BASE; const wo = calcWOpts2(W3, this.bits); for (let window2 = 0; window2 < wo.windows; window2++) { const { nextN, offset, isZero, isNeg, isNegF, offsetF } = calcOffsets2(n, window2, wo); n = nextN; if (isZero) { f = f.add(negateCt2(isNegF, precomputes[offsetF])); } else { p5 = p5.add(negateCt2(isNeg, precomputes[offset])); } } assert02(n); return { p: p5, f }; } wNAFUnsafe(W3, precomputes, n, acc = this.ZERO) { const wo = calcWOpts2(W3, this.bits); for (let window2 = 0; window2 < wo.windows; window2++) { if (n === _0n8) break; const { nextN, offset, isZero, isNeg } = calcOffsets2(n, window2, wo); n = nextN; if (isZero) { continue; } else { const item = precomputes[offset]; acc = acc.add(isNeg ? item.negate() : item); } } assert02(n); return acc; } getPrecomputes(W3, point, transform) { let comp = pointPrecomputes2.get(point); if (!comp) { comp = this.precomputeWindow(point, W3); if (W3 !== 1) { if (typeof transform === "function") comp = transform(comp); pointPrecomputes2.set(point, comp); } } return comp; } cached(point, scalar, transform) { const W3 = getW2(point); return this.wNAF(W3, this.getPrecomputes(W3, point, transform), scalar); } unsafe(point, scalar, transform, prev) { const W3 = getW2(point); if (W3 === 1) return this._unsafeLadder(point, scalar, prev); return this.wNAFUnsafe(W3, this.getPrecomputes(W3, point, transform), scalar, prev); } createCache(P, W3) { validateW2(W3, this.bits); pointWindowSizes2.set(P, W3); pointPrecomputes2.delete(P); } hasCache(elm) { return getW2(elm) !== 1; } }; function mulEndoUnsafe2(Point22, point, k1, k2) { let acc = point; let p1 = Point22.ZERO; let p22 = Point22.ZERO; while (k1 > _0n8 || k2 > _0n8) { if (k1 & _1n8) p1 = p1.add(acc); if (k2 & _1n8) p22 = p22.add(acc); acc = acc.double(); k1 >>= _1n8; k2 >>= _1n8; } return { p1, p2: p22 }; } function pippenger2(c, fieldN, points, scalars) { validateMSMPoints2(points, c); validateMSMScalars2(scalars, fieldN); const plength = points.length; const slength = scalars.length; if (plength !== slength) throw new Error("arrays of points and scalars must have equal length"); const zero = c.ZERO; const wbits = bitLen22(BigInt(plength)); let windowSize = 1; if (wbits > 12) windowSize = wbits - 3; else if (wbits > 4) windowSize = wbits - 2; else if (wbits > 0) windowSize = 2; const MASK = bitMask22(windowSize); const buckets = new Array(Number(MASK) + 1).fill(zero); const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize; let sum = zero; for (let i22 = lastBits; i22 >= 0; i22 -= windowSize) { buckets.fill(zero); for (let j2 = 0; j2 < slength; j2++) { const scalar = scalars[j2]; const wbits2 = Number(scalar >> BigInt(i22) & MASK); buckets[wbits2] = buckets[wbits2].add(points[j2]); } let resI = zero; for (let j2 = buckets.length - 1, sumI = zero; j2 > 0; j2--) { sumI = sumI.add(buckets[j2]); resI = resI.add(sumI); } sum = sum.add(resI); if (i22 !== 0) for (let j2 = 0; j2 < windowSize; j2++) sum = sum.double(); } return sum; } function createField2(order, field, isLE4) { if (field) { if (field.ORDER !== order) throw new Error("Field.ORDER must match order: Fp == p, Fn == n"); validateField22(field); return field; } else { return Field22(order, { isLE: isLE4 }); } } function _createCurveFields2(type, CURVE, curveOpts = {}, FpFnLE) { if (FpFnLE === void 0) FpFnLE = type === "edwards"; if (!CURVE || typeof CURVE !== "object") throw new Error(`expected valid ${type} CURVE object`); for (const p5 of ["p", "n", "h"]) { const val = CURVE[p5]; if (!(typeof val === "bigint" && val > _0n8)) throw new Error(`CURVE.${p5} must be positive bigint`); } const Fp2 = createField2(CURVE.p, curveOpts.Fp, FpFnLE); const Fn = createField2(CURVE.n, curveOpts.Fn, FpFnLE); const _b = type === "weierstrass" ? "b" : "d"; const params = ["Gx", "Gy", "a", _b]; for (const p5 of params) { if (!Fp2.isValid(CURVE[p5])) throw new Error(`CURVE.${p5} must be valid field element of CURVE.Fp`); } CURVE = Object.freeze(Object.assign({}, CURVE)); return { CURVE, Fp: Fp2, Fn }; } var divNearest22 = (num3, den) => (num3 + (num3 >= 0 ? den : -den) / _2n6) / den; function _splitEndoScalar2(k2, basis, n) { const [[a1, b1], [a2, b2]] = basis; const c1 = divNearest22(b2 * k2, n); const c2 = divNearest22(-b1 * k2, n); let k1 = k2 - c1 * a1 - c2 * a2; let k22 = -c1 * b1 - c2 * b2; const k1neg = k1 < _0n9; const k2neg = k22 < _0n9; if (k1neg) k1 = -k1; if (k2neg) k22 = -k22; const MAX_NUM = bitMask22(Math.ceil(bitLen22(n) / 2)) + _1n9; if (k1 < _0n9 || k1 >= MAX_NUM || k22 < _0n9 || k22 >= MAX_NUM) { throw new Error("splitScalar (endomorphism): failed, k=" + k2); } return { k1neg, k1, k2neg, k2: k22 }; } function validateSigFormat2(format) { if (!["compact", "recovered", "der"].includes(format)) throw new Error('Signature format must be "compact", "recovered", or "der"'); return format; } function validateSigOpts2(opts, def) { const optsn = {}; for (let optName of Object.keys(def)) { optsn[optName] = opts[optName] === void 0 ? def[optName] : opts[optName]; } _abool22(optsn.lowS, "lowS"); _abool22(optsn.prehash, "prehash"); if (optsn.format !== void 0) validateSigFormat2(optsn.format); return optsn; } var DERErr22 = class extends Error { constructor(m = "") { super(m); } }; var DER22 = { Err: DERErr22, _tlv: { encode: (tag, data) => { const { Err: E2 } = DER22; if (tag < 0 || tag > 256) throw new E2("tlv.encode: wrong tag"); if (data.length & 1) throw new E2("tlv.encode: unpadded data"); const dataLen = data.length / 2; const len = numberToHexUnpadded22(dataLen); if (len.length / 2 & 128) throw new E2("tlv.encode: long form length too big"); const lenLen = dataLen > 127 ? numberToHexUnpadded22(len.length / 2 | 128) : ""; const t = numberToHexUnpadded22(tag); return t + lenLen + len + data; }, decode(tag, data) { const { Err: E2 } = DER22; let pos = 0; if (tag < 0 || tag > 256) throw new E2("tlv.encode: wrong tag"); if (data.length < 2 || data[pos++] !== tag) throw new E2("tlv.decode: wrong tlv"); const first = data[pos++]; const isLong = !!(first & 128); let length = 0; if (!isLong) length = first; else { const lenLen = first & 127; if (!lenLen) throw new E2("tlv.decode(long): indefinite length not supported"); if (lenLen > 4) throw new E2("tlv.decode(long): byte length is too big"); const lengthBytes = data.subarray(pos, pos + lenLen); if (lengthBytes.length !== lenLen) throw new E2("tlv.decode: length bytes not complete"); if (lengthBytes[0] === 0) throw new E2("tlv.decode(long): zero leftmost byte"); for (const b of lengthBytes) length = length << 8 | b; pos += lenLen; if (length < 128) throw new E2("tlv.decode(long): not minimal encoding"); } const v6 = data.subarray(pos, pos + length); if (v6.length !== length) throw new E2("tlv.decode: wrong value length"); return { v: v6, l: data.subarray(pos + length) }; } }, _int: { encode(num3) { const { Err: E2 } = DER22; if (num3 < _0n9) throw new E2("integer: negative integers are not allowed"); let hex2 = numberToHexUnpadded22(num3); if (Number.parseInt(hex2[0], 16) & 8) hex2 = "00" + hex2; if (hex2.length & 1) throw new E2("unexpected DER parsing assertion: unpadded hex"); return hex2; }, decode(data) { const { Err: E2 } = DER22; if (data[0] & 128) throw new E2("invalid signature integer: negative"); if (data[0] === 0 && !(data[1] & 128)) throw new E2("invalid signature integer: unnecessary leading zero"); return bytesToNumberBE22(data); } }, toSig(hex2) { const { Err: E2, _int: int, _tlv: tlv } = DER22; const data = ensureBytes22("signature", hex2); const { v: seqBytes, l: seqLeftBytes } = tlv.decode(48, data); if (seqLeftBytes.length) throw new E2("invalid signature: left bytes after parsing"); const { v: rBytes, l: rLeftBytes } = tlv.decode(2, seqBytes); const { v: sBytes, l: sLeftBytes } = tlv.decode(2, rLeftBytes); if (sLeftBytes.length) throw new E2("invalid signature: left bytes after parsing"); return { r: int.decode(rBytes), s: int.decode(sBytes) }; }, hexFromSig(sig) { const { _tlv: tlv, _int: int } = DER22; const rs = tlv.encode(2, int.encode(sig.r)); const ss = tlv.encode(2, int.encode(sig.s)); const seq = rs + ss; return tlv.encode(48, seq); } }; var _0n9 = BigInt(0); var _1n9 = BigInt(1); var _2n6 = BigInt(2); var _3n4 = BigInt(3); var _4n4 = BigInt(4); function _normFnElement2(Fn, key) { const { BYTES: expected } = Fn; let num3; if (typeof key === "bigint") { num3 = key; } else { let bytes4 = ensureBytes22("private key", key); try { num3 = Fn.fromBytes(bytes4); } catch (error) { throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`); } } if (!Fn.isValidNot0(num3)) throw new Error("invalid private key: out of range [1..N-1]"); return num3; } function weierstrassN2(params, extraOpts = {}) { const validated = _createCurveFields2("weierstrass", params, extraOpts); const { Fp: Fp2, Fn } = validated; let CURVE = validated.CURVE; const { h: cofactor, n: CURVE_ORDER } = CURVE; _validateObject2(extraOpts, {}, { allowInfinityPoint: "boolean", clearCofactor: "function", isTorsionFree: "function", fromBytes: "function", toBytes: "function", endo: "object", wrapPrivateKey: "boolean" }); const { endo } = extraOpts; if (endo) { if (!Fp2.is0(CURVE.a) || typeof endo.beta !== "bigint" || !Array.isArray(endo.basises)) { throw new Error('invalid endo: expected "beta": bigint and "basises": array'); } } const lengths = getWLengths2(Fp2, Fn); function assertCompressionIsSupported() { if (!Fp2.isOdd) throw new Error("compression is not supported: Field does not have .isOdd()"); } function pointToBytes23(_c, point, isCompressed) { const { x: x2, y: y2 } = point.toAffine(); const bx = Fp2.toBytes(x2); _abool22(isCompressed, "isCompressed"); if (isCompressed) { assertCompressionIsSupported(); const hasEvenY = !Fp2.isOdd(y2); return concatBytes4(pprefix2(hasEvenY), bx); } else { return concatBytes4(Uint8Array.of(4), bx, Fp2.toBytes(y2)); } } function pointFromBytes(bytes4) { _abytes22(bytes4, void 0, "Point"); const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; const length = bytes4.length; const head = bytes4[0]; const tail = bytes4.subarray(1); if (length === comp && (head === 2 || head === 3)) { const x2 = Fp2.fromBytes(tail); if (!Fp2.isValid(x2)) throw new Error("bad point: is not on curve, wrong x"); const y2 = weierstrassEquation(x2); let y3; try { y3 = Fp2.sqrt(y2); } catch (sqrtError) { const err = sqrtError instanceof Error ? ": " + sqrtError.message : ""; throw new Error("bad point: is not on curve, sqrt error" + err); } assertCompressionIsSupported(); const isYOdd = Fp2.isOdd(y3); const isHeadOdd = (head & 1) === 1; if (isHeadOdd !== isYOdd) y3 = Fp2.neg(y3); return { x: x2, y: y3 }; } else if (length === uncomp && head === 4) { const L = Fp2.BYTES; const x2 = Fp2.fromBytes(tail.subarray(0, L)); const y2 = Fp2.fromBytes(tail.subarray(L, L * 2)); if (!isValidXY(x2, y2)) throw new Error("bad point: is not on curve"); return { x: x2, y: y2 }; } else { throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`); } } const encodePoint = extraOpts.toBytes || pointToBytes23; const decodePoint = extraOpts.fromBytes || pointFromBytes; function weierstrassEquation(x2) { const x22 = Fp2.sqr(x2); const x3 = Fp2.mul(x22, x2); return Fp2.add(Fp2.add(x3, Fp2.mul(x2, CURVE.a)), CURVE.b); } function isValidXY(x2, y2) { const left = Fp2.sqr(y2); const right = weierstrassEquation(x2); return Fp2.eql(left, right); } if (!isValidXY(CURVE.Gx, CURVE.Gy)) throw new Error("bad curve params: generator point"); const _4a3 = Fp2.mul(Fp2.pow(CURVE.a, _3n4), _4n4); const _27b2 = Fp2.mul(Fp2.sqr(CURVE.b), BigInt(27)); if (Fp2.is0(Fp2.add(_4a3, _27b2))) throw new Error("bad curve params: a or b"); function acoord(title, n, banZero = false) { if (!Fp2.isValid(n) || banZero && Fp2.is0(n)) throw new Error(`bad point coordinate ${title}`); return n; } function aprjpoint(other) { if (!(other instanceof Point22)) throw new Error("ProjectivePoint expected"); } function splitEndoScalarN(k2) { if (!endo || !endo.basises) throw new Error("no endo"); return _splitEndoScalar2(k2, endo.basises, Fn.ORDER); } const toAffineMemo = memoized2((p5, iz) => { const { X: X2, Y, Z } = p5; if (Fp2.eql(Z, Fp2.ONE)) return { x: X2, y: Y }; const is0 = p5.is0(); if (iz == null) iz = is0 ? Fp2.ONE : Fp2.inv(Z); const x2 = Fp2.mul(X2, iz); const y2 = Fp2.mul(Y, iz); const zz = Fp2.mul(Z, iz); if (is0) return { x: Fp2.ZERO, y: Fp2.ZERO }; if (!Fp2.eql(zz, Fp2.ONE)) throw new Error("invZ was invalid"); return { x: x2, y: y2 }; }); const assertValidMemo = memoized2((p5) => { if (p5.is0()) { if (extraOpts.allowInfinityPoint && !Fp2.is0(p5.Y)) return; throw new Error("bad point: ZERO"); } const { x: x2, y: y2 } = p5.toAffine(); if (!Fp2.isValid(x2) || !Fp2.isValid(y2)) throw new Error("bad point: x or y not field elements"); if (!isValidXY(x2, y2)) throw new Error("bad point: equation left != right"); if (!p5.isTorsionFree()) throw new Error("bad point: not in prime-order subgroup"); return true; }); function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) { k2p = new Point22(Fp2.mul(k2p.X, endoBeta), k2p.Y, k2p.Z); k1p = negateCt2(k1neg, k1p); k2p = negateCt2(k2neg, k2p); return k1p.add(k2p); } class Point22 { constructor(X2, Y, Z) { this.X = acoord("x", X2); this.Y = acoord("y", Y, true); this.Z = acoord("z", Z); Object.freeze(this); } static CURVE() { return CURVE; } static fromAffine(p5) { const { x: x2, y: y2 } = p5 || {}; if (!p5 || !Fp2.isValid(x2) || !Fp2.isValid(y2)) throw new Error("invalid affine point"); if (p5 instanceof Point22) throw new Error("projective point not allowed"); if (Fp2.is0(x2) && Fp2.is0(y2)) return Point22.ZERO; return new Point22(x2, y2, Fp2.ONE); } static fromBytes(bytes4) { const P = Point22.fromAffine(decodePoint(_abytes22(bytes4, void 0, "point"))); P.assertValidity(); return P; } static fromHex(hex2) { return Point22.fromBytes(ensureBytes22("pointHex", hex2)); } get x() { return this.toAffine().x; } get y() { return this.toAffine().y; } precompute(windowSize = 8, isLazy = true) { wnaf.createCache(this, windowSize); if (!isLazy) this.multiply(_3n4); return this; } assertValidity() { assertValidMemo(this); } hasEvenY() { const { y: y2 } = this.toAffine(); if (!Fp2.isOdd) throw new Error("Field doesn't support isOdd"); return !Fp2.isOdd(y2); } equals(other) { aprjpoint(other); const { X: X1, Y: Y1, Z: Z1 } = this; const { X: X2, Y: Y2, Z: Z2 } = other; const U1 = Fp2.eql(Fp2.mul(X1, Z2), Fp2.mul(X2, Z1)); const U2 = Fp2.eql(Fp2.mul(Y1, Z2), Fp2.mul(Y2, Z1)); return U1 && U2; } negate() { return new Point22(this.X, Fp2.neg(this.Y), this.Z); } double() { const { a, b } = CURVE; const b3 = Fp2.mul(b, _3n4); const { X: X1, Y: Y1, Z: Z1 } = this; let { ZERO: X3, ZERO: Y3, ZERO: Z3 } = Fp2; let t0 = Fp2.mul(X1, X1); let t1 = Fp2.mul(Y1, Y1); let t2 = Fp2.mul(Z1, Z1); let t3 = Fp2.mul(X1, Y1); t3 = Fp2.add(t3, t3); Z3 = Fp2.mul(X1, Z1); Z3 = Fp2.add(Z3, Z3); X3 = Fp2.mul(a, Z3); Y3 = Fp2.mul(b3, t2); Y3 = Fp2.add(X3, Y3); X3 = Fp2.sub(t1, Y3); Y3 = Fp2.add(t1, Y3); Y3 = Fp2.mul(X3, Y3); X3 = Fp2.mul(t3, X3); Z3 = Fp2.mul(b3, Z3); t2 = Fp2.mul(a, t2); t3 = Fp2.sub(t0, t2); t3 = Fp2.mul(a, t3); t3 = Fp2.add(t3, Z3); Z3 = Fp2.add(t0, t0); t0 = Fp2.add(Z3, t0); t0 = Fp2.add(t0, t2); t0 = Fp2.mul(t0, t3); Y3 = Fp2.add(Y3, t0); t2 = Fp2.mul(Y1, Z1); t2 = Fp2.add(t2, t2); t0 = Fp2.mul(t2, t3); X3 = Fp2.sub(X3, t0); Z3 = Fp2.mul(t2, t1); Z3 = Fp2.add(Z3, Z3); Z3 = Fp2.add(Z3, Z3); return new Point22(X3, Y3, Z3); } add(other) { aprjpoint(other); const { X: X1, Y: Y1, Z: Z1 } = this; const { X: X2, Y: Y2, Z: Z2 } = other; let { ZERO: X3, ZERO: Y3, ZERO: Z3 } = Fp2; const a = CURVE.a; const b3 = Fp2.mul(CURVE.b, _3n4); let t0 = Fp2.mul(X1, X2); let t1 = Fp2.mul(Y1, Y2); let t2 = Fp2.mul(Z1, Z2); let t3 = Fp2.add(X1, Y1); let t4 = Fp2.add(X2, Y2); t3 = Fp2.mul(t3, t4); t4 = Fp2.add(t0, t1); t3 = Fp2.sub(t3, t4); t4 = Fp2.add(X1, Z1); let t5 = Fp2.add(X2, Z2); t4 = Fp2.mul(t4, t5); t5 = Fp2.add(t0, t2); t4 = Fp2.sub(t4, t5); t5 = Fp2.add(Y1, Z1); X3 = Fp2.add(Y2, Z2); t5 = Fp2.mul(t5, X3); X3 = Fp2.add(t1, t2); t5 = Fp2.sub(t5, X3); Z3 = Fp2.mul(a, t4); X3 = Fp2.mul(b3, t2); Z3 = Fp2.add(X3, Z3); X3 = Fp2.sub(t1, Z3); Z3 = Fp2.add(t1, Z3); Y3 = Fp2.mul(X3, Z3); t1 = Fp2.add(t0, t0); t1 = Fp2.add(t1, t0); t2 = Fp2.mul(a, t2); t4 = Fp2.mul(b3, t4); t1 = Fp2.add(t1, t2); t2 = Fp2.sub(t0, t2); t2 = Fp2.mul(a, t2); t4 = Fp2.add(t4, t2); t0 = Fp2.mul(t1, t4); Y3 = Fp2.add(Y3, t0); t0 = Fp2.mul(t5, t4); X3 = Fp2.mul(t3, X3); X3 = Fp2.sub(X3, t0); t0 = Fp2.mul(t3, t1); Z3 = Fp2.mul(t5, Z3); Z3 = Fp2.add(Z3, t0); return new Point22(X3, Y3, Z3); } subtract(other) { return this.add(other.negate()); } is0() { return this.equals(Point22.ZERO); } multiply(scalar) { const { endo: endo2 } = extraOpts; if (!Fn.isValidNot0(scalar)) throw new Error("invalid scalar: out of range"); let point, fake; const mul3 = (n) => wnaf.cached(this, n, (p5) => normalizeZ2(Point22, p5)); if (endo2) { const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar); const { p: k1p, f: k1f } = mul3(k1); const { p: k2p, f: k2f } = mul3(k2); fake = k1f.add(k2f); point = finishEndo(endo2.beta, k1p, k2p, k1neg, k2neg); } else { const { p: p5, f } = mul3(scalar); point = p5; fake = f; } return normalizeZ2(Point22, [point, fake])[0]; } multiplyUnsafe(sc) { const { endo: endo2 } = extraOpts; const p5 = this; if (!Fn.isValid(sc)) throw new Error("invalid scalar: out of range"); if (sc === _0n9 || p5.is0()) return Point22.ZERO; if (sc === _1n9) return p5; if (wnaf.hasCache(this)) return this.multiply(sc); if (endo2) { const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc); const { p1, p2: p22 } = mulEndoUnsafe2(Point22, p5, k1, k2); return finishEndo(endo2.beta, p1, p22, k1neg, k2neg); } else { return wnaf.unsafe(p5, sc); } } multiplyAndAddUnsafe(Q2, a, b) { const sum = this.multiplyUnsafe(a).add(Q2.multiplyUnsafe(b)); return sum.is0() ? void 0 : sum; } toAffine(invertedZ) { return toAffineMemo(this, invertedZ); } isTorsionFree() { const { isTorsionFree } = extraOpts; if (cofactor === _1n9) return true; if (isTorsionFree) return isTorsionFree(Point22, this); return wnaf.unsafe(this, CURVE_ORDER).is0(); } clearCofactor() { const { clearCofactor } = extraOpts; if (cofactor === _1n9) return this; if (clearCofactor) return clearCofactor(Point22, this); return this.multiplyUnsafe(cofactor); } isSmallOrder() { return this.multiplyUnsafe(cofactor).is0(); } toBytes(isCompressed = true) { _abool22(isCompressed, "isCompressed"); this.assertValidity(); return encodePoint(Point22, this, isCompressed); } toHex(isCompressed = true) { return bytesToHex4(this.toBytes(isCompressed)); } toString() { return ``; } get px() { return this.X; } get py() { return this.X; } get pz() { return this.Z; } toRawBytes(isCompressed = true) { return this.toBytes(isCompressed); } _setWindowSize(windowSize) { this.precompute(windowSize); } static normalizeZ(points) { return normalizeZ2(Point22, points); } static msm(points, scalars) { return pippenger2(Point22, Fn, points, scalars); } static fromPrivateKey(privateKey) { return Point22.BASE.multiply(_normFnElement2(Fn, privateKey)); } } Point22.BASE = new Point22(CURVE.Gx, CURVE.Gy, Fp2.ONE); Point22.ZERO = new Point22(Fp2.ZERO, Fp2.ONE, Fp2.ZERO); Point22.Fp = Fp2; Point22.Fn = Fn; const bits = Fn.BITS; const wnaf = new wNAF22(Point22, extraOpts.endo ? Math.ceil(bits / 2) : bits); Point22.BASE.precompute(8); return Point22; } function pprefix2(hasEvenY) { return Uint8Array.of(hasEvenY ? 2 : 3); } function getWLengths2(Fp2, Fn) { return { secretKey: Fn.BYTES, publicKey: 1 + Fp2.BYTES, publicKeyUncompressed: 1 + 2 * Fp2.BYTES, publicKeyHasPrefix: true, signature: 2 * Fn.BYTES }; } function ecdh2(Point22, ecdhOpts = {}) { const { Fn } = Point22; const randomBytes_ = ecdhOpts.randomBytes || randomBytes3; const lengths = Object.assign(getWLengths2(Point22.Fp, Fn), { seed: getMinHashLength22(Fn.ORDER) }); function isValidSecretKey(secretKey) { try { return !!_normFnElement2(Fn, secretKey); } catch (error) { return false; } } function isValidPublicKey(publicKey, isCompressed) { const { publicKey: comp, publicKeyUncompressed } = lengths; try { const l3 = publicKey.length; if (isCompressed === true && l3 !== comp) return false; if (isCompressed === false && l3 !== publicKeyUncompressed) return false; return !!Point22.fromBytes(publicKey); } catch (error) { return false; } } function randomSecretKey(seed = randomBytes_(lengths.seed)) { return mapHashToField22(_abytes22(seed, lengths.seed, "seed"), Fn.ORDER); } function getPublicKey22(secretKey, isCompressed = true) { return Point22.BASE.multiply(_normFnElement2(Fn, secretKey)).toBytes(isCompressed); } function keygen(seed) { const secretKey = randomSecretKey(seed); return { secretKey, publicKey: getPublicKey22(secretKey) }; } function isProbPub(item) { if (typeof item === "bigint") return false; if (item instanceof Point22) return true; const { secretKey, publicKey, publicKeyUncompressed } = lengths; if (Fn.allowedLengths || secretKey === publicKey) return; const l3 = ensureBytes22("key", item).length; return l3 === publicKey || l3 === publicKeyUncompressed; } function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) { if (isProbPub(secretKeyA) === true) throw new Error("first arg must be private key"); if (isProbPub(publicKeyB) === false) throw new Error("second arg must be public key"); const s = _normFnElement2(Fn, secretKeyA); const b = Point22.fromHex(publicKeyB); return b.multiply(s).toBytes(isCompressed); } const utils = { isValidSecretKey, isValidPublicKey, randomSecretKey, isValidPrivateKey: isValidSecretKey, randomPrivateKey: randomSecretKey, normPrivateKeyToScalar: (key) => _normFnElement2(Fn, key), precompute(windowSize = 8, point = Point22.BASE) { return point.precompute(windowSize, false); } }; return Object.freeze({ getPublicKey: getPublicKey22, getSharedSecret, keygen, Point: Point22, utils, lengths }); } function ecdsa2(Point22, hash3, ecdsaOpts = {}) { ahash2(hash3); _validateObject2(ecdsaOpts, {}, { hmac: "function", lowS: "boolean", randomBytes: "function", bits2int: "function", bits2int_modN: "function" }); const randomBytes4 = ecdsaOpts.randomBytes || randomBytes3; const hmac4 = ecdsaOpts.hmac || ((key, ...msgs) => hmac3(hash3, key, concatBytes4(...msgs))); const { Fp: Fp2, Fn } = Point22; const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn; const { keygen, getPublicKey: getPublicKey22, getSharedSecret, utils, lengths } = ecdh2(Point22, ecdsaOpts); const defaultSigOpts = { prehash: false, lowS: typeof ecdsaOpts.lowS === "boolean" ? ecdsaOpts.lowS : false, format: void 0, extraEntropy: false }; const defaultSigOpts_format = "compact"; function isBiggerThanHalfOrder(number4) { const HALF = CURVE_ORDER >> _1n9; return number4 > HALF; } function validateRS(title, num3) { if (!Fn.isValidNot0(num3)) throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`); return num3; } function validateSigLength(bytes4, format) { validateSigFormat2(format); const size = lengths.signature; const sizer = format === "compact" ? size : format === "recovered" ? size + 1 : void 0; return _abytes22(bytes4, sizer, `${format} signature`); } class Signature { constructor(r, s, recovery) { this.r = validateRS("r", r); this.s = validateRS("s", s); if (recovery != null) this.recovery = recovery; Object.freeze(this); } static fromBytes(bytes4, format = defaultSigOpts_format) { validateSigLength(bytes4, format); let recid; if (format === "der") { const { r: r2, s: s2 } = DER22.toSig(_abytes22(bytes4)); return new Signature(r2, s2); } if (format === "recovered") { recid = bytes4[0]; format = "compact"; bytes4 = bytes4.subarray(1); } const L = Fn.BYTES; const r = bytes4.subarray(0, L); const s = bytes4.subarray(L, L * 2); return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid); } static fromHex(hex2, format) { return this.fromBytes(hexToBytes4(hex2), format); } addRecoveryBit(recovery) { return new Signature(this.r, this.s, recovery); } recoverPublicKey(messageHash) { const FIELD_ORDER = Fp2.ORDER; const { r, s, recovery: rec } = this; if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error("recovery id invalid"); const hasCofactor = CURVE_ORDER * _2n6 < FIELD_ORDER; if (hasCofactor && rec > 1) throw new Error("recovery id is ambiguous for h>1 curve"); const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r; if (!Fp2.isValid(radj)) throw new Error("recovery id 2 or 3 invalid"); const x2 = Fp2.toBytes(radj); const R2 = Point22.fromBytes(concatBytes4(pprefix2((rec & 1) === 0), x2)); const ir = Fn.inv(radj); const h2 = bits2int_modN(ensureBytes22("msgHash", messageHash)); const u1 = Fn.create(-h2 * ir); const u22 = Fn.create(s * ir); const Q2 = Point22.BASE.multiplyUnsafe(u1).add(R2.multiplyUnsafe(u22)); if (Q2.is0()) throw new Error("point at infinify"); Q2.assertValidity(); return Q2; } hasHighS() { return isBiggerThanHalfOrder(this.s); } toBytes(format = defaultSigOpts_format) { validateSigFormat2(format); if (format === "der") return hexToBytes4(DER22.hexFromSig(this)); const r = Fn.toBytes(this.r); const s = Fn.toBytes(this.s); if (format === "recovered") { if (this.recovery == null) throw new Error("recovery bit must be present"); return concatBytes4(Uint8Array.of(this.recovery), r, s); } return concatBytes4(r, s); } toHex(format) { return bytesToHex4(this.toBytes(format)); } assertValidity() { } static fromCompact(hex2) { return Signature.fromBytes(ensureBytes22("sig", hex2), "compact"); } static fromDER(hex2) { return Signature.fromBytes(ensureBytes22("sig", hex2), "der"); } normalizeS() { return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this; } toDERRawBytes() { return this.toBytes("der"); } toDERHex() { return bytesToHex4(this.toBytes("der")); } toCompactRawBytes() { return this.toBytes("compact"); } toCompactHex() { return bytesToHex4(this.toBytes("compact")); } } const bits2int = ecdsaOpts.bits2int || function bits2int_def(bytes4) { if (bytes4.length > 8192) throw new Error("input is too large"); const num3 = bytesToNumberBE22(bytes4); const delta = bytes4.length * 8 - fnBits; return delta > 0 ? num3 >> BigInt(delta) : num3; }; const bits2int_modN = ecdsaOpts.bits2int_modN || function bits2int_modN_def(bytes4) { return Fn.create(bits2int(bytes4)); }; const ORDER_MASK = bitMask22(fnBits); function int2octets(num3) { aInRange2("num < 2^" + fnBits, num3, _0n9, ORDER_MASK); return Fn.toBytes(num3); } function validateMsgAndHash(message, prehash) { _abytes22(message, void 0, "message"); return prehash ? _abytes22(hash3(message), void 0, "prehashed message") : message; } function prepSig(message, privateKey, opts) { if (["recovered", "canonical"].some((k2) => k2 in opts)) throw new Error("sign() legacy options not supported"); const { lowS, prehash, extraEntropy } = validateSigOpts2(opts, defaultSigOpts); message = validateMsgAndHash(message, prehash); const h1int = bits2int_modN(message); const d17 = _normFnElement2(Fn, privateKey); const seedArgs = [int2octets(d17), int2octets(h1int)]; if (extraEntropy != null && extraEntropy !== false) { const e2 = extraEntropy === true ? randomBytes4(lengths.secretKey) : extraEntropy; seedArgs.push(ensureBytes22("extraEntropy", e2)); } const seed = concatBytes4(...seedArgs); const m = h1int; function k2sig(kBytes) { const k2 = bits2int(kBytes); if (!Fn.isValidNot0(k2)) return; const ik = Fn.inv(k2); const q2 = Point22.BASE.multiply(k2).toAffine(); const r = Fn.create(q2.x); if (r === _0n9) return; const s = Fn.create(ik * Fn.create(m + r * d17)); if (s === _0n9) return; let recovery = (q2.x === r ? 0 : 2) | Number(q2.y & _1n9); let normS = s; if (lowS && isBiggerThanHalfOrder(s)) { normS = Fn.neg(s); recovery ^= 1; } return new Signature(r, normS, recovery); } return { seed, k2sig }; } function sign(message, secretKey, opts = {}) { message = ensureBytes22("message", message); const { seed, k2sig } = prepSig(message, secretKey, opts); const drbg = createHmacDrbg22(hash3.outputLen, Fn.BYTES, hmac4); const sig = drbg(seed, k2sig); return sig; } function tryParsingSig(sg) { let sig = void 0; const isHex = typeof sg === "string" || isBytes22(sg); const isObj = !isHex && sg !== null && typeof sg === "object" && typeof sg.r === "bigint" && typeof sg.s === "bigint"; if (!isHex && !isObj) throw new Error("invalid signature, expected Uint8Array, hex string or Signature instance"); if (isObj) { sig = new Signature(sg.r, sg.s); } else if (isHex) { try { sig = Signature.fromBytes(ensureBytes22("sig", sg), "der"); } catch (derError) { if (!(derError instanceof DER22.Err)) throw derError; } if (!sig) { try { sig = Signature.fromBytes(ensureBytes22("sig", sg), "compact"); } catch (error) { return false; } } } if (!sig) return false; return sig; } function verify(signature, message, publicKey, opts = {}) { const { lowS, prehash, format } = validateSigOpts2(opts, defaultSigOpts); publicKey = ensureBytes22("publicKey", publicKey); message = validateMsgAndHash(ensureBytes22("message", message), prehash); if ("strict" in opts) throw new Error("options.strict was renamed to lowS"); const sig = format === void 0 ? tryParsingSig(signature) : Signature.fromBytes(ensureBytes22("sig", signature), format); if (sig === false) return false; try { const P = Point22.fromBytes(publicKey); if (lowS && sig.hasHighS()) return false; const { r, s } = sig; const h2 = bits2int_modN(message); const is = Fn.inv(s); const u1 = Fn.create(h2 * is); const u22 = Fn.create(r * is); const R2 = Point22.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u22)); if (R2.is0()) return false; const v6 = Fn.create(R2.x); return v6 === r; } catch (e2) { return false; } } function recoverPublicKey(signature, message, opts = {}) { const { prehash } = validateSigOpts2(opts, defaultSigOpts); message = validateMsgAndHash(message, prehash); return Signature.fromBytes(signature, "recovered").recoverPublicKey(message).toBytes(); } return Object.freeze({ keygen, getPublicKey: getPublicKey22, getSharedSecret, utils, lengths, Point: Point22, sign, verify, recoverPublicKey, Signature, hash: hash3 }); } function _weierstrass_legacy_opts_to_new2(c) { const CURVE = { a: c.a, b: c.b, p: c.Fp.ORDER, n: c.n, h: c.h, Gx: c.Gx, Gy: c.Gy }; const Fp2 = c.Fp; let allowedLengths = c.allowedPrivateKeyLengths ? Array.from(new Set(c.allowedPrivateKeyLengths.map((l3) => Math.ceil(l3 / 2)))) : void 0; const Fn = Field22(CURVE.n, { BITS: c.nBitLength, allowedLengths, modFromBytes: c.wrapPrivateKey }); const curveOpts = { Fp: Fp2, Fn, allowInfinityPoint: c.allowInfinityPoint, endo: c.endo, isTorsionFree: c.isTorsionFree, clearCofactor: c.clearCofactor, fromBytes: c.fromBytes, toBytes: c.toBytes }; return { CURVE, curveOpts }; } function _ecdsa_legacy_opts_to_new2(c) { const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new2(c); const ecdsaOpts = { hmac: c.hmac, randomBytes: c.randomBytes, lowS: c.lowS, bits2int: c.bits2int, bits2int_modN: c.bits2int_modN }; return { CURVE, curveOpts, hash: c.hash, ecdsaOpts }; } function _ecdsa_new_output_to_legacy2(c, _ecdsa) { const Point22 = _ecdsa.Point; return Object.assign({}, _ecdsa, { ProjectivePoint: Point22, CURVE: Object.assign({}, c, nLength22(Point22.Fn.ORDER, Point22.Fn.BITS)) }); } function weierstrass22(c) { const { CURVE, curveOpts, hash: hash3, ecdsaOpts } = _ecdsa_legacy_opts_to_new2(c); const Point22 = weierstrassN2(CURVE, curveOpts); const signs = ecdsa2(Point22, hash3, ecdsaOpts); return _ecdsa_new_output_to_legacy2(c, signs); } function createCurve22(curveDef, defHash) { const create = (hash3) => weierstrass22({ ...curveDef, hash: hash3 }); return { ...create(defHash), create }; } var secp256k1_CURVE2 = { p: BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"), n: BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"), h: BigInt(1), a: BigInt(0), b: BigInt(7), Gx: BigInt("0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"), Gy: BigInt("0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8") }; var secp256k1_ENDO2 = { beta: BigInt("0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"), basises: [ [BigInt("0x3086d221a7d46bcde86c90e49284eb15"), -BigInt("0xe4437ed6010e88286f547fa90abfe4c3")], [BigInt("0x114ca50f7a8e2f3f657c1108d9d44cfd8"), BigInt("0x3086d221a7d46bcde86c90e49284eb15")] ] }; var _0n10 = /* @__PURE__ */ BigInt(0); var _1n10 = /* @__PURE__ */ BigInt(1); var _2n7 = /* @__PURE__ */ BigInt(2); function sqrtMod22(y2) { const P = secp256k1_CURVE2.p; const _3n5 = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22); const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88); const b2 = y2 * y2 * y2 % P; const b3 = b2 * b2 * y2 % P; const b6 = pow222(b3, _3n5, P) * b3 % P; const b9 = pow222(b6, _3n5, P) * b3 % P; const b11 = pow222(b9, _2n7, P) * b2 % P; const b22 = pow222(b11, _11n, P) * b11 % P; const b44 = pow222(b22, _22n, P) * b22 % P; const b88 = pow222(b44, _44n, P) * b44 % P; const b176 = pow222(b88, _88n, P) * b88 % P; const b220 = pow222(b176, _44n, P) * b44 % P; const b223 = pow222(b220, _3n5, P) * b3 % P; const t1 = pow222(b223, _23n, P) * b22 % P; const t2 = pow222(t1, _6n, P) * b2 % P; const root = pow222(t2, _2n7, P); if (!Fpk12.eql(Fpk12.sqr(root), y2)) throw new Error("Cannot find square root"); return root; } var Fpk12 = Field22(secp256k1_CURVE2.p, { sqrt: sqrtMod22 }); var secp256k122 = createCurve22({ ...secp256k1_CURVE2, Fp: Fpk12, lowS: true, endo: secp256k1_ENDO2 }, sha25632); var TAGGED_HASH_PREFIXES22 = {}; function taggedHash22(tag, ...messages) { let tagP = TAGGED_HASH_PREFIXES22[tag]; if (tagP === void 0) { const tagH = sha25632(utf8ToBytes5(tag)); tagP = concatBytes4(tagH, tagH); TAGGED_HASH_PREFIXES22[tag] = tagP; } return sha25632(concatBytes4(tagP, ...messages)); } var pointToBytes22 = (point) => point.toBytes(true).slice(1); var Pointk12 = /* @__PURE__ */ (() => secp256k122.Point)(); var hasEven2 = (y2) => y2 % _2n7 === _0n10; function schnorrGetExtPubKey22(priv) { const { Fn, BASE } = Pointk12; const d_ = _normFnElement2(Fn, priv); const p5 = BASE.multiply(d_); const scalar = hasEven2(p5.y) ? d_ : Fn.neg(d_); return { scalar, bytes: pointToBytes22(p5) }; } function lift_x22(x2) { const Fp2 = Fpk12; if (!Fp2.isValidNot0(x2)) throw new Error("invalid x: Fail if x \u2265 p"); const xx = Fp2.create(x2 * x2); const c = Fp2.create(xx * x2 + BigInt(7)); let y2 = Fp2.sqrt(c); if (!hasEven2(y2)) y2 = Fp2.neg(y2); const p5 = Pointk12.fromAffine({ x: x2, y: y2 }); p5.assertValidity(); return p5; } var num2 = bytesToNumberBE22; function challenge22(...args) { return Pointk12.Fn.create(num2(taggedHash22("BIP0340/challenge", ...args))); } function schnorrGetPublicKey22(secretKey) { return schnorrGetExtPubKey22(secretKey).bytes; } function schnorrSign22(message, secretKey, auxRand = randomBytes3(32)) { const { Fn } = Pointk12; const m = ensureBytes22("message", message); const { bytes: px, scalar: d17 } = schnorrGetExtPubKey22(secretKey); const a = ensureBytes22("auxRand", auxRand, 32); const t = Fn.toBytes(d17 ^ num2(taggedHash22("BIP0340/aux", a))); const rand = taggedHash22("BIP0340/nonce", t, px, m); const { bytes: rx, scalar: k2 } = schnorrGetExtPubKey22(rand); const e2 = challenge22(rx, px, m); const sig = new Uint8Array(64); sig.set(rx, 0); sig.set(Fn.toBytes(Fn.create(k2 + e2 * d17)), 32); if (!schnorrVerify22(sig, m, px)) throw new Error("sign: Invalid signature produced"); return sig; } function schnorrVerify22(signature, message, publicKey) { const { Fn, BASE } = Pointk12; const sig = ensureBytes22("signature", signature, 64); const m = ensureBytes22("message", message); const pub = ensureBytes22("publicKey", publicKey, 32); try { const P = lift_x22(num2(pub)); const r = num2(sig.subarray(0, 32)); if (!inRange2(r, _1n10, secp256k1_CURVE2.p)) return false; const s = num2(sig.subarray(32, 64)); if (!inRange2(s, _1n10, secp256k1_CURVE2.n)) return false; const e2 = challenge22(Fn.toBytes(r), pointToBytes22(P), m); const R2 = BASE.multiplyUnsafe(s).add(P.multiplyUnsafe(Fn.neg(e2))); const { x: x2, y: y2 } = R2.toAffine(); if (R2.is0() || !hasEven2(y2) || x2 !== r) return false; return true; } catch (error) { return false; } } var schnorr22 = /* @__PURE__ */ (() => { const size = 32; const seedLength = 48; const randomSecretKey = (seed = randomBytes3(seedLength)) => { return mapHashToField22(seed, secp256k1_CURVE2.n); }; secp256k122.utils.randomSecretKey; function keygen(seed) { const secretKey = randomSecretKey(seed); return { secretKey, publicKey: schnorrGetPublicKey22(secretKey) }; } return { keygen, getPublicKey: schnorrGetPublicKey22, sign: schnorrSign22, verify: schnorrVerify22, Point: Pointk12, utils: { randomSecretKey, randomPrivateKey: randomSecretKey, taggedHash: taggedHash22, lift_x: lift_x22, pointToBytes: pointToBytes22, numberToBytesBE: numberToBytesBE22, bytesToNumberBE: bytesToNumberBE22, mod: mod22 }, lengths: { secretKey: size, publicKey: size, publicKeyHasPrefix: false, signature: size * 2, seed: seedLength } }; })(); var sha2564 = sha25632; var import_typescript_lru_cache8 = __toESM2(require_dist2(), 1); var import_tseep32 = __toESM2(require_lib3(), 1); var exports_nip49 = {}; __export2(exports_nip49, { encrypt: () => encrypt32, decrypt: () => decrypt32 }); function pbkdf2Init(hash3, _password, _salt, _opts) { _assert_default.hash(hash3); const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts); const { c, dkLen, asyncTick } = opts; _assert_default.number(c); _assert_default.number(dkLen); _assert_default.number(asyncTick); if (c < 1) throw new Error("PBKDF2: iterations (c) should be >= 1"); const password = toBytes22(_password); const salt = toBytes22(_salt); const DK = new Uint8Array(dkLen); const PRF = hmac22.create(hash3, password); const PRFSalt = PRF._cloneInto().update(salt); return { c, dkLen, asyncTick, DK, PRF, PRFSalt }; } function pbkdf2Output(PRF, PRFSalt, DK, prfW, u3) { PRF.destroy(); PRFSalt.destroy(); if (prfW) prfW.destroy(); u3.fill(0); return DK; } function pbkdf2(hash3, password, salt, opts) { const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash3, password, salt, opts); let prfW; const arr = new Uint8Array(4); const view = createView22(arr); const u3 = new Uint8Array(PRF.outputLen); for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) { const Ti = DK.subarray(pos, pos + PRF.outputLen); view.setInt32(0, ti, false); (prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u3); Ti.set(u3.subarray(0, Ti.length)); for (let ui = 1; ui < c; ui++) { PRF._cloneInto(prfW).update(u3).digestInto(u3); for (let i22 = 0; i22 < Ti.length; i22++) Ti[i22] ^= u3[i22]; } } return pbkdf2Output(PRF, PRFSalt, DK, prfW, u3); } var rotl22 = (a, b) => a << b | a >>> 32 - b; function XorAndSalsa(prev, pi, input, ii, out, oi) { let y00 = prev[pi++] ^ input[ii++], y01 = prev[pi++] ^ input[ii++]; let y02 = prev[pi++] ^ input[ii++], y03 = prev[pi++] ^ input[ii++]; let y04 = prev[pi++] ^ input[ii++], y05 = prev[pi++] ^ input[ii++]; let y06 = prev[pi++] ^ input[ii++], y07 = prev[pi++] ^ input[ii++]; let y08 = prev[pi++] ^ input[ii++], y09 = prev[pi++] ^ input[ii++]; let y10 = prev[pi++] ^ input[ii++], y11 = prev[pi++] ^ input[ii++]; let y12 = prev[pi++] ^ input[ii++], y13 = prev[pi++] ^ input[ii++]; let y14 = prev[pi++] ^ input[ii++], y15 = prev[pi++] ^ input[ii++]; let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15; for (let i22 = 0; i22 < 8; i22 += 2) { x04 ^= rotl22(x00 + x12 | 0, 7); x08 ^= rotl22(x04 + x00 | 0, 9); x12 ^= rotl22(x08 + x04 | 0, 13); x00 ^= rotl22(x12 + x08 | 0, 18); x09 ^= rotl22(x05 + x01 | 0, 7); x13 ^= rotl22(x09 + x05 | 0, 9); x01 ^= rotl22(x13 + x09 | 0, 13); x05 ^= rotl22(x01 + x13 | 0, 18); x14 ^= rotl22(x10 + x06 | 0, 7); x02 ^= rotl22(x14 + x10 | 0, 9); x06 ^= rotl22(x02 + x14 | 0, 13); x10 ^= rotl22(x06 + x02 | 0, 18); x03 ^= rotl22(x15 + x11 | 0, 7); x07 ^= rotl22(x03 + x15 | 0, 9); x11 ^= rotl22(x07 + x03 | 0, 13); x15 ^= rotl22(x11 + x07 | 0, 18); x01 ^= rotl22(x00 + x03 | 0, 7); x02 ^= rotl22(x01 + x00 | 0, 9); x03 ^= rotl22(x02 + x01 | 0, 13); x00 ^= rotl22(x03 + x02 | 0, 18); x06 ^= rotl22(x05 + x04 | 0, 7); x07 ^= rotl22(x06 + x05 | 0, 9); x04 ^= rotl22(x07 + x06 | 0, 13); x05 ^= rotl22(x04 + x07 | 0, 18); x11 ^= rotl22(x10 + x09 | 0, 7); x08 ^= rotl22(x11 + x10 | 0, 9); x09 ^= rotl22(x08 + x11 | 0, 13); x10 ^= rotl22(x09 + x08 | 0, 18); x12 ^= rotl22(x15 + x14 | 0, 7); x13 ^= rotl22(x12 + x15 | 0, 9); x14 ^= rotl22(x13 + x12 | 0, 13); x15 ^= rotl22(x14 + x13 | 0, 18); } out[oi++] = y00 + x00 | 0; out[oi++] = y01 + x01 | 0; out[oi++] = y02 + x02 | 0; out[oi++] = y03 + x03 | 0; out[oi++] = y04 + x04 | 0; out[oi++] = y05 + x05 | 0; out[oi++] = y06 + x06 | 0; out[oi++] = y07 + x07 | 0; out[oi++] = y08 + x08 | 0; out[oi++] = y09 + x09 | 0; out[oi++] = y10 + x10 | 0; out[oi++] = y11 + x11 | 0; out[oi++] = y12 + x12 | 0; out[oi++] = y13 + x13 | 0; out[oi++] = y14 + x14 | 0; out[oi++] = y15 + x15 | 0; } function BlockMix(input, ii, out, oi, r) { let head = oi + 0; let tail = oi + 16 * r; for (let i22 = 0; i22 < 16; i22++) out[tail + i22] = input[ii + (2 * r - 1) * 16 + i22]; for (let i22 = 0; i22 < r; i22++, head += 16, ii += 16) { XorAndSalsa(out, tail, input, ii, out, head); if (i22 > 0) tail += 16; XorAndSalsa(out, head, input, ii += 16, out, tail); } } function scryptInit(password, salt, _opts) { const opts = checkOpts({ dkLen: 32, asyncTick: 10, maxmem: 1024 ** 3 + 1024 }, _opts); const { N, r, p: p5, dkLen, asyncTick, maxmem, onProgress } = opts; _assert_default.number(N); _assert_default.number(r); _assert_default.number(p5); _assert_default.number(dkLen); _assert_default.number(asyncTick); _assert_default.number(maxmem); if (onProgress !== void 0 && typeof onProgress !== "function") throw new Error("progressCb should be function"); const blockSize = 128 * r; const blockSize32 = blockSize / 4; if (N <= 1 || (N & N - 1) !== 0 || N >= 2 ** (blockSize / 8) || N > 2 ** 32) { throw new Error("Scrypt: N must be larger than 1, a power of 2, less than 2^(128 * r / 8) and less than 2^32"); } if (p5 < 0 || p5 > (2 ** 32 - 1) * 32 / blockSize) { throw new Error("Scrypt: p must be a positive integer less than or equal to ((2^32 - 1) * 32) / (128 * r)"); } if (dkLen < 0 || dkLen > (2 ** 32 - 1) * 32) { throw new Error("Scrypt: dkLen should be positive integer less than or equal to (2^32 - 1) * 32"); } const memUsed = blockSize * (N + p5); if (memUsed > maxmem) { throw new Error(`Scrypt: parameters too large, ${memUsed} (128 * r * (N + p)) > ${maxmem} (maxmem)`); } const B = pbkdf2(sha25622, password, salt, { c: 1, dkLen: blockSize * p5 }); const B32 = u32(B); const V2 = u32(new Uint8Array(blockSize * N)); const tmp = u32(new Uint8Array(blockSize)); let blockMixCb = () => { }; if (onProgress) { const totalBlockMix = 2 * N * p5; const callbackPer = Math.max(Math.floor(totalBlockMix / 1e4), 1); let blockMixCnt = 0; blockMixCb = () => { blockMixCnt++; if (onProgress && (!(blockMixCnt % callbackPer) || blockMixCnt === totalBlockMix)) onProgress(blockMixCnt / totalBlockMix); }; } return { N, r, p: p5, dkLen, blockSize32, V: V2, B32, B, tmp, blockMixCb, asyncTick }; } function scryptOutput(password, dkLen, B, V2, tmp) { const res = pbkdf2(sha25622, password, B, { c: 1, dkLen }); B.fill(0); V2.fill(0); tmp.fill(0); return res; } function scrypt(password, salt, opts) { const { N, r, p: p5, dkLen, blockSize32, V: V2, B32, B, tmp, blockMixCb } = scryptInit(password, salt, opts); for (let pi = 0; pi < p5; pi++) { const Pi = blockSize32 * pi; for (let i22 = 0; i22 < blockSize32; i22++) V2[i22] = B32[Pi + i22]; for (let i22 = 0, pos = 0; i22 < N - 1; i22++) { BlockMix(V2, pos, V2, pos += blockSize32, r); blockMixCb(); } BlockMix(V2, (N - 1) * blockSize32, B32, Pi, r); blockMixCb(); for (let i22 = 0; i22 < N; i22++) { const j2 = B32[Pi + blockSize32 - 16] % N; for (let k2 = 0; k2 < blockSize32; k2++) tmp[k2] = B32[Pi + k2] ^ V2[j2 * blockSize32 + k2]; BlockMix(tmp, 0, B32, Pi, r); blockMixCb(); } } return scryptOutput(password, dkLen, B, V2, tmp); } var Bech32MaxSize2 = 5e3; function encodeBech322(prefix, data) { let words = bech322.toWords(data); return bech322.encode(prefix, words, Bech32MaxSize2); } function encodeBytes2(prefix, bytes4) { return encodeBech322(prefix, bytes4); } function encrypt32(sec, password, logn = 16, ksb = 2) { let salt = randomBytes22(16); let n = 2 ** logn; let key = scrypt(password.normalize("NFKC"), salt, { N: n, r: 8, p: 1, dkLen: 32 }); let nonce = randomBytes22(24); let aad = Uint8Array.from([ksb]); let xc2p1 = xchacha20poly1305(key, nonce, aad); let ciphertext = xc2p1.encrypt(sec); let b = concatBytes3(Uint8Array.from([2]), Uint8Array.from([logn]), salt, nonce, aad, ciphertext); return encodeBytes2("ncryptsec", b); } function decrypt32(ncryptsec, password) { let { prefix, words } = bech322.decode(ncryptsec, Bech32MaxSize2); if (prefix !== "ncryptsec") { throw new Error(`invalid prefix ${prefix}, expected 'ncryptsec'`); } let b = new Uint8Array(bech322.fromWords(words)); let version = b[0]; if (version !== 2) { throw new Error(`invalid version ${version}, expected 0x02`); } let logn = b[1]; let n = 2 ** logn; let salt = b.slice(2, 2 + 16); let nonce = b.slice(2 + 16, 2 + 16 + 24); let ksb = b[2 + 16 + 24]; let aad = Uint8Array.from([ksb]); let ciphertext = b.slice(2 + 16 + 24 + 1); let key = scrypt(password.normalize("NFKC"), salt, { N: n, r: 8, p: 1, dkLen: 32 }); let xc2p1 = xchacha20poly1305(key, nonce, aad); let sec = xc2p1.decrypt(ciphertext); return sec; } var import_tseep42 = __toESM2(require_lib3(), 1); var import_debug42 = __toESM2(require_browser2(), 1); var import_debug52 = __toESM2(require_browser2(), 1); var import_debug62 = __toESM2(require_browser2(), 1); var import_light_bolt11_decoder3 = __toESM2(require_bolt112(), 1); var import_debug72 = __toESM2(require_browser2(), 1); var import_tseep52 = __toESM2(require_lib3(), 1); var import_tseep62 = __toESM2(require_lib3(), 1); var import_typescript_lru_cache22 = __toESM2(require_dist2(), 1); var import_typescript_lru_cache32 = __toESM2(require_dist2(), 1); var import_debug82 = __toESM2(require_browser2(), 1); var exports_nip19 = {}; __export2(exports_nip19, { nsecEncode: () => nsecEncode2, npubEncode: () => npubEncode2, nprofileEncode: () => nprofileEncode2, noteEncode: () => noteEncode2, neventEncode: () => neventEncode2, naddrEncode: () => naddrEncode2, encodeBytes: () => encodeBytes3, decodeNostrURI: () => decodeNostrURI2, decode: () => decode22, NostrTypeGuard: () => NostrTypeGuard2, Bech32MaxSize: () => Bech32MaxSize3, BECH32_REGEX: () => BECH32_REGEX22 }); var utf8Decoder2 = new TextDecoder("utf-8"); var utf8Encoder2 = new TextEncoder(); var NostrTypeGuard2 = { isNProfile: (value) => /^nprofile1[a-z\d]+$/.test(value || ""), isNEvent: (value) => /^nevent1[a-z\d]+$/.test(value || ""), isNAddr: (value) => /^naddr1[a-z\d]+$/.test(value || ""), isNSec: (value) => /^nsec1[a-z\d]{58}$/.test(value || ""), isNPub: (value) => /^npub1[a-z\d]{58}$/.test(value || ""), isNote: (value) => /^note1[a-z\d]+$/.test(value || ""), isNcryptsec: (value) => /^ncryptsec1[a-z\d]+$/.test(value || "") }; var Bech32MaxSize3 = 5e3; var BECH32_REGEX22 = /[\x21-\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/; function integerToUint8Array2(number4) { const uint8Array = new Uint8Array(4); uint8Array[0] = number4 >> 24 & 255; uint8Array[1] = number4 >> 16 & 255; uint8Array[2] = number4 >> 8 & 255; uint8Array[3] = number4 & 255; return uint8Array; } function decodeNostrURI2(nip19code) { try { if (nip19code.startsWith("nostr:")) nip19code = nip19code.substring(6); return decode22(nip19code); } catch (_err) { return { type: "invalid", data: null }; } } function decode22(code) { let { prefix, words } = bech322.decode(code, Bech32MaxSize3); let data = new Uint8Array(bech322.fromWords(words)); switch (prefix) { case "nprofile": { let tlv = parseTLV2(data); if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for nprofile"); if (tlv[0][0].length !== 32) throw new Error("TLV 0 should be 32 bytes"); return { type: "nprofile", data: { pubkey: bytesToHex22(tlv[0][0]), relays: tlv[1] ? tlv[1].map((d17) => utf8Decoder2.decode(d17)) : [] } }; } case "nevent": { let tlv = parseTLV2(data); if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for nevent"); if (tlv[0][0].length !== 32) throw new Error("TLV 0 should be 32 bytes"); if (tlv[2] && tlv[2][0].length !== 32) throw new Error("TLV 2 should be 32 bytes"); if (tlv[3] && tlv[3][0].length !== 4) throw new Error("TLV 3 should be 4 bytes"); return { type: "nevent", data: { id: bytesToHex22(tlv[0][0]), relays: tlv[1] ? tlv[1].map((d17) => utf8Decoder2.decode(d17)) : [], author: tlv[2]?.[0] ? bytesToHex22(tlv[2][0]) : void 0, kind: tlv[3]?.[0] ? parseInt(bytesToHex22(tlv[3][0]), 16) : void 0 } }; } case "naddr": { let tlv = parseTLV2(data); if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for naddr"); if (!tlv[2]?.[0]) throw new Error("missing TLV 2 for naddr"); if (tlv[2][0].length !== 32) throw new Error("TLV 2 should be 32 bytes"); if (!tlv[3]?.[0]) throw new Error("missing TLV 3 for naddr"); if (tlv[3][0].length !== 4) throw new Error("TLV 3 should be 4 bytes"); return { type: "naddr", data: { identifier: utf8Decoder2.decode(tlv[0][0]), pubkey: bytesToHex22(tlv[2][0]), kind: parseInt(bytesToHex22(tlv[3][0]), 16), relays: tlv[1] ? tlv[1].map((d17) => utf8Decoder2.decode(d17)) : [] } }; } case "nsec": return { type: prefix, data }; case "npub": case "note": return { type: prefix, data: bytesToHex22(data) }; default: throw new Error(`unknown prefix ${prefix}`); } } function parseTLV2(data) { let result = {}; let rest = data; while (rest.length > 0) { let t = rest[0]; let l3 = rest[1]; let v6 = rest.slice(2, 2 + l3); rest = rest.slice(2 + l3); if (v6.length < l3) throw new Error(`not enough data to read on TLV ${t}`); result[t] = result[t] || []; result[t].push(v6); } return result; } function nsecEncode2(key) { return encodeBytes3("nsec", key); } function npubEncode2(hex2) { return encodeBytes3("npub", hexToBytes22(hex2)); } function noteEncode2(hex2) { return encodeBytes3("note", hexToBytes22(hex2)); } function encodeBech323(prefix, data) { let words = bech322.toWords(data); return bech322.encode(prefix, words, Bech32MaxSize3); } function encodeBytes3(prefix, bytes4) { return encodeBech323(prefix, bytes4); } function nprofileEncode2(profile) { let data = encodeTLV2({ 0: [hexToBytes22(profile.pubkey)], 1: (profile.relays || []).map((url) => utf8Encoder2.encode(url)) }); return encodeBech323("nprofile", data); } function neventEncode2(event) { let kindArray; if (event.kind !== void 0) { kindArray = integerToUint8Array2(event.kind); } let data = encodeTLV2({ 0: [hexToBytes22(event.id)], 1: (event.relays || []).map((url) => utf8Encoder2.encode(url)), 2: event.author ? [hexToBytes22(event.author)] : [], 3: kindArray ? [new Uint8Array(kindArray)] : [] }); return encodeBech323("nevent", data); } function naddrEncode2(addr) { let kind = new ArrayBuffer(4); new DataView(kind).setUint32(0, addr.kind, false); let data = encodeTLV2({ 0: [utf8Encoder2.encode(addr.identifier)], 1: (addr.relays || []).map((url) => utf8Encoder2.encode(url)), 2: [hexToBytes22(addr.pubkey)], 3: [new Uint8Array(kind)] }); return encodeBech323("naddr", data); } function encodeTLV2(tlv) { let entries = []; Object.entries(tlv).reverse().forEach(([t, vs]) => { vs.forEach((v6) => { let entry = new Uint8Array(v6.length + 2); entry.set([parseInt(t)], 0); entry.set([v6.length], 1); entry.set(v6, 2); entries.push(entry); }); }); return concatBytes3(...entries); } var import_debug92 = __toESM2(require_browser2(), 1); var import_debug102 = __toESM2(require_browser2(), 1); var import_tseep72 = __toESM2(require_lib3(), 1); var import_tseep82 = __toESM2(require_lib3(), 1); var import_debug112 = __toESM2(require_browser2(), 1); var import_tseep92 = __toESM2(require_lib3(), 1); var import_debug122 = __toESM2(require_browser2(), 1); var __defProp32 = Object.defineProperty; var __getOwnPropDesc3 = Object.getOwnPropertyDescriptor; var __getOwnPropNames22 = Object.getOwnPropertyNames; var __hasOwnProp22 = Object.prototype.hasOwnProperty; var __copyProps3 = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames22(from)) if (!__hasOwnProp22.call(to, key) && key !== except) __defProp32(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc3(from, key)) || desc.enumerable }); } return to; }; var __reExport3 = (target, mod3, secondTarget) => (__copyProps3(target, mod3, "default"), secondTarget && __copyProps3(secondTarget, mod3, "default")); function getRelaysForSync3(ndk, author, type = "write") { if (!ndk.outboxTracker) return; const item = ndk.outboxTracker.data.get(author); if (!item) return; if (type === "write") { return item.writeRelays; } return item.readRelays; } async function getWriteRelaysFor3(ndk, author, type = "write") { if (!ndk.outboxTracker) return; if (!ndk.outboxTracker.data.has(author)) { await ndk.outboxTracker.trackUsers([author]); } return getRelaysForSync3(ndk, author, type); } function getTopRelaysForAuthors3(ndk, authors) { const relaysWithCount = /* @__PURE__ */ new Map(); authors.forEach((author) => { const writeRelays = getRelaysForSync3(ndk, author); if (writeRelays) { writeRelays.forEach((relay) => { const count = relaysWithCount.get(relay) || 0; relaysWithCount.set(relay, count + 1); }); } }); const sortedRelays = Array.from(relaysWithCount.entries()).sort((a, b) => b[1] - a[1]); return sortedRelays.map((entry) => entry[0]); } function getAllRelaysForAllPubkeys4(ndk, pubkeys, type = "read") { const pubkeysToRelays = /* @__PURE__ */ new Map(); const authorsMissingRelays = /* @__PURE__ */ new Set(); pubkeys.forEach((pubkey) => { const relays = getRelaysForSync3(ndk, pubkey, type); if (relays && relays.size > 0) { relays.forEach((relay) => { const pubkeysInRelay = pubkeysToRelays.get(relay) || /* @__PURE__ */ new Set(); pubkeysInRelay.add(pubkey); }); pubkeysToRelays.set(pubkey, relays); } else { authorsMissingRelays.add(pubkey); } }); return { pubkeysToRelays, authorsMissingRelays }; } function chooseRelayCombinationForPubkeys3(ndk, pubkeys, type, { count, preferredRelays } = {}) { count ?? (count = 2); preferredRelays ?? (preferredRelays = /* @__PURE__ */ new Set()); const pool = ndk.pool; const connectedRelays = pool.connectedRelays(); connectedRelays.forEach((relay) => { preferredRelays?.add(relay.url); }); const relayToAuthorsMap = /* @__PURE__ */ new Map(); const { pubkeysToRelays, authorsMissingRelays } = getAllRelaysForAllPubkeys4(ndk, pubkeys, type); const sortedRelays = getTopRelaysForAuthors3(ndk, pubkeys); const addAuthorToRelay = (author, relay) => { const authorsInRelay = relayToAuthorsMap.get(relay) || []; authorsInRelay.push(author); relayToAuthorsMap.set(relay, authorsInRelay); }; for (const [author, authorRelays] of pubkeysToRelays.entries()) { let missingRelayCount = count; const addedRelaysForAuthor = /* @__PURE__ */ new Set(); for (const relay of connectedRelays) { if (authorRelays.has(relay.url)) { addAuthorToRelay(author, relay.url); addedRelaysForAuthor.add(relay.url); missingRelayCount--; } } for (const authorRelay of authorRelays) { if (addedRelaysForAuthor.has(authorRelay)) continue; if (relayToAuthorsMap.has(authorRelay)) { addAuthorToRelay(author, authorRelay); addedRelaysForAuthor.add(authorRelay); missingRelayCount--; } } if (missingRelayCount <= 0) continue; for (const relay of sortedRelays) { if (missingRelayCount <= 0) break; if (addedRelaysForAuthor.has(relay)) continue; if (authorRelays.has(relay)) { addAuthorToRelay(author, relay); addedRelaysForAuthor.add(relay); missingRelayCount--; } } } for (const author of authorsMissingRelays) { pool.permanentAndConnectedRelays().forEach((relay) => { const authorsInRelay = relayToAuthorsMap.get(relay.url) || []; authorsInRelay.push(author); relayToAuthorsMap.set(relay.url, authorsInRelay); }); } return relayToAuthorsMap; } function getRelaysForFilterWithAuthors2(ndk, authors, relayGoalPerAuthor = 2) { return chooseRelayCombinationForPubkeys3(ndk, authors, "write", { count: relayGoalPerAuthor }); } function tryNormalizeRelayUrl3(url) { try { return normalizeRelayUrl3(url); } catch { return; } } function normalizeRelayUrl3(url) { let r = normalizeUrl3(url, { stripAuthentication: false, stripWWW: false, stripHash: true }); if (!r.endsWith("/")) { r += "/"; } return r; } function normalize22(urls) { const normalized = /* @__PURE__ */ new Set(); for (const url of urls) { try { normalized.add(normalizeRelayUrl3(url)); } catch { } } return Array.from(normalized); } var DATA_URL_DEFAULT_MIME_TYPE3 = "text/plain"; var DATA_URL_DEFAULT_CHARSET3 = "us-ascii"; var testParameter3 = (name, filters) => filters.some((filter) => filter instanceof RegExp ? filter.test(name) : filter === name); var supportedProtocols3 = /* @__PURE__ */ new Set(["https:", "http:", "file:"]); var hasCustomProtocol3 = (urlString) => { try { const { protocol } = new URL(urlString); return protocol.endsWith(":") && !protocol.includes(".") && !supportedProtocols3.has(protocol); } catch { return false; } }; var normalizeDataURL3 = (urlString, { stripHash }) => { const match = /^data:(?[^,]*?),(?[^#]*?)(?:#(?.*))?$/.exec(urlString); if (!match) { throw new Error(`Invalid URL: ${urlString}`); } const type = match.groups?.type ?? ""; const data = match.groups?.data ?? ""; let hash3 = match.groups?.hash ?? ""; const mediaType = type.split(";"); hash3 = stripHash ? "" : hash3; let isBase64 = false; if (mediaType[mediaType.length - 1] === "base64") { mediaType.pop(); isBase64 = true; } const mimeType = mediaType.shift()?.toLowerCase() ?? ""; const attributes = mediaType.map((attribute) => { let [key, value = ""] = attribute.split("=").map((string) => string.trim()); if (key === "charset") { value = value.toLowerCase(); if (value === DATA_URL_DEFAULT_CHARSET3) { return ""; } } return `${key}${value ? `=${value}` : ""}`; }).filter(Boolean); const normalizedMediaType = [...attributes]; if (isBase64) { normalizedMediaType.push("base64"); } if (normalizedMediaType.length > 0 || mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE3) { normalizedMediaType.unshift(mimeType); } return `data:${normalizedMediaType.join(";")},${isBase64 ? data.trim() : data}${hash3 ? `#${hash3}` : ""}`; }; function normalizeUrl3(urlString, options = {}) { options = { defaultProtocol: "http", normalizeProtocol: true, forceHttp: false, forceHttps: false, stripAuthentication: true, stripHash: false, stripTextFragment: true, stripWWW: true, removeQueryParameters: [/^utm_\w+/i], removeTrailingSlash: true, removeSingleSlash: true, removeDirectoryIndex: false, removeExplicitPort: false, sortQueryParameters: true, ...options }; if (typeof options.defaultProtocol === "string" && !options.defaultProtocol.endsWith(":")) { options.defaultProtocol = `${options.defaultProtocol}:`; } urlString = urlString.trim(); if (/^data:/i.test(urlString)) { return normalizeDataURL3(urlString, options); } if (hasCustomProtocol3(urlString)) { return urlString; } const hasRelativeProtocol = urlString.startsWith("//"); const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString); if (!isRelativeUrl) { urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol); } const urlObject = new URL(urlString); urlObject.hostname = urlObject.hostname.toLowerCase(); if (options.forceHttp && options.forceHttps) { throw new Error("The `forceHttp` and `forceHttps` options cannot be used together"); } if (options.forceHttp && urlObject.protocol === "https:") { urlObject.protocol = "http:"; } if (options.forceHttps && urlObject.protocol === "http:") { urlObject.protocol = "https:"; } if (options.stripAuthentication) { urlObject.username = ""; urlObject.password = ""; } if (options.stripHash) { urlObject.hash = ""; } else if (options.stripTextFragment) { urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, ""); } if (urlObject.pathname) { const protocolRegex = /\b[a-z][a-z\d+\-.]{1,50}:\/\//g; let lastIndex = 0; let result = ""; for (; ; ) { const match = protocolRegex.exec(urlObject.pathname); if (!match) { break; } const protocol = match[0]; const protocolAtIndex = match.index; const intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex); result += intermediate.replace(/\/{2,}/g, "/"); result += protocol; lastIndex = protocolAtIndex + protocol.length; } const remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length); result += remnant.replace(/\/{2,}/g, "/"); urlObject.pathname = result; } if (urlObject.pathname) { try { urlObject.pathname = decodeURI(urlObject.pathname); } catch { } } if (options.removeDirectoryIndex === true) { options.removeDirectoryIndex = [/^index\.[a-z]+$/]; } if (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) { let pathComponents = urlObject.pathname.split("/"); const lastComponent = pathComponents[pathComponents.length - 1]; if (testParameter3(lastComponent, options.removeDirectoryIndex)) { pathComponents = pathComponents.slice(0, -1); urlObject.pathname = `${pathComponents.slice(1).join("/")}/`; } } if (urlObject.hostname) { urlObject.hostname = urlObject.hostname.replace(/\.$/, ""); if (options.stripWWW && /^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(urlObject.hostname)) { urlObject.hostname = urlObject.hostname.replace(/^www\./, ""); } } if (Array.isArray(options.removeQueryParameters)) { for (const key of [...urlObject.searchParams.keys()]) { if (testParameter3(key, options.removeQueryParameters)) { urlObject.searchParams.delete(key); } } } if (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) { urlObject.search = ""; } if (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) { for (const key of [...urlObject.searchParams.keys()]) { if (!testParameter3(key, options.keepQueryParameters)) { urlObject.searchParams.delete(key); } } } if (options.sortQueryParameters) { urlObject.searchParams.sort(); try { urlObject.search = decodeURIComponent(urlObject.search); } catch { } } if (options.removeTrailingSlash) { urlObject.pathname = urlObject.pathname.replace(/\/$/, ""); } if (options.removeExplicitPort && urlObject.port) { urlObject.port = ""; } const oldUrlString = urlString; urlString = urlObject.toString(); if (!options.removeSingleSlash && urlObject.pathname === "/" && !oldUrlString.endsWith("/") && urlObject.hash === "") { urlString = urlString.replace(/\/$/, ""); } if ((options.removeTrailingSlash || urlObject.pathname === "/") && urlObject.hash === "" && options.removeSingleSlash) { urlString = urlString.replace(/\/$/, ""); } if (hasRelativeProtocol && !options.normalizeProtocol) { urlString = urlString.replace(/^http:\/\//, "//"); } if (options.stripProtocol) { urlString = urlString.replace(/^(?:https?:)?\/\//, ""); } return urlString; } var NDKRelayKeepalive3 = class { constructor(timeout = 3e4, onSilenceDetected) { __publicField(this, "lastActivity", Date.now()); __publicField(this, "timer"); __publicField(this, "timeout"); __publicField(this, "isRunning", false); this.onSilenceDetected = onSilenceDetected; this.timeout = timeout; } recordActivity() { this.lastActivity = Date.now(); if (this.isRunning) { this.resetTimer(); } } start() { if (this.isRunning) return; this.isRunning = true; this.lastActivity = Date.now(); this.resetTimer(); } stop() { this.isRunning = false; if (this.timer) { clearTimeout(this.timer); this.timer = void 0; } } resetTimer() { if (this.timer) { clearTimeout(this.timer); } this.timer = setTimeout(() => { const silenceTime = Date.now() - this.lastActivity; if (silenceTime >= this.timeout) { this.onSilenceDetected(); } else { const remainingTime = this.timeout - silenceTime; this.timer = setTimeout(() => { this.onSilenceDetected(); }, remainingTime); } }, this.timeout); } }; async function probeRelayConnection3(relay) { const probeId = `probe-${Math.random().toString(36).substring(7)}`; return new Promise((resolve) => { let responded = false; const timeout = setTimeout(() => { if (!responded) { responded = true; relay.send(["CLOSE", probeId]); resolve(false); } }, 5e3); const handler = () => { if (!responded) { responded = true; clearTimeout(timeout); relay.send(["CLOSE", probeId]); resolve(true); } }; relay.once("message", handler); relay.send([ "REQ", probeId, { kinds: [99999], limit: 0 } ]); }); } var MAX_RECONNECT_ATTEMPTS3 = 5; var FLAPPING_THRESHOLD_MS3 = 1e3; var NDKRelayConnectivity3 = class { constructor(ndkRelay, ndk) { __publicField(this, "ndkRelay"); __publicField(this, "ws"); __publicField(this, "_status"); __publicField(this, "timeoutMs"); __publicField(this, "connectedAt"); __publicField(this, "_connectionStats", { attempts: 0, success: 0, durations: [] }); __publicField(this, "debug"); __publicField(this, "netDebug"); __publicField(this, "connectTimeout"); __publicField(this, "reconnectTimeout"); __publicField(this, "ndk"); __publicField(this, "openSubs", /* @__PURE__ */ new Map()); __publicField(this, "openCountRequests", /* @__PURE__ */ new Map()); __publicField(this, "openEventPublishes", /* @__PURE__ */ new Map()); __publicField(this, "pendingAuthPublishes", /* @__PURE__ */ new Map()); __publicField(this, "serial", 0); __publicField(this, "baseEoseTimeout", 4400); __publicField(this, "keepalive"); __publicField(this, "wsStateMonitor"); __publicField(this, "sleepDetector"); __publicField(this, "lastSleepCheck", Date.now()); __publicField(this, "lastMessageSent", Date.now()); __publicField(this, "wasIdle", false); __publicField(this, "updateConnectionStats", { connected: () => { this._connectionStats.success++; this._connectionStats.connectedAt = Date.now(); }, disconnected: () => { if (this._connectionStats.connectedAt) { this._connectionStats.durations.push(Date.now() - this._connectionStats.connectedAt); if (this._connectionStats.durations.length > 100) { this._connectionStats.durations.shift(); } } this._connectionStats.connectedAt = void 0; }, attempt: () => { this._connectionStats.attempts++; this._connectionStats.connectedAt = Date.now(); } }); this.ndkRelay = ndkRelay; this._status = 1; const rand = Math.floor(Math.random() * 1e3); this.debug = this.ndkRelay.debug.extend(`connectivity${rand}`); this.ndk = ndk; this.setupMonitoring(); } setupMonitoring() { this.keepalive = new NDKRelayKeepalive3(12e4, async () => { this.debug("Relay silence detected, probing connection"); const isAlive = await probeRelayConnection3({ send: (msg) => this.send(JSON.stringify(msg)), once: (event, handler) => { const messageHandler = (e2) => { try { const data = JSON.parse(e2.data); if (data[0] === "EOSE" || data[0] === "EVENT" || data[0] === "NOTICE") { handler(); this.ws?.removeEventListener("message", messageHandler); } } catch { } }; this.ws?.addEventListener("message", messageHandler); } }); if (!isAlive) { this.debug("Probe failed, connection is stale"); this.handleStaleConnection(); } }); this.wsStateMonitor = setInterval(() => { if (this._status === 5) { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.debug("WebSocket died silently, reconnecting"); this.handleStaleConnection(); } } }, 5e3); this.sleepDetector = setInterval(() => { const now2 = Date.now(); const elapsed = now2 - this.lastSleepCheck; if (elapsed > 15e3) { this.debug(`Detected possible sleep/wake (${elapsed}ms gap)`); this.handlePossibleWake(); } this.lastSleepCheck = now2; }, 1e4); } handleStaleConnection() { this._status = 1; this.wasIdle = true; this.onDisconnect(); } handlePossibleWake() { this.debug("System wake detected, checking all connections"); this.wasIdle = true; if (this._status >= 5) { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.handleStaleConnection(); } else { probeRelayConnection3({ send: (msg) => this.send(JSON.stringify(msg)), once: (event, handler) => { const messageHandler = (e2) => { try { const data = JSON.parse(e2.data); if (data[0] === "EOSE" || data[0] === "EVENT" || data[0] === "NOTICE") { handler(); this.ws?.removeEventListener("message", messageHandler); } } catch { } }; this.ws?.addEventListener("message", messageHandler); } }).then((isAlive) => { if (!isAlive) { this.handleStaleConnection(); } }); } } } resetReconnectionState() { this.wasIdle = true; if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = void 0; } } async connect(timeoutMs, reconnect = true) { if (this.ws && this.ws.readyState !== WebSocket.OPEN && this.ws.readyState !== WebSocket.CONNECTING) { this.debug("Cleaning up stale WebSocket connection"); try { this.ws.close(); } catch (e2) { } this.ws = void 0; this._status = 1; } if (this._status !== 2 && this._status !== 1 || this.reconnectTimeout) { this.debug("Relay requested to be connected but was in state %s or it had a reconnect timeout", this._status); return; } if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = void 0; } if (this.connectTimeout) { clearTimeout(this.connectTimeout); this.connectTimeout = void 0; } timeoutMs ?? (timeoutMs = this.timeoutMs); if (!this.timeoutMs && timeoutMs) this.timeoutMs = timeoutMs; if (this.timeoutMs) this.connectTimeout = setTimeout(() => this.onConnectionError(reconnect), this.timeoutMs); try { this.updateConnectionStats.attempt(); if (this._status === 1) this._status = 4; else this._status = 2; this.ws = new WebSocket(this.ndkRelay.url); this.ws.onopen = this.onConnect.bind(this); this.ws.onclose = this.onDisconnect.bind(this); this.ws.onmessage = this.onMessage.bind(this); this.ws.onerror = this.onError.bind(this); } catch (e2) { this.debug(`Failed to connect to ${this.ndkRelay.url}`, e2); this._status = 1; if (reconnect) this.handleReconnection(); else this.ndkRelay.emit("delayed-connect", 2 * 24 * 60 * 60 * 1e3); throw e2; } } disconnect() { this._status = 0; this.keepalive?.stop(); if (this.wsStateMonitor) { clearInterval(this.wsStateMonitor); this.wsStateMonitor = void 0; } if (this.sleepDetector) { clearInterval(this.sleepDetector); this.sleepDetector = void 0; } try { this.ws?.close(); } catch (e2) { this.debug("Failed to disconnect", e2); this._status = 1; } } onConnectionError(reconnect) { this.debug(`Error connecting to ${this.ndkRelay.url}`, this.timeoutMs); if (reconnect && !this.reconnectTimeout) { this.handleReconnection(); } } onConnect() { this.netDebug?.("connected", this.ndkRelay); if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = void 0; } if (this.connectTimeout) { clearTimeout(this.connectTimeout); this.connectTimeout = void 0; } this.updateConnectionStats.connected(); this._status = 5; this.keepalive?.start(); this.wasIdle = false; this.ndkRelay.emit("connect"); this.ndkRelay.emit("ready"); } onDisconnect() { this.netDebug?.("disconnected", this.ndkRelay); this.updateConnectionStats.disconnected(); this.keepalive?.stop(); this.clearPendingPublishes(new Error(`Relay ${this.ndkRelay.url} disconnected`)); if (this._status === 5) { this.handleReconnection(); } this._status = 1; this.ndkRelay.emit("disconnect"); } getEventIdFromMessage(msg) { if (msg.charCodeAt(2) !== 69 || msg.charCodeAt(3) !== 86) { return null; } const idPos = msg.indexOf('"id":"'); if (idPos === -1) { return null; } return msg.substring(idPos + 6, idPos + 70); } onMessage(event) { this.netDebug?.(event.data, this.ndkRelay, "recv"); this.keepalive?.recordActivity(); const msg = event.data; const eventId = this.getEventIdFromMessage(msg); if (eventId && this.ndk) { const seenRelays = this.ndk.subManager.seenEvents.get(eventId); if (seenRelays && seenRelays.length > 0) { this.ndk.subManager.seenEvent(eventId, this.ndkRelay); return; } } try { const data = JSON.parse(event.data); const [cmd, id, ..._rest] = data; const handler = this.ndkRelay.getProtocolHandler(cmd); if (handler) { handler(this.ndkRelay, data); return; } switch (cmd) { case "EVENT": { const so = this.openSubs.get(id); const event2 = data[2]; if (!so) { this.debug(`Received event for unknown subscription ${id}`); return; } so.onevent(event2); return; } case "COUNT": { const payload = data[2]; const cr = this.openCountRequests.get(id); if (cr) { cr.resolve(payload.count); this.openCountRequests.delete(id); } return; } case "EOSE": { const so = this.openSubs.get(id); if (!so) return; so.oneose(id); return; } case "OK": { const ok = data[2]; const reason = data[3]; const ep = this.openEventPublishes.get(id); const firstEp = ep?.pop(); if (!ep || !firstEp) { this.debug("Received OK for unknown event publish", id); return; } if (ok) { firstEp.resolve(reason); this.pendingAuthPublishes.delete(id); } else { const isAuthRequired = reason && (reason.toLowerCase().includes("auth-required") || reason.toLowerCase().includes("not authorized") || reason.toLowerCase().includes("blocked: not authorized")); if (isAuthRequired) { const event2 = this.pendingAuthPublishes.get(id); if (event2) { this.debug("Publish failed due to auth-required, will retry after auth", id); ep.push(firstEp); this.openEventPublishes.set(id, ep); } else { firstEp.reject(new Error(reason)); } } else { firstEp.reject(new Error(reason)); this.pendingAuthPublishes.delete(id); } } if (ep.length === 0) { this.openEventPublishes.delete(id); } else if (!ok && !(reason?.toLowerCase().includes("auth-required") || reason?.toLowerCase().includes("not authorized") || reason?.toLowerCase().includes("blocked: not authorized"))) { this.openEventPublishes.set(id, ep); } return; } case "CLOSED": { const so = this.openSubs.get(id); if (!so) return; so.onclosed(data[2]); return; } case "NOTICE": this.onNotice(data[1]); return; case "AUTH": { this.onAuthRequested(data[1]); return; } } } catch (error) { this.debug(`Error parsing message from ${this.ndkRelay.url}: ${error.message}`, error?.stack); return; } } async onAuthRequested(challenge3) { const authPolicy = this.ndkRelay.authPolicy ?? this.ndk?.relayAuthDefaultPolicy; this.debug("Relay requested authentication", { havePolicy: !!authPolicy }); if (this._status === 7) { this.debug("Already authenticating, ignoring"); return; } this._status = 6; if (authPolicy) { if (this._status >= 5) { this._status = 7; let res; try { res = await authPolicy(this.ndkRelay, challenge3); } catch (e2) { this.debug("Authentication policy threw an error", e2); res = false; } this.debug("Authentication policy returned", !!res); if (res instanceof NDKEvent3 || res === true) { if (res instanceof NDKEvent3) { await this.auth(res); } const authenticate = async () => { if (this._status >= 5 && this._status < 8) { const event = new NDKEvent3(this.ndk); event.kind = 22242; event.tags = [ ["relay", this.ndkRelay.url], ["challenge", challenge3] ]; await event.sign(); this.auth(event).then(() => { this._status = 8; this.ndkRelay.emit("authed"); this.debug("Authentication successful"); this.retryPendingAuthPublishes(); }).catch((e2) => { this._status = 6; this.ndkRelay.emit("auth:failed", e2); this.debug("Authentication failed", e2); this.rejectPendingAuthPublishes(e2); }); } else { this.debug("Authentication failed, it changed status, status is %d", this._status); } }; if (res === true) { if (!this.ndk?.signer) { this.debug("No signer available for authentication localhost"); this.ndk?.once("signer:ready", authenticate); } else { authenticate().catch((e2) => { console.error("Error authenticating", e2); }); } } this._status = 5; this.ndkRelay.emit("authed"); } } } else { this.ndkRelay.emit("auth", challenge3); } } onError(error) { this.debug(`WebSocket error on ${this.ndkRelay.url}:`, error); } get status() { return this._status; } isAvailable() { return this._status === 5; } isFlapping() { const durations = this._connectionStats.durations; if (durations.length % 3 !== 0) return false; const sum = durations.reduce((a, b) => a + b, 0); const avg = sum / durations.length; const variance = durations.map((x2) => (x2 - avg) ** 2).reduce((a, b) => a + b, 0) / durations.length; const stdDev = Math.sqrt(variance); const isFlapping = stdDev < FLAPPING_THRESHOLD_MS3; return isFlapping; } async onNotice(notice) { this.ndkRelay.emit("notice", notice); } handleReconnection(attempt = 0) { if (this.reconnectTimeout) return; if (this.isFlapping()) { this.ndkRelay.emit("flapping", this._connectionStats); this._status = 3; return; } let reconnectDelay; if (this.wasIdle) { const aggressiveDelays = [0, 1e3, 2e3, 5e3, 1e4, 3e4]; reconnectDelay = aggressiveDelays[Math.min(attempt, aggressiveDelays.length - 1)]; this.debug(`Using aggressive reconnect after idle, attempt ${attempt}, delay ${reconnectDelay}ms`); } else if (this.connectedAt) { reconnectDelay = Math.max(0, 6e4 - (Date.now() - this.connectedAt)); } else { reconnectDelay = Math.min(1e3 * 2 ** attempt, 3e4); this.debug(`Using standard backoff, attempt ${attempt}, delay ${reconnectDelay}ms`); } this.reconnectTimeout = setTimeout(() => { this.reconnectTimeout = void 0; this._status = 2; this.connect().catch((_err) => { if (attempt < MAX_RECONNECT_ATTEMPTS3) { this.handleReconnection(attempt + 1); } else { this.debug("Max reconnect attempts reached"); this.wasIdle = false; } }); }, reconnectDelay); this.ndkRelay.emit("delayed-connect", reconnectDelay); this.debug("Reconnecting in", reconnectDelay); this._connectionStats.nextReconnectAt = Date.now() + reconnectDelay; } async send(message) { const idleTime = Date.now() - this.lastMessageSent; if (idleTime > 12e4) { this.wasIdle = true; } if (this._status >= 5 && this.ws?.readyState === WebSocket.OPEN) { this.ws?.send(message); this.netDebug?.(message, this.ndkRelay, "send"); this.lastMessageSent = Date.now(); } else { this.debug(`Not connected to ${this.ndkRelay.url} (%d), not sending message ${message}`, this._status); if (this._status >= 5 && this.ws?.readyState !== WebSocket.OPEN) { this.debug(`Stale connection detected, WebSocket state: ${this.ws?.readyState}`); this.handleStaleConnection(); } } } async auth(event) { const ret = new Promise((resolve, reject) => { const val = this.openEventPublishes.get(event.id) ?? []; val.push({ resolve, reject }); this.openEventPublishes.set(event.id, val); }); this.send(`["AUTH",${JSON.stringify(event.rawEvent())}]`); return ret; } clearPendingPublishes(error) { this.rejectPendingAuthPublishes(error); for (const [eventId, resolvers] of this.openEventPublishes.entries()) { while (resolvers.length > 0) { const resolver = resolvers.shift(); if (resolver) { resolver.reject(error); } } this.openEventPublishes.delete(eventId); } } retryPendingAuthPublishes() { if (this.pendingAuthPublishes.size === 0) return; this.debug(`Retrying ${this.pendingAuthPublishes.size} pending publishes after auth`); for (const [eventId, event] of this.pendingAuthPublishes.entries()) { this.debug(`Retrying publish for event ${eventId}`); this.send(`["EVENT",${JSON.stringify(event)}]`); } this.pendingAuthPublishes.clear(); } rejectPendingAuthPublishes(error) { if (this.pendingAuthPublishes.size === 0) return; this.debug(`Rejecting ${this.pendingAuthPublishes.size} pending publishes due to auth failure`); for (const [eventId] of this.pendingAuthPublishes.entries()) { const ep = this.openEventPublishes.get(eventId); if (ep && ep.length > 0) { const resolver = ep.pop(); if (resolver) { resolver.reject(new Error(`Authentication failed: ${error.message}`)); } if (ep.length === 0) { this.openEventPublishes.delete(eventId); } } } this.pendingAuthPublishes.clear(); } async publish(event) { const ret = new Promise((resolve, reject) => { const val = this.openEventPublishes.get(event.id) ?? []; if (val.length > 0) { console.warn(`Duplicate event publishing detected, you are publishing event ${event.id} twice`); } val.push({ resolve, reject }); this.openEventPublishes.set(event.id, val); }); this.pendingAuthPublishes.set(event.id, event); this.send(`["EVENT",${JSON.stringify(event)}]`); return ret; } async count(filters, params) { this.serial++; const id = params?.id || `count:${this.serial}`; const ret = new Promise((resolve, reject) => { this.openCountRequests.set(id, { resolve, reject }); }); this.send(`["COUNT","${id}",${JSON.stringify(filters).substring(1)}`); return ret; } close(subId, reason) { this.send(`["CLOSE","${subId}"]`); const sub = this.openSubs.get(subId); this.openSubs.delete(subId); if (sub) sub.onclose(reason); } req(relaySub) { `${this.send(`["REQ","${relaySub.subId}",${JSON.stringify(relaySub.executeFilters).substring(1)}`)}]`; this.openSubs.set(relaySub.subId, relaySub); } get connectionStats() { return this._connectionStats; } get url() { return this.ndkRelay.url; } get connected() { return this._status >= 5 && this.ws?.readyState === WebSocket.OPEN; } }; async function fetchRelayInformation22(relayUrl) { const httpUrl = relayUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://"); const response = await fetch(httpUrl, { headers: { Accept: "application/nostr+json" } }); if (!response.ok) { throw new Error(`Failed to fetch relay information: ${response.status} ${response.statusText}`); } const data = await response.json(); return data; } var NDKRelayPublisher3 = class { constructor(ndkRelay) { __publicField(this, "ndkRelay"); __publicField(this, "debug"); this.ndkRelay = ndkRelay; this.debug = ndkRelay.debug.extend("publisher"); } async publish(event, timeoutMs = 2500) { let timeout; const publishConnected = () => { return new Promise((resolve, reject) => { try { this.publishEvent(event).then((_result) => { this.ndkRelay.emit("published", event); event.emit("relay:published", this.ndkRelay); resolve(true); }).catch(reject); } catch (err) { reject(err); } }); }; const timeoutPromise = new Promise((_2, reject) => { timeout = setTimeout(() => { timeout = void 0; reject(new Error(`Timeout: ${timeoutMs}ms`)); }, timeoutMs); }); const onConnectHandler = () => { publishConnected().then((result) => connectResolve(result)).catch((err) => connectReject(err)); }; let connectResolve; let connectReject; const onError = (err) => { this.ndkRelay.debug("Publish failed", err, event.id); this.ndkRelay.emit("publish:failed", event, err); event.emit("relay:publish:failed", this.ndkRelay, err); throw err; }; const onFinally = () => { if (timeout) clearTimeout(timeout); this.ndkRelay.removeListener("connect", onConnectHandler); }; if (this.ndkRelay.status >= 5) { return Promise.race([publishConnected(), timeoutPromise]).catch(onError).finally(onFinally); } if (this.ndkRelay.status <= 1) { console.warn("Relay is disconnected, trying to connect to publish an event", this.ndkRelay.url); this.ndkRelay.connect(); } else { console.warn("Relay not connected, waiting for connection to publish an event", this.ndkRelay.url); } return Promise.race([ new Promise((resolve, reject) => { connectResolve = resolve; connectReject = reject; this.ndkRelay.on("connect", onConnectHandler); }), timeoutPromise ]).catch(onError).finally(onFinally); } async publishEvent(event) { return this.ndkRelay.connectivity.publish(event.rawEvent()); } }; function filterFingerprint3(filters, closeOnEose) { const elements = []; for (const filter of filters) { const keys = Object.entries(filter || {}).map(([key, values]) => { if (["since", "until"].includes(key)) { return `${key}:${values}`; } return key; }).sort().join("-"); elements.push(keys); } let id = closeOnEose ? "+" : ""; id += elements.join("|"); return id; } function mergeFilters3(filters) { const result = []; const lastResult = {}; filters.filter((f) => !!f.limit).forEach((filterWithLimit) => result.push(filterWithLimit)); filters = filters.filter((f) => !f.limit); if (filters.length === 0) return result; filters.forEach((filter) => { Object.entries(filter).forEach(([key, value]) => { if (Array.isArray(value)) { if (lastResult[key] === void 0) { lastResult[key] = [...value]; } else { lastResult[key] = Array.from(/* @__PURE__ */ new Set([...lastResult[key], ...value])); } } else { lastResult[key] = value; } }); }); return [...result, lastResult]; } var MAX_ITEMS3 = 3; function formatArray3(items, formatter) { const formatted = formatter ? items.slice(0, MAX_ITEMS3).map(formatter) : items.slice(0, MAX_ITEMS3); const display = formatted.join(","); return items.length > MAX_ITEMS3 ? `${display}+${items.length - MAX_ITEMS3}` : display; } function formatFilters3(filters) { return filters.map((f) => { const parts = []; if (f.ids?.length) { parts.push(`ids:[${formatArray3(f.ids, (id) => String(id).slice(0, 8))}]`); } if (f.kinds?.length) { parts.push(`kinds:[${formatArray3(f.kinds)}]`); } if (f.authors?.length) { parts.push(`authors:[${formatArray3(f.authors, (a) => String(a).slice(0, 8))}]`); } if (f.since) { parts.push(`since:${f.since}`); } if (f.until) { parts.push(`until:${f.until}`); } if (f.limit) { parts.push(`limit:${f.limit}`); } if (f.search) { parts.push(`search:"${String(f.search).slice(0, 20)}"`); } for (const [key, value] of Object.entries(f)) { if (key.startsWith("#") && Array.isArray(value) && value.length > 0) { parts.push(`${key}:[${formatArray3(value, (v6) => String(v6).slice(0, 8))}]`); } } return `{${parts.join(" ")}}`; }).join(", "); } var NDKRelaySubscription3 = class { constructor(relay, fingerprint, topSubManager) { __publicField(this, "fingerprint"); __publicField(this, "items", /* @__PURE__ */ new Map()); __publicField(this, "topSubManager"); __publicField(this, "debug"); __publicField(this, "status", 0); __publicField(this, "onClose"); __publicField(this, "relay"); __publicField(this, "eosed", false); __publicField(this, "executionTimer"); __publicField(this, "fireTime"); __publicField(this, "delayType"); __publicField(this, "executeFilters"); __publicField(this, "id", Math.random().toString(36).substring(7)); __publicField(this, "_subId"); __publicField(this, "subIdParts", /* @__PURE__ */ new Set()); __publicField(this, "executeOnRelayReady", () => { if (this.status !== 2) return; if (this.items.size === 0) { this.debug("No items to execute; this relay was probably too slow to respond and the caller gave up", { status: this.status, fingerprint: this.fingerprint, id: this.id, subId: this.subId }); this.cleanup(); return; } this.debug("Executing on relay ready", { status: this.status, fingerprint: this.fingerprint, itemsSize: this.items.size, filters: formatFilters3(this.compileFilters()) }); this.status = 1; this.execute(); }); __publicField(this, "reExecuteAfterAuth", (() => { const oldSubId = this.subId; this.debug("Re-executing after auth", this.items.size); if (this.eosed) { this.relay.close(this.subId); } else { this.debug("We are abandoning an opened subscription, once it EOSE's, the handler will close it", { oldSubId }); } this._subId = void 0; this.status = 1; this.execute(); this.debug("Re-executed after auth %s \u{1F449} %s", oldSubId, this.subId); }).bind(this)); this.relay = relay; this.topSubManager = topSubManager; this.debug = relay.debug.extend(`sub[${this.id}]`); this.fingerprint = fingerprint || Math.random().toString(36).substring(7); } get subId() { if (this._subId) return this._subId; this._subId = this.fingerprint.slice(0, 15); return this._subId; } addSubIdPart(part) { this.subIdParts.add(part); } addItem(subscription, filters) { if (this.items.has(subscription.internalId)) { return; } subscription.on("close", this.removeItem.bind(this, subscription)); this.items.set(subscription.internalId, { subscription, filters }); if (this.status !== 3) { if (subscription.subId && (!this._subId || this._subId.length < 25)) { if (this.status === 0 || this.status === 1) { this.addSubIdPart(subscription.subId); } } } switch (this.status) { case 0: this.evaluateExecutionPlan(subscription); break; case 3: break; case 1: this.evaluateExecutionPlan(subscription); break; case 4: this.debug("Subscription is closed, cannot add new items", { filters: formatFilters3(filters), subId: subscription.subId, internalId: subscription.internalId }); throw new Error("Cannot add new items to a closed subscription"); } } removeItem(subscription) { this.items.delete(subscription.internalId); if (this.items.size === 0) { if (this.status === 0 || this.status === 1) { this.status = 4; this.cleanup(); return; } if (!this.eosed) return; this.close(); this.cleanup(); } } close() { if (this.status === 4) return; const prevStatus = this.status; this.status = 4; if (prevStatus === 3) { try { this.relay.close(this.subId); } catch (e2) { this.debug("Error closing subscription", e2, this); } } else { this.debug("Subscription wanted to close but it wasn't running, this is probably ok", { subId: this.subId, prevStatus, sub: this }); } this.cleanup(); } cleanup() { if (this.executionTimer) clearTimeout(this.executionTimer); this.relay.off("ready", this.executeOnRelayReady); this.relay.off("authed", this.reExecuteAfterAuth); if (this.onClose) this.onClose(this); } evaluateExecutionPlan(subscription) { if (!subscription.isGroupable()) { this.status = 1; this.execute(); return; } if (subscription.filters.find((filter) => !!filter.limit)) { this.executeFilters = this.compileFilters(); if (this.executeFilters.length >= 10) { this.status = 1; this.execute(); return; } } const delay = subscription.groupableDelay; const delayType = subscription.groupableDelayType; if (!delay) throw new Error("Cannot group a subscription without a delay"); if (this.status === 0) { this.schedule(delay, delayType); } else { const existingDelayType = this.delayType; const timeUntilFire = this.fireTime - Date.now(); if (existingDelayType === "at-least" && delayType === "at-least") { if (timeUntilFire < delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else if (existingDelayType === "at-least" && delayType === "at-most") { if (timeUntilFire > delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else if (existingDelayType === "at-most" && delayType === "at-most") { if (timeUntilFire > delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else if (existingDelayType === "at-most" && delayType === "at-least") { if (timeUntilFire > delay) { if (this.executionTimer) clearTimeout(this.executionTimer); this.schedule(delay, delayType); } } else { throw new Error(`Unknown delay type combination ${existingDelayType} ${delayType}`); } } } schedule(delay, delayType) { this.status = 1; const currentTime = Date.now(); this.fireTime = currentTime + delay; this.delayType = delayType; const timer = setTimeout(() => { this.execute(); }, delay); if (delayType === "at-least") { this.executionTimer = timer; } } finalizeSubId() { if (this.subIdParts.size > 0) { const parts = Array.from(this.subIdParts).map((part) => part.substring(0, 10)); let joined = parts.join("-"); if (joined.length > 20) { joined = joined.substring(0, 20); } this._subId = joined; } else { this._subId = this.fingerprint.slice(0, 15); } this._subId += `-${Math.random().toString(36).substring(2, 7)}`; } execute() { if (this.status !== 1) { return; } if (!this.relay.connected) { this.status = 2; this.debug("Waiting for relay to be ready", { status: this.status, id: this.subId, fingerprint: this.fingerprint, itemsSize: this.items.size }); this.relay.once("ready", this.executeOnRelayReady); return; } if (this.relay.status < 8) { this.relay.once("authed", this.reExecuteAfterAuth); } this.status = 3; this.finalizeSubId(); this.executeFilters = this.compileFilters(); this.relay.req(this); } onstart() { } onevent(event) { this.topSubManager.dispatchEvent(event, this.relay); } oneose(subId) { this.eosed = true; if (subId !== this.subId) { this.debug("Received EOSE for an abandoned subscription", subId, this.subId); this.relay.close(subId); return; } if (this.items.size === 0) { this.close(); } for (const { subscription } of this.items.values()) { subscription.eoseReceived(this.relay); if (subscription.closeOnEose) { this.removeItem(subscription); } } } onclose(_reason) { this.status = 4; } onclosed(reason) { if (!reason) return; for (const { subscription } of this.items.values()) { subscription.closedReceived(this.relay, reason); } } compileFilters() { const mergedFilters = []; const filters = Array.from(this.items.values()).map((item) => item.filters); if (!filters[0]) { this.debug("\u{1F440} No filters to merge", { itemsSize: this.items.size }); return []; } const filterCount = filters[0].length; for (let i22 = 0; i22 < filterCount; i22++) { const allFiltersAtIndex = filters.map((filter) => filter[i22]); const merged = mergeFilters3(allFiltersAtIndex); mergedFilters.push(...merged); } return mergedFilters; } }; var NDKRelaySubscriptionManager3 = class { constructor(relay, generalSubManager) { __publicField(this, "relay"); __publicField(this, "subscriptions"); __publicField(this, "generalSubManager"); this.relay = relay; this.subscriptions = /* @__PURE__ */ new Map(); this.generalSubManager = generalSubManager; } addSubscription(sub, filters) { let relaySub; if (!sub.isGroupable()) { relaySub = this.createSubscription(sub, filters); } else { const filterFp = filterFingerprint3(filters, sub.closeOnEose); if (filterFp) { const existingSubs = this.subscriptions.get(filterFp); relaySub = (existingSubs || []).find((sub2) => sub2.status < 3); } relaySub ?? (relaySub = this.createSubscription(sub, filters, filterFp)); } relaySub.addItem(sub, filters); } createSubscription(_sub, _filters, fingerprint) { const relaySub = new NDKRelaySubscription3(this.relay, fingerprint || null, this.generalSubManager); relaySub.onClose = this.onRelaySubscriptionClose.bind(this); const currentVal = this.subscriptions.get(relaySub.fingerprint) ?? []; this.subscriptions.set(relaySub.fingerprint, [...currentVal, relaySub]); return relaySub; } onRelaySubscriptionClose(sub) { let currentVal = this.subscriptions.get(sub.fingerprint) ?? []; if (!currentVal) { console.warn("Unexpectedly did not find a subscription with fingerprint", sub.fingerprint); } else if (currentVal.length === 1) { this.subscriptions.delete(sub.fingerprint); } else { currentVal = currentVal.filter((s) => s.id !== sub.id); this.subscriptions.set(sub.fingerprint, currentVal); } } }; var _a37; var NDKRelay3 = (_a37 = class extends import_tseep22.EventEmitter { constructor(url, authPolicy, ndk) { super(); __publicField(this, "url"); __publicField(this, "scores"); __publicField(this, "connectivity"); __publicField(this, "subs"); __publicField(this, "publisher"); __publicField(this, "authPolicy"); __publicField(this, "protocolHandlers", /* @__PURE__ */ new Map()); __publicField(this, "_relayInfo"); __publicField(this, "lowestValidationRatio"); __publicField(this, "targetValidationRatio"); __publicField(this, "validationRatioFn"); __publicField(this, "validatedEventCount", 0); __publicField(this, "nonValidatedEventCount", 0); __publicField(this, "trusted", false); __publicField(this, "complaining", false); __publicField(this, "debug"); __publicField(this, "req"); __publicField(this, "close"); this.url = normalizeRelayUrl3(url); this.scores = /* @__PURE__ */ new Map(); this.debug = import_debug29.default(`ndk:relay:${url}`); this.connectivity = new NDKRelayConnectivity3(this, ndk); this.connectivity.netDebug = ndk?.netDebug; this.req = this.connectivity.req.bind(this.connectivity); this.close = this.connectivity.close.bind(this.connectivity); this.subs = new NDKRelaySubscriptionManager3(this, ndk.subManager); this.publisher = new NDKRelayPublisher3(this); this.authPolicy = authPolicy; this.targetValidationRatio = ndk?.initialValidationRatio; this.lowestValidationRatio = ndk?.lowestValidationRatio; this.validationRatioFn = (ndk?.validationRatioFn ?? _a37.defaultValidationRatioUpdateFn).bind(this); this.updateValidationRatio(); if (!ndk) { console.trace("relay created without ndk"); } } updateValidationRatio() { if (this.validationRatioFn && this.validatedEventCount > 0) { const newRatio = this.validationRatioFn(this, this.validatedEventCount, this.nonValidatedEventCount); this.targetValidationRatio = newRatio; } setTimeout(() => { this.updateValidationRatio(); }, 3e4); } get status() { return this.connectivity.status; } get connectionStats() { return this.connectivity.connectionStats; } async connect(timeoutMs, reconnect = true) { return this.connectivity.connect(timeoutMs, reconnect); } disconnect() { if (this.status === 1) { return; } this.connectivity.disconnect(); } subscribe(subscription, filters) { this.subs.addSubscription(subscription, filters); } async publish(event, timeoutMs = 2500) { return this.publisher.publish(event, timeoutMs); } referenceTags() { return [["r", this.url]]; } addValidatedEvent() { this.validatedEventCount++; } addNonValidatedEvent() { this.nonValidatedEventCount++; } get validationRatio() { if (this.nonValidatedEventCount === 0) { return 1; } return this.validatedEventCount / (this.validatedEventCount + this.nonValidatedEventCount); } shouldValidateEvent() { if (this.trusted) { return false; } if (this.targetValidationRatio === void 0) { return true; } if (this.targetValidationRatio >= 1) return true; return Math.random() < this.targetValidationRatio; } get connected() { return this.connectivity.connected; } registerProtocolHandler(messageType, handler) { this.protocolHandlers.set(messageType, handler); } unregisterProtocolHandler(messageType) { this.protocolHandlers.delete(messageType); } getProtocolHandler(messageType) { return this.protocolHandlers.get(messageType); } async fetchInfo(force = false) { const MAX_AGE = 864e5; const ndk = this.connectivity.ndk; if (!force && ndk?.cacheAdapter?.getRelayStatus) { const cached = await ndk.cacheAdapter.getRelayStatus(this.url); if (cached?.nip11 && Date.now() - cached.nip11.fetchedAt < MAX_AGE) { this._relayInfo = cached.nip11.data; return cached.nip11.data; } } if (!force && this._relayInfo) { return this._relayInfo; } this._relayInfo = await fetchRelayInformation22(this.url); if (ndk?.cacheAdapter?.updateRelayStatus) { await ndk.cacheAdapter.updateRelayStatus(this.url, { nip11: { data: this._relayInfo, fetchedAt: Date.now() } }); } return this._relayInfo; } get info() { return this._relayInfo; } }, __publicField(_a37, "defaultValidationRatioUpdateFn", (relay, validatedCount, _nonValidatedCount) => { if (relay.lowestValidationRatio === void 0 || relay.targetValidationRatio === void 0) return 1; let newRatio = relay.validationRatio; if (relay.validationRatio > relay.targetValidationRatio) { const factor = validatedCount / 100; newRatio = Math.max(relay.lowestValidationRatio, relay.validationRatio - factor); } if (newRatio < relay.validationRatio) { return newRatio; } return relay.validationRatio; }), _a37); var NDKPublishError3 = class extends Error { constructor(message, errors, publishedToRelays, intendedRelaySet) { super(message); __publicField(this, "errors"); __publicField(this, "publishedToRelays"); __publicField(this, "intendedRelaySet"); this.errors = errors; this.publishedToRelays = publishedToRelays; this.intendedRelaySet = intendedRelaySet; } get relayErrors() { const errors = []; for (const [relay, err] of this.errors) { errors.push(`${relay.url}: ${err}`); } return errors.join(` `); } }; var NDKRelaySet3 = class _NDKRelaySet2 { constructor(relays, ndk, pool) { __publicField(this, "relays"); __publicField(this, "debug"); __publicField(this, "ndk"); __publicField(this, "pool"); this.relays = relays; this.ndk = ndk; this.pool = pool ?? ndk.pool; this.debug = ndk.debug.extend("relayset"); } addRelay(relay) { this.relays.add(relay); } get relayUrls() { return Array.from(this.relays).map((r) => r.url); } static fromRelayUrls(relayUrls, ndk, connect = true, pool) { pool = pool ?? ndk.pool; if (!pool) throw new Error("No pool provided"); const relays = /* @__PURE__ */ new Set(); for (const url of relayUrls) { const relay = pool.relays.get(normalizeRelayUrl3(url)); if (relay) { if (relay.status < 5 && connect) { relay.connect(); } relays.add(relay); } else { const temporaryRelay = new NDKRelay3(normalizeRelayUrl3(url), ndk?.relayAuthDefaultPolicy, ndk); pool.useTemporaryRelay(temporaryRelay, void 0, `requested from fromRelayUrls ${relayUrls}`); relays.add(temporaryRelay); } } return new _NDKRelaySet2(new Set(relays), ndk, pool); } async publish(event, timeoutMs, requiredRelayCount = 1) { const publishedToRelays = /* @__PURE__ */ new Set(); const errors = /* @__PURE__ */ new Map(); const isEphemeral22 = event.isEphemeral(); event.publishStatus = "pending"; const relayPublishedHandler = (relay) => { publishedToRelays.add(relay); }; event.on("relay:published", relayPublishedHandler); try { const promises = Array.from(this.relays).map((relay) => { return new Promise((resolve) => { const timeoutId = timeoutMs ? setTimeout(() => { if (!publishedToRelays.has(relay)) { errors.set(relay, new Error(`Publish timeout after ${timeoutMs}ms`)); resolve(false); } }, timeoutMs) : null; relay.publish(event, timeoutMs).then((success) => { if (timeoutId) clearTimeout(timeoutId); if (success) { publishedToRelays.add(relay); resolve(true); } else { resolve(false); } }).catch((err) => { if (timeoutId) clearTimeout(timeoutId); if (!isEphemeral22) { errors.set(relay, err); } resolve(false); }); }); }); await Promise.all(promises); if (publishedToRelays.size < requiredRelayCount) { if (!isEphemeral22) { const error = new NDKPublishError3("Not enough relays received the event (" + publishedToRelays.size + " published, " + requiredRelayCount + " required)", errors, publishedToRelays, this); event.publishStatus = "error"; event.publishError = error; this.ndk?.emit("event:publish-failed", event, error, this.relayUrls); throw error; } } else { event.publishStatus = "success"; event.emit("published", { relaySet: this, publishedToRelays }); } return publishedToRelays; } finally { event.off("relay:published", relayPublishedHandler); } } get size() { return this.relays.size; } }; var d6 = import_debug28.default("ndk:outbox:calculate"); async function calculateRelaySetFromEvent3(ndk, event, requiredRelayCount) { const relays = /* @__PURE__ */ new Set(); const authorWriteRelays = await getWriteRelaysFor3(ndk, event.pubkey); if (authorWriteRelays) { authorWriteRelays.forEach((relayUrl) => { const relay = ndk.pool?.getRelay(relayUrl); if (relay) relays.add(relay); }); } let relayHints = event.tags.filter((tag) => ["a", "e"].includes(tag[0])).map((tag) => tag[2]).filter((url) => url?.startsWith("wss://")).filter((url) => { try { new URL(url); return true; } catch { return false; } }).map((url) => normalizeRelayUrl3(url)); relayHints = Array.from(new Set(relayHints)).slice(0, 5); relayHints.forEach((relayUrl) => { const relay = ndk.pool?.getRelay(relayUrl, true, true); if (relay) { d6("Adding relay hint %s", relayUrl); relays.add(relay); } }); const pTags = event.getMatchingTags("p").map((tag) => tag[1]); if (pTags.length < 5) { const pTaggedRelays = Array.from(chooseRelayCombinationForPubkeys3(ndk, pTags, "read", { preferredRelays: new Set(authorWriteRelays) }).keys()); pTaggedRelays.forEach((relayUrl) => { const relay = ndk.pool?.getRelay(relayUrl, false, true); if (relay) { d6("Adding p-tagged relay %s", relayUrl); relays.add(relay); } }); } else { d6("Too many p-tags to consider %d", pTags.length); } ndk.pool?.permanentAndConnectedRelays().forEach((relay) => relays.add(relay)); if (requiredRelayCount && relays.size < requiredRelayCount) { const explicitRelays = ndk.explicitRelayUrls?.filter((url) => !Array.from(relays).some((r) => r.url === url)).slice(0, requiredRelayCount - relays.size); explicitRelays?.forEach((url) => { const relay = ndk.pool?.getRelay(url, false, true); if (relay) { d6("Adding explicit relay %s", url); relays.add(relay); } }); } return new NDKRelaySet3(relays, ndk); } function calculateRelaySetsFromFilter2(ndk, filters, pool, relayGoalPerAuthor) { const result = /* @__PURE__ */ new Map(); const authors = /* @__PURE__ */ new Set(); filters.forEach((filter) => { if (filter.authors) { filter.authors.forEach((author) => authors.add(author)); } }); if (authors.size > 0) { const authorToRelaysMap = getRelaysForFilterWithAuthors2(ndk, Array.from(authors), relayGoalPerAuthor); for (const relayUrl of authorToRelaysMap.keys()) { result.set(relayUrl, []); } for (const filter of filters) { if (filter.authors) { for (const [relayUrl, authors2] of authorToRelaysMap.entries()) { const authorFilterAndRelayPubkeyIntersection = filter.authors.filter((author) => authors2.includes(author)); result.set(relayUrl, [ ...result.get(relayUrl), { ...filter, authors: authorFilterAndRelayPubkeyIntersection } ]); } } else { for (const relayUrl of authorToRelaysMap.keys()) { result.set(relayUrl, [...result.get(relayUrl), filter]); } } } } else { if (ndk.explicitRelayUrls) { ndk.explicitRelayUrls.forEach((relayUrl) => { result.set(relayUrl, filters); }); } } if (result.size === 0) { pool.permanentAndConnectedRelays().slice(0, 5).forEach((relay) => { result.set(relay.url, filters); }); } return result; } function calculateRelaySetsFromFilters2(ndk, filters, pool, relayGoalPerAuthor) { const a = calculateRelaySetsFromFilter2(ndk, filters, pool, relayGoalPerAuthor); return a; } function isValidHex643(value) { if (typeof value !== "string" || value.length !== 64) { return false; } for (let i22 = 0; i22 < 64; i22++) { const c = value.charCodeAt(i22); if (!(c >= 48 && c <= 57 || c >= 97 && c <= 102 || c >= 65 && c <= 70)) { return false; } } return true; } function isValidPubkey3(pubkey) { return isValidHex643(pubkey); } function isValidNip052(input) { if (typeof input !== "string") { return false; } for (let i22 = 0; i22 < input.length; i22++) { if (input.charCodeAt(i22) === 46) { return true; } } return false; } function mergeTags3(tags1, tags2) { const tagMap = /* @__PURE__ */ new Map(); const generateKey = (tag) => tag.join(","); const isContained = (smaller, larger) => { return smaller.every((value, index) => value === larger[index]); }; const processTag = (tag) => { for (const [key, existingTag] of tagMap) { if (isContained(existingTag, tag) || isContained(tag, existingTag)) { if (tag.length >= existingTag.length) { tagMap.set(key, tag); } return; } } tagMap.set(generateKey(tag), tag); }; tags1.concat(tags2).forEach(processTag); return Array.from(tagMap.values()); } var hashtagRegex3 = /(?<=\s|^)(#[^\s!@#$%^&*()=+./,[{\]};:'"?><]+)/g; function generateHashtags3(content) { const hashtags = content.match(hashtagRegex3); const tagIds = /* @__PURE__ */ new Set(); const tag = /* @__PURE__ */ new Set(); if (hashtags) { for (const hashtag of hashtags) { if (tagIds.has(hashtag.slice(1))) continue; tag.add(hashtag.slice(1)); tagIds.add(hashtag.slice(1)); } } return Array.from(tag); } async function generateContentTags3(content, tags = [], opts, ctx) { if (opts?.skipContentTagging) { return { content, tags }; } const tagRegex = /(@|nostr:)(npub|nprofile|note|nevent|naddr)[a-zA-Z0-9]+/g; const promises = []; const addTagIfNew = (t) => { if (!tags.find((t2) => ["q", t[0]].includes(t2[0]) && t2[1] === t[1])) { tags.push(t); } }; content = content.replace(tagRegex, (tag) => { try { const entity = tag.split(/(@|nostr:)/)[2]; const { type, data } = nip19_exports3.decode(entity); let t; if (opts?.filters) { const shouldInclude = !opts.filters.includeTypes || opts.filters.includeTypes.includes(type); const shouldExclude = opts.filters.excludeTypes?.includes(type); if (!shouldInclude || shouldExclude) { return tag; } } switch (type) { case "npub": if (opts?.pTags !== false) { t = ["p", data]; } break; case "nprofile": if (opts?.pTags !== false) { t = ["p", data.pubkey]; } break; case "note": promises.push(new Promise(async (resolve) => { const relay = await maybeGetEventRelayUrl3(entity); addTagIfNew(["q", data, relay]); resolve(); })); break; case "nevent": promises.push(new Promise(async (resolve) => { const { id, author } = data; let { relays } = data; if (!relays || relays.length === 0) { relays = [await maybeGetEventRelayUrl3(entity)]; } addTagIfNew(["q", id, relays[0]]); if (author && opts?.pTags !== false && opts?.pTagOnQTags !== false) addTagIfNew(["p", author]); resolve(); })); break; case "naddr": promises.push(new Promise(async (resolve) => { const id = [data.kind, data.pubkey, data.identifier].join(":"); let relays = data.relays ?? []; if (relays.length === 0) { relays = [await maybeGetEventRelayUrl3(entity)]; } addTagIfNew(["q", id, relays[0]]); if (opts?.pTags !== false && opts?.pTagOnQTags !== false && opts?.pTagOnATags !== false) addTagIfNew(["p", data.pubkey]); resolve(); })); break; default: return tag; } if (t) addTagIfNew(t); return `nostr:${entity}`; } catch (_error) { return tag; } }); await Promise.all(promises); if (!opts?.filters?.excludeTypes?.includes("hashtag")) { const newTags = generateHashtags3(content).map((hashtag) => ["t", hashtag]); tags = mergeTags3(tags, newTags); } if (opts?.pTags !== false && opts?.copyPTagsFromTarget && ctx) { const pTags = ctx.getMatchingTags("p"); for (const pTag of pTags) { if (!pTag[1] || !isValidPubkey3(pTag[1])) continue; if (!tags.find((t) => t[0] === "p" && t[1] === pTag[1])) { tags.push(pTag); } } } return { content, tags }; } async function maybeGetEventRelayUrl3(_nip19Id) { return ""; } async function encrypt42(recipient, signer, scheme = "nip44") { let encrypted; if (!this.ndk) throw new Error("No NDK instance found!"); let currentSigner = signer; if (!currentSigner) { this.ndk.assertSigner(); currentSigner = this.ndk.signer; } if (!currentSigner) throw new Error("no NDK signer"); const currentRecipient = recipient || (() => { const pTags = this.getMatchingTags("p"); if (pTags.length !== 1) { throw new Error("No recipient could be determined and no explicit recipient was provided"); } return this.ndk.getUser({ pubkey: pTags[0][1] }); })(); if (scheme === "nip44" && await isEncryptionEnabled3(currentSigner, "nip44")) { encrypted = await currentSigner.encrypt(currentRecipient, this.content, "nip44"); } if ((!encrypted || scheme === "nip04") && await isEncryptionEnabled3(currentSigner, "nip04")) { encrypted = await currentSigner.encrypt(currentRecipient, this.content, "nip04"); } if (!encrypted) throw new Error("Failed to encrypt event."); this.content = encrypted; } async function decrypt42(sender, signer, scheme) { if (this.ndk?.cacheAdapter?.getDecryptedEvent) { const cachedEvent = await this.ndk.cacheAdapter.getDecryptedEvent(this.id); if (cachedEvent) { this.content = cachedEvent.content; return; } } let decrypted; if (!this.ndk) throw new Error("No NDK instance found!"); let currentSigner = signer; if (!currentSigner) { this.ndk.assertSigner(); currentSigner = this.ndk.signer; } if (!currentSigner) throw new Error("no NDK signer"); const currentSender = sender || this.author; if (!currentSender) throw new Error("No sender provided and no author available"); const currentScheme = scheme || (this.content.match(/\\?iv=/) ? "nip04" : "nip44"); if ((currentScheme === "nip04" || this.kind === 4) && await isEncryptionEnabled3(currentSigner, "nip04") && this.content.search("\\?iv=")) { decrypted = await currentSigner.decrypt(currentSender, this.content, "nip04"); } if (!decrypted && currentScheme === "nip44" && await isEncryptionEnabled3(currentSigner, "nip44")) { decrypted = await currentSigner.decrypt(currentSender, this.content, "nip44"); } if (!decrypted) throw new Error("Failed to decrypt event."); this.content = decrypted; if (this.ndk?.cacheAdapter?.addDecryptedEvent) { this.ndk.cacheAdapter.addDecryptedEvent(this.id, this); } } async function isEncryptionEnabled3(signer, scheme) { if (!signer.encryptionEnabled) return false; if (!scheme) return true; return Boolean(await signer.encryptionEnabled(scheme)); } function eventHasETagMarkers3(event) { for (const tag of event.tags) { if (tag[0] === "e" && (tag[3] ?? "").length > 0) return true; } return false; } function getRootTag3(event, searchTag) { searchTag ?? (searchTag = event.tagType()); const rootEventTag = event.tags.find(isTagRootTag3); if (!rootEventTag) { if (eventHasETagMarkers3(event)) return; const matchingTags = event.getMatchingTags(searchTag); if (matchingTags.length < 3) return matchingTags[0]; } return rootEventTag; } var nip22RootTags3 = /* @__PURE__ */ new Set(["A", "E", "I"]); var nip22ReplyTags3 = /* @__PURE__ */ new Set(["a", "e", "i"]); function getReplyTag3(event, searchTag) { if (event.kind === 1111) { let replyTag2; for (const tag of event.tags) { if (nip22RootTags3.has(tag[0])) replyTag2 = tag; else if (nip22ReplyTags3.has(tag[0])) { replyTag2 = tag; break; } } return replyTag2; } searchTag ?? (searchTag = event.tagType()); let hasMarkers2 = false; let replyTag; for (const tag of event.tags) { if (tag[0] !== searchTag) continue; if ((tag[3] ?? "").length > 0) hasMarkers2 = true; if (hasMarkers2 && tag[3] === "reply") return tag; if (hasMarkers2 && tag[3] === "root") replyTag = tag; if (!hasMarkers2) replyTag = tag; } return replyTag; } function isTagRootTag3(tag) { return tag[0] === "E" || tag[3] === "root"; } async function fetchTaggedEvent3(tag, marker) { if (!this.ndk) throw new Error("NDK instance not found"); const t = this.getMatchingTags(tag, marker); if (t.length === 0) return; const [_2, id, hint] = t[0]; const relay = hint !== "" ? this.ndk.pool.getRelay(hint) : void 0; const event = await this.ndk.fetchEvent(id, {}, relay); return event; } async function fetchRootEvent3(subOpts) { if (!this.ndk) throw new Error("NDK instance not found"); const rootTag = getRootTag3(this); if (!rootTag) return; return this.ndk.fetchEventFromTag(rootTag, this, subOpts); } async function fetchReplyEvent3(subOpts) { if (!this.ndk) throw new Error("NDK instance not found"); const replyTag = getReplyTag3(this); if (!replyTag) return; return this.ndk.fetchEventFromTag(replyTag, this, subOpts); } function isReplaceable3() { if (this.kind === void 0) throw new Error("Kind not set"); return [0, 3].includes(this.kind) || this.kind >= 1e4 && this.kind < 2e4 || this.kind >= 3e4 && this.kind < 4e4; } function isEphemeral3() { if (this.kind === void 0) throw new Error("Kind not set"); return this.kind >= 2e4 && this.kind < 3e4; } function isParamReplaceable3() { if (this.kind === void 0) throw new Error("Kind not set"); return this.kind >= 3e4 && this.kind < 4e4; } var DEFAULT_RELAY_COUNT3 = 2; function encode3(maxRelayCount = DEFAULT_RELAY_COUNT3) { let relays = []; if (this.onRelays.length > 0) { relays = this.onRelays.map((relay) => relay.url); } else if (this.relay) { relays = [this.relay.url]; } if (relays.length > maxRelayCount) { relays = relays.slice(0, maxRelayCount); } if (this.isParamReplaceable()) { return nip19_exports3.naddrEncode({ kind: this.kind, pubkey: this.pubkey, identifier: this.replaceableDTag(), relays }); } if (relays.length > 0) { return nip19_exports3.neventEncode({ id: this.tagId(), relays, author: this.pubkey }); } return nip19_exports3.noteEncode(this.tagId()); } async function repost3(publish = true, signer) { if (!signer && publish) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); signer = this.ndk.signer; } const e2 = new NDKEvent3(this.ndk, { kind: getKind3(this) }); if (!this.isProtected) e2.content = JSON.stringify(this.rawEvent()); e2.tag(this); if (this.kind !== 1) { e2.tags.push(["k", `${this.kind}`]); } if (signer) await e2.sign(signer); if (publish) await e2.publish(); return e2; } function getKind3(event) { if (event.kind === 1) { return 6; } return 16; } function getEventDetails3(event) { if ("inspect" in event && typeof event.inspect === "string") { return event.inspect; } return JSON.stringify(event); } function validateForSerialization3(event) { if (typeof event.kind !== "number") { throw new Error(`Can't serialize event with invalid properties: kind (must be number, got ${typeof event.kind}). Event: ${getEventDetails3(event)}`); } if (typeof event.content !== "string") { throw new Error(`Can't serialize event with invalid properties: content (must be string, got ${typeof event.content}). Event: ${getEventDetails3(event)}`); } if (typeof event.created_at !== "number") { throw new Error(`Can't serialize event with invalid properties: created_at (must be number, got ${typeof event.created_at}). Event: ${getEventDetails3(event)}`); } if (typeof event.pubkey !== "string") { throw new Error(`Can't serialize event with invalid properties: pubkey (must be string, got ${typeof event.pubkey}). Event: ${getEventDetails3(event)}`); } if (!Array.isArray(event.tags)) { throw new Error(`Can't serialize event with invalid properties: tags (must be array, got ${typeof event.tags}). Event: ${getEventDetails3(event)}`); } for (let i22 = 0; i22 < event.tags.length; i22++) { const tag = event.tags[i22]; if (!Array.isArray(tag)) { throw new Error(`Can't serialize event with invalid properties: tags[${i22}] (must be array, got ${typeof tag}). Event: ${getEventDetails3(event)}`); } for (let j2 = 0; j2 < tag.length; j2++) { if (typeof tag[j2] !== "string") { throw new Error(`Can't serialize event with invalid properties: tags[${i22}][${j2}] (must be string, got ${typeof tag[j2]}). Event: ${getEventDetails3(event)}`); } } } } function serialize3(includeSig = false, includeId = false) { validateForSerialization3(this); const payload = [0, this.pubkey, this.created_at, this.kind, this.tags, this.content]; if (includeSig) payload.push(this.sig); if (includeId) payload.push(this.id); return JSON.stringify(payload); } function deserialize3(serializedEvent) { const eventArray = JSON.parse(serializedEvent); const ret = { pubkey: eventArray[1], created_at: eventArray[2], kind: eventArray[3], tags: eventArray[4], content: eventArray[5] }; if (eventArray.length >= 7) { const first = eventArray[6]; const second = eventArray[7]; if (first && first.length === 128) { ret.sig = first; if (second && second.length === 64) { ret.id = second; } } else if (first && first.length === 64) { ret.id = first; if (second && second.length === 128) { ret.sig = second; } } } return ret; } var worker3; var processingQueue3 = {}; function signatureVerificationInit2(w2) { worker3 = w2; worker3.onmessage = (msg) => { if (!Array.isArray(msg.data) || msg.data.length !== 2) { console.error("[NDK] \u274C Signature verification worker received incompatible message format.", ` \u{1F4CB} Expected format: [eventId, boolean]`, ` \u{1F4E6} Received:`, msg.data, ` \u{1F50D} This likely means:`, ` 1. You have a STALE worker.js file that needs updating`, ` 2. Version mismatch between @nostr-dev-kit/ndk and deployed worker`, ` 3. Wrong worker is being used for signature verification`, ` \u2705 Solution: Update your worker files:`, ` cp node_modules/@nostr-dev-kit/ndk/dist/workers/sig-verification.js public/`, ` cp node_modules/@nostr-dev-kit/cache-sqlite-wasm/dist/worker.js public/`, ` \u{1F4A1} Or use Vite/bundler imports instead of static files:`, ` import SigWorker from "@nostr-dev-kit/ndk/workers/sig-verification?worker"`); return; } const [eventId, result] = msg.data; const record = processingQueue3[eventId]; if (!record) { console.error("No record found for event", eventId); return; } delete processingQueue3[eventId]; for (const resolve of record.resolves) { resolve(result); } }; } async function verifySignatureAsync3(event, _persist, relay) { const ndkInstance = event.ndk; const start = Date.now(); let result; if (ndkInstance.signatureVerificationFunction) { result = await ndkInstance.signatureVerificationFunction(event); } else { result = await new Promise((resolve) => { const serialized = event.serialize(); let enqueue = false; if (!processingQueue3[event.id]) { processingQueue3[event.id] = { event, resolves: [], relay }; enqueue = true; } processingQueue3[event.id].resolves.push(resolve); if (!enqueue) return; worker3?.postMessage({ serialized, id: event.id, sig: event.sig, pubkey: event.pubkey }); }); } ndkInstance.signatureVerificationTimeMs += Date.now() - start; return result; } var PUBKEY_REGEX3 = /^[a-f0-9]{64}$/; function validate3() { if (typeof this.kind !== "number") return false; if (typeof this.content !== "string") return false; if (typeof this.created_at !== "number") return false; if (typeof this.pubkey !== "string") return false; if (!this.pubkey.match(PUBKEY_REGEX3)) return false; if (!Array.isArray(this.tags)) return false; for (let i22 = 0; i22 < this.tags.length; i22++) { const tag = this.tags[i22]; if (!Array.isArray(tag)) return false; for (let j2 = 0; j2 < tag.length; j2++) { if (typeof tag[j2] === "object") return false; } } return true; } var verifiedSignatures3 = new import_typescript_lru_cache8.LRUCache({ maxSize: 1e3, entryExpirationTimeInMS: 6e4 }); function verifySignature3(persist) { if (typeof this.signatureVerified === "boolean") return this.signatureVerified; const prevVerification = verifiedSignatures3.get(this.id); if (prevVerification !== null) { this.signatureVerified = !!prevVerification; return this.signatureVerified; } try { if (this.ndk?.asyncSigVerification) { const relayForVerification = this.relay; verifySignatureAsync3(this, persist, relayForVerification).then((result) => { if (persist) { this.signatureVerified = result; if (result) verifiedSignatures3.set(this.id, this.sig); } if (!result) { if (relayForVerification) { this.ndk?.reportInvalidSignature(this, relayForVerification); } else { this.ndk?.reportInvalidSignature(this); } verifiedSignatures3.set(this.id, false); } else { if (relayForVerification) { relayForVerification.addValidatedEvent(); } } }).catch((err) => { console.error("signature verification error", this.id, err); }); } else { const hash3 = sha2564(new TextEncoder().encode(this.serialize())); const res = schnorr22.verify(this.sig, hash3, this.pubkey); if (res) verifiedSignatures3.set(this.id, this.sig); else verifiedSignatures3.set(this.id, false); this.signatureVerified = res; return res; } } catch (_err) { this.signatureVerified = false; return false; } } function getEventHash23() { return getEventHashFromSerializedEvent3(this.serialize()); } function getEventHashFromSerializedEvent3(serializedEvent) { const eventHash = sha2564(new TextEncoder().encode(serializedEvent)); return bytesToHex4(eventHash); } var skipClientTagOnKinds3 = /* @__PURE__ */ new Set([ 0, 4, 1059, 13, 3, 9734, 5 ]); var NDKEvent3 = class _NDKEvent2 extends import_tseep19.EventEmitter { constructor(ndk, event) { super(); __publicField(this, "ndk"); __publicField(this, "created_at"); __publicField(this, "content", ""); __publicField(this, "tags", []); __publicField(this, "kind"); __publicField(this, "id", ""); __publicField(this, "sig"); __publicField(this, "pubkey", ""); __publicField(this, "signatureVerified"); __publicField(this, "_author"); __publicField(this, "relay"); __publicField(this, "publishStatus", "success"); __publicField(this, "publishError"); __publicField(this, "serialize", serialize3.bind(this)); __publicField(this, "getEventHash", getEventHash23.bind(this)); __publicField(this, "validate", validate3.bind(this)); __publicField(this, "verifySignature", verifySignature3.bind(this)); __publicField(this, "isReplaceable", isReplaceable3.bind(this)); __publicField(this, "isEphemeral", isEphemeral3.bind(this)); __publicField(this, "isDvm", () => this.kind && this.kind >= 5e3 && this.kind <= 7e3); __publicField(this, "isParamReplaceable", isParamReplaceable3.bind(this)); __publicField(this, "encode", encode3.bind(this)); __publicField(this, "encrypt", encrypt42.bind(this)); __publicField(this, "decrypt", decrypt42.bind(this)); __publicField(this, "fetchTaggedEvent", fetchTaggedEvent3.bind(this)); __publicField(this, "fetchRootEvent", fetchRootEvent3.bind(this)); __publicField(this, "fetchReplyEvent", fetchReplyEvent3.bind(this)); __publicField(this, "repost", repost3.bind(this)); this.ndk = ndk; this.created_at = event?.created_at; this.content = event?.content || ""; this.tags = event?.tags || []; this.id = event?.id || ""; this.sig = event?.sig; this.pubkey = event?.pubkey || ""; this.kind = event?.kind; if (event instanceof _NDKEvent2) { if (this.relay) { this.relay = event.relay; this.ndk?.subManager.seenEvent(event.id, this.relay); } this.publishStatus = event.publishStatus; this.publishError = event.publishError; } } get onRelays() { let res = []; if (!this.ndk) { if (this.relay) res.push(this.relay); } else { res = this.ndk.subManager.seenEvents.get(this.id) || []; } return res; } static deserialize(ndk, event) { return new _NDKEvent2(ndk, deserialize3(event)); } rawEvent() { return { created_at: this.created_at, content: this.content, tags: this.tags, kind: this.kind, pubkey: this.pubkey, id: this.id, sig: this.sig }; } set author(user) { var _a72; this.pubkey = user.pubkey; this._author = user; (_a72 = this._author).ndk ?? (_a72.ndk = this.ndk); } get author() { if (this._author) return this._author; if (!this.ndk) throw new Error("No NDK instance found"); const user = this.ndk.getUser({ pubkey: this.pubkey }); this._author = user; return user; } tagExternal(entity, type, markerUrl) { const iTag = ["i"]; const kTag = ["k"]; switch (type) { case "url": { const url = new URL(entity); url.hash = ""; iTag.push(url.toString()); kTag.push(`${url.protocol}//${url.host}`); break; } case "hashtag": iTag.push(`#${entity.toLowerCase()}`); kTag.push("#"); break; case "geohash": iTag.push(`geo:${entity.toLowerCase()}`); kTag.push("geo"); break; case "isbn": iTag.push(`isbn:${entity.replace(/-/g, "")}`); kTag.push("isbn"); break; case "podcast:guid": iTag.push(`podcast:guid:${entity}`); kTag.push("podcast:guid"); break; case "podcast:item:guid": iTag.push(`podcast:item:guid:${entity}`); kTag.push("podcast:item:guid"); break; case "podcast:publisher:guid": iTag.push(`podcast:publisher:guid:${entity}`); kTag.push("podcast:publisher:guid"); break; case "isan": iTag.push(`isan:${entity.split("-").slice(0, 4).join("-")}`); kTag.push("isan"); break; case "doi": iTag.push(`doi:${entity.toLowerCase()}`); kTag.push("doi"); break; default: throw new Error(`Unsupported NIP-73 entity type: ${type}`); } if (markerUrl) { iTag.push(markerUrl); } this.tags.push(iTag); this.tags.push(kTag); } tag(target, marker, skipAuthorTag, forceTag, opts) { let tags = []; const isNDKUser = target.fetchProfile !== void 0; if (isNDKUser) { forceTag ?? (forceTag = "p"); if (forceTag === "p" && opts?.pTags === false) { return; } const tag = [forceTag, target.pubkey]; if (marker) tag.push(...["", marker]); tags.push(tag); } else if (target instanceof _NDKEvent2) { const event = target; skipAuthorTag ?? (skipAuthorTag = event?.pubkey === this.pubkey); tags = event.referenceTags(marker, skipAuthorTag, forceTag, opts); if (opts?.pTags !== false) { for (const pTag of event.getMatchingTags("p")) { if (!pTag[1] || !isValidPubkey3(pTag[1])) continue; if (pTag[1] === this.pubkey) continue; if (this.tags.find((t) => t[0] === "p" && t[1] === pTag[1])) continue; this.tags.push(["p", pTag[1]]); } } } else if (Array.isArray(target)) { tags = [target]; } else { throw new Error("Invalid argument", target); } this.tags = mergeTags3(this.tags, tags); } async toNostrEvent(pubkey, opts) { if (!pubkey && this.pubkey === "") { const user = await this.ndk?.signer?.user(); this.pubkey = user?.pubkey || ""; } if (!this.created_at) { this.created_at = Math.floor(Date.now() / 1e3); } const { content, tags } = await this.generateTags(opts); this.content = content || ""; this.tags = tags; try { this.id = this.getEventHash(); } catch (_e2) { } return this.rawEvent(); } getMatchingTags(tagName, marker) { const t = this.tags.filter((tag) => tag[0] === tagName); if (marker === void 0) return t; return t.filter((tag) => tag[3] === marker); } hasTag(tagName, marker) { return this.tags.some((tag) => tag[0] === tagName && (!marker || tag[3] === marker)); } tagValue(tagName, marker) { const tags = this.getMatchingTags(tagName, marker); if (tags.length === 0) return; return tags[0][1]; } get alt() { return this.tagValue("alt"); } set alt(alt) { this.removeTag("alt"); if (alt) this.tags.push(["alt", alt]); } get dTag() { return this.tagValue("d"); } set dTag(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } removeTag(tagName, marker) { const tagNames = Array.isArray(tagName) ? tagName : [tagName]; this.tags = this.tags.filter((tag) => { const include = tagNames.includes(tag[0]); const hasMarker = marker ? tag[3] === marker : true; return !(include && hasMarker); }); } replaceTag(tag) { this.removeTag(tag[0]); this.tags.push(tag); } async sign(signer, opts) { this.ndk?.aiGuardrails?.event?.signing(this); if (!signer) { this.ndk?.assertSigner(); signer = this.ndk?.signer; } else { this.author = await signer.user(); } const nostrEvent = await this.toNostrEvent(void 0, opts); this.sig = await signer.sign(nostrEvent); return this.sig; } async publishReplaceable(relaySet, timeoutMs, requiredRelayCount) { this.id = ""; this.created_at = Math.floor(Date.now() / 1e3); this.sig = ""; return this.publish(relaySet, timeoutMs, requiredRelayCount); } async publish(relaySet, timeoutMs, requiredRelayCount, opts) { if (!requiredRelayCount) requiredRelayCount = 1; if (!this.sig) await this.sign(void 0, opts); if (!this.ndk) throw new Error("NDKEvent must be associated with an NDK instance to publish"); this.ndk.aiGuardrails?.event?.publishing(this); if (!relaySet || relaySet.size === 0) { relaySet = this.ndk.devWriteRelaySet || await calculateRelaySetFromEvent3(this.ndk, this, requiredRelayCount); } if (this.kind === 5 && this.ndk.cacheAdapter?.deleteEventIds) { const eTags = this.getMatchingTags("e").map((tag) => tag[1]); this.ndk.cacheAdapter.deleteEventIds(eTags); } const rawEvent = this.rawEvent(); if (this.ndk.cacheAdapter?.addUnpublishedEvent && shouldTrackUnpublishedEvent3(this)) { try { this.ndk.cacheAdapter.addUnpublishedEvent(this, relaySet.relayUrls); } catch (e2) { console.error("Error adding unpublished event to cache", e2); } } if (this.kind === 5 && this.ndk.cacheAdapter?.deleteEventIds) { this.ndk.cacheAdapter.deleteEventIds(this.getMatchingTags("e").map((tag) => tag[1])); } this.ndk.subManager.dispatchEvent(rawEvent, void 0, true); const relays = await relaySet.publish(this, timeoutMs, requiredRelayCount); relays.forEach((relay) => this.ndk?.subManager.seenEvent(this.id, relay)); return relays; } async generateTags(opts) { let tags = []; const g = await generateContentTags3(this.content, this.tags, opts, this); const content = g.content; tags = g.tags; if (this.kind && this.isParamReplaceable()) { const dTag = this.getMatchingTags("d")[0]; if (!dTag) { const title = this.tagValue("title"); const randLength = title ? 6 : 16; let str = [...Array(randLength)].map(() => Math.random().toString(36)[2]).join(""); if (title && title.length > 0) { str = `${title.replace(/[^a-z0-9]+/gi, "-").replace(/^-|-$/g, "")}-${str}`; } tags.push(["d", str]); } } if (this.shouldAddClientTag) { const clientTag = ["client", this.ndk?.clientName ?? ""]; if (this.ndk?.clientNip89) clientTag.push(this.ndk?.clientNip89); tags.push(clientTag); } else if (this.shouldStripClientTag) { tags = tags.filter((tag) => tag[0] !== "client"); } return { content: content || "", tags }; } get shouldAddClientTag() { if (!this.ndk?.clientName && !this.ndk?.clientNip89) return false; if (skipClientTagOnKinds3.has(this.kind)) return false; if (this.isEphemeral()) return false; if (this.isReplaceable() && !this.isParamReplaceable()) return false; if (this.isDvm()) return false; if (this.hasTag("client")) return false; return true; } get shouldStripClientTag() { return skipClientTagOnKinds3.has(this.kind); } muted() { if (this.ndk?.muteFilter && this.ndk.muteFilter(this)) { return "muted"; } return null; } replaceableDTag() { if (this.kind && this.kind >= 3e4 && this.kind <= 4e4) { const dTag = this.getMatchingTags("d")[0]; const dTagId = dTag ? dTag[1] : ""; return dTagId; } throw new Error("Event is not a parameterized replaceable event"); } deduplicationKey() { if (this.kind === 0 || this.kind === 3 || this.kind && this.kind >= 1e4 && this.kind < 2e4) { return `${this.kind}:${this.pubkey}`; } return this.tagId(); } tagId() { if (this.isParamReplaceable()) { return this.tagAddress(); } return this.id; } tagAddress() { if (this.isParamReplaceable()) { const dTagId = this.dTag ?? ""; return `${this.kind}:${this.pubkey}:${dTagId}`; } if (this.isReplaceable()) { return `${this.kind}:${this.pubkey}:`; } throw new Error("Event is not a replaceable event"); } tagType() { return this.isParamReplaceable() ? "a" : "e"; } tagReference(marker) { let tag; if (this.isParamReplaceable()) { tag = ["a", this.tagAddress()]; } else { tag = ["e", this.tagId()]; } if (this.relay) { tag.push(this.relay.url); } else { tag.push(""); } tag.push(marker ?? ""); if (!this.isParamReplaceable()) { tag.push(this.pubkey); } return tag; } referenceTags(marker, skipAuthorTag, forceTag, opts) { let tags = []; if (this.isParamReplaceable()) { tags = [ [forceTag ?? "a", this.tagAddress()], [forceTag ?? "e", this.id] ]; } else { tags = [[forceTag ?? "e", this.id]]; } tags = tags.map((tag) => { if (tag[0] === "e" || marker) { tag.push(this.relay?.url ?? ""); } else if (this.relay?.url) { tag.push(this.relay?.url); } return tag; }); tags.forEach((tag) => { if (tag[0] === "e") { tag.push(marker ?? ""); tag.push(this.pubkey); } else if (marker) { tag.push(marker); } }); tags = [...tags, ...this.getMatchingTags("h")]; if (!skipAuthorTag && opts?.pTags !== false) tags.push(...this.author.referenceTags()); return tags; } filter() { if (this.isParamReplaceable()) { return { "#a": [this.tagId()] }; } return { "#e": [this.tagId()] }; } nip22Filter() { if (this.isParamReplaceable()) { return { "#A": [this.tagId()] }; } return { "#E": [this.tagId()] }; } async delete(reason, publish = true) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); const e2 = new _NDKEvent2(this.ndk, { kind: 5, content: reason || "" }); e2.tag(this, void 0, true); e2.tags.push(["k", this.kind?.toString()]); if (publish) { this.emit("deleted"); await e2.publish(); } return e2; } set isProtected(val) { this.removeTag("-"); if (val) this.tags.push(["-"]); } get isProtected() { return this.hasTag("-"); } async react(content, publish = true) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); const e2 = new _NDKEvent2(this.ndk, { kind: 7, content }); e2.tag(this); if (this.kind !== 1) { e2.tags.push(["k", `${this.kind}`]); } if (publish) await e2.publish(); return e2; } get isValid() { return this.validate(); } get inspect() { return JSON.stringify(this.rawEvent(), null, 4); } dump() { console.debug(JSON.stringify(this.rawEvent(), null, 4)); console.debug("Event on relays:", this.onRelays.map((relay) => relay.url).join(", ")); } reply(forceNip22, opts) { const reply = new _NDKEvent2(this.ndk); this.ndk?.aiGuardrails?.event?.creatingReply(reply); if (this.kind === 1 && !forceNip22) { reply.kind = 1; const opHasETag = this.hasTag("e"); if (opHasETag) { reply.tags = [ ...reply.tags, ...this.getMatchingTags("e"), ...this.getMatchingTags("p"), ...this.getMatchingTags("a"), ...this.referenceTags("reply", false, void 0, opts) ]; } else { reply.tag(this, "root", false, void 0, opts); } } else { reply.kind = 1111; const carryOverTags = ["A", "E", "I", "P"]; const rootTags = this.tags.filter((tag) => carryOverTags.includes(tag[0])); if (rootTags.length > 0) { const rootKind = this.tagValue("K"); reply.tags.push(...rootTags); if (rootKind) reply.tags.push(["K", rootKind]); let tag; if (this.isParamReplaceable()) { tag = ["a", this.tagAddress()]; const relayHint = this.relay?.url ?? ""; if (relayHint) tag.push(relayHint); } else { tag = ["e", this.tagId()]; const relayHint = this.relay?.url ?? ""; tag.push(relayHint); tag.push(this.pubkey); } reply.tags.push(tag); } else { let lowerTag; let upperTag; const relayHint = this.relay?.url ?? ""; if (this.isParamReplaceable()) { lowerTag = ["a", this.tagAddress(), relayHint]; upperTag = ["A", this.tagAddress(), relayHint]; } else { lowerTag = ["e", this.tagId(), relayHint, this.pubkey]; upperTag = ["E", this.tagId(), relayHint, this.pubkey]; } reply.tags.push(lowerTag); reply.tags.push(upperTag); reply.tags.push(["K", this.kind?.toString()]); if (opts?.pTags !== false && opts?.pTagOnATags !== false) { reply.tags.push(["P", this.pubkey]); } } reply.tags.push(["k", this.kind?.toString()]); if (opts?.pTags !== false) { reply.tags.push(...this.getMatchingTags("p")); reply.tags.push(["p", this.pubkey]); } } return reply; } }; var untrackedUnpublishedEvents3 = /* @__PURE__ */ new Set([ 24133, 13194, 23194, 23195 ]); function shouldTrackUnpublishedEvent3(event) { return !untrackedUnpublishedEvents3.has(event.kind); } var NDKPool3 = class extends import_tseep32.EventEmitter { constructor(relayUrls, ndk, { debug: debug92, name } = {}) { super(); __publicField(this, "_relays", /* @__PURE__ */ new Map()); __publicField(this, "status", "idle"); __publicField(this, "autoConnectRelays", /* @__PURE__ */ new Set()); __publicField(this, "debug"); __publicField(this, "temporaryRelayTimers", /* @__PURE__ */ new Map()); __publicField(this, "flappingRelays", /* @__PURE__ */ new Set()); __publicField(this, "backoffTimes", /* @__PURE__ */ new Map()); __publicField(this, "ndk"); __publicField(this, "disconnectionTimes", /* @__PURE__ */ new Map()); __publicField(this, "systemEventDetector"); __publicField(this, "_name", "unnamed"); this.debug = debug92 ?? ndk.debug.extend("pool"); if (name) this._name = name; this.ndk = ndk; this.relayUrls = relayUrls; if (this.ndk.pools) { this.ndk.pools.push(this); } } get relays() { return this._relays; } set relayUrls(urls) { this._relays.clear(); for (const relayUrl of urls) { const relay = new NDKRelay3(relayUrl, void 0, this.ndk); relay.connectivity.netDebug = this.ndk.netDebug; this.addRelay(relay); } } get name() { return this._name; } set name(name) { this._name = name; this.debug = this.debug.extend(name); } useTemporaryRelay(relay, removeIfUnusedAfter = 3e4, filters) { const relayAlreadyInPool = this.relays.has(relay.url); if (!relayAlreadyInPool) { this.addRelay(relay); this.debug("Adding temporary relay %s for filters %o", relay.url, filters); } const existingTimer = this.temporaryRelayTimers.get(relay.url); if (existingTimer) { clearTimeout(existingTimer); } if (!relayAlreadyInPool || existingTimer) { const timer = setTimeout(() => { if (this.ndk.explicitRelayUrls?.includes(relay.url)) return; this.removeRelay(relay.url); }, removeIfUnusedAfter); this.temporaryRelayTimers.set(relay.url, timer); } } addRelay(relay, connect = true) { const isAlreadyInPool = this.relays.has(relay.url); const isCustomRelayUrl = relay.url.includes("/npub1"); let reconnect = true; const relayUrl = relay.url; if (isAlreadyInPool) return; if (this.ndk.relayConnectionFilter && !this.ndk.relayConnectionFilter(relayUrl)) { this.debug(`Refusing to add relay ${relayUrl}: blocked by relayConnectionFilter`); return; } if (isCustomRelayUrl) { this.debug(`Refusing to add relay ${relayUrl}: is a filter relay`); return; } if (this.ndk.cacheAdapter?.getRelayStatus) { const infoOrPromise = this.ndk.cacheAdapter.getRelayStatus(relayUrl); const info = infoOrPromise instanceof Promise ? void 0 : infoOrPromise; if (info?.dontConnectBefore) { if (info.dontConnectBefore > Date.now()) { const delay = info.dontConnectBefore - Date.now(); this.debug(`Refusing to add relay ${relayUrl}: delayed connect for ${delay}ms`); setTimeout(() => { this.addRelay(relay, connect); }, delay); return; } reconnect = false; } } const noticeHandler = (notice) => this.emit("notice", relay, notice); const connectHandler = () => this.handleRelayConnect(relayUrl); const readyHandler = () => this.handleRelayReady(relay); const disconnectHandler = () => { this.recordDisconnection(relay); this.emit("relay:disconnect", relay); }; const flappingHandler = () => this.handleFlapping(relay); const authHandler = (challenge3) => this.emit("relay:auth", relay, challenge3); const authedHandler = () => this.emit("relay:authed", relay); relay.off("notice", noticeHandler); relay.off("connect", connectHandler); relay.off("ready", readyHandler); relay.off("disconnect", disconnectHandler); relay.off("flapping", flappingHandler); relay.off("auth", authHandler); relay.off("authed", authedHandler); relay.on("notice", noticeHandler); relay.on("connect", connectHandler); relay.on("ready", readyHandler); relay.on("disconnect", disconnectHandler); relay.on("flapping", flappingHandler); relay.on("auth", authHandler); relay.on("authed", authedHandler); relay.on("delayed-connect", (delay) => { if (this.ndk.cacheAdapter?.updateRelayStatus) { this.ndk.cacheAdapter.updateRelayStatus(relay.url, { dontConnectBefore: Date.now() + delay }); } }); this._relays.set(relayUrl, relay); if (connect) this.autoConnectRelays.add(relayUrl); if (connect && this.status === "active") { this.emit("relay:connecting", relay); relay.connect(void 0, reconnect).catch((e2) => { this.debug(`Failed to connect to relay ${relayUrl}`, e2); }); } } removeRelay(relayUrl) { const relay = this.relays.get(relayUrl); if (relay) { relay.disconnect(); this.relays.delete(relayUrl); this.autoConnectRelays.delete(relayUrl); this.emit("relay:disconnect", relay); return true; } const existingTimer = this.temporaryRelayTimers.get(relayUrl); if (existingTimer) { clearTimeout(existingTimer); this.temporaryRelayTimers.delete(relayUrl); } return false; } isRelayConnected(url) { const normalizedUrl = normalizeRelayUrl3(url); const relay = this.relays.get(normalizedUrl); if (!relay) return false; return relay.status === 5; } getRelay(url, connect = true, temporary = false, filters) { let relay = this.relays.get(normalizeRelayUrl3(url)); if (!relay) { relay = new NDKRelay3(url, void 0, this.ndk); relay.connectivity.netDebug = this.ndk.netDebug; if (temporary) { this.useTemporaryRelay(relay, 3e4, filters); } else { this.addRelay(relay, connect); } } return relay; } handleRelayConnect(relayUrl) { const relay = this.relays.get(relayUrl); if (!relay) { console.error("NDK BUG: relay not found in pool", { relayUrl }); return; } this.emit("relay:connect", relay); if (this.stats().connected === this.relays.size) { this.emit("connect"); } } handleRelayReady(relay) { this.emit("relay:ready", relay); } async connect(timeoutMs) { this.status = "active"; this.debug(`Connecting to ${this.relays.size} relays${timeoutMs ? `, timeout ${timeoutMs}ms` : ""}...`); const relaysToConnect = Array.from(this.autoConnectRelays.keys()).map((url) => this.relays.get(url)).filter((relay) => !!relay); for (const relay of relaysToConnect) { if (relay.status !== 5 && relay.status !== 4) { this.emit("relay:connecting", relay); relay.connect().catch((e2) => { this.debug(`Failed to connect to relay ${relay.url}: ${e2 ?? "No reason specified"}`); }); } } const allConnected = () => relaysToConnect.every((r) => r.status === 5); const allConnectedPromise = new Promise((resolve) => { if (allConnected()) { resolve(); return; } const listeners = []; for (const relay of relaysToConnect) { const handler = () => { if (allConnected()) { for (let i22 = 0; i22 < relaysToConnect.length; i22++) { relaysToConnect[i22].off("connect", listeners[i22]); } resolve(); } }; listeners.push(handler); relay.on("connect", handler); } }); const timeoutPromise = typeof timeoutMs === "number" ? new Promise((resolve) => setTimeout(resolve, timeoutMs)) : new Promise(() => { }); await Promise.race([allConnectedPromise, timeoutPromise]); } checkOnFlappingRelays() { const flappingRelaysCount = this.flappingRelays.size; const totalRelays = this.relays.size; if (flappingRelaysCount / totalRelays >= 0.8) { for (const relayUrl of this.flappingRelays) { this.backoffTimes.set(relayUrl, 0); } } } recordDisconnection(relay) { const now2 = Date.now(); this.disconnectionTimes.set(relay.url, now2); for (const [url, time] of this.disconnectionTimes.entries()) { if (now2 - time > 1e4) { this.disconnectionTimes.delete(url); } } this.checkForSystemWideDisconnection(); } checkForSystemWideDisconnection() { const now2 = Date.now(); const recentDisconnections = []; for (const time of this.disconnectionTimes.values()) { if (now2 - time < 5e3) { recentDisconnections.push(time); } } if (recentDisconnections.length > this.relays.size / 2 && this.relays.size > 1) { this.debug(`System-wide disconnection detected: ${recentDisconnections.length}/${this.relays.size} relays disconnected`); this.handleSystemWideReconnection(); } } handleSystemWideReconnection() { if (this.systemEventDetector) { this.debug("System-wide reconnection already in progress, skipping"); return; } this.debug("Initiating system-wide reconnection with reset backoff"); this.systemEventDetector = setTimeout(() => { this.systemEventDetector = void 0; }, 1e4); for (const relay of this.relays.values()) { if (relay.connectivity) { relay.connectivity.resetReconnectionState(); if (relay.status !== 5 && relay.status !== 4) { relay.connect().catch((e2) => { this.debug(`Failed to reconnect relay ${relay.url} after system event: ${e2}`); }); } } } this.disconnectionTimes.clear(); } handleFlapping(relay) { this.debug(`Relay ${relay.url} is flapping`); let currentBackoff = this.backoffTimes.get(relay.url) || 5e3; currentBackoff = currentBackoff * 2; this.backoffTimes.set(relay.url, currentBackoff); this.debug(`Backoff time for ${relay.url} is ${currentBackoff}ms`); setTimeout(() => { this.debug(`Attempting to reconnect to ${relay.url}`); this.emit("relay:connecting", relay); relay.connect(); this.checkOnFlappingRelays(); }, currentBackoff); relay.disconnect(); this.emit("flapping", relay); } size() { return this.relays.size; } stats() { const stats = { total: 0, connected: 0, disconnected: 0, connecting: 0 }; for (const relay of this.relays.values()) { stats.total++; if (relay.status === 5) { stats.connected++; } else if (relay.status === 1) { stats.disconnected++; } else if (relay.status === 4) { stats.connecting++; } } return stats; } connectedRelays() { return Array.from(this.relays.values()).filter((relay) => relay.status >= 5); } permanentAndConnectedRelays() { return Array.from(this.relays.values()).filter((relay) => relay.status >= 5 && !this.temporaryRelayTimers.has(relay.url)); } urls() { return Array.from(this.relays.keys()); } }; var _a38; var NDKDVMJobFeedback3 = (_a38 = class extends NDKEvent3 { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 7e3); } static async from(event) { const e2 = new _a38(event.ndk, event.rawEvent()); if (e2.encrypted) await e2.dvmDecrypt(); return e2; } get status() { return this.tagValue("status"); } set status(status) { this.removeTag("status"); if (status !== void 0) { this.tags.push(["status", status]); } } get encrypted() { return !!this.getMatchingTags("encrypted")[0]; } async dvmDecrypt() { await this.decrypt(); const decryptedContent = JSON.parse(this.content); this.tags.push(...decryptedContent); } }, __publicField(_a38, "kind", 7e3), __publicField(_a38, "kinds", [7e3]), _a38); var _a39; var NDKCashuMintList3 = (_a39 = class extends NDKEvent3 { constructor(ndk, event) { super(ndk, event); __publicField(this, "_p2pk"); this.kind ?? (this.kind = 10019); } static from(event) { return new _a39(event.ndk, event); } set relays(urls) { this.tags = this.tags.filter((t) => t[0] !== "relay"); for (const url of urls) { this.tags.push(["relay", url]); } } get relays() { const r = []; for (const tag of this.tags) { if (tag[0] === "relay") { r.push(tag[1]); } } return r; } set mints(urls) { this.tags = this.tags.filter((t) => t[0] !== "mint"); for (const url of urls) { this.tags.push(["mint", url]); } } get mints() { const r = []; for (const tag of this.tags) { if (tag[0] === "mint") { r.push(tag[1]); } } return Array.from(new Set(r)); } get p2pk() { if (this._p2pk) { return this._p2pk; } this._p2pk = this.tagValue("pubkey") ?? this.pubkey; return this._p2pk; } set p2pk(pubkey) { this._p2pk = pubkey; this.removeTag("pubkey"); if (pubkey) { this.tags.push(["pubkey", pubkey]); } } get relaySet() { return NDKRelaySet3.fromRelayUrls(this.relays, this.ndk); } }, __publicField(_a39, "kind", 10019), __publicField(_a39, "kinds", [10019]), _a39); var _a40; var NDKArticle3 = (_a40 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 30023); } static from(event) { return new _a40(event.ndk, event); } get title() { return this.tagValue("title"); } set title(title) { this.removeTag("title"); if (title) this.tags.push(["title", title]); } get image() { return this.tagValue("image"); } set image(image) { this.removeTag("image"); if (image) this.tags.push(["image", image]); } get summary() { return this.tagValue("summary"); } set summary(summary) { this.removeTag("summary"); if (summary) this.tags.push(["summary", summary]); } get published_at() { const tag = this.tagValue("published_at"); if (tag) { let val = Number.parseInt(tag); if (val > 1e12) { val = Math.floor(val / 1e3); } return val; } return; } set published_at(timestamp) { this.removeTag("published_at"); if (timestamp !== void 0) { this.tags.push(["published_at", timestamp.toString()]); } } async generateTags() { super.generateTags(); if (!this.published_at) { this.published_at = this.created_at; } return super.generateTags(); } get url() { return this.tagValue("url"); } set url(url) { if (url) { this.tags.push(["url", url]); } else { this.removeTag("url"); } } }, __publicField(_a40, "kind", 30023), __publicField(_a40, "kinds", [30023]), _a40); var _a41; var NDKBlossomList3 = (_a41 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 10063); } static from(ndkEvent) { return new _a41(ndkEvent.ndk, ndkEvent.rawEvent()); } get servers() { return this.tags.filter((tag) => tag[0] === "server").map((tag) => tag[1]); } set servers(servers) { this.tags = this.tags.filter((tag) => tag[0] !== "server"); for (const server of servers) { this.tags.push(["server", server]); } } get default() { const servers = this.servers; return servers.length > 0 ? servers[0] : void 0; } set default(server) { if (!server) return; const currentServers = this.servers; const filteredServers = currentServers.filter((s) => s !== server); this.servers = [server, ...filteredServers]; } addServer(server) { if (!server) return; const currentServers = this.servers; if (!currentServers.includes(server)) { this.servers = [...currentServers, server]; } } removeServer(server) { if (!server) return; const currentServers = this.servers; this.servers = currentServers.filter((s) => s !== server); } }, __publicField(_a41, "kind", 10063), __publicField(_a41, "kinds", [10063]), _a41); var _a42; var NDKFedimintMint3 = (_a42 = class extends NDKEvent3 { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 38173); } static async from(event) { const mint = new _a42(event.ndk, event); return mint; } get identifier() { return this.tagValue("d"); } set identifier(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } get inviteCodes() { return this.getMatchingTags("u").map((t) => t[1]); } set inviteCodes(values) { this.removeTag("u"); for (const value of values) { this.tags.push(["u", value]); } } get modules() { return this.getMatchingTags("modules").map((t) => t[1]); } set modules(values) { this.removeTag("modules"); for (const value of values) { this.tags.push(["modules", value]); } } get network() { return this.tagValue("n"); } set network(value) { this.removeTag("n"); if (value) this.tags.push(["n", value]); } get metadata() { if (!this.content) return; try { return JSON.parse(this.content); } catch { return; } } set metadata(value) { if (value) { this.content = JSON.stringify(value); } else { this.content = ""; } } }, __publicField(_a42, "kind", 38173), __publicField(_a42, "kinds", [38173]), _a42); var _a43; var NDKCashuMintAnnouncement3 = (_a43 = class extends NDKEvent3 { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 38172); } static async from(event) { const mint = new _a43(event.ndk, event); return mint; } get identifier() { return this.tagValue("d"); } set identifier(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } get url() { return this.tagValue("u"); } set url(value) { this.removeTag("u"); if (value) this.tags.push(["u", value]); } get nuts() { return this.getMatchingTags("nuts").map((t) => t[1]); } set nuts(values) { this.removeTag("nuts"); for (const value of values) { this.tags.push(["nuts", value]); } } get network() { return this.tagValue("n"); } set network(value) { this.removeTag("n"); if (value) this.tags.push(["n", value]); } get metadata() { if (!this.content) return; try { return JSON.parse(this.content); } catch { return; } } set metadata(value) { if (value) { this.content = JSON.stringify(value); } else { this.content = ""; } } }, __publicField(_a43, "kind", 38172), __publicField(_a43, "kinds", [38172]), _a43); var _a44; var NDKMintRecommendation3 = (_a44 = class extends NDKEvent3 { constructor(ndk, event) { super(ndk, event); this.kind ?? (this.kind = 38e3); } static async from(event) { const recommendation = new _a44(event.ndk, event); return recommendation; } get recommendedKind() { const value = this.tagValue("k"); return value ? Number(value) : void 0; } set recommendedKind(value) { this.removeTag("k"); if (value) this.tags.push(["k", value.toString()]); } get identifier() { return this.tagValue("d"); } set identifier(value) { this.removeTag("d"); if (value) this.tags.push(["d", value]); } get urls() { return this.getMatchingTags("u").map((t) => t[1]); } set urls(values) { this.removeTag("u"); for (const value of values) { this.tags.push(["u", value]); } } get mintEventPointers() { return this.getMatchingTags("a").map((t) => ({ kind: Number(t[1].split(":")[0]), identifier: t[1].split(":")[2], relay: t[2] })); } addMintEventPointer(kind, pubkey, identifier, relay) { const aTag = [`a`, `${kind}:${pubkey}:${identifier}`]; if (relay) aTag.push(relay); this.tags.push(aTag); } get review() { return this.content; } set review(value) { this.content = value; } }, __publicField(_a44, "kind", 38e3), __publicField(_a44, "kinds", [38e3]), _a44); var _a45; var NDKClassified3 = (_a45 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 30402); } static from(event) { return new _a45(event.ndk, event); } get title() { return this.tagValue("title"); } set title(title) { this.removeTag("title"); if (title) this.tags.push(["title", title]); } get summary() { return this.tagValue("summary"); } set summary(summary) { this.removeTag("summary"); if (summary) this.tags.push(["summary", summary]); } get published_at() { const tag = this.tagValue("published_at"); if (tag) { return Number.parseInt(tag); } return; } set published_at(timestamp) { this.removeTag("published_at"); if (timestamp !== void 0) { this.tags.push(["published_at", timestamp.toString()]); } } get location() { return this.tagValue("location"); } set location(location2) { this.removeTag("location"); if (location2) this.tags.push(["location", location2]); } get price() { const priceTag = this.tags.find((tag) => tag[0] === "price"); if (priceTag) { return { amount: Number.parseFloat(priceTag[1]), currency: priceTag[2], frequency: priceTag[3] }; } return; } set price(priceTag) { if (typeof priceTag === "string") { priceTag = { amount: Number.parseFloat(priceTag) }; } if (priceTag?.amount) { const tag = ["price", priceTag.amount.toString()]; if (priceTag.currency) tag.push(priceTag.currency); if (priceTag.frequency) tag.push(priceTag.frequency); this.tags.push(tag); } else { this.removeTag("price"); } } async generateTags() { super.generateTags(); if (!this.published_at) { this.published_at = this.created_at; } return super.generateTags(); } }, __publicField(_a45, "kind", 30402), __publicField(_a45, "kinds", [30402]), _a45); var _a46; var NDKDraft3 = (_a46 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_event"); __publicField(this, "counterparty"); this.kind ?? (this.kind = 31234); } static from(event) { return new _a46(event.ndk, event); } set identifier(id) { this.removeTag("d"); this.tags.push(["d", id]); } get identifier() { return this.dTag; } set event(e2) { if (!(e2 instanceof NDKEvent3)) this._event = new NDKEvent3(void 0, e2); else this._event = e2; this.prepareEvent(); } set checkpoint(parent) { if (parent) { this.tags.push(parent.tagReference()); this.kind = 1234; } else { this.removeTag("a"); this.kind = 31234; } } get isCheckpoint() { return this.kind === 1234; } get isProposal() { const pTag = this.tagValue("p"); return !!pTag && pTag !== this.pubkey; } async getEvent(signer) { if (this._event) return this._event; signer ?? (signer = this.ndk?.signer); if (!signer) throw new Error("No signer available"); if (this.content && this.content.length > 0) { try { const ownPubkey = signer.pubkey; const pubkeys = [this.tagValue("p"), this.pubkey].filter(Boolean); const counterpartyPubkey = pubkeys.find((pubkey) => pubkey !== ownPubkey); let user; user = new NDKUser3({ pubkey: counterpartyPubkey ?? ownPubkey }); await this.decrypt(user, signer); const payload = JSON.parse(this.content); this._event = await wrapEvent32(new NDKEvent3(this.ndk, payload)); return this._event; } catch (e2) { console.error(e2); return; } } else { return null; } } prepareEvent() { if (!this._event) throw new Error("No event has been provided"); this.removeTag("k"); if (this._event.kind) this.tags.push(["k", this._event.kind.toString()]); this.content = JSON.stringify(this._event.rawEvent()); } async save({ signer, publish, relaySet }) { signer ?? (signer = this.ndk?.signer); if (!signer) throw new Error("No signer available"); const user = this.counterparty || await signer.user(); await this.encrypt(user, signer); if (this.counterparty) { const pubkey = this.counterparty.pubkey; this.removeTag("p"); this.tags.push(["p", pubkey]); } if (publish === false) return; return this.publishReplaceable(relaySet); } }, __publicField(_a46, "kind", 31234), __publicField(_a46, "kinds", [31234, 1234]), _a46); function mapImetaTag3(tag) { const data = {}; if (tag.length === 2) { const parts = tag[1].split(" "); for (let i22 = 0; i22 < parts.length; i22 += 2) { const key = parts[i22]; const value = parts[i22 + 1]; if (key === "fallback") { if (!data.fallback) data.fallback = []; data.fallback.push(value); } else { data[key] = value; } } return data; } const tags = tag.slice(1); for (const val of tags) { const parts = val.split(" "); const key = parts[0]; const value = parts.slice(1).join(" "); if (key === "fallback") { if (!data.fallback) data.fallback = []; data.fallback.push(value); } else { data[key] = value; } } return data; } function imetaTagToTag3(imeta) { const tag = ["imeta"]; for (const [key, value] of Object.entries(imeta)) { if (Array.isArray(value)) { for (const v6 of value) { tag.push(`${key} ${v6}`); } } else if (value) { tag.push(`${key} ${value}`); } } return tag; } var _a47; var NDKFollowPack3 = (_a47 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 39089); } static from(ndkEvent) { return new _a47(ndkEvent.ndk, ndkEvent); } get title() { return this.tagValue("title"); } set title(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } get image() { const imetaTag = this.tags.find((tag) => tag[0] === "imeta"); if (imetaTag) { const imeta = mapImetaTag3(imetaTag); if (imeta.url) return imeta.url; } return this.tagValue("image"); } set image(value) { this.tags = this.tags.filter((tag) => tag[0] !== "imeta" && tag[0] !== "image"); if (typeof value === "string") { if (value !== void 0) { this.tags.push(["image", value]); } } else if (value && typeof value === "object") { this.tags.push(imetaTagToTag3(value)); if (value.url) { this.tags.push(["image", value.url]); } } } get pubkeys() { return Array.from(new Set(this.tags.filter((tag) => tag[0] === "p" && tag[1] && isValidPubkey3(tag[1])).map((tag) => tag[1]))); } set pubkeys(pubkeys) { this.tags = this.tags.filter((tag) => tag[0] !== "p"); for (const pubkey of pubkeys) { this.tags.push(["p", pubkey]); } } get description() { return this.tagValue("description"); } set description(value) { this.removeTag("description"); if (value) this.tags.push(["description", value]); } }, __publicField(_a47, "kind", 39089), __publicField(_a47, "kinds", [39089, 39092]), _a47); var _a48; var NDKHighlight3 = (_a48 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_article"); this.kind ?? (this.kind = 9802); } static from(event) { return new _a48(event.ndk, event); } get url() { return this.tagValue("r"); } set context(context) { if (context === void 0) { this.tags = this.tags.filter(([tag, _value]) => tag !== "context"); } else { this.tags = this.tags.filter(([tag, _value]) => tag !== "context"); this.tags.push(["context", context]); } } get context() { return this.tags.find(([tag, _value]) => tag === "context")?.[1] ?? void 0; } get article() { return this._article; } set article(article) { this._article = article; if (typeof article === "string") { this.tags.push(["r", article]); } else { this.tag(article); } } getArticleTag() { return this.getMatchingTags("a")[0] || this.getMatchingTags("e")[0] || this.getMatchingTags("r")[0]; } async getArticle() { if (this._article !== void 0) return this._article; let taggedBech32; const articleTag = this.getArticleTag(); if (!articleTag) return; switch (articleTag[0]) { case "a": { const [kind, pubkey, identifier] = articleTag[1].split(":"); taggedBech32 = nip19_exports3.naddrEncode({ kind: Number.parseInt(kind), pubkey, identifier }); break; } case "e": taggedBech32 = nip19_exports3.noteEncode(articleTag[1]); break; case "r": this._article = articleTag[1]; break; } if (taggedBech32) { let a = await this.ndk?.fetchEvent(taggedBech32); if (a) { if (a.kind === 30023) { a = NDKArticle3.from(a); } this._article = a; } } return this._article; } }, __publicField(_a48, "kind", 9802), __publicField(_a48, "kinds", [9802]), _a48); var _a49; var NDKImage3 = (_a49 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_imetas"); this.kind ?? (this.kind = 20); } static from(event) { return new _a49(event.ndk, event.rawEvent()); } get isValid() { return this.imetas.length > 0; } get imetas() { if (this._imetas) return this._imetas; this._imetas = this.tags.filter((tag) => tag[0] === "imeta").map(mapImetaTag3).filter((imeta) => !!imeta.url); return this._imetas; } set imetas(tags) { this._imetas = tags; this.tags = this.tags.filter((tag) => tag[0] !== "imeta"); this.tags.push(...tags.map(imetaTagToTag3)); } }, __publicField(_a49, "kind", 20), __publicField(_a49, "kinds", [20]), _a49); var _a50; var NDKList3 = (_a50 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_encryptedTags"); __publicField(this, "encryptedTagsLength"); this.kind ?? (this.kind = 30001); } static from(ndkEvent) { return new _a50(ndkEvent.ndk, ndkEvent); } get title() { const titleTag = this.tagValue("title") || this.tagValue("name"); if (titleTag) return titleTag; if (this.kind === 3) { return "Contacts"; } if (this.kind === 1e4) { return "Mute"; } if (this.kind === 10001) { return "Pinned Notes"; } if (this.kind === 10002) { return "Relay Metadata"; } if (this.kind === 10003) { return "Bookmarks"; } if (this.kind === 10004) { return "Communities"; } if (this.kind === 10005) { return "Public Chats"; } if (this.kind === 10006) { return "Blocked Relays"; } if (this.kind === 10007) { return "Search Relays"; } if (this.kind === 10050) { return "Direct Message Receive Relays"; } if (this.kind === 10012) { return "Relay Feeds"; } if (this.kind === 10015) { return "Interests"; } if (this.kind === 10030) { return "Emojis"; } return this.tagValue("d"); } set title(title) { this.removeTag(["title", "name"]); if (title) this.tags.push(["title", title]); } get name() { return this.title; } set name(name) { this.title = name; } get description() { return this.tagValue("description"); } set description(name) { this.removeTag("description"); if (name) this.tags.push(["description", name]); } get image() { return this.tagValue("image"); } set image(name) { this.removeTag("image"); if (name) this.tags.push(["image", name]); } isEncryptedTagsCacheValid() { return !!(this._encryptedTags && this.encryptedTagsLength === this.content.length); } async encryptedTags(useCache = true) { if (useCache && this.isEncryptedTagsCacheValid()) return this._encryptedTags; if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); const user = await this.ndk.signer.user(); try { if (this.content.length > 0) { try { const decryptedContent = await this.ndk.signer.decrypt(user, this.content); const a = JSON.parse(decryptedContent); if (a?.[0]) { this.encryptedTagsLength = this.content.length; return this._encryptedTags = a; } this.encryptedTagsLength = this.content.length; return this._encryptedTags = []; } catch (_e2) { } } } catch (_e2) { } return []; } validateTag(_tagValue) { return true; } getItems(type) { return this.tags.filter((tag) => tag[0] === type); } get items() { return this.tags.filter((t) => { return ![ "d", "L", "l", "title", "name", "description", "published_at", "summary", "image", "thumb", "alt", "expiration", "subject", "client" ].includes(t[0]); }); } async addItem(item, mark = void 0, encrypted = false, position = "bottom") { if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); let tags; if (item instanceof NDKEvent3) { tags = [item.tagReference(mark)]; } else if (item instanceof NDKUser3) { tags = item.referenceTags(); } else if (item instanceof NDKRelay3) { tags = item.referenceTags(); } else if (Array.isArray(item)) { tags = [item]; } else { throw new Error("Invalid object type"); } if (mark) tags[0].push(mark); if (encrypted) { const user = await this.ndk.signer.user(); const currentList = await this.encryptedTags(); if (position === "top") currentList.unshift(...tags); else currentList.push(...tags); this._encryptedTags = currentList; this.encryptedTagsLength = this.content.length; this.content = JSON.stringify(currentList); await this.encrypt(user); } else { if (position === "top") this.tags.unshift(...tags); else this.tags.push(...tags); } this.created_at = Math.floor(Date.now() / 1e3); this.emit("change"); } async removeItemByValue(value, publish = true) { if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); const index = this.tags.findIndex((tag) => tag[1] === value); if (index >= 0) { this.tags.splice(index, 1); } const user = await this.ndk.signer.user(); const encryptedTags = await this.encryptedTags(); const encryptedIndex = encryptedTags.findIndex((tag) => tag[1] === value); if (encryptedIndex >= 0) { encryptedTags.splice(encryptedIndex, 1); this._encryptedTags = encryptedTags; this.encryptedTagsLength = this.content.length; this.content = JSON.stringify(encryptedTags); await this.encrypt(user); } if (publish) { return this.publishReplaceable(); } this.created_at = Math.floor(Date.now() / 1e3); this.emit("change"); } async removeItem(index, encrypted) { if (!this.ndk) throw new Error("NDK instance not set"); if (!this.ndk.signer) throw new Error("NDK signer not set"); if (encrypted) { const user = await this.ndk.signer.user(); const currentList = await this.encryptedTags(); currentList.splice(index, 1); this._encryptedTags = currentList; this.encryptedTagsLength = this.content.length; this.content = JSON.stringify(currentList); await this.encrypt(user); } else { this.tags.splice(index, 1); } this.created_at = Math.floor(Date.now() / 1e3); this.emit("change"); return this; } has(item) { return this.items.some((tag) => tag[1] === item); } filterForItems() { const ids = /* @__PURE__ */ new Set(); const nip33Queries = /* @__PURE__ */ new Map(); const filters = []; for (const tag of this.items) { if (tag[0] === "e" && tag[1]) { ids.add(tag[1]); } else if (tag[0] === "a" && tag[1]) { const [kind, pubkey, dTag] = tag[1].split(":"); if (!kind || !pubkey) continue; const key = `${kind}:${pubkey}`; const item = nip33Queries.get(key) || []; item.push(dTag || ""); nip33Queries.set(key, item); } } if (ids.size > 0) { filters.push({ ids: Array.from(ids) }); } if (nip33Queries.size > 0) { for (const [key, values] of nip33Queries.entries()) { const [kind, pubkey] = key.split(":"); filters.push({ kinds: [Number.parseInt(kind)], authors: [pubkey], "#d": values }); } } return filters; } }, __publicField(_a50, "kind", 30001), __publicField(_a50, "kinds", [ 30001, 10004, 10050, 10030, 10015, 10001, 10002, 10007, 10006, 10003, 10012 ]), _a50); var _a51; var NDKAppHandlerEvent3 = (_a51 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "profile"); this.kind ?? (this.kind = 31990); } static from(ndkEvent) { const event = new _a51(ndkEvent.ndk, ndkEvent.rawEvent()); if (event.isValid) { return event; } return null; } get isValid() { const combinations = /* @__PURE__ */ new Map(); const combinationFromTag = (tag) => [tag[0], tag[2]].join(":").toLowerCase(); const tagsToInspect = ["web", "android", "ios"]; for (const tag of this.tags) { if (tagsToInspect.includes(tag[0])) { const combination = combinationFromTag(tag); if (combinations.has(combination)) { if (combinations.get(combination) !== tag[1].toLowerCase()) { return false; } } combinations.set(combination, tag[1].toLowerCase()); } } return true; } async fetchProfile() { if (this.profile === void 0 && this.content.length > 0) { try { const profile = JSON.parse(this.content); if (profile?.name) { return profile; } this.profile = null; } catch (_e2) { this.profile = null; } } return new Promise((resolve, reject) => { const author = this.author; author.fetchProfile().then(() => { resolve(author.profile); }).catch(reject); }); } }, __publicField(_a51, "kind", 31990), __publicField(_a51, "kinds", [31990]), _a51); var SEVERITY_MAP3 = { ["NO_PROOFS"]: "ERROR", ["INVALID_PROOF_COUNT"]: "ERROR", ["MULTIPLE_RECIPIENTS"]: "ERROR", ["NO_RECIPIENT"]: "ERROR", ["MULTIPLE_MINTS"]: "ERROR", ["NO_MINT"]: "ERROR", ["MULTIPLE_EVENT_TAGS"]: "ERROR", ["MALFORMED_PROOF_SECRET"]: "ERROR", ["MISSING_EVENT_TAG_IN_PROOF"]: "WARNING", ["MISMATCHED_EVENT_TAG_IN_PROOF"]: "WARNING", ["MISSING_SENDER_TAG_IN_PROOF"]: "WARNING", ["MISMATCHED_SENDER_TAG_IN_PROOF"]: "WARNING", ["NO_EVENT_TAG_IN_EVENT"]: "WARNING" }; var ERROR_MESSAGES3 = { ["NO_PROOFS"]: "Nutzap must contain at least one proof", ["INVALID_PROOF_COUNT"]: "Invalid proof count", ["MULTIPLE_RECIPIENTS"]: "Nutzap must have exactly one recipient (p tag)", ["NO_RECIPIENT"]: "Nutzap must have a recipient (p tag)", ["MULTIPLE_MINTS"]: "Nutzap must specify exactly one mint (u tag)", ["NO_MINT"]: "Nutzap must specify a mint (u tag)", ["MULTIPLE_EVENT_TAGS"]: "Nutzap must have at most one event tag (e tag)", ["MALFORMED_PROOF_SECRET"]: "Proof secret is malformed and cannot be parsed", ["MISSING_EVENT_TAG_IN_PROOF"]: "Proof secret missing 'e' tag for replay protection", ["MISMATCHED_EVENT_TAG_IN_PROOF"]: "Proof secret 'e' tag does not match event being zapped", ["MISSING_SENDER_TAG_IN_PROOF"]: "Proof secret missing 'P' tag for sender verification", ["MISMATCHED_SENDER_TAG_IN_PROOF"]: "Proof secret 'P' tag does not match sender pubkey", ["NO_EVENT_TAG_IN_EVENT"]: "Nutzap event missing 'e' tag (recommended for replay protection)" }; function createValidationIssue3(code, proofIndex) { return { code, severity: SEVERITY_MAP3[code], message: ERROR_MESSAGES3[code], proofIndex }; } var _a52; var NDKNutzap3 = (_a52 = class extends NDKEvent3 { constructor(ndk, event) { super(ndk, event); __publicField(this, "debug"); __publicField(this, "_proofs", []); __publicField(this, "sender", this.author); this.kind ?? (this.kind = 9321); this.debug = ndk?.debug.extend("nutzap") ?? import_debug42.default("ndk:nutzap"); if (!this.alt) this.alt = "This is a nutzap"; try { const proofTags = this.getMatchingTags("proof"); if (proofTags.length) { this._proofs = proofTags.map((tag) => JSON.parse(tag[1])); } else { this._proofs = JSON.parse(this.content); } } catch { return; } } static from(event) { const e2 = new _a52(event.ndk, event); if (!e2._proofs || !e2._proofs.length) return; return e2; } set comment(comment) { this.content = comment ?? ""; } get comment() { const c = this.tagValue("comment"); if (c) return c; return this.content; } set proofs(proofs) { this._proofs = proofs; this.tags = this.tags.filter((tag) => tag[0] !== "proof"); for (const proof of proofs) { this.tags.push(["proof", JSON.stringify(proof)]); } } get proofs() { return this._proofs; } get rawP2pk() { const firstProof = this.proofs[0]; try { const secret = JSON.parse(firstProof.secret); let payload; if (typeof secret === "string") { payload = JSON.parse(secret); this.debug("stringified payload", firstProof.secret); } else if (typeof secret === "object") { payload = secret; } if (Array.isArray(payload) && payload[0] === "P2PK" && payload.length > 1 && typeof payload[1] === "object" && payload[1] !== null) { return payload[1].data; } if (typeof payload === "object" && payload !== null && typeof payload[1]?.data === "string") { return payload[1].data; } } catch (e2) { this.debug("error parsing p2pk pubkey", e2, this.proofs[0]); } return; } get p2pk() { const rawP2pk = this.rawP2pk; if (!rawP2pk) return; return rawP2pk.startsWith("02") ? rawP2pk.slice(2) : rawP2pk; } get mint() { return this.tagValue("u"); } set mint(value) { this.replaceTag(["u", value]); } get unit() { let _unit = this.tagValue("unit") ?? "sat"; if (_unit?.startsWith("msat")) _unit = "sat"; return _unit; } set unit(value) { this.removeTag("unit"); if (value?.startsWith("msat")) throw new Error("msat is not allowed, use sat denomination instead"); if (value) this.tag(["unit", value]); } get amount() { const amount = this.proofs.reduce((total, proof) => total + proof.amount, 0); return amount; } set target(target) { this.tags = this.tags.filter((t) => t[0] !== "p"); if (target instanceof NDKEvent3) { this.tags.push(target.tagReference()); } } set recipientPubkey(pubkey) { this.removeTag("p"); this.tag(["p", pubkey]); } get recipientPubkey() { return this.tagValue("p"); } get recipient() { const pubkey = this.recipientPubkey; if (this.ndk) return this.ndk.getUser({ pubkey }); return new NDKUser3({ pubkey }); } async toNostrEvent() { if (this.unit === "msat") { this.unit = "sat"; } this.removeTag("amount"); this.tags.push(["amount", this.amount.toString()]); const event = await super.toNostrEvent(); event.content = this.comment; return event; } get isValid() { const result = this.validateNIP61(); return result.valid; } validateNIP61() { const issues = []; let eTagCount = 0; let pTagCount = 0; let mintTagCount = 0; for (const tag of this.tags) { if (tag[0] === "e") eTagCount++; if (tag[0] === "p") pTagCount++; if (tag[0] === "u") mintTagCount++; } if (this.proofs.length === 0) { issues.push(createValidationIssue3("NO_PROOFS")); } if (pTagCount === 0) { issues.push(createValidationIssue3("NO_RECIPIENT")); } else if (pTagCount > 1) { issues.push(createValidationIssue3("MULTIPLE_RECIPIENTS")); } if (mintTagCount === 0) { issues.push(createValidationIssue3("NO_MINT")); } else if (mintTagCount > 1) { issues.push(createValidationIssue3("MULTIPLE_MINTS")); } if (eTagCount > 1) { issues.push(createValidationIssue3("MULTIPLE_EVENT_TAGS")); } const eventId = this.tagValue("e"); const senderPubkey = this.pubkey; for (let i22 = 0; i22 < this.proofs.length; i22++) { const proof = this.proofs[i22]; try { const secret = JSON.parse(proof.secret); const payload = typeof secret === "string" ? JSON.parse(secret) : secret; if (Array.isArray(payload) && payload[0] === "P2PK" && payload[1]) { const tags = payload[1].tags; if (eventId) { if (!tags) { issues.push(createValidationIssue3("MISSING_EVENT_TAG_IN_PROOF", i22)); } else { const eTag = tags.find((t) => t[0] === "e"); if (!eTag) { issues.push(createValidationIssue3("MISSING_EVENT_TAG_IN_PROOF", i22)); } else if (eTag[1] !== eventId) { issues.push(createValidationIssue3("MISMATCHED_EVENT_TAG_IN_PROOF", i22)); } } } if (!tags) { issues.push(createValidationIssue3("MISSING_SENDER_TAG_IN_PROOF", i22)); } else { const PTag = tags.find((t) => t[0] === "P"); if (!PTag) { issues.push(createValidationIssue3("MISSING_SENDER_TAG_IN_PROOF", i22)); } else if (PTag[1] !== senderPubkey) { issues.push(createValidationIssue3("MISMATCHED_SENDER_TAG_IN_PROOF", i22)); } } } } catch { issues.push(createValidationIssue3("MALFORMED_PROOF_SECRET", i22)); } } if (!eventId && this.proofs.length > 0) { issues.push(createValidationIssue3("NO_EVENT_TAG_IN_EVENT")); } const hasErrors = issues.some((issue) => issue.severity === "ERROR"); return { valid: !hasErrors, issues }; } }, __publicField(_a52, "kind", 9321), __publicField(_a52, "kinds", [_a52.kind]), _a52); var _a53; var NDKProject3 = (_a53 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_signer"); this.kind = 31933; } static from(event) { return new _a53(event.ndk, event.rawEvent()); } set repo(value) { this.removeTag("repo"); if (value) this.tags.push(["repo", value]); } set hashtags(values) { this.removeTag("hashtags"); if (values.filter((t) => t.length > 0).length) this.tags.push(["hashtags", ...values]); } get hashtags() { const tag = this.tags.find((tag2) => tag2[0] === "hashtags"); return tag ? tag.slice(1) : []; } get repo() { return this.tagValue("repo"); } get title() { return this.tagValue("title"); } set title(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } get picture() { return this.tagValue("picture"); } set picture(value) { this.removeTag("picture"); if (value) this.tags.push(["picture", value]); } set description(value) { this.content = value; } get description() { return this.content; } get slug() { return this.dTag ?? "empty-dtag"; } async getSigner() { if (this._signer) return this._signer; const encryptedKey = this.tagValue("key"); if (!encryptedKey) { this._signer = NDKPrivateKeySigner3.generate(); await this.encryptAndSaveNsec(); } else { const decryptedKey = await this.ndk?.signer?.decrypt(this.ndk.activeUser, encryptedKey); if (!decryptedKey) { throw new Error("Failed to decrypt project key or missing signer context."); } this._signer = new NDKPrivateKeySigner3(decryptedKey); } return this._signer; } async getNsec() { const signer = await this.getSigner(); return signer.privateKey; } async setNsec(value) { this._signer = new NDKPrivateKeySigner3(value); await this.encryptAndSaveNsec(); } async encryptAndSaveNsec() { if (!this._signer) throw new Error("Signer is not set."); const key = this._signer.privateKey; const encryptedKey = await this.ndk?.signer?.encrypt(this.ndk.activeUser, key); if (encryptedKey) { this.removeTag("key"); this.tags.push(["key", encryptedKey]); } } }, __publicField(_a53, "kind", 31933), __publicField(_a53, "kinds", [31933]), _a53); var _a54; var NDKProjectTemplate3 = (_a54 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind = 30717; } static from(event) { return new _a54(event.ndk, event.rawEvent()); } get templateId() { return this.dTag ?? ""; } set templateId(value) { this.dTag = value; } get name() { return this.tagValue("title") ?? ""; } set name(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } get description() { return this.tagValue("description") ?? ""; } set description(value) { this.removeTag("description"); if (value) this.tags.push(["description", value]); } get repoUrl() { return this.tagValue("uri") ?? ""; } set repoUrl(value) { this.removeTag("uri"); if (value) this.tags.push(["uri", value]); } get image() { return this.tagValue("image"); } set image(value) { this.removeTag("image"); if (value) this.tags.push(["image", value]); } get command() { return this.tagValue("command"); } set command(value) { this.removeTag("command"); if (value) this.tags.push(["command", value]); } get agentConfig() { const agentTag = this.tagValue("agent"); if (!agentTag) return; try { return JSON.parse(agentTag); } catch { return; } } set agentConfig(value) { this.removeTag("agent"); if (value) { this.tags.push(["agent", JSON.stringify(value)]); } } get templateTags() { return this.getMatchingTags("t").map((tag) => tag[1]).filter(Boolean); } set templateTags(values) { this.tags = this.tags.filter((tag) => tag[0] !== "t"); values.forEach((value) => { if (value) this.tags.push(["t", value]); }); } }, __publicField(_a54, "kind", 30717), __publicField(_a54, "kinds", [30717]), _a54); var READ_MARKER3 = "read"; var WRITE_MARKER3 = "write"; var _a55; var NDKRelayList3 = (_a55 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 10002); } static from(ndkEvent) { return new _a55(ndkEvent.ndk, ndkEvent.rawEvent()); } get readRelayUrls() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").filter((tag) => !tag[2] || tag[2] && tag[2] === READ_MARKER3).map((tag) => tryNormalizeRelayUrl3(tag[1])).filter((url) => !!url); } set readRelayUrls(relays) { for (const relay of relays) { this.tags.push(["r", relay, READ_MARKER3]); } } get writeRelayUrls() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").filter((tag) => !tag[2] || tag[2] && tag[2] === WRITE_MARKER3).map((tag) => tryNormalizeRelayUrl3(tag[1])).filter((url) => !!url); } set writeRelayUrls(relays) { for (const relay of relays) { this.tags.push(["r", relay, WRITE_MARKER3]); } } get bothRelayUrls() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").filter((tag) => !tag[2]).map((tag) => tag[1]); } set bothRelayUrls(relays) { for (const relay of relays) { this.tags.push(["r", relay]); } } get relays() { return this.tags.filter((tag) => tag[0] === "r" || tag[0] === "relay").map((tag) => tag[1]); } get relaySet() { if (!this.ndk) throw new Error("NDKRelayList has no NDK instance"); return new NDKRelaySet3(new Set(this.relays.map((u3) => this.ndk?.pool.getRelay(u3)).filter((r) => !!r)), this.ndk); } }, __publicField(_a55, "kind", 10002), __publicField(_a55, "kinds", [10002]), _a55); function relayListFromKind32(ndk, contactList) { try { const content = JSON.parse(contactList.content); const relayList = new NDKRelayList3(ndk); const readRelays = /* @__PURE__ */ new Set(); const writeRelays = /* @__PURE__ */ new Set(); for (let [key, config] of Object.entries(content)) { try { key = normalizeRelayUrl3(key); } catch { continue; } if (!config) { readRelays.add(key); writeRelays.add(key); } else { const relayConfig = config; if (relayConfig.write) writeRelays.add(key); if (relayConfig.read) readRelays.add(key); } } relayList.readRelayUrls = Array.from(readRelays); relayList.writeRelayUrls = Array.from(writeRelays); return relayList; } catch { } return; } var _a56; var NDKRelayFeedList3 = (_a56 = class extends NDKList3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); if (!rawEvent?.kind) { this.kind = 10012; } } static from(ndkEvent) { return new _a56(ndkEvent.ndk, ndkEvent); } get relayUrls() { return this.getMatchingTags("relay").map((tag) => tag[1]); } get relaySets() { return this.getMatchingTags("a").map((tag) => tag[1]); } async addRelay(relayUrl, mark, encrypted = false, position = "bottom") { const tag = ["relay", relayUrl]; if (mark) tag.push(mark); await this.addItem(tag, void 0, encrypted, position); } async addRelaySet(relaySetNaddr, mark, encrypted = false, position = "bottom") { const tag = ["a", relaySetNaddr]; if (mark) tag.push(mark); await this.addItem(tag, void 0, encrypted, position); } async removeRelay(relayUrl, publish = true) { await this.removeItemByValue(relayUrl, publish); } async removeRelaySet(relaySetNaddr, publish = true) { await this.removeItemByValue(relaySetNaddr, publish); } }, __publicField(_a56, "kind", 10012), __publicField(_a56, "kinds", [10012]), _a56); var _a57; var NDKRepost3 = (_a57 = class extends NDKEvent3 { constructor() { super(...arguments); __publicField(this, "_repostedEvents"); } static from(event) { return new _a57(event.ndk, event.rawEvent()); } async repostedEvents(klass, opts) { const items = []; if (!this.ndk) throw new Error("NDK instance not set"); if (this._repostedEvents !== void 0) return this._repostedEvents; for (const eventId of this.repostedEventIds()) { const filter = filterForId3(eventId); const event = await this.ndk.fetchEvent(filter, opts); if (event) { items.push(klass ? klass.from(event) : event); } } return items; } repostedEventIds() { return this.tags.filter((t) => t[0] === "e" || t[0] === "a").map((t) => t[1]); } }, __publicField(_a57, "kind", 6), __publicField(_a57, "kinds", [6, 16]), _a57); function filterForId3(id) { if (id.match(/:/)) { const [kind, pubkey, identifier] = id.split(":"); return { kinds: [Number.parseInt(kind)], authors: [pubkey], "#d": [identifier] }; } return { ids: [id] }; } var _a58; var NDKSimpleGroupMemberList3 = (_a58 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "relaySet"); __publicField(this, "memberSet", /* @__PURE__ */ new Set()); this.kind ?? (this.kind = 39002); this.memberSet = new Set(this.members); } static from(event) { return new _a58(event.ndk, event); } get members() { return this.getMatchingTags("p").map((tag) => tag[1]); } hasMember(member) { return this.memberSet.has(member); } async publish(relaySet, timeoutMs, requiredRelayCount) { relaySet ?? (relaySet = this.relaySet); return super.publishReplaceable(relaySet, timeoutMs, requiredRelayCount); } }, __publicField(_a58, "kind", 39002), __publicField(_a58, "kinds", [39002]), _a58); var _a59; var NDKSimpleGroupMetadata3 = (_a59 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 39e3); } static from(event) { return new _a59(event.ndk, event); } get name() { return this.tagValue("name"); } get picture() { return this.tagValue("picture"); } get about() { return this.tagValue("about"); } get scope() { if (this.getMatchingTags("public").length > 0) return "public"; if (this.getMatchingTags("public").length > 0) return "private"; return; } set scope(scope) { this.removeTag("public"); this.removeTag("private"); if (scope === "public") { this.tags.push(["public", ""]); } else if (scope === "private") { this.tags.push(["private", ""]); } } get access() { if (this.getMatchingTags("open").length > 0) return "open"; if (this.getMatchingTags("closed").length > 0) return "closed"; return; } set access(access) { this.removeTag("open"); this.removeTag("closed"); if (access === "open") { this.tags.push(["open", ""]); } else if (access === "closed") { this.tags.push(["closed", ""]); } } }, __publicField(_a59, "kind", 39e3), __publicField(_a59, "kinds", [39e3]), _a59); function strToPosition3(positionStr) { const [x2, y2] = positionStr.split(",").map(Number); return { x: x2, y: y2 }; } function strToDimension3(dimensionStr) { const [width, height] = dimensionStr.split("x").map(Number); return { width, height }; } var _a60; var NDKStorySticker3 = (_a60 = class { constructor(arg) { __publicField(this, "type"); __publicField(this, "value"); __publicField(this, "position"); __publicField(this, "dimension"); __publicField(this, "properties"); __publicField(this, "hasValidDimensions", () => { return typeof this.dimension.width === "number" && typeof this.dimension.height === "number" && !Number.isNaN(this.dimension.width) && !Number.isNaN(this.dimension.height); }); __publicField(this, "hasValidPosition", () => { return typeof this.position.x === "number" && typeof this.position.y === "number" && !Number.isNaN(this.position.x) && !Number.isNaN(this.position.y); }); if (Array.isArray(arg)) { const tag = arg; if (tag[0] !== "sticker" || tag.length < 5) { throw new Error("Invalid sticker tag"); } this.type = tag[1]; this.value = tag[2]; this.position = strToPosition3(tag[3]); this.dimension = strToDimension3(tag[4]); const props = {}; for (let i22 = 5; i22 < tag.length; i22++) { const [key, ...rest] = tag[i22].split(" "); props[key] = rest.join(" "); } if (Object.keys(props).length > 0) { this.properties = props; } } else { this.type = arg; this.value = void 0; this.position = { x: 0, y: 0 }; this.dimension = { width: 0, height: 0 }; } } static fromTag(tag) { try { return new _a60(tag); } catch { return null; } } get style() { return this.properties?.style; } set style(style) { if (style) this.properties = { ...this.properties, style }; else delete this.properties?.style; } get rotation() { return this.properties?.rot ? Number.parseFloat(this.properties.rot) : void 0; } set rotation(rotation) { if (rotation !== void 0) { this.properties = { ...this.properties, rot: rotation.toString() }; } else { delete this.properties?.rot; } } get isValid() { return this.hasValidDimensions() && this.hasValidPosition(); } toTag() { if (!this.isValid) { const errors = [ !this.hasValidDimensions() ? "dimensions is invalid" : void 0, !this.hasValidPosition() ? "position is invalid" : void 0 ].filter(Boolean); throw new Error(`Invalid sticker: ${errors.join(", ")}`); } let value; switch (this.type) { case "event": value = this.value.tagId(); break; case "pubkey": value = this.value.pubkey; break; default: value = this.value; } const tag = ["sticker", this.type, value, coordinates3(this.position), dimension3(this.dimension)]; if (this.properties) { for (const [key, propValue] of Object.entries(this.properties)) { tag.push(`${key} ${propValue}`); } } return tag; } }, __publicField(_a60, "Text", "text"), __publicField(_a60, "Pubkey", "pubkey"), __publicField(_a60, "Event", "event"), __publicField(_a60, "Prompt", "prompt"), __publicField(_a60, "Countdown", "countdown"), _a60); var _a61; var NDKStory3 = (_a61 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "_imeta"); __publicField(this, "_dimensions"); this.kind ?? (this.kind = 23); if (rawEvent) { for (const tag of rawEvent.tags) { switch (tag[0]) { case "imeta": this._imeta = mapImetaTag3(tag); break; case "dim": this.dimensions = strToDimension3(tag[1]); break; } } } } static from(event) { return new _a61(event.ndk, event); } get isValid() { return !!this.imeta; } get imeta() { return this._imeta; } set imeta(tag) { this._imeta = tag; this.tags = this.tags.filter((t) => t[0] !== "imeta"); if (tag) { this.tags.push(imetaTagToTag3(tag)); } } get dimensions() { const dimTag = this.tagValue("dim"); if (!dimTag) return; return strToDimension3(dimTag); } set dimensions(dimensions) { this.removeTag("dim"); if (dimensions) { this.tags.push(["dim", `${dimensions.width}x${dimensions.height}`]); } } get duration() { const durTag = this.tagValue("dur"); if (!durTag) return; return Number.parseInt(durTag); } set duration(duration) { this.removeTag("dur"); if (duration !== void 0) { this.tags.push(["dur", duration.toString()]); } } get stickers() { const stickers = []; for (const tag of this.tags) { if (tag[0] !== "sticker" || tag.length < 5) continue; const sticker = NDKStorySticker3.fromTag(tag); if (sticker) stickers.push(sticker); } return stickers; } addSticker(sticker) { let stickerToAdd; if (sticker instanceof NDKStorySticker3) { stickerToAdd = sticker; } else { const tag = [ "sticker", sticker.type, typeof sticker.value === "string" ? sticker.value : "", coordinates3(sticker.position), dimension3(sticker.dimension) ]; if (sticker.properties) { for (const [key, value] of Object.entries(sticker.properties)) { tag.push(`${key} ${value}`); } } stickerToAdd = new NDKStorySticker3(tag); stickerToAdd.value = sticker.value; } if (stickerToAdd.type === "pubkey") { this.tag(stickerToAdd.value); } else if (stickerToAdd.type === "event") { this.tag(stickerToAdd.value); } this.tags.push(stickerToAdd.toTag()); } removeSticker(index) { const stickers = this.stickers; if (index < 0 || index >= stickers.length) return; let stickerCount = 0; for (let i22 = 0; i22 < this.tags.length; i22++) { if (this.tags[i22][0] === "sticker") { if (stickerCount === index) { this.tags.splice(i22, 1); break; } stickerCount++; } } } }, __publicField(_a61, "kind", 23), __publicField(_a61, "kinds", [23]), _a61); var coordinates3 = (position) => `${position.x},${position.y}`; var dimension3 = (dimension22) => `${dimension22.width}x${dimension22.height}`; var _a62; var NDKSubscriptionReceipt3 = (_a62 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "debug"); this.kind ?? (this.kind = 7003); this.debug = ndk?.debug.extend("subscription-start") ?? import_debug52.default("ndk:subscription-start"); } static from(event) { return new _a62(event.ndk, event.rawEvent()); } get recipient() { const pTag = this.getMatchingTags("p")?.[0]; if (!pTag) return; const user = new NDKUser3({ pubkey: pTag[1] }); return user; } set recipient(user) { this.removeTag("p"); if (!user) return; this.tags.push(["p", user.pubkey]); } get subscriber() { const PTag = this.getMatchingTags("P")?.[0]; if (!PTag) return; const user = new NDKUser3({ pubkey: PTag[1] }); return user; } set subscriber(user) { this.removeTag("P"); if (!user) return; this.tags.push(["P", user.pubkey]); } set subscriptionStart(event) { this.debug(`before setting subscription start: ${this.rawEvent}`); this.removeTag("e"); this.tag(event, "subscription", true); this.debug(`after setting subscription start: ${this.rawEvent}`); } get tierName() { const tag = this.getMatchingTags("tier")?.[0]; return tag?.[1]; } get isValid() { const period = this.validPeriod; if (!period) { return false; } if (period.start > period.end) { return false; } const pTags = this.getMatchingTags("p"); const PTags = this.getMatchingTags("P"); if (pTags.length !== 1 || PTags.length !== 1) { return false; } return true; } get validPeriod() { const tag = this.getMatchingTags("valid")?.[0]; if (!tag) return; try { return { start: new Date(Number.parseInt(tag[1]) * 1e3), end: new Date(Number.parseInt(tag[2]) * 1e3) }; } catch { return; } } set validPeriod(period) { this.removeTag("valid"); if (!period) return; this.tags.push([ "valid", Math.floor(period.start.getTime() / 1e3).toString(), Math.floor(period.end.getTime() / 1e3).toString() ]); } get startPeriod() { return this.validPeriod?.start; } get endPeriod() { return this.validPeriod?.end; } isActive(time) { time ?? (time = /* @__PURE__ */ new Date()); const period = this.validPeriod; if (!period) return false; if (time < period.start) return false; if (time > period.end) return false; return true; } }, __publicField(_a62, "kind", 7003), __publicField(_a62, "kinds", [7003]), _a62); var possibleIntervalFrequencies3 = [ "daily", "weekly", "monthly", "quarterly", "yearly" ]; function newAmount3(amount, currency, term) { return ["amount", amount.toString(), currency, term]; } function parseTagToSubscriptionAmount3(tag) { const amount = Number.parseInt(tag[1]); if (Number.isNaN(amount) || amount === void 0 || amount === null || amount <= 0) return; const currency = tag[2]; if (currency === void 0 || currency === "") return; const term = tag[3]; if (term === void 0) return; if (!possibleIntervalFrequencies3.includes(term)) return; return { amount, currency, term }; } var _a63; var NDKSubscriptionTier3 = (_a63 = class extends NDKArticle3 { constructor(ndk, rawEvent) { const k2 = rawEvent?.kind ?? 37001; super(ndk, rawEvent); this.kind = k2; } static from(event) { return new _a63(event.ndk, event); } get perks() { return this.getMatchingTags("perk").map((tag) => tag[1]).filter((perk) => perk !== void 0); } addPerk(perk) { this.tags.push(["perk", perk]); } get amounts() { return this.getMatchingTags("amount").map((tag) => parseTagToSubscriptionAmount3(tag)).filter((a) => a !== void 0); } addAmount(amount, currency, term) { this.tags.push(newAmount3(amount, currency, term)); } set relayUrl(relayUrl) { this.tags.push(["r", relayUrl]); } get relayUrls() { return this.getMatchingTags("r").map((tag) => tag[1]).filter((relay) => relay !== void 0); } get verifierPubkey() { return this.tagValue("p"); } set verifierPubkey(pubkey) { this.removeTag("p"); if (pubkey) this.tags.push(["p", pubkey]); } get isValid() { return this.title !== void 0 && this.amounts.length > 0; } }, __publicField(_a63, "kind", 37001), __publicField(_a63, "kinds", [37001]), _a63); var _a64; var NDKSubscriptionStart3 = (_a64 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); __publicField(this, "debug"); this.kind ?? (this.kind = 7001); this.debug = ndk?.debug.extend("subscription-start") ?? import_debug62.default("ndk:subscription-start"); } static from(event) { return new _a64(event.ndk, event.rawEvent()); } get recipient() { const pTag = this.getMatchingTags("p")?.[0]; if (!pTag) return; const user = new NDKUser3({ pubkey: pTag[1] }); return user; } set recipient(user) { this.removeTag("p"); if (!user) return; this.tags.push(["p", user.pubkey]); } get amount() { const amountTag = this.getMatchingTags("amount")?.[0]; if (!amountTag) return; return parseTagToSubscriptionAmount3(amountTag); } set amount(amount) { this.removeTag("amount"); if (!amount) return; this.tags.push(newAmount3(amount.amount, amount.currency, amount.term)); } get tierId() { const eTag = this.getMatchingTags("e")?.[0]; const aTag = this.getMatchingTags("a")?.[0]; if (!eTag || !aTag) return; return eTag[1] ?? aTag[1]; } set tier(tier) { this.removeTag("e"); this.removeTag("a"); this.removeTag("event"); if (!tier) return; this.tag(tier); this.removeTag("p"); this.tags.push(["p", tier.pubkey]); this.tags.push(["event", JSON.stringify(tier.rawEvent())]); } async fetchTier() { const eventTag = this.tagValue("event"); if (eventTag) { try { const parsedEvent = JSON.parse(eventTag); return new NDKSubscriptionTier3(this.ndk, parsedEvent); } catch { this.debug("Failed to parse event tag"); } } const tierId = this.tierId; if (!tierId) return; const e2 = await this.ndk?.fetchEvent(tierId); if (!e2) return; return NDKSubscriptionTier3.from(e2); } get isValid() { if (this.getMatchingTags("amount").length !== 1) { this.debug("Invalid # of amount tag"); return false; } if (!this.amount) { this.debug("Invalid amount tag"); return false; } if (this.getMatchingTags("p").length !== 1) { this.debug("Invalid # of p tag"); return false; } if (!this.recipient) { this.debug("Invalid p tag"); return false; } return true; } }, __publicField(_a64, "kind", 7001), __publicField(_a64, "kinds", [7001]), _a64); var _a65; var NDKTask3 = (_a65 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind = 1934; } static from(event) { return new _a65(event.ndk, event.rawEvent()); } set title(value) { this.removeTag("title"); if (value) this.tags.push(["title", value]); } get title() { return this.tagValue("title"); } set project(project) { this.removeTag("a"); this.tags.push(project.tagReference()); } get projectSlug() { const tag = this.getMatchingTags("a")[0]; return tag ? tag[1].split(/:/)?.[2] : void 0; } }, __publicField(_a65, "kind", 1934), __publicField(_a65, "kinds", [1934]), _a65); var _a66; var NDKThread3 = (_a66 = class extends NDKEvent3 { constructor(ndk, rawEvent) { super(ndk, rawEvent); this.kind ?? (this.kind = 11); } static from(event) { return new _a66(event.ndk, event); } get title() { return this.tagValue("title"); } set title(title) { this.removeTag("title"); if (title) { this.tags.push(["title", title]); } } }, __publicField(_a66, "kind", 11), __publicField(_a66, "kinds", [11]), _a66); var _a67; var NDKVideo3 = (_a67 = class extends NDKEvent3 { constructor() { super(...arguments); __publicField(this, "_imetas"); } static from(event) { return new _a67(event.ndk, event.rawEvent()); } get title() { return this.tagValue("title"); } set title(title) { this.removeTag("title"); if (title) this.tags.push(["title", title]); } get thumbnail() { let thumbnail; if (this.imetas && this.imetas.length > 0) { thumbnail = this.imetas[0].image?.[0]; } return thumbnail ?? this.tagValue("thumb"); } get imetas() { if (this._imetas) return this._imetas; this._imetas = this.tags.filter((tag) => tag[0] === "imeta").map(mapImetaTag3); return this._imetas; } set imetas(tags) { this._imetas = tags; this.tags = this.tags.filter((tag) => tag[0] !== "imeta"); this.tags.push(...tags.map(imetaTagToTag3)); } get url() { if (this.imetas && this.imetas.length > 0) { return this.imetas[0].url; } return this.tagValue("url"); } get published_at() { const tag = this.tagValue("published_at"); if (tag) { return Number.parseInt(tag); } return; } async generateTags() { super.generateTags(); if (!this.kind) { if (this.imetas?.[0]?.dim) { const [width, height] = this.imetas[0].dim.split("x"); const isPortrait = width && height && Number.parseInt(width) < Number.parseInt(height); const isShort = this.duration && this.duration < 120; if (isShort && isPortrait) this.kind = 22; else this.kind = 21; } } return super.generateTags(); } get duration() { const tag = this.tagValue("duration"); if (tag) { return Number.parseInt(tag); } return; } set duration(dur) { this.removeTag("duration"); if (dur !== void 0) { this.tags.push(["duration", Math.floor(dur).toString()]); } } }, __publicField(_a67, "kind", 21), __publicField(_a67, "kinds", [34235, 34236, 22, 21]), _a67); var _a68; var NDKWiki3 = (_a68 = class extends NDKArticle3 { static from(event) { return new _a68(event.ndk, event.rawEvent()); } get isDefered() { return this.hasTag("a", "defer"); } get deferedId() { return this.tagValue("a", "defer"); } set defer(deferedTo) { this.removeTag("a", "defer"); this.tag(deferedTo, "defer"); } }, __publicField(_a68, "kind", 30818), __publicField(_a68, "kinds", [30818]), _a68); var _a69; var NDKWikiMergeRequest3 = (_a69 = class extends NDKEvent3 { static from(event) { return new _a69(event.ndk, event.rawEvent()); } get targetId() { return this.tagValue("a"); } set target(targetEvent) { this.tags = this.tags.filter((tag) => { if (tag[0] === "a") return true; if (tag[0] === "e" && tag[3] !== "source") return true; }); this.tag(targetEvent); } get sourceId() { return this.tagValue("e", "source"); } set source(sourceEvent) { this.removeTag("e", "source"); this.tag(sourceEvent, "source", false, "e"); } }, __publicField(_a69, "kind", 818), __publicField(_a69, "kinds", [818]), _a69); var registeredEventClasses3 = /* @__PURE__ */ new Set(); function wrapEvent32(event) { const eventWrappingMap = /* @__PURE__ */ new Map(); const builtInClasses = [ NDKImage3, NDKVideo3, NDKCashuMintList3, NDKArticle3, NDKHighlight3, NDKDraft3, NDKWiki3, NDKWikiMergeRequest3, NDKNutzap3, NDKProject3, NDKTask3, NDKProjectTemplate3, NDKSimpleGroupMemberList3, NDKSimpleGroupMetadata3, NDKSubscriptionTier3, NDKSubscriptionStart3, NDKSubscriptionReceipt3, NDKList3, NDKRelayList3, NDKRelayFeedList3, NDKStory3, NDKBlossomList3, NDKFollowPack3, NDKThread3, NDKRepost3, NDKClassified3, NDKAppHandlerEvent3, NDKDVMJobFeedback3, NDKCashuMintAnnouncement3, NDKFedimintMint3, NDKMintRecommendation3 ]; const allClasses = [...builtInClasses, ...registeredEventClasses3]; for (const klass2 of allClasses) { for (const kind of klass2.kinds) { eventWrappingMap.set(kind, klass2); } } const klass = eventWrappingMap.get(event.kind); if (klass) return klass.from(event); return event; } function checkMissingKind2(event, error) { if (event.kind === void 0 || event.kind === null) { error("event-missing-kind", `Cannot sign event without 'kind'. \u{1F4E6} Event data: \u2022 content: ${event.content ? `"${event.content.substring(0, 50)}${event.content.length > 50 ? "..." : ""}"` : "(empty)"} \u2022 tags: ${event.tags.length} tag${event.tags.length !== 1 ? "s" : ""} \u2022 kind: ${event.kind} \u274C Set event.kind before signing.`, "Example: event.kind = 1; // for text note", false); } } function checkContentIsObject2(event, error) { if (typeof event.content === "object") { const contentPreview = JSON.stringify(event.content, null, 2).substring(0, 200); error("event-content-is-object", `Event content is an object. Content must be a string. \u{1F4E6} Your content (${typeof event.content}): ${contentPreview}${JSON.stringify(event.content).length > 200 ? "..." : ""} \u274C event.content = { ... } // WRONG \u2705 event.content = JSON.stringify({ ... }) // CORRECT`, "Use JSON.stringify() for structured data: event.content = JSON.stringify(data)", false); } } function checkCreatedAtMilliseconds2(event, error) { if (event.created_at && event.created_at > 1e10) { const correctValue = Math.floor(event.created_at / 1e3); const dateString = new Date(event.created_at).toISOString(); error("event-created-at-milliseconds", `Event created_at is in milliseconds, not seconds. \u{1F4E6} Your value: \u2022 created_at: ${event.created_at} \u274C \u2022 Interpreted as: ${dateString} \u2022 Should be: ${correctValue} \u2705 Nostr timestamps MUST be in seconds since Unix epoch.`, "Use Math.floor(Date.now() / 1000) instead of Date.now()", false); } } function checkInvalidPTags2(event, error) { const pTags = event.getMatchingTags("p"); pTags.forEach((tag, idx) => { if (tag[1] && !/^[0-9a-f]{64}$/i.test(tag[1])) { const tagPreview = JSON.stringify(tag); error("tag-invalid-p-tag", `p-tag[${idx}] has invalid pubkey. \u{1F4E6} Your tag: ${tagPreview} \u274C Invalid value: "${tag[1]}" \u2022 Length: ${tag[1].length} (expected 64) \u2022 Format: ${tag[1].startsWith("npub") ? "bech32 (npub)" : "unknown"} p-tags MUST contain 64-character hex pubkeys.`, tag[1].startsWith("npub") ? `Use ndkUser.pubkey instead of npub: \u2705 event.tags.push(['p', ndkUser.pubkey]) \u274C event.tags.push(['p', 'npub1...'])` : "p-tags must contain valid hex pubkeys (64 characters, 0-9a-f)", false); } }); } function checkInvalidETags2(event, error) { const eTags = event.getMatchingTags("e"); eTags.forEach((tag, idx) => { if (tag[1] && !/^[0-9a-f]{64}$/i.test(tag[1])) { const tagPreview = JSON.stringify(tag); const isBech32 = tag[1].startsWith("note") || tag[1].startsWith("nevent"); error("tag-invalid-e-tag", `e-tag[${idx}] has invalid event ID. \u{1F4E6} Your tag: ${tagPreview} \u274C Invalid value: "${tag[1]}" \u2022 Length: ${tag[1].length} (expected 64) \u2022 Format: ${isBech32 ? "bech32 (note/nevent)" : "unknown"} e-tags MUST contain 64-character hex event IDs.`, isBech32 ? `Use event.id instead of bech32: \u2705 event.tags.push(['e', referencedEvent.id]) \u274C event.tags.push(['e', 'note1...'])` : "e-tags must contain valid hex event IDs (64 characters, 0-9a-f)", false); } }); } function checkManualReplyMarkers2(event, warn, replyEvents) { if (event.kind !== 1) return; if (replyEvents.has(event)) return; const eTagsWithMarkers = event.tags.filter((tag) => tag[0] === "e" && (tag[3] === "reply" || tag[3] === "root")); if (eTagsWithMarkers.length > 0) { const tagList = eTagsWithMarkers.map((tag, idx) => ` ${idx + 1}. ${JSON.stringify(tag)}`).join(` `); warn("event-manual-reply-markers", `Event has ${eTagsWithMarkers.length} e-tag(s) with manual reply/root markers. \u{1F4E6} Your tags with markers: ${tagList} \u26A0\uFE0F Manual reply markers detected! This will cause incorrect threading.`, `Reply events MUST be created using .reply(): \u2705 CORRECT: const replyEvent = originalEvent.reply(); replyEvent.content = 'good point!'; await replyEvent.publish(); \u274C WRONG: event.tags.push(['e', eventId, '', 'reply']); NDK handles all reply threading automatically - never add reply/root markers manually.`); } } function checkHashtagsWithPrefix2(event, error) { const tTags = event.getMatchingTags("t"); tTags.forEach((tag, idx) => { if (tag[1] && tag[1].startsWith("#")) { const tagPreview = JSON.stringify(tag); error("tag-hashtag-with-prefix", `t-tag[${idx}] contains hashtag with # prefix. \u{1F4E6} Your tag: ${tagPreview} \u274C Invalid value: "${tag[1]}" Hashtag tags should NOT include the # symbol.`, `Remove the # prefix from hashtag tags: \u2705 event.tags.push(['t', 'nostr']) \u274C event.tags.push(['t', '#nostr'])`, false); } }); } function checkReplaceableWithOldTimestamp2(event, warn) { if (event.kind === void 0 || event.kind === null || !event.created_at) return; if (!event.isReplaceable()) return; const nowSeconds = Math.floor(Date.now() / 1e3); const ageSeconds = nowSeconds - event.created_at; const TEN_SECONDS = 10; if (ageSeconds > TEN_SECONDS) { const ageMinutes = Math.floor(ageSeconds / 60); const ageDescription = ageMinutes > 0 ? `${ageMinutes} minute${ageMinutes !== 1 ? "s" : ""}` : `${ageSeconds} seconds`; warn("event-replaceable-old-timestamp", `Publishing a replaceable event with an old created_at timestamp. \u{1F4E6} Event details: \u2022 kind: ${event.kind} (replaceable) \u2022 created_at: ${event.created_at} \u2022 age: ${ageDescription} old \u2022 current time: ${nowSeconds} \u26A0\uFE0F This is wrong and will be rejected by relays.`, `For replaceable events, use publishReplaceable(): \u2705 CORRECT: await event.publishReplaceable(); // Automatically updates created_at to now \u274C WRONG: await event.publish(); // Uses old created_at`); } } function signing2(event, error, warn, replyEvents) { checkMissingKind2(event, error); checkContentIsObject2(event, error); checkCreatedAtMilliseconds2(event, error); checkInvalidPTags2(event, error); checkInvalidETags2(event, error); checkHashtagsWithPrefix2(event, error); checkManualReplyMarkers2(event, warn, replyEvents); } function publishing2(event, warn) { checkReplaceableWithOldTimestamp2(event, warn); } function isNip33Pattern2(filters) { const filterArray = Array.isArray(filters) ? filters : [filters]; if (filterArray.length !== 1) return false; const filter = filterArray[0]; return filter.kinds && Array.isArray(filter.kinds) && filter.kinds.length === 1 && filter.authors && Array.isArray(filter.authors) && filter.authors.length === 1 && filter["#d"] && Array.isArray(filter["#d"]) && filter["#d"].length === 1; } function isReplaceableEventFilter2(filters) { const filterArray = Array.isArray(filters) ? filters : [filters]; if (filterArray.length === 0) { return false; } return filterArray.every((filter) => { if (!filter.kinds || !Array.isArray(filter.kinds) || filter.kinds.length === 0) { return false; } if (!filter.authors || !Array.isArray(filter.authors) || filter.authors.length === 0) { return false; } const allKindsReplaceable = filter.kinds.every((kind) => { return kind === 0 || kind === 3 || kind >= 1e4 && kind <= 19999; }); return allKindsReplaceable; }); } function formatFilter2(filter) { const formatted = JSON.stringify(filter, null, 2); return formatted.split(` `).map((line, idx) => idx === 0 ? line : ` ${line}`).join(` `); } function fetchingEvents2(filters, opts, warn, shouldWarnRatio, incrementCount) { incrementCount(); if (opts?.cacheUsage === "ONLY_CACHE") { return; } const filterArray = Array.isArray(filters) ? filters : [filters]; const formattedFilters = filterArray.map(formatFilter2).join(` --- `); if (isNip33Pattern2(filters)) { const filter = filterArray[0]; warn("fetch-events-usage", `For fetching a NIP-33 addressable event, use fetchEvent() with the naddr directly. \u{1F4E6} Your filter: ` + formattedFilters + ` \u274C BAD: const decoded = nip19.decode(naddr); const events = await ndk.fetchEvents({ kinds: [decoded.data.kind], authors: [decoded.data.pubkey], "#d": [decoded.data.identifier] }); const event = Array.from(events)[0]; \u2705 GOOD: const event = await ndk.fetchEvent(naddr); \u2705 GOOD: const event = await ndk.fetchEvent('naddr1...'); fetchEvent() handles naddr decoding automatically and returns the event directly.`); } else if (isReplaceableEventFilter2(filters)) { return; } else { if (!shouldWarnRatio()) { return; } let filterAnalysis = ""; const hasLimit = filterArray.some((f) => f.limit !== void 0); const totalKinds = new Set(filterArray.flatMap((f) => f.kinds || [])).size; const totalAuthors = new Set(filterArray.flatMap((f) => f.authors || [])).size; if (hasLimit) { const maxLimit = Math.max(...filterArray.map((f) => f.limit || 0)); filterAnalysis += ` \u2022 Limit: ${maxLimit} event${maxLimit !== 1 ? "s" : ""}`; } if (totalKinds > 0) { filterAnalysis += ` \u2022 Kinds: ${totalKinds} type${totalKinds !== 1 ? "s" : ""}`; } if (totalAuthors > 0) { filterAnalysis += ` \u2022 Authors: ${totalAuthors} author${totalAuthors !== 1 ? "s" : ""}`; } warn("fetch-events-usage", `fetchEvents() is a BLOCKING operation that waits for EOSE. In most cases, you should use subscribe() instead. \u{1F4E6} Your filter` + (filterArray.length > 1 ? "s" : "") + `: ` + formattedFilters + (filterAnalysis ? ` \u{1F4CA} Filter analysis:` + filterAnalysis : "") + ` \u274C BAD: const events = await ndk.fetchEvents(filter); \u2705 GOOD: ndk.subscribe(filter, { onEvent: (e) => ... }); Only use fetchEvents() when you MUST block until data arrives.`, "For one-time queries, use fetchEvent() instead of fetchEvents() when expecting a single result."); } } var GuardrailCheckId2 = { NDK_NO_CACHE: "ndk-no-cache", FILTER_BECH32_IN_ARRAY: "filter-bech32-in-array", FILTER_INVALID_HEX: "filter-invalid-hex", FILTER_ONLY_LIMIT: "filter-only-limit", FILTER_LARGE_LIMIT: "filter-large-limit", FILTER_EMPTY: "filter-empty", FILTER_SINCE_AFTER_UNTIL: "filter-since-after-until", FILTER_INVALID_A_TAG: "filter-invalid-a-tag", FILTER_HASHTAG_WITH_PREFIX: "filter-hashtag-with-prefix", FETCH_EVENTS_USAGE: "fetch-events-usage", EVENT_MISSING_KIND: "event-missing-kind", EVENT_PARAM_REPLACEABLE_NO_DTAG: "event-param-replaceable-no-dtag", EVENT_CREATED_AT_MILLISECONDS: "event-created-at-milliseconds", EVENT_NO_NDK_INSTANCE: "event-no-ndk-instance", EVENT_CONTENT_IS_OBJECT: "event-content-is-object", EVENT_MODIFIED_AFTER_SIGNING: "event-modified-after-signing", EVENT_MANUAL_REPLY_MARKERS: "event-manual-reply-markers", TAG_E_FOR_PARAM_REPLACEABLE: "tag-e-for-param-replaceable", TAG_BECH32_VALUE: "tag-bech32-value", TAG_DUPLICATE: "tag-duplicate", TAG_INVALID_P_TAG: "tag-invalid-p-tag", TAG_INVALID_E_TAG: "tag-invalid-e-tag", TAG_HASHTAG_WITH_PREFIX: "tag-hashtag-with-prefix", SUBSCRIBE_NOT_STARTED: "subscribe-not-started", SUBSCRIBE_CLOSE_ON_EOSE_NO_HANDLER: "subscribe-close-on-eose-no-handler", SUBSCRIBE_PASSED_EVENT_NOT_FILTER: "subscribe-passed-event-not-filter", SUBSCRIBE_AWAITED: "subscribe-awaited", RELAY_INVALID_URL: "relay-invalid-url", RELAY_HTTP_INSTEAD_OF_WS: "relay-http-instead-of-ws", RELAY_NO_ERROR_HANDLERS: "relay-no-error-handlers", VALIDATION_PUBKEY_IS_NPUB: "validation-pubkey-is-npub", VALIDATION_PUBKEY_WRONG_LENGTH: "validation-pubkey-wrong-length", VALIDATION_EVENT_ID_IS_BECH32: "validation-event-id-is-bech32", VALIDATION_EVENT_ID_WRONG_LENGTH: "validation-event-id-wrong-length" }; function checkCachePresence2(ndk, shouldCheck) { if (!shouldCheck(GuardrailCheckId2.NDK_NO_CACHE)) return; setTimeout(() => { if (!ndk.cacheAdapter) { const isBrowser = typeof window !== "undefined"; const suggestion = isBrowser ? "Consider using @nostr-dev-kit/ndk-cache-dexie or @nostr-dev-kit/ndk-cache-sqlite-wasm" : "Consider using @nostr-dev-kit/ndk-cache-redis or @nostr-dev-kit/ndk-cache-sqlite"; const message = ` \u{1F916} AI_GUARDRAILS WARNING: NDK initialized without a cache adapter. Apps perform significantly better with caching. \u{1F4A1} ${suggestion} \u{1F507} To disable this check: ndk.aiGuardrails.skip('${GuardrailCheckId2.NDK_NO_CACHE}') or set: ndk.aiGuardrails = { skip: new Set(['${GuardrailCheckId2.NDK_NO_CACHE}']) }`; console.warn(message); } }, 2500); } var AIGuardrails2 = class { constructor(mode = false) { __publicField(this, "enabled", false); __publicField(this, "skipSet", /* @__PURE__ */ new Set()); __publicField(this, "extensions", /* @__PURE__ */ new Map()); __publicField(this, "_nextCallDisabled", null); __publicField(this, "_replyEvents", /* @__PURE__ */ new WeakSet()); __publicField(this, "_fetchEventsCount", 0); __publicField(this, "_subscribeCount", 0); __publicField(this, "ndk", { fetchingEvents: (filters, opts) => { if (!this.enabled) return; fetchingEvents2(filters, opts, this.warn.bind(this), this.shouldWarnAboutFetchEventsRatio.bind(this), this.incrementFetchEventsCount.bind(this)); } }); __publicField(this, "event", { signing: (event) => { if (!this.enabled) return; signing2(event, this.error.bind(this), this.warn.bind(this), this._replyEvents); }, publishing: (event) => { if (!this.enabled) return; publishing2(event, this.warn.bind(this)); }, received: (_event, _relay) => { if (!this.enabled) return; }, creatingReply: (event) => { if (!this.enabled) return; this._replyEvents.add(event); } }); __publicField(this, "subscription", { created: (_filters, _opts) => { if (!this.enabled) return; this.incrementSubscribeCount(); } }); __publicField(this, "relay", { connected: (_relay) => { if (!this.enabled) return; } }); this.setMode(mode); } register(namespace, hooks) { if (this.extensions.has(namespace)) { console.warn(`AIGuardrails: Extension '${namespace}' already registered, overwriting`); } const wrappedHooks = {}; for (const [key, fn] of Object.entries(hooks)) { if (typeof fn === "function") { wrappedHooks[key] = (...args) => { if (!this.enabled) return; fn(...args, this.shouldCheck.bind(this), this.error.bind(this), this.warn.bind(this)); }; } } this.extensions.set(namespace, wrappedHooks); this[namespace] = wrappedHooks; } setMode(mode) { if (typeof mode === "boolean") { this.enabled = mode; this.skipSet.clear(); } else if (mode && typeof mode === "object") { this.enabled = true; this.skipSet = mode.skip || /* @__PURE__ */ new Set(); } } isEnabled() { return this.enabled; } shouldCheck(id) { if (!this.enabled) return false; if (this.skipSet.has(id)) return false; if (this._nextCallDisabled === "all") return false; if (this._nextCallDisabled && this._nextCallDisabled.has(id)) return false; return true; } skip(id) { this.skipSet.add(id); } enable(id) { this.skipSet.delete(id); } getSkipped() { return Array.from(this.skipSet); } captureAndClearNextCallDisabled() { const captured = this._nextCallDisabled; this._nextCallDisabled = null; return captured; } incrementFetchEventsCount() { this._fetchEventsCount++; } incrementSubscribeCount() { this._subscribeCount++; } shouldWarnAboutFetchEventsRatio() { const totalCalls = this._fetchEventsCount + this._subscribeCount; if (totalCalls <= 6) { return false; } const ratio = this._fetchEventsCount / totalCalls; return ratio > 0.5; } error(id, message, hint, canDisable = true) { if (!this.shouldCheck(id)) return; const fullMessage = this.formatMessage(id, "ERROR", message, hint, canDisable); console.error(fullMessage); throw new Error(fullMessage); } warn(id, message, hint) { if (!this.shouldCheck(id)) return; const fullMessage = this.formatMessage(id, "WARNING", message, hint, true); console.error(fullMessage); throw new Error(fullMessage); } formatMessage(id, level, message, hint, canDisable = true) { let output4 = ` \u{1F916} AI_GUARDRAILS ${level}: ${message}`; if (hint) { output4 += ` \u{1F4A1} ${hint}`; } if (canDisable) { output4 += ` \u{1F507} To disable this check: ndk.guardrailOff('${id}').yourMethod() // For one call`; output4 += ` ndk.aiGuardrails.skip('${id}') // Permanently`; output4 += ` or set: ndk.aiGuardrails = { skip: new Set(['${id}']) }`; } return output4; } ndkInstantiated(ndk) { if (!this.enabled) return; checkCachePresence2(ndk, this.shouldCheck.bind(this)); } }; function processFilters2(filters, mode = "validate", debug92, ndk) { if (mode === "ignore") { return filters; } const issues = []; const processedFilters = filters.map((filter, index) => { if (ndk?.aiGuardrails.isEnabled()) { runAIGuardrailsForFilter2(filter, index, ndk); } const result = processFilter2(filter, mode, index, issues, debug92); return result; }); if (mode === "validate" && issues.length > 0) { throw new Error(`Invalid filter(s) detected: ${issues.join(` `)}`); } return processedFilters; } function processFilter2(filter, mode, filterIndex, issues, debug92) { const isValidating = mode === "validate"; const cleanedFilter = isValidating ? filter : { ...filter }; if (filter.ids) { const validIds = []; filter.ids.forEach((id, idx) => { if (id === void 0) { if (isValidating) { issues.push(`Filter[${filterIndex}].ids[${idx}] is undefined`); } else { debug92?.(`Fixed: Removed undefined value at ids[${idx}]`); } } else if (typeof id !== "string") { if (isValidating) { issues.push(`Filter[${filterIndex}].ids[${idx}] is not a string (got ${typeof id})`); } else { debug92?.(`Fixed: Removed non-string value at ids[${idx}] (was ${typeof id})`); } } else if (!isValidHex643(id)) { if (isValidating) { issues.push(`Filter[${filterIndex}].ids[${idx}] is not a valid 64-char hex string: "${id}"`); } else { debug92?.(`Fixed: Removed invalid hex string at ids[${idx}]`); } } else { validIds.push(id); } }); if (!isValidating) { cleanedFilter.ids = validIds.length > 0 ? validIds : void 0; } } if (filter.authors) { const validAuthors = []; filter.authors.forEach((author, idx) => { if (author === void 0) { if (isValidating) { issues.push(`Filter[${filterIndex}].authors[${idx}] is undefined`); } else { debug92?.(`Fixed: Removed undefined value at authors[${idx}]`); } } else if (typeof author !== "string") { if (isValidating) { issues.push(`Filter[${filterIndex}].authors[${idx}] is not a string (got ${typeof author})`); } else { debug92?.(`Fixed: Removed non-string value at authors[${idx}] (was ${typeof author})`); } } else if (!isValidHex643(author)) { if (isValidating) { issues.push(`Filter[${filterIndex}].authors[${idx}] is not a valid 64-char hex pubkey: "${author}"`); } else { debug92?.(`Fixed: Removed invalid hex pubkey at authors[${idx}]`); } } else { validAuthors.push(author); } }); if (!isValidating) { cleanedFilter.authors = validAuthors.length > 0 ? validAuthors : void 0; } } if (filter.kinds) { const validKinds = []; filter.kinds.forEach((kind, idx) => { if (kind === void 0) { if (isValidating) { issues.push(`Filter[${filterIndex}].kinds[${idx}] is undefined`); } else { debug92?.(`Fixed: Removed undefined value at kinds[${idx}]`); } } else if (typeof kind !== "number") { if (isValidating) { issues.push(`Filter[${filterIndex}].kinds[${idx}] is not a number (got ${typeof kind})`); } else { debug92?.(`Fixed: Removed non-number value at kinds[${idx}] (was ${typeof kind})`); } } else if (!Number.isInteger(kind)) { if (isValidating) { issues.push(`Filter[${filterIndex}].kinds[${idx}] is not an integer: ${kind}`); } else { debug92?.(`Fixed: Removed non-integer value at kinds[${idx}]: ${kind}`); } } else if (kind < 0 || kind > 65535) { if (isValidating) { issues.push(`Filter[${filterIndex}].kinds[${idx}] is out of valid range (0-65535): ${kind}`); } else { debug92?.(`Fixed: Removed out-of-range kind at kinds[${idx}]: ${kind}`); } } else { validKinds.push(kind); } }); if (!isValidating) { cleanedFilter.kinds = validKinds.length > 0 ? validKinds : void 0; } } for (const key in filter) { if (key.startsWith("#") && key.length === 2) { const tagValues = filter[key]; if (Array.isArray(tagValues)) { const validValues = []; tagValues.forEach((value, idx) => { if (value === void 0) { if (isValidating) { issues.push(`Filter[${filterIndex}].${key}[${idx}] is undefined`); } else { debug92?.(`Fixed: Removed undefined value at ${key}[${idx}]`); } } else if (typeof value !== "string") { if (isValidating) { issues.push(`Filter[${filterIndex}].${key}[${idx}] is not a string (got ${typeof value})`); } else { debug92?.(`Fixed: Removed non-string value at ${key}[${idx}] (was ${typeof value})`); } } else { if ((key === "#e" || key === "#p") && !isValidHex643(value)) { if (isValidating) { issues.push(`Filter[${filterIndex}].${key}[${idx}] is not a valid 64-char hex string: "${value}"`); } else { debug92?.(`Fixed: Removed invalid hex string at ${key}[${idx}]`); } } else { validValues.push(value); } } }); if (!isValidating) { cleanedFilter[key] = validValues.length > 0 ? validValues : void 0; } } } } if (!isValidating) { Object.keys(cleanedFilter).forEach((key) => { if (cleanedFilter[key] === void 0) { delete cleanedFilter[key]; } }); } return cleanedFilter; } function runAIGuardrailsForFilter2(filter, filterIndex, ndk) { const guards = ndk.aiGuardrails; const filterPreview = JSON.stringify(filter, null, 2); if (Object.keys(filter).length === 1 && filter.limit !== void 0) { guards.error(GuardrailCheckId2.FILTER_ONLY_LIMIT, `Filter[${filterIndex}] contains only 'limit' without any filtering criteria. \u{1F4E6} Your filter: ${filterPreview} \u26A0\uFE0F This will fetch random events from relays without any criteria.`, `Add filtering criteria: \u2705 { kinds: [1], limit: 10 } \u2705 { authors: [pubkey], limit: 10 } \u274C { limit: 10 }`); } if (Object.keys(filter).length === 0) { guards.error(GuardrailCheckId2.FILTER_EMPTY, `Filter[${filterIndex}] is empty. \u{1F4E6} Your filter: ${filterPreview} \u26A0\uFE0F This will request ALL events from relays, which is never what you want.`, `Add filtering criteria like 'kinds', 'authors', or tags.`, false); } if (filter.since !== void 0 && filter.until !== void 0 && filter.since > filter.until) { const sinceDate = new Date(filter.since * 1e3).toISOString(); const untilDate = new Date(filter.until * 1e3).toISOString(); guards.error(GuardrailCheckId2.FILTER_SINCE_AFTER_UNTIL, `Filter[${filterIndex}] has 'since' AFTER 'until'. \u{1F4E6} Your filter: ${filterPreview} \u274C since: ${filter.since} (${sinceDate}) \u274C until: ${filter.until} (${untilDate}) No events can match this time range!`, `'since' must be BEFORE 'until'. Both are Unix timestamps in seconds.`, false); } const bech32Regex = /^n(addr|event|ote|pub|profile)1/; if (filter.ids) { filter.ids.forEach((id, idx) => { if (typeof id === "string") { if (bech32Regex.test(id)) { guards.error(GuardrailCheckId2.FILTER_BECH32_IN_ARRAY, `Filter[${filterIndex}].ids[${idx}] contains bech32: "${id}". IDs must be hex, not bech32.`, `Use filterFromId() to decode bech32 first: import { filterFromId } from "@nostr-dev-kit/ndk"`, false); } else if (!isValidHex643(id)) { guards.error(GuardrailCheckId2.FILTER_INVALID_HEX, `Filter[${filterIndex}].ids[${idx}] is not a valid 64-char hex string: "${id}"`, `Event IDs must be 64-character hexadecimal strings. Invalid IDs often come from corrupted data in user-generated lists. Always validate hex strings before using them in filters: const validIds = ids.filter(id => /^[0-9a-f]{64}$/i.test(id));`, false); } } }); } if (filter.authors) { filter.authors.forEach((author, idx) => { if (typeof author === "string") { if (bech32Regex.test(author)) { guards.error(GuardrailCheckId2.FILTER_BECH32_IN_ARRAY, `Filter[${filterIndex}].authors[${idx}] contains bech32: "${author}". Authors must be hex pubkeys, not npub.`, `Use ndkUser.pubkey instead. Example: { authors: [ndkUser.pubkey] }`, false); } else if (!isValidHex643(author)) { guards.error(GuardrailCheckId2.FILTER_INVALID_HEX, `Filter[${filterIndex}].authors[${idx}] is not a valid 64-char hex pubkey: "${author}"`, `Kind:3 follow lists can contain invalid entries like labels ("Follow List"), partial strings ("highlig"), or other corrupted data. You MUST validate all pubkeys before using them in filters. Example: const validPubkeys = pubkeys.filter(p => /^[0-9a-f]{64}$/i.test(p)); ndk.subscribe({ authors: validPubkeys, kinds: [1] });`, false); } } }); } for (const key in filter) { if (key.startsWith("#") && key.length === 2) { const tagValues = filter[key]; if (Array.isArray(tagValues)) { tagValues.forEach((value, idx) => { if (typeof value === "string") { if (key === "#e" || key === "#p") { if (bech32Regex.test(value)) { guards.error(GuardrailCheckId2.FILTER_BECH32_IN_ARRAY, `Filter[${filterIndex}].${key}[${idx}] contains bech32: "${value}". Tag values must be decoded.`, `Use filterFromId() or nip19.decode() to get the hex value first.`, false); } else if (!isValidHex643(value)) { guards.error(GuardrailCheckId2.FILTER_INVALID_HEX, `Filter[${filterIndex}].${key}[${idx}] is not a valid 64-char hex string: "${value}"`, `${key === "#e" ? "Event IDs" : "Public keys"} in tag filters must be 64-character hexadecimal strings. Kind:3 follow lists and other user-generated content can contain invalid data. Always filter before using: const validValues = values.filter(v => /^[0-9a-f]{64}$/i.test(v));`, false); } } } }); } } } if (filter["#a"]) { const aTags = filter["#a"]; aTags?.forEach((aTag, idx) => { if (typeof aTag === "string") { if (!/^\d+:[0-9a-f]{64}:.*$/.test(aTag)) { guards.error(GuardrailCheckId2.FILTER_INVALID_A_TAG, `Filter[${filterIndex}].#a[${idx}] has invalid format: "${aTag}". Must be "kind:pubkey:d-tag".`, `Example: "30023:fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52:my-article"`, false); } else { const kind = Number.parseInt(aTag.split(":")[0], 10); if (kind < 3e4 || kind > 39999) { guards.error(GuardrailCheckId2.FILTER_INVALID_A_TAG, `Filter[${filterIndex}].#a[${idx}] uses non-addressable kind ${kind}: "${aTag}". #a filters are only for addressable events (kinds 30000-39999).`, `Addressable events include: \u2022 30000-30039: Parameterized Replaceable Events (profiles, settings, etc.) \u2022 30040-39999: Other addressable events For regular events (kind ${kind}), use: \u2022 #e filter for specific event IDs \u2022 kinds + authors filters for event queries`, false); } } } }); } if (filter["#t"]) { const tTags = filter["#t"]; tTags?.forEach((tag, idx) => { if (typeof tag === "string" && tag.startsWith("#")) { guards.error(GuardrailCheckId2.FILTER_HASHTAG_WITH_PREFIX, `Filter[${filterIndex}].#t[${idx}] contains hashtag with # prefix: "${tag}". Hashtag values should NOT include the # symbol.`, `Remove the # prefix from hashtag filters: \u2705 { "#t": ["nostr"] } \u274C { "#t": ["#nostr"] }`, false); } }); } } function queryFullyFilled2(subscription) { if (filterIncludesIds2(subscription.filter)) { if (resultHasAllRequestedIds2(subscription)) { return true; } } return false; } function filterIncludesIds2(filter) { return !!filter.ids; } function resultHasAllRequestedIds2(subscription) { const ids = subscription.filter.ids; return !!ids && ids.length === subscription.eventFirstSeen.size; } function filterFromId2(id) { let decoded; if (id.match(NIP33_A_REGEX2)) { const [kind, pubkey, identifier] = id.split(":"); const filter = { authors: [pubkey], kinds: [Number.parseInt(kind)] }; if (identifier) { filter["#d"] = [identifier]; } return filter; } if (id.match(BECH32_REGEX3)) { try { decoded = nip19_exports3.decode(id); switch (decoded.type) { case "nevent": { const filter = { ids: [decoded.data.id] }; if (decoded.data.author) filter.authors = [decoded.data.author]; if (decoded.data.kind) filter.kinds = [decoded.data.kind]; return filter; } case "note": return { ids: [decoded.data] }; case "naddr": { const filter = { authors: [decoded.data.pubkey], kinds: [decoded.data.kind] }; if (decoded.data.identifier) filter["#d"] = [decoded.data.identifier]; return filter; } } } catch (e2) { console.error("Error decoding", id, e2); } } return { ids: [id] }; } function isNip33AValue2(value) { return value.match(NIP33_A_REGEX2) !== null; } var NIP33_A_REGEX2 = /^(\d+):([0-9A-Fa-f]+)(?::(.*))?$/; var BECH32_REGEX3 = /^n(event|ote|profile|pub|addr)1[\d\w]+$/; function relaysFromBech322(bech3222, ndk) { try { const decoded = nip19_exports3.decode(bech3222); if (["naddr", "nevent"].includes(decoded?.type)) { const data = decoded.data; if (data?.relays) { return data.relays.map((r) => new NDKRelay3(r, ndk.relayAuthDefaultPolicy, ndk)); } } } catch (_e2) { } return []; } var defaultOpts2 = { closeOnEose: false, cacheUsage: "CACHE_FIRST", dontSaveToCache: false, groupable: true, groupableDelay: 10, groupableDelayType: "at-most", cacheUnconstrainFilter: ["limit", "since", "until"], includeMuted: false }; var NDKSubscription2 = class extends import_tseep42.EventEmitter { constructor(ndk, filters, opts, subId) { super(); __publicField(this, "subId"); __publicField(this, "filters"); __publicField(this, "opts"); __publicField(this, "pool"); __publicField(this, "skipVerification", false); __publicField(this, "skipValidation", false); __publicField(this, "exclusiveRelay", false); __publicField(this, "relayFilters"); __publicField(this, "relaySet"); __publicField(this, "ndk"); __publicField(this, "debug"); __publicField(this, "eventFirstSeen", /* @__PURE__ */ new Map()); __publicField(this, "eosesSeen", /* @__PURE__ */ new Set()); __publicField(this, "lastEventReceivedAt"); __publicField(this, "mostRecentCacheEventTimestamp"); __publicField(this, "internalId"); __publicField(this, "closeOnEose"); __publicField(this, "poolMonitor"); __publicField(this, "skipOptimisticPublishEvent", false); __publicField(this, "cacheUnconstrainFilter"); __publicField(this, "onStopped"); __publicField(this, "eoseTimeout"); __publicField(this, "eosed", false); this.ndk = ndk; this.opts = { ...defaultOpts2, ...opts || {} }; this.pool = this.opts.pool || ndk.pool; const rawFilters = Array.isArray(filters) ? filters : [filters]; const validationMode = ndk.filterValidationMode === "validate" ? "validate" : ndk.filterValidationMode === "fix" ? "fix" : "ignore"; this.filters = processFilters2(rawFilters, validationMode, ndk.debug, ndk); if (this.filters.length === 0) { throw new Error("Subscription must have at least one filter"); } this.subId = subId || this.opts.subId; this.internalId = Math.random().toString(36).substring(7); this.debug = ndk.debug.extend(`subscription[${this.opts.subId ?? this.internalId}]`); if (this.opts.relaySet) { this.relaySet = this.opts.relaySet; } else if (this.opts.relayUrls) { this.relaySet = NDKRelaySet3.fromRelayUrls(this.opts.relayUrls, this.ndk); } this.skipVerification = this.opts.skipVerification || false; this.skipValidation = this.opts.skipValidation || false; this.closeOnEose = this.opts.closeOnEose || false; this.skipOptimisticPublishEvent = this.opts.skipOptimisticPublishEvent || false; this.cacheUnconstrainFilter = this.opts.cacheUnconstrainFilter; this.exclusiveRelay = this.opts.exclusiveRelay || false; if (this.opts.onEvent) { this.on("event", this.opts.onEvent); } if (this.opts.onEose) { this.on("eose", this.opts.onEose); } if (this.opts.onClose) { this.on("close", this.opts.onClose); } } relaysMissingEose() { if (!this.relayFilters) return []; const relaysMissingEose = Array.from(this.relayFilters?.keys()).filter((url) => !this.eosesSeen.has(this.pool.getRelay(url, false, false))); return relaysMissingEose; } get filter() { return this.filters[0]; } get groupableDelay() { if (!this.isGroupable()) return; return this.opts?.groupableDelay; } get groupableDelayType() { return this.opts?.groupableDelayType || "at-most"; } isGroupable() { return this.opts?.groupable || false; } shouldQueryCache() { if (this.opts.addSinceFromCache) return true; if (this.opts?.cacheUsage === "ONLY_RELAY") return false; const hasNonEphemeralKind = this.filters.some((f) => f.kinds?.some((k2) => kindIsEphemeral2(k2))); if (hasNonEphemeralKind) return true; return true; } shouldQueryRelays() { return this.opts?.cacheUsage !== "ONLY_CACHE"; } shouldWaitForCache() { if (this.opts.addSinceFromCache) return true; return !!this.opts.closeOnEose && !!this.ndk.cacheAdapter?.locking && this.opts.cacheUsage !== "PARALLEL"; } start(emitCachedEvents = true) { let cacheResult; const updateStateFromCacheResults = (events) => { if (events.length === 0) { if (!emitCachedEvents) cacheResult = events; return; } if (!emitCachedEvents) { let maxTimestamp2 = this.mostRecentCacheEventTimestamp || 0; for (const event of events) { event.ndk = this.ndk; if (event.created_at && event.created_at > maxTimestamp2) { maxTimestamp2 = event.created_at; } } this.mostRecentCacheEventTimestamp = maxTimestamp2; cacheResult = events; return; } let maxTimestamp = this.mostRecentCacheEventTimestamp || 0; for (const event of events) { if (event.created_at && event.created_at > maxTimestamp) { maxTimestamp = event.created_at; } } this.mostRecentCacheEventTimestamp = maxTimestamp; for (const event of events) { this.eventReceived(event, void 0, true, false); } }; const loadFromRelays = () => { if (this.shouldQueryRelays()) { this.startWithRelays(); this.startPoolMonitor(); } else { this.emit("eose", this); } }; if (this.shouldQueryCache()) { cacheResult = this.startWithCache(); if (cacheResult instanceof Promise) { if (this.shouldWaitForCache()) { cacheResult.then((events) => { if (this.opts.onEvents) { let maxTimestamp = this.mostRecentCacheEventTimestamp || 0; for (const event of events) { event.ndk = this.ndk; if (event.created_at && event.created_at > maxTimestamp) { maxTimestamp = event.created_at; } } this.mostRecentCacheEventTimestamp = maxTimestamp; this.opts.onEvents(events); } else { updateStateFromCacheResults(events); } if (queryFullyFilled2(this)) { this.emit("eose", this); return; } loadFromRelays(); }); return null; } cacheResult.then((events) => { if (this.opts.onEvents) { let maxTimestamp = this.mostRecentCacheEventTimestamp || 0; for (const event of events) { event.ndk = this.ndk; if (event.created_at && event.created_at > maxTimestamp) { maxTimestamp = event.created_at; } } this.mostRecentCacheEventTimestamp = maxTimestamp; this.opts.onEvents(events); } else { updateStateFromCacheResults(events); } if (!this.shouldQueryRelays()) { this.emit("eose", this); } }); if (this.shouldQueryRelays()) { loadFromRelays(); } return null; } updateStateFromCacheResults(cacheResult); if (queryFullyFilled2(this)) { this.emit("eose", this); } else { loadFromRelays(); } return cacheResult; } loadFromRelays(); return null; } startPoolMonitor() { const _d = this.debug.extend("pool-monitor"); this.poolMonitor = (relay) => { if (this.relayFilters?.has(relay.url)) return; const calc = calculateRelaySetsFromFilters2(this.ndk, this.filters, this.pool, this.opts.relayGoalPerAuthor); if (calc.get(relay.url)) { this.relayFilters?.set(relay.url, this.filters); relay.subscribe(this, this.filters); } }; this.pool.on("relay:connect", this.poolMonitor); } stop() { this.emit("close", this); this.poolMonitor && this.pool.off("relay:connect", this.poolMonitor); this.onStopped?.(); } hasAuthorsFilter() { return this.filters.some((f) => f.authors?.length); } startWithCache() { if (this.ndk.cacheAdapter?.query) { return this.ndk.cacheAdapter.query(this); } return []; } startWithRelays() { let filters = this.filters; if (this.opts.addSinceFromCache && this.mostRecentCacheEventTimestamp) { const sinceTimestamp = this.mostRecentCacheEventTimestamp + 1; filters = filters.map((filter) => ({ ...filter, since: Math.max(filter.since || 0, sinceTimestamp) })); } if (!this.relaySet || this.relaySet.relays.size === 0) { this.relayFilters = calculateRelaySetsFromFilters2(this.ndk, filters, this.pool, this.opts.relayGoalPerAuthor); } else { this.relayFilters = /* @__PURE__ */ new Map(); for (const relay of this.relaySet.relays) { this.relayFilters.set(relay.url, filters); } } for (const [relayUrl, filters2] of this.relayFilters) { const relay = this.pool.getRelay(relayUrl, true, true, filters2); relay.subscribe(this, filters2); } } refreshRelayConnections() { if (this.relaySet && this.relaySet.relays.size > 0) { return; } const updatedRelaySets = calculateRelaySetsFromFilters2(this.ndk, this.filters, this.pool, this.opts.relayGoalPerAuthor); for (const [relayUrl, filters] of updatedRelaySets) { if (!this.relayFilters?.has(relayUrl)) { this.relayFilters?.set(relayUrl, filters); const relay = this.pool.getRelay(relayUrl, true, true, filters); relay.subscribe(this, filters); } } } eventReceived(event, relay, fromCache = false, optimisticPublish = false) { const eventId = event.id; const eventAlreadySeen = this.eventFirstSeen.has(eventId); let ndkEvent; if (event instanceof NDKEvent3) ndkEvent = event; if (!eventAlreadySeen) { if (this.ndk.futureTimestampGrace !== void 0 && event.created_at) { const currentTime = Math.floor(Date.now() / 1e3); const timeDifference = event.created_at - currentTime; if (timeDifference > this.ndk.futureTimestampGrace) { this.debug("Event discarded: timestamp %d is %d seconds in the future (grace: %d seconds)", event.created_at, timeDifference, this.ndk.futureTimestampGrace); return; } } ndkEvent ?? (ndkEvent = new NDKEvent3(this.ndk, event)); ndkEvent.ndk = this.ndk; ndkEvent.relay = relay; if (!fromCache && !optimisticPublish) { if (!this.skipValidation) { if (!ndkEvent.isValid) { this.debug("Event failed validation %s from relay %s", eventId, relay?.url); return; } } if (relay) { const shouldVerify = relay.shouldValidateEvent(); if (shouldVerify && !this.skipVerification) { ndkEvent.relay = relay; if (this.ndk.asyncSigVerification) { ndkEvent.verifySignature(true); } else { if (!ndkEvent.verifySignature(true)) { this.debug("Event failed signature validation", event); this.ndk.reportInvalidSignature(ndkEvent, relay); return; } relay.addValidatedEvent(); } } else { relay.addNonValidatedEvent(); } } if (this.ndk.cacheAdapter && !this.opts.dontSaveToCache && !kindIsEphemeral2(ndkEvent.kind) && !fromCache) { this.ndk.cacheAdapter.setEvent(ndkEvent, this.filters, relay); } } if (!this.opts.includeMuted && this.ndk.muteFilter && this.ndk.muteFilter(ndkEvent)) { this.debug("Event muted, skipping"); return; } if (!optimisticPublish || this.skipOptimisticPublishEvent !== true) { this.emitEvent(this.opts?.wrap ?? false, ndkEvent, relay, fromCache, optimisticPublish); this.eventFirstSeen.set(eventId, Date.now()); } } else { const timeSinceFirstSeen = Date.now() - (this.eventFirstSeen.get(eventId) || 0); this.emit("event:dup", event, relay, timeSinceFirstSeen, this, fromCache, optimisticPublish); if (this.opts?.onEventDup) { this.opts.onEventDup(event, relay, timeSinceFirstSeen, this, fromCache, optimisticPublish); } if (!fromCache && !optimisticPublish && relay && this.ndk.cacheAdapter?.setEventDup && !this.opts.dontSaveToCache) { ndkEvent ?? (ndkEvent = event instanceof NDKEvent3 ? event : new NDKEvent3(this.ndk, event)); this.ndk.cacheAdapter.setEventDup(ndkEvent, relay); } if (relay) { const signature = verifiedSignatures3.get(eventId); if (signature && typeof signature === "string") { if (event.sig === signature) { relay.addValidatedEvent(); } else { const eventToReport = event instanceof NDKEvent3 ? event : new NDKEvent3(this.ndk, event); this.ndk.reportInvalidSignature(eventToReport, relay); } } } } this.lastEventReceivedAt = Date.now(); } emitEvent(wrap, evt, relay, fromCache, optimisticPublish) { const wrapped = wrap ? wrapEvent32(evt) : evt; if (wrapped instanceof Promise) { wrapped.then((e2) => this.emitEvent(false, e2, relay, fromCache, optimisticPublish)); } else if (wrapped) { this.emit("event", wrapped, relay, this, fromCache, optimisticPublish); } } closedReceived(relay, reason) { this.emit("closed", relay, reason); } eoseReceived(relay) { this.eosesSeen.add(relay); let lastEventSeen = this.lastEventReceivedAt ? Date.now() - this.lastEventReceivedAt : void 0; const hasSeenAllEoses = this.eosesSeen.size === this.relayFilters?.size; const queryFilled = queryFullyFilled2(this); const performEose = (reason) => { if (this.eosed) return; if (this.eoseTimeout) clearTimeout(this.eoseTimeout); this.emit("eose", this); this.eosed = true; if (this.opts?.closeOnEose) this.stop(); }; if (queryFilled || hasSeenAllEoses) { performEose("query filled or seen all"); } else if (this.relayFilters) { let timeToWaitForNextEose = 1e3; const connectedRelays = new Set(this.pool.connectedRelays().map((r) => r.url)); const connectedRelaysWithFilters = Array.from(this.relayFilters.keys()).filter((url) => connectedRelays.has(url)); if (connectedRelaysWithFilters.length === 0) { this.debug("No connected relays, waiting for all relays to connect", Array.from(this.relayFilters.keys()).join(", ")); return; } const percentageOfRelaysThatHaveSentEose = this.eosesSeen.size / connectedRelaysWithFilters.length; if (this.eosesSeen.size >= 2 && percentageOfRelaysThatHaveSentEose >= 0.5) { timeToWaitForNextEose = timeToWaitForNextEose * (1 - percentageOfRelaysThatHaveSentEose); if (timeToWaitForNextEose === 0) { performEose("time to wait was 0"); return; } if (this.eoseTimeout) clearTimeout(this.eoseTimeout); const sendEoseTimeout = () => { lastEventSeen = this.lastEventReceivedAt ? Date.now() - this.lastEventReceivedAt : void 0; if (lastEventSeen !== void 0 && lastEventSeen < 20) { this.eoseTimeout = setTimeout(sendEoseTimeout, timeToWaitForNextEose); } else { performEose(`send eose timeout: ${timeToWaitForNextEose}`); } }; this.eoseTimeout = setTimeout(sendEoseTimeout, timeToWaitForNextEose); } } } }; var kindIsEphemeral2 = (kind) => kind >= 2e4 && kind < 3e4; async function follows3(opts, outbox, kind = 3) { if (!this.ndk) throw new Error("NDK not set"); const contactListEvent = await this.ndk.fetchEvent({ kinds: [kind], authors: [this.pubkey] }, opts || { groupable: false }); if (contactListEvent) { const pubkeys = /* @__PURE__ */ new Set(); contactListEvent.tags.forEach((tag) => { if (tag[0] === "p" && tag[1] && isValidPubkey3(tag[1])) { pubkeys.add(tag[1]); } }); if (outbox) { this.ndk?.outboxTracker?.trackUsers(Array.from(pubkeys)); } return [...pubkeys].reduce((acc, pubkey) => { const user = new NDKUser3({ pubkey }); user.ndk = this.ndk; acc.add(user); return acc; }, /* @__PURE__ */ new Set()); } return /* @__PURE__ */ new Set(); } var NIP05_REGEX22 = /^(?:([\w.+-]+)@)?([\w.-]+)$/; async function getNip05For3(ndk, fullname, _fetch5 = fetch, fetchOpts = {}) { return await ndk.queuesNip05.add({ id: fullname, func: async () => { if (ndk.cacheAdapter?.loadNip05) { const profile = await ndk.cacheAdapter.loadNip05(fullname); if (profile !== "missing") { if (profile) { const user = new NDKUser3({ pubkey: profile.pubkey, relayUrls: profile.relays, nip46Urls: profile.nip46 }); user.ndk = ndk; return user; } if (fetchOpts.cache !== "no-cache") { return null; } } } const match = fullname.match(NIP05_REGEX22); if (!match) return null; const [_2, name = "_", domain] = match; try { const res = await _fetch5(`https://${domain}/.well-known/nostr.json?name=${name}`, fetchOpts); const { names, relays, nip46 } = parseNIP05Result3(await res.json()); const pubkey = names[name.toLowerCase()]; let profile = null; if (pubkey) { profile = { pubkey, relays: relays?.[pubkey], nip46: nip46?.[pubkey] }; } if (ndk?.cacheAdapter?.saveNip05) { ndk.cacheAdapter.saveNip05(fullname, profile); } return profile; } catch (_e2) { if (ndk?.cacheAdapter?.saveNip05) { ndk?.cacheAdapter.saveNip05(fullname, null); } console.error("Failed to fetch NIP05 for", fullname, _e2); return null; } } }); } function parseNIP05Result3(json) { const result = { names: {} }; for (const [name, pubkey] of Object.entries(json.names)) { if (typeof name === "string" && typeof pubkey === "string") { result.names[name.toLowerCase()] = pubkey; } } if (json.relays) { result.relays = {}; for (const [pubkey, relays] of Object.entries(json.relays)) { if (typeof pubkey === "string" && Array.isArray(relays)) { result.relays[pubkey] = relays.filter((relay) => typeof relay === "string"); } } } if (json.nip46) { result.nip46 = {}; for (const [pubkey, nip46] of Object.entries(json.nip46)) { if (typeof pubkey === "string" && Array.isArray(nip46)) { result.nip46[pubkey] = nip46.filter((relay) => typeof relay === "string"); } } } return result; } function profileFromEvent3(event) { const profile = {}; let payload; try { payload = JSON.parse(event.content); } catch (error) { throw new Error(`Failed to parse profile event: ${error}`); } profile.profileEvent = JSON.stringify(event.rawEvent()); for (const key of Object.keys(payload)) { switch (key) { case "name": profile.name = payload.name; break; case "display_name": profile.displayName = payload.display_name; break; case "image": case "picture": profile.picture = payload.picture || payload.image; profile.image = profile.picture; break; case "banner": profile.banner = payload.banner; break; case "bio": profile.bio = payload.bio; break; case "nip05": profile.nip05 = payload.nip05; break; case "lud06": profile.lud06 = payload.lud06; break; case "lud16": profile.lud16 = payload.lud16; break; case "about": profile.about = payload.about; break; case "website": profile.website = payload.website; break; default: profile[key] = payload[key]; break; } } profile.created_at = event.created_at; return profile; } function serializeProfile3(profile) { const payload = {}; for (const [key, val] of Object.entries(profile)) { switch (key) { case "username": case "name": payload.name = val; break; case "displayName": payload.display_name = val; break; case "image": case "picture": payload.picture = val; break; case "bio": case "about": payload.about = val; break; default: payload[key] = val; break; } } return JSON.stringify(payload); } var NDKUser3 = class _NDKUser2 { constructor(opts) { __publicField(this, "ndk"); __publicField(this, "profile"); __publicField(this, "profileEvent"); __publicField(this, "_npub"); __publicField(this, "_pubkey"); __publicField(this, "relayUrls", []); __publicField(this, "nip46Urls", []); __publicField(this, "follows", follows3.bind(this)); if (opts.npub) this._npub = opts.npub; if (opts.hexpubkey) this._pubkey = opts.hexpubkey; if (opts.pubkey) this._pubkey = opts.pubkey; if (opts.relayUrls) this.relayUrls = opts.relayUrls; if (opts.nip46Urls) this.nip46Urls = opts.nip46Urls; if (opts.nprofile) { try { const decoded = nip19_exports3.decode(opts.nprofile); if (decoded.type === "nprofile") { this._pubkey = decoded.data.pubkey; if (decoded.data.relays && decoded.data.relays.length > 0) { this.relayUrls.push(...decoded.data.relays); } } } catch (e2) { console.error("Failed to decode nprofile", e2); } } } get npub() { if (!this._npub) { if (!this._pubkey) throw new Error("pubkey not set"); this._npub = nip19_exports3.npubEncode(this.pubkey); } return this._npub; } get nprofile() { const relays = this.profileEvent?.onRelays?.map((r) => r.url); return nip19_exports3.nprofileEncode({ pubkey: this.pubkey, relays }); } set npub(npub22) { this._npub = npub22; } get pubkey() { if (!this._pubkey) { if (!this._npub) throw new Error("npub not set"); this._pubkey = nip19_exports3.decode(this.npub).data; } return this._pubkey; } set pubkey(pubkey) { this._pubkey = pubkey; } filter() { return { "#p": [this.pubkey] }; } async getZapInfo(timeoutMs) { if (!this.ndk) throw new Error("No NDK instance found"); const promiseWithTimeout = async (promise) => { if (!timeoutMs) return promise; let timeoutId; const timeoutPromise = new Promise((_2, reject) => { timeoutId = setTimeout(() => reject(new Error("Timeout")), timeoutMs); }); try { const result = await Promise.race([promise, timeoutPromise]); if (timeoutId) clearTimeout(timeoutId); return result; } catch (e2) { if (e2 instanceof Error && e2.message === "Timeout") { try { const result = await promise; return result; } catch (_originalError) { return; } } return; } }; const [userProfile, mintListEvent] = await Promise.all([ promiseWithTimeout(this.fetchProfile()), promiseWithTimeout(this.ndk.fetchEvent({ kinds: [10019], authors: [this.pubkey] })) ]); const res = /* @__PURE__ */ new Map(); if (mintListEvent) { const mintList = NDKCashuMintList3.from(mintListEvent); if (mintList.mints.length > 0) { res.set("nip61", { mints: mintList.mints, relays: mintList.relays, p2pk: mintList.p2pk }); } } if (userProfile) { const { lud06, lud16 } = userProfile; res.set("nip57", { lud06, lud16 }); } return res; } static async fromNip05(nip05Id, ndk, skipCache = false) { if (!ndk) throw new Error("No NDK instance found"); const opts = {}; if (skipCache) opts.cache = "no-cache"; const profile = await getNip05For3(ndk, nip05Id, ndk?.httpFetch, opts); if (profile) { const user = new _NDKUser2({ pubkey: profile.pubkey, relayUrls: profile.relays, nip46Urls: profile.nip46 }); user.ndk = ndk; return user; } } async fetchProfile(opts, storeProfileEvent = false) { if (!this.ndk) throw new Error("NDK not set"); let setMetadataEvent = null; if (this.ndk.cacheAdapter && (this.ndk.cacheAdapter.fetchProfile || this.ndk.cacheAdapter.fetchProfileSync) && opts?.cacheUsage !== "ONLY_RELAY") { let profile = null; if (this.ndk.cacheAdapter.fetchProfileSync) { profile = this.ndk.cacheAdapter.fetchProfileSync(this.pubkey); } else if (this.ndk.cacheAdapter.fetchProfile) { profile = await this.ndk.cacheAdapter.fetchProfile(this.pubkey); } if (profile) { this.profile = profile; return profile; } } opts ?? (opts = {}); opts.cacheUsage ?? (opts.cacheUsage = "ONLY_RELAY"); opts.closeOnEose ?? (opts.closeOnEose = true); opts.groupable ?? (opts.groupable = true); opts.groupableDelay ?? (opts.groupableDelay = 25); if (!setMetadataEvent) { setMetadataEvent = await this.ndk.fetchEvent({ kinds: [0], authors: [this.pubkey] }, opts); } if (!setMetadataEvent) return null; this.profile = profileFromEvent3(setMetadataEvent); if (storeProfileEvent && this.profile && this.ndk.cacheAdapter && this.ndk.cacheAdapter.saveProfile) { this.ndk.cacheAdapter.saveProfile(this.pubkey, this.profile); } return this.profile; } async followSet(opts, outbox, kind = 3) { const follows22 = await this.follows(opts, outbox, kind); return new Set(Array.from(follows22).map((f) => f.pubkey)); } tagReference() { return ["p", this.pubkey]; } referenceTags(marker) { const tag = [["p", this.pubkey]]; if (!marker) return tag; tag[0].push("", marker); return tag; } async publish() { if (!this.ndk) throw new Error("No NDK instance found"); if (!this.profile) throw new Error("No profile available"); this.ndk.assertSigner(); const event = new NDKEvent3(this.ndk, { kind: 0, content: serializeProfile3(this.profile) }); await event.publish(); } async follow(newFollow, currentFollowList, kind = 3) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); if (!currentFollowList) { currentFollowList = await this.follows(void 0, void 0, kind); } const followsToAdd = Array.isArray(newFollow) ? newFollow : [newFollow]; let anyAdded = false; for (const follow of followsToAdd) { const followPubkey = typeof follow === "string" ? follow : follow.pubkey; const isAlreadyFollowing = Array.from(currentFollowList).some((item) => typeof item === "string" ? item === followPubkey : item.pubkey === followPubkey); if (!isAlreadyFollowing) { currentFollowList.add(follow); anyAdded = true; } } if (!anyAdded) { return false; } const event = new NDKEvent3(this.ndk, { kind }); for (const follow of currentFollowList) { if (typeof follow === "string") { event.tags.push(["p", follow]); } else { event.tag(follow); } } await event.publish(); return true; } async unfollow(user, currentFollowList, kind = 3) { if (!this.ndk) throw new Error("No NDK instance found"); this.ndk.assertSigner(); if (!currentFollowList) { currentFollowList = await this.follows(void 0, void 0, kind); } const usersToUnfollow = Array.isArray(user) ? user : [user]; const unfollowPubkeys = new Set(usersToUnfollow.map((u3) => typeof u3 === "string" ? u3 : u3.pubkey)); const newUserFollowList = /* @__PURE__ */ new Set(); let foundAny = false; for (const follow of currentFollowList) { const followPubkey = typeof follow === "string" ? follow : follow.pubkey; if (!unfollowPubkeys.has(followPubkey)) { newUserFollowList.add(follow); } else { foundAny = true; } } if (!foundAny) return false; const event = new NDKEvent3(this.ndk, { kind }); for (const follow of newUserFollowList) { if (typeof follow === "string") { event.tags.push(["p", follow]); } else { event.tag(follow); } } return await event.publish(); } async validateNip05(nip05Id) { if (!this.ndk) throw new Error("No NDK instance found"); const profilePointer = await getNip05For3(this.ndk, nip05Id); if (profilePointer === null) return null; return profilePointer.pubkey === this.pubkey; } }; var signerRegistry3 = /* @__PURE__ */ new Map(); function registerSigner3(type, signerClass) { signerRegistry3.set(type, signerClass); } var NDKPrivateKeySigner3 = class _NDKPrivateKeySigner2 { constructor(privateKeyOrNsec, ndk) { __publicField(this, "_user"); __publicField(this, "_privateKey"); __publicField(this, "_pubkey"); if (typeof privateKeyOrNsec === "string") { if (privateKeyOrNsec.startsWith("nsec1")) { const { type, data } = nip19_exports3.decode(privateKeyOrNsec); if (type === "nsec") this._privateKey = data; else throw new Error("Invalid private key provided."); } else if (privateKeyOrNsec.length === 64) { this._privateKey = hexToBytes4(privateKeyOrNsec); } else { throw new Error("Invalid private key provided."); } } else { this._privateKey = privateKeyOrNsec; } this._pubkey = getPublicKey3(this._privateKey); if (ndk) this._user = ndk.getUser({ pubkey: this._pubkey }); this._user ?? (this._user = new NDKUser3({ pubkey: this._pubkey })); } get privateKey() { if (!this._privateKey) throw new Error("Not ready"); return bytesToHex4(this._privateKey); } get pubkey() { if (!this._pubkey) throw new Error("Not ready"); return this._pubkey; } get nsec() { if (!this._privateKey) throw new Error("Not ready"); return nip19_exports3.nsecEncode(this._privateKey); } get npub() { if (!this._pubkey) throw new Error("Not ready"); return nip19_exports3.npubEncode(this._pubkey); } encryptToNcryptsec(password, logn = 16, ksb = 2) { if (!this._privateKey) throw new Error("Private key not available"); return encrypt32(this._privateKey, password, logn, ksb); } static generate() { const privateKey = generateSecretKey3(); return new _NDKPrivateKeySigner2(privateKey); } static fromNcryptsec(ncryptsec, password, ndk) { const privateKeyBytes = decrypt32(ncryptsec, password); return new _NDKPrivateKeySigner2(privateKeyBytes, ndk); } async blockUntilReady() { return this._user; } async user() { return this._user; } get userSync() { return this._user; } async sign(event) { if (!this._privateKey) { throw Error("Attempted to sign without a private key"); } return finalizeEvent3(event, this._privateKey).sig; } async encryptionEnabled(scheme) { const enabled = []; if (!scheme || scheme === "nip04") enabled.push("nip04"); if (!scheme || scheme === "nip44") enabled.push("nip44"); return enabled; } async encrypt(recipient, value, scheme) { if (!this._privateKey || !this.privateKey) { throw Error("Attempted to encrypt without a private key"); } const recipientHexPubKey = recipient.pubkey; if (scheme === "nip44") { const conversationKey = nip44_exports.v2.utils.getConversationKey(this._privateKey, recipientHexPubKey); return await nip44_exports.v2.encrypt(value, conversationKey); } return await nip04_exports.encrypt(this._privateKey, recipientHexPubKey, value); } async decrypt(sender, value, scheme) { if (!this._privateKey || !this.privateKey) { throw Error("Attempted to decrypt without a private key"); } const senderHexPubKey = sender.pubkey; if (scheme === "nip44") { const conversationKey = nip44_exports.v2.utils.getConversationKey(this._privateKey, senderHexPubKey); return await nip44_exports.v2.decrypt(value, conversationKey); } return await nip04_exports.decrypt(this._privateKey, senderHexPubKey, value); } toPayload() { if (!this._privateKey) throw new Error("Private key not available"); const payload = { type: "private-key", payload: this.privateKey }; return JSON.stringify(payload); } static async fromPayload(payloadString, ndk) { const payload = JSON.parse(payloadString); if (payload.type !== "private-key") { throw new Error(`Invalid payload type: expected 'private-key', got ${payload.type}`); } if (!payload.payload || typeof payload.payload !== "string") { throw new Error("Invalid payload content for private-key signer"); } return new _NDKPrivateKeySigner2(payload.payload, ndk); } }; registerSigner3("private-key", NDKPrivateKeySigner3); function dedup2(event1, event2) { if (event1.created_at > event2.created_at) { return event1; } return event2; } async function getRelayListForUser2(pubkey, ndk) { const list = await getRelayListForUsers2([pubkey], ndk); return list.get(pubkey); } async function getRelayListForUsers2(pubkeys, ndk, skipCache = false, timeout = 1e3, relayHints) { const pool = ndk.outboxPool || ndk.pool; const set = /* @__PURE__ */ new Set(); for (const relay of pool.relays.values()) set.add(relay); if (relayHints) { for (const hints of relayHints.values()) { for (const url of hints) { const relay = pool.getRelay(url, true, true); if (relay) set.add(relay); } } } const relayLists = /* @__PURE__ */ new Map(); const fromContactList = /* @__PURE__ */ new Map(); const relaySet = new NDKRelaySet3(set, ndk); if (ndk.cacheAdapter?.locking && !skipCache) { const cachedList = await ndk.fetchEvents({ kinds: [3, 10002], authors: Array.from(new Set(pubkeys)) }, { cacheUsage: "ONLY_CACHE", subId: "ndk-relay-list-fetch" }); for (const relayList of cachedList) { if (relayList.kind === 10002) relayLists.set(relayList.pubkey, NDKRelayList3.from(relayList)); } for (const relayList of cachedList) { if (relayList.kind === 3) { if (relayLists.has(relayList.pubkey)) continue; const list = relayListFromKind32(ndk, relayList); if (list) fromContactList.set(relayList.pubkey, list); } } pubkeys = pubkeys.filter((pubkey) => !relayLists.has(pubkey) && !fromContactList.has(pubkey)); } if (pubkeys.length === 0) return relayLists; const relayListEvents = /* @__PURE__ */ new Map(); const contactListEvents = /* @__PURE__ */ new Map(); return new Promise((resolve) => { let resolved = false; const handleSubscription = async () => { const subscribeOpts = { closeOnEose: true, pool, groupable: true, subId: "ndk-relay-list-fetch", addSinceFromCache: true, relaySet }; if (relaySet) subscribeOpts.relaySet = relaySet; const sub = ndk.subscribe({ kinds: [3, 10002], authors: pubkeys }, subscribeOpts, { onEvent: (event) => { if (event.kind === 10002) { const existingEvent = relayListEvents.get(event.pubkey); if (existingEvent && existingEvent.created_at > event.created_at) return; relayListEvents.set(event.pubkey, event); } else if (event.kind === 3) { const existingEvent = contactListEvents.get(event.pubkey); if (existingEvent && existingEvent.created_at > event.created_at) return; contactListEvents.set(event.pubkey, event); } }, onEose: () => { if (resolved) return; resolved = true; ndk.debug(`[getRelayListForUsers] EOSE - relayListEvents: ${relayListEvents.size}, contactListEvents: ${contactListEvents.size}`); for (const event of relayListEvents.values()) { relayLists.set(event.pubkey, NDKRelayList3.from(event)); } for (const pubkey of pubkeys) { if (relayLists.has(pubkey)) continue; const contactList = contactListEvents.get(pubkey); if (!contactList) continue; const list = relayListFromKind32(ndk, contactList); if (list) relayLists.set(pubkey, list); } ndk.debug(`[getRelayListForUsers] Returning ${relayLists.size} relay lists for ${pubkeys.length} pubkeys`); resolve(relayLists); } }); const hasDisconnectedRelays = Array.from(set).some((relay) => relay.status <= 2); const hasConnectingRelays = Array.from(set).some((relay) => relay.status === 4); let effectiveTimeout = timeout; if (hasDisconnectedRelays || hasConnectingRelays) { effectiveTimeout = timeout + 3e3; } ndk.debug(`[getRelayListForUsers] Setting fallback timeout to ${effectiveTimeout}ms (disconnected: ${hasDisconnectedRelays}, connecting: ${hasConnectingRelays})`, { pubkeys }); setTimeout(() => { if (!resolved) { resolved = true; ndk.debug(`[getRelayListForUsers] Timeout reached, returning ${relayLists.size} relay lists`); resolve(relayLists); } }, effectiveTimeout); }; handleSubscription(); }); } var OutboxItem2 = class { constructor(type) { __publicField(this, "type"); __publicField(this, "relayUrlScores"); __publicField(this, "readRelays"); __publicField(this, "writeRelays"); this.type = type; this.relayUrlScores = /* @__PURE__ */ new Map(); this.readRelays = /* @__PURE__ */ new Set(); this.writeRelays = /* @__PURE__ */ new Set(); } }; var OutboxTracker2 = class extends import_tseep62.EventEmitter { constructor(ndk) { super(); __publicField(this, "data"); __publicField(this, "ndk"); __publicField(this, "debug"); this.ndk = ndk; this.debug = ndk.debug.extend("outbox-tracker"); this.data = new import_typescript_lru_cache22.LRUCache({ maxSize: 1e5, entryExpirationTimeInMS: 2 * 60 * 1e3 }); } async trackUsers(items, skipCache = false) { const promises = []; for (let i22 = 0; i22 < items.length; i22 += 400) { const slice = items.slice(i22, i22 + 400); const pubkeys = slice.map((item) => getKeyFromItem2(item)).filter((pubkey) => !this.data.has(pubkey)); if (pubkeys.length === 0) continue; for (const pubkey of pubkeys) { this.data.set(pubkey, new OutboxItem2("user")); } const relayHints = /* @__PURE__ */ new Map(); for (const item of slice) { if (item instanceof NDKUser3 && item.relayUrls.length > 0) { relayHints.set(item.pubkey, item.relayUrls); } } promises.push(new Promise((resolve) => { getRelayListForUsers2(pubkeys, this.ndk, skipCache, 1e3, relayHints).then((relayLists) => { this.debug(`Received relay lists for ${relayLists.size} pubkeys out of ${pubkeys.length} requested`); for (const [pubkey, relayList] of relayLists) { let outboxItem = this.data.get(pubkey); outboxItem ?? (outboxItem = new OutboxItem2("user")); if (relayList) { outboxItem.readRelays = new Set(normalize22(relayList.readRelayUrls)); outboxItem.writeRelays = new Set(normalize22(relayList.writeRelayUrls)); if (this.ndk.relayConnectionFilter) { for (const relayUrl of outboxItem.readRelays) { if (!this.ndk.relayConnectionFilter(relayUrl)) { outboxItem.readRelays.delete(relayUrl); } } for (const relayUrl of outboxItem.writeRelays) { if (!this.ndk.relayConnectionFilter(relayUrl)) { outboxItem.writeRelays.delete(relayUrl); } } } this.data.set(pubkey, outboxItem); this.emit("user:relay-list-updated", pubkey, outboxItem); this.debug(`Adding ${outboxItem.readRelays.size} read relays and ${outboxItem.writeRelays.size} write relays for ${pubkey}`, relayList?.rawEvent()); } } }).finally(resolve); })); } return Promise.all(promises); } track(item, type, _skipCache = true) { const key = getKeyFromItem2(item); type ?? (type = getTypeFromItem2(item)); let outboxItem = this.data.get(key); if (!outboxItem) { outboxItem = new OutboxItem2(type); if (item instanceof NDKUser3) { this.trackUsers([item]); } } return outboxItem; } }; function getKeyFromItem2(item) { if (item instanceof NDKUser3) { return item.pubkey; } return item; } function getTypeFromItem2(item) { if (item instanceof NDKUser3) { return "user"; } return "kind"; } function correctRelaySet2(relaySet, pool) { const connectedRelays = pool.connectedRelays(); const includesConnectedRelay = Array.from(relaySet.relays).some((relay) => { return connectedRelays.map((r) => r.url).includes(relay.url); }); if (!includesConnectedRelay) { for (const relay of connectedRelays) { relaySet.addRelay(relay); } } if (connectedRelays.length === 0) { for (const relay of pool.relays.values()) { relaySet.addRelay(relay); } } return relaySet; } var NDKSubscriptionManager2 = class { constructor() { __publicField(this, "subscriptions"); __publicField(this, "seenEvents", new import_typescript_lru_cache32.LRUCache({ maxSize: 1e4, entryExpirationTimeInMS: 5 * 60 * 1e3 })); this.subscriptions = /* @__PURE__ */ new Map(); } add(sub) { this.subscriptions.set(sub.internalId, sub); if (sub.onStopped) { } sub.onStopped = () => { this.subscriptions.delete(sub.internalId); }; sub.on("close", () => { this.subscriptions.delete(sub.internalId); }); } seenEvent(eventId, relay) { const current = this.seenEvents.get(eventId) || []; if (!current.some((r) => r.url === relay.url)) { current.push(relay); } this.seenEvents.set(eventId, current); } dispatchEvent(event, relay, optimisticPublish = false) { if (relay) this.seenEvent(event.id, relay); const subscriptions = this.subscriptions.values(); const matchingSubs = []; for (const sub of subscriptions) { if (matchFilters3(sub.filters, event)) { matchingSubs.push(sub); } } for (const sub of matchingSubs) { if (sub.exclusiveRelay && sub.relaySet) { let shouldAccept = false; if (optimisticPublish) { shouldAccept = !sub.skipOptimisticPublishEvent; } else if (!relay) { const eventOnRelays = this.seenEvents.get(event.id) || []; shouldAccept = eventOnRelays.some((r) => sub.relaySet.relays.has(r)); } else { shouldAccept = sub.relaySet.relays.has(relay); } if (!shouldAccept) { sub.debug.extend("exclusive-relay")("Rejected event %s from %s (relay not in exclusive set)", event.id, relay?.url || (optimisticPublish ? "optimistic" : "cache")); continue; } } sub.eventReceived(event, relay, false, optimisticPublish); } } }; var debug63 = import_debug82.default("ndk:active-user"); async function getUserRelayList2(user) { if (!this.autoConnectUserRelays) return; const userRelays = await getRelayListForUser2(user.pubkey, this); if (!userRelays) return; for (const url of userRelays.relays) { let relay = this.pool.relays.get(url); if (!relay) { relay = new NDKRelay3(url, this.relayAuthDefaultPolicy, this); this.pool.addRelay(relay); } } debug63("Connected to %d user relays", userRelays.relays.length); return userRelays; } async function setActiveUser2(user) { if (!this.autoConnectUserRelays) return; const pool = this.outboxPool || this.pool; if (pool.connectedRelays.length > 0) { await getUserRelayList2.call(this, user); } else { pool.once("connect", async () => { await getUserRelayList2.call(this, user); }); } } function getEntity2(entity) { try { const decoded = nip19_exports3.decode(entity); if (decoded.type === "npub") return npub2(this, decoded.data); if (decoded.type === "nprofile") return nprofile2(this, decoded.data); return decoded; } catch (_e2) { return null; } } function npub2(ndk, pubkey) { return ndk.getUser({ pubkey }); } function nprofile2(ndk, profile) { const user = ndk.getUser({ pubkey: profile.pubkey }); if (profile.relays) user.relayUrls = profile.relays; return user; } function isValidHint2(hint) { if (!hint || hint === "") return false; try { new URL(hint); return true; } catch (_e2) { return false; } } async function fetchEventFromTag2(tag, originalEvent, subOpts, fallback = { type: "timeout" }) { const d42 = this.debug.extend("fetch-event-from-tag"); const [_2, id, hint] = tag; subOpts = {}; d42("fetching event from tag", tag, subOpts, fallback); const authorRelays = getRelaysForSync3(this, originalEvent.pubkey); if (authorRelays && authorRelays.size > 0) { d42("fetching event from author relays %o", Array.from(authorRelays)); const relaySet2 = NDKRelaySet3.fromRelayUrls(Array.from(authorRelays), this); const event2 = await this.fetchEvent(id, subOpts, relaySet2); if (event2) return event2; } else { d42("no author relays found for %s", originalEvent.pubkey, originalEvent); } const relaySet = calculateRelaySetsFromFilters2(this, [{ ids: [id] }], this.pool); d42("fetching event without relay hint", relaySet); const event = await this.fetchEvent(id, subOpts); if (event) return event; if (hint && hint !== "") { const event2 = await this.fetchEvent(id, subOpts, this.pool.getRelay(hint, true, true, [{ ids: [id] }])); if (event2) return event2; } let result; const relay = isValidHint2(hint) ? this.pool.getRelay(hint, false, true, [{ ids: [id] }]) : void 0; const fetchMaybeWithRelayHint = new Promise((resolve) => { this.fetchEvent(id, subOpts, relay).then(resolve); }); if (!isValidHint2(hint) || fallback.type === "none") { return fetchMaybeWithRelayHint; } const fallbackFetchPromise = new Promise(async (resolve) => { const fallbackRelaySet = fallback.relaySet; const timeout = fallback.timeout ?? 1500; const timeoutPromise = new Promise((resolve2) => setTimeout(resolve2, timeout)); if (fallback.type === "timeout") await timeoutPromise; if (result) { resolve(result); } else { d42("fallback fetch triggered"); const fallbackEvent = await this.fetchEvent(id, subOpts, fallbackRelaySet); resolve(fallbackEvent); } }); switch (fallback.type) { case "timeout": return Promise.race([fetchMaybeWithRelayHint, fallbackFetchPromise]); case "eose": result = await fetchMaybeWithRelayHint; if (result) return result; return fallbackFetchPromise; } } var Queue22 = class { constructor(_name, maxConcurrency) { __publicField(this, "queue", []); __publicField(this, "maxConcurrency"); __publicField(this, "processing", /* @__PURE__ */ new Set()); __publicField(this, "promises", /* @__PURE__ */ new Map()); this.maxConcurrency = maxConcurrency; } add(item) { if (this.promises.has(item.id)) { return this.promises.get(item.id); } const promise = new Promise((resolve, reject) => { this.queue.push({ ...item, func: () => item.func().then((result) => { resolve(result); return result; }, (error) => { reject(error); throw error; }) }); this.process(); }); this.promises.set(item.id, promise); promise.finally(() => { this.promises.delete(item.id); this.processing.delete(item.id); this.process(); }); return promise; } process() { if (this.processing.size >= this.maxConcurrency || this.queue.length === 0) { return; } const item = this.queue.shift(); if (!item || this.processing.has(item.id)) { return; } this.processing.add(item.id); item.func(); } clear() { this.queue = []; } clearProcessing() { this.processing.clear(); } clearAll() { this.clear(); this.clearProcessing(); } length() { return this.queue.length; } }; var DEFAULT_OUTBOX_RELAYS2 = ["wss://purplepag.es/", "wss://nos.lol/"]; var NDK2 = class extends import_tseep52.EventEmitter { constructor(opts = {}) { super(); __publicField(this, "_explicitRelayUrls"); __publicField(this, "pool"); __publicField(this, "outboxPool"); __publicField(this, "_signer"); __publicField(this, "_activeUser"); __publicField(this, "cacheAdapter"); __publicField(this, "debug"); __publicField(this, "devWriteRelaySet"); __publicField(this, "outboxTracker"); __publicField(this, "muteFilter"); __publicField(this, "relayConnectionFilter"); __publicField(this, "clientName"); __publicField(this, "clientNip89"); __publicField(this, "queuesZapConfig"); __publicField(this, "queuesNip05"); __publicField(this, "asyncSigVerification", false); __publicField(this, "initialValidationRatio", 1); __publicField(this, "lowestValidationRatio", 0.1); __publicField(this, "validationRatioFn"); __publicField(this, "filterValidationMode", "validate"); __publicField(this, "subManager"); __publicField(this, "aiGuardrails"); __publicField(this, "futureTimestampGrace"); __publicField(this, "_signatureVerificationFunction"); __publicField(this, "_signatureVerificationWorker"); __publicField(this, "signatureVerificationTimeMs", 0); __publicField(this, "publishingFailureHandled", false); __publicField(this, "pools", []); __publicField(this, "relayAuthDefaultPolicy"); __publicField(this, "httpFetch"); __publicField(this, "netDebug"); __publicField(this, "autoConnectUserRelays", true); __publicField(this, "_wallet"); __publicField(this, "walletConfig"); __publicField(this, "fetchEventFromTag", fetchEventFromTag2.bind(this)); __publicField(this, "getEntity", getEntity2.bind(this)); this.debug = opts.debug || import_debug72.default("ndk"); this.netDebug = opts.netDebug; this._explicitRelayUrls = opts.explicitRelayUrls || []; this.subManager = new NDKSubscriptionManager2(); this.pool = new NDKPool3(opts.explicitRelayUrls || [], this); this.pool.name = "Main"; this.pool.on("relay:auth", async (relay, challenge3) => { if (this.relayAuthDefaultPolicy) { await this.relayAuthDefaultPolicy(relay, challenge3); } }); this.autoConnectUserRelays = opts.autoConnectUserRelays ?? true; this.clientName = opts.clientName; this.clientNip89 = opts.clientNip89; this.relayAuthDefaultPolicy = opts.relayAuthDefaultPolicy; if (!(opts.enableOutboxModel === false)) { this.outboxPool = new NDKPool3(opts.outboxRelayUrls || DEFAULT_OUTBOX_RELAYS2, this, { debug: this.debug.extend("outbox-pool"), name: "Outbox Pool" }); this.outboxTracker = new OutboxTracker2(this); this.outboxTracker.on("user:relay-list-updated", (pubkey, _outboxItem) => { this.debug(`Outbox relay list updated for ${pubkey}`); for (const subscription of this.subManager.subscriptions.values()) { const isRelevant = subscription.filters.some((filter) => filter.authors?.includes(pubkey)); if (isRelevant && typeof subscription.refreshRelayConnections === "function") { this.debug(`Refreshing relay connections for subscription ${subscription.internalId}`); subscription.refreshRelayConnections(); } } }); } this.signer = opts.signer; this.cacheAdapter = opts.cacheAdapter; this.muteFilter = opts.muteFilter; this.relayConnectionFilter = opts.relayConnectionFilter; if (opts.devWriteRelayUrls) { this.devWriteRelaySet = NDKRelaySet3.fromRelayUrls(opts.devWriteRelayUrls, this); } this.queuesZapConfig = new Queue22("zaps", 3); this.queuesNip05 = new Queue22("nip05", 10); if (opts.signatureVerificationWorker) { this.signatureVerificationWorker = opts.signatureVerificationWorker; } if (opts.signatureVerificationFunction) { this.signatureVerificationFunction = opts.signatureVerificationFunction; } this.initialValidationRatio = opts.initialValidationRatio || 1; this.lowestValidationRatio = opts.lowestValidationRatio || 0.1; this.validationRatioFn = opts.validationRatioFn || this.defaultValidationRatioFn; this.filterValidationMode = opts.filterValidationMode || "validate"; this.aiGuardrails = new AIGuardrails2(opts.aiGuardrails || false); this.futureTimestampGrace = opts.futureTimestampGrace; this.aiGuardrails.ndkInstantiated(this); try { this.httpFetch = fetch; } catch { } } set explicitRelayUrls(urls) { this._explicitRelayUrls = urls.map(normalizeRelayUrl3); this.pool.relayUrls = urls; } get explicitRelayUrls() { return this._explicitRelayUrls || []; } set signatureVerificationWorker(worker22) { this._signatureVerificationWorker = worker22; if (worker22) { signatureVerificationInit2(worker22); this.asyncSigVerification = true; } else { this.asyncSigVerification = false; } } set signatureVerificationFunction(fn) { this._signatureVerificationFunction = fn; this.asyncSigVerification = !!fn; } get signatureVerificationFunction() { return this._signatureVerificationFunction; } addExplicitRelay(urlOrRelay, relayAuthPolicy, connect = true) { let relay; if (typeof urlOrRelay === "string") { relay = new NDKRelay3(urlOrRelay, relayAuthPolicy, this); } else { relay = urlOrRelay; } this.pool.addRelay(relay, connect); this.explicitRelayUrls?.push(relay.url); return relay; } toJSON() { return { relayCount: this.pool.relays.size }.toString(); } get activeUser() { return this._activeUser; } set activeUser(user) { const differentUser = this._activeUser?.pubkey !== user?.pubkey; this._activeUser = user; if (differentUser) { this.emit("activeUser:change", user); } if (user && differentUser) { setActiveUser2.call(this, user); } } get signer() { return this._signer; } set signer(newSigner) { this._signer = newSigner; if (newSigner) this.emit("signer:ready", newSigner); newSigner?.user().then((user) => { user.ndk = this; this.activeUser = user; }); } async connect(timeoutMs) { if (this._signer && this.autoConnectUserRelays) { this.debug("Attempting to connect to user relays specified by signer %o", await this._signer.relays?.(this)); if (this._signer.relays) { const relays = await this._signer.relays(this); relays.forEach((relay) => this.pool.addRelay(relay)); } } const connections = [this.pool.connect(timeoutMs)]; if (this.outboxPool) { connections.push(this.outboxPool.connect(timeoutMs)); } if (this.cacheAdapter?.initializeAsync) { connections.push(this.cacheAdapter.initializeAsync(this)); } return Promise.allSettled(connections).then(() => { }); } reportInvalidSignature(event, relay) { this.debug(`Invalid signature detected for event ${event.id}${relay ? ` from relay ${relay.url}` : ""}`); this.emit("event:invalid-sig", event, relay); } defaultValidationRatioFn(_relay, validatedCount, _nonValidatedCount) { if (validatedCount < 10) return this.initialValidationRatio; const trustFactor = Math.min(validatedCount / 100, 1); const calculatedRatio = this.initialValidationRatio * (1 - trustFactor) + this.lowestValidationRatio * trustFactor; return Math.max(calculatedRatio, this.lowestValidationRatio); } getUser(opts) { if (typeof opts === "string") { if (opts.startsWith("npub1")) { const { type, data } = nip19_exports3.decode(opts); if (type !== "npub") throw new Error(`Invalid npub: ${opts}`); return this.getUser({ pubkey: data }); } else if (opts.startsWith("nprofile1")) { const { type, data } = nip19_exports3.decode(opts); if (type !== "nprofile") throw new Error(`Invalid nprofile: ${opts}`); return this.getUser({ pubkey: data.pubkey, relayUrls: data.relays }); } else { return this.getUser({ pubkey: opts }); } } const user = new NDKUser3(opts); user.ndk = this; return user; } async getUserFromNip05(nip05, skipCache = false) { return NDKUser3.fromNip05(nip05, this, skipCache); } async fetchUser(input, skipCache = false) { if (isValidNip052(input)) { return NDKUser3.fromNip05(input, this, skipCache); } else if (input.startsWith("npub1")) { const { type, data } = nip19_exports3.decode(input); if (type !== "npub") throw new Error(`Invalid npub: ${input}`); const user = new NDKUser3({ pubkey: data }); user.ndk = this; return user; } else if (input.startsWith("nprofile1")) { const { type, data } = nip19_exports3.decode(input); if (type !== "nprofile") throw new Error(`Invalid nprofile: ${input}`); const user = new NDKUser3({ pubkey: data.pubkey, relayUrls: data.relays }); user.ndk = this; return user; } else { const user = new NDKUser3({ pubkey: input }); user.ndk = this; return user; } } subscribe(filters, opts, autoStartOrRelaySet = true, _autoStart = true) { let _relaySet = opts?.relaySet; let autoStart = _autoStart; if (autoStartOrRelaySet instanceof NDKRelaySet3) { console.warn("relaySet is deprecated, use opts.relaySet instead. This will be removed in version v2.14.0"); _relaySet = autoStartOrRelaySet; autoStart = _autoStart; } else if (typeof autoStartOrRelaySet === "boolean" || typeof autoStartOrRelaySet === "object") { autoStart = autoStartOrRelaySet; } const finalOpts = { relaySet: _relaySet, ...opts }; if (autoStart && typeof autoStart === "object") { if (autoStart.onEvent) finalOpts.onEvent = autoStart.onEvent; if (autoStart.onEose) finalOpts.onEose = autoStart.onEose; if (autoStart.onClose) finalOpts.onClose = autoStart.onClose; if (autoStart.onEvents) finalOpts.onEvents = autoStart.onEvents; } const subscription = new NDKSubscription2(this, filters, finalOpts); this.subManager.add(subscription); this.aiGuardrails?.subscription?.created(Array.isArray(filters) ? filters : [filters], finalOpts); const pool = subscription.pool; if (subscription.relaySet) { for (const relay of subscription.relaySet.relays) { pool.useTemporaryRelay(relay, void 0, subscription.filters); } } if (this.outboxPool && subscription.hasAuthorsFilter()) { const authors = subscription.filters.filter((filter) => filter.authors && filter.authors?.length > 0).flatMap((filter) => filter.authors); this.outboxTracker?.trackUsers(authors); } if (autoStart) { setTimeout(async () => { if (this.cacheAdapter?.initializeAsync && !this.cacheAdapter.ready) { await this.cacheAdapter.initializeAsync(this); } subscription.start(); }, 0); } return subscription; } fetchEventSync(idOrFilter) { if (!this.cacheAdapter) throw new Error("Cache adapter not set"); let filters; if (typeof idOrFilter === "string") filters = [filterFromId2(idOrFilter)]; else filters = idOrFilter; const sub = new NDKSubscription2(this, filters); const events = this.cacheAdapter.query(sub); if (events instanceof Promise) throw new Error("Cache adapter is async"); return events.map((e2) => { e2.ndk = this; return e2; }); } async fetchEvent(idOrFilter, opts, relaySetOrRelay) { let filters; let relaySet; if (relaySetOrRelay instanceof NDKRelay3) { relaySet = new NDKRelaySet3(/* @__PURE__ */ new Set([relaySetOrRelay]), this); } else if (relaySetOrRelay instanceof NDKRelaySet3) { relaySet = relaySetOrRelay; } if (!relaySetOrRelay && typeof idOrFilter === "string") { if (!isNip33AValue2(idOrFilter)) { const relays = relaysFromBech322(idOrFilter, this); if (relays.length > 0) { relaySet = new NDKRelaySet3(new Set(relays), this); relaySet = correctRelaySet2(relaySet, this.pool); } } } if (typeof idOrFilter === "string") { filters = [filterFromId2(idOrFilter)]; } else if (Array.isArray(idOrFilter)) { filters = idOrFilter; } else { filters = [idOrFilter]; } if (typeof idOrFilter !== "string") { this.aiGuardrails?.ndk?.fetchingEvents(filters); } if (filters.length === 0) { throw new Error(`Invalid filter: ${JSON.stringify(idOrFilter)}`); } return new Promise((resolve, reject) => { let fetchedEvent = null; const processEvent = (event) => { event.ndk = this; if (!event.isReplaceable()) { clearTimeout(t2); s?.stop(); this.aiGuardrails["_nextCallDisabled"] = null; resolve(event); } else if (!fetchedEvent || fetchedEvent.created_at < event.created_at) { fetchedEvent = event; } }; const subscribeOpts = { ...opts || {}, closeOnEose: true, onEvents: (cachedEvents) => { for (const event of cachedEvents) { processEvent(event); } }, onEvent: (event) => { processEvent(event); }, onEose: () => { clearTimeout(t2); this.aiGuardrails["_nextCallDisabled"] = null; resolve(fetchedEvent); } }; if (relaySet) subscribeOpts.relaySet = relaySet; let s; const t2 = setTimeout(() => { s?.stop(); this.aiGuardrails["_nextCallDisabled"] = null; resolve(fetchedEvent); }, 1e4); s = this.subscribe(filters, subscribeOpts); }); } async fetchEvents(filters, opts, relaySet) { this.aiGuardrails?.ndk?.fetchingEvents(filters, opts); return new Promise((resolve) => { const events = /* @__PURE__ */ new Map(); const processEvent = (event) => { let _event; if (!(event instanceof NDKEvent3)) _event = new NDKEvent3(void 0, event); else _event = event; const dedupKey = _event.deduplicationKey(); const existingEvent = events.get(dedupKey); if (existingEvent) { _event = dedup2(existingEvent, _event); } _event.ndk = this; events.set(dedupKey, _event); }; const subscribeOpts = { ...opts || {}, closeOnEose: true, onEvents: (cachedEvents) => { for (const event of cachedEvents) { processEvent(event); } }, onEvent: processEvent, onEose: () => { this.aiGuardrails["_nextCallDisabled"] = null; resolve(new Set(events.values())); } }; if (relaySet) subscribeOpts.relaySet = relaySet; const _relaySetSubscription = this.subscribe(filters, subscribeOpts); }); } assertSigner() { if (!this.signer) { this.emit("signer:required"); throw new Error("Signer required"); } } guardrailOff(ids) { if (!ids) { this.aiGuardrails["_nextCallDisabled"] = "all"; } else if (typeof ids === "string") { this.aiGuardrails["_nextCallDisabled"] = /* @__PURE__ */ new Set([ids]); } else { this.aiGuardrails["_nextCallDisabled"] = new Set(ids); } return this; } set wallet(wallet) { if (!wallet) { this._wallet = void 0; this.walletConfig = void 0; return; } this._wallet = wallet; this.walletConfig ?? (this.walletConfig = {}); this.walletConfig.lnPay = wallet?.lnPay?.bind(wallet); this.walletConfig.cashuPay = wallet?.cashuPay?.bind(wallet); } get wallet() { return this._wallet; } }; var nip19_exports22 = {}; __reExport3(nip19_exports22, exports_nip19); var nip49_exports3 = {}; __reExport3(nip49_exports3, exports_nip49); function disconnect3(pool, debug92) { debug92 ?? (debug92 = import_debug92.default("ndk:relay:auth-policies:disconnect")); return async (relay) => { debug92?.(`Relay ${relay.url} requested authentication, disconnecting`); pool.removeRelay(relay.url); }; } async function signAndAuth3(event, relay, signer, debug92, resolve, reject) { try { await event.sign(signer); resolve(event); } catch (e2) { debug92?.(`Failed to publish auth event to relay ${relay.url}`, e2); reject(event); } } function signIn3({ ndk, signer, debug: debug92 } = {}) { debug92 ?? (debug92 = import_debug92.default("ndk:auth-policies:signIn")); return async (relay, challenge3) => { debug92?.(`Relay ${relay.url} requested authentication, signing in`); const event = new NDKEvent3(ndk); event.kind = 22242; event.tags = [ ["relay", relay.url], ["challenge", challenge3] ]; signer ?? (signer = ndk?.signer); return new Promise(async (resolve, reject) => { if (signer) { await signAndAuth3(event, relay, signer, debug92, resolve, reject); } else { ndk?.once("signer:ready", async (signer2) => { await signAndAuth3(event, relay, signer2, debug92, resolve, reject); }); } }); }; } var NDKRelayAuthPolicies3 = { disconnect: disconnect3, signIn: signIn3 }; async function ndkSignerFromPayload3(payloadString, ndk) { let parsed; try { parsed = JSON.parse(payloadString); } catch (e2) { console.error("Failed to parse signer payload string", payloadString, e2); return; } if (!parsed || typeof parsed.type !== "string") { console.error("Failed to parse signer payload string", payloadString, new Error("Missing type field")); return; } const SignerClass = signerRegistry3.get(parsed.type); if (!SignerClass) { throw new Error(`Unknown signer type: ${parsed.type}`); } try { return await SignerClass.fromPayload(payloadString, ndk); } catch (e2) { const errorMsg = e2 instanceof Error ? e2.message : String(e2); throw new Error(`Failed to deserialize signer type ${parsed.type}: ${errorMsg}`); } } var NDKNip07Signer3 = class _NDKNip07Signer2 { constructor(waitTimeout = 1e3, ndk) { __publicField(this, "_userPromise"); __publicField(this, "encryptionQueue", []); __publicField(this, "encryptionProcessing", false); __publicField(this, "debug"); __publicField(this, "waitTimeout"); __publicField(this, "_pubkey"); __publicField(this, "ndk"); __publicField(this, "_user"); this.debug = import_debug102.default("ndk:nip07"); this.waitTimeout = waitTimeout; this.ndk = ndk; } get pubkey() { if (!this._pubkey) throw new Error("Not ready"); return this._pubkey; } async blockUntilReady() { await this.waitForExtension(); const pubkey = await window.nostr?.getPublicKey(); if (!pubkey) { throw new Error("User rejected access"); } this._pubkey = pubkey; let user; if (this.ndk) user = this.ndk.getUser({ pubkey }); else user = new NDKUser3({ pubkey }); this._user = user; return user; } async user() { if (!this._userPromise) { this._userPromise = this.blockUntilReady(); } return this._userPromise; } get userSync() { if (!this._user) throw new Error("User not ready"); return this._user; } async sign(event) { await this.waitForExtension(); const signedEvent = await window.nostr?.signEvent(event); if (!signedEvent) throw new Error("Failed to sign event"); return signedEvent.sig; } async relays(ndk) { await this.waitForExtension(); const relays = await window.nostr?.getRelays?.() || {}; const activeRelays = []; for (const url of Object.keys(relays)) { if (relays[url].read && relays[url].write) { activeRelays.push(url); } } return activeRelays.map((url) => new NDKRelay3(url, ndk?.relayAuthDefaultPolicy, ndk)); } async encryptionEnabled(nip) { const enabled = []; if ((!nip || nip === "nip04") && Boolean(window.nostr?.nip04)) enabled.push("nip04"); if ((!nip || nip === "nip44") && Boolean(window.nostr?.nip44)) enabled.push("nip44"); return enabled; } async encrypt(recipient, value, nip = "nip04") { if (!await this.encryptionEnabled(nip)) throw new Error(`${nip}encryption is not available from your browser extension`); await this.waitForExtension(); const recipientHexPubKey = recipient.pubkey; return this.queueEncryption(nip, "encrypt", recipientHexPubKey, value); } async decrypt(sender, value, nip = "nip04") { if (!await this.encryptionEnabled(nip)) throw new Error(`${nip}encryption is not available from your browser extension`); await this.waitForExtension(); const senderHexPubKey = sender.pubkey; return this.queueEncryption(nip, "decrypt", senderHexPubKey, value); } async queueEncryption(scheme, method, counterpartyHexpubkey, value) { return new Promise((resolve, reject) => { this.encryptionQueue.push({ scheme, method, counterpartyHexpubkey, value, resolve, reject }); if (!this.encryptionProcessing) { this.processEncryptionQueue(); } }); } async processEncryptionQueue(item, retries = 0) { if (!item && this.encryptionQueue.length === 0) { this.encryptionProcessing = false; return; } this.encryptionProcessing = true; const currentItem = item || this.encryptionQueue.shift(); if (!currentItem) { this.encryptionProcessing = false; return; } const { scheme, method, counterpartyHexpubkey, value, resolve, reject } = currentItem; this.debug("Processing encryption queue item", { method, counterpartyHexpubkey, value }); try { const result = await window.nostr?.[scheme]?.[method](counterpartyHexpubkey, value); if (!result) throw new Error("Failed to encrypt/decrypt"); resolve(result); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); if (errorMessage.includes("call already executing") && retries < 5) { this.debug("Retrying encryption queue item", { method, counterpartyHexpubkey, value, retries }); setTimeout(() => { this.processEncryptionQueue(currentItem, retries + 1); }, 50 * retries); return; } reject(error instanceof Error ? error : new Error(errorMessage)); } this.processEncryptionQueue(); } waitForExtension() { return new Promise((resolve, reject) => { if (window.nostr) { resolve(); return; } let timerId; const intervalId = setInterval(() => { if (window.nostr) { clearTimeout(timerId); clearInterval(intervalId); resolve(); } }, 100); timerId = setTimeout(() => { clearInterval(intervalId); reject(new Error("NIP-07 extension not available")); }, this.waitTimeout); }); } toPayload() { const payload = { type: "nip07", payload: "" }; return JSON.stringify(payload); } static async fromPayload(payloadString, ndk) { const payload = JSON.parse(payloadString); if (payload.type !== "nip07") { throw new Error(`Invalid payload type: expected 'nip07', got ${payload.type}`); } return new _NDKNip07Signer2(void 0, ndk); } }; registerSigner3("nip07", NDKNip07Signer3); var NDKNostrRpc3 = class extends import_tseep72.EventEmitter { constructor(ndk, signer, debug92, relayUrls) { super(); __publicField(this, "ndk"); __publicField(this, "signer"); __publicField(this, "relaySet"); __publicField(this, "debug"); __publicField(this, "encryptionType", "nip44"); __publicField(this, "pool"); this.ndk = ndk; this.signer = signer; if (relayUrls) { this.pool = new NDKPool3(relayUrls, ndk, { debug: debug92.extend("rpc-pool"), name: "Nostr RPC" }); this.relaySet = new NDKRelaySet3(/* @__PURE__ */ new Set(), ndk, this.pool); for (const url of relayUrls) { const relay = this.pool.getRelay(url, false, false); relay.authPolicy = NDKRelayAuthPolicies3.signIn({ ndk, signer, debug: debug92 }); this.relaySet.addRelay(relay); relay.connect(); } } this.debug = debug92.extend("rpc"); } subscribe(filter) { return new Promise((resolve) => { const sub = this.ndk.subscribe(filter, { closeOnEose: false, groupable: false, cacheUsage: "ONLY_RELAY", pool: this.pool, relaySet: this.relaySet, onEvent: async (event) => { try { const parsedEvent = await this.parseEvent(event); if (parsedEvent.method) { this.emit("request", parsedEvent); } else { this.emit(`response-${parsedEvent.id}`, parsedEvent); this.emit("response", parsedEvent); } } catch (e2) { this.debug("error parsing event", e2, event.rawEvent()); } }, onEose: () => { this.debug("eosed"); resolve(sub); } }); }); } async parseEvent(event) { if (this.encryptionType === "nip44" && event.content.includes("?iv=")) { this.encryptionType = "nip04"; } else if (this.encryptionType === "nip04" && !event.content.includes("?iv=")) { this.encryptionType = "nip44"; } const remoteUser = this.ndk.getUser({ pubkey: event.pubkey }); remoteUser.ndk = this.ndk; let decryptedContent; try { decryptedContent = await this.signer.decrypt(remoteUser, event.content, this.encryptionType); } catch (_e2) { const otherEncryptionType = this.encryptionType === "nip04" ? "nip44" : "nip04"; decryptedContent = await this.signer.decrypt(remoteUser, event.content, otherEncryptionType); this.encryptionType = otherEncryptionType; } const parsedContent = JSON.parse(decryptedContent); const { id, method, params, result, error } = parsedContent; if (method) { return { id, pubkey: event.pubkey, method, params, event }; } return { id, result, error, event }; } async sendResponse(id, remotePubkey, result, kind = 24133, error) { const res = { id, result }; if (error) { res.error = error; } const localUser = await this.signer.user(); const remoteUser = this.ndk.getUser({ pubkey: remotePubkey }); const event = new NDKEvent3(this.ndk, { kind, content: JSON.stringify(res), tags: [["p", remotePubkey]], pubkey: localUser.pubkey }); event.content = await this.signer.encrypt(remoteUser, event.content, this.encryptionType); await event.sign(this.signer); await event.publish(this.relaySet); } async sendRequest(remotePubkey, method, params = [], kind = 24133, cb) { const id = Math.random().toString(36).substring(7); const localUser = await this.signer.user(); const remoteUser = this.ndk.getUser({ pubkey: remotePubkey }); const request = { id, method, params }; const promise = new Promise(() => { const responseHandler = (response) => { if (response.result === "auth_url") { this.once(`response-${id}`, responseHandler); this.emit("authUrl", response.error); } else if (cb) { cb(response); } }; this.once(`response-${id}`, responseHandler); }); const event = new NDKEvent3(this.ndk, { kind, content: JSON.stringify(request), tags: [["p", remotePubkey]], pubkey: localUser.pubkey }); event.content = await this.signer.encrypt(remoteUser, event.content, this.encryptionType); await event.sign(this.signer); await event.publish(this.relaySet); return promise; } }; var ConnectEventHandlingStrategy2 = class { async handle(backend, id, remotePubkey, params) { const [_2, token] = params; const debug92 = backend.debug.extend("connect"); debug92(`connection request from ${remotePubkey}`); if (token && backend.applyToken) { debug92("applying token"); await backend.applyToken(remotePubkey, token); } if (await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "connect", params: token })) { debug92(`connection request from ${remotePubkey} allowed`); return "ack"; } debug92(`connection request from ${remotePubkey} rejected`); return; } }; var GetPublicKeyHandlingStrategy2 = class { async handle(backend, _id, _remotePubkey, _params) { return backend.localUser?.pubkey; } }; var Nip04DecryptHandlingStrategy2 = class { async handle(backend, id, remotePubkey, params) { const [senderPubkey, payload] = params; const senderUser = new NDKUser3({ pubkey: senderPubkey }); const decryptedPayload = await decrypt322(backend, id, remotePubkey, senderUser, payload); return decryptedPayload; } }; async function decrypt322(backend, id, remotePubkey, senderUser, payload) { if (!await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "nip04_decrypt", params: payload })) { backend.debug(`decrypt request from ${remotePubkey} rejected`); return; } return await backend.signer.decrypt(senderUser, payload, "nip04"); } var Nip04EncryptHandlingStrategy2 = class { async handle(backend, id, remotePubkey, params) { const [recipientPubkey, payload] = params; const recipientUser = new NDKUser3({ pubkey: recipientPubkey }); const encryptedPayload = await encrypt322(backend, id, remotePubkey, recipientUser, payload); return encryptedPayload; } }; async function encrypt322(backend, id, remotePubkey, recipientUser, payload) { if (!await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "nip04_encrypt", params: payload })) { backend.debug(`encrypt request from ${remotePubkey} rejected`); return; } return await backend.signer.encrypt(recipientUser, payload, "nip04"); } var Nip44DecryptHandlingStrategy2 = class { async handle(backend, id, remotePubkey, params) { const [senderPubkey, payload] = params; const senderUser = new NDKUser3({ pubkey: senderPubkey }); const decryptedPayload = await decrypt422(backend, id, remotePubkey, senderUser, payload); return decryptedPayload; } }; async function decrypt422(backend, id, remotePubkey, senderUser, payload) { if (!await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "nip44_decrypt", params: payload })) { backend.debug(`decrypt request from ${remotePubkey} rejected`); return; } return await backend.signer.decrypt(senderUser, payload, "nip44"); } var Nip44EncryptHandlingStrategy2 = class { async handle(backend, id, remotePubkey, params) { const [recipientPubkey, payload] = params; const recipientUser = new NDKUser3({ pubkey: recipientPubkey }); const encryptedPayload = await encrypt422(backend, id, remotePubkey, recipientUser, payload); return encryptedPayload; } }; async function encrypt422(backend, id, remotePubkey, recipientUser, payload) { if (!await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "nip44_encrypt", params: payload })) { backend.debug(`encrypt request from ${remotePubkey} rejected`); return; } return await backend.signer.encrypt(recipientUser, payload, "nip44"); } var PingEventHandlingStrategy2 = class { async handle(backend, id, remotePubkey, _params) { const debug92 = backend.debug.extend("ping"); debug92(`ping request from ${remotePubkey}`); if (await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "ping" })) { debug92(`connection request from ${remotePubkey} allowed`); return "pong"; } debug92(`connection request from ${remotePubkey} rejected`); return; } }; var SignEventHandlingStrategy2 = class { async handle(backend, id, remotePubkey, params) { const event = await signEvent2(backend, id, remotePubkey, params); if (!event) return; return JSON.stringify(await event.toNostrEvent()); } }; async function signEvent2(backend, id, remotePubkey, params) { const [eventString] = params; backend.debug(`sign event request from ${remotePubkey}`); const event = new NDKEvent3(backend.ndk, JSON.parse(eventString)); backend.debug("event to sign", event.rawEvent()); if (!await backend.pubkeyAllowed({ id, pubkey: remotePubkey, method: "sign_event", params: event })) { backend.debug(`sign event request from ${remotePubkey} rejected`); return; } backend.debug(`sign event request from ${remotePubkey} allowed`); await event.sign(backend.signer); return event; } var NDKNip46Backend2 = class { constructor(ndk, privateKeyOrSigner, permitCallback, relayUrls) { __publicField(this, "ndk"); __publicField(this, "signer"); __publicField(this, "localUser"); __publicField(this, "debug"); __publicField(this, "rpc"); __publicField(this, "permitCallback"); __publicField(this, "relayUrls"); __publicField(this, "handlers", { connect: new ConnectEventHandlingStrategy2(), sign_event: new SignEventHandlingStrategy2(), nip04_encrypt: new Nip04EncryptHandlingStrategy2(), nip04_decrypt: new Nip04DecryptHandlingStrategy2(), nip44_encrypt: new Nip44EncryptHandlingStrategy2(), nip44_decrypt: new Nip44DecryptHandlingStrategy2(), get_public_key: new GetPublicKeyHandlingStrategy2(), ping: new PingEventHandlingStrategy2() }); this.ndk = ndk; if (privateKeyOrSigner instanceof Uint8Array) { this.signer = new NDKPrivateKeySigner3(privateKeyOrSigner); } else if (privateKeyOrSigner instanceof String) { this.signer = new NDKPrivateKeySigner3(hexToBytes4(privateKeyOrSigner)); } else if (privateKeyOrSigner instanceof NDKPrivateKeySigner3) { this.signer = privateKeyOrSigner; } else { throw new Error("Invalid signer"); } this.debug = ndk.debug.extend("nip46:backend"); this.relayUrls = relayUrls ?? Array.from(ndk.pool.relays.keys()); this.rpc = new NDKNostrRpc3(ndk, this.signer, this.debug, this.relayUrls); this.permitCallback = permitCallback; } async start() { this.localUser = await this.signer.user(); this.ndk.subscribe({ kinds: [24133], "#p": [this.localUser.pubkey] }, { closeOnEose: false, onEvent: (e2) => this.handleIncomingEvent(e2) }); } setStrategy(method, strategy) { this.handlers[method] = strategy; } async applyToken(_pubkey, _token) { throw new Error("connection token not supported"); } async handleIncomingEvent(event) { const { id, method, params } = await this.rpc.parseEvent(event); const remotePubkey = event.pubkey; let response; let errorHandled = false; this.debug("incoming event", { id, method, params }); if (!event.verifySignature(false)) { this.debug("invalid signature", event.rawEvent()); return; } const strategy = this.handlers[method]; if (strategy) { try { response = await strategy.handle(this, id, remotePubkey, params); } catch (e2) { this.debug("error handling event", e2, { id, method, params }); errorHandled = true; try { await this.rpc.sendResponse(id, remotePubkey, "error", void 0, e2.message); } catch (sendError) { this.debug("failed to send error response", sendError); } } } else { this.debug("unsupported method", { method, params }); } if (!errorHandled) { try { if (response) { this.debug(`sending response to ${remotePubkey}`, response); await this.rpc.sendResponse(id, remotePubkey, response); } else { await this.rpc.sendResponse(id, remotePubkey, "error", void 0, "Not authorized"); } } catch (sendError) { this.debug("failed to send response", sendError); } } } async pubkeyAllowed(params) { return this.permitCallback(params); } }; function nostrConnectGenerateSecret3() { return Math.random().toString(36).substring(2, 15); } function generateNostrConnectUri3(pubkey, secret, relay, options) { const meta = { name: options?.name ? encodeURIComponent(options.name) : "", url: options?.url ? encodeURIComponent(options.url) : "", image: options?.image ? encodeURIComponent(options.image) : "", perms: options?.perms ? encodeURIComponent(options.perms) : "" }; let uri = `nostrconnect://${pubkey}?image=${meta.image}&url=${meta.url}&name=${meta.name}&perms=${meta.perms}&secret=${encodeURIComponent(secret)}`; if (relay) { uri += `&relay=${encodeURIComponent(relay)}`; } return uri; } var NDKNip46Signer3 = class _NDKNip46Signer2 extends import_tseep82.EventEmitter { constructor(ndk, userOrConnectionToken, localSigner, relayUrls, nostrConnectOptions) { super(); __publicField(this, "ndk"); __publicField(this, "_user"); __publicField(this, "bunkerPubkey"); __publicField(this, "userPubkey"); __publicField(this, "secret"); __publicField(this, "localSigner"); __publicField(this, "nip05"); __publicField(this, "rpc"); __publicField(this, "debug"); __publicField(this, "relayUrls"); __publicField(this, "subscription"); __publicField(this, "nostrConnectUri"); __publicField(this, "nostrConnectSecret"); this.ndk = ndk; this.debug = ndk.debug.extend("nip46:signer"); this.relayUrls = relayUrls; if (!localSigner) { this.localSigner = NDKPrivateKeySigner3.generate(); } else { if (typeof localSigner === "string") { this.localSigner = new NDKPrivateKeySigner3(localSigner); } else { this.localSigner = localSigner; } } if (userOrConnectionToken === false) { } else if (!userOrConnectionToken) { this.nostrconnectFlowInit(nostrConnectOptions); } else if (userOrConnectionToken.startsWith("bunker://")) { this.bunkerFlowInit(userOrConnectionToken); } else { this.nip05Init(userOrConnectionToken); } this.rpc = new NDKNostrRpc3(this.ndk, this.localSigner, this.debug, this.relayUrls); } get pubkey() { if (!this.userPubkey) throw new Error("Not ready"); return this.userPubkey; } static bunker(ndk, userOrConnectionToken, localSigner) { return new _NDKNip46Signer2(ndk, userOrConnectionToken, localSigner); } static nostrconnect(ndk, relay, localSigner, nostrConnectOptions) { return new _NDKNip46Signer2(ndk, void 0, localSigner, [relay], nostrConnectOptions); } nostrconnectFlowInit(nostrConnectOptions) { this.nostrConnectSecret = nostrConnectGenerateSecret3(); const pubkey = this.localSigner.pubkey; this.nostrConnectUri = generateNostrConnectUri3(pubkey, this.nostrConnectSecret, this.relayUrls?.[0], nostrConnectOptions); } bunkerFlowInit(connectionToken) { const bunkerUrl = new URL(connectionToken); const bunkerPubkey = bunkerUrl.hostname || bunkerUrl.pathname.replace(/^\/\//, ""); const userPubkey = bunkerUrl.searchParams.get("pubkey"); const relayUrls = bunkerUrl.searchParams.getAll("relay"); const secret = bunkerUrl.searchParams.get("secret"); this.bunkerPubkey = bunkerPubkey; this.userPubkey = userPubkey; this.relayUrls = relayUrls; this.secret = secret; } nip05Init(nip05) { this.nip05 = nip05; } async startListening() { if (this.subscription) return; const localUser = await this.localSigner.user(); if (!localUser) throw new Error("Local signer not ready"); this.subscription = await this.rpc.subscribe({ kinds: [24133], "#p": [localUser.pubkey] }); } async user() { if (this._user) return this._user; return this.blockUntilReady(); } get userSync() { if (!this._user) throw new Error("Remote user not ready synchronously"); return this._user; } async blockUntilReadyNostrConnect() { return new Promise((resolve, reject) => { const connect = (response) => { if (response.result === this.nostrConnectSecret) { this._user = response.event.author; this.userPubkey = response.event.pubkey; this.bunkerPubkey = response.event.pubkey; this.rpc.off("response", connect); resolve(this._user); } }; this.startListening(); this.rpc.on("response", connect); }); } async blockUntilReady() { if (!this.bunkerPubkey && !this.nostrConnectSecret && !this.nip05) { throw new Error("Bunker pubkey not set"); } if (this.nostrConnectSecret) return this.blockUntilReadyNostrConnect(); if (this.nip05 && !this.userPubkey) { const user = await NDKUser3.fromNip05(this.nip05, this.ndk); if (user) { this._user = user; this.userPubkey = user.pubkey; this.relayUrls = user.nip46Urls; this.rpc = new NDKNostrRpc3(this.ndk, this.localSigner, this.debug, this.relayUrls); } } if (!this.bunkerPubkey && this.userPubkey) { this.bunkerPubkey = this.userPubkey; } else if (!this.bunkerPubkey) { throw new Error("Bunker pubkey not set"); } await this.startListening(); this.rpc.on("authUrl", (...props) => { this.emit("authUrl", ...props); }); return new Promise((resolve, reject) => { const connectParams = [this.userPubkey ?? ""]; if (this.secret) connectParams.push(this.secret); if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest(this.bunkerPubkey, "connect", connectParams, 24133, (response) => { if (response.result === "ack") { this.getPublicKey().then((pubkey) => { this.userPubkey = pubkey; this._user = this.ndk.getUser({ pubkey }); resolve(this._user); }); } else { reject(response.error); } }); }); } stop() { this.subscription?.stop(); this.subscription = void 0; } async getPublicKey() { if (this.userPubkey) return this.userPubkey; return new Promise((resolve, _reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest(this.bunkerPubkey, "get_public_key", [], 24133, (response) => { resolve(response.result); }); }); } async encryptionEnabled(scheme) { if (scheme) return [scheme]; return Promise.resolve(["nip04", "nip44"]); } async encrypt(recipient, value, scheme = "nip04") { return this.encryption(recipient, value, scheme, "encrypt"); } async decrypt(sender, value, scheme = "nip04") { return this.encryption(sender, value, scheme, "decrypt"); } async encryption(peer, value, scheme, method) { const promise = new Promise((resolve, reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest(this.bunkerPubkey, `${scheme}_${method}`, [peer.pubkey, value], 24133, (response) => { if (!response.error) { resolve(response.result); } else { reject(response.error); } }); }); return promise; } async sign(event) { const promise = new Promise((resolve, reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest(this.bunkerPubkey, "sign_event", [JSON.stringify(event)], 24133, (response) => { if (!response.error) { const json = JSON.parse(response.result); resolve(json.sig); } else { reject(response.error); } }); }); return promise; } async createAccount(username, domain, email) { await this.startListening(); const req = []; if (username) req.push(username); if (domain) req.push(domain); if (email) req.push(email); return new Promise((resolve, reject) => { if (!this.bunkerPubkey) throw new Error("Bunker pubkey not set"); this.rpc.sendRequest(this.bunkerPubkey, "create_account", req, 24133, (response) => { if (!response.error) { const pubkey = response.result; resolve(pubkey); } else { reject(response.error); } }); }); } toPayload() { if (!this.bunkerPubkey || !this.userPubkey) { throw new Error("NIP-46 signer is not fully initialized for serialization"); } const payload = { type: "nip46", payload: { bunkerPubkey: this.bunkerPubkey, userPubkey: this.userPubkey, relayUrls: this.relayUrls, secret: this.secret, localSignerPayload: this.localSigner.toPayload(), nip05: this.nip05 || null } }; return JSON.stringify(payload); } static async fromPayload(payloadString, ndk) { if (!ndk) { throw new Error("NDK instance is required to deserialize NIP-46 signer"); } const parsed = JSON.parse(payloadString); if (parsed.type !== "nip46") { throw new Error(`Invalid payload type: expected 'nip46', got ${parsed.type}`); } const payload = parsed.payload; if (!payload || typeof payload !== "object" || !payload.localSignerPayload) { throw new Error("Invalid payload content for nip46 signer"); } const localSigner = await ndkSignerFromPayload3(payload.localSignerPayload, ndk); if (!localSigner) { throw new Error("Failed to deserialize local signer for NIP-46"); } if (!(localSigner instanceof NDKPrivateKeySigner3)) { throw new Error("Local signer must be an instance of NDKPrivateKeySigner"); } let signer; signer = new _NDKNip46Signer2(ndk, false, localSigner, payload.relayUrls); signer.userPubkey = payload.userPubkey; signer.bunkerPubkey = payload.bunkerPubkey; signer.relayUrls = payload.relayUrls; signer.secret = payload.secret; if (payload.userPubkey) { signer._user = new NDKUser3({ pubkey: payload.userPubkey }); if (signer._user) signer._user.ndk = ndk; } return signer; } }; registerSigner3("nip46", NDKNip46Signer3); function matchFilter22(filter, event) { if (filter.ids && filter.ids.indexOf(event.id) === -1) { return false; } if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) { return false; } if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) { return false; } for (const f in filter) { if (f[0] === "#") { const tagName = f.slice(1); if (tagName === "t") { const values = filter[`#${tagName}`]?.map((v6) => v6.toLowerCase()); if (values && !event.tags.find(([t, v6]) => t === tagName && values?.indexOf(v6.toLowerCase()) !== -1)) return false; } else { const values = filter[`#${tagName}`]; if (values && !event.tags.find(([t, v6]) => t === tagName && values?.indexOf(v6) !== -1)) return false; } } } if (filter.since && event.created_at < filter.since) return false; if (filter.until && event.created_at > filter.until) return false; return true; } var d23 = import_debug122.default("ndk:zapper:ln"); var d33 = import_debug112.default("ndk:zapper"); async function getDecryptedEvent(eventId) { await this.ensureInitialized(); const result = await this.postWorkerMessage({ type: "getDecryptedEvent", payload: { wrapperId: eventId } }); if (result && result.event) { try { const nostrEvent = deserialize3(result.event); return new NDKEvent3(this.ndk, nostrEvent); } catch (e2) { console.error("[getDecryptedEvent] Parse error:", e2); return null; } } return null; } async function getEvent(id) { await this.ensureInitialized(); if (this.degradedMode) return null; const result = await this.postWorkerMessage({ type: "getEvent", payload: { id } }); if (result && result.raw) { try { return JSON.parse(result.raw); } catch { return null; } } return null; } async function getProfiles(filter) { await this.ensureInitialized(); if (typeof filter === "function") { throw new Error("getProfiles with filter functions is not supported in worker mode. Use filter descriptors instead."); } const result = await this.postWorkerMessage({ type: "getProfiles", payload: filter }); const map = /* @__PURE__ */ new Map(); for (const { pubkey, profile } of result) { map.set(pubkey, profile); } return map; } async function getRelayStatus(relayUrl) { await this.ensureInitialized(); const cached = this.metadataCache?.getRelayInfo(relayUrl); if (cached) { return cached; } const result = await this.postWorkerMessage({ type: "getRelayStatus", payload: { relayUrl } }); if (result && result.info) { try { const info = JSON.parse(result.info); this.metadataCache?.setRelayInfo(relayUrl, info); return info; } catch { return; } } return; } async function getUnpublishedEvents2() { await this.ensureInitialized(); const results = await this.postWorkerMessage({ type: "getUnpublishedEvents", payload: {} }); const events = []; if (results) { for (const row of results) { try { const event = JSON.parse(row.event); const relays = row.relays ? JSON.parse(row.relays) : []; events.push({ event, relays, lastTryAt: row.lastTryAt }); } catch { } } } return events; } async function loadNip05(nip05, maxAgeForMissing = 3600) { await this.ensureInitialized(); const cached = this.metadataCache?.getNip05(nip05); if (cached !== void 0) { if (cached.profile === null || cached.profile === void 0) { const now3 = Date.now(); if (cached.fetched_at && cached.fetched_at + maxAgeForMissing * 1e3 < now3) { return "missing"; } return null; } return cached.profile; } const result = await this.postWorkerMessage({ type: "loadNip05", payload: { nip05 } }); if (!result) return "missing"; const now2 = Date.now(); this.metadataCache?.setNip05(nip05, result); if (result.profile === null || result.profile === void 0) { if (result.fetched_at && result.fetched_at + maxAgeForMissing * 1e3 < now2) { return "missing"; } return null; } try { return JSON.parse(result.profile); } catch { return "missing"; } } function query(subscription) { if (this.degradedMode) return []; if (!this.ready) { if (this.initializationPromise) { return this.initializationPromise.then(async () => { if (this.degradedMode) return []; return await queryWorker.call(this, subscription); }); } return []; } return queryWorker.call(this, subscription); } async function queryWorker(subscription) { const cacheFilters = filterForCache(subscription); const result = await this.postWorkerMessage({ type: "query", payload: { filters: cacheFilters, cacheUnconstrainFilter: subscription.cacheUnconstrainFilter, subId: subscription.subId } }); let eventsData; if (result.type === "json") { eventsData = result.events; } else if (result.type === "binary") { const { decodeEvents: decodeEvents22 } = await Promise.resolve().then(() => (init_decoder(), exports_decoder)); try { eventsData = decodeEvents22(result.buffer); } catch (error) { console.error("Failed to decode events from cache, cache may be corrupted:", error); return []; } } else { console.error("Unknown result type from worker:", result.type); return []; } const results = /* @__PURE__ */ new Map(); for (const filter of cacheFilters) { const eventsWithRelay = foundEvents2(subscription, eventsData, filter); for (const { event, relayUrl } of eventsWithRelay) { if (event && event.id) { results.set(event.id, event); this.addCachedEventId(event.id); if (relayUrl) { const relay = subscription.pool.getRelay(relayUrl, false); if (relay) { event.relay = relay; if (subscription.ndk) { subscription.ndk.subManager.seenEvent(event.id, relay); } } } } } } return Array.from(results.values()); } function filterForCache(subscription) { if (!subscription.cacheUnconstrainFilter) return subscription.filters; const filterCopy = subscription.filters.map((filter) => ({ ...filter })); return filterCopy.filter((filter) => { for (const key of subscription.cacheUnconstrainFilter) { delete filter[key]; } return Object.keys(filter).length > 0; }); } function foundEvents2(subscription, records, filter) { const result = []; let now2; for (const record of records) { const eventWithRelay = foundEvent2(subscription, record, record.relay, filter); if (eventWithRelay) { const expiration = eventWithRelay.event.tagValue("expiration"); if (expiration) { now2 ?? (now2 = Math.floor(Date.now() / 1e3)); if (now2 > Number.parseInt(expiration)) continue; } result.push(eventWithRelay); if (filter?.limit && result.length >= filter.limit) break; } } return result; } function foundEvent2(subscription, record, relayUrl, filter) { try { let eventData; if ("raw" in record && record.raw !== void 0 && record.raw !== null) { const rawParsed = JSON.parse(record.raw); if (Array.isArray(rawParsed)) { eventData = { id: rawParsed[0], pubkey: rawParsed[1], created_at: rawParsed[2], kind: rawParsed[3], tags: rawParsed[4], content: rawParsed[5], sig: rawParsed[6] }; } else { eventData = rawParsed; } } else { eventData = { id: record.id, pubkey: record.pubkey, created_at: record.created_at, kind: record.kind, tags: record.tags, content: record.content, sig: record.sig }; } if (filter && !matchFilter22(filter, eventData)) return null; const ndkEvent = new NDKEvent3(void 0, eventData); const relayUrl2 = ("relay_url" in record ? record.relay_url : null) || null; return { event: ndkEvent, relayUrl: relayUrl2 }; } catch (e2) { console.error("failed to deserialize event", e2, "record:", record, "record.raw:", "raw" in record ? record.raw : void 0); return null; } } async function saveNip05(nip05, profile) { const profileStr = profile ? JSON.stringify(profile) : null; const fetchedAt = Date.now(); await this.ensureInitialized(); this.metadataCache?.setNip05(nip05, { profile: profileStr, fetched_at: fetchedAt }); await this.postWorkerMessage({ type: "saveNip05", payload: { nip05, profile: profileStr, fetchedAt } }); } async function saveProfile(pubkey, profile) { const profileStr = JSON.stringify(profile); const updatedAt = Math.floor(Date.now() / 1e3); await this.ensureInitialized(); const entry = { ...profile, cachedAt: updatedAt }; this.metadataCache?.setProfile(pubkey, entry); if (this.degradedMode) return; await this.postWorkerMessage({ type: "saveProfile", payload: { pubkey, profile: profileStr, updatedAt } }); } async function setEvent(event, _filters, _relay) { await this.ensureInitialized(); if (this.degradedMode) return; await this.batchEvent({ id: event.id, pubkey: event.pubkey, created_at: event.created_at, kind: event.kind, tags: event.tags, content: event.content, sig: event.sig }, _relay?.url); } async function updateRelayStatus(relayUrl, info) { const existing = await this.getRelayStatus(relayUrl); const merged = { ...existing, ...info, metadata: { ...existing?.metadata, ...info.metadata } }; if (merged.metadata) { for (const [key, value] of Object.entries(merged.metadata)) { if (value === void 0) { delete merged.metadata[key]; } } } await this.ensureInitialized(); this.metadataCache?.setRelayInfo(relayUrl, merged); await this.postWorkerMessage({ type: "updateRelayStatus", payload: { relayUrl, info: JSON.stringify(merged) } }); } var MetadataLRUCache = class { constructor(maxSize = 1e3) { __publicField(this, "profiles"); __publicField(this, "relayInfo"); __publicField(this, "nip05"); __publicField(this, "maxSize"); __publicField(this, "profileAccessOrder"); __publicField(this, "relayAccessOrder"); __publicField(this, "nip05AccessOrder"); this.profiles = /* @__PURE__ */ new Map(); this.relayInfo = /* @__PURE__ */ new Map(); this.nip05 = /* @__PURE__ */ new Map(); this.maxSize = maxSize; this.profileAccessOrder = []; this.relayAccessOrder = []; this.nip05AccessOrder = []; } getProfile(pubkey) { const entry = this.profiles.get(pubkey); if (entry) { const index = this.profileAccessOrder.indexOf(pubkey); if (index > -1) { this.profileAccessOrder.splice(index, 1); } this.profileAccessOrder.push(pubkey); } return entry?.value; } setProfile(pubkey, profile) { this.profiles.set(pubkey, { value: profile, timestamp: Date.now() }); const index = this.profileAccessOrder.indexOf(pubkey); if (index > -1) { this.profileAccessOrder.splice(index, 1); } this.profileAccessOrder.push(pubkey); if (this.profiles.size > this.maxSize) { const oldest = this.profileAccessOrder.shift(); if (oldest) { this.profiles.delete(oldest); } } } deleteProfile(pubkey) { this.profiles.delete(pubkey); const index = this.profileAccessOrder.indexOf(pubkey); if (index > -1) { this.profileAccessOrder.splice(index, 1); } } getRelayInfo(url) { const entry = this.relayInfo.get(url); if (entry) { const index = this.relayAccessOrder.indexOf(url); if (index > -1) { this.relayAccessOrder.splice(index, 1); } this.relayAccessOrder.push(url); } return entry?.value; } setRelayInfo(url, info) { this.relayInfo.set(url, { value: info, timestamp: Date.now() }); const index = this.relayAccessOrder.indexOf(url); if (index > -1) { this.relayAccessOrder.splice(index, 1); } this.relayAccessOrder.push(url); if (this.relayInfo.size > this.maxSize) { const oldest = this.relayAccessOrder.shift(); if (oldest) { this.relayInfo.delete(oldest); } } } deleteRelayInfo(url) { this.relayInfo.delete(url); const index = this.relayAccessOrder.indexOf(url); if (index > -1) { this.relayAccessOrder.splice(index, 1); } } getNip05(nip05) { const entry = this.nip05.get(nip05); if (entry) { const index = this.nip05AccessOrder.indexOf(nip05); if (index > -1) { this.nip05AccessOrder.splice(index, 1); } this.nip05AccessOrder.push(nip05); } return entry?.value; } setNip05(nip05, result) { this.nip05.set(nip05, { value: result, timestamp: Date.now() }); const index = this.nip05AccessOrder.indexOf(nip05); if (index > -1) { this.nip05AccessOrder.splice(index, 1); } this.nip05AccessOrder.push(nip05); if (this.nip05.size > this.maxSize) { const oldest = this.nip05AccessOrder.shift(); if (oldest) { this.nip05.delete(oldest); } } } deleteNip05(nip05) { this.nip05.delete(nip05); const index = this.nip05AccessOrder.indexOf(nip05); if (index > -1) { this.nip05AccessOrder.splice(index, 1); } } clear() { this.profiles.clear(); this.relayInfo.clear(); this.nip05.clear(); this.profileAccessOrder = []; this.relayAccessOrder = []; this.nip05AccessOrder = []; } getMetrics() { return { profileCount: this.profiles.size, relayInfoCount: this.relayInfo.size, nip05Count: this.nip05.size, totalCount: this.profiles.size + this.relayInfo.size + this.nip05.size, maxSize: this.maxSize }; } }; var PACKAGE_VERSION = "0.8.2"; var NDKCacheAdapterSqliteWasm = class { constructor(options = {}) { __publicField(this, "dbName"); __publicField(this, "wasmUrl"); __publicField(this, "locking", false); __publicField(this, "ndk"); __publicField(this, "ready", false); __publicField(this, "worker"); __publicField(this, "workerUrl"); __publicField(this, "pendingRequests", /* @__PURE__ */ new Map()); __publicField(this, "nextRequestId", 0); __publicField(this, "initializationPromise"); __publicField(this, "degradedMode", false); __publicField(this, "metadataCache"); __publicField(this, "cachedEventIds", /* @__PURE__ */ new Set()); __publicField(this, "eventBatch", []); __publicField(this, "batchTimeout", null); __publicField(this, "BATCH_DELAY_MS", 0); __publicField(this, "MAX_BATCH_SIZE", 100); __publicField(this, "saveDebounceMs"); __publicField(this, "disableAutosave"); this.dbName = options.dbName || "ndk-cache"; this.wasmUrl = options.wasmUrl; this.workerUrl = options.workerUrl; this.saveDebounceMs = options.saveDebounceMs; this.disableAutosave = options.disableAutosave; this.metadataCache = new MetadataLRUCache(options.metadataLruSize || 1e3); } addCachedEventId(eventId) { this.cachedEventIds.add(eventId); } hasCachedEvent(eventId) { return this.cachedEventIds.has(eventId); } async initializeAsync(ndk) { if (this.initializationPromise) { return this.initializationPromise; } this.initializationPromise = (async () => { this.ndk = ndk; try { await this.initializeWorker(); this.ready = true; } catch (error) { this.degradedMode = true; const errorMsg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase(); const isWasmError = errorMsg.includes("wasm") || errorMsg.includes("webassembly") || errorMsg.includes("compile") || errorMsg.includes("instantiate"); if (isWasmError) { console.warn(`[NDK Cache SQLite WASM] Running in degraded mode - WebAssembly unavailable. This is expected in: \u2022 iOS/iPadOS Lockdown Mode \u2022 Browsers with WASM disabled \u2022 Restricted security environments The app will continue to function normally, but events will not be cached locally. All data will be fetched directly from Nostr relays.`); } else { console.error(`[NDK Cache SQLite WASM] Initialization failed, running in degraded mode. Cache will not persist data, but app will continue to function. Error:`, error); } } })(); return this.initializationPromise; } async initializeWorker() { let effectiveWorkerUrl = this.workerUrl; if (!effectiveWorkerUrl) { try { effectiveWorkerUrl = new URL("./worker.js", import_meta.url).toString(); } catch (e2) { console.error("Failed to determine worker URL automatically. Please provide 'workerUrl' option.", e2); throw new Error("Worker URL configuration error."); } } try { this.worker = new Worker(effectiveWorkerUrl, { type: "module" }); } catch (err) { let hasMessage = function(e2) { return typeof e2 === "object" && e2 !== null && "message" in e2 && typeof e2.message === "string"; }; const msg = hasMessage(err) ? err.message.toLowerCase() : ""; if (msg.includes("404") || msg.includes("not found") || msg.includes("failed to fetch") || msg.includes("networkerror") || msg.includes("could not load") || msg.includes("cannot find")) { console.error(`[NDK-cache-sqlite-wasm] Failed to load worker file at "${effectiveWorkerUrl}". This usually means the worker asset is missing or not served correctly (e.g., 404 error). Please ensure the worker file exists at the specified URL and is accessible to the browser. Check your bundler configuration and asset paths. See the documentation for details.`, err); } else { console.error(`[NDK-cache-sqlite-wasm] Error while creating worker at "${effectiveWorkerUrl}":`, err); } throw err; } this.worker.onmessage = (event) => { const data = event.data; if (data.type === "warmupProfiles") { this.handleProfileWarmup(data.profiles); return; } if (data._protocol && data._protocol !== "ndk-cache-sqlite") { console.error("[NDK Cache SQLite WASM] \u274C Wrong worker protocol!", ` Expected: ndk-cache-sqlite`, ` Received: ${data._protocol}`, ` This means the wrong worker instance was passed to the cache adapter.`, ` Make sure you are using the correct worker file for the cache.`); return; } if (data._version && data._version !== PACKAGE_VERSION) { console.warn("[NDK Cache SQLite WASM] \u26A0\uFE0F Worker version mismatch!", ` Library version: ${PACKAGE_VERSION}`, ` Worker version: ${data._version}`, ` Update your worker file:`, ` cp node_modules/@nostr-dev-kit/cache-sqlite-wasm/dist/worker.js public/`); } const { id, result, error } = data; const pending = this.pendingRequests.get(id); if (pending) { if (error) { pending.reject(new Error(`Worker error: ${error.message || error}`)); } else { pending.resolve(result); } this.pendingRequests.delete(id); } }; this.worker.onerror = (event) => { const errorMsg = event.message || "unknown error"; console.error(`[NDK-cache-sqlite-wasm] \u274C Worker failed: ${errorMsg} \u{1F527} Common solutions: 1. Copy worker.js and sql-wasm.wasm to your public directory: cp node_modules/@nostr-dev-kit/cache-sqlite-wasm/dist/worker.js public/ cp node_modules/@nostr-dev-kit/cache-sqlite-wasm/dist/sql-wasm.wasm public/ 2. Ensure your bundler serves the public directory correctly 3. Check browser DevTools Network tab for 404 errors on worker.js Current workerUrl: ${effectiveWorkerUrl}`); this.pendingRequests.forEach((p5) => p5.reject(new Error(`Worker failed: ${errorMsg}. Check console for setup instructions.`))); this.pendingRequests.clear(); }; await this.postWorkerMessage({ type: "init", payload: { dbName: this.dbName, wasmUrl: this.wasmUrl, saveDebounceMs: this.saveDebounceMs, disableAutosave: this.disableAutosave } }); } async postWorkerMessage(message) { if (!this.worker) { return Promise.reject(new Error("Worker not initialized")); } const id = `req-${this.nextRequestId++}`; return new Promise((resolve, reject) => { this.pendingRequests.set(id, { resolve, reject }); const msg = { ...message, id }; this.worker.postMessage(msg); }); } handleProfileWarmup(profiles) { for (const { pubkey, profile } of profiles) { this.metadataCache.setProfile(pubkey, profile); } } async flushEventBatch() { if (this.eventBatch.length === 0) return; const batch = this.eventBatch; this.eventBatch = []; this.batchTimeout = null; try { await this.postWorkerMessage({ type: "setEventBatch", payload: { events: batch.map((item) => ({ event: item.event, relay: item.relay })) } }); batch.forEach((item) => item.resolve()); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); batch.forEach((item) => item.reject(err)); } } batchEvent(event, relay) { if (event.id && this.cachedEventIds.has(event.id)) { return Promise.resolve(); } return new Promise((resolve, reject) => { if (event.id) { this.cachedEventIds.add(event.id); } this.eventBatch.push({ event, relay, resolve, reject }); if (this.eventBatch.length >= this.MAX_BATCH_SIZE) { if (this.batchTimeout !== null) { clearTimeout(this.batchTimeout); this.batchTimeout = null; } this.flushEventBatch(); } else if (this.batchTimeout === null) { this.batchTimeout = setTimeout(() => { this.flushEventBatch(); }, this.BATCH_DELAY_MS); } }); } async setEvent(event, filters, relay) { return setEvent.call(this, event, filters, relay); } async getEvent(eventId) { return getEvent.call(this, eventId); } async fetchProfile(pubkey) { return fetchProfile.call(this, pubkey); } async saveProfile(pubkey, profile) { return saveProfile.call(this, pubkey, profile); } async updateRelayStatus(relayUrl, status) { return updateRelayStatus.call(this, relayUrl, status); } async getRelayStatus(relayUrl) { return getRelayStatus.call(this, relayUrl); } async getDecryptedEvent(eventId) { return getDecryptedEvent.call(this, eventId); } async addDecryptedEvent(wrapperId, decryptedEvent) { return addDecryptedEvent.call(this, wrapperId, decryptedEvent); } async addUnpublishedEvent(event, relayUrls, lastTryAt = Date.now()) { return addUnpublishedEvent2.call(this, event, relayUrls, lastTryAt); } async getUnpublishedEvents() { return getUnpublishedEvents2.call(this); } async discardUnpublishedEvent(eventId) { return discardUnpublishedEvent2.call(this, eventId); } query(subscription) { return query.call(this, subscription); } async getProfiles(filter) { return getProfiles.call(this, filter); } async getCacheStats() { return getCacheStats.call(this); } async loadNip05(nip05) { return loadNip05.call(this, nip05); } async saveNip05(nip05, profile) { return saveNip05.call(this, nip05, profile); } getMetadataCacheStatus() { return this.metadataCache.getMetrics(); } clearMetadataCache() { this.metadataCache.clear(); } async getCacheData(namespace, key, maxAgeInSecs) { await this.ensureInitialized(); const result = await this.postWorkerMessage({ type: "getCacheData", payload: { namespace, key, maxAgeInSecs } }); return result; } async setCacheData(namespace, key, data) { await this.ensureInitialized(); await this.postWorkerMessage({ type: "setCacheData", payload: { namespace, key, data } }); } async ensureInitialized() { if (this.ready) return; if (this.degradedMode) return; if (this.initializationPromise) { await this.initializationPromise; } } }; var src_default = NDKCacheAdapterSqliteWasm; // ndk/cache-browser/src/index.ts var import_debug31 = __toESM(require_browser()); // ndk/cache-browser/src/storage.ts var import_debug30 = __toESM(require_browser()); var debug11 = (0, import_debug30.default)("ndk:cache-browser:storage"); var STORAGE_KEY = "ndk-cache-adapter-preference"; function getPreferredAdapter() { try { if (typeof localStorage === "undefined") { debug11("localStorage not available"); return null; } const stored = localStorage.getItem(STORAGE_KEY); if (stored === "wasm" || stored === "dexie") { debug11("Retrieved preferred adapter: %s", stored); return stored; } debug11("No valid preference found"); return null; } catch (error) { debug11("Error reading from localStorage: %o", error); return null; } } function setPreferredAdapter(type) { try { if (typeof localStorage === "undefined") { debug11("localStorage not available for saving preference"); return; } localStorage.setItem(STORAGE_KEY, type); debug11("Saved preferred adapter: %s", type); } catch (error) { debug11("Error writing to localStorage: %o", error); } } function clearPreferredAdapter() { try { if (typeof localStorage === "undefined") { return; } localStorage.removeItem(STORAGE_KEY); debug11("Cleared adapter preference"); } catch (error) { debug11("Error clearing localStorage: %o", error); } } // ndk/cache-browser/src/index.ts var debug12 = (0, import_debug31.default)("ndk:cache-browser"); var NDKCacheBrowser = class { constructor(options = {}) { this.adapter = null; this.adapterType = "none"; this.initPromise = null; this.locking = true; this.options = options; if (options.debug) { import_debug31.default.enable("ndk:cache-browser*"); } } /** * Get the currently active adapter type */ getAdapterType() { return this.adapterType; } /** * Get the underlying adapter instance (for advanced use cases) */ getAdapter() { return this.adapter; } /** * Initialize the cache adapter with automatic fallback logic */ async initializeAsync(ndk) { if (this.initPromise) { return this.initPromise; } this.initPromise = this._initialize(ndk); return this.initPromise; } async _initialize(ndk) { debug12("Initializing cache adapter"); if (this.options.forceAdapter) { debug12("Forcing adapter: %s", this.options.forceAdapter); if (this.options.forceAdapter === "wasm") { await this.tryWasmAdapter(ndk); } else { await this.tryDexieAdapter(); } return; } const preferred = getPreferredAdapter(); debug12("Preferred adapter from storage: %s", preferred); const tryOrder = preferred === "dexie" ? ["dexie", "wasm"] : ["wasm", "dexie"]; debug12("Try order: %o", tryOrder); for (const adapterType of tryOrder) { if (adapterType === "wasm") { if (await this.tryWasmAdapter(ndk)) { setPreferredAdapter("wasm"); debug12("Successfully initialized with WASM adapter"); return; } } else if (adapterType === "dexie") { if (await this.tryDexieAdapter()) { setPreferredAdapter("dexie"); debug12("Successfully initialized with Dexie adapter"); return; } } } debug12("\u26A0\uFE0F All cache adapters failed - running in degraded mode (no persistent cache)"); this.adapterType = "none"; this.adapter = null; } async tryWasmAdapter(ndk) { try { debug12("Attempting to initialize WASM adapter"); if (!this.options.workerUrl || !this.options.wasmUrl) { debug12("WASM adapter requires workerUrl and wasmUrl options"); return false; } const wasmAdapter = new src_default({ dbName: this.options.dbName || "ndk-cache", workerUrl: this.options.workerUrl, wasmUrl: this.options.wasmUrl }); await wasmAdapter.initializeAsync(ndk); if (wasmAdapter.degradedMode) { debug12("WASM adapter entered degraded mode - WASM unavailable"); return false; } this.adapter = wasmAdapter; this.adapterType = "wasm"; debug12("\u2705 WASM adapter initialized successfully"); return true; } catch (error) { debug12("WASM adapter initialization failed: %o", error); return false; } } async tryDexieAdapter() { try { debug12("Attempting to initialize Dexie adapter"); const dexieAdapter = new NDKCacheAdapterDexie({ dbName: this.options.dbName || "ndk-cache" }); if (dexieAdapter.warmUpPromise) { await dexieAdapter.warmUpPromise; } this.adapter = dexieAdapter; this.adapterType = "dexie"; debug12("\u2705 Dexie adapter initialized successfully"); return true; } catch (error) { debug12("Dexie adapter initialization failed: %o", error); return false; } } // Proxy all NDKCacheAdapter methods to the active adapter async query(subscription) { if (!this.adapter) { return []; } const result = this.adapter.query(subscription); if (result instanceof Promise) { return await result; } return result; } async setEvent(event, filters, relay) { if (!this.adapter) { return; } return this.adapter.setEvent(event, filters, relay); } async fetchProfile(pubkey) { if (!this.adapter || !this.adapter.fetchProfile) { return null; } return this.adapter.fetchProfile(pubkey); } async saveProfile(pubkey, profile) { if (!this.adapter || !this.adapter.saveProfile) { return; } return this.adapter.saveProfile(pubkey, profile); } async loadNip05(nip05, maxAgeForMissing) { if (!this.adapter || !this.adapter.loadNip05) { return "missing"; } return this.adapter.loadNip05(nip05, maxAgeForMissing); } saveNip05(nip05, profile) { if (!this.adapter || !this.adapter.saveNip05) { return; } this.adapter.saveNip05(nip05, profile); } async getProfiles(filter) { if (!this.adapter || !this.adapter.getProfiles) { return void 0; } return this.adapter.getProfiles(filter); } async deleteEventIds(eventIds) { if (!this.adapter || !this.adapter.deleteEventIds) { return; } return this.adapter.deleteEventIds(eventIds); } }; // ndk/cache-dexie/src/index.ts init_dist(); var import_debug35 = __toESM(require_browser()); var import_nostr_tools22 = __toESM(require_nostr_tools()); // ndk/cache-dexie/src/cache-module.ts var import_debug33 = __toESM(require_browser()); var debug13 = (0, import_debug33.default)("ndk:dexie-adapter:modules"); var DexieModuleCollection2 = class { constructor(db3, tableName) { this.db = db3; this.tableName = tableName; } get table() { return this.db[this.tableName]; } async get(id) { const result = await this.table.get(id); return result || null; } async getMany(ids) { const results = await this.table.where(":id").anyOf(ids).toArray(); return results; } async save(item) { await this.table.put(item); } async saveMany(items) { await this.table.bulkPut(items); } async delete(id) { await this.table.delete(id); } async deleteMany(ids) { await this.table.where(":id").anyOf(ids).delete(); } async findBy(field, value) { return await this.table.where(field).equals(value).toArray(); } async where(conditions) { let collection2 = this.table.toCollection(); for (const [field, value] of Object.entries(conditions)) { collection2 = collection2.and((item) => item[field] === value); } return await collection2.toArray(); } async all() { return await this.table.toArray(); } async count(conditions) { if (!conditions) { return await this.table.count(); } let collection2 = this.table.toCollection(); for (const [field, value] of Object.entries(conditions)) { collection2 = collection2.and((item) => item[field] === value); } return await collection2.count(); } async clear() { await this.table.clear(); } }; var DexieCacheModuleManager2 = class { constructor(dbName) { this.dbName = dbName; __publicField(this, "modules", /* @__PURE__ */ new Map()); __publicField(this, "moduleDb"); __publicField(this, "initialized", false); this.moduleDb = new import_wrapper_default(`${dbName}_modules`); this.setupDatabase(); } setupDatabase() { this.moduleDb.version(1).stores({ moduleMetadata: "&namespace" }); } /** * Register a cache module */ async registerModule(module2) { if (!this.moduleDb.isOpen()) { await this.moduleDb.open(); } const metadataTable = this.moduleDb.table("moduleMetadata"); const existingMetadata = await metadataTable.get(module2.namespace); const currentVersion = existingMetadata?.version || 0; if (currentVersion >= module2.version) { debug13(`Module ${module2.namespace} is already at version ${currentVersion}`); return; } const currentDbVersion = this.moduleDb.verno; const newDbVersion = currentDbVersion + 1; this.moduleDb.close(); const stores = { moduleMetadata: "&namespace" }; for (const [collName, collDef] of Object.entries(module2.collections)) { const tableName = `${module2.namespace}_${collName}`; let indexString = `&${collDef.primaryKey}`; if (collDef.indexes) { indexString += `, ${collDef.indexes.join(", ")}`; } if (collDef.compoundIndexes) { const compounds = collDef.compoundIndexes.map((fields) => `[${fields.join("+")}]`); indexString += `, ${compounds.join(", ")}`; } stores[tableName] = indexString; } this.moduleDb.version(newDbVersion).stores(stores); await this.moduleDb.open(); for (let version = currentVersion + 1; version <= module2.version; version++) { if (module2.migrations[version]) { debug13(`Running migration ${version} for module ${module2.namespace}`); const context = { fromVersion: currentVersion, toVersion: version, async getCollection(name) { return new DexieModuleCollection2(this.moduleDb, `${module2.namespace}_${name}`); }, async createCollection(name, definition) { debug13(`Collection ${name} created during schema update`); }, async deleteCollection(name) { debug13(`Collection deletion requires database recreation`); }, async addIndex(collection2, field) { debug13(`Index addition requires database recreation`); } }; await module2.migrations[version](context); } } await metadataTable.put({ namespace: module2.namespace, version: module2.version, lastMigration: Date.now(), collections: Object.keys(module2.collections) }); this.modules.set(module2.namespace, module2); debug13(`Module ${module2.namespace} registered at version ${module2.version}`); } /** * Get a collection from a module */ async getModuleCollection(namespace, collection2) { if (!this.moduleDb.isOpen()) { await this.moduleDb.open(); } const tableName = `${namespace}_${collection2}`; const table = this.moduleDb[tableName]; if (!table) { const metadata = await this.moduleDb.table("moduleMetadata").get(namespace); if (!metadata) { throw new Error(`Module ${namespace} not registered`); } throw new Error(`Collection ${collection2} not found in module ${namespace}`); } return new DexieModuleCollection2(this.moduleDb, tableName); } /** * Check if a module is registered */ hasModule(namespace) { return this.modules.has(namespace); } /** * Get the current version of a module */ async getModuleVersion(namespace) { if (!this.moduleDb.isOpen()) { await this.moduleDb.open(); } const metadata = await this.moduleDb.table("moduleMetadata").get(namespace); return metadata?.version || 0; } }; // ndk/cache-dexie/src/caches/event-tags.ts async function eventTagsWarmUp2(cacheHandler, eventTags) { const array = await eventTags.limit(cacheHandler.maxSize).toArray(); for (const event of array) { cacheHandler.add(event.tagValue, event.eventId, false); } } var eventTagsDump2 = (eventTags, debug15) => { return async (dirtyKeys, cache) => { const entries = []; for (const tagValue of dirtyKeys) { const eventIds = cache.get(tagValue); if (eventIds) { for (const eventId of eventIds) entries.push({ tagValue, eventId }); } } if (entries.length > 0) { debug15(`Saving ${entries.length} events cache entries to database`); await eventTags.bulkPut(entries); } dirtyKeys.clear(); }; }; // ndk/cache-dexie/src/caches/events.ts async function eventsWarmUp2(cacheHandler, events) { const array = await events.limit(cacheHandler.maxSize).toArray(); for (const event of array) { cacheHandler.set(event.id, event, false); } } var eventsDump2 = (events, debug15) => { return async (dirtyKeys, cache) => { const entries = []; for (const event of dirtyKeys) { const entry = cache.get(event); if (entry) entries.push(entry); } if (entries.length > 0) { debug15(`Saving ${entries.length} events cache entries to database`); await events.bulkPut(entries); } dirtyKeys.clear(); }; }; // ndk/cache-dexie/src/caches/nip05.ts async function nip05WarmUp2(cacheHandler, nip05s) { const array = await nip05s.limit(cacheHandler.maxSize).toArray(); for (const nip05 of array) { cacheHandler.set(nip05.nip05, nip05, false); } } var nip05Dump2 = (nip05s, debug15) => { return async (dirtyKeys, cache) => { const entries = []; for (const nip05 of dirtyKeys) { const entry = cache.get(nip05); if (entry) { entries.push({ nip05, ...entry }); } } if (entries.length) { debug15(`Saving ${entries.length} NIP-05 cache entries to database`); await nip05s.bulkPut(entries); } dirtyKeys.clear(); }; }; // ndk/cache-dexie/src/db.ts var Database2 = class extends import_wrapper_default { constructor(name) { super(name); __publicField(this, "profiles"); __publicField(this, "events"); __publicField(this, "eventTags"); __publicField(this, "nip05"); __publicField(this, "lnurl"); __publicField(this, "relayStatus"); __publicField(this, "unpublishedEvents"); __publicField(this, "eventRelays"); __publicField(this, "decryptedEvents"); this.version(18).stores({ profiles: "&pubkey", events: "&id, kind", eventTags: "&tagValue", nip05: "&nip05", lnurl: "&pubkey", relayStatus: "&url", unpublishedEvents: "&id", eventRelays: "[eventId+relayUrl], eventId", decryptedEvents: "&id" }); } }; var db2; function createDatabase2(name) { db2 = new Database2(name); } // ndk/cache-dexie/src/caches/profiles.ts var import_debug34 = __toESM(require_browser()); var d7 = (0, import_debug34.default)("ndk:dexie-adapter:profiles"); async function profilesWarmUp2(cacheHandler, profiles) { const array = await profiles.limit(cacheHandler.maxSize).toArray(); for (const user of array) { const obj = user; cacheHandler.set(user.pubkey, obj, false); } d7("Loaded %d profiles from database", cacheHandler.size()); } var profilesDump2 = (profiles, debug15) => { return async (dirtyKeys, cache) => { const entries = []; for (const pubkey of dirtyKeys) { const entry = cache.get(pubkey); if (entry) { entries.push(entry); } } if (entries.length) { debug15(`Saving ${entries.length} users to database`); await profiles.bulkPut(entries); } dirtyKeys.clear(); }; }; // ndk/cache-dexie/src/caches/relay-info.ts async function relayInfoWarmUp2(cacheHandler, relayStatus) { const array = await relayStatus.limit(cacheHandler.maxSize).toArray(); for (const entry of array) { cacheHandler.set( entry.url, { url: entry.url, updatedAt: entry.updatedAt, lastConnectedAt: entry.lastConnectedAt, dontConnectBefore: entry.dontConnectBefore }, false ); } } var relayInfoDump2 = (relayStatus, debug15) => { return async (dirtyKeys, cache) => { const entries = []; for (const url of dirtyKeys) { const info = cache.get(url); if (info) { entries.push({ url, updatedAt: info.updatedAt, lastConnectedAt: info.lastConnectedAt, dontConnectBefore: info.dontConnectBefore }); } } if (entries.length > 0) { debug15(`Saving ${entries.length} relay status cache entries to database`); await relayStatus.bulkPut(entries); } dirtyKeys.clear(); }; }; // ndk/cache-dexie/src/caches/unpublished-events.ts init_dist(); var WRITE_STATUS_THRESHOLD2 = 3; async function unpublishedEventsWarmUp2(cacheHandler, unpublishedEvents) { await unpublishedEvents.each((unpublishedEvent) => { cacheHandler.set(unpublishedEvent.event.id, unpublishedEvent, false); }); } function unpublishedEventsDump2(unpublishedEvents, debug15) { return async (dirtyKeys, cache) => { const entries = []; for (const eventId of dirtyKeys) { const entry = cache.get(eventId); if (entry) { entries.push(entry); } } if (entries.length > 0) { debug15(`Saving ${entries.length} unpublished events cache entries to database`); await unpublishedEvents.bulkPut(entries); } dirtyKeys.clear(); }; } async function discardUnpublishedEvent3(unpublishedEvents, eventId) { await unpublishedEvents.delete(eventId); } async function getUnpublishedEvents3(unpublishedEvents) { const events = []; await unpublishedEvents.each((unpublishedEvent) => { events.push({ event: new NDKEvent2(void 0, unpublishedEvent.event), relays: Object.keys(unpublishedEvent.relays), lastTryAt: unpublishedEvent.lastTryAt }); }); return events; } function addUnpublishedEvent3(event, relays) { const r = {}; relays.forEach((url) => r[url] = false); this.unpublishedEvents.set(event.id, { id: event.id, event: event.rawEvent(), relays: r }); this.setEvent(event, [], void 0).catch((e2) => { console.error("[addUnpublishedEvent] Failed to store event in main table:", e2); }); const onPublished = (relay) => { const url = relay.url; const existingEntry = this.unpublishedEvents.get(event.id); if (!existingEntry) { event.off("publushed", onPublished); return; } existingEntry.relays[url] = true; this.unpublishedEvents.set(event.id, existingEntry); const successWrites = Object.values(existingEntry.relays).filter((v6) => v6).length; const unsuccessWrites = Object.values(existingEntry.relays).length - successWrites; if (successWrites >= WRITE_STATUS_THRESHOLD2 || unsuccessWrites === 0) { this.unpublishedEvents.delete(event.id); event.off("published", onPublished); } }; event.on("published", onPublished); } // ndk/cache-dexie/src/caches/zapper.ts async function zapperWarmUp2(cacheHandler, lnurls) { const array = await lnurls.limit(cacheHandler.maxSize).toArray(); for (const lnurl of array) { cacheHandler.set(lnurl.pubkey, { document: lnurl.document, fetchedAt: lnurl.fetchedAt }, false); } } var zapperDump2 = (lnurls, debug15) => { return async (dirtyKeys, cache) => { const entries = []; for (const pubkey of dirtyKeys) { const entry = cache.get(pubkey); if (entry) { entries.push({ pubkey, ...entry }); } } if (entries.length) { debug15(`Saving ${entries.length} zapper cache entries to database`); await lnurls.bulkPut(entries); } dirtyKeys.clear(); }; }; // ndk/cache-dexie/src/lru-cache.ts var import_typescript_lru_cache9 = __toESM(require_dist()); var CacheHandler2 = class { constructor(options) { __publicField(this, "cache"); __publicField(this, "dirtyKeys", /* @__PURE__ */ new Set()); __publicField(this, "options"); __publicField(this, "debug"); __publicField(this, "indexes"); __publicField(this, "isSet", false); __publicField(this, "maxSize", 0); this.debug = options.debug; this.options = options; this.maxSize = options.maxSize; if (options.maxSize > 0) { this.cache = new import_typescript_lru_cache9.LRUCache({ maxSize: options.maxSize }); setInterval(() => this.dump().catch(console.error), 1e3 * 10); } this.indexes = /* @__PURE__ */ new Map(); } getSet(key) { return this.cache?.get(key); } /** * Get all entries that match the filter. */ getAllWithFilter(filter) { const ret = /* @__PURE__ */ new Map(); this.cache?.forEach((val, key) => { if (filter(key, val)) { ret.set(key, val); } }); return ret; } get(key) { return this.cache?.get(key); } async getWithFallback(key, table) { let entry = this.get(key); if (!entry) { entry = await table.get(key); if (entry) { this.set(key, entry); } } return entry; } async getManyWithFallback(keys, table) { const entries = []; const missingKeys = []; for (const key of keys) { const entry = this.get(key); if (entry) entries.push(entry); else missingKeys.push(key); } if (entries.length > 0) { this.debug(`Cache hit for keys ${entries.length} and miss for ${missingKeys.length} keys`); } if (missingKeys.length > 0) { const startTime = Date.now(); const missingEntries = await table.bulkGet(missingKeys); const endTime = Date.now(); let foundKeys = 0; for (const entry of missingEntries) { if (entry) { this.set(entry.id, entry); entries.push(entry); foundKeys++; } } this.debug( `Time spent querying database: ${endTime - startTime}ms for ${missingKeys.length} keys, which added ${foundKeys} entries to the cache` ); } return entries; } add(key, value, dirty = true) { const existing = this.get(key) ?? /* @__PURE__ */ new Set(); existing.add(value); this.cache?.set(key, existing); if (dirty) this.dirtyKeys.add(key); } set(key, value, dirty = true) { this.cache?.set(key, value); if (dirty) this.dirtyKeys.add(key); for (const [attribute, index] of this.indexes.entries()) { const indexKey = value[attribute]; if (indexKey) { const indexValue = index.get(indexKey) || /* @__PURE__ */ new Set(); indexValue.add(key); index.set(indexKey, indexValue); } } } size() { return this.cache?.size || 0; } delete(key) { this.cache?.delete(key); this.dirtyKeys.add(key); } async dump() { if (this.dirtyKeys.size > 0 && this.cache) { await this.options.dump(this.dirtyKeys, this.cache); this.dirtyKeys.clear(); } } addIndex(attribute) { this.indexes.set(attribute, new import_typescript_lru_cache9.LRUCache({ maxSize: this.options.maxSize })); } getFromIndex(index, key) { const ret = /* @__PURE__ */ new Set(); const indexValues = this.indexes.get(index); if (indexValues) { const values = indexValues.get(key); if (values) { for (const key2 of values.values()) { const entry = this.get(key2); if (entry) ret.add(entry); } } } return ret; } }; // ndk/cache-dexie/src/index.ts var INDEXABLE_TAGS_LIMIT2 = 10; var NDKCacheAdapterDexie2 = class { constructor(opts = {}) { __publicField(this, "debug"); __publicField(this, "locking", false); __publicField(this, "ready", false); __publicField(this, "profiles"); __publicField(this, "zappers"); __publicField(this, "nip05s"); __publicField(this, "events"); __publicField(this, "eventTags"); __publicField(this, "relayInfo"); __publicField(this, "unpublishedEvents"); __publicField(this, "warmedUp", false); __publicField(this, "warmUpPromise"); __publicField(this, "devMode", false); __publicField(this, "saveSig"); __publicField(this, "_onReady"); __publicField(this, "moduleManager"); __publicField(this, "addUnpublishedEvent", addUnpublishedEvent3.bind(this)); __publicField(this, "getUnpublishedEvents", () => getUnpublishedEvents3(db2.unpublishedEvents)); __publicField(this, "discardUnpublishedEvent", (id) => discardUnpublishedEvent3(db2.unpublishedEvents, id)); const dbName = opts.dbName || "ndk"; createDatabase2(dbName); this.debug = opts.debug || (0, import_debug35.default)("ndk:dexie-adapter"); this.saveSig = opts.saveSig || false; this.moduleManager = new DexieCacheModuleManager2(dbName); this.profiles = new CacheHandler2({ maxSize: opts.profileCacheSize || 1e5, dump: profilesDump2(db2.profiles, this.debug), debug: this.debug }); this.zappers = new CacheHandler2({ maxSize: opts.zapperCacheSize || 200, dump: zapperDump2(db2.lnurl, this.debug), debug: this.debug }); this.nip05s = new CacheHandler2({ maxSize: opts.nip05CacheSize || 1e3, dump: nip05Dump2(db2.nip05, this.debug), debug: this.debug }); this.events = new CacheHandler2({ maxSize: opts.eventCacheSize || 5e4, dump: eventsDump2(db2.events, this.debug), debug: this.debug }); this.events.addIndex("pubkey"); this.events.addIndex("kind"); this.eventTags = new CacheHandler2({ maxSize: opts.eventTagsCacheSize || 1e5, dump: eventTagsDump2(db2.eventTags, this.debug), debug: this.debug }); this.relayInfo = new CacheHandler2({ maxSize: 500, debug: this.debug, dump: relayInfoDump2(db2.relayStatus, this.debug) }); this.unpublishedEvents = new CacheHandler2({ maxSize: 5e3, debug: this.debug, dump: unpublishedEventsDump2(db2.unpublishedEvents, this.debug) }); const profile = (label, fn) => { const start = Date.now(); return fn().then(() => { const end = Date.now(); this.debug(label, "took", end - start, "ms"); }); }; const startTime = Date.now(); this.warmUpPromise = Promise.allSettled([ profile("profilesWarmUp", () => profilesWarmUp2(this.profiles, db2.profiles)), profile("zapperWarmUp", () => zapperWarmUp2(this.zappers, db2.lnurl)), profile("nip05WarmUp", () => nip05WarmUp2(this.nip05s, db2.nip05)), profile("relayInfoWarmUp", () => relayInfoWarmUp2(this.relayInfo, db2.relayStatus)), profile( "unpublishedEventsWarmUp", () => unpublishedEventsWarmUp2(this.unpublishedEvents, db2.unpublishedEvents) ), profile("eventsWarmUp", () => eventsWarmUp2(this.events, db2.events)), profile("eventTagsWarmUp", () => eventTagsWarmUp2(this.eventTags, db2.eventTags)) ]); this.warmUpPromise.then(() => { const endTime = Date.now(); this.warmedUp = true; this.ready = true; this.locking = true; this.debug("Warm up completed, time", endTime - startTime, "ms"); if (this._onReady) this._onReady(); }); } onReady(callback) { this._onReady = callback; } async query(subscription) { if (!this.warmedUp) { const startTime2 = Date.now(); await this.warmUpPromise; this.debug("froze query for", Date.now() - startTime2, "ms", subscription.filters); } const startTime = Date.now(); subscription.filters.map((filter) => this.processFilter(filter, subscription)); const dur = Date.now() - startTime; if (dur > 100) this.debug("query took", dur, "ms", subscription.filter); return []; } async fetchProfile(pubkey) { if (!this.profiles) return null; const user = await this.profiles.getWithFallback(pubkey, db2.profiles); return user; } fetchProfileSync(pubkey) { if (!this.profiles) return null; const user = this.profiles.get(pubkey); return user; } async getProfiles(filter) { if (!this.profiles) return; const filterFn = typeof filter === "function" ? filter : (pubkey, profile) => { const searchLower = filter.contains.toLowerCase(); const fields = filter.fields || (filter.field ? [filter.field] : ["name", "displayName", "nip05"]); return fields.some((field) => { const value = profile[field]; return typeof value === "string" && value.toLowerCase().includes(searchLower); }); }; return this.profiles.getAllWithFilter(filterFn); } saveProfile(pubkey, profile) { const existingValue = this.profiles.get(pubkey); if (existingValue?.created_at && profile.created_at && existingValue.created_at >= profile.created_at) { return; } const cachedAt = Math.floor(Date.now() / 1e3); this.profiles.set(pubkey, { pubkey, ...profile, cachedAt }); this.debug("Saved profile for pubkey", pubkey, profile); } async loadNip05(nip05, maxAgeForMissing = 3600) { const cache = this.nip05s?.get(nip05); if (cache) { if (cache.profile === null) { if (cache.fetchedAt + maxAgeForMissing * 1e3 < Date.now()) return "missing"; return null; } try { return JSON.parse(cache.profile); } catch (_e2) { return "missing"; } } const nip = await db2.nip05.get({ nip05 }); if (!nip) return "missing"; const now2 = Date.now(); if (nip.profile === null) { if (nip.fetchedAt + maxAgeForMissing * 1e3 < now2) return "missing"; return null; } try { return JSON.parse(nip.profile); } catch (_e2) { return "missing"; } } async saveNip05(nip05, profile) { try { const document2 = profile ? JSON.stringify(profile) : null; this.nip05s.set(nip05, { profile: document2, fetchedAt: Date.now() }); } catch (error) { console.error("Failed to save NIP-05 profile for nip05:", nip05, error); } } async loadUsersLNURLDoc(pubkey, maxAgeInSecs = 86400, maxAgeForMissing = 3600) { const cache = this.zappers?.get(pubkey); if (cache) { if (cache.document === null) { if (cache.fetchedAt + maxAgeForMissing * 1e3 < Date.now()) return "missing"; return null; } try { return JSON.parse(cache.document); } catch (_e2) { return "missing"; } } const lnurl = await db2.lnurl.get({ pubkey }); if (!lnurl) return "missing"; const now2 = Date.now(); if (lnurl.fetchedAt + maxAgeInSecs * 1e3 < now2) return "missing"; if (lnurl.document === null) { if (lnurl.fetchedAt + maxAgeForMissing * 1e3 < now2) return "missing"; return null; } try { return JSON.parse(lnurl.document); } catch (_e2) { return "missing"; } } async saveUsersLNURLDoc(pubkey, doc) { try { const document2 = doc ? JSON.stringify(doc) : null; this.zappers?.set(pubkey, { document: document2, fetchedAt: Date.now() }); } catch (error) { console.error("Failed to save LNURL document for pubkey:", pubkey, error); } } processFilter(filter, subscription) { const _filter = { ...filter }; _filter.limit = void 0; const filterKeys = new Set(Object.keys(_filter || {})); filterKeys.delete("since"); filterKeys.delete("limit"); filterKeys.delete("until"); try { if (this.byNip33Query(filterKeys, filter, subscription)) return; if (this.byAuthors(filter, subscription)) return; if (this.byIdsQuery(filter, subscription)) return; if (this.byTags(filter, subscription)) return; if (this.byKinds(filterKeys, filter, subscription)) return; } catch (error) { console.error(error); } } async deleteEventIds(eventIds) { eventIds.forEach((id) => this.events.delete(id)); await db2.events.where({ id: eventIds }).delete(); } async setEvent(event, _filters, relay) { if (event.kind === 0) { if (!this.profiles) return; try { const profile = profileFromEvent2(event); this.saveProfile(event.pubkey, profile); } catch { this.debug(`Failed to save profile for pubkey: ${event.pubkey}`); } } let addEvent = true; if (event.isParamReplaceable()) { const existingEvent = this.events.get(event.tagId()); if (existingEvent && event.created_at && existingEvent.createdAt > event.created_at) { addEvent = false; } } if (addEvent) { const eventData = { id: event.tagId(), pubkey: event.pubkey, kind: event.kind, createdAt: event.created_at ?? Date.now(), relay: relay?.url, event: event.serialize(this.saveSig, true) }; if (this.saveSig && event.sig) { eventData.sig = event.sig; } this.events.set(event.tagId(), eventData); const indexableTags = getIndexableTags2(event); for (const tag of indexableTags) { this.eventTags.add(tag[0] + tag[1], event.tagId()); } if (relay?.url) { db2.eventRelays.put({ eventId: event.id, relayUrl: relay.url, seenAt: Date.now() }).catch((e2) => { this.debug("Failed to store relay provenance", e2); }); } } } setEventDup(event, relay) { if (relay?.url) { db2.eventRelays.put({ eventId: event.id, relayUrl: relay.url, seenAt: Date.now() }).catch((e2) => { this.debug("Failed to store relay provenance for duplicate event", e2); }); } } updateRelayStatus(url, info) { const existing = this.relayInfo.get(url); const merged = { url, updatedAt: Date.now(), ...existing, ...info, metadata: { ...existing?.metadata, ...info.metadata } }; this.relayInfo.set(url, merged); } getRelayStatus(url) { const a = this.relayInfo.get(url); if (a) { return { lastConnectedAt: a.lastConnectedAt, dontConnectBefore: a.dontConnectBefore, consecutiveFailures: a.consecutiveFailures, lastFailureAt: a.lastFailureAt, nip11: a.nip11, metadata: a.metadata }; } } /** * Searches by authors */ byAuthors(filter, subscription) { if (!filter.authors) return false; let _total = 0; for (const pubkey of filter.authors) { let events = Array.from(this.events.getFromIndex("pubkey", pubkey)); if (filter.kinds) events = events.filter((e2) => filter.kinds?.includes(e2.kind)); foundEvents3(subscription, events, filter); _total += events.length; } return true; } /** * Searches by ids */ byIdsQuery(filter, subscription) { if (filter.ids) { for (const id of filter.ids) { const event = this.events.get(id); if (event) foundEvent3(subscription, event, event.relay, filter); } return true; } return false; } /** * Searches by NIP-33 */ byNip33Query(filterKeys, filter, subscription) { const f = ["#d", "authors", "kinds"]; const hasAllKeys = filterKeys.size === f.length && f.every((k2) => filterKeys.has(k2)); if (hasAllKeys && filter.kinds && filter.authors) { for (const kind of filter.kinds) { const replaceableKind = kind >= 3e4 && kind < 4e4; if (!replaceableKind) continue; for (const author of filter.authors) { for (const dTag of filter["#d"]) { const replaceableId = `${kind}:${author}:${dTag}`; const event = this.events.get(replaceableId); if (event) foundEvent3(subscription, event, event.relay, filter); } } } return true; } return false; } /** * Searches by tags and optionally filters by tags */ byTags(filter, subscription) { const tagFilters = Object.entries(filter).filter(([filter2]) => filter2.startsWith("#") && filter2.length === 2).map(([filter2, values]) => [filter2[1], values]); if (tagFilters.length === 0) return false; for (const [tag, values] of tagFilters) { for (const value of values) { const tagValue = tag + value; const eventIds = this.eventTags.getSet(tagValue); if (!eventIds) continue; eventIds.forEach((id) => { const event = this.events.get(id); if (!event) return; if (!filter.kinds || filter.kinds.includes(event.kind)) { foundEvent3(subscription, event, event.relay, filter); } }); } } return true; } byKinds(filterKeys, filter, subscription) { if (!filter.kinds || filterKeys.size !== 1 || !filterKeys.has("kinds")) return false; const limit2 = filter.limit || 500; let totalEvents = 0; const processedEventIds = /* @__PURE__ */ new Set(); const sortedKinds = [...filter.kinds].sort( (a, b) => (this.events.indexes.get("kind")?.get(a)?.size || 0) - (this.events.indexes.get("kind")?.get(b)?.size || 0) ); for (const kind of sortedKinds) { const events = this.events.getFromIndex("kind", kind); for (const event of events) { if (processedEventIds.has(event.id)) continue; processedEventIds.add(event.id); foundEvent3(subscription, event, event.relay, filter); totalEvents++; if (totalEvents >= limit2) break; } if (totalEvents >= limit2) break; } return true; } /** * Register a cache module with its schema and migrations */ async registerModule(module2) { await this.moduleManager.registerModule(module2); } /** * Get a collection from a registered module */ async getModuleCollection(namespace, collection2) { return await this.moduleManager.getModuleCollection(namespace, collection2); } /** * Get a decrypted event from the cache by its wrapper ID */ async getDecryptedEvent(wrapperId) { try { const decrypted = await db2.decryptedEvents.get(wrapperId); if (decrypted) { const nostrEvent = JSON.parse(decrypted.event); return new NDKEvent2(void 0, nostrEvent); } return null; } catch (e2) { console.error(`[cache-dexie] Error getting decrypted event for wrapper ${wrapperId}:`, e2); return null; } } /** * Add a decrypted event to the cache */ async addDecryptedEvent(wrapperId, decryptedEvent) { try { await db2.decryptedEvents.put({ id: wrapperId, event: JSON.stringify(decryptedEvent.rawEvent()) }); } catch (e2) { console.error(`[cache-dexie] Error adding decrypted event for wrapper ${wrapperId}:`, e2); } } }; function foundEvents3(subscription, events, filter) { if (filter?.limit && events.length > filter.limit) { events = events.sort((a, b) => b.createdAt - a.createdAt).slice(0, filter.limit); } for (const event of events) { foundEvent3(subscription, event, event.relay, filter); } } function foundEvent3(subscription, event, relayUrl, filter) { try { const deserializedEvent = deserialize2(event.event); if (filter && !(0, import_nostr_tools22.matchFilter)(filter, deserializedEvent)) return; const ndkEvent = new NDKEvent2(void 0, deserializedEvent); const relay = relayUrl ? subscription.pool.getRelay(relayUrl, false) : void 0; ndkEvent.relay = relay; subscription.eventReceived(ndkEvent, relay, true); } catch (e2) { console.error("failed to deserialize event", e2); } } function getIndexableTags2(event) { const indexableTags = []; if (event.kind === 3) return []; for (const tag of event.tags) { if (tag[0].length !== 1) continue; indexableTags.push(tag); if (indexableTags.length >= INDEXABLE_TAGS_LIMIT2) return []; } return indexableTags; } // ndk/cache-sqlite-wasm/src/functions/addDecryptedEvent.ts async function addDecryptedEvent2(wrapperId, decryptedEvent) { await this.ensureInitialized(); const serialized = decryptedEvent.serialize(true, true); await this.postWorkerMessage({ type: "addDecryptedEvent", payload: { wrapperId, serialized } }); } // ndk/cache-sqlite-wasm/src/functions/addUnpublishedEvent.ts async function addUnpublishedEvent4(event, relayUrls, lastTryAt = Date.now()) { await this.ensureInitialized(); await this.postWorkerMessage({ type: "addUnpublishedEvent", payload: { id: event.id, event: event.serialize(true, true), relays: JSON.stringify(relayUrls) } }); } // ndk/cache-sqlite-wasm/src/functions/discardUnpublishedEvent.ts async function discardUnpublishedEvent4(eventId) { await this.ensureInitialized(); await this.postWorkerMessage({ type: "discardUnpublishedEvent", payload: { id: eventId } }); } // ndk/cache-sqlite-wasm/src/functions/fetchProfile.ts async function fetchProfile2(pubkey) { await this.ensureInitialized(); const cached = this.metadataCache?.getProfile(pubkey); if (cached) { return cached; } if (this.degradedMode) return null; const result = await this.postWorkerMessage({ type: "fetchProfile", payload: { pubkey } }); if (result && result.profile) { try { const profile = JSON.parse(result.profile); const entry = { ...profile, cachedAt: result.updated_at }; this.metadataCache?.setProfile(pubkey, entry); return entry; } catch { return null; } } return null; } // ndk/cache-sqlite-wasm/src/functions/getCacheStats.ts async function getCacheStats2() { await this.ensureInitialized(); return this.postWorkerMessage({ type: "getCacheStats" }); } // ndk/cache-sqlite-wasm/src/functions/getDecryptedEvent.ts init_dist(); async function getDecryptedEvent2(eventId) { await this.ensureInitialized(); const result = await this.postWorkerMessage({ type: "getDecryptedEvent", payload: { wrapperId: eventId } }); if (result && result.event) { try { const nostrEvent = deserialize2(result.event); return new NDKEvent2(this.ndk, nostrEvent); } catch (e2) { console.error("[getDecryptedEvent] Parse error:", e2); return null; } } return null; } // ndk/cache-sqlite-wasm/src/functions/getEvent.ts async function getEvent2(id) { await this.ensureInitialized(); if (this.degradedMode) return null; const result = await this.postWorkerMessage({ type: "getEvent", payload: { id } }); if (result && result.raw) { try { return JSON.parse(result.raw); } catch { return null; } } return null; } // ndk/cache-sqlite-wasm/src/functions/getProfiles.ts async function getProfiles2(filter) { await this.ensureInitialized(); if (typeof filter === "function") { throw new Error("getProfiles with filter functions is not supported in worker mode. Use filter descriptors instead."); } const result = await this.postWorkerMessage({ type: "getProfiles", payload: filter }); const map = /* @__PURE__ */ new Map(); for (const { pubkey, profile } of result) { map.set(pubkey, profile); } return map; } // ndk/cache-sqlite-wasm/src/functions/getRelayStatus.ts async function getRelayStatus2(relayUrl) { await this.ensureInitialized(); const cached = this.metadataCache?.getRelayInfo(relayUrl); if (cached) { return cached; } const result = await this.postWorkerMessage({ type: "getRelayStatus", payload: { relayUrl } }); if (result && result.info) { try { const info = JSON.parse(result.info); this.metadataCache?.setRelayInfo(relayUrl, info); return info; } catch { return void 0; } } return void 0; } // ndk/cache-sqlite-wasm/src/functions/getUnpublishedEvents.ts async function getUnpublishedEvents4() { await this.ensureInitialized(); const results = await this.postWorkerMessage({ type: "getUnpublishedEvents", payload: {} }); const events = []; if (results) { for (const row of results) { try { const event = JSON.parse(row.event); const relays = row.relays ? JSON.parse(row.relays) : []; events.push({ event, relays, lastTryAt: row.lastTryAt }); } catch { } } } return events; } // ndk/cache-sqlite-wasm/src/functions/loadNip05.ts async function loadNip052(nip05, maxAgeForMissing = 3600) { await this.ensureInitialized(); const cached = this.metadataCache?.getNip05(nip05); if (cached !== void 0) { if (cached.profile === null || cached.profile === void 0) { const now3 = Date.now(); if (cached.fetched_at && cached.fetched_at + maxAgeForMissing * 1e3 < now3) { return "missing"; } return null; } return cached.profile; } const result = await this.postWorkerMessage({ type: "loadNip05", payload: { nip05 } }); if (!result) return "missing"; const now2 = Date.now(); this.metadataCache?.setNip05(nip05, result); if (result.profile === null || result.profile === void 0) { if (result.fetched_at && result.fetched_at + maxAgeForMissing * 1e3 < now2) { return "missing"; } return null; } try { return JSON.parse(result.profile); } catch { return "missing"; } } // ndk/cache-sqlite-wasm/src/functions/query.ts init_dist(); function query2(subscription) { if (this.degradedMode) return []; if (!this.ready) { if (this.initializationPromise) { return this.initializationPromise.then(async () => { if (this.degradedMode) return []; return await queryWorker2.call(this, subscription); }); } return []; } return queryWorker2.call(this, subscription); } async function queryWorker2(subscription) { const cacheFilters = filterForCache2(subscription); const result = await this.postWorkerMessage({ type: "query", payload: { filters: cacheFilters, cacheUnconstrainFilter: subscription.cacheUnconstrainFilter, subId: subscription.subId } }); let eventsData; if (result.type === "json") { eventsData = result.events; } else if (result.type === "binary") { const { decodeEvents: decodeEvents3 } = await Promise.resolve().then(() => (init_decoder2(), decoder_exports)); try { eventsData = decodeEvents3(result.buffer); } catch (error) { console.error("Failed to decode events from cache, cache may be corrupted:", error); return []; } } else { console.error("Unknown result type from worker:", result.type); return []; } const results = /* @__PURE__ */ new Map(); for (const filter of cacheFilters) { const eventsWithRelay = foundEvents4(subscription, eventsData, filter); for (const { event, relayUrl } of eventsWithRelay) { if (event && event.id) { results.set(event.id, event); this.addCachedEventId(event.id); if (relayUrl) { const relay = subscription.pool.getRelay(relayUrl, false); if (relay) { event.relay = relay; if (subscription.ndk) { subscription.ndk.subManager.seenEvent(event.id, relay); } } } } } } return Array.from(results.values()); } function filterForCache2(subscription) { if (!subscription.cacheUnconstrainFilter) return subscription.filters; const filterCopy = subscription.filters.map((filter) => ({ ...filter })); return filterCopy.filter((filter) => { for (const key of subscription.cacheUnconstrainFilter) { delete filter[key]; } return Object.keys(filter).length > 0; }); } function foundEvents4(subscription, records, filter) { const result = []; let now2; for (const record of records) { const eventWithRelay = foundEvent4(subscription, record, record.relay, filter); if (eventWithRelay) { const expiration = eventWithRelay.event.tagValue("expiration"); if (expiration) { now2 ?? (now2 = Math.floor(Date.now() / 1e3)); if (now2 > Number.parseInt(expiration)) continue; } result.push(eventWithRelay); if (filter?.limit && result.length >= filter.limit) break; } } return result; } function foundEvent4(subscription, record, relayUrl, filter) { try { let eventData; if ("raw" in record && record.raw !== void 0 && record.raw !== null) { const rawParsed = JSON.parse(record.raw); if (Array.isArray(rawParsed)) { eventData = { id: rawParsed[0], pubkey: rawParsed[1], created_at: rawParsed[2], kind: rawParsed[3], tags: rawParsed[4], content: rawParsed[5], sig: rawParsed[6] }; } else { eventData = rawParsed; } } else { eventData = { id: record.id, pubkey: record.pubkey, created_at: record.created_at, kind: record.kind, tags: record.tags, content: record.content, sig: record.sig }; } if (filter && !matchFilter2(filter, eventData)) return null; const ndkEvent = new NDKEvent2(void 0, eventData); const relayUrl2 = ("relay_url" in record ? record.relay_url : null) || null; return { event: ndkEvent, relayUrl: relayUrl2 }; } catch (e2) { console.error("failed to deserialize event", e2, "record:", record, "record.raw:", "raw" in record ? record.raw : void 0); return null; } } // ndk/cache-sqlite-wasm/src/functions/saveNip05.ts async function saveNip052(nip05, profile) { const profileStr = profile ? JSON.stringify(profile) : null; const fetchedAt = Date.now(); await this.ensureInitialized(); this.metadataCache?.setNip05(nip05, { profile: profileStr, fetched_at: fetchedAt }); await this.postWorkerMessage({ type: "saveNip05", payload: { nip05, profile: profileStr, fetchedAt } }); } // ndk/cache-sqlite-wasm/src/functions/saveProfile.ts async function saveProfile2(pubkey, profile) { const profileStr = JSON.stringify(profile); const updatedAt = Math.floor(Date.now() / 1e3); await this.ensureInitialized(); const entry = { ...profile, cachedAt: updatedAt }; this.metadataCache?.setProfile(pubkey, entry); if (this.degradedMode) return; await this.postWorkerMessage({ type: "saveProfile", payload: { pubkey, profile: profileStr, updatedAt } }); } // ndk/cache-sqlite-wasm/src/functions/setEvent.ts init_dist(); async function setEvent2(event, _filters, _relay) { await this.ensureInitialized(); if (this.degradedMode) return; await this.batchEvent({ id: event.id, pubkey: event.pubkey, created_at: event.created_at, kind: event.kind, tags: event.tags, content: event.content, sig: event.sig }, _relay?.url); } // ndk/cache-sqlite-wasm/src/functions/updateRelayStatus.ts async function updateRelayStatus2(relayUrl, info) { const existing = await this.getRelayStatus(relayUrl); const merged = { ...existing, ...info, metadata: { ...existing?.metadata, ...info.metadata } }; if (merged.metadata) { for (const [key, value] of Object.entries(merged.metadata)) { if (value === void 0) { delete merged.metadata[key]; } } } await this.ensureInitialized(); this.metadataCache?.setRelayInfo(relayUrl, merged); await this.postWorkerMessage({ type: "updateRelayStatus", payload: { relayUrl, info: JSON.stringify(merged) } }); } // ndk/cache-sqlite-wasm/src/cache/metadata-lru.ts var MetadataLRUCache2 = class { constructor(maxSize = 1e3) { __publicField(this, "profiles"); __publicField(this, "relayInfo"); __publicField(this, "nip05"); __publicField(this, "maxSize"); __publicField(this, "profileAccessOrder"); __publicField(this, "relayAccessOrder"); __publicField(this, "nip05AccessOrder"); this.profiles = /* @__PURE__ */ new Map(); this.relayInfo = /* @__PURE__ */ new Map(); this.nip05 = /* @__PURE__ */ new Map(); this.maxSize = maxSize; this.profileAccessOrder = []; this.relayAccessOrder = []; this.nip05AccessOrder = []; } // Profile operations getProfile(pubkey) { const entry = this.profiles.get(pubkey); if (entry) { const index = this.profileAccessOrder.indexOf(pubkey); if (index > -1) { this.profileAccessOrder.splice(index, 1); } this.profileAccessOrder.push(pubkey); } return entry?.value; } setProfile(pubkey, profile) { this.profiles.set(pubkey, { value: profile, timestamp: Date.now() }); const index = this.profileAccessOrder.indexOf(pubkey); if (index > -1) { this.profileAccessOrder.splice(index, 1); } this.profileAccessOrder.push(pubkey); if (this.profiles.size > this.maxSize) { const oldest = this.profileAccessOrder.shift(); if (oldest) { this.profiles.delete(oldest); } } } deleteProfile(pubkey) { this.profiles.delete(pubkey); const index = this.profileAccessOrder.indexOf(pubkey); if (index > -1) { this.profileAccessOrder.splice(index, 1); } } // Relay info operations getRelayInfo(url) { const entry = this.relayInfo.get(url); if (entry) { const index = this.relayAccessOrder.indexOf(url); if (index > -1) { this.relayAccessOrder.splice(index, 1); } this.relayAccessOrder.push(url); } return entry?.value; } setRelayInfo(url, info) { this.relayInfo.set(url, { value: info, timestamp: Date.now() }); const index = this.relayAccessOrder.indexOf(url); if (index > -1) { this.relayAccessOrder.splice(index, 1); } this.relayAccessOrder.push(url); if (this.relayInfo.size > this.maxSize) { const oldest = this.relayAccessOrder.shift(); if (oldest) { this.relayInfo.delete(oldest); } } } deleteRelayInfo(url) { this.relayInfo.delete(url); const index = this.relayAccessOrder.indexOf(url); if (index > -1) { this.relayAccessOrder.splice(index, 1); } } // NIP-05 operations getNip05(nip05) { const entry = this.nip05.get(nip05); if (entry) { const index = this.nip05AccessOrder.indexOf(nip05); if (index > -1) { this.nip05AccessOrder.splice(index, 1); } this.nip05AccessOrder.push(nip05); } return entry?.value; } setNip05(nip05, result) { this.nip05.set(nip05, { value: result, timestamp: Date.now() }); const index = this.nip05AccessOrder.indexOf(nip05); if (index > -1) { this.nip05AccessOrder.splice(index, 1); } this.nip05AccessOrder.push(nip05); if (this.nip05.size > this.maxSize) { const oldest = this.nip05AccessOrder.shift(); if (oldest) { this.nip05.delete(oldest); } } } deleteNip05(nip05) { this.nip05.delete(nip05); const index = this.nip05AccessOrder.indexOf(nip05); if (index > -1) { this.nip05AccessOrder.splice(index, 1); } } // Clear all clear() { this.profiles.clear(); this.relayInfo.clear(); this.nip05.clear(); this.profileAccessOrder = []; this.relayAccessOrder = []; this.nip05AccessOrder = []; } // Get metrics getMetrics() { return { profileCount: this.profiles.size, relayInfoCount: this.relayInfo.size, nip05Count: this.nip05.size, totalCount: this.profiles.size + this.relayInfo.size + this.nip05.size, maxSize: this.maxSize }; } }; // ndk/cache-sqlite-wasm/src/version.ts var PACKAGE_VERSION2 = "0.8.2"; // ndk/cache-sqlite-wasm/src/index.ts var import_meta2 = {}; var NDKCacheAdapterSqliteWasm2 = class { constructor(options = {}) { __publicField(this, "dbName"); __publicField(this, "wasmUrl"); __publicField(this, "locking", false); __publicField(this, "ndk"); __publicField(this, "ready", false); // Web Worker integration __publicField(this, "worker"); __publicField(this, "workerUrl"); __publicField(this, "pendingRequests", /* @__PURE__ */ new Map()); __publicField(this, "nextRequestId", 0); __publicField(this, "initializationPromise"); // Degraded mode for WASM failures (e.g., iOS Lockdown Mode) __publicField(this, "degradedMode", false); // Performance optimizations __publicField(this, "metadataCache"); // In-memory set of cached event IDs for O(1) duplicate checking __publicField(this, "cachedEventIds", /* @__PURE__ */ new Set()); // Event batching for worker mode __publicField(this, "eventBatch", []); __publicField(this, "batchTimeout", null); __publicField(this, "BATCH_DELAY_MS", 0); // Use microtask (0ms) for immediate batching __publicField(this, "MAX_BATCH_SIZE", 100); // Maximum events per batch // Persistence options __publicField(this, "saveDebounceMs"); __publicField(this, "disableAutosave"); this.dbName = options.dbName || "ndk-cache"; this.wasmUrl = options.wasmUrl; this.workerUrl = options.workerUrl; this.saveDebounceMs = options.saveDebounceMs; this.disableAutosave = options.disableAutosave; this.metadataCache = new MetadataLRUCache2(options.metadataLruSize || 1e3); } /** * Track a cached event ID for duplicate checking */ addCachedEventId(eventId) { this.cachedEventIds.add(eventId); } /** * Check if an event ID is already cached */ hasCachedEvent(eventId) { return this.cachedEventIds.has(eventId); } /** * Initializes the worker. */ async initializeAsync(ndk) { if (this.initializationPromise) { return this.initializationPromise; } this.initializationPromise = (async () => { this.ndk = ndk; try { await this.initializeWorker(); this.ready = true; } catch (error) { this.degradedMode = true; const errorMsg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase(); const isWasmError = errorMsg.includes("wasm") || errorMsg.includes("webassembly") || errorMsg.includes("compile") || errorMsg.includes("instantiate"); if (isWasmError) { console.warn( "[NDK Cache SQLite WASM] Running in degraded mode - WebAssembly unavailable.\nThis is expected in:\n \u2022 iOS/iPadOS Lockdown Mode\n \u2022 Browsers with WASM disabled\n \u2022 Restricted security environments\n\nThe app will continue to function normally, but events will not be cached locally.\nAll data will be fetched directly from Nostr relays." ); } else { console.error( "[NDK Cache SQLite WASM] Initialization failed, running in degraded mode.\nCache will not persist data, but app will continue to function.\nError:", error ); } } })(); return this.initializationPromise; } /** * Initializes the Web Worker, sets up message handlers, and sends the init message. */ async initializeWorker() { let effectiveWorkerUrl = this.workerUrl; if (!effectiveWorkerUrl) { try { effectiveWorkerUrl = new URL("./worker.js", import_meta2.url).toString(); } catch (e2) { console.error("Failed to determine worker URL automatically. Please provide 'workerUrl' option.", e2); throw new Error("Worker URL configuration error."); } } try { this.worker = new Worker(effectiveWorkerUrl, { type: "module" }); } catch (err) { let hasMessage2 = function(e2) { return typeof e2 === "object" && e2 !== null && "message" in e2 && typeof e2.message === "string"; }; var hasMessage = hasMessage2; const msg = hasMessage2(err) ? err.message.toLowerCase() : ""; if (msg.includes("404") || msg.includes("not found") || msg.includes("failed to fetch") || msg.includes("networkerror") || msg.includes("could not load") || msg.includes("cannot find")) { console.error( `[NDK-cache-sqlite-wasm] Failed to load worker file at "${effectiveWorkerUrl}". This usually means the worker asset is missing or not served correctly (e.g., 404 error). Please ensure the worker file exists at the specified URL and is accessible to the browser. Check your bundler configuration and asset paths. See the documentation for details.`, err ); } else { console.error(`[NDK-cache-sqlite-wasm] Error while creating worker at "${effectiveWorkerUrl}":`, err); } throw err; } this.worker.onmessage = (event) => { const data = event.data; if (data.type === "warmupProfiles") { this.handleProfileWarmup(data.profiles); return; } if (data._protocol && data._protocol !== "ndk-cache-sqlite") { console.error( "[NDK Cache SQLite WASM] \u274C Wrong worker protocol!", ` Expected: ndk-cache-sqlite`, ` Received: ${data._protocol}`, "\n\nThis means the wrong worker instance was passed to the cache adapter.", "\nMake sure you are using the correct worker file for the cache." ); return; } if (data._version && data._version !== PACKAGE_VERSION2) { console.warn( "[NDK Cache SQLite WASM] \u26A0\uFE0F Worker version mismatch!", ` Library version: ${PACKAGE_VERSION2}`, ` Worker version: ${data._version}`, "\n\nUpdate your worker file:", "\n cp node_modules/@nostr-dev-kit/cache-sqlite-wasm/dist/worker.js public/" ); } const { id, result, error } = data; const pending = this.pendingRequests.get(id); if (pending) { if (error) { pending.reject(new Error(`Worker error: ${error.message || error}`)); } else { pending.resolve(result); } this.pendingRequests.delete(id); } }; this.worker.onerror = (event) => { const errorMsg = event.message || "unknown error"; console.error( `[NDK-cache-sqlite-wasm] \u274C Worker failed: ${errorMsg} \u{1F527} Common solutions: 1. Copy worker.js and sql-wasm.wasm to your public directory: cp node_modules/@nostr-dev-kit/cache-sqlite-wasm/dist/worker.js public/ cp node_modules/@nostr-dev-kit/cache-sqlite-wasm/dist/sql-wasm.wasm public/ 2. Ensure your bundler serves the public directory correctly 3. Check browser DevTools Network tab for 404 errors on worker.js Current workerUrl: ${effectiveWorkerUrl}` ); this.pendingRequests.forEach( (p5) => p5.reject(new Error(`Worker failed: ${errorMsg}. Check console for setup instructions.`)) ); this.pendingRequests.clear(); }; await this.postWorkerMessage({ type: "init", payload: { dbName: this.dbName, wasmUrl: this.wasmUrl, saveDebounceMs: this.saveDebounceMs, disableAutosave: this.disableAutosave } }); } /** * Helper to send messages to the worker and track responses. */ async postWorkerMessage(message) { if (!this.worker) { return Promise.reject(new Error("Worker not initialized")); } const id = `req-${this.nextRequestId++}`; return new Promise((resolve, reject) => { this.pendingRequests.set(id, { resolve, reject }); const msg = { ...message, id }; this.worker.postMessage(msg); }); } /** * Handles profile warmup data from worker */ handleProfileWarmup(profiles) { for (const { pubkey, profile } of profiles) { this.metadataCache.setProfile(pubkey, profile); } } /** * Flushes the batched events to the worker */ async flushEventBatch() { if (this.eventBatch.length === 0) return; const batch = this.eventBatch; this.eventBatch = []; this.batchTimeout = null; try { await this.postWorkerMessage({ type: "setEventBatch", payload: { events: batch.map((item) => ({ event: item.event, relay: item.relay })) } }); batch.forEach((item) => item.resolve()); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); batch.forEach((item) => item.reject(err)); } } /** * Adds an event to the batch and schedules a flush * Protected so setEvent can access it */ batchEvent(event, relay) { if (event.id && this.cachedEventIds.has(event.id)) { return Promise.resolve(); } return new Promise((resolve, reject) => { if (event.id) { this.cachedEventIds.add(event.id); } this.eventBatch.push({ event, relay, resolve, reject }); if (this.eventBatch.length >= this.MAX_BATCH_SIZE) { if (this.batchTimeout !== null) { clearTimeout(this.batchTimeout); this.batchTimeout = null; } this.flushEventBatch(); } else if (this.batchTimeout === null) { this.batchTimeout = setTimeout(() => { this.flushEventBatch(); }, this.BATCH_DELAY_MS); } }); } // Cache operation methods async setEvent(event, filters, relay) { return setEvent2.call(this, event, filters, relay); } async getEvent(eventId) { return getEvent2.call(this, eventId); } async fetchProfile(pubkey) { return fetchProfile2.call(this, pubkey); } async saveProfile(pubkey, profile) { return saveProfile2.call(this, pubkey, profile); } async updateRelayStatus(relayUrl, status) { return updateRelayStatus2.call(this, relayUrl, status); } async getRelayStatus(relayUrl) { return getRelayStatus2.call(this, relayUrl); } async getDecryptedEvent(eventId) { return getDecryptedEvent2.call(this, eventId); } async addDecryptedEvent(wrapperId, decryptedEvent) { return addDecryptedEvent2.call(this, wrapperId, decryptedEvent); } async addUnpublishedEvent(event, relayUrls, lastTryAt = Date.now()) { return addUnpublishedEvent4.call(this, event, relayUrls, lastTryAt); } async getUnpublishedEvents() { return getUnpublishedEvents4.call(this); } async discardUnpublishedEvent(eventId) { return discardUnpublishedEvent4.call(this, eventId); } query(subscription) { return query2.call(this, subscription); } async getProfiles(filter) { return getProfiles2.call(this, filter); } async getCacheStats() { return getCacheStats2.call(this); } async loadNip05(nip05) { return loadNip052.call(this, nip05); } async saveNip05(nip05, profile) { return saveNip052.call(this, nip05, profile); } /** * Get metadata cache status */ getMetadataCacheStatus() { return this.metadataCache.getMetrics(); } /** * Clear metadata cache */ clearMetadataCache() { this.metadataCache.clear(); } // Generic cache data storage async getCacheData(namespace, key, maxAgeInSecs) { await this.ensureInitialized(); const result = await this.postWorkerMessage({ type: "getCacheData", payload: { namespace, key, maxAgeInSecs } }); return result; } async setCacheData(namespace, key, data) { await this.ensureInitialized(); await this.postWorkerMessage({ type: "setCacheData", payload: { namespace, key, data } }); } async ensureInitialized() { if (this.ready) return; if (this.degradedMode) return; if (this.initializationPromise) { await this.initializationPromise; } } }; var src_default2 = NDKCacheAdapterSqliteWasm2; // build/ndk-entry.js init_cashu_ts_es(); // ndk/sessions/src/utils/errors.ts var SessionError = class extends Error { constructor(message) { super(message); this.name = "SessionError"; } }; var SignerDeserializationError = class extends SessionError { constructor(message) { super(message); this.name = "SignerDeserializationError"; } }; var StorageError = class extends SessionError { constructor(message) { super(message); this.name = "StorageError"; } }; var SessionNotFoundError = class extends SessionError { constructor(pubkey) { super(`Session not found for pubkey: ${pubkey}`); this.name = "SessionNotFoundError"; } }; var NoActiveSessionError = class extends SessionError { constructor() { super("No active session"); this.name = "NoActiveSessionError"; } }; var NDKNotInitializedError = class extends SessionError { constructor() { super("NDK not initialized. Call init() first."); this.name = "NDKNotInitializedError"; } }; // ndk/sessions/src/auth-manager.ts var AuthManager = class { constructor(store, getStore) { this.store = store; this.getStore = getStore; } /** * Login with a signer or user * Orchestrates authentication without handling persistence */ async login(userOrSigner, options = {}) { const { setActive = true, ...startOptions } = options; const pubkey = await this.store.addSession(userOrSigner, setActive); this.store.startSession(pubkey, startOptions); return pubkey; } /** * Logout (remove) a session */ logout(pubkey) { const targetPubkey = pubkey ?? this.getStore().activePubkey; if (!targetPubkey) { throw new NoActiveSessionError(); } this.store.removeSession(targetPubkey); } /** * Switch to a different session */ async switchTo(pubkey) { await this.store.switchToUser(pubkey); } }; // ndk/sessions/src/manager.ts init_dist(); // ndk/sessions/src/persistence-manager.ts init_dist(); // ndk/sessions/src/serialization/signer.ts init_dist(); async function serializeSigner(signer) { try { return signer.toPayload(); } catch (error) { return void 0; } } async function deserializeSigner(payload, ndk) { try { const signer = await ndkSignerFromPayload2(payload, ndk); if (!signer) { throw new Error("NDK returned undefined signer"); } return signer; } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; throw new SignerDeserializationError(`Failed to deserialize signer: ${message}`); } } // ndk/sessions/src/serialization/session.ts async function serializeSession(session, signer) { return { pubkey: session.pubkey, signerPayload: signer ? await serializeSigner(signer) : void 0, lastActive: session.lastActive, preferences: session.preferences }; } // ndk/sessions/src/persistence-manager.ts var PersistenceManager = class { constructor(storage, getStore) { this.storage = storage; this.getStore = getStore; } /** * Restore sessions from storage */ async restore() { if (!this.storage) { throw new StorageError("No storage configured"); } const { sessions: serializedSessions, activePubkey } = await this.storage.load(); for (const [pubkey, serialized] of serializedSessions) { await this.restoreSession(pubkey, serialized); } if (activePubkey && this.getStore().sessions.has(activePubkey)) { this.getStore().switchToUser(activePubkey); } } /** * Persist all sessions to storage */ async persist() { if (!this.storage) { throw new StorageError("No storage configured"); } const serialized = await this.serializeAllSessions(); await this.storage.save(serialized, this.getStore().activePubkey); } /** * Clear all sessions from storage */ async clear() { if (!this.storage) { throw new StorageError("No storage configured"); } await this.storage.clear(); } /** * Restore a single session from serialized data * Only restores identity and signer - data will be fetched from relays/cache */ async restoreSession(pubkey, serialized) { const store = this.getStore(); let signer; if (serialized.signerPayload) { try { signer = await deserializeSigner(serialized.signerPayload, store.ndk); } catch (error) { if (error instanceof SignerDeserializationError) { console.warn(`Failed to restore signer for ${pubkey}: ${error.message}`); } } } const user = signer ? await signer.user() : store.ndk?.getUser({ pubkey }) ?? new NDKUser2({ pubkey }); await store.addSession(signer || user, false); store.updateSession(pubkey, { lastActive: serialized.lastActive, preferences: serialized.preferences }); } /** * Serialize all sessions for storage */ async serializeAllSessions() { const store = this.getStore(); const serialized = /* @__PURE__ */ new Map(); for (const [pubkey, session] of store.sessions) { const signer = store.signers.get(pubkey); const serializedSession = await serializeSession(session, signer); serialized.set(pubkey, serializedSession); } return serialized; } }; // ndk/sessions/src/store.ts init_dist(); init_vanilla(); function normalizeMonitor(monitor) { const kinds = []; const constructorMap = /* @__PURE__ */ new Map(); if (!monitor || monitor.length === 0) { return { kinds, constructorMap }; } for (const item of monitor) { if (typeof item === "number") { kinds.push(item); } else if (item.kinds && Array.isArray(item.kinds)) { for (const kind of item.kinds) { kinds.push(kind); constructorMap.set(kind, item); } } } return { kinds, constructorMap }; } function createSessionStore() { return createStore((set, get) => ({ sessions: /* @__PURE__ */ new Map(), signers: /* @__PURE__ */ new Map(), activePubkey: void 0, init: (ndk) => { set({ ndk }); }, addSession: async (userOrSigner, setActive) => { const state = get(); let user; let signer; if ("user" in userOrSigner && typeof userOrSigner.user === "function") { signer = userOrSigner; user = await signer.user(); } else { user = userOrSigner; } const pubkey = user.pubkey; const existingSession = state.sessions.get(pubkey); const session = existingSession || { pubkey, events: /* @__PURE__ */ new Map(), subscriptions: [], lastActive: setActive ? Math.floor(Date.now() / 1e3) : 0 }; const newSessions = new Map(state.sessions); newSessions.set(pubkey, session); const updates = { sessions: newSessions }; if (signer) { const newSigners = new Map(state.signers); newSigners.set(pubkey, signer); updates.signers = newSigners; } if (setActive) { updates.activePubkey = pubkey; if (state.ndk && signer) { state.ndk.signer = signer; user.ndk = state.ndk; state.ndk.activeUser = user; } } set(updates); return pubkey; }, startSession: (pubkey, opts) => { const state = get(); const { ndk, sessions } = state; if (!ndk) { throw new NDKNotInitializedError(); } const session = sessions.get(pubkey); if (!session) { throw new SessionNotFoundError(pubkey); } for (const sub of session.subscriptions) { sub.stop(); } const { kinds: monitorKinds, constructorMap } = normalizeMonitor(opts.monitor); const kinds = buildSubscriptionKinds(opts, monitorKinds); if (kinds.length === 0) { return; } const subscription = ndk.subscribe( { kinds, authors: [pubkey] }, { closeOnEose: false, subId: "session" }, { onEvent: (event) => handleIncomingEvent(event, pubkey, constructorMap, get) } ); get().updateSession(pubkey, { subscriptions: [subscription] }); }, stopSession: (pubkey) => { const state = get(); const session = state.sessions.get(pubkey); if (session?.subscriptions) { for (const sub of session.subscriptions) { sub.stop(); } get().updateSession(pubkey, { subscriptions: [] }); } }, addMonitor: (monitor) => { const state = get(); const { ndk, activePubkey } = state; if (!ndk) { throw new NDKNotInitializedError(); } if (!activePubkey) { console.warn("No active session to add monitor to"); return; } const session = state.sessions.get(activePubkey); if (!session) { throw new SessionNotFoundError(activePubkey); } const { kinds, constructorMap } = normalizeMonitor(monitor); if (kinds.length === 0) { return; } const subscription = ndk.subscribe( { kinds, authors: [activePubkey] }, { closeOnEose: false }, { onEvent: (event) => handleIncomingEvent(event, activePubkey, constructorMap, get) } ); const updatedSubscriptions = [...session.subscriptions, subscription]; get().updateSession(activePubkey, { subscriptions: updatedSubscriptions }); }, switchToUser: async (pubkey) => { const state = get(); if (pubkey === null) { if (state.ndk) { state.ndk.signer = void 0; state.ndk.activeUser = void 0; state.ndk.muteFilter = void 0; state.ndk.relayConnectionFilter = void 0; } set({ activePubkey: void 0 }); return; } const session = state.sessions.get(pubkey); if (!session) { throw new SessionNotFoundError(pubkey); } const signer = state.signers.get(pubkey); get().updateSession(pubkey, { lastActive: Math.floor(Date.now() / 1e3) }); if (state.ndk) { state.ndk.signer = signer; if (session.muteSet || session.mutedWords) { state.ndk.muteFilter = (event) => { if (session.muteSet?.has(event.pubkey)) return true; if (event.id && session.muteSet?.has(event.id)) return true; if (event.content && session.mutedWords && session.mutedWords.size > 0) { const lowerContent = event.content.toLowerCase(); for (const word of session.mutedWords) { if (lowerContent.includes(word)) return true; } } return false; }; } else { state.ndk.muteFilter = void 0; } if (session.blockedRelays && session.blockedRelays.size > 0) { const blockedRelays = session.blockedRelays; state.ndk.relayConnectionFilter = (relayUrl) => { return !blockedRelays.has(relayUrl); }; } else { state.ndk.relayConnectionFilter = void 0; } } set({ activePubkey: pubkey }); if (state.ndk && signer) { const user = await signer.user(); user.ndk = state.ndk; state.ndk.activeUser = user; } }, removeSession: (pubkey) => { const state = get(); const newSessions = new Map(state.sessions); const newSigners = new Map(state.signers); newSessions.delete(pubkey); newSigners.delete(pubkey); const updates = { sessions: newSessions, signers: newSigners }; if (state.activePubkey === pubkey) { const remainingSessions = Array.from(newSessions.keys()); if (remainingSessions.length === 0) { updates.activePubkey = void 0; if (state.ndk) { state.ndk.signer = void 0; state.ndk.activeUser = void 0; state.ndk.muteFilter = void 0; state.ndk.relayConnectionFilter = void 0; } } } set(updates); get().stopSession(pubkey); if (state.activePubkey === pubkey) { const remainingSessions = Array.from(newSessions.keys()); if (remainingSessions.length > 0) { get().switchToUser(remainingSessions[0]).catch((error) => { console.error("Failed to switch session after removal:", error); }); } } }, updateSession: (pubkey, data) => { const state = get(); const session = state.sessions.get(pubkey); if (!session) { return; } const updatedSession = { ...session, ...data }; const newSessions = new Map(state.sessions); newSessions.set(pubkey, updatedSession); set({ sessions: newSessions }); }, updatePreferences: (pubkey, preferences) => { const state = get(); const session = state.sessions.get(pubkey); if (!session) { return; } const updatedPreferences = { ...session.preferences, ...preferences }; get().updateSession(pubkey, { preferences: updatedPreferences }); } })); } function buildSubscriptionKinds(opts, monitorKinds) { const kinds = []; if (opts.follows) { kinds.push(NDKKind2.Contacts); } if (opts.mutes) { kinds.push(NDKKind2.MuteList); } if (opts.blockedRelays) { kinds.push(NDKKind2.BlockRelayList); } if (opts.relayList) { kinds.push(NDKKind2.RelayList); } if (opts.wallet) { kinds.push(NDKKind2.CashuWallet, NDKKind2.CashuMintList); } kinds.push(...monitorKinds); return kinds; } function handleIncomingEvent(event, pubkey, constructorMap, getState) { const currentSession = getState().sessions.get(pubkey); if (!currentSession) { return; } if (event.kind === NDKKind2.Contacts) { handleContactListEvent(event, pubkey, getState); return; } if (event.kind === NDKKind2.MuteList) { handleMuteListEvent(event, pubkey, getState); return; } if (event.kind === NDKKind2.BlockRelayList) { handleBlockRelayListEvent(event, pubkey, getState); return; } if (event.kind === NDKKind2.RelayList) { handleRelayListEvent(event, pubkey, getState); return; } handleReplaceableEvent(event, currentSession, pubkey, constructorMap, getState); } function handleContactListEvent(event, pubkey, getState) { const session = getState().sessions.get(pubkey); if (!session || event.kind === void 0) return; const existingEvent = session.events.get(event.kind); if (existingEvent) { if (existingEvent.id === event.id) return; if ((existingEvent.created_at ?? 0) > (event.created_at ?? 0)) return; } const followSet = /* @__PURE__ */ new Set(); for (const tag of event.tags) { if (tag[0] === "p" && tag[1] && isValidPubkey2(tag[1])) { followSet.add(tag[1]); } } session.events.set(event.kind, event); getState().updateSession(pubkey, { followSet, events: new Map(session.events) }); } function handleMuteListEvent(event, pubkey, getState) { const session = getState().sessions.get(pubkey); if (!session || event.kind === void 0) return; const existingEvent = session.events.get(event.kind); if (existingEvent) { if (existingEvent.id === event.id) return; if ((existingEvent.created_at ?? 0) > (event.created_at ?? 0)) return; } const muteSet = /* @__PURE__ */ new Map(); const mutedWords = /* @__PURE__ */ new Set(); for (const tag of event.tags) { if ((tag[0] === "p" || tag[0] === "e") && tag[1]) { muteSet.set(tag[1], tag[0]); } if (tag[0] === "word" && tag[1]) { mutedWords.add(tag[1].toLowerCase()); } } session.events.set(event.kind, event); getState().updateSession(pubkey, { muteSet, mutedWords, events: new Map(session.events) }); } function handleBlockRelayListEvent(event, pubkey, getState) { const session = getState().sessions.get(pubkey); if (!session || event.kind === void 0) return; const existingEvent = session.events.get(event.kind); if (existingEvent) { if (existingEvent.id === event.id) return; if ((existingEvent.created_at ?? 0) > (event.created_at ?? 0)) return; } const blockedRelays = /* @__PURE__ */ new Set(); for (const tag of event.tags) { if (tag[0] === "relay" && tag[1]) { blockedRelays.add(tag[1]); } } session.events.set(event.kind, event); getState().updateSession(pubkey, { blockedRelays, events: new Map(session.events) }); } function handleRelayListEvent(event, pubkey, getState) { const session = getState().sessions.get(pubkey); if (!session || event.kind === void 0) return; const existingEvent = session.events.get(event.kind); if (existingEvent) { if (existingEvent.id === event.id) return; if ((existingEvent.created_at ?? 0) > (event.created_at ?? 0)) return; } const relayListEvent = NDKRelayList2.from(event); const relayList = /* @__PURE__ */ new Map(); for (const url of relayListEvent.readRelayUrls) { relayList.set(url, { read: true, write: false }); } for (const url of relayListEvent.writeRelayUrls) { const existing = relayList.get(url); if (existing) { existing.write = true; } else { relayList.set(url, { read: false, write: true }); } } for (const url of relayListEvent.bothRelayUrls) { relayList.set(url, { read: true, write: true }); } session.events.set(event.kind, event); getState().updateSession(pubkey, { relayList, events: new Map(session.events) }); } function handleReplaceableEvent(event, session, pubkey, constructorMap, getState) { if (event.kind === void 0) return; const existingEvent = session.events.get(event.kind); if (existingEvent) { if (existingEvent.id === event.id) { return; } if ((existingEvent.created_at ?? 0) > (event.created_at ?? 0)) { return; } } const eventConstructor = constructorMap.get(event.kind); const wrappedEvent = eventConstructor && typeof eventConstructor.from === "function" ? eventConstructor.from(event) : event; session.events.set(event.kind, wrappedEvent); getState().updateSession(pubkey, { events: new Map(session.events) }); } // ndk/sessions/src/utils/debounce.ts function debounce(func, wait) { let timeout = null; return (...args) => { if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { func(...args); }, wait); }; } // ndk/sessions/src/utils/json-serializer.ts function serializeSessionData(data) { const serializable = { sessions: Array.from(data.sessions.entries()), activePubkey: data.activePubkey, version: 1, updatedAt: Date.now() }; return JSON.stringify(serializable, null, 2); } function deserializeSessionData(raw) { const data = JSON.parse(raw); return { sessions: new Map(data.sessions || []), activePubkey: data.activePubkey }; } // ndk/sessions/src/manager.ts var NDKSessionManager = class { constructor(ndk, options = {}) { this.store = createSessionStore(); this.options = { autoSave: options.autoSave ?? true, saveDebounceMs: options.saveDebounceMs ?? 500, ...options }; this.store.getState().init(ndk); this.authManager = new AuthManager(this.store.getState(), () => this.store.getState()); this.persistenceManager = new PersistenceManager(this.options.storage, () => this.store.getState()); if (this.options.autoSave && this.options.storage) { this.setupAutoSave(); } } /** * Get the current store state */ getCurrentState() { return this.store.getState(); } /** * Get all sessions */ getSessions() { return this.getCurrentState().sessions; } /** * Get a specific session */ getSession(pubkey) { return this.getCurrentState().sessions.get(pubkey); } /** * Get the active session */ get activeSession() { const { activePubkey, sessions } = this.getCurrentState(); return activePubkey ? sessions.get(activePubkey) : void 0; } /** * Get the active user */ get activeUser() { const session = this.activeSession; const state = this.getCurrentState(); if (!session || !state.ndk) return void 0; return state.ndk.getUser({ pubkey: session.pubkey }); } /** * Get the active pubkey */ get activePubkey() { return this.getCurrentState().activePubkey; } /** * Check if a session is read-only (no signer available) */ isReadOnly(pubkey) { const targetPubkey = pubkey ?? this.getCurrentState().activePubkey; if (!targetPubkey) return true; return !this.getCurrentState().signers.has(targetPubkey); } /** * Login with a signer or user * * @example * ```typescript * const signer = new NDKPrivateKeySigner(nsec); * await sessions.login(signer); * ``` */ async login(userOrSigner, options = {}) { const loginOptions = { ...this.options.fetches, setActive: options.setActive }; return this.authManager.login(userOrSigner, loginOptions); } /** * Create a new account with optional profile, relays, wallet, and follows * * @param data - Account data to create * @param data.profile - Optional profile metadata (kind:0) * @param data.relays - Optional relay list for NIP-65 (kind:10002) * @param data.wallet - Optional wallet configuration for NIP-60 * @param data.wallet.mints - Mint URLs for the wallet * @param data.wallet.relays - Optional relays for wallet events * @param data.follows - Optional list of pubkeys to follow (kind:3) * @param opts - Behavior options * @param opts.publish - Whether to publish events immediately (default: true) * @param opts.signer - Optional signer to use instead of generating a new one * @returns The signer and signed events array * * @example * ```typescript * // Publish immediately with generated signer * const { signer, events } = await sessions.createAccount({ * profile: { name: 'Alice', about: 'Hello Nostr!' }, * relays: ['wss://relay.damus.io', 'wss://relay.primal.net'], * follows: ['pubkey1...', 'pubkey2...'] * }); * * // Use existing signer * const mySigner = NDKPrivateKeySigner.generate(); * const { signer, events } = await sessions.createAccount({ * profile: { name: 'Alice', about: 'Hello Nostr!' } * }, { signer: mySigner }); * * // Get signed events without publishing * const { signer, events } = await sessions.createAccount({ * profile: { name: 'Alice', about: 'Hello Nostr!' }, * relays: ['wss://relay.damus.io', 'wss://relay.primal.net'] * }, { publish: false }); * // events array will contain signed events when publish is false * ``` */ async createAccount(data, opts) { const state = this.getCurrentState(); const ndk = state.ndk; if (!ndk) throw new Error("NDK not initialized"); const signer = opts?.signer ?? NDKPrivateKeySigner2.generate(); if (!opts?.signer) { await this.login(signer, { setActive: true }); } const publish = opts?.publish !== false; const events = []; if (data?.profile) { const profileEvent = new NDKEvent2(ndk, { kind: NDKKind2.Metadata, content: JSON.stringify(data.profile) }); await profileEvent.sign(signer); if (publish) { await profileEvent.publish(); } else { events.push(profileEvent); } } if (data?.relays && data.relays.length > 0) { const relayList = new NDKRelayList2(ndk); relayList.bothRelayUrls = data.relays; await relayList.sign(signer); if (publish) { await relayList.publish(); } else { events.push(relayList); } } if (data?.wallet) { const { NDKCashuWallet: NDKCashuWallet3 } = await Promise.resolve().then(() => (init_dist3(), dist_exports)); await NDKCashuWallet3.create(ndk, data.wallet.mints, data.wallet.relays); } if (data?.follows && data.follows.length > 0) { const contactList = new NDKEvent2(ndk, { kind: NDKKind2.Contacts, tags: data.follows.map((pubkey) => ["p", pubkey]), content: "" }); await contactList.sign(signer); if (publish) { await contactList.publish(); } else { events.push(contactList); } } return { signer, events }; } /** * Logout (remove) a session * * @param pubkey - Session to logout. If not provided, logs out active session. */ logout(pubkey) { this.authManager.logout(pubkey); } /** * Switch to a different session */ async switchTo(pubkey) { await this.authManager.switchTo(pubkey); } /** * Start fetching data for a session */ startSession(pubkey, options) { this.getCurrentState().startSession(pubkey, options); } /** * Stop fetching data for a session */ stopSession(pubkey) { this.getCurrentState().stopSession(pubkey); } /** * Add monitors to the active session * * @example * ```typescript * // Add monitors to active session * sessions.addMonitor([NDKInterestList, 10050, 10051]); * ``` */ addMonitor(monitor) { this.getCurrentState().addMonitor(monitor); } /** * Enable wallet fetching for a session * * @param pubkey - Session to enable wallet for. If not provided, uses active session. * * @example * ```typescript * // User wants to use wallet features * sessions.enableWallet(userPubkey); * ``` */ enableWallet(pubkey) { const targetPubkey = pubkey ?? this.getCurrentState().activePubkey; if (!targetPubkey) return; const state = this.getCurrentState(); const session = state.sessions.get(targetPubkey); if (!session) return; state.updatePreferences(targetPubkey, { walletEnabled: true }); const currentSubscriptions = session.subscriptions; if (currentSubscriptions && currentSubscriptions.length > 0) { state.stopSession(targetPubkey); state.startSession(targetPubkey, { ...this.options.fetches, wallet: true }); } else { } } /** * Disable wallet fetching for a session * * @param pubkey - Session to disable wallet for. If not provided, uses active session. * * @example * ```typescript * // User wants to disable wallet features * sessions.disableWallet(userPubkey); * ``` */ disableWallet(pubkey) { const targetPubkey = pubkey ?? this.getCurrentState().activePubkey; if (!targetPubkey) return; const state = this.getCurrentState(); const session = state.sessions.get(targetPubkey); if (!session) return; state.updatePreferences(targetPubkey, { walletEnabled: false }); const currentSubscriptions = session.subscriptions; if (currentSubscriptions && currentSubscriptions.length > 0) { state.stopSession(targetPubkey); state.startSession(targetPubkey, { ...this.options.fetches, wallet: false }); } } /** * Check if wallet fetching is enabled for a session * * @param pubkey - Session to check. If not provided, uses active session. * @returns true if wallet fetching is enabled * * @example * ```typescript * if (!sessions.isWalletEnabled()) { * // Show UI prompt to enable wallet * } * ``` */ isWalletEnabled(pubkey) { const targetPubkey = pubkey ?? this.getCurrentState().activePubkey; if (!targetPubkey) return false; const session = this.getCurrentState().sessions.get(targetPubkey); return session?.preferences?.walletEnabled ?? false; } /** * Subscribe to state changes * * @example * ```typescript * const unsubscribe = sessions.subscribe((state) => { * console.log('Active pubkey:', state.activePubkey); * }); * ``` */ subscribe(callback) { return this.store.subscribe(callback); } /** * Restore sessions from storage */ async restore() { await this.persistenceManager.restore(); if (this.options.fetches) { const state = this.getCurrentState(); for (const pubkey of state.sessions.keys()) { const session = state.sessions.get(pubkey); if (!session) continue; const walletEnabled = session.preferences?.walletEnabled ?? this.options.fetches.wallet ?? false; const fetches = { ...this.options.fetches, wallet: walletEnabled }; state.startSession(pubkey, fetches); } } } /** * Persist sessions to storage */ async persist() { return this.persistenceManager.persist(); } /** * Clear all sessions from storage */ async clear() { return this.persistenceManager.clear(); } /** * Cleanup and stop all subscriptions */ destroy() { const state = this.getCurrentState(); for (const pubkey of state.sessions.keys()) { state.stopSession(pubkey); } if (this.unsubscribe) { this.unsubscribe(); } } setupAutoSave() { const debouncedPersist = debounce(() => { this.persistenceManager.persist().catch((error) => { console.error("Failed to auto-save sessions:", error); }); }, this.options.saveDebounceMs); this.unsubscribe = this.store.subscribe(() => { debouncedPersist(); }); } }; // ndk/sessions/src/storage/file-storage.ts var FileStorage = class { constructor(filePath = "./.ndk-sessions.json") { this.filePath = filePath; } async save(sessions, activePubkey) { try { const data = { sessions, activePubkey }; const serialized = serializeSessionData(data); const { writeFile } = await import("fs/promises"); await writeFile(this.filePath, serialized); } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; throw new StorageError(`Failed to save sessions to file: ${message}`); } } async load() { try { const { readFile } = await import("fs/promises"); const raw = await readFile(this.filePath, "utf-8"); return deserializeSessionData(raw); } catch (error) { if (error?.code === "ENOENT") { return { sessions: /* @__PURE__ */ new Map() }; } const message = error instanceof Error ? error.message : "Unknown error"; throw new StorageError(`Failed to load sessions from file: ${message}`); } } async clear() { try { const { unlink } = await import("fs/promises"); await unlink(this.filePath); } catch (error) { if (error?.code !== "ENOENT") { const message = error instanceof Error ? error.message : "Unknown error"; throw new StorageError(`Failed to clear sessions file: ${message}`); } } } }; // ndk/sessions/src/storage/local-storage.ts var LocalStorage = class { constructor(key = "ndk-sessions") { this.key = key; } async save(sessions, activePubkey) { if (typeof window === "undefined" || !window.localStorage) { throw new StorageError("localStorage is not available"); } try { const data = { sessions, activePubkey }; const serialized = serializeSessionData(data); localStorage.setItem(this.key, serialized); } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; throw new StorageError(`Failed to save sessions to localStorage: ${message}`); } } async load() { if (typeof window === "undefined" || !window.localStorage) { return { sessions: /* @__PURE__ */ new Map() }; } const raw = localStorage.getItem(this.key); if (!raw) { return { sessions: /* @__PURE__ */ new Map() }; } try { return deserializeSessionData(raw); } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; throw new StorageError(`Failed to parse sessions from localStorage: ${message}`); } } async clear() { if (typeof window === "undefined" || !window.localStorage) { throw new StorageError("localStorage is not available"); } try { localStorage.removeItem(this.key); } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; throw new StorageError(`Failed to clear sessions from localStorage: ${message}`); } } }; // ndk/sessions/src/storage/memory-storage.ts var MemoryStorage = class { constructor() { this.data = { sessions: /* @__PURE__ */ new Map() }; } async save(sessions, activePubkey) { this.data = { sessions: new Map(sessions), activePubkey }; } async load() { return { sessions: new Map(this.data.sessions), activePubkey: this.data.activePubkey }; } async clear() { this.data = { sessions: /* @__PURE__ */ new Map() }; } }; // ndk/messages/src/cache-module.ts var messagesCacheModule = { namespace: "messages", version: 1, collections: { messages: { primaryKey: "id", indexes: ["conversationId", "timestamp", "sender", "recipient"], compoundIndexes: [ ["conversationId", "timestamp"], // For fetching conversation messages in order ["recipient", "read"] // For fetching unread messages for a user ], schema: { id: "string", content: "string", sender: "string", // pubkey recipient: "string?", // pubkey, optional for group messages timestamp: "number", protocol: "string", read: "boolean", rumor: "object?", conversationId: "string" } }, conversations: { primaryKey: "id", indexes: ["lastMessageAt"], compoundIndexes: [ ["participants"] // For finding conversations by participant ], schema: { id: "string", participants: "string[]", // array of pubkeys name: "string?", avatar: "string?", lastMessageAt: "number?", unreadCount: "number", protocol: "string", metadata: "object?" } }, // For future MLS support mlsGroups: { primaryKey: "id", indexes: ["createdAt"], schema: { id: "string", groupId: "string", epoch: "number", members: "string[]", createdAt: "number", updatedAt: "number", treeHash: "string", confirmedTranscriptHash: "string", interimTranscriptHash: "string", groupContext: "string" // serialized binary } }, // Track which relays are used for DMs dmRelays: { primaryKey: "pubkey", indexes: ["updatedAt"], schema: { pubkey: "string", relays: "string[]", updatedAt: "number" } } }, migrations: { 1: async (context) => { await context.createCollection("messages", messagesCacheModule.collections.messages); await context.createCollection("conversations", messagesCacheModule.collections.conversations); await context.createCollection("mlsGroups", messagesCacheModule.collections.mlsGroups); await context.createCollection("dmRelays", messagesCacheModule.collections.dmRelays); } // Future migrations would go here // 2: async (context) => { // // Add new field or index // await context.addIndex('messages', 'newField'); // } } }; // ndk/messages/src/conversation.ts init_dist(); // ndk/node_modules/eventemitter3/index.mjs var import_index22 = __toESM(require_eventemitter3(), 1); // ndk/messages/src/conversation.ts var NDKConversation = class extends import_index22.default { constructor(id, participants, protocol, storage, myPubkey, nip17) { super(); __publicField(this, "id"); __publicField(this, "participants"); __publicField(this, "protocol"); __publicField(this, "messages", []); __publicField(this, "storage"); __publicField(this, "nip17"); __publicField(this, "myPubkey"); this.id = id; this.participants = participants; this.protocol = protocol; this.storage = storage; this.myPubkey = myPubkey; this.nip17 = nip17; } /** * Send a message in this conversation */ async sendMessage(content) { if (this.protocol === "nip17" && this.nip17) { const recipient = this.participants.find((p5) => p5.pubkey !== this.myPubkey); if (!recipient) { throw new Error("No recipient found in conversation"); } try { const wrappedEvent = await this.nip17.sendMessage(recipient, content); const message = { id: wrappedEvent.id || "", content, sender: new NDKUser2({ pubkey: this.myPubkey }), recipient, timestamp: Math.floor(Date.now() / 1e3), protocol: "nip17", read: true, // Our own messages are always "read" conversationId: this.id }; await this.storage.saveMessage(message); this.messages.push(message); this.emit("message", message); return message; } catch (error) { const errorEvent = { type: "send-failed", message: `Failed to send message: ${error}`, error }; this.emit("error", errorEvent); throw error; } } else { throw new Error(`Protocol ${this.protocol} not supported yet`); } } /** * Get messages in this conversation */ async getMessages(limit2) { if (this.messages.length === 0) { this.messages = await this.storage.getMessages(this.id, limit2); } if (limit2 && this.messages.length > limit2) { return this.messages.slice(-limit2); } return [...this.messages]; } /** * Mark all messages as read */ async markAsRead() { const unreadMessages = this.messages.filter((m) => !m.read); if (unreadMessages.length > 0) { const messageIds = unreadMessages.map((m) => m.id); await this.storage.markAsRead(messageIds); unreadMessages.forEach((m) => m.read = true); } } /** * Get unread count */ getUnreadCount() { return this.messages.filter((m) => !m.read && m.sender.pubkey !== this.myPubkey).length; } /** * Get the other participant in a two-person conversation */ getOtherParticipant() { return this.participants.find((p5) => p5.pubkey !== this.myPubkey); } /** * Get the last message in the conversation */ getLastMessage() { return this.messages[this.messages.length - 1]; } /** * Handle an incoming message (called by NDKMessenger) */ async _handleIncomingMessage(message) { const exists4 = this.messages.find((m) => m.id === message.id); if (!exists4) { this.messages.push(message); this.messages.sort((a, b) => a.timestamp - b.timestamp); await this.storage.saveMessage(message); this.emit("message", message); } } /** * Handle a state change (for future MLS support) */ _handleStateChange(event) { this.emit("state-change", event); } /** * Handle an error */ _handleError(error) { this.emit("error", error); } /** * Clean up resources */ destroy() { this.removeAllListeners(); this.messages = []; } }; // ndk/messages/src/messenger.ts init_dist(); // ndk/messages/src/protocols/nip17.ts init_dist(); var import_pure = __toESM(require_pure()); var NIP17Protocol = class { constructor(ndk, signer) { this.ndk = ndk; this.signer = signer; } /** * Send a NIP-17 direct message */ async sendMessage(recipient, content) { const sender = await this.signer.user(); const rumor = new NDKEvent2(this.ndk); rumor.kind = NDKKind2.PrivateDirectMessage; rumor.content = content; rumor.created_at = Math.floor(Date.now() / 1e3); rumor.pubkey = sender.pubkey; rumor.tags = [["p", recipient.pubkey]]; const wrappedEvent = await giftWrap2(rumor, recipient, this.signer); const recipientRelays = await this.getRecipientDMRelays(recipient); const senderRelays = await this.getUserDMRelays(sender); const allRelays = [.../* @__PURE__ */ new Set([...recipientRelays, ...senderRelays])]; if (allRelays.length > 0) { const relaySet = NDKRelaySet2.fromRelayUrls(allRelays, this.ndk); await wrappedEvent.publish(relaySet); } else { await wrappedEvent.publish(); } return wrappedEvent; } /** * Unwrap a received gift-wrapped message */ async unwrapMessage(wrappedEvent) { try { const rumor = await giftUnwrap2(wrappedEvent, void 0, this.signer); if (rumor.kind !== NDKKind2.PrivateDirectMessage) { return null; } return rumor.rawEvent(); } catch (error) { console.error("Failed to unwrap message:", error); return null; } } /** * Convert a rumor event to NDKMessage format */ rumorToMessage(rumor, myPubkey) { if (!rumor.id) { rumor.id = (0, import_pure.getEventHash)({ ...rumor, kind: rumor.kind ?? 0, created_at: rumor.created_at ?? 0, tags: rumor.tags ?? [], content: rumor.content ?? "", pubkey: rumor.pubkey ?? "" }); } const isOutgoing = rumor.pubkey === myPubkey; const otherPubkey = isOutgoing ? rumor.tags.find((t) => t[0] === "p")?.[1] || "" : rumor.pubkey; const conversationId = [myPubkey, otherPubkey].sort().join(":"); return { id: rumor.id || "", content: rumor.content || "", sender: new NDKUser2({ pubkey: rumor.pubkey }), recipient: isOutgoing ? new NDKUser2({ pubkey: otherPubkey }) : new NDKUser2({ pubkey: myPubkey }), timestamp: rumor.created_at || Math.floor(Date.now() / 1e3), protocol: "nip17", read: isOutgoing, // Outgoing messages are automatically "read" rumor, conversationId }; } /** * Get DM relays for a recipient (kind 10050) */ async getRecipientDMRelays(recipient) { try { const dmRelayList = await this.ndk.fetchEvent({ kinds: [NDKKind2.DirectMessageReceiveRelayList], authors: [recipient.pubkey] }); if (dmRelayList) { const relays = dmRelayList.getMatchingTags("relay").map((t) => t[1]); if (relays.length > 0) { return relays; } } const relayList = await this.ndk.fetchEvent({ kinds: [10002], authors: [recipient.pubkey] }); if (relayList) { const relays = relayList.getMatchingTags("r").map((t) => t[1]); if (relays.length > 0) { return relays.slice(0, 3); } } return []; } catch (error) { console.error("Failed to fetch recipient relays:", error); return []; } } /** * Get DM relays for the current user (kind 10050) */ async getUserDMRelays(user) { try { const dmRelayList = await this.ndk.fetchEvent({ kinds: [NDKKind2.DirectMessageReceiveRelayList], authors: [user.pubkey] }); if (dmRelayList) { const relays = dmRelayList.getMatchingTags("relay").map((t) => t[1]); if (relays.length > 0) { return relays; } } const relayList = await this.ndk.fetchEvent({ kinds: [10002], authors: [user.pubkey] }); if (relayList) { const relays = relayList.getMatchingTags("r").map((t) => t[1]); if (relays.length > 0) { return relays.slice(0, 3); } } return []; } catch (error) { console.error("Failed to fetch user relays:", error); return []; } } /** * Publish the user's DM relay list (kind 10050) */ async publishDMRelayList(relays) { const event = new NDKEvent2(this.ndk); event.kind = NDKKind2.DirectMessageReceiveRelayList; event.tags = relays.map((relay) => ["relay", relay]); event.created_at = Math.floor(Date.now() / 1e3); await event.sign(this.signer); await event.publish(); return event; } }; // ndk/messages/src/storage/cache-module.ts init_dist(); var CacheModuleStorage = class { constructor(cache, myPubkey) { this.cache = cache; this.myPubkey = myPubkey; __publicField(this, "messagesCollection"); __publicField(this, "conversationsCollection"); __publicField(this, "initialized", false); } /** * Initialize the storage by registering the module and getting collections */ async ensureInitialized() { if (this.initialized) return; if (this.cache.registerModule) { await this.cache.registerModule(messagesCacheModule); } if (this.cache.getModuleCollection) { this.messagesCollection = await this.cache.getModuleCollection("messages", "messages"); this.conversationsCollection = await this.cache.getModuleCollection( "messages", "conversations" ); } this.initialized = true; } async saveMessage(message) { await this.ensureInitialized(); if (!this.messagesCollection) return; const cachedMessage = { id: message.id, content: message.content, sender: message.sender.pubkey, recipient: message.recipient?.pubkey, timestamp: message.timestamp, protocol: message.protocol, read: message.read, rumor: message.rumor, conversationId: message.conversationId }; await this.messagesCollection.save(cachedMessage); await this.updateConversationForMessage(message); } async updateConversationForMessage(message) { if (!this.conversationsCollection) return; const conversation = await this.conversationsCollection.get(message.conversationId); if (conversation) { conversation.lastMessageAt = message.timestamp; if (!message.read && message.sender.pubkey !== this.myPubkey) { conversation.unreadCount++; } await this.conversationsCollection.save(conversation); } else { const participants = [message.sender.pubkey]; if (message.recipient) { participants.push(message.recipient.pubkey); } const newConversation = { id: message.conversationId, participants: [...new Set(participants)], // Deduplicate lastMessageAt: message.timestamp, unreadCount: !message.read && message.sender.pubkey !== this.myPubkey ? 1 : 0, protocol: message.protocol }; await this.conversationsCollection.save(newConversation); } } async getMessages(conversationId, limit2) { await this.ensureInitialized(); if (!this.messagesCollection) return []; const cachedMessages = await this.messagesCollection.findBy("conversationId", conversationId); cachedMessages.sort((a, b) => a.timestamp - b.timestamp); let messages = cachedMessages; if (limit2 && messages.length > limit2) { messages = messages.slice(-limit2); } return messages.map((cached) => ({ id: cached.id, content: cached.content, sender: new NDKUser2({ pubkey: cached.sender }), recipient: cached.recipient ? new NDKUser2({ pubkey: cached.recipient }) : void 0, timestamp: cached.timestamp, protocol: cached.protocol, read: cached.read, rumor: cached.rumor, conversationId: cached.conversationId })); } async markAsRead(messageIds) { await this.ensureInitialized(); if (!this.messagesCollection || !this.conversationsCollection) return; for (const id of messageIds) { const message = await this.messagesCollection.get(id); if (message && !message.read) { message.read = true; await this.messagesCollection.save(message); const conversation = await this.conversationsCollection.get(message.conversationId); if (conversation && conversation.unreadCount > 0) { conversation.unreadCount--; await this.conversationsCollection.save(conversation); } } } } async getConversations(userId) { await this.ensureInitialized(); if (!this.conversationsCollection) return []; const allConversations = await this.conversationsCollection.all(); const userConversations = allConversations.filter((conv) => conv.participants.includes(userId)); userConversations.sort((a, b) => (b.lastMessageAt || 0) - (a.lastMessageAt || 0)); return userConversations.map((conv) => ({ id: conv.id, participants: conv.participants, protocol: conv.protocol, name: conv.name, lastMessageAt: conv.lastMessageAt, unreadCount: conv.unreadCount })); } async saveConversation(conversation) { await this.ensureInitialized(); if (!this.conversationsCollection) return; const cachedConversation = { id: conversation.id, participants: conversation.participants, name: conversation.name, lastMessageAt: conversation.lastMessageAt, unreadCount: conversation.unreadCount, protocol: conversation.protocol }; await this.conversationsCollection.save(cachedConversation); } async deleteMessage(messageId) { await this.ensureInitialized(); if (!this.messagesCollection) return; const message = await this.messagesCollection.get(messageId); if (message) { if (!message.read && this.conversationsCollection) { const conversation = await this.conversationsCollection.get(message.conversationId); if (conversation && conversation.unreadCount > 0) { conversation.unreadCount--; await this.conversationsCollection.save(conversation); } } await this.messagesCollection.delete(messageId); } } async clear() { await this.ensureInitialized(); if (this.messagesCollection) { await this.messagesCollection.clear(); } if (this.conversationsCollection) { await this.conversationsCollection.clear(); } } }; // ndk/messages/src/storage/memory.ts var MemoryAdapter = class { constructor() { __publicField(this, "messages", /* @__PURE__ */ new Map()); __publicField(this, "conversations", /* @__PURE__ */ new Map()); __publicField(this, "messagesByConversation", /* @__PURE__ */ new Map()); } async saveMessage(message) { this.messages.set(message.id, message); if (!this.messagesByConversation.has(message.conversationId)) { this.messagesByConversation.set(message.conversationId, /* @__PURE__ */ new Set()); } this.messagesByConversation.get(message.conversationId).add(message.id); const conversation = this.conversations.get(message.conversationId); if (conversation) { conversation.lastMessageAt = message.timestamp; if (!message.read) { conversation.unreadCount++; } } } async getMessages(conversationId, limit2) { const messageIds = this.messagesByConversation.get(conversationId); if (!messageIds) { return []; } const messages = []; for (const id of messageIds) { const message = this.messages.get(id); if (message) { messages.push(message); } } messages.sort((a, b) => a.timestamp - b.timestamp); if (limit2 && messages.length > limit2) { return messages.slice(-limit2); } return messages; } async markAsRead(messageIds) { for (const id of messageIds) { const message = this.messages.get(id); if (message) { const wasUnread = !message.read; message.read = true; if (wasUnread) { const conversation = this.conversations.get(message.conversationId); if (conversation && conversation.unreadCount > 0) { conversation.unreadCount--; } } } } } async getConversations(userId) { const userConversations = []; for (const conversation of this.conversations.values()) { if (conversation.participants.includes(userId)) { userConversations.push({ ...conversation }); } } userConversations.sort((a, b) => (b.lastMessageAt || 0) - (a.lastMessageAt || 0)); return userConversations; } async saveConversation(conversation) { this.conversations.set(conversation.id, { ...conversation }); } async deleteMessage(messageId) { const message = this.messages.get(messageId); if (message) { const messageIds = this.messagesByConversation.get(message.conversationId); if (messageIds) { messageIds.delete(messageId); } if (!message.read) { const conversation = this.conversations.get(message.conversationId); if (conversation && conversation.unreadCount > 0) { conversation.unreadCount--; } } this.messages.delete(messageId); } } async clear() { this.messages.clear(); this.conversations.clear(); this.messagesByConversation.clear(); } /** * Get a single message by ID (helper method) */ async getMessage(messageId) { return this.messages.get(messageId); } /** * Check if a message exists (helper method for deduplication) */ async hasMessage(messageId) { return this.messages.has(messageId); } }; // ndk/messages/src/messenger.ts var NDKMessenger = class extends import_index22.default { constructor(ndk, options) { super(); __publicField(this, "ndk"); __publicField(this, "storage"); __publicField(this, "nip17"); __publicField(this, "conversations", /* @__PURE__ */ new Map()); __publicField(this, "subscription"); __publicField(this, "myPubkey"); __publicField(this, "started", false); this.ndk = ndk; if (!ndk.signer) { throw new Error("NDK must have a signer configured"); } this.storage = options?.storage || new MemoryAdapter(); this.nip17 = new NIP17Protocol(ndk, ndk.signer); if (options?.autoStart) { this.start().catch(console.error); } } /** * Start the messenger (begin listening for messages) */ async start() { if (this.started) return; if (!this.ndk.signer) { throw new Error("NDK signer not configured"); } const user = await this.ndk.signer.user(); this.myPubkey = user.pubkey; if (this.storage instanceof MemoryAdapter && this.ndk.cacheAdapter?.registerModule) { this.storage = new CacheModuleStorage(this.ndk.cacheAdapter, this.myPubkey); } await this.loadConversations(); await this.subscribeToMessages(); this.started = true; } /** * Stop the messenger */ stop() { if (this.subscription) { this.subscription.stop(); this.subscription = void 0; } this.started = false; } /** * Send a direct message to a user */ async sendMessage(recipient, content) { if (!this.myPubkey) { await this.start(); } const conversation = await this.getConversation(recipient); return conversation.sendMessage(content); } /** * Get or create a conversation with a user */ async getConversation(user) { if (!this.myPubkey) { await this.start(); } const conversationId = [this.myPubkey, user.pubkey].sort().join(":"); let conversation = this.conversations.get(conversationId); if (conversation) { return conversation; } conversation = new NDKConversation( conversationId, [new NDKUser2({ pubkey: this.myPubkey }), user], "nip17", // Default to NIP-17 for now this.storage, this.myPubkey, this.nip17 ); const meta = { id: conversationId, participants: [this.myPubkey, user.pubkey], protocol: "nip17", unreadCount: 0 }; await this.storage.saveConversation(meta); this.conversations.set(conversationId, conversation); conversation.on("message", (message) => { this.emit("message", message); }); conversation.on("error", (error) => { this.emit("error", error); }); return conversation; } /** * Get all conversations */ async getConversations() { if (!this.myPubkey) { await this.start(); } await this.loadConversations(); return Array.from(this.conversations.values()); } /** * Publish DM relay list (kind 10050) */ async publishDMRelays(relays) { return this.nip17.publishDMRelayList(relays); } /** * Load conversations from storage */ async loadConversations() { if (!this.myPubkey) return; const metas = await this.storage.getConversations(this.myPubkey); for (const meta of metas) { if (this.conversations.has(meta.id)) { continue; } const participants = meta.participants.map((pubkey) => new NDKUser2({ pubkey })); const conversation = new NDKConversation( meta.id, participants, meta.protocol, this.storage, this.myPubkey, this.nip17 ); const messages = await this.storage.getMessages(meta.id); for (const message of messages) { message.sender = new NDKUser2({ pubkey: message.sender.pubkey }); if (message.recipient) { message.recipient = new NDKUser2({ pubkey: message.recipient.pubkey }); } await conversation._handleIncomingMessage(message); } this.conversations.set(meta.id, conversation); conversation.on("message", (message) => { this.emit("message", message); }); conversation.on("error", (error) => { this.emit("error", error); }); } } /** * Subscribe to incoming messages */ async subscribeToMessages() { if (!this.myPubkey || !this.ndk.signer) return; const user = await this.ndk.signer.user(); const userRelays = await this.nip17.getUserDMRelays(user); const subOptions = { closeOnEose: false }; if (userRelays.length > 0) { const relaySet = NDKRelaySet2.fromRelayUrls(userRelays, this.ndk); subOptions.relaySet = relaySet; } this.subscription = this.ndk.subscribe( { kinds: [NDKKind2.GiftWrap], "#p": [this.myPubkey] }, { ...subOptions, onEvent: async (wrappedEvent) => { await this.handleIncomingMessage(wrappedEvent); }, onEose: () => { console.log("Messages subscription active"); } } ); } /** * Handle an incoming gift-wrapped message */ async handleIncomingMessage(wrappedEvent) { if (!this.myPubkey || !this.ndk.signer) return; try { const rumor = await this.nip17.unwrapMessage(wrappedEvent); if (!rumor) return; const message = this.nip17.rumorToMessage(rumor, this.myPubkey); const otherPubkey = message.sender.pubkey === this.myPubkey ? message.recipient?.pubkey : message.sender.pubkey; if (!otherPubkey) return; const otherUser = new NDKUser2({ pubkey: otherPubkey }); const conversation = await this.getConversation(otherUser); await conversation._handleIncomingMessage(message); this.emit("message", message); if (conversation.getMessages.length === 1) { this.emit("conversation-created", conversation); } } catch (error) { const errorEvent = { type: "decryption-failed", message: `Failed to decrypt message: ${error}`, error }; this.emit("error", errorEvent); } } /** * Clean up resources */ destroy() { this.stop(); this.conversations.forEach((conv) => conv.destroy()); this.conversations.clear(); this.removeAllListeners(); } }; // ndk/wot/src/filter.ts var import_debug37 = __toESM(require_browser(), 1); var d12 = (0, import_debug37.default)("ndk-wot:filter"); function filterByWoT(wot, events, options = {}) { const { maxDepth, minScore, includeUnknown = false } = options; return events.filter((event) => { const pubkey = event.pubkey; const inWoT = wot.includes(pubkey, { maxDepth }); if (!inWoT) { return includeUnknown; } if (minScore !== void 0) { const score = wot.getScore(pubkey); return score >= minScore; } return true; }); } function rankByWoT(wot, events, options = {}) { const { algorithm = "distance", unknownsLast = false, comparator } = options; if (comparator) { return [...events].sort(comparator); } return [...events].sort((a, b) => { const aPubkey = a.pubkey; const bPubkey = b.pubkey; const aInWoT = wot.includes(aPubkey); const bInWoT = wot.includes(bPubkey); if (unknownsLast) { if (!aInWoT && bInWoT) return 1; if (aInWoT && !bInWoT) return -1; if (!aInWoT && !bInWoT) return 0; } switch (algorithm) { case "distance": { const aDistance = wot.getDistance(aPubkey) ?? Infinity; const bDistance = wot.getDistance(bPubkey) ?? Infinity; return aDistance - bDistance; } case "score": { const aScore = wot.getScore(aPubkey); const bScore = wot.getScore(bPubkey); return bScore - aScore; } case "followers": { const aNode = wot.getNode(aPubkey); const bNode = wot.getNode(bPubkey); const aFollowers = aNode?.followedBy.size ?? 0; const bFollowers = bNode?.followedBy.size ?? 0; return bFollowers - aFollowers; } default: return 0; } }); } function createWoTComparator(wot, options = {}) { const { algorithm = "distance", unknownsLast = false } = options; return (a, b) => { const aPubkey = a.pubkey; const bPubkey = b.pubkey; const aInWoT = wot.includes(aPubkey); const bInWoT = wot.includes(bPubkey); if (unknownsLast) { if (!aInWoT && bInWoT) return 1; if (aInWoT && !bInWoT) return -1; if (!aInWoT && !bInWoT) return 0; } switch (algorithm) { case "distance": { const aDistance = wot.getDistance(aPubkey) ?? Infinity; const bDistance = wot.getDistance(bPubkey) ?? Infinity; return aDistance - bDistance; } case "score": { const aScore = wot.getScore(aPubkey); const bScore = wot.getScore(bPubkey); return bScore - aScore; } case "followers": { const aNode = wot.getNode(aPubkey); const bNode = wot.getNode(bPubkey); const aFollowers = aNode?.followedBy.size ?? 0; const bFollowers = bNode?.followedBy.size ?? 0; return bFollowers - aFollowers; } default: return 0; } }; } // ndk/wot/src/wot.ts init_dist(); init_dist2(); var import_debug38 = __toESM(require_browser(), 1); var d13 = (0, import_debug38.default)("ndk-wot"); var NDKWoT = class { constructor(ndk, rootPubkey) { __publicField(this, "ndk"); __publicField(this, "rootPubkey"); __publicField(this, "nodes", /* @__PURE__ */ new Map()); __publicField(this, "loaded", false); if (!this.isValidPubkey(rootPubkey)) { throw new Error(`Invalid root pubkey: ${rootPubkey}`); } this.ndk = ndk; this.rootPubkey = rootPubkey; this.nodes.set(rootPubkey, { pubkey: rootPubkey, depth: 0, followedBy: /* @__PURE__ */ new Set() }); } /** * Build the WOT graph */ async load(options) { const { depth, maxFollows = 1e3, timeout, useNegentropy = true, negentropyMinAuthors = 5, relayUrls } = options; d13( "Building WOT graph for %s with depth %d (negentropy: %s, minAuthors: %d)", this.rootPubkey, depth, useNegentropy, negentropyMinAuthors ); const startTime = Date.now(); const processedUsers = /* @__PURE__ */ new Set(); for (let currentDepth = 0; currentDepth < depth; currentDepth++) { if (timeout && Date.now() - startTime > timeout) { d13("Timeout reached while building WOT graph"); break; } const usersAtDepth = Array.from(this.nodes.values()).filter((node) => node.depth === currentDepth).map((node) => node.pubkey); if (usersAtDepth.length === 0) break; d13("Processing %d users at depth %d", usersAtDepth.length, currentDepth); const followEvents = await this.fetchContactLists({ authors: usersAtDepth, useNegentropy: useNegentropy && usersAtDepth.length >= negentropyMinAuthors, relayUrls }); for (const event of followEvents) { if (processedUsers.has(event.pubkey)) continue; processedUsers.add(event.pubkey); const follows4 = this.extractFollows(event); const limitedFollows = follows4.slice(0, maxFollows); for (const followedPubkey of limitedFollows) { let node = this.nodes.get(followedPubkey); if (!node) { node = { pubkey: followedPubkey, depth: currentDepth + 1, followedBy: /* @__PURE__ */ new Set([event.pubkey]) }; this.nodes.set(followedPubkey, node); } else { if (currentDepth + 1 < node.depth) { node.depth = currentDepth + 1; } node.followedBy.add(event.pubkey); } } } } this.loaded = true; d13("WOT graph built with %d nodes in %dms", this.nodes.size, Date.now() - startTime); } /** * Fetch contact lists efficiently using negentropy when beneficial */ async fetchContactLists(options) { const { authors, useNegentropy, relayUrls } = options; if (!useNegentropy) { d13("Fetching %d contact lists using subscription", authors.length); return await this.fetchViaSubscription(authors); } d13("Attempting negentropy sync for %d contact lists", authors.length); try { const syncOptions = { autoFetch: true, subId: "wot-sync" }; if (relayUrls) { syncOptions.relayUrls = relayUrls; } const result = await NDKSync.sync( this.ndk, { kinds: [NDKKind2.Contacts], authors }, syncOptions ); d13( "Negentropy sync completed: %d events, %d needed, %d we have", result.events.length, result.need.size, result.have.size ); return new Set(result.events); } catch (error) { d13("Negentropy sync failed, falling back to subscription: %s", error); return await this.fetchViaSubscription(authors); } } /** * Fetch contact lists using subscription (reliable method) */ async fetchViaSubscription(authors) { return new Promise((resolve, reject) => { const events = /* @__PURE__ */ new Set(); const timeout = setTimeout(() => { sub.stop(); reject(new Error(`Timeout fetching contact lists for ${authors.length} authors`)); }, 3e4); const sub = this.ndk.subscribe( { kinds: [NDKKind2.Contacts], authors }, { closeOnEose: true, subId: "wot-fetch", addSinceFromCache: true, onEvent: (event) => { events.add(event); }, onEose: () => { clearTimeout(timeout); sub.stop(); d13("Subscription fetch completed: %d events", events.size); resolve(events); } } ); }); } /** * Validate if a string is a valid pubkey (64 char hex) */ isValidPubkey(pubkey) { return /^[0-9a-f]{64}$/i.test(pubkey); } /** * Extract pubkeys from a contacts list event */ extractFollows(event) { const follows4 = []; for (const tag of event.tags) { if (tag[0] === "p") { const pubkey = tag[1]; if (pubkey && typeof pubkey === "string" && this.isValidPubkey(pubkey)) { follows4.push(pubkey); } else if (pubkey) { d13("Skipping invalid p-tag pubkey: %s", pubkey); } } } return follows4; } /** * Get WOT score for a pubkey (lower depth = higher score) */ getScore(pubkey) { const node = this.nodes.get(pubkey); if (!node) return 0; return 1 / (node.depth + 1); } /** * Get distance (depth) for a pubkey */ getDistance(pubkey) { const node = this.nodes.get(pubkey); return node ? node.depth : null; } /** * Check if pubkey is in WOT */ includes(pubkey, options) { const node = this.nodes.get(pubkey); if (!node) return false; if (options?.maxDepth !== void 0) { return node.depth <= options.maxDepth; } return true; } /** * Get all pubkeys in WOT */ getAllPubkeys(options) { let nodes = Array.from(this.nodes.values()); if (options?.maxDepth !== void 0) { const maxDepth = options.maxDepth; nodes = nodes.filter((node) => node.depth <= maxDepth); } return nodes.map((node) => node.pubkey); } /** * Get scores for multiple pubkeys */ getScores(pubkeys) { const scores = /* @__PURE__ */ new Map(); for (const pubkey of pubkeys) { scores.set(pubkey, this.getScore(pubkey)); } return scores; } /** * Get the WOT node for a pubkey */ getNode(pubkey) { return this.nodes.get(pubkey) || null; } /** * Check if WOT graph has been loaded */ isLoaded() { return this.loaded; } /** * Get total number of nodes in the graph */ get size() { return this.nodes.size; } }; // ndk/blossom/src/blossom.ts init_dist(); // ndk/blossom/src/healing/url-healing.ts init_dist(); init_types(); // ndk/blossom/src/upload/uploader.ts init_dist(); init_types(); init_auth(); init_errors(); // ndk/blossom/src/utils/http.ts init_types(); init_constants(); init_errors(); init_logger(); var defaultLogger = new DebugLogger(); async function fetchWithRetry(url, options = {}, retryOptions = {}, logger4 = defaultLogger) { const retry = { ...DEFAULT_RETRY_OPTIONS, ...retryOptions }; const headers = { ...DEFAULT_HEADERS, ...options.headers || {} }; let attempts = 0; const getNextDelay = () => retry.retryDelay * retry.backoffFactor ** attempts; while (attempts <= retry.maxRetries) { try { const response = await fetch(url, { ...options, headers }); if (!response.ok && retry.retryableStatusCodes.includes(response.status) && attempts < retry.maxRetries) { attempts++; const delay = getNextDelay(); logger4.warn( `Request failed with status ${response.status}, retrying in ${delay}ms (attempt ${attempts}/${retry.maxRetries})`, { url } ); await new Promise((resolve) => setTimeout(resolve, delay)); continue; } return response; } catch (error) { if (attempts < retry.maxRetries) { attempts++; const delay = getNextDelay(); logger4.warn(`Network error, retrying in ${delay}ms (attempt ${attempts}/${retry.maxRetries})`, { url, error }); await new Promise((resolve) => setTimeout(resolve, delay)); } else { throw new NDKBlossomServerError( `Network request failed after ${retry.maxRetries} retries: ${error.message}`, ErrorCodes.SERVER_UNAVAILABLE, url, void 0, error ); } } } throw new NDKBlossomServerError( `Request failed after ${retry.maxRetries} retries`, ErrorCodes.SERVER_UNAVAILABLE, url ); } async function checkResourceExists(url, options = {}, retryOptions = {}) { try { const response = await fetchWithRetry( url, { ...options, method: "HEAD" }, retryOptions ); return response.ok; } catch (error) { return false; } } async function checkBlobExists(serverUrl, hash3, retryOptions = {}) { const baseUrl = serverUrl.endsWith("/") ? serverUrl.slice(0, -1) : serverUrl; const url = `${baseUrl}/${hash3}`; return checkResourceExists(url, { method: "HEAD" }, retryOptions); } async function extractResponseJson(response, serverUrl) { if (!response.ok) { if (SERVER_ERROR_STATUS_CODES.includes(response.status)) { throw new NDKBlossomServerError( `Server error: ${response.status} ${response.statusText}`, ErrorCodes.SERVER_ERROR, serverUrl, response.status ); } else { throw new NDKBlossomServerError( `Request rejected: ${response.status} ${response.statusText}`, ErrorCodes.SERVER_REJECTED, serverUrl, response.status ); } } try { return await response.json(); } catch (error) { throw new NDKBlossomServerError( `Invalid JSON response: ${error.message}`, ErrorCodes.SERVER_INVALID_RESPONSE, serverUrl, response.status, error ); } } // ndk/blossom/src/upload/uploader.ts init_logger(); var logger2 = new DebugLogger("ndk:blossom:uploader"); async function uploadToServer(ndk, file, serverUrl, options = {}) { logger2.debug(`Uploading file to ${serverUrl}`, { fileName: file.name, fileType: file.type, fileSize: file.size }); if (!options.sha256Calculator) { throw new NDKBlossomUploadError( "SHA256Calculator is required for upload. Please provide one in options.", "NO_SHA256_CALCULATOR", serverUrl ); } const sha256Calculator = options.sha256Calculator; const hash3 = await sha256Calculator.calculateSha256(file); logger2.debug(`File hash: ${hash3}`); try { const baseUrl = serverUrl.endsWith("/") ? serverUrl.slice(0, -1) : serverUrl; const uploadUrl = `${baseUrl}/upload`; const authOptions = await createAuthenticatedFetchOptions(ndk, "upload", { sha256: hash3, content: `Upload ${file.name}`, signer: options.signer, fetchOptions: { method: "PUT", body: file, headers: { "Content-Type": file.type || "application/octet-stream", ...options.headers } } }); if (options.onProgress) { const originalBody = authOptions.body; if (originalBody instanceof File || originalBody instanceof Blob) { const xhr = new XMLHttpRequest(); const uploadPromise = new Promise((resolve, reject) => { xhr.upload.addEventListener("progress", (event) => { if (event.lengthComputable) { options.onProgress?.({ loaded: event.loaded, total: event.total }); } }); xhr.addEventListener("load", () => { if (xhr.status >= 200 && xhr.status < 300) { try { const parsedResponse = JSON.parse(xhr.responseText); resolve(parsedResponse); } catch (error) { reject( new NDKBlossomServerError( `Invalid response from server: ${error.message}`, ErrorCodes.SERVER_INVALID_RESPONSE, serverUrl, xhr.status, error ) ); } } else { reject( new NDKBlossomServerError( `Upload failed with status ${xhr.status}`, ErrorCodes.SERVER_REJECTED, serverUrl, xhr.status ) ); } }); xhr.addEventListener("error", () => { reject( new NDKBlossomServerError( "Network error during upload", ErrorCodes.SERVER_UNAVAILABLE, serverUrl ) ); }); xhr.addEventListener("abort", () => { reject(new NDKBlossomServerError("Upload aborted", ErrorCodes.UPLOAD_FAILED, serverUrl)); }); xhr.open("PUT", uploadUrl); for (const [key, value] of Object.entries(authOptions.headers || {})) { xhr.setRequestHeader(key, value); } xhr.send(originalBody); }); return await uploadPromise; } } const response = await fetchWithRetry(uploadUrl, authOptions, { maxRetries: options.maxRetries, retryDelay: options.retryDelay }); await extractResponseJson(response, serverUrl); const url = `${baseUrl}/${hash3}`; return { url, size: file.size.toString(), m: file.type, x: hash3 }; } catch (error) { if (error instanceof NDKBlossomServerError || error instanceof NDKBlossomAuthError) { throw error; } throw new NDKBlossomUploadError( `Upload failed: ${error.message}`, ErrorCodes.UPLOAD_FAILED, serverUrl, error ); } } async function uploadFile(ndkBlossom, file, options = {}) { logger2.debug(`Starting file upload`, { fileName: file.name, fileType: file.type, fileSize: file.size }); logger2.debug(`Upload options:`, { hasServer: !!options.server, hasFallbackServer: !!options.fallbackServer, fallbackServer: options.fallbackServer, allOptions: options }); if (options.server) { try { const result = await uploadToServer(ndkBlossom.ndk, file, options.server, options); logger2.debug(`Upload successful to specified server ${options.server}`); return result; } catch (error) { logger2.error(`Upload to specified server ${options.server} failed:`, error); throw new NDKBlossomUploadError( `Upload failed on specified server: ${options.server}: ${error.message}`, ErrorCodes.UPLOAD_FAILED ); } } const serverList = await ndkBlossom.getServerList(); let serverUrls = []; if (serverList && Array.isArray(serverList.servers)) { serverUrls = serverList.servers; } const errors = []; for (const serverUrl of serverUrls) { try { const result = await uploadToServer(ndkBlossom.ndk, file, serverUrl, options); logger2.debug(`Upload successful to ${serverUrl}`); return result; } catch (error) { logger2.error(`Upload to ${serverUrl} failed:`, error); errors.push({ serverUrl, error }); if (options.onServerError && error instanceof NDKBlossomServerError) { const action = options.onServerError(error, serverUrl); if (action === "retry") { try { const result = await uploadToServer(ndkBlossom.ndk, file, serverUrl, options); logger2.debug(`Retry upload successful to ${serverUrl}`); return result; } catch (retryError) { logger2.error(`Retry upload to ${serverUrl} failed:`, retryError); errors.push({ serverUrl, error: retryError }); } } } } } if (options.fallbackServer) { try { const result = await uploadToServer(ndkBlossom.ndk, file, options.fallbackServer, options); logger2.debug(`Upload successful to fallback server ${options.fallbackServer}`); return result; } catch (error) { logger2.error(`Upload to fallback server ${options.fallbackServer} failed:`, error); errors.push({ serverUrl: options.fallbackServer, error }); } } const errorDetails = errors.map((e2) => { const err = e2.error; let details = `${e2.serverUrl}: ${err.message}`; if (err instanceof NDKBlossomServerError) { details += ` (status: ${err.status}, code: ${err.code})`; if (err.cause) { details += ` - ${err.cause.message}`; } } else if (err instanceof NDKBlossomAuthError) { details += ` (auth error, code: ${err.code})`; } else if (err instanceof NDKBlossomUploadError) { details += ` (code: ${err.code})`; if (err.cause) { details += ` - cause: ${err.cause.message}`; } } return details; }); const errorMessage = serverUrls.length === 0 ? `No blossom servers configured. Please add servers to your profile or provide a fallbackServer. ${options.fallbackServer ? `Fallback server also failed: ${errorDetails[0] || "unknown error"}` : ""}` : `Upload failed on all ${serverUrls.length} configured server(s)${options.fallbackServer ? " and fallback server" : ""}: ${errorDetails.join("\n")}`; logger2.error(errorMessage); throw new NDKBlossomUploadError(errorMessage, ErrorCodes.ALL_SERVERS_FAILED); } async function findHashInNostr(ndk, hash3) { logger2.debug(`Searching for hash ${hash3} in nostr events`); const filter = { "#x": [hash3], limit: 10 }; try { const events = await ndk.fetchEvents(filter); if (events.size === 0) { return []; } const foundUrls = /* @__PURE__ */ new Set(); for (const event of events) { for (const tag of event.tags) { if (tag[0] === "imeta") { const imetaTag = mapImetaTag2(tag); if (imetaTag.url && imetaTag.x === hash3) { foundUrls.add(imetaTag.url); } } if (tag[0] === "url" && tag[1]) { const urlHash = extractHashFromUrl(tag[1]); if (urlHash === hash3) { foundUrls.add(tag[1]); } } } } return Array.from(foundUrls); } catch (error) { logger2.error(`Error searching for hash in nostr:`, error); return []; } } // ndk/blossom/src/healing/url-healing.ts init_errors(); init_logger(); var logger3 = new DebugLogger("ndk:blossom:url-healing"); function extractHashFromUrl(url) { try { const urlObj = new URL(url); const pathname = urlObj.pathname; const parts = pathname.split("/"); const lastPart = parts[parts.length - 1]; const hash3 = lastPart.includes(".") ? lastPart.split(".")[0] : lastPart; if (/^[a-f0-9]{64}$/i.test(hash3)) { return hash3; } return void 0; } catch (error) { logger3.error(`Error extracting hash from URL ${url}:`, error); return void 0; } } async function tryUrls(urls, skipUrl) { if (urls.length === 0) return void 0; const filteredUrls = skipUrl ? urls.filter((url) => url !== skipUrl) : urls; if (filteredUrls.length === 0) return void 0; for (const url of filteredUrls) { try { const exists4 = await checkBlobExists(url, ""); if (exists4) { logger3.debug(`Found working URL: ${url}`); return url; } } catch (error) { logger3.debug(`URL check failed for ${url}:`, error); } } logger3.debug(`No working URLs found, returning first URL: ${filteredUrls[0]}`); return filteredUrls[0]; } async function fixUrl(ndk, user, url) { logger3.debug(`Fixing URL: ${url}`); const hash3 = extractHashFromUrl(url); if (!hash3) { logger3.debug(`Invalid URL, no hash found: ${url}`); return url; } try { const exists4 = await checkBlobExists(url, ""); if (exists4) { logger3.debug(`Original URL works, no need to fix: ${url}`); return url; } } catch (error) { logger3.debug(`Original URL check failed: ${error}`); } try { const filter = { kinds: [NDKKind2.BlossomList], authors: [user.pubkey] }; const event = await ndk.fetchEvent(filter); let serverUrls = []; if (event) { const wrappedEvent = wrapEvent2(event); serverUrls = wrappedEvent.servers; } if (serverUrls.length === 0) { logger3.debug(`No servers found for user ${user.pubkey}`); const nostrUrls2 = await findHashInNostr(ndk, hash3); const workingUrl2 = await tryUrls(nostrUrls2, url); if (workingUrl2) { return workingUrl2; } return url; } for (const serverUrl of serverUrls) { try { const baseUrl = serverUrl.endsWith("/") ? serverUrl.slice(0, -1) : serverUrl; const newUrl = `${baseUrl}/${hash3}`; const exists4 = await checkBlobExists(serverUrl, hash3); if (exists4) { logger3.debug(`Found alternative server: ${newUrl}`); return newUrl; } } catch (error) { logger3.debug(`Server check failed for ${serverUrl}:`, error); } } const nostrUrls = await findHashInNostr(ndk, hash3); const workingUrl = await tryUrls(nostrUrls, url); if (workingUrl) { return workingUrl; } logger3.debug(`Could not fix URL: ${url}`); return url; } catch (error) { logger3.debug(`Error fixing URL: ${error}`); return url; } } async function getBlobUrlByHash(ndk, user, hash3) { logger3.debug(`Getting blob URL for hash: ${hash3}`); try { const filter = { kinds: [NDKKind2.BlossomList], authors: [user.pubkey] }; const event = await ndk.fetchEvent(filter); let serverUrls = []; if (event) { serverUrls = event.tags.filter((tag) => tag[0] === "server" && tag[1]).map((tag) => tag[1]); } if (serverUrls.length === 0) { logger3.debug(`No servers found for user ${user.pubkey}`); const nostrUrls = await findHashInNostr(ndk, hash3); const workingUrl = await tryUrls(nostrUrls); if (workingUrl) { return workingUrl; } throw new NDKBlossomNotFoundError( `No servers found for user ${user.pubkey}`, ErrorCodes.USER_SERVER_LIST_NOT_FOUND ); } for (const serverUrl of serverUrls) { try { const baseUrl = serverUrl.endsWith("/") ? serverUrl.slice(0, -1) : serverUrl; const url = `${baseUrl}/${hash3}`; const exists4 = await checkBlobExists(serverUrl, hash3); if (exists4) { logger3.debug(`Found blob on server: ${url}`); return url; } } catch (error) { logger3.debug(`Server check failed for ${serverUrl}:`, error); } } throw new NDKBlossomNotFoundError( `Blob with hash ${hash3} not found on any of user's servers`, ErrorCodes.BLOB_NOT_FOUND ); } catch (error) { if (error instanceof NDKBlossomNotFoundError) { throw error; } const nostrUrls = await findHashInNostr(ndk, hash3); const workingUrl = await tryUrls(nostrUrls); if (workingUrl) { return workingUrl; } throw new NDKBlossomNotFoundError( `Failed to get blob URL: ${error.message}`, ErrorCodes.BLOB_NOT_FOUND, void 0, error ); } } // ndk/blossom/src/blossom.ts init_constants(); init_errors(); init_logger(); // ndk/blossom/src/utils/sha256.ts var DefaultSHA256Calculator = class { /** * Calculate SHA256 hash of a file * * @param file File to hash * @returns Hash as hex string */ async calculateSha256(file) { const buffer = await file.arrayBuffer(); const hashBuffer = await crypto.subtle.digest("SHA-256", buffer); return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join(""); } }; var _defaultSHA256Calculator; function getDefaultSHA256Calculator() { if (!_defaultSHA256Calculator) { _defaultSHA256Calculator = new DefaultSHA256Calculator(); } return _defaultSHA256Calculator; } var defaultSHA256Calculator = { calculateSha256: async (file) => { return getDefaultSHA256Calculator().calculateSha256(file); } }; // ndk/blossom/src/blossom.ts var NDKBlossom = class { /** * Constructor for NDKBlossom * @param ndk NDK instance * @param signer Optional signer to use for authentication (falls back to ndk.signer) */ constructor(ndk, signer) { this.serverConfigs = /* @__PURE__ */ new Map(); this.debugMode = false; this.ndk = ndk; this.signer = signer; this.retryOptions = DEFAULT_RETRY_OPTIONS; this.logger = new DebugLogger(); this.sha256Calculator = defaultSHA256Calculator; } /** * Enable or disable debug mode */ set debug(value) { this.debugMode = value; } /** * Get debug mode status */ get debug() { return this.debugMode; } /** * Set custom logger */ set loggerFunction(logFn) { this.logger = new CustomLogger(logFn); } /** * Set a custom SHA256 calculator implementation * @param calculator Custom SHA256 calculator */ setSHA256Calculator(calculator) { this.sha256Calculator = calculator; } /** * Get the current SHA256 calculator implementation * @returns Current SHA256 calculator */ getSHA256Calculator() { return this.sha256Calculator; } set serverList(serverList) { this._serverList = serverList; } async getServerList(user) { if (this._serverList) { this.logger.debug(`Using cached server list with ${this._serverList.servers.length} servers`); return this._serverList; } user ?? (user = this.ndk.activeUser); if (!user) { this.logger.error("No user available to fetch server list"); throw new NDKBlossomError("No user available to fetch server list", "NO_SIGNER"); } this.logger.debug(`Fetching server list for user ${user.pubkey}`); const filter = { kinds: NDKBlossomList2.kinds, authors: [user.pubkey] }; const event = await this.ndk.fetchEvent(filter); if (!event) { this.logger.warn(`No blossom server list event found for user ${user.pubkey}`); return void 0; } this._serverList = wrapEvent2(event); this.logger.debug( `Found server list with ${this._serverList.servers.length} servers: ${this._serverList.servers.join(", ")}` ); return this._serverList; } /** * Uploads a file to a Blossom server * @param file The file to upload * @param options Upload options * @returns Image metadata */ async upload(file, options = {}) { try { if (this.onUploadProgress) { options.onProgress = (progress) => { if (this.onUploadProgress) { return this.onUploadProgress(progress, file, "unknown"); } return "continue"; }; } if (!options.sha256Calculator) { options.sha256Calculator = this.getSHA256Calculator(); } if (!options.signer) { options.signer = this.signer; } const result = await uploadFile(this, file, options); return result; } catch (error) { if (this.onUploadFailed && error instanceof Error) { this.onUploadFailed( error.message, error instanceof NDKBlossomUploadError ? error.serverUrl : void 0, file ); } throw error; } } /** * Fixes a Blossom URL by finding an alternative server with the same blob * @param user The user whose servers to check * @param url The URL to fix * @returns A fixed URL pointing to a valid Blossom server */ async fixUrl(user, url) { return fixUrl(this.ndk, user, url); } /** * Gets a blob from a URL * @param url The URL of the blob * @returns The blob response */ async getBlob(url) { try { return await fetchWithRetry(url, {}, this.retryOptions); } catch (error) { throw new NDKBlossomNotFoundError( `Failed to fetch blob: ${error.message}`, "BLOB_NOT_FOUND", url, error ); } } /** * Gets a blob by its hash from one of the user's servers * @param user The user whose servers to check * @param hash The hash of the blob * @returns The blob response */ async getBlobByHash(user, hash3) { const url = await getBlobUrlByHash(this.ndk, user, hash3); return this.getBlob(url); } /** * Lists blobs for a user * @param user The user whose blobs to list * @returns Array of blob descriptors */ async listBlobs(user) { const serverList = await this.getServerList(); let serverUrls = []; if (serverList) serverUrls = serverList.servers; if (serverUrls.length === 0) { this.logger.error(`No servers found for user ${user.pubkey}`); return []; } const blobMap = /* @__PURE__ */ new Map(); for (const serverUrl of serverUrls) { try { const baseUrl = serverUrl.endsWith("/") ? serverUrl.slice(0, -1) : serverUrl; const url = `${baseUrl}/list/${user.pubkey}`; const response = await fetchWithRetry(url, {}, this.retryOptions); if (!response.ok) { continue; } const data = await response.json(); if (Array.isArray(data)) { for (const blob of data) { const imeta = { url: blob.url, size: blob.size?.toString(), m: blob.mime_type, x: blob.sha256, dim: blob.width && blob.height ? `${blob.width}x${blob.height}` : void 0, blurhash: blob.blurhash, alt: blob.alt }; if (blob.sha256) { blobMap.set(blob.sha256, imeta); } } } } catch (error) { this.logger.error(`Error listing blobs on server ${serverUrl}:`, error); } } return Array.from(blobMap.values()); } /** * Deletes a blob * @param hash The hash of the blob to delete * @returns True if successful */ async deleteBlob(hash3) { const signer = this.signer ?? this.ndk.signer; if (!signer) { throw new NDKBlossomAuthError("No signer available to delete blob", "NO_SIGNER"); } const pubkey = (await signer.user()).pubkey; const filter = { kinds: [NDKKind2.BlossomList], authors: [pubkey] }; const event = await this.ndk.fetchEvent(filter); let serverUrls = []; if (event) { serverUrls = event.tags.filter((tag) => tag[0] === "server" && tag[1]).map((tag) => tag[1]); } if (serverUrls.length === 0) { this.logger.error(`No servers found for user ${pubkey}`); return false; } let success = false; for (const serverUrl of serverUrls) { try { const baseUrl = serverUrl.endsWith("/") ? serverUrl.slice(0, -1) : serverUrl; const url = `${baseUrl}/${hash3}`; const options = await createAuthenticatedFetchOptions2(this.ndk, "delete", { sha256: hash3, content: `Delete blob ${hash3}`, signer, fetchOptions: { method: "DELETE" } }); const response = await fetchWithRetry(url, options, this.retryOptions); if (response.ok) { success = true; } } catch (error) { this.logger.error(`Error deleting blob on server ${serverUrl}:`, error); } } return success; } /** * Checks if a server has a blob * @param serverUrl The URL of the server * @param hash The hash of the blob * @returns True if the server has the blob */ async checkServerForBlob(serverUrl, hash3) { return checkBlobExists(serverUrl, hash3); } /** * Sets retry options for network operations * @param options Retry options */ setRetryOptions(options) { this.retryOptions = { ...this.retryOptions, ...options }; } /** * Sets server-specific configuration * @param serverUrl The URL of the server * @param config Server configuration */ setServerConfig(serverUrl, config) { this.serverConfigs.set(serverUrl, config); } /** * Gets an optimized version of a blob * @param url The URL of the blob * @param options Optimization options * @returns The optimized blob response */ async getOptimizedBlob(url, options = {}) { try { const urlObj = new URL(url); const baseUrl = `${urlObj.protocol}//${urlObj.host}`; const hash3 = urlObj.pathname.split("/").pop(); if (!hash3) { throw new NDKBlossomOptimizationError("Invalid URL, no hash found", "BLOB_NOT_FOUND", url); } let mediaUrl = `${baseUrl}/media/${hash3}`; const params = new URLSearchParams(); for (const [key, value] of Object.entries(options)) { if (value !== void 0) { params.append(key, value.toString()); } } if (params.toString()) { mediaUrl += `?${params.toString()}`; } const response = await fetchWithRetry(mediaUrl, {}, this.retryOptions); if (!response.ok) { throw new NDKBlossomOptimizationError( `Failed to get optimized blob: ${response.status} ${response.statusText}`, "SERVER_REJECTED", url ); } return response; } catch (error) { if (error instanceof NDKBlossomOptimizationError) { throw error; } throw new NDKBlossomOptimizationError( `Failed to get optimized blob: ${error.message}`, "SERVER_UNSUPPORTED", url, error ); } } /** * Gets an optimized URL for a blob * @param url The URL of the blob * @param options Optimization options * @returns The optimized URL */ async getOptimizedUrl(url, options = {}) { const urlObj = new URL(url); const baseUrl = `${urlObj.protocol}//${urlObj.host}`; const hash3 = urlObj.pathname.split("/").pop(); if (!hash3) { throw new NDKBlossomOptimizationError("Invalid URL, no hash found", "BLOB_NOT_FOUND", url); } let mediaUrl = `${baseUrl}/media/${hash3}`; const params = new URLSearchParams(); for (const [key, value] of Object.entries(options)) { if (value !== void 0) { params.append(key, value.toString()); } } if (params.toString()) { mediaUrl += `?${params.toString()}`; } return mediaUrl; } /** * Generates a srcset for responsive images * @param url The base URL of the image * @param sizes Array of size configurations * @returns A srcset string */ generateSrcset(url, sizes) { const srcset = []; const urlObj = new URL(url); const baseUrl = `${urlObj.protocol}//${urlObj.host}`; const hash3 = urlObj.pathname.split("/").pop(); if (!hash3) { return ""; } for (const size of sizes) { const params = new URLSearchParams(); params.append("width", size.width.toString()); if (size.format) { params.append("format", size.format); } const mediaUrl = `${baseUrl}/media/${hash3}?${params.toString()}`; srcset.push(`${mediaUrl} ${size.width}w`); } return srcset.join(", "); } }; async function createAuthenticatedFetchOptions2(ndk, action, options = {}) { const { createAuthenticatedFetchOptions: authFn } = await Promise.resolve().then(() => (init_auth(), auth_exports)); return authFn(ndk, action, options); } var blossom_default = NDKBlossom; // ndk/wallet/src/nip87/mint-store.ts init_dist(); init_vanilla(); async function fetchMintInfo2(url, ndk) { if (ndk.cacheAdapter?.getCacheData) { try { const cached = await ndk.cacheAdapter.getCacheData("wallet:mint:info", url); if (cached) { return { isOnline: true, info: cached }; } } catch (e2) { console.error("Error reading mint info from cache:", e2); } } try { const response = await fetch(`${url}/v1/info`); if (response.ok) { const info = await response.json(); if (ndk.cacheAdapter?.setCacheData) { try { await ndk.cacheAdapter.setCacheData("wallet:mint:info", url, info); } catch (e2) { console.error("Error caching mint info:", e2); } } return { isOnline: true, info }; } return { isOnline: false }; } catch { return { isOnline: false }; } } function createMintDiscoveryStore2(ndk, options = {}) { const { network = "mainnet", timeout = 1e4, followUsers } = options; let mintSub; let recSub; let timeoutId; const mintsMap = /* @__PURE__ */ new Map(); const store = createStore()((set, get) => ({ mints: [], progress: { announcementsFound: 0, recommendationsFound: 0 }, getMint: (url) => mintsMap.get(url), getTopMints: (limit2 = 10, minRecommendations = 0) => { let filtered = get().mints; if (minRecommendations > 0) { filtered = filtered.filter((m) => m.recommendations.length >= minRecommendations); } return filtered.sort((a, b) => b.score - a.score).slice(0, limit2); }, searchMints: (query3) => { const lowerQuery = query3.toLowerCase(); return get().mints.filter( (mint) => mint.url.toLowerCase().includes(lowerQuery) || mint.name?.toLowerCase().includes(lowerQuery) || mint.description?.toLowerCase().includes(lowerQuery) ); }, recommendMint: async (url, review) => { const rec = new NDKMintRecommendation2(ndk); rec.recommendedKind = NDKKind2.CashuMintAnnouncement; rec.urls = [url]; rec.review = review; await rec.sign(); await rec.publish(); }, stop: () => { mintSub?.stop(); recSub?.stop(); if (timeoutId) { clearTimeout(timeoutId); } } })); mintSub = ndk.subscribe( { kinds: [NDKKind2.CashuMintAnnouncement], limit: 100 }, { closeOnEose: false, onEvent: async (event) => { const mint = await NDKCashuMintAnnouncement2.from(event); if (!mint) return; if (network && mint.network !== network) return; const url = mint.url; if (!url) return; const existing = mintsMap.get(url); const mintData = { url, identifier: mint.identifier, network: mint.network, nuts: mint.nuts || [], name: mint.metadata?.name, description: mint.metadata?.description, icon: mint.metadata?.icon, longDescription: mint.metadata?.longDescription, contact: mint.metadata?.contact, motd: mint.metadata?.motd, recommendations: existing?.recommendations || [], score: existing?.score || 0, lastUpdated: Date.now() }; mintsMap.set(url, mintData); store.setState((state) => ({ mints: Array.from(mintsMap.values()), progress: { ...state.progress, announcementsFound: state.progress.announcementsFound + 1 } })); fetchMintInfo2(url, ndk).then(({ isOnline, info }) => { const existing2 = mintsMap.get(url); if (!existing2) return; mintsMap.set(url, { ...existing2, isOnline, name: info?.name || existing2.name, description: info?.description || existing2.description, icon: info?.icon || existing2.icon, longDescription: info?.longDescription || existing2.longDescription, contact: info?.contact || existing2.contact, motd: info?.motd || existing2.motd, lastUpdated: Date.now() }); store.setState({ mints: Array.from(mintsMap.values()) }); }); } } ); const recFilter = { kinds: [NDKKind2.EcashMintRecommendation], "#k": [NDKKind2.CashuMintAnnouncement.toString()], limit: 500 }; if (followUsers && followUsers.length > 0) { recFilter.authors = followUsers; } recSub = ndk.subscribe(recFilter, { closeOnEose: false, onEvent: async (event) => { const rec = await NDKMintRecommendation2.from(event); if (!rec) return; const urls = rec.urls; for (const url of urls) { const mint = mintsMap.get(url); if (mint) { mint.recommendations.push(rec); mint.score = mint.recommendations.length; mintsMap.set(url, { ...mint }); } else { mintsMap.set(url, { url, nuts: [], recommendations: [rec], score: 1, lastUpdated: Date.now() }); } store.setState((state) => ({ mints: Array.from(mintsMap.values()), progress: { ...state.progress, recommendationsFound: state.progress.recommendationsFound + 1 } })); } } }); if (timeout > 0) { timeoutId = setTimeout(() => { store.getState().stop(); }, timeout); } return store; } // ndk/wallet/src/nutzap-monitor/index.ts init_dist(); var import_tseep26 = __toESM(require_lib(), 1); // ndk/wallet/src/wallets/cashu/wallet/index.ts init_cashu_ts_es(); init_dist(); init_dist2(); // ndk/wallet/src/wallets/index.ts var import_tseep23 = __toESM(require_lib(), 1); // ndk/wallet/src/wallets/cashu/mint.ts init_cashu_ts_es(); var mintWallets2 = /* @__PURE__ */ new Map(); var mintWalletPromises2 = /* @__PURE__ */ new Map(); function mintKey2(mint, unit, pk) { if (unit === "sats") { unit = "sat"; } if (pk) { const pkStr = new TextDecoder().decode(pk); return `${mint}-${unit}-${pkStr}`; } return `${mint}-${unit}`; } async function walletForMint2(mint, { pk, timeout = 15e3, mintInfo, mintKeys, onMintInfoNeeded, onMintInfoLoaded, onMintKeysNeeded, onMintKeysLoaded } = {}) { const startTime = Date.now(); const ts = () => `+${Date.now() - startTime}ms`; if (onMintInfoNeeded) { console.log(`[MINT-CACHE ${ts()}] Querying cache for mint info: ${mint}`); const cacheStartTime = Date.now(); mintInfo ?? (mintInfo = await onMintInfoNeeded(mint)); const cacheTime = Date.now() - cacheStartTime; if (mintInfo) { console.log(`[MINT-CACHE ${ts()}] \u2713 Cache HIT for mint info: ${mint} (${cacheTime}ms)`, { name: mintInfo.name }); } else { console.log(`[MINT-CACHE ${ts()}] \u2717 Cache MISS for mint info: ${mint} (${cacheTime}ms)`); } } if (onMintKeysNeeded) { console.log(`[MINT-CACHE ${ts()}] Querying cache for mint keys: ${mint}`); const cacheStartTime = Date.now(); mintKeys ?? (mintKeys = await onMintKeysNeeded(mint)); const cacheTime = Date.now() - cacheStartTime; if (mintKeys) { console.log(`[MINT-CACHE ${ts()}] \u2713 Cache HIT for mint keys: ${mint} (${cacheTime}ms)`, { count: mintKeys.length }); } else { console.log(`[MINT-CACHE ${ts()}] \u2717 Cache MISS for mint keys: ${mint} (${cacheTime}ms)`); } } if (!mintInfo && onMintInfoLoaded) { console.log(`[MINT-CACHE ${ts()}] Fetching mint info from ${mint}/v1/info`); const fetchStartTime = Date.now(); mintInfo = await q.getInfo(mint); const fetchTime = Date.now() - fetchStartTime; console.log(`[MINT-CACHE ${ts()}] Caching mint info: ${mint} (fetched in ${fetchTime}ms)`, { name: mintInfo.name }); onMintInfoLoaded?.(mint, mintInfo); } const unit = "sat"; const key = mintKey2(mint, unit, pk); if (mintWallets2.has(key)) { console.log(`[MINT-CACHE ${ts()}] Returning cached wallet instance: ${mint}`); return mintWallets2.get(key); } if (mintWalletPromises2.has(key)) { console.log(`[MINT-CACHE ${ts()}] Wallet loading in progress, returning existing promise: ${mint}`); return mintWalletPromises2.get(key); } if (!mintInfo) { if (onMintInfoNeeded) { console.log(`[MINT-CACHE ${ts()}] Querying cache for mint info (second check): ${mint}`); const cacheStartTime = Date.now(); mintInfo = await onMintInfoNeeded(mint); const cacheTime = Date.now() - cacheStartTime; if (mintInfo) { console.log(`[MINT-CACHE ${ts()}] \u2713 Cache HIT for mint info (second check): ${mint} (${cacheTime}ms)`, { name: mintInfo.name }); } else { console.log(`[MINT-CACHE ${ts()}] \u2717 Cache MISS for mint info (second check): ${mint} (${cacheTime}ms)`); } } if (!mintInfo && onMintInfoLoaded) { console.log(`[MINT-CACHE ${ts()}] Fetching mint info from ${mint}/v1/info (second check)`); const fetchStartTime = Date.now(); mintInfo = await q.getInfo(mint); const fetchTime = Date.now() - fetchStartTime; console.log(`[MINT-CACHE ${ts()}] Caching mint info (second check): ${mint} (fetched in ${fetchTime}ms)`, { name: mintInfo.name }); onMintInfoLoaded(mint, mintInfo); } } if (!mintKeys && onMintKeysNeeded) { console.log(`[MINT-CACHE ${ts()}] Querying cache for mint keys (second check): ${mint}`); const cacheStartTime = Date.now(); mintKeys = await onMintKeysNeeded(mint); const cacheTime = Date.now() - cacheStartTime; if (mintKeys) { console.log(`[MINT-CACHE ${ts()}] \u2713 Cache HIT for mint keys (second check): ${mint} (${cacheTime}ms)`, { count: mintKeys.length }); } else { console.log(`[MINT-CACHE ${ts()}] \u2717 Cache MISS for mint keys (second check): ${mint} (${cacheTime}ms)`); } } const wallet = new us(new q(mint), { unit, bip39seed: pk, mintInfo, keys: mintKeys }); const loadPromise = new Promise(async (resolve) => { try { console.log(`[MINT-CACHE ${ts()}] Loading mint wallet: ${mint}`); const loadStartTime = Date.now(); const timeoutPromise = new Promise((_2, rejectTimeout) => { setTimeout(() => { rejectTimeout(new Error("timeout loading mint")); }, timeout); }); await Promise.race([wallet.loadMint(), timeoutPromise]); const loadTime = Date.now() - loadStartTime; console.log(`[MINT-CACHE ${ts()}] Mint wallet loaded: ${mint} (${loadTime}ms)`); mintWallets2.set(key, wallet); mintWalletPromises2.delete(key); if (wallet.keys) { console.log(`[MINT-CACHE ${ts()}] Caching mint keys after loadMint: ${mint}`, { count: wallet.keys.size }); onMintKeysLoaded?.(mint, wallet.keys); } resolve(wallet); } catch (e2) { console.error(`[WALLET ${ts()}] error loading mint`, mint, e2.message); mintWalletPromises2.delete(key); resolve(null); } }); mintWalletPromises2.set(key, loadPromise); return loadPromise; } // ndk/wallet/src/wallets/mint.ts function createMintCacheCallbacks2(adapter) { return { onMintInfoNeeded: async (mint) => { if (!adapter.getCacheData) return void 0; return adapter.getCacheData("wallet:mint:info", mint); }, onMintInfoLoaded: async (mint, info) => { if (!adapter.setCacheData) return; await adapter.setCacheData("wallet:mint:info", mint, info); }, onMintKeysNeeded: async (mint) => { if (!adapter.getCacheData) return void 0; return adapter.getCacheData("wallet:mint:keys", mint); }, onMintKeysLoaded: async (mint, keysets) => { if (!adapter.setCacheData) return; const keysArray = Array.from(keysets.values()); await adapter.setCacheData("wallet:mint:keys", mint, keysArray); } }; } async function getCashuWallet2(mint) { if (this.cashuWallets.has(mint)) return this.cashuWallets.get(mint); const w2 = await walletForMint2(mint, { onMintInfoNeeded: this.onMintInfoNeeded, onMintInfoLoaded: this.onMintInfoLoaded, onMintKeysNeeded: this.onMintKeysNeeded, onMintKeysLoaded: this.onMintKeysLoaded }); if (!w2) throw new Error(`unable to load wallet for mint ${mint}`); this.cashuWallets.set(mint, w2); return w2; } // ndk/wallet/src/wallets/index.ts var NDKWalletStatus2 = /* @__PURE__ */ ((NDKWalletStatus3) => { NDKWalletStatus3["INITIAL"] = "initial"; NDKWalletStatus3["LOADING"] = "loading"; NDKWalletStatus3["READY"] = "ready"; NDKWalletStatus3["FAILED"] = "failed"; return NDKWalletStatus3; })(NDKWalletStatus2 || {}); var NDKWallet2 = class extends import_tseep23.EventEmitter { constructor(ndk) { super(); __publicField(this, "cashuWallets", /* @__PURE__ */ new Map()); __publicField(this, "onMintInfoNeeded"); __publicField(this, "onMintInfoLoaded"); __publicField(this, "onMintKeysNeeded"); __publicField(this, "onMintKeysLoaded"); __publicField(this, "getCashuWallet", getCashuWallet2.bind(this)); __publicField(this, "ndk"); __publicField(this, "status", "initial" /* INITIAL */); /** * An ID of this wallet */ __publicField(this, "walletId", "unknown"); this.ndk = ndk; } get type() { throw new Error("Not implemented"); } /** * Get the balance of this wallet */ get balance() { throw new Error("Not implemented"); } /** * Fetch transaction history */ async fetchTransactions() { return []; } /** * Subscribe to transaction updates. * - Cashu: real-time via relay subscription * - NWC: polls slowly + triggers on wallet activity */ subscribeTransactions(_callback) { return () => { }; } /** * Redeem a set of nutzaps into an NWC wallet. * * This function gets an invoice from the NWC wallet until the total amount of the nutzaps is enough to pay for the invoice * when accounting for fees. * * @param cashuWallet - The cashu wallet to redeem the nutzaps into * @param nutzapIds - The IDs of the nutzaps to redeem * @param proofs - The proofs to redeem * @param privkey - The private key needed to redeem p2pk proofs. */ redeemNutzaps(_nutzaps, _privkey, _opts) { throw new Error("Not implemented"); } }; // ndk/wallet/src/wallets/cashu/deposit.ts var import_debug40 = __toESM(require_browser(), 1); var import_tseep24 = __toESM(require_lib(), 1); // ndk/wallet/src/wallets/cashu/quote.ts init_dist(); // ndk/wallet/src/utils/ln.ts var import_light_bolt11_decoder5 = __toESM(require_bolt11(), 1); function getBolt11ExpiresAt2(bolt11) { const decoded = (0, import_light_bolt11_decoder5.decode)(bolt11); const expiry = decoded.expiry; const timestamp = decoded.sections.find((section) => section.name === "timestamp").value; if (typeof expiry === "number" && typeof timestamp === "number") { return expiry + timestamp; } return void 0; } function getBolt11Amount2(bolt11) { const decoded = (0, import_light_bolt11_decoder5.decode)(bolt11); const section = decoded.sections.find((section2) => section2.name === "amount"); const val = section?.value; return Number(val); } function getBolt11Description2(bolt11) { const decoded = (0, import_light_bolt11_decoder5.decode)(bolt11); const section = decoded.sections.find((section2) => section2.name === "description"); const val = section?.value; return val; } // ndk/wallet/src/wallets/cashu/quote.ts var _NDKCashuQuote = class _NDKCashuQuote extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); __publicField(this, "quoteId"); __publicField(this, "mint"); __publicField(this, "amount"); __publicField(this, "unit"); __publicField(this, "_wallet"); this.kind ?? (this.kind = NDKKind2.CashuQuote); } static async from(event) { const quote = new _NDKCashuQuote(event.ndk, event); const original = event; try { await quote.decrypt(); } catch { quote.content = original.content; } try { const content = JSON.parse(quote.content); quote.quoteId = content.quoteId; quote.mint = content.mint; quote.amount = content.amount; quote.unit = content.unit; } catch (_e2) { return; } return quote; } set wallet(wallet) { this._wallet = wallet; } set invoice(invoice) { const bolt11Expiry = getBolt11ExpiresAt2(invoice); if (bolt11Expiry) this.tags.push(["expiration", bolt11Expiry.toString()]); } async save() { if (!this.ndk) throw new Error("NDK is required"); this.content = JSON.stringify({ quoteId: this.quoteId, mint: this.mint, amount: this.amount, unit: this.unit }); await this.encrypt(this.ndk.activeUser, void 0, "nip44"); await this.sign(); await this.publish(this._wallet?.relaySet); } }; __publicField(_NDKCashuQuote, "kind", NDKKind2.CashuQuote); var NDKCashuQuote2 = _NDKCashuQuote; // ndk/wallet/src/wallets/cashu/wallet/txs.ts init_dist(); async function createOutTxEvent2(ndk, paymentRequest, paymentResult, relaySet, { nutzaps } = {}) { let description = paymentRequest.paymentDescription; let amount; if (paymentRequest.pr) { amount = getBolt11Amount2(paymentRequest.pr); description ?? (description = getBolt11Description2(paymentRequest.pr)); if (amount) amount /= 1e3; } else { amount = paymentRequest.amount; } if (!amount) { console.error("BUG: Unable to find amount for paymentRequest", paymentRequest); } const txEvent = new NDKCashuWalletTx2(ndk); txEvent.direction = "out"; txEvent.amount = amount ?? 0; txEvent.mint = paymentResult.mint; txEvent.description = description; if (paymentResult.fee) txEvent.fee = paymentResult.fee; if (paymentRequest.target) { txEvent.tags.push(paymentRequest.target.tagReference()); if (!(paymentRequest.target instanceof NDKUser2)) { txEvent.tags.push(["p", paymentRequest.target.pubkey]); } } if (nutzaps) { txEvent.description ?? (txEvent.description = "nutzap redeem"); for (const nutzap of nutzaps) txEvent.addRedeemedNutzap(nutzap); } if (paymentResult.stateUpdate?.created) txEvent.createdTokens = [paymentResult.stateUpdate.created]; if (paymentResult.stateUpdate?.deleted) txEvent.destroyedTokenIds = paymentResult.stateUpdate.deleted; if (paymentResult.stateUpdate?.reserved) txEvent.reservedTokens = [paymentResult.stateUpdate.reserved]; await txEvent.sign(); txEvent.publish(relaySet); return txEvent; } async function createInTxEvent2(ndk, proofs, mint, updateStateResult, { nutzaps, fee, description }, relaySet) { const txEvent = new NDKCashuWalletTx2(ndk); const amount = proofsTotalBalance2(proofs); txEvent.direction = "in"; txEvent.amount = amount; txEvent.mint = mint; txEvent.description = description; if (updateStateResult.created) txEvent.createdTokens = [updateStateResult.created]; if (updateStateResult.deleted) txEvent.destroyedTokenIds = updateStateResult.deleted; if (updateStateResult.reserved) txEvent.reservedTokens = [updateStateResult.reserved]; if (nutzaps) for (const nutzap of nutzaps) txEvent.addRedeemedNutzap(nutzap); if (fee) txEvent.fee = fee; await txEvent.sign(); txEvent.publish(relaySet); return txEvent; } // ndk/wallet/src/wallets/cashu/deposit.ts var d14 = (0, import_debug40.default)("ndk-wallet:cashu:deposit"); function randomMint2(wallet) { const mints = wallet.mints; const mint = mints[Math.floor(Math.random() * mints.length)]; return mint; } var NDKCashuDeposit2 = class _NDKCashuDeposit2 extends import_tseep24.EventEmitter { constructor(wallet, amount, mint) { super(); __publicField(this, "mint"); __publicField(this, "amount"); __publicField(this, "quoteId"); __publicField(this, "wallet"); __publicField(this, "checkTimeout"); __publicField(this, "checkIntervalLength", 2500); __publicField(this, "finalized", false); __publicField(this, "quoteEvent"); this.wallet = wallet; this.mint = mint || randomMint2(wallet); this.amount = amount; } static fromQuoteEvent(wallet, quote) { if (!quote.amount) throw new Error("quote has no amount"); if (!quote.mint) throw new Error("quote has no mint"); const deposit = new _NDKCashuDeposit2(wallet, quote.amount, quote.mint); deposit.quoteId = quote.quoteId; return deposit; } /** * Creates a quote ID and start monitoring for payment. * * Once a payment is received, the deposit will emit a "success" event. * * @param pollTime - time in milliseconds between checks * @returns */ async start(pollTime = 2500) { const cashuWallet = await this.wallet.getCashuWallet(this.mint); const quote = await cashuWallet.createMintQuote(this.amount); d14("created quote %s for %d %s", quote.quote, this.amount, this.mint); this.quoteId = quote.quote; this.wallet.depositMonitor.addDeposit(this); setTimeout(this.check.bind(this, pollTime), pollTime); this.createQuoteEvent(quote.quote, quote.request).then((event) => this.quoteEvent = event); return quote.request; } /** * This generates a 7374 event containing the quote ID * with an optional expiration set to the bolt11 expiry (if there is one) */ async createQuoteEvent(quoteId, bolt11) { const { ndk } = this.wallet; const quoteEvent = new NDKCashuQuote2(ndk); quoteEvent.quoteId = quoteId; quoteEvent.mint = this.mint; quoteEvent.amount = this.amount; quoteEvent.wallet = this.wallet; quoteEvent.invoice = bolt11; try { await quoteEvent.save(); d14("saved quote on event %s", quoteEvent.rawEvent()); } catch (e2) { d14("error saving quote on event %s", e2.relayErrors); } return quoteEvent; } async runCheck() { if (!this.finalized) await this.finalize(); if (!this.finalized) this.delayCheck(); } delayCheck() { setTimeout(() => { this.runCheck(); this.checkIntervalLength += 500; }, this.checkIntervalLength); } /** * Check if the deposit has been finalized. * @param timeout A timeout in milliseconds to wait before giving up. */ async check(timeout) { this.runCheck(); if (timeout) { setTimeout(() => { clearTimeout(this.checkTimeout); }, timeout); } } async finalize() { if (!this.quoteId) throw new Error("No quoteId set."); let proofs; try { d14("Checking for minting status of %s", this.quoteId); const cashuWallet = await this.wallet.getCashuWallet(this.mint); const proofsWeHave = await this.wallet.state.getProofs({ mint: this.mint }); proofs = await cashuWallet.mintProofs(this.amount, this.quoteId, { proofsWeHave }); if (proofs.length === 0) return; } catch (e2) { if (e2.message.match(/not paid/i)) return; if (e2.message.match(/already issued/i)) { d14("Mint is saying the quote has already been issued, destroying quote event: %s", e2.message); this.destroyQuoteEvent(); this.finalized = true; return; } if (e2.message.match(/rate limit/i)) { d14("Mint seems to be rate limiting, lowering check interval"); this.checkIntervalLength += 5e3; return; } d14(e2.message); return; } try { this.finalized = true; const updateRes = await this.wallet.state.update( { store: proofs, mint: this.mint }, "Deposit" ); const tokenEvent = updateRes.created; if (!tokenEvent) throw new Error("no token event created"); createInTxEvent2( this.wallet.ndk, proofs, this.mint, updateRes, { description: "Deposit" }, this.wallet.relaySet ); this.emit("success", tokenEvent); this.destroyQuoteEvent(); } catch (e2) { this.emit("error", e2.message); console.error(e2); } } async destroyQuoteEvent() { if (!this.quoteEvent) return; const deleteEvent = await this.quoteEvent.delete(void 0, false); deleteEvent.publish(this.wallet.relaySet); } }; // ndk/wallet/src/wallets/cashu/deposit-monitor.ts var import_tseep25 = __toESM(require_lib(), 1); var NDKCashuDepositMonitor2 = class extends import_tseep25.EventEmitter { constructor() { super(...arguments); __publicField(this, "deposits", /* @__PURE__ */ new Map()); } addDeposit(deposit) { const { quoteId } = deposit; if (!quoteId) throw new Error("deposit has no quote ID"); if (this.deposits.has(quoteId)) return false; deposit.once("success", (_token) => { this.removeDeposit(quoteId); }); this.deposits.set(quoteId, deposit); this.emit("change"); return true; } removeDeposit(quoteId) { this.deposits.delete(quoteId); this.emit("change"); } }; // ndk/wallet/src/wallets/cashu/event-handlers/index.ts init_dist(); // ndk/wallet/src/wallets/cashu/event-handlers/deletion.ts async function handleEventDeletion2(event) { const deletedIds = event.getMatchingTags("e").map((tag) => tag[1]); for (const deletedId of deletedIds) { this.state.removeTokenId(deletedId); } } // ndk/wallet/src/wallets/cashu/event-handlers/quote.ts async function handleQuote2(event) { const quote = await NDKCashuQuote2.from(event); if (!quote) return; const oneHourAgo = Date.now() / 1e3 - 3600; if (event.created_at && event.created_at < oneHourAgo) { return; } const deposit = NDKCashuDeposit2.fromQuoteEvent(this, quote); if (this.depositMonitor.addDeposit(deposit)) { deposit.finalize(); } } // ndk/wallet/src/wallets/cashu/event-handlers/token.ts init_dist(); var _cumulativeTime = 0; var _cumulativeCalls = 0; async function handleToken2(event) { if (this.state.tokens.has(event.id)) return; const startTime = Date.now(); const token = await NDKCashuToken2.from(event); if (!token) { _cumulativeTime += Date.now() - startTime; _cumulativeCalls++; return; } _cumulativeTime += Date.now() - startTime; _cumulativeCalls++; for (const deletedTokenId of token.deletedTokens) { this.state.removeTokenId(deletedTokenId); } this.state.addToken(token); } setInterval(() => { }, 5e3); // ndk/wallet/src/wallets/cashu/event-handlers/index.ts var handlers2 = { [NDKKind2.CashuToken]: handleToken2, [NDKKind2.CashuQuote]: handleQuote2, [NDKKind2.EventDeletion]: handleEventDeletion2 }; var balanceUpdateTimer2 = null; async function eventHandler2(event) { const handler = handlers2[event.kind]; if (handler) { if (balanceUpdateTimer2) clearTimeout(balanceUpdateTimer2); await handler.call(this, event); balanceUpdateTimer2 = setTimeout(() => { this.emit("balance_updated"); }, 100); } } async function eventDupHandler2(_event, _relay, _timeSinceFirstSeen, _sub, _fromCache) { } // ndk/wallet/src/wallets/cashu/validate.ts init_cashu_ts_es(); var import_debug41 = __toESM(require_browser(), 1); var d15 = (0, import_debug41.default)("ndk-wallet:cashu:validate"); async function consolidateTokens2() { d15("checking %d tokens for spent proofs", this.state.tokens.size); const mints = new Set( this.state.getMintsProofs({ validStates: /* @__PURE__ */ new Set(["available", "reserved", "deleted"]) }).keys() ); d15("found %d mints", mints.size); mints.forEach((mint) => { consolidateMintTokens2(mint, this); }); } async function consolidateMintTokens2(mint, wallet, allProofs, onResult, onFailure) { allProofs ?? (allProofs = wallet.state.getProofs({ mint, includeDeleted: true, onlyAvailable: false })); const _wallet = await walletForMint2(mint); if (!_wallet) { return; } let proofStates = []; try { proofStates = await _wallet.checkProofsStates(allProofs); } catch (e2) { onFailure?.(e2.message); return; } const spentProofs = []; const unspentProofs = []; const pendingProofs = []; allProofs.forEach((proof, index) => { const { state } = proofStates[index]; if (state === as.SPENT) { spentProofs.push(proof); } else if (state === as.UNSPENT) { unspentProofs.push(proof); } else { pendingProofs.push(proof); } }); const walletChange = { mint, store: unspentProofs, destroy: spentProofs }; onResult?.(walletChange); const _totalSpentProofs = spentProofs.reduce((acc, proof) => acc + proof.amount, 0); if (walletChange.destroy?.length === 0) return; walletChange.store?.push(...pendingProofs); const totalPendingProofs = pendingProofs.reduce((acc, proof) => acc + proof.amount, 0); wallet.state.reserveProofs(pendingProofs, totalPendingProofs); return wallet.state.update(walletChange, "Consolidate"); } // ndk/wallet/src/wallets/cashu/pay/ln.ts init_cashu_ts_es(); // ndk/wallet/src/wallets/cashu/wallet/fee.ts function calculateFee2(intendedAmount, providedProofs, returnedProofs) { const totalProvided = providedProofs.reduce((acc, p5) => acc + p5.amount, 0); const totalReturned = returnedProofs.reduce((acc, p5) => acc + p5.amount, 0); const totalFee = totalProvided - intendedAmount - totalReturned; if (totalFee < 0) { throw new Error("Invalid fee calculation: received more proofs than sent to mint"); } return totalFee; } // ndk/wallet/src/wallets/cashu/wallet/effect.ts async function withProofReserve2(wallet, cashuWallet, mint, amountWithFees, amountWithoutFees, cb) { cashuWallet ?? (cashuWallet = await wallet.getCashuWallet(mint)); const availableMintProofs = wallet.state.getProofs({ mint, onlyAvailable: true }); const proofs = cashuWallet.selectProofsToSend(availableMintProofs, amountWithFees); const fetchedAmount = proofs.send.reduce((a, b) => a + b.amount, 0); if (fetchedAmount < amountWithFees) return null; wallet.state.reserveProofs(proofs.send, amountWithFees); let cbResult = null; let proofsChange = null; let updateRes = null; try { cbResult = await cb(proofs.send, availableMintProofs); if (!cbResult) return null; proofsChange = { mint, store: cbResult.change, destroy: proofs.send }; updateRes = await wallet.state.update(proofsChange); } catch (e2) { wallet.state.unreserveProofs(proofs.send, amountWithFees, "available"); throw e2; } if (!cbResult) return null; return { result: cbResult.result, proofsChange, stateUpdate: updateRes, mint, fee: calculateFee2(amountWithoutFees, proofs.send, cbResult.change) }; } // ndk/wallet/src/wallets/cashu/pay/ln.ts async function payLn2(wallet, pr2, { amount, unit } = {}) { let invoiceAmount = getBolt11Amount2(pr2); if (!invoiceAmount) throw new Error("invoice amount is required"); invoiceAmount = invoiceAmount / 1e3; if (amount && unit) { if (unit === "msat") { amount = amount / 1e3; } } const eligibleMints = wallet.getMintsWithBalance(invoiceAmount + 3); if (!eligibleMints.length) { return null; } for (const mint of eligibleMints) { try { const result = await executePayment2(mint, pr2, amount ?? invoiceAmount, wallet); if (result) { if (amount) { result.fee = calculateFee2( amount, result.proofsChange?.destroy ?? [], result.proofsChange?.store ?? [] ); } return result; } } catch (error) { wallet.warn(`Failed to execute payment with min ${mint}: ${error}`); } } return null; } async function executePayment2(mint, pr2, amountWithoutFees, wallet) { const cashuWallet = await wallet.getCashuWallet(mint); try { const meltQuote = await cashuWallet.createMeltQuote(pr2); const amountToSend = meltQuote.amount + meltQuote.fee_reserve; const result = await withProofReserve2( wallet, cashuWallet, mint, amountToSend, amountWithoutFees, async (proofsToUse, _allOurProofs) => { const meltResult = await cashuWallet.meltProofs(meltQuote, proofsToUse); if (meltResult.quote.state === et.PAID) { return { result: { preimage: meltResult.quote.payment_preimage ?? "" }, change: meltResult.change }; } return null; } ); return result; } catch (e2) { if (e2 instanceof Error) { if (e2.message.match(/already spent/i)) { setTimeout(() => { consolidateMintTokens2(mint, wallet); }, 2500); } else { throw e2; } } return null; } } // ndk/wallet/src/wallets/cashu/pay/nut.ts init_dist(); // ndk/wallet/src/utils/cashu.ts function ensureIsCashuPubkey2(pubkey) { if (!pubkey) return; let _pubkey = pubkey; if (_pubkey.length === 64) _pubkey = `02${_pubkey}`; if (_pubkey.length !== 66) throw new Error("Invalid pubkey"); return _pubkey; } async function mintProofs2(wallet, quote, amount, mint, p2pk, proofTags) { const mintTokenAttempt = (resolve, reject, attempt) => { const pubkey = ensureIsCashuPubkey2(p2pk); wallet.mintProofs(amount, quote.quote, { pubkey, ...proofTags && proofTags.length > 0 ? { tags: proofTags } : {} }).then((mintProofs3) => { console.debug("minted tokens", mintProofs3); resolve({ proofs: mintProofs3, mint }); }).catch((e2) => { attempt++; if (attempt <= 3) { console.error("error minting tokens", e2); setTimeout(() => mintTokenAttempt(resolve, reject, attempt), attempt * 1500); } else { reject(e2); } }); }; return new Promise((resolve, reject) => { mintTokenAttempt(resolve, reject, 0); }); } // ndk/wallet/src/wallets/cashu/pay/nut.ts async function createToken2(wallet, amount, recipientMints, p2pk, proofTags) { console.log("[createToken] Starting token creation", { amount, recipientMints, p2pk }); p2pk = ensureIsCashuPubkey2(p2pk); const myMintsWithEnoughBalance = wallet.getMintsWithBalance(amount); console.log("[createToken] My mints with enough balance", myMintsWithEnoughBalance); const hasRecipientMints = recipientMints && recipientMints.length > 0; const mintsInCommon = hasRecipientMints ? findMintsInCommon2([recipientMints, myMintsWithEnoughBalance]) : myMintsWithEnoughBalance; console.log("[createToken] Mints in common", { hasRecipientMints, mintsInCommon }); for (const mint of mintsInCommon) { console.log("[createToken] Attempting to create token in mint", mint); try { const res = await createTokenInMint2(wallet, mint, amount, p2pk, proofTags); if (res) { console.log("[createToken] Successfully created token in mint", mint); return res; } console.log("[createToken] Failed to create token in mint", mint); } catch (e2) { console.error("[createToken] Error creating token in mint", mint, e2); } } if (hasRecipientMints) { console.log("[createToken] Attempting cross-mint transfer"); return await createTokenWithMintTransfer2(wallet, amount, recipientMints, p2pk, proofTags); } console.error("[createToken] All token creation attempts failed"); return null; } async function createTokenInMint2(wallet, mint, amount, p2pk, proofTags) { console.log("[createTokenInMint] Starting", { mint, amount, p2pk }); const cashuWallet = await wallet.getCashuWallet(mint); console.log("[createTokenInMint] Got cashu wallet for mint", mint); try { const result = await withProofReserve2( wallet, cashuWallet, mint, amount, amount, async (proofsToUse, allOurProofs) => { console.log("[createTokenInMint] Inside withProofReserve callback", { proofsToUseCount: proofsToUse.length, allOurProofsCount: allOurProofs.length }); const sendResult = await cashuWallet.send(amount, proofsToUse, { pubkey: p2pk, proofsWeHave: allOurProofs, ...proofTags && proofTags.length > 0 ? { tags: proofTags } : {} }); console.log("[createTokenInMint] Send result", { sendCount: sendResult.send.length, keepCount: sendResult.keep.length }); return { result: { proofs: sendResult.send, mint }, change: sendResult.keep, mint }; } ); console.log("[createTokenInMint] Success", result); return result; } catch (e2) { console.error("[createTokenInMint] Error", { mint, error: e2.message, stack: e2.stack }); } return null; } async function createTokenWithMintTransfer2(wallet, amount, recipientMints, p2pk, proofTags) { const generateQuote = async () => { const generateQuoteFromSomeMint = async (mint3) => { const targetMintWallet3 = await walletForMint2(mint3); if (!targetMintWallet3) throw new Error(`unable to load wallet for mint ${mint3}`); const quote3 = await targetMintWallet3.createMintQuote(amount); return { quote: quote3, mint: mint3, targetMintWallet: targetMintWallet3 }; }; const quotesPromises = recipientMints.map(generateQuoteFromSomeMint); const { quote: quote2, mint: mint2, targetMintWallet: targetMintWallet2 } = await Promise.any(quotesPromises); if (!quote2) { throw new Error("failed to get quote from any mint"); } return { quote: quote2, mint: mint2, targetMintWallet: targetMintWallet2 }; }; const { quote, mint: targetMint, targetMintWallet } = await generateQuote(); if (!quote) { return null; } const invoiceAmount = getBolt11Amount2(quote.request); if (!invoiceAmount) throw new Error("invoice amount is required"); const invoiceAmountInSat = invoiceAmount / 1e3; if (invoiceAmountInSat > amount) throw new Error(`invoice amount is more than the amount passed in (${invoiceAmountInSat} vs ${amount})`); const payLNResult = await payLn2(wallet, quote.request, { amount }); if (!payLNResult) { return null; } const { proofs, mint } = await mintProofs2(targetMintWallet, quote, amount, targetMint, p2pk, proofTags); return { ...payLNResult, result: { proofs, mint }, fee: payLNResult.fee }; } function findMintsInCommon2(mintCollections) { const mintCounts = /* @__PURE__ */ new Map(); for (const mints of mintCollections) { for (const mint of mints) { const normalizedMint = normalizeUrl2(mint); if (!mintCounts.has(normalizedMint)) { mintCounts.set(normalizedMint, 1); } else { mintCounts.set(normalizedMint, mintCounts.get(normalizedMint) + 1); } } } const commonMints = []; for (const [mint, count] of mintCounts.entries()) { if (count === mintCollections.length) { commonMints.push(mint); } } return commonMints; } // ndk/wallet/src/wallets/cashu/wallet/payment.ts var PaymentHandler2 = class { constructor(wallet) { __publicField(this, "wallet"); this.wallet = wallet; } /** * Pay a LN invoice with this wallet. This will used cashu proofs to pay a bolt11. */ async lnPay(payment, createTxEvent = true) { if (!payment.pr) throw new Error("pr is required"); const invoiceAmount = getBolt11Amount2(payment.pr); if (!invoiceAmount) throw new Error("invoice amount is required"); if (payment.amount && invoiceAmount > payment.amount) { throw new Error("invoice amount is more than the amount passed in"); } const res = await payLn2(this.wallet, payment.pr, { amount: payment.amount, unit: payment.unit }); if (!res?.result?.preimage) return; if (createTxEvent) { createOutTxEvent2(this.wallet.ndk, payment, res, this.wallet.relaySet); } return res.result; } /** * Swaps tokens to a specific amount, optionally locking to a p2pk. */ async cashuPay(payment) { console.log("[PaymentHandler.cashuPay] Starting cashu payment", { originalAmount: payment.amount, unit: payment.unit, mints: payment.mints, p2pk: payment.p2pk, allowIntramintFallback: payment.allowIntramintFallback }); const satPayment = { ...payment }; if (satPayment.unit?.startsWith("msat")) { satPayment.amount = satPayment.amount / 1e3; satPayment.unit = "sat"; console.log("[PaymentHandler.cashuPay] Converted msat to sat", { newAmount: satPayment.amount, newUnit: satPayment.unit }); } console.log("[PaymentHandler.cashuPay] Creating token with mints", payment.mints); let createResult = await createToken2(this.wallet, satPayment.amount, payment.mints, payment.p2pk, payment.proofTags); if (!createResult?.result) { console.log("[PaymentHandler.cashuPay] Token creation failed with specified mints"); if (payment.allowIntramintFallback) { console.log("[PaymentHandler.cashuPay] Attempting intramint fallback"); createResult = await createToken2(this.wallet, satPayment.amount, void 0, payment.p2pk, payment.proofTags); } if (!createResult?.result) { console.error("[PaymentHandler.cashuPay] Token creation failed completely"); return; } } console.log("[PaymentHandler.cashuPay] Token created successfully", { proofsCount: createResult.result.proofs.length, mint: createResult.result.mint }); createOutTxEvent2(this.wallet.ndk, satPayment, createResult, this.wallet.relaySet); return createResult.result; } }; // ndk/wallet/src/wallets/cashu/wallet/state/balance.ts function getBalance2(opts) { const proofs = this.getProofEntries(opts); return proofs.reduce((sum, proof) => sum + proof.proof.amount, 0); } function getMintsBalances2({ onlyAvailable } = { onlyAvailable: true }) { var _a72; const balances = {}; const proofs = this.getProofEntries({ onlyAvailable }); for (const proof of proofs) { if (!proof.mint) continue; balances[_a72 = proof.mint] ?? (balances[_a72] = 0); balances[proof.mint] += proof.proof.amount; } return balances; } // ndk/wallet/src/wallets/cashu/wallet/state/proofs.ts function addProof2(proofEntry) { this.proofs.set(proofEntry.proof.C, proofEntry); this.journal.push({ memo: "Added proof", timestamp: Date.now(), metadata: { type: "proof", id: proofEntry.proof.C, amount: proofEntry.proof.amount, mint: proofEntry.mint } }); } function reserveProofs2(proofs, amount) { for (const proof of proofs) { this.updateProof(proof, { state: "reserved" }); } this.reserveAmounts.push(amount); } function unreserveProofs2(proofs, amount, newState) { for (const proof of proofs) { this.updateProof(proof, { state: newState }); } const index = this.reserveAmounts.indexOf(amount); if (index !== -1) { this.reserveAmounts.splice(index, 1); } else { throw new Error(`BUG: Amount ${amount} not found in reserveAmounts`); } } function getProofEntries2(opts = {}) { const proofs = /* @__PURE__ */ new Map(); const validStates = /* @__PURE__ */ new Set(["available"]); let { mint, onlyAvailable, includeDeleted } = opts; onlyAvailable ?? (onlyAvailable = true); if (!onlyAvailable) validStates.add("reserved"); if (includeDeleted) validStates.add("deleted"); for (const proofEntry of this.proofs.values()) { if (mint && proofEntry.mint !== mint) continue; if (!validStates.has(proofEntry.state)) continue; if (!proofEntry.proof) continue; proofs.set(proofEntry.proof.C, proofEntry); } return Array.from(proofs.values()); } function updateProof2(proof, state) { const proofC = proof.C; const currentState = this.proofs.get(proofC); if (!currentState) throw new Error("Proof not found"); const newState = { ...currentState, ...state }; this.proofs.set(proofC, newState); this.journal.push({ memo: `Updated proof state: ${JSON.stringify(state)}`, timestamp: Date.now(), metadata: { type: "proof", id: proofC, amount: proof.amount, mint: currentState.mint } }); } // ndk/wallet/src/wallets/cashu/wallet/state/token.ts function addToken2(token) { if (!token.mint) throw new Error("BUG: Token has no mint"); const currentEntry = this.tokens.get(token.id); const state = currentEntry?.state ?? "available"; this.tokens.set(token.id, { token, state }); let _added = 0; let _invalid = 0; for (const proof of token.proofs) { const val = maybeAssociateProofWithToken2(this, proof, token, state); if (val === false) { _invalid++; } else { _added++; } } } function maybeAssociateProofWithToken2(walletState, proof, token, state) { const proofC = proof.C; const proofEntry = walletState.proofs.get(proofC); if (!proofEntry) { walletState.addProof({ mint: token.mint, state, tokenId: token.id, timestamp: token.created_at, proof }); return true; } if (proofEntry.tokenId) { if (proofEntry.tokenId === token.id) { return null; } const existingTokenEntry = walletState.tokens.get(proofEntry.tokenId); if (!existingTokenEntry) { throw new Error( `BUG: Token id ${proofEntry.tokenId} not found, was expected to be associated with proof ${proofC}` ); } const existingToken = existingTokenEntry.token; if (existingToken) { if (existingToken.created_at && (!token.created_at || token.created_at < existingToken.created_at)) { return false; } } walletState.updateProof(proof, { tokenId: token.id, state }); return true; } walletState.updateProof(proof, { tokenId: token.id, state }); return true; } function removeTokenId2(tokenId) { const currentEntry = this.tokens.get(tokenId) || {}; this.tokens.set(tokenId, { ...currentEntry, state: "deleted" }); for (const proofEntry of this.proofs.values()) { const { proof } = proofEntry; if (proofEntry.tokenId === tokenId) { if (!proof) { throw new Error("BUG: Proof entry has no proof"); } this.updateProof(proof, { state: "deleted" }); } } } // ndk/wallet/src/wallets/cashu/wallet/state/update.ts init_dist(); async function update2(stateChange, _memo) { updateInternalState2(this, stateChange); this.wallet.emit("balance_updated"); return updateExternalState2(this, stateChange); } function updateInternalState2(walletState, stateChange) { if (stateChange.store && stateChange.store.length > 0) { for (const proof of stateChange.store) { walletState.addProof({ mint: stateChange.mint, state: "available", proof, timestamp: Date.now() }); } } if (stateChange.destroy && stateChange.destroy.length > 0) { for (const proof of stateChange.destroy) { walletState.updateProof(proof, { state: "deleted" }); } } if (stateChange.reserve && stateChange.reserve.length > 0) { throw new Error("BUG: Proofs should not be reserved via update"); } } async function updateExternalState2(walletState, stateChange) { const newState = calculateNewState2(walletState, stateChange); if (newState.deletedTokenIds.size > 0) { const deleteEvent = new NDKEvent2(walletState.wallet.ndk, { kind: NDKKind2.EventDeletion, tags: [ ["k", NDKKind2.CashuToken.toString()], ...Array.from(newState.deletedTokenIds).map((id) => ["e", id]) ] }); await deleteEvent.sign(); publishWithRetry2(walletState, deleteEvent, walletState.wallet.relaySet); for (const tokenId of newState.deletedTokenIds) { walletState.removeTokenId(tokenId); } } const res = {}; if (newState.saveProofs.length > 0) { const newToken = await createTokenEvent2(walletState, stateChange.mint, newState); res.created = newToken; } return res; } async function publishWithRetry2(walletState, event, relaySet, retryTimeout = 10 * 1e3) { let publishResult; publishResult = await event.publish(relaySet); let type; if (event.kind === NDKKind2.EventDeletion) type = "deletion"; if (event.kind === NDKKind2.CashuToken) type = "token"; if (event.kind === NDKKind2.CashuWallet) type = "wallet"; const journalEntryMetadata = { type, id: event.id, relayUrl: relaySet?.relayUrls.join(",") }; if (publishResult) { walletState.journal.push({ memo: `Publish kind:${event.kind} succeesfully`, timestamp: Date.now(), metadata: journalEntryMetadata }); return publishResult; } walletState.journal.push({ memo: "Publish failed", timestamp: Date.now(), metadata: journalEntryMetadata }); setTimeout(() => { publishWithRetry2(walletState, event, relaySet, retryTimeout); }, retryTimeout); } async function createTokenEvent2(walletState, mint, newState) { const newToken = new NDKCashuToken2(walletState.wallet.ndk); newToken.mint = mint; newToken.proofs = newState.saveProofs; await newToken.toNostrEvent(); walletState.addToken(newToken); newToken.deletedTokens = Array.from(newState.deletedTokenIds); await newToken.sign(); walletState.addToken(newToken); publishWithRetry2(walletState, newToken, walletState.wallet.relaySet); return newToken; } function calculateNewState2(walletState, stateChange) { const destroyProofs = /* @__PURE__ */ new Set(); for (const proof of stateChange.destroy || []) destroyProofs.add(proof.C); const proofsToStore = /* @__PURE__ */ new Map(); let tokensToDelete; for (const proof of stateChange.store || []) proofsToStore.set(proof.C, proof); tokensToDelete = getAffectedTokens2(walletState, stateChange); for (const token of tokensToDelete.values()) { for (const proof of token.proofs) { if (destroyProofs.has(proof.C)) continue; proofsToStore.set(proof.C, proof); } } return { deletedTokenIds: new Set(tokensToDelete.keys()), deletedProofs: destroyProofs, reserveProofs: [], saveProofs: Array.from(proofsToStore.values()) }; } function getAffectedTokens2(walletState, stateChange) { const tokens = /* @__PURE__ */ new Map(); for (const proof of stateChange.destroy || []) { const proofEntry = walletState.proofs.get(proof.C); if (!proofEntry) { continue; } const tokenId = proofEntry.tokenId; if (!tokenId) { continue; } const tokenEntry = walletState.tokens.get(tokenId); if (!tokenEntry?.token) { continue; } tokens.set(tokenId, tokenEntry.token); } return tokens; } // ndk/wallet/src/wallets/cashu/wallet/state/index.ts var WalletState2 = class { constructor(wallet, reservedProofCs = /* @__PURE__ */ new Set()) { this.wallet = wallet; this.reservedProofCs = reservedProofCs; /** * the amounts that are intended to be reserved * this is the net amount we are trying to pay out, * excluding fees and coin sizes * e.g. we might want to pay 5 sats, have 2 sats in fees * and we're using 2 inputs that add up to 8, the reserve amount is 5 * while the reserve proofs add up to 8 */ __publicField(this, "reserveAmounts", []); /** * Source of truth of the proofs this wallet has/had. */ __publicField(this, "proofs", /* @__PURE__ */ new Map()); /** * The tokens that are known to this wallet. */ __publicField(this, "tokens", /* @__PURE__ */ new Map()); __publicField(this, "journal", []); /*************************** * Tokens ***************************/ __publicField(this, "addToken", addToken2.bind(this)); __publicField(this, "removeTokenId", removeTokenId2.bind(this)); /*************************** * Proof management ***************************/ __publicField(this, "addProof", addProof2.bind(this)); /** * Reserves a number of selected proofs and a specific amount. * * The amount and total of the proofs don't need to match. We * might want to use 5 sats and have 2 proofs of 4 sats each. * In that case, the reserve amount is 5, while the reserve proofs * add up to 8. */ __publicField(this, "reserveProofs", reserveProofs2.bind(this)); /** * Unreserves a number of selected proofs and a specific amount. */ __publicField(this, "unreserveProofs", unreserveProofs2.bind(this)); /** * Returns all proof entries, optionally filtered by mint and state */ __publicField(this, "getProofEntries", getProofEntries2.bind(this)); /** * Updates information about a proof */ __publicField(this, "updateProof", updateProof2.bind(this)); /*************************** * Balance ***************************/ /** * Returns the balance of the wallet, optionally filtered by mint and state * * @params opts.mint - optional mint to filter by * @params opts.onlyAvailable - only include available proofs @default true */ __publicField(this, "getBalance", getBalance2.bind(this)); /** * Returns the balances of the different mints * * @params opts.onlyAvailable - only include available proofs @default true */ __publicField(this, "getMintsBalance", getMintsBalances2.bind(this)); /*************************** * State update ***************************/ __publicField(this, "update", update2.bind(this)); } /** This is a debugging function that dumps the state of the wallet */ dump() { const res = { proofs: Array.from(this.proofs.values()), balances: this.getMintsBalance(), totalBalance: this.getBalance(), tokens: Array.from(this.tokens.values()) }; return res; } /** * Returns all proofs, optionally filtered by mint and state * @param opts.mint - optional mint to filter by * @param opts.onlyAvailable - only include available proofs @default true * @param opts.includeDeleted - include deleted proofs @default false */ getProofs(opts) { return this.getProofEntries(opts).map((entry) => entry.proof); } getTokens(opts = { onlyAvailable: true }) { const proofEntries = this.getProofEntries(opts); const tokens = /* @__PURE__ */ new Map(); for (const proofEntry of proofEntries) { const tokenId = proofEntry.tokenId ?? null; const current = tokens.get(tokenId) ?? { tokenId, mint: proofEntry.mint, proofEntries: [] }; current.token ?? (current.token = tokenId ? this.tokens.get(tokenId)?.token : void 0); current.proofEntries.push(proofEntry); tokens.set(tokenId, current); } return tokens; } /** * Gets a list of proofs for each mint * @returns */ getMintsProofs({ validStates = /* @__PURE__ */ new Set(["available"]) } = {}) { const mints = /* @__PURE__ */ new Map(); for (const entry of this.proofs.values()) { if (!entry.mint || !entry.proof) continue; if (!validStates.has(entry.state)) continue; const current = mints.get(entry.mint) || []; current.push(entry.proof); mints.set(entry.mint, current); } return mints; } }; // ndk/wallet/src/wallets/cashu/wallet/index.ts var _NDKCashuWallet = class _NDKCashuWallet extends NDKWallet2 { constructor(ndk) { super(ndk); __publicField(this, "_p2pk"); __publicField(this, "sub"); __publicField(this, "status", "initial" /* INITIAL */); /** * List of mint URLs configured for this wallet. * Modify directly to add/remove mints, then call publish() to save. * * @example * // Add a mint * wallet.mints = [...wallet.mints, 'https://mint.example.com']; * await wallet.publish(); * * @example * // Remove a mint * wallet.mints = wallet.mints.filter(url => url !== 'https://old-mint.com'); * await wallet.publish(); */ __publicField(this, "mints", []); __publicField(this, "privkeys", /* @__PURE__ */ new Map()); __publicField(this, "signer"); __publicField(this, "walletId", "nip-60"); __publicField(this, "depositMonitor", new NDKCashuDepositMonitor2()); /** * Warnings that have been raised */ __publicField(this, "warnings", []); __publicField(this, "paymentHandler"); __publicField(this, "state"); /** * Relay set for wallet events (kinds 7374, 7375, 7376). * Modify directly to add/remove relays, then call publish() to save. * If undefined, falls back to NIP-65 relay list. * * @example * // Set relays * wallet.relaySet = NDKRelaySet.fromRelayUrls(['wss://relay1.com', 'wss://relay2.com'], ndk); * await wallet.publish(); * * @example * // Clear relays (use NIP-65 fallback) * wallet.relaySet = undefined; * await wallet.publish(); */ __publicField(this, "relaySet"); __publicField(this, "_walletRelays", []); __publicField(this, "consolidateTokens", consolidateTokens2.bind(this)); __publicField(this, "wallets", /* @__PURE__ */ new Map()); this.ndk = ndk; this.paymentHandler = new PaymentHandler2(this); this.state = new WalletState2(this); if (ndk.cacheAdapter?.getCacheData && ndk.cacheAdapter?.setCacheData) { const callbacks = createMintCacheCallbacks2(ndk.cacheAdapter); this.onMintInfoNeeded = callbacks.onMintInfoNeeded; this.onMintInfoLoaded = callbacks.onMintInfoLoaded; this.onMintKeysNeeded = callbacks.onMintKeysNeeded; this.onMintKeysLoaded = callbacks.onMintKeysLoaded; } } get type() { return "nip-60"; } /** * Generates a backup event for this wallet */ async backup(publish = true) { if (this.privkeys.size === 0) throw new Error("no privkey to backup"); const backup = new NDKCashuWalletBackup2(this.ndk); const privkeys = []; for (const [_pubkey, signer] of this.privkeys.entries()) { privkeys.push(signer.privateKey); } backup.privkeys = privkeys; backup.mints = this.mints; if (publish) backup.save(this.relaySet); return backup; } /** * Generates nuts that can be used to send to someone. * * Note that this function does not send anything, it just generates a specific amount of proofs. * @param amounts * @returns */ async mintNuts(amounts) { let result; const totalAmount = amounts.reduce((acc, amount) => acc + amount, 0); for (const mint of this.mints) { const wallet = await this.getCashuWallet(mint); let mintProofs3 = await this.state.getProofs({ mint }); try { result = await wallet.send(totalAmount, mintProofs3, { proofsWeHave: mintProofs3, includeFees: true, outputAmounts: { sendAmounts: amounts } }); } catch (e2) { if (e2 instanceof Error && e2.message.match(/already spent/i)) { await consolidateMintTokens2(mint, this, mintProofs3); mintProofs3 = await this.state.getProofs({ mint }); result = await wallet.send(totalAmount, mintProofs3, { proofsWeHave: mintProofs3, includeFees: true, outputAmounts: { sendAmounts: amounts } }); } else { throw e2; } } if (result.send.length > 0) { const change = { store: result?.keep ?? [], destroy: mintProofs3, mint }; const updateRes = await this.state.update(change); createOutTxEvent2( this.ndk, { paymentDescription: "minted nuts", amount: amounts.reduce((acc, amount) => acc + amount, 0) }, { result: { proofs: result.send, mint }, proofsChange: change, stateUpdate: updateRes, mint, fee: 0 }, this.relaySet ); this.emit("balance_updated"); return result; } } } /** * Creates a cashu token that can be sent to someone. * This method mints the specified amount and returns an encoded token string. * * @param amount - Amount in satoshis to send * @param memo - Optional memo to include in the token * @returns Encoded cashu token string * * @example * const token = await wallet.send(1000, "Coffee payment"); * // token is a cashu token string that can be shared */ async send(amount, memo) { if (this.mints.length === 0) throw new Error("No mints configured"); const result = await this.mintNuts([amount]); if (!result) throw new Error("Failed to create token"); return es({ mint: this.mints[0], proofs: result.send, memo }); } /** * Loads a wallet information from an event * @param event */ async loadFromEvent(event) { const _event = new NDKEvent2(event.ndk, event.rawEvent()); await _event.decrypt(); const content = JSON.parse(_event.content); for (const tag of content) { if (tag[0] === "mint") { this.mints.push(tag[1]); } else if (tag[0] === "privkey") { await this.addPrivkey(tag[1]); } else if (tag[0] === "relay") { this._walletRelays.push(tag[1]); } } await this.getP2pk(); } static async from(event) { if (!event.ndk) throw new Error("no ndk instance on event"); const wallet = new _NDKCashuWallet(event.ndk); await wallet.loadFromEvent(event); return wallet; } /** * Creates a new NIP-60 wallet with the specified configuration. * Generates a private key, publishes the wallet event (kind 17375), and creates a backup (kind 375). * * @param ndk - NDK instance * @param mints - Array of mint URLs to configure * @param relays - Optional array of relay URLs for wallet events * @returns The newly created and published wallet * * @example * const wallet = await NDKCashuWallet.create( * ndk, * ['https://mint.example.com'], * ['wss://relay.example.com'] * ); */ static async create(ndk, mints, relays) { const wallet = new _NDKCashuWallet(ndk); const signer = NDKPrivateKeySigner2.generate(); await wallet.addPrivkey(signer.privateKey); wallet.mints = mints; if (relays && relays.length > 0) { wallet.relaySet = NDKRelaySet2.fromRelayUrls(relays, ndk); } await wallet.publish(); await wallet.backup(true); return wallet; } /** * Fetches relay configuration for the wallet according to NIP-60. * First tries to get relays from encrypted wallet relays, * falls back to NIP-65 (kind 10002) relays if not found. */ async fetchWalletRelays(pubkey) { if (this._walletRelays.length > 0) { return NDKRelaySet2.fromRelayUrls(this._walletRelays, this.ndk); } const relayListEvent = await this.ndk.fetchEvent( { kinds: [NDKKind2.RelayList], authors: [pubkey] }, { cacheUsage: NDKSubscriptionCacheUsage2.PARALLEL } ); if (relayListEvent) { return NDKRelayList2.from(relayListEvent).relaySet; } return void 0; } /** * Starts monitoring the wallet. * * Use `since` to start syncing state from a specific timestamp. This should be * used by storing at the app level a time in which we know we were able to communicate * with the relays, for example, by saving the time the wallet has emitted a "ready" event. */ async start(opts) { const activeUser = this.ndk?.activeUser; if (this.status === "ready" /* READY */) return Promise.resolve(); this.setStatus("loading" /* LOADING */); const pubkey = opts?.pubkey ?? activeUser?.pubkey; if (!pubkey) throw new Error("no pubkey"); if (!this.relaySet) { this.relaySet = await this.fetchWalletRelays(pubkey); } const filters = [ { kinds: [NDKKind2.CashuToken], authors: [pubkey] }, { kinds: [NDKKind2.CashuQuote], authors: [pubkey] }, { kinds: [NDKKind2.EventDeletion], authors: [pubkey], "#k": [NDKKind2.CashuToken.toString()] } ]; if (opts?.since) { filters[0].since = opts.since; filters[1].since = opts.since; filters[2].since = opts.since; } if (this.ndk.cacheAdapter) { const cacheEvents = []; const events = await this.ndk.fetchEvents([{ kinds: [NDKKind2.CashuToken], authors: [pubkey] }], { cacheUsage: NDKSubscriptionCacheUsage2.ONLY_CACHE }); cacheEvents.push(...events); for (const event of cacheEvents) { eventHandler2.call(this, event); } this.emit("balance_updated"); } if (this.ndk.cacheAdapter) { try { const syncResult = await NDKSync.sync(this.ndk, filters, { relaySet: this.relaySet, autoFetch: true }); for (const event of syncResult.events) { eventHandler2.call(this, event); } const subOpts = opts ?? {}; subOpts.subId ?? (subOpts.subId = "cashu-wallet-state"); const liveFilters = filters.map((f) => ({ ...f, since: Math.floor(Date.now() / 1e3) - 60 })); this.sub = this.ndk.subscribe(liveFilters, { ...subOpts, relaySet: this.relaySet, closeOnEose: false, onEvent: (event) => { eventHandler2.call(this, event); }, onEventDup: eventDupHandler2.bind(this) }); this.emit("ready"); this.setStatus("ready" /* READY */); } catch (error) { console.error(`[NDKCashuWallet] Sync failed, falling back to subscription:`, error); await this.startWithSubscription(filters, opts); } } else { await this.startWithSubscription(filters, opts); } } /** * Starts wallet monitoring using traditional subscription (fallback when sync unavailable) */ async startWithSubscription(filters, opts) { const subOpts = opts ?? {}; subOpts.subId ?? (subOpts.subId = "cashu-wallet-state"); return new Promise((resolve) => { this.sub = this.ndk.subscribe(filters, { ...subOpts, relaySet: this.relaySet, onEvent: (event) => { eventHandler2.call(this, event); }, onEose: async () => { this.emit("ready"); this.setStatus("ready" /* READY */); resolve(); }, onEventDup: eventDupHandler2.bind(this) }); }); } stop() { this.sub?.stop(); this.setStatus("initial" /* INITIAL */); } setStatus(status) { if (this.status !== status) { this.status = status; this.emit("status_changed", status); } } /** * Returns the p2pk of this wallet or generates a new one if we don't have one */ async getP2pk() { if (this._p2pk) return this._p2pk; if (this.privkeys.size === 0) { const signer = NDKPrivateKeySigner2.generate(); await this.addPrivkey(signer.privateKey); } return this.p2pk; } /** * If this wallet has access to more than one privkey, this will return all of them. */ get p2pks() { return Array.from(this.privkeys.keys()); } async addPrivkey(privkey) { const signer = new NDKPrivateKeySigner2(privkey); const user = await signer.user(); this.privkeys.set(user.pubkey, signer); this._p2pk ?? (this._p2pk = user.pubkey); return this._p2pk; } get p2pk() { if (!this._p2pk) throw new Error("p2pk not set"); return this._p2pk; } set p2pk(pubkey) { if (this.privkeys.has(pubkey)) { this.signer = this.privkeys.get(pubkey); this.p2pk = pubkey; } else { throw new Error(`privkey for ${pubkey} not found`); } } /** * Generates the payload for a wallet event */ walletPayload() { const privkeys = Array.from(this.privkeys.values()).map((signer) => signer.privateKey); const payload = payloadForEvent2(privkeys, this.mints); if (this._walletRelays.length > 0) { payload.push(...this._walletRelays.map((relay) => ["relay", relay])); } return payload; } /** * Publishes the wallet configuration (kind 17375) to save changes. * Call this after modifying mints or relaySet to persist the configuration. * * The wallet event contains encrypted mint URLs, private keys, and relay URLs. * * @example * // Add a mint and save * wallet.mints.push('https://mint.example.com'); * await wallet.publish(); * * @example * // Update relays and save * wallet.relaySet = NDKRelaySet.fromRelayUrls(['wss://relay.example.com'], ndk); * await wallet.publish(); */ async publish() { if (this.relaySet) { this._walletRelays = Array.from(this.relaySet.relays).map((relay) => relay.url); } const event = new NDKEvent2(this.ndk, { content: JSON.stringify(this.walletPayload()), kind: NDKKind2.CashuWallet }); const user = await this.ndk?.signer?.user(); await event.encrypt(user, void 0, "nip44"); return event.publish(this.relaySet); } /** * Publishes the CashuMintList (kind 10019) for nutzap reception. * This public event tells others which mints and relays to use when sending nutzaps. * * @example * await wallet.publishMintList(); */ async publishMintList() { const mintList = new NDKCashuMintList2(this.ndk); mintList.mints = this.mints; if (this.relaySet) { mintList.relays = Array.from(this.relaySet.relays).map((relay) => relay.url); } mintList.p2pk = this.p2pk; return mintList.publishReplaceable(this.relaySet); } /** * Updates wallet configuration (mints and relays) and publishes the changes. * Uses publishReplaceable to ensure the event replaces the previous wallet configuration. * * @param config - Configuration object with mints and optional relays * * @example * // Update mints only * await wallet.update({ mints: ['https://mint.example.com'] }); * * @example * // Update both mints and relays * await wallet.update({ * mints: ['https://mint.example.com'], * relays: ['wss://relay.example.com'] * }); */ async update(config) { this.mints = config.mints; if (config.relays && config.relays.length > 0) { this.relaySet = NDKRelaySet2.fromRelayUrls(config.relays, this.ndk); this._walletRelays = config.relays; } else { this.relaySet = void 0; this._walletRelays = []; } const event = new NDKEvent2(this.ndk, { content: JSON.stringify(this.walletPayload()), kind: NDKKind2.CashuWallet }); const user = await this.ndk?.signer?.user(); await event.encrypt(user, void 0, "nip44"); return event.publishReplaceable(this.relaySet); } /** * Prepares a deposit * @param amount * @param mint * * @example * const wallet = new NDKCashuWallet(...); * const deposit = wallet.deposit(1000, "https://mint.example.com", "sats"); * deposit.on("success", (token) => { * }); * deposit.on("error", (error) => { * }); * * // start monitoring the deposit * deposit.start(); */ deposit(amount, mint) { const deposit = new NDKCashuDeposit2(this, amount, mint); deposit.on("success", (token) => { this.state.addToken(token); }); return deposit; } /** * Receives a token and adds it to the wallet * @param token * @returns the token event that was created */ async receiveToken(token, description) { const { mint } = Me(token); const wallet = await this.getCashuWallet(mint); const proofs = await wallet.receive(token); const updateRes = await this.state.update({ store: proofs, mint }); const tokenEvent = updateRes.created; createInTxEvent2(this.ndk, proofs, mint, updateRes, { description }, this.relaySet); return tokenEvent; } /** * Pay a LN invoice with this wallet */ async lnPay(payment, createTxEvent = true) { return this.paymentHandler.lnPay(payment, createTxEvent); } /** * Swaps tokens to a specific amount, optionally locking to a p2pk. * * This function has side effects: * - It swaps tokens at the mint * - It updates the wallet state (deletes affected tokens, might create new ones) * - It creates a wallet transaction event * * This function returns the proofs that need to be sent to the recipient. * @param amount */ async cashuPay(payment) { return this.paymentHandler.cashuPay(payment); } async redeemNutzaps(nutzaps, privkey, { mint, proofs, cashuWallet }) { if (cashuWallet) { mint ?? (mint = cashuWallet.mint.mintUrl); } else { if (!mint) throw new Error("mint not set"); cashuWallet = await this.getCashuWallet(mint); } if (!mint) throw new Error("mint not set"); if (!proofs) throw new Error("proofs not set"); try { const proofsWeHave = this.state.getProofs({ mint }); const res = await cashuWallet.receive({ proofs, mint }, { proofsWeHave, privkey }); const receivedAmount = proofs.reduce((acc, proof) => acc + proof.amount, 0); const redeemedAmount = res.reduce((acc, proof) => acc + proof.amount, 0); const fee = receivedAmount - redeemedAmount; const updateRes = await this.state.update({ store: res, mint }); createInTxEvent2(this.ndk, res, mint, updateRes, { nutzaps, fee }, this.relaySet); return receivedAmount; } catch (e2) { console.error( "error redeeming nutzaps", nutzaps.map((n) => n.encode()), e2 ); throw e2; } } warn(msg, event, relays) { relays ?? (relays = event?.onRelays); this.warnings.push({ msg, event, relays }); this.emit("warning", { msg, event, relays }); } get balance() { return { amount: this.state.getBalance({ onlyAvailable: true }) }; } /** * Gets the total balance for a specific mint, including reserved proofs */ mintBalance(mint) { return this.mintBalances[mint] || 0; } /** * Gets all tokens, grouped by mint with their total balances */ get mintBalances() { return this.state.getMintsBalance({ onlyAvailable: true }); } /** * Returns a list of mints that have enough available balance (excluding reserved proofs) * to cover the specified amount */ getMintsWithBalance(amount) { const availableBalances = this.state.getMintsBalance({ onlyAvailable: true }); return Object.entries(availableBalances).filter(([_2, balance]) => balance >= amount).map(([mint]) => mint); } /** * Gets mint information for a specific mint URL. * Returns cached info if available, otherwise fetches from the mint. */ async getMintInfo(mintUrl) { const cashuWallet = await this.getCashuWallet(mintUrl); return await cashuWallet.mint.getInfo(); } /** * Fetches transaction history (kind 7376) for this wallet. */ async fetchTransactions() { const user = await this.ndk.signer?.user(); if (!user) return []; const events = await this.ndk.fetchEvents( { kinds: [NDKKind2.CashuWalletTx], authors: [user.pubkey] }, { cacheUsage: NDKSubscriptionCacheUsage2.PARALLEL }, this.relaySet ); const transactions = []; for (const event of events) { const tx = await NDKCashuWalletTx2.from(event); if (tx) { transactions.push(this.txEventToTransaction(tx)); } } return transactions.sort((a, b) => b.timestamp - a.timestamp); } /** * Subscribes to transaction updates (kind 7376) for real-time updates. */ subscribeTransactions(callback) { const user = this.ndk.activeUser; if (!user) return () => { }; const seenIds = /* @__PURE__ */ new Set(); this.fetchTransactions().then((txs) => { for (const tx of txs) { if (!seenIds.has(tx.id)) { seenIds.add(tx.id); callback(tx); } } }); const sub = this.ndk.subscribe( { kinds: [NDKKind2.CashuWalletTx], authors: [user.pubkey] }, { closeOnEose: false, relaySet: this.relaySet, onEvent: async (event) => { if (seenIds.has(event.id)) return; seenIds.add(event.id); const tx = await NDKCashuWalletTx2.from(event); if (tx) { callback(this.txEventToTransaction(tx)); } } } ); return () => sub.stop(); } txEventToTransaction(tx) { return { id: tx.id, direction: tx.direction ?? "out", amount: tx.amount ?? 0, timestamp: tx.created_at ?? 0, description: tx.description, fee: tx.fee, mint: tx.mint }; } }; __publicField(_NDKCashuWallet, "kind", NDKKind2.CashuWallet); __publicField(_NDKCashuWallet, "kinds", [NDKKind2.CashuWallet]); var NDKCashuWallet2 = _NDKCashuWallet; var NDKCashuWalletBackup2 = class _NDKCashuWalletBackup2 extends NDKEvent2 { constructor(ndk, event) { super(ndk, event); __publicField(this, "privkeys", []); __publicField(this, "mints", []); this.kind ?? (this.kind = NDKKind2.CashuWalletBackup); } static async from(event) { if (!event.ndk) throw new Error("no ndk instance on event"); const backup = new _NDKCashuWalletBackup2(event.ndk, event); try { await backup.decrypt(); const content = JSON.parse(backup.content); for (const tag of content) { if (tag[0] === "mint") { backup.mints.push(tag[1]); } else if (tag[0] === "privkey") { backup.privkeys.push(tag[1]); } } } catch (e2) { console.error("error decrypting backup event", backup.encode(), e2); return; } return backup; } async save(relaySet) { if (!this.ndk) throw new Error("no ndk instance"); if (!this.privkeys.length) throw new Error("no privkeys"); this.content = JSON.stringify(payloadForEvent2(this.privkeys, this.mints)); await this.encrypt(this.ndk.activeUser, void 0, "nip44"); return this.publish(relaySet); } }; function payloadForEvent2(privkeys, mints) { if (privkeys.length === 0) throw new Error("privkey not set"); const payload = [ ...mints.map((mint) => ["mint", mint]), ...privkeys.map((privkey) => ["privkey", privkey]) ]; return payload; } // ndk/wallet/src/nutzap-monitor/fetch-page.ts init_dist(); async function fetchPage2(ndk, filter, _knownNutzaps, relaySet) { const events = await ndk.fetchEvents( filter, { cacheUsage: NDKSubscriptionCacheUsage2.ONLY_RELAY, groupable: false, subId: "recent-nutzap" }, relaySet ); return Array.from(events).map((e2) => NDKNutzap2.from(e2)).filter((n) => !!n); } // ndk/wallet/src/nutzap-monitor/group-nutzaps.ts init_dist(); function groupNutzaps2(nutzaps, monitor) { const result = /* @__PURE__ */ new Map(); const getKey = (mint, p2pk = "no-key") => `${mint}:${p2pk}`; for (const nutzap of nutzaps) { if (!monitor.shouldTryRedeem(nutzap)) continue; const mint = nutzap.mint; for (const proof of nutzap.proofs) { const cashuPubkey = proofP2pk2(proof) ?? "no-key"; const key = getKey(mint, cashuPubkey); const group = result.get(key) ?? { mint, cashuPubkey, nostrPubkey: cashuPubkeyToNostrPubkey2(cashuPubkey), nutzaps: [] }; group.nutzaps.push(nutzap); result.set(key, group); } } return Array.from(result.values()); } // ndk/wallet/src/nutzap-monitor/spend-status.ts init_cashu_ts_es(); async function getProofSpendState2(wallet, nutzaps) { const result = { unspentProofs: [], spentProofs: [], nutzapsWithUnspentProofs: [], nutzapsWithSpentProofs: [] }; const proofCs = /* @__PURE__ */ new Set(); const proofs = []; const nutzapMap = /* @__PURE__ */ new Map(); for (const nutzap of nutzaps) { for (const proof of nutzap.proofs) { if (proofCs.has(proof.C)) continue; proofCs.add(proof.C); proofs.push(proof); nutzapMap.set(proof.C, nutzap); } } const states = await wallet.checkProofsStates(proofs); for (let i3 = 0; i3 < states.length; i3++) { const state = states[i3]; const proof = proofs[i3]; const nutzap = nutzapMap.get(proof.C); if (!nutzap) continue; if (state.state === as.SPENT) { result.spentProofs.push(proof); if (!result.nutzapsWithSpentProofs.some((n) => n.id === nutzap.id)) { result.nutzapsWithSpentProofs.push(nutzap); } } else if (state.state === as.UNSPENT) { result.unspentProofs.push(proof); if (!result.nutzapsWithUnspentProofs.some((n) => n.id === nutzap.id)) { result.nutzapsWithUnspentProofs.push(nutzap); } } } return result; } // ndk/wallet/src/nutzap-monitor/index.ts var _startTime = Date.now(); function log2(_msg) { } var NDKNutzapMonitor2 = class extends import_tseep26.EventEmitter { /** * Create a new nutzap monitor. * @param ndk - The NDK instance. * @param user - The user to monitor. * @param mintList - An optional mint list to monitor zaps on, if one is not provided, the monitor will use the relay set from the mint list, which is the correct default behavior of NIP-61 zaps. * @param store - An optional store to save and load nutzap states to. */ constructor(ndk, user, { mintList, store }) { super(); __publicField(this, "store"); __publicField(this, "ndk"); __publicField(this, "user"); __publicField(this, "relaySet"); __publicField(this, "sub"); __publicField(this, "nutzapStates", /* @__PURE__ */ new Map()); __publicField(this, "_wallet"); __publicField(this, "mintList"); __publicField(this, "privkeys", /* @__PURE__ */ new Map()); __publicField(this, "cashuWallets", /* @__PURE__ */ new Map()); __publicField(this, "getCashuWallet", getCashuWallet2.bind(this)); __publicField(this, "onMintInfoNeeded"); __publicField(this, "onMintInfoLoaded"); __publicField(this, "onMintKeysNeeded"); __publicField(this, "onMintKeysLoaded"); this.ndk = ndk; this.user = user; this.mintList = mintList; this.relaySet = mintList?.relaySet; this.store = store; } set wallet(wallet) { this._wallet = wallet; if (wallet) { this.onMintInfoNeeded ?? (this.onMintInfoNeeded = wallet.onMintInfoNeeded); this.onMintInfoLoaded ?? (this.onMintInfoLoaded = wallet.onMintInfoLoaded); this.onMintKeysNeeded ?? (this.onMintKeysNeeded = wallet.onMintKeysNeeded); this.onMintKeysLoaded ?? (this.onMintKeysLoaded = wallet.onMintKeysLoaded); if (wallet instanceof NDKCashuWallet2 && wallet?.privkeys) { for (const [pubkey, signer] of wallet.privkeys.entries()) { try { this.addPrivkey(signer); } catch (e2) { console.error("failed to add privkey from wallet with pubkey", pubkey, e2); } } } } } get wallet() { return this._wallet; } /** * Provide private keys that can be used to redeem nutzaps. * * This is particularly useful when a NWC wallet is used to receive the nutzaps, * since it doesn't have a private key, this allows keeping the private key in a separate * place (ideally a NIP-60 wallet event). * * Multiple keys can be added, and the monitor will use the correct key for the nutzap. */ async addPrivkey(signer) { const pubkey = (await signer.user()).pubkey; if (this.privkeys.has(pubkey)) return; this.privkeys.set(pubkey, signer); if (!this.sub) return; const inMssingPrivKeyState = (state) => state.status === NdkNutzapStatus2.MISSING_PRIVKEY; const ensureIsCashuPubkey3 = (state) => state.nutzap?.p2pk === pubkey; const candidateNutzaps = Array.from(this.nutzapStates.values()).filter(inMssingPrivKeyState).filter(ensureIsCashuPubkey3); if (candidateNutzaps.length > 0) { const nutzaps = candidateNutzaps.map((c) => c.nutzap).filter((n) => !!n); const groupedNutzaps = groupNutzaps2(nutzaps, this); for (const group of groupedNutzaps) { await this.checkAndRedeemGroup(group); } } } async addUserPrivKey() { const { signer } = this.ndk; if (signer instanceof NDKPrivateKeySigner2) { const user = await signer.user(); const pubkey = user.pubkey; this.privkeys.set(pubkey, signer); } } /** * Loads kind:375 backup events and kind:17375 wallet config events from this user * to find all backup keys this user might have used. */ async getBackupKeys() { const backupEvents = await this.ndk.fetchEvents( [{ kinds: [NDKKind2.CashuWalletBackup, NDKKind2.CashuWallet], authors: [this.user.pubkey] }], void 0, this.relaySet ); const keys = Array.from(this.privkeys.values()); const keysNotFound = new Set(keys.map((signer) => signer.privateKey)); for (const event of backupEvents) { if (event.kind === NDKKind2.CashuWalletBackup) { const backup = await NDKCashuWalletBackup2.from(event); if (!backup) continue; for (const privkey of backup.privkeys) { if (keysNotFound.has(privkey)) keysNotFound.delete(privkey); try { const signer = new NDKPrivateKeySigner2(privkey); this.addPrivkey(signer); } catch (e2) { console.error("failed to add privkey", privkey, e2); } } } else if (event.kind === NDKKind2.CashuWallet) { try { await event.decrypt(); const content = JSON.parse(event.content); for (const tag of content) { if (tag[0] === "privkey") { const privkey = tag[1]; if (keysNotFound.has(privkey)) keysNotFound.delete(privkey); try { const signer = new NDKPrivateKeySigner2(privkey); this.addPrivkey(signer); } catch (e2) { console.error("failed to add privkey from wallet config", privkey, e2); } } } } catch (e2) { console.error("failed to decrypt wallet config event", event.encode(), e2); } } } if (keysNotFound.size > 0) { const backup = new NDKCashuWalletBackup2(this.ndk); backup.privkeys = Array.from(keysNotFound); await backup.save(this.relaySet); } } /** * Fetches the wallet's mint list from relays. * This is used for checking if incoming nutzaps match advertised preferences. */ async fetchMintList() { const event = await this.ndk.fetchEvent( { kinds: [NDKKind2.CashuMintList], authors: [this.user.pubkey] }, { cacheUsage: NDKSubscriptionCacheUsage2.PARALLEL, subId: "cashu-mint-list" } ); if (event) { this.mintList = NDKCashuMintList2.from(event); return this.mintList; } return void 0; } /** * Start the nutzap monitor. The monitor will initially look back * for nutzaps it doesn't know about and will try to redeem them. * * @param knownNutzaps - An optional set of nutzaps the app knows about. This is an optimization so that we don't try to redeem nutzaps we know have already been redeemed. * @param pageSize - The number of nutzaps to fetch per page. * */ async start({ filter, opts }) { log2("Starting nutzap monitor"); if (this.sub) this.sub.stop(); if (!this.mintList) { try { const mintList = await this.fetchMintList(); log2(`Fetched mint list with ${mintList?.mints.length ?? 0} mints`); } catch (e2) { console.error("\u274C Failed to fetch mint list", e2); } } try { await this.getBackupKeys(); log2(`Got backup keys ${this.privkeys.size}`); } catch (e2) { console.error("\u274C Failed to get backup keys", e2); } await this.addUserPrivKey(); log2(`Added user privkey ${this.privkeys.size}`); const since = Math.floor(Date.now() / 1e3); const monitorFilter = { kinds: [NDKKind2.Nutzap], "#p": [this.user.pubkey], since }; if (this.store) { log2("Will load nutzaps from store"); try { const nutzaps = await this.store.getAllNutzaps(); log2(`Loaded ${nutzaps.size} nutzaps`); for (const [id, state] of nutzaps.entries()) { this.nutzapStates.set(id, state); } log2(`Changed the state of ${nutzaps.size} nutzaps`); } catch (e2) { console.error("\u274C Failed to load nutzaps from store", e2); } } try { log2("Will start processing redeemable nutzaps from store"); await this.processRedeemableNutzapsFromStore(); log2("Finished processing redeemable nutzaps from store"); } catch (e2) { console.error("\u274C Failed to process redeemable nutzaps from store", e2); } try { log2("Will start processing accumulated nutzaps"); await this.processAccumulatedNutzaps(filter, opts); log2(`Finished processing accumulated nutzaps ${this.nutzapStates.size}`); } catch (e2) { console.error("\u274C Failed to process nutzaps", e2); } log2(`Running filter ${JSON.stringify(monitorFilter)}`); const subscribeOpts = { subId: "ndk-wallet:nutzap-monitor", cacheUsage: NDKSubscriptionCacheUsage2.ONLY_RELAY, wrap: false, // We skip validation so the user knows about nutzaps that were sent but are not valid // this way tooling can be more comprehensive and include nutzaps that were not valid skipValidation: true, ...opts, relaySet: this.relaySet // Pass relaySet via options }; this.sub = this.ndk.subscribe( monitorFilter, subscribeOpts, // this.relaySet, // Removed: Passed via opts { // autoStart handlers (now 3rd argument) onEvent: (event) => this.eventHandler(event) // Added NDKEvent type } ); log2("\u2705 Nutzap monitor started successfully"); return true; } /** * Checks if the group of nutzaps can be redeemed and redeems the ones that can be. */ async checkAndRedeemGroup(group, oldestUnspentNutzapTime) { const cashuWallet = await this.getCashuWallet(group.mint); const spendStates = await getProofSpendState2(cashuWallet, group.nutzaps); for (const nutzap of spendStates.nutzapsWithSpentProofs) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.SPENT, nutzap }); } for (const nutzap of spendStates.nutzapsWithUnspentProofs) { this.emit("seen", nutzap); this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INITIAL, nutzap }); } if (spendStates.unspentProofs.length > 0) { for (const nutzap of spendStates.nutzapsWithUnspentProofs) { if (!oldestUnspentNutzapTime || oldestUnspentNutzapTime > nutzap.created_at) { oldestUnspentNutzapTime = nutzap.created_at; } } await this.redeemNutzaps(group.mint, spendStates.nutzapsWithUnspentProofs, spendStates.unspentProofs); } } /** * Processes nutzaps that have been accumulated while the monitor was offline. * @param startOpts * @param opts */ async processAccumulatedNutzaps(filter = {}, opts) { log2("Processing accumulated nutzaps"); let oldestUnspentNutzapTime; const _filter = { ...filter }; _filter.kinds = [NDKKind2.Nutzap]; _filter["#p"] = [this.user.pubkey]; const knownNutzapIds = new Set(this.nutzapStates.keys()); const nutzaps = await fetchPage2(this.ndk, _filter, knownNutzapIds, this.relaySet); log2(`We loaded ${nutzaps.length} nutzaps from relays`); oldestUnspentNutzapTime = await this.processNutzaps(nutzaps, oldestUnspentNutzapTime); log2("We finished processing thesenutzaps"); if (oldestUnspentNutzapTime) { _filter.since = oldestUnspentNutzapTime - 1; await this.processAccumulatedNutzaps(_filter, opts); } } stop() { this.sub?.stop(); } updateNutzapState(id, state) { const currentState = this.nutzapStates.get(id) ?? {}; if (!currentState.status) state.status ?? (state.status = NdkNutzapStatus2.INITIAL); const stateIsUnchanged = Object.entries(state).every(([key, value]) => { if (key === "nutzap" && currentState.nutzap && value) { return currentState.nutzap.id === value.id; } return currentState[key] === value; }); if (stateIsUnchanged) return; this.nutzapStates.set(id, { ...currentState, ...state }); this.emit("state_changed", id, currentState.status); const serializedState = (state2) => { const res = { ...state2 }; if (res.nutzap) res.nutzap = res.nutzap.id; return JSON.stringify(res); }; const currentStatusStr = serializedState(currentState); const newStatusStr = serializedState(state); log2(`[${id.substring(0, 6)}] ${currentStatusStr} changed to \u{1F449} ${newStatusStr}`); this.store?.setNutzapState(id, state); } async eventHandler(event) { if (this.nutzapStates.has(event.id)) return; const nutzap = await NDKNutzap2.from(event); if (!nutzap) { this.updateNutzapState(event.id, { status: NdkNutzapStatus2.PERMANENT_ERROR, errorMessage: "Failed to parse nutzap" }); return; } if (this.mintList && !this.mintList.mints.includes(nutzap.mint)) { this.emit("seen_in_unknown_mint", nutzap); } this.redeemNutzap(nutzap); } /** * Gathers the necessary information to redeem a nutzap and then redeems it. * @param nutzap */ async redeemNutzap(nutzap) { if (!this.nutzapStates.has(nutzap.id)) this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INITIAL, nutzap }); const rawP2pk = nutzap.rawP2pk; if (rawP2pk) { const cashuPubkey = proofP2pk2(nutzap.proofs[0]); if (cashuPubkey) { const nostrPubkey = cashuPubkeyToNostrPubkey2(cashuPubkey); if (nostrPubkey && !this.privkeys.has(nostrPubkey)) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.MISSING_PRIVKEY, errorMessage: "No privkey found for p2pk" }); return this.nutzapStates.get(nutzap.id); } } } await this.redeemNutzaps(nutzap.mint, [nutzap], nutzap.proofs); return this.nutzapStates.get(nutzap.id); } /** * This function redeems a list of proofs. * * Proofs will be attempted to be redeemed in a single call, so they will all work or none will. * Either call this function with proofs that have been verified to be redeemable or don't group them, * and provide a single nutzap per call. * * All nutzaps MUST be p2pked to the same pubkey. * * @param mint * @param nutzaps * @param proofs * @param privkey Private key that is needed to redeem the nutzaps. * @returns */ async redeemNutzaps(mint, nutzaps, proofs) { if (!this.wallet) throw new Error("wallet not set"); if (!this.wallet.redeemNutzaps) throw new Error("wallet does not support redeeming nutzaps"); const cashuWallet = await this.getCashuWallet(mint); const validNutzaps = []; if (proofs.length > 0) { const cashuPubkey2 = proofP2pk2(proofs[0]); if (!cashuPubkey2) { for (const nutzap of nutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INVALID_NUTZAP, errorMessage: "Invalid nutzap: proof is not p2pk" }); } return; } const nostrPubkey2 = cashuPubkeyToNostrPubkey2(cashuPubkey2); if (!nostrPubkey2) { for (const nutzap of nutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INVALID_NUTZAP, errorMessage: "Invalid nutzap: locked to an invalid public key (not a nostr key)" }); } return; } const privkey2 = this.privkeys.get(nostrPubkey2); if (!privkey2) { for (const nutzap of nutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.MISSING_PRIVKEY, errorMessage: "No privkey found for p2pk" }); } return; } } for (const nutzap of nutzaps) { if (!nutzap.isValid) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INVALID_NUTZAP, errorMessage: "Invalid nutzap" }); continue; } const rawP2pk = nutzap.rawP2pk; if (!rawP2pk) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INVALID_NUTZAP, errorMessage: "Invalid nutzap: locked to an invalid public key (no p2pk)" }); continue; } if (rawP2pk.length !== 66) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.INVALID_NUTZAP, errorMessage: `Invalid nutzap: locked to an invalid public key (length ${rawP2pk.length})` }); continue; } validNutzaps.push(nutzap); } if (validNutzaps.length === 0) return; const cashuPubkey = proofP2pk2(proofs[0]); if (!cashuPubkey) return; const nostrPubkey = cashuPubkeyToNostrPubkey2(cashuPubkey); if (!nostrPubkey) return; const privkey = this.privkeys.get(nostrPubkey); if (!privkey) { for (const nutzap of validNutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.MISSING_PRIVKEY, errorMessage: "No privkey found for p2pk" }); } return; } for (const nutzap of validNutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.PROCESSING }); } try { const totalAmount = await this.wallet.redeemNutzaps(nutzaps, privkey.privateKey, { cashuWallet, proofs, mint }); this.emit("redeemed", nutzaps, totalAmount); for (const nutzap of nutzaps) { const nutzapTotalAmount = proofsTotal2(proofsIntersection2(proofs, nutzap.proofs)); this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.REDEEMED, redeemedAmount: nutzapTotalAmount }); } } catch (e2) { console.error("\u274C Failed to redeem nutzaps", e2.message); if (e2.message?.includes("unknown public key size")) { for (const nutzap of nutzaps) { this.updateNutzapState(nutzap.id, { status: NdkNutzapStatus2.PERMANENT_ERROR, errorMessage: "Invalid p2pk: unknown public key size" }); this.emit("failed", nutzap, "Invalid p2pk: unknown public key size"); } } else { for (const nutzap of nutzaps) { this.emit("failed", nutzap, e2.message); } } } } shouldTryRedeem(nutzap) { const state = this.nutzapStates.get(nutzap.id); if (!state) return true; if ([NdkNutzapStatus2.INITIAL].includes(state.status)) return true; if (state.status === NdkNutzapStatus2.MISSING_PRIVKEY) { const p2pk = state.nutzap?.p2pk; if (p2pk && this.privkeys.has(p2pk)) return true; return false; } if ([NdkNutzapStatus2.SPENT, NdkNutzapStatus2.REDEEMED].includes(state.status)) return false; if ([NdkNutzapStatus2.PERMANENT_ERROR, NdkNutzapStatus2.INVALID_NUTZAP].includes(state.status)) return false; return false; } /** * Process nutzaps from the store that are in a redeemable state. * This includes nutzaps in INITIAL state and those in MISSING_PRIVKEY state * for which we now have the private key. */ async processRedeemableNutzapsFromStore() { const redeemableNutzaps = []; for (const [_id, state] of this.nutzapStates.entries()) { if (!state.nutzap) continue; if (this.shouldTryRedeem(state.nutzap)) { redeemableNutzaps.push(state.nutzap); } } if (redeemableNutzaps.length === 0) return; log2(`We found ${redeemableNutzaps.length} redeemable nutzaps in the store`); await this.processNutzaps(redeemableNutzaps); } /** * Common method to process a collection of nutzaps: * - Group them by mint * - Check and redeem each group * * @param nutzaps The nutzaps to process * @param oldestUnspentNutzapTime Optional timestamp to track the oldest unspent nutzap * @returns The updated oldestUnspentNutzapTime if any nutzaps were processed */ async processNutzaps(nutzaps, oldestUnspentNutzapTime) { const groupedNutzaps = groupNutzaps2(nutzaps, this); for (const group of groupedNutzaps) { log2(`Processing group ${group.mint} with ${group.nutzaps.length} nutzaps`); try { await this.checkAndRedeemGroup(group, oldestUnspentNutzapTime); log2(`Finished processing group ${group.mint}`); } catch (e2) { log2(`Failed to process group ${group.mint}`); console.error(`\u274C Failed to process group ${group.mint}`, e2); } } return oldestUnspentNutzapTime; } }; function proofsIntersection2(proofs1, proofs2) { const proofs2Cs = new Set(proofs2.map((p5) => p5.C)); return proofs1.filter((p5) => proofs2Cs.has(p5.C)); } function proofsTotal2(proofs) { return proofs.reduce((acc, proof) => acc + proof.amount, 0); } // ndk/wallet/src/wallets/cashu/mint/utils.ts init_dist(); async function getCashuMintRecommendations2(ndk, filter) { const f = [ { kinds: [NDKKind2.EcashMintRecommendation], "#k": ["38002"], ...filter || {} }, { kinds: [NDKKind2.CashuMintList], ...filter || {} } ]; const res = {}; const recommendations = await ndk.fetchEvents(f); for (const event of recommendations) { switch (event.kind) { case NDKKind2.EcashMintRecommendation: for (const uTag of event.getMatchingTags("u")) { if (uTag[2] && uTag[2] !== "cashu") continue; const url = uTag[1]; if (!url) continue; const entry = res[url] || { events: [], pubkeys: /* @__PURE__ */ new Set() }; entry.events.push(event); entry.pubkeys.add(event.pubkey); res[url] = entry; } break; case NDKKind2.CashuMintList: for (const mintTag of event.getMatchingTags("mint")) { const url = mintTag[1]; if (!url) continue; const entry = res[url] || { events: [], pubkeys: /* @__PURE__ */ new Set() }; entry.events.push(event); entry.pubkeys.add(event.pubkey); res[url] = entry; } break; } } return res; } // ndk/wallet/src/wallets/nwc/index.ts init_cashu_ts_es(); init_dist(); var import_debug43 = __toESM(require_browser(), 1); // ndk/wallet/src/wallets/nwc/nutzap.ts init_dist(); async function redeemNutzaps2(nutzaps, privkey, { cashuWallet, proofs, mint }) { proofs ?? (proofs = nutzaps.flatMap((n) => n.proofs)); if (!cashuWallet) { if (!mint) throw new Error("No mint provided"); cashuWallet = await this.getCashuWallet(mint); } else { mint = cashuWallet.mint.mintUrl; } const info = await this.getInfo(); if (!info.methods.includes("make_invoice")) throw new Error("This NWC wallet does not support making invoices"); const totalAvailable = proofs.reduce((acc, proof) => acc + proof.amount, 0); let sweepAmount = totalAvailable; while (sweepAmount > 0) { const invoice = await this.makeInvoice(sweepAmount * 1e3, "Nutzap redemption"); const meltQuote = await cashuWallet.createMeltQuote(invoice.invoice); const totalRequired = meltQuote.amount + meltQuote.fee_reserve; if (totalRequired > totalAvailable) { sweepAmount -= meltQuote.fee_reserve; continue; } const result = await cashuWallet.meltProofs(meltQuote, proofs, { privkey }); let change; if (result.change.length > 0) change = await saveChange2(this.ndk, mint, result.change); const description = `Nutzap redemption to external wallet (${this.walletId})`; createOutTxEvent2( this.ndk, { pr: invoice.invoice, paymentDescription: description }, { result: { preimage: invoice.preimage }, mint, fee: meltQuote.fee_reserve, proofsChange: { store: change?.proofs, mint }, stateUpdate: { created: change } }, this.relaySet, { nutzaps } ); return sweepAmount; } throw new Error("Failed to redeem nutzaps"); } async function saveChange2(ndk, mint, change) { const totalChange = change.reduce((acc, proof) => acc + proof.amount, 0); if (totalChange === 0) return; const token = new NDKCashuToken2(ndk); token.mint = mint; token.proofs = change; token.publish(); return token; } // ndk/wallet/src/wallets/nwc/req.ts init_dist(); // ndk/wallet/src/wallets/nwc/res.ts init_dist(); async function waitForResponse2(request) { if (!this.pool) throw new Error("Wallet not initialized"); const sendRequest = () => { if (waitForEoseTimeout) clearTimeout(waitForEoseTimeout); request.publish(this.relaySet); }; const waitForEoseTimeout = setTimeout(sendRequest, 2500); return new Promise((resolve, reject) => { const sub = this.ndk.subscribe( { kinds: [NDKKind2.NostrWalletConnectRes], "#e": [request.id], limit: 1 }, { groupable: false, pool: this.pool, relaySet: this.relaySet, onEvent: async (event) => { try { await event.decrypt(event.author, this.signer); const content = JSON.parse(event.content); if (content.error) { reject(content); } else { resolve(content); } } catch (e2) { console.error("error decrypting event", e2); reject({ result_type: "error", error: { code: "failed_to_parse_response", message: e2.message } }); } finally { sub.stop(); } }, onEose: () => { sendRequest(); } } ); }); } // ndk/wallet/src/wallets/nwc/req.ts async function sendReq2(method, params) { if (!this.walletService || !this.signer) { throw new Error("Wallet not initialized"); } const event = new NDKEvent2(this.ndk, { kind: NDKKind2.NostrWalletConnectReq, tags: [["p", this.walletService.pubkey]], content: JSON.stringify({ method, params }) }); await event.encrypt(this.walletService, this.signer, "nip04"); await event.sign(this.signer); const responsePromise = new Promise((resolve, reject) => { waitForResponse2.call(this, event).then(resolve).catch(reject); }); if (this.timeout) { const timeoutPromise = new Promise( (_2, reject) => setTimeout(() => { this.emit("timeout", method); reject(new Error(`Request timed out after ${this.timeout}ms`)); }, this.timeout) ); return Promise.race([responsePromise, timeoutPromise]); } return responsePromise; } // ndk/wallet/src/wallets/nwc/tx.ts function toWalletTransaction2(tx) { return { id: tx.payment_hash, direction: tx.type === "incoming" ? "in" : "out", amount: Math.floor(tx.amount / 1e3), timestamp: tx.created_at, description: tx.description, fee: tx.fees_paid ? Math.floor(tx.fees_paid / 1e3) : void 0, invoice: tx.invoice }; } // ndk/wallet/src/wallets/nwc/index.ts var d16 = (0, import_debug43.default)("ndk-wallet:nwc"); var TX_POLL_INTERVAL2 = 6e4; var NDKNWCWallet2 = class extends NDKWallet2 { /** * * @param ndk * @param timeout A timeeout to use for all operations. */ constructor(ndk, { timeout, pairingCode, pubkey, relayUrls, secret }) { super(ndk); __publicField(this, "status", "initial" /* INITIAL */); __publicField(this, "walletId", "nwc"); __publicField(this, "pairingCode"); __publicField(this, "walletService"); __publicField(this, "relaySet"); __publicField(this, "signer"); __publicField(this, "_balance"); __publicField(this, "cachedInfo"); __publicField(this, "pool"); __publicField(this, "timeout"); /** * Redeem a set of nutzaps into an NWC wallet. * * This function gets an invoice from the NWC wallet until the total amount of the nutzaps is enough to pay for the invoice * when accounting for fees. * * @param cashuWallet - The cashu wallet to redeem the nutzaps into * @param nutzaps - The nutzaps to redeem * @param proofs - The proofs to redeem * @param mint - The mint to redeem the nutzaps into * @param privkey - The private key needed to redeem p2pk proofs. */ __publicField(this, "redeemNutzaps", redeemNutzaps2.bind(this)); __publicField(this, "req", sendReq2.bind(this)); if (pairingCode) { const u3 = new URL(pairingCode); pubkey = u3.host ?? u3.pathname; relayUrls = u3.searchParams.getAll("relay"); secret = u3.searchParams.get("secret"); this.pairingCode = pairingCode; } if (!pubkey || !relayUrls || !secret) throw new Error("Incomplete initialization parameters"); this.timeout = timeout; this.walletService = this.ndk.getUser({ pubkey }); this.pool = this.getPool(relayUrls); this.relaySet = NDKRelaySet2.fromRelayUrls(relayUrls, this.ndk, true, this.pool); this.signer = new NDKPrivateKeySigner2(secret); this.pool.on("connect", () => { this.status = "ready" /* READY */; this.emit("ready"); }); this.pool.on("relay:disconnect", () => this.status = "loading" /* LOADING */); this.pool.connect(); if (this.pool.connectedRelays().length > 0) { this.status = "ready" /* READY */; this.emit("ready"); } } get type() { return "nwc"; } getPool(relayUrls) { for (const pool of this.ndk.pools) if (pool.name === "NWC") return pool; return new NDKPool2(relayUrls, this.ndk, { name: "NWC" }); } async lnPay(payment) { if (!this.signer) throw new Error("Wallet not initialized"); d16("lnPay", payment.pr); const res = await this.req("pay_invoice", { invoice: payment.pr }); d16("lnPay res", res); if (res.result) { return { preimage: res.result.preimage }; } this.updateBalance(); throw new Error(res.error?.message || "Payment failed"); } /** * Pay by minting tokens. * * This creates a quote on a mint, pays it using NWC and then mints the tokens. * * @param payment - The payment to pay * @param onLnPayment - A callback that is called when an LN payment will be processed * @returns The payment confirmation */ async cashuPay(payment, onLnInvoice, onLnPayment) { if (!payment.mints) throw new Error("No mints provided"); for (const mint of payment.mints) { let amount = payment.amount; amount = amount / 1e3; const wallet = new us(new q(mint), { unit: "sat" }); let quote; try { quote = await wallet.createMintQuote(amount); d16("cashuPay quote", quote); onLnInvoice?.(quote.request); } catch (e2) { console.error("error creating mint quote", e2); throw e2; } if (!quote) throw new Error("Didnt receive a mint quote"); try { const res = await this.req("pay_invoice", { invoice: quote.request }); if (res.result?.preimage) { onLnPayment?.(mint, res.result.preimage); } d16("cashuPay res", res); } catch (e2) { const message = e2?.error?.message || e2?.message || "unknown error"; console.error("error paying invoice", e2, { message }); throw new Error(message); } this.updateBalance(); return mintProofs2(wallet, quote, amount, mint, payment.p2pk); } } /** * Fetch the balance of this wallet */ async updateBalance() { const res = await this.req("get_balance", {}); if (!res.result) throw new Error("Failed to get balance"); if (res.error) throw new Error(res.error.message); this._balance = { amount: res.result?.balance ?? 0 }; this._balance.amount /= 1e3; this.emit("balance_updated"); } /** * Get the balance of this wallet */ get balance() { return this._balance; } async getInfo(refetch = false) { if (refetch) { this.cachedInfo = void 0; } if (this.cachedInfo) return this.cachedInfo; const res = await this.req("get_info", {}); d16("info", res); if (!res.result) throw new Error("Failed to get info"); if (res.error) throw new Error(res.error.message); this.cachedInfo = res.result; if (res.result.alias) this.walletId = res.result.alias; return res.result; } async fetchTransactions() { const res = await this.req("list_transactions", {}); if (!res.result) return []; return res.result.transactions.map(toWalletTransaction2); } subscribeTransactions(callback) { const knownIds = /* @__PURE__ */ new Set(); const poll = async () => { try { const txs = await this.fetchTransactions(); for (const tx of txs) { if (!knownIds.has(tx.id)) { knownIds.add(tx.id); callback(tx); } } } catch (e2) { d16("Error polling transactions", e2); } }; poll(); const interval = setInterval(poll, TX_POLL_INTERVAL2); const boundPoll = () => { poll(); }; this.on("balance_updated", boundPoll); return () => { clearInterval(interval); this.off("balance_updated", boundPoll); }; } async makeInvoice(amount, description) { const res = await this.req("make_invoice", { amount, description }); if (!res.result) throw new Error("Failed to make invoice"); return res.result; } }; // ndk/wallet/src/wallets/webln/index.ts var import_webln2 = __toESM(require_lib4(), 1); // ndk/wallet/src/wallets/webln/pay.ts init_cashu_ts_es(); var NDKLnPay2 = class { constructor(wallet, info) { __publicField(this, "wallet"); __publicField(this, "info"); __publicField(this, "type", "ln"); this.wallet = wallet; this.info = info; } async pay() { if (this.type === "ln") { return this.payLn(); } return this.payNut(); } /** * Uses LN balance to pay to a mint */ async payNut() { const { mints, p2pk } = this.info; let { amount, unit } = this.info; if (!mints) throw new Error("No mints provided"); if (unit === "msat") { amount /= 1e3; unit = "sat"; } const quotesPromises = mints.map(async (mint2) => { const wallet2 = new us(new q(mint2), { unit }); const quote2 = await wallet2.createMintQuote(amount); return { quote: quote2, mint: mint2 }; }); const { quote, mint } = await Promise.any(quotesPromises); if (!quote) { console.warn("failed to get quote from any mint"); throw new Error("failed to get quote from any mint"); } const res = await this.wallet.pay({ pr: quote.request }); if (!res) { console.warn("payment failed"); throw new Error("payment failed"); } const wallet = new us(new q(mint), { unit }); const proofs = await wallet.mintProofs(amount, quote.quote, { pubkey: p2pk }); console.warn("minted tokens with proofs %o", proofs); return { proofs, mint }; } /** * Straightforward; uses LN balance to pay a LN invoice */ async payLn() { const data = this.info; if (!data.pr) throw new Error("missing pr"); const _paid = false; const ret = await this.wallet.pay(data); return ret ? ret.preimage : void 0; } }; // ndk/wallet/src/wallets/webln/index.ts var NDKWebLNWallet2 = class extends NDKWallet2 { constructor(ndk) { super(ndk); __publicField(this, "walletId", "webln"); __publicField(this, "status", "initial" /* INITIAL */); __publicField(this, "provider"); __publicField(this, "_balance"); (0, import_webln2.requestProvider)().then((p5) => { if (p5) { this.provider = p5; this.status = "ready" /* READY */; this.emit("ready"); } else { this.status = "failed" /* FAILED */; } }).catch(() => this.status = "failed" /* FAILED */); } get type() { return "webln"; } async pay(payment) { if (!this.provider) throw new Error("Provider not ready"); return this.provider.sendPayment(payment.pr); } async lnPay(payment) { const pay = new NDKLnPay2(this, payment); const preimage = await pay.payLn(); if (!preimage) return; return { preimage }; } async cashuPay(payment) { const pay = new NDKLnPay2(this, payment); return pay.payNut(); } async updateBalance() { if (!this.provider) { return new Promise((resolve) => { this.once("ready", () => { resolve(); }); }); } const b = await this.provider.getBalance?.(); if (b) this._balance = { amount: b.balance }; return; } get balance() { if (!this.provider) { return void 0; } return this._balance; } }; // build/ndk-entry.js var ndk_entry_default = NDK; return __toCommonJS(ndk_entry_exports); })(); /*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */ /*! Bundled license information: @noble/hashes/esm/utils.js: (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *) @noble/curves/esm/utils.js: @noble/curves/esm/abstract/modular.js: @noble/curves/esm/abstract/curve.js: @noble/curves/esm/abstract/weierstrass.js: @noble/curves/esm/_shortw_utils.js: @noble/curves/esm/secp256k1.js: (*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *) @scure/base/lib/index.js: @scure/base/lib/esm/index.js: (*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) *) dexie/dist/dexie.js: (*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** *) @cashu/cashu-ts/lib/utils-CrQNeCaC.js: (*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh *) (*! * The buffer module from node.js, for the browser. * * @author Feross Aboukhadijeh * @license MIT *) @scure/bip32/lib/esm/index.js: (*! scure-bip32 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) *) */ //# sourceMappingURL=ndk-core.bundle.js.map