feat(phase3): worker container + event bus + scheduled jobs
apps/worker: - BullMQ nightly scheduler (cron 0 3 * * *) - Redis Streams consumer-group per wired/active project - Persists events to Event model schema: - Event model (streamId unique, project + type indexed)
This commit is contained in:
@@ -79,6 +79,20 @@ model Project {
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Event {
|
||||
id String @id @default(cuid())
|
||||
streamId String @unique
|
||||
projectKey String
|
||||
eventType String
|
||||
version Int @default(1)
|
||||
payload Json
|
||||
occurredAt DateTime
|
||||
receivedAt DateTime @default(now())
|
||||
|
||||
@@index([projectKey, occurredAt])
|
||||
@@index([eventType])
|
||||
}
|
||||
|
||||
model AuditLog {
|
||||
id String @id @default(cuid())
|
||||
actorUserId String?
|
||||
|
||||
1
apps/web/tsconfig.tsbuildinfo
Normal file
1
apps/web/tsconfig.tsbuildinfo
Normal file
File diff suppressed because one or more lines are too long
26
apps/worker/Dockerfile
Normal file
26
apps/worker/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
FROM node:22-alpine AS base
|
||||
RUN apk add --no-cache libc6-compat openssl
|
||||
WORKDIR /app
|
||||
RUN corepack enable && corepack prepare pnpm@9.12.0 --activate
|
||||
|
||||
# ---- builder: install everything (need apps/web prisma client) ----
|
||||
FROM base AS builder
|
||||
ENV NODE_ENV=development
|
||||
COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml turbo.json ./
|
||||
COPY apps/web/package.json apps/web/package.json
|
||||
COPY apps/worker/package.json apps/worker/package.json
|
||||
RUN pnpm install --frozen-lockfile --prod=false || pnpm install --prod=false
|
||||
COPY . .
|
||||
WORKDIR /app/apps/web
|
||||
RUN npx --no-install prisma generate
|
||||
|
||||
# ---- runner ----
|
||||
FROM base AS runner
|
||||
ENV NODE_ENV=production
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/package.json /app/pnpm-workspace.yaml /app/turbo.json ./
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
COPY --from=builder /app/apps/web ./apps/web
|
||||
COPY --from=builder /app/apps/worker ./apps/worker
|
||||
|
||||
CMD ["pnpm","--filter","@panel/worker","start"]
|
||||
21
apps/worker/package.json
Normal file
21
apps/worker/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "@panel/worker",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"start": "tsx src/index.ts",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@panel/web": "workspace:*",
|
||||
"@prisma/client": "^5.22.0",
|
||||
"bullmq": "^5.34.0",
|
||||
"ioredis": "^5.4.1",
|
||||
"tsx": "^4.19.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"typescript": "^5.6.0"
|
||||
}
|
||||
}
|
||||
94
apps/worker/src/consumers/event-bus.ts
Normal file
94
apps/worker/src/consumers/event-bus.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { redis } from "../redis";
|
||||
import { prisma } from "../db";
|
||||
|
||||
const GROUP = "panel";
|
||||
const CONSUMER = `panel-worker-${process.env.HOSTNAME ?? "1"}`;
|
||||
|
||||
type RawEvent = {
|
||||
event_type: string;
|
||||
version?: string | number;
|
||||
occurred_at?: string;
|
||||
payload?: string;
|
||||
};
|
||||
|
||||
async function ensureGroup(stream: string) {
|
||||
try {
|
||||
await redis.xgroup("CREATE", stream, GROUP, "0", "MKSTREAM");
|
||||
console.log(`[event-bus] group created for ${stream}`);
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
if (!msg.includes("BUSYGROUP")) throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function consume(stream: string, projectKey: string) {
|
||||
await ensureGroup(stream);
|
||||
while (true) {
|
||||
try {
|
||||
const res = await redis.xreadgroup(
|
||||
"GROUP", GROUP, CONSUMER,
|
||||
"COUNT", 32,
|
||||
"BLOCK", 15000,
|
||||
"STREAMS", stream, ">",
|
||||
) as Array<[string, Array<[string, string[]]>]> | null;
|
||||
|
||||
if (!res) continue;
|
||||
|
||||
for (const [, entries] of res) {
|
||||
for (const [streamId, fields] of entries) {
|
||||
const obj = fieldsToObj(fields);
|
||||
try {
|
||||
await persist(streamId, projectKey, obj);
|
||||
await redis.xack(stream, GROUP, streamId);
|
||||
} catch (err) {
|
||||
console.error(`[event-bus] persist failed ${stream} ${streamId}:`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[event-bus] xreadgroup error for ${stream}:`, err);
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fieldsToObj(fields: string[]): RawEvent {
|
||||
const o: Record<string, string> = {};
|
||||
for (let i = 0; i + 1 < fields.length; i += 2) o[fields[i]] = fields[i + 1];
|
||||
return o as RawEvent;
|
||||
}
|
||||
|
||||
async function persist(streamId: string, projectKey: string, raw: RawEvent) {
|
||||
let payload: unknown = null;
|
||||
try {
|
||||
payload = raw.payload ? JSON.parse(raw.payload) : null;
|
||||
} catch {
|
||||
payload = raw.payload ?? null;
|
||||
}
|
||||
await prisma.event.create({
|
||||
data: {
|
||||
streamId,
|
||||
projectKey,
|
||||
eventType: raw.event_type ?? "unknown",
|
||||
version: raw.version ? Number(raw.version) : 1,
|
||||
occurredAt: raw.occurred_at ? new Date(raw.occurred_at) : new Date(),
|
||||
payload: payload as object,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function startEventBus() {
|
||||
const projects = await prisma.project.findMany({
|
||||
where: { status: { in: ["wired", "active"] } },
|
||||
select: { key: true },
|
||||
});
|
||||
if (projects.length === 0) {
|
||||
console.log("[event-bus] no active projects; idle");
|
||||
return;
|
||||
}
|
||||
for (const p of projects) {
|
||||
const stream = `${p.key}:events`;
|
||||
console.log(`[event-bus] consuming ${stream}`);
|
||||
void consume(stream, p.key);
|
||||
}
|
||||
}
|
||||
3
apps/worker/src/db.ts
Normal file
3
apps/worker/src/db.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
export const prisma = new PrismaClient({ log: ["error"] });
|
||||
32
apps/worker/src/index.ts
Normal file
32
apps/worker/src/index.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { startEventBus } from "./consumers/event-bus";
|
||||
import { startScheduledJobs } from "./schedulers/nightly";
|
||||
import { redis } from "./redis";
|
||||
import { prisma } from "./db";
|
||||
|
||||
async function main() {
|
||||
console.log("[worker] starting…");
|
||||
await redis.ping();
|
||||
console.log("[worker] redis ok");
|
||||
await prisma.$queryRaw`SELECT 1`;
|
||||
console.log("[worker] panel-db ok");
|
||||
|
||||
await startScheduledJobs();
|
||||
await startEventBus();
|
||||
|
||||
console.log("[worker] up.");
|
||||
}
|
||||
|
||||
const shutdown = async (sig: string) => {
|
||||
console.log(`[worker] ${sig} — shutting down`);
|
||||
await prisma.$disconnect().catch(() => {});
|
||||
await redis.quit().catch(() => {});
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
||||
process.on("SIGINT", () => void shutdown("SIGINT"));
|
||||
|
||||
main().catch((e) => {
|
||||
console.error("[worker] fatal:", e);
|
||||
process.exit(1);
|
||||
});
|
||||
13
apps/worker/src/redis.ts
Normal file
13
apps/worker/src/redis.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import IORedis from "ioredis";
|
||||
|
||||
const url = process.env.REDIS_URL;
|
||||
if (!url) throw new Error("REDIS_URL not set");
|
||||
|
||||
export const redis = new IORedis(url, {
|
||||
maxRetriesPerRequest: null,
|
||||
enableReadyCheck: true,
|
||||
});
|
||||
|
||||
redis.on("error", (err) => {
|
||||
console.error("[redis]", err.message);
|
||||
});
|
||||
30
apps/worker/src/schedulers/nightly.ts
Normal file
30
apps/worker/src/schedulers/nightly.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Queue, Worker, type Job } from "bullmq";
|
||||
import { redis } from "../redis";
|
||||
import { prisma } from "../db";
|
||||
|
||||
const QUEUE = "nightly";
|
||||
|
||||
const queue = new Queue(QUEUE, { connection: redis });
|
||||
|
||||
async function nightlyRefresh(_job: Job) {
|
||||
// Placeholder: when materialized views are added (Phase 4+), refresh them here.
|
||||
const projectCount = await prisma.project.count();
|
||||
console.log(`[nightly] refresh run at ${new Date().toISOString()} — ${projectCount} projects`);
|
||||
return { ok: true, projects: projectCount };
|
||||
}
|
||||
|
||||
export async function startScheduledJobs() {
|
||||
// BullMQ repeat: every day at 03:00 server time
|
||||
await queue.upsertJobScheduler(
|
||||
"nightly-refresh",
|
||||
{ pattern: "0 3 * * *" },
|
||||
{
|
||||
name: "nightly-refresh",
|
||||
data: {},
|
||||
opts: { removeOnComplete: 50, removeOnFail: 50 },
|
||||
},
|
||||
);
|
||||
|
||||
new Worker(QUEUE, nightlyRefresh, { connection: redis, concurrency: 1 });
|
||||
console.log("[scheduler] nightly job armed (0 3 * * *)");
|
||||
}
|
||||
16
apps/worker/tsconfig.json
Normal file
16
apps/worker/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["ES2022"],
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
209
pnpm-lock.yaml
generated
209
pnpm-lock.yaml
generated
@@ -8,6 +8,9 @@ importers:
|
||||
|
||||
.:
|
||||
devDependencies:
|
||||
tsx:
|
||||
specifier: ^4.19.2
|
||||
version: 4.21.0
|
||||
turbo:
|
||||
specifier: ^2.5.0
|
||||
version: 2.9.12
|
||||
@@ -115,6 +118,31 @@ importers:
|
||||
specifier: ^5.6.0
|
||||
version: 5.9.3
|
||||
|
||||
apps/worker:
|
||||
dependencies:
|
||||
'@panel/web':
|
||||
specifier: workspace:*
|
||||
version: link:../web
|
||||
'@prisma/client':
|
||||
specifier: ^5.22.0
|
||||
version: 5.22.0(prisma@5.22.0)
|
||||
bullmq:
|
||||
specifier: ^5.34.0
|
||||
version: 5.76.8
|
||||
ioredis:
|
||||
specifier: ^5.4.1
|
||||
version: 5.10.1
|
||||
tsx:
|
||||
specifier: ^4.19.2
|
||||
version: 4.21.0
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^22.0.0
|
||||
version: 22.19.19
|
||||
typescript:
|
||||
specifier: ^5.6.0
|
||||
version: 5.9.3
|
||||
|
||||
packages:
|
||||
|
||||
'@alloc/quick-lru@5.2.0':
|
||||
@@ -750,6 +778,9 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@ioredis/commands@1.5.1':
|
||||
resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
||||
|
||||
@@ -776,6 +807,36 @@ packages:
|
||||
'@cfworker/json-schema':
|
||||
optional: true
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3':
|
||||
resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3':
|
||||
resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3':
|
||||
resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3':
|
||||
resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3':
|
||||
resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3':
|
||||
resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@mswjs/interceptors@0.41.9':
|
||||
resolution: {integrity: sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1421,6 +1482,10 @@ packages:
|
||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
hasBin: true
|
||||
|
||||
bullmq@5.76.8:
|
||||
resolution: {integrity: sha512-v3WTwA8diFtsADaJ8eK2ozyi2CYK9rDZCeoKF+dIPF/MUL8HxAOa3H72Gmu1lC4yKlho6t1PwNr/QpDVqaNEZQ==}
|
||||
engines: {node: '>=12.22.0'}
|
||||
|
||||
bundle-name@4.1.0:
|
||||
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1474,6 +1539,10 @@ packages:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cluster-key-slot@1.1.2:
|
||||
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
cmdk@1.1.1:
|
||||
resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==}
|
||||
peerDependencies:
|
||||
@@ -1534,6 +1603,10 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
cron-parser@4.9.0:
|
||||
resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -1633,6 +1706,10 @@ packages:
|
||||
defu@6.1.7:
|
||||
resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==}
|
||||
|
||||
denque@2.1.0:
|
||||
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
||||
depd@2.0.0:
|
||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -1936,6 +2013,10 @@ packages:
|
||||
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ioredis@5.10.1:
|
||||
resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==}
|
||||
engines: {node: '>=12.22.0'}
|
||||
|
||||
ip-address@10.2.0:
|
||||
resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==}
|
||||
engines: {node: '>= 12'}
|
||||
@@ -2147,6 +2228,12 @@ packages:
|
||||
lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
|
||||
lodash.defaults@4.2.0:
|
||||
resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
|
||||
|
||||
lodash.isarguments@3.1.0:
|
||||
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
|
||||
|
||||
log-symbols@6.0.0:
|
||||
resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -2159,6 +2246,10 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
luxon@3.7.2:
|
||||
resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
@@ -2211,6 +2302,13 @@ packages:
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
msgpackr-extract@3.0.3:
|
||||
resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==}
|
||||
hasBin: true
|
||||
|
||||
msgpackr@2.0.1:
|
||||
resolution: {integrity: sha512-9J+tqTEsbHqY8YohazYgty7LgerFIWxvMLpUjqETSmjHojtJm2WnX2kK/2a1fLI7CO7ERP1YSEUXMucz4j+yBA==}
|
||||
|
||||
msw@2.14.6:
|
||||
resolution: {integrity: sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -2265,6 +2363,9 @@ packages:
|
||||
sass:
|
||||
optional: true
|
||||
|
||||
node-abort-controller@3.1.1:
|
||||
resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
|
||||
|
||||
node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
@@ -2274,6 +2375,10 @@ packages:
|
||||
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
node-gyp-build-optional-packages@5.2.2:
|
||||
resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==}
|
||||
hasBin: true
|
||||
|
||||
node-releases@2.0.44:
|
||||
resolution: {integrity: sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==}
|
||||
|
||||
@@ -2485,6 +2590,14 @@ packages:
|
||||
react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
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'}
|
||||
|
||||
redux-thunk@3.1.0:
|
||||
resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==}
|
||||
peerDependencies:
|
||||
@@ -2621,6 +2734,9 @@ packages:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
standard-as-callback@2.1.0:
|
||||
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
|
||||
|
||||
statuses@2.0.2:
|
||||
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -3422,6 +3538,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.19
|
||||
|
||||
'@ioredis/commands@1.5.1': {}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
@@ -3463,6 +3581,24 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3':
|
||||
optional: true
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3':
|
||||
optional: true
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3':
|
||||
optional: true
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3':
|
||||
optional: true
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3':
|
||||
optional: true
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3':
|
||||
optional: true
|
||||
|
||||
'@mswjs/interceptors@0.41.9':
|
||||
dependencies:
|
||||
'@open-draft/deferred-promise': 2.2.0
|
||||
@@ -3982,6 +4118,17 @@ snapshots:
|
||||
node-releases: 2.0.44
|
||||
update-browserslist-db: 1.2.3(browserslist@4.28.2)
|
||||
|
||||
bullmq@5.76.8:
|
||||
dependencies:
|
||||
cron-parser: 4.9.0
|
||||
ioredis: 5.10.1
|
||||
msgpackr: 2.0.1
|
||||
node-abort-controller: 3.1.1
|
||||
semver: 7.8.0
|
||||
tslib: 2.8.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
bundle-name@4.1.0:
|
||||
dependencies:
|
||||
run-applescript: 7.1.0
|
||||
@@ -4026,6 +4173,8 @@ snapshots:
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
cluster-key-slot@1.1.2: {}
|
||||
|
||||
cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6)
|
||||
@@ -4076,6 +4225,10 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
cron-parser@4.9.0:
|
||||
dependencies:
|
||||
luxon: 3.7.2
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
@@ -4147,6 +4300,8 @@ snapshots:
|
||||
|
||||
defu@6.1.7: {}
|
||||
|
||||
denque@2.1.0: {}
|
||||
|
||||
depd@2.0.0: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
@@ -4486,6 +4641,20 @@ snapshots:
|
||||
|
||||
internmap@2.0.3: {}
|
||||
|
||||
ioredis@5.10.1:
|
||||
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
|
||||
|
||||
ip-address@10.2.0: {}
|
||||
|
||||
ipaddr.js@1.9.1: {}
|
||||
@@ -4621,6 +4790,10 @@ snapshots:
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
lodash.defaults@4.2.0: {}
|
||||
|
||||
lodash.isarguments@3.1.0: {}
|
||||
|
||||
log-symbols@6.0.0:
|
||||
dependencies:
|
||||
chalk: 5.6.2
|
||||
@@ -4634,6 +4807,8 @@ snapshots:
|
||||
dependencies:
|
||||
react: 19.2.6
|
||||
|
||||
luxon@3.7.2: {}
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
@@ -4671,6 +4846,22 @@ snapshots:
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
msgpackr-extract@3.0.3:
|
||||
dependencies:
|
||||
node-gyp-build-optional-packages: 5.2.2
|
||||
optionalDependencies:
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3
|
||||
'@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3
|
||||
'@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3
|
||||
'@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3
|
||||
'@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3
|
||||
optional: true
|
||||
|
||||
msgpackr@2.0.1:
|
||||
optionalDependencies:
|
||||
msgpackr-extract: 3.0.3
|
||||
|
||||
msw@2.14.6(@types/node@22.19.19)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@inquirer/confirm': 6.0.13(@types/node@22.19.19)
|
||||
@@ -4733,6 +4924,8 @@ snapshots:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
|
||||
node-abort-controller@3.1.1: {}
|
||||
|
||||
node-domexception@1.0.0: {}
|
||||
|
||||
node-fetch@3.3.2:
|
||||
@@ -4741,6 +4934,11 @@ snapshots:
|
||||
fetch-blob: 3.2.0
|
||||
formdata-polyfill: 4.0.10
|
||||
|
||||
node-gyp-build-optional-packages@5.2.2:
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
optional: true
|
||||
|
||||
node-releases@2.0.44: {}
|
||||
|
||||
npm-run-path@4.0.1:
|
||||
@@ -4957,6 +5155,12 @@ snapshots:
|
||||
- '@types/react'
|
||||
- redux
|
||||
|
||||
redis-errors@1.2.0: {}
|
||||
|
||||
redis-parser@3.0.0:
|
||||
dependencies:
|
||||
redis-errors: 1.2.0
|
||||
|
||||
redux-thunk@3.1.0(redux@5.0.1):
|
||||
dependencies:
|
||||
redux: 5.0.1
|
||||
@@ -5006,8 +5210,7 @@ snapshots:
|
||||
|
||||
semver@6.3.1: {}
|
||||
|
||||
semver@7.8.0:
|
||||
optional: true
|
||||
semver@7.8.0: {}
|
||||
|
||||
send@1.2.1:
|
||||
dependencies:
|
||||
@@ -5162,6 +5365,8 @@ snapshots:
|
||||
|
||||
source-map@0.6.1: {}
|
||||
|
||||
standard-as-callback@2.1.0: {}
|
||||
|
||||
statuses@2.0.2: {}
|
||||
|
||||
stdin-discarder@0.2.2: {}
|
||||
|
||||
Reference in New Issue
Block a user