@authowl/react 0.21.2 → 0.22.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.
@@ -0,0 +1,68 @@
1
+ "use client";
2
+
3
+ // src/components/captcha-loader.ts
4
+ var SCRIPT_LOAD_TIMEOUT_MS = 1e4;
5
+ var GLOBAL_POLL_INTERVAL_MS = 25;
6
+ var loaders = /* @__PURE__ */ new Map();
7
+ function providerGlobal(adapter) {
8
+ const api = window[adapter.globalName];
9
+ return typeof api?.render === "function" ? api : void 0;
10
+ }
11
+ function loadCaptcha(adapter) {
12
+ const available = providerGlobal(adapter);
13
+ if (available) return Promise.resolve(available);
14
+ const cached = loaders.get(adapter.scriptUrl);
15
+ if (cached) return cached;
16
+ const loading = new Promise((resolve, reject) => {
17
+ const existing = document.querySelector(
18
+ `script[src="${adapter.scriptUrl}"]`
19
+ );
20
+ const script = existing ?? document.createElement("script");
21
+ let poll;
22
+ const cleanup = () => {
23
+ clearTimeout(timeout);
24
+ if (poll !== void 0) clearInterval(poll);
25
+ script.removeEventListener("load", loaded);
26
+ script.removeEventListener("error", failed);
27
+ };
28
+ const resolveWhenPublished = () => {
29
+ const api = providerGlobal(adapter);
30
+ if (!api) return false;
31
+ cleanup();
32
+ resolve(api);
33
+ return true;
34
+ };
35
+ const loaded = () => {
36
+ if (!resolveWhenPublished() && poll === void 0) {
37
+ poll = setInterval(resolveWhenPublished, GLOBAL_POLL_INTERVAL_MS);
38
+ }
39
+ };
40
+ const failed = () => {
41
+ cleanup();
42
+ if (!existing) script.remove();
43
+ reject(new Error(`${adapter.label} failed to load`));
44
+ };
45
+ const timeout = setTimeout(failed, SCRIPT_LOAD_TIMEOUT_MS);
46
+ script.addEventListener("load", loaded, { once: true });
47
+ script.addEventListener("error", failed, { once: true });
48
+ if (existing) {
49
+ loaded();
50
+ } else {
51
+ script.src = adapter.scriptUrl;
52
+ script.async = true;
53
+ script.defer = true;
54
+ const nonce = document.querySelector("script[nonce]")?.nonce;
55
+ if (nonce) script.nonce = nonce;
56
+ document.head.appendChild(script);
57
+ }
58
+ });
59
+ const retryable = loading.catch((error) => {
60
+ loaders.delete(adapter.scriptUrl);
61
+ throw error;
62
+ });
63
+ loaders.set(adapter.scriptUrl, retryable);
64
+ return retryable;
65
+ }
66
+ export {
67
+ loadCaptcha
68
+ };
@@ -0,0 +1,71 @@
1
+ "use client";
2
+
3
+ // src/components/captcha-providers.ts
4
+ function preferredTheme() {
5
+ return typeof window !== "undefined" && window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
6
+ }
7
+ function teardown(api, widgetId) {
8
+ if (api.remove) {
9
+ try {
10
+ api.remove(widgetId);
11
+ return;
12
+ } catch {
13
+ }
14
+ }
15
+ try {
16
+ api.reset?.(widgetId);
17
+ } catch {
18
+ }
19
+ }
20
+ var CAPTCHA_ADAPTERS = {
21
+ turnstile: {
22
+ scriptUrl: "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",
23
+ globalName: "turnstile",
24
+ label: "Cloudflare Turnstile",
25
+ invisibleRenderOptions: ({ siteKey, theme, action, language }) => ({
26
+ sitekey: siteKey,
27
+ theme,
28
+ action,
29
+ language,
30
+ execution: "execute",
31
+ appearance: "interaction-only",
32
+ size: "flexible"
33
+ }),
34
+ teardown
35
+ },
36
+ hcaptcha: {
37
+ scriptUrl: "https://js.hcaptcha.com/1/api.js?render=explicit",
38
+ globalName: "hcaptcha",
39
+ label: "hCaptcha",
40
+ invisibleRenderOptions: ({ siteKey, theme }) => ({
41
+ sitekey: siteKey,
42
+ // 'auto' is Turnstile vocabulary. hCaptcha and reCAPTCHA take light|dark
43
+ // only and silently fall back to light on anything else, which reads badly
44
+ // in a dark application.
45
+ theme: theme === "auto" ? preferredTheme() : theme,
46
+ size: "invisible"
47
+ }),
48
+ teardown
49
+ },
50
+ "recaptcha-v2": {
51
+ scriptUrl: "https://www.google.com/recaptcha/api.js?render=explicit",
52
+ globalName: "grecaptcha",
53
+ label: "reCAPTCHA",
54
+ invisibleRenderOptions: ({ siteKey, theme }) => ({
55
+ sitekey: siteKey,
56
+ // 'auto' is Turnstile vocabulary. hCaptcha and reCAPTCHA take light|dark
57
+ // only and silently fall back to light on anything else, which reads badly
58
+ // in a dark application.
59
+ theme: theme === "auto" ? preferredTheme() : theme,
60
+ size: "invisible"
61
+ }),
62
+ teardown
63
+ }
64
+ };
65
+ function captchaAdapterFor(provider) {
66
+ return CAPTCHA_ADAPTERS[provider] ?? null;
67
+ }
68
+ export {
69
+ CAPTCHA_ADAPTERS,
70
+ captchaAdapterFor
71
+ };