@hellraisercenobit/ai-engineering-gate 0.8.0
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 +82 -0
- package/agents/design-pattern-reviewer.md +40 -0
- package/agents/modern-typescript-reviewer.md +24 -0
- package/agents/testing-pattern-reviewer.md +23 -0
- package/bin/ai-engineering-gate.mjs +6 -0
- package/contracts/axi/axi-standard.md +273 -0
- package/contracts/axi/pin.json +36 -0
- package/contracts/members.json +58 -0
- package/contracts/schemas/arbitration.schema.json +45 -0
- package/contracts/schemas/decision-envelope.schema.json +215 -0
- package/contracts/schemas/declaration.schema.json +190 -0
- package/contracts/schemas/dispute.schema.json +67 -0
- package/contracts/schemas/evidence-append.schema.json +72 -0
- package/contracts/schemas/gate-output.schema.json +296 -0
- package/contracts/schemas/marker.schema.json +75 -0
- package/contracts/schemas/review-envelope.schema.json +378 -0
- package/contracts/schemas/verdict-record.schema.json +191 -0
- package/contracts/suite-contract.md +270 -0
- package/dist/ai-engineering-gate.mjs +2762 -0
- package/package.json +47 -0
- package/skills/engineering/review-design-patterns/LICENSE +6 -0
- package/skills/engineering/review-design-patterns/SKILL.md +196 -0
- package/skills/engineering/review-design-patterns/agents/openai.yaml +3 -0
- package/skills/engineering/review-design-patterns/references/smell-signatures.md +117 -0
- package/skills/engineering/review-design-patterns/review-design-patterns.yaml +25 -0
- package/skills/engineering/review-modern-typescript/LICENSE +21 -0
- package/skills/engineering/review-modern-typescript/SKILL.md +88 -0
- package/skills/engineering/review-modern-typescript/agents/openai.yaml +3 -0
- package/skills/engineering/review-modern-typescript/references/smell-signatures.md +36 -0
- package/skills/engineering/review-modern-typescript/review-modern-typescript.yaml +22 -0
- package/skills/engineering/review-testing-patterns/SKILL.md +108 -0
- package/skills/engineering/review-testing-patterns/agents/openai.yaml +4 -0
- package/skills/engineering/review-testing-patterns/references/compiler-profile.mjs +103 -0
- package/skills/engineering/review-testing-patterns/references/smell-signatures.md +30 -0
- package/skills/engineering/review-testing-patterns/review-testing-patterns.yaml +23 -0
- package/skills/engineering/transpose-design-patterns/LICENSE +6 -0
- package/skills/engineering/transpose-design-patterns/SKILL.md +231 -0
- package/skills/engineering/transpose-design-patterns/agents/openai.yaml +3 -0
- package/skills/engineering/transpose-design-patterns/references/arbitration.schema.json +45 -0
- package/skills/engineering/transpose-design-patterns/references/decision-envelope.schema.json +215 -0
- package/skills/engineering/transpose-design-patterns/references/declaration.schema.json +190 -0
- package/skills/engineering/transpose-design-patterns/references/design-decision-record.schema.json +731 -0
- package/skills/engineering/transpose-design-patterns/references/dispute.schema.json +67 -0
- package/skills/engineering/transpose-design-patterns/references/evidence-append.schema.json +72 -0
- package/skills/engineering/transpose-design-patterns/references/marker.schema.json +75 -0
- package/skills/engineering/transpose-design-patterns/references/pattern-catalog.md +305 -0
- package/skills/engineering/transpose-design-patterns/references/record.example.json +120 -0
- package/skills/engineering/transpose-design-patterns/references/review-envelope.schema.json +378 -0
- package/skills/engineering/transpose-design-patterns/references/suite-contract.md +270 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-angular.md +224 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-php.md +561 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-quarkus.md +283 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-react.md +256 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-vanilla.md +204 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-vue.md +234 -0
- package/skills/engineering/transpose-design-patterns/transpose-design-patterns.yaml +24 -0
- package/skills/engineering/transpose-modern-typescript/LICENSE +21 -0
- package/skills/engineering/transpose-modern-typescript/SKILL.md +90 -0
- package/skills/engineering/transpose-modern-typescript/agents/openai.yaml +3 -0
- package/skills/engineering/transpose-modern-typescript/references/arbitration.schema.json +45 -0
- package/skills/engineering/transpose-modern-typescript/references/catalog.md +38 -0
- package/skills/engineering/transpose-modern-typescript/references/collections.md +50 -0
- package/skills/engineering/transpose-modern-typescript/references/compatibility.md +61 -0
- package/skills/engineering/transpose-modern-typescript/references/decision-envelope.schema.json +215 -0
- package/skills/engineering/transpose-modern-typescript/references/decision-record.schema.json +830 -0
- package/skills/engineering/transpose-modern-typescript/references/declaration.schema.json +190 -0
- package/skills/engineering/transpose-modern-typescript/references/dispute.schema.json +67 -0
- package/skills/engineering/transpose-modern-typescript/references/evidence-append.schema.json +72 -0
- package/skills/engineering/transpose-modern-typescript/references/idioms.md +74 -0
- package/skills/engineering/transpose-modern-typescript/references/marker.schema.json +75 -0
- package/skills/engineering/transpose-modern-typescript/references/platform.md +147 -0
- package/skills/engineering/transpose-modern-typescript/references/record.example.json +172 -0
- package/skills/engineering/transpose-modern-typescript/references/record.md +37 -0
- package/skills/engineering/transpose-modern-typescript/references/review-envelope.schema.json +378 -0
- package/skills/engineering/transpose-modern-typescript/references/suite-contract.md +270 -0
- package/skills/engineering/transpose-modern-typescript/references/types.md +97 -0
- package/skills/engineering/transpose-modern-typescript/transpose-modern-typescript.yaml +22 -0
- package/skills/engineering/transpose-testing-patterns/SKILL.md +96 -0
- package/skills/engineering/transpose-testing-patterns/agents/openai.yaml +4 -0
- package/skills/engineering/transpose-testing-patterns/references/arbitration.schema.json +45 -0
- package/skills/engineering/transpose-testing-patterns/references/catalog.md +218 -0
- package/skills/engineering/transpose-testing-patterns/references/decision-envelope.schema.json +215 -0
- package/skills/engineering/transpose-testing-patterns/references/decision-record.schema.json +857 -0
- package/skills/engineering/transpose-testing-patterns/references/declaration.schema.json +190 -0
- package/skills/engineering/transpose-testing-patterns/references/detect-adapter.mjs +98 -0
- package/skills/engineering/transpose-testing-patterns/references/dispute.schema.json +67 -0
- package/skills/engineering/transpose-testing-patterns/references/doubles-data.md +45 -0
- package/skills/engineering/transpose-testing-patterns/references/evidence-append.schema.json +72 -0
- package/skills/engineering/transpose-testing-patterns/references/journal-event.schema.json +115 -0
- package/skills/engineering/transpose-testing-patterns/references/marker.schema.json +75 -0
- package/skills/engineering/transpose-testing-patterns/references/record.example.json +230 -0
- package/skills/engineering/transpose-testing-patterns/references/record.md +25 -0
- package/skills/engineering/transpose-testing-patterns/references/review-envelope.schema.json +378 -0
- package/skills/engineering/transpose-testing-patterns/references/suite-contract.md +270 -0
- package/skills/engineering/transpose-testing-patterns/references/tdd.md +62 -0
- package/skills/engineering/transpose-testing-patterns/references/transpose-codeception.md +84 -0
- package/skills/engineering/transpose-testing-patterns/references/transpose-karma-jasmine-angular.md +73 -0
- package/skills/engineering/transpose-testing-patterns/references/transpose-vitest.md +111 -0
- package/skills/engineering/transpose-testing-patterns/references/typescript.md +42 -0
- package/skills/engineering/transpose-testing-patterns/transpose-testing-patterns.yaml +23 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Angular Transposition — Design Patterns (TypeScript 2026)
|
|
2
|
+
|
|
3
|
+
Generic, portable Angular wiring for the patterns in [`pattern-catalog.md`](pattern-catalog.md).
|
|
4
|
+
Framework-specific only — keep business logic framework-agnostic in the catalog. Examples use
|
|
5
|
+
throwaway domains (user, cart, payment, order); substitute your own types. This guide states **common**
|
|
6
|
+
Angular practice, not any single project's house rules.
|
|
7
|
+
|
|
8
|
+
## Angular building blocks
|
|
9
|
+
|
|
10
|
+
The transpositions below lean on a few modern-Angular defaults:
|
|
11
|
+
|
|
12
|
+
- **Standalone** components / directives / pipes — no `NgModule` unless wrapping a third-party API.
|
|
13
|
+
- **`inject()`** over constructor injection; **`InjectionToken<T>`** to depend on abstractions.
|
|
14
|
+
- **ECMAScript `#` private fields** for private members (runtime privacy) — not TypeScript `private`.
|
|
15
|
+
- **`signal` / `computed`** for state; expose state read-only.
|
|
16
|
+
- **Host metadata** via the `host` object — not `@HostListener` / `@HostBinding`:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
@Component({
|
|
20
|
+
selector: 'app-example',
|
|
21
|
+
host: { '[class.is-open]': 'open()', '(document:keydown.escape)': 'close()' },
|
|
22
|
+
})
|
|
23
|
+
export class ExampleComponent {
|
|
24
|
+
/* ... */
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Pattern → Angular wiring
|
|
29
|
+
|
|
30
|
+
### Strategy → `InjectionToken` + multi providers
|
|
31
|
+
|
|
32
|
+
Stateless strategies registered with `multi: true`; pick by key — no `switch`.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
export interface PaymentStrategy {
|
|
36
|
+
readonly key: string;
|
|
37
|
+
pay(amount: number): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export const PAYMENT_STRATEGIES = new InjectionToken<readonly PaymentStrategy[]>('PAYMENT_STRATEGIES');
|
|
40
|
+
|
|
41
|
+
providers: [
|
|
42
|
+
{ provide: PAYMENT_STRATEGIES, useClass: StripeStrategy, multi: true },
|
|
43
|
+
{ provide: PAYMENT_STRATEGIES, useClass: PaypalStrategy, multi: true },
|
|
44
|
+
];
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Registry → DI lookup over the provided implementations
|
|
48
|
+
|
|
49
|
+
Inject the multi-provided array; resolve by key. DI _is_ the registry.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
@Injectable({ providedIn: 'root' })
|
|
53
|
+
export class PaymentRegistry {
|
|
54
|
+
readonly #strategies = inject(PAYMENT_STRATEGIES);
|
|
55
|
+
get(key: string): PaymentStrategy | undefined {
|
|
56
|
+
return this.#strategies.find((s) => s.key === key);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Factory → `useFactory` provider or a factory function
|
|
62
|
+
|
|
63
|
+
Isolate creation logic; return an abstraction.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
export const LOGGER = new InjectionToken<Logger>('LOGGER');
|
|
67
|
+
|
|
68
|
+
providers: [{ provide: LOGGER, useFactory: () => (inject(ENV).production ? new RemoteLogger() : new ConsoleLogger()) }];
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Command → injectable use-case or plain function
|
|
72
|
+
|
|
73
|
+
Encapsulate one action; prefer a function or a small injectable over a heavy class.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
@Injectable({ providedIn: 'root' })
|
|
77
|
+
export class CheckoutCart {
|
|
78
|
+
readonly #orders = inject(OrderApi);
|
|
79
|
+
execute(cart: Cart): Promise<Order> {
|
|
80
|
+
return this.#orders.place(cart);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
A signal-store method (`@ngrx/signals` `withMethods`) is **not automatically** the Command — keep the action a distinct injectable use-case (or exported function) the store **delegates** to, provided where its dependencies live (route injector if route-scoped, `providedIn: 'root'` if app-wide). Full rule + layering: catalog → _Command_.
|
|
86
|
+
|
|
87
|
+
### Adapter / DTO Mapping → pure `from`/`to` at the service boundary
|
|
88
|
+
|
|
89
|
+
Map DTO ↔ domain inside the data-access service; never leak DTOs into components or templates.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// UserDto / User / userFromDto: see catalog -> Adapter / DTO Mapping
|
|
93
|
+
@Injectable({ providedIn: 'root' })
|
|
94
|
+
export class UserApi {
|
|
95
|
+
readonly #http = inject(HttpClient);
|
|
96
|
+
async getUser(id: string): Promise<User> {
|
|
97
|
+
return userFromDto(await firstValueFrom(this.#http.get<UserDto>(`/api/users/${id}`)));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Composition → standalone + DI + functional composition
|
|
103
|
+
|
|
104
|
+
Compose behavior through injected services, functions, and host directives — not inheritance.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
@Component({
|
|
108
|
+
selector: 'app-card',
|
|
109
|
+
hostDirectives: [HasTooltipDirective, TrackVisibilityDirective],
|
|
110
|
+
})
|
|
111
|
+
export class CardComponent {
|
|
112
|
+
/* behavior composed via directives + injected services, no base class */
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Singleton / shared state → `providedIn: 'root'` service exposing readonly signals
|
|
117
|
+
|
|
118
|
+
The common case: a root-provided service with private signals and a readonly/computed public surface.
|
|
119
|
+
For a large store, a signal-store library (e.g. `@ngrx/signals`) is a popular scale-up — same principle:
|
|
120
|
+
keep state private, expose it read-only.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
@Injectable({ providedIn: 'root' })
|
|
124
|
+
export class CartStore {
|
|
125
|
+
readonly #items = signal<readonly CartItem[]>([]);
|
|
126
|
+
readonly items = this.#items.asReadonly();
|
|
127
|
+
readonly total = computed(() => this.#items().reduce((sum, i) => sum + i.price, 0));
|
|
128
|
+
add(item: CartItem): void {
|
|
129
|
+
this.#items.update((xs) => [...xs, item]);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Cross-cutting Angular practice
|
|
135
|
+
|
|
136
|
+
### Signals for UI state
|
|
137
|
+
|
|
138
|
+
`signal` / `computed` for local and shared UI state. Avoid `BehaviorSubject` stores for UI state.
|
|
139
|
+
|
|
140
|
+
### RxJS only at async boundaries
|
|
141
|
+
|
|
142
|
+
Keep RxJS for `HttpClient`, websockets, streams. Convert early to promises / signals / plain values.
|
|
143
|
+
Common read: a service method `firstValueFrom(http.get<T>())` returning a typed value (map DTO→domain
|
|
144
|
+
there). Reactive read: `httpResource` / `resource`. Mutation: `firstValueFrom(http.post/put/...)`.
|
|
145
|
+
Route API calls through a service/facade rather than calling `HttpClient` from components.
|
|
146
|
+
|
|
147
|
+
### Keep components thin
|
|
148
|
+
|
|
149
|
+
Components orchestrate UI, bind state, trigger actions. Push logic and mapping into services and pure
|
|
150
|
+
functions.
|
|
151
|
+
|
|
152
|
+
### linkedSignal for controlled inputs
|
|
153
|
+
|
|
154
|
+
When an internal signal mirrors a parent `input()`, use `linkedSignal` (auto-resets on source change),
|
|
155
|
+
not an `effect()`.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
protected readonly value = linkedSignal(() => this.selected() ?? null);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Avoid business logic in effects
|
|
162
|
+
|
|
163
|
+
Put side effects in the method that triggers the change, not in an `effect()` watching state.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
select(user: User): void {
|
|
167
|
+
this.#store.setUser(user);
|
|
168
|
+
this.#analytics.track(user.id); // not inside effect(() => track(this.user()?.id))
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Template SRP
|
|
173
|
+
|
|
174
|
+
No business logic in templates. Move ternaries, multi-condition `@if`, and data-transforming calls into
|
|
175
|
+
`computed`.
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
// Bad (template): {{ user().isActive ? 'Active' : 'Inactive' }}
|
|
179
|
+
// Good (component):
|
|
180
|
+
protected readonly statusLabel = computed(() => (this.user().isActive ? 'Active' : 'Inactive'));
|
|
181
|
+
// template: {{ statusLabel() }}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Forms
|
|
185
|
+
|
|
186
|
+
Typed **Reactive Forms** are the common default for non-trivial forms. **Signal Forms**
|
|
187
|
+
(`@angular/forms/signals`) are a newer, signal-native option (experimental upstream) for signal-driven
|
|
188
|
+
validation. Either way, keep validation rules in pure functions that the form just calls.
|
|
189
|
+
|
|
190
|
+
## Angular Decision Matrix
|
|
191
|
+
|
|
192
|
+
| Problem | Recommended Solution |
|
|
193
|
+
| ----------------------------------------- | --------------------------------------------------------- |
|
|
194
|
+
| Local / shared UI state | signal / computed (root service for shared) |
|
|
195
|
+
| Controlled input sync | linkedSignal |
|
|
196
|
+
| Derived template value | protected computed in the component |
|
|
197
|
+
| Private class member | ECMAScript `#` field |
|
|
198
|
+
| Depend on an abstraction | InjectionToken<T> + inject() |
|
|
199
|
+
| Interchangeable behaviors | Strategy (InjectionToken + multi providers) |
|
|
200
|
+
| Plugin / extension system | Registry (DI lookup over multi providers) |
|
|
201
|
+
| Object creation with logic | Factory (useFactory / factory function) |
|
|
202
|
+
| Reusable action | Command (injectable use-case or function) |
|
|
203
|
+
| API ↔ domain shape mismatch | Adapter — fromDto / toDto pure functions at the service |
|
|
204
|
+
| Cross-cutting behavior | Composition (DI, functions, host directives) |
|
|
205
|
+
| Shared services / app state | Singleton DI (`providedIn: 'root'`) |
|
|
206
|
+
| HTTP read | service method: firstValueFrom(http.get) → map DTO→domain |
|
|
207
|
+
| HTTP reactive read | httpResource / resource |
|
|
208
|
+
| HTTP mutation | firstValueFrom(http.post/put/delete) in a service |
|
|
209
|
+
| Async stream | RxJS |
|
|
210
|
+
| Host class / event / `document:` listener | `host: { ... }` on @Component / @Directive |
|
|
211
|
+
| Form | Reactive Forms (default) / Signal Forms (signal-native) |
|
|
212
|
+
|
|
213
|
+
## Anti-Patterns to Avoid
|
|
214
|
+
|
|
215
|
+
- business logic in components; DTOs leaking into templates; giant services; string magic / `switch` on a type tag
|
|
216
|
+
- `BehaviorSubject` stores for UI state instead of signals
|
|
217
|
+
- exposing writable signals or the whole store object from a service — expose readonly / computed
|
|
218
|
+
- TypeScript `private` instead of ECMAScript `#`
|
|
219
|
+
- complex conditions / domain ternaries in templates — move to `computed`
|
|
220
|
+
- `effect()` for input→signal sync (use `linkedSignal`); business logic inside `effect()`
|
|
221
|
+
- calling `HttpClient` from components — go through a service/facade
|
|
222
|
+
- `@HostListener` / `@HostBinding` in new code — use the `host` object
|
|
223
|
+
- a Command merged into a store/facade method when it must be reusable — keep it a distinct injectable use-case the store delegates to
|
|
224
|
+
- inheritance-heavy Angular code; RxJS for local state; global mutable signals; module-level `let`
|