feat(boundaries): WP-03 — contracts purity + ApiClient confinement
Lint-enforce two architecture rules that were only documented (ADR-0001), landing the rules with the fixes so the build stays green: - contracts/ imports nothing: dashboard-view.dto.ts is now pure wire shapes (inline string-union enums, no domain imports). The DashboardView FE-view type moves to the adapter, which maps wire → domain (compiler-enforced seam). - ApiClient lives only in infrastructure: change-request-form (UI) no longer injects ApiClient — a new ChangeRequestAdapter owns the client and the submit becomes a createSubmitChangeRequest() command factory (createDraftSync shape). draft-sync's wire-DTO import becomes type-only (allowed via allowTypeImports). - Role type moves to shared/domain/role.ts; the ?role= reader stays in shared/infrastructure/role.ts. - eslint: contracts import-ban + @typescript-eslint/no-restricted-imports on api-client (value-only; type imports permitted; infra + shared/upload exempt). Also fixes a PRE-EXISTING bug found while verifying the flow: change-request-form never imported FormsModule, so (ngSubmit) didn't bind and the submit button did a native form submit (page reload) instead of submitting. Verified end-to-end in the running app: submit → command → adapter → backend → reference, success alert shown. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* The two-person letter workflow's role: who is acting, a drafter or an approver.
|
||||
* A pure domain type (no framework, no reading mechanism) — the `?role=` reader and
|
||||
* the X-Role header live in shared/infrastructure/role.ts. Consumers (brief.store,
|
||||
* letter-composer) depend on this type, not on how the role is obtained.
|
||||
*/
|
||||
export type Role = 'drafter' | 'approver';
|
||||
@@ -1,12 +1,13 @@
|
||||
/**
|
||||
* Dev-only role stand-in. This POC has one faked self-service user and no real
|
||||
* identities, so the two-person letter workflow (drafter vs approver) is driven by
|
||||
* a `?role=` query param — exactly the pattern of the `?scenario=` toggle. The
|
||||
* backend receives it as an `X-Role` header (see role.interceptor) and enforces the
|
||||
* approver≠drafter rule; the FE derives `editable` from it.
|
||||
*/
|
||||
export type Role = 'drafter' | 'approver';
|
||||
import { Role } from '@shared/domain/role';
|
||||
|
||||
/**
|
||||
* Dev-only role stand-in (the reading MECHANISM; the `Role` type is domain). This
|
||||
* POC has one faked self-service user and no real identities, so the two-person
|
||||
* letter workflow (drafter vs approver) is driven by a `?role=` query param —
|
||||
* exactly the pattern of the `?scenario=` toggle. The backend receives it as an
|
||||
* `X-Role` header (see role.interceptor) and enforces the approver≠drafter rule;
|
||||
* the FE derives `editable` from it.
|
||||
*/
|
||||
export function currentRole(): Role {
|
||||
return new URLSearchParams(window.location.search).get('role') === 'approver' ? 'approver' : 'drafter';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user