@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.
- 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
|
@@ -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
|
|
9
|
-
operator
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
188
|
+
experiments: Array<{
|
|
176
189
|
id: string;
|
|
177
190
|
arm: string;
|
|
178
191
|
allocationVersion: string;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
//
|
|
6
|
-
//
|
|
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('&', '&').replaceAll('"', '"').replaceAll('<', '<').replaceAll('>', '>');
|
|
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;
|