@gradial/aci 0.1.51-ssr.5 → 0.1.52-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +4 -4
  2. package/dist/astro/assets.d.ts +8 -8
  3. package/dist/astro/assets.js +8 -8
  4. package/dist/astro/index.js +2 -2
  5. package/dist/audience-data-models.d.ts +9 -0
  6. package/dist/audience-data-models.js +3 -0
  7. package/dist/cli/compile-registry.d.mts +5 -1
  8. package/dist/cli/compile-registry.mjs +25 -10
  9. package/dist/cli/validate-content.d.mts +31 -1
  10. package/dist/cli/validate-content.mjs +50 -12
  11. package/dist/content/cache.d.ts +6 -1
  12. package/dist/content/cache.js +26 -0
  13. package/dist/content/provider.d.ts +1 -0
  14. package/dist/content/resolve-slots.d.ts +9 -2
  15. package/dist/content/resolve-slots.js +5 -3
  16. package/dist/content/types.d.ts +2 -0
  17. package/dist/dev/content-watch.js +3 -3
  18. package/dist/edge/personalization-core.d.ts +11 -0
  19. package/dist/edge/personalization-core.js +281 -0
  20. package/dist/internal/domain/edge/compose.d.ts +24 -0
  21. package/dist/internal/domain/edge/compose.js +89 -0
  22. package/dist/internal/domain/edge/index.d.ts +1 -0
  23. package/dist/internal/domain/edge/index.js +1 -0
  24. package/dist/internal/domain/edge/personalization-core.d.ts +4 -1
  25. package/dist/internal/domain/edge/personalization-core.js +25 -8
  26. package/dist/internal/domain/edge/resolve.js +6 -4
  27. package/dist/internal/domain/edge/types.d.ts +12 -0
  28. package/dist/internal/render-proof.d.ts +1 -0
  29. package/dist/internal/render-proof.js +197 -0
  30. package/dist/next/asset-route.d.ts +10 -0
  31. package/dist/next/asset-route.js +45 -0
  32. package/dist/next/collect-route.d.ts +19 -0
  33. package/dist/next/collect-route.js +459 -0
  34. package/dist/next/config.d.ts +9 -1
  35. package/dist/next/config.js +79 -4
  36. package/dist/next/content-watch.js +1 -1
  37. package/dist/next/edge-config.d.ts +1 -0
  38. package/dist/next/edge-config.js +92 -0
  39. package/dist/next/envelope-digest.d.ts +21 -0
  40. package/dist/next/envelope-digest.js +58 -0
  41. package/dist/next/error-pages.d.ts +3 -2
  42. package/dist/next/error-pages.js +9 -14
  43. package/dist/next/error-seat-payload.js +1 -1
  44. package/dist/next/error-seat.d.ts +3 -2
  45. package/dist/next/index.d.ts +2 -2
  46. package/dist/next/index.js +2 -1
  47. package/dist/next/middleware.d.ts +40 -0
  48. package/dist/next/middleware.js +475 -0
  49. package/dist/next/page.d.ts +13 -3
  50. package/dist/next/page.js +61 -82
  51. package/dist/next/personalization-host.d.ts +134 -0
  52. package/dist/next/personalization-host.js +270 -0
  53. package/dist/next/server.d.ts +33 -13
  54. package/dist/next/server.js +374 -60
  55. package/dist/next/vercel-collect-sdk-route.d.ts +27 -0
  56. package/dist/next/vercel-collect-sdk-route.js +181 -0
  57. package/dist/next/vercel-edge-projection-v1.d.ts +24 -0
  58. package/dist/next/vercel-edge-projection-v1.js +886 -0
  59. package/dist/next/vercel-personalization.d.ts +4 -0
  60. package/dist/next/vercel-personalization.js +4 -0
  61. package/dist/next/vercel-s3-personalization.d.ts +79 -0
  62. package/dist/next/vercel-s3-personalization.js +570 -0
  63. package/dist/providers/object.d.ts +3 -0
  64. package/dist/providers/object.js +18 -1
  65. package/dist/providers/s3.d.ts +47 -0
  66. package/dist/providers/s3.js +441 -0
  67. package/dist/providers/vercel-object-reader.d.ts +1 -1
  68. package/dist/providers/vercel-object-reader.js +10 -0
  69. package/dist/types/block-ref.d.ts +2 -0
  70. package/dist/types/block-ref.js +1 -0
  71. package/package.json +7 -7
  72. package/src/cli/compile-registry.mjs +24 -10
  73. package/src/cli/validate-content.mjs +52 -13
  74. package/src/types/block-ref.test.ts +45 -0
  75. package/src/types/block-ref.ts +2 -0
  76. package/templates/astro/starter-content/src/fixtures/atomicBlocks.ts +7 -6
  77. package/templates/astro/starter-content/src/fixtures/composition.ts +3 -2
  78. package/templates/astro/starter-content/src/fixtures/sections.ts +2 -1
  79. package/templates/common/.agents/skills/aci/SKILL.md +8 -7
  80. package/templates/common/.agents/skills/authoring/SKILL.md +11 -3
  81. package/templates/common/.agents/skills/authoring/references/block-registry.md +16 -5
  82. package/templates/common/.agents/skills/authoring/references/block-slots.md +3 -0
  83. package/templates/common/.agents/skills/authoring/references/contracts.md +4 -0
  84. package/templates/common/.agents/skills/authoring/references/sdk-server-functions.md +13 -7
  85. package/templates/common/.agents/skills/migration/references/canon-migration.md +7 -7
  86. package/templates/common/.content/pages/{home/_index.json → _index.json} +10 -0
  87. package/templates/common/starter-content/.content/.rules-baseline.json +8 -0
  88. package/templates/common/starter-content/.content/content-rules/no-public-resource-paths.json +6 -0
  89. package/templates/common/starter-content/.content/dam/blobs/sha256/58/09/5809142b357fca186bcb0fa41661ccaa142800570d2d4d2de2de77b6d8754773 +0 -0
  90. package/templates/common/starter-content/.content/dam/blobs/sha256/d7/df/d7dfcf7c59365632a408534cf56a9cc5ce848ccb5ce067fd655bbda7953ef5e8 +0 -0
  91. package/templates/common/starter-content/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +3 -3
  92. package/templates/common/starter-content/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +3 -3
  93. package/templates/common/starter-content/.content/fragments/navbar/_index.json +23 -20
  94. package/templates/common/starter-content/.content/fragments/site-footer/_index.json +59 -56
  95. package/templates/common/starter-content/.content/pages/404/_index.json +1 -0
  96. package/templates/common/starter-content/.content/pages/about/_index.json +3 -0
  97. package/templates/common/starter-content/.content/pages/error/_index.json +1 -0
  98. package/templates/common/starter-content/.content/pages/home/_index.json +9 -0
  99. package/templates/common/starter-content/.content/patterns/components/button.json +55 -0
  100. package/templates/common/starter-content/.content/patterns/components/container.json +62 -0
  101. package/templates/common/starter-content/.content/patterns/components/feature_card.json +30 -0
  102. package/templates/common/starter-content/.content/patterns/components/feature_grid.json +57 -0
  103. package/templates/common/starter-content/.content/patterns/components/home_hero.json +142 -0
  104. package/templates/common/starter-content/.content/patterns/components/personalization_choice.json +42 -0
  105. package/templates/common/starter-content/.content/patterns/components/site-footer.json +203 -0
  106. package/templates/common/starter-content/.content/patterns/components/site-navigation.json +115 -0
  107. package/templates/common/starter-content/.content/patterns/components/stats_bar.json +45 -0
  108. package/templates/common/starter-content/.content/patterns/layouts/default.json +14 -0
  109. package/templates/nextjs/template/.aci.yaml +10 -0
  110. package/templates/common/starter-content/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
  111. package/templates/common/starter-content/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
  112. package/templates/common/starter-content/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
  113. package/templates/common/starter-content/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
  114. /package/templates/common/.content/overlays/audience/preference-blue/pages/{home/_index.json → _index.json} +0 -0
  115. /package/templates/common/.content/overlays/audience/preference-red/pages/{home/_index.json → _index.json} +0 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Agentic Content Infrastructure SDK
