{"_id":"@affirmfirst/react","name":"@affirmfirst/react","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@affirmfirst/react","version":"1.0.0","description":"React components for aFFirmFirst secure image delivery. Drop-in replacement for <img> tags with encrypted canvas rendering, watermarking, and right-click protection.","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js","types":"./dist/index.d.ts"},"./next":{"import":"./dist/next.mjs","require":"./dist/next.js","types":"./dist/next.d.ts"}},"scripts":{"build":"tsup","dev":"tsup --watch","lint":"tsc --noEmit","clean":"rimraf dist"},"peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0"},"devDependencies":{"@types/react":"^18.2.0","@types/react-dom":"^18.2.0","react":"^18.2.0","react-dom":"^18.2.0","tsup":"^8.0.0","typescript":"^5.3.0","rimraf":"^5.0.0"},"keywords":["react","image-protection","secure-image","canvas-rendering","watermark","affirmfirst","next.js","encrypted-streaming"],"author":{"name":"aFFirmFirst","email":"enquiries@affirmfirst.com"},"license":"MIT","homepage":"https://affirmfirst.com","repository":{"type":"git","url":"git+https://github.com/affirmfirst/react-sdk.git"},"gitHead":"68bbb54b0ff7b469e4b2819362ba45d92cbb25b7","_id":"@affirmfirst/react@1.0.0","bugs":{"url":"https://github.com/affirmfirst/react-sdk/issues"},"_nodeVersion":"24.11.1","_npmVersion":"11.7.0","dist":{"integrity":"sha512-1UN58d4qeMTEeNaKzZaFV7tv4WaT1jYc2pGpoonmebJ59EnJTvmd12Y0zyK9tvLdYlIVpq1lZ/O3T9QWM2JACg==","shasum":"be97176eceaf487556ec933cccae4cd9f61c8d6a","tarball":"https://registry.npmjs.org/@affirmfirst/react/-/react-1.0.0.tgz","fileCount":14,"unpackedSize":58924,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBKv2s+1Lq0p5WGCIlLT6avXWyNjZGqGIbVZ+UQJAZlqAiEA10svuyiXpnmjz42kg24AMQdEx+H/85C7cpI8xo0vFBU="}]},"_npmUser":{"name":"prosolutionx","email":"theprosolutionx@gmail.com"},"directories":{},"maintainers":[{"name":"prosolutionx","email":"theprosolutionx@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react_1.0.0_1773117384041_0.2658372590667333"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-10T04:36:23.885Z","1.0.0":"2026-03-10T04:36:24.177Z","modified":"2026-03-10T04:36:24.457Z"},"maintainers":[{"name":"prosolutionx","email":"theprosolutionx@gmail.com"}],"description":"React components for aFFirmFirst secure image delivery. Drop-in replacement for <img> tags with encrypted canvas rendering, watermarking, and right-click protection.","homepage":"https://affirmfirst.com","keywords":["react","image-protection","secure-image","canvas-rendering","watermark","affirmfirst","next.js","encrypted-streaming"],"repository":{"type":"git","url":"git+https://github.com/affirmfirst/react-sdk.git"},"author":{"name":"aFFirmFirst","email":"enquiries@affirmfirst.com"},"bugs":{"url":"https://github.com/affirmfirst/react-sdk/issues"},"license":"MIT","readme":"# @affirmfirst/react\n\nReact components for **aFFirmFirst** secure image delivery. Drop-in replacement for `<img>` tags with encrypted canvas rendering, watermarking, and right-click protection.\n\n## Installation\n\n```bash\nnpm install @affirmfirst/react\n```\n\n## Quick Start\n\n### 1. Wrap your app with `<AffirmProvider>`\n\n```tsx\n// app/layout.tsx (Next.js) or App.tsx (React)\nimport { AffirmProvider } from '@affirmfirst/react';\n\nexport default function Layout({ children }) {\n  return (\n    <AffirmProvider>\n      {children}\n    </AffirmProvider>\n  );\n}\n```\n\n### 2. Use `<SecureImage>` anywhere\n\n```tsx\nimport { SecureImage } from '@affirmfirst/react';\n\nexport function ProductImage({ imageId }) {\n  return (\n    <SecureImage\n      imageId={imageId}\n      width={600}\n      height={400}\n      alt=\"Product photo\"\n    />\n  );\n}\n```\n\nThat's it. The image is rendered via encrypted canvas — no `<img>` tag, no downloadable URL.\n\n## Components\n\n### `<AffirmProvider>`\n\nAuto-loads the aFFirmFirst SDK script. Place in your root layout. No configuration needed.\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `autoLoad` | `boolean` | `true` | Auto-inject the SDK `<script>` tag |\n\n### `<SecureImage>`\n\nRenders a single protected image.\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `imageId` | `string` | **required** | aFFirmFirst vault image ID |\n| `width` | `string \\| number` | `'100%'` | Container width |\n| `height` | `string \\| number` | `'auto'` | Container height |\n| `lazy` | `boolean` | `true` | Lazy load via IntersectionObserver |\n| `className` | `string` | — | CSS class for wrapper |\n| `style` | `CSSProperties` | — | Inline styles for wrapper |\n| `alt` | `string` | — | Accessibility label |\n| `onLoad` | `() => void` | — | Called when image loads |\n| `onError` | `(error: string) => void` | — | Called on load error |\n\n### `<SecureGallery>`\n\nRenders a grid or masonry layout of protected images.\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `imageIds` | `string[]` | **required** | Array of vault image IDs |\n| `layout` | `'grid' \\| 'masonry'` | `'grid'` | Layout mode |\n| `columns` | `number` | `3` | Number of columns |\n| `gap` | `number` | `8` | Gap between images (px) |\n| `imageHeight` | `string` | `'250px'` | Image height (grid mode) |\n| `className` | `string` | — | CSS class |\n| `style` | `CSSProperties` | — | Inline styles |\n\n## Hooks\n\n### `useAffirmFirst()`\n\nAccess the SDK instance for programmatic control.\n\n```tsx\nimport { useAffirmFirst } from '@affirmfirst/react';\n\nfunction MyComponent() {\n  const { ready, sdk } = useAffirmFirst();\n\n  const handleEmbed = () => {\n    if (ready && sdk) {\n      sdk.embed({\n        imageId: 'clx123abc',\n        container: document.getElementById('my-container'),\n        width: '100%',\n        height: 'auto',\n      });\n    }\n  };\n\n  return <div id=\"my-container\" />;\n}\n```\n\n## Next.js Utilities\n\nImport from `@affirmfirst/react/next`:\n\n### `affirmImageLoader`\n\nCustom image loader for `next/image`. Uses thumbnail URLs for standard `<Image>` rendering (previews, admin panels). For maximum protection, use `<SecureImage>` instead.\n\n```js\n// lib/affirm-loader.js\nimport { affirmImageLoader } from '@affirmfirst/react/next';\nexport default affirmImageLoader;\n```\n\n```js\n// next.config.js\nmodule.exports = {\n  images: {\n    loader: 'custom',\n    loaderFile: './lib/affirm-loader.js',\n  },\n};\n```\n\n### `SDK_SCRIPT_URL`\n\nPre-configured SDK script URL. Use with Next.js `<Script>` if you prefer manual loading over `<AffirmProvider>`.\n\n```tsx\nimport Script from 'next/script';\nimport { SDK_SCRIPT_URL } from '@affirmfirst/react/next';\n\n<Script src={SDK_SCRIPT_URL} strategy=\"afterInteractive\" />\n```\n\n## Usage Examples\n\n### Next.js Ecommerce — Product Page\n\n```tsx\nimport { SecureImage } from '@affirmfirst/react';\n\nexport default function ProductPage({ product }) {\n  return (\n    <div className=\"grid grid-cols-2 gap-8\">\n      <SecureImage\n        imageId={product.affirmImageId}\n        width=\"100%\"\n        height={500}\n        alt={product.name}\n      />\n      <div>\n        <h1>{product.name}</h1>\n        <p>${product.price}</p>\n      </div>\n    </div>\n  );\n}\n\n// SEO: Use signed URL for og:image (crawlers can't run JS)\nexport async function generateMetadata({ params }) {\n  const product = await getProduct(params.id);\n  return {\n    openGraph: {\n      images: [product.signedImageUrl],\n    },\n  };\n}\n```\n\n### Product Gallery\n\n```tsx\nimport { SecureGallery } from '@affirmfirst/react';\n\nexport default function ProductGallery({ imageIds }) {\n  return (\n    <SecureGallery\n      imageIds={imageIds}\n      layout=\"grid\"\n      columns={4}\n      gap={4}\n      imageHeight=\"200px\"\n    />\n  );\n}\n```\n\n### Portfolio Masonry\n\n```tsx\nimport { SecureGallery } from '@affirmfirst/react';\n\nexport default function Portfolio({ imageIds }) {\n  return (\n    <SecureGallery\n      imageIds={imageIds}\n      layout=\"masonry\"\n      columns={3}\n      gap={8}\n    />\n  );\n}\n```\n\n## How It Works\n\n1. `<AffirmProvider>` loads the SDK script (`/sdk/affirm.js`) into the page\n2. `<SecureImage>` creates a container div and calls `AffirmFirst.embed()`\n3. The SDK fetches the image via encrypted streaming (`/stream/secure/:imageId`)\n4. A Web Worker decrypts the AES-256-CBC encrypted bytes in memory\n5. The decrypted image is painted onto a `<canvas>` element — no `<img>` tag exists\n6. Watermark overlay, right-click blocking, and screen capture detection are applied\n\n**Security features active on every image:**\n- AES-256-CBC encryption in transit\n- Canvas rendering (no downloadable URL)\n- Imperceptible pixel noise (anti-scraping)\n- Right-click and drag protection\n- Watermark overlay\n- Screen capture detection\n- Domain locking (referer validation)\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-f083c15f2cd537dbae939f4016c2a061"}