@prosopo/client-bundle-example 2.11.0 → 2.11.2

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @prosopo/client-bundle-example@2.11.0 build:cjs
2
+ > @prosopo/client-bundle-example@2.11.2 build:cjs
3
3
  > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
4
 
5
5
  ViteCommonJSConfig: .
@@ -16,6 +16,7 @@ ViteCommonJSConfig: .
16
16
  {
17
17
  externals: [
18
18
  '@prosopo/dotenv',
19
+ The plugin "vite-tsconfig-paths" is detected. Vite now supports tsconfig paths resolution natively via the resolve.tsconfigPaths option. You can remove the plugin and set resolve.tsconfigPaths: true in your Vite config instead.
19
20
  '@prosopo/logger',
20
21
  '@prosopo/util',
21
22
  '@prosopo/locale',
@@ -23,7 +24,6 @@ ViteCommonJSConfig: .
23
24
  '@prosopo/util-crypto'
24
25
  ]
25
26
  }
26
- The plugin "vite-tsconfig-paths" is detected. Vite now supports tsconfig paths resolution natively via the resolve.tsconfigPaths option. You can remove the plugin and set resolve.tsconfigPaths: true in your Vite config instead.
27
27
  vite v8.1.5 building ssr environment for production...
28
28
  Bundle build started
29
29
  
@@ -33,4 +33,4 @@ computing gzip size...
33
33
  dist/cjs/index.cjs 0.71 kB │ gzip: 0.27 kB
34
34
  dist/cjs/plugins/pages.cjs 4.63 kB │ gzip: 1.20 kB
35
35
 
36
- ✓ built in 55ms
36
+ ✓ built in 60ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/client-bundle-example@2.11.0 build:tsc
2
+ > @prosopo/client-bundle-example@2.11.2 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 2:42:19 PM - Projects in this build:
5
+ 9:56:19 AM - Projects in this build:
6
6
  * ../../dev/config/tsconfig.json
7
7
  * ../../packages/util/tsconfig.json
8
8
  * ../../packages/logger/tsconfig.json
@@ -12,21 +12,21 @@
12
12
  * ../../packages/types/tsconfig.json
13
13
  * tsconfig.json
14
14
 
15
- 2:42:19 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
15
+ 9:56:19 AM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/vite/vite.frontend.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
16
16
 
17
- 2:42:19 PM - Project '../../packages/util/tsconfig.json' is up to date because newest input '../../packages/util/src/url.ts' is older than output '../../packages/util/tsconfig.tsbuildinfo'
17
+ 9:56:19 AM - Project '../../packages/util/tsconfig.json' is up to date because newest input '../../packages/util/src/verifyRecency.ts' is older than output '../../packages/util/tsconfig.tsbuildinfo'
18
18
 
19
- 2:42:19 PM - Project '../../packages/logger/tsconfig.json' is up to date because newest input '../../packages/logger/src/index.ts' is older than output '../../packages/logger/tsconfig.tsbuildinfo'
19
+ 9:56:19 AM - Project '../../packages/logger/tsconfig.json' is up to date because newest input '../../packages/logger/src/tests/loggerBrowser.unit.test.ts' is older than output '../../packages/logger/tsconfig.tsbuildinfo'
20
20
 
21
- 2:42:19 PM - Project '../../packages/dotenv/tsconfig.json' is up to date because newest input '../../packages/dotenv/src/tests/env.unit.test.ts' is older than output '../../packages/dotenv/tsconfig.tsbuildinfo'
21
+ 9:56:19 AM - Project '../../packages/dotenv/tsconfig.json' is up to date because newest input '../../packages/dotenv/src/index.ts' is older than output '../../packages/dotenv/tsconfig.tsbuildinfo'
22
22
 
23
- 2:42:19 PM - Project '../../packages/locale/tsconfig.json' is up to date because newest input '../../packages/locale/src/translationKey.ts' is older than output '../../packages/locale/tsconfig.tsbuildinfo'
23
+ 9:56:19 AM - Project '../../packages/locale/tsconfig.json' is up to date because newest input '../../packages/locale/src/util.ts' is older than output '../../packages/locale/tsconfig.tsbuildinfo'
24
24
 