2
2
 
3
- `@gradial/aci` `0.1.51-ssr.5` is a pre-1.0 public-surface reset for the ACI
3
+ `@gradial/aci` `0.1.52-rc.10` 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.51-ssr.5 zod@^4
11
+ npm install @gradial/aci@0.1.52-rc.10 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.51-ssr.5 init my-site --template next-starter
81
+ npx @gradial/aci@0.1.52-rc.10 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.51-ssr.5 init --list
87
+ npx @gradial/aci@0.1.52-rc.10 init --list
88
88
  ```
89
89
 
90
90
  ## Personalization: The One-Tag Install
@@ -5,16 +5,16 @@
5
5
  * at the URL level. Instead:
6
6
  *
7
7
  * - In dev: the Vite `damAssetPlugin` serves DAM assets from compiled
8
- * content at `/.gradial-dam/{path}` (already handled by withAci).
8
+ * content at `/dam/{path}` (already handled by withAci).
9
9
  *
10
- * - In production: the provider host maps `/.gradial-dam/*` to authoritative
11
- * S3/R2/CDN storage. There is no app-level asset route
12
- * (the retired Next `/gradial/assets` proxy no longer exists).
10
+ * - In production: CloudFront maps `/dam/*` to the S3 bucket
11
+ * containing the compiled assets. There is no app-level asset route
12
+ * (unlike Next.js which has `/gradial/assets/[releaseId]/[...path]`).
13
13
  * The assets are content-addressed (hashed filenames) so the CDN
14
14
  * can cache them with long TTLs.
15
15
  *
16
16
  * - At build time: `withAci`'s `astro:build:done` hook copies the
17
- * `.gradial-dam` directory from compiled content into the build output,
17
+ * `dam` directory from compiled content into the build output,
18
18
  * so `astro build` produces a self-contained artifact.
19
19
  *
20
20
  * This module provides a helper to construct asset URLs that work in both
@@ -25,15 +25,15 @@
25
25
  * Default asset URL prefix. Matches the Vite middleware path and the
26
26
  * provider gateway's asset path.
27
27
  */
