{"_id":"@artymclabin/qa-review","_rev":"6-0d292385efb46ca651b0486cf3301724","name":"@artymclabin/qa-review","dist-tags":{"latest":"0.3.12"},"versions":{"0.3.7":{"name":"@artymclabin/qa-review","version":"0.3.7","license":"MIT","_id":"@artymclabin/qa-review@0.3.7","maintainers":[{"name":"artymclabin","email":"npm@artymclabin.com"}],"homepage":"https://github.com/ArtyMcLabin/qa-review#readme","bugs":{"url":"https://github.com/ArtyMcLabin/qa-review/issues"},"dist":{"shasum":"28b8fa1b56a9df1e370f704007b0fd256cdc751a","tarball":"https://registry.npmjs.org/@artymclabin/qa-review/-/qa-review-0.3.7.tgz","fileCount":34,"integrity":"sha512-OI1QHZyYcfng+FyHkfn54/Cv6E59b9HXTPoNRc7z0U9Ddq6uwg7h5c0VPNwu51W5/RKRIXTmgENTZwZo4LxCxQ==","signatures":[{"sig":"MEUCIQD1HyScG0ZdVjasExIkUNF531c5ooH+sh4xMozvxwRcVwIgSykoJLgLF8Buf2RQpvQHskktVqXpCe2kRwJNcXbBSQE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":168189},"main":"./dist/client/index.js","type":"module","types":"./dist/client/index.d.ts","exports":{".":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"89e5c88d734cbdd3694c27735010a87d474ead31","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","prepare":"npm run build"},"_npmUser":{"name":"artymclabin","email":"npm@artymclabin.com"},"repository":{"url":"git+https://github.com/ArtyMcLabin/qa-review.git","type":"git"},"_npmVersion":"10.9.2","description":"Interactive on-page QA review overlay (spotlight walkthrough, approve/reject verdict ledger) with a framework-agnostic server handler factory and a self-provisioning Postgres store.","directories":{},"sideEffects":false,"_nodeVersion":"22.17.0","dependencies":{"postgres":"^3.4.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26","react":"^19.2.0","vitest":"^3","react-dom":"^19.2.0","typescript":"^5","@types/node":"^22","@types/react":"^19","lucide-react":"^1.25.0","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18","lucide-react":">=0.400.0"},"_npmOperationalInternal":{"tmp":"tmp/qa-review_0.3.7_1785713685345_0.8097864766309149","host":"s3://npm-registry-packages-npm-production"}},"0.3.8":{"name":"@artymclabin/qa-review","version":"0.3.8","license":"MIT","_id":"@artymclabin/qa-review@0.3.8","maintainers":[{"name":"artymclabin","email":"npm@artymclabin.com"}],"homepage":"https://github.com/ArtyMcLabin/qa-review#readme","bugs":{"url":"https://github.com/ArtyMcLabin/qa-review/issues"},"dist":{"shasum":"6f2465d913adc7a74a6fd57da44d487e4ecd3f57","tarball":"https://registry.npmjs.org/@artymclabin/qa-review/-/qa-review-0.3.8.tgz","fileCount":34,"integrity":"sha512-mkCZtN8IEmRdTSS0OC2P5jdqOBuPYLNmJH/7nZbKfE25Mqru06zBaCzn/u43um9/ZDuXTVwQThtYYWHXQZKzjQ==","signatures":[{"sig":"MEUCIHyXpAuD914l+S0yk7Q9VjquBrE1DQtBHUU30RFGqoQVAiEA2w05sKYuQBpLvMXNugckboCj8oWwLY8HhTjZvF5HW2I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@artymclabin%2fqa-review@0.3.8","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":175019},"main":"./dist/client/index.js","type":"module","types":"./dist/client/index.d.ts","exports":{".":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"6f28ae5afb8dfb92668f31f1f08621bcb365f265","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","prepare":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8af9b772-c7b2-4cf7-9e81-7c7a7481a34a"}},"repository":{"url":"git+https://github.com/ArtyMcLabin/qa-review.git","type":"git"},"_npmVersion":"12.0.2","description":"Interactive on-page QA review overlay (spotlight walkthrough, approve/reject verdict ledger) with a framework-agnostic server handler factory and a self-provisioning Postgres store.","directories":{},"sideEffects":false,"_nodeVersion":"22.23.1","dependencies":{"postgres":"^3.4.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26","react":"^19.2.0","vitest":"^3","react-dom":"^19.2.0","typescript":"^5","@types/node":"^22","@types/react":"^19","lucide-react":"^1.25.0","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18","lucide-react":">=0.400.0"},"_npmOperationalInternal":{"tmp":"tmp/qa-review_0.3.8_1786526317209_0.6320743681008947","host":"s3://npm-registry-packages-npm-production"}},"0.3.9":{"name":"@artymclabin/qa-review","version":"0.3.9","license":"MIT","_id":"@artymclabin/qa-review@0.3.9","maintainers":[{"name":"artymclabin","email":"npm@artymclabin.com"}],"homepage":"https://github.com/ArtyMcLabin/qa-review#readme","bugs":{"url":"https://github.com/ArtyMcLabin/qa-review/issues"},"dist":{"shasum":"ad4258681407cde11dbdfc87d69fa4df1a95d21b","tarball":"https://registry.npmjs.org/@artymclabin/qa-review/-/qa-review-0.3.9.tgz","fileCount":34,"integrity":"sha512-8r+7DoyePE37j37cvwXGReS0s1Fimsj6idrqN8/rsXcsHQxH3axc59z1bU88M2GuKxrIrxBCb7flAeQRW6A0lg==","signatures":[{"sig":"MEYCIQDsWZYjiusoAa9v0m6OFWklcyo78izw82uyzyx9zNlHlwIhANxG9aoP/poxH8ebKWM/7hd7LEc6mTBpLqnKlkBUmnX1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQCTG3V/Lu0AKDA8sBRmYVVK24DvuBp0V7OSqxk8Qg2JcgIhAOpohk5Ix4UNfBUgH9YTUIB8+5uPZm8LT2YH8JNsU7H1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@artymclabin%2fqa-review@0.3.9","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":178219},"main":"./dist/client/index.js","type":"module","types":"./dist/client/index.d.ts","exports":{".":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"96c2a0753e7cfd84ca8fae36ff0f43d778e05308","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","prepare":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8af9b772-c7b2-4cf7-9e81-7c7a7481a34a"}},"repository":{"url":"git+https://github.com/ArtyMcLabin/qa-review.git","type":"git"},"_npmVersion":"12.0.2","description":"Interactive on-page QA review overlay (spotlight walkthrough, approve/reject verdict ledger) with a framework-agnostic server handler factory and a self-provisioning Postgres store.","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"postgres":"^3.4.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26","react":"^19.2.0","vitest":"^3","react-dom":"^19.2.0","typescript":"^5","@types/node":"^22","@types/react":"^19","lucide-react":"^1.25.0","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18","lucide-react":">=0.400.0"},"_npmOperationalInternal":{"tmp":"tmp/qa-review_0.3.9_1789316877091_0.18332432993646974","host":"s3://npm-registry-packages-npm-production"}},"0.3.10":{"name":"@artymclabin/qa-review","version":"0.3.10","license":"MIT","_id":"@artymclabin/qa-review@0.3.10","maintainers":[{"name":"artymclabin","email":"npm@artymclabin.com"}],"homepage":"https://github.com/ArtyMcLabin/qa-review#readme","bugs":{"url":"https://github.com/ArtyMcLabin/qa-review/issues"},"dist":{"shasum":"c5c723fca15acf9a0bc63d9d006c6c99a04f695c","tarball":"https://registry.npmjs.org/@artymclabin/qa-review/-/qa-review-0.3.10.tgz","fileCount":34,"integrity":"sha512-KwMNBY9WT75ZvIBjVDVubR/xqqv1cCGVZs4Uw8UWSxxoFb7uWACUbTG/xWo2HaQMbMU32KLsRIM65S6wtXPf4Q==","signatures":[{"sig":"MEQCIGTYe9Qn7zq2iAcaKCjOr8iOu19mapVL6oGGKPfFjsBnAiBIdq7MXLXseWQsLMKkTdvPHKaukylNei37diMFut8h0Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIFOn1a7KsAUBkQ9of4OLRCDQVvb2ctPj5oglQlQxUrq/AiBhV0+mHKwrjaP7/RFvJJeaAwjSp4MslxmA99H7ZsdNpw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@artymclabin%2fqa-review@0.3.10","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":180907},"main":"./dist/client/index.js","type":"module","types":"./dist/client/index.d.ts","exports":{".":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"f537e8ba7e881a5fac3d05754f717e9bcc111012","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","prepare":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8af9b772-c7b2-4cf7-9e81-7c7a7481a34a"}},"repository":{"url":"git+https://github.com/ArtyMcLabin/qa-review.git","type":"git"},"_npmVersion":"12.0.2","description":"Interactive on-page QA review overlay (spotlight walkthrough, approve/reject verdict ledger) with a framework-agnostic server handler factory and a self-provisioning Postgres store.","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"postgres":"^3.4.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26","react":"^19.2.0","vitest":"^3","react-dom":"^19.2.0","typescript":"^5","@types/node":"^22","@types/react":"^19","lucide-react":"^1.25.0","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18","lucide-react":">=0.400.0"},"_npmOperationalInternal":{"tmp":"tmp/qa-review_0.3.10_1789764433494_0.14358986040297483","host":"s3://npm-registry-packages-npm-production"}},"0.3.11":{"name":"@artymclabin/qa-review","version":"0.3.11","license":"MIT","_id":"@artymclabin/qa-review@0.3.11","maintainers":[{"name":"artymclabin","email":"npm@artymclabin.com"}],"homepage":"https://github.com/ArtyMcLabin/qa-review#readme","bugs":{"url":"https://github.com/ArtyMcLabin/qa-review/issues"},"dist":{"shasum":"e170bf601ffb426eed085b67f57c91c0df60849c","tarball":"https://registry.npmjs.org/@artymclabin/qa-review/-/qa-review-0.3.11.tgz","fileCount":34,"integrity":"sha512-Q5EJNaYYVIFWcBmOI+rOksBCIn3oV6s514z6qc8bQF7Twk7CFPM0gxDU8G9oFjTGgbubiZdjYz5VY4KWfosLXg==","signatures":[{"sig":"MEUCIGtOxehcR80FhAKMfSC6uMAdW5QxJIJS7b2G9QEsU3EyAiEA3N7wCUwpsyF8rKqqK89J5W9GH3i+fbLWumUhiZOCmIg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIA+KxbE3Bdvw9XNC2di8X0DvHLkYJRe+Omgn5drJPoxnAiEA/a4S29tqkfgGPUQn+81Tf6oTMQtJf7oh6K58tYI3yk4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@artymclabin%2fqa-review@0.3.11","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":181111},"main":"./dist/client/index.js","type":"module","types":"./dist/client/index.d.ts","exports":{".":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"2b9f64b4be79cdcb43ad92c14557b4a66a0d5d0d","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","prepare":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8af9b772-c7b2-4cf7-9e81-7c7a7481a34a"}},"repository":{"url":"git+https://github.com/ArtyMcLabin/qa-review.git","type":"git"},"_npmVersion":"12.1.0","description":"Interactive on-page QA review overlay (spotlight walkthrough, approve/reject verdict ledger) with a framework-agnostic server handler factory and a self-provisioning Postgres store.","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"postgres":"^3.4.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26","react":"^19.2.0","vitest":"^3","react-dom":"^19.2.0","typescript":"^5","@types/node":"^22","@types/react":"^19","lucide-react":"^1.25.0","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18","lucide-react":">=0.400.0"},"_npmOperationalInternal":{"tmp":"tmp/qa-review_0.3.11_1790204700399_0.16947746972089184","host":"s3://npm-registry-packages-npm-production"}},"0.3.12":{"_id":"@artymclabin/qa-review@0.3.12","bugs":{"url":"https://github.com/ArtyMcLabin/qa-review/issues"},"dist":{"shasum":"c1dbc7f2224b654e911c260c360235022dec1ab3","tarball":"https://registry.npmjs.org/@artymclabin/qa-review/-/qa-review-0.3.12.tgz","fileCount":34,"integrity":"sha512-mTZ8l4NEBEQ9CO4b5hkpSQ34w+ru/2HvP569aa2l2RhZw/NwbVUFaVPl6L6GXprJaMS+yD7TuXBxF8lZ9+jjTA==","signatures":[{"sig":"MEYCIQClAmuxrnAiO/Lf+4KPutvhoDEY/EvnEWYxZMTsI/8UEgIhANzVR7DwHPRbtNREvOgxZmRd+GpDjJrU/gCUjY2nLwNI","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCBzl4A6tDXrP61Zp6SALOGB0qLBWGG1iBDH+FijPVjNwIhAIYI7twj4ylzNAT3TazkJW6LxlR3txNHhapxns9ARx3V"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@artymclabin%2fqa-review@0.3.12","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":183185},"main":"./dist/client/index.js","name":"@artymclabin/qa-review","type":"module","types":"./dist/client/index.d.ts","exports":{".":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"gitHead":"762b7240f7753a4da5e0b9f62402609cea820a9b","license":"MIT","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","prepare":"npm run build"},"version":"0.3.12","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8af9b772-c7b2-4cf7-9e81-7c7a7481a34a"}},"homepage":"https://github.com/ArtyMcLabin/qa-review#readme","repository":{"url":"git+https://github.com/ArtyMcLabin/qa-review.git","type":"git"},"_npmVersion":"12.1.0","description":"Interactive on-page QA review overlay (spotlight walkthrough, approve/reject verdict ledger) with a framework-agnostic server handler factory and a self-provisioning Postgres store.","directories":{},"maintainers":[{"name":"artymclabin","email":"npm@artymclabin.com"}],"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"postgres":"^3.4.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26","react":"^19.2.0","vitest":"^3","react-dom":"^19.2.0","typescript":"^5","@types/node":"^22","@types/react":"^19","lucide-react":"^1.25.0","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18","lucide-react":">=0.400.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/qa-review_0.3.12_1790206375926_0.09265345132696412"}}},"time":{"created":"2026-08-02T23:34:45.197Z","modified":"2026-09-23T23:32:56.347Z","0.3.7":"2026-08-02T23:34:45.492Z","0.3.8":"2026-08-12T09:18:37.369Z","0.3.9":"2026-09-13T16:27:57.180Z","0.3.10":"2026-09-18T20:47:13.573Z","0.3.11":"2026-09-23T23:05:00.487Z","0.3.12":"2026-09-23T23:32:56.032Z"},"bugs":{"url":"https://github.com/ArtyMcLabin/qa-review/issues"},"license":"MIT","homepage":"https://github.com/ArtyMcLabin/qa-review#readme","repository":{"url":"git+https://github.com/ArtyMcLabin/qa-review.git","type":"git"},"description":"Interactive on-page QA review overlay (spotlight walkthrough, approve/reject verdict ledger) with a framework-agnostic server handler factory and a self-provisioning Postgres store.","maintainers":[{"name":"artymclabin","email":"npm@artymclabin.com"}],"readme":"# qa-review\n\nInteractive on-page QA review for React apps: a spotlight walkthrough overlay\nthat steps a reviewer through the elements of a real rendered page\n(approve / reject / note / undo / design-variation picking), plus a durable\nserver-side verdict ledger with a self-provisioning Postgres store.\n\nBuilt for \"founder reviews the page element by element\" workflows: the reviewer\nopens the live page, the overlay dims everything except the current item, and\nevery verdict is persisted immediately - refreshes, storage wipes, and device\nswitches never lose progress.\n\n## Features\n\n- **Spotlight walkthrough** - dims the page except the current `[data-qa]`\n  target; card shows title/subtitle + Approve/Reject/Prev/Next.\n- **Round-based review** - the set of actionable (not-yet-approved) items is\n  frozen at load with a fixed denominator (\"1 of N\"), so approved items never\n  re-appear mid-round.\n- **Durable verdict ledger** - verdicts are saved per item as you go\n  (localStorage cache + server ledger). No bulk reset exists; a single item is\n  re-queued by invalidating just it (`verdict: null`).\n- **Undo, Peek (3s clean-page view), element-picker notes, keyboard driving**\n  (A/R/arrows/U/Esc), draggable panel.\n- **Design variations** - an item can expose N live-swappable variants; the\n  chosen variant is recorded with the approval.\n- **Session snapshots** - optional \"Save review to database\" submit that stores\n  the full run (summary + per-item results) for auditability.\n- **BYO auth** - the server handlers take an `authorize(req)` callback; plug in\n  any gate (SSO, password, none for local tools). Fail-closed.\n- **Self-provisioning Postgres storage** - the adapter creates its own\n  `qa_review_*` tables on first use (versioned, advisory-locked). A `site`\n  scope column lets one database serve many installs.\n- **Cross-page journey** - an ordered multi-page review: finishing a page\n  navigates immediately to the next page with pending items (activation query\n  params survive the hop); a completion panel shows only when the whole\n  journey is clean.\n- **Task items** - selectorless items render as a centered card with an\n  optional action-link button, for visit-this-page checks and decisions.\n- **NOT-ALTERED poka-yoke** - every verdict stores a content fingerprint; a\n  re-shown rejected item whose content still hashes identical gets a\n  system-computed \"NOT ALTERED since your rejection\" badge.\n- **Device-split approvals** - items can require per-device sign-off\n  (PC/mobile); approved only when every required device approved. Plain\n  historical approvals are grandfathered as fully approved.\n- **Sub-highlights** - `highlightWords` marks specific words inside the\n  spotlighted element, replacing \"where to look\" prose.\n- **Codenames** - a deterministic two-word codename per item (\"red-apple\")\n  with a Copy-ref button and an exported resolver, so humans and agents can\n  reference items by name. Included in state GET responses.\n- **Minimize bubble** - the panel collapses to a draggable floating bubble\n  (mouse + touch); tap to restore.\n- **No CSS toolchain required** - the overlay injects its own stylesheet;\n  brand colors come from a small theme prop.\n\n## Install\n\n```bash\nnpm install @artymclabin/qa-review\n# or\npnpm add @artymclabin/qa-review\n```\n\nInstall straight from git if you want an unreleased commit:\n\n```bash\nnpm install github:ArtyMcLabin/qa-review\n```\n\nFor CI environments without registry access, vendor a tarball:\n\n```bash\n# in this repo\nnpm pack   # -> artymclabin-qa-review-<version>.tgz\n# in the consumer\npnpm add ./vendor/artymclabin-qa-review-<version>.tgz\n```\n\nPeer dependencies: `react`, `react-dom`, `lucide-react`.\n\n## Quick start (Next.js App Router)\n\n### 1. Server: mount the handlers\n\n```ts\n// src/lib/qa-review.ts\nimport { createQAReviewHandlers } from \"@artymclabin/qa-review/server\";\n\nexport const qaHandlers = createQAReviewHandlers({\n  site: \"example-site\", // scope for this install (one DB can serve many)\n  authorize: async (req) => {\n    const user = await verifyMySession(req); // your gate; null -> 401\n    return user ? { reviewer: user.name, displayName: user.name } : null;\n  },\n});\n```\n\n```ts\n// src/app/api/qa/state/route.ts\nimport { qaHandlers } from \"@/lib/qa-review\";\nexport const runtime = \"nodejs\";\nexport const dynamic = \"force-dynamic\";\nexport const GET = qaHandlers.stateGET;\nexport const POST = qaHandlers.statePOST;\n```\n\nMount `submitPOST`, `sessionsGET`, and `accessGET` the same way on their own\nroutes as needed.\n\n### 2. Database\n\nSet the connection string (any Postgres - local, Neon, Supabase, RDS):\n\n```bash\nQA_REVIEW_DATABASE_URL=postgresql://user:password@db.example.com/mydb\n```\n\nNo migrations to write: on the first request the adapter provisions\n`qa_review_state` (the per-item ledger), `qa_review_sessions` (session\nsnapshots), and `qa_review_migrations` (its own version bookkeeping).\n\n### 3. Client: mark targets and mount the overlay\n\n```tsx\n// Any element you want reviewed gets a stable [data-qa] anchor:\n<section data-qa=\"hero\">...</section>\n```\n\n```tsx\n\"use client\";\nimport { QAReviewOverlay } from \"@artymclabin/qa-review\";\n\nconst ITEMS = [\n  { id: \"hero\", title: \"Hero headline\", selector: '[data-qa=\"hero\"]' },\n  { id: \"pricing\", title: \"Pricing table\", sub: \"Check the currency.\", selector: '[data-qa=\"pricing\"]' },\n];\n\nexport function PageQA() {\n  return (\n    <QAReviewOverlay\n      items={ITEMS}\n      target=\"example-site:/pricing\" // one ledger bucket per reviewed surface\n      gateParam=\"qaReview\"           // omit if activation is gated upstream\n      submitUrl=\"/api/qa/submit\"     // omit to hide the session-save button\n      theme={{ accent: \"#ffde4d\" }}\n    />\n  );\n}\n```\n\n## API sketch\n\n```ts\n// client\nimport {\n  QAReviewOverlay, // the overlay component\n  createQAStore,   // per-target verdict store (localStorage + server mirror)\n  targetFromLocation, // ?target= override helper (E2E isolation)\n} from \"@artymclabin/qa-review\";\n\n// server\nimport {\n  createQAReviewHandlers, // { stateGET, statePOST, submitPOST, sessionsGET, accessGET }\n  createPostgresStorage,  // default storage; implement QAReviewStorage to swap\n} from \"@artymclabin/qa-review/server\";\n```\n\nState endpoint semantics (the ledger):\n\n- `GET ?target=...` -> `{ ok, verdicts: { itemId: { verdict, note, variant } } }`\n- `POST { target, itemId, verdict | note | variant }` -> merge-upsert one item\n- `POST { target, itemId, verdict: null }` -> delete that one item\n  (re-queues it on the next round). There is deliberately **no reset-all**.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}