@rozie-ui/captcha-react 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Dan Krieger and Rozie.js contributors
3
+ Copyright (c) 2026 One Learning Community LTD
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.cjs CHANGED
@@ -131,7 +131,6 @@ const Captcha = (0, react.forwardRef)(function Captcha(_props, ref) {
131
131
  const { provider, sitekey, token, theme, size, tabindex, options, defaultValue, onTokenChange, defaultToken, ...rest } = _props;
132
132
  return rest;
133
133
  })();
134
- const disposed = (0, react.useRef)(false);
135
134
  const api = (0, react.useRef)(null);
136
135
  const widgetId = (0, react.useRef)(null);
137
136
  const [token, setToken] = (0, _rozie_runtime_react.useControllableState)({
@@ -185,9 +184,9 @@ const Captcha = (0, react.forwardRef)(function Captcha(_props, ref) {
185
184
  return widgetId.current != null && api.current && typeof api.current.getResponse === "function" ? api.current.getResponse(widgetId.current) : "";
186
185
  }
187
186
  (0, react.useEffect)(() => {
188
- disposed.current = false;
187
+ let disposed = false;
189
188
  loadCaptchaApi(props.provider).then((a) => {
190
- if (disposed.current) return;
189
+ if (disposed) return;
191
190
  api.current = a;
192
191
  widgetId.current = api.current.render(widgetEl.current, buildConfig());
193
192
  }).catch((err) => {
@@ -197,7 +196,7 @@ const Captcha = (0, react.forwardRef)(function Captcha(_props, ref) {
197
196
  });
198
197
  });
199
198
  return () => {
200
- disposed.current = true;
199
+ disposed = true;
201
200
  if (widgetId.current == null || !api.current) return;
202
201
  if (typeof api.current.remove === "function") api.current.remove(widgetId.current);
203
202
  else if (typeof api.current.reset === "function") api.current.reset(widgetId.current);
@@ -315,7 +314,7 @@ const RecaptchaV3 = (0, react.forwardRef)(function RecaptchaV3(_props, ref) {
315
314
  defaultValue: props.defaultToken ?? "",
316
315
  onValueChange: props.onTokenChange
317
316
  });
318
- function execute$1(action = null) {
317
+ function execute$1(action) {
319
318
  const a = action != null ? action : props.action;
320
319
  return loadRecaptchaV3(props.sitekey).then(() => execute(props.sitekey, { action: a })).then((tok) => {
321
320
  if (disposed.current) return tok;
package/dist/index.d.cts CHANGED
@@ -2,14 +2,37 @@ import * as React from "react";
2
2
 
3
3
  //#region src/Captcha.d.ts
4
4
  interface CaptchaProps {
5
+ /**
6
+ * Which widget to render: `recaptcha` (Google reCAPTCHA v2), `hcaptcha`, `turnstile` (Cloudflare), or `friendly` (Friendly Captcha). The first three share a near-identical explicit-render API; Friendly Captcha rides an internal `adapt()` bridge onto the same surface. Construction-time — re-key the component to switch it live.
7
+ */
5
8
  provider?: string;
9
+ /**
10
+ * Required. The public site key from your provider dashboard. Identifies your site to the chosen provider.
11
+ */
6
12
  sitekey: string;
13
+ /**
14
+ * The verified response token (two-way `r-model`). As the sole `model: true` prop it drives the Angular `ControlValueAccessor`. Written by the widget on success and cleared on expire/reset, so reading it gives you the live response to send to your server for form submission.
15
+ * @example
16
+ * <Captcha r-model:token="token" provider="recaptcha" sitekey="…" />
17
+ */
7
18
  token?: string;
8
19
  defaultToken?: string;
9
20
  onTokenChange?: (next: string) => void;
21
+ /**
22
+ * Widget color theme: `light` or `dark` (all three core providers), or `auto` (Turnstile only). Construction-time — re-key the component to change it live.
23
+ */
10
24
  theme?: string;
25
+ /**
26
+ * Widget size. reCAPTCHA/hCaptcha accept `normal`/`compact`/`invisible`; Turnstile accepts `normal`/`compact`/`flexible`. A no-op for Friendly Captcha (its `startMode` analog rides through the `options` escape hatch instead). Construction-time.
27
+ */
11
28
  size?: string;
29
+ /**
30
+ * Optional tab index forwarded to the rendered widget. Omitted from the render config when left unset (`null`).
31
+ */
12
32
  tabindex?: (number) | null;
33
+ /**
34
+ * Escape hatch — provider-specific render options merged last (e.g. Turnstile `action`/`cData`/`retry`, hCaptcha `hl`, reCAPTCHA `badge`, Friendly Captcha `startMode`). Lets you reach keys this component does not promote to first-class props.
35
+ */
13
36
  options?: Record<string, unknown>;
14
37
  onVerify?: (...args: unknown[]) => void;
15
38
  onExpire?: (...args: unknown[]) => void;
@@ -24,11 +47,25 @@ declare const Captcha: React.ForwardRefExoticComponent<CaptchaProps & React.RefA
24
47
  //#endregion
25
48
  //#region src/RecaptchaV3.d.ts
26
49
  interface RecaptchaV3Props {
50
+ /**
51
+ * Required. The public reCAPTCHA v3 site key from your Google admin console.
52
+ */
27
53
  sitekey: string;
54
+ /**
55
+ * The default action label reported to reCAPTCHA's risk analysis (e.g. `submit`, `login`). Overridable per call via `execute(action)`.
56
+ */
28
57
  action?: string;
58
+ /**
59
+ * The latest verification token (two-way `r-model`). As the sole `model: true` prop it drives the Angular `ControlValueAccessor`. Written on each successful `execute()` — read it to attach the fresh token to your request.
60
+ * @example
61
+ * <RecaptchaV3 r-model:token="token" sitekey="…" action="signup" />
62
+ */
29
63
  token?: string;
30
64
  defaultToken?: string;
31
65
  onTokenChange?: (next: string) => void;
66
+ /**
67
+ * Opt in to running one `execute()` at mount and emitting `@verify` with the initial token. Off by default — v3 is imperative-first and tokens are short-lived (~2 min), so fetch one at the moment of submission rather than eagerly at mount.
68
+ */
32
69
  executeOnMount?: boolean;
33
70
  onError?: (...args: unknown[]) => void;
34
71
  onVerify?: (...args: unknown[]) => void;
package/dist/index.d.mts CHANGED
@@ -2,14 +2,37 @@ import * as _$react from "react";
2
2
 
3
3
  //#region src/Captcha.d.ts
4
4
  interface CaptchaProps {
5
+ /**
6
+ * Which widget to render: `recaptcha` (Google reCAPTCHA v2), `hcaptcha`, `turnstile` (Cloudflare), or `friendly` (Friendly Captcha). The first three share a near-identical explicit-render API; Friendly Captcha rides an internal `adapt()` bridge onto the same surface. Construction-time — re-key the component to switch it live.
7
+ */
5
8
  provider?: string;
9
+ /**
10
+ * Required. The public site key from your provider dashboard. Identifies your site to the chosen provider.
11
+ */
6
12
  sitekey: string;
13
+ /**
14
+ * The verified response token (two-way `r-model`). As the sole `model: true` prop it drives the Angular `ControlValueAccessor`. Written by the widget on success and cleared on expire/reset, so reading it gives you the live response to send to your server for form submission.
15
+ * @example
16
+ * <Captcha r-model:token="token" provider="recaptcha" sitekey="…" />
17
+ */
7
18
  token?: string;
8
19
  defaultToken?: string;
9
20
  onTokenChange?: (token: string) => void;
21
+ /**
22
+ * Widget color theme: `light` or `dark` (all three core providers), or `auto` (Turnstile only). Construction-time — re-key the component to change it live.
23
+ */
10
24
  theme?: string;
25
+ /**
26
+ * Widget size. reCAPTCHA/hCaptcha accept `normal`/`compact`/`invisible`; Turnstile accepts `normal`/`compact`/`flexible`. A no-op for Friendly Captcha (its `startMode` analog rides through the `options` escape hatch instead). Construction-time.
27
+ */
11
28
  size?: string;
29
+ /**
30
+ * Optional tab index forwarded to the rendered widget. Omitted from the render config when left unset (`null`).
31
+ */
12
32
  tabindex?: (number) | null;
33
+ /**
34
+ * Escape hatch — provider-specific render options merged last (e.g. Turnstile `action`/`cData`/`retry`, hCaptcha `hl`, reCAPTCHA `badge`, Friendly Captcha `startMode`). Lets you reach keys this component does not promote to first-class props.
35
+ */
13
36
  options?: Record<string, any>;
14
37
  onVerify?: (...args: any[]) => void;
15
38
  onExpire?: (...args: any[]) => void;
@@ -24,11 +47,25 @@ declare const Captcha: _$react.ForwardRefExoticComponent<CaptchaProps & _$react.
24
47
  //#endregion
25
48
  //#region src/RecaptchaV3.d.ts
26
49
  interface RecaptchaV3Props {
50
+ /**
51
+ * Required. The public reCAPTCHA v3 site key from your Google admin console.
52
+ */
27
53
  sitekey: string;
54
+ /**
55
+ * The default action label reported to reCAPTCHA's risk analysis (e.g. `submit`, `login`). Overridable per call via `execute(action)`.
56
+ */
28
57
  action?: string;
58
+ /**
59
+ * The latest verification token (two-way `r-model`). As the sole `model: true` prop it drives the Angular `ControlValueAccessor`. Written on each successful `execute()` — read it to attach the fresh token to your request.
60
+ * @example
61
+ * <RecaptchaV3 r-model:token="token" sitekey="…" action="signup" />
62
+ */
29
63
  token?: string;
30
64
  defaultToken?: string;
31
65
  onTokenChange?: (token: string) => void;
66
+ /**
67
+ * Opt in to running one `execute()` at mount and emitting `@verify` with the initial token. Off by default — v3 is imperative-first and tokens are short-lived (~2 min), so fetch one at the moment of submission rather than eagerly at mount.
68
+ */
32
69
  executeOnMount?: boolean;
33
70
  onError?: (...args: any[]) => void;
34
71
  onVerify?: (...args: any[]) => void;
package/dist/index.mjs CHANGED
@@ -127,7 +127,6 @@ const Captcha = forwardRef(function Captcha(_props, ref) {
127
127
  const { provider, sitekey, token, theme, size, tabindex, options, defaultValue, onTokenChange, defaultToken, ...rest } = _props;
128
128
  return rest;
129
129
  })();
130
- const disposed = useRef(false);
131
130
  const api = useRef(null);
132
131
  const widgetId = useRef(null);
133
132
  const [token, setToken] = useControllableState({
@@ -181,9 +180,9 @@ const Captcha = forwardRef(function Captcha(_props, ref) {
181
180
  return widgetId.current != null && api.current && typeof api.current.getResponse === "function" ? api.current.getResponse(widgetId.current) : "";
182
181
  }
183
182
  useEffect(() => {
184
- disposed.current = false;
183
+ let disposed = false;
185
184
  loadCaptchaApi(props.provider).then((a) => {
186
- if (disposed.current) return;
185
+ if (disposed) return;
187
186
  api.current = a;
188
187
  widgetId.current = api.current.render(widgetEl.current, buildConfig());
189
188
  }).catch((err) => {
@@ -193,7 +192,7 @@ const Captcha = forwardRef(function Captcha(_props, ref) {
193
192
  });
194
193
  });
195
194
  return () => {
196
- disposed.current = true;
195
+ disposed = true;
197
196
  if (widgetId.current == null || !api.current) return;
198
197
  if (typeof api.current.remove === "function") api.current.remove(widgetId.current);
199
198
  else if (typeof api.current.reset === "function") api.current.reset(widgetId.current);
@@ -311,7 +310,7 @@ const RecaptchaV3 = forwardRef(function RecaptchaV3(_props, ref) {
311
310
  defaultValue: props.defaultToken ?? "",
312
311
  onValueChange: props.onTokenChange
313
312
  });
314
- function execute$1(action = null) {
313
+ function execute$1(action) {
315
314
  const a = action != null ? action : props.action;
316
315
  return loadRecaptchaV3(props.sitekey).then(() => execute(props.sitekey, { action: a })).then((tok) => {
317
316
  if (disposed.current) return tok;
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@rozie-ui/captcha-react",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "MIT",
7
- "description": "Idiomatic React Captcha — one Rozie source compiled to React.",
7
+ "description": "Idiomatic React CAPTCHAGoogle reCAPTCHA (v2 + v3), hCaptcha, Cloudflare Turnstile & Friendly Captcha from one Rozie source.",
8
8
  "keywords": [
9
9
  "rozie",
10
10
  "rozie-ui",
@@ -45,7 +45,7 @@
45
45
  "src"
46
46
  ],
47
47
  "dependencies": {
48
- "@rozie/runtime-react": "0.1.1"
48
+ "@rozie/runtime-react": "0.2.0"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "react": "^18.2 || ^19",
package/src/Captcha.d.ts CHANGED
@@ -3,14 +3,37 @@ import type { ForwardRefExoticComponent, RefAttributes } from 'react';
3
3
  import type * as React from 'react';
4
4
 
5
5
  export interface CaptchaProps {
6
+ /**
7
+ * Which widget to render: `recaptcha` (Google reCAPTCHA v2), `hcaptcha`, `turnstile` (Cloudflare), or `friendly` (Friendly Captcha). The first three share a near-identical explicit-render API; Friendly Captcha rides an internal `adapt()` bridge onto the same surface. Construction-time — re-key the component to switch it live.
8
+ */
6
9
  provider?: string;
10
+ /**
11
+ * Required. The public site key from your provider dashboard. Identifies your site to the chosen provider.
12
+ */
7
13
  sitekey: string;
14
+ /**
15
+ * The verified response token (two-way `r-model`). As the sole `model: true` prop it drives the Angular `ControlValueAccessor`. Written by the widget on success and cleared on expire/reset, so reading it gives you the live response to send to your server for form submission.
16
+ * @example
17
+ * <Captcha r-model:token="token" provider="recaptcha" sitekey="…" />
18
+ */
8
19
  token?: string;
9
20
  defaultToken?: string;
10
21
  onTokenChange?: (next: string) => void;
22
+ /**
23
+ * Widget color theme: `light` or `dark` (all three core providers), or `auto` (Turnstile only). Construction-time — re-key the component to change it live.
24
+ */
11
25
  theme?: string;
26
+ /**
27
+ * Widget size. reCAPTCHA/hCaptcha accept `normal`/`compact`/`invisible`; Turnstile accepts `normal`/`compact`/`flexible`. A no-op for Friendly Captcha (its `startMode` analog rides through the `options` escape hatch instead). Construction-time.
28
+ */
12
29
  size?: string;
30
+ /**
31
+ * Optional tab index forwarded to the rendered widget. Omitted from the render config when left unset (`null`).
32
+ */
13
33
  tabindex?: (number) | null;
34
+ /**
35
+ * Escape hatch — provider-specific render options merged last (e.g. Turnstile `action`/`cData`/`retry`, hCaptcha `hl`, reCAPTCHA `badge`, Friendly Captcha `startMode`). Lets you reach keys this component does not promote to first-class props.
36
+ */
14
37
  options?: Record<string, unknown>;
15
38
  onVerify?: (...args: unknown[]) => void;
16
39
  onExpire?: (...args: unknown[]) => void;
package/src/Captcha.tsx CHANGED
@@ -7,20 +7,41 @@ import { clsx, useControllableState } from '@rozie/runtime-react';
7
7
  import { loadCaptchaApi } from './internal/loadCaptchaApi';
8
8
 
9
9
  // Live widget handle. Top-level lets → React hoists to useRef (setup-once).
10
- // `disposed` MUST be top-level (not declared inside $onMount): the Solid emitter
11
- // extracts the teardown into a separate onCleanup() whose scope can't see a
12
- // mount-body local, so a `let disposed` inside $onMount is out of scope in the
13
- // teardown (TS2304). Top-level — like api/widgetId — is visible to both.
10
+ // `api`/`widgetId` MUST be top-level — reset()/execute()/getResponse() (the
11
+ // $expose'd imperative handle, callable any time) read them outside $onMount.
14
12
 
15
13
  interface CaptchaProps {
14
+ /**
15
+ * Which widget to render: `recaptcha` (Google reCAPTCHA v2), `hcaptcha`, `turnstile` (Cloudflare), or `friendly` (Friendly Captcha). The first three share a near-identical explicit-render API; Friendly Captcha rides an internal `adapt()` bridge onto the same surface. Construction-time — re-key the component to switch it live.
16
+ */
16
17
  provider?: string;
18
+ /**
19
+ * Required. The public site key from your provider dashboard. Identifies your site to the chosen provider.
20
+ */
17
21
  sitekey: string;
22
+ /**
23
+ * The verified response token (two-way `r-model`). As the sole `model: true` prop it drives the Angular `ControlValueAccessor`. Written by the widget on success and cleared on expire/reset, so reading it gives you the live response to send to your server for form submission.
24
+ * @example
25
+ * <Captcha r-model:token="token" provider="recaptcha" sitekey="…" />
26
+ */
18
27
  token?: string;
19
28
  defaultToken?: string;
20
29
  onTokenChange?: (token: string) => void;
30
+ /**
31
+ * Widget color theme: `light` or `dark` (all three core providers), or `auto` (Turnstile only). Construction-time — re-key the component to change it live.
32
+ */
21
33
  theme?: string;
34
+ /**
35
+ * Widget size. reCAPTCHA/hCaptcha accept `normal`/`compact`/`invisible`; Turnstile accepts `normal`/`compact`/`flexible`. A no-op for Friendly Captcha (its `startMode` analog rides through the `options` escape hatch instead). Construction-time.
36
+ */
22
37
  size?: string;
38
+ /**
39
+ * Optional tab index forwarded to the rendered widget. Omitted from the render config when left unset (`null`).
40
+ */
23
41
  tabindex?: (number) | null;
42
+ /**
43
+ * Escape hatch — provider-specific render options merged last (e.g. Turnstile `action`/`cData`/`retry`, hCaptcha `hl`, reCAPTCHA `badge`, Friendly Captcha `startMode`). Lets you reach keys this component does not promote to first-class props.
44
+ */
24
45
  options?: Record<string, any>;
25
46
  onVerify?: (...args: any[]) => void;
26
47
  onExpire?: (...args: any[]) => void;
@@ -48,7 +69,6 @@ const Captcha = forwardRef<CaptchaHandle, CaptchaProps>(function Captcha(_props:
48
69
  void provider; void sitekey; void token; void theme; void size; void tabindex; void options; void defaultValue; void onTokenChange; void defaultToken;
49
70
  return rest;
50
71
  })();
51
- const disposed = useRef(false);
52
72
  const api = useRef<any>(null);
53
73
  const widgetId = useRef<any>(null);
54
74
  const [token, setToken] = useControllableState({
@@ -109,9 +129,15 @@ const Captcha = forwardRef<CaptchaHandle, CaptchaProps>(function Captcha(_props:
109
129
  }
110
130
 
111
131
  useEffect(() => {
112
- disposed.current = false;
132
+ // Mount-local (not top-level) — read only by this closure's own async
133
+ // .then()/.catch() and the returned teardown below. Emitter-hardening
134
+ // backlog item #2 (project_emitter_hardening_backlog): every target keeps
135
+ // a $onMount setup-local in scope for its own returned teardown, so this
136
+ // no longer needs the prior TOP-LEVEL-`let` workaround (unlike `api`/
137
+ // `widgetId` above, which stay top-level for the unrelated $expose reason).
138
+ let disposed = false;
113
139
  loadCaptchaApi(props.provider).then((a: any) => {
114
- if (disposed.current) return;
140
+ if (disposed) return;
115
141
  api.current = a;
116
142
  widgetId.current = api.current.render(widgetEl.current!, buildConfig());
117
143
  }).catch((err: any) => {
@@ -121,7 +147,7 @@ const Captcha = forwardRef<CaptchaHandle, CaptchaProps>(function Captcha(_props:
121
147
  });
122
148
  });
123
149
  return () => {
124
- disposed.current = true;
150
+ disposed = true;
125
151
  if (widgetId.current == null || !api.current) return;
126
152
  // Turnstile fully removes a widget; reCAPTCHA/hCaptcha only reset.
127
153
  if (typeof api.current.remove === 'function') api.current.remove(widgetId.current);else if (typeof api.current.reset === 'function') api.current.reset(widgetId.current);
@@ -3,11 +3,25 @@ import type { ForwardRefExoticComponent, RefAttributes } from 'react';
3
3
  import type * as React from 'react';
4
4
 
5
5
  export interface RecaptchaV3Props {
6
+ /**
7
+ * Required. The public reCAPTCHA v3 site key from your Google admin console.
8
+ */
6
9
  sitekey: string;
10
+ /**
11
+ * The default action label reported to reCAPTCHA's risk analysis (e.g. `submit`, `login`). Overridable per call via `execute(action)`.
12
+ */
7
13
  action?: string;
14
+ /**
15
+ * The latest verification token (two-way `r-model`). As the sole `model: true` prop it drives the Angular `ControlValueAccessor`. Written on each successful `execute()` — read it to attach the fresh token to your request.
16
+ * @example
17
+ * <RecaptchaV3 r-model:token="token" sitekey="…" action="signup" />
18
+ */
8
19
  token?: string;
9
20
  defaultToken?: string;
10
21
  onTokenChange?: (next: string) => void;
22
+ /**
23
+ * Opt in to running one `execute()` at mount and emitting `@verify` with the initial token. Off by default — v3 is imperative-first and tokens are short-lived (~2 min), so fetch one at the moment of submission rather than eagerly at mount.
24
+ */
11
25
  executeOnMount?: boolean;
12
26
  onError?: (...args: unknown[]) => void;
13
27
  onVerify?: (...args: unknown[]) => void;
@@ -6,18 +6,34 @@ import { clsx, useControllableState } from '@rozie/runtime-react';
6
6
  // codegen copies src/internal/ into every leaf, so this import resolves ×6.
7
7
  import { loadRecaptchaV3, execute as v3Execute } from './internal/loadRecaptchaV3';
8
8
 
9
- // `disposed` MUST be top-level (not declared inside $onMount): the Solid emitter
10
- // extracts the teardown into a separate onCleanup() whose scope can't see a
11
- // mount-body local, so a `let disposed` inside $onMount is out of scope in the
12
- // teardown. Top-level visible to both the mount body and the teardown. It also
13
- // guards a late execute() resolve that fires after the component unmounts.
9
+ // `disposed` MUST be top-level (not $onMount-local): the exported `execute()`
10
+ // below callable any time via `$expose({ execute })`, including after
11
+ // unmount reads it to guard a late resolve that fires post-unmount. That
12
+ // cross-function visibility (not a per-target emitter limitation) is why this
13
+ // one stays top-level even after emitter-hardening backlog item #2 landed
14
+ // (contrast Captcha.rozie's `disposed`, which IS $onMount-local — its
15
+ // exposed handle functions don't read it).
14
16
 
15
17
  interface RecaptchaV3Props {
18
+ /**
19
+ * Required. The public reCAPTCHA v3 site key from your Google admin console.
20
+ */
16
21
  sitekey: string;
22
+ /**
23
+ * The default action label reported to reCAPTCHA's risk analysis (e.g. `submit`, `login`). Overridable per call via `execute(action)`.
24
+ */
17
25
  action?: string;
26
+ /**
27
+ * The latest verification token (two-way `r-model`). As the sole `model: true` prop it drives the Angular `ControlValueAccessor`. Written on each successful `execute()` — read it to attach the fresh token to your request.
28
+ * @example
29
+ * <RecaptchaV3 r-model:token="token" sitekey="…" action="signup" />
30
+ */
18
31
  token?: string;
19
32
  defaultToken?: string;
20
33
  onTokenChange?: (token: string) => void;
34
+ /**
35
+ * Opt in to running one `execute()` at mount and emitting `@verify` with the initial token. Off by default — v3 is imperative-first and tokens are short-lived (~2 min), so fetch one at the moment of submission rather than eagerly at mount.
36
+ */
21
37
  executeOnMount?: boolean;
22
38
  onError?: (...args: any[]) => void;
23
39
  onVerify?: (...args: any[]) => void;
@@ -52,14 +68,13 @@ const RecaptchaV3 = forwardRef<RecaptchaV3Handle, RecaptchaV3Props>(function Rec
52
68
  // ref named `token`, and a same-named param shadows it (`token.value = token`
53
69
  // would write the param). Use `tok` (mirrors Captcha.rozie's `response`).
54
70
  //
55
- // `action = null` (an explicit DEFAULT, not a bare `action`) makes the param
56
- // OPTIONAL required so the no-arg call in $onMount's executeOnMount path
57
- // (`execute()`) typechecks. The type-neutralizer otherwise lowers a bare param
58
- // to a REQUIRED `action: any`, which Vue's strict declaration emit (vue-tsc)
59
- // rejects at the `execute()` call (TS2554) the other five targets don't
60
- // body-typecheck the emitted leaf, so the issue is Vue-only but real. The
61
- // default is logic-neutral: the body already guards `action != null`.
62
- function execute(action = null) {
71
+ // A bare `action` (no author default) is fine the emitter now lowers a
72
+ // TRAILING `$expose` verb param optional (`action?: any`) whenever it sees a
73
+ // fewer-arg internal call to the SAME verb, which the no-arg
74
+ // executeOnMount path (`execute()`) below is (emitter-hardening backlog
75
+ // item #5). The `action = null` author-side default this comment used to
76
+ // require is gone — the compiler owns the arity now, not this source.
77
+ function execute(action?: any) {
63
78
  const a = action != null ? action : props.action;
64
79
  return loadRecaptchaV3(props.sitekey).then(() => v3Execute(props.sitekey, {
65
80
  action: a