@gradial/aci 0.1.20 → 0.1.21
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/dist/next/middleware.d.ts +3 -2
- package/dist/next/middleware.js +49 -8
- package/dist/preview/core.d.ts +5 -0
- package/dist/preview/core.js +1 -1
- package/package.json +2 -1
- package/src/types/block-ref.ts +74 -0
- package/templates/astro/template/.aci.yaml +1 -0
- package/templates/nextjs/template/.aci.yaml +1 -0
- package/templates/nextjs/template/src/middleware.ts +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agentic Content Infrastructure SDK
|
|
2
2
|
|
|
3
|
-
`@gradial/aci` `0.1.
|
|
3
|
+
`@gradial/aci` `0.1.21` 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.
|
|
11
|
+
npm install @gradial/aci@0.1.21 zod@^4
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Root Import
|
|
@@ -68,13 +68,13 @@ go through an app-owned HTTP endpoint backed server-side by a `ContentProvider`.
|
|
|
68
68
|
The first RC scaffold path is Next:
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
-
npx @gradial/aci@0.1.
|
|
71
|
+
npx @gradial/aci@0.1.21 init my-site --template next-starter
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
List available templates:
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
npx @gradial/aci@0.1.
|
|
77
|
+
npx @gradial/aci@0.1.21 init --list
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
## Verify Locally
|
package/bin/aci-darwin-arm64
CHANGED
|
Binary file
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { NextResponse, type NextRequest } from 'next/server.js';
|
|
2
|
-
export interface
|
|
2
|
+
export interface GradialMiddlewareConfig {
|
|
3
3
|
siteId?: string;
|
|
4
4
|
edgeConfig?: string;
|
|
5
5
|
previewSignKey?: string;
|
|
6
6
|
deploymentId?: string;
|
|
7
|
+
deploymentProtectionBypass?: string;
|
|
7
8
|
}
|
|
8
|
-
export declare function
|
|
9
|
+
export declare function createGradialMiddleware(config?: GradialMiddlewareConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
9
10
|
export declare const config: {
|
|
10
11
|
matcher: string[];
|
|
11
12
|
};
|
package/dist/next/middleware.js
CHANGED
|
@@ -3,7 +3,7 @@ import { getUncachedEdgeConfigValue } from './edge-config.js';
|
|
|
3
3
|
import { PREVIEW_TOKEN_COOKIE, PREVIEW_RELEASE_COOKIE, PREVIEW_HEADER, RELEASE_HEADER, extractPreviewToken, resolvePreviewContext, createPreviewCookies, previewSignKey, } from '../preview/core.js';
|
|
4
4
|
const ROUTE_HEADER = 'x-gradial-route';
|
|
5
5
|
const DISPATCH_HEADER = 'x-gradial-dispatch';
|
|
6
|
-
export function
|
|
6
|
+
export function createGradialMiddleware(config = {}) {
|
|
7
7
|
return async function middleware(request) {
|
|
8
8
|
const url = request.nextUrl;
|
|
9
9
|
// ?leave_preview=1 → clear cookies, redirect to clean URL
|
|
@@ -14,7 +14,7 @@ export function createMiddleware(config = {}) {
|
|
|
14
14
|
response.cookies.delete(PREVIEW_RELEASE_COOKIE);
|
|
15
15
|
return response;
|
|
16
16
|
}
|
|
17
|
-
const requestHeaders =
|
|
17
|
+
const requestHeaders = gradialHeaders(request.headers);
|
|
18
18
|
// Try preview token first; if valid, use that release.
|
|
19
19
|
// If no token or invalid, fall through to active release from edge config.
|
|
20
20
|
const preview = await resolvePreviewFromRequest(request, config);
|
|
@@ -22,6 +22,19 @@ export function createMiddleware(config = {}) {
|
|
|
22
22
|
if (!releaseId) {
|
|
23
23
|
return NextResponse.next({ request: { headers: requestHeaders } });
|
|
24
24
|
}
|
|
25
|
+
// Cross-deployment preview: if the token was staged on a different Vercel
|
|
26
|
+
// deployment, rewrite the request to that deployment. This keeps the browser
|
|
27
|
+
// on the production domain while the correct code runtime serves the request.
|
|
28
|
+
const rewriteTarget = preview ? previewRewriteTarget(request, preview, config) : null;
|
|
29
|
+
if (rewriteTarget) {
|
|
30
|
+
const response = NextResponse.rewrite(rewriteTarget, { request: { headers: requestHeaders } });
|
|
31
|
+
if (preview?.token && preview.isPreview) {
|
|
32
|
+
for (const cookie of createPreviewCookies(preview.token, releaseId)) {
|
|
33
|
+
response.cookies.set(cookie.name, cookie.value, cookie.options);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return response;
|
|
37
|
+
}
|
|
25
38
|
requestHeaders.set(RELEASE_HEADER, releaseId);
|
|
26
39
|
requestHeaders.set(ROUTE_HEADER, url.pathname);
|
|
27
40
|
requestHeaders.set(DISPATCH_HEADER, 'ssr-page');
|
|
@@ -38,12 +51,33 @@ export function createMiddleware(config = {}) {
|
|
|
38
51
|
return response;
|
|
39
52
|
};
|
|
40
53
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
54
|
+
function previewRewriteTarget(request, preview, config) {
|
|
55
|
+
const claims = preview.claims;
|
|
56
|
+
if (!claims?.deploymentId || !claims?.deploymentUrl || !preview.token) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const currentDeploymentId = config.deploymentId || process.env.VERCEL_DEPLOYMENT_ID || '';
|
|
60
|
+
if (!currentDeploymentId || currentDeploymentId === claims.deploymentId) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const base = buildDeploymentUrl(claims.deploymentUrl);
|
|
64
|
+
const target = new URL(request.nextUrl.pathname + request.nextUrl.search, base);
|
|
65
|
+
if (!target.searchParams.has('preview_token')) {
|
|
66
|
+
target.searchParams.set('preview_token', preview.token);
|
|
67
|
+
}
|
|
68
|
+
const bypass = config.deploymentProtectionBypass || process.env.VERCEL_DEPLOYMENT_PROTECTION_BYPASS;
|
|
69
|
+
if (bypass) {
|
|
70
|
+
target.searchParams.set('x-vercel-protection-bypass', bypass);
|
|
71
|
+
}
|
|
72
|
+
return target;
|
|
73
|
+
}
|
|
74
|
+
function buildDeploymentUrl(value) {
|
|
75
|
+
if (value.startsWith('http://') || value.startsWith('https://')) {
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
return `https://${value}`;
|
|
79
|
+
}
|
|
80
|
+
function gradialHeaders(headers) {
|
|
47
81
|
const next = new Headers(headers);
|
|
48
82
|
next.delete(RELEASE_HEADER);
|
|
49
83
|
next.delete(ROUTE_HEADER);
|
|
@@ -143,3 +177,10 @@ function clampRelease(activeReleaseId, activeSequence, compatibility) {
|
|
|
143
177
|
function edgeKeySegment(value) {
|
|
144
178
|
return value.replace(/[^A-Za-z0-9_-]/g, '_');
|
|
145
179
|
}
|
|
180
|
+
// Default matcher. _next/static is included so that cross-deployment previews
|
|
181
|
+
// can rewrite static assets to the staged deployment. _next/image is excluded
|
|
182
|
+
// because it is a generic optimization endpoint and does not carry code-specific
|
|
183
|
+
// assets in most configurations. Consumers can override this if needed.
|
|
184
|
+
export const config = {
|
|
185
|
+
matcher: ['/((?!_next/image|favicon.ico|\\.gradial-dam|api/baremetal-assets|gradial/assets).*)'],
|
|
186
|
+
};
|
package/dist/preview/core.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ export interface PreviewClaims {
|
|
|
2
2
|
scope?: string;
|
|
3
3
|
releaseId?: string;
|
|
4
4
|
siteId?: string;
|
|
5
|
+
codeDigest?: string;
|
|
6
|
+
contentSnapshotId?: string;
|
|
7
|
+
deploymentId?: string;
|
|
8
|
+
deploymentUrl?: string;
|
|
5
9
|
expiresAt?: string;
|
|
6
10
|
}
|
|
7
11
|
export interface PreviewTokenSource {
|
|
@@ -18,6 +22,7 @@ export interface PreviewContext {
|
|
|
18
22
|
isPreview: boolean;
|
|
19
23
|
releaseId?: string;
|
|
20
24
|
token?: string;
|
|
25
|
+
claims?: PreviewClaims;
|
|
21
26
|
}
|
|
22
27
|
export interface PreviewCookie {
|
|
23
28
|
name: string;
|
package/dist/preview/core.js
CHANGED
|
@@ -63,7 +63,7 @@ export async function resolvePreviewContext(options) {
|
|
|
63
63
|
if (!claims.expiresAt || Date.parse(claims.expiresAt) <= Date.now()) {
|
|
64
64
|
return { isPreview: false };
|
|
65
65
|
}
|
|
66
|
-
return { isPreview: true, releaseId: claims.releaseId, token };
|
|
66
|
+
return { isPreview: true, releaseId: claims.releaseId, token, claims };
|
|
67
67
|
}
|
|
68
68
|
export function createPreviewCookies(token, releaseId) {
|
|
69
69
|
const common = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradial/aci",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"bin/aci-*",
|
|
11
11
|
"src/cli/*.mjs",
|
|
12
12
|
"src/types/image.ts",
|
|
13
|
+
"src/types/block-ref.ts",
|
|
13
14
|
"templates/common",
|
|
14
15
|
"templates/nextjs",
|
|
15
16
|
"templates/astro",
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ComponentContract } from './component.js';
|
|
3
|
+
|
|
4
|
+
const BLOCK_REF_KEY = '_gradialBlockRef';
|
|
5
|
+
|
|
6
|
+
export interface BlockRefMeta {
|
|
7
|
+
allowed: readonly string[];
|
|
8
|
+
multiple: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const blockRefBase = z.object({
|
|
12
|
+
id: z.string().min(1),
|
|
13
|
+
component: z.string().min(1),
|
|
14
|
+
props: z.record(z.string(), z.unknown()),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
type InferContractProps<T> = T extends ComponentContract<string, infer Schema>
|
|
18
|
+
? Schema extends z.ZodType<infer P> ? P : Record<string, unknown>
|
|
19
|
+
: Record<string, unknown>;
|
|
20
|
+
|
|
21
|
+
type InferContractId<T> = T extends ComponentContract<infer Id, unknown> ? Id : never;
|
|
22
|
+
|
|
23
|
+
export type BlockRefEntry<T extends ComponentContract> = {
|
|
24
|
+
id: string;
|
|
25
|
+
component: InferContractId<T>;
|
|
26
|
+
props: InferContractProps<T>;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type BlockRefUnion<T extends readonly ComponentContract[]> = {
|
|
30
|
+
[K in keyof T]: BlockRefEntry<T[K]>;
|
|
31
|
+
}[number];
|
|
32
|
+
|
|
33
|
+
function attachMeta<T extends z.ZodTypeAny>(
|
|
34
|
+
schema: T,
|
|
35
|
+
allowed: readonly string[],
|
|
36
|
+
multiple: boolean,
|
|
37
|
+
): T {
|
|
38
|
+
Object.defineProperty(schema, BLOCK_REF_KEY, {
|
|
39
|
+
value: { allowed, multiple } satisfies BlockRefMeta,
|
|
40
|
+
enumerable: false,
|
|
41
|
+
writable: false,
|
|
42
|
+
configurable: false,
|
|
43
|
+
});
|
|
44
|
+
return schema;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function blockRefArray<const T extends readonly ComponentContract[]>(
|
|
48
|
+
contracts: T,
|
|
49
|
+
): z.ZodArray<z.ZodType<BlockRefUnion<T>>> {
|
|
50
|
+
return attachMeta(
|
|
51
|
+
z.array(blockRefBase) as unknown as z.ZodArray<z.ZodType<BlockRefUnion<T>>>,
|
|
52
|
+
contracts.map((c) => c.id),
|
|
53
|
+
true,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function blockRef<const T extends readonly ComponentContract[]>(
|
|
58
|
+
contracts: T,
|
|
59
|
+
): z.ZodType<BlockRefUnion<T>> {
|
|
60
|
+
return attachMeta(
|
|
61
|
+
blockRefBase as unknown as z.ZodType<BlockRefUnion<T>>,
|
|
62
|
+
contracts.map((c) => c.id),
|
|
63
|
+
false,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getBlockRefMeta(schema: unknown): BlockRefMeta | null {
|
|
68
|
+
if (schema && typeof schema === 'object' && BLOCK_REF_KEY in schema) {
|
|
69
|
+
return (schema as Record<string, unknown>)[BLOCK_REF_KEY] as BlockRefMeta;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type BlockRef = z.infer<typeof blockRefBase>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createGradialMiddleware } from '@gradial/aci/next/middleware';
|
|
2
2
|
|
|
3
|
-
export default
|
|
3
|
+
export default createGradialMiddleware();
|
|
4
4
|
export { config } from '@gradial/aci/next/middleware';
|