@prosopo/client-bundle-example 2.10.12 → 2.10.19

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.
Files changed (45) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +13 -11
  2. package/.turbo/turbo-build$colon$tsc.log +14 -14
  3. package/.turbo/turbo-build.log +15 -12
  4. package/CHANGELOG.md +51 -0
  5. package/README.md +3 -1
  6. package/dist/_virtual/_rolldown/runtime.js +3 -0
  7. package/dist/cjs/index.cjs +39 -48
  8. package/dist/index.js +41 -47
  9. package/env.development +7 -0
  10. package/env.production +6 -0
  11. package/env.staging +2 -2
  12. package/package.json +13 -8
  13. package/src/assets/dummy.txt +0 -0
  14. package/src/frictionless-explicit-web3.html +249 -0
  15. package/src/frictionless-explicit.html +208 -0
  16. package/src/frictionless-implicit.html +218 -0
  17. package/src/image-explicit-web3.html +198 -0
  18. package/src/image-explicit.html +158 -0
  19. package/src/index.html +207 -0
  20. package/src/index.ts +76 -0
  21. package/src/invisible-frictionless-explicit.html +132 -0
  22. package/src/invisible-frictionless-implicit.html +204 -0
  23. package/src/invisible-image-explicit.html +161 -0
  24. package/src/invisible-image-implicit.html +203 -0
  25. package/src/invisible-pow-explicit.html +161 -0
  26. package/src/invisible-pow-implicit.html +106 -0
  27. package/src/invisible-puzzle-explicit.html +160 -0
  28. package/src/invisible-puzzle-implicit.html +106 -0
  29. package/src/plugins/explanation-injector.ts +294 -0
  30. package/src/plugins/form-filler-injector.ts +233 -0
  31. package/src/plugins/navigation-injector.ts +602 -0
  32. package/src/plugins/status-log-injector.ts +199 -0
  33. package/src/pow-explicit-web3.html +195 -0
  34. package/src/pow-explicit.html +158 -0
  35. package/src/pow-implicit.html +207 -0
  36. package/src/puzzle-explicit.html +158 -0
  37. package/src/puzzle-implicit.html +207 -0
  38. package/src/styles/captcha.css +78 -0
  39. package/src/styles/field.css +12 -0
  40. package/tsconfig.cjs.json +25 -0
  41. package/tsconfig.json +31 -0
  42. package/tsconfig.tsbuildinfo +1 -0
  43. package/tsconfig.types.json +9 -0
  44. package/vite.cjs.config.ts +1 -1
  45. package/vite.config.ts +37 -4
