@webiny/mcp 6.1.0-beta.1 → 6.1.0-beta.3
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/agents/claude.d.ts +2 -0
- package/agents/claude.js +6 -0
- package/agents/claude.js.map +1 -1
- package/agents/cline.d.ts +2 -0
- package/agents/cline.js +6 -0
- package/agents/cline.js.map +1 -1
- package/agents/copilot.d.ts +2 -0
- package/agents/copilot.js +7 -0
- package/agents/copilot.js.map +1 -1
- package/agents/cursor.d.ts +2 -0
- package/agents/cursor.js +6 -0
- package/agents/cursor.js.map +1 -1
- package/agents/discover.d.ts +3 -0
- package/agents/discover.js +29 -0
- package/agents/discover.js.map +1 -0
- package/agents/instructions.d.ts +3 -1
- package/agents/instructions.js +15 -2
- package/agents/instructions.js.map +1 -1
- package/agents/kiro.d.ts +2 -0
- package/agents/kiro.js +6 -0
- package/agents/kiro.js.map +1 -1
- package/agents/opencode.d.ts +2 -0
- package/agents/opencode.js +7 -0
- package/agents/opencode.js.map +1 -1
- package/agents/types.d.ts +16 -0
- package/agents/types.js +3 -0
- package/agents/types.js.map +1 -0
- package/agents/windsurf.d.ts +2 -0
- package/agents/windsurf.js +6 -0
- package/agents/windsurf.js.map +1 -1
- package/cli/ConfigureMcp.js +21 -6
- package/cli/ConfigureMcp.js.map +1 -1
- package/package.json +3 -3
- package/skills/admin/admin-architect/SKILL.md +188 -0
- package/skills/admin/admin-permissions/SKILL.md +159 -0
- package/skills/admin/ui-extensions/SKILL.md +2 -0
- package/skills/api/api-architect/SKILL.md +548 -60
- package/skills/api/event-handler-pattern/SKILL.md +195 -23
- package/skills/api/graphql-api/SKILL.md +231 -31
- package/skills/api/permissions/SKILL.md +291 -0
- package/skills/api/use-case-pattern/SKILL.md +351 -12
- package/skills/api/v5-to-v6-migration/SKILL.md +416 -0
- package/skills/cli-extensions/SKILL.md +1 -1
- package/skills/content-models/SKILL.md +9 -5
- package/skills/full-stack-architect/SKILL.md +4 -0
- package/skills/generated/admin/SKILL.md +1 -1
- package/skills/generated/admin/aco/SKILL.md +1 -1
- package/skills/generated/admin/build-params/SKILL.md +1 -1
- package/skills/generated/admin/cms/SKILL.md +1 -1
- package/skills/generated/admin/configs/SKILL.md +1 -1
- package/skills/generated/admin/env-config/SKILL.md +1 -1
- package/skills/generated/admin/form/SKILL.md +1 -1
- package/skills/generated/admin/graphql-client/SKILL.md +1 -1
- package/skills/generated/admin/lexical/SKILL.md +1 -1
- package/skills/generated/admin/local-storage/SKILL.md +1 -1
- package/skills/generated/admin/router/SKILL.md +1 -1
- package/skills/generated/admin/security/SKILL.md +1 -1
- package/skills/generated/admin/tenancy/SKILL.md +1 -1
- package/skills/generated/admin/ui/SKILL.md +1 -1
- package/skills/generated/admin/website-builder/SKILL.md +1 -1
- package/skills/generated/api/SKILL.md +1 -1
- package/skills/generated/api/aco/SKILL.md +1 -1
- package/skills/generated/api/build-params/SKILL.md +1 -1
- package/skills/generated/api/cms/SKILL.md +1 -1
- package/skills/generated/api/event-publisher/SKILL.md +1 -1
- package/skills/generated/api/file-manager/SKILL.md +1 -1
- package/skills/generated/api/graphql/SKILL.md +1 -1
- package/skills/generated/api/key-value-store/SKILL.md +1 -1
- package/skills/generated/api/logger/SKILL.md +1 -1
- package/skills/generated/api/opensearch/SKILL.md +1 -1
- package/skills/generated/api/scheduler/SKILL.md +1 -1
- package/skills/generated/api/security/SKILL.md +1 -1
- package/skills/generated/api/system/SKILL.md +1 -1
- package/skills/generated/api/tasks/SKILL.md +1 -1
- package/skills/generated/api/tenancy/SKILL.md +1 -1
- package/skills/generated/api/tenant-manager/SKILL.md +1 -1
- package/skills/generated/api/website-builder/SKILL.md +1 -1
- package/skills/generated/cli/SKILL.md +1 -1
- package/skills/generated/cli/command/SKILL.md +1 -1
- package/skills/generated/extensions/SKILL.md +1 -1
- package/skills/generated/infra/SKILL.md +1 -1
- package/skills/infrastructure-extensions/SKILL.md +1 -1
- package/skills/project-structure/SKILL.md +4 -0
- package/skills/webiny-sdk/SKILL.md +1 -1
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: webiny-v5-to-v6-migration
|
|
3
|
+
context: webiny-api
|
|
4
|
+
description: >
|
|
5
|
+
Migration patterns for converting v5 Webiny code to v6 architecture. Use this skill when
|
|
6
|
+
migrating existing v5 plugins to v6 features, converting context plugins to DI services,
|
|
7
|
+
adapting v5 event subscriptions to v6 EventHandlers, or understanding how v5 patterns
|
|
8
|
+
translate to v6. Targeted at AI agents performing migrations.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# v5 → v6 Migration Patterns
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
v6 replaces v5's plugin-based architecture with **feature-based DI**. The key shifts:
|
|
16
|
+
|
|
17
|
+
| v5 Concept | v6 Equivalent |
|
|
18
|
+
| -------------------------------- | ------------------------------------------------------ |
|
|
19
|
+
| `ContextPlugin` | `createAbstraction` + `createImplementation` (Service) |
|
|
20
|
+
| Plugin array | `createFeature` + `container.register()` |
|
|
21
|
+
| `context.myService` | DI injection via constructor |
|
|
22
|
+
| `onEntryAfterCreate.subscribe()` | `EventHandler` feature |
|
|
23
|
+
| `new GraphQLSchemaPlugin()` | `GraphQLSchemaFactory.createImplementation()` |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Pattern 1: Context Plugin → DI Service
|
|
28
|
+
|
|
29
|
+
### v5
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
new ContextPlugin(async context => {
|
|
33
|
+
context.lingotekService = {
|
|
34
|
+
translate: async (docId, locale) => {
|
|
35
|
+
/* ... */
|
|
36
|
+
},
|
|
37
|
+
getStatus: async docId => {
|
|
38
|
+
/* ... */
|
|
39
|
+
},
|
|
40
|
+
deleteProject: async projectId => {
|
|
41
|
+
/* ... */
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### v6
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// features/lingotekService/abstractions.ts
|
|
51
|
+
import { createAbstraction } from "@webiny/feature/api";
|
|
52
|
+
|
|
53
|
+
export interface ILingotekService {
|
|
54
|
+
translate(docId: string, locale: string): Promise<Result<void, Error>>;
|
|
55
|
+
getStatus(docId: string): Promise<Result<TranslationStatus, Error>>;
|
|
56
|
+
deleteProject(projectId: string): Promise<Result<void, Error>>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const LingotekService = createAbstraction<ILingotekService>("MyExt/LingotekService");
|
|
60
|
+
|
|
61
|
+
export namespace LingotekService {
|
|
62
|
+
export type Interface = ILingotekService;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// features/lingotekService/LingotekService.ts
|
|
66
|
+
class LingotekServiceImpl implements LingotekService.Interface {
|
|
67
|
+
constructor(private buildParams: BuildParams.Interface) {}
|
|
68
|
+
|
|
69
|
+
async translate(docId: string, locale: string) {
|
|
70
|
+
/* ... */
|
|
71
|
+
}
|
|
72
|
+
async getStatus(docId: string) {
|
|
73
|
+
/* ... */
|
|
74
|
+
}
|
|
75
|
+
async deleteProject(projectId: string) {
|
|
76
|
+
/* ... */
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default LingotekService.createImplementation({
|
|
81
|
+
implementation: LingotekServiceImpl,
|
|
82
|
+
dependencies: [BuildParams]
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// features/lingotekService/feature.ts
|
|
86
|
+
export const LingotekServiceFeature = createFeature({
|
|
87
|
+
name: "LingotekService",
|
|
88
|
+
register(container) {
|
|
89
|
+
container.register(LingotekServiceImpl).inSingletonScope();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Key difference:** v5 attaches to context object. v6 uses DI — consumers declare the service as a constructor dependency.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Pattern 2: Event Subscription → EventHandler Feature
|
|
99
|
+
|
|
100
|
+
### v5
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
context.cms.onEntryAfterCreate.subscribe(async params => {
|
|
104
|
+
if (params.model.modelId !== "myModel") return;
|
|
105
|
+
await doSomething(params.entry);
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### v6
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// features/syncOnCreate/EntryAfterCreateHandler.ts
|
|
113
|
+
import { EntryAfterCreateEventHandler } from "webiny/api/cms/entry";
|
|
114
|
+
import { LingotekService } from "../lingotekService/abstractions.js";
|
|
115
|
+
import { MY_MODEL_ID } from "~/shared/constants.js";
|
|
116
|
+
|
|
117
|
+
class SyncOnCreateHandler implements EntryAfterCreateEventHandler.Interface {
|
|
118
|
+
constructor(private lingotekService: LingotekService.Interface) {}
|
|
119
|
+
|
|
120
|
+
async handle(event: EntryAfterCreateEventHandler.Event) {
|
|
121
|
+
const { entry, model } = event.payload;
|
|
122
|
+
if (model.modelId !== MY_MODEL_ID) return;
|
|
123
|
+
await this.lingotekService.translate(entry.entryId, "en");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export default EntryAfterCreateEventHandler.createImplementation({
|
|
128
|
+
implementation: SyncOnCreateHandler,
|
|
129
|
+
dependencies: [LingotekService]
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// features/syncOnCreate/feature.ts
|
|
133
|
+
export const SyncOnCreateFeature = createFeature({
|
|
134
|
+
name: "SyncOnCreate",
|
|
135
|
+
register(container) {
|
|
136
|
+
container.register(SyncOnCreateHandler);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Key differences:**
|
|
142
|
+
|
|
143
|
+
- Feature directory is named by **business capability** (`syncOnCreate`), not by event name
|
|
144
|
+
- Handler is a thin orchestrator — business logic lives in the injected service
|
|
145
|
+
- Must filter by `model.modelId` — handler fires for ALL models
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Pattern 3: Plugin Array → Feature Registration
|
|
150
|
+
|
|
151
|
+
### v5
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
export default () => [
|
|
155
|
+
new GraphQLSchemaPlugin({ ... }),
|
|
156
|
+
new ContextPlugin(async ctx => { ... }),
|
|
157
|
+
myModelPlugin,
|
|
158
|
+
eventSubscriptionPlugin
|
|
159
|
+
];
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### v6
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// api/Extension.ts
|
|
166
|
+
import { createFeature } from "webiny/api";
|
|
167
|
+
|
|
168
|
+
export const Extension = createFeature({
|
|
169
|
+
name: "MyExtension",
|
|
170
|
+
register(container) {
|
|
171
|
+
container.register(MyModel);
|
|
172
|
+
container.register(MyGraphQLSchema);
|
|
173
|
+
SyncOnCreateFeature.register(container);
|
|
174
|
+
LingotekServiceFeature.register(container);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Pattern 4: Async Service Bootstrap → ServiceProvider Pattern
|
|
182
|
+
|
|
183
|
+
When a v5 service was initialized with async data (loading settings, fetching config), v6 uses the **ServiceProvider** pattern — a provider abstraction with `async getService()` that lazily creates and caches the service.
|
|
184
|
+
|
|
185
|
+
See the **ServiceProvider Pattern** section in **webiny-api-architect** for the full pattern with abstractions, implementation, and consumer examples.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Pattern 6: Permissions objects
|
|
190
|
+
|
|
191
|
+
### v5
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
[
|
|
195
|
+
{
|
|
196
|
+
name: "content.i18n",
|
|
197
|
+
locales: ["en-US"]
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: "cms.endpoint.read"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: "cms.endpoint.manage"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: "cms.endpoint.preview"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "cms.contentModelGroup",
|
|
210
|
+
groups: {
|
|
211
|
+
"en-US": [LT_TRANSLATION_MODEL_GROUP_ID]
|
|
212
|
+
},
|
|
213
|
+
rwd: "rw",
|
|
214
|
+
own: false,
|
|
215
|
+
pw: ""
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "cms.contentModel",
|
|
219
|
+
models: {
|
|
220
|
+
"en-US": [
|
|
221
|
+
LT_TRANSLATION_DOCUMENT_MODEL_ID,
|
|
222
|
+
LT_CONFIG_MODEL_ID,
|
|
223
|
+
LT_TRANSLATION_PROJECT_MODEL_ID
|
|
224
|
+
]
|
|
225
|
+
},
|
|
226
|
+
rwd: "rwd",
|
|
227
|
+
own: false,
|
|
228
|
+
pw: ""
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: "cms.contentEntry",
|
|
232
|
+
rwd: "rwd",
|
|
233
|
+
own: false,
|
|
234
|
+
pw: ""
|
|
235
|
+
}
|
|
236
|
+
];
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### v6
|
|
240
|
+
|
|
241
|
+
- `content.i18n` no longer exists
|
|
242
|
+
- locale codes no longer exist
|
|
243
|
+
- `models` is an array of `model.modelId` strings
|
|
244
|
+
- `groups` is an array of `group.slug` strings
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
[
|
|
248
|
+
{
|
|
249
|
+
name: "cms.endpoint.read"
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: "cms.endpoint.manage"
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: "cms.endpoint.preview"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: "cms.contentModelGroup",
|
|
259
|
+
groups: ["LT_TRANSLATION_MODEL_GROUP_ID"],
|
|
260
|
+
rwd: "rw",
|
|
261
|
+
own: false,
|
|
262
|
+
pw: ""
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: "cms.contentModel",
|
|
266
|
+
models: [
|
|
267
|
+
"LT_TRANSLATION_DOCUMENT_MODEL_ID",
|
|
268
|
+
"LT_CONFIG_MODEL_ID",
|
|
269
|
+
"LT_TRANSLATION_PROJECT_MODEL_ID"
|
|
270
|
+
],
|
|
271
|
+
rwd: "rwd",
|
|
272
|
+
own: false,
|
|
273
|
+
pw: ""
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "cms.contentEntry",
|
|
277
|
+
rwd: "rwd",
|
|
278
|
+
own: false,
|
|
279
|
+
pw: ""
|
|
280
|
+
}
|
|
281
|
+
];
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Type Resolution Guide
|
|
287
|
+
|
|
288
|
+
When working with Webiny abstractions, always verify types from source before writing code.
|
|
289
|
+
|
|
290
|
+
### Step 1: Find the catalog entry
|
|
291
|
+
|
|
292
|
+
Use MCP skills or generated catalogs to look up the abstraction (e.g., `RoleFactory`).
|
|
293
|
+
|
|
294
|
+
### Step 2: Get the source path
|
|
295
|
+
|
|
296
|
+
The catalog entry includes a `Source` field pointing to the abstraction definition.
|
|
297
|
+
|
|
298
|
+
### Step 3: Read the type definition
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
# Read the abstractions file
|
|
302
|
+
cat node_modules/@webiny/api-core/features/security/roles/shared/abstractions.d.ts
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Common type patterns
|
|
306
|
+
|
|
307
|
+
| Pattern | What to expect |
|
|
308
|
+
| ------------- | -------------------------------------------------- |
|
|
309
|
+
| Factories | Return `Promise<Type[]>` or `Promise<Builder[]>` |
|
|
310
|
+
| UseCases | Have `Input` type and return `Result<Data, Error>` |
|
|
311
|
+
| EventHandlers | Have `Event` with `payload` property |
|
|
312
|
+
| Repositories | Return `Result<T, Error>` — wrap CMS errors |
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Migration Map: v5 → v6 Equivalents
|
|
317
|
+
|
|
318
|
+
### Backend: Context Method Calls → Use Cases
|
|
319
|
+
|
|
320
|
+
| v5 Pattern | v6 Equivalent |
|
|
321
|
+
| ----------------------------------------- | ---------------------------------------- |
|
|
322
|
+
| `context.cms.getModel()` | `GetModelUseCase` |
|
|
323
|
+
| `context.cms.createModel()` | `CreateModelUseCase` |
|
|
324
|
+
| `context.cms.updateEntry()` | `UpdateEntryUseCase` |
|
|
325
|
+
| `context.cms.getSingletonEntryManager()` | `GetSingletonEntryUseCase` |
|
|
326
|
+
| `context.tenancy.getCurrentTenant()` | `TenantContext.getTenant()` |
|
|
327
|
+
| `context.security.withoutAuthorization()` | `IdentityContext.withoutAuthorization()` |
|
|
328
|
+
| `context.aco.folder.delete()` | `DeleteFolderUseCase` |
|
|
329
|
+
| `context.aco.folder.get()` | `GetFolderUseCase` |
|
|
330
|
+
| `context.plugins.register()` | DI container registration |
|
|
331
|
+
| `context.plugins.byType()` | DI container injection |
|
|
332
|
+
|
|
333
|
+
### Backend: Lifecycle Event Subscriptions → EventHandlers
|
|
334
|
+
|
|
335
|
+
| v5 Pattern (`.subscribe()`) | v6 EventHandler |
|
|
336
|
+
| --------------------------------- | ---------------------------------- |
|
|
337
|
+
| `cms.onEntryBeforeCreate` | `EntryBeforeCreateEventHandler` |
|
|
338
|
+
| `cms.onEntryAfterCreate` | `EntryAfterCreateEventHandler` |
|
|
339
|
+
| `cms.onEntryBeforeUpdate` | `EntryBeforeUpdateEventHandler` |
|
|
340
|
+
| `cms.onEntryAfterUpdate` | `EntryAfterUpdateEventHandler` |
|
|
341
|
+
| `cms.onEntryBeforeDelete` | `EntryBeforeDeleteEventHandler` |
|
|
342
|
+
| `cms.onEntryAfterDelete` | `EntryAfterDeleteEventHandler` |
|
|
343
|
+
| `cms.onEntryBeforeMove` | `EntryBeforeMoveEventHandler` |
|
|
344
|
+
| `cms.onEntryBeforePublish` | `EntryBeforePublishEventHandler` |
|
|
345
|
+
| `cms.onEntryBeforeUnpublish` | `EntryBeforeUnpublishEventHandler` |
|
|
346
|
+
| `aco.folder.onFolderBeforeUpdate` | `FolderBeforeUpdateEventHandler` |
|
|
347
|
+
| `aco.folder.onFolderAfterCreate` | `FolderAfterCreateEventHandler` |
|
|
348
|
+
| `aco.folder.onFolderAfterUpdate` | `FolderAfterUpdateEventHandler` |
|
|
349
|
+
|
|
350
|
+
### Backend: Plugin Classes → v6 Equivalents
|
|
351
|
+
|
|
352
|
+
| v5 Plugin | v6 Equivalent |
|
|
353
|
+
| ----------------------------------------------- | ----------------------------- |
|
|
354
|
+
| `ContextPlugin` | DI-registered implementations |
|
|
355
|
+
| `createContextPlugin` | DI-registered implementations |
|
|
356
|
+
| `CmsModelPlugin` | `ModelFactory` |
|
|
357
|
+
| `GraphQLSchemaPlugin` | `GraphQLSchemaFactory` |
|
|
358
|
+
| `createGraphQLSchemaPlugin` | `GraphQLSchemaFactory` |
|
|
359
|
+
| `createTaskDefinition` | `TaskDefinition` |
|
|
360
|
+
| `CmsModelFieldToGraphQLPlugin` | TODO |
|
|
361
|
+
| `createSecurityRolePlugin` | `RoleFactory` |
|
|
362
|
+
| `createSecurityTeamPlugin` | `TeamFactory` |
|
|
363
|
+
| `StorageTransformPlugin` | TODO |
|
|
364
|
+
| `createApiGatewayRoute` | TODO (Adrian) |
|
|
365
|
+
| `CmsModelFieldValidatorPlugin` | TODO |
|
|
366
|
+
| `createCmsGraphQLSchemaSorterPlugin` | TODO |
|
|
367
|
+
| `createCmsEntryElasticsearchBodyModifierPlugin` | TODO |
|
|
368
|
+
|
|
369
|
+
### Admin: React Plugins → AdminConfig API
|
|
370
|
+
|
|
371
|
+
| v5 Pattern | v6 Equivalent |
|
|
372
|
+
| ---------------------------------- | -------------------------------------------------------- |
|
|
373
|
+
| `createComponentPlugin` | `Component.createDecorator` |
|
|
374
|
+
| `RoutePlugin` | `<AdminConfig.Route/>` |
|
|
375
|
+
| `AddMenu` / menu components | `<AdminConfig.Menu/>` |
|
|
376
|
+
| `HasPermission` | `HasPermission` or `createHasPermission` with new schema |
|
|
377
|
+
| `GraphQLPlaygroundTabPlugin` | TODO |
|
|
378
|
+
| `CmsModelFieldTypePlugin` | `<CmsModelFieldType/>` |
|
|
379
|
+
| `CmsModelFieldRendererPlugin` | `<CmsModelFieldRenderer/>` |
|
|
380
|
+
| `AdminAppPermissionRendererPlugin` | `createPermissionSchema` / `<Security.Permissions/>` |
|
|
381
|
+
| `webiny/app/config` | `EnvConfig` |
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Common Migration Mistakes
|
|
386
|
+
|
|
387
|
+
### 1. Creating one abstraction per operation
|
|
388
|
+
|
|
389
|
+
v5 habit: separate plugins per action. v6: group related operations into a **multi-method Service**.
|
|
390
|
+
|
|
391
|
+
### 2. Naming features by technical event
|
|
392
|
+
|
|
393
|
+
v5 habit: thinking in terms of hooks (`onEntryAfterCreate`). v6: features describe business capability (`syncToLingotek`). Files inside can be named technically (`EntryAfterCreateHandler.ts`).
|
|
394
|
+
|
|
395
|
+
### 3. Assuming builder patterns
|
|
396
|
+
|
|
397
|
+
v6 factories sometimes return plain objects, sometimes builder objects. Always read source types first, to understand what the factory in question returns.
|
|
398
|
+
|
|
399
|
+
### 4. Putting event handlers in a handlers/ directory
|
|
400
|
+
|
|
401
|
+
v5 habit: grouping by type. v6: handlers are features — they go in `features/`.
|
|
402
|
+
|
|
403
|
+
### 5. Attaching to context
|
|
404
|
+
|
|
405
|
+
v5: `context.myService = { ... }`. v6: create an abstraction and register it in the DI container via the parent feature, or a standalone feature (`createFeature`).
|
|
406
|
+
|
|
407
|
+
### 6. Inline business logic in event handlers
|
|
408
|
+
|
|
409
|
+
v5 habit: putting logic directly in the subscription callback. v6: handlers are thin orchestrators — extract logic into a Service or UseCase.
|
|
410
|
+
|
|
411
|
+
## Related Skills
|
|
412
|
+
|
|
413
|
+
- **webiny-api-architect** — Full v6 architecture, Services vs UseCases, anti-patterns
|
|
414
|
+
- **webiny-use-case-pattern** — UseCase implementation details
|
|
415
|
+
- **webiny-event-handler-pattern** — EventHandler and domain event patterns
|
|
416
|
+
- **webiny-dependency-injection** — DI pattern and injectable services
|
|
@@ -56,7 +56,7 @@ export default CliCommandFactory.createImplementation({
|
|
|
56
56
|
});
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Register in `webiny.config.tsx
|
|
59
|
+
Register in `webiny.config.tsx` (**YOU MUST include the `.ts` file extension in the `src` prop** — omitting it will cause a build failure):
|
|
60
60
|
|
|
61
61
|
```tsx
|
|
62
62
|
<Cli.Command src={"/extensions/MyCustomCommand.ts"} />
|
|
@@ -43,7 +43,7 @@ class MyModelImpl implements ModelFactory.Interface {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
export
|
|
46
|
+
export default ModelFactory.createImplementation({
|
|
47
47
|
implementation: MyModelImpl,
|
|
48
48
|
dependencies: []
|
|
49
49
|
});
|
|
@@ -55,6 +55,10 @@ Register in `webiny.config.tsx`:
|
|
|
55
55
|
<Api.Extension src={"/extensions/MyModel.ts"} />
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
**YOU MUST include the full file path with the `.ts` extension in the `src` prop.** For example, use `src={"/extensions/MyModel.ts"}`, NOT `src={"/extensions/MyModel"}`. Omitting the file extension will cause a build failure.
|
|
59
|
+
|
|
60
|
+
**YOU MUST use `export default` for the `createImplementation()` call** when the file is targeted directly by an Extension `src` prop. Using a named export (`export const MyModel = ...`) will cause a build failure. Named exports are only valid inside files registered via `createFeature`.
|
|
61
|
+
|
|
58
62
|
## Model Configuration Methods
|
|
59
63
|
|
|
60
64
|
| Method | Purpose |
|
|
@@ -154,7 +158,7 @@ class ProductCategoryModelImpl implements ModelFactory.Interface {
|
|
|
154
158
|
}
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
export
|
|
161
|
+
export default ModelFactory.createImplementation({
|
|
158
162
|
implementation: ProductCategoryModelImpl,
|
|
159
163
|
dependencies: []
|
|
160
164
|
});
|
|
@@ -217,7 +221,7 @@ class ProductModelImpl implements ModelFactory.Interface {
|
|
|
217
221
|
}
|
|
218
222
|
}
|
|
219
223
|
|
|
220
|
-
export
|
|
224
|
+
export default ModelFactory.createImplementation({
|
|
221
225
|
implementation: ProductModelImpl,
|
|
222
226
|
dependencies: []
|
|
223
227
|
});
|
|
@@ -284,7 +288,7 @@ class ContactSubmissionModelImpl implements ModelFactory.Interface {
|
|
|
284
288
|
}
|
|
285
289
|
}
|
|
286
290
|
|
|
287
|
-
export
|
|
291
|
+
export default ModelFactory.createImplementation({
|
|
288
292
|
implementation: ContactSubmissionModelImpl,
|
|
289
293
|
dependencies: []
|
|
290
294
|
});
|
|
@@ -296,7 +300,7 @@ export const ContactSubmissionModel = ModelFactory.createImplementation({
|
|
|
296
300
|
Import: import { ModelFactory } from "webiny/api/cms/model";
|
|
297
301
|
Interface: ModelFactory.Interface
|
|
298
302
|
Builder: ModelFactory.Builder
|
|
299
|
-
Export:
|
|
303
|
+
Export: export default ModelFactory.createImplementation({ implementation, dependencies })
|
|
300
304
|
Register: <Api.Extension src={"/extensions/MyModel.ts"} />
|
|
301
305
|
Deploy: yarn webiny deploy api (or use watch mode)
|
|
302
306
|
```
|
|
@@ -22,6 +22,10 @@ The same rule applies to API extensions — they must go through `<Api.Extension
|
|
|
22
22
|
|
|
23
23
|
These entry-point components are the **only** way to register code that runs inside the Admin app or the API runtime. They use the `src` prop to point to a file that will be loaded in the correct execution environment (browser for Admin, Lambda for API). Bypassing these entry points will fail at runtime because the Admin and API contexts (DI containers, routers, GraphQL registries, etc.) are not available outside their respective runtimes.
|
|
24
24
|
|
|
25
|
+
**YOU MUST include the full file path with the `.ts` or `.tsx` extension in every `src` prop.** For example, use `src={"/extensions/lead/src/index.ts"}`, NOT `src={"/extensions/lead"}`. Omitting the file extension will cause a build failure.
|
|
26
|
+
|
|
27
|
+
**YOU MUST use `export default` for the `createImplementation()` call** when the file is targeted directly by an Extension `src` prop. Using a named export (`export const Foo = SomeFactory.createImplementation(...)`) will cause a build failure. Named exports are only valid inside files registered via `createFeature`.
|
|
28
|
+
|
|
25
29
|
```tsx
|
|
26
30
|
// CORRECT — always use entry-point components
|
|
27
31
|
<Api.Extension src={import.meta.dirname + "/api/Extension.js"} />
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
|
|
16
16
|
## Abstractions
|
|
@@ -13,7 +13,7 @@ Folder event handlers and use cases.
|
|
|
13
13
|
## How to Use
|
|
14
14
|
|
|
15
15
|
1. Find the abstraction you need below
|
|
16
|
-
2.
|
|
16
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
17
17
|
3. Import: `import { Name } from "<importPath>";`
|
|
18
18
|
4. See `webiny-use-case-pattern` or `webiny-event-handler-pattern` skills for implementation patterns
|
|
19
19
|
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
## How to Use
|
|
11
11
|
|
|
12
12
|
1. Find the abstraction you need below
|
|
13
|
-
2.
|
|
13
|
+
2. You MUST read the source file to get the exact interface and types!
|
|
14
14
|
3. Import: `import { Name } from "<importPath>";`
|
|
15
15
|
4. See `webiny-use-case-pattern` or `webiny-event-handler-pattern` skills for implementation patterns
|
|
16
16
|
|