├── .nvmrc ├── .prettierrc ├── .prettierignore ├── .yarnrc.yml ├── .mocharc.yml ├── lib ├── errors.ts ├── index.ts ├── commands.ts ├── config.ts ├── util.ts ├── messages.ts ├── services.ts ├── types.ts ├── store.ts ├── socket.ts ├── entities.ts ├── collection.ts ├── auth.ts └── connection.ts ├── .github ├── release-drafter.yml ├── workflows │ ├── release-drafter.yaml │ ├── ci.yml │ ├── npmpublish.yml │ └── publish-pages.yml └── dependabot.yml ├── .gitignore ├── rollup.config.js ├── .eslintrc ├── tsconfig.json ├── test ├── auth.spec.ts ├── config.spec.ts ├── util.ts ├── entities.spec.ts └── services.spec.ts ├── package.json ├── CLA.md ├── index.html ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md └── yarn.lock /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/iron 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false 4 | } 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | yarn-error.log 3 | LICENSE.md 4 | CLA.md 5 | CODE_OF_CONDUCT.md 6 | .reify-cache 7 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: false 4 | 5 | nodeLinker: node-modules 6 | 7 | yarnPath: .yarn/releases/yarn-4.7.0.cjs 8 | -------------------------------------------------------------------------------- /.mocharc.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/mochajs/mocha/blob/master/example/config/.mocharc.yml 2 | timeout: 100 3 | watch-files: 4 | - "lib/*.ts" 5 | - "test/*.ts" 6 | spec: test/*.spec.ts 7 | -------------------------------------------------------------------------------- /lib/errors.ts: -------------------------------------------------------------------------------- 1 | export const ERR_CANNOT_CONNECT = 1; 2 | export const ERR_INVALID_AUTH = 2; 3 | export const ERR_CONNECTION_LOST = 3; 4 | export const ERR_HASS_HOST_REQUIRED = 4; 5 | export const ERR_INVALID_HTTPS_TO_HTTP = 5; 6 | export const ERR_INVALID_AUTH_CALLBACK = 6; 7 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | categories: 2 | - title: "Breaking Changes" 3 | labels: 4 | - "breaking change" 5 | - title: "Dependencies" 6 | collapse-after: 1 7 | labels: 8 | - "dependencies" 9 | template: | 10 | ## What's Changed 11 | 12 | $CHANGES 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .reify-cache 3 | .rpt2_cache 4 | .rts2_cache* 5 | .vscode/settings.json 6 | 7 | # yarn 8 | .yarn/* 9 | !.yarn/patches 10 | !.yarn/releases 11 | !.yarn/plugins 12 | !.yarn/sdks 13 | !.yarn/versions 14 | .pnp.* 15 | /node_modules/ 16 | yarn-error.log 17 | npm-debug.log -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | input: "dist/index.js", 3 | output: [ 4 | { 5 | file: "dist/haws.cjs", 6 | format: "cjs", 7 | }, 8 | { 9 | file: "dist/haws.umd.js", 10 | format: "umd", 11 | name: "HAWS", 12 | }, 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yaml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | update_release_draft: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "env": { 4 | "browser": true 5 | }, 6 | "rules": { 7 | "no-plusplus": 0, 8 | "comma-dangle": 0, 9 | "no-param-reassign": 0, 10 | "no-underscore-dangle": 0, 11 | "func-names": 0, 12 | "no-multi-assign": 0, 13 | "prefer-destructuring": 0, 14 | "import/extensions": 0, 15 | "no-restricted-globals": 0, 16 | "import/prefer-default-export": 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["mocha"], 4 | "lib": ["es2015", "dom"], 5 | "target": "es2017", 6 | "outDir": "dist", 7 | "declaration": true, 8 | "noFallthroughCasesInSwitch": true, 9 | "noImplicitReturns": true, 10 | "noUnusedLocals": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "strict": true, 13 | "module": "ES2015", 14 | "moduleResolution": "Bundler" 15 | }, 16 | "include": ["lib/*"] 17 | } 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Basic dependabot.yml file with 2 | # minimum configuration for two package managers 3 | 4 | version: 2 5 | updates: 6 | # Enable version updates for npm 7 | - package-ecosystem: "npm" 8 | # Look for `package.json` and `lock` files in the `root` directory 9 | directory: "/" 10 | # Check the npm registry for updates every day (weekdays) 11 | schedule: 12 | interval: "weekly" 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "weekly" 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: CI 5 | 6 | on: 7 | push: 8 | branches: [master] 9 | pull_request: 10 | branches: [master] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 18 | - name: Use Node.js 19 | uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 20 | with: 21 | node-version-file: ".nvmrc" 22 | - run: yarn install --immutable 23 | - run: yarn test 24 | -------------------------------------------------------------------------------- /test/auth.spec.ts: -------------------------------------------------------------------------------- 1 | import { strictEqual } from "assert"; 2 | 3 | import { Auth } from "../dist/auth.js"; 4 | 5 | describe("Auth", () => { 6 | it("should indicate correctly when token expired", () => { 7 | const auth = new Auth({ 8 | hassUrl: "", 9 | clientId: "", 10 | refresh_token: "", 11 | access_token: "", 12 | expires_in: 3000, 13 | expires: Date.now() - 1000, 14 | }); 15 | strictEqual(auth.expired, true); 16 | }); 17 | it("should indicate correctly when token not expired", () => { 18 | const auth = new Auth({ 19 | hassUrl: "", 20 | clientId: "", 21 | refresh_token: "", 22 | access_token: "", 23 | expires_in: 3000, 24 | expires: Date.now() + 1000, 25 | }); 26 | strictEqual(auth.expired, false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | // JS extensions in imports allow tsc output to be consumed by browsers. 2 | import { createSocket } from "./socket.js"; 3 | import { Connection, ConnectionOptions } from "./connection.js"; 4 | 5 | export * from "./auth.js"; 6 | export * from "./collection.js"; 7 | export * from "./connection.js"; 8 | export * from "./config.js"; 9 | export * from "./services.js"; 10 | export * from "./entities.js"; 11 | export * from "./errors.js"; 12 | export * from "./socket.js"; 13 | export * from "./types.js"; 14 | export * from "./commands.js"; 15 | export * from "./store.js"; 16 | 17 | export async function createConnection(options?: Partial) { 18 | const connOptions: ConnectionOptions = { 19 | setupRetry: 0, 20 | createSocket, 21 | ...options, 22 | }; 23 | 24 | const socket = await connOptions.createSocket(connOptions); 25 | const conn = new Connection(socket, connOptions); 26 | return conn; 27 | } 28 | -------------------------------------------------------------------------------- /lib/commands.ts: -------------------------------------------------------------------------------- 1 | import { Connection } from "./connection.js"; 2 | import * as messages from "./messages.js"; 3 | import { 4 | HassEntity, 5 | HassServices, 6 | HassConfig, 7 | HassUser, 8 | HassServiceTarget, 9 | } from "./types.js"; 10 | 11 | export const getStates = (connection: Connection) => 12 | connection.sendMessagePromise(messages.states()); 13 | 14 | export const getServices = (connection: Connection) => 15 | connection.sendMessagePromise(messages.services()); 16 | 17 | export const getConfig = (connection: Connection) => 18 | connection.sendMessagePromise(messages.config()); 19 | 20 | export const getUser = (connection: Connection) => 21 | connection.sendMessagePromise(messages.user()); 22 | 23 | export const callService = ( 24 | connection: Connection, 25 | domain: string, 26 | service: string, 27 | serviceData?: object, 28 | target?: HassServiceTarget, 29 | returnResponse?: boolean, 30 | ) => 31 | connection.sendMessagePromise( 32 | messages.callService(domain, service, serviceData, target, returnResponse), 33 | ); 34 | -------------------------------------------------------------------------------- /.github/workflows/npmpublish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [published] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 15 | - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 16 | with: 17 | node-version-file: ".nvmrc" 18 | - run: yarn install --immutable 19 | - run: yarn test 20 | 21 | publish-npm: 22 | needs: build 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5 26 | - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v5 27 | with: 28 | node-version: 18 29 | registry-url: https://registry.npmjs.org/ 30 | - run: yarn install --immutable 31 | - run: npm publish 32 | env: 33 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 34 | -------------------------------------------------------------------------------- /test/config.spec.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | 3 | import { subscribeConfig } from "../dist/config.js"; 4 | import { MockConnection, AwaitableEvent } from "./util.js"; 5 | 6 | const MOCK_CONFIG = { 7 | hello: "bla", 8 | components: ["frontend"], 9 | }; 10 | 11 | describe("subscribeConfig", () => { 12 | let conn: MockConnection; 13 | let awaitableEvent: AwaitableEvent; 14 | 15 | beforeEach(() => { 16 | conn = new MockConnection(); 17 | conn.mockResponse("get_config", MOCK_CONFIG); 18 | awaitableEvent = new AwaitableEvent(); 19 | }); 20 | 21 | it("should load initial config", async () => { 22 | awaitableEvent.prime(); 23 | subscribeConfig(conn, awaitableEvent.set); 24 | 25 | const config = await awaitableEvent.wait(); 26 | 27 | assert.deepStrictEqual(config, MOCK_CONFIG); 28 | }); 29 | 30 | it("should handle component loaded events", async () => { 31 | subscribeConfig(conn, awaitableEvent.set); 32 | 33 | // We need to sleep to have it process the first full load 34 | await 0; 35 | 36 | awaitableEvent.prime(); 37 | 38 | conn.mockEvent("component_loaded", { 39 | data: { 40 | component: "api", 41 | }, 42 | }); 43 | 44 | const config = await awaitableEvent.wait(); 45 | 46 | assert.deepEqual(config, { 47 | hello: "bla", 48 | components: ["frontend", "api"], 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /.github/workflows/publish-pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | # Add permissions 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | jobs: 15 | build: # Renamed from build-and-deploy for clarity 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 20 | 21 | - name: Set up Node.js 22 | uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 23 | with: 24 | node-version-file: ".nvmrc" # Read version from .nvmrc 25 | 26 | - name: Install dependencies 27 | run: yarn install --frozen-lockfile 28 | 29 | - name: Build 30 | run: yarn build 31 | 32 | - name: Prepare artifact for GitHub Pages 33 | run: | 34 | mkdir ./gh-pages-artifact 35 | cp ./index.html ./gh-pages-artifact/ 36 | cp -r ./dist ./gh-pages-artifact/ 37 | 38 | - name: Upload artifact 39 | uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0 40 | with: 41 | # Upload the specific directory 42 | path: "./gh-pages-artifact" 43 | 44 | deploy: 45 | needs: build 46 | runs-on: ubuntu-latest 47 | environment: 48 | name: github-pages 49 | url: ${{ steps.deployment.outputs.page_url }} 50 | steps: 51 | - name: Deploy to GitHub Pages 52 | id: deployment 53 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 54 | -------------------------------------------------------------------------------- /lib/config.ts: -------------------------------------------------------------------------------- 1 | import { getCollection } from "./collection.js"; 2 | import { HassConfig, UnsubscribeFunc } from "./types.js"; 3 | import { Connection } from "./connection.js"; 4 | import { Store } from "./store.js"; 5 | import { getConfig } from "./commands.js"; 6 | 7 | type ComponentLoadedEvent = { 8 | data: { 9 | component: string; 10 | }; 11 | }; 12 | 13 | function processComponentLoaded( 14 | state: HassConfig, 15 | event: ComponentLoadedEvent, 16 | ): Partial | null { 17 | if (state === undefined) return null; 18 | 19 | return { 20 | components: state.components.concat(event.data.component), 21 | }; 22 | } 23 | 24 | const fetchConfig = (conn: Connection) => getConfig(conn); 25 | const subscribeUpdates = (conn: Connection, store: Store) => 26 | Promise.all([ 27 | conn.subscribeEvents( 28 | store.action(processComponentLoaded), 29 | "component_loaded", 30 | ), 31 | conn.subscribeEvents( 32 | () => fetchConfig(conn).then((config) => store.setState(config, true)), 33 | "core_config_updated", 34 | ), 35 | ]).then((unsubs) => () => unsubs.forEach((unsub) => unsub())); 36 | 37 | export const configColl = (conn: Connection) => 38 | getCollection(conn, "_cnf", fetchConfig, subscribeUpdates); 39 | 40 | export const subscribeConfig = ( 41 | conn: Connection, 42 | onChange: (state: HassConfig) => void, 43 | ): UnsubscribeFunc => configColl(conn).subscribe(onChange); 44 | 45 | export const STATE_NOT_RUNNING = "NOT_RUNNING"; 46 | export const STATE_STARTING = "STARTING"; 47 | export const STATE_RUNNING = "RUNNING"; 48 | export const STATE_STOPPING = "STOPPING"; 49 | export const STATE_FINAL_WRITE = "FINAL_WRITE"; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "home-assistant-js-websocket", 3 | "type": "module", 4 | "sideEffects": false, 5 | "version": "9.6.0", 6 | "description": "Home Assistant websocket client", 7 | "source": "lib/index.ts", 8 | "types": "dist/index.d.ts", 9 | "main": "dist/haws.umd.js", 10 | "module": "dist/index.js", 11 | "exports": { 12 | ".": { 13 | "import": "./dist/index.js", 14 | "require": "./dist/haws.cjs", 15 | "default": "./dist/haws.umd.js" 16 | }, 17 | "./haws.cjs": "./dist/haws.cjs", 18 | "./haws.umd.js": "./dist/haws.umd.js", 19 | "./dist/*": "./dist/*" 20 | }, 21 | "repository": { 22 | "url": "https://github.com/home-assistant/home-assistant-js-websocket.git", 23 | "type": "git" 24 | }, 25 | "scripts": { 26 | "watch": "tsc --watch", 27 | "build": "tsc && rollup -c", 28 | "test": "tsc && prettier --check . && mocha --loader=ts-node/esm", 29 | "format": "prettier --write .", 30 | "prepublishOnly": "rm -rf dist && yarn build && yarn test" 31 | }, 32 | "author": "Paulus Schoutsen ", 33 | "license": "Apache-2.0", 34 | "devDependencies": { 35 | "@types/assert": "^1.4.7", 36 | "@types/mocha": "^10.0.9", 37 | "assert": "^2.0.0", 38 | "husky": "^4.2.5", 39 | "lint-staged": "^16.0.0", 40 | "mocha": "^11.1.0", 41 | "prettier": "^3.0.0", 42 | "rollup": "^4.3.0", 43 | "ts-node": "^10.9.2", 44 | "typescript": "^5.2.2" 45 | }, 46 | "files": [ 47 | "dist" 48 | ], 49 | "husky": { 50 | "hooks": { 51 | "pre-commit": "lint-staged" 52 | } 53 | }, 54 | "lint-staged": { 55 | "*.{ts,js,json,css,md}": [ 56 | "prettier --write" 57 | ] 58 | }, 59 | "packageManager": "yarn@4.7.0" 60 | } 61 | -------------------------------------------------------------------------------- /CLA.md: -------------------------------------------------------------------------------- 1 | # Contributor License Agreement 2 | 3 | ``` 4 | By making a contribution to this project, I certify that: 5 | 6 | (a) The contribution was created in whole or in part by me and I 7 | have the right to submit it under the Apache 2.0 license; or 8 | 9 | (b) The contribution is based upon previous work that, to the best 10 | of my knowledge, is covered under an appropriate open source 11 | license and I have the right under that license to submit that 12 | work with modifications, whether created in whole or in part 13 | by me, under the Apache 2.0 license; or 14 | 15 | (c) The contribution was provided directly to me by some other 16 | person who certified (a), (b) or (c) and I have not modified 17 | it. 18 | 19 | (d) I understand and agree that this project and the contribution 20 | are public and that a record of the contribution (including all 21 | personal information I submit with it) is maintained indefinitely 22 | and may be redistributed consistent with this project or the open 23 | source license(s) involved. 24 | ``` 25 | 26 | ## Attribution 27 | 28 | The text of this license is available under the [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/). It is based on the Linux [Developer Certificate Of Origin](http://elinux.org/Developer_Certificate_Of_Origin), but is modified to explicitly use the Apache 2.0 license 29 | and not mention sign-off. 30 | 31 | ## Signing 32 | 33 | To sign this CLA you must first submit a pull request to a repository under the Home Assistant organization. 34 | 35 | ## Adoption 36 | 37 | This Contributor License Agreement (CLA) was first announced on January 21st, 2017 in [this][cla-blog] blog post and adopted January 28th, 2017. 38 | 39 | [cla-blog]: https://home-assistant.io/blog/2017/01/21/home-assistant-governance/ 40 | -------------------------------------------------------------------------------- /lib/util.ts: -------------------------------------------------------------------------------- 1 | export function parseQuery(queryString: string) { 2 | const query: any = {}; 3 | const items = queryString.split("&"); 4 | for (let i = 0; i < items.length; i++) { 5 | const item = items[i].split("="); 6 | const key = decodeURIComponent(item[0]); 7 | const value = item.length > 1 ? decodeURIComponent(item[1]) : undefined; 8 | query[key] = value; 9 | } 10 | return query as T; 11 | } 12 | 13 | // From: https://davidwalsh.name/javascript-debounce-function 14 | 15 | // Returns a function, that, as long as it continues to be invoked, will not 16 | // be triggered. The function will be called after it stops being called for 17 | // N milliseconds. If `immediate` is passed, trigger the function on the 18 | // leading edge, instead of the trailing. 19 | // eslint-disable-next-line: ban-types 20 | export const debounce = unknown>( 21 | func: T, 22 | wait: number, 23 | immediate = false, 24 | ): T => { 25 | let timeout: number | undefined; 26 | // @ts-ignore 27 | return function (...args) { 28 | // @ts-ignore 29 | const context = this; 30 | const later = () => { 31 | timeout = undefined; 32 | if (!immediate) { 33 | func.apply(context, args); 34 | } 35 | }; 36 | const callNow = immediate && !timeout; 37 | clearTimeout(timeout); 38 | timeout = setTimeout(later, wait); 39 | if (callNow) { 40 | func.apply(context, args); 41 | } 42 | }; 43 | }; 44 | 45 | export const atLeastHaVersion = ( 46 | version: string, 47 | major: number, 48 | minor: number, 49 | patch?: number, 50 | ): boolean => { 51 | const [haMajor, haMinor, haPatch] = version.split(".", 3); 52 | 53 | return ( 54 | Number(haMajor) > major || 55 | (Number(haMajor) === major && 56 | (patch === undefined 57 | ? Number(haMinor) >= minor 58 | : Number(haMinor) > minor)) || 59 | (patch !== undefined && 60 | Number(haMajor) === major && 61 | Number(haMinor) === minor && 62 | Number(haPatch) >= patch) 63 | ); 64 | }; 65 | -------------------------------------------------------------------------------- /test/util.ts: -------------------------------------------------------------------------------- 1 | import { Connection } from "../dist/connection.js"; 2 | import { HaWebSocket } from "../dist/socket.js"; 3 | 4 | class MockWebSocket { 5 | addEventListener(eventType: string, callback: () => {}) {} 6 | removeEventListener(eventType: string, callback: () => {}) {} 7 | send(message: string) {} 8 | close() {} 9 | } 10 | 11 | export class MockConnection extends Connection { 12 | private _mockListeners: { [event: string]: ((data: any) => void)[] }; 13 | private _mockResponses: {}; 14 | 15 | constructor() { 16 | super(new MockWebSocket() as HaWebSocket, {} as any); 17 | this._mockListeners = {}; 18 | this._mockResponses = {}; 19 | } 20 | 21 | // hass events 22 | async subscribeEvents( 23 | eventCallback: (ev: EventType) => void, 24 | eventType?: string, 25 | ) { 26 | if (!eventType) { 27 | throw new Error("mock all events not implemented"); 28 | } 29 | if (!(eventType in this._mockListeners)) { 30 | this._mockListeners[eventType] = []; 31 | } 32 | this._mockListeners[eventType].push(eventCallback); 33 | return () => Promise.resolve(); 34 | } 35 | 36 | mockEvent(event: any, data: any) { 37 | this._mockListeners[event].forEach((cb) => cb(data)); 38 | } 39 | 40 | mockResponse(type: any, data: any) { 41 | // @ts-ignore 42 | this._mockResponses[type] = data; 43 | } 44 | 45 | async sendMessagePromise(message: any) { 46 | if (message.type in this._mockResponses) { 47 | // @ts-ignore 48 | return this._mockResponses[message.type]; 49 | } 50 | throw new Error("Unexpected type"); 51 | } 52 | } 53 | 54 | export class AwaitableEvent { 55 | curPromise?: Promise; 56 | curResolve?: (...args: any[]) => void; 57 | 58 | constructor() { 59 | this.set = this.set.bind(this); 60 | } 61 | 62 | set(...args: any[]): void { 63 | if (this.curResolve) this.curResolve(...args); 64 | } 65 | 66 | prime(): void { 67 | this.curPromise = new Promise((resolve) => { 68 | this.curResolve = resolve; 69 | }); 70 | } 71 | 72 | wait() { 73 | return this.curPromise; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/messages.ts: -------------------------------------------------------------------------------- 1 | import { Error, HassServiceTarget } from "./types.js"; 2 | 3 | export function auth(accessToken: string) { 4 | return { 5 | type: "auth", 6 | access_token: accessToken, 7 | }; 8 | } 9 | 10 | export function supportedFeatures() { 11 | return { 12 | type: "supported_features", 13 | id: 1, // Always the first message after auth 14 | features: { coalesce_messages: 1 }, 15 | }; 16 | } 17 | 18 | export function states() { 19 | return { 20 | type: "get_states", 21 | }; 22 | } 23 | 24 | export function config() { 25 | return { 26 | type: "get_config", 27 | }; 28 | } 29 | 30 | export function services() { 31 | return { 32 | type: "get_services", 33 | }; 34 | } 35 | 36 | export function user() { 37 | return { 38 | type: "auth/current_user", 39 | }; 40 | } 41 | 42 | type ServiceCallMessage = { 43 | type: "call_service"; 44 | domain: string; 45 | service: string; 46 | service_data?: object; 47 | target?: HassServiceTarget; 48 | return_response?: boolean; 49 | }; 50 | 51 | export function callService( 52 | domain: string, 53 | service: string, 54 | serviceData?: object, 55 | target?: HassServiceTarget, 56 | returnResponse?: boolean, 57 | ) { 58 | const message: ServiceCallMessage = { 59 | type: "call_service", 60 | domain, 61 | service, 62 | target, 63 | return_response: returnResponse, 64 | }; 65 | 66 | if (serviceData) { 67 | message.service_data = serviceData; 68 | } 69 | 70 | return message; 71 | } 72 | 73 | type SubscribeEventMessage = { 74 | type: "subscribe_events"; 75 | event_type?: string; 76 | }; 77 | 78 | export function subscribeEvents(eventType?: string) { 79 | const message: SubscribeEventMessage = { 80 | type: "subscribe_events", 81 | }; 82 | 83 | if (eventType) { 84 | message.event_type = eventType; 85 | } 86 | 87 | return message; 88 | } 89 | 90 | export function unsubscribeEvents(subscription: number) { 91 | return { 92 | type: "unsubscribe_events", 93 | subscription, 94 | }; 95 | } 96 | 97 | export function ping() { 98 | return { 99 | type: "ping", 100 | }; 101 | } 102 | 103 | export function error(code: Error, message: string) { 104 | return { 105 | type: "result", 106 | success: false, 107 | error: { 108 | code, 109 | message, 110 | }, 111 | }; 112 | } 113 | -------------------------------------------------------------------------------- /lib/services.ts: -------------------------------------------------------------------------------- 1 | import { getCollection } from "./collection.js"; 2 | import { HassServices, HassDomainServices, UnsubscribeFunc } from "./types.js"; 3 | import { Connection } from "./connection.js"; 4 | import { Store } from "./store.js"; 5 | import { getServices } from "./commands.js"; 6 | import { debounce } from "./util.js"; 7 | 8 | type ServiceRegisteredEvent = { 9 | data: { 10 | domain: string; 11 | service: string; 12 | }; 13 | }; 14 | 15 | type ServiceRemovedEvent = { 16 | data: { 17 | domain: string; 18 | service: string; 19 | }; 20 | }; 21 | 22 | function processServiceRegistered( 23 | conn: Connection, 24 | store: Store, 25 | event: ServiceRegisteredEvent, 26 | ) { 27 | const state = store.state; 28 | if (state === undefined) return; 29 | 30 | const { domain, service } = event.data; 31 | 32 | if (!state.domain?.service) { 33 | const domainInfo = { 34 | ...state[domain], 35 | [service]: { description: "", fields: {} }, 36 | }; 37 | store.setState({ [domain]: domainInfo }); 38 | } 39 | debouncedFetchServices(conn, store); 40 | } 41 | 42 | function processServiceRemoved( 43 | state: HassServices, 44 | event: ServiceRemovedEvent, 45 | ) { 46 | if (state === undefined) return null; 47 | 48 | const { domain, service } = event.data; 49 | const curDomainInfo = state[domain]; 50 | 51 | if (!curDomainInfo || !(service in curDomainInfo)) return null; 52 | 53 | const domainInfo: HassDomainServices = {}; 54 | Object.keys(curDomainInfo).forEach((sKey) => { 55 | if (sKey !== service) domainInfo[sKey] = curDomainInfo[sKey]; 56 | }); 57 | 58 | return { [domain]: domainInfo }; 59 | } 60 | 61 | const debouncedFetchServices = debounce( 62 | (conn: Connection, store: Store) => 63 | fetchServices(conn).then((services) => store.setState(services, true)), 64 | 5000, 65 | ); 66 | 67 | const fetchServices = (conn: Connection) => getServices(conn); 68 | const subscribeUpdates = (conn: Connection, store: Store) => 69 | Promise.all([ 70 | conn.subscribeEvents( 71 | (ev) => 72 | processServiceRegistered(conn, store, ev as ServiceRegisteredEvent), 73 | "service_registered", 74 | ), 75 | conn.subscribeEvents( 76 | store.action(processServiceRemoved), 77 | "service_removed", 78 | ), 79 | ]).then((unsubs) => () => unsubs.forEach((fn) => fn())); 80 | 81 | export const servicesColl = (conn: Connection) => 82 | getCollection(conn, "_srv", fetchServices, subscribeUpdates); 83 | 84 | export const subscribeServices = ( 85 | conn: Connection, 86 | onChange: (state: HassServices) => void, 87 | ): UnsubscribeFunc => servicesColl(conn).subscribe(onChange); 88 | -------------------------------------------------------------------------------- /test/entities.spec.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | 3 | import { subscribeEntities } from "../dist/entities.js"; 4 | import { MockConnection, AwaitableEvent } from "./util.js"; 5 | 6 | const MOCK_LIGHT = { 7 | entity_id: "light.kitchen", 8 | state: "on", 9 | }; 10 | 11 | const MOCK_SWITCH = { 12 | entity_id: "switch.ac", 13 | state: "off", 14 | }; 15 | 16 | const MOCK_ENTITIES = [MOCK_LIGHT, MOCK_SWITCH]; 17 | 18 | describe("subscribeEntities legacy", () => { 19 | let conn: MockConnection; 20 | let awaitableEvent: AwaitableEvent; 21 | 22 | beforeEach(() => { 23 | conn = new MockConnection(); 24 | conn.haVersion = "2022.3.0"; 25 | conn.mockResponse("get_states", MOCK_ENTITIES); 26 | awaitableEvent = new AwaitableEvent(); 27 | }); 28 | 29 | it("should load initial entities", async () => { 30 | awaitableEvent.prime(); 31 | subscribeEntities(conn, awaitableEvent.set); 32 | 33 | const entities = await awaitableEvent.wait(); 34 | assert.deepStrictEqual(entities, { 35 | [MOCK_LIGHT.entity_id]: MOCK_LIGHT, 36 | [MOCK_SWITCH.entity_id]: MOCK_SWITCH, 37 | }); 38 | }); 39 | 40 | it("should handle state changed with updated state", async () => { 41 | subscribeEntities(conn, awaitableEvent.set); 42 | 43 | await 0; 44 | await 0; 45 | await 0; 46 | 47 | awaitableEvent.prime(); 48 | 49 | conn.mockEvent("state_changed", { 50 | data: { 51 | entity_id: "light.kitchen", 52 | new_state: { 53 | entity_id: "light.kitchen", 54 | state: "off", 55 | }, 56 | }, 57 | }); 58 | 59 | const entities = await awaitableEvent.wait(); 60 | 61 | assert.deepEqual(entities, { 62 | [MOCK_SWITCH.entity_id]: MOCK_SWITCH, 63 | "light.kitchen": { 64 | entity_id: "light.kitchen", 65 | state: "off", 66 | }, 67 | }); 68 | }); 69 | 70 | it("should handle state changed with new state", async () => { 71 | subscribeEntities(conn, awaitableEvent.set); 72 | 73 | await 0; 74 | await 0; 75 | await 0; 76 | 77 | awaitableEvent.prime(); 78 | 79 | conn.mockEvent("state_changed", { 80 | data: { 81 | entity_id: "light.living_room", 82 | new_state: { 83 | entity_id: "light.living_room", 84 | state: "off", 85 | }, 86 | }, 87 | }); 88 | 89 | const entities = await awaitableEvent.wait(); 90 | 91 | assert.deepEqual(entities, { 92 | [MOCK_SWITCH.entity_id]: MOCK_SWITCH, 93 | [MOCK_LIGHT.entity_id]: MOCK_LIGHT, 94 | "light.living_room": { 95 | entity_id: "light.living_room", 96 | state: "off", 97 | }, 98 | }); 99 | }); 100 | 101 | it("should handle state changed with removed state", async () => { 102 | subscribeEntities(conn, awaitableEvent.set); 103 | 104 | await 0; 105 | await 0; 106 | await 0; 107 | 108 | awaitableEvent.prime(); 109 | 110 | conn.mockEvent("state_changed", { 111 | data: { 112 | entity_id: "light.kitchen", 113 | new_state: null, 114 | }, 115 | }); 116 | 117 | const entities = await awaitableEvent.wait(); 118 | 119 | assert.deepEqual(entities, { 120 | [MOCK_SWITCH.entity_id]: MOCK_SWITCH, 121 | }); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /test/services.spec.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | 3 | import { subscribeServices } from "../dist/services.js"; 4 | import { MockConnection, AwaitableEvent } from "./util.js"; 5 | 6 | const MOCK_SERVICES = { 7 | light: { 8 | turn_on: { 9 | description: "Turn a light on", 10 | fields: { 11 | entity_id: { 12 | description: "Entity ID to turn on", 13 | example: "light.kitchen", 14 | }, 15 | }, 16 | }, 17 | }, 18 | }; 19 | 20 | describe("subscribeServices", () => { 21 | let conn: MockConnection; 22 | let awaitableEvent: AwaitableEvent; 23 | 24 | beforeEach(() => { 25 | conn = new MockConnection(); 26 | conn.mockResponse("get_services", MOCK_SERVICES); 27 | awaitableEvent = new AwaitableEvent(); 28 | }); 29 | 30 | it("should load initial services", async () => { 31 | awaitableEvent.prime(); 32 | subscribeServices(conn, awaitableEvent.set); 33 | 34 | const services = await awaitableEvent.wait(); 35 | assert.deepStrictEqual(services, MOCK_SERVICES); 36 | }); 37 | 38 | it("should handle service registered events for existing domains", async () => { 39 | subscribeServices(conn, awaitableEvent.set); 40 | 41 | await 0; 42 | 43 | awaitableEvent.prime(); 44 | 45 | conn.mockEvent("service_registered", { 46 | data: { 47 | domain: "light", 48 | service: "toggle", 49 | }, 50 | }); 51 | 52 | const services = await awaitableEvent.wait(); 53 | 54 | assert.deepEqual(services, { 55 | light: { 56 | turn_on: { 57 | description: "Turn a light on", 58 | fields: { 59 | entity_id: { 60 | description: "Entity ID to turn on", 61 | example: "light.kitchen", 62 | }, 63 | }, 64 | }, 65 | toggle: { 66 | description: "", 67 | fields: {}, 68 | }, 69 | }, 70 | }); 71 | }); 72 | 73 | it("should handle service registered events for new domains", async () => { 74 | subscribeServices(conn, awaitableEvent.set); 75 | 76 | // We need to sleep to have it process the first full load 77 | await 0; 78 | awaitableEvent.prime(); 79 | 80 | conn.mockEvent("service_registered", { 81 | data: { 82 | domain: "switch", 83 | service: "turn_on", 84 | }, 85 | }); 86 | 87 | const services = await awaitableEvent.wait(); 88 | 89 | assert.deepEqual(services, { 90 | light: { 91 | turn_on: { 92 | description: "Turn a light on", 93 | fields: { 94 | entity_id: { 95 | description: "Entity ID to turn on", 96 | example: "light.kitchen", 97 | }, 98 | }, 99 | }, 100 | }, 101 | switch: { 102 | turn_on: { 103 | description: "", 104 | fields: {}, 105 | }, 106 | }, 107 | }); 108 | }); 109 | 110 | it("should handle service removed events for existing services", async () => { 111 | subscribeServices(conn, awaitableEvent.set); 112 | 113 | // We need to sleep to have it process the first full load 114 | await 0; 115 | awaitableEvent.prime(); 116 | 117 | conn.mockEvent("service_removed", { 118 | data: { 119 | domain: "light", 120 | service: "turn_on", 121 | }, 122 | }); 123 | 124 | const services = await awaitableEvent.wait(); 125 | 126 | assert.deepEqual(services, { 127 | light: {}, 128 | }); 129 | }); 130 | }); 131 | -------------------------------------------------------------------------------- /lib/types.ts: -------------------------------------------------------------------------------- 1 | // This file has no imports on purpose 2 | // So it can easily be consumed by other TS projects 3 | 4 | export type Error = 1 | 2 | 3 | 4; 5 | 6 | export type UnsubscribeFunc = () => void; 7 | 8 | export type MessageBase = { 9 | id?: number; 10 | type: string; 11 | [key: string]: any; 12 | }; 13 | 14 | export type Context = { 15 | id: string; 16 | user_id: string | null; 17 | parent_id: string | null; 18 | }; 19 | 20 | export type HassEventBase = { 21 | origin: string; 22 | time_fired: string; 23 | context: Context; 24 | }; 25 | 26 | export type HassEvent = HassEventBase & { 27 | event_type: string; 28 | data: { [key: string]: any }; 29 | }; 30 | 31 | export type StateChangedEvent = HassEventBase & { 32 | event_type: "state_changed"; 33 | data: { 34 | entity_id: string; 35 | new_state: HassEntity | null; 36 | old_state: HassEntity | null; 37 | }; 38 | }; 39 | 40 | export type HassConfig = { 41 | latitude: number; 42 | longitude: number; 43 | elevation: number; 44 | radius: number; 45 | unit_system: { 46 | length: string; 47 | mass: string; 48 | volume: string; 49 | temperature: string; 50 | pressure: string; 51 | wind_speed: string; 52 | accumulated_precipitation: string; 53 | }; 54 | location_name: string; 55 | time_zone: string; 56 | components: string[]; 57 | config_dir: string; 58 | allowlist_external_dirs: string[]; 59 | allowlist_external_urls: string[]; 60 | version: string; 61 | config_source: string; 62 | recovery_mode: boolean; 63 | safe_mode: boolean; 64 | state: "NOT_RUNNING" | "STARTING" | "RUNNING" | "STOPPING" | "FINAL_WRITE"; 65 | external_url: string | null; 66 | internal_url: string | null; 67 | currency: string; 68 | country: string | null; 69 | language: string; 70 | }; 71 | 72 | export type HassEntityBase = { 73 | entity_id: string; 74 | state: string; 75 | last_changed: string; 76 | last_updated: string; 77 | attributes: HassEntityAttributeBase; 78 | context: Context; 79 | }; 80 | 81 | export type HassEntityAttributeBase = { 82 | friendly_name?: string; 83 | unit_of_measurement?: string; 84 | icon?: string; 85 | entity_picture?: string; 86 | supported_features?: number; 87 | hidden?: boolean; 88 | assumed_state?: boolean; 89 | device_class?: string; 90 | state_class?: string; 91 | restored?: boolean; 92 | }; 93 | 94 | export type HassEntity = HassEntityBase & { 95 | attributes: { [key: string]: any }; 96 | }; 97 | 98 | export type HassEntities = { [entity_id: string]: HassEntity }; 99 | 100 | export type HassService = { 101 | name?: string; 102 | description?: string; 103 | description_placeholders?: { [placeholder: string]: string }; 104 | target?: {} | null; 105 | fields: { 106 | [field_name: string]: { 107 | example?: string | boolean | number; 108 | default?: unknown; 109 | required?: boolean; 110 | advanced?: boolean; 111 | selector?: {}; 112 | filter?: { 113 | supported_features?: number[]; 114 | attribute?: Record; 115 | }; 116 | // Custom integrations don't use translations and still have name/description 117 | name?: string; 118 | description?: string; 119 | }; 120 | }; 121 | response?: { optional: boolean }; 122 | }; 123 | 124 | export type HassDomainServices = { 125 | [service_name: string]: HassService; 126 | }; 127 | 128 | export type HassServices = { 129 | [domain: string]: HassDomainServices; 130 | }; 131 | 132 | export type HassUser = { 133 | id: string; 134 | is_admin: boolean; 135 | is_owner: boolean; 136 | name: string; 137 | }; 138 | 139 | export type HassServiceTarget = { 140 | entity_id?: string | string[]; 141 | device_id?: string | string[]; 142 | area_id?: string | string[]; 143 | floor_id?: string | string[]; 144 | label_id?: string | string[]; 145 | }; 146 | -------------------------------------------------------------------------------- /lib/store.ts: -------------------------------------------------------------------------------- 1 | import { UnsubscribeFunc } from "./types.js"; 2 | 3 | // (c) Jason Miller 4 | // Unistore - MIT license 5 | // And then adopted to our needs + typescript 6 | 7 | type Listener = (state: State) => void; 8 | type Action = ( 9 | state: State, 10 | ...args: any[] 11 | ) => Partial | Promise> | null; 12 | type BoundAction = (...args: any[]) => void; 13 | 14 | export type Store = { 15 | state: State | undefined; 16 | action(action: Action): BoundAction; 17 | setState(update: Partial, overwrite?: boolean): void; 18 | clearState(): void; 19 | subscribe(listener: Listener): UnsubscribeFunc; 20 | }; 21 | 22 | export const createStore = (state?: State): Store => { 23 | let listeners: Listener[] = []; 24 | 25 | function unsubscribe(listener: Listener | null) { 26 | let out = []; 27 | for (let i = 0; i < listeners.length; i++) { 28 | if (listeners[i] === listener) { 29 | listener = null; 30 | } else { 31 | out.push(listeners[i]); 32 | } 33 | } 34 | listeners = out; 35 | } 36 | 37 | function setState(update: Partial, overwrite: boolean): void { 38 | state = overwrite ? (update as State) : { ...state!, ...update }; 39 | let currentListeners = listeners; 40 | for (let i = 0; i < currentListeners.length; i++) { 41 | currentListeners[i](state); 42 | } 43 | } 44 | 45 | /** 46 | * An observable state container, returned from {@link createStore} 47 | * @name store 48 | */ 49 | 50 | return { 51 | get state() { 52 | return state; 53 | }, 54 | 55 | /** 56 | * Create a bound copy of the given action function. 57 | * The bound returned function invokes action() and persists the result back to the store. 58 | * If the return value of `action` is a Promise, the resolved value will be used as state. 59 | * @param {Function} action An action of the form `action(state, ...args) -> stateUpdate` 60 | * @returns {Function} boundAction() 61 | */ 62 | action(action: Action): BoundAction { 63 | function apply(result: Partial) { 64 | setState(result, false); 65 | } 66 | 67 | // Note: perf tests verifying this implementation: https://esbench.com/bench/5a295e6299634800a0349500 68 | return function () { 69 | let args = [state]; 70 | for (let i = 0; i < arguments.length; i++) args.push(arguments[i]); 71 | // @ts-ignore 72 | let ret = action.apply(this, args); 73 | if (ret != null) { 74 | return ret instanceof Promise ? ret.then(apply) : apply(ret); 75 | } 76 | }; 77 | }, 78 | 79 | /** 80 | * Apply a partial state object to the current state, invoking registered listeners. 81 | * @param {Object} update An object with properties to be merged into state 82 | * @param {Boolean} [overwrite=false] If `true`, update will replace state instead of being merged into it 83 | */ 84 | setState, 85 | 86 | clearState() { 87 | state = undefined; 88 | }, 89 | 90 | /** 91 | * Register a listener function to be called whenever state is changed. Returns an `unsubscribe()` function. 92 | * @param {Function} listener A function to call when state changes. Gets passed the new state. 93 | * @returns {Function} unsubscribe() 94 | */ 95 | subscribe(listener: Listener): UnsubscribeFunc { 96 | listeners.push(listener); 97 | return () => { 98 | unsubscribe(listener); 99 | }; 100 | }, 101 | 102 | // /** 103 | // * Remove a previously-registered listener function. 104 | // * @param {Function} listener The callback previously passed to `subscribe()` that should be removed. 105 | // * @function 106 | // */ 107 | // unsubscribe, 108 | }; 109 | }; 110 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /lib/socket.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a web socket connection with a Home Assistant instance. 3 | */ 4 | import { 5 | ERR_INVALID_AUTH, 6 | ERR_CANNOT_CONNECT, 7 | ERR_HASS_HOST_REQUIRED, 8 | } from "./errors.js"; 9 | import { Error } from "./types.js"; 10 | import type { ConnectionOptions } from "./connection.js"; 11 | import * as messages from "./messages.js"; 12 | import { atLeastHaVersion } from "./util.js"; 13 | 14 | const DEBUG = false; 15 | 16 | export const MSG_TYPE_AUTH_REQUIRED = "auth_required"; 17 | export const MSG_TYPE_AUTH_INVALID = "auth_invalid"; 18 | export const MSG_TYPE_AUTH_OK = "auth_ok"; 19 | 20 | export interface HaWebSocket extends WebSocket { 21 | haVersion: string; 22 | } 23 | 24 | export function createSocket(options: ConnectionOptions): Promise { 25 | if (!options.auth) { 26 | throw ERR_HASS_HOST_REQUIRED; 27 | } 28 | const auth = options.auth; 29 | 30 | // Start refreshing expired tokens even before the WS connection is open. 31 | // We know that we will need auth anyway. 32 | let authRefreshTask = auth.expired 33 | ? auth.refreshAccessToken().then( 34 | () => { 35 | authRefreshTask = undefined; 36 | }, 37 | () => { 38 | authRefreshTask = undefined; 39 | }, 40 | ) 41 | : undefined; 42 | 43 | // Convert from http:// -> ws://, https:// -> wss:// 44 | const url = auth.wsUrl; 45 | 46 | if (DEBUG) { 47 | console.log("[Auth phase] Initializing", url); 48 | } 49 | 50 | function connect( 51 | triesLeft: number, 52 | promResolve: (socket: HaWebSocket) => void, 53 | promReject: (err: Error) => void, 54 | ) { 55 | if (DEBUG) { 56 | console.log("[Auth Phase] New connection", url); 57 | } 58 | 59 | const socket = new WebSocket(url) as HaWebSocket; 60 | 61 | // If invalid auth, we will not try to reconnect. 62 | let invalidAuth = false; 63 | 64 | const closeMessage = () => { 65 | // If we are in error handler make sure close handler doesn't also fire. 66 | socket.removeEventListener("close", closeMessage); 67 | if (invalidAuth) { 68 | promReject(ERR_INVALID_AUTH); 69 | return; 70 | } 71 | 72 | // Reject if we no longer have to retry 73 | if (triesLeft === 0) { 74 | // We never were connected and will not retry 75 | promReject(ERR_CANNOT_CONNECT); 76 | return; 77 | } 78 | 79 | const newTries = triesLeft === -1 ? -1 : triesLeft - 1; 80 | // Try again in a second 81 | setTimeout(() => connect(newTries, promResolve, promReject), 1000); 82 | }; 83 | 84 | // Auth is mandatory, so we can send the auth message right away. 85 | const handleOpen = async (event: MessageEventInit) => { 86 | try { 87 | if (auth.expired) { 88 | await (authRefreshTask ? authRefreshTask : auth.refreshAccessToken()); 89 | } 90 | socket.send(JSON.stringify(messages.auth(auth.accessToken))); 91 | } catch (err) { 92 | // Refresh token failed 93 | invalidAuth = err === ERR_INVALID_AUTH; 94 | socket.close(); 95 | } 96 | }; 97 | 98 | const handleMessage = async (event: MessageEvent) => { 99 | const message = JSON.parse(event.data); 100 | 101 | if (DEBUG) { 102 | console.log("[Auth phase] Received", message); 103 | } 104 | switch (message.type) { 105 | case MSG_TYPE_AUTH_INVALID: 106 | invalidAuth = true; 107 | socket.close(); 108 | break; 109 | 110 | case MSG_TYPE_AUTH_OK: 111 | socket.removeEventListener("open", handleOpen); 112 | socket.removeEventListener("message", handleMessage); 113 | socket.removeEventListener("close", closeMessage); 114 | socket.removeEventListener("error", closeMessage); 115 | socket.haVersion = message.ha_version; 116 | if (atLeastHaVersion(socket.haVersion, 2022, 9)) { 117 | socket.send(JSON.stringify(messages.supportedFeatures())); 118 | } 119 | 120 | promResolve(socket); 121 | break; 122 | 123 | default: 124 | if (DEBUG) { 125 | // We already send response to this message when socket opens 126 | if (message.type !== MSG_TYPE_AUTH_REQUIRED) { 127 | console.warn("[Auth phase] Unhandled message", message); 128 | } 129 | } 130 | } 131 | }; 132 | 133 | socket.addEventListener("open", handleOpen); 134 | socket.addEventListener("message", handleMessage); 135 | socket.addEventListener("close", closeMessage); 136 | socket.addEventListener("error", closeMessage); 137 | } 138 | 139 | return new Promise((resolve, reject) => 140 | connect(options.setupRetry, resolve, reject), 141 | ); 142 | } 143 | -------------------------------------------------------------------------------- /lib/entities.ts: -------------------------------------------------------------------------------- 1 | import { getCollection } from "./collection.js"; 2 | import { 3 | Context, 4 | HassEntities, 5 | StateChangedEvent, 6 | UnsubscribeFunc, 7 | } from "./types.js"; 8 | import { Connection } from "./connection.js"; 9 | import { Store } from "./store.js"; 10 | import { getStates } from "./commands.js"; 11 | import { atLeastHaVersion } from "./util.js"; 12 | 13 | interface EntityState { 14 | /** state */ 15 | s: string; 16 | /** attributes */ 17 | a: { [key: string]: any }; 18 | /** context */ 19 | c: Context | string; 20 | /** last_changed; if set, also applies to lu */ 21 | lc: number; 22 | /** last_updated */ 23 | lu: number; 24 | } 25 | 26 | interface EntityStateRemove { 27 | /** attributes */ 28 | a: string[]; 29 | } 30 | 31 | interface EntityDiff { 32 | /** additions */ 33 | "+"?: Partial; 34 | /** subtractions */ 35 | "-"?: EntityStateRemove; 36 | } 37 | 38 | interface StatesUpdates { 39 | /** add */ 40 | a?: Record; 41 | /** remove */ 42 | r?: string[]; // remove 43 | /** change */ 44 | c: Record; 45 | } 46 | 47 | function processEvent(store: Store, updates: StatesUpdates) { 48 | const state = { ...store.state }; 49 | 50 | if (updates.a) { 51 | for (const entityId in updates.a) { 52 | const newState = updates.a[entityId]; 53 | let last_changed = new Date(newState.lc * 1000).toISOString(); 54 | state[entityId] = { 55 | entity_id: entityId, 56 | state: newState.s, 57 | attributes: newState.a, 58 | context: 59 | typeof newState.c === "string" 60 | ? { id: newState.c, parent_id: null, user_id: null } 61 | : newState.c, 62 | last_changed: last_changed, 63 | last_updated: newState.lu 64 | ? new Date(newState.lu * 1000).toISOString() 65 | : last_changed, 66 | }; 67 | } 68 | } 69 | 70 | if (updates.r) { 71 | for (const entityId of updates.r) { 72 | delete state[entityId]; 73 | } 74 | } 75 | 76 | if (updates.c) { 77 | for (const entityId in updates.c) { 78 | let entityState = state[entityId]; 79 | 80 | if (!entityState) { 81 | console.warn("Received state update for unknown entity", entityId); 82 | continue; 83 | } 84 | 85 | entityState = { ...entityState }; 86 | 87 | const { "+": toAdd, "-": toRemove } = updates.c[entityId]; 88 | const attributesChanged = toAdd?.a || toRemove?.a; 89 | const attributes = attributesChanged 90 | ? { ...entityState.attributes } 91 | : entityState.attributes; 92 | 93 | if (toAdd) { 94 | if (toAdd.s !== undefined) { 95 | entityState.state = toAdd.s; 96 | } 97 | if (toAdd.c) { 98 | if (typeof toAdd.c === "string") { 99 | entityState.context = { ...entityState.context, id: toAdd.c }; 100 | } else { 101 | entityState.context = { ...entityState.context, ...toAdd.c }; 102 | } 103 | } 104 | if (toAdd.lc) { 105 | entityState.last_updated = entityState.last_changed = new Date( 106 | toAdd.lc * 1000, 107 | ).toISOString(); 108 | } else if (toAdd.lu) { 109 | entityState.last_updated = new Date(toAdd.lu * 1000).toISOString(); 110 | } 111 | if (toAdd.a) { 112 | Object.assign(attributes, toAdd.a); 113 | } 114 | } 115 | if (toRemove?.a) { 116 | for (const key of toRemove.a) { 117 | delete attributes[key]; 118 | } 119 | } 120 | if (attributesChanged) { 121 | entityState.attributes = attributes; 122 | } 123 | state[entityId] = entityState; 124 | } 125 | } 126 | 127 | store.setState(state, true); 128 | } 129 | 130 | const subscribeUpdates = (conn: Connection, store: Store) => 131 | conn.subscribeMessage((ev) => processEvent(store, ev), { 132 | type: "subscribe_entities", 133 | }); 134 | 135 | function legacyProcessEvent( 136 | store: Store, 137 | event: StateChangedEvent, 138 | ) { 139 | const state = store.state; 140 | if (state === undefined) return; 141 | 142 | const { entity_id, new_state } = event.data; 143 | if (new_state) { 144 | store.setState({ [new_state.entity_id]: new_state }); 145 | } else { 146 | const newEntities = { ...state }; 147 | delete newEntities[entity_id]; 148 | store.setState(newEntities, true); 149 | } 150 | } 151 | 152 | async function legacyFetchEntities(conn: Connection): Promise { 153 | const states = await getStates(conn); 154 | const entities: HassEntities = {}; 155 | for (let i = 0; i < states.length; i++) { 156 | const state = states[i]; 157 | entities[state.entity_id] = state; 158 | } 159 | return entities; 160 | } 161 | 162 | const legacySubscribeUpdates = (conn: Connection, store: Store) => 163 | conn.subscribeEvents( 164 | (ev) => legacyProcessEvent(store, ev as StateChangedEvent), 165 | "state_changed", 166 | ); 167 | 168 | export const entitiesColl = (conn: Connection) => 169 | atLeastHaVersion(conn.haVersion, 2022, 4, 0) 170 | ? getCollection(conn, "_ent", undefined, subscribeUpdates) 171 | : getCollection(conn, "_ent", legacyFetchEntities, legacySubscribeUpdates); 172 | 173 | export const subscribeEntities = ( 174 | conn: Connection, 175 | onChange: (state: HassEntities) => void, 176 | ): UnsubscribeFunc => entitiesColl(conn).subscribe(onChange); 177 | -------------------------------------------------------------------------------- /lib/collection.ts: -------------------------------------------------------------------------------- 1 | import { Store, createStore } from "./store.js"; 2 | import { Connection } from "./connection.js"; 3 | import { UnsubscribeFunc } from "./types.js"; 4 | 5 | export type Collection = { 6 | state: State; 7 | refresh(): Promise; 8 | subscribe(subscriber: (state: State) => void): UnsubscribeFunc; 9 | }; 10 | 11 | // Time to wait to unsubscribe from updates after last subscriber unsubscribes 12 | const UNSUB_GRACE_PERIOD = 5000; // 5 seconds 13 | const DEBUG = false; 14 | 15 | /** 16 | * 17 | * @param conn connection 18 | * @param key the key to store it on the connection. Must be unique for each collection. 19 | * @param fetchCollection fetch the current state. If undefined assumes subscribeUpdates receives current state 20 | * @param subscribeUpdates subscribe to updates on the current state 21 | * @returns 22 | */ 23 | export const getCollection = ( 24 | conn: Connection, 25 | key: string, 26 | fetchCollection: ((conn: Connection) => Promise) | undefined, 27 | subscribeUpdates?: ( 28 | conn: Connection, 29 | store: Store, 30 | ) => Promise, 31 | options: { unsubGrace: boolean } = { unsubGrace: true }, 32 | ): Collection => { 33 | // @ts-ignore 34 | if (conn[key]) { 35 | // @ts-ignore 36 | return conn[key]; 37 | } 38 | 39 | let active = 0; 40 | let unsubProm: Promise; 41 | let unsubTimer: number | undefined; 42 | let store = createStore(); 43 | 44 | const refresh = (): Promise => { 45 | if (!fetchCollection) { 46 | throw new Error("Collection does not support refresh"); 47 | } 48 | 49 | return fetchCollection(conn).then((state) => store.setState(state, true)); 50 | }; 51 | 52 | const refreshSwallow = () => 53 | refresh().catch((err: unknown) => { 54 | // Swallow errors if socket is connecting, closing or closed. 55 | // We will automatically call refresh again when we re-establish the connection. 56 | if (conn.connected) { 57 | throw err; 58 | } 59 | }); 60 | 61 | const setupUpdateSubscription = () => { 62 | if (unsubTimer !== undefined) { 63 | if (DEBUG) { 64 | console.log(`Prevented unsubscribe for ${key}`); 65 | } 66 | clearTimeout(unsubTimer); 67 | unsubTimer = undefined; 68 | return; 69 | } 70 | 71 | if (DEBUG) { 72 | console.log(`Subscribing to ${key}`); 73 | } 74 | 75 | if (subscribeUpdates) { 76 | unsubProm = subscribeUpdates(conn, store); 77 | } 78 | 79 | if (fetchCollection) { 80 | // Fetch when connection re-established. 81 | conn.addEventListener("ready", refreshSwallow); 82 | refreshSwallow(); 83 | } 84 | 85 | conn.addEventListener("disconnected", handleDisconnect); 86 | }; 87 | 88 | const teardownUpdateSubscription = () => { 89 | if (DEBUG) { 90 | console.log(`Unsubscribing from ${key}`); 91 | } 92 | unsubTimer = undefined; 93 | 94 | // Unsubscribe from changes 95 | if (unsubProm) 96 | unsubProm.then((unsub) => { 97 | unsub(); 98 | }); 99 | store.clearState(); 100 | conn.removeEventListener("ready", refresh); 101 | conn.removeEventListener("disconnected", handleDisconnect); 102 | }; 103 | 104 | const scheduleTeardownUpdateSubscription = () => { 105 | if (DEBUG) { 106 | console.log(`Scheduling unsubscribing from ${key}`); 107 | } 108 | unsubTimer = setTimeout(teardownUpdateSubscription, UNSUB_GRACE_PERIOD); 109 | }; 110 | 111 | const handleDisconnect = () => { 112 | // If we're going to unsubscribe and then lose connection, 113 | // just unsubscribe immediately. 114 | if (unsubTimer) { 115 | clearTimeout(unsubTimer); 116 | teardownUpdateSubscription(); 117 | } 118 | }; 119 | 120 | // @ts-ignore 121 | conn[key] = { 122 | get state() { 123 | return store.state; 124 | }, 125 | 126 | refresh, 127 | 128 | subscribe(subscriber: (state: State) => void): UnsubscribeFunc { 129 | active++; 130 | 131 | if (DEBUG) { 132 | console.log(`New subscriber for ${key}. Active subscribers: ${active}`); 133 | } 134 | 135 | // If this was the first subscriber, attach collection 136 | if (active === 1) { 137 | setupUpdateSubscription(); 138 | } 139 | 140 | const unsub = store.subscribe(subscriber); 141 | 142 | if (store.state !== undefined) { 143 | // Don't call it right away so that caller has time 144 | // to initialize all the things. 145 | setTimeout(() => subscriber(store.state!), 0); 146 | } 147 | 148 | return () => { 149 | unsub(); 150 | active--; 151 | 152 | if (DEBUG) { 153 | console.log(`Unsubscribe for ${key}. Active subscribers: ${active}`); 154 | } 155 | 156 | if (!active) { 157 | options.unsubGrace 158 | ? scheduleTeardownUpdateSubscription() 159 | : teardownUpdateSubscription(); 160 | } 161 | }; 162 | }, 163 | }; 164 | 165 | // @ts-ignore 166 | return conn[key]; 167 | }; 168 | 169 | // Legacy name. It gets a collection and subscribes. 170 | export const createCollection = ( 171 | key: string, 172 | fetchCollection: (conn: Connection) => Promise, 173 | subscribeUpdates: 174 | | ((conn: Connection, store: Store) => Promise) 175 | | undefined, 176 | conn: Connection, 177 | onChange: (state: State) => void, 178 | ): UnsubscribeFunc => 179 | getCollection(conn, key, fetchCollection, subscribeUpdates).subscribe( 180 | onChange, 181 | ); 182 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | [safety@home-assistant.io][email] or by using the report/flag feature of 64 | the medium used. All complaints will be reviewed and investigated promptly and 65 | fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series 87 | of actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or 94 | permanent ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within 114 | the community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.0, available [here][version]. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder][mozilla]. 123 | 124 | ## Adoption 125 | 126 | This Code of Conduct was first adopted January 21st, 2017 and announced in 127 | [this][coc-blog] blog post and has been updated on May 25th, 2020 to version 128 | 2.0 of the [Contributor Covenant][homepage] as announced in [this][coc2-blog] 129 | blog post. 130 | 131 | For answers to common questions about this code of conduct, see the FAQ at 132 | . Translations are available at 133 | . 134 | 135 | [coc-blog]: /blog/2017/01/21/home-assistant-governance/ 136 | [coc2-blog]: /blog/2020/05/25/code-of-conduct-updated/ 137 | [email]: mailto:safety@home-assistant.io 138 | [homepage]: http://contributor-covenant.org 139 | [mozilla]: https://github.com/mozilla/diversity 140 | [version]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html 141 | -------------------------------------------------------------------------------- /lib/auth.ts: -------------------------------------------------------------------------------- 1 | import { parseQuery } from "./util.js"; 2 | import { 3 | ERR_HASS_HOST_REQUIRED, 4 | ERR_INVALID_AUTH, 5 | ERR_INVALID_AUTH_CALLBACK, 6 | ERR_INVALID_HTTPS_TO_HTTP, 7 | } from "./errors.js"; 8 | 9 | export type AuthData = { 10 | hassUrl: string; 11 | clientId: string | null; 12 | expires: number; 13 | refresh_token: string; 14 | access_token: string; 15 | expires_in: number; 16 | }; 17 | 18 | export type SaveTokensFunc = (data: AuthData | null) => void; 19 | export type LoadTokensFunc = () => Promise; 20 | 21 | export type getAuthOptions = { 22 | hassUrl?: string; 23 | clientId?: string | null; 24 | redirectUrl?: string; 25 | authCode?: string; 26 | saveTokens?: SaveTokensFunc; 27 | loadTokens?: LoadTokensFunc; 28 | limitHassInstance?: boolean; 29 | }; 30 | 31 | type QueryCallbackData = 32 | | {} 33 | | { 34 | state: string; 35 | code: string; 36 | auth_callback: string; 37 | }; 38 | 39 | type OAuthState = { 40 | hassUrl: string; 41 | clientId: string | null; 42 | }; 43 | 44 | type AuthorizationCodeRequest = { 45 | grant_type: "authorization_code"; 46 | code: string; 47 | }; 48 | 49 | type RefreshTokenRequest = { 50 | grant_type: "refresh_token"; 51 | refresh_token: string; 52 | }; 53 | 54 | export const genClientId = (): string => 55 | `${location.protocol}//${location.host}/`; 56 | 57 | export const genExpires = (expires_in: number): number => { 58 | return expires_in * 1000 + Date.now(); 59 | }; 60 | 61 | function genRedirectUrl() { 62 | // Get current url but without # part. 63 | const { protocol, host, pathname, search } = location; 64 | return `${protocol}//${host}${pathname}${search}`; 65 | } 66 | 67 | function genAuthorizeUrl( 68 | hassUrl: string, 69 | clientId: string | null, 70 | redirectUrl: string, 71 | state: string, 72 | ) { 73 | let authorizeUrl = `${hassUrl}/auth/authorize?response_type=code&redirect_uri=${encodeURIComponent( 74 | redirectUrl, 75 | )}`; 76 | 77 | if (clientId !== null) { 78 | authorizeUrl += `&client_id=${encodeURIComponent(clientId)}`; 79 | } 80 | 81 | if (state) { 82 | authorizeUrl += `&state=${encodeURIComponent(state)}`; 83 | } 84 | return authorizeUrl; 85 | } 86 | 87 | function redirectAuthorize( 88 | hassUrl: string, 89 | clientId: string | null, 90 | redirectUrl: string, 91 | state: string, 92 | ) { 93 | // Add either ?auth_callback=1 or &auth_callback=1 94 | redirectUrl += (redirectUrl.includes("?") ? "&" : "?") + "auth_callback=1"; 95 | 96 | document.location!.href = genAuthorizeUrl( 97 | hassUrl, 98 | clientId, 99 | redirectUrl, 100 | state, 101 | ); 102 | } 103 | 104 | async function tokenRequest( 105 | hassUrl: string, 106 | clientId: string | null, 107 | data: AuthorizationCodeRequest | RefreshTokenRequest, 108 | ) { 109 | // Browsers don't allow fetching tokens from https -> http. 110 | // Throw an error because it's a pain to debug this. 111 | // Guard against not working in node. 112 | const l = typeof location !== "undefined" && location; 113 | if (l && l.protocol === "https:") { 114 | // Ensure that the hassUrl is hosted on https. 115 | const a = document.createElement("a"); 116 | a.href = hassUrl; 117 | if (a.protocol === "http:" && a.hostname !== "localhost") { 118 | throw ERR_INVALID_HTTPS_TO_HTTP; 119 | } 120 | } 121 | 122 | const formData = new FormData(); 123 | if (clientId !== null) { 124 | formData.append("client_id", clientId); 125 | } 126 | Object.keys(data).forEach((key) => { 127 | // @ts-ignore 128 | formData.append(key, data[key]); 129 | }); 130 | 131 | const resp = await fetch(`${hassUrl}/auth/token`, { 132 | method: "POST", 133 | credentials: "same-origin", 134 | body: formData, 135 | }); 136 | 137 | if (!resp.ok) { 138 | throw resp.status === 400 /* auth invalid */ || 139 | resp.status === 403 /* user not active */ 140 | ? ERR_INVALID_AUTH 141 | : new Error("Unable to fetch tokens"); 142 | } 143 | 144 | const tokens: AuthData = await resp.json(); 145 | tokens.hassUrl = hassUrl; 146 | tokens.clientId = clientId; 147 | tokens.expires = genExpires(tokens.expires_in); 148 | return tokens; 149 | } 150 | 151 | function fetchToken(hassUrl: string, clientId: string | null, code: string) { 152 | return tokenRequest(hassUrl, clientId, { 153 | code, 154 | grant_type: "authorization_code", 155 | }); 156 | } 157 | 158 | function encodeOAuthState(state: OAuthState): string { 159 | return btoa(JSON.stringify(state)); 160 | } 161 | 162 | function decodeOAuthState(encoded: string): OAuthState { 163 | return JSON.parse(atob(encoded)); 164 | } 165 | 166 | export class Auth { 167 | private _saveTokens?: SaveTokensFunc; 168 | data: AuthData; 169 | 170 | constructor(data: AuthData, saveTokens?: SaveTokensFunc) { 171 | this.data = data; 172 | this._saveTokens = saveTokens; 173 | } 174 | 175 | get wsUrl() { 176 | // Convert from http:// -> ws://, https:// -> wss:// 177 | return `ws${this.data.hassUrl.substr(4)}/api/websocket`; 178 | } 179 | 180 | get accessToken() { 181 | return this.data.access_token; 182 | } 183 | 184 | get expired() { 185 | return Date.now() > this.data.expires; 186 | } 187 | 188 | /** 189 | * Refresh the access token. 190 | */ 191 | async refreshAccessToken() { 192 | if (!this.data.refresh_token) throw new Error("No refresh_token"); 193 | 194 | const data = await tokenRequest(this.data.hassUrl, this.data.clientId, { 195 | grant_type: "refresh_token", 196 | refresh_token: this.data.refresh_token, 197 | }); 198 | // Access token response does not contain refresh token. 199 | data.refresh_token = this.data.refresh_token; 200 | this.data = data; 201 | if (this._saveTokens) this._saveTokens(data); 202 | } 203 | 204 | /** 205 | * Revoke the refresh & access tokens. 206 | */ 207 | async revoke() { 208 | if (!this.data.refresh_token) throw new Error("No refresh_token to revoke"); 209 | 210 | const formData = new FormData(); 211 | formData.append("token", this.data.refresh_token); 212 | 213 | // There is no error checking, as revoke will always return 200 214 | await fetch(`${this.data.hassUrl}/auth/revoke`, { 215 | method: "POST", 216 | credentials: "same-origin", 217 | body: formData, 218 | }); 219 | 220 | if (this._saveTokens) { 221 | this._saveTokens(null); 222 | } 223 | } 224 | } 225 | 226 | export function createLongLivedTokenAuth( 227 | hassUrl: string, 228 | access_token: string, 229 | ) { 230 | return new Auth({ 231 | hassUrl, 232 | clientId: null, 233 | expires: Date.now() + 1e11, 234 | refresh_token: "", 235 | access_token, 236 | expires_in: 1e11, 237 | }); 238 | } 239 | 240 | export async function getAuth(options: getAuthOptions = {}): Promise { 241 | let data: AuthData | null | undefined; 242 | 243 | let hassUrl = options.hassUrl; 244 | // Strip trailing slash. 245 | if (hassUrl && hassUrl[hassUrl.length - 1] === "/") { 246 | hassUrl = hassUrl.substr(0, hassUrl.length - 1); 247 | } 248 | const clientId = 249 | options.clientId !== undefined ? options.clientId : genClientId(); 250 | const limitHassInstance = options.limitHassInstance === true; 251 | 252 | // Use auth code if it was passed in 253 | if (options.authCode && hassUrl) { 254 | data = await fetchToken(hassUrl, clientId, options.authCode); 255 | if (options.saveTokens) { 256 | options.saveTokens(data); 257 | } 258 | } 259 | 260 | // Check if we came back from an authorize redirect 261 | if (!data) { 262 | const query = parseQuery(location.search.substr(1)); 263 | 264 | // Check if we got redirected here from authorize page 265 | if ("auth_callback" in query) { 266 | // Restore state 267 | const state = decodeOAuthState(query.state); 268 | 269 | if ( 270 | limitHassInstance && 271 | (state.hassUrl !== hassUrl || state.clientId !== clientId) 272 | ) { 273 | throw ERR_INVALID_AUTH_CALLBACK; 274 | } 275 | 276 | data = await fetchToken(state.hassUrl, state.clientId, query.code); 277 | if (options.saveTokens) { 278 | options.saveTokens(data); 279 | } 280 | } 281 | } 282 | 283 | // Check for stored tokens 284 | if (!data && options.loadTokens) { 285 | data = await options.loadTokens(); 286 | } 287 | 288 | // If the token is for another url, ignore it 289 | if (data && (hassUrl === undefined || data.hassUrl === hassUrl)) { 290 | return new Auth(data, options.saveTokens); 291 | } 292 | 293 | if (hassUrl === undefined) { 294 | throw ERR_HASS_HOST_REQUIRED; 295 | } 296 | 297 | // If no tokens found but a hassUrl was passed in, let's go get some tokens! 298 | redirectAuthorize( 299 | hassUrl, 300 | clientId, 301 | options.redirectUrl || genRedirectUrl(), 302 | encodeOAuthState({ 303 | hassUrl, 304 | clientId, 305 | }), 306 | ); 307 | // Just don't resolve while we navigate to next page 308 | return new Promise(() => {}); 309 | } 310 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | ============== 3 | 4 | _Version 2.0, January 2004_ 5 | _<>_ 6 | 7 | ### Terms and Conditions for use, reproduction, and distribution 8 | 9 | #### 1. Definitions 10 | 11 | “License” shall mean the terms and conditions for use, reproduction, and 12 | distribution as defined by Sections 1 through 9 of this document. 13 | 14 | “Licensor” shall mean the copyright owner or entity authorized by the copyright 15 | owner that is granting the License. 16 | 17 | “Legal Entity” shall mean the union of the acting entity and all other entities 18 | that control, are controlled by, or are under common control with that entity. 19 | For the purposes of this definition, “control” means **(i)** the power, direct or 20 | indirect, to cause the direction or management of such entity, whether by 21 | contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the 22 | outstanding shares, or **(iii)** beneficial ownership of such entity. 23 | 24 | “You” (or “Your”) shall mean an individual or Legal Entity exercising 25 | permissions granted by this License. 26 | 27 | “Source” form shall mean the preferred form for making modifications, including 28 | but not limited to software source code, documentation source, and configuration 29 | files. 30 | 31 | “Object” form shall mean any form resulting from mechanical transformation or 32 | translation of a Source form, including but not limited to compiled object code, 33 | generated documentation, and conversions to other media types. 34 | 35 | “Work” shall mean the work of authorship, whether in Source or Object form, made 36 | available under the License, as indicated by a copyright notice that is included 37 | in or attached to the work (an example is provided in the Appendix below). 38 | 39 | “Derivative Works” shall mean any work, whether in Source or Object form, that 40 | is based on (or derived from) the Work and for which the editorial revisions, 41 | annotations, elaborations, or other modifications represent, as a whole, an 42 | original work of authorship. For the purposes of this License, Derivative Works 43 | shall not include works that remain separable from, or merely link (or bind by 44 | name) to the interfaces of, the Work and Derivative Works thereof. 45 | 46 | “Contribution” shall mean any work of authorship, including the original version 47 | of the Work and any modifications or additions to that Work or Derivative Works 48 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 49 | by the copyright owner or by an individual or Legal Entity authorized to submit 50 | on behalf of the copyright owner. For the purposes of this definition, 51 | “submitted” means any form of electronic, verbal, or written communication sent 52 | to the Licensor or its representatives, including but not limited to 53 | communication on electronic mailing lists, source code control systems, and 54 | issue tracking systems that are managed by, or on behalf of, the Licensor for 55 | the purpose of discussing and improving the Work, but excluding communication 56 | that is conspicuously marked or otherwise designated in writing by the copyright 57 | owner as “Not a Contribution.” 58 | 59 | “Contributor” shall mean Licensor and any individual or Legal Entity on behalf 60 | of whom a Contribution has been received by Licensor and subsequently 61 | incorporated within the Work. 62 | 63 | #### 2. Grant of Copyright License 64 | 65 | Subject to the terms and conditions of this License, each Contributor hereby 66 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 67 | irrevocable copyright license to reproduce, prepare Derivative Works of, 68 | publicly display, publicly perform, sublicense, and distribute the Work and such 69 | Derivative Works in Source or Object form. 70 | 71 | #### 3. Grant of Patent License 72 | 73 | Subject to the terms and conditions of this License, each Contributor hereby 74 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 75 | irrevocable (except as stated in this section) patent license to make, have 76 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 77 | such license applies only to those patent claims licensable by such Contributor 78 | that are necessarily infringed by their Contribution(s) alone or by combination 79 | of their Contribution(s) with the Work to which such Contribution(s) was 80 | submitted. If You institute patent litigation against any entity (including a 81 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 82 | Contribution incorporated within the Work constitutes direct or contributory 83 | patent infringement, then any patent licenses granted to You under this License 84 | for that Work shall terminate as of the date such litigation is filed. 85 | 86 | #### 4. Redistribution 87 | 88 | You may reproduce and distribute copies of the Work or Derivative Works thereof 89 | in any medium, with or without modifications, and in Source or Object form, 90 | provided that You meet the following conditions: 91 | 92 | * **(a)** You must give any other recipients of the Work or Derivative Works a copy of 93 | this License; and 94 | * **(b)** You must cause any modified files to carry prominent notices stating that You 95 | changed the files; and 96 | * **(c)** You must retain, in the Source form of any Derivative Works that You distribute, 97 | all copyright, patent, trademark, and attribution notices from the Source form 98 | of the Work, excluding those notices that do not pertain to any part of the 99 | Derivative Works; and 100 | * **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any 101 | Derivative Works that You distribute must include a readable copy of the 102 | attribution notices contained within such NOTICE file, excluding those notices 103 | that do not pertain to any part of the Derivative Works, in at least one of the 104 | following places: within a NOTICE text file distributed as part of the 105 | Derivative Works; within the Source form or documentation, if provided along 106 | with the Derivative Works; or, within a display generated by the Derivative 107 | Works, if and wherever such third-party notices normally appear. The contents of 108 | the NOTICE file are for informational purposes only and do not modify the 109 | License. You may add Your own attribution notices within Derivative Works that 110 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 111 | provided that such additional attribution notices cannot be construed as 112 | modifying the License. 113 | 114 | You may add Your own copyright statement to Your modifications and may provide 115 | additional or different license terms and conditions for use, reproduction, or 116 | distribution of Your modifications, or for any such Derivative Works as a whole, 117 | provided Your use, reproduction, and distribution of the Work otherwise complies 118 | with the conditions stated in this License. 119 | 120 | #### 5. Submission of Contributions 121 | 122 | Unless You explicitly state otherwise, any Contribution intentionally submitted 123 | for inclusion in the Work by You to the Licensor shall be under the terms and 124 | conditions of this License, without any additional terms or conditions. 125 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 126 | any separate license agreement you may have executed with Licensor regarding 127 | such Contributions. 128 | 129 | #### 6. Trademarks 130 | 131 | This License does not grant permission to use the trade names, trademarks, 132 | service marks, or product names of the Licensor, except as required for 133 | reasonable and customary use in describing the origin of the Work and 134 | reproducing the content of the NOTICE file. 135 | 136 | #### 7. Disclaimer of Warranty 137 | 138 | Unless required by applicable law or agreed to in writing, Licensor provides the 139 | Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, 140 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 141 | including, without limitation, any warranties or conditions of TITLE, 142 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 143 | solely responsible for determining the appropriateness of using or 144 | redistributing the Work and assume any risks associated with Your exercise of 145 | permissions under this License. 146 | 147 | #### 8. Limitation of Liability 148 | 149 | In no event and under no legal theory, whether in tort (including negligence), 150 | contract, or otherwise, unless required by applicable law (such as deliberate 151 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 152 | liable to You for damages, including any direct, indirect, special, incidental, 153 | or consequential damages of any character arising as a result of this License or 154 | out of the use or inability to use the Work (including but not limited to 155 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 156 | any and all other commercial damages or losses), even if such Contributor has 157 | been advised of the possibility of such damages. 158 | 159 | #### 9. Accepting Warranty or Additional Liability 160 | 161 | While redistributing the Work or Derivative Works thereof, You may choose to 162 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 163 | other liability obligations and/or rights consistent with this License. However, 164 | in accepting such obligations, You may act only on Your own behalf and on Your 165 | sole responsibility, not on behalf of any other Contributor, and only if You 166 | agree to indemnify, defend, and hold each Contributor harmless for any liability 167 | incurred by, or claims asserted against, such Contributor by reason of your 168 | accepting any such warranty or additional liability. 169 | 170 | _END OF TERMS AND CONDITIONS_ 171 | 172 | ### APPENDIX: How to apply the Apache License to your work 173 | 174 | To apply the Apache License to your work, attach the following boilerplate 175 | notice, with the fields enclosed by brackets `[]` replaced with your own 176 | identifying information. (Don't include the brackets!) The text should be 177 | enclosed in the appropriate comment syntax for the file format. We also 178 | recommend that a file or class name and description of purpose be included on 179 | the same “printed page” as the copyright notice for easier identification within 180 | third-party archives. 181 | 182 | Copyright [yyyy] [name of copyright owner] 183 | 184 | Licensed under the Apache License, Version 2.0 (the "License"); 185 | you may not use this file except in compliance with the License. 186 | You may obtain a copy of the License at 187 | 188 | http://www.apache.org/licenses/LICENSE-2.0 189 | 190 | Unless required by applicable law or agreed to in writing, software 191 | distributed under the License is distributed on an "AS IS" BASIS, 192 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 193 | See the License for the specific language governing permissions and 194 | limitations under the License. 195 | -------------------------------------------------------------------------------- /lib/connection.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Connection that wraps a socket and provides an interface to interact with 3 | * the Home Assistant websocket API. 4 | */ 5 | import * as messages from "./messages.js"; 6 | import { ERR_INVALID_AUTH, ERR_CONNECTION_LOST } from "./errors.js"; 7 | import { HassEvent, MessageBase } from "./types.js"; 8 | import { HaWebSocket } from "./socket.js"; 9 | import type { Auth } from "./auth.js"; 10 | 11 | const DEBUG = false; 12 | 13 | export type ConnectionOptions = { 14 | setupRetry: number; 15 | auth?: Auth; 16 | createSocket: (options: ConnectionOptions) => Promise; 17 | }; 18 | 19 | export type ConnectionEventListener = ( 20 | conn: Connection, 21 | eventData?: any, 22 | ) => void; 23 | 24 | type Events = "ready" | "disconnected" | "reconnect-error"; 25 | 26 | type WebSocketPongResponse = { 27 | id: number; 28 | type: "pong"; 29 | }; 30 | 31 | type WebSocketEventResponse = { 32 | id: number; 33 | type: "event"; 34 | event: HassEvent; 35 | }; 36 | 37 | type WebSocketResultResponse = { 38 | id: number; 39 | type: "result"; 40 | success: true; 41 | result: any; 42 | }; 43 | 44 | type WebSocketResultErrorResponse = { 45 | id: number; 46 | type: "result"; 47 | success: false; 48 | error: { 49 | code: string; 50 | message: string; 51 | }; 52 | }; 53 | 54 | type WebSocketResponse = 55 | | WebSocketPongResponse 56 | | WebSocketEventResponse 57 | | WebSocketResultResponse 58 | | WebSocketResultErrorResponse; 59 | 60 | type SubscriptionUnsubscribe = () => Promise; 61 | 62 | interface SubscribeEventCommmandInFlight { 63 | resolve: (result?: any) => void; 64 | reject: (err: any) => void; 65 | callback: (ev: T) => void; 66 | subscribe: (() => Promise) | undefined; 67 | unsubscribe: SubscriptionUnsubscribe; 68 | } 69 | 70 | type CommandWithAnswerInFlight = { 71 | resolve: (result?: any) => void; 72 | reject: (err: any) => void; 73 | }; 74 | 75 | type CommandInFlight = 76 | | SubscribeEventCommmandInFlight 77 | | CommandWithAnswerInFlight; 78 | 79 | export class Connection { 80 | options: ConnectionOptions; 81 | commandId: number; 82 | commands: Map; 83 | eventListeners: Map; 84 | closeRequested: boolean; 85 | suspendReconnectPromise?: Promise; 86 | 87 | oldSubscriptions?: Map; 88 | 89 | // We use this to queue messages in flight for the first reconnect 90 | // after the connection has been suspended. 91 | _queuedMessages?: Array<{ 92 | resolve: (value?: unknown) => unknown; 93 | reject?: (err: typeof ERR_CONNECTION_LOST) => unknown; 94 | }>; 95 | socket?: HaWebSocket; 96 | /** 97 | * Version string of the Home Assistant instance. Set to version of last connection while reconnecting. 98 | */ 99 | // @ts-ignore: incorrectly claiming it's not set in constructor. 100 | haVersion: string; 101 | 102 | constructor(socket: HaWebSocket, options: ConnectionOptions) { 103 | // connection options 104 | // - setupRetry: amount of ms to retry when unable to connect on initial setup 105 | // - createSocket: create a new Socket connection 106 | this.options = options; 107 | // id if next command to send 108 | this.commandId = 2; // socket may send 1 at the start to enable features 109 | // info about active subscriptions and commands in flight 110 | this.commands = new Map(); 111 | // map of event listeners 112 | this.eventListeners = new Map(); 113 | // true if a close is requested by the user 114 | this.closeRequested = false; 115 | 116 | this._setSocket(socket); 117 | } 118 | 119 | get connected() { 120 | // Using conn.socket.OPEN instead of WebSocket for better node support 121 | return ( 122 | this.socket !== undefined && this.socket.readyState == this.socket.OPEN 123 | ); 124 | } 125 | 126 | private _setSocket(socket: HaWebSocket) { 127 | this.socket = socket; 128 | this.haVersion = socket.haVersion; 129 | socket.addEventListener("message", this._handleMessage); 130 | socket.addEventListener("close", this._handleClose); 131 | 132 | const oldSubscriptions = this.oldSubscriptions; 133 | if (oldSubscriptions) { 134 | this.oldSubscriptions = undefined; 135 | oldSubscriptions.forEach((info) => { 136 | if ("subscribe" in info && info.subscribe) { 137 | info.subscribe().then((unsub) => { 138 | info.unsubscribe = unsub; 139 | // We need to resolve this in case it wasn't resolved yet. 140 | // This allows us to subscribe while we're disconnected 141 | // and recover properly. 142 | info.resolve(); 143 | }); 144 | } 145 | }); 146 | } 147 | const queuedMessages = this._queuedMessages; 148 | 149 | if (queuedMessages) { 150 | this._queuedMessages = undefined; 151 | for (const queuedMsg of queuedMessages) { 152 | queuedMsg.resolve(); 153 | } 154 | } 155 | 156 | this.fireEvent("ready"); 157 | } 158 | 159 | addEventListener(eventType: Events, callback: ConnectionEventListener) { 160 | let listeners = this.eventListeners.get(eventType); 161 | 162 | if (!listeners) { 163 | listeners = []; 164 | this.eventListeners.set(eventType, listeners); 165 | } 166 | 167 | listeners.push(callback); 168 | } 169 | 170 | removeEventListener(eventType: Events, callback: ConnectionEventListener) { 171 | const listeners = this.eventListeners.get(eventType); 172 | 173 | if (!listeners) { 174 | return; 175 | } 176 | 177 | const index = listeners.indexOf(callback); 178 | 179 | if (index !== -1) { 180 | listeners.splice(index, 1); 181 | } 182 | } 183 | 184 | fireEvent(eventType: Events, eventData?: any) { 185 | (this.eventListeners.get(eventType) || []).forEach((callback) => 186 | callback(this, eventData), 187 | ); 188 | } 189 | 190 | suspendReconnectUntil(suspendPromise: Promise) { 191 | this.suspendReconnectPromise = suspendPromise; 192 | } 193 | 194 | suspend() { 195 | if (!this.suspendReconnectPromise) { 196 | throw new Error("Suspend promise not set"); 197 | } 198 | if (this.socket) { 199 | this.socket.close(); 200 | } 201 | } 202 | 203 | /** 204 | * Reconnect the websocket connection. 205 | * @param force discard old socket instead of gracefully closing it. 206 | */ 207 | reconnect(force = false) { 208 | if (!this.socket) { 209 | return; 210 | } 211 | if (!force) { 212 | this.socket.close(); 213 | return; 214 | } 215 | this.socket.removeEventListener("message", this._handleMessage); 216 | this.socket.removeEventListener("close", this._handleClose); 217 | this.socket.close(); 218 | this._handleClose(); 219 | } 220 | 221 | close() { 222 | this.closeRequested = true; 223 | if (this.socket) { 224 | this.socket.close(); 225 | } 226 | } 227 | 228 | /** 229 | * Subscribe to a specific or all events. 230 | * 231 | * @param callback Callback to be called when a new event fires 232 | * @param eventType 233 | * @returns promise that resolves to an unsubscribe function 234 | */ 235 | async subscribeEvents( 236 | callback: (ev: EventType) => void, 237 | eventType?: string, 238 | ): Promise { 239 | return this.subscribeMessage(callback, messages.subscribeEvents(eventType)); 240 | } 241 | 242 | ping() { 243 | return this.sendMessagePromise(messages.ping()); 244 | } 245 | 246 | sendMessage(message: MessageBase, commandId?: number): void { 247 | if (!this.connected) { 248 | throw ERR_CONNECTION_LOST; 249 | } 250 | 251 | if (DEBUG) { 252 | console.log("Sending", message); 253 | } 254 | 255 | if (this._queuedMessages) { 256 | if (commandId) { 257 | throw new Error("Cannot queue with commandId"); 258 | } 259 | this._queuedMessages.push({ resolve: () => this.sendMessage(message) }); 260 | return; 261 | } 262 | 263 | if (!commandId) { 264 | commandId = this._genCmdId(); 265 | } 266 | message.id = commandId; 267 | 268 | this.socket!.send(JSON.stringify(message)); 269 | } 270 | 271 | sendMessagePromise(message: MessageBase): Promise { 272 | return new Promise((resolve, reject) => { 273 | if (this._queuedMessages) { 274 | this._queuedMessages!.push({ 275 | reject, 276 | resolve: async () => { 277 | try { 278 | resolve(await this.sendMessagePromise(message)); 279 | } catch (err) { 280 | reject(err); 281 | } 282 | }, 283 | }); 284 | return; 285 | } 286 | 287 | const commandId = this._genCmdId(); 288 | this.commands.set(commandId, { resolve, reject }); 289 | this.sendMessage(message, commandId); 290 | }); 291 | } 292 | 293 | /** 294 | * Call a websocket command that starts a subscription on the backend. 295 | * 296 | * @param message the message to start the subscription 297 | * @param callback the callback to be called when a new item arrives 298 | * @param [options.resubscribe] re-established a subscription after a reconnect. Defaults to true. 299 | * @returns promise that resolves to an unsubscribe function 300 | */ 301 | async subscribeMessage( 302 | callback: (result: Result) => void, 303 | subscribeMessage: MessageBase, 304 | options?: { 305 | resubscribe?: boolean; 306 | preCheck?: () => boolean | Promise; 307 | }, 308 | ): Promise { 309 | if (this._queuedMessages) { 310 | await new Promise((resolve, reject) => { 311 | this._queuedMessages!.push({ resolve, reject }); 312 | }); 313 | } 314 | 315 | if (options?.preCheck) { 316 | const precheck = await options.preCheck(); 317 | if (!precheck) { 318 | throw new Error("Pre-check failed"); 319 | } 320 | } 321 | 322 | let info: SubscribeEventCommmandInFlight; 323 | 324 | await new Promise((resolve, reject) => { 325 | // Command ID that will be used 326 | const commandId = this._genCmdId(); 327 | 328 | // We store unsubscribe on info object. That way we can overwrite it in case 329 | // we get disconnected and we have to subscribe again. 330 | info = { 331 | resolve, 332 | reject, 333 | callback, 334 | subscribe: 335 | options?.resubscribe !== false 336 | ? () => this.subscribeMessage(callback, subscribeMessage, options) 337 | : undefined, 338 | unsubscribe: async () => { 339 | // No need to unsubscribe if we're disconnected 340 | if (this.connected) { 341 | await this.sendMessagePromise( 342 | messages.unsubscribeEvents(commandId), 343 | ); 344 | } 345 | this.commands.delete(commandId); 346 | }, 347 | }; 348 | this.commands.set(commandId, info); 349 | 350 | try { 351 | this.sendMessage(subscribeMessage, commandId); 352 | } catch (err) { 353 | // Happens when the websocket is already closing. 354 | // Don't have to handle the error, reconnect logic will pick it up. 355 | } 356 | }); 357 | 358 | return () => info.unsubscribe(); 359 | } 360 | 361 | private _handleMessage = (event: MessageEvent) => { 362 | let messageGroup: WebSocketResponse | WebSocketResponse[] = JSON.parse( 363 | event.data, 364 | ); 365 | 366 | if (!Array.isArray(messageGroup)) { 367 | messageGroup = [messageGroup]; 368 | } 369 | 370 | messageGroup.forEach((message) => { 371 | if (DEBUG) { 372 | console.log("Received", message); 373 | } 374 | 375 | const info = this.commands.get(message.id); 376 | 377 | switch (message.type) { 378 | case "event": 379 | if (info) { 380 | (info as SubscribeEventCommmandInFlight).callback( 381 | message.event, 382 | ); 383 | } else { 384 | console.warn( 385 | `Received event for unknown subscription ${message.id}. Unsubscribing.`, 386 | ); 387 | this.sendMessagePromise( 388 | messages.unsubscribeEvents(message.id), 389 | ).catch((err) => { 390 | if (DEBUG) { 391 | console.warn( 392 | ` Error unsubsribing from unknown subscription ${message.id}`, 393 | err, 394 | ); 395 | } 396 | }); 397 | } 398 | break; 399 | 400 | case "result": 401 | // No info is fine. If just sendMessage is used, we did not store promise for result 402 | if (info) { 403 | if (message.success) { 404 | info.resolve(message.result); 405 | 406 | // Don't remove subscriptions. 407 | if (!("subscribe" in info)) { 408 | this.commands.delete(message.id); 409 | } 410 | } else { 411 | info.reject(message.error); 412 | this.commands.delete(message.id); 413 | } 414 | } 415 | break; 416 | 417 | case "pong": 418 | if (info) { 419 | info.resolve(); 420 | this.commands.delete(message.id); 421 | } else { 422 | console.warn(`Received unknown pong response ${message.id}`); 423 | } 424 | break; 425 | 426 | default: 427 | if (DEBUG) { 428 | console.warn("Unhandled message", message); 429 | } 430 | } 431 | }); 432 | }; 433 | 434 | private _handleClose = async () => { 435 | const oldCommands = this.commands; 436 | 437 | // reset to original state except haVersion 438 | this.commandId = 1; 439 | this.oldSubscriptions = this.commands; 440 | this.commands = new Map(); 441 | this.socket = undefined; 442 | 443 | // Reject in-flight sendMessagePromise requests 444 | oldCommands.forEach((info) => { 445 | // We don't cancel subscribeEvents commands in flight 446 | // as we will be able to recover them. 447 | if (!("subscribe" in info)) { 448 | info.reject(messages.error(ERR_CONNECTION_LOST, "Connection lost")); 449 | } 450 | }); 451 | 452 | if (this.closeRequested) { 453 | return; 454 | } 455 | 456 | this.fireEvent("disconnected"); 457 | 458 | // Disable setupRetry, we control it here with auto-backoff 459 | const options = { ...this.options, setupRetry: 0 }; 460 | 461 | const reconnect = (tries: number) => { 462 | setTimeout( 463 | async () => { 464 | if (this.closeRequested) { 465 | return; 466 | } 467 | if (DEBUG) { 468 | console.log("Trying to reconnect"); 469 | } 470 | try { 471 | const socket = await options.createSocket(options); 472 | this._setSocket(socket); 473 | } catch (err) { 474 | if (this._queuedMessages) { 475 | const queuedMessages = this._queuedMessages; 476 | this._queuedMessages = undefined; 477 | for (const msg of queuedMessages) { 478 | if (msg.reject) { 479 | msg.reject(ERR_CONNECTION_LOST); 480 | } 481 | } 482 | } 483 | if (err === ERR_INVALID_AUTH) { 484 | this.fireEvent("reconnect-error", err); 485 | } else { 486 | reconnect(tries + 1); 487 | } 488 | } 489 | }, 490 | Math.min(tries, 5) * 1000, 491 | ); 492 | }; 493 | 494 | if (this.suspendReconnectPromise) { 495 | await this.suspendReconnectPromise; 496 | this.suspendReconnectPromise = undefined; 497 | // For the first retry after suspend, we will queue up 498 | // all messages. 499 | this._queuedMessages = []; 500 | } 501 | 502 | reconnect(0); 503 | }; 504 | 505 | private _genCmdId() { 506 | return ++this.commandId; 507 | } 508 | } 509 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :aerial_tramway: JavaScript websocket client for Home Assistant 2 | 3 | This is a websocket client written in JavaScript that allows retrieving authentication tokens and communicate with the Home Assistant websocket API. It can be used to integrate Home Assistant into your apps. It has 0 dependencies. 4 | 5 | ## Trying it out 6 | 7 | Check [the demo](https://home-assistant.github.io/home-assistant-js-websocket/) ([source](https://github.com/home-assistant/home-assistant-js-websocket/blob/master/index.html)). 8 | 9 | Clone this repository, then go to home-assistant-js-websocket folder and run the following commands: 10 | 11 | ```bash 12 | yarn install 13 | yarn build 14 | npx http-server -o 15 | # A browser will open, navigate to example.html 16 | ``` 17 | 18 | ## Installation 19 | 20 | ```bash 21 | # With npm 22 | npm i home-assistant-js-websocket 23 | 24 | # With yarn 25 | yarn add home-assistant-js-websocket 26 | ``` 27 | 28 | ## Usage 29 | 30 | To initialize a connection, you need an authentication token for the instance that you want to connect to. This library implements the necessary steps to guide the user to authenticate your website with their Home Assistant instance and give you a token. All you need from the user is the url of their instance. 31 | 32 | ```js 33 | // Example connect code 34 | import { 35 | getAuth, 36 | createConnection, 37 | subscribeEntities, 38 | ERR_HASS_HOST_REQUIRED, 39 | } from "home-assistant-js-websocket"; 40 | 41 | async function connect() { 42 | let auth; 43 | try { 44 | // Try to pick up authentication after user logs in 45 | auth = await getAuth(); 46 | } catch (err) { 47 | if (err === ERR_HASS_HOST_REQUIRED) { 48 | const hassUrl = prompt( 49 | "What host to connect to?", 50 | "http://localhost:8123", 51 | ); 52 | // Redirect user to log in on their instance 53 | auth = await getAuth({ hassUrl }); 54 | } else { 55 | alert(`Unknown error: ${err}`); 56 | return; 57 | } 58 | } 59 | const connection = await createConnection({ auth }); 60 | subscribeEntities(connection, (ent) => console.log(ent)); 61 | } 62 | 63 | connect(); 64 | ``` 65 | 66 | ### `getAuth()` 67 | 68 | Use this method to get authentication from a server via OAuth2. This method will handle redirecting to an instance and fetching the token after the user successful logs in. 69 | 70 | You can pass options using the syntax: 71 | 72 | ```js 73 | getAuth({ hassUrl: "http://localhost:8123" }); 74 | ``` 75 | 76 | | Option | Description | 77 | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 78 | | hassUrl | The url where the Home Assistant instance can be reached. This option is needed so we know where to redirect the user for authentication. Once redirected back, it is not needed to pass this option in. | 79 | | clientId | Client ID to use. Client IDs for Home Assistant is the url of your application. Defaults to domain of current page. Pass `null` if you are making requests on behalf of a system user. | 80 | | redirectUrl | The url to redirect back to when the user has logged in. Defaults to current page. | 81 | | saveTokens | Function to store an object containing the token information. | 82 | | loadTokens | Function that returns a promise that resolves to previously stored token information object or undefined if no info available. | 83 | | authCode | If you have an auth code received via other means, you can pass it in and it will be used to fetch tokens instead of going through the OAuth2 flow. | 84 | | limitHassInstance | If set to true, allow only authentication credentials for the passed in `hassUrl` and `clientId`. Defaults to false. | 85 | 86 | In certain instances `getAuth` will raise an error. These errors can be imported from the package: 87 | 88 | ```js 89 | // When bundling your application 90 | import { 91 | ERR_HASS_HOST_REQUIRED, 92 | ERR_INVALID_AUTH, 93 | } from "home-assistant-js-websocket"; 94 | 95 | // When using the UMD build 96 | HAWS.ERR_HASS_HOST_REQUIRED; 97 | ``` 98 | 99 | | Error | Description | 100 | | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 101 | | `ERR_HASS_HOST_REQUIRED` | You need to pass in `hassUrl` to `getAuth` to continue getting auth. This option is not needed when the user is redirected back after successfully logging in. | 102 | | `ERR_INVALID_AUTH` | This error will be raised if the url contains an authorization code that is no longer valid. | 103 | | `ERR_INVALID_HTTPS_TO_HTTP` | This error is raised if your code is being run from a secure context (hosted via https) and you're trying to fetch tokens from a Home Assistant instance via a non secure context (http). This is called [mixed active content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content#Mixed_active_content) and the browser forbids this. | 104 | | `ERR_INVALID_AUTH_CALLBACK` | This error is raised if only credentials for the specified Home Assistant instance are allowed and the client ID or hassURL in the auth callback state do not match the expected ones. | 105 | | Other errors | Unknown error! | 106 | 107 | ### `createConnection()` 108 | 109 | You need to either provide `auth` or `createSocket` as options to createConnection: 110 | 111 | ```js 112 | createConnection({ auth }); 113 | ``` 114 | 115 | | Option | Description | 116 | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | 117 | | auth | Auth object to use to create a connection. | 118 | | createSocket | Override the createSocket method with your own. `(options) => Promise`. Needs to return a connection that is already authenticated. | 119 | | setupRetry | Number of times to retry initial connection when it fails. Set to -1 for infinite retries. Default is 0 (no retries) | 120 | 121 | Currently the following error codes can be raised by createConnection: 122 | 123 | | Error | Description | 124 | | ------------------ | --------------------------------------------------------- | 125 | | ERR_CANNOT_CONNECT | If the client was unable to connect to the websocket API. | 126 | | ERR_INVALID_AUTH | If the supplied authentication was invalid. | 127 | 128 | You can import them into your code as follows: 129 | 130 | ```javascript 131 | import { 132 | ERR_CANNOT_CONNECT, 133 | ERR_INVALID_AUTH, 134 | } from "home-assistant-js-websocket"; 135 | ``` 136 | 137 | ### Automatic reconnecting 138 | 139 | The connection object will automatically try to reconnect to the server when the connection gets lost. On reconnect, it will automatically resubscribe the event listeners. 140 | 141 | #### Suspend reconnection 142 | 143 | If you don't want to automatically try to reconnect to the server when the connection is lost, you can pass a promise to wait for. The connection will try to reconnect after the promise is resolved. 144 | 145 | ```javascript 146 | connection.suspendReconnectUntil( 147 | new Promise((resolve) => { 148 | // When you want to try to reconnect again, resolve the promise. 149 | resolve(); 150 | }), 151 | ); 152 | ``` 153 | 154 | When the suspend promise resolves until the connection is re-established, all messages being send will be delayed until the connection is established. If the first reconnect fails, the queued messages will be rejected. 155 | 156 | #### Suspend connection 157 | 158 | You can also actively close the connection and wait for a promise to resolve to reconnect again. This promise can be passed either with `suspendReconnectUntil` or with the `suspend` command itself. 159 | 160 | If you don't provide a promise with either of these functions, an error will be thrown. 161 | 162 | ```javascript 163 | connection.suspendReconnectUntil( 164 | new Promise((resolve) => { 165 | // When you want to try to reconnect again, resolve the promise. 166 | resolve(); 167 | }), 168 | ); 169 | connection.suspend(); 170 | ``` 171 | 172 | or 173 | 174 | ```javascript 175 | connection.suspend( 176 | new Promise((resolve) => { 177 | // When you want to try to reconnect again, resolve the promise. 178 | resolve(); 179 | }), 180 | ); 181 | ``` 182 | 183 | #### Close connection 184 | 185 | You can also close the connection, without any reconnect, with `connection.close()`. 186 | 187 | #### Events 188 | 189 | The `Connection` object implements three events related to the reconnecting logic. 190 | 191 | | Event | Data | Description | 192 | | --------------- | ---------- | -------------------------------------------------------------------------------------------------------- | 193 | | ready | - | Fired when authentication is successful and the connection is ready to take commands. | 194 | | disconnected | - | Fired when the connection is lost. | 195 | | reconnect-error | Error code | Fired when we encounter a fatal error when trying to reconnect. Currently limited to `ERR_INVALID_AUTH`. | 196 | 197 | You can attach and remove listeners as follows: 198 | 199 | ```javascript 200 | function eventHandler(connection, data) { 201 | console.log("Connection has been established again"); 202 | } 203 | 204 | conn.addEventListener("ready", eventHandler); 205 | conn.removeEventListener("ready", eventHandler); 206 | ``` 207 | 208 | ### Entities 209 | 210 | You can subscribe to the entities of Home Assistant. Your callback will be called when the entities are first loaded and on every change to the state of any of the entities after that. The callback will be called with a single object that contains the entities keyed by entity_id. 211 | 212 | The function `subscribeEntities` will return an unsubscribe function. 213 | 214 | ```javascript 215 | import { subscribeEntities } from "home-assistant-js-websocket"; 216 | 217 | // conn is the connection from earlier. 218 | subscribeEntities(conn, (entities) => console.log("New entities!", entities)); 219 | ``` 220 | 221 | You can also import the collection: 222 | 223 | ```javascript 224 | import { entitiesColl } from "home-assistant-js-websocket"; 225 | 226 | // conn is the connection from earlier. 227 | const coll = entitiesColl(conn); 228 | console.log(coll.state); 229 | await coll.refresh(); 230 | coll.subscribe((entities) => console.log(entities)); 231 | ``` 232 | 233 | ### Config 234 | 235 | You can subscribe to the config of Home Assistant. Config can change when either a component gets loaded. 236 | 237 | The function `subscribeConfig` will return an unsubscribe function. 238 | 239 | ```javascript 240 | import { subscribeConfig } from "home-assistant-js-websocket"; 241 | 242 | // conn is the connection from earlier. 243 | subscribeConfig(conn, (config) => console.log("New config!", config)); 244 | ``` 245 | 246 | You can also import the collection: 247 | 248 | ```javascript 249 | import { configColl } from "home-assistant-js-websocket"; 250 | 251 | // conn is the connection from earlier. 252 | const coll = configColl(connection); 253 | console.log(coll.state); 254 | await coll.refresh(); 255 | coll.subscribe((config) => console.log(config)); 256 | ``` 257 | 258 | ### Services 259 | 260 | You can subscribe to the available services of Home Assistant. Services can change when a new service gets registered or removed. 261 | 262 | The function `subscribeServices` will return an unsubscribe function. 263 | 264 | ```javascript 265 | import { subscribeServices } from "home-assistant-js-websocket"; 266 | 267 | // conn is the connection from earlier. 268 | subscribeServices(conn, (services) => console.log("New services!", services)); 269 | ``` 270 | 271 | You can also import the collection: 272 | 273 | ```javascript 274 | import { servicesColl } from "home-assistant-js-websocket"; 275 | 276 | // conn is the connection from earlier. 277 | const coll = servicesColl(connection); 278 | console.log(coll.state); 279 | await coll.refresh(); 280 | coll.subscribe((services) => console.log(services)); 281 | ``` 282 | 283 | ### Collections 284 | 285 | Besides entities, config and services you might want to create your own collections. A collection has the following features: 286 | 287 | - Fetch a full data set on initial creation and on reconnect 288 | - Subscribe to events to keep collection up to date 289 | - Share subscription between multiple listeners 290 | - Unsubscribe when no listeners 291 | - Manually trigger a refresh 292 | 293 | ```typescript 294 | // Will only initialize one collection per connection. 295 | getCollection( 296 | conn: Connection, 297 | key: string, 298 | fetchCollection: (conn: Connection) => Promise, 299 | subscribeUpdates: ( 300 | conn: Connection, 301 | store: Store 302 | ) => Promise, 303 | ): Collection 304 | 305 | // Returns object with following type 306 | class Collection { 307 | state: State; 308 | async refresh(): Promise; 309 | subscribe(subscriber: (state: State) => void): UnsubscribeFunc; 310 | } 311 | ``` 312 | 313 | - `conn` is the connection to subscribe to. 314 | - `key` a unique key for the collection 315 | - `fetchCollection` needs to return a Promsise that resolves to the full state 316 | - `subscribeUpdates` needs to subscribe to the updates and update the store. Returns a promise that resolves to an unsubscribe function. 317 | 318 | #### Collection Example 319 | 320 | ```javascript 321 | import { getCollection } from "home-assistant-js-websocket"; 322 | 323 | function panelRegistered(state, event) { 324 | // Returning null means no change. 325 | if (state === undefined) return null; 326 | 327 | // This will be merged with the existing state. 328 | return { 329 | panels: state.panels.concat(event.data.panel), 330 | }; 331 | } 332 | 333 | const fetchPanels = (conn) => conn.sendMessagePromise({ type: "get_panels" }); 334 | const subscribeUpdates = (conn, store) => 335 | conn.subscribeEvents(store.action(panelRegistered), "panel_registered"); 336 | 337 | const panelsColl = getCollection(conn, "_pnl", fetchPanels, subscribeUpdates); 338 | 339 | // Now use collection 340 | console.log(panelsColl.state); 341 | await panelsColl.refresh(); 342 | panelsColl.subscribe((panels) => console.log("New panels!", panels)); 343 | ``` 344 | 345 | Collections are useful to define if data is needed for initial data load. You can create a collection and have code on your page call it before you start rendering the UI. By the time UI is loaded, the data will be available to use. 346 | 347 | ## Connection API Reference 348 | 349 | A connection object is obtained by calling [`createConnection()`](#createconnection). 350 | 351 | ##### `conn.haVersion` 352 | 353 | String containing the current version of Home Assistant. Examples: 354 | 355 | - `0.107.0` 356 | - `0.107.0b1` 357 | - `0.107.0.dev0` 358 | - `2021.3.0` 359 | 360 | ##### `conn.subscribeEvents(eventCallback[, eventType])` 361 | 362 | Subscribe to all or specific events on the Home Assistant bus. Calls `eventCallback` for each event that gets received. 363 | 364 | Returns a promise that will resolve to a function that will cancel the subscription once called. 365 | 366 | Subscription will be automatically re-established after a reconnect. 367 | 368 | Uses `conn.subscribeMessage` under the hood. 369 | 370 | ##### `conn.addEventListener(eventType, listener)` 371 | 372 | Listen for events on the connection. [See docs.](#automatic-reconnecting) 373 | 374 | ##### `conn.sendMessagePromise(message)` 375 | 376 | Send a message to the server. Returns a promise that resolves or rejects based on the result of the server. Special case rejection is `ERR_CONNECTION_LOST` if the connection is lost while the command is in progress. 377 | 378 | ##### `conn.subscribeMessage(callback, subscribeMessage[, options])` 379 | 380 | Call an endpoint in Home Assistant that creates a subscription. Calls `callback` for each item that gets received. 381 | 382 | Returns a promise that will resolve to a function that will cancel the subscription once called. 383 | 384 | Subscription will be automatically re-established after a reconnect unless `options.resubscribe` is false. 385 | 386 | | Option | Description | 387 | | ----------- | ----------------------------------------------- | 388 | | resubscribe | Re-established a subscription after a reconnect | 389 | 390 | ## Auth API Reference 391 | 392 | An instance of Auth is returned from the `getAuth` method. It has the following properties: 393 | 394 | - `wsUrl`: the websocket url of the instance 395 | - `accessToken`: the access token 396 | - `expired`: boolean that indicates if the access token has expired 397 | 398 | ##### `auth.refreshAccessToken()` 399 | 400 | Fetches a new access token from the server. 401 | 402 | ##### `auth.revoke()` 403 | 404 | Makes a request to the server to revoke the refresh and all related access token. Returns a promise that resolves when the request is finished. 405 | 406 | **Note:** If you support storing and retrieving tokens, the returned auth object might load tokens from your cache that are no longer valid. If this happens, the promise returned by `createConnection` will reject with `ERR_INVALID_AUTH`. If that happens, clear your tokens with `storeTokens(null`) and call `getAuth` again. This will pick up the auth flow without relying on stored tokens. 407 | 408 | ## Error Reference 409 | 410 | | Error constant | Error number | 411 | | ------------------------- | ------------ | 412 | | ERR_CANNOT_CONNECT | 1 | 413 | | ERR_INVALID_AUTH | 2 | 414 | | ERR_CONNECTION_LOST | 3 | 415 | | ERR_HASS_HOST_REQUIRED | 4 | 416 | | ERR_INVALID_HTTPS_TO_HTTP | 5 | 417 | 418 | ## Other methods 419 | 420 | The library also contains a few helper method that you can use to ineract with the API. 421 | 422 | - `getUser(connection) -> Promise` 423 | - `callService(connection, domain, service, serviceData?, target?, returnResponse?) -> Promise` (Support for `target` was added in Home Assistant Core 2021.3) 424 | 425 | The following are also available, but it's recommended that you use the subscribe methods documented above. 426 | 427 | - `getStates(connection) -> Promise` 428 | - `getServices(connection) -> Promise` 429 | - `getConfig(connection) -> Promise` 430 | 431 | ## Using this with long-lived access tokens 432 | 433 | If you are in a browser, you should prefer to use the `getAuth()` flow. This will use the more secure refresh/access token pair. If that is not possible, you can ask the user to create a long-lived access token. 434 | 435 | You will need to create your own auth object if you want to use this library with a long-lived access token. 436 | 437 | ```js 438 | import { 439 | Auth, 440 | createConnection, 441 | subscribeEntities, 442 | createLongLivedTokenAuth, 443 | } from "home-assistant-js-websocket"; 444 | 445 | (async () => { 446 | const auth = createLongLivedTokenAuth( 447 | "http://localhost:8123", 448 | "YOUR ACCESS TOKEN", 449 | ); 450 | 451 | const connection = await createConnection({ auth }); 452 | subscribeEntities(connection, (entities) => console.log(entities)); 453 | })(); 454 | ``` 455 | 456 | ## Using this in NodeJS 457 | 458 | NodeJS does not have a WebSocket client built-in, but there are some good ones on NPM. We recommend ws. The easiest way to enable WebSocket is to polyfill it into the global namespace. 459 | Look at https://github.com/keesschollaart81/vscode-home-assistant/blob/master/src/language-service/src/home-assistant/socket.ts as an example using ws. 460 | 461 | If using TypeScript, you will need to install `@types/ws` as well. 462 | 463 | ```js 464 | globalThis.WebSocket = require("ws"); 465 | ``` 466 | 467 | or in TypeScript: 468 | 469 | ```ts 470 | const wnd = globalThis; 471 | wnd.WebSocket = require("ws"); 472 | ``` 473 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # This file is generated by running "yarn install" inside your project. 2 | # Manual changes might be lost - proceed with caution! 3 | 4 | __metadata: 5 | version: 8 6 | cacheKey: 10 7 | 8 | "@babel/code-frame@npm:^7.0.0": 9 | version: 7.12.13 10 | resolution: "@babel/code-frame@npm:7.12.13" 11 | dependencies: 12 | "@babel/highlight": "npm:^7.12.13" 13 | checksum: 10/f8f90562df4948c143ad5bf623fe7bc06be5fc5bb23f1d52212bbfe0fd802e78805e2d2daf3cd235967a171d191fc045c9d21aa4505d347c83beef6c751fd1ce 14 | languageName: node 15 | linkType: hard 16 | 17 | "@babel/helper-validator-identifier@npm:^7.12.11": 18 | version: 7.12.11 19 | resolution: "@babel/helper-validator-identifier@npm:7.12.11" 20 | checksum: 10/c1df067f90ab4c8446f83f2c740796225a3f043f5d3ac6b6d50a989b7bd2a437e5dc76495de345e060e5bafad76e78431cdc7cb77655c9dc6c2fdfc80a076fea 21 | languageName: node 22 | linkType: hard 23 | 24 | "@babel/highlight@npm:^7.12.13": 25 | version: 7.13.10 26 | resolution: "@babel/highlight@npm:7.13.10" 27 | dependencies: 28 | "@babel/helper-validator-identifier": "npm:^7.12.11" 29 | chalk: "npm:^2.0.0" 30 | js-tokens: "npm:^4.0.0" 31 | checksum: 10/2f33624c8e0947101fd72ca8d2af291cd9560bcb3ed63299e5f95a70e64c2a435922d915ede6760f30ff23942589fe42b962b6b8138f868abaa6f7abd4d4f5e9 32 | languageName: node 33 | linkType: hard 34 | 35 | "@cspotcode/source-map-support@npm:^0.8.0": 36 | version: 0.8.1 37 | resolution: "@cspotcode/source-map-support@npm:0.8.1" 38 | dependencies: 39 | "@jridgewell/trace-mapping": "npm:0.3.9" 40 | checksum: 10/b6e38a1712fab242c86a241c229cf562195aad985d0564bd352ac404be583029e89e93028ffd2c251d2c407ecac5fb0cbdca94a2d5c10f29ac806ede0508b3ff 41 | languageName: node 42 | linkType: hard 43 | 44 | "@isaacs/cliui@npm:^8.0.2": 45 | version: 8.0.2 46 | resolution: "@isaacs/cliui@npm:8.0.2" 47 | dependencies: 48 | string-width: "npm:^5.1.2" 49 | string-width-cjs: "npm:string-width@^4.2.0" 50 | strip-ansi: "npm:^7.0.1" 51 | strip-ansi-cjs: "npm:strip-ansi@^6.0.1" 52 | wrap-ansi: "npm:^8.1.0" 53 | wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" 54 | checksum: 10/e9ed5fd27c3aec1095e3a16e0c0cf148d1fee55a38665c35f7b3f86a9b5d00d042ddaabc98e8a1cb7463b9378c15f22a94eb35e99469c201453eb8375191f243 55 | languageName: node 56 | linkType: hard 57 | 58 | "@isaacs/fs-minipass@npm:^4.0.0": 59 | version: 4.0.1 60 | resolution: "@isaacs/fs-minipass@npm:4.0.1" 61 | dependencies: 62 | minipass: "npm:^7.0.4" 63 | checksum: 10/4412e9e6713c89c1e66d80bb0bb5a2a93192f10477623a27d08f228ba0316bb880affabc5bfe7f838f58a34d26c2c190da726e576cdfc18c49a72e89adabdcf5 64 | languageName: node 65 | linkType: hard 66 | 67 | "@jridgewell/resolve-uri@npm:^3.0.3": 68 | version: 3.1.2 69 | resolution: "@jridgewell/resolve-uri@npm:3.1.2" 70 | checksum: 10/97106439d750a409c22c8bff822d648f6a71f3aa9bc8e5129efdc36343cd3096ddc4eeb1c62d2fe48e9bdd4db37b05d4646a17114ecebd3bbcacfa2de51c3c1d 71 | languageName: node 72 | linkType: hard 73 | 74 | "@jridgewell/sourcemap-codec@npm:^1.4.10": 75 | version: 1.5.0 76 | resolution: "@jridgewell/sourcemap-codec@npm:1.5.0" 77 | checksum: 10/4ed6123217569a1484419ac53f6ea0d9f3b57e5b57ab30d7c267bdb27792a27eb0e4b08e84a2680aa55cc2f2b411ffd6ec3db01c44fdc6dc43aca4b55f8374fd 78 | languageName: node 79 | linkType: hard 80 | 81 | "@jridgewell/trace-mapping@npm:0.3.9": 82 | version: 0.3.9 83 | resolution: "@jridgewell/trace-mapping@npm:0.3.9" 84 | dependencies: 85 | "@jridgewell/resolve-uri": "npm:^3.0.3" 86 | "@jridgewell/sourcemap-codec": "npm:^1.4.10" 87 | checksum: 10/83deafb8e7a5ca98993c2c6eeaa93c270f6f647a4c0dc00deb38c9cf9b2d3b7bf15e8839540155247ef034a052c0ec4466f980bf0c9e2ab63b97d16c0cedd3ff 88 | languageName: node 89 | linkType: hard 90 | 91 | "@npmcli/agent@npm:^3.0.0": 92 | version: 3.0.0 93 | resolution: "@npmcli/agent@npm:3.0.0" 94 | dependencies: 95 | agent-base: "npm:^7.1.0" 96 | http-proxy-agent: "npm:^7.0.0" 97 | https-proxy-agent: "npm:^7.0.1" 98 | lru-cache: "npm:^10.0.1" 99 | socks-proxy-agent: "npm:^8.0.3" 100 | checksum: 10/775c9a7eb1f88c195dfb3bce70c31d0fe2a12b28b754e25c08a3edb4bc4816bfedb7ac64ef1e730579d078ca19dacf11630e99f8f3c3e0fd7b23caa5fd6d30a6 101 | languageName: node 102 | linkType: hard 103 | 104 | "@npmcli/fs@npm:^4.0.0": 105 | version: 4.0.0 106 | resolution: "@npmcli/fs@npm:4.0.0" 107 | dependencies: 108 | semver: "npm:^7.3.5" 109 | checksum: 10/405c4490e1ff11cf299775449a3c254a366a4b1ffc79d87159b0ee7d5558ac9f6a2f8c0735fd6ff3873cef014cb1a44a5f9127cb6a1b2dbc408718cca9365b5a 110 | languageName: node 111 | linkType: hard 112 | 113 | "@pkgjs/parseargs@npm:^0.11.0": 114 | version: 0.11.0 115 | resolution: "@pkgjs/parseargs@npm:0.11.0" 116 | checksum: 10/115e8ceeec6bc69dff2048b35c0ab4f8bbee12d8bb6c1f4af758604586d802b6e669dcb02dda61d078de42c2b4ddce41b3d9e726d7daa6b4b850f4adbf7333ff 117 | languageName: node 118 | linkType: hard 119 | 120 | "@rollup/rollup-android-arm-eabi@npm:4.53.3": 121 | version: 4.53.3 122 | resolution: "@rollup/rollup-android-arm-eabi@npm:4.53.3" 123 | conditions: os=android & cpu=arm 124 | languageName: node 125 | linkType: hard 126 | 127 | "@rollup/rollup-android-arm64@npm:4.53.3": 128 | version: 4.53.3 129 | resolution: "@rollup/rollup-android-arm64@npm:4.53.3" 130 | conditions: os=android & cpu=arm64 131 | languageName: node 132 | linkType: hard 133 | 134 | "@rollup/rollup-darwin-arm64@npm:4.53.3": 135 | version: 4.53.3 136 | resolution: "@rollup/rollup-darwin-arm64@npm:4.53.3" 137 | conditions: os=darwin & cpu=arm64 138 | languageName: node 139 | linkType: hard 140 | 141 | "@rollup/rollup-darwin-x64@npm:4.53.3": 142 | version: 4.53.3 143 | resolution: "@rollup/rollup-darwin-x64@npm:4.53.3" 144 | conditions: os=darwin & cpu=x64 145 | languageName: node 146 | linkType: hard 147 | 148 | "@rollup/rollup-freebsd-arm64@npm:4.53.3": 149 | version: 4.53.3 150 | resolution: "@rollup/rollup-freebsd-arm64@npm:4.53.3" 151 | conditions: os=freebsd & cpu=arm64 152 | languageName: node 153 | linkType: hard 154 | 155 | "@rollup/rollup-freebsd-x64@npm:4.53.3": 156 | version: 4.53.3 157 | resolution: "@rollup/rollup-freebsd-x64@npm:4.53.3" 158 | conditions: os=freebsd & cpu=x64 159 | languageName: node 160 | linkType: hard 161 | 162 | "@rollup/rollup-linux-arm-gnueabihf@npm:4.53.3": 163 | version: 4.53.3 164 | resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.53.3" 165 | conditions: os=linux & cpu=arm & libc=glibc 166 | languageName: node 167 | linkType: hard 168 | 169 | "@rollup/rollup-linux-arm-musleabihf@npm:4.53.3": 170 | version: 4.53.3 171 | resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.53.3" 172 | conditions: os=linux & cpu=arm & libc=musl 173 | languageName: node 174 | linkType: hard 175 | 176 | "@rollup/rollup-linux-arm64-gnu@npm:4.53.3": 177 | version: 4.53.3 178 | resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.53.3" 179 | conditions: os=linux & cpu=arm64 & libc=glibc 180 | languageName: node 181 | linkType: hard 182 | 183 | "@rollup/rollup-linux-arm64-musl@npm:4.53.3": 184 | version: 4.53.3 185 | resolution: "@rollup/rollup-linux-arm64-musl@npm:4.53.3" 186 | conditions: os=linux & cpu=arm64 & libc=musl 187 | languageName: node 188 | linkType: hard 189 | 190 | "@rollup/rollup-linux-loong64-gnu@npm:4.53.3": 191 | version: 4.53.3 192 | resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.53.3" 193 | conditions: os=linux & cpu=loong64 & libc=glibc 194 | languageName: node 195 | linkType: hard 196 | 197 | "@rollup/rollup-linux-ppc64-gnu@npm:4.53.3": 198 | version: 4.53.3 199 | resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.53.3" 200 | conditions: os=linux & cpu=ppc64 & libc=glibc 201 | languageName: node 202 | linkType: hard 203 | 204 | "@rollup/rollup-linux-riscv64-gnu@npm:4.53.3": 205 | version: 4.53.3 206 | resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.53.3" 207 | conditions: os=linux & cpu=riscv64 & libc=glibc 208 | languageName: node 209 | linkType: hard 210 | 211 | "@rollup/rollup-linux-riscv64-musl@npm:4.53.3": 212 | version: 4.53.3 213 | resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.53.3" 214 | conditions: os=linux & cpu=riscv64 & libc=musl 215 | languageName: node 216 | linkType: hard 217 | 218 | "@rollup/rollup-linux-s390x-gnu@npm:4.53.3": 219 | version: 4.53.3 220 | resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.53.3" 221 | conditions: os=linux & cpu=s390x & libc=glibc 222 | languageName: node 223 | linkType: hard 224 | 225 | "@rollup/rollup-linux-x64-gnu@npm:4.53.3": 226 | version: 4.53.3 227 | resolution: "@rollup/rollup-linux-x64-gnu@npm:4.53.3" 228 | conditions: os=linux & cpu=x64 & libc=glibc 229 | languageName: node 230 | linkType: hard 231 | 232 | "@rollup/rollup-linux-x64-musl@npm:4.53.3": 233 | version: 4.53.3 234 | resolution: "@rollup/rollup-linux-x64-musl@npm:4.53.3" 235 | conditions: os=linux & cpu=x64 & libc=musl 236 | languageName: node 237 | linkType: hard 238 | 239 | "@rollup/rollup-openharmony-arm64@npm:4.53.3": 240 | version: 4.53.3 241 | resolution: "@rollup/rollup-openharmony-arm64@npm:4.53.3" 242 | conditions: os=openharmony & cpu=arm64 243 | languageName: node 244 | linkType: hard 245 | 246 | "@rollup/rollup-win32-arm64-msvc@npm:4.53.3": 247 | version: 4.53.3 248 | resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.53.3" 249 | conditions: os=win32 & cpu=arm64 250 | languageName: node 251 | linkType: hard 252 | 253 | "@rollup/rollup-win32-ia32-msvc@npm:4.53.3": 254 | version: 4.53.3 255 | resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.53.3" 256 | conditions: os=win32 & cpu=ia32 257 | languageName: node 258 | linkType: hard 259 | 260 | "@rollup/rollup-win32-x64-gnu@npm:4.53.3": 261 | version: 4.53.3 262 | resolution: "@rollup/rollup-win32-x64-gnu@npm:4.53.3" 263 | conditions: os=win32 & cpu=x64 264 | languageName: node 265 | linkType: hard 266 | 267 | "@rollup/rollup-win32-x64-msvc@npm:4.53.3": 268 | version: 4.53.3 269 | resolution: "@rollup/rollup-win32-x64-msvc@npm:4.53.3" 270 | conditions: os=win32 & cpu=x64 271 | languageName: node 272 | linkType: hard 273 | 274 | "@tsconfig/node10@npm:^1.0.7": 275 | version: 1.0.11 276 | resolution: "@tsconfig/node10@npm:1.0.11" 277 | checksum: 10/51fe47d55fe1b80ec35e6e5ed30a13665fd3a531945350aa74a14a1e82875fb60b350c2f2a5e72a64831b1b6bc02acb6760c30b3738b54954ec2dea82db7a267 278 | languageName: node 279 | linkType: hard 280 | 281 | "@tsconfig/node12@npm:^1.0.7": 282 | version: 1.0.11 283 | resolution: "@tsconfig/node12@npm:1.0.11" 284 | checksum: 10/5ce29a41b13e7897a58b8e2df11269c5395999e588b9a467386f99d1d26f6c77d1af2719e407621412520ea30517d718d5192a32403b8dfcc163bf33e40a338a 285 | languageName: node 286 | linkType: hard 287 | 288 | "@tsconfig/node14@npm:^1.0.0": 289 | version: 1.0.3 290 | resolution: "@tsconfig/node14@npm:1.0.3" 291 | checksum: 10/19275fe80c4c8d0ad0abed6a96dbf00642e88b220b090418609c4376e1cef81bf16237bf170ad1b341452feddb8115d8dd2e5acdfdea1b27422071163dc9ba9d 292 | languageName: node 293 | linkType: hard 294 | 295 | "@tsconfig/node16@npm:^1.0.2": 296 | version: 1.0.4 297 | resolution: "@tsconfig/node16@npm:1.0.4" 298 | checksum: 10/202319785901f942a6e1e476b872d421baec20cf09f4b266a1854060efbf78cde16a4d256e8bc949d31e6cd9a90f1e8ef8fb06af96a65e98338a2b6b0de0a0ff 299 | languageName: node 300 | linkType: hard 301 | 302 | "@types/assert@npm:^1.4.7": 303 | version: 1.5.11 304 | resolution: "@types/assert@npm:1.5.11" 305 | checksum: 10/847365f27f46144957a800756c9a4fb96154fcd8467d581192f6c162a655c31ed7d4d8cae4f5cb23d52fa3e3a94fbd6236643ff963380be18fd5b8739be2af8f 306 | languageName: node 307 | linkType: hard 308 | 309 | "@types/estree@npm:1.0.8": 310 | version: 1.0.8 311 | resolution: "@types/estree@npm:1.0.8" 312 | checksum: 10/25a4c16a6752538ffde2826c2cc0c6491d90e69cd6187bef4a006dd2c3c45469f049e643d7e516c515f21484dc3d48fd5c870be158a5beb72f5baf3dc43e4099 313 | languageName: node 314 | linkType: hard 315 | 316 | "@types/mocha@npm:^10.0.9": 317 | version: 10.0.10 318 | resolution: "@types/mocha@npm:10.0.10" 319 | checksum: 10/4e3b61ed5112add86891a5dd3ebdd087714f5e1784a63d47a96424c0825058fd07074e85e43573462f751636c92808fc18a5f3862fe45e649ea98fdc5a3ee2ea 320 | languageName: node 321 | linkType: hard 322 | 323 | "@types/parse-json@npm:^4.0.0": 324 | version: 4.0.0 325 | resolution: "@types/parse-json@npm:4.0.0" 326 | checksum: 10/4df9de98150d2978afc2161482a3a8e6617883effba3223324f079de97ba7eabd7d84b90ced11c3f82b0c08d4a8383f678c9f73e9c41258f769b3fa234a2bb4f 327 | languageName: node 328 | linkType: hard 329 | 330 | "abbrev@npm:^3.0.0": 331 | version: 3.0.0 332 | resolution: "abbrev@npm:3.0.0" 333 | checksum: 10/2ceee14efdeda42ef7355178c1069499f183546ff7112b3efe79c1edef09d20ad9c17939752215fb8f7fcf48d10e6a7c0aa00136dc9cf4d293d963718bb1d200 334 | languageName: node 335 | linkType: hard 336 | 337 | "acorn-walk@npm:^8.1.1": 338 | version: 8.3.4 339 | resolution: "acorn-walk@npm:8.3.4" 340 | dependencies: 341 | acorn: "npm:^8.11.0" 342 | checksum: 10/871386764e1451c637bb8ab9f76f4995d408057e9909be6fb5ad68537ae3375d85e6a6f170b98989f44ab3ff6c74ad120bc2779a3d577606e7a0cd2b4efcaf77 343 | languageName: node 344 | linkType: hard 345 | 346 | "acorn@npm:^8.11.0, acorn@npm:^8.4.1": 347 | version: 8.14.1 348 | resolution: "acorn@npm:8.14.1" 349 | bin: 350 | acorn: bin/acorn 351 | checksum: 10/d1379bbee224e8d44c3c3946e6ba6973e999fbdd4e22e41c3455d7f9b6f72f7ce18d3dc218002e1e48eea789539cf1cb6d1430c81838c6744799c712fb557d92 352 | languageName: node 353 | linkType: hard 354 | 355 | "agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": 356 | version: 7.1.3 357 | resolution: "agent-base@npm:7.1.3" 358 | checksum: 10/3db6d8d4651f2aa1a9e4af35b96ab11a7607af57a24f3bc721a387eaa3b5f674e901f0a648b0caefd48f3fd117c7761b79a3b55854e2aebaa96c3f32cf76af84 359 | languageName: node 360 | linkType: hard 361 | 362 | "ansi-escapes@npm:^7.0.0": 363 | version: 7.0.0 364 | resolution: "ansi-escapes@npm:7.0.0" 365 | dependencies: 366 | environment: "npm:^1.0.0" 367 | checksum: 10/2d0e2345087bd7ae6bf122b9cc05ee35560d40dcc061146edcdc02bc2d7c7c50143cd12a22e69a0b5c0f62b948b7bc9a4539ee888b80f5bd33cdfd82d01a70ab 368 | languageName: node 369 | linkType: hard 370 | 371 | "ansi-regex@npm:^5.0.0, ansi-regex@npm:^5.0.1": 372 | version: 5.0.1 373 | resolution: "ansi-regex@npm:5.0.1" 374 | checksum: 10/2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b 375 | languageName: node 376 | linkType: hard 377 | 378 | "ansi-regex@npm:^6.0.1": 379 | version: 6.0.1 380 | resolution: "ansi-regex@npm:6.0.1" 381 | checksum: 10/1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 382 | languageName: node 383 | linkType: hard 384 | 385 | "ansi-styles@npm:^3.2.1": 386 | version: 3.2.1 387 | resolution: "ansi-styles@npm:3.2.1" 388 | dependencies: 389 | color-convert: "npm:^1.9.0" 390 | checksum: 10/d85ade01c10e5dd77b6c89f34ed7531da5830d2cb5882c645f330079975b716438cd7ebb81d0d6e6b4f9c577f19ae41ab55f07f19786b02f9dfd9e0377395665 391 | languageName: node 392 | linkType: hard 393 | 394 | "ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": 395 | version: 4.3.0 396 | resolution: "ansi-styles@npm:4.3.0" 397 | dependencies: 398 | color-convert: "npm:^2.0.1" 399 | checksum: 10/b4494dfbfc7e4591b4711a396bd27e540f8153914123dccb4cdbbcb514015ada63a3809f362b9d8d4f6b17a706f1d7bea3c6f974b15fa5ae76b5b502070889ff 400 | languageName: node 401 | linkType: hard 402 | 403 | "ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": 404 | version: 6.2.1 405 | resolution: "ansi-styles@npm:6.2.1" 406 | checksum: 10/70fdf883b704d17a5dfc9cde206e698c16bcd74e7f196ab821511651aee4f9f76c9514bdfa6ca3a27b5e49138b89cb222a28caf3afe4567570139577f991df32 407 | languageName: node 408 | linkType: hard 409 | 410 | "arg@npm:^4.1.0": 411 | version: 4.1.3 412 | resolution: "arg@npm:4.1.3" 413 | checksum: 10/969b491082f20cad166649fa4d2073ea9e974a4e5ac36247ca23d2e5a8b3cb12d60e9ff70a8acfe26d76566c71fd351ee5e6a9a6595157eb36f92b1fd64e1599 414 | languageName: node 415 | linkType: hard 416 | 417 | "argparse@npm:^2.0.1": 418 | version: 2.0.1 419 | resolution: "argparse@npm:2.0.1" 420 | checksum: 10/18640244e641a417ec75a9bd38b0b2b6b95af5199aa241b131d4b2fb206f334d7ecc600bd194861610a5579084978bfcbb02baa399dbe442d56d0ae5e60dbaef 421 | languageName: node 422 | linkType: hard 423 | 424 | "array-filter@npm:^1.0.0": 425 | version: 1.0.0 426 | resolution: "array-filter@npm:1.0.0" 427 | checksum: 10/93f8bf988b19971d0a3f1dc01366346038cbacc0417d35cdcfee41f17c6848fc7aa8cf30656f928292ecf81566bb1e03bc711c9a923cc5e41b75e732c9e263bb 428 | languageName: node 429 | linkType: hard 430 | 431 | "assert@npm:^2.0.0": 432 | version: 2.1.0 433 | resolution: "assert@npm:2.1.0" 434 | dependencies: 435 | call-bind: "npm:^1.0.2" 436 | is-nan: "npm:^1.3.2" 437 | object-is: "npm:^1.1.5" 438 | object.assign: "npm:^4.1.4" 439 | util: "npm:^0.12.5" 440 | checksum: 10/6b9d813c8eef1c0ac13feac5553972e4bd180ae16000d4eb5c0ded2489188737c75a5aacefc97a985008b37502f62fe1bad34da1a7481a54bbfabec3964c8aa7 441 | languageName: node 442 | linkType: hard 443 | 444 | "available-typed-arrays@npm:^1.0.2": 445 | version: 1.0.2 446 | resolution: "available-typed-arrays@npm:1.0.2" 447 | dependencies: 448 | array-filter: "npm:^1.0.0" 449 | checksum: 10/b09cb78cb7b9a16bdedadfbe9289aa965799761e131720f54df050335ed308baac98b21593ae45e817423354a93a17cbd466f909c27c3e78b8a11406ce63c09a 450 | languageName: node 451 | linkType: hard 452 | 453 | "balanced-match@npm:^1.0.0": 454 | version: 1.0.0 455 | resolution: "balanced-match@npm:1.0.0" 456 | checksum: 10/9b67bfe558772f40cf743a3469b48b286aecec2ea9fe80c48d74845e53aab1cef524fafedf123a63019b49ac397760573ef5f173f539423061f7217cbb5fbd40 457 | languageName: node 458 | linkType: hard 459 | 460 | "brace-expansion@npm:^2.0.1": 461 | version: 2.0.1 462 | resolution: "brace-expansion@npm:2.0.1" 463 | dependencies: 464 | balanced-match: "npm:^1.0.0" 465 | checksum: 10/a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 466 | languageName: node 467 | linkType: hard 468 | 469 | "braces@npm:^3.0.3": 470 | version: 3.0.3 471 | resolution: "braces@npm:3.0.3" 472 | dependencies: 473 | fill-range: "npm:^7.1.1" 474 | checksum: 10/fad11a0d4697a27162840b02b1fad249c1683cbc510cd5bf1a471f2f8085c046d41094308c577a50a03a579dd99d5a6b3724c4b5e8b14df2c4443844cfcda2c6 475 | languageName: node 476 | linkType: hard 477 | 478 | "browser-stdout@npm:^1.3.1": 479 | version: 1.3.1 480 | resolution: "browser-stdout@npm:1.3.1" 481 | checksum: 10/ac70a84e346bb7afc5045ec6f22f6a681b15a4057447d4cc1c48a25c6dedb302a49a46dd4ddfb5cdd9c96e0c905a8539be1b98ae7bc440512152967009ec7015 482 | languageName: node 483 | linkType: hard 484 | 485 | "cacache@npm:^19.0.1": 486 | version: 19.0.1 487 | resolution: "cacache@npm:19.0.1" 488 | dependencies: 489 | "@npmcli/fs": "npm:^4.0.0" 490 | fs-minipass: "npm:^3.0.0" 491 | glob: "npm:^10.2.2" 492 | lru-cache: "npm:^10.0.1" 493 | minipass: "npm:^7.0.3" 494 | minipass-collect: "npm:^2.0.1" 495 | minipass-flush: "npm:^1.0.5" 496 | minipass-pipeline: "npm:^1.2.4" 497 | p-map: "npm:^7.0.2" 498 | ssri: "npm:^12.0.0" 499 | tar: "npm:^7.4.3" 500 | unique-filename: "npm:^4.0.0" 501 | checksum: 10/ea026b27b13656330c2bbaa462a88181dcaa0435c1c2e705db89b31d9bdf7126049d6d0445ba746dca21454a0cfdf1d6f47fd39d34c8c8435296b30bc5738a13 502 | languageName: node 503 | linkType: hard 504 | 505 | "call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": 506 | version: 1.0.2 507 | resolution: "call-bind@npm:1.0.2" 508 | dependencies: 509 | function-bind: "npm:^1.1.1" 510 | get-intrinsic: "npm:^1.0.2" 511 | checksum: 10/ca787179c1cbe09e1697b56ad499fd05dc0ae6febe5081d728176ade699ea6b1589240cb1ff1fe11fcf9f61538c1af60ad37e8eb2ceb4ef21cd6085dfd3ccedd 512 | languageName: node 513 | linkType: hard 514 | 515 | "callsites@npm:^3.0.0": 516 | version: 3.1.0 517 | resolution: "callsites@npm:3.1.0" 518 | checksum: 10/072d17b6abb459c2ba96598918b55868af677154bec7e73d222ef95a8fdb9bbf7dae96a8421085cdad8cd190d86653b5b6dc55a4484f2e5b2e27d5e0c3fc15b3 519 | languageName: node 520 | linkType: hard 521 | 522 | "camelcase@npm:^6.0.0": 523 | version: 6.2.0 524 | resolution: "camelcase@npm:6.2.0" 525 | checksum: 10/8335cfd0ecc472eae685896a42afd8c9dacd193a91f569120b931c87deb053a1ba82102031b9b48a4dbc1d18066caeacf2e4ace8c3c7f0d02936d348dc0b5a87 526 | languageName: node 527 | linkType: hard 528 | 529 | "chalk@npm:^2.0.0": 530 | version: 2.4.2 531 | resolution: "chalk@npm:2.4.2" 532 | dependencies: 533 | ansi-styles: "npm:^3.2.1" 534 | escape-string-regexp: "npm:^1.0.5" 535 | supports-color: "npm:^5.3.0" 536 | checksum: 10/3d1d103433166f6bfe82ac75724951b33769675252d8417317363ef9d54699b7c3b2d46671b772b893a8e50c3ece70c4b933c73c01e81bc60ea4df9b55afa303 537 | languageName: node 538 | linkType: hard 539 | 540 | "chalk@npm:^4.0.0, chalk@npm:^4.1.0": 541 | version: 4.1.2 542 | resolution: "chalk@npm:4.1.2" 543 | dependencies: 544 | ansi-styles: "npm:^4.1.0" 545 | supports-color: "npm:^7.1.0" 546 | checksum: 10/cb3f3e594913d63b1814d7ca7c9bafbf895f75fbf93b92991980610dfd7b48500af4e3a5d4e3a8f337990a96b168d7eb84ee55efdce965e2ee8efc20f8c8f139 547 | languageName: node 548 | linkType: hard 549 | 550 | "chokidar@npm:^4.0.1": 551 | version: 4.0.3 552 | resolution: "chokidar@npm:4.0.3" 553 | dependencies: 554 | readdirp: "npm:^4.0.1" 555 | checksum: 10/bf2a575ea5596000e88f5db95461a9d59ad2047e939d5a4aac59dd472d126be8f1c1ff3c7654b477cf532d18f42a97279ef80ee847972fd2a25410bf00b80b59 556 | languageName: node 557 | linkType: hard 558 | 559 | "chownr@npm:^3.0.0": 560 | version: 3.0.0 561 | resolution: "chownr@npm:3.0.0" 562 | checksum: 10/b63cb1f73d171d140a2ed8154ee6566c8ab775d3196b0e03a2a94b5f6a0ce7777ee5685ca56849403c8d17bd457a6540672f9a60696a6137c7a409097495b82c 563 | languageName: node 564 | linkType: hard 565 | 566 | "ci-info@npm:^2.0.0": 567 | version: 2.0.0 568 | resolution: "ci-info@npm:2.0.0" 569 | checksum: 10/3b374666a85ea3ca43fa49aa3a048d21c9b475c96eb13c133505d2324e7ae5efd6a454f41efe46a152269e9b6a00c9edbe63ec7fa1921957165aae16625acd67 570 | languageName: node 571 | linkType: hard 572 | 573 | "cli-cursor@npm:^5.0.0": 574 | version: 5.0.0 575 | resolution: "cli-cursor@npm:5.0.0" 576 | dependencies: 577 | restore-cursor: "npm:^5.0.0" 578 | checksum: 10/1eb9a3f878b31addfe8d82c6d915ec2330cec8447ab1f117f4aa34f0137fbb3137ec3466e1c9a65bcb7557f6e486d343f2da57f253a2f668d691372dfa15c090 579 | languageName: node 580 | linkType: hard 581 | 582 | "cli-truncate@npm:^5.0.0": 583 | version: 5.1.0 584 | resolution: "cli-truncate@npm:5.1.0" 585 | dependencies: 586 | slice-ansi: "npm:^7.1.0" 587 | string-width: "npm:^8.0.0" 588 | checksum: 10/3a45844202d456548b371f6b99c5af50c43dc8cc7384378d754e5f36c302eb589c50dc6dbbd815c42411d37895d19ae88228062767e37439de55acd0de137f50 589 | languageName: node 590 | linkType: hard 591 | 592 | "cliui@npm:^8.0.1": 593 | version: 8.0.1 594 | resolution: "cliui@npm:8.0.1" 595 | dependencies: 596 | string-width: "npm:^4.2.0" 597 | strip-ansi: "npm:^6.0.1" 598 | wrap-ansi: "npm:^7.0.0" 599 | checksum: 10/eaa5561aeb3135c2cddf7a3b3f562fc4238ff3b3fc666869ef2adf264be0f372136702f16add9299087fb1907c2e4ec5dbfe83bd24bce815c70a80c6c1a2e950 600 | languageName: node 601 | linkType: hard 602 | 603 | "color-convert@npm:^1.9.0": 604 | version: 1.9.3 605 | resolution: "color-convert@npm:1.9.3" 606 | dependencies: 607 | color-name: "npm:1.1.3" 608 | checksum: 10/ffa319025045f2973919d155f25e7c00d08836b6b33ea2d205418c59bd63a665d713c52d9737a9e0fe467fb194b40fbef1d849bae80d674568ee220a31ef3d10 609 | languageName: node 610 | linkType: hard 611 | 612 | "color-convert@npm:^2.0.1": 613 | version: 2.0.1 614 | resolution: "color-convert@npm:2.0.1" 615 | dependencies: 616 | color-name: "npm:~1.1.4" 617 | checksum: 10/fa00c91b4332b294de06b443923246bccebe9fab1b253f7fe1772d37b06a2269b4039a85e309abe1fe11b267b11c08d1d0473fda3badd6167f57313af2887a64 618 | languageName: node 619 | linkType: hard 620 | 621 | "color-name@npm:1.1.3": 622 | version: 1.1.3 623 | resolution: "color-name@npm:1.1.3" 624 | checksum: 10/09c5d3e33d2105850153b14466501f2bfb30324a2f76568a408763a3b7433b0e50e5b4ab1947868e65cb101bb7cb75029553f2c333b6d4b8138a73fcc133d69d 625 | languageName: node 626 | linkType: hard 627 | 628 | "color-name@npm:~1.1.4": 629 | version: 1.1.4 630 | resolution: "color-name@npm:1.1.4" 631 | checksum: 10/b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 632 | languageName: node 633 | linkType: hard 634 | 635 | "colorette@npm:^2.0.20": 636 | version: 2.0.20 637 | resolution: "colorette@npm:2.0.20" 638 | checksum: 10/0b8de48bfa5d10afc160b8eaa2b9938f34a892530b2f7d7897e0458d9535a066e3998b49da9d21161c78225b272df19ae3a64d6df28b4c9734c0e55bbd02406f 639 | languageName: node 640 | linkType: hard 641 | 642 | "commander@npm:^14.0.2": 643 | version: 14.0.2 644 | resolution: "commander@npm:14.0.2" 645 | checksum: 10/2d202db5e5f9bb770112a3c1579b893d17ac6f6d932183077308bdd96d0f87f0bbe6a68b5b9ed2cf3b2514be6bb7de637480703c0e2db9741ee1b383237deb26 646 | languageName: node 647 | linkType: hard 648 | 649 | "compare-versions@npm:^3.6.0": 650 | version: 3.6.0 651 | resolution: "compare-versions@npm:3.6.0" 652 | checksum: 10/7492a50cdaa2c27f5254eee7c4b38856e1c164991bab3d98d7fd067fe4b570d47123ecb92523b78338be86aa221668fd3868bfe8caa5587dc3ebbe1a03d52b5d 653 | languageName: node 654 | linkType: hard 655 | 656 | "cosmiconfig@npm:^7.0.0": 657 | version: 7.0.1 658 | resolution: "cosmiconfig@npm:7.0.1" 659 | dependencies: 660 | "@types/parse-json": "npm:^4.0.0" 661 | import-fresh: "npm:^3.2.1" 662 | parse-json: "npm:^5.0.0" 663 | path-type: "npm:^4.0.0" 664 | yaml: "npm:^1.10.0" 665 | checksum: 10/861bf4c2c9e88e6c50f14278b25bb0509c484623de11fadf3788a3d543bc7c45178aeebeb6657293b12dc8bd1b86d926c5f25c803c4dc3821d628a1b24c3d20b 666 | languageName: node 667 | linkType: hard 668 | 669 | "create-require@npm:^1.1.0": 670 | version: 1.1.1 671 | resolution: "create-require@npm:1.1.1" 672 | checksum: 10/a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff 673 | languageName: node 674 | linkType: hard 675 | 676 | "cross-spawn@npm:^7.0.6": 677 | version: 7.0.6 678 | resolution: "cross-spawn@npm:7.0.6" 679 | dependencies: 680 | path-key: "npm:^3.1.0" 681 | shebang-command: "npm:^2.0.0" 682 | which: "npm:^2.0.1" 683 | checksum: 10/0d52657d7ae36eb130999dffff1168ec348687b48dd38e2ff59992ed916c88d328cf1d07ff4a4a10bc78de5e1c23f04b306d569e42f7a2293915c081e4dfee86 684 | languageName: node 685 | linkType: hard 686 | 687 | "debug@npm:4, debug@npm:^4.3.4, debug@npm:^4.3.5": 688 | version: 4.4.0 689 | resolution: "debug@npm:4.4.0" 690 | dependencies: 691 | ms: "npm:^2.1.3" 692 | peerDependenciesMeta: 693 | supports-color: 694 | optional: true 695 | checksum: 10/1847944c2e3c2c732514b93d11886575625686056cd765336212dc15de2d2b29612b6cd80e1afba767bb8e1803b778caf9973e98169ef1a24a7a7009e1820367 696 | languageName: node 697 | linkType: hard 698 | 699 | "decamelize@npm:^4.0.0": 700 | version: 4.0.0 701 | resolution: "decamelize@npm:4.0.0" 702 | checksum: 10/b7d09b82652c39eead4d6678bb578e3bebd848add894b76d0f6b395bc45b2d692fb88d977e7cfb93c4ed6c119b05a1347cef261174916c2e75c0a8ca57da1809 703 | languageName: node 704 | linkType: hard 705 | 706 | "define-properties@npm:^1.1.3, define-properties@npm:^1.1.4": 707 | version: 1.2.0 708 | resolution: "define-properties@npm:1.2.0" 709 | dependencies: 710 | has-property-descriptors: "npm:^1.0.0" 711 | object-keys: "npm:^1.1.1" 712 | checksum: 10/e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 713 | languageName: node 714 | linkType: hard 715 | 716 | "diff@npm:^4.0.1": 717 | version: 4.0.2 718 | resolution: "diff@npm:4.0.2" 719 | checksum: 10/ec09ec2101934ca5966355a229d77afcad5911c92e2a77413efda5455636c4cf2ce84057e2d7715227a2eeeda04255b849bd3ae3a4dd22eb22e86e76456df069 720 | languageName: node 721 | linkType: hard 722 | 723 | "diff@npm:^7.0.0": 724 | version: 7.0.0 725 | resolution: "diff@npm:7.0.0" 726 | checksum: 10/e9b8e48d054c9c0c093c65ce8e2637af94b35f2427001607b14e5e0589e534ea3413a7f91ebe6d7c5a1494ace49cb7c7c3972f442ddd96a4767ff091999a082e 727 | languageName: node 728 | linkType: hard 729 | 730 | "eastasianwidth@npm:^0.2.0": 731 | version: 0.2.0 732 | resolution: "eastasianwidth@npm:0.2.0" 733 | checksum: 10/9b1d3e1baefeaf7d70799db8774149cef33b97183a6addceeba0cf6b85ba23ee2686f302f14482006df32df75d32b17c509c143a3689627929e4a8efaf483952 734 | languageName: node 735 | linkType: hard 736 | 737 | "emoji-regex@npm:^10.3.0": 738 | version: 10.3.0 739 | resolution: "emoji-regex@npm:10.3.0" 740 | checksum: 10/b9b084ebe904f13bb4b66ee4c29fb41a7a4a1165adcc33c1ce8056c0194b882cc91ebdc782f1a779b5d7ea7375c5064643a7734893d7c657b44c5c6b9d7bf1e7 741 | languageName: node 742 | linkType: hard 743 | 744 | "emoji-regex@npm:^8.0.0": 745 | version: 8.0.0 746 | resolution: "emoji-regex@npm:8.0.0" 747 | checksum: 10/c72d67a6821be15ec11997877c437491c313d924306b8da5d87d2a2bcc2cec9903cb5b04ee1a088460501d8e5b44f10df82fdc93c444101a7610b80c8b6938e1 748 | languageName: node 749 | linkType: hard 750 | 751 | "emoji-regex@npm:^9.2.2": 752 | version: 9.2.2 753 | resolution: "emoji-regex@npm:9.2.2" 754 | checksum: 10/915acf859cea7131dac1b2b5c9c8e35c4849e325a1d114c30adb8cd615970f6dca0e27f64f3a4949d7d6ed86ecd79a1c5c63f02e697513cddd7b5835c90948b8 755 | languageName: node 756 | linkType: hard 757 | 758 | "encoding@npm:^0.1.13": 759 | version: 0.1.13 760 | resolution: "encoding@npm:0.1.13" 761 | dependencies: 762 | iconv-lite: "npm:^0.6.2" 763 | checksum: 10/bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f 764 | languageName: node 765 | linkType: hard 766 | 767 | "env-paths@npm:^2.2.0": 768 | version: 2.2.1 769 | resolution: "env-paths@npm:2.2.1" 770 | checksum: 10/65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e 771 | languageName: node 772 | linkType: hard 773 | 774 | "environment@npm:^1.0.0": 775 | version: 1.1.0 776 | resolution: "environment@npm:1.1.0" 777 | checksum: 10/dd3c1b9825e7f71f1e72b03c2344799ac73f2e9ef81b78ea8b373e55db021786c6b9f3858ea43a436a2c4611052670ec0afe85bc029c384cc71165feee2f4ba6 778 | languageName: node 779 | linkType: hard 780 | 781 | "err-code@npm:^2.0.2": 782 | version: 2.0.3 783 | resolution: "err-code@npm:2.0.3" 784 | checksum: 10/1d20d825cdcce8d811bfbe86340f4755c02655a7feb2f13f8c880566d9d72a3f6c92c192a6867632e490d6da67b678271f46e01044996a6443e870331100dfdd 785 | languageName: node 786 | linkType: hard 787 | 788 | "error-ex@npm:^1.3.1": 789 | version: 1.3.2 790 | resolution: "error-ex@npm:1.3.2" 791 | dependencies: 792 | is-arrayish: "npm:^0.2.1" 793 | checksum: 10/d547740aa29c34e753fb6fed2c5de81802438529c12b3673bd37b6bb1fe49b9b7abdc3c11e6062fe625d8a296b3cf769a80f878865e25e685f787763eede3ffb 794 | languageName: node 795 | linkType: hard 796 | 797 | "es-abstract@npm:^1.18.0-next.1, es-abstract@npm:^1.18.0-next.2": 798 | version: 1.18.0 799 | resolution: "es-abstract@npm:1.18.0" 800 | dependencies: 801 | call-bind: "npm:^1.0.2" 802 | es-to-primitive: "npm:^1.2.1" 803 | function-bind: "npm:^1.1.1" 804 | get-intrinsic: "npm:^1.1.1" 805 | has: "npm:^1.0.3" 806 | has-symbols: "npm:^1.0.2" 807 | is-callable: "npm:^1.2.3" 808 | is-negative-zero: "npm:^2.0.1" 809 | is-regex: "npm:^1.1.2" 810 | is-string: "npm:^1.0.5" 811 | object-inspect: "npm:^1.9.0" 812 | object-keys: "npm:^1.1.1" 813 | object.assign: "npm:^4.1.2" 814 | string.prototype.trimend: "npm:^1.0.4" 815 | string.prototype.trimstart: "npm:^1.0.4" 816 | unbox-primitive: "npm:^1.0.0" 817 | checksum: 10/98b2dd3778d0bc36b86302603681f26432aad85d2019834a09d5221ca350600eb4b1e95915d442644cfcc422d5996a806c13ca30435655150f4a4e081b5960cb 818 | languageName: node 819 | linkType: hard 820 | 821 | "es-to-primitive@npm:^1.2.1": 822 | version: 1.2.1 823 | resolution: "es-to-primitive@npm:1.2.1" 824 | dependencies: 825 | is-callable: "npm:^1.1.4" 826 | is-date-object: "npm:^1.0.1" 827 | is-symbol: "npm:^1.0.2" 828 | checksum: 10/74aeeefe2714cf99bb40cab7ce3012d74e1e2c1bd60d0a913b467b269edde6e176ca644b5ba03a5b865fb044a29bca05671cd445c85ca2cdc2de155d7fc8fe9b 829 | languageName: node 830 | linkType: hard 831 | 832 | "escalade@npm:^3.1.1": 833 | version: 3.1.1 834 | resolution: "escalade@npm:3.1.1" 835 | checksum: 10/afa618e73362576b63f6ca83c975456621095a1ed42ff068174e3f5cea48afc422814dda548c96e6ebb5333e7265140c7292abcc81bbd6ccb1757d50d3a4e182 836 | languageName: node 837 | linkType: hard 838 | 839 | "escape-string-regexp@npm:^1.0.5": 840 | version: 1.0.5 841 | resolution: "escape-string-regexp@npm:1.0.5" 842 | checksum: 10/6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 843 | languageName: node 844 | linkType: hard 845 | 846 | "escape-string-regexp@npm:^4.0.0": 847 | version: 4.0.0 848 | resolution: "escape-string-regexp@npm:4.0.0" 849 | checksum: 10/98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5 850 | languageName: node 851 | linkType: hard 852 | 853 | "eventemitter3@npm:^5.0.1": 854 | version: 5.0.1 855 | resolution: "eventemitter3@npm:5.0.1" 856 | checksum: 10/ac6423ec31124629c84c7077eed1e6987f6d66c31cf43c6fcbf6c87791d56317ce808d9ead483652436df171b526fc7220eccdc9f3225df334e81582c3cf7dd5 857 | languageName: node 858 | linkType: hard 859 | 860 | "exponential-backoff@npm:^3.1.1": 861 | version: 3.1.2 862 | resolution: "exponential-backoff@npm:3.1.2" 863 | checksum: 10/ca2f01f1aa4dafd3f3917bd531ab5be08c6f5f4b2389d2e974f903de3cbeb50b9633374353516b6afd70905775e33aba11afab1232d3acf0aa2963b98a611c51 864 | languageName: node 865 | linkType: hard 866 | 867 | "fill-range@npm:^7.1.1": 868 | version: 7.1.1 869 | resolution: "fill-range@npm:7.1.1" 870 | dependencies: 871 | to-regex-range: "npm:^5.0.1" 872 | checksum: 10/a7095cb39e5bc32fada2aa7c7249d3f6b01bd1ce461a61b0adabacccabd9198500c6fb1f68a7c851a657e273fce2233ba869638897f3d7ed2e87a2d89b4436ea 873 | languageName: node 874 | linkType: hard 875 | 876 | "find-up@npm:^5.0.0": 877 | version: 5.0.0 878 | resolution: "find-up@npm:5.0.0" 879 | dependencies: 880 | locate-path: "npm:^6.0.0" 881 | path-exists: "npm:^4.0.0" 882 | checksum: 10/07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095 883 | languageName: node 884 | linkType: hard 885 | 886 | "find-versions@npm:^4.0.0": 887 | version: 4.0.0 888 | resolution: "find-versions@npm:4.0.0" 889 | dependencies: 890 | semver-regex: "npm:^3.1.2" 891 | checksum: 10/2b4c749dc33e3fa73a457ca4df616ac13b4b32c53f6297bc862b0814d402a6cfec93a0d308d5502eeb47f2c125906e0f861bf01b756f08395640892186357711 892 | languageName: node 893 | linkType: hard 894 | 895 | "flat@npm:^5.0.2": 896 | version: 5.0.2 897 | resolution: "flat@npm:5.0.2" 898 | bin: 899 | flat: cli.js 900 | checksum: 10/72479e651c15eab53e25ce04c31bab18cfaac0556505cac19221dbbe85bbb9686bc76e4d397e89e5bf516ce667dcf818f8b07e585568edba55abc2bf1f698fb5 901 | languageName: node 902 | linkType: hard 903 | 904 | "foreach@npm:^2.0.5": 905 | version: 2.0.5 906 | resolution: "foreach@npm:2.0.5" 907 | checksum: 10/3962224ad3343019aab128cbfd11ba8e17ef8a67de09d2b217651f097a038294dcf641e07ebeae2b715e1a5e81bd212ebacae323950a3c28bc340a4c5955c032 908 | languageName: node 909 | linkType: hard 910 | 911 | "foreground-child@npm:^3.1.0": 912 | version: 3.3.1 913 | resolution: "foreground-child@npm:3.3.1" 914 | dependencies: 915 | cross-spawn: "npm:^7.0.6" 916 | signal-exit: "npm:^4.0.1" 917 | checksum: 10/427b33f997a98073c0424e5c07169264a62cda806d8d2ded159b5b903fdfc8f0a1457e06b5fc35506497acb3f1e353f025edee796300209ac6231e80edece835 918 | languageName: node 919 | linkType: hard 920 | 921 | "fs-minipass@npm:^3.0.0": 922 | version: 3.0.3 923 | resolution: "fs-minipass@npm:3.0.3" 924 | dependencies: 925 | minipass: "npm:^7.0.3" 926 | checksum: 10/af143246cf6884fe26fa281621d45cfe111d34b30535a475bfa38dafe343dadb466c047a924ffc7d6b7b18265df4110224ce3803806dbb07173bf2087b648d7f 927 | languageName: node 928 | linkType: hard 929 | 930 | "fsevents@npm:~2.3.2": 931 | version: 2.3.2 932 | resolution: "fsevents@npm:2.3.2" 933 | dependencies: 934 | node-gyp: "npm:latest" 935 | checksum: 10/6b5b6f5692372446ff81cf9501c76e3e0459a4852b3b5f1fc72c103198c125a6b8c72f5f166bdd76ffb2fca261e7f6ee5565daf80dca6e571e55bcc589cc1256 936 | conditions: os=darwin 937 | languageName: node 938 | linkType: hard 939 | 940 | "fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": 941 | version: 2.3.2 942 | resolution: "fsevents@patch:fsevents@npm%3A2.3.2#optional!builtin::version=2.3.2&hash=df0bf1" 943 | dependencies: 944 | node-gyp: "npm:latest" 945 | conditions: os=darwin 946 | languageName: node 947 | linkType: hard 948 | 949 | "function-bind@npm:^1.1.1": 950 | version: 1.1.1 951 | resolution: "function-bind@npm:1.1.1" 952 | checksum: 10/d83f2968030678f0b8c3f2183d63dcd969344eb8b55b4eb826a94ccac6de8b87c95bebffda37a6386c74f152284eb02956ff2c496897f35d32bdc2628ac68ac5 953 | languageName: node 954 | linkType: hard 955 | 956 | "get-caller-file@npm:^2.0.5": 957 | version: 2.0.5 958 | resolution: "get-caller-file@npm:2.0.5" 959 | checksum: 10/b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9 960 | languageName: node 961 | linkType: hard 962 | 963 | "get-east-asian-width@npm:^1.0.0": 964 | version: 1.2.0 965 | resolution: "get-east-asian-width@npm:1.2.0" 966 | checksum: 10/c9b280e7c7c67fb89fa17e867c4a9d1c9f1321aba2a9ee27bff37fb6ca9552bccda328c70a80c1f83a0e39ba1b7e3427e60f47823402d19e7a41b83417ec047a 967 | languageName: node 968 | linkType: hard 969 | 970 | "get-east-asian-width@npm:^1.3.0": 971 | version: 1.4.0 972 | resolution: "get-east-asian-width@npm:1.4.0" 973 | checksum: 10/c9ae85bfc2feaf4cc71cdb236e60f1757ae82281964c206c6aa89a25f1987d326ddd8b0de9f9ccd56e37711b9fcd988f7f5137118b49b0b45e19df93c3be8f45 974 | languageName: node 975 | linkType: hard 976 | 977 | "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1": 978 | version: 1.1.1 979 | resolution: "get-intrinsic@npm:1.1.1" 980 | dependencies: 981 | function-bind: "npm:^1.1.1" 982 | has: "npm:^1.0.3" 983 | has-symbols: "npm:^1.0.1" 984 | checksum: 10/7143f5407b000473f4b62717a79628dc151aa622eadac682da0ea3d377fc45839b3ea203d0956d72f6cc8c1f6ae0dcd47fb4bd970647ba5234f9e11679f86cb5 985 | languageName: node 986 | linkType: hard 987 | 988 | "glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7, glob@npm:^10.4.5": 989 | version: 10.5.0 990 | resolution: "glob@npm:10.5.0" 991 | dependencies: 992 | foreground-child: "npm:^3.1.0" 993 | jackspeak: "npm:^3.1.2" 994 | minimatch: "npm:^9.0.4" 995 | minipass: "npm:^7.1.2" 996 | package-json-from-dist: "npm:^1.0.0" 997 | path-scurry: "npm:^1.11.1" 998 | bin: 999 | glob: dist/esm/bin.mjs 1000 | checksum: 10/ab3bccfefcc0afaedbd1f480cd0c4a2c0e322eb3f0aa7ceaa31b3f00b825069f17cf0f1fc8b6f256795074b903f37c0ade37ddda6a176aa57f1c2bbfe7240653 1001 | languageName: node 1002 | linkType: hard 1003 | 1004 | "graceful-fs@npm:^4.2.6": 1005 | version: 4.2.11 1006 | resolution: "graceful-fs@npm:4.2.11" 1007 | checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2 1008 | languageName: node 1009 | linkType: hard 1010 | 1011 | "has-bigints@npm:^1.0.1": 1012 | version: 1.0.1 1013 | resolution: "has-bigints@npm:1.0.1" 1014 | checksum: 10/44ab55868174470065d2e0f8f6def1c990d12b82162a8803c679699fa8a39f966e336f2a33c185092fe8aea7e8bf2e85f1c26add5f29d98f2318bd270096b183 1015 | languageName: node 1016 | linkType: hard 1017 | 1018 | "has-flag@npm:^3.0.0": 1019 | version: 3.0.0 1020 | resolution: "has-flag@npm:3.0.0" 1021 | checksum: 10/4a15638b454bf086c8148979aae044dd6e39d63904cd452d970374fa6a87623423da485dfb814e7be882e05c096a7ccf1ebd48e7e7501d0208d8384ff4dea73b 1022 | languageName: node 1023 | linkType: hard 1024 | 1025 | "has-flag@npm:^4.0.0": 1026 | version: 4.0.0 1027 | resolution: "has-flag@npm:4.0.0" 1028 | checksum: 10/261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad 1029 | languageName: node 1030 | linkType: hard 1031 | 1032 | "has-property-descriptors@npm:^1.0.0": 1033 | version: 1.0.0 1034 | resolution: "has-property-descriptors@npm:1.0.0" 1035 | dependencies: 1036 | get-intrinsic: "npm:^1.1.1" 1037 | checksum: 10/a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb 1038 | languageName: node 1039 | linkType: hard 1040 | 1041 | "has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": 1042 | version: 1.0.3 1043 | resolution: "has-symbols@npm:1.0.3" 1044 | checksum: 10/464f97a8202a7690dadd026e6d73b1ceeddd60fe6acfd06151106f050303eaa75855aaa94969df8015c11ff7c505f196114d22f7386b4a471038da5874cf5e9b 1045 | languageName: node 1046 | linkType: hard 1047 | 1048 | "has@npm:^1.0.3": 1049 | version: 1.0.3 1050 | resolution: "has@npm:1.0.3" 1051 | dependencies: 1052 | function-bind: "npm:^1.1.1" 1053 | checksum: 10/a449f3185b1d165026e8d25f6a8c3390bd25c201ff4b8c1aaf948fc6a5fcfd6507310b8c00c13a3325795ea9791fcc3d79d61eafa313b5750438fc19183df57b 1054 | languageName: node 1055 | linkType: hard 1056 | 1057 | "he@npm:^1.2.0": 1058 | version: 1.2.0 1059 | resolution: "he@npm:1.2.0" 1060 | bin: 1061 | he: bin/he 1062 | checksum: 10/d09b2243da4e23f53336e8de3093e5c43d2c39f8d0d18817abfa32ce3e9355391b2edb4bb5edc376aea5d4b0b59d6a0482aab4c52bc02ef95751e4b818e847f1 1063 | languageName: node 1064 | linkType: hard 1065 | 1066 | "home-assistant-js-websocket@workspace:.": 1067 | version: 0.0.0-use.local 1068 | resolution: "home-assistant-js-websocket@workspace:." 1069 | dependencies: 1070 | "@types/assert": "npm:^1.4.7" 1071 | "@types/mocha": "npm:^10.0.9" 1072 | assert: "npm:^2.0.0" 1073 | husky: "npm:^4.2.5" 1074 | lint-staged: "npm:^16.0.0" 1075 | mocha: "npm:^11.1.0" 1076 | prettier: "npm:^3.0.0" 1077 | rollup: "npm:^4.3.0" 1078 | ts-node: "npm:^10.9.2" 1079 | typescript: "npm:^5.2.2" 1080 | languageName: unknown 1081 | linkType: soft 1082 | 1083 | "http-cache-semantics@npm:^4.1.1": 1084 | version: 4.1.1 1085 | resolution: "http-cache-semantics@npm:4.1.1" 1086 | checksum: 10/362d5ed66b12ceb9c0a328fb31200b590ab1b02f4a254a697dc796850cc4385603e75f53ec59f768b2dad3bfa1464bd229f7de278d2899a0e3beffc634b6683f 1087 | languageName: node 1088 | linkType: hard 1089 | 1090 | "http-proxy-agent@npm:^7.0.0": 1091 | version: 7.0.2 1092 | resolution: "http-proxy-agent@npm:7.0.2" 1093 | dependencies: 1094 | agent-base: "npm:^7.1.0" 1095 | debug: "npm:^4.3.4" 1096 | checksum: 10/d062acfa0cb82beeb558f1043c6ba770ea892b5fb7b28654dbc70ea2aeea55226dd34c02a294f6c1ca179a5aa483c4ea641846821b182edbd9cc5d89b54c6848 1097 | languageName: node 1098 | linkType: hard 1099 | 1100 | "https-proxy-agent@npm:^7.0.1": 1101 | version: 7.0.6 1102 | resolution: "https-proxy-agent@npm:7.0.6" 1103 | dependencies: 1104 | agent-base: "npm:^7.1.2" 1105 | debug: "npm:4" 1106 | checksum: 10/784b628cbd55b25542a9d85033bdfd03d4eda630fb8b3c9477959367f3be95dc476ed2ecbb9836c359c7c698027fc7b45723a302324433590f45d6c1706e8c13 1107 | languageName: node 1108 | linkType: hard 1109 | 1110 | "husky@npm:^4.2.5": 1111 | version: 4.3.8 1112 | resolution: "husky@npm:4.3.8" 1113 | dependencies: 1114 | chalk: "npm:^4.0.0" 1115 | ci-info: "npm:^2.0.0" 1116 | compare-versions: "npm:^3.6.0" 1117 | cosmiconfig: "npm:^7.0.0" 1118 | find-versions: "npm:^4.0.0" 1119 | opencollective-postinstall: "npm:^2.0.2" 1120 | pkg-dir: "npm:^5.0.0" 1121 | please-upgrade-node: "npm:^3.2.0" 1122 | slash: "npm:^3.0.0" 1123 | which-pm-runs: "npm:^1.0.0" 1124 | bin: 1125 | husky-run: bin/run.js 1126 | husky-upgrade: lib/upgrader/bin.js 1127 | checksum: 10/bf525b1133ac68131a50acfea1b6348ed4b3230ff471492852edd97f3038e858ccf075eac431db47c27d928638a108eef70bc3940f289f5a3ac0eca20808969a 1128 | languageName: node 1129 | linkType: hard 1130 | 1131 | "iconv-lite@npm:^0.6.2": 1132 | version: 0.6.3 1133 | resolution: "iconv-lite@npm:0.6.3" 1134 | dependencies: 1135 | safer-buffer: "npm:>= 2.1.2 < 3.0.0" 1136 | checksum: 10/24e3292dd3dadaa81d065c6f8c41b274a47098150d444b96e5f53b4638a9a71482921ea6a91a1f59bb71d9796de25e04afd05919fa64c360347ba65d3766f10f 1137 | languageName: node 1138 | linkType: hard 1139 | 1140 | "import-fresh@npm:^3.2.1": 1141 | version: 3.3.0 1142 | resolution: "import-fresh@npm:3.3.0" 1143 | dependencies: 1144 | parent-module: "npm:^1.0.0" 1145 | resolve-from: "npm:^4.0.0" 1146 | checksum: 10/2cacfad06e652b1edc50be650f7ec3be08c5e5a6f6d12d035c440a42a8cc028e60a5b99ca08a77ab4d6b1346da7d971915828f33cdab730d3d42f08242d09baa 1147 | languageName: node 1148 | linkType: hard 1149 | 1150 | "imurmurhash@npm:^0.1.4": 1151 | version: 0.1.4 1152 | resolution: "imurmurhash@npm:0.1.4" 1153 | checksum: 10/2d30b157a91fe1c1d7c6f653cbf263f039be6c5bfa959245a16d4ee191fc0f2af86c08545b6e6beeb041c56b574d2d5b9f95343d378ab49c0f37394d541e7fc8 1154 | languageName: node 1155 | linkType: hard 1156 | 1157 | "inherits@npm:^2.0.3": 1158 | version: 2.0.4 1159 | resolution: "inherits@npm:2.0.4" 1160 | checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521 1161 | languageName: node 1162 | linkType: hard 1163 | 1164 | "ip-address@npm:^9.0.5": 1165 | version: 9.0.5 1166 | resolution: "ip-address@npm:9.0.5" 1167 | dependencies: 1168 | jsbn: "npm:1.1.0" 1169 | sprintf-js: "npm:^1.1.3" 1170 | checksum: 10/1ed81e06721af012306329b31f532b5e24e00cb537be18ddc905a84f19fe8f83a09a1699862bf3a1ec4b9dea93c55a3fa5faf8b5ea380431469df540f38b092c 1171 | languageName: node 1172 | linkType: hard 1173 | 1174 | "is-arguments@npm:^1.0.4": 1175 | version: 1.1.0 1176 | resolution: "is-arguments@npm:1.1.0" 1177 | dependencies: 1178 | call-bind: "npm:^1.0.0" 1179 | checksum: 10/c8863b9147e457b612cc5125452cb41aaaea0f7ce69b6f5f930be73dd37170d18d828b8a5a0cfa3f36a76d17b976aad9055b087185711a97711499fe930809e4 1180 | languageName: node 1181 | linkType: hard 1182 | 1183 | "is-arrayish@npm:^0.2.1": 1184 | version: 0.2.1 1185 | resolution: "is-arrayish@npm:0.2.1" 1186 | checksum: 10/73ced84fa35e59e2c57da2d01e12cd01479f381d7f122ce41dcbb713f09dbfc651315832cd2bf8accba7681a69e4d6f1e03941d94dd10040d415086360e7005e 1187 | languageName: node 1188 | linkType: hard 1189 | 1190 | "is-bigint@npm:^1.0.1": 1191 | version: 1.0.1 1192 | resolution: "is-bigint@npm:1.0.1" 1193 | checksum: 10/04aa6fde59d2b7929df865acb89c8d7f89f919cc149b8be11e3560b1aab8667e5d939cc8954097c496f7dda80fd5bb67f829ca80ab66cc68918e41e2c1b9c5d7 1194 | languageName: node 1195 | linkType: hard 1196 | 1197 | "is-boolean-object@npm:^1.1.0": 1198 | version: 1.1.0 1199 | resolution: "is-boolean-object@npm:1.1.0" 1200 | dependencies: 1201 | call-bind: "npm:^1.0.0" 1202 | checksum: 10/d6d5113d7c40d111e6611efe3a76e999d6ea7c4bf2f33c41e30383ef005d6992b1d892ff4fb234db2debbbf4b9ba63f72326abdabfd925f80b8d53f5dd68de67 1203 | languageName: node 1204 | linkType: hard 1205 | 1206 | "is-callable@npm:^1.1.4, is-callable@npm:^1.2.3": 1207 | version: 1.2.3 1208 | resolution: "is-callable@npm:1.2.3" 1209 | checksum: 10/34d51c2c4a9f316632cd4975a8d33756ff570281019ab347b26fbc972a4906c873c9e9cb8a10c8313a7797309397fbbc14b5b0e92ceb3dd1804c80459e74e9dc 1210 | languageName: node 1211 | linkType: hard 1212 | 1213 | "is-date-object@npm:^1.0.1": 1214 | version: 1.0.2 1215 | resolution: "is-date-object@npm:1.0.2" 1216 | checksum: 10/96c56c04631f866b3a3aea4b889eac6120c13d8a06dc7e105479ffd6f57e5ea3668f1d779ef30063d4b27aa8e9b235ea7d15bbdab54b056affc678c4769ff143 1217 | languageName: node 1218 | linkType: hard 1219 | 1220 | "is-fullwidth-code-point@npm:^3.0.0": 1221 | version: 3.0.0 1222 | resolution: "is-fullwidth-code-point@npm:3.0.0" 1223 | checksum: 10/44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 1224 | languageName: node 1225 | linkType: hard 1226 | 1227 | "is-fullwidth-code-point@npm:^5.0.0": 1228 | version: 5.0.0 1229 | resolution: "is-fullwidth-code-point@npm:5.0.0" 1230 | dependencies: 1231 | get-east-asian-width: "npm:^1.0.0" 1232 | checksum: 10/8dfb2d2831b9e87983c136f5c335cd9d14c1402973e357a8ff057904612ed84b8cba196319fabedf9aefe4639e14fe3afe9d9966d1d006ebeb40fe1fed4babe5 1233 | languageName: node 1234 | linkType: hard 1235 | 1236 | "is-generator-function@npm:^1.0.7": 1237 | version: 1.0.8 1238 | resolution: "is-generator-function@npm:1.0.8" 1239 | checksum: 10/33693e6e66f135ce280863d8a187944f7d656f9652cc079e66d7fad009818ed4f51c28976e19a7b0278d6b000f7be4fb0214ec88ba16211de31fb4761b897e58 1240 | languageName: node 1241 | linkType: hard 1242 | 1243 | "is-nan@npm:^1.3.2": 1244 | version: 1.3.2 1245 | resolution: "is-nan@npm:1.3.2" 1246 | dependencies: 1247 | call-bind: "npm:^1.0.0" 1248 | define-properties: "npm:^1.1.3" 1249 | checksum: 10/1f784d3472c09bc2e47acba7ffd4f6c93b0394479aa613311dc1d70f1bfa72eb0846c81350967722c959ba65811bae222204d6c65856fdce68f31986140c7b0e 1250 | languageName: node 1251 | linkType: hard 1252 | 1253 | "is-negative-zero@npm:^2.0.1": 1254 | version: 2.0.1 1255 | resolution: "is-negative-zero@npm:2.0.1" 1256 | checksum: 10/3a017d57c2d5e04e9584b82282016dbf8bda34104a40f580e296a9de9bd74a5d9f75a5460bdad0ab98f7a7124bb3d193ffa799cdfdad7bbb547cd9daffa649a8 1257 | languageName: node 1258 | linkType: hard 1259 | 1260 | "is-number-object@npm:^1.0.4": 1261 | version: 1.0.4 1262 | resolution: "is-number-object@npm:1.0.4" 1263 | checksum: 10/02939c84b28d2e4ec0ee2cb5fc8ac53ee3c4d67d801c280aa051c2392afd677fe47c84efd5d13ccd5e00f103041e58743b9fa535fe905a6f49b48315ae1ddcf8 1264 | languageName: node 1265 | linkType: hard 1266 | 1267 | "is-number@npm:^7.0.0": 1268 | version: 7.0.0 1269 | resolution: "is-number@npm:7.0.0" 1270 | checksum: 10/6a6c3383f68afa1e05b286af866017c78f1226d43ac8cb064e115ff9ed85eb33f5c4f7216c96a71e4dfea289ef52c5da3aef5bbfade8ffe47a0465d70c0c8e86 1271 | languageName: node 1272 | linkType: hard 1273 | 1274 | "is-path-inside@npm:^3.0.3": 1275 | version: 3.0.3 1276 | resolution: "is-path-inside@npm:3.0.3" 1277 | checksum: 10/abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 1278 | languageName: node 1279 | linkType: hard 1280 | 1281 | "is-plain-obj@npm:^2.1.0": 1282 | version: 2.1.0 1283 | resolution: "is-plain-obj@npm:2.1.0" 1284 | checksum: 10/cec9100678b0a9fe0248a81743041ed990c2d4c99f893d935545cfbc42876cbe86d207f3b895700c690ad2fa520e568c44afc1605044b535a7820c1d40e38daa 1285 | languageName: node 1286 | linkType: hard 1287 | 1288 | "is-regex@npm:^1.1.2": 1289 | version: 1.1.2 1290 | resolution: "is-regex@npm:1.1.2" 1291 | dependencies: 1292 | call-bind: "npm:^1.0.2" 1293 | has-symbols: "npm:^1.0.1" 1294 | checksum: 10/da07bea5f0ec7b05c8826f8dc0e1322fa7275a96bcc3d46a48fa6a31320a38ceb523ce07b5960b7413b3c6323fa60a33bf8b8855ef3feec7d14a46061882f6ed 1295 | languageName: node 1296 | linkType: hard 1297 | 1298 | "is-string@npm:^1.0.5": 1299 | version: 1.0.5 1300 | resolution: "is-string@npm:1.0.5" 1301 | checksum: 10/aaf13faa599cb831705eec248aaa8a7355554f397841ada961a08642711022ea27ef8176ae0c3f7ba66eee1f6b584ab31bd42cd354878a58bdade388fe163a79 1302 | languageName: node 1303 | linkType: hard 1304 | 1305 | "is-symbol@npm:^1.0.2, is-symbol@npm:^1.0.3": 1306 | version: 1.0.3 1307 | resolution: "is-symbol@npm:1.0.3" 1308 | dependencies: 1309 | has-symbols: "npm:^1.0.1" 1310 | checksum: 10/4854604be4abb5f9d885d4bbc9f9318b7dbda9402fbe172c09861bb8910d97e70fac6dabbf1023a7ec56986f457c92abb08f1c99decce83c06c944130a0b1cd1 1311 | languageName: node 1312 | linkType: hard 1313 | 1314 | "is-typed-array@npm:^1.1.3": 1315 | version: 1.1.5 1316 | resolution: "is-typed-array@npm:1.1.5" 1317 | dependencies: 1318 | available-typed-arrays: "npm:^1.0.2" 1319 | call-bind: "npm:^1.0.2" 1320 | es-abstract: "npm:^1.18.0-next.2" 1321 | foreach: "npm:^2.0.5" 1322 | has-symbols: "npm:^1.0.1" 1323 | checksum: 10/f3a1d1cde869e72f26cc2d4fbe1e9d3ea395269948c69f4192112f3e7026dfeeee7a1ac6cf341951221c1560e2944e08a1dea11462523dfd24cc288c1594cf1b 1324 | languageName: node 1325 | linkType: hard 1326 | 1327 | "is-unicode-supported@npm:^0.1.0": 1328 | version: 0.1.0 1329 | resolution: "is-unicode-supported@npm:0.1.0" 1330 | checksum: 10/a2aab86ee7712f5c2f999180daaba5f361bdad1efadc9610ff5b8ab5495b86e4f627839d085c6530363c6d6d4ecbde340fb8e54bdb83da4ba8e0865ed5513c52 1331 | languageName: node 1332 | linkType: hard 1333 | 1334 | "isexe@npm:^2.0.0": 1335 | version: 2.0.0 1336 | resolution: "isexe@npm:2.0.0" 1337 | checksum: 10/7c9f715c03aff08f35e98b1fadae1b9267b38f0615d501824f9743f3aab99ef10e303ce7db3f186763a0b70a19de5791ebfc854ff884d5a8c4d92211f642ec92 1338 | languageName: node 1339 | linkType: hard 1340 | 1341 | "isexe@npm:^3.1.1": 1342 | version: 3.1.1 1343 | resolution: "isexe@npm:3.1.1" 1344 | checksum: 10/7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e 1345 | languageName: node 1346 | linkType: hard 1347 | 1348 | "jackspeak@npm:^3.1.2": 1349 | version: 3.4.3 1350 | resolution: "jackspeak@npm:3.4.3" 1351 | dependencies: 1352 | "@isaacs/cliui": "npm:^8.0.2" 1353 | "@pkgjs/parseargs": "npm:^0.11.0" 1354 | dependenciesMeta: 1355 | "@pkgjs/parseargs": 1356 | optional: true 1357 | checksum: 10/96f8786eaab98e4bf5b2a5d6d9588ea46c4d06bbc4f2eb861fdd7b6b182b16f71d8a70e79820f335d52653b16d4843b29dd9cdcf38ae80406756db9199497cf3 1358 | languageName: node 1359 | linkType: hard 1360 | 1361 | "js-tokens@npm:^4.0.0": 1362 | version: 4.0.0 1363 | resolution: "js-tokens@npm:4.0.0" 1364 | checksum: 10/af37d0d913fb56aec6dc0074c163cc71cd23c0b8aad5c2350747b6721d37ba118af35abdd8b33c47ec2800de07dedb16a527ca9c530ee004093e04958bd0cbf2 1365 | languageName: node 1366 | linkType: hard 1367 | 1368 | "js-yaml@npm:^4.1.0": 1369 | version: 4.1.1 1370 | resolution: "js-yaml@npm:4.1.1" 1371 | dependencies: 1372 | argparse: "npm:^2.0.1" 1373 | bin: 1374 | js-yaml: bin/js-yaml.js 1375 | checksum: 10/a52d0519f0f4ef5b4adc1cde466cb54c50d56e2b4a983b9d5c9c0f2f99462047007a6274d7e95617a21d3c91fde3ee6115536ed70991cd645ba8521058b78f77 1376 | languageName: node 1377 | linkType: hard 1378 | 1379 | "jsbn@npm:1.1.0": 1380 | version: 1.1.0 1381 | resolution: "jsbn@npm:1.1.0" 1382 | checksum: 10/bebe7ae829bbd586ce8cbe83501dd8cb8c282c8902a8aeeed0a073a89dc37e8103b1244f3c6acd60278bcbfe12d93a3f83c9ac396868a3b3bbc3c5e5e3b648ef 1383 | languageName: node 1384 | linkType: hard 1385 | 1386 | "json-parse-even-better-errors@npm:^2.3.0": 1387 | version: 2.3.1 1388 | resolution: "json-parse-even-better-errors@npm:2.3.1" 1389 | checksum: 10/5f3a99009ed5f2a5a67d06e2f298cc97bc86d462034173308156f15b43a6e850be8511dc204b9b94566305da2947f7d90289657237d210351a39059ff9d666cf 1390 | languageName: node 1391 | linkType: hard 1392 | 1393 | "lines-and-columns@npm:^1.1.6": 1394 | version: 1.1.6 1395 | resolution: "lines-and-columns@npm:1.1.6" 1396 | checksum: 10/198a5436b1fa5cf703bae719c01c686b076f0ad7e1aafd95a58d626cabff302dc0414822126f2f80b58a8c3d66cda8a7b6da064f27130f87e1d3506d6dfd0d68 1397 | languageName: node 1398 | linkType: hard 1399 | 1400 | "lint-staged@npm:^16.0.0": 1401 | version: 16.2.7 1402 | resolution: "lint-staged@npm:16.2.7" 1403 | dependencies: 1404 | commander: "npm:^14.0.2" 1405 | listr2: "npm:^9.0.5" 1406 | micromatch: "npm:^4.0.8" 1407 | nano-spawn: "npm:^2.0.0" 1408 | pidtree: "npm:^0.6.0" 1409 | string-argv: "npm:^0.3.2" 1410 | yaml: "npm:^2.8.1" 1411 | bin: 1412 | lint-staged: bin/lint-staged.js 1413 | checksum: 10/c1fd7685300800ea6d3f073cb450f9e3d2a83966e7c6785ea9608a08e77e1e8e5f1958f77b98ccd7d423daa53bb36016d6fd96a98d22234d0f7f56d7b3f360f2 1414 | languageName: node 1415 | linkType: hard 1416 | 1417 | "listr2@npm:^9.0.5": 1418 | version: 9.0.5 1419 | resolution: "listr2@npm:9.0.5" 1420 | dependencies: 1421 | cli-truncate: "npm:^5.0.0" 1422 | colorette: "npm:^2.0.20" 1423 | eventemitter3: "npm:^5.0.1" 1424 | log-update: "npm:^6.1.0" 1425 | rfdc: "npm:^1.4.1" 1426 | wrap-ansi: "npm:^9.0.0" 1427 | checksum: 10/b78ffd60443aed9a8e0fc9162eb941ea4d63210700d61a895eb29348f23fc668327e26bbca87a9e6a6208e7fa96c475fe1f1c6c19b46f376f547e0cba3b935ae 1428 | languageName: node 1429 | linkType: hard 1430 | 1431 | "locate-path@npm:^6.0.0": 1432 | version: 6.0.0 1433 | resolution: "locate-path@npm:6.0.0" 1434 | dependencies: 1435 | p-locate: "npm:^5.0.0" 1436 | checksum: 10/72eb661788a0368c099a184c59d2fee760b3831c9c1c33955e8a19ae4a21b4116e53fa736dc086cdeb9fce9f7cc508f2f92d2d3aae516f133e16a2bb59a39f5a 1437 | languageName: node 1438 | linkType: hard 1439 | 1440 | "log-symbols@npm:^4.1.0": 1441 | version: 4.1.0 1442 | resolution: "log-symbols@npm:4.1.0" 1443 | dependencies: 1444 | chalk: "npm:^4.1.0" 1445 | is-unicode-supported: "npm:^0.1.0" 1446 | checksum: 10/fce1497b3135a0198803f9f07464165e9eb83ed02ceb2273930a6f8a508951178d8cf4f0378e9d28300a2ed2bc49050995d2bd5f53ab716bb15ac84d58c6ef74 1447 | languageName: node 1448 | linkType: hard 1449 | 1450 | "log-update@npm:^6.1.0": 1451 | version: 6.1.0 1452 | resolution: "log-update@npm:6.1.0" 1453 | dependencies: 1454 | ansi-escapes: "npm:^7.0.0" 1455 | cli-cursor: "npm:^5.0.0" 1456 | slice-ansi: "npm:^7.1.0" 1457 | strip-ansi: "npm:^7.1.0" 1458 | wrap-ansi: "npm:^9.0.0" 1459 | checksum: 10/5abb4131e33b1e7f8416bb194fe17a3603d83e4657c5bf5bb81ce4187f3b00ea481643b85c3d5cefe6037a452cdcf7f1391ab8ea0d9c23e75d19589830ec4f11 1460 | languageName: node 1461 | linkType: hard 1462 | 1463 | "lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": 1464 | version: 10.4.3 1465 | resolution: "lru-cache@npm:10.4.3" 1466 | checksum: 10/e6e90267360476720fa8e83cc168aa2bf0311f3f2eea20a6ba78b90a885ae72071d9db132f40fda4129c803e7dcec3a6b6a6fbb44ca90b081630b810b5d6a41a 1467 | languageName: node 1468 | linkType: hard 1469 | 1470 | "make-error@npm:^1.1.1": 1471 | version: 1.3.6 1472 | resolution: "make-error@npm:1.3.6" 1473 | checksum: 10/b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402 1474 | languageName: node 1475 | linkType: hard 1476 | 1477 | "make-fetch-happen@npm:^14.0.3": 1478 | version: 14.0.3 1479 | resolution: "make-fetch-happen@npm:14.0.3" 1480 | dependencies: 1481 | "@npmcli/agent": "npm:^3.0.0" 1482 | cacache: "npm:^19.0.1" 1483 | http-cache-semantics: "npm:^4.1.1" 1484 | minipass: "npm:^7.0.2" 1485 | minipass-fetch: "npm:^4.0.0" 1486 | minipass-flush: "npm:^1.0.5" 1487 | minipass-pipeline: "npm:^1.2.4" 1488 | negotiator: "npm:^1.0.0" 1489 | proc-log: "npm:^5.0.0" 1490 | promise-retry: "npm:^2.0.1" 1491 | ssri: "npm:^12.0.0" 1492 | checksum: 10/fce0385840b6d86b735053dfe941edc2dd6468fda80fe74da1eeff10cbd82a75760f406194f2bc2fa85b99545b2bc1f84c08ddf994b21830775ba2d1a87e8bdf 1493 | languageName: node 1494 | linkType: hard 1495 | 1496 | "micromatch@npm:^4.0.8": 1497 | version: 4.0.8 1498 | resolution: "micromatch@npm:4.0.8" 1499 | dependencies: 1500 | braces: "npm:^3.0.3" 1501 | picomatch: "npm:^2.3.1" 1502 | checksum: 10/6bf2a01672e7965eb9941d1f02044fad2bd12486b5553dc1116ff24c09a8723157601dc992e74c911d896175918448762df3b3fd0a6b61037dd1a9766ddfbf58 1503 | languageName: node 1504 | linkType: hard 1505 | 1506 | "mimic-function@npm:^5.0.0": 1507 | version: 5.0.1 1508 | resolution: "mimic-function@npm:5.0.1" 1509 | checksum: 10/eb5893c99e902ccebbc267c6c6b83092966af84682957f79313311edb95e8bb5f39fb048d77132b700474d1c86d90ccc211e99bae0935447a4834eb4c882982c 1510 | languageName: node 1511 | linkType: hard 1512 | 1513 | "minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": 1514 | version: 9.0.5 1515 | resolution: "minimatch@npm:9.0.5" 1516 | dependencies: 1517 | brace-expansion: "npm:^2.0.1" 1518 | checksum: 10/dd6a8927b063aca6d910b119e1f2df6d2ce7d36eab91de83167dd136bb85e1ebff97b0d3de1cb08bd1f7e018ca170b4962479fefab5b2a69e2ae12cb2edc8348 1519 | languageName: node 1520 | linkType: hard 1521 | 1522 | "minipass-collect@npm:^2.0.1": 1523 | version: 2.0.1 1524 | resolution: "minipass-collect@npm:2.0.1" 1525 | dependencies: 1526 | minipass: "npm:^7.0.3" 1527 | checksum: 10/b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342 1528 | languageName: node 1529 | linkType: hard 1530 | 1531 | "minipass-fetch@npm:^4.0.0": 1532 | version: 4.0.1 1533 | resolution: "minipass-fetch@npm:4.0.1" 1534 | dependencies: 1535 | encoding: "npm:^0.1.13" 1536 | minipass: "npm:^7.0.3" 1537 | minipass-sized: "npm:^1.0.3" 1538 | minizlib: "npm:^3.0.1" 1539 | dependenciesMeta: 1540 | encoding: 1541 | optional: true 1542 | checksum: 10/7ddfebdbb87d9866e7b5f7eead5a9e3d9d507992af932a11d275551f60006cf7d9178e66d586dbb910894f3e3458d27c0ddf93c76e94d49d0a54a541ddc1263d 1543 | languageName: node 1544 | linkType: hard 1545 | 1546 | "minipass-flush@npm:^1.0.5": 1547 | version: 1.0.5 1548 | resolution: "minipass-flush@npm:1.0.5" 1549 | dependencies: 1550 | minipass: "npm:^3.0.0" 1551 | checksum: 10/56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf 1552 | languageName: node 1553 | linkType: hard 1554 | 1555 | "minipass-pipeline@npm:^1.2.4": 1556 | version: 1.2.4 1557 | resolution: "minipass-pipeline@npm:1.2.4" 1558 | dependencies: 1559 | minipass: "npm:^3.0.0" 1560 | checksum: 10/b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b 1561 | languageName: node 1562 | linkType: hard 1563 | 1564 | "minipass-sized@npm:^1.0.3": 1565 | version: 1.0.3 1566 | resolution: "minipass-sized@npm:1.0.3" 1567 | dependencies: 1568 | minipass: "npm:^3.0.0" 1569 | checksum: 10/40982d8d836a52b0f37049a0a7e5d0f089637298e6d9b45df9c115d4f0520682a78258905e5c8b180fb41b593b0a82cc1361d2c74b45f7ada66334f84d1ecfdd 1570 | languageName: node 1571 | linkType: hard 1572 | 1573 | "minipass@npm:^3.0.0": 1574 | version: 3.3.6 1575 | resolution: "minipass@npm:3.3.6" 1576 | dependencies: 1577 | yallist: "npm:^4.0.0" 1578 | checksum: 10/a5c6ef069f70d9a524d3428af39f2b117ff8cd84172e19b754e7264a33df460873e6eb3d6e55758531580970de50ae950c496256bb4ad3691a2974cddff189f0 1579 | languageName: node 1580 | linkType: hard 1581 | 1582 | "minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2": 1583 | version: 7.1.2 1584 | resolution: "minipass@npm:7.1.2" 1585 | checksum: 10/c25f0ee8196d8e6036661104bacd743785b2599a21de5c516b32b3fa2b83113ac89a2358465bc04956baab37ffb956ae43be679b2262bf7be15fce467ccd7950 1586 | languageName: node 1587 | linkType: hard 1588 | 1589 | "minizlib@npm:^3.0.1": 1590 | version: 3.0.1 1591 | resolution: "minizlib@npm:3.0.1" 1592 | dependencies: 1593 | minipass: "npm:^7.0.4" 1594 | rimraf: "npm:^5.0.5" 1595 | checksum: 10/622cb85f51e5c206a080a62d20db0d7b4066f308cb6ce82a9644da112367c3416ae7062017e631eb7ac8588191cfa4a9a279b8651c399265202b298e98c4acef 1596 | languageName: node 1597 | linkType: hard 1598 | 1599 | "mkdirp@npm:^3.0.1": 1600 | version: 3.0.1 1601 | resolution: "mkdirp@npm:3.0.1" 1602 | bin: 1603 | mkdirp: dist/cjs/src/bin.js 1604 | checksum: 10/16fd79c28645759505914561e249b9a1f5fe3362279ad95487a4501e4467abeb714fd35b95307326b8fd03f3c7719065ef11a6f97b7285d7888306d1bd2232ba 1605 | languageName: node 1606 | linkType: hard 1607 | 1608 | "mocha@npm:^11.1.0": 1609 | version: 11.7.5 1610 | resolution: "mocha@npm:11.7.5" 1611 | dependencies: 1612 | browser-stdout: "npm:^1.3.1" 1613 | chokidar: "npm:^4.0.1" 1614 | debug: "npm:^4.3.5" 1615 | diff: "npm:^7.0.0" 1616 | escape-string-regexp: "npm:^4.0.0" 1617 | find-up: "npm:^5.0.0" 1618 | glob: "npm:^10.4.5" 1619 | he: "npm:^1.2.0" 1620 | is-path-inside: "npm:^3.0.3" 1621 | js-yaml: "npm:^4.1.0" 1622 | log-symbols: "npm:^4.1.0" 1623 | minimatch: "npm:^9.0.5" 1624 | ms: "npm:^2.1.3" 1625 | picocolors: "npm:^1.1.1" 1626 | serialize-javascript: "npm:^6.0.2" 1627 | strip-json-comments: "npm:^3.1.1" 1628 | supports-color: "npm:^8.1.1" 1629 | workerpool: "npm:^9.2.0" 1630 | yargs: "npm:^17.7.2" 1631 | yargs-parser: "npm:^21.1.1" 1632 | yargs-unparser: "npm:^2.0.0" 1633 | bin: 1634 | _mocha: bin/_mocha 1635 | mocha: bin/mocha.js 1636 | checksum: 10/878fcec45d79dad3ec01b896ae75eabd0075b8f91866b338b81b2ef8a2279f1e70fb07fc89054108a46864fbc1e6f6898dc31e64c033ded585748c5fcbf7e704 1637 | languageName: node 1638 | linkType: hard 1639 | 1640 | "ms@npm:^2.1.3": 1641 | version: 2.1.3 1642 | resolution: "ms@npm:2.1.3" 1643 | checksum: 10/aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d 1644 | languageName: node 1645 | linkType: hard 1646 | 1647 | "nano-spawn@npm:^2.0.0": 1648 | version: 2.0.0 1649 | resolution: "nano-spawn@npm:2.0.0" 1650 | checksum: 10/117d35d7bd85b146908de5d3d1177d2b2ee3174e5d884d6bc9555583bf6e50a265f4038b5c134b7cdd768a10d53598ccde5c00d6f55e25e7eed31b86b8d29646 1651 | languageName: node 1652 | linkType: hard 1653 | 1654 | "negotiator@npm:^1.0.0": 1655 | version: 1.0.0 1656 | resolution: "negotiator@npm:1.0.0" 1657 | checksum: 10/b5734e87295324fabf868e36fb97c84b7d7f3156ec5f4ee5bf6e488079c11054f818290fc33804cef7b1ee21f55eeb14caea83e7dafae6492a409b3e573153e5 1658 | languageName: node 1659 | linkType: hard 1660 | 1661 | "node-gyp@npm:latest": 1662 | version: 11.1.0 1663 | resolution: "node-gyp@npm:11.1.0" 1664 | dependencies: 1665 | env-paths: "npm:^2.2.0" 1666 | exponential-backoff: "npm:^3.1.1" 1667 | glob: "npm:^10.3.10" 1668 | graceful-fs: "npm:^4.2.6" 1669 | make-fetch-happen: "npm:^14.0.3" 1670 | nopt: "npm:^8.0.0" 1671 | proc-log: "npm:^5.0.0" 1672 | semver: "npm:^7.3.5" 1673 | tar: "npm:^7.4.3" 1674 | which: "npm:^5.0.0" 1675 | bin: 1676 | node-gyp: bin/node-gyp.js 1677 | checksum: 10/3314ebfeb99dbcdf9e8c810df1ee52294045399873d4ab1e6740608c4fbe63adaf6580c0610b23c6eda125e298536553f5bb6fb0df714016a5c721ed31095e42 1678 | languageName: node 1679 | linkType: hard 1680 | 1681 | "nopt@npm:^8.0.0": 1682 | version: 8.1.0 1683 | resolution: "nopt@npm:8.1.0" 1684 | dependencies: 1685 | abbrev: "npm:^3.0.0" 1686 | bin: 1687 | nopt: bin/nopt.js 1688 | checksum: 10/26ab456c51a96f02a9e5aa8d1b80ef3219f2070f3f3528a040e32fb735b1e651e17bdf0f1476988d3a46d498f35c65ed662d122f340d38ce4a7e71dd7b20c4bc 1689 | languageName: node 1690 | linkType: hard 1691 | 1692 | "object-inspect@npm:^1.9.0": 1693 | version: 1.12.3 1694 | resolution: "object-inspect@npm:1.12.3" 1695 | checksum: 10/532b0036f0472f561180fac0d04fe328ee01f57637624c83fb054f81b5bfe966cdf4200612a499ed391a7ca3c46b20a0bc3a55fc8241d944abe687c556a32b39 1696 | languageName: node 1697 | linkType: hard 1698 | 1699 | "object-is@npm:^1.1.5": 1700 | version: 1.1.5 1701 | resolution: "object-is@npm:1.1.5" 1702 | dependencies: 1703 | call-bind: "npm:^1.0.2" 1704 | define-properties: "npm:^1.1.3" 1705 | checksum: 10/75365aff5da4bebad5d20efd9f9a7a13597e603f5eb03d89da8f578c3f3937fe01c6cb5fce86c0611c48795c0841401fd37c943821db0de703c7b30a290576ad 1706 | languageName: node 1707 | linkType: hard 1708 | 1709 | "object-keys@npm:^1.1.1": 1710 | version: 1.1.1 1711 | resolution: "object-keys@npm:1.1.1" 1712 | checksum: 10/3d81d02674115973df0b7117628ea4110d56042e5326413e4b4313f0bcdf7dd78d4a3acef2c831463fa3796a66762c49daef306f4a0ea1af44877d7086d73bde 1713 | languageName: node 1714 | linkType: hard 1715 | 1716 | "object.assign@npm:^4.1.2, object.assign@npm:^4.1.4": 1717 | version: 4.1.4 1718 | resolution: "object.assign@npm:4.1.4" 1719 | dependencies: 1720 | call-bind: "npm:^1.0.2" 1721 | define-properties: "npm:^1.1.4" 1722 | has-symbols: "npm:^1.0.3" 1723 | object-keys: "npm:^1.1.1" 1724 | checksum: 10/fd82d45289df0a952d772817622ecbaeb4ec933d3abb53267aede083ee38f6a395af8fadfbc569ee575115b0b7c9b286e7cfb2b7a2557b1055f7acbce513bc29 1725 | languageName: node 1726 | linkType: hard 1727 | 1728 | "onetime@npm:^7.0.0": 1729 | version: 7.0.0 1730 | resolution: "onetime@npm:7.0.0" 1731 | dependencies: 1732 | mimic-function: "npm:^5.0.0" 1733 | checksum: 10/eb08d2da9339819e2f9d52cab9caf2557d80e9af8c7d1ae86e1a0fef027d00a88e9f5bd67494d350df360f7c559fbb44e800b32f310fb989c860214eacbb561c 1734 | languageName: node 1735 | linkType: hard 1736 | 1737 | "opencollective-postinstall@npm:^2.0.2": 1738 | version: 2.0.3 1739 | resolution: "opencollective-postinstall@npm:2.0.3" 1740 | bin: 1741 | opencollective-postinstall: index.js 1742 | checksum: 10/69d63778087cd10c9d707d9ed360556780cfdd0cd6241ded0e26632f467f1d5a064f4a9aec19a30c187770c17adba034d988f7684b226f3a73e79f44e73fab0e 1743 | languageName: node 1744 | linkType: hard 1745 | 1746 | "p-limit@npm:^3.0.2": 1747 | version: 3.1.0 1748 | resolution: "p-limit@npm:3.1.0" 1749 | dependencies: 1750 | yocto-queue: "npm:^0.1.0" 1751 | checksum: 10/7c3690c4dbf62ef625671e20b7bdf1cbc9534e83352a2780f165b0d3ceba21907e77ad63401708145ca4e25bfc51636588d89a8c0aeb715e6c37d1c066430360 1752 | languageName: node 1753 | linkType: hard 1754 | 1755 | "p-locate@npm:^5.0.0": 1756 | version: 5.0.0 1757 | resolution: "p-locate@npm:5.0.0" 1758 | dependencies: 1759 | p-limit: "npm:^3.0.2" 1760 | checksum: 10/1623088f36cf1cbca58e9b61c4e62bf0c60a07af5ae1ca99a720837356b5b6c5ba3eb1b2127e47a06865fee59dd0453cad7cc844cda9d5a62ac1a5a51b7c86d3 1761 | languageName: node 1762 | linkType: hard 1763 | 1764 | "p-map@npm:^7.0.2": 1765 | version: 7.0.3 1766 | resolution: "p-map@npm:7.0.3" 1767 | checksum: 10/2ef48ccfc6dd387253d71bf502604f7893ed62090b2c9d73387f10006c342606b05233da0e4f29388227b61eb5aeface6197e166520c465c234552eeab2fe633 1768 | languageName: node 1769 | linkType: hard 1770 | 1771 | "package-json-from-dist@npm:^1.0.0": 1772 | version: 1.0.1 1773 | resolution: "package-json-from-dist@npm:1.0.1" 1774 | checksum: 10/58ee9538f2f762988433da00e26acc788036914d57c71c246bf0be1b60cdbd77dd60b6a3e1a30465f0b248aeb80079e0b34cb6050b1dfa18c06953bb1cbc7602 1775 | languageName: node 1776 | linkType: hard 1777 | 1778 | "parent-module@npm:^1.0.0": 1779 | version: 1.0.1 1780 | resolution: "parent-module@npm:1.0.1" 1781 | dependencies: 1782 | callsites: "npm:^3.0.0" 1783 | checksum: 10/6ba8b255145cae9470cf5551eb74be2d22281587af787a2626683a6c20fbb464978784661478dd2a3f1dad74d1e802d403e1b03c1a31fab310259eec8ac560ff 1784 | languageName: node 1785 | linkType: hard 1786 | 1787 | "parse-json@npm:^5.0.0": 1788 | version: 5.2.0 1789 | resolution: "parse-json@npm:5.2.0" 1790 | dependencies: 1791 | "@babel/code-frame": "npm:^7.0.0" 1792 | error-ex: "npm:^1.3.1" 1793 | json-parse-even-better-errors: "npm:^2.3.0" 1794 | lines-and-columns: "npm:^1.1.6" 1795 | checksum: 10/62085b17d64da57f40f6afc2ac1f4d95def18c4323577e1eced571db75d9ab59b297d1d10582920f84b15985cbfc6b6d450ccbf317644cfa176f3ed982ad87e2 1796 | languageName: node 1797 | linkType: hard 1798 | 1799 | "path-exists@npm:^4.0.0": 1800 | version: 4.0.0 1801 | resolution: "path-exists@npm:4.0.0" 1802 | checksum: 10/505807199dfb7c50737b057dd8d351b82c033029ab94cb10a657609e00c1bc53b951cfdbccab8de04c5584d5eff31128ce6afd3db79281874a5ef2adbba55ed1 1803 | languageName: node 1804 | linkType: hard 1805 | 1806 | "path-key@npm:^3.1.0": 1807 | version: 3.1.1 1808 | resolution: "path-key@npm:3.1.1" 1809 | checksum: 10/55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020 1810 | languageName: node 1811 | linkType: hard 1812 | 1813 | "path-scurry@npm:^1.11.1": 1814 | version: 1.11.1 1815 | resolution: "path-scurry@npm:1.11.1" 1816 | dependencies: 1817 | lru-cache: "npm:^10.2.0" 1818 | minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" 1819 | checksum: 10/5e8845c159261adda6f09814d7725683257fcc85a18f329880ab4d7cc1d12830967eae5d5894e453f341710d5484b8fdbbd4d75181b4d6e1eb2f4dc7aeadc434 1820 | languageName: node 1821 | linkType: hard 1822 | 1823 | "path-type@npm:^4.0.0": 1824 | version: 4.0.0 1825 | resolution: "path-type@npm:4.0.0" 1826 | checksum: 10/5b1e2daa247062061325b8fdbfd1fb56dde0a448fb1455453276ea18c60685bdad23a445dc148cf87bc216be1573357509b7d4060494a6fd768c7efad833ee45 1827 | languageName: node 1828 | linkType: hard 1829 | 1830 | "picocolors@npm:^1.1.1": 1831 | version: 1.1.1 1832 | resolution: "picocolors@npm:1.1.1" 1833 | checksum: 10/e1cf46bf84886c79055fdfa9dcb3e4711ad259949e3565154b004b260cd356c5d54b31a1437ce9782624bf766272fe6b0154f5f0c744fb7af5d454d2b60db045 1834 | languageName: node 1835 | linkType: hard 1836 | 1837 | "picomatch@npm:^2.3.1": 1838 | version: 2.3.1 1839 | resolution: "picomatch@npm:2.3.1" 1840 | checksum: 10/60c2595003b05e4535394d1da94850f5372c9427ca4413b71210f437f7b2ca091dbd611c45e8b37d10036fa8eade25c1b8951654f9d3973bfa66a2ff4d3b08bc 1841 | languageName: node 1842 | linkType: hard 1843 | 1844 | "pidtree@npm:^0.6.0": 1845 | version: 0.6.0 1846 | resolution: "pidtree@npm:0.6.0" 1847 | bin: 1848 | pidtree: bin/pidtree.js 1849 | checksum: 10/ea67fb3159e170fd069020e0108ba7712df9f0fd13c8db9b2286762856ddce414fb33932e08df4bfe36e91fe860b51852aee49a6f56eb4714b69634343add5df 1850 | languageName: node 1851 | linkType: hard 1852 | 1853 | "pkg-dir@npm:^5.0.0": 1854 | version: 5.0.0 1855 | resolution: "pkg-dir@npm:5.0.0" 1856 | dependencies: 1857 | find-up: "npm:^5.0.0" 1858 | checksum: 10/b167bb8dac7bbf22b1d5e30ec223e6b064b84b63010c9d49384619a36734caf95ed23ad23d4f9bd975e8e8082b60a83395f43a89bb192df53a7c25a38ecb57d9 1859 | languageName: node 1860 | linkType: hard 1861 | 1862 | "please-upgrade-node@npm:^3.2.0": 1863 | version: 3.2.0 1864 | resolution: "please-upgrade-node@npm:3.2.0" 1865 | dependencies: 1866 | semver-compare: "npm:^1.0.0" 1867 | checksum: 10/d87c41581a2a022fbe25965a97006238cd9b8cbbf49b39f78d262548149a9d30bd2bdf35fec3d810e0001e630cd46ef13c7e19c389dea8de7e64db271a2381bb 1868 | languageName: node 1869 | linkType: hard 1870 | 1871 | "prettier@npm:^3.0.0": 1872 | version: 3.7.4 1873 | resolution: "prettier@npm:3.7.4" 1874 | bin: 1875 | prettier: bin/prettier.cjs 1876 | checksum: 10/b4d00ea13baed813cb777c444506632fb10faaef52dea526cacd03085f01f6db11fc969ccebedf05bf7d93c3960900994c6adf1b150e28a31afd5cfe7089b313 1877 | languageName: node 1878 | linkType: hard 1879 | 1880 | "proc-log@npm:^5.0.0": 1881 | version: 5.0.0 1882 | resolution: "proc-log@npm:5.0.0" 1883 | checksum: 10/35610bdb0177d3ab5d35f8827a429fb1dc2518d9e639f2151ac9007f01a061c30e0c635a970c9b00c39102216160f6ec54b62377c92fac3b7bfc2ad4b98d195c 1884 | languageName: node 1885 | linkType: hard 1886 | 1887 | "promise-retry@npm:^2.0.1": 1888 | version: 2.0.1 1889 | resolution: "promise-retry@npm:2.0.1" 1890 | dependencies: 1891 | err-code: "npm:^2.0.2" 1892 | retry: "npm:^0.12.0" 1893 | checksum: 10/96e1a82453c6c96eef53a37a1d6134c9f2482f94068f98a59145d0986ca4e497bf110a410adf73857e588165eab3899f0ebcf7b3890c1b3ce802abc0d65967d4 1894 | languageName: node 1895 | linkType: hard 1896 | 1897 | "randombytes@npm:^2.1.0": 1898 | version: 2.1.0 1899 | resolution: "randombytes@npm:2.1.0" 1900 | dependencies: 1901 | safe-buffer: "npm:^5.1.0" 1902 | checksum: 10/4efd1ad3d88db77c2d16588dc54c2b52fd2461e70fe5724611f38d283857094fe09040fa2c9776366803c3152cf133171b452ef717592b65631ce5dc3a2bdafc 1903 | languageName: node 1904 | linkType: hard 1905 | 1906 | "readdirp@npm:^4.0.1": 1907 | version: 4.1.2 1908 | resolution: "readdirp@npm:4.1.2" 1909 | checksum: 10/7b817c265940dba90bb9c94d82920d76c3a35ea2d67f9f9d8bd936adcfe02d50c802b14be3dd2e725e002dddbe2cc1c7a0edfb1bc3a365c9dfd5a61e612eea1e 1910 | languageName: node 1911 | linkType: hard 1912 | 1913 | "require-directory@npm:^2.1.1": 1914 | version: 2.1.1 1915 | resolution: "require-directory@npm:2.1.1" 1916 | checksum: 10/a72468e2589270d91f06c7d36ec97a88db53ae5d6fe3787fadc943f0b0276b10347f89b363b2a82285f650bdcc135ad4a257c61bdd4d00d6df1fa24875b0ddaf 1917 | languageName: node 1918 | linkType: hard 1919 | 1920 | "resolve-from@npm:^4.0.0": 1921 | version: 4.0.0 1922 | resolution: "resolve-from@npm:4.0.0" 1923 | checksum: 10/91eb76ce83621eea7bbdd9b55121a5c1c4a39e54a9ce04a9ad4517f102f8b5131c2cf07622c738a6683991bf54f2ce178f5a42803ecbd527ddc5105f362cc9e3 1924 | languageName: node 1925 | linkType: hard 1926 | 1927 | "restore-cursor@npm:^5.0.0": 1928 | version: 5.1.0 1929 | resolution: "restore-cursor@npm:5.1.0" 1930 | dependencies: 1931 | onetime: "npm:^7.0.0" 1932 | signal-exit: "npm:^4.1.0" 1933 | checksum: 10/838dd54e458d89cfbc1a923b343c1b0f170a04100b4ce1733e97531842d7b440463967e521216e8ab6c6f8e89df877acc7b7f4c18ec76e99fb9bf5a60d358d2c 1934 | languageName: node 1935 | linkType: hard 1936 | 1937 | "retry@npm:^0.12.0": 1938 | version: 0.12.0 1939 | resolution: "retry@npm:0.12.0" 1940 | checksum: 10/1f914879f97e7ee931ad05fe3afa629bd55270fc6cf1c1e589b6a99fab96d15daad0fa1a52a00c729ec0078045fe3e399bd4fd0c93bcc906957bdc17f89cb8e6 1941 | languageName: node 1942 | linkType: hard 1943 | 1944 | "rfdc@npm:^1.4.1": 1945 | version: 1.4.1 1946 | resolution: "rfdc@npm:1.4.1" 1947 | checksum: 10/2f3d11d3d8929b4bfeefc9acb03aae90f971401de0add5ae6c5e38fec14f0405e6a4aad8fdb76344bfdd20c5193110e3750cbbd28ba86d73729d222b6cf4a729 1948 | languageName: node 1949 | linkType: hard 1950 | 1951 | "rimraf@npm:^5.0.5": 1952 | version: 5.0.10 1953 | resolution: "rimraf@npm:5.0.10" 1954 | dependencies: 1955 | glob: "npm:^10.3.7" 1956 | bin: 1957 | rimraf: dist/esm/bin.mjs 1958 | checksum: 10/f3b8ce81eecbde4628b07bdf9e2fa8b684e0caea4999acb1e3b0402c695cd41f28cd075609a808e61ce2672f528ca079f675ab1d8e8d5f86d56643a03e0b8d2e 1959 | languageName: node 1960 | linkType: hard 1961 | 1962 | "rollup@npm:^4.3.0": 1963 | version: 4.53.3 1964 | resolution: "rollup@npm:4.53.3" 1965 | dependencies: 1966 | "@rollup/rollup-android-arm-eabi": "npm:4.53.3" 1967 | "@rollup/rollup-android-arm64": "npm:4.53.3" 1968 | "@rollup/rollup-darwin-arm64": "npm:4.53.3" 1969 | "@rollup/rollup-darwin-x64": "npm:4.53.3" 1970 | "@rollup/rollup-freebsd-arm64": "npm:4.53.3" 1971 | "@rollup/rollup-freebsd-x64": "npm:4.53.3" 1972 | "@rollup/rollup-linux-arm-gnueabihf": "npm:4.53.3" 1973 | "@rollup/rollup-linux-arm-musleabihf": "npm:4.53.3" 1974 | "@rollup/rollup-linux-arm64-gnu": "npm:4.53.3" 1975 | "@rollup/rollup-linux-arm64-musl": "npm:4.53.3" 1976 | "@rollup/rollup-linux-loong64-gnu": "npm:4.53.3" 1977 | "@rollup/rollup-linux-ppc64-gnu": "npm:4.53.3" 1978 | "@rollup/rollup-linux-riscv64-gnu": "npm:4.53.3" 1979 | "@rollup/rollup-linux-riscv64-musl": "npm:4.53.3" 1980 | "@rollup/rollup-linux-s390x-gnu": "npm:4.53.3" 1981 | "@rollup/rollup-linux-x64-gnu": "npm:4.53.3" 1982 | "@rollup/rollup-linux-x64-musl": "npm:4.53.3" 1983 | "@rollup/rollup-openharmony-arm64": "npm:4.53.3" 1984 | "@rollup/rollup-win32-arm64-msvc": "npm:4.53.3" 1985 | "@rollup/rollup-win32-ia32-msvc": "npm:4.53.3" 1986 | "@rollup/rollup-win32-x64-gnu": "npm:4.53.3" 1987 | "@rollup/rollup-win32-x64-msvc": "npm:4.53.3" 1988 | "@types/estree": "npm:1.0.8" 1989 | fsevents: "npm:~2.3.2" 1990 | dependenciesMeta: 1991 | "@rollup/rollup-android-arm-eabi": 1992 | optional: true 1993 | "@rollup/rollup-android-arm64": 1994 | optional: true 1995 | "@rollup/rollup-darwin-arm64": 1996 | optional: true 1997 | "@rollup/rollup-darwin-x64": 1998 | optional: true 1999 | "@rollup/rollup-freebsd-arm64": 2000 | optional: true 2001 | "@rollup/rollup-freebsd-x64": 2002 | optional: true 2003 | "@rollup/rollup-linux-arm-gnueabihf": 2004 | optional: true 2005 | "@rollup/rollup-linux-arm-musleabihf": 2006 | optional: true 2007 | "@rollup/rollup-linux-arm64-gnu": 2008 | optional: true 2009 | "@rollup/rollup-linux-arm64-musl": 2010 | optional: true 2011 | "@rollup/rollup-linux-loong64-gnu": 2012 | optional: true 2013 | "@rollup/rollup-linux-ppc64-gnu": 2014 | optional: true 2015 | "@rollup/rollup-linux-riscv64-gnu": 2016 | optional: true 2017 | "@rollup/rollup-linux-riscv64-musl": 2018 | optional: true 2019 | "@rollup/rollup-linux-s390x-gnu": 2020 | optional: true 2021 | "@rollup/rollup-linux-x64-gnu": 2022 | optional: true 2023 | "@rollup/rollup-linux-x64-musl": 2024 | optional: true 2025 | "@rollup/rollup-openharmony-arm64": 2026 | optional: true 2027 | "@rollup/rollup-win32-arm64-msvc": 2028 | optional: true 2029 | "@rollup/rollup-win32-ia32-msvc": 2030 | optional: true 2031 | "@rollup/rollup-win32-x64-gnu": 2032 | optional: true 2033 | "@rollup/rollup-win32-x64-msvc": 2034 | optional: true 2035 | fsevents: 2036 | optional: true 2037 | bin: 2038 | rollup: dist/bin/rollup 2039 | checksum: 10/e2eff82405061fa907f15dfbf742b1f5fb4b214495c00989bcdbe21da5fcb3f6dec3deabacec491300a53c99da409586cfc77bdf29b411fccb9089b72cd3728d 2040 | languageName: node 2041 | linkType: hard 2042 | 2043 | "safe-buffer@npm:^5.1.0": 2044 | version: 5.2.1 2045 | resolution: "safe-buffer@npm:5.2.1" 2046 | checksum: 10/32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451 2047 | languageName: node 2048 | linkType: hard 2049 | 2050 | "safer-buffer@npm:>= 2.1.2 < 3.0.0": 2051 | version: 2.1.2 2052 | resolution: "safer-buffer@npm:2.1.2" 2053 | checksum: 10/7eaf7a0cf37cc27b42fb3ef6a9b1df6e93a1c6d98c6c6702b02fe262d5fcbd89db63320793b99b21cb5348097d0a53de81bd5f4e8b86e20cc9412e3f1cfb4e83 2054 | languageName: node 2055 | linkType: hard 2056 | 2057 | "semver-compare@npm:^1.0.0": 2058 | version: 1.0.0 2059 | resolution: "semver-compare@npm:1.0.0" 2060 | checksum: 10/75f9c7a7786d1756f64b1429017746721e07bd7691bdad6368f7643885d3a98a27586777e9699456564f4844b407e9f186cc1d588a3f9c0be71310e517e942c3 2061 | languageName: node 2062 | linkType: hard 2063 | 2064 | "semver-regex@npm:^3.1.2": 2065 | version: 3.1.4 2066 | resolution: "semver-regex@npm:3.1.4" 2067 | checksum: 10/3962105908e326aa2cd5c851a2f6d4cc7340d1b06560afc35cd5348d9fa5b1cc0ac0cad7e7cef2072bc12b992c5ae654d9e8d355c19d75d4216fced3b6c5d8a7 2068 | languageName: node 2069 | linkType: hard 2070 | 2071 | "semver@npm:^7.3.5": 2072 | version: 7.7.1 2073 | resolution: "semver@npm:7.7.1" 2074 | bin: 2075 | semver: bin/semver.js 2076 | checksum: 10/4cfa1eb91ef3751e20fc52e47a935a0118d56d6f15a837ab814da0c150778ba2ca4f1a4d9068b33070ea4273629e615066664c2cfcd7c272caf7a8a0f6518b2c 2077 | languageName: node 2078 | linkType: hard 2079 | 2080 | "serialize-javascript@npm:^6.0.2": 2081 | version: 6.0.2 2082 | resolution: "serialize-javascript@npm:6.0.2" 2083 | dependencies: 2084 | randombytes: "npm:^2.1.0" 2085 | checksum: 10/445a420a6fa2eaee4b70cbd884d538e259ab278200a2ededd73253ada17d5d48e91fb1f4cd224a236ab62ea7ba0a70c6af29fc93b4f3d3078bf7da1c031fde58 2086 | languageName: node 2087 | linkType: hard 2088 | 2089 | "shebang-command@npm:^2.0.0": 2090 | version: 2.0.0 2091 | resolution: "shebang-command@npm:2.0.0" 2092 | dependencies: 2093 | shebang-regex: "npm:^3.0.0" 2094 | checksum: 10/6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa 2095 | languageName: node 2096 | linkType: hard 2097 | 2098 | "shebang-regex@npm:^3.0.0": 2099 | version: 3.0.0 2100 | resolution: "shebang-regex@npm:3.0.0" 2101 | checksum: 10/1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222 2102 | languageName: node 2103 | linkType: hard 2104 | 2105 | "signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": 2106 | version: 4.1.0 2107 | resolution: "signal-exit@npm:4.1.0" 2108 | checksum: 10/c9fa63bbbd7431066174a48ba2dd9986dfd930c3a8b59de9c29d7b6854ec1c12a80d15310869ea5166d413b99f041bfa3dd80a7947bcd44ea8e6eb3ffeabfa1f 2109 | languageName: node 2110 | linkType: hard 2111 | 2112 | "slash@npm:^3.0.0": 2113 | version: 3.0.0 2114 | resolution: "slash@npm:3.0.0" 2115 | checksum: 10/94a93fff615f25a999ad4b83c9d5e257a7280c90a32a7cb8b4a87996e4babf322e469c42b7f649fd5796edd8687652f3fb452a86dc97a816f01113183393f11c 2116 | languageName: node 2117 | linkType: hard 2118 | 2119 | "slice-ansi@npm:^7.1.0": 2120 | version: 7.1.0 2121 | resolution: "slice-ansi@npm:7.1.0" 2122 | dependencies: 2123 | ansi-styles: "npm:^6.2.1" 2124 | is-fullwidth-code-point: "npm:^5.0.0" 2125 | checksum: 10/10313dd3cf7a2e4b265f527b1684c7c568210b09743fd1bd74f2194715ed13ffba653dc93a5fa79e3b1711518b8990a732cb7143aa01ddafe626e99dfa6474b2 2126 | languageName: node 2127 | linkType: hard 2128 | 2129 | "smart-buffer@npm:^4.2.0": 2130 | version: 4.2.0 2131 | resolution: "smart-buffer@npm:4.2.0" 2132 | checksum: 10/927484aa0b1640fd9473cee3e0a0bcad6fce93fd7bbc18bac9ad0c33686f5d2e2c422fba24b5899c184524af01e11dd2bd051c2bf2b07e47aff8ca72cbfc60d2 2133 | languageName: node 2134 | linkType: hard 2135 | 2136 | "socks-proxy-agent@npm:^8.0.3": 2137 | version: 8.0.5 2138 | resolution: "socks-proxy-agent@npm:8.0.5" 2139 | dependencies: 2140 | agent-base: "npm:^7.1.2" 2141 | debug: "npm:^4.3.4" 2142 | socks: "npm:^2.8.3" 2143 | checksum: 10/ee99e1dacab0985b52cbe5a75640be6e604135e9489ebdc3048635d186012fbaecc20fbbe04b177dee434c319ba20f09b3e7dfefb7d932466c0d707744eac05c 2144 | languageName: node 2145 | linkType: hard 2146 | 2147 | "socks@npm:^2.8.3": 2148 | version: 2.8.4 2149 | resolution: "socks@npm:2.8.4" 2150 | dependencies: 2151 | ip-address: "npm:^9.0.5" 2152 | smart-buffer: "npm:^4.2.0" 2153 | checksum: 10/ab3af97aeb162f32c80e176c717ccf16a11a6ebb4656a62b94c0f96495ea2a1f4a8206c04b54438558485d83d0c5f61920c07a1a5d3963892a589b40cc6107dd 2154 | languageName: node 2155 | linkType: hard 2156 | 2157 | "sprintf-js@npm:^1.1.3": 2158 | version: 1.1.3 2159 | resolution: "sprintf-js@npm:1.1.3" 2160 | checksum: 10/e7587128c423f7e43cc625fe2f87e6affdf5ca51c1cc468e910d8aaca46bb44a7fbcfa552f787b1d3987f7043aeb4527d1b99559e6621e01b42b3f45e5a24cbb 2161 | languageName: node 2162 | linkType: hard 2163 | 2164 | "ssri@npm:^12.0.0": 2165 | version: 12.0.0 2166 | resolution: "ssri@npm:12.0.0" 2167 | dependencies: 2168 | minipass: "npm:^7.0.3" 2169 | checksum: 10/7024c1a6e39b3f18aa8f1c8290e884fe91b0f9ca5a6c6d410544daad54de0ba664db879afe16412e187c6c292fd60b937f047ee44292e5c2af2dcc6d8e1a9b48 2170 | languageName: node 2171 | linkType: hard 2172 | 2173 | "string-argv@npm:^0.3.2": 2174 | version: 0.3.2 2175 | resolution: "string-argv@npm:0.3.2" 2176 | checksum: 10/f9d3addf887026b4b5f997a271149e93bf71efc8692e7dc0816e8807f960b18bcb9787b45beedf0f97ff459575ee389af3f189d8b649834cac602f2e857e75af 2177 | languageName: node 2178 | linkType: hard 2179 | 2180 | "string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0": 2181 | version: 4.2.2 2182 | resolution: "string-width@npm:4.2.2" 2183 | dependencies: 2184 | emoji-regex: "npm:^8.0.0" 2185 | is-fullwidth-code-point: "npm:^3.0.0" 2186 | strip-ansi: "npm:^6.0.0" 2187 | checksum: 10/343e089b0e66e0f72aab4ad1d9b6f2c9cc5255844b0c83fd9b53f2a3b3fd0421bdd6cb05be96a73117eb012db0887a6c1d64ca95aaa50c518e48980483fea0ab 2188 | languageName: node 2189 | linkType: hard 2190 | 2191 | "string-width@npm:^4.2.3": 2192 | version: 4.2.3 2193 | resolution: "string-width@npm:4.2.3" 2194 | dependencies: 2195 | emoji-regex: "npm:^8.0.0" 2196 | is-fullwidth-code-point: "npm:^3.0.0" 2197 | strip-ansi: "npm:^6.0.1" 2198 | checksum: 10/e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb 2199 | languageName: node 2200 | linkType: hard 2201 | 2202 | "string-width@npm:^5.0.1, string-width@npm:^5.1.2": 2203 | version: 5.1.2 2204 | resolution: "string-width@npm:5.1.2" 2205 | dependencies: 2206 | eastasianwidth: "npm:^0.2.0" 2207 | emoji-regex: "npm:^9.2.2" 2208 | strip-ansi: "npm:^7.0.1" 2209 | checksum: 10/7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 2210 | languageName: node 2211 | linkType: hard 2212 | 2213 | "string-width@npm:^7.0.0": 2214 | version: 7.0.0 2215 | resolution: "string-width@npm:7.0.0" 2216 | dependencies: 2217 | emoji-regex: "npm:^10.3.0" 2218 | get-east-asian-width: "npm:^1.0.0" 2219 | strip-ansi: "npm:^7.1.0" 2220 | checksum: 10/bc0de5700a2690895169fce447ec4ed44bc62de80312c2093d5606bfd48319bb88e48a99e97f269dff2bc9577448b91c26b3804c16e7d9b389699795e4655c3b 2221 | languageName: node 2222 | linkType: hard 2223 | 2224 | "string-width@npm:^8.0.0": 2225 | version: 8.1.0 2226 | resolution: "string-width@npm:8.1.0" 2227 | dependencies: 2228 | get-east-asian-width: "npm:^1.3.0" 2229 | strip-ansi: "npm:^7.1.0" 2230 | checksum: 10/51ee97c4ffee7b94f8a2ee785fac14f81ec9809b9fcec9a4db44e25c717c263af0cc4387c111aef76195c0718dc43766f3678c07fb542294fb0244f7bfbde883 2231 | languageName: node 2232 | linkType: hard 2233 | 2234 | "string.prototype.trimend@npm:^1.0.4": 2235 | version: 1.0.4 2236 | resolution: "string.prototype.trimend@npm:1.0.4" 2237 | dependencies: 2238 | call-bind: "npm:^1.0.2" 2239 | define-properties: "npm:^1.1.3" 2240 | checksum: 10/5733b0f9801276387be136f1591883fc1b6371e263533d7797dc6178916a98bd9f632870f25e58a827ed028c17003b70e37650e80fc6703af6883cb2f3b0c1b3 2241 | languageName: node 2242 | linkType: hard 2243 | 2244 | "string.prototype.trimstart@npm:^1.0.4": 2245 | version: 1.0.4 2246 | resolution: "string.prototype.trimstart@npm:1.0.4" 2247 | dependencies: 2248 | call-bind: "npm:^1.0.2" 2249 | define-properties: "npm:^1.1.3" 2250 | checksum: 10/18e0b7362c51f566a3de095c4bab953174897f6cebe92826234f04d2744b0fdb25095c74661e0c15776d1338d64965be0a4f9c8be8851ee15b827c63a5280fdb 2251 | languageName: node 2252 | linkType: hard 2253 | 2254 | "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.1": 2255 | version: 6.0.1 2256 | resolution: "strip-ansi@npm:6.0.1" 2257 | dependencies: 2258 | ansi-regex: "npm:^5.0.1" 2259 | checksum: 10/ae3b5436d34fadeb6096367626ce987057713c566e1e7768818797e00ac5d62023d0f198c4e681eae9e20701721980b26a64a8f5b91238869592a9c6800719a2 2260 | languageName: node 2261 | linkType: hard 2262 | 2263 | "strip-ansi@npm:^6.0.0": 2264 | version: 6.0.0 2265 | resolution: "strip-ansi@npm:6.0.0" 2266 | dependencies: 2267 | ansi-regex: "npm:^5.0.0" 2268 | checksum: 10/fb33042c065e35dd33f82daf780252c855c520250bf2cc9257718e2868efbfd93f0712e0efc5e90750a0f806ad73971c1ac67785b532563df18aad4fddfde74d 2269 | languageName: node 2270 | linkType: hard 2271 | 2272 | "strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": 2273 | version: 7.1.0 2274 | resolution: "strip-ansi@npm:7.1.0" 2275 | dependencies: 2276 | ansi-regex: "npm:^6.0.1" 2277 | checksum: 10/475f53e9c44375d6e72807284024ac5d668ee1d06010740dec0b9744f2ddf47de8d7151f80e5f6190fc8f384e802fdf9504b76a7e9020c9faee7103623338be2 2278 | languageName: node 2279 | linkType: hard 2280 | 2281 | "strip-json-comments@npm:^3.1.1": 2282 | version: 3.1.1 2283 | resolution: "strip-json-comments@npm:3.1.1" 2284 | checksum: 10/492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 2285 | languageName: node 2286 | linkType: hard 2287 | 2288 | "supports-color@npm:^5.3.0": 2289 | version: 5.5.0 2290 | resolution: "supports-color@npm:5.5.0" 2291 | dependencies: 2292 | has-flag: "npm:^3.0.0" 2293 | checksum: 10/5f505c6fa3c6e05873b43af096ddeb22159831597649881aeb8572d6fe3b81e798cc10840d0c9735e0026b250368851b7f77b65e84f4e4daa820a4f69947f55b 2294 | languageName: node 2295 | linkType: hard 2296 | 2297 | "supports-color@npm:^7.1.0": 2298 | version: 7.2.0 2299 | resolution: "supports-color@npm:7.2.0" 2300 | dependencies: 2301 | has-flag: "npm:^4.0.0" 2302 | checksum: 10/c8bb7afd564e3b26b50ca6ee47572c217526a1389fe018d00345856d4a9b08ffbd61fadaf283a87368d94c3dcdb8f5ffe2650a5a65863e21ad2730ca0f05210a 2303 | languageName: node 2304 | linkType: hard 2305 | 2306 | "supports-color@npm:^8.1.1": 2307 | version: 8.1.1 2308 | resolution: "supports-color@npm:8.1.1" 2309 | dependencies: 2310 | has-flag: "npm:^4.0.0" 2311 | checksum: 10/157b534df88e39c5518c5e78c35580c1eca848d7dbaf31bbe06cdfc048e22c7ff1a9d046ae17b25691128f631a51d9ec373c1b740c12ae4f0de6e292037e4282 2312 | languageName: node 2313 | linkType: hard 2314 | 2315 | "tar@npm:^7.4.3": 2316 | version: 7.4.3 2317 | resolution: "tar@npm:7.4.3" 2318 | dependencies: 2319 | "@isaacs/fs-minipass": "npm:^4.0.0" 2320 | chownr: "npm:^3.0.0" 2321 | minipass: "npm:^7.1.2" 2322 | minizlib: "npm:^3.0.1" 2323 | mkdirp: "npm:^3.0.1" 2324 | yallist: "npm:^5.0.0" 2325 | checksum: 10/12a2a4fc6dee23e07cc47f1aeb3a14a1afd3f16397e1350036a8f4cdfee8dcac7ef5978337a4e7b2ac2c27a9a6d46388fc2088ea7c80cb6878c814b1425f8ecf 2326 | languageName: node 2327 | linkType: hard 2328 | 2329 | "to-regex-range@npm:^5.0.1": 2330 | version: 5.0.1 2331 | resolution: "to-regex-range@npm:5.0.1" 2332 | dependencies: 2333 | is-number: "npm:^7.0.0" 2334 | checksum: 10/10dda13571e1f5ad37546827e9b6d4252d2e0bc176c24a101252153ef435d83696e2557fe128c4678e4e78f5f01e83711c703eef9814eb12dab028580d45980a 2335 | languageName: node 2336 | linkType: hard 2337 | 2338 | "ts-node@npm:^10.9.2": 2339 | version: 10.9.2 2340 | resolution: "ts-node@npm:10.9.2" 2341 | dependencies: 2342 | "@cspotcode/source-map-support": "npm:^0.8.0" 2343 | "@tsconfig/node10": "npm:^1.0.7" 2344 | "@tsconfig/node12": "npm:^1.0.7" 2345 | "@tsconfig/node14": "npm:^1.0.0" 2346 | "@tsconfig/node16": "npm:^1.0.2" 2347 | acorn: "npm:^8.4.1" 2348 | acorn-walk: "npm:^8.1.1" 2349 | arg: "npm:^4.1.0" 2350 | create-require: "npm:^1.1.0" 2351 | diff: "npm:^4.0.1" 2352 | make-error: "npm:^1.1.1" 2353 | v8-compile-cache-lib: "npm:^3.0.1" 2354 | yn: "npm:3.1.1" 2355 | peerDependencies: 2356 | "@swc/core": ">=1.2.50" 2357 | "@swc/wasm": ">=1.2.50" 2358 | "@types/node": "*" 2359 | typescript: ">=2.7" 2360 | peerDependenciesMeta: 2361 | "@swc/core": 2362 | optional: true 2363 | "@swc/wasm": 2364 | optional: true 2365 | bin: 2366 | ts-node: dist/bin.js 2367 | ts-node-cwd: dist/bin-cwd.js 2368 | ts-node-esm: dist/bin-esm.js 2369 | ts-node-script: dist/bin-script.js 2370 | ts-node-transpile-only: dist/bin-transpile.js 2371 | ts-script: dist/bin-script-deprecated.js 2372 | checksum: 10/a91a15b3c9f76ac462f006fa88b6bfa528130dcfb849dd7ef7f9d640832ab681e235b8a2bc58ecde42f72851cc1d5d4e22c901b0c11aa51001ea1d395074b794 2373 | languageName: node 2374 | linkType: hard 2375 | 2376 | "typescript@npm:^5.2.2": 2377 | version: 5.9.3 2378 | resolution: "typescript@npm:5.9.3" 2379 | bin: 2380 | tsc: bin/tsc 2381 | tsserver: bin/tsserver 2382 | checksum: 10/c089d9d3da2729fd4ac517f9b0e0485914c4b3c26f80dc0cffcb5de1719a17951e92425d55db59515c1a7ddab65808466debb864d0d56dcf43f27007d0709594 2383 | languageName: node 2384 | linkType: hard 2385 | 2386 | "typescript@patch:typescript@npm%3A^5.2.2#optional!builtin": 2387 | version: 5.9.3 2388 | resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" 2389 | bin: 2390 | tsc: bin/tsc 2391 | tsserver: bin/tsserver 2392 | checksum: 10/696e1b017bc2635f4e0c94eb4435357701008e2f272f553d06e35b494b8ddc60aa221145e286c28ace0c89ee32827a28c2040e3a69bdc108b1a5dc8fb40b72e3 2393 | languageName: node 2394 | linkType: hard 2395 | 2396 | "unbox-primitive@npm:^1.0.0": 2397 | version: 1.0.1 2398 | resolution: "unbox-primitive@npm:1.0.1" 2399 | dependencies: 2400 | function-bind: "npm:^1.1.1" 2401 | has-bigints: "npm:^1.0.1" 2402 | has-symbols: "npm:^1.0.2" 2403 | which-boxed-primitive: "npm:^1.0.2" 2404 | checksum: 10/16aacdfc555545a89ddc678f136029ead18215f6843b9b707ab383cdc2f739efc34470b6b79c36ce7d376432f75b65b4ecb437d20f97196ba9d4683db0425ea3 2405 | languageName: node 2406 | linkType: hard 2407 | 2408 | "unique-filename@npm:^4.0.0": 2409 | version: 4.0.0 2410 | resolution: "unique-filename@npm:4.0.0" 2411 | dependencies: 2412 | unique-slug: "npm:^5.0.0" 2413 | checksum: 10/6a62094fcac286b9ec39edbd1f8f64ff92383baa430af303dfed1ffda5e47a08a6b316408554abfddd9730c78b6106bef4ca4d02c1231a735ddd56ced77573df 2414 | languageName: node 2415 | linkType: hard 2416 | 2417 | "unique-slug@npm:^5.0.0": 2418 | version: 5.0.0 2419 | resolution: "unique-slug@npm:5.0.0" 2420 | dependencies: 2421 | imurmurhash: "npm:^0.1.4" 2422 | checksum: 10/beafdf3d6f44990e0a5ce560f8f881b4ee811be70b6ba0db25298c31c8cf525ed963572b48cd03be1c1349084f9e339be4241666d7cf1ebdad20598d3c652b27 2423 | languageName: node 2424 | linkType: hard 2425 | 2426 | "util@npm:^0.12.5": 2427 | version: 0.12.5 2428 | resolution: "util@npm:0.12.5" 2429 | dependencies: 2430 | inherits: "npm:^2.0.3" 2431 | is-arguments: "npm:^1.0.4" 2432 | is-generator-function: "npm:^1.0.7" 2433 | is-typed-array: "npm:^1.1.3" 2434 | which-typed-array: "npm:^1.1.2" 2435 | checksum: 10/61a10de7753353dd4d744c917f74cdd7d21b8b46379c1e48e1c4fd8e83f8190e6bd9978fc4e5102ab6a10ebda6019d1b36572fa4a325e175ec8b789a121f6147 2436 | languageName: node 2437 | linkType: hard 2438 | 2439 | "v8-compile-cache-lib@npm:^3.0.1": 2440 | version: 3.0.1 2441 | resolution: "v8-compile-cache-lib@npm:3.0.1" 2442 | checksum: 10/88d3423a52b6aaf1836be779cab12f7016d47ad8430dffba6edf766695e6d90ad4adaa3d8eeb512cc05924f3e246c4a4ca51e089dccf4402caa536b5e5be8961 2443 | languageName: node 2444 | linkType: hard 2445 | 2446 | "which-boxed-primitive@npm:^1.0.2": 2447 | version: 1.0.2 2448 | resolution: "which-boxed-primitive@npm:1.0.2" 2449 | dependencies: 2450 | is-bigint: "npm:^1.0.1" 2451 | is-boolean-object: "npm:^1.1.0" 2452 | is-number-object: "npm:^1.0.4" 2453 | is-string: "npm:^1.0.5" 2454 | is-symbol: "npm:^1.0.3" 2455 | checksum: 10/9c7ca7855255f25ac47f4ce8b59c4cc33629e713fd7a165c9d77a2bb47bf3d9655a5664660c70337a3221cf96742f3589fae15a3a33639908d33e29aa2941efb 2456 | languageName: node 2457 | linkType: hard 2458 | 2459 | "which-pm-runs@npm:^1.0.0": 2460 | version: 1.0.0 2461 | resolution: "which-pm-runs@npm:1.0.0" 2462 | checksum: 10/30cf7aee31f264558070e92414316c169367bb2b84a0a32777d30392fea0892fcf9955b81c3fe7f52165ae5a33f0acfd3bc0916416cb07e6d414c90255c228ca 2463 | languageName: node 2464 | linkType: hard 2465 | 2466 | "which-typed-array@npm:^1.1.2": 2467 | version: 1.1.4 2468 | resolution: "which-typed-array@npm:1.1.4" 2469 | dependencies: 2470 | available-typed-arrays: "npm:^1.0.2" 2471 | call-bind: "npm:^1.0.0" 2472 | es-abstract: "npm:^1.18.0-next.1" 2473 | foreach: "npm:^2.0.5" 2474 | function-bind: "npm:^1.1.1" 2475 | has-symbols: "npm:^1.0.1" 2476 | is-typed-array: "npm:^1.1.3" 2477 | checksum: 10/1fd2df2fc6fe5603280d9d2ef4bc05ff34004e62b317ecd8b0b63cc354cdd933690efae7b14cb7895964ae53fadbdd2859f43a5da6719e3e1b3e8eb59241351e 2478 | languageName: node 2479 | linkType: hard 2480 | 2481 | "which@npm:^2.0.1": 2482 | version: 2.0.2 2483 | resolution: "which@npm:2.0.2" 2484 | dependencies: 2485 | isexe: "npm:^2.0.0" 2486 | bin: 2487 | node-which: ./bin/node-which 2488 | checksum: 10/4782f8a1d6b8fc12c65e968fea49f59752bf6302dc43036c3bf87da718a80710f61a062516e9764c70008b487929a73546125570acea95c5b5dcc8ac3052c70f 2489 | languageName: node 2490 | linkType: hard 2491 | 2492 | "which@npm:^5.0.0": 2493 | version: 5.0.0 2494 | resolution: "which@npm:5.0.0" 2495 | dependencies: 2496 | isexe: "npm:^3.1.1" 2497 | bin: 2498 | node-which: bin/which.js 2499 | checksum: 10/6ec99e89ba32c7e748b8a3144e64bfc74aa63e2b2eacbb61a0060ad0b961eb1a632b08fb1de067ed59b002cec3e21de18299216ebf2325ef0f78e0f121e14e90 2500 | languageName: node 2501 | linkType: hard 2502 | 2503 | "workerpool@npm:^9.2.0": 2504 | version: 9.3.2 2505 | resolution: "workerpool@npm:9.3.2" 2506 | checksum: 10/3d6354bea57aa7a459400858f04e3e9f19438b2783435f35d230248aee7b8c36e49596ea7574c4d8463e854784f28c50aab0a78a515e60315573f74c82d44c82 2507 | languageName: node 2508 | linkType: hard 2509 | 2510 | "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": 2511 | version: 7.0.0 2512 | resolution: "wrap-ansi@npm:7.0.0" 2513 | dependencies: 2514 | ansi-styles: "npm:^4.0.0" 2515 | string-width: "npm:^4.1.0" 2516 | strip-ansi: "npm:^6.0.0" 2517 | checksum: 10/cebdaeca3a6880da410f75209e68cd05428580de5ad24535f22696d7d9cab134d1f8498599f344c3cf0fb37c1715807a183778d8c648d6cc0cb5ff2bb4236540 2518 | languageName: node 2519 | linkType: hard 2520 | 2521 | "wrap-ansi@npm:^8.1.0": 2522 | version: 8.1.0 2523 | resolution: "wrap-ansi@npm:8.1.0" 2524 | dependencies: 2525 | ansi-styles: "npm:^6.1.0" 2526 | string-width: "npm:^5.0.1" 2527 | strip-ansi: "npm:^7.0.1" 2528 | checksum: 10/7b1e4b35e9bb2312d2ee9ee7dc95b8cb5f8b4b5a89f7dde5543fe66c1e3715663094defa50d75454ac900bd210f702d575f15f3f17fa9ec0291806d2578d1ddf 2529 | languageName: node 2530 | linkType: hard 2531 | 2532 | "wrap-ansi@npm:^9.0.0": 2533 | version: 9.0.0 2534 | resolution: "wrap-ansi@npm:9.0.0" 2535 | dependencies: 2536 | ansi-styles: "npm:^6.2.1" 2537 | string-width: "npm:^7.0.0" 2538 | strip-ansi: "npm:^7.1.0" 2539 | checksum: 10/b9d91564c091cf3978a7c18ca0f3e4d4606e83549dbe59cf76f5e77feefdd5ec91443155e8102630524d10a8c275efac8a7082c0f26fa43e6b989dc150d176ce 2540 | languageName: node 2541 | linkType: hard 2542 | 2543 | "y18n@npm:^5.0.5": 2544 | version: 5.0.5 2545 | resolution: "y18n@npm:5.0.5" 2546 | checksum: 10/aa5307f9fe60030928c9f5401b0352009030a05404aae78bfa1d943661cca147a6ff3702466241da7a8d82eb15e952343c501db239854b57f3f28e73999196de 2547 | languageName: node 2548 | linkType: hard 2549 | 2550 | "yallist@npm:^4.0.0": 2551 | version: 4.0.0 2552 | resolution: "yallist@npm:4.0.0" 2553 | checksum: 10/4cb02b42b8a93b5cf50caf5d8e9beb409400a8a4d85e83bb0685c1457e9ac0b7a00819e9f5991ac25ffabb56a78e2f017c1acc010b3a1babfe6de690ba531abd 2554 | languageName: node 2555 | linkType: hard 2556 | 2557 | "yallist@npm:^5.0.0": 2558 | version: 5.0.0 2559 | resolution: "yallist@npm:5.0.0" 2560 | checksum: 10/1884d272d485845ad04759a255c71775db0fac56308764b4c77ea56a20d56679fad340213054c8c9c9c26fcfd4c4b2a90df993b7e0aaf3cdb73c618d1d1a802a 2561 | languageName: node 2562 | linkType: hard 2563 | 2564 | "yaml@npm:^1.10.0": 2565 | version: 1.10.2 2566 | resolution: "yaml@npm:1.10.2" 2567 | checksum: 10/e088b37b4d4885b70b50c9fa1b7e54bd2e27f5c87205f9deaffd1fb293ab263d9c964feadb9817a7b129a5bf30a06582cb08750f810568ecc14f3cdbabb79cb3 2568 | languageName: node 2569 | linkType: hard 2570 | 2571 | "yaml@npm:^2.8.1": 2572 | version: 2.8.1 2573 | resolution: "yaml@npm:2.8.1" 2574 | bin: 2575 | yaml: bin.mjs 2576 | checksum: 10/eae07b3947d405012672ec17ce27348aea7d1fa0534143355d24a43a58f5e05652157ea2182c4fe0604f0540be71f99f1173f9d61018379404507790dff17665 2577 | languageName: node 2578 | linkType: hard 2579 | 2580 | "yargs-parser@npm:^21.1.1": 2581 | version: 21.1.1 2582 | resolution: "yargs-parser@npm:21.1.1" 2583 | checksum: 10/9dc2c217ea3bf8d858041252d43e074f7166b53f3d010a8c711275e09cd3d62a002969a39858b92bbda2a6a63a585c7127014534a560b9c69ed2d923d113406e 2584 | languageName: node 2585 | linkType: hard 2586 | 2587 | "yargs-unparser@npm:^2.0.0": 2588 | version: 2.0.0 2589 | resolution: "yargs-unparser@npm:2.0.0" 2590 | dependencies: 2591 | camelcase: "npm:^6.0.0" 2592 | decamelize: "npm:^4.0.0" 2593 | flat: "npm:^5.0.2" 2594 | is-plain-obj: "npm:^2.1.0" 2595 | checksum: 10/68f9a542c6927c3768c2f16c28f71b19008710abd6b8f8efbac6dcce26bbb68ab6503bed1d5994bdbc2df9a5c87c161110c1dfe04c6a3fe5c6ad1b0e15d9a8a3 2596 | languageName: node 2597 | linkType: hard 2598 | 2599 | "yargs@npm:^17.7.2": 2600 | version: 17.7.2 2601 | resolution: "yargs@npm:17.7.2" 2602 | dependencies: 2603 | cliui: "npm:^8.0.1" 2604 | escalade: "npm:^3.1.1" 2605 | get-caller-file: "npm:^2.0.5" 2606 | require-directory: "npm:^2.1.1" 2607 | string-width: "npm:^4.2.3" 2608 | y18n: "npm:^5.0.5" 2609 | yargs-parser: "npm:^21.1.1" 2610 | checksum: 10/abb3e37678d6e38ea85485ed86ebe0d1e3464c640d7d9069805ea0da12f69d5a32df8e5625e370f9c96dd1c2dc088ab2d0a4dd32af18222ef3c4224a19471576 2611 | languageName: node 2612 | linkType: hard 2613 | 2614 | "yn@npm:3.1.1": 2615 | version: 3.1.1 2616 | resolution: "yn@npm:3.1.1" 2617 | checksum: 10/2c487b0e149e746ef48cda9f8bad10fc83693cd69d7f9dcd8be4214e985de33a29c9e24f3c0d6bcf2288427040a8947406ab27f7af67ee9456e6b84854f02dd6 2618 | languageName: node 2619 | linkType: hard 2620 | 2621 | "yocto-queue@npm:^0.1.0": 2622 | version: 0.1.0 2623 | resolution: "yocto-queue@npm:0.1.0" 2624 | checksum: 10/f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 2625 | languageName: node 2626 | linkType: hard 2627 | --------------------------------------------------------------------------------