@hexclave/shared 1.0.0 → 1.0.1
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/ai/llms/llms.d.ts +17 -0
- package/dist/ai/llms/llms.d.ts.map +1 -0
- package/dist/ai/llms/llms.js +36 -0
- package/dist/ai/llms/llms.js.map +1 -0
- package/dist/ai/unified-prompts/reminders.js +2 -2
- package/dist/ai/unified-prompts/reminders.js.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-index.d.ts +7 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-index.d.ts.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-index.js +6 -5
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-index.js.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts +172 -0
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts.map +1 -0
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js +280 -0
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js.map +1 -0
- package/dist/ai/unified-prompts/skill-site-prompt.d.ts +2 -1
- package/dist/ai/unified-prompts/skill-site-prompt.d.ts.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt.js +36 -32
- package/dist/ai/unified-prompts/skill-site-prompt.js.map +1 -1
- package/dist/config/schema-fuzzer.test.js +1 -0
- package/dist/config/schema-fuzzer.test.js.map +1 -1
- package/dist/config/schema.d.ts +192 -188
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +6 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/config-authoring.d.ts +5 -1
- package/dist/config-authoring.d.ts.map +1 -1
- package/dist/config-authoring.js +5 -0
- package/dist/config-authoring.js.map +1 -1
- package/dist/config-rendering.d.ts +6 -6
- package/dist/config-rendering.d.ts.map +1 -1
- package/dist/config-rendering.js +25 -17
- package/dist/config-rendering.js.map +1 -1
- package/dist/esm/ai/llms/llms.d.ts +17 -0
- package/dist/esm/ai/llms/llms.d.ts.map +1 -0
- package/dist/esm/ai/llms/llms.js +30 -0
- package/dist/esm/ai/llms/llms.js.map +1 -0
- package/dist/esm/ai/unified-prompts/reminders.js +2 -2
- package/dist/esm/ai/unified-prompts/reminders.js.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-index.d.ts +7 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-index.d.ts.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-index.js +4 -4
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-index.js.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts +173 -0
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts.map +1 -0
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js +279 -0
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js.map +1 -0
- package/dist/esm/ai/unified-prompts/skill-site-prompt.d.ts +2 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt.d.ts.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt.js +36 -33
- package/dist/esm/ai/unified-prompts/skill-site-prompt.js.map +1 -1
- package/dist/esm/config/schema-fuzzer.test.js +1 -0
- package/dist/esm/config/schema-fuzzer.test.js.map +1 -1
- package/dist/esm/config/schema.d.ts +192 -188
- package/dist/esm/config/schema.d.ts.map +1 -1
- package/dist/esm/config/schema.js +6 -0
- package/dist/esm/config/schema.js.map +1 -1
- package/dist/esm/config-authoring.d.ts +5 -1
- package/dist/esm/config-authoring.d.ts.map +1 -1
- package/dist/esm/config-authoring.js +5 -1
- package/dist/esm/config-authoring.js.map +1 -1
- package/dist/esm/config-rendering.d.ts +6 -6
- package/dist/esm/config-rendering.d.ts.map +1 -1
- package/dist/esm/config-rendering.js +25 -17
- package/dist/esm/config-rendering.js.map +1 -1
- package/dist/esm/interface/admin-interface.js +1 -1
- package/dist/esm/interface/admin-metrics.d.ts +8 -8
- package/dist/esm/interface/client-interface.js +1 -1
- package/dist/esm/interface/conversations.d.ts +11 -11
- package/dist/esm/interface/crud/current-user.d.ts +9 -9
- package/dist/esm/interface/crud/email-outbox.d.ts +184 -184
- package/dist/esm/interface/crud/products.d.ts +15 -15
- package/dist/esm/interface/crud/products.d.ts.map +1 -1
- package/dist/esm/interface/crud/project-api-keys.d.ts +2 -2
- package/dist/esm/interface/crud/projects.d.ts +28 -28
- package/dist/esm/interface/crud/team-member-profiles.d.ts +20 -20
- package/dist/esm/interface/crud/transactions.d.ts +21 -21
- package/dist/esm/interface/crud/transactions.d.ts.map +1 -1
- package/dist/esm/interface/crud/users.d.ts +14 -14
- package/dist/esm/interface/server-interface.js +1 -1
- package/dist/esm/interface/webhooks.d.ts +2 -2
- package/dist/esm/known-errors.d.ts +5 -5
- package/dist/esm/schema-fields.d.ts +7 -6
- package/dist/esm/schema-fields.d.ts.map +1 -1
- package/dist/esm/schema-fields.js +5 -1
- package/dist/esm/schema-fields.js.map +1 -1
- package/dist/esm/sessions.d.ts +7 -7
- package/dist/esm/stack-config-file.js.map +1 -1
- package/dist/esm/utils/cloud-hosts.d.ts +42 -0
- package/dist/esm/utils/cloud-hosts.d.ts.map +1 -0
- package/dist/esm/utils/cloud-hosts.js +92 -0
- package/dist/esm/utils/cloud-hosts.js.map +1 -0
- package/dist/esm/utils/errors.d.ts +9 -2
- package/dist/esm/utils/errors.d.ts.map +1 -1
- package/dist/esm/utils/errors.js +16 -5
- package/dist/esm/utils/errors.js.map +1 -1
- package/dist/esm/utils/promises.js +1 -1
- package/dist/esm/utils/redirect-urls.d.ts.map +1 -1
- package/dist/esm/utils/redirect-urls.js +3 -3
- package/dist/esm/utils/redirect-urls.js.map +1 -1
- package/dist/esm/utils/urls.d.ts.map +1 -1
- package/dist/esm/utils/urls.js +2 -2
- package/dist/esm/utils/urls.js.map +1 -1
- package/dist/{index-D9ysacya.d.ts → index-fQbeILq7.d.ts} +1 -1
- package/dist/{index-D9ysacya.d.ts.map → index-fQbeILq7.d.ts.map} +1 -1
- package/dist/interface/admin-interface.js +1 -1
- package/dist/interface/admin-metrics.d.ts +8 -8
- package/dist/interface/client-interface.d.ts +1 -1
- package/dist/interface/client-interface.js +1 -1
- package/dist/interface/conversations.d.ts +11 -11
- package/dist/interface/crud/current-user.d.ts +9 -9
- package/dist/interface/crud/email-outbox.d.ts +184 -184
- package/dist/interface/crud/products.d.ts +15 -15
- package/dist/interface/crud/products.d.ts.map +1 -1
- package/dist/interface/crud/project-api-keys.d.ts +2 -2
- package/dist/interface/crud/projects.d.ts +28 -28
- package/dist/interface/crud/team-member-profiles.d.ts +20 -20
- package/dist/interface/crud/transactions.d.ts +21 -21
- package/dist/interface/crud/transactions.d.ts.map +1 -1
- package/dist/interface/crud/users.d.ts +14 -14
- package/dist/interface/server-interface.js +1 -1
- package/dist/interface/webhooks.d.ts +2 -2
- package/dist/known-errors.d.ts +5 -5
- package/dist/schema-fields.d.ts +7 -6
- package/dist/schema-fields.d.ts.map +1 -1
- package/dist/schema-fields.js +5 -0
- package/dist/schema-fields.js.map +1 -1
- package/dist/sessions.d.ts +7 -7
- package/dist/stack-config-file.js.map +1 -1
- package/dist/utils/cloud-hosts.d.ts +42 -0
- package/dist/utils/cloud-hosts.d.ts.map +1 -0
- package/dist/utils/cloud-hosts.js +97 -0
- package/dist/utils/cloud-hosts.js.map +1 -0
- package/dist/utils/errors.d.ts +9 -2
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +16 -4
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/passkey.d.ts +1 -1
- package/dist/utils/promises.js +1 -1
- package/dist/utils/redirect-urls.d.ts.map +1 -1
- package/dist/utils/redirect-urls.js +3 -3
- package/dist/utils/redirect-urls.js.map +1 -1
- package/dist/utils/urls.d.ts.map +1 -1
- package/dist/utils/urls.js +2 -2
- package/dist/utils/urls.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
//#region src/utils/cloud-hosts.tsx
|
|
4
|
+
/**
|
|
5
|
+
* Single source of truth for the stack-auth ↔ hexclave host pairs that the
|
|
6
|
+
* cloud deployment treats as equivalent siblings. Each
|
|
7
|
+
* `[stackAuthHost, hexclaveHost]` pair is consumed in a few places:
|
|
8
|
+
*
|
|
9
|
+
* 1. `CLOUD_API_HOST_BY_REQUEST_HOST` in the backend's `request-api-url.ts` —
|
|
10
|
+
* the allowlist of request hosts we resolve into a JWT `iss` claim.
|
|
11
|
+
* 2. `issuerHostAliases` in `tokens.tsx` — the bidirectional validator alias
|
|
12
|
+
* map, so a token issued under either host validates against the other.
|
|
13
|
+
* 3. `getCloudApiUrlSiblings` below — resolves the branded base URLs used for
|
|
14
|
+
* OAuth `redirect_uri` callbacks (both at runtime and in the dashboard).
|
|
15
|
+
*
|
|
16
|
+
* Deriving all of these from this one list prevents drift.
|
|
17
|
+
*/
|
|
18
|
+
const CLOUD_HOST_PAIRS = [
|
|
19
|
+
["api.stack-auth.com", "api.hexclave.com"],
|
|
20
|
+
["api.dev.stack-auth.com", "api.dev.hexclave.com"],
|
|
21
|
+
["api.staging.stack-auth.com", "api.staging.hexclave.com"]
|
|
22
|
+
];
|
|
23
|
+
function hostFromApiUrlOrHost(input) {
|
|
24
|
+
try {
|
|
25
|
+
return new URL(input).host.split(":")[0].toLowerCase();
|
|
26
|
+
} catch {
|
|
27
|
+
const firstHost = input.split(",")[0]?.trim();
|
|
28
|
+
if (!firstHost) return void 0;
|
|
29
|
+
return firstHost.split(":")[0].toLowerCase();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Given an API URL (or bare host), if its host belongs to a known
|
|
34
|
+
* stack-auth ↔ hexclave cloud pair, return both branded base URLs. Returns
|
|
35
|
+
* null for unknown hosts (localhost, vercel previews, self-host custom
|
|
36
|
+
* domains).
|
|
37
|
+
*/
|
|
38
|
+
function getCloudApiUrlSiblings(apiUrlOrHost) {
|
|
39
|
+
if (!apiUrlOrHost) return null;
|
|
40
|
+
const host = hostFromApiUrlOrHost(apiUrlOrHost);
|
|
41
|
+
if (!host) return null;
|
|
42
|
+
for (const [stackAuthHost, hexclaveHost] of CLOUD_HOST_PAIRS) if (host === stackAuthHost || host === hexclaveHost) return {
|
|
43
|
+
stackAuth: `https://${stackAuthHost}`,
|
|
44
|
+
hexclave: `https://${hexclaveHost}`
|
|
45
|
+
};
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The stack-auth-branded base URL for the given deployment API URL. Used as the
|
|
50
|
+
* OAuth `redirect_uri` base for shared providers and for custom providers that
|
|
51
|
+
* predate `customCallbackUrl` (so existing flows keep hitting
|
|
52
|
+
* `api.stack-auth.com`). Unknown hosts fall back to the input unchanged.
|
|
53
|
+
*/
|
|
54
|
+
function getStackAuthApiBaseUrl(apiUrl) {
|
|
55
|
+
return getCloudApiUrlSiblings(apiUrl)?.stackAuth ?? apiUrl;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The hexclave-branded base URL for the given deployment API URL. Used when a
|
|
59
|
+
* new custom OAuth provider is set up, so its `customCallbackUrl` points at the
|
|
60
|
+
* hexclave brand. Unknown hosts fall back to the input unchanged.
|
|
61
|
+
*/
|
|
62
|
+
function getHexclaveApiBaseUrl(apiUrl) {
|
|
63
|
+
return getCloudApiUrlSiblings(apiUrl)?.hexclave ?? apiUrl;
|
|
64
|
+
}
|
|
65
|
+
(void 0)?.test("getCloudApiUrlSiblings maps both sides of each cloud pair", ({ expect }) => {
|
|
66
|
+
for (const [stackAuthHost, hexclaveHost] of CLOUD_HOST_PAIRS) {
|
|
67
|
+
const expected = {
|
|
68
|
+
stackAuth: `https://${stackAuthHost}`,
|
|
69
|
+
hexclave: `https://${hexclaveHost}`
|
|
70
|
+
};
|
|
71
|
+
for (const host of [stackAuthHost, hexclaveHost]) {
|
|
72
|
+
expect(getCloudApiUrlSiblings(host)).toEqual(expected);
|
|
73
|
+
expect(getCloudApiUrlSiblings(`https://${host}`)).toEqual(expected);
|
|
74
|
+
expect(getCloudApiUrlSiblings(`https://${host.toUpperCase()}:443`)).toEqual(expected);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
(void 0)?.test("getCloudApiUrlSiblings returns null for unknown hosts", ({ expect }) => {
|
|
79
|
+
expect(getCloudApiUrlSiblings("http://localhost:8102")).toBeNull();
|
|
80
|
+
expect(getCloudApiUrlSiblings("https://my-app.vercel.app")).toBeNull();
|
|
81
|
+
expect(getCloudApiUrlSiblings(void 0)).toBeNull();
|
|
82
|
+
expect(getCloudApiUrlSiblings("")).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
(void 0)?.test("getStackAuthApiBaseUrl / getHexclaveApiBaseUrl resolve brands and fall back", ({ expect }) => {
|
|
85
|
+
expect(getStackAuthApiBaseUrl("https://api.hexclave.com")).toBe("https://api.stack-auth.com");
|
|
86
|
+
expect(getHexclaveApiBaseUrl("https://api.stack-auth.com")).toBe("https://api.hexclave.com");
|
|
87
|
+
expect(getStackAuthApiBaseUrl("https://api.dev.stack-auth.com")).toBe("https://api.dev.stack-auth.com");
|
|
88
|
+
expect(getHexclaveApiBaseUrl("http://localhost:8102")).toBe("http://localhost:8102");
|
|
89
|
+
expect(getStackAuthApiBaseUrl("http://localhost:8102")).toBe("http://localhost:8102");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
exports.CLOUD_HOST_PAIRS = CLOUD_HOST_PAIRS;
|
|
94
|
+
exports.getCloudApiUrlSiblings = getCloudApiUrlSiblings;
|
|
95
|
+
exports.getHexclaveApiBaseUrl = getHexclaveApiBaseUrl;
|
|
96
|
+
exports.getStackAuthApiBaseUrl = getStackAuthApiBaseUrl;
|
|
97
|
+
//# sourceMappingURL=cloud-hosts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-hosts.js","names":[],"sources":["../../src/utils/cloud-hosts.tsx"],"sourcesContent":["/**\n * Single source of truth for the stack-auth ↔ hexclave host pairs that the\n * cloud deployment treats as equivalent siblings. Each\n * `[stackAuthHost, hexclaveHost]` pair is consumed in a few places:\n *\n * 1. `CLOUD_API_HOST_BY_REQUEST_HOST` in the backend's `request-api-url.ts` —\n * the allowlist of request hosts we resolve into a JWT `iss` claim.\n * 2. `issuerHostAliases` in `tokens.tsx` — the bidirectional validator alias\n * map, so a token issued under either host validates against the other.\n * 3. `getCloudApiUrlSiblings` below — resolves the branded base URLs used for\n * OAuth `redirect_uri` callbacks (both at runtime and in the dashboard).\n *\n * Deriving all of these from this one list prevents drift.\n */\nexport const CLOUD_HOST_PAIRS: ReadonlyArray<readonly [string, string]> = [\n [\"api.stack-auth.com\", \"api.hexclave.com\"],\n [\"api.dev.stack-auth.com\", \"api.dev.hexclave.com\"],\n [\"api.staging.stack-auth.com\", \"api.staging.hexclave.com\"],\n];\n\nfunction hostFromApiUrlOrHost(input: string): string | undefined {\n try {\n return new URL(input).host.split(\":\")[0].toLowerCase();\n } catch {\n const firstHost = input.split(\",\")[0]?.trim();\n if (!firstHost) return undefined;\n return firstHost.split(\":\")[0].toLowerCase();\n }\n}\n\n/**\n * Given an API URL (or bare host), if its host belongs to a known\n * stack-auth ↔ hexclave cloud pair, return both branded base URLs. Returns\n * null for unknown hosts (localhost, vercel previews, self-host custom\n * domains).\n */\nexport function getCloudApiUrlSiblings(apiUrlOrHost: string | undefined | null): { stackAuth: string, hexclave: string } | null {\n if (!apiUrlOrHost) return null;\n const host = hostFromApiUrlOrHost(apiUrlOrHost);\n if (!host) return null;\n for (const [stackAuthHost, hexclaveHost] of CLOUD_HOST_PAIRS) {\n if (host === stackAuthHost || host === hexclaveHost) {\n return { stackAuth: `https://${stackAuthHost}`, hexclave: `https://${hexclaveHost}` };\n }\n }\n return null;\n}\n\n/**\n * The stack-auth-branded base URL for the given deployment API URL. Used as the\n * OAuth `redirect_uri` base for shared providers and for custom providers that\n * predate `customCallbackUrl` (so existing flows keep hitting\n * `api.stack-auth.com`). Unknown hosts fall back to the input unchanged.\n */\nexport function getStackAuthApiBaseUrl(apiUrl: string): string {\n return getCloudApiUrlSiblings(apiUrl)?.stackAuth ?? apiUrl;\n}\n\n/**\n * The hexclave-branded base URL for the given deployment API URL. Used when a\n * new custom OAuth provider is set up, so its `customCallbackUrl` points at the\n * hexclave brand. Unknown hosts fall back to the input unchanged.\n */\nexport function getHexclaveApiBaseUrl(apiUrl: string): string {\n return getCloudApiUrlSiblings(apiUrl)?.hexclave ?? apiUrl;\n}\n\nimport.meta.vitest?.test(\"getCloudApiUrlSiblings maps both sides of each cloud pair\", ({ expect }) => {\n for (const [stackAuthHost, hexclaveHost] of CLOUD_HOST_PAIRS) {\n const expected = { stackAuth: `https://${stackAuthHost}`, hexclave: `https://${hexclaveHost}` };\n for (const host of [stackAuthHost, hexclaveHost]) {\n expect(getCloudApiUrlSiblings(host)).toEqual(expected);\n expect(getCloudApiUrlSiblings(`https://${host}`)).toEqual(expected);\n expect(getCloudApiUrlSiblings(`https://${host.toUpperCase()}:443`)).toEqual(expected);\n }\n }\n});\n\nimport.meta.vitest?.test(\"getCloudApiUrlSiblings returns null for unknown hosts\", ({ expect }) => {\n expect(getCloudApiUrlSiblings(\"http://localhost:8102\")).toBeNull();\n expect(getCloudApiUrlSiblings(\"https://my-app.vercel.app\")).toBeNull();\n expect(getCloudApiUrlSiblings(undefined)).toBeNull();\n expect(getCloudApiUrlSiblings(\"\")).toBeNull();\n});\n\nimport.meta.vitest?.test(\"getStackAuthApiBaseUrl / getHexclaveApiBaseUrl resolve brands and fall back\", ({ expect }) => {\n expect(getStackAuthApiBaseUrl(\"https://api.hexclave.com\")).toBe(\"https://api.stack-auth.com\");\n expect(getHexclaveApiBaseUrl(\"https://api.stack-auth.com\")).toBe(\"https://api.hexclave.com\");\n expect(getStackAuthApiBaseUrl(\"https://api.dev.stack-auth.com\")).toBe(\"https://api.dev.stack-auth.com\");\n expect(getHexclaveApiBaseUrl(\"http://localhost:8102\")).toBe(\"http://localhost:8102\");\n expect(getStackAuthApiBaseUrl(\"http://localhost:8102\")).toBe(\"http://localhost:8102\");\n});\n"],"mappings":";;;;;;;;;;;;;;;;;AAcA,MAAa,mBAA6D;CACxE,CAAC,sBAAsB,mBAAmB;CAC1C,CAAC,0BAA0B,uBAAuB;CAClD,CAAC,8BAA8B,2BAA2B;CAC3D;AAED,SAAS,qBAAqB,OAAmC;AAC/D,KAAI;AACF,SAAO,IAAI,IAAI,MAAM,CAAC,KAAK,MAAM,IAAI,CAAC,GAAG,aAAa;SAChD;EACN,MAAM,YAAY,MAAM,MAAM,IAAI,CAAC,IAAI,MAAM;AAC7C,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO,UAAU,MAAM,IAAI,CAAC,GAAG,aAAa;;;;;;;;;AAUhD,SAAgB,uBAAuB,cAAyF;AAC9H,KAAI,CAAC,aAAc,QAAO;CAC1B,MAAM,OAAO,qBAAqB,aAAa;AAC/C,KAAI,CAAC,KAAM,QAAO;AAClB,MAAK,MAAM,CAAC,eAAe,iBAAiB,iBAC1C,KAAI,SAAS,iBAAiB,SAAS,aACrC,QAAO;EAAE,WAAW,WAAW;EAAiB,UAAU,WAAW;EAAgB;AAGzF,QAAO;;;;;;;;AAST,SAAgB,uBAAuB,QAAwB;AAC7D,QAAO,uBAAuB,OAAO,EAAE,aAAa;;;;;;;AAQtD,SAAgB,sBAAsB,QAAwB;AAC5D,QAAO,uBAAuB,OAAO,EAAE,YAAY;;CAGrD,SAAW,KAAC,8DAAkE,EAAC,aAAU;AACvF,MAAK,MAAM,CAAC,eAAe,iBAAiB,kBAAkB;EAC5D,MAAM,WAAW;GAAE,WAAW,WAAW;GAAiB,UAAU,WAAW;GAAgB;AAC/F,OAAK,MAAM,QAAQ,CAAC,eAAe,aAAa,EAAE;AAChD,UAAO,uBAAuB,KAAK,CAAC,CAAC,QAAQ,SAAS;AACtD,UAAO,uBAAuB,WAAW,OAAO,CAAC,CAAC,QAAQ,SAAS;AACnE,UAAO,uBAAuB,WAAW,KAAK,aAAa,CAAC,MAAM,CAAC,CAAC,QAAQ,SAAS;;;EAGzF;CAEF,SAAW,KAAC,0DAA8D,EAAA,aAAW;AACnF,QAAO,uBAAuB,wBAAwB,CAAC,CAAC,UAAU;AAClE,QAAO,uBAAuB,4BAA4B,CAAC,CAAC,UAAU;AACtE,QAAO,uBAAuB,OAAU,CAAC,CAAC,UAAU;AACpD,QAAO,uBAAuB,GAAG,CAAC,CAAC,UAAU;EAC7C;CAEF,SAAW,KAAC,gFAAoF,EAAC,aAAU;AACzG,QAAO,uBAAuB,2BAA2B,CAAC,CAAC,KAAK,6BAA6B;AAC7F,QAAO,sBAAsB,6BAA6B,CAAC,CAAC,KAAK,2BAA2B;AAC5F,QAAO,uBAAuB,iCAAiC,CAAC,CAAC,KAAK,iCAAiC;AACvG,QAAO,sBAAsB,wBAAwB,CAAC,CAAC,KAAK,wBAAwB;AACpF,QAAO,uBAAuB,wBAAwB,CAAC,CAAC,KAAK,wBAAwB;EACrF"}
|
package/dist/utils/errors.d.ts
CHANGED
|
@@ -33,7 +33,9 @@ declare class HexclaveAssertionError extends Error {
|
|
|
33
33
|
constructor(message: string, extraData?: (Record<string, any> & ErrorOptions) | undefined);
|
|
34
34
|
}
|
|
35
35
|
declare function errorToNiceString(error: unknown): string;
|
|
36
|
-
|
|
36
|
+
type CaptureLevel = "error" | "warning";
|
|
37
|
+
type ErrorSink = (location: string, error: unknown, level: CaptureLevel, ...extraArgs: unknown[]) => void;
|
|
38
|
+
declare function registerErrorSink(sink: ErrorSink): void;
|
|
37
39
|
/**
|
|
38
40
|
* Captures an error and sends it to the error sinks (most notably, Sentry). Errors caught with captureError are
|
|
39
41
|
* supposed to be seen by an engineer, so they should be actionable and important.
|
|
@@ -44,6 +46,11 @@ declare function registerErrorSink(sink: (location: string, error: unknown) => v
|
|
|
44
46
|
* Errors that bubble up to the top of runAsynchronously or a route handler are already captured with captureError.
|
|
45
47
|
*/
|
|
46
48
|
declare function captureError(location: string, error: unknown): void;
|
|
49
|
+
/**
|
|
50
|
+
* Like captureError, but logs at warning level. Use for issues that an engineer should know about but that aren't
|
|
51
|
+
* severe enough to be treated as an error (e.g. recoverable failures in background tasks).
|
|
52
|
+
*/
|
|
53
|
+
declare function captureWarning(location: string, error: unknown): void;
|
|
47
54
|
type Status = {
|
|
48
55
|
statusCode: number;
|
|
49
56
|
message: string;
|
|
@@ -227,5 +234,5 @@ declare class StatusError extends Error {
|
|
|
227
234
|
toHttpJson(): Json;
|
|
228
235
|
}
|
|
229
236
|
//#endregion
|
|
230
|
-
export { HexclaveAssertionError, StatusError, captureError, concatStacktraces, errorToNiceString, registerErrorSink, throwErr };
|
|
237
|
+
export { CaptureLevel, HexclaveAssertionError, StatusError, captureError, captureWarning, concatStacktraces, errorToNiceString, registerErrorSink, throwErr };
|
|
231
238
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/utils/errors.tsx"],"mappings":";;;iBAMgB,QAAA,CAAS,YAAA,UAAsB,SAAA;AAAA,iBAC/B,QAAA,CAAS,KAAA,EAAO,KAAA;AAAA,iBAChB,QAAA,CAAA,GAAY,IAAA,EAAM,gCAAA;;;;;AADlC;;;;;AACA;;;;;AA0CA;;;;;;;;;iBAAgB,iBAAA,CAAkB,KAAA,EAAO,KAAA,KAAU,MAAA,EAAQ,KAAA;AAAA,cAkB9C,sBAAA,SAA+B,KAAA;EAAA,SACG,SAAA,IAAY,MAAA,gBAAsB,YAAA;cAAnE,OAAA,UAAiC,SAAA,IAAY,MAAA,gBAAsB,YAAA;AAAA;AAAA,iBAqBjE,iBAAA,CAAkB,KAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/utils/errors.tsx"],"mappings":";;;iBAMgB,QAAA,CAAS,YAAA,UAAsB,SAAA;AAAA,iBAC/B,QAAA,CAAS,KAAA,EAAO,KAAA;AAAA,iBAChB,QAAA,CAAA,GAAY,IAAA,EAAM,gCAAA;;;;;AADlC;;;;;AACA;;;;;AA0CA;;;;;;;;;iBAAgB,iBAAA,CAAkB,KAAA,EAAO,KAAA,KAAU,MAAA,EAAQ,KAAA;AAAA,cAkB9C,sBAAA,SAA+B,KAAA;EAAA,SACG,SAAA,IAAY,MAAA,gBAAsB,YAAA;cAAnE,OAAA,UAAiC,SAAA,IAAY,MAAA,gBAAsB,YAAA;AAAA;AAAA,iBAqBjE,iBAAA,CAAkB,KAAA;AAAA,KAMtB,YAAA;AAAA,KAEP,SAAA,IAAa,QAAA,UAAkB,KAAA,WAAgB,KAAA,EAAO,YAAA,KAAiB,SAAA;AAAA,iBAG5D,iBAAA,CAAkB,IAAA,EAAM,SAAA;;;;;;;;;;iBA4CxB,YAAA,CAAa,QAAA,UAAkB,KAAA;;;;AAvD/C;iBA+DgB,cAAA,CAAe,QAAA,UAAkB,KAAA;AAAA,KAK5C,MAAA;EACH,UAAA;EACA,OAAA;AAAA;AAAA,KAGG,gCAAA,IAEH,MAAA,EAAQ,MAAA,EACR,OAAA,cAGA,UAAA,WAAqB,MAAA,EACrB,OAAA;AAAA,cAGW,WAAA,SAAoB,KAAA;EAAA,iBACd,uBAAA;EACV,IAAA;EAAA,SACS,UAAA;EAAA,OAEF,UAAA;;;;SACA,YAAA;;;;SACA,eAAA;;;;SACA,SAAA;;;;SACA,QAAA;;;;SACA,gBAAA;;;;SACA,aAAA;;;;SACA,2BAAA;;;;SACA,cAAA;;;;SACA,QAAA;;;;SACA,IAAA;;;;SACA,cAAA;;;;SACA,kBAAA;;;;SACA,eAAA;;;;SACA,UAAA;;;;SACA,oBAAA;;;;SACA,mBAAA;;;;SACA,iBAAA;;;;SACA,SAAA;;;;SACA,kBAAA;;;;SACA,mBAAA;;;;SACA,MAAA;;;;SACA,gBAAA;;;;SACA,QAAA;;;;SACA,eAAA;;;;SACA,oBAAA;;;;SACA,eAAA;;;;SACA,2BAAA;;;;SACA,0BAAA;;;;SAEA,mBAAA;;;;SACA,cAAA;;;;SACA,UAAA;;;;SACA,kBAAA;;;;SACA,cAAA;;;;SACA,uBAAA;;;;SACA,qBAAA;;;;SACA,mBAAA;;;;SACA,YAAA;;;;SACA,WAAA;;;;SACA,6BAAA;;;;iBAGC,IAAA,EAAM,gCAAA;EAAA,OAgBP,aAAA,CAAc,KAAA,YAAiB,KAAA,IAAS,WAAA;EAK/C,aAAA,CAAA;EAIA,aAAA,CAAA;EAIA,aAAA,CAAA;EAIA,OAAA,CAAA,GAAW,UAAA;EAIX,UAAA,CAAA,GAAc,MAAA;EAMd,iBAAA,CAAA,GAAqB,IAAA;;;;EAWrB,UAAA,CAAA,GAAc,IAAA;AAAA"}
|
package/dist/utils/errors.js
CHANGED
|
@@ -69,17 +69,21 @@ function registerErrorSink(sink) {
|
|
|
69
69
|
if (errorSinks.has(sink)) return;
|
|
70
70
|
errorSinks.add(sink);
|
|
71
71
|
}
|
|
72
|
-
registerErrorSink((location, error, ...extraArgs) => {
|
|
73
|
-
console.error(
|
|
72
|
+
registerErrorSink((location, error, level, ...extraArgs) => {
|
|
73
|
+
(level === "warning" ? console.warn : console.error)(`${level === "warning" ? "\x1B[43m" : "\x1B[41m"}Captured ${level === "warning" ? "warning" : "error"} in ${location}:`, errorToNiceString(error), ...extraArgs, "\x1B[0m");
|
|
74
74
|
});
|
|
75
|
-
registerErrorSink((location, error, ...extraArgs) => {
|
|
75
|
+
registerErrorSink((location, error, level, ...extraArgs) => {
|
|
76
76
|
__globals_js.globalVar.stackCapturedErrors = __globals_js.globalVar.stackCapturedErrors ?? [];
|
|
77
77
|
__globals_js.globalVar.stackCapturedErrors.push({
|
|
78
78
|
location,
|
|
79
79
|
error,
|
|
80
|
+
level,
|
|
80
81
|
extraArgs
|
|
81
82
|
});
|
|
82
83
|
});
|
|
84
|
+
function dispatchToSinks(location, error, level) {
|
|
85
|
+
for (const sink of errorSinks) sink(location, error, level, ...error && (typeof error === "object" || typeof error === "function") && "customCaptureExtraArgs" in error && Array.isArray(error.customCaptureExtraArgs) ? error.customCaptureExtraArgs : []);
|
|
86
|
+
}
|
|
83
87
|
/**
|
|
84
88
|
* Captures an error and sends it to the error sinks (most notably, Sentry). Errors caught with captureError are
|
|
85
89
|
* supposed to be seen by an engineer, so they should be actionable and important.
|
|
@@ -90,7 +94,14 @@ registerErrorSink((location, error, ...extraArgs) => {
|
|
|
90
94
|
* Errors that bubble up to the top of runAsynchronously or a route handler are already captured with captureError.
|
|
91
95
|
*/
|
|
92
96
|
function captureError(location, error) {
|
|
93
|
-
|
|
97
|
+
dispatchToSinks(location, error, "error");
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Like captureError, but logs at warning level. Use for issues that an engineer should know about but that aren't
|
|
101
|
+
* severe enough to be treated as an error (e.g. recoverable failures in background tasks).
|
|
102
|
+
*/
|
|
103
|
+
function captureWarning(location, error) {
|
|
104
|
+
dispatchToSinks(location, error, "warning");
|
|
94
105
|
}
|
|
95
106
|
var StatusError = class extends Error {
|
|
96
107
|
static {
|
|
@@ -386,6 +397,7 @@ StatusError.prototype.name = "StatusError";
|
|
|
386
397
|
exports.HexclaveAssertionError = HexclaveAssertionError;
|
|
387
398
|
exports.StatusError = StatusError;
|
|
388
399
|
exports.captureError = captureError;
|
|
400
|
+
exports.captureWarning = captureWarning;
|
|
389
401
|
exports.concatStacktraces = concatStacktraces;
|
|
390
402
|
exports.errorToNiceString = errorToNiceString;
|
|
391
403
|
exports.registerErrorSink = registerErrorSink;
|
package/dist/utils/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":["globalVar"],"sources":["../../src/utils/errors.tsx"],"sourcesContent":["import { globalVar } from \"./globals\";\nimport { Json } from \"./json\";\nimport { pick } from \"./objects\";\nimport { nicify } from \"./strings\";\n\n\nexport function throwErr(errorMessage: string, extraData?: any): never;\nexport function throwErr(error: Error): never;\nexport function throwErr(...args: StatusErrorConstructorParameters): never;\nexport function throwErr(...args: any[]): never {\n if (typeof args[0] === \"string\") {\n throw new HexclaveAssertionError(args[0], args[1]);\n } else if (args[0] instanceof Error) {\n throw args[0];\n } else {\n // @ts-expect-error\n throw new StatusError(...args);\n }\n}\n\nfunction removeStacktraceNameLine(stack: string): string {\n // some browsers (eg. Chrome) prepend the stack with an extra line with the error name\n const addsNameLine = new Error().stack?.startsWith(\"Error\\n\");\n return stack.split(\"\\n\").slice(addsNameLine ? 1 : 0).join(\"\\n\");\n}\n\n\n/**\n * Concatenates the (original) stacktraces of the given errors onto the first.\n *\n * Note: Very often, the concatStacktracesIfRejected function in promises.tsx is an easier way to use this function.\n *\n * Useful when you invoke an async function to receive a promise without awaiting it immediately. Browsers are smart\n * enough to keep track of the call stack in async function calls when you invoke `.then` within the same async tick,\n * but if you don't, the stacktrace will be lost.\n *\n * Here's an example of the unwanted behavior:\n *\n * ```tsx\n * async function log() {\n * await wait(0); // put the task on the event loop\n * console.log(new Error().stack);\n * }\n *\n * async function main() {\n * await log(); // good; prints both \"log\" and \"main\" on the stacktrace\n * log(); // bad; prints only \"log\" on the stacktrace\n * }\n * ```\n */\nexport function concatStacktraces(first: Error, ...errors: Error[]): void {\n // some browsers (eg. Firefox) add an extra empty line at the end\n const addsEmptyLineAtEnd = first.stack?.endsWith(\"\\n\");\n\n\n // Add a reference to this function itself so that we know that stacktraces were concatenated\n // If you are coming here from a stacktrace, please know that the two parts before and after this line are different\n // stacktraces that were concatenated with concatStacktraces\n const separator = removeStacktraceNameLine(new Error().stack ?? \"\").split(\"\\n\")[0];\n\n\n for (const error of errors) {\n const toAppend = removeStacktraceNameLine(error.stack ?? \"\");\n first.stack += (addsEmptyLineAtEnd ? \"\" : \"\\n\") + separator + \"\\n\" + toAppend;\n }\n}\n\n\nexport class HexclaveAssertionError extends Error {\n constructor(message: string, public readonly extraData?: Record<string, any> & ErrorOptions) {\n const disclaimer = `\\n\\nThis is likely an error in Hexclave. Please make sure you are running the newest version and report it.`;\n super(`${message}${message.endsWith(disclaimer) ? \"\" : disclaimer}`, pick(extraData ?? {}, [\"cause\"]));\n\n Object.defineProperty(this, \"customCaptureExtraArgs\", {\n get() {\n return [this.extraData];\n },\n enumerable: false,\n });\n\n // Use literal dot-form (guarded with `typeof process`) so Next.js / webpack\n // DefinePlugin can inline the value at build time. See getProcessEnv in ./env.\n if ((typeof process !== \"undefined\" ? process.env.NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR : undefined) === \"true\") {\n debugger;\n }\n }\n}\nHexclaveAssertionError.prototype.name = \"HexclaveAssertionError\";\n\n\nexport function errorToNiceString(error: unknown): string {\n if (!(error instanceof Error)) return `${typeof error}<${nicify(error)}>`;\n return nicify(error, { maxDepth: 8 });\n}\n\n\nconst errorSinks = new Set<(location: string, error: unknown, ...extraArgs: unknown[]) => void>();\nexport function registerErrorSink(sink: (location: string, error: unknown) => void): void {\n if (errorSinks.has(sink)) {\n return;\n }\n errorSinks.add(sink);\n}\nregisterErrorSink((location, error, ...extraArgs) => {\n console.error(\n `\\x1b[41mCaptured error in ${location}:`,\n // HACK: Log a nicified version of the error to get around buggy Next.js pretty-printing\n // https://www.reddit.com/r/nextjs/comments/1gkxdqe/comment/m19kxgn/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button\n errorToNiceString(error),\n ...extraArgs,\n \"\\x1b[0m\",\n );\n});\nregisterErrorSink((location, error, ...extraArgs) => {\n globalVar.stackCapturedErrors = globalVar.stackCapturedErrors ?? [];\n globalVar.stackCapturedErrors.push({ location, error, extraArgs });\n});\n\n/**\n * Captures an error and sends it to the error sinks (most notably, Sentry). Errors caught with captureError are\n * supposed to be seen by an engineer, so they should be actionable and important.\n *\n * The location string is a machine-readable ID, and should hence not contain spaces or anything like that. Good\n * examples are: \"api-route-handler\", \"renderPart()\", etc.\n *\n * Errors that bubble up to the top of runAsynchronously or a route handler are already captured with captureError.\n */\nexport function captureError(location: string, error: unknown): void {\n for (const sink of errorSinks) {\n sink(\n location,\n error,\n ...error && (typeof error === 'object' || typeof error === 'function') && \"customCaptureExtraArgs\" in error && Array.isArray(error.customCaptureExtraArgs) ? (error.customCaptureExtraArgs as any[]) : [],\n );\n }\n}\n\n\ntype Status = {\n statusCode: number,\n message: string,\n};\n\ntype StatusErrorConstructorParameters =\n| [\n status: Status,\n message?: string\n]\n| [\n statusCode: number | Status,\n message: string,\n];\n\nexport class StatusError extends Error {\n private readonly __stackStatusErrorBrand = \"stack-status-error-brand-sentinel\" as const;\n public name = \"StatusError\";\n public readonly statusCode: number;\n\n public static BadRequest = { statusCode: 400, message: \"Bad Request\" };\n public static Unauthorized = { statusCode: 401, message: \"Unauthorized\" };\n public static PaymentRequired = { statusCode: 402, message: \"Payment Required\" };\n public static Forbidden = { statusCode: 403, message: \"Forbidden\" };\n public static NotFound = { statusCode: 404, message: \"Not Found\" };\n public static MethodNotAllowed = { statusCode: 405, message: \"Method Not Allowed\" };\n public static NotAcceptable = { statusCode: 406, message: \"Not Acceptable\" };\n public static ProxyAuthenticationRequired = { statusCode: 407, message: \"Proxy Authentication Required\" };\n public static RequestTimeout = { statusCode: 408, message: \"Request Timeout\" };\n public static Conflict = { statusCode: 409, message: \"Conflict\" };\n public static Gone = { statusCode: 410, message: \"Gone\" };\n public static LengthRequired = { statusCode: 411, message: \"Length Required\" };\n public static PreconditionFailed = { statusCode: 412, message: \"Precondition Failed\" };\n public static PayloadTooLarge = { statusCode: 413, message: \"Payload Too Large\" };\n public static URITooLong = { statusCode: 414, message: \"URI Too Long\" };\n public static UnsupportedMediaType = { statusCode: 415, message: \"Unsupported Media Type\" };\n public static RangeNotSatisfiable = { statusCode: 416, message: \"Range Not Satisfiable\" };\n public static ExpectationFailed = { statusCode: 417, message: \"Expectation Failed\" };\n public static ImATeapot = { statusCode: 418, message: \"I'm a teapot\" };\n public static MisdirectedRequest = { statusCode: 421, message: \"Misdirected Request\" };\n public static UnprocessableEntity = { statusCode: 422, message: \"Unprocessable Entity\" };\n public static Locked = { statusCode: 423, message: \"Locked\" };\n public static FailedDependency = { statusCode: 424, message: \"Failed Dependency\" };\n public static TooEarly = { statusCode: 425, message: \"Too Early\" };\n public static UpgradeRequired = { statusCode: 426, message: \"Upgrade Required\" };\n public static PreconditionRequired = { statusCode: 428, message: \"Precondition Required\" };\n public static TooManyRequests = { statusCode: 429, message: \"Too Many Requests\" };\n public static RequestHeaderFieldsTooLarge = { statusCode: 431, message: \"Request Header Fields Too Large\" };\n public static UnavailableForLegalReasons = { statusCode: 451, message: \"Unavailable For Legal Reasons\" };\n\n public static InternalServerError = { statusCode: 500, message: \"Internal Server Error\" };\n public static NotImplemented = { statusCode: 501, message: \"Not Implemented\" };\n public static BadGateway = { statusCode: 502, message: \"Bad Gateway\" };\n public static ServiceUnavailable = { statusCode: 503, message: \"Service Unavailable\" };\n public static GatewayTimeout = { statusCode: 504, message: \"Gateway Timeout\" };\n public static HTTPVersionNotSupported = { statusCode: 505, message: \"HTTP Version Not Supported\" };\n public static VariantAlsoNegotiates = { statusCode: 506, message: \"Variant Also Negotiates\" };\n public static InsufficientStorage = { statusCode: 507, message: \"Insufficient Storage\" };\n public static LoopDetected = { statusCode: 508, message: \"Loop Detected\" };\n public static NotExtended = { statusCode: 510, message: \"Not Extended\" };\n public static NetworkAuthenticationRequired = { statusCode: 511, message: \"Network Authentication Required\" };\n\n\n constructor(...args: StatusErrorConstructorParameters);\n constructor(\n status: number | Status,\n message?: string,\n ) {\n if (typeof status === \"object\") {\n message ??= status.message;\n status = status.statusCode;\n }\n super(message);\n this.statusCode = status;\n if (!message) {\n throw new HexclaveAssertionError(\"StatusError always requires a message unless a Status object is passed\", { cause: this });\n }\n }\n\n public static isStatusError(error: unknown): error is StatusError {\n // like instanceof, but also works for errors thrown in other realms or by different versions of the same package\n return typeof error === \"object\" && error !== null && \"__stackStatusErrorBrand\" in error && error.__stackStatusErrorBrand === \"stack-status-error-brand-sentinel\";\n }\n\n public isClientError() {\n return this.statusCode >= 400 && this.statusCode < 500;\n }\n\n public isServerError() {\n return !this.isClientError();\n }\n\n public getStatusCode(): number {\n return this.statusCode;\n }\n\n public getBody(): Uint8Array {\n return new TextEncoder().encode(this.message);\n }\n\n public getHeaders(): Record<string, string[]> {\n return {\n \"Content-Type\": [\"text/plain; charset=utf-8\"],\n };\n }\n\n public toDescriptiveJson(): Json {\n return {\n status_code: this.getStatusCode(),\n message: this.message,\n headers: this.getHeaders(),\n };\n }\n\n /**\n * @deprecated this is not a good way to make status errors human-readable, use toDescriptiveJson instead\n */\n public toHttpJson(): Json {\n return {\n status_code: this.statusCode,\n body: this.message,\n headers: this.getHeaders(),\n };\n }\n}\nStatusError.prototype.name = \"StatusError\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"errors.js","names":["globalVar"],"sources":["../../src/utils/errors.tsx"],"sourcesContent":["import { globalVar } from \"./globals\";\nimport { Json } from \"./json\";\nimport { pick } from \"./objects\";\nimport { nicify } from \"./strings\";\n\n\nexport function throwErr(errorMessage: string, extraData?: any): never;\nexport function throwErr(error: Error): never;\nexport function throwErr(...args: StatusErrorConstructorParameters): never;\nexport function throwErr(...args: any[]): never {\n if (typeof args[0] === \"string\") {\n throw new HexclaveAssertionError(args[0], args[1]);\n } else if (args[0] instanceof Error) {\n throw args[0];\n } else {\n // @ts-expect-error\n throw new StatusError(...args);\n }\n}\n\nfunction removeStacktraceNameLine(stack: string): string {\n // some browsers (eg. Chrome) prepend the stack with an extra line with the error name\n const addsNameLine = new Error().stack?.startsWith(\"Error\\n\");\n return stack.split(\"\\n\").slice(addsNameLine ? 1 : 0).join(\"\\n\");\n}\n\n\n/**\n * Concatenates the (original) stacktraces of the given errors onto the first.\n *\n * Note: Very often, the concatStacktracesIfRejected function in promises.tsx is an easier way to use this function.\n *\n * Useful when you invoke an async function to receive a promise without awaiting it immediately. Browsers are smart\n * enough to keep track of the call stack in async function calls when you invoke `.then` within the same async tick,\n * but if you don't, the stacktrace will be lost.\n *\n * Here's an example of the unwanted behavior:\n *\n * ```tsx\n * async function log() {\n * await wait(0); // put the task on the event loop\n * console.log(new Error().stack);\n * }\n *\n * async function main() {\n * await log(); // good; prints both \"log\" and \"main\" on the stacktrace\n * log(); // bad; prints only \"log\" on the stacktrace\n * }\n * ```\n */\nexport function concatStacktraces(first: Error, ...errors: Error[]): void {\n // some browsers (eg. Firefox) add an extra empty line at the end\n const addsEmptyLineAtEnd = first.stack?.endsWith(\"\\n\");\n\n\n // Add a reference to this function itself so that we know that stacktraces were concatenated\n // If you are coming here from a stacktrace, please know that the two parts before and after this line are different\n // stacktraces that were concatenated with concatStacktraces\n const separator = removeStacktraceNameLine(new Error().stack ?? \"\").split(\"\\n\")[0];\n\n\n for (const error of errors) {\n const toAppend = removeStacktraceNameLine(error.stack ?? \"\");\n first.stack += (addsEmptyLineAtEnd ? \"\" : \"\\n\") + separator + \"\\n\" + toAppend;\n }\n}\n\n\nexport class HexclaveAssertionError extends Error {\n constructor(message: string, public readonly extraData?: Record<string, any> & ErrorOptions) {\n const disclaimer = `\\n\\nThis is likely an error in Hexclave. Please make sure you are running the newest version and report it.`;\n super(`${message}${message.endsWith(disclaimer) ? \"\" : disclaimer}`, pick(extraData ?? {}, [\"cause\"]));\n\n Object.defineProperty(this, \"customCaptureExtraArgs\", {\n get() {\n return [this.extraData];\n },\n enumerable: false,\n });\n\n // Use literal dot-form (guarded with `typeof process`) so Next.js / webpack\n // DefinePlugin can inline the value at build time. See getProcessEnv in ./env.\n if ((typeof process !== \"undefined\" ? process.env.NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR : undefined) === \"true\") {\n debugger;\n }\n }\n}\nHexclaveAssertionError.prototype.name = \"HexclaveAssertionError\";\n\n\nexport function errorToNiceString(error: unknown): string {\n if (!(error instanceof Error)) return `${typeof error}<${nicify(error)}>`;\n return nicify(error, { maxDepth: 8 });\n}\n\n\nexport type CaptureLevel = \"error\" | \"warning\";\n\ntype ErrorSink = (location: string, error: unknown, level: CaptureLevel, ...extraArgs: unknown[]) => void;\n\nconst errorSinks = new Set<ErrorSink>();\nexport function registerErrorSink(sink: ErrorSink): void {\n if (errorSinks.has(sink)) {\n return;\n }\n errorSinks.add(sink);\n}\nregisterErrorSink((location, error, level, ...extraArgs) => {\n const logger = level === \"warning\" ? console.warn : console.error;\n const colorCode = level === \"warning\" ? \"\\x1b[43m\" : \"\\x1b[41m\";\n const label = level === \"warning\" ? \"warning\" : \"error\";\n logger(\n `${colorCode}Captured ${label} in ${location}:`,\n // HACK: Log a nicified version of the error to get around buggy Next.js pretty-printing\n // https://www.reddit.com/r/nextjs/comments/1gkxdqe/comment/m19kxgn/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button\n errorToNiceString(error),\n ...extraArgs,\n \"\\x1b[0m\",\n );\n});\nregisterErrorSink((location, error, level, ...extraArgs) => {\n globalVar.stackCapturedErrors = globalVar.stackCapturedErrors ?? [];\n globalVar.stackCapturedErrors.push({ location, error, level, extraArgs });\n});\n\nfunction dispatchToSinks(location: string, error: unknown, level: CaptureLevel): void {\n for (const sink of errorSinks) {\n sink(\n location,\n error,\n level,\n ...error && (typeof error === 'object' || typeof error === 'function') && \"customCaptureExtraArgs\" in error && Array.isArray(error.customCaptureExtraArgs) ? (error.customCaptureExtraArgs as any[]) : [],\n );\n }\n}\n\n/**\n * Captures an error and sends it to the error sinks (most notably, Sentry). Errors caught with captureError are\n * supposed to be seen by an engineer, so they should be actionable and important.\n *\n * The location string is a machine-readable ID, and should hence not contain spaces or anything like that. Good\n * examples are: \"api-route-handler\", \"renderPart()\", etc.\n *\n * Errors that bubble up to the top of runAsynchronously or a route handler are already captured with captureError.\n */\nexport function captureError(location: string, error: unknown): void {\n dispatchToSinks(location, error, \"error\");\n}\n\n/**\n * Like captureError, but logs at warning level. Use for issues that an engineer should know about but that aren't\n * severe enough to be treated as an error (e.g. recoverable failures in background tasks).\n */\nexport function captureWarning(location: string, error: unknown): void {\n dispatchToSinks(location, error, \"warning\");\n}\n\n\ntype Status = {\n statusCode: number,\n message: string,\n};\n\ntype StatusErrorConstructorParameters =\n| [\n status: Status,\n message?: string\n]\n| [\n statusCode: number | Status,\n message: string,\n];\n\nexport class StatusError extends Error {\n private readonly __stackStatusErrorBrand = \"stack-status-error-brand-sentinel\" as const;\n public name = \"StatusError\";\n public readonly statusCode: number;\n\n public static BadRequest = { statusCode: 400, message: \"Bad Request\" };\n public static Unauthorized = { statusCode: 401, message: \"Unauthorized\" };\n public static PaymentRequired = { statusCode: 402, message: \"Payment Required\" };\n public static Forbidden = { statusCode: 403, message: \"Forbidden\" };\n public static NotFound = { statusCode: 404, message: \"Not Found\" };\n public static MethodNotAllowed = { statusCode: 405, message: \"Method Not Allowed\" };\n public static NotAcceptable = { statusCode: 406, message: \"Not Acceptable\" };\n public static ProxyAuthenticationRequired = { statusCode: 407, message: \"Proxy Authentication Required\" };\n public static RequestTimeout = { statusCode: 408, message: \"Request Timeout\" };\n public static Conflict = { statusCode: 409, message: \"Conflict\" };\n public static Gone = { statusCode: 410, message: \"Gone\" };\n public static LengthRequired = { statusCode: 411, message: \"Length Required\" };\n public static PreconditionFailed = { statusCode: 412, message: \"Precondition Failed\" };\n public static PayloadTooLarge = { statusCode: 413, message: \"Payload Too Large\" };\n public static URITooLong = { statusCode: 414, message: \"URI Too Long\" };\n public static UnsupportedMediaType = { statusCode: 415, message: \"Unsupported Media Type\" };\n public static RangeNotSatisfiable = { statusCode: 416, message: \"Range Not Satisfiable\" };\n public static ExpectationFailed = { statusCode: 417, message: \"Expectation Failed\" };\n public static ImATeapot = { statusCode: 418, message: \"I'm a teapot\" };\n public static MisdirectedRequest = { statusCode: 421, message: \"Misdirected Request\" };\n public static UnprocessableEntity = { statusCode: 422, message: \"Unprocessable Entity\" };\n public static Locked = { statusCode: 423, message: \"Locked\" };\n public static FailedDependency = { statusCode: 424, message: \"Failed Dependency\" };\n public static TooEarly = { statusCode: 425, message: \"Too Early\" };\n public static UpgradeRequired = { statusCode: 426, message: \"Upgrade Required\" };\n public static PreconditionRequired = { statusCode: 428, message: \"Precondition Required\" };\n public static TooManyRequests = { statusCode: 429, message: \"Too Many Requests\" };\n public static RequestHeaderFieldsTooLarge = { statusCode: 431, message: \"Request Header Fields Too Large\" };\n public static UnavailableForLegalReasons = { statusCode: 451, message: \"Unavailable For Legal Reasons\" };\n\n public static InternalServerError = { statusCode: 500, message: \"Internal Server Error\" };\n public static NotImplemented = { statusCode: 501, message: \"Not Implemented\" };\n public static BadGateway = { statusCode: 502, message: \"Bad Gateway\" };\n public static ServiceUnavailable = { statusCode: 503, message: \"Service Unavailable\" };\n public static GatewayTimeout = { statusCode: 504, message: \"Gateway Timeout\" };\n public static HTTPVersionNotSupported = { statusCode: 505, message: \"HTTP Version Not Supported\" };\n public static VariantAlsoNegotiates = { statusCode: 506, message: \"Variant Also Negotiates\" };\n public static InsufficientStorage = { statusCode: 507, message: \"Insufficient Storage\" };\n public static LoopDetected = { statusCode: 508, message: \"Loop Detected\" };\n public static NotExtended = { statusCode: 510, message: \"Not Extended\" };\n public static NetworkAuthenticationRequired = { statusCode: 511, message: \"Network Authentication Required\" };\n\n\n constructor(...args: StatusErrorConstructorParameters);\n constructor(\n status: number | Status,\n message?: string,\n ) {\n if (typeof status === \"object\") {\n message ??= status.message;\n status = status.statusCode;\n }\n super(message);\n this.statusCode = status;\n if (!message) {\n throw new HexclaveAssertionError(\"StatusError always requires a message unless a Status object is passed\", { cause: this });\n }\n }\n\n public static isStatusError(error: unknown): error is StatusError {\n // like instanceof, but also works for errors thrown in other realms or by different versions of the same package\n return typeof error === \"object\" && error !== null && \"__stackStatusErrorBrand\" in error && error.__stackStatusErrorBrand === \"stack-status-error-brand-sentinel\";\n }\n\n public isClientError() {\n return this.statusCode >= 400 && this.statusCode < 500;\n }\n\n public isServerError() {\n return !this.isClientError();\n }\n\n public getStatusCode(): number {\n return this.statusCode;\n }\n\n public getBody(): Uint8Array {\n return new TextEncoder().encode(this.message);\n }\n\n public getHeaders(): Record<string, string[]> {\n return {\n \"Content-Type\": [\"text/plain; charset=utf-8\"],\n };\n }\n\n public toDescriptiveJson(): Json {\n return {\n status_code: this.getStatusCode(),\n message: this.message,\n headers: this.getHeaders(),\n };\n }\n\n /**\n * @deprecated this is not a good way to make status errors human-readable, use toDescriptiveJson instead\n */\n public toHttpJson(): Json {\n return {\n status_code: this.statusCode,\n body: this.message,\n headers: this.getHeaders(),\n };\n }\n}\nStatusError.prototype.name = \"StatusError\";\n"],"mappings":"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"}
|
package/dist/utils/passkey.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as RegistrationResponseJSON, i as PublicKeyCredentialRequestOptionsJSON, n as AuthenticatorAttestationResponseJSON, r as PublicKeyCredentialCreationOptionsJSON, t as AuthenticationResponseJSON } from "../index-
|
|
1
|
+
import { a as RegistrationResponseJSON, i as PublicKeyCredentialRequestOptionsJSON, n as AuthenticatorAttestationResponseJSON, r as PublicKeyCredentialCreationOptionsJSON, t as AuthenticationResponseJSON } from "../index-fQbeILq7.js";
|
|
2
2
|
export { type AuthenticationResponseJSON, type AuthenticatorAttestationResponseJSON, type PublicKeyCredentialCreationOptionsJSON, type PublicKeyCredentialRequestOptionsJSON, type RegistrationResponseJSON };
|
package/dist/utils/promises.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_chunk = require('../chunk-BE-pF4vm.js');
|
|
3
|
+
let ___index_js = require("../index.js");
|
|
3
4
|
let __errors_js = require("./errors.js");
|
|
4
5
|
let __env_js = require("./env.js");
|
|
5
6
|
let __maps_js = require("./maps.js");
|
|
6
7
|
let __results_js = require("./results.js");
|
|
7
8
|
let __telemetry_js = require("./telemetry.js");
|
|
8
|
-
let ___index_js = require("../index.js");
|
|
9
9
|
let __uuids_js = require("./uuids.js");
|
|
10
10
|
|
|
11
11
|
//#region src/utils/promises.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redirect-urls.d.ts","names":[],"sources":["../../src/utils/redirect-urls.tsx"],"mappings":";KAGK,mBAAA;EACH,cAAA;EACA,cAAA;AAAA;AAAA,iBAgCc,6BAAA,CAA8B,OAAA;EAC5C,SAAA;EACA,yBAAA;EACA,wBAAA;EACA,eAAA;AAAA;AAAA,iBAQc,6BAAA,CAA8B,OAAA;EAC5C,SAAA;EACA,UAAA;EACA,yBAAA;EACA,wBAAA;EACA,eAAA;AAAA;AAAA,iBA0Bc,qCAAA,CAAsC,OAAA;EACpD,SAAA;EACA,yBAAA;EACA,wBAAA;EACA,eAAA;AAAA;AAAA,iBA0Ec,sBAAA,CAAuB,WAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"redirect-urls.d.ts","names":[],"sources":["../../src/utils/redirect-urls.tsx"],"mappings":";KAGK,mBAAA;EACH,cAAA;EACA,cAAA;AAAA;AAAA,iBAgCc,6BAAA,CAA8B,OAAA;EAC5C,SAAA;EACA,yBAAA;EACA,wBAAA;EACA,eAAA;AAAA;AAAA,iBAQc,6BAAA,CAA8B,OAAA;EAC5C,SAAA;EACA,UAAA;EACA,yBAAA;EACA,wBAAA;EACA,eAAA;AAAA;AAAA,iBA0Bc,qCAAA,CAAsC,OAAA;EACpD,SAAA;EACA,yBAAA;EACA,wBAAA;EACA,eAAA;AAAA;AAAA,iBA0Ec,sBAAA,CAAuB,WAAA;AAAA,iBASvB,mBAAA,CACd,WAAA,WAAsB,GAAA,EACtB,MAAA,EAAQ,mBAAA;AAAA,iBAYM,sBAAA,CAAuB,aAAA,UAAuB,cAAA"}
|
|
@@ -12,7 +12,7 @@ const hostedHandlerTemplateProjectIdA = "00000000-0000-4000-8000-000000000000";
|
|
|
12
12
|
const hostedHandlerTemplateProjectIdB = "11111111-1111-4111-8111-111111111111";
|
|
13
13
|
function replaceStackPortPrefix(input, stackPortPrefix) {
|
|
14
14
|
if (input == null) return void 0;
|
|
15
|
-
return stackPortPrefix ? input.replace(/\$\{
|
|
15
|
+
return stackPortPrefix ? input.replace(/\$\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\}/g, stackPortPrefix) : input;
|
|
16
16
|
}
|
|
17
17
|
function getHostedHandlerUrlFromTemplate(template, projectId, hostedPath) {
|
|
18
18
|
return template.replaceAll(hostedHandlerProjectIdPlaceholder, projectId).replaceAll(hostedHandlerPathPlaceholder, hostedPath);
|
|
@@ -100,7 +100,7 @@ function matchesTrustedDomain(testUrl, pattern) {
|
|
|
100
100
|
function isAcceptedNativeAppUrl(urlOrString) {
|
|
101
101
|
const url = (0, __urls_js.createUrlIfValid)(urlOrString);
|
|
102
102
|
if (!url) return false;
|
|
103
|
-
return url.protocol === "stack-auth-mobile-oauth-url:";
|
|
103
|
+
return url.protocol === "stack-auth-mobile-oauth-url:" || url.protocol === "hexclave-mobile-oauth-url:";
|
|
104
104
|
}
|
|
105
105
|
function validateRedirectUrl(urlOrString, config) {
|
|
106
106
|
const url = (0, __urls_js.createUrlIfValid)(urlOrString);
|
|
@@ -150,7 +150,7 @@ function getTrustedParentDomain(currentDomain, trustedDomains) {
|
|
|
150
150
|
allowLocalhost: false,
|
|
151
151
|
trustedDomains: getImplicitlyTrustedDomainsForProject({
|
|
152
152
|
projectId,
|
|
153
|
-
hostedHandlerUrlTemplate: "http://{projectId}.localhost:${
|
|
153
|
+
hostedHandlerUrlTemplate: "http://{projectId}.localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09/{hostedPath}",
|
|
154
154
|
stackPortPrefix: "92"
|
|
155
155
|
})
|
|
156
156
|
})).toBe(true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redirect-urls.js","names":[],"sources":["../../src/utils/redirect-urls.tsx"],"sourcesContent":["import { HexclaveAssertionError, captureError } from \"./errors\";\nimport { createUrlIfValid, isLocalhost, matchHostnamePattern } from \"./urls\";\n\ntype TrustedDomainConfig = {\n allowLocalhost?: boolean,\n trustedDomains: readonly (string | null | undefined)[],\n};\n\nconst defaultHostedHandlerDomainSuffix = \".built-with-stack-auth.com\";\nconst hostedHandlerProjectIdPlaceholder = \"{projectId}\";\nconst hostedHandlerPathPlaceholder = \"{hostedPath}\";\nconst defaultPorts = new Map<string, string>([['https:', '443'], ['http:', '80']]);\nconst hostedHandlerTemplateProjectIdA = \"00000000-0000-4000-8000-000000000000\";\nconst hostedHandlerTemplateProjectIdB = \"11111111-1111-4111-8111-111111111111\";\n\nfunction replaceStackPortPrefix(input: string | undefined, stackPortPrefix: string | undefined): string | undefined {\n if (input == null) return undefined;\n return stackPortPrefix ? input.replace(/\\$\\{NEXT_PUBLIC_STACK_PORT_PREFIX:-81\\}/g, stackPortPrefix) : input;\n}\n\nfunction getHostedHandlerUrlFromTemplate(template: string, projectId: string, hostedPath: string): string {\n return template\n .replaceAll(hostedHandlerProjectIdPlaceholder, projectId)\n .replaceAll(hostedHandlerPathPlaceholder, hostedPath);\n}\n\nfunction assertHostedHandlerTemplateHasProjectOrigin(template: string): void {\n const projectUrlA = new URL(getHostedHandlerUrlFromTemplate(template, hostedHandlerTemplateProjectIdA, \"handler\"));\n const projectUrlB = new URL(getHostedHandlerUrlFromTemplate(template, hostedHandlerTemplateProjectIdB, \"handler\"));\n if (projectUrlA.origin === projectUrlB.origin || !projectUrlA.hostname.includes(hostedHandlerTemplateProjectIdA)) {\n throw new HexclaveAssertionError(\"The hosted handler URL template must put {projectId} in the hostname.\", {\n hostedHandlerUrlTemplate: template,\n hint: \"Use a project-specific origin like 'https://{projectId}.built-with-stack-auth.com/{hostedPath}', not a shared-origin path like 'https://example.com/{projectId}/{hostedPath}'.\",\n });\n }\n}\n\nexport function getHostedHandlerTrustedDomain(options: {\n projectId: string,\n hostedHandlerDomainSuffix?: string,\n hostedHandlerUrlTemplate?: string,\n stackPortPrefix?: string,\n}): string {\n return new URL(getHostedHandlerUrlFromConfig({\n ...options,\n hostedPath: \"handler\",\n })).origin;\n}\n\nexport function getHostedHandlerUrlFromConfig(options: {\n projectId: string,\n hostedPath: string,\n hostedHandlerDomainSuffix?: string,\n hostedHandlerUrlTemplate?: string,\n stackPortPrefix?: string,\n}): string {\n const configuredTemplate = replaceStackPortPrefix(options.hostedHandlerUrlTemplate, options.stackPortPrefix);\n return configuredTemplate == null\n ? (() => {\n const domainSuffix = replaceStackPortPrefix(options.hostedHandlerDomainSuffix, options.stackPortPrefix) ?? defaultHostedHandlerDomainSuffix;\n if (!domainSuffix.startsWith(\".\")) {\n throw new HexclaveAssertionError(\"The hosted handler domain suffix must start with a dot.\", {\n domainSuffix,\n hint: \"Set NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX to a value like '.built-with-stack-auth.com'.\",\n });\n }\n return `https://${options.projectId}${domainSuffix}/${options.hostedPath}`;\n })()\n : (() => {\n if (!configuredTemplate.includes(hostedHandlerProjectIdPlaceholder) || !configuredTemplate.includes(hostedHandlerPathPlaceholder)) {\n throw new HexclaveAssertionError(\"The hosted handler URL template must contain {projectId} and {hostedPath}.\", {\n hostedHandlerUrlTemplate: configuredTemplate,\n hint: \"Set NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE to a value like 'https://{projectId}.built-with-stack-auth.com/{hostedPath}'.\",\n });\n }\n assertHostedHandlerTemplateHasProjectOrigin(configuredTemplate);\n return getHostedHandlerUrlFromTemplate(configuredTemplate, options.projectId, options.hostedPath);\n })();\n}\n\nexport function getImplicitlyTrustedDomainsForProject(options: {\n projectId: string,\n hostedHandlerDomainSuffix?: string,\n hostedHandlerUrlTemplate?: string,\n stackPortPrefix?: string,\n}): string[] {\n return [getHostedHandlerTrustedDomain(options)];\n}\n\nfunction normalizePort(url: URL): string {\n const port = url.port || defaultPorts.get(url.protocol) || '';\n return port ? `${url.hostname}:${port}` : url.hostname;\n}\n\nfunction isDefaultPort(url: URL): boolean {\n return !url.port ||\n (url.protocol === 'https:' && url.port === '443') ||\n (url.protocol === 'http:' && url.port === '80');\n}\n\nfunction portsMatch(url1: URL, url2: URL): boolean {\n return normalizePort(url1) === normalizePort(url2);\n}\n\nfunction parseWildcardUrlPattern(pattern: string): { protocol: string, hostPattern: string } | null {\n const protocolSeparatorIndex = pattern.indexOf(\"://\");\n if (protocolSeparatorIndex === -1) return null;\n\n const protocol = `${pattern.slice(0, protocolSeparatorIndex)}:`;\n const hostAndPath = pattern.slice(protocolSeparatorIndex + \"://\".length);\n const pathStartIndex = hostAndPath.indexOf(\"/\");\n const hostPattern = pathStartIndex === -1 ? hostAndPath : hostAndPath.slice(0, pathStartIndex);\n if (hostPattern === \"\") return null;\n return { protocol, hostPattern };\n}\n\nfunction hostPatternWithoutPort(hostPattern: string): string {\n if (!hostPatternHasExplicitPort(hostPattern)) {\n return hostPattern;\n }\n const portSeparatorIndex = hostPattern.lastIndexOf(\":\");\n return hostPattern.slice(0, portSeparatorIndex);\n}\n\nfunction hostPatternHasExplicitPort(hostPattern: string): boolean {\n const portSeparatorIndex = hostPattern.lastIndexOf(\":\");\n if (portSeparatorIndex === -1) {\n return false;\n }\n const port = hostPattern.slice(portSeparatorIndex + 1);\n return port === \"*\" || (port !== \"\" && [...port].every(char => char >= \"0\" && char <= \"9\"));\n}\n\nfunction matchesTrustedDomain(testUrl: URL, pattern: string): boolean {\n const baseUrl = createUrlIfValid(pattern);\n\n if (baseUrl != null && !pattern.includes('*')) {\n return baseUrl.protocol === testUrl.protocol &&\n baseUrl.hostname === testUrl.hostname &&\n portsMatch(baseUrl, testUrl);\n }\n\n const parsedPattern = parseWildcardUrlPattern(pattern);\n if (parsedPattern == null) {\n captureError(\"invalid-redirect-domain\", new HexclaveAssertionError(\"Invalid domain pattern\", { pattern }));\n return false;\n }\n\n if (testUrl.protocol !== parsedPattern.protocol) {\n return false;\n }\n\n const hasPortInPattern = hostPatternHasExplicitPort(parsedPattern.hostPattern);\n return hasPortInPattern\n ? matchHostnamePattern(parsedPattern.hostPattern, normalizePort(testUrl))\n : matchHostnamePattern(parsedPattern.hostPattern, testUrl.hostname) && isDefaultPort(testUrl);\n}\n\nexport function isAcceptedNativeAppUrl(urlOrString: string): boolean {\n const url = createUrlIfValid(urlOrString);\n if (!url) return false;\n\n return url.protocol === 'stack-auth-mobile-oauth-url:';\n}\n\nexport function validateRedirectUrl(\n urlOrString: string | URL,\n config: TrustedDomainConfig,\n): boolean {\n const url = createUrlIfValid(urlOrString);\n if (!url) return false;\n\n if (config.allowLocalhost === true && isLocalhost(url)) {\n return true;\n }\n\n return config.trustedDomains.some(domain => domain != null && matchesTrustedDomain(url, domain));\n}\n\nexport function getTrustedParentDomain(currentDomain: string, trustedDomains: readonly (string | null | undefined)[]): string | null {\n const hostPatterns = trustedDomains\n .filter((domain): domain is string => domain != null)\n .map((domain) => {\n const url = createUrlIfValid(domain);\n if (url != null && !domain.includes(\"*\")) {\n return url.hostname.toLowerCase();\n }\n const parsedPattern = parseWildcardUrlPattern(domain);\n return parsedPattern == null ? null : hostPatternWithoutPort(parsedPattern.hostPattern).toLowerCase();\n })\n .filter((domain): domain is string => domain != null);\n\n const parts = currentDomain.toLowerCase().split('.');\n for (let i = parts.length - 2; i >= 0; i--) {\n const parentDomain = parts.slice(i).join('.');\n if (hostPatterns.includes(parentDomain) && hostPatterns.includes(`**.${parentDomain}`)) {\n return parentDomain;\n }\n }\n\n return null;\n}\n\nimport.meta.vitest?.test(\"validateRedirectUrl matches exact and wildcard trusted domains\", ({ expect }) => {\n expect(validateRedirectUrl(\"https://example.com\", {\n allowLocalhost: false,\n trustedDomains: [\"https://example.com\"],\n })).toBe(true);\n expect(validateRedirectUrl(\"https://api.example.com\", {\n allowLocalhost: false,\n trustedDomains: [\"https://*.example.com\"],\n })).toBe(true);\n expect(validateRedirectUrl(\"https://api.v2.example.com\", {\n allowLocalhost: false,\n trustedDomains: [\"https://*.example.com\"],\n })).toBe(false);\n});\n\nimport.meta.vitest?.test(\"validateRedirectUrl trusts implicit hosted handler domains\", ({ expect }) => {\n const projectId = \"12345678-1234-4234-8234-123456789abc\";\n expect(validateRedirectUrl(`https://${projectId}.built-with-stack-auth.com/anything`, {\n allowLocalhost: false,\n trustedDomains: getImplicitlyTrustedDomainsForProject({ projectId }),\n })).toBe(true);\n expect(validateRedirectUrl(\"https://other-project.built-with-stack-auth.com/anything\", {\n allowLocalhost: false,\n trustedDomains: getImplicitlyTrustedDomainsForProject({ projectId }),\n })).toBe(false);\n expect(validateRedirectUrl(`http://${projectId}.localhost:9209/anything`, {\n allowLocalhost: false,\n trustedDomains: getImplicitlyTrustedDomainsForProject({\n projectId,\n hostedHandlerUrlTemplate: \"http://{projectId}.localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09/{hostedPath}\",\n stackPortPrefix: \"92\",\n }),\n })).toBe(true);\n});\n\nimport.meta.vitest?.test(\"getImplicitlyTrustedDomainsForProject rejects shared-origin hosted templates\", ({ expect }) => {\n expect(() => getImplicitlyTrustedDomainsForProject({\n projectId: \"12345678-1234-4234-8234-123456789abc\",\n hostedHandlerUrlTemplate: \"https://host.example.com/{projectId}/{hostedPath}\",\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\nimport.meta.vitest?.test(\"validateRedirectUrl respects default and explicit ports\", ({ expect }) => {\n expect(validateRedirectUrl(\"https://example.com:443/path\", {\n allowLocalhost: false,\n trustedDomains: [\"https://example.com\"],\n })).toBe(true);\n expect(validateRedirectUrl(\"http://api.example.com:3000\", {\n allowLocalhost: false,\n trustedDomains: [\"http://*.example.com:3000\"],\n })).toBe(true);\n expect(validateRedirectUrl(\"http://api.example.com\", {\n allowLocalhost: false,\n trustedDomains: [\"http://*.example.com:3000\"],\n })).toBe(false);\n expect(validateRedirectUrl(\"http://api.example.com:1234\", {\n allowLocalhost: false,\n trustedDomains: [\"http://*.example.com:*\"],\n })).toBe(true);\n});\n\nimport.meta.vitest?.test(\"validateRedirectUrl respects localhost allowance and invalid patterns\", ({ expect }) => {\n const originalConsoleError = console.error;\n console.error = () => {};\n try {\n expect(validateRedirectUrl(\"http://localhost:3000\", {\n allowLocalhost: true,\n trustedDomains: [],\n })).toBe(true);\n expect(validateRedirectUrl(\"http://localhost:3000\", {\n allowLocalhost: false,\n trustedDomains: [],\n })).toBe(false);\n expect(validateRedirectUrl(\"https://example.com\", {\n allowLocalhost: false,\n trustedDomains: [\"not a url\"],\n })).toBe(false);\n } finally {\n console.error = originalConsoleError;\n }\n});\n\nimport.meta.vitest?.test(\"getTrustedParentDomain ignores empty entries and strips ports\", ({ expect }) => {\n expect(getTrustedParentDomain(\"app.example.com\", [\n null,\n undefined,\n \"https://example.com\",\n \"https://**.example.com:*\",\n ])).toBe(\"example.com\");\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"redirect-urls.js","names":[],"sources":["../../src/utils/redirect-urls.tsx"],"sourcesContent":["import { HexclaveAssertionError, captureError } from \"./errors\";\nimport { createUrlIfValid, isLocalhost, matchHostnamePattern } from \"./urls\";\n\ntype TrustedDomainConfig = {\n allowLocalhost?: boolean,\n trustedDomains: readonly (string | null | undefined)[],\n};\n\nconst defaultHostedHandlerDomainSuffix = \".built-with-stack-auth.com\";\nconst hostedHandlerProjectIdPlaceholder = \"{projectId}\";\nconst hostedHandlerPathPlaceholder = \"{hostedPath}\";\nconst defaultPorts = new Map<string, string>([['https:', '443'], ['http:', '80']]);\nconst hostedHandlerTemplateProjectIdA = \"00000000-0000-4000-8000-000000000000\";\nconst hostedHandlerTemplateProjectIdB = \"11111111-1111-4111-8111-111111111111\";\n\nfunction replaceStackPortPrefix(input: string | undefined, stackPortPrefix: string | undefined): string | undefined {\n if (input == null) return undefined;\n return stackPortPrefix ? input.replace(/\\$\\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\\}/g, stackPortPrefix) : input;\n}\n\nfunction getHostedHandlerUrlFromTemplate(template: string, projectId: string, hostedPath: string): string {\n return template\n .replaceAll(hostedHandlerProjectIdPlaceholder, projectId)\n .replaceAll(hostedHandlerPathPlaceholder, hostedPath);\n}\n\nfunction assertHostedHandlerTemplateHasProjectOrigin(template: string): void {\n const projectUrlA = new URL(getHostedHandlerUrlFromTemplate(template, hostedHandlerTemplateProjectIdA, \"handler\"));\n const projectUrlB = new URL(getHostedHandlerUrlFromTemplate(template, hostedHandlerTemplateProjectIdB, \"handler\"));\n if (projectUrlA.origin === projectUrlB.origin || !projectUrlA.hostname.includes(hostedHandlerTemplateProjectIdA)) {\n throw new HexclaveAssertionError(\"The hosted handler URL template must put {projectId} in the hostname.\", {\n hostedHandlerUrlTemplate: template,\n hint: \"Use a project-specific origin like 'https://{projectId}.built-with-stack-auth.com/{hostedPath}', not a shared-origin path like 'https://example.com/{projectId}/{hostedPath}'.\",\n });\n }\n}\n\nexport function getHostedHandlerTrustedDomain(options: {\n projectId: string,\n hostedHandlerDomainSuffix?: string,\n hostedHandlerUrlTemplate?: string,\n stackPortPrefix?: string,\n}): string {\n return new URL(getHostedHandlerUrlFromConfig({\n ...options,\n hostedPath: \"handler\",\n })).origin;\n}\n\nexport function getHostedHandlerUrlFromConfig(options: {\n projectId: string,\n hostedPath: string,\n hostedHandlerDomainSuffix?: string,\n hostedHandlerUrlTemplate?: string,\n stackPortPrefix?: string,\n}): string {\n const configuredTemplate = replaceStackPortPrefix(options.hostedHandlerUrlTemplate, options.stackPortPrefix);\n return configuredTemplate == null\n ? (() => {\n const domainSuffix = replaceStackPortPrefix(options.hostedHandlerDomainSuffix, options.stackPortPrefix) ?? defaultHostedHandlerDomainSuffix;\n if (!domainSuffix.startsWith(\".\")) {\n throw new HexclaveAssertionError(\"The hosted handler domain suffix must start with a dot.\", {\n domainSuffix,\n hint: \"Set NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX to a value like '.built-with-stack-auth.com'.\",\n });\n }\n return `https://${options.projectId}${domainSuffix}/${options.hostedPath}`;\n })()\n : (() => {\n if (!configuredTemplate.includes(hostedHandlerProjectIdPlaceholder) || !configuredTemplate.includes(hostedHandlerPathPlaceholder)) {\n throw new HexclaveAssertionError(\"The hosted handler URL template must contain {projectId} and {hostedPath}.\", {\n hostedHandlerUrlTemplate: configuredTemplate,\n hint: \"Set NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE to a value like 'https://{projectId}.built-with-stack-auth.com/{hostedPath}'.\",\n });\n }\n assertHostedHandlerTemplateHasProjectOrigin(configuredTemplate);\n return getHostedHandlerUrlFromTemplate(configuredTemplate, options.projectId, options.hostedPath);\n })();\n}\n\nexport function getImplicitlyTrustedDomainsForProject(options: {\n projectId: string,\n hostedHandlerDomainSuffix?: string,\n hostedHandlerUrlTemplate?: string,\n stackPortPrefix?: string,\n}): string[] {\n return [getHostedHandlerTrustedDomain(options)];\n}\n\nfunction normalizePort(url: URL): string {\n const port = url.port || defaultPorts.get(url.protocol) || '';\n return port ? `${url.hostname}:${port}` : url.hostname;\n}\n\nfunction isDefaultPort(url: URL): boolean {\n return !url.port ||\n (url.protocol === 'https:' && url.port === '443') ||\n (url.protocol === 'http:' && url.port === '80');\n}\n\nfunction portsMatch(url1: URL, url2: URL): boolean {\n return normalizePort(url1) === normalizePort(url2);\n}\n\nfunction parseWildcardUrlPattern(pattern: string): { protocol: string, hostPattern: string } | null {\n const protocolSeparatorIndex = pattern.indexOf(\"://\");\n if (protocolSeparatorIndex === -1) return null;\n\n const protocol = `${pattern.slice(0, protocolSeparatorIndex)}:`;\n const hostAndPath = pattern.slice(protocolSeparatorIndex + \"://\".length);\n const pathStartIndex = hostAndPath.indexOf(\"/\");\n const hostPattern = pathStartIndex === -1 ? hostAndPath : hostAndPath.slice(0, pathStartIndex);\n if (hostPattern === \"\") return null;\n return { protocol, hostPattern };\n}\n\nfunction hostPatternWithoutPort(hostPattern: string): string {\n if (!hostPatternHasExplicitPort(hostPattern)) {\n return hostPattern;\n }\n const portSeparatorIndex = hostPattern.lastIndexOf(\":\");\n return hostPattern.slice(0, portSeparatorIndex);\n}\n\nfunction hostPatternHasExplicitPort(hostPattern: string): boolean {\n const portSeparatorIndex = hostPattern.lastIndexOf(\":\");\n if (portSeparatorIndex === -1) {\n return false;\n }\n const port = hostPattern.slice(portSeparatorIndex + 1);\n return port === \"*\" || (port !== \"\" && [...port].every(char => char >= \"0\" && char <= \"9\"));\n}\n\nfunction matchesTrustedDomain(testUrl: URL, pattern: string): boolean {\n const baseUrl = createUrlIfValid(pattern);\n\n if (baseUrl != null && !pattern.includes('*')) {\n return baseUrl.protocol === testUrl.protocol &&\n baseUrl.hostname === testUrl.hostname &&\n portsMatch(baseUrl, testUrl);\n }\n\n const parsedPattern = parseWildcardUrlPattern(pattern);\n if (parsedPattern == null) {\n captureError(\"invalid-redirect-domain\", new HexclaveAssertionError(\"Invalid domain pattern\", { pattern }));\n return false;\n }\n\n if (testUrl.protocol !== parsedPattern.protocol) {\n return false;\n }\n\n const hasPortInPattern = hostPatternHasExplicitPort(parsedPattern.hostPattern);\n return hasPortInPattern\n ? matchHostnamePattern(parsedPattern.hostPattern, normalizePort(testUrl))\n : matchHostnamePattern(parsedPattern.hostPattern, testUrl.hostname) && isDefaultPort(testUrl);\n}\n\nexport function isAcceptedNativeAppUrl(urlOrString: string): boolean {\n const url = createUrlIfValid(urlOrString);\n if (!url) return false;\n\n // Legacy scheme accepted indefinitely; baked into already-shipped Swift SDK binaries.\n return url.protocol === 'stack-auth-mobile-oauth-url:'\n || url.protocol === 'hexclave-mobile-oauth-url:';\n}\n\nexport function validateRedirectUrl(\n urlOrString: string | URL,\n config: TrustedDomainConfig,\n): boolean {\n const url = createUrlIfValid(urlOrString);\n if (!url) return false;\n\n if (config.allowLocalhost === true && isLocalhost(url)) {\n return true;\n }\n\n return config.trustedDomains.some(domain => domain != null && matchesTrustedDomain(url, domain));\n}\n\nexport function getTrustedParentDomain(currentDomain: string, trustedDomains: readonly (string | null | undefined)[]): string | null {\n const hostPatterns = trustedDomains\n .filter((domain): domain is string => domain != null)\n .map((domain) => {\n const url = createUrlIfValid(domain);\n if (url != null && !domain.includes(\"*\")) {\n return url.hostname.toLowerCase();\n }\n const parsedPattern = parseWildcardUrlPattern(domain);\n return parsedPattern == null ? null : hostPatternWithoutPort(parsedPattern.hostPattern).toLowerCase();\n })\n .filter((domain): domain is string => domain != null);\n\n const parts = currentDomain.toLowerCase().split('.');\n for (let i = parts.length - 2; i >= 0; i--) {\n const parentDomain = parts.slice(i).join('.');\n if (hostPatterns.includes(parentDomain) && hostPatterns.includes(`**.${parentDomain}`)) {\n return parentDomain;\n }\n }\n\n return null;\n}\n\nimport.meta.vitest?.test(\"validateRedirectUrl matches exact and wildcard trusted domains\", ({ expect }) => {\n expect(validateRedirectUrl(\"https://example.com\", {\n allowLocalhost: false,\n trustedDomains: [\"https://example.com\"],\n })).toBe(true);\n expect(validateRedirectUrl(\"https://api.example.com\", {\n allowLocalhost: false,\n trustedDomains: [\"https://*.example.com\"],\n })).toBe(true);\n expect(validateRedirectUrl(\"https://api.v2.example.com\", {\n allowLocalhost: false,\n trustedDomains: [\"https://*.example.com\"],\n })).toBe(false);\n});\n\nimport.meta.vitest?.test(\"validateRedirectUrl trusts implicit hosted handler domains\", ({ expect }) => {\n const projectId = \"12345678-1234-4234-8234-123456789abc\";\n expect(validateRedirectUrl(`https://${projectId}.built-with-stack-auth.com/anything`, {\n allowLocalhost: false,\n trustedDomains: getImplicitlyTrustedDomainsForProject({ projectId }),\n })).toBe(true);\n expect(validateRedirectUrl(\"https://other-project.built-with-stack-auth.com/anything\", {\n allowLocalhost: false,\n trustedDomains: getImplicitlyTrustedDomainsForProject({ projectId }),\n })).toBe(false);\n expect(validateRedirectUrl(`http://${projectId}.localhost:9209/anything`, {\n allowLocalhost: false,\n trustedDomains: getImplicitlyTrustedDomainsForProject({\n projectId,\n hostedHandlerUrlTemplate: \"http://{projectId}.localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09/{hostedPath}\",\n stackPortPrefix: \"92\",\n }),\n })).toBe(true);\n});\n\nimport.meta.vitest?.test(\"getImplicitlyTrustedDomainsForProject rejects shared-origin hosted templates\", ({ expect }) => {\n expect(() => getImplicitlyTrustedDomainsForProject({\n projectId: \"12345678-1234-4234-8234-123456789abc\",\n hostedHandlerUrlTemplate: \"https://host.example.com/{projectId}/{hostedPath}\",\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\nimport.meta.vitest?.test(\"validateRedirectUrl respects default and explicit ports\", ({ expect }) => {\n expect(validateRedirectUrl(\"https://example.com:443/path\", {\n allowLocalhost: false,\n trustedDomains: [\"https://example.com\"],\n })).toBe(true);\n expect(validateRedirectUrl(\"http://api.example.com:3000\", {\n allowLocalhost: false,\n trustedDomains: [\"http://*.example.com:3000\"],\n })).toBe(true);\n expect(validateRedirectUrl(\"http://api.example.com\", {\n allowLocalhost: false,\n trustedDomains: [\"http://*.example.com:3000\"],\n })).toBe(false);\n expect(validateRedirectUrl(\"http://api.example.com:1234\", {\n allowLocalhost: false,\n trustedDomains: [\"http://*.example.com:*\"],\n })).toBe(true);\n});\n\nimport.meta.vitest?.test(\"validateRedirectUrl respects localhost allowance and invalid patterns\", ({ expect }) => {\n const originalConsoleError = console.error;\n console.error = () => {};\n try {\n expect(validateRedirectUrl(\"http://localhost:3000\", {\n allowLocalhost: true,\n trustedDomains: [],\n })).toBe(true);\n expect(validateRedirectUrl(\"http://localhost:3000\", {\n allowLocalhost: false,\n trustedDomains: [],\n })).toBe(false);\n expect(validateRedirectUrl(\"https://example.com\", {\n allowLocalhost: false,\n trustedDomains: [\"not a url\"],\n })).toBe(false);\n } finally {\n console.error = originalConsoleError;\n }\n});\n\nimport.meta.vitest?.test(\"getTrustedParentDomain ignores empty entries and strips ports\", ({ expect }) => {\n expect(getTrustedParentDomain(\"app.example.com\", [\n null,\n undefined,\n \"https://example.com\",\n \"https://**.example.com:*\",\n ])).toBe(\"example.com\");\n});\n"],"mappings":"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"}
|
package/dist/utils/urls.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"urls.d.ts","names":[],"sources":["../../src/utils/urls.tsx"],"mappings":";iBAGgB,gBAAA,CAAA,GAAoB,IAAA,EAAM,qBAAA,QAA6B,GAAA,IAAI,GAAA;AAAA,iBAmB3D,UAAA,CAAW,GAAA;AAAA,iBAeX,eAAA,CAAgB,QAAA;AAAA,iBAwBhB,4BAAA,CAA6B,QAAA;AAAA,iBAkF7B,oBAAA,CAAqB,OAAA,UAAiB,QAAA;AAAA,iBAuDtC,wBAAA,CAAyB,cAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"urls.d.ts","names":[],"sources":["../../src/utils/urls.tsx"],"mappings":";iBAGgB,gBAAA,CAAA,GAAoB,IAAA,EAAM,qBAAA,QAA6B,GAAA,IAAI,GAAA;AAAA,iBAmB3D,UAAA,CAAW,GAAA;AAAA,iBAeX,eAAA,CAAgB,QAAA;AAAA,iBAwBhB,4BAAA,CAA6B,QAAA;AAAA,iBAkF7B,oBAAA,CAAqB,OAAA,UAAiB,QAAA;AAAA,iBAuDtC,wBAAA,CAAyB,cAAA;AAAA,iBAyBzB,iBAAA,CAAkB,cAAA;AAAA,iBAIlB,WAAA,CAAY,WAAA,WAAsB,GAAA;AAAA,iBA+BlC,UAAA,CAAW,GAAA;AAAA,iBA4BX,eAAA,CAAgB,GAAA,EAAK,GAAA;;;;;;iBAsBrB,GAAA,CAAI,OAAA,EAAS,oBAAA,yBAA6C,MAAA,kCAAwC,GAAA;;;;;AA/QlH;iBAmTgB,SAAA,CAAU,OAAA,EAAS,oBAAA,yBAA6C,MAAA;AAAA,iBAyBhE,UAAA,CAAW,SAAA,EAAW,GAAA,EAAK,aAAA,EAAe,GAAA;AAAA,iBAsB1C,WAAA,CAAY,UAAA,UAAoB,cAAA"}
|
package/dist/utils/urls.js
CHANGED
|
@@ -114,10 +114,10 @@ function matchHostnamePattern(pattern, hostname) {
|
|
|
114
114
|
expect(matchHostnamePattern("*.*.org", "example.org")).toBe(false);
|
|
115
115
|
});
|
|
116
116
|
function getHardcodedFallbackUrls(primaryBaseUrl) {
|
|
117
|
-
if (primaryBaseUrl === "https://api.hexclave.com") return ["https://api1.hexclave.com", "https://api2.hexclave.com"];
|
|
118
|
-
if (primaryBaseUrl === "https://api.dev.hexclave.com") return ["https://api1.dev.hexclave.com", "https://api2.dev.hexclave.com"];
|
|
119
117
|
if (primaryBaseUrl === "https://api.stack-auth.com") return ["https://api1.stack-auth.com", "https://api2.stack-auth.com"];
|
|
120
118
|
if (primaryBaseUrl === "https://api.dev.stack-auth.com") return ["https://api1.dev.stack-auth.com", "https://api2.dev.stack-auth.com"];
|
|
119
|
+
if (primaryBaseUrl === "https://api.hexclave.com") return ["https://api1.hexclave.com", "https://api2.hexclave.com"];
|
|
120
|
+
if (primaryBaseUrl === "https://api.dev.hexclave.com") return ["https://api1.dev.hexclave.com", "https://api2.dev.hexclave.com"];
|
|
121
121
|
const localhostMatch = primaryBaseUrl.match(/^http:\/\/localhost:(\d+)02$/);
|
|
122
122
|
if (localhostMatch) return [`http://localhost:${localhostMatch[1]}10`];
|
|
123
123
|
return [];
|
package/dist/utils/urls.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"urls.js","names":[],"sources":["../../src/utils/urls.tsx"],"sourcesContent":["import { generateSecureRandomString } from \"./crypto\";\nimport { templateIdentity } from \"./strings\";\n\nexport function createUrlIfValid(...args: ConstructorParameters<typeof URL>) {\n try {\n return new URL(...args);\n } catch (e) {\n return null;\n }\n}\nimport.meta.vitest?.test(\"createUrlIfValid\", ({ expect }) => {\n // Test with valid URLs\n expect(createUrlIfValid(\"https://example.com\")).toBeInstanceOf(URL);\n expect(createUrlIfValid(\"https://example.com/path?query=value#hash\")).toBeInstanceOf(URL);\n expect(createUrlIfValid(\"/path\", \"https://example.com\")).toBeInstanceOf(URL);\n\n // Test with invalid URLs\n expect(createUrlIfValid(\"\")).toBeNull();\n expect(createUrlIfValid(\"not a url\")).toBeNull();\n expect(createUrlIfValid(\"http://\")).toBeNull();\n});\n\nexport function isValidUrl(url: string) {\n return !!createUrlIfValid(url);\n}\nimport.meta.vitest?.test(\"isValidUrl\", ({ expect }) => {\n // Test with valid URLs\n expect(isValidUrl(\"https://example.com\")).toBe(true);\n expect(isValidUrl(\"http://localhost:3000\")).toBe(true);\n expect(isValidUrl(\"ftp://example.com\")).toBe(true);\n\n // Test with invalid URLs\n expect(isValidUrl(\"\")).toBe(false);\n expect(isValidUrl(\"not a url\")).toBe(false);\n expect(isValidUrl(\"http://\")).toBe(false);\n});\n\nexport function isValidHostname(hostname: string) {\n // Basic validation\n if (!hostname || hostname.startsWith('.') || hostname.endsWith('.') || hostname.includes('..')) {\n return false;\n }\n\n const url = createUrlIfValid(`https://${hostname}`);\n if (!url) return false;\n return url.hostname === hostname;\n}\nimport.meta.vitest?.test(\"isValidHostname\", ({ expect }) => {\n // Test with valid hostnames\n expect(isValidHostname(\"example.com\")).toBe(true);\n expect(isValidHostname(\"localhost\")).toBe(true);\n expect(isValidHostname(\"sub.domain.example.com\")).toBe(true);\n expect(isValidHostname(\"127.0.0.1\")).toBe(true);\n\n // Test with invalid hostnames\n expect(isValidHostname(\"\")).toBe(false);\n expect(isValidHostname(\"example.com/path\")).toBe(false);\n expect(isValidHostname(\"https://example.com\")).toBe(false);\n expect(isValidHostname(\"example com\")).toBe(false);\n});\n\nexport function isValidHostnameWithWildcards(hostname: string) {\n // Empty hostnames are invalid\n if (!hostname) return false;\n\n // Check if it contains wildcards\n const hasWildcard = hostname.includes('*');\n\n if (!hasWildcard) {\n // If no wildcards, validate as a normal hostname\n return isValidHostname(hostname);\n }\n\n // Basic validation checks that apply even with wildcards\n // - Hostname cannot start or end with a dot\n if (hostname.startsWith('.') || hostname.endsWith('.')) {\n return false;\n }\n\n // - No consecutive dots\n if (hostname.includes('..')) {\n return false;\n }\n\n // For wildcard validation, check that non-wildcard parts contain valid characters\n // Replace wildcards with a valid placeholder to check the rest\n const testHostname = hostname.replace(/\\*+/g, 'wildcard');\n\n // Check if the resulting string would be a valid hostname\n if (!/^[a-zA-Z0-9.-]+$/.test(testHostname)) {\n return false;\n }\n\n // Additional check: ensure the pattern makes sense\n // Check each segment between wildcards\n const segments = hostname.split(/\\*+/);\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i];\n if (segment === '') continue; // Empty segments are OK (consecutive wildcards)\n\n // First segment can't start with dot\n if (i === 0 && segment.startsWith('.')) {\n return false;\n }\n\n // Last segment can't end with dot\n if (i === segments.length - 1 && segment.endsWith('.')) {\n return false;\n }\n\n // No segment should have consecutive dots\n if (segment.includes('..')) {\n return false;\n }\n }\n\n return true;\n}\nimport.meta.vitest?.test(\"isValidHostnameWithWildcards\", ({ expect }) => {\n // Test with valid regular hostnames\n expect(isValidHostnameWithWildcards(\"example.com\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"localhost\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"sub.domain.example.com\")).toBe(true);\n\n // Test with valid wildcard hostnames\n expect(isValidHostnameWithWildcards(\"*.example.com\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"a-*.example.com\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"*.*.org\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"**.example.com\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"sub.**.com\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"*-api.*.com\")).toBe(true);\n\n // Test with invalid hostnames\n expect(isValidHostnameWithWildcards(\"\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"example.com/path\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"https://example.com\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"example com\")).toBe(false);\n expect(isValidHostnameWithWildcards(\".example.com\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"example.com.\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"example..com\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"*.example..com\")).toBe(false);\n});\n\nexport function matchHostnamePattern(pattern: string, hostname: string): boolean {\n // If no wildcards, it's an exact match\n if (!pattern.includes('*')) {\n return pattern === hostname;\n }\n\n // Convert the pattern to a regex\n // First, escape all regex special characters except *\n let regexPattern = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Use a placeholder for ** to handle it separately from single *\n const doubleWildcardPlaceholder = '\\x00DOUBLE_WILDCARD\\x00';\n regexPattern = regexPattern.replace(/\\*\\*/g, doubleWildcardPlaceholder);\n\n // Replace single * with a pattern that matches anything except dots\n regexPattern = regexPattern.replace(/\\*/g, '[^.]*');\n\n // Replace the double wildcard placeholder with a pattern that matches anything including dots\n regexPattern = regexPattern.replace(new RegExp(doubleWildcardPlaceholder, 'g'), '.*');\n\n // Anchor the pattern to match the entire hostname\n regexPattern = '^' + regexPattern + '$';\n\n try {\n const regex = new RegExp(regexPattern);\n return regex.test(hostname);\n } catch {\n return false;\n }\n}\nimport.meta.vitest?.test(\"matchHostnamePattern\", ({ expect }) => {\n // Test exact matches\n expect(matchHostnamePattern(\"example.com\", \"example.com\")).toBe(true);\n expect(matchHostnamePattern(\"example.com\", \"other.com\")).toBe(false);\n\n // Test single wildcard matches\n expect(matchHostnamePattern(\"*.example.com\", \"api.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"*.example.com\", \"www.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"*.example.com\", \"example.com\")).toBe(false);\n expect(matchHostnamePattern(\"*.example.com\", \"api.v2.example.com\")).toBe(false);\n\n // Test double wildcard matches\n expect(matchHostnamePattern(\"**.example.com\", \"api.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"**.example.com\", \"api.v2.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"**.example.com\", \"a.b.c.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"**.example.com\", \"example.com\")).toBe(false);\n\n // Test complex patterns\n expect(matchHostnamePattern(\"api-*.example.com\", \"api-v1.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"api-*.example.com\", \"api-v2.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"api-*.example.com\", \"api.example.com\")).toBe(false);\n expect(matchHostnamePattern(\"*.*.org\", \"mail.example.org\")).toBe(true);\n expect(matchHostnamePattern(\"*.*.org\", \"example.org\")).toBe(false);\n});\n\nexport function getHardcodedFallbackUrls(primaryBaseUrl: string): string[] {\n if (primaryBaseUrl === \"https://api.hexclave.com\") {\n return [\"https://api1.hexclave.com\", \"https://api2.hexclave.com\"];\n }\n if (primaryBaseUrl === \"https://api.dev.hexclave.com\") {\n return [\"https://api1.dev.hexclave.com\", \"https://api2.dev.hexclave.com\"];\n }\n // Backward-compat: customers with explicit `baseUrl: \"https://api.stack-auth.com\"`\n // (the pre-rebrand canonical) keep the same fallbacks they had before. Both\n // hostnames resolve to the same backend during the deprecation window.\n if (primaryBaseUrl === \"https://api.stack-auth.com\") {\n return [\"https://api1.stack-auth.com\", \"https://api2.stack-auth.com\"];\n }\n if (primaryBaseUrl === \"https://api.dev.stack-auth.com\") {\n return [\"https://api1.dev.stack-auth.com\", \"https://api2.dev.stack-auth.com\"];\n }\n const localhostMatch = primaryBaseUrl.match(/^http:\\/\\/localhost:(\\d+)02$/);\n if (localhostMatch) {\n return [`http://localhost:${localhostMatch[1]}10`];\n }\n return [];\n}\n\nexport function getDefaultApiUrls(primaryBaseUrl: string): string[] {\n return [primaryBaseUrl, ...getHardcodedFallbackUrls(primaryBaseUrl)];\n}\n\nexport function isLocalhost(urlOrString: string | URL) {\n const url = createUrlIfValid(urlOrString);\n if (!url) return false;\n if (url.hostname === \"localhost\" || url.hostname.endsWith(\".localhost\")) return true;\n if (url.hostname.match(/^127\\.\\d+\\.\\d+\\.\\d+$/)) return true;\n if (url.hostname === \"[::1]\" || url.hostname === \"::1\") return true;\n return false;\n}\nimport.meta.vitest?.test(\"isLocalhost\", ({ expect }) => {\n // Test with localhost URLs\n expect(isLocalhost(\"http://localhost\")).toBe(true);\n expect(isLocalhost(\"https://localhost:8080\")).toBe(true);\n expect(isLocalhost(\"http://sub.localhost\")).toBe(true);\n expect(isLocalhost(\"http://127.0.0.1\")).toBe(true);\n expect(isLocalhost(\"http://127.1.2.3\")).toBe(true);\n expect(isLocalhost(\"http://[::1]\")).toBe(true);\n\n // Test with non-localhost URLs\n expect(isLocalhost(\"https://example.com\")).toBe(false);\n expect(isLocalhost(\"http://192.168.1.1\")).toBe(false);\n expect(isLocalhost(\"http://10.0.0.1\")).toBe(false);\n\n // Test with URL objects\n expect(isLocalhost(new URL(\"http://localhost\"))).toBe(true);\n expect(isLocalhost(new URL(\"https://example.com\"))).toBe(false);\n\n // Test with invalid URLs\n expect(isLocalhost(\"not a url\")).toBe(false);\n expect(isLocalhost(\"\")).toBe(false);\n});\n\nexport function isRelative(url: string) {\n const randomDomain = `${generateSecureRandomString()}.stack-auth.example.com`;\n const u = createUrlIfValid(url, `https://${randomDomain}`);\n if (!u) return false;\n if (u.host !== randomDomain) return false;\n if (u.protocol !== \"https:\") return false;\n return true;\n}\nimport.meta.vitest?.test(\"isRelative\", ({ expect }) => {\n // We can't easily mock generateSecureRandomString in this context\n // but we can still test the function's behavior\n\n // Test with relative URLs\n expect(isRelative(\"/\")).toBe(true);\n expect(isRelative(\"/path\")).toBe(true);\n expect(isRelative(\"/path?query=value#hash\")).toBe(true);\n\n // Test with absolute URLs\n expect(isRelative(\"https://example.com\")).toBe(false);\n expect(isRelative(\"http://example.com\")).toBe(false);\n expect(isRelative(\"//example.com\")).toBe(false);\n\n // Note: The implementation treats empty strings and invalid URLs as relative\n // This is because they can be resolved against a base URL\n expect(isRelative(\"\")).toBe(true);\n expect(isRelative(\"not a url\")).toBe(true);\n});\n\nexport function getRelativePart(url: URL) {\n return url.pathname + url.search + url.hash;\n}\nimport.meta.vitest?.test(\"getRelativePart\", ({ expect }) => {\n // Test with various URLs\n expect(getRelativePart(new URL(\"https://example.com\"))).toBe(\"/\");\n expect(getRelativePart(new URL(\"https://example.com/path\"))).toBe(\"/path\");\n expect(getRelativePart(new URL(\"https://example.com/path?query=value\"))).toBe(\"/path?query=value\");\n expect(getRelativePart(new URL(\"https://example.com/path#hash\"))).toBe(\"/path#hash\");\n expect(getRelativePart(new URL(\"https://example.com/path?query=value#hash\"))).toBe(\"/path?query=value#hash\");\n\n // Test with different domains but same paths\n const url1 = new URL(\"https://example.com/path?query=value#hash\");\n const url2 = new URL(\"https://different.com/path?query=value#hash\");\n expect(getRelativePart(url1)).toBe(getRelativePart(url2));\n});\n\n/**\n * A template literal tag that returns a URL.\n *\n * Any values passed are encoded.\n */\nexport function url(strings: TemplateStringsArray | readonly string[], ...values: (string | number | boolean)[]): URL {\n return new URL(urlString(strings, ...values));\n}\nimport.meta.vitest?.test(\"url\", ({ expect }) => {\n // Test with no interpolation\n expect(url`https://example.com`).toBeInstanceOf(URL);\n expect(url`https://example.com`.href).toBe(\"https://example.com/\");\n\n // Test with string interpolation\n expect(url`https://example.com/${\"path\"}`).toBeInstanceOf(URL);\n expect(url`https://example.com/${\"path\"}`.pathname).toBe(\"/path\");\n\n // Test with number interpolation\n expect(url`https://example.com/${42}`).toBeInstanceOf(URL);\n expect(url`https://example.com/${42}`.pathname).toBe(\"/42\");\n\n // Test with boolean interpolation\n expect(url`https://example.com/${true}`).toBeInstanceOf(URL);\n expect(url`https://example.com/${true}`.pathname).toBe(\"/true\");\n\n // Test with special characters in interpolation\n expect(url`https://example.com/${\"path with spaces\"}`).toBeInstanceOf(URL);\n expect(url`https://example.com/${\"path with spaces\"}`.pathname).toBe(\"/path%20with%20spaces\");\n\n // Test with multiple interpolations\n expect(url`https://example.com/${\"path\"}?query=${\"value\"}`).toBeInstanceOf(URL);\n expect(url`https://example.com/${\"path\"}?query=${\"value\"}`.pathname).toBe(\"/path\");\n expect(url`https://example.com/${\"path\"}?query=${\"value\"}`.search).toBe(\"?query=value\");\n});\n\n\n/**\n * A template literal tag that returns a URL string.\n *\n * Any values passed are encoded.\n */\nexport function urlString(strings: TemplateStringsArray | readonly string[], ...values: (string | number | boolean)[]): string {\n return templateIdentity(strings, ...values.map(encodeURIComponent));\n}\nimport.meta.vitest?.test(\"urlString\", ({ expect }) => {\n // Test with no interpolation\n expect(urlString`https://example.com`).toBe(\"https://example.com\");\n\n // Test with string interpolation\n expect(urlString`https://example.com/${\"path\"}`).toBe(\"https://example.com/path\");\n\n // Test with number interpolation\n expect(urlString`https://example.com/${42}`).toBe(\"https://example.com/42\");\n\n // Test with boolean interpolation\n expect(urlString`https://example.com/${true}`).toBe(\"https://example.com/true\");\n\n // Test with special characters in interpolation\n expect(urlString`https://example.com/${\"path with spaces\"}`).toBe(\"https://example.com/path%20with%20spaces\");\n expect(urlString`https://example.com/${\"?&=\"}`).toBe(\"https://example.com/%3F%26%3D\");\n\n // Test with multiple interpolations\n expect(urlString`https://example.com/${\"path\"}?query=${\"value\"}`).toBe(\"https://example.com/path?query=value\");\n expect(urlString`https://example.com/${\"path\"}?query=${\"value with spaces\"}`).toBe(\"https://example.com/path?query=value%20with%20spaces\");\n});\n\nexport function isChildUrl(parentUrl: URL, maybeChildUrl: URL) {\n return parentUrl.origin === maybeChildUrl.origin\n && isChildPath(parentUrl.pathname, maybeChildUrl.pathname)\n && [...parentUrl.searchParams].every(([key, value]) => maybeChildUrl.searchParams.get(key) === value)\n && (!parentUrl.hash || parentUrl.hash === maybeChildUrl.hash);\n}\nimport.meta.vitest?.test(\"isChildUrl\", ({ expect }) => {\n // true\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://abc.com/\"))).toBe(true);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://abc.com/path\"))).toBe(true);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://abc.com/path?query=value\"))).toBe(true);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://abc.com/path?query=value#hash\"))).toBe(true);\n\n // false\n expect(isChildUrl(new URL(\"https://abc.com\"), new URL(\"https://example.com\"))).toBe(false);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://example.com/path\"))).toBe(false);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://example.com/path?query=value\"))).toBe(false);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://example.com/path?query=value#hash\"))).toBe(false);\n expect(isChildUrl(new URL(\"https://example.com\"), new URL(\"https://abc.com/path?query=value#hash\"))).toBe(false);\n expect(isChildUrl(new URL(\"https://example.com?query=value123\"), new URL(\"https://example.com/path?query=value#hash\"))).toBe(false);\n});\n\nexport function isChildPath(parentPath: string, maybeChildPath: string) {\n parentPath = parentPath.endsWith(\"/\") ? parentPath : parentPath + \"/\";\n maybeChildPath = maybeChildPath.endsWith(\"/\") ? maybeChildPath : maybeChildPath + \"/\";\n return maybeChildPath.startsWith(parentPath);\n}\nimport.meta.vitest?.test(\"isSubPath\", ({ expect }) => {\n expect(isChildPath(\"/\", \"/\")).toBe(true);\n expect(isChildPath(\"/\", \"/path\")).toBe(true);\n expect(isChildPath(\"/path\", \"/\")).toBe(false);\n expect(isChildPath(\"/path\", \"/path\")).toBe(true);\n expect(isChildPath(\"/path/\", \"/path\")).toBe(true);\n expect(isChildPath(\"/path\", \"/path/\")).toBe(true);\n expect(isChildPath(\"/path/\", \"/path/\")).toBe(true);\n expect(isChildPath(\"/path\", \"/path/abc\")).toBe(true);\n expect(isChildPath(\"/path/\", \"/path/abc\")).toBe(true);\n expect(isChildPath(\"/path\", \"/path-abc\")).toBe(false);\n expect(isChildPath(\"/path\", \"/path-abc/\")).toBe(false);\n expect(isChildPath(\"/path/\", \"/path-abc\")).toBe(false);\n expect(isChildPath(\"/path/\", \"/path-abc/\")).toBe(false);\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"urls.js","names":[],"sources":["../../src/utils/urls.tsx"],"sourcesContent":["import { generateSecureRandomString } from \"./crypto\";\nimport { templateIdentity } from \"./strings\";\n\nexport function createUrlIfValid(...args: ConstructorParameters<typeof URL>) {\n try {\n return new URL(...args);\n } catch (e) {\n return null;\n }\n}\nimport.meta.vitest?.test(\"createUrlIfValid\", ({ expect }) => {\n // Test with valid URLs\n expect(createUrlIfValid(\"https://example.com\")).toBeInstanceOf(URL);\n expect(createUrlIfValid(\"https://example.com/path?query=value#hash\")).toBeInstanceOf(URL);\n expect(createUrlIfValid(\"/path\", \"https://example.com\")).toBeInstanceOf(URL);\n\n // Test with invalid URLs\n expect(createUrlIfValid(\"\")).toBeNull();\n expect(createUrlIfValid(\"not a url\")).toBeNull();\n expect(createUrlIfValid(\"http://\")).toBeNull();\n});\n\nexport function isValidUrl(url: string) {\n return !!createUrlIfValid(url);\n}\nimport.meta.vitest?.test(\"isValidUrl\", ({ expect }) => {\n // Test with valid URLs\n expect(isValidUrl(\"https://example.com\")).toBe(true);\n expect(isValidUrl(\"http://localhost:3000\")).toBe(true);\n expect(isValidUrl(\"ftp://example.com\")).toBe(true);\n\n // Test with invalid URLs\n expect(isValidUrl(\"\")).toBe(false);\n expect(isValidUrl(\"not a url\")).toBe(false);\n expect(isValidUrl(\"http://\")).toBe(false);\n});\n\nexport function isValidHostname(hostname: string) {\n // Basic validation\n if (!hostname || hostname.startsWith('.') || hostname.endsWith('.') || hostname.includes('..')) {\n return false;\n }\n\n const url = createUrlIfValid(`https://${hostname}`);\n if (!url) return false;\n return url.hostname === hostname;\n}\nimport.meta.vitest?.test(\"isValidHostname\", ({ expect }) => {\n // Test with valid hostnames\n expect(isValidHostname(\"example.com\")).toBe(true);\n expect(isValidHostname(\"localhost\")).toBe(true);\n expect(isValidHostname(\"sub.domain.example.com\")).toBe(true);\n expect(isValidHostname(\"127.0.0.1\")).toBe(true);\n\n // Test with invalid hostnames\n expect(isValidHostname(\"\")).toBe(false);\n expect(isValidHostname(\"example.com/path\")).toBe(false);\n expect(isValidHostname(\"https://example.com\")).toBe(false);\n expect(isValidHostname(\"example com\")).toBe(false);\n});\n\nexport function isValidHostnameWithWildcards(hostname: string) {\n // Empty hostnames are invalid\n if (!hostname) return false;\n\n // Check if it contains wildcards\n const hasWildcard = hostname.includes('*');\n\n if (!hasWildcard) {\n // If no wildcards, validate as a normal hostname\n return isValidHostname(hostname);\n }\n\n // Basic validation checks that apply even with wildcards\n // - Hostname cannot start or end with a dot\n if (hostname.startsWith('.') || hostname.endsWith('.')) {\n return false;\n }\n\n // - No consecutive dots\n if (hostname.includes('..')) {\n return false;\n }\n\n // For wildcard validation, check that non-wildcard parts contain valid characters\n // Replace wildcards with a valid placeholder to check the rest\n const testHostname = hostname.replace(/\\*+/g, 'wildcard');\n\n // Check if the resulting string would be a valid hostname\n if (!/^[a-zA-Z0-9.-]+$/.test(testHostname)) {\n return false;\n }\n\n // Additional check: ensure the pattern makes sense\n // Check each segment between wildcards\n const segments = hostname.split(/\\*+/);\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i];\n if (segment === '') continue; // Empty segments are OK (consecutive wildcards)\n\n // First segment can't start with dot\n if (i === 0 && segment.startsWith('.')) {\n return false;\n }\n\n // Last segment can't end with dot\n if (i === segments.length - 1 && segment.endsWith('.')) {\n return false;\n }\n\n // No segment should have consecutive dots\n if (segment.includes('..')) {\n return false;\n }\n }\n\n return true;\n}\nimport.meta.vitest?.test(\"isValidHostnameWithWildcards\", ({ expect }) => {\n // Test with valid regular hostnames\n expect(isValidHostnameWithWildcards(\"example.com\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"localhost\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"sub.domain.example.com\")).toBe(true);\n\n // Test with valid wildcard hostnames\n expect(isValidHostnameWithWildcards(\"*.example.com\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"a-*.example.com\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"*.*.org\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"**.example.com\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"sub.**.com\")).toBe(true);\n expect(isValidHostnameWithWildcards(\"*-api.*.com\")).toBe(true);\n\n // Test with invalid hostnames\n expect(isValidHostnameWithWildcards(\"\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"example.com/path\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"https://example.com\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"example com\")).toBe(false);\n expect(isValidHostnameWithWildcards(\".example.com\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"example.com.\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"example..com\")).toBe(false);\n expect(isValidHostnameWithWildcards(\"*.example..com\")).toBe(false);\n});\n\nexport function matchHostnamePattern(pattern: string, hostname: string): boolean {\n // If no wildcards, it's an exact match\n if (!pattern.includes('*')) {\n return pattern === hostname;\n }\n\n // Convert the pattern to a regex\n // First, escape all regex special characters except *\n let regexPattern = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Use a placeholder for ** to handle it separately from single *\n const doubleWildcardPlaceholder = '\\x00DOUBLE_WILDCARD\\x00';\n regexPattern = regexPattern.replace(/\\*\\*/g, doubleWildcardPlaceholder);\n\n // Replace single * with a pattern that matches anything except dots\n regexPattern = regexPattern.replace(/\\*/g, '[^.]*');\n\n // Replace the double wildcard placeholder with a pattern that matches anything including dots\n regexPattern = regexPattern.replace(new RegExp(doubleWildcardPlaceholder, 'g'), '.*');\n\n // Anchor the pattern to match the entire hostname\n regexPattern = '^' + regexPattern + '$';\n\n try {\n const regex = new RegExp(regexPattern);\n return regex.test(hostname);\n } catch {\n return false;\n }\n}\nimport.meta.vitest?.test(\"matchHostnamePattern\", ({ expect }) => {\n // Test exact matches\n expect(matchHostnamePattern(\"example.com\", \"example.com\")).toBe(true);\n expect(matchHostnamePattern(\"example.com\", \"other.com\")).toBe(false);\n\n // Test single wildcard matches\n expect(matchHostnamePattern(\"*.example.com\", \"api.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"*.example.com\", \"www.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"*.example.com\", \"example.com\")).toBe(false);\n expect(matchHostnamePattern(\"*.example.com\", \"api.v2.example.com\")).toBe(false);\n\n // Test double wildcard matches\n expect(matchHostnamePattern(\"**.example.com\", \"api.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"**.example.com\", \"api.v2.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"**.example.com\", \"a.b.c.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"**.example.com\", \"example.com\")).toBe(false);\n\n // Test complex patterns\n expect(matchHostnamePattern(\"api-*.example.com\", \"api-v1.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"api-*.example.com\", \"api-v2.example.com\")).toBe(true);\n expect(matchHostnamePattern(\"api-*.example.com\", \"api.example.com\")).toBe(false);\n expect(matchHostnamePattern(\"*.*.org\", \"mail.example.org\")).toBe(true);\n expect(matchHostnamePattern(\"*.*.org\", \"example.org\")).toBe(false);\n});\n\nexport function getHardcodedFallbackUrls(primaryBaseUrl: string): string[] {\n // `defaultBaseUrl` in `@stackframe/*` SDK builds is `https://api.stack-auth.com`\n // (see `packages/template/src/lib/stack-app/apps/implementations/common.ts`).\n // The `@hexclave/*` mirror packages will get this rewritten to the hexclave\n // hostname by `scripts/rewrite-packages-to-hexclave.ts` in a follow-up PR; both\n // branches below stay listed so either build resolves its own fallback set.\n if (primaryBaseUrl === \"https://api.stack-auth.com\") {\n return [\"https://api1.stack-auth.com\", \"https://api2.stack-auth.com\"];\n }\n if (primaryBaseUrl === \"https://api.dev.stack-auth.com\") {\n return [\"https://api1.dev.stack-auth.com\", \"https://api2.dev.stack-auth.com\"];\n }\n if (primaryBaseUrl === \"https://api.hexclave.com\") {\n return [\"https://api1.hexclave.com\", \"https://api2.hexclave.com\"];\n }\n if (primaryBaseUrl === \"https://api.dev.hexclave.com\") {\n return [\"https://api1.dev.hexclave.com\", \"https://api2.dev.hexclave.com\"];\n }\n const localhostMatch = primaryBaseUrl.match(/^http:\\/\\/localhost:(\\d+)02$/);\n if (localhostMatch) {\n return [`http://localhost:${localhostMatch[1]}10`];\n }\n return [];\n}\n\nexport function getDefaultApiUrls(primaryBaseUrl: string): string[] {\n return [primaryBaseUrl, ...getHardcodedFallbackUrls(primaryBaseUrl)];\n}\n\nexport function isLocalhost(urlOrString: string | URL) {\n const url = createUrlIfValid(urlOrString);\n if (!url) return false;\n if (url.hostname === \"localhost\" || url.hostname.endsWith(\".localhost\")) return true;\n if (url.hostname.match(/^127\\.\\d+\\.\\d+\\.\\d+$/)) return true;\n if (url.hostname === \"[::1]\" || url.hostname === \"::1\") return true;\n return false;\n}\nimport.meta.vitest?.test(\"isLocalhost\", ({ expect }) => {\n // Test with localhost URLs\n expect(isLocalhost(\"http://localhost\")).toBe(true);\n expect(isLocalhost(\"https://localhost:8080\")).toBe(true);\n expect(isLocalhost(\"http://sub.localhost\")).toBe(true);\n expect(isLocalhost(\"http://127.0.0.1\")).toBe(true);\n expect(isLocalhost(\"http://127.1.2.3\")).toBe(true);\n expect(isLocalhost(\"http://[::1]\")).toBe(true);\n\n // Test with non-localhost URLs\n expect(isLocalhost(\"https://example.com\")).toBe(false);\n expect(isLocalhost(\"http://192.168.1.1\")).toBe(false);\n expect(isLocalhost(\"http://10.0.0.1\")).toBe(false);\n\n // Test with URL objects\n expect(isLocalhost(new URL(\"http://localhost\"))).toBe(true);\n expect(isLocalhost(new URL(\"https://example.com\"))).toBe(false);\n\n // Test with invalid URLs\n expect(isLocalhost(\"not a url\")).toBe(false);\n expect(isLocalhost(\"\")).toBe(false);\n});\n\nexport function isRelative(url: string) {\n const randomDomain = `${generateSecureRandomString()}.stack-auth.example.com`;\n const u = createUrlIfValid(url, `https://${randomDomain}`);\n if (!u) return false;\n if (u.host !== randomDomain) return false;\n if (u.protocol !== \"https:\") return false;\n return true;\n}\nimport.meta.vitest?.test(\"isRelative\", ({ expect }) => {\n // We can't easily mock generateSecureRandomString in this context\n // but we can still test the function's behavior\n\n // Test with relative URLs\n expect(isRelative(\"/\")).toBe(true);\n expect(isRelative(\"/path\")).toBe(true);\n expect(isRelative(\"/path?query=value#hash\")).toBe(true);\n\n // Test with absolute URLs\n expect(isRelative(\"https://example.com\")).toBe(false);\n expect(isRelative(\"http://example.com\")).toBe(false);\n expect(isRelative(\"//example.com\")).toBe(false);\n\n // Note: The implementation treats empty strings and invalid URLs as relative\n // This is because they can be resolved against a base URL\n expect(isRelative(\"\")).toBe(true);\n expect(isRelative(\"not a url\")).toBe(true);\n});\n\nexport function getRelativePart(url: URL) {\n return url.pathname + url.search + url.hash;\n}\nimport.meta.vitest?.test(\"getRelativePart\", ({ expect }) => {\n // Test with various URLs\n expect(getRelativePart(new URL(\"https://example.com\"))).toBe(\"/\");\n expect(getRelativePart(new URL(\"https://example.com/path\"))).toBe(\"/path\");\n expect(getRelativePart(new URL(\"https://example.com/path?query=value\"))).toBe(\"/path?query=value\");\n expect(getRelativePart(new URL(\"https://example.com/path#hash\"))).toBe(\"/path#hash\");\n expect(getRelativePart(new URL(\"https://example.com/path?query=value#hash\"))).toBe(\"/path?query=value#hash\");\n\n // Test with different domains but same paths\n const url1 = new URL(\"https://example.com/path?query=value#hash\");\n const url2 = new URL(\"https://different.com/path?query=value#hash\");\n expect(getRelativePart(url1)).toBe(getRelativePart(url2));\n});\n\n/**\n * A template literal tag that returns a URL.\n *\n * Any values passed are encoded.\n */\nexport function url(strings: TemplateStringsArray | readonly string[], ...values: (string | number | boolean)[]): URL {\n return new URL(urlString(strings, ...values));\n}\nimport.meta.vitest?.test(\"url\", ({ expect }) => {\n // Test with no interpolation\n expect(url`https://example.com`).toBeInstanceOf(URL);\n expect(url`https://example.com`.href).toBe(\"https://example.com/\");\n\n // Test with string interpolation\n expect(url`https://example.com/${\"path\"}`).toBeInstanceOf(URL);\n expect(url`https://example.com/${\"path\"}`.pathname).toBe(\"/path\");\n\n // Test with number interpolation\n expect(url`https://example.com/${42}`).toBeInstanceOf(URL);\n expect(url`https://example.com/${42}`.pathname).toBe(\"/42\");\n\n // Test with boolean interpolation\n expect(url`https://example.com/${true}`).toBeInstanceOf(URL);\n expect(url`https://example.com/${true}`.pathname).toBe(\"/true\");\n\n // Test with special characters in interpolation\n expect(url`https://example.com/${\"path with spaces\"}`).toBeInstanceOf(URL);\n expect(url`https://example.com/${\"path with spaces\"}`.pathname).toBe(\"/path%20with%20spaces\");\n\n // Test with multiple interpolations\n expect(url`https://example.com/${\"path\"}?query=${\"value\"}`).toBeInstanceOf(URL);\n expect(url`https://example.com/${\"path\"}?query=${\"value\"}`.pathname).toBe(\"/path\");\n expect(url`https://example.com/${\"path\"}?query=${\"value\"}`.search).toBe(\"?query=value\");\n});\n\n\n/**\n * A template literal tag that returns a URL string.\n *\n * Any values passed are encoded.\n */\nexport function urlString(strings: TemplateStringsArray | readonly string[], ...values: (string | number | boolean)[]): string {\n return templateIdentity(strings, ...values.map(encodeURIComponent));\n}\nimport.meta.vitest?.test(\"urlString\", ({ expect }) => {\n // Test with no interpolation\n expect(urlString`https://example.com`).toBe(\"https://example.com\");\n\n // Test with string interpolation\n expect(urlString`https://example.com/${\"path\"}`).toBe(\"https://example.com/path\");\n\n // Test with number interpolation\n expect(urlString`https://example.com/${42}`).toBe(\"https://example.com/42\");\n\n // Test with boolean interpolation\n expect(urlString`https://example.com/${true}`).toBe(\"https://example.com/true\");\n\n // Test with special characters in interpolation\n expect(urlString`https://example.com/${\"path with spaces\"}`).toBe(\"https://example.com/path%20with%20spaces\");\n expect(urlString`https://example.com/${\"?&=\"}`).toBe(\"https://example.com/%3F%26%3D\");\n\n // Test with multiple interpolations\n expect(urlString`https://example.com/${\"path\"}?query=${\"value\"}`).toBe(\"https://example.com/path?query=value\");\n expect(urlString`https://example.com/${\"path\"}?query=${\"value with spaces\"}`).toBe(\"https://example.com/path?query=value%20with%20spaces\");\n});\n\nexport function isChildUrl(parentUrl: URL, maybeChildUrl: URL) {\n return parentUrl.origin === maybeChildUrl.origin\n && isChildPath(parentUrl.pathname, maybeChildUrl.pathname)\n && [...parentUrl.searchParams].every(([key, value]) => maybeChildUrl.searchParams.get(key) === value)\n && (!parentUrl.hash || parentUrl.hash === maybeChildUrl.hash);\n}\nimport.meta.vitest?.test(\"isChildUrl\", ({ expect }) => {\n // true\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://abc.com/\"))).toBe(true);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://abc.com/path\"))).toBe(true);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://abc.com/path?query=value\"))).toBe(true);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://abc.com/path?query=value#hash\"))).toBe(true);\n\n // false\n expect(isChildUrl(new URL(\"https://abc.com\"), new URL(\"https://example.com\"))).toBe(false);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://example.com/path\"))).toBe(false);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://example.com/path?query=value\"))).toBe(false);\n expect(isChildUrl(new URL(\"https://abc.com/\"), new URL(\"https://example.com/path?query=value#hash\"))).toBe(false);\n expect(isChildUrl(new URL(\"https://example.com\"), new URL(\"https://abc.com/path?query=value#hash\"))).toBe(false);\n expect(isChildUrl(new URL(\"https://example.com?query=value123\"), new URL(\"https://example.com/path?query=value#hash\"))).toBe(false);\n});\n\nexport function isChildPath(parentPath: string, maybeChildPath: string) {\n parentPath = parentPath.endsWith(\"/\") ? parentPath : parentPath + \"/\";\n maybeChildPath = maybeChildPath.endsWith(\"/\") ? maybeChildPath : maybeChildPath + \"/\";\n return maybeChildPath.startsWith(parentPath);\n}\nimport.meta.vitest?.test(\"isSubPath\", ({ expect }) => {\n expect(isChildPath(\"/\", \"/\")).toBe(true);\n expect(isChildPath(\"/\", \"/path\")).toBe(true);\n expect(isChildPath(\"/path\", \"/\")).toBe(false);\n expect(isChildPath(\"/path\", \"/path\")).toBe(true);\n expect(isChildPath(\"/path/\", \"/path\")).toBe(true);\n expect(isChildPath(\"/path\", \"/path/\")).toBe(true);\n expect(isChildPath(\"/path/\", \"/path/\")).toBe(true);\n expect(isChildPath(\"/path\", \"/path/abc\")).toBe(true);\n expect(isChildPath(\"/path/\", \"/path/abc\")).toBe(true);\n expect(isChildPath(\"/path\", \"/path-abc\")).toBe(false);\n expect(isChildPath(\"/path\", \"/path-abc/\")).toBe(false);\n expect(isChildPath(\"/path/\", \"/path-abc\")).toBe(false);\n expect(isChildPath(\"/path/\", \"/path-abc/\")).toBe(false);\n});\n"],"mappings":"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"}
|