@gradial/aci 0.1.53-rc.1 → 0.1.53-rc.2
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/CHANGELOG.md +16 -0
- package/README.md +4 -4
- package/dist/cli/compile-registry.mjs +8 -0
- package/dist/content/provider.d.ts +7 -1
- package/dist/content/provider.js +27 -0
- package/dist/content/types.d.ts +11 -0
- package/dist/next/page.js +1 -0
- package/migrations/page-metadata-head.md +38 -0
- package/package.json +10 -7
- package/src/astro/Head.astro +18 -0
- package/src/cli/compile-registry.mjs +8 -0
- package/templates/astro/template/src/layouts/SiteLayout.astro +5 -11
- package/templates/astro/template/src/pages/404.astro +1 -4
- package/templates/astro/template/src/pages/500.astro +1 -4
- package/templates/astro/template/src/pages/[...slug].astro +1 -4
- package/templates/common/.agents/skills/authoring/SKILL.md +34 -4
- package/templates/common/.agents/skills/authoring/references/block-registry.md +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Page `metadata.robots` accepts boolean `index` and `follow` directives.
|
|
8
|
+
Next and Astro emit the corresponding HTML tag. Publish excludes pages with
|
|
9
|
+
`index: false` from the release sitemap.
|
|
10
|
+
|
|
11
|
+
### Migration required
|
|
12
|
+
|
|
13
|
+
- Existing Astro sites must install `@gradial/aci/astro/Head` and pass resolved
|
|
14
|
+
metadata as one object through their route and layout. Next sites must upgrade
|
|
15
|
+
the SDK and rebuild. See [page metadata migration](./migrations/page-metadata-head.md).
|
|
16
|
+
Packed starter HTML checks in `make test-frontend-contract` verify the tags.
|
|
17
|
+
Sitemap exclusion can precede an SDK upgrade. Inspect the rendered preview
|
|
18
|
+
before telling an author that `noindex` is present. No platform test currently
|
|
19
|
+
enforces that check on older site builds.
|
|
20
|
+
|
|
5
21
|
### Changed
|
|
6
22
|
|
|
7
23
|
- Immutable code builds (`ACI_CODE_ARTIFACT_BUILD=1`) no longer read or compile
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agentic Content Infrastructure SDK
|
|
2
2
|
|
|
3
|
-
`@gradial/aci` `0.1.53-rc.
|
|
3
|
+
`@gradial/aci` `0.1.53-rc.2` is a pre-1.0 public-surface reset for the ACI
|
|
4
4
|
client SDK. Breaking changes from the old `0.1.x` accidental surface are
|
|
5
5
|
intentional: the root import is framework-neutral, framework runtime behavior is
|
|
6
6
|
behind explicit subpaths, and the first required customer path is React/Next.
|
|
@@ -8,7 +8,7 @@ behind explicit subpaths, and the first required customer path is React/Next.
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
npm install @gradial/aci@0.1.53-rc.
|
|
11
|
+
npm install @gradial/aci@0.1.53-rc.2 zod@^4
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Root Import
|
|
@@ -137,13 +137,13 @@ host bundles select and serve immutable release artifacts.
|
|
|
137
137
|
The first RC scaffold path is Next:
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
|
-
npx @gradial/aci@0.1.53-rc.
|
|
140
|
+
npx @gradial/aci@0.1.53-rc.2 init my-site --template next-starter
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
List available templates:
|
|
144
144
|
|
|
145
145
|
```bash
|
|
146
|
-
npx @gradial/aci@0.1.53-rc.
|
|
146
|
+
npx @gradial/aci@0.1.53-rc.2 init --list
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
## Personalization: The One-Tag Install
|
|
@@ -503,6 +503,14 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
503
503
|
title: { type: 'string', minLength: 1 },
|
|
504
504
|
description: { type: 'string' },
|
|
505
505
|
canonical: { type: 'string' },
|
|
506
|
+
robots: {
|
|
507
|
+
type: 'object',
|
|
508
|
+
additionalProperties: false,
|
|
509
|
+
properties: {
|
|
510
|
+
index: { type: 'boolean' },
|
|
511
|
+
follow: { type: 'boolean' },
|
|
512
|
+
},
|
|
513
|
+
},
|
|
506
514
|
},
|
|
507
515
|
},
|
|
508
516
|
regions: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { KernelPage, KernelRouteMetadata, KernelSiteConfig, RenderInput } from './types.js';
|
|
1
|
+
import type { KernelPage, KernelPageRobots, KernelRouteMetadata, KernelSiteConfig, RenderInput } from './types.js';
|
|
2
2
|
export interface RouteEntry {
|
|
3
3
|
slug: string;
|
|
4
4
|
path: string;
|
|
@@ -78,3 +78,9 @@ export interface RenderInputOptions {
|
|
|
78
78
|
}
|
|
79
79
|
export declare function loadRenderInput(provider: ContentProvider, route: string | undefined, options: RenderInputOptions): Promise<RenderInput>;
|
|
80
80
|
export declare function routeMetadataForContent(siteConfig: KernelSiteConfig, page: KernelPage | null): KernelRouteMetadata;
|
|
81
|
+
/**
|
|
82
|
+
* Serializes page robots directives to a `<meta name="robots">` content
|
|
83
|
+
* value, matching what Next.js emits for the same object. Returns '' when
|
|
84
|
+
* no directive is set, so templates can skip the tag.
|
|
85
|
+
*/
|
|
86
|
+
export declare function robotsMetaContent(robots: KernelPageRobots | undefined): string;
|
package/dist/content/provider.js
CHANGED
|
@@ -53,6 +53,33 @@ export function routeMetadataForContent(siteConfig, page) {
|
|
|
53
53
|
title,
|
|
54
54
|
description,
|
|
55
55
|
canonical: page?.metadata?.canonical,
|
|
56
|
+
robots: pageRobots(page?.metadata?.robots),
|
|
56
57
|
siteName: siteConfig.seo?.siteName || siteConfig.title
|
|
57
58
|
};
|
|
58
59
|
}
|
|
60
|
+
function pageRobots(value) {
|
|
61
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
62
|
+
return undefined;
|
|
63
|
+
const { index, follow } = value;
|
|
64
|
+
const robots = {};
|
|
65
|
+
if (typeof index === 'boolean')
|
|
66
|
+
robots.index = index;
|
|
67
|
+
if (typeof follow === 'boolean')
|
|
68
|
+
robots.follow = follow;
|
|
69
|
+
return robots.index === undefined && robots.follow === undefined ? undefined : robots;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Serializes page robots directives to a `<meta name="robots">` content
|
|
73
|
+
* value, matching what Next.js emits for the same object. Returns '' when
|
|
74
|
+
* no directive is set, so templates can skip the tag.
|
|
75
|
+
*/
|
|
76
|
+
export function robotsMetaContent(robots) {
|
|
77
|
+
if (!robots)
|
|
78
|
+
return '';
|
|
79
|
+
const directives = [];
|
|
80
|
+
if (robots.index !== undefined)
|
|
81
|
+
directives.push(robots.index ? 'index' : 'noindex');
|
|
82
|
+
if (robots.follow !== undefined)
|
|
83
|
+
directives.push(robots.follow ? 'follow' : 'nofollow');
|
|
84
|
+
return directives.join(', ');
|
|
85
|
+
}
|
package/dist/content/types.d.ts
CHANGED
|
@@ -37,10 +37,20 @@ export interface SiteFooter {
|
|
|
37
37
|
}
|
|
38
38
|
export type KernelFooterGroup = FooterGroup;
|
|
39
39
|
export type KernelFooterColumn = FooterColumn;
|
|
40
|
+
/**
|
|
41
|
+
* Page-level search engine directives. `index: false` renders `noindex` and
|
|
42
|
+
* drops the page from the release sitemap; `follow: false` renders
|
|
43
|
+
* `nofollow`. Absent keys emit nothing (crawler default: index, follow).
|
|
44
|
+
*/
|
|
45
|
+
export interface KernelPageRobots {
|
|
46
|
+
index?: boolean;
|
|
47
|
+
follow?: boolean;
|
|
48
|
+
}
|
|
40
49
|
export interface KernelPageSeo {
|
|
41
50
|
title?: string;
|
|
42
51
|
description?: string;
|
|
43
52
|
canonical?: string;
|
|
53
|
+
robots?: KernelPageRobots;
|
|
44
54
|
}
|
|
45
55
|
export interface KernelPageBlock {
|
|
46
56
|
$type: 'component';
|
|
@@ -108,6 +118,7 @@ export interface KernelRouteMetadata {
|
|
|
108
118
|
title: string;
|
|
109
119
|
description: string;
|
|
110
120
|
canonical?: string;
|
|
121
|
+
robots?: KernelPageRobots;
|
|
111
122
|
siteName: string;
|
|
112
123
|
}
|
|
113
124
|
export interface RenderInput<TPage extends KernelPage = KernelPage, TSiteConfig extends KernelSiteConfig = KernelSiteConfig> {
|
package/dist/next/page.js
CHANGED
|
@@ -57,6 +57,7 @@ export function createPage(registry, options) {
|
|
|
57
57
|
title: meta.title,
|
|
58
58
|
description: meta.description,
|
|
59
59
|
alternates: meta.canonical ? { canonical: meta.canonical } : undefined,
|
|
60
|
+
robots: meta.robots,
|
|
60
61
|
openGraph: {
|
|
61
62
|
title: meta.title,
|
|
62
63
|
description: meta.description,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Page metadata and crawler directives
|
|
2
|
+
|
|
3
|
+
Upgrade and rebuild each site before using `metadata.robots`. Backend sitemap
|
|
4
|
+
exclusion does not prove that a site emits the HTML directive.
|
|
5
|
+
|
|
6
|
+
## Astro integration
|
|
7
|
+
|
|
8
|
+
Install the SDK-owned head component once. Future metadata fields then follow
|
|
9
|
+
SDK upgrades through the same object.
|
|
10
|
+
|
|
11
|
+
1. Import `Head` from `@gradial/aci/astro/Head` in the site layout.
|
|
12
|
+
2. Replace individual metadata props with `meta: KernelRouteMetadata` from `@gradial/aci/content`.
|
|
13
|
+
3. Render `<Head meta={meta} />` inside `<head>`.
|
|
14
|
+
4. Remove the layout's title, description, canonical, robots, and Open Graph tags that `Head` now owns.
|
|
15
|
+
5. Pass `meta={data.meta}` from the catch-all route.
|
|
16
|
+
6. Pass `meta={errorPage.meta}` from the 404 and 500 seats.
|
|
17
|
+
7. Keep site-owned charset, viewport, icons, scripts, and custom head tags.
|
|
18
|
+
|
|
19
|
+
Use `packages/sdk/templates/astro/template/src/` as the integration reference.
|
|
20
|
+
`getPageData` returns the complete resolved metadata object. No separate robots
|
|
21
|
+
prop or serialized robots string is needed.
|
|
22
|
+
|
|
23
|
+
## Verify the rendered output
|
|
24
|
+
|
|
25
|
+
Verify the directive on the site build that will serve the content. This is a
|
|
26
|
+
release check, not a claim that a search engine has removed a URL.
|
|
27
|
+
|
|
28
|
+
1. Author `"metadata": { "robots": { "index": false, "follow": false } }` on a test page.
|
|
29
|
+
2. Build the site and publish the test content.
|
|
30
|
+
3. Check that the rendered preview has one robots tag containing `noindex, nofollow`.
|
|
31
|
+
4. Check that the release sitemap excludes that page and retains an indexable control page.
|
|
32
|
+
5. Check that robots.txt permits fetching the test page so crawlers can read its tag.
|
|
33
|
+
6. Repeat against the active release after activation.
|
|
34
|
+
|
|
35
|
+
`make test-frontend-contract` checks rendered HTML from packed Next and Astro
|
|
36
|
+
starters. It does not certify a customer's older SDK or copied layout.
|
|
37
|
+
Authoring integrations must inspect rendered preview HTML before claiming the
|
|
38
|
+
directive is present. The companion Grady change must preserve this requirement.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradial/aci",
|
|
3
|
-
"version": "0.1.53-rc.
|
|
3
|
+
"version": "0.1.53-rc.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/gradial/bare-metal-go.git",
|
|
@@ -103,6 +103,9 @@
|
|
|
103
103
|
"./astro/ConsentBanner": {
|
|
104
104
|
"import": "./src/astro/ConsentBanner.astro"
|
|
105
105
|
},
|
|
106
|
+
"./astro/Head": {
|
|
107
|
+
"import": "./src/astro/Head.astro"
|
|
108
|
+
},
|
|
106
109
|
"./consent-banner": {
|
|
107
110
|
"types": "./dist/consent-banner/index.d.ts",
|
|
108
111
|
"import": "./dist/consent-banner/index.js"
|
|
@@ -160,11 +163,11 @@
|
|
|
160
163
|
"zod": "^4.0.0"
|
|
161
164
|
},
|
|
162
165
|
"optionalDependencies": {
|
|
163
|
-
"@gradial/aci-cli-darwin-arm64": "0.1.53-rc.
|
|
164
|
-
"@gradial/aci-cli-darwin-x64": "0.1.53-rc.
|
|
165
|
-
"@gradial/aci-cli-linux-arm64": "0.1.53-rc.
|
|
166
|
-
"@gradial/aci-cli-linux-x64": "0.1.53-rc.
|
|
167
|
-
"@gradial/aci-cli-win32-arm64": "0.1.53-rc.
|
|
168
|
-
"@gradial/aci-cli-win32-x64": "0.1.53-rc.
|
|
166
|
+
"@gradial/aci-cli-darwin-arm64": "0.1.53-rc.2",
|
|
167
|
+
"@gradial/aci-cli-darwin-x64": "0.1.53-rc.2",
|
|
168
|
+
"@gradial/aci-cli-linux-arm64": "0.1.53-rc.2",
|
|
169
|
+
"@gradial/aci-cli-linux-x64": "0.1.53-rc.2",
|
|
170
|
+
"@gradial/aci-cli-win32-arm64": "0.1.53-rc.2",
|
|
171
|
+
"@gradial/aci-cli-win32-x64": "0.1.53-rc.2"
|
|
169
172
|
}
|
|
170
173
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { robotsMetaContent, type KernelRouteMetadata } from '@gradial/aci/content';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
meta: KernelRouteMetadata;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { meta } = Astro.props;
|
|
9
|
+
const robots = robotsMetaContent(meta.robots);
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<title>{meta.title}</title>
|
|
13
|
+
<meta name="description" content={meta.description} />
|
|
14
|
+
{meta.canonical ? <link rel="canonical" href={meta.canonical} /> : null}
|
|
15
|
+
{robots ? <meta name="robots" content={robots} /> : null}
|
|
16
|
+
<meta property="og:title" content={meta.title} />
|
|
17
|
+
<meta property="og:description" content={meta.description} />
|
|
18
|
+
<meta property="og:site_name" content={meta.siteName} />
|
|
@@ -496,6 +496,14 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
496
496
|
title: { type: 'string', minLength: 1 },
|
|
497
497
|
description: { type: 'string' },
|
|
498
498
|
canonical: { type: 'string' },
|
|
499
|
+
robots: {
|
|
500
|
+
type: 'object',
|
|
501
|
+
additionalProperties: false,
|
|
502
|
+
properties: {
|
|
503
|
+
index: { type: 'boolean' },
|
|
504
|
+
follow: { type: 'boolean' },
|
|
505
|
+
},
|
|
506
|
+
},
|
|
499
507
|
},
|
|
500
508
|
},
|
|
501
509
|
regions: {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
import '../design-system/styles.css';
|
|
3
|
+
import Head from '@gradial/aci/astro/Head';
|
|
4
|
+
import type { KernelRouteMetadata } from '@gradial/aci/content';
|
|
3
5
|
|
|
4
6
|
interface Props {
|
|
5
|
-
|
|
6
|
-
description: string;
|
|
7
|
-
canonical?: string;
|
|
8
|
-
siteName?: string;
|
|
7
|
+
meta: KernelRouteMetadata;
|
|
9
8
|
/**
|
|
10
9
|
* Pre-rendered Gradial collect script tag (collectScriptTagHTML from
|
|
11
10
|
* '@gradial/aci/astro'). Empty string / undefined = personalization
|
|
@@ -17,7 +16,7 @@ interface Props {
|
|
|
17
16
|
assignmentsTag?: string;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
const {
|
|
19
|
+
const { meta, collectTag, assignmentsTag } = Astro.props;
|
|
21
20
|
---
|
|
22
21
|
|
|
23
22
|
<!doctype html>
|
|
@@ -25,13 +24,8 @@ const { title, description, canonical, siteName = 'Gradial', collectTag, assignm
|
|
|
25
24
|
<head>
|
|
26
25
|
<meta charset="utf-8" />
|
|
27
26
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
28
|
-
<title>{title}</title>
|
|
29
27
|
<link rel="icon" type="image/png" href="/assets/bare-metal-logo.png" />
|
|
30
|
-
<meta
|
|
31
|
-
{canonical ? <link rel="canonical" href={canonical} /> : null}
|
|
32
|
-
<meta property="og:title" content={title} />
|
|
33
|
-
<meta property="og:description" content={description} />
|
|
34
|
-
<meta property="og:site_name" content={siteName} />
|
|
28
|
+
<Head meta={meta} />
|
|
35
29
|
{collectTag ? <Fragment set:html={collectTag} /> : null}
|
|
36
30
|
{assignmentsTag ? <Fragment set:html={assignmentsTag} /> : null}
|
|
37
31
|
</head>
|
|
@@ -17,10 +17,7 @@ const renderChildren = createRenderChildren(registry);
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
19
|
<SiteLayout
|
|
20
|
-
|
|
21
|
-
description={errorPage.meta.description}
|
|
22
|
-
canonical={errorPage.meta.canonical}
|
|
23
|
-
siteName={errorPage.meta.siteName}
|
|
20
|
+
meta={errorPage.meta}
|
|
24
21
|
>
|
|
25
22
|
{data ? (
|
|
26
23
|
data.layout.slots.map((slot) => {
|
|
@@ -16,10 +16,7 @@ const renderChildren = createRenderChildren(registry);
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
<SiteLayout
|
|
19
|
-
|
|
20
|
-
description={errorPage.meta.description}
|
|
21
|
-
canonical={errorPage.meta.canonical}
|
|
22
|
-
siteName={errorPage.meta.siteName}
|
|
19
|
+
meta={errorPage.meta}
|
|
23
20
|
>
|
|
24
21
|
{data ? (
|
|
25
22
|
data.layout.slots.map((slot) => {
|
|
@@ -25,10 +25,7 @@ const renderChildren = createRenderChildren(registry);
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
27
|
<SiteLayout
|
|
28
|
-
|
|
29
|
-
description={data.meta.description}
|
|
30
|
-
canonical={data.meta.canonical}
|
|
31
|
-
siteName={data.meta.siteName}
|
|
28
|
+
meta={data.meta}
|
|
32
29
|
collectTag={data.collectTag}
|
|
33
30
|
assignmentsTag={data.assignmentsTag}
|
|
34
31
|
>
|
|
@@ -58,6 +58,36 @@ Base page fields are SDK-owned and included in the generated
|
|
|
58
58
|
governs an interior page (see Page Templates); pages under a route a
|
|
59
59
|
template covers omit it.
|
|
60
60
|
|
|
61
|
+
#### Search engine indexing
|
|
62
|
+
|
|
63
|
+
`metadata.robots` requests crawler indexing behavior. An upgraded site renders a
|
|
64
|
+
`<meta name="robots">` tag in the page head:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"metadata": {
|
|
69
|
+
"title": "Private Campaign Page",
|
|
70
|
+
"robots": { "index": false, "follow": false }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- **`index: false`** — renders `noindex` and excludes the page from `sitemap.xml`.
|
|
76
|
+
- **`follow: false`** — renders `nofollow`. Crawlers do not follow the page's
|
|
77
|
+
links.
|
|
78
|
+
- **Omit a key** — no directive for it. Crawlers default to index, follow.
|
|
79
|
+
|
|
80
|
+
Verify the rendered preview HTML before claiming the directive is present.
|
|
81
|
+
Next sites need an SDK upgrade and rebuild. Existing Astro sites also need
|
|
82
|
+
the SDK-owned `@gradial/aci/astro/Head` integration. Sitemap exclusion alone
|
|
83
|
+
does not prove that the page emits `noindex`. Search removal depends on a
|
|
84
|
+
later crawl and is not immediate.
|
|
85
|
+
|
|
86
|
+
Use `index: false` for temporary campaign pages, event landing pages, and
|
|
87
|
+
thank-you pages. Do not also block the path in `robots.txt`: a crawler that
|
|
88
|
+
cannot fetch the page never sees the `noindex` tag, and the URL can stay in
|
|
89
|
+
search results.
|
|
90
|
+
|
|
61
91
|
#### Sitemap visibility
|
|
62
92
|
|
|
63
93
|
`metadata.sitemap.visible` controls whether the page appears in the generated
|
|
@@ -77,10 +107,10 @@ template covers omit it.
|
|
|
77
107
|
}
|
|
78
108
|
```
|
|
79
109
|
|
|
80
|
-
Use `false` for pages that
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
110
|
+
Use `false` only for pages that may appear in search results but should not
|
|
111
|
+
be listed in the sitemap, or for pages already blocked by `robots.txt`. To keep
|
|
112
|
+
a page out of search results, use `metadata.robots` instead — it excludes the
|
|
113
|
+
page from the sitemap too.
|
|
84
114
|
|
|
85
115
|
This does **not** add a `noindex` meta tag — it only controls sitemap inclusion.
|
|
86
116
|
|
|
@@ -143,6 +143,7 @@ const renderChildren = createRenderChildren(registry);
|
|
|
143
143
|
title={data.meta.title}
|
|
144
144
|
description={data.meta.description}
|
|
145
145
|
canonical={data.meta.canonical}
|
|
146
|
+
robots={data.robotsContent}
|
|
146
147
|
siteName={data.meta.siteName}
|
|
147
148
|
collectTag={data.collectTag}
|
|
148
149
|
>
|