@mrciphersmith/keryx 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cli.js +13362 -7078
- package/dist/core.js +11706 -11330
- package/package.json +1 -1
- package/src/gdskills/bundled/agents/go-code-auditor.md +1 -1
- package/src/gdskills/bundled/agents/python-code-auditor.md +1 -1
- package/src/gdskills/bundled/install-manifest.json +271 -4
- package/src/gdskills/bundled/rules/core/model-selection.mdc +51 -0
- package/src/gdskills/bundled/skills/review/review-jev-comments/SKILL.md +184 -0
- package/src/gdskills/bundled/skills/review/review-jev-docs/SKILL.md +189 -0
- package/src/gdskills/bundled/skills/review/review-jev-risk/SKILL.md +190 -0
- package/src/gdskills/bundled/skills/review/review-jev-rules/SKILL.md +267 -0
- package/src/gdskills/bundled/skills/review/review-jev-scenarios/SKILL.md +187 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +39 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +1 -1
- package/src/gdskills/bundled/stacks/angular/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/angular/governance/eval.json +1751 -0
- package/src/gdskills/bundled/stacks/angular/governance/scout.json +32 -0
- package/src/gdskills/bundled/stacks/angular/pack.json +55 -0
- package/src/gdskills/bundled/stacks/angular/rules/coding-style.mdc +82 -0
- package/src/gdskills/bundled/stacks/angular/rules/patterns.mdc +84 -0
- package/src/gdskills/bundled/stacks/angular/rules/security.mdc +70 -0
- package/src/gdskills/bundled/stacks/angular/rules/testing.mdc +73 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-build-fix/SKILL.md +127 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-build-fix/evals.json +72 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-code-review/SKILL.md +98 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-code-review/evals.json +73 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-implementation/SKILL.md +112 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-implementation/evals.json +74 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-testing/SKILL.md +102 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-testing/evals.json +71 -0
- package/src/gdskills/bundled/stacks/mobx/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/mobx/governance/eval.json +904 -0
- package/src/gdskills/bundled/stacks/mobx/governance/scout.json +18 -0
- package/src/gdskills/bundled/stacks/mobx/pack.json +28 -0
- package/src/gdskills/bundled/stacks/mobx/rules/coding-style.mdc +91 -0
- package/src/gdskills/bundled/stacks/mobx/rules/patterns.mdc +122 -0
- package/src/gdskills/bundled/stacks/mobx/rules/security.mdc +56 -0
- package/src/gdskills/bundled/stacks/mobx/rules/testing.mdc +63 -0
- package/src/gdskills/bundled/stacks/mobx/skills/mobx-observable-testing/SKILL.md +124 -0
- package/src/gdskills/bundled/stacks/mobx/skills/mobx-observable-testing/evals.json +73 -0
- package/src/gdskills/bundled/stacks/mobx/skills/mobx-store-implementation/SKILL.md +149 -0
- package/src/gdskills/bundled/stacks/mobx/skills/mobx-store-implementation/evals.json +74 -0
- package/src/gdskills/bundled/stacks/nestjs/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/nestjs/governance/eval.json +1308 -0
- package/src/gdskills/bundled/stacks/nestjs/governance/scout.json +34 -0
- package/src/gdskills/bundled/stacks/nestjs/pack.json +53 -0
- package/src/gdskills/bundled/stacks/nestjs/rules/coding-style.mdc +70 -0
- package/src/gdskills/bundled/stacks/nestjs/rules/patterns.mdc +83 -0
- package/src/gdskills/bundled/stacks/nestjs/rules/security.mdc +73 -0
- package/src/gdskills/bundled/stacks/nestjs/rules/testing.mdc +69 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-build-fix/SKILL.md +157 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-build-fix/evals.json +70 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-implementation/SKILL.md +129 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-implementation/evals.json +71 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-testing/SKILL.md +143 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-testing/evals.json +69 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/governance/eval.json +2413 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/governance/scout.json +42 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/pack.json +42 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/coding-style.mdc +69 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/patterns.mdc +88 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/security.mdc +72 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/testing.mdc +64 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-build-fix/SKILL.md +147 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-build-fix/evals.json +75 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-code-review/SKILL.md +118 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-code-review/evals.json +76 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-implementation/SKILL.md +135 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-implementation/evals.json +78 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-testing/SKILL.md +116 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-testing/evals.json +75 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-upgrade-migration/SKILL.md +134 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-upgrade-migration/evals.json +76 -0
- package/src/gdskills/bundled/stacks/vue/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/vue/governance/eval.json +2215 -0
- package/src/gdskills/bundled/stacks/vue/governance/scout.json +42 -0
- package/src/gdskills/bundled/stacks/vue/pack.json +42 -0
- package/src/gdskills/bundled/stacks/vue/rules/coding-style.mdc +73 -0
- package/src/gdskills/bundled/stacks/vue/rules/patterns.mdc +84 -0
- package/src/gdskills/bundled/stacks/vue/rules/security.mdc +60 -0
- package/src/gdskills/bundled/stacks/vue/rules/testing.mdc +69 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-build-fix/SKILL.md +137 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-build-fix/evals.json +72 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-code-review/SKILL.md +120 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-code-review/evals.json +71 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-implementation/SKILL.md +122 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-implementation/evals.json +72 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-testing/SKILL.md +115 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-testing/evals.json +72 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue2-to-vue3-migration/SKILL.md +135 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue2-to-vue3-migration/evals.json +71 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Use when implementing a new Angular component, service, directive, or pipe -- standalone component structure, signal-based (signal()/computed()/effect()) state, inject()-based dependency injection, and typed Reactive Forms. Wires RxJS-to-signal interop (toSignal/takeUntilDestroyed) correctly and follows the project's own routing conventions. Not for Vue/React component implementation, not for writing unit specs for an Angular component (use angular-testing), and not for repairing an already-failing Angular compiler error (use angular-build-fix).",
|
|
4
|
+
"decision": "fork",
|
|
5
|
+
"topMatch": "angular/angular-code-review",
|
|
6
|
+
"recordedAt": "2026-09-25T06:33:13.884Z",
|
|
7
|
+
"skillName": "angular-implementation",
|
|
8
|
+
"justification": "Top match angular-code-review is this pack's own sibling sharing Angular/signal/inject() vocabulary, but is read-only review of already-written code, explicitly excluded here (this skill authors new components/services); trigger-accuracy eval confirms both route correctly on their own positive prompts. No existing catalog skill (react-implementation, vue-implementation, nestjs-implementation) covers Angular's own standalone-component/signal/inject() authoring surface."
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "Use when writing or fixing a TestBed spec for an Angular component, service, directive, pipe, or route guard -- configuring TestBed.configureTestingModule, driving a ComponentFixture (componentRef.setInput, detectChanges, whenStable), mocking HttpClient with HttpTestingController, testing a CanActivate/CanMatch guard function, and using fakeAsync/tick for deterministic async specs. Not for Vue/React component tests, not for a full user-flow check against a deployed, running instance, and not for implementing the component/service under test (use angular-implementation).",
|
|
12
|
+
"decision": "create",
|
|
13
|
+
"topMatch": "angular/angular-implementation",
|
|
14
|
+
"recordedAt": "2026-09-25T06:33:14.047Z",
|
|
15
|
+
"skillName": "angular-testing"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"query": "Use when reviewing an Angular component/service/directive diff for framework-specific correctness -- untracked RxJS subscriptions missing takeUntilDestroyed/async-pipe cleanup, OnPush components mutating input objects in place instead of replacing the reference, a dependency-injection call made outside a valid injector context, DomSanitizer bypassSecurityTrust* calls on user-influenced data, and NgModule/standalone mixing. Does not review generic JavaScript/TypeScript correctness the base stack reviewer already covers, does not review MobX store logic, and never edits code -- it reports findings only. Not for implementing or fixing the reviewed code (use angular-implementation or angular-build-fix).",
|
|
19
|
+
"decision": "create",
|
|
20
|
+
"topMatch": "angular/angular-implementation",
|
|
21
|
+
"recordedAt": "2026-09-25T06:33:14.214Z",
|
|
22
|
+
"skillName": "angular-code-review"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"query": "Use when resolving an `ng build` failure, an AOT/Ivy template type-checking error (a property that doesn't exist on the bound type, an @if/@for control-flow type mismatch), a missing NgModule declaration/import error, or a DI 'No provider for X' resolution error blocking an Angular build. Applies the smallest root-cause fix and never silences the compiler with strictTemplates: false, an `any` cast in a template context, or `@Injectable` removal. Not for a plain TypeScript compile failure outside any Angular template, and not for authoring a fresh component or injectable from scratch (use angular-implementation).",
|
|
26
|
+
"decision": "fork",
|
|
27
|
+
"topMatch": "nextjs-nuxt/nextjs-nuxt-build-fix",
|
|
28
|
+
"recordedAt": "2026-09-25T06:33:14.375Z",
|
|
29
|
+
"skillName": "angular-build-fix",
|
|
30
|
+
"justification": "Top match nextjs-nuxt-build-fix shares generic 'build fails' vocabulary but targets Next.js/Nuxt compile failures (Server/Client boundary, composable resolution), not Angular's AOT/Ivy template type-checking or NgModule/DI resolution errors this skill covers; the description explicitly excludes a plain TypeScript compile failure with no Angular template involved. No existing catalog skill covers ng build/AOT-specific fixes. Confirmed distinct by clean trigger-accuracy eval."
|
|
31
|
+
}
|
|
32
|
+
]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "angular",
|
|
3
|
+
"family": "framework",
|
|
4
|
+
"extends": "ts-js-node",
|
|
5
|
+
"modules": [
|
|
6
|
+
"angular-rules",
|
|
7
|
+
"angular-skills"
|
|
8
|
+
],
|
|
9
|
+
"detectionMarkers": [
|
|
10
|
+
"angular"
|
|
11
|
+
],
|
|
12
|
+
"provenance": {
|
|
13
|
+
"origin": "authored",
|
|
14
|
+
"sourceRef": "flow 318, Wave 4 batch 2"
|
|
15
|
+
},
|
|
16
|
+
"stability": "experimental",
|
|
17
|
+
"skills": {
|
|
18
|
+
"implement": [
|
|
19
|
+
"angular-implementation"
|
|
20
|
+
],
|
|
21
|
+
"test": [
|
|
22
|
+
"angular-testing"
|
|
23
|
+
],
|
|
24
|
+
"review": [
|
|
25
|
+
"angular-code-review"
|
|
26
|
+
],
|
|
27
|
+
"build-fix": [
|
|
28
|
+
"angular-build-fix"
|
|
29
|
+
],
|
|
30
|
+
"migrate": []
|
|
31
|
+
},
|
|
32
|
+
"agentProfile": {
|
|
33
|
+
"displayName": "Angular",
|
|
34
|
+
"auditFocus": [
|
|
35
|
+
"an RxJS subscription created in a component/directive with no takeUntilDestroyed, async pipe, or explicit ngOnDestroy teardown",
|
|
36
|
+
"a component still using ChangeDetectionStrategy.Default with heavy template bindings where OnPush plus signals would isolate re-renders",
|
|
37
|
+
"a service or dependency reached through constructor-parameter injection mixed inconsistently with inject() in the same file, or inject() called outside an injection context",
|
|
38
|
+
"a template still using *ngIf/*ngFor/*ngSwitch in a component whose codebase has otherwise migrated to @if/@for/@switch control flow",
|
|
39
|
+
"a NgModule-based component/directive/pipe added to a codebase where the rest of the app is standalone",
|
|
40
|
+
"state mutated directly on a class field instead of through a signal's set/update, or a computed signal with a side effect inside its computation"
|
|
41
|
+
],
|
|
42
|
+
"buildCommands": [
|
|
43
|
+
"ng build (or the project's package.json build script wrapping the Angular CLI)",
|
|
44
|
+
"ng test (or the project's configured Karma/Jest runner) for unit tests",
|
|
45
|
+
"npx tsc --noEmit for standalone type-checking outside the Angular compiler",
|
|
46
|
+
"ng lint (or the project's configured eslint-angular script)"
|
|
47
|
+
],
|
|
48
|
+
"fixGuardrails": [
|
|
49
|
+
"never add `standalone: false` or reintroduce an NgModule just to route around a compiler/DI error in an otherwise-standalone codebase",
|
|
50
|
+
"never disable Angular's strict template type checking (`strictTemplates`, `fullTemplateTypeCheck`) in tsconfig to silence a template type error",
|
|
51
|
+
"never swallow an RxJS subscription error with an empty `error: () => {}` handler to stop a console error from failing a build check",
|
|
52
|
+
"never delete or skip a failing TestBed spec to reach a green test run"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.ts", "**/*.html"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Angular coding style
|
|
9
|
+
|
|
10
|
+
Narrows the stack-agnostic style rules to current Angular idiom (standalone
|
|
11
|
+
components, signals, `inject()`). Applies to `*.ts` and component
|
|
12
|
+
`*.html` templates; everything not Angular-specific still comes from the
|
|
13
|
+
common rules this file `extends`.
|
|
14
|
+
|
|
15
|
+
## Components and files
|
|
16
|
+
|
|
17
|
+
- Components, directives, and pipes are standalone by default in current
|
|
18
|
+
Angular — do not set `standalone: false` or write a new `NgModule` to
|
|
19
|
+
register one unless the project itself is still NgModule-based
|
|
20
|
+
end-to-end.
|
|
21
|
+
- One component (and its template/styles) per file, named after the
|
|
22
|
+
component class (`OrderSummary` in `order-summary.ts`, not a generic
|
|
23
|
+
`component.ts`). Match whatever file-suffix convention (`.component.ts`
|
|
24
|
+
vs. plain `.ts`) the project's own `angular.json`/existing files already
|
|
25
|
+
use rather than introducing a second one.
|
|
26
|
+
- Keep a component's template under roughly 60-80 lines before extracting a
|
|
27
|
+
child component; a template doing significant `@if`/`@for` branching for
|
|
28
|
+
more than one concern is a sign the branches belong in separate
|
|
29
|
+
components.
|
|
30
|
+
|
|
31
|
+
## Dependency injection
|
|
32
|
+
|
|
33
|
+
- Prefer the `inject()` function over constructor-parameter injection for
|
|
34
|
+
new code (`private readonly router = inject(Router);`), since it composes
|
|
35
|
+
cleanly with field initializers and works outside constructors (guards,
|
|
36
|
+
resolvers, `provide*` factories). Do not mix `inject()` and
|
|
37
|
+
constructor-parameter injection for different dependencies in the same
|
|
38
|
+
class — pick one per class, and prefer `inject()` for new classes.
|
|
39
|
+
- `inject()` only works inside an injection context (a constructor, a field
|
|
40
|
+
initializer, or a call wrapped in `runInInjectionContext`) — do not call
|
|
41
|
+
it inside a `setTimeout`, a plain async callback, or after the
|
|
42
|
+
constructor has returned.
|
|
43
|
+
|
|
44
|
+
## Signals and state
|
|
45
|
+
|
|
46
|
+
- Use `signal()` for local component/service state that changes over time,
|
|
47
|
+
`computed()` for values derived from other signals, and `input()`/
|
|
48
|
+
`model()` for component inputs — do not read/write a signal's backing
|
|
49
|
+
value directly; always go through `.set()`/`.update()`.
|
|
50
|
+
- Keep a `computed()`'s computation pure (no side effects, no writes to
|
|
51
|
+
other signals inside it) — if a change needs to trigger a side effect,
|
|
52
|
+
use `effect()`, not a computed callback abused for its side effect.
|
|
53
|
+
- Do not mirror a signal's value into a plain class field "for convenience"
|
|
54
|
+
in the template — read the signal directly (`value()`) so Angular's
|
|
55
|
+
fine-grained reactivity tracks the actual dependency.
|
|
56
|
+
|
|
57
|
+
## Templates and control flow
|
|
58
|
+
|
|
59
|
+
- Use the built-in control flow (`@if`, `@for` with a `track` expression,
|
|
60
|
+
`@switch`) in new templates, not `*ngIf`/`*ngFor`/`*ngSwitch` — unless the
|
|
61
|
+
surrounding component's own template still uses the structural-directive
|
|
62
|
+
form throughout, in which case match the existing file rather than
|
|
63
|
+
mixing both styles in one template.
|
|
64
|
+
- Always give `@for` an explicit `track` expression (the item's own stable
|
|
65
|
+
id, not `$index`, unless the list has no natural identity) — an untracked
|
|
66
|
+
or index-tracked loop over data that reorders forces Angular to
|
|
67
|
+
re-render every row instead of just the ones that changed.
|
|
68
|
+
|
|
69
|
+
## Naming and typing
|
|
70
|
+
|
|
71
|
+
- `PascalCase` for component/directive/pipe/service classes, `camelCase`
|
|
72
|
+
for signals, fields, and methods — a signal's own name should read as the
|
|
73
|
+
value it holds (`user`, not `userSignal`), since calling it (`user()`)
|
|
74
|
+
already signals reactivity.
|
|
75
|
+
- Type every `input()`/`output()`/`model()` explicitly
|
|
76
|
+
(`input<Order | null>(null)`) rather than relying on inference from a
|
|
77
|
+
loosely-typed default; an untyped input silently becomes `unknown`/`any`
|
|
78
|
+
at every call site.
|
|
79
|
+
- Avoid `any` in component/service code; when the Angular CLI's generated
|
|
80
|
+
boilerplate produces a loosely-typed value (e.g. a form's raw value),
|
|
81
|
+
narrow it explicitly at the boundary instead of propagating `any`
|
|
82
|
+
through the class.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.ts", "**/*.html"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Angular patterns
|
|
9
|
+
|
|
10
|
+
Idiomatic component/service/change-detection design for current Angular,
|
|
11
|
+
and the anti-patterns to avoid when narrowing the common architecture rules
|
|
12
|
+
to this stack.
|
|
13
|
+
|
|
14
|
+
## Change detection
|
|
15
|
+
|
|
16
|
+
- Default new components to `ChangeDetectionStrategy.OnPush`. With
|
|
17
|
+
signal-based state this is nearly free: reading a signal in a template
|
|
18
|
+
registers it as a dependency and Angular schedules the check for you, so
|
|
19
|
+
`OnPush` plus signals rarely needs a manual `markForCheck()` at all.
|
|
20
|
+
- If a component still receives data via `@Input()` mutation of a plain
|
|
21
|
+
object (not a signal) under `OnPush`, replace the mutation with a new
|
|
22
|
+
object reference (or migrate the input to a signal) — `OnPush` only
|
|
23
|
+
re-checks on a new reference/signal change, not a mutated property on the
|
|
24
|
+
same object.
|
|
25
|
+
- Reach for `ChangeDetectorRef.markForCheck()` only when a value changes
|
|
26
|
+
outside Angular's own change-detection triggers (a third-party callback,
|
|
27
|
+
a native DOM event Angular didn't bind) and cannot reasonably be modeled
|
|
28
|
+
as a signal — not as a routine fix for "the view didn't update".
|
|
29
|
+
|
|
30
|
+
## Dependency injection and service boundaries
|
|
31
|
+
|
|
32
|
+
- Scope a service with `providedIn: 'root'` for app-wide singletons; scope
|
|
33
|
+
it in a route's providers (or a standalone component's `providers`) for
|
|
34
|
+
state that should reset per-feature/per-route instead of leaking across
|
|
35
|
+
the whole app's lifetime.
|
|
36
|
+
- Keep a component's class focused on view state and event wiring; move
|
|
37
|
+
business logic, HTTP calls, and cross-component state into an injectable
|
|
38
|
+
service. A component `inject()`-ing `HttpClient` directly to build
|
|
39
|
+
request logic inline is a sign that logic belongs in a service instead.
|
|
40
|
+
- Use Angular's `HttpClient` (with `provideHttpClient()` and interceptors
|
|
41
|
+
for cross-cutting concerns like auth headers) rather than a bare `fetch`
|
|
42
|
+
scattered through components — interceptors are the idiomatic place for
|
|
43
|
+
auth/error handling, not per-call boilerplate.
|
|
44
|
+
|
|
45
|
+
## RxJS and signal interop
|
|
46
|
+
|
|
47
|
+
- Prefer `toSignal()`/`toObservable()` (`@angular/core/rxjs-interop`) at
|
|
48
|
+
the boundary between RxJS streams (HTTP, router events, form
|
|
49
|
+
`valueChanges`) and signal-based component state, rather than manually
|
|
50
|
+
subscribing into a plain field.
|
|
51
|
+
- When an `Observable` genuinely needs a manual `.subscribe()` inside a
|
|
52
|
+
component (rare once `async` pipe / `toSignal()` cover the common case),
|
|
53
|
+
pipe through `takeUntilDestroyed()` (call it in an injection context, or
|
|
54
|
+
pass an explicit `DestroyRef`) instead of hand-rolling a `Subject`+
|
|
55
|
+
`ngOnDestroy` teardown.
|
|
56
|
+
- Do not fan a single source Observable out into multiple independent
|
|
57
|
+
`.subscribe()` calls that each re-trigger the source's side effects
|
|
58
|
+
(e.g. a re-fetching HTTP call); share it (`shareReplay`, or convert once
|
|
59
|
+
with `toSignal()`) so the side effect runs once per meaningful change.
|
|
60
|
+
|
|
61
|
+
## Forms
|
|
62
|
+
|
|
63
|
+
- Prefer Angular's typed Reactive Forms (`FormGroup<{...}>`,
|
|
64
|
+
`FormControl<T>`) over template-driven forms for anything beyond a
|
|
65
|
+
trivial single-field form — typed forms catch a renamed/removed control
|
|
66
|
+
at compile time instead of a silent runtime `undefined`.
|
|
67
|
+
- Validate with Angular's built-in or custom `ValidatorFn`s registered on
|
|
68
|
+
the control, not ad-hoc validation logic scattered through submit-button
|
|
69
|
+
click handlers.
|
|
70
|
+
|
|
71
|
+
## Anti-patterns
|
|
72
|
+
|
|
73
|
+
- Do not reach into a child component's internals via `ViewChild` to call
|
|
74
|
+
a method or set a field when an `@Input()`/`input()`/signal binding, or
|
|
75
|
+
an `@Output()`/`output()` event, would express the same interaction
|
|
76
|
+
declaratively.
|
|
77
|
+
- Do not perform DOM manipulation directly through `ElementRef.nativeElement`
|
|
78
|
+
in ordinary component code — use Angular's `Renderer2`, template
|
|
79
|
+
bindings, or a directive when direct DOM access is genuinely required
|
|
80
|
+
(a third-party widget, focus management); bypassing Angular's rendering
|
|
81
|
+
layer breaks server-side rendering and change-detection assumptions.
|
|
82
|
+
- Do not put HTTP calls or other side effects inside a `computed()` — a
|
|
83
|
+
computed's callback can re-run any number of times and must stay pure;
|
|
84
|
+
side effects belong in `effect()` or an explicit event handler.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.ts", "**/*.html"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Angular security
|
|
9
|
+
|
|
10
|
+
Stack-specific risks on top of the common security rules, focused on where
|
|
11
|
+
Angular's own sanitization can be bypassed and where DI/routing choices
|
|
12
|
+
create an opening.
|
|
13
|
+
|
|
14
|
+
## Template injection and sanitization bypass
|
|
15
|
+
|
|
16
|
+
- Angular auto-sanitizes values bound into `innerHTML`, `href`,
|
|
17
|
+
style/URL/resource-URL contexts, and script/style properties — do not
|
|
18
|
+
reach for `DomSanitizer.bypassSecurityTrustHtml`/`bypassSecurityTrustUrl`/
|
|
19
|
+
`bypassSecurityTrustResourceUrl`/`bypassSecurityTrustScript` on
|
|
20
|
+
user-supplied or user-influenced content. Those calls tell Angular "trust
|
|
21
|
+
this value completely, skip sanitization" — reserve them for genuinely
|
|
22
|
+
static, developer-authored content (a hardcoded SVG, a known-safe
|
|
23
|
+
asset URL), never for a value that traces back to user input or an
|
|
24
|
+
external API response.
|
|
25
|
+
- Prefer binding through the normal property/interpolation syntax
|
|
26
|
+
(`{{ value }}`, `[textContent]`) over `[innerHTML]` for user-controlled
|
|
27
|
+
text; if `[innerHTML]` is genuinely required for rich content, sanitize
|
|
28
|
+
server-side or via a vetted sanitizer library rather than trusting
|
|
29
|
+
Angular's default sanitizer alone for anything beyond simple formatting.
|
|
30
|
+
- Never write directly to `ElementRef.nativeElement.innerHTML` — that
|
|
31
|
+
bypasses Angular's sanitizer entirely, unlike the `[innerHTML]` template
|
|
32
|
+
binding which still runs through it.
|
|
33
|
+
|
|
34
|
+
## HTTP and CSRF
|
|
35
|
+
|
|
36
|
+
- Send credentials/tokens via `HttpClient` interceptors (`HttpInterceptorFn`
|
|
37
|
+
or a class-based `HttpInterceptor`), not by hand-appending an
|
|
38
|
+
`Authorization` header at each call site — a missed call site is a
|
|
39
|
+
silent auth gap, not a build error.
|
|
40
|
+
- When the backend uses cookie-based sessions, rely on Angular's built-in
|
|
41
|
+
`XSRF-TOKEN`/`X-XSRF-TOKEN` CSRF handling (`withXsrfConfiguration` /
|
|
42
|
+
the default cookie name) rather than disabling `withCredentials` or
|
|
43
|
+
rolling a custom CSRF header scheme, unless the backend's own CSRF
|
|
44
|
+
contract genuinely requires different header/cookie names — configure
|
|
45
|
+
those, don't drop the protection.
|
|
46
|
+
|
|
47
|
+
## Routing and route guards
|
|
48
|
+
|
|
49
|
+
- Gate any route that requires authentication/authorization with a route
|
|
50
|
+
guard (`CanActivateFn`/`CanMatchFn`) that actually checks server-verified
|
|
51
|
+
state (a token's validity, a role from the authenticated session) — never
|
|
52
|
+
a guard that only checks a client-side flag with no corresponding
|
|
53
|
+
server-side check on the API the route's data comes from.
|
|
54
|
+
- Do not put secrets, API keys, or backend-only credentials into
|
|
55
|
+
environment files (`environment.ts`) that ship in the client bundle —
|
|
56
|
+
anything in `environment.*.ts` is public once built; only put
|
|
57
|
+
publicly-safe configuration (API base URLs, feature flags) there.
|
|
58
|
+
|
|
59
|
+
## Dependency injection surface
|
|
60
|
+
|
|
61
|
+
- Do not widen a service's provider scope to `providedIn: 'root'` just to
|
|
62
|
+
work around an injection error, when the service holds per-user or
|
|
63
|
+
per-session state — a root-scoped singleton is shared across the whole
|
|
64
|
+
app's lifetime (and across users in SSR), so session-scoped data leaking
|
|
65
|
+
into it is a cross-request data leak, not just a design smell.
|
|
66
|
+
- Validate and narrow data crossing a trust boundary (route params, query
|
|
67
|
+
params, a form's raw value, a third-party postMessage payload) before
|
|
68
|
+
using it in a template binding or passing it to a service call — treat
|
|
69
|
+
router/query-string values as untrusted input, not as pre-validated
|
|
70
|
+
application state.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.ts", "**/*.html"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Angular testing
|
|
9
|
+
|
|
10
|
+
Test layout, runner, and fixture conventions for Angular components,
|
|
11
|
+
services, and directives — narrows the common testing rules to
|
|
12
|
+
`TestBed`-based Angular idiom.
|
|
13
|
+
|
|
14
|
+
## Layout and runner
|
|
15
|
+
|
|
16
|
+
- Co-locate a unit test with its source (`order-summary.ts` /
|
|
17
|
+
`order-summary.spec.ts`), matching whatever the project's own existing
|
|
18
|
+
spec files already do; do not invent a parallel `__tests__/` tree in an
|
|
19
|
+
Angular CLI project that uses co-located `.spec.ts` files.
|
|
20
|
+
- Run the project's configured runner (`ng test`, or a Jest/Vitest setup if
|
|
21
|
+
the project has migrated off Karma) rather than assuming Karma is
|
|
22
|
+
present — check `angular.json`'s `test` builder or the project's
|
|
23
|
+
`package.json` `test` script before invoking a specific runner by name.
|
|
24
|
+
|
|
25
|
+
## Component tests
|
|
26
|
+
|
|
27
|
+
- Configure the module under test with `TestBed.configureTestingModule`
|
|
28
|
+
(importing the standalone component directly in `imports`, since
|
|
29
|
+
standalone components are imported, not declared), then
|
|
30
|
+
`TestBed.createComponent` to get a `ComponentFixture`.
|
|
31
|
+
- Set signal-based inputs through `fixture.componentRef.setInput('name',
|
|
32
|
+
value)`, not by assigning the component instance's field directly —
|
|
33
|
+
direct field assignment on a signal input does not go through Angular's
|
|
34
|
+
input-binding machinery and can silently not reflect in the template the
|
|
35
|
+
way a real caller's binding would.
|
|
36
|
+
- After a change that affects the DOM (setting an input, clicking a
|
|
37
|
+
button, resolving a promise/observable the component depends on), call
|
|
38
|
+
`fixture.detectChanges()` (or `await fixture.whenStable()` for
|
|
39
|
+
async work) before asserting on the rendered DOM — an assertion made
|
|
40
|
+
before change detection runs reads stale markup.
|
|
41
|
+
- Query the DOM through `fixture.debugElement.query(By.css(...))` /
|
|
42
|
+
`By.directive(...)`, or `fixture.nativeElement.querySelector(...)` for
|
|
43
|
+
plain DOM assertions — prefer a stable selector (a `data-testid`, a
|
|
44
|
+
semantic role/label) over a CSS class that styling changes could break.
|
|
45
|
+
|
|
46
|
+
## Services, HTTP, and doubles
|
|
47
|
+
|
|
48
|
+
- Test an injectable service through `TestBed.inject(MyService)` (or
|
|
49
|
+
direct instantiation for a service with no dependencies), not by
|
|
50
|
+
reaching into a component's private field to get at the service
|
|
51
|
+
instance.
|
|
52
|
+
- Mock `HttpClient`-backed calls with `provideHttpClientTesting()` /
|
|
53
|
+
`HttpTestingController` (`expectOne`, `.flush(...)`) rather than a
|
|
54
|
+
hand-rolled fetch stub — it verifies the exact request was made and lets
|
|
55
|
+
the test control the exact response, and `httpTestingController.verify()`
|
|
56
|
+
at the end of each test catches an unexpected extra request.
|
|
57
|
+
- Stub a genuinely external dependency (a third-party SDK, `window`/
|
|
58
|
+
browser APIs the test environment can't provide) at that boundary via
|
|
59
|
+
Angular's provider overrides (`TestBed.overrideProvider`) — do not mock
|
|
60
|
+
the component's own signals/services, which is what the test is supposed
|
|
61
|
+
to exercise.
|
|
62
|
+
|
|
63
|
+
## Determinism
|
|
64
|
+
|
|
65
|
+
- Prefer `fakeAsync`/`tick()` (or `waitForAsync`) over a real `setTimeout`
|
|
66
|
+
in a spec so a test doesn't depend on wall-clock timing; flush pending
|
|
67
|
+
timers/microtasks explicitly rather than adding an arbitrary `tick(1000)`
|
|
68
|
+
guess.
|
|
69
|
+
- Reset any shared test state (a spy's call count, a `TestBed` module
|
|
70
|
+
configured with `teardown: { destroyAfterEach: true }`, which is
|
|
71
|
+
Angular's own default) between tests so one spec's setup cannot leak
|
|
72
|
+
into the next — do not disable that default teardown to "fix" a
|
|
73
|
+
cross-test leak instead of finding the actual shared state.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: angular-build-fix
|
|
3
|
+
description: "Use when resolving an `ng build` failure, an AOT/Ivy template type-checking error (a property that doesn't exist on the bound type, an @if/@for control-flow type mismatch), a missing NgModule declaration/import error, or a NullInjectorError/'No provider for X' DI resolution error surfacing when the built app bootstraps. Applies the smallest root-cause fix and never silences the compiler with strictTemplates: false, an `any` cast in a template context, or `@Injectable` removal. Not for a plain TypeScript compile failure outside any Angular template, and not for authoring a fresh component or injectable from scratch (use angular-implementation)."
|
|
4
|
+
triggers:
|
|
5
|
+
- "fix this ng build AOT template type error"
|
|
6
|
+
- "resolve this Angular No provider for X DI error"
|
|
7
|
+
- "fix this Angular NG8103/missing NgModule declaration error"
|
|
8
|
+
- "ng build reports a template type-checking failure"
|
|
9
|
+
- "fix this standalone Angular component import error"
|
|
10
|
+
- "Angular build fails after adding an @if block referencing a signal"
|
|
11
|
+
metadata:
|
|
12
|
+
origin: authored
|
|
13
|
+
category: build-fix
|
|
14
|
+
version: "1.0.0"
|
|
15
|
+
compatible_harnesses: "claude,codex,cursor,zed,opencode"
|
|
16
|
+
license: "MIT"
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Angular build-fix (ng build / AOT / DI resolution)
|
|
20
|
+
|
|
21
|
+
Resolve an `ng build` failure: an AOT/Ivy template type-checking error, a
|
|
22
|
+
missing NgModule declaration/import, or a dependency-injection "No
|
|
23
|
+
provider for X" resolution error. Applies the smallest change that fixes
|
|
24
|
+
the actual root cause — never a change that only makes the compiler stop
|
|
25
|
+
complaining. See `rules/coding-style.mdc` and `rules/patterns.mdc` for the
|
|
26
|
+
typing/DI conventions a correct fix should restore.
|
|
27
|
+
|
|
28
|
+
## Workflow
|
|
29
|
+
|
|
30
|
+
### Step 1: Reproduce the failure
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
ng build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Run the project's own `package.json` build script if it wraps `ng build`
|
|
37
|
+
with extra flags (configuration, budgets). Capture the exact error
|
|
38
|
+
message, the file:line, and — for a template error — the template
|
|
39
|
+
expression Angular is complaining about, not just the `.ts` file it's
|
|
40
|
+
attributed to.
|
|
41
|
+
|
|
42
|
+
### Step 2: Classify the failure
|
|
43
|
+
|
|
44
|
+
- **Template type-checking error** (AOT/Ivy `strictTemplates`): a bound
|
|
45
|
+
property/method that doesn't exist on the component's type, an
|
|
46
|
+
`@if`/`@for` expression whose narrowed type doesn't match what the
|
|
47
|
+
template body assumes, or a mismatched event handler signature.
|
|
48
|
+
- **Missing declaration/import error** (e.g. `NG8001`/`NG8103`-style
|
|
49
|
+
"is not a known element" or "can't bind to X since it isn't a known
|
|
50
|
+
property"): a component/directive/pipe used in a template but not
|
|
51
|
+
imported into the standalone component's `imports` array (or not
|
|
52
|
+
declared/exported by the right `NgModule` in an NgModule-based project).
|
|
53
|
+
- **DI resolution error** ("No provider for X", `NullInjectorError`): a
|
|
54
|
+
service injected (via `inject()` or constructor) with no reachable
|
|
55
|
+
provider — missing `providedIn: 'root'`, missing from a component's/
|
|
56
|
+
route's `providers` array, or injected outside any injector context.
|
|
57
|
+
- **Build-tool/config error**: a genuine `angular.json`/`tsconfig.json`
|
|
58
|
+
misconfiguration (a wrong path, a missing budget) rather than a code
|
|
59
|
+
issue.
|
|
60
|
+
|
|
61
|
+
### Step 3: Find the root cause
|
|
62
|
+
|
|
63
|
+
**Template type errors**: trace the bound expression back to the
|
|
64
|
+
component class member it reads — the type Angular is checking against is
|
|
65
|
+
the actual declared type of that signal/field/getter, not a guess. A
|
|
66
|
+
template type error is usually telling the truth about a real mismatch
|
|
67
|
+
between the template and the class.
|
|
68
|
+
|
|
69
|
+
**Missing declaration/import**: check whether the component/directive/pipe
|
|
70
|
+
is standalone (needs adding to the consuming component's `imports`) or
|
|
71
|
+
NgModule-declared (needs adding to that module's `declarations`/`exports`,
|
|
72
|
+
and the consuming module needs to import it) — the fix differs by which
|
|
73
|
+
kind it is; check the component's own `@Component` decorator to tell.
|
|
74
|
+
|
|
75
|
+
**DI resolution**: find where the service is (or should be) provided —
|
|
76
|
+
check its own `@Injectable({ providedIn: ... })`, and any `providers`
|
|
77
|
+
array on the component/route/module in the injection path. A "No provider
|
|
78
|
+
for X" error means the injector tree that resolved this component/service
|
|
79
|
+
has no matching provider anywhere above it, not that the class itself is
|
|
80
|
+
broken.
|
|
81
|
+
|
|
82
|
+
### Step 4: Apply the smallest correct fix
|
|
83
|
+
|
|
84
|
+
- A template type error: fix the actual mismatch — correct the class
|
|
85
|
+
member's type, or correct the template expression reading it — not a
|
|
86
|
+
cast to `any` inside the template or a `$any()` template escape hatch
|
|
87
|
+
used to silence the checker.
|
|
88
|
+
- A missing declaration/import: add the component/directive/pipe to the
|
|
89
|
+
correct `imports` (standalone) or `declarations`/`exports`
|
|
90
|
+
(NgModule) — not a blanket `CUSTOM_ELEMENTS_SCHEMA`/`NO_ERRORS_SCHEMA`
|
|
91
|
+
addition that suppresses unknown-element checking project-wide.
|
|
92
|
+
- A DI resolution error: add the missing `providedIn`/`providers` entry at
|
|
93
|
+
the correct scope — not a workaround that constructs the service with
|
|
94
|
+
`new` instead of injecting it, which breaks DI's own singleton/testing
|
|
95
|
+
benefits.
|
|
96
|
+
- A config error: fix the actual `angular.json`/`tsconfig.json` setting
|
|
97
|
+
that's wrong — call out explicitly in the report that a config file
|
|
98
|
+
changed and why.
|
|
99
|
+
|
|
100
|
+
### Step 5: Verify and report
|
|
101
|
+
|
|
102
|
+
Re-run `ng build` (the exact command from Step 1); confirm it now exits 0.
|
|
103
|
+
Report the root cause and the fix, not just "build passes now".
|
|
104
|
+
|
|
105
|
+
## Red Flags
|
|
106
|
+
|
|
107
|
+
| Rationalization | Why it is wrong |
|
|
108
|
+
|---|---|
|
|
109
|
+
| "I'll wrap the template expression in `$any(...)` to stop the type error" | `$any()` disables type checking for that expression entirely — it hides the real mismatch instead of fixing the class member or the template expression reading it |
|
|
110
|
+
| "I'll set `strictTemplates: false` in tsconfig, then this whole class of error goes away" | That silences template type checking project-wide for every component, not just the one with the error — a genuine project-wide policy change, not a fix for this build failure |
|
|
111
|
+
| "I'll add `NO_ERRORS_SCHEMA` to fix this unknown-element error" | That tells Angular to stop validating unknown elements/attributes anywhere in that module, masking every future typo the same way, not just resolving this one missing import |
|
|
112
|
+
| "No provider for X — I'll just `new X()` instead of injecting it" | That bypasses Angular's DI entirely (no testability via TestBed overrides, no respecting the service's actual provider scope); find and fix the missing provider instead |
|
|
113
|
+
|
|
114
|
+
## Verification
|
|
115
|
+
|
|
116
|
+
Do not report the fix done until all of the following hold:
|
|
117
|
+
|
|
118
|
+
- The exact command that reproduced the failure in Step 1 (`ng build` or
|
|
119
|
+
the project's wrapping script) now exits 0.
|
|
120
|
+
- No `$any()`, template `any` cast, `NO_ERRORS_SCHEMA`/
|
|
121
|
+
`CUSTOM_ELEMENTS_SCHEMA` addition, or `strictTemplates`/`fullTemplateTypeCheck`
|
|
122
|
+
downgrade was added to silence the error.
|
|
123
|
+
- No dependency was constructed with `new` in place of a fixed DI
|
|
124
|
+
provider.
|
|
125
|
+
- The report states the actual root cause, not just "build passes now".
|
|
126
|
+
- `git status` shows changes confined to the files the root cause
|
|
127
|
+
required.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"triggers": {
|
|
3
|
+
"positive": [
|
|
4
|
+
"My Angular template throws 'Property does not exist on type OrderDraft' the moment I run a production build — how do I get it compiling again?",
|
|
5
|
+
"My Angular app throws a NullInjectorError -- 'No provider for OrdersService' -- the instant it bootstraps after a clean build; what's the right way to resolve this DI resolution error?",
|
|
6
|
+
"Fix this Angular error: 'app-order-card' is not a known element",
|
|
7
|
+
"After wiring a signal into a brand-new @if block, `ng build` now reports a template type-checking error it didn't throw before -- what's the right way to fix this Angular build failure?",
|
|
8
|
+
"My standalone Angular component won't compile because I forgot to import CommonModule for @if — how do I track down and fix missing imports like this?",
|
|
9
|
+
"Angular build fails with a template type mismatch on this @for loop"
|
|
10
|
+
],
|
|
11
|
+
"negative": [
|
|
12
|
+
"Fix this tsc type error in our Node.js backend with no Angular involved",
|
|
13
|
+
"Fix this Vue Vite build error about an unresolved SFC import",
|
|
14
|
+
"Implement a new Angular service that calls this REST API",
|
|
15
|
+
"Review this Angular component diff for OnPush mutation bugs",
|
|
16
|
+
"Fix this eslint no-floating-promises failure in our Express service",
|
|
17
|
+
"Write TestBed tests for this Angular component's HTTP calls"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"scenarios": [
|
|
21
|
+
{
|
|
22
|
+
"id": "no-dollar-any-template-escape",
|
|
23
|
+
"prompt": "ng build fails with a template type error: Property 'total' does not exist on type 'OrderDraft' in the template expression `{{ order.total }}`. What's the right way to fix this?",
|
|
24
|
+
"strictness": "high",
|
|
25
|
+
"expected_behavior": [
|
|
26
|
+
{
|
|
27
|
+
"grader": "judge",
|
|
28
|
+
"rubric": "A correct answer treats the template error as a genuine mismatch between the OrderDraft type and the template expression reading a total property from it, and fixes the type (or reads the correct property/derives the value) rather than silencing the checker with a $any() template escape hatch.",
|
|
29
|
+
"pass_criteria": [
|
|
30
|
+
"Names the concrete fix: adds a typed `total` property/accessor to OrderDraft, or changes the template to read/compute the correct existing property instead of a nonexistent `total`",
|
|
31
|
+
"Shows the corrected type declaration or corrected template expression, not just a statement that the type needs fixing"
|
|
32
|
+
],
|
|
33
|
+
"fail_criteria": [
|
|
34
|
+
"Recommends wrapping the expression in `$any(order).total` (or any `$any()` usage) as the fix",
|
|
35
|
+
"Recommends disabling strictTemplates/fullTemplateTypeCheck in tsconfig instead of fixing the actual mismatch"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"calibration": {
|
|
40
|
+
"known_right": "The template is telling the truth: OrderDraft genuinely has no `total` property, so `{{ order.total }}` fails Angular's template type check. Trace where OrderDraft objects are built to see which case applies. If every OrderDraft is meant to carry a running total, add it to the type:\n\ninterface OrderDraft {\n items: LineItem[];\n total: number;\n}\n\nIf total is meant to be derived, change the template (or a computed() the template reads) instead of adding a stored field:\n\ntotal = computed(() => this.order().items.reduce((sum, item) => sum + item.price, 0));\n\n... and bind `{{ total() }}` in the template. Either way, don't reach for `$any(order).total` -- that disables type checking for the whole expression instead of fixing the real mismatch. After the fix, re-run `ng build` and confirm it exits 0 with no new `$any()` calls added.",
|
|
41
|
+
"known_wrong": "Quickest fix: change the template to `{{ $any(order).total }}`. That tells Angular's template checker to stop type-checking the expression, so the build passes immediately without needing to touch the OrderDraft interface or figure out where total should actually come from.",
|
|
42
|
+
"vague": "Fix the OrderDraft type so it actually has what the template expects, rather than working around the checker.",
|
|
43
|
+
"subtle_wrong": "Since OrderDraft doesn't have total, I added it as `total: any` on the interface so the template compiles regardless of whether it ends up as a number or something else later. This gets the build green without needing to pin down the exact type right now."
|
|
44
|
+
},
|
|
45
|
+
"anti_patterns": ["$any("]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "fix-missing-provider-not-new",
|
|
49
|
+
"prompt": "The ng build itself succeeds, but the app throws at bootstrap: NullInjectorError: No provider for OrdersService!. What's the correct fix?",
|
|
50
|
+
"strictness": "high",
|
|
51
|
+
"expected_behavior": [
|
|
52
|
+
{
|
|
53
|
+
"grader": "judge",
|
|
54
|
+
"rubric": "A correct answer fixes the actual dependency-injection configuration -- adding providedIn: 'root' on the service, or adding it to the relevant component's/route's providers array -- rather than working around the error by constructing the service directly with `new` instead of injecting it.",
|
|
55
|
+
"pass_criteria": [
|
|
56
|
+
"Identifies that the fix is to provide the service correctly, e.g. `@Injectable({ providedIn: 'root' })` on OrdersService, or adding it to the consuming component's/route's providers array",
|
|
57
|
+
"Explains that the error means no injector in the resolution path has a provider for OrdersService, not that the service class itself is broken"
|
|
58
|
+
],
|
|
59
|
+
"fail_criteria": [
|
|
60
|
+
"Recommends replacing the injected/inject()-obtained OrdersService with `new OrdersService(...)` constructed manually as the fix"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"calibration": {
|
|
65
|
+
"known_right": "NullInjectorError means the injector tree resolving whatever calls `inject(OrdersService)` (or has it in its constructor) has no provider for it anywhere above it -- the class itself is fine, it's a configuration gap. Fix it at the provider level: if OrdersService should be an app-wide singleton, add `@Injectable({ providedIn: 'root' })` to its class decorator. If it's meant to be scoped to a specific feature/route instead, add `OrdersService` to that route's or that standalone component's `providers` array. Don't route around it with `new OrdersService(...)` -- that bypasses Angular's DI graph entirely, breaking TestBed overrides and any other service OrdersService itself depends on via injection. After adding the provider, re-run `ng build` (and the app) and confirm the error is gone.",
|
|
66
|
+
"known_wrong": "Simplest fix: instead of injecting it, just construct it directly wherever it's used: `private orders = new OrdersService();`. That sidesteps the whole provider error since you're not going through Angular's injector at all.",
|
|
67
|
+
"vague": "You need to make sure OrdersService is actually provided somewhere the injector can find it.",
|
|
68
|
+
"subtle_wrong": "I'd fix this by adding `providers: [OrdersService]` to every component that injects it, one by one, so each component has its own local instance and provider entry -- that guarantees no injector in the tree is ever missing it, without needing to figure out whether it should really be root-scoped or feature-scoped."
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|