@gradial/aci 0.1.30 → 0.1.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/bin/aci-darwin-arm64 +0 -0
- package/bin/aci.js +33 -32
- package/dist/astro/assets.d.ts +46 -0
- package/dist/astro/assets.js +51 -0
- package/dist/astro/index.d.ts +2 -0
- package/dist/astro/index.js +8 -0
- package/dist/astro/page.d.ts +88 -0
- package/dist/astro/page.js +117 -0
- package/dist/content/provider.d.ts +7 -0
- package/dist/dev/content-watch.d.ts +1 -0
- package/dist/dev/content-watch.js +1 -1
- package/dist/next/config.d.ts +6 -0
- package/dist/next/config.js +20 -13
- package/dist/next/content-watch.d.ts +1 -0
- package/dist/next/content-watch.js +2 -2
- package/dist/next/index.d.ts +1 -0
- package/dist/next/index.js +1 -0
- package/dist/next/middleware.d.ts +3 -2
- package/dist/next/middleware.js +40 -7
- package/dist/next/page.d.ts +13 -5
- package/dist/next/page.js +42 -17
- package/dist/next/server.d.ts +1 -1
- package/dist/next/server.js +33 -9
- package/dist/providers/s3.d.ts +7 -0
- package/dist/providers/s3.js +20 -0
- package/dist/testing/conformance/checks.d.ts +12 -0
- package/dist/testing/conformance/checks.js +483 -0
- package/dist/testing/conformance/index.d.ts +3 -0
- package/dist/testing/conformance/index.js +2 -0
- package/dist/testing/conformance/preset.d.ts +14 -0
- package/dist/testing/conformance/preset.js +98 -0
- package/dist/testing/conformance/types.d.ts +45 -0
- package/dist/testing/conformance/types.js +7 -0
- package/package.json +17 -9
- package/src/cli/materialize-template.mjs +133 -0
- package/src/cli/validate-content.mjs +0 -0
- package/src/cli/verify-next-cdn-routes.mjs +10 -19
- package/templates/astro/starter-content/src/cms/contracts/components/button.contract.ts +19 -0
- package/templates/astro/starter-content/src/cms/contracts/components/container.contract.ts +19 -0
- package/templates/astro/starter-content/src/cms/contracts/components/featureCard.contract.ts +15 -0
- package/templates/astro/starter-content/src/cms/contracts/components/featureGrid.contract.ts +16 -0
- package/templates/astro/starter-content/src/cms/contracts/components/homeHero.contract.ts +36 -0
- package/templates/astro/starter-content/src/cms/contracts/components/index.ts +21 -0
- package/templates/astro/starter-content/src/cms/contracts/components/statsBar.contract.ts +19 -0
- package/templates/astro/starter-content/src/cms/registry.ts +28 -0
- package/templates/astro/template/{.aci.yaml → .aci.yaml.tmpl} +1 -1
- package/templates/astro/template/README.md.tmpl +53 -0
- package/templates/astro/template/gitignore +12 -0
- package/templates/astro/template/package.json.tmpl +5 -3
- package/templates/astro/template/src/cms/contracts/components/index.ts +3 -0
- package/templates/astro/template/src/cms/registry.ts +3 -26
- package/templates/astro/template/src/pages/[...slug].astro +13 -35
- package/templates/astro/template/tests/conformance.test.ts.tmpl +12 -0
- package/templates/common/.agents/AGENTS.md +3 -5
- package/templates/common/.agents/skills/aci/SKILL.md +72 -23
- package/templates/common/.agents/skills/authoring/SKILL.md +53 -14
- package/templates/common/.agents/skills/authoring/references/block-registry.md +109 -15
- package/templates/common/.agents/skills/authoring/references/contracts.md +23 -7
- package/templates/common/.agents/skills/authoring/references/repo-structure.md +35 -19
- package/templates/common/.agents/skills/authoring/references/sdk-server-functions.md +75 -17
- package/templates/common/.agents/skills/migration/SKILL.md +7 -1
- package/templates/common/.agents/skills/migration/references/canon-migration.md +471 -0
- package/templates/common/.content/config/site.json.tmpl +15 -0
- package/templates/common/.content/pages/home/_index.json +4 -103
- package/templates/common/.nvmrc +1 -0
- package/templates/common/README.md.tmpl +32 -11
- package/templates/common/contracts/components/index.ts +1 -19
- package/templates/common/design-system/tokens/colors.css +34 -10
- package/templates/common/starter-content/.content/config/robots.txt +2 -0
- package/templates/common/starter-content/.content/pages/home/_index.json +112 -0
- package/templates/common/starter-content/.content/schemas/site.schema.json +138 -0
- package/templates/nextjs/starter-content/src/cms/contracts/components/container.contract.ts +19 -0
- package/templates/nextjs/starter-content/src/cms/contracts/components/featureCard.contract.ts +15 -0
- package/templates/nextjs/starter-content/src/cms/contracts/components/featureGrid.contract.ts +16 -0
- package/templates/nextjs/starter-content/src/cms/contracts/components/index.ts +21 -0
- package/templates/nextjs/starter-content/src/cms/contracts/components/siteFooter.contract.ts +45 -0
- package/templates/nextjs/starter-content/src/cms/contracts/components/siteNavigation.contract.ts +30 -0
- package/templates/nextjs/starter-content/src/cms/contracts/components/statsBar.contract.ts +19 -0
- package/templates/nextjs/starter-content/src/cms/registry.ts +28 -0
- package/templates/nextjs/template/{.aci.yaml → .aci.yaml.tmpl} +1 -1
- package/templates/nextjs/template/README.md.tmpl +52 -0
- package/templates/nextjs/template/gitignore +15 -0
- package/templates/nextjs/template/next.config.mjs +1 -6
- package/templates/nextjs/template/package.json.tmpl +5 -5
- package/templates/nextjs/template/src/app/gradial/assets/[...path]/route.ts +1 -4
- package/templates/nextjs/template/src/app/layout.tsx +1 -3
- package/templates/nextjs/template/src/cms/contracts/components/index.ts +1 -13
- package/templates/nextjs/template/src/cms/registry.ts +3 -26
- package/templates/nextjs/template/src/design-system/styles.css +346 -0
- package/templates/nextjs/template/src/design-system/tokens/colors.css +110 -0
- package/templates/nextjs/template/src/design-system/tokens/components.css +86 -0
- package/templates/nextjs/template/src/design-system/tokens/index.css +6 -0
- package/templates/nextjs/template/src/design-system/tokens/radii.css +5 -0
- package/templates/nextjs/template/src/design-system/tokens/shadows.css +9 -0
- package/templates/nextjs/template/src/design-system/tokens/spacing.css +26 -0
- package/templates/nextjs/template/src/design-system/tokens/typography.css +32 -0
- package/templates/nextjs/template/src/middleware.ts.tmpl +12 -0
- package/templates/nextjs/template/tests/conformance.test.ts.tmpl +12 -0
- package/templates/nextjs/template/src/app/_components/ConsentManager.tsx +0 -132
- package/templates/nextjs/template/src/cms/contracts/components/container.contract.ts +0 -20
- package/templates/nextjs/template/src/middleware.ts +0 -7
- /package/templates/{nextjs/template → astro/starter-content}/src/cms/contracts/components/siteFooter.contract.ts +0 -0
- /package/templates/{nextjs/template → astro/starter-content}/src/cms/contracts/components/siteNavigation.contract.ts +0 -0
- /package/templates/astro/{template → starter-content}/src/components/chrome/Footer.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/chrome/Header.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/elements/AtomicBlock.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/elements/AtomicBlocks.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/elements/Button.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/elements/ButtonBlock.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/elements/Container.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/elements/Icon.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/elements/Logo.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/elements/RemoteImage.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/sections/ContainerSection.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/sections/FeatureCard.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/sections/FeatureGrid.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/sections/HomeHero.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/components/sections/StatsBar.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/fixtures/atomicBlocks.ts +0 -0
- /package/templates/astro/{template → starter-content}/src/fixtures/composition.ts +0 -0
- /package/templates/astro/{template → starter-content}/src/fixtures/index.ts +0 -0
- /package/templates/astro/{template → starter-content}/src/fixtures/sections.ts +0 -0
- /package/templates/astro/{template → starter-content}/src/render/RenderPage.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/stories/CompositionStoryFrame.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/stories/HomeHero.stories.ts +0 -0
- /package/templates/astro/{template → starter-content}/src/stories/SectionStoryFrame.astro +0 -0
- /package/templates/astro/{template → starter-content}/src/stories/StoryDocFrame.astro +0 -0
- /package/templates/common/{.content → starter-content/.content}/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
- /package/templates/common/{.content → starter-content/.content}/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
- /package/templates/common/{.content → starter-content/.content}/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
- /package/templates/common/{.content → starter-content/.content}/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
- /package/templates/common/{.content → starter-content/.content}/config/site.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/dam/folders/seed/ast_site_about_hero/asset.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/dam/folders/seed/ast_site_about_hero/editorial.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/dam/folders/seed/ast_site_about_hero/governance.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/dam/folders/seed/ast_site_about_hero/versions/v0001.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/dam/folders/seed/ast_site_home_hero/asset.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/dam/folders/seed/ast_site_home_hero/editorial.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/dam/folders/seed/ast_site_home_hero/governance.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/dam/folders/seed/ast_site_home_hero/versions/v0001.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/fragments/navbar/_index.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/fragments/site-footer/_index.json +0 -0
- /package/templates/common/{.content → starter-content/.content}/pages/about/_index.json +0 -0
- /package/templates/common/{public → starter-content/public}/assets/Dwire-model.png +0 -0
- /package/templates/common/{public → starter-content/public}/assets/seattle.avif +0 -0
- /package/templates/nextjs/{template → starter-content}/src/cms/contracts/components/button.contract.ts +0 -0
- /package/templates/nextjs/{template → starter-content}/src/cms/contracts/components/homeHero.contract.ts +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/chrome/Footer.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/chrome/Header.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/elements/AtomicBlock.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/elements/Button.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/elements/ButtonBlock.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/elements/Container.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/elements/Icon.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/elements/Logo.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/sections/ContainerSection.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/sections/FeatureCard.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/sections/FeatureGrid.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/sections/HomeHero.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/components/sections/StatsBar.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/stories/Button.stories.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/stories/Header.stories.tsx +0 -0
- /package/templates/nextjs/{template → starter-content}/src/stories/HomeHero.stories.tsx +0 -0
|
@@ -5,6 +5,31 @@ description: Onboarding, running builds, validating, and troubleshooting ACI sit
|
|
|
5
5
|
|
|
6
6
|
# ACI — Onboarding and Troubleshooting
|
|
7
7
|
|
|
8
|
+
## Creating a New Site
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# Scaffold-only (default) — golden-path skeleton, no demo content
|
|
12
|
+
# Astro is standard for new customer sites
|
|
13
|
+
aci init my-site --template astro-starter
|
|
14
|
+
|
|
15
|
+
# Next.js is for specific SSR properties
|
|
16
|
+
aci init my-site --template next-starter
|
|
17
|
+
|
|
18
|
+
# With demo content, components, and stories
|
|
19
|
+
aci init my-site --template astro-starter --with-starter-content
|
|
20
|
+
|
|
21
|
+
# List available templates
|
|
22
|
+
aci init --list
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The **scaffold** is the new-customer entry point: config, framework wiring,
|
|
26
|
+
page route, empty registry, design system, and agent skills. Add your own
|
|
27
|
+
contracts and components from here.
|
|
28
|
+
|
|
29
|
+
The **--with-starter-content** flag overlays demo material on top of the
|
|
30
|
+
scaffold: `.content/` fixtures, demo components (sections, elements, chrome),
|
|
31
|
+
a full registry, stories, and fixtures.
|
|
32
|
+
|
|
8
33
|
## How ACI Works
|
|
9
34
|
|
|
10
35
|
```
|
|
@@ -41,8 +66,11 @@ Each framework uses a packaged SDK adapter for ACI dev/build behavior:
|
|
|
41
66
|
- **Next.js**: `withAci()` in `next.config.ts`
|
|
42
67
|
|
|
43
68
|
These adapters own canonical ACI behavior: content watch/reload, local DAM
|
|
44
|
-
derivative serving at `/.gradial-dam/*`,
|
|
45
|
-
|
|
69
|
+
derivative serving at `/.gradial-dam/*`, asset path rewrites, output mode,
|
|
70
|
+
outputFileTracingRoot, base-path/build-mode/dist-dir env injection, CDN
|
|
71
|
+
headers, and content-watch plugin. Repos should pass an empty `withAci()`
|
|
72
|
+
call and rely on SDK defaults. Use `passthroughRoutes` only for
|
|
73
|
+
site-specific non-CMS routes.
|
|
46
74
|
|
|
47
75
|
Only Next.js and Astro are supported. SvelteKit support has been removed.
|
|
48
76
|
|
|
@@ -52,14 +80,11 @@ All commands run from the site directory (where `.aci.yaml` lives).
|
|
|
52
80
|
|
|
53
81
|
| Command | Purpose |
|
|
54
82
|
|---------|---------|
|
|
55
|
-
| `npm run
|
|
56
|
-
| `npm run
|
|
57
|
-
| `npm run aci:validate` | Validate content against schemas without building. |
|
|
58
|
-
| `npm run aci:doctor` | Check project health: config, content, schemas, routes. |
|
|
59
|
-
| `npm run aci:build` | Full build: contracts + content + framework. |
|
|
83
|
+
| `npm run dev` | Start dev server with content hot-reload (auto-compiles content). |
|
|
84
|
+
| `npm run build` | Framework production build. |
|
|
60
85
|
| `npm run typecheck` | TypeScript type checking. |
|
|
61
|
-
| `npm
|
|
62
|
-
| `npm run
|
|
86
|
+
| `npm test` | Run conformance tests. |
|
|
87
|
+
| `npm run content:compile` | Compile content from `.content/` into `.aci/compiled/`. Standard: `aci content compile --content .content --out .aci/compiled` |
|
|
63
88
|
|
|
64
89
|
## Development Workflow
|
|
65
90
|
|
|
@@ -67,39 +92,63 @@ All commands run from the site directory (where `.aci.yaml` lives).
|
|
|
67
92
|
|
|
68
93
|
```bash
|
|
69
94
|
npm install
|
|
70
|
-
npm run aci:compile
|
|
71
95
|
npm run dev
|
|
72
96
|
```
|
|
73
97
|
|
|
98
|
+
The dev server compiles content on startup — no manual compile needed.
|
|
99
|
+
|
|
74
100
|
The dev server watches for content changes and re-compiles automatically
|
|
75
101
|
via the framework's ACI adapter. Edit `.content/` files and see changes
|
|
76
|
-
in the browser.
|
|
102
|
+
in the browser. No manual compile step needed before `dev`.
|
|
77
103
|
|
|
78
104
|
### Full local build
|
|
79
105
|
|
|
80
106
|
```bash
|
|
81
|
-
npm run aci:compile
|
|
82
107
|
npm run content:compile
|
|
83
108
|
npm run typecheck
|
|
84
|
-
npm run build
|
|
85
109
|
```
|
|
86
110
|
|
|
87
111
|
### Content editing loop
|
|
88
112
|
|
|
89
113
|
1. Edit `.content/pages/{slug}/_index.json` or `.content/config/site.json`.
|
|
90
|
-
2. Run `npm run
|
|
91
|
-
3. Run `npm run
|
|
92
|
-
4.
|
|
93
|
-
5. Check the output for your changes.
|
|
114
|
+
2. Run `npm run content:compile` to compile content.
|
|
115
|
+
3. Run `npm run build` to build the site.
|
|
116
|
+
4. Check the output for your changes.
|
|
94
117
|
|
|
95
118
|
## Test Order (Fastest to Slowest)
|
|
96
119
|
|
|
97
|
-
1. `npm run
|
|
98
|
-
2. `npm
|
|
99
|
-
3. `npm run
|
|
100
|
-
4. `npm run
|
|
101
|
-
5. `npm run build` —
|
|
102
|
-
|
|
120
|
+
1. `npm run typecheck` — TypeScript
|
|
121
|
+
2. `npm test` — conformance tests
|
|
122
|
+
3. `npm run content:compile` — content compile
|
|
123
|
+
4. `npm run build` — framework build
|
|
124
|
+
5. `npm run build:storybook` — Storybook (if configured)
|
|
125
|
+
|
|
126
|
+
## Conformance Checks
|
|
127
|
+
|
|
128
|
+
Repos run conformance tests in CI using the `@gradial/aci/testing` preset:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { conformancePreset } from '@gradial/aci/testing';
|
|
132
|
+
|
|
133
|
+
conformancePreset({
|
|
134
|
+
framework: 'next',
|
|
135
|
+
siteId: 'your-tenant-id',
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Default checks validate:
|
|
140
|
+
- `config` -- `withAci()` present in config
|
|
141
|
+
- `registry` -- `createRegistry()` usage (no hand-rolled maps)
|
|
142
|
+
- `pageEntry` -- `createPage(registry)` usage in catch-all route
|
|
143
|
+
- `middleware` -- `createGradialMiddleware({ siteId })` with correct siteId
|
|
144
|
+
- `noFileContentProvider` -- no direct `FileContentProvider` imports in app code
|
|
145
|
+
- `noCustomContent` -- no `src/lib/content.ts` shim
|
|
146
|
+
- `noLegacyAssetRoute` -- no `[releaseId]` segment in asset route path
|
|
147
|
+
- `sdkVersion` -- SDK version compatibility
|
|
148
|
+
|
|
149
|
+
Optional (disabled by default):
|
|
150
|
+
- `buildSucceeds` -- full build completes
|
|
151
|
+
- `contentCompile` -- content compilation succeeds
|
|
103
152
|
|
|
104
153
|
## Troubleshooting
|
|
105
154
|
|
|
@@ -12,6 +12,7 @@ description: Author content and build components for ACI sites. Use when creatin
|
|
|
12
12
|
| Path | Purpose |
|
|
13
13
|
|------|---------|
|
|
14
14
|
| `.content/config/site.json` | Site metadata, SEO defaults |
|
|
15
|
+
| `.content/config/robots.txt` | Authored crawler rules; publish bundles this file verbatim as `assets/robots.txt` when present |
|
|
15
16
|
| `.content/pages/{slug}/_index.json` | Page content (slug maps to route) |
|
|
16
17
|
| `.content/fragments/{id}/_index.json` | Shared fragments (navbar, footer — used by layout defaults) |
|
|
17
18
|
| `.aci/schemas/page.schema.json` | Page validation schema (auto-generated from contracts) |
|
|
@@ -28,7 +29,8 @@ Every page JSON requires:
|
|
|
28
29
|
"layout": "default",
|
|
29
30
|
"metadata": {
|
|
30
31
|
"title": "Page Title",
|
|
31
|
-
"description": "Meta description"
|
|
32
|
+
"description": "Meta description",
|
|
33
|
+
"sitemap": { "visible": true }
|
|
32
34
|
},
|
|
33
35
|
"regions": {
|
|
34
36
|
"main": []
|
|
@@ -36,6 +38,35 @@ Every page JSON requires:
|
|
|
36
38
|
}
|
|
37
39
|
```
|
|
38
40
|
|
|
41
|
+
Base page fields are SDK-owned and included in the generated
|
|
42
|
+
`.aci/schemas/page.schema.json`.
|
|
43
|
+
|
|
44
|
+
#### Sitemap visibility
|
|
45
|
+
|
|
46
|
+
`metadata.sitemap.visible` controls whether the page appears in the generated
|
|
47
|
+
`sitemap.xml` release artifact:
|
|
48
|
+
|
|
49
|
+
- **Omit or `true`** (default) — the page is included in the sitemap when other
|
|
50
|
+
filters allow it (published status, robots.txt rules).
|
|
51
|
+
- **`false`** — the page is excluded from the sitemap. The page still renders
|
|
52
|
+
normally; only its sitemap presence changes.
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"metadata": {
|
|
57
|
+
"title": "Private Campaign Page",
|
|
58
|
+
"sitemap": { "visible": false }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Use `false` for pages that should publish but stay out of search engine
|
|
64
|
+
discovery: temporary campaign pages, event-specific landing pages, staging
|
|
65
|
+
pages, or pages already blocked by `robots.txt` (setting it explicitly makes
|
|
66
|
+
the intent clear in content).
|
|
67
|
+
|
|
68
|
+
This does **not** add a `noindex` meta tag — it only controls sitemap inclusion.
|
|
69
|
+
|
|
39
70
|
The `layout` field is required — it determines which slots the page can
|
|
40
71
|
fill. The `default` layout provides `header`/`main`/`footer` slots with
|
|
41
72
|
fragment defaults for chrome (navbar, footer). Pages typically only fill
|
|
@@ -400,8 +431,7 @@ framework.
|
|
|
400
431
|
#### 6. Verify
|
|
401
432
|
|
|
402
433
|
```bash
|
|
403
|
-
npm run aci:compile #
|
|
404
|
-
npm run aci:validate # Content validates
|
|
434
|
+
npm run aci:compile # Contracts + content compile
|
|
405
435
|
npm run typecheck # Types check
|
|
406
436
|
npm run build # Framework builds
|
|
407
437
|
```
|
|
@@ -446,24 +476,26 @@ type Props = ContentProps & { className?: string };
|
|
|
446
476
|
### Layout Contracts
|
|
447
477
|
|
|
448
478
|
Layouts define page-level slot structure and optional fragment defaults
|
|
449
|
-
for chrome.
|
|
450
|
-
|
|
479
|
+
for chrome. Use `defineLayoutContract`, `slot()`, and `fragmentDefault()`
|
|
480
|
+
from the SDK:
|
|
451
481
|
|
|
452
482
|
```ts
|
|
453
483
|
// src/cms/contracts/layouts/index.ts
|
|
484
|
+
import { defineLayoutContract, slot, fragmentDefault } from '@gradial/aci';
|
|
485
|
+
|
|
454
486
|
export default [
|
|
455
|
-
{
|
|
487
|
+
defineLayoutContract({
|
|
456
488
|
name: 'default',
|
|
457
489
|
slots: [
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
490
|
+
slot('header'), // filled by fragment default
|
|
491
|
+
slot('main', true), // filled by page regions (required)
|
|
492
|
+
slot('footer'), // filled by fragment default
|
|
461
493
|
],
|
|
462
494
|
defaults: {
|
|
463
|
-
header:
|
|
464
|
-
footer:
|
|
495
|
+
header: fragmentDefault('navbar'),
|
|
496
|
+
footer: fragmentDefault('site-footer'),
|
|
465
497
|
},
|
|
466
|
-
},
|
|
498
|
+
}),
|
|
467
499
|
];
|
|
468
500
|
```
|
|
469
501
|
|
|
@@ -553,14 +585,21 @@ Three theme classes control semantic token resolution:
|
|
|
553
585
|
|
|
554
586
|
## Storybook
|
|
555
587
|
|
|
556
|
-
Every ACI frontend repo should have Storybook configured.
|
|
557
|
-
|
|
588
|
+
Every ACI frontend repo should have Storybook configured. Use **Storybook 10+**
|
|
589
|
+
with the framework-specific adapter:
|
|
590
|
+
|
|
591
|
+
- **Next.js**: `@storybook/nextjs-vite`
|
|
592
|
+
- **Astro**: `@storybook-astro/framework`
|
|
593
|
+
|
|
594
|
+
Stories live under `src/stories/` and follow the framework's Storybook setup.
|
|
558
595
|
|
|
559
596
|
```bash
|
|
560
597
|
npm run storybook # Dev mode
|
|
561
598
|
npm run build:storybook # Build all stories
|
|
562
599
|
```
|
|
563
600
|
|
|
601
|
+
All repos should converge on Storybook 10. Repos on Storybook 9 should upgrade.
|
|
602
|
+
|
|
564
603
|
## Deep Dive: Component Registry
|
|
565
604
|
|
|
566
605
|
Read `references/block-registry.md` for the `createRegistry` pattern,
|
|
@@ -45,18 +45,31 @@ The SDK provides `createPage(registry)` which owns param normalization, content
|
|
|
45
45
|
loading, layout resolution, metadata generation, notFound handling, slot
|
|
46
46
|
resolution (including fragment defaults), and block rendering.
|
|
47
47
|
|
|
48
|
+
Path shape: `/__r/{releaseId}/routes/{...slug}`. The SDK extracts releaseId
|
|
49
|
+
internally; middleware rewrites public URLs to `/__r/{releaseId}/routes/{path}`
|
|
50
|
+
for the app-level route. The `routes` namespace leaves room for future siblings
|
|
51
|
+
under the release prefix. The filesystem uses an **optional catch-all** `[[...slug]]`
|
|
52
|
+
(not required `[...slug]`). Production middleware always rewrites to
|
|
53
|
+
`/__r/{releaseId}/routes/{path}`, but file-provider local dev passes requests
|
|
54
|
+
unrewritten so `/` arrives with an empty slug.
|
|
55
|
+
|
|
48
56
|
Chrome (Header/Footer) is **not** in `layout.tsx`. It is CMS-managed via
|
|
49
|
-
layout fragment defaults. The `layout.tsx` file is minimal
|
|
57
|
+
layout fragment defaults. The `layout.tsx` file is minimal and includes
|
|
58
|
+
`<DevRefresh />`:
|
|
50
59
|
|
|
51
60
|
```tsx
|
|
52
61
|
// src/app/layout.tsx — minimal, no chrome
|
|
53
62
|
import type { ReactNode } from 'react';
|
|
63
|
+
import { DevRefresh } from '@gradial/aci/next';
|
|
54
64
|
import '@/design-system/styles.css';
|
|
55
65
|
|
|
56
66
|
export default function Layout({ children }: { children: ReactNode }) {
|
|
57
67
|
return (
|
|
58
68
|
<html lang="en">
|
|
59
|
-
<body>
|
|
69
|
+
<body>
|
|
70
|
+
{children}
|
|
71
|
+
<DevRefresh />
|
|
72
|
+
</body>
|
|
60
73
|
</html>
|
|
61
74
|
);
|
|
62
75
|
}
|
|
@@ -95,23 +108,83 @@ an app-owned HTTP endpoint backed by a server-side `ContentProvider`.
|
|
|
95
108
|
|
|
96
109
|
## Astro Pattern
|
|
97
110
|
|
|
98
|
-
|
|
111
|
+
The SDK provides `getPageData(Astro)` as the Astro equivalent of Next.js
|
|
112
|
+
`createPage`. It encapsulates route normalization, content resolution (preview
|
|
113
|
+
render input vs FileContentProvider), metadata derivation, layout resolution,
|
|
114
|
+
slot filling, and collect-tag emission into a single call.
|
|
99
115
|
|
|
100
116
|
```astro
|
|
101
117
|
---
|
|
102
118
|
// src/pages/[...slug].astro
|
|
103
|
-
import {
|
|
104
|
-
import { registry } from '
|
|
119
|
+
import { getGradialStaticPaths, getPageData, createRenderChildren } from '@gradial/aci/astro';
|
|
120
|
+
import { registry } from '../cms/registry';
|
|
121
|
+
import SiteLayout from '../layouts/SiteLayout.astro';
|
|
105
122
|
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
123
|
+
export const getStaticPaths = getGradialStaticPaths;
|
|
124
|
+
|
|
125
|
+
const data = await getPageData(Astro);
|
|
126
|
+
const renderChildren = createRenderChildren(registry);
|
|
109
127
|
---
|
|
128
|
+
|
|
129
|
+
<SiteLayout
|
|
130
|
+
title={data.meta.title}
|
|
131
|
+
description={data.meta.description}
|
|
132
|
+
canonical={data.meta.canonical}
|
|
133
|
+
siteName={data.meta.siteName}
|
|
134
|
+
collectTag={data.collectTag}
|
|
135
|
+
>
|
|
136
|
+
{data.layout.slots.map((slot) => {
|
|
137
|
+
const blocks = data.slots[slot.name];
|
|
138
|
+
if (!blocks?.length) return null;
|
|
139
|
+
const rendered = renderChildren(blocks);
|
|
140
|
+
if (slot.name === 'main') {
|
|
141
|
+
return (
|
|
142
|
+
<main id="content-main">
|
|
143
|
+
{rendered.map((child) => {
|
|
144
|
+
const Comp = child.Component;
|
|
145
|
+
return Comp ? <Comp {...child.props} /> : null;
|
|
146
|
+
})}
|
|
147
|
+
</main>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return rendered.map((child) => {
|
|
151
|
+
const Comp = child.Component;
|
|
152
|
+
return Comp ? <Comp {...child.props} /> : null;
|
|
153
|
+
});
|
|
154
|
+
})}
|
|
155
|
+
</SiteLayout>
|
|
110
156
|
```
|
|
111
157
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
158
|
+
`getPageData()` returns a `PageData` object with `{ route, siteConfig, page,
|
|
159
|
+
meta, layout, slots, collectTag }`. The page template receives typed data
|
|
160
|
+
and only needs to iterate slots and render blocks via the registry.
|
|
161
|
+
|
|
162
|
+
For DAM asset URLs, use `damAssetUrl()`:
|
|
163
|
+
|
|
164
|
+
```astro
|
|
165
|
+
---
|
|
166
|
+
import { damAssetUrl } from '@gradial/aci/astro';
|
|
167
|
+
---
|
|
168
|
+
<img src={damAssetUrl('images/hero.webp')} alt="Hero" />
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Astro Asset Serving
|
|
172
|
+
|
|
173
|
+
**DAM assets are content-addressed and globally shared.**
|
|
174
|
+
|
|
175
|
+
DAM derivative assets (processed images, optimized media) are staged ONCE at the
|
|
176
|
+
hosting bucket's global `.gradial-dam/` location. The content-addressed filenames
|
|
177
|
+
contain hashes, so each unique asset is written only once and shared across all
|
|
178
|
+
releases.
|
|
179
|
+
|
|
180
|
+
The CloudFront function rewrites `/.gradial-dam/*` requests to the global
|
|
181
|
+
`{keyPrefix}/.gradial-dam/*` S3 path with no release lookup. These assets are
|
|
182
|
+
served with immutable caching (`max-age=31536000`) because the filenames contain
|
|
183
|
+
content hashes.
|
|
184
|
+
|
|
185
|
+
No separate CloudFront origin behavior is needed — the existing CloudFront
|
|
186
|
+
function in `infra/modules/hosting-cloudfront-site/function.js.tftpl` handles
|
|
187
|
+
the rewrite automatically.
|
|
115
188
|
|
|
116
189
|
### Astro-Specific Paths
|
|
117
190
|
|
|
@@ -119,26 +192,47 @@ resolution, metadata generation, notFound handling, slot resolution
|
|
|
119
192
|
|---------|------|
|
|
120
193
|
| Routes | `src/pages/[...slug].astro` |
|
|
121
194
|
| Layout | `src/layouts/SiteLayout.astro` |
|
|
122
|
-
| Config | `astro.config.mjs` with `withAci()` |
|
|
195
|
+
| Config | `astro.config.mjs` with bare `withAci()` |
|
|
123
196
|
| Registry | `src/cms/registry.ts` (shared) |
|
|
124
197
|
| Storybook | `.storybook/main.ts`, `.storybook/preview.ts` |
|
|
198
|
+
| Assets | CloudFront function rewrite (automatic, no infrastructure changes needed) |
|
|
125
199
|
|
|
126
200
|
## Middleware
|
|
127
201
|
|
|
202
|
+
Middleware requires explicit `siteId`. The SDK exports a canonical matcher that
|
|
203
|
+
repos re-export:
|
|
204
|
+
|
|
128
205
|
```ts
|
|
129
206
|
// src/middleware.ts
|
|
130
|
-
import {
|
|
131
|
-
export default createMiddleware();
|
|
207
|
+
import { createGradialMiddleware } from '@gradial/aci/next/middleware';
|
|
132
208
|
export { config } from '@gradial/aci/next/middleware';
|
|
209
|
+
export default createGradialMiddleware({ siteId: 'your-tenant-id' });
|
|
133
210
|
```
|
|
134
211
|
|
|
212
|
+
The middleware rewrites public URLs to `/__r/{releaseId}/routes/{path}` for
|
|
213
|
+
the app-level route. The SDK extracts releaseId internally.
|
|
214
|
+
|
|
135
215
|
## Asset Routes
|
|
136
216
|
|
|
217
|
+
**Preferred: NO app-level asset route.** Assets are served via URL rewrite
|
|
218
|
+
(Next.js `withAci()`) or CloudFront function rewrite (Astro/CloudFront) directly
|
|
219
|
+
to S3.
|
|
220
|
+
|
|
221
|
+
DAM derivative assets are content-addressed and globally shared — staged once at
|
|
222
|
+
`{keyPrefix}/.gradial-dam/` and shared across all releases. The SDK's `withAci()`
|
|
223
|
+
already injects a `/.gradial-dam/:path*` rewrite. For CloudFront, the existing
|
|
224
|
+
CloudFront function handles the rewrite automatically.
|
|
225
|
+
|
|
226
|
+
For transitional compatibility, the SDK still exports an asset route handler:
|
|
227
|
+
|
|
137
228
|
```ts
|
|
138
|
-
// src/app/gradial/assets/[
|
|
229
|
+
// src/app/gradial/assets/[...path]/route.ts (transitional)
|
|
139
230
|
export { GET, runtime, dynamic } from '@gradial/aci/next/asset-route';
|
|
140
231
|
```
|
|
141
232
|
|
|
233
|
+
**Note**: Asset URLs do NOT include `[releaseId]` in the path. DAM assets are
|
|
234
|
+
globally scoped and content-addressed via filename hashes.
|
|
235
|
+
|
|
142
236
|
## Anti-Patterns
|
|
143
237
|
|
|
144
238
|
- `Record<string, any>` or `Record<string, ComponentType<any>>` — hides type errors
|
|
@@ -61,15 +61,25 @@ export default [heroContract, buttonContract];
|
|
|
61
61
|
|
|
62
62
|
## Layout Contract API
|
|
63
63
|
|
|
64
|
+
Use `defineLayoutContract`, `slot()`, and `fragmentDefault()` from the SDK:
|
|
65
|
+
|
|
64
66
|
```ts
|
|
67
|
+
// src/cms/contracts/layouts/index.ts
|
|
68
|
+
import { defineLayoutContract, slot, fragmentDefault } from '@gradial/aci';
|
|
69
|
+
|
|
65
70
|
export default [
|
|
66
|
-
{
|
|
71
|
+
defineLayoutContract({
|
|
67
72
|
name: 'marketing',
|
|
68
73
|
slots: [
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
slot('header'),
|
|
75
|
+
slot('main', true),
|
|
76
|
+
slot('footer'),
|
|
71
77
|
],
|
|
72
|
-
|
|
78
|
+
defaults: {
|
|
79
|
+
header: fragmentDefault('site-header'),
|
|
80
|
+
footer: fragmentDefault('site-footer'),
|
|
81
|
+
},
|
|
82
|
+
}),
|
|
73
83
|
];
|
|
74
84
|
```
|
|
75
85
|
|
|
@@ -79,12 +89,14 @@ Content pages reference layouts by name:
|
|
|
79
89
|
{
|
|
80
90
|
"layout": "marketing",
|
|
81
91
|
"regions": {
|
|
82
|
-
"main": [...]
|
|
83
|
-
"footer": [...]
|
|
92
|
+
"main": [...]
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
```
|
|
87
96
|
|
|
97
|
+
The `header` and `footer` slots are filled automatically by fragment defaults
|
|
98
|
+
when the page doesn't provide blocks for them.
|
|
99
|
+
|
|
88
100
|
## Multiple Runtime Mappings
|
|
89
101
|
|
|
90
102
|
One runtime component may implement multiple contract names. Define
|
|
@@ -117,7 +129,7 @@ capabilities:
|
|
|
117
129
|
fragmentRender: true
|
|
118
130
|
|
|
119
131
|
routes:
|
|
120
|
-
cmsManaged: "/[...slug]"
|
|
132
|
+
cmsManaged: "/[[...slug]]"
|
|
121
133
|
frameworkOwned:
|
|
122
134
|
- "/api/*"
|
|
123
135
|
- "/_next/*" # Next.js
|
|
@@ -151,3 +163,7 @@ rendererProtocol: stdio-json
|
|
|
151
163
|
|
|
152
164
|
- `componentRegistry` and `layoutRegistry` point to contract-only modules.
|
|
153
165
|
- No secrets in `.aci.yaml`, docs, fixtures, or generated files.
|
|
166
|
+
- The `cmsManaged` route pattern should match the filesystem: use `/[[...slug]]`
|
|
167
|
+
(optional catch-all) for Next.js repos. Production middleware always rewrites
|
|
168
|
+
to `/__r/{releaseId}/routes/{path}` so slug is non-empty, but file-provider
|
|
169
|
+
local dev passes requests unrewritten so `/` arrives with an empty slug.
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
# Repository Structure Reference
|
|
2
2
|
|
|
3
|
+
## Generation Modes
|
|
4
|
+
|
|
5
|
+
The `aci init` command generates sites in two modes:
|
|
6
|
+
|
|
7
|
+
| Mode | Command | What you get |
|
|
8
|
+
|------|---------|-------------|
|
|
9
|
+
| **Scaffold** (default) | `aci init my-site --template next-starter` | Golden-path skeleton: config, framework wiring, page route, empty registry, design system, agent skills. No demo content or components. This is the starting point for a new customer. |
|
|
10
|
+
| **With starter content** | `aci init my-site --template next-starter --with-starter-content` | Scaffold + demo content (`.content/`), demo components (sections, elements, chrome), full registry, stories, and fixtures. Use for demos or learning. |
|
|
11
|
+
|
|
12
|
+
Available templates: `next-starter`, `astro-starter`.
|
|
13
|
+
|
|
3
14
|
## Directory Layout
|
|
4
15
|
|
|
16
|
+
The scaffold generates the structure below. Items marked **(starter content only)** are
|
|
17
|
+
only present when `--with-starter-content` is used.
|
|
18
|
+
|
|
5
19
|
```
|
|
6
20
|
.aci.yaml ACI config: contract paths, framework, routes
|
|
7
21
|
package.json Dependencies and scripts
|
|
@@ -11,7 +25,7 @@ tsconfig.json TypeScript config
|
|
|
11
25
|
site.json Site metadata, navigation, footer, branding, SEO
|
|
12
26
|
pages/
|
|
13
27
|
home/_index.json Homepage (route /)
|
|
14
|
-
about/_index.json About page (route /about)
|
|
28
|
+
about/_index.json About page (route /about) (starter content only)
|
|
15
29
|
fragments/
|
|
16
30
|
navbar/_index.json Navbar fragment (layout default)
|
|
17
31
|
site-footer/_index.json Footer fragment (layout default)
|
|
@@ -19,14 +33,14 @@ src/
|
|
|
19
33
|
cms/
|
|
20
34
|
contracts/
|
|
21
35
|
components/ Component contracts (framework-agnostic)
|
|
22
|
-
homeHero.contract.ts Hero section contract
|
|
23
|
-
button.contract.ts Button block contract
|
|
24
|
-
container.contract.ts Container (nested blocks) contract
|
|
25
|
-
featureGrid.contract.ts Feature grid contract
|
|
26
|
-
featureCard.contract.ts Feature card contract
|
|
27
|
-
statsBar.contract.ts Stats bar contract
|
|
28
|
-
siteNavigation.contract.ts Site navigation contract
|
|
29
|
-
siteFooter.contract.ts Site footer contract
|
|
36
|
+
homeHero.contract.ts Hero section contract (starter content only)
|
|
37
|
+
button.contract.ts Button block contract (starter content only)
|
|
38
|
+
container.contract.ts Container (nested blocks) contract (starter content only)
|
|
39
|
+
featureGrid.contract.ts Feature grid contract (starter content only)
|
|
40
|
+
featureCard.contract.ts Feature card contract (starter content only)
|
|
41
|
+
statsBar.contract.ts Stats bar contract (starter content only)
|
|
42
|
+
siteNavigation.contract.ts Site navigation contract (starter content only)
|
|
43
|
+
siteFooter.contract.ts Site footer contract (starter content only)
|
|
30
44
|
index.ts Component contract barrel
|
|
31
45
|
layouts/
|
|
32
46
|
index.ts Layout contract barrel (plain objects)
|
|
@@ -55,7 +69,7 @@ src/
|
|
|
55
69
|
index.css Barrel import for all token files
|
|
56
70
|
app/ Framework routes (Next.js App Router)
|
|
57
71
|
layout.tsx Root layout (minimal, imports CSS only)
|
|
58
|
-
[[...slug]]/page.tsx
|
|
72
|
+
[[...slug]]/page.tsx Optional catch-all for all CMS pages
|
|
59
73
|
pages/ Framework routes (Astro file-based routing)
|
|
60
74
|
[...slug].astro Catch-all for all CMS pages
|
|
61
75
|
layouts/ Page layout wrappers (framework-specific)
|
|
@@ -87,11 +101,11 @@ Only Next.js and Astro are supported. SvelteKit support has been removed.
|
|
|
87
101
|
| Concern | Path |
|
|
88
102
|
|---------|------|
|
|
89
103
|
| Routes | `src/app/[[...slug]]/page.tsx` (optional catch-all, `force-dynamic`) |
|
|
90
|
-
| Root layout | `src/app/layout.tsx` (minimal — imports CSS, no chrome) |
|
|
91
|
-
| Config | `next.config.ts` with `withAci()` |
|
|
104
|
+
| Root layout | `src/app/layout.tsx` (minimal — imports CSS, includes `<DevRefresh />`, no chrome) |
|
|
105
|
+
| Config | `next.config.ts` with bare `withAci()` |
|
|
92
106
|
| Registry | `src/cms/registry.ts` (shared, `createRegistry` with `[contract, component]` tuples) |
|
|
93
|
-
| Middleware | `src/middleware.ts` |
|
|
94
|
-
| Asset
|
|
107
|
+
| Middleware | `src/middleware.ts` with `createGradialMiddleware({ siteId })` and SDK-exported matcher |
|
|
108
|
+
| Asset route | `src/app/gradial/assets/[...path]/route.ts` — thin SDK re-export from `@gradial/aci/next/asset-route`. Serves CMS documents (robots.txt, sitemap.xml) via withAci rewrites and local-dev DAM assets. Hand-rolled asset routes and the legacy release-scoped `[releaseId]/[...path]` variant are banned. Astro has no route (CDN/global path). |
|
|
95
109
|
|
|
96
110
|
### Astro
|
|
97
111
|
|
|
@@ -145,13 +159,15 @@ Standard scripts across all frameworks:
|
|
|
145
159
|
"dev": "...",
|
|
146
160
|
"build": "...",
|
|
147
161
|
"typecheck": "...",
|
|
148
|
-
"
|
|
149
|
-
"content:compile": "aci
|
|
150
|
-
"aci:build": "aci build --content ./.content",
|
|
151
|
-
"aci:doctor": "aci doctor",
|
|
152
|
-
"aci:validate": "aci build --validate-only"
|
|
162
|
+
"test": "node --import tsx --test tests/**/*.test.ts",
|
|
163
|
+
"content:compile": "aci content compile --content .content --out .aci/compiled"
|
|
153
164
|
}
|
|
154
165
|
}
|
|
155
166
|
```
|
|
156
167
|
|
|
168
|
+
The `dev` command auto-compiles content; no manual compile step is needed
|
|
169
|
+
before starting the dev server.
|
|
170
|
+
|
|
157
171
|
Customer repos use `npm` as the package manager. The monorepo uses `pnpm`.
|
|
172
|
+
|
|
173
|
+
**Note**: `aci doctor` has been removed. Use the `@gradial/aci/testing` conformance preset in CI instead.
|