@hexclave/next 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,7 +14,7 @@ import { envVars } from "../../../../generated/env.js";
14
14
  import { resolveHandlerUrls } from "../../url-targets.js";
15
15
 
16
16
  //#region src/lib/hexclave-app/apps/implementations/common.ts
17
- const clientVersion = "js @hexclave/next@1.0.8";
17
+ const clientVersion = "js @hexclave/next@1.0.10";
18
18
  if (clientVersion.startsWith("STACK_COMPILE_TIME")) throw new HexclaveAssertionError("Client version was not replaced. Something went wrong during build!");
19
19
  const replaceHexclavePortPrefix = (input) => {
20
20
  if (!input) return input;
@@ -70,7 +70,7 @@ declare class _HexclaveServerAppImplIncomplete<HasTokenStore extends boolean, Pr
70
70
  protected _serverNotificationCategoryFromCrud(userId: string, crud: NotificationPreferenceCrud['Server']['Read']): NotificationCategory;
71
71
  protected _serverOAuthProviderFromCrud(crud: OAuthProviderCrud['Server']['Read']): {
72
72
  id: string;
73
- type: "google" | "github" | "microsoft" | "spotify" | "facebook" | "discord" | "gitlab" | "bitbucket" | "linkedin" | "apple" | "x" | "twitch";
73
+ type: "x" | "google" | "github" | "microsoft" | "spotify" | "facebook" | "discord" | "gitlab" | "bitbucket" | "linkedin" | "apple" | "twitch";
74
74
  userId: string;
75
75
  accountId: string;
76
76
  email: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"url-targets.d.ts","names":[],"sources":["../../../../src/lib/hexclave-app/url-targets.ts"],"mappings":";;;cA2Ga,mBAAA,GAAuB,OAAA;EAAW,SAAA;EAAmB,QAAA;AAAA;AAAA,cAyBrD,uBAAA,GAA2B,OAAA;EACtC,SAAA;EACA,WAAA;EACA,aAAA;AAAA;AAAA,cA+DW,kBAAA,GAAsB,OAAA;EAAW,IAAA,EAAM,iBAAA;EAA+B,SAAA;AAAA,MAAsB,mBAAA;AAAA,cAqJ5F,sBAAA,GAA0B,OAAA;EACrC,iBAAA,UAvNA;EAyNA,MAAA;EACA,SAAA;AAAA;AAAA,cAOW,oCAAA,GAAwC,OAAA;EACnD,aAAA,EAAe,uBAAA;EACf,SAAA;EACA,WAAA;AAAA;AAAA,iBAiBc,aAAA,CAAc,QAAA,UAAkB,cAAA;EAA4B,KAAA;EAAe,UAAA;EAAoB,aAAA;EAA8B,aAAA;AAAA;AAAA,cAuBhI,4BAAA,GAAgC,OAAA;EAAW,GAAA;EAAa,SAAA;AAAA"}
1
+ {"version":3,"file":"url-targets.d.ts","names":[],"sources":["../../../../src/lib/hexclave-app/url-targets.ts"],"mappings":";;;cA2Ga,mBAAA,GAAuB,OAAA;EAAW,SAAA;EAAmB,QAAA;AAAA;AAAA,cA0BrD,uBAAA,GAA2B,OAAA;EACtC,SAAA;EACA,WAAA;EACA,aAAA;AAAA;AAAA,cA+DW,kBAAA,GAAsB,OAAA;EAAW,IAAA,EAAM,iBAAA;EAA+B,SAAA;AAAA,MAAsB,mBAAA;AAAA,cAqJ5F,sBAAA,GAA0B,OAAA;EACrC,iBAAA,UAvNA;EAyNA,MAAA;EACA,SAAA;AAAA;AAAA,cAOW,oCAAA,GAAwC,OAAA;EACnD,aAAA,EAAe,uBAAA;EACf,SAAA;EACA,WAAA;AAAA;AAAA,iBAiBc,aAAA,CAAc,QAAA,UAAkB,cAAA;EAA4B,KAAA;EAAe,UAAA;EAAoB,aAAA;EAA8B,aAAA;AAAA;AAAA,cAuBhI,4BAAA,GAAgC,OAAA;EAAW,GAAA;EAAa,SAAA;AAAA"}
@@ -60,6 +60,7 @@ const isRelativeUrlString = (url) => {
60
60
  return !schemePrefixRegex.test(url);
61
61
  };
62
62
  const nonHostedHandlerNames = new Set([
63
+ "home",
63
64
  "afterSignIn",
64
65
  "afterSignUp",
65
66
  "afterSignOut"
@@ -1 +1 @@
1
- {"version":3,"file":"url-targets.js","names":[],"sources":["../../../../src/lib/hexclave-app/url-targets.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { getCustomPagePrompts, type CustomPagePrompt } from \"@hexclave/shared/dist/interface/handler-urls\";\nimport { HexclaveAssertionError } from \"@hexclave/shared/dist/utils/errors\";\nimport { getHostedHandlerUrlFromConfig } from \"@hexclave/shared/dist/utils/redirect-urls\";\nimport { envVars } from \"../../generated/env\";\nimport { DefaultHandlerUrlTarget, HandlerPageUrls, HandlerUrlOptions, HandlerUrlTarget, HandlerUrls, ResolvedHandlerUrls } from \"./common\";\n\nconst localUrlPlaceholderOrigin = \"http://example.com\";\nconst schemePrefixRegex = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:/;\n\nconst customPagePrompts: Record<keyof Omit<HandlerPageUrls, \"handler\">, CustomPagePrompt> = getCustomPagePrompts();\n\nconst joinHandlerComponentPath = (basePath: string, pagePath: string): string => {\n const normalizedBasePath = basePath.endsWith(\"/\") && basePath.length > 1\n ? basePath.slice(0, -1)\n : basePath;\n if (pagePath.length === 0) {\n return normalizedBasePath;\n }\n if (normalizedBasePath === \"/\") {\n return `/${pagePath}`;\n }\n return `${normalizedBasePath}/${pagePath}`;\n};\n\nconst getHostedPagePathForHandlerName = (handlerName: keyof HandlerUrls): string => {\n switch (handlerName) {\n case \"handler\": {\n return \"\";\n }\n case \"home\": {\n return \"\";\n }\n case \"afterSignIn\": {\n return \"\";\n }\n case \"afterSignUp\": {\n return \"\";\n }\n case \"afterSignOut\": {\n return \"\";\n }\n case \"signIn\": {\n return \"sign-in\";\n }\n case \"signUp\": {\n return \"sign-up\";\n }\n case \"signOut\": {\n return \"sign-out\";\n }\n case \"emailVerification\": {\n return \"email-verification\";\n }\n case \"passwordReset\": {\n return \"password-reset\";\n }\n case \"forgotPassword\": {\n return \"forgot-password\";\n }\n case \"oauthCallback\": {\n return \"oauth-callback\";\n }\n case \"magicLinkCallback\": {\n return \"magic-link-callback\";\n }\n case \"accountSettings\": {\n return \"account-settings\";\n }\n case \"teamInvitation\": {\n return \"team-invitation\";\n }\n case \"cliAuthConfirm\": {\n return \"cli-auth-confirm\";\n }\n case \"mfa\": {\n return \"mfa\";\n }\n case \"error\": {\n return \"error\";\n }\n case \"onboarding\": {\n return \"onboarding\";\n }\n }\n};\n\nconst resolveCustomTargetUrl = (options: {\n target: { type: \"custom\", url: string, version: number },\n handlerName: keyof HandlerUrls,\n}): string => {\n const handlerName = options.handlerName;\n if (handlerName in customPagePrompts) {\n const customPagePrompt = customPagePrompts[handlerName as keyof typeof customPagePrompts];\n if (options.target.version === 0 || options.target.version in customPagePrompt.versions) {\n return options.target.url;\n }\n\n throw new Error(`Unsupported custom page version ${options.target.version} for ${options.handlerName} page at ${options.target.url}. The latest supported version of this page is ${Math.max(0, ...Object.keys(customPagePrompt.versions).map(Number))}. Please upgrade your Hexclave SDK to a version that supports this version.`);\n } else {\n throw new Error(`URL target ${options.handlerName} cannot be a custom page. Please specify the URL as a string instead.`);\n }\n};\n\nexport const getHostedHandlerUrl = (options: { projectId: string, pagePath: string }): string => {\n const normalizedPagePath = options.pagePath.replace(/^\\/+/, \"\");\n const hostedPath = normalizedPagePath.length > 0 ? `handler/${normalizedPagePath}` : \"handler\";\n return getHostedHandlerUrlFromConfig({\n projectId: options.projectId,\n hostedPath,\n hostedHandlerDomainSuffix: envVars.HEXCLAVE_HOSTED_HANDLER_DOMAIN_SUFFIX,\n hostedHandlerUrlTemplate: envVars.HEXCLAVE_HOSTED_HANDLER_URL_TEMPLATE,\n hexclavePortPrefix: envVars.HEXCLAVE_PORT_PREFIX,\n });\n};\n\nconst isRelativeUrlString = (url: string): boolean => {\n if (url.startsWith(\"//\")) {\n return false;\n }\n return !schemePrefixRegex.test(url);\n};\n\nconst nonHostedHandlerNames = new Set<keyof HandlerUrls>([\n \"afterSignIn\",\n \"afterSignUp\",\n \"afterSignOut\",\n]);\n\nexport const isLocalHandlerUrlTarget = (options: {\n targetUrl: string,\n handlerPath: string,\n currentOrigin?: string,\n}): boolean => {\n const urlObject = new URL(options.targetUrl, localUrlPlaceholderOrigin);\n const isHandlerPathTarget = urlObject.pathname === options.handlerPath\n || urlObject.pathname.startsWith(`${options.handlerPath}/`);\n if (!isHandlerPathTarget) {\n return false;\n }\n\n // On server we only have path information, so treat matching handler paths as local.\n if (options.currentOrigin == null) {\n return true;\n }\n\n return isRelativeUrlString(options.targetUrl) || urlObject.origin === options.currentOrigin;\n};\n\nconst resolveUrlTarget = (options: {\n target: HandlerUrlTarget,\n fallbackPath: string,\n handlerName: keyof HandlerUrls,\n projectId: string,\n}): string => {\n if (typeof options.target === \"string\") {\n return options.target;\n }\n\n switch (options.target.type) {\n case \"handler-component\": {\n return options.fallbackPath;\n }\n case \"hosted\": {\n if (nonHostedHandlerNames.has(options.handlerName)) {\n return options.fallbackPath;\n }\n return getHostedHandlerUrl({\n projectId: options.projectId,\n pagePath: getHostedPagePathForHandlerName(options.handlerName),\n });\n }\n case \"custom\": {\n return resolveCustomTargetUrl({\n target: options.target,\n handlerName: options.handlerName,\n });\n }\n }\n};\n\nconst assertOAuthCallbackTargetIsRelative = (target: HandlerUrlTarget): void => {\n const url = typeof target === \"string\"\n ? target\n : target.type === \"custom\"\n ? target.url\n : null;\n if (url != null && !isRelativeUrlString(url)) {\n throw new HexclaveAssertionError(\"OAuth callback URLs must be relative.\", {\n oauthCallbackUrl: url,\n hint: \"Use a relative URL like '/handler/oauth-callback', or use { type: 'hosted' } to let Stack use the current page for hosted callbacks.\",\n });\n }\n};\n\nexport const resolveHandlerUrls = (options: { urls: HandlerUrlOptions | undefined, projectId: string }): ResolvedHandlerUrls => {\n const configuredUrls = options.urls;\n const defaultTarget = configuredUrls?.default ?? { type: \"handler-component\" } as const;\n const oauthCallbackTarget: HandlerUrlTarget = configuredUrls?.oauthCallback ?? (\n defaultTarget.type === \"hosted\"\n ? defaultTarget\n : { type: \"handler-component\" }\n );\n assertOAuthCallbackTargetIsRelative(oauthCallbackTarget);\n let handlerComponentBasePath = \"/handler\";\n if (typeof configuredUrls?.handler === \"string\") {\n handlerComponentBasePath = configuredUrls.handler;\n } else if (configuredUrls?.handler != null && configuredUrls.handler.type === \"custom\") {\n handlerComponentBasePath = resolveCustomTargetUrl({\n target: configuredUrls.handler,\n handlerName: \"handler\",\n });\n }\n\n const homeTarget = configuredUrls?.home ?? defaultTarget;\n const localHome = resolveUrlTarget({\n target: typeof homeTarget !== \"string\" && homeTarget.type === \"hosted\"\n ? { type: \"handler-component\" }\n : homeTarget,\n fallbackPath: \"/\",\n handlerName: \"home\",\n projectId: options.projectId,\n });\n const home = resolveUrlTarget({\n target: homeTarget,\n fallbackPath: \"/\",\n handlerName: \"home\",\n projectId: options.projectId,\n });\n const afterSignIn = resolveUrlTarget({\n target: configuredUrls?.afterSignIn ?? defaultTarget,\n fallbackPath: localHome,\n handlerName: \"afterSignIn\",\n projectId: options.projectId,\n });\n\n return {\n handler: resolveUrlTarget({\n target: configuredUrls?.handler ?? defaultTarget,\n fallbackPath: handlerComponentBasePath,\n handlerName: \"handler\",\n projectId: options.projectId,\n }),\n signIn: resolveUrlTarget({\n target: configuredUrls?.signIn ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"sign-in\"),\n handlerName: \"signIn\",\n projectId: options.projectId,\n }),\n signUp: resolveUrlTarget({\n target: configuredUrls?.signUp ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"sign-up\"),\n handlerName: \"signUp\",\n projectId: options.projectId,\n }),\n afterSignIn,\n afterSignUp: resolveUrlTarget({\n target: configuredUrls?.afterSignUp ?? defaultTarget,\n fallbackPath: afterSignIn,\n handlerName: \"afterSignUp\",\n projectId: options.projectId,\n }),\n signOut: resolveUrlTarget({\n target: configuredUrls?.signOut ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"sign-out\"),\n handlerName: \"signOut\",\n projectId: options.projectId,\n }),\n afterSignOut: resolveUrlTarget({\n target: configuredUrls?.afterSignOut ?? defaultTarget,\n fallbackPath: localHome,\n handlerName: \"afterSignOut\",\n projectId: options.projectId,\n }),\n emailVerification: resolveUrlTarget({\n target: configuredUrls?.emailVerification ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"email-verification\"),\n handlerName: \"emailVerification\",\n projectId: options.projectId,\n }),\n passwordReset: resolveUrlTarget({\n target: configuredUrls?.passwordReset ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"password-reset\"),\n handlerName: \"passwordReset\",\n projectId: options.projectId,\n }),\n forgotPassword: resolveUrlTarget({\n target: configuredUrls?.forgotPassword ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"forgot-password\"),\n handlerName: \"forgotPassword\",\n projectId: options.projectId,\n }),\n home,\n oauthCallback: resolveUrlTarget({\n target: oauthCallbackTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"oauth-callback\"),\n handlerName: \"oauthCallback\",\n projectId: options.projectId,\n }),\n magicLinkCallback: resolveUrlTarget({\n target: configuredUrls?.magicLinkCallback ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"magic-link-callback\"),\n handlerName: \"magicLinkCallback\",\n projectId: options.projectId,\n }),\n accountSettings: resolveUrlTarget({\n target: configuredUrls?.accountSettings ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"account-settings\"),\n handlerName: \"accountSettings\",\n projectId: options.projectId,\n }),\n teamInvitation: resolveUrlTarget({\n target: configuredUrls?.teamInvitation ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"team-invitation\"),\n handlerName: \"teamInvitation\",\n projectId: options.projectId,\n }),\n cliAuthConfirm: resolveUrlTarget({\n target: configuredUrls?.cliAuthConfirm ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"cli-auth-confirm\"),\n handlerName: \"cliAuthConfirm\",\n projectId: options.projectId,\n }),\n mfa: resolveUrlTarget({\n target: configuredUrls?.mfa ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"mfa\"),\n handlerName: \"mfa\",\n projectId: options.projectId,\n }),\n error: resolveUrlTarget({\n target: configuredUrls?.error ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"error\"),\n handlerName: \"error\",\n projectId: options.projectId,\n }),\n onboarding: resolveUrlTarget({\n target: configuredUrls?.onboarding ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"onboarding\"),\n handlerName: \"onboarding\",\n projectId: options.projectId,\n }),\n };\n};\n\nexport const buildCliAuthConfirmUrl = (options: {\n cliAuthConfirmUrl: string,\n /** Used as the base URL only when cliAuthConfirmUrl is relative. */\n appUrl: string,\n loginCode: string,\n}): string => {\n const url = new URL(options.cliAuthConfirmUrl, options.appUrl);\n url.searchParams.set(\"login_code\", options.loginCode);\n return url.toString();\n};\n\nexport const resolveUnknownHandlerPathFallbackUrl = (options: {\n defaultTarget: DefaultHandlerUrlTarget | undefined,\n projectId: string,\n unknownPath: string,\n}): string | null => {\n const defaultTarget = options.defaultTarget ?? { type: \"handler-component\" } satisfies DefaultHandlerUrlTarget;\n\n switch (defaultTarget.type) {\n case \"handler-component\": {\n return null;\n }\n case \"hosted\": {\n return getHostedHandlerUrl({\n projectId: options.projectId,\n pagePath: options.unknownPath,\n });\n }\n }\n};\n\nexport function getPagePrompt(pageName: string, currentVersion?: number): { title: string; fullPrompt: string; upgradePrompt: string | null; latestVersion: number } | null {\n if (!(pageName in customPagePrompts)) return null;\n const prompt = customPagePrompts[pageName as keyof typeof customPagePrompts];\n const versionKeys = Object.keys(prompt.versions).map(Number);\n const latestVersion = versionKeys.length > 0 ? Math.max(...versionKeys) : 0;\n\n let upgradePrompt: string | null = null;\n if (currentVersion != null) {\n const relevantVersions = versionKeys\n .filter(v => v > currentVersion)\n .sort((a, b) => a - b);\n const prompts = relevantVersions\n .map(v => prompt.versions[v].upgradePrompt)\n .filter(p => p.length > 0);\n upgradePrompt = prompts.length > 0 ? prompts.join(\"\\n\\n\") : null;\n } else {\n const upgradeEntry = latestVersion > 0 ? prompt.versions[latestVersion] : undefined;\n upgradePrompt = upgradeEntry?.upgradePrompt ?? null;\n }\n\n return { title: prompt.title, fullPrompt: prompt.fullPrompt, upgradePrompt, latestVersion };\n}\n\nexport const isHostedHandlerUrlForProject = (options: { url: string, projectId: string }): boolean => {\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(options.url);\n } catch {\n return false;\n }\n\n const hostedBaseUrl = new URL(getHostedHandlerUrl({ projectId: options.projectId, pagePath: \"\" }));\n return parsedUrl.origin === hostedBaseUrl.origin\n && (parsedUrl.pathname === hostedBaseUrl.pathname || parsedUrl.pathname.startsWith(`${hostedBaseUrl.pathname}/`));\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"url-targets.js","names":[],"sources":["../../../../src/lib/hexclave-app/url-targets.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { getCustomPagePrompts, type CustomPagePrompt } from \"@hexclave/shared/dist/interface/handler-urls\";\nimport { HexclaveAssertionError } from \"@hexclave/shared/dist/utils/errors\";\nimport { getHostedHandlerUrlFromConfig } from \"@hexclave/shared/dist/utils/redirect-urls\";\nimport { envVars } from \"../../generated/env\";\nimport { DefaultHandlerUrlTarget, HandlerPageUrls, HandlerUrlOptions, HandlerUrlTarget, HandlerUrls, ResolvedHandlerUrls } from \"./common\";\n\nconst localUrlPlaceholderOrigin = \"http://example.com\";\nconst schemePrefixRegex = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:/;\n\nconst customPagePrompts: Record<keyof Omit<HandlerPageUrls, \"handler\">, CustomPagePrompt> = getCustomPagePrompts();\n\nconst joinHandlerComponentPath = (basePath: string, pagePath: string): string => {\n const normalizedBasePath = basePath.endsWith(\"/\") && basePath.length > 1\n ? basePath.slice(0, -1)\n : basePath;\n if (pagePath.length === 0) {\n return normalizedBasePath;\n }\n if (normalizedBasePath === \"/\") {\n return `/${pagePath}`;\n }\n return `${normalizedBasePath}/${pagePath}`;\n};\n\nconst getHostedPagePathForHandlerName = (handlerName: keyof HandlerUrls): string => {\n switch (handlerName) {\n case \"handler\": {\n return \"\";\n }\n case \"home\": {\n return \"\";\n }\n case \"afterSignIn\": {\n return \"\";\n }\n case \"afterSignUp\": {\n return \"\";\n }\n case \"afterSignOut\": {\n return \"\";\n }\n case \"signIn\": {\n return \"sign-in\";\n }\n case \"signUp\": {\n return \"sign-up\";\n }\n case \"signOut\": {\n return \"sign-out\";\n }\n case \"emailVerification\": {\n return \"email-verification\";\n }\n case \"passwordReset\": {\n return \"password-reset\";\n }\n case \"forgotPassword\": {\n return \"forgot-password\";\n }\n case \"oauthCallback\": {\n return \"oauth-callback\";\n }\n case \"magicLinkCallback\": {\n return \"magic-link-callback\";\n }\n case \"accountSettings\": {\n return \"account-settings\";\n }\n case \"teamInvitation\": {\n return \"team-invitation\";\n }\n case \"cliAuthConfirm\": {\n return \"cli-auth-confirm\";\n }\n case \"mfa\": {\n return \"mfa\";\n }\n case \"error\": {\n return \"error\";\n }\n case \"onboarding\": {\n return \"onboarding\";\n }\n }\n};\n\nconst resolveCustomTargetUrl = (options: {\n target: { type: \"custom\", url: string, version: number },\n handlerName: keyof HandlerUrls,\n}): string => {\n const handlerName = options.handlerName;\n if (handlerName in customPagePrompts) {\n const customPagePrompt = customPagePrompts[handlerName as keyof typeof customPagePrompts];\n if (options.target.version === 0 || options.target.version in customPagePrompt.versions) {\n return options.target.url;\n }\n\n throw new Error(`Unsupported custom page version ${options.target.version} for ${options.handlerName} page at ${options.target.url}. The latest supported version of this page is ${Math.max(0, ...Object.keys(customPagePrompt.versions).map(Number))}. Please upgrade your Hexclave SDK to a version that supports this version.`);\n } else {\n throw new Error(`URL target ${options.handlerName} cannot be a custom page. Please specify the URL as a string instead.`);\n }\n};\n\nexport const getHostedHandlerUrl = (options: { projectId: string, pagePath: string }): string => {\n const normalizedPagePath = options.pagePath.replace(/^\\/+/, \"\");\n const hostedPath = normalizedPagePath.length > 0 ? `handler/${normalizedPagePath}` : \"handler\";\n return getHostedHandlerUrlFromConfig({\n projectId: options.projectId,\n hostedPath,\n hostedHandlerDomainSuffix: envVars.HEXCLAVE_HOSTED_HANDLER_DOMAIN_SUFFIX,\n hostedHandlerUrlTemplate: envVars.HEXCLAVE_HOSTED_HANDLER_URL_TEMPLATE,\n hexclavePortPrefix: envVars.HEXCLAVE_PORT_PREFIX,\n });\n};\n\nconst isRelativeUrlString = (url: string): boolean => {\n if (url.startsWith(\"//\")) {\n return false;\n }\n return !schemePrefixRegex.test(url);\n};\n\nconst nonHostedHandlerNames = new Set<keyof HandlerUrls>([\n \"home\",\n \"afterSignIn\",\n \"afterSignUp\",\n \"afterSignOut\",\n]);\n\nexport const isLocalHandlerUrlTarget = (options: {\n targetUrl: string,\n handlerPath: string,\n currentOrigin?: string,\n}): boolean => {\n const urlObject = new URL(options.targetUrl, localUrlPlaceholderOrigin);\n const isHandlerPathTarget = urlObject.pathname === options.handlerPath\n || urlObject.pathname.startsWith(`${options.handlerPath}/`);\n if (!isHandlerPathTarget) {\n return false;\n }\n\n // On server we only have path information, so treat matching handler paths as local.\n if (options.currentOrigin == null) {\n return true;\n }\n\n return isRelativeUrlString(options.targetUrl) || urlObject.origin === options.currentOrigin;\n};\n\nconst resolveUrlTarget = (options: {\n target: HandlerUrlTarget,\n fallbackPath: string,\n handlerName: keyof HandlerUrls,\n projectId: string,\n}): string => {\n if (typeof options.target === \"string\") {\n return options.target;\n }\n\n switch (options.target.type) {\n case \"handler-component\": {\n return options.fallbackPath;\n }\n case \"hosted\": {\n if (nonHostedHandlerNames.has(options.handlerName)) {\n return options.fallbackPath;\n }\n return getHostedHandlerUrl({\n projectId: options.projectId,\n pagePath: getHostedPagePathForHandlerName(options.handlerName),\n });\n }\n case \"custom\": {\n return resolveCustomTargetUrl({\n target: options.target,\n handlerName: options.handlerName,\n });\n }\n }\n};\n\nconst assertOAuthCallbackTargetIsRelative = (target: HandlerUrlTarget): void => {\n const url = typeof target === \"string\"\n ? target\n : target.type === \"custom\"\n ? target.url\n : null;\n if (url != null && !isRelativeUrlString(url)) {\n throw new HexclaveAssertionError(\"OAuth callback URLs must be relative.\", {\n oauthCallbackUrl: url,\n hint: \"Use a relative URL like '/handler/oauth-callback', or use { type: 'hosted' } to let Stack use the current page for hosted callbacks.\",\n });\n }\n};\n\nexport const resolveHandlerUrls = (options: { urls: HandlerUrlOptions | undefined, projectId: string }): ResolvedHandlerUrls => {\n const configuredUrls = options.urls;\n const defaultTarget = configuredUrls?.default ?? { type: \"handler-component\" } as const;\n const oauthCallbackTarget: HandlerUrlTarget = configuredUrls?.oauthCallback ?? (\n defaultTarget.type === \"hosted\"\n ? defaultTarget\n : { type: \"handler-component\" }\n );\n assertOAuthCallbackTargetIsRelative(oauthCallbackTarget);\n let handlerComponentBasePath = \"/handler\";\n if (typeof configuredUrls?.handler === \"string\") {\n handlerComponentBasePath = configuredUrls.handler;\n } else if (configuredUrls?.handler != null && configuredUrls.handler.type === \"custom\") {\n handlerComponentBasePath = resolveCustomTargetUrl({\n target: configuredUrls.handler,\n handlerName: \"handler\",\n });\n }\n\n const homeTarget = configuredUrls?.home ?? defaultTarget;\n const localHome = resolveUrlTarget({\n target: typeof homeTarget !== \"string\" && homeTarget.type === \"hosted\"\n ? { type: \"handler-component\" }\n : homeTarget,\n fallbackPath: \"/\",\n handlerName: \"home\",\n projectId: options.projectId,\n });\n const home = resolveUrlTarget({\n target: homeTarget,\n fallbackPath: \"/\",\n handlerName: \"home\",\n projectId: options.projectId,\n });\n const afterSignIn = resolveUrlTarget({\n target: configuredUrls?.afterSignIn ?? defaultTarget,\n fallbackPath: localHome,\n handlerName: \"afterSignIn\",\n projectId: options.projectId,\n });\n\n return {\n handler: resolveUrlTarget({\n target: configuredUrls?.handler ?? defaultTarget,\n fallbackPath: handlerComponentBasePath,\n handlerName: \"handler\",\n projectId: options.projectId,\n }),\n signIn: resolveUrlTarget({\n target: configuredUrls?.signIn ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"sign-in\"),\n handlerName: \"signIn\",\n projectId: options.projectId,\n }),\n signUp: resolveUrlTarget({\n target: configuredUrls?.signUp ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"sign-up\"),\n handlerName: \"signUp\",\n projectId: options.projectId,\n }),\n afterSignIn,\n afterSignUp: resolveUrlTarget({\n target: configuredUrls?.afterSignUp ?? defaultTarget,\n fallbackPath: afterSignIn,\n handlerName: \"afterSignUp\",\n projectId: options.projectId,\n }),\n signOut: resolveUrlTarget({\n target: configuredUrls?.signOut ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"sign-out\"),\n handlerName: \"signOut\",\n projectId: options.projectId,\n }),\n afterSignOut: resolveUrlTarget({\n target: configuredUrls?.afterSignOut ?? defaultTarget,\n fallbackPath: localHome,\n handlerName: \"afterSignOut\",\n projectId: options.projectId,\n }),\n emailVerification: resolveUrlTarget({\n target: configuredUrls?.emailVerification ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"email-verification\"),\n handlerName: \"emailVerification\",\n projectId: options.projectId,\n }),\n passwordReset: resolveUrlTarget({\n target: configuredUrls?.passwordReset ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"password-reset\"),\n handlerName: \"passwordReset\",\n projectId: options.projectId,\n }),\n forgotPassword: resolveUrlTarget({\n target: configuredUrls?.forgotPassword ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"forgot-password\"),\n handlerName: \"forgotPassword\",\n projectId: options.projectId,\n }),\n home,\n oauthCallback: resolveUrlTarget({\n target: oauthCallbackTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"oauth-callback\"),\n handlerName: \"oauthCallback\",\n projectId: options.projectId,\n }),\n magicLinkCallback: resolveUrlTarget({\n target: configuredUrls?.magicLinkCallback ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"magic-link-callback\"),\n handlerName: \"magicLinkCallback\",\n projectId: options.projectId,\n }),\n accountSettings: resolveUrlTarget({\n target: configuredUrls?.accountSettings ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"account-settings\"),\n handlerName: \"accountSettings\",\n projectId: options.projectId,\n }),\n teamInvitation: resolveUrlTarget({\n target: configuredUrls?.teamInvitation ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"team-invitation\"),\n handlerName: \"teamInvitation\",\n projectId: options.projectId,\n }),\n cliAuthConfirm: resolveUrlTarget({\n target: configuredUrls?.cliAuthConfirm ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"cli-auth-confirm\"),\n handlerName: \"cliAuthConfirm\",\n projectId: options.projectId,\n }),\n mfa: resolveUrlTarget({\n target: configuredUrls?.mfa ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"mfa\"),\n handlerName: \"mfa\",\n projectId: options.projectId,\n }),\n error: resolveUrlTarget({\n target: configuredUrls?.error ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"error\"),\n handlerName: \"error\",\n projectId: options.projectId,\n }),\n onboarding: resolveUrlTarget({\n target: configuredUrls?.onboarding ?? defaultTarget,\n fallbackPath: joinHandlerComponentPath(handlerComponentBasePath, \"onboarding\"),\n handlerName: \"onboarding\",\n projectId: options.projectId,\n }),\n };\n};\n\nexport const buildCliAuthConfirmUrl = (options: {\n cliAuthConfirmUrl: string,\n /** Used as the base URL only when cliAuthConfirmUrl is relative. */\n appUrl: string,\n loginCode: string,\n}): string => {\n const url = new URL(options.cliAuthConfirmUrl, options.appUrl);\n url.searchParams.set(\"login_code\", options.loginCode);\n return url.toString();\n};\n\nexport const resolveUnknownHandlerPathFallbackUrl = (options: {\n defaultTarget: DefaultHandlerUrlTarget | undefined,\n projectId: string,\n unknownPath: string,\n}): string | null => {\n const defaultTarget = options.defaultTarget ?? { type: \"handler-component\" } satisfies DefaultHandlerUrlTarget;\n\n switch (defaultTarget.type) {\n case \"handler-component\": {\n return null;\n }\n case \"hosted\": {\n return getHostedHandlerUrl({\n projectId: options.projectId,\n pagePath: options.unknownPath,\n });\n }\n }\n};\n\nexport function getPagePrompt(pageName: string, currentVersion?: number): { title: string; fullPrompt: string; upgradePrompt: string | null; latestVersion: number } | null {\n if (!(pageName in customPagePrompts)) return null;\n const prompt = customPagePrompts[pageName as keyof typeof customPagePrompts];\n const versionKeys = Object.keys(prompt.versions).map(Number);\n const latestVersion = versionKeys.length > 0 ? Math.max(...versionKeys) : 0;\n\n let upgradePrompt: string | null = null;\n if (currentVersion != null) {\n const relevantVersions = versionKeys\n .filter(v => v > currentVersion)\n .sort((a, b) => a - b);\n const prompts = relevantVersions\n .map(v => prompt.versions[v].upgradePrompt)\n .filter(p => p.length > 0);\n upgradePrompt = prompts.length > 0 ? prompts.join(\"\\n\\n\") : null;\n } else {\n const upgradeEntry = latestVersion > 0 ? prompt.versions[latestVersion] : undefined;\n upgradePrompt = upgradeEntry?.upgradePrompt ?? null;\n }\n\n return { title: prompt.title, fullPrompt: prompt.fullPrompt, upgradePrompt, latestVersion };\n}\n\nexport const isHostedHandlerUrlForProject = (options: { url: string, projectId: string }): boolean => {\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(options.url);\n } catch {\n return false;\n }\n\n const hostedBaseUrl = new URL(getHostedHandlerUrl({ projectId: options.projectId, pagePath: \"\" }));\n return parsedUrl.origin === hostedBaseUrl.origin\n && (parsedUrl.pathname === hostedBaseUrl.pathname || parsedUrl.pathname.startsWith(`${hostedBaseUrl.pathname}/`));\n};\n"],"mappings":"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"}
@@ -96,6 +96,7 @@ describe("handler URL targets", () => {
96
96
  });
97
97
  expect(urls.signIn).toBe("https://project-id.example-stack-hosted.test/handler/sign-in");
98
98
  expect(urls.signOut).toBe("https://project-id.example-stack-hosted.test/handler/sign-out");
99
+ expect(urls.home).toBe("/");
99
100
  expect(urls.afterSignIn).toBe("/");
100
101
  expect(urls.afterSignUp).toBe("/");
101
102
  expect(urls.afterSignOut).toBe("/");
@@ -1 +1 @@
1
- {"version":3,"file":"url-targets.test.js","names":[],"sources":["../../../../src/lib/hexclave-app/url-targets.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { buildCliAuthConfirmUrl, getPagePrompt, isLocalHandlerUrlTarget, resolveHandlerUrls, resolveUnknownHandlerPathFallbackUrl } from \"./url-targets\";\n\ndescribe(\"handler URL targets\", () => {\n afterEach(() => {\n vi.unstubAllEnvs();\n });\n\n it(\"treats handler-component targets the same as omitted values\", () => {\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n handler: \"/custom-handler\",\n signIn: { type: \"handler-component\" },\n },\n });\n\n expect(urls.signIn).toBe(\"/custom-handler/sign-in\");\n });\n\n it(\"treats custom v0 page targets like legacy string targets\", () => {\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n handler: \"/custom-handler\",\n signIn: { type: \"handler-component\" },\n signUp: { type: \"custom\", url: \"/sign-up-explicit\", version: 0 },\n },\n });\n\n expect(urls.signIn).toBe(\"/custom-handler/sign-in\");\n expect(urls.signUp).toBe(\"/sign-up-explicit\");\n });\n\n it(\"throws on v0 custom target for handler page\", () => {\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n handler: { type: \"custom\", url: \"/custom-handler\", version: 0 },\n },\n })).toThrowError(/cannot be a custom page/);\n });\n\n it(\"supports the latest documented custom target version\", () => {\n const signInPrompt = getPagePrompt(\"signIn\");\n if (signInPrompt == null) {\n throw new Error(\"Expected signIn prompt metadata to exist\");\n }\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n signIn: { type: \"custom\", url: \"/custom-sign-in\", version: signInPrompt.latestVersion },\n },\n });\n\n expect(urls.signIn).toBe(\"/custom-sign-in\");\n });\n\n it(\"throws on custom target versions newer than the latest supported version\", () => {\n const signInPrompt = getPagePrompt(\"signIn\");\n if (signInPrompt == null) {\n throw new Error(\"Expected signIn prompt metadata to exist\");\n }\n\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n signIn: { type: \"custom\", url: \"/custom-sign-in\", version: signInPrompt.latestVersion + 1 },\n },\n })).toThrowError(/Unsupported custom page version/);\n });\n\n it(\"throws on non-zero custom version for handler page\", () => {\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n handler: { type: \"custom\", url: \"/custom-handler\", version: 1 },\n },\n })).toThrowError(/cannot be a custom page/);\n });\n\n it(\"uses hosted defaults for unspecified URLs\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX\", \".example-stack-hosted.test\");\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n signUp: \"/sign-up\",\n default: { type: \"hosted\" },\n },\n });\n\n expect(urls.signUp).toBe(\"/sign-up\");\n expect(urls.signIn).toBe(\"https://project-id.example-stack-hosted.test/handler/sign-in\");\n expect(urls.cliAuthConfirm).toBe(\"https://project-id.example-stack-hosted.test/handler/cli-auth-confirm\");\n });\n\n it(\"keeps redirect-only post-auth targets local even when the default target is hosted\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX\", \".example-stack-hosted.test\");\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n default: { type: \"hosted\" },\n },\n });\n\n expect(urls.signIn).toBe(\"https://project-id.example-stack-hosted.test/handler/sign-in\");\n expect(urls.signOut).toBe(\"https://project-id.example-stack-hosted.test/handler/sign-out\");\n expect(urls.afterSignIn).toBe(\"/\");\n expect(urls.afterSignUp).toBe(\"/\");\n expect(urls.afterSignOut).toBe(\"/\");\n });\n\n it(\"rejects absolute OAuth callback string targets\", () => {\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n oauthCallback: \"https://app.example.test/oauth-callback\",\n },\n })).toThrowErrorMatchingInlineSnapshot(`\n [HexclaveAssertionError: OAuth callback URLs must be relative.\n\n This is likely an error in Hexclave. Please make sure you are running the newest version and report it.]\n `);\n });\n\n it(\"rejects absolute OAuth callback custom targets\", () => {\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n oauthCallback: { type: \"custom\", url: \"https://app.example.test/oauth-callback\", version: 0 },\n },\n })).toThrowErrorMatchingInlineSnapshot(`\n [HexclaveAssertionError: OAuth callback URLs must be relative.\n\n This is likely an error in Hexclave. Please make sure you are running the newest version and report it.]\n `);\n });\n\n it(\"inherits a hosted default target for the OAuth callback\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX\", \".example-stack-hosted.test\");\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n default: { type: \"hosted\" },\n },\n });\n\n expect(urls.signIn).toBe(\"https://project-id.example-stack-hosted.test/handler/sign-in\");\n expect(urls.oauthCallback).toBe(\"https://project-id.example-stack-hosted.test/handler/oauth-callback\");\n });\n\n it(\"supports custom CLI auth confirmation targets\", () => {\n const cliAuthConfirmPrompt = getPagePrompt(\"cliAuthConfirm\");\n if (cliAuthConfirmPrompt == null) {\n throw new Error(\"Expected cliAuthConfirm prompt metadata to exist\");\n }\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n cliAuthConfirm: { type: \"custom\", url: \"/cli/authorize\", version: cliAuthConfirmPrompt.latestVersion },\n },\n });\n\n expect(urls.cliAuthConfirm).toBe(\"/cli/authorize\");\n });\n\n it(\"builds CLI auth login URLs from the resolved confirmation target\", () => {\n expect(buildCliAuthConfirmUrl({\n cliAuthConfirmUrl: \"/cli/authorize\",\n appUrl: \"https://app.example.test/base\",\n loginCode: \"login-code\",\n })).toBe(\"https://app.example.test/cli/authorize?login_code=login-code\");\n });\n\n it(\"uses default target for unknown /handler/* pages\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX\", \".example-stack-hosted.test\");\n\n const url = resolveUnknownHandlerPathFallbackUrl({\n defaultTarget: { type: \"hosted\" },\n projectId: \"project-id\",\n unknownPath: \"custom-page\",\n });\n\n expect(url).toBe(\"https://project-id.example-stack-hosted.test/handler/custom-page\");\n });\n\n it(\"uses the full hosted handler URL template when configured\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE\", \"http://{projectId}.localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09/{hostedPath}\");\n vi.stubEnv(\"NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX\", \"93\");\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n default: { type: \"hosted\" },\n },\n });\n\n expect(urls.signIn).toBe(\"http://project-id.localhost:9309/handler/sign-in\");\n expect(urls.accountSettings).toBe(\"http://project-id.localhost:9309/handler/account-settings\");\n });\n\n it(\"validates the hosted handler URL template placeholders\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE\", \"http://localhost:9309/{projectId}/handler\");\n\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n default: { type: \"hosted\" },\n },\n })).toThrowError(/\\{projectId\\} and \\{hostedPath\\}/);\n });\n\n it(\"rejects hosted handler URL templates that put the project ID in the path\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE\", \"http://localhost:9309/{projectId}/{hostedPath}\");\n\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n default: { type: \"hosted\" },\n },\n })).toThrowErrorMatchingInlineSnapshot(`\n [HexclaveAssertionError: The hosted handler URL template must put {projectId} in the hostname.\n\n This is likely an error in Hexclave. Please make sure you are running the newest version and report it.]\n `);\n });\n});\n\ndescribe(\"isLocalHandlerUrlTarget\", () => {\n it(\"treats relative handler URLs as local targets\", () => {\n expect(isLocalHandlerUrlTarget({\n targetUrl: \"/handler/sign-in\",\n handlerPath: \"/handler\",\n currentOrigin: \"http://p91.localhost:9101\",\n })).toBe(true);\n });\n\n it(\"treats same-origin absolute handler URLs as local targets\", () => {\n expect(isLocalHandlerUrlTarget({\n targetUrl: \"http://p91.localhost:9101/handler/sign-in\",\n handlerPath: \"/handler\",\n currentOrigin: \"http://p91.localhost:9101\",\n })).toBe(true);\n });\n\n it(\"treats cross-origin absolute handler URLs as non-local targets\", () => {\n expect(isLocalHandlerUrlTarget({\n targetUrl: \"https://project-id.built-with-hexclave.com/handler/sign-in\",\n handlerPath: \"/handler\",\n currentOrigin: \"http://p91.localhost:9101\",\n })).toBe(false);\n });\n\n it(\"treats non-handler paths as non-local targets\", () => {\n expect(isLocalHandlerUrlTarget({\n targetUrl: \"/projects\",\n handlerPath: \"/handler\",\n currentOrigin: \"http://p91.localhost:9101\",\n })).toBe(false);\n });\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"url-targets.test.js","names":[],"sources":["../../../../src/lib/hexclave-app/url-targets.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { buildCliAuthConfirmUrl, getPagePrompt, isLocalHandlerUrlTarget, resolveHandlerUrls, resolveUnknownHandlerPathFallbackUrl } from \"./url-targets\";\n\ndescribe(\"handler URL targets\", () => {\n afterEach(() => {\n vi.unstubAllEnvs();\n });\n\n it(\"treats handler-component targets the same as omitted values\", () => {\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n handler: \"/custom-handler\",\n signIn: { type: \"handler-component\" },\n },\n });\n\n expect(urls.signIn).toBe(\"/custom-handler/sign-in\");\n });\n\n it(\"treats custom v0 page targets like legacy string targets\", () => {\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n handler: \"/custom-handler\",\n signIn: { type: \"handler-component\" },\n signUp: { type: \"custom\", url: \"/sign-up-explicit\", version: 0 },\n },\n });\n\n expect(urls.signIn).toBe(\"/custom-handler/sign-in\");\n expect(urls.signUp).toBe(\"/sign-up-explicit\");\n });\n\n it(\"throws on v0 custom target for handler page\", () => {\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n handler: { type: \"custom\", url: \"/custom-handler\", version: 0 },\n },\n })).toThrowError(/cannot be a custom page/);\n });\n\n it(\"supports the latest documented custom target version\", () => {\n const signInPrompt = getPagePrompt(\"signIn\");\n if (signInPrompt == null) {\n throw new Error(\"Expected signIn prompt metadata to exist\");\n }\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n signIn: { type: \"custom\", url: \"/custom-sign-in\", version: signInPrompt.latestVersion },\n },\n });\n\n expect(urls.signIn).toBe(\"/custom-sign-in\");\n });\n\n it(\"throws on custom target versions newer than the latest supported version\", () => {\n const signInPrompt = getPagePrompt(\"signIn\");\n if (signInPrompt == null) {\n throw new Error(\"Expected signIn prompt metadata to exist\");\n }\n\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n signIn: { type: \"custom\", url: \"/custom-sign-in\", version: signInPrompt.latestVersion + 1 },\n },\n })).toThrowError(/Unsupported custom page version/);\n });\n\n it(\"throws on non-zero custom version for handler page\", () => {\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n handler: { type: \"custom\", url: \"/custom-handler\", version: 1 },\n },\n })).toThrowError(/cannot be a custom page/);\n });\n\n it(\"uses hosted defaults for unspecified URLs\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX\", \".example-stack-hosted.test\");\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n signUp: \"/sign-up\",\n default: { type: \"hosted\" },\n },\n });\n\n expect(urls.signUp).toBe(\"/sign-up\");\n expect(urls.signIn).toBe(\"https://project-id.example-stack-hosted.test/handler/sign-in\");\n expect(urls.cliAuthConfirm).toBe(\"https://project-id.example-stack-hosted.test/handler/cli-auth-confirm\");\n });\n\n it(\"keeps redirect-only post-auth targets local even when the default target is hosted\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX\", \".example-stack-hosted.test\");\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n default: { type: \"hosted\" },\n },\n });\n\n expect(urls.signIn).toBe(\"https://project-id.example-stack-hosted.test/handler/sign-in\");\n expect(urls.signOut).toBe(\"https://project-id.example-stack-hosted.test/handler/sign-out\");\n expect(urls.home).toBe(\"/\");\n expect(urls.afterSignIn).toBe(\"/\");\n expect(urls.afterSignUp).toBe(\"/\");\n expect(urls.afterSignOut).toBe(\"/\");\n });\n\n it(\"rejects absolute OAuth callback string targets\", () => {\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n oauthCallback: \"https://app.example.test/oauth-callback\",\n },\n })).toThrowErrorMatchingInlineSnapshot(`\n [HexclaveAssertionError: OAuth callback URLs must be relative.\n\n This is likely an error in Hexclave. Please make sure you are running the newest version and report it.]\n `);\n });\n\n it(\"rejects absolute OAuth callback custom targets\", () => {\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n oauthCallback: { type: \"custom\", url: \"https://app.example.test/oauth-callback\", version: 0 },\n },\n })).toThrowErrorMatchingInlineSnapshot(`\n [HexclaveAssertionError: OAuth callback URLs must be relative.\n\n This is likely an error in Hexclave. Please make sure you are running the newest version and report it.]\n `);\n });\n\n it(\"inherits a hosted default target for the OAuth callback\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX\", \".example-stack-hosted.test\");\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n default: { type: \"hosted\" },\n },\n });\n\n expect(urls.signIn).toBe(\"https://project-id.example-stack-hosted.test/handler/sign-in\");\n expect(urls.oauthCallback).toBe(\"https://project-id.example-stack-hosted.test/handler/oauth-callback\");\n });\n\n it(\"supports custom CLI auth confirmation targets\", () => {\n const cliAuthConfirmPrompt = getPagePrompt(\"cliAuthConfirm\");\n if (cliAuthConfirmPrompt == null) {\n throw new Error(\"Expected cliAuthConfirm prompt metadata to exist\");\n }\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n cliAuthConfirm: { type: \"custom\", url: \"/cli/authorize\", version: cliAuthConfirmPrompt.latestVersion },\n },\n });\n\n expect(urls.cliAuthConfirm).toBe(\"/cli/authorize\");\n });\n\n it(\"builds CLI auth login URLs from the resolved confirmation target\", () => {\n expect(buildCliAuthConfirmUrl({\n cliAuthConfirmUrl: \"/cli/authorize\",\n appUrl: \"https://app.example.test/base\",\n loginCode: \"login-code\",\n })).toBe(\"https://app.example.test/cli/authorize?login_code=login-code\");\n });\n\n it(\"uses default target for unknown /handler/* pages\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX\", \".example-stack-hosted.test\");\n\n const url = resolveUnknownHandlerPathFallbackUrl({\n defaultTarget: { type: \"hosted\" },\n projectId: \"project-id\",\n unknownPath: \"custom-page\",\n });\n\n expect(url).toBe(\"https://project-id.example-stack-hosted.test/handler/custom-page\");\n });\n\n it(\"uses the full hosted handler URL template when configured\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE\", \"http://{projectId}.localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09/{hostedPath}\");\n vi.stubEnv(\"NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX\", \"93\");\n\n const urls = resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n default: { type: \"hosted\" },\n },\n });\n\n expect(urls.signIn).toBe(\"http://project-id.localhost:9309/handler/sign-in\");\n expect(urls.accountSettings).toBe(\"http://project-id.localhost:9309/handler/account-settings\");\n });\n\n it(\"validates the hosted handler URL template placeholders\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE\", \"http://localhost:9309/{projectId}/handler\");\n\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n default: { type: \"hosted\" },\n },\n })).toThrowError(/\\{projectId\\} and \\{hostedPath\\}/);\n });\n\n it(\"rejects hosted handler URL templates that put the project ID in the path\", () => {\n vi.stubEnv(\"NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE\", \"http://localhost:9309/{projectId}/{hostedPath}\");\n\n expect(() => resolveHandlerUrls({\n projectId: \"project-id\",\n urls: {\n default: { type: \"hosted\" },\n },\n })).toThrowErrorMatchingInlineSnapshot(`\n [HexclaveAssertionError: The hosted handler URL template must put {projectId} in the hostname.\n\n This is likely an error in Hexclave. Please make sure you are running the newest version and report it.]\n `);\n });\n});\n\ndescribe(\"isLocalHandlerUrlTarget\", () => {\n it(\"treats relative handler URLs as local targets\", () => {\n expect(isLocalHandlerUrlTarget({\n targetUrl: \"/handler/sign-in\",\n handlerPath: \"/handler\",\n currentOrigin: \"http://p91.localhost:9101\",\n })).toBe(true);\n });\n\n it(\"treats same-origin absolute handler URLs as local targets\", () => {\n expect(isLocalHandlerUrlTarget({\n targetUrl: \"http://p91.localhost:9101/handler/sign-in\",\n handlerPath: \"/handler\",\n currentOrigin: \"http://p91.localhost:9101\",\n })).toBe(true);\n });\n\n it(\"treats cross-origin absolute handler URLs as non-local targets\", () => {\n expect(isLocalHandlerUrlTarget({\n targetUrl: \"https://project-id.built-with-hexclave.com/handler/sign-in\",\n handlerPath: \"/handler\",\n currentOrigin: \"http://p91.localhost:9101\",\n })).toBe(false);\n });\n\n it(\"treats non-handler paths as non-local targets\", () => {\n expect(isLocalHandlerUrlTarget({\n targetUrl: \"/projects\",\n handlerPath: \"/handler\",\n currentOrigin: \"http://p91.localhost:9101\",\n })).toBe(false);\n });\n});\n"],"mappings":"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"}