(undefined);
+ const writeParam = useRef<((id: number | null) => void) | null>(null);
- const setTeamId = React.useCallback(
- (id: number | undefined) => {
- void setTeamIdRaw(id ?? null);
- },
- [setTeamIdRaw]
- );
+ const setTeamId = useCallback((id: number | undefined) => {
+ // Optimistic: consumers update immediately, the URL follows.
+ setTeamIdState(id);
+ writeParam.current?.(id ?? null);
+ }, []);
+
+ const value = useMemo(() => ({ teamId, setTeamId }), [teamId, setTeamId]);
- const value = React.useMemo(
- () => ({ teamId: teamId ?? undefined, setTeamId }),
- [teamId, setTeamId]
+ return (
+
+
+
+
+ {children}
+
);
+}
+
+function TeamQueryParamSync({
+ onChange,
+ writeParam,
+}: {
+ onChange: (id: number | undefined) => void;
+ writeParam: React.RefObject<((id: number | null) => void) | null>;
+}) {
+ const [param, setParam] = useQueryState("team", parseAsInteger);
+
+ useEffect(() => {
+ writeParam.current = (id) => void setParam(id);
+ }, [setParam, writeParam]);
+
+ useEffect(() => {
+ onChange(param ?? undefined);
+ }, [param, onChange]);
- return {children};
+ return null;
}
diff --git a/apps/web/src/components/ui/sidebar.tsx b/apps/web/src/components/ui/sidebar.tsx
index 208386482..b21655e70 100644
--- a/apps/web/src/components/ui/sidebar.tsx
+++ b/apps/web/src/components/ui/sidebar.tsx
@@ -591,10 +591,13 @@ function SidebarMenuSkeleton({
}: React.ComponentProps<"div"> & {
showIcon?: boolean;
}) {
- // Random width between 50 to 90%.
- const [width] = React.useState(() => {
- return `${Math.floor(Math.random() * 40) + 50}%`;
- });
+ // Random width between 50 to 90%, set after mount so render stays
+ // deterministic (Math.random() during render is disallowed under Cache
+ // Components).
+ const [width, setWidth] = React.useState("70%");
+ React.useEffect(() => {
+ setWidth(`${Math.floor(Math.random() * 40) + 50}%`);
+ }, []);
return (
svc.getMostPlayedHeroes(mapId)))
+ );
+}
+
+export async function getCachedMatchStory(mapId: number, mapDataId: number) {
+ "use cache";
+ cacheLife("max");
+ cacheTag(mapTag(mapId));
+ // A story failure must never break the map page — the tab just hides.
+ return AppRuntime.runPromise(
+ MatchStoryService.pipe(
+ Effect.flatMap((svc) => svc.getMatchStory(mapDataId)),
+ Effect.catchAll(() => Effect.succeed(null))
+ )
+ );
+}
+
+/** Map name + team name shown in the map page frame. Immutable after upload. */
+export async function getCachedMapDetails(mapId: number, mapDataId: number) {
+ "use cache";
+ cacheLife("max");
+ cacheTag(mapTag(mapId));
+ return prisma.matchStart.findFirst({
+ where: { MapDataId: mapDataId },
+ select: { map_name: true, team_1_name: true },
+ orderBy: { id: "asc" },
+ });
+}
+
+/**
+ * Replay code + VOD link for the map page frame. Both are user-editable:
+ * replayCode edits go through update-scrim-options (revalidates the scrim
+ * tag) and VOD edits through /api/vod (revalidates the map tag).
+ */
+export async function getCachedMapRow(scrimId: number, mapId: number) {
+ "use cache";
+ cacheLife("max");
+ cacheTag(mapTag(mapId));
+ cacheTag(scrimTag(scrimId));
+ return prisma.map.findFirst({
+ where: { id: mapId },
+ select: { replayCode: true, vod: true },
+ });
+}
+
+/** Hero bans for the map header. Immutable after upload. */
+export async function getCachedHeroBans(mapId: number, mapDataId: number) {
+ "use cache";
+ cacheLife("max");
+ cacheTag(mapTag(mapId));
+ return prisma.heroBan.findMany({
+ where: { MapDataId: mapDataId },
+ orderBy: { id: "asc" },
+ });
+}
+
+/**
+ * Guest-mode visibility for the map page frame. Toggled via
+ * update-scrim-options, which revalidates the scrim tag.
+ */
+export async function getCachedScrimVisibility(scrimId: number) {
+ "use cache";
+ cacheLife("max");
+ cacheTag(scrimTag(scrimId));
+ return prisma.scrim.findFirst({
+ where: { id: scrimId },
+ select: { guestMode: true },
+ });
+}
+
+/**
+ * Fight-initiation analysis for the initiation tab. Derived purely from
+ * immutable match events; failures degrade to "not available" so a bad log
+ * never breaks the tab (and errors are not cached).
+ */
+export async function getCachedFightInitiation(
+ mapId: number,
+ mapDataId: number
+) {
+ "use cache";
+ cacheLife("hours");
+ cacheTag(mapTag(mapId));
+ return getFightInitiationForMapData(mapDataId);
+}
diff --git a/apps/web/src/data/cached/map-viewer.ts b/apps/web/src/data/cached/map-viewer.ts
new file mode 100644
index 000000000..eba56f7e8
--- /dev/null
+++ b/apps/web/src/data/cached/map-viewer.ts
@@ -0,0 +1,52 @@
+import "server-only";
+
+import { AppRuntime } from "@/data/runtime";
+import { UserService } from "@/data/user";
+import { auth, isAuthedToViewMap, type Session } from "@/lib/auth";
+import { getColorblindMode } from "@/lib/server-utils";
+import type { User } from "@/generated/prisma/browser";
+import { Effect } from "effect";
+import { cacheLife } from "next/cache";
+
+export type MapViewerContext =
+ | { canView: false }
+ | {
+ canView: true;
+ session: Session | null;
+ user: User | null;
+ team1Color: string;
+ team2Color: string;
+ };
+
+/**
+ * The per-viewer request work for a map page render — access gate, session,
+ * user row, and colorblind palette — in one `"use cache: private"` scope.
+ *
+ * Private caching serves two purposes: it lets Next's runtime prefetch
+ * (`prefetch = 'allow-runtime'` on the map page) execute this and everything
+ * behind it ahead of a tab click (uncached session/DB reads would otherwise
+ * abort the prefetch at the surrounding Suspense boundary), and it lets the
+ * browser reuse the result across tab navigations within the stale window.
+ * Results live only in the browser's memory, never on the server.
+ */
+export async function getMapViewerContext(
+ scrimId: number,
+ mapId: number
+): Promise {
+ "use cache: private";
+ // stale >= 30s is required for runtime prefetching; "minutes" keeps access
+ // revocation lag bounded at the profile's 5-minute client stale window.
+ cacheLife("minutes");
+
+ if (!(await isAuthedToViewMap(scrimId, mapId))) {
+ return { canView: false };
+ }
+
+ const session = await auth();
+ const user = await AppRuntime.runPromise(
+ UserService.pipe(Effect.flatMap((svc) => svc.getUser(session?.user?.email)))
+ );
+ const { team1, team2 } = await getColorblindMode(user?.id ?? "");
+
+ return { canView: true, session, user, team1Color: team1, team2Color: team2 };
+}
diff --git a/apps/web/src/data/cached/scrim-cache.ts b/apps/web/src/data/cached/scrim-cache.ts
new file mode 100644
index 000000000..85da9df69
--- /dev/null
+++ b/apps/web/src/data/cached/scrim-cache.ts
@@ -0,0 +1,73 @@
+import "server-only";
+
+import { AppRuntime } from "@/data/runtime";
+import {
+ ScrimOverviewService,
+ ScrimPositionalArtifactsService,
+ ScrimPositionalStatsService,
+ ScrimService,
+} from "@/data/scrim";
+import { ScrimInitiationService } from "@/data/scrim/initiation-service";
+import { scrimTag } from "@/lib/cache-tags";
+import { Effect } from "effect";
+import { cacheLife, cacheTag } from "next/cache";
+
+// Scrim-level aggregates depend on the full set of maps in the scrim, so unlike
+// per-map reads they change when maps are added, removed, or edited. They're
+// tagged `scrim:${scrimId}` and invalidated from the scrim mutation routes.
+
+export async function getCachedScrim(scrimId: number) {
+ "use cache";
+ cacheLife("max");
+ cacheTag(scrimTag(scrimId));
+ return AppRuntime.runPromise(
+ ScrimService.pipe(Effect.flatMap((svc) => svc.getScrim(scrimId)))
+ );
+}
+
+export async function getCachedScrimOverview(scrimId: number, teamId: number) {
+ "use cache";
+ cacheLife("max");
+ cacheTag(scrimTag(scrimId));
+ return AppRuntime.runPromise(
+ ScrimOverviewService.pipe(
+ Effect.flatMap((svc) => svc.getScrimOverview(scrimId, teamId))
+ )
+ );
+}
+
+export async function getCachedScrimPositionalStats(scrimId: number) {
+ "use cache";
+ cacheLife("max");
+ cacheTag(scrimTag(scrimId));
+ return AppRuntime.runPromise(
+ ScrimPositionalStatsService.pipe(
+ Effect.flatMap((svc) => svc.getScrimPositionalStats(scrimId))
+ )
+ );
+}
+
+export async function getCachedScrimPositionalArtifacts(
+ scrimId: number,
+ teamId: number
+) {
+ "use cache";
+ cacheLife("max");
+ cacheTag(scrimTag(scrimId));
+ return AppRuntime.runPromise(
+ ScrimPositionalArtifactsService.pipe(
+ Effect.flatMap((svc) => svc.getScrimPositionalArtifacts(scrimId, teamId))
+ )
+ );
+}
+
+export async function getCachedScrimInitiation(scrimId: number) {
+ "use cache";
+ cacheLife("max");
+ cacheTag(scrimTag(scrimId));
+ return AppRuntime.runPromise(
+ ScrimInitiationService.pipe(
+ Effect.flatMap((svc) => svc.getScrimInitiation(scrimId))
+ )
+ );
+}
diff --git a/apps/web/src/data/cached/team-cache.ts b/apps/web/src/data/cached/team-cache.ts
new file mode 100644
index 000000000..1d8c171a7
--- /dev/null
+++ b/apps/web/src/data/cached/team-cache.ts
@@ -0,0 +1,35 @@
+import "server-only";
+
+import { AppRuntime } from "@/data/runtime";
+import { type TeamDateRange, TeamStatsService } from "@/data/team";
+import { getTeamSubstituteNames } from "@/data/team/substitutes";
+import { teamStatsTag } from "@/lib/cache-tags";
+import { Effect } from "effect";
+import { cacheLife, cacheTag } from "next/cache";
+
+// Team stats change whenever a scrim is uploaded to (or removed from) the team,
+// so they're tagged `team-stats:${teamId}` and invalidated from the scrim
+// mutation routes. `cacheLife('days')` is a safety net in case an invalidation
+// is ever missed; the per-request date range is passed in as part of the key,
+// never read inside the cache scope.
+
+export async function getCachedTeamWinrates(
+ teamId: number,
+ dateRange: TeamDateRange | undefined
+) {
+ "use cache";
+ cacheLife("days");
+ cacheTag(teamStatsTag(teamId));
+ return AppRuntime.runPromise(
+ TeamStatsService.pipe(
+ Effect.flatMap((svc) => svc.getTeamWinrates(teamId, dateRange))
+ )
+ );
+}
+
+export async function getCachedTeamSubstituteNames(teamId: number) {
+ "use cache";
+ cacheLife("days");
+ cacheTag(teamStatsTag(teamId));
+ return getTeamSubstituteNames(teamId);
+}
diff --git a/apps/web/src/data/scrim/overview-service.ts b/apps/web/src/data/scrim/overview-service.ts
index 2ad545402..c11a58655 100644
--- a/apps/web/src/data/scrim/overview-service.ts
+++ b/apps/web/src/data/scrim/overview-service.ts
@@ -2161,12 +2161,14 @@ export const make: Effect.Effect<
deaths: aggregated.deaths,
heroDamageDealt: aggregated.heroDamageDealt,
healingDealt: aggregated.healingDealt,
+ damageBlocked: aggregated.damageBlocked,
heroTimePlayed: aggregated.heroTimePlayed,
kdRatio,
eliminationsPer10: aggregated.eliminationsPer10,
deathsPer10: aggregated.deathsPer10,
heroDamagePer10: aggregated.heroDamagePer10,
healingDealtPer10: aggregated.healingDealtPer10,
+ damageBlockedPer10: aggregated.damageBlockedPer10,
firstDeathCount: totalFirstDeaths,
firstDeathRate:
totalFights > 0 ? (totalFirstDeaths / totalFights) * 100 : 0,
@@ -2213,7 +2215,7 @@ export const make: Effect.Effect<
player.primaryHero,
player.heroDamageDealt,
player.healingDealt,
- 0,
+ player.damageBlocked,
player.deaths,
player.eliminations
);
diff --git a/apps/web/src/data/scrim/types.ts b/apps/web/src/data/scrim/types.ts
index 50a17342e..4245f1fef 100644
--- a/apps/web/src/data/scrim/types.ts
+++ b/apps/web/src/data/scrim/types.ts
@@ -140,12 +140,14 @@ export type PlayerScrimPerformance = {
deaths: number;
heroDamageDealt: number;
healingDealt: number;
+ damageBlocked: number;
heroTimePlayed: number;
kdRatio: number;
eliminationsPer10: number;
deathsPer10: number;
heroDamagePer10: number;
healingDealtPer10: number;
+ damageBlockedPer10: number;
firstDeathCount: number;
firstDeathRate: number;
teamFirstDeathCount: number;
diff --git a/apps/web/src/hooks/use-predictive-prefetch.ts b/apps/web/src/hooks/use-predictive-prefetch.ts
index 00d854b1f..5789e5391 100644
--- a/apps/web/src/hooks/use-predictive-prefetch.ts
+++ b/apps/web/src/hooks/use-predictive-prefetch.ts
@@ -7,7 +7,6 @@ import {
type Sample,
type Vec,
} from "@/lib/predictive-prefetch";
-import type { Route } from "next";
import { useRouter } from "next/navigation";
import type { RefObject } from "react";
import { useEffect, useRef } from "react";
@@ -36,6 +35,12 @@ export type PredictivePrefetchOptions = {
coneAngleDeg?: number;
minSpeed?: number;
enabled?: boolean;
+ /**
+ * "auto" (default) prefetches the App Shell; "full" issues a runtime
+ * prefetch that includes request data — only useful when the destination
+ * route sets `prefetch = 'allow-runtime'`.
+ */
+ prefetchKind?: "auto" | "full";
/**
* When provided, called each animation frame with the full detection state.
* Used only by the debug overlay; absent in normal use (zero overhead).
@@ -43,6 +48,27 @@ export type PredictivePrefetchOptions = {
onFrame?: (frame: PredictivePrefetchDebugFrame) => void;
};
+/**
+ * Prefetch a route with an explicit kind. `kind: "full"` issues a runtime
+ * prefetch (request data included) when the destination route sets
+ * `prefetch = 'allow-runtime'`. Centralized here because `typedRoutes`
+ * narrows the router's `prefetch` signature to `(href)` even though the
+ * runtime accepts an options argument, and the `PrefetchKind` enum lives in
+ * next's internals (its FULL value is the string "full").
+ */
+export function prefetchRoute(
+ router: ReturnType,
+ href: string,
+ kind: "auto" | "full"
+): void {
+ (
+ router.prefetch as unknown as (
+ href: string,
+ options?: { kind: "auto" | "full" }
+ ) => void
+ )(href, kind === "full" ? { kind } : undefined);
+}
+
// Rolling sample window for the velocity estimate.
const SAMPLE_WINDOW_MS = 50;
const MAX_SAMPLES = 6;
@@ -62,6 +88,7 @@ export function usePredictivePrefetch(
coneAngleDeg = 30,
minSpeed = 0.15,
enabled = true,
+ prefetchKind = "auto",
onFrame,
} = options;
@@ -93,9 +120,11 @@ export function usePredictivePrefetch(
if (!container || !cursor) return;
const velocity = estimateVelocity(samples);
+ // Anchors plus non-link targets (e.g. tab triggers) that opt in via
+ // data-prefetch-href.
const anchors = Array.from(
- container.querySelectorAll(
- 'a[href^="/"]:not([href^="//"])'
+ container.querySelectorAll(
+ 'a[href^="/"]:not([href^="//"]), [data-prefetch-href^="/"]:not([data-prefetch-href^="//"])'
)
);
@@ -105,7 +134,9 @@ export function usePredictivePrefetch(
: null;
for (const anchor of anchors) {
- const href = anchor.getAttribute("href");
+ const href =
+ anchor.getAttribute("href") ??
+ anchor.getAttribute("data-prefetch-href");
// Same-origin paths only: reject protocol-relative ("//host") and the
// back-slash variant browsers normalize to it, plus in-page hashes.
if (
@@ -130,7 +161,7 @@ export function usePredictivePrefetch(
optsRef.current
);
if (heading && !already) {
- router.prefetch(href as Route);
+ prefetchRoute(router, href, prefetchKind);
prefetched.add(href);
}
@@ -179,5 +210,5 @@ export function usePredictivePrefetch(
window.removeEventListener("pointermove", handlePointerMove);
if (frame) cancelAnimationFrame(frame);
};
- }, [containerRef, router, enabled]);
+ }, [containerRef, router, enabled, prefetchKind]);
}
diff --git a/apps/web/src/instrumentation-node.ts b/apps/web/src/instrumentation-node.ts
index c0cf75290..e1d9db8c2 100644
--- a/apps/web/src/instrumentation-node.ts
+++ b/apps/web/src/instrumentation-node.ts
@@ -15,7 +15,6 @@ import {
import {
BatchSpanProcessor,
NodeTracerProvider,
- SimpleSpanProcessor,
} from "@opentelemetry/sdk-trace-node";
import {
ATTR_SERVICE_NAME,
@@ -74,11 +73,10 @@ export function registerNode() {
const provider = new NodeTracerProvider({
resource: RESOURCE,
- spanProcessors: [
- IS_PROD
- ? new BatchSpanProcessor(otlpTraceExporter)
- : new SimpleSpanProcessor(otlpTraceExporter),
- ],
+ // BatchSpanProcessor exports on a timer rather than synchronously on span
+ // end, so trace export never reads Date.now() inside a Cache Components
+ // prerender pass (which the dev server runs per request).
+ spanProcessors: [new BatchSpanProcessor(otlpTraceExporter)],
});
registerInstrumentations({
@@ -188,9 +186,7 @@ const EffectTracingLive = NodeSdk.layer(() => ({
[ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: ENVIRONMENT,
},
},
- spanProcessor: IS_PROD
- ? new BatchSpanProcessor(new OTLPTraceExporter(OTLP_CONFIG))
- : new SimpleSpanProcessor(new OTLPTraceExporter(OTLP_CONFIG)),
+ spanProcessor: new BatchSpanProcessor(new OTLPTraceExporter(OTLP_CONFIG)),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter(METRIC_EXPORTER_CONFIG),
exportIntervalMillis: 10_000,
diff --git a/apps/web/src/instrumentation.ts b/apps/web/src/instrumentation.ts
index 258741260..298bf9a39 100644
--- a/apps/web/src/instrumentation.ts
+++ b/apps/web/src/instrumentation.ts
@@ -7,6 +7,11 @@ type RequestErrorArgs = Parameters;
export async function register() {
if (process.env.NEXT_RUNTIME !== "nodejs") return;
+ // OpenTelemetry generates span IDs with Math.random(), which Cache Components
+ // disallows during prerendering. Build-time prerenders need no telemetry;
+ // runtime requests still register and export traces.
+ if (process.env.NEXT_PHASE === "phase-production-build") return;
+
const { registerNode } = await import("./instrumentation-node");
registerNode();
}
diff --git a/apps/web/src/lib/cache-tags.ts b/apps/web/src/lib/cache-tags.ts
new file mode 100644
index 000000000..17bcb2626
--- /dev/null
+++ b/apps/web/src/lib/cache-tags.ts
@@ -0,0 +1,29 @@
+import { revalidateTag } from "next/cache";
+
+// Tag helpers for the cached data readers under `src/data/cached`. Kept in one
+// place so the tag format stays in sync between the readers and the mutation
+// routes that invalidate them.
+
+export function scrimTag(scrimId: number) {
+ return `scrim:${scrimId}`;
+}
+
+export function mapTag(mapId: number) {
+ return `map:${mapId}`;
+}
+
+export function teamStatsTag(teamId: number) {
+ return `team-stats:${teamId}`;
+}
+
+export function revalidateScrim(scrimId: number) {
+ revalidateTag(scrimTag(scrimId), "max");
+}
+
+export function revalidateMap(mapId: number) {
+ revalidateTag(mapTag(mapId), "max");
+}
+
+export function revalidateTeamStats(teamId: number) {
+ revalidateTag(teamStatsTag(teamId), "max");
+}
diff --git a/apps/web/src/lib/flags-helpers.ts b/apps/web/src/lib/flags-helpers.ts
index 8fd5c8fac..376e6e84e 100644
--- a/apps/web/src/lib/flags-helpers.ts
+++ b/apps/web/src/lib/flags-helpers.ts
@@ -4,7 +4,6 @@ import {
dataLabeling,
faceitScouting,
mapComparison,
- newLandingPage,
overviewCard,
positionalData,
queryBuilder,
@@ -14,6 +13,70 @@ import {
tournament,
ultimateImpactTool,
} from "@/lib/flags";
+import { logger } from "@/lib/axiom/server";
+import { FLAGS_CODE_HEADER, pageFlags } from "@/lib/flags-precompute";
+import { getPrecomputed } from "flags/next";
+import { headers } from "next/headers";
+
+type PageFlag = (typeof pageFlags)[number];
+
+async function getFlagsCode(): Promise {
+ return (await headers()).get(FLAGS_CODE_HEADER);
+}
+
+/**
+ * Read one flag's value from the code precomputed in `proxy.ts`. This is the
+ * only supported way to read a flag in render code (pages, layouts, server
+ * components): decoding is pure computation, so PPR's cache-warming and final
+ * prerender passes always agree. A missing code (proxy matcher gap, build
+ * time) deterministically yields `false` — the same request never sees two
+ * different values. Route handlers and server actions call flags live instead.
+ */
+export async function getFlag(f: PageFlag): Promise {
+ const code = await getFlagsCode();
+ if (!code) {
+ logger.warn(
+ `flags: request has no precomputed flags code; "${f.key}" falls back to false`
+ );
+ return false;
+ }
+ return getPrecomputed(f, pageFlags, code);
+}
+
+/** Precomputed equivalent of `resolveAllFlags` for render code. */
+export async function getAllFlags(): Promise {
+ const [
+ mapComparisonEnabled,
+ overviewCardEnabled,
+ scoutingEnabled,
+ faceitScoutingEnabled,
+ dataLabelingEnabled,
+ simulationToolEnabled,
+ ultimateImpactToolEnabled,
+ tempoChartEnabled,
+ positionalDataEnabled,
+ aiChatEnabled,
+ tournamentEnabled,
+ coachingCanvasEnabled,
+ queryBuilderEnabled,
+ ] = await Promise.all(pageFlags.map((f) => getFlag(f)));
+
+ return {
+ scoutingEnabled,
+ faceitScoutingEnabled,
+ mapComparisonEnabled,
+ overviewCardEnabled,
+ dataLabelingEnabled,
+ simulationToolEnabled,
+ ultimateImpactToolEnabled,
+ tempoChartEnabled,
+ aiChatEnabled,
+ positionalDataEnabled,
+ tournamentEnabled,
+ coachingCanvasEnabled,
+ queryBuilderEnabled,
+ };
+}
export type FeatureFlags = {
scoutingEnabled: boolean;
@@ -24,7 +87,6 @@ export type FeatureFlags = {
simulationToolEnabled: boolean;
ultimateImpactToolEnabled: boolean;
tempoChartEnabled: boolean;
- newLandingPageEnabled: boolean;
aiChatEnabled: boolean;
positionalDataEnabled: boolean;
tournamentEnabled: boolean;
@@ -32,6 +94,13 @@ export type FeatureFlags = {
queryBuilderEnabled: boolean;
};
+/**
+ * Live evaluation of every flag. ONLY for route handlers and server actions,
+ * which are never prerendered. Render code must use `getAllFlags`/`getFlag`
+ * instead — a live evaluation that transiently fails falls back to
+ * `defaultValue` in one prerender pass but not the other, desyncing the
+ * passes' `use cache` calls (HANGING_PROMISE_REJECTION).
+ */
export async function resolveAllFlags(): Promise {
const [
scoutingEnabled,
@@ -42,7 +111,6 @@ export async function resolveAllFlags(): Promise {
simulationToolEnabled,
ultimateImpactToolEnabled,
tempoChartEnabled,
- newLandingPageEnabled,
aiChatEnabled,
positionalDataEnabled,
tournamentEnabled,
@@ -57,7 +125,6 @@ export async function resolveAllFlags(): Promise {
simulationTool(),
ultimateImpactTool(),
tempoChart(),
- newLandingPage(),
aiChat(),
positionalData(),
tournament(),
@@ -74,7 +141,6 @@ export async function resolveAllFlags(): Promise {
simulationToolEnabled,
ultimateImpactToolEnabled,
tempoChartEnabled,
- newLandingPageEnabled,
aiChatEnabled,
positionalDataEnabled,
tournamentEnabled,
@@ -93,7 +159,6 @@ export function toFlagValues(flags: FeatureFlags): Record {
"simulation-tool": flags.simulationToolEnabled,
"ultimate-impact-tool": flags.ultimateImpactToolEnabled,
"tempo-chart": flags.tempoChartEnabled,
- "new-landing-page": flags.newLandingPageEnabled,
"ai-chat": flags.aiChatEnabled,
"positional-data": flags.positionalDataEnabled,
tournament: flags.tournamentEnabled,
diff --git a/apps/web/src/lib/flags-precompute.ts b/apps/web/src/lib/flags-precompute.ts
new file mode 100644
index 000000000..abb121b63
--- /dev/null
+++ b/apps/web/src/lib/flags-precompute.ts
@@ -0,0 +1,49 @@
+import {
+ aiChat,
+ coachingCanvas,
+ dataLabeling,
+ faceitScouting,
+ mapComparison,
+ overviewCard,
+ positionalData,
+ queryBuilder,
+ scoutingTool,
+ simulationTool,
+ tempoChart,
+ tournament,
+ ultimateImpactTool,
+} from "@/lib/flags";
+
+/**
+ * Request header carrying the precomputed flags code from `proxy.ts` to
+ * render code. Read via `getFlag`/`getAllFlags` in `flags-helpers.ts`.
+ *
+ * These live outside `flags.ts` on purpose: the Flags Explorer discovery
+ * endpoint (`.well-known/vercel/flags`) treats every export of that module
+ * as a flag definition.
+ */
+export const FLAGS_CODE_HEADER = "x-flags-code";
+
+/**
+ * Every flag, in a FIXED order (the order is part of the precompute
+ * encoding). `proxy.ts` evaluates this group once per page request; render
+ * code decodes the result instead of evaluating live. Live evaluation can
+ * fall back to `defaultValue` on a transient error in only one of PPR's two
+ * prerender passes, which desyncs the passes' `use cache` calls and causes
+ * HANGING_PROMISE_REJECTION. Add new flags to the END of this array.
+ */
+export const pageFlags = [
+ mapComparison,
+ overviewCard,
+ scoutingTool,
+ faceitScouting,
+ dataLabeling,
+ simulationTool,
+ ultimateImpactTool,
+ tempoChart,
+ positionalData,
+ aiChat,
+ tournament,
+ coachingCanvas,
+ queryBuilder,
+] as const;
diff --git a/apps/web/src/lib/flags.ts b/apps/web/src/lib/flags.ts
index 0adaa274e..4cbb5fa2d 100644
--- a/apps/web/src/lib/flags.ts
+++ b/apps/web/src/lib/flags.ts
@@ -16,14 +16,47 @@ type Entities = {
};
};
+/**
+ * Retry an idempotent read a few times on a transient failure. `identify` does
+ * two DB reads (session + user/teams) that run during PPR's prerender; a
+ * one-off pool/connection blip ("Connection closed") there makes the flag fall
+ * back to its defaultValue in only one of the two prerender passes, so the
+ * passes disagree on the flag/entities cache key and log an "Unexpected cache
+ * miss". Retrying the reads keeps the outcome stable across a blip. Both ops
+ * are read-only and idempotent, so replaying them is safe.
+ */
+async function withDbRetry(op: () => Promise, attempts = 3): Promise {
+ let lastError: unknown;
+ for (let attempt = 0; attempt < attempts; attempt++) {
+ try {
+ return await op();
+ } catch (error) {
+ lastError = error;
+ if (attempt < attempts - 1) {
+ await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1)));
+ }
+ }
+ }
+ throw lastError;
+}
+
const identify = dedupe(async (): Promise => {
- const session = await auth();
+ const session = await withDbRetry(() => auth());
if (!session) return { user: undefined, teams: undefined };
- const user = await prisma.user.findUnique({
- where: { email: session?.user?.email },
- include: { teams: true },
- });
+ const user = await withDbRetry(() =>
+ prisma.user.findUnique({
+ where: { email: session?.user?.email },
+ // `teams` feeds `idArray` below, which becomes part of the flag
+ // evaluation entities. Under `partialPrefetching`, flags are precomputed
+ // during prerendering, so a non-deterministic team order makes the
+ // cache-warming and final prerender passes disagree on the cache key
+ // ("Unexpected cache miss after cache warming phase"). Order
+ // deterministically to keep the entities — and therefore the cache key —
+ // stable across passes.
+ include: { teams: { orderBy: { id: "asc" } } },
+ })
+ );
return {
user: user
@@ -186,18 +219,6 @@ export const positionalData = flag({
identify,
});
-export const newLandingPage = flag({
- key: "new-landing-page",
- adapter: vercelAdapter(),
- options: [
- { value: true, label: "Enabled" },
- { value: false, label: "Disabled" },
- ],
- defaultValue: false,
- description: "Show the redesigned landing page",
- identify,
-});
-
export const aiChat = flag({
key: "ai-chat",
adapter: vercelAdapter(),
diff --git a/apps/web/src/lib/map-data-resolver.ts b/apps/web/src/lib/map-data-resolver.ts
index 92ea814ce..474fb9708 100644
--- a/apps/web/src/lib/map-data-resolver.ts
+++ b/apps/web/src/lib/map-data-resolver.ts
@@ -1,13 +1,30 @@
+import { mapTag } from "@/lib/cache-tags";
import prisma from "@/lib/prisma";
+import { cacheLife, cacheTag } from "next/cache";
/**
* Resolves a Map.id to its corresponding MapData.id.
* URL route params use Map.id, but event tables reference MapData.id.
+ *
+ * Cached: the mapping is immutable once uploaded (re-uploads add rows, but the
+ * oldest row stays pinned below), and the result feeds nearly every cached map
+ * read — caching it also lets runtime prefetches advance past it. Invalidated
+ * via `map:${mapId}` on map removal.
*/
export async function resolveMapDataId(mapId: number): Promise {
+ "use cache";
+ cacheLife("max");
+ cacheTag(mapTag(mapId));
const mapData = await prisma.mapData.findFirst({
where: { mapId },
select: { id: true },
+ // A map can have more than one MapData row (e.g. a re-upload). Without an
+ // explicit order, `findFirst` returns an arbitrary row that can differ
+ // between calls — and this id feeds cached-function arguments (e.g.
+ // getCachedMatchStory), so a non-deterministic result breaks the cache key
+ // between PPR's warming and final prerender passes ("Unexpected cache miss
+ // after cache warming phase"). Pin the oldest row for a stable result.
+ orderBy: { id: "asc" },
});
if (!mapData) {
const directCheck = await prisma.mapData.findUnique({
@@ -23,15 +40,23 @@ export async function resolveMapDataId(mapId: number): Promise {
/**
* Resolves a Map.id to MapData.id only when the map belongs to the route scrim.
* Use this for request-controlled route params so MapData.id fallbacks cannot
- * cross resource boundaries.
+ * cross resource boundaries. Cached for the same reasons as `resolveMapDataId`.
*/
export async function resolveScrimMapDataId(
scrimId: number,
mapId: number
): Promise {
+ "use cache";
+ cacheLife("max");
+ cacheTag(mapTag(mapId));
const mapData = await prisma.mapData.findFirst({
where: { scrimId, mapId },
select: { id: true },
+ // Deterministic pick: a map can have multiple MapData rows and this id is a
+ // cached-function argument across every map tab, so an unordered `findFirst`
+ // makes the cache key differ between PPR passes ("Unexpected cache miss
+ // after cache warming phase" → dynamic "use cache" hanging promise).
+ orderBy: { id: "asc" },
});
if (!mapData) {
throw new Error(`No MapData found for scrimId=${scrimId}, mapId=${mapId}`);
diff --git a/apps/web/src/lib/metadata-i18n.ts b/apps/web/src/lib/metadata-i18n.ts
new file mode 100644
index 000000000..af37e4592
--- /dev/null
+++ b/apps/web/src/lib/metadata-i18n.ts
@@ -0,0 +1,60 @@
+import { defaultLocale, type Locale } from "@/i18n/config";
+import { createTranslator } from "next-intl";
+import enMessages from "../../messages/en.json";
+
+/**
+ * Cookie-free translator for `generateMetadata`.
+ *
+ * next-intl's `getTranslations` always resolves the per-request locale through
+ * the request config (`getUserLocale` → `cookies()`), which forces every
+ * route's `` to render dynamically under Cache Components. Metadata is
+ * instead resolved in the default locale from the statically-imported catalog
+ * so it can be prerendered. Page content is still localized at runtime via the
+ * `NextIntlClientProvider` in the root layout.
+ */
+export function getMetadataTranslations(namespace?: string) {
+ return getStaticTranslations(namespace);
+}
+
+/**
+ * The same cookie-free, default-locale translator for static-shell UI:
+ * `loading.tsx` skeletons and Suspense fallbacks. Fallbacks must be
+ * prerenderable — `getTranslations` reads the LOCALE cookie and would force
+ * the shell dynamic. The localized content replaces them when it streams.
+ */
+export function getStaticTranslations(namespace?: string) {
+ // The app does not augment next-intl's global `Messages` type, so cast to the
+ // translator's default (loosely-typed) options instead of having it infer a
+ // strict namespace union from the imported catalog.
+ return createTranslator({
+ locale: defaultLocale,
+ messages: enMessages,
+ namespace,
+ } as Parameters[0]);
+}
+
+/**
+ * Cookie-free translator for an EXPLICIT locale, for use inside `"use cache"`
+ * scopes. `getTranslations` resolves the locale from the LOCALE cookie, and
+ * request APIs are forbidden inside public cache scopes — so cached server
+ * components take the locale as a prop (making it part of the cache key) and
+ * translate through this instead.
+ */
+export async function getLocaleTranslations(
+ locale: Locale,
+ namespace?: string
+) {
+ const messages =
+ locale === defaultLocale
+ ? enMessages
+ : (
+ (await import(`../../messages/${locale}.json`)) as {
+ default: typeof enMessages;
+ }
+ ).default;
+ return createTranslator({
+ locale,
+ messages,
+ namespace,
+ } as Parameters[0]);
+}
diff --git a/apps/web/src/lib/mvp-score.ts b/apps/web/src/lib/mvp-score.ts
index 59932fec5..8a8a47613 100644
--- a/apps/web/src/lib/mvp-score.ts
+++ b/apps/web/src/lib/mvp-score.ts
@@ -232,10 +232,16 @@ export async function calculateMVPScoreFromStats({
if (validStats.length === 0) return null;
- // Only evaluate the most-played hero to avoid bias towards frequent swaps
- const mostPlayedRow = validStats.reduce((prev, current) =>
- prev.hero_time_played > current.hero_time_played ? prev : current
- );
+ // Only evaluate the most-played hero to avoid bias towards frequent swaps.
+ // Break exact-time ties on a stable field (player_hero) rather than array
+ // position — otherwise an unordered `playerStats` order could pick a
+ // different hero between PPR passes and desync the downstream stat cache.
+ const mostPlayedRow = validStats.reduce((prev, current) => {
+ if (current.hero_time_played !== prev.hero_time_played) {
+ return current.hero_time_played > prev.hero_time_played ? current : prev;
+ }
+ return current.player_hero < prev.player_hero ? current : prev;
+ });
const heroScore = await calculateHeroMVPScore(
playerName,
diff --git a/apps/web/src/lib/parser/client.ts b/apps/web/src/lib/parser/client.ts
index 2bc7b2cbe..0322ff5eb 100644
--- a/apps/web/src/lib/parser/client.ts
+++ b/apps/web/src/lib/parser/client.ts
@@ -205,6 +205,17 @@ export async function parseDataFromTXT(file: File) {
? await file?.text()
: (file as unknown as string);
+ return parseLogText(fileContent);
+}
+
+/**
+ * Parse already-read log text into structured `ParserData`. Split out from
+ * `parseDataFromTXT` so callers that need the raw text for other reasons (the
+ * bulk uploader reads it once to also run corruption detection and to log how
+ * many bytes the file actually yielded) can parse without a second `File.text()`
+ * read. Pure and synchronous: same text in -> same data out.
+ */
+export function parseLogText(fileContent: string): ParserData {
const lines = fileContent
.split("\n")
.map((line) => splitLinePreservingCoords(line));
diff --git a/apps/web/src/lib/parser/persistence.ts b/apps/web/src/lib/parser/persistence.ts
index a8812e8f7..675778ee4 100644
--- a/apps/web/src/lib/parser/persistence.ts
+++ b/apps/web/src/lib/parser/persistence.ts
@@ -136,13 +136,21 @@ async function insertMapEventRows(
let done = 0;
onProgress?.(0, total);
- await Promise.all(
- tasks.map(async ([, run], i) => {
- await run();
- done += weights[i];
- onProgress?.(done, total);
- })
- );
+ // Run the inserts SEQUENTIALLY, not under Promise.all. During ingestion `db`
+ // is an interactive-transaction client pinned to a single Postgres
+ // connection, and a connection can only execute one query at a time. Firing
+ // all event-type inserts concurrently issues overlapping queries on that one
+ // connection, which the pg driver only tolerates by queueing them (now a
+ // deprecation warning: "Calling client.query() when the client is already
+ // executing a query") and a future pg release will reject outright. Serial
+ // execution keeps one in-flight query per connection and still streams
+ // weighted progress after each event type commits.
+ for (let i = 0; i < tasks.length; i++) {
+ const [, run] = tasks[i];
+ await run();
+ done += weights[i];
+ onProgress?.(done, total);
+ }
}
export async function createNewScrimFromParsedData(
diff --git a/apps/web/src/lib/query-builder/compute/player-outliers.ts b/apps/web/src/lib/query-builder/compute/player-outliers.ts
index c444185ad..7da4dcc23 100644
--- a/apps/web/src/lib/query-builder/compute/player-outliers.ts
+++ b/apps/web/src/lib/query-builder/compute/player-outliers.ts
@@ -150,6 +150,9 @@ export async function computePlayerOutliers(
const mapDataIds = Array.from(scopedMapIds);
const playerStats = await prisma.playerStat.findMany({
where: { MapDataId: { in: mapDataIds } },
+ // Deterministic order so downstream Map insertion order (which decides the
+ // primary-hero tie-break below) is stable across renders.
+ orderBy: [{ MapDataId: "asc" }, { id: "asc" }],
});
const maxTimeByMap = new Map();
@@ -193,7 +196,8 @@ export async function computePlayerOutliers(
(totals) => {
const primaryHero =
Array.from(totals.heroTimes.entries()).sort(
- (a, b) => b[1] - a[1]
+ // Time desc, then hero name asc so exact ties resolve deterministically.
+ (a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)
)[0]?.[0] ?? "Unknown";
const role = determineRole(primaryHero as HeroName);
const statDefs = statDefsForRole(role);
diff --git a/apps/web/src/lib/stat-percentiles.ts b/apps/web/src/lib/stat-percentiles.ts
index f228a8017..65d6354e0 100644
--- a/apps/web/src/lib/stat-percentiles.ts
+++ b/apps/web/src/lib/stat-percentiles.ts
@@ -1,7 +1,7 @@
import prisma from "@/lib/prisma";
import type { HeroName } from "@/types/heroes";
import { Prisma } from "@/generated/prisma/client";
-import { unstable_cache } from "next/cache";
+import { cacheLife, cacheTag } from "next/cache";
// These baselines scan the whole PlayerStat table per hero (the app's biggest
// rows-read offender) yet are global, slowly-changing population statistics —
@@ -444,9 +444,16 @@ function buildMultiStatComparisonQuery({
`;
}
-async function compareMultipleStatsToDistributionUncached(
+export async function compareMultipleStatsToDistribution(
params: MultiStatComparisonParams
): Promise {
+ "use cache";
+ cacheLife({
+ revalidate: HERO_BASELINE_TTL_SECONDS,
+ expire: HERO_BASELINE_TTL_SECONDS * 4,
+ });
+ cacheTag(HERO_BASELINE_TAG);
+
const query = buildMultiStatComparisonQuery(params);
const result =
await prisma.$queryRaw<{ hero: string; comparisons: StatComparison[] }[]>(
@@ -463,12 +470,6 @@ async function compareMultipleStatsToDistributionUncached(
};
}
-export const compareMultipleStatsToDistribution = unstable_cache(
- compareMultipleStatsToDistributionUncached,
- ["multi-stat-comparison"],
- { revalidate: HERO_BASELINE_TTL_SECONDS, tags: [HERO_BASELINE_TAG] }
-);
-
function buildStatDistributionBaselineQuery({
hero,
stat,
@@ -522,20 +523,21 @@ function buildStatDistributionBaselineQuery({
`;
}
-async function getStatDistributionBaselineUncached(
+export async function getStatDistributionBaseline(
params: StatDistributionBaselineParams
): Promise {
+ "use cache";
+ cacheLife({
+ revalidate: HERO_BASELINE_TTL_SECONDS,
+ expire: HERO_BASELINE_TTL_SECONDS * 4,
+ });
+ cacheTag(HERO_BASELINE_TAG);
+
const query = buildStatDistributionBaselineQuery(params);
const result = await prisma.$queryRaw(query);
return result[0] ?? null;
}
-export const getStatDistributionBaseline = unstable_cache(
- getStatDistributionBaselineUncached,
- ["stat-distribution-baseline"],
- { revalidate: HERO_BASELINE_TTL_SECONDS, tags: [HERO_BASELINE_TAG] }
-);
-
export type {
StatDistributionBaseline,
StatDistributionBaselineParams,
diff --git a/apps/web/src/lib/usage/queries.ts b/apps/web/src/lib/usage/queries.ts
index 9658c3a8f..deb1a933f 100644
--- a/apps/web/src/lib/usage/queries.ts
+++ b/apps/web/src/lib/usage/queries.ts
@@ -2,7 +2,7 @@ import "server-only";
import prisma from "@/lib/prisma";
import type { UsageEnv } from "@/generated/prisma/client";
-import { unstable_cache } from "next/cache";
+import { cacheLife } from "next/cache";
import { FUNNELS, computeFunnel, type FunnelStep } from "./funnels";
import { dayKey } from "./rollup";
@@ -170,10 +170,11 @@ async function loadFunnels(
}));
}
-export function getFunnels(env: UsageEnv, days = 30): Promise {
- return unstable_cache(
- () => loadFunnels(env, days),
- ["usage-funnels", env, String(days)],
- { revalidate: 60 * 60 * 24 } // daily
- )();
+export async function getFunnels(
+ env: UsageEnv,
+ days = 30
+): Promise {
+ "use cache";
+ cacheLife("days");
+ return loadFunnels(env, days);
}
diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts
index e33108327..aa6d55869 100644
--- a/apps/web/src/lib/utils.ts
+++ b/apps/web/src/lib/utils.ts
@@ -292,7 +292,13 @@ export function ultimateStartToKillEvent(
*/
export async function translateMapName(name: string) {
const t = await getTranslations("maps");
- return t(toKebabCase(name));
+ const key = toKebabCase(name);
+ // The "maps" catalog is keyed by map slug and has no entry for unknown or
+ // fallback names (e.g. a map imported with an empty map_name resolves to the
+ // literal "Map" -> "map"). next-intl has no English fallback, so t("map")
+ // throws MISSING_MESSAGE and 500s the map page's generateMetadata. Degrade to
+ // the raw name instead of throwing when the slug isn't in the catalog.
+ return t.has(key) ? t(key) : name;
}
/**
@@ -304,7 +310,10 @@ export async function translateMapName(name: string) {
*/
export function useMapName(name: string) {
const t = useTranslations("maps");
- return t(toKebabCase(name));
+ const key = toKebabCase(name);
+ // See translateMapName: fall back to the raw name when the slug isn't in the
+ // "maps" catalog so an unknown/empty map name can't throw MISSING_MESSAGE.
+ return t.has(key) ? t(key) : name;
}
/**
diff --git a/apps/web/src/lib/win-probability/artifact-store.ts b/apps/web/src/lib/win-probability/artifact-store.ts
index bdfcc3b15..8850b532b 100644
--- a/apps/web/src/lib/win-probability/artifact-store.ts
+++ b/apps/web/src/lib/win-probability/artifact-store.ts
@@ -1,11 +1,13 @@
import { Logger } from "@/lib/logger";
import { r2 } from "@/lib/r2";
+import { cacheLife, cacheTag, revalidateTag } from "next/cache";
import { featureHash } from "./features";
import type { ModelArtifact } from "./model";
const MODEL_PREFIX = "wp-models";
const LATEST_KEY = `${MODEL_PREFIX}/latest.json`;
-export const ARTIFACT_TTL_MS = 60 * 60 * 1000;
+/** Cache tag for the live model artifact; busted by `publishArtifact`. */
+const ARTIFACT_TAG = "wp-artifact";
type LatestPointer = { key: string; modelVersion: number };
@@ -33,12 +35,6 @@ function parseArtifact(raw: string): ModelArtifact | null {
}
}
-let cache: { artifact: ModelArtifact | null; fetchedAt: number } | null = null;
-
-export function __resetArtifactCacheForTests(): void {
- cache = null;
-}
-
async function fetchLatest(): Promise {
try {
const pointerRaw = await r2.download(LATEST_KEY);
@@ -68,17 +64,24 @@ async function fetchLatest(): Promise {
}
/**
- * TTL-cached loader. Returns null — never throws — on any failure; callers
- * hide the WP surface when no model is servable. Failures are cached too,
- * so an R2 outage costs one attempt per TTL window.
+ * Loads the live model artifact. Returns null — never throws — on any failure;
+ * callers hide the WP surface when no model is servable (failures are cached
+ * too, so an R2 outage costs one fetch per revalidation window).
+ *
+ * Wrapped in `use cache` so Next governs the TTL. This is deliberate: the old
+ * hand-rolled `Date.now()` module TTL read the wall clock, and this loader is
+ * the FIRST call inside `getCachedMatchStory`'s `use cache` body — so that
+ * `Date.now()` executed inside a cache scope, turning `getCachedMatchStory`
+ * into a dynamic `use cache` that never committed. The result: a
+ * HANGING_PROMISE_REJECTION and an 8.9MB model re-download on every map render.
+ * `fetchLatest()` takes no arguments and reads only the deterministic
+ * `featureHash()`, so it is safe to cache; `publishArtifact` busts the tag.
*/
export async function loadLatestArtifact(): Promise {
- if (cache !== null && Date.now() - cache.fetchedAt < ARTIFACT_TTL_MS) {
- return cache.artifact;
- }
- const artifact = await fetchLatest();
- cache = { artifact, fetchedAt: Date.now() };
- return artifact;
+ "use cache";
+ cacheLife("hours");
+ cacheTag(ARTIFACT_TAG);
+ return fetchLatest();
}
/** Versioned publish: model object first, pointer advance last — a crash
@@ -108,5 +111,8 @@ export async function publishArtifact(
),
contentType: "application/json",
});
+ // Drop the cached artifact so the freshly-published model is served
+ // immediately instead of waiting out the `cacheLife` window.
+ revalidateTag(ARTIFACT_TAG, "hours");
return { key, modelVersion };
}
diff --git a/apps/web/src/proxy.ts b/apps/web/src/proxy.ts
index 8549fef5a..42260a42c 100644
--- a/apps/web/src/proxy.ts
+++ b/apps/web/src/proxy.ts
@@ -4,16 +4,60 @@ import {
httpRequestDuration,
} from "@/lib/axiom/metrics";
import { logger } from "@/lib/axiom/server";
+import { FLAGS_CODE_HEADER, pageFlags } from "@/lib/flags-precompute";
import { transformMiddlewareRequest } from "@axiomhq/nextjs";
+import { precompute } from "flags/next";
import type { NextFetchEvent, NextRequest } from "next/server";
import { NextResponse } from "next/server";
-export function proxy(request: NextRequest, event: NextFetchEvent) {
+/**
+ * Paths that never read flags during render: API route handlers and server
+ * actions evaluate flags live, and static/well-known endpoints don't use
+ * them. Skipping avoids the identify DB reads on those requests.
+ */
+function shouldPrecomputeFlags(pathname: string): boolean {
+ return !(
+ pathname.startsWith("/api/") ||
+ pathname.startsWith("/.well-known/") ||
+ pathname.startsWith("/monitoring")
+ );
+}
+
+/**
+ * Evaluate all page flags exactly once per request and forward the signed
+ * result to the app on a request header. Render code decodes it via
+ * `getFlag`/`getAllFlags` — pure computation — so PPR's two prerender passes
+ * always see identical flag values. (A live evaluation that transiently fails
+ * in one pass silently flips to `defaultValue` and desyncs the passes'
+ * `use cache` calls: the HANGING_PROMISE_REJECTION class.) On precompute
+ * failure we forward WITHOUT the header: every reader then deterministically
+ * falls back to false for this request.
+ */
+async function withPrecomputedFlags(
+ request: NextRequest
+): Promise {
+ if (!shouldPrecomputeFlags(request.nextUrl.pathname)) {
+ return NextResponse.next();
+ }
+ try {
+ const code = await precompute(pageFlags);
+ const requestHeaders = new Headers(request.headers);
+ requestHeaders.set(FLAGS_CODE_HEADER, code);
+ return NextResponse.next({ request: { headers: requestHeaders } });
+ } catch (error) {
+ logger.warn("flags: precompute failed; falling back to defaults", {
+ error,
+ });
+ return NextResponse.next();
+ }
+}
+
+export async function proxy(request: NextRequest, event: NextFetchEvent) {
const start = performance.now();
logger.info(...transformMiddlewareRequest(request));
- const response = NextResponse.next();
+ const response = await withPrecomputedFlags(request);
const pathname = request.nextUrl.pathname;
const method = request.method;
diff --git a/apps/web/test/flags-precomputed.test.ts b/apps/web/test/flags-precomputed.test.ts
new file mode 100644
index 000000000..92a537131
--- /dev/null
+++ b/apps/web/test/flags-precomputed.test.ts
@@ -0,0 +1,73 @@
+import { flag, serialize } from "flags/next";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+process.env.FLAGS_SECRET = "dGVzdC1zZWNyZXQtdGVzdC1zZWNyZXQtdGVzdC1zZWNyZXQ";
+
+const flagA = flag({
+ key: "flag-a",
+ options: [{ value: true }, { value: false }],
+ defaultValue: false,
+ decide: () => true,
+});
+const flagB = flag({
+ key: "flag-b",
+ options: [{ value: true }, { value: false }],
+ defaultValue: false,
+ decide: () => false,
+});
+const testGroup = [flagA, flagB] as const;
+
+const requestHeaders = new Map();
+vi.mock("next/headers", () => ({
+ headers: () =>
+ Promise.resolve({
+ get: (name: string) => requestHeaders.get(name) ?? null,
+ }),
+}));
+vi.mock("@/lib/flags-precompute", () => ({
+ FLAGS_CODE_HEADER: "x-flags-code",
+ pageFlags: testGroup,
+}));
+vi.mock("@/lib/axiom/server", () => ({
+ logger: { warn: vi.fn() },
+}));
+// flags-helpers also re-exports a live resolver over the real flag module,
+// whose import chain reaches Stripe/Prisma env setup — stub it out.
+vi.mock("@/lib/flags", () => {
+ function stub() {
+ return Promise.resolve(false);
+ }
+ return {
+ aiChat: stub,
+ coachingCanvas: stub,
+ dataLabeling: stub,
+ faceitScouting: stub,
+ mapComparison: stub,
+ newLandingPage: stub,
+ overviewCard: stub,
+ positionalData: stub,
+ queryBuilder: stub,
+ scoutingTool: stub,
+ simulationTool: stub,
+ tempoChart: stub,
+ tournament: stub,
+ ultimateImpactTool: stub,
+ };
+});
+
+describe("getFlag", () => {
+ beforeEach(() => requestHeaders.clear());
+
+ it("decodes the precomputed value from the request header", async () => {
+ const { getFlag } = await import("@/lib/flags-helpers");
+ const code = await serialize(testGroup, [true, false]);
+ requestHeaders.set("x-flags-code", code);
+ await expect(getFlag(flagA)).resolves.toBe(true);
+ await expect(getFlag(flagB)).resolves.toBe(false);
+ });
+
+ it("falls back to false when the header is missing", async () => {
+ const { getFlag } = await import("@/lib/flags-helpers");
+ await expect(getFlag(flagA)).resolves.toBe(false);
+ });
+});
diff --git a/apps/web/test/win-probability/artifact-store.test.ts b/apps/web/test/win-probability/artifact-store.test.ts
index f8f33bee6..ab1b68e91 100644
--- a/apps/web/test/win-probability/artifact-store.test.ts
+++ b/apps/web/test/win-probability/artifact-store.test.ts
@@ -1,6 +1,6 @@
import { featureHash } from "@/lib/win-probability/features";
import type { ModelArtifact } from "@/lib/win-probability/model";
-import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
+import { beforeEach, describe, expect, test, vi } from "vitest";
const download = vi.fn();
const upload = vi.fn();
@@ -13,9 +13,16 @@ vi.mock("@/lib/r2", () => ({
vi.mock("@/lib/logger", () => ({
Logger: { warn: vi.fn(), info: vi.fn(), error: vi.fn() },
}));
+// `loadLatestArtifact` is now a `use cache` function; caching + TTL are Next's
+// responsibility (governed by `cacheLife`), not a hand-rolled module cache.
+// Outside the Next runtime these are no-ops so we can exercise the fetch logic.
+vi.mock("next/cache", () => ({
+ cacheLife: vi.fn(),
+ cacheTag: vi.fn(),
+ revalidateTag: vi.fn(),
+}));
import {
- __resetArtifactCacheForTests,
loadLatestArtifact,
publishArtifact,
} from "@/lib/win-probability/artifact-store";
@@ -37,15 +44,9 @@ function artifact(overrides: Partial = {}): ModelArtifact {
}
beforeEach(() => {
- vi.useFakeTimers();
download.mockReset();
upload.mockReset();
upload.mockResolvedValue({ key: "x" });
- __resetArtifactCacheForTests();
-});
-
-afterEach(() => {
- vi.useRealTimers();
});
function stubDownloads(pointerVersion: number, model: ModelArtifact) {
@@ -65,25 +66,17 @@ function stubDownloads(pointerVersion: number, model: ModelArtifact) {
}
describe("loadLatestArtifact", () => {
- test("loads via the pointer and caches within the TTL", async () => {
+ test("resolves the live model via the pointer", async () => {
stubDownloads(2, artifact());
- const first = await loadLatestArtifact();
- expect(first?.modelVersion).toBe(2);
- expect(download).toHaveBeenCalledTimes(2); // pointer + model
-
- await loadLatestArtifact();
- expect(download).toHaveBeenCalledTimes(2); // cached
-
- vi.advanceTimersByTime(61 * 60 * 1000);
- await loadLatestArtifact();
- expect(download).toHaveBeenCalledTimes(4); // TTL expired → refetched
+ const result = await loadLatestArtifact();
+ expect(result?.modelVersion).toBe(2);
+ expect(download).toHaveBeenCalledWith("wp-models/latest.json");
+ expect(download).toHaveBeenCalledWith("wp-models/model-v2.json");
});
- test("returns null on download failure without throwing, and caches the failure", async () => {
+ test("returns null on download failure without throwing", async () => {
download.mockRejectedValue(new Error("boom"));
expect(await loadLatestArtifact()).toBeNull();
- expect(await loadLatestArtifact()).toBeNull();
- expect(download).toHaveBeenCalledTimes(1); // one attempt per TTL window
});
test("returns null on feature hash mismatch", async () => {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cb575e691..231c080ca 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -199,7 +199,7 @@ importers:
version: 0.2.2(@axiomhq/js@1.7.0)
'@axiomhq/nextjs':
specifier: ^0.2.2
- version: 0.2.2(@axiomhq/logging@0.2.2(@axiomhq/js@1.7.0))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))
+ version: 0.2.2(@axiomhq/logging@0.2.2(@axiomhq/js@1.7.0))(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))
'@axiomhq/react':
specifier: ^0.2.2
version: 0.2.2(@axiomhq/logging@0.2.2(@axiomhq/js@1.7.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -223,7 +223,7 @@ importers:
version: 0.63.0(@effect/platform@0.96.0(effect@3.21.4))(@opentelemetry/api@1.9.1)(@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-logs@0.219.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-node@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-web@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)(effect@3.21.4)
'@flags-sdk/vercel':
specifier: ^1.0.1
- version: 1.4.2(@aws-sdk/credential-provider-web-identity@3.972.55)(flags@4.2.0(@opentelemetry/api@1.9.1)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(ws@8.21.0)
+ version: 1.4.2(@aws-sdk/credential-provider-web-identity@3.972.55)(flags@4.2.0(@opentelemetry/api@1.9.1)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(ws@8.21.0)
'@floating-ui/react':
specifier: ^0.27.16
version: 0.27.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -238,7 +238,7 @@ importers:
version: 5.4.0(react-hook-form@7.80.0(react@19.2.3))
'@next/third-parties':
specifier: ^16.1.1
- version: 16.2.9(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
+ version: 16.2.9(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
'@opentelemetry/api':
specifier: ^1.9.1
version: 1.9.1
@@ -418,16 +418,16 @@ importers:
version: 1.38.0
'@vercel/analytics':
specifier: ^2.0.1
- version: 2.0.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
+ version: 2.0.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
'@vercel/blob':
specifier: ^2.4.1
version: 2.4.1
'@vercel/edge-config':
specifier: ^1.4.0
- version: 1.4.3(@opentelemetry/api@1.9.1)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))
+ version: 1.4.3(@opentelemetry/api@1.9.1)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))
'@vercel/flags-core':
specifier: ^1.0.1
- version: 1.5.1(@aws-sdk/credential-provider-web-identity@3.972.55)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(ws@8.21.0)
+ version: 1.5.1(@aws-sdk/credential-provider-web-identity@3.972.55)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(ws@8.21.0)
'@vercel/functions':
specifier: ^3.1.0
version: 3.7.3(@aws-sdk/credential-provider-web-identity@3.972.55)(ws@8.21.0)
@@ -436,10 +436,10 @@ importers:
version: 3.0.0
'@vercel/speed-insights':
specifier: ^1.2.0
- version: 1.3.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
+ version: 1.3.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
'@vercel/toolbar':
specifier: ^0.2.6
- version: 0.2.6(@vercel/analytics@2.0.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(@vercel/speed-insights@1.3.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.3.6(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0))
+ version: 0.2.6(@vercel/analytics@2.0.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(@vercel/speed-insights@1.3.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.3.6(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0))
'@xstate/store':
specifier: ^3.17.1
version: 3.17.5(react@19.2.3)
@@ -457,7 +457,7 @@ importers:
version: 1.0.0
better-auth:
specifier: ^1.6.20
- version: 1.6.20(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.1)(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@typescript/typescript6@6.0.1)(magicast@0.5.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)))(mysql2@3.15.3)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(pg@8.22.0)(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@typescript/typescript6@6.0.1)(magicast@0.5.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vitest@3.2.6(@types/debug@4.1.13)(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0))
+ version: 1.6.20(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.1)(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@typescript/typescript6@6.0.1)(magicast@0.5.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)))(mysql2@3.15.3)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(pg@8.22.0)(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@typescript/typescript6@6.0.1)(magicast@0.5.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vitest@3.2.6(@types/debug@4.1.13)(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0))
cheerio:
specifier: ^1.2.0
version: 1.2.0
@@ -484,7 +484,7 @@ importers:
version: 8.6.0(react@19.2.3)
flags:
specifier: ^4.0.3
- version: 4.2.0(@opentelemetry/api@1.9.1)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ version: 4.2.0(@opentelemetry/api@1.9.1)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
framer-motion:
specifier: ^12.23.13
version: 12.42.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -493,7 +493,7 @@ importers:
version: 7.4.2
geist:
specifier: ^1.5.1
- version: 1.7.2(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))
+ version: 1.7.2(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))
input-otp:
specifier: ^1.4.2
version: 1.4.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -513,26 +513,26 @@ importers:
specifier: ^5.1.5
version: 5.1.16
next:
- specifier: 16.2.9
- version: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ specifier: 16.3.0-preview.5
+ version: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
next-axiom:
specifier: ^1.9.2
- version: 1.10.0(@aws-sdk/credential-provider-web-identity@3.972.55)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
+ version: 1.10.0(@aws-sdk/credential-provider-web-identity@3.972.55)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
next-intl:
specifier: ^4.9.2
- version: 4.13.0(@swc/helpers@0.5.17)(@typescript/typescript6@6.0.1)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
+ version: 4.13.0(@swc/helpers@0.5.17)(@typescript/typescript6@6.0.1)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
next-seo:
specifier: ^7.2.0
- version: 7.2.0(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
+ version: 7.2.0(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
nextstepjs:
specifier: ^2.1.2
- version: 2.2.0(motion@12.42.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ version: 2.2.0(motion@12.42.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
nuqs:
specifier: ^2.8.9
- version: 2.8.9(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
+ version: 2.8.9(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
pg:
specifier: ^8.21.0
version: 8.22.0
@@ -1877,8 +1877,8 @@ packages:
'@next/env@16.2.6':
resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==}
- '@next/env@16.2.9':
- resolution: {integrity: sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg==}
+ '@next/env@16.3.0-preview.5':
+ resolution: {integrity: sha512-XqdVR0utAWMsVc1OIyO48D32vrdmC4/uAgI3Ds088YlOO4vfGKXXVyvkGFkOZkOK0xg7bNYNfJAarX4A0tYqGg==}
'@next/swc-darwin-arm64@16.2.6':
resolution: {integrity: sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==}
@@ -1886,8 +1886,8 @@ packages:
cpu: [arm64]
os: [darwin]
- '@next/swc-darwin-arm64@16.2.9':
- resolution: {integrity: sha512-HkfxNYUCmcct0Xsqib5KxqMSHV4AHJq857BNRchyBDs4YS19aHzVfn1kDuBYKqLLQBjXgnkIsjV2Kd4d2wzYhw==}
+ '@next/swc-darwin-arm64@16.3.0-preview.5':
+ resolution: {integrity: sha512-PPWAJGoIkzVpz5hOD9V/qGNdkBuWj3QXhjQU8BQ1FXlMy6xsy4+aD/3UoasKy/HYInW4h1LqdQtDhiQkLYrrMA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
@@ -1898,8 +1898,8 @@ packages:
cpu: [x64]
os: [darwin]
- '@next/swc-darwin-x64@16.2.9':
- resolution: {integrity: sha512-7IAtK4MeybpqRV9GRABWEhJ62mOS+rzWOzOTFie4cSEtm12xsoOMJRcECoZx3FHPzFAqN/IJtHqWAFOLfl152w==}
+ '@next/swc-darwin-x64@16.3.0-preview.5':
+ resolution: {integrity: sha512-UPN/RS1H+kr9fgJrbFoH7bs1b9q2/G5cFe+uUf0nP4Hlgfl8NzfTBHEJKTfLAGqi1Qemwuyd29pvRy2vwEjL5g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
@@ -1910,8 +1910,8 @@ packages:
cpu: [arm64]
os: [linux]
- '@next/swc-linux-arm64-gnu@16.2.9':
- resolution: {integrity: sha512-hBD75iWpUtkL9SmQmcRhmLomn9jgkPzCEkbOcLgHymPEKzv+6ONy13RRiIEz/iEObjkS2Jlb5gYS2XGoS3X4rw==}
+ '@next/swc-linux-arm64-gnu@16.3.0-preview.5':
+ resolution: {integrity: sha512-kh+bKgk9ZIlmxMkEPnQZXtKc7/AyUyIS9jXgbKt4hWyxXEEZVDmXhiU2bh1zZpthMr/l09wz9z6CvfXtCWUJBA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -1922,8 +1922,8 @@ packages:
cpu: [arm64]
os: [linux]
- '@next/swc-linux-arm64-musl@16.2.9':
- resolution: {integrity: sha512-qZTI3pf9SGc/obr8NkQAekBxmp1QK+kVm+VAf3BALLfFAj+1kUhkTxmrWpVos9R/UYIA8AWX2p6cGI5WdwzVUA==}
+ '@next/swc-linux-arm64-musl@16.3.0-preview.5':
+ resolution: {integrity: sha512-m09/acXFGhlp+U6m7Wn0AqsmLqars3qI9eBXDpPJm4h/XVS9HPHNzWGy2BI7F1iLoFX59Uy0tcau9ey7JVud3w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -1934,8 +1934,8 @@ packages:
cpu: [x64]
os: [linux]
- '@next/swc-linux-x64-gnu@16.2.9':
- resolution: {integrity: sha512-xm0HfRNX+UkH4R3c18ynswjj5o5uEj/7iI9p9omdtTSIsRCzQqkGMA+10nzJ4EHnYC3as65IMhbbl5fWRUWHYg==}
+ '@next/swc-linux-x64-gnu@16.3.0-preview.5':
+ resolution: {integrity: sha512-/EBiqRjLZJWJo6Keq9upJfhrP+tNpePy1beBfOL+tUn68inwNiJEjx+0Lgve99Zur8kSk9TgSmDmwgQxX4iM+g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -1946,8 +1946,8 @@ packages:
cpu: [x64]
os: [linux]
- '@next/swc-linux-x64-musl@16.2.9':
- resolution: {integrity: sha512-QumimHkGEG6vM3PfEDWKyKen03NcqLOkeKB1EfcPe7VxzmEiCa4jNnMyBn/US5zcd/VE1CI+O8Ovb3lfjVHfGw==}
+ '@next/swc-linux-x64-musl@16.3.0-preview.5':
+ resolution: {integrity: sha512-lUCiPFoecSGkM8aeY6UAgQDiJjR3DhPsI036mznlHFg89ZLoeRdo521N4nmk6EpbPpNzRujgiboBkbuyexDgCg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -1958,8 +1958,8 @@ packages:
cpu: [arm64]
os: [win32]
- '@next/swc-win32-arm64-msvc@16.2.9':
- resolution: {integrity: sha512-hzQpKZvw8rAwI6A2uQh6SacCSvNAXaIkPNsWwzqqfRiIMiXMfH936skDhz1OO6KpvdKkJrgHHtqQOq5PIXOvdQ==}
+ '@next/swc-win32-arm64-msvc@16.3.0-preview.5':
+ resolution: {integrity: sha512-Nr4e3dRB86gElIgysL/L7dr9tuRLIq3looK8hLxnYDLUvLza2Tu/7Ik/X6DSRGejIrbZsYjnH3S4xYeAAf7Prw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
@@ -1970,8 +1970,8 @@ packages:
cpu: [x64]
os: [win32]
- '@next/swc-win32-x64-msvc@16.2.9':
- resolution: {integrity: sha512-qr2VL3Ce5QrwgO2yh1ujSBawrimjVKX8FGF/cOynmdYKJY0BdHpGVNIRK1tqONB10Vkm25Ub1BD2bkjWs4+96w==}
+ '@next/swc-win32-x64-msvc@16.3.0-preview.5':
+ resolution: {integrity: sha512-Svg+VCRUbyNsBuh96hN+1ael8dNXqVQVZqOe9tqFlF4mUzIk5CQFcn5VsZPrz8GNP9HCxJfrfy3PM0cXoSXliw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -7557,8 +7557,8 @@ packages:
sass:
optional: true
- next@16.2.9:
- resolution: {integrity: sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==}
+ next@16.3.0-preview.5:
+ resolution: {integrity: sha512-I5rVC4VcvAL1FPr6AY5WEQUSe6o1Bt0Oa/qH5hfPhci4FRMCPeAQ95tgxFOgJDk2wME1K009k0bjS17nQ0Bq1w==}
engines: {node: '>=20.9.0'}
hasBin: true
peerDependencies:
@@ -7854,6 +7854,10 @@ packages:
points-on-path@0.2.1:
resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==}
+ postcss@8.5.10:
+ resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
postcss@8.5.15:
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
engines: {node: ^10 || ^12 || >=14}
@@ -9553,10 +9557,10 @@ snapshots:
optionalDependencies:
'@axiomhq/js': 1.7.0
- '@axiomhq/nextjs@0.2.2(@axiomhq/logging@0.2.2(@axiomhq/js@1.7.0))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))':
+ '@axiomhq/nextjs@0.2.2(@axiomhq/logging@0.2.2(@axiomhq/js@1.7.0))(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))':
dependencies:
'@axiomhq/logging': 0.2.2(@axiomhq/js@1.7.0)
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
'@axiomhq/react@0.2.2(@axiomhq/logging@0.2.2(@axiomhq/js@1.7.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
@@ -10091,10 +10095,10 @@ snapshots:
'@eslint/core': 1.2.1
levn: 0.4.1
- '@flags-sdk/vercel@1.4.2(@aws-sdk/credential-provider-web-identity@3.972.55)(flags@4.2.0(@opentelemetry/api@1.9.1)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(ws@8.21.0)':
+ '@flags-sdk/vercel@1.4.2(@aws-sdk/credential-provider-web-identity@3.972.55)(flags@4.2.0(@opentelemetry/api@1.9.1)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(ws@8.21.0)':
dependencies:
- '@vercel/flags-core': 1.5.1(@aws-sdk/credential-provider-web-identity@3.972.55)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(ws@8.21.0)
- flags: 4.2.0(@opentelemetry/api@1.9.1)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@vercel/flags-core': 1.5.1(@aws-sdk/credential-provider-web-identity@3.972.55)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(ws@8.21.0)
+ flags: 4.2.0(@opentelemetry/api@1.9.1)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
transitivePeerDependencies:
- '@aws-sdk/credential-provider-web-identity'
- '@openfeature/server-sdk'
@@ -10407,59 +10411,59 @@ snapshots:
'@next/env@16.2.6': {}
- '@next/env@16.2.9': {}
+ '@next/env@16.3.0-preview.5': {}
'@next/swc-darwin-arm64@16.2.6':
optional: true
- '@next/swc-darwin-arm64@16.2.9':
+ '@next/swc-darwin-arm64@16.3.0-preview.5':
optional: true
'@next/swc-darwin-x64@16.2.6':
optional: true
- '@next/swc-darwin-x64@16.2.9':
+ '@next/swc-darwin-x64@16.3.0-preview.5':
optional: true
'@next/swc-linux-arm64-gnu@16.2.6':
optional: true
- '@next/swc-linux-arm64-gnu@16.2.9':
+ '@next/swc-linux-arm64-gnu@16.3.0-preview.5':
optional: true
'@next/swc-linux-arm64-musl@16.2.6':
optional: true
- '@next/swc-linux-arm64-musl@16.2.9':
+ '@next/swc-linux-arm64-musl@16.3.0-preview.5':
optional: true
'@next/swc-linux-x64-gnu@16.2.6':
optional: true
- '@next/swc-linux-x64-gnu@16.2.9':
+ '@next/swc-linux-x64-gnu@16.3.0-preview.5':
optional: true
'@next/swc-linux-x64-musl@16.2.6':
optional: true
- '@next/swc-linux-x64-musl@16.2.9':
+ '@next/swc-linux-x64-musl@16.3.0-preview.5':
optional: true
'@next/swc-win32-arm64-msvc@16.2.6':
optional: true
- '@next/swc-win32-arm64-msvc@16.2.9':
+ '@next/swc-win32-arm64-msvc@16.3.0-preview.5':
optional: true
'@next/swc-win32-x64-msvc@16.2.6':
optional: true
- '@next/swc-win32-x64-msvc@16.2.9':
+ '@next/swc-win32-x64-msvc@16.3.0-preview.5':
optional: true
- '@next/third-parties@16.2.9(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)':
+ '@next/third-parties@16.2.9(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)':
dependencies:
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
react: 19.2.3
third-party-capital: 1.0.20
@@ -13711,9 +13715,9 @@ snapshots:
'@use-gesture/core': 10.3.1
react: 19.2.7
- '@vercel/analytics@2.0.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)':
+ '@vercel/analytics@2.0.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)':
optionalDependencies:
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
react: 19.2.3
'@vercel/blob@2.4.1':
@@ -13743,21 +13747,21 @@ snapshots:
'@opentelemetry/api': 1.9.1
next: 16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
- '@vercel/edge-config@1.4.3(@opentelemetry/api@1.9.1)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))':
+ '@vercel/edge-config@1.4.3(@opentelemetry/api@1.9.1)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))':
dependencies:
'@vercel/edge-config-fs': 0.1.0
optionalDependencies:
'@opentelemetry/api': 1.9.1
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
- '@vercel/flags-core@1.5.1(@aws-sdk/credential-provider-web-identity@3.972.55)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(ws@8.21.0)':
+ '@vercel/flags-core@1.5.1(@aws-sdk/credential-provider-web-identity@3.972.55)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(ws@8.21.0)':
dependencies:
'@vercel/functions': 3.7.3(@aws-sdk/credential-provider-web-identity@3.972.55)(ws@8.21.0)
'@vercel/oidc': 3.5.0
jose: 5.2.1
js-xxhash: 4.0.0
optionalDependencies:
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
transitivePeerDependencies:
- '@aws-sdk/credential-provider-web-identity'
- ws
@@ -13779,7 +13783,7 @@ snapshots:
dependencies:
'@upstash/redis': 1.38.0
- '@vercel/microfrontends@2.3.6(@vercel/analytics@2.0.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(@vercel/speed-insights@1.3.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.3.6(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0))':
+ '@vercel/microfrontends@2.3.6(@vercel/analytics@2.0.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(@vercel/speed-insights@1.3.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.3.6(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0))':
dependencies:
'@next/env': 16.0.10
'@types/md5': 2.3.6
@@ -13794,9 +13798,9 @@ snapshots:
path-to-regexp: 6.3.0
semver: 7.8.5
optionalDependencies:
- '@vercel/analytics': 2.0.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
- '@vercel/speed-insights': 1.3.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ '@vercel/analytics': 2.0.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
+ '@vercel/speed-insights': 1.3.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
vite: 7.3.6(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0)
@@ -13818,22 +13822,22 @@ snapshots:
'@vercel/cli-exec': 1.0.0
jose: 5.10.0
- '@vercel/speed-insights@1.3.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)':
+ '@vercel/speed-insights@1.3.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3)':
optionalDependencies:
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
react: 19.2.3
- '@vercel/toolbar@0.2.6(@vercel/analytics@2.0.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(@vercel/speed-insights@1.3.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.3.6(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0))':
+ '@vercel/toolbar@0.2.6(@vercel/analytics@2.0.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(@vercel/speed-insights@1.3.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.3.6(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0))':
dependencies:
'@tinyhttp/app': 1.3.0
- '@vercel/microfrontends': 2.3.6(@vercel/analytics@2.0.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(@vercel/speed-insights@1.3.1(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.3.6(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0))
+ '@vercel/microfrontends': 2.3.6(@vercel/analytics@2.0.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(@vercel/speed-insights@1.3.1(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3))(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.3.6(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0))
chokidar: 3.6.0
execa: 5.1.1
find-up: 5.0.0
get-port: 5.1.1
strip-ansi: 6.0.1
optionalDependencies:
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
react: 19.2.3
vite: 7.3.6(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0)
transitivePeerDependencies:
@@ -14031,7 +14035,7 @@ snapshots:
baseline-browser-mapping@2.10.38: {}
- better-auth@1.6.20(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.1)(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@typescript/typescript6@6.0.1)(magicast@0.5.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)))(mysql2@3.15.3)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(pg@8.22.0)(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@typescript/typescript6@6.0.1)(magicast@0.5.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vitest@3.2.6(@types/debug@4.1.13)(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0)):
+ better-auth@1.6.20(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.1)(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@typescript/typescript6@6.0.1)(magicast@0.5.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)))(mysql2@3.15.3)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(pg@8.22.0)(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@typescript/typescript6@6.0.1)(magicast@0.5.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vitest@3.2.6(@types/debug@4.1.13)(@types/node@20.19.43)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(tsx@4.20.5)(yaml@2.9.0)):
dependencies:
'@better-auth/core': 1.6.20(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.6(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0)
'@better-auth/drizzle-adapter': 1.6.20(@better-auth/core@1.6.20(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.6(zod@4.4.3))(jose@6.2.3)(kysely@0.29.2)(nanostores@1.3.0))(@better-auth/utils@0.4.2)
@@ -14053,7 +14057,7 @@ snapshots:
optionalDependencies:
'@prisma/client': 7.8.0(@typescript/typescript6@6.0.1)(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@typescript/typescript6@6.0.1)(magicast@0.5.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
mysql2: 3.15.3
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
pg: 8.22.0
prisma: 7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@typescript/typescript6@6.0.1)(magicast@0.5.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
@@ -15024,13 +15028,13 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
- flags@4.2.0(@opentelemetry/api@1.9.1)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
+ flags@4.2.0(@opentelemetry/api@1.9.1)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@edge-runtime/cookies': 5.0.2
jose: 5.10.0
optionalDependencies:
'@opentelemetry/api': 1.9.1
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
@@ -15172,9 +15176,9 @@ snapshots:
fuse.js@7.4.2: {}
- geist@1.7.2(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)):
+ geist@1.7.2(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)):
dependencies:
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
generate-function@2.3.1:
dependencies:
@@ -16400,10 +16404,10 @@ snapshots:
negotiator@1.0.0: {}
- next-axiom@1.10.0(@aws-sdk/credential-provider-web-identity@3.972.55)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3):
+ next-axiom@1.10.0(@aws-sdk/credential-provider-web-identity@3.972.55)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3):
dependencies:
'@vercel/functions': 2.2.13(@aws-sdk/credential-provider-web-identity@3.972.55)
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
react: 19.2.3
use-deep-compare: 1.3.0(react@19.2.3)
whatwg-fetch: 3.6.20
@@ -16412,14 +16416,14 @@ snapshots:
next-intl-swc-plugin-extractor@4.13.0: {}
- next-intl@4.13.0(@swc/helpers@0.5.17)(@typescript/typescript6@6.0.1)(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3):
+ next-intl@4.13.0(@swc/helpers@0.5.17)(@typescript/typescript6@6.0.1)(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3):
dependencies:
'@formatjs/intl-localematcher': 0.8.10
'@parcel/watcher': 2.5.1
'@swc/core': 1.15.43(@swc/helpers@0.5.17)
icu-minify: 4.13.0
negotiator: 1.0.0
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
next-intl-swc-plugin-extractor: 4.13.0
po-parser: 2.1.1
react: 19.2.3
@@ -16434,9 +16438,9 @@ snapshots:
next: 16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
react: 19.2.7
- next-seo@7.2.0(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3):
+ next-seo@7.2.0(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3):
dependencies:
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
react: 19.2.3
next-themes@0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
@@ -16503,25 +16507,25 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
- next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0):
+ next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0):
dependencies:
- '@next/env': 16.2.9
+ '@next/env': 16.3.0-preview.5
'@swc/helpers': 0.5.15
baseline-browser-mapping: 2.10.38
caniuse-lite: 1.0.30001799
- postcss: 8.5.15
+ postcss: 8.5.10
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.3)
optionalDependencies:
- '@next/swc-darwin-arm64': 16.2.9
- '@next/swc-darwin-x64': 16.2.9
- '@next/swc-linux-arm64-gnu': 16.2.9
- '@next/swc-linux-arm64-musl': 16.2.9
- '@next/swc-linux-x64-gnu': 16.2.9
- '@next/swc-linux-x64-musl': 16.2.9
- '@next/swc-win32-arm64-msvc': 16.2.9
- '@next/swc-win32-x64-msvc': 16.2.9
+ '@next/swc-darwin-arm64': 16.3.0-preview.5
+ '@next/swc-darwin-x64': 16.3.0-preview.5
+ '@next/swc-linux-arm64-gnu': 16.3.0-preview.5
+ '@next/swc-linux-arm64-musl': 16.3.0-preview.5
+ '@next/swc-linux-x64-gnu': 16.3.0-preview.5
+ '@next/swc-linux-x64-musl': 16.3.0-preview.5
+ '@next/swc-win32-arm64-msvc': 16.3.0-preview.5
+ '@next/swc-win32-x64-msvc': 16.3.0-preview.5
'@opentelemetry/api': 1.9.1
babel-plugin-react-compiler: 1.0.0
sass: 1.101.0
@@ -16530,13 +16534,13 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
- nextstepjs@2.2.0(motion@12.42.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
+ nextstepjs@2.2.0(motion@12.42.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
motion: 12.42.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
optionalDependencies:
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
node-addon-api@7.1.1: {}
@@ -16559,12 +16563,12 @@ snapshots:
dependencies:
boolbase: 1.0.0
- nuqs@2.8.9(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3):
+ nuqs@2.8.9(next@16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0))(react@19.2.3):
dependencies:
'@standard-schema/spec': 1.0.0
react: 19.2.3
optionalDependencies:
- next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
+ next: 16.3.0-preview.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.101.0)
nypm@0.6.6:
dependencies:
@@ -16899,6 +16903,12 @@ snapshots:
path-data-parser: 0.1.0
points-on-curve: 0.2.0
+ postcss@8.5.10:
+ dependencies:
+ nanoid: 3.3.15
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
postcss@8.5.15:
dependencies:
nanoid: 3.3.15