@webiny/mcp 6.0.0 → 6.1.0-beta.1
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 -2
- package/agents/claude.js.map +1 -1
- package/agents/cline.d.ts +2 -2
- package/agents/cline.js.map +1 -1
- package/agents/copilot.d.ts +2 -2
- package/agents/copilot.js +1 -1
- package/agents/copilot.js.map +1 -1
- package/agents/cursor.d.ts +2 -2
- package/agents/cursor.js.map +1 -1
- package/agents/instructions.js +1 -1
- package/agents/instructions.js.map +1 -1
- package/agents/kiro.d.ts +15 -0
- package/agents/kiro.js +33 -0
- package/agents/kiro.js.map +1 -0
- package/agents/opencode.d.ts +22 -0
- package/agents/opencode.js +76 -0
- package/agents/opencode.js.map +1 -0
- package/agents/shared.d.ts +5 -5
- package/agents/shared.js +3 -3
- package/agents/shared.js.map +1 -1
- package/agents/windsurf.d.ts +2 -2
- package/agents/windsurf.js.map +1 -1
- package/bin.d.ts +2 -0
- package/bin.js +4 -0
- package/bin.js.map +1 -0
- package/cli/ConfigureMcp.d.ts +7 -14
- package/cli/ConfigureMcp.js +24 -52
- package/cli/ConfigureMcp.js.map +1 -1
- package/cli/McpServer.d.ts +3 -10
- package/cli/McpServer.js +83 -109
- package/cli/McpServer.js.map +1 -1
- package/cli.d.ts +1 -0
- package/cli.js +60 -0
- package/cli.js.map +1 -0
- package/index.d.ts +6 -1
- package/index.js +7 -1
- package/index.js.map +1 -1
- package/package.json +11 -23
- package/skills/admin/admin-architect/SKILL.md +389 -0
- package/skills/admin/ui-extensions/SKILL.md +268 -0
- package/skills/api/api-architect/SKILL.md +189 -0
- package/skills/api/custom-field-type/SKILL.md +263 -0
- package/skills/api/event-handler-pattern/SKILL.md +131 -0
- package/skills/{custom-graphql-api → api/graphql-api}/SKILL.md +3 -3
- package/skills/api/use-case-pattern/SKILL.md +102 -0
- package/skills/cli-extensions/SKILL.md +45 -47
- package/skills/configure-auth0/SKILL.md +4 -4
- package/skills/configure-okta/SKILL.md +3 -3
- package/skills/content-models/SKILL.md +197 -196
- package/skills/dependency-injection/SKILL.md +9 -219
- package/skills/full-stack-architect/SKILL.md +195 -0
- package/skills/generated/admin/SKILL.md +119 -0
- package/skills/generated/admin/aco/SKILL.md +28 -0
- package/skills/generated/admin/build-params/SKILL.md +33 -0
- package/skills/generated/admin/cms/SKILL.md +342 -0
- package/skills/generated/admin/configs/SKILL.md +23 -0
- package/skills/generated/admin/env-config/SKILL.md +30 -0
- package/skills/generated/admin/form/SKILL.md +88 -0
- package/skills/generated/admin/graphql-client/SKILL.md +23 -0
- package/skills/generated/admin/lexical/SKILL.md +105 -0
- package/skills/generated/admin/local-storage/SKILL.md +42 -0
- package/skills/generated/admin/router/SKILL.md +48 -0
- package/skills/generated/admin/security/SKILL.md +63 -0
- package/skills/generated/admin/tenancy/SKILL.md +64 -0
- package/skills/generated/admin/ui/SKILL.md +468 -0
- package/skills/generated/admin/website-builder/SKILL.md +318 -0
- package/skills/generated/api/SKILL.md +40 -0
- package/skills/generated/api/aco/SKILL.md +202 -0
- package/skills/generated/api/build-params/SKILL.md +31 -0
- package/skills/generated/api/cms/SKILL.md +646 -0
- package/skills/generated/api/event-publisher/SKILL.md +31 -0
- package/skills/generated/api/file-manager/SKILL.md +189 -0
- package/skills/generated/api/graphql/SKILL.md +61 -0
- package/skills/generated/api/key-value-store/SKILL.md +31 -0
- package/skills/generated/api/logger/SKILL.md +25 -0
- package/skills/generated/api/opensearch/SKILL.md +39 -0
- package/skills/generated/api/scheduler/SKILL.md +112 -0
- package/skills/generated/api/security/SKILL.md +317 -0
- package/skills/generated/api/system/SKILL.md +34 -0
- package/skills/generated/api/tasks/SKILL.md +31 -0
- package/skills/generated/api/tenancy/SKILL.md +124 -0
- package/skills/generated/api/tenant-manager/SKILL.md +34 -0
- package/skills/generated/api/website-builder/SKILL.md +356 -0
- package/skills/generated/cli/SKILL.md +28 -0
- package/skills/generated/cli/command/SKILL.md +24 -0
- package/skills/generated/extensions/SKILL.md +43 -0
- package/skills/generated/infra/SKILL.md +190 -0
- package/skills/infrastructure-extensions/SKILL.md +3 -2
- package/skills/local-development/SKILL.md +2 -28
- package/skills/project-structure/SKILL.md +78 -56
- package/skills/webiny-sdk/SKILL.md +77 -76
- package/skills/website-builder/SKILL.md +143 -149
- package/ui.d.ts +24 -0
- package/ui.js +31 -0
- package/ui.js.map +1 -0
- package/Extension.d.ts +0 -2
- package/Extension.js +0 -11
- package/Extension.js.map +0 -1
- package/skills/admin-ui-extensions/SKILL.md +0 -267
- package/skills/api-custom-feature/SKILL.md +0 -195
- package/skills/lifecycle-events/SKILL.md +0 -348
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: webiny-admin-ui-extensions
|
|
3
|
-
context: webiny-extensions
|
|
4
|
-
description: >
|
|
5
|
-
Customizing the Webiny Admin UI -- white-labeling, custom data list columns, page-type forms,
|
|
6
|
-
and Lexical editor plugins. Use this skill when the developer wants to change branding (logo,
|
|
7
|
-
title, theme colors), add custom columns to content entry list views, create custom forms
|
|
8
|
-
for Website Builder page types, or extend the Lexical rich text editor. Covers AdminConfig,
|
|
9
|
-
ContentEntryListConfig, Browser.Table.Column, Bind, useForm, and form validation.
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Admin UI Extensions
|
|
13
|
-
|
|
14
|
-
## TL;DR
|
|
15
|
-
|
|
16
|
-
Admin extensions customize the Webiny Admin application. There are three main categories: **white-labeling** (logos, titles, theme colors), **custom data list columns** (adding columns to content entry tables), and **custom page-type forms** (custom form fields for Website Builder page types). All are React components registered via `<Admin.Extension>` in `webiny.config.tsx`.
|
|
17
|
-
|
|
18
|
-
## White-Labeling
|
|
19
|
-
|
|
20
|
-
### Theme Colors
|
|
21
|
-
|
|
22
|
-
```tsx
|
|
23
|
-
// extensions/AdminBranding/AdminTheme.tsx
|
|
24
|
-
import React from "react";
|
|
25
|
-
import { AdminConfig } from "webiny/admin/configs";
|
|
26
|
-
|
|
27
|
-
const { Theme } = AdminConfig;
|
|
28
|
-
|
|
29
|
-
const AdminTheme = () => {
|
|
30
|
-
return (
|
|
31
|
-
<AdminConfig.Public>
|
|
32
|
-
<Theme.Color palette={"primary"} color={"purple"} />
|
|
33
|
-
<Theme.Color palette={"secondary"} color={"green"} />
|
|
34
|
-
</AdminConfig.Public>
|
|
35
|
-
);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export default AdminTheme;
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
- `palette` -- `"primary"`, `"secondary"`, `"neutral"`, etc.
|
|
42
|
-
- `color` -- any CSS color value: named colors, hex (`"#6B46C1"`), or RGB.
|
|
43
|
-
|
|
44
|
-
### Logo and Title
|
|
45
|
-
|
|
46
|
-
```tsx
|
|
47
|
-
// extensions/AdminBranding/AdminTitleLogo.tsx
|
|
48
|
-
import React from "react";
|
|
49
|
-
import { AdminConfig } from "webiny/admin/configs";
|
|
50
|
-
import squareLogo from "./logo.png";
|
|
51
|
-
import horizontalLogo from "./logo.png";
|
|
52
|
-
|
|
53
|
-
const { Title, Logo } = AdminConfig;
|
|
54
|
-
|
|
55
|
-
const AdminTitleLogo = () => {
|
|
56
|
-
return (
|
|
57
|
-
<AdminConfig.Public>
|
|
58
|
-
<Title value={"ACME Corp"} />
|
|
59
|
-
<Logo
|
|
60
|
-
squareLogo={<img src={squareLogo} alt={"ACME Corp"} />}
|
|
61
|
-
horizontalLogo={<img src={horizontalLogo} alt={"ACME Corp"} />}
|
|
62
|
-
/>
|
|
63
|
-
</AdminConfig.Public>
|
|
64
|
-
);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export default AdminTitleLogo;
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Register both:
|
|
71
|
-
|
|
72
|
-
```tsx
|
|
73
|
-
<Admin.Extension src={"/extensions/AdminBranding/AdminTheme.tsx"} />
|
|
74
|
-
<Admin.Extension src={"/extensions/AdminBranding/AdminTitleLogo.tsx"} />
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Available AdminConfig Components
|
|
78
|
-
|
|
79
|
-
| Component | Purpose |
|
|
80
|
-
|---|---|
|
|
81
|
-
| `<Theme.Color palette="..." color="..." />` | Set theme color palette |
|
|
82
|
-
| `<Title value="..." />` | Set the Admin app title |
|
|
83
|
-
| `<Logo squareLogo={...} horizontalLogo={...} />` | Set square and horizontal logos |
|
|
84
|
-
|
|
85
|
-
All must be wrapped in `<AdminConfig.Public>`.
|
|
86
|
-
|
|
87
|
-
## Custom Data List Columns
|
|
88
|
-
|
|
89
|
-
Add custom columns to the content entry list view in the Admin UI. Columns can be restricted to specific content models.
|
|
90
|
-
|
|
91
|
-
### Full Example: Email Columns for Contact Submissions
|
|
92
|
-
|
|
93
|
-
```tsx
|
|
94
|
-
// extensions/contactSubmission/EmailEntryListColumn.tsx
|
|
95
|
-
import React from "react";
|
|
96
|
-
import { ContentEntryListConfig } from "webiny/admin/cms/entry/list";
|
|
97
|
-
|
|
98
|
-
const { Browser } = ContentEntryListConfig;
|
|
99
|
-
|
|
100
|
-
// Custom cell component for the Email Type column
|
|
101
|
-
interface ContactSubmissionTableRow {
|
|
102
|
-
values: {
|
|
103
|
-
emailType: "work" | "personal";
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export const EmailTypeCell = () => {
|
|
108
|
-
const { useTableRow, isFolderRow } = ContentEntryListConfig.Browser.Table.Column;
|
|
109
|
-
const { row } = useTableRow<ContactSubmissionTableRow>();
|
|
110
|
-
|
|
111
|
-
if (isFolderRow(row)) {
|
|
112
|
-
return <>{"-"}</>;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const emailType = row.data.values.emailType;
|
|
116
|
-
return emailType === "work" ? <>{"Business"}</> : <>{"Personal"}</>;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// Main extension component
|
|
120
|
-
const EmailEntryListColumn = () => {
|
|
121
|
-
return (
|
|
122
|
-
<ContentEntryListConfig>
|
|
123
|
-
{/* Simple column using path (no custom cell needed) */}
|
|
124
|
-
<Browser.Table.Column
|
|
125
|
-
name={"email"}
|
|
126
|
-
after={"name"}
|
|
127
|
-
path={"values.email"}
|
|
128
|
-
header={"Email"}
|
|
129
|
-
modelIds={["contactSubmission"]}
|
|
130
|
-
/>
|
|
131
|
-
{/* Custom cell column */}
|
|
132
|
-
<Browser.Table.Column
|
|
133
|
-
name={"emailType"}
|
|
134
|
-
after={"email"}
|
|
135
|
-
header={"Email Type"}
|
|
136
|
-
modelIds={["contactSubmission"]}
|
|
137
|
-
cell={<EmailTypeCell />}
|
|
138
|
-
/>
|
|
139
|
-
</ContentEntryListConfig>
|
|
140
|
-
);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
export default EmailEntryListColumn;
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
Register:
|
|
147
|
-
|
|
148
|
-
```tsx
|
|
149
|
-
<Admin.Extension src={"/extensions/contactSubmission/EmailEntryListColumn.tsx"} />
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Column Props Reference
|
|
153
|
-
|
|
154
|
-
| Prop | Type | Description |
|
|
155
|
-
|---|---|---|
|
|
156
|
-
| `name` | `string` | Unique column identifier |
|
|
157
|
-
| `header` | `string` | Column header text |
|
|
158
|
-
| `path` | `string` | Dot-path to the data field (e.g., `"values.email"`) -- for simple columns |
|
|
159
|
-
| `cell` | `ReactElement` | Custom React component for complex rendering |
|
|
160
|
-
| `modelIds` | `string[]` | Restrict column to specific content models |
|
|
161
|
-
| `after` | `string` | Position this column after another column by name |
|
|
162
|
-
|
|
163
|
-
### Custom Cell Hooks
|
|
164
|
-
|
|
165
|
-
Inside a custom `cell` component:
|
|
166
|
-
|
|
167
|
-
- `useTableRow<T>()` -- access the full row data, typed with your interface
|
|
168
|
-
- `isFolderRow(row)` -- check if the current row is a folder (return placeholder content)
|
|
169
|
-
|
|
170
|
-
## Custom Page-Type Forms
|
|
171
|
-
|
|
172
|
-
Create custom forms for Website Builder page types using Webiny's form components:
|
|
173
|
-
|
|
174
|
-
```tsx
|
|
175
|
-
// extensions/customPageTypes/RetailPageForm.tsx
|
|
176
|
-
import React from "react";
|
|
177
|
-
import { Grid, Input, Select } from "webiny/admin/ui";
|
|
178
|
-
import { pagePathFromTitle } from "webiny/admin/website-builder";
|
|
179
|
-
import type { FormApi } from "webiny/admin/form";
|
|
180
|
-
import { Bind, UnsetOnUnmount, useForm, validation } from "webiny/admin/form";
|
|
181
|
-
|
|
182
|
-
const generatePath = (form: FormApi) => () => {
|
|
183
|
-
const title = form.getValue("properties.title");
|
|
184
|
-
const language = form.getValue("extensions.language");
|
|
185
|
-
|
|
186
|
-
const titlePath = pagePathFromTitle(title ?? "");
|
|
187
|
-
const parts = [language, titlePath].filter(Boolean);
|
|
188
|
-
|
|
189
|
-
form.setValue("properties.path", `/${parts.join("/")}`);
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
export const RetailPageForm = () => {
|
|
193
|
-
const form = useForm();
|
|
194
|
-
|
|
195
|
-
return (
|
|
196
|
-
<>
|
|
197
|
-
<Grid.Column span={12}>
|
|
198
|
-
<UnsetOnUnmount name={"properties.title"}>
|
|
199
|
-
<Bind name={"properties.title"} validators={[validation.create("required")]}>
|
|
200
|
-
<Input label={"Title"} onBlur={generatePath(form)} />
|
|
201
|
-
</Bind>
|
|
202
|
-
</UnsetOnUnmount>
|
|
203
|
-
</Grid.Column>
|
|
204
|
-
<Grid.Column span={12}>
|
|
205
|
-
<UnsetOnUnmount name={"extensions.language"}>
|
|
206
|
-
<Bind
|
|
207
|
-
name={"extensions.language"}
|
|
208
|
-
validators={[validation.create("required")]}
|
|
209
|
-
afterChange={generatePath(form)}
|
|
210
|
-
>
|
|
211
|
-
<Select
|
|
212
|
-
placeholder={"Select a language"}
|
|
213
|
-
label={"Language"}
|
|
214
|
-
options={[
|
|
215
|
-
{ label: "English", value: "en" },
|
|
216
|
-
{ label: "German", value: "de" },
|
|
217
|
-
{ label: "French", value: "fr" }
|
|
218
|
-
]}
|
|
219
|
-
/>
|
|
220
|
-
</Bind>
|
|
221
|
-
</UnsetOnUnmount>
|
|
222
|
-
</Grid.Column>
|
|
223
|
-
<Grid.Column span={12}>
|
|
224
|
-
<UnsetOnUnmount name={"properties.path"}>
|
|
225
|
-
<Bind name={"properties.path"} validators={[validation.create("required")]}>
|
|
226
|
-
<Input label={"Path"} />
|
|
227
|
-
</Bind>
|
|
228
|
-
</UnsetOnUnmount>
|
|
229
|
-
</Grid.Column>
|
|
230
|
-
</>
|
|
231
|
-
);
|
|
232
|
-
};
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### Form Components Reference
|
|
236
|
-
|
|
237
|
-
| Component / Hook | Import | Purpose |
|
|
238
|
-
|---|---|---|
|
|
239
|
-
| `Bind` | `"webiny/admin/form"` | Bind a form field to a name path |
|
|
240
|
-
| `useForm()` | `"webiny/admin/form"` | Access the form API (`getValue`, `setValue`) |
|
|
241
|
-
| `validation` | `"webiny/admin/form"` | Create validators (`validation.create("required")`) |
|
|
242
|
-
| `UnsetOnUnmount` | `"webiny/admin/form"` | Clear the field value when the component unmounts |
|
|
243
|
-
| `Grid.Column` | `"webiny/admin/ui"` | Layout grid column (`span={12}` for full width) |
|
|
244
|
-
| `Input` | `"webiny/admin/ui"` | Text input field |
|
|
245
|
-
| `Select` | `"webiny/admin/ui"` | Dropdown select with options |
|
|
246
|
-
| `FormApi` | `"webiny/admin/form"` | Type for the form API object |
|
|
247
|
-
|
|
248
|
-
## Lexical Editor Plugins
|
|
249
|
-
|
|
250
|
-
Admin extensions can also add custom plugins to the Lexical rich text editor used in both the Headless CMS and the Website Builder. These are registered as `<Admin.Extension>` and use imports from `"webiny/admin/lexical"`, `"webiny/admin/cms/lexical"`, and `"webiny/admin/website-builder/lexical"`.
|
|
251
|
-
|
|
252
|
-
## Quick Reference
|
|
253
|
-
|
|
254
|
-
```
|
|
255
|
-
White-label import: import { AdminConfig } from "webiny/admin/configs";
|
|
256
|
-
Data list import: import { ContentEntryListConfig } from "webiny/admin/cms/entry/list";
|
|
257
|
-
Form imports: import { Bind, useForm, validation } from "webiny/admin/form";
|
|
258
|
-
UI imports: import { Grid, Input, Select } from "webiny/admin/ui";
|
|
259
|
-
Register: <Admin.Extension src={"/extensions/MyAdminExtension.tsx"} />
|
|
260
|
-
Develop: yarn webiny watch admin
|
|
261
|
-
Deploy: yarn webiny deploy admin
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
## Related Skills
|
|
265
|
-
|
|
266
|
-
- `project-structure` -- How to register Admin extensions
|
|
267
|
-
- `content-models` -- Define models that your list columns target
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: webiny-api-custom-feature
|
|
3
|
-
context: webiny-extensions
|
|
4
|
-
description: >
|
|
5
|
-
Creating custom API features with createFeature and createAbstraction.
|
|
6
|
-
Use this skill when the developer wants to define a new backend service,
|
|
7
|
-
register an abstraction in the DI container, create a reusable API module,
|
|
8
|
-
or wire up custom business logic as a Webiny feature. Covers the full pattern:
|
|
9
|
-
define an interface, create an abstraction token, implement and register
|
|
10
|
-
via createFeature, and register the extension in webiny.config.tsx.
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# Custom API Features
|
|
14
|
-
|
|
15
|
-
## TL;DR
|
|
16
|
-
|
|
17
|
-
Use `createFeature` and `createAbstraction` from `"webiny/api"` to define reusable backend services. An abstraction is a DI token typed to an interface. A feature registers concrete implementations into the DI container. Other extensions (GraphQL schemas, lifecycle hooks, etc.) can then declare the abstraction as a dependency and receive the implementation automatically.
|
|
18
|
-
|
|
19
|
-
## Pattern
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
// extensions/myFeature/MyFeature.ts
|
|
23
|
-
import { createFeature, createAbstraction } from "webiny/api";
|
|
24
|
-
|
|
25
|
-
// 1. Define the interface
|
|
26
|
-
export interface IMyService {
|
|
27
|
-
doSomething(): string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// 2. Create the abstraction (DI token)
|
|
31
|
-
export const MyService = createAbstraction<IMyService>("MyService");
|
|
32
|
-
|
|
33
|
-
// 3. Export the interface via namespace (convention for consumers)
|
|
34
|
-
export namespace MyService {
|
|
35
|
-
export type Interface = IMyService;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 4. Register the feature (default export)
|
|
39
|
-
export default createFeature({
|
|
40
|
-
name: "MyApp/MyFeature",
|
|
41
|
-
register(container) {
|
|
42
|
-
// Register a plain object instance
|
|
43
|
-
container.registerInstance(MyService, {
|
|
44
|
-
doSomething() {
|
|
45
|
-
return "Hello!";
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Core APIs
|
|
53
|
-
|
|
54
|
-
### `createAbstraction<T>(name: string)`
|
|
55
|
-
|
|
56
|
-
Creates a typed DI token (an `Abstraction<T>` instance). The generic `T` is the interface that implementations must satisfy. The `name` string is used for debugging and error messages.
|
|
57
|
-
|
|
58
|
-
| Import | `import { createAbstraction } from "webiny/api"` |
|
|
59
|
-
|---|---|
|
|
60
|
-
| Returns | `Abstraction<T>` |
|
|
61
|
-
| Usage | Pass to `container.registerInstance()`, `container.register()`, or as a dependency in other extensions |
|
|
62
|
-
|
|
63
|
-
### `createFeature(def)`
|
|
64
|
-
|
|
65
|
-
Creates a feature definition that the framework loads as an API extension. Must be the **default export** of the file.
|
|
66
|
-
|
|
67
|
-
| Import | `import { createFeature } from "webiny/api"` |
|
|
68
|
-
|---|---|
|
|
69
|
-
| `def.name` | Unique feature name (convention: `"AppName/FeatureName"`) |
|
|
70
|
-
| `def.register(container)` | Called at startup with the DI `Container` instance |
|
|
71
|
-
|
|
72
|
-
### Container Registration Methods
|
|
73
|
-
|
|
74
|
-
| Method | When to Use |
|
|
75
|
-
|---|---|
|
|
76
|
-
| `container.registerInstance(abstraction, instance)` | Register a plain object that satisfies the interface |
|
|
77
|
-
| `container.register(Implementation)` | Register a class (created via `Abstraction.createImplementation`) |
|
|
78
|
-
| `container.registerFactory(abstraction, () => instance)` | Register a lazy factory |
|
|
79
|
-
|
|
80
|
-
## Extension Registration
|
|
81
|
-
|
|
82
|
-
Register the feature file using `<Api.Extension>` in your extension's React entry point:
|
|
83
|
-
|
|
84
|
-
```tsx
|
|
85
|
-
// extensions/myFeature/Extension.tsx
|
|
86
|
-
import React from "react";
|
|
87
|
-
import { Api } from "webiny/extensions";
|
|
88
|
-
|
|
89
|
-
export const MyFeature = () => {
|
|
90
|
-
return <Api.Extension src={"@/extensions/myFeature/MyFeature.ts"} />;
|
|
91
|
-
};
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Then mount it in `webiny.config.tsx` or your app's extension composition.
|
|
95
|
-
|
|
96
|
-
## Examples
|
|
97
|
-
|
|
98
|
-
### Simple Service with Instance Registration
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
// extensions/pricing/PricingFeature.ts
|
|
102
|
-
import { createFeature, createAbstraction } from "webiny/api";
|
|
103
|
-
|
|
104
|
-
export interface IPricingService {
|
|
105
|
-
calculatePrice(basePrice: number, quantity: number): number;
|
|
106
|
-
getDiscount(customerId: string): Promise<number>;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export const PricingService = createAbstraction<IPricingService>("PricingService");
|
|
110
|
-
|
|
111
|
-
export namespace PricingService {
|
|
112
|
-
export type Interface = IPricingService;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export default createFeature({
|
|
116
|
-
name: "MyApp/Pricing",
|
|
117
|
-
register(container) {
|
|
118
|
-
container.registerInstance(PricingService, {
|
|
119
|
-
calculatePrice(basePrice, quantity) {
|
|
120
|
-
return basePrice * quantity;
|
|
121
|
-
},
|
|
122
|
-
async getDiscount(_customerId) {
|
|
123
|
-
return 0.1; // 10% default discount
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Service with Class Registration and Dependencies
|
|
131
|
-
|
|
132
|
-
When your service itself needs dependencies, use `createImplementation` on the abstraction and `container.register()`:
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
// extensions/notifications/NotificationFeature.ts
|
|
136
|
-
import { createFeature, createAbstraction } from "webiny/api";
|
|
137
|
-
import { Logger } from "webiny/api/logger";
|
|
138
|
-
|
|
139
|
-
export interface INotificationService {
|
|
140
|
-
notify(userId: string, message: string): Promise<void>;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export const NotificationService = createAbstraction<INotificationService>("NotificationService");
|
|
144
|
-
|
|
145
|
-
export namespace NotificationService {
|
|
146
|
-
export type Interface = INotificationService;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Class implementation with injected dependencies
|
|
150
|
-
class NotificationServiceImpl implements INotificationService {
|
|
151
|
-
constructor(private logger: Logger.Interface) {}
|
|
152
|
-
|
|
153
|
-
async notify(userId: string, message: string) {
|
|
154
|
-
this.logger.info(`Notifying ${userId}: ${message}`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Attach DI metadata to the class
|
|
159
|
-
const NotificationServiceRegistration = NotificationService.createImplementation({
|
|
160
|
-
implementation: NotificationServiceImpl,
|
|
161
|
-
dependencies: [Logger]
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
export default createFeature({
|
|
165
|
-
name: "MyApp/Notifications",
|
|
166
|
-
register(container) {
|
|
167
|
-
container.register(NotificationServiceRegistration);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
## Key Rules
|
|
173
|
-
|
|
174
|
-
1. **Default export** -- `createFeature()` result must be the default export of the file.
|
|
175
|
-
2. **One feature per file** -- each `.ts` file loaded via `<Api.Extension>` should export one feature.
|
|
176
|
-
3. **Namespace convention** -- export `namespace MyService { export type Interface = IMyService; }` so consumers can type dependencies as `MyService.Interface`.
|
|
177
|
-
4. **Name uniqueness** -- feature names must be globally unique; use `"AppName/FeatureName"` convention.
|
|
178
|
-
5. **Constructor param order** -- when using class registration, the `dependencies` array must match constructor parameter order exactly.
|
|
179
|
-
|
|
180
|
-
## Quick Reference
|
|
181
|
-
|
|
182
|
-
| What | How |
|
|
183
|
-
|---|---|
|
|
184
|
-
| Import | `import { createFeature, createAbstraction } from "webiny/api"` |
|
|
185
|
-
| Create token | `const MyService = createAbstraction<IMyService>("MyService")` |
|
|
186
|
-
| Register instance | `container.registerInstance(MyService, { ... })` |
|
|
187
|
-
| Register class | `container.register(MyService.createImplementation({ implementation, dependencies }))` |
|
|
188
|
-
| Extension entry | `<Api.Extension src={"@/extensions/myFeature/MyFeature.ts"} />` |
|
|
189
|
-
| Deploy | `yarn webiny deploy api --env=dev` |
|
|
190
|
-
|
|
191
|
-
## Related Skills
|
|
192
|
-
|
|
193
|
-
- **webiny-dependency-injection** -- Full DI pattern details, all injectable services, decorator and composite patterns
|
|
194
|
-
- **webiny-custom-graphql-api** -- How to consume your custom feature's abstraction in a GraphQL schema
|
|
195
|
-
- **webiny-project-structure** -- Extension file layout and `webiny.config.tsx` registration
|