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

6.5 KiB

Tasks — @suaveplan/react-auth usePasskeyAutofill

Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes.

0. Blocking prerequisite

  • 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

  • 1.1 Read openspec/archive/2026-05-11-enterprise-webauthn/proposal.md + tasks.md to confirm the usePasskeyAutofill deferral
  • 1.2 Read openspec/AGENTS.md §13 and §14
  • 1.3 Draft proposal.md from openspec/templates/proposal.md
  • 1.4 Draft design.md covering hook signature, lifecycle, SSR safety, error translation
  • 1.5 Draft specs/react-auth/spec.md from openspec/templates/spec-delta.md
  • 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 AbortSignals 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