Files
2026-05-27 23:29:53 +01:00

105 lines
6.5 KiB
Markdown

# Tasks — `@suaveplan/react-auth` `usePasskeyAutofill`
> Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes.
## 0. Blocking prerequisite
- [x] 0.1 `@suaveplan/webauthn` exports `authenticate({ mediation: "conditional" })` and `isConditionalMediationAvailable()` — verified at `packages/auth/webauthn/src/authenticate/authenticate.ts` and `packages/auth/webauthn/src/index.ts`.
## 1. Spec authoring
- [x] 1.1 Read `openspec/archive/2026-05-11-enterprise-webauthn/proposal.md` + `tasks.md` to confirm the `usePasskeyAutofill` deferral
- [x] 1.2 Read `openspec/AGENTS.md` §13 and §14
- [x] 1.3 Draft `proposal.md` from `openspec/templates/proposal.md`
- [x] 1.4 Draft `design.md` covering hook signature, lifecycle, SSR safety, error translation
- [x] 1.5 Draft `specs/react-auth/spec.md` from `openspec/templates/spec-delta.md`
- [x] 1.6 Every requirement in the spec delta carries a `**Verified by:**` line naming concrete `it(...)` titles
## 2. Package scaffold
- [ ] 2.1 Create `packages/auth/react-auth/` with `package.json` (browser tier, React 19 peer, `@suaveplan/webauthn workspace:^`, `@suaveplan/error workspace:^`, `@suaveplan/types workspace:^`, `zod catalog:forms`, `tslib catalog:build`, `@vitest/coverage-istanbul catalog:testing`)
- [ ] 2.2 `tsconfig.json` extending `@suaveplan/typescript-config/browser`
- [ ] 2.3 `vitest.config.ts` via `createTestConfig` with jsdom environment and 100/100/100/100 thresholds
- [ ] 2.4 `package.json` `exports` map covering `.`, `./use-passkey-autofill/use-passkey-autofill`, `./passkey-autofill-field/passkey-autofill-field`, `./errors`
- [ ] 2.5 `"sideEffects": false`, `"browser"` field set
## 3. Implementation
### 3.1 Errors
- [ ] 3.1.1 `src/errors/errors.ts` — `defineCodes("REACT_AUTH", ["AUTOFILL_UNAVAILABLE", "AUTOFILL_FAILED", "ABORTED", "SSR_NO_OP", "INVALID_OPTIONS"])`
### 3.2 Utilities
- [ ] 3.2.1 `src/utils/compose-signals.ts` — composes N `AbortSignal`s into one (any aborts → composed aborts); handles already-aborted signals
### 3.3 Hook
- [ ] 3.3.1 `src/use-passkey-autofill/use-passkey-autofill.types.ts` — named `UsePasskeyAutofillOptions`, `UsePasskeyAutofillReturn` interfaces
- [ ] 3.3.2 `src/use-passkey-autofill/use-passkey-autofill.schemas.ts` — `UsePasskeyAutofillOptionsSchema` Zod schema; throws `REACT_AUTH.INVALID_OPTIONS` on parse failure
- [ ] 3.3.3 `src/use-passkey-autofill/use-passkey-autofill.ts` — hook body: SSR no-op, feature-detect, dispatch, AbortController ownership, stable `abort` callback, error translation
- [ ] 3.3.4 Effect re-runs on option-identity change; old controller aborts cleanly
### 3.4 Component wrapper
- [ ] 3.4.1 `src/passkey-autofill-field/passkey-autofill-field.types.ts` — named `PasskeyAutofillFieldProps`
- [ ] 3.4.2 `src/passkey-autofill-field/passkey-autofill-field.tsx` — wraps hook + `<input autocomplete="username webauthn">`; forwards `onBlur` → `abort()`
### 3.5 Barrels
- [ ] 3.5.1 `src/index.ts` — explicit named re-exports (no `export *`)
- [ ] 3.5.2 Per-feature `index.ts` files — explicit named re-exports
## 4. Tests (runner-neutral imports — `@suaveplan/testing/runner`)
- [ ] 4.1 `src/use-passkey-autofill/use-passkey-autofill.test.tsx` — happy path, SSR no-op, feature-unavailable, abort on unmount, abort on caller signal, abort via returned callback, option-identity change re-runs, error translation for every `WEBAUTHN.*` code, Zod rejection at entry
- [ ] 4.2 `src/passkey-autofill-field/passkey-autofill-field.test.tsx` — renders input with correct autocomplete, forwards `inputProps`, blur calls `abort` only when no credential delivered
- [ ] 4.3 `src/errors/errors.test.ts` — every `REACT_AUTH.*` code reachable from a production path
- [ ] 4.4 `src/utils/compose-signals.test.ts` — any-aborts semantics, already-aborted inputs, no leaks
- [ ] 4.5 Every `it(...)` carries `{ timeout: 5000 }` (or 30000 for async)
## 5. Stories
- [ ] 5.1 `src/use-passkey-autofill/use-passkey-autofill.stories.tsx` — default, playground, variants (unavailable, error, abort-on-blur)
- [ ] 5.2 `src/passkey-autofill-field/passkey-autofill-field.stories.tsx` — default, playground, variants
## 6. Documentation
- [ ] 6.1 Sibling `.md` for `use-passkey-autofill.ts`, `passkey-autofill-field.tsx`, `errors.ts`, `compose-signals.ts` (≥200 words, basic + advanced examples, full API reference)
- [ ] 6.2 `README.md` at package root with Modules table
- [ ] 6.3 Add `@suaveplan/react-auth` row to `CLAUDE.md` under an Auth/React section
## 7. Pre-archive verification (the 5-gate triple)
- [ ] 7.1 `bun run check` — Biome 0 errors / 0 warnings / 0 info
- [ ] 7.2 `bunx turbo lint typecheck test --filter=@suaveplan/react-auth` — green
- [ ] 7.3 `bunx turbo test:coverage --filter=@suaveplan/react-auth` — 100/100/100/100
- [ ] 7.4 `bun scripts/openspec-conformance.ts @suaveplan/react-auth` — exit 0, no `error` findings
- [ ] 7.5 `bunx turbo build --filter=@suaveplan/react-auth` — green
## 8. Pre-archive 7-row checklist (AGENTS.md §13)
- [ ] 8.1 Generics threaded; `expectTypeOf` assertions on hook return type
- [ ] 8.2 Zod schema wired at hook entry boundary
- [ ] 8.3 Every `REACT_AUTH.*` code thrown by ≥1 production path
- [ ] 8.4 Strict-mode behaviors tested (feature-available vs unavailable)
- [ ] 8.5 Hook lifecycle tested under React 19 strict-mode double-mount
- [ ] 8.6 AbortSignal composition tested for all sources (caller signal, returned `abort`, unmount)
- [ ] 8.7 No retroactive tick
## 9. Banned-pattern + barrel + runner-import scans
- [ ] 9.1 `grep -rn "@deprecated\|TODO\|FIXME\|ts-ignore\|ts-expect-error\|\.skip\|\.only" packages/auth/react-auth/src/ --include="*.ts" --include="*.tsx"` — zero output
- [ ] 9.2 `grep -rn "^\s*export\s\+\(type\s\+\)\?\*\s\+from" packages/auth/react-auth/src/ --include="index.ts"` — zero output (feature barrels must use named exports)
- [ ] 9.3 `grep -rnE "from ['\"](vitest|bun:test)['\"]" packages/auth/react-auth/src/ --include="*.ts" --include="*.tsx"` — zero output
## 10. Archive
- [ ] 10.1 Fold `specs/react-auth/spec.md` into `openspec/specs/react-auth/spec.md`
- [ ] 10.2 Move `openspec/changes/add-react-auth-passkey-autofill/` → `openspec/archive/<yyyy-mm-dd>-add-react-auth-passkey-autofill/`
- [ ] 10.3 Refresh `MANIFEST.md` via `bun scripts/folder-reorg/generate-manifest.ts`
- [ ] 10.4 Refresh `MISSING.yml` via `bun scripts/generate-missing.ts`
- [ ] 10.5 Update `STATUS.md` — add `@suaveplan/react-auth` row in `Completed`
- [ ] 10.6 Update `SUMMARY.md` with per-package paragraph
- [ ] 10.7 `bunx markdownlint-cli2 --fix` and stage results