@gradial/aci 0.1.52-rc.3 → 0.1.52-rc.34-e2e.1
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 +80 -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 -194
- package/dist/audience-data-models.js +314 -51
- package/dist/cli/compile-registry.d.mts +5 -1
- package/dist/cli/compile-registry.mjs +31 -46
- package/dist/cli/mintlify-render.mjs +8 -0
- package/dist/cli/validate-content.d.mts +31 -1
- package/dist/cli/validate-content.mjs +50 -12
- 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 +67 -20
- 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 +33 -4
- package/dist/content/resolve-slots.js +127 -19
- package/dist/content/types.d.ts +7 -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/compose.d.ts +24 -0
- package/dist/internal/domain/edge/compose.js +89 -0
- package/dist/internal/domain/edge/identity.d.ts +5 -0
- package/dist/internal/domain/edge/identity.js +50 -14
- package/dist/internal/domain/edge/index.d.ts +2 -0
- package/dist/internal/domain/edge/index.js +2 -0
- 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 +789 -198
- package/dist/internal/domain/edge/personalization_index.d.ts +17 -0
- package/dist/internal/domain/edge/personalization_index.js +56 -0
- package/dist/internal/domain/edge/record/codec.d.ts +6 -32
- package/dist/internal/domain/edge/record/codec.js +86 -168
- 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 +93 -41
- package/dist/internal/domain/edge/types.js +3 -3
- package/dist/internal/domain/edge/variant_key.d.ts +3 -0
- package/dist/internal/domain/edge/variant_key.js +44 -0
- package/dist/internal/platform/collectionstore/errors.d.ts +24 -0
- package/dist/internal/platform/collectionstore/errors.js +48 -0
- package/dist/mimic/core.d.ts +2 -0
- package/dist/mimic/core.js +31 -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/hosted-page-target.d.ts +7 -0
- package/dist/next/hosted-page-target.js +39 -0
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.js +3 -1
- package/dist/next/page.d.ts +29 -6
- package/dist/next/page.js +47 -25
- package/dist/next/release-context.d.ts +29 -0
- package/dist/next/release-context.js +55 -0
- package/dist/next/render.d.ts +1 -1
- package/dist/next/render.js +15 -5
- package/dist/next/server.d.ts +8 -0
- package/dist/next/server.js +44 -3
- package/dist/next/vercel-edge-projection-v1.d.ts +0 -9
- package/dist/next/vercel-edge-projection-v1.js +9 -539
- 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/VideoPlayer.js +2 -2
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.js +2 -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.d.ts +2 -0
- package/dist/types/block-ref.js +3 -2
- package/dist/types/component.d.ts +9 -0
- package/dist/types/config.d.ts +0 -1
- package/dist/types/image.d.ts +54 -17
- package/dist/types/image.js +102 -18
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.js +0 -1
- package/dist/types/media.d.ts +14 -6
- package/dist/types/page.d.ts +0 -1
- package/dist/types/video.d.ts +7 -3
- package/migrations/conformance-registry.md +22 -0
- package/package.json +21 -7
- package/src/cli/compile-registry.mjs +30 -42
- package/src/cli/mintlify-render.mjs +9 -0
- package/src/cli/validate-content.mjs +52 -13
- package/src/types/block-ref.test.ts +60 -0
- package/src/types/block-ref.ts +4 -2
- package/src/types/component.ts +10 -0
- package/src/types/config.ts +0 -1
- package/src/types/image.ts +133 -29
- package/src/types/index.ts +0 -1
- package/src/types/page.ts +0 -1
- package/src/types/video.ts +1 -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/atomicBlocks.ts +7 -6
- package/templates/astro/starter-content/src/fixtures/composition.ts +3 -2
- package/templates/astro/starter-content/src/fixtures/sections.ts +3 -3
- 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 +203 -43
- package/templates/common/.agents/skills/authoring/references/block-registry.md +19 -3
- package/templates/common/.agents/skills/authoring/references/block-slots.md +3 -0
- 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 +217 -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/{home/_index.json → _index.json} +10 -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/.rules-baseline.json +8 -0
- package/templates/common/starter-content/.content/config/site.json +1 -1
- package/templates/common/starter-content/.content/content-rules/no-public-resource-paths.json +6 -0
- package/templates/common/starter-content/.content/dam/README.md +74 -0
- package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000101}/asset.json +1 -1
- package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000101}/governance.json +2 -1
- package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_about_hero → 000000000000000000000000102}/asset.json +1 -1
- package/templates/common/starter-content/.content/dam/folders/seed/{ast_site_home_hero → 000000000000000000000000102}/governance.json +2 -1
- package/templates/common/starter-content/.content/fragments/navbar/_index.json +23 -20
- package/templates/common/starter-content/.content/fragments/site-footer/_index.json +59 -56
- package/templates/common/starter-content/.content/pages/404/_index.json +2 -2
- package/templates/common/starter-content/.content/pages/{home/_index.json → _index.json} +23 -3
- package/templates/common/starter-content/.content/pages/about/_index.json +5 -2
- package/templates/common/starter-content/.content/pages/error/_index.json +2 -2
- package/templates/common/starter-content/.content/patterns/components/button.json +55 -0
- package/templates/common/starter-content/.content/patterns/components/container.json +67 -0
- package/templates/common/starter-content/.content/patterns/components/feature_card.json +30 -0
- package/templates/common/starter-content/.content/patterns/components/feature_grid.json +59 -0
- package/templates/common/starter-content/.content/patterns/components/home_hero.json +151 -0
- package/templates/common/starter-content/.content/patterns/components/personalization_choice.json +42 -0
- package/templates/common/starter-content/.content/patterns/components/site-footer.json +203 -0
- package/templates/common/starter-content/.content/patterns/components/site-navigation.json +115 -0
- package/templates/common/starter-content/.content/patterns/components/stats_bar.json +45 -0
- 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 +9 -0
- 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/next/collect-route.d.ts +0 -19
- package/dist/next/collect-route.js +0 -459
- 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-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/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/.content/overlays/audience/preference-blue/pages/{home/_index.json → _index.json} +0 -0
- /package/templates/common/.content/overlays/audience/preference-red/pages/{home/_index.json → _index.json} +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_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_about_hero → 000000000000000000000000102}/versions/v0001.json +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { FragmentRefNode } from '../types/page.js';
|
|
2
|
+
export type MintlifyJSONValue = null | boolean | number | string | MintlifyJSONValue[] | {
|
|
3
|
+
[key: string]: MintlifyJSONValue;
|
|
4
|
+
};
|
|
5
|
+
export interface MintlifyNavigationGroup {
|
|
6
|
+
group: string;
|
|
7
|
+
pages: string[];
|
|
8
|
+
}
|
|
9
|
+
/** Native Mintlify docs.json configuration from `.content/config/mintlify.json`. */
|
|
10
|
+
export type MintlifySite = Record<string, MintlifyJSONValue>;
|
|
11
|
+
export interface MintlifyBlock {
|
|
12
|
+
$type: 'component';
|
|
13
|
+
id: string;
|
|
14
|
+
component: 'mintlify.markdown' | 'mintlify.mdx' | 'mintlify.anchor' | 'mintlify.steps' | 'mintlify.step' | 'mintlify.card-group' | 'mintlify.card' | 'mintlify.accordion-group' | 'mintlify.accordion' | 'mintlify.callout' | 'mintlify.tabs' | 'mintlify.tab' | 'mintlify.snippet-ref';
|
|
15
|
+
props: {
|
|
16
|
+
source?: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
variant?: 'info' | 'warning' | 'note' | 'tip' | 'check';
|
|
19
|
+
children?: MintlifyBlock[];
|
|
20
|
+
[key: string]: MintlifyJSONValue | MintlifyBlock[] | undefined;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface MintlifyDocument {
|
|
24
|
+
$type: 'page';
|
|
25
|
+
id: string;
|
|
26
|
+
status: string;
|
|
27
|
+
path: string;
|
|
28
|
+
metadata: {
|
|
29
|
+
title: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
mintlify?: {
|
|
32
|
+
sidebarTitle?: string;
|
|
33
|
+
icon?: string;
|
|
34
|
+
frontmatter?: Record<string, MintlifyJSONValue>;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
regions: {
|
|
38
|
+
main: MintlifyBlock[];
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export interface MintlifySnippet {
|
|
42
|
+
$type: 'fragment';
|
|
43
|
+
id: string;
|
|
44
|
+
path: string;
|
|
45
|
+
block: MintlifyBlock;
|
|
46
|
+
}
|
|
47
|
+
export interface MintlifyConfigFragment {
|
|
48
|
+
$type: 'mintlify.config-fragment';
|
|
49
|
+
schemaVersion: 1;
|
|
50
|
+
path: string;
|
|
51
|
+
value: MintlifyJSONValue;
|
|
52
|
+
}
|
|
53
|
+
export interface MintlifyAPISpec {
|
|
54
|
+
$type: 'mintlify.api-spec';
|
|
55
|
+
schemaVersion: 1;
|
|
56
|
+
path: string;
|
|
57
|
+
format: 'openapi' | 'asyncapi' | 'graphql';
|
|
58
|
+
source: string;
|
|
59
|
+
}
|
|
60
|
+
export interface MintlifySource {
|
|
61
|
+
site: MintlifySite;
|
|
62
|
+
documents: MintlifyDocument[];
|
|
63
|
+
snippets?: MintlifySnippet[];
|
|
64
|
+
configFragments?: MintlifyConfigFragment[];
|
|
65
|
+
apiSpecs?: MintlifyAPISpec[];
|
|
66
|
+
}
|
|
67
|
+
export interface MaterializeMintlifySiteOptions extends Omit<MintlifySource, 'documents'> {
|
|
68
|
+
documents: Array<Omit<MintlifyDocument, 'regions'> & {
|
|
69
|
+
regions: {
|
|
70
|
+
main: Array<MintlifyBlock | FragmentRefNode>;
|
|
71
|
+
};
|
|
72
|
+
}>;
|
|
73
|
+
outputDir: string;
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MintlifyAPISpec, MintlifyConfigFragment, MintlifyDocument, MintlifyJSONValue, MintlifySite, MintlifySnippet } from './types.js';
|
|
2
|
+
export declare function validateSource(site: MintlifyJSONValue, documents: MintlifyDocument[], snippets: MintlifySnippet[], apiSpecs: MintlifyAPISpec[]): void;
|
|
3
|
+
export declare function validateSite(value: unknown, label: string): MintlifySite;
|
|
4
|
+
export declare function validateDocument(value: unknown, label: string, sourcePath?: string): MintlifyDocument;
|
|
5
|
+
export declare function validateSnippet(value: unknown, label: string, sourcePath?: string): MintlifySnippet;
|
|
6
|
+
export declare function validateConfigFragment(value: unknown, label: string): MintlifyConfigFragment;
|
|
7
|
+
export declare function validateAPISpec(value: unknown, label: string): MintlifyAPISpec;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { validateBlock, validateSnippetReferences } from './blocks.js';
|
|
2
|
+
import { cleanDocumentPath, cleanFilePath, isExternalReference, isMintlifyJSONValue, isObject, nonEmptyString, } from './json.js';
|
|
3
|
+
const CONFIG_FRAGMENT_SCHEMA_VERSION = 1;
|
|
4
|
+
const API_SPEC_SCHEMA_VERSION = 1;
|
|
5
|
+
export function validateSource(site, documents, snippets, apiSpecs) {
|
|
6
|
+
const paths = new Set();
|
|
7
|
+
for (const document of documents) {
|
|
8
|
+
if (paths.has(document.path))
|
|
9
|
+
throw new Error(`ACI Mintlify document path is duplicated: ${document.path}`);
|
|
10
|
+
paths.add(document.path);
|
|
11
|
+
}
|
|
12
|
+
const navigated = new Set();
|
|
13
|
+
const navigation = isObject(site) ? site.navigation : undefined;
|
|
14
|
+
validateNavigationPartitions(navigation);
|
|
15
|
+
for (const page of navigationPages(navigation)) {
|
|
16
|
+
// Links are valid Mintlify navigation items but are not ACI documents.
|
|
17
|
+
if (isExternalReference(page))
|
|
18
|
+
continue;
|
|
19
|
+
const normalized = cleanDocumentPath(page);
|
|
20
|
+
if (!normalized || normalized !== page)
|
|
21
|
+
throw new Error(`ACI Mintlify navigation page is invalid: ${page}`);
|
|
22
|
+
if (!paths.has(page))
|
|
23
|
+
throw new Error(`ACI Mintlify navigation references missing document: ${page}`);
|
|
24
|
+
if (navigated.has(page))
|
|
25
|
+
throw new Error(`ACI Mintlify navigation references document more than once: ${page}`);
|
|
26
|
+
navigated.add(page);
|
|
27
|
+
}
|
|
28
|
+
validateRedirects(isObject(site) ? site.redirects : undefined, paths);
|
|
29
|
+
validateSnippetReferences(documents, snippets);
|
|
30
|
+
validateAPISpecReferences(site, apiSpecs);
|
|
31
|
+
}
|
|
32
|
+
export function validateSite(value, label) {
|
|
33
|
+
if (!isObject(value) || !isMintlifyJSONValue(value)) {
|
|
34
|
+
throw new Error(`ACI Mintlify site is invalid: ${label}`);
|
|
35
|
+
}
|
|
36
|
+
if ('$schema' in value && value.$schema !== 'https://mintlify.com/docs.json') {
|
|
37
|
+
throw new Error(`ACI Mintlify site has an unsupported docs schema: ${label}`);
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
export function validateDocument(value, label, sourcePath) {
|
|
42
|
+
const metadata = isObject(value) && isObject(value.metadata) ? value.metadata : undefined;
|
|
43
|
+
const regions = isObject(value) && isObject(value.regions) ? value.regions : undefined;
|
|
44
|
+
if (!isObject(value) || value.$type !== 'page' || !nonEmptyString(value.id) || !nonEmptyString(value.status) || !nonEmptyString(sourcePath) || !metadata || !nonEmptyString(metadata.title) || !regions || !Array.isArray(regions.main)) {
|
|
45
|
+
throw new Error(`ACI Mintlify document is invalid: ${label}`);
|
|
46
|
+
}
|
|
47
|
+
for (const region of Object.keys(regions)) {
|
|
48
|
+
if (region !== 'main')
|
|
49
|
+
throw new Error(`ACI Mintlify document ${label} has unsupported region ${JSON.stringify(region)}; only main is supported`);
|
|
50
|
+
}
|
|
51
|
+
const documentPath = cleanDocumentPath(sourcePath);
|
|
52
|
+
if (!documentPath || documentPath !== sourcePath)
|
|
53
|
+
throw new Error(`ACI Mintlify document path is invalid: ${sourcePath}`);
|
|
54
|
+
const ids = new Set();
|
|
55
|
+
const blocks = regions.main.map((block, index) => validateBlock(block, `${label}.regions.main[${index}]`, ids));
|
|
56
|
+
const renderer = isObject(metadata.mintlify) ? metadata.mintlify : {};
|
|
57
|
+
return {
|
|
58
|
+
$type: 'page', id: value.id, status: value.status, path: documentPath,
|
|
59
|
+
metadata: { title: metadata.title,
|
|
60
|
+
...(nonEmptyString(metadata.description) ? { description: metadata.description } : {}),
|
|
61
|
+
mintlify: {
|
|
62
|
+
...(nonEmptyString(renderer.sidebarTitle) ? { sidebarTitle: renderer.sidebarTitle } : {}),
|
|
63
|
+
...(nonEmptyString(renderer.icon) ? { icon: renderer.icon } : {}),
|
|
64
|
+
...(validFrontmatter(renderer.frontmatter) ? { frontmatter: renderer.frontmatter } : {}),
|
|
65
|
+
},
|
|
66
|
+
}, regions: { main: blocks },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export function validateSnippet(value, label, sourcePath) {
|
|
70
|
+
if (!isObject(value) || value.$type !== 'fragment' || !nonEmptyString(value.id) || !nonEmptyString(sourcePath) || !isObject(value.block)) {
|
|
71
|
+
throw new Error(`ACI Mintlify snippet is invalid: ${label}`);
|
|
72
|
+
}
|
|
73
|
+
const snippetPath = cleanFilePath(sourcePath, '.mdx');
|
|
74
|
+
if (!snippetPath || snippetPath !== sourcePath)
|
|
75
|
+
throw new Error(`ACI Mintlify snippet path is invalid: ${sourcePath}`);
|
|
76
|
+
const ids = new Set();
|
|
77
|
+
return {
|
|
78
|
+
$type: 'fragment', id: value.id,
|
|
79
|
+
path: snippetPath, block: validateBlock(value.block, `${label}.block`, ids),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function validateConfigFragment(value, label) {
|
|
83
|
+
if (!isObject(value) || value.$type !== 'mintlify.config-fragment' || value.schemaVersion !== CONFIG_FRAGMENT_SCHEMA_VERSION || !nonEmptyString(value.path) || !isMintlifyJSONValue(value.value)) {
|
|
84
|
+
throw new Error(`ACI Mintlify config fragment is invalid: ${label}`);
|
|
85
|
+
}
|
|
86
|
+
const fragmentPath = cleanFilePath(value.path, '.json');
|
|
87
|
+
if (!fragmentPath || fragmentPath !== value.path || fragmentPath === 'docs.json')
|
|
88
|
+
throw new Error(`ACI Mintlify config fragment path is invalid: ${value.path}`);
|
|
89
|
+
return { $type: 'mintlify.config-fragment', schemaVersion: CONFIG_FRAGMENT_SCHEMA_VERSION, path: fragmentPath, value: value.value };
|
|
90
|
+
}
|
|
91
|
+
export function validateAPISpec(value, label) {
|
|
92
|
+
if (!isObject(value) || value.$type !== 'mintlify.api-spec' || value.schemaVersion !== API_SPEC_SCHEMA_VERSION || !nonEmptyString(value.path) || !['openapi', 'asyncapi', 'graphql'].includes(String(value.format)) || !nonEmptyString(value.source)) {
|
|
93
|
+
throw new Error(`ACI Mintlify API specification is invalid: ${label}`);
|
|
94
|
+
}
|
|
95
|
+
const sourcePath = value.path;
|
|
96
|
+
const extensions = value.format === 'graphql' ? ['.graphql', '.gql', '.json'] : ['.json', '.yaml', '.yml'];
|
|
97
|
+
const specPath = extensions.map((extension) => cleanFilePath(sourcePath, extension)).find(Boolean) ?? '';
|
|
98
|
+
if (!specPath || specPath !== sourcePath)
|
|
99
|
+
throw new Error(`ACI Mintlify API specification path is invalid: ${sourcePath}`);
|
|
100
|
+
return {
|
|
101
|
+
$type: 'mintlify.api-spec',
|
|
102
|
+
schemaVersion: API_SPEC_SCHEMA_VERSION,
|
|
103
|
+
path: specPath,
|
|
104
|
+
format: value.format,
|
|
105
|
+
source: value.source,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function validFrontmatter(value) {
|
|
109
|
+
if (value === undefined)
|
|
110
|
+
return false;
|
|
111
|
+
if (!isObject(value))
|
|
112
|
+
throw new Error('ACI Mintlify document frontmatter is invalid');
|
|
113
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
114
|
+
if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(key) || !isMintlifyJSONValue(entry)) {
|
|
115
|
+
throw new Error('ACI Mintlify document frontmatter is invalid');
|
|
116
|
+
}
|
|
117
|
+
if (['title', 'description', 'sidebarTitle', 'icon'].includes(key))
|
|
118
|
+
throw new Error(`ACI Mintlify document frontmatter key is reserved: ${key}`);
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
function validateRedirects(value, documentPaths) {
|
|
123
|
+
if (value === undefined)
|
|
124
|
+
return;
|
|
125
|
+
if (!Array.isArray(value))
|
|
126
|
+
throw new Error('ACI Mintlify redirects are invalid');
|
|
127
|
+
const redirects = new Map();
|
|
128
|
+
const routes = new Set([...documentPaths].map((documentPath) => `/${documentPath}`));
|
|
129
|
+
for (const entry of value) {
|
|
130
|
+
if (!isObject(entry) || !nonEmptyString(entry.source) || !nonEmptyString(entry.destination) || (entry.permanent !== undefined && typeof entry.permanent !== 'boolean')) {
|
|
131
|
+
throw new Error('ACI Mintlify redirect is invalid');
|
|
132
|
+
}
|
|
133
|
+
if (!entry.source.startsWith('/') || entry.source.includes('?') || entry.source.includes('#')) {
|
|
134
|
+
throw new Error(`ACI Mintlify redirect source is invalid: ${entry.source}`);
|
|
135
|
+
}
|
|
136
|
+
if (!entry.destination.startsWith('/') && !isExternalReference(entry.destination)) {
|
|
137
|
+
throw new Error(`ACI Mintlify redirect destination is invalid: ${entry.destination}`);
|
|
138
|
+
}
|
|
139
|
+
if (redirects.has(entry.source))
|
|
140
|
+
throw new Error(`ACI Mintlify redirect source is duplicated: ${entry.source}`);
|
|
141
|
+
if (!entry.source.includes('*') && !entry.source.includes(':') && routes.has(trimRouteSlash(entry.source))) {
|
|
142
|
+
throw new Error(`ACI Mintlify redirect source collides with a document: ${entry.source}`);
|
|
143
|
+
}
|
|
144
|
+
redirects.set(entry.source, entry.destination);
|
|
145
|
+
}
|
|
146
|
+
for (const [source, destination] of redirects) {
|
|
147
|
+
if (isExternalReference(destination) || destination.includes('*') || destination.includes(':'))
|
|
148
|
+
continue;
|
|
149
|
+
const destinationPath = trimRouteSlash(destination.split(/[?#]/, 1)[0]);
|
|
150
|
+
if (!routes.has(destinationPath) && !redirects.has(destinationPath)) {
|
|
151
|
+
throw new Error(`ACI Mintlify redirect destination is missing: ${destination}`);
|
|
152
|
+
}
|
|
153
|
+
const seen = new Set([source]);
|
|
154
|
+
let next = destinationPath;
|
|
155
|
+
while (redirects.has(next)) {
|
|
156
|
+
if (seen.has(next))
|
|
157
|
+
throw new Error(`ACI Mintlify redirect cycle includes: ${next}`);
|
|
158
|
+
seen.add(next);
|
|
159
|
+
const target = redirects.get(next);
|
|
160
|
+
if (isExternalReference(target) || target.includes('*') || target.includes(':'))
|
|
161
|
+
break;
|
|
162
|
+
next = trimRouteSlash(target.split(/[?#]/, 1)[0]);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function trimRouteSlash(value) {
|
|
167
|
+
return value.length > 1 ? value.replace(/\/+$/, '') : value;
|
|
168
|
+
}
|
|
169
|
+
function validateAPISpecReferences(site, specs) {
|
|
170
|
+
const byPath = new Map();
|
|
171
|
+
for (const spec of specs) {
|
|
172
|
+
if (byPath.has(spec.path))
|
|
173
|
+
throw new Error(`ACI Mintlify API specification path is duplicated: ${spec.path}`);
|
|
174
|
+
byPath.set(spec.path, spec);
|
|
175
|
+
}
|
|
176
|
+
const visit = (value) => {
|
|
177
|
+
if (Array.isArray(value)) {
|
|
178
|
+
for (const child of value)
|
|
179
|
+
visit(child);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (!isObject(value))
|
|
183
|
+
return;
|
|
184
|
+
for (const [key, child] of Object.entries(value)) {
|
|
185
|
+
if (['openapi', 'asyncapi', 'graphql'].includes(key)) {
|
|
186
|
+
const references = typeof child === 'string' ? [child] : Array.isArray(child) ? child.filter((entry) => typeof entry === 'string') : [];
|
|
187
|
+
for (const reference of references) {
|
|
188
|
+
if (isExternalReference(reference))
|
|
189
|
+
continue;
|
|
190
|
+
const spec = byPath.get(reference);
|
|
191
|
+
if (!spec || spec.format !== key)
|
|
192
|
+
throw new Error(`ACI Mintlify API specification reference is missing or has the wrong format: ${reference}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
visit(child);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
visit(site);
|
|
199
|
+
}
|
|
200
|
+
function navigationPages(value) {
|
|
201
|
+
if (!isObject(value))
|
|
202
|
+
return [];
|
|
203
|
+
const pages = [];
|
|
204
|
+
visitNavigation(value, pages);
|
|
205
|
+
return pages;
|
|
206
|
+
}
|
|
207
|
+
function visitNavigation(value, pages) {
|
|
208
|
+
if (Array.isArray(value)) {
|
|
209
|
+
for (const item of value)
|
|
210
|
+
visitNavigation(item, pages);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (!isObject(value))
|
|
214
|
+
return;
|
|
215
|
+
for (const [key, child] of Object.entries(value)) {
|
|
216
|
+
if (key === 'pages' && Array.isArray(child)) {
|
|
217
|
+
for (const item of child) {
|
|
218
|
+
if (typeof item === 'string')
|
|
219
|
+
pages.push(item);
|
|
220
|
+
else
|
|
221
|
+
visitNavigation(item, pages);
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
visitNavigation(child, pages);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function validateNavigationPartitions(value) {
|
|
229
|
+
if (Array.isArray(value)) {
|
|
230
|
+
for (const child of value)
|
|
231
|
+
validateNavigationPartitions(child);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!isObject(value))
|
|
235
|
+
return;
|
|
236
|
+
for (const [key, child] of Object.entries(value)) {
|
|
237
|
+
if ((key === 'languages' || key === 'versions') && Array.isArray(child)) {
|
|
238
|
+
const identityKey = key === 'languages' ? 'language' : 'version';
|
|
239
|
+
const identities = new Set();
|
|
240
|
+
let defaults = 0;
|
|
241
|
+
for (const entry of child) {
|
|
242
|
+
if (!isObject(entry) || !nonEmptyString(entry[identityKey]))
|
|
243
|
+
throw new Error(`ACI Mintlify ${key} navigation is invalid`);
|
|
244
|
+
const identity = entry[identityKey];
|
|
245
|
+
if (identities.has(identity))
|
|
246
|
+
throw new Error(`ACI Mintlify ${key} navigation is duplicated: ${identity}`);
|
|
247
|
+
identities.add(identity);
|
|
248
|
+
if (entry.default === true)
|
|
249
|
+
defaults += 1;
|
|
250
|
+
}
|
|
251
|
+
if (defaults > 1)
|
|
252
|
+
throw new Error(`ACI Mintlify navigation has multiple default ${key}`);
|
|
253
|
+
}
|
|
254
|
+
validateNavigationPartitions(child);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useLayoutEffect } from 'react';
|
|
3
|
+
/** Signals from the committed page tree, including a page with no assignments. */
|
|
4
|
+
export function AssignmentsNavigation({ json }) {
|
|
5
|
+
// Deliberately runs on each page commit, including unchanged assignment props
|
|
6
|
+
// on a query navigation. The collector deduplicates hydration/StrictMode and
|
|
7
|
+
// same-URL/hash commits; discarded concurrent renders never run this effect.
|
|
8
|
+
useLayoutEffect(() => {
|
|
9
|
+
const win = window;
|
|
10
|
+
if (typeof win.aci !== 'function') {
|
|
11
|
+
const queue = (...args) => { (queue.q ??= []).push(args); };
|
|
12
|
+
win.aci = queue;
|
|
13
|
+
}
|
|
14
|
+
const assignments = json ? JSON.parse(json).experiments : [];
|
|
15
|
+
win.aci('navigation-complete', { url: win.location.href, assignments });
|
|
16
|
+
});
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
@@ -7,8 +7,9 @@ export interface AssignmentsScriptProps {
|
|
|
7
7
|
* (`<script type="application/json" data-aci-assignments>` with a
|
|
8
8
|
* {"v":1,"experiments":[…]} body, personalization-reporting design/04) when
|
|
9
9
|
* the page payload carries a build-time `experiments` block, and nothing
|
|
10
|
-
* otherwise.
|
|
10
|
+
* otherwise. Its client commit hook also signals empty pages so SPA
|
|
11
|
+
* attribution clears when leaving an experiment. createPage appends it to the page tree exactly once per page;
|
|
11
12
|
* sites composing their own pages can render it directly. The JSON body is
|
|
12
13
|
* HTML-safe (assignmentsScriptJSON \u-escapes angle brackets).
|
|
13
14
|
*/
|
|
14
|
-
export declare function AssignmentsScript({ page }: AssignmentsScriptProps): import("react").JSX.Element
|
|
15
|
+
export declare function AssignmentsScript({ page }: AssignmentsScriptProps): import("react").JSX.Element;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { AssignmentsNavigation } from './assignments-navigation.js';
|
|
2
3
|
import { assignmentsScriptJSON, } from '../content/personalization.js';
|
|
3
4
|
/**
|
|
4
5
|
* AssignmentsScript renders the page-level experiment assignment tag
|
|
5
6
|
* (`<script type="application/json" data-aci-assignments>` with a
|
|
6
7
|
* {"v":1,"experiments":[…]} body, personalization-reporting design/04) when
|
|
7
8
|
* the page payload carries a build-time `experiments` block, and nothing
|
|
8
|
-
* otherwise.
|
|
9
|
+
* otherwise. Its client commit hook also signals empty pages so SPA
|
|
10
|
+
* attribution clears when leaving an experiment. createPage appends it to the page tree exactly once per page;
|
|
9
11
|
* sites composing their own pages can render it directly. The JSON body is
|
|
10
12
|
* HTML-safe (assignmentsScriptJSON \u-escapes angle brackets).
|
|
11
13
|
*/
|
|
12
14
|
export function AssignmentsScript({ page }) {
|
|
13
15
|
const json = assignmentsScriptJSON(page);
|
|
14
|
-
|
|
15
|
-
return null;
|
|
16
|
-
return (_jsx("script", { type: "application/json", "data-aci-assignments": "", dangerouslySetInnerHTML: { __html: json } }));
|
|
16
|
+
return (_jsxs(_Fragment, { children: [json ? _jsx("script", { type: "application/json", "data-aci-assignments": "", dangerouslySetInnerHTML: { __html: json } }) : null, _jsx(AssignmentsNavigation, { json: json })] }));
|
|
17
17
|
}
|
|
@@ -17,6 +17,10 @@ export interface NextResolvedErrorPage extends ResolvedErrorPage {
|
|
|
17
17
|
* warn posture (D-5). Never throws: an unreachable site config degrades to
|
|
18
18
|
* the shipped default — an error surface must not re-enter the failing
|
|
19
19
|
* system (ANTI-2).
|
|
20
|
+
*
|
|
21
|
+
* The seat receives no route params, so on a hosted request its release
|
|
22
|
+
* comes from the router's signed release context (resolveReleaseId): the
|
|
23
|
+
* same release the catch-all of that request was rewritten to.
|
|
20
24
|
*/
|
|
21
25
|
export declare function resolveErrorPage(kind: ErrorPageKind, config?: FetchConfig): Promise<NextResolvedErrorPage>;
|
|
22
26
|
/**
|
package/dist/next/error-pages.js
CHANGED
|
@@ -11,6 +11,10 @@ import { ErrorPageMarker } from './error-page-marker.js';
|
|
|
11
11
|
* warn posture (D-5). Never throws: an unreachable site config degrades to
|
|
12
12
|
* the shipped default — an error surface must not re-enter the failing
|
|
13
13
|
* system (ANTI-2).
|
|
14
|
+
*
|
|
15
|
+
* The seat receives no route params, so on a hosted request its release
|
|
16
|
+
* comes from the router's signed release context (resolveReleaseId): the
|
|
17
|
+
* same release the catch-all of that request was rewritten to.
|
|
14
18
|
*/
|
|
15
19
|
export async function resolveErrorPage(kind, config = {}) {
|
|
16
20
|
let siteConfig = null;
|
|
@@ -13,7 +13,8 @@ import type { ErrorSeatPayload } from './error-seat.js';
|
|
|
13
13
|
* - present-but-broken bindings — exactly the enumerated REQ-5 failures
|
|
14
14
|
* (malformed route, unresolved page, unpublished page, audience/experiment
|
|
15
15
|
* overlays — REQ-11) → `errors` in BOTH postures;
|
|
16
|
-
* -
|
|
16
|
+
* - selected fragment binding or expansion failures are errors in both postures;
|
|
17
|
+
* - other exceptions (stale/corrupt compiled content, old manifest
|
|
17
18
|
* shapes) warns-and-defaults under warn and errors only under enforce, so
|
|
18
19
|
* a stale .aci/compiled tree can never break a binding-less build.
|
|
19
20
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FragmentResolutionError, resolveSlotsSync } from '../content/resolve-slots.js';
|
|
1
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { ERROR_PAGE_ROUTE_PATTERN } from '../content/error-pages.js';
|
|
@@ -86,29 +87,13 @@ export function buildErrorSeatPayload(contentRoot, posture) {
|
|
|
86
87
|
const route = bindings.catchAll;
|
|
87
88
|
const page = readJSON(root, catchAllEntry.payloadRef);
|
|
88
89
|
const layout = readJSON(root, `layouts/${page.layout}.json`);
|
|
89
|
-
const slots = {}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
const defaults = layout.defaults?.[slot.name];
|
|
99
|
-
if (!defaults?.length)
|
|
100
|
-
continue;
|
|
101
|
-
const blocks = [];
|
|
102
|
-
for (const ref of defaults) {
|
|
103
|
-
const fragmentRef = manifest.fragments[ref.fragmentId]?.payloadRef;
|
|
104
|
-
if (!fragmentRef)
|
|
105
|
-
continue;
|
|
106
|
-
const fragment = readJSON(root, fragmentRef);
|
|
107
|
-
blocks.push({ id: fragment.id, component: fragment.component, props: fragment.props || {} });
|
|
108
|
-
}
|
|
109
|
-
if (blocks.length)
|
|
110
|
-
slots[slot.name] = blocks;
|
|
111
|
-
}
|
|
90
|
+
const slots = resolveSlotsSync(layout, page.regions ?? {}, (id) => {
|
|
91
|
+
const fragmentRef = manifest.fragments[id]?.payloadRef;
|
|
92
|
+
if (!fragmentRef)
|
|
93
|
+
throw new Error(`Fragment not found: ${id}`);
|
|
94
|
+
return readJSON(root, fragmentRef);
|
|
95
|
+
});
|
|
96
|
+
const slotOrder = layout.slots.map((slot) => slot.name);
|
|
112
97
|
return {
|
|
113
98
|
payload: {
|
|
114
99
|
defaulted: false,
|
|
@@ -123,7 +108,12 @@ export function buildErrorSeatPayload(contentRoot, posture) {
|
|
|
123
108
|
};
|
|
124
109
|
}
|
|
125
110
|
catch (error) {
|
|
126
|
-
|
|
111
|
+
if (error instanceof FragmentResolutionError) {
|
|
112
|
+
errors.push(error.message);
|
|
113
|
+
return defaulted();
|
|
114
|
+
}
|
|
115
|
+
// Neither a selected-fragment failure nor an enumerated REQ-5 binding
|
|
116
|
+
// failure (those are pushed to
|
|
127
117
|
// `errors` above): stale/corrupt compiled content or an old manifest
|
|
128
118
|
// shape. Warn-and-default under the warn posture so it can never break a
|
|
129
119
|
// build; fail closed only under enforce (quorum RISK-002).
|
package/dist/next/error-seat.js
CHANGED
|
@@ -69,7 +69,7 @@ function renderPayload(data, registry) {
|
|
|
69
69
|
// async and server-oriented). Unknown components are skipped — the error
|
|
70
70
|
// seat degrades, it never crashes.
|
|
71
71
|
function renderClientBlocks(blocks, registry) {
|
|
72
|
-
return blocks.map((block) => {
|
|
72
|
+
return blocks.map((block, index) => {
|
|
73
73
|
const entry = registry.find((r) => r.id === block.component);
|
|
74
74
|
if (!entry)
|
|
75
75
|
return null;
|
|
@@ -78,6 +78,11 @@ function renderClientBlocks(blocks, registry) {
|
|
|
78
78
|
if (entry.contract.blockSlots) {
|
|
79
79
|
componentProps.renderChildren = (children) => renderClientBlocks(children, registry);
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
// sdk I-19 (ADR 0051): the error seat is an injection seam too.
|
|
82
|
+
componentProps['data-aci-block'] = block.id;
|
|
83
|
+
componentProps['data-aci-component'] = block.component;
|
|
84
|
+
// Index+id composite: a repeated fragment placement repeats its authored
|
|
85
|
+
// id, and sibling keys must stay unique for React reconciliation.
|
|
86
|
+
return _jsx(Component, { ...componentProps }, `${index}:${block.id}`);
|
|
82
87
|
});
|
|
83
88
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const VARIANT_NAMESPACE = '__variant';
|
|
2
|
+
export function parseHostedPageTarget(rawSegments) {
|
|
3
|
+
const segments = decodeSegments(rawSegments);
|
|
4
|
+
if (!segments)
|
|
5
|
+
return null;
|
|
6
|
+
const variantAt = segments.indexOf(VARIANT_NAMESPACE);
|
|
7
|
+
if (variantAt < 0)
|
|
8
|
+
return target('base', segments, segments);
|
|
9
|
+
if (variantAt !== segments.lastIndexOf(VARIANT_NAMESPACE) || variantAt !== segments.length - 2) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
...target('variant', segments.slice(0, variantAt), segments),
|
|
14
|
+
variantKey: segments[variantAt + 1],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function target(kind, routeSegments, targetSegments) {
|
|
18
|
+
return {
|
|
19
|
+
kind,
|
|
20
|
+
route: routeSegments.length === 0 ? '/' : `/${routeSegments.join('/')}/`,
|
|
21
|
+
pageRef: ['routes', ...targetSegments, 'index.json'].join('/'),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function decodeSegments(rawSegments) {
|
|
25
|
+
const segments = [];
|
|
26
|
+
for (const raw of rawSegments) {
|
|
27
|
+
let segment;
|
|
28
|
+
try {
|
|
29
|
+
segment = decodeURIComponent(raw);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (!segment || segment === '.' || segment === '..' || segment.includes('/'))
|
|
35
|
+
return null;
|
|
36
|
+
segments.push(segment);
|
|
37
|
+
}
|
|
38
|
+
return segments;
|
|
39
|
+
}
|
package/dist/next/index.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ export { DevRefresh, type DevRefreshProps } from './dev-refresh.js';
|
|
|
2
2
|
export { withAci, type WithAciOptions } from './config.js';
|
|
3
3
|
export { isPreviewMode } from './preview-mode.js';
|
|
4
4
|
export { createPage, type CreatePageOptions, type SlotPropsFn } from './page.js';
|
|
5
|
+
export { renderBlock, renderBlocks } from './render.js';
|
|
5
6
|
export { getSiteConfig, getFragment, getLayout, getPage, getRenderInput, routeFromNextParams } from './server.js';
|
|
6
7
|
export { PageNotFoundError, FragmentNotFoundError, LayoutNotFoundError } from './server.js';
|
|
7
8
|
export { CollectScript, type CollectScriptProps } from './collect-script.js';
|
|
8
|
-
export { aciMiddlewareMatcher } from './middleware.js';
|
|
9
9
|
export { AssignmentsScript, type AssignmentsScriptProps } from './assignments-script.js';
|
|
10
10
|
export { createNotFound, renderPageForRoute, resolveErrorPage, type NextResolvedErrorPage, } from './error-pages.js';
|
|
11
11
|
export { ErrorPageInstrumentation } from './error-page-instrumentation.js';
|
package/dist/next/index.js
CHANGED
|
@@ -2,10 +2,12 @@ export { DevRefresh } from './dev-refresh.js';
|
|
|
2
2
|
export { withAci } from './config.js';
|
|
3
3
|
export { isPreviewMode } from './preview-mode.js';
|
|
4
4
|
export { createPage } from './page.js';
|
|
5
|
+
// The block renderer seam (sdk I-19, ADR 0051): exported so a site's own
|
|
6
|
+
// conformance test can prove its components carry root identity.
|
|
7
|
+
export { renderBlock, renderBlocks } from './render.js';
|
|
5
8
|
export { getSiteConfig, getFragment, getLayout, getPage, getRenderInput, routeFromNextParams } from './server.js';
|
|
6
9
|
export { PageNotFoundError, FragmentNotFoundError, LayoutNotFoundError } from './server.js';
|
|
7
10
|
export { CollectScript } from './collect-script.js';
|
|
8
|
-
export { aciMiddlewareMatcher } from './middleware.js';
|
|
9
11
|
export { AssignmentsScript } from './assignments-script.js';
|
|
10
12
|
export { createNotFound, renderPageForRoute, resolveErrorPage, } from './error-pages.js';
|
|
11
13
|
export { ErrorPageInstrumentation } from './error-page-instrumentation.js';
|
package/dist/next/page.d.ts
CHANGED
|
@@ -31,12 +31,10 @@ export interface CreatePageOptions {
|
|
|
31
31
|
* the collect script tag exactly once per page (design/04 contract tag).
|
|
32
32
|
*
|
|
33
33
|
* Release-scoped routes (production): the middleware rewrites a public URL
|
|
34
|
-
* to `/__r/{releaseId}/
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* The `routes` namespace segment leaves room for future siblings under
|
|
39
|
-
* the release prefix (e.g. `/__r/{releaseId}/assets/*`).
|
|
34
|
+
* to `/__r/{releaseId}/{targetRef}`. The target ref selects the exact immutable
|
|
35
|
+
* base or variant page while its prefix identifies the logical public route.
|
|
36
|
+
* createPage passes both identities to the content loader, giving each target
|
|
37
|
+
* a distinct CDN cache key without sending selection headers into Next.
|
|
40
38
|
*
|
|
41
39
|
* Dev pass-through (ACI_CONTENT_PROVIDER=file): the middleware passes
|
|
42
40
|
* requests through unrewritten, so `/` arrives with an empty/undefined
|
|
@@ -54,4 +52,29 @@ export declare function createPage(registry: Registry, options?: CreatePageOptio
|
|
|
54
52
|
generateMetadata: ({ params }: NextPageProps) => Promise<Metadata>;
|
|
55
53
|
default: ({ params }: NextPageProps) => Promise<import("react").JSX.Element>;
|
|
56
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Resolves the logical route and releaseId from Next.js catch-all params.
|
|
57
|
+
*
|
|
58
|
+
* Two shapes are supported:
|
|
59
|
+
*
|
|
60
|
+
* 1. **Rewritten (production):** middleware rewrites a public URL to the exact
|
|
61
|
+
* selected target under `/__r/{releaseId}`. A root variant therefore has
|
|
62
|
+
* `['__r', releaseId, '__variant', variantKey]`. The full suffix selects the
|
|
63
|
+
* immutable page object; the segments before the reserved `__variant`
|
|
64
|
+
* namespace form the logical public route.
|
|
65
|
+
*
|
|
66
|
+
* 2. **Dev pass-through (ACI_CONTENT_PROVIDER=file):** middleware passes
|
|
67
|
+
* requests through unrewritten, so the slug is the raw public path
|
|
68
|
+
* segments (possibly empty/undefined for `/`). No releaseId is present;
|
|
69
|
+
* the file provider serves content from disk.
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveRouteAndRelease(params: Promise<{
|
|
72
|
+
slug?: string[];
|
|
73
|
+
}> | {
|
|
74
|
+
slug?: string[];
|
|
75
|
+
}): Promise<{
|
|
76
|
+
route: string;
|
|
77
|
+
releaseId?: string;
|
|
78
|
+
pageRef?: string;
|
|
79
|
+
}>;
|
|
57
80
|
export {};
|