28
- export declare const DAM_ASSET_PREFIX = "/.gradial-dam";
28
+ export declare const DAM_ASSET_PREFIX = "/dam";
29
29
  /**
30
30
  * Construct a DAM asset URL from a logical asset path.
31
31
  *
32
- * In both dev and production, DAM assets live under `/.gradial-dam/`.
32
+ * In both dev and production, DAM assets live under `/dam/`.
33
33
  * This function normalizes the path and prepends the prefix.
34
34
  *
35
35
  * @param logicalPath - The asset path as stored in content (e.g. "images/hero.webp")
36
- * @returns The full URL path (e.g. "/.gradial-dam/images/hero.webp")
36
+ * @returns The full URL path (e.g. "/dam/images/hero.webp")
37
37
  *
38
38
  * @example
39
39
  * ```astro
@@ -5,16 +5,16 @@
5
5
  * at the URL level. Instead:
6
6
  *
7
7
  * - In dev: the Vite `damAssetPlugin` serves DAM assets from compiled
8
- * content at `/.gradial-dam/{path}` (already handled by withAci).
8
+ * content at `/dam/{path}` (already handled by withAci).
9
9
  *
10
- * - In production: the provider host maps `/.gradial-dam/*` to authoritative
11
- * S3/R2/CDN storage. There is no app-level asset route
12
- * (the retired Next `/gradial/assets` proxy no longer exists).
10
+ * - In production: CloudFront maps `/dam/*` to the S3 bucket
11
+ * containing the compiled assets. There is no app-level asset route
12
+ * (unlike Next.js which has `/gradial/assets/[releaseId]/[...path]`).
13
13
  * The assets are content-addressed (hashed filenames) so the CDN
14
14
  * can cache them with long TTLs.
15
15
  *
16
16
  * - At build time: `withAci`'s `astro:build:done` hook copies the
17
- * `.gradial-dam` directory from compiled content into the build output,
17
+ * `dam` directory from compiled content into the build output,
18
18
  * so `astro build` produces a self-contained artifact.
19
19
  *
20
20
  * This module provides a helper to construct asset URLs that work in both
@@ -25,15 +25,15 @@
25
25
  * Default asset URL prefix. Matches the Vite middleware path and the
26
26
  * provider gateway's asset path.
27
27
  */
