@webiny/mcp 6.2.0 → 6.3.0-beta.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.
@@ -34,24 +34,26 @@ import { SomeUseCase } from "webiny/api/<category>";
34
34
  import { SomeEventHandler } from "webiny/api/<category>";
35
35
 
36
36
  class MyHandler implements SomeEventHandler.Interface {
37
- constructor(private someUseCase: SomeUseCase.Interface) {}
37
+ constructor(private someUseCase: SomeUseCase.Interface) {}
38
38
 
39
- async handle(event: SomeEventHandler.Event) {
40
- const result = await this.someUseCase.execute({ /* input */ });
39
+ async handle(event: SomeEventHandler.Event) {
40
+ const result = await this.someUseCase.execute({
41
+ /* input */
42
+ });
41
43
 
42
- if (result.isFail()) {
43
- console.error(result.error.message);
44
- return;
45
- }
46
-
47
- const value = result.value;
48
- // ... use value
44
+ if (result.isFail()) {
45
+ console.error(result.error.message);
46
+ return;
49
47
  }
48
+
49
+ const value = result.value;
50
+ // ... use value
51
+ }
50
52
  }
51
53
 
52
54
  export default SomeEventHandler.createImplementation({
53
- implementation: MyHandler,
54
- dependencies: [SomeUseCase]
55
+ implementation: MyHandler,
56
+ dependencies: [SomeUseCase]
55
57
  });
56
58
  ```
57
59
 
@@ -63,15 +65,15 @@ To replace the default implementation, register your own:
63
65
  import { SomeUseCase } from "webiny/api/<category>";
64
66
 
65
67
  class CustomImplementation implements SomeUseCase.Interface {
66
- async execute(input) {
67
- // Custom logic
68
- return Result.ok(/* ... */);
69
- }
68
+ async execute(input) {
69
+ // Custom logic
70
+ return Result.ok(/* ... */);
71
+ }
70
72
  }
71
73
 
72
74
  export default SomeUseCase.createImplementation({
73
- implementation: CustomImplementation,
74
- dependencies: []
75
+ implementation: CustomImplementation,
76
+ dependencies: []
75
77
  });
76
78
  ```
77
79
 
@@ -101,24 +103,24 @@ Every feature defines errors extending `BaseError`. Never use generic `Error` fo
101
103
  import { BaseError } from "@webiny/feature/api";
102
104
 
103
105
  export class EntityNotFoundError extends BaseError {
104
- override readonly code = "Entity/NotFound" as const;
105
- constructor(id: string) {
106
- super({ message: `Entity with id "${id}" was not found!` });
107
- }
106
+ override readonly code = "Entity/NotFound" as const;
107
+ constructor(id: string) {
108
+ super({ message: `Entity with id "${id}" was not found!` });
109
+ }
108
110
  }
109
111
 
110
112
  export class EntityPersistenceError extends BaseError<{ error: Error }> {
111
- override readonly code = "Entity/Persist" as const;
112
- constructor(error: Error) {
113
- super({ message: error.message, data: { error } });
114
- }
113
+ override readonly code = "Entity/Persist" as const;
114
+ constructor(error: Error) {
115
+ super({ message: error.message, data: { error } });
116
+ }
115
117
  }
116
118
 
117
119
  export class EntityValidationError extends BaseError<{ message: string }> {
118
- override readonly code = "Entity/Validation" as const;
119
- constructor(message: string) {
120
- super({ message, data: { message } });
121
- }
120
+ override readonly code = "Entity/Validation" as const;
121
+ constructor(message: string) {
122
+ super({ message, data: { message } });
123
+ }
122
124
  }
123
125
  ```
124
126
 
@@ -130,54 +132,58 @@ Define an `IErrors` interface mapping error names to types, then create a union
130
132
  // features/createEntity/abstractions.ts
131
133
  import { createAbstraction, Result } from "@webiny/feature/api";
132
134
  import { NotAuthorizedError } from "@webiny/api-core/features/security/shared/errors.js";
133
- import { EntityPersistenceError, EntityModelNotFoundError, EntityCreationError } from "~/api/domain/errors.js";
135
+ import {
136
+ EntityPersistenceError,
137
+ EntityModelNotFoundError,
138
+ EntityCreationError
139
+ } from "~/api/domain/errors.js";
134
140
 
135
141
  // REPOSITORY errors
136
142
  export interface ICreateEntityRepositoryErrors {
137
- persistence: EntityPersistenceError;
138
- modelNotFound: EntityModelNotFoundError;
139
- creation: EntityCreationError;
143
+ persistence: EntityPersistenceError;
144
+ modelNotFound: EntityModelNotFoundError;
145
+ creation: EntityCreationError;
140
146
  }
141
147
 
142
148
  type RepositoryError = ICreateEntityRepositoryErrors[keyof ICreateEntityRepositoryErrors];
143
149
 
144
150
  export interface ICreateEntityRepository {
145
- execute(entity: Entity): Promise<Result<Entity, RepositoryError>>;
151
+ execute(entity: Entity): Promise<Result<Entity, RepositoryError>>;
146
152
  }
147
153
 
148
154
  export const CreateEntityRepository = createAbstraction<ICreateEntityRepository>(
149
- "MyExt/CreateEntityRepository"
155
+ "MyExt/CreateEntityRepository"
150
156
  );
151
157
 
152
158
  export namespace CreateEntityRepository {
153
- export type Interface = ICreateEntityRepository;
154
- export type Error = RepositoryError;
155
- export type Return = Promise<Result<Entity, RepositoryError>>;
159
+ export type Interface = ICreateEntityRepository;
160
+ export type Error = RepositoryError;
161
+ export type Return = Promise<Result<Entity, RepositoryError>>;
156
162
  }
157
163
 
158
164
  // USE CASE errors — superset of repository errors
159
165
  export interface ICreateEntityUseCaseErrors {
160
- persistence: EntityPersistenceError;
161
- modelNotFound: EntityModelNotFoundError;
162
- creation: EntityCreationError;
163
- notAuthorized: NotAuthorizedError;
166
+ persistence: EntityPersistenceError;
167
+ modelNotFound: EntityModelNotFoundError;
168
+ creation: EntityCreationError;
169
+ notAuthorized: NotAuthorizedError;
164
170
  }
165
171
 
166
172
  type UseCaseError = ICreateEntityUseCaseErrors[keyof ICreateEntityUseCaseErrors];
167
173
 
168
174
  export interface ICreateEntityUseCase {
169
- execute(input: CreateEntityInput): Promise<Result<Entity, UseCaseError>>;
175
+ execute(input: CreateEntityInput): Promise<Result<Entity, UseCaseError>>;
170
176
  }
171
177
 
172
178
  export const CreateEntityUseCase = createAbstraction<ICreateEntityUseCase>(
173
- "MyExt/CreateEntityUseCase"
179
+ "MyExt/CreateEntityUseCase"
174
180
  );
175
181
 
176
182
  export namespace CreateEntityUseCase {
177
- export type Interface = ICreateEntityUseCase;
178
- export type Input = CreateEntityInput;
179
- export type Error = UseCaseError;
180
- export type Return = Promise<Result<Entity, UseCaseError>>;
183
+ export type Interface = ICreateEntityUseCase;
184
+ export type Input = CreateEntityInput;
185
+ export type Error = UseCaseError;
186
+ export type Return = Promise<Result<Entity, UseCaseError>>;
181
187
  }
182
188
  ```
183
189
 
@@ -192,7 +198,7 @@ return Result.fail(new EntityNotFoundError(id));
192
198
 
193
199
  // Check result
194
200
  if (result.isFail()) {
195
- return Result.fail(result.error);
201
+ return Result.fail(result.error);
196
202
  }
197
203
 
198
204
  // Access value
@@ -207,7 +213,10 @@ Never use `result.isError()`, `result.getError()`, or `result.getValue()` — th
207
213
 
208
214
  ```ts
209
215
  // features/createEntity/CreateEntityUseCase.ts
210
- import { CreateEntityUseCase as UseCaseAbstraction, CreateEntityRepository } from "./abstractions.js";
216
+ import {
217
+ CreateEntityUseCase as UseCaseAbstraction,
218
+ CreateEntityRepository
219
+ } from "./abstractions.js";
211
220
  import { Result } from "@webiny/feature/api";
212
221
  import { IdentityContext } from "@webiny/api-core/exports/api/security.js";
213
222
  import { NotAuthorizedError } from "@webiny/api-core/features/security/shared/errors.js";
@@ -215,37 +224,38 @@ import { Entity } from "~/shared/Entity.js";
215
224
  import { EntityId } from "~/api/domain/EntityId.js";
216
225
 
217
226
  class CreateEntityUseCase implements UseCaseAbstraction.Interface {
218
- constructor(
219
- private identityContext: IdentityContext.Interface,
220
- private repository: CreateEntityRepository.Interface
221
- ) {}
222
-
223
- async execute(input: UseCaseAbstraction.Input): UseCaseAbstraction.Return {
224
- if (!this.identityContext.getPermission("mypackage.entity")) {
225
- return Result.fail(new NotAuthorizedError({ message: "Not authorized to create entities!" }));
226
- }
227
-
228
- const entity = Entity.from({
229
- id: EntityId.from(input.id),
230
- values: { name: input.name, status: "disabled" }
231
- });
232
-
233
- const result = await this.repository.execute(entity);
234
- if (result.isFail()) {
235
- return Result.fail(result.error);
236
- }
237
-
238
- return Result.ok(result.value);
227
+ constructor(
228
+ private identityContext: IdentityContext.Interface,
229
+ private repository: CreateEntityRepository.Interface
230
+ ) {}
231
+
232
+ async execute(input: UseCaseAbstraction.Input): UseCaseAbstraction.Return {
233
+ if (!this.identityContext.getPermission("mypackage.entity")) {
234
+ return Result.fail(new NotAuthorizedError({ message: "Not authorized to create entities!" }));
239
235
  }
236
+
237
+ const entity = Entity.from({
238
+ id: EntityId.from(input.id),
239
+ values: { name: input.name, status: "disabled" }
240
+ });
241
+
242
+ const result = await this.repository.execute(entity);
243
+ if (result.isFail()) {
244
+ return Result.fail(result.error);
245
+ }
246
+
247
+ return Result.ok(result.value);
248
+ }
240
249
  }
241
250
 
242
251
  export default UseCaseAbstraction.createImplementation({
243
- implementation: CreateEntityUseCase,
244
- dependencies: [IdentityContext, CreateEntityRepository]
252
+ implementation: CreateEntityUseCase,
253
+ dependencies: [IdentityContext, CreateEntityRepository]
245
254
  });
246
255
  ```
247
256
 
248
257
  **Rules:**
258
+
249
259
  - Class implements `UseCaseAbstraction.Interface`
250
260
  - Constructor params typed with `.Interface` from their abstractions
251
261
  - Return type uses `UseCaseAbstraction.Return`
@@ -269,36 +279,36 @@ import { GetModelUseCase } from "@webiny/api-headless-cms/exports/api/cms/model"
269
279
  import { ENTITY_MODEL_ID } from "~/shared/constants.js";
270
280
 
271
281
  class CreateEntityRepository implements RepositoryAbstraction.Interface {
272
- constructor(
273
- private getModelUseCase: GetModelUseCase.Interface,
274
- private createEntryUseCase: CreateEntryUseCase.Interface
275
- ) {}
276
-
277
- async execute(entity: Entity): RepositoryAbstraction.Return {
278
- const modelResult = await this.getModelUseCase.execute(ENTITY_MODEL_ID);
279
- if (modelResult.isFail()) {
280
- return Result.fail(new EntityModelNotFoundError());
281
- }
282
-
283
- const createResult = await this.createEntryUseCase.execute(modelResult.value, {
284
- id: entity.id,
285
- values: {
286
- name: entity.values.name,
287
- status: entity.values.status
288
- }
289
- });
290
-
291
- if (createResult.isFail()) {
292
- return Result.fail(new EntityCreationError(createResult.error));
293
- }
294
-
295
- return Result.ok(entity);
282
+ constructor(
283
+ private getModelUseCase: GetModelUseCase.Interface,
284
+ private createEntryUseCase: CreateEntryUseCase.Interface
285
+ ) {}
286
+
287
+ async execute(entity: Entity): RepositoryAbstraction.Return {
288
+ const modelResult = await this.getModelUseCase.execute(ENTITY_MODEL_ID);
289
+ if (modelResult.isFail()) {
290
+ return Result.fail(new EntityModelNotFoundError());
296
291
  }
292
+
293
+ const createResult = await this.createEntryUseCase.execute(modelResult.value, {
294
+ id: entity.id,
295
+ values: {
296
+ name: entity.values.name,
297
+ status: entity.values.status
298
+ }
299
+ });
300
+
301
+ if (createResult.isFail()) {
302
+ return Result.fail(new EntityCreationError(createResult.error));
303
+ }
304
+
305
+ return Result.ok(entity);
306
+ }
297
307
  }
298
308
 
299
309
  export default RepositoryAbstraction.createImplementation({
300
- implementation: CreateEntityRepository,
301
- dependencies: [GetModelUseCase, CreateEntryUseCase]
310
+ implementation: CreateEntityRepository,
311
+ dependencies: [GetModelUseCase, CreateEntryUseCase]
302
312
  });
303
313
  ```
304
314
 
@@ -316,6 +326,7 @@ import { ListModelsUseCase } from "@webiny/api-headless-cms/exports/api/cms/mode
316
326
  ```
317
327
 
318
328
  **Rules:**
329
+
319
330
  - Always resolve the CMS model first via `GetModelUseCase`
320
331
  - Wrap CMS errors in domain-specific errors
321
332
  - Register repositories in **singleton scope**
@@ -333,12 +344,12 @@ import { Entity as EntityClass } from "~/shared/Entity.js";
333
344
  import type { Entity, EntityDto, EntityValues } from "~/shared/Entity.js";
334
345
 
335
346
  export class EntryToEntityMapper {
336
- static toEntity(entry: { entryId: string; values: EntityValues }): Entity {
337
- return EntityClass.from({
338
- id: entry.entryId,
339
- values: entry.values
340
- });
341
- }
347
+ static toEntity(entry: { entryId: string; values: EntityValues }): Entity {
348
+ return EntityClass.from({
349
+ id: entry.entryId,
350
+ values: entry.values
351
+ });
352
+ }
342
353
  }
343
354
  ```
344
355
 
@@ -360,22 +371,22 @@ import { IdentityContext } from "@webiny/api-core/exports/api/security.js";
360
371
  import { NotAuthorizedError } from "@webiny/api-core/features/security/shared/errors.js";
361
372
 
362
373
  class GetEntityByIdWithAuthorizationImpl implements GetEntityByIdUseCase.Interface {
363
- constructor(
364
- private identityContext: IdentityContext.Interface,
365
- private decoratee: GetEntityByIdUseCase.Interface // decoratee is LAST
366
- ) {}
367
-
368
- async execute(id: string): GetEntityByIdUseCase.Return {
369
- if (!this.identityContext.getPermission("mypackage.entity")) {
370
- return Result.fail(new NotAuthorizedError());
371
- }
372
- return this.decoratee.execute(id);
374
+ constructor(
375
+ private identityContext: IdentityContext.Interface,
376
+ private decoratee: GetEntityByIdUseCase.Interface // decoratee is LAST
377
+ ) {}
378
+
379
+ async execute(id: string): GetEntityByIdUseCase.Return {
380
+ if (!this.identityContext.getPermission("mypackage.entity")) {
381
+ return Result.fail(new NotAuthorizedError());
373
382
  }
383
+ return this.decoratee.execute(id);
384
+ }
374
385
  }
375
386
 
376
387
  export const GetEntityByIdWithAuthorization = GetEntityByIdUseCase.createDecorator({
377
- decorator: GetEntityByIdWithAuthorizationImpl,
378
- dependencies: [IdentityContext] // does NOT include decoratee
388
+ decorator: GetEntityByIdWithAuthorizationImpl,
389
+ dependencies: [IdentityContext] // does NOT include decoratee
379
390
  });
380
391
  ```
381
392
 
@@ -389,16 +400,17 @@ import GetEntityByIdRepository from "./GetEntityByIdRepository.js";
389
400
  import { GetEntityByIdWithAuthorization } from "./decorators/GetEntityByIdWithAuthorization.js";
390
401
 
391
402
  export const GetEntityByIdFeature = createFeature({
392
- name: "GetEntityById",
393
- register(container) {
394
- container.register(GetEntityByIdUseCase);
395
- container.register(GetEntityByIdRepository).inSingletonScope();
396
- container.registerDecorator(GetEntityByIdWithAuthorization);
397
- }
403
+ name: "GetEntityById",
404
+ register(container) {
405
+ container.register(GetEntityByIdUseCase);
406
+ container.register(GetEntityByIdRepository).inSingletonScope();
407
+ container.registerDecorator(GetEntityByIdWithAuthorization);
408
+ }
398
409
  });
399
410
  ```
400
411
 
401
412
  **Rules:**
413
+
402
414
  - Implements the same interface as the use case it decorates
403
415
  - Constructor: extra dependencies first, `decoratee` **last**
404
416
  - Use `UseCaseAbstraction.createDecorator(...)` — the `dependencies` array does NOT include the decoratee
@@ -410,6 +422,7 @@ export const GetEntityByIdFeature = createFeature({
410
422
  ## Schema-Based Permissions
411
423
 
412
424
  For implementing authorization in use cases, see the **webiny-api-permissions** skill. It covers:
425
+
413
426
  - Permission schema definition with `createPermissions`
414
427
  - All permission methods (`canRead`, `canEdit`, `canDelete`, `canPublish`, `onlyOwnRecords`, etc.)
415
428
  - Use case patterns for every CRUD operation (get, list, update, delete, publish)
@@ -154,11 +154,11 @@ export default CorePulumi.createImplementation({
154
154
 
155
155
  The `dependencies` array supports three forms per entry:
156
156
 
157
- | Form | Meaning |
158
- |------|---------|
159
- | `Abstraction` | Single required dependency (shorthand) |
160
- | `[Abstraction, { optional: true }]` | Single optional dependency — injects `undefined` if not registered |
161
- | `[Abstraction, { multiple: true }]` | Multi-injection — injects **all** registered implementations as `T[]` |
157
+ | Form | Meaning |
158
+ | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
159
+ | `Abstraction` | Single required dependency (shorthand) |
160
+ | `[Abstraction, { optional: true }]` | Single optional dependency — injects `undefined` if not registered |
161
+ | `[Abstraction, { multiple: true }]` | Multi-injection — injects **all** registered implementations as `T[]` |
162
162
  | `[Abstraction, { multiple: true, optional: true }]` | Multi-injection, optional — injects `undefined` if none registered (vs empty `[]` with just `multiple`) |
163
163
 
164
164
  ### Multi-injection (`{ multiple: true }`)
@@ -169,15 +169,15 @@ Use when a class needs all registered implementations of an abstraction. The con
169
169
 
170
170
  ```ts
171
171
  interface IPageType {
172
- name: string;
173
- label: string;
174
- modify(form: IFormModel): void;
172
+ name: string;
173
+ label: string;
174
+ modify(form: IFormModel): void;
175
175
  }
176
176
 
177
177
  export const PageType = createAbstraction<IPageType>("PageType");
178
178
 
179
179
  export namespace PageType {
180
- export type Interface = IPageType;
180
+ export type Interface = IPageType;
181
181
  }
182
182
  ```
183
183
 
@@ -186,32 +186,34 @@ export namespace PageType {
186
186
  ```ts
187
187
  // StaticPageType.ts
188
188
  class StaticPageTypeImpl implements PageType.Interface {
189
- name = "static";
190
- label = "Static Page";
191
- modify(form: IFormModel) { /* no-op — base form is sufficient */ }
189
+ name = "static";
190
+ label = "Static Page";
191
+ modify(form: IFormModel) {
192
+ /* no-op — base form is sufficient */
193
+ }
192
194
  }
193
195
 
194
196
  export const StaticPageType = PageType.createImplementation({
195
- implementation: StaticPageTypeImpl,
196
- dependencies: []
197
+ implementation: StaticPageTypeImpl,
198
+ dependencies: []
197
199
  });
198
200
 
199
201
  // ProductPageType.ts (in another package/extension)
200
202
  class ProductPageTypeImpl implements PageType.Interface {
201
- name = "product";
202
- label = "Product Page";
203
- modify(form: IFormModel) {
204
- form.fields(fields => ({
205
- product: fields.select().label("Product").required("Product is required")
206
- }));
207
- form.field("title").disabled(true);
208
- form.field("path").disabled(true);
209
- }
203
+ name = "product";
204
+ label = "Product Page";
205
+ modify(form: IFormModel) {
206
+ form.fields(fields => ({
207
+ product: fields.select().label("Product").required("Product is required")
208
+ }));
209
+ form.field("title").disabled(true);
210
+ form.field("path").disabled(true);
211
+ }
210
212
  }
211
213
 
212
214
  export const ProductPageType = PageType.createImplementation({
213
- implementation: ProductPageTypeImpl,
214
- dependencies: []
215
+ implementation: ProductPageTypeImpl,
216
+ dependencies: []
215
217
  });
216
218
  ```
217
219
 
@@ -219,20 +221,20 @@ export const ProductPageType = PageType.createImplementation({
219
221
 
220
222
  ```ts
221
223
  class CreatePagePresenterImpl implements CreatePagePresenter.Interface {
222
- constructor(
223
- private factory: FormModelFactory.Interface,
224
- private pageTypes: PageType.Interface[],
225
- private modifiers: CreatePageFormModifier.Interface[]
226
- ) {}
224
+ constructor(
225
+ private factory: FormModelFactory.Interface,
226
+ private pageTypes: PageType.Interface[],
227
+ private modifiers: CreatePageFormModifier.Interface[]
228
+ ) {}
227
229
  }
228
230
 
229
231
  export const CreatePagePresenter = PresenterAbstraction.createImplementation({
230
- implementation: CreatePagePresenterImpl,
231
- dependencies: [
232
- FormModelFactory,
233
- [PageType, { multiple: true }],
234
- [CreatePageFormModifier, { multiple: true }]
235
- ]
232
+ implementation: CreatePagePresenterImpl,
233
+ dependencies: [
234
+ FormModelFactory,
235
+ [PageType, { multiple: true }],
236
+ [CreatePageFormModifier, { multiple: true }]
237
+ ]
236
238
  });
237
239
  ```
238
240
 
@@ -240,12 +242,12 @@ export const CreatePagePresenter = PresenterAbstraction.createImplementation({
240
242
 
241
243
  ```ts
242
244
  export const CreatePageFeature = createFeature({
243
- name: "CreatePage",
244
- register(container) {
245
- container.register(StaticPageType); // first PageType impl
246
- container.register(ProductPageType); // second PageType impl
247
- container.register(CreatePagePresenter);
248
- }
245
+ name: "CreatePage",
246
+ register(container) {
247
+ container.register(StaticPageType); // first PageType impl
248
+ container.register(ProductPageType); // second PageType impl
249
+ container.register(CreatePagePresenter);
250
+ }
249
251
  });
250
252
  ```
251
253
 
@@ -257,18 +259,15 @@ Use when a dependency may not be registered. The container injects `undefined` i
257
259
 
258
260
  ```ts
259
261
  class MyPresenterImpl {
260
- constructor(
261
- private required: RequiredService.Interface,
262
- private analytics: AnalyticsService.Interface | undefined
263
- ) {}
262
+ constructor(
263
+ private required: RequiredService.Interface,
264
+ private analytics: AnalyticsService.Interface | undefined
265
+ ) {}
264
266
  }
265
267
 
266
268
  export const MyPresenter = Abstraction.createImplementation({
267
- implementation: MyPresenterImpl,
268
- dependencies: [
269
- RequiredService,
270
- [AnalyticsService, { optional: true }]
271
- ]
269
+ implementation: MyPresenterImpl,
270
+ dependencies: [RequiredService, [AnalyticsService, { optional: true }]]
272
271
  });
273
272
  ```
274
273
 
@@ -276,21 +275,21 @@ export const MyPresenter = Abstraction.createImplementation({
276
275
 
277
276
  ### Registration
278
277
 
279
- | Method | Description |
280
- |--------|-------------|
281
- | `container.register(Impl)` | Register a class implementation. Returns `RegistrationBuilder` with `.inSingletonScope()`. Multiple registrations of the same abstraction accumulate — `resolve()` returns the last, `resolveAll()` returns all. |
282
- | `container.registerInstance(Abstraction, instance)` | Register a pre-built instance (no constructor resolution). |
283
- | `container.registerFactory(Abstraction, factory)` | Register a factory function. Called on every `resolve()`. |
284
- | `container.registerDecorator(Decorator)` | Register a decorator that wraps resolved instances. Applied in registration order. |
285
- | `container.registerComposite(Composite)` | Register a composite that aggregates all implementations behind a single `resolve()`. |
278
+ | Method | Description |
279
+ | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
280
+ | `container.register(Impl)` | Register a class implementation. Returns `RegistrationBuilder` with `.inSingletonScope()`. Multiple registrations of the same abstraction accumulate — `resolve()` returns the last, `resolveAll()` returns all. |
281
+ | `container.registerInstance(Abstraction, instance)` | Register a pre-built instance (no constructor resolution). |
282
+ | `container.registerFactory(Abstraction, factory)` | Register a factory function. Called on every `resolve()`. |
283
+ | `container.registerDecorator(Decorator)` | Register a decorator that wraps resolved instances. Applied in registration order. |
284
+ | `container.registerComposite(Composite)` | Register a composite that aggregates all implementations behind a single `resolve()`. |
286
285
 
287
286
  ### Resolution
288
287
 
289
- | Method | Description |
290
- |--------|-------------|
291
- | `container.resolve(Abstraction)` | Resolve single instance (last registered wins). Throws if not registered. |
288
+ | Method | Description |
289
+ | ----------------------------------- | ----------------------------------------------------------------------------- |
290
+ | `container.resolve(Abstraction)` | Resolve single instance (last registered wins). Throws if not registered. |
292
291
  | `container.resolveAll(Abstraction)` | Resolve all registered implementations as `T[]`. Returns empty array if none. |
293
- | `container.createChildContainer()` | Create a child container that inherits parent registrations. |
292
+ | `container.createChildContainer()` | Create a child container that inherits parent registrations. |
294
293
 
295
294
  ### Lifetime Scopes
296
295
 
@@ -305,20 +304,20 @@ Decorators wrap resolved instances. The decoratee is always the **last** constru
305
304
 
306
305
  ```ts
307
306
  class LoggingServiceDecorator implements MyService.Interface {
308
- constructor(
309
- private logger: Logger.Interface,
310
- private decoratee: MyService.Interface // LAST param — injected automatically
311
- ) {}
312
-
313
- execute() {
314
- this.logger.info("Before");
315
- this.decoratee.execute();
316
- }
307
+ constructor(
308
+ private logger: Logger.Interface,
309
+ private decoratee: MyService.Interface // LAST param — injected automatically
310
+ ) {}
311
+
312
+ execute() {
313
+ this.logger.info("Before");
314
+ this.decoratee.execute();
315
+ }
317
316
  }
318
317
 
319
318
  export const MyServiceLoggingDecorator = MyService.createDecorator({
320
- decorator: LoggingServiceDecorator,
321
- dependencies: [Logger] // decoratee is NOT listed
319
+ decorator: LoggingServiceDecorator,
320
+ dependencies: [Logger] // decoratee is NOT listed
322
321
  });
323
322
 
324
323
  // Registration:
@@ -331,16 +330,16 @@ Composites aggregate multiple implementations behind a single `resolve()` call.
331
330
 
332
331
  ```ts
333
332
  class AllValidatorsComposite implements Validator.Interface {
334
- constructor(private validators: Validator.Interface[]) {}
333
+ constructor(private validators: Validator.Interface[]) {}
335
334
 
336
- validate(input: unknown) {
337
- for (const v of this.validators) v.validate(input);
338
- }
335
+ validate(input: unknown) {
336
+ for (const v of this.validators) v.validate(input);
337
+ }
339
338
  }
340
339
 
341
340
  export const ValidatorComposite = Validator.createComposite({
342
- implementation: AllValidatorsComposite,
343
- dependencies: [[Validator, { multiple: true }]]
341
+ implementation: AllValidatorsComposite,
342
+ dependencies: [[Validator, { multiple: true }]]
344
343
  });
345
344
 
346
345
  // Registration: