@gradial/aci 0.1.52-rc.6 → 0.1.52-rc.7

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.
Files changed (29) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +4 -4
  3. package/dist/content/provider.d.ts +1 -0
  4. package/dist/internal/domain/edge/compose.d.ts +24 -0
  5. package/dist/internal/domain/edge/compose.js +89 -0
  6. package/dist/internal/domain/edge/index.d.ts +1 -0
  7. package/dist/internal/domain/edge/index.js +1 -0
  8. package/dist/internal/domain/edge/types.d.ts +11 -0
  9. package/dist/next/hosted-page-target.d.ts +7 -0
  10. package/dist/next/hosted-page-target.js +39 -0
  11. package/dist/next/page.d.ts +29 -6
  12. package/dist/next/page.js +23 -23
  13. package/dist/next/server.js +28 -1
  14. package/dist/providers/s3.js +11 -4
  15. package/package.json +7 -7
  16. package/templates/common/starter-content/.content/patterns/components/button.json +2 -2
  17. package/templates/common/starter-content/.content/patterns/components/container.json +2 -2
  18. package/templates/common/starter-content/.content/patterns/components/feature_card.json +2 -2
  19. package/templates/common/starter-content/.content/patterns/components/feature_grid.json +2 -2
  20. package/templates/common/starter-content/.content/patterns/components/home_hero.json +2 -2
  21. package/templates/common/starter-content/.content/patterns/components/personalization_choice.json +42 -0
  22. package/templates/common/starter-content/.content/patterns/components/site-footer.json +2 -2
  23. package/templates/common/starter-content/.content/patterns/components/site-navigation.json +2 -2
  24. package/templates/common/starter-content/.content/patterns/components/stats_bar.json +2 -2
  25. package/templates/common/starter-content/.content/patterns/layouts/default.json +2 -2
  26. package/templates/nextjs/template/.aci.yaml +10 -0
  27. /package/templates/common/.content/overlays/audience/preference-blue/pages/{home/_index.json → _index.json} +0 -0
  28. /package/templates/common/.content/overlays/audience/preference-red/pages/{home/_index.json → _index.json} +0 -0
  29. /package/templates/common/.content/pages/{home/_index.json → _index.json} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.52-rc.7 - 2026-08-19
4
+
5
+ ### Fixed
6
+
7
+ - Resolve Vercel SSR base and variant targets from the immutable `__r` path,
8
+ keeping `__variant` target identity separate from the logical public route.
9
+
3
10
  ## 0.1.45 - 2026-07-29
4
11
 
5
12
  ### Fixed
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Agentic Content Infrastructure SDK
2
2
 
3
- `@gradial/aci` `0.1.52-rc.6` is a pre-1.0 public-surface reset for the ACI
3
+ `@gradial/aci` `0.1.52-rc.7` 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.52-rc.6 zod@^4
11
+ npm install @gradial/aci@0.1.52-rc.7 zod@^4
12
12
  ```
13
13
 
14
14
  ## Root Import
@@ -78,13 +78,13 @@ host bundles select and serve immutable release artifacts.
78
78
  The first RC scaffold path is Next:
79
79
 
80
80
  ```bash
81
- npx @gradial/aci@0.1.52-rc.6 init my-site --template next-starter
81
+ npx @gradial/aci@0.1.52-rc.7 init my-site --template next-starter
82
82
  ```
83
83
 
84
84
  List available templates:
85
85
 
86
86
  ```bash