@@ -0,0 +1,249 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <link href="https://cdn.muicss.com/mui-0.10.3/css/mui.min.css" rel="stylesheet" type="text/css"/>
5
+ <link href="styles/field.css" rel="stylesheet" type="text/css"/>
6
+
7
+ <script src="index.js"></script>
8
+ <title>Procaptcha demo: Frictionless CAPTCHA</title>
9
+ <link href="styles/captcha.css" rel="stylesheet" type="text/css"/>
10
+ <script type="module">
11
+ window.onCaptchaFailed = function () {
12
+ console.log('Challenge failed');
13
+ updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
14
+ }
15
+
16
+ window.addEventListener('load', () => {
17
+ updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
18
+
19
+ window.setIsError = (isError) => {
20
+ const messageContainer = document.getElementById('messageContainer');
21
+ messageContainer.style.color = isError ? 'red' : 'black';
22
+ messageContainer.style.display = 'block';
23
+ };
24
+
25
+ window.setMessage = (message) => {
26
+ document.getElementById('message').innerText = message;
27
+ };
28
+
29
+ window.onLoggedIn = (token) => {
30
+ updateCaptchaStatus('Logged in, fetching private resource', 'info');
31
+ const url = new URL("/private", config.serverUrl).href;
32
+ console.log("getting private resource with token ", token, "at", url);
33
+ fetch(url, {
34
+ method: "GET",
35
+ headers: {
36
+ Origin: "http://localhost:9232", // TODO: change this to env var
37
+ "Content-Type": "application/json",
38
+ Authorization: `Bearer ${token}`,
39
+ },
40
+ })
41
+ .then(async (res) => {
42
+ try {
43
+ const jsonRes = await res.json();
44
+ if (res.status === 200) {
45
+ updateCaptchaStatus(`Successfully accessed private resource`, 'success');
46
+ setMessage(jsonRes.message);
47
+ } else {
48
+ updateCaptchaStatus(`Failed to access private resource: ${res.status}`, 'error');
49
+ }
50
+ } catch (err) {
51
+ console.log(err);
52
+ updateCaptchaStatus(`Error parsing response: ${err}`, 'error');
53
+ }
54
+ })
55
+ .catch((err) => {
56
+ console.log(err);
57
+ updateCaptchaStatus(`Fetch error: ${err}`, 'error');
58
+ });
59
+ };
60
+
61
+ window.onActionHandler = () => {
62
+ updateCaptchaStatus('Form submission initiated', 'info');
63
+
64
+ const procaptchaElements = document.getElementsByName('procaptcha-response');
65
+
66
+ if (!procaptchaElements.length) {
67
+ updateCaptchaStatus('Error: No CAPTCHA response found', 'error');
68
+ alert("Must complete captcha");
69
+ return
70
+ }
71
+
72
+ const procaptchaToken = procaptchaElements[0].value;
73
+ updateCaptchaStatus(`Token received: ${procaptchaToken.substring(0, 15)}...`, 'success');
74
+
75
+ const payload = {
76
+ email: document.getElementById('email').value,
77
+ name: document.getElementById('name').value,
78
+ password: document.getElementById('password').value,
79
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_FRICTIONLESS,
80
+ "procaptcha-response": procaptchaToken,
81
+ };
82
+ const url = new URL('signup', import.meta.env.PROSOPO_SERVER_URL).href;
83
+ console.log("posting to", url, "with payload", payload);
84
+ fetch(url, {
85
+ method: "POST",
86
+ headers: {
87
+ "Content-Type": "application/json",
88
+ },
89
+ body: JSON.stringify(payload),
90
+ contentType: "application/json",
91
+ }).then((response) => {
92
+ return new Promise((resolve) => response.json()
93
+ .then((json) => resolve({
94
+ status: response.status,
95
+ ok: response.ok,
96
+ json,
97
+ })))
98
+ })
99
+ .then(async ({status, json, ok}) => {
100
+ console.log("status", status, "json", json.message, "ok", ok);
101
+ console.log("json", json)
102
+ try {
103
+ if (status !== 200) {
104
+ updateCaptchaStatus(`API Error: ${json.message}`, 'error');
105
+ setIsError(true);
106
+ setMessage(json.message);
107
+ } else {
108
+ updateCaptchaStatus(`API Success: ${json.message}`, 'success');
109
+ setIsError(false);
110
+ setMessage(json.message);
111
+ }
112
+ } catch (err) {
113
+ console.log(err);
114
+ updateCaptchaStatus(`Error processing response: ${err}`, 'error');
115
+ }
116
+ })
117
+ .catch((err) => {
118
+ console.log(err);
119
+ updateCaptchaStatus(`Fetch error: ${err}`, 'error');
120
+ setIsError(true);
121
+ setMessage("Error: " + err);
122
+ });
123
+ };
124
+
125
+ // Monitor DOM for CAPTCHA initialization
126
+ const observer = new MutationObserver((mutations) => {
127
+ mutations.forEach((mutation) => {
128
+ if (mutation.addedNodes.length) {
129
+ for (let i = 0; i < mutation.addedNodes.length; i++) {
130
+ const node = mutation.addedNodes[i];
131
+ if (node.classList && (node.classList.contains('procaptcha') ||
132
+ node.querySelector && node.querySelector('.procaptcha'))) {
133
+ updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
134
+ observer.disconnect();
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ });
140
+ });
141
+
142
+ observer.observe(document.body, { childList: true, subtree: true });
143
+ });
144
+
145
+ // updateCaptchaStatus is now provided by status-log-injector
146
+ </script>
147
+ </head>
148
+ <body>
149
+
150
+ <div class="mui-container">
151
+ <h1>Frictionless CAPTCHA - Explicit Rendering</h1>
152
+ <p>This example demonstrates how to use Procaptcha in frictionless mode with explicit rendering.</p>
153
+
154
+ <!-- CAPTCHA Status Display will be injected by the status-log-injector plugin -->
155
+
156
+ <form action="%PROSOPO_SERVER_URL%/signup" method="POST" class="mui-form" id="demo-form">
157
+ <h2>Example Login Form</h2>
158
+ <div class="mui-textfield mui-textfield--float-label">
159
+ <label for="name">Name</label>
160
+ <input id="name" type="text" name="name" required/>
161
+ </div>
162
+ <div class="mui-textfield mui-textfield--float-label">
163
+ <label for="email">Email Address</label>
164
+ <input id="email" type="email" name="email" required/>
165
+ </div>
166
+ <div class="mui-textfield mui-textfield--float-label">
167
+ <label for="password">Password</label>
168
+ <input id="password" type="password" name="password" required/>
169
+ </div>
170
+ <div class="mui-textfield mui-textfield--float-label">
171
+ <div id="procaptcha-container"></div>
172
+ </div>
173
+ <div id="messageContainer" style="display: none; color: black;"><span id="message"></span></div>
174
+ <button type="button" class="mui-btn mui-btn--raised" onclick="onActionHandler()" data-cy="submit-button">Submit</button>
175
+ </form>
176
+
177
+ <!-- Console output display area -->
178
+ <div id="console-output" class="console-output" style="display: none;"></div>
179
+
180
+ <!-- Explanation will be injected by the explanation-injector plugin -->
181
+ </div>
182
+ <script type="module">
183
+ // Pattern to avoid race condition between Procaptcha script loading and Procaptcha render function call
184
+ import { render } from "%VITE_BUNDLE_URL%"
185
+ import { web3AccountsSubscribe, web3Enable } from "@polkadot/extension-dapp";
186
+
187
+ // Define a callback function to be called when the CAPTCHA is verified
188
+ function onCaptchaVerified(output) {
189
+ console.log('Challenge passed');
190
+ window.updateCaptchaStatus('Challenge passed successfully!', 'success');
191
+ window.updateCaptchaStatus(`Token generated: ${output.substring(0, 15)}...`, 'success');
192
+ }
193
+
194
+ // Get web3 accounts and create select element
195
+ async function getWeb3Accounts() {
196
+ return new Promise((resolve) =>{ web3AccountsSubscribe((accounts) => {
197
+ if (accounts.length === 0) {
198
+ window.updateCaptchaStatus('No accounts found. Please connect your wallet.', 'error');
199
+ } else {
200
+ window.updateCaptchaStatus(`Connected to ${accounts.length} account(s).`, 'info');
201
+ // render a modal and get the user to select 1 account
202
+ const accountSelect = document.createElement('select');
203
+ accountSelect.id = 'account-select';
204
+ accountSelect.classList.add('mui-select');
205
+ accountSelect.classList.add('account-select');
206
+ accounts.forEach((account) => {
207
+ const option = document.createElement('option');
208
+ option.value = account.address;
209
+ option.textContent = `${account.meta.name} (${account.address})`;
210
+ accountSelect.appendChild(option);
211
+ });
212
+ const accountContainer = document.createElement('div');
213
+ accountContainer.innerHTML = '<label for="account-select"><h2>Select Account:</h2></label>';
214
+ accountContainer.appendChild(accountSelect);
215
+ document.getElementById('demo-form').prepend(accountContainer);
216
+ window.updateCaptchaStatus('Accounts loaded successfully', 'info');
217
+ }
218
+ resolve(accounts);
219
+ })});
220
+ }
221
+ // Wait for DOM content to be loaded
222
+ document.addEventListener('DOMContentLoaded', async function() {
223
+ await web3Enable("Procaptcha Demo");
224
+
225
+ await getWeb3Accounts();
226
+
227
+ // Get the selected account from the dropdown
228
+ const account = document.getElementById('account-select') ? document.getElementById('account-select').value : null;
229
+ window.updateCaptchaStatus(`Rendering the CAPTCHA for account ${account}`, 'info');
230
+
231
+ // Get the Element using elementId
232
+ const captchaContainer = document.getElementById('procaptcha-container');
233
+
234
+ // Render the CAPTCHA
235
+ const widgetId = render(captchaContainer, {
236
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_FRICTIONLESS,
237
+ callback: onCaptchaVerified,
238
+ "failed-callback": function () {
239
+ console.log('Challenge failed');
240
+ window.updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
241
+ },
242
+ web3: true,
243
+ userAccountAddress: account
244
+ });
245
+ })
246
+
247
+ </script>
248
+ </body>
249
+ </html>
@@ -0,0 +1,208 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <link href="https://cdn.muicss.com/mui-0.10.3/css/mui.min.css" rel="stylesheet" type="text/css"/>
5
+ <link href="styles/field.css" rel="stylesheet" type="text/css"/>
6
+
7
+ <script src="index.js"></script>
8
+ <title>Procaptcha demo: Frictionless CAPTCHA</title>
9
+ <link href="styles/captcha.css" rel="stylesheet" type="text/css"/>
10
+ <script type="module">
11
+ window.onCaptchaFailed = function () {
12
+ console.log('Challenge failed');
13
+ updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
14
+ }
15
+
16
+ window.addEventListener('load', () => {
17
+ updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
18
+
19
+ window.setIsError = (isError) => {
20
+ const messageContainer = document.getElementById('messageContainer');
21
+ messageContainer.style.color = isError ? 'red' : 'black';
22
+ messageContainer.style.display = 'block';
23
+ };
24
+
25
+ window.setMessage = (message) => {
26
+ document.getElementById('message').innerText = message;
27
+ };
28
+
29
+ window.onLoggedIn = (token) => {
30
+ updateCaptchaStatus('Logged in, fetching private resource', 'info');
31
+ const url = new URL("/private", config.serverUrl).href;
32
+ console.log("getting private resource with token ", token, "at", url);
33
+ fetch(url, {
34
+ method: "GET",
35
+ headers: {
36
+ Origin: "http://localhost:9232", // TODO: change this to env var
37
+ "Content-Type": "application/json",
38
+ Authorization: `Bearer ${token}`,
39
+ },
40
+ })
41
+ .then(async (res) => {
42
+ try {
43
+ const jsonRes = await res.json();
44
+ if (res.status === 200) {
45
+ updateCaptchaStatus(`Successfully accessed private resource`, 'success');
46
+ setMessage(jsonRes.message);
47
+ } else {
48
+ updateCaptchaStatus(`Failed to access private resource: ${res.status}`, 'error');
49
+ }
50
+ } catch (err) {
51
+ console.log(err);
52
+ updateCaptchaStatus(`Error parsing response: ${err}`, 'error');
53
+ }
54
+ })
55
+ .catch((err) => {
56
+ console.log(err);
57
+ updateCaptchaStatus(`Fetch error: ${err}`, 'error');
58
+ });
59
+ };
60
+
61
+ window.onActionHandler = () => {
62
+ updateCaptchaStatus('Form submission initiated', 'info');
63
+
64
+ const procaptchaElements = document.getElementsByName('procaptcha-response');
65
+
66
+ if (!procaptchaElements.length) {
67
+ updateCaptchaStatus('Error: No CAPTCHA response found', 'error');
68
+ alert("Must complete captcha");
69
+ return
70
+ }
71
+
72
+ const procaptchaToken = procaptchaElements[0].value;
73
+ updateCaptchaStatus(`Token received: ${procaptchaToken.substring(0, 15)}...`, 'success');
74
+
75
+ const payload = {
76
+ email: document.getElementById('email').value,
77
+ name: document.getElementById('name').value,
78
+ password: document.getElementById('password').value,
79
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_FRICTIONLESS,
80
+ "procaptcha-response": procaptchaToken,
81
+ };
82
+ const url = new URL('signup', import.meta.env.PROSOPO_SERVER_URL).href;
83
+ console.log("posting to", url, "with payload", payload);
84
+ fetch(url, {
85
+ method: "POST",
86
+ headers: {
87
+ "Content-Type": "application/json",
88
+ },
89
+ body: JSON.stringify(payload),
90
+ contentType: "application/json",
91
+ }).then((response) => {
92
+ return new Promise((resolve) => response.json()
93
+ .then((json) => resolve({
94
+ status: response.status,
95
+ ok: response.ok,
96
+ json,
97
+ })))
98
+ })
99
+ .then(async ({status, json, ok}) => {
100
+ console.log("status", status, "json", json.message, "ok", ok);
101
+ console.log("json", json)
102
+ try {
103
+ if (status !== 200) {
104
+ updateCaptchaStatus(`API Error: ${json.message}`, 'error');
105
+ setIsError(true);
106
+ setMessage(json.message);
107
+ } else {
108
+ updateCaptchaStatus(`API Success: ${json.message}`, 'success');
109
+ setIsError(false);
110
+ setMessage(json.message);
111
+ }
112
+ } catch (err) {
113
+ console.log(err);
114
+ updateCaptchaStatus(`Error processing response: ${err}`, 'error');
115
+ }
116
+ })
117
+ .catch((err) => {
118
+ console.log(err);
119
+ updateCaptchaStatus(`Fetch error: ${err}`, 'error');
120
+ setIsError(true);
121
+ setMessage("Error: " + err);
122
+ });
123
+ };
124
+
125
+ // Monitor DOM for CAPTCHA initialization
126
+ const observer = new MutationObserver((mutations) => {
127
+ mutations.forEach((mutation) => {
128
+ if (mutation.addedNodes.length) {
129
+ for (let i = 0; i < mutation.addedNodes.length; i++) {
130
+ const node = mutation.addedNodes[i];
131
+ if (node.classList && (node.classList.contains('procaptcha') ||
132
+ node.querySelector && node.querySelector('.procaptcha'))) {
133
+ updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
134
+ observer.disconnect();
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ });
140
+ });
141
+
142
+ observer.observe(document.body, { childList: true, subtree: true });
143
+ });
144
+
145
+ // updateCaptchaStatus is now provided by status-log-injector
146
+ </script>
147
+ </head>
148
+ <body>
149
+
150
+ <div class="mui-container">
151
+ <h1>Frictionless CAPTCHA - Explicit Rendering</h1>
152
+ <p>This example demonstrates how to use Procaptcha in frictionless mode with explicit rendering.</p>
153
+
154
+ <!-- CAPTCHA Status Display will be injected by the status-log-injector plugin -->
155
+
156
+ <form action="%PROSOPO_SERVER_URL%/signup" method="POST" class="mui-form">
157
+ <h2>Example Login Form</h2>
158
+ <div class="mui-textfield mui-textfield--float-label">
159
+ <label for="name">Name</label>
160
+ <input id="name" type="text" name="name" required/>
161
+ </div>
162
+ <div class="mui-textfield mui-textfield--float-label">
163
+ <label for="email">Email Address</label>
164
+ <input id="email" type="email" name="email" required/>
165
+ </div>
166
+ <div class="mui-textfield mui-textfield--float-label">
167
+ <label for="password">Password</label>
168
+ <input id="password" type="password" name="password" required/>
169
+ </div>
170
+ <div class="mui-textfield mui-textfield--float-label">
171
+ <div id="procaptcha-container"></div>
172
+ </div>
173
+ <div id="messageContainer" style="display: none; color: black;"><span id="message"></span></div>
174
+ <button type="button" class="mui-btn mui-btn--raised" onclick="onActionHandler()" data-cy="submit-button">Submit</button>
175
+ </form>
176
+
177
+ <!-- Console output display area -->
178
+ <div id="console-output" class="console-output" style="display: none;"></div>
179
+
180
+ <!-- Explanation will be injected by the explanation-injector plugin -->
181
+ </div>
182
+ <script type="module">
183
+ // Pattern to avoid race condition between Procaptcha script loading and Procaptcha render function call
184
+ import { render } from "%VITE_BUNDLE_URL%"
185
+
186
+ // Define a callback function to be called when the CAPTCHA is verified
187
+ function onCaptchaVerified(output) {
188
+ console.log('Challenge passed');
189
+ window.updateCaptchaStatus('Challenge passed successfully!', 'success');
190
+ window.updateCaptchaStatus(`Token generated: ${output.substring(0, 15)}...`, 'success');
191
+ }
192
+
193
+ // Get the Element using elementId
194
+ const captchaContainer = document.getElementById('procaptcha-container');
195
+
196
+ // Render the CAPTCHA
197
+ const widgetId = render(captchaContainer, {
198
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_FRICTIONLESS,
199
+ callback: onCaptchaVerified,
200
+ "failed-callback": function () {
201
+ console.log('Challenge failed');
202
+ window.updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
203
+ }
204
+ });
205
+
206
+ </script>
207
+ </body>
208
+ </html>
@@ -0,0 +1,218 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <link href="https://cdn.muicss.com/mui-0.10.3/css/mui.min.css" rel="stylesheet" type="text/css"/>
5
+ <link href="styles/field.css" rel="stylesheet" type="text/css"/>
6
+
7
+ <script src="index.js"></script>
8
+ <link href="styles/captcha.css" rel="stylesheet" type="text/css"/>
9
+ <title>Procaptcha demo: Frictionless CAPTCHA</title>
10
+ <script type="text/javascript">
11
+ window.onCaptchaFailed = function () {
12
+ console.log('Challenge failed');
13
+ updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
14
+ }
15
+ </script>
16
+ <script type="module">
17
+ // Function to update CAPTCHA status display is now provided by status-log-injector
18
+
19
+ window.addEventListener('load', () => {
20
+ updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
21
+
22
+ window.setIsError = (isError) => {
23
+ const messageContainer = document.getElementById('messageContainer');
24
+ messageContainer.style.color = isError ? 'red' : 'black';
25
+ messageContainer.style.display = 'block';
26
+ };
27
+
28
+ window.setMessage = (message) => {
29
+ document.getElementById('message').innerText = message;
30
+ };
31
+
32
+ window.onLoggedIn = (token) => {
33
+ updateCaptchaStatus('Logged in, fetching private resource', 'info');
34
+ const url = new URL("/private", config.serverUrl).href;
35
+ const origin = new URL(window.location.href).origin;
36
+ console.log("getting private resource with token ", token, "at", url);
37
+ fetch(url, {
38
+ method: "GET",
39
+ headers: {
40
+ Origin: `${origin}`,
41
+ "Content-Type": "application/json",
42
+ Authorization: `Bearer ${token}`,
43
+ },
44
+ })
45
+ .then(async (res) => {
46
+ try {
47
+ const jsonRes = await res.json();
48
+ if (res.status === 200) {
49
+ updateCaptchaStatus(`Successfully accessed private resource`, 'success');
50
+ setMessage(jsonRes.message);
51
+ } else {
52
+ updateCaptchaStatus(`Failed to access private resource: ${res.status}`, 'error');
53
+ }
54
+ } catch (err) {
55
+ console.log(err);
56
+ updateCaptchaStatus(`Error parsing response: ${err}`, 'error');
57
+ }
58
+ })
59
+ .catch((err) => {
60
+ console.log(err);
61
+ updateCaptchaStatus(`Fetch error: ${err}`, 'error');
62
+ });
63
+ };
64
+
65
+ window.onActionHandler = () => {
66
+ updateCaptchaStatus('Form submission initiated', 'info');
67
+
68
+ const procaptchaElements = document.getElementsByName('procaptcha-response');
69
+
70
+ if (!procaptchaElements.length) {
71
+ updateCaptchaStatus('Error: No CAPTCHA response found', 'error');
72
+ alert("Must complete captcha");
73
+ return
74
+ }
75
+
76
+ const procaptchaToken = procaptchaElements[0].value;
77
+ updateCaptchaStatus(`Token received: ${procaptchaToken.substring(0, 15)}...`, 'success');
78
+
79
+ const payload = {
80
+ email: document.getElementById('email').value,
81
+ name: document.getElementById('name').value,
82
+ password: document.getElementById('password').value,
83
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_FRICTIONLESS,
84
+ "procaptcha-response": procaptchaToken,
85
+ };
86
+ const url = new URL('signup', import.meta.env.PROSOPO_SERVER_URL).href;
87
+ console.log("posting to", url, "with payload", payload);
88
+ fetch(url, {
89
+ method: "POST",
90
+ headers: {
91
+ "Content-Type": "application/json",
92
+ },
93
+ body: JSON.stringify(payload),
94
+ contentType: "application/json",
95
+ }).then((response) => {
96
+ return new Promise((resolve) => response.json()
97
+ .then((json) => resolve({
98
+ status: response.status,
99
+ ok: response.ok,
100
+ json,
101
+ })))
102
+ })
103
+ .then(async ({status, json, ok}) => {
104
+ console.log("status", status, "json", json.message, "ok", ok);
105
+ console.log("json", json)
106
+ try {
107
+ if (status !== 200) {
108
+ updateCaptchaStatus(`API Error: ${json.message}`, 'error');
109
+ setIsError(true);
110
+ setMessage(json.message);
111
+ } else {
112
+ updateCaptchaStatus(`API Success: ${json.message}`, 'success');
113
+ setIsError(false);
114
+ setMessage(json.message);
115
+ }
116
+ } catch (err) {
117
+ console.log(err);
118
+ updateCaptchaStatus(`Error processing response: ${err}`, 'error');
119
+ }
120
+ })
121
+ .catch((err) => {
122
+ console.log(err);
123
+ updateCaptchaStatus(`Fetch error: ${err}`, 'error');
124
+ setIsError(true);
125
+ setMessage("Error: " + err);
126
+ });
127
+ };
128
+
129
+ // Monitor DOM for CAPTCHA initialization
130
+ const observer = new MutationObserver((mutations) => {
131
+ mutations.forEach((mutation) => {
132
+ if (mutation.addedNodes.length) {
133
+ for (let i = 0; i < mutation.addedNodes.length; i++) {
134
+ const node = mutation.addedNodes[i];
135
+ if (node.classList && (node.classList.contains('procaptcha') ||
136
+ node.querySelector && node.querySelector('.procaptcha'))) {
137
+ updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
138
+ observer.disconnect();
139
+ break;
140
+ }
141
+ }
142
+ }
143
+ });
144
+ });
145
+
146
+ observer.observe(document.body, { childList: true, subtree: true });
147
+ });
148
+
149
+ // updateCaptchaStatus is now provided by status-log-injector
150
+ </script>
151
+ </head>
152
+ <body>
153
+
154
+ <div class="mui-container">
155
+ <h1>Frictionless CAPTCHA - Explicit Rendering</h1>
156
+ <p>This example demonstrates how to use Procaptcha in frictionless mode with explicit rendering.</p>
157
+
158
+ <!-- CAPTCHA Status Display will be injected by the status-log-injector plugin -->
159
+
160
+ <form action="%PROSOPO_SERVER_URL%/signup" method="POST" class="mui-form">
161
+ <h2>Example Login Form</h2>
162
+ <div class="mui-textfield mui-textfield--float-label">
163
+ <label for="name">Name</label>
164
+ <input id="name" type="text" name="name" required/>
165
+ </div>
166
+ <div class="mui-textfield mui-textfield--float-label">
167
+ <label for="email">Email Address</label>
168
+ <input id="email" type="email" name="email" required/>
169
+ </div>
170
+ <div class="mui-textfield mui-textfield--float-label">
171
+ <label for="password">Password</label>
172
+ <input id="password" type="password" name="password" required/>
173
+ </div>
174
+ <div class="mui-textfield mui-textfield--float-label">
175
+ <div id="procaptcha-container"></div>
176
+ </div>
177
+ <div id="messageContainer" style="display: none; color: black;"><span id="message"></span></div>
178
+ <button type="button" class="mui-btn mui-btn--raised" onclick="onActionHandler()" data-cy="submit-button">Submit</button>
179
+ </form>
180
+
181
+ </div>
182
+ <script type="module">
183
+ (async () => {
184
+
185
+ const host = new URL(window.location.href).host;
186
+
187
+ // Use predefined VITE_BUNDLE_URL or default to localhost with the same host as the page, to ensure we load the
188
+ // bundle from the correct location in different environments
189
+ const bundleUrl = import.meta.env.VITE_BUNDLE_URL || 'https://localhost:9269/procaptcha.bundle.js'.replace('localhost', host);
190
+
191
+ // Pattern to avoid race condition between Procaptcha script loading and Procaptcha render function call
192
+ const {render} = await import(bundleUrl);
193
+
194
+ // Define a callback function to be called when the CAPTCHA is verified
195
+ function onCaptchaVerified(output) {
196
+ console.log('Challenge passed');
197
+ window.updateCaptchaStatus('Challenge passed successfully!', 'success');
198
+ window.updateCaptchaStatus(`Token generated: ${output.substring(0, 15)}...`, 'success');
199
+ }
200
+
201
+ const captchaContainer = document.getElementById('procaptcha-container');
202
+
203
+ // Render the CAPTCHA
204
+ const widgetId = render(captchaContainer, {
205
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_FRICTIONLESS,
206
+ callback: onCaptchaVerified,
207
+ "failed-callback": function () {
208
+ console.log('Challenge failed');
209
+ window.updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
210
+ }
211
+ });
212
+
213
+ window.updateCaptchaStatus('CAPTCHA render function called with widget ID: ' + widgetId, 'info');
214
+ })();
215
+ </script>
216
+
217
+ </body>
218
+ </html>