From 6bf57766feb8321f860baf300140563cd9539053 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sun, 14 Dec 2025 20:36:24 -0800 Subject: Init --- .ci/ci.cjs | 5147 ++++++++++++++++++++++++++++++++++++++++++ .ci/ci.json | 3 + .ci/ci.ts | 76 + .ci/package-lock.json | 544 +++++ .ci/package.json | 17 + .ci/tsconfig.json | 28 + .dockerignore | 8 + .eslintrc.cjs | 12 + .gitignore | 5 + .prettierrc | 7 + Dockerfile | 17 + README.md | 345 +++ examples.sh | 162 ++ package-lock.json | 1861 +++++++++++++++ package.json | 36 + src/activity/index.ts | 438 ++++ src/index.ts | 64 + src/integrations/hcaptcha.ts | 32 + src/integrations/ntfy.ts | 40 + src/server/index.ts | 84 + src/storage/index.ts | 114 + src/token/index.ts | 76 + src/types/index.ts | 78 + tsconfig.json | 24 + 24 files changed, 9218 insertions(+) create mode 100755 .ci/ci.cjs create mode 100644 .ci/ci.json create mode 100644 .ci/ci.ts create mode 100644 .ci/package-lock.json create mode 100644 .ci/package.json create mode 100644 .ci/tsconfig.json create mode 100644 .dockerignore create mode 100644 .eslintrc.cjs create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 Dockerfile create mode 100644 README.md create mode 100755 examples.sh create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/activity/index.ts create mode 100644 src/index.ts create mode 100644 src/integrations/hcaptcha.ts create mode 100644 src/integrations/ntfy.ts create mode 100644 src/server/index.ts create mode 100644 src/storage/index.ts create mode 100644 src/token/index.ts create mode 100644 src/types/index.ts create mode 100644 tsconfig.json diff --git a/.ci/ci.cjs b/.ci/ci.cjs new file mode 100755 index 0000000..1a44166 --- /dev/null +++ b/.ci/ci.cjs @@ -0,0 +1,5147 @@ +#!/usr/bin/env node +"use strict"; +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 __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __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 || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/@emprespresso/pengueno/dist/leftpadesque/prepend.js +var require_prepend = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/leftpadesque/prepend.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.prependWith = void 0; + var prependWith = (arr, prep) => Array(arr.length * 2).fill(0).map((_, i) => i % 2 === 0).map((isPrep, i) => isPrep ? prep : arr[Math.floor(i / 2)]); + exports2.prependWith = prependWith; + } +}); + +// node_modules/@emprespresso/pengueno/dist/leftpadesque/debug.js +var require_debug = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/leftpadesque/debug.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.isDebug = exports2.isProd = void 0; + var _hasEnv = true; + var _env = _hasEnv && (process.env.ENVIRONMENT ?? "").toLowerCase().includes("prod") ? "production" : "development"; + var isProd = () => _env === "production"; + exports2.isProd = isProd; + var _debug = !(0, exports2.isProd)() || _hasEnv && ["y", "t"].some((process.env.DEBUG ?? "").toLowerCase().startsWith); + var isDebug = () => _debug; + exports2.isDebug = isDebug; + } +}); + +// node_modules/@emprespresso/pengueno/dist/leftpadesque/memoize.js +var require_memoize = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/leftpadesque/memoize.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.memoize = void 0; + var memoize = (fn) => { + const cache = /* @__PURE__ */ new Map(); + return (...args) => { + const key = JSON.stringify(args); + if (cache.has(key)) { + return cache.get(key); + } + const res = fn(...args); + cache.set(key, res); + return res; + }; + }; + exports2.memoize = memoize; + } +}); + +// node_modules/@emprespresso/pengueno/dist/leftpadesque/index.js +var require_leftpadesque = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/leftpadesque/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_prepend(), exports2); + __exportStar(require_debug(), exports2); + __exportStar(require_memoize(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/misc.js +var require_misc = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/misc.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/object.js +var require_object = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/object.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.isObject = void 0; + var isObject3 = (o) => typeof o === "object" && !Array.isArray(o) && !!o; + exports2.isObject = isObject3; + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/tagged.js +var require_tagged = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/tagged.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.isTagged = void 0; + var index_1 = require_types(); + var isTagged = (o, tag) => !!((0, index_1.isObject)(o) && "_tag" in o && o._tag === tag); + exports2.isTagged = isTagged; + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/fn/callable.js +var require_callable = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/fn/callable.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/fn/optional.js +var require_optional = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/fn/optional.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.Optional = exports2.IOptionalEmptyError = exports2.isOptional = exports2.IOptionalTag = void 0; + var pengueno_1 = require_dist2(); + exports2.IOptionalTag = "IOptional"; + var isOptional = (o) => (0, pengueno_1.isTagged)(o, exports2.IOptionalTag); + exports2.isOptional = isOptional; + var IOptionalEmptyError = class extends Error { + }; + exports2.IOptionalEmptyError = IOptionalEmptyError; + var OSomeTag = "O.Some"; + var ONoneTag = "O.None"; + var isNone = (o) => (0, pengueno_1.isTagged)(o, ONoneTag); + var isSome = (o) => (0, pengueno_1.isTagged)(o, OSomeTag); + var _Tagged = class { + _tag; + constructor(_tag = exports2.IOptionalTag) { + this._tag = _tag; + } + }; + var Optional = class _Optional extends _Tagged { + self; + constructor(self) { + super(); + this.self = self; + } + move(t) { + return this.map(() => t); + } + orSome(supplier) { + if (isNone(this.self)) + return _Optional.from(supplier()); + return this; + } + get() { + if (isNone(this.self)) + throw new IOptionalEmptyError("called get() on None optional"); + return this.self.value; + } + filter(mapper) { + if (isNone(this.self) || !mapper(this.self.value)) + return _Optional.none(); + return _Optional.some(this.self.value); + } + map(mapper) { + if (isNone(this.self)) + return _Optional.none(); + return _Optional.from(mapper(this.self.value)); + } + flatMap(mapper) { + if (isNone(this.self)) + return _Optional.none(); + return _Optional.from(mapper(this.self.value)).orSome(() => _Optional.none()).get(); + } + present() { + return isSome(this.self); + } + *[Symbol.iterator]() { + if (isSome(this.self)) + yield this.self.value; + } + static some(value) { + return new _Optional({ value, _tag: OSomeTag }); + } + static _none = new _Optional({ _tag: ONoneTag }); + static none() { + return this._none; + } + static from(value) { + if (value === null || value === void 0) + return _Optional.none(); + return _Optional.some(value); + } + }; + exports2.Optional = Optional; + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/fn/either.js +var require_either = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/fn/either.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.Either = exports2.isRight = exports2.isLeft = exports2.isEither = exports2.IEitherTag = void 0; + var pengueno_1 = require_dist2(); + exports2.IEitherTag = "IEither"; + var isEither = (o) => (0, pengueno_1.isTagged)(o, exports2.IEitherTag); + exports2.isEither = isEither; + var ELeftTag = "E.Left"; + var isLeft = (o) => (0, pengueno_1.isTagged)(o, ELeftTag); + exports2.isLeft = isLeft; + var ERightTag = "E.Right"; + var isRight = (o) => (0, pengueno_1.isTagged)(o, ERightTag); + exports2.isRight = isRight; + var _Tagged = class { + _tag; + constructor(_tag = exports2.IEitherTag) { + this._tag = _tag; + } + }; + var Either2 = class _Either extends _Tagged { + self; + constructor(self) { + super(); + this.self = self; + } + moveRight(t) { + return this.mapRight(() => t); + } + mapBoth(errBranch, okBranch) { + if ((0, exports2.isLeft)(this.self)) + return _Either.left(errBranch(this.self.err)); + return _Either.right(okBranch(this.self.ok)); + } + mapRight(mapper) { + if ((0, exports2.isRight)(this.self)) + return _Either.right(mapper(this.self.ok)); + return _Either.left(this.self.err); + } + mapLeft(mapper) { + if ((0, exports2.isLeft)(this.self)) + return _Either.left(mapper(this.self.err)); + return _Either.right(this.self.ok); + } + flatMap(mapper) { + if ((0, exports2.isRight)(this.self)) + return mapper(this.self.ok); + return _Either.left(this.self.err); + } + filter(mapper) { + if ((0, exports2.isLeft)(this.self)) + return _Either.left(this.self.err); + return _Either.fromFailable(() => this.right().filter(mapper).get()); + } + async flatMapAsync(mapper) { + if ((0, exports2.isLeft)(this.self)) + return Promise.resolve(_Either.left(this.self.err)); + return await mapper(this.self.ok).catch((err) => _Either.left(err)); + } + fold(leftFolder, rightFolder) { + if ((0, exports2.isLeft)(this.self)) + return leftFolder(this.self.err); + return rightFolder(this.self.ok); + } + left() { + if ((0, exports2.isLeft)(this.self)) + return pengueno_1.Optional.from(this.self.err); + return pengueno_1.Optional.none(); + } + right() { + if ((0, exports2.isRight)(this.self)) + return pengueno_1.Optional.from(this.self.ok); + return pengueno_1.Optional.none(); + } + joinRight(other, mapper) { + return this.flatMap((t) => other.mapRight((o) => mapper(o, t))); + } + joinRightAsync(other, mapper) { + return this.flatMapAsync(async (t) => { + const o = typeof other === "function" ? other() : other; + return await o.then((other2) => other2.mapRight((o2) => mapper(o2, t))); + }); + } + swap() { + if ((0, exports2.isRight)(this.self)) + return _Either.left(this.self.ok); + return _Either.right(this.self.err); + } + static left(e) { + return new _Either({ err: e, _tag: ELeftTag }); + } + static right(t) { + return new _Either({ ok: t, _tag: ERightTag }); + } + static fromFailable(s) { + try { + return _Either.right(s()); + } catch (e) { + return _Either.left(e); + } + } + static async fromFailableAsync(s) { + return await (typeof s === "function" ? s() : s).then((t) => _Either.right(t)).catch((e) => _Either.left(e)); + } + static async retrying(s, attempts = 3, interval = (attempt) => _Either.attemptWait(attempt)) { + let result = _Either.right(new Error("No attempts made")); + for (let attempt = 0; attempt < attempts && result.right().present(); attempt++) { + await interval(attempt); + const currentAttempt = await s().then((s2) => s2.swap()); + result = await result.joinRightAsync(() => Promise.resolve(currentAttempt), (res, _prevError) => res); + } + return result.swap(); + } + static attemptWait(attempt, backoffFactor = 500, jitter = 300, exponent = 1.3) { + if (attempt === 0) { + return Promise.resolve(); + } + const wait = Math.pow(exponent, attempt) * backoffFactor + jitter * Math.random() * Math.pow(exponent, attempt); + return new Promise((res) => setTimeout(res, wait)); + } + }; + exports2.Either = Either2; + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/fn/index.js +var require_fn = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/fn/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_callable(), exports2); + __exportStar(require_optional(), exports2); + __exportStar(require_either(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/collections/cons.js +var require_cons = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/collections/cons.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.ListZipper = exports2.Cons = void 0; + var pengueno_1 = require_dist2(); + var Cons = class _Cons { + value; + next; + constructor(value, next = pengueno_1.Optional.none()) { + this.value = value; + this.next = next; + } + before(head) { + return new _Cons(this.value, head); + } + replace(_value) { + return new _Cons(_value, this.next); + } + *[Symbol.iterator]() { + for (let cur = pengueno_1.Optional.some(this); cur.present(); cur = cur.flatMap((cur2) => cur2.next)) { + yield cur.get().value; + } + } + static addOnto(items, tail) { + return Array.from(items).reverse().reduce((cons, value) => pengueno_1.Optional.from(new _Cons(value, cons)), tail); + } + static from(items) { + return _Cons.addOnto(items, pengueno_1.Optional.none()); + } + }; + exports2.Cons = Cons; + var ListZipper = class _ListZipper { + reversedPathToHead; + currentHead; + constructor(reversedPathToHead, currentHead) { + this.reversedPathToHead = reversedPathToHead; + this.currentHead = currentHead; + } + read() { + return this.currentHead.map(({ value }) => value); + } + next() { + return this.currentHead.map((head) => new _ListZipper(pengueno_1.Optional.some(head.before(this.reversedPathToHead)), head.next)); + } + previous() { + return this.reversedPathToHead.map((lastVisited) => new _ListZipper(lastVisited.next, pengueno_1.Optional.some(lastVisited.before(this.currentHead)))); + } + prependChunk(values) { + return new _ListZipper(Cons.addOnto(Array.from(values).reverse(), this.reversedPathToHead), this.currentHead); + } + prepend(value) { + return this.prependChunk([value]); + } + remove() { + const newHead = this.currentHead.flatMap((right) => right.next); + return new _ListZipper(this.reversedPathToHead, newHead); + } + replace(value) { + const newHead = this.currentHead.map((right) => right.replace(value)); + return new _ListZipper(this.reversedPathToHead, newHead); + } + *[Symbol.iterator]() { + let head = this; + for (let prev = head.previous(); prev.present(); prev = prev.flatMap((p) => p.previous())) { + head = prev.get(); + } + if (head.currentHead.present()) + yield* head.currentHead.get(); + } + collection() { + return Array.from(this); + } + static from(iterable) { + return new _ListZipper(pengueno_1.Optional.none(), Cons.from(iterable)); + } + }; + exports2.ListZipper = ListZipper; + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/collections/jsonds.js +var require_jsonds = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/collections/jsonds.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.JSONHashMap = exports2.JSONSet = void 0; + var JSONSet = class { + items; + constructor(items = /* @__PURE__ */ new Set()) { + this.items = items; + } + add(item) { + const itemJson = JSON.stringify(item, Object.keys(item).sort()); + this.items.add(itemJson); + } + has(item) { + const itemJson = JSON.stringify(item, Object.keys(item).sort()); + return this.items.has(itemJson); + } + delete(item) { + const itemJson = JSON.stringify(item, Object.keys(item).sort()); + return this.items.delete(itemJson); + } + clear() { + this.items.clear(); + } + size() { + return this.items.size; + } + }; + exports2.JSONSet = JSONSet; + var JSONHashMap = class { + map; + constructor(map = /* @__PURE__ */ new Map()) { + this.map = map; + } + set(key, value) { + const keyJson = JSON.stringify(key, Object.keys(key).sort()); + this.map.set(keyJson, value); + } + get(key) { + const keyJson = JSON.stringify(key, Object.keys(key).sort()); + return this.map.get(keyJson); + } + has(key) { + const keyJson = JSON.stringify(key, Object.keys(key).sort()); + return this.map.has(keyJson); + } + keys() { + return Array.from(this.map.keys()).map((x) => JSON.parse(x)); + } + delete(key) { + const keyJson = JSON.stringify(key, Object.keys(key).sort()); + return this.map.delete(keyJson); + } + clear() { + this.map.clear(); + } + size() { + return this.map.size; + } + }; + exports2.JSONHashMap = JSONHashMap; + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/collections/index.js +var require_collections = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/collections/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_cons(), exports2); + __exportStar(require_jsonds(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/types/index.js +var require_types = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/types/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_misc(), exports2); + __exportStar(require_object(), exports2); + __exportStar(require_tagged(), exports2); + __exportStar(require_fn(), exports2); + __exportStar(require_collections(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/itrace.js +var require_itrace = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/itrace.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.TraceableImpl = void 0; + var TraceableImpl = class _TraceableImpl { + item; + trace; + constructor(item, trace) { + this.item = item; + this.trace = trace; + } + map(mapper) { + const result = mapper(this); + return new _TraceableImpl(result, this.trace); + } + coExtend(mapper) { + const results = mapper(this); + return Array.from(results).map((result) => this.move(result)); + } + flatMap(mapper) { + return mapper(this); + } + flatMapAsync(mapper) { + return new _TraceableImpl(mapper(this).then((t) => t.get()), this.trace); + } + traceScope(mapper) { + return new _TraceableImpl(this.get(), this.trace.traceScope(mapper(this))); + } + peek(peek) { + peek(this); + return this; + } + move(t) { + return this.map(() => t); + } + bimap(mapper) { + const { item, trace: _trace } = mapper(this); + return this.move(item).traceScope(() => _trace); + } + get() { + return this.item; + } + }; + exports2.TraceableImpl = TraceableImpl; + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/metric/emittable.js +var require_emittable = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/metric/emittable.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.EmittableMetric = void 0; + var index_1 = require_metric2(); + var EmittableMetric = class { + name; + unit; + constructor(name, unit) { + this.name = name; + this.unit = unit; + } + withValue(value) { + return { + name: this.name, + unit: this.unit, + emissionTimestamp: Date.now(), + value, + _tag: index_1.MetricValueTag + }; + } + }; + exports2.EmittableMetric = EmittableMetric; + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/metric/metric.js +var require_metric = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/metric/metric.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.ResultMetric = exports2.Metric = void 0; + var index_1 = require_metric2(); + var _Tagged = class { + _tag; + constructor(_tag = index_1.IMetricTag) { + this._tag = _tag; + } + }; + var Metric = class _Metric extends _Tagged { + name; + parent; + count; + time; + static DELIM = "."; + constructor(name, parent = void 0, count = new index_1.EmittableMetric(_Metric.join(name, "count"), index_1.Unit.COUNT), time = new index_1.EmittableMetric(_Metric.join(name, "time"), index_1.Unit.MILLISECONDS)) { + super(); + this.name = name; + this.parent = parent; + this.count = count; + this.time = time; + } + child(_name) { + const childName = _Metric.join(this.name, _name); + return new _Metric(childName, this); + } + asResult() { + return ResultMetric.from(this); + } + static join(...name) { + return name.join(_Metric.DELIM); + } + static fromName(name) { + return new _Metric(name); + } + }; + exports2.Metric = Metric; + var ResultMetric = class _ResultMetric extends Metric { + name; + parent; + failure; + success; + warn; + constructor(name, parent = void 0, failure, success, warn) { + super(name, parent); + this.name = name; + this.parent = parent; + this.failure = failure; + this.success = success; + this.warn = warn; + } + static from(metric) { + const failure = metric.child("failure"); + const success = metric.child("success"); + const warn = metric.child("warn"); + return new _ResultMetric(metric.name, metric.parent, failure, success, warn); + } + }; + exports2.ResultMetric = ResultMetric; + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/metric/trace.js +var require_trace = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/metric/trace.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.MetricsTrace = exports2.isMetricsTraceSupplier = void 0; + var pengueno_1 = require_dist2(); + var isMetricsTraceSupplier = (t) => (0, pengueno_1.isMetricValue)(t) || (0, pengueno_1.isIMetric)(t) || Array.isArray(t) && t.every((_m) => (0, pengueno_1.isMetricValue)(_m) || (0, pengueno_1.isIMetric)(_m)); + exports2.isMetricsTraceSupplier = isMetricsTraceSupplier; + var MetricsTrace = class _MetricsTrace { + metricConsumer; + activeTraces; + completedTraces; + constructor(metricConsumer, activeTraces = /* @__PURE__ */ new Map(), completedTraces = /* @__PURE__ */ new Set()) { + this.metricConsumer = metricConsumer; + this.activeTraces = activeTraces; + this.completedTraces = completedTraces; + } + traceScope(trace) { + const now = Date.now(); + const metricsToTrace = (Array.isArray(trace) ? trace : [trace]).filter(pengueno_1.isIMetric); + const initialTraces = new Map(metricsToTrace.map((metric) => [metric.name, now])); + return new _MetricsTrace(this.metricConsumer, initialTraces, this.completedTraces); + } + trace(metrics) { + if (!metrics || typeof metrics === "string") { + return this; + } + const now = Date.now(); + const allMetrics = Array.isArray(metrics) ? metrics : [metrics]; + const valuesToEmit = allMetrics.filter(pengueno_1.isMetricValue); + const traceableMetrics = allMetrics.filter(pengueno_1.isIMetric); + const metricsToStart = traceableMetrics.filter((m) => !this.activeTraces.has(m.name)); + const metricsToEnd = traceableMetrics.filter((m) => this.activeTraces.has(m.name) && !this.completedTraces.has(m.name)); + const endedMetricValues = metricsToEnd.flatMap((metric) => [ + metric.count.withValue(1), + metric.time.withValue(now - this.activeTraces.get(metric.name)) + ]); + const parentBasedMetrics = metricsToStart.filter((metric) => { + const parent = metric.parent; + return parent && this.activeTraces.has(parent.name); + }); + const parentBasedValues = parentBasedMetrics.flatMap((metric) => { + const parentStart = this.activeTraces.get(metric.parent.name); + return [metric.count.withValue(1), metric.time.withValue(now - parentStart)]; + }); + const allMetricsToEmit = [...valuesToEmit, ...endedMetricValues, ...parentBasedValues]; + if (allMetricsToEmit.length > 0) { + this.metricConsumer(allMetricsToEmit); + } + const nextActiveTraces = new Map([ + ...this.activeTraces, + ...metricsToStart.map((m) => [m.name, now]) + ]); + const nextCompletedTraces = /* @__PURE__ */ new Set([ + ...this.completedTraces, + ...metricsToEnd.map((m) => m.name), + ...parentBasedMetrics.map((m) => m.name) + ]); + return new _MetricsTrace(this.metricConsumer, nextActiveTraces, nextCompletedTraces); + } + }; + exports2.MetricsTrace = MetricsTrace; + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/metric/index.js +var require_metric2 = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/metric/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.isIMetric = exports2.IMetricTag = exports2.isMetricValue = exports2.MetricValueTag = exports2.Unit = void 0; + var pengueno_1 = require_dist2(); + var Unit; + (function(Unit2) { + Unit2["COUNT"] = "COUNT"; + Unit2["MILLISECONDS"] = "MILLISECONDS"; + })(Unit || (exports2.Unit = Unit = {})); + exports2.MetricValueTag = "MetricValue"; + var isMetricValue = (t) => (0, pengueno_1.isTagged)(t, exports2.MetricValueTag); + exports2.isMetricValue = isMetricValue; + exports2.IMetricTag = "IMetric"; + var isIMetric = (t) => (0, pengueno_1.isTagged)(t, exports2.IMetricTag); + exports2.isIMetric = isIMetric; + __exportStar(require_emittable(), exports2); + __exportStar(require_metric(), exports2); + __exportStar(require_trace(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/log/ansi.js +var require_ansi = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/log/ansi.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.ANSI = void 0; + exports2.ANSI = { + RESET: "\x1B[0m", + BOLD: "\x1B[1m", + DIM: "\x1B[2m", + RED: "\x1B[31m", + GREEN: "\x1B[32m", + YELLOW: "\x1B[33m", + BLUE: "\x1B[34m", + MAGENTA: "\x1B[35m", + CYAN: "\x1B[36m", + WHITE: "\x1B[37m", + BRIGHT_RED: "\x1B[91m", + BRIGHT_YELLOW: "\x1B[93m", + GRAY: "\x1B[90m" + }; + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/log/level.js +var require_level = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/log/level.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.isLogLevel = exports2.logLevelOrder = exports2.LogLevel = void 0; + var LogLevel; + (function(LogLevel2) { + LogLevel2["UNKNOWN"] = "UNKNOWN"; + LogLevel2["INFO"] = "INFO"; + LogLevel2["WARN"] = "WARN"; + LogLevel2["DEBUG"] = "DEBUG"; + LogLevel2["ERROR"] = "ERROR"; + LogLevel2["SYS"] = "SYS"; + })(LogLevel || (exports2.LogLevel = LogLevel = {})); + exports2.logLevelOrder = [ + LogLevel.DEBUG, + LogLevel.INFO, + LogLevel.WARN, + LogLevel.ERROR, + LogLevel.SYS + ]; + var isLogLevel = (l) => typeof l === "string" && exports2.logLevelOrder.some((level) => level === l); + exports2.isLogLevel = isLogLevel; + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/log/logger.js +var require_logger = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/log/logger.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/log/pretty_json_console.js +var require_pretty_json_console = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/log/pretty_json_console.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PrettyJsonConsoleLogger = void 0; + var index_1 = require_log(); + var PrettyJsonConsoleLogger = class { + log(level, ...trace) { + const message = JSON.stringify({ + level, + trace + }, null, 4); + const styled = `${this.getStyle(level)}${message}${index_1.ANSI.RESET} +`; + this.getStream(level)(styled); + } + getStream(level) { + if (level === index_1.LogLevel.ERROR) { + return console.error; + } + return console.log; + } + getStyle(level) { + switch (level) { + case index_1.LogLevel.UNKNOWN: + case index_1.LogLevel.INFO: + return `${index_1.ANSI.MAGENTA}`; + case index_1.LogLevel.DEBUG: + return `${index_1.ANSI.CYAN}`; + case index_1.LogLevel.WARN: + return `${index_1.ANSI.BRIGHT_YELLOW}`; + case index_1.LogLevel.ERROR: + return `${index_1.ANSI.BRIGHT_RED}`; + case index_1.LogLevel.SYS: + return `${index_1.ANSI.DIM}${index_1.ANSI.BLUE}`; + } + } + }; + exports2.PrettyJsonConsoleLogger = PrettyJsonConsoleLogger; + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/log/trace.js +var require_trace2 = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/log/trace.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.LogTrace = void 0; + var pengueno_1 = require_dist2(); + var index_1 = require_log(); + var LogTrace = class _LogTrace { + logger; + traces; + defaultLevel; + allowedLevels; + constructor(logger = new index_1.PrettyJsonConsoleLogger(), traces = [defaultTrace], defaultLevel = index_1.LogLevel.INFO, allowedLevels = defaultAllowedLevelsSupplier) { + this.logger = logger; + this.traces = traces; + this.defaultLevel = defaultLevel; + this.allowedLevels = allowedLevels; + } + traceScope(trace) { + return new _LogTrace(this.logger, this.traces.concat(trace), this.defaultLevel, this.allowedLevels); + } + trace(trace) { + const { traces, level: _level } = this.foldTraces(this.traces.concat(trace)); + if (!this.allowedLevels().has(_level)) + return; + const level = _level === index_1.LogLevel.UNKNOWN ? this.defaultLevel : _level; + this.logger.log(level, ...traces); + } + foldTraces(_traces) { + const _logTraces = _traces.map((trace) => typeof trace === "function" ? trace() : trace); + const _level = _logTraces.filter((trace) => (0, index_1.isLogLevel)(trace)).reduce((acc, level2) => Math.max(index_1.logLevelOrder.indexOf(level2), acc), -1); + const level = index_1.logLevelOrder[_level] ?? index_1.LogLevel.UNKNOWN; + const traces = _logTraces.filter((trace) => !(0, index_1.isLogLevel)(trace)).map((trace) => { + if (typeof trace === "object") { + return `TracedException.Name = ${trace.name}, TracedException.Message = ${trace.message}, TracedException.Stack = ${trace.stack}`; + } + return trace; + }); + return { + level, + traces + }; + } + }; + exports2.LogTrace = LogTrace; + var defaultTrace = () => `TimeStamp = ${(/* @__PURE__ */ new Date()).toISOString()}`; + var defaultAllowedLevels = (0, pengueno_1.memoize)((isDebug) => /* @__PURE__ */ new Set([ + index_1.LogLevel.UNKNOWN, + ...isDebug ? [index_1.LogLevel.DEBUG] : [], + index_1.LogLevel.INFO, + index_1.LogLevel.WARN, + index_1.LogLevel.ERROR, + index_1.LogLevel.SYS + ])); + var defaultAllowedLevelsSupplier = () => defaultAllowedLevels((0, pengueno_1.isDebug)()); + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/log/index.js +var require_log = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/log/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_ansi(), exports2); + __exportStar(require_level(), exports2); + __exportStar(require_logger(), exports2); + __exportStar(require_pretty_json_console(), exports2); + __exportStar(require_trace2(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/trace.js +var require_trace3 = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/trace.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.LogMetricTraceable = exports2.LogMetricTrace = exports2.EmbeddedMetricsTraceable = exports2.LogTraceable = void 0; + var _1 = require_trace4(); + var LogTraceable = class _LogTraceable extends _1.TraceableImpl { + static LogTrace = new _1.LogTrace(); + static of(t) { + return new _LogTraceable(t, _LogTraceable.LogTrace); + } + }; + exports2.LogTraceable = LogTraceable; + var getEmbeddedMetricConsumer = (logTrace) => (metrics) => { + if (metrics.length === 0) + return; + logTrace.traceScope(_1.LogLevel.SYS).trace(`Metrics = ${JSON.stringify(metrics)}`); + }; + var EmbeddedMetricsTraceable = class _EmbeddedMetricsTraceable extends _1.TraceableImpl { + static MetricsTrace = new _1.MetricsTrace(getEmbeddedMetricConsumer(LogTraceable.LogTrace)); + static of(t, metricsTrace = _EmbeddedMetricsTraceable.MetricsTrace) { + return new _EmbeddedMetricsTraceable(t, metricsTrace); + } + }; + exports2.EmbeddedMetricsTraceable = EmbeddedMetricsTraceable; + var LogMetricTrace = class _LogMetricTrace { + logTrace; + metricsTrace; + constructor(logTrace, metricsTrace) { + this.logTrace = logTrace; + this.metricsTrace = metricsTrace; + } + traceScope(trace) { + if ((0, _1.isMetricsTraceSupplier)(trace)) { + return new _LogMetricTrace(this.logTrace, this.metricsTrace.traceScope(trace)); + } + return new _LogMetricTrace(this.logTrace.traceScope(trace), this.metricsTrace); + } + trace(trace) { + if ((0, _1.isMetricsTraceSupplier)(trace)) { + this.metricsTrace.trace(trace); + return this; + } + this.logTrace.trace(trace); + return this; + } + }; + exports2.LogMetricTrace = LogMetricTrace; + var LogMetricTraceable = class _LogMetricTraceable extends _1.TraceableImpl { + static ofLogTraceable(t) { + const metricsTrace = new _1.MetricsTrace(getEmbeddedMetricConsumer(t.trace)); + return new _LogMetricTraceable(t.get(), new LogMetricTrace(t.trace, metricsTrace)); + } + static of(t) { + const logTrace = LogTraceable.of(t); + return _LogMetricTraceable.ofLogTraceable(logTrace); + } + }; + exports2.LogMetricTraceable = LogMetricTraceable; + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/util.js +var require_util = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/util.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.TraceUtil = void 0; + var pengueno_1 = require_dist2(); + var TraceUtil = class _TraceUtil { + static promiseify(mapper) { + return (traceablePromise) => traceablePromise.flatMapAsync(async (t) => t.move(await t.get()).map(mapper)).get(); + } + static traceResultingEither(metric, warnOnFailure = false) { + return (t) => { + if (metric) + t.trace.trace(t.get().fold((_err) => warnOnFailure ? metric.warn : metric.failure, (_ok) => metric.success)); + return t.traceScope((_t) => _t.get().fold((_err) => warnOnFailure ? pengueno_1.LogLevel.WARN : pengueno_1.LogLevel.ERROR, (_ok) => pengueno_1.LogLevel.INFO)); + }; + } + static withTrace(trace) { + return (t) => t.traceScope(() => trace); + } + static withMetricTrace(metric) { + return _TraceUtil.withTrace(metric); + } + static withFunctionTrace(f) { + return _TraceUtil.withTrace(`fn.${f.name}`); + } + static withClassTrace(c) { + return _TraceUtil.withTrace(`class.${c.constructor.name}`); + } + }; + exports2.TraceUtil = TraceUtil; + } +}); + +// node_modules/@emprespresso/pengueno/dist/trace/index.js +var require_trace4 = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/trace/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_itrace(), exports2); + __exportStar(require_metric2(), exports2); + __exportStar(require_log(), exports2); + __exportStar(require_trace3(), exports2); + __exportStar(require_util(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/process/exec.js +var require_exec = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/process/exec.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.getStdoutMany = exports2.getStdout = exports2.CmdMetric = void 0; + var pengueno_1 = require_dist2(); + var node_child_process_1 = require("node:child_process"); + exports2.CmdMetric = pengueno_1.Metric.fromName("Exec").asResult(); + var getStdout = (cmd, options = { streamTraceable: [] }) => cmd.flatMap(pengueno_1.TraceUtil.withFunctionTrace(exports2.getStdout)).flatMap((tCmd) => tCmd.traceScope(() => `Command = ${tCmd.get()}`)).map((tCmd) => { + const cmd2 = tCmd.get(); + const _exec = typeof cmd2 === "string" ? cmd2 : cmd2.join(" "); + const env = options.clearEnv ? options.env : { ...process.env, ...options.env }; + return pengueno_1.Either.fromFailableAsync(new Promise((res, rej) => { + const proc = (0, node_child_process_1.exec)(_exec, { env }); + let stdout = ""; + proc.stdout?.on("data", (d) => { + const s = d.toString(); + stdout += s; + if (options.streamTraceable?.includes("stdout")) { + tCmd.trace.trace(s); + } + }); + const stderr = ""; + proc.stderr?.on("data", (d) => { + const s = d.toString(); + stdout += s; + if (options.streamTraceable?.includes("stderr")) { + tCmd.trace.trace(s); + } + }); + proc.on("exit", (code) => { + const streams = { stdout, stderr }; + if (code === 0) { + res(streams); + } else { + rej(new Error(`exited with non-zero code: ${code}. ${stderr}`)); + } + }); + })); + }).map(pengueno_1.TraceUtil.promiseify((tEitherStdStreams) => tEitherStdStreams.get().mapRight(({ stderr, stdout }) => { + if (stderr) + tEitherStdStreams.trace.traceScope(pengueno_1.LogLevel.DEBUG).trace(`StdErr = ${stderr}`); + return stdout; + }))).peek(pengueno_1.TraceUtil.promiseify(pengueno_1.TraceUtil.traceResultingEither(exports2.CmdMetric))).get(); + exports2.getStdout = getStdout; + var getStdoutMany = (cmds, options = { streamTraceable: [] }) => cmds.coExtend((t) => t.get()).reduce(async (_result, tCmd) => { + const result = await _result; + return result.joinRightAsync(() => tCmd.map((cmd) => (0, exports2.getStdout)(cmd, options)).get(), (stdout, pre) => pre.concat(stdout)); + }, Promise.resolve(pengueno_1.Either.right([]))); + exports2.getStdoutMany = getStdoutMany; + } +}); + +// node_modules/@emprespresso/pengueno/dist/process/env.js +var require_env = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/process/env.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.getRequiredEnvVars = exports2.getRequiredEnv = exports2.getEnv = void 0; + var pengueno_1 = require_dist2(); + var getEnv = (name) => pengueno_1.Optional.from(process.env[name]); + exports2.getEnv = getEnv; + var getRequiredEnv = (name) => pengueno_1.Either.fromFailable(() => (0, exports2.getEnv)(name).get()).mapLeft(() => new Error(`environment variable "${name}" is required D:`)); + exports2.getRequiredEnv = getRequiredEnv; + var getRequiredEnvVars = (vars) => { + const emptyEnvironment = pengueno_1.Either.right({}); + const addTo = (env, key, val) => ({ + ...env, + [key]: val + }); + return vars.reduce((environment, key) => environment.joinRight((0, exports2.getRequiredEnv)(key), (value, environment2) => addTo(environment2, key, value)), emptyEnvironment); + }; + exports2.getRequiredEnvVars = getRequiredEnvVars; + } +}); + +// node_modules/@emprespresso/pengueno/dist/process/validate_identifier.js +var require_validate_identifier = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/process/validate_identifier.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.validateExecutionEntries = exports2.validateIdentifier = void 0; + var pengueno_1 = require_dist2(); + var validateIdentifier = (token) => { + return /^[a-zA-Z0-9_\?\&\=\-:. \/]+$/.test(token) && !token.includes(".."); + }; + exports2.validateIdentifier = validateIdentifier; + var validateExecutionEntries = (obj) => { + const invalidEntries = Object.entries(obj).filter((e) => !e.every((x) => typeof x === "string" && (0, exports2.validateIdentifier)(x))); + if (invalidEntries.length > 0) + return pengueno_1.Either.left(invalidEntries); + return pengueno_1.Either.right(obj); + }; + exports2.validateExecutionEntries = validateExecutionEntries; + } +}); + +// node_modules/@emprespresso/pengueno/dist/process/argv.js +var require_argv = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/process/argv.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.argv = exports2.getArg = exports2.isArgKey = void 0; + var pengueno_1 = require_dist2(); + var isArgKey = (k) => k.startsWith("--"); + exports2.isArgKey = isArgKey; + var getArg = (arg, argv2, whenValue) => { + const argIndex = pengueno_1.Optional.from(argv2.findIndex((_argv) => (0, exports2.isArgKey)(_argv) && _argv.split("=")[0] === arg)).filter((index) => index >= 0 && index < argv2.length); + if (!argIndex.present()) { + return pengueno_1.Optional.from(whenValue.absent).map((v) => pengueno_1.Either.right(v)).orSome(() => pengueno_1.Either.left(new Error(`arg ${arg} is not present in arguments list and does not have an 'absent' value`))).get(); + } + return argIndex.flatMap((idx) => pengueno_1.Optional.from(argv2.at(idx)).map((_argv) => _argv.includes("=") ? _argv.split("=")[1] : argv2.at(idx + 1))).filter((next) => !(0, exports2.isArgKey)(next)).map((next) => whenValue.present(next)).orSome(() => whenValue.unspecified).map((v) => pengueno_1.Either.right(v)).get(); + }; + exports2.getArg = getArg; + var argv = (args, handlers, argv2 = process.argv.slice(2)) => { + const defaultHandler = { present: (value) => value }; + const processArg = (arg) => { + const handler = handlers?.[arg] ?? defaultHandler; + return (0, exports2.getArg)(arg, argv2, handler).mapRight((value) => [arg, value]); + }; + const res = args.map(processArg).reduce((acc, current) => acc.flatMap((accValue) => current.mapRight(([key, value]) => ({ + ...accValue, + [key]: value + }))), pengueno_1.Either.right({})).mapRight((result) => result); + return res; + }; + exports2.argv = argv; + } +}); + +// node_modules/@emprespresso/pengueno/dist/process/signals.js +var require_signals = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/process/signals.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.Signals = exports2.SigTermMetric = exports2.SigIntMetric = void 0; + var pengueno_1 = require_dist2(); + exports2.SigIntMetric = pengueno_1.Metric.fromName("SigInt").asResult(); + exports2.SigTermMetric = pengueno_1.Metric.fromName("SigTerm").asResult(); + var Signals = class { + static async awaitClose(t) { + const success = pengueno_1.Either.right(void 0); + return new Promise((res) => { + const metricizedInterruptHandler = (metric) => (err) => t.flatMap(pengueno_1.TraceUtil.withMetricTrace(metric)).peek((_t) => _t.trace.trace("closing")).move(pengueno_1.Optional.from(err).map((e) => pengueno_1.Either.left(e)).orSome(() => success).get()).flatMap(pengueno_1.TraceUtil.traceResultingEither(metric)).map((e) => res(e.get())).peek((_t) => _t.trace.trace("finished")).get(); + const sigintCloser = metricizedInterruptHandler(exports2.SigIntMetric); + const sigtermCloser = metricizedInterruptHandler(exports2.SigTermMetric); + process.on("SIGINT", () => t.flatMap(pengueno_1.TraceUtil.withTrace("SIGINT")).get().close(sigintCloser)); + process.on("SIGTERM", () => t.flatMap(pengueno_1.TraceUtil.withTrace("SIGTERM")).get().close(sigtermCloser)); + }); + } + }; + exports2.Signals = Signals; + } +}); + +// node_modules/@emprespresso/pengueno/dist/process/index.js +var require_process = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/process/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_exec(), exports2); + __exportStar(require_env(), exports2); + __exportStar(require_validate_identifier(), exports2); + __exportStar(require_argv(), exports2); + __exportStar(require_signals(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/http/body.js +var require_body = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/http/body.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/http/status.js +var require_status = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/http/status.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.HttpStatusCodes = void 0; + exports2.HttpStatusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing (WebDAV)", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status (WebDAV)", + 208: "Already Reported (WebDAV)", + 226: "IM Used", + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 306: "(Unused)", + 307: "Temporary Redirect", + 308: "Permanent Redirect (experimental)", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot (RFC 2324)", + 420: "Enhance Your Calm (Twitter)", + 422: "Unprocessable Entity (WebDAV)", + 423: "Locked (WebDAV)", + 424: "Failed Dependency (WebDAV)", + 425: "Reserved for WebDAV", + 426: "Upgrade Required", + 428: "Precondition Required", + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 444: "No Response (Nginx)", + 449: "Retry With (Microsoft)", + 450: "Blocked by Windows Parental Controls (Microsoft)", + 451: "Unavailable For Legal Reasons", + 499: "Client Closed Request (Nginx)", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates (Experimental)", + 507: "Insufficient Storage (WebDAV)", + 508: "Loop Detected (WebDAV)", + 509: "Bandwidth Limit Exceeded (Apache)", + 510: "Not Extended", + 511: "Network Authentication Required", + 598: "Network read timeout error", + 599: "Network connect timeout error" + }; + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/http/method.js +var require_method = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/http/method.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/http/index.js +var require_http = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/http/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_body(), exports2); + __exportStar(require_status(), exports2); + __exportStar(require_method(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/response/pengueno.js +var require_pengueno = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/response/pengueno.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.JsonResponse = exports2.PenguenoResponse = exports2.getResponseMetrics = void 0; + var pengueno_1 = require_dist2(); + var getHeaders = (req, extraHeaders) => { + const optHeaders = { + ...req.getResponseHeaders(), + ...extraHeaders + }; + optHeaders["Content-Type"] = (optHeaders["Content-Type"] ?? "text/plain") + "; charset=utf-8"; + return optHeaders; + }; + var ResponseCodeMetrics = [0, 1, 2, 3, 4, 5].map((x) => pengueno_1.Metric.fromName(`response.${x}xx`).asResult()); + var getResponseMetrics = (status, elapsedMs) => { + const index = Math.floor(status / 100); + return ResponseCodeMetrics.flatMap((metric, i) => pengueno_1.Optional.from(i).filter((i2) => i2 === index).map(() => [metric.count.withValue(1)]).flatMap((metricValues) => pengueno_1.Optional.from(elapsedMs).map((ms) => metricValues.concat(metric.time.withValue(ms))).orSome(() => metricValues)).orSome(() => [metric.count.withValue(0)]).get()); + }; + exports2.getResponseMetrics = getResponseMetrics; + var PenguenoResponse = class { + _body; + statusText; + status; + headers; + constructor(req, _body, opts) { + this._body = _body; + this.headers = getHeaders(req.get(), opts?.headers ?? {}); + this.status = opts.status; + this.statusText = opts.statusText ?? pengueno_1.HttpStatusCodes[this.status]; + req.trace.trace((0, exports2.getResponseMetrics)(opts.status, req.get().elapsedTimeMs())); + } + body() { + return this._body; + } + }; + exports2.PenguenoResponse = PenguenoResponse; + var JsonResponse = class extends PenguenoResponse { + constructor(req, e, _opts) { + const opts = { ..._opts, headers: { ..._opts.headers, "Content-Type": "application/json" } }; + if ((0, pengueno_1.isEither)(e)) { + super(req, JSON.stringify(e.fold((error) => ({ error, ok: void 0 }), (ok) => ({ ok }))), opts); + return; + } + super(req, JSON.stringify(Math.floor(opts.status / 100) > 4 ? { error: e } : { ok: e }), opts); + } + }; + exports2.JsonResponse = JsonResponse; + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/response/index.js +var require_response = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/response/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_pengueno(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/request/pengueno.js +var require_pengueno2 = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/request/pengueno.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PenguenoRequest = void 0; + var greetings = ["hewwo :D", "hiya cutie", "boop!", "sending virtual hugs!", "stay pawsitive"]; + var penguenoGreeting = () => greetings[Math.floor(Math.random() * greetings.length)]; + var PenguenoRequest = class _PenguenoRequest { + req; + id; + at; + constructor(req, id, at) { + this.req = req; + this.id = id; + this.at = at; + } + elapsedTimeMs(after = () => Date.now()) { + return after() - this.at.getTime(); + } + getResponseHeaders() { + const RequestId = this.id; + const RequestReceivedUnix = this.at.getTime(); + const RequestHandleUnix = Date.now(); + const DeltaUnix = this.elapsedTimeMs(() => RequestHandleUnix); + const Hai = penguenoGreeting(); + return Object.entries({ + RequestId, + RequestReceivedUnix, + RequestHandleUnix, + DeltaUnix, + Hai + }).reduce((acc, [key, val]) => ({ ...acc, [key]: val.toString() }), {}); + } + static from(request) { + const id = crypto.randomUUID(); + return request.bimap((tRequest) => { + const request2 = tRequest.get(); + const url = new URL(request2.url); + const { pathname } = url; + const trace = `RequestId = ${id}, Method = ${request2.method}, Path = ${pathname}`; + return { item: new _PenguenoRequest(request2, id, /* @__PURE__ */ new Date()), trace }; + }); + } + }; + exports2.PenguenoRequest = PenguenoRequest; + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/request/index.js +var require_request = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/request/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_pengueno2(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/activity/health.js +var require_health = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/activity/health.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.HealthCheckActivityImpl = exports2.HealthCheckOutput = exports2.HealthCheckInput = void 0; + var pengueno_1 = require_dist2(); + var HealthCheckInput; + (function(HealthCheckInput2) { + HealthCheckInput2[HealthCheckInput2["CHECK"] = 0] = "CHECK"; + })(HealthCheckInput || (exports2.HealthCheckInput = HealthCheckInput = {})); + var HealthCheckOutput; + (function(HealthCheckOutput2) { + HealthCheckOutput2[HealthCheckOutput2["YAASSSLAYQUEEN"] = 0] = "YAASSSLAYQUEEN"; + })(HealthCheckOutput || (exports2.HealthCheckOutput = HealthCheckOutput = {})); + var healthCheckMetric = pengueno_1.Metric.fromName("Health").asResult(); + var HealthCheckActivityImpl = class { + check; + constructor(check) { + this.check = check; + } + checkHealth(req) { + return req.flatMap(pengueno_1.TraceUtil.withFunctionTrace(this.checkHealth)).flatMap(pengueno_1.TraceUtil.withMetricTrace(healthCheckMetric)).flatMap((r) => r.move(HealthCheckInput.CHECK).map((input) => this.check(input))).peek(pengueno_1.TraceUtil.promiseify(pengueno_1.TraceUtil.traceResultingEither(healthCheckMetric))).map(pengueno_1.TraceUtil.promiseify((h) => { + const { status, message } = h.get().fold(() => ({ status: 500, message: "err" }), () => ({ status: 200, message: "ok" })); + return new pengueno_1.JsonResponse(req, message, { status }); + })).get(); + } + }; + exports2.HealthCheckActivityImpl = HealthCheckActivityImpl; + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/activity/fourohfour.js +var require_fourohfour = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/activity/fourohfour.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.FourOhFourActivityImpl = void 0; + var pengueno_1 = require_dist2(); + var messages = [ + "D: meow-t found! your api call ran away!", + "404-bidden! but like...in a cute way >:3 !", + ":< your data went on a paw-sible vacation!", + "uwu~ not found, but found our hearts instead!" + ]; + var randomFourOhFour = () => messages[Math.floor(Math.random() * messages.length)]; + var FourOhFourActivityImpl = class { + fourOhFour(req) { + return req.move(new pengueno_1.JsonResponse(req, randomFourOhFour(), { status: 404 })).map((resp) => Promise.resolve(resp.get())).get(); + } + }; + exports2.FourOhFourActivityImpl = FourOhFourActivityImpl; + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/activity/index.js +var require_activity = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/activity/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_health(), exports2); + __exportStar(require_fourohfour(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/filter/method.js +var require_method2 = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/filter/method.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.requireMethod = void 0; + var pengueno_1 = require_dist2(); + var requireMethod = (methods) => (req) => req.flatMap(pengueno_1.TraceUtil.withFunctionTrace(exports2.requireMethod)).map((t) => { + const { req: { method } } = t.get(); + if (!methods.includes(method)) { + const msg = "that's not how you pet me (\u22DF\uFE4F\u22DE)~"; + t.trace.traceScope(pengueno_1.LogLevel.WARN).trace(msg); + return pengueno_1.Either.left(new pengueno_1.PenguenoError(msg, 405)); + } + return pengueno_1.Either.right(method); + }).get(); + exports2.requireMethod = requireMethod; + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/filter/json.js +var require_json = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/filter/json.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.jsonModel = void 0; + var pengueno_1 = require_dist2(); + var ParseJsonMetric = pengueno_1.Metric.fromName("JsonParse").asResult(); + var jsonModel = (jsonTransformer) => (r) => r.flatMap(pengueno_1.TraceUtil.withFunctionTrace(exports2.jsonModel)).flatMap(pengueno_1.TraceUtil.withMetricTrace(ParseJsonMetric)).map((j) => pengueno_1.Either.fromFailableAsync(j.get().req.json()).then((either) => either.mapLeft((errReason) => { + j.trace.traceScope(pengueno_1.LogLevel.WARN).trace(errReason); + return new pengueno_1.PenguenoError("seems to be invalid JSON (>//<) can you fix?", 400); + }))).flatMapAsync(pengueno_1.TraceUtil.promiseify(pengueno_1.TraceUtil.traceResultingEither(ParseJsonMetric))).map(pengueno_1.TraceUtil.promiseify((traceableEitherJson) => traceableEitherJson.get().mapRight((j) => traceableEitherJson.move(j)).flatMap(jsonTransformer))).get(); + exports2.jsonModel = jsonModel; + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/filter/index.js +var require_filter = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/filter/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PenguenoError = exports2.ErrorSource = void 0; + var pengueno_1 = require_dist2(); + var ErrorSource; + (function(ErrorSource2) { + ErrorSource2["USER"] = "WARN"; + ErrorSource2["SYSTEM"] = "ERROR"; + })(ErrorSource || (exports2.ErrorSource = ErrorSource = {})); + var PenguenoError = class extends Error { + message; + status; + source; + constructor(message, status) { + super(message); + this.message = message; + this.status = status; + this.source = Math.floor(status / 100) === 4 ? ErrorSource.USER : ErrorSource.SYSTEM; + } + }; + exports2.PenguenoError = PenguenoError; + __exportStar(require_method2(), exports2); + __exportStar(require_json(), exports2); + } +}); + +// node_modules/hono/dist/cjs/compose.js +var require_compose = __commonJS({ + "node_modules/hono/dist/cjs/compose.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var compose_exports = {}; + __export(compose_exports, { + compose: () => compose + }); + module2.exports = __toCommonJS(compose_exports); + var compose = (middleware, onError, onNotFound) => { + return (context, next) => { + let index = -1; + return dispatch(0); + async function dispatch(i) { + if (i <= index) { + throw new Error("next() called multiple times"); + } + index = i; + let res; + let isError = false; + let handler; + if (middleware[i]) { + handler = middleware[i][0][0]; + context.req.routeIndex = i; + } else { + handler = i === middleware.length && next || void 0; + } + if (handler) { + try { + res = await handler(context, () => dispatch(i + 1)); + } catch (err) { + if (err instanceof Error && onError) { + context.error = err; + res = await onError(err, context); + isError = true; + } else { + throw err; + } + } + } else { + if (context.finalized === false && onNotFound) { + res = await onNotFound(context); + } + } + if (res && (context.finalized === false || isError)) { + context.res = res; + } + return context; + } + }; + }; + } +}); + +// node_modules/hono/dist/cjs/http-exception.js +var require_http_exception = __commonJS({ + "node_modules/hono/dist/cjs/http-exception.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var http_exception_exports = {}; + __export(http_exception_exports, { + HTTPException: () => HTTPException + }); + module2.exports = __toCommonJS(http_exception_exports); + var HTTPException = class extends Error { + res; + status; + constructor(status = 500, options) { + super(options?.message, { cause: options?.cause }); + this.res = options?.res; + this.status = status; + } + getResponse() { + if (this.res) { + const newResponse = new Response(this.res.body, { + status: this.status, + headers: this.res.headers + }); + return newResponse; + } + return new Response(this.message, { + status: this.status + }); + } + }; + } +}); + +// node_modules/hono/dist/cjs/request/constants.js +var require_constants = __commonJS({ + "node_modules/hono/dist/cjs/request/constants.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var constants_exports = {}; + __export(constants_exports, { + GET_MATCH_RESULT: () => GET_MATCH_RESULT + }); + module2.exports = __toCommonJS(constants_exports); + var GET_MATCH_RESULT = Symbol(); + } +}); + +// node_modules/hono/dist/cjs/utils/body.js +var require_body2 = __commonJS({ + "node_modules/hono/dist/cjs/utils/body.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var body_exports = {}; + __export(body_exports, { + parseBody: () => parseBody + }); + module2.exports = __toCommonJS(body_exports); + var import_request = require_request2(); + var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => { + const { all = false, dot = false } = options; + const headers = request instanceof import_request.HonoRequest ? request.raw.headers : request.headers; + const contentType = headers.get("Content-Type"); + if (contentType?.startsWith("multipart/form-data") || contentType?.startsWith("application/x-www-form-urlencoded")) { + return parseFormData(request, { all, dot }); + } + return {}; + }; + async function parseFormData(request, options) { + const formData = await request.formData(); + if (formData) { + return convertFormDataToBodyData(formData, options); + } + return {}; + } + function convertFormDataToBodyData(formData, options) { + const form = /* @__PURE__ */ Object.create(null); + formData.forEach((value, key) => { + const shouldParseAllValues = options.all || key.endsWith("[]"); + if (!shouldParseAllValues) { + form[key] = value; + } else { + handleParsingAllValues(form, key, value); + } + }); + if (options.dot) { + Object.entries(form).forEach(([key, value]) => { + const shouldParseDotValues = key.includes("."); + if (shouldParseDotValues) { + handleParsingNestedValues(form, key, value); + delete form[key]; + } + }); + } + return form; + } + var handleParsingAllValues = (form, key, value) => { + if (form[key] !== void 0) { + if (Array.isArray(form[key])) { + ; + form[key].push(value); + } else { + form[key] = [form[key], value]; + } + } else { + if (!key.endsWith("[]")) { + form[key] = value; + } else { + form[key] = [value]; + } + } + }; + var handleParsingNestedValues = (form, key, value) => { + let nestedForm = form; + const keys = key.split("."); + keys.forEach((key2, index) => { + if (index === keys.length - 1) { + nestedForm[key2] = value; + } else { + if (!nestedForm[key2] || typeof nestedForm[key2] !== "object" || Array.isArray(nestedForm[key2]) || nestedForm[key2] instanceof File) { + nestedForm[key2] = /* @__PURE__ */ Object.create(null); + } + nestedForm = nestedForm[key2]; + } + }); + }; + } +}); + +// node_modules/hono/dist/cjs/utils/url.js +var require_url = __commonJS({ + "node_modules/hono/dist/cjs/utils/url.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var url_exports = {}; + __export(url_exports, { + checkOptionalParameter: () => checkOptionalParameter, + decodeURIComponent_: () => decodeURIComponent_, + getPath: () => getPath, + getPathNoStrict: () => getPathNoStrict, + getPattern: () => getPattern, + getQueryParam: () => getQueryParam, + getQueryParams: () => getQueryParams, + getQueryStrings: () => getQueryStrings, + mergePath: () => mergePath, + splitPath: () => splitPath, + splitRoutingPath: () => splitRoutingPath, + tryDecode: () => tryDecode + }); + module2.exports = __toCommonJS(url_exports); + var splitPath = (path) => { + const paths = path.split("/"); + if (paths[0] === "") { + paths.shift(); + } + return paths; + }; + var splitRoutingPath = (routePath) => { + const { groups, path } = extractGroupsFromPath(routePath); + const paths = splitPath(path); + return replaceGroupMarks(paths, groups); + }; + var extractGroupsFromPath = (path) => { + const groups = []; + path = path.replace(/\{[^}]+\}/g, (match, index) => { + const mark = `@${index}`; + groups.push([mark, match]); + return mark; + }); + return { groups, path }; + }; + var replaceGroupMarks = (paths, groups) => { + for (let i = groups.length - 1; i >= 0; i--) { + const [mark] = groups[i]; + for (let j = paths.length - 1; j >= 0; j--) { + if (paths[j].includes(mark)) { + paths[j] = paths[j].replace(mark, groups[i][1]); + break; + } + } + } + return paths; + }; + var patternCache = {}; + var getPattern = (label, next) => { + if (label === "*") { + return "*"; + } + const match = label.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/); + if (match) { + const cacheKey = `${label}#${next}`; + if (!patternCache[cacheKey]) { + if (match[2]) { + patternCache[cacheKey] = next && next[0] !== ":" && next[0] !== "*" ? [cacheKey, match[1], new RegExp(`^${match[2]}(?=/${next})`)] : [label, match[1], new RegExp(`^${match[2]}$`)]; + } else { + patternCache[cacheKey] = [label, match[1], true]; + } + } + return patternCache[cacheKey]; + } + return null; + }; + var tryDecode = (str, decoder) => { + try { + return decoder(str); + } catch { + return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match) => { + try { + return decoder(match); + } catch { + return match; + } + }); + } + }; + var tryDecodeURI = (str) => tryDecode(str, decodeURI); + var getPath = (request) => { + const url = request.url; + const start = url.indexOf("/", url.indexOf(":") + 4); + let i = start; + for (; i < url.length; i++) { + const charCode = url.charCodeAt(i); + if (charCode === 37) { + const queryIndex = url.indexOf("?", i); + const path = url.slice(start, queryIndex === -1 ? void 0 : queryIndex); + return tryDecodeURI(path.includes("%25") ? path.replace(/%25/g, "%2525") : path); + } else if (charCode === 63) { + break; + } + } + return url.slice(start, i); + }; + var getQueryStrings = (url) => { + const queryIndex = url.indexOf("?", 8); + return queryIndex === -1 ? "" : "?" + url.slice(queryIndex + 1); + }; + var getPathNoStrict = (request) => { + const result = getPath(request); + return result.length > 1 && result.at(-1) === "/" ? result.slice(0, -1) : result; + }; + var mergePath = (base, sub, ...rest) => { + if (rest.length) { + sub = mergePath(sub, ...rest); + } + return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`; + }; + var checkOptionalParameter = (path) => { + if (path.charCodeAt(path.length - 1) !== 63 || !path.includes(":")) { + return null; + } + const segments = path.split("/"); + const results = []; + let basePath = ""; + segments.forEach((segment) => { + if (segment !== "" && !/\:/.test(segment)) { + basePath += "/" + segment; + } else if (/\:/.test(segment)) { + if (/\?/.test(segment)) { + if (results.length === 0 && basePath === "") { + results.push("/"); + } else { + results.push(basePath); + } + const optionalSegment = segment.replace("?", ""); + basePath += "/" + optionalSegment; + results.push(basePath); + } else { + basePath += "/" + segment; + } + } + }); + return results.filter((v, i, a) => a.indexOf(v) === i); + }; + var _decodeURI = (value) => { + if (!/[%+]/.test(value)) { + return value; + } + if (value.indexOf("+") !== -1) { + value = value.replace(/\+/g, " "); + } + return value.indexOf("%") !== -1 ? tryDecode(value, decodeURIComponent_) : value; + }; + var _getQueryParam = (url, key, multiple) => { + let encoded; + if (!multiple && key && !/[%+]/.test(key)) { + let keyIndex2 = url.indexOf("?", 8); + if (keyIndex2 === -1) { + return void 0; + } + if (!url.startsWith(key, keyIndex2 + 1)) { + keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1); + } + while (keyIndex2 !== -1) { + const trailingKeyCode = url.charCodeAt(keyIndex2 + key.length + 1); + if (trailingKeyCode === 61) { + const valueIndex = keyIndex2 + key.length + 2; + const endIndex = url.indexOf("&", valueIndex); + return _decodeURI(url.slice(valueIndex, endIndex === -1 ? void 0 : endIndex)); + } else if (trailingKeyCode == 38 || isNaN(trailingKeyCode)) { + return ""; + } + keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1); + } + encoded = /[%+]/.test(url); + if (!encoded) { + return void 0; + } + } + const results = {}; + encoded ??= /[%+]/.test(url); + let keyIndex = url.indexOf("?", 8); + while (keyIndex !== -1) { + const nextKeyIndex = url.indexOf("&", keyIndex + 1); + let valueIndex = url.indexOf("=", keyIndex); + if (valueIndex > nextKeyIndex && nextKeyIndex !== -1) { + valueIndex = -1; + } + let name = url.slice( + keyIndex + 1, + valueIndex === -1 ? nextKeyIndex === -1 ? void 0 : nextKeyIndex : valueIndex + ); + if (encoded) { + name = _decodeURI(name); + } + keyIndex = nextKeyIndex; + if (name === "") { + continue; + } + let value; + if (valueIndex === -1) { + value = ""; + } else { + value = url.slice(valueIndex + 1, nextKeyIndex === -1 ? void 0 : nextKeyIndex); + if (encoded) { + value = _decodeURI(value); + } + } + if (multiple) { + if (!(results[name] && Array.isArray(results[name]))) { + results[name] = []; + } + ; + results[name].push(value); + } else { + results[name] ??= value; + } + } + return key ? results[key] : results; + }; + var getQueryParam = _getQueryParam; + var getQueryParams = (url, key) => { + return _getQueryParam(url, key, true); + }; + var decodeURIComponent_ = decodeURIComponent; + } +}); + +// node_modules/hono/dist/cjs/request.js +var require_request2 = __commonJS({ + "node_modules/hono/dist/cjs/request.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var request_exports = {}; + __export(request_exports, { + HonoRequest: () => HonoRequest, + cloneRawRequest: () => cloneRawRequest + }); + module2.exports = __toCommonJS(request_exports); + var import_http_exception = require_http_exception(); + var import_constants = require_constants(); + var import_body = require_body2(); + var import_url = require_url(); + var tryDecodeURIComponent = (str) => (0, import_url.tryDecode)(str, import_url.decodeURIComponent_); + var HonoRequest = class { + raw; + #validatedData; + #matchResult; + routeIndex = 0; + path; + bodyCache = {}; + constructor(request, path = "/", matchResult = [[]]) { + this.raw = request; + this.path = path; + this.#matchResult = matchResult; + this.#validatedData = {}; + } + param(key) { + return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams(); + } + #getDecodedParam(key) { + const paramKey = this.#matchResult[0][this.routeIndex][1][key]; + const param = this.#getParamValue(paramKey); + return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param; + } + #getAllDecodedParams() { + const decoded = {}; + const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]); + for (const key of keys) { + const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]); + if (value !== void 0) { + decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value; + } + } + return decoded; + } + #getParamValue(paramKey) { + return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey; + } + query(key) { + return (0, import_url.getQueryParam)(this.url, key); + } + queries(key) { + return (0, import_url.getQueryParams)(this.url, key); + } + header(name) { + if (name) { + return this.raw.headers.get(name) ?? void 0; + } + const headerData = {}; + this.raw.headers.forEach((value, key) => { + headerData[key] = value; + }); + return headerData; + } + async parseBody(options) { + return this.bodyCache.parsedBody ??= await (0, import_body.parseBody)(this, options); + } + #cachedBody = (key) => { + const { bodyCache, raw } = this; + const cachedBody = bodyCache[key]; + if (cachedBody) { + return cachedBody; + } + const anyCachedKey = Object.keys(bodyCache)[0]; + if (anyCachedKey) { + return bodyCache[anyCachedKey].then((body) => { + if (anyCachedKey === "json") { + body = JSON.stringify(body); + } + return new Response(body)[key](); + }); + } + return bodyCache[key] = raw[key](); + }; + json() { + return this.#cachedBody("text").then((text) => JSON.parse(text)); + } + text() { + return this.#cachedBody("text"); + } + arrayBuffer() { + return this.#cachedBody("arrayBuffer"); + } + blob() { + return this.#cachedBody("blob"); + } + formData() { + return this.#cachedBody("formData"); + } + addValidatedData(target, data) { + this.#validatedData[target] = data; + } + valid(target) { + return this.#validatedData[target]; + } + get url() { + return this.raw.url; + } + get method() { + return this.raw.method; + } + get [import_constants.GET_MATCH_RESULT]() { + return this.#matchResult; + } + get matchedRoutes() { + return this.#matchResult[0].map(([[, route]]) => route); + } + get routePath() { + return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path; + } + }; + var cloneRawRequest = async (req) => { + if (!req.raw.bodyUsed) { + return req.raw.clone(); + } + const cacheKey = Object.keys(req.bodyCache)[0]; + if (!cacheKey) { + throw new import_http_exception.HTTPException(500, { + message: "Cannot clone request: body was already consumed and not cached. Please use HonoRequest methods (e.g., req.json(), req.text()) instead of consuming req.raw directly." + }); + } + const requestInit = { + body: await req[cacheKey](), + cache: req.raw.cache, + credentials: req.raw.credentials, + headers: req.header(), + integrity: req.raw.integrity, + keepalive: req.raw.keepalive, + method: req.method, + mode: req.raw.mode, + redirect: req.raw.redirect, + referrer: req.raw.referrer, + referrerPolicy: req.raw.referrerPolicy, + signal: req.raw.signal + }; + return new Request(req.url, requestInit); + }; + } +}); + +// node_modules/hono/dist/cjs/utils/html.js +var require_html = __commonJS({ + "node_modules/hono/dist/cjs/utils/html.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var html_exports = {}; + __export(html_exports, { + HtmlEscapedCallbackPhase: () => HtmlEscapedCallbackPhase, + escapeToBuffer: () => escapeToBuffer, + raw: () => raw, + resolveCallback: () => resolveCallback, + resolveCallbackSync: () => resolveCallbackSync, + stringBufferToString: () => stringBufferToString + }); + module2.exports = __toCommonJS(html_exports); + var HtmlEscapedCallbackPhase = { + Stringify: 1, + BeforeStream: 2, + Stream: 3 + }; + var raw = (value, callbacks) => { + const escapedString = new String(value); + escapedString.isEscaped = true; + escapedString.callbacks = callbacks; + return escapedString; + }; + var escapeRe = /[&<>'"]/; + var stringBufferToString = async (buffer, callbacks) => { + let str = ""; + callbacks ||= []; + const resolvedBuffer = await Promise.all(buffer); + for (let i = resolvedBuffer.length - 1; ; i--) { + str += resolvedBuffer[i]; + i--; + if (i < 0) { + break; + } + let r = resolvedBuffer[i]; + if (typeof r === "object") { + callbacks.push(...r.callbacks || []); + } + const isEscaped = r.isEscaped; + r = await (typeof r === "object" ? r.toString() : r); + if (typeof r === "object") { + callbacks.push(...r.callbacks || []); + } + if (r.isEscaped ?? isEscaped) { + str += r; + } else { + const buf = [str]; + escapeToBuffer(r, buf); + str = buf[0]; + } + } + return raw(str, callbacks); + }; + var escapeToBuffer = (str, buffer) => { + const match = str.search(escapeRe); + if (match === -1) { + buffer[0] += str; + return; + } + let escape; + let index; + let lastIndex = 0; + for (index = match; index < str.length; index++) { + switch (str.charCodeAt(index)) { + case 34: + escape = """; + break; + case 39: + escape = "'"; + break; + case 38: + escape = "&"; + break; + case 60: + escape = "<"; + break; + case 62: + escape = ">"; + break; + default: + continue; + } + buffer[0] += str.substring(lastIndex, index) + escape; + lastIndex = index + 1; + } + buffer[0] += str.substring(lastIndex, index); + }; + var resolveCallbackSync = (str) => { + const callbacks = str.callbacks; + if (!callbacks?.length) { + return str; + } + const buffer = [str]; + const context = {}; + callbacks.forEach((c) => c({ phase: HtmlEscapedCallbackPhase.Stringify, buffer, context })); + return buffer[0]; + }; + var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => { + if (typeof str === "object" && !(str instanceof String)) { + if (!(str instanceof Promise)) { + str = str.toString(); + } + if (str instanceof Promise) { + str = await str; + } + } + const callbacks = str.callbacks; + if (!callbacks?.length) { + return Promise.resolve(str); + } + if (buffer) { + buffer[0] += str; + } else { + buffer = [str]; + } + const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then( + (res) => Promise.all( + res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context, buffer)) + ).then(() => buffer[0]) + ); + if (preserveCallbacks) { + return raw(await resStr, callbacks); + } else { + return resStr; + } + }; + } +}); + +// node_modules/hono/dist/cjs/context.js +var require_context = __commonJS({ + "node_modules/hono/dist/cjs/context.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var context_exports = {}; + __export(context_exports, { + Context: () => Context, + TEXT_PLAIN: () => TEXT_PLAIN + }); + module2.exports = __toCommonJS(context_exports); + var import_request = require_request2(); + var import_html = require_html(); + var TEXT_PLAIN = "text/plain; charset=UTF-8"; + var setDefaultContentType = (contentType, headers) => { + return { + "Content-Type": contentType, + ...headers + }; + }; + var Context = class { + #rawRequest; + #req; + env = {}; + #var; + finalized = false; + error; + #status; + #executionCtx; + #res; + #layout; + #renderer; + #notFoundHandler; + #preparedHeaders; + #matchResult; + #path; + constructor(req, options) { + this.#rawRequest = req; + if (options) { + this.#executionCtx = options.executionCtx; + this.env = options.env; + this.#notFoundHandler = options.notFoundHandler; + this.#path = options.path; + this.#matchResult = options.matchResult; + } + } + get req() { + this.#req ??= new import_request.HonoRequest(this.#rawRequest, this.#path, this.#matchResult); + return this.#req; + } + get event() { + if (this.#executionCtx && "respondWith" in this.#executionCtx) { + return this.#executionCtx; + } else { + throw Error("This context has no FetchEvent"); + } + } + get executionCtx() { + if (this.#executionCtx) { + return this.#executionCtx; + } else { + throw Error("This context has no ExecutionContext"); + } + } + get res() { + return this.#res ||= new Response(null, { + headers: this.#preparedHeaders ??= new Headers() + }); + } + set res(_res) { + if (this.#res && _res) { + _res = new Response(_res.body, _res); + for (const [k, v] of this.#res.headers.entries()) { + if (k === "content-type") { + continue; + } + if (k === "set-cookie") { + const cookies = this.#res.headers.getSetCookie(); + _res.headers.delete("set-cookie"); + for (const cookie of cookies) { + _res.headers.append("set-cookie", cookie); + } + } else { + _res.headers.set(k, v); + } + } + } + this.#res = _res; + this.finalized = true; + } + render = (...args) => { + this.#renderer ??= (content) => this.html(content); + return this.#renderer(...args); + }; + setLayout = (layout) => this.#layout = layout; + getLayout = () => this.#layout; + setRenderer = (renderer) => { + this.#renderer = renderer; + }; + header = (name, value, options) => { + if (this.finalized) { + this.#res = new Response(this.#res.body, this.#res); + } + const headers = this.#res ? this.#res.headers : this.#preparedHeaders ??= new Headers(); + if (value === void 0) { + headers.delete(name); + } else if (options?.append) { + headers.append(name, value); + } else { + headers.set(name, value); + } + }; + status = (status) => { + this.#status = status; + }; + set = (key, value) => { + this.#var ??= /* @__PURE__ */ new Map(); + this.#var.set(key, value); + }; + get = (key) => { + return this.#var ? this.#var.get(key) : void 0; + }; + get var() { + if (!this.#var) { + return {}; + } + return Object.fromEntries(this.#var); + } + #newResponse(data, arg, headers) { + const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers(); + if (typeof arg === "object" && "headers" in arg) { + const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers); + for (const [key, value] of argHeaders) { + if (key.toLowerCase() === "set-cookie") { + responseHeaders.append(key, value); + } else { + responseHeaders.set(key, value); + } + } + } + if (headers) { + for (const [k, v] of Object.entries(headers)) { + if (typeof v === "string") { + responseHeaders.set(k, v); + } else { + responseHeaders.delete(k); + for (const v2 of v) { + responseHeaders.append(k, v2); + } + } + } + } + const status = typeof arg === "number" ? arg : arg?.status ?? this.#status; + return new Response(data, { status, headers: responseHeaders }); + } + newResponse = (...args) => this.#newResponse(...args); + body = (data, arg, headers) => this.#newResponse(data, arg, headers); + text = (text, arg, headers) => { + return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse( + text, + arg, + setDefaultContentType(TEXT_PLAIN, headers) + ); + }; + json = (object, arg, headers) => { + return this.#newResponse( + JSON.stringify(object), + arg, + setDefaultContentType("application/json", headers) + ); + }; + html = (html, arg, headers) => { + const res = (html2) => this.#newResponse(html2, arg, setDefaultContentType("text/html; charset=UTF-8", headers)); + return typeof html === "object" ? (0, import_html.resolveCallback)(html, import_html.HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html); + }; + redirect = (location, status) => { + const locationString = String(location); + this.header( + "Location", + !/[^\x00-\xFF]/.test(locationString) ? locationString : encodeURI(locationString) + ); + return this.newResponse(null, status ?? 302); + }; + notFound = () => { + this.#notFoundHandler ??= () => new Response(); + return this.#notFoundHandler(this); + }; + }; + } +}); + +// node_modules/hono/dist/cjs/router.js +var require_router = __commonJS({ + "node_modules/hono/dist/cjs/router.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var router_exports = {}; + __export(router_exports, { + MESSAGE_MATCHER_IS_ALREADY_BUILT: () => MESSAGE_MATCHER_IS_ALREADY_BUILT, + METHODS: () => METHODS, + METHOD_NAME_ALL: () => METHOD_NAME_ALL, + METHOD_NAME_ALL_LOWERCASE: () => METHOD_NAME_ALL_LOWERCASE, + UnsupportedPathError: () => UnsupportedPathError + }); + module2.exports = __toCommonJS(router_exports); + var METHOD_NAME_ALL = "ALL"; + var METHOD_NAME_ALL_LOWERCASE = "all"; + var METHODS = ["get", "post", "put", "delete", "options", "patch"]; + var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built."; + var UnsupportedPathError = class extends Error { + }; + } +}); + +// node_modules/hono/dist/cjs/utils/constants.js +var require_constants2 = __commonJS({ + "node_modules/hono/dist/cjs/utils/constants.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var constants_exports = {}; + __export(constants_exports, { + COMPOSED_HANDLER: () => COMPOSED_HANDLER + }); + module2.exports = __toCommonJS(constants_exports); + var COMPOSED_HANDLER = "__COMPOSED_HANDLER"; + } +}); + +// node_modules/hono/dist/cjs/hono-base.js +var require_hono_base = __commonJS({ + "node_modules/hono/dist/cjs/hono-base.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var hono_base_exports = {}; + __export(hono_base_exports, { + HonoBase: () => Hono2 + }); + module2.exports = __toCommonJS(hono_base_exports); + var import_compose = require_compose(); + var import_context = require_context(); + var import_router = require_router(); + var import_constants = require_constants2(); + var import_url = require_url(); + var notFoundHandler = (c) => { + return c.text("404 Not Found", 404); + }; + var errorHandler = (err, c) => { + if ("getResponse" in err) { + const res = err.getResponse(); + return c.newResponse(res.body, res); + } + console.error(err); + return c.text("Internal Server Error", 500); + }; + var Hono2 = class _Hono { + get; + post; + put; + delete; + options; + patch; + all; + on; + use; + router; + getPath; + _basePath = "/"; + #path = "/"; + routes = []; + constructor(options = {}) { + const allMethods = [...import_router.METHODS, import_router.METHOD_NAME_ALL_LOWERCASE]; + allMethods.forEach((method) => { + this[method] = (args1, ...args) => { + if (typeof args1 === "string") { + this.#path = args1; + } else { + this.#addRoute(method, this.#path, args1); + } + args.forEach((handler) => { + this.#addRoute(method, this.#path, handler); + }); + return this; + }; + }); + this.on = (method, path, ...handlers) => { + for (const p of [path].flat()) { + this.#path = p; + for (const m of [method].flat()) { + handlers.map((handler) => { + this.#addRoute(m.toUpperCase(), this.#path, handler); + }); + } + } + return this; + }; + this.use = (arg1, ...handlers) => { + if (typeof arg1 === "string") { + this.#path = arg1; + } else { + this.#path = "*"; + handlers.unshift(arg1); + } + handlers.forEach((handler) => { + this.#addRoute(import_router.METHOD_NAME_ALL, this.#path, handler); + }); + return this; + }; + const { strict, ...optionsWithoutStrict } = options; + Object.assign(this, optionsWithoutStrict); + this.getPath = strict ?? true ? options.getPath ?? import_url.getPath : import_url.getPathNoStrict; + } + #clone() { + const clone = new _Hono({ + router: this.router, + getPath: this.getPath + }); + clone.errorHandler = this.errorHandler; + clone.#notFoundHandler = this.#notFoundHandler; + clone.routes = this.routes; + return clone; + } + #notFoundHandler = notFoundHandler; + errorHandler = errorHandler; + route(path, app) { + const subApp = this.basePath(path); + app.routes.map((r) => { + let handler; + if (app.errorHandler === errorHandler) { + handler = r.handler; + } else { + handler = async (c, next) => (await (0, import_compose.compose)([], app.errorHandler)(c, () => r.handler(c, next))).res; + handler[import_constants.COMPOSED_HANDLER] = r.handler; + } + subApp.#addRoute(r.method, r.path, handler); + }); + return this; + } + basePath(path) { + const subApp = this.#clone(); + subApp._basePath = (0, import_url.mergePath)(this._basePath, path); + return subApp; + } + onError = (handler) => { + this.errorHandler = handler; + return this; + }; + notFound = (handler) => { + this.#notFoundHandler = handler; + return this; + }; + mount(path, applicationHandler, options) { + let replaceRequest; + let optionHandler; + if (options) { + if (typeof options === "function") { + optionHandler = options; + } else { + optionHandler = options.optionHandler; + if (options.replaceRequest === false) { + replaceRequest = (request) => request; + } else { + replaceRequest = options.replaceRequest; + } + } + } + const getOptions = optionHandler ? (c) => { + const options2 = optionHandler(c); + return Array.isArray(options2) ? options2 : [options2]; + } : (c) => { + let executionContext = void 0; + try { + executionContext = c.executionCtx; + } catch { + } + return [c.env, executionContext]; + }; + replaceRequest ||= (() => { + const mergedPath = (0, import_url.mergePath)(this._basePath, path); + const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length; + return (request) => { + const url = new URL(request.url); + url.pathname = url.pathname.slice(pathPrefixLength) || "/"; + return new Request(url, request); + }; + })(); + const handler = async (c, next) => { + const res = await applicationHandler(replaceRequest(c.req.raw), ...getOptions(c)); + if (res) { + return res; + } + await next(); + }; + this.#addRoute(import_router.METHOD_NAME_ALL, (0, import_url.mergePath)(path, "*"), handler); + return this; + } + #addRoute(method, path, handler) { + method = method.toUpperCase(); + path = (0, import_url.mergePath)(this._basePath, path); + const r = { basePath: this._basePath, path, method, handler }; + this.router.add(method, path, [handler, r]); + this.routes.push(r); + } + #handleError(err, c) { + if (err instanceof Error) { + return this.errorHandler(err, c); + } + throw err; + } + #dispatch(request, executionCtx, env, method) { + if (method === "HEAD") { + return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))(); + } + const path = this.getPath(request, { env }); + const matchResult = this.router.match(method, path); + const c = new import_context.Context(request, { + path, + matchResult, + env, + executionCtx, + notFoundHandler: this.#notFoundHandler + }); + if (matchResult[0].length === 1) { + let res; + try { + res = matchResult[0][0][0][0](c, async () => { + c.res = await this.#notFoundHandler(c); + }); + } catch (err) { + return this.#handleError(err, c); + } + return res instanceof Promise ? res.then( + (resolved) => resolved || (c.finalized ? c.res : this.#notFoundHandler(c)) + ).catch((err) => this.#handleError(err, c)) : res ?? this.#notFoundHandler(c); + } + const composed = (0, import_compose.compose)(matchResult[0], this.errorHandler, this.#notFoundHandler); + return (async () => { + try { + const context = await composed(c); + if (!context.finalized) { + throw new Error( + "Context is not finalized. Did you forget to return a Response object or `await next()`?" + ); + } + return context.res; + } catch (err) { + return this.#handleError(err, c); + } + })(); + } + fetch = (request, ...rest) => { + return this.#dispatch(request, rest[1], rest[0], request.method); + }; + request = (input, requestInit, Env, executionCtx) => { + if (input instanceof Request) { + return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx); + } + input = input.toString(); + return this.fetch( + new Request( + /^https?:\/\//.test(input) ? input : `http://localhost${(0, import_url.mergePath)("/", input)}`, + requestInit + ), + Env, + executionCtx + ); + }; + fire = () => { + addEventListener("fetch", (event) => { + event.respondWith(this.#dispatch(event.request, event, void 0, event.request.method)); + }); + }; + }; + } +}); + +// node_modules/hono/dist/cjs/router/reg-exp-router/matcher.js +var require_matcher = __commonJS({ + "node_modules/hono/dist/cjs/router/reg-exp-router/matcher.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var matcher_exports = {}; + __export(matcher_exports, { + emptyParam: () => emptyParam, + match: () => match + }); + module2.exports = __toCommonJS(matcher_exports); + var import_router = require_router(); + var emptyParam = []; + function match(method, path) { + const matchers = this.buildAllMatchers(); + const match2 = (method2, path2) => { + const matcher = matchers[method2] || matchers[import_router.METHOD_NAME_ALL]; + const staticMatch = matcher[2][path2]; + if (staticMatch) { + return staticMatch; + } + const match3 = path2.match(matcher[0]); + if (!match3) { + return [[], emptyParam]; + } + const index = match3.indexOf("", 1); + return [matcher[1][index], match3]; + }; + this.match = match2; + return match2(method, path); + } + } +}); + +// node_modules/hono/dist/cjs/router/reg-exp-router/node.js +var require_node = __commonJS({ + "node_modules/hono/dist/cjs/router/reg-exp-router/node.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var node_exports = {}; + __export(node_exports, { + Node: () => Node, + PATH_ERROR: () => PATH_ERROR + }); + module2.exports = __toCommonJS(node_exports); + var LABEL_REG_EXP_STR = "[^/]+"; + var ONLY_WILDCARD_REG_EXP_STR = ".*"; + var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)"; + var PATH_ERROR = Symbol(); + var regExpMetaChars = new Set(".\\+*[^]$()"); + function compareKey(a, b) { + if (a.length === 1) { + return b.length === 1 ? a < b ? -1 : 1 : -1; + } + if (b.length === 1) { + return 1; + } + if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) { + return 1; + } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) { + return -1; + } + if (a === LABEL_REG_EXP_STR) { + return 1; + } else if (b === LABEL_REG_EXP_STR) { + return -1; + } + return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length; + } + var Node = class _Node { + #index; + #varIndex; + #children = /* @__PURE__ */ Object.create(null); + insert(tokens, index, paramMap, context, pathErrorCheckOnly) { + if (tokens.length === 0) { + if (this.#index !== void 0) { + throw PATH_ERROR; + } + if (pathErrorCheckOnly) { + return; + } + this.#index = index; + return; + } + const [token, ...restTokens] = tokens; + const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/); + let node; + if (pattern) { + const name = pattern[1]; + let regexpStr = pattern[2] || LABEL_REG_EXP_STR; + if (name && pattern[2]) { + if (regexpStr === ".*") { + throw PATH_ERROR; + } + regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:"); + if (/\((?!\?:)/.test(regexpStr)) { + throw PATH_ERROR; + } + } + node = this.#children[regexpStr]; + if (!node) { + if (Object.keys(this.#children).some( + (k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR + )) { + throw PATH_ERROR; + } + if (pathErrorCheckOnly) { + return; + } + node = this.#children[regexpStr] = new _Node(); + if (name !== "") { + node.#varIndex = context.varIndex++; + } + } + if (!pathErrorCheckOnly && name !== "") { + paramMap.push([name, node.#varIndex]); + } + } else { + node = this.#children[token]; + if (!node) { + if (Object.keys(this.#children).some( + (k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR + )) { + throw PATH_ERROR; + } + if (pathErrorCheckOnly) { + return; + } + node = this.#children[token] = new _Node(); + } + } + node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly); + } + buildRegExpStr() { + const childKeys = Object.keys(this.#children).sort(compareKey); + const strList = childKeys.map((k) => { + const c = this.#children[k]; + return (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + c.buildRegExpStr(); + }); + if (typeof this.#index === "number") { + strList.unshift(`#${this.#index}`); + } + if (strList.length === 0) { + return ""; + } + if (strList.length === 1) { + return strList[0]; + } + return "(?:" + strList.join("|") + ")"; + } + }; + } +}); + +// node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +var require_trie = __commonJS({ + "node_modules/hono/dist/cjs/router/reg-exp-router/trie.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var trie_exports = {}; + __export(trie_exports, { + Trie: () => Trie + }); + module2.exports = __toCommonJS(trie_exports); + var import_node = require_node(); + var Trie = class { + #context = { varIndex: 0 }; + #root = new import_node.Node(); + insert(path, index, pathErrorCheckOnly) { + const paramAssoc = []; + const groups = []; + for (let i = 0; ; ) { + let replaced = false; + path = path.replace(/\{[^}]+\}/g, (m) => { + const mark = `@\\${i}`; + groups[i] = [mark, m]; + i++; + replaced = true; + return mark; + }); + if (!replaced) { + break; + } + } + const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || []; + for (let i = groups.length - 1; i >= 0; i--) { + const [mark] = groups[i]; + for (let j = tokens.length - 1; j >= 0; j--) { + if (tokens[j].indexOf(mark) !== -1) { + tokens[j] = tokens[j].replace(mark, groups[i][1]); + break; + } + } + } + this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly); + return paramAssoc; + } + buildRegExp() { + let regexp = this.#root.buildRegExpStr(); + if (regexp === "") { + return [/^$/, [], []]; + } + let captureIndex = 0; + const indexReplacementMap = []; + const paramReplacementMap = []; + regexp = regexp.replace(/#(\d+)|@(\d+)|\.\*\$/g, (_, handlerIndex, paramIndex) => { + if (handlerIndex !== void 0) { + indexReplacementMap[++captureIndex] = Number(handlerIndex); + return "$()"; + } + if (paramIndex !== void 0) { + paramReplacementMap[Number(paramIndex)] = ++captureIndex; + return ""; + } + return ""; + }); + return [new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap]; + } + }; + } +}); + +// node_modules/hono/dist/cjs/router/reg-exp-router/router.js +var require_router2 = __commonJS({ + "node_modules/hono/dist/cjs/router/reg-exp-router/router.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var router_exports = {}; + __export(router_exports, { + RegExpRouter: () => RegExpRouter2 + }); + module2.exports = __toCommonJS(router_exports); + var import_router = require_router(); + var import_url = require_url(); + var import_matcher = require_matcher(); + var import_node = require_node(); + var import_trie = require_trie(); + var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)]; + var wildcardRegExpCache = /* @__PURE__ */ Object.create(null); + function buildWildcardRegExp(path) { + return wildcardRegExpCache[path] ??= new RegExp( + path === "*" ? "" : `^${path.replace( + /\/\*$|([.\\+*[^\]$()])/g, + (_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)" + )}$` + ); + } + function clearWildcardRegExpCache() { + wildcardRegExpCache = /* @__PURE__ */ Object.create(null); + } + function buildMatcherFromPreprocessedRoutes(routes) { + const trie = new import_trie.Trie(); + const handlerData = []; + if (routes.length === 0) { + return nullMatcher; + } + const routesWithStaticPathFlag = routes.map( + (route) => [!/\*|\/:/.test(route[0]), ...route] + ).sort( + ([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length + ); + const staticMap = /* @__PURE__ */ Object.create(null); + for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) { + const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i]; + if (pathErrorCheckOnly) { + staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), import_matcher.emptyParam]; + } else { + j++; + } + let paramAssoc; + try { + paramAssoc = trie.insert(path, j, pathErrorCheckOnly); + } catch (e) { + throw e === import_node.PATH_ERROR ? new import_router.UnsupportedPathError(path) : e; + } + if (pathErrorCheckOnly) { + continue; + } + handlerData[j] = handlers.map(([h, paramCount]) => { + const paramIndexMap = /* @__PURE__ */ Object.create(null); + paramCount -= 1; + for (; paramCount >= 0; paramCount--) { + const [key, value] = paramAssoc[paramCount]; + paramIndexMap[key] = value; + } + return [h, paramIndexMap]; + }); + } + const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp(); + for (let i = 0, len = handlerData.length; i < len; i++) { + for (let j = 0, len2 = handlerData[i].length; j < len2; j++) { + const map = handlerData[i][j]?.[1]; + if (!map) { + continue; + } + const keys = Object.keys(map); + for (let k = 0, len3 = keys.length; k < len3; k++) { + map[keys[k]] = paramReplacementMap[map[keys[k]]]; + } + } + } + const handlerMap = []; + for (const i in indexReplacementMap) { + handlerMap[i] = handlerData[indexReplacementMap[i]]; + } + return [regexp, handlerMap, staticMap]; + } + function findMiddleware(middleware, path) { + if (!middleware) { + return void 0; + } + for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) { + if (buildWildcardRegExp(k).test(path)) { + return [...middleware[k]]; + } + } + return void 0; + } + var RegExpRouter2 = class { + name = "RegExpRouter"; + #middleware; + #routes; + constructor() { + this.#middleware = { [import_router.METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) }; + this.#routes = { [import_router.METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) }; + } + add(method, path, handler) { + const middleware = this.#middleware; + const routes = this.#routes; + if (!middleware || !routes) { + throw new Error(import_router.MESSAGE_MATCHER_IS_ALREADY_BUILT); + } + if (!middleware[method]) { + ; + [middleware, routes].forEach((handlerMap) => { + handlerMap[method] = /* @__PURE__ */ Object.create(null); + Object.keys(handlerMap[import_router.METHOD_NAME_ALL]).forEach((p) => { + handlerMap[method][p] = [...handlerMap[import_router.METHOD_NAME_ALL][p]]; + }); + }); + } + if (path === "/*") { + path = "*"; + } + const paramCount = (path.match(/\/:/g) || []).length; + if (/\*$/.test(path)) { + const re = buildWildcardRegExp(path); + if (method === import_router.METHOD_NAME_ALL) { + Object.keys(middleware).forEach((m) => { + middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[import_router.METHOD_NAME_ALL], path) || []; + }); + } else { + middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[import_router.METHOD_NAME_ALL], path) || []; + } + Object.keys(middleware).forEach((m) => { + if (method === import_router.METHOD_NAME_ALL || method === m) { + Object.keys(middleware[m]).forEach((p) => { + re.test(p) && middleware[m][p].push([handler, paramCount]); + }); + } + }); + Object.keys(routes).forEach((m) => { + if (method === import_router.METHOD_NAME_ALL || method === m) { + Object.keys(routes[m]).forEach( + (p) => re.test(p) && routes[m][p].push([handler, paramCount]) + ); + } + }); + return; + } + const paths = (0, import_url.checkOptionalParameter)(path) || [path]; + for (let i = 0, len = paths.length; i < len; i++) { + const path2 = paths[i]; + Object.keys(routes).forEach((m) => { + if (method === import_router.METHOD_NAME_ALL || method === m) { + routes[m][path2] ||= [ + ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[import_router.METHOD_NAME_ALL], path2) || [] + ]; + routes[m][path2].push([handler, paramCount - len + i + 1]); + } + }); + } + } + match = import_matcher.match; + buildAllMatchers() { + const matchers = /* @__PURE__ */ Object.create(null); + Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => { + matchers[method] ||= this.#buildMatcher(method); + }); + this.#middleware = this.#routes = void 0; + clearWildcardRegExpCache(); + return matchers; + } + #buildMatcher(method) { + const routes = []; + let hasOwnRoute = method === import_router.METHOD_NAME_ALL; + [this.#middleware, this.#routes].forEach((r) => { + const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : []; + if (ownRoute.length !== 0) { + hasOwnRoute ||= true; + routes.push(...ownRoute); + } else if (method !== import_router.METHOD_NAME_ALL) { + routes.push( + ...Object.keys(r[import_router.METHOD_NAME_ALL]).map((path) => [path, r[import_router.METHOD_NAME_ALL][path]]) + ); + } + }); + if (!hasOwnRoute) { + return null; + } else { + return buildMatcherFromPreprocessedRoutes(routes); + } + } + }; + } +}); + +// node_modules/hono/dist/cjs/router/reg-exp-router/prepared-router.js +var require_prepared_router = __commonJS({ + "node_modules/hono/dist/cjs/router/reg-exp-router/prepared-router.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var prepared_router_exports = {}; + __export(prepared_router_exports, { + PreparedRegExpRouter: () => PreparedRegExpRouter2, + buildInitParams: () => buildInitParams2, + serializeInitParams: () => serializeInitParams2 + }); + module2.exports = __toCommonJS(prepared_router_exports); + var import_router = require_router(); + var import_matcher = require_matcher(); + var import_router2 = require_router2(); + var PreparedRegExpRouter2 = class { + name = "PreparedRegExpRouter"; + #matchers; + #relocateMap; + constructor(matchers, relocateMap) { + this.#matchers = matchers; + this.#relocateMap = relocateMap; + } + #addWildcard(method, handlerData) { + const matcher = this.#matchers[method]; + matcher[1].forEach((list) => list && list.push(handlerData)); + Object.values(matcher[2]).forEach((list) => list[0].push(handlerData)); + } + #addPath(method, path, handler, indexes, map) { + const matcher = this.#matchers[method]; + if (!map) { + matcher[2][path][0].push([handler, {}]); + } else { + indexes.forEach((index) => { + if (typeof index === "number") { + matcher[1][index].push([handler, map]); + } else { + ; + matcher[2][index || path][0].push([handler, map]); + } + }); + } + } + add(method, path, handler) { + if (!this.#matchers[method]) { + const all = this.#matchers[import_router.METHOD_NAME_ALL]; + const staticMap = {}; + for (const key in all[2]) { + staticMap[key] = [all[2][key][0].slice(), import_matcher.emptyParam]; + } + this.#matchers[method] = [ + all[0], + all[1].map((list) => Array.isArray(list) ? list.slice() : 0), + staticMap + ]; + } + if (path === "/*" || path === "*") { + const handlerData = [handler, {}]; + if (method === import_router.METHOD_NAME_ALL) { + for (const m in this.#matchers) { + this.#addWildcard(m, handlerData); + } + } else { + this.#addWildcard(method, handlerData); + } + return; + } + const data = this.#relocateMap[path]; + if (!data) { + throw new Error(`Path ${path} is not registered`); + } + for (const [indexes, map] of data) { + if (method === import_router.METHOD_NAME_ALL) { + for (const m in this.#matchers) { + this.#addPath(m, path, handler, indexes, map); + } + } else { + this.#addPath(method, path, handler, indexes, map); + } + } + } + buildAllMatchers() { + return this.#matchers; + } + match = import_matcher.match; + }; + var buildInitParams2 = ({ paths }) => { + const RegExpRouterWithMatcherExport = class extends import_router2.RegExpRouter { + buildAndExportAllMatchers() { + return this.buildAllMatchers(); + } + }; + const router = new RegExpRouterWithMatcherExport(); + for (const path of paths) { + router.add(import_router.METHOD_NAME_ALL, path, path); + } + const matchers = router.buildAndExportAllMatchers(); + const all = matchers[import_router.METHOD_NAME_ALL]; + const relocateMap = {}; + for (const path of paths) { + if (path === "/*" || path === "*") { + continue; + } + all[1].forEach((list, i) => { + list.forEach(([p, map]) => { + if (p === path) { + if (relocateMap[path]) { + relocateMap[path][0][1] = { + ...relocateMap[path][0][1], + ...map + }; + } else { + relocateMap[path] = [[[], map]]; + } + if (relocateMap[path][0][0].findIndex((j) => j === i) === -1) { + relocateMap[path][0][0].push(i); + } + } + }); + }); + for (const path2 in all[2]) { + all[2][path2][0].forEach(([p]) => { + if (p === path) { + relocateMap[path] ||= [[[]]]; + const value = path2 === path ? "" : path2; + if (relocateMap[path][0][0].findIndex((v) => v === value) === -1) { + relocateMap[path][0][0].push(value); + } + } + }); + } + } + for (let i = 0, len = all[1].length; i < len; i++) { + all[1][i] = all[1][i] ? [] : 0; + } + for (const path in all[2]) { + all[2][path][0] = []; + } + return [matchers, relocateMap]; + }; + var serializeInitParams2 = ([matchers, relocateMap]) => { + const matchersStr = JSON.stringify( + matchers, + (_, value) => value instanceof RegExp ? `##${value.toString()}##` : value + ).replace(/"##(.+?)##"/g, (_, str) => str.replace(/\\\\/g, "\\")); + const relocateMapStr = JSON.stringify(relocateMap); + return `[${matchersStr},${relocateMapStr}]`; + }; + } +}); + +// node_modules/hono/dist/cjs/router/reg-exp-router/index.js +var require_reg_exp_router = __commonJS({ + "node_modules/hono/dist/cjs/router/reg-exp-router/index.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var reg_exp_router_exports = {}; + __export(reg_exp_router_exports, { + PreparedRegExpRouter: () => import_prepared_router.PreparedRegExpRouter, + RegExpRouter: () => import_router.RegExpRouter, + buildInitParams: () => import_prepared_router.buildInitParams, + serializeInitParams: () => import_prepared_router.serializeInitParams + }); + module2.exports = __toCommonJS(reg_exp_router_exports); + var import_router = require_router2(); + var import_prepared_router = require_prepared_router(); + } +}); + +// node_modules/hono/dist/cjs/router/smart-router/router.js +var require_router3 = __commonJS({ + "node_modules/hono/dist/cjs/router/smart-router/router.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var router_exports = {}; + __export(router_exports, { + SmartRouter: () => SmartRouter2 + }); + module2.exports = __toCommonJS(router_exports); + var import_router = require_router(); + var SmartRouter2 = class { + name = "SmartRouter"; + #routers = []; + #routes = []; + constructor(init) { + this.#routers = init.routers; + } + add(method, path, handler) { + if (!this.#routes) { + throw new Error(import_router.MESSAGE_MATCHER_IS_ALREADY_BUILT); + } + this.#routes.push([method, path, handler]); + } + match(method, path) { + if (!this.#routes) { + throw new Error("Fatal error"); + } + const routers = this.#routers; + const routes = this.#routes; + const len = routers.length; + let i = 0; + let res; + for (; i < len; i++) { + const router = routers[i]; + try { + for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) { + router.add(...routes[i2]); + } + res = router.match(method, path); + } catch (e) { + if (e instanceof import_router.UnsupportedPathError) { + continue; + } + throw e; + } + this.match = router.match.bind(router); + this.#routers = [router]; + this.#routes = void 0; + break; + } + if (i === len) { + throw new Error("Fatal error"); + } + this.name = `SmartRouter + ${this.activeRouter.name}`; + return res; + } + get activeRouter() { + if (this.#routes || this.#routers.length !== 1) { + throw new Error("No active router has been determined yet."); + } + return this.#routers[0]; + } + }; + } +}); + +// node_modules/hono/dist/cjs/router/smart-router/index.js +var require_smart_router = __commonJS({ + "node_modules/hono/dist/cjs/router/smart-router/index.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var smart_router_exports = {}; + __export(smart_router_exports, { + SmartRouter: () => import_router.SmartRouter + }); + module2.exports = __toCommonJS(smart_router_exports); + var import_router = require_router3(); + } +}); + +// node_modules/hono/dist/cjs/router/trie-router/node.js +var require_node2 = __commonJS({ + "node_modules/hono/dist/cjs/router/trie-router/node.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var node_exports = {}; + __export(node_exports, { + Node: () => Node + }); + module2.exports = __toCommonJS(node_exports); + var import_router = require_router(); + var import_url = require_url(); + var emptyParams = /* @__PURE__ */ Object.create(null); + var Node = class _Node { + #methods; + #children; + #patterns; + #order = 0; + #params = emptyParams; + constructor(method, handler, children) { + this.#children = children || /* @__PURE__ */ Object.create(null); + this.#methods = []; + if (method && handler) { + const m = /* @__PURE__ */ Object.create(null); + m[method] = { handler, possibleKeys: [], score: 0 }; + this.#methods = [m]; + } + this.#patterns = []; + } + insert(method, path, handler) { + this.#order = ++this.#order; + let curNode = this; + const parts = (0, import_url.splitRoutingPath)(path); + const possibleKeys = []; + for (let i = 0, len = parts.length; i < len; i++) { + const p = parts[i]; + const nextP = parts[i + 1]; + const pattern = (0, import_url.getPattern)(p, nextP); + const key = Array.isArray(pattern) ? pattern[0] : p; + if (key in curNode.#children) { + curNode = curNode.#children[key]; + if (pattern) { + possibleKeys.push(pattern[1]); + } + continue; + } + curNode.#children[key] = new _Node(); + if (pattern) { + curNode.#patterns.push(pattern); + possibleKeys.push(pattern[1]); + } + curNode = curNode.#children[key]; + } + curNode.#methods.push({ + [method]: { + handler, + possibleKeys: possibleKeys.filter((v, i, a) => a.indexOf(v) === i), + score: this.#order + } + }); + return curNode; + } + #getHandlerSets(node, method, nodeParams, params) { + const handlerSets = []; + for (let i = 0, len = node.#methods.length; i < len; i++) { + const m = node.#methods[i]; + const handlerSet = m[method] || m[import_router.METHOD_NAME_ALL]; + const processedSet = {}; + if (handlerSet !== void 0) { + handlerSet.params = /* @__PURE__ */ Object.create(null); + handlerSets.push(handlerSet); + if (nodeParams !== emptyParams || params && params !== emptyParams) { + for (let i2 = 0, len2 = handlerSet.possibleKeys.length; i2 < len2; i2++) { + const key = handlerSet.possibleKeys[i2]; + const processed = processedSet[handlerSet.score]; + handlerSet.params[key] = params?.[key] && !processed ? params[key] : nodeParams[key] ?? params?.[key]; + processedSet[handlerSet.score] = true; + } + } + } + } + return handlerSets; + } + search(method, path) { + const handlerSets = []; + this.#params = emptyParams; + const curNode = this; + let curNodes = [curNode]; + const parts = (0, import_url.splitPath)(path); + const curNodesQueue = []; + for (let i = 0, len = parts.length; i < len; i++) { + const part = parts[i]; + const isLast = i === len - 1; + const tempNodes = []; + for (let j = 0, len2 = curNodes.length; j < len2; j++) { + const node = curNodes[j]; + const nextNode = node.#children[part]; + if (nextNode) { + nextNode.#params = node.#params; + if (isLast) { + if (nextNode.#children["*"]) { + handlerSets.push( + ...this.#getHandlerSets(nextNode.#children["*"], method, node.#params) + ); + } + handlerSets.push(...this.#getHandlerSets(nextNode, method, node.#params)); + } else { + tempNodes.push(nextNode); + } + } + for (let k = 0, len3 = node.#patterns.length; k < len3; k++) { + const pattern = node.#patterns[k]; + const params = node.#params === emptyParams ? {} : { ...node.#params }; + if (pattern === "*") { + const astNode = node.#children["*"]; + if (astNode) { + handlerSets.push(...this.#getHandlerSets(astNode, method, node.#params)); + astNode.#params = params; + tempNodes.push(astNode); + } + continue; + } + const [key, name, matcher] = pattern; + if (!part && !(matcher instanceof RegExp)) { + continue; + } + const child = node.#children[key]; + const restPathString = parts.slice(i).join("/"); + if (matcher instanceof RegExp) { + const m = matcher.exec(restPathString); + if (m) { + params[name] = m[0]; + handlerSets.push(...this.#getHandlerSets(child, method, node.#params, params)); + if (Object.keys(child.#children).length) { + child.#params = params; + const componentCount = m[0].match(/\//)?.length ?? 0; + const targetCurNodes = curNodesQueue[componentCount] ||= []; + targetCurNodes.push(child); + } + continue; + } + } + if (matcher === true || matcher.test(part)) { + params[name] = part; + if (isLast) { + handlerSets.push(...this.#getHandlerSets(child, method, params, node.#params)); + if (child.#children["*"]) { + handlerSets.push( + ...this.#getHandlerSets(child.#children["*"], method, params, node.#params) + ); + } + } else { + child.#params = params; + tempNodes.push(child); + } + } + } + } + curNodes = tempNodes.concat(curNodesQueue.shift() ?? []); + } + if (handlerSets.length > 1) { + handlerSets.sort((a, b) => { + return a.score - b.score; + }); + } + return [handlerSets.map(({ handler, params }) => [handler, params])]; + } + }; + } +}); + +// node_modules/hono/dist/cjs/router/trie-router/router.js +var require_router4 = __commonJS({ + "node_modules/hono/dist/cjs/router/trie-router/router.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var router_exports = {}; + __export(router_exports, { + TrieRouter: () => TrieRouter2 + }); + module2.exports = __toCommonJS(router_exports); + var import_url = require_url(); + var import_node = require_node2(); + var TrieRouter2 = class { + name = "TrieRouter"; + #node; + constructor() { + this.#node = new import_node.Node(); + } + add(method, path, handler) { + const results = (0, import_url.checkOptionalParameter)(path); + if (results) { + for (let i = 0, len = results.length; i < len; i++) { + this.#node.insert(method, results[i], handler); + } + return; + } + this.#node.insert(method, path, handler); + } + match(method, path) { + return this.#node.search(method, path); + } + }; + } +}); + +// node_modules/hono/dist/cjs/router/trie-router/index.js +var require_trie_router = __commonJS({ + "node_modules/hono/dist/cjs/router/trie-router/index.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var trie_router_exports = {}; + __export(trie_router_exports, { + TrieRouter: () => import_router.TrieRouter + }); + module2.exports = __toCommonJS(trie_router_exports); + var import_router = require_router4(); + } +}); + +// node_modules/hono/dist/cjs/hono.js +var require_hono = __commonJS({ + "node_modules/hono/dist/cjs/hono.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var hono_exports = {}; + __export(hono_exports, { + Hono: () => Hono2 + }); + module2.exports = __toCommonJS(hono_exports); + var import_hono_base = require_hono_base(); + var import_reg_exp_router = require_reg_exp_router(); + var import_smart_router = require_smart_router(); + var import_trie_router = require_trie_router(); + var Hono2 = class extends import_hono_base.HonoBase { + constructor(options = {}) { + super(options); + this.router = options.router ?? new import_smart_router.SmartRouter({ + routers: [new import_reg_exp_router.RegExpRouter(), new import_trie_router.TrieRouter()] + }); + } + }; + } +}); + +// node_modules/hono/dist/cjs/index.js +var require_cjs = __commonJS({ + "node_modules/hono/dist/cjs/index.js"(exports2, module2) { + "use strict"; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var src_exports = {}; + __export(src_exports, { + Hono: () => import_hono.Hono + }); + module2.exports = __toCommonJS(src_exports); + var import_hono = require_hono(); + } +}); + +// node_modules/@hono/node-server/dist/index.js +var require_dist = __commonJS({ + "node_modules/@hono/node-server/dist/index.js"(exports2, module2) { + "use strict"; + var __create2 = Object.create; + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __getProtoOf2 = Object.getPrototypeOf; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp2(target, name, { get: all[name], enumerable: true }); + }; + var __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; + }; + var __toESM2 = (mod, isNodeMode, target) => (target = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps2( + // 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 || !mod || !mod.__esModule ? __defProp2(target, "default", { value: mod, enumerable: true }) : target, + mod + )); + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var src_exports = {}; + __export(src_exports, { + RequestError: () => RequestError, + createAdaptorServer: () => createAdaptorServer, + getRequestListener: () => getRequestListener, + serve: () => serve + }); + module2.exports = __toCommonJS(src_exports); + var import_node_http = require("http"); + var import_node_http22 = require("http2"); + var import_node_http2 = require("http2"); + var import_node_stream = require("stream"); + var RequestError = class extends Error { + constructor(message, options) { + super(message, options); + this.name = "RequestError"; + } + }; + var toRequestError = (e) => { + if (e instanceof RequestError) { + return e; + } + return new RequestError(e.message, { cause: e }); + }; + var GlobalRequest = global.Request; + var Request2 = class extends GlobalRequest { + constructor(input, options) { + if (typeof input === "object" && getRequestCache in input) { + input = input[getRequestCache](); + } + if (typeof options?.body?.getReader !== "undefined") { + ; + options.duplex ??= "half"; + } + super(input, options); + } + }; + var newHeadersFromIncoming = (incoming) => { + const headerRecord = []; + const rawHeaders = incoming.rawHeaders; + for (let i = 0; i < rawHeaders.length; i += 2) { + const { [i]: key, [i + 1]: value } = rawHeaders; + if (key.charCodeAt(0) !== /*:*/ + 58) { + headerRecord.push([key, value]); + } + } + return new Headers(headerRecord); + }; + var wrapBodyStream = Symbol("wrapBodyStream"); + var newRequestFromIncoming = (method, url, headers, incoming, abortController) => { + const init = { + method, + headers, + signal: abortController.signal + }; + if (method === "TRACE") { + init.method = "GET"; + const req = new Request2(url, init); + Object.defineProperty(req, "method", { + get() { + return "TRACE"; + } + }); + return req; + } + if (!(method === "GET" || method === "HEAD")) { + if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) { + init.body = new ReadableStream({ + start(controller) { + controller.enqueue(incoming.rawBody); + controller.close(); + } + }); + } else if (incoming[wrapBodyStream]) { + let reader; + init.body = new ReadableStream({ + async pull(controller) { + try { + reader ||= import_node_stream.Readable.toWeb(incoming).getReader(); + const { done, value } = await reader.read(); + if (done) { + controller.close(); + } else { + controller.enqueue(value); + } + } catch (error) { + controller.error(error); + } + } + }); + } else { + init.body = import_node_stream.Readable.toWeb(incoming); + } + } + return new Request2(url, init); + }; + var getRequestCache = Symbol("getRequestCache"); + var requestCache = Symbol("requestCache"); + var incomingKey = Symbol("incomingKey"); + var urlKey = Symbol("urlKey"); + var headersKey = Symbol("headersKey"); + var abortControllerKey = Symbol("abortControllerKey"); + var getAbortController = Symbol("getAbortController"); + var requestPrototype = { + get method() { + return this[incomingKey].method || "GET"; + }, + get url() { + return this[urlKey]; + }, + get headers() { + return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]); + }, + [getAbortController]() { + this[getRequestCache](); + return this[abortControllerKey]; + }, + [getRequestCache]() { + this[abortControllerKey] ||= new AbortController(); + return this[requestCache] ||= newRequestFromIncoming( + this.method, + this[urlKey], + this.headers, + this[incomingKey], + this[abortControllerKey] + ); + } + }; + [ + "body", + "bodyUsed", + "cache", + "credentials", + "destination", + "integrity", + "mode", + "redirect", + "referrer", + "referrerPolicy", + "signal", + "keepalive" + ].forEach((k) => { + Object.defineProperty(requestPrototype, k, { + get() { + return this[getRequestCache]()[k]; + } + }); + }); + ["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => { + Object.defineProperty(requestPrototype, k, { + value: function() { + return this[getRequestCache]()[k](); + } + }); + }); + Object.setPrototypeOf(requestPrototype, Request2.prototype); + var newRequest = (incoming, defaultHostname) => { + const req = Object.create(requestPrototype); + req[incomingKey] = incoming; + const incomingUrl = incoming.url || ""; + if (incomingUrl[0] !== "/" && // short-circuit for performance. most requests are relative URL. + (incomingUrl.startsWith("http://") || incomingUrl.startsWith("https://"))) { + if (incoming instanceof import_node_http2.Http2ServerRequest) { + throw new RequestError("Absolute URL for :path is not allowed in HTTP/2"); + } + try { + const url2 = new URL(incomingUrl); + req[urlKey] = url2.href; + } catch (e) { + throw new RequestError("Invalid absolute URL", { cause: e }); + } + return req; + } + const host = (incoming instanceof import_node_http2.Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname; + if (!host) { + throw new RequestError("Missing host header"); + } + let scheme; + if (incoming instanceof import_node_http2.Http2ServerRequest) { + scheme = incoming.scheme; + if (!(scheme === "http" || scheme === "https")) { + throw new RequestError("Unsupported scheme"); + } + } else { + scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http"; + } + const url = new URL(`${scheme}://${host}${incomingUrl}`); + if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) { + throw new RequestError("Invalid host header"); + } + req[urlKey] = url.href; + return req; + }; + var responseCache = Symbol("responseCache"); + var getResponseCache = Symbol("getResponseCache"); + var cacheKey = Symbol("cache"); + var GlobalResponse = global.Response; + var Response2 = class _Response { + #body; + #init; + [getResponseCache]() { + delete this[cacheKey]; + return this[responseCache] ||= new GlobalResponse(this.#body, this.#init); + } + constructor(body, init) { + let headers; + this.#body = body; + if (init instanceof _Response) { + const cachedGlobalResponse = init[responseCache]; + if (cachedGlobalResponse) { + this.#init = cachedGlobalResponse; + this[getResponseCache](); + return; + } else { + this.#init = init.#init; + headers = new Headers(init.#init.headers); + } + } else { + this.#init = init; + } + if (typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) { + headers ||= init?.headers || { "content-type": "text/plain; charset=UTF-8" }; + this[cacheKey] = [init?.status || 200, body, headers]; + } + } + get headers() { + const cache = this[cacheKey]; + if (cache) { + if (!(cache[2] instanceof Headers)) { + cache[2] = new Headers(cache[2]); + } + return cache[2]; + } + return this[getResponseCache]().headers; + } + get status() { + return this[cacheKey]?.[0] ?? this[getResponseCache]().status; + } + get ok() { + const status = this.status; + return status >= 200 && status < 300; + } + }; + ["body", "bodyUsed", "redirected", "statusText", "trailers", "type", "url"].forEach((k) => { + Object.defineProperty(Response2.prototype, k, { + get() { + return this[getResponseCache]()[k]; + } + }); + }); + ["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => { + Object.defineProperty(Response2.prototype, k, { + value: function() { + return this[getResponseCache]()[k](); + } + }); + }); + Object.setPrototypeOf(Response2, GlobalResponse); + Object.setPrototypeOf(Response2.prototype, GlobalResponse.prototype); + async function readWithoutBlocking(readPromise) { + return Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(void 0))]); + } + function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromise) { + const cancel = (error) => { + reader.cancel(error).catch(() => { + }); + }; + writable.on("close", cancel); + writable.on("error", cancel); + (currentReadPromise ?? reader.read()).then(flow, handleStreamError); + return reader.closed.finally(() => { + writable.off("close", cancel); + writable.off("error", cancel); + }); + function handleStreamError(error) { + if (error) { + writable.destroy(error); + } + } + function onDrain() { + reader.read().then(flow, handleStreamError); + } + function flow({ done, value }) { + try { + if (done) { + writable.end(); + } else if (!writable.write(value)) { + writable.once("drain", onDrain); + } else { + return reader.read().then(flow, handleStreamError); + } + } catch (e) { + handleStreamError(e); + } + } + } + function writeFromReadableStream(stream, writable) { + if (stream.locked) { + throw new TypeError("ReadableStream is locked."); + } else if (writable.destroyed) { + return; + } + return writeFromReadableStreamDefaultReader(stream.getReader(), writable); + } + var buildOutgoingHttpHeaders = (headers) => { + const res = {}; + if (!(headers instanceof Headers)) { + headers = new Headers(headers ?? void 0); + } + const cookies = []; + for (const [k, v] of headers) { + if (k === "set-cookie") { + cookies.push(v); + } else { + res[k] = v; + } + } + if (cookies.length > 0) { + res["set-cookie"] = cookies; + } + res["content-type"] ??= "text/plain; charset=UTF-8"; + return res; + }; + var X_ALREADY_SENT = "x-hono-already-sent"; + var import_node_crypto = __toESM2(require("crypto")); + var webFetch = global.fetch; + if (typeof global.crypto === "undefined") { + global.crypto = import_node_crypto.default; + } + global.fetch = (info, init) => { + init = { + // Disable compression handling so people can return the result of a fetch + // directly in the loader without messing with the Content-Encoding header. + compress: false, + ...init + }; + return webFetch(info, init); + }; + var outgoingEnded = Symbol("outgoingEnded"); + var handleRequestError = () => new Response(null, { + status: 400 + }); + var handleFetchError = (e) => new Response(null, { + status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500 + }); + var handleResponseError = (e, outgoing) => { + const err = e instanceof Error ? e : new Error("unknown error", { cause: e }); + if (err.code === "ERR_STREAM_PREMATURE_CLOSE") { + console.info("The user aborted a request."); + } else { + console.error(e); + if (!outgoing.headersSent) { + outgoing.writeHead(500, { "Content-Type": "text/plain" }); + } + outgoing.end(`Error: ${err.message}`); + outgoing.destroy(err); + } + }; + var flushHeaders = (outgoing) => { + if ("flushHeaders" in outgoing && outgoing.writable) { + outgoing.flushHeaders(); + } + }; + var responseViaCache = async (res, outgoing) => { + let [status, body, header] = res[cacheKey]; + if (header instanceof Headers) { + header = buildOutgoingHttpHeaders(header); + } + if (typeof body === "string") { + header["Content-Length"] = Buffer.byteLength(body); + } else if (body instanceof Uint8Array) { + header["Content-Length"] = body.byteLength; + } else if (body instanceof Blob) { + header["Content-Length"] = body.size; + } + outgoing.writeHead(status, header); + if (typeof body === "string" || body instanceof Uint8Array) { + outgoing.end(body); + } else if (body instanceof Blob) { + outgoing.end(new Uint8Array(await body.arrayBuffer())); + } else { + flushHeaders(outgoing); + await writeFromReadableStream(body, outgoing)?.catch( + (e) => handleResponseError(e, outgoing) + ); + } + ; + outgoing[outgoingEnded]?.(); + }; + var isPromise = (res) => typeof res.then === "function"; + var responseViaResponseObject = async (res, outgoing, options = {}) => { + if (isPromise(res)) { + if (options.errorHandler) { + try { + res = await res; + } catch (err) { + const errRes = await options.errorHandler(err); + if (!errRes) { + return; + } + res = errRes; + } + } else { + res = await res.catch(handleFetchError); + } + } + if (cacheKey in res) { + return responseViaCache(res, outgoing); + } + const resHeaderRecord = buildOutgoingHttpHeaders(res.headers); + if (res.body) { + const reader = res.body.getReader(); + const values = []; + let done = false; + let currentReadPromise = void 0; + if (resHeaderRecord["transfer-encoding"] !== "chunked") { + let maxReadCount = 2; + for (let i = 0; i < maxReadCount; i++) { + currentReadPromise ||= reader.read(); + const chunk = await readWithoutBlocking(currentReadPromise).catch((e) => { + console.error(e); + done = true; + }); + if (!chunk) { + if (i === 1) { + await new Promise((resolve) => setTimeout(resolve)); + maxReadCount = 3; + continue; + } + break; + } + currentReadPromise = void 0; + if (chunk.value) { + values.push(chunk.value); + } + if (chunk.done) { + done = true; + break; + } + } + if (done && !("content-length" in resHeaderRecord)) { + resHeaderRecord["content-length"] = values.reduce((acc, value) => acc + value.length, 0); + } + } + outgoing.writeHead(res.status, resHeaderRecord); + values.forEach((value) => { + ; + outgoing.write(value); + }); + if (done) { + outgoing.end(); + } else { + if (values.length === 0) { + flushHeaders(outgoing); + } + await writeFromReadableStreamDefaultReader(reader, outgoing, currentReadPromise); + } + } else if (resHeaderRecord[X_ALREADY_SENT]) { + } else { + outgoing.writeHead(res.status, resHeaderRecord); + outgoing.end(); + } + ; + outgoing[outgoingEnded]?.(); + }; + var getRequestListener = (fetchCallback, options = {}) => { + const autoCleanupIncoming = options.autoCleanupIncoming ?? true; + if (options.overrideGlobalObjects !== false && global.Request !== Request2) { + Object.defineProperty(global, "Request", { + value: Request2 + }); + Object.defineProperty(global, "Response", { + value: Response2 + }); + } + return async (incoming, outgoing) => { + let res, req; + try { + req = newRequest(incoming, options.hostname); + let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD"; + if (!incomingEnded) { + ; + incoming[wrapBodyStream] = true; + incoming.on("end", () => { + incomingEnded = true; + }); + if (incoming instanceof import_node_http22.Http2ServerRequest) { + ; + outgoing[outgoingEnded] = () => { + if (!incomingEnded) { + setTimeout(() => { + if (!incomingEnded) { + setTimeout(() => { + incoming.destroy(); + outgoing.destroy(); + }); + } + }); + } + }; + } + } + outgoing.on("close", () => { + const abortController = req[abortControllerKey]; + if (abortController) { + if (incoming.errored) { + req[abortControllerKey].abort(incoming.errored.toString()); + } else if (!outgoing.writableFinished) { + req[abortControllerKey].abort("Client connection prematurely closed."); + } + } + if (!incomingEnded) { + setTimeout(() => { + if (!incomingEnded) { + setTimeout(() => { + incoming.destroy(); + }); + } + }); + } + }); + res = fetchCallback(req, { incoming, outgoing }); + if (cacheKey in res) { + return responseViaCache(res, outgoing); + } + } catch (e) { + if (!res) { + if (options.errorHandler) { + res = await options.errorHandler(req ? e : toRequestError(e)); + if (!res) { + return; + } + } else if (!req) { + res = handleRequestError(); + } else { + res = handleFetchError(e); + } + } else { + return handleResponseError(e, outgoing); + } + } + try { + return await responseViaResponseObject(res, outgoing, options); + } catch (e) { + return handleResponseError(e, outgoing); + } + }; + }; + var createAdaptorServer = (options) => { + const fetchCallback = options.fetch; + const requestListener = getRequestListener(fetchCallback, { + hostname: options.hostname, + overrideGlobalObjects: options.overrideGlobalObjects, + autoCleanupIncoming: options.autoCleanupIncoming + }); + const createServer = options.createServer || import_node_http.createServer; + const server = createServer(options.serverOptions || {}, requestListener); + return server; + }; + var serve = (options, listeningListener) => { + const server = createAdaptorServer(options); + server.listen(options?.port ?? 3e3, options.hostname, () => { + const serverInfo = server.address(); + listeningListener && listeningListener(serverInfo); + }); + return server; + }; + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/hono/proxy.js +var require_proxy = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/hono/proxy.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } : function(o, v) { + o["default"] = v; + }); + var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + }(); + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.HonoProxy = void 0; + var pengueno_1 = require_dist2(); + var AppLifetimeMetric = pengueno_1.Metric.fromName("HonoAppLifetime").asResult(); + var AppRequestMetric = pengueno_1.Metric.fromName("HonoAppRequest"); + var HonoProxy = class { + server; + app; + constructor(server) { + this.server = server; + } + async initializeApp() { + if (this.app) + return this.app; + const { Hono: Hono2 } = await Promise.resolve().then(() => __importStar(require_cjs())); + this.app = pengueno_1.LogMetricTraceable.of(new Hono2()).flatMap(pengueno_1.TraceUtil.withTrace(`AppId = ${crypto.randomUUID()}`)).flatMap(pengueno_1.TraceUtil.withMetricTrace(AppLifetimeMetric)); + return this.app; + } + async serve(port, hostname) { + const { serve } = await Promise.resolve().then(() => __importStar(require_dist())); + const app = await this.initializeApp(); + return app.map((tApp) => pengueno_1.Either.fromFailable(() => { + const app2 = tApp.get(); + app2.all("*", async (c) => tApp.flatMap(pengueno_1.TraceUtil.withMetricTrace(AppRequestMetric)).move(c.req).flatMap((tRequest) => pengueno_1.PenguenoRequest.from(tRequest)).map((req) => this.server.serve(req)).map(pengueno_1.TraceUtil.promiseify((tResponse) => { + tResponse.trace.trace(AppRequestMetric.count.withValue(1)); + return new Response(tResponse.get().body(), tResponse.get()); + })).get()); + return serve({ + fetch: (_r) => app2.fetch(_r), + port, + hostname + }); + })).peek(pengueno_1.TraceUtil.traceResultingEither()).peek((tServe) => tServe.get().mapRight(() => tServe.trace.trace(`haii im still listening at http://${hostname}:${port} ~uwu dont think i forgot`))).map((tEitherServer) => tEitherServer.get().mapRight((server) => tEitherServer.move(server)).flatMapAsync((tServer) => pengueno_1.Signals.awaitClose(tServer))).peek(pengueno_1.TraceUtil.promiseify(pengueno_1.TraceUtil.traceResultingEither(AppLifetimeMetric))).get(); + } + }; + exports2.HonoProxy = HonoProxy; + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/hono/index.js +var require_hono2 = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/hono/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_proxy(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/server/index.js +var require_server = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/server/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_http(), exports2); + __exportStar(require_response(), exports2); + __exportStar(require_request(), exports2); + __exportStar(require_activity(), exports2); + __exportStar(require_filter(), exports2); + __exportStar(require_hono2(), exports2); + } +}); + +// node_modules/@emprespresso/pengueno/dist/index.js +var require_dist2 = __commonJS({ + "node_modules/@emprespresso/pengueno/dist/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_leftpadesque(), exports2); + __exportStar(require_types(), exports2); + __exportStar(require_trace4(), exports2); + __exportStar(require_process(), exports2); + __exportStar(require_server(), exports2); + } +}); + +// node_modules/@emprespresso/ci_model/dist/job/index.js +var import_pengueno = __toESM(require_dist2(), 1); +var JobTypes = [ + "fetch_code", + "ci_pipeline", + "build_docker_image.js", + "ansible_playbook.js", + "checkout_ci.js", + "npm_publish.js", + "coolify_webhook.js" +]; +var isJobType = (j) => typeof j === "string" && JobTypes.includes(j); +var isJob = (j) => !!((0, import_pengueno.isObject)(j) && "arguments" in j && (0, import_pengueno.isObject)(j.arguments) && "type" in j && isJobType(j.type) && j); + +// node_modules/@emprespresso/ci_model/dist/pipeline/index.js +var import_pengueno3 = __toESM(require_dist2(), 1); + +// node_modules/@emprespresso/ci_model/dist/pipeline/builder.js +var BasePipelineBuilder = class { + stages = []; + addStage(stage) { + this.stages.push(stage); + return this; + } + build() { + return new PipelineImpl(this.stages); + } +}; +var DefaultGitHookPipelineBuilder = class extends BasePipelineBuilder { + remoteUrl; + refname; + constructor(remoteUrl = process.env.remote, rev = process.env.rev, refname = process.env.refname) { + super(); + this.remoteUrl = remoteUrl; + this.refname = refname; + this.addStage({ + parallelJobs: [ + { + type: "fetch_code", + arguments: { + remoteUrl, + checkout: rev, + path: this.getSourceDestination() + } + } + ] + }); + } + getSourceDestination() { + return this.remoteUrl.replace(".git", "").split("/").at(-1) ?? "src"; + } + getBranch() { + const branchRefPrefix = "refs/heads/"; + return this.refname.split(branchRefPrefix).at(1); + } +}; + +// node_modules/@emprespresso/ci_model/dist/pipeline/impl.js +var import_pengueno2 = __toESM(require_dist2(), 1); +var PipelineImpl = class _PipelineImpl { + serialJobs; + constructor(serialJobs) { + this.serialJobs = serialJobs; + } + serialize() { + return JSON.stringify({ serialJobs: this.serialJobs }); + } + static from(s) { + return import_pengueno2.Either.fromFailable(() => JSON.parse(s)).flatMap((eitherPipelineJson) => isPipeline(eitherPipelineJson) ? import_pengueno2.Either.right(eitherPipelineJson) : import_pengueno2.Either.left(new Error("oh noes D: its a bad pipewine :(("))).mapRight((pipeline) => new _PipelineImpl(pipeline.serialJobs)); + } +}; + +// node_modules/@emprespresso/ci_model/dist/pipeline/index.js +var isPipelineStage = (t) => (0, import_pengueno3.isObject)(t) && "parallelJobs" in t && Array.isArray(t.parallelJobs) && t.parallelJobs.every((j) => isJob(j)); +var isPipeline = (t) => (0, import_pengueno3.isObject)(t) && "serialJobs" in t && Array.isArray(t.serialJobs) && t.serialJobs.every((p) => isPipelineStage(p)); + +// dist/ci.js +var REGISTRY = "img.liz.coffee"; +var NAMESPACE = "emprespresso"; +var IMG = "adelie"; +var getPipeline = () => { + const gitHookPipeline = new DefaultGitHookPipelineBuilder(); + const branch = gitHookPipeline.getBranch(); + if (!branch) + return gitHookPipeline.build(); + const commonBuildArgs = { + context: gitHookPipeline.getSourceDestination(), + registry: REGISTRY, + namespace: NAMESPACE, + imageTag: branch + }; + const adeliePackageBuild = { + type: "build_docker_image.js", + arguments: { + ...commonBuildArgs, + repository: IMG, + buildTarget: IMG, + dockerfile: "Dockerfile" + } + }; + gitHookPipeline.addStage({ + parallelJobs: [adeliePackageBuild] + }); + const webhookUrl = getReleaseDeployment(branch); + if (webhookUrl === null) { + return gitHookPipeline.build(); + } + const release = { + type: "coolify_webhook.js", + arguments: { + webhookUrl + } + }; + gitHookPipeline.addStage({ parallelJobs: [release] }); + return gitHookPipeline.build(); +}; +var getReleaseDeployment = (branch) => { + switch (branch) { + case "release": + return "https://plane.liz.coffee/api/v1/deploy?uuid=mkgcsgw800cwkog004sco44w&force=false"; + case "main": + return "https://plane.liz.coffee/api/v1/deploy?uuid=l8wksc08gcossck408gscgo8&force=false"; + default: + return null; + } +}; +var main = () => { + const data = getPipeline().serialize(); + process.stdout.write(data); +}; +main(); diff --git a/.ci/ci.json b/.ci/ci.json new file mode 100644 index 0000000..6a51028 --- /dev/null +++ b/.ci/ci.json @@ -0,0 +1,3 @@ +{ + "workflow": ".ci/ci.cjs" +} diff --git a/.ci/ci.ts b/.ci/ci.ts new file mode 100644 index 0000000..a08a283 --- /dev/null +++ b/.ci/ci.ts @@ -0,0 +1,76 @@ +#!/usr/bin/env node + +import { + AnsiblePlaybookJob, + BuildDockerImageJob, + DefaultGitHookPipelineBuilder, + NpmPublishJob, + FetchCodeJob, + CoolifyWebhookJob, + Job, +} from "@emprespresso/ci_model"; +import { join } from "path"; + +const REGISTRY = "img.liz.coffee"; +const NAMESPACE = "emprespresso"; +const IMG = "adelie"; +const REMOTE = "https://code.liz.coffee"; + +const getPipeline = () => { + const gitHookPipeline = new DefaultGitHookPipelineBuilder(); + const branch = gitHookPipeline.getBranch(); + if (!branch) return gitHookPipeline.build(); + + const commonBuildArgs = { + context: gitHookPipeline.getSourceDestination(), + registry: REGISTRY, + namespace: NAMESPACE, + imageTag: branch, + }; + + const adeliePackageBuild: BuildDockerImageJob = { + type: "build_docker_image.js", + arguments: { + ...commonBuildArgs, + repository: IMG, + buildTarget: IMG, + dockerfile: "Dockerfile", + }, + }; + gitHookPipeline.addStage({ + parallelJobs: [adeliePackageBuild], + }); + + const webhookUrl = getReleaseDeployment(branch); + if (webhookUrl === null) { + return gitHookPipeline.build(); + } + + const release: CoolifyWebhookJob = { + type: "coolify_webhook.js", + arguments: { + webhookUrl: webhookUrl!!, + }, + }; + gitHookPipeline.addStage({ parallelJobs: [release] }); + + return gitHookPipeline.build(); +}; + +const getReleaseDeployment = (branch: string) => { + switch (branch) { + case "release": + return "https://plane.liz.coffee/api/v1/deploy?uuid=p8g8gg4g88wow48488sgko8g&force=false"; + case "main": + return "https://plane.liz.coffee/api/v1/deploy?uuid=l8wksc08gcossck408gscgo8&force=false"; + default: + return null; + } +}; + +const main = () => { + const data = getPipeline().serialize(); + process.stdout.write(data); +}; + +main(); diff --git a/.ci/package-lock.json b/.ci/package-lock.json new file mode 100644 index 0000000..b400133 --- /dev/null +++ b/.ci/package-lock.json @@ -0,0 +1,544 @@ +{ + "name": ".ci", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@emprespresso/ci_model": "^0.3.0" + }, + "devDependencies": { + "@types/node": "^24.10.1", + "esbuild": "0.25.5" + } + }, + "node_modules/@emprespresso/ci_model": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@emprespresso/ci_model/-/ci_model-0.3.0.tgz", + "integrity": "sha512-Ik7hYZXqsTr+VH70F+ms8HGHb0QeSOpojiFYgQoyW4iXvEdih5jsAH5npWOqxOHviynRu8PIeqJ3z5HizvO1JQ==", + "dependencies": { + "@emprespresso/pengueno": "^0.0.17" + } + }, + "node_modules/@emprespresso/pengueno": { + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/@emprespresso/pengueno/-/pengueno-0.0.17.tgz", + "integrity": "sha512-pyfXLYGoLlQlMQAyYptRLLiRrnQmCju4I1/sv1KtsJv06oPvNqzHqUzJJGHrXg/90krg7GRcxLKQ0TnQ2Bskjw==", + "license": "MIT", + "engines": { + "node": ">=22.16.0", + "npm": ">=10.0.0" + }, + "optionalDependencies": { + "@hono/node-server": "^1.18.2", + "hono": "^4.8.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.7", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.7.tgz", + "integrity": "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@types/node": { + "version": "24.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.3.tgz", + "integrity": "sha512-gqkrWUsS8hcm0r44yn7/xZeV1ERva/nLgrLxFRUGb7aoNMIJfZJ3AC261zDQuOAKC7MiXai1WCpYc48jAHoShQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } + }, + "node_modules/hono": { + "version": "4.10.8", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.10.8.tgz", + "integrity": "sha512-DDT0A0r6wzhe8zCGoYOmMeuGu3dyTAE40HHjwUsWFTEy5WxK1x2WDSsBPlEXgPbRIFY6miDualuUDbasPogIww==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/.ci/package.json b/.ci/package.json new file mode 100644 index 0000000..e8b3c72 --- /dev/null +++ b/.ci/package.json @@ -0,0 +1,17 @@ +{ + "scripts": { + "build": "tsc && esbuild --format=cjs --target=node22 --platform=node --bundle --outfile=ci.cjs dist/ci.js && chmod +x ci.cjs", + "clean": "rm -rf dist ci.cjs" + }, + "dependencies": { + "@emprespresso/ci_model": "^0.3.0" + }, + "devDependencies": { + "@types/node": "^24.10.1", + "esbuild": "0.25.5" + }, + "files": [ + "dist/**/*", + "package.json" + ] +} diff --git a/.ci/tsconfig.json b/.ci/tsconfig.json new file mode 100644 index 0000000..7cfeadc --- /dev/null +++ b/.ci/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "moduleResolution": "node", + "outDir": "./dist", + "rootDir": "./", + "composite": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "noEmit": false, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["**/*.ts"], + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"] +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fa7cf1c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +dist +*.log +.git +.gitignore +.DS_Store +data +README.md diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..ab3fde4 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,12 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], + rules: { + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7970f70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +*.log +.DS_Store +data diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..96fec79 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "trailingComma": "all", + "singleQuote": true, + "printWidth": 120, + "tabWidth": 4 +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5fa41f6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM node:24-slim AS base + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci --quiet + +COPY . . +RUN npm run build + +# Create data directory +RUN mkdir -p /app/data + +EXPOSE 9000 + +ENTRYPOINT ["node", "dist/index.js"] +CMD ["--port", "9000", "--host", "0.0.0.0", "--data-dir", "/app/data"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..064fa71 --- /dev/null +++ b/README.md @@ -0,0 +1,345 @@ +# Posthook - Dynamic Webhook Receiver + +A simple API service for receiving and storing webhook requests with dynamic route registration. + +## Features + +- Dynamic route/topic registration +- Multiple content type support (JSON, form, text, raw) +- CSRF token protection with HMAC-signed stateless tokens +- hCaptcha protection per route +- ntfy notification alerts when webhooks are received +- Form redirect support (`_redirect` field) +- Request storage with timestamp-prefixed UUIDs +- Per-topic storage organization +- TypeScript with Pengueno framework +- Comprehensive metrics and tracing +- Single Dockerfile deployment + +## API Endpoints + +### Route Prefixes + +- **`/`** - Public routes (webhooks, token generation, health) +- **`/admin`** - Admin routes (route management) - Put behind OAuth proxy + +### Admin Endpoints + +**⚠️ Recommendation**: Put `/admin/*` behind an OAuth proxy (e.g., OAuth2 Proxy, Pomerium) for authentication. + +#### Register a Route +```bash +POST /admin/routes +Content-Type: application/json + +{ + "name": "my-webhook", + "contentType": "json", + "hcaptchaProtected": false +} +``` + +With hCaptcha protection: +```bash +POST /admin/routes +Content-Type: application/json + +{ + "name": "protected-webhook", + "contentType": "json", + "hcaptchaProtected": true, + "hcaptchaSecret": "your-hcaptcha-secret" +} +``` + +With ntfy notifications: +```bash +POST /admin/routes +Content-Type: application/json + +{ + "name": "notified-webhook", + "contentType": "json", + "hcaptchaProtected": false, + "ntfy": { + "enabled": true, + "server": "https://ntfy.sh", + "topic": "my-webhook-alerts" + } +} +``` + +With CSRF token protection: +```bash +POST /admin/routes +Content-Type: application/json + +{ + "name": "secure-form", + "contentType": "form", + "hcaptchaProtected": false, + "requireToken": true +} +``` + +#### List Routes +```bash +GET /admin/routes +``` + +### Public Webhook Endpoints + +#### Get CSRF Token (for routes with requireToken: true) +```bash +GET /hook/{routeName}/token +``` + +Response: +```json +{ + "ok": { + "token": "eyJ0aW1lc3RhbXAiOjE3MDI4MzQ1Njc4OTB5...", + "expiresAt": 1702835467890 + } +} +``` + +**Note**: Tokens expire in **30 seconds**. Generate the token server-side when rendering your form, not via client-side fetch. + +#### Send Webhook + +Send webhooks to registered routes: +```bash +POST /hook/{routeName} +Content-Type: application/json + +{ + "your": "data" +} +``` + +For hCaptcha-protected routes, include the token: +```bash +POST /hook/{routeName} +H-Captcha-Response: +Content-Type: application/json + +{ + "your": "data" +} +``` + +For form submissions with redirect (returns 303 redirect instead of JSON): +```bash +POST /hook/{routeName} +Content-Type: application/x-www-form-urlencoded + +name=John&email=john@example.com&_redirect=https://example.com/thank-you +``` + +Or in HTML: +```html +
+ + + + +
+``` + +For token-protected routes, include the token: +```bash +POST /hook/{routeName} +X-CSRF-Token: +Content-Type: application/json + +{ + "your": "data" +} +``` + +Or in form data: +```bash +POST /hook/{routeName} +Content-Type: application/x-www-form-urlencoded + +name=John&email=john@example.com&_token= +``` + +#### Health Check +```bash +GET /health +``` + +## Content Types + +- `json` - Parse request body as JSON +- `form` - Parse as URL-encoded form data (supports `_redirect` field) +- `text` - Store as plain text +- `raw` - Store raw body +- `multipart` - Multipart form data (not yet implemented, will support `_redirect` field) + +## CSRF Token Protection + +Posthook supports stateless CSRF token protection using HMAC-signed tokens. When `requireToken: true` is set on a route, clients must include a valid token with their request. + +### How it works: + +1. **Token Generation**: Tokens are HMAC-signed with a secret and contain: + - Route name (prevents token reuse across routes) + - Timestamp (30-second TTL) + +2. **Token Delivery**: Tokens can be provided via: + - `_token` field in form data (extracted and not stored) + - `X-CSRF-Token` request header + +3. **Validation**: Server validates: + - HMAC signature + - Route name matches + - Token not expired (30 seconds) + - Timestamp not from future + +### Token Secret Configuration: + +```bash +# Via environment variable (recommended for production) +export POSTHOOK_TOKEN_SECRET=your-long-random-secret-here +npm start + +# Via command line +npm start -- --token-secret your-long-random-secret-here + +# Auto-generated (not recommended - changes on restart) +npm start # Warns and generates random secret +``` + +### Server-Side Token Generation (Recommended): + +With a 30-second TTL, you should generate the token server-side when rendering the form, not via client-side JavaScript. + +**Example with server-side rendering**: +```html + +
+ + + + + +
+``` + +**Your server** (Node.js example): +```javascript +app.get('/contact', async (req, res) => { + const response = await fetch('http://localhost:9000/hook/contact-form/token'); + const { ok: { token } } = await response.json(); + res.render('contact', { token }); +}); +``` + +## Form Redirects + +When using `contentType: "form"` or `contentType: "multipart"`, you can include a `_redirect` field in your form data. If the request is successfully stored, the server will respond with a `303 See Other` redirect to the specified URL instead of a JSON response. + +The `_redirect` field is: +- Extracted from the form data and not stored in the request body +- Only honored on successful requests (validation failures, errors, etc. will still return JSON error responses) +- Useful for traditional HTML form submissions where you want to redirect users after submission + +Example form flow: +1. User submits form with `_redirect` field (and `_token` if required) +2. Posthook validates token (if required) and stores the request +3. Posthook sends ntfy notification (if configured) +4. Posthook responds with `303 See Other` and `Location: {redirect_url}` header +5. Browser automatically redirects user to the specified URL + +## ntfy Notifications + +When you register a route with ntfy enabled, posthook will send a notification to your specified ntfy server and topic whenever a webhook is received. The notification includes: + +- Request timestamp +- Request UUID +- HTTP method +- Route name + +Example notification: +``` +Title: Webhook received: my-webhook +Message: Method: POST +Timestamp: 2024-12-14T19:30:45.123Z +UUID: 123e4567-e89b-12d3-a456-426614174000 +``` + +## Storage Format + +Requests are stored per-topic in `data/{routeName}/{timestamp}_{uuid}.json`: + +```json +{ + "timestamp": 1702834567890, + "uuid": "123e4567-e89b-12d3-a456-426614174000", + "routeName": "my-webhook", + "method": "POST", + "headers": { + "content-type": "application/json" + }, + "body": { + "your": "data" + } +} +``` + +## Usage + +### Development +```bash +npm install +npm run build +export POSTHOOK_TOKEN_SECRET=your-secret-here +npm start -- --port 9000 --host 0.0.0.0 --data-dir ./data +``` + +### Docker +```bash +docker build -t posthook . +docker run -p 9000:9000 \ + -v $(pwd)/data:/app/data \ + -e POSTHOOK_TOKEN_SECRET=your-secret-here \ + posthook +``` + +### With OAuth Proxy (Recommended for Production) + +Protect admin routes with an OAuth proxy: + +```bash +# Run posthook +docker run -p 9000:9000 posthook + +# Run OAuth2 Proxy in front of /admin routes +docker run -p 4180:4180 \ + quay.io/oauth2-proxy/oauth2-proxy:latest \ + --upstream=http://localhost:9000/admin \ + --http-address=0.0.0.0:4180 \ + --provider=google \ + --client-id=your-client-id \ + --client-secret=your-client-secret \ + --cookie-secret=random-secret-here \ + --email-domain=yourdomain.com +``` + +Then configure your reverse proxy: +- `/admin/*` → OAuth2 Proxy on port 4180 +- `/` → Posthook on port 9000 + +## Configuration + +### Command Line Arguments: +- `--port` - Server port (default: 9000) +- `--host` - Server host (default: 0.0.0.0) +- `--data-dir` - Data storage directory (default: ./data) +- `--token-secret` - HMAC secret for token signing (optional, generates random if not provided) + +### Environment Variables: +- `POSTHOOK_TOKEN_SECRET` - HMAC secret for token signing (recommended for production) diff --git a/examples.sh b/examples.sh new file mode 100755 index 0000000..69c1eb8 --- /dev/null +++ b/examples.sh @@ -0,0 +1,162 @@ +#!/bin/bash + +# Example usage of posthook API + +BASE_URL="${BASE_URL:-http://localhost:9000}" + +echo "=== Posthook Examples ===" +echo + +# 1. Register a simple JSON webhook +echo "1. Registering a simple JSON webhook..." +curl -X POST "$BASE_URL/admin/routes" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "simple-webhook", + "contentType": "json", + "hcaptchaProtected": false + }' +echo +echo + +# 2. Register a form webhook +echo "2. Registering a form webhook..." +curl -X POST "$BASE_URL/admin/routes" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "form-webhook", + "contentType": "form", + "hcaptchaProtected": false + }' +echo +echo + +# 3. Register an hCaptcha-protected webhook +echo "3. Registering an hCaptcha-protected webhook..." +curl -X POST "$BASE_URL/admin/routes" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "protected-webhook", + "contentType": "json", + "hcaptchaProtected": true, + "hcaptchaSecret": "0x0000000000000000000000000000000000000000" + }' +echo +echo + +# 3b. Register a webhook with ntfy notifications +echo "3b. Registering a webhook with ntfy notifications..." +curl -X POST "$BASE_URL/admin/routes" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "ntfy-webhook", + "contentType": "json", + "hcaptchaProtected": false, + "ntfy": { + "enabled": true, + "server": "https://ntfy.sh", + "topic": "posthook-demo-alerts" + } + }' +echo +echo + +# 3c. Register a CSRF token-protected form +echo "3c. Registering a CSRF token-protected form..." +curl -X POST "$BASE_URL/admin/routes" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "secure-form", + "contentType": "form", + "hcaptchaProtected": false, + "requireToken": true + }' +echo +echo + +# 4. List all routes +echo "4. Listing all routes..." +curl -X GET "$BASE_URL/admin/routes" +echo +echo + +# 5. Send a test webhook to simple-webhook +echo "5. Sending test data to simple-webhook..." +curl -X POST "$BASE_URL/hook/simple-webhook" \ + -H "Content-Type: application/json" \ + -d '{ + "event": "test", + "data": { + "foo": "bar", + "timestamp": 1234567890 + } + }' +echo +echo + +# 6. Send a form webhook +echo "6. Sending form data to form-webhook..." +curl -X POST "$BASE_URL/hook/form-webhook" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "name=John&email=john@example.com&message=Hello+World" +echo +echo + +# 6b. Send a webhook to ntfy-enabled route (will trigger notification) +echo "6b. Sending webhook to ntfy-webhook (check https://ntfy.sh/posthook-demo-alerts)..." +curl -X POST "$BASE_URL/hook/ntfy-webhook" \ + -H "Content-Type: application/json" \ + -d '{ + "event": "test-notification", + "message": "This should trigger an ntfy alert" + }' +echo +echo + +# 6c. Send a form with redirect (should return 303 redirect) +echo "6c. Sending form with _redirect (should return 303 redirect)..." +curl -v -X POST "$BASE_URL/hook/form-webhook" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "name=Jane&email=jane@example.com&message=Testing+redirect&_redirect=https://example.com/thank-you" +echo +echo + +# 6d. Get CSRF token for secure-form +echo "6d. Getting CSRF token for secure-form..." +TOKEN_RESPONSE=$(curl -s -X GET "$BASE_URL/hook/secure-form/token") +TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.ok.token') +echo "Token: $TOKEN" +echo +echo + +# 6e. Send form with CSRF token +echo "6e. Sending form with CSRF token..." +curl -X POST "$BASE_URL/hook/secure-form" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "name=Secure&email=secure@example.com&message=With+token&_token=$TOKEN" +echo +echo + +# 6f. Try sending without token (should fail with 400) +echo "6f. Trying to send without token (should fail)..." +curl -X POST "$BASE_URL/hook/secure-form" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "name=Insecure&email=insecure@example.com&message=No+token" +echo +echo + +# 7. Test 404 on non-existent route +echo "7. Testing 404 on non-existent route..." +curl -X POST "$BASE_URL/hook/does-not-exist" \ + -H "Content-Type: application/json" \ + -d '{"test": true}' +echo +echo + +# 8. Health check +echo "8. Health check..." +curl -X GET "$BASE_URL/health" +echo +echo + +echo "=== Examples complete ===" diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d771e35 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1861 @@ +{ + "name": "@emprespresso/posthook", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@emprespresso/posthook", + "version": "0.1.0", + "dependencies": { + "@emprespresso/pengueno": "^0.0.17", + "@hono/node-server": "^1.14.0", + "hono": "^4.8.9" + }, + "devDependencies": { + "@types/node": "^24.0.3", + "@typescript-eslint/eslint-plugin": "^8.34.1", + "@typescript-eslint/parser": "^8.34.1", + "eslint": "^8.34.1", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-prettier": "^5.5.0", + "prettier": "^3.5.3", + "typescript": "^5.8.3" + }, + "engines": { + "node": ">=22.16.0", + "npm": ">=10.0.0" + } + }, + "node_modules/@emprespresso/pengueno": { + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/@emprespresso/pengueno/-/pengueno-0.0.17.tgz", + "integrity": "sha512-pyfXLYGoLlQlMQAyYptRLLiRrnQmCju4I1/sv1KtsJv06oPvNqzHqUzJJGHrXg/90krg7GRcxLKQ0TnQ2Bskjw==", + "license": "MIT", + "engines": { + "node": ">=22.16.0", + "npm": ">=10.0.0" + }, + "optionalDependencies": { + "@hono/node-server": "^1.18.2", + "hono": "^4.8.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.7", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.7.tgz", + "integrity": "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@types/node": { + "version": "24.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", + "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz", + "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/type-utils": "8.49.0", + "@typescript-eslint/utils": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.49.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz", + "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz", + "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.49.0", + "@typescript-eslint/types": "^8.49.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz", + "integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz", + "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz", + "integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz", + "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz", + "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.49.0", + "@typescript-eslint/tsconfig-utils": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", + "debug": "^4.3.4", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz", + "integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz", + "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.49.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hono": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.1.tgz", + "integrity": "sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7232519 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "@emprespresso/posthook", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "lint": "eslint src --ext .ts", + "lint:fix": "eslint src --ext .ts --fix", + "format": "prettier --write .", + "format:check": "prettier --check .", + "type-check": "tsc --noEmit", + "clean": "rm -rf dist node_modules", + "start": "node dist/index.js" + }, + "dependencies": { + "@emprespresso/pengueno": "^0.0.17", + "@hono/node-server": "^1.14.0", + "hono": "^4.8.9" + }, + "devDependencies": { + "@types/node": "^24.0.3", + "@typescript-eslint/eslint-plugin": "^8.34.1", + "@typescript-eslint/parser": "^8.34.1", + "eslint": "^8.34.1", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-prettier": "^5.5.0", + "prettier": "^3.5.3", + "typescript": "^5.8.3" + }, + "engines": { + "node": ">=22.16.0", + "npm": ">=10.0.0" + } +} diff --git a/src/activity/index.ts b/src/activity/index.ts new file mode 100644 index 0000000..20d123c --- /dev/null +++ b/src/activity/index.ts @@ -0,0 +1,438 @@ +import { + Either, + ErrorSource, + type IActivity, + type IEither, + type ITraceable, + jsonModel, + JsonResponse, + LogLevel, + LogMetricTraceSupplier, + Metric, + PenguenoError, + PenguenoResponse, + type PenguenoRequest, + type ServerTrace, + TraceUtil, +} from '@emprespresso/pengueno'; +import type { Storage } from '../storage/index.js'; +import type { RouteConfig } from '../types/index.js'; +import { isRouteConfig, ContentType } from '../types/index.js'; +import { verifyHCaptcha } from '../integrations/hcaptcha.js'; +import { sendNtfyNotification } from '../integrations/ntfy.js'; +import { TokenSigner } from '../token/index.js'; + +const routeConfigMetric = Metric.fromName('Route.Config').asResult(); +const webhookRequestMetric = Metric.fromName('Webhook.Process').asResult(); +const listRoutesMetric = Metric.fromName('Routes.List').asResult(); +const tokenGenerateMetric = Metric.fromName('Token.Generate').asResult(); + +export interface IRegisterRouteActivity { + registerRoute: IActivity; +} + +export class RegisterRouteActivityImpl implements IRegisterRouteActivity { + constructor(private readonly storage: Storage) {} + + private trace(r: ITraceable) { + return r.flatMap(TraceUtil.withClassTrace(this)).flatMap(TraceUtil.withMetricTrace(routeConfigMetric)); + } + + public registerRoute(r: ITraceable) { + const routeConfigTransformer = (j: ITraceable): IEither => { + const config = j.get(); + if (!isRouteConfig(config)) { + const err = 'Invalid route configuration'; + j.trace.traceScope(LogLevel.WARN).trace(err); + return Either.left(new PenguenoError(err, 400)); + } + return Either.right(config); + }; + + return this.trace(r) + .map(jsonModel(routeConfigTransformer)) + .map(async (tEitherConfig) => { + const eitherConfig = await tEitherConfig.get(); + return eitherConfig.flatMapAsync(async (config) => { + const eitherStored = await this.storage.registerRoute(config); + return eitherStored.mapLeft((e) => new PenguenoError(e.message, 500)); + }); + }) + .flatMapAsync( + TraceUtil.promiseify((tEitherStored) => { + const errorSource = tEitherStored + .get() + .left() + .map(({ source }) => source) + .orSome(() => ErrorSource.SYSTEM) + .get(); + const shouldWarn = errorSource === ErrorSource.USER; + return TraceUtil.traceResultingEither( + routeConfigMetric, + shouldWarn, + )(tEitherStored); + }), + ) + .peek( + TraceUtil.promiseify((tResult) => + tResult.get().mapRight(() => tResult.trace.trace('Route registered successfully')), + ), + ) + .map( + TraceUtil.promiseify((tEitherResult) => { + const result = tEitherResult.get().mapRight(() => ({ success: true })); + return new JsonResponse(r, result, { + status: result.fold( + ({ status }) => status, + () => 200, + ), + }); + }), + ) + .get(); + } +} + +export interface IWebhookActivity { + processWebhook: (routeName: string) => IActivity; +} + +export class WebhookActivityImpl implements IWebhookActivity { + constructor( + private readonly storage: Storage, + private readonly signer: TokenSigner, + ) {} + + private trace(r: ITraceable) { + return r.flatMap(TraceUtil.withClassTrace(this)).flatMap(TraceUtil.withMetricTrace(webhookRequestMetric)); + } + + private async parseBody( + req: PenguenoRequest, + contentType: ContentType, + ): Promise> { + try { + const rawBody = await req.req.text(); + + type ParsedBody = { body: unknown; redirect: string | undefined; token: string | undefined }; + + switch (contentType) { + case ContentType.JSON: + try { + return Either.right({ + body: JSON.parse(rawBody), + redirect: undefined, + token: undefined, + }); + } catch { + return Either.left(new PenguenoError('Invalid JSON', 400)); + } + + case ContentType.FORM: + try { + const formData = new URLSearchParams(rawBody); + const obj: Record = {}; + let redirect: string | undefined; + let token: string | undefined; + + for (const [key, value] of formData.entries()) { + if (key === '_redirect') { + redirect = value; + } else if (key === '_token') { + token = value; + } else { + obj[key] = value; + } + } + return Either.right({ body: obj, redirect, token }); + } catch { + return Either.left(new PenguenoError('Invalid form data', 400)); + } + + case ContentType.TEXT: + return Either.right({ body: rawBody, redirect: undefined, token: undefined }); + + case ContentType.RAW: + return Either.right({ body: rawBody, redirect: undefined, token: undefined }); + + case ContentType.MULTIPART: + return Either.left(new PenguenoError('Multipart not yet implemented', 501)); + + default: + return Either.right({ body: rawBody, redirect: undefined, token: undefined }); + } + } catch (err) { + return Either.left(new PenguenoError(err instanceof Error ? err.message : String(err), 500)); + } + } + + public processWebhook(routeName: string) { + return (r: ITraceable) => { + type WebhookResult = { success: true; stored: string; redirect: string | undefined }; + + return this.trace(r) + .flatMapAsync(async (tReq) => { + const route = this.storage.getRoute(routeName); + if (!route) { + tReq.trace.traceScope(LogLevel.WARN).trace(`Route not found: ${routeName}`); + return tReq.move( + Either.left(new PenguenoError('Route not found', 404)), + ); + } + + const req = tReq.get().req; + const headers = req.header(); + const query = req.query(); + + // Extract hCaptcha token if route is protected + if (route.hcaptchaProtected) { + const hCaptchaToken = headers['h-captcha-response'] ?? query['h-captcha-response']; + if (!hCaptchaToken) { + tReq.trace.traceScope(LogLevel.WARN).trace('Missing hCaptcha token'); + return tReq.move( + Either.left( + new PenguenoError('Missing hCaptcha token', 400), + ), + ); + } + + if (!route.hcaptchaSecret) { + tReq.trace.traceScope(LogLevel.ERROR).trace('hCaptcha secret not configured'); + return tReq.move( + Either.left( + new PenguenoError('Server misconfiguration', 500), + ), + ); + } + + const verifyResult = await verifyHCaptcha(hCaptchaToken, route.hcaptchaSecret); + const isValid = verifyResult.fold( + () => false, + (success) => success, + ); + + if (!isValid) { + tReq.trace.traceScope(LogLevel.WARN).trace('hCaptcha verification failed'); + return tReq.move( + Either.left( + new PenguenoError('hCaptcha verification failed', 403), + ), + ); + } + } + + // Parse body based on content type + const bodyResult = await this.parseBody(tReq.get(), route.contentType); + if (bodyResult.left().present()) { + return tReq.move(Either.left(bodyResult.left().get())); + } + + const { body, redirect, token: bodyToken } = bodyResult.right().get(); + + // Validate token if required + if (route.requireToken) { + const csrfToken = bodyToken ?? headers['x-csrf-token']; + if (!csrfToken) { + tReq.trace.traceScope(LogLevel.WARN).trace('Missing CSRF token'); + return tReq.move( + Either.left(new PenguenoError('Missing CSRF token', 400)), + ); + } + + const validationResult = this.signer.validate(csrfToken, routeName); + if (validationResult.left().present()) { + const error = validationResult.left().get(); + tReq.trace.traceScope(LogLevel.WARN).trace(`Token validation failed: ${error.message}`); + return tReq.move( + Either.left( + new PenguenoError('Invalid or expired token', 403), + ), + ); + } + } + + // Store the request + const storeResult = await this.storage.storeRequest(routeName, req.method, headers, body); + if (storeResult.left().present()) { + return tReq.move( + Either.left( + new PenguenoError(storeResult.left().get().message, 500), + ), + ); + } + + const storedRequest = storeResult.right().get(); + + // Send ntfy notification if configured + if (route.ntfy?.enabled) { + const ntfyResult = await sendNtfyNotification(route.ntfy, storedRequest); + if (ntfyResult.left().present()) { + const err = ntfyResult.left().get(); + tReq.trace.traceScope(LogLevel.WARN).trace(`ntfy notification failed: ${err.message}`); + } else { + tReq.trace.trace('ntfy notification sent'); + } + } + + const filename = `${storedRequest.timestamp}_${storedRequest.uuid}.json`; + return tReq.move( + Either.right({ + success: true, + stored: filename, + redirect, + }), + ); + }) + .flatMapAsync( + TraceUtil.promiseify((tEitherResult) => { + const errorSource = tEitherResult + .get() + .left() + .map(({ source }) => source) + .orSome(() => ErrorSource.SYSTEM) + .get(); + const shouldWarn = errorSource === ErrorSource.USER; + return TraceUtil.traceResultingEither( + webhookRequestMetric, + shouldWarn, + )(tEitherResult); + }), + ) + .peek( + TraceUtil.promiseify((tResult) => + tResult.get().mapRight(() => tResult.trace.trace('Webhook request processed successfully')), + ), + ) + .map( + TraceUtil.promiseify((tEitherResult) => { + const result = tEitherResult.get(); + + // Check if we should redirect + const shouldRedirect = result.fold( + () => false, + (data) => data.redirect !== undefined, + ); + + if (shouldRedirect) { + const redirectUrl = result.right().get().redirect!; + return new PenguenoResponse(r, '', { + status: 303, + statusText: 'See Other', + headers: { Location: redirectUrl }, + }); + } + + // Return JSON response for non-redirect cases + return new JsonResponse(r, result, { + status: result.fold( + ({ status }) => status, + () => 200, + ), + }); + }), + ) + .get(); + }; + } +} + +export interface IListRoutesActivity { + listRoutes: IActivity; +} + +export class ListRoutesActivityImpl implements IListRoutesActivity { + constructor(private readonly storage: Storage) {} + + private trace(r: ITraceable) { + return r.flatMap(TraceUtil.withClassTrace(this)).flatMap(TraceUtil.withMetricTrace(listRoutesMetric)); + } + + public listRoutes(r: ITraceable) { + type ListRoutesResult = { + routes: Array<{ + name: string; + contentType: ContentType; + hcaptchaProtected: boolean; + ntfyEnabled: boolean; + requireToken: boolean; + }>; + }; + + return this.trace(r) + .map((tReq) => { + void tReq.get(); + + const routes = this.storage.listRoutes(); + const sanitized = routes.map(({ name, contentType, hcaptchaProtected, ntfy, requireToken }) => ({ + name, + contentType, + hcaptchaProtected, + ntfyEnabled: ntfy?.enabled || false, + requireToken: requireToken || false, + })); + return Either.right({ routes: sanitized }); + }) + .peek( + TraceUtil.traceResultingEither( + listRoutesMetric, + ), + ) + .map( + async (tEitherResult) => + new JsonResponse(r, tEitherResult.get(), { + status: 200, + }), + ) + .get(); + } +} + +export interface ITokenGenerateActivity { + generateToken: (routeName: string) => IActivity; +} + +export class TokenGenerateActivityImpl implements ITokenGenerateActivity { + constructor( + private readonly storage: Storage, + private readonly signer: TokenSigner, + ) {} + + private trace(r: ITraceable) { + return r.flatMap(TraceUtil.withClassTrace(this)).flatMap(TraceUtil.withMetricTrace(tokenGenerateMetric)); + } + + public generateToken(routeName: string) { + return (r: ITraceable) => { + type TokenResult = { token: string; expiresAt: number }; + + return this.trace(r) + .map((tReq) => { + const route = this.storage.getRoute(routeName); + if (!route) { + tReq.trace.traceScope(LogLevel.WARN).trace(`Route not found: ${routeName}`); + return Either.left(new PenguenoError('Route not found', 404)); + } + + const token = this.signer.generate(routeName); + const expiresAt = Date.now() + 30 * 1000; // 30 seconds + + return Either.right({ token, expiresAt }); + }) + .peek( + TraceUtil.traceResultingEither( + tokenGenerateMetric, + ), + ) + .map( + async (tEitherResult) => + new JsonResponse(r, tEitherResult.get(), { + status: tEitherResult.get().fold( + ({ status }) => status, + () => 200, + ), + }), + ) + .get(); + }; + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..cbc946a --- /dev/null +++ b/src/index.ts @@ -0,0 +1,64 @@ +#!/usr/bin/env node + +import { argv, Either, getEnv, type IEither, HonoProxy } from '@emprespresso/pengueno'; +import { PosthookServer } from './server/index.js'; +import { Storage } from './storage/index.js'; +import { TokenSigner } from './token/index.js'; + +const main = async (_argv = process.argv.slice(2)): Promise> => { + const argsResult = argv( + ['--port', '--host', '--data-dir', '--token-secret'], + { + '--port': { absent: 9000, present: (port) => parseInt(port) }, + '--host': { absent: '0.0.0.0', present: (host) => host }, + '--data-dir': { absent: './data', present: (dir) => dir }, + '--token-secret': { absent: undefined, present: (secret) => secret }, + }, + _argv, + ); + + return argsResult + .mapRight((args) => ({ + port: args['--port'], + host: args['--host'], + dataDir: args['--data-dir'], + tokenSecret: args['--token-secret'], + })) + .flatMapAsync(async (config) => { + // Initialize storage + const storage = new Storage(config.dataDir); + const initResult = await storage.init(); + + if (initResult.left().present()) { + return Either.left(initResult.left().get()); + } + + // Initialize token signer (use env var or command line arg or generate random) + const envSecret = getEnv('POSTHOOK_TOKEN_SECRET'); + const secret = config.tokenSecret ?? (envSecret.present() ? envSecret.get() : undefined); + const signer = new TokenSigner(secret); + + if (config.tokenSecret === undefined && !envSecret.present()) { + console.log('No token secret provided; generated a random one (will not persist across restarts).'); + console.log('Set POSTHOOK_TOKEN_SECRET or pass --token-secret to use a persistent secret.'); + } + + console.log(`Storage initialized at: ${config.dataDir}`); + console.log(`Starting server on ${config.host}:${config.port}`); + + // Create and start server + const server = new PosthookServer(storage, signer); + const hono = new HonoProxy(server); + + return hono.serve(config.port, config.host); + }); +}; + +if (process.argv[1] === import.meta.filename) { + await main().then((eitherDone) => + eitherDone.mapLeft((err) => { + console.error('error:', err); + process.exit(1); + }), + ); +} diff --git a/src/integrations/hcaptcha.ts b/src/integrations/hcaptcha.ts new file mode 100644 index 0000000..78ff356 --- /dev/null +++ b/src/integrations/hcaptcha.ts @@ -0,0 +1,32 @@ +import { Either, type IEither } from '@emprespresso/pengueno'; + +export interface HCaptchaResponse { + success: boolean; + challenge_ts?: string; + hostname?: string; + 'error-codes'?: string[]; +} + +export async function verifyHCaptcha(token: string, secret: string): Promise> { + try { + const response = await fetch('https://hcaptcha.com/siteverify', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + secret, + response: token, + }), + }); + + if (!response.ok) { + return Either.left(new Error(`hCaptcha verification failed: ${response.statusText}`)); + } + + const result = (await response.json()) as HCaptchaResponse; + return Either.right(result.success); + } catch (err) { + return Either.left(err instanceof Error ? err : new Error(String(err))); + } +} diff --git a/src/integrations/ntfy.ts b/src/integrations/ntfy.ts new file mode 100644 index 0000000..cf815ed --- /dev/null +++ b/src/integrations/ntfy.ts @@ -0,0 +1,40 @@ +import { Either, type IEither } from '@emprespresso/pengueno'; +import type { NtfyConfig, StoredRequest } from '../types/index.js'; + +export interface NtfyNotification { + topic: string; + title: string; + message: string; + tags?: string[]; + priority?: number; +} + +export async function sendNtfyNotification(config: NtfyConfig, request: StoredRequest): Promise> { + if (!config.enabled || !config.server || !config.topic) { + return Either.right(undefined); + } + + try { + const url = `${config.server}/${config.topic}`; + const title = `Webhook received: ${request.routeName}`; + const message = `Method: ${request.method}\nTimestamp: ${new Date(request.timestamp).toISOString()}\nUUID: ${request.uuid}`; + + const response = await fetch(url, { + method: 'POST', + headers: { + Title: title, + Tags: 'webhook,posthook', + Priority: '3', + }, + body: message, + }); + + if (!response.ok) { + return Either.left(new Error(`ntfy notification failed: ${response.statusText}`)); + } + + return Either.right(undefined); + } catch (err) { + return Either.left(err instanceof Error ? err : new Error(String(err))); + } +} diff --git a/src/server/index.ts b/src/server/index.ts new file mode 100644 index 0000000..0747680 --- /dev/null +++ b/src/server/index.ts @@ -0,0 +1,84 @@ +import { + Either, + FourOhFourActivityImpl, + HealthCheckActivityImpl, + type HealthChecker, + HealthCheckOutput, + type IFourOhFourActivity, + type IHealthCheckActivity, + type ITraceable, + PenguenoRequest, + Server, + type ServerTrace, +} from '@emprespresso/pengueno'; +import { Storage } from '../storage/index.js'; +import { + ListRoutesActivityImpl, + RegisterRouteActivityImpl, + TokenGenerateActivityImpl, + WebhookActivityImpl, + type IListRoutesActivity, + type IRegisterRouteActivity, + type ITokenGenerateActivity, + type IWebhookActivity, +} from '../activity/index.js'; +import { TokenSigner } from '../token/index.js'; + +const defaultHealthCheck: HealthChecker = async (input) => { + void input.get(); + return Either.right(HealthCheckOutput.YAASSSLAYQUEEN); +}; + +export class PosthookServer implements Server { + constructor( + storage: Storage, + signer: TokenSigner, + healthCheck: HealthChecker = defaultHealthCheck, + private readonly healthCheckActivity: IHealthCheckActivity = new HealthCheckActivityImpl(healthCheck), + private readonly registerRouteActivity: IRegisterRouteActivity = new RegisterRouteActivityImpl(storage), + private readonly webhookActivity: IWebhookActivity = new WebhookActivityImpl(storage, signer), + private readonly tokenGenerateActivity: ITokenGenerateActivity = new TokenGenerateActivityImpl(storage, signer), + private readonly listRoutesActivity: IListRoutesActivity = new ListRoutesActivityImpl(storage), + private readonly fourOhFourActivity: IFourOhFourActivity = new FourOhFourActivityImpl(), + ) {} + + public serve(req: ITraceable) { + const url = new URL(req.get().req.url); + const { pathname } = url; + + // === Public Routes (/) === + + // Health check endpoint + if (pathname === '/health') { + return this.healthCheckActivity.checkHealth(req); + } + + // Token generation endpoint - /hook/{routeName}/token + const tokenMatch = pathname.match(/^\/hook\/([^/]+)\/token$/); + if (tokenMatch && req.get().req.method === 'GET') { + const routeName = tokenMatch[1]; + return this.tokenGenerateActivity.generateToken(routeName)(req); + } + + // Dynamic webhook endpoints - /hook/{routeName} + const hookMatch = pathname.match(/^\/hook\/([^/]+)$/); + if (hookMatch && req.get().req.method === 'POST') { + const routeName = hookMatch[1]; + return this.webhookActivity.processWebhook(routeName)(req); + } + + // === Admin Routes (/admin) - Put behind OAuth proxy === + + // Admin endpoints for route management + if (pathname === '/admin/routes' && req.get().req.method === 'POST') { + return this.registerRouteActivity.registerRoute(req); + } + + if (pathname === '/admin/routes' && req.get().req.method === 'GET') { + return this.listRoutesActivity.listRoutes(req); + } + + // 404 for everything else + return this.fourOhFourActivity.fourOhFour(req); + } +} diff --git a/src/storage/index.ts b/src/storage/index.ts new file mode 100644 index 0000000..2c8ffb2 --- /dev/null +++ b/src/storage/index.ts @@ -0,0 +1,114 @@ +import { randomUUID } from 'crypto'; +import { mkdir, writeFile, readFile } from 'fs/promises'; +import { join } from 'path'; +import { isSafeRouteName, type RouteConfig, type StoredRequest } from '../types/index.js'; +import { Either, type IEither } from '@emprespresso/pengueno'; + +export class Storage { + private routes: Map = new Map(); + + constructor(private readonly dataDir: string = './data') {} + + async init(): Promise> { + try { + await mkdir(this.dataDir, { recursive: true }); + await this.loadRoutes(); + return Either.right(undefined); + } catch (err) { + return Either.left(err instanceof Error ? err : new Error(String(err))); + } + } + + private async loadRoutes(): Promise { + try { + const routesPath = join(this.dataDir, 'routes.json'); + const data = await readFile(routesPath, 'utf-8'); + const routes = JSON.parse(data) as RouteConfig[]; + for (const route of routes) { + if (!isSafeRouteName(route.name)) { + continue; + } + this.routes.set(route.name, route); + } + } catch { + // routes file doesn't exist yet, that's ok + } + } + + private async saveRoutes(): Promise> { + try { + const routesPath = join(this.dataDir, 'routes.json'); + const routes = Array.from(this.routes.values()); + await writeFile(routesPath, JSON.stringify(routes, null, 2)); + return Either.right(undefined); + } catch (err) { + return Either.left(err instanceof Error ? err : new Error(String(err))); + } + } + + async registerRoute(config: RouteConfig): Promise> { + if (!isSafeRouteName(config.name)) { + return Either.left(new Error('Invalid route name')); + } + + this.routes.set(config.name, config); + const routeDir = join(this.dataDir, config.name); + try { + await mkdir(routeDir, { recursive: true }); + } catch (err) { + return Either.left(err instanceof Error ? err : new Error(String(err))); + } + return this.saveRoutes(); + } + + getRoute(name: string): RouteConfig | undefined { + if (!isSafeRouteName(name)) return undefined; + return this.routes.get(name); + } + + listRoutes(): RouteConfig[] { + return Array.from(this.routes.values()); + } + + async deleteRoute(name: string): Promise> { + if (!isSafeRouteName(name)) { + return Either.left(new Error('Invalid route name')); + } + this.routes.delete(name); + return this.saveRoutes(); + } + + async storeRequest( + routeName: string, + method: string, + headers: Record, + body: unknown, + files?: StoredRequest['files'], + ): Promise> { + if (!isSafeRouteName(routeName)) { + return Either.left(new Error('Invalid route name')); + } + + const timestamp = Date.now(); + const uuid = randomUUID(); + const filename = `${timestamp}_${uuid}.json`; + + const stored: StoredRequest = { + timestamp, + uuid, + routeName, + method, + headers, + body, + files, + }; + + const filepath = join(this.dataDir, routeName, filename); + try { + await writeFile(filepath, JSON.stringify(stored, null, 2)); + return Either.right(stored); + } catch (err) { + return Either.left(err instanceof Error ? err : new Error(String(err))); + } + } +} diff --git a/src/token/index.ts b/src/token/index.ts new file mode 100644 index 0000000..7251714 --- /dev/null +++ b/src/token/index.ts @@ -0,0 +1,76 @@ +import { createHmac, randomBytes } from 'crypto'; +import { Either, type IEither } from '@emprespresso/pengueno'; + +export interface TokenPayload { + routeName: string; + timestamp: number; +} + +export class TokenSigner { + private readonly secret: string; + private readonly ttlSeconds: number; + + constructor(secret?: string, ttlSeconds: number = 30) { + this.secret = secret || randomBytes(32).toString('hex'); + this.ttlSeconds = ttlSeconds; + } + + generate(routeName: string): string { + const timestamp = Date.now(); + const payload = JSON.stringify({ routeName, timestamp }); + const signature = this.sign(payload); + const token = Buffer.from(`${payload}.${signature}`).toString('base64url'); + return token; + } + + validate(token: string, expectedRoute: string): IEither { + try { + const decoded = Buffer.from(token, 'base64url').toString('utf-8'); + const lastDotIndex = decoded.lastIndexOf('.'); + + if (lastDotIndex === -1) { + return Either.left(new Error('Invalid token format')); + } + + const payload = decoded.substring(0, lastDotIndex); + const signature = decoded.substring(lastDotIndex + 1); + + // Verify signature + const expectedSignature = this.sign(payload); + if (signature !== expectedSignature) { + return Either.left(new Error('Invalid token signature')); + } + + // Parse payload + const parsed: TokenPayload = JSON.parse(payload); + + // Check route name + if (parsed.routeName !== expectedRoute) { + return Either.left(new Error('Token route mismatch')); + } + + // Check expiration + const now = Date.now(); + const age = (now - parsed.timestamp) / 1000; + if (age > this.ttlSeconds) { + return Either.left(new Error('Token expired')); + } + + if (age < 0) { + return Either.left(new Error('Token from future')); + } + + return Either.right(parsed); + } catch (err) { + return Either.left(err instanceof Error ? err : new Error(String(err))); + } + } + + private sign(payload: string): string { + return createHmac('sha256', this.secret).update(payload).digest('hex'); + } + + getSecret(): string { + return this.secret; + } +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..fbfc70d --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,78 @@ +export enum ContentType { + JSON = 'json', + FORM = 'form', + MULTIPART = 'multipart', + TEXT = 'text', + RAW = 'raw', +} + +export interface NtfyConfig { + enabled: boolean; + server?: string; + topic?: string; +} + +export interface RouteConfig { + name: string; + contentType: ContentType; + hcaptchaProtected: boolean; + hcaptchaSecret?: string; + ntfy?: NtfyConfig; + requireToken?: boolean; +} + +export interface StoredRequest { + timestamp: number; + uuid: string; + routeName: string; + method: string; + headers: Record; + body: unknown; + files?: Array<{ + filename: string; + contentType: string; + size: number; + path: string; + }>; +} + +const ROUTE_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i; + +export function isSafeRouteName(name: unknown): name is string { + if (typeof name !== 'string') return false; + if (name !== name.trim()) return false; + if (name === '.' || name === '..') return false; + if (name.includes('/') || name.includes('\\')) return false; + return ROUTE_NAME_PATTERN.test(name); +} + +export function isRouteConfig(obj: unknown): obj is RouteConfig { + if (typeof obj !== 'object' || obj === null) return false; + const r = obj as Record; + + const validBasic = + isSafeRouteName(r.name) && + typeof r.contentType === 'string' && + Object.values(ContentType).includes(r.contentType as ContentType) && + typeof r.hcaptchaProtected === 'boolean' && + (r.hcaptchaProtected === false || typeof r.hcaptchaSecret === 'string'); + + if (!validBasic) return false; + + // Validate ntfy config if present + if (r.ntfy !== undefined) { + if (typeof r.ntfy !== 'object' || r.ntfy === null) return false; + const ntfy = r.ntfy as Record; + if (typeof ntfy.enabled !== 'boolean') return false; + if (ntfy.enabled && (typeof ntfy.server !== 'string' || typeof ntfy.topic !== 'string')) { + return false; + } + } + + // Validate requireToken if present + if (r.requireToken !== undefined && typeof r.requireToken !== 'boolean') { + return false; + } + + return true; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0203457 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "lib": ["ES2022"], + "moduleResolution": "node", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist"] +} -- cgit v1.2.3-70-g09d2