87
- npx @gradial/aci@0.1.52-rc.6 init --list
87
+ npx @gradial/aci@0.1.52-rc.7 init --list
88
88
  ```
89
89
 
90
90
  ## Personalization: The One-Tag Install
@@ -21,6 +21,7 @@ export interface FragmentEntry {
21
21
  }
22
22
  export interface LayoutEntry {
23
23
  name: string;
24
+ payloadRef: string;
24
25
  slots: LayoutSlot[];
25
26
  defaults?: Record<string, FragmentRef[]>;
26
27
  }
@@ -0,0 +1,24 @@
1
+ export interface ObjectReader {
2
+ readObject(path: string): Promise<Uint8Array>;
3
+ }
4
+ export interface ComposeObjectRef {
5
+ path: string;
6
+ digest: string;
7
+ }
8
+ export interface ComposeSlot {
9
+ name: string;
10
+ required: boolean;
11
+ placeholder: string;
12
+ parts: ComposeObjectRef[];
13
+ }
14
+ export interface ComposeEnvelope {
15
+ schema: 'gradial.compose-envelope.v1';
16
+ algorithm: 'aci-html-compose-v1';
17
+ shell: ComposeObjectRef;
18
+ slots: ComposeSlot[];
19
+ }
20
+ export declare function composeEnvelope(reader: ObjectReader, envelopePath: string, envelopeDigest: string): Promise<{
21
+ html: string;
22
+ contentType: string;
23
+ }>;
24
+ export declare function validateEnvelope(envelope: unknown): ComposeEnvelope;
@@ -0,0 +1,89 @@
1
+ const decoder = new TextDecoder('utf-8', { fatal: true });
2
+ export async function composeEnvelope(reader, envelopePath, envelopeDigest) {
3
+ const envelopeBytes = await reader.readObject(envelopePath);
4
+ await requireDigest(envelopeBytes, envelopeDigest, 'compose envelope');
5
+ let parsed;
6
+ try {
7
+ parsed = JSON.parse(decoder.decode(envelopeBytes));
8
+ }
9
+ catch {
10
+ throw new Error('compose envelope is malformed');
11
+ }
12
+ const envelope = validateEnvelope(parsed);
13
+ const shellBytes = await reader.readObject(envelope.shell.path);
14
+ await requireDigest(shellBytes, envelope.shell.digest, 'compose shell');
15
+ let html = decoder.decode(shellBytes);
16
+ for (const slot of envelope.slots) {
17
+ if (occurrences(html, slot.placeholder) !== 1) {
18
+ throw new Error(`compose slot ${JSON.stringify(slot.name)} placeholder must occur exactly once`);
19
+ }
20
+ if (slot.required && slot.parts.length === 0) {
21
+ throw new Error(`compose slot ${JSON.stringify(slot.name)} is required`);
22
+ }
23
+ const parts = [];
24
+ for (const part of slot.parts) {
25
+ const bytes = await reader.readObject(part.path);
26
+ await requireDigest(bytes, part.digest, `compose slot ${slot.name}`);
27
+ parts.push(decoder.decode(bytes));
28
+ }
29
+ html = html.replace(slot.placeholder, parts.join(''));
30
+ }
31
+ if (html.includes('<!--ACI_SLOT:')) {
32
+ throw new Error('compose shell has unresolved slots');
33
+ }
34
+ return { html, contentType: 'text/html; charset=utf-8' };
35
+ }
36
+ export function validateEnvelope(envelope) {
37
+ if (!envelope || typeof envelope !== 'object') {
38
+ throw new Error('compose envelope is malformed');
39
+ }
40
+ const obj = envelope;
41
+ if (obj.schema !== 'gradial.compose-envelope.v1'
42
+ || obj.algorithm !== 'aci-html-compose-v1'
43
+ || !Array.isArray(obj.slots)) {
44
+ throw new Error('compose envelope is malformed');
45
+ }
46
+ const env = envelope;
47
+ validateRef(env.shell);
48
+ const names = new Set();
49
+ for (const slot of env.slots) {
50
+ if (!slot || !/^[A-Za-z][A-Za-z0-9_-]*$/.test(slot.name) || names.has(slot.name)) {
51
+ throw new Error('compose slot is malformed');
52
+ }
53
+ names.add(slot.name);
54
+ if (slot.placeholder !== `<!--ACI_SLOT:${slot.name}-->` || !Array.isArray(slot.parts)) {
55
+ throw new Error(`compose slot ${JSON.stringify(slot.name)} is malformed`);
56
+ }
57
+ for (const part of slot.parts)
58
+ validateRef(part);
59
+ }
60
+ return env;
61
+ }
62
+ function validateRef(ref) {
63
+ const path = String(ref?.path || '');
64
+ if (!path || path.includes('\\') || path.split('/').some((part) => !part || part === '.' || part === '..')) {
65
+ throw new Error('compose object path is invalid');
66
+ }
67
+ if (!/^[0-9a-f]{64}$/.test(String(ref.digest || '').toLowerCase())) {
68
+ throw new Error('compose object digest is invalid');
69
+ }
70
+ }
71
+ async function requireDigest(bytes, want, label) {
72
+ const ab = new ArrayBuffer(bytes.byteLength);
73
+ new Uint8Array(ab).set(bytes);
74
+ const actual = Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', ab)), (byte) => byte.toString(16).padStart(2, '0')).join('');
75
+ if (actual !== want.toLowerCase())
76
+ throw new Error(`${label} digest mismatch`);
77
+ }
78
+ function occurrences(value, needle) {
79
+ let count = 0;
80
+ let offset = 0;
81
+ while (true) {
82
+ offset = value.indexOf(needle, offset);
83
+ if (offset < 0)
84
+ break;
85
+ count += 1;
86
+ offset += needle.length;
87
+ }
88
+ return count;
89
+ }
@@ -1,4 +1,5 @@
1
1
  export * from './types.js';
2
2
  export * from './personalization-core.js';
3
3
  export * from './identity.js';
4
+ export * from './compose.js';
4
5
  export { resolvePersonalizationRequest, } from './resolve.js';
@@ -3,4 +3,5 @@
3
3
  export * from './types.js';
4
4
  export * from './personalization-core.js';
5
5
  export * from './identity.js';
6
+ export * from './compose.js';
6
7
  export { resolvePersonalizationRequest, } from './resolve.js';
@@ -178,4 +178,15 @@ export interface PersonalizationResolution {
178
178
  allocationVersion: string;
179
179
  };
180
180
  }
181
+ export interface CompositionDescriptor {
182
+ layoutArtifact: string;
183
+ fragments?: Record<string, string>;
184
+ }
185
+ export type SelectedTargetKind = 'direct' | 'compose' | 'render';
186
+ export interface SelectedTarget {
187
+ kind: SelectedTargetKind;
188
+ path: string;
189
+ digest: string;
190
+ composition?: CompositionDescriptor;
191
+ }
181
192
  export { IdentityErasureFencedError } from '../../platform/collectionstore/errors.js';
@@ -0,0 +1,7 @@
1
+ export type HostedPageTarget = Readonly<{
2
+ kind: 'base' | 'variant';
3
+ route: string;
4
+ pageRef: string;
5
+ variantKey?: string;
6
+ }>;
7
+ export declare function parseHostedPageTarget(rawSegments: readonly string[]): HostedPageTarget | null;
@@ -0,0 +1,39 @@
1
+ const VARIANT_NAMESPACE = '__variant';
2
+ export function parseHostedPageTarget(rawSegments) {
3
+ const segments = decodeSegments(rawSegments);
4
+ if (!segments)
5
+ return null;
6
+ const variantAt = segments.indexOf(VARIANT_NAMESPACE);
7
+ if (variantAt < 0)
8
+ return target('base', segments, segments);
9
+ if (variantAt !== segments.lastIndexOf(VARIANT_NAMESPACE) || variantAt !== segments.length - 2) {
10
+ return null;
11
+ }
12
+ return {
13
+ ...target('variant', segments.slice(0, variantAt), segments),
14
+ variantKey: segments[variantAt + 1],
15
+ };
16
+ }
17
+ function target(kind, routeSegments, targetSegments) {
18
+ return {
19
+ kind,
20
+ route: routeSegments.length === 0 ? '/' : `/${routeSegments.join('/')}/`,
21
+ pageRef: ['routes', ...targetSegments, 'index.json'].join('/'),
22
+ };
23
+ }
24
+ function decodeSegments(rawSegments) {
25
+ const segments = [];
26
+ for (const raw of rawSegments) {
27
+ let segment;
28
+ try {
29
+ segment = decodeURIComponent(raw);
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ if (!segment || segment === '.' || segment === '..' || segment.includes('/'))
35
+ return null;
36
+ segments.push(segment);
37
+ }
38
+ return segments;
39
+ }
@@ -31,12 +31,10 @@ export interface CreatePageOptions {
31
31
  * the collect script tag exactly once per page (design/04 contract tag).
32
32
  *
33
33
  * Release-scoped routes (production): the middleware rewrites a public URL
34
- * to `/__r/{releaseId}/routes/{path}`. The optional catch-all `[[...slug]]`
35
- * receives `['__r', releaseId, 'routes', ...pathSegments]`. createPage
36
- * extracts the releaseId and passes it to the content loader, giving
37
- * each release a unique CDN cache key without a separate route folder.
38
- * The `routes` namespace segment leaves room for future siblings under
39
- * the release prefix (e.g. `/__r/{releaseId}/assets/*`).
34
+ * to `/__r/{releaseId}/{targetRef}`. The target ref selects the exact immutable
35
+ * base or variant page while its prefix identifies the logical public route.
36
+ * createPage passes both identities to the content loader, giving each target
37
+ * a distinct CDN cache key without sending selection headers into Next.
40
38
  *
41
39
  * Dev pass-through (ACI_CONTENT_PROVIDER=file): the middleware passes
42
40
  * requests through unrewritten, so `/` arrives with an empty/undefined
@@ -54,4 +52,29 @@ export declare function createPage(registry: Registry, options?: CreatePageOptio
54
52
  generateMetadata: ({ params }: NextPageProps) => Promise<Metadata>;
55
53
  default: ({ params }: NextPageProps) => Promise<import("react").JSX.Element>;
56
54
  };
55
+ /**
56
+ * Resolves the logical route and releaseId from Next.js catch-all params.
57
+ *
58
+ * Two shapes are supported:
59
+ *
60
+ * 1. **Rewritten (production):** middleware rewrites a public URL to the exact
61
+ * selected target under `/__r/{releaseId}`. A root variant therefore has
62
+ * `['__r', releaseId, '__variant', variantKey]`. The full suffix selects the
63
+ * immutable page object; the segments before the reserved `__variant`
64
+ * namespace form the logical public route.
65
+ *
66
+ * 2. **Dev pass-through (ACI_CONTENT_PROVIDER=file):** middleware passes
67
+ * requests through unrewritten, so the slug is the raw public path
68
+ * segments (possibly empty/undefined for `/`). No releaseId is present;
69
+ * the file provider serves content from disk.
70
+ */
71
+ export declare function resolveRouteAndRelease(params: Promise<{
72
+ slug?: string[];
73
+ }> | {
74
+ slug?: string[];
75
+ }): Promise<{
76
+ route: string;
77
+ releaseId?: string;
78
+ pageRef?: string;
79
+ }>;
57
80
  export {};
