@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.
- package/CHANGELOG.md +126 -0
- package/README.md +86 -5
- package/dist/astro/error-page.js +1 -0
- package/dist/astro/index.js +4 -0
- package/dist/astro/page.d.ts +5 -2
- package/dist/astro/page.js +35 -23
- package/dist/astro/preview.js +2 -17
- package/dist/audience-data-models.d.ts +308 -197
- package/dist/audience-data-models.js +313 -51
- package/dist/cli/compile-registry.mjs +6 -34
- package/dist/cli/mintlify-render.mjs +8 -0
- package/dist/collect/aci.js +1 -1
- package/dist/collect/aci.js.sha256 +1 -1
- package/dist/collect/api.d.ts +6 -7
- package/dist/collect/api.js +21 -8
- package/dist/collect/assignments.d.ts +7 -9
- package/dist/collect/assignments.js +23 -32
- package/dist/collect/batcher.d.ts +9 -7
- package/dist/collect/batcher.js +6 -5
- package/dist/collect/consent/envelope.d.ts +7 -1
- package/dist/collect/consent/envelope.js +3 -1
- package/dist/collect/consent/mapping.d.ts +2 -0
- package/dist/collect/consent/mapping.js +1 -0
- package/dist/collect/consenthold.d.ts +38 -0
- package/dist/collect/consenthold.js +82 -0
- package/dist/collect/declarative.js +7 -5
- package/dist/collect/gapbuffer.d.ts +13 -4
- package/dist/collect/gapbuffer.js +21 -10
- package/dist/collect/index.js +78 -21
- package/dist/collect/pageviews.d.ts +11 -11
- package/dist/collect/pageviews.js +27 -22
- package/dist/consent-banner/index.d.ts +17 -0
- package/dist/consent-banner/index.js +23 -0
- package/dist/content/index.d.ts +2 -1
- package/dist/content/index.js +4 -1
- package/dist/content/provider.d.ts +16 -2
- package/dist/content/provider.js +5 -1
- package/dist/content/resolve-slots.d.ts +24 -2
- package/dist/content/resolve-slots.js +127 -21
- package/dist/content/types.d.ts +5 -1
- package/dist/edge/expression-capabilities.v1.d.ts +106 -0
- package/dist/edge/expression-capabilities.v1.js +165 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/internal/domain/edge/identity.d.ts +47 -4
- package/dist/internal/domain/edge/identity.js +130 -26
- package/dist/internal/domain/edge/index.d.ts +1 -1
- package/dist/internal/domain/edge/index.js +1 -1
- package/dist/internal/domain/edge/mimic_token.d.ts +14 -0
- package/dist/internal/domain/edge/mimic_token.js +125 -0
- package/dist/internal/domain/edge/personalization-core.d.ts +61 -22
- package/dist/internal/domain/edge/personalization-core.js +790 -200
- package/dist/internal/domain/edge/personalization_index.d.ts +17 -0
- package/dist/internal/domain/edge/personalization_index.js +55 -0
- package/dist/internal/domain/edge/resolve.js +76 -39
- package/dist/internal/domain/edge/testdata/flat_only_validator_v1.d.ts +10 -0
- package/dist/internal/domain/edge/testdata/flat_only_validator_v1.js +253 -0
- package/dist/internal/domain/edge/types.d.ts +82 -78
- package/dist/internal/domain/edge/types.js +3 -3
- package/dist/internal/domain/edge/variant_key.d.ts +4 -0
- package/dist/internal/domain/edge/variant_key.js +56 -0
- package/dist/internal/platform/collectionstore/errors.d.ts +24 -0
- package/dist/internal/platform/collectionstore/errors.js +48 -0
- package/dist/mintlify/blocks.d.ts +6 -0
- package/dist/mintlify/blocks.js +201 -0
- package/dist/mintlify/config.d.ts +3 -0
- package/dist/mintlify/config.js +77 -0
- package/dist/mintlify/index.d.ts +3 -0
- package/dist/mintlify/index.js +2 -0
- package/dist/mintlify/json.d.ts +8 -0
- package/dist/mintlify/json.js +37 -0
- package/dist/mintlify/materialize.d.ts +14 -0
- package/dist/mintlify/materialize.js +254 -0
- package/dist/mintlify/types.d.ts +74 -0
- package/dist/mintlify/types.js +1 -0
- package/dist/mintlify/validate.d.ts +7 -0
- package/dist/mintlify/validate.js +256 -0
- package/dist/next/assignments-navigation.d.ts +4 -0
- package/dist/next/assignments-navigation.js +18 -0
- package/dist/next/assignments-script.d.ts +3 -2
- package/dist/next/assignments-script.js +5 -5
- package/dist/next/error-pages.d.ts +4 -0
- package/dist/next/error-pages.js +4 -0
- package/dist/next/error-seat-payload.d.ts +2 -1
- package/dist/next/error-seat-payload.js +14 -24
- package/dist/next/error-seat.js +7 -2
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.js +3 -1
- package/dist/next/page.d.ts +2 -1
- package/dist/next/page.js +15 -4
- package/dist/next/release-context.d.ts +30 -0
- package/dist/next/release-context.js +56 -0
- package/dist/next/render.d.ts +1 -1
- package/dist/next/render.js +15 -5
- package/dist/next/server.d.ts +9 -3
- package/dist/next/server.js +17 -32
- package/dist/preview/core.d.ts +1 -0
- package/dist/preview/core.js +27 -0
- package/dist/react/VideoPlayer.d.ts +3 -3
- package/dist/react/index.d.ts +3 -3
- package/dist/registry.d.ts +4 -2
- package/dist/testing/conformance/checks.d.ts +2 -2
- package/dist/testing/conformance/checks.js +2 -0
- package/dist/testing/conformance/index.d.ts +1 -0
- package/dist/testing/conformance/index.js +1 -0
- package/dist/testing/conformance/next-stubs.d.ts +6 -0
- package/dist/testing/conformance/next-stubs.js +9 -0
- package/dist/testing/conformance/preset.d.ts +3 -2
- package/dist/testing/conformance/preset.js +15 -8
- package/dist/testing/conformance/root-identity.d.ts +31 -0
- package/dist/testing/conformance/root-identity.js +361 -0
- package/dist/testing/conformance/stubs/next-image.d.ts +7 -0
- package/dist/testing/conformance/stubs/next-image.js +5 -0
- package/dist/testing/conformance/stubs/next-link.d.ts +7 -0
- package/dist/testing/conformance/stubs/next-link.js +4 -0
- package/dist/testing/conformance/types.d.ts +5 -2
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +4 -3
- package/dist/types/block-ref.js +2 -2
- package/dist/types/component.d.ts +9 -0
- package/dist/types/config.d.ts +0 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.js +0 -1
- package/dist/types/page.d.ts +0 -1
- package/migrations/conformance-registry.md +22 -0
- package/migrations/experiment-goal-ids.md +43 -0
- package/package.json +21 -7
- package/src/cli/compile-registry.mjs +6 -30
- package/src/cli/mintlify-render.mjs +9 -0
- package/src/types/block-ref.test.ts +16 -1
- package/src/types/block-ref.ts +2 -2
- package/src/types/component.ts +10 -0
- package/src/types/config.ts +0 -1
- package/src/types/index.ts +0 -1
- package/src/types/page.ts +0 -1
- package/templates/astro/starter-content/src/components/chrome/Footer.astro +5 -3
- package/templates/astro/starter-content/src/components/chrome/Header.astro +5 -3
- package/templates/astro/starter-content/src/components/elements/Button.astro +5 -2
- package/templates/astro/starter-content/src/components/elements/ButtonBlock.astro +5 -3
- package/templates/astro/starter-content/src/components/sections/ContainerSection.astro +4 -3
- package/templates/astro/starter-content/src/components/sections/FeatureCard.astro +4 -3
- package/templates/astro/starter-content/src/components/sections/FeatureGrid.astro +4 -3
- package/templates/astro/starter-content/src/components/sections/HomeHero.astro +5 -3
- package/templates/astro/starter-content/src/components/sections/PersonalizationChoice.astro +4 -3
- package/templates/astro/starter-content/src/components/sections/StatsBar.astro +4 -3
- package/templates/astro/starter-content/src/fixtures/sections.ts +1 -1
- package/templates/astro/starter-content/src/stories/CompositionStoryFrame.astro +7 -2
- package/templates/astro/template/.aci.yaml.tmpl +0 -1
- package/templates/astro/template/package.json.tmpl +5 -5
- package/templates/astro/template/src/layouts/SiteLayout.astro +2 -2
- package/templates/common/.agents/skills/aci/SKILL.md +58 -1
- package/templates/common/.agents/skills/authoring/SKILL.md +194 -42
- package/templates/common/.agents/skills/authoring/references/block-registry.md +19 -3
- package/templates/common/.agents/skills/authoring/references/contracts.md +9 -30
- package/templates/common/.agents/skills/authoring/references/repo-structure.md +5 -4
- package/templates/common/.agents/skills/authoring/references/sdk-server-functions.md +3 -3
- package/templates/common/.agents/skills/dam-delivery/SKILL.md +216 -0
- package/templates/common/.agents/skills/dam-delivery/references/permalink-grammar.md +167 -0
- package/templates/common/.agents/skills/migration/references/canon-migration.md +18 -67
- package/templates/common/.agents/skills/migration/references/selector-map.md +1 -1
- package/templates/common/.content/fragments/navbar/_index.json +24 -0
- package/templates/common/.content/fragments/site-footer/_index.json +61 -0
- package/templates/common/.content/pages/404/_index.json +4 -2
- package/templates/common/.content/pages/_index.json +0 -1
- package/templates/common/.content/pages/error/_index.json +4 -2
- package/templates/common/.content/schemas/site.schema.json +11 -1
- package/templates/common/.content/templates/error.json +24 -0
- package/templates/common/.content/templates/home.json +28 -0
- package/templates/common/.content/templates/site.json +8 -0
- package/templates/common/.content/templates/standard.json +24 -0
- package/templates/common/.nvmrc +1 -1
- package/templates/common/starter-content/.content/config/site.json +1 -1
- package/templates/common/starter-content/.content/dam/README.md +74 -0
- package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000101}/asset.json +1 -1
- package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000102}/asset.json +1 -1
- package/templates/common/starter-content/.content/pages/404/_index.json +1 -2
- package/templates/common/starter-content/.content/pages/{home/_index.json → _index.json} +14 -3
- package/templates/common/starter-content/.content/pages/about/_index.json +2 -2
- package/templates/common/starter-content/.content/pages/error/_index.json +1 -2
- package/templates/common/starter-content/.content/patterns/components/button.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/container.json +8 -3
- package/templates/common/starter-content/.content/patterns/components/feature_card.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/feature_grid.json +5 -3
- package/templates/common/starter-content/.content/patterns/components/home_hero.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/personalization_choice.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/site-footer.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/site-navigation.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/stats_bar.json +2 -2
- package/templates/common/starter-content/.content/schemas/site.schema.json +11 -1
- package/templates/nextjs/starter-content/src/components/chrome/Footer.tsx +4 -3
- package/templates/nextjs/starter-content/src/components/chrome/Header.tsx +4 -3
- package/templates/nextjs/starter-content/src/components/elements/Button.tsx +4 -3
- package/templates/nextjs/starter-content/src/components/elements/ButtonBlock.tsx +4 -3
- package/templates/nextjs/starter-content/src/components/sections/ContainerSection.tsx +4 -2
- package/templates/nextjs/starter-content/src/components/sections/FeatureCard.tsx +4 -3
- package/templates/nextjs/starter-content/src/components/sections/FeatureGrid.tsx +4 -2
- package/templates/nextjs/starter-content/src/components/sections/HomeHero.tsx +4 -3
- package/templates/nextjs/starter-content/src/components/sections/PersonalizationChoice.tsx +4 -2
- package/templates/nextjs/starter-content/src/components/sections/StatsBar.tsx +4 -3
- package/templates/nextjs/template/.aci.yaml +0 -1
- package/templates/nextjs/template/.aci.yaml.tmpl +0 -1
- package/templates/nextjs/template/package.json.tmpl +4 -3
- package/templates/nextjs/template/src/app/_components/c15t-bridge.ts +30 -0
- package/templates/nextjs/template/src/app/_components/c15t-consent-ui.tsx +37 -0
- package/templates/nextjs/template/src/app/_components/consent-manager.css +63 -0
- package/templates/nextjs/template/src/app/_components/consent-manager.tsx +76 -0
- package/templates/nextjs/template/src/app/fonts/inter/OFL.txt +93 -0
- package/templates/nextjs/template/src/app/fonts/inter/inter-latin.woff2 +0 -0
- package/templates/nextjs/template/src/app/fonts/space-grotesk/OFL.txt +93 -0
- package/templates/nextjs/template/src/app/fonts/space-grotesk/space-grotesk-latin.woff2 +0 -0
- package/templates/nextjs/template/src/app/layout.tsx +12 -7
- package/templates/nextjs/template/tests/conformance.test.ts.tmpl +1 -0
- package/dist/define-layout.d.ts +0 -6
- package/dist/define-layout.js +0 -14
- package/dist/edge/personalization-core.d.ts +0 -11
- package/dist/edge/personalization-core.js +0 -269
- package/dist/internal/domain/edge/compose.d.ts +0 -24
- package/dist/internal/domain/edge/compose.js +0 -89
- package/dist/internal/domain/edge/record/codec.d.ts +0 -71
- package/dist/internal/domain/edge/record/codec.js +0 -401
- package/dist/next/collect-route.d.ts +0 -19
- package/dist/next/collect-route.js +0 -459
- package/dist/next/edge-config.d.ts +0 -1
- package/dist/next/edge-config.js +0 -92
- package/dist/next/envelope-digest.d.ts +0 -21
- package/dist/next/envelope-digest.js +0 -58
- package/dist/next/middleware.d.ts +0 -40
- package/dist/next/middleware.js +0 -475
- package/dist/next/personalization-host.d.ts +0 -134
- package/dist/next/personalization-host.js +0 -270
- package/dist/next/root-layout.d.ts +0 -4
- package/dist/next/root-layout.js +0 -5
- package/dist/next/vercel-collect-sdk-route.d.ts +0 -27
- package/dist/next/vercel-collect-sdk-route.js +0 -181
- package/dist/next/vercel-edge-projection-v1.d.ts +0 -24
- package/dist/next/vercel-edge-projection-v1.js +0 -880
- package/dist/next/vercel-personalization.d.ts +0 -4
- package/dist/next/vercel-personalization.js +0 -4
- package/dist/next/vercel-s3-personalization.d.ts +0 -79
- package/dist/next/vercel-s3-personalization.js +0 -570
- package/dist/types/layout.d.ts +0 -24
- package/src/types/layout.ts +0 -29
- package/templates/astro/starter-content/src/render/RenderPage.astro +0 -67
- package/templates/astro/template/src/cms/contracts/layouts/index.ts +0 -18
- package/templates/common/starter-content/.content/patterns/layouts/default.json +0 -14
- package/templates/nextjs/starter-content/src/components/elements/AtomicBlock.tsx +0 -13
- package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +0 -16
- /package/dist/{types/layout.js → cli/mintlify-render.d.mts} +0 -0
- /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000101}/editorial.json +0 -0
- /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000101}/governance.json +0 -0
- /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000101}/versions/v0001.json +0 -0
- /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000102}/editorial.json +0 -0
- /package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000102}/governance.json +0 -0
- /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 {};
|