@gradial/aci 0.1.52-rc.8 → 0.1.52

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 (254) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +86 -5
  3. package/dist/astro/error-page.js +1 -0
  4. package/dist/astro/index.js +4 -0
  5. package/dist/astro/page.d.ts +5 -2
  6. package/dist/astro/page.js +35 -23
  7. package/dist/astro/preview.js +2 -17
  8. package/dist/audience-data-models.d.ts +308 -197
  9. package/dist/audience-data-models.js +313 -51
  10. package/dist/cli/compile-registry.mjs +6 -34
  11. package/dist/cli/mintlify-render.mjs +8 -0
  12. package/dist/collect/aci.js +1 -1
  13. package/dist/collect/aci.js.sha256 +1 -1
  14. package/dist/collect/api.d.ts +6 -7
  15. package/dist/collect/api.js +21 -8
  16. package/dist/collect/assignments.d.ts +7 -9
  17. package/dist/collect/assignments.js +23 -32
  18. package/dist/collect/batcher.d.ts +9 -7
  19. package/dist/collect/batcher.js +6 -5
  20. package/dist/collect/consent/envelope.d.ts +7 -1
  21. package/dist/collect/consent/envelope.js +3 -1
  22. package/dist/collect/consent/mapping.d.ts +2 -0
  23. package/dist/collect/consent/mapping.js +1 -0
  24. package/dist/collect/consenthold.d.ts +38 -0
  25. package/dist/collect/consenthold.js +82 -0
  26. package/dist/collect/declarative.js +7 -5
  27. package/dist/collect/gapbuffer.d.ts +13 -4
  28. package/dist/collect/gapbuffer.js +21 -10
  29. package/dist/collect/index.js +78 -21
  30. package/dist/collect/pageviews.d.ts +11 -11
  31. package/dist/collect/pageviews.js +27 -22
  32. package/dist/consent-banner/index.d.ts +17 -0
  33. package/dist/consent-banner/index.js +23 -0
  34. package/dist/content/index.d.ts +2 -1
  35. package/dist/content/index.js +4 -1
  36. package/dist/content/provider.d.ts +16 -2
  37. package/dist/content/provider.js +5 -1
  38. package/dist/content/resolve-slots.d.ts +24 -2
  39. package/dist/content/resolve-slots.js +127 -21
  40. package/dist/content/types.d.ts +5 -1
  41. package/dist/edge/expression-capabilities.v1.d.ts +106 -0
  42. package/dist/edge/expression-capabilities.v1.js +165 -0
  43. package/dist/index.d.ts +1 -2
  44. package/dist/index.js +0 -1
  45. package/dist/internal/domain/edge/identity.d.ts +47 -4
  46. package/dist/internal/domain/edge/identity.js +130 -26
  47. package/dist/internal/domain/edge/index.d.ts +1 -1
  48. package/dist/internal/domain/edge/index.js +1 -1
  49. package/dist/internal/domain/edge/mimic_token.d.ts +14 -0
  50. package/dist/internal/domain/edge/mimic_token.js +125 -0
  51. package/dist/internal/domain/edge/personalization-core.d.ts +61 -22
  52. package/dist/internal/domain/edge/personalization-core.js +790 -200
  53. package/dist/internal/domain/edge/personalization_index.d.ts +17 -0
  54. package/dist/internal/domain/edge/personalization_index.js +55 -0
  55. package/dist/internal/domain/edge/resolve.js +76 -39
  56. package/dist/internal/domain/edge/testdata/flat_only_validator_v1.d.ts +10 -0
  57. package/dist/internal/domain/edge/testdata/flat_only_validator_v1.js +253 -0
  58. package/dist/internal/domain/edge/types.d.ts +82 -78
  59. package/dist/internal/domain/edge/types.js +3 -3
  60. package/dist/internal/domain/edge/variant_key.d.ts +4 -0
  61. package/dist/internal/domain/edge/variant_key.js +56 -0
  62. package/dist/internal/platform/collectionstore/errors.d.ts +24 -0
  63. package/dist/internal/platform/collectionstore/errors.js +48 -0
  64. package/dist/mintlify/blocks.d.ts +6 -0
  65. package/dist/mintlify/blocks.js +201 -0
  66. package/dist/mintlify/config.d.ts +3 -0
  67. package/dist/mintlify/config.js +77 -0
  68. package/dist/mintlify/index.d.ts +3 -0
  69. package/dist/mintlify/index.js +2 -0
  70. package/dist/mintlify/json.d.ts +8 -0
  71. package/dist/mintlify/json.js +37 -0
  72. package/dist/mintlify/materialize.d.ts +14 -0
  73. package/dist/mintlify/materialize.js +254 -0
  74. package/dist/mintlify/types.d.ts +74 -0
  75. package/dist/mintlify/types.js +1 -0
  76. package/dist/mintlify/validate.d.ts +7 -0
  77. package/dist/mintlify/validate.js +256 -0
  78. package/dist/next/assignments-navigation.d.ts +4 -0
  79. package/dist/next/assignments-navigation.js +18 -0
  80. package/dist/next/assignments-script.d.ts +3 -2
  81. package/dist/next/assignments-script.js +5 -5
  82. package/dist/next/error-pages.d.ts +4 -0
  83. package/dist/next/error-pages.js +4 -0
  84. package/dist/next/error-seat-payload.d.ts +2 -1
  85. package/dist/next/error-seat-payload.js +14 -24
  86. package/dist/next/error-seat.js +7 -2
  87. package/dist/next/index.d.ts +1 -1
  88. package/dist/next/index.js +3 -1
  89. package/dist/next/page.d.ts +2 -1
  90. package/dist/next/page.js +15 -4
  91. package/dist/next/release-context.d.ts +30 -0
  92. package/dist/next/release-context.js +56 -0
  93. package/dist/next/render.d.ts +1 -1
  94. package/dist/next/render.js +15 -5
  95. package/dist/next/server.d.ts +9 -3
  96. package/dist/next/server.js +17 -32
  97. package/dist/preview/core.d.ts +1 -0
  98. package/dist/preview/core.js +27 -0
  99. package/dist/react/VideoPlayer.d.ts +3 -3
  100. package/dist/react/index.d.ts +3 -3
  101. package/dist/registry.d.ts +4 -2
  102. package/dist/testing/conformance/checks.d.ts +2 -2
  103. package/dist/testing/conformance/checks.js +2 -0
  104. package/dist/testing/conformance/index.d.ts +1 -0
  105. package/dist/testing/conformance/index.js +1 -0
  106. package/dist/testing/conformance/next-stubs.d.ts +6 -0
  107. package/dist/testing/conformance/next-stubs.js +9 -0
  108. package/dist/testing/conformance/preset.d.ts +3 -2
  109. package/dist/testing/conformance/preset.js +15 -8
  110. package/dist/testing/conformance/root-identity.d.ts +31 -0
  111. package/dist/testing/conformance/root-identity.js +361 -0
  112. package/dist/testing/conformance/stubs/next-image.d.ts +7 -0
  113. package/dist/testing/conformance/stubs/next-image.js +5 -0
  114. package/dist/testing/conformance/stubs/next-link.d.ts +7 -0
  115. package/dist/testing/conformance/stubs/next-link.js +4 -0
  116. package/dist/testing/conformance/types.d.ts +5 -2
  117. package/dist/testing/index.d.ts +1 -1
  118. package/dist/testing/index.js +4 -3
  119. package/dist/types/block-ref.js +2 -2
  120. package/dist/types/component.d.ts +9 -0
  121. package/dist/types/config.d.ts +0 -1
  122. package/dist/types/index.d.ts +0 -1
  123. package/dist/types/index.js +0 -1
  124. package/dist/types/page.d.ts +0 -1
  125. package/migrations/conformance-registry.md +22 -0
  126. package/migrations/experiment-goal-ids.md +43 -0
  127. package/package.json +21 -7
  128. package/src/cli/compile-registry.mjs +6 -30
  129. package/src/cli/mintlify-render.mjs +9 -0
  130. package/src/types/block-ref.test.ts +16 -1
  131. package/src/types/block-ref.ts +2 -2
  132. package/src/types/component.ts +10 -0
  133. package/src/types/config.ts +0 -1
  134. package/src/types/index.ts +0 -1
  135. package/src/types/page.ts +0 -1
  136. package/templates/astro/starter-content/src/components/chrome/Footer.astro +5 -3
  137. package/templates/astro/starter-content/src/components/chrome/Header.astro +5 -3
  138. package/templates/astro/starter-content/src/components/elements/Button.astro +5 -2
  139. package/templates/astro/starter-content/src/components/elements/ButtonBlock.astro +5 -3
  140. package/templates/astro/starter-content/src/components/sections/ContainerSection.astro +4 -3
  141. package/templates/astro/starter-content/src/components/sections/FeatureCard.astro +4 -3
  142. package/templates/astro/starter-content/src/components/sections/FeatureGrid.astro +4 -3
  143. package/templates/astro/starter-content/src/components/sections/HomeHero.astro +5 -3
  144. package/templates/astro/starter-content/src/components/sections/PersonalizationChoice.astro +4 -3
  145. package/templates/astro/starter-content/src/components/sections/StatsBar.astro +4 -3
  146. package/templates/astro/starter-content/src/fixtures/sections.ts +1 -1
  147. package/templates/astro/starter-content/src/stories/CompositionStoryFrame.astro +7 -2
  148. package/templates/astro/template/.aci.yaml.tmpl +0 -1
  149. package/templates/astro/template/package.json.tmpl +5 -5
  150. package/templates/astro/template/src/layouts/SiteLayout.astro +2 -2
  151. package/templates/common/.agents/skills/aci/SKILL.md +58 -1
  152. package/templates/common/.agents/skills/authoring/SKILL.md +194 -42
  153. package/templates/common/.agents/skills/authoring/references/block-registry.md +19 -3
  154. package/templates/common/.agents/skills/authoring/references/contracts.md +9 -30
  155. package/templates/common/.agents/skills/authoring/references/repo-structure.md +5 -4
  156. package/templates/common/.agents/skills/authoring/references/sdk-server-functions.md +3 -3
  157. package/templates/common/.agents/skills/dam-delivery/SKILL.md +216 -0
  158. package/templates/common/.agents/skills/dam-delivery/references/permalink-grammar.md +167 -0
  159. package/templates/common/.agents/skills/migration/references/canon-migration.md +18 -67
  160. package/templates/common/.agents/skills/migration/references/selector-map.md +1 -1
  161. package/templates/common/.content/fragments/navbar/_index.json +24 -0
  162. package/templates/common/.content/fragments/site-footer/_index.json +61 -0
  163. package/templates/common/.content/pages/404/_index.json +4 -2
  164. package/templates/common/.content/pages/_index.json +0 -1
  165. package/templates/common/.content/pages/error/_index.json +4 -2
  166. package/templates/common/.content/schemas/site.schema.json +11 -1
  167. package/templates/common/.content/templates/error.json +24 -0
  168. package/templates/common/.content/templates/home.json +28 -0
  169. package/templates/common/.content/templates/site.json +8 -0
  170. package/templates/common/.content/templates/standard.json +24 -0
  171. package/templates/common/.nvmrc +1 -1
  172. package/templates/common/starter-content/.content/config/site.json +1 -1
  173. package/templates/common/starter-content/.content/dam/README.md +74 -0
  174. package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000101}/asset.json +1 -1
  175. package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000102}/asset.json +1 -1
  176. package/templates/common/starter-content/.content/pages/404/_index.json +1 -2
  177. package/templates/common/starter-content/.content/pages/{home/_index.json → _index.json} +14 -3
  178. package/templates/common/starter-content/.content/pages/about/_index.json +2 -2
  179. package/templates/common/starter-content/.content/pages/error/_index.json +1 -2
  180. package/templates/common/starter-content/.content/patterns/components/button.json +2 -2
  181. package/templates/common/starter-content/.content/patterns/components/container.json +8 -3
  182. package/templates/common/starter-content/.content/patterns/components/feature_card.json +2 -2
  183. package/templates/common/starter-content/.content/patterns/components/feature_grid.json +5 -3
  184. package/templates/common/starter-content/.content/patterns/components/home_hero.json +2 -2
  185. package/templates/common/starter-content/.content/patterns/components/personalization_choice.json +2 -2
  186. package/templates/common/starter-content/.content/patterns/components/site-footer.json +2 -2
  187. package/templates/common/starter-content/.content/patterns/components/site-navigation.json +2 -2
  188. package/templates/common/starter-content/.content/patterns/components/stats_bar.json +2 -2
  189. package/templates/common/starter-content/.content/schemas/site.schema.json +11 -1
  190. package/templates/nextjs/starter-content/src/components/chrome/Footer.tsx +4 -3
  191. package/templates/nextjs/starter-content/src/components/chrome/Header.tsx +4 -3
  192. package/templates/nextjs/starter-content/src/components/elements/Button.tsx +4 -3
  193. package/templates/nextjs/starter-content/src/components/elements/ButtonBlock.tsx +4 -3
  194. package/templates/nextjs/starter-content/src/components/sections/ContainerSection.tsx +4 -2
  195. package/templates/nextjs/starter-content/src/components/sections/FeatureCard.tsx +4 -3
  196. package/templates/nextjs/starter-content/src/components/sections/FeatureGrid.tsx +4 -2
  197. package/templates/nextjs/starter-content/src/components/sections/HomeHero.tsx +4 -3
  198. package/templates/nextjs/starter-content/src/components/sections/PersonalizationChoice.tsx +4 -2
  199. package/templates/nextjs/starter-content/src/components/sections/StatsBar.tsx +4 -3
  200. package/templates/nextjs/template/.aci.yaml +0 -1
  201. package/templates/nextjs/template/.aci.yaml.tmpl +0 -1
  202. package/templates/nextjs/template/package.json.tmpl +4 -3
  203. package/templates/nextjs/template/src/app/_components/c15t-bridge.ts +30 -0
  204. package/templates/nextjs/template/src/app/_components/c15t-consent-ui.tsx +37 -0
  205. package/templates/nextjs/template/src/app/_components/consent-manager.css +63 -0
  206. package/templates/nextjs/template/src/app/_components/consent-manager.tsx +76 -0
  207. package/templates/nextjs/template/src/app/fonts/inter/OFL.txt +93 -0
  208. package/templates/nextjs/template/src/app/fonts/inter/inter-latin.woff2 +0 -0
  209. package/templates/nextjs/template/src/app/fonts/space-grotesk/OFL.txt +93 -0
  210. package/templates/nextjs/template/src/app/fonts/space-grotesk/space-grotesk-latin.woff2 +0 -0
  211. package/templates/nextjs/template/src/app/layout.tsx +12 -7
  212. package/templates/nextjs/template/tests/conformance.test.ts.tmpl +1 -0
  213. package/dist/define-layout.d.ts +0 -6
  214. package/dist/define-layout.js +0 -14
  215. package/dist/edge/personalization-core.d.ts +0 -11
  216. package/dist/edge/personalization-core.js +0 -269
  217. package/dist/internal/domain/edge/compose.d.ts +0 -24
  218. package/dist/internal/domain/edge/compose.js +0 -89
  219. package/dist/internal/domain/edge/record/codec.d.ts +0 -71
  220. package/dist/internal/domain/edge/record/codec.js +0 -401
  221. package/dist/next/collect-route.d.ts +0 -19
  222. package/dist/next/collect-route.js +0 -459
  223. package/dist/next/edge-config.d.ts +0 -1
  224. package/dist/next/edge-config.js +0 -92
  225. package/dist/next/envelope-digest.d.ts +0 -21
  226. package/dist/next/envelope-digest.js +0 -58
  227. package/dist/next/middleware.d.ts +0 -40
  228. package/dist/next/middleware.js +0 -475
  229. package/dist/next/personalization-host.d.ts +0 -134
  230. package/dist/next/personalization-host.js +0 -270
  231. package/dist/next/root-layout.d.ts +0 -4
  232. package/dist/next/root-layout.js +0 -5
  233. package/dist/next/vercel-collect-sdk-route.d.ts +0 -27
  234. package/dist/next/vercel-collect-sdk-route.js +0 -181
  235. package/dist/next/vercel-edge-projection-v1.d.ts +0 -24
  236. package/dist/next/vercel-edge-projection-v1.js +0 -880
  237. package/dist/next/vercel-personalization.d.ts +0 -4
  238. package/dist/next/vercel-personalization.js +0 -4
  239. package/dist/next/vercel-s3-personalization.d.ts +0 -79
  240. package/dist/next/vercel-s3-personalization.js +0 -570
  241. package/dist/types/layout.d.ts +0 -24
  242. package/src/types/layout.ts +0 -29
  243. package/templates/astro/starter-content/src/render/RenderPage.astro +0 -67
  244. package/templates/astro/template/src/cms/contracts/layouts/index.ts +0 -18
  245. package/templates/common/starter-content/.content/patterns/layouts/default.json +0 -14
  246. package/templates/nextjs/starter-content/src/components/elements/AtomicBlock.tsx +0 -13
  247. package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +0 -16
  248. /package/dist/{types/layout.js → cli/mintlify-render.d.mts} +0 -0
  249. /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000101}/editorial.json +0 -0
  250. /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000101}/governance.json +0 -0
  251. /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000101}/versions/v0001.json +0 -0
  252. /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000102}/editorial.json +0 -0
  253. /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000102}/governance.json +0 -0
  254. /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000102}/versions/v0001.json +0 -0