28
- export const DAM_ASSET_PREFIX = '/.gradial-dam';
28
+ export const DAM_ASSET_PREFIX = '/dam';
29
29
  /**
30
30
  * Construct a DAM asset URL from a logical asset path.
31
31
  *
32
- * In both dev and production, DAM assets live under `/.gradial-dam/`.
32
+ * In both dev and production, DAM assets live under `/dam/`.
33
33
  * This function normalizes the path and prepends the prefix.
34
34
  *
35
35
  * @param logicalPath - The asset path as stored in content (e.g. "images/hero.webp")
36
- * @returns The full URL path (e.g. "/.gradial-dam/images/hero.webp")
36
+ * @returns The full URL path (e.g. "/dam/images/hero.webp")
37
37
  *
38
38
  * @example
39
39
  * ```astro
@@ -94,10 +94,10 @@ export function withAci(options = {}) {
94
94
  'astro:build:done'({ dir }) {
95
95
  const siteDir = process.cwd();
96
96
  const compiledRoot = path.resolve(siteDir, options.compiledRoot || process.env.ACI_CONTENT_ROOT || '.aci/compiled');
97
- const source = path.join(compiledRoot, '.gradial-dam');
97
+ const source = path.join(compiledRoot, 'dam');
98
98
  if (!fs.existsSync(source))
99
99
  return;
100
- const target = path.join(dir.pathname, '.gradial-dam');
100
+ const target = path.join(dir.pathname, 'dam');
101
101
  fs.rmSync(target, { recursive: true, force: true });
102
102
  fs.cpSync(source, target, { recursive: true });
103
103
  },
@@ -336,6 +336,9 @@ export declare const experimentDataModel: import("./index.js").DataModelContract
336
336
  routes: z.ZodOptional<z.ZodArray<z.ZodString>>;
337
337
  fragments: z.ZodOptional<z.ZodArray<z.ZodString>>;
338
338
  }, z.core.$strip>;
339
+ eligibility: z.ZodOptional<z.ZodObject<{
340
+ audienceRef: z.ZodString;
341
+ }, z.core.$strict>>;
339
342
  primaryGoalRef: z.ZodOptional<z.ZodString>;
340
343
  primaryGoalId: z.ZodOptional<z.ZodString>;
341
344
  goalRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -426,6 +429,9 @@ export declare const experimentationDataModels: (import("./index.js").DataModelC
426
429
  routes: z.ZodOptional<z.ZodArray<z.ZodString>>;
427
430
  fragments: z.ZodOptional<z.ZodArray<z.ZodString>>;
428
431
  }, z.core.$strip>;
432
+ eligibility: z.ZodOptional<z.ZodObject<{
433
+ audienceRef: z.ZodString;
434
+ }, z.core.$strict>>;
429
435
  primaryGoalRef: z.ZodOptional<z.ZodString>;
430
436
  primaryGoalId: z.ZodOptional<z.ZodString>;
431
437
  goalRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -794,6 +800,9 @@ export declare const audienceDataModels: (import("./index.js").DataModelContract
794
800
  routes: z.ZodOptional<z.ZodArray<z.ZodString>>;
795
801
  fragments: z.ZodOptional<z.ZodArray<z.ZodString>>;
796
802
  }, z.core.$strip>;
803
+ eligibility: z.ZodOptional<z.ZodObject<{
804
+ audienceRef: z.ZodString;
805
+ }, z.core.$strict>>;
797
806
  primaryGoalRef: z.ZodOptional<z.ZodString>;
798
807
  primaryGoalId: z.ZodOptional<z.ZodString>;
799
808
  goalRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -127,6 +127,9 @@ export const experimentDataModel = defineDataModel({
127
127
  routes: z.array(z.string().min(1)).optional(),
128
128
  fragments: z.array(z.string().min(1)).optional(),
129
129
  }),
130
+ eligibility: z.object({
131
+ audienceRef: z.string().min(1),
132
+ }).strict().optional(),
130
133
  primaryGoalRef: z.string().optional(),
131
134
  primaryGoalId: z.string().optional(),
132
135
  goalRefs: z.array(z.string().min(1)).optional(),
@@ -1,2 +1,6 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ export function compileRegistry(options: any): Promise<{
3
+ registry: string;
4
+ schemas: string[];
5
+ }>;
6
+ export function generatePageSchema(schemaDir: any, componentEntries: any, outDir: any): Promise<string>;
@@ -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';
@@ -18,14 +19,16 @@ try {
18
19
  globalThis.React = React;
19
20
  }
20
21
  catch { /* react not available — component files without JSX will still work */ }
