@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,131 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.52
4
+
5
+ ### Changed
6
+
7
+ - Prerelease versions are no longer committed. Canary builds
8
+ (`@gradial/aci@canary`) publish automatically from every SDK change on
9
+ `main`; release candidates (`@gradial/aci@rc`) and stable releases publish
10
+ on demand. All are prereleases or releases of the next stable version.
11
+
12
+ ### Removed
13
+
14
+ - `@gradial/aci/next/server` no longer reads Vercel Edge Config.
15
+ `resolveReleaseId()` resolves an explicit release, then the verified routed
16
+ release, then `ACI_RELEASE_ID`, and otherwise throws; it never falls back to
17
+ an Edge Config active-release pointer. `FetchConfig` loses `edgeConfig`,
18
+ `deploymentId`, and `siteId`, which only that pointer read used. The
19
+ `EDGE_CONFIG` and `VERCEL_DEPLOYMENT_ID` environment variables are ignored.
20
+
21
+ ## 0.1.52-rc.41
22
+
23
+ ### Changed
24
+
25
+ - Shared edge claims support collection-free external-audience delivery.
26
+ Personalization requires a recognized issuer marker; signed older cookies
27
+ remain valid evidence for consent withdrawal.
28
+
29
+ ## 0.1.52-rc.40
30
+
31
+ ### Fixed
32
+
33
+ - Mimic pages retain Next hydration and native 404 rendering while the hosting
34
+ platform owns their private response and session banner. Browser measurement
35
+ stays disabled for the lifetime of each mimic document, including after its
36
+ cookie expires or another tab exits the session. Rebuild and redeploy sites
37
+ with this SDK and the matching hosting platform to pick up the fix.
38
+
39
+ ## 0.1.52-rc.38
40
+
41
+ ### Migration required
42
+
43
+ - Link secondary experiment goals with `goalIds` instead of the retired
44
+ `goalRefs` field. Remove `goalRefs` from authored documents and authoring
45
+ integrations; `primaryGoalRef` remains supported. See the
46
+ [secondary-goal migration](./migrations/experiment-goal-ids.md).
47
+
48
+ ## 0.1.52-rc.37
49
+
50
+ ### Removed
51
+
52
+ - Unused edge identity helpers and types. The shared runtime retains the
53
+ production identity and routing behavior.
54
+
55
+ ## 0.1.52-rc.36
56
+
57
+ ### Removed
58
+
59
+ - The unused compact route-record codec and static compose terminal target.
60
+ Static delivery selects complete HTML through the shared edge runtime.
61
+
62
+ ## 0.1.52-rc.35
63
+
64
+ ### Fixed
65
+
66
+ - Next returns HTTP 404 for missing native assets and API endpoints that reach
67
+ the CMS catch-all without a routed release, including `/favicon.ico`, instead
68
+ of failing metadata and page rendering with a release-selection error.
69
+
70
+ ## 0.1.52-rc.34
71
+
72
+ ### Changed
73
+
74
+ - The `dam-delivery` agent skill now says a future `expiresAt` is admitted to
75
+ public delivery and can be set, moved, or cleared on a public version; only
76
+ a past expiry vetoes approval or is refused on a public version.
77
+
78
+ ## 0.1.52-rc.33
79
+
80
+ ### Migration required
81
+
82
+ - Next sites must set `registry` in `conformancePreset` (or
83
+ `runConformanceChecks`) to the render registry module used by their app, for
84
+ example `registry: 'src/cms/registry.ts'`. The default `rootIdentity` check
85
+ fails with configuration guidance when this option is absent. See
86
+ [the registry-selection migration](./migrations/conformance-registry.md).
87
+
88
+ ### Fixed
89
+
90
+ - Next `rootIdentity` imports only the site's explicitly named registry. It no
91
+ longer executes arbitrary source files containing `createRegistry(` or
92
+ silently checks a secondary registry.
93
+
94
+ ## Unreleased
95
+
96
+ **Public API removal:** `visitorProfileDataModel` is gone (see Removed
97
+ below) — delete it from any imports.
98
+
99
+ ### Changed
100
+
101
+ - The audience/experimentation author data models
102
+ (`packages/sdk/src/audience-data-models.ts`) are now generated from the Go
103
+ document readers' contracts (`scripts/generate-data-models.go`, verified in
104
+ CI by `make data-models-verify`) instead of hand-written. This fixes drift
105
+ toward the server contract: `experimentDataModel` gains `horizon` (with the
106
+ ADR 0042/0049 constraints), `promotedArmId`/`promotedAt`, and the
107
+ `eligibility.audienceRef` `$ref:audiences/{id}` grammar; rule and goal
108
+ expressions support nested all/any groups with the kernel's depth and
109
+ condition budgets; goal `eventRule` validates in event scope; segment
110
+ criteria accept the `geo` filter; providers require
111
+ `identity.absencePolicy` and enforce the format↔mapping matrix; ids use the
112
+ shared identifier grammar.
113
+
114
+ ### Fixed
115
+
116
+ - The `dam-delivery` agent skill shipped in the site templates described two
117
+ delivery lanes that no longer exist (signed `/dam/i/…` URLs and `/dam/a/…`
118
+ permalinks). It now describes the one public link, `/dam/{assetId}`, and
119
+ its `w`, `h`, `fit`, `fm`, `q` query. A site scaffolded earlier keeps its
120
+ old copy: replace its `.agents/skills/dam-delivery/` with
121
+ `templates/common/.agents/skills/dam-delivery/` from this package.
122
+
123
+ ### Removed
124
+
125
+ - `visitorProfileDataModel`: the schema had no server-side document contract
126
+ and no consumers. Drop it from imports; `audienceDataModels` no longer
127
+ includes it.
128
+
3
129
  ## 0.1.52-rc.7 - 2026-08-19
