chore(deps): update npm packages within declared ranges; reformat for prettier 3.9.4
Some checks failed
CI / frontend (push) Successful in 1m46s
CI / storybook-a11y (push) Successful in 4m23s
CI / backend (push) Successful in 1m14s
CI / codeql (csharp) (push) Has been cancelled
CI / codeql (javascript-typescript) (push) Has been cancelled
CI / api-client-drift (push) Has been cancelled
CI / e2e (push) Has been cancelled

npm update brought every package to the latest version its existing package.json
range allows (Angular tooling 22.0.2/22.0.4 -> 22.0.5, prettier 3.8.4 -> 3.9.4,
typescript-eslint 8.62.0 -> 8.62.1); package.json itself needed no range changes.

Auditing actual deprecation warnings (not just outdated versions) found nothing
further to fix: @angular/platform-browser-dynamic and @angular-devkit/build-angular
are deprecated by Angular but still required peer dependencies of the latest
published @storybook/angular (10.4.6 — peer range still `>=18.0.0 < 22.0.0`,
already why .npmrc sets legacy-peer-deps); jest-process-manager/expect-playwright
are transitive-only through @storybook/test-runner's latest stable (0.24.4). No
newer version of either Storybook package exists yet that drops them. The
remaining npm audit advisory (@babel/core, low severity) is the same
already-documented, deliberately-left issue in README.md (fixing it downgrades
Angular). Left package.json's overrides untouched.

The prettier bump alone changed formatting opinions on files this session didn't
otherwise touch (a stale markdown italics marker, a few object-literal wrap
points) — reformatted everything so `format:check` (part of CI) doesn't regress.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 10:29:36 +02:00
parent 556f2f47bf
commit 44eb2d2186
17 changed files with 1696 additions and 2667 deletions

View File

