@gradial/aci 0.1.52-rc.9 → 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,269 +0,0 @@
1
- // Runtime-neutral personalization selection. Vercel imports this module and
2
- // the CloudFront Terraform wrapper inlines the same source into its function.
3
- // Keep this file free of platform APIs and dependencies.
4
- export function selectPersonalizationVariant(request, route, claims, experiments, identity) {
5
- return resolvePersonalizationDecision(request, route, claims, experiments, identity).variant;
6
- }
7
- export function resolvePersonalizationDecision(request, route, claims, experiments, identity) {
8
- if (!route)
9
- return { variant: null, experiment: null };
10
- let variant = selectRuleVariant(request, route, claims, experiments);
11
- let experiment = null;
12
- if (identity && identity.usable && route.variants && experiments) {
13
- const candidates = Object.values(experiments)
14
- .filter((item) => (item.kind === 'ab' || item.kind === 'abn') && item.status === 'running')
15
- .filter((item) => routeHasExperiment(route, item.id))
16
- .sort((left, right) => (right.priority || 0) - (left.priority || 0)
17
- || String(left.id || '').localeCompare(String(right.id || '')));
18
- for (let index = 0; index < candidates.length; index++) {
19
- const item = candidates[index];
20
- const assignment = assignExperiment(item, identity);
21
- const candidate = assignment && route.variants[assignment.variantKey];
22
- if (!candidate)
23
- continue;
24
- if (!variant || betterVariant(candidate.priority || 0, candidate.variantKey, variant.priority || 0, variant.variantKey)) {
25
- variant = candidate;
26
- experiment = assignment;
27
- }
28
- break;
29
- }
30
- }
31
- return { variant: variant || null, experiment };
32
- }
33
- function selectRuleVariant(request, route, claims, experiments) {
34
- if (!route.variants)
35
- return null;
36
- const matches = [];
37
- for (const key in route.variants) {
38
- const variant = route.variants[key];
39
- const values = variant.variantValues || variantValuesFromKey(key);
40
- const experimentID = String(values.experiment || '').trim();
41
- if (experimentID && (!experiments || !experiments[experimentID]
42
- || experiments[experimentID].status !== 'running'))
43
- continue;
44
- if (!requestDimensionsMatch(request, values))
45
- continue;
46
- const membershipMatch = (values.audience && claims && (claims.aud || []).includes(values.audience))
47
- || (values.segment && claims && (claims.seg || []).includes(values.segment));
48
- const ruleMatch = variant.rule ? evaluateRule(request, variant.rule) : false;
49
- if (membershipMatch || ruleMatch)
50
- matches.push(variant);
51
- }
52
- matches.sort((left, right) => (right.priority || 0) - (left.priority || 0)
53
- || String(left.variantKey || '').localeCompare(String(right.variantKey || '')));
54
- return matches[0] || null;
55
- }
56
- // CloudFront KVS values are capped at 1 KiB. The variant map key is the
57
- // canonical encoding of its dimensions, so compact route records omit the
58
- // duplicated variantValues object and recover it at the edge.
59
- function variantValuesFromKey(key) {
60
- const values = {};
61
- const parts = String(key || '').replace(/&/g, ';').split(';');
62
- for (let index = 0; index < parts.length; index++) {
63
- const separator = parts[index].indexOf('=');
64
- if (separator > 0)
65
- values[parts[index].slice(0, separator)] = parts[index].slice(separator + 1);
66
- }
67
- return values;
68
- }
69
- function routeHasExperiment(route, experimentID) {
70
- for (const key in route.variants || {}) {
71
- const values = route.variants[key].variantValues || variantValuesFromKey(key);
72
- if (values.experiment === experimentID)
73
- return true;
74
- }
75
- return false;
76
- }
77
- function assignExperiment(experiment, identity) {
78
- const version = String(experiment.allocationVersion || '').trim();
79
- const salt = String(experiment.salt || '').trim();
80
- const siteID = String(identity.siteId || '').trim();
81
- const identityKey = `${String(identity.namespace || '').trim()}:${String(identity.identityHash || '').trim()}`;
82
- if (!version || !salt || !siteID || identityKey === ':')
83
- return null;
84
- const input = [siteID, String(experiment.id || ''), version, identityKey, salt].join('\0');
85
- const bucket = sha256Bucket(input);
86
- let cumulative = 0;
87
- const arms = experiment.arms || [];
88
- for (let index = 0; index < arms.length; index++) {
89
- const arm = arms[index];
90
- if ((arm.allocationBasisPoints || 0) <= 0)
91
- continue;
92
- cumulative += arm.allocationBasisPoints;
93
- if (bucket < cumulative) {
94
- return {
95
- experimentId: experiment.id,
96
- armId: arm.id,
97
- allocationVersion: version,
98
- variantKey: arm.variantKey,
99
- bucket,
100
- };
101
- }
102
- }
103
- return null;
104
- }
105
- function betterVariant(candidatePriority, candidateKey, bestPriority, bestKey) {
106
- if (candidatePriority !== bestPriority)
107
- return candidatePriority > bestPriority;
108
- return String(candidateKey || '').localeCompare(String(bestKey || '')) < 0;
109
- }
110
- function requestDimensionsMatch(request, values) {
111
- for (const rawKey in values) {
112
- const rawWant = values[rawKey];
113
- const key = String(rawKey).trim();
114
- const normalized = key.toLowerCase();
115
- const want = String(rawWant).trim();
116
- if (!key || !want || ['audience', 'segment', 'experiment', 'experimentarm'].includes(normalized))
117
- continue;
118
- const candidates = (request.query[key] || []).concat(request.query['aci_' + key] || [], request.headers['x-aci-' + normalized] || '', request.headers['x-gradial-' + normalized] || '', request.cookies[key] || '', request.cookies['aci_' + key] || '');
119
- if (normalized === 'locale') {
120
- const locales = String(request.headers['accept-language'] || '')
121
- .split(',').map((part) => part.split(';')[0].trim()).filter(Boolean);
122
- for (let localeIndex = 0; localeIndex < locales.length; localeIndex++)
123
- candidates.push(locales[localeIndex]);
124
- }
125
- if (!candidates.some((candidate) => normalized === 'locale'
126
- ? String(candidate).toLowerCase() === want.toLowerCase()
127
- : String(candidate) === want))
128
- return false;
129
- }
130
- return true;
131
- }
132
- function evaluateRule(request, rule) {
133
- const all = rule.all || [];
134
- const any = rule.any || [];
135
- if (all.length + any.length === 0)
136
- return false;
137
- return all.every((condition) => evaluateCondition(request, condition))
138
- && (any.length === 0 || any.some((condition) => evaluateCondition(request, condition)));
139
- }
140
- function evaluateCondition(request, condition) {
141
- const values = signalValues(request, condition.field);
142
- if (condition.operator === 'exists')
143
- return values.length > 0;
144
- if (values.length === 0 || !['string', 'number', 'boolean'].includes(typeof condition.value))
145
- return false;
146
- const expected = String(condition.value);
147
- return values.some((value) => {
148
- switch (condition.operator) {
149
- case 'equals': return value === expected;
150
- case 'contains': return value.includes(expected);
151
- case 'startsWith': return value.startsWith(expected);
152
- default: return false;
153
- }
154
- });
155
- }
156
- function signalValues(request, field) {
157
- if (field.startsWith('query.'))
158
- return request.query[field.slice(6)] || [];
159
- if (field.startsWith('header.'))
160
- return request.headers[field.slice(7).toLowerCase()] ? [request.headers[field.slice(7).toLowerCase()]] : [];
161
- if (field.startsWith('cookie.'))
162
- return request.cookies[field.slice(7)] ? [request.cookies[field.slice(7)]] : [];
163
- if (field.startsWith('utm.'))
164
- return request.query['utm_' + field.slice(4)] || [];
165
- if (field.startsWith('geo.')) {
166
- const geo = request.signals && request.signals.geo;
167
- const value = geo && geo[field.slice(4).toLowerCase()];
168
- return value ? [value] : [];
169
- }
170
- if (field === 'referrer') {
171
- const value = request.headers.referer || request.headers.referrer;
172
- return value ? [value] : [];
173
- }
174
- return [];
175
- }
176
- // SHA-256 mirrors experiment.assignment.go: the first eight digest bytes are
177
- // interpreted big-endian and reduced modulo 10,000 basis points.
178
- // CLOUDFRONT_STRIP_START — the build script removes this block for CloudFront
179
- // Functions (experiment bucketing moves to the hosting Lambda to stay under
180
- // the 10 KiB function size limit).
181
- function sha256Bucket(value) {
182
- const bytes = utf8(value);
183
- const words = new Uint32Array(64);
184
- const bitLength = bytes.length * 8;
185
- const paddedLength = ((bytes.length + 9 + 63) >> 6) << 6;
186
- const data = new Uint8Array(paddedLength);
187
- data.set(bytes);
188
- data[bytes.length] = 128;
189
- // The message length is a 64-bit big-endian integer across the final
190
- // eight bytes. The previous code wrote a single truncated byte into each
191
- // word's most significant position, corrupting every digest (caught by
192
- // the assignment parity vectors, spec experiment-variant-caching REQ-9).
193
- const bitsHigh = Math.floor(bitLength / 0x100000000);
194
- const bitsLow = bitLength >>> 0;
195
- data[paddedLength - 8] = (bitsHigh >>> 24) & 255;
196
- data[paddedLength - 7] = (bitsHigh >>> 16) & 255;
197
- data[paddedLength - 6] = (bitsHigh >>> 8) & 255;
198
- data[paddedLength - 5] = bitsHigh & 255;
199
- data[paddedLength - 4] = (bitsLow >>> 24) & 255;
200
- data[paddedLength - 3] = (bitsLow >>> 16) & 255;
201
- data[paddedLength - 2] = (bitsLow >>> 8) & 255;
202
- data[paddedLength - 1] = bitsLow & 255;
203
- let a = 0x6a09e667, b = 0xbb67ae85, c = 0x3c6ef372, d = 0xa54ff53a;
204
- let e = 0x510e527f, f = 0x9b05688c, g = 0x1f83d9ab, h = 0x5be0cd19;
205
- const k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
206
- for (let offset = 0; offset < paddedLength; offset += 64) {
207
- for (let i = 0; i < 16; i++) {
208
- const p = offset + i * 4;
209
- words[i] = (data[p] << 24) | (data[p + 1] << 16) | (data[p + 2] << 8) | data[p + 3];
210
- }
211
- for (let i = 16; i < 64; i++) {
212
- const x = words[i - 15], y = words[i - 2];
213
- words[i] = (words[i - 16] + (rotr(x, 7) ^ rotr(x, 18) ^ (x >>> 3)) + words[i - 7] + (rotr(y, 17) ^ rotr(y, 19) ^ (y >>> 10))) >>> 0;
214
- }
215
- let aa = a, bb = b, cc = c, dd = d, ee = e, ff = f, gg = g, hh = h;
216
- for (let i = 0; i < 64; i++) {
217
- const s1 = rotr(ee, 6) ^ rotr(ee, 11) ^ rotr(ee, 25);
218
- const ch = (ee & ff) ^ (~ee & gg);
219
- const t1 = (hh + s1 + ch + k[i] + words[i]) >>> 0;
220
- const s0 = rotr(aa, 2) ^ rotr(aa, 13) ^ rotr(aa, 22);
221
- const maj = (aa & bb) ^ (aa & cc) ^ (bb & cc);
222
- const t2 = (s0 + maj) >>> 0;
223
- hh = gg;
224
- gg = ff;
225
- ff = ee;
226
- ee = (dd + t1) >>> 0;
227
- dd = cc;
228
- cc = bb;
229
- bb = aa;
230
- aa = (t1 + t2) >>> 0;
231
- }
232
- a = (a + aa) >>> 0;
233
- b = (b + bb) >>> 0;
234
- c = (c + cc) >>> 0;
235
- d = (d + dd) >>> 0;
236
- e = (e + ee) >>> 0;
237
- f = (f + ff) >>> 0;
238
- g = (g + gg) >>> 0;
239
- h = (h + hh) >>> 0;
240
- }
241
- // Reduce the 64-bit big-endian value modulo 10000 in 32-bit halves:
242
- // (hi*2^32 + lo) % 10000 == ((hi%10000) * (2^32 % 10000) + lo%10000) % 10000,
243
- // and 2^32 % 10000 = 7296. Multiplying hi by 2^32 directly exceeds
244
- // Number.MAX_SAFE_INTEGER and rounds away the low bits — that float error
245
- // made most JS buckets diverge from assignment.go (caught by the
246
- // assignment parity vectors, spec experiment-variant-caching REQ-9).
247
- return (((a >>> 0) % 10000) * 7296 + ((b >>> 0) % 10000)) % 10000;
248
- }
249
- function rotr(value, amount) { return (value >>> amount) | (value << (32 - amount)); }
250
- function utf8(value) {
251
- const out = [];
252
- for (let i = 0; i < value.length; i++) {
253
- let code = value.charCodeAt(i);
254
- if (code >= 0xd800 && code <= 0xdbff && i + 1 < value.length) {
255
- const next = value.charCodeAt(++i);
256
- code = 0x10000 + ((code - 0xd800) << 10) + (next - 0xdc00);
257
- }
258
- if (code < 0x80)
259
- out.push(code);
260
- else if (code < 0x800)
261
- out.push(0xc0 | (code >> 6), 0x80 | (code & 63));
262
- else if (code < 0x10000)
263
- out.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 63), 0x80 | (code & 63));
264
- else
265
- out.push(0xf0 | (code >> 18), 0x80 | ((code >> 12) & 63), 0x80 | ((code >> 6) & 63), 0x80 | (code & 63));
266
- }
267
- return new Uint8Array(out);
268
- }
269
- // CLOUDFRONT_STRIP_END
@@ -1,24 +0,0 @@
1
- export interface ObjectReader {
2
- readObject(path: string): Promise<Uint8Array>;
3
- }
4
- export interface ComposeObjectRef {
5
- path: string;
6
- digest: string;
7
- }
8
- export interface ComposeSlot {
9
- name: string;
10
- required: boolean;
11
- placeholder: string;
12
- parts: ComposeObjectRef[];
13
- }
14
- export interface ComposeEnvelope {
15
- schema: 'gradial.compose-envelope.v1';
16
- algorithm: 'aci-html-compose-v1';
17
- shell: ComposeObjectRef;
18
- slots: ComposeSlot[];
19
- }
20
- export declare function composeEnvelope(reader: ObjectReader, envelopePath: string, envelopeDigest: string): Promise<{
21
- html: string;
22
- contentType: string;
23
- }>;
24
- export declare function validateEnvelope(envelope: unknown): ComposeEnvelope;
@@ -1,89 +0,0 @@
1
- const decoder = new TextDecoder('utf-8', { fatal: true });
2
- export async function composeEnvelope(reader, envelopePath, envelopeDigest) {
3
- const envelopeBytes = await reader.readObject(envelopePath);
4
- await requireDigest(envelopeBytes, envelopeDigest, 'compose envelope');
5
- let parsed;
6
- try {
7
- parsed = JSON.parse(decoder.decode(envelopeBytes));
8
- }
9
- catch {
10
- throw new Error('compose envelope is malformed');
11
- }
12
- const envelope = validateEnvelope(parsed);
13
- const shellBytes = await reader.readObject(envelope.shell.path);
14
- await requireDigest(shellBytes, envelope.shell.digest, 'compose shell');
15
- let html = decoder.decode(shellBytes);
16
- for (const slot of envelope.slots) {
17
- if (occurrences(html, slot.placeholder) !== 1) {
18
- throw new Error(`compose slot ${JSON.stringify(slot.name)} placeholder must occur exactly once`);
19
- }
20
- if (slot.required && slot.parts.length === 0) {
21
- throw new Error(`compose slot ${JSON.stringify(slot.name)} is required`);
22
- }
23
- const parts = [];
24
- for (const part of slot.parts) {
25
- const bytes = await reader.readObject(part.path);
26
- await requireDigest(bytes, part.digest, `compose slot ${slot.name}`);
27
- parts.push(decoder.decode(bytes));
28
- }
29
- html = html.replace(slot.placeholder, parts.join(''));
30
- }
31
- if (html.includes('<!--ACI_SLOT:')) {
32
- throw new Error('compose shell has unresolved slots');
33
- }
34
- return { html, contentType: 'text/html; charset=utf-8' };
35
- }
36
- export function validateEnvelope(envelope) {
37
- if (!envelope || typeof envelope !== 'object') {
38
- throw new Error('compose envelope is malformed');
39
- }
40
- const obj = envelope;
41
- if (obj.schema !== 'gradial.compose-envelope.v1'
42
- || obj.algorithm !== 'aci-html-compose-v1'
43
- || !Array.isArray(obj.slots)) {
44
- throw new Error('compose envelope is malformed');
45
- }
46
- const env = envelope;
47
- validateRef(env.shell);
48
- const names = new Set();
49
- for (const slot of env.slots) {
50
- if (!slot || !/^[A-Za-z][A-Za-z0-9_-]*$/.test(slot.name) || names.has(slot.name)) {
51
- throw new Error('compose slot is malformed');
52
- }
53
- names.add(slot.name);
54
- if (slot.placeholder !== `<!--ACI_SLOT:${slot.name}-->` || !Array.isArray(slot.parts)) {
55
- throw new Error(`compose slot ${JSON.stringify(slot.name)} is malformed`);
56
- }
57
- for (const part of slot.parts)
58
- validateRef(part);
59
- }
60
- return env;
61
- }
62
- function validateRef(ref) {
63
- const path = String(ref?.path || '');
64
- if (!path || path.includes('\\') || path.split('/').some((part) => !part || part === '.' || part === '..')) {
65
- throw new Error('compose object path is invalid');
66
- }
67
- if (!/^[0-9a-f]{64}$/.test(String(ref.digest || '').toLowerCase())) {
68
- throw new Error('compose object digest is invalid');
69
- }
70
- }
71
- async function requireDigest(bytes, want, label) {
72
- const ab = new ArrayBuffer(bytes.byteLength);
73
- new Uint8Array(ab).set(bytes);
74
- const actual = Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', ab)), (byte) => byte.toString(16).padStart(2, '0')).join('');
75
- if (actual !== want.toLowerCase())
76
- throw new Error(`${label} digest mismatch`);
77
- }
78
- function occurrences(value, needle) {
79
- let count = 0;
80
- let offset = 0;
81
- while (true) {
82
- offset = value.indexOf(needle, offset);
83
- if (offset < 0)
84
- break;
85
- count += 1;
86
- offset += needle.length;
87
- }
88
- return count;
89
- }
@@ -1,71 +0,0 @@
1
- import type { EdgeRouteEntry, ClassificationData } from '../types.js';
2
- /**
3
- * Unique sentinel object returned by expand functions on failure.
4
- * Callers check `result === INVALID`. Never throws.
5
- */
6
- export declare const INVALID: unique symbol;
7
- export type Invalid = typeof INVALID;
8
- interface CompactDispatch {
9
- k: 's';
10
- ap: string;
11
- }
12
- interface CompactCondition {
13
- f: string;
14
- o: string;
15
- w?: unknown;
16
- }
17
- interface CompactRule {
18
- a?: CompactCondition[];
19
- n?: CompactCondition[];
20
- }
21
- interface CompactVariant {
22
- pr?: number;
23
- r?: CompactRule;
24
- d?: CompactDispatch;
25
- vk?: string;
26
- vv?: Record<string, string>;
27
- }
28
- export interface CompactRouteRecord {
29
- s?: number;
30
- p: string;
31
- d: CompactDispatch;
32
- v?: Record<string, CompactVariant>;
33
- x?: string[];
34
- }
35
- export declare function projectRouteRecord(entry: EdgeRouteEntry): CompactRouteRecord;
36
- export declare function expandRouteRecord(raw: unknown): EdgeRouteEntry | Invalid;
37
- export interface CompactClassificationData {
38
- au?: Record<string, {
39
- r?: CompactRule;
40
- }>;
41
- sg?: Record<string, {
42
- r?: CompactRule;
43
- }>;
44
- x?: Record<string, CompactExperiment>;
45
- }
46
- interface CompactExperimentArm {
47
- id: string;
48
- ab?: number;
49
- vk?: string;
50
- ro?: string;
51
- }
52
- interface CompactExperiment {
53
- id: string;
54
- st?: string;
55
- k?: string;
56
- av?: string;
57
- sl?: string;
58
- pr?: number;
59
- ar?: CompactExperimentArm[];
60
- dn?: string;
61
- pg?: string;
62
- gl?: string[];
63
- }
64
- export declare function projectClassificationData(data: ClassificationData): CompactClassificationData;
65
- export declare function expandClassificationData(raw: unknown): ClassificationData | Invalid;
66
- export declare const MARKER = "{\"s\":1,\"e\":1}";
67
- export declare function isMarker(raw: string): boolean;
68
- export declare function canonicalSerialize(value: unknown): string;
69
- export declare function variantValuesFromKey(key: string): Record<string, string>;
70
- export declare function pathHash16(path: string): string;
71
- export {};