@clerk/shared 4.37.0-snapshot.v20260925223922 → 4.37.0

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.
@@ -11,6 +11,9 @@ interface ProtectCheckRunnerResource<TResource> {
11
11
  }
12
12
  interface ProtectCheckRunOptions {
13
13
  container: HTMLDivElement;
14
+ expiredReloads: {
15
+ current: number;
16
+ };
14
17
  signal?: AbortSignal;
15
18
  setWidgetVisible?: (visible: boolean) => Promise<void>;
16
19
  loadTimeoutMs?: number;
@@ -23,14 +26,7 @@ type ProtectCheckRunOutcome<TResource> = {
23
26
  status: 'reissued';
24
27
  };
25
28
  /** Runs one Protect challenge against a sign-in or sign-up resource and submits the proof token. */
26
- declare class ProtectCheckRunner<TResource> {
27
- private readonly resource;
28
- private expiredReloads;
29
- constructor(resource: ProtectCheckRunnerResource<TResource>);
30
- reset(): void;
31
- run(protectCheck: ProtectCheckResource, options: ProtectCheckRunOptions): Promise<ProtectCheckRunOutcome<TResource>>;
32
- private reloadExpired;
33
- }
29
+ declare function runProtectCheck<TResource>(resource: ProtectCheckRunnerResource<TResource>, protectCheck: ProtectCheckResource, options: ProtectCheckRunOptions): Promise<ProtectCheckRunOutcome<TResource>>;
34
30
  //#endregion
35
- export { MAX_EXPIRED_PROTECT_CHECK_RELOADS, ProtectCheckRunOptions, ProtectCheckRunOutcome, ProtectCheckRunner, ProtectCheckRunnerResource };
31
+ export { MAX_EXPIRED_PROTECT_CHECK_RELOADS, ProtectCheckRunOptions, ProtectCheckRunOutcome, ProtectCheckRunnerResource, runProtectCheck };
36
32
  //# sourceMappingURL=protectCheckRunner.d.mts.map
@@ -11,6 +11,9 @@ interface ProtectCheckRunnerResource<TResource> {
11
11
  }
12
12
  interface ProtectCheckRunOptions {
13
13
  container: HTMLDivElement;
14
+ expiredReloads: {
15
+ current: number;
16
+ };
14
17
  signal?: AbortSignal;
15
18
  setWidgetVisible?: (visible: boolean) => Promise<void>;
16
19
  loadTimeoutMs?: number;
@@ -23,14 +26,7 @@ type ProtectCheckRunOutcome<TResource> = {
23
26
  status: 'reissued';
24
27
  };
25
28
  /** Runs one Protect challenge against a sign-in or sign-up resource and submits the proof token. */
26
- declare class ProtectCheckRunner<TResource> {
27
- private readonly resource;
28
- private expiredReloads;
29
- constructor(resource: ProtectCheckRunnerResource<TResource>);
30
- reset(): void;
31
- run(protectCheck: ProtectCheckResource, options: ProtectCheckRunOptions): Promise<ProtectCheckRunOutcome<TResource>>;
32
- private reloadExpired;
33
- }
29
+ declare function runProtectCheck<TResource>(resource: ProtectCheckRunnerResource<TResource>, protectCheck: ProtectCheckResource, options: ProtectCheckRunOptions): Promise<ProtectCheckRunOutcome<TResource>>;
34
30
  //#endregion
35
- export { MAX_EXPIRED_PROTECT_CHECK_RELOADS, ProtectCheckRunOptions, ProtectCheckRunOutcome, ProtectCheckRunner, ProtectCheckRunnerResource };
31
+ export { MAX_EXPIRED_PROTECT_CHECK_RELOADS, ProtectCheckRunOptions, ProtectCheckRunOutcome, ProtectCheckRunnerResource, runProtectCheck };
36
32
  //# sourceMappingURL=protectCheckRunner.d.ts.map
@@ -9,58 +9,48 @@ const MAX_EXPIRED_PROTECT_CHECK_RELOADS = 2;
9
9
  const isExpired = (protectCheck) => protectCheck.expiresAt !== void 0 && protectCheck.expiresAt < Date.now();
10
10
  const expiredError = () => new require_clerkRuntimeError.ClerkRuntimeError("Protect verification expired", { code: require_internal_clerk_js_constants.ERROR_CODES.PROTECT_CHECK_TIMED_OUT });
11
11
  const abortedError = () => new require_clerkRuntimeError.ClerkRuntimeError("Protect check aborted by caller", { code: "protect_check_aborted" });
12
+ const reloadExpired = async (resource, expiredReloads) => {
13
+ if (expiredReloads.current >= 2) throw expiredError();
14
+ expiredReloads.current += 1;
15
+ await resource.reload();
16
+ const refreshed = resource.getProtectCheck();
17
+ if (!refreshed) return {
18
+ status: "resolved",
19
+ resource: resource.getResource()
20
+ };
21
+ if (isExpired(refreshed)) throw expiredError();
22
+ return { status: "reissued" };
23
+ };
12
24
  /** Runs one Protect challenge against a sign-in or sign-up resource and submits the proof token. */
13
- var ProtectCheckRunner = class {
14
- resource;
15
- expiredReloads = 0;
16
- constructor(resource) {
17
- this.resource = resource;
18
- }
19
- reset() {
20
- this.expiredReloads = 0;
21
- }
22
- async run(protectCheck, options) {
23
- const { container, signal, setWidgetVisible, loadTimeoutMs } = options;
24
- if (signal?.aborted) throw abortedError();
25
- if (isExpired(protectCheck)) return this.reloadExpired();
26
- while (container.firstChild) container.removeChild(container.firstChild);
27
- const proofToken = await require_internal_clerk_js_protectCheck.executeProtectCheck(protectCheck, container, {
28
- signal,
29
- setWidgetVisible,
30
- loadTimeoutMs
31
- });
25
+ async function runProtectCheck(resource, protectCheck, options) {
26
+ const { container, expiredReloads, signal, setWidgetVisible, loadTimeoutMs } = options;
27
+ if (signal?.aborted) throw abortedError();
28
+ if (isExpired(protectCheck)) return reloadExpired(resource, expiredReloads);
29
+ const proofToken = await require_internal_clerk_js_protectCheck.executeProtectCheck(protectCheck, container, {
30
+ signal,
31
+ setWidgetVisible,
32
+ loadTimeoutMs
33
+ });
34
+ if (signal?.aborted) throw abortedError();
35
+ try {
36
+ return {
37
+ status: "resolved",
38
+ resource: await resource.submitProtectCheck({ proofToken })
39
+ };
40
+ } catch (err) {
32
41
  if (signal?.aborted) throw abortedError();
33
- try {
42
+ if (require_error.isClerkAPIResponseError(err) && err.errors?.[0]?.code === require_internal_clerk_js_constants.ERROR_CODES.PROTECT_CHECK_ALREADY_RESOLVED) {
43
+ await resource.reload();
34
44
  return {
35
45
  status: "resolved",
36
- resource: await this.resource.submitProtectCheck({ proofToken })
46
+ resource: resource.getResource()
37
47
  };
38
- } catch (err) {
39
- if (require_error.isClerkAPIResponseError(err) && err.errors?.[0]?.code === require_internal_clerk_js_constants.ERROR_CODES.PROTECT_CHECK_ALREADY_RESOLVED) {
40
- await this.resource.reload();
41
- return {
42
- status: "resolved",
43
- resource: this.resource.getResource()
44
- };
45
- }
46
- throw err;
47
48
  }
49
+ throw err;
48
50
  }
49
- async reloadExpired() {
50
- if (this.expiredReloads >= 2) throw expiredError();
51
- this.expiredReloads += 1;
52
- await this.resource.reload();
53
- const refreshed = this.resource.getProtectCheck();
54
- if (!refreshed) return {
55
- status: "resolved",
56
- resource: this.resource.getResource()
57
- };
58
- if (isExpired(refreshed)) throw expiredError();
59
- return { status: "reissued" };
60
- }
61
- };
51
+ }
62
52
 
63
53
  //#endregion
64
54
  exports.MAX_EXPIRED_PROTECT_CHECK_RELOADS = MAX_EXPIRED_PROTECT_CHECK_RELOADS;
65
- exports.ProtectCheckRunner = ProtectCheckRunner;
55
+ exports.runProtectCheck = runProtectCheck;
66
56
  //# sourceMappingURL=protectCheckRunner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"protectCheckRunner.js","names":["ClerkRuntimeError","ERROR_CODES","executeProtectCheck","isClerkAPIResponseError"],"sources":["../../../src/internal/clerk-js/protectCheckRunner.ts"],"sourcesContent":["import { ClerkRuntimeError, isClerkAPIResponseError } from '../../error';\nimport type { ProtectCheckResource } from '../../types';\nimport { ERROR_CODES } from './constants';\nimport { executeProtectCheck } from './protectCheck';\n\n// A GET reload does not re-mint an expired challenge today, so an uncapped reload loop would spin forever.\nexport const MAX_EXPIRED_PROTECT_CHECK_RELOADS = 2;\n\nexport interface ProtectCheckRunnerResource<TResource> {\n getProtectCheck: () => ProtectCheckResource | null | undefined;\n getResource: () => TResource;\n reload: () => Promise<unknown>;\n submitProtectCheck: (params: { proofToken: string }) => Promise<TResource>;\n}\n\nexport interface ProtectCheckRunOptions {\n container: HTMLDivElement;\n signal?: AbortSignal;\n setWidgetVisible?: (visible: boolean) => Promise<void>;\n loadTimeoutMs?: number;\n}\n\n/** `reissued` means the expired challenge was replaced by a fresh one on reload, so run again with it. */\nexport type ProtectCheckRunOutcome<TResource> = { status: 'resolved'; resource: TResource } | { status: 'reissued' };\n\nconst isExpired = (protectCheck: ProtectCheckResource) =>\n protectCheck.expiresAt !== undefined && protectCheck.expiresAt < Date.now();\n\nconst expiredError = () =>\n new ClerkRuntimeError('Protect verification expired', { code: ERROR_CODES.PROTECT_CHECK_TIMED_OUT });\n\nconst abortedError = () => new ClerkRuntimeError('Protect check aborted by caller', { code: 'protect_check_aborted' });\n\n/** Runs one Protect challenge against a sign-in or sign-up resource and submits the proof token. */\nexport class ProtectCheckRunner<TResource> {\n private expiredReloads = 0;\n\n public constructor(private readonly resource: ProtectCheckRunnerResource<TResource>) {}\n\n public reset(): void {\n this.expiredReloads = 0;\n }\n\n public async run(\n protectCheck: ProtectCheckResource,\n options: ProtectCheckRunOptions,\n ): Promise<ProtectCheckRunOutcome<TResource>> {\n const { container, signal, setWidgetVisible, loadTimeoutMs } = options;\n if (signal?.aborted) {\n throw abortedError();\n }\n if (isExpired(protectCheck)) {\n return this.reloadExpired();\n }\n\n while (container.firstChild) {\n container.removeChild(container.firstChild);\n }\n\n // Deliberately unraced. Only the module load is bounded. The challenge itself may wait on a\n // person, and a timeout here used to abort valid challenges.\n const proofToken = await executeProtectCheck(protectCheck, container, { signal, setWidgetVisible, loadTimeoutMs });\n if (signal?.aborted) {\n throw abortedError();\n }\n\n try {\n const resource = await this.resource.submitProtectCheck({ proofToken });\n return { status: 'resolved', resource };\n } catch (err) {\n if (isClerkAPIResponseError(err) && err.errors?.[0]?.code === ERROR_CODES.PROTECT_CHECK_ALREADY_RESOLVED) {\n await this.resource.reload();\n return { status: 'resolved', resource: this.resource.getResource() };\n }\n throw err;\n }\n }\n\n private async reloadExpired(): Promise<ProtectCheckRunOutcome<TResource>> {\n if (this.expiredReloads >= MAX_EXPIRED_PROTECT_CHECK_RELOADS) {\n throw expiredError();\n }\n this.expiredReloads += 1;\n\n await this.resource.reload();\n\n const refreshed = this.resource.getProtectCheck();\n if (!refreshed) {\n return { status: 'resolved', resource: this.resource.getResource() };\n }\n if (isExpired(refreshed)) {\n throw expiredError();\n }\n return { status: 'reissued' };\n }\n}\n"],"mappings":";;;;;;;AAMA,MAAa,oCAAoC;AAmBjD,MAAM,aAAa,iBACjB,aAAa,cAAc,UAAa,aAAa,YAAY,KAAK,IAAI;AAE5E,MAAM,qBACJ,IAAIA,4CAAkB,gCAAgC,EAAE,MAAMC,gDAAY,wBAAwB,CAAC;AAErG,MAAM,qBAAqB,IAAID,4CAAkB,mCAAmC,EAAE,MAAM,wBAAwB,CAAC;;AAGrH,IAAa,qBAAb,MAA2C;CAGL;CAFpC,AAAQ,iBAAiB;CAEzB,AAAO,YAAY,AAAiB,UAAiD;EAAjD;CAAkD;CAEtF,AAAO,QAAc;EACnB,KAAK,iBAAiB;CACxB;CAEA,MAAa,IACX,cACA,SAC4C;EAC5C,MAAM,EAAE,WAAW,QAAQ,kBAAkB,kBAAkB;EAC/D,IAAI,QAAQ,SACV,MAAM,aAAa;EAErB,IAAI,UAAU,YAAY,GACxB,OAAO,KAAK,cAAc;EAG5B,OAAO,UAAU,YACf,UAAU,YAAY,UAAU,UAAU;EAK5C,MAAM,aAAa,MAAME,2DAAoB,cAAc,WAAW;GAAE;GAAQ;GAAkB;EAAc,CAAC;EACjH,IAAI,QAAQ,SACV,MAAM,aAAa;EAGrB,IAAI;GAEF,OAAO;IAAE,QAAQ;IAAY,gBADN,KAAK,SAAS,mBAAmB,EAAE,WAAW,CAAC;GAChC;EACxC,SAAS,KAAK;GACZ,IAAIC,sCAAwB,GAAG,KAAK,IAAI,SAAS,EAAE,EAAE,SAASF,gDAAY,gCAAgC;IACxG,MAAM,KAAK,SAAS,OAAO;IAC3B,OAAO;KAAE,QAAQ;KAAY,UAAU,KAAK,SAAS,YAAY;IAAE;GACrE;GACA,MAAM;EACR;CACF;CAEA,MAAc,gBAA4D;EACxE,IAAI,KAAK,qBACP,MAAM,aAAa;EAErB,KAAK,kBAAkB;EAEvB,MAAM,KAAK,SAAS,OAAO;EAE3B,MAAM,YAAY,KAAK,SAAS,gBAAgB;EAChD,IAAI,CAAC,WACH,OAAO;GAAE,QAAQ;GAAY,UAAU,KAAK,SAAS,YAAY;EAAE;EAErE,IAAI,UAAU,SAAS,GACrB,MAAM,aAAa;EAErB,OAAO,EAAE,QAAQ,WAAW;CAC9B;AACF"}
1
+ {"version":3,"file":"protectCheckRunner.js","names":["ClerkRuntimeError","ERROR_CODES","executeProtectCheck","isClerkAPIResponseError"],"sources":["../../../src/internal/clerk-js/protectCheckRunner.ts"],"sourcesContent":["import { ClerkRuntimeError, isClerkAPIResponseError } from '../../error';\nimport type { ProtectCheckResource } from '../../types';\nimport { ERROR_CODES } from './constants';\nimport { executeProtectCheck } from './protectCheck';\n\n// A GET reload does not re-mint an expired challenge today, so an uncapped reload loop would spin forever.\nexport const MAX_EXPIRED_PROTECT_CHECK_RELOADS = 2;\n\nexport interface ProtectCheckRunnerResource<TResource> {\n getProtectCheck: () => ProtectCheckResource | null | undefined;\n getResource: () => TResource;\n reload: () => Promise<unknown>;\n submitProtectCheck: (params: { proofToken: string }) => Promise<TResource>;\n}\n\nexport interface ProtectCheckRunOptions {\n container: HTMLDivElement;\n expiredReloads: { current: number };\n signal?: AbortSignal;\n setWidgetVisible?: (visible: boolean) => Promise<void>;\n loadTimeoutMs?: number;\n}\n\n/** `reissued` means the expired challenge was replaced by a fresh one on reload, so run again with it. */\nexport type ProtectCheckRunOutcome<TResource> = { status: 'resolved'; resource: TResource } | { status: 'reissued' };\n\nconst isExpired = (protectCheck: ProtectCheckResource) =>\n protectCheck.expiresAt !== undefined && protectCheck.expiresAt < Date.now();\n\nconst expiredError = () =>\n new ClerkRuntimeError('Protect verification expired', { code: ERROR_CODES.PROTECT_CHECK_TIMED_OUT });\n\nconst abortedError = () => new ClerkRuntimeError('Protect check aborted by caller', { code: 'protect_check_aborted' });\n\nconst reloadExpired = async <TResource>(\n resource: ProtectCheckRunnerResource<TResource>,\n expiredReloads: { current: number },\n): Promise<ProtectCheckRunOutcome<TResource>> => {\n if (expiredReloads.current >= MAX_EXPIRED_PROTECT_CHECK_RELOADS) {\n throw expiredError();\n }\n expiredReloads.current += 1;\n\n await resource.reload();\n\n const refreshed = resource.getProtectCheck();\n if (!refreshed) {\n return { status: 'resolved', resource: resource.getResource() };\n }\n if (isExpired(refreshed)) {\n throw expiredError();\n }\n return { status: 'reissued' };\n};\n\n/** Runs one Protect challenge against a sign-in or sign-up resource and submits the proof token. */\nexport async function runProtectCheck<TResource>(\n resource: ProtectCheckRunnerResource<TResource>,\n protectCheck: ProtectCheckResource,\n options: ProtectCheckRunOptions,\n): Promise<ProtectCheckRunOutcome<TResource>> {\n const { container, expiredReloads, signal, setWidgetVisible, loadTimeoutMs } = options;\n if (signal?.aborted) {\n throw abortedError();\n }\n if (isExpired(protectCheck)) {\n return reloadExpired(resource, expiredReloads);\n }\n\n // Deliberately unraced. Only the module load is bounded. The challenge itself may wait on a\n // person, and a timeout here used to abort valid challenges.\n const proofToken = await executeProtectCheck(protectCheck, container, { signal, setWidgetVisible, loadTimeoutMs });\n if (signal?.aborted) {\n throw abortedError();\n }\n\n try {\n const updated = await resource.submitProtectCheck({ proofToken });\n return { status: 'resolved', resource: updated };\n } catch (err) {\n if (signal?.aborted) {\n throw abortedError();\n }\n if (isClerkAPIResponseError(err) && err.errors?.[0]?.code === ERROR_CODES.PROTECT_CHECK_ALREADY_RESOLVED) {\n await resource.reload();\n return { status: 'resolved', resource: resource.getResource() };\n }\n throw err;\n }\n}\n"],"mappings":";;;;;;;AAMA,MAAa,oCAAoC;AAoBjD,MAAM,aAAa,iBACjB,aAAa,cAAc,UAAa,aAAa,YAAY,KAAK,IAAI;AAE5E,MAAM,qBACJ,IAAIA,4CAAkB,gCAAgC,EAAE,MAAMC,gDAAY,wBAAwB,CAAC;AAErG,MAAM,qBAAqB,IAAID,4CAAkB,mCAAmC,EAAE,MAAM,wBAAwB,CAAC;AAErH,MAAM,gBAAgB,OACpB,UACA,mBAC+C;CAC/C,IAAI,eAAe,cACjB,MAAM,aAAa;CAErB,eAAe,WAAW;CAE1B,MAAM,SAAS,OAAO;CAEtB,MAAM,YAAY,SAAS,gBAAgB;CAC3C,IAAI,CAAC,WACH,OAAO;EAAE,QAAQ;EAAY,UAAU,SAAS,YAAY;CAAE;CAEhE,IAAI,UAAU,SAAS,GACrB,MAAM,aAAa;CAErB,OAAO,EAAE,QAAQ,WAAW;AAC9B;;AAGA,eAAsB,gBACpB,UACA,cACA,SAC4C;CAC5C,MAAM,EAAE,WAAW,gBAAgB,QAAQ,kBAAkB,kBAAkB;CAC/E,IAAI,QAAQ,SACV,MAAM,aAAa;CAErB,IAAI,UAAU,YAAY,GACxB,OAAO,cAAc,UAAU,cAAc;CAK/C,MAAM,aAAa,MAAME,2DAAoB,cAAc,WAAW;EAAE;EAAQ;EAAkB;CAAc,CAAC;CACjH,IAAI,QAAQ,SACV,MAAM,aAAa;CAGrB,IAAI;EAEF,OAAO;GAAE,QAAQ;GAAY,UAAU,MADjB,SAAS,mBAAmB,EAAE,WAAW,CAAC;EACjB;CACjD,SAAS,KAAK;EACZ,IAAI,QAAQ,SACV,MAAM,aAAa;EAErB,IAAIC,sCAAwB,GAAG,KAAK,IAAI,SAAS,EAAE,EAAE,SAASF,gDAAY,gCAAgC;GACxG,MAAM,SAAS,OAAO;GACtB,OAAO;IAAE,QAAQ;IAAY,UAAU,SAAS,YAAY;GAAE;EAChE;EACA,MAAM;CACR;AACF"}
@@ -8,57 +8,47 @@ const MAX_EXPIRED_PROTECT_CHECK_RELOADS = 2;
8
8
  const isExpired = (protectCheck) => protectCheck.expiresAt !== void 0 && protectCheck.expiresAt < Date.now();
9
9
  const expiredError = () => new ClerkRuntimeError("Protect verification expired", { code: ERROR_CODES.PROTECT_CHECK_TIMED_OUT });
10
10
  const abortedError = () => new ClerkRuntimeError("Protect check aborted by caller", { code: "protect_check_aborted" });
11
+ const reloadExpired = async (resource, expiredReloads) => {
12
+ if (expiredReloads.current >= 2) throw expiredError();
13
+ expiredReloads.current += 1;
14
+ await resource.reload();
15
+ const refreshed = resource.getProtectCheck();
16
+ if (!refreshed) return {
17
+ status: "resolved",
18
+ resource: resource.getResource()
19
+ };
20
+ if (isExpired(refreshed)) throw expiredError();
21
+ return { status: "reissued" };
22
+ };
11
23
  /** Runs one Protect challenge against a sign-in or sign-up resource and submits the proof token. */
12
- var ProtectCheckRunner = class {
13
- resource;
14
- expiredReloads = 0;
15
- constructor(resource) {
16
- this.resource = resource;
17
- }
18
- reset() {
19
- this.expiredReloads = 0;
20
- }
21
- async run(protectCheck, options) {
22
- const { container, signal, setWidgetVisible, loadTimeoutMs } = options;
23
- if (signal?.aborted) throw abortedError();
24
- if (isExpired(protectCheck)) return this.reloadExpired();
25
- while (container.firstChild) container.removeChild(container.firstChild);
26
- const proofToken = await executeProtectCheck(protectCheck, container, {
27
- signal,
28
- setWidgetVisible,
29
- loadTimeoutMs
30
- });
24
+ async function runProtectCheck(resource, protectCheck, options) {
25
+ const { container, expiredReloads, signal, setWidgetVisible, loadTimeoutMs } = options;
26
+ if (signal?.aborted) throw abortedError();
27
+ if (isExpired(protectCheck)) return reloadExpired(resource, expiredReloads);
28
+ const proofToken = await executeProtectCheck(protectCheck, container, {
29
+ signal,
30
+ setWidgetVisible,
31
+ loadTimeoutMs
32
+ });
33
+ if (signal?.aborted) throw abortedError();
34
+ try {
35
+ return {
36
+ status: "resolved",
37
+ resource: await resource.submitProtectCheck({ proofToken })
38
+ };
39
+ } catch (err) {
31
40
  if (signal?.aborted) throw abortedError();
32
- try {
41
+ if (isClerkAPIResponseError(err) && err.errors?.[0]?.code === ERROR_CODES.PROTECT_CHECK_ALREADY_RESOLVED) {
42
+ await resource.reload();
33
43
  return {
34
44
  status: "resolved",
35
- resource: await this.resource.submitProtectCheck({ proofToken })
45
+ resource: resource.getResource()
36
46
  };
37
- } catch (err) {
38
- if (isClerkAPIResponseError(err) && err.errors?.[0]?.code === ERROR_CODES.PROTECT_CHECK_ALREADY_RESOLVED) {
39
- await this.resource.reload();
40
- return {
41
- status: "resolved",
42
- resource: this.resource.getResource()
43
- };
44
- }
45
- throw err;
46
47
  }
48
+ throw err;
47
49
  }
48
- async reloadExpired() {
49
- if (this.expiredReloads >= 2) throw expiredError();
50
- this.expiredReloads += 1;
51
- await this.resource.reload();
52
- const refreshed = this.resource.getProtectCheck();
53
- if (!refreshed) return {
54
- status: "resolved",
55
- resource: this.resource.getResource()
56
- };
57
- if (isExpired(refreshed)) throw expiredError();
58
- return { status: "reissued" };
59
- }
60
- };
50
+ }
61
51
 
62
52
  //#endregion
63
- export { MAX_EXPIRED_PROTECT_CHECK_RELOADS, ProtectCheckRunner };
53
+ export { MAX_EXPIRED_PROTECT_CHECK_RELOADS, runProtectCheck };
64
54
  //# sourceMappingURL=protectCheckRunner.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"protectCheckRunner.mjs","names":[],"sources":["../../../src/internal/clerk-js/protectCheckRunner.ts"],"sourcesContent":["import { ClerkRuntimeError, isClerkAPIResponseError } from '../../error';\nimport type { ProtectCheckResource } from '../../types';\nimport { ERROR_CODES } from './constants';\nimport { executeProtectCheck } from './protectCheck';\n\n// A GET reload does not re-mint an expired challenge today, so an uncapped reload loop would spin forever.\nexport const MAX_EXPIRED_PROTECT_CHECK_RELOADS = 2;\n\nexport interface ProtectCheckRunnerResource<TResource> {\n getProtectCheck: () => ProtectCheckResource | null | undefined;\n getResource: () => TResource;\n reload: () => Promise<unknown>;\n submitProtectCheck: (params: { proofToken: string }) => Promise<TResource>;\n}\n\nexport interface ProtectCheckRunOptions {\n container: HTMLDivElement;\n signal?: AbortSignal;\n setWidgetVisible?: (visible: boolean) => Promise<void>;\n loadTimeoutMs?: number;\n}\n\n/** `reissued` means the expired challenge was replaced by a fresh one on reload, so run again with it. */\nexport type ProtectCheckRunOutcome<TResource> = { status: 'resolved'; resource: TResource } | { status: 'reissued' };\n\nconst isExpired = (protectCheck: ProtectCheckResource) =>\n protectCheck.expiresAt !== undefined && protectCheck.expiresAt < Date.now();\n\nconst expiredError = () =>\n new ClerkRuntimeError('Protect verification expired', { code: ERROR_CODES.PROTECT_CHECK_TIMED_OUT });\n\nconst abortedError = () => new ClerkRuntimeError('Protect check aborted by caller', { code: 'protect_check_aborted' });\n\n/** Runs one Protect challenge against a sign-in or sign-up resource and submits the proof token. */\nexport class ProtectCheckRunner<TResource> {\n private expiredReloads = 0;\n\n public constructor(private readonly resource: ProtectCheckRunnerResource<TResource>) {}\n\n public reset(): void {\n this.expiredReloads = 0;\n }\n\n public async run(\n protectCheck: ProtectCheckResource,\n options: ProtectCheckRunOptions,\n ): Promise<ProtectCheckRunOutcome<TResource>> {\n const { container, signal, setWidgetVisible, loadTimeoutMs } = options;\n if (signal?.aborted) {\n throw abortedError();\n }\n if (isExpired(protectCheck)) {\n return this.reloadExpired();\n }\n\n while (container.firstChild) {\n container.removeChild(container.firstChild);\n }\n\n // Deliberately unraced. Only the module load is bounded. The challenge itself may wait on a\n // person, and a timeout here used to abort valid challenges.\n const proofToken = await executeProtectCheck(protectCheck, container, { signal, setWidgetVisible, loadTimeoutMs });\n if (signal?.aborted) {\n throw abortedError();\n }\n\n try {\n const resource = await this.resource.submitProtectCheck({ proofToken });\n return { status: 'resolved', resource };\n } catch (err) {\n if (isClerkAPIResponseError(err) && err.errors?.[0]?.code === ERROR_CODES.PROTECT_CHECK_ALREADY_RESOLVED) {\n await this.resource.reload();\n return { status: 'resolved', resource: this.resource.getResource() };\n }\n throw err;\n }\n }\n\n private async reloadExpired(): Promise<ProtectCheckRunOutcome<TResource>> {\n if (this.expiredReloads >= MAX_EXPIRED_PROTECT_CHECK_RELOADS) {\n throw expiredError();\n }\n this.expiredReloads += 1;\n\n await this.resource.reload();\n\n const refreshed = this.resource.getProtectCheck();\n if (!refreshed) {\n return { status: 'resolved', resource: this.resource.getResource() };\n }\n if (isExpired(refreshed)) {\n throw expiredError();\n }\n return { status: 'reissued' };\n }\n}\n"],"mappings":";;;;;;AAMA,MAAa,oCAAoC;AAmBjD,MAAM,aAAa,iBACjB,aAAa,cAAc,UAAa,aAAa,YAAY,KAAK,IAAI;AAE5E,MAAM,qBACJ,IAAI,kBAAkB,gCAAgC,EAAE,MAAM,YAAY,wBAAwB,CAAC;AAErG,MAAM,qBAAqB,IAAI,kBAAkB,mCAAmC,EAAE,MAAM,wBAAwB,CAAC;;AAGrH,IAAa,qBAAb,MAA2C;CAGL;CAFpC,AAAQ,iBAAiB;CAEzB,AAAO,YAAY,AAAiB,UAAiD;EAAjD;CAAkD;CAEtF,AAAO,QAAc;EACnB,KAAK,iBAAiB;CACxB;CAEA,MAAa,IACX,cACA,SAC4C;EAC5C,MAAM,EAAE,WAAW,QAAQ,kBAAkB,kBAAkB;EAC/D,IAAI,QAAQ,SACV,MAAM,aAAa;EAErB,IAAI,UAAU,YAAY,GACxB,OAAO,KAAK,cAAc;EAG5B,OAAO,UAAU,YACf,UAAU,YAAY,UAAU,UAAU;EAK5C,MAAM,aAAa,MAAM,oBAAoB,cAAc,WAAW;GAAE;GAAQ;GAAkB;EAAc,CAAC;EACjH,IAAI,QAAQ,SACV,MAAM,aAAa;EAGrB,IAAI;GAEF,OAAO;IAAE,QAAQ;IAAY,gBADN,KAAK,SAAS,mBAAmB,EAAE,WAAW,CAAC;GAChC;EACxC,SAAS,KAAK;GACZ,IAAI,wBAAwB,GAAG,KAAK,IAAI,SAAS,EAAE,EAAE,SAAS,YAAY,gCAAgC;IACxG,MAAM,KAAK,SAAS,OAAO;IAC3B,OAAO;KAAE,QAAQ;KAAY,UAAU,KAAK,SAAS,YAAY;IAAE;GACrE;GACA,MAAM;EACR;CACF;CAEA,MAAc,gBAA4D;EACxE,IAAI,KAAK,qBACP,MAAM,aAAa;EAErB,KAAK,kBAAkB;EAEvB,MAAM,KAAK,SAAS,OAAO;EAE3B,MAAM,YAAY,KAAK,SAAS,gBAAgB;EAChD,IAAI,CAAC,WACH,OAAO;GAAE,QAAQ;GAAY,UAAU,KAAK,SAAS,YAAY;EAAE;EAErE,IAAI,UAAU,SAAS,GACrB,MAAM,aAAa;EAErB,OAAO,EAAE,QAAQ,WAAW;CAC9B;AACF"}
1
+ {"version":3,"file":"protectCheckRunner.mjs","names":[],"sources":["../../../src/internal/clerk-js/protectCheckRunner.ts"],"sourcesContent":["import { ClerkRuntimeError, isClerkAPIResponseError } from '../../error';\nimport type { ProtectCheckResource } from '../../types';\nimport { ERROR_CODES } from './constants';\nimport { executeProtectCheck } from './protectCheck';\n\n// A GET reload does not re-mint an expired challenge today, so an uncapped reload loop would spin forever.\nexport const MAX_EXPIRED_PROTECT_CHECK_RELOADS = 2;\n\nexport interface ProtectCheckRunnerResource<TResource> {\n getProtectCheck: () => ProtectCheckResource | null | undefined;\n getResource: () => TResource;\n reload: () => Promise<unknown>;\n submitProtectCheck: (params: { proofToken: string }) => Promise<TResource>;\n}\n\nexport interface ProtectCheckRunOptions {\n container: HTMLDivElement;\n expiredReloads: { current: number };\n signal?: AbortSignal;\n setWidgetVisible?: (visible: boolean) => Promise<void>;\n loadTimeoutMs?: number;\n}\n\n/** `reissued` means the expired challenge was replaced by a fresh one on reload, so run again with it. */\nexport type ProtectCheckRunOutcome<TResource> = { status: 'resolved'; resource: TResource } | { status: 'reissued' };\n\nconst isExpired = (protectCheck: ProtectCheckResource) =>\n protectCheck.expiresAt !== undefined && protectCheck.expiresAt < Date.now();\n\nconst expiredError = () =>\n new ClerkRuntimeError('Protect verification expired', { code: ERROR_CODES.PROTECT_CHECK_TIMED_OUT });\n\nconst abortedError = () => new ClerkRuntimeError('Protect check aborted by caller', { code: 'protect_check_aborted' });\n\nconst reloadExpired = async <TResource>(\n resource: ProtectCheckRunnerResource<TResource>,\n expiredReloads: { current: number },\n): Promise<ProtectCheckRunOutcome<TResource>> => {\n if (expiredReloads.current >= MAX_EXPIRED_PROTECT_CHECK_RELOADS) {\n throw expiredError();\n }\n expiredReloads.current += 1;\n\n await resource.reload();\n\n const refreshed = resource.getProtectCheck();\n if (!refreshed) {\n return { status: 'resolved', resource: resource.getResource() };\n }\n if (isExpired(refreshed)) {\n throw expiredError();\n }\n return { status: 'reissued' };\n};\n\n/** Runs one Protect challenge against a sign-in or sign-up resource and submits the proof token. */\nexport async function runProtectCheck<TResource>(\n resource: ProtectCheckRunnerResource<TResource>,\n protectCheck: ProtectCheckResource,\n options: ProtectCheckRunOptions,\n): Promise<ProtectCheckRunOutcome<TResource>> {\n const { container, expiredReloads, signal, setWidgetVisible, loadTimeoutMs } = options;\n if (signal?.aborted) {\n throw abortedError();\n }\n if (isExpired(protectCheck)) {\n return reloadExpired(resource, expiredReloads);\n }\n\n // Deliberately unraced. Only the module load is bounded. The challenge itself may wait on a\n // person, and a timeout here used to abort valid challenges.\n const proofToken = await executeProtectCheck(protectCheck, container, { signal, setWidgetVisible, loadTimeoutMs });\n if (signal?.aborted) {\n throw abortedError();\n }\n\n try {\n const updated = await resource.submitProtectCheck({ proofToken });\n return { status: 'resolved', resource: updated };\n } catch (err) {\n if (signal?.aborted) {\n throw abortedError();\n }\n if (isClerkAPIResponseError(err) && err.errors?.[0]?.code === ERROR_CODES.PROTECT_CHECK_ALREADY_RESOLVED) {\n await resource.reload();\n return { status: 'resolved', resource: resource.getResource() };\n }\n throw err;\n }\n}\n"],"mappings":";;;;;;AAMA,MAAa,oCAAoC;AAoBjD,MAAM,aAAa,iBACjB,aAAa,cAAc,UAAa,aAAa,YAAY,KAAK,IAAI;AAE5E,MAAM,qBACJ,IAAI,kBAAkB,gCAAgC,EAAE,MAAM,YAAY,wBAAwB,CAAC;AAErG,MAAM,qBAAqB,IAAI,kBAAkB,mCAAmC,EAAE,MAAM,wBAAwB,CAAC;AAErH,MAAM,gBAAgB,OACpB,UACA,mBAC+C;CAC/C,IAAI,eAAe,cACjB,MAAM,aAAa;CAErB,eAAe,WAAW;CAE1B,MAAM,SAAS,OAAO;CAEtB,MAAM,YAAY,SAAS,gBAAgB;CAC3C,IAAI,CAAC,WACH,OAAO;EAAE,QAAQ;EAAY,UAAU,SAAS,YAAY;CAAE;CAEhE,IAAI,UAAU,SAAS,GACrB,MAAM,aAAa;CAErB,OAAO,EAAE,QAAQ,WAAW;AAC9B;;AAGA,eAAsB,gBACpB,UACA,cACA,SAC4C;CAC5C,MAAM,EAAE,WAAW,gBAAgB,QAAQ,kBAAkB,kBAAkB;CAC/E,IAAI,QAAQ,SACV,MAAM,aAAa;CAErB,IAAI,UAAU,YAAY,GACxB,OAAO,cAAc,UAAU,cAAc;CAK/C,MAAM,aAAa,MAAM,oBAAoB,cAAc,WAAW;EAAE;EAAQ;EAAkB;CAAc,CAAC;CACjH,IAAI,QAAQ,SACV,MAAM,aAAa;CAGrB,IAAI;EAEF,OAAO;GAAE,QAAQ;GAAY,UAAU,MADjB,SAAS,mBAAmB,EAAE,WAAW,CAAC;EACjB;CACjD,SAAS,KAAK;EACZ,IAAI,QAAQ,SACV,MAAM,aAAa;EAErB,IAAI,wBAAwB,GAAG,KAAK,IAAI,SAAS,EAAE,EAAE,SAAS,YAAY,gCAAgC;GACxG,MAAM,SAAS,OAAO;GACtB,OAAO;IAAE,QAAQ;IAAY,UAAU,SAAS,YAAY;GAAE;EAChE;EACA,MAAM;CACR;AACF"}
package/dist/keys.js CHANGED
@@ -21,7 +21,8 @@ const PUBLISHABLE_FRONTEND_API_DEV_REGEX = /^(([a-z]+)-){2}([0-9]{1,2})\.clerk\.
21
21
  * @returns An unpadded base64-encoded publishable key with appropriate prefix (pk_live_ or pk_test_).
22
22
  */
23
23
  function buildPublishableKey(frontendApi) {
24
- return `${PUBLISHABLE_FRONTEND_API_DEV_REGEX.test(frontendApi) || frontendApi.startsWith("clerk.") && require_constants.LEGACY_DEV_INSTANCE_SUFFIXES.some((s) => frontendApi.endsWith(s)) ? PUBLISHABLE_KEY_TEST_PREFIX : PUBLISHABLE_KEY_LIVE_PREFIX}${require_isomorphicBtoa.isomorphicBtoa(`${frontendApi}$`).replace(/=+$/, "")}`;
24
+ const isLegacyDevKey = frontendApi.startsWith("clerk.") && require_constants.LEGACY_DEV_INSTANCE_SUFFIXES.some((s) => frontendApi.endsWith(s)) && !frontendApi.endsWith(".prod.lclclerk.com");
25
+ return `${PUBLISHABLE_FRONTEND_API_DEV_REGEX.test(frontendApi) || isLegacyDevKey ? PUBLISHABLE_KEY_TEST_PREFIX : PUBLISHABLE_KEY_LIVE_PREFIX}${require_isomorphicBtoa.isomorphicBtoa(`${frontendApi}$`).replace(/=+$/, "")}`;
25
26
  }
26
27
  /**
27
28
  * Derives a publishable key from the current hostname. Intended for multi-domain
package/dist/keys.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","names":["LEGACY_DEV_INSTANCE_SUFFIXES","isomorphicBtoa","keySetupGuidance","isomorphicAtob","DEV_OR_STAGING_SUFFIXES"],"sources":["../src/keys.ts"],"sourcesContent":["import { DEV_OR_STAGING_SUFFIXES, LEGACY_DEV_INSTANCE_SUFFIXES } from './constants';\nimport { keySetupGuidance } from './errors/keySetupGuidance';\nimport { isomorphicAtob } from './isomorphicAtob';\nimport { isomorphicBtoa } from './isomorphicBtoa';\nimport type { PublishableKey } from './types';\n\n/**\n * Configuration options for parsing publishable keys.\n */\ntype ParsePublishableKeyOptions = {\n /** Whether to throw an error if parsing fails */\n fatal?: boolean;\n /** Custom domain to use for satellite instances */\n domain?: string;\n /** Proxy URL to use instead of the decoded frontend API */\n proxyUrl?: string;\n /** Whether this is a satellite instance */\n isSatellite?: boolean;\n};\n\n/** Prefix used for production publishable keys */\nconst PUBLISHABLE_KEY_LIVE_PREFIX = 'pk_live_';\n\n/** Prefix used for development publishable keys */\nconst PUBLISHABLE_KEY_TEST_PREFIX = 'pk_test_';\n\n/**\n * Regular expression that matches development frontend API keys.\n * Matches patterns like: foo-bar-13.clerk.accounts.dev.\n */\nconst PUBLISHABLE_FRONTEND_API_DEV_REGEX = /^(([a-z]+)-){2}([0-9]{1,2})\\.clerk\\.accounts([a-z.]*)(dev|com)$/i;\n\n/**\n * Converts a frontend API URL into an unpadded base64-encoded publishable key.\n *\n * @param frontendApi - The frontend API URL (e.g., 'clerk.example.com').\n * @returns An unpadded base64-encoded publishable key with appropriate prefix (pk_live_ or pk_test_).\n */\nexport function buildPublishableKey(frontendApi: string): string {\n const isDevKey =\n PUBLISHABLE_FRONTEND_API_DEV_REGEX.test(frontendApi) ||\n (frontendApi.startsWith('clerk.') && LEGACY_DEV_INSTANCE_SUFFIXES.some(s => frontendApi.endsWith(s)));\n const keyPrefix = isDevKey ? PUBLISHABLE_KEY_TEST_PREFIX : PUBLISHABLE_KEY_LIVE_PREFIX;\n return `${keyPrefix}${isomorphicBtoa(`${frontendApi}$`).replace(/=+$/, '')}`;\n}\n\n/**\n * Derives a publishable key from the current hostname. Intended for multi-domain\n * setups (e.g. custom domains on top of a default domain) where the correct key\n * must be resolved per request.\n *\n * Pass the configured publishable key as `fallbackKey` so that development\n * instances (pk_test_) are returned as-is instead of being incorrectly derived\n * from the host (e.g. localhost).\n *\n * @example\n * // React (use window.location.hostname, not window.location.host, to avoid including the port)\n * <ClerkProvider publishableKey={publishableKeyFromHost(window.location.hostname, import.meta.env.VITE_CLERK_PUBLISHABLE_KEY)}>\n *\n * @example\n * // Express (inside clerkMiddleware callback)\n * // Validate req.hostname against a known allowlist before passing it in.\n * // When `trust proxy` is enabled, req.hostname reads from X-Forwarded-Host\n * // and can be spoofed if your proxy is not properly configured.\n * const ALLOWED_HOSTS = ['domain-a.com', 'domain-b.com'];\n * clerkMiddleware((req) => {\n * if (!ALLOWED_HOSTS.includes(req.hostname)) throw new Error('Unknown host');\n * return { publishableKey: publishableKeyFromHost(req.hostname, process.env.CLERK_PUBLISHABLE_KEY) };\n * })\n */\nexport function publishableKeyFromHost(host: string, fallbackKey?: string): string {\n if (fallbackKey && isDevelopmentFromPublishableKey(fallbackKey)) {\n return fallbackKey;\n }\n const hostname = host.toLowerCase().replace(/:\\d+$/, '');\n if (!hostname) {\n throw new Error('Host must not be empty.');\n }\n return buildPublishableKey(`clerk.${hostname}`);\n}\n\n/**\n * Validates that a decoded publishable key has the correct format.\n * The decoded value should be a frontend API followed by exactly one '$' at the end.\n *\n * @param decoded - The decoded publishable key string to validate.\n * @returns `true` if the decoded key has valid format, `false` otherwise.\n */\nfunction isValidDecodedPublishableKey(decoded: string): boolean {\n if (!decoded.endsWith('$')) {\n return false;\n }\n\n const withoutTrailing = decoded.slice(0, -1);\n if (withoutTrailing.includes('$')) {\n return false;\n }\n\n return withoutTrailing.includes('.');\n}\n\nexport function parsePublishableKey(\n key: string | undefined,\n options: ParsePublishableKeyOptions & { fatal: true },\n): PublishableKey;\nexport function parsePublishableKey(\n key: string | undefined,\n options?: ParsePublishableKeyOptions,\n): PublishableKey | null;\n/**\n * Parses and validates a publishable key, extracting the frontend API and instance type.\n *\n * @param key - The publishable key to parse.\n * @param options - Configuration options for parsing.\n * @param options.fatal\n * @param options.domain\n * @param options.proxyUrl\n * @param options.isSatellite\n * @returns Parsed publishable key object with instanceType and frontendApi, or null if invalid.\n *\n * @throws {Error} When options.fatal is true and key is missing or invalid.\n */\nexport function parsePublishableKey(\n key: string | undefined,\n options: { fatal?: boolean; domain?: string; proxyUrl?: string; isSatellite?: boolean } = {},\n): PublishableKey | null {\n key = key || '';\n\n if (!key || !isPublishableKey(key)) {\n if (options.fatal && !key) {\n throw new Error(`Publishable key is missing.\\n\\n${keySetupGuidance}`);\n }\n if (options.fatal && !isPublishableKey(key)) {\n throw new Error(\n `Publishable key not valid (expected format: pk_test_... or pk_live_...).\\n\\n${keySetupGuidance}`,\n );\n }\n return null;\n }\n\n const instanceType = key.startsWith(PUBLISHABLE_KEY_LIVE_PREFIX) ? 'production' : 'development';\n\n let decodedFrontendApi: string;\n try {\n decodedFrontendApi = isomorphicAtob(key.split('_')[2]);\n } catch {\n if (options.fatal) {\n throw new Error(`Publishable key not valid: Failed to decode key.\\n\\n${keySetupGuidance}`);\n }\n return null;\n }\n\n if (!isValidDecodedPublishableKey(decodedFrontendApi)) {\n if (options.fatal) {\n throw new Error(`Publishable key not valid: Decoded key has invalid format.\\n\\n${keySetupGuidance}`);\n }\n return null;\n }\n\n let frontendApi = decodedFrontendApi.slice(0, -1);\n\n if (options.proxyUrl) {\n frontendApi = options.proxyUrl;\n } else if (instanceType !== 'development' && options.domain && options.isSatellite) {\n frontendApi = `clerk.${options.domain}`;\n }\n\n return {\n instanceType,\n frontendApi,\n };\n}\n\n/**\n * Checks if the provided key is a valid publishable key.\n *\n * @param key - The key to be checked. Defaults to an empty string if not provided.\n * @returns `true` if 'key' is a valid publishable key, `false` otherwise.\n */\nexport function isPublishableKey(key: string = '') {\n try {\n const hasValidPrefix = key.startsWith(PUBLISHABLE_KEY_LIVE_PREFIX) || key.startsWith(PUBLISHABLE_KEY_TEST_PREFIX);\n\n if (!hasValidPrefix) {\n return false;\n }\n\n const parts = key.split('_');\n if (parts.length !== 3) {\n return false;\n }\n\n const encodedPart = parts[2];\n if (!encodedPart) {\n return false;\n }\n\n const decoded = isomorphicAtob(encodedPart);\n return isValidDecodedPublishableKey(decoded);\n } catch {\n return false;\n }\n}\n\n/**\n * Creates a memoized cache for checking if URLs are development or staging environments.\n * Uses a Map to cache results for better performance on repeated checks.\n *\n * @returns An object with an isDevOrStagingUrl method that checks if a URL is dev/staging.\n */\nexport function createDevOrStagingUrlCache() {\n const devOrStagingUrlCache = new Map<string, boolean>();\n\n return {\n /**\n * Checks if a URL is a development or staging environment.\n *\n * @param url - The URL to check (string or URL object).\n * @returns `true` if the URL is a development or staging environment, `false` otherwise.\n */\n isDevOrStagingUrl: (url: string | URL): boolean => {\n if (!url) {\n return false;\n }\n\n const hostname = typeof url === 'string' ? url : url.hostname;\n let res = devOrStagingUrlCache.get(hostname);\n if (res === undefined) {\n res = DEV_OR_STAGING_SUFFIXES.some(s => hostname.endsWith(s));\n devOrStagingUrlCache.set(hostname, res);\n }\n return res;\n },\n };\n}\n\n/**\n * Checks if a publishable key is for a development environment.\n * Supports both legacy format (test_) and new format (pk_test_).\n *\n * @param apiKey - The API key to check.\n * @returns `true` if the key is for development, `false` otherwise.\n */\nexport function isDevelopmentFromPublishableKey(apiKey: string): boolean {\n return apiKey.startsWith('test_') || apiKey.startsWith('pk_test_');\n}\n\n/**\n * Checks if a publishable key is for a production environment.\n * Supports both legacy format (live_) and new format (pk_live_).\n *\n * @param apiKey - The API key to check.\n * @returns `true` if the key is for production, `false` otherwise.\n */\nexport function isProductionFromPublishableKey(apiKey: string): boolean {\n return apiKey.startsWith('live_') || apiKey.startsWith('pk_live_');\n}\n\n/**\n * Checks if a secret key is for a development environment.\n * Supports both legacy format (test_) and new format (sk_test_).\n *\n * @param apiKey - The secret key to check.\n * @returns `true` if the key is for development, `false` otherwise.\n */\nexport function isDevelopmentFromSecretKey(apiKey: string): boolean {\n return apiKey.startsWith('test_') || apiKey.startsWith('sk_test_');\n}\n\n/**\n * Checks if a secret key is for a production environment.\n * Supports both legacy format (live_) and new format (sk_live_).\n *\n * @param apiKey - The secret key to check.\n * @returns `true` if the key is for production, `false` otherwise.\n */\nexport function isProductionFromSecretKey(apiKey: string): boolean {\n return apiKey.startsWith('live_') || apiKey.startsWith('sk_live_');\n}\n\n/**\n * Generates a unique cookie suffix based on the publishable key using SHA-1 hashing.\n * The suffix is base64-encoded and URL-safe (+ and / characters are replaced).\n *\n * @param publishableKey - The publishable key to generate suffix from.\n * @param subtle - The SubtleCrypto interface to use for hashing (defaults to globalThis.crypto.subtle).\n * @returns A promise that resolves to an 8-character URL-safe base64 string.\n */\nexport async function getCookieSuffix(\n publishableKey: string,\n subtle: SubtleCrypto = globalThis.crypto.subtle,\n): Promise<string> {\n const data = new TextEncoder().encode(publishableKey);\n const digest = await subtle.digest('sha-1', data);\n const stringDigest = String.fromCharCode(...new Uint8Array(digest));\n // Base 64 Encoding with URL and Filename Safe Alphabet: https://datatracker.ietf.org/doc/html/rfc4648#section-5\n return isomorphicBtoa(stringDigest).replace(/\\+/gi, '-').replace(/\\//gi, '_').substring(0, 8);\n}\n\n/**\n * Creates a suffixed cookie name by appending the cookie suffix to the base name.\n * Used to create unique cookie names based on the publishable key.\n *\n * @param cookieName - The base cookie name.\n * @param cookieSuffix - The suffix to append (typically generated by getCookieSuffix).\n * @returns The suffixed cookie name in format: `${cookieName}_${cookieSuffix}`.\n */\nexport const getSuffixedCookieName = (cookieName: string, cookieSuffix: string): string => {\n return `${cookieName}_${cookieSuffix}`;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"keys.js","names":["LEGACY_DEV_INSTANCE_SUFFIXES","isomorphicBtoa","keySetupGuidance","isomorphicAtob","DEV_OR_STAGING_SUFFIXES"],"sources":["../src/keys.ts"],"sourcesContent":["import { DEV_OR_STAGING_SUFFIXES, LEGACY_DEV_INSTANCE_SUFFIXES } from './constants';\nimport { keySetupGuidance } from './errors/keySetupGuidance';\nimport { isomorphicAtob } from './isomorphicAtob';\nimport { isomorphicBtoa } from './isomorphicBtoa';\nimport type { PublishableKey } from './types';\n\n/**\n * Configuration options for parsing publishable keys.\n */\ntype ParsePublishableKeyOptions = {\n /** Whether to throw an error if parsing fails */\n fatal?: boolean;\n /** Custom domain to use for satellite instances */\n domain?: string;\n /** Proxy URL to use instead of the decoded frontend API */\n proxyUrl?: string;\n /** Whether this is a satellite instance */\n isSatellite?: boolean;\n};\n\n/** Prefix used for production publishable keys */\nconst PUBLISHABLE_KEY_LIVE_PREFIX = 'pk_live_';\n\n/** Prefix used for development publishable keys */\nconst PUBLISHABLE_KEY_TEST_PREFIX = 'pk_test_';\n\n/**\n * Regular expression that matches development frontend API keys.\n * Matches patterns like: foo-bar-13.clerk.accounts.dev.\n */\nconst PUBLISHABLE_FRONTEND_API_DEV_REGEX = /^(([a-z]+)-){2}([0-9]{1,2})\\.clerk\\.accounts([a-z.]*)(dev|com)$/i;\n\n/**\n * Converts a frontend API URL into an unpadded base64-encoded publishable key.\n *\n * @param frontendApi - The frontend API URL (e.g., 'clerk.example.com').\n * @returns An unpadded base64-encoded publishable key with appropriate prefix (pk_live_ or pk_test_).\n */\nexport function buildPublishableKey(frontendApi: string): string {\n const isLegacyDevKey =\n frontendApi.startsWith('clerk.') &&\n LEGACY_DEV_INSTANCE_SUFFIXES.some(s => frontendApi.endsWith(s)) &&\n !frontendApi.endsWith('.prod.lclclerk.com');\n const isDevKey = PUBLISHABLE_FRONTEND_API_DEV_REGEX.test(frontendApi) || isLegacyDevKey;\n const keyPrefix = isDevKey ? PUBLISHABLE_KEY_TEST_PREFIX : PUBLISHABLE_KEY_LIVE_PREFIX;\n return `${keyPrefix}${isomorphicBtoa(`${frontendApi}$`).replace(/=+$/, '')}`;\n}\n\n/**\n * Derives a publishable key from the current hostname. Intended for multi-domain\n * setups (e.g. custom domains on top of a default domain) where the correct key\n * must be resolved per request.\n *\n * Pass the configured publishable key as `fallbackKey` so that development\n * instances (pk_test_) are returned as-is instead of being incorrectly derived\n * from the host (e.g. localhost).\n *\n * @example\n * // React (use window.location.hostname, not window.location.host, to avoid including the port)\n * <ClerkProvider publishableKey={publishableKeyFromHost(window.location.hostname, import.meta.env.VITE_CLERK_PUBLISHABLE_KEY)}>\n *\n * @example\n * // Express (inside clerkMiddleware callback)\n * // Validate req.hostname against a known allowlist before passing it in.\n * // When `trust proxy` is enabled, req.hostname reads from X-Forwarded-Host\n * // and can be spoofed if your proxy is not properly configured.\n * const ALLOWED_HOSTS = ['domain-a.com', 'domain-b.com'];\n * clerkMiddleware((req) => {\n * if (!ALLOWED_HOSTS.includes(req.hostname)) throw new Error('Unknown host');\n * return { publishableKey: publishableKeyFromHost(req.hostname, process.env.CLERK_PUBLISHABLE_KEY) };\n * })\n */\nexport function publishableKeyFromHost(host: string, fallbackKey?: string): string {\n if (fallbackKey && isDevelopmentFromPublishableKey(fallbackKey)) {\n return fallbackKey;\n }\n const hostname = host.toLowerCase().replace(/:\\d+$/, '');\n if (!hostname) {\n throw new Error('Host must not be empty.');\n }\n return buildPublishableKey(`clerk.${hostname}`);\n}\n\n/**\n * Validates that a decoded publishable key has the correct format.\n * The decoded value should be a frontend API followed by exactly one '$' at the end.\n *\n * @param decoded - The decoded publishable key string to validate.\n * @returns `true` if the decoded key has valid format, `false` otherwise.\n */\nfunction isValidDecodedPublishableKey(decoded: string): boolean {\n if (!decoded.endsWith('$')) {\n return false;\n }\n\n const withoutTrailing = decoded.slice(0, -1);\n if (withoutTrailing.includes('$')) {\n return false;\n }\n\n return withoutTrailing.includes('.');\n}\n\nexport function parsePublishableKey(\n key: string | undefined,\n options: ParsePublishableKeyOptions & { fatal: true },\n): PublishableKey;\nexport function parsePublishableKey(\n key: string | undefined,\n options?: ParsePublishableKeyOptions,\n): PublishableKey | null;\n/**\n * Parses and validates a publishable key, extracting the frontend API and instance type.\n *\n * @param key - The publishable key to parse.\n * @param options - Configuration options for parsing.\n * @param options.fatal\n * @param options.domain\n * @param options.proxyUrl\n * @param options.isSatellite\n * @returns Parsed publishable key object with instanceType and frontendApi, or null if invalid.\n *\n * @throws {Error} When options.fatal is true and key is missing or invalid.\n */\nexport function parsePublishableKey(\n key: string | undefined,\n options: { fatal?: boolean; domain?: string; proxyUrl?: string; isSatellite?: boolean } = {},\n): PublishableKey | null {\n key = key || '';\n\n if (!key || !isPublishableKey(key)) {\n if (options.fatal && !key) {\n throw new Error(`Publishable key is missing.\\n\\n${keySetupGuidance}`);\n }\n if (options.fatal && !isPublishableKey(key)) {\n throw new Error(\n `Publishable key not valid (expected format: pk_test_... or pk_live_...).\\n\\n${keySetupGuidance}`,\n );\n }\n return null;\n }\n\n const instanceType = key.startsWith(PUBLISHABLE_KEY_LIVE_PREFIX) ? 'production' : 'development';\n\n let decodedFrontendApi: string;\n try {\n decodedFrontendApi = isomorphicAtob(key.split('_')[2]);\n } catch {\n if (options.fatal) {\n throw new Error(`Publishable key not valid: Failed to decode key.\\n\\n${keySetupGuidance}`);\n }\n return null;\n }\n\n if (!isValidDecodedPublishableKey(decodedFrontendApi)) {\n if (options.fatal) {\n throw new Error(`Publishable key not valid: Decoded key has invalid format.\\n\\n${keySetupGuidance}`);\n }\n return null;\n }\n\n let frontendApi = decodedFrontendApi.slice(0, -1);\n\n if (options.proxyUrl) {\n frontendApi = options.proxyUrl;\n } else if (instanceType !== 'development' && options.domain && options.isSatellite) {\n frontendApi = `clerk.${options.domain}`;\n }\n\n return {\n instanceType,\n frontendApi,\n };\n}\n\n/**\n * Checks if the provided key is a valid publishable key.\n *\n * @param key - The key to be checked. Defaults to an empty string if not provided.\n * @returns `true` if 'key' is a valid publishable key, `false` otherwise.\n */\nexport function isPublishableKey(key: string = '') {\n try {\n const hasValidPrefix = key.startsWith(PUBLISHABLE_KEY_LIVE_PREFIX) || key.startsWith(PUBLISHABLE_KEY_TEST_PREFIX);\n\n if (!hasValidPrefix) {\n return false;\n }\n\n const parts = key.split('_');\n if (parts.length !== 3) {\n return false;\n }\n\n const encodedPart = parts[2];\n if (!encodedPart) {\n return false;\n }\n\n const decoded = isomorphicAtob(encodedPart);\n return isValidDecodedPublishableKey(decoded);\n } catch {\n return false;\n }\n}\n\n/**\n * Creates a memoized cache for checking if URLs are development or staging environments.\n * Uses a Map to cache results for better performance on repeated checks.\n *\n * @returns An object with an isDevOrStagingUrl method that checks if a URL is dev/staging.\n */\nexport function createDevOrStagingUrlCache() {\n const devOrStagingUrlCache = new Map<string, boolean>();\n\n return {\n /**\n * Checks if a URL is a development or staging environment.\n *\n * @param url - The URL to check (string or URL object).\n * @returns `true` if the URL is a development or staging environment, `false` otherwise.\n */\n isDevOrStagingUrl: (url: string | URL): boolean => {\n if (!url) {\n return false;\n }\n\n const hostname = typeof url === 'string' ? url : url.hostname;\n let res = devOrStagingUrlCache.get(hostname);\n if (res === undefined) {\n res = DEV_OR_STAGING_SUFFIXES.some(s => hostname.endsWith(s));\n devOrStagingUrlCache.set(hostname, res);\n }\n return res;\n },\n };\n}\n\n/**\n * Checks if a publishable key is for a development environment.\n * Supports both legacy format (test_) and new format (pk_test_).\n *\n * @param apiKey - The API key to check.\n * @returns `true` if the key is for development, `false` otherwise.\n */\nexport function isDevelopmentFromPublishableKey(apiKey: string): boolean {\n return apiKey.startsWith('test_') || apiKey.startsWith('pk_test_');\n}\n\n/**\n * Checks if a publishable key is for a production environment.\n * Supports both legacy format (live_) and new format (pk_live_).\n *\n * @param apiKey - The API key to check.\n * @returns `true` if the key is for production, `false` otherwise.\n */\nexport function isProductionFromPublishableKey(apiKey: string): boolean {\n return apiKey.startsWith('live_') || apiKey.startsWith('pk_live_');\n}\n\n/**\n * Checks if a secret key is for a development environment.\n * Supports both legacy format (test_) and new format (sk_test_).\n *\n * @param apiKey - The secret key to check.\n * @returns `true` if the key is for development, `false` otherwise.\n */\nexport function isDevelopmentFromSecretKey(apiKey: string): boolean {\n return apiKey.startsWith('test_') || apiKey.startsWith('sk_test_');\n}\n\n/**\n * Checks if a secret key is for a production environment.\n * Supports both legacy format (live_) and new format (sk_live_).\n *\n * @param apiKey - The secret key to check.\n * @returns `true` if the key is for production, `false` otherwise.\n */\nexport function isProductionFromSecretKey(apiKey: string): boolean {\n return apiKey.startsWith('live_') || apiKey.startsWith('sk_live_');\n}\n\n/**\n * Generates a unique cookie suffix based on the publishable key using SHA-1 hashing.\n * The suffix is base64-encoded and URL-safe (+ and / characters are replaced).\n *\n * @param publishableKey - The publishable key to generate suffix from.\n * @param subtle - The SubtleCrypto interface to use for hashing (defaults to globalThis.crypto.subtle).\n * @returns A promise that resolves to an 8-character URL-safe base64 string.\n */\nexport async function getCookieSuffix(\n publishableKey: string,\n subtle: SubtleCrypto = globalThis.crypto.subtle,\n): Promise<string> {\n const data = new TextEncoder().encode(publishableKey);\n const digest = await subtle.digest('sha-1', data);\n const stringDigest = String.fromCharCode(...new Uint8Array(digest));\n // Base 64 Encoding with URL and Filename Safe Alphabet: https://datatracker.ietf.org/doc/html/rfc4648#section-5\n return isomorphicBtoa(stringDigest).replace(/\\+/gi, '-').replace(/\\//gi, '_').substring(0, 8);\n}\n\n/**\n * Creates a suffixed cookie name by appending the cookie suffix to the base name.\n * Used to create unique cookie names based on the publishable key.\n *\n * @param cookieName - The base cookie name.\n * @param cookieSuffix - The suffix to append (typically generated by getCookieSuffix).\n * @returns The suffixed cookie name in format: `${cookieName}_${cookieSuffix}`.\n */\nexport const getSuffixedCookieName = (cookieName: string, cookieSuffix: string): string => {\n return `${cookieName}_${cookieSuffix}`;\n};\n"],"mappings":"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"}
package/dist/keys.mjs CHANGED
@@ -20,7 +20,8 @@ const PUBLISHABLE_FRONTEND_API_DEV_REGEX = /^(([a-z]+)-){2}([0-9]{1,2})\.clerk\.
20
20
  * @returns An unpadded base64-encoded publishable key with appropriate prefix (pk_live_ or pk_test_).
21
21
  */
22
22
  function buildPublishableKey(frontendApi) {
23
- return `${PUBLISHABLE_FRONTEND_API_DEV_REGEX.test(frontendApi) || frontendApi.startsWith("clerk.") && LEGACY_DEV_INSTANCE_SUFFIXES.some((s) => frontendApi.endsWith(s)) ? PUBLISHABLE_KEY_TEST_PREFIX : PUBLISHABLE_KEY_LIVE_PREFIX}${isomorphicBtoa(`${frontendApi}$`).replace(/=+$/, "")}`;
23
+ const isLegacyDevKey = frontendApi.startsWith("clerk.") && LEGACY_DEV_INSTANCE_SUFFIXES.some((s) => frontendApi.endsWith(s)) && !frontendApi.endsWith(".prod.lclclerk.com");
24
+ return `${PUBLISHABLE_FRONTEND_API_DEV_REGEX.test(frontendApi) || isLegacyDevKey ? PUBLISHABLE_KEY_TEST_PREFIX : PUBLISHABLE_KEY_LIVE_PREFIX}${isomorphicBtoa(`${frontendApi}$`).replace(/=+$/, "")}`;
24
25
  }
25
26
  /**
26
27
  * Derives a publishable key from the current hostname. Intended for multi-domain
package/dist/keys.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"keys.mjs","names":[],"sources":["../src/keys.ts"],"sourcesContent":["import { DEV_OR_STAGING_SUFFIXES, LEGACY_DEV_INSTANCE_SUFFIXES } from './constants';\nimport { keySetupGuidance } from './errors/keySetupGuidance';\nimport { isomorphicAtob } from './isomorphicAtob';\nimport { isomorphicBtoa } from './isomorphicBtoa';\nimport type { PublishableKey } from './types';\n\n/**\n * Configuration options for parsing publishable keys.\n */\ntype ParsePublishableKeyOptions = {\n /** Whether to throw an error if parsing fails */\n fatal?: boolean;\n /** Custom domain to use for satellite instances */\n domain?: string;\n /** Proxy URL to use instead of the decoded frontend API */\n proxyUrl?: string;\n /** Whether this is a satellite instance */\n isSatellite?: boolean;\n};\n\n/** Prefix used for production publishable keys */\nconst PUBLISHABLE_KEY_LIVE_PREFIX = 'pk_live_';\n\n/** Prefix used for development publishable keys */\nconst PUBLISHABLE_KEY_TEST_PREFIX = 'pk_test_';\n\n/**\n * Regular expression that matches development frontend API keys.\n * Matches patterns like: foo-bar-13.clerk.accounts.dev.\n */\nconst PUBLISHABLE_FRONTEND_API_DEV_REGEX = /^(([a-z]+)-){2}([0-9]{1,2})\\.clerk\\.accounts([a-z.]*)(dev|com)$/i;\n\n/**\n * Converts a frontend API URL into an unpadded base64-encoded publishable key.\n *\n * @param frontendApi - The frontend API URL (e.g., 'clerk.example.com').\n * @returns An unpadded base64-encoded publishable key with appropriate prefix (pk_live_ or pk_test_).\n */\nexport function buildPublishableKey(frontendApi: string): string {\n const isDevKey =\n PUBLISHABLE_FRONTEND_API_DEV_REGEX.test(frontendApi) ||\n (frontendApi.startsWith('clerk.') && LEGACY_DEV_INSTANCE_SUFFIXES.some(s => frontendApi.endsWith(s)));\n const keyPrefix = isDevKey ? PUBLISHABLE_KEY_TEST_PREFIX : PUBLISHABLE_KEY_LIVE_PREFIX;\n return `${keyPrefix}${isomorphicBtoa(`${frontendApi}$`).replace(/=+$/, '')}`;\n}\n\n/**\n * Derives a publishable key from the current hostname. Intended for multi-domain\n * setups (e.g. custom domains on top of a default domain) where the correct key\n * must be resolved per request.\n *\n * Pass the configured publishable key as `fallbackKey` so that development\n * instances (pk_test_) are returned as-is instead of being incorrectly derived\n * from the host (e.g. localhost).\n *\n * @example\n * // React (use window.location.hostname, not window.location.host, to avoid including the port)\n * <ClerkProvider publishableKey={publishableKeyFromHost(window.location.hostname, import.meta.env.VITE_CLERK_PUBLISHABLE_KEY)}>\n *\n * @example\n * // Express (inside clerkMiddleware callback)\n * // Validate req.hostname against a known allowlist before passing it in.\n * // When `trust proxy` is enabled, req.hostname reads from X-Forwarded-Host\n * // and can be spoofed if your proxy is not properly configured.\n * const ALLOWED_HOSTS = ['domain-a.com', 'domain-b.com'];\n * clerkMiddleware((req) => {\n * if (!ALLOWED_HOSTS.includes(req.hostname)) throw new Error('Unknown host');\n * return { publishableKey: publishableKeyFromHost(req.hostname, process.env.CLERK_PUBLISHABLE_KEY) };\n * })\n */\nexport function publishableKeyFromHost(host: string, fallbackKey?: string): string {\n if (fallbackKey && isDevelopmentFromPublishableKey(fallbackKey)) {\n return fallbackKey;\n }\n const hostname = host.toLowerCase().replace(/:\\d+$/, '');\n if (!hostname) {\n throw new Error('Host must not be empty.');\n }\n return buildPublishableKey(`clerk.${hostname}`);\n}\n\n/**\n * Validates that a decoded publishable key has the correct format.\n * The decoded value should be a frontend API followed by exactly one '$' at the end.\n *\n * @param decoded - The decoded publishable key string to validate.\n * @returns `true` if the decoded key has valid format, `false` otherwise.\n */\nfunction isValidDecodedPublishableKey(decoded: string): boolean {\n if (!decoded.endsWith('$')) {\n return false;\n }\n\n const withoutTrailing = decoded.slice(0, -1);\n if (withoutTrailing.includes('$')) {\n return false;\n }\n\n return withoutTrailing.includes('.');\n}\n\nexport function parsePublishableKey(\n key: string | undefined,\n options: ParsePublishableKeyOptions & { fatal: true },\n): PublishableKey;\nexport function parsePublishableKey(\n key: string | undefined,\n options?: ParsePublishableKeyOptions,\n): PublishableKey | null;\n/**\n * Parses and validates a publishable key, extracting the frontend API and instance type.\n *\n * @param key - The publishable key to parse.\n * @param options - Configuration options for parsing.\n * @param options.fatal\n * @param options.domain\n * @param options.proxyUrl\n * @param options.isSatellite\n * @returns Parsed publishable key object with instanceType and frontendApi, or null if invalid.\n *\n * @throws {Error} When options.fatal is true and key is missing or invalid.\n */\nexport function parsePublishableKey(\n key: string | undefined,\n options: { fatal?: boolean; domain?: string; proxyUrl?: string; isSatellite?: boolean } = {},\n): PublishableKey | null {\n key = key || '';\n\n if (!key || !isPublishableKey(key)) {\n if (options.fatal && !key) {\n throw new Error(`Publishable key is missing.\\n\\n${keySetupGuidance}`);\n }\n if (options.fatal && !isPublishableKey(key)) {\n throw new Error(\n `Publishable key not valid (expected format: pk_test_... or pk_live_...).\\n\\n${keySetupGuidance}`,\n );\n }\n return null;\n }\n\n const instanceType = key.startsWith(PUBLISHABLE_KEY_LIVE_PREFIX) ? 'production' : 'development';\n\n let decodedFrontendApi: string;\n try {\n decodedFrontendApi = isomorphicAtob(key.split('_')[2]);\n } catch {\n if (options.fatal) {\n throw new Error(`Publishable key not valid: Failed to decode key.\\n\\n${keySetupGuidance}`);\n }\n return null;\n }\n\n if (!isValidDecodedPublishableKey(decodedFrontendApi)) {\n if (options.fatal) {\n throw new Error(`Publishable key not valid: Decoded key has invalid format.\\n\\n${keySetupGuidance}`);\n }\n return null;\n }\n\n let frontendApi = decodedFrontendApi.slice(0, -1);\n\n if (options.proxyUrl) {\n frontendApi = options.proxyUrl;\n } else if (instanceType !== 'development' && options.domain && options.isSatellite) {\n frontendApi = `clerk.${options.domain}`;\n }\n\n return {\n instanceType,\n frontendApi,\n };\n}\n\n/**\n * Checks if the provided key is a valid publishable key.\n *\n * @param key - The key to be checked. Defaults to an empty string if not provided.\n * @returns `true` if 'key' is a valid publishable key, `false` otherwise.\n */\nexport function isPublishableKey(key: string = '') {\n try {\n const hasValidPrefix = key.startsWith(PUBLISHABLE_KEY_LIVE_PREFIX) || key.startsWith(PUBLISHABLE_KEY_TEST_PREFIX);\n\n if (!hasValidPrefix) {\n return false;\n }\n\n const parts = key.split('_');\n if (parts.length !== 3) {\n return false;\n }\n\n const encodedPart = parts[2];\n if (!encodedPart) {\n return false;\n }\n\n const decoded = isomorphicAtob(encodedPart);\n return isValidDecodedPublishableKey(decoded);\n } catch {\n return false;\n }\n}\n\n/**\n * Creates a memoized cache for checking if URLs are development or staging environments.\n * Uses a Map to cache results for better performance on repeated checks.\n *\n * @returns An object with an isDevOrStagingUrl method that checks if a URL is dev/staging.\n */\nexport function createDevOrStagingUrlCache() {\n const devOrStagingUrlCache = new Map<string, boolean>();\n\n return {\n /**\n * Checks if a URL is a development or staging environment.\n *\n * @param url - The URL to check (string or URL object).\n * @returns `true` if the URL is a development or staging environment, `false` otherwise.\n */\n isDevOrStagingUrl: (url: string | URL): boolean => {\n if (!url) {\n return false;\n }\n\n const hostname = typeof url === 'string' ? url : url.hostname;\n let res = devOrStagingUrlCache.get(hostname);\n if (res === undefined) {\n res = DEV_OR_STAGING_SUFFIXES.some(s => hostname.endsWith(s));\n devOrStagingUrlCache.set(hostname, res);\n }\n return res;\n },\n };\n}\n\n/**\n * Checks if a publishable key is for a development environment.\n * Supports both legacy format (test_) and new format (pk_test_).\n *\n * @param apiKey - The API key to check.\n * @returns `true` if the key is for development, `false` otherwise.\n */\nexport function isDevelopmentFromPublishableKey(apiKey: string): boolean {\n return apiKey.startsWith('test_') || apiKey.startsWith('pk_test_');\n}\n\n/**\n * Checks if a publishable key is for a production environment.\n * Supports both legacy format (live_) and new format (pk_live_).\n *\n * @param apiKey - The API key to check.\n * @returns `true` if the key is for production, `false` otherwise.\n */\nexport function isProductionFromPublishableKey(apiKey: string): boolean {\n return apiKey.startsWith('live_') || apiKey.startsWith('pk_live_');\n}\n\n/**\n * Checks if a secret key is for a development environment.\n * Supports both legacy format (test_) and new format (sk_test_).\n *\n * @param apiKey - The secret key to check.\n * @returns `true` if the key is for development, `false` otherwise.\n */\nexport function isDevelopmentFromSecretKey(apiKey: string): boolean {\n return apiKey.startsWith('test_') || apiKey.startsWith('sk_test_');\n}\n\n/**\n * Checks if a secret key is for a production environment.\n * Supports both legacy format (live_) and new format (sk_live_).\n *\n * @param apiKey - The secret key to check.\n * @returns `true` if the key is for production, `false` otherwise.\n */\nexport function isProductionFromSecretKey(apiKey: string): boolean {\n return apiKey.startsWith('live_') || apiKey.startsWith('sk_live_');\n}\n\n/**\n * Generates a unique cookie suffix based on the publishable key using SHA-1 hashing.\n * The suffix is base64-encoded and URL-safe (+ and / characters are replaced).\n *\n * @param publishableKey - The publishable key to generate suffix from.\n * @param subtle - The SubtleCrypto interface to use for hashing (defaults to globalThis.crypto.subtle).\n * @returns A promise that resolves to an 8-character URL-safe base64 string.\n */\nexport async function getCookieSuffix(\n publishableKey: string,\n subtle: SubtleCrypto = globalThis.crypto.subtle,\n): Promise<string> {\n const data = new TextEncoder().encode(publishableKey);\n const digest = await subtle.digest('sha-1', data);\n const stringDigest = String.fromCharCode(...new Uint8Array(digest));\n // Base 64 Encoding with URL and Filename Safe Alphabet: https://datatracker.ietf.org/doc/html/rfc4648#section-5\n return isomorphicBtoa(stringDigest).replace(/\\+/gi, '-').replace(/\\//gi, '_').substring(0, 8);\n}\n\n/**\n * Creates a suffixed cookie name by appending the cookie suffix to the base name.\n * Used to create unique cookie names based on the publishable key.\n *\n * @param cookieName - The base cookie name.\n * @param cookieSuffix - The suffix to append (typically generated by getCookieSuffix).\n * @returns The suffixed cookie name in format: `${cookieName}_${cookieSuffix}`.\n */\nexport const getSuffixedCookieName = (cookieName: string, cookieSuffix: string): string => {\n return `${cookieName}_${cookieSuffix}`;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"keys.mjs","names":[],"sources":["../src/keys.ts"],"sourcesContent":["import { DEV_OR_STAGING_SUFFIXES, LEGACY_DEV_INSTANCE_SUFFIXES } from './constants';\nimport { keySetupGuidance } from './errors/keySetupGuidance';\nimport { isomorphicAtob } from './isomorphicAtob';\nimport { isomorphicBtoa } from './isomorphicBtoa';\nimport type { PublishableKey } from './types';\n\n/**\n * Configuration options for parsing publishable keys.\n */\ntype ParsePublishableKeyOptions = {\n /** Whether to throw an error if parsing fails */\n fatal?: boolean;\n /** Custom domain to use for satellite instances */\n domain?: string;\n /** Proxy URL to use instead of the decoded frontend API */\n proxyUrl?: string;\n /** Whether this is a satellite instance */\n isSatellite?: boolean;\n};\n\n/** Prefix used for production publishable keys */\nconst PUBLISHABLE_KEY_LIVE_PREFIX = 'pk_live_';\n\n/** Prefix used for development publishable keys */\nconst PUBLISHABLE_KEY_TEST_PREFIX = 'pk_test_';\n\n/**\n * Regular expression that matches development frontend API keys.\n * Matches patterns like: foo-bar-13.clerk.accounts.dev.\n */\nconst PUBLISHABLE_FRONTEND_API_DEV_REGEX = /^(([a-z]+)-){2}([0-9]{1,2})\\.clerk\\.accounts([a-z.]*)(dev|com)$/i;\n\n/**\n * Converts a frontend API URL into an unpadded base64-encoded publishable key.\n *\n * @param frontendApi - The frontend API URL (e.g., 'clerk.example.com').\n * @returns An unpadded base64-encoded publishable key with appropriate prefix (pk_live_ or pk_test_).\n */\nexport function buildPublishableKey(frontendApi: string): string {\n const isLegacyDevKey =\n frontendApi.startsWith('clerk.') &&\n LEGACY_DEV_INSTANCE_SUFFIXES.some(s => frontendApi.endsWith(s)) &&\n !frontendApi.endsWith('.prod.lclclerk.com');\n const isDevKey = PUBLISHABLE_FRONTEND_API_DEV_REGEX.test(frontendApi) || isLegacyDevKey;\n const keyPrefix = isDevKey ? PUBLISHABLE_KEY_TEST_PREFIX : PUBLISHABLE_KEY_LIVE_PREFIX;\n return `${keyPrefix}${isomorphicBtoa(`${frontendApi}$`).replace(/=+$/, '')}`;\n}\n\n/**\n * Derives a publishable key from the current hostname. Intended for multi-domain\n * setups (e.g. custom domains on top of a default domain) where the correct key\n * must be resolved per request.\n *\n * Pass the configured publishable key as `fallbackKey` so that development\n * instances (pk_test_) are returned as-is instead of being incorrectly derived\n * from the host (e.g. localhost).\n *\n * @example\n * // React (use window.location.hostname, not window.location.host, to avoid including the port)\n * <ClerkProvider publishableKey={publishableKeyFromHost(window.location.hostname, import.meta.env.VITE_CLERK_PUBLISHABLE_KEY)}>\n *\n * @example\n * // Express (inside clerkMiddleware callback)\n * // Validate req.hostname against a known allowlist before passing it in.\n * // When `trust proxy` is enabled, req.hostname reads from X-Forwarded-Host\n * // and can be spoofed if your proxy is not properly configured.\n * const ALLOWED_HOSTS = ['domain-a.com', 'domain-b.com'];\n * clerkMiddleware((req) => {\n * if (!ALLOWED_HOSTS.includes(req.hostname)) throw new Error('Unknown host');\n * return { publishableKey: publishableKeyFromHost(req.hostname, process.env.CLERK_PUBLISHABLE_KEY) };\n * })\n */\nexport function publishableKeyFromHost(host: string, fallbackKey?: string): string {\n if (fallbackKey && isDevelopmentFromPublishableKey(fallbackKey)) {\n return fallbackKey;\n }\n const hostname = host.toLowerCase().replace(/:\\d+$/, '');\n if (!hostname) {\n throw new Error('Host must not be empty.');\n }\n return buildPublishableKey(`clerk.${hostname}`);\n}\n\n/**\n * Validates that a decoded publishable key has the correct format.\n * The decoded value should be a frontend API followed by exactly one '$' at the end.\n *\n * @param decoded - The decoded publishable key string to validate.\n * @returns `true` if the decoded key has valid format, `false` otherwise.\n */\nfunction isValidDecodedPublishableKey(decoded: string): boolean {\n if (!decoded.endsWith('$')) {\n return false;\n }\n\n const withoutTrailing = decoded.slice(0, -1);\n if (withoutTrailing.includes('$')) {\n return false;\n }\n\n return withoutTrailing.includes('.');\n}\n\nexport function parsePublishableKey(\n key: string | undefined,\n options: ParsePublishableKeyOptions & { fatal: true },\n): PublishableKey;\nexport function parsePublishableKey(\n key: string | undefined,\n options?: ParsePublishableKeyOptions,\n): PublishableKey | null;\n/**\n * Parses and validates a publishable key, extracting the frontend API and instance type.\n *\n * @param key - The publishable key to parse.\n * @param options - Configuration options for parsing.\n * @param options.fatal\n * @param options.domain\n * @param options.proxyUrl\n * @param options.isSatellite\n * @returns Parsed publishable key object with instanceType and frontendApi, or null if invalid.\n *\n * @throws {Error} When options.fatal is true and key is missing or invalid.\n */\nexport function parsePublishableKey(\n key: string | undefined,\n options: { fatal?: boolean; domain?: string; proxyUrl?: string; isSatellite?: boolean } = {},\n): PublishableKey | null {\n key = key || '';\n\n if (!key || !isPublishableKey(key)) {\n if (options.fatal && !key) {\n throw new Error(`Publishable key is missing.\\n\\n${keySetupGuidance}`);\n }\n if (options.fatal && !isPublishableKey(key)) {\n throw new Error(\n `Publishable key not valid (expected format: pk_test_... or pk_live_...).\\n\\n${keySetupGuidance}`,\n );\n }\n return null;\n }\n\n const instanceType = key.startsWith(PUBLISHABLE_KEY_LIVE_PREFIX) ? 'production' : 'development';\n\n let decodedFrontendApi: string;\n try {\n decodedFrontendApi = isomorphicAtob(key.split('_')[2]);\n } catch {\n if (options.fatal) {\n throw new Error(`Publishable key not valid: Failed to decode key.\\n\\n${keySetupGuidance}`);\n }\n return null;\n }\n\n if (!isValidDecodedPublishableKey(decodedFrontendApi)) {\n if (options.fatal) {\n throw new Error(`Publishable key not valid: Decoded key has invalid format.\\n\\n${keySetupGuidance}`);\n }\n return null;\n }\n\n let frontendApi = decodedFrontendApi.slice(0, -1);\n\n if (options.proxyUrl) {\n frontendApi = options.proxyUrl;\n } else if (instanceType !== 'development' && options.domain && options.isSatellite) {\n frontendApi = `clerk.${options.domain}`;\n }\n\n return {\n instanceType,\n frontendApi,\n };\n}\n\n/**\n * Checks if the provided key is a valid publishable key.\n *\n * @param key - The key to be checked. Defaults to an empty string if not provided.\n * @returns `true` if 'key' is a valid publishable key, `false` otherwise.\n */\nexport function isPublishableKey(key: string = '') {\n try {\n const hasValidPrefix = key.startsWith(PUBLISHABLE_KEY_LIVE_PREFIX) || key.startsWith(PUBLISHABLE_KEY_TEST_PREFIX);\n\n if (!hasValidPrefix) {\n return false;\n }\n\n const parts = key.split('_');\n if (parts.length !== 3) {\n return false;\n }\n\n const encodedPart = parts[2];\n if (!encodedPart) {\n return false;\n }\n\n const decoded = isomorphicAtob(encodedPart);\n return isValidDecodedPublishableKey(decoded);\n } catch {\n return false;\n }\n}\n\n/**\n * Creates a memoized cache for checking if URLs are development or staging environments.\n * Uses a Map to cache results for better performance on repeated checks.\n *\n * @returns An object with an isDevOrStagingUrl method that checks if a URL is dev/staging.\n */\nexport function createDevOrStagingUrlCache() {\n const devOrStagingUrlCache = new Map<string, boolean>();\n\n return {\n /**\n * Checks if a URL is a development or staging environment.\n *\n * @param url - The URL to check (string or URL object).\n * @returns `true` if the URL is a development or staging environment, `false` otherwise.\n */\n isDevOrStagingUrl: (url: string | URL): boolean => {\n if (!url) {\n return false;\n }\n\n const hostname = typeof url === 'string' ? url : url.hostname;\n let res = devOrStagingUrlCache.get(hostname);\n if (res === undefined) {\n res = DEV_OR_STAGING_SUFFIXES.some(s => hostname.endsWith(s));\n devOrStagingUrlCache.set(hostname, res);\n }\n return res;\n },\n };\n}\n\n/**\n * Checks if a publishable key is for a development environment.\n * Supports both legacy format (test_) and new format (pk_test_).\n *\n * @param apiKey - The API key to check.\n * @returns `true` if the key is for development, `false` otherwise.\n */\nexport function isDevelopmentFromPublishableKey(apiKey: string): boolean {\n return apiKey.startsWith('test_') || apiKey.startsWith('pk_test_');\n}\n\n/**\n * Checks if a publishable key is for a production environment.\n * Supports both legacy format (live_) and new format (pk_live_).\n *\n * @param apiKey - The API key to check.\n * @returns `true` if the key is for production, `false` otherwise.\n */\nexport function isProductionFromPublishableKey(apiKey: string): boolean {\n return apiKey.startsWith('live_') || apiKey.startsWith('pk_live_');\n}\n\n/**\n * Checks if a secret key is for a development environment.\n * Supports both legacy format (test_) and new format (sk_test_).\n *\n * @param apiKey - The secret key to check.\n * @returns `true` if the key is for development, `false` otherwise.\n */\nexport function isDevelopmentFromSecretKey(apiKey: string): boolean {\n return apiKey.startsWith('test_') || apiKey.startsWith('sk_test_');\n}\n\n/**\n * Checks if a secret key is for a production environment.\n * Supports both legacy format (live_) and new format (sk_live_).\n *\n * @param apiKey - The secret key to check.\n * @returns `true` if the key is for production, `false` otherwise.\n */\nexport function isProductionFromSecretKey(apiKey: string): boolean {\n return apiKey.startsWith('live_') || apiKey.startsWith('sk_live_');\n}\n\n/**\n * Generates a unique cookie suffix based on the publishable key using SHA-1 hashing.\n * The suffix is base64-encoded and URL-safe (+ and / characters are replaced).\n *\n * @param publishableKey - The publishable key to generate suffix from.\n * @param subtle - The SubtleCrypto interface to use for hashing (defaults to globalThis.crypto.subtle).\n * @returns A promise that resolves to an 8-character URL-safe base64 string.\n */\nexport async function getCookieSuffix(\n publishableKey: string,\n subtle: SubtleCrypto = globalThis.crypto.subtle,\n): Promise<string> {\n const data = new TextEncoder().encode(publishableKey);\n const digest = await subtle.digest('sha-1', data);\n const stringDigest = String.fromCharCode(...new Uint8Array(digest));\n // Base 64 Encoding with URL and Filename Safe Alphabet: https://datatracker.ietf.org/doc/html/rfc4648#section-5\n return isomorphicBtoa(stringDigest).replace(/\\+/gi, '-').replace(/\\//gi, '_').substring(0, 8);\n}\n\n/**\n * Creates a suffixed cookie name by appending the cookie suffix to the base name.\n * Used to create unique cookie names based on the publishable key.\n *\n * @param cookieName - The base cookie name.\n * @param cookieSuffix - The suffix to append (typically generated by getCookieSuffix).\n * @returns The suffixed cookie name in format: `${cookieName}_${cookieSuffix}`.\n */\nexport const getSuffixedCookieName = (cookieName: string, cookieSuffix: string): string => {\n return `${cookieName}_${cookieSuffix}`;\n};\n"],"mappings":"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"}
@@ -140,7 +140,7 @@ const clerkJSScriptUrl = (opts) => {
140
140
  const clerkUIScriptUrl = (opts) => {
141
141
  const { __internal_clerkUIUrl, __internal_clerkUIVersion, proxyUrl, domain, publishableKey } = opts;
142
142
  if (__internal_clerkUIUrl) return __internal_clerkUIUrl;
143
- const version = require_versionSelector.versionSelector(__internal_clerkUIVersion, "1.37.0-snapshot.v20260925223922");
143
+ const version = require_versionSelector.versionSelector(__internal_clerkUIVersion, "1.37.0");
144
144
  if (proxyUrl && require_proxy.isProxyUrlRelative(proxyUrl)) return buildRelativeProxyScriptUrl(proxyUrl, "ui", version, "ui.browser.js");
145
145
  return `https://${buildScriptHost({
146
146
  publishableKey,
@@ -1 +1 @@
1
- {"version":3,"file":"loadClerkJsScript.js","names":["createDevOrStagingUrlCache","buildErrorThrower","ClerkRuntimeError","versionSelector","isProxyUrlRelative","isValidProxyUrl","proxyUrlToAbsoluteURL","parsePublishableKey","addClerkPrefix"],"sources":["../src/loadClerkJsScript.ts"],"sourcesContent":["import { buildErrorThrower, ClerkRuntimeError } from './error';\nimport { createDevOrStagingUrlCache, parsePublishableKey } from './keys';\nimport { loadScript } from './loadScript';\nimport { isProxyUrlRelative, isValidProxyUrl, proxyUrlToAbsoluteURL } from './proxy';\nimport type { SDKMetadata } from './types';\nimport { addClerkPrefix } from './url';\nimport { versionSelector } from './versionSelector';\n\nconst { isDevOrStagingUrl } = createDevOrStagingUrlCache();\n\nconst errorThrower = buildErrorThrower({ packageName: '@clerk/shared' });\n\nexport type LoadClerkJSScriptOptions = {\n publishableKey: string;\n /** @internal */\n __internal_clerkJSUrl?: string;\n /** @internal */\n __internal_clerkJSVersion?: string;\n sdkMetadata?: SDKMetadata;\n proxyUrl?: string;\n domain?: string;\n nonce?: string;\n /**\n * Timeout in milliseconds to wait for clerk-js to load before considering it failed.\n *\n * @default 15000 (15 seconds)\n */\n scriptLoadTimeout?: number;\n};\n\n/**\n * @deprecated Use `LoadClerkJSScriptOptions` instead. This alias will be removed in a future major version.\n */\nexport type LoadClerkJsScriptOptions = LoadClerkJSScriptOptions;\n\nexport type LoadClerkUIScriptOptions = {\n publishableKey: string;\n /** @internal */\n __internal_clerkUIUrl?: string;\n /** @internal */\n __internal_clerkUIVersion?: string;\n proxyUrl?: string;\n domain?: string;\n nonce?: string;\n scriptLoadTimeout?: number;\n};\n\n/**\n * Validates that window.Clerk exists and is properly initialized.\n * This ensures we don't have false positives where the script loads but Clerk is malformed.\n *\n * @returns `true` if window.Clerk exists and has the expected structure with a load method.\n */\nfunction isClerkGlobalProperlyLoaded(prop: 'Clerk' | '__internal_ClerkUICtor'): boolean {\n if (typeof window === 'undefined' || !(window as any)[prop]) {\n return false;\n }\n\n // Basic validation that window.Clerk has the expected structure\n const val = (window as any)[prop];\n return !!val;\n}\nconst isClerkProperlyLoaded = () => isClerkGlobalProperlyLoaded('Clerk');\nconst isClerkUIProperlyLoaded = () => isClerkGlobalProperlyLoaded('__internal_ClerkUICtor');\n\n/**\n * Checks if an existing script has a request error using Performance API.\n *\n * @param scriptUrl - The URL of the script to check.\n * @returns True if the script has failed to load due to a network/HTTP error.\n */\nfunction hasScriptRequestError(scriptUrl: string): boolean {\n if (typeof window === 'undefined' || !window.performance) {\n return false;\n }\n\n const entries = performance.getEntriesByName(scriptUrl, 'resource') as PerformanceResourceTiming[];\n\n if (entries.length === 0) {\n return false;\n }\n\n const scriptEntry = entries[entries.length - 1];\n\n // transferSize === 0 with responseEnd === 0 indicates network failure\n // transferSize === 0 with responseEnd > 0 might be a 4xx/5xx error or blocked request\n if (scriptEntry.transferSize === 0 && scriptEntry.decodedBodySize === 0) {\n // If there was no response at all, it's definitely an error\n if (scriptEntry.responseEnd === 0) {\n return true;\n }\n // If we got a response but no content, likely an HTTP error (4xx/5xx)\n if (scriptEntry.responseEnd > 0 && scriptEntry.responseStart > 0) {\n return true;\n }\n\n if ('responseStatus' in scriptEntry) {\n const status = (scriptEntry as any).responseStatus;\n if (status >= 400) {\n return true;\n }\n if (scriptEntry.responseStatus === 0) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Hotloads the Clerk JS script with robust failure detection.\n *\n * Uses a timeout-based approach to ensure absolute certainty about load success/failure.\n * If the script fails to load within the timeout period, or loads but doesn't create\n * a proper Clerk instance, the promise rejects with an error.\n *\n * @param opts - The options used to build the Clerk JS script URL and load the script.\n * Must include a `publishableKey` if no existing script is found.\n * @returns Promise that resolves with null if Clerk loads successfully, or rejects with an error.\n *\n * @example\n * ```typescript\n * try {\n * await loadClerkJsScript({ publishableKey: 'pk_test_...' });\n * console.log('Clerk loaded successfully');\n * } catch (error) {\n * console.error('Failed to load Clerk:', error.message);\n * }\n * ```\n */\nexport const loadClerkJSScript = async (opts?: LoadClerkJSScriptOptions): Promise<HTMLScriptElement | null> => {\n const timeout = opts?.scriptLoadTimeout ?? 15000;\n const rejectWith = (error?: Error) =>\n new ClerkRuntimeError('Failed to load Clerk JS' + (error?.message ? `, ${error.message}` : ''), {\n code: 'failed_to_load_clerk_js',\n cause: error,\n });\n\n if (isClerkProperlyLoaded()) {\n return null;\n }\n\n if (!opts?.publishableKey) {\n errorThrower.throwMissingPublishableKeyError();\n return null;\n }\n\n const scriptUrl = clerkJSScriptUrl(opts);\n const existingScript = document.querySelector<HTMLScriptElement>('script[data-clerk-js-script]');\n\n if (existingScript) {\n if (hasScriptRequestError(scriptUrl)) {\n existingScript.remove();\n } else {\n try {\n await waitForPredicateWithTimeout(timeout, isClerkProperlyLoaded, rejectWith(), existingScript);\n return null;\n } catch {\n existingScript.remove();\n }\n }\n }\n\n const loadPromise = waitForPredicateWithTimeout(timeout, isClerkProperlyLoaded, rejectWith());\n\n loadScript(scriptUrl, {\n async: true,\n crossOrigin: 'anonymous',\n nonce: opts.nonce,\n beforeLoad: applyAttributesToScript(buildClerkJSScriptAttributes(opts)),\n }).catch(error => {\n throw rejectWith(error);\n });\n\n return loadPromise;\n};\n\nexport const loadClerkUIScript = async (opts?: LoadClerkUIScriptOptions): Promise<HTMLScriptElement | null> => {\n const timeout = opts?.scriptLoadTimeout ?? 15000;\n const rejectWith = (error?: Error) =>\n new ClerkRuntimeError('Failed to load Clerk UI' + (error?.message ? `, ${error.message}` : ''), {\n code: 'failed_to_load_clerk_ui',\n cause: error,\n });\n\n if (isClerkUIProperlyLoaded()) {\n return null;\n }\n\n if (!opts?.publishableKey) {\n errorThrower.throwMissingPublishableKeyError();\n return null;\n }\n\n const scriptUrl = clerkUIScriptUrl(opts);\n const existingScript = document.querySelector<HTMLScriptElement>('script[data-clerk-ui-script]');\n\n if (existingScript) {\n if (hasScriptRequestError(scriptUrl)) {\n existingScript.remove();\n } else {\n try {\n await waitForPredicateWithTimeout(timeout, isClerkUIProperlyLoaded, rejectWith(), existingScript);\n return null;\n } catch {\n existingScript.remove();\n }\n }\n }\n\n const loadPromise = waitForPredicateWithTimeout(timeout, isClerkUIProperlyLoaded, rejectWith());\n\n loadScript(scriptUrl, {\n async: true,\n crossOrigin: 'anonymous',\n nonce: opts.nonce,\n beforeLoad: applyAttributesToScript(buildClerkUIScriptAttributes(opts)),\n }).catch(error => {\n throw rejectWith(error);\n });\n\n return loadPromise;\n};\n\nexport const clerkJSScriptUrl = (opts: LoadClerkJSScriptOptions) => {\n const { __internal_clerkJSUrl, __internal_clerkJSVersion, proxyUrl, domain, publishableKey } = opts;\n\n if (__internal_clerkJSUrl) {\n return __internal_clerkJSUrl;\n }\n\n const version = versionSelector(__internal_clerkJSVersion);\n\n if (proxyUrl && isProxyUrlRelative(proxyUrl)) {\n return buildRelativeProxyScriptUrl(proxyUrl, 'clerk-js', version, 'clerk.browser.js');\n }\n\n const scriptHost = buildScriptHost({ publishableKey, proxyUrl, domain });\n return `https://${scriptHost}/npm/@clerk/clerk-js@${version}/dist/clerk.browser.js`;\n};\n\nexport const clerkUIScriptUrl = (opts: LoadClerkUIScriptOptions) => {\n const { __internal_clerkUIUrl, __internal_clerkUIVersion, proxyUrl, domain, publishableKey } = opts;\n\n if (__internal_clerkUIUrl) {\n return __internal_clerkUIUrl;\n }\n\n const version = versionSelector(__internal_clerkUIVersion, UI_PACKAGE_VERSION);\n\n if (proxyUrl && isProxyUrlRelative(proxyUrl)) {\n return buildRelativeProxyScriptUrl(proxyUrl, 'ui', version, 'ui.browser.js');\n }\n\n const scriptHost = buildScriptHost({ publishableKey, proxyUrl, domain });\n return `https://${scriptHost}/npm/@clerk/ui@${version}/dist/ui.browser.js`;\n};\n\nexport const buildClerkJSScriptAttributes = (options: LoadClerkJSScriptOptions) => {\n const obj: Record<string, string> = {};\n\n if (options.publishableKey) {\n obj['data-clerk-publishable-key'] = options.publishableKey;\n }\n\n if (options.proxyUrl) {\n obj['data-clerk-proxy-url'] = options.proxyUrl;\n }\n\n if (options.domain) {\n obj['data-clerk-domain'] = options.domain;\n }\n\n if (options.nonce) {\n obj.nonce = options.nonce;\n }\n\n return obj;\n};\n\nexport const buildClerkUIScriptAttributes = (options: LoadClerkUIScriptOptions) => {\n // TODO @nikos do we need this?\n return buildClerkJSScriptAttributes(options);\n};\n\nconst applyAttributesToScript = (attributes: Record<string, string>) => (script: HTMLScriptElement) => {\n for (const attribute in attributes) {\n script.setAttribute(attribute, attributes[attribute]);\n }\n};\n\nconst stripTrailingSlashes = (value: string) => {\n while (value.endsWith('/')) {\n value = value.slice(0, -1);\n }\n\n return value;\n};\n\nconst buildRelativeProxyScriptUrl = (proxyUrl: string, packageName: string, version: string, fileName: string) => {\n return `${stripTrailingSlashes(proxyUrl)}/npm/@clerk/${packageName}@${version}/dist/${fileName}`;\n};\n\nexport const buildScriptHost = (opts: { publishableKey: string; proxyUrl?: string; domain?: string }) => {\n const { proxyUrl, domain, publishableKey } = opts;\n\n if (!!proxyUrl && isValidProxyUrl(proxyUrl)) {\n const resolvedProxyUrl = proxyUrlToAbsoluteURL(proxyUrl);\n\n if (isProxyUrlRelative(resolvedProxyUrl)) {\n return parsePublishableKey(publishableKey)?.frontendApi || '';\n }\n\n return resolvedProxyUrl.replace(/http(s)?:\\/\\//, '');\n } else if (domain && !isDevOrStagingUrl(parsePublishableKey(publishableKey)?.frontendApi || '')) {\n return addClerkPrefix(domain);\n } else {\n return parsePublishableKey(publishableKey)?.frontendApi || '';\n }\n};\n\nfunction waitForPredicateWithTimeout(\n timeoutMs: number,\n predicate: () => boolean,\n rejectWith: Error,\n existingScript?: HTMLScriptElement,\n): Promise<HTMLScriptElement | null> {\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n const cleanup = (timeoutId: ReturnType<typeof setTimeout>, pollInterval: ReturnType<typeof setInterval>) => {\n clearTimeout(timeoutId);\n clearInterval(pollInterval);\n };\n\n // Bail out early if the script fails to load, instead of waiting for the entire timeout\n existingScript?.addEventListener('error', () => {\n cleanup(timeoutId, pollInterval);\n reject(rejectWith);\n });\n\n const checkAndResolve = () => {\n if (resolved) {\n return;\n }\n\n if (predicate()) {\n resolved = true;\n cleanup(timeoutId, pollInterval);\n resolve(null);\n }\n };\n\n const handleTimeout = () => {\n if (resolved) {\n return;\n }\n\n resolved = true;\n cleanup(timeoutId, pollInterval);\n\n if (!predicate()) {\n reject(rejectWith);\n } else {\n resolve(null);\n }\n };\n\n const timeoutId = setTimeout(handleTimeout, timeoutMs);\n\n checkAndResolve();\n\n const pollInterval = setInterval(() => {\n if (resolved) {\n clearInterval(pollInterval);\n return;\n }\n checkAndResolve();\n }, 100);\n });\n}\n\nexport function setClerkJSLoadingErrorPackageName(packageName: string) {\n errorThrower.setPackageName({ packageName });\n}\n\n/**\n * @deprecated Use `loadClerkJSScript` instead. This alias will be removed in a future major version.\n */\nexport const loadClerkJsScript = loadClerkJSScript;\n\n/**\n * @deprecated Use `clerkJSScriptUrl` instead. This alias will be removed in a future major version.\n */\nexport const clerkJsScriptUrl = clerkJSScriptUrl;\n\n/**\n * @deprecated Use `buildClerkJSScriptAttributes` instead. This alias will be removed in a future major version.\n */\nexport const buildClerkJsScriptAttributes = buildClerkJSScriptAttributes;\n\n/**\n * @deprecated Use `setClerkJSLoadingErrorPackageName` instead. This alias will be removed in a future major version.\n */\nexport const setClerkJsLoadingErrorPackageName = setClerkJSLoadingErrorPackageName;\n"],"mappings":"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"}
1
+ {"version":3,"file":"loadClerkJsScript.js","names":["createDevOrStagingUrlCache","buildErrorThrower","ClerkRuntimeError","versionSelector","isProxyUrlRelative","isValidProxyUrl","proxyUrlToAbsoluteURL","parsePublishableKey","addClerkPrefix"],"sources":["../src/loadClerkJsScript.ts"],"sourcesContent":["import { buildErrorThrower, ClerkRuntimeError } from './error';\nimport { createDevOrStagingUrlCache, parsePublishableKey } from './keys';\nimport { loadScript } from './loadScript';\nimport { isProxyUrlRelative, isValidProxyUrl, proxyUrlToAbsoluteURL } from './proxy';\nimport type { SDKMetadata } from './types';\nimport { addClerkPrefix } from './url';\nimport { versionSelector } from './versionSelector';\n\nconst { isDevOrStagingUrl } = createDevOrStagingUrlCache();\n\nconst errorThrower = buildErrorThrower({ packageName: '@clerk/shared' });\n\nexport type LoadClerkJSScriptOptions = {\n publishableKey: string;\n /** @internal */\n __internal_clerkJSUrl?: string;\n /** @internal */\n __internal_clerkJSVersion?: string;\n sdkMetadata?: SDKMetadata;\n proxyUrl?: string;\n domain?: string;\n nonce?: string;\n /**\n * Timeout in milliseconds to wait for clerk-js to load before considering it failed.\n *\n * @default 15000 (15 seconds)\n */\n scriptLoadTimeout?: number;\n};\n\n/**\n * @deprecated Use `LoadClerkJSScriptOptions` instead. This alias will be removed in a future major version.\n */\nexport type LoadClerkJsScriptOptions = LoadClerkJSScriptOptions;\n\nexport type LoadClerkUIScriptOptions = {\n publishableKey: string;\n /** @internal */\n __internal_clerkUIUrl?: string;\n /** @internal */\n __internal_clerkUIVersion?: string;\n proxyUrl?: string;\n domain?: string;\n nonce?: string;\n scriptLoadTimeout?: number;\n};\n\n/**\n * Validates that window.Clerk exists and is properly initialized.\n * This ensures we don't have false positives where the script loads but Clerk is malformed.\n *\n * @returns `true` if window.Clerk exists and has the expected structure with a load method.\n */\nfunction isClerkGlobalProperlyLoaded(prop: 'Clerk' | '__internal_ClerkUICtor'): boolean {\n if (typeof window === 'undefined' || !(window as any)[prop]) {\n return false;\n }\n\n // Basic validation that window.Clerk has the expected structure\n const val = (window as any)[prop];\n return !!val;\n}\nconst isClerkProperlyLoaded = () => isClerkGlobalProperlyLoaded('Clerk');\nconst isClerkUIProperlyLoaded = () => isClerkGlobalProperlyLoaded('__internal_ClerkUICtor');\n\n/**\n * Checks if an existing script has a request error using Performance API.\n *\n * @param scriptUrl - The URL of the script to check.\n * @returns True if the script has failed to load due to a network/HTTP error.\n */\nfunction hasScriptRequestError(scriptUrl: string): boolean {\n if (typeof window === 'undefined' || !window.performance) {\n return false;\n }\n\n const entries = performance.getEntriesByName(scriptUrl, 'resource') as PerformanceResourceTiming[];\n\n if (entries.length === 0) {\n return false;\n }\n\n const scriptEntry = entries[entries.length - 1];\n\n // transferSize === 0 with responseEnd === 0 indicates network failure\n // transferSize === 0 with responseEnd > 0 might be a 4xx/5xx error or blocked request\n if (scriptEntry.transferSize === 0 && scriptEntry.decodedBodySize === 0) {\n // If there was no response at all, it's definitely an error\n if (scriptEntry.responseEnd === 0) {\n return true;\n }\n // If we got a response but no content, likely an HTTP error (4xx/5xx)\n if (scriptEntry.responseEnd > 0 && scriptEntry.responseStart > 0) {\n return true;\n }\n\n if ('responseStatus' in scriptEntry) {\n const status = (scriptEntry as any).responseStatus;\n if (status >= 400) {\n return true;\n }\n if (scriptEntry.responseStatus === 0) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Hotloads the Clerk JS script with robust failure detection.\n *\n * Uses a timeout-based approach to ensure absolute certainty about load success/failure.\n * If the script fails to load within the timeout period, or loads but doesn't create\n * a proper Clerk instance, the promise rejects with an error.\n *\n * @param opts - The options used to build the Clerk JS script URL and load the script.\n * Must include a `publishableKey` if no existing script is found.\n * @returns Promise that resolves with null if Clerk loads successfully, or rejects with an error.\n *\n * @example\n * ```typescript\n * try {\n * await loadClerkJsScript({ publishableKey: 'pk_test_...' });\n * console.log('Clerk loaded successfully');\n * } catch (error) {\n * console.error('Failed to load Clerk:', error.message);\n * }\n * ```\n */\nexport const loadClerkJSScript = async (opts?: LoadClerkJSScriptOptions): Promise<HTMLScriptElement | null> => {\n const timeout = opts?.scriptLoadTimeout ?? 15000;\n const rejectWith = (error?: Error) =>\n new ClerkRuntimeError('Failed to load Clerk JS' + (error?.message ? `, ${error.message}` : ''), {\n code: 'failed_to_load_clerk_js',\n cause: error,\n });\n\n if (isClerkProperlyLoaded()) {\n return null;\n }\n\n if (!opts?.publishableKey) {\n errorThrower.throwMissingPublishableKeyError();\n return null;\n }\n\n const scriptUrl = clerkJSScriptUrl(opts);\n const existingScript = document.querySelector<HTMLScriptElement>('script[data-clerk-js-script]');\n\n if (existingScript) {\n if (hasScriptRequestError(scriptUrl)) {\n existingScript.remove();\n } else {\n try {\n await waitForPredicateWithTimeout(timeout, isClerkProperlyLoaded, rejectWith(), existingScript);\n return null;\n } catch {\n existingScript.remove();\n }\n }\n }\n\n const loadPromise = waitForPredicateWithTimeout(timeout, isClerkProperlyLoaded, rejectWith());\n\n loadScript(scriptUrl, {\n async: true,\n crossOrigin: 'anonymous',\n nonce: opts.nonce,\n beforeLoad: applyAttributesToScript(buildClerkJSScriptAttributes(opts)),\n }).catch(error => {\n throw rejectWith(error);\n });\n\n return loadPromise;\n};\n\nexport const loadClerkUIScript = async (opts?: LoadClerkUIScriptOptions): Promise<HTMLScriptElement | null> => {\n const timeout = opts?.scriptLoadTimeout ?? 15000;\n const rejectWith = (error?: Error) =>\n new ClerkRuntimeError('Failed to load Clerk UI' + (error?.message ? `, ${error.message}` : ''), {\n code: 'failed_to_load_clerk_ui',\n cause: error,\n });\n\n if (isClerkUIProperlyLoaded()) {\n return null;\n }\n\n if (!opts?.publishableKey) {\n errorThrower.throwMissingPublishableKeyError();\n return null;\n }\n\n const scriptUrl = clerkUIScriptUrl(opts);\n const existingScript = document.querySelector<HTMLScriptElement>('script[data-clerk-ui-script]');\n\n if (existingScript) {\n if (hasScriptRequestError(scriptUrl)) {\n existingScript.remove();\n } else {\n try {\n await waitForPredicateWithTimeout(timeout, isClerkUIProperlyLoaded, rejectWith(), existingScript);\n return null;\n } catch {\n existingScript.remove();\n }\n }\n }\n\n const loadPromise = waitForPredicateWithTimeout(timeout, isClerkUIProperlyLoaded, rejectWith());\n\n loadScript(scriptUrl, {\n async: true,\n crossOrigin: 'anonymous',\n nonce: opts.nonce,\n beforeLoad: applyAttributesToScript(buildClerkUIScriptAttributes(opts)),\n }).catch(error => {\n throw rejectWith(error);\n });\n\n return loadPromise;\n};\n\nexport const clerkJSScriptUrl = (opts: LoadClerkJSScriptOptions) => {\n const { __internal_clerkJSUrl, __internal_clerkJSVersion, proxyUrl, domain, publishableKey } = opts;\n\n if (__internal_clerkJSUrl) {\n return __internal_clerkJSUrl;\n }\n\n const version = versionSelector(__internal_clerkJSVersion);\n\n if (proxyUrl && isProxyUrlRelative(proxyUrl)) {\n return buildRelativeProxyScriptUrl(proxyUrl, 'clerk-js', version, 'clerk.browser.js');\n }\n\n const scriptHost = buildScriptHost({ publishableKey, proxyUrl, domain });\n return `https://${scriptHost}/npm/@clerk/clerk-js@${version}/dist/clerk.browser.js`;\n};\n\nexport const clerkUIScriptUrl = (opts: LoadClerkUIScriptOptions) => {\n const { __internal_clerkUIUrl, __internal_clerkUIVersion, proxyUrl, domain, publishableKey } = opts;\n\n if (__internal_clerkUIUrl) {\n return __internal_clerkUIUrl;\n }\n\n const version = versionSelector(__internal_clerkUIVersion, UI_PACKAGE_VERSION);\n\n if (proxyUrl && isProxyUrlRelative(proxyUrl)) {\n return buildRelativeProxyScriptUrl(proxyUrl, 'ui', version, 'ui.browser.js');\n }\n\n const scriptHost = buildScriptHost({ publishableKey, proxyUrl, domain });\n return `https://${scriptHost}/npm/@clerk/ui@${version}/dist/ui.browser.js`;\n};\n\nexport const buildClerkJSScriptAttributes = (options: LoadClerkJSScriptOptions) => {\n const obj: Record<string, string> = {};\n\n if (options.publishableKey) {\n obj['data-clerk-publishable-key'] = options.publishableKey;\n }\n\n if (options.proxyUrl) {\n obj['data-clerk-proxy-url'] = options.proxyUrl;\n }\n\n if (options.domain) {\n obj['data-clerk-domain'] = options.domain;\n }\n\n if (options.nonce) {\n obj.nonce = options.nonce;\n }\n\n return obj;\n};\n\nexport const buildClerkUIScriptAttributes = (options: LoadClerkUIScriptOptions) => {\n // TODO @nikos do we need this?\n return buildClerkJSScriptAttributes(options);\n};\n\nconst applyAttributesToScript = (attributes: Record<string, string>) => (script: HTMLScriptElement) => {\n for (const attribute in attributes) {\n script.setAttribute(attribute, attributes[attribute]);\n }\n};\n\nconst stripTrailingSlashes = (value: string) => {\n while (value.endsWith('/')) {\n value = value.slice(0, -1);\n }\n\n return value;\n};\n\nconst buildRelativeProxyScriptUrl = (proxyUrl: string, packageName: string, version: string, fileName: string) => {\n return `${stripTrailingSlashes(proxyUrl)}/npm/@clerk/${packageName}@${version}/dist/${fileName}`;\n};\n\nexport const buildScriptHost = (opts: { publishableKey: string; proxyUrl?: string; domain?: string }) => {\n const { proxyUrl, domain, publishableKey } = opts;\n\n if (!!proxyUrl && isValidProxyUrl(proxyUrl)) {\n const resolvedProxyUrl = proxyUrlToAbsoluteURL(proxyUrl);\n\n if (isProxyUrlRelative(resolvedProxyUrl)) {\n return parsePublishableKey(publishableKey)?.frontendApi || '';\n }\n\n return resolvedProxyUrl.replace(/http(s)?:\\/\\//, '');\n } else if (domain && !isDevOrStagingUrl(parsePublishableKey(publishableKey)?.frontendApi || '')) {\n return addClerkPrefix(domain);\n } else {\n return parsePublishableKey(publishableKey)?.frontendApi || '';\n }\n};\n\nfunction waitForPredicateWithTimeout(\n timeoutMs: number,\n predicate: () => boolean,\n rejectWith: Error,\n existingScript?: HTMLScriptElement,\n): Promise<HTMLScriptElement | null> {\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n const cleanup = (timeoutId: ReturnType<typeof setTimeout>, pollInterval: ReturnType<typeof setInterval>) => {\n clearTimeout(timeoutId);\n clearInterval(pollInterval);\n };\n\n // Bail out early if the script fails to load, instead of waiting for the entire timeout\n existingScript?.addEventListener('error', () => {\n cleanup(timeoutId, pollInterval);\n reject(rejectWith);\n });\n\n const checkAndResolve = () => {\n if (resolved) {\n return;\n }\n\n if (predicate()) {\n resolved = true;\n cleanup(timeoutId, pollInterval);\n resolve(null);\n }\n };\n\n const handleTimeout = () => {\n if (resolved) {\n return;\n }\n\n resolved = true;\n cleanup(timeoutId, pollInterval);\n\n if (!predicate()) {\n reject(rejectWith);\n } else {\n resolve(null);\n }\n };\n\n const timeoutId = setTimeout(handleTimeout, timeoutMs);\n\n checkAndResolve();\n\n const pollInterval = setInterval(() => {\n if (resolved) {\n clearInterval(pollInterval);\n return;\n }\n checkAndResolve();\n }, 100);\n });\n}\n\nexport function setClerkJSLoadingErrorPackageName(packageName: string) {\n errorThrower.setPackageName({ packageName });\n}\n\n/**\n * @deprecated Use `loadClerkJSScript` instead. This alias will be removed in a future major version.\n */\nexport const loadClerkJsScript = loadClerkJSScript;\n\n/**\n * @deprecated Use `clerkJSScriptUrl` instead. This alias will be removed in a future major version.\n */\nexport const clerkJsScriptUrl = clerkJSScriptUrl;\n\n/**\n * @deprecated Use `buildClerkJSScriptAttributes` instead. This alias will be removed in a future major version.\n */\nexport const buildClerkJsScriptAttributes = buildClerkJSScriptAttributes;\n\n/**\n * @deprecated Use `setClerkJSLoadingErrorPackageName` instead. This alias will be removed in a future major version.\n */\nexport const setClerkJsLoadingErrorPackageName = setClerkJSLoadingErrorPackageName;\n"],"mappings":"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"}
@@ -139,7 +139,7 @@ const clerkJSScriptUrl = (opts) => {
139
139
  const clerkUIScriptUrl = (opts) => {
140
140
  const { __internal_clerkUIUrl, __internal_clerkUIVersion, proxyUrl, domain, publishableKey } = opts;
141
141
  if (__internal_clerkUIUrl) return __internal_clerkUIUrl;
142
- const version = versionSelector(__internal_clerkUIVersion, "1.37.0-snapshot.v20260925223922");
142
+ const version = versionSelector(__internal_clerkUIVersion, "1.37.0");
143
143
  if (proxyUrl && isProxyUrlRelative(proxyUrl)) return buildRelativeProxyScriptUrl(proxyUrl, "ui", version, "ui.browser.js");
144
144
  return `https://${buildScriptHost({
145
145
  publishableKey,
@@ -1 +1 @@
1
- {"version":3,"file":"loadClerkJsScript.mjs","names":[],"sources":["../src/loadClerkJsScript.ts"],"sourcesContent":["import { buildErrorThrower, ClerkRuntimeError } from './error';\nimport { createDevOrStagingUrlCache, parsePublishableKey } from './keys';\nimport { loadScript } from './loadScript';\nimport { isProxyUrlRelative, isValidProxyUrl, proxyUrlToAbsoluteURL } from './proxy';\nimport type { SDKMetadata } from './types';\nimport { addClerkPrefix } from './url';\nimport { versionSelector } from './versionSelector';\n\nconst { isDevOrStagingUrl } = createDevOrStagingUrlCache();\n\nconst errorThrower = buildErrorThrower({ packageName: '@clerk/shared' });\n\nexport type LoadClerkJSScriptOptions = {\n publishableKey: string;\n /** @internal */\n __internal_clerkJSUrl?: string;\n /** @internal */\n __internal_clerkJSVersion?: string;\n sdkMetadata?: SDKMetadata;\n proxyUrl?: string;\n domain?: string;\n nonce?: string;\n /**\n * Timeout in milliseconds to wait for clerk-js to load before considering it failed.\n *\n * @default 15000 (15 seconds)\n */\n scriptLoadTimeout?: number;\n};\n\n/**\n * @deprecated Use `LoadClerkJSScriptOptions` instead. This alias will be removed in a future major version.\n */\nexport type LoadClerkJsScriptOptions = LoadClerkJSScriptOptions;\n\nexport type LoadClerkUIScriptOptions = {\n publishableKey: string;\n /** @internal */\n __internal_clerkUIUrl?: string;\n /** @internal */\n __internal_clerkUIVersion?: string;\n proxyUrl?: string;\n domain?: string;\n nonce?: string;\n scriptLoadTimeout?: number;\n};\n\n/**\n * Validates that window.Clerk exists and is properly initialized.\n * This ensures we don't have false positives where the script loads but Clerk is malformed.\n *\n * @returns `true` if window.Clerk exists and has the expected structure with a load method.\n */\nfunction isClerkGlobalProperlyLoaded(prop: 'Clerk' | '__internal_ClerkUICtor'): boolean {\n if (typeof window === 'undefined' || !(window as any)[prop]) {\n return false;\n }\n\n // Basic validation that window.Clerk has the expected structure\n const val = (window as any)[prop];\n return !!val;\n}\nconst isClerkProperlyLoaded = () => isClerkGlobalProperlyLoaded('Clerk');\nconst isClerkUIProperlyLoaded = () => isClerkGlobalProperlyLoaded('__internal_ClerkUICtor');\n\n/**\n * Checks if an existing script has a request error using Performance API.\n *\n * @param scriptUrl - The URL of the script to check.\n * @returns True if the script has failed to load due to a network/HTTP error.\n */\nfunction hasScriptRequestError(scriptUrl: string): boolean {\n if (typeof window === 'undefined' || !window.performance) {\n return false;\n }\n\n const entries = performance.getEntriesByName(scriptUrl, 'resource') as PerformanceResourceTiming[];\n\n if (entries.length === 0) {\n return false;\n }\n\n const scriptEntry = entries[entries.length - 1];\n\n // transferSize === 0 with responseEnd === 0 indicates network failure\n // transferSize === 0 with responseEnd > 0 might be a 4xx/5xx error or blocked request\n if (scriptEntry.transferSize === 0 && scriptEntry.decodedBodySize === 0) {\n // If there was no response at all, it's definitely an error\n if (scriptEntry.responseEnd === 0) {\n return true;\n }\n // If we got a response but no content, likely an HTTP error (4xx/5xx)\n if (scriptEntry.responseEnd > 0 && scriptEntry.responseStart > 0) {\n return true;\n }\n\n if ('responseStatus' in scriptEntry) {\n const status = (scriptEntry as any).responseStatus;\n if (status >= 400) {\n return true;\n }\n if (scriptEntry.responseStatus === 0) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Hotloads the Clerk JS script with robust failure detection.\n *\n * Uses a timeout-based approach to ensure absolute certainty about load success/failure.\n * If the script fails to load within the timeout period, or loads but doesn't create\n * a proper Clerk instance, the promise rejects with an error.\n *\n * @param opts - The options used to build the Clerk JS script URL and load the script.\n * Must include a `publishableKey` if no existing script is found.\n * @returns Promise that resolves with null if Clerk loads successfully, or rejects with an error.\n *\n * @example\n * ```typescript\n * try {\n * await loadClerkJsScript({ publishableKey: 'pk_test_...' });\n * console.log('Clerk loaded successfully');\n * } catch (error) {\n * console.error('Failed to load Clerk:', error.message);\n * }\n * ```\n */\nexport const loadClerkJSScript = async (opts?: LoadClerkJSScriptOptions): Promise<HTMLScriptElement | null> => {\n const timeout = opts?.scriptLoadTimeout ?? 15000;\n const rejectWith = (error?: Error) =>\n new ClerkRuntimeError('Failed to load Clerk JS' + (error?.message ? `, ${error.message}` : ''), {\n code: 'failed_to_load_clerk_js',\n cause: error,\n });\n\n if (isClerkProperlyLoaded()) {\n return null;\n }\n\n if (!opts?.publishableKey) {\n errorThrower.throwMissingPublishableKeyError();\n return null;\n }\n\n const scriptUrl = clerkJSScriptUrl(opts);\n const existingScript = document.querySelector<HTMLScriptElement>('script[data-clerk-js-script]');\n\n if (existingScript) {\n if (hasScriptRequestError(scriptUrl)) {\n existingScript.remove();\n } else {\n try {\n await waitForPredicateWithTimeout(timeout, isClerkProperlyLoaded, rejectWith(), existingScript);\n return null;\n } catch {\n existingScript.remove();\n }\n }\n }\n\n const loadPromise = waitForPredicateWithTimeout(timeout, isClerkProperlyLoaded, rejectWith());\n\n loadScript(scriptUrl, {\n async: true,\n crossOrigin: 'anonymous',\n nonce: opts.nonce,\n beforeLoad: applyAttributesToScript(buildClerkJSScriptAttributes(opts)),\n }).catch(error => {\n throw rejectWith(error);\n });\n\n return loadPromise;\n};\n\nexport const loadClerkUIScript = async (opts?: LoadClerkUIScriptOptions): Promise<HTMLScriptElement | null> => {\n const timeout = opts?.scriptLoadTimeout ?? 15000;\n const rejectWith = (error?: Error) =>\n new ClerkRuntimeError('Failed to load Clerk UI' + (error?.message ? `, ${error.message}` : ''), {\n code: 'failed_to_load_clerk_ui',\n cause: error,\n });\n\n if (isClerkUIProperlyLoaded()) {\n return null;\n }\n\n if (!opts?.publishableKey) {\n errorThrower.throwMissingPublishableKeyError();\n return null;\n }\n\n const scriptUrl = clerkUIScriptUrl(opts);\n const existingScript = document.querySelector<HTMLScriptElement>('script[data-clerk-ui-script]');\n\n if (existingScript) {\n if (hasScriptRequestError(scriptUrl)) {\n existingScript.remove();\n } else {\n try {\n await waitForPredicateWithTimeout(timeout, isClerkUIProperlyLoaded, rejectWith(), existingScript);\n return null;\n } catch {\n existingScript.remove();\n }\n }\n }\n\n const loadPromise = waitForPredicateWithTimeout(timeout, isClerkUIProperlyLoaded, rejectWith());\n\n loadScript(scriptUrl, {\n async: true,\n crossOrigin: 'anonymous',\n nonce: opts.nonce,\n beforeLoad: applyAttributesToScript(buildClerkUIScriptAttributes(opts)),\n }).catch(error => {\n throw rejectWith(error);\n });\n\n return loadPromise;\n};\n\nexport const clerkJSScriptUrl = (opts: LoadClerkJSScriptOptions) => {\n const { __internal_clerkJSUrl, __internal_clerkJSVersion, proxyUrl, domain, publishableKey } = opts;\n\n if (__internal_clerkJSUrl) {\n return __internal_clerkJSUrl;\n }\n\n const version = versionSelector(__internal_clerkJSVersion);\n\n if (proxyUrl && isProxyUrlRelative(proxyUrl)) {\n return buildRelativeProxyScriptUrl(proxyUrl, 'clerk-js', version, 'clerk.browser.js');\n }\n\n const scriptHost = buildScriptHost({ publishableKey, proxyUrl, domain });\n return `https://${scriptHost}/npm/@clerk/clerk-js@${version}/dist/clerk.browser.js`;\n};\n\nexport const clerkUIScriptUrl = (opts: LoadClerkUIScriptOptions) => {\n const { __internal_clerkUIUrl, __internal_clerkUIVersion, proxyUrl, domain, publishableKey } = opts;\n\n if (__internal_clerkUIUrl) {\n return __internal_clerkUIUrl;\n }\n\n const version = versionSelector(__internal_clerkUIVersion, UI_PACKAGE_VERSION);\n\n if (proxyUrl && isProxyUrlRelative(proxyUrl)) {\n return buildRelativeProxyScriptUrl(proxyUrl, 'ui', version, 'ui.browser.js');\n }\n\n const scriptHost = buildScriptHost({ publishableKey, proxyUrl, domain });\n return `https://${scriptHost}/npm/@clerk/ui@${version}/dist/ui.browser.js`;\n};\n\nexport const buildClerkJSScriptAttributes = (options: LoadClerkJSScriptOptions) => {\n const obj: Record<string, string> = {};\n\n if (options.publishableKey) {\n obj['data-clerk-publishable-key'] = options.publishableKey;\n }\n\n if (options.proxyUrl) {\n obj['data-clerk-proxy-url'] = options.proxyUrl;\n }\n\n if (options.domain) {\n obj['data-clerk-domain'] = options.domain;\n }\n\n if (options.nonce) {\n obj.nonce = options.nonce;\n }\n\n return obj;\n};\n\nexport const buildClerkUIScriptAttributes = (options: LoadClerkUIScriptOptions) => {\n // TODO @nikos do we need this?\n return buildClerkJSScriptAttributes(options);\n};\n\nconst applyAttributesToScript = (attributes: Record<string, string>) => (script: HTMLScriptElement) => {\n for (const attribute in attributes) {\n script.setAttribute(attribute, attributes[attribute]);\n }\n};\n\nconst stripTrailingSlashes = (value: string) => {\n while (value.endsWith('/')) {\n value = value.slice(0, -1);\n }\n\n return value;\n};\n\nconst buildRelativeProxyScriptUrl = (proxyUrl: string, packageName: string, version: string, fileName: string) => {\n return `${stripTrailingSlashes(proxyUrl)}/npm/@clerk/${packageName}@${version}/dist/${fileName}`;\n};\n\nexport const buildScriptHost = (opts: { publishableKey: string; proxyUrl?: string; domain?: string }) => {\n const { proxyUrl, domain, publishableKey } = opts;\n\n if (!!proxyUrl && isValidProxyUrl(proxyUrl)) {\n const resolvedProxyUrl = proxyUrlToAbsoluteURL(proxyUrl);\n\n if (isProxyUrlRelative(resolvedProxyUrl)) {\n return parsePublishableKey(publishableKey)?.frontendApi || '';\n }\n\n return resolvedProxyUrl.replace(/http(s)?:\\/\\//, '');\n } else if (domain && !isDevOrStagingUrl(parsePublishableKey(publishableKey)?.frontendApi || '')) {\n return addClerkPrefix(domain);\n } else {\n return parsePublishableKey(publishableKey)?.frontendApi || '';\n }\n};\n\nfunction waitForPredicateWithTimeout(\n timeoutMs: number,\n predicate: () => boolean,\n rejectWith: Error,\n existingScript?: HTMLScriptElement,\n): Promise<HTMLScriptElement | null> {\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n const cleanup = (timeoutId: ReturnType<typeof setTimeout>, pollInterval: ReturnType<typeof setInterval>) => {\n clearTimeout(timeoutId);\n clearInterval(pollInterval);\n };\n\n // Bail out early if the script fails to load, instead of waiting for the entire timeout\n existingScript?.addEventListener('error', () => {\n cleanup(timeoutId, pollInterval);\n reject(rejectWith);\n });\n\n const checkAndResolve = () => {\n if (resolved) {\n return;\n }\n\n if (predicate()) {\n resolved = true;\n cleanup(timeoutId, pollInterval);\n resolve(null);\n }\n };\n\n const handleTimeout = () => {\n if (resolved) {\n return;\n }\n\n resolved = true;\n cleanup(timeoutId, pollInterval);\n\n if (!predicate()) {\n reject(rejectWith);\n } else {\n resolve(null);\n }\n };\n\n const timeoutId = setTimeout(handleTimeout, timeoutMs);\n\n checkAndResolve();\n\n const pollInterval = setInterval(() => {\n if (resolved) {\n clearInterval(pollInterval);\n return;\n }\n checkAndResolve();\n }, 100);\n });\n}\n\nexport function setClerkJSLoadingErrorPackageName(packageName: string) {\n errorThrower.setPackageName({ packageName });\n}\n\n/**\n * @deprecated Use `loadClerkJSScript` instead. This alias will be removed in a future major version.\n */\nexport const loadClerkJsScript = loadClerkJSScript;\n\n/**\n * @deprecated Use `clerkJSScriptUrl` instead. This alias will be removed in a future major version.\n */\nexport const clerkJsScriptUrl = clerkJSScriptUrl;\n\n/**\n * @deprecated Use `buildClerkJSScriptAttributes` instead. This alias will be removed in a future major version.\n */\nexport const buildClerkJsScriptAttributes = buildClerkJSScriptAttributes;\n\n/**\n * @deprecated Use `setClerkJSLoadingErrorPackageName` instead. This alias will be removed in a future major version.\n */\nexport const setClerkJsLoadingErrorPackageName = setClerkJSLoadingErrorPackageName;\n"],"mappings":"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"}
1
+ {"version":3,"file":"loadClerkJsScript.mjs","names":[],"sources":["../src/loadClerkJsScript.ts"],"sourcesContent":["import { buildErrorThrower, ClerkRuntimeError } from './error';\nimport { createDevOrStagingUrlCache, parsePublishableKey } from './keys';\nimport { loadScript } from './loadScript';\nimport { isProxyUrlRelative, isValidProxyUrl, proxyUrlToAbsoluteURL } from './proxy';\nimport type { SDKMetadata } from './types';\nimport { addClerkPrefix } from './url';\nimport { versionSelector } from './versionSelector';\n\nconst { isDevOrStagingUrl } = createDevOrStagingUrlCache();\n\nconst errorThrower = buildErrorThrower({ packageName: '@clerk/shared' });\n\nexport type LoadClerkJSScriptOptions = {\n publishableKey: string;\n /** @internal */\n __internal_clerkJSUrl?: string;\n /** @internal */\n __internal_clerkJSVersion?: string;\n sdkMetadata?: SDKMetadata;\n proxyUrl?: string;\n domain?: string;\n nonce?: string;\n /**\n * Timeout in milliseconds to wait for clerk-js to load before considering it failed.\n *\n * @default 15000 (15 seconds)\n */\n scriptLoadTimeout?: number;\n};\n\n/**\n * @deprecated Use `LoadClerkJSScriptOptions` instead. This alias will be removed in a future major version.\n */\nexport type LoadClerkJsScriptOptions = LoadClerkJSScriptOptions;\n\nexport type LoadClerkUIScriptOptions = {\n publishableKey: string;\n /** @internal */\n __internal_clerkUIUrl?: string;\n /** @internal */\n __internal_clerkUIVersion?: string;\n proxyUrl?: string;\n domain?: string;\n nonce?: string;\n scriptLoadTimeout?: number;\n};\n\n/**\n * Validates that window.Clerk exists and is properly initialized.\n * This ensures we don't have false positives where the script loads but Clerk is malformed.\n *\n * @returns `true` if window.Clerk exists and has the expected structure with a load method.\n */\nfunction isClerkGlobalProperlyLoaded(prop: 'Clerk' | '__internal_ClerkUICtor'): boolean {\n if (typeof window === 'undefined' || !(window as any)[prop]) {\n return false;\n }\n\n // Basic validation that window.Clerk has the expected structure\n const val = (window as any)[prop];\n return !!val;\n}\nconst isClerkProperlyLoaded = () => isClerkGlobalProperlyLoaded('Clerk');\nconst isClerkUIProperlyLoaded = () => isClerkGlobalProperlyLoaded('__internal_ClerkUICtor');\n\n/**\n * Checks if an existing script has a request error using Performance API.\n *\n * @param scriptUrl - The URL of the script to check.\n * @returns True if the script has failed to load due to a network/HTTP error.\n */\nfunction hasScriptRequestError(scriptUrl: string): boolean {\n if (typeof window === 'undefined' || !window.performance) {\n return false;\n }\n\n const entries = performance.getEntriesByName(scriptUrl, 'resource') as PerformanceResourceTiming[];\n\n if (entries.length === 0) {\n return false;\n }\n\n const scriptEntry = entries[entries.length - 1];\n\n // transferSize === 0 with responseEnd === 0 indicates network failure\n // transferSize === 0 with responseEnd > 0 might be a 4xx/5xx error or blocked request\n if (scriptEntry.transferSize === 0 && scriptEntry.decodedBodySize === 0) {\n // If there was no response at all, it's definitely an error\n if (scriptEntry.responseEnd === 0) {\n return true;\n }\n // If we got a response but no content, likely an HTTP error (4xx/5xx)\n if (scriptEntry.responseEnd > 0 && scriptEntry.responseStart > 0) {\n return true;\n }\n\n if ('responseStatus' in scriptEntry) {\n const status = (scriptEntry as any).responseStatus;\n if (status >= 400) {\n return true;\n }\n if (scriptEntry.responseStatus === 0) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Hotloads the Clerk JS script with robust failure detection.\n *\n * Uses a timeout-based approach to ensure absolute certainty about load success/failure.\n * If the script fails to load within the timeout period, or loads but doesn't create\n * a proper Clerk instance, the promise rejects with an error.\n *\n * @param opts - The options used to build the Clerk JS script URL and load the script.\n * Must include a `publishableKey` if no existing script is found.\n * @returns Promise that resolves with null if Clerk loads successfully, or rejects with an error.\n *\n * @example\n * ```typescript\n * try {\n * await loadClerkJsScript({ publishableKey: 'pk_test_...' });\n * console.log('Clerk loaded successfully');\n * } catch (error) {\n * console.error('Failed to load Clerk:', error.message);\n * }\n * ```\n */\nexport const loadClerkJSScript = async (opts?: LoadClerkJSScriptOptions): Promise<HTMLScriptElement | null> => {\n const timeout = opts?.scriptLoadTimeout ?? 15000;\n const rejectWith = (error?: Error) =>\n new ClerkRuntimeError('Failed to load Clerk JS' + (error?.message ? `, ${error.message}` : ''), {\n code: 'failed_to_load_clerk_js',\n cause: error,\n });\n\n if (isClerkProperlyLoaded()) {\n return null;\n }\n\n if (!opts?.publishableKey) {\n errorThrower.throwMissingPublishableKeyError();\n return null;\n }\n\n const scriptUrl = clerkJSScriptUrl(opts);\n const existingScript = document.querySelector<HTMLScriptElement>('script[data-clerk-js-script]');\n\n if (existingScript) {\n if (hasScriptRequestError(scriptUrl)) {\n existingScript.remove();\n } else {\n try {\n await waitForPredicateWithTimeout(timeout, isClerkProperlyLoaded, rejectWith(), existingScript);\n return null;\n } catch {\n existingScript.remove();\n }\n }\n }\n\n const loadPromise = waitForPredicateWithTimeout(timeout, isClerkProperlyLoaded, rejectWith());\n\n loadScript(scriptUrl, {\n async: true,\n crossOrigin: 'anonymous',\n nonce: opts.nonce,\n beforeLoad: applyAttributesToScript(buildClerkJSScriptAttributes(opts)),\n }).catch(error => {\n throw rejectWith(error);\n });\n\n return loadPromise;\n};\n\nexport const loadClerkUIScript = async (opts?: LoadClerkUIScriptOptions): Promise<HTMLScriptElement | null> => {\n const timeout = opts?.scriptLoadTimeout ?? 15000;\n const rejectWith = (error?: Error) =>\n new ClerkRuntimeError('Failed to load Clerk UI' + (error?.message ? `, ${error.message}` : ''), {\n code: 'failed_to_load_clerk_ui',\n cause: error,\n });\n\n if (isClerkUIProperlyLoaded()) {\n return null;\n }\n\n if (!opts?.publishableKey) {\n errorThrower.throwMissingPublishableKeyError();\n return null;\n }\n\n const scriptUrl = clerkUIScriptUrl(opts);\n const existingScript = document.querySelector<HTMLScriptElement>('script[data-clerk-ui-script]');\n\n if (existingScript) {\n if (hasScriptRequestError(scriptUrl)) {\n existingScript.remove();\n } else {\n try {\n await waitForPredicateWithTimeout(timeout, isClerkUIProperlyLoaded, rejectWith(), existingScript);\n return null;\n } catch {\n existingScript.remove();\n }\n }\n }\n\n const loadPromise = waitForPredicateWithTimeout(timeout, isClerkUIProperlyLoaded, rejectWith());\n\n loadScript(scriptUrl, {\n async: true,\n crossOrigin: 'anonymous',\n nonce: opts.nonce,\n beforeLoad: applyAttributesToScript(buildClerkUIScriptAttributes(opts)),\n }).catch(error => {\n throw rejectWith(error);\n });\n\n return loadPromise;\n};\n\nexport const clerkJSScriptUrl = (opts: LoadClerkJSScriptOptions) => {\n const { __internal_clerkJSUrl, __internal_clerkJSVersion, proxyUrl, domain, publishableKey } = opts;\n\n if (__internal_clerkJSUrl) {\n return __internal_clerkJSUrl;\n }\n\n const version = versionSelector(__internal_clerkJSVersion);\n\n if (proxyUrl && isProxyUrlRelative(proxyUrl)) {\n return buildRelativeProxyScriptUrl(proxyUrl, 'clerk-js', version, 'clerk.browser.js');\n }\n\n const scriptHost = buildScriptHost({ publishableKey, proxyUrl, domain });\n return `https://${scriptHost}/npm/@clerk/clerk-js@${version}/dist/clerk.browser.js`;\n};\n\nexport const clerkUIScriptUrl = (opts: LoadClerkUIScriptOptions) => {\n const { __internal_clerkUIUrl, __internal_clerkUIVersion, proxyUrl, domain, publishableKey } = opts;\n\n if (__internal_clerkUIUrl) {\n return __internal_clerkUIUrl;\n }\n\n const version = versionSelector(__internal_clerkUIVersion, UI_PACKAGE_VERSION);\n\n if (proxyUrl && isProxyUrlRelative(proxyUrl)) {\n return buildRelativeProxyScriptUrl(proxyUrl, 'ui', version, 'ui.browser.js');\n }\n\n const scriptHost = buildScriptHost({ publishableKey, proxyUrl, domain });\n return `https://${scriptHost}/npm/@clerk/ui@${version}/dist/ui.browser.js`;\n};\n\nexport const buildClerkJSScriptAttributes = (options: LoadClerkJSScriptOptions) => {\n const obj: Record<string, string> = {};\n\n if (options.publishableKey) {\n obj['data-clerk-publishable-key'] = options.publishableKey;\n }\n\n if (options.proxyUrl) {\n obj['data-clerk-proxy-url'] = options.proxyUrl;\n }\n\n if (options.domain) {\n obj['data-clerk-domain'] = options.domain;\n }\n\n if (options.nonce) {\n obj.nonce = options.nonce;\n }\n\n return obj;\n};\n\nexport const buildClerkUIScriptAttributes = (options: LoadClerkUIScriptOptions) => {\n // TODO @nikos do we need this?\n return buildClerkJSScriptAttributes(options);\n};\n\nconst applyAttributesToScript = (attributes: Record<string, string>) => (script: HTMLScriptElement) => {\n for (const attribute in attributes) {\n script.setAttribute(attribute, attributes[attribute]);\n }\n};\n\nconst stripTrailingSlashes = (value: string) => {\n while (value.endsWith('/')) {\n value = value.slice(0, -1);\n }\n\n return value;\n};\n\nconst buildRelativeProxyScriptUrl = (proxyUrl: string, packageName: string, version: string, fileName: string) => {\n return `${stripTrailingSlashes(proxyUrl)}/npm/@clerk/${packageName}@${version}/dist/${fileName}`;\n};\n\nexport const buildScriptHost = (opts: { publishableKey: string; proxyUrl?: string; domain?: string }) => {\n const { proxyUrl, domain, publishableKey } = opts;\n\n if (!!proxyUrl && isValidProxyUrl(proxyUrl)) {\n const resolvedProxyUrl = proxyUrlToAbsoluteURL(proxyUrl);\n\n if (isProxyUrlRelative(resolvedProxyUrl)) {\n return parsePublishableKey(publishableKey)?.frontendApi || '';\n }\n\n return resolvedProxyUrl.replace(/http(s)?:\\/\\//, '');\n } else if (domain && !isDevOrStagingUrl(parsePublishableKey(publishableKey)?.frontendApi || '')) {\n return addClerkPrefix(domain);\n } else {\n return parsePublishableKey(publishableKey)?.frontendApi || '';\n }\n};\n\nfunction waitForPredicateWithTimeout(\n timeoutMs: number,\n predicate: () => boolean,\n rejectWith: Error,\n existingScript?: HTMLScriptElement,\n): Promise<HTMLScriptElement | null> {\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n const cleanup = (timeoutId: ReturnType<typeof setTimeout>, pollInterval: ReturnType<typeof setInterval>) => {\n clearTimeout(timeoutId);\n clearInterval(pollInterval);\n };\n\n // Bail out early if the script fails to load, instead of waiting for the entire timeout\n existingScript?.addEventListener('error', () => {\n cleanup(timeoutId, pollInterval);\n reject(rejectWith);\n });\n\n const checkAndResolve = () => {\n if (resolved) {\n return;\n }\n\n if (predicate()) {\n resolved = true;\n cleanup(timeoutId, pollInterval);\n resolve(null);\n }\n };\n\n const handleTimeout = () => {\n if (resolved) {\n return;\n }\n\n resolved = true;\n cleanup(timeoutId, pollInterval);\n\n if (!predicate()) {\n reject(rejectWith);\n } else {\n resolve(null);\n }\n };\n\n const timeoutId = setTimeout(handleTimeout, timeoutMs);\n\n checkAndResolve();\n\n const pollInterval = setInterval(() => {\n if (resolved) {\n clearInterval(pollInterval);\n return;\n }\n checkAndResolve();\n }, 100);\n });\n}\n\nexport function setClerkJSLoadingErrorPackageName(packageName: string) {\n errorThrower.setPackageName({ packageName });\n}\n\n/**\n * @deprecated Use `loadClerkJSScript` instead. This alias will be removed in a future major version.\n */\nexport const loadClerkJsScript = loadClerkJSScript;\n\n/**\n * @deprecated Use `clerkJSScriptUrl` instead. This alias will be removed in a future major version.\n */\nexport const clerkJsScriptUrl = clerkJSScriptUrl;\n\n/**\n * @deprecated Use `buildClerkJSScriptAttributes` instead. This alias will be removed in a future major version.\n */\nexport const buildClerkJsScriptAttributes = buildClerkJSScriptAttributes;\n\n/**\n * @deprecated Use `setClerkJSLoadingErrorPackageName` instead. This alias will be removed in a future major version.\n */\nexport const setClerkJsLoadingErrorPackageName = setClerkJSLoadingErrorPackageName;\n"],"mappings":"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"}
@@ -1238,7 +1238,11 @@ type __internal_LocalizationResource = {
1238
1238
  badge__active: LocalizationValue;
1239
1239
  badge__inactive: LocalizationValue;
1240
1240
  descriptionLine1: LocalizationValue;
1241
+ /**
1242
+ * @deprecated Use `primaryButton__configure` instead.
1243
+ */
1241
1244
  primaryButton__startConfiguration: LocalizationValue;
1245
+ primaryButton__configure: LocalizationValue;
1242
1246
  primaryButton__continueConfiguration: LocalizationValue;
1243
1247
  primaryButton__addConnection: LocalizationValue;
1244
1248
  domainLabel: LocalizationValue;
@@ -1315,13 +1319,12 @@ type __internal_LocalizationResource = {
1315
1319
  };
1316
1320
  directorySyncSection: {
1317
1321
  title: LocalizationValue;
1318
- badge__unconfigured: LocalizationValue;
1319
1322
  badge__ssoRequired: LocalizationValue;
1320
1323
  badge__active: LocalizationValue;
1321
1324
  badge__inactive: LocalizationValue;
1322
1325
  description: LocalizationValue;
1323
1326
  error__load: LocalizationValue;
1324
- primaryButton__startConfiguration: LocalizationValue;
1327
+ primaryButton__configure: LocalizationValue;
1325
1328
  menuAction__edit: LocalizationValue;
1326
1329
  menuAction__activate: LocalizationValue;
1327
1330
  menuAction__deactivate: LocalizationValue;
@@ -2489,6 +2492,7 @@ type UnstableErrors = WithParamName<{
2489
2492
  passkey_retrieval_cancelled: LocalizationValue;
2490
2493
  passkey_registration_cancelled: LocalizationValue;
2491
2494
  passkey_already_exists: LocalizationValue;
2495
+ too_many_unverified_identifications: LocalizationValue;
2492
2496
  web3_missing_identifier: LocalizationValue;
2493
2497
  web3_solana_signature_generation_failed: LocalizationValue;
2494
2498
  web3_signature_request_rejected: LocalizationValue;
@@ -1238,7 +1238,11 @@ type __internal_LocalizationResource = {
1238
1238
  badge__active: LocalizationValue;
1239
1239
  badge__inactive: LocalizationValue;
1240
1240
  descriptionLine1: LocalizationValue;
1241
+ /**
1242
+ * @deprecated Use `primaryButton__configure` instead.
1243
+ */
1241
1244
  primaryButton__startConfiguration: LocalizationValue;
1245
+ primaryButton__configure: LocalizationValue;
1242
1246
  primaryButton__continueConfiguration: LocalizationValue;
1243
1247
  primaryButton__addConnection: LocalizationValue;
1244
1248
  domainLabel: LocalizationValue;
@@ -1315,13 +1319,12 @@ type __internal_LocalizationResource = {
1315
1319
  };
1316
1320
  directorySyncSection: {
1317
1321
  title: LocalizationValue;
1318
- badge__unconfigured: LocalizationValue;
1319
1322
  badge__ssoRequired: LocalizationValue;
1320
1323
  badge__active: LocalizationValue;
1321
1324
  badge__inactive: LocalizationValue;
1322
1325
  description: LocalizationValue;
1323
1326
  error__load: LocalizationValue;
1324
- primaryButton__startConfiguration: LocalizationValue;
1327
+ primaryButton__configure: LocalizationValue;
1325
1328
  menuAction__edit: LocalizationValue;
1326
1329
  menuAction__activate: LocalizationValue;
1327
1330
  menuAction__deactivate: LocalizationValue;
@@ -2489,6 +2492,7 @@ type UnstableErrors = WithParamName<{
2489
2492
  passkey_retrieval_cancelled: LocalizationValue;
2490
2493
  passkey_registration_cancelled: LocalizationValue;
2491
2494
  passkey_already_exists: LocalizationValue;
2495
+ too_many_unverified_identifications: LocalizationValue;
2492
2496
  web3_missing_identifier: LocalizationValue;
2493
2497
  web3_solana_signature_generation_failed: LocalizationValue;
2494
2498
  web3_signature_request_rejected: LocalizationValue;
@@ -12,7 +12,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
12
12
  * @param packageVersion - The version of `@clerk/clerk-js` that will be used if an explicit version is not provided
13
13
  * @returns The npm tag, version or major version to use
14
14
  */
15
- const versionSelector = (clerkJSVersion, packageVersion = "6.35.0-snapshot.v20260925223922") => {
15
+ const versionSelector = (clerkJSVersion, packageVersion = "6.35.0") => {
16
16
  if (clerkJSVersion) return clerkJSVersion;
17
17
  const prereleaseTag = getPrereleaseTag(packageVersion);
18
18
  if (prereleaseTag) {
@@ -1 +1 @@
1
- {"version":3,"file":"versionSelector.js","names":[],"sources":["../src/versionSelector.ts"],"sourcesContent":["/**\n * This version selector is a bit complicated, so here is the flow:\n * 1. Use the clerkJSVersion prop on the provider\n * 2. Use the exact `@clerk/clerk-js` version if it is a `@snapshot` prerelease\n * 3. Use the prerelease tag of `@clerk/clerk-js` or the packageVersion provided\n * 4. Fallback to the major version of `@clerk/clerk-js` or the packageVersion provided\n *\n * @param clerkJSVersion - The optional clerkJSVersion prop on the provider\n * @param packageVersion - The version of `@clerk/clerk-js` that will be used if an explicit version is not provided\n * @returns The npm tag, version or major version to use\n */\nexport const versionSelector = (clerkJSVersion: string | undefined, packageVersion = JS_PACKAGE_VERSION) => {\n if (clerkJSVersion) {\n return clerkJSVersion;\n }\n\n const prereleaseTag = getPrereleaseTag(packageVersion);\n if (prereleaseTag) {\n if (prereleaseTag === 'snapshot') {\n return packageVersion;\n }\n\n return prereleaseTag;\n }\n\n return getMajorVersion(packageVersion);\n};\n\nconst getPrereleaseTag = (packageVersion: string) =>\n packageVersion\n .trim()\n .replace(/^v/, '')\n .match(/-(.+?)(\\.|$)/)?.[1];\n\nexport const getMajorVersion = (packageVersion: string) => packageVersion.trim().replace(/^v/, '').split('.')[0];\n"],"mappings":";;;;;;;;;;;;;;AAWA,MAAa,mBAAmB,gBAAoC,uDAAwC;CAC1G,IAAI,gBACF,OAAO;CAGT,MAAM,gBAAgB,iBAAiB,cAAc;CACrD,IAAI,eAAe;EACjB,IAAI,kBAAkB,YACpB,OAAO;EAGT,OAAO;CACT;CAEA,OAAO,gBAAgB,cAAc;AACvC;AAEA,MAAM,oBAAoB,mBACxB,eACG,KAAK,CAAC,CACN,QAAQ,MAAM,EAAE,CAAC,CACjB,MAAM,cAAc,CAAC,GAAG;AAE7B,MAAa,mBAAmB,mBAA2B,eAAe,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC"}
1
+ {"version":3,"file":"versionSelector.js","names":[],"sources":["../src/versionSelector.ts"],"sourcesContent":["/**\n * This version selector is a bit complicated, so here is the flow:\n * 1. Use the clerkJSVersion prop on the provider\n * 2. Use the exact `@clerk/clerk-js` version if it is a `@snapshot` prerelease\n * 3. Use the prerelease tag of `@clerk/clerk-js` or the packageVersion provided\n * 4. Fallback to the major version of `@clerk/clerk-js` or the packageVersion provided\n *\n * @param clerkJSVersion - The optional clerkJSVersion prop on the provider\n * @param packageVersion - The version of `@clerk/clerk-js` that will be used if an explicit version is not provided\n * @returns The npm tag, version or major version to use\n */\nexport const versionSelector = (clerkJSVersion: string | undefined, packageVersion = JS_PACKAGE_VERSION) => {\n if (clerkJSVersion) {\n return clerkJSVersion;\n }\n\n const prereleaseTag = getPrereleaseTag(packageVersion);\n if (prereleaseTag) {\n if (prereleaseTag === 'snapshot') {\n return packageVersion;\n }\n\n return prereleaseTag;\n }\n\n return getMajorVersion(packageVersion);\n};\n\nconst getPrereleaseTag = (packageVersion: string) =>\n packageVersion\n .trim()\n .replace(/^v/, '')\n .match(/-(.+?)(\\.|$)/)?.[1];\n\nexport const getMajorVersion = (packageVersion: string) => packageVersion.trim().replace(/^v/, '').split('.')[0];\n"],"mappings":";;;;;;;;;;;;;;AAWA,MAAa,mBAAmB,gBAAoC,8BAAwC;CAC1G,IAAI,gBACF,OAAO;CAGT,MAAM,gBAAgB,iBAAiB,cAAc;CACrD,IAAI,eAAe;EACjB,IAAI,kBAAkB,YACpB,OAAO;EAGT,OAAO;CACT;CAEA,OAAO,gBAAgB,cAAc;AACvC;AAEA,MAAM,oBAAoB,mBACxB,eACG,KAAK,CAAC,CACN,QAAQ,MAAM,EAAE,CAAC,CACjB,MAAM,cAAc,CAAC,GAAG;AAE7B,MAAa,mBAAmB,mBAA2B,eAAe,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC"}
@@ -10,7 +10,7 @@
10
10
  * @param packageVersion - The version of `@clerk/clerk-js` that will be used if an explicit version is not provided
11
11
  * @returns The npm tag, version or major version to use
12
12
  */
13
- const versionSelector = (clerkJSVersion, packageVersion = "6.35.0-snapshot.v20260925223922") => {
13
+ const versionSelector = (clerkJSVersion, packageVersion = "6.35.0") => {
14
14
  if (clerkJSVersion) return clerkJSVersion;
15
15
  const prereleaseTag = getPrereleaseTag(packageVersion);
16
16
  if (prereleaseTag) {
@@ -1 +1 @@
1
- {"version":3,"file":"versionSelector.mjs","names":[],"sources":["../src/versionSelector.ts"],"sourcesContent":["/**\n * This version selector is a bit complicated, so here is the flow:\n * 1. Use the clerkJSVersion prop on the provider\n * 2. Use the exact `@clerk/clerk-js` version if it is a `@snapshot` prerelease\n * 3. Use the prerelease tag of `@clerk/clerk-js` or the packageVersion provided\n * 4. Fallback to the major version of `@clerk/clerk-js` or the packageVersion provided\n *\n * @param clerkJSVersion - The optional clerkJSVersion prop on the provider\n * @param packageVersion - The version of `@clerk/clerk-js` that will be used if an explicit version is not provided\n * @returns The npm tag, version or major version to use\n */\nexport const versionSelector = (clerkJSVersion: string | undefined, packageVersion = JS_PACKAGE_VERSION) => {\n if (clerkJSVersion) {\n return clerkJSVersion;\n }\n\n const prereleaseTag = getPrereleaseTag(packageVersion);\n if (prereleaseTag) {\n if (prereleaseTag === 'snapshot') {\n return packageVersion;\n }\n\n return prereleaseTag;\n }\n\n return getMajorVersion(packageVersion);\n};\n\nconst getPrereleaseTag = (packageVersion: string) =>\n packageVersion\n .trim()\n .replace(/^v/, '')\n .match(/-(.+?)(\\.|$)/)?.[1];\n\nexport const getMajorVersion = (packageVersion: string) => packageVersion.trim().replace(/^v/, '').split('.')[0];\n"],"mappings":";;;;;;;;;;;;AAWA,MAAa,mBAAmB,gBAAoC,uDAAwC;CAC1G,IAAI,gBACF,OAAO;CAGT,MAAM,gBAAgB,iBAAiB,cAAc;CACrD,IAAI,eAAe;EACjB,IAAI,kBAAkB,YACpB,OAAO;EAGT,OAAO;CACT;CAEA,OAAO,gBAAgB,cAAc;AACvC;AAEA,MAAM,oBAAoB,mBACxB,eACG,KAAK,CAAC,CACN,QAAQ,MAAM,EAAE,CAAC,CACjB,MAAM,cAAc,CAAC,GAAG;AAE7B,MAAa,mBAAmB,mBAA2B,eAAe,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC"}
1
+ {"version":3,"file":"versionSelector.mjs","names":[],"sources":["../src/versionSelector.ts"],"sourcesContent":["/**\n * This version selector is a bit complicated, so here is the flow:\n * 1. Use the clerkJSVersion prop on the provider\n * 2. Use the exact `@clerk/clerk-js` version if it is a `@snapshot` prerelease\n * 3. Use the prerelease tag of `@clerk/clerk-js` or the packageVersion provided\n * 4. Fallback to the major version of `@clerk/clerk-js` or the packageVersion provided\n *\n * @param clerkJSVersion - The optional clerkJSVersion prop on the provider\n * @param packageVersion - The version of `@clerk/clerk-js` that will be used if an explicit version is not provided\n * @returns The npm tag, version or major version to use\n */\nexport const versionSelector = (clerkJSVersion: string | undefined, packageVersion = JS_PACKAGE_VERSION) => {\n if (clerkJSVersion) {\n return clerkJSVersion;\n }\n\n const prereleaseTag = getPrereleaseTag(packageVersion);\n if (prereleaseTag) {\n if (prereleaseTag === 'snapshot') {\n return packageVersion;\n }\n\n return prereleaseTag;\n }\n\n return getMajorVersion(packageVersion);\n};\n\nconst getPrereleaseTag = (packageVersion: string) =>\n packageVersion\n .trim()\n .replace(/^v/, '')\n .match(/-(.+?)(\\.|$)/)?.[1];\n\nexport const getMajorVersion = (packageVersion: string) => packageVersion.trim().replace(/^v/, '').split('.')[0];\n"],"mappings":";;;;;;;;;;;;AAWA,MAAa,mBAAmB,gBAAoC,8BAAwC;CAC1G,IAAI,gBACF,OAAO;CAGT,MAAM,gBAAgB,iBAAiB,cAAc;CACrD,IAAI,eAAe;EACjB,IAAI,kBAAkB,YACpB,OAAO;EAGT,OAAO;CACT;CAEA,OAAO,gBAAgB,cAAc;AACvC;AAEA,MAAM,oBAAoB,mBACxB,eACG,KAAK,CAAC,CACN,QAAQ,MAAM,EAAE,CAAC,CACjB,MAAM,cAAc,CAAC,GAAG;AAE7B,MAAa,mBAAmB,mBAA2B,eAAe,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clerk/shared",
3
- "version": "4.37.0-snapshot.v20260925223922",
3
+ "version": "4.37.0",
4
4
  "description": "Internal package utils used by the Clerk SDKs",
5
5
  "repository": {
6
6
  "type": "git",