From 095df722ec20ab517c9adbb69b5805151d0d23d2 Mon Sep 17 00:00:00 2001 From: taichunmin Date: Wed, 17 Jul 2024 00:08:17 +0800 Subject: [PATCH] v0.3.16: add mfu support. --- README.md | 2 +- package.json | 34 +- pages/demos.md | 16 +- pug/include/bootstrapV4.pug | 10 + pug/src/hf14a-scanner.pug | 2 +- pug/src/lf-em410x.pug | 2 +- pug/src/mfkey32.pug | 7 +- pug/src/mifare-xiaomi.pug | 2 +- pug/src/mifare1k.pug | 2 +- src/ChameleonUltra.test.ts | 8 +- src/ChameleonUltra.ts | 833 ++++++++++++++++++++++++++++---- src/Crypto1.test.ts | 2 +- src/Crypto1.ts | 2 +- src/EventAsyncGenerator.test.ts | 3 +- src/ResponseDecoder.test.ts | 2 +- src/ResponseDecoder.ts | 2 +- src/enums.ts | 244 +++++++++- src/helper.ts | 7 +- src/index.ts | 3 + src/plugin/BufferMockAdapter.ts | 2 +- src/plugin/Debug.ts | 4 +- src/plugin/DfuZip.ts | 2 +- src/plugin/SerialPortAdapter.ts | 4 +- src/plugin/WebbleAdapter.ts | 8 +- src/plugin/WebserialAdapter.ts | 6 +- typedoc.json | 1 + yarn.lock | 661 +++++++++++++++---------- 27 files changed, 1442 insertions(+), 429 deletions(-) diff --git a/README.md b/README.md index c404640..7dc90c0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

A JavaScript SDK for ChameleonUltra support Web Bluetooth API, Web Serial API and Node.js.

-Demo • +DemoDocumentationReference