25
- 2:42:19 PM - Project '../../packages/util-crypto/tsconfig.json' is up to date because newest input '../../packages/util-crypto/src/types.ts' is older than output '../../packages/util-crypto/tsconfig.tsbuildinfo'
25
+ 9:56:19 AM - Project '../../packages/util-crypto/tsconfig.json' is up to date because newest input '../../packages/util-crypto/src/types.ts' is older than output '../../packages/util-crypto/tsconfig.tsbuildinfo'
26
26
 
27
- 2:42:19 PM - Project '../../packages/types/tsconfig.json' is up to date because newest input '../../packages/types/src/types.test-d.ts' is older than output '../../packages/types/tsconfig.tsbuildinfo'
27
+ 9:56:20 AM - Project '../../packages/types/tsconfig.json' is up to date because newest input '../../packages/types/src/types.test-d.ts' is older than output '../../packages/types/tsconfig.tsbuildinfo'
28
28
 
29
- 2:42:19 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
29
+ 9:56:20 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
30
30
 
31
- 2:42:19 PM - Building project '/home/runner/work/captcha/captcha/demos/client-bundle-example/tsconfig.json'...
31
+ 9:56:20 AM - Building project '/home/runner/work/captcha/captcha/demos/client-bundle-example/tsconfig.json'...
32
32
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/client-bundle-example@2.11.0 build
2
+ > @prosopo/client-bundle-example@2.11.2 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/client-bundle-example@2.11.0 build:cross-env
6
+ > @prosopo/client-bundle-example@2.11.2 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -38,4 +38,4 @@ dist/_virtual/_rolldown/runtime.js 0.04 kB │ gzip: 0.06 kB
38
38
  dist/index.js 0.42 kB │ gzip: 0.19 kB
39
39
  dist/plugins/pages.js 4.04 kB │ gzip: 1.15 kB
40
40
 
41
- ✓ built in 151ms
41
+ ✓ built in 59ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @prosopo/client-bundle-example
2
2
 
3
+ ## 2.11.2
4
+ ### Patch Changes
5
+
6
+ - d4c1fa2: Make the explicit-render demo pages show a widget again. The status-log helper was defined in a module script that Vite placed after the page's own entry module, so `window.updateCaptchaStatus(...)` threw at the top of frictionless-, image-, puzzle- and puzzle-bind-explicit before `render()` was called. The helper is now defined by a classic script at the start of `<head>`, and only the callback wrapping stays in a module after the entry.
7
+
8
+ ## 2.11.1
9
+ ### Patch Changes
10
+
11
+ - 495c016: The public demo at demo.prosopo.io now reports page views to Plausible under the `demo.prosopo.io` site, so we can see who uses it. The tracker only loads on that exact host, so local runs, staging and the Cypress suite send nothing.
12
+
3
13
  ## 2.11.0
4
14
  ### Minor Changes
5
15
 
package/package.json CHANGED
@@ -25,13 +25,13 @@
25
25
  "start": "NODE_ENV=${NODE_ENV:-development}; vite serve ./src --port 9232 --config vite.config.ts --mode=$NODE_ENV",
26
26
  "test": "NODE_ENV=${NODE_ENV:-test}; npx vitest run --config ./vite.test.config.ts"
27
27
  },