@@ -1,17 +1,27 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export async function renderBlock(block, registry, extraProps) {
2
+ export async function renderBlock(block, registry, key, extraProps) {
3
3
  const entry = registry.find((r) => r.id === block.component);
4
4
  if (!entry) {
5
- 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));
5
+ 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] })] }, key));
6
6
  }
7
7
  const Component = entry.component;
8
8
  const hasBlockSlots = !!entry.contract.blockSlots;
9
- const componentProps = { ...block.props, ...extraProps };
9
+ // sdk I-19 (ADR 0051): root identity props are applied last so neither
10
+ // content props nor tenant slotProps can overwrite them.
11
+ const componentProps = {
12
+ ...block.props,
13
+ ...extraProps,
14
+ 'data-aci-block': block.id,
15
+ 'data-aci-component': block.component,
16
+ };
10
17
  if (hasBlockSlots) {
11
18
  componentProps.renderChildren = (blocks) => renderBlocks(blocks, registry, extraProps);
12
19
  }
13
- return _jsx(Component, { ...componentProps }, block.id);
20
+ return _jsx(Component, { ...componentProps }, key);
14
21
  }
15
22
  export async function renderBlocks(blocks, registry, extraProps) {
16
- return Promise.all(blocks.map((block) => renderBlock(block, registry, extraProps)));
23
+ // Sibling keys are index+id composites: one fragment placed twice repeats
24
+ // its authored block id, and React reconciliation keys must be unique per
25
+ // sibling (ADR 0051 covers analytics identity, not reconciliation).
26
+ return Promise.all(blocks.map((block, index) => renderBlock(block, registry, `${index}:${block.id}`, extraProps)));
17
27
  }
