Redis client factory (error handling and good defaults) (#1761)

* @internal/redis package for creating Redis clients with sensible defaults and error handling

* Added @internal/redis to webapp
This commit is contained in:
Matt Aitken
2025-03-05 18:32:46 +00:00
committed by GitHub
parent 4358e2d2ef
commit 983bb411cc
18 changed files with 247 additions and 143 deletions
+2 -1
View File
@@ -52,6 +52,7 @@
"@heroicons/react": "^2.0.12",
"@internal/run-engine": "workspace:*",
"@internal/zod-worker": "workspace:*",
"@internal/redis": "workspace:*",
"@internal/redis-worker": "workspace:*",
"@internationalized/date": "^3.5.1",
"@lezer/highlight": "^1.1.6",
@@ -258,4 +259,4 @@
"engines": {
"node": ">=16.0.0"
}
}
}
+3 -1
View File
@@ -38,7 +38,9 @@
"@internal/run-engine": ["../../internal-packages/run-engine/src/index"],
"@internal/run-engine/*": ["../../internal-packages/run-engine/src/*"],
"@internal/redis-worker": ["../../internal-packages/redis-worker/src/index"],
"@internal/redis-worker/*": ["../../internal-packages/redis-worker/src/*"]
"@internal/redis-worker/*": ["../../internal-packages/redis-worker/src/*"],
"@internal/redis": ["../../internal-packages/redis/src/index"],
"@internal/redis/*": ["../../internal-packages/redis/src/*"]
},
"noEmit": true
}
+2 -1
View File
@@ -7,6 +7,7 @@
"type": "module",
"dependencies": {
"@opentelemetry/api": "^1.9.0",
"@internal/redis": "workspace:*",
"@trigger.dev/core": "workspace:*",
"ioredis": "^5.3.2",
"lodash.omit": "^4.5.0",
@@ -23,4 +24,4 @@
"typecheck": "tsc --noEmit",
"test": "vitest --no-file-parallelism"
}
}
}
+22 -27
View File
@@ -1,3 +1,4 @@
import { createRedisClient } from "@internal/redis";
import { Logger } from "@trigger.dev/core/logger";
import Redis, { type Callback, type RedisOptions, type Result } from "ioredis";
import { nanoid } from "nanoid";
@@ -50,35 +51,29 @@ export class SimpleQueue<TMessageCatalog extends MessageCatalogSchema> {
logger?: Logger;
}) {
this.name = name;
this.redis = new Redis({
...redisOptions,
keyPrefix: `${redisOptions.keyPrefix ?? ""}{queue:${name}:}`,
retryStrategy(times) {
const delay = Math.min(times * 50, 1000);
return delay;
},
maxRetriesPerRequest: 20,
});
this.#registerCommands();
this.schema = schema;
this.logger = logger ?? new Logger("SimpleQueue", "debug");
this.redis.on("error", (error) => {
this.logger.error(`Redis Error for queue ${this.name}:`, { queue: this.name, error });
});
this.redis.on("connect", () => {
this.logger.log(`Redis connected for queue ${this.name}`);
});
this.redis.on("reconnecting", () => {
this.logger.warn(`Redis reconnecting for queue ${this.name}`);
});
this.redis.on("close", () => {
this.logger.warn(`Redis connection closed for queue ${this.name}`);
});
this.redis = createRedisClient(
{
...redisOptions,
keyPrefix: `${redisOptions.keyPrefix ?? ""}{queue:${name}:}`,
retryStrategy(times) {
const delay = Math.min(times * 50, 1000);
return delay;
},
maxRetriesPerRequest: 20,
},
{
onError: (error) => {
this.logger.error(`RedisWorker queue redis client error:`, {
error,
keyPrefix: redisOptions.keyPrefix,
});
},
}
);
this.#registerCommands();
this.schema = schema;
}
async enqueue({
@@ -5,6 +5,7 @@ import { expect } from "vitest";
import { z } from "zod";
import { Worker } from "./worker.js";
import Redis from "ioredis";
import { createRedisClient } from "@internal/redis";
describe("Worker", () => {
redisTest("Process items that don't throw", { timeout: 30_000 }, async ({ redisContainer }) => {
@@ -241,7 +242,7 @@ describe("Worker", () => {
expect(dlqSize).toBe(1);
// Create a Redis client to publish the redrive message
const redisClient = new Redis({
const redisClient = createRedisClient({
host: redisContainer.getHost(),
port: redisContainer.getPort(),
password: redisContainer.getPassword(),
+9 -1
View File
@@ -9,6 +9,7 @@ import Redis from "ioredis";
import { nanoid } from "nanoid";
import { startSpan } from "./telemetry.js";
import pLimit from "p-limit";
import { createRedisClient } from "@internal/redis";
export type WorkerCatalog = {
[key: string]: {
@@ -108,7 +109,14 @@ class Worker<TCatalog extends WorkerCatalog> {
this.setupShutdownHandlers();
this.subscriber = new Redis(this.options.redisOptions);
this.subscriber = createRedisClient(this.options.redisOptions, {
onError: (error) => {
this.logger.error(`RedisWorker subscriber redis client error:`, {
error,
keyPrefix: this.options.redisOptions.keyPrefix,
});
},
});
this.setupSubscriber();
return this;
+3 -1
View File
@@ -18,7 +18,9 @@
"@internal/testcontainers": ["../../internal-packages/testcontainers/src/index"],
"@internal/testcontainers/*": ["../../internal-packages/testcontainers/src/*"],
"@trigger.dev/core": ["../../packages/core/src/index"],
"@trigger.dev/core/*": ["../../packages/core/src/*"]
"@trigger.dev/core/*": ["../../packages/core/src/*"],
"@internal/redis": ["../../internal-packages/redis/src/index"],
"@internal/redis/*": ["../../internal-packages/redis/src/*"]
}
},
"exclude": ["node_modules"]
+3
View File
@@ -0,0 +1,3 @@
# Redis
This is a simple package that is used to return a valid Redis client and provides an error callback. It will log and swallow errors if they're not handled.
+18
View File
@@ -0,0 +1,18 @@
{
"name": "@internal/redis",
"private": true,
"version": "0.0.1",
"main": "./src/index.ts",
"types": "./src/index.ts",
"type": "module",
"dependencies": {
"ioredis": "^5.3.2",
"@trigger.dev/core": "workspace:*"
},
"devDependencies": {
"vitest": "^1.4.0"
},
"scripts": {
"typecheck": "tsc --noEmit"
}
}
+40
View File
@@ -0,0 +1,40 @@
import { Redis, RedisOptions } from "ioredis";
import { Logger } from "@trigger.dev/core/logger";
const defaultOptions: Partial<RedisOptions> = {
retryStrategy: (times: number) => {
const delay = Math.min(times * 50, 1000);
return delay;
},
maxRetriesPerRequest: 20,
};
const logger = new Logger("Redis", "debug");
export function createRedisClient(
options: RedisOptions,
handlers?: { onError?: (err: Error) => void }
): Redis {
const client = new Redis({
...defaultOptions,
...options,
});
// Skip error handling setup if running in Vitest
if (process.env.VITEST) {
client.on("error", (error) => {
// swallow errors
});
return client;
}
client.on("error", (error) => {
if (handlers?.onError) {
handlers.onError(error);
} else {
logger.error(`Redis client error:`, { error, keyPrefix: options.keyPrefix });
}
});
return client;
}
+23
View File
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ES2019",
"lib": ["ES2019", "DOM", "DOM.Iterable", "DOM.AsyncIterable"],
"module": "CommonJS",
"moduleResolution": "Node",
"moduleDetection": "force",
"verbatimModuleSyntax": false,
"types": ["vitest/globals"],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"preserveWatchOutput": true,
"skipLibCheck": true,
"noEmit": true,
"strict": true,
"paths": {
"@trigger.dev/core": ["../../packages/core/src/index"],
"@trigger.dev/core/*": ["../../packages/core/src/*"]
}
},
"exclude": ["node_modules"]
}
@@ -5,6 +5,7 @@
"main": "./src/index.ts",
"types": "./src/index.ts",
"dependencies": {
"@internal/redis": "workspace:*",
"@internal/redis-worker": "workspace:*",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/semantic-conventions": "^1.27.0",
@@ -1,3 +1,4 @@
import { createRedisClient } from "@internal/redis";
import { Worker } from "@internal/redis-worker";
import { Attributes, Span, SpanKind, trace, Tracer } from "@opentelemetry/api";
import { assertExhaustive } from "@trigger.dev/core";
@@ -135,10 +136,20 @@ export class RunEngine {
constructor(private readonly options: RunEngineOptions) {
this.prisma = options.prisma;
this.runLockRedis = new Redis({
...options.runLock.redis,
keyPrefix: `${options.runLock.redis.keyPrefix}runlock:`,
});
this.runLockRedis = createRedisClient(
{
...options.runLock.redis,
keyPrefix: `${options.runLock.redis.keyPrefix}runlock:`,
},
{
onError: (error) => {
this.logger.error(`RunLock redis client error:`, {
error,
keyPrefix: options.runLock.redis.keyPrefix,
});
},
}
);
this.runLock = new RunLocker({ redis: this.runLockRedis });
this.runQueue = new RunQueue({
@@ -1,11 +1,11 @@
import { createRedisClient } from "@internal/redis";
import { redisTest } from "@internal/testcontainers";
import { expect } from "vitest";
import { RunLocker } from "./locking.js";
import Redis from "ioredis";
describe("RunLocker", () => {
redisTest("Test acquiring a lock works", { timeout: 15_000 }, async ({ redisOptions }) => {
const redis = new Redis(redisOptions);
const redis = createRedisClient(redisOptions);
try {
const runLock = new RunLocker({ redis });
@@ -23,7 +23,7 @@ describe("RunLocker", () => {
});
redisTest("Test double locking works", { timeout: 15_000 }, async ({ redisOptions }) => {
const redis = new Redis(redisOptions);
const redis = createRedisClient(redisOptions);
try {
const runLock = new RunLocker({ redis });
@@ -7,6 +7,7 @@ import { setTimeout } from "node:timers/promises";
import { RunQueue } from "./index.js";
import { SimpleWeightedChoiceStrategy } from "./simpleWeightedPriorityStrategy.js";
import { InputPayload } from "./types.js";
import { createRedisClient } from "@internal/redis";
const testOptions = {
name: "rq",
@@ -468,7 +469,7 @@ describe("RunQueue", () => {
},
});
const redis = new Redis({ ...redisOptions, keyPrefix: "runqueue:test:" });
const redis = createRedisClient({ ...redisOptions, keyPrefix: "runqueue:test:" });
try {
await queue.enqueueMessage({
@@ -598,7 +599,7 @@ describe("RunQueue", () => {
},
});
const redis = new Redis({ ...redisOptions, keyPrefix: "runqueue:test:" });
const redis = createRedisClient({ ...redisOptions, keyPrefix: "runqueue:test:" });
try {
await queue.enqueueMessage({
@@ -689,7 +690,7 @@ describe("RunQueue", () => {
},
});
const redis = new Redis({ ...redisOptions, keyPrefix: "runqueue:test:" });
const redis = createRedisClient({ ...redisOptions, keyPrefix: "runqueue:test:" });
try {
await queue.enqueueMessage({
@@ -803,7 +804,7 @@ describe("RunQueue", () => {
},
});
const redis = new Redis({ ...redisOptions, keyPrefix: "runqueue:test:" });
const redis = createRedisClient({ ...redisOptions, keyPrefix: "runqueue:test:" });
try {
await queue.enqueueMessage({
@@ -858,7 +859,7 @@ describe("RunQueue", () => {
expect(dlqMembers).toContain(messageProd.runId);
//redrive
const redisClient = new Redis({
const redisClient = createRedisClient({
host: redisContainer.getHost(),
port: redisContainer.getPort(),
password: redisContainer.getPassword(),
@@ -21,6 +21,7 @@ import {
RunQueueKeyProducer,
RunQueuePriorityStrategy,
} from "./types.js";
import { createRedisClient } from "@internal/redis";
const SemanticAttributes = {
QUEUE: "runqueue.queue",
@@ -71,13 +72,27 @@ export class RunQueue {
constructor(private readonly options: RunQueueOptions) {
this.retryOptions = options.retryOptions ?? defaultRetrySettings;
this.redis = new Redis(options.redis);
this.redis = createRedisClient(options.redis, {
onError: (error) => {
this.logger.error(`RunQueue redis client error:`, {
error,
keyPrefix: options.redis.keyPrefix,
});
},
});
this.logger = options.logger;
this.keys = new RunQueueShortKeyProducer("rq:");
this.queuePriorityStrategy = options.queuePriorityStrategy;
this.subscriber = new Redis(options.redis);
this.subscriber = createRedisClient(options.redis, {
onError: (error) => {
this.logger.error(`RunQueue subscriber redis client error:`, {
error,
keyPrefix: options.redis.keyPrefix,
});
},
});
this.#setupSubscriber();
this.#registerCommands();
@@ -19,6 +19,8 @@
"@internal/testcontainers/*": ["../../internal-packages/testcontainers/src/*"],
"@internal/redis-worker": ["../../internal-packages/redis-worker/src/index"],
"@internal/redis-worker/*": ["../../internal-packages/redis-worker/src/*"],
"@internal/redis": ["../../internal-packages/redis/src/index"],
"@internal/redis/*": ["../../internal-packages/redis/src/*"],
"@trigger.dev/core": ["../../packages/core/src/index"],
"@trigger.dev/core/*": ["../../packages/core/src/*"],
"@trigger.dev/database": ["../database/src/index"],
+76 -96
View File
@@ -228,6 +228,9 @@ importers:
'@heroicons/react':
specifier: ^2.0.12
version: 2.0.13(react@18.2.0)
'@internal/redis':
specifier: workspace:*
version: link:../../internal-packages/redis
'@internal/redis-worker':
specifier: workspace:*
version: link:../../internal-packages/redis-worker
@@ -654,7 +657,7 @@ importers:
version: link:../../internal-packages/testcontainers
'@remix-run/dev':
specifier: 2.1.0
version: 2.1.0(@remix-run/serve@2.1.0)(@types/node@18.11.18)(ts-node@10.9.1)(typescript@5.5.4)
version: 2.1.0(@remix-run/serve@2.1.0)(@types/node@20.14.14)(ts-node@10.9.1)(typescript@5.5.4)
'@remix-run/eslint-config':
specifier: 2.1.0
version: 2.1.0(eslint@8.31.0)(react@18.2.0)(typescript@5.5.4)
@@ -825,7 +828,7 @@ importers:
version: 3.4.1(ts-node@10.9.1)
ts-node:
specifier: ^10.7.0
version: 10.9.1(@swc/core@1.3.26)(@types/node@18.11.18)(typescript@5.5.4)
version: 10.9.1(@swc/core@1.3.26)(@types/node@20.14.14)(typescript@5.5.4)
tsconfig-paths:
specifier: ^3.14.1
version: 3.14.1
@@ -834,7 +837,7 @@ importers:
version: 4.0.5(typescript@5.5.4)
vitest:
specifier: ^1.4.0
version: 1.4.0(@types/node@18.11.18)
version: 1.4.0(@types/node@20.14.14)
docs: {}
@@ -904,8 +907,24 @@ importers:
specifier: ^1.167.3
version: 1.167.3
internal-packages/redis:
dependencies:
'@trigger.dev/core':
specifier: workspace:*
version: link:../../packages/core
ioredis:
specifier: ^5.3.2
version: 5.3.2
devDependencies:
vitest:
specifier: ^1.4.0
version: 1.6.0(@types/node@20.14.14)
internal-packages/redis-worker:
dependencies:
'@internal/redis':
specifier: workspace:*
version: link:../redis
'@opentelemetry/api':
specifier: ^1.9.0
version: 1.9.0
@@ -940,6 +959,9 @@ importers:
internal-packages/run-engine:
dependencies:
'@internal/redis':
specifier: workspace:*
version: link:../redis
'@internal/redis-worker':
specifier: workspace:*
version: link:../redis-worker
@@ -1990,7 +2012,7 @@ importers:
version: link:../../packages/cli-v3
ts-node:
specifier: ^10.9.2
version: 10.9.2(@types/node@20.4.2)(typescript@5.5.4)
version: 10.9.2(@types/node@20.14.14)(typescript@5.5.4)
tsconfig-paths:
specifier: ^4.2.0
version: 4.2.0
@@ -14829,7 +14851,7 @@ packages:
- encoding
dev: false
/@remix-run/dev@2.1.0(@remix-run/serve@2.1.0)(@types/node@18.11.18)(ts-node@10.9.1)(typescript@5.5.4):
/@remix-run/dev@2.1.0(@remix-run/serve@2.1.0)(@types/node@20.14.14)(ts-node@10.9.1)(typescript@5.5.4):
resolution: {integrity: sha512-Hn5lw46F+a48dp5uHKe68ckaHgdStW4+PmLod+LMFEqrMbkF0j4XD1ousebxlv989o0Uy/OLgfRMgMy4cBOvHg==}
engines: {node: '>=18.0.0'}
hasBin: true
@@ -14854,7 +14876,7 @@ packages:
'@remix-run/serve': 2.1.0(typescript@5.5.4)
'@remix-run/server-runtime': 2.1.0(typescript@5.5.4)
'@types/mdx': 2.0.5
'@vanilla-extract/integration': 6.2.1(@types/node@18.11.18)
'@vanilla-extract/integration': 6.2.1(@types/node@20.14.14)
arg: 5.0.2
cacache: 17.1.4
chalk: 4.1.2
@@ -17041,9 +17063,6 @@ packages:
/@types/node@12.20.55:
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
/@types/node@18.11.18:
resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
/@types/node@18.19.20:
resolution: {integrity: sha512-SKXZvI375jkpvAj8o+5U2518XQv76mAsixqfXiVyWyXZbVWQK25RurFovYpVIxVzul0rZoH58V/3SkEnm7s3qA==}
dependencies:
@@ -17066,9 +17085,6 @@ packages:
dependencies:
undici-types: 5.26.5
/@types/node@20.4.2:
resolution: {integrity: sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==}
/@types/nodemailer@6.4.17:
resolution: {integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==}
dependencies:
@@ -17622,7 +17638,7 @@ packages:
outdent: 0.8.0
dev: true
/@vanilla-extract/integration@6.2.1(@types/node@18.11.18):
/@vanilla-extract/integration@6.2.1(@types/node@20.14.14):
resolution: {integrity: sha512-+xYJz07G7TFAMZGrOqArOsURG+xcYvqctujEkANjw2McCBvGEK505RxQqOuNiA9Mi9hgGdNp2JedSa94f3eoLg==}
dependencies:
'@babel/core': 7.22.17
@@ -17636,8 +17652,8 @@ packages:
lodash: 4.17.21
mlly: 1.7.1
outdent: 0.8.0
vite: 4.4.9(@types/node@18.11.18)
vite-node: 0.28.5(@types/node@18.11.18)
vite: 4.4.9(@types/node@20.14.14)
vite-node: 0.28.5(@types/node@20.14.14)
transitivePeerDependencies:
- '@types/node'
- less
@@ -27068,7 +27084,7 @@ packages:
dependencies:
lilconfig: 2.1.0
postcss: 8.4.29
ts-node: 10.9.1(@swc/core@1.3.26)(@types/node@18.11.18)(typescript@5.5.4)
ts-node: 10.9.1(@swc/core@1.3.26)(@types/node@20.14.14)(typescript@5.5.4)
yaml: 2.3.1
dev: true
@@ -27086,7 +27102,7 @@ packages:
dependencies:
lilconfig: 2.1.0
postcss: 8.4.44
ts-node: 10.9.1(@swc/core@1.3.26)(@types/node@18.11.18)(typescript@5.5.4)
ts-node: 10.9.1(@swc/core@1.3.26)(@types/node@20.14.14)(typescript@5.5.4)
yaml: 2.3.1
/postcss-loader@8.1.1(postcss@8.4.44)(typescript@5.5.4)(webpack@5.88.2):
@@ -30831,7 +30847,7 @@ packages:
/ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
/ts-node@10.9.1(@swc/core@1.3.26)(@types/node@18.11.18)(typescript@5.5.4):
/ts-node@10.9.1(@swc/core@1.3.26)(@types/node@20.14.14)(typescript@5.5.4):
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
hasBin: true
peerDependencies:
@@ -30851,7 +30867,7 @@ packages:
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.3
'@types/node': 18.11.18
'@types/node': 20.14.14
acorn: 8.10.0
acorn-walk: 8.2.0
arg: 4.1.3
@@ -30862,7 +30878,7 @@ packages:
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
/ts-node@10.9.2(@types/node@20.4.2)(typescript@5.5.4):
/ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4):
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
hasBin: true
peerDependencies:
@@ -30881,7 +30897,7 @@ packages:
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.3
'@types/node': 20.4.2
'@types/node': 20.14.14
acorn: 8.12.1
acorn-walk: 8.3.2
arg: 4.1.3
@@ -31335,7 +31351,7 @@ packages:
pg: 8.11.5
reflect-metadata: 0.2.2
sha.js: 2.4.11
ts-node: 10.9.2(@types/node@20.4.2)(typescript@5.5.4)
ts-node: 10.9.2(@types/node@20.14.14)(typescript@5.5.4)
tslib: 2.6.2
uuid: 9.0.0
yargs: 17.7.2
@@ -31897,30 +31913,6 @@ packages:
d3-timer: 3.0.1
dev: false
/vite-node@0.28.5(@types/node@18.11.18):
resolution: {integrity: sha512-LmXb9saMGlrMZbXTvOveJKwMTBTNUH66c8rJnQ0ZPNX+myPEol64+szRzXtV5ORb0Hb/91yq+/D3oERoyAt6LA==}
engines: {node: '>=v14.16.0'}
hasBin: true
dependencies:
cac: 6.7.14
debug: 4.3.7
mlly: 1.7.1
pathe: 1.1.2
picocolors: 1.1.1
source-map: 0.6.1
source-map-support: 0.5.21
vite: 4.4.9(@types/node@18.11.18)
transitivePeerDependencies:
- '@types/node'
- less
- lightningcss
- sass
- stylus
- sugarss
- supports-color
- terser
dev: true
/vite-node@0.28.5(@types/node@18.19.20):
resolution: {integrity: sha512-LmXb9saMGlrMZbXTvOveJKwMTBTNUH66c8rJnQ0ZPNX+myPEol64+szRzXtV5ORb0Hb/91yq+/D3oERoyAt6LA==}
engines: {node: '>=v14.16.0'}
@@ -31945,7 +31937,31 @@ packages:
- terser
dev: true
/vite-node@1.4.0(@types/node@18.11.18):
/vite-node@0.28.5(@types/node@20.14.14):
resolution: {integrity: sha512-LmXb9saMGlrMZbXTvOveJKwMTBTNUH66c8rJnQ0ZPNX+myPEol64+szRzXtV5ORb0Hb/91yq+/D3oERoyAt6LA==}
engines: {node: '>=v14.16.0'}
hasBin: true
dependencies:
cac: 6.7.14
debug: 4.3.7
mlly: 1.7.1
pathe: 1.1.2
picocolors: 1.1.1
source-map: 0.6.1
source-map-support: 0.5.21
vite: 4.4.9(@types/node@20.14.14)
transitivePeerDependencies:
- '@types/node'
- less
- lightningcss
- sass
- stylus
- sugarss
- supports-color
- terser
dev: true
/vite-node@1.4.0(@types/node@20.14.14):
resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
@@ -31954,7 +31970,7 @@ packages:
debug: 4.3.7
pathe: 1.1.2
picocolors: 1.1.1
vite: 5.2.7(@types/node@18.11.18)
vite: 5.2.7(@types/node@20.14.14)
transitivePeerDependencies:
- '@types/node'
- less
@@ -32087,42 +32103,6 @@ packages:
fsevents: 2.3.3
dev: true
/vite@4.4.9(@types/node@18.11.18):
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
'@types/node': '>= 14'
less: '*'
lightningcss: ^1.21.0
sass: '*'
stylus: '*'
sugarss: '*'
terser: ^5.4.0
peerDependenciesMeta:
'@types/node':
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
dependencies:
'@types/node': 18.11.18
esbuild: 0.18.11
postcss: 8.4.44
rollup: 3.29.1
optionalDependencies:
fsevents: 2.3.3
dev: true
/vite@4.4.9(@types/node@18.19.20):
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -32159,12 +32139,12 @@ packages:
fsevents: 2.3.3
dev: true
/vite@5.2.7(@types/node@18.11.18):
resolution: {integrity: sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==}
engines: {node: ^18.0.0 || >=20.0.0}
/vite@4.4.9(@types/node@20.14.14):
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
'@types/node': ^18.0.0 || >=20.0.0
'@types/node': '>= 14'
less: '*'
lightningcss: ^1.21.0
sass: '*'
@@ -32187,10 +32167,10 @@ packages:
terser:
optional: true
dependencies:
'@types/node': 18.11.18
esbuild: 0.20.2
'@types/node': 20.14.14
esbuild: 0.18.11
postcss: 8.4.44
rollup: 4.13.2
rollup: 3.29.1
optionalDependencies:
fsevents: 2.3.3
dev: true
@@ -32287,7 +32267,7 @@ packages:
- terser
dev: true
/vitest@1.4.0(@types/node@18.11.18):
/vitest@1.4.0(@types/node@20.14.14):
resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
@@ -32312,7 +32292,7 @@ packages:
jsdom:
optional: true
dependencies:
'@types/node': 18.11.18
'@types/node': 20.14.14
'@vitest/expect': 1.4.0
'@vitest/runner': 1.4.0
'@vitest/snapshot': 1.4.0
@@ -32330,8 +32310,8 @@ packages:
strip-literal: 2.1.0
tinybench: 2.6.0
tinypool: 0.8.3
vite: 5.2.7(@types/node@18.11.18)
vite-node: 1.4.0(@types/node@18.11.18)
vite: 5.2.7(@types/node@20.14.14)
vite-node: 1.4.0(@types/node@20.14.14)
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less
@@ -32381,7 +32361,7 @@ packages:
local-pkg: 0.5.0
magic-string: 0.30.11
pathe: 1.1.2
picocolors: 1.0.1
picocolors: 1.1.1
std-env: 3.7.0
strip-literal: 2.1.0
tinybench: 2.9.0