28
- "version": "2.11.0",
28
+ "version": "2.11.2",
29
29
  "devDependencies": {
30
30
  "@emotion/cache": "11.14.0",
31
- "@prosopo/config": "3.3.17",
32
- "@prosopo/dotenv": "3.0.56",
33
- "@prosopo/locale": "3.4.3",
34
- "@prosopo/types": "5.9.0",
31
+ "@prosopo/config": "3.4.2",
32
+ "@prosopo/dotenv": "3.0.58",
33
+ "@prosopo/locale": "3.6.1",
34
+ "@prosopo/types": "5.11.0",
35
35
  "@types/node": "22.5.5",
36
36
  "@vitest/coverage-v8": "4.1.11",
37
37
  "concurrently": "9.0.1",
@@ -40,13 +40,31 @@ const DOCS_URL = "https://docs.prosopo.io/";
40
40
  const ariaCurrent = (isCurrent: boolean): string =>
41
41
  isCurrent ? ' aria-current="page"' : "";
42
42
 
43
+ export const DEMO_HOSTNAME = "demo.prosopo.io";
44
+ const PLAUSIBLE_SCRIPT_URL =
45
+ "https://prosopo.io/js/script.kairee5buy1chae8eit0so8ahphae9Oo.js";
46
+
47
+ // Only the public demo reports to Plausible: local runs and the Cypress suite
48
+ // serve these same pages.
49
+ const analyticsTag = `
50
+ <script>
51
+ (function () {
52
+ if (location.hostname !== "${DEMO_HOSTNAME}") return;
53
+ var tracker = document.createElement("script");
54
+ tracker.defer = true;
55
+ tracker.setAttribute("data-domain", "${DEMO_HOSTNAME}");
56
+ tracker.src = "${PLAUSIBLE_SCRIPT_URL}";
57
+ document.head.appendChild(tracker);
58
+ })();
59
+ </script>`;
60
+
43
61
  const headTags = (prefix: string): string => `
44
62
  <meta charset="utf-8">
45
63
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
46
64
  <link rel="preconnect" href="https://fonts.googleapis.com">
47
65
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
48
66
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700;800&display=swap">
49
- <link rel="stylesheet" href="${prefix}styles/demo.css">`;
67
+ <link rel="stylesheet" href="${prefix}styles/demo.css">${analyticsTag}`;
50
68
 
51
69
  const header = `
52
70
  <header class="demo-header">
@@ -17,9 +17,18 @@
17
17
  import type { IndexHtmlTransformContext, Plugin } from "vite";
18
18
  import { fillSlot } from "./slots.js";
19
19
 
20
+ // Opening tag of <head>, attributes and all.
21
+ const HEAD_OPEN = /<head(\s[^>]*)?>/i;
22
+
20
23
  export default function statusLogInjector(): Plugin {
21
- const statusLogJs = `
22
- <script type="module">
24
+ // A classic script at the very start of <head>, so it runs during parsing,
25
+ // before any module script. By the time this plugin runs ("post"), Vite has
26
+ // already hoisted the page's own entry module into <head>, and module
27
+ // scripts run in document order: defined in the module below, the helper
28
+ // did not exist yet when the explicit-render pages called it at top level,
29
+ // so they threw before render() and never showed a widget.
30
+ const updateStatusJs = `
31
+ <script>
23
32
  function updateCaptchaStatus(message, type = 'info') {
24
33
  const statusContainer = document.getElementById('captcha-status');
25
34
  if (!statusContainer) return;
@@ -43,6 +52,14 @@ export default function statusLogInjector(): Plugin {
43
52
  }
44
53
 
45
54
  window.updateCaptchaStatus = updateCaptchaStatus;
55
+ </script>
56
+ `;
57
+
58
+ // A module, after the page's entry module, because it wraps the callbacks
59
+ // that module defines.
60
+ const statusLogJs = `
61
+ <script type="module">
62
+ const updateCaptchaStatus = window.updateCaptchaStatus;
46
63
 
47
64
  // Wrap the page's own callbacks so every page logs the same events.
48
65
  const originalOnCaptchaFailed = window.onCaptchaFailed;
@@ -104,7 +121,13 @@ export default function statusLogInjector(): Plugin {
104
121
  return html;
105
122
  }
106
123
 
107
- const withJs = html.replace("</head>", () => `${statusLogJs}</head>`);
124
+ const withHelper = HEAD_OPEN.test(html)
125
+ ? html.replace(HEAD_OPEN, (open) => `${open}${updateStatusJs}`)
126
+ : html;
127
+ const withJs = withHelper.replace(
128
+ "</head>",
129
+ () => `${statusLogJs}</head>`,
130
+ );
108
131
 
109
132
  const inSlot = fillSlot(withJs, "events", statusLogHtml);
110
133
  if (inSlot) {
@@ -24,7 +24,7 @@ import codeSnippetInjector, {
24
24
  snippetFor,
25
25
  } from "../plugins/code-snippet-injector.js";
26
26
  import formFillerInjector from "../plugins/form-filler-injector.js";
27
- import layoutInjector from "../plugins/layout-injector.js";
27
+ import layoutInjector, { DEMO_HOSTNAME } from "../plugins/layout-injector.js";
28
28
  import {
29
29
  type DemoRendering,
30
30
  demoPages,
@@ -161,6 +161,47 @@ describe("layoutInjector", () => {
161
161
  );
162
162
  });
163
163
 
164
+ it("loads Plausible only when served from the public demo host", () => {
165
+ const html = handler(page(), ctx(`${cwd}/src/pow-explicit.html`));
166
+ const inlineScript = html.match(
167
+ /<script>\s*\(function \(\) \{([\s\S]*?)\}\)\(\);\s*<\/script>/,
168
+ )?.[1];
169
+ expect(inlineScript).toBeDefined();
170
+
171
+ type FakeElement = Record<string, unknown>;
172
+ const loadedFrom = (hostname: string): FakeElement[] => {
173
+ const appended: FakeElement[] = [];
174
+ const fakeDocument = {
175
+ createElement: (): FakeElement => {
176
+ const element: FakeElement = {};
177
+ element.setAttribute = (name: string, value: string): void => {
178
+ element[name] = value;
179
+ };
180
+ return element;
181
+ },
182
+ head: {
183
+ appendChild: (element: FakeElement): void => {
184
+ appended.push(element);
185
+ },
186
+ },
187
+ };
188
+ new Function("location", "document", inlineScript ?? "")(
189
+ { hostname },
190
+ fakeDocument,
191
+ );
192
+ return appended;
193
+ };
194
+
195
+ expect(loadedFrom("localhost")).toHaveLength(0);
196
+ expect(loadedFrom(`staging.${DEMO_HOSTNAME}`)).toHaveLength(0);
197
+ const [tracker] = loadedFrom(DEMO_HOSTNAME);
198
+ expect(tracker).toMatchObject({
199
+ defer: true,
200
+ "data-domain": DEMO_HOSTNAME,
201
+ src: "https://prosopo.io/js/script.kairee5buy1chae8eit0so8ahphae9Oo.js",
202
+ });
203
+ });
204
+
164
205
  it("marks the current type, mode and rendering", () => {
165
206
  const html = handler(page(), ctx(`${cwd}/src/pow-explicit.html`));
166
207
 
@@ -290,6 +331,32 @@ describe("statusLogInjector", () => {
290
331
  expect(html).toContain("updateCaptchaStatus");
291
332
  });
292
333
 
334
+ it("defines updateCaptchaStatus before the page's entry module runs", () => {
335
+ // What Vite hands a "post" transform: the entry module already hoisted
336
+ // into <head>. Module scripts run in document order, so the helper must
337
+ // be defined by a classic script ahead of it.
338
+ const built =
339
+ '<html><head><title>demo</title><script type="module" crossorigin src="/assets/entry.js"></script></head><body><p>hi</p></body></html>';
340
+ const html = handler(built, ctx("src/frictionless-explicit.html"));
341
+
342
+ const definedAt = html.indexOf("window.updateCaptchaStatus = ");
343
+ const entryAt = html.indexOf('src="/assets/entry.js"');
344
+ expect(definedAt).toBeGreaterThan(-1);
345
+ expect(definedAt).toBeLessThan(entryAt);
346
+ const definingScript = html.lastIndexOf("<script", definedAt);
347
+ expect(html.slice(definingScript, definedAt)).not.toContain("module");
348
+ });
349
+
350
+ it("wraps the page callbacks after the page's entry module", () => {
351
+ const built =
352
+ '<html><head><script type="module" src="/assets/entry.js"></script></head><body></body></html>';
353
+ const html = handler(built, ctx("src/image-explicit.html"));
354
+
355
+ expect(html.indexOf("window.onCaptchaFailed = ")).toBeGreaterThan(
356
+ html.indexOf('src="/assets/entry.js"'),
357
+ );
358
+ });
359
+
293
360
  it("fills the events slot when the layout provides one", () => {
294
361
  const html = handler(
295
362
  page(`<form></form>${slotMarkup("events")}`),