package/dist/next/page.js CHANGED
@@ -7,6 +7,7 @@ import { renderBlocks } from './render.js';
7
7
  import { AssignmentsScript } from './assignments-script.js';
8
8
  import { CollectScript } from './collect-script.js';
9
9
  import { renderProofContentFor } from '../internal/render-proof.js';
10
+ import { parseHostedPageTarget } from './hosted-page-target.js';
10
11
  /**
11
12
  * Creates the catch-all route handler for a Next.js ACI site.
12
13
  *
@@ -28,12 +29,10 @@ import { renderProofContentFor } from '../internal/render-proof.js';
28
29
  * the collect script tag exactly once per page (design/04 contract tag).
29
30
  *
30
31
  * Release-scoped routes (production): the middleware rewrites a public URL
31
- * to `/__r/{releaseId}/routes/{path}`. The optional catch-all `[[...slug]]`
32
- * receives `['__r', releaseId, 'routes', ...pathSegments]`. createPage
33
- * extracts the releaseId and passes it to the content loader, giving
34
- * each release a unique CDN cache key without a separate route folder.
35
- * The `routes` namespace segment leaves room for future siblings under
36
- * the release prefix (e.g. `/__r/{releaseId}/assets/*`).
32
+ * to `/__r/{releaseId}/{targetRef}`. The target ref selects the exact immutable
33
+ * base or variant page while its prefix identifies the logical public route.
34
+ * createPage passes both identities to the content loader, giving each target
35
+ * a distinct CDN cache key without sending selection headers into Next.
37
36
  *
38
37
  * Dev pass-through (ACI_CONTENT_PROVIDER=file): the middleware passes
39
38
  * requests through unrewritten, so `/` arrives with an empty/undefined
@@ -49,8 +48,9 @@ import { renderProofContentFor } from '../internal/render-proof.js';
49
48
  export function createPage(registry, options) {
50
49
  const dynamic = 'force-dynamic';
51
50
  async function generateMetadata({ params }) {
52
- const { route, releaseId } = await resolveRouteAndRelease(params);
53
- const input = await getRenderInput(route, { releaseId });
51
+ const { route, releaseId, pageRef } = await resolveRouteAndRelease(params);
52
+ const fetchConfig = { releaseId, pageRef };
53
+ const input = await getRenderInput(route, fetchConfig);
54
54
  const meta = routeMetadataForContent(input.siteConfig, input.page);
55
55
  return {
56
56
  title: meta.title,
@@ -64,8 +64,8 @@ export function createPage(registry, options) {
64
64
  };
65
65
  }
66
66
  async function CatchAllPage({ params }) {
67
- const { route, releaseId } = await resolveRouteAndRelease(params);
68
- const fetchConfig = { releaseId };
67
+ const { route, releaseId, pageRef } = await resolveRouteAndRelease(params);
68
+ const fetchConfig = { releaseId, pageRef };
69
69
  try {
70
70
  const input = await getRenderInput(route, fetchConfig);
71
71
  if (!input.page)
@@ -105,19 +105,18 @@ export function createPage(registry, options) {
105
105
  *
106
106
  * Two shapes are supported:
107
107
  *
108
- * 1. **Rewritten (production):** middleware rewrites a public URL to
109
- * `/__r/{releaseId}/routes/{path}`, so slug is
110
- * `['__r', releaseId, 'routes', ...pathSegments]`. The releaseId is
111
- * extracted; the remaining segments form the content route. The `routes`
112
- * segment is the namespace for page routes (siblings like `assets/` live
113
- * under the same release prefix).
108
+ * 1. **Rewritten (production):** middleware rewrites a public URL to the exact
109
+ * selected target under `/__r/{releaseId}`. A root variant therefore has
110
+ * `['__r', releaseId, '__variant', variantKey]`. The full suffix selects the
111
+ * immutable page object; the segments before the reserved `__variant`
112
+ * namespace form the logical public route.
114
113
  *
115
114
  * 2. **Dev pass-through (ACI_CONTENT_PROVIDER=file):** middleware passes
116
115
  * requests through unrewritten, so the slug is the raw public path
117
116
  * segments (possibly empty/undefined for `/`). No releaseId is present;
118
117
  * the file provider serves content from disk.
119
118
  */
