From 6514bca7277fab2fc3fc52da6aa3b9f6ce4b98ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Leszczy=C5=84ski?= Date: Thu, 18 Jul 2024 09:50:55 -0700 Subject: [PATCH] Migrate to TypeScript (#345) --- .github/workflows/check_lib.yml | 1 + .github/workflows/prod_build_lib.yml | 1 + cli/eslint.config.js | 14 + cli/package.json | 9 + cli/src.ts/{actions.js => actions.ts} | 10 +- cli/src.ts/{args_bridge.js => args_bridge.ts} | 0 cli/src.ts/{args_cli.js => args_cli.ts} | 46 +- .../{args_gateway.js => args_gateway.ts} | 0 cli/src.ts/{cli.js => cli.ts} | 30 +- .../{entry_bridge.js => entry_bridge.ts} | 2 +- cli/src.ts/{entry_cli.js => entry_cli.ts} | 0 .../{entry_gateway.js => entry_gateway.ts} | 47 +- cli/src.ts/{util.js => util.ts} | 9 +- cli/src.ts/{version.js => version.ts} | 4 +- cli/src.ts/{ws_server.js => ws_server.ts} | 84 +- cli/tsconfig.json | 7 +- cli/webpack.config.js | 13 +- cli/yarn.lock | 583 +++++++++++++- core/.gitignore | 1 + core/eslint.config.js | 14 + core/package.json | 18 +- core/src.ts/api/{common.js => common.ts} | 3 + core/src.ts/api/{desktop.js => desktop.ts} | 0 .../api/{react-native.js => react-native.ts} | 0 core/src.ts/api/{web.js => web.ts} | 3 +- core/src.ts/drivers/{common.js => common.ts} | 12 +- .../drivers/{credential.js => credential.ts} | 31 +- .../{nfc_manager.js => nfc_manager.ts} | 18 +- core/src.ts/drivers/{pcsc.js => pcsc.ts} | 81 +- .../drivers/{read_ndef.js => read_ndef.ts} | 21 +- core/src.ts/drivers/{web.js => web.ts} | 50 +- core/src.ts/drivers/{webnfc.js => webnfc.ts} | 107 +-- core/src.ts/halo/{bridge.js => bridge.ts} | 94 ++- core/src.ts/halo/cmd_exec.ts | 130 ++++ core/src.ts/halo/{cmdcodes.js => cmdcodes.ts} | 0 core/src.ts/halo/command_types.ts | 220 ++++++ core/src.ts/halo/{commands.js => commands.ts} | 361 +++++---- core/src.ts/halo/{errors.js => errors.ts} | 6 +- .../halo/{exceptions.js => exceptions.ts} | 53 +- core/src.ts/halo/{flags.js => flags.ts} | 6 +- .../halo/gateway/{executor.js => executor.ts} | 43 +- .../gateway/{requestor.js => requestor.ts} | 69 +- core/src.ts/halo/{jwe_util.js => jwe_util.ts} | 23 +- core/src.ts/halo/{keyflags.js => keyflags.ts} | 3 +- core/src.ts/halo/{tests.js => tests.ts} | 68 +- core/src.ts/halo/{util.js => util.ts} | 76 +- core/src.ts/{index.tsx => index.ts} | 0 core/src.ts/types.ts | 229 ++++++ core/src.ts/types_webnfc.ts | 83 ++ .../web/{soft_prompt.js => soft_prompt.ts} | 10 +- core/src.ts/web/{web_apis.js => web_apis.ts} | 0 .../src.ts/web/{web_utils.js => web_utils.ts} | 30 +- core/src.ts/web/{weblib.js => weblib.ts} | 2 + core/tsconfig.commonjs.json | 8 + core/tsconfig.json | 7 +- core/webpack.config.js | 13 +- core/yarn.lock | 724 +++++++++++++++++- 57 files changed, 2869 insertions(+), 608 deletions(-) create mode 100644 cli/eslint.config.js rename cli/src.ts/{actions.js => actions.ts} (52%) rename cli/src.ts/{args_bridge.js => args_bridge.ts} (100%) rename cli/src.ts/{args_cli.js => args_cli.ts} (80%) rename cli/src.ts/{args_gateway.js => args_gateway.ts} (100%) rename cli/src.ts/{cli.js => cli.ts} (84%) rename cli/src.ts/{entry_bridge.js => entry_bridge.ts} (93%) rename cli/src.ts/{entry_cli.js => entry_cli.ts} (100%) rename cli/src.ts/{entry_gateway.js => entry_gateway.ts} (82%) rename cli/src.ts/{util.js => util.ts} (56%) rename cli/src.ts/{version.js => version.ts} (89%) rename cli/src.ts/{ws_server.js => ws_server.ts} (82%) create mode 100644 core/eslint.config.js rename core/src.ts/api/{common.js => common.ts} (94%) rename core/src.ts/api/{desktop.js => desktop.ts} (100%) rename core/src.ts/api/{react-native.js => react-native.ts} (100%) rename core/src.ts/api/{web.js => web.ts} (92%) rename core/src.ts/drivers/{common.js => common.ts} (88%) rename core/src.ts/drivers/{credential.js => credential.ts} (73%) rename core/src.ts/drivers/{nfc_manager.js => nfc_manager.ts} (57%) rename core/src.ts/drivers/{pcsc.js => pcsc.ts} (66%) rename core/src.ts/drivers/{read_ndef.js => read_ndef.ts} (79%) rename core/src.ts/drivers/{web.js => web.ts} (65%) rename core/src.ts/drivers/{webnfc.js => webnfc.ts} (68%) rename core/src.ts/halo/{bridge.js => bridge.ts} (67%) create mode 100644 core/src.ts/halo/cmd_exec.ts rename core/src.ts/halo/{cmdcodes.js => cmdcodes.ts} (100%) create mode 100644 core/src.ts/halo/command_types.ts rename core/src.ts/halo/{commands.js => commands.ts} (61%) rename core/src.ts/halo/{errors.js => errors.ts} (94%) rename core/src.ts/halo/{exceptions.js => exceptions.ts} (65%) rename core/src.ts/halo/{flags.js => flags.ts} (86%) rename core/src.ts/halo/gateway/{executor.js => executor.ts} (73%) rename core/src.ts/halo/gateway/{requestor.js => requestor.ts} (81%) rename core/src.ts/halo/{jwe_util.js => jwe_util.ts} (77%) rename core/src.ts/halo/{keyflags.js => keyflags.ts} (90%) rename core/src.ts/halo/{tests.js => tests.ts} (66%) rename core/src.ts/halo/{util.js => util.ts} (70%) rename core/src.ts/{index.tsx => index.ts} (100%) create mode 100644 core/src.ts/types.ts create mode 100644 core/src.ts/types_webnfc.ts rename core/src.ts/web/{soft_prompt.js => soft_prompt.ts} (95%) rename core/src.ts/web/{web_apis.js => web_apis.ts} (100%) rename core/src.ts/web/{web_utils.js => web_utils.ts} (72%) rename core/src.ts/web/{weblib.js => weblib.ts} (79%) create mode 100644 core/tsconfig.commonjs.json diff --git a/.github/workflows/check_lib.yml b/.github/workflows/check_lib.yml index a859f52..a3aa500 100644 --- a/.github/workflows/check_lib.yml +++ b/.github/workflows/check_lib.yml @@ -32,6 +32,7 @@ jobs: run: | cd core ./node_modules/.bin/tsc + ./node_modules/.bin/tsc -p tsconfig.commonjs.json - name: Run webpack (root) run: | cd core diff --git a/.github/workflows/prod_build_lib.yml b/.github/workflows/prod_build_lib.yml index fe45276..9ca177d 100644 --- a/.github/workflows/prod_build_lib.yml +++ b/.github/workflows/prod_build_lib.yml @@ -148,6 +148,7 @@ jobs: cd core yarn install --frozen-lockfile --production=false ./node_modules/.bin/tsc + ./node_modules/.bin/tsc -p tsconfig.commonjs.json - name: Publish package to npmjs run: yarn publish env: diff --git a/cli/eslint.config.js b/cli/eslint.config.js new file mode 100644 index 0000000..8dddcde --- /dev/null +++ b/cli/eslint.config.js @@ -0,0 +1,14 @@ +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + "rules": { + "@typescript-eslint/no-unused-vars": "off" + } + } +); diff --git a/cli/package.json b/cli/package.json index 72d84f8..9db3132 100644 --- a/cli/package.json +++ b/cli/package.json @@ -44,6 +44,7 @@ }, "dependencies": { "@arx-research/libhalo": "../core", + "argparse": "^2.0.1", "bufferutil": "^4.0.8", "express": "^4.19.2", "jsonwebtoken": "^9.0.2", @@ -53,10 +54,18 @@ "ws": "^8.18.0" }, "devDependencies": { + "@eslint/js": "^9.7.0", + "@types/argparse": "^2.0.16", + "@types/express": "^4.17.21", + "@types/jsonwebtoken": "^9.0.6", + "@types/nunjucks": "^3.2.6", + "@types/ws": "^8.5.11", "@yao-pkg/pkg": "^5.12.0", + "eslint": "^9.7.0", "resedit": "^2.0.2", "ts-loader": "^9.5.1", "typescript": "^5.5.3", + "typescript-eslint": "^7.16.0", "webpack": "^5.92.1", "webpack-cli": "^5.1.4" } diff --git a/cli/src.ts/actions.js b/cli/src.ts/actions.ts similarity index 52% rename from cli/src.ts/actions.js rename to cli/src.ts/actions.ts index d473e35..fe8fb8b 100644 --- a/cli/src.ts/actions.js +++ b/cli/src.ts/actions.ts @@ -4,18 +4,18 @@ * License: MIT */ -import {Action, ArgumentError} from "argparse"; +import {Action, ActionConstructorOptions, ArgumentError, ArgumentParser, Namespace} from "argparse"; class JSONParseAction extends Action { - constructor() { - super(...arguments); + constructor(options: ActionConstructorOptions) { + super(options); } - call(parser, namespace, values, option_string) { + call(parser: ArgumentParser, namespace: Namespace, values: string, option_string: string | null) { try { namespace[this.dest] = JSON.parse(values); } catch (e) { - throw ArgumentError(this, e.message); + throw new ArgumentError(this, ( e).message); } } } diff --git a/cli/src.ts/args_bridge.js b/cli/src.ts/args_bridge.ts similarity index 100% rename from cli/src.ts/args_bridge.js rename to cli/src.ts/args_bridge.ts diff --git a/cli/src.ts/args_cli.js b/cli/src.ts/args_cli.ts similarity index 80% rename from cli/src.ts/args_cli.js rename to cli/src.ts/args_cli.ts index 1f751cb..f2f56d6 100644 --- a/cli/src.ts/args_cli.js +++ b/cli/src.ts/args_cli.ts @@ -21,7 +21,7 @@ subparsers.add_parser("version", {help: "Get tag version."}); subparsers.add_parser("cli_version", {help: "Get halocli build version."}); if (process.env.__UNSAFE_ENABLE_TESTS === "1") { - let testParser = subparsers.add_parser("test", {help: "Run test suite against the tag. Please do not use this command."}); + const testParser = subparsers.add_parser("test", {help: "Run test suite against the tag. Please do not use this command."}); testParser.add_argument("--unsafe", { help: "I understand that this command might reconfigure the tag in undesired way and that it might be " + "not possible to rollback certain changes made by this command.", @@ -33,7 +33,7 @@ if (process.env.__UNSAFE_ENABLE_TESTS === "1") { subparsers.add_parser("read_ndef", {help: "Read dynamic URL on the tag."}); -let signParser = subparsers.add_parser("sign", {help: "Sign message using ECDSA/Keccak algorithm."}); +const signParser = subparsers.add_parser("sign", {help: "Sign message using ECDSA/Keccak algorithm."}); signParser.add_argument("-k", "--key-no", { dest: 'keyNo', help: "Number of the key slot to use.", @@ -62,7 +62,7 @@ signParser.add_argument("--legacy-sign-command", { "default": false }); -let signRandomParser = subparsers.add_parser("sign_random", {help: "Sign random digest using key slot #2."}); +const signRandomParser = subparsers.add_parser("sign_random", {help: "Sign random digest using key slot #2."}); signRandomParser.add_argument("-k", "--key-no", { dest: 'keyNo', help: "Number of the key slot to use.", @@ -70,7 +70,7 @@ signRandomParser.add_argument("-k", "--key-no", { 'default': 2 }); -let signChallenge = subparsers.add_parser("sign_challenge", {help: "Sign challenge using ECDSA/Keccak algorithm."}); +const signChallenge = subparsers.add_parser("sign_challenge", {help: "Sign challenge using ECDSA/Keccak algorithm."}); signChallenge.add_argument("-k", "--key-no", { dest: 'keyNo', help: "Number of the key slot to use.", @@ -79,7 +79,7 @@ signChallenge.add_argument("-k", "--key-no", { }); signChallenge.add_argument("-c", "--challenge", {help: "Challenge to be signed (32 bytes hex)."}); -let writeLatchParser = subparsers.add_parser("write_latch", {help: "Write value into the latch slot."}); +const writeLatchParser = subparsers.add_parser("write_latch", {help: "Write value into the latch slot."}); writeLatchParser.add_argument("-n", "--latch-no", { dest: "latchNo", help: "Number of the latch slot to use.", @@ -91,7 +91,7 @@ writeLatchParser.add_argument("-d", "--data", { required: true }); -let setNDEFCfgParser = subparsers.add_parser("cfg_ndef", {help: "Configure the tag's NDEF data."}); +const setNDEFCfgParser = subparsers.add_parser("cfg_ndef", {help: "Configure the tag's NDEF data."}); setNDEFCfgParser.add_argument("--flag-use-text", { dest: "flagUseText", help: "Use text NDEF record instead of the URL record.", @@ -148,7 +148,7 @@ setNDEFCfgParser.add_argument("--flag-show-latch2-sig", { }); setNDEFCfgParser.add_argument("--flag-hide-rndsig", { dest: "flagHideRNDSIG", - help: "Hide \"rnd\" and \"rndsig\" fields. The counter\'s signature will be generated only upon manual request.", + help: "Hide \"rnd\" and \"rndsig\" fields. The counter's signature will be generated only upon manual request.", action: 'store_true', required: false }); @@ -189,7 +189,7 @@ setNDEFCfgParser.add_argument("--pkn", { required: false }); -let genKeyParser = subparsers.add_parser("gen_key", {help: "Perform the first step of the key generation procedure."}); +const genKeyParser = subparsers.add_parser("gen_key", {help: "Perform the first step of the key generation procedure."}); genKeyParser.add_argument("-k", "--key-no", { dest: 'keyNo', type: 'int', @@ -202,7 +202,7 @@ genKeyParser.add_argument("--entropy", { required: false }); -let genKeyConfirmParser = subparsers.add_parser("gen_key_confirm", {help: "Confirm the public key generated by gen_key command."}); +const genKeyConfirmParser = subparsers.add_parser("gen_key_confirm", {help: "Confirm the public key generated by gen_key command."}); genKeyConfirmParser.add_argument("-k", "--key-no", { dest: 'keyNo', type: 'int', @@ -215,7 +215,7 @@ genKeyConfirmParser.add_argument("--public-key", { required: true }); -let genKeyFinalizeParser = subparsers.add_parser("gen_key_finalize", {help: "Finalize key generation procedure."}); +const genKeyFinalizeParser = subparsers.add_parser("gen_key_finalize", {help: "Finalize key generation procedure."}); genKeyFinalizeParser.add_argument("-k", "--key-no", { dest: 'keyNo', type: 'int', @@ -228,7 +228,7 @@ genKeyFinalizeParser.add_argument("--password", { required: false }); -let fullGenKeyParser = subparsers.add_parser("full_gen_key", {help: "Perform the full key generation procedure."}); +const fullGenKeyParser = subparsers.add_parser("full_gen_key", {help: "Perform the full key generation procedure."}); fullGenKeyParser.add_argument("-k", "--key-no", { dest: 'keyNo', type: 'int', @@ -246,7 +246,7 @@ fullGenKeyParser.add_argument("--password", { required: false }); -let setPasswordParser = subparsers.add_parser("set_password", {help: "Set password for slot #3."}); +const setPasswordParser = subparsers.add_parser("set_password", {help: "Set password for slot #3."}); setPasswordParser.add_argument("-k", "--key-no", { dest: 'keyNo', 'default': 3, @@ -259,7 +259,7 @@ setPasswordParser.add_argument("--password", { required: true }); -let replacePasswordParser = subparsers.add_parser("replace_password", {help: "Replace password for slot #3."}); +const replacePasswordParser = subparsers.add_parser("replace_password", {help: "Replace password for slot #3."}); replacePasswordParser.add_argument("-k", "--key-no", { dest: 'keyNo', 'default': 3, @@ -277,7 +277,7 @@ replacePasswordParser.add_argument("--new-password", { required: true }); -let unsetPasswordParser = subparsers.add_parser("unset_password", {help: "Unset password for slot #3."}); +const unsetPasswordParser = subparsers.add_parser("unset_password", {help: "Unset password for slot #3."}); unsetPasswordParser.add_argument("-k", "--key-no", { dest: 'keyNo', 'default': 3, @@ -292,7 +292,7 @@ unsetPasswordParser.add_argument("--password", { subparsers.add_parser("get_pkeys", {help: "Get tag's public keys #1, #2 and #3."}); -let getKeyInfoParser = subparsers.add_parser("get_key_info", {help: "Get key information."}); +const getKeyInfoParser = subparsers.add_parser("get_key_info", {help: "Get key information."}); getKeyInfoParser.add_argument("-k", "--key-no", { dest: 'keyNo', type: 'int', @@ -301,14 +301,14 @@ getKeyInfoParser.add_argument("-k", "--key-no", { subparsers.add_parser("get_transport_pk", {help: "[Key backup] Get secure transport credentials from the target HaLo tag."}); -let loadTransportPKParser = subparsers.add_parser("load_transport_pk", {help: "[Key backup] Load target tag's secure transport credentials into source tag."}); +const loadTransportPKParser = subparsers.add_parser("load_transport_pk", {help: "[Key backup] Load target tag's secure transport credentials into source tag."}); loadTransportPKParser.add_argument("--data", { dest: 'data', help: "Source tag's secure transport credentials.", required: true }); -let exportKeyParser = subparsers.add_parser("export_key", {help: "[Key backup] Export encrypted key pair from the source HaLo tag."}); +const exportKeyParser = subparsers.add_parser("export_key", {help: "[Key backup] Export encrypted key pair from the source HaLo tag."}); exportKeyParser.add_argument("-k", "--key-no", { dest: 'keyNo', type: 'int', @@ -326,7 +326,7 @@ exportKeyParser.add_argument("--data", { required: true }); -let importKeyInitParser = subparsers.add_parser("import_key_init", {help: "[Key backup] Initialize import of the encrypted key pair into the target HaLo tag."}); +const importKeyInitParser = subparsers.add_parser("import_key_init", {help: "[Key backup] Initialize import of the encrypted key pair into the target HaLo tag."}); importKeyInitParser.add_argument("-k", "--key-no", { dest: 'keyNo', type: 'int', @@ -339,7 +339,7 @@ importKeyInitParser.add_argument("--data", { required: true }); -let importKeyParser = subparsers.add_parser("import_key", {help: "[Key backup] Finalize import of the encrypted key pair into the target HaLo tag."}); +const importKeyParser = subparsers.add_parser("import_key", {help: "[Key backup] Finalize import of the encrypted key pair into the target HaLo tag."}); importKeyParser.add_argument("-k", "--key-no", { dest: 'keyNo', type: 'int', @@ -347,7 +347,7 @@ importKeyParser.add_argument("-k", "--key-no", { required: true }); -let getDataStructParser = subparsers.add_parser("get_data_struct", {help: "Get certain data from the tag."}); +const getDataStructParser = subparsers.add_parser("get_data_struct", {help: "Get certain data from the tag."}); getDataStructParser.add_argument("-s", "--spec", { dest: 'spec', type: 'str', @@ -355,7 +355,7 @@ getDataStructParser.add_argument("-s", "--spec", { required: true }); -let getGraffitiParser = subparsers.add_parser("get_graffiti", {help: "Get graffiti data from the tag."}); +const getGraffitiParser = subparsers.add_parser("get_graffiti", {help: "Get graffiti data from the tag."}); getGraffitiParser.add_argument("-n", "--slot-no", { dest: 'slotNo', type: 'int', @@ -363,7 +363,7 @@ getGraffitiParser.add_argument("-n", "--slot-no", { required: true }); -let storeGraffitiParser = subparsers.add_parser("store_graffiti", {help: "Store graffiti data to the tag."}); +const storeGraffitiParser = subparsers.add_parser("store_graffiti", {help: "Store graffiti data to the tag."}); storeGraffitiParser.add_argument("-n", "--slot-no", { dest: 'slotNo', type: 'int', @@ -380,7 +380,7 @@ storeGraffitiParser.add_argument("--data", { subparsers.add_parser("pcsc_detect", {help: "Detect PC/SC readers and HaLo tags (for debugging)."}); function parseArgs() { - let args = parser.parse_args(); + const args = parser.parse_args(); if (!args.name) { printVersionInfo(); diff --git a/cli/src.ts/args_gateway.js b/cli/src.ts/args_gateway.ts similarity index 100% rename from cli/src.ts/args_gateway.js rename to cli/src.ts/args_gateway.ts diff --git a/cli/src.ts/cli.js b/cli/src.ts/cli.ts similarity index 84% rename from cli/src.ts/cli.js rename to cli/src.ts/cli.ts index e285610..1c71b10 100644 --- a/cli/src.ts/cli.js +++ b/cli/src.ts/cli.ts @@ -5,6 +5,8 @@ */ import {Buffer} from 'buffer/index.js'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error import {NFC} from 'nfc-pcsc'; import open from 'open'; @@ -19,16 +21,18 @@ import { wsEventReaderDisconnected } from "./ws_server.js"; import {execHaloCmdPCSC} from "@arx-research/libhalo/api/desktop"; +import {HaloCommandObject, Reader} from "@arx-research/libhalo/types"; +import {Namespace} from "argparse"; const nfc = new NFC(); -let stopPCSCTimeout = null; +let stopPCSCTimeout: number | null = null; let isConnected = false; let isClosing = false; -async function checkCard(reader) { +async function checkCard(reader: Reader) { // try to select Halo ETH Core Layer try { - let resSelect = await reader.transmit(Buffer.from("00A4040007481199130E9F0100", "hex"), 255); + const resSelect = await reader.transmit(Buffer.from("00A4040007481199130E9F0100", "hex"), 255); return resSelect.compare(Buffer.from([0x90, 0x00])) === 0; } catch (e) { console.error(e); @@ -36,8 +40,8 @@ async function checkCard(reader) { } } -function runHalo(entryMode, args) { - nfc.on('reader', reader => { +function runHalo(entryMode: string, args: Namespace) { + nfc.on('reader', (reader: Reader) => { reader.autoProcessing = false; reader.on('error', err => { @@ -70,11 +74,11 @@ function runHalo(entryMode, args) { console.log("Tag inserted:", reader.reader.name, '(Type: ' + card.type + ', ATR: ' + card.atr.toString('hex').toUpperCase() + ')'); } - clearTimeout(stopPCSCTimeout); + clearTimeout(stopPCSCTimeout!); stopPCSCTimeout = setTimeout(stopPCSC, 4000, "timeout", args.output); (async () => { - let res = await checkCard(reader); + const res = await checkCard(reader); if (res) { clearTimeout(stopPCSCTimeout); @@ -86,7 +90,7 @@ function runHalo(entryMode, args) { res = {"status": "ok"}; } else if (args.name === "test") { res = await __runTestSuite({"__this_is_unsafe": true}, - "pcsc", async (command) => await execHaloCmdPCSC(command, reader)); + "pcsc", async (command: HaloCommandObject) => await execHaloCmdPCSC(command, reader)); } else { try { res = await execHaloCmdPCSC(args, reader); @@ -94,7 +98,7 @@ function runHalo(entryMode, args) { if (args.output === "color") { console.error(e); } else { - console.log(JSON.stringify({"_exception": {"message": String(e), "stack": e.stack}})); + console.log(JSON.stringify({"_exception": {"message": String(e), "stack": ( e).stack}})); } } } @@ -130,14 +134,14 @@ function runHalo(entryMode, args) { }); }); - nfc.on('error', err => { + nfc.on('error', (err: Error) => { if (!isClosing) { console.log('an error occurred', err); } }); - function stopPCSC(code, output) { - clearTimeout(stopPCSCTimeout); + function stopPCSC(code: string, output: string) { + clearTimeout(stopPCSCTimeout!); if (code === "error" && output === "color") { console.error('Command execution failed.'); @@ -149,7 +153,7 @@ function runHalo(entryMode, args) { } } - for (let rdrName in nfc.readers) { + for (const rdrName in nfc.readers) { nfc.readers[rdrName].close(); } diff --git a/cli/src.ts/entry_bridge.js b/cli/src.ts/entry_bridge.ts similarity index 93% rename from cli/src.ts/entry_bridge.js rename to cli/src.ts/entry_bridge.ts index a90c051..f0f216e 100644 --- a/cli/src.ts/entry_bridge.js +++ b/cli/src.ts/entry_bridge.ts @@ -8,7 +8,7 @@ import {parseArgs} from './args_bridge.js'; import {runHalo} from "./cli.js"; import {printVersionInfo} from "./version.js"; -let args = parseArgs(); +const args = parseArgs(); if (!args) { process.exit(0); diff --git a/cli/src.ts/entry_cli.js b/cli/src.ts/entry_cli.ts similarity index 100% rename from cli/src.ts/entry_cli.js rename to cli/src.ts/entry_cli.ts diff --git a/cli/src.ts/entry_gateway.js b/cli/src.ts/entry_gateway.ts similarity index 82% rename from cli/src.ts/entry_gateway.js rename to cli/src.ts/entry_gateway.ts index 6d9b585..3dd11e8 100644 --- a/cli/src.ts/entry_gateway.js +++ b/cli/src.ts/entry_gateway.ts @@ -6,7 +6,8 @@ import {webcrypto as crypto} from 'crypto'; import express from "express"; -import {WebSocketServer} from 'ws'; +import {RawData, WebSocket, WebSocketServer} from 'ws'; +import {IncomingMessage} from "http"; import queryString from 'query-string'; import nunjucks from "nunjucks"; import {parse} from "url"; @@ -14,27 +15,35 @@ import {parse} from "url"; import {parseArgs} from './args_gateway.js'; import {printVersionInfo, getBuildInfo} from "./version.js"; import {dirname} from "./util.js"; +import {Namespace} from "argparse"; -let buildInfo = getBuildInfo(); +const buildInfo = getBuildInfo(); const REQUESTOR_SESS_LIMIT = 10 * 60 * 1000; const MAX_SESSION_LIMIT = 1000; -let args = parseArgs(); +const args = parseArgs(); if (!args) { process.exit(0); } -let sessionIds = {}; +interface SocketState { + requestor: WebSocket, + executor: WebSocket | null + requestUID: null + reconnects: number +} -function processRequestor(ws, req) { +const sessionIds: Record = {}; + +function processRequestor(ws: WebSocket, req: IncomingMessage) { if (Object.keys(sessionIds).length >= MAX_SESSION_LIMIT) { ws.close(4053, "Too many connections."); return; } - let sessionId = Buffer.from(crypto.getRandomValues(new Uint8Array(16))).toString('base64url'); + const sessionId = Buffer.from(crypto.getRandomValues(new Uint8Array(16))).toString('base64url'); let ipStr = 'IP: ' + req.socket.remoteAddress; if (req.headers['x-forwarded-for']) { @@ -50,7 +59,7 @@ function processRequestor(ws, req) { "reconnects": 0 }; - let sobj = sessionIds[sessionId]; + const sobj = sessionIds[sessionId]; setTimeout(() => { ws.close(4080, "Session timed out."); @@ -68,7 +77,7 @@ function processRequestor(ws, req) { }); ws.on('message', function message(data) { - let obj = JSON.parse(data); + const obj = JSON.parse(data.toString('utf-8')); if (obj.type === "keepalive") { // ignore @@ -101,13 +110,13 @@ function processRequestor(ws, req) { })); } -function processExecutor(ws, req, sessionId) { - if (!sessionIds.hasOwnProperty(sessionId)) { +function processExecutor(ws: WebSocket, req: IncomingMessage, sessionId: string) { + if (!Object.prototype.hasOwnProperty.call(sessionIds, sessionId)) { ws.close(4052, "No such session ID."); return; } - let sobj = sessionIds[sessionId]; + const sobj = sessionIds[sessionId]; let ipStr = 'IP: ' + req.socket.remoteAddress; if (req.headers['x-forwarded-for']) { @@ -138,8 +147,8 @@ function processExecutor(ws, req, sessionId) { sobj.requestor.send(JSON.stringify({"type": "executor_disconnected"})); }); - ws.on('message', (data) => { - let obj = JSON.parse(data); + ws.on('message', (data: RawData) => { + const obj = JSON.parse(data.toString('utf-8')); if (obj.type === "keepalive") { // ignore @@ -165,11 +174,11 @@ function processExecutor(ws, req, sessionId) { ws.send(JSON.stringify({"type": "ping"})); } -function createServer(args) { +function createServer(args: Namespace) { const app = express(); const server = app.listen(args.listenPort, args.listenHost); - let wss = new WebSocketServer({ + const wss = new WebSocketServer({ noServer: true, maxPayload: 6 * 1024 // max packet size - 6 kB }); @@ -199,7 +208,7 @@ function createServer(args) { }); server.on('upgrade', (request, socket, head) => { - const { pathname } = parse(request.url); + const { pathname } = parse(request.url!); if (pathname === "/ws") { wss.handleUpgrade(request, socket, head, socket => { @@ -212,13 +221,13 @@ function createServer(args) { wss.on('connection', (ws, req) => { try { - let query = req.url.split('?', 2)[1]; - let qs = queryString.parse(query); + const query = req.url!.split('?', 2)[1]; + const qs = queryString.parse(query); if (qs.side === "requestor") { processRequestor(ws, req); } else if (qs.side === "executor") { - processExecutor(ws, req, qs.sessionId); + processExecutor(ws, req, qs.sessionId as string); } else { ws.close(4050, "Invalid query string parameters specified."); } diff --git a/cli/src.ts/util.js b/cli/src.ts/util.ts similarity index 56% rename from cli/src.ts/util.js rename to cli/src.ts/util.ts index 7e65fa3..84837df 100644 --- a/cli/src.ts/util.js +++ b/cli/src.ts/util.ts @@ -1,18 +1,21 @@ import { fileURLToPath } from 'node:url'; -import { dirname as path_dirname } from 'node:path'; +import { dirname as path_dirname, join as path_join } from 'node:path'; import crypto from "crypto"; function randomBuffer() { return Buffer.from(crypto.getRandomValues(new Uint8Array(32))); } -let dirname; +let dirname: string; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error if (process.pkg && process.pkg.entrypoint) { dirname = __dirname; } else { const filename = fileURLToPath(import.meta.url); - dirname = path_dirname(filename); + dirname = path_join(path_dirname(filename), '..'); + console.log(dirname); } export {dirname, randomBuffer}; diff --git a/cli/src.ts/version.js b/cli/src.ts/version.ts similarity index 89% rename from cli/src.ts/version.js rename to cli/src.ts/version.ts index 3dcf0d2..88f12fc 100644 --- a/cli/src.ts/version.js +++ b/cli/src.ts/version.ts @@ -10,7 +10,7 @@ function getVersionInfo() { } function getBuildInfo() { - let versionInfo = getVersionInfo(); + const versionInfo = getVersionInfo(); return { tagName: versionInfo ? versionInfo.tagName : 'SNAPSHOT', @@ -20,7 +20,7 @@ function getBuildInfo() { } function printVersionInfo() { - let versionInfo = getVersionInfo(); + const versionInfo = getVersionInfo(); if (versionInfo) { console.log(versionInfo.name + ' (' + versionInfo.tagName + '; ' + versionInfo.commitId + ')'); diff --git a/cli/src.ts/ws_server.js b/cli/src.ts/ws_server.ts similarity index 82% rename from cli/src.ts/ws_server.js rename to cli/src.ts/ws_server.ts index 1feffe6..efbef87 100644 --- a/cli/src.ts/ws_server.js +++ b/cli/src.ts/ws_server.ts @@ -1,26 +1,33 @@ import express from 'express'; import nunjucks from "nunjucks"; -import {WebSocketServer} from 'ws'; -import {dirname, randomBuffer} from "./util.js"; +import {WebSocket, WebSocketServer} from 'ws'; import jwt from 'jsonwebtoken'; import https from "https"; import fs from "fs"; import path from "path"; import os from "os"; import util from "util"; -import {execHaloCmdPCSC} from "@arx-research/libhalo/api/desktop"; + +import {dirname, randomBuffer} from "./util.js"; import {getBuildInfo} from "./version.js"; + +import {execHaloCmdPCSC} from "@arx-research/libhalo/api/desktop"; import {NFCOperationError} from "@arx-research/libhalo/api/common"; +import {Reader} from "@arx-research/libhalo/types"; +import {Namespace} from "argparse"; -let wss = null; +let wss: WebSocketServer | null = null; -let currentWsClient = null; -let currentState = null; +let currentWsClient: WebSocket | null = null; +let currentState: { + handle: string + reader: Reader +} | null = null; -let jwtSigningKey = randomBuffer().toString('hex'); -let userConsentOrigins = new Set(); +const jwtSigningKey = randomBuffer().toString('hex'); +const userConsentOrigins = new Set(); -let buildInfo = getBuildInfo(); +const buildInfo = getBuildInfo(); function generateHandle() { return randomBuffer().toString('base64'); @@ -38,13 +45,13 @@ async function makeCSRFToken() { }); } -async function validateCSRFToken(token) { +async function validateCSRFToken(token: string) { return new Promise((resolve, reject) => { jwt.verify(token, jwtSigningKey, (err, decoded) => { if (err) { reject(err); } else { - if (decoded.purpose !== 'csrf-consent') { + if ((> decoded).purpose !== 'csrf-consent') { reject(new Error('Incorrect token purpose.')); } else { resolve(decoded); @@ -54,7 +61,7 @@ async function validateCSRFToken(token) { }); } -function sendToCurrentWs(ws, data) { +function sendToCurrentWs(ws: WebSocket | null, data: unknown) { console.log('send', util.inspect(data, {showHidden: false, depth: null, colors: true})); if (currentWsClient !== null && (ws === null || currentWsClient === ws)) { @@ -65,7 +72,7 @@ function sendToCurrentWs(ws, data) { return false; } -function wsEventCardConnected(reader) { +function wsEventCardConnected(reader: Reader) { if (currentState) { sendToCurrentWs(null, { "event": "handle_removed", @@ -77,7 +84,7 @@ function wsEventCardConnected(reader) { }); } - let handle = generateHandle(); + const handle = generateHandle(); currentState = {"handle": handle, "reader": reader}; sendToCurrentWs(null, { "event": "handle_added", @@ -89,7 +96,7 @@ function wsEventCardConnected(reader) { }); } -function wsEventCardIncompatible(reader) { +function wsEventCardIncompatible(reader: Reader) { sendToCurrentWs(null, { "event": "handle_not_compatible", "uid": null, @@ -100,7 +107,7 @@ function wsEventCardIncompatible(reader) { }); } -function wsEventCardDisconnected(reader) { +function wsEventCardDisconnected(reader: Reader) { if (currentState !== null && currentState.reader === reader) { sendToCurrentWs(null, { "event": "handle_removed", @@ -114,7 +121,7 @@ function wsEventCardDisconnected(reader) { } } -function wsEventReaderConnected(reader) { +function wsEventReaderConnected(reader: Reader) { sendToCurrentWs(null, { "event": "reader_added", "uid": null, @@ -124,7 +131,7 @@ function wsEventReaderConnected(reader) { }); } -function wsEventReaderDisconnected(reader) { +function wsEventReaderDisconnected(reader: Reader) { sendToCurrentWs(null, { "event": "reader_removed", "uid": null, @@ -156,7 +163,7 @@ function readTLSData() { return null; } -function wsCreateServer(args, getReaderNames) { +function wsCreateServer(args: Namespace, getReaderNames: () => string[]) { const tlsData = readTLSData(); let serverTLS = null; @@ -199,8 +206,16 @@ function wsCreateServer(args, getReaderNames) { }); app.get('/consent', async (req, res) => { - let url = new URL(req.query.website); - let csrfToken = await makeCSRFToken(); + let url; + + try { + url = new URL(req.query.website as string); + } catch (e) { + res.status(400).send('Bad request.'); + return; + } + + const csrfToken = await makeCSRFToken(); res.render('consent.html', { csrfToken, @@ -227,28 +242,28 @@ function wsCreateServer(args, getReaderNames) { return; } - let url = new URL(req.body.website); + const url = new URL(req.body.website); userConsentOrigins.add(url.protocol + '//' + url.host); res.render('consent_close.html'); }); server.on('upgrade', (request, socket, head) => { - wss.handleUpgrade(request, socket, head, socket => { - wss.emit('connection', socket, request); + wss && wss.handleUpgrade(request, socket, head, socket => { + wss && wss.emit('connection', socket, request); }); }); if (serverTLS) { serverTLS.on('upgrade', (request, socket, head) => { - wss.handleUpgrade(request, socket, head, socket => { - wss.emit('connection', socket, request); + wss && wss.handleUpgrade(request, socket, head, socket => { + wss && wss.emit('connection', socket, request); }); }); } wss.on('connection', (ws, req) => { - let parts = req.url.split('?'); + const parts = req.url!.split('?'); if (parts.length === 2 && parts[1] === "ping=1") { ws.close(4090, "Pong."); @@ -259,14 +274,14 @@ function wsCreateServer(args, getReaderNames) { let originHostname; try { - originHostname = new URL(req.headers.origin).hostname; + originHostname = new URL(req.headers.origin!).hostname; } catch (e) { ws.close(4003, "Failed to parse origin URL."); return; } if (args.allowOrigins) { - let allowedOrigins = args.allowOrigins.split(';'); + const allowedOrigins = args.allowOrigins.split(';'); if (allowedOrigins.includes(req.headers.origin)) { permitted = true; @@ -299,7 +314,7 @@ function wsCreateServer(args, getReaderNames) { return; } - let packet = JSON.parse(data); + const packet = JSON.parse(data.toString('utf-8')); console.log('recv', util.inspect(packet, {showHidden: false, depth: null, colors: true})); if (packet.type === "exec_halo") { @@ -308,7 +323,7 @@ function wsCreateServer(args, getReaderNames) { throw new NFCOperationError("Invalid handle."); } - let res = await execHaloCmdPCSC(packet.command, currentState.reader); + const res = await execHaloCmdPCSC(packet.command, currentState.reader); sendToCurrentWs(ws, { "event": "exec_success", "uid": packet.uid, @@ -316,7 +331,8 @@ function wsCreateServer(args, getReaderNames) { "res": res } }); - } catch (e) { + } catch (err) { + const e = err as Error; sendToCurrentWs(ws, { "event": "exec_exception", "uid": packet.uid, @@ -345,9 +361,9 @@ function wsCreateServer(args, getReaderNames) { } }); - let readerNames = getReaderNames(); + const readerNames = getReaderNames(); - for (let readerName of readerNames) { + for (const readerName of readerNames) { sendToCurrentWs(null, { "event": "reader_added", "uid": null, diff --git a/cli/tsconfig.json b/cli/tsconfig.json index 80ecc7d..e5d35dc 100644 --- a/cli/tsconfig.json +++ b/cli/tsconfig.json @@ -6,8 +6,7 @@ "forceConsistentCasingInFileNames": true, "importHelpers": true, "lib": [ - "es2020", - "es5", + "es2022", "dom" ], "moduleResolution": "node16", @@ -16,7 +15,7 @@ "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, - "noUnusedLocals": true, + "noUnusedLocals": false, "preserveSymlinks": true, "preserveWatchOutput": true, "pretty": false, @@ -29,7 +28,7 @@ "allowJs": true }, "include": [ - "./src.ts/**/*.tsx", + "./src.ts/**/*.ts", "./src.ts/**/*.js" ], "exclude": [] diff --git a/cli/webpack.config.js b/cli/webpack.config.js index 5058a74..e9cae6c 100644 --- a/cli/webpack.config.js +++ b/cli/webpack.config.js @@ -6,9 +6,9 @@ const __dirname = dirname(__filename); export default { entry: { - entry_cli: './src.ts/entry_cli.js', - entry_bridge: './src.ts/entry_bridge.js', - entry_gateway: './src.ts/entry_gateway.js', + entry_cli: './src.ts/entry_cli.ts', + entry_bridge: './src.ts/entry_bridge.ts', + entry_gateway: './src.ts/entry_gateway.ts', }, output: { filename: '[name].bundle.cjs', @@ -19,14 +19,17 @@ export default { module: { rules: [ { - test: /\.tsx?$/, + test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, resolve: { - extensions: ['.tsx', '.ts', '.js'], + extensions: ['.ts', '.js'], + extensionAlias: { + '.js': ['.js', '.ts'], + }, fallback: { vm: false, chokidar: false, diff --git a/cli/yarn.lock b/cli/yarn.lock index 5a2e787..bf08824 100644 --- a/cli/yarn.lock +++ b/cli/yarn.lock @@ -10,7 +10,6 @@ "@arx-research/libhalo@../core": version "1.4.3" dependencies: - argparse "^2.0.1" buffer "^6.0.3" elliptic "^6.5.5" ethers "^6.13.1" @@ -69,6 +68,62 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" + integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== + +"@eslint/config-array@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.17.0.tgz#ff305e1ee618a00e6e5d0485454c8d92d94a860d" + integrity sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA== + dependencies: + "@eslint/object-schema" "^2.1.4" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/eslintrc@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" + integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.7.0", "@eslint/js@^9.7.0": + version "9.7.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.7.0.tgz#b712d802582f02b11cfdf83a85040a296afec3f0" + integrity sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng== + +"@eslint/object-schema@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" + integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" + integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== + "@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -134,7 +189,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -150,6 +205,26 @@ bindings "^1.5.0" nan "^2.14.0" +"@types/argparse@^2.0.16": + version "2.0.16" + resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-2.0.16.tgz#3bb7ccd2844b3a8bcd6efbd217f6c0ea06a80d22" + integrity sha512-aMqBra2JlqpFeCWOinCtpRpiCkPIXH8hahW2+FkGzvWjfE5sAqtOcrjN5DRcMnTQqFDe6gb1CVYuGnBH0lhXwA== + +"@types/body-parser@*": + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + "@types/eslint-scope@^3.7.3": version "3.7.7" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" @@ -171,11 +246,48 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/express-serve-static-core@^4.17.33": + version "4.19.5" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz#218064e321126fcf9048d1ca25dd2465da55d9c6" + integrity sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@^4.17.21": + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + "@types/json-schema@*", "@types/json-schema@^7.0.8": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/jsonwebtoken@^9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz#d1af3544d99ad992fb6681bbe60676e06b032bd3" + integrity sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw== + dependencies: + "@types/node" "*" + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + "@types/node@*": version "20.14.10" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.10.tgz#a1a218290f1b6428682e3af044785e5874db469a" @@ -188,6 +300,126 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== +"@types/nunjucks@^3.2.6": + version "3.2.6" + resolved "https://registry.yarnpkg.com/@types/nunjucks/-/nunjucks-3.2.6.tgz#6d6e0363719545df8b9a024279902edf68b2caa9" + integrity sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w== + +"@types/qs@*": + version "6.9.15" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" + integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/ws@^8.5.11": + version "8.5.11" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.11.tgz#90ad17b3df7719ce3e6bc32f83ff954d38656508" + integrity sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w== + dependencies: + "@types/node" "*" + +"@typescript-eslint/eslint-plugin@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz#b3563927341eca15124a18c6f94215f779f5c02a" + integrity sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "7.16.0" + "@typescript-eslint/type-utils" "7.16.0" + "@typescript-eslint/utils" "7.16.0" + "@typescript-eslint/visitor-keys" "7.16.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/parser@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.16.0.tgz#53fae8112f8c912024aea7b499cf7374487af6d8" + integrity sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw== + dependencies: + "@typescript-eslint/scope-manager" "7.16.0" + "@typescript-eslint/types" "7.16.0" + "@typescript-eslint/typescript-estree" "7.16.0" + "@typescript-eslint/visitor-keys" "7.16.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz#eb0757af5720c9c53c8010d7a0355ae27e17b7e5" + integrity sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw== + dependencies: + "@typescript-eslint/types" "7.16.0" + "@typescript-eslint/visitor-keys" "7.16.0" + +"@typescript-eslint/type-utils@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz#ec52b1932b8fb44a15a3e20208e0bd49d0b6bd00" + integrity sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg== + dependencies: + "@typescript-eslint/typescript-estree" "7.16.0" + "@typescript-eslint/utils" "7.16.0" + debug "^4.3.4" + ts-api-utils "^1.3.0" + +"@typescript-eslint/types@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.16.0.tgz#60a19d7e7a6b1caa2c06fac860829d162a036ed2" + integrity sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw== + +"@typescript-eslint/typescript-estree@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz#98ac779d526fab2a781e5619c9250f3e33867c09" + integrity sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw== + dependencies: + "@typescript-eslint/types" "7.16.0" + "@typescript-eslint/visitor-keys" "7.16.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/utils@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.16.0.tgz#b38dc0ce1778e8182e227c98d91d3418449aa17f" + integrity sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.16.0" + "@typescript-eslint/types" "7.16.0" + "@typescript-eslint/typescript-estree" "7.16.0" + +"@typescript-eslint/visitor-keys@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz#a1d99fa7a3787962d6e0efd436575ef840e23b06" + integrity sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg== + dependencies: + "@typescript-eslint/types" "7.16.0" + eslint-visitor-keys "^3.4.3" + "@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" @@ -387,7 +619,12 @@ acorn-import-attributes@^1.9.5: resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== -acorn@^8.7.1, acorn@^8.8.2: +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.12.0, acorn@^8.7.1, acorn@^8.8.2: version "8.12.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== @@ -409,7 +646,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.12.5: +ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -534,6 +771,14 @@ body-parser@1.20.2: type-is "~1.6.18" unpipe "1.0.0" +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + brace-expansion@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" @@ -619,6 +864,11 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -629,7 +879,7 @@ caniuse-lite@^1.0.30001640: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz#6aa6610eb24067c246d30c57f055a9d0a7f8d05f" integrity sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA== -chalk@^4.1.0, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -719,6 +969,11 @@ commander@^5.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -769,7 +1024,7 @@ create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^7.0.3: +cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -812,7 +1067,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4: +debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== @@ -841,6 +1096,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + default-browser-id@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" @@ -1073,6 +1333,11 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -1081,6 +1346,80 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.2.tgz#5cbb33d4384c9136083a71190d548158fe128f94" + integrity sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" + integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== + +eslint@^9.7.0: + version "9.7.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.7.0.tgz#bedb48e1cdc2362a0caaa106a4c6ed943e8b09e4" + integrity sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.11.0" + "@eslint/config-array" "^0.17.0" + "@eslint/eslintrc" "^3.1.0" + "@eslint/js" "9.7.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.3.0" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.0.2" + eslint-visitor-keys "^4.0.0" + espree "^10.1.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^10.0.1, espree@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56" + integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA== + dependencies: + acorn "^8.12.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.0.0" + +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -1093,11 +1432,16 @@ estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.2.0: +estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -1163,7 +1507,7 @@ express@^4.19.2: utils-merge "1.0.1" vary "~1.1.2" -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -1184,6 +1528,11 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + fastest-levenshtein@^1.0.12: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" @@ -1196,6 +1545,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -1234,11 +1590,32 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + flat@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -1336,11 +1713,23 @@ glob-parent@^5.1.2: dependencies: is-glob "^4.0.1" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + globalthis@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" @@ -1373,6 +1762,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -1476,11 +1870,19 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0: +ignore@^5.2.0, ignore@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-local@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" @@ -1489,6 +1891,11 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" @@ -1597,7 +2004,7 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -1628,6 +2035,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -1724,11 +2136,23 @@ js-sha256@^0.11.0: resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.11.0.tgz#256a921d9292f7fe98905face82e367abaca9576" integrity sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q== +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -1739,6 +2163,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -1781,11 +2210,26 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -1798,6 +2242,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -1828,6 +2279,11 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" @@ -1914,6 +2370,20 @@ minimatch@9.0.4: dependencies: brace-expansion "^2.0.1" +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -1957,6 +2427,11 @@ napi-build-utils@^1.0.1: resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + negotiator@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -2051,6 +2526,18 @@ open@^10.1.0: is-inside-container "^1.0.0" is-wsl "^3.1.0" +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + p-is-promise@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-3.0.0.tgz#58e78c7dfe2e163cf2a04ff869e7c1dba64a5971" @@ -2063,6 +2550,13 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -2070,11 +2564,25 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -2166,6 +2674,11 @@ prebuild-install@7.1.1: tar-fs "^2.0.0" tunnel-agent "^0.6.0" +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -2344,6 +2857,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" @@ -2426,7 +2944,7 @@ schema-utils@^3.1.1, schema-utils@^3.2.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -semver@^7.3.4, semver@^7.3.5, semver@^7.5.4: +semver@^7.3.4, semver@^7.3.5, semver@^7.5.4, semver@^7.6.0: version "7.6.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== @@ -2648,6 +3166,11 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -2719,6 +3242,11 @@ terser@^5.26.0: commander "^2.20.0" source-map-support "~0.5.20" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -2741,6 +3269,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + ts-loader@^9.5.1: version "9.5.1" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.1.tgz#63d5912a86312f1fbe32cef0859fb8b2193d9b89" @@ -2764,6 +3297,13 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -2816,6 +3356,15 @@ typed-array-length@^1.0.6: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" +typescript-eslint@^7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-7.16.0.tgz#8466a7c6d8b5acfdcaf53ecc8ecf240072b346d8" + integrity sha512-kaVRivQjOzuoCXU6+hLnjo3/baxyzWVO5GrnExkFzETRYJKVHYkrJglOu2OCm8Hi9RPDWX1PTNNTpU5KRV0+RA== + dependencies: + "@typescript-eslint/eslint-plugin" "7.16.0" + "@typescript-eslint/parser" "7.16.0" + "@typescript-eslint/utils" "7.16.0" + typescript@^5.5.3: version "5.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa" @@ -3009,6 +3558,11 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -3094,3 +3648,8 @@ yargs@^16.2.0: string-width "^4.2.0" y18n "^5.0.5" yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/core/.gitignore b/core/.gitignore index b548694..554deab 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -1,3 +1,4 @@ /node_modules/ /dist/ +/lib.commonjs/ /lib.esm/ diff --git a/core/eslint.config.js b/core/eslint.config.js new file mode 100644 index 0000000..8dddcde --- /dev/null +++ b/core/eslint.config.js @@ -0,0 +1,14 @@ +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + "rules": { + "@typescript-eslint/no-unused-vars": "off" + } + } +); diff --git a/core/package.json b/core/package.json index 97fdfdf..cbc7953 100644 --- a/core/package.json +++ b/core/package.json @@ -30,21 +30,31 @@ "type": "module", "exports": { ".": { + "require": "./lib.commonjs/index.js", "import": "./lib.esm/index.js" }, "./api/common": { + "require": "./lib.commonjs/api/common.js", "import": "./lib.esm/api/common.js" }, "./api/desktop": { + "require": "./lib.commonjs/api/desktop.js", "import": "./lib.esm/api/desktop.js" }, "./api/react-native": { + "require": "./lib.commonjs/api/react-native.js", "import": "./lib.esm/api/react-native.js" }, "./api/web": { + "require": "./lib.commonjs/api/web.js", "import": "./lib.esm/api/web.js" }, + "./types": { + "require": "./lib.commonjs/types.js", + "import": "./lib.esm/types.js" + }, "./__tests": { + "require": "./lib.commonjs/halo/tests.js", "import": "./lib.esm/halo/tests.js" } }, @@ -52,7 +62,6 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { - "argparse": "^2.0.1", "buffer": "^6.0.3", "elliptic": "^6.5.5", "ethers": "^6.13.1", @@ -64,10 +73,17 @@ "websocket-as-promised": "^2.1.0" }, "devDependencies": { + "@eslint/js": "^9.7.0", + "@types/elliptic": "^6.4.18", + "@types/pbkdf2": "^3.1.2", + "@types/qrcode": "^1.5.5", "crypto-browserify": "^3.12.0", + "eslint": "^9.7.0", "process": "^0.11.10", "stream-browserify": "^3.0.0", + "ts-loader": "^9.5.1", "typescript": "^5.5.3", + "typescript-eslint": "^7.16.0", "webpack": "^5.92.1", "webpack-cli": "^5.1.4" } diff --git a/core/src.ts/api/common.js b/core/src.ts/api/common.ts similarity index 94% rename from core/src.ts/api/common.js rename to core/src.ts/api/common.ts index 612b747..b8bb4a9 100644 --- a/core/src.ts/api/common.js +++ b/core/src.ts/api/common.ts @@ -14,6 +14,7 @@ import { NFCBadTransportError, NFCBridgeConsentError } from "../halo/exceptions.js"; +import {BaseHaloAPI} from "../halo/cmd_exec.js"; import { parsePublicKeys, convertSignature, recoverPublicKey, sigToDer, SECP256k1_ORDER, BJJ_ORDER @@ -24,6 +25,8 @@ import { * except the lib's index.js. The library's structure is subject to change in the next versions. */ export { + BaseHaloAPI, + // exported utils parsePublicKeys as haloParsePublicKeys, convertSignature as haloConvertSignature, diff --git a/core/src.ts/api/desktop.js b/core/src.ts/api/desktop.ts similarity index 100% rename from core/src.ts/api/desktop.js rename to core/src.ts/api/desktop.ts diff --git a/core/src.ts/api/react-native.js b/core/src.ts/api/react-native.ts similarity index 100% rename from core/src.ts/api/react-native.js rename to core/src.ts/api/react-native.ts diff --git a/core/src.ts/api/web.js b/core/src.ts/api/web.ts similarity index 92% rename from core/src.ts/api/web.js rename to core/src.ts/api/web.ts index 59fe835..1888fc0 100644 --- a/core/src.ts/api/web.js +++ b/core/src.ts/api/web.ts @@ -8,7 +8,7 @@ import {HaloGateway} from "../halo/gateway/requestor.js"; import {HaloBridge} from "../halo/bridge.js"; import {haloFindBridge} from "../web/web_utils.js"; import {haloGateExecutorCreateWs, haloGateExecutorUserConfirm, haloGateExecutorSetHost} from "../halo/gateway/executor.js"; -import {execHaloCmdWeb, detectMethod} from "../drivers/web.js"; +import {HaloWebAPI, execHaloCmdWeb, detectMethod} from "../drivers/web.js"; import {checkWebNFCPermission} from "../drivers/webnfc.js"; /** @@ -17,6 +17,7 @@ import {checkWebNFCPermission} from "../drivers/webnfc.js"; */ export { // for web usage + HaloWebAPI, execHaloCmdWeb, haloFindBridge, detectMethod as haloGetDefaultMethod, diff --git a/core/src.ts/drivers/common.js b/core/src.ts/drivers/common.ts similarity index 88% rename from core/src.ts/drivers/common.js rename to core/src.ts/drivers/common.ts index deba888..472b998 100644 --- a/core/src.ts/drivers/common.js +++ b/core/src.ts/drivers/common.ts @@ -15,11 +15,13 @@ import { cmdGetGraffiti, cmdStoreGraffiti } from "../halo/commands.js"; import {ERROR_CODES} from "../halo/errors.js"; +import {ExecHaloCmdOptions, HaloCommandObject, HaloResponseObject} from "../types.js"; +import {Buffer} from 'buffer/index.js'; -async function execHaloCmd(command, options) { +async function execHaloCmd(command: HaloCommandObject, options: ExecHaloCmdOptions): Promise { command = Object.assign({}, command); - let commandName = command.name; + const commandName = command.name; delete command['name']; switch (commandName) { @@ -72,10 +74,10 @@ async function execHaloCmd(command, options) { } } -function checkErrors(res) { +function checkErrors(res: Buffer) { if (res.length === 2 && res[0] === 0xE1) { - if (ERROR_CODES.hasOwnProperty(res[1])) { - let err = ERROR_CODES[res[1]]; + if (Object.prototype.hasOwnProperty.call(ERROR_CODES, res[1])) { + const err = ERROR_CODES[res[1]]; throw new HaloTagError(err[0], "Tag responded with error: [" + err[0] + "] " + err[1]); } else { throw new HaloLogicError("Tag responded with unknown error: " + res.toString('hex')); diff --git a/core/src.ts/drivers/credential.js b/core/src.ts/drivers/credential.ts similarity index 73% rename from core/src.ts/drivers/credential.js rename to core/src.ts/drivers/credential.ts index e92605f..3cb5d79 100644 --- a/core/src.ts/drivers/credential.js +++ b/core/src.ts/drivers/credential.ts @@ -7,10 +7,10 @@ import {HaloTagError, NFCOperationError, NFCMethodNotSupported} from "../halo/exceptions.js"; import {ERROR_CODES} from "../halo/errors.js"; import {arr2hex, isWebDebugEnabled} from "../halo/util.js"; +import {ExecOptions, ExecReturnStruct} from "../types.js"; +import {Buffer} from 'buffer/index.js'; -const FLAG_USE_NEW_MODE = 0x00; - -async function execCredential(request, options) { +async function execCredential(request: Buffer, options: ExecOptions): Promise { const webDebug = isWebDebugEnabled(); if (webDebug) { @@ -27,12 +27,12 @@ async function execCredential(request, options) { options.statusCallback = () => null; } - let challenge = new Uint8Array(32); + const challenge = new Uint8Array(32); crypto.getRandomValues(challenge); - let encodedRequest = new Uint8Array([...request]); - let ctrl = new AbortController(); - let u2fReq = { + const encodedRequest = new Uint8Array([...request]); + const ctrl = new AbortController(); + const u2fReq: CredentialRequestOptions = { "publicKey": { "allowCredentials": [ { @@ -61,16 +61,16 @@ async function execCredential(request, options) { }); try { - u2fRes = await navigator.credentials.get(u2fReq); + u2fRes = await navigator.credentials.get(u2fReq) as PublicKeyCredential; } catch (e) { if (webDebug) { console.log('[libhalo] execCredential() exception:', e); } - if (e.name === "NotSupportedError") { + if (( e).name === "NotSupportedError") { throw new NFCMethodNotSupported("The call threw NotSupportedError. Please update your browser."); } else { - throw new NFCOperationError("Failed to execute command. " + e.name + ": " + e.message); + throw new NFCOperationError("Failed to execute command. " + ( e).name + ": " + ( e).message); } } @@ -84,19 +84,20 @@ async function execCredential(request, options) { cancelScan: () => ctrl.abort(), }); - let res = u2fRes.response.signature; - let resBuf = new Uint8Array(res); + const res = (u2fRes.response as AuthenticatorAssertionResponse).signature; + const resBuf = new Uint8Array(res); if (resBuf.length === 2 && resBuf[0] === 0xE1) { if (webDebug) { console.log('[libhalo] execCredential() command fail:', arr2hex(resBuf)); } - if (ERROR_CODES.hasOwnProperty(resBuf[1])) { - let err = ERROR_CODES[resBuf[1]]; + if (Object.prototype.hasOwnProperty.call(ERROR_CODES, resBuf[1])) { + const err = ERROR_CODES[resBuf[1]]; throw new HaloTagError(err[0], err[1]); } else { - throw new HaloTagError("Command returned an unknown error: " + arr2hex(resBuf)); + const errCode = arr2hex([resBuf[1]]); + throw new HaloTagError("ERROR_CODE_" + errCode, "Command returned an unknown error: " + arr2hex(resBuf)); } } diff --git a/core/src.ts/drivers/nfc_manager.js b/core/src.ts/drivers/nfc_manager.ts similarity index 57% rename from core/src.ts/drivers/nfc_manager.js rename to core/src.ts/drivers/nfc_manager.ts index 59794a7..83154c6 100644 --- a/core/src.ts/drivers/nfc_manager.js +++ b/core/src.ts/drivers/nfc_manager.ts @@ -8,10 +8,11 @@ import {checkErrors, execHaloCmd} from "./common.js"; import {HaloLogicError} from "../halo/exceptions.js"; import {readNDEF} from "./read_ndef.js"; import {Buffer} from 'buffer/index.js'; +import {EmptyOptions, HaloCommandObject, RNNFCManager} from "../types.js"; -async function execCoreCommandRN(nfcManager, command) { - let selectCmd = [...Buffer.from("00A4040007481199130E9F0100", "hex")]; - let resSelect = Buffer.from(await nfcManager.isoDepHandler.transceive(selectCmd)); +async function execCoreCommandRN(nfcManager: RNNFCManager, command: Buffer) { + const selectCmd = [...Buffer.from("00A4040007481199130E9F0100", "hex")]; + const resSelect = Buffer.from(await nfcManager.isoDepHandler.transceive(selectCmd)); if (resSelect.compare(Buffer.from([0x90, 0x00])) !== 0) { throw new HaloLogicError("Unable to initiate communication with the tag. Failed to select HaLo core."); @@ -24,7 +25,7 @@ async function execCoreCommandRN(nfcManager, command) { Buffer.from("00", "hex") ]); - let res = Buffer.from(await nfcManager.isoDepHandler.transceive([...cmdBuf])); + const res = Buffer.from(await nfcManager.isoDepHandler.transceive([...cmdBuf])); checkErrors(res); return { @@ -33,16 +34,15 @@ async function execCoreCommandRN(nfcManager, command) { }; } -async function execHaloCmdRN(nfcManager, command, options) { - options = options ? Object.assign({}, options) : {}; - +// eslint-disable-next-line @typescript-eslint/no-unused-vars +async function execHaloCmdRN(nfcManager: RNNFCManager, command: HaloCommandObject, options?: EmptyOptions) { if (command.name === "read_ndef") { - let wrappedTransceive = async (payload) => Buffer.from(await nfcManager.isoDepHandler.transceive([...payload])); + const wrappedTransceive = async (payload: Buffer) => Buffer.from(await nfcManager.isoDepHandler.transceive([...payload])); return await readNDEF(wrappedTransceive); } else { return await execHaloCmd(command, { method: 'nfc-manager', - exec: async (command) => await execCoreCommandRN(nfcManager, command), + exec: async (command: Buffer) => await execCoreCommandRN(nfcManager, command), }); } } diff --git a/core/src.ts/drivers/pcsc.js b/core/src.ts/drivers/pcsc.ts similarity index 66% rename from core/src.ts/drivers/pcsc.js rename to core/src.ts/drivers/pcsc.ts index 632dfdb..e62109a 100644 --- a/core/src.ts/drivers/pcsc.js +++ b/core/src.ts/drivers/pcsc.ts @@ -7,27 +7,33 @@ import {readNDEF} from "./read_ndef.js"; import {HaloLogicError, NFCOperationError} from "../halo/exceptions.js"; import {execHaloCmd, checkErrors} from "./common.js"; +import { + ExecHaloCmdOptions, ExecOptions, + HaloCommandObject, + Reader +} from "../types.js"; +import {Buffer} from 'buffer/index.js'; -async function selectCore(reader) { +async function selectCore(reader: Reader) { let res; try { res = await reader.transmit(Buffer.from("00A4040007481199130E9F0100", "hex"), 255); } catch (e) { - throw new NFCOperationError(e.message); + throw new NFCOperationError(( e).message); } - let statusCheck = res.slice(-2).compare(Buffer.from([0x91, 0x00])) !== 0; + const statusCheck = res.slice(-2).compare(Buffer.from([0x91, 0x00])) !== 0; if (!statusCheck) { throw new HaloLogicError("Unable to select HaLo core."); } } -async function selectU2FLayer(reader) { +async function selectU2FLayer(reader: Reader) { try { - let res = await reader.transmit(Buffer.from("00A4040008A0000006472F0001", "hex"), 255); - let statusCheck = res.slice(-2).compare(Buffer.from([0x91, 0x00])) !== 0; + const res = await reader.transmit(Buffer.from("00A4040008A0000006472F0001", "hex"), 255); + const statusCheck = res.slice(-2).compare(Buffer.from([0x91, 0x00])) !== 0; if (!statusCheck) { throw new HaloLogicError("Unable to select HaLo U2F layer."); @@ -38,27 +44,27 @@ async function selectU2FLayer(reader) { } } -async function transceive(reader, command, options) { +async function transceive(reader: Reader, command: Buffer, options: ExecOptions) { options = options || {}; - let start = performance.now(); + const start = performance.now(); let res; try { res = await reader.transmit(command, 255); } catch (e) { - throw new NFCOperationError(e.message); + throw new NFCOperationError(( e).message); } - let end = performance.now(); + const end = performance.now(); if (process.env.DEBUG_PCSC === "1") { console.log('=> ' + command.toString('hex')); console.log('<= [' + Math.round(end - start) + ' ms] ' + res.toString('hex')); } - let check1 = res.slice(-2).compare(Buffer.from([0x90, 0x00])) !== 0; - let check2 = res.slice(-2).compare(Buffer.from([0x91, 0x00])) !== 0; + const check1 = res.slice(-2).compare(Buffer.from([0x90, 0x00])) !== 0; + const check2 = res.slice(-2).compare(Buffer.from([0x91, 0x00])) !== 0; if (!options.noCheck) { if (check1 && check2) { @@ -71,30 +77,30 @@ async function transceive(reader, command, options) { } } -async function getVersion(reader) { - let versionRes = await transceive(reader, Buffer.from("00510000010700", "hex"), {noCheck: true}); +async function getVersion(reader: Reader): Promise { + const versionRes = await transceive(reader, Buffer.from("00510000010700", "hex"), {noCheck: true}); if (versionRes.slice(-2).compare(Buffer.from([0x90, 0x00])) !== 0) { // GET_FV_VERSION command not supported, fallback to NDEF - let wrappedTransceive = async (payload) => await transceive(reader, payload, {noCheck: true}); - let url = await readNDEF(wrappedTransceive); + const wrappedTransceive = async (payload: Buffer) => await transceive(reader, payload, {noCheck: true}); + const url = await readNDEF(wrappedTransceive); if (!url.qs.v) { return '01.C1.000001.00000000'; - } else if (url.qs.v.toLowerCase() === 'c2') { + } else if ((url.qs.v as string).toLowerCase() === 'c2') { return '01.C2.000002.00000000'; - } else if (url.qs.v.toLowerCase() === 'c3') { + } else if ((url.qs.v as string).toLowerCase() === 'c3') { return '01.C3.000003.00000000'; } else { - return url.qs.v; + return (url.qs.v as string); } } else { return versionRes.slice(0, -2).toString(); } } -async function getAddonVersion(reader) { - let addonVersionRes = await transceive(reader, Buffer.from("00510000011000", "hex"), {noCheck: true}); +async function getAddonVersion(reader: Reader) { + const addonVersionRes = await transceive(reader, Buffer.from("00510000011000", "hex"), {noCheck: true}); if (addonVersionRes.slice(-2).compare(Buffer.from([0x90, 0x00])) !== 0) { return null; @@ -103,8 +109,8 @@ async function getAddonVersion(reader) { return addonVersionRes.slice(0, -2).toString(); } -function wrapCommandForU2F(command) { - let payload = Buffer.concat([ +function wrapCommandForU2F(command: Buffer) { + const payload = Buffer.concat([ Buffer.from(Array(64)), Buffer.from([command.length]), command @@ -118,11 +124,11 @@ function wrapCommandForU2F(command) { ]); } -function unwrapResultFromU2F(res) { +function unwrapResultFromU2F(res: Buffer) { return res.slice(5); } -async function execCoreCommand(reader, command, options) { +async function execCoreCommand(reader: Reader, command: Buffer, options?: ExecOptions) { options = Object.assign({}, options); let cmdBuf; @@ -157,35 +163,36 @@ async function execCoreCommand(reader, command, options) { }; } -function makeOptions(reader) { +function makeOptions(reader: Reader): ExecHaloCmdOptions { return { method: 'pcsc', exec: async (command, options) => await execCoreCommand(reader, command, options), } } -async function execHaloCmdPCSC(command, reader) { +async function execHaloCmdPCSC(command: HaloCommandObject, reader: Reader) { await selectCore(reader); - let version = await getVersion(reader); + const version = await getVersion(reader); - let [verMajor, verMinor, verSeq, verShortId] = version.split('.'); - verSeq = parseInt(verSeq, 10); + const [verMajor, verMinor, verSeqStr, verShortId] = version.split('.'); + const verMajorNum = parseInt(verMajor, 10); + const verSeq = parseInt(verSeqStr, 10); - if (verMajor > 1) { + if (verMajorNum > 1) { throw new HaloLogicError("This version of CLI doesn't support major release version " + verMajor + ". Please update."); } - let options = makeOptions(reader); + const options = makeOptions(reader); command = {...command}; if (command.name === "version") { // PCSC-specific version retrieval command - let addonVersion = await getAddonVersion(reader); + const addonVersion = await getAddonVersion(reader); let addonParts = null; if (addonVersion) { - let [verAMajor, verAMinor, verASeq, verAShortId] = addonVersion.split('.'); - verASeq = parseInt(verASeq, 10); + const [verAMajor, verAMinor, verASeqStr, verAShortId] = addonVersion.split('.'); + const verASeq = parseInt(verASeqStr, 10); addonParts = { verAMajor, verAMinor, @@ -211,7 +218,7 @@ async function execHaloCmdPCSC(command, reader) { }; } else if (command.name === "read_ndef") { // PCSC-specific NDEF reader command - let wrappedTransceive = async (payload) => await transceive(reader, payload, {noCheck: true}); + const wrappedTransceive = async (payload: Buffer) => await transceive(reader, payload, {noCheck: true}); return await readNDEF(wrappedTransceive); } else if (command.name === "full_gen_key") { await selectCore(reader); @@ -230,7 +237,7 @@ async function execHaloCmdPCSC(command, reader) { }, options); } - let subPkRes = await execHaloCmd({ + const subPkRes = await execHaloCmd({ "name": "gen_key_finalize", "keyNo": command.keyNo, "password": command.password diff --git a/core/src.ts/drivers/read_ndef.js b/core/src.ts/drivers/read_ndef.ts similarity index 79% rename from core/src.ts/drivers/read_ndef.js rename to core/src.ts/drivers/read_ndef.ts index 5313b4f..144bcf5 100644 --- a/core/src.ts/drivers/read_ndef.js +++ b/core/src.ts/drivers/read_ndef.ts @@ -7,29 +7,30 @@ import {Buffer} from 'buffer/index.js'; import queryString from 'query-string'; import {HaloLogicError} from "../halo/exceptions.js"; +import {TransceiveFunc} from "../types.js"; -async function readNDEF(transceive) { - let resSelect = await transceive(Buffer.from("00A4040007D276000085010100", "hex")); +async function readNDEF(transceive: TransceiveFunc) { + const resSelect = await transceive(Buffer.from("00A4040007D276000085010100", "hex")); if (resSelect.compare(Buffer.from([0x90, 0x00])) !== 0) { throw new HaloLogicError("Unable to read NDEF, failed to select NDEF application."); } // assume that NDEF file is 0xE104 - let resSelectFile = await transceive(Buffer.from("00A4000C02E10400", "hex")); + const resSelectFile = await transceive(Buffer.from("00A4000C02E10400", "hex")); if (resSelectFile.compare(Buffer.from([0x90, 0x00])) !== 0) { throw new HaloLogicError("Unable to read NDEF, failed to select NDEF file."); } - let readCmdBuf = Buffer.from("00B0000002", "hex"); - let resReadLength = await transceive(readCmdBuf); + const readCmdBuf = Buffer.from("00B0000002", "hex"); + const resReadLength = await transceive(readCmdBuf); if (resReadLength.slice(-2).compare(Buffer.from([0x90, 0x00])) !== 0) { throw new HaloLogicError("Unable to read NDEF, failed to read length bytes."); } - let ndefLen = resReadLength.readUInt16BE(0) + 2; + const ndefLen = resReadLength.readUInt16BE(0) + 2; let tmpNdefLen = ndefLen; let offset = 0; @@ -42,7 +43,7 @@ async function readNDEF(transceive) { // (the same APDU is just working fine lol) readCmdBuf[4] = 0x30; - let resReadNDEF = await transceive(readCmdBuf); + const resReadNDEF = await transceive(readCmdBuf); if (resReadNDEF.slice(-2).compare(Buffer.from([0x90, 0x00])) !== 0) { throw new HaloLogicError("Unable to read NDEF, failed to read NDEF file."); @@ -67,15 +68,15 @@ async function readNDEF(transceive) { } fullBuf = fullBuf.slice(4); - let lengthData = fullBuf.readUInt16BE(0); + const lengthData = fullBuf.readUInt16BE(0); if (fullBuf[2] !== 0x54 && fullBuf[2] !== 0x55) { throw new HaloLogicError("Failed to parse NDEF, unsupported record type."); } - let skipLen = fullBuf[3]; + const skipLen = fullBuf[3]; fullBuf = fullBuf.slice(4 + skipLen, 4 + skipLen + lengthData); - let parsed = queryString.parseUrl(fullBuf.toString()); + const parsed = queryString.parseUrl(fullBuf.toString()); return { url: parsed.url, diff --git a/core/src.ts/drivers/web.js b/core/src.ts/drivers/web.ts similarity index 65% rename from core/src.ts/drivers/web.js rename to core/src.ts/drivers/web.ts index 9fb1b72..03840fd 100644 --- a/core/src.ts/drivers/web.js +++ b/core/src.ts/drivers/web.ts @@ -4,10 +4,19 @@ import {execWebNFC} from "./webnfc.js"; import {execHaloCmd} from "./common.js"; import {emulatedPromptStatusCallback} from "../web/soft_prompt.js"; import {isWebDebugEnabled} from "../halo/util.js"; +import { + ExecHaloCmdOptions, + ExecHaloCmdWebOptions, + HaloCommandObject, HaloResponseObject, + HaloWebMethod, + StatusCallbackDetails +} from "../types.js"; +import {Buffer} from 'buffer/index.js'; +import {BaseHaloAPI} from "../halo/cmd_exec.js"; -let isCallRunning = null; +let isCallRunning = false; -function makeDefault(curValue, defaultValue) { +function makeDefault(curValue: Type | null | undefined, defaultValue: Type): Type { if (typeof curValue === "undefined") { return defaultValue; } @@ -23,9 +32,11 @@ function makeDefault(curValue, defaultValue) { * Detect the best command execution method for the current device. * @returns {string} Either "credential" or "webnfc". */ -function detectMethod() { +export function detectMethod() { try { - new NDEFReader(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + new window.NDEFReader(); } catch (e) { // WebNFC not supported return "credential"; @@ -34,7 +45,7 @@ function detectMethod() { return "webnfc"; } -function defaultStatusCallback(cause, statusObj) { +function defaultStatusCallback(cause: string, statusObj: StatusCallbackDetails) { if (statusObj.execMethod === "webnfc") { return emulatedPromptStatusCallback(cause, statusObj); } @@ -50,7 +61,7 @@ function defaultStatusCallback(cause, statusObj) { * @param options Additional options for the command executor. * @returns {Promise<*>} Command execution result. */ -async function execHaloCmdWeb(command, options) { +export async function execHaloCmdWeb(command: HaloCommandObject, options?: ExecHaloCmdWebOptions) { if (options && !options.noDebounce && isCallRunning) { throw new NFCAbortedError("The operation was debounced."); } @@ -58,8 +69,8 @@ async function execHaloCmdWeb(command, options) { isCallRunning = true; options = options ? Object.assign({}, options) : {}; - options.method = makeDefault(options.method, detectMethod()); - options.noDebounce = makeDefault(options.noDebounce, false); + options.method = makeDefault(options.method, detectMethod()); + options.noDebounce = makeDefault(options.noDebounce, false); options.statusCallback = makeDefault(options.statusCallback, defaultStatusCallback); command = command ? Object.assign({}, command) : {}; @@ -70,19 +81,19 @@ async function execHaloCmdWeb(command, options) { } try { - let cmdOpts = {}; + let cmdOpts: ExecHaloCmdOptions; if (options.method === "credential") { cmdOpts = { method: "credential", - exec: async (command) => await execCredential(command, { + exec: async (command: Buffer) => await execCredential(command, { statusCallback: options.statusCallback }) }; } else if (options.method === "webnfc") { cmdOpts = { method: "webnfc", - exec: async (command) => await execWebNFC(command, { + exec: async (command: Buffer) => await execWebNFC(command, { statusCallback: options.statusCallback }) }; @@ -104,7 +115,16 @@ async function execHaloCmdWeb(command, options) { } } -export { - execHaloCmdWeb, - detectMethod -}; +export class HaloWebAPI extends BaseHaloAPI { + private readonly options: ExecHaloCmdWebOptions | undefined; + + constructor(options?: ExecHaloCmdWebOptions) { + super(); + + this.options = options; + } + + executeCommand(args: HaloCommandObject): Promise { + return execHaloCmdWeb(args, this.options); + } +} diff --git a/core/src.ts/drivers/webnfc.js b/core/src.ts/drivers/webnfc.ts similarity index 68% rename from core/src.ts/drivers/webnfc.js rename to core/src.ts/drivers/webnfc.ts index 6fda761..75aa24d 100644 --- a/core/src.ts/drivers/webnfc.js +++ b/core/src.ts/drivers/webnfc.ts @@ -12,9 +12,12 @@ import { NFCAbortedError } from "../halo/exceptions.js"; import {arr2hex, hex2arr, isWebDebugEnabled} from "../halo/util.js"; +import {ExecOptions, ExecReturnStruct} from "../types.js"; +import type {NDEFReader} from "../types_webnfc.js"; +import {Buffer} from 'buffer/index.js'; -let ndef = null; -let ctrl = null; +let ndef: NDEFReader | null = null; +let ctrl: AbortController | null = null; let blurEventInstalled = false; function detectWindowMinimized() { @@ -39,12 +42,14 @@ async function checkWebNFCPermission() { try { const controller = new AbortController(); - const ndef = new NDEFReader(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + const ndef = new window.NDEFReader() as NDEFReader; await ndef.scan({ signal: controller.signal }); controller.abort(); return true; } catch (e) { - if (e.name === "NotAllowedError") { + if (( e).name === "NotAllowedError") { return false; } @@ -52,7 +57,7 @@ async function checkWebNFCPermission() { } } -async function execWebNFC(request, options) { +async function execWebNFC(request: Buffer, options: ExecOptions): Promise { const webDebug = isWebDebugEnabled(); if (webDebug) { @@ -69,7 +74,7 @@ async function execWebNFC(request, options) { console.log('[libhalo] execWebNFC() internal error checking WebNFC permission:', e); } - throw new NFCMethodNotSupported("Internal error when checking WebNFC permission: " + e.toString()); + throw new NFCMethodNotSupported("Internal error when checking WebNFC permission: " + ( e).toString()); } if (!isWebNFCGranted) { @@ -88,7 +93,9 @@ async function execWebNFC(request, options) { if (!ndef) { try { - ndef = new NDEFReader(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + ndef = new window.NDEFReader() as NDEFReader; } catch (e) { if (webDebug) { console.log('[libhalo] execWebNFC() failed createing NDEFReader'); @@ -121,13 +128,13 @@ async function execWebNFC(request, options) { options.statusCallback("init", { execMethod: "webnfc", execStep: "nfc-write", - cancelScan: () => ctrl.abort(), + cancelScan: () => ctrl && ctrl.abort(), }); } else if (writeStatus === "nfc-write-error") { options.statusCallback("retry", { execMethod: "webnfc", execStep: "nfc-write-error", - cancelScan: () => ctrl.abort(), + cancelScan: () => ctrl && ctrl.abort(), }); } @@ -146,9 +153,9 @@ async function execWebNFC(request, options) { console.log('[libhalo] execWebNFC() write exception:', e); } - if (e.name === "NotAllowedError") { + if (( e).name === "NotAllowedError") { throw new NFCPermissionRequestDenied("NFC permission request denied by the user."); - } else if (e.name === "AbortError") { + } else if (( e).name === "AbortError") { throw new NFCAbortedError("Operation restarted by the user or webpage minimized (during write)."); } else { writeStatus = "nfc-write-error"; @@ -165,11 +172,11 @@ async function execWebNFC(request, options) { options.statusCallback("again", { execMethod: "webnfc", execStep: "nfc-read", - cancelScan: () => ctrl.abort(), + cancelScan: () => ctrl && ctrl.abort(), }); return new Promise((resolve, reject) => { - ctrl.signal.addEventListener('abort', () => { + ctrl!.signal.addEventListener('abort', () => { if (webDebug) { console.log('[libhalo] execWebNFC() operation aborted during read'); } @@ -177,7 +184,7 @@ async function execWebNFC(request, options) { reject(new NFCAbortedError("Operation restarted by the user or webpage minimized (during read).")); }); - if (ctrl.signal.aborted) { + if (ctrl!.signal.aborted) { if (webDebug) { console.log('[libhalo] execWebNFC() operation aborted during read'); } @@ -185,65 +192,71 @@ async function execWebNFC(request, options) { reject(new NFCAbortedError("Operation restarted by the user or webpage minimized (during read).")); } - ndef.onreadingerror = (event) => { + ndef!.onreadingerror = (event) => { if (webDebug) { console.log('[libhalo] execWebNFC() read error'); } - options.statusCallback("retry", { - execMethod: "webnfc", - execStep: "nfc-read-error", - cancelScan: () => ctrl.abort(), - }); + if (options.statusCallback) { + options.statusCallback("retry", { + execMethod: "webnfc", + execStep: "nfc-read-error", + cancelScan: () => ctrl && ctrl.abort(), + }); + } }; - ndef.onreading = (event) => { + ndef!.onreading = (event) => { if (webDebug) { console.log('[libhalo] execWebNFC() read event received, parsing'); } try { - let out = {}; - let decoded = new TextDecoder("utf-8").decode(event.message.records[0].data); - let url = new URL(decoded); + const out: Record = {}; + const decoded = new TextDecoder("utf-8").decode(event.message.records[0].data); + const url = new URL(decoded); if (webDebug) { console.log('[libhalo] execWebNFC() read result url:', url); } - for (let k of url.searchParams.keys()) { - out[k] = url.searchParams.get(k); + for (const k of url.searchParams.keys()) { + out[k] = url.searchParams.get(k)!; } - let resBuf = hex2arr(out.res); + const resBuf = hex2arr(out.res); if (resBuf[0] === 0xE1) { if (webDebug) { console.log('[libhalo] execWebNFC() command fail:', arr2hex(resBuf)); } - if (ERROR_CODES.hasOwnProperty(resBuf[1])) { - let err = ERROR_CODES[resBuf[1]]; - ndef.onreading = () => null; - ndef.onreadingerror = () => null; + if (Object.prototype.hasOwnProperty.call(ERROR_CODES, resBuf[1])) { + const err = ERROR_CODES[resBuf[1]]; + ndef!.onreading = () => null; + ndef!.onreadingerror = () => null; reject(new HaloTagError(err[0], err[1])); } else { - ndef.onreading = () => null; - ndef.onreadingerror = () => null; - reject(new HaloTagError("Command returned an unknown error: " + arr2hex(resBuf))); + ndef!.onreading = () => null; + ndef!.onreadingerror = () => null; + + const errCode = arr2hex([resBuf[1]]); + reject(new HaloTagError("ERROR_CODE_" + errCode, "Command returned an unknown error: " + arr2hex(resBuf))); } return; } - options.statusCallback("scanned", { - execMethod: "webnfc", - execStep: "nfc-success", - cancelScan: () => ctrl.abort(), - }); + if (options.statusCallback) { + options.statusCallback("scanned", { + execMethod: "webnfc", + execStep: "nfc-success", + cancelScan: () => ctrl && ctrl.abort(), + }); + } - ndef.onreading = () => null; - ndef.onreadingerror = () => null; + ndef!.onreading = () => null; + ndef!.onreadingerror = () => null; delete out['res']; @@ -262,11 +275,13 @@ async function execWebNFC(request, options) { console.log('[libhalo] execWebNFC() parse error:', e); } - options.statusCallback("retry", { - execMethod: "webnfc", - execStep: "nfc-parse-error", - cancelScan: () => ctrl.abort(), - }); + if (options.statusCallback) { + options.statusCallback("retry", { + execMethod: "webnfc", + execStep: "nfc-parse-error", + cancelScan: () => ctrl && ctrl.abort(), + }); + } } }; }); diff --git a/core/src.ts/halo/bridge.js b/core/src.ts/halo/bridge.ts similarity index 67% rename from core/src.ts/halo/bridge.js rename to core/src.ts/halo/bridge.ts index a02985f..12befbe 100644 --- a/core/src.ts/halo/bridge.js +++ b/core/src.ts/halo/bridge.ts @@ -1,40 +1,55 @@ import queryString from "query-string"; import WebSocketAsPromised from "websocket-as-promised"; -import {HaloLogicError, HaloTagError, NFCOperationError, NFCBadTransportError, NFCAbortedError, NFCBridgeConsentError} from "./exceptions.js"; +import { + HaloLogicError, + HaloTagError, + NFCOperationError, + NFCBadTransportError, + NFCAbortedError, + NFCBridgeConsentError, + NFCBridgeUnexpectedError +} from "./exceptions.js"; import {haloFindBridge} from "../web/web_utils.js"; import {webDebug} from "./util.js"; +import {BridgeEvent, BridgeHandleAdded, BridgeOptions, HaloCommandObject} from "../types.js"; class HaloBridge { - constructor(options) { + private isRunning: boolean; + private lastHandle: string | null; + private url: string | null; + private readonly createWebSocket: (url: string) => WebSocket; + private ws: WebSocketAsPromised | null; + + constructor(options: BridgeOptions) { options = Object.assign({}, options); this.isRunning = false; - this.lastCommand = null; this.lastHandle = null; this.url = null; + this.ws = null; this.createWebSocket = options.createWebSocket ? options.createWebSocket - : (url) => new WebSocket(url); + : (url: string) => new WebSocket(url); } - waitForWelcomePacket() { + waitForWelcomePacket(): Promise> { return new Promise((resolve, reject) => { - let welcomeWaitTimeout = setTimeout(() => { + const welcomeWaitTimeout = setTimeout(() => { reject(new NFCBadTransportError("Server doesn't send ws_connected packet for 6 seconds after accepting the connection.")); }, 6000); - this.ws.onClose.addListener((event) => { + this.ws!.onClose.addListener((event) => { if (event.code === 4002) { // no user consent - reject(new NFCBridgeConsentError()); + reject(new NFCBridgeConsentError("No user consent for this origin.")); } else { reject(new NFCBadTransportError("WebSocket closed when waiting for ws_connected packet. " + "Reason: [" + event.code + "] " + event.reason)); } }); - this.ws.onUnpackedMessage.addListener(data => { + this.ws!.onUnpackedMessage.addListener(data => { if (data.event === "ws_connected") { clearTimeout(welcomeWaitTimeout); resolve(data); @@ -46,10 +61,10 @@ class HaloBridge { async connect() { this.url = await haloFindBridge({createWebSocket: this.createWebSocket}); - this.ws = new WebSocketAsPromised(this.url, { + this.ws = new WebSocketAsPromised(this.url!, { createWebSocket: url => this.createWebSocket(url), packMessage: data => JSON.stringify(data), - unpackMessage: data => JSON.parse(data), + unpackMessage: data => JSON.parse(data as string), attachRequestId: (data, requestId) => Object.assign({uid: requestId}, data), extractRequestId: data => data && data.uid }); @@ -62,17 +77,17 @@ class HaloBridge { } }); - let waitPromise = this.waitForWelcomePacket(); + const waitPromise = this.waitForWelcomePacket(); await this.ws.open(); - let welcomeMsg = await waitPromise; - let serverVersion = welcomeMsg.serverVersion; + const welcomeMsg = await waitPromise; + const serverVersion = welcomeMsg.serverVersion; return { serverVersion: serverVersion }; } - getConsentURL(websiteURL, options) { + getConsentURL(websiteURL: string, options: unknown) { if (!this.url) { return null; } @@ -80,6 +95,8 @@ class HaloBridge { return this.url .replace('ws://', 'http://') .replace('wss://', 'https://') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error .replace('/ws', '/consent?' + queryString.stringify({'website': websiteURL, ...options})); } @@ -90,7 +107,7 @@ class HaloBridge { } async waitForHandle() { - if (!this.ws.isOpened) { + if (this.ws === null || !this.ws.isOpened) { throw new NFCBadTransportError("Bridge is not open."); } @@ -99,30 +116,36 @@ class HaloBridge { } return new Promise((resolve, reject) => { - const msgListener = data => { + const msgListener = (data: BridgeEvent) => { if (data.event === "handle_added") { - this.lastHandle = data.data.handle; + this.lastHandle = (data as BridgeHandleAdded).data.handle; - this.ws.onUnpackedMessage.removeListener(msgListener); - this.ws.onClose.removeListener(closeListener); + if (this.ws) { + this.ws.onUnpackedMessage.removeListener(msgListener); + this.ws.onClose.removeListener(closeListener); + } resolve(this.lastHandle); } }; - const closeListener = data => { - this.ws.onUnpackedMessage.removeListener(msgListener); - this.ws.onClose.removeListener(closeListener); + const closeListener = (data: never) => { + if (this.ws) { + this.ws.onUnpackedMessage.removeListener(msgListener); + this.ws.onClose.removeListener(closeListener); + } reject(new NFCBadTransportError("Bridge server has disconnected.")); }; - this.ws.onUnpackedMessage.addListener(msgListener); - this.ws.onClose.addListener(closeListener); + if (this.ws) { + this.ws.onUnpackedMessage.addListener(msgListener); + this.ws.onClose.addListener(closeListener); + } }); } - async execHaloCmd(command) { + async execHaloCmd(command: HaloCommandObject) { webDebug('[halo-bridge] called execHaloCmd()', command); if (this.isRunning) { @@ -130,11 +153,15 @@ class HaloBridge { throw new NFCAbortedError("Can not make multiple calls to execHaloCmd() in parallel."); } + if (!this.ws) { + throw new NFCBadTransportError("Bridge was not opened."); + } + this.isRunning = true; try { webDebug('[halo-bridge] waiting for card tap'); - let handle = await this.waitForHandle(); + const handle = await this.waitForHandle(); webDebug('[halo-bridge] sending request to execute command', handle); let res; @@ -147,7 +174,7 @@ class HaloBridge { }); } catch (e) { webDebug('[halo-bridge] exception when trying to sendRequest', e); - throw new NFCBadTransportError('Failed to send request: ' + e.toString()); + throw new NFCBadTransportError('Failed to send request: ' + ( e).toString()); } if (res.event === "exec_success") { @@ -159,23 +186,22 @@ class HaloBridge { switch (res.data.exception.kind) { case 'HaloLogicError': - e = new HaloLogicError(res.data.exception.message); + e = new HaloLogicError(res.data.exception.message, res.data.exception.stack); break; case 'HaloTagError': - e = new HaloTagError(res.data.exception.name, res.data.exception.message); + e = new HaloTagError(res.data.exception.name, res.data.exception.message, res.data.exception.stack); break; case 'NFCOperationError': // allow some time for the PC/SC reader to re-poll for the card await new Promise((resolve, reject) => setTimeout(resolve, 500)); - e = new NFCOperationError(res.data.exception.message); + e = new NFCOperationError(res.data.exception.message, res.data.exception.stack); break; default: - e = new Error("Unexpected exception occurred while executing the command. " + - res.data.exception.name + ": " + res.data.exception.message); + e = new NFCBridgeUnexpectedError("Unexpected exception occurred while executing the command. " + + res.data.exception.name + ": " + res.data.exception.message, res.data.exception.stack); break; } - e.stackOnExecutor = res.data.exception.stack; webDebug('[halo-bridge] throwing exception as the call result', e); throw e; } else { diff --git a/core/src.ts/halo/cmd_exec.ts b/core/src.ts/halo/cmd_exec.ts new file mode 100644 index 0000000..152cda2 --- /dev/null +++ b/core/src.ts/halo/cmd_exec.ts @@ -0,0 +1,130 @@ +/** + * LibHaLo - Programmatically interact with HaLo tags from the web browser, mobile application or the desktop. + * Copyright by Arx Research, Inc., a Delaware corporation + * License: MIT + */ +import { + HaloCmdExportKey, + HaloCmdGetDataStruct, + HaloCmdGetGraffiti, HaloCmdGetKeyInfo, + HaloCmdGetPkeys, + HaloCmdGetTransportPK, + HaloCmdImportKey, + HaloCmdImportKeyInit, + HaloCmdLoadTransportPK, HaloCmdReplacePassword, HaloCmdSetPassword, HaloCmdSetURLSubdomain, + HaloCmdSign, + HaloCmdSignChallenge, + HaloCmdSignRandom, + HaloCmdStoreGraffiti, HaloCmdUnsetPassword, + HaloCmdWriteLatch, + HaloResExportKey, + HaloResGetGraffiti, HaloResGetKeyInfo, + HaloResGetPkeys, HaloResGetTransportPK, + HaloResImportKey, + HaloResImportKeyInit, + HaloResLoadTransportPK, HaloResReplacePassword, HaloResSetPassword, HaloResSetURLSubdomain, + HaloResSign, + HaloResSignChallenge, + HaloResSignRandom, HaloResUnsetPassword, + HaloResWriteLatch +} from "./command_types.js"; +import { + HaloCmdCFGNDEF, + HaloCmdGenKey, HaloCmdGenKeyConfirm, HaloCmdGenKeyFinalize, + HaloCommandObject, + HaloResCFGNDEF, + HaloResGenKey, HaloResGenKeyConfirm, HaloResGenKeyFinalize, + HaloResponseObject +} from "../types.js"; + +export abstract class BaseHaloAPI { + abstract executeCommand(args: HaloCommandObject): Promise; + + getPkeys(args: HaloCmdGetPkeys): Promise { + return this.executeCommand(args); + } + + sign(args: HaloCmdSign): Promise { + return this.executeCommand(args); + } + + writeLatch(args: HaloCmdWriteLatch): Promise { + return this.executeCommand(args); + } + + signRandom(args: HaloCmdSignRandom): Promise { + return this.executeCommand(args); + } + + signChallenge(args: HaloCmdSignChallenge): Promise { + return this.executeCommand(args); + } + + cfgNDEF(args: HaloCmdCFGNDEF): Promise { + return this.executeCommand(args); + } + + genKey(args: HaloCmdGenKey): Promise { + return this.executeCommand(args); + } + + genKeyConfirm(args: HaloCmdGenKeyConfirm): Promise { + return this.executeCommand(args); + } + + genKeyFinalize(args: HaloCmdGenKeyFinalize): Promise { + return this.executeCommand(args); + } + + setURLSubdomain(args: HaloCmdSetURLSubdomain): Promise { + return this.executeCommand(args); + } + + getKeyInfo(args: HaloCmdGetKeyInfo): Promise { + return this.executeCommand(args); + } + + setPassword(args: HaloCmdSetPassword): Promise { + return this.executeCommand(args); + } + + unsetPassword(args: HaloCmdUnsetPassword): Promise { + return this.executeCommand(args); + } + + replacePassword(args: HaloCmdReplacePassword): Promise { + return this.executeCommand(args); + } + + getTransportPK(args: HaloCmdGetTransportPK): Promise { + return this.executeCommand(args); + } + + loadTransportPK(args: HaloCmdLoadTransportPK): Promise { + return this.executeCommand(args); + } + + exportKey(args: HaloCmdExportKey): Promise { + return this.executeCommand(args); + } + + importKeyInit(args: HaloCmdImportKeyInit): Promise { + return this.executeCommand(args); + } + + importKey(args: HaloCmdImportKey): Promise { + return this.executeCommand(args); + } + + getDataStruct(args: HaloCmdGetDataStruct): Promise { + return this.executeCommand(args); + } + + getGraffiti(args: HaloCmdGetGraffiti): Promise { + return this.executeCommand(args); + } + + storeGraffiti(args: HaloCmdStoreGraffiti): Promise { + return this.executeCommand(args); + } +} diff --git a/core/src.ts/halo/cmdcodes.js b/core/src.ts/halo/cmdcodes.ts similarity index 100% rename from core/src.ts/halo/cmdcodes.js rename to core/src.ts/halo/cmdcodes.ts diff --git a/core/src.ts/halo/command_types.ts b/core/src.ts/halo/command_types.ts new file mode 100644 index 0000000..915777a --- /dev/null +++ b/core/src.ts/halo/command_types.ts @@ -0,0 +1,220 @@ +import {ASCIIString, HexString, KeyFlags, KeySlotNo, KeyState, PublicKeyList} from "../types.js"; +import {TypedDataDomain, TypedDataField} from "ethers"; + +export interface HaloCmdGetPkeys {} + +export interface HaloResGetPkeys { + publicKeys: PublicKeyList + compressedPublicKeys: PublicKeyList + etherAddresses: PublicKeyList +} + + +export interface BaseHaloCmdSign { + keyNo: KeySlotNo + format?: "text" | "hex" + password?: ASCIIString + + legacySignCommand?: boolean +} + +export interface HaloCmdSignVariant1 extends BaseHaloCmdSign { + digest: HexString + message?: undefined + typedData?: undefined +} + +export interface HaloCmdSignVariant2 extends BaseHaloCmdSign { + digest?: undefined + message: HexString + typedData?: undefined +} + +export interface HaloCmdSignVariant3 extends BaseHaloCmdSign { + digest?: undefined + message?: undefined + typedData: { + domain: TypedDataDomain + types: Record> + value: Record + } +} + +export type HaloCmdSign = HaloCmdSignVariant1 | HaloCmdSignVariant2 | HaloCmdSignVariant3; + +export interface HaloResInputObj { + keyNo: KeySlotNo + digest: HexString + message?: HexString + typedData?: { + domain: TypedDataDomain + types: Record> + value: Record + primaryType: string + domainHash: string + } +} + +export interface HaloResSign { + input: HaloResInputObj + signature: { + der: HexString + raw?: { + r: string + s: string + v: number + } + ether?: string + } + publicKey?: string + etherAddress?: string +} + +export interface HaloCmdGetDataStruct { + spec: string +} + +export interface HaloResGetDataStruct { + isPartial: boolean + data: Record +} + +export interface HaloCmdGetGraffiti { + slotNo: number +} + +export interface HaloResGetGraffiti { + data: ASCIIString +} + +export interface HaloCmdStoreGraffiti { + slotNo: number, + data: ASCIIString +} + +export interface HaloResStoreGraffiti { + status: "ok" +} + +export interface HaloCmdWriteLatch { + data: HexString + latchNo: number +} + +export interface HaloResWriteLatch { + status: "ok" +} + +export interface HaloCmdSignRandom { + keyNo: KeySlotNo +} + +export interface HaloResSignRandom { + counter: number + payload: HexString + signature: HexString + publicKey: HexString +} + +export interface HaloCmdSignChallenge { + keyNo: KeySlotNo + challenge: HexString +} + +export interface HaloResSignChallenge { + signature: HexString + publicKey: HexString + attestSig: HexString +} + +export interface HaloCmdImportKeyInit { + keyNo: KeySlotNo + data: HexString +} + +export interface HaloResImportKeyInit { + status: "ok" +} + +export interface HaloCmdImportKey { + keyNo: KeySlotNo +} + +export interface HaloResImportKey { + publicKey: HexString +} + +export interface HaloCmdExportKey { + keyNo: KeySlotNo + password: ASCIIString + data: HexString +} + +export interface HaloResExportKey { + data: HexString +} + +export interface HaloCmdGetTransportPK { + +} + +export interface HaloResGetTransportPK { + data: HexString + rootPublicKey: HexString +} + +export interface HaloCmdLoadTransportPK { + data: HexString +} + +export interface HaloResLoadTransportPK { + data: HexString + rootPublicKey: HexString +} + +export interface HaloCmdSetPassword { + password: ASCIIString + keyNo: KeySlotNo +} + +export interface HaloResSetPassword { + status: "ok" +} + +export interface HaloCmdUnsetPassword { + password: ASCIIString + keyNo: KeySlotNo +} + +export interface HaloResUnsetPassword { + status: "ok" +} + +export interface HaloCmdReplacePassword { + currentPassword: ASCIIString + newPassword: ASCIIString + keyNo: KeySlotNo +} + +export interface HaloResReplacePassword { + status: "ok" +} + +export interface HaloCmdGetKeyInfo { + keyNo: KeySlotNo +} + +export interface HaloResGetKeyInfo { + keyState: KeyState + publicKey: HexString + attestSig: HexString +} + +export interface HaloCmdSetURLSubdomain { + subdomain: ASCIIString + allowSignatureDER: HexString +} + +export interface HaloResSetURLSubdomain { + status: "ok" +} diff --git a/core/src.ts/halo/commands.js b/core/src.ts/halo/commands.ts similarity index 61% rename from core/src.ts/halo/commands.js rename to core/src.ts/halo/commands.ts index 3f1f0db..a386af2 100644 --- a/core/src.ts/halo/commands.js +++ b/core/src.ts/halo/commands.ts @@ -4,17 +4,59 @@ * License: MIT */ +import crypto from 'crypto'; + import {Buffer} from 'buffer/index.js'; import {ethers} from 'ethers'; import {HaloLogicError, HaloTagError} from "./exceptions.js"; -import {convertSignature, mode, parseSig, parsePublicKeys, randomBuffer, SECP256k1_ORDER, BJJ_ORDER, sigToDer} from "./util.js"; +import { + convertSignature, + mode, + parseSig, + parsePublicKeys, + randomBuffer, + SECP256k1_ORDER, + BJJ_ORDER, + sigToDer +} from "./util.js"; import {FLAGS} from "./flags.js"; import {sha256} from "js-sha256"; import elliptic from 'elliptic'; import {CMD_CODES as CMD} from './cmdcodes.js'; import pbkdf2 from 'pbkdf2'; -import crypto from 'crypto-browserify'; import {KEY_FLAGS, parseKeyFlags} from "./keyflags.js"; +import { + ExecHaloCmdOptions, + ExecReturnStruct, HaloCmdCFGNDEF, HaloCmdGenKey, HaloCmdGenKeyConfirm, HaloCmdGenKeyFinalize, + HaloResCFGNDEF, HaloResGenKey, HaloResGenKeyConfirm, HaloResGenKeyFinalize, + PublicKeyList +} from "../types.js"; +import { + HaloCmdExportKey, + HaloCmdGetDataStruct, + HaloCmdGetGraffiti, HaloCmdGetKeyInfo, + HaloCmdGetPkeys, + HaloCmdGetTransportPK, + HaloCmdImportKey, + HaloCmdImportKeyInit, HaloCmdLoadTransportPK, HaloCmdReplacePassword, HaloCmdSetPassword, HaloCmdSetURLSubdomain, + HaloCmdSign, + HaloCmdSignChallenge, + HaloCmdSignRandom, + HaloCmdStoreGraffiti, HaloCmdUnsetPassword, + HaloCmdWriteLatch, + HaloResExportKey, + HaloResGetDataStruct, + HaloResGetGraffiti, HaloResGetKeyInfo, + HaloResGetPkeys, HaloResGetTransportPK, + HaloResImportKey, + HaloResImportKeyInit, + HaloResInputObj, HaloResLoadTransportPK, HaloResReplacePassword, HaloResSetPassword, HaloResSetURLSubdomain, + HaloResSign, + HaloResSignChallenge, + HaloResSignRandom, + HaloResStoreGraffiti, HaloResUnsetPassword, + HaloResWriteLatch +} from "./command_types.js"; const ec = new elliptic.ec('secp256k1'); @@ -33,33 +75,33 @@ const ec = new elliptic.ec('secp256k1'); * if that's possible. */ -function extractPublicKeyWebNFC(keyNo, resp) { +function extractPublicKeyWebNFC(keyNo: number, resp: ExecReturnStruct) { let publicKey = null; - let pkKey = "pk" + keyNo; + const pkKey = "pk" + keyNo; - if (resp.extra.hasOwnProperty(pkKey)) { + if (Object.prototype.hasOwnProperty.call(resp.extra, pkKey)) { publicKey = Buffer.from(resp.extra[pkKey], "hex"); - } else if (resp.extra.hasOwnProperty("static")) { - let pkeys = parsePublicKeys(Buffer.from(resp.extra["static"], "hex")); + } else if (Object.prototype.hasOwnProperty.call(resp.extra, "static")) { + const pkeys = parsePublicKeys(Buffer.from(resp.extra["static"], "hex")); publicKey = pkeys[keyNo]; } return publicKey; } -async function cmdGetPkeys(options, args) { - let payload = Buffer.concat([ +async function cmdGetPkeys(options: ExecHaloCmdOptions, args: HaloCmdGetPkeys): Promise { + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_GET_PKEYS]) ]); - let resp = await options.exec(payload); - let res = Buffer.from(resp.result, "hex"); + const resp = await options.exec(payload); + const res = Buffer.from(resp.result, "hex"); - let publicKeys = parsePublicKeys(res); - let compressedPublicKeys = {}; - let etherAddresses = {}; + const publicKeys = parsePublicKeys(res); + const compressedPublicKeys: PublicKeyList = {}; + const etherAddresses: PublicKeyList = {}; - for (let pkNo of Object.keys(publicKeys)) { + for (const pkNo of Object.keys(publicKeys).map(Number)) { compressedPublicKeys[pkNo] = ec.keyFromPublic(publicKeys[pkNo], 'hex').getPublic().encodeCompressed('hex'); etherAddresses[pkNo] = ethers.computeAddress('0x' + publicKeys[pkNo]); } @@ -67,14 +109,14 @@ async function cmdGetPkeys(options, args) { return {publicKeys, compressedPublicKeys, etherAddresses}; } -async function cmdSign(options, args) { - let checks = [ - args.hasOwnProperty("digest") && typeof args.digest !== "undefined", - args.hasOwnProperty("message") && typeof args.message !== "undefined", - args.hasOwnProperty("typedData") && typeof args.typedData !== "undefined" +async function cmdSign(options: ExecHaloCmdOptions, args: HaloCmdSign): Promise { + const checks = [ + Object.prototype.hasOwnProperty.call(args, "digest") && typeof args.digest !== "undefined", + Object.prototype.hasOwnProperty.call(args, "message") && typeof args.message !== "undefined", + Object.prototype.hasOwnProperty.call(args, "typedData") && typeof args.typedData !== "undefined" ]; - let numDataArgs = checks.filter((x) => !!x).length; + const numDataArgs = checks.filter((x) => !!x).length; if (numDataArgs !== 1) { throw new HaloLogicError("One of the following arguments are required: digest, message, typedData."); @@ -83,7 +125,7 @@ async function cmdSign(options, args) { let messageBuf = null; let digestBuf = null; - if (args.hasOwnProperty("message") && typeof args.message !== "undefined") { + if (Object.prototype.hasOwnProperty.call(args, "message") && typeof args.message !== "undefined") { if (args.format === "text") { messageBuf = Buffer.from(args.message); } else if (!args.format || args.format === "hex") { @@ -103,7 +145,7 @@ async function cmdSign(options, args) { } digestBuf = Buffer.from(ethers.hashMessage(messageBuf).slice(2), "hex"); - } else if (args.hasOwnProperty("typedData") && typeof args.typedData !== "undefined") { + } else if (Object.prototype.hasOwnProperty.call(args, "typedData") && typeof args.typedData !== "undefined") { let hashStr; try { @@ -113,7 +155,7 @@ async function cmdSign(options, args) { } digestBuf = Buffer.from(hashStr.slice(2), "hex"); - } else if (args.hasOwnProperty("digest") && typeof args.digest !== "undefined") { + } else if (Object.prototype.hasOwnProperty.call(args, "digest") && typeof args.digest !== "undefined") { digestBuf = Buffer.from(args.digest, "hex"); if (args.digest.length !== digestBuf.length * 2 || digestBuf.length !== 32) { @@ -127,7 +169,7 @@ async function cmdSign(options, args) { let pwdHash = null; if (args.password) { - let derivedKey = pbkdf2.pbkdf2Sync(args.password, 'HaLoChipSalt', 5000, 16, 'sha512'); + const derivedKey = pbkdf2.pbkdf2Sync(args.password, 'HaLoChipSalt', 5000, 16, 'sha512'); pwdHash = Buffer.from(sha256(Buffer.concat([ Buffer.from([0x19]), @@ -183,9 +225,9 @@ async function cmdSign(options, args) { throw e; } - let sigBuf = Buffer.from(resp.result, "hex"); - let sigLen = sigBuf[1] + 2; - let sig = sigBuf.slice(0, sigLen).toString('hex'); + const sigBuf = Buffer.from(resp.result, "hex"); + const sigLen = sigBuf[1] + 2; + const sig = sigBuf.slice(0, sigLen).toString('hex'); let publicKey = null; if ((!args.legacySignCommand && options.method !== "webnfc") || args.keyNo > 3) { @@ -198,18 +240,19 @@ async function cmdSign(options, args) { publicKey = extractPublicKeyWebNFC(args.keyNo, resp); } - let inputObj = { + const inputObj: HaloResInputObj = { "keyNo": args.keyNo, - "digest": digestBuf.toString('hex'), + "digest": digestBuf.toString('hex') }; if (messageBuf !== null) { inputObj.message = messageBuf.toString('hex'); } else if (args.typedData) { - inputObj.typedData = args.typedData; - - inputObj.primaryType = ethers.TypedDataEncoder.getPrimaryType(args.typedData.types); - inputObj.domainHash = ethers.TypedDataEncoder.hashDomain(args.typedData.domain).slice(2); + inputObj.typedData = { + primaryType: ethers.TypedDataEncoder.getPrimaryType(args.typedData.types), + domainHash: ethers.TypedDataEncoder.hashDomain(args.typedData.domain).slice(2), + ...args.typedData + }; } if (args.keyNo >= 0x60) { @@ -218,7 +261,7 @@ async function cmdSign(options, args) { "signature": { "der": sigToDer(parseSig(Buffer.from(sig, "hex"), BJJ_ORDER)).toString('hex') }, - "publicKey": publicKey.toString('hex') + "publicKey": publicKey ? publicKey.toString('hex') : undefined }; } @@ -239,8 +282,8 @@ async function cmdSign(options, args) { } } -async function cmdWriteLatch(options, args) { - let payload = Buffer.concat([ +async function cmdWriteLatch(options: ExecHaloCmdOptions, args: HaloCmdWriteLatch): Promise { + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_LATCH_DATA, args.latchNo]), Buffer.from(args.data, "hex") ]); @@ -249,15 +292,15 @@ async function cmdWriteLatch(options, args) { return {"status": "ok"}; } -async function cmdSignRandom(options, args) { - let resp = await options.exec(Buffer.from([CMD.SHARED_CMD_SIGN_RANDOM, args.keyNo])); +async function cmdSignRandom(options: ExecHaloCmdOptions, args: HaloCmdSignRandom): Promise { + const resp = await options.exec(Buffer.from([CMD.SHARED_CMD_SIGN_RANDOM, args.keyNo])); - let resBuf = Buffer.from(resp.result, 'hex'); - let digest = resBuf.slice(0, 32); + const resBuf = Buffer.from(resp.result, 'hex'); + const digest = resBuf.slice(0, 32); let signature = resBuf.slice(32, 32 + resBuf[33] + 2); - let publicKey = resBuf.slice(32 + resBuf[33] + 2); + const publicKey = resBuf.slice(32 + resBuf[33] + 2); - let counter = digest.readUInt32BE(0); + const counter = digest.readUInt32BE(0); if (args.keyNo >= 0x60) { signature = sigToDer(parseSig(signature, BJJ_ORDER)); @@ -273,16 +316,16 @@ async function cmdSignRandom(options, args) { }; } -async function cmdSignChallenge(options, args) { - let challengeBuf = Buffer.from(args.challenge, "hex"); - let resp = await options.exec(Buffer.from([CMD.SHARED_CMD_SIGN_CHALLENGE, args.keyNo, ...challengeBuf])); +async function cmdSignChallenge(options: ExecHaloCmdOptions, args: HaloCmdSignChallenge): Promise { + const challengeBuf = Buffer.from(args.challenge, "hex"); + const resp = await options.exec(Buffer.from([CMD.SHARED_CMD_SIGN_CHALLENGE, args.keyNo, ...challengeBuf])); - let resBuf = Buffer.from(resp.result, 'hex'); - let sigLen = 2 + resBuf[1]; + const resBuf = Buffer.from(resp.result, 'hex'); + const sigLen = 2 + resBuf[1]; let signature = resBuf.slice(0, sigLen); - let publicKey = resBuf.slice(sigLen, sigLen + 65); - let attestSig = resBuf.slice(sigLen + 65); + const publicKey = resBuf.slice(sigLen, sigLen + 65); + const attestSig = resBuf.slice(sigLen + 65); if (args.keyNo >= 0x60) { signature = sigToDer(parseSig(signature, BJJ_ORDER)); @@ -297,7 +340,7 @@ async function cmdSignChallenge(options, args) { }; } -async function cmdCfgNDEF(options, args) { +async function cmdCfgNDEF(options: ExecHaloCmdOptions, args: HaloCmdCFGNDEF): Promise { if (args.flagHidePk1 && args.flagHidePk2) { throw new HaloLogicError("It's not allowed to use both flagHidePk1 and flagHidePk2."); } @@ -305,7 +348,7 @@ async function cmdCfgNDEF(options, args) { let flagBuf = Buffer.alloc(3); Object.keys(args) - .filter((k) => k.startsWith('flag') && args[k]) + .filter((k) => k.startsWith('flag') && args[k as keyof typeof args]) .map((k) => FLAGS[k]) .forEach((v) => { flagBuf[v[0]] |= v[1]; @@ -318,7 +361,7 @@ async function cmdCfgNDEF(options, args) { flagBuf[2] = args.pkN; } - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_SET_NDEF_MODE]), flagBuf ]); @@ -330,7 +373,7 @@ async function cmdCfgNDEF(options, args) { }; } -async function cmdGenKey(options, args) { +async function cmdGenKey(options: ExecHaloCmdOptions, args: HaloCmdGenKey): Promise { if (!args.entropy) { if (options.method === "pcsc") { args.entropy = randomBuffer().toString("hex"); @@ -339,13 +382,13 @@ async function cmdGenKey(options, args) { } } - let entropyBuf = Buffer.from(args.entropy, "hex"); + const entropyBuf = Buffer.from(args.entropy, "hex"); if (entropyBuf.length !== 32) { throw new HaloLogicError("The command.entropy should be exactly 32 bytes, hex encoded."); } - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_GENERATE_KEY_INIT]), Buffer.from([args.keyNo]), entropyBuf @@ -365,25 +408,25 @@ async function cmdGenKey(options, args) { throw e; } - let res = Buffer.from(resp.result, "hex"); + const res = Buffer.from(resp.result, "hex"); if (res[0] === 0x00) { - let m1Prefixed = Buffer.concat([ + const m1Prefixed = Buffer.concat([ Buffer.from([0x19]), Buffer.from("Key generation sample:\n"), res.slice(1, 1 + 32) ]); - let m2Prefixed = Buffer.concat([ + const m2Prefixed = Buffer.concat([ Buffer.from([0x19]), Buffer.from("Key generation sample:\n"), res.slice(1 + 32, 1 + 64) ]); - let msg1 = Buffer.from(sha256(m1Prefixed), 'hex'); - let msg2 = Buffer.from(sha256(m2Prefixed), 'hex'); - let sig = res.slice(1 + 64); - let sig1Length = sig[1]; - let sig1 = sig.slice(0, 2 + sig1Length); - let sig2 = sig.slice(2 + sig1Length); + const msg1 = Buffer.from(sha256(m1Prefixed), 'hex'); + const msg2 = Buffer.from(sha256(m2Prefixed), 'hex'); + const sig = res.slice(1 + 64); + const sig1Length = sig[1]; + const sig1 = sig.slice(0, 2 + sig1Length); + const sig2 = sig.slice(2 + sig1Length); let curveOrder = SECP256k1_ORDER; @@ -391,21 +434,21 @@ async function cmdGenKey(options, args) { curveOrder = BJJ_ORDER; } - let candidates = []; + const candidates: string[] = []; for (let i = 0; i < 2; i++) { candidates.push(ec.recoverPubKey(msg1, parseSig(sig1, curveOrder), i).encode('hex')); candidates.push(ec.recoverPubKey(msg2, parseSig(sig2, curveOrder), i).encode('hex')); } - let bestPk = Buffer.from(mode(candidates), 'hex'); + const bestPk = Buffer.from(mode(candidates), 'hex'); return { "publicKey": bestPk.toString('hex'), "needsConfirmPK": true }; } else if (res[0] === 0x01) { - let rootKeyPk = res.slice(0, 65); - let rootKeyAttest = res.slice(65); + const rootKeyPk = res.slice(0, 65); + const rootKeyAttest = res.slice(65); return { "rootPublicKey": rootKeyPk.toString('hex'), @@ -417,18 +460,18 @@ async function cmdGenKey(options, args) { } } -async function cmdGenKeyConfirm(options, args) { - let payload = Buffer.concat([ +async function cmdGenKeyConfirm(options: ExecHaloCmdOptions, args: HaloCmdGenKeyConfirm): Promise { + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_GENERATE_KEY_CONT]), Buffer.from([args.keyNo]), Buffer.from(args.publicKey, "hex") ]); - let resp = await options.exec(payload); - let res = Buffer.from(resp.result, "hex"); + const resp = await options.exec(payload); + const res = Buffer.from(resp.result, "hex"); - let rootPublicKey = res.slice(0, 65); - let rootAttestSig = res.slice(65 + 1); + const rootPublicKey = res.slice(0, 65); + const rootAttestSig = res.slice(65 + 1); return { rootPublicKey: rootPublicKey.toString('hex'), @@ -436,11 +479,11 @@ async function cmdGenKeyConfirm(options, args) { }; } -async function cmdGenKeyFinalize(options, args) { +async function cmdGenKeyFinalize(options: ExecHaloCmdOptions, args: HaloCmdGenKeyFinalize): Promise { let payload; if (args.password) { - let derivedKey = pbkdf2.pbkdf2Sync(args.password, 'HaLoChipSalt', 5000, 16, 'sha512'); + const derivedKey = pbkdf2.pbkdf2Sync(args.password, 'HaLoChipSalt', 5000, 16, 'sha512'); payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_GENERATE_KEY_FIN_PWD]), @@ -454,12 +497,12 @@ async function cmdGenKeyFinalize(options, args) { ]); } - let resp = await options.exec(payload); - let res = Buffer.from(resp.result, "hex"); + const resp = await options.exec(payload); + const res = Buffer.from(resp.result, "hex"); - let newKeyNo = res.slice(0, 1); - let publicKey = res.slice(1, 1 + 65); - let attestSig = res.slice(1 + 65); + // const newKeyNo = res.slice(0, 1); + const publicKey = res.slice(1, 1 + 65); + const attestSig = res.slice(1 + 65); return { publicKey: publicKey.toString('hex'), @@ -467,8 +510,8 @@ async function cmdGenKeyFinalize(options, args) { }; } -async function cmdSetURLSubdomain(options, args) { - let payload = Buffer.concat([ +async function cmdSetURLSubdomain(options: ExecHaloCmdOptions, args: HaloCmdSetURLSubdomain): Promise { + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_SET_URL_SUBDOMAIN]), Buffer.from([args.subdomain.length]), Buffer.from(args.subdomain), @@ -480,16 +523,16 @@ async function cmdSetURLSubdomain(options, args) { return {"status": "ok"}; } -async function cmdGetKeyInfo(options, args) { - let payload = Buffer.concat([ +async function cmdGetKeyInfo(options: ExecHaloCmdOptions, args: HaloCmdGetKeyInfo): Promise { + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_GET_KEY_INFO]), Buffer.from([args.keyNo]), ]); - let resp = await options.exec(payload); - let res = Buffer.from(resp.result, "hex"); + const resp = await options.exec(payload); + const res = Buffer.from(resp.result, "hex"); - let keyFlags = res.slice(1, 2)[0]; + const keyFlags = res.slice(1, 2)[0]; let failedAuthCtr = 0; let off = 2; @@ -499,8 +542,8 @@ async function cmdGetKeyInfo(options, args) { failedAuthCtr = res.slice(2, 3)[0]; } - let publicKey = res.slice(off, off + 65); - let attestSig = res.slice(off + 65); + const publicKey = res.slice(off, off + 65); + const attestSig = res.slice(off + 65); return { keyState: { @@ -512,10 +555,10 @@ async function cmdGetKeyInfo(options, args) { }; } -async function cmdSetPassword(options, args) { - let derivedKey = pbkdf2.pbkdf2Sync(args.password, 'HaLoChipSalt', 5000, 16, 'sha512'); +async function cmdSetPassword(options: ExecHaloCmdOptions, args: HaloCmdSetPassword): Promise { + const derivedKey = pbkdf2.pbkdf2Sync(args.password, 'HaLoChipSalt', 5000, 16, 'sha512'); - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_SET_PASSWORD]), Buffer.from([args.keyNo]), derivedKey @@ -526,16 +569,16 @@ async function cmdSetPassword(options, args) { return {"status": "ok"}; } -async function cmdUnsetPassword(options, args) { - let derivedKey = pbkdf2.pbkdf2Sync(args.password, 'HaLoChipSalt', 5000, 16, 'sha512'); - let authHash = Buffer.from(sha256(Buffer.concat([ +async function cmdUnsetPassword(options: ExecHaloCmdOptions, args: HaloCmdUnsetPassword): Promise { + const derivedKey = pbkdf2.pbkdf2Sync(args.password, 'HaLoChipSalt', 5000, 16, 'sha512'); + const authHash = Buffer.from(sha256(Buffer.concat([ Buffer.from([0x19]), Buffer.from("Unset password auth:\n"), Buffer.from([args.keyNo]), derivedKey ])), "hex"); - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_UNSET_PASSWORD]), Buffer.from([args.keyNo]), authHash @@ -546,20 +589,20 @@ async function cmdUnsetPassword(options, args) { return {"status": "ok"}; } -async function cmdReplacePassword(options, args) { - let curPassword = pbkdf2.pbkdf2Sync(args.currentPassword, 'HaLoChipSalt', 5000, 16, 'sha512'); - let newPassword = pbkdf2.pbkdf2Sync(args.newPassword, 'HaLoChipSalt', 5000, 16, 'sha512'); +async function cmdReplacePassword(options: ExecHaloCmdOptions, args: HaloCmdReplacePassword): Promise { + const curPassword = pbkdf2.pbkdf2Sync(args.currentPassword, 'HaLoChipSalt', 5000, 16, 'sha512'); + const newPassword = pbkdf2.pbkdf2Sync(args.newPassword, 'HaLoChipSalt', 5000, 16, 'sha512'); - let plaintext = Buffer.concat([ + const plaintext = Buffer.concat([ Buffer.from(sha256(newPassword), "hex").slice(0, 16), newPassword ]); - let cipher = crypto.createCipheriv('aes-128-cbc', curPassword, Buffer.alloc(16)); + const cipher = crypto.createCipheriv('aes-128-cbc', curPassword, Buffer.alloc(16)); cipher.setAutoPadding(false); - let ct = Buffer.from(cipher.update(plaintext, 'buffer', 'hex') + cipher.final('hex'), 'hex'); + const ct = Buffer.from(cipher.update(plaintext, undefined, 'hex') + cipher.final('hex'), 'hex'); - let authHash = Buffer.from(sha256(Buffer.concat([ + const authHash = Buffer.from(sha256(Buffer.concat([ Buffer.from([0x19]), Buffer.from("Replace password auth:\n"), Buffer.from([args.keyNo]), @@ -567,7 +610,7 @@ async function cmdReplacePassword(options, args) { curPassword ])), "hex"); - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_REPLACE_PASSWORD]), Buffer.from([args.keyNo]), ct, @@ -579,17 +622,17 @@ async function cmdReplacePassword(options, args) { return {"status": "ok"}; } -async function _internalLoadPK(options, payload) { - let resp = await options.exec(payload, {pcscExecLayer: "u2f"}); - let res = Buffer.from(resp.result, "hex"); +async function _internalLoadPK(options: ExecHaloCmdOptions, payload: Buffer) { + const resp = await options.exec(payload, {pcscExecLayer: "u2f"}); + const res = Buffer.from(resp.result, "hex"); if (res[0] !== 0x01) { throw new HaloLogicError("Unsupported protocol version reported by the HaLo tag."); } - let sigLen = res[2] + 2; - let data = res.slice(1, sigLen + 1 + 65); - let rootPK = res.slice(sigLen + 1 + 65); + const sigLen = res[2] + 2; + const data = res.slice(1, sigLen + 1 + 65); + const rootPK = res.slice(sigLen + 1 + 65); return { "data": data.toString('hex'), @@ -597,24 +640,24 @@ async function _internalLoadPK(options, payload) { } } -async function cmdGetTransportPK(options, args) { +async function cmdGetTransportPK(options: ExecHaloCmdOptions, args: HaloCmdGetTransportPK): Promise { if (options.method !== "credential" && options.method !== "pcsc") { throw new HaloLogicError("Unsupported execution method. Please set options.method = 'credential'."); } - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.CRED_CMD_GET_TRANSPORT_PK_ATT]) ]); return await _internalLoadPK(options, payload); } -async function cmdLoadTransportPK(options, args) { +async function cmdLoadTransportPK(options: ExecHaloCmdOptions, args: HaloCmdLoadTransportPK): Promise { if (options.method !== "credential" && options.method !== "pcsc") { throw new HaloLogicError("Unsupported execution method. Please set options.method = 'credential'."); } - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.CRED_CMD_LOAD_TRANSPORT_PK]), Buffer.from(args.data, 'hex') ]); @@ -622,17 +665,17 @@ async function cmdLoadTransportPK(options, args) { return await _internalLoadPK(options, payload); } -async function cmdExportKey(options, args) { +async function cmdExportKey(options: ExecHaloCmdOptions, args: HaloCmdExportKey): Promise { if (options.method !== "credential" && options.method !== "pcsc") { throw new HaloLogicError("Unsupported execution method. Please set options.method = 'credential'."); } - let derivedKey = pbkdf2.pbkdf2Sync(args.password, 'HaLoChipSalt', 5000, 16, 'sha512'); - let dataBuf = Buffer.from(args.data, 'hex'); - let sigLen = dataBuf[1] + 2; - let publicKeyBuf = dataBuf.slice(sigLen); + const derivedKey = pbkdf2.pbkdf2Sync(args.password, 'HaLoChipSalt', 5000, 16, 'sha512'); + const dataBuf = Buffer.from(args.data, 'hex'); + const sigLen = dataBuf[1] + 2; + const publicKeyBuf = dataBuf.slice(sigLen); - let pwdHash = Buffer.from(sha256(Buffer.concat([ + const pwdHash = Buffer.from(sha256(Buffer.concat([ Buffer.from([0x19]), Buffer.from("Key backup:\n"), Buffer.from([args.keyNo]), @@ -640,26 +683,26 @@ async function cmdExportKey(options, args) { publicKeyBuf, ])), "hex"); - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.CRED_CMD_EXPORT_KEY]), Buffer.from([args.keyNo]), pwdHash ]); - let resp = await options.exec(payload, {pcscExecLayer: "u2f"}); - let res = Buffer.from(resp.result, "hex"); + const resp = await options.exec(payload, {pcscExecLayer: "u2f"}); + const res = Buffer.from(resp.result, "hex"); return { "data": res.toString('hex') } } -async function cmdImportKeyInit(options, args) { +async function cmdImportKeyInit(options: ExecHaloCmdOptions, args: HaloCmdImportKeyInit): Promise { if (options.method !== "credential" && options.method !== "pcsc") { throw new HaloLogicError("Unsupported execution method. Please set options.method = 'credential'."); } - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.CRED_CMD_IMPORT_KEY_INIT]), Buffer.from([args.keyNo]), Buffer.from(args.data, 'hex') @@ -672,40 +715,40 @@ async function cmdImportKeyInit(options, args) { } } -async function cmdImportKey(options, args) { +async function cmdImportKey(options: ExecHaloCmdOptions, args: HaloCmdImportKey): Promise { if (options.method !== "credential" && options.method !== "pcsc") { throw new HaloLogicError("Unsupported execution method. Please set options.method = 'credential'."); } - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.CRED_CMD_IMPORT_KEY]), Buffer.from([args.keyNo]) ]); - let resp = await options.exec(payload, {pcscExecLayer: "u2f"}); - let res = Buffer.from(resp.result, "hex"); + const resp = await options.exec(payload, {pcscExecLayer: "u2f"}); + const res = Buffer.from(resp.result, "hex"); return { "publicKey": res.slice(1).toString('hex') } } -async function cmdGetDataStruct(options, args) { - let specItems = args.spec.split(','); - specItems = specItems.map((item) => item.split(':', 2)); +async function cmdGetDataStruct(options: ExecHaloCmdOptions, args: HaloCmdGetDataStruct): Promise { + const specParts = args.spec.split(','); + let specItems = specParts.map((item: string) => item.split(':', 2)); - const TYPES = { - "publicKey": 0x01, - "publicKeyAttest": 0x02, - "keySlotFlags": 0x03, + const TYPES: Record = { + "publicKey": 0x01, + "publicKeyAttest": 0x02, + "keySlotFlags": 0x03, "keySlotFailedAuthCtr": 0x04, - "latchValue": 0x20, - "latchAttest": 0x21, - "graffiti": 0x22, - "firmwareVersion": 0xF0 + "latchValue": 0x20, + "latchAttest": 0x21, + "graffiti": 0x22, + "firmwareVersion": 0xF0 }; - const SPECIAL_MSG = { + const SPECIAL_MSG: Record = { 0x01: "keySlotOutOfBounds", 0x02: "keySlotNotGenerated", 0x03: "latchNotSet", @@ -714,12 +757,12 @@ async function cmdGetDataStruct(options, args) { let data = Buffer.alloc(0); - for (let item of specItems) { + for (const item of specItems) { if (!TYPES[item[0]]) { throw new HaloLogicError("Unsupported object type: " + item[0]); } - let val = parseInt(item[1]); + const val = parseInt(item[1]); if (val < 0 || val > 255) { throw new HaloLogicError("Too high index value at: " + item[0] + ":" + item[1]); @@ -731,19 +774,19 @@ async function cmdGetDataStruct(options, args) { ]); } - let payload = Buffer.concat([ + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_GET_DATA_STRUCT]), data ]); - let resp = await options.exec(payload); + const resp = await options.exec(payload); let res = Buffer.from(resp.result, "hex"); specItems = specItems.reverse(); - let out = {}; + const out: Record = {}; while (res.length > 0) { - let item = specItems.pop(); + const item = specItems.pop() as string[]; let len = res[0]; let data; @@ -752,8 +795,8 @@ async function cmdGetDataStruct(options, args) { if (len === 0xFF) { // no value returned, special message len = 1; - let msgCode = res.slice(1, 2)[0]; - let specialMsg = SPECIAL_MSG[msgCode]; + const msgCode = res.slice(1, 2)[0]; + const specialMsg = SPECIAL_MSG[msgCode]; if (specialMsg) { value = {"error": specialMsg}; @@ -761,7 +804,7 @@ async function cmdGetDataStruct(options, args) { value = {"error": 'unknown_' + msgCode.toString()}; } } else if (item[0] === "keySlotFlags") { - let keyFlags = res.slice(1, len + 1)[0]; + const keyFlags = res.slice(1, len + 1)[0]; value = parseKeyFlags(keyFlags); } else if (item[0] === "keySlotFailedAuthCtr") { value = res.slice(1, len + 1)[0]; @@ -786,22 +829,22 @@ async function cmdGetDataStruct(options, args) { }; } -async function cmdGetGraffiti(options, args) { - let payload = Buffer.concat([ +async function cmdGetGraffiti(options: ExecHaloCmdOptions, args: HaloCmdGetGraffiti): Promise { + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_GET_GRAFFITI]), Buffer.from([args.slotNo]) ]); - let resp = await options.exec(payload); - let res = Buffer.from(resp.result, "hex"); + const resp = await options.exec(payload); + const res = Buffer.from(resp.result, "hex"); return { "data": res.slice(1).toString('ascii') } } -async function cmdStoreGraffiti(options, args) { - let payload = Buffer.concat([ +async function cmdStoreGraffiti(options: ExecHaloCmdOptions, args: HaloCmdStoreGraffiti): Promise { + const payload = Buffer.concat([ Buffer.from([CMD.SHARED_CMD_STORE_GRAFFITI]), Buffer.from([args.slotNo]), Buffer.from(args.data, 'ascii') @@ -838,3 +881,5 @@ export { cmdGetGraffiti, cmdStoreGraffiti, }; + +export type * from "./command_types.js"; diff --git a/core/src.ts/halo/errors.js b/core/src.ts/halo/errors.ts similarity index 94% rename from core/src.ts/halo/errors.js rename to core/src.ts/halo/errors.ts index 36b6ed3..99952ac 100644 --- a/core/src.ts/halo/errors.js +++ b/core/src.ts/halo/errors.ts @@ -4,7 +4,11 @@ * License: MIT */ -let ERROR_CODES = { +type ErrorCodeLookup = { + [key: number]: [ErrorCode: string, ErrorDescription: string]; +}; + +const ERROR_CODES: ErrorCodeLookup = { 0x01: ["ERROR_CODE_UNKNOWN_CMD", "Unknown command code."], 0x02: ["ERROR_CODE_INVALID_KEY_NO", "Invalid key number."], 0x03: ["ERROR_CODE_INVALID_LENGTH", "Invalid length."], diff --git a/core/src.ts/halo/exceptions.js b/core/src.ts/halo/exceptions.ts similarity index 65% rename from core/src.ts/halo/exceptions.js rename to core/src.ts/halo/exceptions.ts index 4555c39..634e0d2 100644 --- a/core/src.ts/halo/exceptions.js +++ b/core/src.ts/halo/exceptions.ts @@ -9,9 +9,12 @@ * The "name" property will contain the exact error name (e.g. ERROR_CODE_INVALID_KEY_NO). */ class HaloTagError extends Error { - constructor(name, message) { + public stackOnExecutor: string | undefined; + + constructor(name: string, message: string, stackOnExecutor?: string) { super("The NFC tag encountered an error when executing command: " + message); this.name = name; + this.stackOnExecutor = stackOnExecutor; } } @@ -20,9 +23,12 @@ class HaloTagError extends Error { * Check "message" property for the detailed information. */ class HaloLogicError extends Error { - constructor(message) { + public stackOnExecutor: string | undefined; + + constructor(message: string, stackOnExecutor?: string) { super(message); this.name = "HaloLogicError"; + this.stackOnExecutor = stackOnExecutor; } } @@ -30,7 +36,7 @@ class HaloLogicError extends Error { * The user has denied NFC access permission. */ class NFCPermissionRequestDenied extends Error { - constructor(message) { + constructor(message: string) { super(message); this.name = "NFCPermissionRequestDenied"; } @@ -42,7 +48,7 @@ class NFCPermissionRequestDenied extends Error { * for the detailed explanation. */ class NFCMethodNotSupported extends Error { - constructor(message) { + constructor(message: string) { super(message); this.name = "NFCMethodNotSupported"; } @@ -54,7 +60,7 @@ class NFCMethodNotSupported extends Error { * This error should be ignored on the frontend. */ class NFCAbortedError extends Error { - constructor(message) { + constructor(message: string) { super(message); this.name = "NFCAbortedError"; } @@ -65,9 +71,12 @@ class NFCAbortedError extends Error { * Check "message" property for more details. */ class NFCOperationError extends Error { - constructor(message) { + public stackOnExecutor: string | undefined; + + constructor(message: string, stackOnExecutor?: string) { super(message); this.name = "NFCOperationError"; + this.stackOnExecutor = stackOnExecutor; } } @@ -76,7 +85,7 @@ class NFCOperationError extends Error { * and can no longer be used without creating a completely new instance first. */ class NFCBadTransportError extends Error { - constructor(message) { + constructor(message: string) { super(message); this.name = "NFCBadTransportError"; } @@ -86,12 +95,36 @@ class NFCBadTransportError extends Error { * The current origin is not on the HaLo Bridge's allow list. */ class NFCBridgeConsentError extends Error { - constructor(message) { + constructor(message: string) { super(message); this.name = "NFCBridgeConsentError"; } } +/** + * Bridge has encountered an internal unexpected error. + */ +class NFCBridgeUnexpectedError extends Error { + public stackOnExecutor: string; + + constructor(message: string, stackOnExecutor: string) { + super(message); + this.stackOnExecutor = stackOnExecutor; + } +} + +/** + * Gateway has encountered an internal unexpected error. + */ +class NFCGatewayUnexpectedError extends Error { + public stackOnExecutor: string; + + constructor(message: string, stackOnExecutor: string) { + super(message); + this.stackOnExecutor = stackOnExecutor; + } +} + export { HaloTagError, HaloLogicError, @@ -100,5 +133,7 @@ export { NFCAbortedError, NFCOperationError, NFCBadTransportError, - NFCBridgeConsentError + NFCBridgeConsentError, + NFCBridgeUnexpectedError, + NFCGatewayUnexpectedError, }; diff --git a/core/src.ts/halo/flags.js b/core/src.ts/halo/flags.ts similarity index 86% rename from core/src.ts/halo/flags.js rename to core/src.ts/halo/flags.ts index cd64b0c..d2ef73c 100644 --- a/core/src.ts/halo/flags.js +++ b/core/src.ts/halo/flags.ts @@ -4,7 +4,11 @@ * License: MIT */ -const FLAGS = { +type FlagLookup = { + [flagName: string]: [bytePos: number, bitPos: number]; +}; + +const FLAGS: FlagLookup = { flagUseText: [0, 0x01], flagHidePk1: [0, 0x02], flagHidePk2: [0, 0x04], diff --git a/core/src.ts/halo/gateway/executor.js b/core/src.ts/halo/gateway/executor.ts similarity index 73% rename from core/src.ts/halo/gateway/executor.js rename to core/src.ts/halo/gateway/executor.ts index 3c6bfcb..f6c6e3f 100644 --- a/core/src.ts/halo/gateway/executor.js +++ b/core/src.ts/halo/gateway/executor.ts @@ -2,28 +2,37 @@ import {JWEUtil} from "../jwe_util.js"; import queryString from "query-string"; import WebSocketAsPromised from "websocket-as-promised"; import {execHaloCmdWeb} from "../../drivers/web.js"; +import {HaloCommandObject} from "../../types.js"; -let gatewayServerHost = null; -let currentCmd = null; -let jweUtil = new JWEUtil(); -let wsp; +let gatewayServerHost: string | null = null; +let currentCmd: HaloCommandObject | null = null; +let wsp: WebSocketAsPromised | null = null; +const jweUtil = new JWEUtil(); -function createWs(url) { +function createWs(url: string) { return new WebSocketAsPromised(url, { packMessage: data => JSON.stringify(data), - unpackMessage: data => JSON.parse(data), + unpackMessage: data => JSON.parse(data as string), attachRequestId: (data, requestId) => Object.assign({uid: requestId}, data), extractRequestId: data => data && data.uid }); } -function haloGateExecutorSetHost(newGatewayServerHost) { +function haloGateExecutorSetHost(newGatewayServerHost: string) { gatewayServerHost = newGatewayServerHost; } -async function haloGateExecutorCreateWs(logCallback, newCommandCallback) { +interface ExecutorLogCallback { + (message: string): void +} + +interface ExecutorNewCommandCallback { + (command: HaloCommandObject): void +} + +async function haloGateExecutorCreateWs(logCallback: ExecutorLogCallback, newCommandCallback: ExecutorNewCommandCallback) { let serverPrefix; - let searchParts = window.location.hash.split('/'); + const searchParts = window.location.hash.split('/'); if (searchParts.length !== 3 || searchParts[0] !== "#!" || searchParts[2] !== "") { throw new Error("Malformed executor URL provided - failed to analyse fragment part."); @@ -78,9 +87,9 @@ async function haloGateExecutorCreateWs(logCallback, newCommandCallback) { await wsp.open(); } -async function haloGateExecutorUserConfirm(logCallback) { +async function haloGateExecutorUserConfirm(logCallback: ExecutorLogCallback) { let res; - let nonce = currentCmd.nonce; + const nonce = currentCmd.nonce; logCallback('Please tap HaLo tag to the back of your smartphone and hold it for a while...'); @@ -90,13 +99,15 @@ async function haloGateExecutorUserConfirm(logCallback) { output: await execHaloCmdWeb(currentCmd.command) }; } catch (e) { + const err = e as Error; + res = { status: "exception", exception: { - kind: e.constructor.name, - name: e.name, - message: e.message, - stack: e.stack + kind: err.constructor.name, + name: err.name, + message: err.message, + stack: err.stack } } } @@ -104,7 +115,7 @@ async function haloGateExecutorUserConfirm(logCallback) { logCallback('Command executed, sending result over the network...'); currentCmd = null; - wsp.sendPacked({ + wsp!.sendPacked({ "type": "executed_cmd", "payload": await jweUtil.encrypt({ response: res, diff --git a/core/src.ts/halo/gateway/requestor.js b/core/src.ts/halo/gateway/requestor.ts similarity index 81% rename from core/src.ts/halo/gateway/requestor.js rename to core/src.ts/halo/gateway/requestor.ts index f571788..de85508 100644 --- a/core/src.ts/halo/gateway/requestor.js +++ b/core/src.ts/halo/gateway/requestor.ts @@ -2,10 +2,19 @@ import QRCode from "qrcode"; import WebSocketAsPromised from "websocket-as-promised"; import crypto from "crypto"; import {JWEUtil} from "../jwe_util.js"; -import {HaloLogicError, HaloTagError, NFCBadTransportError, NFCAbortedError, NFCOperationError} from "../exceptions.js"; +import { + HaloLogicError, + HaloTagError, + NFCBadTransportError, + NFCAbortedError, + NFCOperationError, + NFCGatewayUnexpectedError +} from "../exceptions.js"; import {webDebug} from "../util.js"; +import {GatewayWelcomeMsg, HaloCommandObject} from "../../types.js"; -function makeQR(url) { + +function makeQR(url: string) { return new Promise((resolve, reject) => { QRCode.toDataURL(url, function (err, url) { if (err) { @@ -18,7 +27,20 @@ function makeQR(url) { } class HaloGateway { - constructor(gatewayServer, options) { + private jweUtil: JWEUtil; + private isRunning: boolean; + private hasExecutor: boolean; + private closeTimeout: NodeJS.Timeout | null; + + private lastCommand: null; + private gatewayServer: string; + private gatewayServerHttp: string; + + private ws: WebSocketAsPromised; + + constructor(gatewayServer: string, options: { + createWebSocket?: (url: string) => WebSocket + }) { this.jweUtil = new JWEUtil(); this.isRunning = false; this.hasExecutor = false; @@ -28,9 +50,9 @@ class HaloGateway { this.gatewayServer = gatewayServer; options = Object.assign({}, options); - let createWebSocket = options.createWebSocket ? options.createWebSocket : (url) => new WebSocket(url); + const createWebSocket = options.createWebSocket ? options.createWebSocket : (url: string) => new WebSocket(url); - let urlObj = new URL(gatewayServer); + const urlObj = new URL(gatewayServer); if (urlObj.protocol === 'wss:') { urlObj.protocol = 'https:'; @@ -51,13 +73,13 @@ class HaloGateway { this.ws = new WebSocketAsPromised(this.gatewayServer + '/ws?side=requestor', { createWebSocket: url => createWebSocket(url), packMessage: data => JSON.stringify(data), - unpackMessage: data => JSON.parse(data), + unpackMessage: data => JSON.parse(data as string), attachRequestId: (data, requestId) => Object.assign({uid: requestId}, data), extractRequestId: data => data && data.uid }); this.ws.onSend.addListener(data => { - let obj = JSON.parse(data); + const obj = JSON.parse(data); if (obj.type === "request_cmd") { this.lastCommand = obj; @@ -93,7 +115,7 @@ class HaloGateway { waitForWelcomePacket() { return new Promise((resolve, reject) => { - let welcomeWaitTimeout = setTimeout(() => { + const welcomeWaitTimeout = setTimeout(() => { reject(new NFCBadTransportError("Server doesn't send welcome packet for 6 seconds after accepting the connection.")); }, 6000); @@ -111,13 +133,13 @@ class HaloGateway { } async startPairing() { - let sharedKey = await this.jweUtil.generateKey(); + const sharedKey = await this.jweUtil.generateKey(); - let waitPromise = this.waitForWelcomePacket(); + const waitPromise = this.waitForWelcomePacket(); const promiseRes = await Promise.all([this.ws.open(), waitPromise]); - const welcomeMsg = promiseRes[1]; + const welcomeMsg = promiseRes[1] as GatewayWelcomeMsg; - let serverVersion = welcomeMsg.serverVersion; + const serverVersion = welcomeMsg.serverVersion; /** * URL format in the QR Code: @@ -135,8 +157,8 @@ class HaloGateway { * example: * https://dev-gate.example.com/e?id=-l6QxdU3xLyDTR2oT7bjnw#!/3LKNuIJV0Ltp0dhNw09tCQ/ */ - let execURL = this.gatewayServerHttp + '?id=' + welcomeMsg.sessionId + '#!/' + sharedKey + '/'; - let qrCode = await makeQR(execURL); + const execURL = this.gatewayServerHttp + '?id=' + welcomeMsg.sessionId + '#!/' + sharedKey + '/'; + const qrCode = await makeQR(execURL); return { execURL: execURL, @@ -159,7 +181,7 @@ class HaloGateway { }) } - async execHaloCmd(command) { + async execHaloCmd(command: HaloCommandObject) { webDebug('[halo-requestor] called execHaloCmd()', command); if (this.isRunning) { @@ -178,7 +200,7 @@ class HaloGateway { } this.isRunning = true; - let nonce = crypto.randomBytes(8).toString('hex'); + const nonce = crypto.randomBytes(8).toString('hex'); try { webDebug('[halo-requestor] sending request to execute command', nonce, command); @@ -194,7 +216,7 @@ class HaloGateway { }); } catch (e) { webDebug('[halo-requestor] exception when trying to sendRequest', e); - throw new NFCBadTransportError('Failed to send request: ' + e.toString()); + throw new NFCBadTransportError('Failed to send request: ' + ( e).toString()); } if (res.type !== "result_cmd") { @@ -217,7 +239,7 @@ class HaloGateway { throw new NFCBadTransportError("Mismatched nonce in reply."); } - let resolution = out.response; + const resolution = out.response; if (resolution.status === "success") { webDebug('[halo-requestor] returning with success', resolution.output); @@ -229,21 +251,20 @@ class HaloGateway { switch (resolution.exception.kind) { case 'HaloLogicError': - e = new HaloLogicError(resolution.exception.message); + e = new HaloLogicError(resolution.exception.message, resolution.exception.stack); break; case 'HaloTagError': - e = new HaloTagError(resolution.exception.name, resolution.exception.message); + e = new HaloTagError(resolution.exception.name, resolution.exception.message, resolution.exception.stack); break; case 'NFCOperationError': - e = new NFCOperationError(resolution.exception.message); + e = new NFCOperationError(resolution.exception.message, resolution.exception.stack); break; default: - e = new Error("Unexpected exception occurred while executing the command. " + - resolution.exception.name + ": " + resolution.exception.message); + e = new NFCGatewayUnexpectedError("Unexpected exception occurred while executing the command. " + + resolution.exception.name + ": " + resolution.exception.message, resolution.exception.stack); break; } - e.stackOnExecutor = resolution.exception.stack; webDebug('[halo-requestor] throwing exception as call result', e); throw e; } else { diff --git a/core/src.ts/halo/jwe_util.js b/core/src.ts/halo/jwe_util.ts similarity index 77% rename from core/src.ts/halo/jwe_util.js rename to core/src.ts/halo/jwe_util.ts index 88d7776..07fad10 100644 --- a/core/src.ts/halo/jwe_util.js +++ b/core/src.ts/halo/jwe_util.ts @@ -4,13 +4,15 @@ const subtle = crypto.webcrypto && crypto.webcrypto.subtle ? crypto.webcrypto.su import * as jose from 'jose'; class JWEUtil { - constructor() { + private sharedKeyObj: CryptoKey | null; + constructor() { + this.sharedKeyObj = null; } async generateKey() { - let sharedKey = crypto.randomBytes(16) - let sharedKeyEnc = sharedKey + const sharedKey = crypto.randomBytes(16) + const sharedKeyEnc = sharedKey .toString('base64') .replace('+', '-') .replace('/', '_') @@ -20,10 +22,11 @@ class JWEUtil { "encrypt", "decrypt", ]); + return sharedKeyEnc; } - async loadKey(sharedKey) { + async loadKey(sharedKey: string) { // automatically add "=" padding if it's not present let padLen = (-sharedKey.length % 3) + 3; @@ -41,14 +44,22 @@ class JWEUtil { ]); } - async encrypt(data) { + async encrypt(data: unknown) { + if (this.sharedKeyObj === null) { + throw new Error("Key is not loaded nor was generated."); + } + return await new jose.CompactEncrypt( new TextEncoder().encode(JSON.stringify(data))) .setProtectedHeader({alg: 'dir', enc: 'A128GCM'}) .encrypt(this.sharedKeyObj); } - async decrypt(jwe) { + async decrypt(jwe: string) { + if (this.sharedKeyObj === null) { + throw new Error("Key is not loaded nor was generated."); + } + const hdr = jose.decodeProtectedHeader(jwe); if (Object.keys(hdr).length !== 2 || hdr.alg !== "dir" || hdr.enc !== "A128GCM") { diff --git a/core/src.ts/halo/keyflags.js b/core/src.ts/halo/keyflags.ts similarity index 90% rename from core/src.ts/halo/keyflags.js rename to core/src.ts/halo/keyflags.ts index 91c4ac2..2f6de4c 100644 --- a/core/src.ts/halo/keyflags.js +++ b/core/src.ts/halo/keyflags.ts @@ -3,6 +3,7 @@ * Copyright by Arx Research, Inc., a Delaware corporation * License: MIT */ +import {KeyFlags} from "../types.js"; const KEY_FLAGS = { KEYFLG_IS_PWD_PROTECTED: 0x01, @@ -13,7 +14,7 @@ const KEY_FLAGS = { KEYFLG_IS_EXPORTED: 0x20 } -function parseKeyFlags(keyFlags) { +function parseKeyFlags(keyFlags: number): KeyFlags { return { isPasswordProtected: !!(keyFlags & KEY_FLAGS.KEYFLG_IS_PWD_PROTECTED), hasMandatoryPassword: !!(keyFlags & KEY_FLAGS.KEYFLG_MANDATORY_PASSWORD), diff --git a/core/src.ts/halo/tests.js b/core/src.ts/halo/tests.ts similarity index 66% rename from core/src.ts/halo/tests.js rename to core/src.ts/halo/tests.ts index e0df173..0ba6b47 100644 --- a/core/src.ts/halo/tests.js +++ b/core/src.ts/halo/tests.ts @@ -6,33 +6,34 @@ import {HaloLogicError, HaloTagError} from "../api/common.js"; import elliptic from 'elliptic'; +import {HaloCommandObject, HaloResponseObject} from "../types.js"; const ec = new elliptic.ec('secp256k1'); -function assert(condition) { +function assert(condition: unknown) { if (!condition) { throw new Error("Assertion failed."); } } class SkipTest extends Error { - constructor(message) { + constructor(message: string) { super(message); this.name = "SkipTest"; } } const tests = [ - ["testLegacySign1", async function(driver, exec) { + ["testLegacySign1", async function(driver: string, exec: (cmd: HaloCommandObject) => Promise) { if (driver !== 'webnfc') { - let resPkeys = await exec({ + const resPkeys = await exec({ "name": "get_pkeys" }); - let pk1 = ec.keyFromPublic(resPkeys.publicKeys[1], 'hex'); - let digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1"; + const pk1 = ec.keyFromPublic(resPkeys.publicKeys[1], 'hex'); + const digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1"; - let res = await exec({ + const res = await exec({ "name": "sign", "keyNo": 1, "digest": digest, @@ -41,32 +42,32 @@ const tests = [ assert(pk1.verify(digest, res.signature.der)); } else { - let digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1"; + const digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1"; - let res = await exec({ + const res = await exec({ "name": "sign", "keyNo": 1, "digest": digest, "legacySignCommand": true }); - let pk1 = ec.keyFromPublic(res.publicKey, 'hex'); + const pk1 = ec.keyFromPublic(res.publicKey, 'hex'); assert(pk1.verify(digest, res.signature.der)); } }], - ["testSign1", async function(driver, exec) { - let digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1"; + ["testSign1", async function(driver: string, exec: (cmd: HaloCommandObject) => Promise) { + const digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1"; - let res = await exec({ + const res = await exec({ "name": "sign", "keyNo": 1, "digest": digest }); - let pk1 = ec.keyFromPublic(res.publicKey, 'hex'); + const pk1 = ec.keyFromPublic(res.publicKey, 'hex'); assert(pk1.verify(digest, res.signature.der)); }], - ["testKeyGen3", async function(driver, exec) { + ["testKeyGen3", async function(driver: string, exec: (cmd: HaloCommandObject) => Promise) { let resGenKey; try { @@ -88,40 +89,40 @@ const tests = [ assert(resGenKey.needsConfirm); - let resGenKeyConfirm = await exec({ + const resGenKeyConfirm = await exec({ "name": "gen_key_confirm", "publicKey": resGenKey.publicKey }); assert(resGenKeyConfirm.status === "ok"); - let resGenKeyFinalize = await exec({ + const resGenKeyFinalize = await exec({ "name": "gen_key_finalize" }); assert(resGenKeyConfirm.status === "ok"); }], - ["testSign3", async function(driver, exec) { - let digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1"; + ["testSign3", async function(driver: string, exec: (cmd: HaloCommandObject) => Promise) { + const digest = "b64ab259577c3a28fda62c8e64744c8dd42a82155fbca7de02a1d85d8383d4e1"; - let res = await exec({ + const res = await exec({ "name": "sign", "keyNo": 3, "digest": digest }); - let pk3 = ec.keyFromPublic(res.publicKey, 'hex'); + const pk3 = ec.keyFromPublic(res.publicKey, 'hex'); assert(pk3.verify(digest, res.signature.der)); }], - ["testSign1Typed", async function(driver, exec) { - let domain = { + ["testSign1Typed", async function(driver: string, exec: (cmd: HaloCommandObject) => Promise) { + const domain = { name: 'Ether Mail', version: '1', chainId: 1, verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' }; - let types = { + const types = { Person: [ { name: 'name', type: 'string' }, { name: 'wallet', type: 'address' } @@ -133,7 +134,7 @@ const tests = [ ] }; - let value = { + const value = { from: { name: 'Cow', wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826' @@ -145,13 +146,13 @@ const tests = [ contents: 'Hello, Bob!' }; - let res = await exec({ + const res = await exec({ "name": "sign", "keyNo": 1, "typedData": {domain, types, value} }); - let pk1 = ec.keyFromPublic(res.publicKey, 'hex'); + const pk1 = ec.keyFromPublic(res.publicKey, 'hex'); assert(res.input.digest === "be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2"); assert(res.input.primaryType === "Mail"); assert(res.input.domainHash === "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"); @@ -159,20 +160,21 @@ const tests = [ }] ]; -async function __runTestSuite(__unsafe, driver, exec) { +async function __runTestSuite(__unsafe: {__this_is_unsafe: true}, driver: string, exec: (cmd: HaloCommandObject) => Promise) { if (!__unsafe.__this_is_unsafe) { throw new HaloLogicError("This method is used for internal testing, shouldn't be invoked directly."); } - let passed = []; - let skipped = []; - let failed = []; + const passed = []; + const skipped = []; + const failed = []; - for (let testObj of tests) { + for (const testObj of tests) { console.log(testObj[0]); try { - await testObj[1](driver, exec); + const testFunc = testObj[1] as (driver: string, exec: (cmd: HaloCommandObject) => Promise) => Promise; + await testFunc(driver, exec); console.log(testObj[0], 'PASSED'); passed.push(testObj[0]); } catch (e) { diff --git a/core/src.ts/halo/util.js b/core/src.ts/halo/util.ts similarity index 70% rename from core/src.ts/halo/util.js rename to core/src.ts/halo/util.ts index 83cbe2b..325be36 100644 --- a/core/src.ts/halo/util.js +++ b/core/src.ts/halo/util.ts @@ -6,31 +6,37 @@ import {Buffer} from 'buffer/index.js'; import elliptic from 'elliptic'; -import {ethers} from 'ethers'; +import {ethers, Signature} from 'ethers'; import {HaloLogicError} from "./exceptions.js"; import crypto from "crypto"; import {BN} from 'bn.js'; +import {PublicKeyList} from "../types.js"; const ec = new elliptic.ec('secp256k1'); const SECP256k1_ORDER = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n; const BJJ_ORDER = 0x060c89ce5c263405370a08b6d0302b0bab3eedb83920ee0a677297dc392126f1n; -function hex2arr(hexString) { +interface SignatureObj { + r: string + s: string +} + +function hex2arr(hexString: string) { return new Uint8Array( - hexString.match(/.{1,2}/g).map( + hexString.match(/.{1,2}/g)!.map( byte => parseInt(byte, 16) ) ); } -function arr2hex(buffer) { +function arr2hex(buffer: number[] | Uint8Array) { return [...new Uint8Array(buffer)] .map(x => x.toString(16).padStart(2, '0')) .join(''); } -function parsePublicKeys(buffer) { +function parsePublicKeys(buffer: Buffer | string): PublicKeyList { let buf; if (typeof buffer === "string") { @@ -39,17 +45,17 @@ function parsePublicKeys(buffer) { buf = Buffer.from(buffer); } - let out = {}; + const out: PublicKeyList = {}; let keyNo = 1; while (true) { - let keyLength = buf[0]; + const keyLength = buf[0]; if (typeof keyLength === "undefined" || keyLength === 0) { break; } - let key = buf.slice(1, 1 + keyLength); + const key = buf.slice(1, 1 + keyLength); out[keyNo] = key.toString('hex'); buf = buf.slice(1 + keyLength); keyNo++; @@ -58,25 +64,25 @@ function parsePublicKeys(buffer) { return out; } -function parseSig(res, curveOrder) { +function parseSig(res: Buffer, curveOrder: bigint) { if (res[0] !== 0x30 || res[2] !== 0x02) { throw new HaloLogicError("Unable to parse signature, unexpected header (1)."); } - let rLen = res[3]; + const rLen = res[3]; if (res[rLen + 4] !== 0x02) { throw new HaloLogicError("Unable to parse signature, unexpected header (2)."); } - let sLen = res[rLen + 5]; + const sLen = res[rLen + 5]; if (res.length !== rLen + 4 + 2 + sLen) { throw new HaloLogicError("Unable to parse signature, unexpected length."); } - let r = res.slice(4, rLen + 4); - let s = res.slice(rLen + 4 + 2, rLen + 4 + 2 + sLen); + const r = res.slice(4, rLen + 4); + const s = res.slice(rLen + 4 + 2, rLen + 4 + 2 + sLen); let rn = BigInt('0x' + r.toString('hex')); let sn = BigInt('0x' + s.toString('hex')); @@ -95,12 +101,12 @@ function parseSig(res, curveOrder) { }; } -function sigToDer(sig) { - let r = BigInt('0x' + sig.r); - let s = BigInt('0x' + sig.s); +function sigToDer(sig: SignatureObj) { + const r = BigInt('0x' + sig.r); + const s = BigInt('0x' + sig.s); - let padR = r.toString(16).length % 2 ? '0' : ''; - let padS = s.toString(16).length % 2 ? '0' : ''; + const padR = r.toString(16).length % 2 ? '0' : ''; + const padS = s.toString(16).length % 2 ? '0' : ''; let encR = Buffer.from(padR + r.toString(16), 'hex'); let encS = Buffer.from(padS + s.toString(16), 'hex'); @@ -125,9 +131,9 @@ function sigToDer(sig) { ]); } -function convertSignature(digest, signature, publicKey, curveOrder) { - signature = Buffer.from(signature, "hex"); - let fixedSig = parseSig(signature, curveOrder); +function convertSignature(digest: string, signature: string, publicKey: string, curveOrder: bigint) { + const sigBuf = Buffer.from(signature, "hex"); + const fixedSig = parseSig(sigBuf, curveOrder); let recoveryParam = null; @@ -142,12 +148,12 @@ function convertSignature(digest, signature, publicKey, curveOrder) { throw new HaloLogicError("Failed to get recovery param."); } - let finalSig = '0x' + fixedSig.r + const finalSig = '0x' + fixedSig.r + fixedSig.s + Buffer.from([27 + recoveryParam]).toString('hex'); - let pkeyAddress = ethers.computeAddress('0x' + publicKey); - let recoveredAddress = ethers.recoverAddress('0x' + digest, finalSig); + const pkeyAddress = ethers.computeAddress('0x' + publicKey); + const recoveredAddress = ethers.recoverAddress('0x' + digest, finalSig); if (pkeyAddress !== recoveredAddress) { throw new HaloLogicError("Failed to correctly recover public key from the signature."); @@ -158,16 +164,16 @@ function convertSignature(digest, signature, publicKey, curveOrder) { ...fixedSig, v: recoveryParam + 0x1b }, - "der": sigToDer(parseSig(signature, curveOrder)).toString('hex'), - "ether": finalSig.toString('hex') + "der": sigToDer(parseSig(sigBuf, curveOrder)).toString('hex'), + "ether": finalSig }; } -function recoverPublicKey(digest, signature, curveOrder) { - let out = []; +function recoverPublicKey(digest: string, signature: string, curveOrder: bigint) { + const out = []; - signature = Buffer.from(signature, "hex"); - let fixedSig = parseSig(signature, curveOrder); + const sigBuf = Buffer.from(signature, "hex"); + const fixedSig = parseSig(sigBuf, curveOrder); for (let i = 0; i < 2; i++) { out.push(ec.recoverPubKey(new BN(digest, 16), fixedSig, i).encode('hex')); @@ -176,11 +182,15 @@ function recoverPublicKey(digest, signature, curveOrder) { return out; } -function mode(arr) { +function mode(arr: Type[]): Type { + if (arr.length <= 0) { + throw new Error("Zero-length array."); + } + return arr.sort((a, b) => arr.filter(v => v === a).length - arr.filter(v => v === b).length - ).pop(); + ).pop()!; } function randomBuffer() { @@ -191,7 +201,7 @@ function isWebDebugEnabled() { return typeof window !== "undefined" && window.localStorage && window.localStorage.getItem("DEBUG_LIBHALO_WEB") === "1"; } -function webDebug(...args) { +function webDebug(...args: unknown[]) { if (isWebDebugEnabled()) { console.log(...args); } diff --git a/core/src.ts/index.tsx b/core/src.ts/index.ts similarity index 100% rename from core/src.ts/index.tsx rename to core/src.ts/index.ts diff --git a/core/src.ts/types.ts b/core/src.ts/types.ts new file mode 100644 index 0000000..cfd4918 --- /dev/null +++ b/core/src.ts/types.ts @@ -0,0 +1,229 @@ +import {Buffer} from 'buffer/index.js'; + + +// These types should only be used internally for the command dispatchers. +// There are specific arg/return types defined for each HaLo command. + +// eslint-disable-next-line +export type HaloCommandObject = any; +// eslint-disable-next-line +export type HaloResponseObject = any; + +export interface Card { + type: string + atr: Buffer +} + +export interface ReaderEventListener { + (eventName: 'card', listener: (card: Card) => void): void; + (eventName: 'card.off', listener: (card: Card) => void): void; + (eventName: 'error', listener: (err: Error) => void): void; + (eventName: 'end', listener: () => void): void; +} + +export interface Reader { + reader: { + name: string + } + autoProcessing: boolean + transmit: (data: Buffer, responseMaxLength: number) => Promise; + on: ReaderEventListener +} + +export interface TransceiveFunc { + (data: Buffer): Promise; +} + +export interface StatusCallbackDetails { + execMethod: string + execStep: string + cancelScan: () => void +} + +export interface PublicKeyList { + [keyNo: number]: string +} + +export interface ExecReturnStruct { + result: string + extra: Record +} + +export interface ExecHaloCmdOptions { + method: "credential" | "pcsc" | "webnfc" | "nfc-manager" + exec: (command: Buffer, options?: ExecOptions) => Promise +} + +export interface ExecOptions { + statusCallback?: (status: string, statusDetails: StatusCallbackDetails) => void; +} + +export type HaloWebMethod = "credential" | "webnfc"; + +export interface ExecHaloCmdWebOptions extends ExecOptions { + method?: HaloWebMethod + noDebounce?: boolean +} + +export interface EmptyOptions { + +} + +export interface RNNFCManagerIsoDepHandler { + transceive: (data: number[]) => Promise; +} + +export interface RNNFCManager { + isoDepHandler: RNNFCManagerIsoDepHandler +} + +export interface ExecOptions { + noCheck?: boolean + pcscExecLayer?: "u2f" +} + +export interface GatewayWelcomeMsg { + serverVersion: { + tagName: string + commitId: string + version: number[] + } + sessionId: string +} + +export interface BridgeOptions { + createWebSocket?: (url: string) => WebSocket +} + +export interface BridgeEvent { + event: "handle_added" | "handle_removed" | "handle_not_compatible" | "reader_added" | "reader_removed" | "exec_success" | "exec_exception" + uid: string | null +} + +export interface BridgeHandleEvent extends BridgeEvent { + event: "handle_added" | "handle_removed" + uid: null + data: { + handle: string + reader_name: string + } +} + +export interface BridgeHandleAdded extends BridgeHandleEvent { + event: "handle_added" +} + +export interface BridgeHandleRemoved extends BridgeHandleEvent { + event: "handle_removed" +} + +export interface BridgeHandleNotCompatibleEvent extends BridgeEvent { + event: "handle_not_compatible" + uid: null + data: { + reader_name: string + message: string + } +} + +export interface FindBridgeOptions { + wsPort?: number + wssPort?: number + createWebSocket?: (url: string) => WebSocket + diagnose?: boolean +} + +export interface FindBridgeOptionsNoDiagnose extends FindBridgeOptions { + diagnose?: false +} + +export interface FindBridgeOptionsDiagnose extends FindBridgeOptions { + diagnose: true +} + +export interface FindBridgeResult { + urls: string[] + errors: string[] +} + +export interface KeyFlags { + isPasswordProtected: boolean + hasMandatoryPassword: boolean + rawSignCommandNotUsed: boolean + isImported: boolean + isExported: boolean +} + +export interface KeyState extends KeyFlags { + failedAuthCounter: number +} + +export interface HaloCmdCFGNDEF { + flagUseText: boolean + flagHidePk1: boolean + flagHidePk2: boolean + flagHidePk3: boolean + flagShowPk1Attest: boolean + flagShowPk2Attest: boolean + flagHideRNDSIG: boolean + flagHideCMDRES: boolean + + flagShowPk3Attest: boolean + flagShowLatch1Sig: boolean + flagShowLatch2Sig: boolean + flagLegacyStatic: boolean + flagShowPkN: boolean + flagShowPkNAttest: boolean + flagRNDSIGUseBJJ62: boolean + + pkN: KeySlotNo +} + +export interface HaloResCFGNDEF { + status: "ok" + cfgBytes: HexString +} + +export interface HaloCmdGenKey { + keyNo: KeySlotNo + entropy?: HexString +} + +export interface HaloResGenKeyV1 { + needsConfirmPK: true + publicKey: HexString +} + +export interface HaloResGenKeyV2 { + needsConfirmPK: false + rootPublicKey: HexString + rootAttestSig: HexString +} + +export type HaloResGenKey = HaloResGenKeyV1 | HaloResGenKeyV2; + +export interface HaloCmdGenKeyConfirm { + keyNo: KeySlotNo + publicKey: HexString +} + +export interface HaloResGenKeyConfirm { + rootPublicKey: HexString + rootAttestSig: HexString +} + +export interface HaloCmdGenKeyFinalize { + keyNo: KeySlotNo + password?: ASCIIString +} + +export interface HaloResGenKeyFinalize { + publicKey: HexString + attestSig: HexString +} + +export type KeySlotNo = number; +export type ASCIIString = string; +export type HexString = string; + +export * from './types_webnfc.js'; diff --git a/core/src.ts/types_webnfc.ts b/core/src.ts/types_webnfc.ts new file mode 100644 index 0000000..5739d0b --- /dev/null +++ b/core/src.ts/types_webnfc.ts @@ -0,0 +1,83 @@ +// Type definitions for Web NFC +// Project: https://github.com/w3c/web-nfc +// Definitions by: Takefumi Yoshii +// TypeScript Version: 3.9 + +// This type definitions referenced to WebIDL. +// https://w3c.github.io/web-nfc/#actual-idl-index + +export interface Window { + NDEFMessage: NDEFMessage +} +export declare class NDEFMessage { + constructor(messageInit: NDEFMessageInit) + records: ReadonlyArray +} +export declare interface NDEFMessageInit { + records: NDEFRecordInit[] +} + +export declare type NDEFRecordDataSource = string | BufferSource | NDEFMessageInit + +export interface Window { + NDEFRecord: NDEFRecord +} +export declare class NDEFRecord { + constructor(recordInit: NDEFRecordInit) + readonly recordType: string + readonly mediaType?: string + readonly id?: string + readonly data?: DataView + readonly encoding?: string + readonly lang?: string + toRecords?: () => NDEFRecord[] +} +export declare interface NDEFRecordInit { + recordType: string + mediaType?: string + id?: string + encoding?: string + lang?: string + data?: NDEFRecordDataSource +} + +export declare type NDEFMessageSource = string | BufferSource | NDEFMessageInit + +export interface Window { + NDEFReader: NDEFReader +} +export declare class NDEFReader extends EventTarget { + constructor() + onreading: (this: this, event: NDEFReadingEvent) => any + onreadingerror: (this: this, error: Event) => any + scan: (options?: NDEFScanOptions) => Promise + write: ( + message: NDEFMessageSource, + options?: NDEFWriteOptions + ) => Promise + makeReadOnly: (options?: NDEFMakeReadOnlyOptions) => Promise +} + +export interface Window { + NDEFReadingEvent: NDEFReadingEvent +} +export declare class NDEFReadingEvent extends Event { + constructor(type: string, readingEventInitDict: NDEFReadingEventInit) + serialNumber: string + message: NDEFMessage +} +export interface NDEFReadingEventInit extends EventInit { + serialNumber?: string + message: NDEFMessageInit +} + +export interface NDEFWriteOptions { + overwrite?: boolean + signal?: AbortSignal +} +export interface NDEFMakeReadOnlyOptions { + signal?: AbortSignal +} +export interface NDEFScanOptions { + signal: AbortSignal +} diff --git a/core/src.ts/web/soft_prompt.js b/core/src.ts/web/soft_prompt.ts similarity index 95% rename from core/src.ts/web/soft_prompt.js rename to core/src.ts/web/soft_prompt.ts index 6147605..8aeed5b 100644 --- a/core/src.ts/web/soft_prompt.js +++ b/core/src.ts/web/soft_prompt.ts @@ -1,3 +1,5 @@ +import {StatusCallbackDetails} from "../types.js"; + const PROMPT_STYLES = ` @keyframes __libhalo_popup_waitingRingGrow { 0% { @@ -155,7 +157,7 @@ const PROMPT_HTML = ` `; -function emulatedPromptStatusCallback(status, statusObj) { +function emulatedPromptStatusCallback(status: string, statusObj: StatusCallbackDetails) { if (!document.getElementById('__libhalo_popup_stylesheet')) { const style = document.createElement('style'); style.setAttribute('id', '__libhalo_popup_stylesheet'); @@ -185,9 +187,9 @@ function emulatedPromptStatusCallback(status, statusObj) { default: statusText = "<" + status + ">"; break; } - pdiv.innerText = statusText; - cancelBtn.onclick = statusObj.cancelScan; - rdiv.style.display = status !== 'finished' ? 'block' : 'none'; + pdiv!.innerText = statusText; + cancelBtn!.onclick = statusObj.cancelScan; + rdiv!.style.display = status !== 'finished' ? 'block' : 'none'; } export { diff --git a/core/src.ts/web/web_apis.js b/core/src.ts/web/web_apis.ts similarity index 100% rename from core/src.ts/web/web_apis.js rename to core/src.ts/web/web_apis.ts diff --git a/core/src.ts/web/web_utils.js b/core/src.ts/web/web_utils.ts similarity index 72% rename from core/src.ts/web/web_utils.js rename to core/src.ts/web/web_utils.ts index b0dbf50..fcc9d06 100644 --- a/core/src.ts/web/web_utils.js +++ b/core/src.ts/web/web_utils.ts @@ -1,18 +1,19 @@ import WebSocketAsPromised from 'websocket-as-promised'; import {NFCBadTransportError} from "../halo/exceptions.js"; +import {FindBridgeOptions, FindBridgeOptionsDiagnose, FindBridgeOptionsNoDiagnose, FindBridgeResult} from "../types.js"; -function haloCreateWs(url) { +function haloCreateWs(url: string) { return new WebSocketAsPromised(url, { packMessage: data => JSON.stringify(data), - unpackMessage: data => JSON.parse(data), + unpackMessage: data => JSON.parse(data as string), attachRequestId: (data, requestId) => Object.assign({uid: requestId}, data), extractRequestId: data => data && data.uid }); } -function runHealthCheck(url, openTimeout, createWebSocket) { +function runHealthCheck(url: string, openTimeout: number, createWebSocket: (url: string) => WebSocket) { return new Promise((resolve, reject) => { - let closeTimeout = null; + let closeTimeout: NodeJS.Timeout | null = null; const pingUrl = url.includes('?') ? (url + '&ping=1') : (url + '?ping=1'); const wsp = new WebSocketAsPromised(pingUrl, { @@ -45,12 +46,12 @@ function runHealthCheck(url, openTimeout, createWebSocket) { }); } -function createChecks(wsPort, wssPort, createWebSocket) { +function createChecks(wsPort: number, wssPort: number, createWebSocket: (url: string) => WebSocket) { // detect Firefox - const isFirefox = typeof window !== "undefined" && window.hasOwnProperty("InternalError"); + const isFirefox = typeof window !== "undefined" && Object.prototype.hasOwnProperty.call(window, "InternalError"); const openTimeout = isFirefox ? 10000 : 5000; - let checks = [ + const checks = [ runHealthCheck('ws://127.0.0.1:' + wsPort + '/ws', openTimeout, createWebSocket) ]; @@ -65,7 +66,10 @@ function createChecks(wsPort, wssPort, createWebSocket) { return checks; } -async function haloFindBridge(options) { +async function haloFindBridge(options: FindBridgeOptionsNoDiagnose): Promise; +async function haloFindBridge(options: FindBridgeOptionsDiagnose): Promise; + +async function haloFindBridge(options: FindBridgeOptions) { options = Object.assign({}, options) || {}; if (!options.wsPort) { @@ -80,14 +84,14 @@ async function haloFindBridge(options) { const wssPort = options.wssPort; const createWebSocket = options.createWebSocket ? options.createWebSocket - : (url) => new WebSocket(url); + : (url: string) => new WebSocket(url); if (options.diagnose) { - let res = await Promise.allSettled(createChecks(wsPort, wssPort, createWebSocket)); - let urls = []; - let errors = []; + const res = await Promise.allSettled(createChecks(wsPort, wssPort, createWebSocket)); + const urls = []; + const errors = []; - for (let o of res) { + for (const o of res) { if (o.status === "fulfilled") { urls.push(o.value); } else { diff --git a/core/src.ts/web/weblib.js b/core/src.ts/web/weblib.ts similarity index 79% rename from core/src.ts/web/weblib.js rename to core/src.ts/web/weblib.ts index 7da1f69..8d6bac9 100644 --- a/core/src.ts/web/weblib.js +++ b/core/src.ts/web/weblib.ts @@ -8,6 +8,8 @@ import * as all_exports from "./web_apis.js"; if (typeof window !== "undefined") { Object.keys(all_exports).forEach((key) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error window[key] = all_exports[key]; }); } diff --git a/core/tsconfig.commonjs.json b/core/tsconfig.commonjs.json new file mode 100644 index 0000000..0fdc624 --- /dev/null +++ b/core/tsconfig.commonjs.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "moduleResolution": "node10", + "module": "commonjs", + "outDir": "./lib.commonjs" + } +} diff --git a/core/tsconfig.json b/core/tsconfig.json index 80ecc7d..e5d35dc 100644 --- a/core/tsconfig.json +++ b/core/tsconfig.json @@ -6,8 +6,7 @@ "forceConsistentCasingInFileNames": true, "importHelpers": true, "lib": [ - "es2020", - "es5", + "es2022", "dom" ], "moduleResolution": "node16", @@ -16,7 +15,7 @@ "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, - "noUnusedLocals": true, + "noUnusedLocals": false, "preserveSymlinks": true, "preserveWatchOutput": true, "pretty": false, @@ -29,7 +28,7 @@ "allowJs": true }, "include": [ - "./src.ts/**/*.tsx", + "./src.ts/**/*.ts", "./src.ts/**/*.js" ], "exclude": [] diff --git a/core/webpack.config.js b/core/webpack.config.js index bbf4be1..3341184 100644 --- a/core/webpack.config.js +++ b/core/webpack.config.js @@ -10,7 +10,7 @@ const __dirname = dirname(__filename); export default { entry: { - app: './src.ts/web/weblib.js', + app: './src.ts/web/weblib.ts', }, output: { filename: 'libhalo.js', @@ -21,14 +21,17 @@ export default { module: { rules: [ { - test: /\.tsx?$/, + test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, resolve: { - extensions: ['.tsx', '.ts', '.js'], + extensions: ['.ts', '.js'], + extensionAlias: { + '.js': ['.js', '.ts'], + }, fallback: { vm: false, buffer: resolve(__dirname, './node_modules/buffer/index.js'), @@ -41,7 +44,9 @@ export default { apply: (compiler) => { compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => { compilation.getAssets().forEach((asset) => { - fs.copyFileSync('./dist/' + asset.name, '../cli/assets/static/' + asset.name); + if (asset.name === 'libhalo.js') { + fs.copyFileSync('./dist/' + asset.name, '../cli/assets/static/' + asset.name); + } }); }); } diff --git a/core/yarn.lock b/core/yarn.lock index f8de600..684b2e2 100644 --- a/core/yarn.lock +++ b/core/yarn.lock @@ -12,6 +12,62 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" + integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== + +"@eslint/config-array@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.17.0.tgz#ff305e1ee618a00e6e5d0485454c8d92d94a860d" + integrity sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA== + dependencies: + "@eslint/object-schema" "^2.1.4" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/eslintrc@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" + integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.7.0", "@eslint/js@^9.7.0": + version "9.7.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.7.0.tgz#b712d802582f02b11cfdf83a85040a296afec3f0" + integrity sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng== + +"@eslint/object-schema@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" + integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" + integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== + "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -64,6 +120,41 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@types/bn.js@*": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" + integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== + dependencies: + "@types/node" "*" + +"@types/elliptic@^6.4.18": + version "6.4.18" + resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.18.tgz#bc96e26e1ccccbabe8b6f0e409c85898635482e1" + integrity sha512-UseG6H5vjRiNpQvrhy4VF/JXdA3V/Fp5amvveaL+fs28BZ6xIKJBPnUPRlEaZpysD9MbpfaLi8lbl7PGUAkpWw== + dependencies: + "@types/bn.js" "*" + "@types/eslint-scope@^3.7.3": version "3.7.7" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" @@ -102,6 +193,101 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== +"@types/pbkdf2@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.2.tgz#2dc43808e9985a2c69ff02e2d2027bd4fe33e8dc" + integrity sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew== + dependencies: + "@types/node" "*" + +"@types/qrcode@^1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.5.5.tgz#993ff7c6b584277eee7aac0a20861eab682f9dac" + integrity sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg== + dependencies: + "@types/node" "*" + +"@typescript-eslint/eslint-plugin@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz#b3563927341eca15124a18c6f94215f779f5c02a" + integrity sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "7.16.0" + "@typescript-eslint/type-utils" "7.16.0" + "@typescript-eslint/utils" "7.16.0" + "@typescript-eslint/visitor-keys" "7.16.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/parser@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.16.0.tgz#53fae8112f8c912024aea7b499cf7374487af6d8" + integrity sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw== + dependencies: + "@typescript-eslint/scope-manager" "7.16.0" + "@typescript-eslint/types" "7.16.0" + "@typescript-eslint/typescript-estree" "7.16.0" + "@typescript-eslint/visitor-keys" "7.16.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz#eb0757af5720c9c53c8010d7a0355ae27e17b7e5" + integrity sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw== + dependencies: + "@typescript-eslint/types" "7.16.0" + "@typescript-eslint/visitor-keys" "7.16.0" + +"@typescript-eslint/type-utils@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz#ec52b1932b8fb44a15a3e20208e0bd49d0b6bd00" + integrity sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg== + dependencies: + "@typescript-eslint/typescript-estree" "7.16.0" + "@typescript-eslint/utils" "7.16.0" + debug "^4.3.4" + ts-api-utils "^1.3.0" + +"@typescript-eslint/types@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.16.0.tgz#60a19d7e7a6b1caa2c06fac860829d162a036ed2" + integrity sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw== + +"@typescript-eslint/typescript-estree@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz#98ac779d526fab2a781e5619c9250f3e33867c09" + integrity sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw== + dependencies: + "@typescript-eslint/types" "7.16.0" + "@typescript-eslint/visitor-keys" "7.16.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/utils@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.16.0.tgz#b38dc0ce1778e8182e227c98d91d3418449aa17f" + integrity sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.16.0" + "@typescript-eslint/types" "7.16.0" + "@typescript-eslint/typescript-estree" "7.16.0" + +"@typescript-eslint/visitor-keys@7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz#a1d99fa7a3787962d6e0efd436575ef840e23b06" + integrity sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg== + dependencies: + "@typescript-eslint/types" "7.16.0" + eslint-visitor-keys "^3.4.3" + "@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" @@ -253,7 +439,12 @@ acorn-import-attributes@^1.9.5: resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== -acorn@^8.7.1, acorn@^8.8.2: +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.12.0, acorn@^8.7.1, acorn@^8.8.2: version "8.12.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== @@ -268,7 +459,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.12.5: +ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -283,7 +474,7 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^4.0.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -303,6 +494,11 @@ array-buffer-byte-length@^1.0.1: call-bind "^1.0.5" is-array-buffer "^3.0.4" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + arraybuffer.prototype.slice@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" @@ -333,6 +529,11 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -348,6 +549,28 @@ bn.js@^5.0.0, bn.js@^5.2.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -447,6 +670,11 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -457,6 +685,14 @@ caniuse-lite@^1.0.30001640: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz#6aa6610eb24067c246d30c57f055a9d0a7f8d05f" integrity sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA== +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chnl@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/chnl/-/chnl-1.2.0.tgz#d818c95367767a0880508e7cc0b5b3503f58fd4c" @@ -520,6 +756,11 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -556,7 +797,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^7.0.3: +cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -609,6 +850,13 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -619,6 +867,11 @@ decode-uri-component@^0.4.1: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz#2ac4859663c704be22bf7db760a1494a49ab2cc5" integrity sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ== +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" @@ -659,6 +912,13 @@ dijkstrajs@^1.0.1: resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz#4c8dbdea1f0f6478bff94d9c49c784d623e4fc23" integrity sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + electron-to-chromium@^1.4.820: version "1.4.827" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.827.tgz#76068ed1c71dd3963e1befc8ae815004b2da6a02" @@ -687,7 +947,7 @@ encode-utf8@^1.0.3: resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda" integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw== -enhanced-resolve@^5.17.0: +enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.0: version "5.17.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== @@ -799,6 +1059,11 @@ escalade@^3.1.2: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -807,6 +1072,80 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.2.tgz#5cbb33d4384c9136083a71190d548158fe128f94" + integrity sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" + integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== + +eslint@^9.7.0: + version "9.7.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.7.0.tgz#bedb48e1cdc2362a0caaa106a4c6ed943e8b09e4" + integrity sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.11.0" + "@eslint/config-array" "^0.17.0" + "@eslint/eslintrc" "^3.1.0" + "@eslint/js" "9.7.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.3.0" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.0.2" + eslint-visitor-keys "^4.0.0" + espree "^10.1.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^10.0.1, espree@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56" + integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA== + dependencies: + acorn "^8.12.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.0.0" + +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -819,11 +1158,16 @@ estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.2.0: +estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + ethers@^6.13.1: version "6.13.1" resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.1.tgz#2b9f9c7455cde9d38b30fe6589972eb083652961" @@ -850,21 +1194,58 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-glob@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + fastest-levenshtein@^1.0.12: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + filter-obj@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-5.1.0.tgz#5bd89676000a713d7db2e197f660274428e524ed" @@ -878,11 +1259,32 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + flat@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -935,11 +1337,30 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + globalthis@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" @@ -948,6 +1369,18 @@ globalthis@^1.0.3: define-properties "^1.2.1" gopd "^1.0.1" +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -960,6 +1393,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -1040,6 +1478,19 @@ ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-local@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" @@ -1048,6 +1499,11 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" @@ -1116,11 +1572,23 @@ is-date-object@^1.0.1: dependencies: has-tostringtag "^1.0.0" +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-negative-zero@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" @@ -1133,6 +1601,16 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -1222,6 +1700,18 @@ js-sha256@^0.11.0: resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.11.0.tgz#256a921d9292f7fe98905face82e367abaca9576" integrity sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q== +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -1232,11 +1722,31 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -1249,6 +1759,18 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -1263,6 +1785,19 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.0, micromatch@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -1293,6 +1828,30 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -1323,6 +1882,18 @@ object.assign@^4.1.5: has-symbols "^1.0.3" object-keys "^1.1.1" +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -1330,6 +1901,13 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -1337,11 +1915,25 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-asn1@^5.0.0, parse-asn1@^5.1.7: version "5.1.7" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.7.tgz#73cdaaa822125f9647165625eb45f8a051d2df06" @@ -1369,6 +1961,11 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pbkdf2@^3.0.3, pbkdf2@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" @@ -1385,6 +1982,11 @@ picocolors@^1.0.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -1402,6 +2004,11 @@ possible-typed-array-names@^1.0.0: resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -1469,6 +2076,11 @@ query-string@^9.0.0: filter-obj "^5.1.0" split-on-first "^3.0.0" +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -1540,6 +2152,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" @@ -1554,6 +2171,11 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -1562,6 +2184,13 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + safe-array-concat@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" @@ -1600,6 +2229,11 @@ schema-utils@^3.1.1, schema-utils@^3.2.0: ajv "^6.12.5" ajv-keywords "^3.5.2" +semver@^7.3.4, semver@^7.6.0: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + serialize-javascript@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" @@ -1671,6 +2305,11 @@ side-channel@^1.0.4: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -1684,6 +2323,11 @@ source-map@^0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + split-on-first@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-3.0.0.tgz#f04959c9ea8101b9b0bbf35a61b9ebea784a23e7" @@ -1755,6 +2399,18 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + supports-color@^8.0.0: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" @@ -1793,11 +2449,46 @@ terser@^5.26.0: commander "^2.20.0" source-map-support "~0.5.20" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + +ts-loader@^9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.1.tgz#63d5912a86312f1fbe32cef0859fb8b2193d9b89" + integrity sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + source-map "^0.7.4" + tslib@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + typed-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" @@ -1842,6 +2533,15 @@ typed-array-length@^1.0.6: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" +typescript-eslint@^7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-7.16.0.tgz#8466a7c6d8b5acfdcaf53ecc8ecf240072b346d8" + integrity sha512-kaVRivQjOzuoCXU6+hLnjo3/baxyzWVO5GrnExkFzETRYJKVHYkrJglOu2OCm8Hi9RPDWX1PTNNTpU5KRV0+RA== + dependencies: + "@typescript-eslint/eslint-plugin" "7.16.0" + "@typescript-eslint/parser" "7.16.0" + "@typescript-eslint/utils" "7.16.0" + typescript@^5.5.3: version "5.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa" @@ -2002,6 +2702,11 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -2045,3 +2750,8 @@ yargs@^15.3.1: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^18.1.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==