@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.
- package/CHANGELOG.md +7 -0
- package/README.md +4 -4
- package/dist/content/provider.d.ts +1 -0
- package/dist/internal/domain/edge/compose.d.ts +24 -0
- package/dist/internal/domain/edge/compose.js +89 -0
- package/dist/internal/domain/edge/index.d.ts +1 -0
- package/dist/internal/domain/edge/index.js +1 -0
- package/dist/internal/domain/edge/types.d.ts +11 -0
- package/dist/next/hosted-page-target.d.ts +7 -0
- package/dist/next/hosted-page-target.js +39 -0
- package/dist/next/page.d.ts +29 -6
- package/dist/next/page.js +23 -23
- package/dist/next/server.js +28 -1
- package/dist/providers/s3.js +11 -4
- package/package.json +7 -7
- package/templates/common/starter-content/.content/patterns/components/button.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/container.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/feature_card.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/feature_grid.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/home_hero.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/personalization_choice.json +42 -0
- package/templates/common/starter-content/.content/patterns/components/site-footer.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/site-navigation.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/stats_bar.json +2 -2
- package/templates/common/starter-content/.content/patterns/layouts/default.json +2 -2
- package/templates/nextjs/template/.aci.yaml +10 -0
- /package/templates/common/.content/overlays/audience/preference-blue/pages/{home/_index.json → _index.json} +0 -0
- /package/templates/common/.content/overlays/audience/preference-red/pages/{home/_index.json → _index.json} +0 -0
- /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.
|
|
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.
|
|
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.
|
|
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.
|
|
87
|
+
npx @gradial/aci@0.1.52-rc.7 init --list
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
## Personalization: The One-Tag Install
|
|
@@ -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
|
+
}
|
|
@@ -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,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
|
+
}
|
package/dist/next/page.d.ts
CHANGED
|
@@ -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}/
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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}/
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
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
|
|
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}
|
|
110
|
-
* `['__r', releaseId, '
|
|
111
|
-
*
|
|
112
|
-
*
|
|
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
|
-
|
|
129
|
-
|
|
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 {
|
|
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 };
|
package/dist/next/server.js
CHANGED
|
@@ -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.
|
|
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);
|
package/dist/providers/s3.js
CHANGED
|
@@ -78,12 +78,13 @@ export class S3ContentProvider {
|
|
|
78
78
|
}
|
|
79
79
|
async getLayout(name) {
|
|
80
80
|
const manifest = await this.manifest();
|
|
81
|
-
const
|
|
82
|
-
if (!
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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.
|
|
150
|
-
"@gradial/aci-cli-darwin-x64": "0.1.52-rc.
|
|
151
|
-
"@gradial/aci-cli-linux-arm64": "0.1.52-rc.
|
|
152
|
-
"@gradial/aci-cli-linux-x64": "0.1.52-rc.
|
|
153
|
-
"@gradial/aci-cli-win32-arm64": "0.1.52-rc.
|
|
154
|
-
"@gradial/aci-cli-win32-x64": "0.1.52-rc.
|
|
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": "
|
|
4
|
-
"codeDigest": "sha256:
|
|
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": "
|
|
4
|
-
"codeDigest": "sha256:
|
|
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": "
|
|
4
|
-
"codeDigest": "sha256:
|
|
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": "
|
|
4
|
-
"codeDigest": "sha256:
|
|
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": "
|
|
4
|
-
"codeDigest": "sha256:
|
|
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",
|
package/templates/common/starter-content/.content/patterns/components/personalization_choice.json
ADDED
|
@@ -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": "
|
|
4
|
-
"codeDigest": "sha256:
|
|
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": "
|
|
4
|
-
"codeDigest": "sha256:
|
|
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": "
|
|
4
|
-
"codeDigest": "sha256:
|
|
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": "
|
|
4
|
-
"codeDigest": "sha256:
|
|
3
|
+
"buildId": "codebuild_01a017ed-c52f-703a-a6b4-a0943c057d31",
|
|
4
|
+
"codeDigest": "sha256:119dd8414ca342ca67f4dd4703dcb504630220bbdca9130b1501ee7b25693134",
|
|
5
5
|
"name": "default",
|
|
6
6
|
"slots": [
|
|
7
7
|
"header",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|