From 8820b39b37d2bb8f47179ca948bdc94b9e16c416 Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Tue, 23 Jun 2026 11:22:44 -0700 Subject: [PATCH] Stop using app.urls everywhere --- .../(outside-dashboard)/projects/actions.ts | 3 ++- .../[projectId]/teams/[teamId]/page-client.tsx | 4 ++-- packages/shared/src/config-authoring.ts | 4 +++- .../src/components-page/hexclave-handler-client.tsx | 11 ++++++----- packages/template/src/dev-tool/dev-tool-core.ts | 2 +- .../apps/implementations/client-app-impl.ts | 3 ++- .../lib/hexclave-app/apps/interfaces/client-app.ts | 1 + 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/actions.ts b/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/actions.ts index e1af2bd6d..807ce9271 100644 --- a/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/actions.ts +++ b/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/actions.ts @@ -1,5 +1,6 @@ "use server"; import { isRemoteDevelopmentEnvironmentEnabled } from "@/lib/remote-development-environment/env"; +import { hexclaveAppInternalsSymbol } from "@hexclave/next"; async function getServerApp() { if (isRemoteDevelopmentEnvironmentEnabled()) { @@ -40,7 +41,7 @@ export async function listInvitations(teamId: string) { export async function inviteUser(teamId: string, email: string, origin: string) { const hexclaveServerApp = await getServerApp(); - const callbackUrl = new URL(hexclaveServerApp.urls.teamInvitation, origin).toString(); + const callbackUrl = new URL(hexclaveServerApp[hexclaveAppInternalsSymbol].getUrls().teamInvitation, origin).toString(); const user = await hexclaveServerApp.getUser(); const team = await user?.getTeam(teamId); if (!team) { diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/teams/[teamId]/page-client.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/teams/[teamId]/page-client.tsx index 3bbfb31fa..f2d75c857 100644 --- a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/teams/[teamId]/page-client.tsx +++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/teams/[teamId]/page-client.tsx @@ -12,7 +12,7 @@ import { ALL_APPS_FRONTEND } from "@/lib/apps-frontend"; import { isAppEnabled } from "@/lib/apps-utils"; import { yupResolver } from '@hookform/resolvers/yup'; import { DatabaseIcon, PlusIcon } from "@phosphor-icons/react"; -import { ServerTeam } from '@hexclave/next'; +import { ServerTeam, hexclaveAppInternalsSymbol } from '@hexclave/next'; import { AppId } from "@hexclave/shared/dist/apps/apps-config"; import { strictEmailSchema, yupObject } from '@hexclave/shared/dist/schema-fields'; import { HexclaveAssertionError, throwErr } from '@hexclave/shared/dist/utils/errors'; @@ -60,7 +60,7 @@ export function AddUserDialog(props: { } await props.team.inviteUser({ email: values.email, - callbackUrl: new URL(adminApp.urls.teamInvitation, domain).toString(), + callbackUrl: new URL(adminApp[hexclaveAppInternalsSymbol].getUrls().teamInvitation, domain).toString(), }); setSubmitted(true); } finally { diff --git a/packages/shared/src/config-authoring.ts b/packages/shared/src/config-authoring.ts index 1aadb5a60..b2b6d0e95 100644 --- a/packages/shared/src/config-authoring.ts +++ b/packages/shared/src/config-authoring.ts @@ -31,7 +31,9 @@ export function defineStackConfig(config: StrictStackConfig): Stack return config; } -// Hexclave alias — separate function so it does not inherit the deprecation tag. +/** + * Defines a Hexclave project configuration as code. See the documentation at https://skill.hexclave.com for more information. + */ export function defineHexclaveConfig(config: StrictStackConfig): HexclaveConfig { return config; } diff --git a/packages/template/src/components-page/hexclave-handler-client.tsx b/packages/template/src/components-page/hexclave-handler-client.tsx index 3c7972b6d..d79a87e43 100644 --- a/packages/template/src/components-page/hexclave-handler-client.tsx +++ b/packages/template/src/components-page/hexclave-handler-client.tsx @@ -214,7 +214,7 @@ function renderComponent(props: { } for (const [key, value] of Object.entries(pathAliases)) { if (path.toLowerCase().replaceAll('-', '') === key.toLowerCase().replaceAll('-', '')) { - const redirectUrl = `${app.urls.handler}/${value}?${new URLSearchParams(searchParams).toString()}`; + const redirectUrl = `${app[hexclaveAppInternalsSymbol].getUrls().handler}/${value}?${new URLSearchParams(searchParams).toString()}`; return { redirect: redirectUrl }; } } @@ -270,6 +270,7 @@ function RedirectToPage(props: { export function HexclaveHandlerClient(props: BaseHandlerProps & Partial & { location?: string }) { // Use hooks to get app const hexclaveApp = useStackApp(); + const handlerUrls = hexclaveApp[hexclaveAppInternalsSymbol].getUrls(); const clientOrigin = useClientOriginAfterHydration(); // IF_PLATFORM next @@ -281,13 +282,13 @@ export function HexclaveHandlerClient(props: BaseHandlerProps & Partial { - const handlerPath = new URL(hexclaveApp.urls.handler, 'http://example.com').pathname; + const handlerPath = new URL(handlerUrls.handler, 'http://example.com').pathname; const relativePath = currentLocation.startsWith(handlerPath) ? currentLocation.slice(handlerPath.length).replace(/^\/+/, '') : currentLocation.replace(/^\/+/, ''); @@ -297,7 +298,7 @@ export function HexclaveHandlerClient(props: BaseHandlerProps & Partial { return resolveUnknownHandlerPathFallbackUrl({ @@ -308,7 +309,7 @@ export function HexclaveHandlerClient(props: BaseHandlerProps & Partial { - const url = hexclaveApp.urls[name]; + const url = handlerUrls[name]; const isCrossDomainLocalOauthCallback = name === "oauthCallback" && searchParams.hexclave_cross_domain_auth === "1"; if (isCrossDomainLocalOauthCallback) { return false; diff --git a/packages/template/src/dev-tool/dev-tool-core.ts b/packages/template/src/dev-tool/dev-tool-core.ts index d8c704c37..409e67f59 100644 --- a/packages/template/src/dev-tool/dev-tool-core.ts +++ b/packages/template/src/dev-tool/dev-tool-core.ts @@ -1848,7 +1848,7 @@ function createSupportTab(app: StackClientApp): HTMLElement { function createComponentsTab(app: StackClientApp): HTMLElement { const container = h('div', { className: 'sdt-pg-layout' }); const apiBaseUrl = resolveApiBaseUrl(app); - const urls = app.urls; + const urls = app[hexclaveAppInternalsSymbol].getUrls(); const urlOptions: HandlerUrlOptions = app[hexclaveAppInternalsSymbol].getConstructorOptions().urls ?? {}; const PAGE_ENTRIES: { key: keyof HandlerUrls; label: string }[] = [ diff --git a/packages/template/src/lib/hexclave-app/apps/implementations/client-app-impl.ts b/packages/template/src/lib/hexclave-app/apps/implementations/client-app-impl.ts index 740265774..c20bd6ff7 100644 --- a/packages/template/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +++ b/packages/template/src/lib/hexclave-app/apps/implementations/client-app-impl.ts @@ -896,7 +896,7 @@ export class _HexclaveClientAppImplIncomplete { return await this._interface.sendClientRequest(path, requestOptions, await this._getSession(), requestType); }, + getUrls: () => this._getUrls(), getRedirectMethod: () => this._redirectMethod ?? throwErr("Redirect method should have been initialized in the Stack client app constructor"), redirectToUrl: async (url: string | URL, options?: { replace?: boolean }) => { await this._redirectTo({ url, ...options }); diff --git a/packages/template/src/lib/hexclave-app/apps/interfaces/client-app.ts b/packages/template/src/lib/hexclave-app/apps/interfaces/client-app.ts index 3a237938c..4f15aa325 100644 --- a/packages/template/src/lib/hexclave-app/apps/interfaces/client-app.ts +++ b/packages/template/src/lib/hexclave-app/apps/interfaces/client-app.ts @@ -131,6 +131,7 @@ export type StackClientApp>, addRequestListener(listener: RequestListener): () => void, sendRequest(path: string, requestOptions: RequestInit, requestType?: "client" | "server" | "admin"): Promise, + getUrls(): Readonly, getRedirectMethod(): RedirectMethod, redirectToUrl(url: string | URL, options?: { replace?: boolean }): Promise, redirectToHandler(handlerName: keyof HandlerUrls, options?: RedirectToOptions): Promise,