You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat: CAPTCHA, hidden tracking field, preview fix, tests + CI
- fix(admin): Preview modal scrolls internally instead of overflowing
on tall forms (flex: 1 / minHeight: 0 / alignItems: flex-start).
- feat(captcha): per-form CAPTCHA (Cloudflare Turnstile or Google
reCAPTCHA v2). Server-side verify (fail-closed), secret key never
exposed to the browser, CSP relaxed for the provider on the public
page, client guard blocks submit without a token. Saving is blocked
when a provider is chosen without keys; keys reset on provider change.
- feat(fields): hidden tracking field — captures a URL query param
(e.g. utm_source) or a default value, stored with the submission and
included in CSV export. Never rendered visibly.
- feat(admin): "Test & learn regex" help link on pattern validation.
- refactor(admin): drop the redundant Actions column in the submissions
inbox (the whole row already opens the detail drawer).
- test: Vitest + seed tests for verifyCaptcha (fail-closed), public
schema secret-key stripping, and field validation.
- ci: run the test suite in CI.
devDeps: vitest; zod pinned to align the transitive ai/zod-v4
resolution for local symlinked plugin dev (not shipped — files: ["dist"]).
* fix(ci): regenerate package-lock so npm ci is in sync
* fix(ci): additive lockfile (keep original dep versions, add vitest/zod)
* fix: address review — hidden field tab leak, CSP script-src, captcha fail-closed + timeout
* feat(captcha): validate secret key in settings + honest public-form failure message
A hidden tracking field. It never shows on the form but its value is stored with every submission — ideal for UTM parameters, referrers or campaign IDs.
162
+
</div>
163
+
<TextFieldlabel="Name (technical)"value={field.name}onChange={(v)=>update({name: v})}monohint="Key stored with each submission (e.g. utm_source)."/>
164
+
<TextFieldlabel="Prefill from URL parameter"value={field.queryParam||''}onChange={(v)=>update({queryParam: v})}monoplaceholder="utm_source"hint="If the form URL has ?utm_source=… its value is captured automatically."/>
165
+
<TextFieldlabel="Default value"value={String(field.defaultValue??'')}onChange={(v)=>update({defaultValue: v})}hint="Used when the URL parameter above is absent."/>
<inputvalue={settings.captcha.siteKey}onChange={(e)=>patch({captcha: { ...settings.captcha!,siteKey: e.target.value}})}placeholder="Public — sent to the browser"style={{ ...dInp,fontFamily: 'ui-monospace, Menlo, monospace',fontSize: 12}}/>
0 commit comments