Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ build
/src/data/
/src/pages/docs/
/public/algolia/sitemap.xml

/public/docs-static/raw/
# Mac files
.DS_Store

# Bruno
v6
remote
.claude/settings.local.json
11 changes: 11 additions & 0 deletions .mdx-validation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"targetDir": "docs/developer-docs/6.0.x",
"exceptions": [
"get-started/welcome.mdx",
"get-started/install-webiny.mdx",
"overview/pricing.mdx",
"overview/features/security.mdx",
"tenant-manager/manage-tenants.mdx",
"tenant-manager/extend-tenant-model.mdx"
]
}
226 changes: 226 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# Project Instructions

## Critical Project Conventions

### Package Manager
- **ALWAYS use `yarn`, NEVER use `npm`** - This project exclusively uses Yarn
- All script execution: `yarn <script-name>`
- All package operations: `yarn add`, `yarn install`, etc.

### Validation and Quality
- **MDX/.ai.txt Pairing**: Every `.mdx` file must have a corresponding `.ai.txt` companion file
- Run `yarn validate:mdx` to verify pairing before commits/PRs
- Exceptions are defined in `.mdx-validation.json` (supports exact paths and glob patterns)
- The validation script (`scripts/validate-mdx-pairing.ts`) checks bidirectionally

### Project Structure
- Documentation: `docs/developer-docs/6.0.x/`
- Plans/design docs: `plans/` (project root)
- Scripts: `scripts/` (TypeScript scripts executed via `tsx`)
- Validation config: `.mdx-validation.json` (project root)

## Documentation Structure
- Documentation files live in `docs/developer-docs/6.0.x/`
- `.mdx` files are the documentation pages
- `.ai.txt` files are AI companion context files — read them to understand the corresponding `.mdx` file (sources, decisions, patterns, tone guidelines)

### Directory Layout
```
docs/developer-docs/6.0.x/
├── basic/ # Foundational patterns: DI (createAbstraction/createImplementation/createDecorator), Result pattern
├── get-started/ # Welcome + installation guide
├── graphql/ # GraphQL schema building with factory pattern
├── headless-cms/ # Largest section — all CMS extensibility
│ ├── builder/ # ModelBuilder, GroupBuilder, FieldBuilder APIs
│ ├── event-handler/ # Entry/model/group lifecycle events
│ ├── examples/ # Private model, single-entry model
│ ├── ui/ # Field renderers
│ └── use-case/ # Entry/model/group business logic
├── overview/ # Pricing, security features
│ └── features/
├── tasks/ # Background task system (Runner, Context, Controller)
└── website-builder/ # Website Builder extensibility
├── event-handler/ # Page/redirect lifecycle events
└── use-case/ # Page/redirect business logic
```

### Key Patterns
- Each major system follows a consistent taxonomy: **builder** (define structure) / **event-handler** (react to events) / **use-case** (custom business logic)
- `basic/di.mdx` and `basic/result.mdx` are foundational — referenced by all other sections
- `.ai.txt` files contain: source info, key decisions, understanding, code patterns, related docs, and tone guidelines
- Tone is calibrated per doc type: conceptual (about), technical (reference), practical (examples)

### MDX/.ai.txt Pairing Exceptions
Current exceptions (defined in `.mdx-validation.json`):
- `get-started/welcome.mdx` - introductory page
- `get-started/install-webiny.mdx` - installation guide
- `overview/pricing.mdx` - pricing overview
- `overview/features/security.mdx` - security features

## MDX Writing Conventions

### Frontmatter
Every `.mdx` file uses exactly three required fields:
```yaml
---
id: <8-char-alphanumeric> # e.g., "kp9m2xnf" — short, random, lowercase
title: <Title Case>
description: <One-line description>
---
```
Optional fields (rare, only for special pages like welcome): `pageHeader: false`, `fullWidth: true`.

### Page Structure
Every page follows this order:
1. Frontmatter
2. Component imports
3. `<Alert type="success" title="WHAT YOU'LL LEARN">` block with bullet list of questions
4. `## Overview` — always the first H2, 1-3 paragraphs of prose
5. Content sections as H2 (`##`) with H3 (`###`) subsections as needed
6. No H1 headings in the body — the page title comes from frontmatter