@@ -82,7 +82,7 @@ model a discriminated union instead.** Three tools, all in `shared/application`:
machine's State/Msg types are context-prefixed (`ChangeRequestState`,
`ChangeRequestMsg`), never bare `State`/`Msg`; a top-level machine exports
`initial` + `reduce`. A **composable sub-machine** embedded inside a parent
model keeps prefixed *value* exports instead (`initialUpload`/`reduceUpload`,
model keeps prefixed _value_ exports instead (`initialUpload`/`reduceUpload`,
see `upload.machine.ts`) — prefixing there avoids alias noise at the
composition site.
- **`Result<E,T>` + value objects** ("parse, don't validate") — raw input becomes a

View File

@@ -4,7 +4,7 @@ Status: done (69880ef)
Phase: 2 — CIBG fidelity
> **Deviation:** file-input's label-button was already reworked to `.btn-primary
> .btn-upload` by the earlier out-of-order "CIBG UI fidelity pass" (WP-11/12) — the
.btn-upload` by the earlier out-of-order "CIBG UI fidelity pass" (WP-11/12) — the
> vendored upload vocabulary (`.btn-upload`) supersedes this WP's original
> `.btn-secondary` assumption, so no change was needed there. Icon affordances
> (chevron/pijl classes) are verified present in the vendored CSS, but no in-scope

4235
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -79,7 +79,10 @@ describe('BriefStore action state (Idle | Busy | Failed)', () => {
approveResult = {
ok: true,
value: { ...view, brief: { ...brief, status: { tag: 'approved', approvedBy: 'u2', approvedAt: 't' } } },
value: {
...view,
brief: { ...brief, status: { tag: 'approved', approvedBy: 'u2', approvedAt: 't' } },
},
};
await store.approve();
expect(store.busy()).toBe(false);

View File

@@ -8,10 +8,16 @@ import { parsePostcode } from '@registratie/domain/value-objects/postcode';
const postcode = parsePostcode('2514 EA');
if (!postcode.ok) throw new Error('fixture postcode should parse');
const data: Valid = { straat: 'Lange Voorhout 9', postcode: postcode.value, woonplaats: 'Den Haag' };
const data: Valid = {
straat: 'Lange Voorhout 9',
postcode: postcode.value,
woonplaats: 'Den Haag',
};
function setup(adapter: Partial<ChangeRequestAdapter>) {
TestBed.configureTestingModule({ providers: [{ provide: ChangeRequestAdapter, useValue: adapter }] });
TestBed.configureTestingModule({
providers: [{ provide: ChangeRequestAdapter, useValue: adapter }],
});
return TestBed.runInInjectionContext(() => createSubmitChangeRequest());
}

View File

@@ -13,7 +13,9 @@ describe('change-request reduce', () => {
it('SetField updates the draft while editing', () => {
const s = reduce(initial, { tag: 'SetField', key: 'straat', value: 'Lange Voorhout 9' });
expect(s.tag).toBe('Editing');
expect((s as Extract<ChangeRequestState, { tag: 'Editing' }>).draft.straat).toBe('Lange Voorhout 9');
expect((s as Extract<ChangeRequestState, { tag: 'Editing' }>).draft.straat).toBe(
'Lange Voorhout 9',
);
});
it('Submit with an invalid draft stays Editing and reports field errors', () => {

View File

@@ -3,7 +3,9 @@ import { parseAantekening } from './big-register.adapter';
describe('big-register.adapter parse boundary', () => {
it('parses known aantekening types', () => {
expect(parseAantekening({ type: 'Specialisme', omschrijving: 'x', datum: '2026-01-01' })).toEqual({
expect(
parseAantekening({ type: 'Specialisme', omschrijving: 'x', datum: '2026-01-01' }),
).toEqual({
ok: true,
value: { type: 'Specialisme', omschrijving: 'x', datum: '2026-01-01' },
});

View File

@@ -10,7 +10,12 @@ import {
} from '@registratie/ui/address-fields/address-fields.component';
import { createStore } from '@shared/application/store';
import { whenTag } from '@shared/kernel/fp';
import { ChangeRequestState, ChangeRequestMsg, initial, reduce } from '@registratie/domain/change-request.machine';
import {
ChangeRequestState,
ChangeRequestMsg,
initial,
reduce,
} from '@registratie/domain/change-request.machine';
import { createSubmitChangeRequest } from '@registratie/application/submit-change-request';
/**

View File

@@ -12,8 +12,7 @@ export function assertNever(x: never): never {
/** A computation that either succeeded with a value or failed with an error.
Plain objects (no classes) to match the signal/httpResource ergonomics. */
export type Result<E, T> =
| { readonly ok: true; readonly value: T }
| { readonly ok: false; readonly error: E };
{ readonly ok: true; readonly value: T } | { readonly ok: false; readonly error: E };
export const ok = <T>(value: T): Result<never, T> => ({ ok: true, value });
export const err = <E>(error: E): Result<E, never> => ({ ok: false, error });

View File

@@ -19,8 +19,7 @@ const meta: Meta<WizardShellComponent> = {
cibgGap: true,
docs: {
description: {
component:
'CIBG-gap extension (error summary only) — see Foundations/CIBG Gap Register.',
component: 'CIBG-gap extension (error summary only) — see Foundations/CIBG Gap Register.',
},
},
},

View File

@@ -245,10 +245,7 @@ export class RichTextEditorComponent {
}
if (!(e.ctrlKey || e.metaKey) || e.altKey) return;
const cmd = { b: 'bold', i: 'italic', u: 'underline' }[e.key.toLowerCase()] as
| 'bold'
| 'italic'
| 'underline'
| undefined;
'bold' | 'italic' | 'underline' | undefined;
if (!cmd) return;
e.preventDefault();
this.format(cmd);

View File

@@ -83,8 +83,7 @@ export type UploadMsg =
type: 'BackgroundUploadsReturned';
results: Array<
{ localId: string } & (
| { success: true; documentId: string }
| { success: false; reason: string }
{ success: true; documentId: string } | { success: false; reason: string }
)
>;
};

View File

@@ -22,7 +22,7 @@ placed above the `@Component` decorator, plus `parameters: { cibgGap: true }` an
## The register
| Component | Closest CIBG concept | Why hand-rolled |
| --- | --- | --- |
| --------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `skeleton` | Laadindicatie | No loading-skeleton class in the vendored build. |
| `spinner` | Laadindicatie | No loading-spinner class in the vendored build. |
| `rich-text-editor` | Tekstgebied | No rich-text/WYSIWYG pattern; toolbar buttons still use vendored `.btn-ghost` (WP-10). |

View File

@@ -34,7 +34,7 @@ Never the other way — `registratie` may not import `herregistratie`, and no co
## Five layers, one direction
| Layer | Job | Angular allowed? |
| --- | --- | --- |
| ----------------- | ----------------------------------------- | ----------------- |
| `domain/` | business rules + data types | **No — pure TS.** |
| `application/` | coordinate state/tasks (stores, commands) | yes (signals) |
| `infrastructure/` | where data comes from (HTTP adapters) | yes (HTTP) |

View File

@@ -36,7 +36,11 @@ dispatches a message describing the outcome:
// command = "go do it, then say what happened" — reduce never sees the HTTP call itself
async function submit(store: Store<WizardState, WizardMsg>) {
const r = await adapter.submit(toDto(store.model()));
store.dispatch(r.ok ? { tag: 'SubmitConfirmed', referentie: r.value } : { tag: 'SubmitFailed', error: r.error });
store.dispatch(
r.ok
? { tag: 'SubmitConfirmed', referentie: r.value }
: { tag: 'SubmitFailed', error: r.error },
);
}
```
@@ -60,7 +64,7 @@ worse name, and it's the thing a newcomer copies if two idioms are visible side
Wire every machine through `createStore`, full stop.
`dispatch` uses `model.update(…)`, not `model.set(reduce(model(), msg))` — the latter
reads `model()` *inside* the call, which means an `effect()` that both reads `model` and
reads `model()` _inside_ the call, which means an `effect()` that both reads `model` and
calls `dispatch` would subscribe to its own write and livelock. `.update()`'s callback
receives the current value directly, untracked.
@@ -73,7 +77,7 @@ receives the current value directly, untracked.
- A top-level machine exports `initial` (the starting Model) and `reduce` — unprefixed,
since the file/module already disambiguates them at the import site
(`import { initial, reduce } from './herregistratie.machine'`).
- A **composable sub-machine** — one embedded *inside* a parent Model, like
- A **composable sub-machine** — one embedded _inside_ a parent Model, like
`upload.machine.ts`'s upload-widget state living inside the registratie wizard's own
Model — keeps **prefixed value exports** instead: `initialUpload`, `reduceUpload`.
The parent machine already imports several machines' `initial`/`reduce`; prefixing the
@@ -90,7 +94,7 @@ a function of what I already have?"
## Where RemoteData fits in
A machine owns the **domain** lifecycle of what it holds once it exists (draft →
submitted → approved, in the brief's case). It should generally *not* also own the
submitted → approved, in the brief's case). It should generally _not_ also own the
**fetch** lifecycle (loading/failed) for the initial GET that produces it — that's a
generic concern `RemoteData` already models once, consistently, across the app (see
[Foundations/RemoteData & Async](?path=/docs/foundations-remotedata-async--docs)). Where

View File

@@ -37,7 +37,11 @@ enters.
```ts
// before — the wire's `type` string is trusted outright
function toAantekening(n: AantekeningDto): Aantekening {
return { type: n.type as AantekeningType, omschrijving: n.omschrijving ?? '', datum: n.datum ?? '' };
return {
type: n.type as AantekeningType,
omschrijving: n.omschrijving ?? '',
datum: n.datum ?? '',
};
}
```
@@ -48,7 +52,11 @@ const AANTEKENING_TYPES: readonly AantekeningType[] = ['Specialisme', 'Aantekeni
export function parseAantekening(n: AantekeningDto): Result<string, Aantekening> {
if (!n.type || !AANTEKENING_TYPES.includes(n.type as AantekeningType))
return err(`aantekening: unknown type ${n.type}`);
return ok({ type: n.type as AantekeningType, omschrijving: n.omschrijving ?? '', datum: n.datum ?? '' });
return ok({
type: n.type as AantekeningType,
omschrijving: n.omschrijving ?? '',
datum: n.datum ?? '',
});
}
```
@@ -86,7 +94,9 @@ policyResource() {
```ts
// after — a domain-side type + a validated parse; the resource never surfaces raw wire shape
export interface IntakePolicy { readonly scholingThreshold: number }
export interface IntakePolicy {
readonly scholingThreshold: number;
}
export function parseIntakePolicy(json: unknown): Result<string, IntakePolicy> {
if (typeof json !== 'object' || json === null) return err('intake-policy: not an object');
@@ -99,7 +109,7 @@ export function parseIntakePolicy(json: unknown): Result<string, IntakePolicy> {
## The sanctioned exception
Narrowing `unknown` to `Partial<Dto>` so you can *start* checking fields is fine — that's not a
Narrowing `unknown` to `Partial<Dto>` so you can _start_ checking fields is fine — that's not a
trust decision, it's just giving the compiler a shape to probe (`const dto = json as
Partial<DashboardViewDto>`, see `dashboard-view.adapter.ts`). What's never fine is casting a
field to its final domain type without having checked it first.

View File

@@ -12,7 +12,11 @@ believe?). `src/app/shared/application/remote-data.ts` closes that off with one
union instead:
```ts
type RemoteData<E, T> = { tag: 'Loading' } | { tag: 'Empty' } | { tag: 'Failure'; error: E } | { tag: 'Success'; value: T };
type RemoteData<E, T> =
| { tag: 'Loading' }
| { tag: 'Empty' }
| { tag: 'Failure'; error: E }
| { tag: 'Success'; value: T };
```
## Combining sources
@@ -50,7 +54,7 @@ flashes it; override with an `appAsyncLoading` template. `appAsyncEmpty` and
This is a real Angular constraint, not an oversight: a structural directive's type
parameter can only be inferred from an **input bound on that same element** (this is how
`*ngFor="let x of items"` and `*ngIf="x as y"` work — the type comes from `ngForOf`/`ngIf`,
inputs on the very same tag). `<ng-template appAsyncLoaded let-p>` sits on a *different*
inputs on the very same tag). `<ng-template appAsyncLoaded let-p>` sits on a _different_
node than `<app-async [data]="…">`, so `p` cannot inherit a type from that sibling input,
even though they're nested in the same template. Angular types it `unknown`, and
`ngTemplateContextGuard` can't fix that without an input to seed it from — the shared
@@ -72,7 +76,7 @@ protected readonly loaded = computed(() => {
```html
<!-- in the template, inside <ng-template appAsyncLoaded> -->
@if (loaded(); as s) {
<app-letter-composer [brief]="s.brief" ... />
<app-letter-composer [brief]="s.brief" ... />
}
```
@@ -93,5 +97,5 @@ fetch's loading/failure, which is a generic concern `RemoteData` already models.
machine's own `loading`/`failed` tags purely mirror the fetch (nothing extra beyond "not
loaded yet" / "the GET failed"), project them onto a `RemoteData` computed at the store
layer for `<app-async>` to render, the way `BriefStore.remoteData` does — the machine
keeps deciding what the *letter* is doing, `RemoteData` keeps deciding what the *fetch* is
keeps deciding what the _letter_ is doing, `RemoteData` keeps deciding what the _fetch_ is
doing.