Merge remote-tracking branch 'origin/dev' into llm-mcp-flow

This commit is contained in:
Aadesh Kheria 2026-04-14 21:20:24 -07:00
commit 15e587980f
65 changed files with 1024 additions and 303 deletions

View File

@ -22,6 +22,8 @@ concurrency:
env:
EMULATOR_IMAGE_NAME: stack-local-emulator
EMULATOR_IMAGE_DIR: ${{ github.workspace }}/docker/local-emulator/qemu/images
EMULATOR_RUN_DIR: ${{ github.workspace }}/docker/local-emulator/qemu/run
jobs:
build:

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/backend",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"type": "module",
@ -99,7 +99,7 @@
"jiti": "^2.6.1",
"jose": "^6.1.3",
"json-diff": "^1.0.6",
"next": "16.2.2",
"next": "16.1.7",
"nodemailer": "^6.9.10",
"oidc-provider": "^8.5.1",
"openid-client": "5.6.4",

View File

@ -259,6 +259,45 @@ export async function seed() {
console.log('Internal team created');
}
// Upsert the internal API key set before any flake-prone work (dummy-project
// seed, email/svix, clickhouse). The emulator CLI authenticates against the
// internal project using the pck stored here, so it must land before the rest
// of the seed even if something later fails.
const isLocalEmulator = process.env.NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR === 'true';
const rawPck = process.env.STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY;
if (isLocalEmulator && !rawPck) {
// Emulator images build before a per-VM pck is available. Runtime boots set
// STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY from the VM-generated
// random value and re-run the seed, which upserts the internal key set then.
console.log('Skipping internal API key set (no pck provided; emulator mode).');
} else {
const keySet = {
publishableClientKey: rawPck || throwErr('STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY is not set'),
secretServerKey: isLocalEmulator
? (process.env.STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY ?? null)
: (process.env.STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY || throwErr('STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY is not set')),
superSecretAdminKey: isLocalEmulator
? (process.env.STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY ?? null)
: (process.env.STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY || throwErr('STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY is not set')),
};
await globalPrismaClient.apiKeySet.upsert({
where: { projectId_id: { projectId: 'internal', id: apiKeyId } },
update: {
...keySet,
},
create: {
id: apiKeyId,
projectId: 'internal',
description: "Internal API key set",
expiresAt: new Date('2099-12-31T23:59:59Z'),
...keySet,
}
});
console.log('Updated internal API key set');
}
const shouldSeedDummyProject = process.env.STACK_SEED_ENABLE_DUMMY_PROJECT === 'true';
if (shouldSeedDummyProject) {
await seedDummyProject({
@ -268,28 +307,6 @@ export async function seed() {
});
}
const keySet = {
publishableClientKey: process.env.STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY || throwErr('STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY is not set'),
secretServerKey: process.env.STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY || throwErr('STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY is not set'),
superSecretAdminKey: process.env.STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY || throwErr('STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY is not set'),
};
await globalPrismaClient.apiKeySet.upsert({
where: { projectId_id: { projectId: 'internal', id: apiKeyId } },
update: {
...keySet,
},
create: {
id: apiKeyId,
projectId: 'internal',
description: "Internal API key set",
expiresAt: new Date('2099-12-31T23:59:59Z'),
...keySet,
}
});
console.log('Updated internal API key set');
// Create optional default admin user if credentials are provided.
// This user will be able to login to the dashboard with both email/password and magic link.

View File

@ -130,7 +130,7 @@ export const GET = createSmartRouteHandler({
async function processRequest(request: OutgoingRequest): Promise<void> {
// Prisma JsonValue doesn't carry a precise shape for this JSON blob.
const options = request.qstashOptions as any;
const baseUrl = getEnvVariable("NEXT_PUBLIC_STACK_API_URL");
const baseUrl = getEnvVariable("NEXT_PUBLIC_SERVER_STACK_API_URL", "") || getEnvVariable("NEXT_PUBLIC_STACK_API_URL");
let fullUrl = new URL(options.url, baseUrl).toString();
@ -157,7 +157,7 @@ export const GET = createSmartRouteHandler({
function buildUpstashRequest(request: OutgoingRequest): UpstashRequest {
// Prisma JsonValue doesn't carry a precise shape for this JSON blob.
const options = request.qstashOptions as any;
const baseUrl = getEnvVariable("NEXT_PUBLIC_STACK_API_URL");
const baseUrl = getEnvVariable("NEXT_PUBLIC_SERVER_STACK_API_URL", "") || getEnvVariable("NEXT_PUBLIC_STACK_API_URL");
let fullUrl = new URL(options.url, baseUrl).toString();

View File

@ -1,4 +1,5 @@
import { Prisma } from "@/generated/prisma/client";
import { overrideEnvironmentConfigOverride } from "@/lib/config";
import {
LOCAL_EMULATOR_ADMIN_USER_ID,
LOCAL_EMULATOR_ONLY_ENDPOINT_MESSAGE,
@ -58,14 +59,15 @@ async function assertLocalEmulatorOwnerTeamReadiness() {
}
}
async function getOrCreateLocalEmulatorProjectId(absoluteFilePath: string): Promise<string> {
async function getOrCreateLocalEmulatorProjectId(absoluteFilePath: string): Promise<{ projectId: string, created: boolean }> {
const existingRows = await globalPrismaClient.$queryRaw<LocalEmulatorProjectMappingRow[]>(Prisma.sql`
SELECT "projectId"
FROM "LocalEmulatorProject"
WHERE "absoluteFilePath" = ${absoluteFilePath}
LIMIT 1
`);
const projectId = existingRows[0] ? existingRows[0].projectId : generateUuid();
const existingRow = existingRows.length > 0 ? existingRows[0] : undefined;
const projectId = existingRow ? existingRow.projectId : generateUuid();
await globalPrismaClient.project.upsert({
where: {
@ -98,6 +100,25 @@ async function getOrCreateLocalEmulatorProjectId(absoluteFilePath: string): Prom
},
});
const created = existingRow === undefined;
// Seed environment-level defaults BEFORE registering as a LocalEmulatorProject:
// once registered, setEnvironmentConfigOverride refuses to write.
// - domains.allowLocalhost: fresh emulator projects allow localhost redirects
// so developers don't hit "Redirect URL not whitelisted" before configuring
// trustedDomains.
// - payments.testMode: emulator payments always go through stripe-mock.
if (created) {
await overrideEnvironmentConfigOverride({
projectId,
branchId: DEFAULT_BRANCH_ID,
environmentConfigOverrideOverride: {
"domains.allowLocalhost": true,
"payments.testMode": true,
},
});
}
await globalPrismaClient.$executeRaw(Prisma.sql`
INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt")
VALUES (${absoluteFilePath}, ${projectId}, NOW(), NOW())
@ -107,7 +128,7 @@ async function getOrCreateLocalEmulatorProjectId(absoluteFilePath: string): Prom
"updatedAt" = NOW()
`);
return projectId;
return { projectId, created };
}
async function getOrCreateCredentials(projectId: string) {
@ -142,7 +163,7 @@ async function getOrCreateCredentials(projectId: string) {
},
});
if (!keySet.secretServerKey || !keySet.superSecretAdminKey) {
if (!keySet.publishableClientKey || !keySet.secretServerKey || !keySet.superSecretAdminKey) {
throw new StackAssertionError("Local emulator key set is missing required keys.", {
projectId,
keySetId: keySet.id,
@ -150,6 +171,7 @@ async function getOrCreateCredentials(projectId: string) {
}
return {
publishableClientKey: keySet.publishableClientKey,
secretServerKey: keySet.secretServerKey,
superSecretAdminKey: keySet.superSecretAdminKey,
};
@ -179,6 +201,7 @@ export const POST = createSmartRouteHandler({
bodyType: yupString().oneOf(["json"]).defined(),
body: yupObject({
project_id: yupString().defined(),
publishable_client_key: yupString().defined(),
secret_server_key: yupString().defined(),
super_secret_admin_key: yupString().defined(),
branch_config_override_string: yupString().defined(),
@ -215,7 +238,7 @@ export const POST = createSmartRouteHandler({
await assertLocalEmulatorOwnerTeamReadiness();
const projectId = await getOrCreateLocalEmulatorProjectId(absoluteFilePath);
const { projectId } = await getOrCreateLocalEmulatorProjectId(absoluteFilePath);
const credentials = await getOrCreateCredentials(projectId);
const fileConfig = await readConfigFromFile(absoluteFilePath);
@ -224,6 +247,7 @@ export const POST = createSmartRouteHandler({
bodyType: "json" as const,
body: {
project_id: projectId,
publishable_client_key: credentials.publishableClientKey,
secret_server_key: credentials.secretServerKey,
super_secret_admin_key: credentials.superSecretAdminKey,
branch_config_override_string: JSON.stringify(fileConfig),

View File

@ -1,3 +1,4 @@
import { isLocalEmulatorEnabled } from "@/lib/local-emulator";
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
import { getNodeEnvironment } from "@stackframe/stack-shared/dist/utils/env";
@ -59,7 +60,7 @@ export const ALLOWED_MODEL_IDS: ReadonlySet<string> = new Set([
]);
export function createOpenRouterProvider() {
const baseURL = getNodeEnvironment() === "development"
const baseURL = (getNodeEnvironment() === "development" || isLocalEmulatorEnabled())
? "http://localhost:8102/api/latest/integrations/ai-proxy/v1"
: "https://api.stack-auth.com/api/latest/integrations/ai-proxy/v1";
return createOpenRouter({

View File

@ -1,6 +1,7 @@
import { traceSpan } from '@/utils/telemetry';
import { runAsynchronouslyAndWaitUntil } from '@/utils/background-tasks';
import { getEnvVariable, getNodeEnvironment } from '@stackframe/stack-shared/dist/utils/env';
import { isLocalEmulatorEnabled } from "@/lib/local-emulator";
import { StackAssertionError, captureError } from '@stackframe/stack-shared/dist/utils/errors';
import { Result } from '@stackframe/stack-shared/dist/utils/results';
import { Sandbox } from '@vercel/sandbox';
@ -27,11 +28,13 @@ function createFreestyleEngine(): JsEngine {
let baseUrl = getEnvVariable("STACK_FREESTYLE_API_ENDPOINT", "") || undefined;
if (apiKey === "mock_stack_freestyle_key") {
if (!["development", "test"].includes(getNodeEnvironment())) {
if (!["development", "test"].includes(getNodeEnvironment()) && !isLocalEmulatorEnabled()) {
throw new StackAssertionError("Mock Freestyle key used in production; please set the STACK_FREESTYLE_API_KEY environment variable.");
}
const prefix = getEnvVariable("NEXT_PUBLIC_STACK_PORT_PREFIX", "81");
baseUrl = `http://localhost:${prefix}22`;
if (!baseUrl) {
const prefix = getEnvVariable("NEXT_PUBLIC_STACK_PORT_PREFIX", "81");
baseUrl = `http://localhost:${prefix}22`;
}
}
const freestyle = new FreestyleClient({
@ -147,7 +150,7 @@ export async function executeJavascript(code: string, options: ExecuteJavascript
return await runWithFallback(code, options);
} else {
if (getNodeEnvironment().includes("prod")) {
if (getNodeEnvironment().includes("prod") && !isLocalEmulatorEnabled()) {
throw new StackAssertionError("STACK_VERCEL_SANDBOX_TOKEN is set to the disabled sentinel value in production. Please configure a real Vercel Sandbox token.");
}

View File

@ -7,7 +7,6 @@ import type { UsersCrud } from "@stackframe/stack-shared/dist/interface/crud/use
import type { inlineProductSchema, productSchema, productSchemaWithMetadata } from "@stackframe/stack-shared/dist/schema-fields";
import { SUPPORTED_CURRENCIES } from "@stackframe/stack-shared/dist/utils/currency-constants";
import { FAR_FUTURE_DATE, addInterval, getIntervalsElapsed } from "@stackframe/stack-shared/dist/utils/dates";
import { getEnvVariable, getNodeEnvironment } from "@stackframe/stack-shared/dist/utils/env";
import { StackAssertionError, StatusError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
import { filterUndefined, getOrUndefined, has, typedEntries, typedFromEntries, typedKeys, typedValues } from "@stackframe/stack-shared/dist/utils/objects";
import { typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
@ -15,11 +14,9 @@ import { isUuid } from "@stackframe/stack-shared/dist/utils/uuids";
import Stripe from "stripe";
import * as yup from "yup";
import { Tenancy } from "./tenancies";
import { getStripeForAccount } from "./stripe";
import { getStripeForAccount, useStripeMock } from "./stripe";
const DEFAULT_PRODUCT_START_DATE = new Date("1973-01-01T12:00:00.000Z"); // monday
const stripeSecretKey = getEnvVariable("STACK_STRIPE_SECRET_KEY", "");
const useStripeMock = stripeSecretKey === "sk_test_mockstripekey" && ["development", "test"].includes(getNodeEnvironment());
type Product = yup.InferType<typeof productSchema>;
type ProductWithMetadata = yup.InferType<typeof productSchemaWithMetadata>;

View File

@ -8,15 +8,18 @@ import { getEnvVariable, getNodeEnvironment } from "@stackframe/stack-shared/dis
import { captureError, StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
import Stripe from "stripe";
import type * as yup from "yup";
import { isLocalEmulatorEnabled } from "./local-emulator";
import { createStripeProxy, type StripeOverridesMap } from "./stripe-proxy";
const stripeSecretKey = getEnvVariable("STACK_STRIPE_SECRET_KEY", "");
const useStripeMock = stripeSecretKey === "sk_test_mockstripekey" && ["development", "test"].includes(getNodeEnvironment());
export const useStripeMock = isLocalEmulatorEnabled()
|| (stripeSecretKey === "sk_test_mockstripekey" && ["development", "test"].includes(getNodeEnvironment()));
const stackPortPrefix = getEnvVariable("NEXT_PUBLIC_STACK_PORT_PREFIX", "81");
const stripeMockPort = Number(getEnvVariable("STACK_STRIPE_MOCK_PORT", "") || `${stackPortPrefix}23`);
const stripeConfig: Stripe.StripeConfig = useStripeMock ? {
protocol: "http",
host: "localhost",
port: Number(`${stackPortPrefix}23`),
port: stripeMockPort,
} : {};
/** Product type as stored in Stripe metadata (same as config product schema) */

View File

@ -28,6 +28,12 @@ export async function ensureUpstashSignature(fullReq: SmartRequest): Promise<voi
if ((nodeEnv.includes("development") || nodeEnv.includes("test")) && url.hostname === "localhost") {
url.hostname = "host.docker.internal";
}
// The backend binds to 0.0.0.0, so Next.js reports the incoming URL with that
// hostname. QStash signs the URL we told it to call (e.g. localhost), so
// normalize 0.0.0.0 back to localhost for signature verification.
if (url.hostname === "0.0.0.0") {
url.hostname = "localhost";
}
const isValid = await upstashReceiver.verify({
signature: upstashSignature,

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/dashboard",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"scripts": {
@ -84,7 +84,7 @@
"input-otp": "^1.4.1",
"jose": "^6.1.3",
"lodash": "^4.17.21",
"next": "16.2.2",
"next": "16.1.7",
"next-themes": "^0.2.1",
"posthog-js": "^1.336.1",
"react": "19.2.3",

View File

@ -49,9 +49,75 @@ const AVAILABLE_TABLES = new Map([
defaultOrderBy: "event_at",
defaultOrderDir: "DESC" as const,
}],
["users", {
displayName: "Users",
baseQuery: "SELECT * FROM default.users",
defaultOrderBy: "signed_up_at",
defaultOrderDir: "DESC" as const,
}],
["contact_channels", {
displayName: "Contact Channels",
baseQuery: "SELECT * FROM default.contact_channels",
defaultOrderBy: "created_at",
defaultOrderDir: "DESC" as const,
}],
["teams", {
displayName: "Teams",
baseQuery: "SELECT * FROM default.teams",
defaultOrderBy: "created_at",
defaultOrderDir: "DESC" as const,
}],
["team_member_profiles", {
displayName: "Team Member Profiles",
baseQuery: "SELECT * FROM default.team_member_profiles",
defaultOrderBy: "created_at",
defaultOrderDir: "DESC" as const,
}],
["team_permissions", {
displayName: "Team Permissions",
baseQuery: "SELECT * FROM default.team_permissions",
defaultOrderBy: "created_at",
defaultOrderDir: "DESC" as const,
}],
["team_invitations", {
displayName: "Team Invitations",
baseQuery: "SELECT * FROM default.team_invitations",
defaultOrderBy: "created_at",
defaultOrderDir: "DESC" as const,
}],
["email_outboxes", {
displayName: "Email Outboxes",
baseQuery: "SELECT * FROM default.email_outboxes",
defaultOrderBy: "created_at",
defaultOrderDir: "DESC" as const,
}],
["project_permissions", {
displayName: "Project Permissions",
baseQuery: "SELECT * FROM default.project_permissions",
defaultOrderBy: "created_at",
defaultOrderDir: "DESC" as const,
}],
["notification_preferences", {
displayName: "Notification Preferences",
baseQuery: "SELECT * FROM default.notification_preferences",
defaultOrderBy: "user_id",
defaultOrderDir: "DESC" as const,
}],
["refresh_tokens", {
displayName: "Refresh Tokens",
baseQuery: "SELECT * FROM default.refresh_tokens",
defaultOrderBy: "created_at",
defaultOrderDir: "DESC" as const,
}],
["connected_accounts", {
displayName: "Connected Accounts",
baseQuery: "SELECT * FROM default.connected_accounts",
defaultOrderBy: "created_at",
defaultOrderDir: "DESC" as const,
}],
]);
type TableId = "events";
type TableId = string;
type SortDir = "ASC" | "DESC";
const PAGE_SIZE = 50;

View File

@ -482,6 +482,9 @@ export default function PageClient() {
mockProject={{
config: {
...project.config,
credentialEnabled: passwordEnabled,
magicLinkEnabled: otpEnabled,
passkeyEnabled: passkeyEnabled,
oauthProviders: enabledProviders
.map(([, provider]) => provider)
.filter((provider): provider is AdminOAuthProviderConfig => !!provider),

View File

@ -457,12 +457,10 @@ function NewDraftDropdown({
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="center">
<DropdownMenuItem onClick={onCreateFromScratch} className="gap-2">
<FileText className="h-4 w-4" />
<DropdownMenuItem onClick={onCreateFromScratch} icon={<FileText className="h-4 w-4" />}>
Create from scratch
</DropdownMenuItem>
<DropdownMenuItem onClick={onCreateFromTemplate} className="gap-2">
<Copy className="h-4 w-4" />
<DropdownMenuItem onClick={onCreateFromTemplate} icon={<Copy className="h-4 w-4" />}>
Create from template
</DropdownMenuItem>
</DropdownMenuContent>
@ -480,12 +478,10 @@ function NewDraftDropdown({
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={onCreateFromScratch} className="gap-2">
<FileText className="h-4 w-4" />
<DropdownMenuItem onClick={onCreateFromScratch} icon={<FileText className="h-4 w-4" />}>
Create from scratch
</DropdownMenuItem>
<DropdownMenuItem onClick={onCreateFromTemplate} className="gap-2">
<Copy className="h-4 w-4" />
<DropdownMenuItem onClick={onCreateFromTemplate} icon={<Copy className="h-4 w-4" />}>
Create from template
</DropdownMenuItem>
</DropdownMenuContent>

View File

@ -61,7 +61,9 @@ function PaymentsLayoutInner({ children }: { children: React.ReactNode }) {
});
};
if (!stripeAccountInfo) {
const isLocalEmulator = getPublicEnvVar("NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR") === "true";
if (!stripeAccountInfo && !isLocalEmulator) {
return (
<div className="mx-auto max-w-sm h-full flex items-center">
<Card className="w-full">
@ -172,7 +174,7 @@ function PaymentsLayoutInner({ children }: { children: React.ReactNode }) {
</div>
</div>
</div>
) : !stripeAccountInfo.details_submitted && (
) : stripeAccountInfo && !stripeAccountInfo.details_submitted && (
<div className="flex justify-center px-4 pt-4 sm:px-6 sm:pt-6">
<div className={cn(
"w-full max-w-[1250px] rounded-2xl p-4 sm:p-5",
@ -236,7 +238,7 @@ function PaymentsLayoutInner({ children }: { children: React.ReactNode }) {
</div>
</div>
)}
{getPublicEnvVar("NEXT_PUBLIC_STACK_IS_PREVIEW") !== "true" && (
{getPublicEnvVar("NEXT_PUBLIC_STACK_IS_PREVIEW") !== "true" && getPublicEnvVar("NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR") !== "true" && (
<div className={cn(bannerHasItems && "p-4", "flex justify-center")}>
<div style={{ maxWidth: 1250, width: '100%' }}>
<ConnectNotificationBanner

View File

@ -8,12 +8,13 @@ import { StripeConnectProvider } from "@/components/payments/stripe-connect-prov
export default function PageClient() {
const isPreview = getPublicEnvVar("NEXT_PUBLIC_STACK_IS_PREVIEW") === "true";
const isLocalEmulator = getPublicEnvVar("NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR") === "true";
return (
<PageLayout title="Payouts">
{isPreview ? (
{isPreview || isLocalEmulator ? (
<Alert>
Payouts are unavailable in preview mode.
Payouts are unavailable in {isLocalEmulator ? "the local emulator" : "preview mode"}.
</Alert>
) : (
<StripeConnectProvider>

View File

@ -1630,9 +1630,9 @@ function Draggable(props: {
<div className="text-red-500 text-sm p-2 bg-red-500/10 font-mono whitespace-pre-wrap">
A runtime error occured while rendering this widget.<br />
<br />
<button className="text-blue-500 hover:underline" onClick={() => {
props.reset();
}}>Reload widget</button><br />
{props.reset && <button className="text-blue-500 hover:underline" onClick={() => {
props.reset!();
}}>Reload widget</button>}<br />
<br />
{errorToNiceString(props.error)}
</div>

View File

@ -211,8 +211,8 @@ export const ToolInvocationCard = memo(function ToolInvocationCard({
const { label, icon: Icon } = getToolDisplay();
const input = invocation.input as { query?: string };
const queryArg = input.query;
const input = invocation.input as { query?: string } | undefined;
const queryArg = input?.query;
const result = invocation.output as { success?: boolean, result?: unknown[], error?: string, rowCount?: number };
return (

View File

@ -1,6 +1,6 @@
'use client';
import { InternalApiKey } from '@stackframe/stack';
import { DesignDataTable } from "@/components/design-components";
import { DesignCard, DesignDataTable } from "@/components/design-components";
import { ActionCell, ActionDialog, BadgeCell, DataTableColumnHeader, DataTableFacetedFilter, DateCell, SearchToolbarItem, TextCell, standardFilterFn } from "@/components/ui";
import { ColumnDef, Row, Table } from "@tanstack/react-table";
import { useMemo, useState } from "react";
@ -144,12 +144,13 @@ export function InternalApiKeyTable(props: { apiKeys: InternalApiKey[], showPubl
});
}, [props.apiKeys]);
return <DesignDataTable
data={extendedApiKeys}
columns={columns}
toolbarRender={toolbarRender}
defaultColumnFilters={[{ id: 'status', value: ['valid'] }]}
defaultSorting={[]}
glassmorphic
/>;
return <DesignCard glassmorphic>
<DesignDataTable
data={extendedApiKeys}
columns={columns}
toolbarRender={toolbarRender}
defaultColumnFilters={[{ id: 'status', value: ['valid'] }]}
defaultSorting={[]}
/>
</DesignCard>;
}

View File

@ -13,6 +13,7 @@ import { useEffect } from "react";
import { appearanceVariablesForTheme } from "./stripe-theme-variables";
const isPreview = getPublicEnvVar("NEXT_PUBLIC_STACK_IS_PREVIEW") === "true";
const isLocalEmulator = getPublicEnvVar("NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR") === "true";
type StripeConnectProviderProps = {
children: React.ReactNode,
@ -36,7 +37,7 @@ export function StripeConnectProvider({ children }: StripeConnectProviderProps)
const adminApp = useAdminApp();
const { resolvedTheme } = useTheme();
const stripeConnectInstance = isPreview ? null : getStripeConnectInstance(adminApp);
const stripeConnectInstance = isPreview || isLocalEmulator ? null : getStripeConnectInstance(adminApp);
useEffect(() => {
if (!stripeConnectInstance) return;
@ -47,7 +48,7 @@ export function StripeConnectProvider({ children }: StripeConnectProviderProps)
});
}, [resolvedTheme, stripeConnectInstance]);
// In preview mode, skip Stripe Connect initialization entirely
// In preview/emulator mode, skip Stripe Connect initialization entirely
if (!stripeConnectInstance) {
return <>{children}</>;
}

View File

@ -166,7 +166,7 @@ export function RepeatingInput({
disabled={disabled || readOnly}
className={cn(
"rounded-r-none border-0 focus-visible:ring-0 focus-visible:ring-offset-0",
prefix && "pl-7",
prefix && "!pl-7",
inputClassName
)}
/>

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/dev-launchpad",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"scripts": {

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/e2e-tests",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"type": "module",

View File

@ -1,7 +1,7 @@
{
"name": "@stackframe/hosted-components",
"private": true,
"version": "2.8.83",
"version": "2.8.84",
"type": "module",
"scripts": {
"dev": "vite dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09",

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/mock-oauth-server",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"main": "index.js",

View File

@ -52,6 +52,7 @@ COPY docs ./docs
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
ENV NEXT_CONFIG_OUTPUT=standalone
ENV NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY=pk_test_mock_publishable_key_for_local_emulator
# Build the backend NextJS app
RUN pnpm turbo run docker-build --filter=@stackframe/backend... --filter=@stackframe/dashboard...
@ -87,8 +88,47 @@ RUN cp -a /app/node_modules /pruned-node_modules && \
date-fns@2* date-fns@3*
# ── Freestyle mock build ─────────────────────────────────────────────────────
FROM node-base AS freestyle-mock-builder
WORKDIR /freestyle-mock
COPY docker/dependencies/freestyle-mock/Dockerfile /tmp/freestyle-mock-dockerfile
# Extract the inline package.json and server.mjs from the Dockerfile's RUN cat commands,
# then install dependencies. This avoids duplicating the source.
RUN node -e " \
const fs = require('fs'); \
const df = fs.readFileSync('/tmp/freestyle-mock-dockerfile', 'utf8'); \
const pkgMatch = df.match(/cat <<'EOF' > package\\.json\\n([\\s\\S]*?)\\nEOF/); \
fs.writeFileSync('package.json', pkgMatch[1]); \
const srvMatch = df.match(/cat <<'EOF' > server\\.mjs\\n([\\s\\S]*?)\\nEOF/); \
let server = srvMatch[1]; \
server = server.replace('server.listen(8080)', 'server.listen(process.env.PORT || 8080)'); \
server = server.replace( \
'from \"fs/promises\"', \
'from \"fs/promises\"; import { symlinkSync } from \"fs\"' \
); \
server = server.replace( \
'await mkdir(workDir, { recursive: true });', \
'await mkdir(workDir, { recursive: true }); try { symlinkSync(\"/app/freestyle-mock/node_modules\", join(workDir, \"node_modules\")); } catch {}' \
); \
fs.writeFileSync('server.mjs', server); \
"
RUN npm install
# ── Mock OAuth server build ───────────────────────────────────────────────────
FROM node-base AS mock-oauth-builder
WORKDIR /mock-oauth
COPY apps/mock-oauth-server/package.json .
RUN pnpm install && pnpm add esbuild --save-dev
COPY apps/mock-oauth-server/src ./src
RUN npx esbuild src/index.ts --bundle --platform=node --target=node22 --outfile=dist/index.cjs
# ── Service binary stages ─────────────────────────────────────────────────────
FROM stripe/stripe-mock:v0.195.0 AS stripe-mock-bin
FROM inbucket/inbucket:3.1.0 AS inbucket-bin
FROM svix/svix-server:v1.88.0 AS svix-bin
FROM clickhouse/clickhouse-server:25.10 AS clickhouse-bin
@ -159,6 +199,9 @@ COPY --from=node-base /usr/local/bin/node /usr/local/bin/node
# Inbucket
COPY --from=inbucket-bin /opt/inbucket /opt/inbucket
# Stripe mock
COPY --from=stripe-mock-bin /bin/stripe-mock /usr/local/bin/stripe-mock
# Svix (UPX-compressed)
COPY --from=upx-compress /out/svix-server /usr/local/bin/svix-server
@ -191,6 +234,14 @@ RUN cp -a /app/node_modules /app/node_modules.standalone 2>/dev/null || mkdir -p
COPY --from=migration-pruner /pruned-node_modules ./node_modules
COPY --from=builder /app/packages ./packages
# Mock OAuth server (bundled single file)
COPY --from=mock-oauth-builder /mock-oauth/dist/index.cjs /app/mock-oauth-server/index.cjs
# Freestyle mock (JS execution for email rendering)
COPY --from=freestyle-mock-builder /freestyle-mock /app/freestyle-mock
COPY --from=node-base /usr/local/bin/npm /usr/local/bin/npm
COPY --from=node-base /usr/local/lib/node_modules/npm /usr/local/lib/node_modules/npm
RUN mkdir -p \
/data/postgres \
/data/redis \
@ -207,17 +258,18 @@ RUN mkdir -p \
&& chown -R postgres:postgres /data/postgres
COPY docker/local-emulator/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY docker/local-emulator/run-cron-jobs.sh /run-cron-jobs.sh
COPY docker/local-emulator/entrypoint.sh /entrypoint.sh
COPY docker/local-emulator/init-services.sh /init-services.sh
COPY docker/local-emulator/start-app.sh /start-app.sh
COPY docker/local-emulator/clickhouse-config.xml /etc/clickhouse-server/config.xml
COPY docker/local-emulator/clickhouse-users.xml /etc/clickhouse-server/users.xml
COPY docker/server/entrypoint.sh /app-entrypoint.sh
RUN chmod +x /entrypoint.sh /init-services.sh /start-app.sh /app-entrypoint.sh
RUN chmod +x /entrypoint.sh /init-services.sh /start-app.sh /app-entrypoint.sh /run-cron-jobs.sh
# PostgreSQL: 5432, Redis: 6379, Inbucket: 2500/9001/1100,
# Svix: 8071, ClickHouse: 8123/9009, MinIO: 9090, QStash: 8080
# Backend: 8102, Dashboard: 8101
EXPOSE 5432 6379 2500 9001 1100 8071 8123 9009 9090 8080 8101 8102
# Backend: 8102, Dashboard: 8101, Mock OAuth: 8114
EXPOSE 5432 6379 2500 9001 1100 8071 8123 9009 9090 8080 8101 8102 8114
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -15,6 +15,8 @@
<max_server_memory_usage_to_ram_ratio>0.5</max_server_memory_usage_to_ram_ratio>
<custom_settings_prefixes>SQL_</custom_settings_prefixes>
<user_directories>
<users_xml>
<path>users.xml</path>

View File

@ -28,4 +28,11 @@ if [ -z "$(ls -A "$PGDATA" 2>/dev/null)" ]; then
gosu postgres "$PG_BIN/pg_ctl" -D "$PGDATA" stop -w
fi
# Generate a fresh CRON_SECRET per container start. The cron endpoints are
# internal — nothing outside the container calls them — so we don't want the
# baked-in mock value from .env.development to be a usable credential against
# a running emulator. Overriding here propagates to both the backend and the
# run-cron-jobs.sh loop via supervisord's inherited environment.
export CRON_SECRET="$(openssl rand -hex 32)"
exec /usr/bin/supervisord -n -c /etc/supervisor/conf.d/supervisord.conf

View File

@ -90,9 +90,11 @@ const entries = [
fromSource("apps/backend/.env.development", backendEnv, "STACK_SEED_INTERNAL_PROJECT_ALLOW_LOCALHOST"),
fromSource("apps/backend/.env.development", backendEnv, "STACK_SEED_INTERNAL_PROJECT_OAUTH_PROVIDERS"),
fromSource("apps/backend/.env.development", backendEnv, "STACK_SEED_INTERNAL_PROJECT_USER_INTERNAL_ACCESS"),
fromSource("apps/backend/.env.development", backendEnv, "STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY"),
fromSource("apps/backend/.env.development", backendEnv, "STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY"),
fromSource("apps/backend/.env.development", backendEnv, "STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY"),
// STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY is generated per-VM at boot
// by docker/local-emulator/qemu/cloud-init/emulator/user-data and injected via
// /run/stack-auth/local-emulator.env. SECRET_SERVER_KEY and SUPER_SECRET_ADMIN_KEY
// are intentionally omitted so the seed script leaves them null on the internal
// project; per-project credentials come from /api/v1/internal/local-emulator/project.
blank(),
comment("# Third-party/test integrations"),
fromSource("apps/backend/.env.development", backendEnv, "STACK_SVIX_API_KEY"),
@ -159,7 +161,7 @@ const entries = [
literal("STACK_S3_ENDPOINT", "http://127.0.0.1:9090"),
literal("STACK_QSTASH_URL", "http://127.0.0.1:8080"),
literal("STACK_CLICKHOUSE_URL", "http://127.0.0.1:8123"),
literal("STACK_CLICKHOUSE_DATABASE", "analytics"),
literal("STACK_CLICKHOUSE_DATABASE", "default"),
literal("STACK_EMAIL_MONITOR_INBUCKET_API_URL", "http://127.0.0.1:9001"),
literal("BACKEND_PORT", "8102"),
literal("DASHBOARD_PORT", "8101"),

View File

@ -5,7 +5,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=common.sh
source "$SCRIPT_DIR/common.sh"
IMAGE_DIR="$SCRIPT_DIR/images"
IMAGE_DIR="${EMULATOR_IMAGE_DIR:-$HOME/.stack/emulator/images}"
CLOUD_INIT_ROOT="$SCRIPT_DIR/cloud-init"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"

View File

@ -58,7 +58,7 @@ write_files:
#!/bin/bash
set -euo pipefail
mkdir -p /mnt/stack-runtime /run/stack-auth
mkdir -p /mnt/stack-runtime /run/stack-auth /var/lib/stack-auth
runtime_device="$(readlink -f /dev/disk/by-label/STACKCFG)"
mountpoint -q /mnt/stack-runtime || mount -o ro "$runtime_device" /mnt/stack-runtime
@ -67,6 +67,24 @@ write_files:
source /mnt/stack-runtime/base.env
set +a
# Generate and persist the internal-project keys on first boot; reuse
# across container restarts so the dashboard keeps its internal-project
# session. Reset via `stack emulator reset`.
#
# pck: used by stack-cli to auth against /api/v1/internal/local-emulator/project
# ssk/sak: required by the emulator's own dashboard (StackServerApp
# construction throws without them). Not used by user-app flows; the
# /local-emulator/project route mints separate per-project credentials.
umask 077
for key in internal-pck internal-ssk internal-sak; do
if [ ! -s "/var/lib/stack-auth/$key" ]; then
openssl rand -hex 32 > "/var/lib/stack-auth/$key"
fi
done
INTERNAL_PCK="$(cat /var/lib/stack-auth/internal-pck)"
INTERNAL_SSK="$(cat /var/lib/stack-auth/internal-ssk)"
INTERNAL_SAK="$(cat /var/lib/stack-auth/internal-sak)"
# Container-local dependencies run on localhost. Host-only development
# services (such as the OAuth mock server) are reachable via the QEMU
# user-network host alias.
@ -78,6 +96,9 @@ write_files:
# Static vars from base config and runtime (e.g. API keys, feature flags)
cat /mnt/stack-runtime/base.env
cat /mnt/stack-runtime/runtime.env
printf 'STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY=%s\n' "$INTERNAL_PCK"
printf 'STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY=%s\n' "$INTERNAL_SSK"
printf 'STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY=%s\n' "$INTERNAL_SAK"
# Computed vars — depend on port prefix or deps host
# Host-side ports (for browser URLs — browser runs on host, not in VM)
@ -108,7 +129,10 @@ write_files:
STACK_CLICKHOUSE_URL=http://${DEPS_HOST}:8123
STACK_EMAIL_MONITOR_VERIFICATION_CALLBACK_URL=http://localhost:${HP_DASHBOARD}/handler/email-verification
STACK_EMAIL_MONITOR_INBUCKET_API_URL=http://${DEPS_HOST}:9001
STACK_OAUTH_MOCK_URL=http://${HOST_SERVICES_HOST}:${P}14
STACK_OAUTH_MOCK_URL=http://localhost:${P}14
STACK_FREESTYLE_API_ENDPOINT=http://${DEPS_HOST}:8180
STACK_STRIPE_MOCK_PORT=12111
NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY=pk_test_mock_publishable_key_for_local_emulator
BACKEND_PORT=${P}02
DASHBOARD_PORT=${P}01
COMPUTED
@ -135,20 +159,54 @@ write_files:
/usr/local/bin/mount-host-fs
/usr/local/bin/render-stack-env
# Publish the internal publishable client key to the host via 9p so the
# stack-cli can authenticate its bootstrap call to
# /api/v1/internal/local-emulator/project.
set -a
source /mnt/stack-runtime/runtime.env
set +a
if [ -n "${STACK_EMULATOR_VM_DIR_HOST:-}" ] && [ -s /var/lib/stack-auth/internal-pck ]; then
install -m 0600 /var/lib/stack-auth/internal-pck \
"/host${STACK_EMULATOR_VM_DIR_HOST}/internal-pck"
fi
docker rm -f stack >/dev/null 2>&1 || true
exec docker run \
--rm \
--name stack \
--network host \
--add-host host.docker.internal:host-gateway \
--env-file /run/stack-auth/local-emulator.env \
-v stack-postgres-data:/data/postgres \
-v stack-redis-data:/data/redis \
-v stack-clickhouse-data:/data/clickhouse \
-v stack-minio-data:/data/minio \
-v stack-inbucket-data:/data/inbucket \
-v /host:/host \
stack-local-emulator
# Mirror container stdout/stderr to a host-visible log for debugging.
# The container already bind-mounts /host:/host, so we reuse that path.
# Falls back to stdout (captured by systemd-journald) when no host log is set.
if [ -n "${STACK_EMULATOR_VM_DIR_HOST:-}" ]; then
host_log="/host${STACK_EMULATOR_VM_DIR_HOST}/stack.log"
: > "$host_log" 2>/dev/null || true
exec docker run \
--rm \
--name stack \
--network host \
--add-host host.docker.internal:host-gateway \
--env-file /run/stack-auth/local-emulator.env \
-v stack-postgres-data:/data/postgres \
-v stack-redis-data:/data/redis \
-v stack-clickhouse-data:/data/clickhouse \
-v stack-minio-data:/data/minio \
-v stack-inbucket-data:/data/inbucket \
-v /host:/host \
stack-local-emulator 2>&1 | tee -a "$host_log"
else
exec docker run \
--rm \
--name stack \
--network host \
--add-host host.docker.internal:host-gateway \
--env-file /run/stack-auth/local-emulator.env \
-v stack-postgres-data:/data/postgres \
-v stack-redis-data:/data/redis \
-v stack-clickhouse-data:/data/clickhouse \
-v stack-minio-data:/data/minio \
-v stack-inbucket-data:/data/inbucket \
-v /host:/host \
stack-local-emulator
fi
- path: /usr/local/bin/wait-for-deps
permissions: '0755'
@ -231,7 +289,7 @@ write_files:
NEXT_PUBLIC_SERVER_STACK_DASHBOARD_URL=http://127.0.0.1:8101
NEXT_PUBLIC_STACK_SVIX_SERVER_URL=http://localhost:8071
NEXT_PUBLIC_STACK_PORT_PREFIX=81
STACK_CLICKHOUSE_DATABASE=analytics
STACK_CLICKHOUSE_DATABASE=default
BACKEND_PORT=8102
DASHBOARD_PORT=8101
@ -369,10 +427,23 @@ write_files:
log "Skipping smoke test: build arch is arm64 and cross-arch TCG can't reliably run the backend."
else
log "Running smoke test on slim image..."
# build.env sets NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR=true, which makes
# docker/server/entrypoint.sh require the three internal SEED keys.
# At real-VM boot those come from render-stack-env via
# /run/stack-auth/local-emulator.env, but that path doesn't run during
# the build-time smoke test. Mint throwaway hex keys for this container
# only; they must be hex because entrypoint.sh also validates that
# before the internal ApiKeySet bootstrap SQL.
SMOKE_PCK="$(openssl rand -hex 32)"
SMOKE_SSK="$(openssl rand -hex 32)"
SMOKE_SAK="$(openssl rand -hex 32)"
docker run --rm --name smoke-test \
--network host \
--env-file /etc/stack-build.env \
--env-file /etc/stack-build-computed.env \
-e STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY="$SMOKE_PCK" \
-e STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY="$SMOKE_SSK" \
-e STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY="$SMOKE_SAK" \
-e STACK_SKIP_MIGRATIONS=true \
-e STACK_SKIP_SEED_SCRIPT=true \
-e STACK_RUNTIME_WORK_DIR=/app \

View File

@ -5,8 +5,8 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=common.sh
source "$SCRIPT_DIR/common.sh"
IMAGE_DIR="$SCRIPT_DIR/images"
RUN_DIR="${EMULATOR_RUN_DIR:-$SCRIPT_DIR/run}"
IMAGE_DIR="${EMULATOR_IMAGE_DIR:-$HOME/.stack/emulator/images}"
RUN_DIR="${EMULATOR_RUN_DIR:-$HOME/.stack/emulator/run}"
VM_RAM="${EMULATOR_RAM:-4096}"
VM_CPUS="${EMULATOR_CPUS:-4}"
@ -89,6 +89,7 @@ prepare_runtime_config_iso() {
printf "STACK_EMULATOR_BACKEND_HOST_PORT=%s\n" "$EMULATOR_BACKEND_PORT"
printf "STACK_EMULATOR_MINIO_HOST_PORT=%s\n" "$EMULATOR_MINIO_PORT"
printf "STACK_EMULATOR_INBUCKET_HOST_PORT=%s\n" "$EMULATOR_INBUCKET_PORT"
printf "STACK_EMULATOR_VM_DIR_HOST=%s\n" "$VM_DIR"
} > "$cfg_dir/runtime.env"
cp "$SCRIPT_DIR/../.env.development" "$cfg_dir/base.env"
make_iso_from_dir "$cfg_iso" "STACKCFG" "$cfg_dir"
@ -201,10 +202,16 @@ build_qemu_cmd() {
local netdev="user,id=net0"
# Only expose user-facing services; internal deps stay inside the VM.
netdev+=",hostfwd=tcp::${EMULATOR_DASHBOARD_PORT}-:${PORT_PREFIX}01"
netdev+=",hostfwd=tcp::${EMULATOR_BACKEND_PORT}-:${PORT_PREFIX}02"
netdev+=",hostfwd=tcp::${EMULATOR_MINIO_PORT}-:9090"
netdev+=",hostfwd=tcp::${EMULATOR_INBUCKET_PORT}-:9001"
# Bind to 127.0.0.1 so the emulator is not reachable from the LAN.
netdev+=",hostfwd=tcp:127.0.0.1:${EMULATOR_DASHBOARD_PORT}-:${PORT_PREFIX}01"
netdev+=",hostfwd=tcp:127.0.0.1:${EMULATOR_BACKEND_PORT}-:${PORT_PREFIX}02"
netdev+=",hostfwd=tcp:127.0.0.1:${EMULATOR_MINIO_PORT}-:9090"
netdev+=",hostfwd=tcp:127.0.0.1:${EMULATOR_INBUCKET_PORT}-:9001"
# Mock OAuth server: browser redirects land on `localhost:${PORT_PREFIX}14`
# (backend sets STACK_OAUTH_MOCK_URL to that value), so we forward host:port
# ↔ VM:port on the same number. Collides with pnpm dev, but the two modes
# are mutually exclusive.
netdev+=",hostfwd=tcp:127.0.0.1:${PORT_PREFIX}14-:${PORT_PREFIX}14"
QEMU_CMD=(
"$qemu_bin"
@ -249,7 +256,7 @@ tail_vm_logs() {
}
ensure_ports_free() {
local ports=("$EMULATOR_DASHBOARD_PORT" "$EMULATOR_BACKEND_PORT" "$EMULATOR_MINIO_PORT" "$EMULATOR_INBUCKET_PORT")
local ports=("$EMULATOR_DASHBOARD_PORT" "$EMULATOR_BACKEND_PORT" "$EMULATOR_MINIO_PORT" "$EMULATOR_INBUCKET_PORT" "${PORT_PREFIX}14")
local port
for port in "${ports[@]}"; do
if lsof -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1; then

View File

@ -0,0 +1,32 @@
#!/bin/bash
# Polls backend cron endpoints in parallel background loops, matching vercel.json cron config.
# Replaces the tsx scripts used in dev mode since tsx is not in the final image.
set -e
BACKEND_URL="http://127.0.0.1:${BACKEND_PORT:-8102}"
if [ -z "${CRON_SECRET:-}" ]; then
echo "CRON_SECRET is not set; refusing to start cron loops." >&2
exit 1
fi
# Wait for the backend to be ready
until curl -fsS "${BACKEND_URL}/health" >/dev/null 2>&1; do sleep 2; done
echo "Cron jobs started."
run_loop() {
local endpoint="$1"
while true; do
curl -sf -o /dev/null --max-time 120 "${BACKEND_URL}${endpoint}" \
-H "Authorization: Bearer ${CRON_SECRET}" || true
sleep 1
done
}
run_loop "/api/latest/internal/email-queue-step" &
run_loop "/api/latest/internal/external-db-sync/sequencer" &
run_loop "/api/latest/internal/external-db-sync/poller" &
wait

View File

@ -50,7 +50,8 @@ environment=
INBUCKET_WEB_ADDR="0.0.0.0:9001",
INBUCKET_POP3_ADDR="0.0.0.0:1100",
INBUCKET_STORAGE_TYPE="file",
INBUCKET_STORAGE_PARAMS="path:/data/inbucket"
INBUCKET_STORAGE_PARAMS="path:/data/inbucket",
INBUCKET_WEB_UIDIR="/opt/inbucket/ui"
autostart=true
autorestart=true
priority=20
@ -120,6 +121,43 @@ stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; --- Stripe mock ---
[program:stripe-mock]
command=/usr/local/bin/stripe-mock -port 12111
autostart=true
autorestart=true
priority=20
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; --- Freestyle mock (JS execution for email rendering) ---
[program:freestyle-mock]
command=/usr/local/bin/node /app/freestyle-mock/server.mjs
environment=NODE_PATH="/app/freestyle-mock/node_modules",PORT="8180"
autostart=true
autorestart=true
priority=20
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; --- Mock OAuth server ---
[program:mock-oauth]
command=/usr/local/bin/node /app/mock-oauth-server/index.cjs
autostart=true
autorestart=true
priority=20
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; --- Post-startup init ---
[program:init-services]
@ -134,6 +172,19 @@ stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; --- Cron jobs (email queue, external DB sync) ---
[program:cron-jobs]
command=/run-cron-jobs.sh
autostart=true
autorestart=true
startsecs=0
priority=70
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; --- Stack Auth backend + dashboard ---
[program:stack-app]

View File

@ -11,14 +11,28 @@ fi
# ============= ENV VARS =============
export STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY=${STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY:-$(openssl rand -base64 32)}
export STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY=${STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY:-$(openssl rand -base64 32)}
export STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY=${STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY:-$(openssl rand -base64 32)}
if [ "$NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR" = "true" ]; then
for v in STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY; do
if [ -z "${!v:-}" ]; then
echo "$v must be set in local-emulator mode (injected by the QEMU VM)." >&2
exit 1
fi
done
export STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY
else
export STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY=${STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY:-$(openssl rand -base64 32)}
export STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY=${STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY:-$(openssl rand -base64 32)}
export STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY=${STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY:-$(openssl rand -base64 32)}
fi
export NEXT_PUBLIC_STACK_PROJECT_ID=internal
export NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=${STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY}
export STACK_SECRET_SERVER_KEY=${STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY}
export STACK_SUPER_SECRET_ADMIN_KEY=${STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY}
if [ -n "${STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY:-}" ]; then
export STACK_SECRET_SERVER_KEY=${STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY}
fi
if [ -n "${STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY:-}" ]; then
export STACK_SUPER_SECRET_ADMIN_KEY=${STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY}
fi
export NEXT_PUBLIC_BROWSER_STACK_DASHBOARD_URL=${NEXT_PUBLIC_STACK_DASHBOARD_URL}
export NEXT_PUBLIC_STACK_PORT_PREFIX=${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}
@ -65,6 +79,44 @@ else
cd ../..
fi
# ============= LOCAL EMULATOR: BOOTSTRAP INTERNAL API KEY SET =============
# The build-time seed ran without any keys (the VM generates random ones on
# first boot). The slim image strips apps/backend/dist so we can't re-run the
# full seed here. Instead, targeted-upsert the internal api key set with the
# VM-supplied keys:
# - pck: used by stack-cli to auth against /api/v1/internal/local-emulator/project
# - ssk/sak: required by the emulator's own dashboard (StackServerApp ctor
# throws without ssk). User-app flows don't use these — per-project
# credentials come from the /local-emulator/project route.
if [ "$NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR" = "true" ] && [ -n "${STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY:-}" ] && [ -n "${STACK_DATABASE_CONNECTION_STRING:-}" ]; then
# Validate the keys are hex-only to defuse any SQL-injection risk (the VM
# generates them via `openssl rand -hex 32`, so this is an assert, not a filter).
for varname in STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY; do
val="${!varname:-}"
if [ -z "$val" ]; then
echo "ERROR: $varname is not set; refusing to bootstrap internal api key set." >&2
exit 1
fi
if ! printf '%s' "$val" | grep -Eq '^[0-9a-fA-F]+$'; then
echo "ERROR: $varname is not hex-only; refusing to bootstrap internal api key set." >&2
exit 1
fi
done
echo "Bootstrapping internal API key set (emulator runtime)..."
psql "$STACK_DATABASE_CONNECTION_STRING" -v ON_ERROR_STOP=1 <<SQL
INSERT INTO "ApiKeySet" ("projectId", id, description, "expiresAt", "createdAt", "updatedAt", "publishableClientKey", "secretServerKey", "superSecretAdminKey")
VALUES ('internal', '3142e763-b230-44b5-8636-aa62f7489c26', 'Internal API key set', '2099-12-31T23:59:59Z', NOW(), NOW(),
'${STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY}',
'${STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY}',
'${STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY}')
ON CONFLICT ("projectId", id) DO UPDATE SET
"publishableClientKey" = EXCLUDED."publishableClientKey",
"secretServerKey" = EXCLUDED."secretServerKey",
"superSecretAdminKey" = EXCLUDED."superSecretAdminKey",
"updatedAt" = NOW();
SQL
fi
# ============= ENV VARS =============
# Create a working directory for our processed files.

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/docs-mintlify",
"version": "2.8.83",
"version": "2.8.84",
"private": true,
"scripts": {
"dev": "mint dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}04 --no-open",

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/stack-docs",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"description": "",
"main": "index.js",

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/example-cjs-test",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"scripts": {

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/convex-example",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"scripts": {

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/example-demo-app",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"description": "",
"private": true,

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/docs-examples",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"description": "",
"private": true,

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/e-commerce-demo",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"scripts": {

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/js-example",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"description": "",

View File

@ -1,7 +1,7 @@
{
"name": "@stackframe/lovable-react-18-example",
"private": true,
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"type": "module",
"scripts": {

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/example-middleware-demo",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"scripts": {

View File

@ -1,7 +1,7 @@
{
"name": "react-example",
"private": true,
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"type": "module",
"scripts": {

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/example-supabase",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"private": true,
"scripts": {

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/dashboard-ui-components",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/init-stack",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"description": "The setup wizard for Stack. https://stack-auth.com",
"main": "dist/index.mjs",

View File

@ -1,7 +1,7 @@
{
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
"name": "@stackframe/js",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"sideEffects": false,
"main": "./dist/index.js",

View File

@ -1,7 +1,7 @@
{
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
"name": "@stackframe/react",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"sideEffects": false,
"main": "./dist/index.js",

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/stack-cli",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"description": "The CLI for Stack Auth. https://stack-auth.com",
"main": "dist/index.js",
@ -10,7 +10,7 @@
},
"scripts": {
"clean": "rimraf node_modules && rimraf dist",
"build": "tsdown",
"build": "tsdown && node scripts/copy-emulator-assets.mjs",
"dev": "tsdown --watch",
"lint": "eslint --ext .tsx,.ts .",
"typecheck": "tsc --noEmit"

View File

@ -0,0 +1,27 @@
#!/usr/bin/env node
import { execFileSync } from "child_process";
import { chmodSync, cpSync, mkdirSync } from "fs";
import { dirname, join, resolve } from "path";
import { fileURLToPath } from "url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const packageRoot = resolve(__dirname, "..");
const qemuSrc = resolve(packageRoot, "../../docker/local-emulator/qemu");
const envGenScript = resolve(packageRoot, "../../docker/local-emulator/generate-env-development.mjs");
const envSrc = resolve(packageRoot, "../../docker/local-emulator/.env.development");
const distDir = join(packageRoot, "dist");
const emulatorDist = join(distDir, "emulator");
execFileSync(process.execPath, [envGenScript], { stdio: "inherit" });
mkdirSync(emulatorDist, { recursive: true });
for (const name of ["run-emulator.sh", "common.sh", "cloud-init"]) {
cpSync(join(qemuSrc, name), join(emulatorDist, name), { recursive: true });
}
chmodSync(join(emulatorDist, "run-emulator.sh"), 0o755);
cpSync(envSrc, join(distDir, ".env.development"));
console.log(`Copied emulator assets into ${emulatorDist} (+ .env.development into ${distDir}).`);

View File

@ -1,9 +1,87 @@
import { Command } from "commander";
import { execFileSync, spawn } from "child_process";
import { existsSync, mkdirSync, renameSync, unlinkSync } from "fs";
import { join, resolve } from "path";
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync } from "fs";
import { homedir } from "os";
import { dirname, join, resolve } from "path";
import { fileURLToPath } from "url";
import { CliError } from "../lib/errors.js";
const DEFAULT_EMULATOR_BACKEND_PORT = 26701;
function emulatorBackendPort(): number {
const raw = process.env.EMULATOR_BACKEND_PORT;
if (!raw) return DEFAULT_EMULATOR_BACKEND_PORT;
const parsed = Number(raw);
if (!Number.isInteger(parsed) || parsed <= 0) {
throw new CliError(`Invalid EMULATOR_BACKEND_PORT: ${raw}`);
}
return parsed;
}
function emulatorHome(): string {
return process.env.STACK_EMULATOR_HOME ?? join(homedir(), ".stack", "emulator");
}
function emulatorRunDir(): string {
return join(emulatorHome(), "run");
}
function emulatorImageDir(): string {
return join(emulatorHome(), "images");
}
function internalPckPath(): string {
return join(emulatorRunDir(), "vm", "internal-pck");
}
async function readInternalPck(timeoutMs = 60_000): Promise<string> {
const path = internalPckPath();
const deadline = Date.now() + timeoutMs;
let delay = 250;
while (Date.now() < deadline) {
if (existsSync(path)) {
const contents = readFileSync(path, "utf-8").trim();
if (contents) return contents;
}
await new Promise((r) => setTimeout(r, delay));
delay = Math.min(delay * 2, 2000);
}
throw new CliError(`Timed out waiting for emulator internal publishable client key at ${path}`);
}
type EmulatorCredentials = {
project_id: string,
publishable_client_key: string,
secret_server_key: string,
};
async function fetchEmulatorCredentials(pck: string, backendPort: number, configFile: string): Promise<EmulatorCredentials> {
const url = `http://127.0.0.1:${backendPort}/api/v1/internal/local-emulator/project`;
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Stack-Project-Id": "internal",
"X-Stack-Access-Type": "client",
"X-Stack-Publishable-Client-Key": pck,
},
body: JSON.stringify({ absolute_file_path: configFile }),
});
if (!res.ok) {
throw new CliError(`Failed to initialize local emulator project (${res.status}): ${await res.text()}`);
}
const data = await res.json() as {
project_id: string,
publishable_client_key: string,
secret_server_key: string,
};
return {
project_id: data.project_id,
publishable_client_key: data.publishable_client_key,
secret_server_key: data.secret_server_key,
};
}
function gh(args: string[]): string {
try {
return execFileSync("gh", args, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
@ -15,27 +93,63 @@ function gh(args: string[]): string {
}
}
function findQemuDir(): string {
for (const rel of ["docker/local-emulator/qemu", "../docker/local-emulator/qemu"]) {
const dir = resolve(process.cwd(), rel);
if (existsSync(join(dir, "run-emulator.sh"))) return dir;
}
throw new CliError("Could not find QEMU emulator directory. Run this from the stack-auth repo root.");
function emulatorScriptsDir(): string {
const here = dirname(fileURLToPath(import.meta.url));
const bundled = join(here, "emulator");
if (existsSync(join(bundled, "run-emulator.sh"))) return bundled;
const repo = resolve(here, "../../../docker/local-emulator/qemu");
if (existsSync(join(repo, "run-emulator.sh"))) return repo;
throw new CliError("Emulator scripts not found in CLI bundle.");
}
function emulatorSpawnEnv(extra?: Record<string, string>): NodeJS.ProcessEnv {
return {
...process.env,
EMULATOR_RUN_DIR: emulatorRunDir(),
EMULATOR_IMAGE_DIR: emulatorImageDir(),
...extra,
};
}
function runEmulator(action: string, env?: Record<string, string>): Promise<void> {
const qemuDir = findQemuDir();
return new Promise((resolve, reject) => {
const child = spawn(join(qemuDir, "run-emulator.sh"), [action], {
const scriptsDir = emulatorScriptsDir();
mkdirSync(emulatorRunDir(), { recursive: true });
mkdirSync(emulatorImageDir(), { recursive: true });
return new Promise((resolvePromise, reject) => {
const child = spawn(join(scriptsDir, "run-emulator.sh"), [action], {
stdio: "inherit",
env: { ...process.env, ...env },
cwd: qemuDir,
env: emulatorSpawnEnv(env),
cwd: scriptsDir,
});
child.on("close", (code) => code === 0 ? resolve() : reject(new CliError(`run-emulator.sh ${action} exited with code ${code}`)));
child.on("close", (code) => code === 0 ? resolvePromise() : reject(new CliError(`run-emulator.sh ${action} exited with code ${code}`)));
child.on("error", (err) => reject(new CliError(`Failed to run run-emulator.sh: ${err.message}`)));
});
}
function isEmulatorRunning(): boolean {
const scriptsDir = emulatorScriptsDir();
try {
execFileSync(join(scriptsDir, "run-emulator.sh"), ["status"], {
stdio: "pipe",
cwd: scriptsDir,
env: emulatorSpawnEnv(),
});
return true;
} catch {
return false;
}
}
async function startEmulator(arch: "arm64" | "amd64"): Promise<void> {
mkdirSync(emulatorImageDir(), { recursive: true });
const img = join(emulatorImageDir(), `stack-emulator-${arch}.qcow2`);
if (!existsSync(img)) {
console.log("No emulator image found. Pulling latest...");
pullRelease(arch);
}
await runEmulator("start", { EMULATOR_ARCH: arch });
}
function resolveArch(raw?: string): "arm64" | "amd64" {
const arch = raw ?? (process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "amd64" : null);
if (arch === "arm64" || arch === "amd64") return arch;
@ -47,7 +161,7 @@ function pullRelease(arch: "arm64" | "amd64", opts: { repo?: string; branch?: st
const branch = opts.branch ?? "dev";
const tag = opts.tag ?? `emulator-${branch}-latest`;
const asset = `stack-emulator-${arch}.qcow2`;
const imageDir = join(findQemuDir(), "images");
const imageDir = emulatorImageDir();
mkdirSync(imageDir, { recursive: true });
const dest = join(imageDir, asset);
const tmpDest = `${dest}.download`;
@ -89,7 +203,7 @@ export function registerEmulatorCommand(program: Command) {
runId = String(runs[0].databaseId);
}
const imageDir = join(findQemuDir(), "images");
const imageDir = emulatorImageDir();
mkdirSync(imageDir, { recursive: true });
const dest = join(imageDir, `stack-emulator-${arch}.qcow2`);
if (existsSync(dest)) unlinkSync(dest);
@ -110,14 +224,91 @@ export function registerEmulatorCommand(program: Command) {
.command("start")
.description("Start the emulator in the background (auto-pulls the latest image if none exists)")
.option("--arch <arch>", "Target architecture (default: current system arch). Non-native uses software emulation and is significantly slower.")
.action(async (opts) => {
.option("--config-file <path>", "Path to a config file; when set, credentials for this project are printed to stdout as JSON")
.action(async (opts: { arch?: string, configFile?: string }) => {
const arch = resolveArch(opts.arch);
const img = join(findQemuDir(), "images", `stack-emulator-${arch}.qcow2`);
if (!existsSync(img)) {
console.log("No emulator image found. Pulling latest...");
pullRelease(arch);
let resolvedConfigFile: string | undefined;
if (opts.configFile) {
resolvedConfigFile = resolve(opts.configFile);
if (!existsSync(resolvedConfigFile)) {
throw new CliError(`Config file not found: ${resolvedConfigFile}`);
}
}
await runEmulator("start", { EMULATOR_ARCH: arch });
if (isEmulatorRunning()) {
console.warn("Emulator already running, reusing existing instance.");
} else {
await startEmulator(arch);
}
if (resolvedConfigFile) {
const pck = await readInternalPck();
const creds = await fetchEmulatorCredentials(pck, emulatorBackendPort(), resolvedConfigFile);
console.log(JSON.stringify(creds, null, 2));
}
});
emulator
.command("run")
.description("Start the emulator, run a command, and stop the emulator when the command exits")
.argument("<cmd>", "Command to run (e.g. \"npm run dev\")")
.option("--arch <arch>", "Target architecture")
.option("--config-file <path>", "Path to a config file; fetches credentials and injects STACK_PROJECT_ID / STACK_PUBLISHABLE_CLIENT_KEY / STACK_SECRET_SERVER_KEY into the child")
.action(async (cmd: string, opts: { arch?: string, configFile?: string }) => {
const arch = resolveArch(opts.arch);
let resolvedConfigFile: string | undefined;
if (opts.configFile) {
resolvedConfigFile = resolve(opts.configFile);
if (!existsSync(resolvedConfigFile)) {
throw new CliError(`Config file not found: ${resolvedConfigFile}`);
}
}
const alreadyRunning = isEmulatorRunning();
if (alreadyRunning) {
console.log("Emulator already running, reusing existing instance.");
} else {
await startEmulator(arch);
}
const childEnv: Record<string, string> = { ...process.env as Record<string, string> };
if (resolvedConfigFile) {
const pck = await readInternalPck();
const backendPort = emulatorBackendPort();
const creds = await fetchEmulatorCredentials(pck, backendPort, resolvedConfigFile);
const apiUrl = `http://127.0.0.1:${backendPort}`;
childEnv.STACK_PROJECT_ID = creds.project_id;
childEnv.NEXT_PUBLIC_STACK_PROJECT_ID = creds.project_id;
childEnv.STACK_PUBLISHABLE_CLIENT_KEY = creds.publishable_client_key;
childEnv.NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY = creds.publishable_client_key;
childEnv.STACK_SECRET_SERVER_KEY = creds.secret_server_key;
childEnv.STACK_API_URL = apiUrl;
childEnv.NEXT_PUBLIC_STACK_API_URL = apiUrl;
}
const child = spawn(cmd, { shell: true, stdio: "inherit", env: childEnv });
const forward = (signal: NodeJS.Signals) => () => child.kill(signal);
const onSigint = forward("SIGINT");
const onSigterm = forward("SIGTERM");
process.on("SIGINT", onSigint);
process.on("SIGTERM", onSigterm);
child.on("close", (code) => {
process.off("SIGINT", onSigint);
process.off("SIGTERM", onSigterm);
const exitCode = code ?? 1;
if (alreadyRunning) {
process.exit(exitCode);
} else {
console.log("\nStopping emulator...");
runEmulator("stop")
.catch(() => { /* best-effort stop */ })
.finally(() => process.exit(exitCode));
}
});
});
emulator.command("stop").description("Stop the emulator (data preserved; use 'reset' to clear)").action(() => runEmulator("stop"));

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/stack-sc",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"exports": {
"./force-react-server": {

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/stack-shared",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"scripts": {
"build": "rimraf dist && tsdown",

View File

@ -465,6 +465,7 @@ export class StackClientInterface {
session: InternalSession | null,
requestType: "client" | "server" | "admin" = "client",
apiUrlOverride?: string,
retryOptions?: { maxAttempts?: number, skipDiagnostics?: boolean },
) {
session ??= this.createSession({
refreshToken: null,
@ -472,19 +473,20 @@ export class StackClientInterface {
if (apiUrlOverride) {
return await this._networkRetry(
() => this.sendClientRequestInner(path, requestOptions, session!, requestType, apiUrlOverride),
session,
requestType,
);
}
return await this._withFallback(async (apiUrl, retryOptions) => {
return await this._networkRetry(
() => this.sendClientRequestInner(path, requestOptions, session!, requestType, apiUrl),
() => this.sendClientRequestInner(path, requestOptions, session!, requestType, apiUrlOverride, retryOptions),
session,
requestType,
retryOptions,
);
}
return await this._withFallback(async (apiUrl, fallbackRetryOptions) => {
return await this._networkRetry(
() => this.sendClientRequestInner(path, requestOptions, session!, requestType, apiUrl, retryOptions),
session,
requestType,
{ ...fallbackRetryOptions, ...retryOptions },
);
});
}
@ -513,6 +515,7 @@ export class StackClientInterface {
session,
"client",
this.getAnalyticsApiUrl(),
{ maxAttempts: 1, skipDiagnostics: true },
);
return Result.ok(response);
} catch (e) {
@ -537,6 +540,7 @@ export class StackClientInterface {
session,
"client",
this.getAnalyticsApiUrl(),
{ maxAttempts: 1, skipDiagnostics: true },
);
return Result.ok(response);
} catch (e) {
@ -576,6 +580,7 @@ export class StackClientInterface {
session: InternalSession,
requestType: "client" | "server" | "admin",
apiUrlOverride?: string,
innerOptions?: { skipDiagnostics?: boolean },
): Promise<Result<Response & {
usedTokens: {
accessToken: AccessToken,
@ -682,6 +687,8 @@ export class StackClientInterface {
// Likely to be a network error. Retry if the request is idempotent, throw network error otherwise.
if (HTTP_METHODS[(params.method ?? "GET") as HttpMethod].idempotent) {
return Result.error(e);
} else if (innerOptions?.skipDiagnostics) {
throw e;
} else {
throw await this._createNetworkError(e, session, requestType);
}

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/stack-ui",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@ -1,7 +1,7 @@
{
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
"name": "@stackframe/stack",
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"sideEffects": false,
"main": "./dist/index.js",

View File

@ -11,7 +11,7 @@
"//": "NEXT_LINE_PLATFORM template",
"private": true,
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"sideEffects": false,
"main": "./dist/index.js",

View File

@ -2,7 +2,7 @@
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
"name": "@stackframe/template",
"private": true,
"version": "2.8.83",
"version": "2.8.84",
"repository": "https://github.com/stack-auth/stack-auth",
"sideEffects": false,
"main": "./dist/index.js",

View File

@ -126,6 +126,7 @@ export class _StackAdminAppImplIncomplete<HasTokenStore extends boolean, Project
constructor(options: StackAdminAppConstructorOptions<HasTokenStore, ProjectId>, extraOptions?: { uniqueIdentifier?: string, checkString?: string, interface?: StackAdminInterface }) {
const resolvedOptions = resolveConstructorOptions(options);
const publishableClientKey = resolvedOptions.publishableClientKey ?? getDefaultPublishableClientKey();
super(resolvedOptions, {

View File

@ -188,7 +188,7 @@ importers:
version: 1.2.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@sentry/nextjs':
specifier: ^10.45.0
version: 10.45.0(@opentelemetry/context-async-hooks@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.2.2(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2))
version: 10.45.0(@opentelemetry/context-async-hooks@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.7(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2))
'@simplewebauthn/server':
specifier: ^13.3.0
version: 13.3.0
@ -244,8 +244,8 @@ importers:
specifier: ^1.0.6
version: 1.0.6
next:
specifier: 16.2.2
version: 16.2.2(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
specifier: 16.1.7
version: 16.1.7(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
nodemailer:
specifier: ^6.9.10
version: 6.9.13
@ -477,7 +477,7 @@ importers:
version: 2.0.2(react@19.2.3)
'@sentry/nextjs':
specifier: ^10.11.0
version: 10.11.0(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2))
version: 10.11.0(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2))
'@stackframe/dashboard-ui-components':
specifier: workspace:*
version: link:../../packages/dashboard-ui-components
@ -507,10 +507,10 @@ importers:
version: 3.13.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@vercel/analytics':
specifier: ^1.2.2
version: 1.3.1(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)
version: 1.3.1(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)
'@vercel/speed-insights':
specifier: ^1.0.12
version: 1.0.12(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)
version: 1.0.12(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)
ai:
specifier: ^6.0.0
version: 6.0.81(zod@4.1.12)
@ -540,7 +540,7 @@ importers:
version: 1.4.0
geist:
specifier: ^1
version: 1.3.0(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
version: 1.3.0(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
input-otp:
specifier: ^1.4.1
version: 1.4.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@ -551,11 +551,11 @@ importers:
specifier: ^4.17.21
version: 4.17.21
next:
specifier: 16.2.2
version: 16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
specifier: 16.1.7
version: 16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next-themes:
specifier: ^0.2.1
version: 0.2.1(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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)
version: 0.2.1(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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)
posthog-js:
specifier: ^1.336.1
version: 1.336.1
@ -5654,6 +5654,9 @@ packages:
'@next/env@16.1.5':
resolution: {integrity: sha512-CRSCPJiSZoi4Pn69RYBDI9R7YK2g59vLexPQFXY0eyw+ILevIenCywzg+DqmlBik9zszEnw2HLFOUlLAcJbL7g==}
'@next/env@16.1.7':
resolution: {integrity: sha512-rJJbIdJB/RQr2F1nylZr/PJzamvNNhfr3brdKP6s/GW850jbtR70QlSfFselvIBbcPUOlQwBakexjFzqLzF6pg==}
'@next/env@16.2.2':
resolution: {integrity: sha512-LqSGz5+xGk9EL/iBDr2yo/CgNQV6cFsNhRR2xhSXYh7B/hb4nePCxlmDvGEKG30NMHDFf0raqSyOZiQrO7BkHQ==}
@ -5687,6 +5690,12 @@ packages:
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-arm64@16.1.7':
resolution: {integrity: sha512-b2wWIE8sABdyafc4IM8r5Y/dS6kD80JRtOGrUiKTsACFQfWWgUQ2NwoUX1yjFMXVsAwcQeNpnucF2ZrujsBBPg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-arm64@16.2.2':
resolution: {integrity: sha512-B92G3ulrwmkDSEJEp9+XzGLex5wC1knrmCSIylyVeiAtCIfvEJYiN3v5kXPlYt5R4RFlsfO/v++aKV63Acrugg==}
engines: {node: '>= 10'}
@ -5711,6 +5720,12 @@ packages:
cpu: [x64]
os: [darwin]
'@next/swc-darwin-x64@16.1.7':
resolution: {integrity: sha512-zcnVaaZulS1WL0Ss38R5Q6D2gz7MtBu8GZLPfK+73D/hp4GFMrC2sudLky1QibfV7h6RJBJs/gOFvYP0X7UVlQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-darwin-x64@16.2.2':
resolution: {integrity: sha512-7ZwSgNKJNQiwW0CKhNm9B1WS2L1Olc4B2XY0hPYCAL3epFnugMhuw5TMWzMilQ3QCZcCHoYm9NGWTHbr5REFxw==}
engines: {node: '>= 10'}
@ -5738,6 +5753,13 @@ packages:
os: [linux]
libc: [glibc]
'@next/swc-linux-arm64-gnu@16.1.7':
resolution: {integrity: sha512-2ant89Lux/Q3VyC8vNVg7uBaFVP9SwoK2jJOOR0L8TQnX8CAYnh4uctAScy2Hwj2dgjVHqHLORQZJ2wH6VxhSQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@next/swc-linux-arm64-gnu@16.2.2':
resolution: {integrity: sha512-c3m8kBHMziMgo2fICOP/cd/5YlrxDU5YYjAJeQLyFsCqVF8xjOTH/QYG4a2u48CvvZZSj1eHQfBCbyh7kBr30Q==}
engines: {node: '>= 10'}
@ -5766,6 +5788,13 @@ packages:
os: [linux]
libc: [musl]
'@next/swc-linux-arm64-musl@16.1.7':
resolution: {integrity: sha512-uufcze7LYv0FQg9GnNeZ3/whYfo+1Q3HnQpm16o6Uyi0OVzLlk2ZWoY7j07KADZFY8qwDbsmFnMQP3p3+Ftprw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@next/swc-linux-arm64-musl@16.2.2':
resolution: {integrity: sha512-VKLuscm0P/mIfzt+SDdn2+8TNNJ7f0qfEkA+az7OqQbjzKdBxAHs0UvuiVoCtbwX+dqMEL9U54b5wQ/aN3dHeg==}
engines: {node: '>= 10'}
@ -5794,6 +5823,13 @@ packages:
os: [linux]
libc: [glibc]
'@next/swc-linux-x64-gnu@16.1.7':
resolution: {integrity: sha512-KWVf2gxYvHtvuT+c4MBOGxuse5TD7DsMFYSxVxRBnOzok/xryNeQSjXgxSv9QpIVlaGzEn/pIuI6Koosx8CGWA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@next/swc-linux-x64-gnu@16.2.2':
resolution: {integrity: sha512-kU3OPHJq6sBUjOk7wc5zJ7/lipn8yGldMoAv4z67j6ov6Xo/JvzA7L7LCsyzzsXmgLEhk3Qkpwqaq/1+XpNR3g==}
engines: {node: '>= 10'}
@ -5822,6 +5858,13 @@ packages:
os: [linux]
libc: [musl]
'@next/swc-linux-x64-musl@16.1.7':
resolution: {integrity: sha512-HguhaGwsGr1YAGs68uRKc4aGWxLET+NevJskOcCAwXbwj0fYX0RgZW2gsOCzr9S11CSQPIkxmoSbuVaBp4Z3dA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@next/swc-linux-x64-musl@16.2.2':
resolution: {integrity: sha512-CKXRILyErMtUftp+coGcZ38ZwE/Aqq45VMCcRLr2I4OXKrgxIBDXHnBgeX/UMil0S09i2JXaDL3Q+TN8D/cKmg==}
engines: {node: '>= 10'}
@ -5847,6 +5890,12 @@ packages:
cpu: [arm64]
os: [win32]
'@next/swc-win32-arm64-msvc@16.1.7':
resolution: {integrity: sha512-S0n3KrDJokKTeFyM/vGGGR8+pCmXYrjNTk2ZozOL1C/JFdfUIL9O1ATaJOl5r2POe56iRChbsszrjMAdWSv7kQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-arm64-msvc@16.2.2':
resolution: {integrity: sha512-sS/jSk5VUoShUqINJFvNjVT7JfR5ORYj/+/ZpOYbbIohv/lQfduWnGAycq2wlknbOql2xOR0DoV0s6Xfcy49+g==}
engines: {node: '>= 10'}
@ -5877,6 +5926,12 @@ packages:
cpu: [x64]
os: [win32]
'@next/swc-win32-x64-msvc@16.1.7':
resolution: {integrity: sha512-mwgtg8CNZGYm06LeEd+bNnOUfwOyNem/rOiP14Lsz+AnUY92Zq/LXwtebtUiaeVkhbroRCQ0c8GlR4UT1U+0yg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@next/swc-win32-x64-msvc@16.2.2':
resolution: {integrity: sha512-aHaKceJgdySReT7qeck5oShucxWRiiEuwCGK8HHALe6yZga8uyFpLkPgaRw3kkF04U7ROogL/suYCNt/+CuXGA==}
engines: {node: '>= 10'}
@ -15656,6 +15711,27 @@ packages:
sass:
optional: true
next@16.1.7:
resolution: {integrity: sha512-WM0L7WrSvKwoLegLYr6V+mz+RIofqQgVAfHhMp9a88ms0cFX8iX9ew+snpWlSBwpkURJOUdvCEt3uLl3NNzvWg==}
engines: {node: '>=20.9.0'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
'@playwright/test': ^1.51.1
babel-plugin-react-compiler: '*'
react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
'@playwright/test':
optional: true
babel-plugin-react-compiler:
optional: true
sass:
optional: true
next@16.2.2:
resolution: {integrity: sha512-i6AJdyVa4oQjyvX/6GeER8dpY/xlIV+4NMv/svykcLtURJSy/WzDnnUk/TM4d0uewFHK7xSQz4TbIwPgjky+3A==}
engines: {node: '>=20.9.0'}
@ -17130,11 +17206,6 @@ packages:
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
resolve@1.22.10:
resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
engines: {node: '>= 0.4'}
hasBin: true
resolve@1.22.11:
resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
engines: {node: '>= 0.4'}
@ -18204,6 +18275,12 @@ packages:
peerDependencies:
typescript: '>=4.2.0'
ts-api-utils@2.1.0:
resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
engines: {node: '>=18.12'}
peerDependencies:
typescript: '>=4.8.4'
ts-api-utils@2.4.0:
resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==}
engines: {node: '>=18.12'}
@ -20753,7 +20830,7 @@ snapshots:
dependencies:
'@babel/compat-data': 7.26.2
'@babel/helper-validator-option': 7.25.9
browserslist: 4.24.4
browserslist: 4.27.0
lru-cache: 5.1.1
semver: 6.3.1
@ -22065,11 +22142,6 @@ snapshots:
eslint: 8.57.1
eslint-visitor-keys: 3.4.3
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.1(jiti@2.6.1))':
dependencies:
eslint: 9.39.1(jiti@2.6.1)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.1': {}
'@eslint-community/regexpp@4.12.2': {}
@ -23451,6 +23523,8 @@ snapshots:
'@next/env@16.1.5': {}
'@next/env@16.1.7': {}
'@next/env@16.2.2': {}
'@next/eslint-plugin-next@14.2.17':
@ -23478,6 +23552,9 @@ snapshots:
'@next/swc-darwin-arm64@16.1.5':
optional: true
'@next/swc-darwin-arm64@16.1.7':
optional: true
'@next/swc-darwin-arm64@16.2.2':
optional: true
@ -23490,6 +23567,9 @@ snapshots:
'@next/swc-darwin-x64@16.1.5':
optional: true
'@next/swc-darwin-x64@16.1.7':
optional: true
'@next/swc-darwin-x64@16.2.2':
optional: true
@ -23502,6 +23582,9 @@ snapshots:
'@next/swc-linux-arm64-gnu@16.1.5':
optional: true
'@next/swc-linux-arm64-gnu@16.1.7':
optional: true
'@next/swc-linux-arm64-gnu@16.2.2':
optional: true
@ -23514,6 +23597,9 @@ snapshots:
'@next/swc-linux-arm64-musl@16.1.5':
optional: true
'@next/swc-linux-arm64-musl@16.1.7':
optional: true
'@next/swc-linux-arm64-musl@16.2.2':
optional: true
@ -23526,6 +23612,9 @@ snapshots:
'@next/swc-linux-x64-gnu@16.1.5':
optional: true
'@next/swc-linux-x64-gnu@16.1.7':
optional: true
'@next/swc-linux-x64-gnu@16.2.2':
optional: true
@ -23538,6 +23627,9 @@ snapshots:
'@next/swc-linux-x64-musl@16.1.5':
optional: true
'@next/swc-linux-x64-musl@16.1.7':
optional: true
'@next/swc-linux-x64-musl@16.2.2':
optional: true
@ -23550,6 +23642,9 @@ snapshots:
'@next/swc-win32-arm64-msvc@16.1.5':
optional: true
'@next/swc-win32-arm64-msvc@16.1.7':
optional: true
'@next/swc-win32-arm64-msvc@16.2.2':
optional: true
@ -23565,6 +23660,9 @@ snapshots:
'@next/swc-win32-x64-msvc@16.1.5':
optional: true
'@next/swc-win32-x64-msvc@16.1.7':
optional: true
'@next/swc-win32-x64-msvc@16.2.2':
optional: true
@ -28172,6 +28270,33 @@ snapshots:
'@sentry/core@10.45.0': {}
'@sentry/nextjs@10.11.0(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2))':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/semantic-conventions': 1.37.0
'@rollup/plugin-commonjs': 28.0.1(rollup@4.50.1)
'@sentry-internal/browser-utils': 10.11.0
'@sentry/bundler-plugin-core': 4.3.0(encoding@0.1.13)
'@sentry/core': 10.11.0
'@sentry/node': 10.11.0
'@sentry/opentelemetry': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)
'@sentry/react': 10.11.0(react@19.2.3)
'@sentry/vercel-edge': 10.11.0
'@sentry/webpack-plugin': 4.3.0(encoding@0.1.13)(webpack@5.92.0(esbuild@0.24.2))
chalk: 3.0.0
next: 16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
resolve: 1.22.8
rollup: 4.50.1
stacktrace-parser: 0.1.11
transitivePeerDependencies:
- '@opentelemetry/context-async-hooks'
- '@opentelemetry/core'
- '@opentelemetry/sdk-trace-base'
- encoding
- react
- supports-color
- webpack
'@sentry/nextjs@10.11.0(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(webpack@5.92.0(esbuild@0.24.2))':
dependencies:
'@opentelemetry/api': 1.9.0
@ -28199,34 +28324,7 @@ snapshots:
- supports-color
- webpack
'@sentry/nextjs@10.11.0(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2))':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/semantic-conventions': 1.37.0
'@rollup/plugin-commonjs': 28.0.1(rollup@4.50.1)
'@sentry-internal/browser-utils': 10.11.0
'@sentry/bundler-plugin-core': 4.3.0(encoding@0.1.13)
'@sentry/core': 10.11.0
'@sentry/node': 10.11.0
'@sentry/opentelemetry': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)
'@sentry/react': 10.11.0(react@19.2.3)
'@sentry/vercel-edge': 10.11.0
'@sentry/webpack-plugin': 4.3.0(encoding@0.1.13)(webpack@5.92.0(esbuild@0.24.2))
chalk: 3.0.0
next: 16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
resolve: 1.22.8
rollup: 4.50.1
stacktrace-parser: 0.1.11
transitivePeerDependencies:
- '@opentelemetry/context-async-hooks'
- '@opentelemetry/core'
- '@opentelemetry/sdk-trace-base'
- encoding
- react
- supports-color
- webpack
'@sentry/nextjs@10.45.0(@opentelemetry/context-async-hooks@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.2.2(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2))':
'@sentry/nextjs@10.45.0(@opentelemetry/context-async-hooks@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.7(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2))':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/semantic-conventions': 1.40.0
@ -28239,7 +28337,7 @@ snapshots:
'@sentry/react': 10.45.0(react@19.2.3)
'@sentry/vercel-edge': 10.45.0
'@sentry/webpack-plugin': 5.1.1(encoding@0.1.13)(webpack@5.92.0(esbuild@0.24.2))
next: 16.2.2(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next: 16.1.7(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
rollup: 4.57.1
stacktrace-parser: 0.1.11
transitivePeerDependencies:
@ -30267,7 +30365,7 @@ snapshots:
'@typescript-eslint/eslint-plugin@8.46.3(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.46.3
'@typescript-eslint/type-utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
@ -30277,7 +30375,7 @@ snapshots:
graphemer: 1.4.0
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.4.0(typescript@5.9.3)
ts-api-utils: 2.1.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@ -30337,8 +30435,8 @@ snapshots:
'@typescript-eslint/project-service@8.46.3(typescript@5.9.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3)
'@typescript-eslint/types': 8.56.1
'@typescript-eslint/tsconfig-utils': 8.46.3(typescript@5.9.3)
'@typescript-eslint/types': 8.46.3
debug: 4.4.3
typescript: 5.9.3
transitivePeerDependencies:
@ -30383,7 +30481,7 @@ snapshots:
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
debug: 4.4.3
eslint: 9.39.1(jiti@2.6.1)
ts-api-utils: 2.4.0(typescript@5.9.3)
ts-api-utils: 2.1.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@ -30432,7 +30530,7 @@ snapshots:
is-glob: 4.0.3
minimatch: 9.0.5
semver: 7.7.3
ts-api-utils: 2.4.0(typescript@5.9.3)
ts-api-utils: 2.1.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@ -30454,7 +30552,7 @@ snapshots:
'@typescript-eslint/utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.1(jiti@2.6.1))
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
'@typescript-eslint/scope-manager': 8.46.3
'@typescript-eslint/types': 8.46.3
'@typescript-eslint/typescript-estree': 8.46.3(typescript@5.9.3)
@ -30504,11 +30602,11 @@ snapshots:
jose: 5.6.3
neverthrow: 7.2.0
'@vercel/analytics@1.3.1(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)':
'@vercel/analytics@1.3.1(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)':
dependencies:
server-only: 0.0.1
optionalDependencies:
next: 16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next: 16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
'@vercel/functions@2.0.0(@aws-sdk/credential-provider-web-identity@3.972.27)':
@ -30546,9 +30644,9 @@ snapshots:
xdg-app-paths: 5.1.0
zod: 3.24.4
'@vercel/speed-insights@1.0.12(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)':
'@vercel/speed-insights@1.0.12(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)':
optionalDependencies:
next: 16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next: 16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
'@vitejs/plugin-react@4.3.3(vite@7.3.1(@types/node@20.17.6)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.19.3)(yaml@2.6.0))':
@ -31176,7 +31274,7 @@ snapshots:
dependencies:
'@babel/runtime': 7.28.4
cosmiconfig: 7.1.0
resolve: 1.22.10
resolve: 1.22.11
bail@2.0.2: {}
@ -33227,7 +33325,7 @@ snapshots:
eslint: 8.57.1
eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1)
fast-glob: 3.3.2
fast-glob: 3.3.3
get-tsconfig: 4.8.1
is-core-module: 2.15.1
is-glob: 4.0.3
@ -33875,7 +33973,7 @@ snapshots:
router: 2.2.0
send: 1.2.0
serve-static: 2.2.0
statuses: 2.0.2
statuses: 2.0.1
type-is: 2.0.1
vary: 1.1.2
transitivePeerDependencies:
@ -33933,7 +34031,7 @@ snapshots:
'@nodelib/fs.walk': 1.2.8
glob-parent: 5.1.2
merge2: 1.4.1
micromatch: 4.0.7
micromatch: 4.0.8
fast-glob@3.3.3:
dependencies:
@ -34051,7 +34149,7 @@ snapshots:
escape-html: 1.0.3
on-finished: 2.4.1
parseurl: 1.3.3
statuses: 2.0.2
statuses: 2.0.1
transitivePeerDependencies:
- supports-color
@ -34442,9 +34540,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
geist@1.3.0(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)):
geist@1.3.0(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)):
dependencies:
next: 16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next: 16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
generate-function@2.3.1:
dependencies:
@ -34646,7 +34744,7 @@ snapshots:
dependencies:
array-union: 2.1.0
dir-glob: 3.0.1
fast-glob: 3.3.2
fast-glob: 3.3.3
ignore: 5.3.2
merge2: 1.4.1
slash: 3.0.0
@ -36913,9 +37011,9 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
next-themes@0.2.1(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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):
next-themes@0.2.1(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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):
dependencies:
next: 16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next: 16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.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)
@ -37083,6 +37181,56 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
next@16.1.7(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@next/env': 16.1.7
'@swc/helpers': 0.5.15
baseline-browser-mapping: 2.10.16
caniuse-lite: 1.0.30001751
postcss: 8.4.31
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
styled-jsx: 5.1.6(@babel/core@7.26.0)(react@19.2.3)
optionalDependencies:
'@next/swc-darwin-arm64': 16.1.7
'@next/swc-darwin-x64': 16.1.7
'@next/swc-linux-arm64-gnu': 16.1.7
'@next/swc-linux-arm64-musl': 16.1.7
'@next/swc-linux-x64-gnu': 16.1.7
'@next/swc-linux-x64-musl': 16.1.7
'@next/swc-win32-arm64-msvc': 16.1.7
'@next/swc-win32-x64-msvc': 16.1.7
'@opentelemetry/api': 1.9.0
sharp: 0.34.5
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@next/env': 16.1.7
'@swc/helpers': 0.5.15
baseline-browser-mapping: 2.10.16
caniuse-lite: 1.0.30001751
postcss: 8.4.31
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.3)
optionalDependencies:
'@next/swc-darwin-arm64': 16.1.7
'@next/swc-darwin-x64': 16.1.7
'@next/swc-linux-arm64-gnu': 16.1.7
'@next/swc-linux-arm64-musl': 16.1.7
'@next/swc-linux-x64-gnu': 16.1.7
'@next/swc-linux-x64-musl': 16.1.7
'@next/swc-win32-arm64-msvc': 16.1.7
'@next/swc-win32-x64-msvc': 16.1.7
'@opentelemetry/api': 1.9.0
sharp: 0.34.5
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
next@16.2.2(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
dependencies:
'@next/env': 16.2.2
@ -37133,31 +37281,6 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
next@16.2.2(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@next/env': 16.2.2
'@swc/helpers': 0.5.15
baseline-browser-mapping: 2.10.16
caniuse-lite: 1.0.30001751
postcss: 8.4.31
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
styled-jsx: 5.1.6(@babel/core@7.26.0)(react@19.2.3)
optionalDependencies:
'@next/swc-darwin-arm64': 16.2.2
'@next/swc-darwin-x64': 16.2.2
'@next/swc-linux-arm64-gnu': 16.2.2
'@next/swc-linux-arm64-musl': 16.2.2
'@next/swc-linux-x64-gnu': 16.2.2
'@next/swc-linux-x64-musl': 16.2.2
'@next/swc-win32-arm64-msvc': 16.2.2
'@next/swc-win32-x64-msvc': 16.2.2
'@opentelemetry/api': 1.9.0
sharp: 0.34.5
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
dependencies:
'@next/env': 16.2.2
@ -37183,31 +37306,6 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@next/env': 16.2.2
'@swc/helpers': 0.5.15
baseline-browser-mapping: 2.10.16
caniuse-lite: 1.0.30001751
postcss: 8.4.31
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.3)
optionalDependencies:
'@next/swc-darwin-arm64': 16.2.2
'@next/swc-darwin-x64': 16.2.2
'@next/swc-linux-arm64-gnu': 16.2.2
'@next/swc-linux-arm64-musl': 16.2.2
'@next/swc-linux-x64-gnu': 16.2.2
'@next/swc-linux-x64-musl': 16.2.2
'@next/swc-win32-arm64-msvc': 16.2.2
'@next/swc-win32-x64-msvc': 16.2.2
'@opentelemetry/api': 1.9.0
sharp: 0.34.5
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
nf3@0.1.12: {}
nice-try@1.0.5: {}
@ -37329,7 +37427,7 @@ snapshots:
normalize-package-data@2.5.0:
dependencies:
hosted-git-info: 2.8.9
resolve: 1.22.10
resolve: 1.22.11
semver: 5.7.2
validate-npm-package-license: 3.0.4
@ -37882,7 +37980,7 @@ snapshots:
postcss: 8.5.6
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.8
resolve: 1.22.11
postcss-js@4.0.1(postcss@8.5.6):
dependencies:
@ -39071,12 +39169,6 @@ snapshots:
resolve-pkg-maps@1.0.0: {}
resolve@1.22.10:
dependencies:
is-core-module: 2.16.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
resolve@1.22.11:
dependencies:
is-core-module: 2.16.1
@ -39503,7 +39595,7 @@ snapshots:
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.2
statuses: 2.0.1
transitivePeerDependencies:
- supports-color
@ -40638,6 +40730,10 @@ snapshots:
dependencies:
typescript: 5.9.3
ts-api-utils@2.1.0(typescript@5.9.3):
dependencies:
typescript: 5.9.3
ts-api-utils@2.4.0(typescript@5.9.3):
dependencies:
typescript: 5.9.3

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/swift-sdk",
"version": "2.8.83",
"version": "2.8.84",
"private": true,
"description": "Stack Auth Swift SDK",
"scripts": {

View File

@ -1,6 +1,6 @@
{
"name": "@stackframe/sdk-spec",
"version": "2.8.83",
"version": "2.8.84",
"private": true,
"description": "Stack Auth SDK specification files",
"scripts": {}