4
130
 
5
131
  ### Fixed
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Agentic Content Infrastructure SDK
2
2
 
3
- `@gradial/aci` `0.1.52-rc.7` is a pre-1.0 public-surface reset for the ACI
3
+ `@gradial/aci` `0.1.52` 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.52-rc.7 zod@^4
11
+ npm install @gradial/aci@0.1.52 zod@^4
12
12
  ```
13
13
 
14
14
  ## Root Import
@@ -62,6 +62,65 @@ export default withAci(nextConfig, {
62
62
 
63
63
  Astro public subpath is `@gradial/aci/astro`.
64
64
 
65
+ ## Page Templates
66
+
67
+ Pages use content-authored templates for region order, defaults, composition
68
+ rules, and personalization locks. Start with the [page templates guide](docs/page-templates.md),
69
+ including the `blog_body` contract pattern for page-specific container children.
70
+ [Component contracts](docs/component-contracts.md) define props and nested block types.
71
+
72
+ ## Block Root Identity
73
+
74
+ The renderer passes every block two props, `data-aci-block` (the authored
75
+ block id) and `data-aci-component`. A component extends `RootIdentityProps`
76
+ and spreads the rest of its props onto its root element, so analytics can join
77
+ events to content by (page path, block id):
78
+
79
+ ```tsx
80
+ import type { RootIdentityProps } from '@gradial/aci';
81
+
82
+ export function StatsBar({ stats = [], ...root }: ContentProps & RootIdentityProps) {
83
+ return <section {...root} className="…">…</section>;
84
+ }
85
+ ```
86
+
87
+ Three checks make this required: `createRegistry` fails typecheck for a
88
+ component whose props type lacks the keys, the default `rootIdentity`
89
+ conformance check renders every registered component and fails when the root
90
+ lacks the attributes, and publish rejects a release whose rendered HTML is
91
+ missing a block id. `runConformanceChecks` is async; `await` it before reading
92
+ `.passed`.
93
+
94
+ ## Mintlify
95
+
96
+ `@gradial/aci/mintlify` is the Mintlify adapter used automatically by the ACI
97
+ CLI. Mintlify remains the renderer; ACI owns the standard `.content` tree.
98
+ Documents use stable ACI component IDs and an
99
+ ordered recursive block tree: Markdown slices for prose, typed nested Mintlify
100
+ components and stable accessibility-safe anchors for common structures, and
101
+ explicit `mintlify.mdx` nodes for native syntax that has not been promoted to a
102
+ typed contract.
103
+
104
+ The source tree may also contain independently governed snippets, recursive
105
+ `docs.json` `$ref` fragments, and OpenAPI, AsyncAPI, or GraphQL descriptions:
106
+
107
+ ```text
108
+ .content/
109
+ config/site.json
110
+ config/mintlify.json
111
+ pages/**/_index.json
112
+ fragments/**/_index.json
113
+ ```
114
+
115
+ ```bash
116
+ aci content compile
117
+ ```
118
+
119
+ See `starters/mintlify/` for the smallest runnable site. `.aci.yaml` selects
120
+ `framework: mintlify`; no renderer or validation flag is needed. The command
121
+ validates the graph and writes the native project to
122
+ `.aci/compiled/renderers/mintlify/` beside the ordinary compiled manifest.
123
+
65
124
  ## Build-Time Content Provider
66
125
 
67
126
  The file provider reads compiler output while generating complete static pages:
@@ -78,13 +137,13 @@ host bundles select and serve immutable release artifacts.
78
137
  The first RC scaffold path is Next:
79
138
 
80
139
  ```bash
