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
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:
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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' },
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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.',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 }
|
||||
)
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -21,17 +21,17 @@ 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). |
|
||||
| `wizard-shell` (error summary only) | Foutmelding | No error-summary/Veldvalidatie list class; renders inside a vendored `.feedback-error` alert. |
|
||||
| `application-link` (non-navigating row) | Aanvragen | The vendored `.dashboard-block.applications li a` chain only styles `<a>`; `.static-row` mirrors it from tokens for the informational (non-link) case. |
|
||||
| `debug-state` | n/a | Dev-only tool, deliberately off-theme — see the component's own `ponytail:` note. |
|
||||
| `status-badge` | n/a | Deliberate custom status dot, not Bootstrap's `.badge` (pill padding/colour don't fit). |
|
||||
| `card` (`.app-card`) | n/a | No vendored generic-card class; prefer the vendored **Datablock** (`app-data-block`, WP-12) for application/user data. |
|
||||
| `placeholder-chip` | n/a | No vendored inline-chip/tag class. |
|
||||
| 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). |
|
||||
| `wizard-shell` (error summary only) | Foutmelding | No error-summary/Veldvalidatie list class; renders inside a vendored `.feedback-error` alert. |
|
||||
| `application-link` (non-navigating row) | Aanvragen | The vendored `.dashboard-block.applications li a` chain only styles `<a>`; `.static-row` mirrors it from tokens for the informational (non-link) case. |
|
||||
| `debug-state` | n/a | Dev-only tool, deliberately off-theme — see the component's own `ponytail:` note. |
|
||||
| `status-badge` | n/a | Deliberate custom status dot, not Bootstrap's `.badge` (pill padding/colour don't fit). |
|
||||
| `card` (`.app-card`) | n/a | No vendored generic-card class; prefer the vendored **Datablock** (`app-data-block`, WP-12) for application/user data. |
|
||||
| `placeholder-chip` | n/a | No vendored inline-chip/tag class. |
|
||||
|
||||
Not a gap: `confirmation` renders entirely with vendored `.confirmation*` classes (no `styles:
|
||||
[...]` block) — its header comment names the pattern, no marker needed. The `upload/` suite
|
||||
|
||||
@@ -33,13 +33,13 @@ 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) |
|
||||
| `contracts/` | wire DTOs (the FE⇄BE seam) | no |
|
||||
| `ui/` | how it looks (components, pages) | yes |
|
||||
| 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) |
|
||||
| `contracts/` | wire DTOs (the FE⇄BE seam) | no |
|
||||
| `ui/` | how it looks (components, pages) | yes |
|
||||
|
||||
`ui → application → domain`; `ui`/`layout` never import `infrastructure/` directly — they
|
||||
reach data through an application store or command.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user