@hadoku/task-ui-components 4.22.7 → 4.23.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/components/ConnectedSettings.d.ts.map +1 -1
- package/dist/components/ConnectedSettings.js +24 -2
- package/dist/components/ConnectedSettings.js.map +1 -1
- package/dist/components/ServiceAccess.d.ts +3 -0
- package/dist/components/ServiceAccess.d.ts.map +1 -0
- package/dist/components/ServiceAccess.js +216 -0
- package/dist/components/ServiceAccess.js.map +1 -0
- package/dist/lib/settingsClient.d.ts +14 -0
- package/dist/lib/settingsClient.d.ts.map +1 -1
- package/dist/lib/settingsClient.js +8 -2
- package/dist/lib/settingsClient.js.map +1 -1
- package/dist/vendor/uqr/index.js +688 -0
- package/dist/vendor/uqr/index.js.map +1 -0
- package/dist/vendor/wolffm/prefs-client/avatar.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ConnectedSettings.tsx +36 -1
- package/src/components/ServiceAccess.tsx +279 -0
- package/src/lib/settingsClient.ts +28 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../node_modules/.pnpm/uqr@0.1.3/node_modules/uqr/dist/index.mjs"],"sourcesContent":["var QrCodeDataType = /* @__PURE__ */ ((QrCodeDataType2) => {\n QrCodeDataType2[QrCodeDataType2[\"Border\"] = -1] = \"Border\";\n QrCodeDataType2[QrCodeDataType2[\"Data\"] = 0] = \"Data\";\n QrCodeDataType2[QrCodeDataType2[\"Function\"] = 1] = \"Function\";\n QrCodeDataType2[QrCodeDataType2[\"Position\"] = 2] = \"Position\";\n QrCodeDataType2[QrCodeDataType2[\"Timing\"] = 3] = \"Timing\";\n QrCodeDataType2[QrCodeDataType2[\"Alignment\"] = 4] = \"Alignment\";\n return QrCodeDataType2;\n})(QrCodeDataType || {});\n\nconst LOW = [0, 1];\nconst MEDIUM = [1, 0];\nconst QUARTILE = [2, 3];\nconst HIGH = [3, 2];\nconst EccMap = {\n L: LOW,\n M: MEDIUM,\n Q: QUARTILE,\n H: HIGH\n};\nconst NUMERIC_REGEX = /^\\d*$/;\nconst ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+./:-]*$/;\nconst ALPHANUMERIC_CHARSET = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:\";\nconst MIN_VERSION = 1;\nconst MAX_VERSION = 40;\nconst PENALTY_N1 = 3;\nconst PENALTY_N2 = 3;\nconst PENALTY_N3 = 40;\nconst PENALTY_N4 = 10;\nconst ECC_CODEWORDS_PER_BLOCK = [\n // Version: (note that index 0 is for padding, and is set to an illegal value)\n // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level\n [-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],\n // Low\n [-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28],\n // Medium\n [-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],\n // Quartile\n [-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30]\n // High\n];\nconst NUM_ERROR_CORRECTION_BLOCKS = [\n // Version: (note that index 0 is for padding, and is set to an illegal value)\n // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level\n [-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25],\n // Low\n [-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49],\n // Medium\n [-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68],\n // Quartile\n [-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81]\n // High\n];\nclass QrCode {\n /* -- Constructor (low level) and fields -- */\n // Creates a new QR Code with the given version number,\n // error correction level, data codeword bytes, and mask number.\n // This is a low-level API that most users should not use directly.\n // A mid-level API is the encodeSegments() function.\n constructor(version, ecc, dataCodewords, msk) {\n this.version = version;\n this.ecc = ecc;\n if (version < MIN_VERSION || version > MAX_VERSION)\n throw new RangeError(\"Version value out of range\");\n if (msk < -1 || msk > 7)\n throw new RangeError(\"Mask value out of range\");\n this.size = version * 4 + 17;\n const row = Array.from({ length: this.size }).fill(false);\n for (let i = 0; i < this.size; i++) {\n this.modules.push(row.slice());\n this.types.push(row.map(() => 0));\n }\n this.drawFunctionPatterns();\n const allCodewords = this.addEccAndInterleave(dataCodewords);\n this.drawCodewords(allCodewords);\n if (msk === -1) {\n let minPenalty = 1e9;\n for (let i = 0; i < 8; i++) {\n this.applyMask(i);\n this.drawFormatBits(i);\n const penalty = this.getPenaltyScore();\n if (penalty < minPenalty) {\n msk = i;\n minPenalty = penalty;\n }\n this.applyMask(i);\n }\n }\n this.mask = msk;\n this.applyMask(msk);\n this.drawFormatBits(msk);\n }\n /* -- Fields -- */\n // The width and height of this QR Code, measured in modules, between\n // 21 and 177 (inclusive). This is equal to version * 4 + 17.\n size;\n // The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).\n // Even if a QR Code is created with automatic masking requested (mask = -1),\n // the resulting object still has a mask value between 0 and 7.\n mask;\n // The modules of this QR Code (false = light, true = dark).\n // Immutable after constructor finishes. Accessed through getModule().\n modules = [];\n types = [];\n /* -- Accessor methods -- */\n // Returns the color of the module (pixel) at the given coordinates, which is false\n // for light or true for dark. The top left corner has the coordinates (x=0, y=0).\n // If the given coordinates are out of bounds, then false (light) is returned.\n getModule(x, y) {\n return x >= 0 && x < this.size && y >= 0 && y < this.size && this.modules[y][x];\n }\n /* -- Private helper methods for constructor: Drawing function modules -- */\n // Reads this object's version field, and draws and marks all function modules.\n drawFunctionPatterns() {\n for (let i = 0; i < this.size; i++) {\n this.setFunctionModule(6, i, i % 2 === 0, QrCodeDataType.Timing);\n this.setFunctionModule(i, 6, i % 2 === 0, QrCodeDataType.Timing);\n }\n this.drawFinderPattern(3, 3);\n this.drawFinderPattern(this.size - 4, 3);\n this.drawFinderPattern(3, this.size - 4);\n const alignPatPos = this.getAlignmentPatternPositions();\n const numAlign = alignPatPos.length;\n for (let i = 0; i < numAlign; i++) {\n for (let j = 0; j < numAlign; j++) {\n if (!(i === 0 && j === 0 || i === 0 && j === numAlign - 1 || i === numAlign - 1 && j === 0))\n this.drawAlignmentPattern(alignPatPos[i], alignPatPos[j]);\n }\n }\n this.drawFormatBits(0);\n this.drawVersion();\n }\n // Draws two copies of the format bits (with its own error correction code)\n // based on the given mask and this object's error correction level field.\n drawFormatBits(mask) {\n const data = this.ecc[1] << 3 | mask;\n let rem = data;\n for (let i = 0; i < 10; i++)\n rem = rem << 1 ^ (rem >>> 9) * 1335;\n const bits = (data << 10 | rem) ^ 21522;\n for (let i = 0; i <= 5; i++)\n this.setFunctionModule(8, i, getBit(bits, i));\n this.setFunctionModule(8, 7, getBit(bits, 6));\n this.setFunctionModule(8, 8, getBit(bits, 7));\n this.setFunctionModule(7, 8, getBit(bits, 8));\n for (let i = 9; i < 15; i++)\n this.setFunctionModule(14 - i, 8, getBit(bits, i));\n for (let i = 0; i < 8; i++)\n this.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i));\n for (let i = 8; i < 15; i++)\n this.setFunctionModule(8, this.size - 15 + i, getBit(bits, i));\n this.setFunctionModule(8, this.size - 8, true);\n }\n // Draws two copies of the version bits (with its own error correction code),\n // based on this object's version field, iff 7 <= version <= 40.\n drawVersion() {\n if (this.version < 7)\n return;\n let rem = this.version;\n for (let i = 0; i < 12; i++)\n rem = rem << 1 ^ (rem >>> 11) * 7973;\n const bits = this.version << 12 | rem;\n for (let i = 0; i < 18; i++) {\n const color = getBit(bits, i);\n const a = this.size - 11 + i % 3;\n const b = Math.floor(i / 3);\n this.setFunctionModule(a, b, color);\n this.setFunctionModule(b, a, color);\n }\n }\n // Draws a 9*9 finder pattern including the border separator,\n // with the center module at (x, y). Modules can be out of bounds.\n drawFinderPattern(x, y) {\n for (let dy = -4; dy <= 4; dy++) {\n for (let dx = -4; dx <= 4; dx++) {\n const dist = Math.max(Math.abs(dx), Math.abs(dy));\n const xx = x + dx;\n const yy = y + dy;\n if (xx >= 0 && xx < this.size && yy >= 0 && yy < this.size)\n this.setFunctionModule(xx, yy, dist !== 2 && dist !== 4, QrCodeDataType.Position);\n }\n }\n }\n // Draws a 5*5 alignment pattern, with the center module\n // at (x, y). All modules must be in bounds.\n drawAlignmentPattern(x, y) {\n for (let dy = -2; dy <= 2; dy++) {\n for (let dx = -2; dx <= 2; dx++) {\n this.setFunctionModule(\n x + dx,\n y + dy,\n Math.max(Math.abs(dx), Math.abs(dy)) !== 1,\n QrCodeDataType.Alignment\n );\n }\n }\n }\n // Sets the color of a module and marks it as a function module.\n // Only used by the constructor. Coordinates must be in bounds.\n setFunctionModule(x, y, isDark, type = QrCodeDataType.Function) {\n this.modules[y][x] = isDark;\n this.types[y][x] = type;\n }\n /* -- Private helper methods for constructor: Codewords and masking -- */\n // Returns a new byte string representing the given data with the appropriate error correction\n // codewords appended to it, based on this object's version and error correction level.\n addEccAndInterleave(data) {\n const ver = this.version;\n const ecl = this.ecc;\n if (data.length !== getNumDataCodewords(ver, ecl))\n throw new RangeError(\"Invalid argument\");\n const numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecl[0]][ver];\n const blockEccLen = ECC_CODEWORDS_PER_BLOCK[ecl[0]][ver];\n const rawCodewords = Math.floor(getNumRawDataModules(ver) / 8);\n const numShortBlocks = numBlocks - rawCodewords % numBlocks;\n const shortBlockLen = Math.floor(rawCodewords / numBlocks);\n const blocks = [];\n const rsDiv = reedSolomonComputeDivisor(blockEccLen);\n for (let i = 0, k = 0; i < numBlocks; i++) {\n const dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1));\n k += dat.length;\n const ecc = reedSolomonComputeRemainder(dat, rsDiv);\n if (i < numShortBlocks)\n dat.push(0);\n blocks.push(dat.concat(ecc));\n }\n const result = [];\n for (let i = 0; i < blocks[0].length; i++) {\n blocks.forEach((block, j) => {\n if (i !== shortBlockLen - blockEccLen || j >= numShortBlocks)\n result.push(block[i]);\n });\n }\n return result;\n }\n // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire\n // data area of this QR Code. Function modules need to be marked off before this is called.\n drawCodewords(data) {\n if (data.length !== Math.floor(getNumRawDataModules(this.version) / 8))\n throw new RangeError(\"Invalid argument\");\n let i = 0;\n for (let right = this.size - 1; right >= 1; right -= 2) {\n if (right === 6)\n right = 5;\n for (let vert = 0; vert < this.size; vert++) {\n for (let j = 0; j < 2; j++) {\n const x = right - j;\n const upward = (right + 1 & 2) === 0;\n const y = upward ? this.size - 1 - vert : vert;\n if (!this.types[y][x] && i < data.length * 8) {\n this.modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7));\n i++;\n }\n }\n }\n }\n }\n // XORs the codeword modules in this QR Code with the given mask pattern.\n // The function modules must be marked and the codeword bits must be drawn\n // before masking. Due to the arithmetic of XOR, calling applyMask() with\n // the same mask value a second time will undo the mask. A final well-formed\n // QR Code needs exactly one (not zero, two, etc.) mask applied.\n applyMask(mask) {\n if (mask < 0 || mask > 7)\n throw new RangeError(\"Mask value out of range\");\n for (let y = 0; y < this.size; y++) {\n for (let x = 0; x < this.size; x++) {\n let invert;\n switch (mask) {\n case 0:\n invert = (x + y) % 2 === 0;\n break;\n case 1:\n invert = y % 2 === 0;\n break;\n case 2:\n invert = x % 3 === 0;\n break;\n case 3:\n invert = (x + y) % 3 === 0;\n break;\n case 4:\n invert = (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0;\n break;\n case 5:\n invert = x * y % 2 + x * y % 3 === 0;\n break;\n case 6:\n invert = (x * y % 2 + x * y % 3) % 2 === 0;\n break;\n case 7:\n invert = ((x + y) % 2 + x * y % 3) % 2 === 0;\n break;\n default:\n throw new Error(\"Unreachable\");\n }\n if (!this.types[y][x] && invert)\n this.modules[y][x] = !this.modules[y][x];\n }\n }\n }\n // Calculates and returns the penalty score based on state of this QR Code's current modules.\n // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.\n getPenaltyScore() {\n let result = 0;\n for (let y = 0; y < this.size; y++) {\n let runColor = false;\n let runX = 0;\n const runHistory = [0, 0, 0, 0, 0, 0, 0];\n for (let x = 0; x < this.size; x++) {\n if (this.modules[y][x] === runColor) {\n runX++;\n if (runX === 5)\n result += PENALTY_N1;\n else if (runX > 5)\n result++;\n } else {\n this.finderPenaltyAddHistory(runX, runHistory);\n if (!runColor)\n result += this.finderPenaltyCountPatterns(runHistory) * PENALTY_N3;\n runColor = this.modules[y][x];\n runX = 1;\n }\n }\n result += this.finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3;\n }\n for (let x = 0; x < this.size; x++) {\n let runColor = false;\n let runY = 0;\n const runHistory = [0, 0, 0, 0, 0, 0, 0];\n for (let y = 0; y < this.size; y++) {\n if (this.modules[y][x] === runColor) {\n runY++;\n if (runY === 5)\n result += PENALTY_N1;\n else if (runY > 5)\n result++;\n } else {\n this.finderPenaltyAddHistory(runY, runHistory);\n if (!runColor)\n result += this.finderPenaltyCountPatterns(runHistory) * PENALTY_N3;\n runColor = this.modules[y][x];\n runY = 1;\n }\n }\n result += this.finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3;\n }\n for (let y = 0; y < this.size - 1; y++) {\n for (let x = 0; x < this.size - 1; x++) {\n const color = this.modules[y][x];\n if (color === this.modules[y][x + 1] && color === this.modules[y + 1][x] && color === this.modules[y + 1][x + 1]) {\n result += PENALTY_N2;\n }\n }\n }\n let dark = 0;\n for (const row of this.modules)\n dark = row.reduce((sum, color) => sum + (color ? 1 : 0), dark);\n const total = this.size * this.size;\n const k = Math.ceil(Math.abs(dark * 20 - total * 10) / total) - 1;\n result += k * PENALTY_N4;\n return result;\n }\n /* -- Private helper functions -- */\n // Returns an ascending list of positions of alignment patterns for this version number.\n // Each position is in the range [0,177), and are used on both the x and y axes.\n // This could be implemented as lookup table of 40 variable-length lists of integers.\n getAlignmentPatternPositions() {\n if (this.version === 1) {\n return [];\n } else {\n const numAlign = Math.floor(this.version / 7) + 2;\n const step = this.version === 32 ? 26 : Math.ceil((this.version * 4 + 4) / (numAlign * 2 - 2)) * 2;\n const result = [6];\n for (let pos = this.size - 7; result.length < numAlign; pos -= step)\n result.splice(1, 0, pos);\n return result;\n }\n }\n // Can only be called immediately after a light run is added, and\n // returns either 0, 1, or 2. A helper function for getPenaltyScore().\n finderPenaltyCountPatterns(runHistory) {\n const n = runHistory[1];\n const core = n > 0 && runHistory[2] === n && runHistory[3] === n * 3 && runHistory[4] === n && runHistory[5] === n;\n return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0);\n }\n // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().\n finderPenaltyTerminateAndCount(currentRunColor, currentRunLength, runHistory) {\n if (currentRunColor) {\n this.finderPenaltyAddHistory(currentRunLength, runHistory);\n currentRunLength = 0;\n }\n currentRunLength += this.size;\n this.finderPenaltyAddHistory(currentRunLength, runHistory);\n return this.finderPenaltyCountPatterns(runHistory);\n }\n // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().\n finderPenaltyAddHistory(currentRunLength, runHistory) {\n if (runHistory[0] === 0)\n currentRunLength += this.size;\n runHistory.pop();\n runHistory.unshift(currentRunLength);\n }\n}\nfunction appendBits(val, len, bb) {\n if (len < 0 || len > 31 || val >>> len !== 0)\n throw new RangeError(\"Value out of range\");\n for (let i = len - 1; i >= 0; i--)\n bb.push(val >>> i & 1);\n}\nfunction getBit(x, i) {\n return (x >>> i & 1) !== 0;\n}\nclass QrSegment {\n // Creates a new QR Code segment with the given attributes and data.\n // The character count (numChars) must agree with the mode and the bit buffer length,\n // but the constraint isn't checked. The given bit buffer is cloned and stored.\n constructor(mode, numChars, bitData) {\n this.mode = mode;\n this.numChars = numChars;\n this.bitData = bitData;\n if (numChars < 0)\n throw new RangeError(\"Invalid argument\");\n this.bitData = bitData.slice();\n }\n /* -- Methods -- */\n // Returns a new copy of the data bits of this segment.\n getData() {\n return this.bitData.slice();\n }\n}\nconst MODE_NUMERIC = [1, 10, 12, 14];\nconst MODE_ALPHANUMERIC = [2, 9, 11, 13];\nconst MODE_BYTE = [4, 8, 16, 16];\nfunction numCharCountBits(mode, ver) {\n return mode[Math.floor((ver + 7) / 17) + 1];\n}\nfunction makeBytes(data) {\n const bb = [];\n for (const b of data)\n appendBits(b, 8, bb);\n return new QrSegment(MODE_BYTE, data.length, bb);\n}\nfunction makeNumeric(digits) {\n if (!isNumeric(digits))\n throw new RangeError(\"String contains non-numeric characters\");\n const bb = [];\n for (let i = 0; i < digits.length; ) {\n const n = Math.min(digits.length - i, 3);\n appendBits(Number.parseInt(digits.substring(i, i + n), 10), n * 3 + 1, bb);\n i += n;\n }\n return new QrSegment(MODE_NUMERIC, digits.length, bb);\n}\nfunction makeAlphanumeric(text) {\n if (!isAlphanumeric(text))\n throw new RangeError(\"String contains unencodable characters in alphanumeric mode\");\n const bb = [];\n let i;\n for (i = 0; i + 2 <= text.length; i += 2) {\n let temp = ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45;\n temp += ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1));\n appendBits(temp, 11, bb);\n }\n if (i < text.length)\n appendBits(ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6, bb);\n return new QrSegment(MODE_ALPHANUMERIC, text.length, bb);\n}\nfunction makeSegments(text) {\n if (text === \"\")\n return [];\n else if (isNumeric(text))\n return [makeNumeric(text)];\n else if (isAlphanumeric(text))\n return [makeAlphanumeric(text)];\n else\n return [makeBytes(toUtf8ByteArray(text))];\n}\nfunction isNumeric(text) {\n return NUMERIC_REGEX.test(text);\n}\nfunction isAlphanumeric(text) {\n return ALPHANUMERIC_REGEX.test(text);\n}\nfunction getTotalBits(segs, version) {\n let result = 0;\n for (const seg of segs) {\n const ccbits = numCharCountBits(seg.mode, version);\n if (seg.numChars >= 1 << ccbits)\n return Number.POSITIVE_INFINITY;\n result += 4 + ccbits + seg.bitData.length;\n }\n return result;\n}\nfunction toUtf8ByteArray(str) {\n str = encodeURI(str);\n const result = [];\n for (let i = 0; i < str.length; i++) {\n if (str.charAt(i) !== \"%\") {\n result.push(str.charCodeAt(i));\n } else {\n result.push(Number.parseInt(str.substring(i + 1, i + 3), 16));\n i += 2;\n }\n }\n return result;\n}\nfunction getNumRawDataModules(ver) {\n if (ver < MIN_VERSION || ver > MAX_VERSION)\n throw new RangeError(\"Version number out of range\");\n let result = (16 * ver + 128) * ver + 64;\n if (ver >= 2) {\n const numAlign = Math.floor(ver / 7) + 2;\n result -= (25 * numAlign - 10) * numAlign - 55;\n if (ver >= 7)\n result -= 36;\n }\n return result;\n}\nfunction getNumDataCodewords(ver, ecl) {\n return Math.floor(getNumRawDataModules(ver) / 8) - ECC_CODEWORDS_PER_BLOCK[ecl[0]][ver] * NUM_ERROR_CORRECTION_BLOCKS[ecl[0]][ver];\n}\nfunction reedSolomonComputeDivisor(degree) {\n if (degree < 1 || degree > 255)\n throw new RangeError(\"Degree out of range\");\n const result = [];\n for (let i = 0; i < degree - 1; i++)\n result.push(0);\n result.push(1);\n let root = 1;\n for (let i = 0; i < degree; i++) {\n for (let j = 0; j < result.length; j++) {\n result[j] = reedSolomonMultiply(result[j], root);\n if (j + 1 < result.length)\n result[j] ^= result[j + 1];\n }\n root = reedSolomonMultiply(root, 2);\n }\n return result;\n}\nfunction reedSolomonComputeRemainder(data, divisor) {\n const result = divisor.map((_) => 0);\n for (const b of data) {\n const factor = b ^ result.shift();\n result.push(0);\n divisor.forEach((coef, i) => result[i] ^= reedSolomonMultiply(coef, factor));\n }\n return result;\n}\nfunction reedSolomonMultiply(x, y) {\n if (x >>> 8 !== 0 || y >>> 8 !== 0)\n throw new RangeError(\"Byte out of range\");\n let z = 0;\n for (let i = 7; i >= 0; i--) {\n z = z << 1 ^ (z >>> 7) * 285;\n z ^= (y >>> i & 1) * x;\n }\n return z;\n}\nfunction encodeSegments(segs, ecl, minVersion = 1, maxVersion = 40, mask = -1, boostEcl = true) {\n if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) {\n throw new RangeError(\"Invalid value\");\n }\n let version;\n let dataUsedBits;\n for (version = minVersion; ; version++) {\n const dataCapacityBits2 = getNumDataCodewords(version, ecl) * 8;\n const usedBits = getTotalBits(segs, version);\n if (usedBits <= dataCapacityBits2) {\n dataUsedBits = usedBits;\n break;\n }\n if (version >= maxVersion)\n throw new RangeError(\"Data too long\");\n }\n for (const newEcl of [MEDIUM, QUARTILE, HIGH]) {\n if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8)\n ecl = newEcl;\n }\n const bb = [];\n for (const seg of segs) {\n appendBits(seg.mode[0], 4, bb);\n appendBits(seg.numChars, numCharCountBits(seg.mode, version), bb);\n for (const b of seg.getData())\n bb.push(b);\n }\n const dataCapacityBits = getNumDataCodewords(version, ecl) * 8;\n appendBits(0, Math.min(4, dataCapacityBits - bb.length), bb);\n appendBits(0, (8 - bb.length % 8) % 8, bb);\n for (let padByte = 236; bb.length < dataCapacityBits; padByte ^= 236 ^ 17)\n appendBits(padByte, 8, bb);\n const dataCodewords = Array.from({ length: Math.ceil(bb.length / 8) }, () => 0);\n bb.forEach((b, i) => dataCodewords[i >>> 3] |= b << 7 - (i & 7));\n return new QrCode(version, ecl, dataCodewords, mask);\n}\n\nfunction encode(data, options) {\n const {\n ecc = \"L\",\n boostEcc = false,\n minVersion = 1,\n maxVersion = 40,\n maskPattern = -1,\n border = 1\n } = options || {};\n const segment = typeof data === \"string\" ? makeSegments(data) : Array.isArray(data) ? [makeBytes(data)] : void 0;\n if (!segment)\n throw new Error(`uqr only supports encoding string and binary data, but got: ${typeof data}`);\n const qr = encodeSegments(\n segment,\n EccMap[ecc],\n minVersion,\n maxVersion,\n maskPattern,\n boostEcc\n );\n const result = addBorder({\n version: qr.version,\n maskPattern: qr.mask,\n size: qr.size,\n data: qr.modules,\n types: qr.types\n }, border);\n if (options?.invert)\n result.data = result.data.map((row) => row.map((mod) => !mod));\n options?.onEncoded?.(result);\n return result;\n}\nfunction addBorder(input, border = 1) {\n if (!border)\n return input;\n const { size } = input;\n const newSize = size + border * 2;\n input.size = newSize;\n input.data.forEach((row) => {\n for (let i = 0; i < border; i++) {\n row.unshift(false);\n row.push(false);\n }\n });\n for (let i = 0; i < border; i++) {\n input.data.unshift(Array.from({ length: newSize }, (_) => false));\n input.data.push(Array.from({ length: newSize }, (_) => false));\n }\n const b = QrCodeDataType.Border;\n input.types.forEach((row) => {\n for (let i = 0; i < border; i++) {\n row.unshift(b);\n row.push(b);\n }\n });\n for (let i = 0; i < border; i++) {\n input.types.unshift(Array.from({ length: newSize }, (_) => b));\n input.types.push(Array.from({ length: newSize }, (_) => b));\n }\n return input;\n}\nfunction getDataAt(data, x, y, defaults = false) {\n if (x < 0 || y < 0 || x >= data.length || y >= data.length)\n return defaults;\n return data[y][x];\n}\n\nfunction renderUnicode(data, options = {}) {\n const {\n whiteChar = \"\\u2588\",\n blackChar = \"\\u2591\"\n } = options;\n const result = encode(data, options);\n return result.data.map((row) => {\n return row.map((mod) => mod ? blackChar : whiteChar).join(\"\");\n }).join(\"\\n\");\n}\nfunction renderANSI(data, options = {}) {\n return renderUnicode(data, {\n ...options,\n blackChar: \"\\x1B[40m\\u3000\\x1B[0m\",\n whiteChar: \"\\x1B[47m\\u3000\\x1B[0m\"\n });\n}\nfunction renderUnicodeCompact(data, options = {}) {\n const palette = {\n WHITE_ALL: \"\\u2588\",\n WHITE_BLACK: \"\\u2580\",\n BLACK_WHITE: \"\\u2584\",\n BLACK_ALL: \" \"\n };\n const result = encode(data, options);\n const WHITE = false;\n const BLACK = true;\n const at = (x, y) => getDataAt(result.data, x, y, true);\n const lines = [];\n let line = \"\";\n for (let row = 0; row < result.size; row += 2) {\n for (let col = 0; col < result.size; col++) {\n if (at(col, row) === WHITE && at(col, row + 1) === WHITE)\n line += palette.WHITE_ALL;\n else if (at(col, row) === WHITE && at(col, row + 1) === BLACK)\n line += palette.WHITE_BLACK;\n else if (at(col, row) === BLACK && at(col, row + 1) === WHITE)\n line += palette.BLACK_WHITE;\n else\n line += palette.BLACK_ALL;\n }\n lines.push(line);\n line = \"\";\n }\n return lines.join(\"\\n\");\n}\n\nfunction escapeAttr(value) {\n return value.replace(/&/g, \"&\").replace(/\"/g, \""\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\nfunction renderSVG(data, options = {}) {\n const result = encode(data, options);\n const {\n pixelSize = 10,\n whiteColor = \"white\",\n blackColor = \"black\"\n } = options;\n const height = result.size * pixelSize;\n const width = result.size * pixelSize;\n let svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 ${width} ${height}\">`;\n const paths = [];\n for (let row = 0; row < result.size; row++) {\n for (let col = 0; col < result.size; col++) {\n const x = col * pixelSize;\n const y = row * pixelSize;\n if (result.data[row][col])\n paths.push(`M${x},${y}h${pixelSize}v${pixelSize}h-${pixelSize}z`);\n }\n }\n svg += `<rect fill=\"${escapeAttr(whiteColor)}\" width=\"${width}\" height=\"${height}\"/>`;\n svg += `<path fill=\"${escapeAttr(blackColor)}\" d=\"${paths.join(\"\")}\"/>`;\n svg += \"</svg>\";\n return svg;\n}\n\nexport { QrCodeDataType, encode, renderANSI, renderSVG, renderUnicode, renderUnicodeCompact };\n"],"names":[],"mappings":"AAAG,IAAC,iBAAkC,kBAAC,oBAAoB;AACzD,kBAAgB,gBAAgB,QAAQ,IAAI,EAAE,IAAI;AAClD,kBAAgB,gBAAgB,MAAM,IAAI,CAAC,IAAI;AAC/C,kBAAgB,gBAAgB,UAAU,IAAI,CAAC,IAAI;AACnD,kBAAgB,gBAAgB,UAAU,IAAI,CAAC,IAAI;AACnD,kBAAgB,gBAAgB,QAAQ,IAAI,CAAC,IAAI;AACjD,kBAAgB,gBAAgB,WAAW,IAAI,CAAC,IAAI;AACpD,SAAO;AACT,GAAG,kBAAkB,CAAA,CAAE;AAEvB,MAAM,MAAM,CAAC,GAAG,CAAC;AACjB,MAAM,SAAS,CAAC,GAAG,CAAC;AACpB,MAAM,WAAW,CAAC,GAAG,CAAC;AACtB,MAAM,OAAO,CAAC,GAAG,CAAC;AAClB,MAAM,SAAS;AAAA,EACb,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AACA,MAAM,gBAAgB;AACtB,MAAM,qBAAqB;AAC3B,MAAM,uBAAuB;AAC7B,MAAM,cAAc;AACpB,MAAM,cAAc;AACpB,MAAM,aAAa;AACnB,MAAM,aAAa;AACnB,MAAM,aAAa;AACnB,MAAM,aAAa;AACnB,MAAM,0BAA0B;AAAA;AAAA;AAAA,EAG9B,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EAElK,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EAEnK,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EAEnK,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAErK;AACA,MAAM,8BAA8B;AAAA;AAAA;AAAA,EAGlC,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EAE5I,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EAErJ,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAAA,EAExJ,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA;AAE3J;AACA,MAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMX,YAAY,SAAS,KAAK,eAAe,KAAK;AAC5C,SAAK,UAAU;AACf,SAAK,MAAM;AACX,QAAI,UAAU,eAAe,UAAU;AACrC,YAAM,IAAI,WAAW,4BAA4B;AACnD,QAAI,MAAM,MAAM,MAAM;AACpB,YAAM,IAAI,WAAW,yBAAyB;AAChD,SAAK,OAAO,UAAU,IAAI;AAC1B,UAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,KAAK,KAAI,CAAE,EAAE,KAAK,KAAK;AACxD,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AAClC,WAAK,QAAQ,KAAK,IAAI,MAAK,CAAE;AAC7B,WAAK,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC;AAAA,IAClC;AACA,SAAK,qBAAoB;AACzB,UAAM,eAAe,KAAK,oBAAoB,aAAa;AAC3D,SAAK,cAAc,YAAY;AAC/B,QAAI,QAAQ,IAAI;AACd,UAAI,aAAa;AACjB,eAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,aAAK,UAAU,CAAC;AAChB,aAAK,eAAe,CAAC;AACrB,cAAM,UAAU,KAAK,gBAAe;AACpC,YAAI,UAAU,YAAY;AACxB,gBAAM;AACN,uBAAa;AAAA,QACf;AACA,aAAK,UAAU,CAAC;AAAA,MAClB;AAAA,IACF;AACA,SAAK,OAAO;AACZ,SAAK,UAAU,GAAG;AAClB,SAAK,eAAe,GAAG;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA;AAAA;AAAA,EAGA,UAAU,CAAA;AAAA,EACV,QAAQ,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKR,UAAU,GAAG,GAAG;AACd,WAAO,KAAK,KAAK,IAAI,KAAK,QAAQ,KAAK,KAAK,IAAI,KAAK,QAAQ,KAAK,QAAQ,CAAC,EAAE,CAAC;AAAA,EAChF;AAAA;AAAA;AAAA,EAGA,uBAAuB;AACrB,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AAClC,WAAK,kBAAkB,GAAG,GAAG,IAAI,MAAM,GAAG,eAAe,MAAM;AAC/D,WAAK,kBAAkB,GAAG,GAAG,IAAI,MAAM,GAAG,eAAe,MAAM;AAAA,IACjE;AACA,SAAK,kBAAkB,GAAG,CAAC;AAC3B,SAAK,kBAAkB,KAAK,OAAO,GAAG,CAAC;AACvC,SAAK,kBAAkB,GAAG,KAAK,OAAO,CAAC;AACvC,UAAM,cAAc,KAAK,6BAA4B;AACrD,UAAM,WAAW,YAAY;AAC7B,aAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,eAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,YAAI,EAAE,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM,WAAW,KAAK,MAAM;AACvF,eAAK,qBAAqB,YAAY,CAAC,GAAG,YAAY,CAAC,CAAC;AAAA,MAC5D;AAAA,IACF;AACA,SAAK,eAAe,CAAC;AACrB,SAAK,YAAW;AAAA,EAClB;AAAA;AAAA;AAAA,EAGA,eAAe,MAAM;AACnB,UAAM,OAAO,KAAK,IAAI,CAAC,KAAK,IAAI;AAChC,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,IAAI,IAAI;AACtB,YAAM,OAAO,KAAK,QAAQ,KAAK;AACjC,UAAM,QAAQ,QAAQ,KAAK,OAAO;AAClC,aAAS,IAAI,GAAG,KAAK,GAAG;AACtB,WAAK,kBAAkB,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAC9C,SAAK,kBAAkB,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAC5C,SAAK,kBAAkB,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAC5C,SAAK,kBAAkB,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAC5C,aAAS,IAAI,GAAG,IAAI,IAAI;AACtB,WAAK,kBAAkB,KAAK,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AACnD,aAAS,IAAI,GAAG,IAAI,GAAG;AACrB,WAAK,kBAAkB,KAAK,OAAO,IAAI,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AAC9D,aAAS,IAAI,GAAG,IAAI,IAAI;AACtB,WAAK,kBAAkB,GAAG,KAAK,OAAO,KAAK,GAAG,OAAO,MAAM,CAAC,CAAC;AAC/D,SAAK,kBAAkB,GAAG,KAAK,OAAO,GAAG,IAAI;AAAA,EAC/C;AAAA;AAAA;AAAA,EAGA,cAAc;AACZ,QAAI,KAAK,UAAU;AACjB;AACF,QAAI,MAAM,KAAK;AACf,aAAS,IAAI,GAAG,IAAI,IAAI;AACtB,YAAM,OAAO,KAAK,QAAQ,MAAM;AAClC,UAAM,OAAO,KAAK,WAAW,KAAK;AAClC,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,YAAM,QAAQ,OAAO,MAAM,CAAC;AAC5B,YAAM,IAAI,KAAK,OAAO,KAAK,IAAI;AAC/B,YAAM,IAAI,KAAK,MAAM,IAAI,CAAC;AAC1B,WAAK,kBAAkB,GAAG,GAAG,KAAK;AAClC,WAAK,kBAAkB,GAAG,GAAG,KAAK;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA,EAGA,kBAAkB,GAAG,GAAG;AACtB,aAAS,KAAK,IAAI,MAAM,GAAG,MAAM;AAC/B,eAAS,KAAK,IAAI,MAAM,GAAG,MAAM;AAC/B,cAAM,OAAO,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,IAAI,EAAE,CAAC;AAChD,cAAM,KAAK,IAAI;AACf,cAAM,KAAK,IAAI;AACf,YAAI,MAAM,KAAK,KAAK,KAAK,QAAQ,MAAM,KAAK,KAAK,KAAK;AACpD,eAAK,kBAAkB,IAAI,IAAI,SAAS,KAAK,SAAS,GAAG,eAAe,QAAQ;AAAA,MACpF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA,EAGA,qBAAqB,GAAG,GAAG;AACzB,aAAS,KAAK,IAAI,MAAM,GAAG,MAAM;AAC/B,eAAS,KAAK,IAAI,MAAM,GAAG,MAAM;AAC/B,aAAK;AAAA,UACH,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,IAAI,EAAE,CAAC,MAAM;AAAA,UACzC,eAAe;AAAA,QACzB;AAAA,MACM;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA,EAGA,kBAAkB,GAAG,GAAG,QAAQ,OAAO,eAAe,UAAU;AAC9D,SAAK,QAAQ,CAAC,EAAE,CAAC,IAAI;AACrB,SAAK,MAAM,CAAC,EAAE,CAAC,IAAI;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAIA,oBAAoB,MAAM;AACxB,UAAM,MAAM,KAAK;AACjB,UAAM,MAAM,KAAK;AACjB,QAAI,KAAK,WAAW,oBAAoB,KAAK,GAAG;AAC9C,YAAM,IAAI,WAAW,kBAAkB;AACzC,UAAM,YAAY,4BAA4B,IAAI,CAAC,CAAC,EAAE,GAAG;AACzD,UAAM,cAAc,wBAAwB,IAAI,CAAC,CAAC,EAAE,GAAG;AACvD,UAAM,eAAe,KAAK,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAC7D,UAAM,iBAAiB,YAAY,eAAe;AAClD,UAAM,gBAAgB,KAAK,MAAM,eAAe,SAAS;AACzD,UAAM,SAAS,CAAA;AACf,UAAM,QAAQ,0BAA0B,WAAW;AACnD,aAAS,IAAI,GAAG,IAAI,GAAG,IAAI,WAAW,KAAK;AACzC,YAAM,MAAM,KAAK,MAAM,GAAG,IAAI,gBAAgB,eAAe,IAAI,iBAAiB,IAAI,EAAE;AACxF,WAAK,IAAI;AACT,YAAM,MAAM,4BAA4B,KAAK,KAAK;AAClD,UAAI,IAAI;AACN,YAAI,KAAK,CAAC;AACZ,aAAO,KAAK,IAAI,OAAO,GAAG,CAAC;AAAA,IAC7B;AACA,UAAM,SAAS,CAAA;AACf,aAAS,IAAI,GAAG,IAAI,OAAO,CAAC,EAAE,QAAQ,KAAK;AACzC,aAAO,QAAQ,CAAC,OAAO,MAAM;AAC3B,YAAI,MAAM,gBAAgB,eAAe,KAAK;AAC5C,iBAAO,KAAK,MAAM,CAAC,CAAC;AAAA,MACxB,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAGA,cAAc,MAAM;AAClB,QAAI,KAAK,WAAW,KAAK,MAAM,qBAAqB,KAAK,OAAO,IAAI,CAAC;AACnE,YAAM,IAAI,WAAW,kBAAkB;AACzC,QAAI,IAAI;AACR,aAAS,QAAQ,KAAK,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG;AACtD,UAAI,UAAU;AACZ,gBAAQ;AACV,eAAS,OAAO,GAAG,OAAO,KAAK,MAAM,QAAQ;AAC3C,iBAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,gBAAM,IAAI,QAAQ;AAClB,gBAAM,UAAU,QAAQ,IAAI,OAAO;AACnC,gBAAM,IAAI,SAAS,KAAK,OAAO,IAAI,OAAO;AAC1C,cAAI,CAAC,KAAK,MAAM,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,GAAG;AAC5C,iBAAK,QAAQ,CAAC,EAAE,CAAC,IAAI,OAAO,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,EAAE;AACtD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,MAAM;AACd,QAAI,OAAO,KAAK,OAAO;AACrB,YAAM,IAAI,WAAW,yBAAyB;AAChD,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AAClC,eAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AAClC,YAAI;AACJ,gBAAQ,MAAI;AAAA,UACV,KAAK;AACH,sBAAU,IAAI,KAAK,MAAM;AACzB;AAAA,UACF,KAAK;AACH,qBAAS,IAAI,MAAM;AACnB;AAAA,UACF,KAAK;AACH,qBAAS,IAAI,MAAM;AACnB;AAAA,UACF,KAAK;AACH,sBAAU,IAAI,KAAK,MAAM;AACzB;AAAA,UACF,KAAK;AACH,sBAAU,KAAK,MAAM,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,KAAK,MAAM;AACzD;AAAA,UACF,KAAK;AACH,qBAAS,IAAI,IAAI,IAAI,IAAI,IAAI,MAAM;AACnC;AAAA,UACF,KAAK;AACH,sBAAU,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM;AACzC;AAAA,UACF,KAAK;AACH,uBAAW,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,MAAM;AAC3C;AAAA,UACF;AACE,kBAAM,IAAI,MAAM,aAAa;AAAA,QACzC;AACQ,YAAI,CAAC,KAAK,MAAM,CAAC,EAAE,CAAC,KAAK;AACvB,eAAK,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA,EAGA,kBAAkB;AAChB,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AAClC,UAAI,WAAW;AACf,UAAI,OAAO;AACX,YAAM,aAAa,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AACvC,eAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AAClC,YAAI,KAAK,QAAQ,CAAC,EAAE,CAAC,MAAM,UAAU;AACnC;AACA,cAAI,SAAS;AACX,sBAAU;AAAA,mBACH,OAAO;AACd;AAAA,QACJ,OAAO;AACL,eAAK,wBAAwB,MAAM,UAAU;AAC7C,cAAI,CAAC;AACH,sBAAU,KAAK,2BAA2B,UAAU,IAAI;AAC1D,qBAAW,KAAK,QAAQ,CAAC,EAAE,CAAC;AAC5B,iBAAO;AAAA,QACT;AAAA,MACF;AACA,gBAAU,KAAK,+BAA+B,UAAU,MAAM,UAAU,IAAI;AAAA,IAC9E;AACA,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AAClC,UAAI,WAAW;AACf,UAAI,OAAO;AACX,YAAM,aAAa,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AACvC,eAAS,IAAI,GAAG,IAAI,KAAK,MAAM,KAAK;AAClC,YAAI,KAAK,QAAQ,CAAC,EAAE,CAAC,MAAM,UAAU;AACnC;AACA,cAAI,SAAS;AACX,sBAAU;AAAA,mBACH,OAAO;AACd;AAAA,QACJ,OAAO;AACL,eAAK,wBAAwB,MAAM,UAAU;AAC7C,cAAI,CAAC;AACH,sBAAU,KAAK,2BAA2B,UAAU,IAAI;AAC1D,qBAAW,KAAK,QAAQ,CAAC,EAAE,CAAC;AAC5B,iBAAO;AAAA,QACT;AAAA,MACF;AACA,gBAAU,KAAK,+BAA+B,UAAU,MAAM,UAAU,IAAI;AAAA,IAC9E;AACA,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,GAAG,KAAK;AACtC,eAAS,IAAI,GAAG,IAAI,KAAK,OAAO,GAAG,KAAK;AACtC,cAAM,QAAQ,KAAK,QAAQ,CAAC,EAAE,CAAC;AAC/B,YAAI,UAAU,KAAK,QAAQ,CAAC,EAAE,IAAI,CAAC,KAAK,UAAU,KAAK,QAAQ,IAAI,CAAC,EAAE,CAAC,KAAK,UAAU,KAAK,QAAQ,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG;AAChH,oBAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AACA,QAAI,OAAO;AACX,eAAW,OAAO,KAAK;AACrB,aAAO,IAAI,OAAO,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,IAAI,IAAI;AAC/D,UAAM,QAAQ,KAAK,OAAO,KAAK;AAC/B,UAAM,IAAI,KAAK,KAAK,KAAK,IAAI,OAAO,KAAK,QAAQ,EAAE,IAAI,KAAK,IAAI;AAChE,cAAU,IAAI;AACd,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,+BAA+B;AAC7B,QAAI,KAAK,YAAY,GAAG;AACtB,aAAO,CAAA;AAAA,IACT,OAAO;AACL,YAAM,WAAW,KAAK,MAAM,KAAK,UAAU,CAAC,IAAI;AAChD,YAAM,OAAO,KAAK,YAAY,KAAK,KAAK,KAAK,MAAM,KAAK,UAAU,IAAI,MAAM,WAAW,IAAI,EAAE,IAAI;AACjG,YAAM,SAAS,CAAC,CAAC;AACjB,eAAS,MAAM,KAAK,OAAO,GAAG,OAAO,SAAS,UAAU,OAAO;AAC7D,eAAO,OAAO,GAAG,GAAG,GAAG;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA,EAGA,2BAA2B,YAAY;AACrC,UAAM,IAAI,WAAW,CAAC;AACtB,UAAM,OAAO,IAAI,KAAK,WAAW,CAAC,MAAM,KAAK,WAAW,CAAC,MAAM,IAAI,KAAK,WAAW,CAAC,MAAM,KAAK,WAAW,CAAC,MAAM;AACjH,YAAQ,QAAQ,WAAW,CAAC,KAAK,IAAI,KAAK,WAAW,CAAC,KAAK,IAAI,IAAI,MAAM,QAAQ,WAAW,CAAC,KAAK,IAAI,KAAK,WAAW,CAAC,KAAK,IAAI,IAAI;AAAA,EACtI;AAAA;AAAA,EAEA,+BAA+B,iBAAiB,kBAAkB,YAAY;AAC5E,QAAI,iBAAiB;AACnB,WAAK,wBAAwB,kBAAkB,UAAU;AACzD,yBAAmB;AAAA,IACrB;AACA,wBAAoB,KAAK;AACzB,SAAK,wBAAwB,kBAAkB,UAAU;AACzD,WAAO,KAAK,2BAA2B,UAAU;AAAA,EACnD;AAAA;AAAA,EAEA,wBAAwB,kBAAkB,YAAY;AACpD,QAAI,WAAW,CAAC,MAAM;AACpB,0BAAoB,KAAK;AAC3B,eAAW,IAAG;AACd,eAAW,QAAQ,gBAAgB;AAAA,EACrC;AACF;AACA,SAAS,WAAW,KAAK,KAAK,IAAI;AAChC,MAAI,MAAM,KAAK,MAAM,MAAM,QAAQ,QAAQ;AACzC,UAAM,IAAI,WAAW,oBAAoB;AAC3C,WAAS,IAAI,MAAM,GAAG,KAAK,GAAG;AAC5B,OAAG,KAAK,QAAQ,IAAI,CAAC;AACzB;AACA,SAAS,OAAO,GAAG,GAAG;AACpB,UAAQ,MAAM,IAAI,OAAO;AAC3B;AACA,MAAM,UAAU;AAAA;AAAA;AAAA;AAAA,EAId,YAAY,MAAM,UAAU,SAAS;AACnC,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,SAAK,UAAU;AACf,QAAI,WAAW;AACb,YAAM,IAAI,WAAW,kBAAkB;AACzC,SAAK,UAAU,QAAQ,MAAK;AAAA,EAC9B;AAAA;AAAA;AAAA,EAGA,UAAU;AACR,WAAO,KAAK,QAAQ,MAAK;AAAA,EAC3B;AACF;AACA,MAAM,eAAe,CAAC,GAAG,IAAI,IAAI,EAAE;AACnC,MAAM,oBAAoB,CAAC,GAAG,GAAG,IAAI,EAAE;AACvC,MAAM,YAAY,CAAC,GAAG,GAAG,IAAI,EAAE;AAC/B,SAAS,iBAAiB,MAAM,KAAK;AACnC,SAAO,KAAK,KAAK,OAAO,MAAM,KAAK,EAAE,IAAI,CAAC;AAC5C;AACA,SAAS,UAAU,MAAM;AACvB,QAAM,KAAK,CAAA;AACX,aAAW,KAAK;AACd,eAAW,GAAG,GAAG,EAAE;AACrB,SAAO,IAAI,UAAU,WAAW,KAAK,QAAQ,EAAE;AACjD;AACA,SAAS,YAAY,QAAQ;AAC3B,MAAI,CAAC,UAAU,MAAM;AACnB,UAAM,IAAI,WAAW,wCAAwC;AAC/D,QAAM,KAAK,CAAA;AACX,WAAS,IAAI,GAAG,IAAI,OAAO,UAAU;AACnC,UAAM,IAAI,KAAK,IAAI,OAAO,SAAS,GAAG,CAAC;AACvC,eAAW,OAAO,SAAS,OAAO,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE;AACzE,SAAK;AAAA,EACP;AACA,SAAO,IAAI,UAAU,cAAc,OAAO,QAAQ,EAAE;AACtD;AACA,SAAS,iBAAiB,MAAM;AAC9B,MAAI,CAAC,eAAe,IAAI;AACtB,UAAM,IAAI,WAAW,6DAA6D;AACpF,QAAM,KAAK,CAAA;AACX,MAAI;AACJ,OAAK,IAAI,GAAG,IAAI,KAAK,KAAK,QAAQ,KAAK,GAAG;AACxC,QAAI,OAAO,qBAAqB,QAAQ,KAAK,OAAO,CAAC,CAAC,IAAI;AAC1D,YAAQ,qBAAqB,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC;AACvD,eAAW,MAAM,IAAI,EAAE;AAAA,EACzB;AACA,MAAI,IAAI,KAAK;AACX,eAAW,qBAAqB,QAAQ,KAAK,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE;AAChE,SAAO,IAAI,UAAU,mBAAmB,KAAK,QAAQ,EAAE;AACzD;AACA,SAAS,aAAa,MAAM;AAC1B,MAAI,SAAS;AACX,WAAO,CAAA;AAAA,WACA,UAAU,IAAI;AACrB,WAAO,CAAC,YAAY,IAAI,CAAC;AAAA,WAClB,eAAe,IAAI;AAC1B,WAAO,CAAC,iBAAiB,IAAI,CAAC;AAAA;AAE9B,WAAO,CAAC,UAAU,gBAAgB,IAAI,CAAC,CAAC;AAC5C;AACA,SAAS,UAAU,MAAM;AACvB,SAAO,cAAc,KAAK,IAAI;AAChC;AACA,SAAS,eAAe,MAAM;AAC5B,SAAO,mBAAmB,KAAK,IAAI;AACrC;AACA,SAAS,aAAa,MAAM,SAAS;AACnC,MAAI,SAAS;AACb,aAAW,OAAO,MAAM;AACtB,UAAM,SAAS,iBAAiB,IAAI,MAAM,OAAO;AACjD,QAAI,IAAI,YAAY,KAAK;AACvB,aAAO,OAAO;AAChB,cAAU,IAAI,SAAS,IAAI,QAAQ;AAAA,EACrC;AACA,SAAO;AACT;AACA,SAAS,gBAAgB,KAAK;AAC5B,QAAM,UAAU,GAAG;AACnB,QAAM,SAAS,CAAA;AACf,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,QAAI,IAAI,OAAO,CAAC,MAAM,KAAK;AACzB,aAAO,KAAK,IAAI,WAAW,CAAC,CAAC;AAAA,IAC/B,OAAO;AACL,aAAO,KAAK,OAAO,SAAS,IAAI,UAAU,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAC5D,WAAK;AAAA,IACP;AAAA,EACF;AACA,SAAO;AACT;AACA,SAAS,qBAAqB,KAAK;AACjC,MAAI,MAAM,eAAe,MAAM;AAC7B,UAAM,IAAI,WAAW,6BAA6B;AACpD,MAAI,UAAU,KAAK,MAAM,OAAO,MAAM;AACtC,MAAI,OAAO,GAAG;AACZ,UAAM,WAAW,KAAK,MAAM,MAAM,CAAC,IAAI;AACvC,eAAW,KAAK,WAAW,MAAM,WAAW;AAC5C,QAAI,OAAO;AACT,gBAAU;AAAA,EACd;AACA,SAAO;AACT;AACA,SAAS,oBAAoB,KAAK,KAAK;AACrC,SAAO,KAAK,MAAM,qBAAqB,GAAG,IAAI,CAAC,IAAI,wBAAwB,IAAI,CAAC,CAAC,EAAE,GAAG,IAAI,4BAA4B,IAAI,CAAC,CAAC,EAAE,GAAG;AACnI;AACA,SAAS,0BAA0B,QAAQ;AACzC,MAAI,SAAS,KAAK,SAAS;AACzB,UAAM,IAAI,WAAW,qBAAqB;AAC5C,QAAM,SAAS,CAAA;AACf,WAAS,IAAI,GAAG,IAAI,SAAS,GAAG;AAC9B,WAAO,KAAK,CAAC;AACf,SAAO,KAAK,CAAC;AACb,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,aAAO,CAAC,IAAI,oBAAoB,OAAO,CAAC,GAAG,IAAI;AAC/C,UAAI,IAAI,IAAI,OAAO;AACjB,eAAO,CAAC,KAAK,OAAO,IAAI,CAAC;AAAA,IAC7B;AACA,WAAO,oBAAoB,MAAM,CAAC;AAAA,EACpC;AACA,SAAO;AACT;AACA,SAAS,4BAA4B,MAAM,SAAS;AAClD,QAAM,SAAS,QAAQ,IAAI,CAAC,MAAM,CAAC;AACnC,aAAW,KAAK,MAAM;AACpB,UAAM,SAAS,IAAI,OAAO,MAAK;AAC/B,WAAO,KAAK,CAAC;AACb,YAAQ,QAAQ,CAAC,MAAM,MAAM,OAAO,CAAC,KAAK,oBAAoB,MAAM,MAAM,CAAC;AAAA,EAC7E;AACA,SAAO;AACT;AACA,SAAS,oBAAoB,GAAG,GAAG;AACjC,MAAI,MAAM,MAAM,KAAK,MAAM,MAAM;AAC/B,UAAM,IAAI,WAAW,mBAAmB;AAC1C,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,QAAI,KAAK,KAAK,MAAM,KAAK;AACzB,UAAM,MAAM,IAAI,KAAK;AAAA,EACvB;AACA,SAAO;AACT;AACA,SAAS,eAAe,MAAM,KAAK,aAAa,GAAG,aAAa,IAAI,OAAO,IAAI,WAAW,MAAM;AAC9F,MAAI,EAAE,eAAe,cAAc,cAAc,cAAc,cAAc,gBAAgB,OAAO,MAAM,OAAO,GAAG;AAClH,UAAM,IAAI,WAAW,eAAe;AAAA,EACtC;AACA,MAAI;AACJ,MAAI;AACJ,OAAK,UAAU,cAAc,WAAW;AACtC,UAAM,oBAAoB,oBAAoB,SAAS,GAAG,IAAI;AAC9D,UAAM,WAAW,aAAa,MAAM,OAAO;AAC3C,QAAI,YAAY,mBAAmB;AACjC,qBAAe;AACf;AAAA,IACF;AACA,QAAI,WAAW;AACb,YAAM,IAAI,WAAW,eAAe;AAAA,EACxC;AACA,aAAW,UAAU,CAAC,QAAQ,UAAU,IAAI,GAAG;AAC7C,QAAI,YAAY,gBAAgB,oBAAoB,SAAS,MAAM,IAAI;AACrE,YAAM;AAAA,EACV;AACA,QAAM,KAAK,CAAA;AACX,aAAW,OAAO,MAAM;AACtB,eAAW,IAAI,KAAK,CAAC,GAAG,GAAG,EAAE;AAC7B,eAAW,IAAI,UAAU,iBAAiB,IAAI,MAAM,OAAO,GAAG,EAAE;AAChE,eAAW,KAAK,IAAI,QAAO;AACzB,SAAG,KAAK,CAAC;AAAA,EACb;AACA,QAAM,mBAAmB,oBAAoB,SAAS,GAAG,IAAI;AAC7D,aAAW,GAAG,KAAK,IAAI,GAAG,mBAAmB,GAAG,MAAM,GAAG,EAAE;AAC3D,aAAW,IAAI,IAAI,GAAG,SAAS,KAAK,GAAG,EAAE;AACzC,WAAS,UAAU,KAAK,GAAG,SAAS,kBAAkB,WAAW,MAAM;AACrE,eAAW,SAAS,GAAG,EAAE;AAC3B,QAAM,gBAAgB,MAAM,KAAK,EAAE,QAAQ,KAAK,KAAK,GAAG,SAAS,CAAC,EAAC,GAAI,MAAM,CAAC;AAC9E,KAAG,QAAQ,CAAC,GAAG,MAAM,cAAc,MAAM,CAAC,KAAK,KAAK,KAAK,IAAI,EAAE;AAC/D,SAAO,IAAI,OAAO,SAAS,KAAK,eAAe,IAAI;AACrD;AAEA,SAAS,OAAO,MAAM,SAAS;AAC7B,QAAM;AAAA,IACJ,MAAM;AAAA,IACN,WAAW;AAAA,IACX,aAAa;AAAA,IACb,aAAa;AAAA,IACb,cAAc;AAAA,IACd,SAAS;AAAA,EACb,IAAM,WAAW,CAAA;AACf,QAAM,UAAU,OAAO,SAAS,WAAW,aAAa,IAAI,IAAI,MAAM,QAAQ,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI;AAC1G,MAAI,CAAC;AACH,UAAM,IAAI,MAAM,+DAA+D,OAAO,IAAI,EAAE;AAC9F,QAAM,KAAK;AAAA,IACT;AAAA,IACA,OAAO,GAAG;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACE,QAAM,SAAS,UAAU;AAAA,IACvB,SAAS,GAAG;AAAA,IACZ,aAAa,GAAG;AAAA,IAChB,MAAM,GAAG;AAAA,IACT,MAAM,GAAG;AAAA,IACT,OAAO,GAAG;AAAA,EACd,GAAK,MAAM;AACT,MAAI,SAAS;AACX,WAAO,OAAO,OAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;AAC/D,WAAS,YAAY,MAAM;AAC3B,SAAO;AACT;AACA,SAAS,UAAU,OAAO,SAAS,GAAG;AACpC,MAAI,CAAC;AACH,WAAO;AACT,QAAM,EAAE,KAAI,IAAK;AACjB,QAAM,UAAU,OAAO,SAAS;AAChC,QAAM,OAAO;AACb,QAAM,KAAK,QAAQ,CAAC,QAAQ;AAC1B,aAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,UAAI,QAAQ,KAAK;AACjB,UAAI,KAAK,KAAK;AAAA,IAChB;AAAA,EACF,CAAC;AACD,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,UAAM,KAAK,QAAQ,MAAM,KAAK,EAAE,QAAQ,QAAO,GAAI,CAAC,MAAM,KAAK,CAAC;AAChE,UAAM,KAAK,KAAK,MAAM,KAAK,EAAE,QAAQ,QAAO,GAAI,CAAC,MAAM,KAAK,CAAC;AAAA,EAC/D;AACA,QAAM,IAAI,eAAe;AACzB,QAAM,MAAM,QAAQ,CAAC,QAAQ;AAC3B,aAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,UAAI,QAAQ,CAAC;AACb,UAAI,KAAK,CAAC;AAAA,IACZ;AAAA,EACF,CAAC;AACD,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,UAAM,MAAM,QAAQ,MAAM,KAAK,EAAE,QAAQ,QAAO,GAAI,CAAC,MAAM,CAAC,CAAC;AAC7D,UAAM,MAAM,KAAK,MAAM,KAAK,EAAE,QAAQ,QAAO,GAAI,CAAC,MAAM,CAAC,CAAC;AAAA,EAC5D;AACA,SAAO;AACT;AAsDA,SAAS,WAAW,OAAO;AACzB,SAAO,MAAM,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,QAAQ,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM;AACxG;AACA,SAAS,UAAU,MAAM,UAAU,IAAI;AACrC,QAAM,SAAS,OAAO,MAAM,OAAO;AACnC,QAAM;AAAA,IACJ,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,aAAa;AAAA,EACjB,IAAM;AACJ,QAAM,SAAS,OAAO,OAAO;AAC7B,QAAM,QAAQ,OAAO,OAAO;AAC5B,MAAI,MAAM,wDAAwD,KAAK,IAAI,MAAM;AACjF,QAAM,QAAQ,CAAA;AACd,WAAS,MAAM,GAAG,MAAM,OAAO,MAAM,OAAO;AAC1C,aAAS,MAAM,GAAG,MAAM,OAAO,MAAM,OAAO;AAC1C,YAAM,IAAI,MAAM;AAChB,YAAM,IAAI,MAAM;AAChB,UAAI,OAAO,KAAK,GAAG,EAAE,GAAG;AACtB,cAAM,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,IAAI,SAAS,KAAK,SAAS,GAAG;AAAA,IACpE;AAAA,EACF;AACA,SAAO,eAAe,WAAW,UAAU,CAAC,YAAY,KAAK,aAAa,MAAM;AAChF,SAAO,eAAe,WAAW,UAAU,CAAC,QAAQ,MAAM,KAAK,EAAE,CAAC;AAClE,SAAO;AACP,SAAO;AACT;","x_google_ignoreList":[0]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar.js","sources":["../../../../../node_modules/.pnpm/@wolffm+prefs-client@1.1.
|
|
1
|
+
{"version":3,"file":"avatar.js","sources":["../../../../../node_modules/.pnpm/@wolffm+prefs-client@1.1.8_react@19.2.8_zod@4.6.7/node_modules/@wolffm/prefs-client/dist/avatar.js"],"sourcesContent":["// src/constants.ts\nvar PREFS_API_BASE = \"https://hadoku.me/prefs\";\n\n// src/avatar-contract.ts\nvar AVATAR_SOURCES = [\"sprite\", \"discord\", \"steam\", \"upload\"];\nvar LINK_PROVIDERS = [\"discord\", \"steam\"];\nvar LINK_MESSAGE_TYPE = \"hadoku-avatar-link\";\nvar RESOLVE_MAX_IDS = 100;\nvar SPRITE_ID_PATTERN = /^[a-z][a-z0-9-]{0,31}$/;\nvar LINK_CALLBACK_PATH = \"/account/link/callback\";\n\n// src/avatar.ts\nvar LINK_WINDOW_NAME = \"hadoku-avatar-link\";\nvar LINK_POPUP_MARK = \"hadoku-avatar-link-popup\";\nfunction markPopup(on) {\n try {\n if (on) sessionStorage.setItem(LINK_POPUP_MARK, \"1\");\n else sessionStorage.removeItem(LINK_POPUP_MARK);\n } catch {\n }\n}\nvar AvatarError = class extends Error {\n constructor(status, message) {\n super(message);\n this.status = status;\n this.name = \"AvatarError\";\n }\n status;\n};\nasync function readJson(res) {\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new AvatarError(res.status, body?.message ?? `HTTP ${res.status}`);\n }\n return await res.json();\n}\nfunction createAvatarClient(opts = {}) {\n const base = `${opts.baseUrl ?? PREFS_API_BASE}/api/v1/_avatar`;\n const f = opts.fetch ?? fetch;\n const call = (path, init = {}) => f(`${base}${path}`, { credentials: \"include\", ...init }).then(readJson);\n const send = (path, method, body) => call(path, {\n method,\n ...body === void 0 ? {} : { headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify(body) }\n });\n const getSettings = () => call(\"\");\n async function linkInPopup(provider) {\n markPopup(true);\n const popup = window.open(\"about:blank\", LINK_WINDOW_NAME, \"popup,width=520,height=760\");\n markPopup(false);\n if (!popup)\n throw new AvatarError(0, \"The sign-in window was blocked: allow pop-ups for hadoku.me\");\n let authorizeUrl;\n try {\n ({ authorizeUrl } = await send(`/link/${provider}`, \"POST\", {}));\n } catch (err) {\n popup.close();\n throw err;\n }\n popup.location.href = authorizeUrl;\n return new Promise((resolve, reject) => {\n const channel = new BroadcastChannel(LINK_MESSAGE_TYPE);\n let settled = false;\n const finish = (failure) => {\n if (settled) return;\n settled = true;\n channel.close();\n clearInterval(closedPoll);\n clearTimeout(giveUp);\n if (failure) reject(new AvatarError(400, failure));\n else getSettings().then(resolve, reject);\n };\n channel.onmessage = (e) => {\n if (e.data?.type !== LINK_MESSAGE_TYPE) return;\n finish(e.data.ok ? void 0 : e.data.error);\n };\n const closedPoll = setInterval(() => {\n if (!popup.closed) return;\n clearInterval(closedPoll);\n setTimeout(() => finish(), 1500);\n }, 500);\n const giveUp = setTimeout(() => finish(), 10 * 60 * 1e3);\n });\n }\n return {\n /** The caller's settings, including the resolved avatar. */\n get: getSettings,\n /** Pick a source. 409 when it is not available (not linked, nothing uploaded). */\n choose: (source) => send(\"\", \"PUT\", { source }),\n /** Set the source, the character, or both in one write. */\n update: (u) => send(\"\", \"PUT\", u),\n /**\n * Begin linking. Navigate the browser to the returned URL; the provider\n * returns it to hadoku.me/account/link/callback, which completes the link.\n * Call this only from the user's own click: nothing is fetched from\n * Discord or Steam before they ask.\n */\n startLink: (provider) => send(`/link/${provider}`, \"POST\", {}),\n /**\n * Link in a popup, so the page never navigates. Call it straight from the\n * click handler: the window is opened before the first await, which is\n * what popup blockers allow. Resolves with fresh settings once the popup\n * reports back or is closed (closing is not an error: the settings say\n * whether the link landed). Rejects only when the popup could not open\n * or the link could not start.\n */\n linkInPopup: (provider) => linkInPopup(provider),\n /** Finish a link from the callback page's query string (`location.search`). */\n completeLink: (query) => send(\"/link/complete\", \"POST\", { query: query.replace(/^\\?/, \"\") }),\n /** Delete the linked id and its cached avatar URL. */\n unlink: (provider) => send(`/link/${provider}`, \"DELETE\"),\n /** Upload an image (PNG, JPEG, WebP or GIF). It is re-encoded and becomes the chosen source. */\n upload: (image) => call(\"/upload\", { method: \"POST\", body: image }),\n removeUpload: () => send(\"/upload\", \"DELETE\")\n };\n}\nasync function resolveAvatars(opts, userIds) {\n const f = opts.fetch ?? fetch;\n const url = `${opts.baseUrl ?? PREFS_API_BASE}/api/v1/_avatar/resolve`;\n const unique = [...new Set(userIds)];\n const out = {};\n for (let i = 0; i < unique.length; i += RESOLVE_MAX_IDS) {\n const res = await f(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"X-User-Key\": opts.serviceKey },\n body: JSON.stringify({ userIds: unique.slice(i, i + RESOLVE_MAX_IDS) })\n });\n Object.assign(out, (await readJson(res)).avatars);\n }\n return out;\n}\nexport {\n AVATAR_SOURCES,\n AvatarError,\n LINK_CALLBACK_PATH,\n LINK_MESSAGE_TYPE,\n LINK_POPUP_MARK,\n LINK_PROVIDERS,\n RESOLVE_MAX_IDS,\n SPRITE_ID_PATTERN,\n createAvatarClient,\n resolveAvatars\n};\n"],"names":[],"mappings":"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","x_google_ignoreList":[0]}
|
package/package.json
CHANGED
|
@@ -33,6 +33,12 @@ import React, { useCallback, useEffect, useId, useRef, useState } from 'react'
|
|
|
33
33
|
import { useSettingsTab } from '@wolffm/themes'
|
|
34
34
|
import { SettingsIcon } from './ThemeIcons'
|
|
35
35
|
import { AvatarPicker } from './AvatarPicker'
|
|
36
|
+
|
|
37
|
+
// A grantee's own service credentials, with a QR encoder for TOTP enrolment:
|
|
38
|
+
// its own chunk, fetched only when a grantee opens it.
|
|
39
|
+
const ServiceAccess = React.lazy(() =>
|
|
40
|
+
import('./ServiceAccess').then(m => ({ default: m.ServiceAccess }))
|
|
41
|
+
)
|
|
36
42
|
import {
|
|
37
43
|
deleteOwnAccount,
|
|
38
44
|
prefetchSettings,
|
|
@@ -261,6 +267,9 @@ export function ConnectedSettings({
|
|
|
261
267
|
|
|
262
268
|
// Avatar: the picker loads only when opened, never at mount (AvatarPicker).
|
|
263
269
|
const [avatarOpen, setAvatarOpen] = useState(false)
|
|
270
|
+
// Service identities this person may mint for (whoami.serviceGrants).
|
|
271
|
+
const [serviceGrants, setServiceGrants] = useState(0)
|
|
272
|
+
const [serviceOpen, setServiceOpen] = useState(false)
|
|
264
273
|
|
|
265
274
|
// Content level
|
|
266
275
|
const [content, setContent] = useState<ContentLevelState | null>(null)
|
|
@@ -378,9 +387,10 @@ export function ConnectedSettings({
|
|
|
378
387
|
useEffect(() => {
|
|
379
388
|
let live = true
|
|
380
389
|
prefetchSettings({ userType: userTypeProp, name: nameProp })
|
|
381
|
-
.then(({ identity, content: state, passkeys }) => {
|
|
390
|
+
.then(({ identity, content: state, passkeys, serviceGrants: grants }) => {
|
|
382
391
|
if (!live) return
|
|
383
392
|
setPasskeyCount(passkeys)
|
|
393
|
+
setServiceGrants(grants)
|
|
384
394
|
// Props stay authoritative: the host passed them because it can see an
|
|
385
395
|
// identity this component can't resolve for itself.
|
|
386
396
|
if (userTypeProp === undefined) {
|
|
@@ -720,6 +730,31 @@ export function ConnectedSettings({
|
|
|
720
730
|
{avatarOpen && <AvatarPicker />}
|
|
721
731
|
</section>
|
|
722
732
|
)}
|
|
733
|
+
|
|
734
|
+
{/* Service access: only for someone the operator granted a
|
|
735
|
+
service identity. Collapsed until Open, like Avatar. */}
|
|
736
|
+
{!resolving && serviceGrants > 0 && (
|
|
737
|
+
<section className="settings-popout__row settings-popout__row--stack">
|
|
738
|
+
<span className="settings-popout__inline">
|
|
739
|
+
<span className="settings-popout__label settings-popout__label--grow">
|
|
740
|
+
Service access
|
|
741
|
+
</span>
|
|
742
|
+
<button
|
|
743
|
+
type="button"
|
|
744
|
+
className="settings-popout__btn"
|
|
745
|
+
aria-expanded={serviceOpen}
|
|
746
|
+
onClick={() => setServiceOpen(v => !v)}
|
|
747
|
+
>
|
|
748
|
+
{serviceOpen ? 'Done' : 'Open'}
|
|
749
|
+
</button>
|
|
750
|
+
</span>
|
|
751
|
+
{serviceOpen && (
|
|
752
|
+
<React.Suspense fallback={<span className="settings-popout__hint">…</span>}>
|
|
753
|
+
<ServiceAccess />
|
|
754
|
+
</React.Suspense>
|
|
755
|
+
)}
|
|
756
|
+
</section>
|
|
757
|
+
)}
|
|
723
758
|
</>
|
|
724
759
|
)}
|
|
725
760
|
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useState } from 'react'
|
|
2
|
+
import { renderSVG } from 'uqr'
|
|
3
|
+
import { loadSignInClient, type PasskeyAssertion } from '../lib/settingsClient'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Service access: a grantee's own credentials for the service identities the
|
|
7
|
+
* operator granted them (hadoku_site docs/planning/service-grants-2026-10-31.md).
|
|
8
|
+
*
|
|
9
|
+
* Two steps, both behind a fresh passkey tap:
|
|
10
|
+
* 1. enrol an authenticator app once (the QR is shown once, then confirmed
|
|
11
|
+
* with one code);
|
|
12
|
+
* 2. mint a credential: passkey tap + a current code. The key is shown once
|
|
13
|
+
* and lives 7 days.
|
|
14
|
+
*
|
|
15
|
+
* Loaded only when opened (React.lazy in ConnectedSettings), so the QR encoder
|
|
16
|
+
* costs a grantee one chunk and everyone else nothing.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const BASE = '/session/service-access'
|
|
20
|
+
|
|
21
|
+
interface Grant {
|
|
22
|
+
identityUserId: string
|
|
23
|
+
name: string | null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface Credential {
|
|
27
|
+
prefix: string
|
|
28
|
+
identityUserId: string
|
|
29
|
+
name: string | null
|
|
30
|
+
expiresAt: number | null
|
|
31
|
+
lastSeenAt: number
|
|
32
|
+
live: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface State {
|
|
36
|
+
grants: Grant[]
|
|
37
|
+
totp: 'none' | 'pending' | 'active'
|
|
38
|
+
credentials: Credential[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const S = {
|
|
42
|
+
stack: { display: 'flex', flexDirection: 'column', gap: 8 },
|
|
43
|
+
line: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' },
|
|
44
|
+
grow: { flex: 1, minWidth: 0 },
|
|
45
|
+
mono: { fontFamily: 'ui-monospace, monospace', fontSize: '0.75rem', wordBreak: 'break-all' },
|
|
46
|
+
code: { width: '7ch', fontFamily: 'ui-monospace, monospace', letterSpacing: '0.1em' },
|
|
47
|
+
qr: { width: 168, height: 168 }
|
|
48
|
+
} satisfies Record<string, React.CSSProperties>
|
|
49
|
+
|
|
50
|
+
async function call<T>(path: string, body?: unknown): Promise<T> {
|
|
51
|
+
const res = await fetch(`${BASE}${path}`, {
|
|
52
|
+
method: body === undefined ? 'GET' : 'POST',
|
|
53
|
+
credentials: 'same-origin',
|
|
54
|
+
headers: body === undefined ? undefined : { 'Content-Type': 'application/json' },
|
|
55
|
+
body: body === undefined ? undefined : JSON.stringify(body)
|
|
56
|
+
})
|
|
57
|
+
const data = (await res.json().catch(() => ({}))) as T & { error?: string }
|
|
58
|
+
if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`)
|
|
59
|
+
return data
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function stepUp(): Promise<PasskeyAssertion> {
|
|
63
|
+
const client = await loadSignInClient()
|
|
64
|
+
if (!client?.stepUpPasskey) throw new Error('Passkeys unavailable here')
|
|
65
|
+
return client.stepUpPasskey()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function left(expiresAt: number | null): string {
|
|
69
|
+
if (expiresAt === null) return ''
|
|
70
|
+
const h = Math.floor((expiresAt - Date.now()) / 3_600_000)
|
|
71
|
+
return h >= 24 ? `${Math.floor(h / 24)}d left` : h >= 1 ? `${h}h left` : 'under 1h left'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const message = (err: unknown) => (err instanceof Error ? err.message : String(err))
|
|
75
|
+
|
|
76
|
+
export function ServiceAccess() {
|
|
77
|
+
const [state, setState] = useState<State | null>(null)
|
|
78
|
+
const [error, setError] = useState<string | null>(null)
|
|
79
|
+
const [busy, setBusy] = useState(false)
|
|
80
|
+
const [enrol, setEnrol] = useState<{ uri: string; secret: string } | null>(null)
|
|
81
|
+
const [code, setCode] = useState('')
|
|
82
|
+
const [minted, setMinted] = useState<{ key: string; name: string; expiresAt: number } | null>(
|
|
83
|
+
null
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const load = useCallback(async () => {
|
|
87
|
+
setState(await call<State>(''))
|
|
88
|
+
}, [])
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
load().catch((err: unknown) => setError(message(err)))
|
|
92
|
+
}, [load])
|
|
93
|
+
|
|
94
|
+
const run = async (work: () => Promise<void>) => {
|
|
95
|
+
setBusy(true)
|
|
96
|
+
setError(null)
|
|
97
|
+
try {
|
|
98
|
+
await work()
|
|
99
|
+
} catch (err) {
|
|
100
|
+
setError(message(err))
|
|
101
|
+
} finally {
|
|
102
|
+
setBusy(false)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!state) {
|
|
107
|
+
return (
|
|
108
|
+
<span className="settings-popout__hint" role={error ? 'alert' : undefined}>
|
|
109
|
+
{error ?? '…'}
|
|
110
|
+
</span>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const codeOk = /^\d{6}$/.test(code)
|
|
115
|
+
const codeInput = (
|
|
116
|
+
<input
|
|
117
|
+
value={code}
|
|
118
|
+
onChange={e => setCode(e.target.value.replace(/\D/g, '').slice(0, 6))}
|
|
119
|
+
inputMode="numeric"
|
|
120
|
+
autoComplete="one-time-code"
|
|
121
|
+
placeholder="123456"
|
|
122
|
+
aria-label="Authenticator code"
|
|
123
|
+
className="settings-popout__input"
|
|
124
|
+
style={S.code}
|
|
125
|
+
/>
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div style={S.stack}>
|
|
130
|
+
{state.totp !== 'active' && !enrol && (
|
|
131
|
+
<span style={S.line}>
|
|
132
|
+
<span className="settings-popout__label" style={S.grow}>
|
|
133
|
+
Authenticator
|
|
134
|
+
</span>
|
|
135
|
+
<button
|
|
136
|
+
type="button"
|
|
137
|
+
className="settings-popout__btn settings-popout__btn--primary"
|
|
138
|
+
disabled={busy}
|
|
139
|
+
onClick={() => {
|
|
140
|
+
run(async () => {
|
|
141
|
+
setEnrol(
|
|
142
|
+
await call<{ uri: string; secret: string }>('/totp/start', {
|
|
143
|
+
assertion: await stepUp()
|
|
144
|
+
})
|
|
145
|
+
)
|
|
146
|
+
}).catch(console.error)
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
Set up
|
|
150
|
+
</button>
|
|
151
|
+
</span>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
{enrol && (
|
|
155
|
+
<div style={S.stack}>
|
|
156
|
+
<span
|
|
157
|
+
role="img"
|
|
158
|
+
aria-label="Authenticator QR code"
|
|
159
|
+
style={S.qr}
|
|
160
|
+
dangerouslySetInnerHTML={{ __html: renderSVG(enrol.uri) }}
|
|
161
|
+
/>
|
|
162
|
+
<span style={S.mono}>{enrol.secret}</span>
|
|
163
|
+
<span style={S.line}>
|
|
164
|
+
{codeInput}
|
|
165
|
+
<button
|
|
166
|
+
type="button"
|
|
167
|
+
className="settings-popout__btn settings-popout__btn--primary"
|
|
168
|
+
disabled={busy || !codeOk}
|
|
169
|
+
onClick={() => {
|
|
170
|
+
run(async () => {
|
|
171
|
+
await call('/totp/confirm', { code })
|
|
172
|
+
setEnrol(null)
|
|
173
|
+
setCode('')
|
|
174
|
+
await load()
|
|
175
|
+
}).catch(console.error)
|
|
176
|
+
}}
|
|
177
|
+
>
|
|
178
|
+
Confirm
|
|
179
|
+
</button>
|
|
180
|
+
</span>
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
183
|
+
|
|
184
|
+
{minted && (
|
|
185
|
+
<div style={S.stack}>
|
|
186
|
+
<span className="settings-popout__label">
|
|
187
|
+
{minted.name} · shown once · {left(minted.expiresAt)}
|
|
188
|
+
</span>
|
|
189
|
+
<span style={S.mono}>{minted.key}</span>
|
|
190
|
+
<span style={S.line}>
|
|
191
|
+
<button
|
|
192
|
+
type="button"
|
|
193
|
+
className="settings-popout__btn"
|
|
194
|
+
onClick={() => {
|
|
195
|
+
navigator.clipboard.writeText(minted.key).catch(console.error)
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
198
|
+
Copy
|
|
199
|
+
</button>
|
|
200
|
+
<button type="button" className="settings-popout__btn" onClick={() => setMinted(null)}>
|
|
201
|
+
Hide
|
|
202
|
+
</button>
|
|
203
|
+
</span>
|
|
204
|
+
</div>
|
|
205
|
+
)}
|
|
206
|
+
|
|
207
|
+
{state.totp === 'active' && (
|
|
208
|
+
<span style={S.line}>
|
|
209
|
+
<span className="settings-popout__label" style={S.grow}>
|
|
210
|
+
Code
|
|
211
|
+
</span>
|
|
212
|
+
{codeInput}
|
|
213
|
+
</span>
|
|
214
|
+
)}
|
|
215
|
+
|
|
216
|
+
{state.totp === 'active' &&
|
|
217
|
+
state.grants.map(g => (
|
|
218
|
+
<span key={g.identityUserId} style={S.line}>
|
|
219
|
+
<span className="settings-popout__label" style={{ ...S.grow, ...S.mono }}>
|
|
220
|
+
{g.name ?? g.identityUserId}
|
|
221
|
+
</span>
|
|
222
|
+
<button
|
|
223
|
+
type="button"
|
|
224
|
+
className="settings-popout__btn settings-popout__btn--primary"
|
|
225
|
+
disabled={busy || !codeOk}
|
|
226
|
+
onClick={() => {
|
|
227
|
+
run(async () => {
|
|
228
|
+
const assertion = await stepUp()
|
|
229
|
+
const out = await call<{ key: string; name: string; expiresAt: number }>(
|
|
230
|
+
'/mint',
|
|
231
|
+
{
|
|
232
|
+
identityUserId: g.identityUserId,
|
|
233
|
+
assertion,
|
|
234
|
+
totp: code
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
setCode('')
|
|
238
|
+
setMinted(out)
|
|
239
|
+
await load()
|
|
240
|
+
}).catch(console.error)
|
|
241
|
+
}}
|
|
242
|
+
>
|
|
243
|
+
Mint
|
|
244
|
+
</button>
|
|
245
|
+
</span>
|
|
246
|
+
))}
|
|
247
|
+
|
|
248
|
+
{state.credentials
|
|
249
|
+
.filter(c => c.live)
|
|
250
|
+
.map(c => (
|
|
251
|
+
<span key={c.prefix} style={S.line}>
|
|
252
|
+
<span style={{ ...S.grow, ...S.mono }}>
|
|
253
|
+
{c.prefix}… {c.name}
|
|
254
|
+
</span>
|
|
255
|
+
<span className="settings-popout__hint">{left(c.expiresAt)}</span>
|
|
256
|
+
<button
|
|
257
|
+
type="button"
|
|
258
|
+
className="settings-popout__btn"
|
|
259
|
+
disabled={busy}
|
|
260
|
+
onClick={() => {
|
|
261
|
+
run(async () => {
|
|
262
|
+
await call('/revoke', { prefix: c.prefix })
|
|
263
|
+
await load()
|
|
264
|
+
}).catch(console.error)
|
|
265
|
+
}}
|
|
266
|
+
>
|
|
267
|
+
Revoke
|
|
268
|
+
</button>
|
|
269
|
+
</span>
|
|
270
|
+
))}
|
|
271
|
+
|
|
272
|
+
{error && (
|
|
273
|
+
<span className="settings-popout__error" role="alert">
|
|
274
|
+
{error}
|
|
275
|
+
</span>
|
|
276
|
+
)}
|
|
277
|
+
</div>
|
|
278
|
+
)
|
|
279
|
+
}
|
|
@@ -56,6 +56,10 @@ const WHOAMI_GLOBAL = '__hadokuWhoami'
|
|
|
56
56
|
*
|
|
57
57
|
* `passkeys` (edge-router ≥ 2026-10-28) counts the passkeys that sign this
|
|
58
58
|
* session in. Absent means unknown, never none.
|
|
59
|
+
*
|
|
60
|
+
* `serviceGrants` (edge-router ≥ 2026-10-31) counts the service identities
|
|
61
|
+
* this person may mint their own credentials for; Service access shows only
|
|
62
|
+
* above zero.
|
|
59
63
|
*/
|
|
60
64
|
interface WhoamiBody {
|
|
61
65
|
valid?: boolean
|
|
@@ -64,6 +68,7 @@ interface WhoamiBody {
|
|
|
64
68
|
contentLevel?: number
|
|
65
69
|
maxContentLevel?: number
|
|
66
70
|
passkeys?: number
|
|
71
|
+
serviceGrants?: number
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
// Hard timeout on every settings request. Without it, a request throttled by
|
|
@@ -293,6 +298,17 @@ export interface SignInClient {
|
|
|
293
298
|
signInWithPasskey?: (opts?: { signal?: AbortSignal }) => Promise<SignInResult>
|
|
294
299
|
/** Saves a passkey for the signed-in person on this device. Throws Error(message). */
|
|
295
300
|
addPasskey?: (opts?: { signal?: AbortSignal }) => Promise<{ saved: boolean; passkeys: number }>
|
|
301
|
+
/** A fresh passkey tap from the signed-in person; the assertion to post. Throws Error(message). */
|
|
302
|
+
stepUpPasskey?: (opts?: { signal?: AbortSignal }) => Promise<PasskeyAssertion>
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** What `stepUpPasskey` resolves to: the WebAuthn assertion, base64url fields. */
|
|
306
|
+
export interface PasskeyAssertion {
|
|
307
|
+
id: string
|
|
308
|
+
clientDataJSON: string
|
|
309
|
+
authenticatorData: string
|
|
310
|
+
signature: string
|
|
311
|
+
userHandle: string | null
|
|
296
312
|
}
|
|
297
313
|
|
|
298
314
|
export function loadSignInClient(): Promise<SignInClient | null> {
|
|
@@ -308,6 +324,10 @@ export function loadSignInClient(): Promise<SignInClient | null> {
|
|
|
308
324
|
addPasskey:
|
|
309
325
|
typeof m.addPasskey === 'function'
|
|
310
326
|
? (m.addPasskey as SignInClient['addPasskey'])
|
|
327
|
+
: undefined,
|
|
328
|
+
stepUpPasskey:
|
|
329
|
+
typeof m.stepUpPasskey === 'function'
|
|
330
|
+
? (m.stepUpPasskey as SignInClient['stepUpPasskey'])
|
|
311
331
|
: undefined
|
|
312
332
|
}
|
|
313
333
|
: null,
|
|
@@ -353,6 +373,8 @@ export interface SettingsSnapshot {
|
|
|
353
373
|
content: ContentLevelState | null
|
|
354
374
|
/** Passkeys that sign this person in; null = unknown (older edge, host-supplied identity). */
|
|
355
375
|
passkeys: number | null
|
|
376
|
+
/** Service identities this person may mint for; 0 when unknown. */
|
|
377
|
+
serviceGrants: number
|
|
356
378
|
}
|
|
357
379
|
|
|
358
380
|
/** Identity the host already knows, so we don't ask the server for it again. */
|
|
@@ -432,7 +454,12 @@ async function resolveSettings(hints: SettingsPrefetchHints): Promise<SettingsSn
|
|
|
432
454
|
contentPromise,
|
|
433
455
|
bodyPromise
|
|
434
456
|
])
|
|
435
|
-
return {
|
|
457
|
+
return {
|
|
458
|
+
identity,
|
|
459
|
+
content,
|
|
460
|
+
passkeys: passkeysFromWhoami(body),
|
|
461
|
+
serviceGrants: body?.valid && typeof body.serviceGrants === 'number' ? body.serviceGrants : 0
|
|
462
|
+
}
|
|
436
463
|
}
|
|
437
464
|
|
|
438
465
|
function passkeysFromWhoami(body: WhoamiBody | null): number | null {
|