@hexclave/js 1.0.6 → 1.0.9
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.
- package/dist/dev-tool/dev-tool-core.js +2 -2
- package/dist/dev-tool/dev-tool-core.js.map +1 -1
- package/dist/esm/dev-tool/dev-tool-core.js +2 -2
- package/dist/esm/dev-tool/dev-tool-core.js.map +1 -1
- package/dist/esm/generated/env.d.ts +26 -0
- package/dist/esm/{lib → generated}/env.d.ts.map +1 -1
- package/dist/esm/generated/env.js +67 -0
- package/dist/esm/generated/env.js.map +1 -0
- package/dist/esm/global.d.ts +8 -0
- package/dist/esm/global.d.ts.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +81 -4
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +2 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +23 -18
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.d.ts +8 -8
- package/dist/esm/lib/hexclave-app/apps/implementations/common.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +28 -14
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/hexclave-app/url-targets.js +4 -4
- package/dist/esm/lib/hexclave-app/url-targets.js.map +1 -1
- package/dist/generated/env.d.ts +26 -0
- package/dist/{lib → generated}/env.d.ts.map +1 -1
- package/dist/generated/env.js +69 -0
- package/dist/generated/env.js.map +1 -0
- package/dist/global.d.d.ts +0 -0
- package/dist/global.d.ts +8 -0
- package/dist/global.d.ts.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +81 -4
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +2 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +23 -18
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.d.ts +8 -8
- package/dist/lib/hexclave-app/apps/implementations/common.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.js +28 -14
- package/dist/lib/hexclave-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/hexclave-app/url-targets.js +4 -4
- package/dist/lib/hexclave-app/url-targets.js.map +1 -1
- package/package.json +8 -4
- package/src/dev-tool/dev-tool-core.ts +2 -2
- package/src/generated/.gitignore +3 -0
- package/src/global.css +13 -0
- package/src/global.d.ts +12 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +105 -4
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +33 -18
- package/src/lib/hexclave-app/apps/implementations/common.ts +33 -14
- package/src/lib/hexclave-app/url-targets.ts +4 -4
- package/dist/esm/lib/env.d.ts +0 -42
- package/dist/esm/lib/env.js +0 -93
- package/dist/esm/lib/env.js.map +0 -1
- package/dist/lib/env.d.ts +0 -42
- package/dist/lib/env.js +0 -95
- package/dist/lib/env.js.map +0 -1
- package/src/lib/env.ts +0 -93
|
@@ -21,10 +21,10 @@ declare function resolveConstructorOptions<T extends {
|
|
|
21
21
|
declare function getUrls(partial: HandlerUrlOptions, options: {
|
|
22
22
|
projectId: string;
|
|
23
23
|
}): ResolvedHandlerUrls;
|
|
24
|
-
declare function getDefaultProjectId():
|
|
25
|
-
declare function getDefaultPublishableClientKey():
|
|
26
|
-
declare function getDefaultSecretServerKey():
|
|
27
|
-
declare function getDefaultSuperSecretAdminKey():
|
|
24
|
+
declare function getDefaultProjectId(): any;
|
|
25
|
+
declare function getDefaultPublishableClientKey(): any;
|
|
26
|
+
declare function getDefaultSecretServerKey(): any;
|
|
27
|
+
declare function getDefaultSuperSecretAdminKey(): any;
|
|
28
28
|
declare function getDefaultExtraRequestHeaders(): any;
|
|
29
29
|
/**
|
|
30
30
|
* Returns the base URL for the Stack API.
|
|
@@ -32,9 +32,9 @@ declare function getDefaultExtraRequestHeaders(): any;
|
|
|
32
32
|
* The URL can be specified in several ways, in order of precedence:
|
|
33
33
|
* 1. Directly through userSpecifiedBaseUrl parameter as string or browser/server object
|
|
34
34
|
* 2. Through environment variables:
|
|
35
|
-
* - Browser:
|
|
36
|
-
* - Server:
|
|
37
|
-
* -
|
|
35
|
+
* - Browser: NEXT_PUBLIC_HEXCLAVE_API_URL_BROWSER/VITE_HEXCLAVE_API_URL_BROWSER
|
|
36
|
+
* - Server: HEXCLAVE_API_URL_SERVER
|
|
37
|
+
* - Default: HEXCLAVE_API_URL
|
|
38
38
|
* 3. Default base URL if none of the above are specified
|
|
39
39
|
*
|
|
40
40
|
* The function also ensures the URL doesn't end with a trailing slash
|
|
@@ -47,7 +47,7 @@ declare function getDefaultExtraRequestHeaders(): any;
|
|
|
47
47
|
declare function getBaseUrl(userSpecifiedBaseUrl: string | {
|
|
48
48
|
browser: string;
|
|
49
49
|
server: string;
|
|
50
|
-
} | undefined):
|
|
50
|
+
} | undefined): any;
|
|
51
51
|
declare const defaultBaseUrl = "https://api.hexclave.com";
|
|
52
52
|
declare const defaultAnalyticsBaseUrl = "https://r.hexclave.com";
|
|
53
53
|
declare function getAnalyticsBaseUrl(regularBaseUrl: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","names":[],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/common.ts"],"mappings":";;;;;;;cAmBa,aAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"common.d.ts","names":[],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/common.ts"],"mappings":";;;;;;;cAmBa,aAAA;AAAA,cAgCA,WAAA,uBAAmC,OAAA,GAAU,YAAA,EAAc,CAAA,KAAM,OAAA,CAAQ,CAAA,MAAE,UAAA,CAAA,CAAA,EAAA,MAAA,CAAA,CAAA;AAAA,cAO3E,oBAAA,uBAA4C,OAAA,GAAU,OAAA,EAAS,eAAA,EAAiB,iBAAA,EAAmB,CAAA,KAAM,OAAA,CAAQ,CAAA,MAAE,UAAA,EAAA,eAAA,KAAA,CAAA,GAAA,MAAA,CAAA,CAAA;AAAA,KAa3H,OAAA;EAAA,CAAa,0BAAA;IAA+B,qBAAA;EAAA;AAAA;AAAA,iBACjC,yBAAA;EAAsC,YAAA,GAAe,OAAA;AAAA,EAAA,CAAW,OAAA,EAAS,CAAA,GAAI,CAAA;EAAM,YAAA;AAAA;AAAA,iBAOnF,OAAA,CAAQ,OAAA,EAAS,iBAAA,EAAmB,OAAA;EAAW,SAAA;AAAA,IAAsB,mBAAA;AAAA,iBAOrE,mBAAA,CAAA;AAAA,iBAIA,8BAAA,CAAA;AAAA,iBAIA,yBAAA,CAAA;AAAA,iBAIA,6BAAA,CAAA;AAAA,iBAIA,6BAAA,CAAA;;;;;;;;;;;;AA5ChB;;;;;;;iBAkEgB,UAAA,CAAW,oBAAA;EAAiC,OAAA;EAAiB,MAAA;AAAA;AAAA,cAwBhE,cAAA;AAAA,cACA,uBAAA;AAAA,iBAOG,mBAAA,CAAoB,cAAA;AAAA,iBA0BpB,cAAA,CAAe,mBAAA;EAAgC,OAAA;EAAiB,MAAA;AAAA;AAAA,KAcpE,WAAA;EACV,WAAA;EACA,YAAA;AAAA;AAAA,iBAGc,qBAAA,CAAA,GAAqB,KAAA,CAAA,WAAA"}
|
|
@@ -8,19 +8,33 @@ let _hexclave_shared_dist_utils_env = require("@hexclave/shared/dist/utils/env")
|
|
|
8
8
|
let _hexclave_shared_dist_utils_objects = require("@hexclave/shared/dist/utils/objects");
|
|
9
9
|
let ______common_js = require("../../common.js");
|
|
10
10
|
let _hexclave_shared_dist_utils_stores = require("@hexclave/shared/dist/utils/stores");
|
|
11
|
-
let
|
|
11
|
+
let ____________generated_env_js = require("../../../../generated/env.js");
|
|
12
12
|
let ______url_targets_js = require("../../url-targets.js");
|
|
13
13
|
let _hexclave_shared_dist_utils_caches = require("@hexclave/shared/dist/utils/caches");
|
|
14
14
|
let _hexclave_shared_dist_utils_globals = require("@hexclave/shared/dist/utils/globals");
|
|
15
15
|
|
|
16
16
|
//#region src/lib/hexclave-app/apps/implementations/common.ts
|
|
17
|
-
const clientVersion = "js @hexclave/js@1.0.
|
|
17
|
+
const clientVersion = "js @hexclave/js@1.0.9";
|
|
18
18
|
if (clientVersion.startsWith("STACK_COMPILE_TIME")) throw new _hexclave_shared_dist_utils_errors.HexclaveAssertionError("Client version was not replaced. Something went wrong during build!");
|
|
19
19
|
const replaceHexclavePortPrefix = (input) => {
|
|
20
20
|
if (!input) return input;
|
|
21
|
-
const prefix =
|
|
21
|
+
const prefix = ____________generated_env_js.envVars.HEXCLAVE_PORT_PREFIX;
|
|
22
22
|
return prefix ? input.replace(/\$\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\}/g, prefix) : input;
|
|
23
23
|
};
|
|
24
|
+
const showMissingConfigAlertInBrowser = (message) => {
|
|
25
|
+
if (!(0, _hexclave_shared_dist_utils_env.isBrowserLike)()) return;
|
|
26
|
+
const global = globalThis;
|
|
27
|
+
const alertAlreadyShownKey = "__hexclave_missing_config_alert_already_shown";
|
|
28
|
+
if (global[alertAlreadyShownKey]) return;
|
|
29
|
+
global[alertAlreadyShownKey] = true;
|
|
30
|
+
const alertFn = global.alert;
|
|
31
|
+
if (typeof alertFn === "function") alertFn(message);
|
|
32
|
+
};
|
|
33
|
+
const throwMissingProjectIdError = () => {
|
|
34
|
+
const message = "Welcome to Hexclave! It seems that you haven't provided a project ID. Please create a project on the Hexclave dashboard at https://app.hexclave.com and put it in the HEXCLAVE_PROJECT_ID environment variable.";
|
|
35
|
+
showMissingConfigAlertInBrowser(message);
|
|
36
|
+
return (0, _hexclave_shared_dist_utils_errors.throwErr)(new Error(message));
|
|
37
|
+
};
|
|
24
38
|
const createCache = (fetcher) => {
|
|
25
39
|
return new _hexclave_shared_dist_utils_caches.AsyncCache(async (dependencies) => await _hexclave_shared_dist_utils_results.Result.fromThrowingAsync(async () => await fetcher(dependencies)), {});
|
|
26
40
|
};
|
|
@@ -43,19 +57,19 @@ function getUrls(partial, options) {
|
|
|
43
57
|
});
|
|
44
58
|
}
|
|
45
59
|
function getDefaultProjectId() {
|
|
46
|
-
return
|
|
60
|
+
return ____________generated_env_js.envVars.HEXCLAVE_PROJECT_ID || throwMissingProjectIdError();
|
|
47
61
|
}
|
|
48
62
|
function getDefaultPublishableClientKey() {
|
|
49
|
-
return
|
|
63
|
+
return ____________generated_env_js.envVars.HEXCLAVE_PUBLISHABLE_CLIENT_KEY;
|
|
50
64
|
}
|
|
51
65
|
function getDefaultSecretServerKey() {
|
|
52
|
-
return
|
|
66
|
+
return ____________generated_env_js.envVars.HEXCLAVE_SECRET_SERVER_KEY || (0, _hexclave_shared_dist_utils_errors.throwErr)(/* @__PURE__ */ new Error("No secret server key provided. Please copy your key from the Hexclave dashboard and put it in the HEXCLAVE_SECRET_SERVER_KEY environment variable."));
|
|
53
67
|
}
|
|
54
68
|
function getDefaultSuperSecretAdminKey() {
|
|
55
|
-
return
|
|
69
|
+
return ____________generated_env_js.envVars.HEXCLAVE_SUPER_SECRET_ADMIN_KEY || (0, _hexclave_shared_dist_utils_errors.throwErr)(/* @__PURE__ */ new Error("No super secret admin key provided. Please copy your key from the Hexclave dashboard and put it in the HEXCLAVE_SUPER_SECRET_ADMIN_KEY environment variable."));
|
|
56
70
|
}
|
|
57
71
|
function getDefaultExtraRequestHeaders() {
|
|
58
|
-
return JSON.parse(
|
|
72
|
+
return JSON.parse(____________generated_env_js.envVars.HEXCLAVE_EXTRA_REQUEST_HEADERS || "{}");
|
|
59
73
|
}
|
|
60
74
|
/**
|
|
61
75
|
* Returns the base URL for the Stack API.
|
|
@@ -63,9 +77,9 @@ function getDefaultExtraRequestHeaders() {
|
|
|
63
77
|
* The URL can be specified in several ways, in order of precedence:
|
|
64
78
|
* 1. Directly through userSpecifiedBaseUrl parameter as string or browser/server object
|
|
65
79
|
* 2. Through environment variables:
|
|
66
|
-
* - Browser:
|
|
67
|
-
* - Server:
|
|
68
|
-
* -
|
|
80
|
+
* - Browser: NEXT_PUBLIC_HEXCLAVE_API_URL_BROWSER/VITE_HEXCLAVE_API_URL_BROWSER
|
|
81
|
+
* - Server: HEXCLAVE_API_URL_SERVER
|
|
82
|
+
* - Default: HEXCLAVE_API_URL
|
|
69
83
|
* 3. Default base URL if none of the above are specified
|
|
70
84
|
*
|
|
71
85
|
* The function also ensures the URL doesn't end with a trailing slash
|
|
@@ -81,9 +95,9 @@ function getBaseUrl(userSpecifiedBaseUrl) {
|
|
|
81
95
|
else if ((0, _hexclave_shared_dist_utils_env.isBrowserLike)()) url = userSpecifiedBaseUrl.browser;
|
|
82
96
|
else url = userSpecifiedBaseUrl.server;
|
|
83
97
|
else {
|
|
84
|
-
if ((0, _hexclave_shared_dist_utils_env.isBrowserLike)()) url =
|
|
85
|
-
else url =
|
|
86
|
-
url = url ||
|
|
98
|
+
if ((0, _hexclave_shared_dist_utils_env.isBrowserLike)()) url = ____________generated_env_js.envVars.HEXCLAVE_API_URL_BROWSER;
|
|
99
|
+
else url = ____________generated_env_js.envVars.HEXCLAVE_API_URL_SERVER;
|
|
100
|
+
url = url || ____________generated_env_js.envVars.HEXCLAVE_API_URL || defaultBaseUrl;
|
|
87
101
|
}
|
|
88
102
|
return replaceHexclavePortPrefix(url.endsWith("/") ? url.slice(0, -1) : url);
|
|
89
103
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.js","names":[],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/common.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 { InternalSession } from \"@hexclave/shared/dist/sessions\";\nimport { AsyncCache } from \"@hexclave/shared/dist/utils/caches\";\nimport { isBrowserLike } from \"@hexclave/shared/dist/utils/env\";\nimport { HexclaveAssertionError, captureError, concatStacktraces, throwErr } from \"@hexclave/shared/dist/utils/errors\";\nimport { createGlobal, getGlobal } from \"@hexclave/shared/dist/utils/globals\";\nimport { filterUndefined, omit } from \"@hexclave/shared/dist/utils/objects\";\nimport { ReactPromise, runAsynchronously } from \"@hexclave/shared/dist/utils/promises\";\nimport { suspendIfSsr, use } from \"@hexclave/shared/dist/utils/react\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { Store } from \"@hexclave/shared/dist/utils/stores\";\nimport { getDefaultApiUrls } from \"@hexclave/shared/dist/utils/urls\";\nimport { envVars } from \"../../../env\";\nimport { HandlerUrlOptions, ResolvedHandlerUrls, hexclaveAppInternalsSymbol } from \"../../common\";\nimport { resolveHandlerUrls } from \"../../url-targets\";\n\nexport const clientVersion = \"STACK_COMPILE_TIME_CLIENT_PACKAGE_VERSION_SENTINEL\";\nif (clientVersion.startsWith(\"STACK_COMPILE_TIME\")) {\n throw new HexclaveAssertionError(\"Client version was not replaced. Something went wrong during build!\");\n}\n\nconst replaceHexclavePortPrefix = <T extends string | undefined>(input: T): T => {\n if (!input) return input;\n const prefix = envVars.NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX;\n return prefix ? input.replace(/\\$\\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\\}/g, prefix) as T : input;\n};\n\n\nexport const createCache = <D extends any[], T>(fetcher: (dependencies: D) => Promise<T>) => {\n return new AsyncCache<D, Result<T>>(\n async (dependencies) => await Result.fromThrowingAsync(async () => await fetcher(dependencies)),\n {},\n );\n};\n\nexport const createCacheBySession = <D extends any[], T>(fetcher: (session: InternalSession, extraDependencies: D) => Promise<T> ) => {\n return new AsyncCache<[InternalSession, ...D], Result<T>>(\n async ([session, ...extraDependencies]) => await Result.fromThrowingAsync(async () => await fetcher(session, extraDependencies)),\n {\n onSubscribe: ([session], refresh) => {\n const handler = session.onInvalidate(() => refresh());\n return () => handler.unsubscribe();\n },\n },\n );\n};\n\n\ntype AppLike = { [hexclaveAppInternalsSymbol]: { getConstructorOptions: () => any } };\nexport function resolveConstructorOptions<T extends { inheritsFrom?: AppLike }>(options: T): T & { inheritsFrom?: undefined } {\n return {\n ...options.inheritsFrom?.[hexclaveAppInternalsSymbol].getConstructorOptions() ?? {},\n ...filterUndefined(omit(options, [\"inheritsFrom\"])),\n };\n}\n\nexport function getUrls(partial: HandlerUrlOptions, options: { projectId: string }): ResolvedHandlerUrls {\n return resolveHandlerUrls({\n urls: partial,\n projectId: options.projectId,\n });\n}\n\nexport function getDefaultProjectId() {\n return envVars.NEXT_PUBLIC_STACK_PROJECT_ID || envVars.STACK_PROJECT_ID || throwErr(new Error(\"Welcome to Hexclave! It seems that you haven't provided a project ID. Please create a project on the Hexclave dashboard at https://app.hexclave.com and put it in the NEXT_PUBLIC_STACK_PROJECT_ID environment variable.\"));\n}\n\nexport function getDefaultPublishableClientKey() {\n return envVars.NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY || envVars.STACK_PUBLISHABLE_CLIENT_KEY;\n}\n\nexport function getDefaultSecretServerKey() {\n return envVars.STACK_SECRET_SERVER_KEY || throwErr(new Error(\"No secret server key provided. Please copy your key from the Hexclave dashboard and put it in the STACK_SECRET_SERVER_KEY environment variable.\"));\n}\n\nexport function getDefaultSuperSecretAdminKey() {\n return envVars.STACK_SUPER_SECRET_ADMIN_KEY || throwErr(new Error(\"No super secret admin key provided. Please copy your key from the Hexclave dashboard and put it in the STACK_SUPER_SECRET_ADMIN_KEY environment variable.\"));\n}\n\nexport function getDefaultExtraRequestHeaders() {\n return JSON.parse(envVars.NEXT_PUBLIC_STACK_EXTRA_REQUEST_HEADERS || envVars.STACK_EXTRA_REQUEST_HEADERS || '{}');\n}\n\n/**\n * Returns the base URL for the Stack API.\n *\n * The URL can be specified in several ways, in order of precedence:\n * 1. Directly through userSpecifiedBaseUrl parameter as string or browser/server object\n * 2. Through environment variables:\n * - Browser: NEXT_PUBLIC_BROWSER_STACK_API_URL\n * - Server: NEXT_PUBLIC_SERVER_STACK_API_URL\n * - Fallback: NEXT_PUBLIC_STACK_API_URL or NEXT_PUBLIC_STACK_URL\n * 3. Default base URL if none of the above are specified\n *\n * The function also ensures the URL doesn't end with a trailing slash\n * by removing it if present.\n *\n * @param userSpecifiedBaseUrl - Optional URL override as string or {browser, server} object\n * @returns The configured base URL without trailing slash\n\n */\nexport function getBaseUrl(userSpecifiedBaseUrl: string | { browser: string, server: string } | undefined) {\n let url;\n if (userSpecifiedBaseUrl) {\n if (typeof userSpecifiedBaseUrl === \"string\") {\n url = userSpecifiedBaseUrl;\n } else {\n if (isBrowserLike()) {\n url = userSpecifiedBaseUrl.browser;\n } else {\n url = userSpecifiedBaseUrl.server;\n }\n }\n } else {\n // note: NEXT_PUBLIC_BROWSER_STACK_API_URL was renamed to NEXT_PUBLIC_STACK_API_URL_BROWSER, and NEXT_PUBLIC_STACK_URL to NEXT_PUBLIC_STACK_API_URL\n if (isBrowserLike()) {\n url = envVars.NEXT_PUBLIC_BROWSER_STACK_API_URL || envVars.NEXT_PUBLIC_STACK_API_URL_BROWSER || envVars.STACK_API_URL_BROWSER;\n } else {\n url = envVars.NEXT_PUBLIC_SERVER_STACK_API_URL || envVars.NEXT_PUBLIC_STACK_API_URL_SERVER || envVars.STACK_API_URL_SERVER;\n }\n url = url || envVars.NEXT_PUBLIC_STACK_API_URL || envVars.STACK_API_URL || envVars.NEXT_PUBLIC_STACK_URL || defaultBaseUrl;\n }\n\n return replaceHexclavePortPrefix(url.endsWith('/') ? url.slice(0, -1) : url);\n}\n\nexport const defaultBaseUrl = \"https://api.hexclave.com\";\nexport const defaultAnalyticsBaseUrl = \"https://r.hexclave.com\";\n\nconst analyticsBaseUrlsByApiBaseUrl = new Map<string, string>([\n [defaultBaseUrl, defaultAnalyticsBaseUrl],\n [\"https://api.stack-auth.com\", \"https://r.stack-auth.com\"],\n]);\n\nexport function getAnalyticsBaseUrl(regularBaseUrl: string): string {\n return analyticsBaseUrlsByApiBaseUrl.get(regularBaseUrl) ?? regularBaseUrl;\n}\n\n\nfunction fetchBackendUrlsInBackground(primaryBaseUrl: string): void {\n createGlobal('__stack-fetch-backend-urls-started', () => {\n runAsynchronously(async () => {\n try {\n const res = await fetch(`${primaryBaseUrl}/api/v1/internal/backend-urls`);\n if (!res.ok) {\n return;\n }\n const data = await res.json();\n if (!Array.isArray(data.urls) || !data.urls.every((u: unknown) => typeof u === 'string')) {\n return;\n }\n createGlobal('__stack-fetched-backend-urls', () => data.urls as string[]);\n } catch (e) {\n captureError('fetch-backend-urls-in-background', e);\n }\n });\n return true;\n });\n}\n\nexport function resolveApiUrls(userExplicitBaseUrl: string | { browser: string, server: string } | undefined): () => string[] {\n return () => {\n if (userExplicitBaseUrl != null) {\n return [getBaseUrl(userExplicitBaseUrl)];\n }\n const primary = getBaseUrl(undefined);\n // Always try to fetch server-configured URLs (supports custom domains via\n // STACK_BACKEND_URLS_CONFIG). Hardcoded fallbacks are used as a default\n // until the background fetch completes.\n fetchBackendUrlsInBackground(primary);\n return getGlobal('__stack-fetched-backend-urls') ?? getDefaultApiUrls(primary);\n };\n}\n\nexport type TokenObject = {\n accessToken: string | null,\n refreshToken: string | null,\n};\n\nexport function createEmptyTokenStore() {\n return new Store<TokenObject>({\n refreshToken: null,\n accessToken: null,\n });\n}\n\n\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"common.js","names":[],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/common.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 { InternalSession } from \"@hexclave/shared/dist/sessions\";\nimport { AsyncCache } from \"@hexclave/shared/dist/utils/caches\";\nimport { isBrowserLike } from \"@hexclave/shared/dist/utils/env\";\nimport { HexclaveAssertionError, captureError, concatStacktraces, throwErr } from \"@hexclave/shared/dist/utils/errors\";\nimport { createGlobal, getGlobal } from \"@hexclave/shared/dist/utils/globals\";\nimport { filterUndefined, omit } from \"@hexclave/shared/dist/utils/objects\";\nimport { ReactPromise, runAsynchronously } from \"@hexclave/shared/dist/utils/promises\";\nimport { suspendIfSsr, use } from \"@hexclave/shared/dist/utils/react\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { Store } from \"@hexclave/shared/dist/utils/stores\";\nimport { getDefaultApiUrls } from \"@hexclave/shared/dist/utils/urls\";\nimport { envVars } from \"../../../../generated/env\";\nimport { HandlerUrlOptions, ResolvedHandlerUrls, hexclaveAppInternalsSymbol } from \"../../common\";\nimport { resolveHandlerUrls } from \"../../url-targets\";\n\nexport const clientVersion = \"STACK_COMPILE_TIME_CLIENT_PACKAGE_VERSION_SENTINEL\";\nif (clientVersion.startsWith(\"STACK_COMPILE_TIME\")) {\n throw new HexclaveAssertionError(\"Client version was not replaced. Something went wrong during build!\");\n}\n\nconst replaceHexclavePortPrefix = <T extends string | undefined>(input: T): T => {\n if (!input) return input;\n const prefix = envVars.HEXCLAVE_PORT_PREFIX;\n return prefix ? input.replace(/\\$\\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\\}/g, prefix) as T : input;\n};\n\nconst showMissingConfigAlertInBrowser = (message: string) => {\n if (!isBrowserLike()) return;\n\n const global = globalThis as any;\n const alertAlreadyShownKey = \"__hexclave_missing_config_alert_already_shown\";\n if (global[alertAlreadyShownKey]) return;\n global[alertAlreadyShownKey] = true;\n\n const alertFn = global.alert;\n if (typeof alertFn === \"function\") {\n alertFn(message);\n }\n};\n\nconst throwMissingProjectIdError = (): never => {\n const message = \"Welcome to Hexclave! It seems that you haven't provided a project ID. Please create a project on the Hexclave dashboard at https://app.hexclave.com and put it in the HEXCLAVE_PROJECT_ID environment variable.\";\n showMissingConfigAlertInBrowser(message);\n return throwErr(new Error(message));\n};\n\n\nexport const createCache = <D extends any[], T>(fetcher: (dependencies: D) => Promise<T>) => {\n return new AsyncCache<D, Result<T>>(\n async (dependencies) => await Result.fromThrowingAsync(async () => await fetcher(dependencies)),\n {},\n );\n};\n\nexport const createCacheBySession = <D extends any[], T>(fetcher: (session: InternalSession, extraDependencies: D) => Promise<T> ) => {\n return new AsyncCache<[InternalSession, ...D], Result<T>>(\n async ([session, ...extraDependencies]) => await Result.fromThrowingAsync(async () => await fetcher(session, extraDependencies)),\n {\n onSubscribe: ([session], refresh) => {\n const handler = session.onInvalidate(() => refresh());\n return () => handler.unsubscribe();\n },\n },\n );\n};\n\n\ntype AppLike = { [hexclaveAppInternalsSymbol]: { getConstructorOptions: () => any } };\nexport function resolveConstructorOptions<T extends { inheritsFrom?: AppLike }>(options: T): T & { inheritsFrom?: undefined } {\n return {\n ...options.inheritsFrom?.[hexclaveAppInternalsSymbol].getConstructorOptions() ?? {},\n ...filterUndefined(omit(options, [\"inheritsFrom\"])),\n };\n}\n\nexport function getUrls(partial: HandlerUrlOptions, options: { projectId: string }): ResolvedHandlerUrls {\n return resolveHandlerUrls({\n urls: partial,\n projectId: options.projectId,\n });\n}\n\nexport function getDefaultProjectId() {\n return envVars.HEXCLAVE_PROJECT_ID || throwMissingProjectIdError();\n}\n\nexport function getDefaultPublishableClientKey() {\n return envVars.HEXCLAVE_PUBLISHABLE_CLIENT_KEY;\n}\n\nexport function getDefaultSecretServerKey() {\n return envVars.HEXCLAVE_SECRET_SERVER_KEY || throwErr(new Error(\"No secret server key provided. Please copy your key from the Hexclave dashboard and put it in the HEXCLAVE_SECRET_SERVER_KEY environment variable.\"));\n}\n\nexport function getDefaultSuperSecretAdminKey() {\n return envVars.HEXCLAVE_SUPER_SECRET_ADMIN_KEY || throwErr(new Error(\"No super secret admin key provided. Please copy your key from the Hexclave dashboard and put it in the HEXCLAVE_SUPER_SECRET_ADMIN_KEY environment variable.\"));\n}\n\nexport function getDefaultExtraRequestHeaders() {\n return JSON.parse(envVars.HEXCLAVE_EXTRA_REQUEST_HEADERS || '{}');\n}\n\n/**\n * Returns the base URL for the Stack API.\n *\n * The URL can be specified in several ways, in order of precedence:\n * 1. Directly through userSpecifiedBaseUrl parameter as string or browser/server object\n * 2. Through environment variables:\n * - Browser: NEXT_PUBLIC_HEXCLAVE_API_URL_BROWSER/VITE_HEXCLAVE_API_URL_BROWSER\n * - Server: HEXCLAVE_API_URL_SERVER\n * - Default: HEXCLAVE_API_URL\n * 3. Default base URL if none of the above are specified\n *\n * The function also ensures the URL doesn't end with a trailing slash\n * by removing it if present.\n *\n * @param userSpecifiedBaseUrl - Optional URL override as string or {browser, server} object\n * @returns The configured base URL without trailing slash\n\n */\nexport function getBaseUrl(userSpecifiedBaseUrl: string | { browser: string, server: string } | undefined) {\n let url;\n if (userSpecifiedBaseUrl) {\n if (typeof userSpecifiedBaseUrl === \"string\") {\n url = userSpecifiedBaseUrl;\n } else {\n if (isBrowserLike()) {\n url = userSpecifiedBaseUrl.browser;\n } else {\n url = userSpecifiedBaseUrl.server;\n }\n }\n } else {\n if (isBrowserLike()) {\n url = envVars.HEXCLAVE_API_URL_BROWSER;\n } else {\n url = envVars.HEXCLAVE_API_URL_SERVER;\n }\n url = url || envVars.HEXCLAVE_API_URL || defaultBaseUrl;\n }\n\n return replaceHexclavePortPrefix(url.endsWith('/') ? url.slice(0, -1) : url);\n}\n\nexport const defaultBaseUrl = \"https://api.hexclave.com\";\nexport const defaultAnalyticsBaseUrl = \"https://r.hexclave.com\";\n\nconst analyticsBaseUrlsByApiBaseUrl = new Map<string, string>([\n [defaultBaseUrl, defaultAnalyticsBaseUrl],\n [\"https://api.stack-auth.com\", \"https://r.stack-auth.com\"],\n]);\n\nexport function getAnalyticsBaseUrl(regularBaseUrl: string): string {\n return analyticsBaseUrlsByApiBaseUrl.get(regularBaseUrl) ?? regularBaseUrl;\n}\n\n\nfunction fetchBackendUrlsInBackground(primaryBaseUrl: string): void {\n createGlobal('__stack-fetch-backend-urls-started', () => {\n runAsynchronously(async () => {\n try {\n const res = await fetch(`${primaryBaseUrl}/api/v1/internal/backend-urls`);\n if (!res.ok) {\n return;\n }\n const data = await res.json();\n if (!Array.isArray(data.urls) || !data.urls.every((u: unknown) => typeof u === 'string')) {\n return;\n }\n createGlobal('__stack-fetched-backend-urls', () => data.urls as string[]);\n } catch (e) {\n captureError('fetch-backend-urls-in-background', e);\n }\n });\n return true;\n });\n}\n\nexport function resolveApiUrls(userExplicitBaseUrl: string | { browser: string, server: string } | undefined): () => string[] {\n return () => {\n if (userExplicitBaseUrl != null) {\n return [getBaseUrl(userExplicitBaseUrl)];\n }\n const primary = getBaseUrl(undefined);\n // Always try to fetch server-configured URLs (supports custom domains via\n // STACK_BACKEND_URLS_CONFIG). Hardcoded fallbacks are used as a default\n // until the background fetch completes.\n fetchBackendUrlsInBackground(primary);\n return getGlobal('__stack-fetched-backend-urls') ?? getDefaultApiUrls(primary);\n };\n}\n\nexport type TokenObject = {\n accessToken: string | null,\n refreshToken: string | null,\n};\n\nexport function createEmptyTokenStore() {\n return new Store<TokenObject>({\n refreshToken: null,\n accessToken: null,\n });\n}\n\n\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ const require_chunk = require('../../chunk-BE-pF4vm.js');
|
|
|
3
3
|
let _hexclave_shared_dist_utils_errors = require("@hexclave/shared/dist/utils/errors");
|
|
4
4
|
let _hexclave_shared_dist_interface_handler_urls = require("@hexclave/shared/dist/interface/handler-urls");
|
|
5
5
|
let _hexclave_shared_dist_utils_redirect_urls = require("@hexclave/shared/dist/utils/redirect-urls");
|
|
6
|
-
let
|
|
6
|
+
let ______generated_env_js = require("../../generated/env.js");
|
|
7
7
|
|
|
8
8
|
//#region src/lib/hexclave-app/url-targets.ts
|
|
9
9
|
const localUrlPlaceholderOrigin = "http://example.com";
|
|
@@ -52,9 +52,9 @@ const getHostedHandlerUrl = (options) => {
|
|
|
52
52
|
return (0, _hexclave_shared_dist_utils_redirect_urls.getHostedHandlerUrlFromConfig)({
|
|
53
53
|
projectId: options.projectId,
|
|
54
54
|
hostedPath,
|
|
55
|
-
hostedHandlerDomainSuffix:
|
|
56
|
-
hostedHandlerUrlTemplate:
|
|
57
|
-
hexclavePortPrefix:
|
|
55
|
+
hostedHandlerDomainSuffix: ______generated_env_js.envVars.HEXCLAVE_HOSTED_HANDLER_DOMAIN_SUFFIX,
|
|
56
|
+
hostedHandlerUrlTemplate: ______generated_env_js.envVars.HEXCLAVE_HOSTED_HANDLER_URL_TEMPLATE,
|
|
57
|
+
hexclavePortPrefix: ______generated_env_js.envVars.HEXCLAVE_PORT_PREFIX
|
|
58
58
|
});
|
|
59
59
|
};
|
|
60
60
|
const isRelativeUrlString = (url) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-targets.js","names":["envVars","HexclaveAssertionError"],"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 \"../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.NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX,\n hostedHandlerUrlTemplate: envVars.NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE,\n hexclavePortPrefix: envVars.NEXT_PUBLIC_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":["envVars","HexclaveAssertionError"],"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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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
|
3
3
|
"name": "@hexclave/js",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.9",
|
|
5
5
|
"repository": "https://github.com/hexclave/hexclave",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"rrweb": "^1.1.3",
|
|
58
58
|
"tsx": "^4.21.0",
|
|
59
59
|
"yup": "^1.7.1",
|
|
60
|
-
"@hexclave/shared": "1.0.
|
|
60
|
+
"@hexclave/shared": "1.0.9"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@quetzallabs/i18n": "^0.1.19",
|
|
@@ -83,7 +83,11 @@
|
|
|
83
83
|
"typecheck": "tsc --noEmit",
|
|
84
84
|
"clean": "rimraf dist && rimraf node_modules",
|
|
85
85
|
"lint": "eslint --ext .tsx,.ts .",
|
|
86
|
-
"
|
|
87
|
-
"
|
|
86
|
+
"codegen": "pnpm run env",
|
|
87
|
+
"codegen:watch": "concurrently -n \"env\" -k \"pnpm run env:watch\"",
|
|
88
|
+
"build": "rimraf dist && pnpm run codegen && tsdown",
|
|
89
|
+
"dev": "concurrently -n \"build,codegen\" -k \"tsdown --watch\" \"pnpm run codegen:watch\"",
|
|
90
|
+
"env": "tsx ./scripts/generate-env.ts",
|
|
91
|
+
"env:watch": "chokidar --silent './scripts/generate-env.ts' -c 'pnpm run env' --throttle 2000"
|
|
88
92
|
}
|
|
89
93
|
}
|
|
@@ -7,7 +7,7 @@ import type { RequestLogEntry } from "@hexclave/shared/dist/interface/client-int
|
|
|
7
7
|
import { runAsynchronously } from "@hexclave/shared/dist/utils/promises";
|
|
8
8
|
import { isLocalhost } from "@hexclave/shared/dist/utils/urls";
|
|
9
9
|
import type { StackClientApp } from "../lib/hexclave-app";
|
|
10
|
-
import { envVars } from "../
|
|
10
|
+
import { envVars } from "../generated/env";
|
|
11
11
|
import { getBaseUrl } from "../lib/hexclave-app/apps/implementations/common";
|
|
12
12
|
import type { HandlerUrlOptions, HandlerUrls, HandlerUrlTarget } from "../lib/hexclave-app/common";
|
|
13
13
|
import { hexclaveAppInternalsSymbol } from "../lib/hexclave-app/common";
|
|
@@ -211,7 +211,7 @@ function resolveApiBaseUrl(app: StackClientApp<true>): string {
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
function shouldShowDashboardTab(app: StackClientApp<true>): boolean {
|
|
214
|
-
return envVars.
|
|
214
|
+
return envVars.HEXCLAVE_IS_LOCAL_EMULATOR === "true" && isLocalhost(resolveApiBaseUrl(app));
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
function getTabsForApp(app: StackClientApp<true>): { id: TabId; label: string; icon: string }[] {
|
package/src/global.css
ADDED
package/src/global.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
//===========================================
|
|
3
|
+
// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template
|
|
4
|
+
//===========================================
|
|
5
|
+
import type { } from "react/canary";
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
|
9
|
+
interface ImportMeta {
|
|
10
|
+
readonly env?: Record<string, string | undefined>,
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -54,12 +54,13 @@ function createMockDocument(): Document {
|
|
|
54
54
|
|
|
55
55
|
describe("StackClientApp cross-domain auth", () => {
|
|
56
56
|
it("uses the fresh post-auth refresh token when minting a cross-domain handoff", async () => {
|
|
57
|
+
const freshAccessToken = createAccessTokenString("fresh-refresh-token-id");
|
|
57
58
|
const clientApp = new StackClientApp({
|
|
58
59
|
baseUrl: "http://localhost:12345",
|
|
59
60
|
projectId: "00000000-0000-4000-8000-000000000000",
|
|
60
61
|
publishableClientKey: "stack-pk-test",
|
|
61
62
|
tokenStore: {
|
|
62
|
-
accessToken: "stale-
|
|
63
|
+
accessToken: createAccessTokenString("stale-refresh-token-id"),
|
|
63
64
|
refreshToken: "stale-refresh-token",
|
|
64
65
|
},
|
|
65
66
|
redirectMethod: "none",
|
|
@@ -68,24 +69,43 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
68
69
|
|
|
69
70
|
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
70
71
|
const originalSendClientRequest = Reflect.get(clientInterface, "sendClientRequest");
|
|
72
|
+
const originalFetchNewAccessToken = Reflect.get(clientInterface, "fetchNewAccessToken");
|
|
71
73
|
const capturedRefreshTokens: string[] = [];
|
|
74
|
+
const capturedAccessTokenRefreshTokenIds: string[] = [];
|
|
75
|
+
const refreshedRawRefreshTokens: string[] = [];
|
|
72
76
|
|
|
73
77
|
Reflect.set(clientInterface, "sendClientRequest", async (_path: unknown, _requestOptions: unknown, session: unknown) => {
|
|
74
78
|
const getRefreshToken = Reflect.get(session ?? {}, "getRefreshToken");
|
|
79
|
+
const getOrFetchLikelyValidTokens = Reflect.get(session ?? {}, "getOrFetchLikelyValidTokens");
|
|
75
80
|
if (typeof getRefreshToken !== "function") {
|
|
76
81
|
throw new Error("Expected cross-domain auth to pass a session to the client interface.");
|
|
77
82
|
}
|
|
83
|
+
if (typeof getOrFetchLikelyValidTokens !== "function") {
|
|
84
|
+
throw new Error("Expected cross-domain auth to pass a session with token accessors.");
|
|
85
|
+
}
|
|
78
86
|
const refreshToken = getRefreshToken.call(session);
|
|
79
87
|
const refreshTokenString = Reflect.get(refreshToken ?? {}, "token");
|
|
80
88
|
if (typeof refreshTokenString !== "string") {
|
|
81
89
|
throw new Error("Expected cross-domain auth to pass a refresh-token-backed session.");
|
|
82
90
|
}
|
|
83
91
|
capturedRefreshTokens.push(refreshTokenString);
|
|
92
|
+
const tokens = await getOrFetchLikelyValidTokens.call(session, 0, null);
|
|
93
|
+
capturedAccessTokenRefreshTokenIds.push(tokens.accessToken.payload.refresh_token_id);
|
|
84
94
|
return {
|
|
85
95
|
ok: true,
|
|
86
96
|
json: async () => ({ redirect_url: "https://example.com/handler/oauth-callback?code=handoff-code&state=handoff-state" }),
|
|
87
97
|
};
|
|
88
98
|
});
|
|
99
|
+
Reflect.set(clientInterface, "fetchNewAccessToken", async (refreshToken: unknown) => {
|
|
100
|
+
const refreshTokenString = Reflect.get(refreshToken ?? {}, "token");
|
|
101
|
+
if (typeof refreshTokenString !== "string") {
|
|
102
|
+
throw new Error("Expected refresh token while fetching a new access token.");
|
|
103
|
+
}
|
|
104
|
+
refreshedRawRefreshTokens.push(refreshTokenString);
|
|
105
|
+
return AccessToken.createIfValid(freshAccessToken) ?? (() => {
|
|
106
|
+
throw new Error("Expected test access token to be valid");
|
|
107
|
+
})();
|
|
108
|
+
});
|
|
89
109
|
|
|
90
110
|
try {
|
|
91
111
|
const createCrossDomainAuthRedirectUrl = Reflect.get(clientApp, "_createCrossDomainAuthRedirectUrl");
|
|
@@ -99,15 +119,18 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
99
119
|
codeChallenge: "abcdefghijklmnopqrstuvwxyzABCDEFG_0123456789-._~",
|
|
100
120
|
afterCallbackRedirectUrl: "https://example.com/account-settings",
|
|
101
121
|
overrideTokenStoreInit: {
|
|
102
|
-
accessToken: "fresh-
|
|
122
|
+
accessToken: createAccessTokenString("fresh-stale-refresh-token-id"),
|
|
103
123
|
refreshToken: "fresh-refresh-token",
|
|
104
124
|
},
|
|
105
125
|
})).resolves.toBe("https://example.com/handler/oauth-callback?code=handoff-code&state=handoff-state");
|
|
106
126
|
} finally {
|
|
107
127
|
Reflect.set(clientInterface, "sendClientRequest", originalSendClientRequest);
|
|
128
|
+
Reflect.set(clientInterface, "fetchNewAccessToken", originalFetchNewAccessToken);
|
|
108
129
|
}
|
|
109
130
|
|
|
131
|
+
expect(refreshedRawRefreshTokens).toEqual(["fresh-refresh-token"]);
|
|
110
132
|
expect(capturedRefreshTokens).toEqual(["fresh-refresh-token"]);
|
|
133
|
+
expect(capturedAccessTokenRefreshTokenIds).toEqual(["fresh-refresh-token-id"]);
|
|
111
134
|
});
|
|
112
135
|
|
|
113
136
|
it("uses a fresh nested OAuth state while preserving the outer cross-domain return state", async () => {
|
|
@@ -136,7 +159,7 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
136
159
|
const previousWindow = globalThis.window;
|
|
137
160
|
const previousDocument = globalThis.document;
|
|
138
161
|
let redirectedUrl = "";
|
|
139
|
-
vi.spyOn(clientApp as any, "
|
|
162
|
+
vi.spyOn(clientApp as any, "_fetchCurrentRefreshTokenIdIfSignedIn").mockResolvedValue(null);
|
|
140
163
|
vi.spyOn(clientApp as any, "_getCrossDomainHandoffParamsForRedirect").mockResolvedValue({
|
|
141
164
|
state: "fresh-nested-state",
|
|
142
165
|
codeChallenge: "fresh-nested-code-challenge",
|
|
@@ -203,6 +226,13 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
203
226
|
const previousWindow = globalThis.window;
|
|
204
227
|
const previousDocument = globalThis.document;
|
|
205
228
|
let redirectedUrl = "";
|
|
229
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
230
|
+
const originalFetchNewAccessToken = Reflect.get(clientInterface, "fetchNewAccessToken");
|
|
231
|
+
Reflect.set(clientInterface, "fetchNewAccessToken", async () => {
|
|
232
|
+
return AccessToken.createIfValid(hostedAccessToken) ?? (() => {
|
|
233
|
+
throw new Error("Expected test access token to be valid");
|
|
234
|
+
})();
|
|
235
|
+
});
|
|
206
236
|
vi.spyOn(clientApp as any, "_isTrusted").mockResolvedValue(true);
|
|
207
237
|
|
|
208
238
|
globalThis.document = createMockDocument();
|
|
@@ -219,6 +249,7 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
219
249
|
try {
|
|
220
250
|
await expect((clientApp as any)._maybeHandleNestedCrossDomainAuth()).rejects.toThrowError("INTENTIONAL_TEST_ABORT");
|
|
221
251
|
} finally {
|
|
252
|
+
Reflect.set(clientInterface, "fetchNewAccessToken", originalFetchNewAccessToken);
|
|
222
253
|
globalThis.window = previousWindow;
|
|
223
254
|
globalThis.document = previousDocument;
|
|
224
255
|
}
|
|
@@ -230,6 +261,74 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
230
261
|
expect(new URL(redirectedUrl).origin).toBe("https://demo.stack-auth.com");
|
|
231
262
|
});
|
|
232
263
|
|
|
264
|
+
it("uses the latest browser refresh cookie before computing nested cross-domain session IDs", async () => {
|
|
265
|
+
const projectId = "00000000-0000-4000-8000-000000000007";
|
|
266
|
+
const previousWindow = globalThis.window;
|
|
267
|
+
const previousDocument = globalThis.document;
|
|
268
|
+
|
|
269
|
+
globalThis.document = createMockDocument();
|
|
270
|
+
globalThis.window = {
|
|
271
|
+
location: {
|
|
272
|
+
href: "https://demo.stack-auth.com/",
|
|
273
|
+
protocol: "https:",
|
|
274
|
+
hostname: "demo.stack-auth.com",
|
|
275
|
+
},
|
|
276
|
+
} as any;
|
|
277
|
+
|
|
278
|
+
const clientApp = new StackClientApp({
|
|
279
|
+
baseUrl: "http://localhost:12345",
|
|
280
|
+
projectId,
|
|
281
|
+
publishableClientKey: "stack-pk-test",
|
|
282
|
+
tokenStore: "cookie",
|
|
283
|
+
redirectMethod: "none",
|
|
284
|
+
noAutomaticPrefetch: true,
|
|
285
|
+
});
|
|
286
|
+
const clientInterface = Reflect.get(clientApp, "_interface");
|
|
287
|
+
const originalFetchNewAccessToken = Reflect.get(clientInterface, "fetchNewAccessToken");
|
|
288
|
+
const refreshedRawRefreshTokens: string[] = [];
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
const getBrowserCookieTokenStore = Reflect.get(clientApp, "_getBrowserCookieTokenStore");
|
|
292
|
+
if (typeof getBrowserCookieTokenStore !== "function") {
|
|
293
|
+
throw new Error("Expected StackClientApp to expose _getBrowserCookieTokenStore in tests.");
|
|
294
|
+
}
|
|
295
|
+
const tokenStore = getBrowserCookieTokenStore.call(clientApp);
|
|
296
|
+
tokenStore.set({
|
|
297
|
+
refreshToken: "old-refresh-token",
|
|
298
|
+
accessToken: createAccessTokenString("old-refresh-token-id"),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
document.cookie = `__Host-hexclave-refresh-${projectId}--default=${JSON.stringify({
|
|
302
|
+
refresh_token: "new-refresh-token",
|
|
303
|
+
updated_at_millis: 1,
|
|
304
|
+
})}`;
|
|
305
|
+
Reflect.set(clientInterface, "fetchNewAccessToken", async (refreshToken: unknown) => {
|
|
306
|
+
const refreshTokenString = Reflect.get(refreshToken ?? {}, "token");
|
|
307
|
+
if (typeof refreshTokenString !== "string") {
|
|
308
|
+
throw new Error("Expected refresh token while fetching a new access token.");
|
|
309
|
+
}
|
|
310
|
+
refreshedRawRefreshTokens.push(refreshTokenString);
|
|
311
|
+
return AccessToken.createIfValid(createAccessTokenString("new-refresh-token-id")) ?? (() => {
|
|
312
|
+
throw new Error("Expected test access token to be valid");
|
|
313
|
+
})();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
const fetchCurrentRefreshTokenIdIfSignedIn = Reflect.get(clientApp, "_fetchCurrentRefreshTokenIdIfSignedIn");
|
|
317
|
+
if (typeof fetchCurrentRefreshTokenIdIfSignedIn !== "function") {
|
|
318
|
+
throw new Error("Expected StackClientApp to expose _fetchCurrentRefreshTokenIdIfSignedIn in tests.");
|
|
319
|
+
}
|
|
320
|
+
await expect(fetchCurrentRefreshTokenIdIfSignedIn.call(clientApp, {
|
|
321
|
+
awaitPendingAuthResolutions: false,
|
|
322
|
+
})).resolves.toBe("new-refresh-token-id");
|
|
323
|
+
} finally {
|
|
324
|
+
Reflect.set(clientInterface, "fetchNewAccessToken", originalFetchNewAccessToken);
|
|
325
|
+
globalThis.window = previousWindow;
|
|
326
|
+
globalThis.document = previousDocument;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
expect(refreshedRawRefreshTokens).toEqual(["new-refresh-token"]);
|
|
330
|
+
});
|
|
331
|
+
|
|
233
332
|
it("uses direct sign-out instead of hosted sign-out redirects when code execution is available", async () => {
|
|
234
333
|
const clientApp = new StackClientApp({
|
|
235
334
|
baseUrl: "http://localhost:12345",
|
|
@@ -290,7 +389,9 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
290
389
|
if (typeof signOut !== "function") {
|
|
291
390
|
throw new Error("Expected StackClientApp to expose _signOut in tests.");
|
|
292
391
|
}
|
|
293
|
-
await expect(signOut.call(clientApp,
|
|
392
|
+
await expect(signOut.call(clientApp, Reflect.get(clientInterface, "createSession").call(clientInterface, {
|
|
393
|
+
refreshToken: null,
|
|
394
|
+
}))).rejects.toThrowError("INTENTIONAL_TEST_ABORT");
|
|
294
395
|
} finally {
|
|
295
396
|
Reflect.set(clientInterface, "signOut", originalSignOut);
|
|
296
397
|
globalThis.window = previousWindow;
|