21
- const args = parseArgs(process.argv.slice(2));
22
- try {
23
- const result = await compileRegistry(args);
24
- console.log(JSON.stringify({ success: true, ...result, errors: [] }, null, 2));
25
- }
26
- catch (error) {
27
- console.error(JSON.stringify({ success: false, errors: [String(error?.message ?? error)] }, null, 2));
28
- process.exitCode = 1;
22
+ if (isMainModule()) {
23
+ const args = parseArgs(process.argv.slice(2));
24
+ try {
25
+ const result = await compileRegistry(args);
26
+ console.log(JSON.stringify({ success: true, ...result, errors: [] }, null, 2));
27
+ }
28
+ catch (error) {
29
+ console.error(JSON.stringify({ success: false, errors: [String(error?.message ?? error)] }, null, 2));
30
+ process.exitCode = 1;
31
+ }
29
32
  }
30
33
  async function compileRegistry(options) {
31
34
  const required = ['components', 'layouts', 'output'];
@@ -440,9 +443,10 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
440
443
  const defs = {
441
444
  baseBlock: {
442
445
  type: 'object',
443
- required: ['id', 'component', 'props'],
446
+ required: ['$type', 'id', 'component', 'props'],
444
447
  additionalProperties: true,
445
448
  properties: {
449
+ $type: { const: 'component' },
446
450
  id: { type: 'string', minLength: 1 },
447
451
  component: { type: 'string' },
448
452
  props: { type: 'object' },
@@ -757,3 +761,14 @@ function parseArgs(argv) {
757
761
  }
758
762
  return out;
759
763
  }
764
+ function isMainModule() {
765
+ if (!process.argv[1])
766
+ return false;
767
+ try {
768
+ return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(path.resolve(process.argv[1]));
769
+ }
770
+ catch {
771
+ return false;
772
+ }
773
+ }
774
+ export { compileRegistry, generatePageSchema };
@@ -1,2 +1,32 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ export function validateContent(options: any): Promise<{
3
+ warnings?: any[] | undefined;
4
+ success: boolean;
5
+ pagesValidated: number;
6
+ blocksValidated: number;
7
+ errors: ({
8
+ page: string;
9
+ message: string;
10
+ fragment?: undefined;
11
+ component?: undefined;
12
+ path?: undefined;
13
+ } | {
14
+ fragment: string;
15
+ message: string;
16
+ page?: undefined;
17
+ component?: undefined;
18
+ path?: undefined;
19
+ } | {
20
+ fragment: string;
21
+ component: any;
22
+ message: string;
23
+ page?: undefined;
24
+ path?: undefined;
25
+ } | {
26
+ fragment: string;
27
+ component: any;
28
+ path: any;
29
+ message: any;
30
+ page?: undefined;
31
+ })[];
32
+ }>;
@@ -19,21 +19,25 @@
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
- const args = parseArgs(process.argv.slice(2));
25
- try {
26
- const result = await validateContent(args);
27
- console.log(JSON.stringify(result, null, 2));
28
- if (!result.success)
25
+ import { fileURLToPath } from 'node:url';
26
+ if (isMainModule()) {
27
+ const args = parseArgs(process.argv.slice(2));
28
+ try {
29
+ const result = await validateContent(args);
30
+ console.log(JSON.stringify(result, null, 2));
31
+ if (!result.success)
32
+ process.exitCode = 1;
33
+ }
34
+ catch (error) {
35
+ console.error(JSON.stringify({
36
+ success: false,
37
+ errors: [{ message: String(error?.message ?? error) }],
38
+ }, null, 2));
29
39
  process.exitCode = 1;
30
- }
31
- catch (error) {
32
- console.error(JSON.stringify({
33
- success: false,
34
- errors: [{ message: String(error?.message ?? error) }],
35
- }, null, 2));
36
- process.exitCode = 1;
40
+ }
37
41
  }
38
42
  // ---------------------------------------------------------------------------
39
43
  // Main
@@ -86,6 +90,22 @@ async function validateContent(options) {
86
90
  const blocks = collectBlocks(pageData);
87
91
  for (const block of blocks) {
88
92
  blocksValidated++;
93
+ if (!block || typeof block !== 'object') {
94
+ collector.push({
95
+ page: relativePath,
96
+ blockId: '<unknown>',
97
+ message: 'page region entry must be a component block',
98
+ });
99
+ continue;
100
+ }
101
+ if (block.$type !== 'component') {
102
+ collector.push({
103
+ page: relativePath,
104
+ blockId: block.id ?? '<unknown>',
105
+ path: '$type',
106
+ message: 'block "$type" must be "component"',
107
+ });
108
+ }
89
109
  if (!block.component) {
90
110
  collector.push({
91
111
  page: relativePath,
@@ -348,6 +368,13 @@ function validateNestedSlots(parentBlock, componentMap, schemaMap, path = 'props
348
368
  issues.push({ path: nestedPath, message: `${parentBlock.component}: block ref child must be a block` });
349
369
  continue;
350
370
  }
371
+ if (nestedBlock.$type !== 'component') {
372
+ issues.push({
373
+ path: `${nestedPath}.$type`,
374
+ message: `${parentBlock.component}: block ref child "$type" must be "component"`,
375
+ });
376
+ continue;
377
+ }
351
378
  if (!componentMap.has(nestedBlock.component)) {
352
379
  issues.push({ path: `${nestedPath}.component`, message: `${parentBlock.component}: unknown nested component "${nestedBlock.component}"` });
353
380
  continue;
@@ -424,3 +451,14 @@ function parseArgs(argv) {
424
451
  }
425
452
  return out;
426
453
  }
454
+ function isMainModule() {
455
+ if (!process.argv[1])
456
+ return false;
457
+ try {
458
+ return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(path.resolve(process.argv[1]));
459
+ }
460
+ catch {
461
+ return false;
462
+ }
463
+ }
464
+ export { validateContent };
@@ -1,11 +1,16 @@
1
- import type { ContentProvider } from './provider.js';
1
+ import type { CompiledManifest, ContentProvider } from './provider.js';
2
2
  export declare function getCacheStats(): {
3
+ manifest: {
4
+ hits: number;
5
+ misses: number;
6
+ };
3
7
  siteConfig: {
4
8
  hits: number;
5
9
  misses: number;
6
10
  };
7
11
  };
8
12
  export declare function resetCacheStats(): void;
13
+ export declare function getCachedManifest(provider: ContentProvider): Promise<CompiledManifest>;
9
14
  /**
10
15
  * Get site config with caching. Use this instead of calling provider.getSiteConfig() directly.
11
16
  */
@@ -5,19 +5,44 @@
5
5
  *
6
6
  * Keyed by provider instance for automatic invalidation when its input changes.
7
7
  */
8
+ const manifestCache = new WeakMap();
8
9
  const siteConfigCache = new WeakMap();
9
10
  // Cache hit/miss stats for observability
11
+ let manifestHits = 0;
12
+ let manifestMisses = 0;
10
13
  let siteConfigHits = 0;
11
14
  let siteConfigMisses = 0;
12
15
  export function getCacheStats() {
13
16
  return {
17
+ manifest: { hits: manifestHits, misses: manifestMisses },
14
18
  siteConfig: { hits: siteConfigHits, misses: siteConfigMisses },
15
19
  };
16
20
  }
17
21
  export function resetCacheStats() {
22
+ manifestHits = 0;
23
+ manifestMisses = 0;
18
24
  siteConfigHits = 0;
19
25
  siteConfigMisses = 0;
20
26
  }
27
+ // getCachedManifest is intentionally capability based: the general content
28
+ // interface does not require a manifest, while release-backed providers do.
29
+ export function getCachedManifest(provider) {
30
+ const cached = manifestCache.get(provider);
31
+ if (cached) {
32
+ manifestHits++;
33
+ return cached;
34
+ }
35
+ if (!('manifest' in provider) || typeof provider.manifest !== 'function') {
36
+ throw new Error('Provider does not support manifest()');
37
+ }
38
+ manifestMisses++;
39
+ const promise = provider.manifest().catch((error) => {
40
+ manifestCache.delete(provider);
41
+ throw error;
42
+ });
43
+ manifestCache.set(provider, promise);
44
+ return promise;
45
+ }
21
46
  /**
22
47
  * Get site config with caching. Use this instead of calling provider.getSiteConfig() directly.
23
48
  */
@@ -44,5 +69,6 @@ export function getCachedSiteConfig(provider) {
44
69
  * Useful for testing or manual cache invalidation.
45
70
  */
46
71
  export function clearProviderCache(provider) {
72
+ manifestCache.delete(provider);
47
73
  siteConfigCache.delete(provider);
48
74
  }
@@ -21,6 +21,7 @@ export interface FragmentEntry {
21
21
  }
22
22
  export interface LayoutEntry {
23
23
  name: string;
24
+ payloadRef: string;
24
25
  slots: LayoutSlot[];
25
26
  defaults?: Record<string, FragmentRef[]>;
26
27
  }
@@ -1,9 +1,16 @@
1
1
  import type { LayoutEntry } from './provider.js';
2
2
  import type { KernelPageBlock } from './types.js';
3
- export interface FragmentData {
4
- id: string;
3
+ export interface FragmentBlock {
4
+ $type: 'component';
5
5
  component: string;
6
6
  props?: Record<string, unknown>;
7
7
  }
8
+ export interface FragmentData {
9
+ fragmentId?: string;
10
+ id?: string;
11
+ block?: FragmentBlock;
12
+ component?: string;
13
+ props?: Record<string, unknown>;
14
+ }
8
15
  export type FragmentLoader = (id: string) => Promise<FragmentData>;
9
16
  export declare function resolveSlots(layout: LayoutEntry, pageRegions: Record<string, KernelPageBlock[]>, getFragment: FragmentLoader): Promise<Record<string, KernelPageBlock[]>>;
@@ -11,10 +11,12 @@ export async function resolveSlots(layout, pageRegions, getFragment) {
11
11
  const blocks = [];
12
12
  for (const ref of defaults) {
13
13
  const fragment = await getFragment(ref.fragmentId);
14
+ const block = fragment.block ?? { component: fragment.component ?? '', props: fragment.props };
14
15
  blocks.push({
15
- id: fragment.id,
16
- component: fragment.component,
17
- props: fragment.props || {},
16
+ $type: 'component',
17
+ id: fragment.id ?? fragment.fragmentId ?? ref.fragmentId,
18
+ component: block.component,
19
+ props: block.props || {},
18
20
  });
19
21
  }
20
22
  resolved[slot.name] = blocks;
@@ -42,11 +42,13 @@ export interface KernelPageSeo {
42
42
  canonical?: string;
43
43
  }
44
44
  export interface KernelPageBlock {
45
+ $type: 'component';
45
46
  id: string;
46
47
  component: string;
47
48
  props: Record<string, unknown>;
48
49
  }
49
50
  export interface KernelAtomicBlock {
51
+ $type: 'component';
50
52
  id: string;
51
53
  component: string;
52
54
  props: Record<string, unknown>;
@@ -95,7 +95,7 @@ export function damAssetPlugin(options = {}) {
95
95
  configureServer(server) {
96
96
  if (!server.middlewares)
97
97
  return;
98
- server.middlewares.use('/.gradial-dam', async (req, res, next) => {
98
+ server.middlewares.use('/dam', async (req, res, next) => {
99
99
  const root = path.resolve(options.compiledRoot || compiledContentRoot());
100
100
  const requestPath = safeRequestPath(req.url || '');
101
101
  if (!requestPath) {
@@ -103,7 +103,7 @@ export function damAssetPlugin(options = {}) {
103
103
  res.end('asset not found');
104
104
  return;
105
105
  }
106
- const filePath = path.resolve(root, '.gradial-dam', requestPath.replace(/^\/+/, ''));
106
+ const filePath = path.resolve(root, 'dam', requestPath.replace(/^\/+/, ''));
107
107
  if (!isInside(root, filePath)) {
108
108
  res.statusCode = 404;
109
109
  res.end('asset not found');
@@ -127,7 +127,7 @@ function safeRequestPath(url) {
127
127
  const rawPath = url.split('?')[0] || '';
128
128
  try {
129
129
  const decoded = decodeURIComponent(rawPath);
130
- return decoded.startsWith('/.gradial-dam/') ? decoded.slice('/.gradial-dam/'.length) : decoded;
130
+ return decoded.startsWith('/dam/') ? decoded.slice('/dam/'.length) : decoded;
131
131
  }
132
132
  catch {
133
133
  return '';
@@ -0,0 +1,11 @@
1
+ export function selectPersonalizationVariant(request: any, route: any, claims: any, experiments: any, identity: any): any;
2
+ export function resolvePersonalizationDecision(request: any, route: any, claims: any, experiments: any, identity: any): {
3
+ variant: any;
4
+ experiment: {
5
+ experimentId: any;
6
+ armId: any;
7
+ allocationVersion: string;
8
+ variantKey: any;
9
+ bucket: number;
10
+ } | null;
11
+ };