feat(queue): add redis connection module with health check (MQ-002)
This commit is contained in:
@@ -12,5 +12,8 @@
|
|||||||
"lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"vitest.config.ts\"",
|
"lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"vitest.config.ts\"",
|
||||||
"build": "tsc -p tsconfig.build.json",
|
"build": "tsc -p tsconfig.build.json",
|
||||||
"test": "vitest run"
|
"test": "vitest run"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ioredis": "^5.10.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,14 @@
|
|||||||
export const packageVersion = '0.0.1';
|
export const packageVersion = '0.0.1';
|
||||||
|
|
||||||
|
export {
|
||||||
|
assertRedisHealthy,
|
||||||
|
createRedisClient,
|
||||||
|
resolveRedisUrl,
|
||||||
|
runRedisHealthCheck,
|
||||||
|
} from './redis-connection.js';
|
||||||
|
export type {
|
||||||
|
CreateRedisClientOptions,
|
||||||
|
RedisClientConstructor,
|
||||||
|
RedisHealthCheck,
|
||||||
|
RedisPingClient,
|
||||||
|
} from './redis-connection.js';
|
||||||
|
|||||||
95
packages/queue/src/redis-connection.ts
Normal file
95
packages/queue/src/redis-connection.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import Redis, { type RedisOptions } from 'ioredis';
|
||||||
|
|
||||||
|
const ERR_MISSING_REDIS_URL =
|
||||||
|
'Missing required Valkey/Redis connection URL. Set VALKEY_URL or REDIS_URL.';
|
||||||
|
|
||||||
|
export interface RedisHealthCheck {
|
||||||
|
readonly checkedAt: number;
|
||||||
|
readonly latencyMs: number;
|
||||||
|
readonly ok: boolean;
|
||||||
|
readonly response?: string;
|
||||||
|
readonly error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RedisPingClient {
|
||||||
|
ping(): Promise<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RedisClientConstructor<TClient> = new (
|
||||||
|
url: string,
|
||||||
|
options?: RedisOptions,
|
||||||
|
) => TClient;
|
||||||
|
|
||||||
|
export interface CreateRedisClientOptions<TClient> {
|
||||||
|
readonly env?: NodeJS.ProcessEnv;
|
||||||
|
readonly redisConstructor?: RedisClientConstructor<TClient>;
|
||||||
|
readonly redisOptions?: RedisOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveRedisUrl(env: NodeJS.ProcessEnv = process.env): string {
|
||||||
|
const resolvedUrl = env.VALKEY_URL ?? env.REDIS_URL;
|
||||||
|
|
||||||
|
if (typeof resolvedUrl !== 'string' || resolvedUrl.trim().length === 0) {
|
||||||
|
throw new Error(ERR_MISSING_REDIS_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRedisClient<TClient = Redis>(
|
||||||
|
options: CreateRedisClientOptions<TClient> = {},
|
||||||
|
): TClient {
|
||||||
|
const redisUrl = resolveRedisUrl(options.env);
|
||||||
|
|
||||||
|
const RedisCtor =
|
||||||
|
options.redisConstructor ??
|
||||||
|
(Redis as unknown as RedisClientConstructor<TClient>);
|
||||||
|
|
||||||
|
return new RedisCtor(redisUrl, {
|
||||||
|
maxRetriesPerRequest: null,
|
||||||
|
...options.redisOptions,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runRedisHealthCheck(
|
||||||
|
client: RedisPingClient,
|
||||||
|
): Promise<RedisHealthCheck> {
|
||||||
|
const startedAt = process.hrtime.bigint();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await client.ping();
|
||||||
|
const elapsedMs = Number((process.hrtime.bigint() - startedAt) / 1_000_000n);
|
||||||
|
|
||||||
|
return {
|
||||||
|
checkedAt: Date.now(),
|
||||||
|
latencyMs: elapsedMs,
|
||||||
|
ok: true,
|
||||||
|
response,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
const elapsedMs = Number((process.hrtime.bigint() - startedAt) / 1_000_000n);
|
||||||
|
const message =
|
||||||
|
error instanceof Error ? error.message : 'Unknown redis health check error';
|
||||||
|
|
||||||
|
return {
|
||||||
|
checkedAt: Date.now(),
|
||||||
|
latencyMs: elapsedMs,
|
||||||
|
ok: false,
|
||||||
|
error: message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function assertRedisHealthy(
|
||||||
|
client: RedisPingClient,
|
||||||
|
): Promise<RedisHealthCheck> {
|
||||||
|
const health = await runRedisHealthCheck(client);
|
||||||
|
|
||||||
|
if (!health.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Redis health check failed after ${health.latencyMs}ms: ${health.error ?? 'unknown error'}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return health;
|
||||||
|
}
|
||||||
76
packages/queue/tests/redis-connection.test.ts
Normal file
76
packages/queue/tests/redis-connection.test.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createRedisClient,
|
||||||
|
resolveRedisUrl,
|
||||||
|
runRedisHealthCheck,
|
||||||
|
} from '../src/redis-connection.js';
|
||||||
|
|
||||||
|
describe('resolveRedisUrl', () => {
|
||||||
|
it('prefers VALKEY_URL when both env vars are present', () => {
|
||||||
|
const url = resolveRedisUrl({
|
||||||
|
VALKEY_URL: 'redis://valkey.local:6379',
|
||||||
|
REDIS_URL: 'redis://redis.local:6379',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(url).toBe('redis://valkey.local:6379');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to REDIS_URL when VALKEY_URL is missing', () => {
|
||||||
|
const url = resolveRedisUrl({
|
||||||
|
REDIS_URL: 'redis://redis.local:6379',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(url).toBe('redis://redis.local:6379');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws loudly when no redis environment variable exists', () => {
|
||||||
|
expect(() => resolveRedisUrl({})).toThrowError(
|
||||||
|
/Missing required Valkey\/Redis connection URL/i,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createRedisClient', () => {
|
||||||
|
it('uses env URL for client creation with no hardcoded defaults', () => {
|
||||||
|
class FakeRedis {
|
||||||
|
public readonly url: string;
|
||||||
|
|
||||||
|
public constructor(url: string) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = createRedisClient({
|
||||||
|
env: {
|
||||||
|
VALKEY_URL: 'redis://queue.local:6379',
|
||||||
|
},
|
||||||
|
redisConstructor: FakeRedis,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(client.url).toBe('redis://queue.local:6379');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('runRedisHealthCheck', () => {
|
||||||
|
it('returns healthy status when ping succeeds', async () => {
|
||||||
|
const health = await runRedisHealthCheck({
|
||||||
|
ping: () => Promise.resolve('PONG'),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(health.ok).toBe(true);
|
||||||
|
expect(health.response).toBe('PONG');
|
||||||
|
expect(health.latencyMs).toBeTypeOf('number');
|
||||||
|
expect(health.latencyMs).toBeGreaterThanOrEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns unhealthy status when ping fails', async () => {
|
||||||
|
const health = await runRedisHealthCheck({
|
||||||
|
ping: () => Promise.reject(new Error('connection refused')),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(health.ok).toBe(false);
|
||||||
|
expect(health.error).toMatch(/connection refused/i);
|
||||||
|
expect(health.latencyMs).toBeTypeOf('number');
|
||||||
|
});
|
||||||
|
});
|
||||||
70
pnpm-lock.yaml
generated
70
pnpm-lock.yaml
generated
@@ -27,7 +27,11 @@ importers:
|
|||||||
specifier: ^3.0.8
|
specifier: ^3.0.8
|
||||||
version: 3.2.4(@types/node@22.19.15)
|
version: 3.2.4(@types/node@22.19.15)
|
||||||
|
|
||||||
packages/queue: {}
|
packages/queue:
|
||||||
|
dependencies:
|
||||||
|
ioredis:
|
||||||
|
specifier: ^5.10.0
|
||||||
|
version: 5.10.0
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -241,6 +245,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
||||||
engines: {node: '>=18.18'}
|
engines: {node: '>=18.18'}
|
||||||
|
|
||||||
|
'@ioredis/commands@1.5.1':
|
||||||
|
resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==}
|
||||||
|
|
||||||
'@jridgewell/sourcemap-codec@1.5.5':
|
'@jridgewell/sourcemap-codec@1.5.5':
|
||||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||||
|
|
||||||
@@ -530,6 +537,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==}
|
resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: '>= 16'}
|
||||||
|
|
||||||
|
cluster-key-slot@1.1.2:
|
||||||
|
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
engines: {node: '>=7.0.0'}
|
engines: {node: '>=7.0.0'}
|
||||||
@@ -560,6 +571,10 @@ packages:
|
|||||||
deep-is@0.1.4:
|
deep-is@0.1.4:
|
||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
|
|
||||||
|
denque@2.1.0:
|
||||||
|
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
|
||||||
|
engines: {node: '>=0.10'}
|
||||||
|
|
||||||
es-module-lexer@1.7.0:
|
es-module-lexer@1.7.0:
|
||||||
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
||||||
|
|
||||||
@@ -691,6 +706,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
||||||
engines: {node: '>=0.8.19'}
|
engines: {node: '>=0.8.19'}
|
||||||
|
|
||||||
|
ioredis@5.10.0:
|
||||||
|
resolution: {integrity: sha512-HVBe9OFuqs+Z6n64q09PQvP1/R4Bm+30PAyyD4wIEqssh3v9L21QjCVk4kRLucMBcDokJTcLjsGeVRlq/nH6DA==}
|
||||||
|
engines: {node: '>=12.22.0'}
|
||||||
|
|
||||||
is-extglob@2.1.1:
|
is-extglob@2.1.1:
|
||||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -729,6 +748,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
lodash.defaults@4.2.0:
|
||||||
|
resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
|
||||||
|
|
||||||
|
lodash.isarguments@3.1.0:
|
||||||
|
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
|
||||||
|
|
||||||
lodash.merge@4.6.2:
|
lodash.merge@4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
@@ -806,6 +831,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
redis-errors@1.2.0:
|
||||||
|
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
redis-parser@3.0.0:
|
||||||
|
resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
resolve-from@4.0.0:
|
resolve-from@4.0.0:
|
||||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -838,6 +871,9 @@ packages:
|
|||||||
stackback@0.0.2:
|
stackback@0.0.2:
|
||||||
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||||
|
|
||||||
|
standard-as-callback@2.1.0:
|
||||||
|
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
|
||||||
|
|
||||||
std-env@3.10.0:
|
std-env@3.10.0:
|
||||||
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
||||||
|
|
||||||
@@ -1123,6 +1159,8 @@ snapshots:
|
|||||||
|
|
||||||
'@humanwhocodes/retry@0.4.3': {}
|
'@humanwhocodes/retry@0.4.3': {}
|
||||||
|
|
||||||
|
'@ioredis/commands@1.5.1': {}
|
||||||
|
|
||||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||||
|
|
||||||
'@rollup/rollup-android-arm-eabi@4.59.0':
|
'@rollup/rollup-android-arm-eabi@4.59.0':
|
||||||
@@ -1401,6 +1439,8 @@ snapshots:
|
|||||||
|
|
||||||
check-error@2.1.3: {}
|
check-error@2.1.3: {}
|
||||||
|
|
||||||
|
cluster-key-slot@1.1.2: {}
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name: 1.1.4
|
color-name: 1.1.4
|
||||||
@@ -1423,6 +1463,8 @@ snapshots:
|
|||||||
|
|
||||||
deep-is@0.1.4: {}
|
deep-is@0.1.4: {}
|
||||||
|
|
||||||
|
denque@2.1.0: {}
|
||||||
|
|
||||||
es-module-lexer@1.7.0: {}
|
es-module-lexer@1.7.0: {}
|
||||||
|
|
||||||
esbuild@0.27.3:
|
esbuild@0.27.3:
|
||||||
@@ -1578,6 +1620,20 @@ snapshots:
|
|||||||
|
|
||||||
imurmurhash@0.1.4: {}
|
imurmurhash@0.1.4: {}
|
||||||
|
|
||||||
|
ioredis@5.10.0:
|
||||||
|
dependencies:
|
||||||
|
'@ioredis/commands': 1.5.1
|
||||||
|
cluster-key-slot: 1.1.2
|
||||||
|
debug: 4.4.3
|
||||||
|
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
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
is-extglob@2.1.1: {}
|
is-extglob@2.1.1: {}
|
||||||
|
|
||||||
is-glob@4.0.3:
|
is-glob@4.0.3:
|
||||||
@@ -1611,6 +1667,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
|
|
||||||
|
lodash.defaults@4.2.0: {}
|
||||||
|
|
||||||
|
lodash.isarguments@3.1.0: {}
|
||||||
|
|
||||||
lodash.merge@4.6.2: {}
|
lodash.merge@4.6.2: {}
|
||||||
|
|
||||||
loupe@3.2.1: {}
|
loupe@3.2.1: {}
|
||||||
@@ -1676,6 +1736,12 @@ snapshots:
|
|||||||
|
|
||||||
punycode@2.3.1: {}
|
punycode@2.3.1: {}
|
||||||
|
|
||||||
|
redis-errors@1.2.0: {}
|
||||||
|
|
||||||
|
redis-parser@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
redis-errors: 1.2.0
|
||||||
|
|
||||||
resolve-from@4.0.0: {}
|
resolve-from@4.0.0: {}
|
||||||
|
|
||||||
rollup@4.59.0:
|
rollup@4.59.0:
|
||||||
@@ -1723,6 +1789,8 @@ snapshots:
|
|||||||
|
|
||||||
stackback@0.0.2: {}
|
stackback@0.0.2: {}
|
||||||
|
|
||||||
|
standard-as-callback@2.1.0: {}
|
||||||
|
|
||||||
std-env@3.10.0: {}
|
std-env@3.10.0: {}
|
||||||
|
|
||||||
strip-json-comments@3.1.1: {}
|
strip-json-comments@3.1.1: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user