diff --git a/package.json b/package.json index 2e5f50a..9bc1315 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "module": "./dist/index.mjs", "name": "chameleon-ultra.js", "type": "commonjs", - "version": "0.3.15", + "version": "0.3.16", "bugs": { "url": "https://github.com/taichunmin/chameleon-ultra.js/issues" }, @@ -20,9 +20,9 @@ } ], "dependencies": { - "@taichunmin/buffer": "^0.13.6", - "@taichunmin/crc": "^0.0.13", - "debug": "^4.3.5", + "@taichunmin/buffer": "^0.13.7", + "@taichunmin/crc": "^0.0.14", + "debug": "^4.3.6", "jszip": "^3.10.1", "lodash": "^4.17.21", "serialport": "^12.0.0", @@ -37,16 +37,16 @@ "@types/jest": "^29.5.12", "@types/livereload": "^0.9.5", "@types/lodash": "^4.17.7", - "@types/node": "^20.14.10", + "@types/node": "^22.0.2", "@types/pug": "^2.0.10", "@types/serve-static": "^1.15.7", "@types/uglify-js": "^3.17.5", "@types/web-bluetooth": "^0.0.20", - "@typescript-eslint/eslint-plugin": "^7.16.1", - "@typescript-eslint/parser": "^7.16.1", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", "chokidar": "^3.6.0", "concurrently": "^8.2.2", - "dayjs": "^1.11.11", + "dayjs": "^1.11.12", "dotenv": "^16.4.5", "esbuild-plugins-node-modules-polyfill": "^1.6.4", "eslint": "^8.57.0", @@ -54,8 +54,8 @@ "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-local-rules": "^3.0.2", - "eslint-plugin-n": "^17.9.0", - "eslint-plugin-promise": "^6.4.0", + "eslint-plugin-n": "^17.10.1", + "eslint-plugin-promise": "^7.0.0", "eslint-plugin-pug": "^1.2.5", "eslint-plugin-tsdoc": "^0.3.0", "finalhandler": "^1.2.0", @@ -69,16 +69,16 @@ "rimraf": "^6.0.1", "serve-static": "^1.15.0", "supports-color": "^9.4.0", - "ts-jest": "^29.2.2", + "ts-jest": "^29.2.4", "ts-node": "^10.9.2", - "tsup": "^8.1.0", - "tsx": "^4.16.2", - "typedoc": "^0.26.4", - "typedoc-plugin-mdn-links": "^3.2.4", + "tsup": "^8.2.3", + "tsx": "^4.16.5", + "typedoc": "^0.26.5", + "typedoc-plugin-mdn-links": "^3.2.6", "typedoc-plugin-missing-exports": "^3.0.0", "typedoc-plugin-rename-defaults": "^0.7.1", "typedoc-plugin-zod": "^1.2.0", - "typescript": "^5.5.3", + "typescript": "^5.5.4", "utility-types": "^3.11.0" }, "exports": { @@ -168,7 +168,7 @@ "build:pug": "tsx ./pug/build.ts", "build:sitemap": "tsx ./sitemap.ts", "build": "yarn build:js && yarn build:docs && yarn build:pug && yarn build:sitemap", - "dev:docs": "nodemon --watch src --ext ts,md --exec \"yarn build:docs\"", + "dev:docs": "nodemon --watch src --watch pages --ext ts,md --exec \"yarn build:docs\"", "dev:https": "tsx ./https.ts", "dev:js": "yarn build:js --watch", "dev:pug": "nodemon --watch pug --ext pug --exec \"yarn build:pug\"", diff --git a/pages/demos.md b/pages/demos.md index 309c318..aa9deb7 100644 --- a/pages/demos.md +++ b/pages/demos.md @@ -1,14 +1,10 @@ -# Demos +--- +title: Demos +group: Documents +category: Guides +--- -- [Demos](#demos) - - [device-settings.html](#device-settingshtml) - - [dfu.html](#dfuhtml) - - [mfkey32.html](#mfkey32html) - - [mifare1k.html](#mifare1khtml) - - [hf14a-scanner.html](#hf14a-scannerhtml) - - [mifare-xiaomi.html](#mifare-xiaomihtml) - - [mifare-value.html](#mifare-valuehtml) - - [lf-em410x.html](#lf-em410xhtml) +# Demos ## [device-settings.html](https://taichunmin.idv.tw/chameleon-ultra.js/device-settings.html) diff --git a/pug/include/bootstrapV4.pug b/pug/include/bootstrapV4.pug index a99d24b..7f6bda9 100644 --- a/pug/include/bootstrapV4.pug +++ b/pug/include/bootstrapV4.pug @@ -61,4 +61,14 @@ html(lang="zh-Hant") script(crossorigin="anonymous", src=`${baseurl}plugin/DfuZip.global.js`) script(crossorigin="anonymous", src=`${baseurl}plugin/WebbleAdapter.global.js`) script(crossorigin="anonymous", src=`${baseurl}plugin/WebserialAdapter.global.js`) + script(type="importmap"). + { + "imports": { + "@taichunmin/buffer": "https://cdn.jsdelivr.net/npm/@taichunmin/buffer@0/+esm", + "@taichunmin/crc/crc16a": "https://cdn.jsdelivr.net/npm/@taichunmin/crc@0/crc16a/+esm", + "@taichunmin/crc/crc32": "https://cdn.jsdelivr.net/npm/@taichunmin/crc@0/crc32/+esm", + "jszip": "https://cdn.jsdelivr.net/npm/jszip/+esm", + "lodash": "https://cdn.jsdelivr.net/npm/lodash/+esm" + } + } block script diff --git a/pug/src/hf14a-scanner.pug b/pug/src/hf14a-scanner.pug index bf4bcc3..529f195 100644 --- a/pug/src/hf14a-scanner.pug +++ b/pug/src/hf14a-scanner.pug @@ -118,7 +118,7 @@ block script const newTags = [] for (const tag of scanned) { newTags.push({ - atqa: toHex(tag.atqa.reverse()), + atqa: toHex(tag.atqa.toReversed()), sak: toHex(tag.sak), ts: Date.now(), uid: toHex(tag.uid), diff --git a/pug/src/lf-em410x.pug b/pug/src/lf-em410x.pug index 425fc95..20c766b 100644 --- a/pug/src/lf-em410x.pug +++ b/pug/src/lf-em410x.pug @@ -80,7 +80,7 @@ block script ultraBle.use(new WebbleAdapter()) const toHex = buf => _.toUpper(buf.toString('hex')) - const WELL_KNOWN_KEYS = ['19920427', '51243648'] + const WELL_KNOWN_KEYS = ['1DD00A11', '19920427', '51243648'] window.vm = new Vue({ el: '#app', diff --git a/pug/src/mfkey32.pug b/pug/src/mfkey32.pug index f55e4d8..e6697f6 100644 --- a/pug/src/mfkey32.pug +++ b/pug/src/mfkey32.pug @@ -115,6 +115,7 @@ block script const ultraBle = new ChameleonUltra() ultraBle.use(new Debug()) ultraBle.use(new WebbleAdapter()) + const toHex = buf => _.toUpper(buf.toString('hex')) window.vm = new Vue({ el: '#app', @@ -166,10 +167,10 @@ block script const tag1 = _.first(await ultra.cmdHf14aScan()) await ultra.cmdChangeDeviceMode(DeviceMode.TAG) const tag2 = { - atqa: _.toUpper(tag1.atqa.reverse().toString('hex')), - sak: _.toUpper(tag1.sak.toString('hex')), + atqa: toHex(tag1.atqa.toReversed()), + sak: toHex(tag1.sak), slot, - uid: _.toUpper(tag1.uid.toString('hex')), + uid: toHex(tag1.uid), } tag2.tagType = (tag2.atqa === '0002' && tag2.sak === '18') ? 1 : 0 console.log(tag2) diff --git a/pug/src/mifare-xiaomi.pug b/pug/src/mifare-xiaomi.pug index 6f40bcc..093a606 100644 --- a/pug/src/mifare-xiaomi.pug +++ b/pug/src/mifare-xiaomi.pug @@ -482,7 +482,7 @@ block script dumpB64: dump.toString('base64url'), uid: toHex(dump.subarray(0, 4)), sak: toHex(dump.subarray(5, 6)), - atqa: toHex(dump.subarray(6, 8).reverse()), + atqa: toHex(dump.subarray(6, 8).toReversed()), }) }, showLoading (opts = {}) { diff --git a/pug/src/mifare1k.pug b/pug/src/mifare1k.pug index d461ead..58145d6 100644 --- a/pug/src/mifare1k.pug +++ b/pug/src/mifare1k.pug @@ -632,7 +632,7 @@ block script mfCardSetAntiColl (hf14aAntiColl) { for (const k of ['uid', 'atqa', 'sak', 'ats']) { let buf = hf14aAntiColl?.[k] ?? new Buffer() - if (k === 'atqa') buf = buf.reverse() + if (k === 'atqa') buf = buf.toReversed() this.$set(this.ss, k, buf.toString('hex')) } }, diff --git a/src/ChameleonUltra.test.ts b/src/ChameleonUltra.test.ts index 0bafe89..45f4199 100644 --- a/src/ChameleonUltra.test.ts +++ b/src/ChameleonUltra.test.ts @@ -1,7 +1,6 @@ -import _ from 'lodash' import { Buffer } from '@taichunmin/buffer' +import _ from 'lodash' import { ChameleonUltra } from './ChameleonUltra' -import BufferMockAdapter from './plugin/BufferMockAdapter' import { AnimationMode, ButtonAction, @@ -17,6 +16,7 @@ import { Slot, TagType, } from './enums' +import BufferMockAdapter from './plugin/BufferMockAdapter' describe('ChameleonUltra with BufferMockAdapter', () => { let ultra: ChameleonUltra @@ -1133,7 +1133,7 @@ describe('ChameleonUltra with BufferMockAdapter', () => { adapter.send.push(Buffer.from('11ef 07da 0000 0010 0f 040dc445420d2981e7480000e1100600 c7', 'hex')) // act - const actual = await ultra.mfuReadPages({ pageOffset: 0 }) + const actual = await ultra.mfuReadPages({ start: 0 }) // assert expect(actual).toEqual(Buffer.from('040dc445420d2981e7480000e1100600', 'hex')) @@ -1149,7 +1149,7 @@ describe('ChameleonUltra with BufferMockAdapter', () => { adapter.send.push(Buffer.from('11ef 07da 0000 0000 1f 00', 'hex')) // act - await ultra.mfuWritePage({ pageOffset: 9, data: Buffer.from('00000000', 'hex') }) + await ultra.mfuWritePage({ start: 9, data: Buffer.from('00000000', 'hex') }) // assert expect(adapter.recv).toEqual([ diff --git a/src/ChameleonUltra.ts b/src/ChameleonUltra.ts index 7d33e2d..8cf98ba 100644 --- a/src/ChameleonUltra.ts +++ b/src/ChameleonUltra.ts @@ -1,26 +1,13 @@ -import _ from 'lodash' import { Buffer } from '@taichunmin/buffer' -import { EventAsyncGenerator } from './EventAsyncGenerator' -import { EventEmitter } from './EventEmitter' -import { middlewareCompose, sleep, type MiddlewareComposeFn, versionCompare } from './helper' -import { type DfuImage } from './plugin/DfuZip' -import { type ReadableStream, type UnderlyingSink, type WritableStreamDefaultController, WritableStream } from 'node:stream/web' -import * as Decoder from './ResponseDecoder' import crc16a from '@taichunmin/crc/crc16a' import crc32 from '@taichunmin/crc/crc32' - +import _ from 'lodash' +import { type ReadableStream, type UnderlyingSink, type WritableStreamDefaultController, WritableStream } from 'node:stream/web' import { - Cmd, - DeviceMode, - DfuObjType, - DfuOp, - DfuResCode, - Mf1KeyType, - MfuCmd, - RespStatus, type AnimationMode, type ButtonAction, type ButtonType, + type DarksideStatus, type DeviceModel, type DfuFwId, type DfuFwType, @@ -29,21 +16,37 @@ import { type Mf1PrngType, type Mf1VblockOperator, type Slot, - type TagType, - + Cmd, + DeviceMode, + DfuObjType, + DfuOp, + DfuResCode, isAnimationMode, isButtonAction, isButtonType, isDeviceMode, isDfuFwId, + isFailedRespStatus, isMf1EmuWriteMode, isMf1KeyType, isMf1VblockOperator, + isMfuEmuTagType, isSlot, isTagType, isValidDfuObjType, isValidFreqType, + Mf1KeyType, + MfuCmd, + MfuVerToMfuTagType, + MfuTagType, + RespStatus, + TagType, } from './enums' +import { EventAsyncGenerator } from './EventAsyncGenerator' +import { EventEmitter } from './EventEmitter' +import { type MiddlewareComposeFn, middlewareCompose, sleep, versionCompare } from './helper' +import { type DfuImage } from './plugin/DfuZip' +import * as Decoder from './ResponseDecoder' const READ_DEFAULT_TIMEOUT = 5e3 const START_OF_FRAME = new Buffer(2).writeUInt16BE(0x11EF) @@ -59,6 +62,10 @@ function validateMf1BlockKey (block: any, keyType: any, key: any, prefix: string bufIsLenOrFail(key, 6, `${prefix}key`) } +function toUpperHex (buf: Buffer): string { + return _.toUpper(buf.toString('hex')) +} + /** * The core library of `chameleon-ultra.js`. You need to register exactly one adapter to the `ChameleonUltra` instance. * @@ -138,8 +145,7 @@ export class ChameleonUltra { static VERSION_SUPPORTED = VERSION_SUPPORTED /** - * @group Internal - * @internal + * @hidden */ readDefaultTimeout: number = READ_DEFAULT_TIMEOUT @@ -148,14 +154,12 @@ export class ChameleonUltra { * - `disconnected`: Emitted when device is disconnected. * - `connected`: Emitted when device is connected. * - `debug`: Emitted when debug message is generated. `(logName: string, formatter: any, ...args: [] | any[]) => void` - * @internal - * @group Internal + * @hidden */ readonly emitter = new EventEmitter() /** - * @group Internal - * @internal + * @hidden */ port?: ChameleonSerialPort @@ -862,7 +866,10 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdSlotGetInfo (): Promise { + async cmdSlotGetInfo (): Promise> { const cmd = Cmd.GET_SLOT_INFO // cmd = 1019 const readResp = await this.#createReadRespFn({ cmd }) await this.#sendCmd({ cmd }) @@ -943,7 +950,10 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdSlotGetIsEnable (): Promise { + async cmdSlotGetIsEnable (): Promise> { const cmd = Cmd.GET_ENABLED_SLOTS // cmd = 1023 const readResp = await this.#createReadRespFn({ cmd }) await this.#sendCmd({ cmd }) @@ -988,7 +998,10 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdGetBatteryInfo (): Promise { + async cmdGetBatteryInfo (): Promise<{ + voltage: number + level: number + }> { const cmd = Cmd.GET_BATTERY_INFO // cmd = 1025 const readResp = await this.#createReadRespFn({ cmd }) await this.#sendCmd({ cmd }) @@ -1196,7 +1209,14 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdGetDeviceSettings (): Promise { + async cmdGetDeviceSettings (): Promise<{ + version: number + animation: AnimationMode + buttonPressAction: ButtonAction[] + buttonLongPressAction: ButtonAction[] + blePairingMode: boolean + blePairingKey: string + }> { const cmd = Cmd.GET_DEVICE_SETTINGS // cmd = 1034 const readResp = await this.#createReadRespFn({ cmd }) await this.#sendCmd({ cmd }) @@ -1303,7 +1323,12 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdHf14aScan (): Promise { + async cmdHf14aScan (): Promise> { await this.assureDeviceMode(DeviceMode.READER) const cmd = Cmd.HF14A_SCAN // cmd = 2000 const readResp = await this.#createReadRespFn({ cmd }) @@ -1404,7 +1429,7 @@ export class ChameleonUltra { async cmdMf1AcquireStaticNested ( known: { block: number, key: Buffer, keyType: Mf1KeyType }, target: { block: number, keyType: Mf1KeyType } - ): Promise { + ): Promise<{ uid: Buffer, atks: Array<{ nt1: Buffer, nt2: Buffer }> }> { validateMf1BlockKey(known.block, known.keyType, known.key, 'known.') if (!isMf1BlockNo(target.block)) throw new TypeError('Invalid target.block') if (!isMf1KeyType(target.keyType)) throw new TypeError('Invalid target.keyType') @@ -1485,7 +1510,15 @@ export class ChameleonUltra { keyType: Mf1KeyType, isFirst: boolean | number, syncMax: number = 30 - ): Promise { + ): Promise<{ + status: DarksideStatus + uid?: Buffer + nt?: Buffer + par?: Buffer + ks?: Buffer + nr?: Buffer + ar?: Buffer + }> { if (!_.isSafeInteger(block)) throw new TypeError('Invalid block') if (!isMf1KeyType(keyType)) throw new TypeError('Invalid keyType') if (_.isNil(isFirst)) throw new TypeError('Invalid isFirst') @@ -1538,7 +1571,11 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdMf1TestNtDistance (known: { block: number, key: Buffer, keyType: Mf1KeyType }): Promise { + async cmdMf1TestNtDistance (known: { + block: number + key: Buffer + keyType: Mf1KeyType + }): Promise<{ uid: Buffer, dist: Buffer }> { validateMf1BlockKey(known.block, known.keyType, known.key, 'known.') await this.assureDeviceMode(DeviceMode.READER) const cmd = Cmd.MF1_DETECT_NT_DIST // cmd = 2005 @@ -1557,6 +1594,9 @@ export class ChameleonUltra { * @param target.block - The block of target key. * @param target.keyType - The key type of target key. * @returns The result of mifare nested attack. + * - nt1: Unblocked explicitly random number + * - nt2: Random number of nested verification encryption + * - par: The puppet test of the communication process of nested verification encryption, only the 'low 3 digits', that is, the right 3 * @group Mifare Classic Related * @example * ```js @@ -1594,7 +1634,7 @@ export class ChameleonUltra { async cmdMf1AcquireNested ( known: { block: number, key: Buffer, keyType: Mf1KeyType }, target: { block: number, keyType: Mf1KeyType } - ): Promise { + ): Promise> { validateMf1BlockKey(known.block, known.keyType, known.key, 'known.') if (!_.isSafeInteger(target.block)) throw new TypeError('Invalid target.block') if (!isMf1KeyType(target.keyType)) throw new TypeError('Invalid target.keyType') @@ -1737,7 +1777,11 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async hf14aInfo (): Promise { + async hf14aInfo (): Promise> { const items = [] const antiColls = await this.cmdHf14aScan() for (const antiColl of antiColls) { @@ -1996,7 +2040,7 @@ export class ChameleonUltra { } let bitsCnt = 80 - for (let b of mask as unknown as number[]) while (b > 0) [bitsCnt, b] = [bitsCnt - (b & 0b1), b >>> 1] + for (let b of mask) while (b > 0) [bitsCnt, b] = [bitsCnt - (b & 0b1), b >>> 1] if (bitsCnt < 1) return null await this.assureDeviceMode(DeviceMode.READER) @@ -2192,7 +2236,15 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdMf1GetDetectionLogs (offset: number = 0): Promise { + async cmdMf1GetDetectionLogs (offset: number = 0): Promise> { if (!_.isSafeInteger(offset)) throw new TypeError('Invalid offset') const cmd = Cmd.MF1_GET_DETECTION_LOG // cmd = 4006 const readResp = await this.#createReadRespFn({ cmd }) @@ -2266,7 +2318,13 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdMf1GetEmuSettings (): Promise { + async cmdMf1GetEmuSettings (): Promise<{ + detection: boolean + gen1a: boolean + gen2: boolean + antiColl: boolean + write: Mf1EmuWriteMode + }> { const cmd = Cmd.MF1_GET_EMULATOR_CONFIG // cmd = 4009 const readResp = await this.#createReadRespFn({ cmd }) await this.#sendCmd({ cmd }) @@ -2458,7 +2516,7 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdHf14aGetAntiCollData (): Promise { + async cmdHf14aGetAntiCollData (): Promise<{ uid: Buffer, atqa: Buffer, sak: Buffer, ats: Buffer } | null> { const cmd = Cmd.HF14A_GET_ANTI_COLL_DATA // cmd = 4018 const readResp = await this.#createReadRespFn({ cmd }) await this.#sendCmd({ cmd }) @@ -2467,64 +2525,308 @@ export class ChameleonUltra { } /** - * Set the em410x id of actived slot. - * @param id - The em410x id of actived slot. - * @group Emulator Related + * Get the magic mode of actived slot. + * + * If the actived slot is in magic mode, all read and write protection is bypassed. + * + * - The UID (page 0-1) can be write. + * - Static Lock Bytes (page 2) and Dynamic Lock Bytes can be write with any value. + * - The Capability Container CC of NTAG (page 3) can be write with any value. + * - PWD and PACK can be read. + * - All other pages can be read/write without authentication. + * - The counter of NTAG can be read without authentication. + * @group Mifare Ultralight Related + * @returns The magic mode of actived slot. + * @example + * ```js + * async function run (ultra) { + * console.log(await ultra.cmdMfuGetMagicMode()) // false + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdMfuGetMagicMode (): Promise { + const cmd = Cmd.MF0_NTAG_GET_UID_MAGIC_MODE // cmd = 4019 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd }) + return (await readResp()).data[0] === 1 + } + + /** + * Set the magic mode of actived slot. + * + * If the actived slot is in magic mode, all read and write protection is bypassed. + * + * - The UID (page 0-1) can be write. + * - Static Lock Bytes (page 2) and Dynamic Lock Bytes can be write with any value. + * - The Capability Container CC of NTAG (page 3) can be write with any value. + * - PWD and PACK can be read. + * - All other pages can be read/write without authentication. + * - The counter of NTAG can be read without authentication. + * @param enable - The magic mode of actived slot. + * @group Mifare Ultralight Related + * @example + * ```js + * async function run (ultra) { + * await ultra.cmdMfuSetMagicMode(false) + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdMfuSetMagicMode (enable: boolean | number): Promise { + if (_.isNil(enable)) throw new TypeError('enable is required') + const cmd = Cmd.MF0_NTAG_SET_UID_MAGIC_MODE // cmd = 4020 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd, data: Buffer.pack('!?', enable) }) + await readResp() + } + + /** + * Get the page data of actived slot. + * @param offset - The start page of actived slot. + * @param length - The count of pages to be get. Must satisfy: `1 <= length <= 128`. + * @group Mifare Ultralight Related + * @returns The page data of actived slot. + * @example + * ```js + * async function run (ultra) { + * const data = await ultra.cmdMfuReadEmuPage(1) + * console.log(data.toString('hex')) // 'fa5c6480' + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdMfuReadEmuPage (offset: number = 0, length: number = 1): Promise { + if (!_.inRange(length, 1, 129)) throw new TypeError('length must be in range [1, 128]') + const cmd = Cmd.MF0_NTAG_READ_EMU_PAGE_DATA // cmd = 4021 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd, data: Buffer.pack('!BB', offset, length) }) + return (await readResp()).data + } + + /** + * Set the page data of actived slot. + * @param offset - The start page of actived slot. + * @param data - The data to be write. Length of data must be multiples of 4 and satisfy: `4 <= data.length <= 508`. + * @group Mifare Ultralight Related * @example * ```js * async function run (ultra) { * const { Buffer } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm') - * await ultra.cmdEm410xSetEmuId(Buffer.from('deadbeef88', 'hex')) + * await ultra.cmdMfuWriteEmuPage(1, Buffer.from('fa5c6480', 'hex')) * } * * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdEm410xSetEmuId (id: Buffer): Promise { - bufIsLenOrFail(id, 5, 'id') - const cmd = Cmd.EM410X_SET_EMU_ID // cmd = 5000 + async cmdMfuWriteEmuPage (offset: number, data: Buffer): Promise { + if (!_.isSafeInteger(offset)) throw new TypeError('Invalid offset') + if (!Buffer.isBuffer(data)) throw new TypeError('data must be a Buffer') + if (!_.inRange(data.length, 4, 509)) throw new TypeError('data.length must be in range [4, 508]') + const pageSize = Math.trunc(data.length / 4) + if (data.length !== pageSize * 4) throw new TypeError('data.length must be multiples of 4') + const cmd = Cmd.MF0_NTAG_WRITE_EMU_PAGE_DATA // cmd = 4022 const readResp = await this.#createReadRespFn({ cmd }) - await this.#sendCmd({ cmd, data: id }) + await this.#sendCmd({ cmd, data: Buffer.pack(`!BB${data.length}s`, offset, pageSize, data) }) await readResp() } /** - * Get the em410x id of actived slot. - * @returns The em410x id of actived slot. - * @group Emulator Related + * Get the version of actived slot. The version is used to retrieve information on the NTAG family, the product version, storage size and other product data required to identify the specific NTAG21x. + * @group Mifare Ultralight Related + * @returns The version of actived slot. * @example * ```js * async function run (ultra) { - * const id = await ultra.cmdEm410xGetEmuId() - * console.log(id.toString('hex')) // 'deadbeef88' + * const data = await ultra.cmdMfuGetEmuVersion() + * console.log(data.toString('hex')) // '0004040201001103' * } * * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async cmdEm410xGetEmuId (): Promise { - const cmd = Cmd.EM410X_GET_EMU_ID // cmd = 5001 + async cmdMfuGetEmuVersion (): Promise { + const cmd = Cmd.MF0_NTAG_GET_VERSION_DATA // cmd = 4023 const readResp = await this.#createReadRespFn({ cmd }) await this.#sendCmd({ cmd }) - return (await readResp()).data + const { data } = await readResp() + return data.length > 0 ? data : undefined } /** - * Check if the firmware version is supported by SDK. - * @returns `true` if the firmware version is supported, `false` otherwise. - * @group Device Related + * Set the version of actived slot. The version is used to retrieve information on the NTAG family, the product version, storage size and other product data required to identify the specific NTAG21x. + * @param version - The version of actived slot. + * @group Mifare Ultralight Related * @example * ```js * async function run (ultra) { - * if (await ultra.isSupportedAppVersion()) throw new Error('Firmware version is not supported. Please update the firmware.') + * const { Buffer } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm') + * await ultra.cmdMfuSetEmuVersion(Buffer.from('0004040201001103', 'hex')) * } * * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async isSupportedAppVersion (): Promise { - const version = await this.cmdGetAppVersion() - return versionCompare(version, VERSION_SUPPORTED.gte) >= 0 && versionCompare(version, VERSION_SUPPORTED.lt) < 0 + async cmdMfuSetEmuVersion (version: Buffer): Promise { + bufIsLenOrFail(version, 8, 'version') + const cmd = Cmd.MF0_NTAG_SET_VERSION_DATA // cmd = 4024 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd, data: version }) + await readResp() + } + + /** + * Get the signature of actived slot. NTAG21x features a cryptographically supported originality check. The signature is used to verify with a certain confidence that the tag is using an IC manufactured by NXP Semiconductors. The signature digital is based on standard Elliptic Curve Cryptography (curve name secp128r1), according to the ECDSA algorithm. + * @group Mifare Ultralight Related + * @returns The signature of actived slot. + * @example + * ```js + * async function run (ultra) { + * const data = await ultra.cmdMfuGetEmuSignature() + * console.log(data.toString('hex')) // '0000000000000000000000000000000000000000000000000000000000000000' + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdMfuGetEmuSignature (): Promise { + const cmd = Cmd.MF0_NTAG_GET_SIGNATURE_DATA // cmd = 4025 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd }) + const { data } = await readResp() + return data.length > 0 ? data : undefined + } + + /** + * Set the signature of actived slot. NTAG21x features a cryptographically supported originality check. The signature is used to verify with a certain confidence that the tag is using an IC manufactured by NXP Semiconductors. The signature digital is based on standard Elliptic Curve Cryptography (curve name secp128r1), according to the ECDSA algorithm. + * @param signature - The signature. The signature must be a 32 bytes Buffer. + * @group Mifare Ultralight Related + * @example + * ```js + * async function run (ultra) { + * const { Buffer } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm') + * const signature = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') + * await ultra.cmdMfuSetEmuSignature(signature) + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdMfuSetEmuSignature (signature: Buffer): Promise { + bufIsLenOrFail(signature, 32, 'signature') + const cmd = Cmd.MF0_NTAG_SET_SIGNATURE_DATA // cmd = 4026 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd, data: signature }) + await readResp() + } + + /** + * Read the counter and tearing of actived slot. + * + * NTAG21x features a NFC counter function. The NFC counter is enabled or disabled with the NFC_CNT_EN bit. This function enables NTAG21x to automatically increase the 24 bit counter value, triggered by the first valid READ or FAST_READ command after the NTAG21x tag is powered by an RF field. + * @param addr - The address of the counter. + * @group Mifare Ultralight Related + * @returns + * - counter: The counter of the specified address. + * - tearing: The slot is in tearing mode or not. + * @example + * ```js + * async function run (ultra) { + * console.log(await ultra.cmdMfuGetEmuCounter(0)) + * // { "counter": 0, "tearing": false } + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdMfuGetEmuCounter (addr: number): Promise<{ counter?: number, tearing?: boolean }> { + if (!_.includes([0, 1, 2], addr)) throw new TypeError('Invalid addr') + const cmd = Cmd.MF0_NTAG_GET_COUNTER_DATA // cmd = 4027 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd, data: Buffer.pack('!B', addr) }) + const { data } = await readResp() + if (data.length !== 4) return { counter: undefined, tearing: undefined } + const [counter, tearing] = [data.readUIntBE(0, 3), data[3] === 0x00] + return { counter, tearing } + } + + /** + * Set the counter and reset tearing of actived slot. + * + * NTAG21x features a NFC counter function. The NFC counter is enabled or disabled with the NFC_CNT_EN bit. This function enables NTAG21x to automatically increase the 24 bit counter value, triggered by the first valid READ or FAST_READ command after the NTAG21x tag is powered by an RF field. + * @param opts.addr - The address of the counter. + * @param opts.counter - The counter to be write. The counter must be a 24-bit unsigned integer. + * @param opts.resetTearing - `true` to reset tearing. + * @group Mifare Ultralight Related + * @example + * ```js + * async function run (ultra) { + * await ultra.cmdMfuSetEmuCounter({ addr: 0, counter: 1 }) + * console.log(await ultra.cmdMfuGetEmuCounter(0)) + * // { "counter": 1, "tearing": false } + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdMfuSetEmuCounter (opts: { + addr?: number + counter?: number + resetTearing?: boolean + }): Promise { + const { addr = 2, counter = 0, resetTearing = false } = opts + if (!_.includes([0, 1, 2], addr)) throw new TypeError('Invalid addr') + if (!_.isSafeInteger(counter) || !_.inRange(counter, 0x1000000)) throw new TypeError('Invalid counter') + const data = new Buffer(4) + data[0] = (resetTearing ? 0x80 : 0x00) + addr + data.writeUIntBE(counter, 1, 3) + const cmd = Cmd.MF0_NTAG_SET_COUNTER_DATA // cmd = 4028 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd, data }) + await readResp() + } + + /** + * Reset the authentication failed counter of actived slot. + * @group Mifare Ultralight Related + * @returns The original value of the unsuccessful auth counter before reset. + * @example + * ```js + * async function run (ultra) { + * console.log(await ultra.cmdMfuResetEmuAuthFailedCounter()) // 0 + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdMfuResetEmuAuthFailedCounter (): Promise { + const cmd = Cmd.MF0_NTAG_RESET_AUTH_CNT // cmd = 4029 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd }) + return (await readResp()).data[0] + } + + /** + * Get the number of pages available in the actived slot. + * @group Mifare Ultralight Related + * @returns The number of pages available in the actived slot. + * @example + * ```js + * async function run (ultra) { + * console.log(await ultra.cmdMfuGetEmuPageSize()) // 135 + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdMfuGetEmuPageSize (): Promise { + const cmd = Cmd.MF0_NTAG_GET_PAGE_COUNT // cmd = 4030 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd }) + return (await readResp()).data[0] } /** @@ -2533,15 +2835,16 @@ export class ChameleonUltra { * @param opts.keepRfField - `true` to keep RF field after auth, `false` to disable RF field. * @param opts.key - The password to be verified. The password must be a 4 bytes Buffer. * @group Mifare Ultralight Related - * @returns + * @returns The password authentication acknowledge, PACK */ async mfuAuth (opts: { autoSelect?: boolean keepRfField?: boolean key: Buffer + timeout?: number }): Promise { try { - const { autoSelect = true, keepRfField = true, key } = opts + const { autoSelect = true, keepRfField = true, key, timeout } = opts if (!Buffer.isBuffer(key)) throw new TypeError('key must be a Buffer') if (key.length === 16) throw new Error('auth Ultralight-C is not implemented') if (key.length !== 4) throw new Error('key must be a 4 bytes Buffer.') @@ -2551,8 +2854,15 @@ export class ChameleonUltra { data: Buffer.pack(`!B${key.length}s`, MfuCmd.PWD_AUTH, key), keepRfField, waitResponse: true, + timeout, }) - return mfuCheckRespNakCrc16a(resp) + try { + return mfuCheckRespNakCrc16a(resp) + } catch (err) { + const resp = err?.data?.resp + if (resp.length === 1 && resp[0] === 0x04) err.status = RespStatus.MF_ERR_AUTH + throw err + } } catch (err) { throw _.set(new Error(`Auth failed: ${err.message}`), 'originalError', err) } @@ -2560,7 +2870,7 @@ export class ChameleonUltra { /** * Read 4 pages (16 bytes) from Mifare Ultralight - * @param opts.pageOffset - page number to read + * @param opts.start - start page address * @param opts.key - The password to be verified. The password must be a 4 bytes Buffer. * @returns 4 pages (16 bytes) * @group Mifare Ultralight Related @@ -2568,25 +2878,246 @@ export class ChameleonUltra { * @example * ```js * async function run (ultra) { - * const data = await ultra.mfuReadPages({ pageOffset: 0 }) + * const data = await ultra.mfuReadPages({ start: 0 }) * console.log(data.toString('hex')) // '040dc445420d2981e7480000e1100600' * } * * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async mfuReadPages (opts: { key?: Buffer, pageOffset: number }): Promise { - const { key, pageOffset } = opts - if (!_.isSafeInteger(pageOffset)) throw new TypeError('Invalid pageOffset') + async mfuReadPages (opts: { key?: Buffer, start: number, timeout?: number }): Promise { + const { key, start, timeout } = opts + if (!_.isSafeInteger(start)) throw new TypeError('Invalid start') if (!_.isNil(key)) await this.mfuAuth({ keepRfField: true, key }) return await this.cmdHf14aRaw({ appendCrc: true, autoSelect: _.isNil(key), checkResponseCrc: true, - data: Buffer.pack('!BB', MfuCmd.READ, pageOffset), + data: Buffer.pack('!BB', MfuCmd.READ, start), + timeout, }) } + /** + * Read multiple pages from start to end. For example if the start address is 0x03 and the end address is 0x07 then pages 0x03, 0x04, 0x05, 0x06 and 0x07 are returned. If the addressed page is outside of accessible area, NTAG21x replies a NAK. + * @param opts.start - start page address + * @param opts.end - end page address + * @param opts.key - The password to be verified. The password must be a 4 bytes Buffer. + * @returns 4 pages (16 bytes) + * @group Mifare Ultralight Related + * @see [MF0ICU1 MIFARE Ultralight contactless single-ticket IC](https://www.nxp.com/docs/en/data-sheet/MF0ICU1.pdf#page=16) + * @example + * ```js + * async function run (ultra) { + * const data = await ultra.mfuFastReadPages({ start: 0, end: 3 }) + * console.log(data.toString('hex')) // '047c79896cb62a8171480000e1103e00' + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async mfuFastReadPages (opts: { key?: Buffer, start: number, end: number, timeout?: number }): Promise { + const { end, key, start, timeout } = opts + if (!_.isSafeInteger(start) || start < 0 || start > end) throw new TypeError('Invalid start') + if (!_.isSafeInteger(end) || end < start || end > 0xFF) throw new TypeError('Invalid end') + if (!_.isNil(key)) await this.mfuAuth({ keepRfField: true, key }) + return await this.cmdHf14aRaw({ + appendCrc: true, + autoSelect: _.isNil(key), + checkResponseCrc: true, + data: Buffer.pack('!BBB', MfuCmd.FAST_READ, start, end), + timeout, + }) + } + + /** + * Detect Mifare Ultralight tag and return the tag infomation. + * @returns The tag infomation of detected tag. + * @group Mifare Ultralight Related + * @see [Proxmark3 `hf mfu info`](https://github.com/RfidResearchGroup/proxmark3/blob/4e0d4d3ad454285e62fc1a22c2ef3adda508ed01/client/src/cmdhfmfu.c#L2089) + * @example + * ```js + * async function run (ultra) { + * const { MfuTagTypeName } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm') + * const MfuTagType = await ultra.mfuDetectTagType() + * console.log(`tagType = ${MfuTagTypeName.get(MfuTagType)}`) + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async mfuDetectTagType (): Promise { + const timeout = 500 + const tags = await this.cmdHf14aScan() + if (tags.length > 1) throw new Error('More than one tag detected.') + else if (tags.length === 0) throw new Error('Tag not found.') + + const tag = tags[0] + if (!(tag.atqa.readUint16LE(0) === 0x0044 && tag.sak[0] === 0x00)) throw new Error(`Unknown tag: atqa = ${toUpperHex(tag.atqa.toReversed())}, sak = ${toUpperHex(tag.sak)}`) + + if (tag.uid[0] === 0x05) { // Infinition MY-D tests, Exam high nibble + const nib = tag.uid[1] >>> 4 + if (nib === 1) return MfuTagType.MY_D + else if (nib === 2) return MfuTagType.MY_D_NFC + else if (nib === 3) return MfuTagType.MY_D_MOVE + else if (nib === 7) return MfuTagType.MY_D_MOVE_LEAN + else return MfuTagType.UNKNOWN + } + + // try GET_VERSION cmd + // const ver1 = await this.mfuGetVersion({ timeout }).catch(err => { this.#emitErr(err) }) + const ver1 = await this.cmdHf14aRaw({ + appendCrc: true, + autoSelect: true, + data: Buffer.pack('!B', MfuCmd.GET_VERSION), + timeout, + }).catch(err => { this.#emitErr(err) }) + // console.log(`ver1 = ${ver1?.toString('hex')}`) + + if (Buffer.isBuffer(ver1)) { + if (ver1.length === 10) { + let tagType: MfuTagType | undefined + tagType = MfuVerToMfuTagType.get(toUpperHex(ver1.subarray(0, 8))) + if (!_.isNil(tagType)) return tagType + tagType = MfuVerToMfuTagType.get(toUpperHex(ver1.subarray(0, 7))) + if (!_.isNil(tagType)) return tagType + if (ver1[2] === 0x04) return MfuTagType.NTAG + if (ver1[2] === 0x03) return MfuTagType.UL_EV1 + } else if (ver1.length === 1) return MfuTagType.UL_C + else if (ver1.length === 0) return MfuTagType.UL + else return MfuTagType.UNKNOWN + } + + // try TDES_AUTH cmd (should has resp if it is a Ultralight-C) + const auth1 = await this.cmdHf14aRaw({ + appendCrc: true, + autoSelect: true, + data: Buffer.pack('!B', MfuCmd.TDES_AUTH), + timeout, + }).catch(err => { this.#emitErr(err) }) + // console.log(`auth1 = ${auth1?.toString('hex')}`) + if (Buffer.isBuffer(auth1)) return MfuTagType.UL_C + + // try read page 0x26 (should error if it is a Ultralight) + const read1 = await this.mfuReadPages({ start: 0x26, timeout }).catch(err => { this.#emitErr(err) }) + // console.log(`read1 = ${read1?.toString('hex')}`) + if ((read1?.length ?? 0) === 0) return MfuTagType.UL + + // try read page 0x30 (should error if it is a ntag203) + const read2 = await this.mfuReadPages({ start: 0x30, timeout }).catch(err => { this.#emitErr(err) }) + // console.log(`read2 = ${read2?.toString('hex')}`) + if ((read2?.length ?? 0) === 0) return MfuTagType.NTAG_203 + + return MfuTagType.UNKNOWN + } + + /** + * Read the dump of Mifare Ultralight tag. + * @param opts.key - The key to read pages if tag is read protected. + * @param opts.start - start page address + * @param opts.end - end page address + * @returns The dump of Mifare Ultralight tag. + * @group Mifare Ultralight Related + * @example + * ```js + * async function run (ultra) { + * const dump = await ultra.mfuReadDump() + * console.log(`Read ${dump.length} bytes.`) // Read 160 bytes. + * return dump + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async mfuReadDump (opts: { + key?: Buffer + end?: number + start?: number + } = {}): Promise { + const { key, end = 0x100, start = 0 } = opts + const dump = new Buffer(4 * (end - start)) + let dumpOffset = 0 + try { // read until error + while (dumpOffset < dump.length) { + let buf1 = await this.mfuReadPages({ key, start: start + (dumpOffset / 4) }) + if (buf1.length === 0) break + if (buf1.length > dump.length - dumpOffset) buf1 = buf1.subarray(0, dump.length - dumpOffset) + dump.set(buf1, dumpOffset) + dumpOffset += buf1.length + } + } catch (err) { + this.#emitErr(err) + } + return dump.subarray(0, dumpOffset) + } + + /** + * Assert the tag type of actived slot is Mifare Ultralight like. Throw an error if the tag type is not Mifare Ultralight like. + * @returns The tag type of actived slot. + * @group Mifare Ultralight Related + * @example + * ```js + * async function run (ultra) { + * const { TagType } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm') + * const tagType = await ultra.mfuAssertEmuTagType() + * console.log(TagType[tagType]) // '040dc445420d2981e7480000e1100600' + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async mfuAssertEmuTagType (): Promise { + const slot = await this.cmdSlotGetActive() + const slotInfo = await this.cmdSlotGetInfo() + const { hfTagType } = slotInfo[slot] + if (!isMfuEmuTagType(hfTagType)) throw new Error(`Invalid tagType: ${TagType[hfTagType]}`) + return hfTagType + } + + /** + * Get the mifare ultralight settings of actived emulator slot. + * @group Mifare Ultralight Related + * @returns + * - counters: The value of the NFC one-way counter. + * - magic: The magic mode. + * - pageSize: The page size. + * - signature: The IC specific, 32-byte ECC signature. + * - tearing: The slot is in tearing mode or not. + * - version: The version information for the specific NTAG21x type. + * @example + * ```js + * async function run (ultra) { + * const data = await ultra.mfuGetEmuSettings() + * console.log(data) + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async mfuGetEmuSettings (): Promise<{ + counters: Array + magic?: boolean + pageSize?: number + signature?: Buffer + tearing?: boolean + version?: Buffer + }> { + await this.mfuAssertEmuTagType() + const catchErr = (err: Error): undefined => { this.#emitErr(err) } + const magic = await this.cmdMfuGetMagicMode().catch(catchErr) + const pageSize = await this.cmdMfuGetEmuPageSize().catch(catchErr) + const signature = await this.cmdMfuGetEmuSignature().catch(catchErr) + const version = await this.cmdMfuGetEmuVersion().catch(catchErr) + let tearing: boolean | undefined + const counters: Array = [] + for (let i = 0; i < 3; i++) { + const counter = await this.cmdMfuGetEmuCounter(i).catch(catchErr) + counters.push(counter?.counter) + tearing ??= counter?.tearing + } + return { counters, magic, pageSize, signature, tearing, version } + } + /** * The READ_CNT command is used to read out the current value of the NFC one-way counter of the Mifare Ultralight. The command has a single argument specifying the counter number and returns the 24-bit counter value of the corresponding counter. If the NFC_CNT_PWD_PROT bit is set to 1b the counter is password protected and can only be read with the READ_CNT command after a previous valid password authentication. * @param opts.addr - The counter addr to read. Must be `0`, `1` or `2`. Default is `2`. @@ -2663,18 +3194,19 @@ export class ChameleonUltra { * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async mfuGetVersion (): Promise { + async mfuGetVersion (opts: { timeout?: number } = {}): Promise { const resp = await this.cmdHf14aRaw({ appendCrc: true, autoSelect: true, data: Buffer.pack('!B', MfuCmd.GET_VERSION), + timeout: opts.timeout, }) return mfuCheckRespNakCrc16a(resp) } /** * Write 1 page (4 bytes) to Mifare Ultralight - * @param opts.pageOffset - page number to read + * @param opts.start - start page address * @param opts.data - `4 bytes`, the page data to be written. * @param opts.key - The password to be verified. The password must be a 4 bytes Buffer. * @group Mifare Ultralight Related @@ -2683,25 +3215,136 @@ export class ChameleonUltra { * ```js * async function run (ultra) { * const { Buffer } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm') - * const data = await ultra.mfuWritePage({ pageOffset: 9, data: Buffer.from('00000000', 'hex') }) + * const data = await ultra.mfuWritePage({ start: 9, data: Buffer.from('00000000', 'hex') }) * } * * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html * ``` */ - async mfuWritePage (opts: { data: Buffer, key?: Buffer, pageOffset: number }): Promise { - const { data, key, pageOffset } = opts - if (!_.isSafeInteger(pageOffset)) throw new TypeError('Invalid pageOffset') + async mfuWritePage (opts: { data: Buffer, key?: Buffer, start: number }): Promise { + const { data, key, start } = opts + if (!_.isSafeInteger(start)) throw new TypeError('Invalid start') bufIsLenOrFail(data, 4, 'data') if (!_.isNil(key)) await this.mfuAuth({ keepRfField: true, key }) await this.cmdHf14aRaw({ appendCrc: true, autoSelect: _.isNil(key), checkResponseCrc: true, - data: Buffer.pack('!BB4s', MfuCmd.WRITE, pageOffset, data), + data: Buffer.pack('!BB4s', MfuCmd.WRITE, start, data), }) } + /** + * Get the mifare ultralight emulator data of actived slot. + * @returns The mifare ultralight emulator data of actived slot. + * @group Mifare Ultralight Related + * @example + * ```js + * async function run (ultra) { + * const dump = await ultra.mfuReadEmuDump() + * console.log(`Read ${dump.length} bytes.`) + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async mfuReadEmuDump (): Promise { + const pageSize = await this.cmdMfuGetEmuPageSize() + const dump = new Buffer(pageSize * 4) + for (let i = 0; i < pageSize; i += 128) { // max length of resp is 512 bytes + const buf1 = dump.subarray(i * 4) + buf1.set(await this.cmdMfuReadEmuPage(i, Math.min(128, pageSize - i))) + } + return dump + } + + /** + * Write new dump to the actived slot. + * @param dump - New dump to be write to actived slot. + * @group Mifare Ultralight Related + * @example + * ```js + * async function run (ultra) { + * const dump = new Buffer(540) // Dump size of NTAG_213 is 540 bytes. + * dump.set(Buffer.from('04689571fa5c648042480fe0', 'hex')) + * dump.set(Buffer.from('040000ff00000000ffffffff', 'hex'), 524) + * await ultra.mfuWriteEmuDump(dump) + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async mfuWriteEmuDump (dump: Buffer): Promise { + const pageSize = await this.cmdMfuGetEmuPageSize() + dump = dump.subarray(0, pageSize * 4) // truncate dump + bufIsLenOrFail(dump, pageSize * 4, 'dump') + for (let i = 0; i < pageSize; i += 127) { // max length of req is 512 bytes, (512 - 2) / 4 = 127 + const buf1 = dump.subarray(i * 4).subarray(0, 508) // 127 * 4 + await this.cmdMfuWriteEmuPage(i, buf1) + } + } + + /** + * Set the em410x id of actived slot. + * @param id - The em410x id of actived slot. + * @group Emulator Related + * @example + * ```js + * async function run (ultra) { + * const { Buffer } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm') + * await ultra.cmdEm410xSetEmuId(Buffer.from('deadbeef88', 'hex')) + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdEm410xSetEmuId (id: Buffer): Promise { + bufIsLenOrFail(id, 5, 'id') + const cmd = Cmd.EM410X_SET_EMU_ID // cmd = 5000 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd, data: id }) + await readResp() + } + + /** + * Get the em410x id of actived slot. + * @returns The em410x id of actived slot. + * @group Emulator Related + * @example + * ```js + * async function run (ultra) { + * const id = await ultra.cmdEm410xGetEmuId() + * console.log(id.toString('hex')) // 'deadbeef88' + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async cmdEm410xGetEmuId (): Promise { + const cmd = Cmd.EM410X_GET_EMU_ID // cmd = 5001 + const readResp = await this.#createReadRespFn({ cmd }) + await this.#sendCmd({ cmd }) + return (await readResp()).data + } + + /** + * Check if the firmware version is supported by SDK. + * @returns `true` if the firmware version is supported, `false` otherwise. + * @group Device Related + * @example + * ```js + * async function run (ultra) { + * if (await ultra.isSupportedAppVersion()) throw new Error('Firmware version is not supported. Please update the firmware.') + * } + * + * await run(vm.ultra) // you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html + * ``` + */ + async isSupportedAppVersion (): Promise { + const version = await this.cmdGetAppVersion() + return versionCompare(version, VERSION_SUPPORTED.gte) >= 0 && versionCompare(version, VERSION_SUPPORTED.lt) < 0 + } + /** * Send Mifare Classic HALT command and close RF field. * @group Mifare Classic Related @@ -3238,7 +3881,7 @@ export class ChameleonUltra { const readResp = await this.#createReadRespFn({ op }) await this.#sendBuffer(Buffer.pack('([ @@ -3628,10 +4252,10 @@ class UltraFrame { get cmd (): Cmd { return this.buf.readUInt16BE(2) } get data (): Buffer { return this.buf.subarray(9, -1) } get inspect (): string { return UltraFrame.inspect(this) } - get status (): number { return this.buf.readUInt16BE(4) } + get status (): RespStatus { return this.buf.readUInt16BE(4) } get errMsg (): string | undefined { const status = this.status - if (!RespStatusFail.has(status)) return + if (!isFailedRespStatus(status)) return return RespStatusMsg.get(status) ?? `Unknown status code: ${status}` } } @@ -3653,7 +4277,7 @@ export class DfuFrame { get data (): Buffer { return this.buf.subarray(this.isResp === 1 ? 3 : 1) } get inspect (): string { return DfuFrame.inspect(this) } get op (): number { return this.buf[this.isResp] } - get result (): number { + get result (): DfuResCode { if (this.isResp === 0) return DfuResCode.SUCCESS return this.buf[2] === DfuResCode.EXT_ERROR ? this.buf.readUInt16BE(2) : this.buf[2] } @@ -3693,10 +4317,11 @@ function bufIsLenOrFail (buf: Buffer, len: number, name: string): void { } function mfuCheckRespNakCrc16a (resp: Buffer): Buffer { - if (resp.length === 1 && resp[0] !== 0x0A) throw new Error(`received NAK 0x${resp.toString('hex')}`) - if (resp.length < 3) throw new Error('unknown resp') + const createErr = (status: RespStatus, msg: string): Error => _.merge(new Error(msg), { status, data: { resp } }) + if (resp.length === 1 && resp[0] !== 0x0A) throw createErr(RespStatus.HF_ERR_STAT, `received NAK 0x${resp.toString('hex')}`) + if (resp.length < 3) throw createErr(RespStatus.HF_ERR_CRC, 'unexpected resp') const data = resp.subarray(0, -2) - if (crc16a(data) !== resp.readUInt16LE(data.length)) throw new Error('invalid crc16a of resp') + if (crc16a(data) !== resp.readUInt16LE(data.length)) throw createErr(RespStatus.HF_ERR_CRC, 'invalid crc16a of resp') return data } diff --git a/src/Crypto1.test.ts b/src/Crypto1.test.ts index ef5fe40..503cdd7 100644 --- a/src/Crypto1.test.ts +++ b/src/Crypto1.test.ts @@ -1,5 +1,5 @@ -import _ from 'lodash' import { Buffer } from '@taichunmin/buffer' +import _ from 'lodash' import Crypto1 from './Crypto1' test('#reset()', async () => { diff --git a/src/Crypto1.ts b/src/Crypto1.ts index 990d26b..2b26580 100644 --- a/src/Crypto1.ts +++ b/src/Crypto1.ts @@ -2,8 +2,8 @@ * @example * import Crypto1 from 'chameleon-ultra.js/Crypto1' */ -import _ from 'lodash' import { Buffer } from '@taichunmin/buffer' +import _ from 'lodash' import { Mf1KeyType } from './enums' const LF_POLY_ODD = 0x29CE5C diff --git a/src/EventAsyncGenerator.test.ts b/src/EventAsyncGenerator.test.ts index cbaba9d..55ba09d 100644 --- a/src/EventAsyncGenerator.test.ts +++ b/src/EventAsyncGenerator.test.ts @@ -1,7 +1,6 @@ import { EventEmitter } from 'events' import { EventAsyncGenerator } from './EventAsyncGenerator' - -const sleep = async (t: number): Promise => new Promise(resolve => setTimeout(resolve, t)) +import { sleep } from './helper' describe('EventIterable', () => { test('should await immediate onData value', async () => { diff --git a/src/ResponseDecoder.test.ts b/src/ResponseDecoder.test.ts index 33f2819..be10e41 100644 --- a/src/ResponseDecoder.test.ts +++ b/src/ResponseDecoder.test.ts @@ -1,5 +1,5 @@ -import * as sut from './ResponseDecoder' import { Buffer } from '@taichunmin/buffer' +import * as sut from './ResponseDecoder' describe('is not buffer', () => { test.each([ diff --git a/src/ResponseDecoder.ts b/src/ResponseDecoder.ts index e214e3e..74e1df0 100644 --- a/src/ResponseDecoder.ts +++ b/src/ResponseDecoder.ts @@ -1,5 +1,5 @@ -import _ from 'lodash' import { Buffer } from '@taichunmin/buffer' +import _ from 'lodash' import { type Class } from 'utility-types' import { diff --git a/src/enums.ts b/src/enums.ts index b7506e9..237d3e0 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -10,6 +10,13 @@ export function createIsEnum (e: T): (val: any) => val is T return (val: any): val is T[keyof T] => ev.has(val) } +export type ValueOf = T[keyof T] + +export function createIsValueOfArr (arr: T): (val: any) => val is ValueOf { + const set = new Set(arr) + return (val: any): val is ValueOf => set.has(val) +} + export enum AnimationMode { FULL = 0, SHORT = 1, @@ -109,6 +116,18 @@ export enum Cmd { MF1_GET_WRITE_MODE = 4016, MF1_SET_WRITE_MODE = 4017, HF14A_GET_ANTI_COLL_DATA = 4018, + MF0_NTAG_GET_UID_MAGIC_MODE = 4019, + MF0_NTAG_SET_UID_MAGIC_MODE = 4020, + MF0_NTAG_READ_EMU_PAGE_DATA = 4021, + MF0_NTAG_WRITE_EMU_PAGE_DATA = 4022, + MF0_NTAG_GET_VERSION_DATA = 4023, + MF0_NTAG_SET_VERSION_DATA = 4024, + MF0_NTAG_GET_SIGNATURE_DATA = 4025, + MF0_NTAG_SET_SIGNATURE_DATA = 4026, + MF0_NTAG_GET_COUNTER_DATA = 4027, + MF0_NTAG_SET_COUNTER_DATA = 4028, + MF0_NTAG_RESET_AUTH_CNT = 4029, + MF0_NTAG_GET_PAGE_COUNT = 4030, EM410X_SET_EMU_ID = 5000, EM410X_GET_EMU_ID = 5001, @@ -224,6 +243,8 @@ export enum RespStatus { FLASH_WRITE_FAIL = 0x70, /** Flash read failed */ FLASH_READ_FAIL = 0x71, + /** Invalid slot type */ + INVALID_SLOT_TYPE = 0x72, } export enum Slot { @@ -252,17 +273,24 @@ export enum TagType { MIFARE_2048 = 1002, MIFARE_4096 = 1003, // 11xx: MFUL / NTAG series + /** NTAG213 (NT2H1511) */ NTAG_213 = 1100, + /** NTAG215 (NT2H1511) */ NTAG_215 = 1101, + /** NTAG216 (NT2H1611) */ NTAG_216 = 1102, - /** Mifare Ultralight */ + /** Mifare Ultralight (MF0ICU1) */ MF0_ICU1 = 1103, - /** Mifare Ultralight C */ + /** Mifare Ultralight C (MF0ICU2) */ MF0_ICU2 = 1104, - /** Mifare Ultralight EV1 (640 bit) */ + /** Mifare Ultralight EV1 (MF0UL11/MF0ULH11) */ MF0_UL11 = 1105, - /** Mifare Ultralight EV2 (1312 bit) */ + /** Mifare Ultralight EV2 (MF0UL21/MF0ULH21) */ MF0_UL21 = 1106, + /** NTAG210 (NT2L1011) */ + NTAG_210 = 1107, + /** NTAG212 (NT2L1211) */ + NTAG_212 = 1108, // 12xx: MIFARE Plus series // 13xx: DESFire series // 14xx: ST25TA series @@ -373,17 +401,6 @@ export enum DfuFwId { SOFTDEVICE = 0x02, } -export enum MfuCmd { - PWD_AUTH = 0x1B, - READ = 0x30, - READ_CNT = 0x39, - FAST_READ = 0x3A, - READ_SIG = 0x3C, - GET_VERSION = 0x60, - WRITE = 0xA2, - COMP_WRITE = 0xA0, -} - export const isAnimationMode = createIsEnum(AnimationMode) export const isButtonAction = createIsEnum(ButtonAction) export const isButtonType = createIsEnum(ButtonType) @@ -400,5 +417,198 @@ export const isMf1VblockOperator = createIsEnum(Mf1VblockOperator) export const isRespStatus = createIsEnum(RespStatus) export const isSlot = createIsEnum(Slot) export const isTagType = createIsEnum(TagType) -export const isValidDfuObjType = createIsEnum(_.pick(DfuObjType, ['COMMAND', 'DATA'])) -export const isValidFreqType = createIsEnum(_.pick(FreqType, ['HF', 'LF'])) + +export const isFailedRespStatus = createIsValueOfArr([ + RespStatus.HF_TAG_NOT_FOUND, + RespStatus.HF_ERR_STAT, + RespStatus.HF_ERR_CRC, + RespStatus.HF_COLLISION, + RespStatus.HF_ERR_BCC, + RespStatus.MF_ERR_AUTH, + RespStatus.HF_ERR_PARITY, + RespStatus.HF_ERR_ATS, + RespStatus.EM410X_TAG_NOT_FOUND, + RespStatus.PAR_ERR, + RespStatus.DEVICE_MODE_ERROR, + RespStatus.INVALID_CMD, + RespStatus.NOT_IMPLEMENTED, + RespStatus.FLASH_WRITE_FAIL, + RespStatus.FLASH_READ_FAIL, + RespStatus.INVALID_SLOT_TYPE, +] as const) +export const isMfuEmuTagType = createIsValueOfArr([ + TagType.MF0_ICU1, + TagType.MF0_ICU2, + TagType.MF0_UL11, + TagType.MF0_UL21, + TagType.NTAG_210, + TagType.NTAG_212, + TagType.NTAG_213, + TagType.NTAG_215, + TagType.NTAG_216, +] as const) +export const isValidDfuObjType = createIsValueOfArr([ + DfuObjType.COMMAND, + DfuObjType.DATA, +] as const) +export const isValidFreqType = createIsValueOfArr([ + FreqType.HF, + FreqType.LF, +] as const) + +export enum MfuCmd { + CHECK_TEARING_EVENT = 0x3E, + COMP_WRITE = 0xA0, + FAST_READ = 0x3A, + GET_VERSION = 0x60, + INCR_CNT = 0xA5, + /** 3DES Authentication for MF0ICU2 */ + TDES_AUTH = 0x1A, + PWD_AUTH = 0x1B, + READ = 0x30, + READ_CNT = 0x39, + READ_SIG = 0x3C, + VCSL = 0x4B, + WRITE = 0xA2, +} + +export enum MfuTagType { + UNKNOWN = 0x0, + UL = 1, + UL_C = 2, + UL_EV1_48 = 3, + UL_EV1_128 = 4, + NTAG = 5, + NTAG_203 = 6, + NTAG_210 = 7, + NTAG_212 = 8, + NTAG_213 = 9, + NTAG_215 = 10, + NTAG_216 = 11, + MY_D = 12, + MY_D_NFC = 13, + /** my-d move / my-d move NFC */ + MY_D_MOVE = 14, + MY_D_MOVE_LEAN = 15, + NTAG_I2C_1K = 16, + NTAG_I2C_2K = 17, + NTAG_I2C_1K_PLUS = 18, + NTAG_I2C_2K_PLUS = 19, + FUDAN_UL = 20, + NTAG_213_F = 21, + NTAG_216_F = 22, + UL_EV1 = 23, + UL_NANO_40 = 24, + NTAG_213_TT = 25, + NTAG_213_C = 26, + NTAG_210u = 27, + UL_AES = 28, +} + +export const MfuVerToMfuTagType = new Map([ + ['0004030101000B', MfuTagType.UL_EV1_48], + ['0004030101000E', MfuTagType.UL_EV1_128], + ['0004030102000B', MfuTagType.UL_NANO_40], + ['0004030104000F03', MfuTagType.UL_AES], + ['0004030201000B', MfuTagType.UL_EV1_48], + ['0004030201000E', MfuTagType.UL_EV1_128], + ['0004040101000B', MfuTagType.NTAG_210], + ['0004040101000E', MfuTagType.NTAG_212], + ['0004040102000B', MfuTagType.NTAG_210u], + ['0004040201000F', MfuTagType.NTAG_213], + ['00040402010011', MfuTagType.NTAG_215], + ['00040402010013', MfuTagType.NTAG_216], + ['0004040201010F', MfuTagType.NTAG_213_C], + ['0004040202000B', MfuTagType.NTAG_210u], + ['0004040203000F', MfuTagType.NTAG_213_TT], + ['0004040401000F', MfuTagType.NTAG_213_F], + ['00040404010013', MfuTagType.NTAG_216_F], + ['00040405020113', MfuTagType.NTAG_I2C_1K], + ['00040405020115', MfuTagType.NTAG_I2C_2K], + ['00040405020213', MfuTagType.NTAG_I2C_1K_PLUS], + ['00040405020215', MfuTagType.NTAG_I2C_2K_PLUS], + ['0034210101000E', MfuTagType.UL_EV1_128], // Mikron JSC Russia EV1 41 pages tag + ['0053040201000F', MfuTagType.NTAG_213], // Shanghai Feiju Microelectronics Co. Ltd. China (Xiaomi Air Purifier filter) +]) + +export const MfuTagTypeName = new Map([ + [MfuTagType.FUDAN_UL, 'FUDAN Ultralight Compatible (or other compatible)'], + [MfuTagType.MY_D_MOVE_LEAN, 'INFINEON my-d™ move lean (SLE 66R01L)'], + [MfuTagType.MY_D_MOVE, 'INFINEON my-d™ move (SLE 66R01P) / INFINEON my-d™ move NFC (SLE 66R01P)'], + [MfuTagType.MY_D_NFC, 'INFINEON my-d™ NFC (SLE 66RxxP)'], + [MfuTagType.MY_D, 'INFINEON my-d™ (SLE 66RxxS)'], + [MfuTagType.NTAG_203, 'NTAG 203 144bytes (NT2H0301F0DT)'], + [MfuTagType.NTAG_210, 'NTAG 210 48bytes (NT2L1011G0DU)'], + [MfuTagType.NTAG_210u, 'NTAG 210u (micro) 48bytes (NT2L1001G0DU)'], + [MfuTagType.NTAG_212, 'NTAG 212 128bytes (NT2L1211G0DU)'], + [MfuTagType.NTAG_213_C, 'NTAG 213C 144bytes (NT2H1311C1DTL)'], + [MfuTagType.NTAG_213_F, 'NTAG 213F 144bytes (NT2H1311F0DTL)'], + [MfuTagType.NTAG_213_TT, 'NTAG 213TT 144bytes (NT2H1311TTDU)'], + [MfuTagType.NTAG_213, 'NTAG 213 144bytes (NT2H1311G0DU)'], + [MfuTagType.NTAG_215, 'NTAG 215 504bytes (NT2H1511G0DU)'], + [MfuTagType.NTAG_216_F, 'NTAG 216F 888bytes (NT2H1611F0DTL)'], + [MfuTagType.NTAG_216, 'NTAG 216 888bytes (NT2H1611G0DU)'], + [MfuTagType.NTAG_I2C_1K_PLUS, 'NTAG I2C plus 888bytes (NT3H2111FHK)'], + [MfuTagType.NTAG_I2C_1K, 'NTAG I2C 888bytes (NT3H1101FHK)'], + [MfuTagType.NTAG_I2C_2K_PLUS, 'NTAG I2C plus 1912bytes (NT3H2211FHK)'], + [MfuTagType.NTAG_I2C_2K, 'NTAG I2C 1904bytes (NT3H1201FHK)'], + [MfuTagType.NTAG, 'NTAG UNKNOWN'], + [MfuTagType.UL_AES, 'MIFARE Ultralight AES'], + [MfuTagType.UL_C, 'MIFARE Ultralight C (MF0ULC)'], + [MfuTagType.UL_EV1_128, 'MIFARE Ultralight EV1 128bytes (MF0UL2101)'], + [MfuTagType.UL_EV1_48, 'MIFARE Ultralight EV1 48bytes (MF0UL1101)'], + [MfuTagType.UL_EV1, 'MIFARE Ultralight EV1 UNKNOWN'], + [MfuTagType.UL_NANO_40, 'MIFARE Ultralight Nano 40bytes (MF0UNH00)'], + [MfuTagType.UL, 'MIFARE Ultralight (MF0ICU1)'], + [TagType.MF0_ICU1, 'MIFARE Ultralight (MF0ICU1)'], + [TagType.MF0_ICU2, 'MIFARE Ultralight C (MF0ULC)'], + [TagType.MF0_UL11, 'MIFARE Ultralight EV1 48bytes (MF0UL1101)'], + [TagType.MF0_UL21, 'MIFARE Ultralight EV1 128bytes (MF0UL2101)'], + [TagType.NTAG_210, 'NTAG 210 48bytes (NT2L1011G0DU)'], + [TagType.NTAG_212, 'NTAG 212 128bytes (NT2L1211G0DU)'], + [TagType.NTAG_213, 'NTAG 213 144bytes (NT2H1311G0DU)'], + [TagType.NTAG_215, 'NTAG 215 504bytes (NT2H1511G0DU)'], + [TagType.NTAG_216, 'NTAG 216 888bytes (NT2H1611G0DU)'], +]) + +/** + * Get the maximum page number of a specific mifare ultralight tag type. + */ +export const MfuMaxPage = new Map([ + [MfuTagType.FUDAN_UL, 0x10], + [MfuTagType.MY_D_MOVE_LEAN, 0x10], + [MfuTagType.MY_D_MOVE, 0x26], + [MfuTagType.MY_D_NFC, 0x100], + [MfuTagType.MY_D, 0x10], + [MfuTagType.NTAG_203, 0x2A], + [MfuTagType.NTAG_210, 0x14], + [MfuTagType.NTAG_210u, 0x14], + [MfuTagType.NTAG_212, 0x29], + [MfuTagType.NTAG_213_C, 0x2D], + [MfuTagType.NTAG_213_F, 0x2D], + [MfuTagType.NTAG_213_TT, 0x2D], + [MfuTagType.NTAG_213, 0x2D], + [MfuTagType.NTAG_215, 0x87], + [MfuTagType.NTAG_216_F, 0xE7], + [MfuTagType.NTAG_216, 0xE7], + [MfuTagType.NTAG_I2C_1K_PLUS, 0xEA], + [MfuTagType.NTAG_I2C_1K, 0xEA], + [MfuTagType.NTAG_I2C_2K_PLUS, 0xEA], + [MfuTagType.NTAG_I2C_2K, 0xEA], + [MfuTagType.UL_AES, 0x38], + [MfuTagType.UL_C, 0x30], + [MfuTagType.UL_EV1_128, 0x29], + [MfuTagType.UL_EV1_48, 0x14], + [MfuTagType.UL_EV1, 0x14], + [MfuTagType.UL_NANO_40, 0x0B], + [MfuTagType.UL, 0x10], + [TagType.MF0_ICU1, 0x10], + [TagType.MF0_ICU2, 0x30], + [TagType.MF0_UL11, 0x14], + [TagType.MF0_UL21, 0x29], + [TagType.NTAG_210, 0x14], + [TagType.NTAG_212, 0x29], + [TagType.NTAG_213, 0x2D], + [TagType.NTAG_215, 0x87], + [TagType.NTAG_216, 0xE7], +]) diff --git a/src/helper.ts b/src/helper.ts index 365e5b2..51960a1 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -41,7 +41,12 @@ export function middlewareCompose (middlewares: MiddlewareComposeFn[]): (ctx?: R } export async function sleep (ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)) + let timer: NodeJS.Timeout | undefined + await new Promise(resolve => { timer = setTimeout(resolve, ms) }) + if (timer !== undefined) { + clearTimeout(timer) + timer = undefined + } } export function versionCompare (str1: string, str2: string): number { diff --git a/src/index.ts b/src/index.ts index 31f95f4..44de2d9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,9 @@ export { Mf1KeyType, Mf1PrngType, Mf1VblockOperator, + MfuMaxPage, + MfuTagType, + MfuTagTypeName, Slot, TagType, } from './enums' diff --git a/src/plugin/BufferMockAdapter.ts b/src/plugin/BufferMockAdapter.ts index 96d289e..e71664b 100644 --- a/src/plugin/BufferMockAdapter.ts +++ b/src/plugin/BufferMockAdapter.ts @@ -1,6 +1,6 @@ +import { type Buffer } from '@taichunmin/buffer' import _ from 'lodash' import { ReadableStream, WritableStream, type ReadableStreamController } from 'node:stream/web' -import { type Buffer } from '@taichunmin/buffer' import { type ChameleonPlugin, type ChameleonSerialPort, type PluginInstallContext } from '../ChameleonUltra' const ReadableStream1: typeof ReadableStream = (globalThis as any)?.ReadableStream ?? ReadableStream diff --git a/src/plugin/Debug.ts b/src/plugin/Debug.ts index 6259316..f986ffd 100644 --- a/src/plugin/Debug.ts +++ b/src/plugin/Debug.ts @@ -1,7 +1,7 @@ -import _ from 'lodash' import { type Buffer } from '@taichunmin/buffer' -import { type ChameleonPlugin, type PluginInstallContext as ChameleonCtx } from '../ChameleonUltra' import createDebugger, { type Debugger } from 'debug' +import _ from 'lodash' +import { type PluginInstallContext as ChameleonCtx, type ChameleonPlugin } from '../ChameleonUltra' let Buffer1: typeof Buffer diff --git a/src/plugin/DfuZip.ts b/src/plugin/DfuZip.ts index af49430..8594706 100644 --- a/src/plugin/DfuZip.ts +++ b/src/plugin/DfuZip.ts @@ -1,6 +1,6 @@ -import _ from 'lodash' import { Buffer } from '@taichunmin/buffer' import JSZip from 'jszip' +import _ from 'lodash' export default class DfuZip { readonly #buf: Buffer diff --git a/src/plugin/SerialPortAdapter.ts b/src/plugin/SerialPortAdapter.ts index 14ec955..25cce8b 100644 --- a/src/plugin/SerialPortAdapter.ts +++ b/src/plugin/SerialPortAdapter.ts @@ -1,7 +1,7 @@ import _ from 'lodash' -import { Duplex } from 'stream' import { SerialPort } from 'serialport' -import { type ChameleonPlugin, type PluginInstallContext, type ChameleonUltra } from '../ChameleonUltra' +import { Duplex } from 'stream' +import { type ChameleonPlugin, type ChameleonUltra, type PluginInstallContext } from '../ChameleonUltra' async function findDevicePath (): Promise { const device = _.find(await SerialPort.list(), { vendorId: '6868', productId: '8686' }) // ChameleonUltra diff --git a/src/plugin/WebbleAdapter.ts b/src/plugin/WebbleAdapter.ts index 4b2998b..c46f91e 100644 --- a/src/plugin/WebbleAdapter.ts +++ b/src/plugin/WebbleAdapter.ts @@ -1,10 +1,10 @@ +import { type Buffer } from '@taichunmin/buffer' import _ from 'lodash' -import { DfuOp } from '../enums' -import { sleep } from '../helper' import { TransformStream, type UnderlyingSink, WritableStream } from 'node:stream/web' import { type bluetooth } from 'webbluetooth' -import { type Buffer } from '@taichunmin/buffer' -import { type ChameleonPlugin, type ChameleonUltra, type PluginInstallContext, type ChameleonSerialPort } from '../ChameleonUltra' +import { type ChameleonPlugin, type ChameleonSerialPort, type ChameleonUltra, type PluginInstallContext } from '../ChameleonUltra' +import { DfuOp } from '../enums' +import { sleep } from '../helper' const DFU_CTRL_CHAR_UUID = '8ec90001-f315-4f60-9fb8-838830daea50' const DFU_PACKT_CHAR_UUID = '8ec90002-f315-4f60-9fb8-838830daea50' diff --git a/src/plugin/WebserialAdapter.ts b/src/plugin/WebserialAdapter.ts index 300f9de..6a1fb03 100644 --- a/src/plugin/WebserialAdapter.ts +++ b/src/plugin/WebserialAdapter.ts @@ -1,10 +1,10 @@ +import { type Buffer } from '@taichunmin/buffer' import _ from 'lodash' +import { TransformStream, WritableStream, type Transformer, type TransformStreamDefaultController } from 'stream/web' import { serial, type SerialPort } from 'web-serial-polyfill' -import { sleep } from '../helper' -import { type Buffer } from '@taichunmin/buffer' import { type ChameleonPlugin, type ChameleonUltra, type PluginInstallContext } from '../ChameleonUltra' import { type EventEmitter } from '../EventEmitter' -import { TransformStream, type Transformer, type TransformStreamDefaultController, WritableStream } from 'stream/web' +import { sleep } from '../helper' // https://github.com/RfidResearchGroup/ChameleonUltra/blob/main/resource/tools/enter_dfu.py const WEBSERIAL_FILTERS = [ diff --git a/typedoc.json b/typedoc.json index 6bf8496..8f6cfc3 100644 --- a/typedoc.json +++ b/typedoc.json @@ -6,6 +6,7 @@ "includeVersion": true, "out": "dist", "plugin": ["typedoc-plugin-mdn-links", "typedoc-plugin-missing-exports", "typedoc-plugin-rename-defaults"], + "projectDocuments": ["pages/*.md"], "navigationLinks": { "Demos": "https://github.com/taichunmin/chameleon-ultra.js/blob/master/pages/demos.md", "GitHub": "https://github.com/taichunmin/chameleon-ultra.js", diff --git a/yarn.lock b/yarn.lock index bfa2f3b..a7aee7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -321,116 +321,236 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== +"@esbuild/aix-ppc64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz#145b74d5e4a5223489cabdc238d8dad902df5259" + integrity sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ== + "@esbuild/android-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== +"@esbuild/android-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz#453bbe079fc8d364d4c5545069e8260228559832" + integrity sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ== + "@esbuild/android-arm@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== +"@esbuild/android-arm@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.23.0.tgz#26c806853aa4a4f7e683e519cd9d68e201ebcf99" + integrity sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g== + "@esbuild/android-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== +"@esbuild/android-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.23.0.tgz#1e51af9a6ac1f7143769f7ee58df5b274ed202e6" + integrity sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ== + "@esbuild/darwin-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== +"@esbuild/darwin-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz#d996187a606c9534173ebd78c58098a44dd7ef9e" + integrity sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow== + "@esbuild/darwin-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== +"@esbuild/darwin-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz#30c8f28a7ef4e32fe46501434ebe6b0912e9e86c" + integrity sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ== + "@esbuild/freebsd-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== +"@esbuild/freebsd-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz#30f4fcec8167c08a6e8af9fc14b66152232e7fb4" + integrity sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw== + "@esbuild/freebsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== +"@esbuild/freebsd-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz#1003a6668fe1f5d4439e6813e5b09a92981bc79d" + integrity sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ== + "@esbuild/linux-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== +"@esbuild/linux-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz#3b9a56abfb1410bb6c9138790f062587df3e6e3a" + integrity sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw== + "@esbuild/linux-arm@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== +"@esbuild/linux-arm@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz#237a8548e3da2c48cd79ae339a588f03d1889aad" + integrity sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw== + "@esbuild/linux-ia32@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== +"@esbuild/linux-ia32@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz#4269cd19cb2de5de03a7ccfc8855dde3d284a238" + integrity sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA== + "@esbuild/linux-loong64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== +"@esbuild/linux-loong64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz#82b568f5658a52580827cc891cb69d2cb4f86280" + integrity sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A== + "@esbuild/linux-mips64el@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== +"@esbuild/linux-mips64el@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz#9a57386c926262ae9861c929a6023ed9d43f73e5" + integrity sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w== + "@esbuild/linux-ppc64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== +"@esbuild/linux-ppc64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz#f3a79fd636ba0c82285d227eb20ed8e31b4444f6" + integrity sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw== + "@esbuild/linux-riscv64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== +"@esbuild/linux-riscv64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz#f9d2ef8356ce6ce140f76029680558126b74c780" + integrity sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw== + "@esbuild/linux-s390x@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== +"@esbuild/linux-s390x@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz#45390f12e802201f38a0229e216a6aed4351dfe8" + integrity sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg== + "@esbuild/linux-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== +"@esbuild/linux-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz#c8409761996e3f6db29abcf9b05bee8d7d80e910" + integrity sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ== + "@esbuild/netbsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== +"@esbuild/netbsd-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz#ba70db0114380d5f6cfb9003f1d378ce989cd65c" + integrity sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw== + +"@esbuild/openbsd-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz#72fc55f0b189f7a882e3cf23f332370d69dfd5db" + integrity sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ== + "@esbuild/openbsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== +"@esbuild/openbsd-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz#b6ae7a0911c18fe30da3db1d6d17a497a550e5d8" + integrity sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg== + "@esbuild/sunos-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== +"@esbuild/sunos-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz#58f0d5e55b9b21a086bfafaa29f62a3eb3470ad8" + integrity sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA== + "@esbuild/win32-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== +"@esbuild/win32-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz#b858b2432edfad62e945d5c7c9e5ddd0f528ca6d" + integrity sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ== + "@esbuild/win32-ia32@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== +"@esbuild/win32-ia32@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz#167ef6ca22a476c6c0c014a58b4f43ae4b80dec7" + integrity sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA== + "@esbuild/win32-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== +"@esbuild/win32-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz#db44a6a08520b5f25bbe409f34a59f2d4bcc7ced" + integrity sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g== + "@eslint-community/eslint-utils@^4.1.2", "@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" @@ -798,85 +918,85 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@rollup/rollup-android-arm-eabi@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz#bbd0e616b2078cd2d68afc9824d1fadb2f2ffd27" - integrity sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ== - -"@rollup/rollup-android-arm64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz#97255ef6384c5f73f4800c0de91f5f6518e21203" - integrity sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA== - -"@rollup/rollup-darwin-arm64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz#b6dd74e117510dfe94541646067b0545b42ff096" - integrity sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w== - -"@rollup/rollup-darwin-x64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz#e07d76de1cec987673e7f3d48ccb8e106d42c05c" - integrity sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA== - -"@rollup/rollup-linux-arm-gnueabihf@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz#9f1a6d218b560c9d75185af4b8bb42f9f24736b8" - integrity sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA== - -"@rollup/rollup-linux-arm-musleabihf@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz#53618b92e6ffb642c7b620e6e528446511330549" - integrity sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A== - -"@rollup/rollup-linux-arm64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz#99a7ba5e719d4f053761a698f7b52291cefba577" - integrity sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw== - -"@rollup/rollup-linux-arm64-musl@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz#f53db99a45d9bc00ce94db8a35efa7c3c144a58c" - integrity sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ== - -"@rollup/rollup-linux-powerpc64le-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz#cbb0837408fe081ce3435cf3730e090febafc9bf" - integrity sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA== - -"@rollup/rollup-linux-riscv64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz#8ed09c1d1262ada4c38d791a28ae0fea28b80cc9" - integrity sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg== - -"@rollup/rollup-linux-s390x-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz#938138d3c8e0c96f022252a28441dcfb17afd7ec" - integrity sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg== - -"@rollup/rollup-linux-x64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz#1a7481137a54740bee1ded4ae5752450f155d942" - integrity sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w== - -"@rollup/rollup-linux-x64-musl@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz#f1186afc601ac4f4fc25fac4ca15ecbee3a1874d" - integrity sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg== - -"@rollup/rollup-win32-arm64-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz#ed6603e93636a96203c6915be4117245c1bd2daf" - integrity sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA== - -"@rollup/rollup-win32-ia32-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz#14e0b404b1c25ebe6157a15edb9c46959ba74c54" - integrity sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg== - -"@rollup/rollup-win32-x64-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4" - integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g== +"@rollup/rollup-android-arm-eabi@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.0.tgz#3d9fd50164b94964f5de68c3c4ce61933b3a338d" + integrity sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w== + +"@rollup/rollup-android-arm64@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.0.tgz#e1a6d4bca2eb08c84fd996a4bf896ce4b6f4014c" + integrity sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw== + +"@rollup/rollup-darwin-arm64@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.0.tgz#0a3fffea69489a24a96079af414b0be78df8abbc" + integrity sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA== + +"@rollup/rollup-darwin-x64@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.0.tgz#13fbdb15f58f090871b0ffff047ece06ad6ad74c" + integrity sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg== + +"@rollup/rollup-linux-arm-gnueabihf@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.0.tgz#e9d9219ddf6f6e946e2ee322198af12466d2c868" + integrity sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw== + +"@rollup/rollup-linux-arm-musleabihf@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.0.tgz#4ba804a00b5e793196a622f6977e05f23e01f59a" + integrity sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ== + +"@rollup/rollup-linux-arm64-gnu@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.0.tgz#d871e3f41de759a6db27fc99235b782ba47c15cc" + integrity sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug== + +"@rollup/rollup-linux-arm64-musl@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.0.tgz#6e63f7ad4cc51bd2c693a2826fd279de9eaa05b5" + integrity sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ== + +"@rollup/rollup-linux-powerpc64le-gnu@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.0.tgz#1540b284d91c440bc9fa7a1714cfb71a5597e94d" + integrity sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ== + +"@rollup/rollup-linux-riscv64-gnu@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.0.tgz#70ae58103b5bc7ba2e2235738b51d97022c8ef92" + integrity sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg== + +"@rollup/rollup-linux-s390x-gnu@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.0.tgz#579ca5f271421a961d3c73d221202c79e02ff03a" + integrity sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA== + +"@rollup/rollup-linux-x64-gnu@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.0.tgz#f0282d761b8b4e7b92b236813475248e37231849" + integrity sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA== + +"@rollup/rollup-linux-x64-musl@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.0.tgz#65da807ac66c505ad14b76f1e5976006cb67dd5f" + integrity sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A== + +"@rollup/rollup-win32-arm64-msvc@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.0.tgz#1eed24b91f421c2eea8bb7ca8889ba0c867e1780" + integrity sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg== + +"@rollup/rollup-win32-ia32-msvc@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.0.tgz#1ed93c9cdc84e185359797a686f4d1576afcea58" + integrity sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q== + +"@rollup/rollup-win32-x64-msvc@4.19.0": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.0.tgz#baf9b65023ea2ecc5e6ec68f787a0fecfd8ee84c" + integrity sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag== "@serialport/binding-mock@10.2.2": version "10.2.2" @@ -1000,17 +1120,17 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@taichunmin/buffer@^0.13.6": - version "0.13.6" - resolved "https://registry.yarnpkg.com/@taichunmin/buffer/-/buffer-0.13.6.tgz#e75a098498adca2c5b51ca85fc53ac5357633f8b" - integrity sha512-3CM5cD1dkWm8l7VFOL0Etz/wQzjHPMTRWQD/QvtSVBBK5uEEvIaQrD/jII+Q+Jy9S6wZZSzadP70vF3Rpuyu0g== +"@taichunmin/buffer@^0.13.7": + version "0.13.7" + resolved "https://registry.yarnpkg.com/@taichunmin/buffer/-/buffer-0.13.7.tgz#231edb56621d5ef7d94b413d6a44896276df1118" + integrity sha512-fgyoLFHQ54Z3ON1IxXa/64qesq1Vx/dWfN9NWlHpfYTV7KgxTA1rHOQuD9+yT+SQIrasBAXS3bwO4/mXUSGLjw== dependencies: lodash "^4.17.21" -"@taichunmin/crc@^0.0.13": - version "0.0.13" - resolved "https://registry.yarnpkg.com/@taichunmin/crc/-/crc-0.0.13.tgz#0ea897dcf6db9d6a33884c45736e445d427ce419" - integrity sha512-FFCwWq8LOfSP8wLNU9Ct8na1PQGHprM1dkjHStO7DwSZ9224kn/FGbwt1Xp4WQP6a0gKhdCH9HTVdxHhvd00+Q== +"@taichunmin/crc@^0.0.14": + version "0.0.14" + resolved "https://registry.yarnpkg.com/@taichunmin/crc/-/crc-0.0.14.tgz#db39dc70859c60f8b00ea2592d1d327ede453043" + integrity sha512-p48XZOs1glqE1laRl6JPuVMeM2T/iBn+LEtiXHV7sbCoKyuPULLVraB/dSpPn83Cgb0ThPBFIKenz/zPyCpz0A== "@tsconfig/node-lts@^20.1.3": version "20.1.3" @@ -1184,12 +1304,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.3.tgz#5b763b321cd3b80f6b8dde7a37e1a77ff9358dd9" integrity sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA== -"@types/node@^20.14.10": - version "20.14.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.10.tgz#a1a218290f1b6428682e3af044785e5874db469a" - integrity sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ== +"@types/node@^22.0.2": + version "22.0.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.0.2.tgz#9fb1a2b31970871e8bf696f0e8a40d2e6d2bd04e" + integrity sha512-yPL6DyFwY5PiMVEwymNeqUTKsDczQBJ/5T7W/46RwLU/VH+AA8aT5TZkvBviLKLbbm0hlfftEkGrNzfRk/fofQ== dependencies: - undici-types "~5.26.4" + undici-types "~6.11.1" "@types/pug@^2.0.10": version "2.0.10" @@ -1266,16 +1386,16 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^7.16.1": - version "7.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.1.tgz#f5f5da52db674b1f2cdb9d5f3644e5b2ec750465" - integrity sha512-SxdPak/5bO0EnGktV05+Hq8oatjAYVY3Zh2bye9pGZy6+jwyR3LG3YKkV4YatlsgqXP28BTeVm9pqwJM96vf2A== +"@typescript-eslint/eslint-plugin@^7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz#b16d3cf3ee76bf572fdf511e79c248bdec619ea3" + integrity sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "7.16.1" - "@typescript-eslint/type-utils" "7.16.1" - "@typescript-eslint/utils" "7.16.1" - "@typescript-eslint/visitor-keys" "7.16.1" + "@typescript-eslint/scope-manager" "7.18.0" + "@typescript-eslint/type-utils" "7.18.0" + "@typescript-eslint/utils" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" graphemer "^1.4.0" ignore "^5.3.1" natural-compare "^1.4.0" @@ -1292,15 +1412,15 @@ "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" -"@typescript-eslint/parser@^7.16.1": - version "7.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.16.1.tgz#84c581cf86c8b2becd48d33ddc41a6303d57b274" - integrity sha512-u+1Qx86jfGQ5i4JjK33/FnawZRpsLxRnKzGE6EABZ40KxVT/vWsiZFEBBHjFOljmmV3MBYOHEKi0Jm9hbAOClA== +"@typescript-eslint/parser@^7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.18.0.tgz#83928d0f1b7f4afa974098c64b5ce6f9051f96a0" + integrity sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg== dependencies: - "@typescript-eslint/scope-manager" "7.16.1" - "@typescript-eslint/types" "7.16.1" - "@typescript-eslint/typescript-estree" "7.16.1" - "@typescript-eslint/visitor-keys" "7.16.1" + "@typescript-eslint/scope-manager" "7.18.0" + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/typescript-estree" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" debug "^4.3.4" "@typescript-eslint/scope-manager@6.21.0": @@ -1311,21 +1431,21 @@ "@typescript-eslint/types" "6.21.0" "@typescript-eslint/visitor-keys" "6.21.0" -"@typescript-eslint/scope-manager@7.16.1": - version "7.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.16.1.tgz#2b43041caabf8ddd74512b8b550b9fc53ca3afa1" - integrity sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw== +"@typescript-eslint/scope-manager@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz#c928e7a9fc2c0b3ed92ab3112c614d6bd9951c83" + integrity sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA== dependencies: - "@typescript-eslint/types" "7.16.1" - "@typescript-eslint/visitor-keys" "7.16.1" + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" -"@typescript-eslint/type-utils@7.16.1": - version "7.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.16.1.tgz#4d7ae4f3d9e3c8cbdabae91609b1a431de6aa6ca" - integrity sha512-rbu/H2MWXN4SkjIIyWcmYBjlp55VT+1G3duFOIukTNFxr9PI35pLc2ydwAfejCEitCv4uztA07q0QWanOHC7dA== +"@typescript-eslint/type-utils@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz#2165ffaee00b1fbbdd2d40aa85232dab6998f53b" + integrity sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA== dependencies: - "@typescript-eslint/typescript-estree" "7.16.1" - "@typescript-eslint/utils" "7.16.1" + "@typescript-eslint/typescript-estree" "7.18.0" + "@typescript-eslint/utils" "7.18.0" debug "^4.3.4" ts-api-utils "^1.3.0" @@ -1334,10 +1454,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== -"@typescript-eslint/types@7.16.1": - version "7.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.16.1.tgz#bbab066276d18e398bc64067b23f1ce84dfc6d8c" - integrity sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ== +"@typescript-eslint/types@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.18.0.tgz#b90a57ccdea71797ffffa0321e744f379ec838c9" + integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ== "@typescript-eslint/typescript-estree@6.21.0": version "6.21.0" @@ -1353,13 +1473,13 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/typescript-estree@7.16.1": - version "7.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.1.tgz#9b145ba4fd1dde1986697e1ce57dc501a1736dd3" - integrity sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ== +"@typescript-eslint/typescript-estree@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz#b5868d486c51ce8f312309ba79bdb9f331b37931" + integrity sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA== dependencies: - "@typescript-eslint/types" "7.16.1" - "@typescript-eslint/visitor-keys" "7.16.1" + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" @@ -1367,15 +1487,15 @@ semver "^7.6.0" ts-api-utils "^1.3.0" -"@typescript-eslint/utils@7.16.1": - version "7.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.16.1.tgz#df42dc8ca5a4603016fd102db0346cdab415cdb7" - integrity sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA== +"@typescript-eslint/utils@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.18.0.tgz#bca01cde77f95fc6a8d5b0dbcbfb3d6ca4be451f" + integrity sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "7.16.1" - "@typescript-eslint/types" "7.16.1" - "@typescript-eslint/typescript-estree" "7.16.1" + "@typescript-eslint/scope-manager" "7.18.0" + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/typescript-estree" "7.18.0" "@typescript-eslint/visitor-keys@6.21.0": version "6.21.0" @@ -1385,12 +1505,12 @@ "@typescript-eslint/types" "6.21.0" eslint-visitor-keys "^3.4.1" -"@typescript-eslint/visitor-keys@7.16.1": - version "7.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.1.tgz#4287bcf44c34df811ff3bb4d269be6cfc7d8c74b" - integrity sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg== +"@typescript-eslint/visitor-keys@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz#0564629b6124d67607378d0f0332a0495b25e7d7" + integrity sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg== dependencies: - "@typescript-eslint/types" "7.16.1" + "@typescript-eslint/types" "7.18.0" eslint-visitor-keys "^3.4.3" "@ungap/structured-clone@^1.2.0": @@ -1791,14 +1911,14 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bundle-require@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-4.1.0.tgz#3d5fcd19d5160d4cbac5e95ed5a394d1ecd40ce6" - integrity sha512-FeArRFM+ziGkRViKRnSTbHZc35dgmR9yNog05Kn0+ItI59pOAISGvnnIwW1WgFZQW59IxD9QpJnUPkdIPfZuXg== +bundle-require@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-5.0.0.tgz#071521bdea6534495cf23e92a83f889f91729e93" + integrity sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w== dependencies: load-tsconfig "^0.2.3" -cac@^6.7.12: +cac@^6.7.14: version "6.7.14" resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== @@ -1888,7 +2008,7 @@ character-parser@^2.2.0: optionalDependencies: fsevents "~2.3.2" -chokidar@^3.5.1, chokidar@^3.6.0: +chokidar@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -2039,6 +2159,11 @@ confbox@^0.1.7: resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.7.tgz#ccfc0a2bcae36a84838e83a3b7f770fb17d6c579" integrity sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA== +consola@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.2.3.tgz#0741857aa88cfa0d6fd53f1cff0375136e98502f" + integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ== + console-control-strings@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" @@ -2101,10 +2226,10 @@ date-fns@^2.30.0: dependencies: "@babel/runtime" "^7.21.0" -dayjs@^1.11.11: - version "1.11.11" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e" - integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== +dayjs@^1.11.12: + version "1.11.12" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.12.tgz#5245226cc7f40a15bf52e0b99fd2a04669ccac1d" + integrity sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg== debug@2.6.9: version "2.6.9" @@ -2134,6 +2259,13 @@ debug@^4.3.5: dependencies: ms "2.1.2" +debug@^4.3.6: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -2260,7 +2392,7 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -ejs@^3.0.0: +ejs@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== @@ -2398,7 +2530,37 @@ esbuild-plugins-node-modules-polyfill@^1.6.4: local-pkg "^0.5.0" resolve.exports "^2.0.2" -esbuild@^0.21.4, esbuild@~0.21.5: +esbuild@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.23.0.tgz#de06002d48424d9fdb7eb52dbe8e95927f852599" + integrity sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA== + optionalDependencies: + "@esbuild/aix-ppc64" "0.23.0" + "@esbuild/android-arm" "0.23.0" + "@esbuild/android-arm64" "0.23.0" + "@esbuild/android-x64" "0.23.0" + "@esbuild/darwin-arm64" "0.23.0" + "@esbuild/darwin-x64" "0.23.0" + "@esbuild/freebsd-arm64" "0.23.0" + "@esbuild/freebsd-x64" "0.23.0" + "@esbuild/linux-arm" "0.23.0" + "@esbuild/linux-arm64" "0.23.0" + "@esbuild/linux-ia32" "0.23.0" + "@esbuild/linux-loong64" "0.23.0" + "@esbuild/linux-mips64el" "0.23.0" + "@esbuild/linux-ppc64" "0.23.0" + "@esbuild/linux-riscv64" "0.23.0" + "@esbuild/linux-s390x" "0.23.0" + "@esbuild/linux-x64" "0.23.0" + "@esbuild/netbsd-x64" "0.23.0" + "@esbuild/openbsd-arm64" "0.23.0" + "@esbuild/openbsd-x64" "0.23.0" + "@esbuild/sunos-x64" "0.23.0" + "@esbuild/win32-arm64" "0.23.0" + "@esbuild/win32-ia32" "0.23.0" + "@esbuild/win32-x64" "0.23.0" + +esbuild@~0.21.5: version "0.21.5" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== @@ -2523,24 +2685,24 @@ eslint-plugin-local-rules@^3.0.2: resolved "https://registry.yarnpkg.com/eslint-plugin-local-rules/-/eslint-plugin-local-rules-3.0.2.tgz#84c02ea1d604ecb00970779ad27f00738ff361ae" integrity sha512-IWME7GIYHXogTkFsToLdBCQVJ0U4kbSuVyDT+nKoR4UgtnVrrVeNWuAZkdEu1nxkvi9nsPccGehEEF6dgA28IQ== -eslint-plugin-n@^17.9.0: - version "17.9.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.9.0.tgz#91b43d4e10a35e455bfac2c64671f9cecc396590" - integrity sha512-CPSaXDXdrT4nsrOrO4mT4VB6FMUkoySRkHWuuJJHVqsIEjIeZgMY1H7AzSwPbDScikBmLN82KeM1u7ixV7PzGg== +eslint-plugin-n@^17.10.1: + version "17.10.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.10.1.tgz#da2a3fd1a41c9d901bbc06b8c4d4d5916e012913" + integrity sha512-hm/q37W6efDptJXdwirsm6A257iY6ZNtpoSG0wEzFzjJ3AhL7OhEIhdSR2e4OdYfHO5EDeqlCfFrjf9q208IPw== dependencies: "@eslint-community/eslint-utils" "^4.4.0" enhanced-resolve "^5.17.0" eslint-plugin-es-x "^7.5.0" get-tsconfig "^4.7.0" - globals "^15.0.0" + globals "^15.8.0" ignore "^5.2.4" - minimatch "^9.0.0" + minimatch "^9.0.5" semver "^7.5.3" -eslint-plugin-promise@^6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.4.0.tgz#54926d53c79541efe9cea6ac1d823a58bbed1106" - integrity sha512-/KWWRaD3fGkVCZsdR0RU53PSthFmoHVhZl+y9+6DqeDLSikLdlUVpVEAmI6iCRR5QyOjBYBqHZV/bdv4DJ4Gtw== +eslint-plugin-promise@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-7.0.0.tgz#2d54cf6e92213cb2915be62abf338b64c089431d" + integrity sha512-wb1ECT+b90ndBdAujhIdAU8oQ3Vt5gKqP/t78KOmg0ifynrvc2jGR9f6ndbOVNFpKf6jLUBlBBDF3H3Wk0JICg== eslint-plugin-pug@^1.2.5: version "1.2.5" @@ -2660,7 +2822,7 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -execa@^5.0.0: +execa@^5.0.0, execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -3035,10 +3197,10 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" -globals@^15.0.0: - version "15.2.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-15.2.0.tgz#fbcea7f8964a71d8c6e6867ddadb9788ae1083d8" - integrity sha512-FQ5YwCHZM3nCmtb5FzEWwdUc9K5d3V/w9mzcz8iGD1gC/aOTHc6PouYu0kkKipNJqHAT7m51sqzQjEjIP+cK0A== +globals@^15.8.0: + version "15.9.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.9.0.tgz#e9de01771091ffbc37db5714dab484f9f69ff399" + integrity sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA== globalthis@^1.0.3: version "1.0.3" @@ -3047,7 +3209,7 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globby@^11.0.3, globby@^11.1.0: +globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -3896,7 +4058,7 @@ jju@~1.4.0: resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== -joycon@^3.0.1: +joycon@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== @@ -4042,10 +4204,10 @@ lie@~3.3.0: dependencies: immediate "~3.0.5" -lilconfig@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.1.tgz#9d8a246fa753106cfc205fd2d77042faca56e5e3" - integrity sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ== +lilconfig@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb" + integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow== lines-and-columns@^1.1.6: version "1.2.4" @@ -4276,7 +4438,7 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.0, minimatch@^9.0.4: +minimatch@^9.0.4: version "9.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== @@ -4682,6 +4844,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -4708,13 +4875,12 @@ pkg-types@^1.0.3, pkg-types@^1.1.0: mlly "^1.7.0" pathe "^1.1.2" -postcss-load-config@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" - integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== +postcss-load-config@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz#6fd7dcd8ae89badcf1b2d644489cbabf83aa8096" + integrity sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g== dependencies: - lilconfig "^3.0.0" - yaml "^2.3.4" + lilconfig "^3.1.1" prebuild-install@^7.1.1: version "7.1.1" @@ -5057,29 +5223,29 @@ rimraf@^6.0.1: glob "^11.0.0" package-json-from-dist "^1.0.0" -rollup@^4.0.2: - version "4.18.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.18.0.tgz#497f60f0c5308e4602cf41136339fbf87d5f5dda" - integrity sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg== +rollup@^4.19.0: + version "4.19.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.19.0.tgz#83b08cc0b2bc38c26c194cb7f2cdabd84a2a8c02" + integrity sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA== dependencies: "@types/estree" "1.0.5" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.18.0" - "@rollup/rollup-android-arm64" "4.18.0" - "@rollup/rollup-darwin-arm64" "4.18.0" - "@rollup/rollup-darwin-x64" "4.18.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.18.0" - "@rollup/rollup-linux-arm-musleabihf" "4.18.0" - "@rollup/rollup-linux-arm64-gnu" "4.18.0" - "@rollup/rollup-linux-arm64-musl" "4.18.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.18.0" - "@rollup/rollup-linux-riscv64-gnu" "4.18.0" - "@rollup/rollup-linux-s390x-gnu" "4.18.0" - "@rollup/rollup-linux-x64-gnu" "4.18.0" - "@rollup/rollup-linux-x64-musl" "4.18.0" - "@rollup/rollup-win32-arm64-msvc" "4.18.0" - "@rollup/rollup-win32-ia32-msvc" "4.18.0" - "@rollup/rollup-win32-x64-msvc" "4.18.0" + "@rollup/rollup-android-arm-eabi" "4.19.0" + "@rollup/rollup-android-arm64" "4.19.0" + "@rollup/rollup-darwin-arm64" "4.19.0" + "@rollup/rollup-darwin-x64" "4.19.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.19.0" + "@rollup/rollup-linux-arm-musleabihf" "4.19.0" + "@rollup/rollup-linux-arm64-gnu" "4.19.0" + "@rollup/rollup-linux-arm64-musl" "4.19.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.19.0" + "@rollup/rollup-linux-riscv64-gnu" "4.19.0" + "@rollup/rollup-linux-s390x-gnu" "4.19.0" + "@rollup/rollup-linux-x64-gnu" "4.19.0" + "@rollup/rollup-linux-x64-musl" "4.19.0" + "@rollup/rollup-win32-arm64-msvc" "4.19.0" + "@rollup/rollup-win32-ia32-msvc" "4.19.0" + "@rollup/rollup-win32-x64-msvc" "4.19.0" fsevents "~2.3.2" run-parallel@^1.1.9: @@ -5468,7 +5634,7 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -sucrase@^3.20.3: +sucrase@^3.35.0: version "3.35.0" resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== @@ -5634,13 +5800,13 @@ ts-interface-checker@^0.1.9: resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== -ts-jest@^29.2.2: - version "29.2.2" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.2.tgz#0d2387bb04d39174b20a05172a968f258aedff4d" - integrity sha512-sSW7OooaKT34AAngP6k1VS669a0HdLxkQZnlC7T76sckGCokXFnvJ3yRlQZGRTAoV5K19HfSgCiSwWOSIfcYlg== +ts-jest@^29.2.4: + version "29.2.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.4.tgz#38ccf487407d7a63054a72689f6f99b075e296e5" + integrity sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw== dependencies: bs-logger "0.x" - ejs "^3.0.0" + ejs "^3.1.10" fast-json-stable-stringify "2.x" jest-util "^29.0.0" json5 "^2.2.3" @@ -5683,30 +5849,32 @@ tslib@^2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tsup@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/tsup/-/tsup-8.1.0.tgz#354ce9def1721f5029564382ea2a42dc67fbb489" - integrity sha512-UFdfCAXukax+U6KzeTNO2kAARHcWxmKsnvSPXUcfA1D+kU05XDccCrkffCQpFaWDsZfV0jMyTsxU39VfCp6EOg== - dependencies: - bundle-require "^4.0.0" - cac "^6.7.12" - chokidar "^3.5.1" - debug "^4.3.1" - esbuild "^0.21.4" - execa "^5.0.0" - globby "^11.0.3" - joycon "^3.0.1" - postcss-load-config "^4.0.1" +tsup@^8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/tsup/-/tsup-8.2.3.tgz#4a1ff2962a4d7c8265fea661b0dd9668de58916d" + integrity sha512-6YNT44oUfXRbZuSMNmN36GzwPPIlD2wBccY7looM2fkTcxkf2NEmwr3OZuDZoySklnrIG4hoEtzy8yUXYOqNcg== + dependencies: + bundle-require "^5.0.0" + cac "^6.7.14" + chokidar "^3.6.0" + consola "^3.2.3" + debug "^4.3.5" + esbuild "^0.23.0" + execa "^5.1.1" + globby "^11.1.0" + joycon "^3.1.1" + picocolors "^1.0.1" + postcss-load-config "^6.0.1" resolve-from "^5.0.0" - rollup "^4.0.2" + rollup "^4.19.0" source-map "0.8.0-beta.0" - sucrase "^3.20.3" + sucrase "^3.35.0" tree-kill "^1.2.2" -tsx@^4.16.2: - version "4.16.2" - resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.16.2.tgz#8722be119ae226ef0b4c6210d5ee90f3ba823f19" - integrity sha512-C1uWweJDgdtX2x600HjaFaucXTilT7tgUZHbOE4+ypskZ1OP8CRCSDkCxG6Vya9EwaFIVagWwpaVAn5wzypaqQ== +tsx@^4.16.5: + version "4.16.5" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.16.5.tgz#49c2a8f4d4d66bd7cf538e23e7368a1919a9a1ca" + integrity sha512-ArsiAQHEW2iGaqZ8fTA1nX0a+lN5mNTyuGRRO6OW3H/Yno1y9/t1f9YOI1Cfoqz63VAthn++ZYcbDP7jPflc+A== dependencies: esbuild "~0.21.5" get-tsconfig "^4.7.5" @@ -5781,10 +5949,10 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typedoc-plugin-mdn-links@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-3.2.4.tgz#343f0c08e29de876389c5259017bb7d03a269491" - integrity sha512-TmBBmtIuTR4mSuYjcmiibjvmpM2oSeUC0E5cgPnrM3HEdhiXsDGNA6SyPorvauqYN5Hd0si2EJLJs6KjP9b4Lw== +typedoc-plugin-mdn-links@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-3.2.6.tgz#7b89ea8695dd8931733921607f3730a1ac358a3e" + integrity sha512-Z4znE5EyzSnRpQCQ/XERVEyN8V6Gm6z6/fK0Hgb2NgagGztD8Y/sGyXnYY43F5jJvZyIZCnfKDd8KXJ21ZYLvg== typedoc-plugin-missing-exports@^3.0.0: version "3.0.0" @@ -5803,10 +5971,10 @@ typedoc-plugin-zod@^1.2.0: resolved "https://registry.yarnpkg.com/typedoc-plugin-zod/-/typedoc-plugin-zod-1.2.0.tgz#7273faefb1acbfcbe842a3497052d07f8fb25d26" integrity sha512-eFvPQ4iJsoru4HJP8z7F0NhRxPBjgVPoM0j/xipB7nkGLYhfUjEOlABayaTdBiMocPNCXOCBjFWBHEJGBiMRdw== -typedoc@^0.26.4: - version "0.26.4" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.26.4.tgz#7e83047369a29a710d429dac20996680cae9a314" - integrity sha512-FlW6HpvULDKgc3rK04V+nbFyXogPV88hurarDPOjuuB5HAwuAlrCMQ5NeH7Zt68a/ikOKu6Z/0hFXAeC9xPccQ== +typedoc@^0.26.5: + version "0.26.5" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.26.5.tgz#08032bd57cac3d56e8ac296a07e3482dc0c645ac" + integrity sha512-Vn9YKdjKtDZqSk+by7beZ+xzkkr8T8CYoiasqyt4TTRFy5+UHzL/mF/o4wGBjRF+rlWQHDb0t6xCpA3JNL5phg== dependencies: lunr "^2.3.9" markdown-it "^14.1.0" @@ -5814,10 +5982,10 @@ typedoc@^0.26.4: shiki "^1.9.1" yaml "^2.4.5" -typescript@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa" - integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ== +typescript@^5.5.4: + version "5.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== uc.micro@^2.0.0, uc.micro@^2.1.0: version "2.1.0" @@ -5849,10 +6017,10 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.11.1: + version "6.11.1" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.11.1.tgz#432ea6e8efd54a48569705a699e62d8f4981b197" + integrity sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ== universalify@^2.0.0: version "2.0.0" @@ -6060,11 +6228,6 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^2.3.4: - version "2.4.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.2.tgz#7a2b30f2243a5fc299e1f14ca58d475ed4bc5362" - integrity sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA== - yaml@^2.4.5: version "2.4.5" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e"