@@ -3,7 +3,6 @@ import { type ReleaseObjectReader } from '../providers/object.js';
3
3
  import type { RenderInput } from '../content/types.js';
4
4
  export { FragmentNotFoundError, PageNotFoundError, LayoutNotFoundError };
5
5
  export interface FetchConfig {
6
- siteId?: string;
7
6
  bucket?: string;
8
7
  keyPrefix?: string;
9
8
  region?: string;
@@ -13,8 +12,6 @@ export interface FetchConfig {
13
12
  roleArn?: string;
14
13
  endpoint?: string;
15
14
  releaseId?: string;
16
- edgeConfig?: string;
17
- deploymentId?: string;
18
15
  }
19
16
  export interface ServerRenderInput<TPage, TSite extends SiteConfig = SiteConfig> {
20
17
  route: string;
@@ -54,4 +51,13 @@ export declare class DamAssetNotFoundError extends Error {
54
51
  constructor(assetPath: string, cause?: unknown);
55
52
  }
56
53
  export declare function getDamAssetResponse(assetPath: string, config?: FetchConfig): Promise<Response>;
54
+ /**
55
+ * The release a read uses: the caller's explicit release (the catch-all passes
56
+ * the one its `/__r/{releaseId}` path names), else the release the hosting
57
+ * router dispatched this request to (release-context.ts — the same release,
58
+ * for code such as the not-found seat that receives no route params), else a
59
+ * process pin. A routed release outranks the pin because the catch-all of the
60
+ * same request renders the routed release regardless of the pin. There is no
61
+ * further source: without one of the three the read fails.
62
+ */
57
63
  export declare function resolveReleaseId(config?: FetchConfig): Promise<string>;
@@ -7,8 +7,7 @@ import { S3ContentProvider } from '../providers/s3.js';
7
7
  import { VercelReleaseObjectReader } from '../providers/vercel-object-reader.js';
8
8
  import { GRADIAL_RENDER_INPUT_HEADER, getPendingRenderInput } from '../runtime/page.js';
9
9
  import { attachRenderProofReceipt, selectVerifiedPage, } from '../internal/render-proof.js';
10
- import { getUncachedEdgeConfigValue } from './edge-config.js';
11
- import { activeReleaseV1Key, clampDeploymentCompatibleReleaseV1, decodeActiveReleaseV1, decodeDeploymentCompatibilityV1, deploymentCompatibilityKey, } from './vercel-edge-projection-v1.js';
10
+ import { routedRelease } from './release-context.js';
12
11
  export { FragmentNotFoundError, PageNotFoundError, LayoutNotFoundError };
13
12
  const releaseObjectReaderSymbol = Symbol.for('@gradial/aci.release-object-reader');
14
13
  const objectProviders = new WeakMap();
@@ -44,8 +43,9 @@ export async function getFragment(name, config = {}) {
44
43
  const runtimeInput = await getPageRuntimeRenderInput();
45
44
  if (runtimeInput) {
46
45
  const fragments = runtimeInput.fragments || {};
47
- if (name in fragments)
46
+ if (Object.hasOwn(fragments, name))
48
47
  return fragments[name];
48
+ throw new FragmentNotFoundError(name, new Error('fragment is not bound by the selected page'));
49
49
  }
50
50
  const provider = await resolveProviderWithFallback(config);
51
51
  return await provider.getFragment(name);
@@ -291,15 +291,25 @@ async function safeResponseText(res) {
291
291
  return '';
292
292
  }
293
293
  }
294
+ /**
295
+ * The release a read uses: the caller's explicit release (the catch-all passes
296
+ * the one its `/__r/{releaseId}` path names), else the release the hosting
297
+ * router dispatched this request to (release-context.ts — the same release,
298
+ * for code such as the not-found seat that receives no route params), else a
299
+ * process pin. A routed release outranks the pin because the catch-all of the
300
+ * same request renders the routed release regardless of the pin. There is no
301
+ * further source: without one of the three the read fails.
302
+ */
294
303
  export async function resolveReleaseId(config = {}) {
295
304
  if (config.releaseId)
296
305
  return config.releaseId;
306
+ const routed = await routedRelease();
307
+ if (routed.releaseId)
308
+ return routed.releaseId;
297
309
  if (process.env.ACI_RELEASE_ID)
298
310
  return process.env.ACI_RELEASE_ID;
299
- const activeReleaseId = await activeReleaseFromEdgeConfig(config);
300
- if (activeReleaseId)
301
- return activeReleaseId;
302
- throw new Error('ACI release ID is required. Pass releaseId from the release-scoped route or set ACI_RELEASE_ID.');
311
+ throw new Error('ACI release ID is required. Pass releaseId from the release-scoped route or set ACI_RELEASE_ID.'
312
+ + (routed.rejected ? ` The request carried a release context that was not used: ${routed.rejected}.` : ''));
303
313
  }
304
314
  async function resolveS3Provider(config) {
305
315
  const releaseId = await resolveReleaseId(config);
@@ -386,34 +396,9 @@ function sameS3ProviderConfig(left, right) {
386
396
  && left.roleArn === right.roleArn
387
397
  && left.endpoint === right.endpoint;
388
398
  }
389
- async function activeReleaseFromEdgeConfig(config) {
390
- const edgeConfig = config.edgeConfig || process.env.EDGE_CONFIG || '';
391
- const siteId = config.siteId || process.env.ACI_SITE_ID || '';
392
- if (!edgeConfig || !siteId)
393
- return '';
394
- const active = await activeReleasePointer(edgeConfig, siteId);
395
- if (active == null)
396
- return '';
397
- const deploymentId = config.deploymentId || process.env.VERCEL_DEPLOYMENT_ID || '';
398
- if (!deploymentId)
399
- return active.releaseId;
400
- const compatibilityValue = await getEdgeConfigValue(edgeConfig, deploymentCompatibilityKey(siteId, deploymentId));
401
- if (compatibilityValue == null)
402
- return active.releaseId;
403
- return clampDeploymentCompatibleReleaseV1(active, decodeDeploymentCompatibilityV1(compatibilityValue));
404
- }
405
- async function getEdgeConfigValue(edgeConfig, key) {
406
- return getUncachedEdgeConfigValue(edgeConfig, key);
407
- }
408
399
  function releaseKey(config, releaseId) {
409
400
  return joinKey(config.keyPrefix || process.env.ACI_S3_KEY_PREFIX || '', 'releases', releaseId);
410
401
  }
411
- async function activeReleasePointer(edgeConfig, siteId) {
412
- const value = await getEdgeConfigValue(edgeConfig, activeReleaseV1Key(siteId));
413
- if (value == null)
414
- return null;
415
- return decodeActiveReleaseV1(value);
416
- }
417
402
  class InjectedReaderProvider {
418
403
  releaseId;
419
404
  reader;
@@ -37,6 +37,7 @@ export interface PreviewCookie {
37
37
  export declare const PREVIEW_TOKEN_COOKIE = "aci_preview";
38
38
  export declare const PREVIEW_RELEASE_COOKIE = "aci_preview_release";
39
39
  export declare function validatePreviewToken(token: string, signKey: string): Promise<PreviewClaims | null>;
40
+ export declare function parseCookies(header: string): Map<string, string>;
40
41
  export declare function extractPreviewToken(source: PreviewTokenSource): string | undefined;
41
42
  export declare function previewSignKey(): string | undefined;
42
43
  export declare function resolvePreviewContext(options: PreviewResolveOptions): Promise<PreviewContext>;
@@ -18,6 +18,33 @@ export async function validatePreviewToken(token, signKey) {
18
18
  return null;
19
19
  }
20
20
  }
21
+ // parseCookies is the SDK-side raw Cookie-header parser for callers that only
22
+ // have the header string (the Astro integration; Next hands us its own parsed
23
+ // cookies). It is deliberately not the canonical edge parser
24
+ // (internal/domain/edge/identity.ts parseCookieHeader): SDK source cannot
25
+ // import internal/domain/edge — assemble-dist re-roots the emitted tree and
26
+ // the relative specifier depth changes — and framework cookie writers
27
+ // URI-encode values, so this one decodes them (falling back to the raw value
28
+ // on a malformed escape rather than throwing mid-request).
29
+ export function parseCookies(header) {
30
+ const cookies = new Map();
31
+ for (const part of header.split(';')) {
32
+ const separator = part.indexOf('=');
33
+ if (separator < 0)
34
+ continue;
35
+ const name = part.slice(0, separator).trim();
36
+ if (!name)
37
+ continue;
38
+ const value = part.slice(separator + 1).trim();
39
+ try {
40
+ cookies.set(name, decodeURIComponent(value));
41
+ }
42
+ catch {
43
+ cookies.set(name, value);
44
+ }
45
+ }
46
+ return cookies;
47
+ }
21
48
  export function extractPreviewToken(source) {
22
49
  const query = source.query?.get('preview_token');
23
50
  if (query)
@@ -11,21 +11,20 @@ export interface VideoPlayerProps extends Omit<VideoHTMLAttributes<HTMLVideoElem
11
11
  */
12
12
  export declare function VideoPlayer({ video, ...attrs }: VideoPlayerProps): import("react").DetailedReactHTMLElement<{
13
13
  id?: string | undefined | undefined;
14
+ title: string;
14
15
  role?: import("react").AriaRole | undefined;
15
16
  prefix?: string | undefined | undefined;
16
- title: string;
17
17
  rel?: string | undefined | undefined;
18
18
  content?: string | undefined | undefined;
19
19
  slot?: string | undefined | undefined;
20
20
  style?: import("react").CSSProperties | undefined;
21
21
  dir?: string | undefined | undefined;
22
22
  hidden?: boolean | undefined | undefined;
23
+ children?: import("react").ReactNode;
23
24
  dangerouslySetInnerHTML?: {
24
25
  __html: string | TrustedHTML;
25
26
  } | undefined | undefined;
26
- children?: import("react").ReactNode;
27
27
  className?: string | undefined | undefined;
28
- lang?: string | undefined | undefined;
29
28
  crossOrigin?: "" | "anonymous" | "use-credentials" | undefined;
30
29
  defaultChecked?: boolean | undefined | undefined;
31
30
  defaultValue?: string | number | readonly string[] | undefined;
@@ -38,6 +37,7 @@ export declare function VideoPlayer({ video, ...attrs }: VideoPlayerProps): impo
38
37
  contextMenu?: string | undefined | undefined;
39
38
  draggable?: (boolean | "true" | "false") | undefined;
40
39
  enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined | undefined;
40
+ lang?: string | undefined | undefined;
41
41
  nonce?: string | undefined | undefined;
42
42
  spellCheck?: (boolean | "true" | "false") | undefined;
43
43
  tabIndex?: number | undefined | undefined;
@@ -13,6 +13,7 @@ export interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, 's
13
13
  }
14
14
  export declare function Image({ image, value, ...attrs }: ImageProps): import("react").DetailedReactHTMLElement<import("../types/image.js").ImageAttrs, HTMLElement> | import("react").DetailedReactHTMLElement<{
15
15
  id?: string | undefined | undefined;
16
+ title?: string | undefined | undefined;
16
17
  role?: import("react").AriaRole | undefined;
17
18
  prefix?: string | undefined | undefined;
18
19
  width?: number | string | undefined | undefined;
@@ -21,7 +22,6 @@ export declare function Image({ image, value, ...attrs }: ImageProps): import("r
21
22
  srcSet?: string | undefined | undefined;
22
23
  loading?: "eager" | "lazy" | undefined | undefined;
23
24
  decoding?: "async" | "auto" | "sync" | undefined | undefined;
24
- title?: string | undefined | undefined;
25
25
  rel?: string | undefined | undefined;
26
26
  content?: string | undefined | undefined;
27
27
  referrerPolicy?: import("react").HTMLAttributeReferrerPolicy | undefined;
@@ -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;
@@ -1,4 +1,4 @@
1
- import type { ComponentContract, InferComponentProps } from './types/component.js';
1
+ import type { ComponentContract, ComponentContractID, InferComponentProps } from './types/component.js';
2
2
  export type AnyComponent = (abstract new (...args: any[]) => any) | ((props: any) => any) | Record<string, any>;
3
3
  export interface RegistryEntry {
4
4
  id: string;
@@ -6,7 +6,9 @@ export interface RegistryEntry {
6
6
  contract: ComponentContract;
7
7
  }
8
8
  export type Registry = readonly RegistryEntry[];
9
- type ValidatedEntry<T> = T extends readonly [infer C extends ComponentContract, infer Comp] ? Comp extends (props: InferComponentProps<C>) => any ? T : Comp extends (props: InferComponentProps<C> & infer _) => any ? T : Comp extends new (props: InferComponentProps<C>, ...args: any[]) => any ? T : Comp extends Record<string, any> ? T : never : never;
9
+ type ValidatedEntry<T> = T extends readonly [infer C extends ComponentContract, infer Comp] ? Comp extends (props: InferComponentProps<C>) => any ? RootIdentityChecked<C, Comp, T> : Comp extends (props: InferComponentProps<C> & infer _) => any ? RootIdentityChecked<C, Comp, T> : Comp extends new (props: InferComponentProps<C>, ...args: any[]) => any ? RootIdentityChecked<C, Comp, T> : Comp extends Record<string, any> ? T : never : never;
10
+ type DeclaredProps<Comp> = Comp extends (props: infer P) => any ? P : Comp extends abstract new (props: infer P, ...args: any[]) => any ? P : Record<string, any>;
11
+ type RootIdentityChecked<C extends ComponentContract, Comp, T> = 'data-aci-block' extends keyof DeclaredProps<Comp> ? 'data-aci-component' extends keyof DeclaredProps<Comp> ? T : `${ComponentContractID<C>}: props type must extend RootIdentityProps from @gradial/aci` : `${ComponentContractID<C>}: props type must extend RootIdentityProps from @gradial/aci`;
10
12
  type ValidatedEntries<T extends readonly (readonly [ComponentContract, AnyComponent])[]> = {
11
13
  [K in keyof T]: ValidatedEntry<T[K]>;
12
14
  };
@@ -1,4 +1,4 @@
1
- import type { ConformanceCheckResult, ConformanceCheckName, ConformanceContext } from './types.js';
1
+ import type { ConformanceCheckResult, ConformanceCheckName, ConformanceContext, ConformanceCheckFn } from './types.js';
2
2
  export declare function checkConfig(ctx: ConformanceContext): ConformanceCheckResult[];
3
3
  export declare function checkRegistry(ctx: ConformanceContext): ConformanceCheckResult[];
4
4
  export declare function checkPageEntry(ctx: ConformanceContext): ConformanceCheckResult[];
@@ -9,4 +9,4 @@ export declare function checkNoRequestTimeAssetRoute(ctx: ConformanceContext): C
9
9
  export declare function checkBuildSucceeds(ctx: ConformanceContext): ConformanceCheckResult[];
10
10
  export declare function checkContentCompile(ctx: ConformanceContext): ConformanceCheckResult[];
11
11
  export declare function checkSdkVersion(ctx: ConformanceContext): ConformanceCheckResult[];
12
- export declare const CHECK_REGISTRY: Record<ConformanceCheckName, (ctx: ConformanceContext) => ConformanceCheckResult[]>;
12
+ export declare const CHECK_REGISTRY: Record<ConformanceCheckName, ConformanceCheckFn>;
@@ -9,6 +9,7 @@
9
9
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
10
10
  import { join, relative } from 'node:path';
11
11
  import { execSync } from 'node:child_process';
12
+ import { checkRootIdentity } from './root-identity.js';
12
13
  // ---------------------------------------------------------------------------
13
14
  // Helpers
14
15
  // ---------------------------------------------------------------------------
@@ -445,4 +446,5 @@ export const CHECK_REGISTRY = {
445
446
  buildSucceeds: checkBuildSucceeds,
446
447
  contentCompile: checkContentCompile,
447
448
  sdkVersion: checkSdkVersion,
449
+ rootIdentity: checkRootIdentity,
448
450
  };
@@ -1,3 +1,4 @@
1
1
  export { conformancePreset, runConformanceChecks } from './preset.js';
2
2
  export { checkConfig, checkRegistry, checkPageEntry, checkMiddleware, checkNoFileContentProvider, checkNoCustomContent, checkNoRequestTimeAssetRoute, checkBuildSucceeds, checkContentCompile, checkSdkVersion, CHECK_REGISTRY, } from './checks.js';
3
+ export { checkRootIdentity, findRootIdentityFailures, formatRootIdentityFailures, astroAuthoredBlocks, type RootIdentityFailure, } from './root-identity.js';
3
4
  export type { ConformanceFramework, ConformanceCheckName, ConformancePresetOptions, ConformanceCheckResult, ConformanceResult, ConformanceContext, ConformanceCheckFn, } from './types.js';
@@ -1,2 +1,3 @@
1
1
  export { conformancePreset, runConformanceChecks } from './preset.js';
2
2
  export { checkConfig, checkRegistry, checkPageEntry, checkMiddleware, checkNoFileContentProvider, checkNoCustomContent, checkNoRequestTimeAssetRoute, checkBuildSucceeds, checkContentCompile, checkSdkVersion, CHECK_REGISTRY, } from './checks.js';
3
+ export { checkRootIdentity, findRootIdentityFailures, formatRootIdentityFailures, astroAuthoredBlocks, } from './root-identity.js';
@@ -0,0 +1,6 @@
1
+ export declare function resolve(specifier: string, context: unknown, nextResolve: (specifier: string, context: unknown) => Promise<{
2
+ url: string;
3
+ }>): Promise<{
4
+ url: string;
5
+ shortCircuit?: boolean;
6
+ }>;
@@ -0,0 +1,9 @@
1
+ // Node module customization hook for the rootIdentity check: outside Next's
2
+ // bundler, `next/link` and `next/image` resolve to CommonJS interop objects
3
+ // that react-dom/server cannot render. Swap them for plain-element stubs.
4
+ export async function resolve(specifier, context, nextResolve) {
5
+ if (specifier === 'next/link' || specifier === 'next/image') {
6
+ return { url: new URL(`./stubs/${specifier.replace('/', '-')}.js`, import.meta.url).href, shortCircuit: true };
7
+ }
8
+ return nextResolve(specifier, context);
9
+ }
@@ -2,9 +2,10 @@ import type { ConformancePresetOptions, ConformanceResult } from './types.js';
2
2
  /**
3
3
  * Run all resolved checks and return an aggregate result.
4
4
  * This is the programmatic API — call it directly if you don't want the
5
- * node:test integration.
5
+ * node:test integration. It is async: the rootIdentity check renders
6
+ * components, so `await` the result before reading `.passed`.
6
7
  */
7
- export declare function runConformanceChecks(options: ConformancePresetOptions): ConformanceResult;
8
+ export declare function runConformanceChecks(options: ConformancePresetOptions): Promise<ConformanceResult>;
8
9
  /**
9
10
  * Register a conformance test suite using node:test.
10
11
  *
@@ -10,6 +10,7 @@
10
10
  * conformancePreset({
11
11
  * framework: 'next',
12
12
  * siteId: 'site_mysite',
13
+ * registry: 'src/cms/registry.ts',
13
14
  * });
14
15
  * ```
15
16
  *
@@ -29,6 +30,9 @@ const ALL_CHECKS = [
29
30
  'noCustomContent',
30
31
  'noRequestTimeAssetRoute',
31
32
  'sdkVersion',
33
+ // Renders every registered component (ADR 0051); on Astro it scans dist/,
34
+ // building first when absent.
35
+ 'rootIdentity',
32
36
  // Expensive checks last — these shell out
33
37
  'contentCompile',
34
38
  'buildSucceeds',
@@ -51,19 +55,21 @@ function resolveChecks(input) {
51
55
  /**
52
56
  * Run all resolved checks and return an aggregate result.
53
57
  * This is the programmatic API — call it directly if you don't want the
54
- * node:test integration.
58
+ * node:test integration. It is async: the rootIdentity check renders
59
+ * components, so `await` the result before reading `.passed`.
55
60
  */
56
- export function runConformanceChecks(options) {
61
+ export async function runConformanceChecks(options) {
57
62
  const ctx = {
58
63
  framework: options.framework,
59
64
  siteId: options.siteId ?? '',
60
65
  rootDir: options.rootDir || process.cwd(),
66
+ registry: options.registry,
61
67
  };
62
68
  const checks = resolveChecks(options.checks);
63
- const allResults = checks.flatMap((name) => {
64
- const checkFn = CHECK_REGISTRY[name];
65
- return checkFn(ctx);
66
- });
69
+ const allResults = [];
70
+ for (const name of checks) {
71
+ allResults.push(...(await CHECK_REGISTRY[name](ctx)));
72
+ }
67
73
  const failures = allResults.filter((r) => !r.ok);
68
74
  return {
69
75
  passed: failures.length === 0,
@@ -84,11 +90,12 @@ export function conformancePreset(options) {
84
90
  framework: options.framework,
85
91
  siteId: options.siteId ?? '',
86
92
  rootDir: options.rootDir || process.cwd(),
93
+ registry: options.registry,
87
94
  };
88
95
  for (const checkName of checks) {
89
- await t.test(checkName, () => {
96
+ await t.test(checkName, async () => {
90
97
  const checkFn = CHECK_REGISTRY[checkName];
91
- const results = checkFn(ctx);
98
+ const results = await checkFn(ctx);
92
99
  for (const result of results) {
93
100
  assert.ok(result.ok, `${result.label}${result.detail ? ` (${result.detail})` : ''}`);
94
101
  }
@@ -0,0 +1,31 @@
1
+ import type { ConformanceCheckResult, ConformanceContext } from './types.js';
2
+ import type { Registry } from '../../registry.js';
3
+ import type { KernelPageBlock } from '../../content/types.js';
4
+ export interface RootIdentityFailure {
5
+ component: string;
6
+ file?: string;
7
+ missing: string[];
8
+ }
9
+ /** Minimal React surface the renderer needs; injected so tests can pass fakes. */
10
+ export interface ReactRuntime {
11
+ renderToStaticMarkup: (element: unknown) => string;
12
+ }
13
+ /**
14
+ * Render each block through the SDK renderer and report every component
15
+ * whose root element lacks the identity attributes. `blocks` are the blocks
16
+ * to exercise; registry entries not covered by a block are probed with empty
17
+ * props so an unused component cannot hide a forgotten spread.
18
+ */
19
+ export declare function findRootIdentityFailures(registry: Registry, blocks: readonly KernelPageBlock[], react: ReactRuntime, fileFor?: (component: string) => string | undefined): Promise<RootIdentityFailure[]>;
20
+ /** The failure message: one line of what, the list, one line of fix. */
21
+ export declare function formatRootIdentityFailures(failures: readonly RootIdentityFailure[], framework: 'next' | 'astro'): string;
22
+ export declare function checkRootIdentity(ctx: ConformanceContext): Promise<ConformanceCheckResult[]>;
23
+ /**
24
+ * Every block the Astro scan must find in dist/: page blocks by their own id,
25
+ * plus fragments (navbar, footer) by fragment id, which is the block id the
26
+ * renderer synthesizes when a layout slot default places them.
27
+ */
28
+ export declare function astroAuthoredBlocks(contentDir: string): Array<{
29
+ id: string;
30
+ component: string;
31
+ }>;