### Components
Only one MDX component is used: `<Alert>` from `@/components/Alert`.
```mdx
import {Alert} from "@/components/Alert";
```

Alert types:
- `type="success"` — "WHAT YOU'LL LEARN" opener (every page)
- `type="info"` — supplemental links or context (inline, overview pages)
- `type="warning"` — important cautions (inline, overview pages)

No other custom components are used (no `<Tabs>`, `<Steps>`, etc.).

### Code Blocks
- Language tag is always specified: ` ```typescript ` or ` ```graphql `
- **File path annotations** go after the language tag: ` ```typescript extensions/cms/group/eventHandler/create/beforeCreate.ts `
- Use ` ```tsx ` for `webiny.config.tsx` files
- GraphQL SDL in TypeScript uses `/* GraphQL */` tag: `builder.addTypeDefs(/* GraphQL */ \`...\`)`
- Minimal comments in code — only for pedagogical correct/wrong comparisons using `// ✅ Correct` / `// ❌ Wrong`
- No shell/bash code blocks

### Import Paths in Code Examples
```typescript
// Webiny v6 API imports — use "webiny/" prefix, NOT "@webiny/"
import { ModelFactory } from "webiny/api/cms/model";
import { CreateEntryUseCase } from "webiny/api/cms/entry";
import { Logger } from "webiny/api/logger";
import { Api } from "webiny/extensions";
import { Result } from "webiny/api";

// Local file imports use .js extensions (ESM)
import { BookRepository } from "./abstractions/BookRepository.js";

// Type-only imports
import type { CmsEntry } from "webiny/api";
```

### Text Formatting
- Backticks for: code, class names, method names, type names, package names
- **Bold** for key labels: `**Naming Convention:**`, `**Key Point:**`
- Bullet lists use `-`, not numbered lists (even for sequential steps)
- No emojis in prose
- Inline links use standard markdown: `[text](/docs/path)`
- "Webiny" always capitalized

## Tone and Voice

### General Rules
- Concise and technical — no filler, no marketing language in developer docs
- Direct and instructional — "Use `createAbstraction()` to create one"
- Address the reader as "you"
- Explain "why" briefly before showing "how"
- Avoid "we" statements — use "The system provides/offers" instead
- No analogies in published docs (save those for `.ai.txt` context)
- Accessible to junior developers while remaining technically complete

### Per Doc Type
| Doc Type | Tone | Focus |
|---|---|---|
| `about.mdx` | Conceptual, accessible | Why and what, link to reference for how |
| `reference.mdx` | Simple, concise, API-focused | Method signatures, minimal examples |
| `example.mdx` | Practical, production-ready | Complete working code, copy-paste friendly |
| `event-handler/*.mdx` | Production-ready examples | When/why to use each event, real-world scenarios |
| `use-case/*.mdx` | Technical and complete | Full abstraction + implementation patterns |
| `builder/*.mdx` | Technical reference | Complete method docs, practical examples per type |
| `management.mdx` | Practical, operation-focused | Complete examples, error handling |
| `get-started/`, `overview/` | Conversational, welcoming | High-level survey, no code |

## TypeScript Code Patterns

### DI Pattern (used in all implementations)
```typescript
import { SomeAbstraction } from "webiny/api/cms/entity";
import { Logger } from "webiny/api/logger";

// Implementation class with Impl suffix
class SomeAbstractionImpl implements SomeAbstraction.Interface {
public constructor(private logger: Logger.Interface) {}

public async handle(event: SomeAbstraction.Event): Promise<void> {
// implementation
}
}

// Factory registration — MUST be default export
const SomeAbstractionConst = SomeAbstraction.createImplementation({
implementation: SomeAbstractionImpl,
dependencies: [Logger]
});

export default SomeAbstractionConst;
```

### Result Pattern (used in all use cases)
```typescript
const result = await someUseCase.execute(params);
if (result.isFail()) {
// handle error — return early (guard clause)
return { error: result.error, data: null };
}
const value = result.value;
```

### Namespace Export Pattern (abstractions)
```typescript
export namespace MyAbstraction {
export type Interface = IMyAbstraction;
export type Params = IMyAbstractionParams;
export type Return = IMyAbstractionReturn;
}
```

