diff options
| -rwxr-xr-x | .ci/ci.cjs | 5147 | ||||
| -rw-r--r-- | .ci/ci.json | 3 | ||||
| -rw-r--r-- | .ci/ci.ts | 76 | ||||
| -rw-r--r-- | .ci/package-lock.json | 544 | ||||
| -rw-r--r-- | .ci/package.json | 17 | ||||
| -rw-r--r-- | .ci/tsconfig.json | 28 | ||||
| -rw-r--r-- | .dockerignore | 8 | ||||
| -rw-r--r-- | .eslintrc.cjs | 12 | ||||
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | .prettierrc | 7 | ||||
| -rw-r--r-- | Dockerfile | 17 | ||||
| -rw-r--r-- | README.md | 345 | ||||
| -rwxr-xr-x | examples.sh | 162 | ||||
| -rw-r--r-- | package-lock.json | 1861 | ||||
| -rw-r--r-- | package.json | 36 | ||||
| -rw-r--r-- | src/activity/index.ts | 438 | ||||
| -rw-r--r-- | src/index.ts | 64 | ||||
| -rw-r--r-- | src/integrations/hcaptcha.ts | 32 | ||||
| -rw-r--r-- | src/integrations/ntfy.ts | 40 | ||||
| -rw-r--r-- | src/server/index.ts | 84 | ||||
| -rw-r--r-- | src/storage/index.ts | 114 | ||||
| -rw-r--r-- | src/token/index.ts | 76 | ||||
| -rw-r--r-- | src/types/index.ts | 78 | ||||
| -rw-r--r-- | tsconfig.json | 24 |
24 files changed, 9218 insertions, 0 deletions
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 = <metrics>${JSON.stringify(metrics)}</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: <token> +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 +<form action="/hook/my-form" method="POST"> + <input type="text" name="name" required> + <input type="email" name="email" required> + <input type="hidden" name="_redirect" value="https://example.com/thank-you"> + <button type="submit">Submit</button> +</form> +``` + +For token-protected routes, include the token: +```bash +POST /hook/{routeName} +X-CSRF-Token: <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=<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 fetches the token before rendering --> +<form action="/hook/contact-form" method="POST"> + <input type="hidden" name="_token" value="<%= token %>"> + <input type="text" name="name" required> + <input type="email" name="email" required> + <input type="hidden" name="_redirect" value="/thanks"> + <button type="submit">Submit</button> +</form> +``` + +**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<PenguenoRequest, ServerTrace>) { + return r.flatMap(TraceUtil.withClassTrace(this)).flatMap(TraceUtil.withMetricTrace(routeConfigMetric)); + } + + public registerRoute(r: ITraceable<PenguenoRequest, ServerTrace>) { + const routeConfigTransformer = (j: ITraceable<unknown, ServerTrace>): IEither<PenguenoError, RouteConfig> => { + 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<PenguenoError, void, LogMetricTraceSupplier>( + 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<PenguenoRequest, ServerTrace>) { + return r.flatMap(TraceUtil.withClassTrace(this)).flatMap(TraceUtil.withMetricTrace(webhookRequestMetric)); + } + + private async parseBody( + req: PenguenoRequest, + contentType: ContentType, + ): Promise<IEither<PenguenoError, { body: unknown; redirect: string | undefined; token: string | undefined }>> { + 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(<ParsedBody>{ + 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<string, string> = {}; + 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(<ParsedBody>{ body: obj, redirect, token }); + } catch { + return Either.left(new PenguenoError('Invalid form data', 400)); + } + + case ContentType.TEXT: + return Either.right(<ParsedBody>{ body: rawBody, redirect: undefined, token: undefined }); + + case ContentType.RAW: + return Either.right(<ParsedBody>{ body: rawBody, redirect: undefined, token: undefined }); + + case ContentType.MULTIPART: + return Either.left(new PenguenoError('Multipart not yet implemented', 501)); + + default: + return Either.right(<ParsedBody>{ 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<PenguenoRequest, ServerTrace>) => { + 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<PenguenoError, WebhookResult>(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<PenguenoError, WebhookResult>( + new PenguenoError('Missing hCaptcha token', 400), + ), + ); + } + + if (!route.hcaptchaSecret) { + tReq.trace.traceScope(LogLevel.ERROR).trace('hCaptcha secret not configured'); + return tReq.move( + Either.left<PenguenoError, WebhookResult>( + 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<PenguenoError, WebhookResult>( + 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<PenguenoError, WebhookResult>(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<PenguenoError, WebhookResult>(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<PenguenoError, WebhookResult>( + 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<PenguenoError, WebhookResult>( + 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<PenguenoError, WebhookResult>({ + 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<PenguenoError, WebhookResult, LogMetricTraceSupplier>( + 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<PenguenoRequest, ServerTrace>) { + return r.flatMap(TraceUtil.withClassTrace(this)).flatMap(TraceUtil.withMetricTrace(listRoutesMetric)); + } + + public listRoutes(r: ITraceable<PenguenoRequest, ServerTrace>) { + 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<PenguenoError, ListRoutesResult>({ routes: sanitized }); + }) + .peek( + TraceUtil.traceResultingEither<PenguenoError, ListRoutesResult, LogMetricTraceSupplier>( + 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<PenguenoRequest, ServerTrace>) { + return r.flatMap(TraceUtil.withClassTrace(this)).flatMap(TraceUtil.withMetricTrace(tokenGenerateMetric)); + } + + public generateToken(routeName: string) { + return (r: ITraceable<PenguenoRequest, ServerTrace>) => { + 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<PenguenoError, TokenResult>(new PenguenoError('Route not found', 404)); + } + + const token = this.signer.generate(routeName); + const expiresAt = Date.now() + 30 * 1000; // 30 seconds + + return Either.right<PenguenoError, TokenResult>({ token, expiresAt }); + }) + .peek( + TraceUtil.traceResultingEither<PenguenoError, TokenResult, LogMetricTraceSupplier>( + 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<IEither<Error, void>> => { + 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<IEither<Error, boolean>> { + 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<IEither<Error, void>> { + if (!config.enabled || !config.server || !config.topic) { + return Either.right(<void>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(<void>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<PenguenoRequest, ServerTrace>) { + 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<string, RouteConfig> = new Map(); + + constructor(private readonly dataDir: string = './data') {} + + async init(): Promise<IEither<Error, void>> { + try { + await mkdir(this.dataDir, { recursive: true }); + await this.loadRoutes(); + return Either.right(<void>undefined); + } catch (err) { + return Either.left(err instanceof Error ? err : new Error(String(err))); + } + } + + private async loadRoutes(): Promise<void> { + 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<IEither<Error, void>> { + 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(<void>undefined); + } catch (err) { + return Either.left(err instanceof Error ? err : new Error(String(err))); + } + } + + async registerRoute(config: RouteConfig): Promise<IEither<Error, void>> { + 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<IEither<Error, void>> { + 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<string, string>, + body: unknown, + files?: StoredRequest['files'], + ): Promise<IEither<Error, StoredRequest>> { + 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<Error, TokenPayload> { + 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<string, string>; + 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<string, unknown>; + + 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<string, unknown>; + 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"] +} |