120
- async function resolveRouteAndRelease(params) {
119
+ export async function resolveRouteAndRelease(params) {
121
120
  const resolved = await params;
122
121
  const slug = resolved.slug ?? [];
123
122
  if (slug.length >= 2 && slug[0] === '__r') {
@@ -125,13 +124,14 @@ async function resolveRouteAndRelease(params) {
125
124
  if (!releaseId || releaseId.includes('/') || releaseId === '.' || releaseId === '..') {
126
125
  return { route: '/' };
127
126
  }
128
- // Skip the 'routes' namespace segment when present.
129
- const rest = slug.length >= 3 && slug[2] === 'routes' ? slug.slice(3) : slug.slice(2);
130
- if (rest.length === 0)
131
- return { route: '/', releaseId };
132
- if (rest.some((s) => s === '' || s === '.' || s === '..'))
127
+ const target = parseHostedPageTarget(slug.slice(2));
128
+ if (!target)
133
129
  return { route: '/' };
134
- return { route: `/${rest.join('/')}/`, releaseId };
130
+ return {
131
+ route: target.route,
132
+ releaseId,
133
+ pageRef: target.pageRef,
134
+ };
135
135
  }
136
136
  const route = routeFromSlug(slug);
137
137
  return { route };
@@ -1,14 +1,18 @@
1
1
  import { headers } from 'next/headers.js';
2
2
  import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError, } from '../content/provider.js';
3
+ import { ObjectContentProvider } from '../providers/object.js';
3
4
  import { normalizeRoute } from '../content/routes.js';
4
5
  import { getCachedManifest, getCachedSiteConfig } from '../content/cache.js';
5
6
  import { S3ContentProvider } from '../providers/s3.js';
7
+ import { VercelReleaseObjectReader } from '../providers/vercel-object-reader.js';
6
8
  import { GRADIAL_RENDER_INPUT_HEADER, getPendingRenderInput } from '../runtime/page.js';
7
9
  import { attachRenderProofReceipt, selectVerifiedPage, } from '../internal/render-proof.js';
8
10
  import { getUncachedEdgeConfigValue } from './edge-config.js';
9
11
  import { activeReleaseV1Key, clampDeploymentCompatibleReleaseV1, decodeActiveReleaseV1, decodeDeploymentCompatibilityV1, deploymentCompatibilityKey, } from './vercel-edge-projection-v1.js';
10
12
  export { FragmentNotFoundError, PageNotFoundError, LayoutNotFoundError };
11
- const releaseObjectReaderSymbol = Symbol.for('aci.releaseObjectReader');
13
+ const releaseObjectReaderSymbol = Symbol.for('@gradial/aci.release-object-reader');
14
+ const objectProviders = new WeakMap();
15
+ let vercelReleaseObjectReader;
12
16
  export function setReleaseObjectReader(reader) {
13
17
  const scope = globalThis;
14
18
  if (reader)
@@ -339,6 +343,29 @@ async function resolveS3Provider(config) {
339
343
  }
340
344
  async function resolveProviderWithFallback(config) {
341
345
  const injected = getInjectedReader();
346
+ const pageRef = config.pageRef;
347
+ const hostedReaderConfigured = injected || config.roleArn || process.env.AWS_ROLE_ARN;
348
+ if (pageRef && hostedReaderConfigured) {
349
+ const releaseId = config.releaseId || process.env.ACI_RELEASE_ID || 'test';
350
+ let provider = objectProviders.get(config);
351
+ if (!provider) {
352
+ if (!injected && !vercelReleaseObjectReader) {
353
+ vercelReleaseObjectReader = new VercelReleaseObjectReader({
354
+ bucket: config.bucket,
355
+ keyPrefix: config.keyPrefix,
356
+ region: config.region,
357
+ roleArn: config.roleArn,
358
+ endpoint: config.endpoint,
359
+ });
360
+ }
361
+ const reader = injected || vercelReleaseObjectReader;
362
+ if (!reader)
363
+ throw new Error('ACI hosted page reader is unavailable');
364
+ provider = new ObjectContentProvider({ releaseId, pageRef, reader });
365
+ objectProviders.set(config, provider);
366
+ }
367
+ return provider;
368
+ }
342
369
  if (injected) {
343
370
  const releaseId = config.releaseId || process.env.ACI_RELEASE_ID || 'test';
344
371
  return new InjectedReaderProvider(releaseId, injected);
@@ -78,12 +78,13 @@ export class S3ContentProvider {
78
78
  }
79
79
  async getLayout(name) {
80
80
  const manifest = await this.manifest();
81
- const layouts = manifest.layouts;
82
- if (!layouts || !layouts[name]) {
81
+ const entry = manifest.layouts?.[name];
82
+ if (!entry) {
83
83
  throw new LayoutNotFoundError(name);
84
84
  }
85
+ const ref = entry.payloadRef;
85
86
  try {
86
- return await this.getJSON(`layouts/${name}.json`);
87
+ return await this.getJSON(ref);
87
88
  }
88
89
  catch (error) {
89
90
  throw new LayoutNotFoundError(name, error);
@@ -199,7 +200,13 @@ export class S3ContentProvider {
199
200
  }
200
201
  let page;
201
202
  try {
202
- page = JSON.parse(source);
203
+ const parsed = JSON.parse(source);
204
+ if (parsed?.schemaVersion === 'gradial.ssr-page.v1' && parsed.page !== undefined) {
205
+ page = parsed.page;
206
+ }
207
+ else {
208
+ page = parsed;
209
+ }
203
210
  }
204
211
  catch (cause) {
205
212
  throw pageIntegrityError(`invalid JSON for ${tuple.contentRef}`, cause);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradial/aci",
3
- "version": "0.1.52-rc.6",
3
+ "version": "0.1.52-rc.7",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/gradial/bare-metal-go.git",
@@ -146,11 +146,11 @@
146
146
  "zod": "^4.0.0"
147
147
  },
148
148
  "optionalDependencies": {
149
- "@gradial/aci-cli-darwin-arm64": "0.1.52-rc.6",
150
- "@gradial/aci-cli-darwin-x64": "0.1.52-rc.6",
151
- "@gradial/aci-cli-linux-arm64": "0.1.52-rc.6",
152
- "@gradial/aci-cli-linux-x64": "0.1.52-rc.6",
153
- "@gradial/aci-cli-win32-arm64": "0.1.52-rc.6",
154
- "@gradial/aci-cli-win32-x64": "0.1.52-rc.6"
149
+ "@gradial/aci-cli-darwin-arm64": "0.1.52-rc.7",
150
+ "@gradial/aci-cli-darwin-x64": "0.1.52-rc.7",
151
+ "@gradial/aci-cli-linux-arm64": "0.1.52-rc.7",
152
+ "@gradial/aci-cli-linux-x64": "0.1.52-rc.7",
153
+ "@gradial/aci-cli-win32-arm64": "0.1.52-rc.7",
154
+ "@gradial/aci-cli-win32-x64": "0.1.52-rc.7"
155
155
  }
156
156
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$type": "componentPattern",
3
- "buildId": "codebuild_01a01331-b53d-7bab-a900-d3cef26465d6",
4
- "codeDigest": "sha256:827b1337ab20e5c054419de4e66f6ffedfc210ca1c34dfdee6c23091edd69d02",
3
+ "buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
4
+ "codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
5
5
  "name": "button",
6
6
  "schema": {
7
7
  "$schema": "https://json-schema.org/draft/2020-12/schema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$type": "componentPattern",
3
- "buildId": "codebuild_01a01331-b53d-7bab-a900-d3cef26465d6",
4
- "codeDigest": "sha256:827b1337ab20e5c054419de4e66f6ffedfc210ca1c34dfdee6c23091edd69d02",
3
+ "buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
4
+ "codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
5
5
  "name": "container",
6
6
  "schema": {
7
7
  "$schema": "https://json-schema.org/draft/2020-12/schema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$type": "componentPattern",
3
- "buildId": "codebuild_01a01331-b53d-7bab-a900-d3cef26465d6",
4
- "codeDigest": "sha256:827b1337ab20e5c054419de4e66f6ffedfc210ca1c34dfdee6c23091edd69d02",
3
+ "buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
4
+ "codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
5
5
  "name": "feature_card",
6
6
  "schema": {
7
7
  "$schema": "https://json-schema.org/draft/2020-12/schema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$type": "componentPattern",
3
- "buildId": "codebuild_01a01331-b53d-7bab-a900-d3cef26465d6",
4
- "codeDigest": "sha256:827b1337ab20e5c054419de4e66f6ffedfc210ca1c34dfdee6c23091edd69d02",
3
+ "buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
4
+ "codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
5
5
  "name": "feature_grid",
6
6
  "schema": {
7
7
  "$schema": "https://json-schema.org/draft/2020-12/schema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$type": "componentPattern",
3
- "buildId": "codebuild_01a01331-b53d-7bab-a900-d3cef26465d6",
4
- "codeDigest": "sha256:827b1337ab20e5c054419de4e66f6ffedfc210ca1c34dfdee6c23091edd69d02",
3
+ "buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
4
+ "codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
5
5
  "name": "home_hero",
6
6
  "schema": {
7
7
  "$schema": "https://json-schema.org/draft/2020-12/schema",
@@ -0,0 +1,42 @@
1
+ {
2
+ "$type": "componentPattern",
3
+ "buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
4
+ "codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
5
+ "name": "personalization_choice",
6
+ "schema": {
7
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
8
+ "title": "personalization_choice",
9
+ "type": "object",
10
+ "properties": {
11
+ "title": {
12
+ "type": "string",
13
+ "minLength": 1
14
+ },
15
+ "description": {
16
+ "type": "string"
17
+ },
18
+ "redLabel": {
19
+ "type": "string",
20
+ "minLength": 1
21
+ },
22
+ "blueLabel": {
23
+ "type": "string",
24
+ "minLength": 1
25
+ },
26
+ "refreshLabel": {
27
+ "type": "string",
28
+ "minLength": 1
29
+ }
30
+ },
31
+ "required": [
32
+ "title",
33
+ "redLabel",
34
+ "blueLabel",
35
+ "refreshLabel"
36
+ ],
37
+ "additionalProperties": false
38
+ },
39
+ "imageSlots": {},
40
+ "notes": "",
41
+ "skills": []
42
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$type": "componentPattern",
3
- "buildId": "codebuild_01a01331-b53d-7bab-a900-d3cef26465d6",
4
- "codeDigest": "sha256:827b1337ab20e5c054419de4e66f6ffedfc210ca1c34dfdee6c23091edd69d02",
3
+ "buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
4
+ "codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
5
5
  "name": "site-footer",
6
6
  "schema": {
7
7
  "$schema": "https://json-schema.org/draft/2020-12/schema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$type": "componentPattern",
3
- "buildId": "codebuild_01a01331-b53d-7bab-a900-d3cef26465d6",
4
- "codeDigest": "sha256:827b1337ab20e5c054419de4e66f6ffedfc210ca1c34dfdee6c23091edd69d02",
3
+ "buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
4
+ "codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
5
5
  "name": "site-navigation",
6
6
  "schema": {
7
7
  "$schema": "https://json-schema.org/draft/2020-12/schema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$type": "componentPattern",
3
- "buildId": "codebuild_01a01331-b53d-7bab-a900-d3cef26465d6",
4
- "codeDigest": "sha256:827b1337ab20e5c054419de4e66f6ffedfc210ca1c34dfdee6c23091edd69d02",
3
+ "buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
4
+ "codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
5
5
  "name": "stats_bar",
6
6
  "schema": {
7
7
  "$schema": "https://json-schema.org/draft/2020-12/schema",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$type": "layoutPattern",
3
- "buildId": "codebuild_01a01331-b53d-7bab-a900-d3cef26465d6",
4
- "codeDigest": "sha256:827b1337ab20e5c054419de4e66f6ffedfc210ca1c34dfdee6c23091edd69d02",
3
+ "buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
4
+ "codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
5
5
  "name": "default",
6
6
  "slots": [
7
7
  "header",
@@ -0,0 +1,10 @@
1
+ version: "1"
2
+ siteId: "starter-nextjs"
3
+ framework: next
4
+ source:
5
+ root: "./"
6
+ componentRegistry: "src/cms/registry.ts"
7
+ layoutRegistry: "src/app/layout.tsx"
8
+ routes:
9
+ cmsManaged: "/[...slug]"
10
+ rendererProtocol: stdio-json