@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
@@ -4,10 +4,17 @@ export interface EdgeDispatch {
4
4
  /** Release-relative immutable HTML artifact selected for this route. */
5
5
  artifactPath: string;
6
6
  }
7
+ /**
8
+ * One member of an expression group: a leaf condition, or a nested envelope
9
+ * when it carries `all`/`any` (ADR 0040). Presence of a group key decides
10
+ * which it is, so flat records stay a byte-identical subset.
11
+ */
7
12
  export interface EdgeRuleCondition {
8
- field: string;
9
- operator: 'exists' | 'equals' | 'contains' | 'startsWith' | string;
13
+ field?: string;
14
+ operator?: 'exists' | 'equals' | 'contains' | 'startsWith' | 'greaterThan' | 'greaterThanOrEqual' | 'lessThan' | 'lessThanOrEqual' | 'before' | 'after' | 'between' | 'in' | string;
10
15
  value?: unknown;
16
+ all?: EdgeRuleCondition[];
17
+ any?: EdgeRuleCondition[];
11
18
  }
12
19
  export interface EdgeRuleExpression {
13
20
  all?: EdgeRuleCondition[];
@@ -26,20 +33,6 @@ export interface EdgeRouteEntry {
26
33
  dispatch: EdgeDispatch;
27
34
  variants?: Record<string, EdgeRouteVariant>;
28
35
  }
29
- export interface CollectSDKEntry {
30
- hash: string;
31
- path: string;
32
- }
33
- export interface EdgeRouteManifest {
34
- manifestVersion: string;
35
- releaseId: string;
36
- siteId: string;
37
- routes: Record<string, EdgeRouteEntry>;
38
- experiments?: Record<string, PersonalizationExperiment>;
39
- collectSdk?: CollectSDKEntry;
40
- notFoundPage?: string;
41
- errorPage?: string;
42
- }
43
36
  export interface PersonalizationRequestContext {
44
37
  path: string;
45
38
  query: Record<string, string[]>;
@@ -48,9 +41,30 @@ export interface PersonalizationRequestContext {
48
41
  signals?: {
49
42
  geo?: Record<string, string>;
50
43
  };
44
+ /**
45
+ * Epoch milliseconds resolving the time.now clock fact (ADR 0038).
46
+ * Defaults to Date.now(); hosts may pin it once per request so every
47
+ * expression in one evaluation reads the same instant.
48
+ */
49
+ now?: number;
51
50
  identityHash?: string;
52
51
  identityNamespace?: string;
53
52
  siteId?: string;
53
+ /**
54
+ * Called when a variant's rule fails validation during serving (ADR 0040).
55
+ * Records pass the write-time gate before shipping, so this only fires on
56
+ * version skew or a projection bug — a precise signal that must not vanish.
57
+ * Receives routing facts and diagnostic codes only, never request data.
58
+ */
59
+ onRuleRejected?: (event: RuleRejectionEvent) => void;
60
+ }
61
+ export interface RuleRejectionEvent {
62
+ experimentId?: string;
63
+ armId?: string;
64
+ variantKey: string;
65
+ diagnosticCode: string;
66
+ diagnosticPath: string;
67
+ diagnosticCount: number;
54
68
  }
55
69
  export interface PersonalizationClaims {
56
70
  aud?: string[];
@@ -58,13 +72,7 @@ export interface PersonalizationClaims {
58
72
  iat: number;
59
73
  exp: number;
60
74
  }
61
- export interface PersonalizationVariant {
62
- variantKey: string;
63
- variantValues?: Record<string, string>;
64
- priority?: number;
65
- rule?: EdgeRuleExpression;
66
- dispatch: EdgeDispatch;
67
- }
75
+ export type PersonalizationVariant = EdgeRouteVariant;
68
76
  export interface PersonalizationRoute {
69
77
  variants?: Record<string, PersonalizationVariant>;
70
78
  }
@@ -75,10 +83,19 @@ export interface PersonalizationExperiment {
75
83
  priority?: number;
76
84
  allocationVersion?: string;
77
85
  salt?: string;
86
+ eligibilityAudienceId?: string;
87
+ /**
88
+ * ADR 0050 (experiment I-18): the projection carries this only when the
89
+ * experiment declares `eligibility.mode: "selected"`; absent means the
90
+ * claims gate (I-6) applies. The selected-leg predicate itself is slice
91
+ * A5 (edge-functions I-16) — A3 is projection plumbing only.
92
+ */
93
+ eligibilityMode?: 'selected';
78
94
  arms: Array<{
79
95
  id: string;
80
96
  allocationBasisPoints?: number;
81
- variantKey: string;
97
+ /** DEBT(edge-functions): decode old arm keys until every retained tenant release passes the ADR-0045 removal inventory. */
98
+ variantKey?: string;
82
99
  }>;
83
100
  }
84
101
  export interface PersonalizationIdentity {
@@ -93,24 +110,14 @@ export interface PersonalizationExperimentDecision {
93
110
  allocationVersion: string;
94
111
  variantKey: string;
95
112
  bucket: number;
113
+ /** audience-mimic design/09: the token's assertion replaced the dice. */
114
+ forced?: boolean;
96
115
  }
97
116
  export interface PersonalizationDecision {
117
+ /** ADR-0045: number of route experiments excluded as legacy pair-only entries. */
118
+ legacyExperimentExclusions?: number;
98
119
  variant: PersonalizationVariant | null;
99
- experiment: PersonalizationExperimentDecision | null;
100
- }
101
- export interface ClassificationData {
102
- audiences: Record<string, {
103
- rule: EdgeRuleExpression;
104
- }>;
105
- segments: Record<string, {
106
- rule: EdgeRuleExpression;
107
- }>;
108
- experiments: Record<string, PersonalizationExperiment>;
109
- }
110
- export interface Classification {
111
- matchedAudiences: string[];
112
- matchedSegments: string[];
113
- experimentArms: Record<string, PersonalizationExperimentDecision>;
120
+ experiments: PersonalizationExperimentDecision[];
114
121
  }
115
122
  export interface SigningKey {
116
123
  kid: string;
@@ -120,18 +127,6 @@ export interface IdentitySigningKeys {
120
127
  current: SigningKey;
121
128
  previous?: SigningKey;
122
129
  }
123
- export interface CookieMutation {
124
- name: string;
125
- value: string;
126
- options: {
127
- path: '/';
128
- httpOnly?: boolean;
129
- secure?: boolean;
130
- sameSite: 'lax';
131
- maxAge: number;
132
- expires?: Date;
133
- };
134
- }
135
130
  export interface ConsentSnapshot {
136
131
  version: number;
137
132
  purposes: string[];
@@ -145,20 +140,6 @@ export interface ConsentSignals {
145
140
  allowsIdentityTier: boolean;
146
141
  privacyDenied: boolean;
147
142
  }
148
- export interface PersonalizationEnvelopeWrite {
149
- key: string;
150
- envelopeId: string;
151
- siteId: string;
152
- identityNamespace: string;
153
- identityHash: string;
154
- records: readonly unknown[];
155
- digest: string;
156
- }
157
- export interface PersonalizationCollectionStore {
158
- readClaims(siteId: string, namespace: string, identityHash: string, now?: Date): Promise<string | null>;
159
- appendEnvelope(write: PersonalizationEnvelopeWrite): Promise<void>;
160
- putSuppression(key: string, record: unknown): Promise<void>;
161
- }
162
143
  export interface PersonalizationHostConfig {
163
144
  siteId: string;
164
145
  route: EdgeRouteEntry | null;
@@ -169,24 +150,47 @@ export interface PersonalizationHostConfig {
169
150
  secureCookies?: boolean;
170
151
  now?: () => Date;
171
152
  randomBytes?: (length: number) => Uint8Array;
153
+ verifiedVisitor?: {
154
+ rawIdentity: string | null;
155
+ claims: ClaimsPayload | null;
156
+ };
157
+ requestGeo?: {
158
+ country?: string;
159
+ region?: string;
160
+ city?: string;
161
+ };
162
+ issueIdentity?: boolean;
163
+ mimicClaims?: {
164
+ aud?: string[];
165
+ seg?: string[];
166
+ arms?: Record<string, string>;
167
+ };
168
+ /**
169
+ * Passed straight through to the request context's rejection sink so a host
170
+ * can record a variant skipped for a rule it could not validate (ADR 0040).
171
+ * Unset means the skip stays silent, which is what made the skew incident
172
+ * the ADR predicts undebuggable.
173
+ */
174
+ onRuleRejected?: (event: RuleRejectionEvent) => void;
175
+ }
176
+ export interface ClaimsPayload {
177
+ aud?: string[];
178
+ seg?: string[];
179
+ iat: number;
180
+ exp: number;
172
181
  }
173
182
  export interface PersonalizationResolution {
183
+ /** ADR-0045: number of route experiments excluded as legacy pair-only entries. */
184
+ legacyExperimentExclusions?: number;
185
+ /** ADR 0048: the raw identity issued for this request, unsigned and not yet persisted. */
186
+ issuedIdentity?: string;
174
187
  variantKey: string;
175
- experiment?: {
188
+ experiments: Array<{
176
189
  id: string;
177
190
  arm: string;
178
191
  allocationVersion: string;
179
- };
180
- }
181
- export interface CompositionDescriptor {
182
- layoutArtifact: string;
183
- fragments?: Record<string, string>;
184
- }
185
- export type SelectedTargetKind = 'direct' | 'compose' | 'render';
186
- export interface SelectedTarget {
187
- kind: SelectedTargetKind;
188
- path: string;
189
- digest: string;
190
- composition?: CompositionDescriptor;
192
+ /** audience-mimic design/09: the assignment came from the token, not the dice. */
193
+ forced?: boolean;
194
+ }>;
191
195
  }
192
196
  export { IdentityErasureFencedError } from '../../platform/collectionstore/errors.js';
@@ -1,7 +1,7 @@
1
1
  // Shared edge-decisioning types.
2
2
  //
3
3
  // These types are the canonical shape for the edge decisioning algorithm.
4
- // All host adapters (AWS, Cloudflare, Vercel, emulator) import from here.
5
- // The SDK's personalization-core types mirror these — the two are kept in
6
- // sync by cross-language round-trip tests (spec edge-decisioning R-conformance).
4
+ // All host adapters (AWS, Cloudflare, Vercel, emulator) import from here,
5
+ // and the SDK build compiles this module into its dist/internal tree — there
6
+ // is no SDK-side mirror (ADR 0041 retired the hand-ported copy).
7
7
  export { IdentityErasureFencedError } from '../../platform/collectionstore/errors.js';
@@ -0,0 +1,4 @@
1
+ export declare function compareUTF8(left: string, right: string): number;
2
+ export declare function canonicalVariantKey(values: Record<string, string>): string;
3
+ export declare function parseVariantKey(key: string): Record<string, string>;
4
+ export declare function variantValuesFromKey(key: string): Record<string, string>;
@@ -0,0 +1,56 @@
1
+ // Canonical factor keys mirror audience.VariantKey/ParseVariantKey. Their
2
+ // shared vector lives in audience/testdata/variant-key.v1.json.
3
+ export function compareUTF8(left, right) {
4
+ const a = Array.from(left);
5
+ const b = Array.from(right);
6
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
7
+ const difference = a[i].codePointAt(0) - b[i].codePointAt(0);
8
+ if (difference)
9
+ return difference;
10
+ }
11
+ return a.length - b.length;
12
+ }
13
+ export function canonicalVariantKey(values) {
14
+ const fixed = ['locale', 'audience', 'segment'].filter((name) => values[name]?.trim());
15
+ const rest = Object.keys(values).filter((name) => values[name]?.trim() && !fixed.includes(name));
16
+ const generic = rest.filter((name) => !name.startsWith('experiment.')).sort(compareUTF8);
17
+ const experiments = rest.filter((name) => name.startsWith('experiment.')).sort(compareUTF8);
18
+ return [...fixed, ...generic, ...experiments].map((name) => `${name}=${values[name].trim()}`).join(';') || 'base';
19
+ }
20
+ export function parseVariantKey(key) {
21
+ const values = {};
22
+ if (key === 'base')
23
+ return values;
24
+ for (const part of key.split(';')) {
25
+ const separator = part.indexOf('=');
26
+ const name = part.slice(0, separator);
27
+ const value = part.slice(separator + 1);
28
+ if (separator < 1 || !validToken(name) || !validToken(value))
29
+ throw new Error('variant key has malformed factors');
30
+ if (name === 'experiment' || name === 'experimentArm')
31
+ throw new Error('pair-form experiment key is not canonical');
32
+ if (name.startsWith('experiment.') && (!/^[a-zA-Z0-9_-]+$/.test(name.slice('experiment.'.length)) || !/^[a-zA-Z0-9_-]+$/.test(value)))
33
+ throw new Error('variant key has malformed experiment factor');
34
+ if (Object.prototype.hasOwnProperty.call(values, name))
35
+ throw new Error('variant key repeats a dimension');
36
+ Object.defineProperty(values, name, { value, enumerable: true, writable: true, configurable: true });
37
+ }
38
+ if (canonicalVariantKey(values) !== key)
39
+ throw new Error('variant key is not canonically ordered');
40
+ return values;
41
+ }
42
+ // Lenient reader for a route record's variant key when the record carries no
43
+ // variantValues. Unlike parseVariantKey it never throws and also accepts `&`
44
+ // as a separator, so it is not interchangeable with the canonical parser.
45
+ export function variantValuesFromKey(key) {
46
+ const values = {};
47
+ for (const part of String(key || '').replace(/&/g, ';').split(';')) {
48
+ const sep = part.indexOf('=');
49
+ if (sep > 0)
50
+ values[part.slice(0, sep).trim()] = part.slice(sep + 1).trim();
51
+ }
52
+ return values;
53
+ }
54
+ function validToken(value) {
55
+ return value !== '' && value !== '.' && value !== '..' && !/[\p{White_Space};=/]/u.test(value);
56
+ }
@@ -3,3 +3,27 @@ export declare class IdentityErasureFencedError extends Error {
3
3
  readonly code = "IDENTITY_ERASURE_FENCED";
4
4
  constructor();
5
5
  }
6
+ export declare class CollectionGateBusyError extends Error {
7
+ readonly name = "CollectionGateBusyError";
8
+ readonly code = "COLLECTION_GATE_BUSY";
9
+ constructor(detail?: string);
10
+ }
11
+ export declare class EnvelopeIdCollisionError extends Error {
12
+ readonly name = "EnvelopeIdCollisionError";
13
+ readonly code = "ENVELOPE_ID_COLLISION";
14
+ readonly envelopeId: string;
15
+ readonly existingDigest: string;
16
+ readonly incomingDigest: string;
17
+ constructor(envelopeId: string, existingDigest: string, incomingDigest: string);
18
+ }
19
+ export declare class AdmissionConflictError extends Error {
20
+ readonly kind: 'envelope' | 'event';
21
+ readonly name = "AdmissionConflictError";
22
+ readonly code = "COLLECT_ADMISSION_CONFLICT";
23
+ constructor(kind: 'envelope' | 'event');
24
+ }
25
+ export declare class CollectionStoreError extends Error {
26
+ readonly code: string;
27
+ readonly name = "CollectionStoreError";
28
+ constructor(code: string, message: string, options?: ErrorOptions);
29
+ }
@@ -5,3 +5,51 @@ export class IdentityErasureFencedError extends Error {
5
5
  super('identity collection is fenced by erasure');
6
6
  }
7
7
  }
8
+ // Go parity: audience.ErrCollectionGateBusy. A live or unproven collecting
9
+ // generation owns the identity; the caller must fail loudly and retry later,
10
+ // never treat the append as stored or dropped.
11
+ export class CollectionGateBusyError extends Error {
12
+ name = 'CollectionGateBusyError';
13
+ code = 'COLLECTION_GATE_BUSY';
14
+ constructor(detail) {
15
+ super(detail
16
+ ? `audience: identity collection gate is busy: ${detail}`
17
+ : 'audience: identity collection gate is busy');
18
+ }
19
+ }
20
+ // Go parity: audience.EnvelopeIDCollisionError. The envelope ID already names
21
+ // an object whose stable-input digest differs (or is absent), so this request
22
+ // is not an identical retry and must not be acknowledged as stored.
23
+ export class EnvelopeIdCollisionError extends Error {
24
+ name = 'EnvelopeIdCollisionError';
25
+ code = 'ENVELOPE_ID_COLLISION';
26
+ envelopeId;
27
+ existingDigest;
28
+ incomingDigest;
29
+ constructor(envelopeId, existingDigest, incomingDigest) {
30
+ super(`audience: envelope ID collision: ${envelopeId}`);
31
+ this.envelopeId = envelopeId;
32
+ this.existingDigest = existingDigest;
33
+ this.incomingDigest = incomingDigest;
34
+ }
35
+ }
36
+ // Different site-scoped acceptance owner; no visitor identifiers in the error.
37
+ export class AdmissionConflictError extends Error {
38
+ kind;
39
+ name = 'AdmissionConflictError';
40
+ code = 'COLLECT_ADMISSION_CONFLICT';
41
+ constructor(kind) {
42
+ super(`collect ${kind} ID already belongs to another acceptance; retry only the original sealed envelope with its original identity and consent`);
43
+ this.kind = kind;
44
+ }
45
+ }
46
+ // Internal store failure with a stable bounded code. Collector diagnostics log
47
+ // the code and never the message, which may interpolate object keys.
48
+ export class CollectionStoreError extends Error {
49
+ code;
50
+ name = 'CollectionStoreError';
51
+ constructor(code, message, options) {
52
+ super(message, options);
53
+ this.code = code;
54
+ }
55
+ }
@@ -0,0 +1,6 @@
1
+ import type { MintlifyBlock, MintlifyDocument, MintlifySnippet } from './types.js';
2
+ export declare function renderDocument(document: MintlifyDocument): string;
3
+ export declare function renderSnippet(snippet: MintlifySnippet): string;
4
+ export declare function validateBlock(value: unknown, label: string, ids: Set<string>): MintlifyBlock;
5
+ export declare function visitBlocks(blocks: MintlifyBlock[], visit: (block: MintlifyBlock) => void): void;
6
+ export declare function validateSnippetReferences(documents: MintlifyDocument[], snippets: MintlifySnippet[]): void;
@@ -0,0 +1,201 @@
1
+ import { fragmentRootScope, preserveFragmentScope } from '../content/resolve-slots.js';
2
+ import { cleanFilePath, isMintlifyJSONValue, isObject, nonEmptyString } from './json.js';
3
+ const GENERATED_MARKER = '{/* Generated by @gradial/aci; edit .content instead. */}';
4
+ const SUPPORTED_COMPONENTS = new Set([
5
+ 'mintlify.markdown', 'mintlify.mdx', 'mintlify.anchor', 'mintlify.steps', 'mintlify.step',
6
+ 'mintlify.card-group', 'mintlify.card', 'mintlify.accordion-group',
7
+ 'mintlify.accordion', 'mintlify.callout', 'mintlify.tabs', 'mintlify.tab',
8
+ 'mintlify.snippet-ref',
9
+ ]);
10
+ export function renderDocument(document) {
11
+ const frontmatter = [`title: ${JSON.stringify(document.metadata.title)}`];
12
+ if (document.metadata.description)
13
+ frontmatter.push(`description: ${JSON.stringify(document.metadata.description)}`);
14
+ if (document.metadata.mintlify?.sidebarTitle)
15
+ frontmatter.push(`sidebarTitle: ${JSON.stringify(document.metadata.mintlify.sidebarTitle)}`);
16
+ if (document.metadata.mintlify?.icon)
17
+ frontmatter.push(`icon: ${JSON.stringify(document.metadata.mintlify.icon)}`);
18
+ for (const [key, value] of Object.entries(document.metadata.mintlify?.frontmatter ?? {}).sort(([left], [right]) => left.localeCompare(right))) {
19
+ frontmatter.push(`${key}: ${JSON.stringify(value)}`);
20
+ }
21
+ return `---\n${frontmatter.join('\n')}\n---\n\n${GENERATED_MARKER}\n\n${renderContent(document.regions.main)}`;
22
+ }
23
+ export function renderSnippet(snippet) {
24
+ return `${GENERATED_MARKER}\n\n${renderContent([snippet.block])}`;
25
+ }
26
+ function renderContent(blocks) {
27
+ const aliases = snippetAliases(blocks);
28
+ const imports = [...aliases].map(([snippetPath, alias]) => `import ${alias} from "/${snippetPath}";`).join('\n');
29
+ const body = renderBlocks(blocks, 0, aliases).replace(/\n*$/, '\n');
30
+ return imports ? `${imports}\n\n${body}` : body;
31
+ }
32
+ function renderBlocks(blocks, indentation, aliases) {
33
+ return blocks.map((block) => renderBlock(block, indentation, aliases)).join('\n\n');
34
+ }
35
+ function renderBlock(block, indentation, aliases) {
36
+ const prefix = ' '.repeat(indentation);
37
+ if (block.component === 'mintlify.markdown' || block.component === 'mintlify.mdx')
38
+ return indentSource(block.props.source, indentation);
39
+ if (block.component === 'mintlify.anchor')
40
+ return `${prefix}<span id="${escapeAttribute(block.props.name)}"></span>`;
41
+ if (block.component === 'mintlify.snippet-ref') {
42
+ const snippetPath = block.props.path;
43
+ const alias = aliases.get(snippetPath);
44
+ const values = block.props.values;
45
+ const attributes = Object.entries(values).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => renderAttribute(key, value)).join('');
46
+ return `${prefix}<${alias}${attributes} />`;
47
+ }
48
+ const children = block.props.children ?? [];
49
+ const tag = nativeComponentName(block);
50
+ const attributes = Object.entries(block.props)
51
+ .filter(([key]) => key !== 'children' && key !== 'variant')
52
+ .sort(([left], [right]) => left.localeCompare(right))
53
+ .map(([key, value]) => renderAttribute(key, value))
54
+ .join('');
55
+ if (children.length === 0)
56
+ return `${prefix}<${tag}${attributes}>\n${prefix}</${tag}>`;
57
+ return `${prefix}<${tag}${attributes}>\n${renderBlocks(children, indentation + 2, aliases)}\n${prefix}</${tag}>`;
58
+ }
59
+ function snippetAliases(blocks) {
60
+ const aliases = new Map();
61
+ visitBlocks(blocks, (block) => {
62
+ if (block.component !== 'mintlify.snippet-ref')
63
+ return;
64
+ const snippetPath = block.props.path;
65
+ if (!aliases.has(snippetPath))
66
+ aliases.set(snippetPath, `AciSnippet${aliases.size + 1}`);
67
+ });
68
+ return aliases;
69
+ }
70
+ function nativeComponentName(block) {
71
+ if (block.component === 'mintlify.callout') {
72
+ const variant = block.props.variant;
73
+ return variant[0].toUpperCase() + variant.slice(1);
74
+ }
75
+ return block.component.slice('mintlify.'.length).split('-').map((part) => part[0].toUpperCase() + part.slice(1)).join('');
76
+ }
77
+ function renderAttribute(key, value) {
78
+ if (value === true)
79
+ return ` ${key}`;
80
+ if (typeof value === 'string')
81
+ return ` ${key}="${escapeAttribute(value)}"`;
82
+ return ` ${key}={${JSON.stringify(value)}}`;
83
+ }
84
+ function escapeAttribute(value) {
85
+ return value.replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
86
+ }
87
+ function indentSource(source, indentation) {
88
+ const normalized = source.replace(/\r\n/g, '\n').replace(/^\n+|\n+$/g, '');
89
+ const prefix = ' '.repeat(indentation);
90
+ return normalized.split('\n').map((line) => line.length > 0 ? `${prefix}${line}` : '').join('\n');
91
+ }
92
+ export function validateBlock(value, label, ids) {
93
+ if (!isObject(value) || value.$type !== 'component' || !nonEmptyString(value.id) || !nonEmptyString(value.component) || !isObject(value.props)) {
94
+ throw new Error(`ACI Mintlify block is invalid: ${label}`);
95
+ }
96
+ if (Object.hasOwn(value, fragmentRootScope))
97
+ ids = new Set();
98
+ if (ids.has(value.id))
99
+ throw new Error(`ACI Mintlify block ID is duplicated: ${value.id}`);
100
+ ids.add(value.id);
101
+ if (!SUPPORTED_COMPONENTS.has(value.component))
102
+ throw new Error(`ACI Mintlify component is unsupported: ${value.component}`);
103
+ const props = {};
104
+ for (const [key, entry] of Object.entries(value.props)) {
105
+ if (key === 'children') {
106
+ if (!Array.isArray(entry))
107
+ throw new Error(`ACI Mintlify block children are invalid: ${label}`);
108
+ props.children = entry.map((child, index) => validateBlock(child, `${label}.props.children[${index}]`, ids));
109
+ }
110
+ else if (!isMintlifyJSONValue(entry)) {
111
+ throw new Error(`ACI Mintlify block property is invalid: ${label}.${key}`);
112
+ }
113
+ else {
114
+ props[key] = entry;
115
+ }
116
+ }
117
+ validateComponentProps(value.component, props, label);
118
+ return preserveFragmentScope(value, { $type: 'component', id: value.id, component: value.component, props });
119
+ }
120
+ function validateComponentProps(component, props, label) {
121
+ if (component === 'mintlify.markdown' || component === 'mintlify.mdx') {
122
+ if (typeof props.source !== 'string' || props.children !== undefined)
123
+ throw new Error(`ACI Mintlify source block is invalid: ${label}`);
124
+ return;
125
+ }
126
+ if (component === 'mintlify.anchor') {
127
+ if (typeof props.name !== 'string' || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(props.name) || Object.keys(props).some((key) => key !== 'name')) {
128
+ throw new Error(`ACI Mintlify anchor is invalid: ${label}`);
129
+ }
130
+ return;
131
+ }
132
+ if (component === 'mintlify.snippet-ref') {
133
+ if (!nonEmptyString(props.path) || cleanFilePath(props.path, '.mdx') !== props.path || !isObject(props.values) || !isMintlifyJSONValue(props.values) || props.children !== undefined) {
134
+ throw new Error(`ACI Mintlify snippet reference is invalid: ${label}`);
135
+ }
136
+ return;
137
+ }
138
+ if (!Array.isArray(props.children))
139
+ throw new Error(`ACI Mintlify component children are required: ${label}`);
140
+ if (component === 'mintlify.callout' && !['info', 'warning', 'note', 'tip', 'check'].includes(String(props.variant))) {
141
+ throw new Error(`ACI Mintlify callout variant is invalid: ${label}`);
142
+ }
143
+ if (['mintlify.step', 'mintlify.card', 'mintlify.accordion', 'mintlify.tab'].includes(component) && !nonEmptyString(props.title)) {
144
+ throw new Error(`ACI Mintlify component title is required: ${label}`);
145
+ }
146
+ const expectedChild = component === 'mintlify.steps' ? 'mintlify.step'
147
+ : component === 'mintlify.card-group' ? 'mintlify.card'
148
+ : component === 'mintlify.accordion-group' ? 'mintlify.accordion'
149
+ : component === 'mintlify.tabs' ? 'mintlify.tab'
150
+ : undefined;
151
+ if (expectedChild && props.children?.some((child) => child.component !== expectedChild))
152
+ throw new Error(`ACI Mintlify ${component} child is invalid: ${label}`);
153
+ }
154
+ export function visitBlocks(blocks, visit) {
155
+ for (const block of blocks) {
156
+ visit(block);
157
+ if (block.props.children)
158
+ visitBlocks(block.props.children, visit);
159
+ }
160
+ }
161
+ function snippetReferences(blocks) {
162
+ const references = [];
163
+ visitBlocks(blocks, (block) => {
164
+ if (block.component === 'mintlify.snippet-ref')
165
+ references.push(block.props.path);
166
+ });
167
+ return references;
168
+ }
169
+ export function validateSnippetReferences(documents, snippets) {
170
+ const byPath = new Map();
171
+ for (const snippet of snippets) {
172
+ if (byPath.has(snippet.path))
173
+ throw new Error(`ACI Mintlify snippet path is duplicated: ${snippet.path}`);
174
+ byPath.set(snippet.path, snippet);
175
+ }
176
+ for (const document of documents) {
177
+ for (const reference of snippetReferences(document.regions.main)) {
178
+ if (!byPath.has(reference))
179
+ throw new Error(`ACI Mintlify snippet reference is missing: ${reference}`);
180
+ }
181
+ }
182
+ const visited = new Set();
183
+ const visiting = new Set();
184
+ const walk = (snippetPath) => {
185
+ if (visited.has(snippetPath))
186
+ return;
187
+ if (visiting.has(snippetPath))
188
+ throw new Error(`ACI Mintlify snippet reference cycle includes: ${snippetPath}`);
189
+ visiting.add(snippetPath);
190
+ const snippet = byPath.get(snippetPath);
191
+ for (const reference of snippetReferences([snippet.block])) {
192
+ if (!byPath.has(reference))
193
+ throw new Error(`ACI Mintlify snippet reference is missing: ${reference}`);
194
+ walk(reference);
195
+ }
196
+ visiting.delete(snippetPath);
197
+ visited.add(snippetPath);
198
+ };
199
+ for (const snippetPath of byPath.keys())
200
+ walk(snippetPath);
201
+ }
@@ -0,0 +1,3 @@
1
+ import type { MintlifyConfigFragment, MintlifyJSONValue, MintlifySite } from './types.js';
2
+ export declare function toDocsJSON(site: MintlifySite): Record<string, unknown>;
3
+ export declare function resolveConfigReferences(configuration: MintlifyJSONValue, fragments: MintlifyConfigFragment[]): MintlifyJSONValue;