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:
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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_/, ""),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
};
|
||||
Vendored
+5
@@ -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
@@ -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 = {
|
||||
|
||||
@@ -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"],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -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),
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Generated
+81
-42
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user