@gradial/aci 0.1.20-preview.2 → 0.1.20-preview.3
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 +8 -10
- package/bin/aci +0 -0
- package/dist/astro/index.d.ts +2 -0
- package/dist/astro/index.js +4 -0
- package/dist/content/index.d.ts +1 -0
- package/dist/content/index.js +1 -0
- package/dist/content/provider.d.ts +19 -0
- package/dist/content/provider.js +7 -0
- package/dist/content/resolve-slots.d.ts +9 -0
- package/dist/content/resolve-slots.js +24 -0
- package/dist/content/types.d.ts +1 -1
- package/dist/content/validation.js +0 -1
- package/dist/define-component.js +0 -6
- package/dist/define-layout.d.ts +2 -0
- package/dist/define-layout.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/next/asset-route.d.ts +2 -0
- package/dist/next/asset-route.js +10 -2
- package/dist/next/config.js +1 -1
- package/dist/next/middleware.d.ts +6 -2
- package/dist/next/middleware.js +7 -2
- package/dist/next/page.d.ts +16 -15
- package/dist/next/page.js +27 -9
- package/dist/next/render.d.ts +5 -0
- package/dist/next/render.js +15 -0
- package/dist/next/root-layout.d.ts +4 -0
- package/dist/next/root-layout.js +5 -0
- package/dist/next/server.d.ts +3 -2
- package/dist/next/server.js +12 -2
- package/dist/providers/file.d.ts +2 -0
- package/dist/providers/file.js +61 -1
- package/dist/providers/s3.d.ts +1 -0
- package/dist/providers/s3.js +14 -1
- package/dist/registry.d.ts +15 -0
- package/dist/registry.js +10 -0
- package/dist/sveltekit/index.d.ts +2 -0
- package/dist/sveltekit/index.js +4 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.js +15 -2
- package/dist/types/component.d.ts +0 -9
- package/dist/types/config.d.ts +0 -1
- package/dist/types/page.d.ts +1 -2
- package/dist/types/render-mode.d.ts +0 -1
- package/package.json +19 -13
- package/src/cli/compile-registry.mjs +132 -12
- package/src/types/component.ts +0 -10
- package/src/types/config.ts +0 -1
- package/src/types/page.ts +1 -2
- package/src/types/render-mode.ts +0 -8
- package/dist/content/cache.test.d.ts +0 -1
- package/dist/content/cache.test.js +0 -86
- package/dist/next/preview-banner.d.ts +0 -4
- package/dist/next/preview-banner.js +0 -51
- package/dist/next/preview.d.ts +0 -7
- package/dist/next/preview.js +0 -37
- package/src/cli/verify-renderer.mjs +0 -73
package/README.md
CHANGED
|
@@ -10,9 +10,9 @@ content provider helpers, and framework-specific subpaths from one package.
|
|
|
10
10
|
The fastest way to create a new ACI site is via the scaffolding script:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
|
|
13
|
+
pnpm exec tsx scripts/create-site.ts --framework astro --name my-site --dir ./my-site
|
|
14
14
|
cd my-site
|
|
15
|
-
|
|
15
|
+
pnpm run dev
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
Available frameworks: `astro`, `nextjs`, `sveltekit`.
|
|
@@ -28,8 +28,8 @@ See the [templates directory](templates/) for the canonical starter source.
|
|
|
28
28
|
## Installation
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
pnpm add @gradial/aci zod@^4
|
|
32
|
+
pnpm add -D typescript tsx
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
Common imports:
|
|
@@ -78,7 +78,6 @@ export const heroContract = defineComponentContract({
|
|
|
78
78
|
headline: z.string(),
|
|
79
79
|
image: GradialImageSchema,
|
|
80
80
|
}),
|
|
81
|
-
renderModes: { canStatic: true, canSSR: true, canClientIsland: false },
|
|
82
81
|
});
|
|
83
82
|
|
|
84
83
|
export type HeroProps = z.infer<typeof heroContract.schema>;
|
|
@@ -107,7 +106,6 @@ const content = new FixtureContentProvider({
|
|
|
107
106
|
$type: 'page',
|
|
108
107
|
status: 'published',
|
|
109
108
|
layout: 'marketing',
|
|
110
|
-
renderMode: 'static',
|
|
111
109
|
metadata: { title: 'Test Home' },
|
|
112
110
|
regions: { main: [] }
|
|
113
111
|
}
|
|
@@ -123,7 +121,7 @@ Use this in unit tests when you want the same provider API as
|
|
|
123
121
|
Publish from this package directory:
|
|
124
122
|
|
|
125
123
|
```bash
|
|
126
|
-
|
|
124
|
+
pnpm publish --access public
|
|
127
125
|
```
|
|
128
126
|
|
|
129
127
|
The package includes a `prepack` script that builds `dist` before packing or
|
|
@@ -131,15 +129,15 @@ publishing.
|
|
|
131
129
|
|
|
132
130
|
## Verify the Published Shape Locally
|
|
133
131
|
|
|
134
|
-
Before publishing, inspect the exact tarball
|
|
132
|
+
Before publishing, inspect the exact tarball pnpm will upload. This catches
|
|
135
133
|
package `files` mistakes where local source imports work from the repo but fail
|
|
136
|
-
after
|
|
134
|
+
after pnpm filters the packed files.
|
|
137
135
|
|
|
138
136
|
From `packages/sdk`:
|
|
139
137
|
|
|
140
138
|
```bash
|
|
141
139
|
tmpdir="$(mktemp -d /tmp/gradial-aci-pack.XXXXXX)"
|
|
142
|
-
filename="$(
|
|
140
|
+
filename="$(pnpm pack --pack-destination "$tmpdir" | tail -n 1)"
|
|
143
141
|
tar -tzf "$tmpdir/$filename" | sort | rg '^(package/src/cli/|package/src/types/|package/dist/types/)'
|
|
144
142
|
echo "tarball=$tmpdir/$filename"
|
|
145
143
|
```
|
package/bin/aci
CHANGED
|
Binary file
|
package/dist/astro/index.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export declare function getGradialStaticPaths(): Promise<Array<{
|
|
|
33
33
|
};
|
|
34
34
|
}>>;
|
|
35
35
|
export declare function getPageRuntimeRenderInput(request: Request): RenderInput | null;
|
|
36
|
+
export { createRegistry, registryLookup } from '../registry.js';
|
|
37
|
+
export type { Registry, RegistryEntry, AnyComponent } from '../registry.js';
|
|
36
38
|
export { getPreviewMode, createPreviewBannerHTML, createPreviewCookies, type PreviewContext, type PreviewCookie, } from './preview.js';
|
|
37
39
|
export interface GradialAstroOptions extends GradialContentWatchOptions {
|
|
38
40
|
/** Path to compiled content root. Defaults to ACI_CONTENT_ROOT env or '.aci/compiled' */
|
package/dist/astro/index.js
CHANGED
|
@@ -32,6 +32,10 @@ export function getPageRuntimeRenderInput(request) {
|
|
|
32
32
|
return getPendingRenderInputFromHeaders(request.headers);
|
|
33
33
|
}
|
|
34
34
|
// ---------------------------------------------------------------------------
|
|
35
|
+
// Registry helpers
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
export { createRegistry, registryLookup } from '../registry.js';
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
35
39
|
// Preview support
|
|
36
40
|
// ---------------------------------------------------------------------------
|
|
37
41
|
export { getPreviewMode, createPreviewBannerHTML, createPreviewCookies, } from './preview.js';
|
package/dist/content/index.d.ts
CHANGED
package/dist/content/index.js
CHANGED
|
@@ -12,17 +12,33 @@ export interface FragmentEntry {
|
|
|
12
12
|
digest: string;
|
|
13
13
|
fragmentRefs?: string[];
|
|
14
14
|
}
|
|
15
|
+
export interface LayoutEntry {
|
|
16
|
+
name: string;
|
|
17
|
+
slots: LayoutSlot[];
|
|
18
|
+
defaults?: Record<string, FragmentRef[]>;
|
|
19
|
+
}
|
|
20
|
+
export interface LayoutSlot {
|
|
21
|
+
name: string;
|
|
22
|
+
required: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface FragmentRef {
|
|
25
|
+
kind: string;
|
|
26
|
+
fragmentId: string;
|
|
27
|
+
inline: boolean;
|
|
28
|
+
}
|
|
15
29
|
export interface CompiledManifest {
|
|
16
30
|
manifestVersion: string;
|
|
17
31
|
buildDigest: string;
|
|
18
32
|
siteConfigRef: string;
|
|
19
33
|
routes: Record<string, RouteEntry>;
|
|
20
34
|
fragments: Record<string, FragmentEntry>;
|
|
35
|
+
layouts?: Record<string, LayoutEntry>;
|
|
21
36
|
}
|
|
22
37
|
export interface ContentProvider {
|
|
23
38
|
getSiteConfig<T = unknown>(): Promise<T>;
|
|
24
39
|
getPage<T = unknown>(route: string): Promise<T>;
|
|
25
40
|
getFragment<T = unknown>(id: string): Promise<T>;
|
|
41
|
+
getLayout<T = unknown>(name: string): Promise<T>;
|
|
26
42
|
listRoutes(): Promise<RouteEntry[]>;
|
|
27
43
|
}
|
|
28
44
|
export declare class PageNotFoundError extends Error {
|
|
@@ -31,6 +47,9 @@ export declare class PageNotFoundError extends Error {
|
|
|
31
47
|
export declare class FragmentNotFoundError extends Error {
|
|
32
48
|
constructor(id: string, cause?: unknown);
|
|
33
49
|
}
|
|
50
|
+
export declare class LayoutNotFoundError extends Error {
|
|
51
|
+
constructor(name: string, cause?: unknown);
|
|
52
|
+
}
|
|
34
53
|
export interface RenderInputOptions {
|
|
35
54
|
domain?: string;
|
|
36
55
|
locale?: string;
|
package/dist/content/provider.js
CHANGED
|
@@ -16,6 +16,13 @@ export class FragmentNotFoundError extends Error {
|
|
|
16
16
|
this.cause = cause;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
export class LayoutNotFoundError extends Error {
|
|
20
|
+
constructor(name, cause) {
|
|
21
|
+
super(`Layout not found: ${name}`);
|
|
22
|
+
this.name = 'LayoutNotFoundError';
|
|
23
|
+
this.cause = cause;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
19
26
|
import { getCachedSiteConfig } from './cache.js';
|
|
20
27
|
export async function loadRenderInput(provider, route = '/', options = {}) {
|
|
21
28
|
const normalized = normalizeRoute(route);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LayoutEntry } from './provider.js';
|
|
2
|
+
import type { KernelPageBlock } from './types.js';
|
|
3
|
+
export interface FragmentData {
|
|
4
|
+
id: string;
|
|
5
|
+
component: string;
|
|
6
|
+
props?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export type FragmentLoader = (id: string) => Promise<FragmentData>;
|
|
9
|
+
export declare function resolveSlots(layout: LayoutEntry, pageRegions: Record<string, KernelPageBlock[]>, getFragment: FragmentLoader): Promise<Record<string, KernelPageBlock[]>>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export async function resolveSlots(layout, pageRegions, getFragment) {
|
|
2
|
+
const resolved = {};
|
|
3
|
+
for (const slot of layout.slots) {
|
|
4
|
+
const pageBlocks = pageRegions[slot.name];
|
|
5
|
+
if (pageBlocks && pageBlocks.length > 0) {
|
|
6
|
+
resolved[slot.name] = pageBlocks;
|
|
7
|
+
continue;
|
|
8
|
+
}
|
|
9
|
+
const defaults = layout.defaults?.[slot.name];
|
|
10
|
+
if (defaults?.length) {
|
|
11
|
+
const blocks = [];
|
|
12
|
+
for (const ref of defaults) {
|
|
13
|
+
const fragment = await getFragment(ref.fragmentId);
|
|
14
|
+
blocks.push({
|
|
15
|
+
id: fragment.id,
|
|
16
|
+
component: fragment.component,
|
|
17
|
+
props: fragment.props || {},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
resolved[slot.name] = blocks;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return resolved;
|
|
24
|
+
}
|
package/dist/content/types.d.ts
CHANGED
|
@@ -55,7 +55,6 @@ export interface KernelPage {
|
|
|
55
55
|
$type: 'page';
|
|
56
56
|
status: string;
|
|
57
57
|
layout: string;
|
|
58
|
-
renderMode: string;
|
|
59
58
|
metadata: KernelPageSeo;
|
|
60
59
|
regions: Record<string, KernelPageBlock[]>;
|
|
61
60
|
}
|
|
@@ -93,6 +92,7 @@ export interface RenderInput<TPage extends KernelPage = KernelPage, TSiteConfig
|
|
|
93
92
|
siteConfig: TSiteConfig;
|
|
94
93
|
page: TPage | null;
|
|
95
94
|
fragments?: Record<string, unknown>;
|
|
95
|
+
layouts?: Record<string, unknown>;
|
|
96
96
|
}
|
|
97
97
|
export interface RenderOutput {
|
|
98
98
|
route: string;
|
|
@@ -121,7 +121,6 @@ export function createContentSchemas(contracts = {}) {
|
|
|
121
121
|
$type: z.literal('page'),
|
|
122
122
|
status: z.string().min(1),
|
|
123
123
|
layout: z.string().min(1),
|
|
124
|
-
renderMode: z.string().min(1),
|
|
125
124
|
metadata: z.looseObject({
|
|
126
125
|
title: z.string().optional(),
|
|
127
126
|
description: z.string().optional(),
|
package/dist/define-component.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
export function defineComponent(definition) {
|
|
2
|
-
const renderModes = definition.renderModes ?? definition.render;
|
|
3
|
-
if (!renderModes) {
|
|
4
|
-
throw new Error(`component ${definition.name} must define renderModes`);
|
|
5
|
-
}
|
|
6
2
|
return {
|
|
7
3
|
...definition,
|
|
8
|
-
renderModes,
|
|
9
|
-
render: renderModes,
|
|
10
4
|
varyDimensions: definition.varyDimensions ?? definition.vary,
|
|
11
5
|
vary: definition.vary ?? definition.varyDimensions,
|
|
12
6
|
};
|
package/dist/define-layout.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { LayoutContract, LayoutDefinition, LayoutSlot } from './types/layout.js';
|
|
2
|
+
import type { FragmentRefNode } from './types/page.js';
|
|
2
3
|
export declare function defineLayout(definition: LayoutDefinition): LayoutContract;
|
|
3
4
|
export declare const defineLayoutContract: typeof defineLayout;
|
|
4
5
|
export declare function slot(name: string, required?: boolean): LayoutSlot;
|
|
6
|
+
export declare function fragmentDefault(fragmentId: string): FragmentRefNode;
|
package/dist/define-layout.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export * from './block-ref.js';
|
|
|
2
2
|
export * from './define-component.js';
|
|
3
3
|
export * from './define-data-model.js';
|
|
4
4
|
export * from './define-layout.js';
|
|
5
|
+
export * from './registry.js';
|
|
5
6
|
export * from './types/index.js';
|
|
6
7
|
// Note: content/ is NOT re-exported here because it imports node:path,
|
|
7
8
|
// making it incompatible with client-side bundlers. Import server-only
|
|
@@ -5,5 +5,7 @@ type AssetRouteParams = {
|
|
|
5
5
|
releaseId?: string;
|
|
6
6
|
path?: string[];
|
|
7
7
|
};
|
|
8
|
+
export declare const runtime = "nodejs";
|
|
9
|
+
export declare const dynamic = "force-dynamic";
|
|
8
10
|
export declare function GET(_request: Request, { params }: AssetRouteContext): Promise<Response>;
|
|
9
11
|
export {};
|
package/dist/next/asset-route.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
import { FileContentProvider } from '../providers/file.js';
|
|
1
2
|
import { getReleaseAssetResponse } from './server.js';
|
|
3
|
+
const LOCAL_RELEASE_ID = 'local';
|
|
4
|
+
export const runtime = 'nodejs';
|
|
5
|
+
export const dynamic = 'force-dynamic';
|
|
2
6
|
export async function GET(_request, { params }) {
|
|
3
7
|
const resolvedParams = await params;
|
|
4
8
|
const releaseId = resolvedParams.releaseId || '';
|
|
5
|
-
const assetPath = (resolvedParams.path || []).join('/');
|
|
6
|
-
if (!releaseId ||
|
|
9
|
+
const assetPath = '/' + (resolvedParams.path || []).join('/');
|
|
10
|
+
if (!releaseId || assetPath === '/') {
|
|
7
11
|
return new Response('asset not found', { status: 404 });
|
|
8
12
|
}
|
|
13
|
+
if (releaseId === LOCAL_RELEASE_ID) {
|
|
14
|
+
const provider = new FileContentProvider();
|
|
15
|
+
return provider.fetchRaw(assetPath);
|
|
16
|
+
}
|
|
9
17
|
try {
|
|
10
18
|
return await getReleaseAssetResponse(releaseId, assetPath);
|
|
11
19
|
}
|
package/dist/next/config.js
CHANGED
|
@@ -9,7 +9,7 @@ export function withGradialAci(nextConfig = {}, options = {}) {
|
|
|
9
9
|
const gradialRewrites = [
|
|
10
10
|
{
|
|
11
11
|
source: `${assetPrefix}/:path*`,
|
|
12
|
-
destination: `/
|
|
12
|
+
destination: `/gradial/assets/local${assetPrefix}/:path*`,
|
|
13
13
|
},
|
|
14
14
|
];
|
|
15
15
|
if (!userRewrites)
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { NextResponse, type NextRequest } from 'next/server.js';
|
|
2
2
|
export interface GradialMiddlewareConfig {
|
|
3
|
-
siteId
|
|
3
|
+
siteId?: string;
|
|
4
4
|
edgeConfig?: string;
|
|
5
|
+
previewSignKey?: string;
|
|
5
6
|
deploymentId?: string;
|
|
6
7
|
}
|
|
7
|
-
export declare function createGradialMiddleware(config
|
|
8
|
+
export declare function createGradialMiddleware(config?: GradialMiddlewareConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
9
|
+
export declare const config: {
|
|
10
|
+
matcher: string[];
|
|
11
|
+
};
|
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 createGradialMiddleware(config) {
|
|
6
|
+
export function createGradialMiddleware(config = {}) {
|
|
7
7
|
return async function gradialMiddleware(request) {
|
|
8
8
|
const url = request.nextUrl;
|
|
9
9
|
// ?leave_preview=1 → clear cookies, redirect to clean URL
|
|
@@ -38,6 +38,11 @@ export function createGradialMiddleware(config) {
|
|
|
38
38
|
return response;
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
export const config = {
|
|
42
|
+
matcher: [
|
|
43
|
+
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
|
|
44
|
+
],
|
|
45
|
+
};
|
|
41
46
|
function gradialHeaders(headers) {
|
|
42
47
|
const next = new Headers(headers);
|
|
43
48
|
next.delete(RELEASE_HEADER);
|
|
@@ -59,7 +64,7 @@ async function resolvePreviewFromRequest(request, config) {
|
|
|
59
64
|
console.warn('[gradial/mw] GRADIAL_PREVIEW_SIGN_KEY not set — preview tokens will be ignored');
|
|
60
65
|
return null;
|
|
61
66
|
}
|
|
62
|
-
return await resolvePreviewContext({ token, siteId: config.siteId, signKey });
|
|
67
|
+
return await resolvePreviewContext({ token, siteId: config.siteId || '', signKey });
|
|
63
68
|
}
|
|
64
69
|
function cookieMap(cookies) {
|
|
65
70
|
const map = new Map();
|
package/dist/next/page.d.ts
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
|
-
import type { ComponentType } from 'react';
|
|
2
1
|
import type { Metadata } from 'next';
|
|
3
|
-
import { type KernelPage
|
|
4
|
-
|
|
5
|
-
page: KernelPage;
|
|
6
|
-
siteConfig: KernelSiteConfig;
|
|
7
|
-
}
|
|
8
|
-
export interface CreateAciPageOptions {
|
|
9
|
-
/** The site's CmsPage component that renders chrome + content blocks. */
|
|
10
|
-
CmsPage: ComponentType<AciPageProps>;
|
|
11
|
-
}
|
|
2
|
+
import { type KernelPage } from '../content/index.js';
|
|
3
|
+
import type { Registry } from '../registry.js';
|
|
12
4
|
type NextPageProps = {
|
|
13
5
|
params: Promise<{
|
|
14
6
|
slug?: string[];
|
|
15
7
|
}>;
|
|
16
8
|
};
|
|
9
|
+
export type SlotPropsFn = (slotName: string, page: KernelPage) => Record<string, unknown> | undefined;
|
|
10
|
+
export interface CreatePageOptions {
|
|
11
|
+
slotProps?: SlotPropsFn;
|
|
12
|
+
}
|
|
17
13
|
/**
|
|
18
14
|
* Creates the catch-all route handler for a Next.js ACI site.
|
|
19
15
|
*
|
|
20
16
|
* Usage in `src/app/[[...slug]]/page.tsx`:
|
|
21
17
|
*
|
|
22
18
|
* ```tsx
|
|
23
|
-
* import {
|
|
24
|
-
* import {
|
|
19
|
+
* import { createPage } from '@gradial/aci/next/page';
|
|
20
|
+
* import { registry } from '@/cms/registry';
|
|
25
21
|
*
|
|
26
|
-
* const page =
|
|
22
|
+
* const page = createPage(registry);
|
|
27
23
|
* export const dynamic = page.dynamic;
|
|
28
24
|
* export const generateMetadata = page.generateMetadata;
|
|
29
25
|
* export default page.default;
|
|
30
26
|
* ```
|
|
31
27
|
*
|
|
32
28
|
* The SDK owns param normalization, content loading, metadata generation,
|
|
33
|
-
*
|
|
29
|
+
* notFound handling, layout resolution, and block rendering via the registry.
|
|
30
|
+
*
|
|
31
|
+
* Layout slots: createPage loads the page's layout, iterates all slots, and
|
|
32
|
+
* fills each from the page's regions. If a slot is empty, it falls back to
|
|
33
|
+
* the layout's fragment defaults (loaded via getFragment). This lets chrome
|
|
34
|
+
* be CMS-managed fragments instead of hardcoded component imports.
|
|
34
35
|
*/
|
|
35
|
-
export declare function
|
|
36
|
+
export declare function createPage(registry: Registry, options?: CreatePageOptions): {
|
|
36
37
|
dynamic: "force-dynamic";
|
|
37
38
|
generateMetadata: ({ params }: NextPageProps) => Promise<Metadata>;
|
|
38
39
|
default: ({ params }: NextPageProps) => Promise<import("react").JSX.Element>;
|
package/dist/next/page.js
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { notFound } from 'next/navigation.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { Fragment } from 'react';
|
|
4
|
+
import { PageNotFoundError, routeFromNextParams, getRenderInput, getLayout, getFragment, } from './server.js';
|
|
5
|
+
import { routeMetadataForContent, resolveSlots, } from '../content/index.js';
|
|
6
|
+
import { renderBlocks } from './render.js';
|
|
5
7
|
/**
|
|
6
8
|
* Creates the catch-all route handler for a Next.js ACI site.
|
|
7
9
|
*
|
|
8
10
|
* Usage in `src/app/[[...slug]]/page.tsx`:
|
|
9
11
|
*
|
|
10
12
|
* ```tsx
|
|
11
|
-
* import {
|
|
12
|
-
* import {
|
|
13
|
+
* import { createPage } from '@gradial/aci/next/page';
|
|
14
|
+
* import { registry } from '@/cms/registry';
|
|
13
15
|
*
|
|
14
|
-
* const page =
|
|
16
|
+
* const page = createPage(registry);
|
|
15
17
|
* export const dynamic = page.dynamic;
|
|
16
18
|
* export const generateMetadata = page.generateMetadata;
|
|
17
19
|
* export default page.default;
|
|
18
20
|
* ```
|
|
19
21
|
*
|
|
20
22
|
* The SDK owns param normalization, content loading, metadata generation,
|
|
21
|
-
*
|
|
23
|
+
* notFound handling, layout resolution, and block rendering via the registry.
|
|
24
|
+
*
|
|
25
|
+
* Layout slots: createPage loads the page's layout, iterates all slots, and
|
|
26
|
+
* fills each from the page's regions. If a slot is empty, it falls back to
|
|
27
|
+
* the layout's fragment defaults (loaded via getFragment). This lets chrome
|
|
28
|
+
* be CMS-managed fragments instead of hardcoded component imports.
|
|
22
29
|
*/
|
|
23
|
-
export function
|
|
30
|
+
export function createPage(registry, options) {
|
|
24
31
|
const dynamic = 'force-dynamic';
|
|
25
32
|
async function generateMetadata({ params }) {
|
|
26
33
|
const route = await routeFromNextParams(params);
|
|
@@ -43,7 +50,18 @@ export function createAciPage({ CmsPage }) {
|
|
|
43
50
|
const input = await getRenderInput(route);
|
|
44
51
|
if (!input.page || input.page.status !== 'published')
|
|
45
52
|
notFound();
|
|
46
|
-
|
|
53
|
+
const layout = await getLayout(input.page.layout);
|
|
54
|
+
const resolvedSlots = await resolveSlots(layout, input.page.regions || {}, (id) => getFragment(id));
|
|
55
|
+
return (_jsx(_Fragment, { children: layout.slots.map((slot) => {
|
|
56
|
+
const blocks = resolvedSlots[slot.name];
|
|
57
|
+
if (!blocks?.length)
|
|
58
|
+
return null;
|
|
59
|
+
const extraProps = options?.slotProps?.(slot.name, input.page);
|
|
60
|
+
if (slot.name === 'main') {
|
|
61
|
+
return (_jsx("main", { id: "content-main", children: renderBlocks(blocks, registry, extraProps) }, "main"));
|
|
62
|
+
}
|
|
63
|
+
return (_jsx(Fragment, { children: renderBlocks(blocks, registry, extraProps) }, slot.name));
|
|
64
|
+
}) }));
|
|
47
65
|
}
|
|
48
66
|
catch (error) {
|
|
49
67
|
if (error instanceof PageNotFoundError)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { Registry } from '../registry.js';
|
|
3
|
+
import type { KernelPageBlock } from '../content/types.js';
|
|
4
|
+
export declare function renderBlock(block: KernelPageBlock, registry: Registry, extraProps?: Record<string, unknown>): ReactElement | null;
|
|
5
|
+
export declare function renderBlocks(blocks: readonly KernelPageBlock[], registry: Registry, extraProps?: Record<string, unknown>): (ReactElement | null)[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function renderBlock(block, registry, extraProps) {
|
|
3
|
+
const entry = registry.find((r) => r.name === block.component);
|
|
4
|
+
if (!entry) {
|
|
5
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
6
|
+
return (_jsxs("div", { className: "mx-auto my-4 max-w-[var(--max-width-container)] rounded-card border-2 border-red-600 bg-red-50 px-5 py-4 font-mono text-sm leading-relaxed text-red-900", children: [_jsx("strong", { children: "Unknown block:" }), " ", _jsx("code", { children: block.component }), _jsx("br", {}), _jsxs("span", { className: "text-red-800 opacity-70", children: ["id: ", block.id] })] }, block.id));
|
|
7
|
+
}
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const Component = entry.component;
|
|
11
|
+
return _jsx(Component, { ...block.props, ...extraProps }, block.id);
|
|
12
|
+
}
|
|
13
|
+
export function renderBlocks(blocks, registry, extraProps) {
|
|
14
|
+
return blocks.map((block) => renderBlock(block, registry, extraProps));
|
|
15
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DevRefresh } from './dev-refresh.js';
|
|
3
|
+
export function RootLayout({ children }) {
|
|
4
|
+
return (_jsx("html", { lang: "en", children: _jsxs("body", { children: [children, _jsx(DevRefresh, {})] }) }));
|
|
5
|
+
}
|
package/dist/next/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FragmentNotFoundError, PageNotFoundError } from '../content/provider.js';
|
|
1
|
+
import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError } from '../content/provider.js';
|
|
2
2
|
import type { RenderInput } from '../content/types.js';
|
|
3
|
-
export { FragmentNotFoundError, PageNotFoundError };
|
|
3
|
+
export { FragmentNotFoundError, PageNotFoundError, LayoutNotFoundError };
|
|
4
4
|
export interface GradialFetchConfig {
|
|
5
5
|
siteId?: string;
|
|
6
6
|
bucket?: string;
|
|
@@ -29,6 +29,7 @@ export interface GradialSiteConfig {
|
|
|
29
29
|
export declare function getPage<T = unknown>(route?: string, config?: GradialFetchConfig): Promise<T>;
|
|
30
30
|
export declare function getSiteConfig<T extends GradialSiteConfig = GradialSiteConfig>(config?: GradialFetchConfig): Promise<T>;
|
|
31
31
|
export declare function getFragment<T = unknown>(name: string, config?: GradialFetchConfig): Promise<T>;
|
|
32
|
+
export declare function getLayout<T = unknown>(name: string, config?: GradialFetchConfig): Promise<T>;
|
|
32
33
|
export declare function getRoutes(config?: GradialFetchConfig): Promise<string[]>;
|
|
33
34
|
export declare function getRenderInput<TPage, TSite extends GradialSiteConfig = GradialSiteConfig>(route?: string, config?: GradialFetchConfig): Promise<GradialRenderInput<TPage, TSite>>;
|
|
34
35
|
export declare function getPageRuntimeRenderInput(): Promise<RenderInput | null>;
|
package/dist/next/server.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { headers } from 'next/headers.js';
|
|
2
|
-
import { FragmentNotFoundError, PageNotFoundError, } from '../content/provider.js';
|
|
2
|
+
import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError, } from '../content/provider.js';
|
|
3
3
|
import { getCachedManifest, getCachedSiteConfig } from '../content/cache.js';
|
|
4
4
|
import { S3ContentProvider } from '../providers/s3.js';
|
|
5
5
|
import { GRADIAL_RENDER_INPUT_HEADER, getPendingRenderInput } from '../runtime/page.js';
|
|
6
6
|
import { getUncachedEdgeConfigValue } from './edge-config.js';
|
|
7
7
|
const RELEASE_HEADER = 'x-gradial-release-id';
|
|
8
8
|
const PREVIEW_HEADER = 'x-gradial-preview';
|
|
9
|
-
export { FragmentNotFoundError, PageNotFoundError };
|
|
9
|
+
export { FragmentNotFoundError, PageNotFoundError, LayoutNotFoundError };
|
|
10
10
|
export async function getPage(route = '', config = {}) {
|
|
11
11
|
const runtimeInput = await getPageRuntimeRenderInput();
|
|
12
12
|
if (runtimeInput)
|
|
@@ -31,6 +31,16 @@ export async function getFragment(name, config = {}) {
|
|
|
31
31
|
const provider = await resolveProviderWithFallback(config);
|
|
32
32
|
return await provider.getFragment(name);
|
|
33
33
|
}
|
|
34
|
+
export async function getLayout(name, config = {}) {
|
|
35
|
+
const runtimeInput = await getPageRuntimeRenderInput();
|
|
36
|
+
if (runtimeInput) {
|
|
37
|
+
const layouts = runtimeInput.layouts || {};
|
|
38
|
+
if (name in layouts)
|
|
39
|
+
return layouts[name];
|
|
40
|
+
}
|
|
41
|
+
const provider = await resolveProviderWithFallback(config);
|
|
42
|
+
return await provider.getLayout(name);
|
|
43
|
+
}
|
|
34
44
|
export async function getRoutes(config = {}) {
|
|
35
45
|
const provider = await resolveProviderWithFallback(config);
|
|
36
46
|
const routes = await provider.listRoutes();
|
package/dist/providers/file.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export declare class FileContentProvider implements ContentProvider {
|
|
|
7
7
|
getSiteConfig<T = unknown>(): Promise<T>;
|
|
8
8
|
getPage<T = unknown>(route: string): Promise<T>;
|
|
9
9
|
getFragment<T = unknown>(id: string): Promise<T>;
|
|
10
|
+
getLayout<T = unknown>(name: string): Promise<T>;
|
|
10
11
|
manifest(): Promise<CompiledManifest>;
|
|
12
|
+
fetchRaw(key: string): Promise<Response>;
|
|
11
13
|
private readJSON;
|
|
12
14
|
}
|