### Registration in webiny.config.tsx
```tsx
import React from "react";
import { Api } from "webiny/extensions";

export const Extensions = () => {
return (
<>
{/* ... all your other extensions */}
<Api.Extension src={"/extensions/cms/entity/path/to/file.ts"} />
</>
);
};
```

### Event Handler Naming
- Class names: `On[Resource][Before|After][Operation]Handler` (e.g., `OnPageBeforeCreateHandler`)
- Abstraction tokens: `[Resource][Before|After][Operation]Handler` (without "On" prefix)
- "Before" handlers receive `payload.input`; "After" handlers receive the completed entity (e.g., `payload.page`)

## .ai.txt File Format
Each `.ai.txt` companion file contains these sections:
1. `AI Context: [Page Title] ([filename])` — header
2. `Source of Information:` — numbered list of research sources
3. `Key Documentation Decisions:` — editorial choices made
4. `[Topic-specific understanding]` — detailed domain knowledge
5. `[Code patterns/snippets]` — verified TypeScript templates
6. `Related Documents:` — cross-references to sibling docs
7. `Key Code Locations:` — source code paths in the Webiny repo
8. `Tone Guidelines:` — explicit style instructions for the page

### Maintenance
- When new documentation files (`.mdx` or `.ai.txt`) are added, update the directory layout and notes in this AGENTS.md to reflect the changes.
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Startup
- Always read `~/.claude/settings.json` at the start of every conversation.
- Read `./AGENTS.md"
59 changes: 55 additions & 4 deletions docs.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,78 @@ const linkWhitelist: string[] = [...redirects.map(r => r.source), "/forms/produc
*/
const whitelistedVersions: string[] = [];

/**
* Only build versions at or above this version (e.g., "5.40.x").
* Set via MIN_VERSION environment variable or modify here.
* Set to empty string to build all versions.
*/
const minVersionToBuild = process.env.MIN_VERSION || "";

const filterByEnvironment = (version: Version) => {
// In `preview`, if there are specific versions whitelisted for deployment, those are the only ones we'll output.
if (preview && whitelistedVersions.length > 0) {
return whitelistedVersions.includes(version.getValue());
}

// If minVersionToBuild is set, only build versions >= minVersion or `latest`.
if (minVersionToBuild) {
if (minVersionToBuild === "latest") {
return version.isLatest();
}
const versionNum = parseFloat(version.getValue().replace(/[^\d.]/g, ""));
const minVersionNum = parseFloat(minVersionToBuild.replace(/[^\d.]/g, ""));
return versionNum >= minVersionNum;
}

// Build everything.
return true;
};

const filterFilePathByVersion = (filePath: string): boolean => {
// Extract version from file path (e.g., /docs/developer-docs/5.40.x/... or /docs/user-guides/5.40.x/...)
const versionMatch = filePath.match(/\/(\d+\.\d+\.x)\//);

if (!versionMatch) {
// If no version in path, include the file (e.g., non-versioned docs)
return true;
}

const versionString = versionMatch[1];

// Use the same filtering logic as filterByEnvironment
if (preview && whitelistedVersions.length > 0) {
return whitelistedVersions.includes(versionString);
}

if (minVersionToBuild) {
if (minVersionToBuild === "latest") {
// For file paths, we can't determine if it's "latest" without more context
// So we'll include all versioned files when minVersionToBuild is "latest"
return true;
}
const versionNum = parseFloat(versionString.replace(/[^\d.]/g, ""));
const minVersionNum = parseFloat(minVersionToBuild.replace(/[^\d.]/g, ""));
return versionNum >= minVersionNum;
}

return true;
};

const existsInDocs = (link: string) => {
return fs.pathExists(path.join(__dirname, `src/pages/${link}.js`));
};

export default {
projectRootDir: __dirname,
cleanOutputDir: path.resolve("src/pages/docs"),
cleanOutputDir: [path.resolve("src/pages/docs"), path.resolve("public/raw/docs")],
sitemapOutputPath: path.resolve("public/algolia/sitemap.xml"),
linkValidator: new LinkValidator(linkWhitelist, link => {
return existsInDocs(link);
}),
linkValidator: new LinkValidator(
linkWhitelist,
link => {
return existsInDocs(link);
},
filterFilePathByVersion
),
documentRoots: [
/* Developer Docs */
new VersionedDocumentRootConfig({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ import { createWatchApp, createBuildApp } from "@webiny/project-utils";

// Exports fundamental watch and build commands.
// Need to inject environment variables or link your application with an existing GraphQL API?
// See https://www.webiny.com/docs/core-development-concepts/scaffolding/full-stack-application/webiny-config.
// See https://www.webiny.com/docs/{version}/core-development-concepts/scaffolding/full-stack-application/webiny-config.
export default {
commands: {
async watch(options, context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ import Cloudfront from "./cloudfront";
const DEBUG = String(process.env.DEBUG);

// Enables logs forwarding.
// https://www.webiny.com/docs/core-development-concepts/basics/watch-command#enabling-logs-forwarding
// https://www.webiny.com/docs/{version}/core-development-concepts/basics/watch-command#enabling-logs-forwarding
const WEBINY_LOGS_FORWARD_URL = String(process.env.WEBINY_LOGS_FORWARD_URL);

export default () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ import Cognito from "./cognito";
import S3 from "./s3";

// Among other things, this determines the amount of information we reveal on runtime errors.
// https://www.webiny.com/docs/core-development-concepts/environment-variables/#debug-environment-variable
// https://www.webiny.com/docs/{version}/core-development-concepts/environment-variables/#debug-environment-variable
const DEBUG = String(process.env.DEBUG);

// Enables logs forwarding.
Expand Down
2 changes: 1 addition & 1 deletion docs/developer-docs/5.28.x/get-started/install-webiny.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Before proceeding, make sure you have the following:
- Webiny works with both yarn versions [1 (classic)](https://yarnpkg.com/en/docs/install) and [>=2 (berry)](https://yarnpkg.com/)
- for version 1 - **1.22.0** or later is required
3. **AWS account and user credentials**
- in order to deploy Webiny, you must have a valid [AWS account and user credentials](/docs/infrastructure/aws/configure-aws-credentials) set up on your system
- in order to deploy Webiny, you must have a valid [AWS account and user credentials](/docs/{version}/infrastructure/aws/configure-aws-credentials) set up on your system

## Project Setup

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@ In certain cases, this might be reasonable. For example, you can reduce developm

<Alert type="info">

Depending on the environment, the **API** project application is deployed as two different sets of cloud infrastructure resources - development and production. Visit the [API Overview - Default VPC](/docs/architecture/deployment-modes/development) key topic to learn more.
Depending on the environment, the **API** project application is deployed as two different sets of cloud infrastructure resources - development and production. Visit the [API Overview - Default VPC](/docs/{version}/architecture/deployment-modes/development) key topic to learn more.

</Alert>
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ Currently, Webiny is not using the [Automation API](https://www.pulumi.com/blog/
Switching to a different infrastructure-as-code (IaC) solution would require a significant amount of work as it would involve rewriting almost all of the Webiny IaC code from scratch for a different IaC solution. **Therefore, we do not recommend it.**

However, if you want to experiment with it, you would need to create a custom CLI plugin similar to the [Webiny CLI plugin for Pulumi](https://github.com/webiny/webiny-js/tree/next/packages/cli-plugin-deploy-pulumi). This plugin is responsible for creating the deploy, destroy, watch, and other commands.
To use your custom plugin, you would then need to import it into the `webiny.project.ts` file and remove the Pulumi plugin. The most challenging part would be transforming all of the Pulumi code into respective IaC code (i.e. transforming all Pulumi code to [CloudFormation](https://aws.amazon.com/cloudformation/), [Terraform](https://www.terraform.io/), or other IaC). You can refer to the Pulumi code to see the resources Webiny deploys and the [Cloud Infrastructure documentation](https://www.webiny.com/docs/architecture/introduction) for more information.
To use your custom plugin, you would then need to import it into the `webiny.project.ts` file and remove the Pulumi plugin. The most challenging part would be transforming all of the Pulumi code into respective IaC code (i.e. transforming all Pulumi code to [CloudFormation](https://aws.amazon.com/cloudformation/), [Terraform](https://www.terraform.io/), or other IaC). You can refer to the Pulumi code to see the resources Webiny deploys and the [Cloud Infrastructure documentation](https://www.webiny.com/docs/{version}/architecture/introduction) for more information.

**Please keep in mind that this process will require a significant amount of effort, and it's recommended to stick with Pulumi unless your organization has strict policies that require using a different IaC solution.**

Expand Down
Loading