{"_id":"@adamarant/contact-form","_rev":"2-15cd04f726bbe174afe4e0a4c27be1ea","name":"@adamarant/contact-form","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@adamarant/contact-form","version":"0.1.0","keywords":["contact-form","resend","email","turnstile","honeypot","rate-limit","nextjs","react"],"license":"MIT","_id":"@adamarant/contact-form@0.1.0","maintainers":[{"name":"digiko-npm","email":"riccardo@digiko.io"}],"homepage":"https://github.com/adamarant/contact-form#readme","bugs":{"url":"https://github.com/adamarant/contact-form/issues"},"dist":{"shasum":"f25054e9e7b929801f8ea425770f52b70ddf4a9b","tarball":"https://registry.npmjs.org/@adamarant/contact-form/-/contact-form-0.1.0.tgz","fileCount":83,"integrity":"sha512-tgbCvAnRxdqcLaivr54YRR6ML4WxjgByjv3m09PLRN373Svm/6opVOp28J+ZlTo43AsgV82GB0iNKOt76qt3bw==","signatures":[{"sig":"MEQCIB0stqvUoz5Fp+HHBg0jvo45fK3/NORHVwJbCm36+5S5AiADwueW9LkN1dV3Sovwi+YZ1sDU55IO1F8yEquhp0Y/zQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":149279},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./email":{"types":"./dist/email/index.d.ts","import":"./dist/email/index.js"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","import":"./dist/server/index.js"},"./styles":"./src/styles/contact-form.css","./styles.css":"./src/styles/contact-form.css"},"gitHead":"c776d92a8b5c1f09a07ce0f5b91073fe0cf9f055","scripts":{"dev":"tsc --watch","build":"tsc","clean":"rm -rf dist","type-check":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"digiko-npm","email":"riccardo@digiko.io"},"repository":{"url":"git+https://github.com/adamarant/contact-form.git","type":"git"},"_npmVersion":"11.6.2","description":"Drop-in contact form + secured Resend email flow (Cloudflare Turnstile, honeypot/time-trap, rate-limit). Configure fields, recipient and keys per project. Next.js or any fetch runtime.","directories":{},"_nodeVersion":"25.2.1","dependencies":{"resend":"^6.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","typescript":"^5","@types/react":"^19","@upstash/redis":"^1.36.0"},"peerDependencies":{"react":">=18.0.0","@upstash/redis":"^1.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"@upstash/redis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/contact-form_0.1.0_1781864483985_0.700015982187012","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@adamarant/contact-form","version":"0.1.1","description":"Drop-in contact form + secured Resend email flow (Cloudflare Turnstile, honeypot/time-trap, rate-limit). Configure fields, recipient and keys per project. Next.js or any fetch runtime.","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./server":{"types":"./dist/server/index.d.ts","import":"./dist/server/index.js"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js"},"./email":{"types":"./dist/email/index.d.ts","import":"./dist/email/index.js"},"./styles":"./src/styles/contact-form.css","./styles.css":"./src/styles/contact-form.css"},"scripts":{"build":"tsc","dev":"tsc --watch","type-check":"tsc --noEmit","clean":"rm -rf dist","prepublishOnly":"npm run build"},"publishConfig":{"access":"public"},"peerDependencies":{"@upstash/redis":"^1.0.0","react":">=18.0.0"},"peerDependenciesMeta":{"@upstash/redis":{"optional":true},"react":{"optional":true}},"dependencies":{"resend":"^6.0.0"},"devDependencies":{"@types/react":"^19","@upstash/redis":"^1.36.0","react":"^19.0.0","typescript":"^5"},"repository":{"type":"git","url":"git+https://github.com/adamarant/contact-form.git"},"license":"MIT","keywords":["contact-form","resend","email","turnstile","honeypot","rate-limit","nextjs","react"],"gitHead":"25a0343ff68c3ef9b0b8a927b9c435adb0a02ea3","_id":"@adamarant/contact-form@0.1.1","bugs":{"url":"https://github.com/adamarant/contact-form/issues"},"homepage":"https://github.com/adamarant/contact-form#readme","_nodeVersion":"24.16.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-Yet4S3Jg2XrMdI36X171EbXvqQYOkCbwiVeKe9NKG1PGMeLxuBQWMy7KEkwCSF6zh7cZiy0Tp8EZiD6vGfUlmw==","shasum":"e644eacfd5cfc6ed495c99fb0e46d15fe11206a9","tarball":"https://registry.npmjs.org/@adamarant/contact-form/-/contact-form-0.1.1.tgz","fileCount":83,"unpackedSize":150690,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDkFLZBoFRkgLQzG2oQZK/mAE+T97xdfNLqiCkv6OH8yAIhAOHWRGlVxFF0avoSyFDRFkwGvLDW4lS9beelniVQTw0O"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:84f0e76d-f602-4219-9f8b-d1938d1d0998"}},"directories":{},"maintainers":[{"name":"digiko-npm","email":"riccardo@digiko.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/contact-form_0.1.1_1781871781929_0.7232715649940722"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-19T10:21:23.778Z","modified":"2026-06-19T12:23:02.367Z","0.1.0":"2026-06-19T10:21:24.114Z","0.1.1":"2026-06-19T12:23:02.059Z"},"bugs":{"url":"https://github.com/adamarant/contact-form/issues"},"license":"MIT","homepage":"https://github.com/adamarant/contact-form#readme","keywords":["contact-form","resend","email","turnstile","honeypot","rate-limit","nextjs","react"],"repository":{"type":"git","url":"git+https://github.com/adamarant/contact-form.git"},"description":"Drop-in contact form + secured Resend email flow (Cloudflare Turnstile, honeypot/time-trap, rate-limit). Configure fields, recipient and keys per project. Next.js or any fetch runtime.","maintainers":[{"name":"digiko-npm","email":"riccardo@digiko.io"}],"readme":"# @adamarant/contact-form\n\nDrop-in **contact form + secured Resend email flow** for Next.js (or any Fetch\nruntime). Install once, then per project you only customize three things: the\n**fields**, the **recipient**, and the **API keys**.\n\nSecurity is built in: Cloudflare Turnstile, honeypot + time-trap, per-IP\nrate-limiting, and strict server-side validation. Standalone — **no design\nsystem or database required** (both are optional).\n\n## Install\n\n```bash\nnpm install @adamarant/contact-form\n```\n\nPeer deps are optional and pulled in only if you use them: `react` (client form),\n`@upstash/redis` (durable rate-limit). `resend` is bundled.\n\n## Server — the secured route\n\nDrop a single handler into an App Router route. It runs the full pipeline\n(parse → honeypot → validate → rate-limit → Turnstile → optional persist → email)\nand returns `{ success: true }` or `{ success: false, error }`.\n\n```ts\n// src/app/api/contact/route.ts\nimport { createContactRoute } from '@adamarant/contact-form/server'\n\nexport const POST = createContactRoute({\n  fields: [\n    { name: 'name',    label: 'Name',    required: true, autoComplete: 'name' },\n    { name: 'email',   label: 'Email',   type: 'email', required: true, autoComplete: 'email' },\n    { name: 'message', label: 'Message', type: 'textarea', required: true },\n  ],\n  resend: {\n    apiKey: process.env.RESEND_API_KEY!,    // keys live in the consuming app, never in this lib\n    from: 'Acme <noreply@acme.com>',        // domain must be verified in Resend\n    to: 'hello@acme.com',\n  },\n  brand: { name: 'Acme', url: 'https://acme.com' },\n\n  // Optional security layers — omit any you don't want:\n  turnstile: { secretKey: process.env.TURNSTILE_SECRET_KEY! },\n  rateLimit: {\n    redis: {\n      url: process.env.UPSTASH_REDIS_REST_URL!,\n      token: process.env.UPSTASH_REDIS_REST_TOKEN!,\n    },\n    maxAttempts: 5,\n    windowMs: 60 * 60 * 1000,\n  },\n\n  // Optional: persist the lead before the email is sent (DB / CRM).\n  // onSubmit: async ({ data, ip, userAgent }) => { await db.insert(...) },\n})\n```\n\nOmit `rateLimit.redis` to use the in-memory limiter (per-process; fine for a\nsingle instance, multiplied per instance on serverless). Pass `rateLimit: false`\nto disable. Omit `turnstile` to skip the captcha.\n\n## Client — the ready form\n\n```tsx\n'use client'\nimport { ContactForm } from '@adamarant/contact-form/client'\nimport '@adamarant/contact-form/styles'   // optional default styling\n\nconst fields = [\n  { name: 'name',    label: 'Name',    required: true },\n  { name: 'email',   label: 'Email',   type: 'email', required: true },\n  { name: 'message', label: 'Message', type: 'textarea', required: true },\n] as const\n\nexport function Contact() {\n  return (\n    <ContactForm\n      action=\"/api/contact\"\n      fields={fields}\n      turnstileSiteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY}\n    />\n  )\n}\n```\n\nThe form ships the honeypot, the Turnstile widget (self-loaded, no extra\ndependency), the submit state machine, and success/error UI.\n\n### Styling\n\nTwo options:\n\n1. **Import the default stylesheet** (`@adamarant/contact-form/styles`) and tune\n   the `--acf-*` variables.\n2. **Map to your own classes** via the `classNames` prop — e.g. a design-system\n   consumer:\n\n   ```tsx\n   <ContactForm\n     action=\"/api/contact\"\n     fields={fields}\n     classNames={{ input: 'ds-input', textarea: 'ds-textarea', label: 'ds-label', button: 'ds-btn ds-btn--full' }}\n   />\n   ```\n\n### Localized copy\n\n```tsx\n<ContactForm\n  action=\"/api/contact\"\n  fields={fields}\n  locale=\"it\"\n  messages={{\n    submit: 'Invia',\n    sending: 'Invio…',\n    successTitle: 'Messaggio inviato',\n    successMessage: 'Grazie, ti rispondiamo presto.',\n    errors: { rate_limited: 'Troppi tentativi, riprova più tardi.' },\n  }}\n/>\n```\n\nError codes returned by the route: `missing_fields`, `invalid_email`,\n`too_long`, `rate_limited`, `captcha_failed`, `invalid_request`, `server_error`.\n\n## Email template\n\nThe default template renders a clean notification (inline fields as a table,\n`textarea`/`block` fields as full-width boxes, a Reply button targeting the\nsubmitter). Customize via `brand`, `subject`, `intro`, or replace it entirely\nwith `render`. You can also call `renderContactEmail` from\n`@adamarant/contact-form/email` directly.\n\n## Resend notes\n\n- Each project sets its own `RESEND_API_KEY` + verified `from` domain. One Resend\n  plan covers a fixed number of verified domains (e.g. the €20 tier = 10 domains).\n- With no API key configured, sends are a no-op \"mock\" success — handy in dev.\n\n## API surface\n\n| Entry | Exports |\n|-------|---------|\n| `@adamarant/contact-form/server` | `createContactRoute`, plus primitives: `verifyTurnstileToken`, `validateHoneypot`, `createRateLimiter`, `validateContact`, `sendContactEmail`, `getClientIp` |\n| `@adamarant/contact-form/client` | `ContactForm`, `Turnstile` |\n| `@adamarant/contact-form/email`  | `renderContactEmail` |\n| `@adamarant/contact-form`        | shared types + `HONEYPOT_FIELD_NAMES` |\n| `@adamarant/contact-form/styles` | optional default CSS |\n\n## License\n\nMIT\n","readmeFilename":"README.md"}