diff --git a/.codebeatignore b/.codebeatignore
deleted file mode 100644
index 3669b72..0000000
--- a/.codebeatignore
+++ /dev/null
@@ -1 +0,0 @@
-benchmark/**
diff --git a/.codebeatsettings b/.codebeatsettings
deleted file mode 100644
index 84f6f2a..0000000
--- a/.codebeatsettings
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "TYPESCRIPT": {
- "TOTAL_LOC": [500, 1000, 1500, 2000],
- "TOO_MANY_FUNCTIONS": [40, 50, 60, 70],
- "TOTAL_COMPLEXITY": [100, 180, 280, 400]
- }
-}
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 67bcd75..c564f7b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,16 +2,49 @@ name: Build
on:
push:
+ branches: [master]
+ tags: ['*']
pull_request:
+permissions:
+ contents: read
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
jobs:
- build:
+ quality:
+ name: Lint & format
runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 24.x
+ cache: npm
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Check formatting
+ run: npm run format:check
+ - name: Lint
+ run: npm run lint
+
+ test:
+ name: Test (Node ${{ matrix.node-version }})
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
strategy:
+ fail-fast: false
matrix:
- node-version: [lts/*]
-
+ node-version: [22.x, 24.x]
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -20,9 +53,10 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
+ cache: npm
- name: Install dependencies
- run: npm install
+ run: npm ci
- name: Run tests
run: npm test
diff --git a/.gitignore b/.gitignore
index e04899e..229818a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ tmp/
out-tsc/
build/
.nyc_output/
+.agent-out/
node_modules/
diff --git a/.oxfmtrc.json b/.oxfmtrc.json
new file mode 100644
index 0000000..16d1869
--- /dev/null
+++ b/.oxfmtrc.json
@@ -0,0 +1,9 @@
+{
+ "singleQuote": true,
+ "tabWidth": 4,
+ "printWidth": 80,
+ "semi": true,
+ "trailingComma": "all",
+ "arrowParens": "avoid",
+ "bracketSpacing": true
+}
diff --git a/.oxlintrc.json b/.oxlintrc.json
new file mode 100644
index 0000000..2757f3e
--- /dev/null
+++ b/.oxlintrc.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
+ "categories": {
+ "correctness": "error"
+ },
+ "rules": {
+ "no-debugger": "error",
+ "no-console": ["warn", { "allow": ["warn", "error", "info"] }],
+ "no-unused-vars": "warn"
+ },
+ "ignorePatterns": [
+ "**/*.js",
+ "**/*.d.ts",
+ "docs/",
+ "coverage/",
+ ".nyc_output/",
+ ".agent-out/",
+ "node_modules/"
+ ]
+}
diff --git a/README.md b/README.md
index b11ca5f..d33e73a 100644
--- a/README.md
+++ b/README.md
@@ -2,63 +2,63 @@
[](https://rawgit.com/imqueue/core/master/LICENSE)
-Simple JSON-based messaging queue for inter service communication
+Simple JSON-based messaging queue for inter-service communication
**Related packages:**
- - [@imqueue/rpc](https://github.com/imqueue/rpc) - RPC-like client/service
- implementation over @imqueue/core.
- - [@imqueue/cli](https://github.com/imqueue/cli) - Command Line Interface
- for imqueue.
+- [@imqueue/rpc](https://github.com/imqueue/rpc) - RPC-like client/service
+ implementation over @imqueue/core.
+- [@imqueue/cli](https://github.com/imqueue/cli) - Command Line Interface
+ for imqueue.
# Features
With current implementation on RedisQueue:
- - **Fast unwarranted** message delivery (if consumer grab the message and dies
- message will be lost). Up to ~35-40k of 1Kb messages per second on i7 core by
- benchmarks.
- - **Fast warranted** message delivery (only 1.5-2 times slower than
- unwarranted). If consumer grab a message and dies it will be re-scheduled
- in queue. Up to ~20-25K of 1Kb messages per second on i7 core by benchmarks.
- - **No timers or constant redis polling** used for implementation, as result -
- no delays in delivery and low CPU usage on application workers. When idling
- it does not consume resources!
- - **Supports gzip compression for messages** (decrease traffic usage, but
- slower).
- - **Concurrent workers model supported**, the same queue can have multiple
- consumers.
- - **Delayed messages supported**, fast as ~10K of 1Kb messages per second on i7
- core by benchmarks.
- - **Safe predictable scaling of queues**. Scaling number of workers does not
- influence traffic usage.
- - **Round-robin message balancing between several redis instances**. This
- allows easy messaging queue redis horizontal scaling.
- - **TypeScript included!**
+- **Fast, unreliable** message delivery (if a consumer grabs the message and dies,
+ the message will be lost). Up to ~35–40k of 1Kb messages per second on an i7 core
+ by benchmarks.
+- **Fast, guaranteed** message delivery (only 1.5–2 times slower than
+ unreliable mode). If a consumer grabs a message and dies, it will be rescheduled
+ to the queue. Up to ~20–25k of 1Kb messages per second on an i7 core by benchmarks.
+- **No timers or constant Redis polling** used for implementation, resulting in
+ no delays in delivery and low CPU usage on application workers. When idle,
+ it consumes no resources.
+- **Supports Gzip compression for messages** (decreases traffic usage but is slower).
+- **Concurrent workers model supported**, the same queue can have multiple
+ consumers.
+- **Delayed messages supported**, fast as ~10K of 1Kb messages per second on i7
+ core by benchmarks.
+- **Safe, predictable scaling of queues**. Scaling the number of workers does not
+ increase traffic usage.
+- **Round-robin message balancing between multiple Redis instances**. This
+ allows easy horizontal scaling of the messaging queue across Redis instances.
+- **TypeScript included!**
# Requirements
-Currently this module have only one available adapter which is Redis server
-related. So redis-server > 3.8+ is required.
+Currently this module has only one available adapter, which is Redis. Redis server
+6.2+ is required (the queue relies on `LMOVE`/`BLMOVE` commands for safe message
+delivery).
-If config command is disabled on redis it will be required to turn on manually
-keyspace notification events (actual on use with ElasticCache on AWS), like:
+If the config command is disabled on Redis, you must manually enable keyspace
+notification events (particularly when using AWS ElasticCache), like this:
-~~~
+```
notify-keyspace-events Ex
-~~~
+```
-Further, more adapters will be added... if needed.
+More adapters will be added in the future as needed.
# Install
-~~~bash
+```bash
npm i --save @imqueue/core
-~~~
+```
# Usage
-~~~typescript
+```typescript
import IMQ, { IMessageQueue, IJson } from '@imqueue/core';
(async () => {
@@ -90,27 +90,27 @@ import IMQ, { IMessageQueue, IJson } from '@imqueue/core';
const delay = 1000;
await queueOne.send('QueueOne', { delay }, delay);
})();
-~~~
+```
# Benchmarking
-First of all make sure redis-server is running on the localhost. Current
-version of benchmark supposed to have redis running localhost because
-it is going to measure it's CPU usage stats.
+First, make sure redis-server is running on localhost. The current version of the
+benchmark requires Redis to be running on localhost so it can measure its CPU
+usage statistics.
-All workers during benchmark test will have their dedicated CPU affinity
-to make sure collected stats as accurate as possible.
+All workers during the benchmark test will have dedicated CPU affinity to ensure
+the collected statistics are as accurate as possible.
-~~~bash
-git clone git@github.com:Mikhus/core.git
-cd imq
+```bash
+git clone git@github.com:imqueue/core.git
+cd core
node benchmark -c 4 -m 10000
-~~~
+```
Other possible benchmark options:
-~~~
-node benchmark -h
+```
+node benchmark -h
Options:
--version Show version number [boolean]
-h, --help Show help [boolean]
@@ -128,30 +128,40 @@ Options:
-p, --port Redis server port to connect to.
-t, --message-multiply-times Increase sample message data given number of
times.
-~~~
+```
-Number of child workers running message queues are limited to a max number
-of CPU in the system -2. First one, which is CPU0 is reserved for OS tasks and
-for stats collector process. Second, which is CPU1 is dedicated to redis
-process running on a local machine, All others are safe to run queue workers.
+The number of child workers running message queues is limited to the number of CPUs
+in the system minus 2. The first CPU (CPU0) is reserved for OS tasks and the stats
+collector process. The second CPU (CPU1) is dedicated to the local Redis process.
+All others are available to run queue workers.
-For example, if there is 8 cores on a machine it is safe to run up to 6 workers.
-For 4-core machine this number is limited to 2.
-If there is less cores results will not give good visibility of load.
+For example, on an 8-core machine you can safely run up to 6 workers. On a 4-core
+machine, this limit is 2 workers. If there are fewer cores, the results will not
+provide good visibility of the load.
-*NOTE: paragraphs above this note are Linux-only related! On MacOS
-there is no good way to set process affinity to a CPU core, Windows support is
-not tested and is missing for the moment in benchmarking. I does not mean
-benchmark will not work on Mac & Win but the results won't be accurate and
-predictable.*
+**NOTE:** The paragraphs above apply to Linux only. On macOS there is no reliable way
+to set process CPU affinity, and Windows support is not currently implemented for
+benchmarking. This does not mean the benchmark won't work on macOS or Windows, but
+the results will not be accurate or predictable on those platforms.
## Running Unit Tests
-~~~bash
+Tests run on the native Node.js test runner (`node:test`) with `node:assert` and
+no external test framework, so a plain clone and install is all that is needed:
+
+```bash
git clone git@github.com:imqueue/core.git
-cd imq
+cd core
+npm install
npm test
-~~~
+```
+
+To produce a coverage report use:
+
+```bash
+npm run test-coverage # prints coverage summary to the console
+npm run test-lcov # writes coverage/lcov.info
+```
## License
diff --git a/benchmark/affinity.ts b/benchmark/affinity.ts
index 06cb7e3..6368362 100644
--- a/benchmark/affinity.ts
+++ b/benchmark/affinity.ts
@@ -21,11 +21,11 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
-import { execSync as exec } from 'child_process';
-import * as os from 'os';
+import { execSync as exec } from 'node:child_process';
+import { platform } from 'node:os';
export function setAffinity(cpu: number) {
- if (os.platform() === 'linux') {
+ if (platform() === 'linux') {
exec(`taskset -cp ${cpu} ${process.pid}`);
}
-}
\ No newline at end of file
+}
diff --git a/benchmark/index.ts b/benchmark/index.ts
index 3276452..f2c49dd 100644
--- a/benchmark/index.ts
+++ b/benchmark/index.ts
@@ -21,84 +21,141 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
-import { execSync as exec } from 'child_process';
-import * as os from 'os';
-import * as fs from 'fs';
-import * as yargs from 'yargs';
+import cluster from 'node:cluster';
+import { execSync as exec } from 'node:child_process';
+import { arch, cpus, freemem, platform, totalmem } from 'node:os';
+import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
+import { parseArgs } from 'node:util';
import { run } from './redis-test';
-import { resolve } from 'path';
-import { uuid, AnyJson } from '..';
+import { resolve } from 'node:path';
+import { randomUUID as uuid } from 'node:crypto';
+import { AnyJson } from '..';
import { setAffinity } from './affinity';
-const cluster: any = require('cluster');
+/**
+ * Command line options: canonical long name -> parseArgs config and description
+ * used to render `--help`.
+ */
+const OPTIONS: {
+ [name: string]: {
+ type: 'string' | 'boolean';
+ short: string;
+ describe: string;
+ };
+} = {
+ help: {
+ type: 'boolean',
+ short: 'h',
+ describe: 'Show this help.',
+ },
+ children: {
+ type: 'string',
+ short: 'c',
+ describe: 'Number of children test process to fork.',
+ },
+ delay: {
+ type: 'string',
+ short: 'd',
+ describe:
+ 'Number of milliseconds to delay message delivery for ' +
+ 'delayed messages. By default delayed messages is off and this ' +
+ 'argument is equal to 0.',
+ },
+ messages: {
+ type: 'string',
+ short: 'm',
+ describe:
+ 'Number of messages to be sent by a child process ' +
+ 'during test execution.',
+ },
+ gzip: {
+ type: 'boolean',
+ short: 'z',
+ describe: 'Use gzip for message encoding/decoding.',
+ },
+ safe: {
+ type: 'boolean',
+ short: 's',
+ describe: 'Use safe (guaranteed) message delivery algorithm.',
+ },
+ 'example-message': {
+ type: 'string',
+ short: 'e',
+ describe:
+ 'Path to a file containing JSON of example message to ' +
+ 'use during the tests.',
+ },
+ port: {
+ type: 'string',
+ short: 'p',
+ describe: 'Redis server port to connect to.',
+ },
+ 'message-multiply-times': {
+ type: 'string',
+ short: 't',
+ describe:
+ 'Increase sample message data given number of times per ' +
+ 'request.',
+ },
+};
/**
* Command line args
- * @type {yargs.Arguments}
*/
-const ARGV: any = yargs
-.help('h')
-.alias('h', 'help')
-
-.alias('c', 'children')
-.describe('c', 'Number of children test process to fork')
-
-.alias('d', 'delay')
-.describe('d', 'Number of milliseconds to delay message delivery for ' +
- 'delayed messages. By default delayed messages is of and this ' +
- 'argument is equal to 0.')
-
-.alias('m', 'messages')
-.describe('m', 'Number of messages to be sent by a child process ' +
- 'during test execution.')
-
-.alias('z', 'gzip')
-.describe('z', 'Use gzip for message encoding/decoding.')
-
-.alias('s', 'safe')
-.describe('s', 'Use safe (guaranteed) message delivery algorithm.')
-
-.alias('e', 'example-message')
-.describe('e', 'Path to a file containing JSON of example message to ' +
- 'use during the tests.')
-
-.alias('p', 'port')
-.describe('p', 'Redis server port to connect to.')
-
-.alias('t', 'message-multiply-times')
-.describe('t', 'Increase sample message data given number of times per ' +
- 'request.')
-
-.boolean(['h', 'z', 's'])
- .argv;
+const ARGV: any = parseArgs({
+ options: Object.fromEntries(
+ Object.entries(OPTIONS).map(([name, { type, short }]) => [
+ name,
+ { type, short },
+ ]),
+ ) as any,
+ allowPositionals: true,
+}).values;
+
+if (ARGV.help) {
+ const help = Object.entries(OPTIONS)
+ .map(([name, o]) => ` -${o.short}, --${name}\n ${o.describe}`)
+ .join('\n\n');
+
+ process.stdout.write(
+ `Usage: node benchmark [options]\n\nOptions:\n${help}\n`,
+ );
+ process.exit(0);
+}
-let maxChildren = Number(ARGV.c) || 1;
+let maxChildren = Number(ARGV.children) || 1;
const METRICS_DELAY = 100;
-const CPUS = os.cpus();
+const CPUS = cpus();
const numCpus = CPUS.length;
const CPU_NAMES = ['Redis Process, CPU Used, %'];
-const STEPS = Number(ARGV.m) || 10000;
-const MSG_DELAY = Number(ARGV.d) || 0;
-const USE_GZIP: boolean = !!ARGV.z;
-const MSG_MULTIPLIER = Number(ARGV.t) || 0;
-const SAFE_DELIVERY: boolean = !!ARGV.s;
-const REDIS_PORT: number = Number(ARGV.p) || 6379;
+const STEPS = Number(ARGV.messages) || 10000;
+const MSG_DELAY = Number(ARGV.delay) || 0;
+const USE_GZIP: boolean = !!ARGV.gzip;
+const MSG_MULTIPLIER = Number(ARGV['message-multiply-times']) || 0;
+const SAFE_DELIVERY: boolean = !!ARGV.safe;
+const REDIS_PORT: number = Number(ARGV.port) || 6379;
let SAMPLE_MESSAGE: AnyJson;
-if (ARGV.e) {
+if (ARGV['example-message']) {
try {
- SAMPLE_MESSAGE = JSON.parse(fs.readFileSync(ARGV.e + '').toString());
+ SAMPLE_MESSAGE = JSON.parse(
+ readFileSync(ARGV['example-message'] + '').toString(),
+ );
if (MSG_MULTIPLIER) {
- SAMPLE_MESSAGE = new Array(MSG_MULTIPLIER)
- .fill(SAMPLE_MESSAGE);
+ SAMPLE_MESSAGE = Array.from(
+ { length: MSG_MULTIPLIER },
+ () => SAMPLE_MESSAGE,
+ );
}
- } catch (err) {
- console.warn('Given example message is invalid, ' +
- 'proceeding test execution with with standard ' +
- 'example message.');
+ } catch {
+ console.warn(
+ 'Given example message is invalid, ' +
+ 'proceeding test execution with with standard ' +
+ 'example message.',
+ );
}
}
@@ -120,9 +177,9 @@ for (let i = 0; i < maxChildren; i++) {
* @param {number} i
* @returns {{idle: number, total: number}}
*/
-function cpuAvg(i: number) {
- const cpus = os.cpus();
- const cpu: any = cpus[i];
+function cpuAvg(i: number): { idle: number; total: number } {
+ const cpuList = cpus();
+ const cpu: any = cpuList[i];
let totalIdle = 0;
let totalTick = 0;
@@ -134,7 +191,7 @@ function cpuAvg(i: number) {
return {
idle: totalIdle / cpus.length,
- total: totalTick / cpus.length
+ total: totalTick / cpus.length,
};
}
@@ -143,16 +200,18 @@ interface MachineStats {
memStats: any[];
}
+interface BuildStatsInput {
+ metrics: any[];
+ memusage: any[];
+}
+
/**
- * Does stats aggregation and returns results
+ * Does stat aggregation and returns results
*
- * @param {any} metrics
- * @param {any} memusage
+ * @param {BuildStatsInput} input
* @return {MachineStats}
*/
-function buildStats(
- { metrics, memusage }: any
-): MachineStats {
+function buildStats({ metrics, memusage }: BuildStatsInput): MachineStats {
const stats: any[] = [];
const memStats: any[] = ['System Memory Used, %'];
@@ -165,67 +224,92 @@ function buildStats(
stats[cpu] = [CPU_NAMES[cpu]];
}
- stats[cpu].push(100 - ~~(100 * idle / total));
+ stats[cpu].push(100 - ~~((100 * idle) / total));
}
}
for (let i = 0, s = memusage.length; i < s; i++) {
- memStats.push(100 - ~~(100 * memusage[i].free / memusage[i].total));
+ memStats.push(100 - ~~((100 * memusage[i].free) / memusage[i].total));
}
return { stats, memStats };
}
+interface ChartConfig {
+ bindto: string;
+ data: { columns: any[] };
+ point: { show: boolean };
+ axis: {
+ x: {
+ type: string;
+ categories: any;
+ tick: {
+ centered: boolean;
+ fit: boolean;
+ culling: { max: number };
+ outer: boolean;
+ };
+ };
+ y: { max: number; tick: { outer: boolean } };
+ };
+ zoom: { enabled: boolean };
+}
+
/**
* Returns chart config for given chart id and stats
*
* @param {string} id
* @param {any[]} stats
- * @return {any}
+ * @return {ChartConfig}
*/
-function buildChartConfig(id: string, stats: any[]) {
+function buildChartConfig(id: string, stats: any[]): ChartConfig {
return {
bindto: `#${id}`,
data: {
- columns: stats
+ columns: stats,
},
point: { show: false },
axis: {
x: {
type: 'category',
- categories: stats[0].slice(1).map((v: any, i: number) =>
- ((i * 100) / 1000).toFixed(1) + 's' as any
- ),
+ categories: stats[0]
+ .slice(1)
+ .map(
+ (_: any, i: number) =>
+ ((i * 100) / 1000).toFixed(1) + 's',
+ ),
tick: {
centered: true,
fit: false,
culling: { max: 20 },
- outer: false
- }
+ outer: false,
+ },
},
y: {
max: 100,
- tick: { outer: false }
- }
+ tick: { outer: false },
+ },
},
zoom: {
- enabled: true
- }
+ enabled: true,
+ },
};
}
/**
- * Return bytes count for a given data and bytes key
+ * Return byte count for a given data and bytes key
*
* @param {any[]} data
* @param {Intl.NumberFormat} fmt
* @param {string} key
* @return {string}
*/
-function bytesCount(data: any[], fmt: Intl.NumberFormat, key: string) {
- return fmt.format(Math.round(data.reduce((prev, next) =>
- prev + next[key], 0
- ) / data.length))
+function bytesCount(data: any[], fmt: Intl.NumberFormat, key: string): string {
+ return fmt.format(
+ Math.round(
+ data.reduce((prev, next) => prev + next[key], 0) / data.length,
+ ),
+ );
}
/**
@@ -234,17 +318,15 @@ function bytesCount(data: any[], fmt: Intl.NumberFormat, key: string) {
* @param {{ metrics: any, memusage: any }} stats
* @param {any} data
*/
-function saveStats({ metrics, memusage }: any, data: any[]) {
+function saveStats({ metrics, memusage }: any, data: any[]) {
const { stats, memStats } = buildStats({ metrics, memusage });
const config = buildChartConfig('cpu-usage', stats);
const memConfig = buildChartConfig('memory-usage', [memStats]);
- const fmt = new Intl.NumberFormat(
- 'en-US', { maximumSignificantDigits: 3 }
- );
+ const fmt = new Intl.NumberFormat('en-US', { maximumSignificantDigits: 3 });
// language=HTML
let html = `
-
+
IMQ Benchmark results
@@ -254,57 +336,68 @@ function saveStats({ metrics, memusage }: any, data: any[]) {
IMQ Benchmark Results
- This test was executed using CPU affinity assignment for each running
- process. Redis server has it's own dedicated CPU core, all worker processes
+
This test was executed using a CPU affinity assignment for each running
+ process. Redis server has its own dedicated CPU core, all worker processes
are attached to their own cores as well. Each worker process running a bunch
- of requests simultaneously in asynchronous manner, so depending on the
- given test parameters all requests are executed almost at the same time.
+ of requests simultaneously in an asynchronous manner, so depending on the
+ given test parameters, all requests are executed almost at the same time.
- NOTE: In MacOS it is not possible to implement CPU affinity assignment for
+ NOTE: In macOS it is not possible to implement CPU affinity assignment for
a given process, so there is no way to guaranty a proper process load
visibility.
Test Execution Information
- - Execution Datetime: ${ new Date().toISOString() }
+ - Execution Datetime: ${new Date().toISOString()}
- System Info:
- - CPU: ${CPUS[0].model} × ${ numCpus } cores
- - CPU Clock Speed: ${ CPUS[0].speed }Mhz
- - RAM: ${ Math.ceil(os.totalmem() / Math.pow(1024, 3)) }GB
- - OS Architecture: ${ os.arch() }
- - OS Platform: ${ os.platform() }
- - Node Version: ${ process.versions.node }
+ - CPU: ${CPUS[0].model} × ${numCpus} cores
+ - CPU Clock Speed: ${CPUS[0].speed}Mhz
+ - RAM: ${Math.ceil(totalmem() / Math.pow(1024, 3))}GB
+ - OS Architecture: ${arch()}
+ - OS Platform: ${platform()}
+ - Node Version: ${process.versions.node}
- Number of workers: ${fmt.format(maxChildren)}
- Number of messages per worker: ${fmt.format(STEPS)}
- Total messages executed: ${fmt.format(STEPS * maxChildren)}
- - Round-trip ratio across all workers is: ${
- fmt.format(data.reduce((prev, next) =>
- prev + next.ratio, 0
- ))
- } msg/sec
- - Average message payload to redis is: ${
- bytesCount(data, fmt, 'bytesLen')
- } bytes
- - Average source message payload is: ${
- bytesCount(data, fmt, 'srcBytesLen')
- } bytes
- - Average time of all messages delivery is: ${
- fmt.format(Number((data.reduce((prev, next) =>
- prev + next.time, 0
- ) / 1000 / data.length).toFixed(2)))
- } sec ±10 ms
- - Max delivery time is: ${
- fmt.format(
- Number((Math.max.apply(null, data.map((item => item.time)))
- / 1000).toFixed(2)))
- } sec ±10 ms
+ - Round-trip ratio across all workers is: ${fmt.format(
+ data.reduce((prev, next) => prev + next.ratio, 0),
+ )} msg/sec
+ - Average message payload to redis is: ${bytesCount(
+ data,
+ fmt,
+ 'bytesLen',
+ )} bytes
+ - Average source message payload is: ${bytesCount(
+ data,
+ fmt,
+ 'srcBytesLen',
+ )} bytes
+ - The average time of all messages deliveries is: ${fmt.format(
+ Number(
+ (
+ data.reduce((prev, next) => prev + next.time, 0) /
+ 1000 /
+ data.length
+ ).toFixed(2),
+ ),
+ )} sec ±10 ms
+ - Max delivery time is: ${fmt.format(
+ Number(
+ (
+ Math.max.apply(
+ null,
+ data.map(item => item.time),
+ ) / 1000
+ ).toFixed(2),
+ ),
+ )} sec ±10 ms
${MSG_DELAY ? '- Message delivery delay used: ' + MSG_DELAY + '
' : ''}
- - Gzip compression for messages is: ${ USE_GZIP ? 'On' : 'Off' }
- - Safe delivery is: ${ SAFE_DELIVERY ? 'On' : 'Off' }
+ - Gzip compression for messages is: ${USE_GZIP ? 'On' : 'Off'}
+ - Safe delivery is: ${SAFE_DELIVERY ? 'On' : 'Off'}
CPU Usage
@@ -323,24 +416,20 @@ function saveStats({ metrics, memusage }: any, data: any[]) {
`;
const htmlFile = resolve(__dirname, `../benchmark-result/${uuid()}.html`);
- if (!fs.existsSync('./benchmark-result')) {
- fs.mkdirSync('./benchmark-result');
+ if (!existsSync('./benchmark-result')) {
+ mkdirSync('./benchmark-result');
}
- fs.writeFileSync(htmlFile, html, { encoding: 'utf8' });
-
- console.log('Benchmark stats saved!');
- console.log(`Opening file://${htmlFile}`);
+ writeFileSync(htmlFile, html, { encoding: 'utf8' });
- import('open').then(open =>
- open.default(`file://${htmlFile}`, { wait: false }),
- );
+ console.info('Benchmark stats saved!');
+ console.info(`Open in browser: file://${htmlFile}`);
process.exit(0);
}
// main program:
-if (cluster.isMaster) {
+if (cluster.isPrimary) {
setAffinity(1);
const statsWorker = cluster.fork();
@@ -350,7 +439,7 @@ if (cluster.isMaster) {
const data: any[] = [];
statsWorker.on('message', (msg: any) => {
- if (/^metrics:/.test(msg)) {
+ if (msg.startsWith('metrics:')) {
saveStats(JSON.parse(msg.split('metrics:').pop() || ''), data);
process.exit(0);
}
@@ -361,7 +450,7 @@ if (cluster.isMaster) {
worker.send(`imq ${i}`);
worker.on('message', (msg: string) => {
- if (/^data:/.test(msg)) {
+ if (msg.startsWith('data:')) {
done++;
data.push(JSON.parse(msg.split('data:').pop() || ''));
@@ -371,15 +460,13 @@ if (cluster.isMaster) {
}
});
}
-}
-
-else {
+} else {
const metrics: any[] = [];
const memusage: any[] = [];
let metricsInterval: any;
process.on('message', async (msg: string) => {
- if (/^imq/.test(msg)) {
+ if (msg.startsWith('imq')) {
const index = parseInt(String(msg.split(/\s+/).pop()), 10);
const core = numCpus <= 2 ? 1 : index + 2;
@@ -392,55 +479,56 @@ else {
MSG_DELAY,
USE_GZIP,
SAFE_DELIVERY,
- SAMPLE_MESSAGE
+ SAMPLE_MESSAGE,
);
(
process).send('data:' + JSON.stringify(data));
- }
-
- catch (err) {
+ } catch (err) {
(process).send('data:' + JSON.stringify(null));
- console.error(err.stack);
+ console.error(err instanceof Error ? err.stack : err);
process.exit(1);
}
- }
-
- else if (msg === 'stats') {
+ } else if (msg === 'stats') {
setAffinity(1);
const redisProcess = exec('ps ax|grep redis-server')
- .toString('utf8')
- .split(/\r?\n/)[0];
+ .toString('utf8')
+ .split(/\r?\n/)[0];
const core = numCpus > 1 ? 1 : 0;
- if (core && os.platform() === 'linux' &&
+ if (
+ core &&
+ platform() === 'linux' &&
/redis-server/.test(redisProcess) &&
!/grep/.test(redisProcess)
) {
const redisPid = parseInt(redisProcess.split(/\s+/)[0], 10);
- redisPid && exec(`taskset -cp ${core} ${redisPid}`);
+ if (redisPid) {
+ exec(`taskset -cp ${core} ${redisPid}`);
+ }
}
metricsInterval = setInterval(() => {
- metrics.push(
- CPU_NAMES.map((name: string, i: number) => cpuAvg(i + 1))
- );
+ metrics.push(CPU_NAMES.map((_, i: number) => cpuAvg(i + 1)));
memusage.push({
- total: os.totalmem(),
- free: os.freemem()
+ total: totalmem(),
+ free: freemem(),
});
}, METRICS_DELAY);
- }
-
- else if (msg === 'stop') {
- metricsInterval && clearInterval(metricsInterval);
+ } else if (msg === 'stop') {
+ if (metricsInterval) {
+ clearInterval(metricsInterval);
+ }
metricsInterval = null;
- console.log('Finalizing...');
- (process).send('metrics:' + JSON.stringify({
- metrics,
- memusage
- }));
+ console.info('Finalizing...');
+ (process).send(
+ 'metrics:' +
+ JSON.stringify({
+ metrics,
+ memusage,
+ }),
+ );
process.exit(0);
}
});
-}
\ No newline at end of file
+}
diff --git a/benchmark/redis-test.ts b/benchmark/redis-test.ts
index 82e7c17..77991cb 100644
--- a/benchmark/redis-test.ts
+++ b/benchmark/redis-test.ts
@@ -21,66 +21,66 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
-import IMQ, {
- IMQOptions,
- IJson,
- uuid,
- pack,
- JsonObject, AnyJson,
-} from '../index';
+import { randomUUID as uuid } from 'node:crypto';
+import IMQ, { IMQOptions, JsonObject, AnyJson } from '../index';
+import { pack } from '../src/helpers';
/**
* Sample message used within tests
*/
const JSON_EXAMPLE: JsonObject = {
- "Glossary": {
- "Title": "Example Glossary",
- "GlossDiv": {
- "Title": "ß∆",
- "GlossList": [{
- "GlossEntry": {
- "ID": "SGML",
- "SortAs": "SGML",
- "GlossTerm": "Standard Generalized Markup Language",
- "Acronym": "SGML",
- "Abbrev": "ISO 8879:1986",
- "GlossDef": {
- "para": "A meta-markup language, used to create markup languages such as DocBook.",
- "GlossSeeAlso": ["GML", "XML"]
+ Glossary: {
+ Title: 'Example Glossary',
+ GlossDiv: {
+ Title: 'ß∆',
+ GlossList: [
+ {
+ GlossEntry: {
+ ID: 'SGML',
+ SortAs: 'SGML',
+ GlossTerm: 'Standard Generalized Markup Language',
+ Acronym: 'SGML',
+ Abbrev: 'ISO 8879:1986',
+ GlossDef: {
+ para: 'A meta-markup language, used to create markup languages such as DocBook.',
+ GlossSeeAlso: ['GML', 'XML'],
+ },
+ GlossSee: 'markup, non-markup, joke-cup',
},
- "GlossSee": "markup, non-markup, joke-cup"
- }
- }, {
- "GlossEntry": {
- "ID": "SGML",
- "SortAs": "SGML",
- "GlossTerm": "Standard Generalized Markup Language",
- "Acronym": "SGML",
- "Abbrev": "ISO 8879:1986",
- "MoreBytes": "",
- "GlossDef": {
- "para": "A meta-markup language, used to create markup languages such as DocBook.",
- "GlossSeeAlso": ["GML", "XML", "FML", "RML", "FCL"]
+ },
+ {
+ GlossEntry: {
+ ID: 'SGML',
+ SortAs: 'SGML',
+ GlossTerm: 'Standard Generalized Markup Language',
+ Acronym: 'SGML',
+ Abbrev: 'ISO 8879:1986',
+ MoreBytes: '',
+ GlossDef: {
+ para: 'A meta-markup language, used to create markup languages such as DocBook.',
+ GlossSeeAlso: ['GML', 'XML', 'FML', 'RML', 'FCL'],
+ },
+ GlossSee: 'markup',
},
- "GlossSee": "markup"
- }
- }, {
- "GlossEntry": {
- "ID": "SGML",
- "SortAs": "SGML",
- "GlossTerm": "Standard Generalized Markup Language",
- "Acronym": "SGML",
- "Abbrev": "ISO 8879:1986",
- "MoreBytes": [0, 1, 2, 3, 4, 5, 6],
- "GlossDef": {
- "para": "A meta-markup language, used to create markup languages such as DocBook.",
- "GlossSeeAlso": ["GML", "XML", "OGL", "PPL"]
+ },
+ {
+ GlossEntry: {
+ ID: 'SGML',
+ SortAs: 'SGML',
+ GlossTerm: 'Standard Generalized Markup Language',
+ Acronym: 'SGML',
+ Abbrev: 'ISO 8879:1986',
+ MoreBytes: [0, 1, 2, 3, 4, 5, 6],
+ GlossDef: {
+ para: 'A meta-markup language, used to create markup languages such as DocBook.',
+ GlossSeeAlso: ['GML', 'XML', 'OGL', 'PPL'],
+ },
+ GlossSee: 'markup',
},
- "GlossSee": "markup"
- }
- }]
- }
- }
+ },
+ ],
+ },
+ },
};
/**
@@ -90,7 +90,7 @@ const JSON_EXAMPLE: JsonObject = {
* @param {boolean} useGzip
* @returns {number}
*/
-export function bytes(str: string, useGzip: boolean = false) {
+export function bytes(str: string, useGzip: boolean = false): number {
return Buffer.from(str, useGzip ? 'binary' : 'utf8').length;
}
@@ -102,7 +102,7 @@ export function bytes(str: string, useGzip: boolean = false) {
* @param {number} [msgDelay]
* @param {boolean} [useGzip]
* @param {boolean} safeDelivery
- * @param {IJson} jsonExample
+ * @param {AnyJson} jsonExample
* @returns {Promise}
*/
export async function run(
@@ -111,74 +111,76 @@ export async function run(
msgDelay: number = 0,
useGzip: boolean = false,
safeDelivery: boolean = false,
- jsonExample: AnyJson = JSON_EXAMPLE
-) {
+ jsonExample: AnyJson = JSON_EXAMPLE,
+): Promise {
const bytesLen = bytes(
(useGzip ? pack : JSON.stringify)(jsonExample),
- useGzip
+ useGzip,
);
const srcBytesLen = bytes(JSON.stringify(jsonExample));
- return new Promise(async (resolve) => {
- const queueName = `imq-test:${uuid()}`;
- const options: Partial = {
- vendor: 'Redis',
- port,
- useGzip,
- safeDelivery
- };
- const mq = await IMQ.create(queueName, options).start();
+ return new Promise(resolve => {
+ void (async () => {
+ const queueName = `imq-test:${uuid()}`;
+ const options: Partial = {
+ vendor: 'Redis',
+ port,
+ useGzip,
+ safeDelivery,
+ };
+ const mq = await IMQ.create(queueName, options).start();
- let count = 0;
- const fmt = new Intl.NumberFormat(
- 'en-US', { maximumSignificantDigits: 3 }
- );
+ let count = 0;
+ const fmt = new Intl.NumberFormat('en-US', {
+ maximumSignificantDigits: 3,
+ });
- mq.on('message', () => count++);
+ mq.on('message', () => count++);
- if (msgDelay) {
- console.log(
- 'Sending %s messages, using %s delay please, wait...',
- fmt.format(steps),
- fmt.format(msgDelay)
- );
- } else {
- console.log(
- 'Sending %s messages, please, wait...',
- fmt.format(steps)
- );
- }
+ if (msgDelay) {
+ console.info(
+ 'Sending %s messages, using %s delay please, wait...',
+ fmt.format(steps),
+ fmt.format(msgDelay),
+ );
+ } else {
+ console.info(
+ 'Sending %s messages, please, wait...',
+ fmt.format(steps),
+ );
+ }
- const start = Date.now();
+ const start = Date.now();
- for (let i = 0; i < steps; i++) {
- mq.send(queueName, jsonExample as JsonObject, msgDelay).catch();
- }
+ for (let i = 0; i < steps; i++) {
+ mq.send(queueName, jsonExample as JsonObject, msgDelay).catch();
+ }
- const interval = setInterval(async () => {
- if (count >= steps) {
- const time = Date.now() - start;
- const ratio = count / (time / 1000);
+ const interval = setInterval(async () => {
+ if (count >= steps) {
+ const time = Date.now() - start;
+ const ratio = count / (time / 1000);
- console.log(
- '%s is sent/received in %s ±10 ms',
- fmt.format(count),
- fmt.format(time)
- );
- console.log(
- 'Round-trip ratio: %s messages/sec',
- fmt.format(ratio)
- );
- console.log(
- 'Message payload is: %s bytes',
- fmt.format(bytesLen)
- );
+ console.info(
+ '%s is sent/received in %s ±10 ms',
+ fmt.format(count),
+ fmt.format(time),
+ );
+ console.info(
+ 'Round-trip ratio: %s messages/sec',
+ fmt.format(ratio),
+ );
+ console.info(
+ 'Message payload is: %s bytes',
+ fmt.format(bytesLen),
+ );
- mq.destroy().catch();
+ mq.destroy().catch();
- clearInterval(interval);
- resolve({ count, time, ratio, bytesLen, srcBytesLen });
- }
- }, 10);
+ clearInterval(interval);
+ resolve({ count, time, ratio, bytesLen, srcBytesLen });
+ }
+ }, 10);
+ })();
});
-}
\ No newline at end of file
+}
diff --git a/eslint.config.mjs b/eslint.config.mjs
deleted file mode 100644
index a0210a5..0000000
--- a/eslint.config.mjs
+++ /dev/null
@@ -1,98 +0,0 @@
-/*!
- * I'm Queue Software Project
- * Copyright (C) 2025 imqueue.com
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- * If you want to use this code in a closed source (commercial) project, you can
- * purchase a proprietary commercial license. Please contact us at
- * to get commercial licensing options.
- */
-import typescriptEslintEslintPlugin from "@typescript-eslint/eslint-plugin";
-import tsParser from "@typescript-eslint/parser";
-import path from "node:path";
-import { fileURLToPath } from "node:url";
-import js from "@eslint/js";
-import { FlatCompat } from "@eslint/eslintrc";
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
-const compat = new FlatCompat({
- baseDirectory: __dirname,
- recommendedConfig: js.configs.recommended,
- allConfig: js.configs.all
-});
-
-export default [...compat.extends(
- "plugin:@typescript-eslint/recommended",
- "plugin:@typescript-eslint/recommended-requiring-type-checking",
-), {
- plugins: {
- "@typescript-eslint": typescriptEslintEslintPlugin,
- },
-
- languageOptions: {
- parser: tsParser,
- ecmaVersion: 5,
- sourceType: "module",
-
- parserOptions: {
- project: "tsconfig.json",
- },
- },
-
- rules: {
- "@typescript-eslint/interface-name-prefix": "off",
- "@typescript-eslint/explicit-function-return-type": ["warn"],
- "@typescript-eslint/explicit-module-boundary-types": "off",
- "@typescript-eslint/no-unsafe-member-access": "off",
- "@typescript-eslint/no-explicit-any": "off",
- "@typescript-eslint/no-unsafe-assignment": "warn",
- "@typescript-eslint/no-unsafe-argument": "warn",
- "no-trailing-spaces": "warn",
- "no-unused-vars": "off",
- "@typescript-eslint/no-unused-vars": ["warn"],
- "semi": ["warn", "always"],
-
- "no-console": ["warn", {
- allow: ["warn", "error", "info"],
- }],
-
- "no-debugger": "error",
- "arrow-parens": ["warn", "as-needed"],
-
- "quotes": ["error", "single", {
- avoidEscape: true,
- allowTemplateLiterals: true,
- }],
-
- "comma-dangle": ["warn", {
- arrays: "always-multiline",
- objects: "always-multiline",
- imports: "always-multiline",
- exports: "always-multiline",
- functions: "always-multiline",
- }],
-
- "max-len": ["warn", {
- code: 80,
- ignoreComments: true,
- ignoreRegExpLiterals: true,
- ignoreTrailingComments: true,
- ignoreTemplateLiterals: true,
- ignoreStrings: true,
- ignoreUrls: true,
- }],
- },
-}];
diff --git a/index.ts b/index.ts
index a2f8af0..755ab2c 100644
--- a/index.ts
+++ b/index.ts
@@ -21,7 +21,6 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
-import 'reflect-metadata';
import {
IMessageQueue,
IMessageQueueConstructor,
@@ -65,8 +64,9 @@ export default class IMQ {
ClassName = `Clustered${ClassName}`;
}
- const Adapter: IMessageQueueConstructor =
- require(`${__dirname}/src/${ClassName}.js`)[ClassName];
+ const Adapter: IMessageQueueConstructor = require(
+ `${__dirname}/src/${ClassName}.js`,
+ )[ClassName];
return new Adapter(name, options, mode);
}
diff --git a/package-lock.json b/package-lock.json
index c5a9f71..f95cd48 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,884 +1,775 @@
{
"name": "@imqueue/core",
- "version": "2.0.26",
+ "version": "3.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@imqueue/core",
- "version": "2.0.26",
+ "version": "3.0.0",
"license": "GPL-3.0-only",
"dependencies": {
- "ioredis": "^5.7.0"
+ "ioredis": "^5.11.1"
},
"devDependencies": {
- "@eslint/js": "^9.33.0",
- "@types/chai": "^5.2.2",
- "@types/eslint__eslintrc": "^2.1.2",
- "@types/mocha": "^10.0.0",
"@types/mock-require": "^3.0.0",
- "@types/node": "^24.2.1",
- "@types/sinon": "^17.0.4",
- "@types/yargs": "^17.0.33",
- "@typescript-eslint/eslint-plugin": "^8.39.0",
- "@typescript-eslint/parser": "^8.39.0",
- "@typescript-eslint/typescript-estree": "^8.39.0",
- "chai": "^5.2.1",
- "coveralls-next": "^5.0.0",
- "eslint": "^9.33.0",
- "eslint-plugin-jsdoc": "^52.0.4",
- "mocha": "^11.7.1",
- "mocha-lcov-reporter": "^1.3.0",
+ "@types/node": "^24.9.1",
"mock-require": "^3.0.3",
- "nyc": "^17.1.0",
- "open": "^10.2.0",
- "reflect-metadata": "^0.2.2",
- "sinon": "^21.0.0",
- "source-map-support": "^0.5.21",
- "ts-node": "^10.9.2",
- "typedoc": "^0.28.9",
- "typescript": "^5.9.2",
- "yargs": "^18.0.0"
+ "oxfmt": "0.57.0",
+ "oxlint": "1.72.0",
+ "typedoc": "^0.28.20",
+ "typescript": "^6.0.3"
}
},
- "node_modules/@babel/code-frame": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
- "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "node_modules/@gerrit0/mini-shiki": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz",
+ "integrity": "sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.27.1",
- "js-tokens": "^4.0.0",
- "picocolors": "^1.1.1"
- },
- "engines": {
- "node": ">=6.9.0"
+ "@shikijs/engine-oniguruma": "^3.23.0",
+ "@shikijs/langs": "^3.23.0",
+ "@shikijs/themes": "^3.23.0",
+ "@shikijs/types": "^3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
}
},
- "node_modules/@babel/compat-data": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
- "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+ "node_modules/@ioredis/commands": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz",
+ "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==",
+ "license": "MIT"
+ },
+ "node_modules/@oxfmt/binding-android-arm-eabi": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.57.0.tgz",
+ "integrity": "sha512-qVBsEO+KugOsCmUHcO8iqNnqc65p7PCKpCs8M66mPZ+Ri+CWbcpoQOEJBg2OTu03+0qu++NK1jj6IzvQVs0Sig==",
+ "cpu": [
+ "arm"
+ ],
"dev": true,
"license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/core": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
- "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+ "node_modules/@oxfmt/binding-android-arm64": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.57.0.tgz",
+ "integrity": "sha512-mp6PibWbao3aizijcheOeHQaYEhcUAt8pwLniYbtLfHxL/psFF0BykAwCj+s3c6qIpa8yN8keZICWrqtZ70w8g==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.5",
- "@babel/helper-compilation-targets": "^7.27.2",
- "@babel/helper-module-transforms": "^7.28.3",
- "@babel/helpers": "^7.28.4",
- "@babel/parser": "^7.28.5",
- "@babel/template": "^7.27.2",
- "@babel/traverse": "^7.28.5",
- "@babel/types": "^7.28.5",
- "@jridgewell/remapping": "^2.3.5",
- "convert-source-map": "^2.0.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.3",
- "semver": "^6.3.1"
- },
+ "optional": true,
+ "os": [
+ "android"
+ ],
"engines": {
- "node": ">=6.9.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/babel"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/core/node_modules/convert-source-map": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@babel/core/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "node_modules/@oxfmt/binding-darwin-arm64": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.57.0.tgz",
+ "integrity": "sha512-T+0stuCBqmUVY+aMIvrgXhzGhHO3sD5tNiiEcYqgSdPsnukskQqn2u5qOVD0sv1l7RLdFS5Z/f5Wi9Ktyjr3Eg==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/generator": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
- "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+ "node_modules/@oxfmt/binding-darwin-x64": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.57.0.tgz",
+ "integrity": "sha512-O+3JbqWs/mCI2oi4xfhRO2IVPFJNDDEBV8Odo+ZpmsUOeKJfjXoNH7nDmBEQcDgK7NfjDIyE7kRgYSZcTLDO0A==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.28.5",
- "@babel/types": "^7.28.5",
- "@jridgewell/gen-mapping": "^0.3.12",
- "@jridgewell/trace-mapping": "^0.3.28",
- "jsesc": "^3.0.2"
- },
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/helper-compilation-targets": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
- "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "node_modules/@oxfmt/binding-freebsd-x64": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.57.0.tgz",
+ "integrity": "sha512-pxwhxVC+JkLX9twOQ/8C/vbuOQcMZyKIDmiRDZfO7yITuVcIdZCiLRqqf4QOxb2+8FWrRXzQpm+1DBKcMpHSSQ==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@babel/compat-data": "^7.27.2",
- "@babel/helper-validator-option": "^7.27.1",
- "browserslist": "^4.24.0",
- "lru-cache": "^5.1.1",
- "semver": "^6.3.1"
- },
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "node_modules/@oxfmt/binding-linux-arm-gnueabihf": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.57.0.tgz",
+ "integrity": "sha512-pxBU4zH2imB/MDBfth2rOMeVxXUMjRQLCazagwLARIFH3hVlxZJBlM4nSnHXaIHJK4/qezoFCIORN6AY8Mra4A==",
+ "cpu": [
+ "arm"
+ ],
"dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/helper-globals": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
- "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "node_modules/@oxfmt/binding-linux-arm-musleabihf": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.57.0.tgz",
+ "integrity": "sha512-JAprOzt8tycYou36ZgEw14DlRHTiN8qdtKANdV3VZIRIvTI/lh/cX13c9pJ/EnDk2GT3FASH7KvCgQ2AufAifQ==",
+ "cpu": [
+ "arm"
+ ],
"dev": true,
"license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/helper-module-imports": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
- "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "node_modules/@oxfmt/binding-linux-arm64-gnu": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.57.0.tgz",
+ "integrity": "sha512-ajtjaxSaj9xl4BW7REt+Cef/ttzbAq00Bq4z7JUDZEfgFXdwSjH8K9bF+IcIJzZB9lKqMfQ4eHuSFOvvlvtqOg==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
- "dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
- },
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/helper-module-transforms": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
- "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "node_modules/@oxfmt/binding-linux-arm64-musl": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.57.0.tgz",
+ "integrity": "sha512-p4Y/+RYk9Bk5WO+zHSUXAClRmZ2fbJCejMuCAsU2HhyME4jqf6Ftt/mJYEwIah1wGCBDYOB7wEGV1x5bCEZ6hA==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
- "dependencies": {
- "@babel/helper-module-imports": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.28.3"
- },
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/helper-string-parser": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
- "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "node_modules/@oxfmt/binding-linux-ppc64-gnu": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.57.0.tgz",
+ "integrity": "sha512-By6tRALAZsno0F4zedmtG+wdMvJiJmJoXM4d3+A9zHE4HRXLqXITwRH8mgrlcXc5yJM2g2W3riRPwTYdgemZLQ==",
+ "cpu": [
+ "ppc64"
+ ],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/helper-validator-identifier": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
- "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "node_modules/@oxfmt/binding-linux-riscv64-gnu": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.57.0.tgz",
+ "integrity": "sha512-skYeG+RgvyzspqVEBsEprL90OYYZfoVNqB3HcCNR6QDJyXKOzfDRT3zncnHmUaFluIlBHuY23mU1b5WGgR98hA==",
+ "cpu": [
+ "riscv64"
+ ],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/helper-validator-option": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
- "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "node_modules/@oxfmt/binding-linux-riscv64-musl": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.57.0.tgz",
+ "integrity": "sha512-FFgACrZOXAXUh5KQh2mt1CDOVOZmn+QzHP71wM9QobNwyQvoFfyAeefVUltW83g3sm7LTiH3yfFqLLVUpA5ZFQ==",
+ "cpu": [
+ "riscv64"
+ ],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/helpers": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
- "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "node_modules/@oxfmt/binding-linux-s390x-gnu": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.57.0.tgz",
+ "integrity": "sha512-Nm/BAOfQeFiiKd502mZn/GAVKJwtd0RdCg17G3Wz/WSOIQmDi3+7/SZH4BHn1Ye5KvTVH3ua8WvfwLLycNIuvA==",
+ "cpu": [
+ "s390x"
+ ],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
- "dependencies": {
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.4"
- },
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/parser": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
- "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "node_modules/@oxfmt/binding-linux-x64-gnu": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.57.0.tgz",
+ "integrity": "sha512-BiSy5Ku3mQqyxS6YIqAJgd403wEUWvI7kerfzPxc2l/txZVmZM0pSj7oDM+4bGBExowxOi7o73jEam1W0EDTZg==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
- "dependencies": {
- "@babel/types": "^7.28.5"
- },
- "bin": {
- "parser": "bin/babel-parser.js"
- },
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=6.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/template": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
- "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "node_modules/@oxfmt/binding-linux-x64-musl": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.57.0.tgz",
+ "integrity": "sha512-BCRkJiotz5s9afLYD2LuMvzAoDYx9H17E/YbDyu4xK7l4zHDPeny9ErSXL//i/nJyaOwRk08x4b8cgJC00+JDg==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/parser": "^7.27.2",
- "@babel/types": "^7.27.1"
- },
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/traverse": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
- "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+ "node_modules/@oxfmt/binding-openharmony-arm64": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.57.0.tgz",
+ "integrity": "sha512-4Oaxe1qrGgXfpCJ1C/ERJ2iCtV2rN1R79ga9fsfyVHfSQRu/hVW780u2KDqZWFZ/iGTHODJji0JemxqFZ63eIQ==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.5",
- "@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.5",
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.5",
- "debug": "^4.3.1"
- },
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@babel/types": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
- "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "node_modules/@oxfmt/binding-win32-arm64-msvc": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.57.0.tgz",
+ "integrity": "sha512-MYLAsDnhdNsSGheLYhWgbk0vfIrlS84iQYun/y21fX6u0jj8iBtYtbpZMdiqYeuf8U12eVPUjVY2xE2NrCfJ0g==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.28.5"
- },
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">=6.9.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@cspotcode/source-map-support": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
- "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "node_modules/@oxfmt/binding-win32-ia32-msvc": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.57.0.tgz",
+ "integrity": "sha512-PBwdzZALJY/jcCx2E6is0yu+cuVXeySTDmwuseD+9j0mHqlRNxwlKgsyRTBed/woPeqfVfuXfWjoq4Cx2Zt3Eg==",
+ "cpu": [
+ "ia32"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@jridgewell/trace-mapping": "0.3.9"
- },
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">=12"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.9",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
- "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "node_modules/@oxfmt/binding-win32-x64-msvc": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.57.0.tgz",
+ "integrity": "sha512-bQJdH9i4RRfw55jm7+8/xS7GzHLLTbHx4huhrrDxQJaJtbSDbsyOnODvP1ftT7EG0KFKAYO2S+q6AcioXODx8w==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.0.3",
- "@jridgewell/sourcemap-codec": "^1.4.10"
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@es-joy/jsdoccomment": {
- "version": "0.52.0",
- "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.52.0.tgz",
- "integrity": "sha512-BXuN7BII+8AyNtn57euU2Yxo9yA/KUDNzrpXyi3pfqKmBhhysR6ZWOebFh3vyPoqA3/j1SOvGgucElMGwlXing==",
+ "node_modules/@oxlint/binding-android-arm-eabi": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.72.0.tgz",
+ "integrity": "sha512-zhCmvn+1Mj3UchAc/90i99S0t7jJUsHmFVSPg4UWrjO8b8eaSGwscgO6QAUtvHBstkjQwBttQNswEnAF1mIQdA==",
+ "cpu": [
+ "arm"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@types/estree": "^1.0.8",
- "@typescript-eslint/types": "^8.34.1",
- "comment-parser": "1.4.1",
- "esquery": "^1.6.0",
- "jsdoc-type-pratt-parser": "~4.1.0"
- },
+ "optional": true,
+ "os": [
+ "android"
+ ],
"engines": {
- "node": ">=20.11.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint-community/eslint-utils": {
- "version": "4.9.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
- "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "node_modules/@oxlint/binding-android-arm64": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.72.0.tgz",
+ "integrity": "sha512-mtH+aY/ozv1eZoCUC2owjFAtyNBKHpJHygKeEu9zXXnQGW1Q2/qOpvx+I+Lf23+TvTz66F4iiXUbl2cGvoLPCQ==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "eslint-visitor-keys": "^3.4.3"
- },
+ "optional": true,
+ "os": [
+ "android"
+ ],
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- },
- "peerDependencies": {
- "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint-community/regexpp": {
- "version": "4.12.2",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
- "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "node_modules/@oxlint/binding-darwin-arm64": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.72.0.tgz",
+ "integrity": "sha512-EvnajNPDtfknB3ZieeOOyDTwJn9QXDiwfnF4ZDQqART6RG6hjY4WigQcZdGoK2dkB3e1vrmEzN9aYbQCUkh/gQ==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/config-array": {
- "version": "0.21.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
- "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "node_modules/@oxlint/binding-darwin-x64": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.72.0.tgz",
+ "integrity": "sha512-ZkCdEa/G80A7vEHfeCDz/+L3m33DE73v32mDKhgOIgz8Uwf0DFcK7+uu6qC+7LEhmz5fpOe1osWKyjSNMydFIQ==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/object-schema": "^2.1.7",
- "debug": "^4.3.1",
- "minimatch": "^3.1.2"
- },
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/config-array/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "node_modules/@oxlint/binding-freebsd-x64": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.72.0.tgz",
+ "integrity": "sha512-NroXv2vh+sxVY1uya/rM5pjhx1hm8BzlYpx9q67QP0Xhw5MH2bf5GJylpvLEC+781p1Xli/317EoV9AlGwViag==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/config-array/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "node_modules/@oxlint/binding-linux-arm-gnueabihf": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.72.0.tgz",
+ "integrity": "sha512-0NDywYgfj279Ou/BcQuCYSj7NJwBfmWn5qc5uGO/Ny7fUWmXyIpvawqX/8acQlWG6IXelJsJhj+JAy6sjsKj0A==",
+ "cpu": [
+ "arm"
+ ],
"dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": "*"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/config-helpers": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz",
- "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==",
+ "node_modules/@oxlint/binding-linux-arm-musleabihf": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.72.0.tgz",
+ "integrity": "sha512-4vpXB06h65Ezsy4hRyrGjGrfa1SkVPii09yaajiYhmVpgsFiLD+KNxIx/BNAY+XiO+i1yqp9HHdwqM8VTqa5XQ==",
+ "cpu": [
+ "arm"
+ ],
"dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/core": "^0.16.0"
- },
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/core": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
- "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
+ "node_modules/@oxlint/binding-linux-arm64-gnu": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.72.0.tgz",
+ "integrity": "sha512-immaN4g2ZGFiOkKrvRX9LvzZdd2GkQM5wR+UyzYyUuyhUTXGQ4HKUJH18xp4G8OfhCVaVAJfKZxwE1r8+4hhaQ==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@types/json-schema": "^7.0.15"
- },
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/eslintrc": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
- "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "node_modules/@oxlint/binding-linux-arm64-musl": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.72.0.tgz",
+ "integrity": "sha512-JGHS9Mnr7iWyyLDxgCv1MhzVpAckgptg00F2gnxt/GD7lQ2SW1BRcxHqhSTaSdDpjWRrBkBxMMh4+Hn3aVtExg==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
- "dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
- },
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "node_modules/@oxlint/binding-linux-ppc64-gnu": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.72.0.tgz",
+ "integrity": "sha512-AOYgBZqxNshrg83P9v0RYv+m8s10Cqkj4/PxXFDhcS3k7FqsIG5+CxErshZCIN7G8iy4Y+VGfAsuEdar8AcbBg==",
+ "cpu": [
+ "ppc64"
+ ],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/eslintrc/node_modules/ignore": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
- "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "node_modules/@oxlint/binding-linux-riscv64-gnu": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.72.0.tgz",
+ "integrity": "sha512-QMybPS5ij3/vrKG67mqzHwW++91sYxK/PPUVi6SBtNCEzW4niS52fVBdXbQ6nou0wWbUPEpx8Sl/ZjtgE3clXA==",
+ "cpu": [
+ "riscv64"
+ ],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">= 4"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/eslintrc/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "node_modules/@oxlint/binding-linux-riscv64-musl": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.72.0.tgz",
+ "integrity": "sha512-gOc3W7JV0PXRpIL7stUlLe3Wa9Gp0Kdlup87IT3gHDvPKck2xNgMIl/Gs2lldYY2lyXZDC4rWi3hmoLUobkgbQ==",
+ "cpu": [
+ "riscv64"
+ ],
"dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": "*"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/js": {
- "version": "9.38.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz",
- "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==",
+ "node_modules/@oxlint/binding-linux-s390x-gnu": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.72.0.tgz",
+ "integrity": "sha512-rpGxph+FjjHcYI5q6uxB3Az+tnfmEnDbSA8+PK9ZE/VzyUAkvBOMeuY7ZQMhu5mpZH7YQDsTdW6Cx4kV/msc6w==",
+ "cpu": [
+ "s390x"
+ ],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/object-schema": {
- "version": "2.1.7",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
- "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "node_modules/@oxlint/binding-linux-x64-gnu": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.72.0.tgz",
+ "integrity": "sha512-WND+uhf/Ko13SLqQMWQUgsZuLvYYEvL0ZKgg0tgGYfLqxG7l8Ju123fHDMJyYSDl5E3bUbpFUuii/OvMreFQzw==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "license": "Apache-2.0",
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@eslint/plugin-kit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
- "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
+ "node_modules/@oxlint/binding-linux-x64-musl": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.72.0.tgz",
+ "integrity": "sha512-SrpbrUL70nG9vh6zP4/oKHWgLuHquwsr7MW9XOn0olBVgh10Uqr8qscKhQoBGEn6olK/IUpn5GSKcdQ5AjUhGA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/core": "^0.16.0",
- "levn": "^0.4.1"
- },
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@gerrit0/mini-shiki": {
- "version": "3.14.0",
- "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.14.0.tgz",
- "integrity": "sha512-c5X8fwPLOtUS8TVdqhynz9iV0GlOtFUT1ppXYzUUlEXe4kbZ/mvMT8wXoT8kCwUka+zsiloq7sD3pZ3+QVTuNQ==",
+ "node_modules/@oxlint/binding-openharmony-arm64": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.72.0.tgz",
+ "integrity": "sha512-qkrsEn6NmgFKr7U/QnezQMb+q/vzAy0Dd9Y95gQGQTyjzDLN+HRZMuM5u70iyH4nBLCfKBzhjMsYCehKay2jyg==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@shikijs/engine-oniguruma": "^3.14.0",
- "@shikijs/langs": "^3.14.0",
- "@shikijs/themes": "^3.14.0",
- "@shikijs/types": "^3.14.0",
- "@shikijs/vscode-textmate": "^10.0.2"
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@humanfs/core": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
- "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "node_modules/@oxlint/binding-win32-arm64-msvc": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.72.0.tgz",
+ "integrity": "sha512-LWR6ZlFZph+KPjXv8opgZsXRDCdrdQe8VL8Cg9zxCoBS73h6znzZpydVgmdnwj8mB9AuSM5jxEgDJDpQkjboeg==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "license": "Apache-2.0",
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">=18.18.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@humanfs/node": {
- "version": "0.16.7",
- "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
- "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "node_modules/@oxlint/binding-win32-ia32-msvc": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.72.0.tgz",
+ "integrity": "sha512-yt6HEh7IsHvtjRWtmeZRX134eaXKHq5Gnqlf1xBJdJl1JtdoRUEJw3nAxpZoUDS860cX/foKbztO441anVBtVQ==",
+ "cpu": [
+ "ia32"
+ ],
"dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@humanfs/core": "^0.19.1",
- "@humanwhocodes/retry": "^0.4.0"
- },
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">=18.18.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@humanwhocodes/module-importer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
- "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "node_modules/@oxlint/binding-win32-x64-msvc": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.72.0.tgz",
+ "integrity": "sha512-b2eKFD2hX7tIwmo/cyH6TDq8vzWRZ2qNHrzoGntUTmq0h3zQh/uX3eTSHCwI8OB/ADQfJCRelLItK8BsxuucDA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "license": "Apache-2.0",
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">=12.22"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@humanwhocodes/retry": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
- "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "node_modules/@shikijs/engine-oniguruma": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz",
+ "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==",
"dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
}
},
- "node_modules/@ioredis/commands": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz",
- "integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==",
- "license": "MIT"
- },
- "node_modules/@isaacs/cliui": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
- "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "node_modules/@shikijs/langs": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz",
+ "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
"dependencies": {
- "string-width": "^5.1.2",
- "string-width-cjs": "npm:string-width@^4.2.0",
- "strip-ansi": "^7.0.1",
- "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
- "wrap-ansi": "^8.1.0",
- "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
- "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "camelcase": "^5.3.1",
- "find-up": "^4.1.0",
- "get-package-type": "^0.1.0",
- "js-yaml": "^3.13.1",
- "resolve-from": "^5.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "sprintf-js": "~1.0.2"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-locate": "^4.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-try": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-limit": "^2.2.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/schema": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.13",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
- "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.0",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "node_modules/@jridgewell/remapping": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
- "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.31",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
- "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@pkgjs/parseargs": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
- "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/@shikijs/engine-oniguruma": {
- "version": "3.14.0",
- "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.14.0.tgz",
- "integrity": "sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/types": "3.14.0",
- "@shikijs/vscode-textmate": "^10.0.2"
- }
- },
- "node_modules/@shikijs/langs": {
- "version": "3.14.0",
- "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.14.0.tgz",
- "integrity": "sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/types": "3.14.0"
+ "@shikijs/types": "3.23.0"
}
},
"node_modules/@shikijs/themes": {
- "version": "3.14.0",
- "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.14.0.tgz",
- "integrity": "sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA==",
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz",
+ "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@shikijs/types": "3.14.0"
+ "@shikijs/types": "3.23.0"
}
},
"node_modules/@shikijs/types": {
- "version": "3.14.0",
- "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.14.0.tgz",
- "integrity": "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ==",
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz",
+ "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -893,121 +784,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@sinonjs/commons": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
- "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "type-detect": "4.0.8"
- }
- },
- "node_modules/@sinonjs/fake-timers": {
- "version": "13.0.5",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz",
- "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@sinonjs/commons": "^3.0.1"
- }
- },
- "node_modules/@sinonjs/samsam": {
- "version": "8.0.3",
- "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz",
- "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@sinonjs/commons": "^3.0.1",
- "type-detect": "^4.1.0"
- }
- },
- "node_modules/@sinonjs/samsam/node_modules/type-detect": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz",
- "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@tsconfig/node10": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
- "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@tsconfig/node12": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
- "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@tsconfig/node14": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
- "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@tsconfig/node16": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
- "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/chai": {
- "version": "5.2.3",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
- "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/deep-eql": "*",
- "assertion-error": "^2.0.1"
- }
- },
- "node_modules/@types/deep-eql": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
- "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/eslint": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
- "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
- },
- "node_modules/@types/eslint__eslintrc": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@types/eslint__eslintrc/-/eslint__eslintrc-2.1.2.tgz",
- "integrity": "sha512-qXvzPFY7Rz05xD8ZApXJ3S8xStQD2Ibzu3EFIF0UMNOAfLY5xUu3H61q0JrHo2OXD6rcFG75yUxNQbkKtFKBSw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/eslint": "*"
- }
- },
- "node_modules/@types/estree": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
- "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/hast": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
@@ -1018,20 +794,6 @@
"@types/unist": "*"
}
},
- "node_modules/@types/json-schema": {
- "version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/mocha": {
- "version": "10.0.10",
- "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz",
- "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/mock-require": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/mock-require/-/mock-require-3.0.0.tgz",
@@ -1040,32 +802,15 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "24.9.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
- "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "undici-types": "~7.16.0"
- }
- },
- "node_modules/@types/sinon": {
- "version": "17.0.4",
- "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz",
- "integrity": "sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==",
+ "version": "24.13.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz",
+ "integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@types/sinonjs__fake-timers": "*"
+ "undici-types": "~7.18.0"
}
},
- "node_modules/@types/sinonjs__fake-timers": {
- "version": "15.0.1",
- "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-15.0.1.tgz",
- "integrity": "sha512-Ko2tjWJq8oozHzHV+reuvS5KYIRAokHnGbDwGh/J64LntgpbuylF74ipEL24HCyRjf9FOlBiBHWBR1RlVKsI1w==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/unist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
@@ -1073,4393 +818,445 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/yargs": {
- "version": "17.0.34",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz",
- "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@types/yargs-parser": {
- "version": "21.0.3",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
- "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.46.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz",
- "integrity": "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.46.2",
- "@typescript-eslint/type-utils": "8.46.2",
- "@typescript-eslint/utils": "8.46.2",
- "@typescript-eslint/visitor-keys": "8.46.2",
- "graphemer": "^1.4.0",
- "ignore": "^7.0.0",
- "natural-compare": "^1.4.0",
- "ts-api-utils": "^2.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "@typescript-eslint/parser": "^8.46.2",
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/parser": {
- "version": "8.46.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.2.tgz",
- "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/scope-manager": "8.46.2",
- "@typescript-eslint/types": "8.46.2",
- "@typescript-eslint/typescript-estree": "8.46.2",
- "@typescript-eslint/visitor-keys": "8.46.2",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/project-service": {
- "version": "8.46.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.2.tgz",
- "integrity": "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.46.2",
- "@typescript-eslint/types": "^8.46.2",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "8.46.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.2.tgz",
- "integrity": "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.46.2",
- "@typescript-eslint/visitor-keys": "8.46.2"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.46.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz",
- "integrity": "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/type-utils": {
- "version": "8.46.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.2.tgz",
- "integrity": "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.46.2",
- "@typescript-eslint/typescript-estree": "8.46.2",
- "@typescript-eslint/utils": "8.46.2",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/types": {
- "version": "8.46.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.2.tgz",
- "integrity": "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.46.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.2.tgz",
- "integrity": "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/project-service": "8.46.2",
- "@typescript-eslint/tsconfig-utils": "8.46.2",
- "@typescript-eslint/types": "8.46.2",
- "@typescript-eslint/visitor-keys": "8.46.2",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/utils": {
- "version": "8.46.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.2.tgz",
- "integrity": "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.46.2",
- "@typescript-eslint/types": "8.46.2",
- "@typescript-eslint/typescript-estree": "8.46.2"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.46.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.2.tgz",
- "integrity": "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.46.2",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
- "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-jsx": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
- }
- },
- "node_modules/acorn-walk": {
- "version": "8.3.4",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
- "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "acorn": "^8.11.0"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/aggregate-error": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
- "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "clean-stack": "^2.0.0",
- "indent-string": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/ansi-regex": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
- "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/append-transform": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz",
- "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "default-require-extensions": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/archy": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
- "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/are-docs-informative": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz",
- "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/arg": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
- "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/argparse": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true,
- "license": "Python-2.0"
- },
- "node_modules/assertion-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/baseline-browser-mapping": {
- "version": "2.8.20",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz",
- "integrity": "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==",
- "dev": true,
- "license": "Apache-2.0",
- "bin": {
- "baseline-browser-mapping": "dist/cli.js"
- }
- },
- "node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fill-range": "^7.1.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/browser-stdout": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
- "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/browserslist": {
- "version": "4.27.0",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz",
- "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "baseline-browser-mapping": "^2.8.19",
- "caniuse-lite": "^1.0.30001751",
- "electron-to-chromium": "^1.5.238",
- "node-releases": "^2.0.26",
- "update-browserslist-db": "^1.1.4"
- },
- "bin": {
- "browserslist": "cli.js"
- },
- "engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
- }
- },
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/bundle-name": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
- "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "run-applescript": "^7.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/caching-transform": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
- "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "hasha": "^5.0.0",
- "make-dir": "^3.0.0",
- "package-hash": "^4.0.0",
- "write-file-atomic": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/caniuse-lite": {
- "version": "1.0.30001751",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz",
- "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "CC-BY-4.0"
- },
- "node_modules/chai": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
- "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assertion-error": "^2.0.1",
- "check-error": "^2.1.1",
- "deep-eql": "^5.0.1",
- "loupe": "^3.1.0",
- "pathval": "^2.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/check-error": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
- "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 16"
- }
- },
- "node_modules/chokidar": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "readdirp": "^4.0.1"
- },
- "engines": {
- "node": ">= 14.16.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/clean-stack": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
- "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/cliui": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
- "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^7.2.0",
- "strip-ansi": "^7.1.0",
- "wrap-ansi": "^9.0.0"
- },
- "engines": {
- "node": ">=20"
- }
- },
- "node_modules/cliui/node_modules/ansi-styles": {
- "version": "6.2.3",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
- "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/cliui/node_modules/emoji-regex": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
- "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/cliui/node_modules/string-width": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
- "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cliui/node_modules/wrap-ansi": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
- "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.2.1",
- "string-width": "^7.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/cluster-key-slot": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
- "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/comment-parser": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz",
- "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 12.0.0"
- }
- },
- "node_modules/commondir": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
- "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/coveralls-next": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/coveralls-next/-/coveralls-next-5.0.0.tgz",
- "integrity": "sha512-RCj6Oflf6iQtN3Q5b0SSemEbQBzeBjQlLUrc3bfNECTy83hMJA9krdNZ5GTRm7Jpbyo92yKUbQDP5FYlWcL5sA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "js-yaml": "4.1.0",
- "lcov-parse": "1.0.0",
- "log-driver": "1.2.7",
- "minimist": "1.2.8"
- },
- "bin": {
- "coveralls": "bin/coveralls.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/create-require": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
- "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/deep-eql": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
- "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/deep-is": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/default-browser": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz",
- "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "bundle-name": "^4.1.0",
- "default-browser-id": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/default-browser-id": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz",
- "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/default-require-extensions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz",
- "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "strip-bom": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/define-lazy-prop": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
- "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/denque": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
- "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/diff": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
- "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.3.1"
- }
- },
- "node_modules/eastasianwidth": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/electron-to-chromium": {
- "version": "1.5.241",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.241.tgz",
- "integrity": "sha512-ILMvKX/ZV5WIJzzdtuHg8xquk2y0BOGlFOxBVwTpbiXqWIH0hamG45ddU4R3PQ0gYu+xgo0vdHXHli9sHIGb4w==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/es6-error": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
- "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint": {
- "version": "9.38.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz",
- "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.8.0",
- "@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.21.1",
- "@eslint/config-helpers": "^0.4.1",
- "@eslint/core": "^0.16.0",
- "@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.38.0",
- "@eslint/plugin-kit": "^0.4.0",
- "@humanfs/node": "^0.16.6",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.4.2",
- "@types/estree": "^1.0.6",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.6",
- "debug": "^4.3.2",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.4.0",
- "eslint-visitor-keys": "^4.2.1",
- "espree": "^10.4.0",
- "esquery": "^1.5.0",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^8.0.0",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "ignore": "^5.2.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.3"
- },
- "bin": {
- "eslint": "bin/eslint.js"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- },
- "peerDependencies": {
- "jiti": "*"
- },
- "peerDependenciesMeta": {
- "jiti": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-plugin-jsdoc": {
- "version": "52.0.4",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-52.0.4.tgz",
- "integrity": "sha512-be5OzGlLExvcK13Il3noU7/v7WmAQGenTmCaBKf1pwVtPOb6X+PGFVnJad0QhMj4KKf45XjE4hbsBxv25q1fTg==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@es-joy/jsdoccomment": "~0.52.0",
- "are-docs-informative": "^0.0.2",
- "comment-parser": "1.4.1",
- "debug": "^4.4.1",
- "escape-string-regexp": "^4.0.0",
- "espree": "^10.4.0",
- "esquery": "^1.6.0",
- "parse-imports-exports": "^0.2.4",
- "semver": "^7.7.2",
- "spdx-expression-parse": "^4.0.0"
- },
- "engines": {
- "node": ">=20.11.0"
- },
- "peerDependencies": {
- "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"
- }
- },
- "node_modules/eslint-scope": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
- "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/eslint/node_modules/eslint-visitor-keys": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
- "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint/node_modules/ignore": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
- "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/eslint/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/espree": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
- "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "acorn": "^8.15.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/espree/node_modules/eslint-visitor-keys": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
- "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
- "dev": true,
- "license": "BSD-2-Clause",
- "bin": {
- "esparse": "bin/esparse.js",
- "esvalidate": "bin/esvalidate.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "estraverse": "^5.1.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/esrecurse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.8"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/fast-glob/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/fast-json-stable-stringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fast-levenshtein": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fastq": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
- "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "reusify": "^1.0.4"
- }
- },
- "node_modules/file-entry-cache": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
- "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "flat-cache": "^4.0.0"
- },
- "engines": {
- "node": ">=16.0.0"
- }
- },
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/find-cache-dir": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
- "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "commondir": "^1.0.1",
- "make-dir": "^3.0.2",
- "pkg-dir": "^4.1.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
- }
- },
- "node_modules/find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/flat": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
- "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "bin": {
- "flat": "cli.js"
- }
- },
- "node_modules/flat-cache": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
- "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.4"
- },
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/flatted": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
- "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/foreground-child": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
- "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "cross-spawn": "^7.0.6",
- "signal-exit": "^4.0.1"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/fromentries": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
- "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/get-caller-file": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/get-east-asian-width": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz",
- "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/get-package-type": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
- "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^3.1.2",
- "minimatch": "^9.0.4",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^1.11.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.3"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/hasha": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
- "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-stream": "^2.0.0",
- "type-fest": "^0.8.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "he": "bin/he"
- }
- },
- "node_modules/html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/ignore": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
- "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/import-fresh": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
- "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.19"
- }
- },
- "node_modules/indent-string": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
- "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/ioredis": {
- "version": "5.8.2",
- "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.2.tgz",
- "integrity": "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==",
- "license": "MIT",
- "dependencies": {
- "@ioredis/commands": "1.4.0",
- "cluster-key-slot": "^1.1.0",
- "debug": "^4.3.4",
- "denque": "^2.1.0",
- "lodash.defaults": "^4.2.0",
- "lodash.isarguments": "^3.1.0",
- "redis-errors": "^1.2.0",
- "redis-parser": "^3.0.0",
- "standard-as-callback": "^2.1.0"
- },
- "engines": {
- "node": ">=12.22.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/ioredis"
- }
- },
- "node_modules/is-docker": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
- "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "is-docker": "cli.js"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-inside-container": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
- "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-docker": "^3.0.0"
- },
- "bin": {
- "is-inside-container": "cli.js"
- },
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/is-path-inside": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
- "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-plain-obj": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-typedarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/is-unicode-supported": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
- "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-windows": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
- "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-wsl": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz",
- "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-inside-container": "^1.0.0"
- },
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/istanbul-lib-coverage": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
- "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/istanbul-lib-hook": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz",
- "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "append-transform": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/istanbul-lib-instrument": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
- "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@babel/core": "^7.23.9",
- "@babel/parser": "^7.23.9",
- "@istanbuljs/schema": "^0.1.3",
- "istanbul-lib-coverage": "^3.2.0",
- "semver": "^7.5.4"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-processinfo": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz",
- "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "archy": "^1.0.0",
- "cross-spawn": "^7.0.3",
- "istanbul-lib-coverage": "^3.2.0",
- "p-map": "^3.0.0",
- "rimraf": "^3.0.0",
- "uuid": "^8.3.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/istanbul-lib-report": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
- "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^4.0.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-report/node_modules/make-dir": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
- "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.5.3"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/istanbul-lib-source-maps": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
- "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0",
- "source-map": "^0.6.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-reports": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
- "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/jackspeak": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
- "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/cliui": "^8.0.2"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- },
- "optionalDependencies": {
- "@pkgjs/parseargs": "^0.11.0"
- }
- },
- "node_modules/js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/jsdoc-type-pratt-parser": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz",
- "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/jsesc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
- "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/json-buffer": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
- "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/json-stable-stringify-without-jsonify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "json5": "lib/cli.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/keyv": {
- "version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "json-buffer": "3.0.1"
- }
- },
- "node_modules/lcov-parse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz",
- "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "bin": {
- "lcov-parse": "bin/cli.js"
- }
- },
- "node_modules/levn": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/linkify-it": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
- "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "uc.micro": "^2.0.0"
- }
- },
- "node_modules/locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-locate": "^5.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/lodash.defaults": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
- "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
- "license": "MIT"
- },
- "node_modules/lodash.flattendeep": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
- "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lodash.isarguments": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
- "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
- "license": "MIT"
- },
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/log-driver": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz",
- "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=0.8.6"
- }
- },
- "node_modules/log-symbols": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
- "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chalk": "^4.1.0",
- "is-unicode-supported": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/loupe": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
- "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^3.0.2"
- }
- },
- "node_modules/lunr": {
- "version": "2.3.9",
- "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
- "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/make-dir/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/make-error": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
- "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/markdown-it": {
- "version": "14.1.0",
- "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
- "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "argparse": "^2.0.1",
- "entities": "^4.4.0",
- "linkify-it": "^5.0.0",
- "mdurl": "^2.0.0",
- "punycode.js": "^2.3.1",
- "uc.micro": "^2.1.0"
- },
- "bin": {
- "markdown-it": "bin/markdown-it.mjs"
- }
- },
- "node_modules/mdurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
- "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/micromatch": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
- "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "braces": "^3.0.3",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/mocha": {
- "version": "11.7.4",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.4.tgz",
- "integrity": "sha512-1jYAaY8x0kAZ0XszLWu14pzsf4KV740Gld4HXkhNTXwcHx4AUEDkPzgEHg9CM5dVcW+zv036tjpsEbLraPJj4w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "browser-stdout": "^1.3.1",
- "chokidar": "^4.0.1",
- "debug": "^4.3.5",
- "diff": "^7.0.0",
- "escape-string-regexp": "^4.0.0",
- "find-up": "^5.0.0",
- "glob": "^10.4.5",
- "he": "^1.2.0",
- "is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
- "log-symbols": "^4.1.0",
- "minimatch": "^9.0.5",
- "ms": "^2.1.3",
- "picocolors": "^1.1.1",
- "serialize-javascript": "^6.0.2",
- "strip-json-comments": "^3.1.1",
- "supports-color": "^8.1.1",
- "workerpool": "^9.2.0",
- "yargs": "^17.7.2",
- "yargs-parser": "^21.1.1",
- "yargs-unparser": "^2.0.0"
- },
- "bin": {
- "_mocha": "bin/_mocha",
- "mocha": "bin/mocha.js"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/mocha-lcov-reporter": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/mocha-lcov-reporter/-/mocha-lcov-reporter-1.3.0.tgz",
- "integrity": "sha512-/5zI2tW4lq/ft8MGpYQ1nIH6yePPtIzdGeUEwFMKfMRdLfAQ1QW2c68eEJop32tNdN5srHa/E2TzB+erm3YMYA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">= 0.6.0"
- }
- },
- "node_modules/mocha/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/mocha/node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/mocha/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/mocha/node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
- "node_modules/mocha/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/mocha/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/mocha/node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
- }
- },
- "node_modules/mocha/node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/mocha/node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/mock-require": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz",
- "integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-caller-file": "^1.0.2",
- "normalize-path": "^2.1.1"
- },
- "engines": {
- "node": ">=4.3.0"
- }
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "license": "MIT"
- },
- "node_modules/natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/node-preload": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
- "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "process-on-spawn": "^1.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/node-releases": {
- "version": "2.0.26",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz",
- "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/normalize-path": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "remove-trailing-separator": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/nyc": {
- "version": "17.1.0",
- "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz",
- "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@istanbuljs/load-nyc-config": "^1.0.0",
- "@istanbuljs/schema": "^0.1.2",
- "caching-transform": "^4.0.0",
- "convert-source-map": "^1.7.0",
- "decamelize": "^1.2.0",
- "find-cache-dir": "^3.2.0",
- "find-up": "^4.1.0",
- "foreground-child": "^3.3.0",
- "get-package-type": "^0.1.0",
- "glob": "^7.1.6",
- "istanbul-lib-coverage": "^3.0.0",
- "istanbul-lib-hook": "^3.0.0",
- "istanbul-lib-instrument": "^6.0.2",
- "istanbul-lib-processinfo": "^2.0.2",
- "istanbul-lib-report": "^3.0.0",
- "istanbul-lib-source-maps": "^4.0.0",
- "istanbul-reports": "^3.0.2",
- "make-dir": "^3.0.0",
- "node-preload": "^0.2.1",
- "p-map": "^3.0.0",
- "process-on-spawn": "^1.0.0",
- "resolve-from": "^5.0.0",
- "rimraf": "^3.0.0",
- "signal-exit": "^3.0.2",
- "spawn-wrap": "^2.0.0",
- "test-exclude": "^6.0.0",
- "yargs": "^15.0.2"
- },
- "bin": {
- "nyc": "bin/nyc.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/nyc/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/nyc/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/nyc/node_modules/cliui": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^6.2.0"
- }
- },
- "node_modules/nyc/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/nyc/node_modules/find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/nyc/node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
- "node_modules/nyc/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/nyc/node_modules/locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-locate": "^4.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/nyc/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/nyc/node_modules/p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-try": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/nyc/node_modules/p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-limit": "^2.2.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/nyc/node_modules/resolve-from": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/nyc/node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/nyc/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/nyc/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/nyc/node_modules/wrap-ansi": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/nyc/node_modules/y18n": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/nyc/node_modules/yargs": {
- "version": "15.4.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cliui": "^6.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^4.1.0",
- "get-caller-file": "^2.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^4.2.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^18.1.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/nyc/node_modules/yargs-parser": {
- "version": "18.1.3",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/open": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
- "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "default-browser": "^5.2.1",
- "define-lazy-prop": "^3.0.0",
- "is-inside-container": "^1.0.0",
- "wsl-utils": "^0.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/optionator": {
- "version": "0.9.4",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
- "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "deep-is": "^0.1.3",
- "fast-levenshtein": "^2.0.6",
- "levn": "^0.4.1",
- "prelude-ls": "^1.2.1",
- "type-check": "^0.4.0",
- "word-wrap": "^1.2.5"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-locate": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-limit": "^3.0.2"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-map": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
- "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "aggregate-error": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/package-hash": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz",
- "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "graceful-fs": "^4.1.15",
- "hasha": "^5.0.0",
- "lodash.flattendeep": "^4.4.0",
- "release-zalgo": "^1.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/package-json-from-dist": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
- "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
- "dev": true,
- "license": "BlueOak-1.0.0"
- },
- "node_modules/parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "callsites": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/parse-imports-exports": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz",
- "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "parse-statements": "1.0.11"
- }
- },
- "node_modules/parse-statements": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz",
- "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-scurry": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
- "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "lru-cache": "^10.2.0",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
- },
- "engines": {
- "node": ">=16 || 14 >=14.18"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/path-scurry/node_modules/lru-cache": {
- "version": "10.4.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
- "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/pathval": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
- "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14.16"
- }
- },
- "node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "find-up": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/pkg-dir/node_modules/find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/pkg-dir/node_modules/locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-locate": "^4.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/pkg-dir/node_modules/p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-try": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/pkg-dir/node_modules/p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-limit": "^2.2.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/prelude-ls": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
- "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/process-on-spawn": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz",
- "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fromentries": "^1.2.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/punycode": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/punycode.js": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
- "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "^5.1.0"
- }
- },
- "node_modules/readdirp": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
- "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14.18.0"
- },
- "funding": {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/redis-errors": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
- "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/redis-parser": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
- "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
- "license": "MIT",
- "dependencies": {
- "redis-errors": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/reflect-metadata": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
- "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
- "dev": true,
- "license": "Apache-2.0"
- },
- "node_modules/release-zalgo": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
- "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "es6-error": "^4.0.1"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/remove-trailing-separator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
- "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/require-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/reusify": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
- "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
- "node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/rimraf/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/rimraf/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/rimraf/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/run-applescript": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
- "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/semver": {
- "version": "7.7.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
- "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/serialize-javascript": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
- "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "randombytes": "^2.1.0"
- }
- },
- "node_modules/set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/signal-exit": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
- "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/sinon": {
- "version": "21.0.0",
- "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz",
- "integrity": "sha512-TOgRcwFPbfGtpqvZw+hyqJDvqfapr1qUlOizROIk4bBLjlsjlB00Pg6wMFXNtJRpu+eCZuVOaLatG7M8105kAw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@sinonjs/commons": "^3.0.1",
- "@sinonjs/fake-timers": "^13.0.5",
- "@sinonjs/samsam": "^8.0.1",
- "diff": "^7.0.0",
- "supports-color": "^7.2.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/sinon"
- }
- },
- "node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
- "node_modules/spawn-wrap": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz",
- "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "foreground-child": "^2.0.0",
- "is-windows": "^1.0.2",
- "make-dir": "^3.0.0",
- "rimraf": "^3.0.0",
- "signal-exit": "^3.0.2",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/spawn-wrap/node_modules/foreground-child": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
- "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "cross-spawn": "^7.0.0",
- "signal-exit": "^3.0.2"
- },
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/spawn-wrap/node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/spdx-exceptions": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
- "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
- "dev": true,
- "license": "CC-BY-3.0"
- },
- "node_modules/spdx-expression-parse": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz",
- "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-license-ids": {
- "version": "3.0.22",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz",
- "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==",
- "dev": true,
- "license": "CC0-1.0"
- },
- "node_modules/sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
- "dev": true,
- "license": "BSD-3-Clause"
- },
- "node_modules/standard-as-callback": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
- "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
- "license": "MIT"
- },
- "node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/string-width-cjs": {
- "name": "string-width",
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width-cjs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/string-width-cjs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
- "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
- "node_modules/strip-ansi-cjs": {
- "name": "strip-ansi",
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-bom": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
- "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/test-exclude": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
- "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^7.1.4",
- "minimatch": "^3.0.4"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/test-exclude/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/test-exclude/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/test-exclude/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/ts-api-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
- "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18.12"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4"
- }
- },
- "node_modules/ts-node": {
- "version": "10.9.2",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
- "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@cspotcode/source-map-support": "^0.8.0",
- "@tsconfig/node10": "^1.0.7",
- "@tsconfig/node12": "^1.0.7",
- "@tsconfig/node14": "^1.0.0",
- "@tsconfig/node16": "^1.0.2",
- "acorn": "^8.4.1",
- "acorn-walk": "^8.1.1",
- "arg": "^4.1.0",
- "create-require": "^1.1.0",
- "diff": "^4.0.1",
- "make-error": "^1.1.1",
- "v8-compile-cache-lib": "^3.0.1",
- "yn": "3.1.1"
- },
- "bin": {
- "ts-node": "dist/bin.js",
- "ts-node-cwd": "dist/bin-cwd.js",
- "ts-node-esm": "dist/bin-esm.js",
- "ts-node-script": "dist/bin-script.js",
- "ts-node-transpile-only": "dist/bin-transpile.js",
- "ts-script": "dist/bin-script-deprecated.js"
- },
- "peerDependencies": {
- "@swc/core": ">=1.2.50",
- "@swc/wasm": ">=1.2.50",
- "@types/node": "*",
- "typescript": ">=2.7"
- },
- "peerDependenciesMeta": {
- "@swc/core": {
- "optional": true
- },
- "@swc/wasm": {
- "optional": true
- }
- }
- },
- "node_modules/ts-node/node_modules/diff": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
- "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.3.1"
- }
- },
- "node_modules/type-check": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
- "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "prelude-ls": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/typedarray-to-buffer": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
- "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-typedarray": "^1.0.0"
- }
- },
- "node_modules/typedoc": {
- "version": "0.28.14",
- "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.14.tgz",
- "integrity": "sha512-ftJYPvpVfQvFzpkoSfHLkJybdA/geDJ8BGQt/ZnkkhnBYoYW6lBgPQXu6vqLxO4X75dA55hX8Af847H5KXlEFA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@gerrit0/mini-shiki": "^3.12.0",
- "lunr": "^2.3.9",
- "markdown-it": "^14.1.0",
- "minimatch": "^9.0.5",
- "yaml": "^2.8.1"
- },
- "bin": {
- "typedoc": "bin/typedoc"
- },
- "engines": {
- "node": ">= 18",
- "pnpm": ">= 10"
- },
- "peerDependencies": {
- "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x"
- }
- },
- "node_modules/typescript": {
- "version": "5.9.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
- "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "dev": true,
- "license": "Apache-2.0",
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/uc.micro": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
- "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/undici-types": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
- "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/update-browserslist-db": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
- "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "escalade": "^3.2.0",
- "picocolors": "^1.1.1"
- },
- "bin": {
- "update-browserslist-db": "cli.js"
- },
- "peerDependencies": {
- "browserslist": ">= 4.21.0"
- }
- },
- "node_modules/uri-js": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
- "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "punycode": "^2.1.0"
- }
- },
- "node_modules/uuid": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
- "node_modules/v8-compile-cache-lib": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
- "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/which-module": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
- "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
- "license": "ISC"
+ "license": "Python-2.0"
},
- "node_modules/word-wrap": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
- "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=0.10.0"
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/workerpool": {
- "version": "9.3.4",
- "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz",
- "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==",
- "dev": true,
- "license": "Apache-2.0"
- },
- "node_modules/wrap-ansi": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
- "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "node_modules/brace-expansion": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+ "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "ansi-styles": "^6.1.0",
- "string-width": "^5.0.1",
- "strip-ansi": "^7.0.1"
+ "balanced-match": "^4.0.2"
},
"engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/wrap-ansi-cjs": {
- "name": "wrap-ansi",
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
+ "node_modules/cluster-key-slot": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz",
+ "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
+ "ms": "^2.1.3"
},
"engines": {
- "node": ">=10"
+ "node": ">=6.0"
},
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
}
},
- "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
+ "node_modules/denque": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+ "license": "Apache-2.0",
"engines": {
- "node": ">=8"
+ "node": ">=0.10"
}
},
- "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/wrap-ansi-cjs/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
+ "license": "BSD-2-Clause",
"engines": {
- "node": ">=8"
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
}
},
- "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "node_modules/get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
"dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ioredis": {
+ "version": "5.11.1",
+ "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz",
+ "integrity": "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==",
"license": "MIT",
"dependencies": {
- "ansi-regex": "^5.0.1"
+ "@ioredis/commands": "1.10.0",
+ "cluster-key-slot": "1.1.1",
+ "debug": "4.4.3",
+ "denque": "2.1.0",
+ "redis-errors": "1.2.0",
+ "redis-parser": "3.0.0",
+ "standard-as-callback": "2.1.0"
},
"engines": {
- "node": ">=8"
+ "node": ">=12.22.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ioredis"
}
},
- "node_modules/wrap-ansi/node_modules/ansi-styles": {
- "version": "6.2.3",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
- "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "node_modules/linkify-it": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz",
+ "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==",
"dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/markdown-it"
+ }
+ ],
"license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ "dependencies": {
+ "uc.micro": "^2.0.0"
}
},
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "node_modules/lunr": {
+ "version": "2.3.9",
+ "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
"dev": true,
- "license": "ISC"
+ "license": "MIT"
},
- "node_modules/write-file-atomic": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "node_modules/markdown-it": {
+ "version": "14.3.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz",
+ "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==",
"dev": true,
- "license": "ISC",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/markdown-it"
+ }
+ ],
+ "license": "MIT",
"dependencies": {
- "imurmurhash": "^0.1.4",
- "is-typedarray": "^1.0.0",
- "signal-exit": "^3.0.2",
- "typedarray-to-buffer": "^3.1.5"
+ "argparse": "^2.0.1",
+ "entities": "^4.5.0",
+ "linkify-it": "^5.0.2",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
}
},
- "node_modules/write-file-atomic/node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"dev": true,
- "license": "ISC"
+ "license": "MIT"
},
- "node_modules/wsl-utils": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
- "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
+ "node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"dev": true,
- "license": "MIT",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "is-wsl": "^3.1.0"
+ "brace-expansion": "^5.0.5"
},
"engines": {
- "node": ">=18"
+ "node": "18 || 20 || >=22"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "node_modules/mock-require": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz",
+ "integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
+ "dependencies": {
+ "get-caller-file": "^1.0.2",
+ "normalize-path": "^2.1.1"
+ },
"engines": {
- "node": ">=10"
+ "node": ">=4.3.0"
}
},
- "node_modules/yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true,
- "license": "ISC"
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
},
- "node_modules/yaml": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
- "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
+ "node_modules/normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
"dev": true,
- "license": "ISC",
- "bin": {
- "yaml": "bin.mjs"
+ "license": "MIT",
+ "dependencies": {
+ "remove-trailing-separator": "^1.0.1"
},
"engines": {
- "node": ">= 14.6"
+ "node": ">=0.10.0"
}
},
- "node_modules/yargs": {
- "version": "18.0.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
- "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "node_modules/oxfmt": {
+ "version": "0.57.0",
+ "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.57.0.tgz",
+ "integrity": "sha512-ZB7Bi+rGDSqmVIo9jwcLyFgjxXvQhDdU+jx+ZrVy6VRiVXK2+CHc4hO3J4dUQjHe7V0ymHB+MDuv5z+NhK07HA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "cliui": "^9.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "string-width": "^7.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^22.0.0"
+ "tinypool": "2.1.0"
+ },
+ "bin": {
+ "oxfmt": "bin/oxfmt"
},
"engines": {
- "node": "^20.19.0 || ^22.12.0 || >=23"
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ },
+ "optionalDependencies": {
+ "@oxfmt/binding-android-arm-eabi": "0.57.0",
+ "@oxfmt/binding-android-arm64": "0.57.0",
+ "@oxfmt/binding-darwin-arm64": "0.57.0",
+ "@oxfmt/binding-darwin-x64": "0.57.0",
+ "@oxfmt/binding-freebsd-x64": "0.57.0",
+ "@oxfmt/binding-linux-arm-gnueabihf": "0.57.0",
+ "@oxfmt/binding-linux-arm-musleabihf": "0.57.0",
+ "@oxfmt/binding-linux-arm64-gnu": "0.57.0",
+ "@oxfmt/binding-linux-arm64-musl": "0.57.0",
+ "@oxfmt/binding-linux-ppc64-gnu": "0.57.0",
+ "@oxfmt/binding-linux-riscv64-gnu": "0.57.0",
+ "@oxfmt/binding-linux-riscv64-musl": "0.57.0",
+ "@oxfmt/binding-linux-s390x-gnu": "0.57.0",
+ "@oxfmt/binding-linux-x64-gnu": "0.57.0",
+ "@oxfmt/binding-linux-x64-musl": "0.57.0",
+ "@oxfmt/binding-openharmony-arm64": "0.57.0",
+ "@oxfmt/binding-win32-arm64-msvc": "0.57.0",
+ "@oxfmt/binding-win32-ia32-msvc": "0.57.0",
+ "@oxfmt/binding-win32-x64-msvc": "0.57.0"
+ },
+ "peerDependencies": {
+ "svelte": "^5.0.0",
+ "vite-plus": "*"
+ },
+ "peerDependenciesMeta": {
+ "svelte": {
+ "optional": true
+ },
+ "vite-plus": {
+ "optional": true
+ }
}
},
- "node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "node_modules/oxlint": {
+ "version": "1.72.0",
+ "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.72.0.tgz",
+ "integrity": "sha512-1rhdZIP/EvoI91ABIwNU5Q8+bWf8mjrS5UzIOZld4d4bXxJvtlUhlQvaoTogIGin/qdErMOrwaIJvCSIAKTLhA==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
+ "bin": {
+ "oxlint": "bin/oxlint"
+ },
"engines": {
- "node": ">=12"
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ },
+ "optionalDependencies": {
+ "@oxlint/binding-android-arm-eabi": "1.72.0",
+ "@oxlint/binding-android-arm64": "1.72.0",
+ "@oxlint/binding-darwin-arm64": "1.72.0",
+ "@oxlint/binding-darwin-x64": "1.72.0",
+ "@oxlint/binding-freebsd-x64": "1.72.0",
+ "@oxlint/binding-linux-arm-gnueabihf": "1.72.0",
+ "@oxlint/binding-linux-arm-musleabihf": "1.72.0",
+ "@oxlint/binding-linux-arm64-gnu": "1.72.0",
+ "@oxlint/binding-linux-arm64-musl": "1.72.0",
+ "@oxlint/binding-linux-ppc64-gnu": "1.72.0",
+ "@oxlint/binding-linux-riscv64-gnu": "1.72.0",
+ "@oxlint/binding-linux-riscv64-musl": "1.72.0",
+ "@oxlint/binding-linux-s390x-gnu": "1.72.0",
+ "@oxlint/binding-linux-x64-gnu": "1.72.0",
+ "@oxlint/binding-linux-x64-musl": "1.72.0",
+ "@oxlint/binding-openharmony-arm64": "1.72.0",
+ "@oxlint/binding-win32-arm64-msvc": "1.72.0",
+ "@oxlint/binding-win32-ia32-msvc": "1.72.0",
+ "@oxlint/binding-win32-x64-msvc": "1.72.0"
+ },
+ "peerDependencies": {
+ "oxlint-tsgolint": ">=0.22.1",
+ "vite-plus": "*"
+ },
+ "peerDependenciesMeta": {
+ "oxlint-tsgolint": {
+ "optional": true
+ },
+ "vite-plus": {
+ "optional": true
+ }
}
},
- "node_modules/yargs-unparser": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
- "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "camelcase": "^6.0.0",
- "decamelize": "^4.0.0",
- "flat": "^5.0.2",
- "is-plain-obj": "^2.1.0"
- },
"engines": {
- "node": ">=10"
+ "node": ">=6"
}
},
- "node_modules/yargs-unparser/node_modules/camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "dev": true,
+ "node_modules/redis-errors": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
+ "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
"license": "MIT",
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">=4"
}
},
- "node_modules/yargs-unparser/node_modules/decamelize": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
- "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
- "dev": true,
+ "node_modules/redis-parser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
+ "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"license": "MIT",
- "engines": {
- "node": ">=10"
+ "dependencies": {
+ "redis-errors": "^1.0.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": ">=4"
}
},
- "node_modules/yargs/node_modules/emoji-regex": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
- "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "node_modules/remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
"dev": true,
+ "license": "ISC"
+ },
+ "node_modules/standard-as-callback": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
+ "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
"license": "MIT"
},
- "node_modules/yargs/node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "node_modules/tinypool": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-2.1.0.tgz",
+ "integrity": "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
"engines": {
- "node": "6.* || 8.* || >= 10.*"
+ "node": "^20.0.0 || >=22.0.0"
}
},
- "node_modules/yargs/node_modules/string-width": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
- "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "node_modules/typedoc": {
+ "version": "0.28.20",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.20.tgz",
+ "integrity": "sha512-uSKqkh8Cr48vllnEy+jdaAgOeR6Y+QCBW7usgUsKj7gJEfR7stw9U/fE49LBnj2tPRKPY0c0EBJSWe9Appmplg==",
"dev": true,
- "license": "MIT",
+ "license": "Apache-2.0",
"dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
+ "@gerrit0/mini-shiki": "^3.23.0",
+ "lunr": "^2.3.9",
+ "markdown-it": "^14.3.0",
+ "minimatch": "^10.2.5",
+ "yaml": "^2.9.0"
+ },
+ "bin": {
+ "typedoc": "bin/typedoc"
},
"engines": {
- "node": ">=18"
+ "node": ">= 18",
+ "pnpm": ">= 10"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "peerDependencies": {
+ "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x"
}
},
- "node_modules/yargs/node_modules/yargs-parser": {
- "version": "22.0.0",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
- "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "node_modules/typescript": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
- "license": "ISC",
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
"engines": {
- "node": "^20.19.0 || ^22.12.0 || >=23"
+ "node": ">=14.17"
}
},
- "node_modules/yn": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
- "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
+ "license": "MIT"
},
- "node_modules/yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "node_modules/yaml": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
"dev": true,
- "license": "MIT",
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
"engines": {
- "node": ">=10"
+ "node": ">= 14.6"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/eemeli"
}
}
}
diff --git a/package.json b/package.json
index d41118a..7ad2a6b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@imqueue/core",
- "version": "2.0.26",
+ "version": "3.0.0",
"description": "Simple JSON-based messaging queue for inter service communication",
"keywords": [
"message-queue",
@@ -12,20 +12,25 @@
],
"scripts": {
"benchmark": "node benchmark -c $(( $(nproc) - 2 )) -m 100000",
- "prepare": "./node_modules/.bin/tsc",
- "test": "./node_modules/.bin/tsc && ./node_modules/.bin/nyc mocha --exit --timeout 10000 && ./node_modules/.bin/nyc report --reporter=text-lcov",
- "test-fast": "./node_modules/.bin/tsc && ./node_modules/.bin/nyc mocha --exit --timeout 10000 && /usr/bin/env node -e \"import('open').then(open => open.default('file://`pwd`/coverage/index.html', { wait: false }))\"",
- "test-local": "export COVERALLS_REPO_TOKEN=$IMQ_COVERALLS_TOKEN && npm test && /usr/bin/env node -e \"import('open').then(open => open.default('https://coveralls.io/github/imqueue/imq', { wait: false }))\"",
+ "clean-compiled": "npm run clean-js && npm run clean-typedefs && npm run clean-maps",
+ "build": "npm run clean-compiled && tsc",
+ "prepare": "npm run build",
+ "lint": "oxlint",
+ "format": "oxfmt \"index.ts\" \"src/**/*.ts\" \"test/**/*.ts\" \"benchmark/**/*.ts\"",
+ "format:check": "oxfmt --check \"index.ts\" \"src/**/*.ts\" \"test/**/*.ts\" \"benchmark/**/*.ts\"",
+ "test": "npm run build && node --test --test-timeout=15000 $(find test -name '*.spec.js')",
+ "test-coverage": "npm run build && node --enable-source-maps --test --experimental-test-coverage --test-timeout=15000 $(find test -name '*.spec.js')",
+ "test-lcov": "npm run build && mkdir -p coverage && node --enable-source-maps --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info --test-timeout=15000 $(find test -name '*.spec.js'); node scripts/strip-comment-coverage.mjs coverage/lcov.info",
+ "test-coverage-html": "npm run test-lcov; genhtml coverage/lcov.info --output-directory coverage/html --ignore-errors inconsistent,corrupt,format,mismatch && echo \"Coverage report: file://$(pwd)/coverage/html/index.html\"",
"test-dev": "npm run test && npm run clean-js && npm run clean-typedefs && npm run clean-maps",
- "test-coverage": "cat ./coverage/lcov.info | CODECLIMATE_API_HOST=https://codebeat.co/webhooks/code_coverage CODECLIMATE_REPO_TOKEN=85bb2a18-4ebb-4e48-a2ce-92b7bf438b1a ./node_modules/.bin/codeclimate-test-reporter",
"clean-typedefs": "find . -name '*.d.ts' -not -wholename '*node_modules*' -not -wholename '*generator*' -type f -delete",
"clean-maps": "find . -name '*.js.map' -not -wholename '*node_modules*' -not -wholename '*generator*' -type f -delete",
"clean-js": "find . -name '*.js' -not -wholename '*node_modules*' -not -wholename '*generator*' -type f -delete",
- "clean-tests": "rm -rf .nyc_output coverage",
+ "clean-tests": "rm -rf .nyc_output coverage .agent-out",
"clean-doc": "rm -rf docs",
"clean-benchmark": "rm -rf benchmark-result",
"clean": "npm run clean-tests && npm run clean-typedefs && npm run clean-maps && npm run clean-js && npm run clean-doc && npm run clean-benchmark",
- "doc": "rm -rf docs && typedoc --excludePrivate --excludeExternals --hideGenerator --exclude \"**/+(test|node_modules|docs|coverage|benchmark|.nyc_output)/**/*\" --out ./docs . && /usr/bin/env node -e \"import('open').then(open => open.default('file://`pwd`/docs/index.html',{wait:false}))\""
+ "doc": "typedoc && echo \"Docs generated: file://$(pwd)/docs/index.html\""
},
"repository": {
"type": "git",
@@ -38,69 +43,17 @@
"author": "imqueue.com (https://imqueue.com)",
"license": "GPL-3.0-only",
"dependencies": {
- "ioredis": "^5.8.2"
+ "ioredis": "^5.11.1"
},
"devDependencies": {
- "@eslint/js": "^9.33.0",
- "@types/chai": "^5.2.2",
- "@types/eslint__eslintrc": "^2.1.2",
- "@types/mocha": "^10.0.0",
"@types/mock-require": "^3.0.0",
- "@types/node": "^24.2.1",
- "@types/sinon": "^17.0.4",
- "@types/yargs": "^17.0.33",
- "@typescript-eslint/eslint-plugin": "^8.39.0",
- "@typescript-eslint/parser": "^8.39.0",
- "@typescript-eslint/typescript-estree": "^8.39.0",
- "chai": "^5.2.1",
- "coveralls-next": "^5.0.0",
- "eslint": "^9.33.0",
- "eslint-plugin-jsdoc": "^52.0.4",
- "mocha": "^11.7.1",
- "mocha-lcov-reporter": "^1.3.0",
+ "@types/node": "^24.9.1",
"mock-require": "^3.0.3",
- "nyc": "^17.1.0",
- "open": "^10.2.0",
- "reflect-metadata": "^0.2.2",
- "sinon": "^21.0.0",
- "source-map-support": "^0.5.21",
- "ts-node": "^10.9.2",
- "typedoc": "^0.28.9",
- "typescript": "^5.9.2",
- "yargs": "^18.0.0"
+ "oxfmt": "0.57.0",
+ "oxlint": "1.72.0",
+ "typedoc": "^0.28.20",
+ "typescript": "^6.0.3"
},
"main": "index.js",
- "typescript": {
- "definitions": "index.d.ts"
- },
- "mocha": {
- "require": [
- "ts-node/register",
- "source-map-support/register"
- ],
- "recursive": true,
- "bail": true,
- "full-trace": true,
- "exit": true,
- "timeout": 10000
- },
- "nyc": {
- "check-coverage": false,
- "extension": [
- ".ts"
- ],
- "exclude": [
- "**/*.d.ts",
- "**/test/**"
- ],
- "require": [
- "ts-node/register"
- ],
- "reporter": [
- "html",
- "text",
- "text-summary",
- "lcovonly"
- ]
- }
+ "types": "index.d.ts"
}
diff --git a/scripts/strip-comment-coverage.mjs b/scripts/strip-comment-coverage.mjs
new file mode 100644
index 0000000..6dd6926
--- /dev/null
+++ b/scripts/strip-comment-coverage.mjs
@@ -0,0 +1,397 @@
+/*!
+ * Coverage post-processor
+ *
+ * I'm Queue Software Project
+ * Copyright (C) 2025 imqueue.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * If you want to use this code in a closed source (commercial) project, you can
+ * purchase a proprietary commercial license. Please contact us at
+ * to get commercial licensing options.
+ */
+
+/**
+ * Node's built-in coverage (`--experimental-test-coverage --enable-source-maps`)
+ * reports every non-code line of a `.ts` source as uncovered, because comment
+ * and blank lines have no source-map mappings and therefore fall outside every
+ * covered V8 byte-range. That paints the license header and each JSDoc block
+ * red in the HTML report and pushes per-file line% far below reality.
+ *
+ * Node's reporter also emits records that the (strict) genhtml LCOV 2.x tool
+ * rejects — function entries with `undefined` line numbers or synthetic,
+ * duplicated names (``, and decorated methods all
+ * mis-named `has`/`get`), plus `BRDA:undefined,...` branch records. Left in,
+ * they produce a wall of warnings and a fatal "unexpected category" error.
+ *
+ * This script rewrites an lcov file in place: it drops the `DA`/`BRDA` records
+ * for lines that contain no executable code (comments and blank lines), drops
+ * all function records and malformed branch records, then recomputes the
+ * `LF`/`LH`/`BRF`/`BRH` totals. It uses the TypeScript scanner (already a dev
+ * dependency) to locate comments reliably — comment markers inside strings,
+ * template literals and regexes are not misdetected. The result renders in
+ * genhtml with accurate line + branch coverage and no warnings or errors.
+ */
+import { readFileSync, writeFileSync, existsSync } from 'node:fs';
+import ts from 'typescript';
+
+const lcovPath = process.argv[2] || 'coverage/lcov.info';
+
+if (!existsSync(lcovPath)) {
+ console.warn(`strip-comment-coverage: ${lcovPath} not found, skipping`);
+ process.exit(0);
+}
+
+/**
+ * Returns the set of 1-based line numbers in the given source that hold no
+ * executable code — i.e. blank lines and lines whose only content is a comment.
+ *
+ * @param {string} text
+ * @returns {Set}
+ */
+function nonCodeLines(text) {
+ const commentChars = new Uint8Array(text.length);
+ const scanner = ts.createScanner(
+ ts.ScriptTarget.Latest,
+ /* skipTrivia */ false,
+ ts.LanguageVariant.Standard,
+ text,
+ );
+
+ let token = scanner.scan();
+
+ while (token !== ts.SyntaxKind.EndOfFileToken) {
+ if (
+ token === ts.SyntaxKind.SingleLineCommentTrivia ||
+ token === ts.SyntaxKind.MultiLineCommentTrivia
+ ) {
+ const start = scanner.getTokenStart();
+ const end = scanner.getTokenEnd();
+
+ for (let i = start; i < end; i++) {
+ commentChars[i] = 1;
+ }
+ }
+
+ token = scanner.scan();
+ }
+
+ const lines = text.split('\n');
+ const result = new Set();
+ let pos = 0;
+
+ for (let ln = 0; ln < lines.length; ln++) {
+ const line = lines[ln];
+ let hasCode = false;
+
+ for (let i = 0; i < line.length; i++) {
+ const ch = line[i];
+
+ if (ch === ' ' || ch === '\t' || ch === '\r') {
+ continue;
+ }
+
+ if (!commentChars[pos + i]) {
+ hasCode = true;
+ break;
+ }
+ }
+
+ if (!hasCode) {
+ result.add(ln + 1);
+ }
+
+ pos += line.length + 1; // account for the split '\n'
+ }
+
+ return result;
+}
+
+/**
+ * Returns the set of 1-based lines occupied by top-level import statements.
+ * Imports compile to `require(...)` that runs on module load, so they are
+ * always executed, yet Node's source-mapped coverage frequently mis-reports
+ * them as uncovered (the mapping lands outside the covered byte-range). Treating
+ * them as non-coverable corrects that artifact rather than hiding real logic.
+ *
+ * @param {string} text
+ * @returns {Set}
+ */
+function importLines(text) {
+ const sf = ts.createSourceFile(
+ 'x.ts',
+ text,
+ ts.ScriptTarget.Latest,
+ false,
+ );
+ const result = new Set();
+
+ for (const stmt of sf.statements) {
+ if (
+ ts.isImportDeclaration(stmt) ||
+ ts.isImportEqualsDeclaration(stmt)
+ ) {
+ const from = sf.getLineAndCharacterOfPosition(stmt.getStart(sf)).line;
+ const to = sf.getLineAndCharacterOfPosition(stmt.getEnd()).line;
+
+ for (let line = from; line <= to; line++) {
+ result.add(line + 1);
+ }
+ }
+ }
+
+ return result;
+}
+
+const B64 =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+const B64MAP = new Map([...B64].map((c, i) => [c, i]));
+
+/**
+ * Decodes one base64 VLQ source-map segment into its integer fields.
+ *
+ * @param {string} seg
+ * @returns {number[]}
+ */
+function decodeVLQ(seg) {
+ const out = [];
+ let shift = 0;
+ let value = 0;
+
+ for (const ch of seg) {
+ const digit = B64MAP.get(ch);
+
+ if (digit === undefined) {
+ break;
+ }
+
+ value += (digit & 31) << shift;
+
+ if (digit & 32) {
+ shift += 5;
+ } else {
+ out.push(value & 1 ? -(value >> 1) : value >> 1);
+ value = 0;
+ shift = 0;
+ }
+ }
+
+ return out;
+}
+
+/**
+ * Returns the set of 1-based source lines that produced generated JavaScript,
+ * read from the file's `.js.map`. These are exactly the coverable lines — every
+ * other line (comments, blanks, type-only declarations, interfaces) emits no
+ * code and can never be executed. Returns null when no source map is present.
+ *
+ * @param {string} sourceFile - path of the .ts source (e.g. src/RedisQueue.ts)
+ * @returns {Set | null}
+ */
+function emittedLines(sourceFile) {
+ const mapPath = sourceFile.replace(/\.ts$/, '.js.map');
+
+ if (!existsSync(mapPath)) {
+ return null;
+ }
+
+ let map;
+
+ try {
+ map = JSON.parse(readFileSync(mapPath, 'utf8'));
+ } catch {
+ return null;
+ }
+
+ if (typeof map.mappings !== 'string') {
+ return null;
+ }
+
+ const lines = new Set();
+ let srcIndex = 0;
+ let srcLine = 0;
+
+ for (const group of map.mappings.split(';')) {
+ for (const seg of group.split(',')) {
+ if (!seg) {
+ continue;
+ }
+
+ const fields = decodeVLQ(seg);
+
+ // [genCol, srcIndex, srcLine, srcCol, nameIndex]; <4 fields = no
+ // source position for this segment
+ if (fields.length >= 4) {
+ srcIndex += fields[1];
+ srcLine += fields[2];
+
+ if (srcIndex === 0) {
+ lines.add(srcLine + 1); // map lines are 0-based
+ }
+ }
+ }
+ }
+
+ return lines;
+}
+
+const coverableCache = new Map();
+
+/**
+ * Resolves how to filter a source file's line records:
+ * - `{ mode: 'keep', set }` — keep only lines in `set` (source-map driven);
+ * - `{ mode: 'drop', set }` — drop lines in `set` (comment-scanner fallback).
+ *
+ * @param {string} sourceFile
+ * @returns {{ mode: 'keep' | 'drop', set: Set }}
+ */
+function coverableInfo(sourceFile) {
+ if (coverableCache.has(sourceFile)) {
+ return coverableCache.get(sourceFile);
+ }
+
+ const emitted = sourceFile ? emittedLines(sourceFile) : null;
+ let info;
+
+ if (emitted && existsSync(sourceFile)) {
+ // A line is coverable only if it emitted JS (has a source-map mapping)
+ // AND is not a comment/blank AND is not an import statement. The comment
+ // check is required because `removeComments: false` keeps license/JSDoc
+ // comments in the output, so they carry mappings too; the mapping check
+ // drops type-only lines (interfaces, type aliases) which emit nothing;
+ // the import check drops the mis-mapped import artifact.
+ const text = readFileSync(sourceFile, 'utf8');
+ const nonCode = nonCodeLines(text);
+ const imports = importLines(text);
+ const keep = new Set(
+ [...emitted].filter(
+ line => !nonCode.has(line) && !imports.has(line),
+ ),
+ );
+ info = { mode: 'keep', set: keep };
+ } else if (sourceFile && existsSync(sourceFile)) {
+ const text = readFileSync(sourceFile, 'utf8');
+ const skip = nonCodeLines(text);
+
+ for (const line of importLines(text)) {
+ skip.add(line);
+ }
+
+ info = { mode: 'drop', set: skip };
+ } else {
+ info = { mode: 'drop', set: new Set() };
+ }
+
+ coverableCache.set(sourceFile, info);
+
+ return info;
+}
+
+let strippedLines = 0;
+
+/**
+ * Rewrites a single lcov record so that genhtml accepts it without warnings or
+ * errors. It:
+ * - drops DA/BRDA entries on non-code (comment/blank) lines;
+ * - drops all function records (FN/FNDA/FNF/FNH) — Node's source-mapped
+ * function detection is unreliable here (undefined line numbers, mis-named
+ * and duplicated entries), which genhtml rejects, so line + branch coverage
+ * is kept instead;
+ * - drops malformed branch records such as `BRDA:undefined,...`;
+ * - recomputes the LF/LH/BRF/BRH totals.
+ *
+ * @param {string[]} recordLines - record body, excluding the end_of_record line
+ * @returns {string}
+ */
+function processRecord(recordLines) {
+ const sfLine = recordLines.find(line => line.startsWith('SF:'));
+ const sourceFile = sfLine ? sfLine.slice(3).trim() : '';
+ const info = coverableInfo(sourceFile);
+
+ // true when a source line produces no executable code and must be dropped
+ const drop = n => (info.mode === 'keep' ? !info.set.has(n) : info.set.has(n));
+
+ let lf = 0;
+ let lh = 0;
+ let brf = 0;
+ let brh = 0;
+
+ const kept = recordLines.filter(line => {
+ // drop all function records — unreliable under source maps
+ if (/^(FN:|FNDA:|FNF:|FNH:)/.test(line)) {
+ return false;
+ }
+
+ const da = line.match(/^DA:(\d+),(\d+)/);
+
+ if (da) {
+ if (drop(+da[1])) {
+ strippedLines++;
+
+ return false;
+ }
+
+ lf++;
+
+ if (+da[2] > 0) {
+ lh++;
+ }
+
+ return true;
+ }
+
+ if (line.startsWith('BRDA:')) {
+ const brda = line.match(/^BRDA:(\d+),\d+,\d+,(\d+|-)$/);
+
+ // drop malformed (e.g. BRDA:undefined,...) or non-code branches
+ if (!brda || drop(+brda[1])) {
+ return false;
+ }
+
+ brf++;
+
+ if (brda[2] !== '-' && +brda[2] > 0) {
+ brh++;
+ }
+
+ return true;
+ }
+
+ // drop the old totals — they are recomputed below
+ return !/^(LF|LH|BRF|BRH):/.test(line);
+ });
+
+ return (
+ `${kept.join('\n')}\n` +
+ `LF:${lf}\nLH:${lh}\nBRF:${brf}\nBRH:${brh}\nend_of_record\n`
+ );
+}
+
+const output = [];
+let record = [];
+
+for (const line of readFileSync(lcovPath, 'utf8').split('\n')) {
+ if (line === 'end_of_record') {
+ output.push(processRecord(record));
+ record = [];
+ } else if (line !== '') {
+ record.push(line);
+ }
+}
+
+writeFileSync(lcovPath, output.join(''));
+console.info(
+ `strip-comment-coverage: removed ${strippedLines} comment/blank line ` +
+ `entries from ${lcovPath}`,
+);
diff --git a/src/ClusterManager.ts b/src/ClusterManager.ts
index 86bbab5..eeff007 100644
--- a/src/ClusterManager.ts
+++ b/src/ClusterManager.ts
@@ -22,14 +22,12 @@
* to get commercial licensing options.
*/
import { IMessageQueueConnection, IServerInput } from './IMessageQueue';
-import { uuid } from './uuid';
+import { randomUUID } from 'node:crypto';
export interface ICluster {
- add: (server: IServerInput) => T;
+ add: (server: IServerInput) => IMessageQueueConnection;
remove: (server: IServerInput) => void;
- find: (
- server: IServerInput,
- ) => T | undefined;
+ find: (server: IServerInput) => IMessageQueueConnection | undefined;
}
export interface InitializedCluster extends ICluster {
@@ -42,20 +40,31 @@ export abstract class ClusterManager {
protected constructor() {}
public init(cluster: ICluster): InitializedCluster {
- const initializedCluster = Object.assign(
- cluster,
- { id: uuid() },
- ) as InitializedCluster;
+ const initializedCluster: InitializedCluster = {
+ ...cluster,
+ id: randomUUID(),
+ };
this.clusters.push(initializedCluster);
return initializedCluster;
}
- public async anyCluster(
+ /**
+ * Applies the given callback to every registered cluster. Each cluster
+ * is handled independently: a callback that throws (synchronously or
+ * asynchronously) for one cluster never prevents the remaining clusters
+ * from being processed.
+ *
+ * @param {(cluster: InitializedCluster) => Promise | void} fn
+ * @returns {Promise}
+ */
+ public async forEachCluster(
fn: (cluster: InitializedCluster) => Promise | void,
): Promise {
- await Promise.all(this.clusters.map(cluster => fn(cluster)));
+ await Promise.allSettled(
+ this.clusters.map(async cluster => fn(cluster)),
+ );
}
public async remove(
@@ -67,9 +76,9 @@ export abstract class ClusterManager {
this.clusters = this.clusters.filter(cluster => cluster.id !== id);
if (
- this.clusters.length === 0
- && destroy
- && typeof this.destroy === 'function'
+ this.clusters.length === 0 &&
+ destroy &&
+ typeof this.destroy === 'function'
) {
await this.destroy();
}
diff --git a/src/ClusteredRedisQueue.ts b/src/ClusteredRedisQueue.ts
index 739535e..52a7cc6 100644
--- a/src/ClusteredRedisQueue.ts
+++ b/src/ClusteredRedisQueue.ts
@@ -21,10 +21,10 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
-import { EventEmitter } from 'events';
+import { EventEmitter } from 'node:events';
+import { InitializedCluster } from './ClusterManager';
+import { buildOptions, copyEventEmitter } from './helpers';
import {
- buildOptions,
- copyEventEmitter,
DEFAULT_IMQ_OPTIONS,
EventMap,
ILogger,
@@ -36,7 +36,13 @@ import {
JsonObject,
RedisQueue,
} from '.';
-import { InitializedCluster } from './ClusterManager';
+
+/**
+ * Time (ms) send() waits for the first cluster server to become available
+ * before rejecting, when the cluster is still empty. Configurable via the
+ * IMQ_SEND_INIT_TIMEOUT environment variable, defaults to 30,000.
+ */
+const SEND_INIT_TIMEOUT = +(process.env.IMQ_SEND_INIT_TIMEOUT || 0) || 30000;
interface ClusterServer extends IMessageQueueConnection {
imq?: RedisQueue;
@@ -46,18 +52,18 @@ interface ClusterState {
started: boolean;
subscription: {
channel: string;
- handler: (data: JsonObject) => any;
+ handler: (data: JsonObject) => void;
} | null;
}
/**
* Class ClusteredRedisQueue
- * Implements the possibility to scale queues horizontally between several
+ Implements the possibility to scale queues horizontally between several
* redis instances.
*/
-export class ClusteredRedisQueue implements IMessageQueue,
- EventEmitter {
-
+export class ClusteredRedisQueue
+ implements IMessageQueue, EventEmitter
+{
/**
* Logger instance associated with this queue instance
*
@@ -92,7 +98,6 @@ export class ClusteredRedisQueue implements IMessageQueue,
*
* @type {IMessageQueueConnection[]}
*/
- // tslint:disable-next-line:completed-docs
private servers: ClusterServer[] = [];
/**
@@ -102,7 +107,11 @@ export class ClusteredRedisQueue implements IMessageQueue,
*/
private currentQueue: number = 0;
- // noinspection TypeScriptFieldCanBeMadeReadonly
+ /**
+ * Time (ms) send() waits for the first server when the cluster is empty
+ */
+ private readonly sendInitTimeout: number = SEND_INIT_TIMEOUT;
+
/**
* Total length of RedisQueue instances
*
@@ -136,31 +145,30 @@ export class ClusteredRedisQueue implements IMessageQueue,
/**
* Class constructor
*
- * @constructor
* @param {string} name
* @param {Partial} options
- * @param {IMQMode} [mode]
+ * @param {IMQMode} [_mode]
*/
public constructor(
public name: string,
options?: Partial,
- mode: IMQMode = IMQMode.BOTH,
+ _mode: IMQMode = IMQMode.BOTH,
) {
this.templateEmitter = new EventEmitter();
this.clusterEmitter = new EventEmitter();
this.options = buildOptions(DEFAULT_IMQ_OPTIONS, options);
- // istanbul ignore next
this.logger = this.options.logger || console;
if (!this.options.cluster && !this.options.clusterManagers?.length) {
- throw new TypeError('ClusteredRedisQueue: cluster ' +
- 'configuration is missing!');
+ throw new TypeError(
+ 'ClusteredRedisQueue: cluster ' + 'configuration is missing!',
+ );
}
this.mqOptions = { ...this.options };
- const cluster = [...this.mqOptions.cluster || []];
+ const cluster = [...(this.mqOptions.cluster || [])];
delete this.mqOptions.cluster;
@@ -172,11 +180,13 @@ export class ClusteredRedisQueue implements IMessageQueue,
this.verbose('Initializing cluster managers...');
for (const manager of this.options.clusterManagers) {
- this.initializedClusters.push(manager.init({
- add: this.addServer.bind(this),
- remove: this.removeServer.bind(this),
- find: this.findServer.bind(this),
- }));
+ this.initializedClusters.push(
+ manager.init({
+ add: this.addServer.bind(this),
+ remove: this.removeServer.bind(this),
+ find: this.findServer.bind(this),
+ }),
+ );
}
}
}
@@ -190,8 +200,10 @@ export class ClusteredRedisQueue implements IMessageQueue,
public async start(): Promise {
this.state.started = true;
- return await this.batch('start',
- 'Starting clustered redis message queue...');
+ return await this.batch(
+ 'start',
+ 'Starting clustered redis message queue...',
+ );
}
/**
@@ -203,20 +215,22 @@ export class ClusteredRedisQueue implements IMessageQueue,
public async stop(): Promise {
this.state.started = false;
- return await this.batch('stop',
- 'Stopping clustered redis message queue...');
+ return await this.batch(
+ 'stop',
+ 'Stopping clustered redis message queue...',
+ );
}
/**
- * Sends a message to given queue name with the given data.
+ * Sends a message to a given queue name with the given data.
* Supposed to be an async function.
*
- * @param {string} toQueue - queue name to which message should be sent to
+ * @param {string} toQueue - queue name to which a message should be sent to
* @param {JsonObject} message - message data
* @param {number} [delay] - if specified, a message will be handled in the
- * target queue after a specified period of time in milliseconds.
+ target queue after a specified period of time in milliseconds.
* @param {(err: Error) => void} [errorHandler] - callback called only when
- * internal error occurs during message send execution.
+ internal error occurs during message send execution.
* @returns {Promise} - message identifier
*/
public async send(
@@ -226,29 +240,84 @@ export class ClusteredRedisQueue implements IMessageQueue,
errorHandler?: (err: Error) => void,
): Promise {
if (!this.imqLength) {
- return await new Promise(resolve => this.clusterEmitter.once(
- 'initialized',
- async ({ imq }) => {
- resolve(await imq.send(
- toQueue,
- message,
- delay,
- errorHandler,
- ));
- },
- ));
+ return this.sendWhenInitialized(
+ toQueue,
+ message,
+ delay,
+ errorHandler,
+ );
}
- if (this.currentQueue >= this.imqLength) {
- this.currentQueue = 0;
+ const imq = this.selectQueue();
+
+ return imq.send(toQueue, message, delay, errorHandler);
+ }
+
+ /**
+ * Picks the next queue for a round-robin send, preferring an instance
+ * whose redis connection is currently ready so messages are not routed
+ * to a host that is known to be down. Falls back to the plain
+ * round-robin pick when no instance reports are ready.
+ *
+ * @returns {RedisQueue}
+ */
+ private selectQueue(): RedisQueue {
+ const count = this.imqLength;
+ const start = this.currentQueue % count;
+
+ for (let offset = 0; offset < count; offset++) {
+ const index = (start + offset) % count;
+ const candidate = this.imqs[index];
+
+ if (candidate.available) {
+ this.currentQueue = index + 1;
+
+ return candidate;
+ }
}
- const imq: any = this.imqs[this.currentQueue];
- const id = await imq.send(toQueue, message, delay, errorHandler);
+ this.currentQueue = start + 1;
- this.currentQueue++;
+ return this.imqs[start];
+ }
- return id;
+ /**
+ * Sends a message once the first cluster server becomes available.
+ * Rejects (rather than hanging forever) if none appears within the
+ * configured timeout and propagates any sent failure.
+ *
+ * @returns {Promise}
+ */
+ private sendWhenInitialized(
+ toQueue: string,
+ message: JsonObject,
+ delay?: number,
+ errorHandler?: (err: Error) => void,
+ ): Promise {
+ return new Promise((resolve, reject) => {
+ const onInitialized = ({ imq }: { imq: RedisQueue }): void => {
+ clearTimeout(timer);
+ imq.send(toQueue, message, delay, errorHandler).then(
+ resolve,
+ reject,
+ );
+ };
+
+ const timer = setTimeout(() => {
+ this.clusterEmitter.removeListener(
+ 'initialized',
+ onInitialized,
+ );
+ reject(
+ new Error(
+ 'ClusteredRedisQueue: no cluster server became ' +
+ 'available to send the message',
+ ),
+ );
+ }, this.sendInitTimeout);
+
+ this.clusterEmitter.once('initialized', onInitialized);
+ });
}
/**
@@ -261,21 +330,22 @@ export class ClusteredRedisQueue implements IMessageQueue,
public async destroy(): Promise {
this.state.started = false;
- await this.batch('destroy',
- 'Destroying clustered redis message queue...');
+ await this.batch(
+ 'destroy',
+ 'Destroying clustered redis message queue...',
+ );
if (!this.options.clusterManagers?.length) {
return;
}
- for await (const manager of this.options.clusterManagers) {
- for await (const cluster of this.initializedClusters) {
+ for (const manager of this.options.clusterManagers) {
+ for (const cluster of this.initializedClusters) {
await manager.remove(cluster);
}
}
}
- // noinspection JSUnusedGlobalSymbols
/**
* Clears queue data in queue host application.
* Supposed to be an async function.
@@ -283,8 +353,10 @@ export class ClusteredRedisQueue implements IMessageQueue,
* @returns {Promise}
*/
public async clear(): Promise {
- return await this.batch('clear',
- 'Clearing clustered redis message queue...');
+ return await this.batch(
+ 'clear',
+ 'Clearing clustered redis message queue...',
+ );
}
public async queueLength(): Promise {
@@ -301,27 +373,31 @@ export class ClusteredRedisQueue implements IMessageQueue,
private verbose(message: string): void {
if (this.options.verbose) {
- this.logger.info(`[IMQ-CORE][ClusteredRedisQueue][${
- this.name
- }]: ${ message }`);
+ this.logger.info(
+ `[IMQ-CORE][ClusteredRedisQueue][${this.name}]: ${message}`,
+ );
}
}
/**
* Batch imq action processing on all registered imqs at once
*
- * @access private
* @param {string} action
* @param {string} message
- * @return {Promise}
+ * @returns {Promise}
*/
- private async batch(action: string, message: string): Promise {
+ private async batch(
+ action: 'start' | 'stop' | 'destroy' | 'clear',
+ message: string,
+ ): Promise {
this.logger.info(message);
- const promises = [];
+ const promises: Promise[] = [];
for (const imq of this.imqs) {
- promises.push(imq[action]());
+ const run = imq[action] as () => Promise;
+
+ promises.push(run.call(imq));
}
await Promise.all(promises);
@@ -329,139 +405,120 @@ export class ClusteredRedisQueue implements IMessageQueue,
return this;
}
- /* tslint:disable */
// EventEmitter interface
- // istanbul ignore next
- public on(...args: any[]) {
+ /**
+ * Applies the named EventEmitter method to every underlying emitter,
+ * forwarding the call across the whole cluster. Dispatch is reflective
+ * (method chosen by name), so a single contained cast bridges the dynamic
+ * call while the public method signatures below stay fully typed.
+ *
+ * @template K - EventEmitter method name
+ * @param {K} method - name of the EventEmitter method to invoke
+ * @param {any[]} args - arguments to pass to the method
+ * @returns {unknown[]} - results from each emitter call
+ */
+ private applyToEmitters(
+ method: K,
+ args: any[],
+ ): unknown[] {
+ const results: unknown[] = [];
+
for (const imq of this.eventEmitters()) {
- imq.on.apply(imq, args);
+ const fn = imq[method] as unknown as (...a: any[]) => unknown;
+
+ results.push(fn.apply(imq, args));
}
- return this;
+ return results;
}
- // istanbul ignore next
- // noinspection JSUnusedGlobalSymbols
- public off(...args: any[]) {
- for (const imq of this.eventEmitters()) {
- imq.off.apply(imq, args);
- }
+ public on(...args: any[]): this {
+ this.applyToEmitters('on', args);
return this;
}
- // istanbul ignore next
- public once(...args: any[]) {
- for (const imq of this.eventEmitters()) {
- imq.once.apply(imq, args);
- }
+ public off(...args: any[]): this {
+ this.applyToEmitters('off', args);
return this;
}
- // istanbul ignore next
- public addListener(...args: any[]) {
- for (const imq of this.eventEmitters()) {
- imq.addListener.apply(imq, args);
- }
+ public once(...args: any[]): this {
+ this.applyToEmitters('once', args);
return this;
}
- // istanbul ignore next
- public removeListener(...args: any[]) {
- for (const imq of this.eventEmitters()) {
- imq.removeListener.apply(imq, args);
- }
+ public addListener(...args: any[]): this {
+ this.applyToEmitters('addListener', args);
return this;
}
- // istanbul ignore next
- public removeAllListeners(...args: any[]) {
- for (const imq of this.eventEmitters()) {
- imq.removeAllListeners.apply(imq, args);
- }
+ public removeListener(...args: any[]): this {
+ this.applyToEmitters('removeListener', args);
return this;
}
- // istanbul ignore next
- public prependListener(...args: any[]) {
- for (const imq of this.eventEmitters()) {
- imq.prependListener.apply(imq, args);
- }
+ public removeAllListeners(...args: any[]): this {
+ this.applyToEmitters('removeAllListeners', args);
return this;
}
- // istanbul ignore next
- public prependOnceListener(...args: any[]) {
- for (const imq of this.eventEmitters()) {
- imq.prependOnceListener.apply(imq, args);
- }
+ public prependListener(...args: any[]): this {
+ this.applyToEmitters('prependListener', args);
return this;
}
- // istanbul ignore next
- public setMaxListeners(...args: any[]) {
- for (const imq of this.eventEmitters()) {
- imq.setMaxListeners.apply(imq, args);
- }
+ public prependOnceListener(...args: any[]): this {
+ this.applyToEmitters('prependOnceListener', args);
return this;
}
- // istanbul ignore next
- public listeners(...args: any[]) {
- let listeners: any[] = [];
-
- for (const imq of this.eventEmitters()) {
- listeners = listeners.concat(imq.listeners.apply(imq, args));
- }
+ public setMaxListeners(...args: any[]): this {
+ this.applyToEmitters('setMaxListeners', args);
- return listeners;
+ return this;
}
- // istanbul ignore next
- public rawListeners(...args: any[]) {
- let rawListeners: any[] = [];
-
- for (const imq of this.eventEmitters()) {
- rawListeners = rawListeners.concat(
- imq.rawListeners.apply(imq, args),
- );
- }
+ // Aggregates listeners across every underlying emitter, so the result is
+ // an untyped union rather than Node's per-emitter conditional listener type.
+ public listeners(...args: any[]): any[] {
+ return this.applyToEmitters('listeners', args).flat();
+ }
- return rawListeners;
+ public rawListeners(...args: any[]): any[] {
+ return this.applyToEmitters('rawListeners', args).flat();
}
- // istanbul ignore next
- public getMaxListeners() {
+ public getMaxListeners(): number {
return this.templateEmitter.getMaxListeners();
}
- // istanbul ignore next
- public emit(...args: any[]) {
- for (const imq of this.eventEmitters()) {
- imq.emit.apply(imq, args);
- }
+ public emit(...args: any[]): boolean {
+ this.applyToEmitters('emit', args);
return true;
}
- // istanbul ignore next
- public eventNames(...args: any[]) {
- return this.templateEmitter.eventNames.apply(this.imqs[0], args);
+ public eventNames(): (keyof EventMap)[] {
+ const source = this.imqs[0] || this.templateEmitter;
+
+ return source.eventNames() as (keyof EventMap)[];
}
- // istanbul ignore next
- public listenerCount(...args: any[]) {
- return this.templateEmitter.listenerCount.apply(this.imqs[0], args);
+ public listenerCount(...args: any[]): number {
+ const source = this.imqs[0] || this.templateEmitter;
+ const fn = source.listenerCount as (...a: any[]) => number;
+
+ return fn.apply(source, args);
}
- // istanbul ignore next
public async publish(data: JsonObject, toName?: string): Promise {
const promises: Array> = [];
@@ -472,10 +529,9 @@ export class ClusteredRedisQueue implements IMessageQueue,
await Promise.all(promises);
}
- // istanbul ignore next
public async subscribe(
channel: string,
- handler: (data: JsonObject) => any,
+ handler: (data: JsonObject) => void,
): Promise {
this.state.subscription = { channel, handler };
@@ -488,7 +544,6 @@ export class ClusteredRedisQueue implements IMessageQueue,
await Promise.all(promises);
}
- // istanbul ignore next
public async unsubscribe(): Promise {
this.state.subscription = null;
@@ -508,7 +563,7 @@ export class ClusteredRedisQueue implements IMessageQueue,
* @returns {void}
*/
protected addServer(server: IServerInput): ClusterServer {
- this.verbose(`Adding new server: ${ JSON.stringify(server) }`);
+ this.verbose(`Adding new server: ${JSON.stringify(server)}`);
return this.addServerWithQueueInitializing(server, true);
}
@@ -520,7 +575,7 @@ export class ClusteredRedisQueue implements IMessageQueue,
* @returns {void}
*/
protected removeServer(server: IServerInput): void {
- this.verbose(`Removing the server: ${ JSON.stringify(server) }`);
+ this.verbose(`Removing the server: ${JSON.stringify(server)}`);
const remove = this.findServer(server);
@@ -532,17 +587,18 @@ export class ClusteredRedisQueue implements IMessageQueue,
if (imqToRemove) {
this.imqs = this.imqs.filter(
- imq => imqToRemove.redisKey !== imq.redisKey
+ imq => imqToRemove.redisKey !== imq.redisKey,
);
- imqToRemove.destroy().catch();
+ imqToRemove
+ .destroy()
+ .catch((err: unknown) =>
+ this.verbose(`Error destroying removed server: ${err}`),
+ );
}
this.imqLength = this.imqs.length;
this.servers = this.servers.filter(
- existing => !ClusteredRedisQueue.matchServers(
- existing,
- server,
- ),
+ existing => !ClusteredRedisQueue.matchServers(existing, server),
);
this.clusterEmitter.emit('remove', {
server: remove,
@@ -569,6 +625,8 @@ export class ClusteredRedisQueue implements IMessageQueue,
const opts = { ...this.mqOptions, ...newServer };
const imq = new RedisQueue(this.name, opts);
+ copyEventEmitter(this.templateEmitter, imq);
+
if (initializeQueue) {
this.initializeQueue(imq).then(() => {
this.clusterEmitter.emit('initialized', {
@@ -593,13 +651,12 @@ export class ClusteredRedisQueue implements IMessageQueue,
}
private async initializeQueue(imq: RedisQueue): Promise {
- copyEventEmitter(this.templateEmitter, imq);
- this.verbose(`Initializing queue with state: ${
- JSON.stringify(this.state)
- }`);
+ this.verbose(
+ `Initializing queue with state: ${JSON.stringify(this.state)}`,
+ );
if (this.state.started) {
- await imq.start();
+ await imq.start();
}
if (this.state.subscription) {
@@ -611,11 +668,8 @@ export class ClusteredRedisQueue implements IMessageQueue,
}
private findServer(server: IServerInput): ClusterServer | undefined {
- return this.servers.find(
- existing => ClusteredRedisQueue.matchServers(
- existing,
- server,
- ),
+ return this.servers.find(existing =>
+ ClusteredRedisQueue.matchServers(existing, server),
);
}
@@ -623,8 +677,8 @@ export class ClusteredRedisQueue implements IMessageQueue,
source: IServerInput,
target: IServerInput,
): boolean {
- const sameAddress = target.host === source.host
- && target.port === source.port;
+ const sameAddress =
+ target.host === source.host && target.port === source.port;
if (!target.id && !source.id) {
return sameAddress;
diff --git a/src/IMessageQueue.ts b/src/IMessageQueue.ts
index 51d714b..6b8aa81 100644
--- a/src/IMessageQueue.ts
+++ b/src/IMessageQueue.ts
@@ -21,17 +21,23 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
-import { EventEmitter } from 'events';
+import { EventEmitter } from 'node:events';
import { IMQMode } from './IMQMode';
import { ClusterManager } from './ClusterManager';
-export { EventEmitter } from 'events';
+export { EventEmitter } from 'node:events';
/**
* Represents any JSON-serializable value
*/
-export type AnyJson = boolean | number | string | null | undefined |
- JsonArray | JsonObject;
+export type AnyJson =
+ | boolean
+ | number
+ | string
+ | null
+ | undefined
+ | JsonArray
+ | JsonObject;
/**
* Represents JSON serializable object
@@ -45,14 +51,6 @@ export interface JsonObject {
*/
export interface JsonArray extends Array {}
-/**
- * Alias for JsonObject, legacy, outdated, deprecated, try to avoid using.
- * Stands here for backward-compatibility only
- *
- * @deprecated
- */
-export type IJson = JsonObject;
-
/**
* Logger interface
*/
@@ -60,34 +58,34 @@ export interface ILogger {
/**
* Log level function
*
- * @param {...any[]} args
+ * @param {...unknown[]} args
*/
- log(...args: any[]): void;
+ log(...args: unknown[]): void;
/**
* Info level function
*
- * @param {...any[]} args
+ * @param {...unknown[]} args
*/
- info(...args: any[]): void;
+ info(...args: unknown[]): void;
/**
* Warning level function
*
- * @param {...any[]} args
+ * @param {...unknown[]} args
*/
- warn(...args: any[]): void;
+ warn(...args: unknown[]): void;
/**
* Error level function
*
- * @param {...any[]} args
+ * @param {...unknown[]} args
*/
- error(...args: any[]): void;
+ error(...args: unknown[]): void;
}
/**
- * Defines message format.
+ * Defines a message format.
*
* @type {IMessage}
*/
@@ -195,12 +193,11 @@ export interface IMQOptions extends Partial {
cleanup: boolean;
/**
- * Defines cleanup pattern for the name of the queue
- * which should be removed during cleanup processing
+ * Cleanup pattern for queue names that should be removed during cleanup
*
* @type {string}
*/
- cleanupFilter: string,
+ cleanupFilter: string;
/**
* Message queue vendor
@@ -217,45 +214,42 @@ export interface IMQOptions extends Partial {
prefix?: string;
/**
- * Logger defined to be used within message queue in runtime
+ * Logger instance to use for message queue logging at runtime
*
* @type {ILogger}
*/
logger?: ILogger;
/**
- * Watcher check delay period. This is used by a queue watcher
- * agent to make sure at least one watcher is available for
- * queue operations.
+ * Delay period (in milliseconds) between watcher availability checks.
+ * Used to ensure at least one watcher is available for queue operations.
*
* @type {number}
*/
watcherCheckDelay?: number;
/**
- * A way to serialize message using compression. Will increase
- * load to worker process but can decrease network traffic between worker
- * and queue host application
+ * Enable message compression for serialization. Increases a worker CPU load
+ * but decreases network traffic between workers and the queue host.
*
* @type {boolean}
*/
useGzip?: boolean;
/**
- * Enables/disables safe message delivery. When safe message delivery
- * is turned on it will use more complex algorithm for message handling
- * by a worker process, guaranteeing that if worker fails the message will
- * be delivered to another possible worker anyway. In most cases it
- * is not required unless it is required by a system design.
+ * Enable guaranteed message delivery. When enabled, uses a more complex
+ * algorithm for message handling, ensuring that if a worker fails, the
+ * message will be delivered to another worker. Required only for systems
+ * that demand guaranteed delivery semantics.
*
* @type {boolean}
*/
safeDelivery?: boolean;
/**
- * Time-to-live of worker queues (after this time messages are back to
- * main queue for handling if worker died). Only works if safeDelivery
- * option enabled.
+ * Time-to-live (in milliseconds) for messages in worker queues. After this
+ * period, unacknowledged messages return to the main queue for reprocessing
+ * if the worker died. Only effective when safeDelivery is enabled.
*
* @type {number}
*/
@@ -276,6 +270,16 @@ export interface IMQOptions extends Partial {
*/
clusterManagers?: ClusterManager[];
+ /**
+ * Enable process signal handling (SIGTERM, SIGINT, SIGABRT) by the queue.
+ * When enabled, the queue releases its watcher lock and exits gracefully
+ * on these signals. Disable if the host application manages shutdown.
+ *
+ * @default true
+ * @type {boolean}
+ */
+ handleSignals?: boolean;
+
/**
* Enables/disables verbose logging
*
@@ -296,8 +300,8 @@ export interface IMQOptions extends Partial {
}
export interface EventMap {
- message: [data: any, id: string, from: string],
- error: [error: Error, eventName: string],
+ message: [data: JsonObject, id: string, from: string];
+ error: [error: Error, eventName: string];
}
export type IMessageQueueConstructor = new (
@@ -311,130 +315,129 @@ export type IMessageQueueConstructor = new (
*
* @example
* ~~~typescript
- * import { IMessageQueue, EventEmitter, uuid } from '@imqueue/core';
+ * import { IMessageQueue, EventEmitter } from '@imqueue/core';
+ * import { randomUUID } from 'node:crypto';
*
- * class SomeMQAdapter implements IMessageQueue extends EventEmitter {
- * public async start(): Promise {
- * // ... implementation goes here
- * return this;
- * }
- * public async stop(): Promise {
- * // ... implementation goes here
- * return this;
- * }
- * public async send(
- * toQueue: string,
- * message: JsonObject,
- * delay?: number
- * ): Promise {
- * const messageId = uuid();
- * // ... implementation goes here
- * return messageId;
- * }
- * public async destroy(): Promise {
- * // ... implementation goes here
- * }
- * public async clear(): Promise {
- * // ... implementation goes here
- * return this;
- * }
+ * class SomeMQAdapter extends EventEmitter implements IMessageQueue {
+ * public async start(): Promise {
+ * // ... implementation goes here
+ * return this;
+ * }
+ * public async stop(): Promise {
+ * // ... implementation goes here
+ * return this;
+ * }
+ * public async send(
+ * toQueue: string,
+ * message: JsonObject,
+ * delay?: number,
+ * ): Promise {
+ * const messageId = randomUUID();
+ * // ... implementation goes here
+ * return messageId;
+ * }
+ * public async destroy(): Promise {
+ * // ... implementation goes here
+ * }
+ * public async clear(): Promise {
+ * // ... implementation goes here
+ * return this;
+ * }
* }
* ~~~
*/
export interface IMessageQueue extends EventEmitter {
/**
- * Message event. Occurs every time queue got a message.
+ * Message event. Occurs every time the queue receives a message.
*
* @event IMessageQueue#message
* @type {JsonObject} message - message data
* @type {string} id - message identifier
- * @type {string} from - source queue produced the message
+ * @type {string} from - source queue that produced the message
*/
/**
- * Error event. Occurs every time queue caught an error.
+ * Error event. Occurs every time the queue encounters an error.
*
* @event IMessageQueue#error
- * @type {Error} err - error caught by message queue
+ * @type {Error} err - error caught by the message queue
* @type {string} code - message queue error code
*/
/**
* Starts the messaging queue.
- * Supposed to be an async function.
*
* @returns {Promise}
*/
start(): Promise;
/**
- * Stops the queue (should stop handle queue messages).
- * Supposed to be an async function.
+ * Stops the queue from handling messages.
*
* @returns {Promise}
*/
stop(): Promise;
/**
- * Sends a message to given queue name with the given data.
- * Supposed to be an async function.
+ * Sends a message to the specified queue with the given data.
*
- * @param {string} toQueue - queue name to which message should be sent to
- * @param {JsonObject} message - message data
- * @param {number} [delay] - if specified, message will be handled in the
- * target queue after specified period of time in milliseconds.
- * @param {(err: Error) => void} [errorHandler] - callback called only when
- * internal error occurs during message send execution.
- * @returns {Promise} - message identifier
+ * @param {string} toQueue - name of the destination queue
+ * @param {JsonObject} message - message data to send
+ * @param {number} [delay] - if specified, the message will be handled in
+ * the target queue after the specified delay in milliseconds
+ * @param {(err: Error) => void} [errorHandler] - callback invoked only
+ * when an internal error occurs during message send execution
+ * @returns {Promise} - the message identifier
*/
- send(toQueue: string, message: JsonObject, delay?: number,
- errorHandler?: (err: Error) => void): Promise;
+ send(
+ toQueue: string,
+ message: JsonObject,
+ delay?: number,
+ errorHandler?: (err: Error) => void,
+ ): Promise;
/**
- * Creates or uses subscription channel with the given name and sets
+ * Creates or uses a subscription channel with the given name and sets
* message handler on data receive
*
* @param {string} channel - channel name
- * @param {(data: JsonObject) => any} handler
+ * @param {(data: JsonObject) => void} handler
*/
subscribe(
channel: string,
- handler: (data: JsonObject) => any,
+ handler: (data: JsonObject) => void,
): Promise;
/**
- * Closes subscription channel
+ * Closes the subscription channel
*
- * @return {Promise}
+ * @returns {Promise}
*/
unsubscribe(): Promise;
/**
- * Publishes data to current queue channel
+ * Publishes data to the current queue channel
*
- * If toName specified will publish to pubsub with different name. This
- * can be used to implement broadcasting some messages to other subscribers
- * on other pubsub channels. Different name should be in the same namespace
- * (same imq prefix)
+ * If toName is specified, publishes to a pubsub with a different name. This
+ * can be used to broadcast messages to other subscribers on different pubsub
+ * channels. Different names must be in the same namespace (same imq prefix).
*
- * @param {JsonObject} data - data to publish as channel message
- * @param {string} [toName] - different name of the pubsub to publish to
- * @return {Promise}
+ * @param {JsonObject} data - data to publish as a channel message
+ * @param {string} [toName] - optional different pubsub name to publish to
+ * @returns {Promise}
*/
publish(data: JsonObject, toName?: string): Promise;
/**
- * Safely destroys current queue, unregistered all set event
- * listeners and connections.
- * Supposed to be an async function.
+ * Safely destroys the current queue, unregistering all event listeners
+ * and closing connections.
*
* @returns {Promise}
*/
destroy(): Promise;
/**
- * Clears queue data in queue host application.
- * Supposed to be an async function.
+ * Clears all queue data from the queue host application.
*
* @returns {Promise}
*/
diff --git a/src/RedisQueue.ts b/src/RedisQueue.ts
index 7b54c27..4170a34 100644
--- a/src/RedisQueue.ts
+++ b/src/RedisQueue.ts
@@ -21,10 +21,9 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
-import * as crypto from 'crypto';
-import { EventEmitter } from 'events';
-import * as os from 'os';
-import { gunzipSync as gunzip, gzipSync as gzip } from 'zlib';
+import { EventEmitter } from 'node:events';
+import { randomUUID } from 'node:crypto';
+import { hostname } from 'node:os';
import {
IMessageQueue,
IRedisClient,
@@ -34,16 +33,52 @@ import {
ILogger,
IMQMode,
EventMap,
- buildOptions,
profile,
- uuid,
} from '.';
+import {
+ buildOptions,
+ escapeRegExp,
+ randomInt,
+ pack,
+ sha1,
+ unpack,
+ envInt,
+} from './helpers';
import Redis from './redis';
const RX_CLIENT_NAME = /name=(\S+)/g;
const RX_CLIENT_TEST = /:(reader|writer|watcher)/;
const RX_CLIENT_CLEAN = /:(reader|writer|watcher).*$/;
+/** Base delay (ms) for the exponential reconnection backoff */
+const RECONNECT_BASE_DELAY = 1000;
+
+/** Upper cap (ms) for the exponential reconnection backoff */
+const RECONNECT_MAX_DELAY = 30000;
+
+/** SCAN batch size used while sweeping keys */
+const SCAN_COUNT = '1000';
+
+/**
+ * ioredis retry strategy that disables the built-in reconnection — this
+ * queue performs its own capped-backoff reconnection instead.
+ *
+ * @returns {null}
+ */
+function noRetryStrategy(): null {
+ return null;
+}
+
+/**
+ * Resolves after the given number of milliseconds.
+ *
+ * @param {number} ms
+ * @returns {Promise}
+ */
+function delay(ms: number): Promise {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
export const DEFAULT_IMQ_OPTIONS: IMQOptions = {
host: 'localhost',
port: 6379,
@@ -55,72 +90,37 @@ export const DEFAULT_IMQ_OPTIONS: IMQOptions = {
safeDeliveryTtl: 5000,
useGzip: false,
watcherCheckDelay: 5000,
+ handleSignals: true,
};
-export const IMQ_SHUTDOWN_TIMEOUT = +(process.env.IMQ_SHUTDOWN_TIMEOUT || 1000);
-
-/**
- * Returns SHA1 hash sum of the given string
- *
- * @param {string} str
- * @returns {string}
- */
-export function sha1(str: string): string {
- const sha: crypto.Hash = crypto.createHash('sha1');
-
- sha.update(str);
-
- return sha.digest('hex');
-}
+export const IMQ_SHUTDOWN_TIMEOUT = envInt('IMQ_SHUTDOWN_TIMEOUT', 1000);
/**
- * Returns random integer between given min and max
- *
- * @param {number} min
- * @param {number} max
- * @returns {number}
+ * Grace period (ms) for a graceful QUIT to complete before a channel is
+ * forcibly disconnected. A reader blocked on an infinite BRPOP/BLMOVE can
+ * never let QUIT through, so without this the socket would leak and keep
+ * the process alive.
*/
-export function intrand(min: number, max: number): number {
- return Math.floor(Math.random() * (max - min + 1)) + min;
-}
-
-/**
- * Compress given data and returns binary string
- *
- * @param {any} data
- * @returns {string}
- */
-// istanbul ignore next
-export function pack(data: any): string {
- return gzip(JSON.stringify(data)).toString('binary');
-}
-
-/**
- * Decompress binary string and returns plain data
- *
- * @param {string} data
- * @returns {any}
- */
-// istanbul ignore next
-export function unpack(data: string): any {
- return JSON.parse(gunzip(Buffer.from(data, 'binary')).toString());
-}
+export const IMQ_CONNECTION_QUIT_TIMEOUT = envInt(
+ 'IMQ_CONNECTION_QUIT_TIMEOUT',
+ 1000,
+);
type RedisConnectionChannel = 'reader' | 'writer' | 'watcher' | 'subscription';
-const IMQ_REDIS_MAX_LISTENERS_LIMIT = +(
- process.env.IMQ_REDIS_MAX_LISTENERS_LIMIT || 10000
+const IMQ_REDIS_MAX_LISTENERS_LIMIT = envInt(
+ 'IMQ_REDIS_MAX_LISTENERS_LIMIT',
+ 10000,
);
/**
* Class RedisQueue
- * Implements simple messaging queue over redis.
+ * Implements a simple messaging queue over redis.
*/
-export class RedisQueue extends EventEmitter
- implements IMessageQueue {
-
- [name: string]: any;
-
+export class RedisQueue
+ extends EventEmitter
+ implements IMessageQueue
+{
/**
* Writer connections collection
*
@@ -135,6 +135,27 @@ export class RedisQueue extends EventEmitter
*/
private static watchers: { [key: string]: IRedisClient } = {};
+ /**
+ * Number of started queue instances per shared writer connection key
+ *
+ * @type {{}}
+ */
+ private static writerRefs: { [key: string]: number } = {};
+
+ /**
+ * All started queue instances within the current process
+ *
+ * @type {Set}
+ */
+ private static readonly instances: Set = new Set();
+
+ /**
+ * True when process-level signal handlers were bound
+ *
+ * @type {boolean}
+ */
+ private static signalsBound: boolean = false;
+
/**
* @event message (message: JsonObject, id: string, from: string)
*/
@@ -190,23 +211,48 @@ export class RedisQueue extends EventEmitter
private watchOwner: boolean = false;
/**
- * Signals initialization state
+ * Subscription handlers registered through subscribe(). Kept to be
+ * able to restore the subscription after a connection replacement.
*
- * @type {boolean}
+ * @type {Array<(data: JsonObject) => void>}
*/
- private signalsInitialized: boolean = false;
+ private subscriptionHandlers: Array<(data: JsonObject) => void> = [];
/**
* Will store check interval reference
*/
- private safeCheckInterval: any;
+ private safeCheckInterval?: NodeJS.Timeout;
+
+ /**
+ * Periodic watcher existence check interval reference
+ */
+ private watcherCheckInterval?: NodeJS.Timeout;
+
+ /**
+ * Guards overlapping watcher check runs
+ */
+ private watcherCheckBusy: boolean = false;
+
+ /**
+ * Connected client keys seen during the previous cleanup sweep. Used
+ * to give temporarily disconnected clients one sweep of grace before
+ * removing their keys.
+ */
+ private lastConnectedKeys: string[] = [];
+
+ /**
+ * True while this instance holds a reference to the shared writer
+ */
+ private writerAcquired: boolean = false;
/**
* Internal per-channel reconnection state
*/
- private reconnectTimers: Partial> = {};
- private reconnectAttempts: Partial>
- = {};
+ private reconnectTimers: Partial<
+ Record
+ > = {};
+ private reconnectAttempts: Partial> =
+ {};
private reconnecting: Partial> = {};
/**
@@ -215,49 +261,48 @@ export class RedisQueue extends EventEmitter
public readonly redisKey: string;
/**
- * LUA scripts for redis
+ * Lua scripts for redis
*
* @type {{moveDelayed: {code: string}}}
*/
- // tslint:disable-next-line:completed-docs
- private scripts: { [name: string]: { code: string, checksum?: string } } = {
+ private scripts: { [name: string]: { code: string; checksum?: string } } = {
moveDelayed: {
- code:
- 'local messages = redis.call(' +
- '"zrangebyscore", KEYS[1], "-inf", ARGV[1]) ' +
- 'local count = table.getn(messages) ' +
- 'local message ' +
- 'local i = 1 ' +
- 'if count > 0 then ' +
- 'while messages[i] do ' +
- 'redis.call("lpush", KEYS[2], messages[i]) ' +
- 'i = i + 1 ' +
- 'end ' +
- 'redis.call("zremrangebyscore", KEYS[1], ' +
- '"-inf", ARGV[1]) ' +
- 'end ' +
- 'return count',
+ code: `
+ local messages = redis.call(
+ "zrangebyscore", KEYS[1], "-inf", ARGV[1])
+ local count = table.getn(messages)
+ local message
+ local i = 1
+ if count > 0 then
+ while messages[i] do
+ redis.call("lpush", KEYS[2], messages[i])
+ i = i + 1
+ end
+ redis.call("zremrangebyscore", KEYS[1],
+ "-inf", ARGV[1])
+ end
+ return count
+ `,
},
};
/**
- * Serializes given data object into string
+ * Serializes a given data object into string
*
- * @param {any} data
+ * @param {unknown} data
* @returns {string}
*/
- private readonly pack: (data: any) => string;
+ private readonly pack: (data: unknown) => string;
/**
- * Deserialize string data into object
+ * Deserialize string data into an object
*
* @param {string} data
- * @returns {any}
+ * @returns {unknown}
*/
- private readonly unpack: (data: string) => any;
+ private readonly unpack: (data: string) => unknown;
/**
- * @constructor
* @param {string} name
* @param {IMQOptions} [options]
* @param {IMQMode} [mode]
@@ -269,30 +314,32 @@ export class RedisQueue extends EventEmitter
) {
super();
- this.options = buildOptions(
- DEFAULT_IMQ_OPTIONS,
- options,
- );
+ this.options = buildOptions(DEFAULT_IMQ_OPTIONS, options);
- /* tslint:disable */
this.pack = this.options.useGzip ? pack : JSON.stringify;
this.unpack = this.options.useGzip ? unpack : JSON.parse;
- /* tslint:enable */
this.redisKey = `${this.options.host}:${this.options.port}`;
- this.verbose(`Initializing queue on ${
- this.options.host }:${
- this.options.port} with prefix ${
- this.options.prefix } and safeDelivery = ${
- this.options.safeDelivery }, and safeDeliveryTtl = ${
- this.options.safeDeliveryTtl }, and watcherCheckDelay = ${
- this.options.watcherCheckDelay }, and useGzip = ${
- this.options.useGzip }`);
+ for (const script of Object.keys(this.scripts)) {
+ this.scripts[script].checksum = sha1(this.scripts[script].code);
+ }
+
+ this.verbose(
+ `Initializing queue on ${this.options.host}:${
+ this.options.port
+ } with prefix ${this.options.prefix} and safeDelivery = ${
+ this.options.safeDelivery
+ }, and safeDeliveryTtl = ${
+ this.options.safeDeliveryTtl
+ }, and watcherCheckDelay = ${
+ this.options.watcherCheckDelay
+ }, and useGzip = ${this.options.useGzip}`,
+ );
}
private verbose(message: string): void {
if (this.options.verbose) {
- this.logger.info(`[IMQ-CORE][${ this.name }]: ${ message }`);
+ this.logger.info(`[IMQ-CORE][${this.name}]: ${message}`);
}
}
@@ -301,25 +348,24 @@ export class RedisQueue extends EventEmitter
* data read handler
*
* @param {string} channel
- * @param {(data: JsonObject) => any} handler
- * @return {Promise}
+ * @param {(data: JsonObject) => void} handler
+ * @returns {Promise}
*/
public async subscribe(
channel: string,
- handler: (data: JsonObject) => any,
+ handler: (data: JsonObject) => void,
): Promise {
- // istanbul ignore next
if (!channel) {
throw new TypeError(
- `${ channel }: No subscription channel name provided!`,
+ `${channel}: No subscription channel name provided!`,
);
}
- // istanbul ignore next
if (this.subscriptionName && this.subscriptionName !== channel) {
throw new TypeError(
`Invalid channel name provided: expected "${
- this.subscriptionName}", but "${ channel }" given instead!`,
+ this.subscriptionName
+ }", but "${channel}" given instead!`,
);
} else if (!this.subscriptionName) {
this.subscriptionName = channel;
@@ -329,26 +375,71 @@ export class RedisQueue extends EventEmitter
const chan = await this.connect('subscription', this.options);
await chan.subscribe(fcn);
+ this.attachSubscriptionHandler(chan, handler);
+ this.subscriptionHandlers.push(handler);
+
+ this.verbose(`Subscribed to ${channel} channel`);
+ }
+
+ /**
+ * Attaches a subscription message handler to a given channel
+ * connection
+ *
+ * @param {IRedisClient} chan
+ * @param {(data: JsonObject) => void} handler
+ */
+ private attachSubscriptionHandler(
+ chan: IRedisClient,
+ handler: (data: JsonObject) => void,
+ ): void {
+ const fcn = `${this.options.prefix}:${this.subscriptionName}`;
- // istanbul ignore next
chan.on('message', (ch: string, message: string) => {
if (ch === fcn && typeof handler === 'function') {
- handler(JSON.parse(message) as unknown as JsonObject);
+ handler(JSON.parse(message) as JsonObject);
}
- this.verbose(`Received message from ${
- ch } channel, data: ${
- JSON.stringify(message) }`,
+ this.verbose(
+ `Received message from ${ch} channel, data: ${JSON.stringify(
+ message,
+ )}`,
);
});
+ }
+
+ /**
+ * Restores the subscription state on a freshly created subscription
+ * connection. Used after a connection replacement on reconnection,
+ * otherwise the new connection would silently stay unsubscribed.
+ *
+ * @returns {Promise}
+ */
+ private async restoreSubscription(): Promise {
+ const chan = this.subscription;
+
+ if (
+ !chan ||
+ !this.subscriptionName ||
+ !this.subscriptionHandlers.length
+ ) {
+ return;
+ }
+
+ const fcn = `${this.options.prefix}:${this.subscriptionName}`;
+
+ await chan.subscribe(fcn);
+
+ for (const handler of this.subscriptionHandlers) {
+ this.attachSubscriptionHandler(chan, handler);
+ }
- this.verbose(`Subscribed to ${ channel } channel`);
+ this.verbose(`Restored subscription to ${this.subscriptionName}`);
}
/**
* Closes subscription channel
*
- * @return {Promise}
+ * @returns {Promise}
*/
public async unsubscribe(): Promise {
if (this.subscription) {
@@ -360,20 +451,24 @@ export class RedisQueue extends EventEmitter
`${this.options.prefix}:${this.subscriptionName}`,
);
- this.verbose(`Unsubscribed from ${
- this.subscriptionName } channel`);
+ this.verbose(
+ `Unsubscribed from ${this.subscriptionName} channel`,
+ );
}
this.subscription.removeAllListeners();
- this.subscription.quit();
+ await this.subscription.quit().catch(error => {
+ this.verbose(`Unsubscribe quit error: ${error}`);
+ });
this.subscription.disconnect(false);
} catch (error) {
- this.verbose(`Unsubscribe error: ${ error }`);
+ this.verbose(`Unsubscribe error: ${error}`);
}
}
this.subscriptionName = undefined;
this.subscription = undefined;
+ this.subscriptionHandlers = [];
}
/**
@@ -395,14 +490,9 @@ export class RedisQueue extends EventEmitter
const jsonData = JSON.stringify(data);
const name = toName || this.name;
- await this.writer.publish(
- `${this.options.prefix}:${ name }`,
- jsonData,
- );
+ await this.writer.publish(`${this.options.prefix}:${name}`, jsonData);
- this.verbose(`Published message to ${
- name } channel, data: ${
- jsonData }
+ this.verbose(`Published message to ${name} channel, data: ${jsonData}
`);
}
@@ -413,7 +503,7 @@ export class RedisQueue extends EventEmitter
*/
public async start(): Promise {
if (!this.name) {
- throw new TypeError(`${ this.name }: No queue name provided!`);
+ throw new TypeError(`${this.name}: No queue name provided!`);
}
if (this.initialized) {
@@ -424,7 +514,6 @@ export class RedisQueue extends EventEmitter
const connPromises = [];
- // istanbul ignore next
if (!this.reader && this.isWorker()) {
this.verbose('Initializing reader...');
connPromises.push(this.connect('reader', this.options));
@@ -439,44 +528,129 @@ export class RedisQueue extends EventEmitter
this.verbose('Connections initialized');
- if (!this.signalsInitialized) {
- this.verbose('Setting up OS signal handlers...');
- // istanbul ignore next
- const free = async () => {
- let exitCode = 0;
+ RedisQueue.instances.add(this);
- setTimeout(() => {
- this.verbose(`Shutting down after ${
- IMQ_SHUTDOWN_TIMEOUT } timeout`,
- );
- process.exit(exitCode);
- }, IMQ_SHUTDOWN_TIMEOUT);
-
- try {
- if (this.watchOwner) {
- this.verbose('Freeing watcher lock...');
- await this.unlock();
- }
- } catch (err) {
- this.logger.error(err);
- exitCode = 1;
- }
- };
-
- process.on('SIGTERM', free);
- process.on('SIGINT', free);
- process.on('SIGABRT', free);
+ if (this.options.handleSignals !== false) {
+ RedisQueue.bindSignals();
+ }
- this.signalsInitialized = true;
- this.verbose('OS signal handlers initialized!');
+ if (!this.writerAcquired) {
+ RedisQueue.writerRefs[this.redisKey] =
+ (RedisQueue.writerRefs[this.redisKey] || 0) + 1;
+ this.writerAcquired = true;
}
await this.initWatcher();
+ this.startWatcherCheck();
this.initialized = true;
return this;
}
+ /**
+ * Binds process-level signal handlers once per process. On shutdown
+ * signals, frees watcher locks held by any queue instance and exits.
+ */
+ private static bindSignals(): void {
+ if (RedisQueue.signalsBound) {
+ return;
+ }
+
+ RedisQueue.signalsBound = true;
+
+ const free = (): void => {
+ void RedisQueue.freeAndExit();
+ };
+
+ process.on('SIGTERM', free);
+ process.on('SIGINT', free);
+ process.on('SIGABRT', free);
+ }
+
+ /**
+ * Frees watcher locks held by all started queue instances and exits
+ * the process, forcing exit after IMQ_SHUTDOWN_TIMEOUT at the latest.
+ *
+ * @returns {Promise}
+ */
+ private static async freeAndExit(): Promise {
+ let exitCode = 0;
+ const timer = setTimeout(() => {
+ process.exit(exitCode || 1);
+ }, IMQ_SHUTDOWN_TIMEOUT);
+
+ await Promise.all(
+ [...RedisQueue.instances]
+ .filter(queue => queue.watchOwner)
+ .map(queue =>
+ queue.unlock().catch(err => {
+ queue.logger.error(err);
+ exitCode = 1;
+ }),
+ ),
+ );
+
+ clearTimeout(timer);
+ process.exit(exitCode);
+ }
+
+ /**
+ * Starts a periodic check ensuring a watcher connection exists across
+ * the queue network, re-electing an owner when the previous one died.
+ * Also serves as a polling fallback moving due to delayed messages when
+ * keyspace notifications are unavailable.
+ */
+ private startWatcherCheck(): void {
+ if (this.watcherCheckInterval || !this.options.watcherCheckDelay) {
+ return;
+ }
+
+ this.watcherCheckInterval = setInterval(
+ this.runWatcherCheck.bind(this),
+ this.options.watcherCheckDelay,
+ );
+ this.watcherCheckInterval.unref();
+ }
+
+ /**
+ * A single watcher-existence check tick: re-elects a watcher owner when
+ * none exists and moves due to delayed messages as a keyspace-notification
+ * fallback. Errors are contained so the interval never crashes.
+ *
+ * @returns {Promise}
+ */
+ private async runWatcherCheck(): Promise {
+ if (this.watcherCheckBusy || this.destroyed || !this.writer) {
+ return;
+ }
+
+ this.watcherCheckBusy = true;
+
+ try {
+ if (!(await this.watcherCount())) {
+ await this.initWatcher();
+ }
+
+ if (this.isWorker()) {
+ await this.processDelayed(this.key);
+ }
+ } catch (err) {
+ this.verbose(`Watcher check error: ${err}`);
+ } finally {
+ this.watcherCheckBusy = false;
+ }
+ }
+
+ /**
+ * Stops the periodic watcher check
+ */
+ private stopWatcherCheck(): void {
+ if (this.watcherCheckInterval) {
+ clearInterval(this.watcherCheckInterval);
+ this.watcherCheckInterval = undefined;
+ }
+ }
+
/**
* Sends a given message to a given queue (by name)
*
@@ -496,7 +670,6 @@ export class RedisQueue extends EventEmitter
throw new TypeError('IMQ: Unable to publish in WORKER only mode!');
}
- // istanbul ignore next
if (!this.writer) {
await this.start();
}
@@ -505,51 +678,70 @@ export class RedisQueue extends EventEmitter
throw new TypeError('IMQ: unable to initialize queue!');
}
- const id = uuid();
+ const id = randomUUID();
const data: IMessage = { id, message, from: this.name };
const key = `${this.options.prefix}:${toQueue}`;
const packet = this.pack(data);
- const cb = (error: any, op: string) => {
- // istanbul ignore next
+ const onWriteError = (error: unknown, op: string): void => {
if (error) {
- this.verbose(`Writer ${ op } error: ${ error }`);
+ this.verbose(`Writer ${op} error: ${error}`);
if (errorHandler) {
- errorHandler(error as unknown as Error);
+ errorHandler(
+ error instanceof Error
+ ? error
+ : new Error(String(error)),
+ );
}
}
};
if (delay) {
- this.writer.zadd(`${key}:delayed`, Date.now() + delay, packet,
- (err: any) => {
- // istanbul ignore next
+ this.writer.zadd(
+ `${key}:delayed`,
+ Date.now() + delay,
+ packet,
+ (err?: Error | null) => {
if (err) {
- cb(err, 'ZADD');
+ onWriteError(err, 'ZADD');
return;
}
- this.writer.set(`${key}:${id}:ttl`, '', 'PX', delay, 'NX',
- (err: any) => {
- // istanbul ignore next
- if (err) {
- cb(err, 'SET');
-
- return;
- }
- },
- ).catch((err: any) => cb(err, 'SET'));
- });
+ this.writer
+ .set(
+ `${key}:${id}:ttl`,
+ '',
+ 'PX',
+ delay,
+ 'NX',
+ (err?: Error | null) => {
+ if (err) {
+ onWriteError(err, 'SET');
+
+ return;
+ }
+ },
+ )
+ .catch((err: unknown) => onWriteError(err, 'SET'));
+ },
+ );
} else {
- this.writer.lpush(key, packet, (err: any) => {
- // istanbul ignore next
- if (err) {
- cb(err, 'LPUSH');
+ const result = this.writer.lpush(
+ key,
+ packet,
+ (err?: Error | null) => {
+ if (err) {
+ onWriteError(err, 'LPUSH');
+ }
+ },
+ );
- return;
- }
- });
+ // guard against unhandled rejections from promise-returning
+ // client implementations in fire-and-forget mode
+ if (result && typeof result.catch === 'function') {
+ result.catch((err: unknown) => onWriteError(err, 'LPUSH'));
+ }
}
return id;
@@ -566,7 +758,7 @@ export class RedisQueue extends EventEmitter
if (this.reader) {
this.verbose('Destroying reader...');
- this.destroyChannel('reader', this);
+ this.destroyChannel('reader');
delete this.reader;
}
@@ -579,19 +771,37 @@ export class RedisQueue extends EventEmitter
}
/**
- * Gracefully destroys this queue
+ * Gracefully destroys this queue handle. Does not remove queue data
+ * from redis unless clearData is explicitly set to true, so that
+ * destroying one handle (e.g., on scale-down) never wipes messages
+ * still pending for other producers/consumers.
*
+ * @param {boolean} [clearData] - when true, also clears queue data
* @returns {Promise}
*/
@profile()
- public async destroy(): Promise {
+ public async destroy(clearData: boolean = false): Promise {
this.verbose('Destroying queue...');
this.destroyed = true;
+ RedisQueue.instances.delete(this);
this.removeAllListeners();
this.cleanSafeCheckInterval();
- this.destroyWatcher();
+ this.stopWatcherCheck();
+
+ if (this.watchOwner) {
+ await this.unlock().catch(err =>
+ this.verbose(`Unlock error: ${err}`),
+ );
+ this.destroyWatcher();
+ this.watchOwner = false;
+ }
+
await this.stop();
- await this.clear();
+
+ if (clearData) {
+ await this.clear();
+ }
+
this.destroyWriter();
await this.unsubscribe();
this.verbose('Queue destroyed!');
@@ -613,16 +823,16 @@ export class RedisQueue extends EventEmitter
await Promise.all([
this.writer.del(this.key),
- this.writer.del(`${ this.key }:delayed`),
+ this.writer.del(`${this.key}:delayed`),
]);
this.verbose('Expired queue keys cleared!');
} catch (err) {
- // istanbul ignore next
if (this.initialized) {
this.logger.error(
- `${ context.name }: error clearing the redis queue host ${
- this.redisKey } on writer, pid ${ process.pid }:`,
+ `${this.name}: error clearing the redis queue host ${
+ this.redisKey
+ } on writer, pid ${process.pid}:`,
err,
);
}
@@ -648,7 +858,7 @@ export class RedisQueue extends EventEmitter
/**
* Returns true if publisher mode is enabled on this queue, false otherwise.
*
- * @return {boolean}
+ * @returns {boolean}
*/
public isPublisher(): boolean {
return this.mode === IMQMode.BOTH || this.mode === IMQMode.PUBLISHER;
@@ -657,13 +867,25 @@ export class RedisQueue extends EventEmitter
/**
* Returns true if worker mode is enabled on this queue, false otherwise.
*
- * @return {boolean}
+ * @returns {boolean}
*/
public isWorker(): boolean {
return this.mode === IMQMode.BOTH || this.mode === IMQMode.WORKER;
}
- // noinspection JSMethodCanBeStatic
+ /**
+ * Returns false only when this queue is known to be unable to accept
+ * writes right now — i.e., it has a writer connection currently
+ * in a non-ready (reconnecting/closed) state. A queue that has not yet
+ * connected is considered available, since a sending lazily connects it.
+ * Used for health-aware routing in the clustered queue.
+ *
+ * @returns {boolean}
+ */
+ public get available(): boolean {
+ return !this.writer || this.writer.status === 'ready';
+ }
+
/**
* Writer connection associated with this queue instance
*
@@ -673,13 +895,11 @@ export class RedisQueue extends EventEmitter
return RedisQueue.writers[this.redisKey];
}
- // noinspection JSUnusedLocalSymbols
/**
* Writer connection setter.
*
* @param {IRedisClient} conn
*/
- // noinspection JSUnusedLocalSymbols,JSUnusedLocalSymbols
private set writer(conn: IRedisClient) {
RedisQueue.writers[this.redisKey] = conn;
}
@@ -693,41 +913,83 @@ export class RedisQueue extends EventEmitter
return RedisQueue.watchers[this.redisKey];
}
- // noinspection JSUnusedLocalSymbols
/**
* Watcher setter sets the watcher connection property for this
* queue instance
*
* @param {IRedisClient} conn
*/
- // noinspection JSUnusedLocalSymbols
private set watcher(conn: IRedisClient) {
RedisQueue.watchers[this.redisKey] = conn;
}
+ /**
+ * Returns the connection currently bound to the given channel, if any.
+ *
+ * @param {RedisConnectionChannel} channel
+ * @returns {IRedisClient | undefined}
+ */
+ private connectionOf(
+ channel: RedisConnectionChannel,
+ ): IRedisClient | undefined {
+ switch (channel) {
+ case 'reader':
+ return this.reader;
+ case 'writer':
+ return this.writer;
+ case 'watcher':
+ return this.watcher;
+ case 'subscription':
+ return this.subscription;
+ }
+ }
+
+ /**
+ * Binds the given connection to the given channel.
+ *
+ * @param {RedisConnectionChannel} channel
+ * @param {IRedisClient} conn
+ */
+ private bindConnection(
+ channel: RedisConnectionChannel,
+ conn: IRedisClient,
+ ): void {
+ switch (channel) {
+ case 'reader':
+ this.reader = conn;
+ break;
+ case 'writer':
+ this.writer = conn;
+ break;
+ case 'watcher':
+ this.watcher = conn;
+ break;
+ case 'subscription':
+ this.subscription = conn;
+ break;
+ }
+ }
+
/**
* Logger instance associated with the current queue instance
* @type {ILogger}
*/
private get logger(): ILogger {
- // istanbul ignore next
return this.options.logger || console;
}
/**
* Return a lock key for watcher connection
*
- * @access private
* @returns {string}
*/
private get lockKey(): string {
- return `${ this.options.prefix }:watch:lock`;
+ return `${this.options.prefix}:watch:lock`;
}
/**
* Returns current queue key
*
- * @access private
* @returns {string}
*/
private get key(): string {
@@ -736,14 +998,12 @@ export class RedisQueue extends EventEmitter
/**
* Destroys watcher channel
- *
- * @access private
*/
@profile()
private destroyWatcher(): void {
if (this.watcher) {
this.verbose('Destroying watcher...');
- this.destroyChannel('watcher', this);
+ this.destroyChannel('watcher');
delete RedisQueue.watchers[this.redisKey];
this.verbose('Watcher destroyed!');
}
@@ -751,14 +1011,28 @@ export class RedisQueue extends EventEmitter
/**
* Destroys writer channel
- *
- * @access private
*/
@profile()
- private destroyWriter(): void {
+ private destroyWriter(release: boolean = true): void {
+ if (release && this.writerAcquired) {
+ this.writerAcquired = false;
+ RedisQueue.writerRefs[this.redisKey] = Math.max(
+ 0,
+ (RedisQueue.writerRefs[this.redisKey] || 1) - 1,
+ );
+
+ if (RedisQueue.writerRefs[this.redisKey] > 0) {
+ // the shared writer connection is still used by other
+ // queue instances within this process
+ this.verbose('Writer is still in use, skipping destroy...');
+
+ return;
+ }
+ }
+
if (this.writer) {
this.verbose('Destroying writer...');
- this.destroyChannel('writer', this);
+ this.destroyChannel('writer');
delete RedisQueue.writers[this.redisKey];
this.verbose('Writer destroyed!');
}
@@ -766,66 +1040,82 @@ export class RedisQueue extends EventEmitter
/**
* Destroys any channel
- *
- * @access private
*/
@profile()
- private destroyChannel(
- channel: RedisConnectionChannel,
- context: RedisQueue = this,
- ): void {
- const client = context[channel];
+ private destroyChannel(channel: RedisConnectionChannel): void {
+ const client = this.connectionOf(channel);
- if (client) {
- try {
- client.removeAllListeners();
- client.quit().then(() => {
+ if (!client) {
+ return;
+ }
+
+ try {
+ client.removeAllListeners();
+
+ let disconnected = false;
+ const forceDisconnect = (): void => {
+ if (disconnected) {
+ return;
+ }
+
+ disconnected = true;
+
+ try {
client.disconnect(false);
- }).catch(e => {
- this.verbose(`Error quitting ${ channel }: ${ e }`);
- });
- } catch (error) {
- this.verbose(`Error destroying ${ channel }: ${ error }`);
- }
+ } catch (error) {
+ this.verbose(`Error disconnecting ${channel}: ${error}`);
+ }
+ };
+
+ // graceful quit for idle connections, but a reader blocked on an
+ // infinite BRPOP/BLMOVE never lets QUIT through, so guarantee a
+ // forced disconnect after a short grace period to avoid leaking
+ // the socket (which would keep the process alive)
+ client.quit().then(forceDisconnect, forceDisconnect);
+
+ const timer = setTimeout(
+ forceDisconnect,
+ IMQ_CONNECTION_QUIT_TIMEOUT,
+ );
+
+ // the grace timer itself must not keep the process alive; while
+ // the leaked socket keeps the loop running the timer still fires
+ timer.unref();
+ } catch (error) {
+ this.verbose(`Error destroying ${channel}: ${error}`);
}
}
/**
* Establishes a given connection channel by its name
*
- * @access private
* @param {RedisConnectionChannel} channel
* @param {IMQOptions} options
- * @param {any} context
* @returns {Promise}
*/
private async connect(
channel: RedisConnectionChannel,
options: IMQOptions,
- context: RedisQueue = this,
): Promise {
- this.verbose(`Connecting to ${ channel } channel...`);
+ this.verbose(`Connecting to ${channel} channel...`);
+
+ const existing = this.connectionOf(channel);
- // istanbul ignore next
- if (context[channel]) {
- return context[channel];
+ if (existing) {
+ return existing;
}
- const redis = new Redis({
- // istanbul ignore next
+ const redis: IRedisClient = new Redis({
port: options.port || 6379,
- // istanbul ignore next
host: options.host || 'localhost',
- // istanbul ignore next
username: options.username,
- // istanbul ignore next
password: options.password,
connectionName: this.getChannelName(
- context.name + '',
+ this.name,
options.prefix || '',
channel,
),
- retryStrategy: this.retryStrategy(),
+ retryStrategy: noRetryStrategy,
autoResubscribe: true,
enableOfflineQueue: true,
autoResendUnfulfilledCommands: true,
@@ -835,8 +1125,8 @@ export class RedisQueue extends EventEmitter
lazyConnect: true,
});
- context[channel] = redis;
- context[channel].__imq = true;
+ this.bindConnection(channel, redis);
+ redis.__imq = true;
for (const event of [
'wait',
@@ -845,42 +1135,39 @@ export class RedisQueue extends EventEmitter
'connect',
'close',
]) {
- redis.on(event, () => {
- context.verbose(`Redis Event fired: ${ event }`);
- });
+ redis.on(event, () => this.verbose(`Redis Event fired: ${event}`));
}
redis.setMaxListeners(IMQ_REDIS_MAX_LISTENERS_LIMIT);
- redis.on('error', this.onErrorHandler(context, channel));
- redis.on('end', this.onCloseHandler(context, channel));
+ redis.on('error', this.onErrorHandler(channel));
+ redis.on('end', this.onCloseHandler(channel));
await redis.connect();
this.logger.info(
'%s: %s channel connected, host %s, pid %s',
- context.name, channel, this.redisKey, process.pid,
+ this.name,
+ channel,
+ this.redisKey,
+ process.pid,
);
switch (channel) {
- case 'reader': this.read(); break;
- case 'writer': await this.processDelayed(this.key); break;
- case 'watcher': await this.initWatcher(); break;
+ case 'reader':
+ this.read();
+ break;
+ case 'writer':
+ await this.processDelayed(this.key);
+ break;
+ case 'watcher':
+ await this.initWatcher();
+ break;
+ case 'subscription':
+ await this.restoreSubscription();
+ break;
}
- return context[channel];
- }
-
- // istanbul ignore next
- /**
- * Builds and returns redis reconnection strategy
- *
- * @returns {() => (number | void | null)}
- * @private
- */
- private retryStrategy(): () => null {
- return () => {
- return null;
- };
+ return redis;
}
/**
@@ -891,184 +1178,185 @@ export class RedisQueue extends EventEmitter
* @private
*/
private scheduleReconnect(channel: RedisConnectionChannel): void {
- if (this.destroyed) {
- return;
- }
-
- if (this.reconnecting[channel]) {
+ if (this.destroyed || this.reconnecting[channel]) {
return;
}
const attempts = (this.reconnectAttempts[channel] || 0) + 1;
- const delay = Math.min(30000, 1000 * Math.pow(2, attempts - 1));
+ const delayMs = Math.min(
+ RECONNECT_MAX_DELAY,
+ RECONNECT_BASE_DELAY * 2 ** (attempts - 1),
+ );
this.reconnecting[channel] = true;
this.reconnectAttempts[channel] = attempts;
- this.verbose(`Scheduling ${ channel } reconnect in ${
- delay } ms (attempt ${ attempts })`);
+ this.verbose(
+ `Scheduling ${channel} reconnect in ${delayMs} ms ` +
+ `(attempt ${attempts})`,
+ );
if (this.reconnectTimers[channel]) {
- clearTimeout(this.reconnectTimers[channel] as any);
+ clearTimeout(this.reconnectTimers[channel]);
}
- this.reconnectTimers[channel] = setTimeout(async () => {
- if (this.destroyed) {
- this.reconnecting[channel] = false;
-
- return;
- }
+ this.reconnectTimers[channel] = setTimeout(
+ this.reconnectNow.bind(this, channel),
+ delayMs,
+ );
+ }
- try {
- switch (channel) {
- case 'watcher':
- this.destroyWatcher();
- break;
- case 'writer':
- this.destroyWriter();
- break;
- case 'reader':
- this.destroyChannel(channel, this);
- this.reader = undefined;
+ /**
+ * Performs a single reconnection attempt for the given channel,
+ * rescheduling itself on failure. Errors are handled internally, so the
+ * scheduled timer never produces an unhandled rejection.
+ *
+ * @param {RedisConnectionChannel} channel
+ * @returns {Promise}
+ */
+ private async reconnectNow(channel: RedisConnectionChannel): Promise {
+ if (this.destroyed) {
+ this.reconnecting[channel] = false;
- break;
- case 'subscription':
- this.destroyChannel(channel, this);
- this.subscription = undefined;
+ return;
+ }
- break;
- }
+ try {
+ switch (channel) {
+ case 'watcher':
+ this.destroyWatcher();
+ break;
+ case 'writer':
+ // replace the broken shared connection without
+ // releasing this instance's reference to it
+ this.destroyWriter(false);
+ break;
+ case 'reader':
+ this.destroyChannel(channel);
+ this.reader = undefined;
+ break;
+ case 'subscription':
+ this.destroyChannel(channel);
+ this.subscription = undefined;
+ break;
+ }
- await this.connect(channel, this.options);
- this.reconnectAttempts[channel] = 0;
- this.reconnecting[channel] = false;
+ await this.connect(channel, this.options);
+ this.reconnectAttempts[channel] = 0;
+ this.reconnecting[channel] = false;
- if (this.reconnectTimers[channel]) {
- clearTimeout(this.reconnectTimers[channel] as any);
- this.reconnectTimers[channel] = undefined;
- }
-
- this.verbose(`Reconnected ${ channel } channel`);
- } catch (err) {
- this.reconnecting[channel] = false;
- this.verbose(`Reconnect ${ channel } failed: ${ err }`);
- this.scheduleReconnect(channel);
+ if (this.reconnectTimers[channel]) {
+ clearTimeout(this.reconnectTimers[channel]);
+ this.reconnectTimers[channel] = undefined;
}
- }, delay);
+
+ this.verbose(`Reconnected ${channel} channel`);
+ } catch (err) {
+ this.reconnecting[channel] = false;
+ this.verbose(`Reconnect ${channel} failed: ${err}`);
+ this.scheduleReconnect(channel);
+ }
}
- // noinspection JSMethodCanBeStatic
/**
* Generates channel name
*
* @param {string} contextName
* @param {string} prefix
* @param {RedisConnectionChannel} name
- * @return {string}
+ * @returns {string}
*/
private getChannelName(
contextName: string,
prefix: string,
name: RedisConnectionChannel,
): string {
- const uniqueSuffix = `pid:${ process.pid }:host:${ os.hostname() }`;
+ const uniqueSuffix = `pid:${process.pid}:host:${hostname()}`;
- return`${ prefix }:${ contextName }:${ name }:${ uniqueSuffix }`;
+ return `${prefix}:${contextName}:${name}:${uniqueSuffix}`;
}
/**
* Builds and returns connection error handler
*
- * @access private
- * @param {RedisQueue} context
* @param {RedisConnectionChannel} channel
- * @return {(err: Error) => void}
+ * @returns {(err: Error) => void}
*/
private onErrorHandler(
- context: RedisQueue,
channel: RedisConnectionChannel,
): (error: Error) => void {
- // istanbul ignore next
- return ((error: Error & { code: string }) => {
- this.verbose(`Redis Error: ${ error }`);
+ return (error: Error & { code?: string }) => {
+ this.verbose(`Redis Error: ${error}`);
if (this.destroyed) {
return;
}
this.logger.error(
- `${context.name}: error connecting redis host ${
- this.redisKey} on ${
- channel}, pid ${process.pid}:`,
+ `${this.name}: error connecting redis host ${
+ this.redisKey
+ } on ${channel}, pid ${process.pid}:`,
error,
);
if (
error.code === 'ECONNREFUSED' ||
error.code === 'ETIMEDOUT' ||
- context[channel]?.status !== 'ready'
+ this.connectionOf(channel)?.status !== 'ready'
) {
this.scheduleReconnect(channel);
}
- });
+ };
}
/**
* Builds and returns redis connection close handler
*
- * @access private
- * @param {RedisQueue} context
* @param {RedisConnectionChannel} channel
- * @return {(...args: any[]) => any}
+ * @returns {() => void}
*/
- private onCloseHandler(
- context: RedisQueue,
- channel: RedisConnectionChannel,
- ): (...args: any[]) => any {
- this.verbose(`Redis ${ channel } is closing...`);
+ private onCloseHandler(channel: RedisConnectionChannel): () => void {
+ this.verbose(`Redis ${channel} is closing...`);
- // istanbul ignore next
- return (() => {
+ return () => {
this.initialized = false;
this.logger.warn(
'%s: redis connection %s closed on host %s, pid %s!',
- context.name, channel, this.redisKey, process.pid,
+ this.name,
+ channel,
+ this.redisKey,
+ process.pid,
);
if (!this.destroyed) {
this.scheduleReconnect(channel);
}
- });
+ };
}
/**
* Processes given redis-queue message
*
- * @access private
- * @param {[any, any]} msg
+ * @param {[string, string]} msg
* @returns {RedisQueue}
*/
- private process(msg: [any, any]): RedisQueue {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ private process(msg: [string, string]): RedisQueue {
const [queue, data] = msg;
- // istanbul ignore next
if (!queue || queue !== this.key) {
return this;
}
try {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-argument
- const { id, message, from } = this.unpack(data);
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+ const { id, message, from } = this.unpack(data) as IMessage;
+
this.emit('message', message, id, from);
} catch (err) {
- // istanbul ignore next
this.emitError(
'OnMessage',
'process error - message is invalid',
- err as unknown as Error,
+ err,
);
}
@@ -1078,19 +1366,17 @@ export class RedisQueue extends EventEmitter
/**
* Returns the number of established watcher connections on redis
*
- * @access private
* @returns {Promise}
*/
- // istanbul ignore next
private async watcherCount(): Promise {
if (!this.writer) {
return 0;
}
const rx = new RegExp(
- `\\bname=${this.options.prefix}:[\\S]+?:watcher:`,
+ `\\bname=${escapeRegExp(this.options.prefix || '')}:\\S+?:watcher:`,
);
- const list = await this.writer.client('LIST');
+ const list = (await this.writer.client('LIST')) as string;
if (!list || !list.split) {
return 0;
@@ -1102,33 +1388,52 @@ export class RedisQueue extends EventEmitter
/**
* Processes delayed a message by its given redis key
*
- * @access private
* @param {string} key
* @returns {Promise}
*/
private async processDelayed(key: string): Promise {
try {
- if (this.scripts.moveDelayed.checksum) {
+ if (!this.scripts.moveDelayed.checksum || !this.writer) {
+ return;
+ }
+
+ try {
await this.writer.evalsha(
this.scripts.moveDelayed.checksum,
- 2, `${ key }:delayed`, key, Date.now(),
+ 2,
+ `${key}:delayed`,
+ key,
+ Date.now(),
);
+ } catch (err) {
+ // the script may not be cached on the redis host (fresh
+ // host, restart, non-owner instance) - fall back to EVAL,
+ // which caches it as a side effect
+ if (err instanceof Error && /NOSCRIPT/.test(err.message)) {
+ await this.writer.eval(
+ this.scripts.moveDelayed.code,
+ 2,
+ `${key}:delayed`,
+ key,
+ Date.now(),
+ );
+ } else {
+ throw err;
+ }
}
} catch (err) {
this.emitError(
'OnProcessDelayed',
'error processing delayed queue',
- err as unknown as Error,
+ err,
);
}
}
- // istanbul ignore next
/**
* Watch routine
*
- * @access private
- * @return {Promise}
+ * @returns {Promise}
*/
private async processWatch(): Promise {
const now = Date.now();
@@ -1136,17 +1441,15 @@ export class RedisQueue extends EventEmitter
while (true) {
try {
- const data = await this.writer.scan(
+ const [next, keys] = await this.writer.scan(
cursor,
'MATCH',
- `${ this.options.prefix }:*:worker:*`,
+ `${this.options.prefix}:*:worker:*`,
'COUNT',
- '1000',
+ SCAN_COUNT,
);
- cursor = data.shift() as string;
-
- const keys = data.shift() as string[] || [];
+ cursor = next;
await this.processKeys(keys, now);
@@ -1157,7 +1460,7 @@ export class RedisQueue extends EventEmitter
this.emitError(
'OnSafeDelivery',
'safe queue message delivery problem',
- err as unknown as Error,
+ err,
);
this.cleanSafeCheckInterval();
@@ -1166,42 +1469,49 @@ export class RedisQueue extends EventEmitter
}
}
- // istanbul ignore next
/**
* Process given keys from a message queue
*
- * @access private
* @param {string[]} keys
* @param {number} now
- * @return {Promise}
+ * @returns {Promise}
*/
private async processKeys(keys: string[], now: number): Promise {
if (!keys.length) {
return;
}
- this.verbose(`Watching ${ keys.length } keys: ${
- keys.map(key => `"${ key }"`).join(', ')
- }`);
+ this.verbose(
+ `Watching ${keys.length} keys: ${keys
+ .map(key => `"${key}"`)
+ .join(', ')}`,
+ );
for (const key of keys) {
const kp: string[] = key.split(':');
- if (Number(kp.pop()) < now) {
+ // the last key segment is the worker's lease deadline: only
+ // re-queue messages of workers whose lease has expired (the
+ // worker died mid-processing); fresh leases belong to live
+ // workers and must not be touched
+ if (Number(kp.pop()) >= now) {
continue;
}
- await this.writer.rpoplpush(key, `${ kp.shift() }:${ kp.shift() }`);
+ await this.writer.lmove(
+ key,
+ `${kp.shift()}:${kp.shift()}`,
+ 'RIGHT',
+ 'LEFT',
+ );
}
}
- // istanbul ignore next
/**
* Watch message processor
*
- * @access private
* @param {...any[]} args
- * @return {Promise}
+ * @returns {Promise}
*/
private async onWatchMessage(...args: any[]): Promise {
try {
@@ -1215,23 +1525,16 @@ export class RedisQueue extends EventEmitter
await this.processDelayed(key.join(':'));
} catch (err) {
- this.emitError(
- 'OnWatch',
- 'watch error',
- err as unknown as Error,
- );
+ this.emitError('OnWatch', 'watch error', err);
}
}
- // istanbul ignore next
/**
* Clears safe check interval
- *
- * @access private
*/
private cleanSafeCheckInterval(): void {
if (this.safeCheckInterval) {
- clearInterval(this.safeCheckInterval as number);
+ clearInterval(this.safeCheckInterval);
delete this.safeCheckInterval;
}
}
@@ -1239,64 +1542,41 @@ export class RedisQueue extends EventEmitter
/**
* Setups watch a process on delayed messages
*
- * @access private
* @returns {RedisQueue}
*/
- // istanbul ignore next
private watch(): RedisQueue {
if (!this.writer || !this.watcher || this.watcher.__ready__) {
return this;
}
try {
- this.writer.config(
- 'SET',
- 'notify-keyspace-events',
- 'Ex',
- ).catch(err => {
- this.emitError(
- 'OnConfig',
- 'events config error',
- err as unknown as Error,
+ this.writer
+ .config('SET', 'notify-keyspace-events', 'Ex')
+ .catch((err: unknown) =>
+ this.emitError('OnConfig', 'events config error', err),
);
- });
} catch (err) {
- this.emitError(
- 'OnConfig',
- 'events config error',
- err as unknown as Error,
- );
+ this.emitError('OnConfig', 'events config error', err);
}
- this.watcher.on(
- 'pmessage',
- this.onWatchMessage.bind(this) as unknown as () => void,
- );
- this.watcher.psubscribe(
- '__keyevent@0__:expired',
- `${ this.options.prefix }:delayed:*`,
- ).catch(err => {
- this.verbose(`Error subscribing to watcher channel: ${ err }`);
- });
+ this.watcher.on('pmessage', this.onWatchMessage.bind(this));
+ this.watcher
+ .psubscribe(
+ '__keyevent@0__:expired',
+ `${this.options.prefix}:delayed:*`,
+ )
+ .catch((err: unknown) =>
+ this.verbose(`Error subscribing to watcher channel: ${err}`),
+ );
// watch for expired unhandled safe queues
- if (!this.safeCheckInterval) {
- if (this.options.safeDeliveryTtl != null) {
- this.safeCheckInterval = setInterval(
- (async (): Promise => {
- if (!this.writer) {
- this.cleanSafeCheckInterval();
-
- return ;
- }
-
- if (this.options.safeDelivery) {
- await this.processWatch();
- }
-
- await this.processCleanup();
- }) as unknown as () => void, this.options.safeDeliveryTtl);
- }
+ if (!this.safeCheckInterval && this.options.safeDeliveryTtl != null) {
+ this.safeCheckInterval = setInterval(
+ this.runSafeCheck.bind(this),
+ this.options.safeDeliveryTtl,
+ );
+ // maintenance timer must not keep the process alive on its own
+ this.safeCheckInterval.unref();
}
this.watcher.__ready__ = true;
@@ -1304,10 +1584,29 @@ export class RedisQueue extends EventEmitter
return this;
}
+ /**
+ * A single safe-delivery maintenance tick: recovers messages from dead
+ * workers (when safe delivery is on) and prunes orphaned keys.
+ *
+ * @returns {Promise}
+ */
+ private async runSafeCheck(): Promise {
+ if (!this.writer) {
+ this.cleanSafeCheckInterval();
+
+ return;
+ }
+
+ if (this.options.safeDelivery) {
+ await this.processWatch();
+ }
+
+ await this.processCleanup();
+ }
+
/**
* Cleans up orphaned keys from redis
*
- * @access private
* @returns {Promise}
*/
private async processCleanup(): Promise {
@@ -1319,54 +1618,70 @@ export class RedisQueue extends EventEmitter
}
const filter: RegExp = new RegExp(
- this.options.prefix + ':' +
- (this.options.cleanupFilter || '*').replace(/\*/g, '.*'),
+ escapeRegExp(this.options.prefix || '') +
+ ':' +
+ escapeRegExp(this.options.cleanupFilter || '*').replace(
+ /\\\*/g,
+ '.*',
+ ),
'i',
);
- this.verbose(`Cleaning up keys matching ${ filter }`);
+ this.verbose(`Cleaning up keys matching ${filter}`);
- const clients: string = (await this.writer.client(
- 'LIST',
- ) as string).toString() || '';
+ const clients: string =
+ ((await this.writer.client('LIST')) as string).toString() || '';
const connectedKeys = (clients.match(RX_CLIENT_NAME) || [])
- .filter((name: string) =>
- RX_CLIENT_TEST.test(name) && filter.test(name),
+ .filter(
+ (name: string) =>
+ RX_CLIENT_TEST.test(name) && filter.test(name),
)
- .map((name: string) => name
- .replace(/^name=/, '')
- .replace(RX_CLIENT_CLEAN, ''),
+ .map((name: string) =>
+ name.replace(/^name=/, '').replace(RX_CLIENT_CLEAN, ''),
)
- .filter((name: string, i: number, a: string[]) =>
- a.indexOf(name) === i,
+ .filter(
+ (name: string, i: number, a: string[]) =>
+ a.indexOf(name) === i,
);
+ // clients seen connected during the previous sweep get one
+ // sweep of grace: a client merely reconnecting (the
+ // backoff can reach tens of seconds) must not have any keys
+ // deleted from under it
+ const knownKeys = connectedKeys.concat(
+ this.lastConnectedKeys.filter(
+ key => !connectedKeys.includes(key),
+ ),
+ );
+
+ this.lastConnectedKeys = connectedKeys;
+
const keysToRemove: string[] = [];
let cursor = '0';
- this.verbose(`Found connected keys: ${
- connectedKeys.map(k => `"${ k }"`).join(', ')
- }`);
+ this.verbose(
+ `Found connected keys: ${knownKeys
+ .map(k => `"${k}"`)
+ .join(', ')}`,
+ );
while (true) {
- const data = await this.writer.scan(
+ const [next, keys] = await this.writer.scan(
cursor,
'MATCH',
- `${
- this.options.prefix}:${
+ `${this.options.prefix}:${
this.options.cleanupFilter || '*'
}`,
'COUNT',
- '1000',
+ SCAN_COUNT,
);
- cursor = data.shift() as string;
-
- const keys = data.shift() as string[] || [];
+ cursor = next;
keysToRemove.push(
...keys.filter(
- key => key !== this.lockKey &&
- connectedKeys.every(
+ key =>
+ key !== this.lockKey &&
+ knownKeys.every(
connectedKey =>
key.indexOf(connectedKey) === -1,
),
@@ -1374,16 +1689,18 @@ export class RedisQueue extends EventEmitter
);
if (cursor === '0') {
- break ;
+ break;
}
}
if (keysToRemove.length) {
await this.writer.del(...keysToRemove);
- this.verbose(`Keys ${
- keysToRemove.map(k => `"${ k }"`).join(', ')
- } were successfully removed!`);
+ this.verbose(
+ `Keys ${keysToRemove
+ .map(k => `"${k}"`)
+ .join(', ')} were successfully removed!`,
+ );
}
} catch (err) {
this.logger.warn('Clean-up error occurred:', err);
@@ -1392,7 +1709,6 @@ export class RedisQueue extends EventEmitter
return this;
}
- // noinspection JSUnusedLocalSymbols
/**
* Unreliable but fast way of message handling by the queue
*/
@@ -1412,75 +1728,83 @@ export class RedisQueue extends EventEmitter
this.process(msg);
}
} catch (err) {
- // istanbul ignore next
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
- if (err.message.match(/Stream connection ended/)) {
+ // a closed/ended reader connection means the queue is
+ // stopping, reconnecting, or being destroyed - end the
+ // loop quietly; reconnection (if any) is driven by the
+ // connection close handler, not by this loop
+ if (
+ this.destroyed ||
+ !this.reader ||
+ (err instanceof Error &&
+ /Stream connection ended|Connection is closed/i.test(
+ err.message,
+ ))
+ ) {
break;
}
- // istanbul ignore next
- // noinspection ExceptionCaughtLocallyJS
throw err;
}
}
} catch (err) {
- // istanbul ignore next
- this.emitError(
- 'OnReadUnsafe',
- 'unsafe reader failed',
- err as unknown as Error,
- );
+ this.emitError('OnReadUnsafe', 'unsafe reader failed', err);
}
}
- // noinspection JSUnusedLocalSymbols
/**
- * Reliable but slow method of message handling by message queue
+ * Reliable but slow method of message handling by message queue.
+ *
+ * Uses a bounded blocking pop so the lease deadline embedded into the
+ * worker key never goes stale: with an infinite block a message
+ * arriving long after the pop started would be born with an already
+ * expired lease and be immediately re-queued by the watcher.
*/
private async readSafe(): Promise {
- try {
- const key = this.key;
-
- while (true) {
- const expire: number = Date.now() +
- Number(this.options.safeDeliveryTtl);
- const workerKey = `${key}:worker:${uuid()}:${expire}`;
+ const key = this.key;
+ // blocking timeout in seconds, at most half of the lease ttl, so
+ // a claimed message always has at least half the ttl remaining
+ const timeout = Math.max(
+ 0.1,
+ Number(this.options.safeDeliveryTtl) / 2000,
+ );
- if (!this.reader || !this.writer) {
- break;
- }
+ while (true) {
+ if (!this.reader || !this.writer || this.destroyed) {
+ break;
+ }
- try {
- await this.reader.brpoplpush(this.key, workerKey, 0);
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- } catch (_) {
- // istanbul ignore next
- break;
- }
+ const expire: number =
+ Date.now() + Number(this.options.safeDeliveryTtl);
+ const workerKey = `${key}:worker:${randomUUID()}:${expire}`;
+ let msg: string | null;
- const msgArr: any = await this.writer.lrange(
- workerKey, -1, 1,
+ try {
+ msg = await this.reader.blmove(
+ this.key,
+ workerKey,
+ 'RIGHT',
+ 'LEFT',
+ timeout,
);
+ } catch {
+ // reader connection ended (stop/reconnect)
+ break;
+ }
- if (!msgArr || msgArr?.length !== 1) {
- // noinspection ExceptionCaughtLocallyJS
- throw new Error('Wrong messages count');
- }
-
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- const [msg] = msgArr as any[];
+ if (msg === null || msg === undefined) {
+ // blocking pop timed out: regenerate the lease and retry
+ continue;
+ }
+ try {
this.process([key, msg]);
- this.writer.del(workerKey).catch(e =>
- this.logger.warn('OnReadSafe: del error', e));
+ this.writer
+ .del(workerKey)
+ .catch(e => this.logger.warn('OnReadSafe: del error', e));
+ } catch (err) {
+ // a single message failure must never kill the read loop
+ this.emitError('OnReadSafe', 'safe reader failed', err);
}
- } catch (err) {
- // istanbul ignore next
- this.emitError(
- 'OnReadSafe',
- 'safe reader failed',
- err as unknown as Error,
- );
}
}
@@ -1490,22 +1814,21 @@ export class RedisQueue extends EventEmitter
* @returns {RedisQueue}
*/
private read(): RedisQueue {
- // istanbul ignore next
if (!this.reader) {
this.logger.error(
`${this.name}: reader connection is not initialized, pid ${
- process.pid} on redis host ${this.redisKey}!`,
+ process.pid
+ } on redis host ${this.redisKey}!`,
);
return this;
}
- const readMethod = this.options.safeDelivery
- ? 'readSafe'
- : 'readUnsafe';
+ const runReader = this.options.safeDelivery
+ ? this.readSafe
+ : this.readUnsafe;
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
- process.nextTick(this[readMethod].bind(this));
+ process.nextTick(runReader.bind(this));
return this;
}
@@ -1513,7 +1836,6 @@ export class RedisQueue extends EventEmitter
/**
* Checks if the watcher connection is locked
*
- * @access private
* @returns {Promise}
*/
private async isLocked(): Promise {
@@ -1527,7 +1849,6 @@ export class RedisQueue extends EventEmitter
/**
* Locks watcher connection
*
- * @access private
* @returns {Promise}
*/
private async lock(): Promise {
@@ -1541,7 +1862,6 @@ export class RedisQueue extends EventEmitter
/**
* Unlocks watcher connection
*
- * @access private
* @returns {Promise}
*/
private async unlock(): Promise {
@@ -1552,25 +1872,34 @@ export class RedisQueue extends EventEmitter
return false;
}
- // istanbul ignore next
/**
* Emits error
*
- * @access private
* @param {string} eventName
* @param {string} message
- * @param {Error} err
+ * @param {unknown} err
*/
- private emitError(eventName: string, message: string, err: Error): void {
- this.emit('error', err, eventName);
+ private emitError(eventName: string, message: string, err: unknown): void {
+ const error = err instanceof Error ? err : new Error(String(err));
+
+ // emitting 'error' with no listeners attached would throw and
+ // crash the process from a background routine - always log, but
+ // only emit when someone actually listens
+ if (this.listenerCount('error') > 0) {
+ this.emit('error', error, eventName);
+ }
+
this.logger.error(
- `${this.name}: ${ message }, pid ${
- process.pid } on redis host ${ this.redisKey }:`,
+ `${this.name}: ${message}, pid ${
+ process.pid
+ } on redis host ${this.redisKey}:`,
err,
);
- this.verbose(`Error in event ${
- eventName }: ${ message }, pid ${
- process.pid } on redis host ${ this.redisKey }: ${ err }`);
+ this.verbose(
+ `Error in event ${eventName}: ${message}, pid ${
+ process.pid
+ } on redis host ${this.redisKey}: ${err}`,
+ );
}
/**
@@ -1578,7 +1907,6 @@ export class RedisQueue extends EventEmitter
*
* @returns {Promise}
*/
- // istanbul ignore next
private async ownWatch(): Promise {
const owned = await this.lock();
@@ -1587,14 +1915,13 @@ export class RedisQueue extends EventEmitter
for (const script of Object.keys(this.scripts)) {
try {
- const checksum = sha1(this.scripts[script].code);
-
- this.scripts[script].checksum = checksum;
+ // checksums are pre-computed at construction time
+ const checksum = this.scripts[script].checksum as string;
- const scriptExists = await this.writer.script(
+ const scriptExists = (await this.writer.script(
'EXISTS',
checksum,
- ) as number[];
+ )) as number[];
const loaded = (scriptExists || []).shift();
if (!loaded) {
@@ -1604,11 +1931,7 @@ export class RedisQueue extends EventEmitter
);
}
} catch (err) {
- this.emitError(
- 'OnScriptLoad',
- 'script load error',
- err as unknown as Error,
- );
+ this.emitError('OnScriptLoad', 'script load error', err);
}
}
@@ -1618,33 +1941,20 @@ export class RedisQueue extends EventEmitter
}
}
- // istanbul ignore next
/**
- * This method returns a watcher lock resolver function
+ * Attempts to take over an orphaned watcher lock: if the lock is held
+ * but no watcher connection is actually alive, releases and re-acquires
+ * ownership. Used to resolve a possible watcher deadlock.
*
- * @access private
- * @param {(...args: any[]) => void} resolve
- * @param {(...args: any[]) => void} reject
- * @return {() => Promise}
+ * @returns {Promise}
*/
- private watchLockResolver(
- resolve: (...args: any[]) => void,
- reject: (...args: any[]) => void,
- ): () => Promise {
- return (async () => {
- try {
- const noWatcher = !await this.watcherCount();
-
- if (await this.isLocked() && noWatcher) {
- await this.unlock();
- await this.ownWatch();
- }
+ private async resolveWatchLock(): Promise {
+ const noWatcher = !(await this.watcherCount());
- resolve();
- } catch (err) {
- reject(err);
- }
- });
+ if ((await this.isLocked()) && noWatcher) {
+ await this.unlock();
+ await this.ownWatch();
+ }
}
/**
@@ -1653,47 +1963,34 @@ export class RedisQueue extends EventEmitter
*
* @returns {Promise}
*/
- // istanbul ignore next
private async initWatcher(): Promise {
- return new Promise(
- (async (
- resolve: (...args: any[]) => void,
- reject: (...args: any[]) => void,
- ): Promise => {
- try {
- if (!await this.watcherCount()) {
- this.verbose('Initializing watcher...');
-
- await this.ownWatch();
-
- if (this.watchOwner && this.watcher) {
- resolve();
- } else {
- // check for possible deadlock to resolve
- setTimeout(
- this.watchLockResolver(
- resolve,
- reject,
- ) as unknown as () => void,
- intrand(1, 50),
- );
- }
- } else {
- resolve();
- }
- } catch (err) {
- this.logger.error(
- `${ this.name }: error initializing watcher, pid ${
- process.pid } on redis host ${ this.redisKey }`,
- err,
- );
+ try {
+ if (await this.watcherCount()) {
+ return;
+ }
- reject(err);
- }
- }) as unknown as (
- resolve: (...args: any[]) => void,
- reject: (...args: any[]) => void,
- ) => void,
- );
+ this.verbose('Initializing watcher...');
+
+ await this.ownWatch();
+
+ if (this.watchOwner && this.watcher) {
+ return;
+ }
+
+ // another instance may hold the lock while its watcher died:
+ // wait a small random interval (to avoid a thundering herd) and
+ // try to resolve the possible deadlock
+ await delay(randomInt(1, 50));
+ await this.resolveWatchLock();
+ } catch (err) {
+ this.logger.error(
+ `${this.name}: error initializing watcher, pid ${
+ process.pid
+ } on redis host ${this.redisKey}`,
+ err,
+ );
+
+ throw err;
+ }
}
}
diff --git a/src/UDPClusterManager.ts b/src/UDPClusterManager.ts
index 73bb159..b67f222 100644
--- a/src/UDPClusterManager.ts
+++ b/src/UDPClusterManager.ts
@@ -21,11 +21,17 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
+import { join } from 'node:path';
+import { Worker } from 'node:worker_threads';
import { ClusterManager, ICluster } from './ClusterManager';
-import { Worker } from 'worker_threads';
-import * as path from 'path';
+import { ILogger, IServerInput } from './IMessageQueue';
-process.setMaxListeners(10000);
+/** Shape of a message posted from the UDP worker thread */
+interface WorkerMessage {
+ type?: string;
+ server?: unknown;
+ error?: string;
+}
export interface UDPClusterManagerOptions {
/**
@@ -44,7 +50,7 @@ export interface UDPClusterManagerOptions {
address: string;
/**
- * Message queue limited broadcast address
+ * Message-queue-limited broadcast address
*
* @default "255.255.255.255"
* @type {string}
@@ -70,130 +76,349 @@ export interface UDPClusterManagerOptions {
* @type {boolean}
*/
useAliveCheck: boolean;
+
+ /**
+ * Enable process signal handling (SIGTERM, SIGINT, SIGABRT) by the
+ * manager. When enabled, the manager stops its UDP workers on these
+ * signals and then re-raises the signal, so the process terminates
+ * through the default signal behavior. Disable if the host application
+ * manages its own shutdown sequence.
+ *
+ * @default true
+ * @type {boolean}
+ */
+ handleSignals: boolean;
+
+ /**
+ * Logger used for worker supervision messages
+ *
+ * @default console
+ * @type {ILogger}
+ */
+ logger: ILogger;
}
+/**
+ * Subset of the manager options handed over to the UDP worker thread. Only
+ * structured-cloneable values may appear here (no logger).
+ */
+export type UDPWorkerOptions = Omit<
+ UDPClusterManagerOptions,
+ 'logger' | 'handleSignals'
+>;
+
const IMQ_UDP_CLUSTER_MANAGER_ALIVE_CHECK = !!+(
process.env.IMQ_UDP_CLUSTER_MANAGER_ALIVE_CHECK || 1
);
+/** Delay (ms) before an unexpectedly dead worker is re-spawned */
+const WORKER_RESPAWN_DELAY = 1000;
+
export const DEFAULT_UDP_CLUSTER_MANAGER_OPTIONS: UDPClusterManagerOptions = {
port: 63000,
address: '255.255.255.255',
aliveTimeoutCorrection: 5000,
useAliveCheck: IMQ_UDP_CLUSTER_MANAGER_ALIVE_CHECK,
+ handleSignals: true,
+ logger: console,
};
export class UDPClusterManager extends ClusterManager {
private static workers: Record = {};
- public static sockets: Record = {};
+
+ /** Number of manager instances sharing each worker (by worker key) */
+ private static workerRefs: Record = {};
+
+ /** Live manager instances per worker key, used for re-attachment */
+ private static instances: Record> = {};
+
+ /** True once process-level signal handlers have been registered */
+ private static signalsBound: boolean = false;
+
+ /** True while the process is shutting down via a signal */
+ private static shuttingDown: boolean = false;
+
+ /**
+ * Workers we terminated on purpose. worker.terminate() makes the 'exit'
+ * event fire with a non-zero code, which would otherwise be mistaken for
+ * a crash and trigger a spurious "exited unexpectedly" warning and respawn.
+ */
+ private static intentionallyStopped: WeakSet = new WeakSet();
+
private readonly options: UDPClusterManagerOptions;
- private workerKey: string;
- private worker: Worker;
+ private workerKey!: string;
+ private worker!: Worker;
+ private destroyed: boolean = false;
constructor(options?: Partial) {
super();
this.options = {
...DEFAULT_UDP_CLUSTER_MANAGER_OPTIONS,
- ...options || {},
+ ...options,
};
this.startWorkerListener();
- process.on('SIGTERM', UDPClusterManager.free);
- process.on('SIGINT', UDPClusterManager.free);
- process.on('SIGABRT', UDPClusterManager.free);
+ if (this.options.handleSignals) {
+ UDPClusterManager.bindSignals();
+ }
+ }
+
+ private get logger(): ILogger {
+ return this.options.logger;
+ }
+
+ /**
+ * Builds the shared-worker key from every option the worker thread
+ * depends on, so managers configured differently never silently share
+ * a worker built from another manager's options.
+ *
+ * @param {UDPClusterManagerOptions} options
+ * @returns {string}
+ */
+ private static workerKeyFor(options: UDPClusterManagerOptions): string {
+ return [
+ options.address,
+ options.port,
+ options.limitedAddress ?? '',
+ options.aliveTimeoutCorrection,
+ options.useAliveCheck,
+ ].join('|');
+ }
+
+ /**
+ * Registers process-level shutdown handlers exactly once per process.
+ * After stopping the workers, the original signal is re-raised, so the
+ * default termination behavior (which registering a handler cancels)
+ * still applies, and the process exits.
+ */
+ private static bindSignals(): void {
+ if (UDPClusterManager.signalsBound) {
+ return;
+ }
+
+ UDPClusterManager.signalsBound = true;
+
+ const onSignal = (signal: NodeJS.Signals): void => {
+ void UDPClusterManager.freeAndRaise(signal);
+ };
+
+ process.once('SIGTERM', onSignal);
+ process.once('SIGINT', onSignal);
+ process.once('SIGABRT', onSignal);
+ }
+
+ /**
+ * Stops all workers and re-raises the given signal, so the process
+ * terminates through the default signal behavior.
+ *
+ * @param {NodeJS.Signals} signal
+ * @returns {Promise}
+ */
+ private static async freeAndRaise(signal: NodeJS.Signals): Promise {
+ await UDPClusterManager.free();
+ // the once-registered handler is already removed at this point, so
+ // re-raising hits the default handler and terminates the process
+ process.kill(process.pid, signal);
}
private static async free(): Promise {
+ UDPClusterManager.shuttingDown = true;
+
const workerKeys = Object.keys(UDPClusterManager.workers);
- await Promise.all(workerKeys.map(
- workerKey => UDPClusterManager.destroyWorker(
- workerKey,
- UDPClusterManager.workers[workerKey],
- )),
+ await Promise.all(
+ workerKeys.map(workerKey =>
+ UDPClusterManager.destroyWorker(
+ workerKey,
+ UDPClusterManager.workers[workerKey],
+ ),
+ ),
);
}
+ /**
+ * Registers this instance on the (possibly shared) worker for its
+ * options. Every instance attaches its own message listener, so all
+ * managers sharing a worker receive cluster updates.
+ */
private startWorkerListener(): void {
- this.workerKey = `${ this.options.address }:${ this.options.port }`;
+ this.workerKey = UDPClusterManager.workerKeyFor(this.options);
- if (UDPClusterManager.workers[this.workerKey]) {
- this.worker = UDPClusterManager.workers[this.workerKey];
+ UDPClusterManager.workerRefs[this.workerKey] =
+ (UDPClusterManager.workerRefs[this.workerKey] || 0) + 1;
+ (UDPClusterManager.instances[this.workerKey] ??= new Set()).add(this);
- return;
- }
+ this.worker =
+ UDPClusterManager.workers[this.workerKey] || this.spawnWorker();
+ this.worker.on('message', this.onWorkerMessage);
+ }
- this.worker = new Worker(path.join(__dirname, './UDPWorker.js'), {
- workerData: this.options,
+ /**
+ * Spawns and supervises a UDP worker for this manager's options. On an
+ * unexpected worker death the worker is dropped from the registry, and
+ * a re-spawn is scheduled while live manager instances remain.
+ *
+ * @returns {Worker}
+ */
+ private spawnWorker(): Worker {
+ const workerData: UDPWorkerOptions = {
+ port: this.options.port,
+ address: this.options.address,
+ limitedAddress: this.options.limitedAddress,
+ aliveTimeoutCorrection: this.options.aliveTimeoutCorrection,
+ useAliveCheck: this.options.useAliveCheck,
+ };
+ const worker = new Worker(join(__dirname, './UDPWorker.js'), {
+ workerData,
});
- this.worker.on('message', message => {
- const [className, method] = message.type?.split(':');
+ const workerKey = this.workerKey;
- if (className !== 'cluster') {
- return;
- }
+ // many manager instances may listen on one shared worker
+ worker.setMaxListeners(0);
- return this.anyCluster(cluster => {
- if (method === 'add') {
- try {
- const existing = typeof (cluster as any).find === 'function'
- ? (cluster as any).find(message.server, true)
- : undefined;
- if (existing) {
- return;
- }
- } catch {/* ignore */}
- }
+ worker.on('error', err => {
+ this.logger.error(
+ `UDPClusterManager: worker ${workerKey} error:`,
+ err,
+ );
+ });
+ worker.on('exit', code => {
+ if (UDPClusterManager.workers[workerKey] === worker) {
+ delete UDPClusterManager.workers[workerKey];
+ }
- const clusterMethod = (cluster as any)[method as keyof ICluster];
+ // an exit we caused via terminate() (graceful destroy/shutdown)
+ // is expected, even though terminate() reports a non-zero code
+ if (UDPClusterManager.intentionallyStopped.has(worker)) {
+ UDPClusterManager.intentionallyStopped.delete(worker);
- if (!clusterMethod) {
- return;
- }
+ return;
+ }
- clusterMethod(message.server);
- });
+ if (code !== 0 && !UDPClusterManager.shuttingDown) {
+ this.logger.warn(
+ `UDPClusterManager: worker ${workerKey} exited ` +
+ `unexpectedly (code ${code})`,
+ );
+ UDPClusterManager.respawn(workerKey);
+ }
});
- UDPClusterManager.workers[this.workerKey] = this.worker;
- }
+ UDPClusterManager.workers[this.workerKey] = worker;
- public async destroy(): Promise {
- await UDPClusterManager.destroyWorker(this.workerKey, this.worker);
+ return worker;
}
- public static async destroySocket(key: string, socket?: any): Promise {
- if (!socket) {
+ /**
+ * Schedules a replacement worker for the given key and re-attaches all
+ * live manager instances to it, so cluster membership does not silently
+ * freeze after a worker crash.
+ *
+ * @param {string} workerKey
+ */
+ private static respawn(workerKey: string): void {
+ const instances = UDPClusterManager.instances[workerKey];
+
+ if (UDPClusterManager.shuttingDown || !instances?.size) {
return;
}
- try {
- if (typeof socket.removeAllListeners === 'function') {
- socket.removeAllListeners();
+ const timer = setTimeout(() => {
+ const [first] = instances;
+
+ if (
+ !first ||
+ UDPClusterManager.shuttingDown ||
+ UDPClusterManager.workers[workerKey]
+ ) {
+ return;
+ }
+
+ const worker = first.spawnWorker();
+
+ for (const instance of instances) {
+ instance.worker = worker;
+ worker.on('message', instance.onWorkerMessage);
}
- } catch (e) {
- throw e;
+ }, WORKER_RESPAWN_DELAY);
+
+ timer.unref();
+ }
+
+ /**
+ * Bound per-instance worker message listener, kept as a field so it can
+ * be detached from the shared worker when this instance is destroyed.
+ */
+ private readonly onWorkerMessage = (message: WorkerMessage): void => {
+ void this.handleWorkerMessage(message);
+ };
+
+ /**
+ * Applies a worker cluster message (add/remove) to every registered
+ * cluster. Cluster callback errors are contained per cluster, so the
+ * worker message listener can never raise an unhandled rejection.
+ *
+ * @param {WorkerMessage} message
+ * @returns {Promise}
+ */
+ private async handleWorkerMessage(message: WorkerMessage): Promise {
+ if (message.type === 'error') {
+ this.logger.warn(
+ `UDPClusterManager: worker socket error: ${message.error}`,
+ );
+
+ return;
}
- if (typeof socket.close !== 'function') {
+ const [className, method] = String(message.type ?? '').split(':');
+
+ if (className !== 'cluster') {
return;
}
- await new Promise((resolve) => {
- socket.close(() => {
- if (typeof socket.unref === 'function') {
- socket.unref();
- }
- if (UDPClusterManager.sockets && key in UDPClusterManager.sockets) {
- delete UDPClusterManager.sockets[key];
- }
- resolve();
- });
+ const action = method as keyof ICluster;
+
+ await this.forEachCluster(cluster => {
+ const server = message.server as IServerInput;
+
+ if (action === 'add' && cluster.find(server)) {
+ return;
+ }
+
+ const handler = cluster[action] as
+ | ((server: IServerInput) => unknown)
+ | undefined;
+
+ handler?.(server);
});
}
+ public async destroy(): Promise {
+ if (this.destroyed) {
+ return;
+ }
+
+ this.destroyed = true;
+ this.worker.off('message', this.onWorkerMessage);
+ UDPClusterManager.instances[this.workerKey]?.delete(this);
+
+ const refs = UDPClusterManager.workerRefs[this.workerKey] ?? 0;
+
+ if (refs > 1) {
+ // the worker is still shared with other manager instances
+ // configured the same way — just release this reference
+ UDPClusterManager.workerRefs[this.workerKey] = refs - 1;
+
+ return;
+ }
+
+ delete UDPClusterManager.workerRefs[this.workerKey];
+ delete UDPClusterManager.instances[this.workerKey];
+ await UDPClusterManager.destroyWorker(this.workerKey, this.worker);
+ }
+
private static async destroyWorker(
workerKey: string,
worker?: Worker,
@@ -203,22 +428,32 @@ export class UDPClusterManager extends ClusterManager {
}
return new Promise(resolve => {
- const timeout = setTimeout(() => {
+ const finish = (): void => {
+ worker.off('message', onMessage);
+ clearTimeout(timeout);
+ // mark before terminating: the resulting non-zero exit is
+ // intentional and must not be treated as a crash
+ UDPClusterManager.intentionallyStopped.add(worker);
worker.terminate();
- resolve();
- }, 5000);
-
- worker.postMessage({ type: 'stop' });
- worker.once('message', (message) => {
- if (message.type === 'stopped') {
- clearTimeout(timeout);
- worker.terminate();
+ if (UDPClusterManager.workers[workerKey] === worker) {
delete UDPClusterManager.workers[workerKey];
+ }
- resolve();
+ resolve();
+ };
+ // a persistent, filtering listener: unrelated cluster messages
+ // arriving between the stop request and the stop confirmation
+ // must not consume the wait
+ const onMessage = (message: WorkerMessage): void => {
+ if (message.type === 'stopped') {
+ finish();
}
- });
+ };
+ const timeout = setTimeout(finish, 5000);
+
+ worker.on('message', onMessage);
+ worker.postMessage({ type: 'stop' });
});
}
}
diff --git a/src/UDPWorker.ts b/src/UDPWorker.ts
index da83b06..13a7644 100644
--- a/src/UDPWorker.ts
+++ b/src/UDPWorker.ts
@@ -27,13 +27,11 @@ import {
parentPort,
workerData,
MessagePort,
-} from 'worker_threads';
-import { createSocket, Socket } from 'dgram';
-import { networkInterfaces } from 'os';
-import { UDPClusterManagerOptions } from './UDPClusterManager';
-import { uuid } from './uuid';
-
-process.setMaxListeners(10000);
+} from 'node:worker_threads';
+import { createSocket, Socket } from 'node:dgram';
+import { networkInterfaces } from 'node:os';
+import { randomUUID } from 'node:crypto';
+import { UDPWorkerOptions } from './UDPClusterManager';
enum MessageType {
Up = 'up',
@@ -49,25 +47,43 @@ interface Message {
timeout: number;
}
-class UDPWorker {
+export class UDPWorker {
private readonly socket: Socket;
private readonly servers = new Map();
constructor(
- private readonly options: UDPClusterManagerOptions,
+ private readonly options: UDPWorkerOptions,
private readonly messagePort: MessagePort,
) {
this.setupMessageHandlers();
- this.setupProcessHandlers();
this.socket = createSocket({
type: 'udp4',
reuseAddr: true,
reusePort: true,
- }).bind(this.options.port, this.selectNetworkInterface());
- this.socket.on(
- 'message',
- message => this.processMessage(this.parseMessage(message)),
- );
+ });
+ // surface socket failures (bind errors, network errors) to the
+ // main thread instead of crashing the whole process with an
+ // unhandled 'error' event
+ this.socket.on('error', err => {
+ this.messagePort.postMessage({
+ type: 'error',
+ error: err.message,
+ });
+ });
+ this.socket.on('message', input => {
+ // a malformed datagram on the broadcast port must never crash
+ // the worker (and, through it, the host process)
+ try {
+ const message = this.parseMessage(input);
+
+ if (message) {
+ this.processMessage(message);
+ }
+ } catch {
+ // ignore broken datagrams
+ }
+ });
+ this.socket.bind(this.options.port, this.selectNetworkInterface());
}
private static getServerKey(message: Message): string {
@@ -82,12 +98,6 @@ class UDPWorker {
});
}
- private setupProcessHandlers(): void {
- process.on('SIGTERM', this.cleanup);
- process.on('SIGINT', this.cleanup);
- process.on('SIGABRT', this.cleanup);
- }
-
private addServer(message: Message): void {
this.messagePort.postMessage({
type: 'cluster:add',
@@ -119,24 +129,25 @@ class UDPWorker {
}
private serverAliveWait(message: Message): void {
- const stamp = uuid();
+ const stamp = randomUUID();
const correction = this.options.aliveTimeoutCorrection ?? 0;
const effectiveTimeout = message.timeout + correction + 1;
const key = UDPWorker.getServerKey(message);
this.servers.set(key, stamp);
- const t: any = setTimeout(() => setImmediate(() => {
- if (this.servers.get(key) === stamp) {
- this.removeServer(message);
- }
- }), effectiveTimeout);
- // Avoid keeping the event loop alive due to pending timers
- try {
- if (t && typeof t.unref === 'function') {
- t.unref();
- }
- } catch {/* ignore */}
+ const timer: NodeJS.Timeout = setTimeout(
+ () =>
+ setImmediate(() => {
+ if (this.servers.get(key) === stamp) {
+ this.removeServer(message);
+ }
+ }),
+ effectiveTimeout,
+ );
+
+ // a pending liveness timer must not keep the worker alive on its own
+ timer.unref();
}
private processMessage(message: Message): void {
@@ -151,13 +162,13 @@ class UDPWorker {
private selectNetworkInterface(): string {
const interfaces = networkInterfaces();
- const broadcastAddress = this.options.address
- || this.options.limitedAddress;
+ const broadcastAddress =
+ this.options.address || this.options.limitedAddress;
const defaultAddress = '0.0.0.0';
if (
- !broadcastAddress
- || broadcastAddress === this.options.limitedAddress
+ !broadcastAddress ||
+ broadcastAddress === this.options.limitedAddress
) {
return defaultAddress;
}
@@ -168,8 +179,9 @@ class UDPWorker {
}
for (const net of interfaces[key]) {
- const shouldBeSelected = net.family === 'IPv4'
- && net.address.startsWith(
+ const shouldBeSelected =
+ net.family === 'IPv4' &&
+ net.address.startsWith(
broadcastAddress.replace(/\.255/g, ''),
);
@@ -182,23 +194,44 @@ class UDPWorker {
return defaultAddress;
}
- private parseMessage(input: Buffer): Message {
- const [
- name,
- id,
- type,
- address = '',
- timeout = '0',
- ] = input.toString().split('\t');
- const [host, port] = address.split(':');
+ /**
+ * Parses a raw broadcast datagram into a message. Returns null for
+ * malformed input (missing fields, non-numeric port, or timeout), so
+ * garbage on the broadcast port is dropped instead of producing
+ * NaN-driven timers or crashes.
+ *
+ * @param {Buffer} input
+ * @returns {Message | null}
+ */
+ private parseMessage(input: Buffer): Message | null {
+ const [name, id, type, address = '', timeout = '0'] = input
+ .toString()
+ .split('\t');
+ const [host, port = ''] = address.split(':');
+ const portNumber = parseInt(port, 10);
+ const timeoutMs = parseFloat(timeout) * 1000;
+
+ if (
+ !name ||
+ !id ||
+ !type ||
+ !host ||
+ !Number.isFinite(portNumber) ||
+ portNumber <= 0 ||
+ portNumber > 65535 ||
+ !Number.isFinite(timeoutMs) ||
+ timeoutMs < 0
+ ) {
+ return null;
+ }
return {
id,
name,
type: type.toLowerCase() as MessageType,
host,
- port: parseInt(port),
- timeout: parseFloat(timeout) * 1000,
+ port: portNumber,
+ timeout: timeoutMs,
};
}
@@ -220,7 +253,9 @@ class UDPWorker {
this.servers.clear();
if (this.socket) {
- this.socket.removeAllListeners();
+ // keep the 'error' listener attached: a socket error during
+ // close must not crash the worker as an unhandled 'error' event
+ this.socket.removeAllListeners('message');
}
}
}
diff --git a/src/helpers/buildOptions.ts b/src/helpers/buildOptions.ts
new file mode 100644
index 0000000..3b78a8d
--- /dev/null
+++ b/src/helpers/buildOptions.ts
@@ -0,0 +1,38 @@
+/*!
+ * Helper: buildOptions
+ *
+ * I'm Queue Software Project
+ * Copyright (C) 2025 imqueue.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * If you want to use this code in a closed source (commercial) project, you can
+ * purchase a proprietary commercial license. Please contact us at
+ * to get commercial licensing options.
+ */
+/**
+ * Safely builds full options definition using default options and
+ * partial given options
+ *
+ * @template T
+ * @param {T} defaultOptions
+ * @param {Partial} givenOptions
+ * @returns {T}
+ */
+export function buildOptions(
+ defaultOptions: T,
+ givenOptions?: Partial,
+): T {
+ return Object.assign({}, defaultOptions, givenOptions);
+}
diff --git a/src/copyEventEmitter.ts b/src/helpers/copyEventEmitter.ts
similarity index 86%
rename from src/copyEventEmitter.ts
rename to src/helpers/copyEventEmitter.ts
index 2d4559f..526266f 100644
--- a/src/copyEventEmitter.ts
+++ b/src/helpers/copyEventEmitter.ts
@@ -21,8 +21,8 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
-import { EventEmitter } from 'events';
-import * as util from 'util';
+import { EventEmitter } from 'node:events';
+import { inspect } from 'node:util';
export function copyEventEmitter(
source: EventEmitter & {
@@ -39,9 +39,9 @@ export function copyEventEmitter(
const listeners = source.rawListeners(event) as any[];
for (const originalListener of listeners) {
- if (util.inspect(originalListener).includes('onceWrapper')) {
- const realListener = originalListener?.listener
- || originalListener;
+ if (inspect(originalListener).includes('onceWrapper')) {
+ const realListener =
+ originalListener?.listener || originalListener;
target.once(event, realListener);
} else {
diff --git a/src/helpers/envInt.ts b/src/helpers/envInt.ts
new file mode 100644
index 0000000..4ba82b1
--- /dev/null
+++ b/src/helpers/envInt.ts
@@ -0,0 +1,36 @@
+/*!
+ * Helper: envInt
+ *
+ * I'm Queue Software Project
+ * Copyright (C) 2025 imqueue.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * If you want to use this code in a closed source (commercial) project, you can
+ * purchase a proprietary commercial license. Please contact us at
+ * to get commercial licensing options.
+ */
+/**
+ * Reads integer value from environment variable, falling back to the
+ * given default on missing or non-numeric values
+ *
+ * @param {string} name
+ * @param {number} defaultValue
+ * @returns {number}
+ */
+export function envInt(name: string, defaultValue: number): number {
+ const value = Number(process.env[name] || defaultValue);
+
+ return isNaN(value) ? defaultValue : value;
+}
diff --git a/src/helpers/escapeRegExp.ts b/src/helpers/escapeRegExp.ts
new file mode 100644
index 0000000..05f5bf1
--- /dev/null
+++ b/src/helpers/escapeRegExp.ts
@@ -0,0 +1,32 @@
+/*!
+ * Helper: escapeRegExp
+ *
+ * I'm Queue Software Project
+ * Copyright (C) 2025 imqueue.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * If you want to use this code in a closed source (commercial) project, you can
+ * purchase a proprietary commercial license. Please contact us at
+ * to get commercial licensing options.
+ */
+/**
+ * Escapes regular expression special characters in a given string
+ *
+ * @param {string} str
+ * @returns {string}
+ */
+export function escapeRegExp(str: string): string {
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
diff --git a/src/helpers/index.ts b/src/helpers/index.ts
new file mode 100644
index 0000000..9b6fb2f
--- /dev/null
+++ b/src/helpers/index.ts
@@ -0,0 +1,31 @@
+/*!
+ * IMQ helper functions
+ *
+ * I'm Queue Software Project
+ * Copyright (C) 2025 imqueue.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * If you want to use this code in a closed source (commercial) project, you can
+ * purchase a proprietary commercial license. Please contact us at
+ * to get commercial licensing options.
+ */
+export * from './buildOptions';
+export * from './sha1';
+export * from './randomInt';
+export * from './pack';
+export * from './unpack';
+export * from './escapeRegExp';
+export * from './copyEventEmitter';
+export * from './envInt';
diff --git a/src/helpers/pack.ts b/src/helpers/pack.ts
new file mode 100644
index 0000000..24a45b4
--- /dev/null
+++ b/src/helpers/pack.ts
@@ -0,0 +1,34 @@
+/*!
+ * Helper: pack
+ *
+ * I'm Queue Software Project
+ * Copyright (C) 2025 imqueue.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * If you want to use this code in a closed source (commercial) project, you can
+ * purchase a proprietary commercial license. Please contact us at
+ * to get commercial licensing options.
+ */
+import { gzipSync } from 'node:zlib';
+
+/**
+ * Compresses the given data and returns a binary string
+ *
+ * @param {unknown} data - data to compress
+ * @returns {string} - compressed data as a binary string
+ */
+export function pack(data: unknown): string {
+ return gzipSync(JSON.stringify(data)).toString('binary');
+}
diff --git a/src/helpers/randomInt.ts b/src/helpers/randomInt.ts
new file mode 100644
index 0000000..1546a71
--- /dev/null
+++ b/src/helpers/randomInt.ts
@@ -0,0 +1,33 @@
+/*!
+ * Helper: intrand
+ *
+ * I'm Queue Software Project
+ * Copyright (C) 2025 imqueue.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * If you want to use this code in a closed source (commercial) project, you can
+ * purchase a proprietary commercial license. Please contact us at
+ * to get commercial licensing options.
+ */
+/**
+ * Returns random integer between given min and max
+ *
+ * @param {number} min
+ * @param {number} max
+ * @returns {number}
+ */
+export function randomInt(min: number, max: number): number {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+}
diff --git a/src/helpers/sha1.ts b/src/helpers/sha1.ts
new file mode 100644
index 0000000..0ca22eb
--- /dev/null
+++ b/src/helpers/sha1.ts
@@ -0,0 +1,38 @@
+/*!
+ * Helper: sha1
+ *
+ * I'm Queue Software Project
+ * Copyright (C) 2025 imqueue.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * If you want to use this code in a closed source (commercial) project, you can
+ * purchase a proprietary commercial license. Please contact us at
+ * to get commercial licensing options.
+ */
+import { createHash, Hash } from 'node:crypto';
+
+/**
+ * Returns SHA1 hash sum of the given string
+ *
+ * @param {string} str
+ * @returns {string}
+ */
+export function sha1(str: string): string {
+ const sha: Hash = createHash('sha1');
+
+ sha.update(str);
+
+ return sha.digest('hex');
+}
diff --git a/src/helpers/unpack.ts b/src/helpers/unpack.ts
new file mode 100644
index 0000000..573cd68
--- /dev/null
+++ b/src/helpers/unpack.ts
@@ -0,0 +1,36 @@
+/*!
+ * Helper: unpack
+ *
+ * I'm Queue Software Project
+ * Copyright (C) 2025 imqueue.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * If you want to use this code in a closed source (commercial) project, you can
+ * purchase a proprietary commercial license. Please contact us at
+ * to get commercial licensing options.
+ */
+import { gunzipSync } from 'node:zlib';
+
+/**
+ * Decompresses a binary string and returns the decompressed data
+ *
+ * @param {string} data - compressed data string
+ * @returns {unknown} - decompressed data
+ */
+export function unpack(data: string): unknown {
+ return JSON.parse(
+ gunzipSync(Buffer.from(data, 'binary')).toString(),
+ ) as unknown;
+}
diff --git a/src/index.ts b/src/index.ts
index c5c1b5f..c4f6406 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -19,31 +19,10 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
-// noinspection JSUnusedGlobalSymbols
-/**
- * Safely builds full options definition using default options and
- * partial given options
- *
- * @template T
- * @param {T} defaultOptions
- * @param {Partial} givenOptions
- * @return {T}
- */
-// istanbul ignore next
-export function buildOptions(
- defaultOptions: T,
- givenOptions?: Partial,
-): T {
- return Object.assign({}, defaultOptions, givenOptions);
-}
-
-export * from './IMQMode';
export * from './profile';
-export * from './uuid';
-export * from './promisify';
export * from './redis';
+export * from './IMQMode';
export * from './IMessageQueue';
export * from './RedisQueue';
export * from './ClusteredRedisQueue';
export * from './UDPClusterManager';
-export * from './copyEventEmitter';
diff --git a/src/profile.ts b/src/profile.ts
index 2a6c783..ae4eb69 100644
--- a/src/profile.ts
+++ b/src/profile.ts
@@ -21,11 +21,9 @@
* purchase a proprietary commercial license. Please contact us at
* to get commercial licensing options.
*/
-import 'reflect-metadata';
import { ILogger } from '.';
export enum LogLevel {
- // noinspection JSUnusedGlobalSymbols
LOG = 'log',
INFO = 'info',
WARN = 'warn',
@@ -42,19 +40,19 @@ export interface ProfileDecoratorOptions {
*/
enableDebugArgs?: boolean;
/**
- * Defines log/level for logger
- * By default is log
+ * Defines log/level for logger,
+ * By default, is a log
*/
logLevel?: LogLevel;
}
/**
- * Checks if log level is set to proper value or returns default one
+ * Validates a log level value or returns the default
*
- * @param {*} level
- * @return {LogLevel}
+ * @param {unknown} level - the level to validate
+ * @returns {LogLevel} - validated log level or INFO if invalid
*/
-export function verifyLogLevel(level: any): LogLevel {
+export function verifyLogLevel(level: unknown): LogLevel {
switch (level) {
case LogLevel.LOG:
case LogLevel.INFO:
@@ -75,40 +73,38 @@ const DEFAULT_OPTIONS: ProfileDecoratorOptions = {
export type AllowedTimeFormat = 'microseconds' | 'milliseconds' | 'seconds';
/**
- * Environment variable IMQ_LOG_TIME=[1, 0] - enables/disables profiled
- * timings logging
+ * Environment variable IMQ_LOG_TIME=[1, 0]. Enable or disable profiled
+ * timing logging.
*
* @type {boolean}
*/
-export const IMQ_LOG_TIME = !!+(process.env.IMQ_LOG_TIME || 0);
+export const IMQ_LOG_TIME: boolean = !!+(process.env.IMQ_LOG_TIME || 0);
/**
- * Environment variable IMQ_LOG_ARGS=[1, 0] - enables/disables profiled
- * call arguments to be logged
+ * Environment variable IMQ_LOG_ARGS=[1, 0]. Enable or disable logging of
+ * profiled call arguments.
*
* @type {boolean}
*/
-export const IMQ_LOG_ARGS = !!+(process.env.IMQ_LOG_ARGS || 0);
+export const IMQ_LOG_ARGS: boolean = !!+(process.env.IMQ_LOG_ARGS || 0);
/**
- * Environment variable IMQ_LOG_TIME_FORMAT=[
- * 'microseconds',
- * 'milliseconds',
- * 'seconds'
- * ]. Specifies profiled time logging format, by default is 'microseconds'
+ * Environment variable IMQ_LOG_TIME_FORMAT. Specifies the format for profiled
+ * time logging. Values: 'microseconds', 'milliseconds', 'seconds'.
+ * Default: 'microseconds'
*
* @type {AllowedTimeFormat | string}
*/
export const IMQ_LOG_TIME_FORMAT: AllowedTimeFormat =
- process.env.IMQ_LOG_TIME_FORMAT as AllowedTimeFormat || 'microseconds';
+ (process.env.IMQ_LOG_TIME_FORMAT as AllowedTimeFormat) || 'microseconds';
export interface DebugInfoOptions {
/**
- * Turns on/off time debugging
+ * Enable or disable execution time debugging
*/
debugTime: boolean;
/**
- * Turns on/off args debugging
+ * Enable or disable call arguments debugging
*/
debugArgs: boolean;
/**
@@ -118,19 +114,19 @@ export interface DebugInfoOptions {
/**
* Call arguments
*/
- args: any[];
+ args: unknown[];
/**
* Method name
*/
methodName: string;
/**
- * Execution start timestamp
+ * Execution start timestamp (from process.hrtime.bigint or milliseconds)
*/
- start: any;
+ start: bigint | number;
/**
- * Logger implementation
+ * Logger implementation (absent when the target carries no logger)
*/
- logger: ILogger;
+ logger?: ILogger;
/**
* Log level to use for the call
*/
@@ -138,16 +134,9 @@ export interface DebugInfoOptions {
}
/**
- * Prints debug information
+ * Logs debug information about a function call
*
- * @param {boolean} debugTime
- * @param {boolean} debugArgs
- * @param {string} className
- * @param {any[]} args
- * @param {string} methodName
- * @param {number} start
- * @param {ILogger} logger
- * @param {LogLevel} logLevel
+ * @param {DebugInfoOptions} options - debug information options
*/
export function logDebugInfo({
debugTime,
@@ -159,18 +148,15 @@ export function logDebugInfo({
logger,
logLevel,
}: DebugInfoOptions) {
- const log = logger && typeof logger[logLevel] === 'function'
- ? logger[logLevel].bind(logger) : undefined;
+ const log =
+ logger && typeof logger[logLevel] === 'function'
+ ? logger[logLevel].bind(logger)
+ : undefined;
if (debugTime) {
- // noinspection TypeScriptUnresolvedFunction
- const time = parseInt(
- ((process.hrtime as any).bigint() - BigInt(start)) as any,
- 10,
- ) / 1000;
+ const time = Number(process.hrtime.bigint() - BigInt(start)) / 1000;
let timeStr: string;
- // istanbul ignore next
switch (IMQ_LOG_TIME_FORMAT) {
case 'milliseconds':
timeStr = (time / 1000).toFixed(3) + ' ms';
@@ -190,26 +176,30 @@ export function logDebugInfo({
if (debugArgs) {
let argStr: string = '';
- const cache: any[] = [];
+ const cache: unknown[] = [];
try {
- argStr = JSON.stringify(args, (key: string, value: any) => {
- if (typeof value === 'object' && value !== null) {
- if (~cache.indexOf(value)) {
- try {
- return JSON.parse(JSON.stringify(value));
- } catch (error) {
- return;
+ argStr = JSON.stringify(
+ args,
+ (_key: string, value: unknown) => {
+ if (typeof value === 'object' && value !== null) {
+ if (~cache.indexOf(value)) {
+ try {
+ return JSON.parse(JSON.stringify(value));
+ } catch {
+ return;
+ }
}
- }
- cache.push(value);
- }
+ cache.push(value);
+ }
- return value;
- }, 2);
+ return value;
+ },
+ 2,
+ );
} catch (err) {
- logger.error(err);
+ logger?.error(err);
}
if (log) {
@@ -218,6 +208,59 @@ export function logDebugInfo({
}
}
+/**
+ * Resolves the class name of a decorated call target — the class itself for
+ * a static call, or the instance's constructor for an instance call.
+ *
+ * @param {unknown} target
+ * @returns {string}
+ */
+function resolveClassName(target: unknown): string {
+ if (typeof target === 'function') {
+ return target.name;
+ }
+
+ if (typeof target === 'object' && target !== null) {
+ return (
+ (target as { constructor?: { name?: string } }).constructor?.name ??
+ ''
+ );
+ }
+
+ return '';
+}
+
+/**
+ * Extracts the logger instance from a decorated target, if present.
+ *
+ * @param {unknown} target - the decorated target object
+ * @returns {ILogger | undefined} - the logger instance or undefined
+ */
+function resolveLogger(target: unknown): ILogger | undefined {
+ if (typeof target === 'object' && target !== null) {
+ return (target as { logger?: ILogger }).logger;
+ }
+
+ return undefined;
+}
+
+/**
+ * Type guard detecting a thenable (promise-like) value. Generic so that
+ * narrowing preserves the input type (T & PromiseLike) rather than widening
+ * to PromiseLike