@xtr-dev/rondevu-server 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -474,6 +474,7 @@ var wNAF = (n) => {
474
474
  };
475
475
 
476
476
  // src/crypto.ts
477
+ var import_node_buffer = require("node:buffer");
477
478
  hashes.sha512Async = async (message) => {
478
479
  return new Uint8Array(await crypto.subtle.digest("SHA-512", message));
479
480
  };
@@ -482,8 +483,7 @@ var USERNAME_MIN_LENGTH = 3;
482
483
  var USERNAME_MAX_LENGTH = 32;
483
484
  var TIMESTAMP_TOLERANCE_MS = 5 * 60 * 1e3;
484
485
  function base64ToBytes(base64) {
485
- const binString = atob(base64);
486
- return Uint8Array.from(binString, (char) => char.codePointAt(0));
486
+ return new Uint8Array(import_node_buffer.Buffer.from(base64, "base64"));
487
487
  }
488
488
  function validateAuthMessage(expectedUsername, message) {
489
489
  const parts = message.split(":");
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts", "../src/app.ts", "../node_modules/@noble/ed25519/index.js", "../src/crypto.ts", "../src/rpc.ts", "../src/config.ts", "../src/storage/sqlite.ts", "../src/storage/hash-id.ts"],
4
- "sourcesContent": ["import { serve } from '@hono/node-server';\nimport { createApp } from './app.ts';\nimport { loadConfig } from './config.ts';\nimport { SQLiteStorage } from './storage/sqlite.ts';\nimport { Storage } from './storage/types.ts';\n\n/**\n * Main entry point for the standalone Node.js server\n */\nasync function main() {\n const config = loadConfig();\n\n console.log('Starting Rondevu server...');\n console.log('Configuration:', {\n port: config.port,\n storageType: config.storageType,\n storagePath: config.storagePath,\n offerDefaultTtl: `${config.offerDefaultTtl}ms`,\n offerMaxTtl: `${config.offerMaxTtl}ms`,\n offerMinTtl: `${config.offerMinTtl}ms`,\n cleanupInterval: `${config.cleanupInterval}ms`,\n maxOffersPerRequest: config.maxOffersPerRequest,\n corsOrigins: config.corsOrigins,\n version: config.version,\n });\n\n let storage: Storage;\n\n if (config.storageType === 'sqlite') {\n storage = new SQLiteStorage(config.storagePath);\n console.log('Using SQLite storage');\n } else {\n throw new Error('Unsupported storage type');\n }\n\n // Start periodic cleanup of expired offers\n const cleanupInterval = setInterval(async () => {\n try {\n const now = Date.now();\n const deleted = await storage.deleteExpiredOffers(now);\n if (deleted > 0) {\n console.log(`Cleanup: Deleted ${deleted} expired offer(s)`);\n }\n } catch (err) {\n console.error('Cleanup error:', err);\n }\n }, config.cleanupInterval);\n\n const app = createApp(storage, config);\n\n const server = serve({\n fetch: app.fetch,\n port: config.port,\n });\n\n console.log(`Server running on http://localhost:${config.port}`);\n console.log('Ready to accept connections');\n\n // Graceful shutdown handler\n const shutdown = async () => {\n console.log('\\nShutting down gracefully...');\n clearInterval(cleanupInterval);\n await storage.close();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\nmain().catch((err) => {\n console.error('Fatal error:', err);\n process.exit(1);\n});\n", "import { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { Storage } from './storage/types.ts';\nimport { Config } from './config.ts';\nimport { handleRpc, RpcRequest } from './rpc.ts';\n\n// Constants\nconst MAX_BATCH_SIZE = 100;\n\n/**\n * Creates the Hono application with RPC interface\n */\nexport function createApp(storage: Storage, config: Config) {\n const app = new Hono();\n\n // Enable CORS\n app.use('/*', cors({\n origin: (origin) => {\n if (config.corsOrigins.length === 1 && config.corsOrigins[0] === '*') {\n return origin;\n }\n if (config.corsOrigins.includes(origin)) {\n return origin;\n }\n return config.corsOrigins[0];\n },\n allowMethods: ['GET', 'POST', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Origin'],\n exposeHeaders: ['Content-Type'],\n credentials: false,\n maxAge: 86400,\n }));\n\n // Root endpoint - server info\n app.get('/', (c) => {\n return c.json({\n version: config.version,\n name: 'Rondevu',\n description: 'WebRTC signaling with RPC interface and Ed25519 authentication',\n }, 200);\n });\n\n // Health check\n app.get('/health', (c) => {\n return c.json({\n status: 'ok',\n timestamp: Date.now(),\n version: config.version,\n }, 200);\n });\n\n /**\n * POST /rpc\n * RPC endpoint - accepts single or batch method calls\n */\n app.post('/rpc', async (c) => {\n try {\n const body = await c.req.json();\n\n // Support both single request and batch array\n const requests: RpcRequest[] = Array.isArray(body) ? body : [body];\n\n // Validate requests\n if (requests.length === 0) {\n return c.json({ error: 'Empty request array' }, 400);\n }\n\n if (requests.length > MAX_BATCH_SIZE) {\n return c.json({ error: `Too many requests in batch (max ${MAX_BATCH_SIZE})` }, 400);\n }\n\n // Handle RPC\n const responses = await handleRpc(requests, storage, config);\n\n // Return single response or array based on input\n return c.json(Array.isArray(body) ? responses : responses[0], 200);\n } catch (err) {\n console.error('RPC error:', err);\n return c.json({\n success: false,\n error: 'Invalid request format',\n }, 400);\n }\n });\n\n // 404 for all other routes\n app.all('*', (c) => {\n return c.json({\n error: 'Not found. Use POST /rpc for all API calls.',\n }, 404);\n });\n\n return app;\n}\n", "/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */\n/**\n * 5KB JS implementation of ed25519 EdDSA signatures.\n * Compliant with RFC8032, FIPS 186-5 & ZIP215.\n * @module\n * @example\n * ```js\nimport * as ed from '@noble/ed25519';\n(async () => {\n const secretKey = ed.utils.randomSecretKey();\n const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);\n const pubKey = await ed.getPublicKeyAsync(secretKey); // Sync methods are also present\n const signature = await ed.signAsync(message, secretKey);\n const isValid = await ed.verifyAsync(signature, message, pubKey);\n})();\n```\n */\n/**\n * Curve params. ed25519 is twisted edwards curve. Equation is \u2212x\u00B2 + y\u00B2 = -a + dx\u00B2y\u00B2.\n * * P = `2n**255n - 19n` // field over which calculations are done\n * * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points\n * * h = 8 // cofactor\n * * a = `Fp.create(BigInt(-1))` // equation param\n * * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param\n * * Gx, Gy are coordinates of Generator / base point\n */\nconst ed25519_CURVE = {\n p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,\n n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,\n h: 8n,\n a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,\n d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,\n Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,\n Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,\n};\nconst { p: P, n: N, Gx, Gy, a: _a, d: _d, h } = ed25519_CURVE;\nconst L = 32; // field / group byte length\nconst L2 = 64;\n// Helpers and Precomputes sections are reused between libraries\n// ## Helpers\n// ----------\nconst captureTrace = (...args) => {\n if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(...args);\n }\n};\nconst err = (message = '') => {\n const e = new Error(message);\n captureTrace(e, err);\n throw e;\n};\nconst isBig = (n) => typeof n === 'bigint'; // is big integer\nconst isStr = (s) => typeof s === 'string'; // is string\nconst isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n/** Asserts something is Uint8Array. */\nconst abytes = (value, length, title = '') => {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n err(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);\n }\n return value;\n};\n/** create Uint8Array */\nconst u8n = (len) => new Uint8Array(len);\nconst u8fr = (buf) => Uint8Array.from(buf);\nconst padh = (n, pad) => n.toString(16).padStart(pad, '0');\nconst bytesToHex = (b) => Array.from(abytes(b))\n .map((e) => padh(e, 2))\n .join('');\nconst C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters\nconst _ch = (ch) => {\n if (ch >= C._0 && ch <= C._9)\n return ch - C._0; // '2' => 50-48\n if (ch >= C.A && ch <= C.F)\n return ch - (C.A - 10); // 'B' => 66-(65-10)\n if (ch >= C.a && ch <= C.f)\n return ch - (C.a - 10); // 'b' => 98-(97-10)\n return;\n};\nconst hexToBytes = (hex) => {\n const e = 'hex invalid';\n if (!isStr(hex))\n return err(e);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2)\n return err(e);\n const array = u8n(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n // treat each char as ASCII\n const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16\n const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char\n if (n1 === undefined || n2 === undefined)\n return err(e);\n array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9\n }\n return array;\n};\nconst cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments\nconst subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined, consider polyfill');\n// prettier-ignore\nconst concatBytes = (...arrs) => {\n const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); // create u8a of summed length\n let pad = 0; // walk through each array,\n arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type\n return r;\n};\n/** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */\nconst randomBytes = (len = L) => {\n const c = cr();\n return c.getRandomValues(u8n(len));\n};\nconst big = BigInt;\nconst assertRange = (n, min, max, msg = 'bad number: out of range') => (isBig(n) && min <= n && n < max ? n : err(msg));\n/** modular division */\nconst M = (a, b = P) => {\n const r = a % b;\n return r >= 0n ? r : b + r;\n};\nconst modN = (a) => M(a, N);\n/** Modular inversion using euclidean GCD (non-CT). No negative exponent for now. */\n// prettier-ignore\nconst invert = (num, md) => {\n if (num === 0n || md <= 0n)\n err('no inverse n=' + num + ' mod=' + md);\n let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;\n while (a !== 0n) {\n const q = b / a, r = b % a;\n const m = x - u * q, n = y - v * q;\n b = a, a = r, x = u, y = v, u = m, v = n;\n }\n return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point\n};\nconst callHash = (name) => {\n // @ts-ignore\n const fn = hashes[name];\n if (typeof fn !== 'function')\n err('hashes.' + name + ' not set');\n return fn;\n};\nconst hash = (msg) => callHash('sha512')(msg);\nconst apoint = (p) => (p instanceof Point ? p : err('Point expected'));\n// ## End of Helpers\n// -----------------\nconst B256 = 2n ** 256n;\n/** Point in XYZT extended coordinates. */\nclass Point {\n static BASE;\n static ZERO;\n X;\n Y;\n Z;\n T;\n constructor(X, Y, Z, T) {\n const max = B256;\n this.X = assertRange(X, 0n, max);\n this.Y = assertRange(Y, 0n, max);\n this.Z = assertRange(Z, 1n, max);\n this.T = assertRange(T, 0n, max);\n Object.freeze(this);\n }\n static CURVE() {\n return ed25519_CURVE;\n }\n static fromAffine(p) {\n return new Point(p.x, p.y, 1n, M(p.x * p.y));\n }\n /** RFC8032 5.1.3: Uint8Array to Point. */\n static fromBytes(hex, zip215 = false) {\n const d = _d;\n // Copy array to not mess it up.\n const normed = u8fr(abytes(hex, L));\n // adjust first LE byte = last BE byte\n const lastByte = hex[31];\n normed[31] = lastByte & ~0x80;\n const y = bytesToNumLE(normed);\n // zip215=true: 0 <= y < 2^256\n // zip215=false, RFC8032: 0 <= y < 2^255-19\n const max = zip215 ? B256 : P;\n assertRange(y, 0n, max);\n const y2 = M(y * y); // y\u00B2\n const u = M(y2 - 1n); // u=y\u00B2-1\n const v = M(d * y2 + 1n); // v=dy\u00B2+1\n let { isValid, value: x } = uvRatio(u, v); // (uv\u00B3)(uv\u2077)^(p-5)/8; square root\n if (!isValid)\n err('bad point: y not sqrt'); // not square root: bad point\n const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate\n const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit\n if (!zip215 && x === 0n && isLastByteOdd)\n err('bad point: x==0, isLastByteOdd'); // x=0, x_0=1\n if (isLastByteOdd !== isXOdd)\n x = M(-x);\n return new Point(x, y, 1n, M(x * y)); // Z=1, T=xy\n }\n static fromHex(hex, zip215) {\n return Point.fromBytes(hexToBytes(hex), zip215);\n }\n get x() {\n return this.toAffine().x;\n }\n get y() {\n return this.toAffine().y;\n }\n /** Checks if the point is valid and on-curve. */\n assertValidity() {\n const a = _a;\n const d = _d;\n const p = this;\n if (p.is0())\n return err('bad point: ZERO'); // TODO: optimize, with vars below?\n // Equation in affine coordinates: ax\u00B2 + y\u00B2 = 1 + dx\u00B2y\u00B2\n // Equation in projective coordinates (X/Z, Y/Z, Z): (aX\u00B2 + Y\u00B2)Z\u00B2 = Z\u2074 + dX\u00B2Y\u00B2\n const { X, Y, Z, T } = p;\n const X2 = M(X * X); // X\u00B2\n const Y2 = M(Y * Y); // Y\u00B2\n const Z2 = M(Z * Z); // Z\u00B2\n const Z4 = M(Z2 * Z2); // Z\u2074\n const aX2 = M(X2 * a); // aX\u00B2\n const left = M(Z2 * M(aX2 + Y2)); // (aX\u00B2 + Y\u00B2)Z\u00B2\n const right = M(Z4 + M(d * M(X2 * Y2))); // Z\u2074 + dX\u00B2Y\u00B2\n if (left !== right)\n return err('bad point: equation left != right (1)');\n // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T\n const XY = M(X * Y);\n const ZT = M(Z * T);\n if (XY !== ZT)\n return err('bad point: equation left != right (2)');\n return this;\n }\n /** Equality check: compare points P&Q. */\n equals(other) {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality\n const X1Z2 = M(X1 * Z2);\n const X2Z1 = M(X2 * Z1);\n const Y1Z2 = M(Y1 * Z2);\n const Y2Z1 = M(Y2 * Z1);\n return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;\n }\n is0() {\n return this.equals(I);\n }\n /** Flip point over y coordinate. */\n negate() {\n return new Point(M(-this.X), this.Y, this.Z, M(-this.T));\n }\n /** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */\n double() {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const a = _a;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd\n const A = M(X1 * X1);\n const B = M(Y1 * Y1);\n const C = M(2n * M(Z1 * Z1));\n const D = M(a * A);\n const x1y1 = X1 + Y1;\n const E = M(M(x1y1 * x1y1) - A - B);\n const G = D + B;\n const F = G - C;\n const H = D - B;\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n /** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */\n add(other) {\n const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;\n const { X: X2, Y: Y2, Z: Z2, T: T2 } = apoint(other); // doesn't check if other on-curve\n const a = _a;\n const d = _d;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3\n const A = M(X1 * X2);\n const B = M(Y1 * Y2);\n const C = M(T1 * d * T2);\n const D = M(Z1 * Z2);\n const E = M((X1 + Y1) * (X2 + Y2) - A - B);\n const F = M(D - C);\n const G = M(D + C);\n const H = M(B - a * A);\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n subtract(other) {\n return this.add(apoint(other).negate());\n }\n /**\n * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.\n * Uses {@link wNAF} for base point.\n * Uses fake point to mitigate side-channel leakage.\n * @param n scalar by which point is multiplied\n * @param safe safe mode guards against timing attacks; unsafe mode is faster\n */\n multiply(n, safe = true) {\n if (!safe && (n === 0n || this.is0()))\n return I;\n assertRange(n, 1n, N);\n if (n === 1n)\n return this;\n if (this.equals(G))\n return wNAF(n).p;\n // init result point & fake point\n let p = I;\n let f = G;\n for (let d = this; n > 0n; d = d.double(), n >>= 1n) {\n // if bit is present, add to point\n // if not present, add to fake, for timing safety\n if (n & 1n)\n p = p.add(d);\n else if (safe)\n f = f.add(d);\n }\n return p;\n }\n multiplyUnsafe(scalar) {\n return this.multiply(scalar, false);\n }\n /** Convert point to 2d xy affine point. (X, Y, Z) \u220B (x=X/Z, y=Y/Z) */\n toAffine() {\n const { X, Y, Z } = this;\n // fast-paths for ZERO point OR Z=1\n if (this.equals(I))\n return { x: 0n, y: 1n };\n const iz = invert(Z, P);\n // (Z * Z^-1) must be 1, otherwise bad math\n if (M(Z * iz) !== 1n)\n err('invalid inverse');\n // x = X*Z^-1; y = Y*Z^-1\n const x = M(X * iz);\n const y = M(Y * iz);\n return { x, y };\n }\n toBytes() {\n const { x, y } = this.assertValidity().toAffine();\n const b = numTo32bLE(y);\n // store sign in first LE byte\n b[31] |= x & 1n ? 0x80 : 0;\n return b;\n }\n toHex() {\n return bytesToHex(this.toBytes());\n }\n clearCofactor() {\n return this.multiply(big(h), false);\n }\n isSmallOrder() {\n return this.clearCofactor().is0();\n }\n isTorsionFree() {\n // Multiply by big number N. We can't `mul(N)` because of checks. Instead, we `mul(N/2)*2+1`\n let p = this.multiply(N / 2n, false).double();\n if (N % 2n)\n p = p.add(this);\n return p.is0();\n }\n}\n/** Generator / base point */\nconst G = new Point(Gx, Gy, 1n, M(Gx * Gy));\n/** Identity / zero point */\nconst I = new Point(0n, 1n, 1n, 0n);\n// Static aliases\nPoint.BASE = G;\nPoint.ZERO = I;\nconst numTo32bLE = (num) => hexToBytes(padh(assertRange(num, 0n, B256), L2)).reverse();\nconst bytesToNumLE = (b) => big('0x' + bytesToHex(u8fr(abytes(b)).reverse()));\nconst pow2 = (x, power) => {\n // pow2(x, 4) == x^(2^4)\n let r = x;\n while (power-- > 0n) {\n r *= r;\n r %= P;\n }\n return r;\n};\n// prettier-ignore\nconst pow_2_252_3 = (x) => {\n const x2 = (x * x) % P; // x^2, bits 1\n const b2 = (x2 * x) % P; // x^3, bits 11\n const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111\n const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111\n const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)\n const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)\n const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)\n const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)\n const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)\n const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)\n const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)\n const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.\n return { pow_p_5_8, b2 };\n};\nconst RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; // \u221A-1\n// for sqrt comp\n// prettier-ignore\nconst uvRatio = (u, v) => {\n const v3 = M(v * v * v); // v\u00B3\n const v7 = M(v3 * v3 * v); // v\u2077\n const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv\u2077)^(p-5)/8\n let x = M(u * v3 * pow); // (uv\u00B3)(uv\u2077)^(p-5)/8\n const vx2 = M(v * x * x); // vx\u00B2\n const root1 = x; // First root candidate\n const root2 = M(x * RM1); // Second root candidate; RM1 is \u221A-1\n const useRoot1 = vx2 === u; // If vx\u00B2 = u (mod p), x is a square root\n const useRoot2 = vx2 === M(-u); // If vx\u00B2 = -u, set x <-- x * 2^((p-1)/4)\n const noRoot = vx2 === M(-u * RM1); // There is no valid root, vx\u00B2 = -u\u221A-1\n if (useRoot1)\n x = root1;\n if (useRoot2 || noRoot)\n x = root2; // We return root2 anyway, for const-time\n if ((M(x) & 1n) === 1n)\n x = M(-x); // edIsNegative\n return { isValid: useRoot1 || useRoot2, value: x };\n};\n// N == L, just weird naming\nconst modL_LE = (hash) => modN(bytesToNumLE(hash)); // modulo L; but little-endian\n/** hashes.sha512 should conform to the interface. */\n// TODO: rename\nconst sha512a = (...m) => hashes.sha512Async(concatBytes(...m)); // Async SHA512\nconst sha512s = (...m) => callHash('sha512')(concatBytes(...m));\n// RFC8032 5.1.5\nconst hash2extK = (hashed) => {\n // slice creates a copy, unlike subarray\n const head = hashed.slice(0, L);\n head[0] &= 248; // Clamp bits: 0b1111_1000\n head[31] &= 127; // 0b0111_1111\n head[31] |= 64; // 0b0100_0000\n const prefix = hashed.slice(L, L2); // secret key \"prefix\"\n const scalar = modL_LE(head); // modular division over curve order\n const point = G.multiply(scalar); // public key point\n const pointBytes = point.toBytes(); // point serialized to Uint8Array\n return { head, prefix, scalar, point, pointBytes };\n};\n// RFC8032 5.1.5; getPublicKey async, sync. Hash priv key and extract point.\nconst getExtendedPublicKeyAsync = (secretKey) => sha512a(abytes(secretKey, L)).then(hash2extK);\nconst getExtendedPublicKey = (secretKey) => hash2extK(sha512s(abytes(secretKey, L)));\n/** Creates 32-byte ed25519 public key from 32-byte secret key. Async. */\nconst getPublicKeyAsync = (secretKey) => getExtendedPublicKeyAsync(secretKey).then((p) => p.pointBytes);\n/** Creates 32-byte ed25519 public key from 32-byte secret key. To use, set `hashes.sha512` first. */\nconst getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;\nconst hashFinishA = (res) => sha512a(res.hashable).then(res.finish);\nconst hashFinishS = (res) => res.finish(sha512s(res.hashable));\n// Code, shared between sync & async sign\nconst _sign = (e, rBytes, msg) => {\n const { pointBytes: P, scalar: s } = e;\n const r = modL_LE(rBytes); // r was created outside, reduce it modulo L\n const R = G.multiply(r).toBytes(); // R = [r]B\n const hashable = concatBytes(R, P, msg); // dom2(F, C) || R || A || PH(M)\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n const S = modN(r + modL_LE(hashed) * s); // S = (r + k * s) mod L; 0 <= s < l\n return abytes(concatBytes(R, numTo32bLE(S)), L2); // 64-byte sig: 32b R.x + 32b LE(S)\n };\n return { hashable, finish };\n};\n/**\n * Signs message using secret key. Async.\n * Follows RFC8032 5.1.6.\n */\nconst signAsync = async (message, secretKey) => {\n const m = abytes(message);\n const e = await getExtendedPublicKeyAsync(secretKey);\n const rBytes = await sha512a(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishA(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\n/**\n * Signs message using secret key. To use, set `hashes.sha512` first.\n * Follows RFC8032 5.1.6.\n */\nconst sign = (message, secretKey) => {\n const m = abytes(message);\n const e = getExtendedPublicKey(secretKey);\n const rBytes = sha512s(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishS(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\nconst defaultVerifyOpts = { zip215: true };\nconst _verify = (sig, msg, pub, opts = defaultVerifyOpts) => {\n sig = abytes(sig, L2); // Signature hex str/Bytes, must be 64 bytes\n msg = abytes(msg); // Message hex str/Bytes\n pub = abytes(pub, L);\n const { zip215 } = opts; // switch between zip215 and rfc8032 verif\n let A;\n let R;\n let s;\n let SB;\n let hashable = Uint8Array.of();\n try {\n A = Point.fromBytes(pub, zip215); // public key A decoded\n R = Point.fromBytes(sig.slice(0, L), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P\n s = bytesToNumLE(sig.slice(L, L2)); // Decode second half as an integer S\n SB = G.multiply(s, false); // in the range 0 <= s < L\n hashable = concatBytes(R.toBytes(), A.toBytes(), msg); // dom2(F, C) || R || A || PH(M)\n }\n catch (error) { }\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n if (SB == null)\n return false; // false if try-catch catched an error\n if (!zip215 && A.isSmallOrder())\n return false; // false for SBS: Strongly Binding Signature\n const k = modL_LE(hashed); // decode in little-endian, modulo L\n const RkA = R.add(A.multiply(k, false)); // [8]R + [8][k]A'\n return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A'\n };\n return { hashable, finish };\n};\n/** Verifies signature on message and public key. Async. Follows RFC8032 5.1.7. */\nconst verifyAsync = async (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishA(_verify(signature, message, publicKey, opts));\n/** Verifies signature on message and public key. To use, set `hashes.sha512` first. Follows RFC8032 5.1.7. */\nconst verify = (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishS(_verify(signature, message, publicKey, opts));\n/** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */\nconst etc = {\n bytesToHex: bytesToHex,\n hexToBytes: hexToBytes,\n concatBytes: concatBytes,\n mod: M,\n invert: invert,\n randomBytes: randomBytes,\n};\nconst hashes = {\n sha512Async: async (message) => {\n const s = subtle();\n const m = concatBytes(message);\n return u8n(await s.digest('SHA-512', m.buffer));\n },\n sha512: undefined,\n};\n// FIPS 186 B.4.1 compliant key generation produces private keys\n// with modulo bias being neglible. takes >N+16 bytes, returns (hash mod n-1)+1\nconst randomSecretKey = (seed = randomBytes(L)) => seed;\nconst keygen = (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = getPublicKey(secretKey);\n return { secretKey, publicKey };\n};\nconst keygenAsync = async (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = await getPublicKeyAsync(secretKey);\n return { secretKey, publicKey };\n};\n/** ed25519-specific key utilities. */\nconst utils = {\n getExtendedPublicKeyAsync: getExtendedPublicKeyAsync,\n getExtendedPublicKey: getExtendedPublicKey,\n randomSecretKey: randomSecretKey,\n};\n// ## Precomputes\n// --------------\nconst W = 8; // W is window size\nconst scalarBits = 256;\nconst pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8, NOT 32 - see wNAF loop\nconst pwindowSize = 2 ** (W - 1); // 128 for W=8\nconst precompute = () => {\n const points = [];\n let p = G;\n let b = p;\n for (let w = 0; w < pwindows; w++) {\n b = p;\n points.push(b);\n for (let i = 1; i < pwindowSize; i++) {\n b = b.add(p);\n points.push(b);\n } // i=1, bc we skip 0\n p = b.double();\n }\n return points;\n};\nlet Gpows = undefined; // precomputes for base point G\n// const-time negate\nconst ctneg = (cnd, p) => {\n const n = p.negate();\n return cnd ? n : p;\n};\n/**\n * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by\n * caching multiples of G (base point). Cache is stored in 32MB of RAM.\n * Any time `G.multiply` is done, precomputes are used.\n * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.\n *\n * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,\n * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.\n *\n * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().\n */\nconst wNAF = (n) => {\n const comp = Gpows || (Gpows = precompute());\n let p = I;\n let f = G; // f must be G, or could become I in the end\n const pow_2_w = 2 ** W; // 256 for W=8\n const maxNum = pow_2_w; // 256 for W=8\n const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111\n const shiftBy = big(W); // 8 for W=8\n for (let w = 0; w < pwindows; w++) {\n let wbits = Number(n & mask); // extract W bits.\n n >>= shiftBy; // shift number by W bits.\n // We use negative indexes to reduce size of precomputed table by 2x.\n // Instead of needing precomputes 0..256, we only calculate them for 0..128.\n // If an index > 128 is found, we do (256-index) - where 256 is next window.\n // Naive: index +127 => 127, +224 => 224\n // Optimized: index +127 => 127, +224 => 256-32\n if (wbits > pwindowSize) {\n wbits -= maxNum;\n n += 1n;\n }\n const off = w * pwindowSize;\n const offF = off; // offsets, evaluate both\n const offP = off + Math.abs(wbits) - 1;\n const isEven = w % 2 !== 0; // conditions, evaluate both\n const isNeg = wbits < 0;\n if (wbits === 0) {\n // off == I: can't add it. Adding random offF instead.\n f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point\n }\n else {\n p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point\n }\n }\n if (n !== 0n)\n err('invalid wnaf');\n return { p, f }; // return both real and fake points for JIT\n};\n// !! Remove the export to easily use in REPL / browser console\nexport { etc, getPublicKey, getPublicKeyAsync, hash, hashes, keygen, keygenAsync, Point, sign, signAsync, utils, verify, verifyAsync, };\n", "/**\n * Crypto utilities for Ed25519-based authentication\n * Uses @noble/ed25519 for Ed25519 signature verification\n * Uses Web Crypto API for compatibility with both Node.js and Cloudflare Workers\n */\n\nimport * as ed25519 from '@noble/ed25519';\n\n// Set SHA-512 hash function for ed25519 (required in @noble/ed25519 v3+)\n// Uses Web Crypto API (compatible with both Node.js and Cloudflare Workers)\ned25519.hashes.sha512Async = async (message: Uint8Array) => {\n return new Uint8Array(await crypto.subtle.digest('SHA-512', message as BufferSource));\n};\n\n// Username validation\nconst USERNAME_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;\nconst USERNAME_MIN_LENGTH = 3;\nconst USERNAME_MAX_LENGTH = 32;\n\n// Timestamp validation (5 minutes tolerance)\nconst TIMESTAMP_TOLERANCE_MS = 5 * 60 * 1000;\n\n/**\n * Generates an anonymous username for users who don't want to claim one\n * Format: anon-{timestamp}-{random}\n * This reduces collision probability to near-zero\n */\nexport function generateAnonymousUsername(): string {\n const timestamp = Date.now().toString(36);\n const random = crypto.getRandomValues(new Uint8Array(3));\n const hex = Array.from(random).map(b => b.toString(16).padStart(2, '0')).join('');\n return `anon-${timestamp}-${hex}`;\n}\n\n/**\n * Convert Uint8Array to base64 string\n */\nfunction bytesToBase64(bytes: Uint8Array): string {\n const binString = Array.from(bytes, (byte) =>\n String.fromCodePoint(byte)\n ).join('');\n return btoa(binString);\n}\n\n/**\n * Convert base64 string to Uint8Array\n */\nfunction base64ToBytes(base64: string): Uint8Array {\n const binString = atob(base64);\n return Uint8Array.from(binString, (char) => char.codePointAt(0)!);\n}\n\n/**\n * Validates a generic auth message format\n * Expected format: action:username:params:timestamp\n * Validates that the message contains the expected username and has a valid timestamp\n */\nexport function validateAuthMessage(\n expectedUsername: string,\n message: string\n): { valid: boolean; error?: string } {\n const parts = message.split(':');\n\n if (parts.length < 3) {\n return { valid: false, error: 'Invalid message format: must have at least action:username:timestamp' };\n }\n\n // Extract username (second part) and timestamp (last part)\n const messageUsername = parts[1];\n const timestamp = parseInt(parts[parts.length - 1], 10);\n\n // Validate username matches\n if (messageUsername !== expectedUsername) {\n return { valid: false, error: 'Username in message does not match authenticated username' };\n }\n\n // Validate timestamp\n if (isNaN(timestamp)) {\n return { valid: false, error: 'Invalid timestamp in message' };\n }\n\n const timestampCheck = validateTimestamp(timestamp);\n if (!timestampCheck.valid) {\n return timestampCheck;\n }\n\n return { valid: true };\n}\n\n// ===== Username and Ed25519 Signature Utilities =====\n\n/**\n * Validates username format\n * Rules: 3-32 chars, lowercase alphanumeric + dash, must start/end with alphanumeric\n */\nexport function validateUsername(username: string): { valid: boolean; error?: string } {\n if (typeof username !== 'string') {\n return { valid: false, error: 'Username must be a string' };\n }\n\n if (username.length < USERNAME_MIN_LENGTH) {\n return { valid: false, error: `Username must be at least ${USERNAME_MIN_LENGTH} characters` };\n }\n\n if (username.length > USERNAME_MAX_LENGTH) {\n return { valid: false, error: `Username must be at most ${USERNAME_MAX_LENGTH} characters` };\n }\n\n if (!USERNAME_REGEX.test(username)) {\n return { valid: false, error: 'Username must be lowercase alphanumeric with optional dashes, and start/end with alphanumeric' };\n }\n\n return { valid: true };\n}\n\n/**\n * Validates service FQN format (service:version@username or service:version)\n * Service name: lowercase alphanumeric with dots/dashes (e.g., chat, file-share, com.example.chat)\n * Version: semantic versioning (1.0.0, 2.1.3-beta, etc.)\n * Username: optional, lowercase alphanumeric with dashes\n */\nexport function validateServiceFqn(fqn: string): { valid: boolean; error?: string } {\n if (typeof fqn !== 'string') {\n return { valid: false, error: 'Service FQN must be a string' };\n }\n\n // Parse the FQN\n const parsed = parseServiceFqn(fqn);\n if (!parsed) {\n return { valid: false, error: 'Service FQN must be in format: service:version[@username]' };\n }\n\n const { serviceName, version, username } = parsed;\n\n // Validate service name (alphanumeric with dots/dashes)\n const serviceNameRegex = /^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/;\n if (!serviceNameRegex.test(serviceName)) {\n return { valid: false, error: 'Service name must be lowercase alphanumeric with optional dots/dashes' };\n }\n\n if (serviceName.length < 1 || serviceName.length > 128) {\n return { valid: false, error: 'Service name must be 1-128 characters' };\n }\n\n // Validate version (semantic versioning)\n const versionRegex = /^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-z0-9.-]+)?$/;\n if (!versionRegex.test(version)) {\n return { valid: false, error: 'Version must be semantic versioning (e.g., 1.0.0, 2.1.3-beta)' };\n }\n\n // Validate username if present\n if (username) {\n const usernameCheck = validateUsername(username);\n if (!usernameCheck.valid) {\n return usernameCheck;\n }\n }\n\n return { valid: true };\n}\n\n/**\n * Parse semantic version string into components\n */\nexport function parseVersion(version: string): { major: number; minor: number; patch: number; prerelease?: string } | null {\n const match = version.match(/^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-[a-z0-9.-]+)?$/);\n if (!match) return null;\n\n return {\n major: parseInt(match[1], 10),\n minor: parseInt(match[2], 10),\n patch: parseInt(match[3], 10),\n prerelease: match[4]?.substring(1), // Remove leading dash\n };\n}\n\n/**\n * Check if two versions are compatible (same major version)\n * Following semver rules: ^1.0.0 matches 1.x.x but not 2.x.x\n */\nexport function isVersionCompatible(requested: string, available: string): boolean {\n const req = parseVersion(requested);\n const avail = parseVersion(available);\n\n if (!req || !avail) return false;\n\n // Major version must match\n if (req.major !== avail.major) return false;\n\n // If major is 0, minor must also match (0.x.y is unstable)\n if (req.major === 0 && req.minor !== avail.minor) return false;\n\n // Available version must be >= requested version\n if (avail.minor < req.minor) return false;\n if (avail.minor === req.minor && avail.patch < req.patch) return false;\n\n // Prerelease versions are only compatible with exact matches\n if (req.prerelease && req.prerelease !== avail.prerelease) return false;\n\n return true;\n}\n\n/**\n * Parse service FQN into components\n * Formats supported:\n * - service:version@username (e.g., \"chat:1.0.0@alice\")\n * - service:version (e.g., \"chat:1.0.0\") for discovery\n */\nexport function parseServiceFqn(fqn: string): { serviceName: string; version: string; username: string | null } | null {\n if (!fqn || typeof fqn !== 'string') return null;\n\n // Check if username is present\n const atIndex = fqn.lastIndexOf('@');\n let serviceVersion: string;\n let username: string | null = null;\n\n if (atIndex > 0) {\n // Format: service:version@username\n serviceVersion = fqn.substring(0, atIndex);\n username = fqn.substring(atIndex + 1);\n } else {\n // Format: service:version (no username)\n serviceVersion = fqn;\n }\n\n // Split service:version\n const colonIndex = serviceVersion.indexOf(':');\n if (colonIndex <= 0) return null; // No colon or colon at start\n\n const serviceName = serviceVersion.substring(0, colonIndex);\n const version = serviceVersion.substring(colonIndex + 1);\n\n if (!serviceName || !version) return null;\n\n return {\n serviceName,\n version,\n username,\n };\n}\n\n/**\n * Validates timestamp is within acceptable range (prevents replay attacks)\n */\nexport function validateTimestamp(timestamp: number): { valid: boolean; error?: string } {\n if (typeof timestamp !== 'number' || !Number.isFinite(timestamp)) {\n return { valid: false, error: 'Timestamp must be a finite number' };\n }\n\n const now = Date.now();\n const diff = Math.abs(now - timestamp);\n\n if (diff > TIMESTAMP_TOLERANCE_MS) {\n return { valid: false, error: `Timestamp too old or too far in future (tolerance: ${TIMESTAMP_TOLERANCE_MS / 1000}s)` };\n }\n\n return { valid: true };\n}\n\n/**\n * Verifies Ed25519 signature\n * @param publicKey Base64-encoded Ed25519 public key (32 bytes)\n * @param signature Base64-encoded Ed25519 signature (64 bytes)\n * @param message Message that was signed (UTF-8 string)\n * @returns true if signature is valid, false otherwise\n */\nexport async function verifyEd25519Signature(\n publicKey: string,\n signature: string,\n message: string\n): Promise<boolean> {\n try {\n // Decode base64 to bytes\n const publicKeyBytes = base64ToBytes(publicKey);\n const signatureBytes = base64ToBytes(signature);\n\n // Encode message as UTF-8\n const encoder = new TextEncoder();\n const messageBytes = encoder.encode(message);\n\n // Verify signature using @noble/ed25519 (async version)\n const isValid = await ed25519.verifyAsync(signatureBytes, messageBytes, publicKeyBytes);\n return isValid;\n } catch (err) {\n console.error('Ed25519 signature verification failed:', err);\n return false;\n }\n}\n\n/**\n * Validates a username claim request\n * Verifies format, timestamp, and signature\n */\nexport async function validateUsernameClaim(\n username: string,\n publicKey: string,\n signature: string,\n message: string\n): Promise<{ valid: boolean; error?: string }> {\n // Validate username format\n const usernameCheck = validateUsername(username);\n if (!usernameCheck.valid) {\n return usernameCheck;\n }\n\n // Parse message format: \"claim:{username}:{timestamp}\"\n const parts = message.split(':');\n if (parts.length !== 3 || parts[0] !== 'claim' || parts[1] !== username) {\n return { valid: false, error: 'Invalid message format (expected: claim:{username}:{timestamp})' };\n }\n\n const timestamp = parseInt(parts[2], 10);\n if (isNaN(timestamp)) {\n return { valid: false, error: 'Invalid timestamp in message' };\n }\n\n // Validate timestamp\n const timestampCheck = validateTimestamp(timestamp);\n if (!timestampCheck.valid) {\n return timestampCheck;\n }\n\n // Verify signature\n const signatureValid = await verifyEd25519Signature(publicKey, signature, message);\n if (!signatureValid) {\n return { valid: false, error: 'Invalid signature' };\n }\n\n return { valid: true };\n}\n\n/**\n * Validates a service publish signature\n * Message format: publish:{username}:{serviceFqn}:{timestamp}\n */\nexport async function validateServicePublish(\n username: string,\n serviceFqn: string,\n publicKey: string,\n signature: string,\n message: string\n): Promise<{ valid: boolean; error?: string }> {\n // Validate username format\n const usernameCheck = validateUsername(username);\n if (!usernameCheck.valid) {\n return usernameCheck;\n }\n\n // Parse message format: \"publish:{username}:{serviceFqn}:{timestamp}\"\n // Note: serviceFqn can contain colons (e.g., \"chat:2.0.0@user\"), so we need careful parsing\n const parts = message.split(':');\n if (parts.length < 4 || parts[0] !== 'publish' || parts[1] !== username) {\n return { valid: false, error: 'Invalid message format (expected: publish:{username}:{serviceFqn}:{timestamp})' };\n }\n\n // The timestamp is the last part\n const timestamp = parseInt(parts[parts.length - 1], 10);\n if (isNaN(timestamp)) {\n return { valid: false, error: 'Invalid timestamp in message' };\n }\n\n // The serviceFqn is everything between username and timestamp\n const extractedServiceFqn = parts.slice(2, parts.length - 1).join(':');\n if (extractedServiceFqn !== serviceFqn) {\n return { valid: false, error: `Service FQN mismatch (expected: ${serviceFqn}, got: ${extractedServiceFqn})` };\n }\n\n // Validate timestamp\n const timestampCheck = validateTimestamp(timestamp);\n if (!timestampCheck.valid) {\n return timestampCheck;\n }\n\n // Verify signature\n const signatureValid = await verifyEd25519Signature(publicKey, signature, message);\n if (!signatureValid) {\n return { valid: false, error: 'Invalid signature' };\n }\n\n return { valid: true };\n}\n", "import { Context } from 'hono';\nimport { Storage } from './storage/types.ts';\nimport { Config } from './config.ts';\nimport {\n validateUsernameClaim,\n validateServicePublish,\n validateServiceFqn,\n parseServiceFqn,\n isVersionCompatible,\n verifyEd25519Signature,\n validateAuthMessage,\n validateUsername,\n} from './crypto.ts';\n\n// Constants\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * RPC request format\n */\nexport interface RpcRequest {\n method: string;\n message: string;\n signature: string;\n publicKey?: string; // Optional: for auto-claiming usernames\n params?: any;\n}\n\n/**\n * RPC response format\n */\nexport interface RpcResponse {\n success: boolean;\n result?: any;\n error?: string;\n}\n\n/**\n * RPC method handler\n */\ntype RpcHandler = (\n params: any,\n message: string,\n signature: string,\n publicKey: string | undefined,\n storage: Storage,\n config: Config\n) => Promise<any>;\n\n/**\n * Verify authentication for a method call\n * Automatically claims username if it doesn't exist\n */\nasync function verifyAuth(\n username: string,\n message: string,\n signature: string,\n publicKey: string | undefined,\n storage: Storage\n): Promise<{ valid: boolean; error?: string }> {\n // Get username record to fetch public key\n let usernameRecord = await storage.getUsername(username);\n\n // Auto-claim username if it doesn't exist\n if (!usernameRecord) {\n if (!publicKey) {\n return {\n valid: false,\n error: `Username \"${username}\" is not claimed and no public key provided for auto-claim.`,\n };\n }\n\n // Validate username format before claiming\n const usernameValidation = validateUsername(username);\n if (!usernameValidation.valid) {\n return usernameValidation;\n }\n\n // Verify signature against the current message (not a claim message)\n const signatureValid = await verifyEd25519Signature(publicKey, signature, message);\n if (!signatureValid) {\n return { valid: false, error: 'Invalid signature for auto-claim' };\n }\n\n // Auto-claim the username\n const expiresAt = Date.now() + 365 * 24 * 60 * 60 * 1000; // 365 days\n await storage.claimUsername({\n username,\n publicKey,\n expiresAt,\n });\n\n usernameRecord = await storage.getUsername(username);\n if (!usernameRecord) {\n return { valid: false, error: 'Failed to claim username' };\n }\n }\n\n // Verify Ed25519 signature\n const isValid = await verifyEd25519Signature(\n usernameRecord.publicKey,\n signature,\n message\n );\n if (!isValid) {\n return { valid: false, error: 'Invalid signature' };\n }\n\n // Validate message format and timestamp\n const validation = validateAuthMessage(username, message);\n if (!validation.valid) {\n return { valid: false, error: validation.error };\n }\n\n return { valid: true };\n}\n\n/**\n * Extract username from message\n */\nfunction extractUsername(message: string): string | null {\n // Message format: method:username:...\n const parts = message.split(':');\n if (parts.length < 2) return null;\n return parts[1];\n}\n\n/**\n * RPC Method Handlers\n */\n\nconst handlers: Record<string, RpcHandler> = {\n /**\n * Check if username is available\n */\n async getUser(params, message, signature, publicKey, storage, config) {\n const { username } = params;\n const claimed = await storage.getUsername(username);\n\n if (!claimed) {\n return {\n username,\n available: true,\n };\n }\n\n return {\n username: claimed.username,\n available: false,\n claimedAt: claimed.claimedAt,\n expiresAt: claimed.expiresAt,\n publicKey: claimed.publicKey,\n };\n },\n\n /**\n * Get service by FQN - Supports 3 modes:\n * 1. Direct lookup: FQN includes @username\n * 2. Paginated discovery: FQN without @username, with limit/offset\n * 3. Random discovery: FQN without @username, no limit\n */\n async getService(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, limit, offset } = params;\n const username = extractUsername(message);\n\n // Verify authentication\n if (username) {\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n }\n\n // Parse and validate FQN\n const fqnValidation = validateServiceFqn(serviceFqn);\n if (!fqnValidation.valid) {\n throw new Error(fqnValidation.error || 'Invalid service FQN');\n }\n\n const parsed = parseServiceFqn(serviceFqn);\n if (!parsed) {\n throw new Error('Failed to parse service FQN');\n }\n\n // Helper: Filter services by version compatibility\n const filterCompatibleServices = (services) => {\n return services.filter((s) => {\n const serviceVersion = parseServiceFqn(s.serviceFqn);\n return (\n serviceVersion &&\n isVersionCompatible(parsed.version, serviceVersion.version)\n );\n });\n };\n\n // Helper: Find available offer for service\n const findAvailableOffer = async (service) => {\n const offers = await storage.getOffersForService(service.id);\n return offers.find((o) => !o.answererUsername);\n };\n\n // Helper: Build service response object\n const buildServiceResponse = (service, offer) => ({\n serviceId: service.id,\n username: service.username,\n serviceFqn: service.serviceFqn,\n offerId: offer.id,\n sdp: offer.sdp,\n createdAt: service.createdAt,\n expiresAt: service.expiresAt,\n });\n\n // Mode 1: Paginated discovery\n if (limit !== undefined) {\n const pageLimit = Math.min(Math.max(1, limit), MAX_PAGE_SIZE);\n const pageOffset = Math.max(0, offset || 0);\n\n const allServices = await storage.getServicesByName(parsed.service, parsed.version);\n const compatibleServices = filterCompatibleServices(allServices);\n\n // Get unique services per username with available offers\n const usernameSet = new Set<string>();\n const uniqueServices: any[] = [];\n\n for (const service of compatibleServices) {\n if (!usernameSet.has(service.username)) {\n usernameSet.add(service.username);\n const availableOffer = await findAvailableOffer(service);\n\n if (availableOffer) {\n uniqueServices.push(buildServiceResponse(service, availableOffer));\n }\n }\n }\n\n // Paginate results\n const paginatedServices = uniqueServices.slice(pageOffset, pageOffset + pageLimit);\n\n return {\n services: paginatedServices,\n count: paginatedServices.length,\n limit: pageLimit,\n offset: pageOffset,\n };\n }\n\n // Mode 2: Direct lookup with username\n if (parsed.username) {\n const service = await storage.getServiceByFqn(serviceFqn);\n if (!service) {\n throw new Error('Service not found');\n }\n\n const availableOffer = await findAvailableOffer(service);\n if (!availableOffer) {\n throw new Error('Service has no available offers');\n }\n\n return buildServiceResponse(service, availableOffer);\n }\n\n // Mode 3: Random discovery without username\n const allServices = await storage.getServicesByName(parsed.service, parsed.version);\n const compatibleServices = filterCompatibleServices(allServices);\n\n if (compatibleServices.length === 0) {\n throw new Error('No services found');\n }\n\n const randomService = compatibleServices[Math.floor(Math.random() * compatibleServices.length)];\n const availableOffer = await findAvailableOffer(randomService);\n\n if (!availableOffer) {\n throw new Error('Service has no available offers');\n }\n\n return buildServiceResponse(randomService, availableOffer);\n },\n\n /**\n * Publish a service\n */\n async publishService(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, offers, ttl } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required for service publishing');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n // Validate service FQN\n const fqnValidation = validateServiceFqn(serviceFqn);\n if (!fqnValidation.valid) {\n throw new Error(fqnValidation.error || 'Invalid service FQN');\n }\n\n const parsed = parseServiceFqn(serviceFqn);\n if (!parsed || !parsed.username) {\n throw new Error('Service FQN must include username');\n }\n\n if (parsed.username !== username) {\n throw new Error('Service FQN username must match authenticated username');\n }\n\n // Validate offers\n if (!offers || !Array.isArray(offers) || offers.length === 0) {\n throw new Error('Must provide at least one offer');\n }\n\n if (offers.length > config.maxOffersPerRequest) {\n throw new Error(\n `Too many offers (max ${config.maxOffersPerRequest})`\n );\n }\n\n // Validate each offer has valid SDP\n offers.forEach((offer, index) => {\n if (!offer || typeof offer !== 'object') {\n throw new Error(`Invalid offer at index ${index}: must be an object`);\n }\n if (!offer.sdp || typeof offer.sdp !== 'string') {\n throw new Error(`Invalid offer at index ${index}: missing or invalid SDP`);\n }\n if (!offer.sdp.trim()) {\n throw new Error(`Invalid offer at index ${index}: SDP cannot be empty`);\n }\n });\n\n // Create service with offers\n const now = Date.now();\n const offerTtl =\n ttl !== undefined\n ? Math.min(\n Math.max(ttl, config.offerMinTtl),\n config.offerMaxTtl\n )\n : config.offerDefaultTtl;\n const expiresAt = now + offerTtl;\n\n // Prepare offer requests with TTL\n const offerRequests = offers.map(offer => ({\n username,\n serviceFqn,\n sdp: offer.sdp,\n expiresAt,\n }));\n\n const result = await storage.createService({\n serviceFqn,\n expiresAt,\n offers: offerRequests,\n });\n\n return {\n serviceId: result.service.id,\n username: result.service.username,\n serviceFqn: result.service.serviceFqn,\n offers: result.offers.map(offer => ({\n offerId: offer.id,\n sdp: offer.sdp,\n createdAt: offer.createdAt,\n expiresAt: offer.expiresAt,\n })),\n createdAt: result.service.createdAt,\n expiresAt: result.service.expiresAt,\n };\n },\n\n /**\n * Delete a service\n */\n async deleteService(params, message, signature, publicKey, storage, config) {\n const { serviceFqn } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n const parsed = parseServiceFqn(serviceFqn);\n if (!parsed || !parsed.username) {\n throw new Error('Service FQN must include username');\n }\n\n const service = await storage.getServiceByFqn(serviceFqn);\n if (!service) {\n throw new Error('Service not found');\n }\n\n const deleted = await storage.deleteService(service.id, username);\n if (!deleted) {\n throw new Error('Service not found or not owned by this username');\n }\n\n return { success: true };\n },\n\n /**\n * Answer an offer\n */\n async answerOffer(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, offerId, sdp } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n if (!sdp || typeof sdp !== 'string' || sdp.length === 0) {\n throw new Error('Invalid SDP');\n }\n\n if (sdp.length > 64 * 1024) {\n throw new Error('SDP too large (max 64KB)');\n }\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new Error('Offer not found');\n }\n\n if (offer.answererUsername) {\n throw new Error('Offer already answered');\n }\n\n await storage.answerOffer(offerId, username, sdp);\n\n return { success: true, offerId };\n },\n\n /**\n * Get answer for an offer\n */\n async getOfferAnswer(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, offerId } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new Error('Offer not found');\n }\n\n if (offer.username !== username) {\n throw new Error('Not authorized to access this offer');\n }\n\n if (!offer.answererUsername || !offer.answerSdp) {\n throw new Error('Offer not yet answered');\n }\n\n return {\n sdp: offer.answerSdp,\n offerId: offer.id,\n answererId: offer.answererUsername,\n answeredAt: offer.answeredAt,\n };\n },\n\n /**\n * Combined polling for answers and ICE candidates\n */\n async poll(params, message, signature, publicKey, storage, config) {\n const { since } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n const sinceTimestamp = since || 0;\n\n // Get all answered offers\n const answeredOffers = await storage.getAnsweredOffers(username);\n const filteredAnswers = answeredOffers.filter(\n (offer) => offer.answeredAt && offer.answeredAt > sinceTimestamp\n );\n\n // Get all user's offers\n const allOffers = await storage.getOffersByUsername(username);\n\n // For each offer, get ICE candidates from both sides\n const iceCandidatesByOffer: Record<string, any[]> = {};\n\n for (const offer of allOffers) {\n const offererCandidates = await storage.getIceCandidates(\n offer.id,\n 'offerer',\n sinceTimestamp\n );\n const answererCandidates = await storage.getIceCandidates(\n offer.id,\n 'answerer',\n sinceTimestamp\n );\n\n const allCandidates = [\n ...offererCandidates.map((c: any) => ({\n ...c,\n role: 'offerer' as const,\n })),\n ...answererCandidates.map((c: any) => ({\n ...c,\n role: 'answerer' as const,\n })),\n ];\n\n if (allCandidates.length > 0) {\n const isOfferer = offer.username === username;\n const filtered = allCandidates.filter((c) =>\n isOfferer ? c.role === 'answerer' : c.role === 'offerer'\n );\n\n if (filtered.length > 0) {\n iceCandidatesByOffer[offer.id] = filtered;\n }\n }\n }\n\n return {\n answers: filteredAnswers.map((offer) => ({\n offerId: offer.id,\n serviceId: offer.serviceId,\n answererId: offer.answererUsername,\n sdp: offer.answerSdp,\n answeredAt: offer.answeredAt,\n })),\n iceCandidates: iceCandidatesByOffer,\n };\n },\n\n /**\n * Add ICE candidates\n */\n async addIceCandidates(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, offerId, candidates } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n if (!Array.isArray(candidates) || candidates.length === 0) {\n throw new Error('Missing or invalid required parameter: candidates');\n }\n\n // Validate each candidate is an object (don't enforce structure per CLAUDE.md)\n candidates.forEach((candidate, index) => {\n if (!candidate || typeof candidate !== 'object') {\n throw new Error(`Invalid candidate at index ${index}: must be an object`);\n }\n });\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new Error('Offer not found');\n }\n\n const role = offer.username === username ? 'offerer' : 'answerer';\n const count = await storage.addIceCandidates(\n offerId,\n username,\n role,\n candidates\n );\n\n return { count, offerId };\n },\n\n /**\n * Get ICE candidates\n */\n async getIceCandidates(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, offerId, since } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n const sinceTimestamp = since || 0;\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new Error('Offer not found');\n }\n\n const isOfferer = offer.username === username;\n const role = isOfferer ? 'answerer' : 'offerer';\n\n const candidates = await storage.getIceCandidates(\n offerId,\n role,\n sinceTimestamp\n );\n\n return {\n candidates: candidates.map((c: any) => ({\n candidate: c.candidate,\n createdAt: c.createdAt,\n })),\n offerId,\n };\n },\n};\n\n/**\n * Handle RPC batch request\n */\nexport async function handleRpc(\n requests: RpcRequest[],\n storage: Storage,\n config: Config\n): Promise<RpcResponse[]> {\n const responses: RpcResponse[] = [];\n\n for (const request of requests) {\n try {\n const { method, message, signature, publicKey, params } = request;\n\n // Validate request\n if (!method || typeof method !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid method',\n });\n continue;\n }\n\n if (!message || typeof message !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid message',\n });\n continue;\n }\n\n if (!signature || typeof signature !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid signature',\n });\n continue;\n }\n\n // Get handler\n const handler = handlers[method];\n if (!handler) {\n responses.push({\n success: false,\n error: `Unknown method: ${method}`,\n });\n continue;\n }\n\n // Execute handler\n const result = await handler(\n params || {},\n message,\n signature,\n publicKey,\n storage,\n config\n );\n\n responses.push({\n success: true,\n result,\n });\n } catch (err) {\n responses.push({\n success: false,\n error: (err as Error).message || 'Internal server error',\n });\n }\n }\n\n return responses;\n}\n", "/**\n * Application configuration\n * Reads from environment variables with sensible defaults\n */\nexport interface Config {\n port: number;\n storageType: 'sqlite' | 'memory';\n storagePath: string;\n corsOrigins: string[];\n version: string;\n offerDefaultTtl: number;\n offerMaxTtl: number;\n offerMinTtl: number;\n cleanupInterval: number;\n maxOffersPerRequest: number;\n}\n\n/**\n * Loads configuration from environment variables\n */\nexport function loadConfig(): Config {\n return {\n port: parseInt(process.env.PORT || '3000', 10),\n storageType: (process.env.STORAGE_TYPE || 'sqlite') as 'sqlite' | 'memory',\n storagePath: process.env.STORAGE_PATH || ':memory:',\n corsOrigins: process.env.CORS_ORIGINS\n ? process.env.CORS_ORIGINS.split(',').map(o => o.trim())\n : ['*'],\n version: process.env.VERSION || 'unknown',\n offerDefaultTtl: parseInt(process.env.OFFER_DEFAULT_TTL || '60000', 10),\n offerMaxTtl: parseInt(process.env.OFFER_MAX_TTL || '86400000', 10),\n offerMinTtl: parseInt(process.env.OFFER_MIN_TTL || '60000', 10),\n cleanupInterval: parseInt(process.env.CLEANUP_INTERVAL || '60000', 10),\n maxOffersPerRequest: parseInt(process.env.MAX_OFFERS_PER_REQUEST || '100', 10)\n };\n}\n", "import Database from 'better-sqlite3';\nimport { randomUUID } from 'node:crypto';\nimport {\n Storage,\n Offer,\n IceCandidate,\n CreateOfferRequest,\n Username,\n ClaimUsernameRequest,\n Service,\n CreateServiceRequest,\n} from './types.ts';\nimport { generateOfferHash } from './hash-id.ts';\nimport { parseServiceFqn } from '../crypto.ts';\n\nconst YEAR_IN_MS = 365 * 24 * 60 * 60 * 1000; // 365 days\n\n/**\n * SQLite storage adapter for rondevu DNS-like system\n * Supports both file-based and in-memory databases\n */\nexport class SQLiteStorage implements Storage {\n private db: Database.Database;\n\n /**\n * Creates a new SQLite storage instance\n * @param path Path to SQLite database file, or ':memory:' for in-memory database\n */\n constructor(path: string = ':memory:') {\n this.db = new Database(path);\n this.initializeDatabase();\n }\n\n /**\n * Initializes database schema with username and service-based structure\n */\n private initializeDatabase(): void {\n this.db.exec(`\n -- WebRTC signaling offers\n CREATE TABLE IF NOT EXISTS offers (\n id TEXT PRIMARY KEY,\n username TEXT NOT NULL,\n service_id TEXT,\n sdp TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n last_seen INTEGER NOT NULL,\n answerer_username TEXT,\n answer_sdp TEXT,\n answered_at INTEGER,\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_offers_username ON offers(username);\n CREATE INDEX IF NOT EXISTS idx_offers_service ON offers(service_id);\n CREATE INDEX IF NOT EXISTS idx_offers_expires ON offers(expires_at);\n CREATE INDEX IF NOT EXISTS idx_offers_last_seen ON offers(last_seen);\n CREATE INDEX IF NOT EXISTS idx_offers_answerer ON offers(answerer_username);\n\n -- ICE candidates table\n CREATE TABLE IF NOT EXISTS ice_candidates (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n offer_id TEXT NOT NULL,\n username TEXT NOT NULL,\n role TEXT NOT NULL CHECK(role IN ('offerer', 'answerer')),\n candidate TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n FOREIGN KEY (offer_id) REFERENCES offers(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_ice_offer ON ice_candidates(offer_id);\n CREATE INDEX IF NOT EXISTS idx_ice_username ON ice_candidates(username);\n CREATE INDEX IF NOT EXISTS idx_ice_created ON ice_candidates(created_at);\n\n -- Usernames table\n CREATE TABLE IF NOT EXISTS usernames (\n username TEXT PRIMARY KEY,\n public_key TEXT NOT NULL UNIQUE,\n claimed_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n last_used INTEGER NOT NULL,\n metadata TEXT,\n CHECK(length(username) >= 3 AND length(username) <= 32)\n );\n\n CREATE INDEX IF NOT EXISTS idx_usernames_expires ON usernames(expires_at);\n CREATE INDEX IF NOT EXISTS idx_usernames_public_key ON usernames(public_key);\n\n -- Services table (new schema with extracted fields for discovery)\n CREATE TABLE IF NOT EXISTS services (\n id TEXT PRIMARY KEY,\n service_fqn TEXT NOT NULL,\n service_name TEXT NOT NULL,\n version TEXT NOT NULL,\n username TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n FOREIGN KEY (username) REFERENCES usernames(username) ON DELETE CASCADE,\n UNIQUE(service_fqn)\n );\n\n CREATE INDEX IF NOT EXISTS idx_services_fqn ON services(service_fqn);\n CREATE INDEX IF NOT EXISTS idx_services_discovery ON services(service_name, version);\n CREATE INDEX IF NOT EXISTS idx_services_username ON services(username);\n CREATE INDEX IF NOT EXISTS idx_services_expires ON services(expires_at);\n `);\n\n // Enable foreign keys\n this.db.pragma('foreign_keys = ON');\n }\n\n // ===== Offer Management =====\n\n async createOffers(offers: CreateOfferRequest[]): Promise<Offer[]> {\n const created: Offer[] = [];\n\n // Generate hash-based IDs for all offers first\n const offersWithIds = await Promise.all(\n offers.map(async (offer) => ({\n ...offer,\n id: offer.id || await generateOfferHash(offer.sdp),\n }))\n );\n\n // Use transaction for atomic creation\n const transaction = this.db.transaction((offersWithIds: (CreateOfferRequest & { id: string })[]) => {\n const offerStmt = this.db.prepare(`\n INSERT INTO offers (id, username, service_id, sdp, created_at, expires_at, last_seen)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const offer of offersWithIds) {\n const now = Date.now();\n\n // Insert offer\n offerStmt.run(\n offer.id,\n offer.username,\n offer.serviceId || null,\n offer.sdp,\n now,\n offer.expiresAt,\n now\n );\n\n created.push({\n id: offer.id,\n username: offer.username,\n serviceId: offer.serviceId || undefined,\n serviceFqn: offer.serviceFqn,\n sdp: offer.sdp,\n createdAt: now,\n expiresAt: offer.expiresAt,\n lastSeen: now,\n });\n }\n });\n\n transaction(offersWithIds);\n return created;\n }\n\n async getOffersByUsername(username: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE username = ? AND expires_at > ?\n ORDER BY last_seen DESC\n `);\n\n const rows = stmt.all(username, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getOfferById(offerId: string): Promise<Offer | null> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE id = ? AND expires_at > ?\n `);\n\n const row = stmt.get(offerId, Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return this.rowToOffer(row);\n }\n\n async deleteOffer(offerId: string, ownerUsername: string): Promise<boolean> {\n const stmt = this.db.prepare(`\n DELETE FROM offers\n WHERE id = ? AND username = ?\n `);\n\n const result = stmt.run(offerId, ownerUsername);\n return result.changes > 0;\n }\n\n async deleteExpiredOffers(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM offers WHERE expires_at < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n async answerOffer(\n offerId: string,\n answererUsername: string,\n answerSdp: string\n ): Promise<{ success: boolean; error?: string }> {\n // Check if offer exists and is not expired\n const offer = await this.getOfferById(offerId);\n\n if (!offer) {\n return {\n success: false,\n error: 'Offer not found or expired'\n };\n }\n\n // Check if offer already has an answerer\n if (offer.answererUsername) {\n return {\n success: false,\n error: 'Offer already answered'\n };\n }\n\n // Update offer with answer\n const stmt = this.db.prepare(`\n UPDATE offers\n SET answerer_username = ?, answer_sdp = ?, answered_at = ?\n WHERE id = ? AND answerer_username IS NULL\n `);\n\n const result = stmt.run(answererUsername, answerSdp, Date.now(), offerId);\n\n if (result.changes === 0) {\n return {\n success: false,\n error: 'Offer already answered (race condition)'\n };\n }\n\n return { success: true };\n }\n\n async getAnsweredOffers(offererUsername: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE username = ? AND answerer_username IS NOT NULL AND expires_at > ?\n ORDER BY answered_at DESC\n `);\n\n const rows = stmt.all(offererUsername, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n // ===== ICE Candidate Management =====\n\n async addIceCandidates(\n offerId: string,\n username: string,\n role: 'offerer' | 'answerer',\n candidates: any[]\n ): Promise<number> {\n const stmt = this.db.prepare(`\n INSERT INTO ice_candidates (offer_id, username, role, candidate, created_at)\n VALUES (?, ?, ?, ?, ?)\n `);\n\n const baseTimestamp = Date.now();\n const transaction = this.db.transaction((candidates: any[]) => {\n for (let i = 0; i < candidates.length; i++) {\n stmt.run(\n offerId,\n username,\n role,\n JSON.stringify(candidates[i]),\n baseTimestamp + i\n );\n }\n });\n\n transaction(candidates);\n return candidates.length;\n }\n\n async getIceCandidates(\n offerId: string,\n targetRole: 'offerer' | 'answerer',\n since?: number\n ): Promise<IceCandidate[]> {\n let query = `\n SELECT * FROM ice_candidates\n WHERE offer_id = ? AND role = ?\n `;\n\n const params: any[] = [offerId, targetRole];\n\n if (since !== undefined) {\n query += ' AND created_at > ?';\n params.push(since);\n }\n\n query += ' ORDER BY created_at ASC';\n\n const stmt = this.db.prepare(query);\n const rows = stmt.all(...params) as any[];\n\n return rows.map(row => ({\n id: row.id,\n offerId: row.offer_id,\n username: row.username,\n role: row.role,\n candidate: JSON.parse(row.candidate),\n createdAt: row.created_at,\n }));\n }\n\n // ===== Username Management =====\n\n async claimUsername(request: ClaimUsernameRequest): Promise<Username> {\n const now = Date.now();\n const expiresAt = now + YEAR_IN_MS;\n\n // Try to insert or update\n const stmt = this.db.prepare(`\n INSERT INTO usernames (username, public_key, claimed_at, expires_at, last_used, metadata)\n VALUES (?, ?, ?, ?, ?, NULL)\n ON CONFLICT(username) DO UPDATE SET\n expires_at = ?,\n last_used = ?\n WHERE public_key = ?\n `);\n\n const result = stmt.run(\n request.username,\n request.publicKey,\n now,\n expiresAt,\n now,\n expiresAt,\n now,\n request.publicKey\n );\n\n if (result.changes === 0) {\n throw new Error('Username already claimed by different public key');\n }\n\n return {\n username: request.username,\n publicKey: request.publicKey,\n claimedAt: now,\n expiresAt,\n lastUsed: now,\n };\n }\n\n async getUsername(username: string): Promise<Username | null> {\n const stmt = this.db.prepare(`\n SELECT * FROM usernames\n WHERE username = ? AND expires_at > ?\n `);\n\n const row = stmt.get(username, Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return {\n username: row.username,\n publicKey: row.public_key,\n claimedAt: row.claimed_at,\n expiresAt: row.expires_at,\n lastUsed: row.last_used,\n metadata: row.metadata || undefined,\n };\n }\n\n async touchUsername(username: string): Promise<boolean> {\n const now = Date.now();\n const expiresAt = now + YEAR_IN_MS;\n\n const stmt = this.db.prepare(`\n UPDATE usernames\n SET last_used = ?, expires_at = ?\n WHERE username = ? AND expires_at > ?\n `);\n\n const result = stmt.run(now, expiresAt, username, now);\n return result.changes > 0;\n }\n\n async deleteExpiredUsernames(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM usernames WHERE expires_at < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n // ===== Service Management =====\n\n async createService(request: CreateServiceRequest): Promise<{\n service: Service;\n offers: Offer[];\n }> {\n const serviceId = randomUUID();\n const now = Date.now();\n\n // Parse FQN to extract components\n const parsed = parseServiceFqn(request.serviceFqn);\n if (!parsed) {\n throw new Error(`Invalid service FQN: ${request.serviceFqn}`);\n }\n if (!parsed.username) {\n throw new Error(`Service FQN must include username: ${request.serviceFqn}`);\n }\n\n const { serviceName, version, username } = parsed;\n\n const transaction = this.db.transaction(() => {\n // Delete existing service with same (service_name, version, username) and its related offers (upsert behavior)\n const existingService = this.db.prepare(`\n SELECT id FROM services\n WHERE service_name = ? AND version = ? AND username = ?\n `).get(serviceName, version, username) as any;\n\n if (existingService) {\n // Delete related offers first (no FK cascade from offers to services)\n this.db.prepare(`\n DELETE FROM offers WHERE service_id = ?\n `).run(existingService.id);\n\n // Delete the service\n this.db.prepare(`\n DELETE FROM services WHERE id = ?\n `).run(existingService.id);\n }\n\n // Insert new service with extracted fields\n this.db.prepare(`\n INSERT INTO services (id, service_fqn, service_name, version, username, created_at, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `).run(\n serviceId,\n request.serviceFqn,\n serviceName,\n version,\n username,\n now,\n request.expiresAt\n );\n\n // Touch username to extend expiry (inline logic)\n const expiresAt = now + YEAR_IN_MS;\n this.db.prepare(`\n UPDATE usernames\n SET last_used = ?, expires_at = ?\n WHERE username = ? AND expires_at > ?\n `).run(now, expiresAt, username, now);\n });\n\n transaction();\n\n // Create offers with serviceId (after transaction)\n const offerRequests = request.offers.map(offer => ({\n ...offer,\n serviceId,\n }));\n const offers = await this.createOffers(offerRequests);\n\n return {\n service: {\n id: serviceId,\n serviceFqn: request.serviceFqn,\n serviceName,\n version,\n username,\n createdAt: now,\n expiresAt: request.expiresAt,\n },\n offers,\n };\n }\n\n async getOffersForService(serviceId: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE service_id = ? AND expires_at > ?\n ORDER BY created_at ASC\n `);\n\n const rows = stmt.all(serviceId, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getServiceById(serviceId: string): Promise<Service | null> {\n const stmt = this.db.prepare(`\n SELECT * FROM services\n WHERE id = ? AND expires_at > ?\n `);\n\n const row = stmt.get(serviceId, Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return this.rowToService(row);\n }\n\n async getServiceByFqn(serviceFqn: string): Promise<Service | null> {\n const stmt = this.db.prepare(`\n SELECT * FROM services\n WHERE service_fqn = ? AND expires_at > ?\n `);\n\n const row = stmt.get(serviceFqn, Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return this.rowToService(row);\n }\n\n async discoverServices(\n serviceName: string,\n version: string,\n limit: number,\n offset: number\n ): Promise<Service[]> {\n // Query for unique services with available offers\n // We join with offers and filter for available ones (answerer_username IS NULL)\n const stmt = this.db.prepare(`\n SELECT DISTINCT s.* FROM services s\n INNER JOIN offers o ON o.service_id = s.id\n WHERE s.service_name = ?\n AND s.version = ?\n AND s.expires_at > ?\n AND o.answerer_username IS NULL\n AND o.expires_at > ?\n ORDER BY s.created_at DESC\n LIMIT ? OFFSET ?\n `);\n\n const rows = stmt.all(serviceName, version, Date.now(), Date.now(), limit, offset) as any[];\n return rows.map(row => this.rowToService(row));\n }\n\n async getRandomService(serviceName: string, version: string): Promise<Service | null> {\n // Get a random service with an available offer\n const stmt = this.db.prepare(`\n SELECT s.* FROM services s\n INNER JOIN offers o ON o.service_id = s.id\n WHERE s.service_name = ?\n AND s.version = ?\n AND s.expires_at > ?\n AND o.answerer_username IS NULL\n AND o.expires_at > ?\n ORDER BY RANDOM()\n LIMIT 1\n `);\n\n const row = stmt.get(serviceName, version, Date.now(), Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return this.rowToService(row);\n }\n\n async deleteService(serviceId: string, username: string): Promise<boolean> {\n const stmt = this.db.prepare(`\n DELETE FROM services\n WHERE id = ? AND username = ?\n `);\n\n const result = stmt.run(serviceId, username);\n return result.changes > 0;\n }\n\n async deleteExpiredServices(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM services WHERE expires_at < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n async close(): Promise<void> {\n this.db.close();\n }\n\n // ===== Helper Methods =====\n\n /**\n * Helper method to convert database row to Offer object\n */\n private rowToOffer(row: any): Offer {\n return {\n id: row.id,\n username: row.username,\n serviceId: row.service_id || undefined,\n serviceFqn: row.service_fqn || undefined,\n sdp: row.sdp,\n createdAt: row.created_at,\n expiresAt: row.expires_at,\n lastSeen: row.last_seen,\n answererUsername: row.answerer_username || undefined,\n answerSdp: row.answer_sdp || undefined,\n answeredAt: row.answered_at || undefined,\n };\n }\n\n /**\n * Helper method to convert database row to Service object\n */\n private rowToService(row: any): Service {\n return {\n id: row.id,\n serviceFqn: row.service_fqn,\n serviceName: row.service_name,\n version: row.version,\n username: row.username,\n createdAt: row.created_at,\n expiresAt: row.expires_at,\n };\n }\n}\n", "/**\n * Generates a content-based offer ID using SHA-256 hash\n * Creates deterministic IDs based on offer SDP content\n * PeerID is not included as it's inferred from authentication\n * Uses Web Crypto API for compatibility with both Node.js and Cloudflare Workers\n *\n * @param sdp - The WebRTC SDP offer\n * @returns SHA-256 hash of the SDP content\n */\nexport async function generateOfferHash(sdp: string): Promise<string> {\n // Sanitize and normalize the offer content\n // Only include core offer content (not peerId - that's inferred from auth)\n const sanitizedOffer = {\n sdp\n };\n\n // Create non-prettified JSON string\n const jsonString = JSON.stringify(sanitizedOffer);\n\n // Convert string to Uint8Array for hashing\n const encoder = new TextEncoder();\n const data = encoder.encode(jsonString);\n\n // Generate SHA-256 hash\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\n // Convert hash to hex string\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n\n return hashHex;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { serve } from '@hono/node-server';\nimport { createApp } from './app.ts';\nimport { loadConfig } from './config.ts';\nimport { SQLiteStorage } from './storage/sqlite.ts';\nimport { Storage } from './storage/types.ts';\n\n/**\n * Main entry point for the standalone Node.js server\n */\nasync function main() {\n const config = loadConfig();\n\n console.log('Starting Rondevu server...');\n console.log('Configuration:', {\n port: config.port,\n storageType: config.storageType,\n storagePath: config.storagePath,\n offerDefaultTtl: `${config.offerDefaultTtl}ms`,\n offerMaxTtl: `${config.offerMaxTtl}ms`,\n offerMinTtl: `${config.offerMinTtl}ms`,\n cleanupInterval: `${config.cleanupInterval}ms`,\n maxOffersPerRequest: config.maxOffersPerRequest,\n corsOrigins: config.corsOrigins,\n version: config.version,\n });\n\n let storage: Storage;\n\n if (config.storageType === 'sqlite') {\n storage = new SQLiteStorage(config.storagePath);\n console.log('Using SQLite storage');\n } else {\n throw new Error('Unsupported storage type');\n }\n\n // Start periodic cleanup of expired offers\n const cleanupInterval = setInterval(async () => {\n try {\n const now = Date.now();\n const deleted = await storage.deleteExpiredOffers(now);\n if (deleted > 0) {\n console.log(`Cleanup: Deleted ${deleted} expired offer(s)`);\n }\n } catch (err) {\n console.error('Cleanup error:', err);\n }\n }, config.cleanupInterval);\n\n const app = createApp(storage, config);\n\n const server = serve({\n fetch: app.fetch,\n port: config.port,\n });\n\n console.log(`Server running on http://localhost:${config.port}`);\n console.log('Ready to accept connections');\n\n // Graceful shutdown handler\n const shutdown = async () => {\n console.log('\\nShutting down gracefully...');\n clearInterval(cleanupInterval);\n await storage.close();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\nmain().catch((err) => {\n console.error('Fatal error:', err);\n process.exit(1);\n});\n", "import { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { Storage } from './storage/types.ts';\nimport { Config } from './config.ts';\nimport { handleRpc, RpcRequest } from './rpc.ts';\n\n// Constants\nconst MAX_BATCH_SIZE = 100;\n\n/**\n * Creates the Hono application with RPC interface\n */\nexport function createApp(storage: Storage, config: Config) {\n const app = new Hono();\n\n // Enable CORS\n app.use('/*', cors({\n origin: (origin) => {\n if (config.corsOrigins.length === 1 && config.corsOrigins[0] === '*') {\n return origin;\n }\n if (config.corsOrigins.includes(origin)) {\n return origin;\n }\n return config.corsOrigins[0];\n },\n allowMethods: ['GET', 'POST', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Origin'],\n exposeHeaders: ['Content-Type'],\n credentials: false,\n maxAge: 86400,\n }));\n\n // Root endpoint - server info\n app.get('/', (c) => {\n return c.json({\n version: config.version,\n name: 'Rondevu',\n description: 'WebRTC signaling with RPC interface and Ed25519 authentication',\n }, 200);\n });\n\n // Health check\n app.get('/health', (c) => {\n return c.json({\n status: 'ok',\n timestamp: Date.now(),\n version: config.version,\n }, 200);\n });\n\n /**\n * POST /rpc\n * RPC endpoint - accepts single or batch method calls\n */\n app.post('/rpc', async (c) => {\n try {\n const body = await c.req.json();\n\n // Support both single request and batch array\n const requests: RpcRequest[] = Array.isArray(body) ? body : [body];\n\n // Validate requests\n if (requests.length === 0) {\n return c.json({ error: 'Empty request array' }, 400);\n }\n\n if (requests.length > MAX_BATCH_SIZE) {\n return c.json({ error: `Too many requests in batch (max ${MAX_BATCH_SIZE})` }, 400);\n }\n\n // Handle RPC\n const responses = await handleRpc(requests, storage, config);\n\n // Return single response or array based on input\n return c.json(Array.isArray(body) ? responses : responses[0], 200);\n } catch (err) {\n console.error('RPC error:', err);\n return c.json({\n success: false,\n error: 'Invalid request format',\n }, 400);\n }\n });\n\n // 404 for all other routes\n app.all('*', (c) => {\n return c.json({\n error: 'Not found. Use POST /rpc for all API calls.',\n }, 404);\n });\n\n return app;\n}\n", "/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */\n/**\n * 5KB JS implementation of ed25519 EdDSA signatures.\n * Compliant with RFC8032, FIPS 186-5 & ZIP215.\n * @module\n * @example\n * ```js\nimport * as ed from '@noble/ed25519';\n(async () => {\n const secretKey = ed.utils.randomSecretKey();\n const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);\n const pubKey = await ed.getPublicKeyAsync(secretKey); // Sync methods are also present\n const signature = await ed.signAsync(message, secretKey);\n const isValid = await ed.verifyAsync(signature, message, pubKey);\n})();\n```\n */\n/**\n * Curve params. ed25519 is twisted edwards curve. Equation is \u2212x\u00B2 + y\u00B2 = -a + dx\u00B2y\u00B2.\n * * P = `2n**255n - 19n` // field over which calculations are done\n * * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points\n * * h = 8 // cofactor\n * * a = `Fp.create(BigInt(-1))` // equation param\n * * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param\n * * Gx, Gy are coordinates of Generator / base point\n */\nconst ed25519_CURVE = {\n p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,\n n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,\n h: 8n,\n a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,\n d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,\n Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,\n Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,\n};\nconst { p: P, n: N, Gx, Gy, a: _a, d: _d, h } = ed25519_CURVE;\nconst L = 32; // field / group byte length\nconst L2 = 64;\n// Helpers and Precomputes sections are reused between libraries\n// ## Helpers\n// ----------\nconst captureTrace = (...args) => {\n if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(...args);\n }\n};\nconst err = (message = '') => {\n const e = new Error(message);\n captureTrace(e, err);\n throw e;\n};\nconst isBig = (n) => typeof n === 'bigint'; // is big integer\nconst isStr = (s) => typeof s === 'string'; // is string\nconst isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n/** Asserts something is Uint8Array. */\nconst abytes = (value, length, title = '') => {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n err(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);\n }\n return value;\n};\n/** create Uint8Array */\nconst u8n = (len) => new Uint8Array(len);\nconst u8fr = (buf) => Uint8Array.from(buf);\nconst padh = (n, pad) => n.toString(16).padStart(pad, '0');\nconst bytesToHex = (b) => Array.from(abytes(b))\n .map((e) => padh(e, 2))\n .join('');\nconst C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters\nconst _ch = (ch) => {\n if (ch >= C._0 && ch <= C._9)\n return ch - C._0; // '2' => 50-48\n if (ch >= C.A && ch <= C.F)\n return ch - (C.A - 10); // 'B' => 66-(65-10)\n if (ch >= C.a && ch <= C.f)\n return ch - (C.a - 10); // 'b' => 98-(97-10)\n return;\n};\nconst hexToBytes = (hex) => {\n const e = 'hex invalid';\n if (!isStr(hex))\n return err(e);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2)\n return err(e);\n const array = u8n(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n // treat each char as ASCII\n const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16\n const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char\n if (n1 === undefined || n2 === undefined)\n return err(e);\n array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9\n }\n return array;\n};\nconst cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments\nconst subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined, consider polyfill');\n// prettier-ignore\nconst concatBytes = (...arrs) => {\n const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); // create u8a of summed length\n let pad = 0; // walk through each array,\n arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type\n return r;\n};\n/** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */\nconst randomBytes = (len = L) => {\n const c = cr();\n return c.getRandomValues(u8n(len));\n};\nconst big = BigInt;\nconst assertRange = (n, min, max, msg = 'bad number: out of range') => (isBig(n) && min <= n && n < max ? n : err(msg));\n/** modular division */\nconst M = (a, b = P) => {\n const r = a % b;\n return r >= 0n ? r : b + r;\n};\nconst modN = (a) => M(a, N);\n/** Modular inversion using euclidean GCD (non-CT). No negative exponent for now. */\n// prettier-ignore\nconst invert = (num, md) => {\n if (num === 0n || md <= 0n)\n err('no inverse n=' + num + ' mod=' + md);\n let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;\n while (a !== 0n) {\n const q = b / a, r = b % a;\n const m = x - u * q, n = y - v * q;\n b = a, a = r, x = u, y = v, u = m, v = n;\n }\n return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point\n};\nconst callHash = (name) => {\n // @ts-ignore\n const fn = hashes[name];\n if (typeof fn !== 'function')\n err('hashes.' + name + ' not set');\n return fn;\n};\nconst hash = (msg) => callHash('sha512')(msg);\nconst apoint = (p) => (p instanceof Point ? p : err('Point expected'));\n// ## End of Helpers\n// -----------------\nconst B256 = 2n ** 256n;\n/** Point in XYZT extended coordinates. */\nclass Point {\n static BASE;\n static ZERO;\n X;\n Y;\n Z;\n T;\n constructor(X, Y, Z, T) {\n const max = B256;\n this.X = assertRange(X, 0n, max);\n this.Y = assertRange(Y, 0n, max);\n this.Z = assertRange(Z, 1n, max);\n this.T = assertRange(T, 0n, max);\n Object.freeze(this);\n }\n static CURVE() {\n return ed25519_CURVE;\n }\n static fromAffine(p) {\n return new Point(p.x, p.y, 1n, M(p.x * p.y));\n }\n /** RFC8032 5.1.3: Uint8Array to Point. */\n static fromBytes(hex, zip215 = false) {\n const d = _d;\n // Copy array to not mess it up.\n const normed = u8fr(abytes(hex, L));\n // adjust first LE byte = last BE byte\n const lastByte = hex[31];\n normed[31] = lastByte & ~0x80;\n const y = bytesToNumLE(normed);\n // zip215=true: 0 <= y < 2^256\n // zip215=false, RFC8032: 0 <= y < 2^255-19\n const max = zip215 ? B256 : P;\n assertRange(y, 0n, max);\n const y2 = M(y * y); // y\u00B2\n const u = M(y2 - 1n); // u=y\u00B2-1\n const v = M(d * y2 + 1n); // v=dy\u00B2+1\n let { isValid, value: x } = uvRatio(u, v); // (uv\u00B3)(uv\u2077)^(p-5)/8; square root\n if (!isValid)\n err('bad point: y not sqrt'); // not square root: bad point\n const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate\n const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit\n if (!zip215 && x === 0n && isLastByteOdd)\n err('bad point: x==0, isLastByteOdd'); // x=0, x_0=1\n if (isLastByteOdd !== isXOdd)\n x = M(-x);\n return new Point(x, y, 1n, M(x * y)); // Z=1, T=xy\n }\n static fromHex(hex, zip215) {\n return Point.fromBytes(hexToBytes(hex), zip215);\n }\n get x() {\n return this.toAffine().x;\n }\n get y() {\n return this.toAffine().y;\n }\n /** Checks if the point is valid and on-curve. */\n assertValidity() {\n const a = _a;\n const d = _d;\n const p = this;\n if (p.is0())\n return err('bad point: ZERO'); // TODO: optimize, with vars below?\n // Equation in affine coordinates: ax\u00B2 + y\u00B2 = 1 + dx\u00B2y\u00B2\n // Equation in projective coordinates (X/Z, Y/Z, Z): (aX\u00B2 + Y\u00B2)Z\u00B2 = Z\u2074 + dX\u00B2Y\u00B2\n const { X, Y, Z, T } = p;\n const X2 = M(X * X); // X\u00B2\n const Y2 = M(Y * Y); // Y\u00B2\n const Z2 = M(Z * Z); // Z\u00B2\n const Z4 = M(Z2 * Z2); // Z\u2074\n const aX2 = M(X2 * a); // aX\u00B2\n const left = M(Z2 * M(aX2 + Y2)); // (aX\u00B2 + Y\u00B2)Z\u00B2\n const right = M(Z4 + M(d * M(X2 * Y2))); // Z\u2074 + dX\u00B2Y\u00B2\n if (left !== right)\n return err('bad point: equation left != right (1)');\n // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T\n const XY = M(X * Y);\n const ZT = M(Z * T);\n if (XY !== ZT)\n return err('bad point: equation left != right (2)');\n return this;\n }\n /** Equality check: compare points P&Q. */\n equals(other) {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality\n const X1Z2 = M(X1 * Z2);\n const X2Z1 = M(X2 * Z1);\n const Y1Z2 = M(Y1 * Z2);\n const Y2Z1 = M(Y2 * Z1);\n return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;\n }\n is0() {\n return this.equals(I);\n }\n /** Flip point over y coordinate. */\n negate() {\n return new Point(M(-this.X), this.Y, this.Z, M(-this.T));\n }\n /** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */\n double() {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const a = _a;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd\n const A = M(X1 * X1);\n const B = M(Y1 * Y1);\n const C = M(2n * M(Z1 * Z1));\n const D = M(a * A);\n const x1y1 = X1 + Y1;\n const E = M(M(x1y1 * x1y1) - A - B);\n const G = D + B;\n const F = G - C;\n const H = D - B;\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n /** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */\n add(other) {\n const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;\n const { X: X2, Y: Y2, Z: Z2, T: T2 } = apoint(other); // doesn't check if other on-curve\n const a = _a;\n const d = _d;\n // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3\n const A = M(X1 * X2);\n const B = M(Y1 * Y2);\n const C = M(T1 * d * T2);\n const D = M(Z1 * Z2);\n const E = M((X1 + Y1) * (X2 + Y2) - A - B);\n const F = M(D - C);\n const G = M(D + C);\n const H = M(B - a * A);\n const X3 = M(E * F);\n const Y3 = M(G * H);\n const T3 = M(E * H);\n const Z3 = M(F * G);\n return new Point(X3, Y3, Z3, T3);\n }\n subtract(other) {\n return this.add(apoint(other).negate());\n }\n /**\n * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.\n * Uses {@link wNAF} for base point.\n * Uses fake point to mitigate side-channel leakage.\n * @param n scalar by which point is multiplied\n * @param safe safe mode guards against timing attacks; unsafe mode is faster\n */\n multiply(n, safe = true) {\n if (!safe && (n === 0n || this.is0()))\n return I;\n assertRange(n, 1n, N);\n if (n === 1n)\n return this;\n if (this.equals(G))\n return wNAF(n).p;\n // init result point & fake point\n let p = I;\n let f = G;\n for (let d = this; n > 0n; d = d.double(), n >>= 1n) {\n // if bit is present, add to point\n // if not present, add to fake, for timing safety\n if (n & 1n)\n p = p.add(d);\n else if (safe)\n f = f.add(d);\n }\n return p;\n }\n multiplyUnsafe(scalar) {\n return this.multiply(scalar, false);\n }\n /** Convert point to 2d xy affine point. (X, Y, Z) \u220B (x=X/Z, y=Y/Z) */\n toAffine() {\n const { X, Y, Z } = this;\n // fast-paths for ZERO point OR Z=1\n if (this.equals(I))\n return { x: 0n, y: 1n };\n const iz = invert(Z, P);\n // (Z * Z^-1) must be 1, otherwise bad math\n if (M(Z * iz) !== 1n)\n err('invalid inverse');\n // x = X*Z^-1; y = Y*Z^-1\n const x = M(X * iz);\n const y = M(Y * iz);\n return { x, y };\n }\n toBytes() {\n const { x, y } = this.assertValidity().toAffine();\n const b = numTo32bLE(y);\n // store sign in first LE byte\n b[31] |= x & 1n ? 0x80 : 0;\n return b;\n }\n toHex() {\n return bytesToHex(this.toBytes());\n }\n clearCofactor() {\n return this.multiply(big(h), false);\n }\n isSmallOrder() {\n return this.clearCofactor().is0();\n }\n isTorsionFree() {\n // Multiply by big number N. We can't `mul(N)` because of checks. Instead, we `mul(N/2)*2+1`\n let p = this.multiply(N / 2n, false).double();\n if (N % 2n)\n p = p.add(this);\n return p.is0();\n }\n}\n/** Generator / base point */\nconst G = new Point(Gx, Gy, 1n, M(Gx * Gy));\n/** Identity / zero point */\nconst I = new Point(0n, 1n, 1n, 0n);\n// Static aliases\nPoint.BASE = G;\nPoint.ZERO = I;\nconst numTo32bLE = (num) => hexToBytes(padh(assertRange(num, 0n, B256), L2)).reverse();\nconst bytesToNumLE = (b) => big('0x' + bytesToHex(u8fr(abytes(b)).reverse()));\nconst pow2 = (x, power) => {\n // pow2(x, 4) == x^(2^4)\n let r = x;\n while (power-- > 0n) {\n r *= r;\n r %= P;\n }\n return r;\n};\n// prettier-ignore\nconst pow_2_252_3 = (x) => {\n const x2 = (x * x) % P; // x^2, bits 1\n const b2 = (x2 * x) % P; // x^3, bits 11\n const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111\n const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111\n const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)\n const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)\n const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)\n const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)\n const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)\n const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)\n const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)\n const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.\n return { pow_p_5_8, b2 };\n};\nconst RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; // \u221A-1\n// for sqrt comp\n// prettier-ignore\nconst uvRatio = (u, v) => {\n const v3 = M(v * v * v); // v\u00B3\n const v7 = M(v3 * v3 * v); // v\u2077\n const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv\u2077)^(p-5)/8\n let x = M(u * v3 * pow); // (uv\u00B3)(uv\u2077)^(p-5)/8\n const vx2 = M(v * x * x); // vx\u00B2\n const root1 = x; // First root candidate\n const root2 = M(x * RM1); // Second root candidate; RM1 is \u221A-1\n const useRoot1 = vx2 === u; // If vx\u00B2 = u (mod p), x is a square root\n const useRoot2 = vx2 === M(-u); // If vx\u00B2 = -u, set x <-- x * 2^((p-1)/4)\n const noRoot = vx2 === M(-u * RM1); // There is no valid root, vx\u00B2 = -u\u221A-1\n if (useRoot1)\n x = root1;\n if (useRoot2 || noRoot)\n x = root2; // We return root2 anyway, for const-time\n if ((M(x) & 1n) === 1n)\n x = M(-x); // edIsNegative\n return { isValid: useRoot1 || useRoot2, value: x };\n};\n// N == L, just weird naming\nconst modL_LE = (hash) => modN(bytesToNumLE(hash)); // modulo L; but little-endian\n/** hashes.sha512 should conform to the interface. */\n// TODO: rename\nconst sha512a = (...m) => hashes.sha512Async(concatBytes(...m)); // Async SHA512\nconst sha512s = (...m) => callHash('sha512')(concatBytes(...m));\n// RFC8032 5.1.5\nconst hash2extK = (hashed) => {\n // slice creates a copy, unlike subarray\n const head = hashed.slice(0, L);\n head[0] &= 248; // Clamp bits: 0b1111_1000\n head[31] &= 127; // 0b0111_1111\n head[31] |= 64; // 0b0100_0000\n const prefix = hashed.slice(L, L2); // secret key \"prefix\"\n const scalar = modL_LE(head); // modular division over curve order\n const point = G.multiply(scalar); // public key point\n const pointBytes = point.toBytes(); // point serialized to Uint8Array\n return { head, prefix, scalar, point, pointBytes };\n};\n// RFC8032 5.1.5; getPublicKey async, sync. Hash priv key and extract point.\nconst getExtendedPublicKeyAsync = (secretKey) => sha512a(abytes(secretKey, L)).then(hash2extK);\nconst getExtendedPublicKey = (secretKey) => hash2extK(sha512s(abytes(secretKey, L)));\n/** Creates 32-byte ed25519 public key from 32-byte secret key. Async. */\nconst getPublicKeyAsync = (secretKey) => getExtendedPublicKeyAsync(secretKey).then((p) => p.pointBytes);\n/** Creates 32-byte ed25519 public key from 32-byte secret key. To use, set `hashes.sha512` first. */\nconst getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;\nconst hashFinishA = (res) => sha512a(res.hashable).then(res.finish);\nconst hashFinishS = (res) => res.finish(sha512s(res.hashable));\n// Code, shared between sync & async sign\nconst _sign = (e, rBytes, msg) => {\n const { pointBytes: P, scalar: s } = e;\n const r = modL_LE(rBytes); // r was created outside, reduce it modulo L\n const R = G.multiply(r).toBytes(); // R = [r]B\n const hashable = concatBytes(R, P, msg); // dom2(F, C) || R || A || PH(M)\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n const S = modN(r + modL_LE(hashed) * s); // S = (r + k * s) mod L; 0 <= s < l\n return abytes(concatBytes(R, numTo32bLE(S)), L2); // 64-byte sig: 32b R.x + 32b LE(S)\n };\n return { hashable, finish };\n};\n/**\n * Signs message using secret key. Async.\n * Follows RFC8032 5.1.6.\n */\nconst signAsync = async (message, secretKey) => {\n const m = abytes(message);\n const e = await getExtendedPublicKeyAsync(secretKey);\n const rBytes = await sha512a(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishA(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\n/**\n * Signs message using secret key. To use, set `hashes.sha512` first.\n * Follows RFC8032 5.1.6.\n */\nconst sign = (message, secretKey) => {\n const m = abytes(message);\n const e = getExtendedPublicKey(secretKey);\n const rBytes = sha512s(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))\n return hashFinishS(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature\n};\nconst defaultVerifyOpts = { zip215: true };\nconst _verify = (sig, msg, pub, opts = defaultVerifyOpts) => {\n sig = abytes(sig, L2); // Signature hex str/Bytes, must be 64 bytes\n msg = abytes(msg); // Message hex str/Bytes\n pub = abytes(pub, L);\n const { zip215 } = opts; // switch between zip215 and rfc8032 verif\n let A;\n let R;\n let s;\n let SB;\n let hashable = Uint8Array.of();\n try {\n A = Point.fromBytes(pub, zip215); // public key A decoded\n R = Point.fromBytes(sig.slice(0, L), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P\n s = bytesToNumLE(sig.slice(L, L2)); // Decode second half as an integer S\n SB = G.multiply(s, false); // in the range 0 <= s < L\n hashable = concatBytes(R.toBytes(), A.toBytes(), msg); // dom2(F, C) || R || A || PH(M)\n }\n catch (error) { }\n const finish = (hashed) => {\n // k = SHA512(dom2(F, C) || R || A || PH(M))\n if (SB == null)\n return false; // false if try-catch catched an error\n if (!zip215 && A.isSmallOrder())\n return false; // false for SBS: Strongly Binding Signature\n const k = modL_LE(hashed); // decode in little-endian, modulo L\n const RkA = R.add(A.multiply(k, false)); // [8]R + [8][k]A'\n return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A'\n };\n return { hashable, finish };\n};\n/** Verifies signature on message and public key. Async. Follows RFC8032 5.1.7. */\nconst verifyAsync = async (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishA(_verify(signature, message, publicKey, opts));\n/** Verifies signature on message and public key. To use, set `hashes.sha512` first. Follows RFC8032 5.1.7. */\nconst verify = (signature, message, publicKey, opts = defaultVerifyOpts) => hashFinishS(_verify(signature, message, publicKey, opts));\n/** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */\nconst etc = {\n bytesToHex: bytesToHex,\n hexToBytes: hexToBytes,\n concatBytes: concatBytes,\n mod: M,\n invert: invert,\n randomBytes: randomBytes,\n};\nconst hashes = {\n sha512Async: async (message) => {\n const s = subtle();\n const m = concatBytes(message);\n return u8n(await s.digest('SHA-512', m.buffer));\n },\n sha512: undefined,\n};\n// FIPS 186 B.4.1 compliant key generation produces private keys\n// with modulo bias being neglible. takes >N+16 bytes, returns (hash mod n-1)+1\nconst randomSecretKey = (seed = randomBytes(L)) => seed;\nconst keygen = (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = getPublicKey(secretKey);\n return { secretKey, publicKey };\n};\nconst keygenAsync = async (seed) => {\n const secretKey = randomSecretKey(seed);\n const publicKey = await getPublicKeyAsync(secretKey);\n return { secretKey, publicKey };\n};\n/** ed25519-specific key utilities. */\nconst utils = {\n getExtendedPublicKeyAsync: getExtendedPublicKeyAsync,\n getExtendedPublicKey: getExtendedPublicKey,\n randomSecretKey: randomSecretKey,\n};\n// ## Precomputes\n// --------------\nconst W = 8; // W is window size\nconst scalarBits = 256;\nconst pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8, NOT 32 - see wNAF loop\nconst pwindowSize = 2 ** (W - 1); // 128 for W=8\nconst precompute = () => {\n const points = [];\n let p = G;\n let b = p;\n for (let w = 0; w < pwindows; w++) {\n b = p;\n points.push(b);\n for (let i = 1; i < pwindowSize; i++) {\n b = b.add(p);\n points.push(b);\n } // i=1, bc we skip 0\n p = b.double();\n }\n return points;\n};\nlet Gpows = undefined; // precomputes for base point G\n// const-time negate\nconst ctneg = (cnd, p) => {\n const n = p.negate();\n return cnd ? n : p;\n};\n/**\n * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by\n * caching multiples of G (base point). Cache is stored in 32MB of RAM.\n * Any time `G.multiply` is done, precomputes are used.\n * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.\n *\n * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,\n * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.\n *\n * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().\n */\nconst wNAF = (n) => {\n const comp = Gpows || (Gpows = precompute());\n let p = I;\n let f = G; // f must be G, or could become I in the end\n const pow_2_w = 2 ** W; // 256 for W=8\n const maxNum = pow_2_w; // 256 for W=8\n const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111\n const shiftBy = big(W); // 8 for W=8\n for (let w = 0; w < pwindows; w++) {\n let wbits = Number(n & mask); // extract W bits.\n n >>= shiftBy; // shift number by W bits.\n // We use negative indexes to reduce size of precomputed table by 2x.\n // Instead of needing precomputes 0..256, we only calculate them for 0..128.\n // If an index > 128 is found, we do (256-index) - where 256 is next window.\n // Naive: index +127 => 127, +224 => 224\n // Optimized: index +127 => 127, +224 => 256-32\n if (wbits > pwindowSize) {\n wbits -= maxNum;\n n += 1n;\n }\n const off = w * pwindowSize;\n const offF = off; // offsets, evaluate both\n const offP = off + Math.abs(wbits) - 1;\n const isEven = w % 2 !== 0; // conditions, evaluate both\n const isNeg = wbits < 0;\n if (wbits === 0) {\n // off == I: can't add it. Adding random offF instead.\n f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point\n }\n else {\n p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point\n }\n }\n if (n !== 0n)\n err('invalid wnaf');\n return { p, f }; // return both real and fake points for JIT\n};\n// !! Remove the export to easily use in REPL / browser console\nexport { etc, getPublicKey, getPublicKeyAsync, hash, hashes, keygen, keygenAsync, Point, sign, signAsync, utils, verify, verifyAsync, };\n", "/**\n * Crypto utilities for Ed25519-based authentication\n * Uses @noble/ed25519 for Ed25519 signature verification\n * Uses Web Crypto API for compatibility with both Node.js and Cloudflare Workers\n */\n\nimport * as ed25519 from '@noble/ed25519';\nimport { Buffer } from 'node:buffer';\n\n// Set SHA-512 hash function for ed25519 (required in @noble/ed25519 v3+)\n// Uses Web Crypto API (compatible with both Node.js and Cloudflare Workers)\ned25519.hashes.sha512Async = async (message: Uint8Array) => {\n return new Uint8Array(await crypto.subtle.digest('SHA-512', message as BufferSource));\n};\n\n// Username validation\nconst USERNAME_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;\nconst USERNAME_MIN_LENGTH = 3;\nconst USERNAME_MAX_LENGTH = 32;\n\n// Timestamp validation (5 minutes tolerance)\nconst TIMESTAMP_TOLERANCE_MS = 5 * 60 * 1000;\n\n/**\n * Generates an anonymous username for users who don't want to claim one\n * Format: anon-{timestamp}-{random}\n * This reduces collision probability to near-zero\n */\nexport function generateAnonymousUsername(): string {\n const timestamp = Date.now().toString(36);\n const random = crypto.getRandomValues(new Uint8Array(3));\n const hex = Array.from(random).map(b => b.toString(16).padStart(2, '0')).join('');\n return `anon-${timestamp}-${hex}`;\n}\n\n/**\n * Convert Uint8Array to base64 string\n * Uses Buffer for compatibility with Node.js-based clients\n */\nfunction bytesToBase64(bytes: Uint8Array): string {\n return Buffer.from(bytes).toString('base64');\n}\n\n/**\n * Convert base64 string to Uint8Array\n * Uses Buffer for compatibility with Node.js-based clients\n */\nfunction base64ToBytes(base64: string): Uint8Array {\n return new Uint8Array(Buffer.from(base64, 'base64'));\n}\n\n/**\n * Validates a generic auth message format\n * Expected format: action:username:params:timestamp\n * Validates that the message contains the expected username and has a valid timestamp\n */\nexport function validateAuthMessage(\n expectedUsername: string,\n message: string\n): { valid: boolean; error?: string } {\n const parts = message.split(':');\n\n if (parts.length < 3) {\n return { valid: false, error: 'Invalid message format: must have at least action:username:timestamp' };\n }\n\n // Extract username (second part) and timestamp (last part)\n const messageUsername = parts[1];\n const timestamp = parseInt(parts[parts.length - 1], 10);\n\n // Validate username matches\n if (messageUsername !== expectedUsername) {\n return { valid: false, error: 'Username in message does not match authenticated username' };\n }\n\n // Validate timestamp\n if (isNaN(timestamp)) {\n return { valid: false, error: 'Invalid timestamp in message' };\n }\n\n const timestampCheck = validateTimestamp(timestamp);\n if (!timestampCheck.valid) {\n return timestampCheck;\n }\n\n return { valid: true };\n}\n\n// ===== Username and Ed25519 Signature Utilities =====\n\n/**\n * Validates username format\n * Rules: 3-32 chars, lowercase alphanumeric + dash, must start/end with alphanumeric\n */\nexport function validateUsername(username: string): { valid: boolean; error?: string } {\n if (typeof username !== 'string') {\n return { valid: false, error: 'Username must be a string' };\n }\n\n if (username.length < USERNAME_MIN_LENGTH) {\n return { valid: false, error: `Username must be at least ${USERNAME_MIN_LENGTH} characters` };\n }\n\n if (username.length > USERNAME_MAX_LENGTH) {\n return { valid: false, error: `Username must be at most ${USERNAME_MAX_LENGTH} characters` };\n }\n\n if (!USERNAME_REGEX.test(username)) {\n return { valid: false, error: 'Username must be lowercase alphanumeric with optional dashes, and start/end with alphanumeric' };\n }\n\n return { valid: true };\n}\n\n/**\n * Validates service FQN format (service:version@username or service:version)\n * Service name: lowercase alphanumeric with dots/dashes (e.g., chat, file-share, com.example.chat)\n * Version: semantic versioning (1.0.0, 2.1.3-beta, etc.)\n * Username: optional, lowercase alphanumeric with dashes\n */\nexport function validateServiceFqn(fqn: string): { valid: boolean; error?: string } {\n if (typeof fqn !== 'string') {\n return { valid: false, error: 'Service FQN must be a string' };\n }\n\n // Parse the FQN\n const parsed = parseServiceFqn(fqn);\n if (!parsed) {\n return { valid: false, error: 'Service FQN must be in format: service:version[@username]' };\n }\n\n const { serviceName, version, username } = parsed;\n\n // Validate service name (alphanumeric with dots/dashes)\n const serviceNameRegex = /^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/;\n if (!serviceNameRegex.test(serviceName)) {\n return { valid: false, error: 'Service name must be lowercase alphanumeric with optional dots/dashes' };\n }\n\n if (serviceName.length < 1 || serviceName.length > 128) {\n return { valid: false, error: 'Service name must be 1-128 characters' };\n }\n\n // Validate version (semantic versioning)\n const versionRegex = /^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-z0-9.-]+)?$/;\n if (!versionRegex.test(version)) {\n return { valid: false, error: 'Version must be semantic versioning (e.g., 1.0.0, 2.1.3-beta)' };\n }\n\n // Validate username if present\n if (username) {\n const usernameCheck = validateUsername(username);\n if (!usernameCheck.valid) {\n return usernameCheck;\n }\n }\n\n return { valid: true };\n}\n\n/**\n * Parse semantic version string into components\n */\nexport function parseVersion(version: string): { major: number; minor: number; patch: number; prerelease?: string } | null {\n const match = version.match(/^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-[a-z0-9.-]+)?$/);\n if (!match) return null;\n\n return {\n major: parseInt(match[1], 10),\n minor: parseInt(match[2], 10),\n patch: parseInt(match[3], 10),\n prerelease: match[4]?.substring(1), // Remove leading dash\n };\n}\n\n/**\n * Check if two versions are compatible (same major version)\n * Following semver rules: ^1.0.0 matches 1.x.x but not 2.x.x\n */\nexport function isVersionCompatible(requested: string, available: string): boolean {\n const req = parseVersion(requested);\n const avail = parseVersion(available);\n\n if (!req || !avail) return false;\n\n // Major version must match\n if (req.major !== avail.major) return false;\n\n // If major is 0, minor must also match (0.x.y is unstable)\n if (req.major === 0 && req.minor !== avail.minor) return false;\n\n // Available version must be >= requested version\n if (avail.minor < req.minor) return false;\n if (avail.minor === req.minor && avail.patch < req.patch) return false;\n\n // Prerelease versions are only compatible with exact matches\n if (req.prerelease && req.prerelease !== avail.prerelease) return false;\n\n return true;\n}\n\n/**\n * Parse service FQN into components\n * Formats supported:\n * - service:version@username (e.g., \"chat:1.0.0@alice\")\n * - service:version (e.g., \"chat:1.0.0\") for discovery\n */\nexport function parseServiceFqn(fqn: string): { serviceName: string; version: string; username: string | null } | null {\n if (!fqn || typeof fqn !== 'string') return null;\n\n // Check if username is present\n const atIndex = fqn.lastIndexOf('@');\n let serviceVersion: string;\n let username: string | null = null;\n\n if (atIndex > 0) {\n // Format: service:version@username\n serviceVersion = fqn.substring(0, atIndex);\n username = fqn.substring(atIndex + 1);\n } else {\n // Format: service:version (no username)\n serviceVersion = fqn;\n }\n\n // Split service:version\n const colonIndex = serviceVersion.indexOf(':');\n if (colonIndex <= 0) return null; // No colon or colon at start\n\n const serviceName = serviceVersion.substring(0, colonIndex);\n const version = serviceVersion.substring(colonIndex + 1);\n\n if (!serviceName || !version) return null;\n\n return {\n serviceName,\n version,\n username,\n };\n}\n\n/**\n * Validates timestamp is within acceptable range (prevents replay attacks)\n */\nexport function validateTimestamp(timestamp: number): { valid: boolean; error?: string } {\n if (typeof timestamp !== 'number' || !Number.isFinite(timestamp)) {\n return { valid: false, error: 'Timestamp must be a finite number' };\n }\n\n const now = Date.now();\n const diff = Math.abs(now - timestamp);\n\n if (diff > TIMESTAMP_TOLERANCE_MS) {\n return { valid: false, error: `Timestamp too old or too far in future (tolerance: ${TIMESTAMP_TOLERANCE_MS / 1000}s)` };\n }\n\n return { valid: true };\n}\n\n/**\n * Verifies Ed25519 signature\n * @param publicKey Base64-encoded Ed25519 public key (32 bytes)\n * @param signature Base64-encoded Ed25519 signature (64 bytes)\n * @param message Message that was signed (UTF-8 string)\n * @returns true if signature is valid, false otherwise\n */\nexport async function verifyEd25519Signature(\n publicKey: string,\n signature: string,\n message: string\n): Promise<boolean> {\n try {\n // Decode base64 to bytes\n const publicKeyBytes = base64ToBytes(publicKey);\n const signatureBytes = base64ToBytes(signature);\n\n // Encode message as UTF-8\n const encoder = new TextEncoder();\n const messageBytes = encoder.encode(message);\n\n // Verify signature using @noble/ed25519 (async version)\n const isValid = await ed25519.verifyAsync(signatureBytes, messageBytes, publicKeyBytes);\n return isValid;\n } catch (err) {\n console.error('Ed25519 signature verification failed:', err);\n return false;\n }\n}\n\n/**\n * Validates a username claim request\n * Verifies format, timestamp, and signature\n */\nexport async function validateUsernameClaim(\n username: string,\n publicKey: string,\n signature: string,\n message: string\n): Promise<{ valid: boolean; error?: string }> {\n // Validate username format\n const usernameCheck = validateUsername(username);\n if (!usernameCheck.valid) {\n return usernameCheck;\n }\n\n // Parse message format: \"claim:{username}:{timestamp}\"\n const parts = message.split(':');\n if (parts.length !== 3 || parts[0] !== 'claim' || parts[1] !== username) {\n return { valid: false, error: 'Invalid message format (expected: claim:{username}:{timestamp})' };\n }\n\n const timestamp = parseInt(parts[2], 10);\n if (isNaN(timestamp)) {\n return { valid: false, error: 'Invalid timestamp in message' };\n }\n\n // Validate timestamp\n const timestampCheck = validateTimestamp(timestamp);\n if (!timestampCheck.valid) {\n return timestampCheck;\n }\n\n // Verify signature\n const signatureValid = await verifyEd25519Signature(publicKey, signature, message);\n if (!signatureValid) {\n return { valid: false, error: 'Invalid signature' };\n }\n\n return { valid: true };\n}\n\n/**\n * Validates a service publish signature\n * Message format: publish:{username}:{serviceFqn}:{timestamp}\n */\nexport async function validateServicePublish(\n username: string,\n serviceFqn: string,\n publicKey: string,\n signature: string,\n message: string\n): Promise<{ valid: boolean; error?: string }> {\n // Validate username format\n const usernameCheck = validateUsername(username);\n if (!usernameCheck.valid) {\n return usernameCheck;\n }\n\n // Parse message format: \"publish:{username}:{serviceFqn}:{timestamp}\"\n // Note: serviceFqn can contain colons (e.g., \"chat:2.0.0@user\"), so we need careful parsing\n const parts = message.split(':');\n if (parts.length < 4 || parts[0] !== 'publish' || parts[1] !== username) {\n return { valid: false, error: 'Invalid message format (expected: publish:{username}:{serviceFqn}:{timestamp})' };\n }\n\n // The timestamp is the last part\n const timestamp = parseInt(parts[parts.length - 1], 10);\n if (isNaN(timestamp)) {\n return { valid: false, error: 'Invalid timestamp in message' };\n }\n\n // The serviceFqn is everything between username and timestamp\n const extractedServiceFqn = parts.slice(2, parts.length - 1).join(':');\n if (extractedServiceFqn !== serviceFqn) {\n return { valid: false, error: `Service FQN mismatch (expected: ${serviceFqn}, got: ${extractedServiceFqn})` };\n }\n\n // Validate timestamp\n const timestampCheck = validateTimestamp(timestamp);\n if (!timestampCheck.valid) {\n return timestampCheck;\n }\n\n // Verify signature\n const signatureValid = await verifyEd25519Signature(publicKey, signature, message);\n if (!signatureValid) {\n return { valid: false, error: 'Invalid signature' };\n }\n\n return { valid: true };\n}\n", "import { Context } from 'hono';\nimport { Storage } from './storage/types.ts';\nimport { Config } from './config.ts';\nimport {\n validateUsernameClaim,\n validateServicePublish,\n validateServiceFqn,\n parseServiceFqn,\n isVersionCompatible,\n verifyEd25519Signature,\n validateAuthMessage,\n validateUsername,\n} from './crypto.ts';\n\n// Constants\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * RPC request format\n */\nexport interface RpcRequest {\n method: string;\n message: string;\n signature: string;\n publicKey?: string; // Optional: for auto-claiming usernames\n params?: any;\n}\n\n/**\n * RPC response format\n */\nexport interface RpcResponse {\n success: boolean;\n result?: any;\n error?: string;\n}\n\n/**\n * RPC method handler\n */\ntype RpcHandler = (\n params: any,\n message: string,\n signature: string,\n publicKey: string | undefined,\n storage: Storage,\n config: Config\n) => Promise<any>;\n\n/**\n * Verify authentication for a method call\n * Automatically claims username if it doesn't exist\n */\nasync function verifyAuth(\n username: string,\n message: string,\n signature: string,\n publicKey: string | undefined,\n storage: Storage\n): Promise<{ valid: boolean; error?: string }> {\n // Get username record to fetch public key\n let usernameRecord = await storage.getUsername(username);\n\n // Auto-claim username if it doesn't exist\n if (!usernameRecord) {\n if (!publicKey) {\n return {\n valid: false,\n error: `Username \"${username}\" is not claimed and no public key provided for auto-claim.`,\n };\n }\n\n // Validate username format before claiming\n const usernameValidation = validateUsername(username);\n if (!usernameValidation.valid) {\n return usernameValidation;\n }\n\n // Verify signature against the current message (not a claim message)\n const signatureValid = await verifyEd25519Signature(publicKey, signature, message);\n if (!signatureValid) {\n return { valid: false, error: 'Invalid signature for auto-claim' };\n }\n\n // Auto-claim the username\n const expiresAt = Date.now() + 365 * 24 * 60 * 60 * 1000; // 365 days\n await storage.claimUsername({\n username,\n publicKey,\n expiresAt,\n });\n\n usernameRecord = await storage.getUsername(username);\n if (!usernameRecord) {\n return { valid: false, error: 'Failed to claim username' };\n }\n }\n\n // Verify Ed25519 signature\n const isValid = await verifyEd25519Signature(\n usernameRecord.publicKey,\n signature,\n message\n );\n if (!isValid) {\n return { valid: false, error: 'Invalid signature' };\n }\n\n // Validate message format and timestamp\n const validation = validateAuthMessage(username, message);\n if (!validation.valid) {\n return { valid: false, error: validation.error };\n }\n\n return { valid: true };\n}\n\n/**\n * Extract username from message\n */\nfunction extractUsername(message: string): string | null {\n // Message format: method:username:...\n const parts = message.split(':');\n if (parts.length < 2) return null;\n return parts[1];\n}\n\n/**\n * RPC Method Handlers\n */\n\nconst handlers: Record<string, RpcHandler> = {\n /**\n * Check if username is available\n */\n async getUser(params, message, signature, publicKey, storage, config) {\n const { username } = params;\n const claimed = await storage.getUsername(username);\n\n if (!claimed) {\n return {\n username,\n available: true,\n };\n }\n\n return {\n username: claimed.username,\n available: false,\n claimedAt: claimed.claimedAt,\n expiresAt: claimed.expiresAt,\n publicKey: claimed.publicKey,\n };\n },\n\n /**\n * Get service by FQN - Supports 3 modes:\n * 1. Direct lookup: FQN includes @username\n * 2. Paginated discovery: FQN without @username, with limit/offset\n * 3. Random discovery: FQN without @username, no limit\n */\n async getService(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, limit, offset } = params;\n const username = extractUsername(message);\n\n // Verify authentication\n if (username) {\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n }\n\n // Parse and validate FQN\n const fqnValidation = validateServiceFqn(serviceFqn);\n if (!fqnValidation.valid) {\n throw new Error(fqnValidation.error || 'Invalid service FQN');\n }\n\n const parsed = parseServiceFqn(serviceFqn);\n if (!parsed) {\n throw new Error('Failed to parse service FQN');\n }\n\n // Helper: Filter services by version compatibility\n const filterCompatibleServices = (services) => {\n return services.filter((s) => {\n const serviceVersion = parseServiceFqn(s.serviceFqn);\n return (\n serviceVersion &&\n isVersionCompatible(parsed.version, serviceVersion.version)\n );\n });\n };\n\n // Helper: Find available offer for service\n const findAvailableOffer = async (service) => {\n const offers = await storage.getOffersForService(service.id);\n return offers.find((o) => !o.answererUsername);\n };\n\n // Helper: Build service response object\n const buildServiceResponse = (service, offer) => ({\n serviceId: service.id,\n username: service.username,\n serviceFqn: service.serviceFqn,\n offerId: offer.id,\n sdp: offer.sdp,\n createdAt: service.createdAt,\n expiresAt: service.expiresAt,\n });\n\n // Mode 1: Paginated discovery\n if (limit !== undefined) {\n const pageLimit = Math.min(Math.max(1, limit), MAX_PAGE_SIZE);\n const pageOffset = Math.max(0, offset || 0);\n\n const allServices = await storage.getServicesByName(parsed.service, parsed.version);\n const compatibleServices = filterCompatibleServices(allServices);\n\n // Get unique services per username with available offers\n const usernameSet = new Set<string>();\n const uniqueServices: any[] = [];\n\n for (const service of compatibleServices) {\n if (!usernameSet.has(service.username)) {\n usernameSet.add(service.username);\n const availableOffer = await findAvailableOffer(service);\n\n if (availableOffer) {\n uniqueServices.push(buildServiceResponse(service, availableOffer));\n }\n }\n }\n\n // Paginate results\n const paginatedServices = uniqueServices.slice(pageOffset, pageOffset + pageLimit);\n\n return {\n services: paginatedServices,\n count: paginatedServices.length,\n limit: pageLimit,\n offset: pageOffset,\n };\n }\n\n // Mode 2: Direct lookup with username\n if (parsed.username) {\n const service = await storage.getServiceByFqn(serviceFqn);\n if (!service) {\n throw new Error('Service not found');\n }\n\n const availableOffer = await findAvailableOffer(service);\n if (!availableOffer) {\n throw new Error('Service has no available offers');\n }\n\n return buildServiceResponse(service, availableOffer);\n }\n\n // Mode 3: Random discovery without username\n const allServices = await storage.getServicesByName(parsed.service, parsed.version);\n const compatibleServices = filterCompatibleServices(allServices);\n\n if (compatibleServices.length === 0) {\n throw new Error('No services found');\n }\n\n const randomService = compatibleServices[Math.floor(Math.random() * compatibleServices.length)];\n const availableOffer = await findAvailableOffer(randomService);\n\n if (!availableOffer) {\n throw new Error('Service has no available offers');\n }\n\n return buildServiceResponse(randomService, availableOffer);\n },\n\n /**\n * Publish a service\n */\n async publishService(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, offers, ttl } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required for service publishing');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n // Validate service FQN\n const fqnValidation = validateServiceFqn(serviceFqn);\n if (!fqnValidation.valid) {\n throw new Error(fqnValidation.error || 'Invalid service FQN');\n }\n\n const parsed = parseServiceFqn(serviceFqn);\n if (!parsed || !parsed.username) {\n throw new Error('Service FQN must include username');\n }\n\n if (parsed.username !== username) {\n throw new Error('Service FQN username must match authenticated username');\n }\n\n // Validate offers\n if (!offers || !Array.isArray(offers) || offers.length === 0) {\n throw new Error('Must provide at least one offer');\n }\n\n if (offers.length > config.maxOffersPerRequest) {\n throw new Error(\n `Too many offers (max ${config.maxOffersPerRequest})`\n );\n }\n\n // Validate each offer has valid SDP\n offers.forEach((offer, index) => {\n if (!offer || typeof offer !== 'object') {\n throw new Error(`Invalid offer at index ${index}: must be an object`);\n }\n if (!offer.sdp || typeof offer.sdp !== 'string') {\n throw new Error(`Invalid offer at index ${index}: missing or invalid SDP`);\n }\n if (!offer.sdp.trim()) {\n throw new Error(`Invalid offer at index ${index}: SDP cannot be empty`);\n }\n });\n\n // Create service with offers\n const now = Date.now();\n const offerTtl =\n ttl !== undefined\n ? Math.min(\n Math.max(ttl, config.offerMinTtl),\n config.offerMaxTtl\n )\n : config.offerDefaultTtl;\n const expiresAt = now + offerTtl;\n\n // Prepare offer requests with TTL\n const offerRequests = offers.map(offer => ({\n username,\n serviceFqn,\n sdp: offer.sdp,\n expiresAt,\n }));\n\n const result = await storage.createService({\n serviceFqn,\n expiresAt,\n offers: offerRequests,\n });\n\n return {\n serviceId: result.service.id,\n username: result.service.username,\n serviceFqn: result.service.serviceFqn,\n offers: result.offers.map(offer => ({\n offerId: offer.id,\n sdp: offer.sdp,\n createdAt: offer.createdAt,\n expiresAt: offer.expiresAt,\n })),\n createdAt: result.service.createdAt,\n expiresAt: result.service.expiresAt,\n };\n },\n\n /**\n * Delete a service\n */\n async deleteService(params, message, signature, publicKey, storage, config) {\n const { serviceFqn } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n const parsed = parseServiceFqn(serviceFqn);\n if (!parsed || !parsed.username) {\n throw new Error('Service FQN must include username');\n }\n\n const service = await storage.getServiceByFqn(serviceFqn);\n if (!service) {\n throw new Error('Service not found');\n }\n\n const deleted = await storage.deleteService(service.id, username);\n if (!deleted) {\n throw new Error('Service not found or not owned by this username');\n }\n\n return { success: true };\n },\n\n /**\n * Answer an offer\n */\n async answerOffer(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, offerId, sdp } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n if (!sdp || typeof sdp !== 'string' || sdp.length === 0) {\n throw new Error('Invalid SDP');\n }\n\n if (sdp.length > 64 * 1024) {\n throw new Error('SDP too large (max 64KB)');\n }\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new Error('Offer not found');\n }\n\n if (offer.answererUsername) {\n throw new Error('Offer already answered');\n }\n\n await storage.answerOffer(offerId, username, sdp);\n\n return { success: true, offerId };\n },\n\n /**\n * Get answer for an offer\n */\n async getOfferAnswer(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, offerId } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new Error('Offer not found');\n }\n\n if (offer.username !== username) {\n throw new Error('Not authorized to access this offer');\n }\n\n if (!offer.answererUsername || !offer.answerSdp) {\n throw new Error('Offer not yet answered');\n }\n\n return {\n sdp: offer.answerSdp,\n offerId: offer.id,\n answererId: offer.answererUsername,\n answeredAt: offer.answeredAt,\n };\n },\n\n /**\n * Combined polling for answers and ICE candidates\n */\n async poll(params, message, signature, publicKey, storage, config) {\n const { since } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n const sinceTimestamp = since || 0;\n\n // Get all answered offers\n const answeredOffers = await storage.getAnsweredOffers(username);\n const filteredAnswers = answeredOffers.filter(\n (offer) => offer.answeredAt && offer.answeredAt > sinceTimestamp\n );\n\n // Get all user's offers\n const allOffers = await storage.getOffersByUsername(username);\n\n // For each offer, get ICE candidates from both sides\n const iceCandidatesByOffer: Record<string, any[]> = {};\n\n for (const offer of allOffers) {\n const offererCandidates = await storage.getIceCandidates(\n offer.id,\n 'offerer',\n sinceTimestamp\n );\n const answererCandidates = await storage.getIceCandidates(\n offer.id,\n 'answerer',\n sinceTimestamp\n );\n\n const allCandidates = [\n ...offererCandidates.map((c: any) => ({\n ...c,\n role: 'offerer' as const,\n })),\n ...answererCandidates.map((c: any) => ({\n ...c,\n role: 'answerer' as const,\n })),\n ];\n\n if (allCandidates.length > 0) {\n const isOfferer = offer.username === username;\n const filtered = allCandidates.filter((c) =>\n isOfferer ? c.role === 'answerer' : c.role === 'offerer'\n );\n\n if (filtered.length > 0) {\n iceCandidatesByOffer[offer.id] = filtered;\n }\n }\n }\n\n return {\n answers: filteredAnswers.map((offer) => ({\n offerId: offer.id,\n serviceId: offer.serviceId,\n answererId: offer.answererUsername,\n sdp: offer.answerSdp,\n answeredAt: offer.answeredAt,\n })),\n iceCandidates: iceCandidatesByOffer,\n };\n },\n\n /**\n * Add ICE candidates\n */\n async addIceCandidates(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, offerId, candidates } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n if (!Array.isArray(candidates) || candidates.length === 0) {\n throw new Error('Missing or invalid required parameter: candidates');\n }\n\n // Validate each candidate is an object (don't enforce structure per CLAUDE.md)\n candidates.forEach((candidate, index) => {\n if (!candidate || typeof candidate !== 'object') {\n throw new Error(`Invalid candidate at index ${index}: must be an object`);\n }\n });\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new Error('Offer not found');\n }\n\n const role = offer.username === username ? 'offerer' : 'answerer';\n const count = await storage.addIceCandidates(\n offerId,\n username,\n role,\n candidates\n );\n\n return { count, offerId };\n },\n\n /**\n * Get ICE candidates\n */\n async getIceCandidates(params, message, signature, publicKey, storage, config) {\n const { serviceFqn, offerId, since } = params;\n const username = extractUsername(message);\n\n if (!username) {\n throw new Error('Username required');\n }\n\n // Verify authentication\n const auth = await verifyAuth(username, message, signature, publicKey, storage);\n if (!auth.valid) {\n throw new Error(auth.error);\n }\n\n const sinceTimestamp = since || 0;\n\n const offer = await storage.getOfferById(offerId);\n if (!offer) {\n throw new Error('Offer not found');\n }\n\n const isOfferer = offer.username === username;\n const role = isOfferer ? 'answerer' : 'offerer';\n\n const candidates = await storage.getIceCandidates(\n offerId,\n role,\n sinceTimestamp\n );\n\n return {\n candidates: candidates.map((c: any) => ({\n candidate: c.candidate,\n createdAt: c.createdAt,\n })),\n offerId,\n };\n },\n};\n\n/**\n * Handle RPC batch request\n */\nexport async function handleRpc(\n requests: RpcRequest[],\n storage: Storage,\n config: Config\n): Promise<RpcResponse[]> {\n const responses: RpcResponse[] = [];\n\n for (const request of requests) {\n try {\n const { method, message, signature, publicKey, params } = request;\n\n // Validate request\n if (!method || typeof method !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid method',\n });\n continue;\n }\n\n if (!message || typeof message !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid message',\n });\n continue;\n }\n\n if (!signature || typeof signature !== 'string') {\n responses.push({\n success: false,\n error: 'Missing or invalid signature',\n });\n continue;\n }\n\n // Get handler\n const handler = handlers[method];\n if (!handler) {\n responses.push({\n success: false,\n error: `Unknown method: ${method}`,\n });\n continue;\n }\n\n // Execute handler\n const result = await handler(\n params || {},\n message,\n signature,\n publicKey,\n storage,\n config\n );\n\n responses.push({\n success: true,\n result,\n });\n } catch (err) {\n responses.push({\n success: false,\n error: (err as Error).message || 'Internal server error',\n });\n }\n }\n\n return responses;\n}\n", "/**\n * Application configuration\n * Reads from environment variables with sensible defaults\n */\nexport interface Config {\n port: number;\n storageType: 'sqlite' | 'memory';\n storagePath: string;\n corsOrigins: string[];\n version: string;\n offerDefaultTtl: number;\n offerMaxTtl: number;\n offerMinTtl: number;\n cleanupInterval: number;\n maxOffersPerRequest: number;\n}\n\n/**\n * Loads configuration from environment variables\n */\nexport function loadConfig(): Config {\n return {\n port: parseInt(process.env.PORT || '3000', 10),\n storageType: (process.env.STORAGE_TYPE || 'sqlite') as 'sqlite' | 'memory',\n storagePath: process.env.STORAGE_PATH || ':memory:',\n corsOrigins: process.env.CORS_ORIGINS\n ? process.env.CORS_ORIGINS.split(',').map(o => o.trim())\n : ['*'],\n version: process.env.VERSION || 'unknown',\n offerDefaultTtl: parseInt(process.env.OFFER_DEFAULT_TTL || '60000', 10),\n offerMaxTtl: parseInt(process.env.OFFER_MAX_TTL || '86400000', 10),\n offerMinTtl: parseInt(process.env.OFFER_MIN_TTL || '60000', 10),\n cleanupInterval: parseInt(process.env.CLEANUP_INTERVAL || '60000', 10),\n maxOffersPerRequest: parseInt(process.env.MAX_OFFERS_PER_REQUEST || '100', 10)\n };\n}\n", "import Database from 'better-sqlite3';\nimport { randomUUID } from 'node:crypto';\nimport {\n Storage,\n Offer,\n IceCandidate,\n CreateOfferRequest,\n Username,\n ClaimUsernameRequest,\n Service,\n CreateServiceRequest,\n} from './types.ts';\nimport { generateOfferHash } from './hash-id.ts';\nimport { parseServiceFqn } from '../crypto.ts';\n\nconst YEAR_IN_MS = 365 * 24 * 60 * 60 * 1000; // 365 days\n\n/**\n * SQLite storage adapter for rondevu DNS-like system\n * Supports both file-based and in-memory databases\n */\nexport class SQLiteStorage implements Storage {\n private db: Database.Database;\n\n /**\n * Creates a new SQLite storage instance\n * @param path Path to SQLite database file, or ':memory:' for in-memory database\n */\n constructor(path: string = ':memory:') {\n this.db = new Database(path);\n this.initializeDatabase();\n }\n\n /**\n * Initializes database schema with username and service-based structure\n */\n private initializeDatabase(): void {\n this.db.exec(`\n -- WebRTC signaling offers\n CREATE TABLE IF NOT EXISTS offers (\n id TEXT PRIMARY KEY,\n username TEXT NOT NULL,\n service_id TEXT,\n sdp TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n last_seen INTEGER NOT NULL,\n answerer_username TEXT,\n answer_sdp TEXT,\n answered_at INTEGER,\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_offers_username ON offers(username);\n CREATE INDEX IF NOT EXISTS idx_offers_service ON offers(service_id);\n CREATE INDEX IF NOT EXISTS idx_offers_expires ON offers(expires_at);\n CREATE INDEX IF NOT EXISTS idx_offers_last_seen ON offers(last_seen);\n CREATE INDEX IF NOT EXISTS idx_offers_answerer ON offers(answerer_username);\n\n -- ICE candidates table\n CREATE TABLE IF NOT EXISTS ice_candidates (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n offer_id TEXT NOT NULL,\n username TEXT NOT NULL,\n role TEXT NOT NULL CHECK(role IN ('offerer', 'answerer')),\n candidate TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n FOREIGN KEY (offer_id) REFERENCES offers(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_ice_offer ON ice_candidates(offer_id);\n CREATE INDEX IF NOT EXISTS idx_ice_username ON ice_candidates(username);\n CREATE INDEX IF NOT EXISTS idx_ice_created ON ice_candidates(created_at);\n\n -- Usernames table\n CREATE TABLE IF NOT EXISTS usernames (\n username TEXT PRIMARY KEY,\n public_key TEXT NOT NULL UNIQUE,\n claimed_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n last_used INTEGER NOT NULL,\n metadata TEXT,\n CHECK(length(username) >= 3 AND length(username) <= 32)\n );\n\n CREATE INDEX IF NOT EXISTS idx_usernames_expires ON usernames(expires_at);\n CREATE INDEX IF NOT EXISTS idx_usernames_public_key ON usernames(public_key);\n\n -- Services table (new schema with extracted fields for discovery)\n CREATE TABLE IF NOT EXISTS services (\n id TEXT PRIMARY KEY,\n service_fqn TEXT NOT NULL,\n service_name TEXT NOT NULL,\n version TEXT NOT NULL,\n username TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n FOREIGN KEY (username) REFERENCES usernames(username) ON DELETE CASCADE,\n UNIQUE(service_fqn)\n );\n\n CREATE INDEX IF NOT EXISTS idx_services_fqn ON services(service_fqn);\n CREATE INDEX IF NOT EXISTS idx_services_discovery ON services(service_name, version);\n CREATE INDEX IF NOT EXISTS idx_services_username ON services(username);\n CREATE INDEX IF NOT EXISTS idx_services_expires ON services(expires_at);\n `);\n\n // Enable foreign keys\n this.db.pragma('foreign_keys = ON');\n }\n\n // ===== Offer Management =====\n\n async createOffers(offers: CreateOfferRequest[]): Promise<Offer[]> {\n const created: Offer[] = [];\n\n // Generate hash-based IDs for all offers first\n const offersWithIds = await Promise.all(\n offers.map(async (offer) => ({\n ...offer,\n id: offer.id || await generateOfferHash(offer.sdp),\n }))\n );\n\n // Use transaction for atomic creation\n const transaction = this.db.transaction((offersWithIds: (CreateOfferRequest & { id: string })[]) => {\n const offerStmt = this.db.prepare(`\n INSERT INTO offers (id, username, service_id, sdp, created_at, expires_at, last_seen)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const offer of offersWithIds) {\n const now = Date.now();\n\n // Insert offer\n offerStmt.run(\n offer.id,\n offer.username,\n offer.serviceId || null,\n offer.sdp,\n now,\n offer.expiresAt,\n now\n );\n\n created.push({\n id: offer.id,\n username: offer.username,\n serviceId: offer.serviceId || undefined,\n serviceFqn: offer.serviceFqn,\n sdp: offer.sdp,\n createdAt: now,\n expiresAt: offer.expiresAt,\n lastSeen: now,\n });\n }\n });\n\n transaction(offersWithIds);\n return created;\n }\n\n async getOffersByUsername(username: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE username = ? AND expires_at > ?\n ORDER BY last_seen DESC\n `);\n\n const rows = stmt.all(username, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getOfferById(offerId: string): Promise<Offer | null> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE id = ? AND expires_at > ?\n `);\n\n const row = stmt.get(offerId, Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return this.rowToOffer(row);\n }\n\n async deleteOffer(offerId: string, ownerUsername: string): Promise<boolean> {\n const stmt = this.db.prepare(`\n DELETE FROM offers\n WHERE id = ? AND username = ?\n `);\n\n const result = stmt.run(offerId, ownerUsername);\n return result.changes > 0;\n }\n\n async deleteExpiredOffers(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM offers WHERE expires_at < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n async answerOffer(\n offerId: string,\n answererUsername: string,\n answerSdp: string\n ): Promise<{ success: boolean; error?: string }> {\n // Check if offer exists and is not expired\n const offer = await this.getOfferById(offerId);\n\n if (!offer) {\n return {\n success: false,\n error: 'Offer not found or expired'\n };\n }\n\n // Check if offer already has an answerer\n if (offer.answererUsername) {\n return {\n success: false,\n error: 'Offer already answered'\n };\n }\n\n // Update offer with answer\n const stmt = this.db.prepare(`\n UPDATE offers\n SET answerer_username = ?, answer_sdp = ?, answered_at = ?\n WHERE id = ? AND answerer_username IS NULL\n `);\n\n const result = stmt.run(answererUsername, answerSdp, Date.now(), offerId);\n\n if (result.changes === 0) {\n return {\n success: false,\n error: 'Offer already answered (race condition)'\n };\n }\n\n return { success: true };\n }\n\n async getAnsweredOffers(offererUsername: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE username = ? AND answerer_username IS NOT NULL AND expires_at > ?\n ORDER BY answered_at DESC\n `);\n\n const rows = stmt.all(offererUsername, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n // ===== ICE Candidate Management =====\n\n async addIceCandidates(\n offerId: string,\n username: string,\n role: 'offerer' | 'answerer',\n candidates: any[]\n ): Promise<number> {\n const stmt = this.db.prepare(`\n INSERT INTO ice_candidates (offer_id, username, role, candidate, created_at)\n VALUES (?, ?, ?, ?, ?)\n `);\n\n const baseTimestamp = Date.now();\n const transaction = this.db.transaction((candidates: any[]) => {\n for (let i = 0; i < candidates.length; i++) {\n stmt.run(\n offerId,\n username,\n role,\n JSON.stringify(candidates[i]),\n baseTimestamp + i\n );\n }\n });\n\n transaction(candidates);\n return candidates.length;\n }\n\n async getIceCandidates(\n offerId: string,\n targetRole: 'offerer' | 'answerer',\n since?: number\n ): Promise<IceCandidate[]> {\n let query = `\n SELECT * FROM ice_candidates\n WHERE offer_id = ? AND role = ?\n `;\n\n const params: any[] = [offerId, targetRole];\n\n if (since !== undefined) {\n query += ' AND created_at > ?';\n params.push(since);\n }\n\n query += ' ORDER BY created_at ASC';\n\n const stmt = this.db.prepare(query);\n const rows = stmt.all(...params) as any[];\n\n return rows.map(row => ({\n id: row.id,\n offerId: row.offer_id,\n username: row.username,\n role: row.role,\n candidate: JSON.parse(row.candidate),\n createdAt: row.created_at,\n }));\n }\n\n // ===== Username Management =====\n\n async claimUsername(request: ClaimUsernameRequest): Promise<Username> {\n const now = Date.now();\n const expiresAt = now + YEAR_IN_MS;\n\n // Try to insert or update\n const stmt = this.db.prepare(`\n INSERT INTO usernames (username, public_key, claimed_at, expires_at, last_used, metadata)\n VALUES (?, ?, ?, ?, ?, NULL)\n ON CONFLICT(username) DO UPDATE SET\n expires_at = ?,\n last_used = ?\n WHERE public_key = ?\n `);\n\n const result = stmt.run(\n request.username,\n request.publicKey,\n now,\n expiresAt,\n now,\n expiresAt,\n now,\n request.publicKey\n );\n\n if (result.changes === 0) {\n throw new Error('Username already claimed by different public key');\n }\n\n return {\n username: request.username,\n publicKey: request.publicKey,\n claimedAt: now,\n expiresAt,\n lastUsed: now,\n };\n }\n\n async getUsername(username: string): Promise<Username | null> {\n const stmt = this.db.prepare(`\n SELECT * FROM usernames\n WHERE username = ? AND expires_at > ?\n `);\n\n const row = stmt.get(username, Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return {\n username: row.username,\n publicKey: row.public_key,\n claimedAt: row.claimed_at,\n expiresAt: row.expires_at,\n lastUsed: row.last_used,\n metadata: row.metadata || undefined,\n };\n }\n\n async touchUsername(username: string): Promise<boolean> {\n const now = Date.now();\n const expiresAt = now + YEAR_IN_MS;\n\n const stmt = this.db.prepare(`\n UPDATE usernames\n SET last_used = ?, expires_at = ?\n WHERE username = ? AND expires_at > ?\n `);\n\n const result = stmt.run(now, expiresAt, username, now);\n return result.changes > 0;\n }\n\n async deleteExpiredUsernames(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM usernames WHERE expires_at < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n // ===== Service Management =====\n\n async createService(request: CreateServiceRequest): Promise<{\n service: Service;\n offers: Offer[];\n }> {\n const serviceId = randomUUID();\n const now = Date.now();\n\n // Parse FQN to extract components\n const parsed = parseServiceFqn(request.serviceFqn);\n if (!parsed) {\n throw new Error(`Invalid service FQN: ${request.serviceFqn}`);\n }\n if (!parsed.username) {\n throw new Error(`Service FQN must include username: ${request.serviceFqn}`);\n }\n\n const { serviceName, version, username } = parsed;\n\n const transaction = this.db.transaction(() => {\n // Delete existing service with same (service_name, version, username) and its related offers (upsert behavior)\n const existingService = this.db.prepare(`\n SELECT id FROM services\n WHERE service_name = ? AND version = ? AND username = ?\n `).get(serviceName, version, username) as any;\n\n if (existingService) {\n // Delete related offers first (no FK cascade from offers to services)\n this.db.prepare(`\n DELETE FROM offers WHERE service_id = ?\n `).run(existingService.id);\n\n // Delete the service\n this.db.prepare(`\n DELETE FROM services WHERE id = ?\n `).run(existingService.id);\n }\n\n // Insert new service with extracted fields\n this.db.prepare(`\n INSERT INTO services (id, service_fqn, service_name, version, username, created_at, expires_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `).run(\n serviceId,\n request.serviceFqn,\n serviceName,\n version,\n username,\n now,\n request.expiresAt\n );\n\n // Touch username to extend expiry (inline logic)\n const expiresAt = now + YEAR_IN_MS;\n this.db.prepare(`\n UPDATE usernames\n SET last_used = ?, expires_at = ?\n WHERE username = ? AND expires_at > ?\n `).run(now, expiresAt, username, now);\n });\n\n transaction();\n\n // Create offers with serviceId (after transaction)\n const offerRequests = request.offers.map(offer => ({\n ...offer,\n serviceId,\n }));\n const offers = await this.createOffers(offerRequests);\n\n return {\n service: {\n id: serviceId,\n serviceFqn: request.serviceFqn,\n serviceName,\n version,\n username,\n createdAt: now,\n expiresAt: request.expiresAt,\n },\n offers,\n };\n }\n\n async getOffersForService(serviceId: string): Promise<Offer[]> {\n const stmt = this.db.prepare(`\n SELECT * FROM offers\n WHERE service_id = ? AND expires_at > ?\n ORDER BY created_at ASC\n `);\n\n const rows = stmt.all(serviceId, Date.now()) as any[];\n return rows.map(row => this.rowToOffer(row));\n }\n\n async getServiceById(serviceId: string): Promise<Service | null> {\n const stmt = this.db.prepare(`\n SELECT * FROM services\n WHERE id = ? AND expires_at > ?\n `);\n\n const row = stmt.get(serviceId, Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return this.rowToService(row);\n }\n\n async getServiceByFqn(serviceFqn: string): Promise<Service | null> {\n const stmt = this.db.prepare(`\n SELECT * FROM services\n WHERE service_fqn = ? AND expires_at > ?\n `);\n\n const row = stmt.get(serviceFqn, Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return this.rowToService(row);\n }\n\n async discoverServices(\n serviceName: string,\n version: string,\n limit: number,\n offset: number\n ): Promise<Service[]> {\n // Query for unique services with available offers\n // We join with offers and filter for available ones (answerer_username IS NULL)\n const stmt = this.db.prepare(`\n SELECT DISTINCT s.* FROM services s\n INNER JOIN offers o ON o.service_id = s.id\n WHERE s.service_name = ?\n AND s.version = ?\n AND s.expires_at > ?\n AND o.answerer_username IS NULL\n AND o.expires_at > ?\n ORDER BY s.created_at DESC\n LIMIT ? OFFSET ?\n `);\n\n const rows = stmt.all(serviceName, version, Date.now(), Date.now(), limit, offset) as any[];\n return rows.map(row => this.rowToService(row));\n }\n\n async getRandomService(serviceName: string, version: string): Promise<Service | null> {\n // Get a random service with an available offer\n const stmt = this.db.prepare(`\n SELECT s.* FROM services s\n INNER JOIN offers o ON o.service_id = s.id\n WHERE s.service_name = ?\n AND s.version = ?\n AND s.expires_at > ?\n AND o.answerer_username IS NULL\n AND o.expires_at > ?\n ORDER BY RANDOM()\n LIMIT 1\n `);\n\n const row = stmt.get(serviceName, version, Date.now(), Date.now()) as any;\n\n if (!row) {\n return null;\n }\n\n return this.rowToService(row);\n }\n\n async deleteService(serviceId: string, username: string): Promise<boolean> {\n const stmt = this.db.prepare(`\n DELETE FROM services\n WHERE id = ? AND username = ?\n `);\n\n const result = stmt.run(serviceId, username);\n return result.changes > 0;\n }\n\n async deleteExpiredServices(now: number): Promise<number> {\n const stmt = this.db.prepare('DELETE FROM services WHERE expires_at < ?');\n const result = stmt.run(now);\n return result.changes;\n }\n\n async close(): Promise<void> {\n this.db.close();\n }\n\n // ===== Helper Methods =====\n\n /**\n * Helper method to convert database row to Offer object\n */\n private rowToOffer(row: any): Offer {\n return {\n id: row.id,\n username: row.username,\n serviceId: row.service_id || undefined,\n serviceFqn: row.service_fqn || undefined,\n sdp: row.sdp,\n createdAt: row.created_at,\n expiresAt: row.expires_at,\n lastSeen: row.last_seen,\n answererUsername: row.answerer_username || undefined,\n answerSdp: row.answer_sdp || undefined,\n answeredAt: row.answered_at || undefined,\n };\n }\n\n /**\n * Helper method to convert database row to Service object\n */\n private rowToService(row: any): Service {\n return {\n id: row.id,\n serviceFqn: row.service_fqn,\n serviceName: row.service_name,\n version: row.version,\n username: row.username,\n createdAt: row.created_at,\n expiresAt: row.expires_at,\n };\n }\n}\n", "/**\n * Generates a content-based offer ID using SHA-256 hash\n * Creates deterministic IDs based on offer SDP content\n * PeerID is not included as it's inferred from authentication\n * Uses Web Crypto API for compatibility with both Node.js and Cloudflare Workers\n *\n * @param sdp - The WebRTC SDP offer\n * @returns SHA-256 hash of the SDP content\n */\nexport async function generateOfferHash(sdp: string): Promise<string> {\n // Sanitize and normalize the offer content\n // Only include core offer content (not peerId - that's inferred from auth)\n const sanitizedOffer = {\n sdp\n };\n\n // Create non-prettified JSON string\n const jsonString = JSON.stringify(sanitizedOffer);\n\n // Convert string to Uint8Array for hashing\n const encoder = new TextEncoder();\n const data = encoder.encode(jsonString);\n\n // Generate SHA-256 hash\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\n // Convert hash to hex string\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n\n return hashHex;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yBAAsB;;;ACAtB,kBAAqB;AACrB,kBAAqB;;;ACyBrB,IAAM,gBAAgB;AAAA,EAClB,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,IAAI;AACR;AACA,IAAM,EAAE,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,IAAI;AAChD,IAAM,IAAI;AACV,IAAM,KAAK;AAIX,IAAM,eAAe,IAAI,SAAS;AAC9B,MAAI,uBAAuB,SAAS,OAAO,MAAM,sBAAsB,YAAY;AAC/E,UAAM,kBAAkB,GAAG,IAAI;AAAA,EACnC;AACJ;AACA,IAAM,MAAM,CAAC,UAAU,OAAO;AAC1B,QAAM,IAAI,IAAI,MAAM,OAAO;AAC3B,eAAa,GAAG,GAAG;AACnB,QAAM;AACV;AACA,IAAM,QAAQ,CAAC,MAAM,OAAO,MAAM;AAClC,IAAM,QAAQ,CAAC,MAAM,OAAO,MAAM;AAClC,IAAM,UAAU,CAAC,MAAM,aAAa,cAAe,YAAY,OAAO,CAAC,KAAK,EAAE,YAAY,SAAS;AAEnG,IAAM,SAAS,CAAC,OAAO,QAAQ,QAAQ,OAAO;AAC1C,QAAM,QAAQ,QAAQ,KAAK;AAC3B,QAAM,MAAM,OAAO;AACnB,QAAM,WAAW,WAAW;AAC5B,MAAI,CAAC,SAAU,YAAY,QAAQ,QAAS;AACxC,UAAM,SAAS,SAAS,IAAI,KAAK;AACjC,UAAM,QAAQ,WAAW,cAAc,MAAM,KAAK;AAClD,UAAM,MAAM,QAAQ,UAAU,GAAG,KAAK,QAAQ,OAAO,KAAK;AAC1D,QAAI,SAAS,wBAAwB,QAAQ,WAAW,GAAG;AAAA,EAC/D;AACA,SAAO;AACX;AAEA,IAAM,MAAM,CAAC,QAAQ,IAAI,WAAW,GAAG;AACvC,IAAM,OAAO,CAAC,QAAQ,WAAW,KAAK,GAAG;AACzC,IAAM,OAAO,CAAC,GAAG,QAAQ,EAAE,SAAS,EAAE,EAAE,SAAS,KAAK,GAAG;AACzD,IAAM,aAAa,CAAC,MAAM,MAAM,KAAK,OAAO,CAAC,CAAC,EACzC,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,EACrB,KAAK,EAAE;AACZ,IAAM,IAAI,EAAE,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;AACxD,IAAM,MAAM,CAAC,OAAO;AAChB,MAAI,MAAM,EAAE,MAAM,MAAM,EAAE;AACtB,WAAO,KAAK,EAAE;AAClB,MAAI,MAAM,EAAE,KAAK,MAAM,EAAE;AACrB,WAAO,MAAM,EAAE,IAAI;AACvB,MAAI,MAAM,EAAE,KAAK,MAAM,EAAE;AACrB,WAAO,MAAM,EAAE,IAAI;AACvB;AACJ;AACA,IAAM,aAAa,CAAC,QAAQ;AACxB,QAAM,IAAI;AACV,MAAI,CAAC,MAAM,GAAG;AACV,WAAO,IAAI,CAAC;AAChB,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,KAAK;AAChB,MAAI,KAAK;AACL,WAAO,IAAI,CAAC;AAChB,QAAM,QAAQ,IAAI,EAAE;AACpB,WAAS,KAAK,GAAG,KAAK,GAAG,KAAK,IAAI,MAAM,MAAM,GAAG;AAE7C,UAAM,KAAK,IAAI,IAAI,WAAW,EAAE,CAAC;AACjC,UAAM,KAAK,IAAI,IAAI,WAAW,KAAK,CAAC,CAAC;AACrC,QAAI,OAAO,UAAa,OAAO;AAC3B,aAAO,IAAI,CAAC;AAChB,UAAM,EAAE,IAAI,KAAK,KAAK;AAAA,EAC1B;AACA,SAAO;AACX;AACA,IAAM,KAAK,MAAM,YAAY;AAC7B,IAAM,SAAS,MAAM,GAAG,GAAG,UAAU,IAAI,kDAAkD;AAE3F,IAAM,cAAc,IAAI,SAAS;AAC7B,QAAM,IAAI,IAAI,KAAK,OAAO,CAAC,KAAK,MAAM,MAAM,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAChE,MAAI,MAAM;AACV,OAAK,QAAQ,OAAK;AAAE,MAAE,IAAI,GAAG,GAAG;AAAG,WAAO,EAAE;AAAA,EAAQ,CAAC;AACrD,SAAO;AACX;AAMA,IAAM,MAAM;AACZ,IAAM,cAAc,CAAC,GAAG,KAAK,KAAK,MAAM,+BAAgC,MAAM,CAAC,KAAK,OAAO,KAAK,IAAI,MAAM,IAAI,IAAI,GAAG;AAErH,IAAM,IAAI,CAAC,GAAG,IAAI,MAAM;AACpB,QAAM,IAAI,IAAI;AACd,SAAO,KAAK,KAAK,IAAI,IAAI;AAC7B;AACA,IAAM,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;AAG1B,IAAM,SAAS,CAAC,KAAK,OAAO;AACxB,MAAI,QAAQ,MAAM,MAAM;AACpB,QAAI,kBAAkB,MAAM,UAAU,EAAE;AAC5C,MAAI,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI;AACxD,SAAO,MAAM,IAAI;AACb,UAAM,IAAI,IAAI,GAAG,IAAI,IAAI;AACzB,UAAM,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI;AACjC,QAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;AAAA,EAC3C;AACA,SAAO,MAAM,KAAK,EAAE,GAAG,EAAE,IAAI,IAAI,YAAY;AACjD;AASA,IAAM,SAAS,CAAC,MAAO,aAAa,QAAQ,IAAI,IAAI,gBAAgB;AAGpE,IAAM,OAAO,MAAM;AAEnB,IAAM,QAAN,MAAM,OAAM;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY,GAAG,GAAG,GAAG,GAAG;AACpB,UAAM,MAAM;AACZ,SAAK,IAAI,YAAY,GAAG,IAAI,GAAG;AAC/B,SAAK,IAAI,YAAY,GAAG,IAAI,GAAG;AAC/B,SAAK,IAAI,YAAY,GAAG,IAAI,GAAG;AAC/B,SAAK,IAAI,YAAY,GAAG,IAAI,GAAG;AAC/B,WAAO,OAAO,IAAI;AAAA,EACtB;AAAA,EACA,OAAO,QAAQ;AACX,WAAO;AAAA,EACX;AAAA,EACA,OAAO,WAAW,GAAG;AACjB,WAAO,IAAI,OAAM,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AAAA,EAC/C;AAAA;AAAA,EAEA,OAAO,UAAU,KAAK,SAAS,OAAO;AAClC,UAAM,IAAI;AAEV,UAAM,SAAS,KAAK,OAAO,KAAK,CAAC,CAAC;AAElC,UAAM,WAAW,IAAI,EAAE;AACvB,WAAO,EAAE,IAAI,WAAW,CAAC;AACzB,UAAM,IAAI,aAAa,MAAM;AAG7B,UAAM,MAAM,SAAS,OAAO;AAC5B,gBAAY,GAAG,IAAI,GAAG;AACtB,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,UAAM,IAAI,EAAE,KAAK,EAAE;AACnB,UAAM,IAAI,EAAE,IAAI,KAAK,EAAE;AACvB,QAAI,EAAE,SAAS,OAAO,EAAE,IAAI,QAAQ,GAAG,CAAC;AACxC,QAAI,CAAC;AACD,UAAI,uBAAuB;AAC/B,UAAM,UAAU,IAAI,QAAQ;AAC5B,UAAM,iBAAiB,WAAW,SAAU;AAC5C,QAAI,CAAC,UAAU,MAAM,MAAM;AACvB,UAAI,gCAAgC;AACxC,QAAI,kBAAkB;AAClB,UAAI,EAAE,CAAC,CAAC;AACZ,WAAO,IAAI,OAAM,GAAG,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC;AAAA,EACvC;AAAA,EACA,OAAO,QAAQ,KAAK,QAAQ;AACxB,WAAO,OAAM,UAAU,WAAW,GAAG,GAAG,MAAM;AAAA,EAClD;AAAA,EACA,IAAI,IAAI;AACJ,WAAO,KAAK,SAAS,EAAE;AAAA,EAC3B;AAAA,EACA,IAAI,IAAI;AACJ,WAAO,KAAK,SAAS,EAAE;AAAA,EAC3B;AAAA;AAAA,EAEA,iBAAiB;AACb,UAAM,IAAI;AACV,UAAM,IAAI;AACV,UAAM,IAAI;AACV,QAAI,EAAE,IAAI;AACN,aAAO,IAAI,iBAAiB;AAGhC,UAAM,EAAE,GAAG,GAAG,GAAG,EAAE,IAAI;AACvB,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,UAAM,KAAK,EAAE,KAAK,EAAE;AACpB,UAAM,MAAM,EAAE,KAAK,CAAC;AACpB,UAAM,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC/B,UAAM,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACtC,QAAI,SAAS;AACT,aAAO,IAAI,uCAAuC;AAEtD,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,QAAI,OAAO;AACP,aAAO,IAAI,uCAAuC;AACtD,WAAO;AAAA,EACX;AAAA;AAAA,EAEA,OAAO,OAAO;AACV,UAAM,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI;AAChC,UAAM,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,OAAO,KAAK;AAC5C,UAAM,OAAO,EAAE,KAAK,EAAE;AACtB,UAAM,OAAO,EAAE,KAAK,EAAE;AACtB,UAAM,OAAO,EAAE,KAAK,EAAE;AACtB,UAAM,OAAO,EAAE,KAAK,EAAE;AACtB,WAAO,SAAS,QAAQ,SAAS;AAAA,EACrC;AAAA,EACA,MAAM;AACF,WAAO,KAAK,OAAO,CAAC;AAAA,EACxB;AAAA;AAAA,EAEA,SAAS;AACL,WAAO,IAAI,OAAM,EAAE,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;AAAA,EAC3D;AAAA;AAAA,EAEA,SAAS;AACL,UAAM,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI;AAChC,UAAM,IAAI;AAEV,UAAM,IAAI,EAAE,KAAK,EAAE;AACnB,UAAM,IAAI,EAAE,KAAK,EAAE;AACnB,UAAMA,KAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC3B,UAAM,IAAI,EAAE,IAAI,CAAC;AACjB,UAAM,OAAO,KAAK;AAClB,UAAM,IAAI,EAAE,EAAE,OAAO,IAAI,IAAI,IAAI,CAAC;AAClC,UAAMC,KAAI,IAAI;AACd,UAAM,IAAIA,KAAID;AACd,UAAM,IAAI,IAAI;AACd,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,UAAM,KAAK,EAAEC,KAAI,CAAC;AAClB,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,UAAM,KAAK,EAAE,IAAIA,EAAC;AAClB,WAAO,IAAI,OAAM,IAAI,IAAI,IAAI,EAAE;AAAA,EACnC;AAAA;AAAA,EAEA,IAAI,OAAO;AACP,UAAM,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI;AACvC,UAAM,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,OAAO,KAAK;AACnD,UAAM,IAAI;AACV,UAAM,IAAI;AAEV,UAAM,IAAI,EAAE,KAAK,EAAE;AACnB,UAAM,IAAI,EAAE,KAAK,EAAE;AACnB,UAAMD,KAAI,EAAE,KAAK,IAAI,EAAE;AACvB,UAAM,IAAI,EAAE,KAAK,EAAE;AACnB,UAAM,IAAI,GAAG,KAAK,OAAO,KAAK,MAAM,IAAI,CAAC;AACzC,UAAM,IAAI,EAAE,IAAIA,EAAC;AACjB,UAAMC,KAAI,EAAE,IAAID,EAAC;AACjB,UAAM,IAAI,EAAE,IAAI,IAAI,CAAC;AACrB,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,UAAM,KAAK,EAAEC,KAAI,CAAC;AAClB,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,UAAM,KAAK,EAAE,IAAIA,EAAC;AAClB,WAAO,IAAI,OAAM,IAAI,IAAI,IAAI,EAAE;AAAA,EACnC;AAAA,EACA,SAAS,OAAO;AACZ,WAAO,KAAK,IAAI,OAAO,KAAK,EAAE,OAAO,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAS,GAAG,OAAO,MAAM;AACrB,QAAI,CAAC,SAAS,MAAM,MAAM,KAAK,IAAI;AAC/B,aAAO;AACX,gBAAY,GAAG,IAAI,CAAC;AACpB,QAAI,MAAM;AACN,aAAO;AACX,QAAI,KAAK,OAAO,CAAC;AACb,aAAO,KAAK,CAAC,EAAE;AAEnB,QAAI,IAAI;AACR,QAAI,IAAI;AACR,aAAS,IAAI,MAAM,IAAI,IAAI,IAAI,EAAE,OAAO,GAAG,MAAM,IAAI;AAGjD,UAAI,IAAI;AACJ,YAAI,EAAE,IAAI,CAAC;AAAA,eACN;AACL,YAAI,EAAE,IAAI,CAAC;AAAA,IACnB;AACA,WAAO;AAAA,EACX;AAAA,EACA,eAAe,QAAQ;AACnB,WAAO,KAAK,SAAS,QAAQ,KAAK;AAAA,EACtC;AAAA;AAAA,EAEA,WAAW;AACP,UAAM,EAAE,GAAG,GAAG,EAAE,IAAI;AAEpB,QAAI,KAAK,OAAO,CAAC;AACb,aAAO,EAAE,GAAG,IAAI,GAAG,GAAG;AAC1B,UAAM,KAAK,OAAO,GAAG,CAAC;AAEtB,QAAI,EAAE,IAAI,EAAE,MAAM;AACd,UAAI,iBAAiB;AAEzB,UAAM,IAAI,EAAE,IAAI,EAAE;AAClB,UAAM,IAAI,EAAE,IAAI,EAAE;AAClB,WAAO,EAAE,GAAG,EAAE;AAAA,EAClB;AAAA,EACA,UAAU;AACN,UAAM,EAAE,GAAG,EAAE,IAAI,KAAK,eAAe,EAAE,SAAS;AAChD,UAAM,IAAI,WAAW,CAAC;AAEtB,MAAE,EAAE,KAAK,IAAI,KAAK,MAAO;AACzB,WAAO;AAAA,EACX;AAAA,EACA,QAAQ;AACJ,WAAO,WAAW,KAAK,QAAQ,CAAC;AAAA,EACpC;AAAA,EACA,gBAAgB;AACZ,WAAO,KAAK,SAAS,IAAI,CAAC,GAAG,KAAK;AAAA,EACtC;AAAA,EACA,eAAe;AACX,WAAO,KAAK,cAAc,EAAE,IAAI;AAAA,EACpC;AAAA,EACA,gBAAgB;AAEZ,QAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,OAAO;AAC5C,QAAI,IAAI;AACJ,UAAI,EAAE,IAAI,IAAI;AAClB,WAAO,EAAE,IAAI;AAAA,EACjB;AACJ;AAEA,IAAM,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;AAE1C,IAAM,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,EAAE;AAElC,MAAM,OAAO;AACb,MAAM,OAAO;AACb,IAAM,aAAa,CAAC,QAAQ,WAAW,KAAK,YAAY,KAAK,IAAI,IAAI,GAAG,EAAE,CAAC,EAAE,QAAQ;AACrF,IAAM,eAAe,CAAC,MAAM,IAAI,OAAO,WAAW,KAAK,OAAO,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC5E,IAAM,OAAO,CAAC,GAAG,UAAU;AAEvB,MAAI,IAAI;AACR,SAAO,UAAU,IAAI;AACjB,SAAK;AACL,SAAK;AAAA,EACT;AACA,SAAO;AACX;AAEA,IAAM,cAAc,CAAC,MAAM;AACvB,QAAM,KAAM,IAAI,IAAK;AACrB,QAAM,KAAM,KAAK,IAAK;AACtB,QAAM,KAAM,KAAK,IAAI,EAAE,IAAI,KAAM;AACjC,QAAM,KAAM,KAAK,IAAI,EAAE,IAAI,IAAK;AAChC,QAAM,MAAO,KAAK,IAAI,EAAE,IAAI,KAAM;AAClC,QAAM,MAAO,KAAK,KAAK,GAAG,IAAI,MAAO;AACrC,QAAM,MAAO,KAAK,KAAK,GAAG,IAAI,MAAO;AACrC,QAAM,MAAO,KAAK,KAAK,GAAG,IAAI,MAAO;AACrC,QAAM,OAAQ,KAAK,KAAK,GAAG,IAAI,MAAO;AACtC,QAAM,OAAQ,KAAK,MAAM,GAAG,IAAI,MAAO;AACvC,QAAM,OAAQ,KAAK,MAAM,GAAG,IAAI,MAAO;AACvC,QAAM,YAAa,KAAK,MAAM,EAAE,IAAI,IAAK;AACzC,SAAO,EAAE,WAAW,GAAG;AAC3B;AACA,IAAM,MAAM;AAGZ,IAAM,UAAU,CAAC,GAAG,MAAM;AACtB,QAAM,KAAK,EAAE,IAAI,IAAI,CAAC;AACtB,QAAM,KAAK,EAAE,KAAK,KAAK,CAAC;AACxB,QAAM,MAAM,YAAY,IAAI,EAAE,EAAE;AAChC,MAAI,IAAI,EAAE,IAAI,KAAK,GAAG;AACtB,QAAM,MAAM,EAAE,IAAI,IAAI,CAAC;AACvB,QAAM,QAAQ;AACd,QAAM,QAAQ,EAAE,IAAI,GAAG;AACvB,QAAM,WAAW,QAAQ;AACzB,QAAM,WAAW,QAAQ,EAAE,CAAC,CAAC;AAC7B,QAAM,SAAS,QAAQ,EAAE,CAAC,IAAI,GAAG;AACjC,MAAI;AACA,QAAI;AACR,MAAI,YAAY;AACZ,QAAI;AACR,OAAK,EAAE,CAAC,IAAI,QAAQ;AAChB,QAAI,EAAE,CAAC,CAAC;AACZ,SAAO,EAAE,SAAS,YAAY,UAAU,OAAO,EAAE;AACrD;AAEA,IAAM,UAAU,CAAC,SAAS,KAAK,aAAa,IAAI,CAAC;AAGjD,IAAM,UAAU,IAAI,MAAM,OAAO,YAAY,YAAY,GAAG,CAAC,CAAC;AAsB9D,IAAM,cAAc,CAAC,QAAQ,QAAQ,IAAI,QAAQ,EAAE,KAAK,IAAI,MAAM;AAmClE,IAAM,oBAAoB,EAAE,QAAQ,KAAK;AACzC,IAAM,UAAU,CAAC,KAAK,KAAK,KAAK,OAAO,sBAAsB;AACzD,QAAM,OAAO,KAAK,EAAE;AACpB,QAAM,OAAO,GAAG;AAChB,QAAM,OAAO,KAAK,CAAC;AACnB,QAAM,EAAE,OAAO,IAAI;AACnB,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI,WAAW,WAAW,GAAG;AAC7B,MAAI;AACA,QAAI,MAAM,UAAU,KAAK,MAAM;AAC/B,QAAI,MAAM,UAAU,IAAI,MAAM,GAAG,CAAC,GAAG,MAAM;AAC3C,QAAI,aAAa,IAAI,MAAM,GAAG,EAAE,CAAC;AACjC,SAAK,EAAE,SAAS,GAAG,KAAK;AACxB,eAAW,YAAY,EAAE,QAAQ,GAAG,EAAE,QAAQ,GAAG,GAAG;AAAA,EACxD,SACO,OAAO;AAAA,EAAE;AAChB,QAAM,SAAS,CAAC,WAAW;AAEvB,QAAI,MAAM;AACN,aAAO;AACX,QAAI,CAAC,UAAU,EAAE,aAAa;AAC1B,aAAO;AACX,UAAM,IAAI,QAAQ,MAAM;AACxB,UAAM,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,KAAK,CAAC;AACtC,WAAO,IAAI,IAAI,GAAG,OAAO,CAAC,EAAE,cAAc,EAAE,IAAI;AAAA,EACpD;AACA,SAAO,EAAE,UAAU,OAAO;AAC9B;AAEA,IAAM,cAAc,OAAO,WAAW,SAAS,WAAW,OAAO,sBAAsB,YAAY,QAAQ,WAAW,SAAS,WAAW,IAAI,CAAC;AAY/I,IAAM,SAAS;AAAA,EACX,aAAa,OAAO,YAAY;AAC5B,UAAM,IAAI,OAAO;AACjB,UAAM,IAAI,YAAY,OAAO;AAC7B,WAAO,IAAI,MAAM,EAAE,OAAO,WAAW,EAAE,MAAM,CAAC;AAAA,EAClD;AAAA,EACA,QAAQ;AACZ;AAsBA,IAAM,IAAI;AACV,IAAM,aAAa;AACnB,IAAM,WAAW,KAAK,KAAK,aAAa,CAAC,IAAI;AAC7C,IAAM,cAAc,MAAM,IAAI;AAC9B,IAAM,aAAa,MAAM;AACrB,QAAM,SAAS,CAAC;AAChB,MAAI,IAAI;AACR,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AAC/B,QAAI;AACJ,WAAO,KAAK,CAAC;AACb,aAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AAClC,UAAI,EAAE,IAAI,CAAC;AACX,aAAO,KAAK,CAAC;AAAA,IACjB;AACA,QAAI,EAAE,OAAO;AAAA,EACjB;AACA,SAAO;AACX;AACA,IAAI,QAAQ;AAEZ,IAAM,QAAQ,CAAC,KAAK,MAAM;AACtB,QAAM,IAAI,EAAE,OAAO;AACnB,SAAO,MAAM,IAAI;AACrB;AAYA,IAAM,OAAO,CAAC,MAAM;AAChB,QAAM,OAAO,UAAU,QAAQ,WAAW;AAC1C,MAAI,IAAI;AACR,MAAI,IAAI;AACR,QAAM,UAAU,KAAK;AACrB,QAAM,SAAS;AACf,QAAM,OAAO,IAAI,UAAU,CAAC;AAC5B,QAAM,UAAU,IAAI,CAAC;AACrB,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AAC/B,QAAI,QAAQ,OAAO,IAAI,IAAI;AAC3B,UAAM;AAMN,QAAI,QAAQ,aAAa;AACrB,eAAS;AACT,WAAK;AAAA,IACT;AACA,UAAM,MAAM,IAAI;AAChB,UAAM,OAAO;AACb,UAAM,OAAO,MAAM,KAAK,IAAI,KAAK,IAAI;AACrC,UAAM,SAAS,IAAI,MAAM;AACzB,UAAM,QAAQ,QAAQ;AACtB,QAAI,UAAU,GAAG;AAEb,UAAI,EAAE,IAAI,MAAM,QAAQ,KAAK,IAAI,CAAC,CAAC;AAAA,IACvC,OACK;AACD,UAAI,EAAE,IAAI,MAAM,OAAO,KAAK,IAAI,CAAC,CAAC;AAAA,IACtC;AAAA,EACJ;AACA,MAAI,MAAM;AACN,QAAI,cAAc;AACtB,SAAO,EAAE,GAAG,EAAE;AAClB;;;AC5mBA,yBAAuB;AAIf,OAAO,cAAc,OAAO,YAAwB;AAC1D,SAAO,IAAI,WAAW,MAAM,OAAO,OAAO,OAAO,WAAW,OAAuB,CAAC;AACtF;AAGA,IAAM,iBAAiB;AACvB,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAG5B,IAAM,yBAAyB,IAAI,KAAK;AA0BxC,SAAS,cAAc,QAA4B;AACjD,SAAO,IAAI,WAAW,0BAAO,KAAK,QAAQ,QAAQ,CAAC;AACrD;AAOO,SAAS,oBACd,kBACA,SACoC;AACpC,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAE/B,MAAI,MAAM,SAAS,GAAG;AACpB,WAAO,EAAE,OAAO,OAAO,OAAO,uEAAuE;AAAA,EACvG;AAGA,QAAM,kBAAkB,MAAM,CAAC;AAC/B,QAAM,YAAY,SAAS,MAAM,MAAM,SAAS,CAAC,GAAG,EAAE;AAGtD,MAAI,oBAAoB,kBAAkB;AACxC,WAAO,EAAE,OAAO,OAAO,OAAO,4DAA4D;AAAA,EAC5F;AAGA,MAAI,MAAM,SAAS,GAAG;AACpB,WAAO,EAAE,OAAO,OAAO,OAAO,+BAA+B;AAAA,EAC/D;AAEA,QAAM,iBAAiB,kBAAkB,SAAS;AAClD,MAAI,CAAC,eAAe,OAAO;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;AAQO,SAAS,iBAAiB,UAAsD;AACrF,MAAI,OAAO,aAAa,UAAU;AAChC,WAAO,EAAE,OAAO,OAAO,OAAO,4BAA4B;AAAA,EAC5D;AAEA,MAAI,SAAS,SAAS,qBAAqB;AACzC,WAAO,EAAE,OAAO,OAAO,OAAO,6BAA6B,mBAAmB,cAAc;AAAA,EAC9F;AAEA,MAAI,SAAS,SAAS,qBAAqB;AACzC,WAAO,EAAE,OAAO,OAAO,OAAO,4BAA4B,mBAAmB,cAAc;AAAA,EAC7F;AAEA,MAAI,CAAC,eAAe,KAAK,QAAQ,GAAG;AAClC,WAAO,EAAE,OAAO,OAAO,OAAO,gGAAgG;AAAA,EAChI;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;AAQO,SAAS,mBAAmB,KAAiD;AAClF,MAAI,OAAO,QAAQ,UAAU;AAC3B,WAAO,EAAE,OAAO,OAAO,OAAO,+BAA+B;AAAA,EAC/D;AAGA,QAAM,SAAS,gBAAgB,GAAG;AAClC,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,OAAO,OAAO,OAAO,4DAA4D;AAAA,EAC5F;AAEA,QAAM,EAAE,aAAa,SAAS,SAAS,IAAI;AAG3C,QAAM,mBAAmB;AACzB,MAAI,CAAC,iBAAiB,KAAK,WAAW,GAAG;AACvC,WAAO,EAAE,OAAO,OAAO,OAAO,wEAAwE;AAAA,EACxG;AAEA,MAAI,YAAY,SAAS,KAAK,YAAY,SAAS,KAAK;AACtD,WAAO,EAAE,OAAO,OAAO,OAAO,wCAAwC;AAAA,EACxE;AAGA,QAAM,eAAe;AACrB,MAAI,CAAC,aAAa,KAAK,OAAO,GAAG;AAC/B,WAAO,EAAE,OAAO,OAAO,OAAO,gEAAgE;AAAA,EAChG;AAGA,MAAI,UAAU;AACZ,UAAM,gBAAgB,iBAAiB,QAAQ;AAC/C,QAAI,CAAC,cAAc,OAAO;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;AAKO,SAAS,aAAa,SAA8F;AACzH,QAAM,QAAQ,QAAQ,MAAM,+CAA+C;AAC3E,MAAI,CAAC,MAAO,QAAO;AAEnB,SAAO;AAAA,IACL,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,IAC5B,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,IAC5B,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,IAC5B,YAAY,MAAM,CAAC,GAAG,UAAU,CAAC;AAAA;AAAA,EACnC;AACF;AAMO,SAAS,oBAAoB,WAAmB,WAA4B;AACjF,QAAM,MAAM,aAAa,SAAS;AAClC,QAAM,QAAQ,aAAa,SAAS;AAEpC,MAAI,CAAC,OAAO,CAAC,MAAO,QAAO;AAG3B,MAAI,IAAI,UAAU,MAAM,MAAO,QAAO;AAGtC,MAAI,IAAI,UAAU,KAAK,IAAI,UAAU,MAAM,MAAO,QAAO;AAGzD,MAAI,MAAM,QAAQ,IAAI,MAAO,QAAO;AACpC,MAAI,MAAM,UAAU,IAAI,SAAS,MAAM,QAAQ,IAAI,MAAO,QAAO;AAGjE,MAAI,IAAI,cAAc,IAAI,eAAe,MAAM,WAAY,QAAO;AAElE,SAAO;AACT;AAQO,SAAS,gBAAgB,KAAuF;AACrH,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAG5C,QAAM,UAAU,IAAI,YAAY,GAAG;AACnC,MAAI;AACJ,MAAI,WAA0B;AAE9B,MAAI,UAAU,GAAG;AAEf,qBAAiB,IAAI,UAAU,GAAG,OAAO;AACzC,eAAW,IAAI,UAAU,UAAU,CAAC;AAAA,EACtC,OAAO;AAEL,qBAAiB;AAAA,EACnB;AAGA,QAAM,aAAa,eAAe,QAAQ,GAAG;AAC7C,MAAI,cAAc,EAAG,QAAO;AAE5B,QAAM,cAAc,eAAe,UAAU,GAAG,UAAU;AAC1D,QAAM,UAAU,eAAe,UAAU,aAAa,CAAC;AAEvD,MAAI,CAAC,eAAe,CAAC,QAAS,QAAO;AAErC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKO,SAAS,kBAAkB,WAAuD;AACvF,MAAI,OAAO,cAAc,YAAY,CAAC,OAAO,SAAS,SAAS,GAAG;AAChE,WAAO,EAAE,OAAO,OAAO,OAAO,oCAAoC;AAAA,EACpE;AAEA,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,OAAO,KAAK,IAAI,MAAM,SAAS;AAErC,MAAI,OAAO,wBAAwB;AACjC,WAAO,EAAE,OAAO,OAAO,OAAO,sDAAsD,yBAAyB,GAAI,KAAK;AAAA,EACxH;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;AASA,eAAsB,uBACpB,WACA,WACA,SACkB;AAClB,MAAI;AAEF,UAAM,iBAAiB,cAAc,SAAS;AAC9C,UAAM,iBAAiB,cAAc,SAAS;AAG9C,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,eAAe,QAAQ,OAAO,OAAO;AAG3C,UAAM,UAAU,MAAc,YAAY,gBAAgB,cAAc,cAAc;AACtF,WAAO;AAAA,EACT,SAASC,MAAK;AACZ,YAAQ,MAAM,0CAA0CA,IAAG;AAC3D,WAAO;AAAA,EACT;AACF;;;AC/QA,IAAM,gBAAgB;AAsCtB,eAAe,WACb,UACA,SACA,WACA,WACA,SAC6C;AAE7C,MAAI,iBAAiB,MAAM,QAAQ,YAAY,QAAQ;AAGvD,MAAI,CAAC,gBAAgB;AACnB,QAAI,CAAC,WAAW;AACd,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO,aAAa,QAAQ;AAAA,MAC9B;AAAA,IACF;AAGA,UAAM,qBAAqB,iBAAiB,QAAQ;AACpD,QAAI,CAAC,mBAAmB,OAAO;AAC7B,aAAO;AAAA,IACT;AAGA,UAAM,iBAAiB,MAAM,uBAAuB,WAAW,WAAW,OAAO;AACjF,QAAI,CAAC,gBAAgB;AACnB,aAAO,EAAE,OAAO,OAAO,OAAO,mCAAmC;AAAA,IACnE;AAGA,UAAM,YAAY,KAAK,IAAI,IAAI,MAAM,KAAK,KAAK,KAAK;AACpD,UAAM,QAAQ,cAAc;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,qBAAiB,MAAM,QAAQ,YAAY,QAAQ;AACnD,QAAI,CAAC,gBAAgB;AACnB,aAAO,EAAE,OAAO,OAAO,OAAO,2BAA2B;AAAA,IAC3D;AAAA,EACF;AAGA,QAAM,UAAU,MAAM;AAAA,IACpB,eAAe;AAAA,IACf;AAAA,IACA;AAAA,EACF;AACA,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,OAAO,OAAO,OAAO,oBAAoB;AAAA,EACpD;AAGA,QAAM,aAAa,oBAAoB,UAAU,OAAO;AACxD,MAAI,CAAC,WAAW,OAAO;AACrB,WAAO,EAAE,OAAO,OAAO,OAAO,WAAW,MAAM;AAAA,EACjD;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;AAKA,SAAS,gBAAgB,SAAgC;AAEvD,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,MAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,SAAO,MAAM,CAAC;AAChB;AAMA,IAAM,WAAuC;AAAA;AAAA;AAAA;AAAA,EAI3C,MAAM,QAAQ,QAAQ,SAAS,WAAW,WAAW,SAAS,QAAQ;AACpE,UAAM,EAAE,SAAS,IAAI;AACrB,UAAM,UAAU,MAAM,QAAQ,YAAY,QAAQ;AAElD,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,QACL;AAAA,QACA,WAAW;AAAA,MACb;AAAA,IACF;AAEA,WAAO;AAAA,MACL,UAAU,QAAQ;AAAA,MAClB,WAAW;AAAA,MACX,WAAW,QAAQ;AAAA,MACnB,WAAW,QAAQ;AAAA,MACnB,WAAW,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,WAAW,QAAQ,SAAS,WAAW,WAAW,SAAS,QAAQ;AACvE,UAAM,EAAE,YAAY,OAAO,OAAO,IAAI;AACtC,UAAM,WAAW,gBAAgB,OAAO;AAGxC,QAAI,UAAU;AACZ,YAAM,OAAO,MAAM,WAAW,UAAU,SAAS,WAAW,WAAW,OAAO;AAC9E,UAAI,CAAC,KAAK,OAAO;AACf,cAAM,IAAI,MAAM,KAAK,KAAK;AAAA,MAC5B;AAAA,IACF;AAGA,UAAM,gBAAgB,mBAAmB,UAAU;AACnD,QAAI,CAAC,cAAc,OAAO;AACxB,YAAM,IAAI,MAAM,cAAc,SAAS,qBAAqB;AAAA,IAC9D;AAEA,UAAM,SAAS,gBAAgB,UAAU;AACzC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAGA,UAAM,2BAA2B,CAAC,aAAa;AAC7C,aAAO,SAAS,OAAO,CAAC,MAAM;AAC5B,cAAM,iBAAiB,gBAAgB,EAAE,UAAU;AACnD,eACE,kBACA,oBAAoB,OAAO,SAAS,eAAe,OAAO;AAAA,MAE9D,CAAC;AAAA,IACH;AAGA,UAAM,qBAAqB,OAAO,YAAY;AAC5C,YAAM,SAAS,MAAM,QAAQ,oBAAoB,QAAQ,EAAE;AAC3D,aAAO,OAAO,KAAK,CAAC,MAAM,CAAC,EAAE,gBAAgB;AAAA,IAC/C;AAGA,UAAM,uBAAuB,CAAC,SAAS,WAAW;AAAA,MAChD,WAAW,QAAQ;AAAA,MACnB,UAAU,QAAQ;AAAA,MAClB,YAAY,QAAQ;AAAA,MACpB,SAAS,MAAM;AAAA,MACf,KAAK,MAAM;AAAA,MACX,WAAW,QAAQ;AAAA,MACnB,WAAW,QAAQ;AAAA,IACrB;AAGA,QAAI,UAAU,QAAW;AACvB,YAAM,YAAY,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,GAAG,aAAa;AAC5D,YAAM,aAAa,KAAK,IAAI,GAAG,UAAU,CAAC;AAE1C,YAAMC,eAAc,MAAM,QAAQ,kBAAkB,OAAO,SAAS,OAAO,OAAO;AAClF,YAAMC,sBAAqB,yBAAyBD,YAAW;AAG/D,YAAM,cAAc,oBAAI,IAAY;AACpC,YAAM,iBAAwB,CAAC;AAE/B,iBAAW,WAAWC,qBAAoB;AACxC,YAAI,CAAC,YAAY,IAAI,QAAQ,QAAQ,GAAG;AACtC,sBAAY,IAAI,QAAQ,QAAQ;AAChC,gBAAMC,kBAAiB,MAAM,mBAAmB,OAAO;AAEvD,cAAIA,iBAAgB;AAClB,2BAAe,KAAK,qBAAqB,SAASA,eAAc,CAAC;AAAA,UACnE;AAAA,QACF;AAAA,MACF;AAGA,YAAM,oBAAoB,eAAe,MAAM,YAAY,aAAa,SAAS;AAEjF,aAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO,kBAAkB;AAAA,QACzB,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF;AAGA,QAAI,OAAO,UAAU;AACnB,YAAM,UAAU,MAAM,QAAQ,gBAAgB,UAAU;AACxD,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI,MAAM,mBAAmB;AAAA,MACrC;AAEA,YAAMA,kBAAiB,MAAM,mBAAmB,OAAO;AACvD,UAAI,CAACA,iBAAgB;AACnB,cAAM,IAAI,MAAM,iCAAiC;AAAA,MACnD;AAEA,aAAO,qBAAqB,SAASA,eAAc;AAAA,IACrD;AAGA,UAAM,cAAc,MAAM,QAAQ,kBAAkB,OAAO,SAAS,OAAO,OAAO;AAClF,UAAM,qBAAqB,yBAAyB,WAAW;AAE/D,QAAI,mBAAmB,WAAW,GAAG;AACnC,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAEA,UAAM,gBAAgB,mBAAmB,KAAK,MAAM,KAAK,OAAO,IAAI,mBAAmB,MAAM,CAAC;AAC9F,UAAM,iBAAiB,MAAM,mBAAmB,aAAa;AAE7D,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAEA,WAAO,qBAAqB,eAAe,cAAc;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,QAAQ,SAAS,WAAW,WAAW,SAAS,QAAQ;AAC3E,UAAM,EAAE,YAAY,QAAQ,IAAI,IAAI;AACpC,UAAM,WAAW,gBAAgB,OAAO;AAExC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAGA,UAAM,OAAO,MAAM,WAAW,UAAU,SAAS,WAAW,WAAW,OAAO;AAC9E,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,KAAK,KAAK;AAAA,IAC5B;AAGA,UAAM,gBAAgB,mBAAmB,UAAU;AACnD,QAAI,CAAC,cAAc,OAAO;AACxB,YAAM,IAAI,MAAM,cAAc,SAAS,qBAAqB;AAAA,IAC9D;AAEA,UAAM,SAAS,gBAAgB,UAAU;AACzC,QAAI,CAAC,UAAU,CAAC,OAAO,UAAU;AAC/B,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AAEA,QAAI,OAAO,aAAa,UAAU;AAChC,YAAM,IAAI,MAAM,wDAAwD;AAAA,IAC1E;AAGA,QAAI,CAAC,UAAU,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAG;AAC5D,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAEA,QAAI,OAAO,SAAS,OAAO,qBAAqB;AAC9C,YAAM,IAAI;AAAA,QACR,wBAAwB,OAAO,mBAAmB;AAAA,MACpD;AAAA,IACF;AAGA,WAAO,QAAQ,CAAC,OAAO,UAAU;AAC/B,UAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,cAAM,IAAI,MAAM,0BAA0B,KAAK,qBAAqB;AAAA,MACtE;AACA,UAAI,CAAC,MAAM,OAAO,OAAO,MAAM,QAAQ,UAAU;AAC/C,cAAM,IAAI,MAAM,0BAA0B,KAAK,0BAA0B;AAAA,MAC3E;AACA,UAAI,CAAC,MAAM,IAAI,KAAK,GAAG;AACrB,cAAM,IAAI,MAAM,0BAA0B,KAAK,uBAAuB;AAAA,MACxE;AAAA,IACF,CAAC;AAGD,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,WACJ,QAAQ,SACJ,KAAK;AAAA,MACH,KAAK,IAAI,KAAK,OAAO,WAAW;AAAA,MAChC,OAAO;AAAA,IACT,IACA,OAAO;AACb,UAAM,YAAY,MAAM;AAGxB,UAAM,gBAAgB,OAAO,IAAI,YAAU;AAAA,MACzC;AAAA,MACA;AAAA,MACA,KAAK,MAAM;AAAA,MACX;AAAA,IACF,EAAE;AAEF,UAAM,SAAS,MAAM,QAAQ,cAAc;AAAA,MACzC;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AAED,WAAO;AAAA,MACL,WAAW,OAAO,QAAQ;AAAA,MAC1B,UAAU,OAAO,QAAQ;AAAA,MACzB,YAAY,OAAO,QAAQ;AAAA,MAC3B,QAAQ,OAAO,OAAO,IAAI,YAAU;AAAA,QAClC,SAAS,MAAM;AAAA,QACf,KAAK,MAAM;AAAA,QACX,WAAW,MAAM;AAAA,QACjB,WAAW,MAAM;AAAA,MACnB,EAAE;AAAA,MACF,WAAW,OAAO,QAAQ;AAAA,MAC1B,WAAW,OAAO,QAAQ;AAAA,IAC5B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,QAAQ,SAAS,WAAW,WAAW,SAAS,QAAQ;AAC1E,UAAM,EAAE,WAAW,IAAI;AACvB,UAAM,WAAW,gBAAgB,OAAO;AAExC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAGA,UAAM,OAAO,MAAM,WAAW,UAAU,SAAS,WAAW,WAAW,OAAO;AAC9E,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,KAAK,KAAK;AAAA,IAC5B;AAEA,UAAM,SAAS,gBAAgB,UAAU;AACzC,QAAI,CAAC,UAAU,CAAC,OAAO,UAAU;AAC/B,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AAEA,UAAM,UAAU,MAAM,QAAQ,gBAAgB,UAAU;AACxD,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAEA,UAAM,UAAU,MAAM,QAAQ,cAAc,QAAQ,IAAI,QAAQ;AAChE,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AAEA,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,QAAQ,SAAS,WAAW,WAAW,SAAS,QAAQ;AACxE,UAAM,EAAE,YAAY,SAAS,IAAI,IAAI;AACrC,UAAM,WAAW,gBAAgB,OAAO;AAExC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAGA,UAAM,OAAO,MAAM,WAAW,UAAU,SAAS,WAAW,WAAW,OAAO;AAC9E,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,KAAK,KAAK;AAAA,IAC5B;AAEA,QAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,IAAI,WAAW,GAAG;AACvD,YAAM,IAAI,MAAM,aAAa;AAAA,IAC/B;AAEA,QAAI,IAAI,SAAS,KAAK,MAAM;AAC1B,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,QAAQ,MAAM,QAAQ,aAAa,OAAO;AAChD,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AAEA,QAAI,MAAM,kBAAkB;AAC1B,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,UAAM,QAAQ,YAAY,SAAS,UAAU,GAAG;AAEhD,WAAO,EAAE,SAAS,MAAM,QAAQ;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,QAAQ,SAAS,WAAW,WAAW,SAAS,QAAQ;AAC3E,UAAM,EAAE,YAAY,QAAQ,IAAI;AAChC,UAAM,WAAW,gBAAgB,OAAO;AAExC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAGA,UAAM,OAAO,MAAM,WAAW,UAAU,SAAS,WAAW,WAAW,OAAO;AAC9E,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,KAAK,KAAK;AAAA,IAC5B;AAEA,UAAM,QAAQ,MAAM,QAAQ,aAAa,OAAO;AAChD,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AAEA,QAAI,MAAM,aAAa,UAAU;AAC/B,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAEA,QAAI,CAAC,MAAM,oBAAoB,CAAC,MAAM,WAAW;AAC/C,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,WAAO;AAAA,MACL,KAAK,MAAM;AAAA,MACX,SAAS,MAAM;AAAA,MACf,YAAY,MAAM;AAAA,MAClB,YAAY,MAAM;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAK,QAAQ,SAAS,WAAW,WAAW,SAAS,QAAQ;AACjE,UAAM,EAAE,MAAM,IAAI;AAClB,UAAM,WAAW,gBAAgB,OAAO;AAExC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAGA,UAAM,OAAO,MAAM,WAAW,UAAU,SAAS,WAAW,WAAW,OAAO;AAC9E,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,KAAK,KAAK;AAAA,IAC5B;AAEA,UAAM,iBAAiB,SAAS;AAGhC,UAAM,iBAAiB,MAAM,QAAQ,kBAAkB,QAAQ;AAC/D,UAAM,kBAAkB,eAAe;AAAA,MACrC,CAAC,UAAU,MAAM,cAAc,MAAM,aAAa;AAAA,IACpD;AAGA,UAAM,YAAY,MAAM,QAAQ,oBAAoB,QAAQ;AAG5D,UAAM,uBAA8C,CAAC;AAErD,eAAW,SAAS,WAAW;AAC7B,YAAM,oBAAoB,MAAM,QAAQ;AAAA,QACtC,MAAM;AAAA,QACN;AAAA,QACA;AAAA,MACF;AACA,YAAM,qBAAqB,MAAM,QAAQ;AAAA,QACvC,MAAM;AAAA,QACN;AAAA,QACA;AAAA,MACF;AAEA,YAAM,gBAAgB;AAAA,QACpB,GAAG,kBAAkB,IAAI,CAAC,OAAY;AAAA,UACpC,GAAG;AAAA,UACH,MAAM;AAAA,QACR,EAAE;AAAA,QACF,GAAG,mBAAmB,IAAI,CAAC,OAAY;AAAA,UACrC,GAAG;AAAA,UACH,MAAM;AAAA,QACR,EAAE;AAAA,MACJ;AAEA,UAAI,cAAc,SAAS,GAAG;AAC5B,cAAM,YAAY,MAAM,aAAa;AACrC,cAAM,WAAW,cAAc;AAAA,UAAO,CAAC,MACrC,YAAY,EAAE,SAAS,aAAa,EAAE,SAAS;AAAA,QACjD;AAEA,YAAI,SAAS,SAAS,GAAG;AACvB,+BAAqB,MAAM,EAAE,IAAI;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS,gBAAgB,IAAI,CAAC,WAAW;AAAA,QACvC,SAAS,MAAM;AAAA,QACf,WAAW,MAAM;AAAA,QACjB,YAAY,MAAM;AAAA,QAClB,KAAK,MAAM;AAAA,QACX,YAAY,MAAM;AAAA,MACpB,EAAE;AAAA,MACF,eAAe;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,QAAQ,SAAS,WAAW,WAAW,SAAS,QAAQ;AAC7E,UAAM,EAAE,YAAY,SAAS,WAAW,IAAI;AAC5C,UAAM,WAAW,gBAAgB,OAAO;AAExC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAGA,UAAM,OAAO,MAAM,WAAW,UAAU,SAAS,WAAW,WAAW,OAAO;AAC9E,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,KAAK,KAAK;AAAA,IAC5B;AAEA,QAAI,CAAC,MAAM,QAAQ,UAAU,KAAK,WAAW,WAAW,GAAG;AACzD,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AAGA,eAAW,QAAQ,CAAC,WAAW,UAAU;AACvC,UAAI,CAAC,aAAa,OAAO,cAAc,UAAU;AAC/C,cAAM,IAAI,MAAM,8BAA8B,KAAK,qBAAqB;AAAA,MAC1E;AAAA,IACF,CAAC;AAED,UAAM,QAAQ,MAAM,QAAQ,aAAa,OAAO;AAChD,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AAEA,UAAM,OAAO,MAAM,aAAa,WAAW,YAAY;AACvD,UAAM,QAAQ,MAAM,QAAQ;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,WAAO,EAAE,OAAO,QAAQ;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,QAAQ,SAAS,WAAW,WAAW,SAAS,QAAQ;AAC7E,UAAM,EAAE,YAAY,SAAS,MAAM,IAAI;AACvC,UAAM,WAAW,gBAAgB,OAAO;AAExC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAGA,UAAM,OAAO,MAAM,WAAW,UAAU,SAAS,WAAW,WAAW,OAAO;AAC9E,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,KAAK,KAAK;AAAA,IAC5B;AAEA,UAAM,iBAAiB,SAAS;AAEhC,UAAM,QAAQ,MAAM,QAAQ,aAAa,OAAO;AAChD,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AAEA,UAAM,YAAY,MAAM,aAAa;AACrC,UAAM,OAAO,YAAY,aAAa;AAEtC,UAAM,aAAa,MAAM,QAAQ;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,WAAO;AAAA,MACL,YAAY,WAAW,IAAI,CAAC,OAAY;AAAA,QACtC,WAAW,EAAE;AAAA,QACb,WAAW,EAAE;AAAA,MACf,EAAE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAKA,eAAsB,UACpB,UACA,SACA,QACwB;AACxB,QAAM,YAA2B,CAAC;AAElC,aAAW,WAAW,UAAU;AAC9B,QAAI;AACF,YAAM,EAAE,QAAQ,SAAS,WAAW,WAAW,OAAO,IAAI;AAG1D,UAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,kBAAU,KAAK;AAAA,UACb,SAAS;AAAA,UACT,OAAO;AAAA,QACT,CAAC;AACD;AAAA,MACF;AAEA,UAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,kBAAU,KAAK;AAAA,UACb,SAAS;AAAA,UACT,OAAO;AAAA,QACT,CAAC;AACD;AAAA,MACF;AAEA,UAAI,CAAC,aAAa,OAAO,cAAc,UAAU;AAC/C,kBAAU,KAAK;AAAA,UACb,SAAS;AAAA,UACT,OAAO;AAAA,QACT,CAAC;AACD;AAAA,MACF;AAGA,YAAM,UAAU,SAAS,MAAM;AAC/B,UAAI,CAAC,SAAS;AACZ,kBAAU,KAAK;AAAA,UACb,SAAS;AAAA,UACT,OAAO,mBAAmB,MAAM;AAAA,QAClC,CAAC;AACD;AAAA,MACF;AAGA,YAAM,SAAS,MAAM;AAAA,QACnB,UAAU,CAAC;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,gBAAU,KAAK;AAAA,QACb,SAAS;AAAA,QACT;AAAA,MACF,CAAC;AAAA,IACH,SAASC,MAAK;AACZ,gBAAU,KAAK;AAAA,QACb,SAAS;AAAA,QACT,OAAQA,KAAc,WAAW;AAAA,MACnC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;;;AH7sBA,IAAM,iBAAiB;AAKhB,SAAS,UAAU,SAAkB,QAAgB;AAC1D,QAAM,MAAM,IAAI,iBAAK;AAGrB,MAAI,IAAI,UAAM,kBAAK;AAAA,IACjB,QAAQ,CAAC,WAAW;AAClB,UAAI,OAAO,YAAY,WAAW,KAAK,OAAO,YAAY,CAAC,MAAM,KAAK;AACpE,eAAO;AAAA,MACT;AACA,UAAI,OAAO,YAAY,SAAS,MAAM,GAAG;AACvC,eAAO;AAAA,MACT;AACA,aAAO,OAAO,YAAY,CAAC;AAAA,IAC7B;AAAA,IACA,cAAc,CAAC,OAAO,QAAQ,SAAS;AAAA,IACvC,cAAc,CAAC,gBAAgB,QAAQ;AAAA,IACvC,eAAe,CAAC,cAAc;AAAA,IAC9B,aAAa;AAAA,IACb,QAAQ;AAAA,EACV,CAAC,CAAC;AAGF,MAAI,IAAI,KAAK,CAAC,MAAM;AAClB,WAAO,EAAE,KAAK;AAAA,MACZ,SAAS,OAAO;AAAA,MAChB,MAAM;AAAA,MACN,aAAa;AAAA,IACf,GAAG,GAAG;AAAA,EACR,CAAC;AAGD,MAAI,IAAI,WAAW,CAAC,MAAM;AACxB,WAAO,EAAE,KAAK;AAAA,MACZ,QAAQ;AAAA,MACR,WAAW,KAAK,IAAI;AAAA,MACpB,SAAS,OAAO;AAAA,IAClB,GAAG,GAAG;AAAA,EACR,CAAC;AAMD,MAAI,KAAK,QAAQ,OAAO,MAAM;AAC5B,QAAI;AACF,YAAM,OAAO,MAAM,EAAE,IAAI,KAAK;AAG9B,YAAM,WAAyB,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;AAGjE,UAAI,SAAS,WAAW,GAAG;AACzB,eAAO,EAAE,KAAK,EAAE,OAAO,sBAAsB,GAAG,GAAG;AAAA,MACrD;AAEA,UAAI,SAAS,SAAS,gBAAgB;AACpC,eAAO,EAAE,KAAK,EAAE,OAAO,mCAAmC,cAAc,IAAI,GAAG,GAAG;AAAA,MACpF;AAGA,YAAM,YAAY,MAAM,UAAU,UAAU,SAAS,MAAM;AAG3D,aAAO,EAAE,KAAK,MAAM,QAAQ,IAAI,IAAI,YAAY,UAAU,CAAC,GAAG,GAAG;AAAA,IACnE,SAASC,MAAK;AACZ,cAAQ,MAAM,cAAcA,IAAG;AAC/B,aAAO,EAAE,KAAK;AAAA,QACZ,SAAS;AAAA,QACT,OAAO;AAAA,MACT,GAAG,GAAG;AAAA,IACR;AAAA,EACF,CAAC;AAGD,MAAI,IAAI,KAAK,CAAC,MAAM;AAClB,WAAO,EAAE,KAAK;AAAA,MACZ,OAAO;AAAA,IACT,GAAG,GAAG;AAAA,EACR,CAAC;AAED,SAAO;AACT;;;AIzEO,SAAS,aAAqB;AACnC,SAAO;AAAA,IACL,MAAM,SAAS,QAAQ,IAAI,QAAQ,QAAQ,EAAE;AAAA,IAC7C,aAAc,QAAQ,IAAI,gBAAgB;AAAA,IAC1C,aAAa,QAAQ,IAAI,gBAAgB;AAAA,IACzC,aAAa,QAAQ,IAAI,eACrB,QAAQ,IAAI,aAAa,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,IACrD,CAAC,GAAG;AAAA,IACR,SAAS,QAAQ,IAAI,WAAW;AAAA,IAChC,iBAAiB,SAAS,QAAQ,IAAI,qBAAqB,SAAS,EAAE;AAAA,IACtE,aAAa,SAAS,QAAQ,IAAI,iBAAiB,YAAY,EAAE;AAAA,IACjE,aAAa,SAAS,QAAQ,IAAI,iBAAiB,SAAS,EAAE;AAAA,IAC9D,iBAAiB,SAAS,QAAQ,IAAI,oBAAoB,SAAS,EAAE;AAAA,IACrE,qBAAqB,SAAS,QAAQ,IAAI,0BAA0B,OAAO,EAAE;AAAA,EAC/E;AACF;;;ACnCA,4BAAqB;AACrB,yBAA2B;;;ACQ3B,eAAsB,kBAAkB,KAA8B;AAGpE,QAAM,iBAAiB;AAAA,IACrB;AAAA,EACF;AAGA,QAAM,aAAa,KAAK,UAAU,cAAc;AAGhD,QAAM,UAAU,IAAI,YAAY;AAChC,QAAM,OAAO,QAAQ,OAAO,UAAU;AAGtC,QAAM,aAAa,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI;AAG7D,QAAM,YAAY,MAAM,KAAK,IAAI,WAAW,UAAU,CAAC;AACvD,QAAM,UAAU,UAAU,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAE3E,SAAO;AACT;;;ADhBA,IAAM,aAAa,MAAM,KAAK,KAAK,KAAK;AAMjC,IAAM,gBAAN,MAAuC;AAAA;AAAA;AAAA;AAAA;AAAA,EAO5C,YAAY,OAAe,YAAY;AACrC,SAAK,KAAK,IAAI,sBAAAC,QAAS,IAAI;AAC3B,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAA2B;AACjC,SAAK,GAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAoEZ;AAGD,SAAK,GAAG,OAAO,mBAAmB;AAAA,EACpC;AAAA;AAAA,EAIA,MAAM,aAAa,QAAgD;AACjE,UAAM,UAAmB,CAAC;AAG1B,UAAM,gBAAgB,MAAM,QAAQ;AAAA,MAClC,OAAO,IAAI,OAAO,WAAW;AAAA,QAC3B,GAAG;AAAA,QACH,IAAI,MAAM,MAAM,MAAM,kBAAkB,MAAM,GAAG;AAAA,MACnD,EAAE;AAAA,IACJ;AAGA,UAAM,cAAc,KAAK,GAAG,YAAY,CAACC,mBAA2D;AAClG,YAAM,YAAY,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,OAGjC;AAED,iBAAW,SAASA,gBAAe;AACjC,cAAM,MAAM,KAAK,IAAI;AAGrB,kBAAU;AAAA,UACR,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM,aAAa;AAAA,UACnB,MAAM;AAAA,UACN;AAAA,UACA,MAAM;AAAA,UACN;AAAA,QACF;AAEA,gBAAQ,KAAK;AAAA,UACX,IAAI,MAAM;AAAA,UACV,UAAU,MAAM;AAAA,UAChB,WAAW,MAAM,aAAa;AAAA,UAC9B,YAAY,MAAM;AAAA,UAClB,KAAK,MAAM;AAAA,UACX,WAAW;AAAA,UACX,WAAW,MAAM;AAAA,UACjB,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,gBAAY,aAAa;AACzB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,oBAAoB,UAAoC;AAC5D,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI5B;AAED,UAAM,OAAO,KAAK,IAAI,UAAU,KAAK,IAAI,CAAC;AAC1C,WAAO,KAAK,IAAI,SAAO,KAAK,WAAW,GAAG,CAAC;AAAA,EAC7C;AAAA,EAEA,MAAM,aAAa,SAAwC;AACzD,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AAED,UAAM,MAAM,KAAK,IAAI,SAAS,KAAK,IAAI,CAAC;AAExC,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,WAAW,GAAG;AAAA,EAC5B;AAAA,EAEA,MAAM,YAAY,SAAiB,eAAyC;AAC1E,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AAED,UAAM,SAAS,KAAK,IAAI,SAAS,aAAa;AAC9C,WAAO,OAAO,UAAU;AAAA,EAC1B;AAAA,EAEA,MAAM,oBAAoB,KAA8B;AACtD,UAAM,OAAO,KAAK,GAAG,QAAQ,yCAAyC;AACtE,UAAM,SAAS,KAAK,IAAI,GAAG;AAC3B,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,MAAM,YACJ,SACA,kBACA,WAC+C;AAE/C,UAAM,QAAQ,MAAM,KAAK,aAAa,OAAO;AAE7C,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,MAAM,kBAAkB;AAC1B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAGA,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI5B;AAED,UAAM,SAAS,KAAK,IAAI,kBAAkB,WAAW,KAAK,IAAI,GAAG,OAAO;AAExE,QAAI,OAAO,YAAY,GAAG;AACxB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAAA,EAEA,MAAM,kBAAkB,iBAA2C;AACjE,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI5B;AAED,UAAM,OAAO,KAAK,IAAI,iBAAiB,KAAK,IAAI,CAAC;AACjD,WAAO,KAAK,IAAI,SAAO,KAAK,WAAW,GAAG,CAAC;AAAA,EAC7C;AAAA;AAAA,EAIA,MAAM,iBACJ,SACA,UACA,MACA,YACiB;AACjB,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AAED,UAAM,gBAAgB,KAAK,IAAI;AAC/B,UAAM,cAAc,KAAK,GAAG,YAAY,CAACC,gBAAsB;AAC7D,eAAS,IAAI,GAAG,IAAIA,YAAW,QAAQ,KAAK;AAC1C,aAAK;AAAA,UACH;AAAA,UACA;AAAA,UACA;AAAA,UACA,KAAK,UAAUA,YAAW,CAAC,CAAC;AAAA,UAC5B,gBAAgB;AAAA,QAClB;AAAA,MACF;AAAA,IACF,CAAC;AAED,gBAAY,UAAU;AACtB,WAAO,WAAW;AAAA,EACpB;AAAA,EAEA,MAAM,iBACJ,SACA,YACA,OACyB;AACzB,QAAI,QAAQ;AAAA;AAAA;AAAA;AAKZ,UAAM,SAAgB,CAAC,SAAS,UAAU;AAE1C,QAAI,UAAU,QAAW;AACvB,eAAS;AACT,aAAO,KAAK,KAAK;AAAA,IACnB;AAEA,aAAS;AAET,UAAM,OAAO,KAAK,GAAG,QAAQ,KAAK;AAClC,UAAM,OAAO,KAAK,IAAI,GAAG,MAAM;AAE/B,WAAO,KAAK,IAAI,UAAQ;AAAA,MACtB,IAAI,IAAI;AAAA,MACR,SAAS,IAAI;AAAA,MACb,UAAU,IAAI;AAAA,MACd,MAAM,IAAI;AAAA,MACV,WAAW,KAAK,MAAM,IAAI,SAAS;AAAA,MACnC,WAAW,IAAI;AAAA,IACjB,EAAE;AAAA,EACJ;AAAA;AAAA,EAIA,MAAM,cAAc,SAAkD;AACpE,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,YAAY,MAAM;AAGxB,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAO5B;AAED,UAAM,SAAS,KAAK;AAAA,MAClB,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IACV;AAEA,QAAI,OAAO,YAAY,GAAG;AACxB,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AAEA,WAAO;AAAA,MACL,UAAU,QAAQ;AAAA,MAClB,WAAW,QAAQ;AAAA,MACnB,WAAW;AAAA,MACX;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,UAA4C;AAC5D,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AAED,UAAM,MAAM,KAAK,IAAI,UAAU,KAAK,IAAI,CAAC;AAEzC,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,UAAU,IAAI;AAAA,MACd,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,UAAU,IAAI;AAAA,MACd,UAAU,IAAI,YAAY;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,UAAoC;AACtD,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,YAAY,MAAM;AAExB,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI5B;AAED,UAAM,SAAS,KAAK,IAAI,KAAK,WAAW,UAAU,GAAG;AACrD,WAAO,OAAO,UAAU;AAAA,EAC1B;AAAA,EAEA,MAAM,uBAAuB,KAA8B;AACzD,UAAM,OAAO,KAAK,GAAG,QAAQ,4CAA4C;AACzE,UAAM,SAAS,KAAK,IAAI,GAAG;AAC3B,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA,EAIA,MAAM,cAAc,SAGjB;AACD,UAAM,gBAAY,+BAAW;AAC7B,UAAM,MAAM,KAAK,IAAI;AAGrB,UAAM,SAAS,gBAAgB,QAAQ,UAAU;AACjD,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,wBAAwB,QAAQ,UAAU,EAAE;AAAA,IAC9D;AACA,QAAI,CAAC,OAAO,UAAU;AACpB,YAAM,IAAI,MAAM,sCAAsC,QAAQ,UAAU,EAAE;AAAA,IAC5E;AAEA,UAAM,EAAE,aAAa,SAAS,SAAS,IAAI;AAE3C,UAAM,cAAc,KAAK,GAAG,YAAY,MAAM;AAE5C,YAAM,kBAAkB,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,OAGvC,EAAE,IAAI,aAAa,SAAS,QAAQ;AAErC,UAAI,iBAAiB;AAEnB,aAAK,GAAG,QAAQ;AAAA;AAAA,SAEf,EAAE,IAAI,gBAAgB,EAAE;AAGzB,aAAK,GAAG,QAAQ;AAAA;AAAA,SAEf,EAAE,IAAI,gBAAgB,EAAE;AAAA,MAC3B;AAGA,WAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,OAGf,EAAE;AAAA,QACD;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV;AAGA,YAAM,YAAY,MAAM;AACxB,WAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,OAIf,EAAE,IAAI,KAAK,WAAW,UAAU,GAAG;AAAA,IACtC,CAAC;AAED,gBAAY;AAGZ,UAAM,gBAAgB,QAAQ,OAAO,IAAI,YAAU;AAAA,MACjD,GAAG;AAAA,MACH;AAAA,IACF,EAAE;AACF,UAAM,SAAS,MAAM,KAAK,aAAa,aAAa;AAEpD,WAAO;AAAA,MACL,SAAS;AAAA,QACP,IAAI;AAAA,QACJ,YAAY,QAAQ;AAAA,QACpB;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX,WAAW,QAAQ;AAAA,MACrB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,oBAAoB,WAAqC;AAC7D,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAI5B;AAED,UAAM,OAAO,KAAK,IAAI,WAAW,KAAK,IAAI,CAAC;AAC3C,WAAO,KAAK,IAAI,SAAO,KAAK,WAAW,GAAG,CAAC;AAAA,EAC7C;AAAA,EAEA,MAAM,eAAe,WAA4C;AAC/D,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AAED,UAAM,MAAM,KAAK,IAAI,WAAW,KAAK,IAAI,CAAC;AAE1C,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,aAAa,GAAG;AAAA,EAC9B;AAAA,EAEA,MAAM,gBAAgB,YAA6C;AACjE,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AAED,UAAM,MAAM,KAAK,IAAI,YAAY,KAAK,IAAI,CAAC;AAE3C,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,aAAa,GAAG;AAAA,EAC9B;AAAA,EAEA,MAAM,iBACJ,aACA,SACA,OACA,QACoB;AAGpB,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAU5B;AAED,UAAM,OAAO,KAAK,IAAI,aAAa,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,OAAO,MAAM;AACjF,WAAO,KAAK,IAAI,SAAO,KAAK,aAAa,GAAG,CAAC;AAAA,EAC/C;AAAA,EAEA,MAAM,iBAAiB,aAAqB,SAA0C;AAEpF,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAU5B;AAED,UAAM,MAAM,KAAK,IAAI,aAAa,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,CAAC;AAEjE,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,aAAa,GAAG;AAAA,EAC9B;AAAA,EAEA,MAAM,cAAc,WAAmB,UAAoC;AACzE,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,KAG5B;AAED,UAAM,SAAS,KAAK,IAAI,WAAW,QAAQ;AAC3C,WAAO,OAAO,UAAU;AAAA,EAC1B;AAAA,EAEA,MAAM,sBAAsB,KAA8B;AACxD,UAAM,OAAO,KAAK,GAAG,QAAQ,2CAA2C;AACxE,UAAM,SAAS,KAAK,IAAI,GAAG;AAC3B,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,GAAG,MAAM;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,WAAW,KAAiB;AAClC,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,UAAU,IAAI;AAAA,MACd,WAAW,IAAI,cAAc;AAAA,MAC7B,YAAY,IAAI,eAAe;AAAA,MAC/B,KAAK,IAAI;AAAA,MACT,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,UAAU,IAAI;AAAA,MACd,kBAAkB,IAAI,qBAAqB;AAAA,MAC3C,WAAW,IAAI,cAAc;AAAA,MAC7B,YAAY,IAAI,eAAe;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,KAAmB;AACtC,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,YAAY,IAAI;AAAA,MAChB,aAAa,IAAI;AAAA,MACjB,SAAS,IAAI;AAAA,MACb,UAAU,IAAI;AAAA,MACd,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AACF;;;AN5mBA,eAAe,OAAO;AACpB,QAAM,SAAS,WAAW;AAE1B,UAAQ,IAAI,4BAA4B;AACxC,UAAQ,IAAI,kBAAkB;AAAA,IAC5B,MAAM,OAAO;AAAA,IACb,aAAa,OAAO;AAAA,IACpB,aAAa,OAAO;AAAA,IACpB,iBAAiB,GAAG,OAAO,eAAe;AAAA,IAC1C,aAAa,GAAG,OAAO,WAAW;AAAA,IAClC,aAAa,GAAG,OAAO,WAAW;AAAA,IAClC,iBAAiB,GAAG,OAAO,eAAe;AAAA,IAC1C,qBAAqB,OAAO;AAAA,IAC5B,aAAa,OAAO;AAAA,IACpB,SAAS,OAAO;AAAA,EAClB,CAAC;AAED,MAAI;AAEJ,MAAI,OAAO,gBAAgB,UAAU;AACnC,cAAU,IAAI,cAAc,OAAO,WAAW;AAC9C,YAAQ,IAAI,sBAAsB;AAAA,EACpC,OAAO;AACL,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AAGA,QAAM,kBAAkB,YAAY,YAAY;AAC9C,QAAI;AACF,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,UAAU,MAAM,QAAQ,oBAAoB,GAAG;AACrD,UAAI,UAAU,GAAG;AACf,gBAAQ,IAAI,oBAAoB,OAAO,mBAAmB;AAAA,MAC5D;AAAA,IACF,SAASC,MAAK;AACZ,cAAQ,MAAM,kBAAkBA,IAAG;AAAA,IACrC;AAAA,EACF,GAAG,OAAO,eAAe;AAEzB,QAAM,MAAM,UAAU,SAAS,MAAM;AAErC,QAAM,aAAS,0BAAM;AAAA,IACnB,OAAO,IAAI;AAAA,IACX,MAAM,OAAO;AAAA,EACf,CAAC;AAED,UAAQ,IAAI,sCAAsC,OAAO,IAAI,EAAE;AAC/D,UAAQ,IAAI,6BAA6B;AAGzC,QAAM,WAAW,YAAY;AAC3B,YAAQ,IAAI,+BAA+B;AAC3C,kBAAc,eAAe;AAC7B,UAAM,QAAQ,MAAM;AACpB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAChC;AAEA,KAAK,EAAE,MAAM,CAACA,SAAQ;AACpB,UAAQ,MAAM,gBAAgBA,IAAG;AACjC,UAAQ,KAAK,CAAC;AAChB,CAAC;",
6
6
  "names": ["C", "G", "err", "allServices", "compatibleServices", "availableOffer", "err", "err", "Database", "offersWithIds", "candidates", "err"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtr-dev/rondevu-server",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "DNS-like WebRTC signaling server with username claiming and service discovery",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
package/src/crypto.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import * as ed25519 from '@noble/ed25519';
8
+ import { Buffer } from 'node:buffer';
8
9
 
9
10
  // Set SHA-512 hash function for ed25519 (required in @noble/ed25519 v3+)
10
11
  // Uses Web Crypto API (compatible with both Node.js and Cloudflare Workers)
@@ -34,20 +35,18 @@ export function generateAnonymousUsername(): string {
34
35
 
35
36
  /**
36
37
  * Convert Uint8Array to base64 string
38
+ * Uses Buffer for compatibility with Node.js-based clients
37
39
  */
38
40
  function bytesToBase64(bytes: Uint8Array): string {
39
- const binString = Array.from(bytes, (byte) =>
40
- String.fromCodePoint(byte)
41
- ).join('');
42
- return btoa(binString);
41
+ return Buffer.from(bytes).toString('base64');
43
42
  }
44
43
 
45
44
  /**
46
45
  * Convert base64 string to Uint8Array
46
+ * Uses Buffer for compatibility with Node.js-based clients
47
47
  */
48
48
  function base64ToBytes(base64: string): Uint8Array {
49
- const binString = atob(base64);
50
- return Uint8Array.from(binString, (char) => char.codePointAt(0)!);
49
+ return new Uint8Array(Buffer.from(base64, 'base64'));
51
50
  }
52
51
 
53
52
  /**
package/wrangler.toml CHANGED
@@ -17,7 +17,7 @@ OFFER_MIN_TTL = "60000" # Min offer TTL: 1 minute
17
17
  MAX_OFFERS_PER_REQUEST = "100" # Max offers per request
18
18
  MAX_TOPICS_PER_OFFER = "50" # Max topics per offer
19
19
  CORS_ORIGINS = "*" # Comma-separated list of allowed origins
20
- VERSION = "0.4.0" # Semantic version
20
+ VERSION = "0.5.1" # Semantic version
21
21
 
22
22
  # AUTH_SECRET should be set as a secret, not a var
23
23
  # Run: npx wrangler secret put AUTH_SECRET