Stop using app.urls everywhere

This commit is contained in:
Konstantin Wohlwend 2026-06-23 11:22:44 -07:00
parent 0132ec151a
commit 8820b39b37
7 changed files with 17 additions and 11 deletions

View File

@ -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) {

View File

@ -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 {

View File

@ -31,7 +31,9 @@ export function defineStackConfig(config: StrictStackConfig<StackConfig>): 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>): HexclaveConfig {
return config;
}

View File

@ -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<RouteProps> & { 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<RoutePro
const navigate = hexclaveApp.useNavigate();
const navigateRef = useRef(navigate);
navigateRef.current = navigate;
const currentLocation = props.location ?? (typeof window === "undefined" ? new URL(hexclaveApp.urls.handler, placeholderOrigin).pathname : window.location.pathname);
const currentLocation = props.location ?? (typeof window === "undefined" ? new URL(handlerUrls.handler, placeholderOrigin).pathname : window.location.pathname);
const searchParamsSource = new URLSearchParams(typeof window === "undefined" ? "" : window.location.search);
const redirectTargets: (string | undefined)[] = [];
END_PLATFORM */
const { path, searchParams, handlerPath } = useMemo(() => {
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<RoutePro
searchParams: Object.fromEntries(searchParamsSource.entries()),
handlerPath,
};
}, [currentLocation, searchParamsSource, hexclaveApp.urls.handler]);
}, [currentLocation, searchParamsSource, handlerUrls.handler]);
const getDefaultUnknownPathUrl = (unknownPath: string): string | null => {
return resolveUnknownHandlerPathFallbackUrl({
@ -308,7 +309,7 @@ export function HexclaveHandlerClient(props: BaseHandlerProps & Partial<RoutePro
};
const shouldRedirectToPage = (name: keyof HandlerUrls): boolean => {
const url = hexclaveApp.urls[name];
const url = handlerUrls[name];
const isCrossDomainLocalOauthCallback = name === "oauthCallback" && searchParams.hexclave_cross_domain_auth === "1";
if (isCrossDomainLocalOauthCallback) {
return false;

View File

@ -1848,7 +1848,7 @@ function createSupportTab(app: StackClientApp<true>): HTMLElement {
function createComponentsTab(app: StackClientApp<true>): 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 }[] = [

View File

@ -896,7 +896,7 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
protected _getOAuthCallbackRedirectUri(): string {
if (!this._isOAuthCallbackUrlHosted()) {
return this.urls.oauthCallback;
return this._getUrls().oauthCallback;
}
if (typeof window === "undefined") {
throw new HexclaveAssertionError("Hosted OAuth callback URLs require a browser environment to use the current URL as the redirect URI");
@ -4190,6 +4190,7 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
) => {
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 });

View File

@ -131,6 +131,7 @@ export type StackClientApp<HasTokenStore extends boolean = boolean, ProjectId ex
sendAnalyticsEventBatch(body: string, options: { keepalive: boolean }): Promise<Result<Response, Error>>,
addRequestListener(listener: RequestListener): () => void,
sendRequest(path: string, requestOptions: RequestInit, requestType?: "client" | "server" | "admin"): Promise<Response>,
getUrls(): Readonly<ResolvedHandlerUrls>,
getRedirectMethod(): RedirectMethod,
redirectToUrl(url: string | URL, options?: { replace?: boolean }): Promise<void>,
redirectToHandler(handlerName: keyof HandlerUrls, options?: RedirectToOptions): Promise<void>,