@gradial/aci 0.1.52-rc.2 → 0.1.52-rc.22
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 +34 -4
- package/dist/astro/preview.js +2 -17
- package/dist/audience-data-models.d.ts +289 -16
- package/dist/audience-data-models.js +99 -2
- package/dist/cli/compile-registry.d.mts +5 -1
- package/dist/cli/compile-registry.mjs +25 -10
- package/dist/cli/mintlify-render.d.mts +1 -0
- package/dist/cli/mintlify-render.mjs +8 -0
- package/dist/cli/validate-content.d.mts +31 -1
- package/dist/cli/validate-content.mjs +50 -12
- package/dist/collect/aci.js +1 -1
- package/dist/collect/aci.js.sha256 +1 -1
- package/dist/collect/api.d.ts +3 -7
- package/dist/collect/api.js +2 -8
- package/dist/collect/batcher.d.ts +6 -5
- package/dist/collect/batcher.js +6 -5
- package/dist/collect/index.js +1 -1
- package/dist/content/provider.d.ts +1 -0
- package/dist/content/resolve-slots.d.ts +9 -2
- package/dist/content/resolve-slots.js +5 -3
- package/dist/content/types.d.ts +2 -0
- package/dist/edge/expression-capabilities.v1.d.ts +106 -0
- package/dist/edge/expression-capabilities.v1.js +165 -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/identity.js +1 -1
- package/dist/internal/domain/edge/index.d.ts +2 -0
- package/dist/internal/domain/edge/index.js +2 -0
- package/dist/internal/domain/edge/mimic_token.d.ts +13 -0
- package/dist/internal/domain/edge/mimic_token.js +58 -0
- package/dist/internal/domain/edge/personalization-core.d.ts +69 -5
- package/dist/internal/domain/edge/personalization-core.js +693 -58
- package/dist/internal/domain/edge/record/codec.d.ts +5 -2
- package/dist/internal/domain/edge/record/codec.js +85 -12
- package/dist/internal/domain/edge/resolve.js +38 -18
- package/dist/internal/domain/edge/testdata/flat_only_validator_v1.d.ts +10 -0
- package/dist/internal/domain/edge/testdata/flat_only_validator_v1.js +253 -0
- package/dist/internal/domain/edge/types.d.ts +52 -9
- package/dist/internal/domain/edge/types.js +3 -3
- package/dist/mimic/core.d.ts +2 -0
- package/dist/mimic/core.js +31 -0
- package/dist/mintlify/blocks.d.ts +6 -0
- package/dist/mintlify/blocks.js +198 -0
- package/dist/mintlify/config.d.ts +3 -0
- package/dist/mintlify/config.js +77 -0
- package/dist/mintlify/index.d.ts +3 -0
- package/dist/mintlify/index.js +2 -0
- package/dist/mintlify/json.d.ts +8 -0
- package/dist/mintlify/json.js +37 -0
- package/dist/mintlify/materialize.d.ts +12 -0
- package/dist/mintlify/materialize.js +159 -0
- package/dist/mintlify/types.d.ts +69 -0
- package/dist/mintlify/types.js +1 -0
- package/dist/mintlify/validate.d.ts +7 -0
- package/dist/mintlify/validate.js +252 -0
- package/dist/next/error-seat-payload.js +1 -1
- package/dist/next/hosted-page-target.d.ts +7 -0
- package/dist/next/hosted-page-target.js +39 -0
- package/dist/next/index.d.ts +0 -1
- package/dist/next/index.js +0 -1
- package/dist/next/page.d.ts +29 -6
- package/dist/next/page.js +47 -25
- package/dist/next/server.js +28 -1
- package/dist/next/vercel-edge-projection-v1.d.ts +0 -9
- package/dist/next/vercel-edge-projection-v1.js +9 -539
- package/dist/preview/core.d.ts +1 -0
- package/dist/preview/core.js +27 -0
- package/dist/providers/s3.js +4 -3
- package/dist/react/VideoPlayer.d.ts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/types/block-ref.d.ts +2 -0
- package/dist/types/block-ref.js +1 -0
- package/package.json +20 -7
- package/src/cli/compile-registry.mjs +24 -10
- package/src/cli/mintlify-render.mjs +9 -0
- package/src/cli/validate-content.mjs +52 -13
- package/src/types/block-ref.test.ts +45 -0
- package/src/types/block-ref.ts +2 -0
- package/templates/astro/starter-content/src/fixtures/atomicBlocks.ts +7 -6
- package/templates/astro/starter-content/src/fixtures/composition.ts +3 -2
- package/templates/astro/starter-content/src/fixtures/sections.ts +3 -2
- package/templates/astro/template/package.json.tmpl +1 -1
- package/templates/astro/template/src/layouts/SiteLayout.astro +2 -2
- package/templates/common/.agents/skills/aci/SKILL.md +56 -0
- package/templates/common/.agents/skills/authoring/SKILL.md +15 -6
- package/templates/common/.agents/skills/authoring/references/block-slots.md +3 -0
- package/templates/common/.agents/skills/dam-delivery/SKILL.md +228 -0
- package/templates/common/.agents/skills/dam-delivery/references/parameter-contract.md +107 -0
- package/templates/common/.agents/skills/dam-delivery/references/permalink-grammar.md +131 -0
- package/templates/common/.agents/skills/migration/references/selector-map.md +1 -1
- package/templates/common/.content/pages/{home/_index.json → _index.json} +10 -0
- package/templates/common/.content/schemas/site.schema.json +1 -1
- package/templates/common/.nvmrc +1 -1
- package/templates/common/starter-content/.content/.rules-baseline.json +8 -0
- package/templates/common/starter-content/.content/config/site.json +1 -1
- package/templates/common/starter-content/.content/content-rules/no-public-resource-paths.json +6 -0
- package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000101}/asset.json +1 -1
- package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000102}/asset.json +1 -1
- package/templates/common/starter-content/.content/fragments/navbar/_index.json +23 -20
- package/templates/common/starter-content/.content/fragments/site-footer/_index.json +59 -56
- package/templates/common/starter-content/.content/pages/404/_index.json +2 -1
- package/templates/common/starter-content/.content/pages/{home/_index.json → _index.json} +11 -2
- package/templates/common/starter-content/.content/pages/about/_index.json +4 -1
- package/templates/common/starter-content/.content/pages/error/_index.json +2 -1
- package/templates/common/starter-content/.content/patterns/components/button.json +55 -0
- package/templates/common/starter-content/.content/patterns/components/container.json +62 -0
- package/templates/common/starter-content/.content/patterns/components/feature_card.json +30 -0
- package/templates/common/starter-content/.content/patterns/components/feature_grid.json +57 -0
- package/templates/common/starter-content/.content/patterns/components/home_hero.json +142 -0
- package/templates/common/starter-content/.content/patterns/components/site-footer.json +203 -0
- package/templates/common/starter-content/.content/patterns/components/site-navigation.json +115 -0
- package/templates/common/starter-content/.content/patterns/components/stats_bar.json +45 -0
- package/templates/common/starter-content/.content/patterns/layouts/default.json +14 -0
- package/templates/common/starter-content/.content/schemas/site.schema.json +1 -1
- package/templates/nextjs/template/.aci.yaml +10 -0
- package/templates/nextjs/template/package.json.tmpl +4 -3
- package/templates/nextjs/template/src/app/_components/c15t-bridge.ts +30 -0
- package/templates/nextjs/template/src/app/_components/c15t-consent-ui.tsx +37 -0
- package/templates/nextjs/template/src/app/_components/consent-manager.css +63 -0
- package/templates/nextjs/template/src/app/_components/consent-manager.tsx +76 -0
- package/templates/nextjs/template/src/app/layout.tsx +2 -0
- package/dist/edge/personalization-core.d.ts +0 -11
- package/dist/edge/personalization-core.js +0 -269
- package/dist/next/collect-route.d.ts +0 -19
- package/dist/next/collect-route.js +0 -459
- package/dist/next/envelope-digest.d.ts +0 -21
- package/dist/next/envelope-digest.js +0 -58
- package/dist/next/middleware.d.ts +0 -40
- package/dist/next/middleware.js +0 -475
- package/dist/next/personalization-host.d.ts +0 -134
- package/dist/next/personalization-host.js +0 -270
- package/dist/next/root-layout.d.ts +0 -4
- package/dist/next/root-layout.js +0 -5
- package/dist/next/vercel-collect-sdk-route.d.ts +0 -27
- package/dist/next/vercel-collect-sdk-route.js +0 -181
- package/dist/next/vercel-personalization.d.ts +0 -4
- package/dist/next/vercel-personalization.js +0 -4
- package/dist/next/vercel-s3-personalization.d.ts +0 -79
- package/dist/next/vercel-s3-personalization.js +0 -570
- /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/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000101}/editorial.json +0 -0
- /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000101}/governance.json +0 -0
- /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000101}/versions/v0001.json +0 -0
- /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000102}/editorial.json +0 -0
- /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000102}/governance.json +0 -0
- /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000102}/versions/v0001.json +0 -0
package/dist/react/index.d.ts
CHANGED
|
@@ -29,12 +29,11 @@ export declare function Image({ image, value, ...attrs }: ImageProps): import("r
|
|
|
29
29
|
style?: import("react").CSSProperties | undefined;
|
|
30
30
|
dir?: string | undefined | undefined;
|
|
31
31
|
hidden?: boolean | undefined | undefined;
|
|
32
|
+
children?: ReactNode;
|
|
32
33
|
dangerouslySetInnerHTML?: {
|
|
33
34
|
__html: string | TrustedHTML;
|
|
34
35
|
} | undefined | undefined;
|
|
35
|
-
children?: ReactNode;
|
|
36
36
|
className?: string | undefined | undefined;
|
|
37
|
-
lang?: string | undefined | undefined;
|
|
38
37
|
crossOrigin?: "" | "anonymous" | "use-credentials" | undefined;
|
|
39
38
|
fetchPriority?: "high" | "low" | "auto" | undefined | undefined;
|
|
40
39
|
useMap?: string | undefined | undefined;
|
|
@@ -49,6 +48,7 @@ export declare function Image({ image, value, ...attrs }: ImageProps): import("r
|
|
|
49
48
|
contextMenu?: string | undefined | undefined;
|
|
50
49
|
draggable?: (boolean | "true" | "false") | undefined;
|
|
51
50
|
enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined | undefined;
|
|
51
|
+
lang?: string | undefined | undefined;
|
|
52
52
|
nonce?: string | undefined | undefined;
|
|
53
53
|
spellCheck?: (boolean | "true" | "false") | undefined;
|
|
54
54
|
tabIndex?: number | undefined | undefined;
|
package/dist/testing/index.js
CHANGED
|
@@ -53,9 +53,9 @@ export class FixtureContentProvider {
|
|
|
53
53
|
return [...this.pages.entries()]
|
|
54
54
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
55
55
|
.map(([routePath, page]) => ({
|
|
56
|
-
slug: routePath === '/' ? '
|
|
56
|
+
slug: routePath === '/' ? '' : routePath.replace(/^\//, ''),
|
|
57
57
|
path: routePath,
|
|
58
|
-
payloadRef:
|
|
58
|
+
payloadRef: routePath === '/' ? 'routes/index.json' : `routes/${routePath.replace(/^\//, '')}.json`,
|
|
59
59
|
digest: '',
|
|
60
60
|
}));
|
|
61
61
|
}
|
|
@@ -5,6 +5,7 @@ export interface BlockRefMeta {
|
|
|
5
5
|
multiple: boolean;
|
|
6
6
|
}
|
|
7
7
|
declare const blockRefBase: z.ZodObject<{
|
|
8
|
+
$type: z.ZodLiteral<"component">;
|
|
8
9
|
id: z.ZodString;
|
|
9
10
|
component: z.ZodString;
|
|
10
11
|
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -12,6 +13,7 @@ declare const blockRefBase: z.ZodObject<{
|
|
|
12
13
|
type InferContractProps<T> = T extends ComponentContract<string, infer Schema> ? Schema extends z.ZodType<infer P> ? P : Record<string, unknown> : Record<string, unknown>;
|
|
13
14
|
type InferContractId<T> = T extends ComponentContract<infer Id, unknown> ? Id : never;
|
|
14
15
|
export type BlockRefEntry<T extends ComponentContract> = {
|
|
16
|
+
$type: 'component';
|
|
15
17
|
id: string;
|
|
16
18
|
component: InferContractId<T>;
|
|
17
19
|
props: InferContractProps<T>;
|
package/dist/types/block-ref.js
CHANGED
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.22",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/gradial/bare-metal-go.git",
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
"types": "./dist/compiler/index.d.ts",
|
|
42
42
|
"import": "./dist/compiler/index.js"
|
|
43
43
|
},
|
|
44
|
+
"./expression-capabilities": {
|
|
45
|
+
"types": "./dist/edge/expression-capabilities.v1.d.ts",
|
|
46
|
+
"import": "./dist/edge/expression-capabilities.v1.js"
|
|
47
|
+
},
|
|
48
|
+
"./mintlify": {
|
|
49
|
+
"types": "./dist/mintlify/index.d.ts",
|
|
50
|
+
"import": "./dist/mintlify/index.js"
|
|
51
|
+
},
|
|
44
52
|
"./providers/file": {
|
|
45
53
|
"types": "./dist/providers/file.d.ts",
|
|
46
54
|
"import": "./dist/providers/file.js"
|
|
@@ -76,6 +84,11 @@
|
|
|
76
84
|
"import": "./dist/next/error-seat.js",
|
|
77
85
|
"default": "./dist/next/error-seat.js"
|
|
78
86
|
},
|
|
87
|
+
"./next/asset-route": {
|
|
88
|
+
"types": "./dist/next/asset-route.d.ts",
|
|
89
|
+
"import": "./dist/next/asset-route.js",
|
|
90
|
+
"default": "./dist/next/asset-route.js"
|
|
91
|
+
},
|
|
79
92
|
"./next/server": {
|
|
80
93
|
"types": "./dist/next/server.d.ts",
|
|
81
94
|
"import": "./dist/next/server.js",
|
|
@@ -146,11 +159,11 @@
|
|
|
146
159
|
"zod": "^4.0.0"
|
|
147
160
|
},
|
|
148
161
|
"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.
|
|
162
|
+
"@gradial/aci-cli-darwin-arm64": "0.1.52-rc.22",
|
|
163
|
+
"@gradial/aci-cli-darwin-x64": "0.1.52-rc.22",
|
|
164
|
+
"@gradial/aci-cli-linux-arm64": "0.1.52-rc.22",
|
|
165
|
+
"@gradial/aci-cli-linux-x64": "0.1.52-rc.22",
|
|
166
|
+
"@gradial/aci-cli-win32-arm64": "0.1.52-rc.22",
|
|
167
|
+
"@gradial/aci-cli-win32-x64": "0.1.52-rc.22"
|
|
155
168
|
}
|
|
156
169
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
2
3
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
4
|
import path from 'node:path';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
import { ImageSchema } from '../types/image.ts';
|
|
7
8
|
import { VideoSchema } from '../types/video.ts';
|
|
@@ -19,14 +20,15 @@ try {
|
|
|
19
20
|
if (!globalThis.React) globalThis.React = React;
|
|
20
21
|
} catch { /* react not available — component files without JSX will still work */ }
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} catch (error) {
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
if (isMainModule()) {
|
|
24
|
+
const args = parseArgs(process.argv.slice(2));
|
|
25
|
+
try {
|
|
26
|
+
const result = await compileRegistry(args);
|
|
27
|
+
console.log(JSON.stringify({ success: true, ...result, errors: [] }, null, 2));
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(JSON.stringify({ success: false, errors: [String(error?.message ?? error)] }, null, 2));
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
}
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
async function compileRegistry(options) {
|
|
@@ -430,9 +432,10 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
430
432
|
const defs = {
|
|
431
433
|
baseBlock: {
|
|
432
434
|
type: 'object',
|
|
433
|
-
required: ['id', 'component', 'props'],
|
|
435
|
+
required: ['$type', 'id', 'component', 'props'],
|
|
434
436
|
additionalProperties: true,
|
|
435
437
|
properties: {
|
|
438
|
+
$type: { const: 'component' },
|
|
436
439
|
id: { type: 'string', minLength: 1 },
|
|
437
440
|
component: { type: 'string' },
|
|
438
441
|
props: { type: 'object' },
|
|
@@ -741,3 +744,14 @@ function parseArgs(argv) {
|
|
|
741
744
|
}
|
|
742
745
|
return out;
|
|
743
746
|
}
|
|
747
|
+
|
|
748
|
+
function isMainModule() {
|
|
749
|
+
if (!process.argv[1]) return false;
|
|
750
|
+
try {
|
|
751
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(path.resolve(process.argv[1]));
|
|
752
|
+
} catch {
|
|
753
|
+
return false;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
export { compileRegistry, generatePageSchema };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { loadCompiledMintlifySource, materializeMintlifySite } from '../../dist/mintlify/index.js';
|
|
3
|
+
|
|
4
|
+
const [sourceArg, compiledArg, outputArg] = process.argv.slice(2);
|
|
5
|
+
if (!sourceArg || !compiledArg || !outputArg) {
|
|
6
|
+
throw new Error('Mintlify renderer requires content, compiled, and output directories');
|
|
7
|
+
}
|
|
8
|
+
const source = await loadCompiledMintlifySource(path.resolve(sourceArg), path.resolve(compiledArg));
|
|
9
|
+
await materializeMintlifySite({ ...source, outputDir: path.resolve(outputArg) });
|
|
@@ -19,21 +19,24 @@
|
|
|
19
19
|
* Uses the JSON schemas generated by compile-registry — portable, no React/CSS
|
|
20
20
|
* imports needed, same schemas usable by Go backend or other tools.
|
|
21
21
|
*/
|
|
22
|
+
import { realpathSync } from 'node:fs';
|
|
22
23
|
import { readdir, readFile } from 'node:fs/promises';
|
|
23
24
|
import path from 'node:path';
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
import { fileURLToPath } from 'node:url';
|
|
26
|
+
|
|
27
|
+
if (isMainModule()) {
|
|
28
|
+
const args = parseArgs(process.argv.slice(2));
|
|
29
|
+
try {
|
|
30
|
+
const result = await validateContent(args);
|
|
31
|
+
console.log(JSON.stringify(result, null, 2));
|
|
32
|
+
if (!result.success) process.exitCode = 1;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(JSON.stringify({
|
|
35
|
+
success: false,
|
|
36
|
+
errors: [{ message: String(error?.message ?? error) }],
|
|
37
|
+
}, null, 2));
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
// ---------------------------------------------------------------------------
|
|
@@ -95,6 +98,24 @@ async function validateContent(options) {
|
|
|
95
98
|
for (const block of blocks) {
|
|
96
99
|
blocksValidated++;
|
|
97
100
|
|
|
101
|
+
if (!block || typeof block !== 'object') {
|
|
102
|
+
collector.push({
|
|
103
|
+
page: relativePath,
|
|
104
|
+
blockId: '<unknown>',
|
|
105
|
+
message: 'page region entry must be a component block',
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (block.$type !== 'component') {
|
|
111
|
+
collector.push({
|
|
112
|
+
page: relativePath,
|
|
113
|
+
blockId: block.id ?? '<unknown>',
|
|
114
|
+
path: '$type',
|
|
115
|
+
message: 'block "$type" must be "component"',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
98
119
|
if (!block.component) {
|
|
99
120
|
collector.push({
|
|
100
121
|
page: relativePath,
|
|
@@ -380,6 +401,13 @@ function validateNestedSlots(parentBlock, componentMap, schemaMap, path = 'props
|
|
|
380
401
|
issues.push({ path: nestedPath, message: `${parentBlock.component}: block ref child must be a block` });
|
|
381
402
|
continue;
|
|
382
403
|
}
|
|
404
|
+
if (nestedBlock.$type !== 'component') {
|
|
405
|
+
issues.push({
|
|
406
|
+
path: `${nestedPath}.$type`,
|
|
407
|
+
message: `${parentBlock.component}: block ref child "$type" must be "component"`,
|
|
408
|
+
});
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
383
411
|
|
|
384
412
|
if (!componentMap.has(nestedBlock.component)) {
|
|
385
413
|
issues.push({ path: `${nestedPath}.component`, message: `${parentBlock.component}: unknown nested component "${nestedBlock.component}"` });
|
|
@@ -450,3 +478,14 @@ function parseArgs(argv) {
|
|
|
450
478
|
}
|
|
451
479
|
return out;
|
|
452
480
|
}
|
|
481
|
+
|
|
482
|
+
function isMainModule() {
|
|
483
|
+
if (!process.argv[1]) return false;
|
|
484
|
+
try {
|
|
485
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(path.resolve(process.argv[1]));
|
|
486
|
+
} catch {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export { validateContent };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
import { compileSchema } from '../compiler/zod-to-jsonschema.js';
|
|
6
|
+
import { defineComponentContract } from '../define-component.js';
|
|
7
|
+
import { blockRefArray } from './block-ref.js';
|
|
8
|
+
|
|
9
|
+
const childContract = defineComponentContract({
|
|
10
|
+
id: 'child',
|
|
11
|
+
props: z.object({
|
|
12
|
+
label: z.string(),
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('block references require and accept the component discriminator', () => {
|
|
17
|
+
const schema = blockRefArray([childContract]);
|
|
18
|
+
const typed = {
|
|
19
|
+
$type: 'component',
|
|
20
|
+
id: 'child-1',
|
|
21
|
+
component: 'child',
|
|
22
|
+
props: { label: 'Hello' },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
assert.equal(schema.safeParse([typed]).success, true);
|
|
26
|
+
assert.equal(schema.safeParse([{
|
|
27
|
+
id: typed.id,
|
|
28
|
+
component: typed.component,
|
|
29
|
+
props: typed.props,
|
|
30
|
+
}]).success, false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('compiled block-reference schema exposes the component discriminator', () => {
|
|
34
|
+
const compiled = compileSchema(blockRefArray([childContract])) as {
|
|
35
|
+
items?: {
|
|
36
|
+
properties?: Record<string, { const?: string }>;
|
|
37
|
+
required?: string[];
|
|
38
|
+
additionalProperties?: boolean;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
assert.equal(compiled.items?.properties?.$type?.const, 'component');
|
|
43
|
+
assert.equal(compiled.items?.required?.includes('$type'), true);
|
|
44
|
+
assert.equal(compiled.items?.additionalProperties, false);
|
|
45
|
+
});
|
package/src/types/block-ref.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface BlockRefMeta {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const blockRefBase = z.object({
|
|
12
|
+
$type: z.literal('component'),
|
|
12
13
|
id: z.string().min(1),
|
|
13
14
|
component: z.string().min(1),
|
|
14
15
|
props: z.record(z.string(), z.unknown()),
|
|
@@ -21,6 +22,7 @@ type InferContractProps<T> = T extends ComponentContract<string, infer Schema>
|
|
|
21
22
|
type InferContractId<T> = T extends ComponentContract<infer Id, unknown> ? Id : never;
|
|
22
23
|
|
|
23
24
|
export type BlockRefEntry<T extends ComponentContract> = {
|
|
25
|
+
$type: 'component';
|
|
24
26
|
id: string;
|
|
25
27
|
component: InferContractId<T>;
|
|
26
28
|
props: InferContractProps<T>;
|
|
@@ -13,17 +13,18 @@ export const atomicBlockStoryFixtures: AtomicBlockStoryFixture[] = [
|
|
|
13
13
|
storybookId: 'core-atomic-blocks-button--variants',
|
|
14
14
|
title: 'Button / All Variants',
|
|
15
15
|
block: {
|
|
16
|
+
$type: 'component',
|
|
16
17
|
id: 'fixture-button-variants',
|
|
17
18
|
component: 'container',
|
|
18
19
|
props: {
|
|
19
20
|
theme: 'auto',
|
|
20
21
|
blocks: [
|
|
21
|
-
{ id: 'btn-primary', component: 'button', props: { label: 'Primary button', href: '#', variant: 'primary' } },
|
|
22
|
-
{ id: 'btn-secondary', component: 'button', props: { label: 'Secondary button', href: '#', variant: 'secondary' } },
|
|
23
|
-
{ id: 'btn-tertiary', component: 'button', props: { label: 'Tertiary button', href: '#', variant: 'tertiary' } },
|
|
24
|
-
{ id: 'btn-icon-before', component: 'button', props: { label: 'Icon before', href: '#', variant: 'primary', icon: 'arrow-right', iconPosition: 'before' } },
|
|
25
|
-
{ id: 'btn-icon-after', component: 'button', props: { label: 'Icon after', href: '#', variant: 'secondary', icon: 'arrow-right', iconPosition: 'after' } },
|
|
26
|
-
{ id: 'btn-new-window', component: 'button', props: { label: 'Opens in new tab', href: 'https://example.com', variant: 'primary', newWindow: true, icon: 'arrow-right', iconPosition: 'after' } }
|
|
22
|
+
{ $type: 'component', id: 'btn-primary', component: 'button', props: { label: 'Primary button', href: '#', variant: 'primary' } },
|
|
23
|
+
{ $type: 'component', id: 'btn-secondary', component: 'button', props: { label: 'Secondary button', href: '#', variant: 'secondary' } },
|
|
24
|
+
{ $type: 'component', id: 'btn-tertiary', component: 'button', props: { label: 'Tertiary button', href: '#', variant: 'tertiary' } },
|
|
25
|
+
{ $type: 'component', id: 'btn-icon-before', component: 'button', props: { label: 'Icon before', href: '#', variant: 'primary', icon: 'arrow-right', iconPosition: 'before' } },
|
|
26
|
+
{ $type: 'component', id: 'btn-icon-after', component: 'button', props: { label: 'Icon after', href: '#', variant: 'secondary', icon: 'arrow-right', iconPosition: 'after' } },
|
|
27
|
+
{ $type: 'component', id: 'btn-new-window', component: 'button', props: { label: 'Opens in new tab', href: 'https://example.com', variant: 'primary', newWindow: true, icon: 'arrow-right', iconPosition: 'after' } }
|
|
27
28
|
]
|
|
28
29
|
}
|
|
29
30
|
}
|
|
@@ -14,14 +14,15 @@ export const compositionStoryFixtures: CompositionStoryFixture[] = [
|
|
|
14
14
|
storybookId: 'composition-container--buttons',
|
|
15
15
|
title: 'Container / Buttons',
|
|
16
16
|
block: {
|
|
17
|
+
$type: 'component',
|
|
17
18
|
id: 'fixture-container-buttons',
|
|
18
19
|
component: 'container',
|
|
19
20
|
props: {
|
|
20
21
|
theme: 'auto',
|
|
21
22
|
className: 'space-y-4',
|
|
22
23
|
blocks: [
|
|
23
|
-
{ id: 'btn-1', component: 'button', props: { label: 'Primary action', href: '#', variant: 'primary', icon: 'arrow_forward', iconPosition: 'after' } },
|
|
24
|
-
{ id: 'btn-2', component: 'button', props: { label: 'Secondary action', href: '#', variant: 'secondary' } }
|
|
24
|
+
{ $type: 'component', id: 'btn-1', component: 'button', props: { label: 'Primary action', href: '#', variant: 'primary', icon: 'arrow_forward', iconPosition: 'after' } },
|
|
25
|
+
{ $type: 'component', id: 'btn-2', component: 'button', props: { label: 'Secondary action', href: '#', variant: 'secondary' } }
|
|
25
26
|
]
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -14,6 +14,7 @@ export const sectionStoryFixtures: SectionStoryFixture[] = [
|
|
|
14
14
|
storybookId: 'sections-home-hero--default',
|
|
15
15
|
title: 'Home Hero',
|
|
16
16
|
block: {
|
|
17
|
+
$type: 'component',
|
|
17
18
|
id: 'fixture-home-hero',
|
|
18
19
|
component: 'home_hero',
|
|
19
20
|
props: {
|
|
@@ -22,9 +23,9 @@ export const sectionStoryFixtures: SectionStoryFixture[] = [
|
|
|
22
23
|
description: 'One content model, three renderers. Build once in Next.js, Astro, and SvelteKit—then prove parity with shared fixtures.',
|
|
23
24
|
ctaLabel: 'Get started',
|
|
24
25
|
ctaHref: '/docs',
|
|
25
|
-
|
|
26
|
+
image: {
|
|
26
27
|
$type: 'gradial.image',
|
|
27
|
-
assetId: '
|
|
28
|
+
assetId: '3Ih26etWBTYfnAd0EjD0pP2h902',
|
|
28
29
|
versionId: 'v0001',
|
|
29
30
|
alt: 'Seattle skyline at dusk',
|
|
30
31
|
sources: [
|
|
@@ -48,8 +48,8 @@ const { title, description, canonical, siteName = 'Gradial', collectTag, assignm
|
|
|
48
48
|
// island (@astrojs/react + @c15t/react) or any setup built on
|
|
49
49
|
// c15t's createConsentManagerStore; this bridge picks it up either
|
|
50
50
|
// way. Without a c15t setup the poll gives up silently and only the
|
|
51
|
-
// JS consent API (aci('consent', …)
|
|
52
|
-
//
|
|
51
|
+
// JS consent API (aci('consent', …)) writes envelopes — it is live
|
|
52
|
+
// under every preset, including c15t.
|
|
53
53
|
//
|
|
54
54
|
// Consents are mirrored only after a real visitor decision
|
|
55
55
|
// (consentInfo set), never for pre-decision defaults, so the SDK
|
|
@@ -152,6 +152,56 @@ Optional (disabled by default):
|
|
|
152
152
|
|
|
153
153
|
## Troubleshooting
|
|
154
154
|
|
|
155
|
+
### Deploying a starter to a hosted Vercel tenant
|
|
156
|
+
|
|
157
|
+
Run the CLI from the starter repository root (where `.aci.yaml` lives), or
|
|
158
|
+
pass `--site-dir .` from that directory. Do not pass an absolute site path
|
|
159
|
+
from another working directory: older CLI versions can serialize it as a
|
|
160
|
+
`../../../tmp/...` source folder, which makes the server-side build fail with
|
|
161
|
+
a generic HTTP 500. The source ref must also be a pushed, cloneable branch;
|
|
162
|
+
`ref: HEAD` from a detached worktree is not cloneable by the build service.
|
|
163
|
+
|
|
164
|
+
The hosted publish sequence is:
|
|
165
|
+
|
|
166
|
+
1. `GRADIAL_CODE_SOURCE_TOKEN="$(gh auth token)" aci code-build --site-dir . --branch <test-branch> ...`
|
|
167
|
+
2. `aci branch land <test-branch>` to move the artifact link to `main`.
|
|
168
|
+
3. Promote `/config/site.json` and the content paths needed by the release.
|
|
169
|
+
4. Run `aci release publish --activate ...`.
|
|
170
|
+
|
|
171
|
+
Match the tenant rendering mode to the starter's actual route capability. A
|
|
172
|
+
Next starter with `dynamicParams = false` and no release-scoped runtime route
|
|
173
|
+
is an SSG starter and must use `rendering.mode = "ssg"`; `ssr` is only valid
|
|
174
|
+
when the frontend can resolve arbitrary host release routes at request time.
|
|
175
|
+
A mismatch can build successfully but then fail SSG rendering or return a
|
|
176
|
+
live Next 404 after the host rewrites a CMS page.
|
|
177
|
+
|
|
178
|
+
Injected platform functions must be dependency-closed inside their own
|
|
179
|
+
`.vercel/output/functions/<name>.func` mount. A prebuilt Vercel deploy does not
|
|
180
|
+
install a function-local `package.json` after upload. Native dependencies such
|
|
181
|
+
as `sharp` therefore belong to the host-function artifact, built for Vercel's
|
|
182
|
+
Linux/x64 runtime and injected beside the handler; never add them to the
|
|
183
|
+
starter's `package.json` merely to make a platform function resolve.
|
|
184
|
+
|
|
185
|
+
Publish validates `refs/heads/live`, not only `main`. If `main` deletes a
|
|
186
|
+
stale incompatible page or fragment that was previously promoted, promote
|
|
187
|
+
that same path from `main` with `aci promote --path ... --yes`; because the
|
|
188
|
+
source path is absent, the promote records a delete on `live`. This is the
|
|
189
|
+
API-safe way to remove old live content—direct writes to `live` are forbidden.
|
|
190
|
+
|
|
191
|
+
The same rule applies to breaking DAM identifier changes: every
|
|
192
|
+
`dam.assetRef` in `live` is resolved during publish, so one legacy identifier
|
|
193
|
+
blocks the release. Update the reference on a candidate branch through
|
|
194
|
+
`aci content push`, land it, and promote that exact content path before
|
|
195
|
+
publishing again. Do not weaken identifier validation or reactivate an old
|
|
196
|
+
release to bypass the content migration.
|
|
197
|
+
|
|
198
|
+
Legacy assets themselves are replaced through the product API, not rewritten
|
|
199
|
+
in storage: download or retain the needed source bytes, upload them again so
|
|
200
|
+
DAM creates fresh canonical IDs, approve them, replace every content reference,
|
|
201
|
+
then land, promote, and publish the affected paths. This is the supported
|
|
202
|
+
breaking cutover for pre-public tenants; there is no `ast_` alias or bulk
|
|
203
|
+
object-store migration path.
|
|
204
|
+
|
|
155
205
|
### "Unknown component" error during content compile
|
|
156
206
|
|
|
157
207
|
The content JSON references a `component` name that is not in the contract
|
|
@@ -209,3 +259,9 @@ development, design tokens, and the full contract/runtime split.
|
|
|
209
259
|
|
|
210
260
|
Read `.agents/skills/migration/SKILL.md` for reverse-engineering a live
|
|
211
261
|
site or design into ACI.
|
|
262
|
+
|
|
263
|
+
## Deep Dive: DAM Delivery
|
|
264
|
+
|
|
265
|
+
Read `.agents/skills/dam-delivery/SKILL.md` for signed on-demand image
|
|
266
|
+
transform URLs: when to mint them, the closed parameter contract, expiry
|
|
267
|
+
semantics, and why they must never be baked into published payloads.
|
|
@@ -78,12 +78,13 @@ Pages map to URL routes by their folder path under `/pages/`:
|
|
|
78
78
|
|
|
79
79
|
| Page Path | Route |
|
|
80
80
|
|-----------|-------|
|
|
81
|
-
| `/pages/
|
|
81
|
+
| `/pages/_index.json` | `/` |
|
|
82
82
|
| `/pages/about/_index.json` | `/about` |
|
|
83
83
|
| `/pages/products/_index.json` | `/products` |
|
|
84
84
|
| `/pages/products/shoes/_index.json` | `/products/shoes` |
|
|
85
85
|
|
|
86
|
-
The
|
|
86
|
+
The empty slug is the site root. Every named folder maps directly, including
|
|
87
|
+
`/pages/home/_index.json` at `/home`.
|
|
87
88
|
|
|
88
89
|
### Content Rules
|
|
89
90
|
|
|
@@ -98,6 +99,7 @@ The `home` slug is special-cased to route `/`. All other slugs map directly.
|
|
|
98
99
|
|
|
99
100
|
```json
|
|
100
101
|
{
|
|
102
|
+
"$type": "component",
|
|
101
103
|
"id": "hero-section",
|
|
102
104
|
"component": "home_hero",
|
|
103
105
|
"props": {
|
|
@@ -109,6 +111,7 @@ The `home` slug is special-cased to route `/`. All other slugs map directly.
|
|
|
109
111
|
```
|
|
110
112
|
|
|
111
113
|
- `component` must match a contract `id` exactly.
|
|
114
|
+
- Every embedded block must declare `"$type": "component"`.
|
|
112
115
|
- `props` must validate against the contract's Zod schema.
|
|
113
116
|
- `id` is unique within the page.
|
|
114
117
|
|
|
@@ -141,12 +144,14 @@ export const containerContract = defineComponentContract({
|
|
|
141
144
|
**Content JSON:**
|
|
142
145
|
```json
|
|
143
146
|
{
|
|
147
|
+
"$type": "component",
|
|
144
148
|
"id": "cta-section",
|
|
145
149
|
"component": "container",
|
|
146
150
|
"props": {
|
|
147
151
|
"className": "space-y-4",
|
|
148
152
|
"content": [
|
|
149
153
|
{
|
|
154
|
+
"$type": "component",
|
|
150
155
|
"id": "primary-cta",
|
|
151
156
|
"component": "button",
|
|
152
157
|
"props": {
|
|
@@ -156,6 +161,7 @@ export const containerContract = defineComponentContract({
|
|
|
156
161
|
}
|
|
157
162
|
},
|
|
158
163
|
{
|
|
164
|
+
"$type": "component",
|
|
159
165
|
"id": "secondary-cta",
|
|
160
166
|
"component": "button",
|
|
161
167
|
"props": {
|
|
@@ -182,6 +188,7 @@ The `button` component is a common atomic block:
|
|
|
182
188
|
|
|
183
189
|
```json
|
|
184
190
|
{
|
|
191
|
+
"$type": "component",
|
|
185
192
|
"id": "cta-button",
|
|
186
193
|
"component": "button",
|
|
187
194
|
"props": {
|
|
@@ -480,8 +487,9 @@ framework.
|
|
|
480
487
|
#### 5. Add content
|
|
481
488
|
|
|
482
489
|
```json
|
|
483
|
-
// .content/pages/
|
|
490
|
+
// .content/pages/_index.json (inside regions.main)
|
|
484
491
|
{
|
|
492
|
+
"$type": "component",
|
|
485
493
|
"id": "features",
|
|
486
494
|
"component": "feature_showcase",
|
|
487
495
|
"props": {
|
|
@@ -570,9 +578,10 @@ export default [
|
|
|
570
578
|
];
|
|
571
579
|
```
|
|
572
580
|
|
|
573
|
-
Fragment content lives in `.content/fragments/{id}/_index.json` and
|
|
574
|
-
|
|
575
|
-
Footer) are registered in the registry
|
|
581
|
+
Fragment content lives in `.content/fragments/{id}/_index.json` and retains
|
|
582
|
+
the resource envelope `"$type": "fragment"` while declaring `component` +
|
|
583
|
+
`props`. Chrome components (Header, Footer) are registered in the registry
|
|
584
|
+
alongside page components.
|
|
576
585
|
|
|
577
586
|
The `layout` field in page JSON is **required** — there is no implicit
|
|
578
587
|
default. Every page must declare which layout it uses.
|
|
@@ -113,12 +113,14 @@ type RenderChildren<T extends readonly BlockRef[]> = (blocks: T) => Promise<Reac
|
|
|
113
113
|
|
|
114
114
|
```json
|
|
115
115
|
{
|
|
116
|
+
"$type": "component",
|
|
116
117
|
"id": "cta-section",
|
|
117
118
|
"component": "container",
|
|
118
119
|
"props": {
|
|
119
120
|
"theme": "dark",
|
|
120
121
|
"blocks": [
|
|
121
122
|
{
|
|
123
|
+
"$type": "component",
|
|
122
124
|
"id": "primary-cta",
|
|
123
125
|
"component": "callout",
|
|
124
126
|
"props": {
|
|
@@ -127,6 +129,7 @@ type RenderChildren<T extends readonly BlockRef[]> = (blocks: T) => Promise<Reac
|
|
|
127
129
|
}
|
|
128
130
|
},
|
|
129
131
|
{
|
|
132
|
+
"$type": "component",
|
|
130
133
|
"id": "heading",
|
|
131
134
|
"component": "heading",
|
|
132
135
|
"props": {
|