@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
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
2
3
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
4
|
import path from 'node:path';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
import { ImageSchema } from '../types/image.ts';
|
|
7
8
|
import { VideoSchema } from '../types/video.ts';
|
|
@@ -19,24 +20,27 @@ try {
|
|
|
19
20
|
if (!globalThis.React) globalThis.React = React;
|
|
20
21
|
} catch { /* react not available — component files without JSX will still work */ }
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} catch (error) {
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
if (isMainModule()) {
|
|
24
|
+
const args = parseArgs(process.argv.slice(2));
|
|
25
|
+
try {
|
|
26
|
+
const result = await compileRegistry(args);
|
|
27
|
+
console.log(JSON.stringify({ success: true, ...result, errors: [] }, null, 2));
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(JSON.stringify({ success: false, errors: [String(error?.message ?? error)] }, null, 2));
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
}
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
async function compileRegistry(options) {
|
|
33
|
-
|
|
35
|
+
if (options.layouts) {
|
|
36
|
+
throw new Error('--layouts is retired; author page composition under /templates instead');
|
|
37
|
+
}
|
|
38
|
+
const required = ['components', 'output'];
|
|
34
39
|
for (const key of required) {
|
|
35
40
|
if (!options[key]) throw new Error(`missing --${key}`);
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
const components = await loadDefaultArray(options.components, 'component registry');
|
|
39
|
-
const layouts = await loadDefaultArray(options.layouts, 'layout registry');
|
|
40
44
|
const dataModels = options.data
|
|
41
45
|
? await loadDefaultArray(options.data, 'data model registry')
|
|
42
46
|
: [];
|
|
@@ -66,9 +70,6 @@ async function compileRegistry(options) {
|
|
|
66
70
|
});
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
const seenLayouts = new Set();
|
|
70
|
-
const layoutEntries = layouts.map((layout, index) => validateLayout(layout, index, seenLayouts));
|
|
71
|
-
|
|
72
73
|
const seenDataModels = new Set();
|
|
73
74
|
const dataModelEntries = [];
|
|
74
75
|
if (dataModels.length > 0) {
|
|
@@ -102,7 +103,6 @@ async function compileRegistry(options) {
|
|
|
102
103
|
manifestVersion: '1',
|
|
103
104
|
generatedAt: new Date().toISOString(),
|
|
104
105
|
components: componentEntries,
|
|
105
|
-
layouts: layoutEntries,
|
|
106
106
|
dataModels: dataModelEntries,
|
|
107
107
|
};
|
|
108
108
|
const registryPath = path.join(outDir, 'registry.json');
|
|
@@ -116,7 +116,7 @@ async function compileRegistry(options) {
|
|
|
116
116
|
|
|
117
117
|
async function loadDefaultArray(modulePath, label) {
|
|
118
118
|
const mod = await import(pathToFileURL(path.resolve(modulePath)).href);
|
|
119
|
-
const value = mod.default ?? mod.registry ?? mod.components ?? mod.
|
|
119
|
+
const value = mod.default ?? mod.registry ?? mod.components ?? mod.dataModels;
|
|
120
120
|
if (!Array.isArray(value)) throw new Error(`${label} must export an array`);
|
|
121
121
|
return value;
|
|
122
122
|
}
|
|
@@ -346,12 +346,10 @@ function isImageSchema(schema) {
|
|
|
346
346
|
return compiled?.type === 'object'
|
|
347
347
|
&& properties.$type?.const === 'gradial.image'
|
|
348
348
|
&& properties.assetId?.type === 'string'
|
|
349
|
-
&& properties.versionId?.type === 'string'
|
|
350
349
|
&& properties.alt?.type === 'string'
|
|
351
350
|
&& properties.sources?.type === 'array'
|
|
352
351
|
&& required.has('$type')
|
|
353
352
|
&& required.has('assetId')
|
|
354
|
-
&& required.has('versionId')
|
|
355
353
|
&& required.has('alt')
|
|
356
354
|
&& required.has('sources');
|
|
357
355
|
} catch {
|
|
@@ -388,27 +386,6 @@ function unwrapSchema(schema) {
|
|
|
388
386
|
return current;
|
|
389
387
|
}
|
|
390
388
|
|
|
391
|
-
function validateLayout(layout, index, seen) {
|
|
392
|
-
if (!layout || typeof layout !== 'object') throw new Error(`layouts[${index}] must be an object`);
|
|
393
|
-
if (!layout.name) throw new Error(`layouts[${index}].name is required`);
|
|
394
|
-
if (seen.has(layout.name)) throw new Error(`duplicate layout name ${layout.name}`);
|
|
395
|
-
seen.add(layout.name);
|
|
396
|
-
if (!Array.isArray(layout.slots)) throw new Error(`layouts[${index}].slots must be an array`);
|
|
397
|
-
const slots = [];
|
|
398
|
-
const seenSlots = new Set();
|
|
399
|
-
for (const [slotIndex, slot] of layout.slots.entries()) {
|
|
400
|
-
if (!slot?.name) throw new Error(`layouts[${index}].slots[${slotIndex}].name is required`);
|
|
401
|
-
if (seenSlots.has(slot.name)) throw new Error(`duplicate slot name ${slot.name} in layout ${layout.name}`);
|
|
402
|
-
seenSlots.add(slot.name);
|
|
403
|
-
slots.push({ name: slot.name, required: Boolean(slot.required) });
|
|
404
|
-
}
|
|
405
|
-
const entry = { name: layout.name, slots };
|
|
406
|
-
if (layout.defaults && typeof layout.defaults === 'object') {
|
|
407
|
-
entry.defaults = layout.defaults;
|
|
408
|
-
}
|
|
409
|
-
return entry;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
389
|
function compileSchema(schema, name) {
|
|
413
390
|
const toJSONSchema = z.toJSONSchema;
|
|
414
391
|
if (typeof toJSONSchema !== 'function') {
|
|
@@ -430,9 +407,10 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
430
407
|
const defs = {
|
|
431
408
|
baseBlock: {
|
|
432
409
|
type: 'object',
|
|
433
|
-
required: ['id', 'component', 'props'],
|
|
410
|
+
required: ['$type', 'id', 'component', 'props'],
|
|
434
411
|
additionalProperties: true,
|
|
435
412
|
properties: {
|
|
413
|
+
$type: { const: 'component' },
|
|
436
414
|
id: { type: 'string', minLength: 1 },
|
|
437
415
|
component: { type: 'string' },
|
|
438
416
|
props: { type: 'object' },
|
|
@@ -502,13 +480,12 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
502
480
|
$id: 'https://baremetal.local/schemas/page.schema.json',
|
|
503
481
|
title: 'bare-metal Page',
|
|
504
482
|
type: 'object',
|
|
505
|
-
required: ['$type', 'id', 'status', '
|
|
483
|
+
required: ['$type', 'id', 'status', 'metadata', 'regions'],
|
|
506
484
|
additionalProperties: true,
|
|
507
485
|
properties: {
|
|
508
486
|
$type: { const: 'page' },
|
|
509
487
|
id: { type: 'string', minLength: 1 },
|
|
510
488
|
status: { type: 'string', minLength: 1 },
|
|
511
|
-
layout: { type: 'string', minLength: 1 },
|
|
512
489
|
metadata: {
|
|
513
490
|
type: 'object',
|
|
514
491
|
required: ['title'],
|
|
@@ -741,3 +718,14 @@ function parseArgs(argv) {
|
|
|
741
718
|
}
|
|
742
719
|
return out;
|
|
743
720
|
}
|
|
721
|
+
|
|
722
|
+
function isMainModule() {
|
|
723
|
+
if (!process.argv[1]) return false;
|
|
724
|
+
try {
|
|
725
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(path.resolve(process.argv[1]));
|
|
726
|
+
} catch {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
export { compileRegistry, generatePageSchema };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { loadCompiledMintlifySource, materializeMintlifySite } from '../../dist/mintlify/index.js';
|
|
3
|
+
|
|
4
|
+
const [sourceArg, compiledArg, outputArg] = process.argv.slice(2);
|
|
5
|
+
if (!sourceArg || !compiledArg || !outputArg) {
|
|
6
|
+
throw new Error('Mintlify renderer requires content, compiled, and output directories');
|
|
7
|
+
}
|
|
8
|
+
const source = await loadCompiledMintlifySource(path.resolve(sourceArg), path.resolve(compiledArg));
|
|
9
|
+
await materializeMintlifySite({ ...source, outputDir: path.resolve(outputArg) });
|
|
@@ -19,21 +19,24 @@
|
|
|
19
19
|
* Uses the JSON schemas generated by compile-registry — portable, no React/CSS
|
|
20
20
|
* imports needed, same schemas usable by Go backend or other tools.
|
|
21
21
|
*/
|
|
22
|
+
import { realpathSync } from 'node:fs';
|
|
22
23
|
import { readdir, readFile } from 'node:fs/promises';
|
|
23
24
|
import path from 'node:path';
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
import { fileURLToPath } from 'node:url';
|
|
26
|
+
|
|
27
|
+
if (isMainModule()) {
|
|
28
|
+
const args = parseArgs(process.argv.slice(2));
|
|
29
|
+
try {
|
|
30
|
+
const result = await validateContent(args);
|
|
31
|
+
console.log(JSON.stringify(result, null, 2));
|
|
32
|
+
if (!result.success) process.exitCode = 1;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(JSON.stringify({
|
|
35
|
+
success: false,
|
|
36
|
+
errors: [{ message: String(error?.message ?? error) }],
|
|
37
|
+
}, null, 2));
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
// ---------------------------------------------------------------------------
|
|
@@ -95,6 +98,24 @@ async function validateContent(options) {
|
|
|
95
98
|
for (const block of blocks) {
|
|
96
99
|
blocksValidated++;
|
|
97
100
|
|
|
101
|
+
if (!block || typeof block !== 'object') {
|
|
102
|
+
collector.push({
|
|
103
|
+
page: relativePath,
|
|
104
|
+
blockId: '<unknown>',
|
|
105
|
+
message: 'page region entry must be a component block',
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (block.$type !== 'component') {
|
|
111
|
+
collector.push({
|
|
112
|
+
page: relativePath,
|
|
113
|
+
blockId: block.id ?? '<unknown>',
|
|
114
|
+
path: '$type',
|
|
115
|
+
message: 'block "$type" must be "component"',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
98
119
|
if (!block.component) {
|
|
99
120
|
collector.push({
|
|
100
121
|
page: relativePath,
|
|
@@ -380,6 +401,13 @@ function validateNestedSlots(parentBlock, componentMap, schemaMap, path = 'props
|
|
|
380
401
|
issues.push({ path: nestedPath, message: `${parentBlock.component}: block ref child must be a block` });
|
|
381
402
|
continue;
|
|
382
403
|
}
|
|
404
|
+
if (nestedBlock.$type !== 'component') {
|
|
405
|
+
issues.push({
|
|
406
|
+
path: `${nestedPath}.$type`,
|
|
407
|
+
message: `${parentBlock.component}: block ref child "$type" must be "component"`,
|
|
408
|
+
});
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
383
411
|
|
|
384
412
|
if (!componentMap.has(nestedBlock.component)) {
|
|
385
413
|
issues.push({ path: `${nestedPath}.component`, message: `${parentBlock.component}: unknown nested component "${nestedBlock.component}"` });
|
|
@@ -450,3 +478,14 @@ function parseArgs(argv) {
|
|
|
450
478
|
}
|
|
451
479
|
return out;
|
|
452
480
|
}
|
|
481
|
+
|
|
482
|
+
function isMainModule() {
|
|
483
|
+
if (!process.argv[1]) return false;
|
|
484
|
+
try {
|
|
485
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(path.resolve(process.argv[1]));
|
|
486
|
+
} catch {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export { validateContent };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
import { compileSchema } from '../compiler/zod-to-jsonschema.js';
|
|
6
|
+
import { defineComponentContract } from '../define-component.js';
|
|
7
|
+
import { blockRef, blockRefArray } from './block-ref.js';
|
|
8
|
+
|
|
9
|
+
const childContract = defineComponentContract({
|
|
10
|
+
id: 'child',
|
|
11
|
+
props: z.object({
|
|
12
|
+
label: z.string(),
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('block references require and accept the component discriminator', () => {
|
|
17
|
+
const schema = blockRefArray([childContract]);
|
|
18
|
+
const typed = {
|
|
19
|
+
$type: 'component',
|
|
20
|
+
id: 'child-1',
|
|
21
|
+
component: 'child',
|
|
22
|
+
props: { label: 'Hello' },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
assert.equal(schema.safeParse([typed]).success, true);
|
|
26
|
+
assert.equal(schema.safeParse([{
|
|
27
|
+
id: typed.id,
|
|
28
|
+
component: typed.component,
|
|
29
|
+
props: typed.props,
|
|
30
|
+
}]).success, false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('compiled block-reference schema exposes the component discriminator', () => {
|
|
34
|
+
const compiled = compileSchema(blockRefArray([childContract])) as {
|
|
35
|
+
items?: {
|
|
36
|
+
properties?: Record<string, { const?: string }>;
|
|
37
|
+
required?: string[];
|
|
38
|
+
additionalProperties?: boolean;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
assert.equal(compiled.items?.properties?.$type?.const, 'component');
|
|
43
|
+
assert.equal(compiled.items?.required?.includes('$type'), true);
|
|
44
|
+
assert.equal(compiled.items?.additionalProperties, false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
test('block-reference contracts enforce allowed child IDs at runtime and in compiled schemas', () => {
|
|
49
|
+
const other = defineComponentContract({ id: 'other', props: z.object({}) });
|
|
50
|
+
const allowed = { $type: 'component', id: 'child-1', component: 'child', props: { label: 'Hello' } };
|
|
51
|
+
const disallowed = { ...allowed, component: 'button' };
|
|
52
|
+
for (const multiple of [false, true]) {
|
|
53
|
+
const schema = multiple ? blockRefArray([childContract, other]) : blockRef([childContract, other]);
|
|
54
|
+
assert.equal(schema.safeParse(multiple ? [allowed] : allowed).success, true);
|
|
55
|
+
assert.equal(schema.safeParse(multiple ? [disallowed] : disallowed).success, false, 'a nested component outside the contract must be refused');
|
|
56
|
+
const compiled = compileSchema(schema) as any;
|
|
57
|
+
const block = multiple ? compiled.items : compiled;
|
|
58
|
+
assert.deepEqual(block.properties.component.enum, ['child', 'other'], 'published JSON Schema must enforce the same finite child set');
|
|
59
|
+
}
|
|
60
|
+
});
|
package/src/types/block-ref.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface BlockRefMeta {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const blockRefBase = z.object({
|
|
12
|
+
$type: z.literal('component'),
|
|
12
13
|
id: z.string().min(1),
|
|
13
14
|
component: z.string().min(1),
|
|
14
15
|
props: z.record(z.string(), z.unknown()),
|
|
@@ -21,6 +22,7 @@ type InferContractProps<T> = T extends ComponentContract<string, infer Schema>
|
|
|
21
22
|
type InferContractId<T> = T extends ComponentContract<infer Id, unknown> ? Id : never;
|
|
22
23
|
|
|
23
24
|
export type BlockRefEntry<T extends ComponentContract> = {
|
|
25
|
+
$type: 'component';
|
|
24
26
|
id: string;
|
|
25
27
|
component: InferContractId<T>;
|
|
26
28
|
props: InferContractProps<T>;
|
|
@@ -48,7 +50,7 @@ export function blockRefArray<const T extends readonly ComponentContract[]>(
|
|
|
48
50
|
contracts: T,
|
|
49
51
|
): z.ZodArray<z.ZodType<BlockRefUnion<T>>> {
|
|
50
52
|
return attachMeta(
|
|
51
|
-
z.array(blockRefBase) as unknown as z.ZodArray<z.ZodType<BlockRefUnion<T>>>,
|
|
53
|
+
z.array(blockRefBase.extend({ component: z.enum(contracts.map((c) => c.id)) })) as unknown as z.ZodArray<z.ZodType<BlockRefUnion<T>>>,
|
|
52
54
|
contracts.map((c) => c.id),
|
|
53
55
|
true,
|
|
54
56
|
);
|
|
@@ -58,7 +60,7 @@ export function blockRef<const T extends readonly ComponentContract[]>(
|
|
|
58
60
|
contracts: T,
|
|
59
61
|
): z.ZodType<BlockRefUnion<T>> {
|
|
60
62
|
return attachMeta(
|
|
61
|
-
blockRefBase as unknown as z.ZodType<BlockRefUnion<T>>,
|
|
63
|
+
blockRefBase.extend({ component: z.enum(contracts.map((c) => c.id)) }) as unknown as z.ZodType<BlockRefUnion<T>>,
|
|
62
64
|
contracts.map((c) => c.id),
|
|
63
65
|
false,
|
|
64
66
|
);
|
package/src/types/component.ts
CHANGED
|
@@ -23,6 +23,16 @@ export interface ComponentValidationIssue {
|
|
|
23
23
|
severity?: 'error' | 'warning';
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Root identity props the SDK renderers inject into every page and fragment
|
|
28
|
+
* block (sdk I-19, ADR 0051). A component spreads them onto its own root
|
|
29
|
+
* element; the starter conformance test fails a component that drops them.
|
|
30
|
+
*/
|
|
31
|
+
export interface RootIdentityProps {
|
|
32
|
+
'data-aci-block'?: string;
|
|
33
|
+
'data-aci-component'?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
export interface ComponentDefinition<TId extends string = string, TSchema = unknown> {
|
|
27
37
|
id: TId;
|
|
28
38
|
displayName?: string;
|
package/src/types/config.ts
CHANGED
package/src/types/image.ts
CHANGED
|
@@ -3,19 +3,28 @@ import { z } from 'zod';
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// Image source variants
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
6
|
+
// A DAM source carries the asset identity's slot presentation — the declared
|
|
7
|
+
// candidate widths, the fit, and the MIME format — and NEVER a URL: the SDK
|
|
8
|
+
// constructs the /dam/{assetId} permalink and its srcset at render time
|
|
9
|
+
// (ADR 0062: the compiler emits asset ids only). Static assets keep their
|
|
10
|
+
// authored src passthrough.
|
|
8
11
|
// ---------------------------------------------------------------------------
|
|
9
12
|
|
|
10
13
|
export interface ImageSource {
|
|
11
|
-
/**
|
|
12
|
-
src
|
|
13
|
-
/**
|
|
14
|
+
/** Authored asset path — static sources only; DAM sources never set it. */
|
|
15
|
+
src?: string;
|
|
16
|
+
/** Declared candidate widths for the srcset (DAM sources). Empty or absent
|
|
17
|
+
* when the slot declares no outputs: the SDK renders the bare permalink
|
|
18
|
+
* (no params, original bytes). */
|
|
19
|
+
widths?: number[];
|
|
20
|
+
/** Layout width in pixels — the largest declared width, or the intrinsic
|
|
21
|
+
* width for static/original-aspect sources. */
|
|
14
22
|
width?: number;
|
|
15
|
-
/**
|
|
23
|
+
/** Layout height in pixels — the height at `width` (aspect carrier). */
|
|
16
24
|
height?: number;
|
|
17
|
-
/**
|
|
18
|
-
srcset
|
|
25
|
+
/** Transform fit for DAM sources: 'max' fits inside the box; 'crop' fills
|
|
26
|
+
* the declared aspect and emits an h param per srcset width. */
|
|
27
|
+
fit?: 'max' | 'crop';
|
|
19
28
|
/** sizes attribute from the slot contract. */
|
|
20
29
|
sizes?: string;
|
|
21
30
|
/** Media query scoping this source (art direction). */
|
|
@@ -25,7 +34,7 @@ export interface ImageSource {
|
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
// ---------------------------------------------------------------------------
|
|
28
|
-
// Image — DAM-backed image asset with compiler-resolved
|
|
37
|
+
// Image — DAM-backed image asset with compiler-resolved live asset URLs
|
|
29
38
|
//
|
|
30
39
|
// Content authors write an asset reference: { "$type": "dam.assetRef", ... }
|
|
31
40
|
// or for static files: { "$type": "static.assetRef", "src": "/path" }
|
|
@@ -37,8 +46,9 @@ export interface Image {
|
|
|
37
46
|
$type: 'gradial.image';
|
|
38
47
|
/** DAM asset identifier (or "static:<path>" for non-DAM assets). */
|
|
39
48
|
assetId: string;
|
|
40
|
-
/** DAM version identifier
|
|
41
|
-
|
|
49
|
+
/** DAM version identifier — absent for DAM images, which compile to the
|
|
50
|
+
* versionless live URL /dam/{assetId}; never set for static images. */
|
|
51
|
+
versionId?: string;
|
|
42
52
|
/** Accessible description of the image content. */
|
|
43
53
|
alt: string;
|
|
44
54
|
/** <source> candidates produced for the asset slot. */
|
|
@@ -66,10 +76,11 @@ export interface SlotOutput {
|
|
|
66
76
|
// ---------------------------------------------------------------------------
|
|
67
77
|
|
|
68
78
|
export const ImageSourceSchema = z.object({
|
|
69
|
-
src: z.string().min(1),
|
|
70
|
-
|
|
79
|
+
src: z.string().min(1).optional(),
|
|
80
|
+
widths: z.array(z.number().int().positive()).optional(),
|
|
71
81
|
width: z.number().int().nonnegative().optional(),
|
|
72
82
|
height: z.number().int().nonnegative().optional(),
|
|
83
|
+
fit: z.enum(['max', 'crop']).optional(),
|
|
73
84
|
sizes: z.string().min(1).optional(),
|
|
74
85
|
media: z.string().min(1).optional(),
|
|
75
86
|
type: z.string().min(1),
|
|
@@ -78,17 +89,113 @@ export const ImageSourceSchema = z.object({
|
|
|
78
89
|
export const ImageSchema = z.object({
|
|
79
90
|
$type: z.literal('gradial.image'),
|
|
80
91
|
assetId: z.string().min(1),
|
|
81
|
-
versionId: z.string().min(1),
|
|
92
|
+
versionId: z.string().min(1).optional(),
|
|
82
93
|
alt: z.string(),
|
|
83
94
|
sources: z.array(ImageSourceSchema).min(1),
|
|
84
95
|
});
|
|
85
96
|
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// DAM permalink construction (ADR 0062)
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
/** Default DAM delivery quality, matching the edge's DEFAULT_QUALITY. */
|
|
102
|
+
export const DAM_IMAGE_QUALITY = 82;
|
|
103
|
+
|
|
104
|
+
let damAssetBase = '';
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Configures the origin for DAM permalinks. The default is the empty string:
|
|
108
|
+
* relative same-origin URLs (`/dam/{assetId}`), served by the site's own
|
|
109
|
+
* hosting edge. Set a base (e.g. `https://assets.example.com`) when images
|
|
110
|
+
* are consumed off-origin — email, external CMS fields, social cards — or
|
|
111
|
+
* when delivery is hosted on a dedicated domain. Call once during app setup,
|
|
112
|
+
* before the first render.
|
|
113
|
+
*/
|
|
114
|
+
export function configureDamAssets(options: { baseUrl?: string } = {}): void {
|
|
115
|
+
damAssetBase = (options.baseUrl ?? '').replace(/\/+$/, '');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The DAM permalink for an asset: `{base}/dam/{assetId}`. No params means
|
|
120
|
+
* original bytes.
|
|
121
|
+
*/
|
|
122
|
+
export function damAssetUrl(assetId: string): string {
|
|
123
|
+
return `${damAssetBase}/dam/${assetId}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const FORMAT_PARAMS: Record<string, string> = {
|
|
127
|
+
'image/avif': 'avif',
|
|
128
|
+
'image/webp': 'webp',
|
|
129
|
+
'image/jpeg': 'jpg',
|
|
130
|
+
'image/png': 'png',
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
function sourceFormatParams(source: ImageSource): string {
|
|
134
|
+
const fm = FORMAT_PARAMS[source.type] ?? 'webp';
|
|
135
|
+
return `fm=${fm}&q=${DAM_IMAGE_QUALITY}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function srcsetWidth(assetId: string, source: ImageSource, width: number): string {
|
|
139
|
+
const params = [`w=${width}`];
|
|
140
|
+
if (source.fit === 'crop' && source.width && source.height) {
|
|
141
|
+
const height = Math.round((width * source.height) / source.width);
|
|
142
|
+
params.push(`h=${height}`, 'fit=crop');
|
|
143
|
+
} else {
|
|
144
|
+
params.push('fit=max');
|
|
145
|
+
}
|
|
146
|
+
params.push(sourceFormatParams(source));
|
|
147
|
+
return `${damAssetUrl(assetId)}?${params.join('&')} ${width}w`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function damImageAttrs(image: Image, primary: ImageSource): ImageAttrs {
|
|
151
|
+
const base: ImageAttrs = {
|
|
152
|
+
src: damAssetUrl(image.assetId),
|
|
153
|
+
alt: image.alt,
|
|
154
|
+
width: primary.width && primary.width > 0 ? primary.width : undefined,
|
|
155
|
+
height: primary.height && primary.height > 0 ? primary.height : undefined,
|
|
156
|
+
loading: 'lazy',
|
|
157
|
+
decoding: 'async',
|
|
158
|
+
};
|
|
159
|
+
if (primary.widths && primary.widths.length > 0) {
|
|
160
|
+
// Primary src at the largest declared width; srcset covers them all.
|
|
161
|
+
const largest = primary.widths[primary.widths.length - 1];
|
|
162
|
+
const params = [`w=${largest}`];
|
|
163
|
+
if (primary.fit === 'crop' && primary.width && primary.height) {
|
|
164
|
+
params.push(`h=${Math.round((largest * primary.height) / primary.width)}`, 'fit=crop');
|
|
165
|
+
} else {
|
|
166
|
+
params.push('fit=max');
|
|
167
|
+
}
|
|
168
|
+
params.push(sourceFormatParams(primary));
|
|
169
|
+
base.src = `${damAssetUrl(image.assetId)}?${params.join('&')}`;
|
|
170
|
+
if (primary.widths.length > 1) {
|
|
171
|
+
base.srcSet = primary.widths.map((w) => srcsetWidth(image.assetId, primary, w)).join(', ');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return base;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function staticImageAttrs(image: Image, primary: ImageSource): ImageAttrs {
|
|
178
|
+
return {
|
|
179
|
+
src: primary.src ?? '',
|
|
180
|
+
alt: image.alt,
|
|
181
|
+
width: primary.width && primary.width > 0 ? primary.width : undefined,
|
|
182
|
+
height: primary.height && primary.height > 0 ? primary.height : undefined,
|
|
183
|
+
loading: 'lazy',
|
|
184
|
+
decoding: 'async',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
86
188
|
// ---------------------------------------------------------------------------
|
|
87
189
|
// Shared attrs helper — consumed by React, Astro, and Svelte components
|
|
88
190
|
// ---------------------------------------------------------------------------
|
|
89
191
|
|
|
90
192
|
export type ImageAttrs = Record<string, unknown>;
|
|
91
193
|
|
|
194
|
+
/** True when the image resolves to authored static bytes, not a DAM asset. */
|
|
195
|
+
export function isStaticImage(image: Image): boolean {
|
|
196
|
+
return image.assetId.startsWith('static:');
|
|
197
|
+
}
|
|
198
|
+
|
|
92
199
|
/**
|
|
93
200
|
* The primary rendition of an image — its first source entry.
|
|
94
201
|
*/
|
|
@@ -100,24 +207,21 @@ export function primaryImageSource(image: Image): ImageSource {
|
|
|
100
207
|
* Computes the full set of <img> attributes for an Image.
|
|
101
208
|
*
|
|
102
209
|
* Framework components (React <Picture>, Astro <img>, Svelte <img>) call this
|
|
103
|
-
* and spread the result into
|
|
104
|
-
* serialization.
|
|
105
|
-
*
|
|
106
|
-
*
|
|
210
|
+
* and spread the result into the native element. No HTML string
|
|
211
|
+
* serialization. DAM images construct their /dam/{assetId} permalink and
|
|
212
|
+
* srcset here from the slot-declared widths; static images pass their
|
|
213
|
+
* authored src through. Media-scoped sources need a <picture> wrapper and
|
|
214
|
+
* are not representable as plain <img> attributes, so srcset/sizes are taken
|
|
215
|
+
* from the first unscoped source only.
|
|
107
216
|
*/
|
|
108
217
|
export function imageAttrs(image: Image, overrides: ImageAttrs = {}): ImageAttrs {
|
|
109
218
|
const primary = image.sources[0];
|
|
110
219
|
const unscoped = image.sources.find((s) => !s.media) ?? primary;
|
|
111
220
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
sizes: unscoped?.sizes || undefined,
|
|
119
|
-
loading: 'lazy',
|
|
120
|
-
decoding: 'async',
|
|
121
|
-
...overrides,
|
|
122
|
-
};
|
|
221
|
+
const base = isStaticImage(image) ? staticImageAttrs(image, primary) : damImageAttrs(image, primary);
|
|
222
|
+
|
|
223
|
+
if (unscoped?.sizes) {
|
|
224
|
+
base.sizes = unscoped.sizes;
|
|
225
|
+
}
|
|
226
|
+
return { ...base, ...overrides };
|
|
123
227
|
}
|
package/src/types/index.ts
CHANGED
package/src/types/page.ts
CHANGED
package/src/types/video.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface VideoSource {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
|
-
// Video — DAM-backed video asset with compiler-resolved
|
|
14
|
+
// Video — DAM-backed video asset with compiler-resolved live asset URLs
|
|
15
15
|
//
|
|
16
16
|
// Content authors write: { "$type": "dam.assetRef", ... }
|
|
17
17
|
// or for static files: { "$type": "static.assetRef", "src": "/path" }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import Container from '../elements/Container.astro';
|
|
3
|
+
import type { RootIdentityProps } from '@gradial/aci';
|
|
3
4
|
import Logo from '../elements/Logo.astro';
|
|
4
5
|
|
|
5
6
|
interface FooterLink {
|
|
@@ -21,7 +22,7 @@ interface FooterColumn {
|
|
|
21
22
|
links?: FooterLink[];
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
interface Props {
|
|
25
|
+
interface Props extends RootIdentityProps {
|
|
25
26
|
brandHref: string;
|
|
26
27
|
brandLabel?: string;
|
|
27
28
|
brandLogo?: { src: string; alt?: string } | null;
|
|
@@ -38,7 +39,8 @@ const {
|
|
|
38
39
|
social = [],
|
|
39
40
|
columns = [],
|
|
40
41
|
legalLinks = [],
|
|
41
|
-
copyright = ''
|
|
42
|
+
copyright = '',
|
|
43
|
+
...root
|
|
42
44
|
} = Astro.props;
|
|
43
45
|
|
|
44
46
|
function columnGroups(column: FooterColumn): FooterGroup[] {
|
|
@@ -54,7 +56,7 @@ function columnGroups(column: FooterColumn): FooterGroup[] {
|
|
|
54
56
|
}
|
|
55
57
|
---
|
|
56
58
|
|
|
57
|
-
<footer class="bm-footer [font-family:var(--font-family-base)] text-fg" id="footer">
|
|
59
|
+
<footer {...root} class="bm-footer [font-family:var(--font-family-base)] text-fg" id="footer">
|
|
58
60
|
<Container className="py-[var(--spacing-footer-y)]">
|
|
59
61
|
<div class="grid gap-12 pb-24 md:grid-cols-[var(--footer-grid-columns)] lg:gap-24">
|
|
60
62
|
<a href={brandHref} class="self-start">
|