{"_id":"@anishwij/meta-cookies","_rev":"2-41e31f7068fc24ed25b55dbd58b81de2","name":"@anishwij/meta-cookies","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@anishwij/meta-cookies","version":"0.1.0","keywords":["meta","facebook","pixel","cookies","fbp","fbc","fbclid","conversions-api","capi","nextjs","middleware","server-side","tracking","attribution"],"author":{"name":"Anish Wijesinghe"},"license":"MIT","_id":"@anishwij/meta-cookies@0.1.0","maintainers":[{"name":"anish-wij","email":"anishwijesinghe@gmail.com"}],"homepage":"https://github.com/anishwij/aw-turbo/tree/main/packages/meta-cookies","bugs":{"url":"https://github.com/anishwij/aw-turbo/issues"},"dist":{"shasum":"d1b09e0f30eee78d4ed79a3b07b9913195108939","tarball":"https://registry.npmjs.org/@anishwij/meta-cookies/-/meta-cookies-0.1.0.tgz","fileCount":12,"integrity":"sha512-dXf707mbVrs0rKQu1QSUStDrMeDCxWJaqFKfkdrKacry3U6DNaeWhcuaZRLF8UL0oxwNs+/VW9lQoDmEBnElng==","signatures":[{"sig":"MEQCIGV3vvNhLicv7/4UrqEpNFK44Fzfc+6gijispZy1/wmvAiA6Tt10qwH+ORZgT2N5GHJiqgHd+NjS0lCAph6zST6a0Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":22625},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"gitHead":"8d1352801748403fc88a000a7a9caa37341225fd","scripts":{"dev":"tsc --watch","build":"tsc","check-types":"tsc --noEmit","prepublishOnly":"bun run build"},"_npmUser":{"name":"anish-wij","email":"anishwijesinghe@gmail.com"},"repository":{"url":"git+https://github.com/anishwij/aw-turbo.git","type":"git","directory":"packages/meta-cookies"},"_npmVersion":"11.6.0","description":"Lightweight server-side Meta (Facebook) Pixel cookie management for Next.js middleware","directories":{},"_nodeVersion":"24.9.0","_hasShrinkwrap":false,"devDependencies":{"next":"^15.5.0","typescript":"5.9.2","@repo/typescript-config":"*"},"peerDependencies":{"next":">=13.0.0"},"_npmOperationalInternal":{"tmp":"tmp/meta-cookies_0.1.0_1759726749299_0.6394066918467589","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@anishwij/meta-cookies","version":"0.1.2","description":"Lightweight server-side Meta (Facebook) Pixel cookie management for Next.js middleware","author":{"name":"Anish Wijesinghe"},"license":"MIT","homepage":"https://github.com/anishwij/aw-turbo/tree/main/packages/meta-cookies","repository":{"type":"git","url":"git+https://github.com/anishwij/aw-turbo.git","directory":"packages/meta-cookies"},"bugs":{"url":"https://github.com/anishwij/aw-turbo/issues"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"keywords":["meta","facebook","pixel","cookies","fbp","fbc","fbclid","conversions-api","capi","nextjs","middleware","server-side","tracking","attribution"],"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"scripts":{"build":"tsc","dev":"tsc --watch","check-types":"tsc --noEmit","test":"vitest run","test:watch":"vitest --watch","test:ui":"vitest --ui","test:coverage":"vitest run --coverage","prepublishOnly":"bun run build"},"peerDependencies":{"next":">=13.0.0"},"devDependencies":{"@repo/typescript-config":"*","@repo/vitest-config":"*","next":"^15.5.0","typescript":"5.9.2","vitest":"^3.2.4"},"engines":{"node":">=18.0.0"},"_id":"@anishwij/meta-cookies@0.1.2","gitHead":"0053464dc70980094e53c7ebbd6871743f0b6e2d","_nodeVersion":"24.9.0","_npmVersion":"11.6.0","dist":{"integrity":"sha512-Jjl2w9cCyYR7T4iTplDCt8tSSGub3NhtmBmb3Tw+yKAPB9uzDe7AdVEkuuFzu1FuqjesEuhdbsNm96YMykD+pw==","shasum":"ac24f649d9cd751207669cab9628de18a99d120c","tarball":"https://registry.npmjs.org/@anishwij/meta-cookies/-/meta-cookies-0.1.2.tgz","fileCount":33,"unpackedSize":72627,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIA+4yfZbuy0tSLwaQSf1pnRfe7ETlNxcNXA3qU290lvNAiBrDZyXUiD4OvathIZo/3t+GuVOXhU2uovDOZ5J6fpeYA=="}]},"_npmUser":{"name":"anish-wij","email":"anishwijesinghe@gmail.com"},"directories":{},"maintainers":[{"name":"anish-wij","email":"anishwijesinghe@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/meta-cookies_0.1.2_1760109541071_0.5756636126312846"},"_hasShrinkwrap":false}},"time":{"created":"2025-10-06T04:59:09.246Z","modified":"2025-10-10T15:19:01.472Z","0.1.0":"2025-10-06T04:59:09.472Z","0.1.2":"2025-10-10T15:19:01.271Z"},"bugs":{"url":"https://github.com/anishwij/aw-turbo/issues"},"author":{"name":"Anish Wijesinghe"},"license":"MIT","homepage":"https://github.com/anishwij/aw-turbo/tree/main/packages/meta-cookies","keywords":["meta","facebook","pixel","cookies","fbp","fbc","fbclid","conversions-api","capi","nextjs","middleware","server-side","tracking","attribution"],"repository":{"type":"git","url":"git+https://github.com/anishwij/aw-turbo.git","directory":"packages/meta-cookies"},"description":"Lightweight server-side Meta (Facebook) Pixel cookie management for Next.js middleware","maintainers":[{"name":"anish-wij","email":"anishwijesinghe@gmail.com"}],"readme":"# @anishwij/meta-cookies\n\nA lightweight, server-side implementation for handling Meta (Facebook) Pixel cookies (`_fbc` and `_fbp`) in Next.js applications. Fully aligned with Meta's official CAPI Param Builder implementation, ensuring maximum compatibility with the Conversions API and Pixel events.\n\n## Features\n\n### Core Functionality\n- **5-Segment Cookie Format**: Implements Meta's official format with language/version token appendix\n- **Automatic Cookie Management**: Sets/updates `_fbc` when `fbclid` is detected in URL or referrer\n- **Smart FBP Generation**: Creates `_fbp` cookies with unique identifiers following Meta's spec\n- **Legacy Cookie Migration**: Automatically upgrades 4-segment cookies to new 5-segment format\n\n### Advanced Features\n- **Enhanced Domain Resolution**:\n  - IP address support (IPv4/IPv6 with bracketing)\n  - Multi-part TLD handling (e.g., `.co.uk`, `.com.au`)\n  - Custom ETLD+1 resolver interface\n- **Browser Compatibility**: Chrome detection for optimal SameSite attribute handling\n- **Smart Update Logic**: Only updates timestamps when payloads change (not on every request)\n- **Version Tracking**: Encodes package version in cookie appendix for debugging\n- **TypeScript-First**: Full type safety with comprehensive interfaces\n\n## Installation\n\n```bash\nnpm install @anishwij/meta-cookies\n```\n\n```bash\nyarn add @anishwij/meta-cookies\n```\n\n```bash\npnpm add @anishwij/meta-cookies\n```\n\n```bash\nbun add @anishwij/meta-cookies\n```\n\nRequires Next.js >= 13 (for middleware support).\n\n## Usage\n\n### Basic Integration in Next.js Middleware\n\nImport and use in your `middleware.ts` file:\n\n```typescript\nimport { NextRequest, NextResponse } from 'next/server';\nimport { addMetaCookies } from '@anishwij/meta-cookies';\n\nexport function middleware(request: NextRequest) {\n  const response = NextResponse.next();\n  return addMetaCookies(request, response);\n}\n\nexport const config = {\n  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\n};\n```\n\nThis will automatically handle Meta cookies on all routes (excluding static assets).\n\n### With Configuration\n\nPass options to customize behavior:\n\n```typescript\nimport { addMetaCookies } from '@anishwij/meta-cookies';\n\nexport function middleware(request: NextRequest) {\n  const response = NextResponse.next();\n  return addMetaCookies(request, response, {\n    constants: { MAX_AGE: 3600 * 24 * 90 }, // 90 days in seconds\n    isSecure: true, // Force secure flag\n    etldPlus1Resolver: ['example.com', 'subdomain.example.com'], // Custom domain list\n    packageVersion: '0.1.0', // Optional: override version for appendix\n  });\n}\n```\n\n### Standalone Helpers\n\nFor custom logic (e.g., in API routes or server components):\n\n```typescript\nimport { NextRequest } from 'next/server';\nimport { extractFbclid, formatFbc, getRootDomainFromHost } from '@anishwij/meta-cookies';\n\nexport async function GET(request: NextRequest) {\n  const fbclid = extractFbclid(request);\n  if (fbclid) {\n    const domain = getRootDomainFromHost(request.headers.get('host') || '');\n    const subdomainIndex = domain.split('.').length - 1;\n    const fbc = formatFbc(fbclid, subdomainIndex);\n    // Use in your response or Conversions API payload\n  }\n  // ...\n}\n```\n\n### Extracting Cookies Server-Side\n\n```typescript\nimport { getMetaCookies } from '@anishwij/meta-cookies';\n\nconst { fbc, fbp } = getMetaCookies(request);\n// Send to Meta Conversions API\n```\n\n### Preparing Event Params\n\n```typescript\nimport { prepareMetaEventParams } from '@anishwij/meta-cookies';\n\nconst params = prepareMetaEventParams(request);\n// Use in fetch to Meta API: { ...eventData, ...params }\n```\n\n### Deleting Cookies\n\n```typescript\nimport { deleteMetaCookies } from '@anishwij/meta-cookies';\n\ndeleteMetaCookies(response);\n// e.g., in a logout route\n```\n\n### Refreshing Cookies\n\n```typescript\nimport { refreshMetaCookies } from '@anishwij/meta-cookies';\n\nrefreshMetaCookies(request, response);\n// Regenerates if invalid or missing\n```\n\n## API Reference\n\nNote: All modules are re-exported from the package root (e.g., `import { ParsedCookie } from '@anishwij/meta-cookies'`).\n\n### `addMetaCookies(request: NextRequest, response: NextResponse, options?: MetaCookiesOptions): NextResponse`\n\nThe main function to add/update Meta cookies. Mutates and returns the response.\n\n- **Parameters**:\n  - `request`: The incoming Next.js request.\n  - `response`: The Next.js response object (e.g., from `NextResponse.next()`).\n  - `options` (optional): Customization options (see below).\n\n### `MetaCookiesOptions`\n\n```typescript\ninterface MetaCookiesOptions {\n  constants?: Partial<MetaCookieConstants>; // Override defaults like cookie names or max age\n  isSecure?: boolean; // Override protocol-based secure flag\n  etldPlus1Resolver?: ETLDPlus1Resolver | string[]; // Advanced domain resolution\n  packageVersion?: string; // Version for appendix encoding\n}\n```\n\n### `MetaCookieConstants` (Defaults)\n\n```typescript\nconst META_COOKIE_DEFAULTS = {\n  FBC_NAME: '_fbc',\n  FBP_NAME: '_fbp',\n  MAX_AGE: 7776000, // 90 days in seconds\n  PREFIX: 'fb',\n  FBCLID_MAX_LENGTH: 500, // Max length for fbclid param\n} as const;\n\nconst COOKIE_FORMAT = {\n  MIN_SEGMENTS: 4, // Legacy format\n  MAX_SEGMENTS: 5, // New format with appendix\n  DEFAULT_FORMAT: 0x01,\n  LANGUAGE_TOKEN_INDEX: 0x04, // Node.js\n  APPENDIX_LENGTH_V1: 2, // Legacy 2-char tokens\n  APPENDIX_LENGTH_V2: 8, // New 8-char appendix\n} as const;\n```\n\n### Exported Helpers\n\n#### Cookie Management\n- `parseCookie(value: string, prefix?: string): ParsedCookie` - Validates and parses a cookie value (supports 4-5 segments, configurable prefix).\n- `extractFbclid(request: NextRequest, maxLength?: number): string | null` - Gets `fbclid` from URL or referrer.\n- `formatFbc(fbclid: string, subdomainIndex: number, prefix?: string, version?: string): string` - Formats `_fbc` value with appendix.\n- `formatFbp(subdomainIndex: number, prefix?: string, version?: string): string` - Formats `_fbp` value with appendix.\n- `updateCookieWithLanguageToken(cookieValue: string, version?: string): string` - Adds appendix to 4-segment cookies.\n- `preprocessCookie(value: string | null | undefined, subdomainIndex: number, version?: string, prefix?: string): CookieData | null` - Processes and validates existing cookies.\n- `getMetaCookies(request: NextRequest, constants?: MetaCookieConstants): { fbc: string | null; fbp: string | null }` - Extracts and validates Meta cookies.\n- `prepareMetaEventParams(request: NextRequest, constants?: MetaCookieConstants): { fbc?: string; fbp?: string }` - Prepares params for Meta API events.\n- `deleteMetaCookies(response: NextResponse, constants?: MetaCookieConstants): NextResponse` - Deletes Meta cookies.\n- `refreshMetaCookies(request: NextRequest, response: NextResponse, options?: MetaCookiesOptions): NextResponse` - Refreshes/repairs invalid or missing cookies.\n\n#### Domain Resolution\n- `getRootDomainFromHost(host: string): string` - Enhanced root domain extraction with IP and multi-TLD support.\n- `getRootDomainFromHostEnhanced(host: string): string` - Advanced version with extended TLD list.\n- `getSubdomainIndex(domain: string): number` - Calculates the subdomain index.\n- `computeETLDPlus1ForHost(host: string, resolver?: ETLDPlus1Resolver | string[]): { etldPlus1: string; subdomainIndex: number }` - Advanced ETLD+1 computation.\n- `DomainListResolver` - Class implementing ETLDPlus1Resolver interface for custom domain lists.\n- `getSimpleETLDPlus1(hostname: string): string` - Basic ETLD+1 calculation.\n\n#### Utilities\n- `isIPAddress(value: string): boolean` - Detects IPv4 or IPv6 addresses.\n- `isIPv4Address(value: string): boolean` - Validates IPv4 format.\n- `isIPv6Address(value: string): boolean` - Validates IPv6 format.\n- `maybeBracketIPv6(value: string): string` - Adds brackets to IPv6 addresses for cookies.\n- `extractHostFromHttpHost(value: string): string | null` - Extracts hostname from HTTP host header.\n- `isDigit(str: string): boolean` - Checks if string is all digits.\n- `getAppendixInfo(isNew: boolean, version?: string): string` - Generates version appendix for cookies.\n\n#### Browser Detection\n- `detectBrowser(userAgent?: string | null): BrowserInfo` - Analyzes user agent for browser detection.\n- `getSameSiteAttribute(userAgent?: string | null): 'lax' | 'none' | undefined` - Determines appropriate SameSite attribute.\n\n### Types\n\n```typescript\ninterface ParsedCookie {\n  valid: boolean\n  parts: string[]\n  fbclid?: string\n  subdomainIndex?: number\n  timestamp?: number\n  languageToken?: string\n  prefix?: string  // Cookie prefix (e.g., 'fb')\n}\n\ninterface CookieData {\n  creationTime: number\n  payload: string\n  subdomainIndex: number\n  languageToken?: string\n}\n\ninterface BrowserInfo {\n  isChrome: boolean\n  isEdge: boolean\n  isOpera: boolean\n  isCriOS: boolean\n  shouldUseSameSiteLax: boolean\n}\n\ninterface ETLDPlus1Resolver {\n  resolveETLDPlus1(hostname: string): string\n}\n```\n\nSee `src/types.ts` for complete type definitions.\n\n## Cookie Format\n\nMeta cookies follow a specific format:\n\n```\nfb.{subdomain_index}.{timestamp}.{payload}.{appendix}\n```\n\n- **fb**: Fixed prefix\n- **subdomain_index**: Number based on domain depth (e.g., 1 for `.example.com`)\n- **timestamp**: Unix timestamp in milliseconds\n- **payload**: The fbclid value for `_fbc`, random ID for `_fbp`\n- **appendix**: 8-character base64url string encoding version and metadata\n\nExample cookies:\n```\n_fbc=fb.1.1704067200000.IwAR1a2b3c.AQQBAAEA\n_fbp=fb.1.1704067200000.1234567890.AQQAAAEA\n```\n\n## Examples\n\n### Custom Domain Logic\n\nFor advanced TLD handling (e.g., integrating a public suffix library):\n\n```typescript\nimport { addMetaCookies } from '@anishwij/meta-cookies';\nimport psl from 'psl'; // Optional external lib\n\nconst customResolver: ETLDPlus1Resolver = {\n  resolveETLDPlus1: (hostname: string) => {\n    const parsed = psl.parse(hostname);\n    return parsed.domain || hostname;\n  }\n};\n\naddMetaCookies(request, response, { etldPlus1Resolver: customResolver });\n```\n\n### Using Domain List Resolver\n\nFor known domains without external dependencies:\n\n```typescript\nimport { addMetaCookies } from '@anishwij/meta-cookies';\n\nconst knownDomains = ['example.com', 'app.example.com', 'staging.example.com'];\n\nexport function middleware(request: NextRequest) {\n  const response = NextResponse.next();\n  return addMetaCookies(request, response, {\n    etldPlus1Resolver: knownDomains // Uses built-in DomainListResolver\n  });\n}\n```\n\n### IP Address Support\n\nThe package automatically handles IP addresses:\n\n```typescript\n// IPv4: Sets cookies on the IP directly\n// Host: 192.168.1.1 → Cookie domain: 192.168.1.1\n\n// IPv6: Properly brackets for cookie domain\n// Host: ::1 → Cookie domain: [::1]\n```\n\n### Custom Cookie Prefix\n\nFor specialized use cases, you can use a custom prefix instead of 'fb':\n\n```typescript\nimport { parseCookie, formatFbc } from '@anishwij/meta-cookies';\n\n// Parse cookie with custom prefix\nconst parsed = parseCookie('custom.1.1704067200000.abc123.AQQBAAEA', 'custom');\n\n// Format cookies with custom prefix\nconst fbc = formatFbc('abc123', 1, 'custom');\n\n// Use with middleware\naddMetaCookies(request, response, {\n  constants: { PREFIX: 'custom' }\n});\n```\n\n### Testing in Development\n\nRun your Next.js app locally. Check cookies in browser dev tools after visiting a page with `?fbclid=example` in the URL. The cookies will now include the appendix segment.\n\n## Gotchas & Best Practices\n\n- **Cookie Format**: The package now uses Meta's official 5-segment format with appendix. Legacy cookies are auto-migrated.\n- **Production vs. Dev**: Secure flag is auto-detected via protocol (`https:`). Override with `isSecure` if needed.\n- **Domain Sharing**: Cookies are set on the root domain (e.g., `.example.com`) for subdomain access. Test on custom domains.\n- **IP Address Handling**: IPv6 addresses are automatically bracketed. IPv4 addresses work as-is.\n- **Browser Compatibility**: Chrome gets `SameSite=Lax`, other browsers get no SameSite attribute for maximum compatibility.\n- **Update Logic**: Timestamps only update when payloads change, reducing unnecessary cookie writes.\n- **Meta Compatibility**: Fully aligned with Meta's CAPI Param Builder for perfect Conversions API integration.\n- **Ad Blockers**: Server-side setting helps bypass blockers, but client-side Pixel may still be needed for full functionality.\n- **Privacy**: Ensure compliance with GDPR/CCPA; these cookies track user behavior.\n\n\n## Acknowledgments\n\nThis package is a TypeScript implementation fully aligned with Meta's official [CAPI Param Builder](https://github.com/facebook/capi-param-builder), implementing the exact cookie format and validation logic from Meta's reference implementation. It follows the specifications in Meta's [Conversions API documentation](https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/fbp-and-fbc/) including the 5-segment format with appendix tokens.\n\nThis is an independent implementation designed specifically for Next.js middleware, providing a clean, type-safe API while maintaining complete compatibility with Meta's standards. Not affiliated with or endorsed by Meta Platforms, Inc.\n\n## License\n\nMIT License. See [LICENSE](./LICENSE) for details.\n","readmeFilename":"README.md"}