chore(webapp): migrate from Remix compiler to Vite (#4188)

Replaces Remix compiler with the Vite plugin. The Express server
(cluster, socket.io, ws) and the Docker image contract are unchanged.
This commit is contained in:
Katia Bulatova
2026-07-21 15:57:13 +02:00
committed by GitHub
parent dc87b884e7
commit d05f1a7398
38 changed files with 489 additions and 322 deletions
@@ -0,0 +1,5 @@
---
"@trigger.dev/sdk": patch
---
Suppress a build-time warning that could appear in Vite-based projects when the optional `@ai-sdk/otel` package is not installed.
@@ -1,7 +1,7 @@
// Recovers from a rolling deploy rotating the content-hashed /build assets out from
// Recovers from a rolling deploy rotating the content-hashed /assets files out from
// under a page. Each image serves only its own build and hard-404s unknown hashes, so
// a client can request a hash the serving replica doesn't have and get missing styles
// or a failed asset load. On such a /build load failure we do a bounded full document
// or a failed asset load. On such an asset load failure we do a bounded full document
// reload: the fresh document (and, under sticky routing, all of its assets) lands on a
// single live build, so the asset resolves. Bounded via sessionStorage so it can never
// loop; when the budget is spent it stops rather than reloading forever.
@@ -39,7 +39,7 @@ export function staleAssetRecoveryScript() {
}
function recover() {
// One recovery per page: a broken load fails several /build assets at once and each
// One recovery per page: a broken load fails several hashed assets at once and each
// fires its own error event before location.reload() commits — without this guard a
// single incident would burn the entire reload budget.
if (recovering) return;
@@ -62,7 +62,9 @@ export function staleAssetRecoveryScript() {
: el.tagName === "SCRIPT"
? (el as HTMLScriptElement).src
: null;
if (url && url.indexOf("/build/") !== -1) recover();
// Match the pathname, not the full URL — a query string or third-party
// URL containing /assets/ must not burn the reload budget.
if (url && new URL(url, location.href).pathname.indexOf("/assets/") !== -1) recover();
},
true
);
@@ -10,7 +10,6 @@ import {
} from "@heroicons/react/20/solid";
import type { Prisma } from "@trigger.dev/database";
import { z } from "zod";
import { logger } from "~/services/logger.server";
import { cn } from "~/utils/cn";
export const AvatarType = z.enum(["icon", "letters", "image"]);
@@ -45,7 +44,7 @@ export function parseAvatar(json: Prisma.JsonValue, defaultAvatar: Avatar): Avat
const parsed = AvatarData.safeParse(json);
if (!parsed.success) {
logger.error("Invalid org avatar", { json, error: parsed.error });
console.error("Invalid org avatar", { json, error: parsed.error });
return defaultAvatar;
}
@@ -1,5 +1,8 @@
import type { DataPoint } from "regression";
import { linear } from "regression";
// Default-import: regression is CJS and its named exports aren't statically
// analyzable under ESM interop.
import regression from "regression";
const { linear } = regression;
import type { PrismaClientOrTransaction } from "~/db.server";
import { env } from "~/env.server";
import { clickhouseFactory } from "~/services/clickhouse/clickhouseFactoryInstance.server";
+5 -3
View File
@@ -1,11 +1,14 @@
import type { LinksFunction, LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import type { ShouldRevalidateFunction } from "@remix-run/react";
import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from "@remix-run/react";
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "@remix-run/react";
import { type UseDataFunctionReturn, typedjson, useTypedLoaderData } from "remix-typedjson";
import { ExternalScripts } from "remix-utils/external-scripts";
import type { ToastMessage } from "~/models/message.server";
import { commitSession, getSession } from "~/models/message.server";
import tailwindStylesheetUrl from "~/tailwind.css";
// Fonts imported here so Vite rebases the urls and emits the woff2 assets
import "non.geist";
import "non.geist/mono";
import tailwindStylesheetUrl from "~/tailwind.css?url";
import { RouteErrorDisplay } from "./components/ErrorDisplay";
import { StaleAssetRecovery } from "./components/StaleAssetRecovery";
import { AppContainer, MainCenteredContainer } from "./components/layout/AppLayout";
@@ -145,7 +148,6 @@ export default function App() {
<ScrollRestoration />
<ExternalScripts />
<Scripts />
<LiveReload />
</body>
</html>
</>
@@ -9,7 +9,7 @@ const ParamsSchema = z.object({
errorId: z.string(),
});
export const { action, loader } = createActionApiRoute(
const route = createActionApiRoute(
{
params: ParamsSchema,
body: IgnoreErrorRequestBody,
@@ -56,3 +56,6 @@ export const { action, loader } = createActionApiRoute(
return json(updated);
}
);
export const action = route.action;
export const loader = route.loader;
@@ -9,7 +9,7 @@ const ParamsSchema = z.object({
errorId: z.string(),
});
export const { action, loader } = createActionApiRoute(
const route = createActionApiRoute(
{
params: ParamsSchema,
body: ResolveErrorRequestBody,
@@ -48,3 +48,6 @@ export const { action, loader } = createActionApiRoute(
return json(updated);
}
);
export const action = route.action;
export const loader = route.loader;
@@ -8,7 +8,7 @@ const ParamsSchema = z.object({
errorId: z.string(),
});
export const { action, loader } = createActionApiRoute(
const route = createActionApiRoute(
{
params: ParamsSchema,
method: "POST",
@@ -40,3 +40,6 @@ export const { action, loader } = createActionApiRoute(
return json(updated);
}
);
export const action = route.action;
export const loader = route.loader;
@@ -13,7 +13,7 @@ const BodySchema = z.object({
taskIdentifier: z.string().min(1, "Task identifier is required"),
});
export const { action } = createActionApiRoute(
const route = createActionApiRoute(
{
params: ParamsSchema,
body: BodySchema,
@@ -50,3 +50,7 @@ export const { action } = createActionApiRoute(
}
}
);
export const action = route.action;
// The builder's loader handles CORS OPTIONS preflight
export const loader = route.loader;
@@ -10,7 +10,8 @@ import {
createLoaderPATApiRoute,
} from "~/services/routeBuilders/apiBuilder.server";
import { ServiceValidationError } from "~/v3/services/common.server";
import { isCuid } from "cuid";
import cuid from "cuid";
const { isCuid } = cuid;
const ParamsSchema = z.object({
orgParam: z.string(),
@@ -10,7 +10,7 @@ const BodySchema = z.object({
concurrencyLimit: z.number().int().min(0).max(100000),
});
export const { action } = createActionApiRoute(
const route = createActionApiRoute(
{
body: BodySchema,
params: z.object({
@@ -73,3 +73,7 @@ export const { action } = createActionApiRoute(
);
}
);
export const action = route.action;
// The builder's loader answers non-POST methods with a 405
export const loader = route.loader;
@@ -9,7 +9,7 @@ const BodySchema = z.object({
type: RetrieveQueueType.default("id"),
});
export const { action } = createActionApiRoute(
const route = createActionApiRoute(
{
body: BodySchema,
params: z.object({
@@ -73,3 +73,7 @@ export const { action } = createActionApiRoute(
);
}
);
export const action = route.action;
// The builder's loader answers non-POST methods with a 405
export const loader = route.loader;
@@ -9,7 +9,7 @@ const BodySchema = z.object({
action: z.enum(["pause", "resume"]),
});
export const { action } = createActionApiRoute(
const route = createActionApiRoute(
{
body: BodySchema,
params: z.object({
@@ -44,3 +44,7 @@ export const { action } = createActionApiRoute(
return json(q);
}
);
export const action = route.action;
// The builder's loader answers non-POST methods with a 405
export const loader = route.loader;
@@ -1,21 +1,18 @@
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
import { json } from "@remix-run/server-runtime";
import { tryCatch } from "@trigger.dev/core/utils";
import type { RunMetadataChangeOperation } from "@trigger.dev/core/v3/schemas";
import { UpdateMetadataRequestBody } from "@trigger.dev/core/v3";
import { z } from "zod";
import { $replica } from "~/db.server";
// Aliased to avoid shadowing the local `env: AuthenticatedEnvironment`
// parameter the route handler and `routeOperationsToRun` use.
// Aliased to avoid shadowing the local `env` parameter in the handler.
import { env as appEnv } from "~/env.server";
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { authenticateApiRequest } from "~/services/apiAuth.server";
import { logger } from "~/services/logger.server";
import { updateMetadataService } from "~/services/metadata/updateMetadataInstance.server";
import { publishChangeRecord } from "~/services/realtime/runChangeNotifierInstance.server";
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
import { ServiceValidationError } from "~/v3/services/common.server";
import { applyMetadataMutationToBufferedRun } from "~/v3/mollifier/applyMetadataMutation.server";
import { routeOperationsToRun } from "~/v3/mollifier/routeOperationsToRun.server";
import { findRunByIdWithMollifierFallback } from "~/v3/mollifier/readFallback.server";
import { runStore } from "~/v3/runStore.server";
@@ -80,114 +77,6 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
return json({ error: "Run not found" }, { status: 404 });
}
// Route parent/root operations to the existing PG service by directly
// invoking it against the parent/root runId. The service ingests via
// its batching worker, which targets PG by id. If the parent/root is
// itself buffered we recurse through our buffered-mutation helper.
// `_ingestion_only` flag: a synthetic body that has the operations
// promoted to top-level `operations` so the service applies them to
// `targetRunId` directly.
// Exported so the silent-failure logging behaviour can be unit-tested.
// The route handler itself isn't an attractive test target (createActionApiRoute
// wraps it in auth + body parsing + error-handler middleware), but the
// fan-out helper carries the load-bearing logic — including the ops-
// visibility branch this change adds.
export async function routeOperationsToRun(
targetRunId: string | undefined,
operations: RunMetadataChangeOperation[] | undefined,
env: AuthenticatedEnvironment
): Promise<void> {
if (!targetRunId || !operations || operations.length === 0) return;
// Try PG first via the existing service (this is how parent/root
// operations have always landed; preserve that). Accepts the full
// AuthenticatedEnvironment so we don't have to recover the unsafe
// `as unknown` cast that the previous narrowed `{ id, organizationId }`
// signature forced on us.
//
// Two non-success outcomes from `call`:
// * throws — PG threw (e.g. "Cannot update metadata for a completed
// run", or a transient PG outage).
// * resolves with undefined — PG row didn't exist (the target may be
// buffered, not yet materialised).
// Either way we want to try the buffer fallback below; treating the
// undefined-return as success would make the fallback unreachable.
const [error, result] = await tryCatch(
updateMetadataService.call(targetRunId, { operations }, env)
);
if (!error && result !== undefined) {
// The parent/root run changed too — wake its live feeds (only when something was
// actually written here; buffered writes publish from the flusher).
if (result.updatedAtMs !== undefined) {
publishChangeRecord({
runId: result.runId,
envId: env.id,
tags: result.runTags,
batchId: result.batchId,
updatedAtMs: result.updatedAtMs,
});
}
return;
}
if (error) {
// PG threw — auxiliary op, stay best-effort and don't surface this
// to the caller (the caller's primary mutation already landed). But
// warn so a genuine PG outage on these ops isn't invisible.
logger.warn("metadata route: parent/root PG op failed", {
targetRunId,
error: error instanceof Error ? error.message : String(error),
});
}
// Buffer fallback only makes sense for friendlyId-keyed entries. The
// PG-side parent/root IDs are internal cuids; the buffer keys entries
// by friendlyId, so passing the internal id would silently no-op.
// Skip explicitly — a buffered child's parent is always materialised
// in PG already (a buffered run hasn't executed, so it can't have
// triggered the child), so the buffered-parent branch isn't actually
// reachable. Treating the no-op as intentional rather than incidental.
if (!targetRunId.startsWith("run_")) return;
// Best-effort buffer fallback. Wrap so a transient Redis throw on
// this auxiliary op can't 500 the request after the primary mutation
// already succeeded.
const [bufferError, bufferOutcome] = await tryCatch(
applyMetadataMutationToBufferedRun({
runId: targetRunId,
environmentId: env.id,
organizationId: env.organizationId,
maximumSize: appEnv.TASK_RUN_METADATA_MAXIMUM_SIZE,
maxRetries: appEnv.TRIGGER_MOLLIFIER_METADATA_MAX_RETRIES,
backoffBaseMs: appEnv.TRIGGER_MOLLIFIER_METADATA_BACKOFF_BASE_MS,
backoffStepMs: appEnv.TRIGGER_MOLLIFIER_METADATA_BACKOFF_STEP_MS,
body: { operations },
})
);
if (bufferError) {
logger.warn("metadata route: buffer fallback for parent/root op failed", {
targetRunId,
error: bufferError instanceof Error ? bufferError.message : String(bufferError),
});
return;
}
// `applyMetadataMutationToBufferedRun` reports non-throw failures via
// its returned outcome kind: `not_found`, `busy`, `version_exhausted`,
// `metadata_too_large`. Without inspecting `.kind`, the parent/root
// operation can silently disappear — no PG row landed it (handled
// above) and the buffer rejected it for one of these reasons but the
// helper returned cleanly. Surface a warn log per non-success branch
// so ops can trace why a parent/root op went missing. The customer's
// primary mutation has already succeeded by this point; this remains
// best-effort, so we still don't bubble these to the response.
if (bufferOutcome && bufferOutcome.kind !== "applied") {
logger.warn("metadata route: parent/root buffer op did not apply", {
targetRunId,
kind: bufferOutcome.kind,
});
}
}
const { action } = createActionApiRoute(
{
params: ParamsSchema,
@@ -40,7 +40,7 @@ function sessionResource(
return anyResource([...ids].map((id) => ({ type: "sessions" as const, id })));
}
export const { action } = createActionApiRoute(
const route = createActionApiRoute(
{
...routeConfig,
method: "PUT",
@@ -94,3 +94,5 @@ export const loader = createLoaderApiRoute(
return json({ presignedUrl: signed.url });
}
);
export const action = route.action;
@@ -9,12 +9,12 @@ import { commitAuthenticatedSession } from "~/services/sessionDuration.server";
import { trackAndClearReferralSource } from "~/services/referralSource.server";
import { appendRedirectTo, ssoRedirectFromAuthError } from "~/services/ssoAutoDiscovery.server";
import type { AuthUser } from "~/services/authUser";
import { redirectCookie } from "./auth.github";
import { githubRedirectCookie } from "~/services/redirectCookies.server";
import { sanitizeRedirectPath } from "~/utils";
export let loader: LoaderFunction = async ({ request }) => {
const cookie = request.headers.get("Cookie");
const redirectValue = await redirectCookie.parse(cookie);
const redirectValue = await githubRedirectCookie.parse(cookie);
const redirectTo = sanitizeRedirectPath(redirectValue);
// The SSO auto-discovery gate runs inside the strategy's verify
+3 -10
View File
@@ -1,6 +1,6 @@
import { type ActionFunction, type LoaderFunction, redirect, createCookie } from "@remix-run/node";
import { type ActionFunction, type LoaderFunction, redirect } from "@remix-run/node";
import { authenticator } from "~/services/auth.server";
import { env } from "~/env.server";
import { githubRedirectCookie } from "~/services/redirectCookies.server";
import { sanitizeRedirectPath } from "~/utils";
export let loader: LoaderFunction = () => redirect("/login");
@@ -23,15 +23,8 @@ export let action: ActionFunction = async ({ request }) => {
if (error instanceof Response) {
// we need to append a Set-Cookie header with a cookie storing the
// returnTo value (store the sanitized path)
error.headers.append("Set-Cookie", await redirectCookie.serialize(safeRedirect));
error.headers.append("Set-Cookie", await githubRedirectCookie.serialize(safeRedirect));
}
throw error;
}
};
export const redirectCookie = createCookie("redirect-to", {
maxAge: 60 * 60, // 1 hour
httpOnly: true,
sameSite: "lax",
secure: env.NODE_ENV === "production",
});
@@ -9,12 +9,12 @@ import { commitAuthenticatedSession } from "~/services/sessionDuration.server";
import { trackAndClearReferralSource } from "~/services/referralSource.server";
import { appendRedirectTo, ssoRedirectFromAuthError } from "~/services/ssoAutoDiscovery.server";
import type { AuthUser } from "~/services/authUser";
import { redirectCookie } from "./auth.google";
import { googleRedirectCookie } from "~/services/redirectCookies.server";
import { sanitizeRedirectPath } from "~/utils";
export let loader: LoaderFunction = async ({ request }) => {
const cookie = request.headers.get("Cookie");
const redirectValue = await redirectCookie.parse(cookie);
const redirectValue = await googleRedirectCookie.parse(cookie);
const redirectTo = sanitizeRedirectPath(redirectValue);
// The SSO auto-discovery gate runs inside the strategy's verify
+3 -10
View File
@@ -1,6 +1,6 @@
import { type ActionFunction, type LoaderFunction, redirect, createCookie } from "@remix-run/node";
import { type ActionFunction, type LoaderFunction, redirect } from "@remix-run/node";
import { authenticator } from "~/services/auth.server";
import { env } from "~/env.server";
import { googleRedirectCookie } from "~/services/redirectCookies.server";
import { sanitizeRedirectPath } from "~/utils";
export let loader: LoaderFunction = () => redirect("/login");
@@ -23,15 +23,8 @@ export let action: ActionFunction = async ({ request }) => {
if (error instanceof Response) {
// we need to append a Set-Cookie header with a cookie storing the
// returnTo value (store the sanitized path)
error.headers.append("Set-Cookie", await redirectCookie.serialize(safeRedirect));
error.headers.append("Set-Cookie", await googleRedirectCookie.serialize(safeRedirect));
}
throw error;
}
};
export const redirectCookie = createCookie("google-redirect-to", {
maxAge: 60 * 60, // 1 hour
httpOnly: true,
sameSite: "lax",
secure: env.NODE_ENV === "production",
});
@@ -0,0 +1,19 @@
import { createCookie } from "@remix-run/node";
import { env } from "~/env.server";
// Post-auth redirect cookies. Kept in a .server module: Vite can't strip
// non-standard route exports that pull in server-only code.
export const githubRedirectCookie = createCookie("redirect-to", {
maxAge: 60 * 60, // 1 hour
httpOnly: true,
sameSite: "lax",
secure: env.NODE_ENV === "production",
});
export const googleRedirectCookie = createCookie("google-redirect-to", {
maxAge: 60 * 60, // 1 hour
httpOnly: true,
sameSite: "lax",
secure: env.NODE_ENV === "production",
});
-3
View File
@@ -1,6 +1,3 @@
@import url("non.geist");
@import url("non.geist/mono");
@import "react-grid-layout/css/styles.css" layer(base);
@import "react-resizable/css/styles.css" layer(base);
@@ -3,29 +3,34 @@ import { Counter, Gauge } from "prom-client";
import { metricsRegister } from "~/metrics.server";
import { logger } from "~/services/logger.server";
import { signalsEmitter } from "~/services/signals.server";
import { singleton } from "~/utils/singleton";
const loadFailures = new Counter({
name: "reloading_registry_load_failures_total",
help: "Failed loads of a reloading registry",
labelNames: ["name"],
registers: [metricsRegister],
});
const lastSuccessfulLoadAt = new Gauge({
name: "reloading_registry_last_successful_load_timestamp_seconds",
help: "Unix time of the last successful registry load (staleness signal)",
labelNames: ["name"],
registers: [metricsRegister],
});
// 0 until the first successful load, then 1. Starts at 0 (not absent) so a
// never-loaded registry is an alertable series, distinct from "feature off".
const registryLoaded = new Gauge({
name: "reloading_registry_loaded",
help: "1 once the registry has loaded at least once, else 0 (0 = serving cold fallback)",
labelNames: ["name"],
registers: [metricsRegister],
});
// singleton: module-scope registrations double-register under Vite dev HMR
const { loadFailures, lastSuccessfulLoadAt, registryLoaded } = singleton(
"reloadingRegistryMetrics",
() => ({
loadFailures: new Counter({
name: "reloading_registry_load_failures_total",
help: "Failed loads of a reloading registry",
labelNames: ["name"],
registers: [metricsRegister],
}),
lastSuccessfulLoadAt: new Gauge({
name: "reloading_registry_last_successful_load_timestamp_seconds",
help: "Unix time of the last successful registry load (staleness signal)",
labelNames: ["name"],
registers: [metricsRegister],
}),
// 0 until the first successful load, then 1. Starts at 0 (not absent) so a
// never-loaded registry is an alertable series, distinct from "feature off".
registryLoaded: new Gauge({
name: "reloading_registry_loaded",
help: "1 once the registry has loaded at least once, else 0 (0 = serving cold fallback)",
labelNames: ["name"],
registers: [metricsRegister],
}),
})
);
export type ReloadingRegistry<T> = {
isReady: Promise<void>;
@@ -0,0 +1,116 @@
import { tryCatch } from "@trigger.dev/core/utils";
import type { RunMetadataChangeOperation } from "@trigger.dev/core/v3/schemas";
import { env as appEnv } from "~/env.server";
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { logger } from "~/services/logger.server";
import { updateMetadataService } from "~/services/metadata/updateMetadataInstance.server";
import { publishChangeRecord } from "~/services/realtime/runChangeNotifierInstance.server";
import { applyMetadataMutationToBufferedRun } from "./applyMetadataMutation.server";
// Route parent/root operations to the existing PG service by directly
// invoking it against the parent/root runId. The service ingests via
// its batching worker, which targets PG by id. If the parent/root is
// itself buffered we recurse through our buffered-mutation helper.
// `_ingestion_only` flag: a synthetic body that has the operations
// promoted to top-level `operations` so the service applies them to
// `targetRunId` directly.
// Exported so the silent-failure logging behaviour can be unit-tested.
// The route handler itself isn't an attractive test target (createActionApiRoute
// wraps it in auth + body parsing + error-handler middleware), but the
// fan-out helper carries the load-bearing logic — including the ops-
// visibility branch this change adds.
export async function routeOperationsToRun(
targetRunId: string | undefined,
operations: RunMetadataChangeOperation[] | undefined,
env: AuthenticatedEnvironment
): Promise<void> {
if (!targetRunId || !operations || operations.length === 0) return;
// Try PG first via the existing service (this is how parent/root
// operations have always landed; preserve that). Accepts the full
// AuthenticatedEnvironment so we don't have to recover the unsafe
// `as unknown` cast that the previous narrowed `{ id, organizationId }`
// signature forced on us.
//
// Two non-success outcomes from `call`:
// * throws — PG threw (e.g. "Cannot update metadata for a completed
// run", or a transient PG outage).
// * resolves with undefined — PG row didn't exist (the target may be
// buffered, not yet materialised).
// Either way we want to try the buffer fallback below; treating the
// undefined-return as success would make the fallback unreachable.
const [error, result] = await tryCatch(
updateMetadataService.call(targetRunId, { operations }, env)
);
if (!error && result !== undefined) {
// The parent/root run changed too — wake its live feeds (only when something was
// actually written here; buffered writes publish from the flusher).
if (result.updatedAtMs !== undefined) {
publishChangeRecord({
runId: result.runId,
envId: env.id,
tags: result.runTags,
batchId: result.batchId,
updatedAtMs: result.updatedAtMs,
});
}
return;
}
if (error) {
// PG threw — auxiliary op, stay best-effort and don't surface this
// to the caller (the caller's primary mutation already landed). But
// warn so a genuine PG outage on these ops isn't invisible.
logger.warn("metadata route: parent/root PG op failed", {
targetRunId,
error: error instanceof Error ? error.message : String(error),
});
}
// Buffer fallback only makes sense for friendlyId-keyed entries. The
// PG-side parent/root IDs are internal cuids; the buffer keys entries
// by friendlyId, so passing the internal id would silently no-op.
// Skip explicitly — a buffered child's parent is always materialised
// in PG already (a buffered run hasn't executed, so it can't have
// triggered the child), so the buffered-parent branch isn't actually
// reachable. Treating the no-op as intentional rather than incidental.
if (!targetRunId.startsWith("run_")) return;
// Best-effort buffer fallback. Wrap so a transient Redis throw on
// this auxiliary op can't 500 the request after the primary mutation
// already succeeded.
const [bufferError, bufferOutcome] = await tryCatch(
applyMetadataMutationToBufferedRun({
runId: targetRunId,
environmentId: env.id,
organizationId: env.organizationId,
maximumSize: appEnv.TASK_RUN_METADATA_MAXIMUM_SIZE,
maxRetries: appEnv.TRIGGER_MOLLIFIER_METADATA_MAX_RETRIES,
backoffBaseMs: appEnv.TRIGGER_MOLLIFIER_METADATA_BACKOFF_BASE_MS,
backoffStepMs: appEnv.TRIGGER_MOLLIFIER_METADATA_BACKOFF_STEP_MS,
body: { operations },
})
);
if (bufferError) {
logger.warn("metadata route: buffer fallback for parent/root op failed", {
targetRunId,
error: bufferError instanceof Error ? bufferError.message : String(bufferError),
});
return;
}
// `applyMetadataMutationToBufferedRun` reports non-throw failures via
// its returned outcome kind: `not_found`, `busy`, `version_exhausted`,
// `metadata_too_large`. Without inspecting `.kind`, the parent/root
// operation can silently disappear — no PG row landed it (handled
// above) and the buffer rejected it for one of these reasons but the
// helper returned cleanly. Surface a warn log per non-success branch
// so ops can trace why a parent/root op went missing. The customer's
// primary mutation has already succeeded by this point; this remains
// best-effort, so we still don't bubble these to the response.
if (bufferOutcome && bufferOutcome.kind !== "applied") {
logger.warn("metadata route: parent/root buffer op did not apply", {
targetRunId,
kind: bufferOutcome.kind,
});
}
}
+1 -5
View File
@@ -2,7 +2,6 @@ import { column, type BucketThreshold, type TableSchema } from "@internal/tsql";
import { z } from "zod";
import { autoFormatSQL } from "~/components/code/TSQLEditor";
import { runFriendlyStatus, runStatusTitleFromStatus } from "~/components/runs/v3/TaskRunStatus";
import { logger } from "~/services/logger.server";
export const QueryScopeSchema = z.enum(["organization", "project", "environment"]);
export type QueryScope = z.infer<typeof QueryScopeSchema>;
@@ -443,10 +442,7 @@ export const runsSchema: TableSchema = {
...column("Array(String)", {
description: "Any bulk actions that operated on this run.",
example: '["bulk_12345678", "bulk_34567890"]',
whereTransform: (value: string) => {
logger.log(`WHERE TRANSFORM: ${value}`);
return value.replace(/^bulk_/, "");
},
whereTransform: (value: string) => value.replace(/^bulk_/, ""),
}),
},
},
+8 -4
View File
@@ -5,11 +5,11 @@
"sideEffects": false,
"scripts": {
"build": "run-s build:** && pnpm run upload:sourcemaps",
"build:remix": "remix build --sourcemap",
"build:remix": "remix vite:build",
"build:server": "esbuild --platform=node --format=cjs ./server.ts --outdir=build --sourcemap",
"build:otlpworker": "esbuild --platform=node --format=cjs --bundle ./app/v3/otlpTransformWorker.ts --outfile=build/otlpTransformWorker.cjs --sourcemap",
"build:sentry": "esbuild --platform=node --format=cjs --outbase=. ./sentry.server.ts ./app/utils/sentryTraceContext.server.ts --outdir=build --sourcemap",
"dev": "cross-env PORT=3030 remix dev -c \"node ./build/server.js\"",
"dev": "cross-env NODE_ENV=development PORT=3030 tsx ./server.ts",
"dev:worker": "cross-env NODE_PATH=../../node_modules/.pnpm/node_modules node ./build/server.js",
"format": "oxfmt .",
"lint": "oxlint -c ../../.oxlintrc.json",
@@ -130,6 +130,7 @@
"@vercel/sdk": "^1.19.1",
"@window-splitter/react": "1.1.3",
"ai": "^6.0.116",
"assert": "^2.1.0",
"assert-never": "^1.2.1",
"aws4fetch": "^1.0.18",
"class-variance-authority": "^0.5.2",
@@ -170,6 +171,7 @@
"p-map": "^6.0.0",
"p-retry": "^4.6.1",
"parse-duration": "^2.1.0",
"pg": "8.15.6",
"posthog-js": "^1.93.3",
"posthog-node": "5.35.6",
"prism-react-renderer": "^2.3.1",
@@ -204,8 +206,9 @@
"superjson": "^2.2.1",
"tailwind-merge": "^3.6.0",
"tailwind-scrollbar-hide": "^4.0.0",
"tw-animate-css": "^1.4.0",
"tiny-invariant": "^1.2.0",
"tw-animate-css": "^1.4.0",
"util": "^0.12.5",
"uuid": "^14.0.0",
"ws": "^8.11.0",
"zod": "3.25.76",
@@ -260,7 +263,8 @@
"tailwindcss": "^4.3.1",
"tsconfig-paths": "^3.14.1",
"tsx": "^4.20.6",
"vite-tsconfig-paths": "^4.0.5"
"vite-tsconfig-paths": "^5.1.4",
"vite": "^6.4.2"
},
"engines": {
"node": ">=18.19.0 || >=20.6.0"
-48
View File
@@ -1,48 +0,0 @@
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
dev: {
port: 8002,
},
// Tailwind v4 runs through PostCSS (@tailwindcss/postcss), not the built-in Remix integration
tailwind: false,
postcss: true,
cacheDirectory: "./node_modules/.cache/remix",
ignoredRouteFiles: ["**/.*"],
serverModuleFormat: "cjs",
serverDependenciesToBundle: [
/^remix-utils.*/,
/^@internal\//, // Bundle all internal packages
/^@trigger\.dev\//, // Bundle all trigger packages
"marked",
"agentcrumbs",
"axios",
"p-limit",
"p-map",
"yocto-queue",
"@unkey/cache",
"@unkey/cache/stores",
"emails",
"highlight.run",
"random-words",
"superjson",
"copy-anything",
"is-what",
"prismjs/components/prism-json",
"prismjs/components/prism-typescript",
"redlock",
"parse-duration",
"uncrypto",
"std-env",
"uuid",
],
browserNodeBuiltinsPolyfill: {
modules: {
path: true,
os: true,
crypto: true,
http2: true,
assert: true,
util: true,
},
},
};
+5
View File
@@ -1,2 +1,7 @@
/// <reference types="@remix-run/dev" />
/// <reference types="vite/client" />
/// <reference types="@remix-run/node/globals" />
// Font packages that resolve to CSS — imported for their side effect only.
declare module "non.geist";
declare module "non.geist/mono";
+54 -22
View File
@@ -1,13 +1,13 @@
import "./sentry.server";
import { createRequestHandler } from "@remix-run/express";
import { broadcastDevReady, logDevReady } from "@remix-run/server-runtime";
import compression from "compression";
import type { Server as EngineServer } from "engine.io";
import express, { type RequestHandler } from "express";
import morgan from "morgan";
import { nanoid } from "nanoid";
import path from "path";
import { pathToFileURL } from "node:url";
import type { Server as IoServer } from "socket.io";
import type { WebSocketServer } from "ws";
import type { RateLimitMiddleware } from "~/services/apiRateLimit.server";
@@ -74,6 +74,12 @@ function installPrimarySignalHandlers() {
});
}
// Bundled to CJS (esbuild rewrites import() to require); vite and the Remix
// server bundle are ESM, so load them via a real dynamic import.
const dynamicImport = new Function("specifier", "return import(specifier)") as (
specifier: string
) => Promise<any>;
if (ENABLE_CLUSTER && cluster.isPrimary) {
process.title = `node webapp-server primary`;
console.log(`[cluster] Primary ${process.pid} is starting with ${WORKERS} workers`);
@@ -92,6 +98,13 @@ if (ENABLE_CLUSTER && cluster.isPrimary) {
installPrimarySignalHandlers();
} else {
startServer().catch((error) => {
console.error("Failed to start server:", error);
process.exit(1);
});
}
async function startServer() {
const app = express();
if (process.env.DISABLE_COMPRESSION !== "1") {
@@ -101,16 +114,32 @@ if (ENABLE_CLUSTER && cluster.isPrimary) {
// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
app.disable("x-powered-by");
// Remix fingerprints its assets so we can cache forever.
app.use("/build", express.static("public/build", { immutable: true, maxAge: "1y" }));
// Stale dev builds can request an old hashed manifest; don't fall through to Remix.
app.use("/build", (_req, res) => {
res.status(404).end();
});
const MODE = process.env.NODE_ENV;
// Everything else (like favicon.ico) is cached for an hour. You may want to be
// more aggressive with this caching.
app.use(express.static("public", { maxAge: "1h" }));
// In development, Vite serves assets (and handles HMR) via middleware.
// Only NODE_ENV=development boots Vite — scripts that run the built server
// without NODE_ENV (start:local, dev:worker) must serve the build.
const viteDevServer =
MODE === "development"
? await dynamicImport("vite").then((vite) =>
vite.createServer({ server: { middlewareMode: true } })
)
: undefined;
if (viteDevServer) {
app.use(viteDevServer.middlewares);
} else {
// Vite fingerprints its assets so we can cache forever.
app.use("/assets", express.static("build/client/assets", { immutable: true, maxAge: "1y" }));
// Stale clients can request an old hashed asset; hard-404 instead of falling
// through to Remix and answering a .js request with HTML.
app.use("/assets", (_req, res) => {
res.status(404).end();
});
// Everything else (like favicon.ico) is cached for an hour. You may want to be
// more aggressive with this caching.
app.use(express.static("build/client", { maxAge: "1h" }));
}
// On high-volume machine-ingest services (e.g. otel) the per-request access
// log dominates log volume. HTTP_ACCESS_LOG_DISABLED suppresses successful
@@ -127,9 +156,17 @@ if (ENABLE_CLUSTER && cluster.isPrimary) {
? `node webapp-worker-${cluster.isWorker ? cluster.worker?.id : "solo"}`
: "node webapp-server";
const MODE = process.env.NODE_ENV;
const BUILD_DIR = path.join(process.cwd(), "build");
const build = require(BUILD_DIR);
const loadBuild = () => {
if (viteDevServer) {
return viteDevServer.ssrLoadModule("virtual:remix/server-build");
}
return dynamicImport(
pathToFileURL(path.join(process.cwd(), "build", "server", "index.mjs")).href
);
};
// Boots the entry.server singletons (socket.io, wss, rate limiters).
const build = await loadBuild();
const port = process.env.REMIX_APP_PORT || process.env.PORT || 3000;
@@ -207,7 +244,7 @@ if (ENABLE_CLUSTER && cluster.isPrimary) {
"*",
// @ts-ignore
createRequestHandler({
build,
build: viteDevServer ? loadBuild : build,
mode: MODE,
})
);
@@ -220,7 +257,7 @@ if (ENABLE_CLUSTER && cluster.isPrimary) {
"/healthcheck",
// @ts-ignore
createRequestHandler({
build,
build: viteDevServer ? loadBuild : build,
mode: MODE,
})
);
@@ -232,12 +269,6 @@ if (ENABLE_CLUSTER && cluster.isPrimary) {
ENABLE_CLUSTER && cluster.isWorker ? ` [worker ${cluster.worker?.id}/${process.pid}]` : ""
}`
);
if (MODE === "development") {
broadcastDevReady(build)
.then(() => logDevReady(build))
.catch(console.error);
}
});
server.keepAliveTimeout = HTTP_KEEPALIVE_TIMEOUT_MS;
@@ -259,6 +290,8 @@ if (ENABLE_CLUSTER && cluster.isPrimary) {
console.log("Express server closed gracefully.");
}
});
// Dev-only: release Vite's file watchers and HMR websocket
viteDevServer?.close();
}
process.on("SIGTERM", closeServer);
@@ -304,7 +337,6 @@ if (ENABLE_CLUSTER && cluster.isPrimary) {
});
});
} else {
require(BUILD_DIR);
console.log(`✅ app ready (skipping http server)`);
}
}
@@ -52,7 +52,7 @@ vi.mock("~/services/logger.server", () => ({
},
}));
import { routeOperationsToRun } from "~/routes/api.v1.runs.$runId.metadata";
import { routeOperationsToRun } from "~/v3/mollifier/routeOperationsToRun.server";
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
const env = {
+66
View File
@@ -0,0 +1,66 @@
import { vitePlugin as remix } from "@remix-run/dev";
import { defaultClientConditions, defaultServerConditions, defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [
remix({
ignoredRouteFiles: ["**/.*"],
// .mjs so the CJS server.ts wrapper can dynamic-import it
serverBuildFile: "index.mjs",
}),
tsconfigPaths(),
],
resolve: {
// Resolve workspace packages to TS source (same condition the CLI uses)
conditions: ["@triggerdotdev/source", ...defaultClientConditions],
// Browser polyfills for node builtins used by client deps (antlr4ts)
alias: [
{ find: /^assert$/, replacement: "assert/" },
{ find: /^util$/, replacement: "util/" },
],
},
optimizeDeps: {
// Crawl all routes up front - mid-session re-optimization duplicates React
entries: ["./app/entry.client.tsx", "./app/root.tsx", "./app/routes/**/*.{ts,tsx}"],
esbuildOptions: {
// node globals for prebundled CJS deps (client-only by construction)
define: { global: "globalThis" },
inject: ["./vite/node-globals-shim.js"],
},
},
server: {
warmup: {
clientFiles: ["./app/entry.client.tsx", "./app/root.tsx", "./app/components/**/*.tsx"],
ssrFiles: ["./app/entry.server.tsx", "./app/root.tsx"],
},
},
build: {
sourcemap: true,
rollupOptions: {
// Prisma wrappers and pg have CJS/native pieces Rollup can't inline
external: [/^@trigger\.dev\/database$/, /^@internal\/run-ops-database$/, /^pg$/],
},
},
ssr: {
resolve: {
conditions: ["@triggerdotdev/source", ...defaultServerConditions],
externalConditions: ["@triggerdotdev/source", "node"],
},
// CJS Prisma clients and native pg must load through node
external: ["@trigger.dev/database", "@internal/run-ops-database", "pg"],
// CJS deps whose named exports node's ESM interop can't detect
noExternal: [
/^@radix-ui\//,
"react-use",
"cron-parser",
"@fingerprintjs/fingerprintjs-pro-react",
"@kapaai/react-sdk",
"@fingerprintjs/fingerprintjs-pro",
"@fingerprintjs/fingerprintjs-pro-spa",
],
optimizeDeps: {
include: ["cron-parser"],
},
},
});
+10
View File
@@ -0,0 +1,10 @@
// Minimal `process` stand-in injected into prebundled browser deps
// (see vite.config.ts optimizeDeps). Client-only.
export const process = {
env: {},
browser: true,
version: "",
platform: "browser",
cwd: () => "/",
nextTick: (fn, ...args) => setTimeout(() => fn(...args), 0),
};
+6
View File
@@ -91,6 +91,12 @@ COPY --from=dev-deps --chown=node:node /triggerdotdev/internal-packages/database
# Run-ops Prisma client (query engine + client). Only constructed when the split is enabled,
# but the image must carry it so a split-on deployment doesn't hit a missing query engine.
COPY --from=dev-deps --chown=node:node /triggerdotdev/internal-packages/run-ops-database/generated ./internal-packages/run-ops-database/generated
# These workspace packages stay external to the Vite SSR bundle, so their
# built entry points must ship in the image (core is the sdk's runtime dep).
COPY --from=builder --chown=node:node /triggerdotdev/internal-packages/database/dist ./internal-packages/database/dist
COPY --from=builder --chown=node:node /triggerdotdev/internal-packages/run-ops-database/dist ./internal-packages/run-ops-database/dist
COPY --from=builder --chown=node:node /triggerdotdev/packages/trigger-sdk/dist ./packages/trigger-sdk/dist
COPY --from=builder --chown=node:node /triggerdotdev/packages/core/dist ./packages/core/dist
COPY --from=builder --chown=node:node /triggerdotdev/apps/webapp/build/server.js ./apps/webapp/build/server.js
COPY --from=builder --chown=node:node /triggerdotdev/apps/webapp/build ./apps/webapp/build
COPY --from=builder --chown=node:node /triggerdotdev/apps/webapp/public ./apps/webapp/public
+4 -4
View File
@@ -231,10 +231,10 @@ services:
"--query",
"SELECT 1",
]
interval: "3s"
timeout: "5s"
retries: "5"
start_period: "10s"
interval: 3s
timeout: 5s
retries: 5
start_period: 10s
clickhouse_migrator:
build:
+2 -1
View File
@@ -91,7 +91,8 @@ class LazyController implements RoleBaseAccessController {
}
const moduleName = "@triggerdotdev/plugins/rbac";
try {
const module = await import(moduleName);
// Optional plugin, resolved at runtime only
const module = await import(/* @vite-ignore */ moduleName);
const plugin: RoleBasedAccessControlPlugin = module.default;
console.log("RBAC: using plugin implementation");
return plugin.create({
@@ -1,8 +1,12 @@
// import { default: Redlock } from "redlock";
const { default: Redlock } = require("redlock");
import { AsyncLocalStorage } from "async_hooks";
import type { Redis } from "@internal/redis";
import type * as redlock from "redlock";
import * as redlockModule from "redlock";
// redlock is CJS with `exports.default`; probe the interop shapes instead of
// a bare require(), which breaks in ESM module runners.
const Redlock = ((redlockModule as any).default?.default ??
(redlockModule as any).default ??
redlockModule) as typeof redlockModule.default;
import { tryCatch } from "@trigger.dev/core";
import type { Logger } from "@trigger.dev/core/logger";
import type { Tracer, Meter, ObservableResult, Attributes, Histogram } from "@internal/tracing";
@@ -34,12 +38,12 @@ export class LockAcquisitionTimeoutError extends Error {
interface LockContext {
resources: string;
signal: redlock.RedlockAbortSignal;
signal: redlockModule.RedlockAbortSignal;
lockType: string;
}
interface ManualLockContext {
lock: redlock.Lock;
lock: redlockModule.Lock;
timeout: NodeJS.Timeout | null | undefined;
extension: Promise<void> | undefined;
}
@@ -60,7 +64,7 @@ export interface LockRetryConfig {
}
export class RunLocker {
private redlock: InstanceType<typeof redlock.default>;
private redlock: InstanceType<typeof redlockModule.default>;
private asyncLocalStorage: AsyncLocalStorage<LockContext>;
private logger: Logger;
private tracer: Tracer;
@@ -216,7 +220,7 @@ export class RunLocker {
let totalWaitTime = 0;
// Retry the lock acquisition with exponential backoff
let lock: redlock.Lock | undefined;
let lock: redlockModule.Lock | undefined;
let lastError: Error | undefined;
for (let attempt = 0; attempt <= maxAttempts; attempt++) {
@@ -346,7 +350,7 @@ export class RunLocker {
// Create an AbortController for our signal
const controller = new AbortController();
const signal = controller.signal as redlock.RedlockAbortSignal;
const signal = controller.signal as redlockModule.RedlockAbortSignal;
const manualContext: ManualLockContext = {
lock,
@@ -425,7 +429,7 @@ export class RunLocker {
#setupAutoExtension(
context: ManualLockContext,
duration: number,
signal: redlock.RedlockAbortSignal,
signal: redlockModule.RedlockAbortSignal,
controller: AbortController
): void {
if (this.automaticExtensionThreshold > duration - 100) {
@@ -460,7 +464,7 @@ export class RunLocker {
async #extendLock(
context: ManualLockContext,
duration: number,
signal: redlock.RedlockAbortSignal,
signal: redlockModule.RedlockAbortSignal,
controller: AbortController,
scheduleNext: () => void
): Promise<void> {
+2 -1
View File
@@ -56,7 +56,8 @@ export class LazyController implements SsoController {
}
const moduleName = "@triggerdotdev/plugins/sso";
const importer =
options?.importer ?? ((m: string) => import(m) as Promise<{ default: SsoPlugin }>);
options?.importer ??
((m: string) => import(/* @vite-ignore */ m) as Promise<{ default: SsoPlugin }>);
try {
const module = await importer(moduleName);
const plugin: SsoPlugin = module.default;
@@ -42,10 +42,10 @@ async function register(): Promise<void> {
if (typeof aiMod.registerTelemetry !== "function") {
return; // v5 / v6 — `ai` core emits spans itself, nothing to wire.
}
// Computed specifier keeps the optional peer out of static bundler
// resolution; resolves at runtime only when the customer installed it.
// Computed specifier + @vite-ignore keep the optional peer out of static
// bundler resolution; resolves at runtime only when installed.
const otelSpecifier = ["@ai-sdk", "otel"].join("/");
const otelMod: any = await import(otelSpecifier).catch(() => null);
const otelMod: any = await import(/* @vite-ignore */ otelSpecifier).catch(() => null);
if (typeof otelMod?.OpenTelemetry !== "function") {
return; // optional peer not installed
}
+81 -42
View File
@@ -516,6 +516,9 @@ importers:
ai:
specifier: 6.0.116
version: 6.0.116(zod@3.25.76)
assert:
specifier: ^2.1.0
version: 2.1.0
assert-never:
specifier: ^1.2.1
version: 1.2.1
@@ -636,6 +639,9 @@ importers:
parse-duration:
specifier: ^2.1.0
version: 2.1.4
pg:
specifier: 8.15.6
version: 8.15.6
posthog-js:
specifier: ^1.93.3
version: 1.93.3
@@ -744,6 +750,9 @@ importers:
tw-animate-css:
specifier: ^1.4.0
version: 1.4.0
util:
specifier: ^0.12.5
version: 0.12.5
uuid:
specifier: ^14.0.0
version: 14.0.0
@@ -901,9 +910,12 @@ importers:
tsx:
specifier: ^4.20.6
version: 4.20.6
vite:
specifier: ^6.4.2
version: 6.4.2(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.20.6)(yaml@2.9.0)
vite-tsconfig-paths:
specifier: ^4.0.5
version: 4.0.5(typescript@6.0.3)
specifier: ^5.1.4
version: 5.1.4(typescript@6.0.3)(vite@6.4.2(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.20.6)(yaml@2.9.0))
docs: {}
@@ -8495,6 +8507,9 @@ packages:
assert-never@1.2.1:
resolution: {integrity: sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==}
assert@2.1.0:
resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==}
assertion-error@2.0.1:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
@@ -8786,10 +8801,6 @@ packages:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
call-bind@1.0.7:
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
engines: {node: '>= 0.4'}
call-bind@1.0.8:
resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
engines: {node: '>= 0.4'}
@@ -9581,10 +9592,6 @@ packages:
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
engines: {node: '>=12'}
define-properties@1.1.4:
resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
engines: {node: '>= 0.4'}
define-properties@1.2.1:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
@@ -11139,6 +11146,10 @@ packages:
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
engines: {node: '>=8'}
is-nan@1.3.2:
resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==}
engines: {node: '>= 0.4'}
is-negative-zero@2.0.2:
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
engines: {node: '>= 0.4'}
@@ -12510,6 +12521,10 @@ packages:
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
engines: {node: '>= 0.4'}
object-is@1.1.6:
resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
engines: {node: '>= 0.4'}
object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
@@ -12873,8 +12888,8 @@ packages:
pg-cloudflare@1.2.7:
resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==}
pg-connection-string@2.8.5:
resolution: {integrity: sha512-Ni8FuZ8yAF+sWZzojvtLE2b03cqjO5jNULcHFfM9ZZ0/JXrgom5pBREbtnAw7oxsxJqHw9Nz/XWORUEL3/IFow==}
pg-connection-string@2.9.1:
resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==}
pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
@@ -12884,8 +12899,8 @@ packages:
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
engines: {node: '>=4'}
pg-pool@3.9.6:
resolution: {integrity: sha512-rFen0G7adh1YmgvrmE5IPIqbb+IgEzENUm+tzm6MLLDSlPRoZVhzU1WdML9PV2W5GOdRA9qBKURlbt1OsXOsPw==}
pg-pool@3.10.1:
resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==}
peerDependencies:
pg: '>=8.0'
@@ -15224,6 +15239,14 @@ packages:
vite-tsconfig-paths@4.0.5:
resolution: {integrity: sha512-/L/eHwySFYjwxoYt1WRJniuK/jPv+WGwgRGBYx3leciR5wBeqntQpUE6Js6+TJemChc+ter7fDBKieyEWDx4yQ==}
vite-tsconfig-paths@5.1.4:
resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==}
peerDependencies:
vite: ^6.4.2
peerDependenciesMeta:
vite:
optional: true
vite@4.4.9:
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -23336,6 +23359,14 @@ snapshots:
assert-never@1.2.1: {}
assert@2.1.0:
dependencies:
call-bind: 1.0.8
is-nan: 1.3.2
object-is: 1.1.6
object.assign: 4.1.5
util: 0.12.5
assertion-error@2.0.1: {}
ast-v8-to-istanbul@1.0.2:
@@ -23700,14 +23731,6 @@ snapshots:
es-errors: 1.3.0
function-bind: 1.1.2
call-bind@1.0.7:
dependencies:
es-define-property: 1.0.1
es-errors: 1.3.0
function-bind: 1.1.2
get-intrinsic: 1.3.0
set-function-length: 1.2.2
call-bind@1.0.8:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -24502,11 +24525,6 @@ snapshots:
define-lazy-prop@3.0.0: {}
define-properties@1.1.4:
dependencies:
has-property-descriptors: 1.0.2
object-keys: 1.1.1
define-properties@1.2.1:
dependencies:
define-data-property: 1.1.4
@@ -26112,7 +26130,7 @@ snapshots:
hast-util-to-jsx-runtime@2.3.6:
dependencies:
'@types/estree': 1.0.8
'@types/estree': 1.0.9
'@types/hast': 3.0.4
'@types/unist': 3.0.3
comma-separated-tokens: 2.0.3
@@ -26442,6 +26460,11 @@ snapshots:
is-interactive@1.0.0: {}
is-nan@1.3.2:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
is-negative-zero@2.0.2: {}
is-negative-zero@2.0.3: {}
@@ -28122,6 +28145,11 @@ snapshots:
object-inspect@1.13.4: {}
object-is@1.1.6:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
object-keys@1.1.1: {}
object.assign@4.1.5:
@@ -28553,13 +28581,13 @@ snapshots:
pg-cloudflare@1.2.7:
optional: true
pg-connection-string@2.8.5: {}
pg-connection-string@2.9.1: {}
pg-int8@1.0.1: {}
pg-numeric@1.0.2: {}
pg-pool@3.9.6(pg@8.15.6):
pg-pool@3.10.1(pg@8.15.6):
dependencies:
pg: 8.15.6
@@ -28587,9 +28615,9 @@ snapshots:
pg@8.15.6:
dependencies:
pg-connection-string: 2.8.5
pg-pool: 3.9.6(pg@8.15.6)
pg-protocol: 1.9.5
pg-connection-string: 2.9.1
pg-pool: 3.10.1(pg@8.15.6)
pg-protocol: 1.10.3
pg-types: 2.2.0
pgpass: 1.0.5
optionalDependencies:
@@ -30245,8 +30273,8 @@ snapshots:
string.prototype.padend@3.1.4:
dependencies:
call-bind: 1.0.7
define-properties: 1.1.4
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.21.1
string.prototype.trim@1.2.9:
@@ -31298,10 +31326,21 @@ snapshots:
- supports-color
- typescript
vite-tsconfig-paths@5.1.4(typescript@6.0.3)(vite@6.4.2(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.20.6)(yaml@2.9.0)):
dependencies:
debug: 4.4.3(supports-color@10.0.0)
globrex: 0.1.2
tsconfck: 3.1.3(typescript@6.0.3)
optionalDependencies:
vite: 6.4.2(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.20.6)(yaml@2.9.0)
transitivePeerDependencies:
- supports-color
- typescript
vite@4.4.9(@types/node@24.13.3)(lightningcss@1.32.0)(terser@5.46.1):
dependencies:
esbuild: 0.18.20
postcss: 8.5.10
postcss: 8.5.15
rollup: 3.29.1
optionalDependencies:
'@types/node': 24.13.3
@@ -31314,9 +31353,9 @@ snapshots:
esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
postcss: 8.5.10
postcss: 8.5.15
rollup: 4.60.1
tinyglobby: 0.2.16
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 24.13.3
fsevents: 2.3.3
@@ -31331,9 +31370,9 @@ snapshots:
esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
postcss: 8.5.10
postcss: 8.5.15
rollup: 4.60.1
tinyglobby: 0.2.16
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 24.13.3
fsevents: 2.3.3
@@ -31348,9 +31387,9 @@ snapshots:
esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
postcss: 8.5.10
postcss: 8.5.15
rollup: 4.60.1
tinyglobby: 0.2.16
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 24.13.3
fsevents: 2.3.3