81
- npx @gradial/aci@0.1.52-rc.7 init my-site --template next-starter
140
+ npx @gradial/aci@0.1.52 init my-site --template next-starter
82
141
  ```
83
142
 
84
143
  List available templates:
85
144
 
86
145
  ```bash
87
- npx @gradial/aci@0.1.52-rc.7 init --list
146
+ npx @gradial/aci@0.1.52 init --list
88
147
  ```
89
148
 
90
149
  ## Personalization: The One-Tag Install
@@ -107,7 +166,7 @@ Tag attributes (the tag is the only per-site configuration surface):
107
166
 
108
167
  | Attribute | Meaning |
109
168
  | --- | --- |
110
- | `data-aci-consent` | CMP preset: `c15t`, `onetrust`, `cookiebot`, `trustarc`, or `manual` (default `manual` — only the JS consent API `aci('consent', …)` writes envelopes) |
169
+ | `data-aci-consent` | CMP preset: `c15t`, `onetrust`, `cookiebot`, `trustarc`, or `manual` (default `manual` — only the JS consent API `aci('consent', …)` writes envelopes). A CMP preset tells the SDK the CMP will decide: until it writes consent, a first-time visitor's events (the exposure and page view included) wait in page memory, are sent once consent is granted, and are dropped on denial or when the page closes undecided. Only name a preset whose CMP is actually wired. Under `manual`, ACI's own consent banner (`ConsentBanner.astro`) plays that role: while it awaits the visitor, first views are held the same way. |
111
170
  | `data-aci-consent-categories` | Flat comma list of CMP categories that imply the personalization purpose (e.g. `measurement,marketing`); each preset ships a sane default |
112
171
  | `data-aci-site` | Optional site ID, debug only — the edge binding is the site authority |
113
172
  | `data-aci-debug` | `1` enables structured `console.debug` diagnostics |
@@ -134,6 +193,28 @@ has no Astro UI package, so the Astro kit ships the bridge only (in
134
193
  `@c15t/react`) or any setup built on c15t's store and the bridge picks it
135
194
  up; until then only the `manual` JS consent API writes envelopes.
136
195
 
196
+ ### Client-side navigation and experiment attribution
197
+
198
+ Next `createPage` includes the completion hook. Custom Next pages must render
199
+ `<AssignmentsScript page={page} />` on every page, including pages without
200
+ experiments. Astro client routing uses `astro:page-load` after the document swap.
201
+ Rebuild sites using the SDK framework integration when upgrading this contract.
202
+
203
+ For another client router, call this command after committing the destination
204
+ page. Pass the new page's complete assignment set, or `[]` for a base page:
205
+
206
+ ```js
207
+ window.aci('navigation-complete', {
208
+ url: window.location.href,
209
+ assignments: page.experiments ?? [],
210
+ });
211
+ ```
212
+
213
+ Changing the URL immediately clears attribution. Events during the transition
214
+ carry no experiment context; the completion hook emits the new exposures and
215
+ page view before subsequent conversions. Query changes count as navigation;
216
+ hash-only changes and duplicate completions do not create another page view.
217
+
137
218
  ## Verify Locally
138
219
 
139
220
  From `packages/sdk`:
@@ -50,6 +50,7 @@ export async function getErrorPageData(astro, kind, options = {}) {
50
50
  if (resolved.route) {
51
51
  const slug = resolved.route.replace(/^\/+|\/+$/g, '');
52
52
  const data = await getPageData({
53
+ isPrerendered: astro.isPrerendered,
53
54
  params: { slug },
54
55
  request: astro.request,
55
56
  url: astro.url,
@@ -61,6 +61,10 @@ export function createRenderChildren(registry) {
61
61
  if (hasBlockSlots) {
62
62
  props.renderChildren = renderChildren;
63
63
  }
64
+ // sdk I-19 (ADR 0051): root identity props are applied last so content
65
+ // props cannot overwrite them. The template spreads them onto the root.
66
+ props['data-aci-block'] = block.id;
67
+ props['data-aci-component'] = block.component;
64
68
  return { Component: entry?.component, props };
65
69
  });
66
70
  }
@@ -31,12 +31,13 @@
31
31
  * iterate slots and render blocks via the registry.
32
32
  */
33
33
  import type { KernelPage, KernelSiteConfig, KernelRouteMetadata, KernelPageBlock } from '../content/types.js';
34
- import type { LayoutEntry } from '../content/provider.js';
34
+ import { type LayoutEntry } from '../content/provider.js';
35
35
  /**
36
36
  * Minimal Astro global shape — only the properties getPageData reads.
37
37
  * This avoids depending on Astro's own types at the SDK level.
38
38
  */
39
39
  export interface AstroGlobal {
40
+ isPrerendered?: boolean;
40
41
  params: {
41
42
  slug?: string;
42
43
  };
@@ -51,6 +52,8 @@ export interface AstroGlobal {
51
52
  export interface PageData {
52
53
  /** Route string (normalized, e.g. "/" or "/about"). */
53
54
  route: string;
55
+ /** Selected render tuple, including the explicit "base" identity. */
56
+ variantKey: string;
54
57
  /** Site configuration. */
55
58
  siteConfig: KernelSiteConfig;
56
59
  /** Page content. */
@@ -91,4 +94,4 @@ export interface GetPageDataOptions {
91
94
  * directly from CloudFront in production (the CDN points at the release
92
95
  * artifacts in S3, no runtime content fetching).
93
96
  */
94
- export declare function getPageData(astro: AstroGlobal, _options?: GetPageDataOptions): Promise<PageData>;
97
+ export declare function getPageData(astro: AstroGlobal, options?: GetPageDataOptions): Promise<PageData>;
@@ -30,11 +30,12 @@
30
30
  * The page template receives a typed data object and only needs to
31
31
  * iterate slots and render blocks via the registry.
32
32
  */
33
+ import { FragmentNotFoundError } from '../content/provider.js';
33
34
  import { normalizeRoute } from '../content/routes.js';
34
35
  import { routeMetadataForContent } from '../content/provider.js';
35
36
  import { resolveSlots } from '../content/resolve-slots.js';
36
37
  import { assignmentsScriptTagHTML, collectScriptTagHTML } from '../content/personalization.js';
37
- import { getPendingRenderInputFromHeaders } from '../runtime/page.js';
38
+ import { GRADIAL_RENDER_INPUT_HEADER, getPendingRenderInputFromHeaders } from '../runtime/page.js';
38
39
  // ---------------------------------------------------------------------------
39
40
  // Implementation
40
41
  // ---------------------------------------------------------------------------
@@ -52,23 +53,25 @@ import { getPendingRenderInputFromHeaders } from '../runtime/page.js';
52
53
  * directly from CloudFront in production (the CDN points at the release
53
54
  * artifacts in S3, no runtime content fetching).
54
55
  */
55
- export async function getPageData(astro, _options) {
56
+ export async function getPageData(astro, options) {
56
57
  const route = normalizeRoute(astro.params.slug ? `/${astro.params.slug}` : '/');
57
- // Check for preview render input first (same as Next.js tier 1)
58
- // Skip during prerender/static generation — request.headers is not available
58
+ // Static Astro generation is explicit; unexpected header access errors and
59
+ // expired render-input IDs must not turn a selected variant into local base.
59
60
  let runtimeInput = null;
60
- try {
61
- runtimeInput = getPendingRenderInputFromHeaders(astro.request.headers);
62
- }
63
- catch {
64
- // Astro throws when accessing request.headers during prerender — this is
65
- // expected and harmless; preview mode is a request-time concern.
61
+ if (!astro.isPrerendered) {
62
+ const headers = astro.request.headers;
63
+ runtimeInput = getPendingRenderInputFromHeaders(headers);
64
+ if (headers.has(GRADIAL_RENDER_INPUT_HEADER) && !runtimeInput) {
65
+ throw new Error(`ACI Astro render input is unavailable for route ${route}; refusing to render local base content`);
66
+ }
66
67
  }
67
68
  let siteConfig;
68
69
  let page;
69
70
  let providerModule = null;
70
71
  let provider = null;
72
+ let variantKey;
71
73
  if (runtimeInput) {
74
+ variantKey = requireVariantKey(runtimeInput.variantKey, route);
72
75
  siteConfig = runtimeInput.siteConfig;
73
76
  page = runtimeInput.page;
74
77
  }
@@ -76,19 +79,23 @@ export async function getPageData(astro, _options) {
76
79
  // Lazy-import FileContentProvider to avoid pulling it in when
77
80
  // preview render input is available
78
81
  providerModule = await import('../providers/file.js');
79
- provider = new providerModule.FileContentProvider();
82
+ provider = new providerModule.FileContentProvider(options?.compiledRoot);
80
83
  [siteConfig, page] = await Promise.all([
81
84
  provider.getSiteConfig(),
82
85
  provider.getPage(route),
83
86
  ]);
87
+ const entry = (await provider.listRoutes()).find((entry) => normalizeRoute(entry.path) === route);
88
+ variantKey = requireVariantKey(entry?.variantKey, route);
84
89
  }
85
90
  const meta = routeMetadataForContent(siteConfig, page);
86
91
  // Resolve layout: prefer render input, fall back to provider
87
- const layout = await resolveLayout(runtimeInput, provider, providerModule, page.layout);
92
+ const layout = await resolveLayout(runtimeInput, provider, page.layout, route);
88
93
  // Resolve slots: page regions filled, then layout fragment defaults
89
94
  const slots = await resolveSlots(layout, page?.regions || {}, async (id) => {
90
- if (runtimeInput?.fragments?.[id]) {
91
- return runtimeInput.fragments[id];
95
+ if (runtimeInput) {
96
+ if (Object.hasOwn(runtimeInput.fragments ?? {}, id))
97
+ return runtimeInput.fragments[id];
98
+ throw new FragmentNotFoundError(id, new Error('fragment is not bound by the selected page'));
92
99
  }
93
100
  const p = provider ?? new (await import('../providers/file.js')).FileContentProvider();
94
101
  return await p.getFragment(id);
@@ -97,22 +104,27 @@ export async function getPageData(astro, _options) {
97
104
  const isPreview = astro.url.searchParams.has('preview_token');
98
105
  const collectTag = isPreview ? '' : collectScriptTagHTML(siteConfig);
99
106
  const assignmentsTag = isPreview ? '' : assignmentsScriptTagHTML(page);
100
- return { route, siteConfig, page, meta, layout, slots, collectTag, assignmentsTag };
107
+ return { route, variantKey, siteConfig, page, meta, layout, slots, collectTag, assignmentsTag };
108
+ }
109
+ function requireVariantKey(value, route) {
110
+ if (typeof value !== 'string' || !value.trim() || value !== value.trim()) {
111
+ throw new Error(`ACI Astro render variant identity is missing or invalid for route ${route}; supply an explicit variantKey ("base" for an unpersonalized render)`);
112
+ }
113
+ return value;
101
114
  }
102
115
  // ---------------------------------------------------------------------------
103
116
  // Internal helpers
104
117
  // ---------------------------------------------------------------------------
105
- async function resolveLayout(runtimeInput, provider, providerModule, layoutName) {
106
- // Check render input first
107
- if (runtimeInput?.layouts?.[layoutName]) {
108
- return runtimeInput.layouts[layoutName];
118
+ async function resolveLayout(runtimeInput, provider, layoutName, route) {
119
+ if (runtimeInput) {
120
+ const layout = runtimeInput.layouts?.[layoutName];
121
+ if (!layout)
122
+ throw new Error(`ACI Astro render input for route ${route} is missing its selected layout ${JSON.stringify(layoutName)}`);
123
+ return layout;
109
124
  }
110
125
  // Fall back to provider
111
126
  if (provider) {
112
127
  return await provider.getLayout(layoutName);
113
128
  }
114
- // Last resort: create a new provider
115
- const mod = providerModule ?? await import('../providers/file.js');
116
- const p = new mod.FileContentProvider();
117
- return await p.getLayout(layoutName);
129
+ throw new Error(`ACI Astro content provider is unavailable for route ${route}`);
118
130
  }
@@ -1,9 +1,9 @@
1
- import { extractPreviewToken, resolvePreviewContext, createPreviewBannerHTML as coreCreatePreviewBannerHTML, createPreviewCookies as coreCreatePreviewCookies, previewSignKey, } from '../preview/core.js';
1
+ import { extractPreviewToken, parseCookies, resolvePreviewContext, createPreviewBannerHTML as coreCreatePreviewBannerHTML, createPreviewCookies as coreCreatePreviewCookies, previewSignKey, } from '../preview/core.js';
2
2
  export async function getPreviewMode(request, siteId) {
3
3
  const url = new URL(request.url);
4
4
  const token = extractPreviewToken({
5
5
  query: url.searchParams,
6
- cookies: parseCookieHeader(request.headers.get('cookie') || ''),
6
+ cookies: parseCookies(request.headers.get('cookie') || ''),
7
7
  authorization: request.headers.get('authorization') || undefined,
8
8
  });
9
9
  if (!token)
@@ -16,18 +16,3 @@ export function createPreviewBannerHTML(releaseId) {
16
16
  export function createPreviewCookies(token, releaseId) {
17
17
  return coreCreatePreviewCookies(token, releaseId);
18
18
  }
19
- function parseCookieHeader(header) {
20
- const cookies = new Map();
21
- for (const part of header.split(';')) {
22
- const trimmed = part.trim();
23
- if (!trimmed)
24
- continue;
25
- const idx = trimmed.indexOf('=');
26
- if (idx < 0)
27
- continue;
28
- const name = trimmed.slice(0, idx).trim();
29
- const value = trimmed.slice(idx + 1).trim();
30
- cookies.set(name, decodeURIComponent(value));
31
- }
32
- return cookies;
33
- }