@gradial/aci 0.1.20-preview.2 → 0.1.20
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/README.md +48 -110
- package/bin/{aci → aci-darwin-arm64} +0 -0
- package/bin/aci.js +99 -6
- package/dist/assets/index.d.ts +3 -3
- package/dist/assets/index.js +3 -3
- package/dist/astro/index.d.ts +14 -3
- package/dist/astro/index.js +24 -5
- package/dist/content/assets.d.ts +17 -0
- package/dist/content/assets.js +65 -0
- package/dist/content/index.d.ts +2 -0
- package/dist/content/index.js +2 -0
- package/dist/content/provider.d.ts +19 -0
- package/dist/content/provider.js +7 -0
- package/dist/content/resolve-slots.d.ts +9 -0
- package/dist/content/resolve-slots.js +24 -0
- package/dist/content/types.d.ts +1 -1
- package/dist/define-component.d.ts +1 -2
- package/dist/define-component.js +73 -11
- package/dist/define-layout.d.ts +2 -0
- package/dist/define-layout.js +3 -0
- package/dist/dev/content-watch.d.ts +37 -0
- package/dist/dev/content-watch.js +154 -0
- package/dist/dev/index.d.ts +24 -43
- package/dist/dev/index.js +4 -169
- package/dist/index.d.ts +14 -5
- package/dist/index.js +8 -8
- package/dist/next/asset-route.d.ts +2 -0
- package/dist/next/asset-route.js +12 -4
- package/dist/next/config.d.ts +2 -2
- package/dist/next/config.js +6 -6
- package/dist/next/content-watch.js +6 -1
- package/dist/next/index.d.ts +2 -2
- package/dist/next/index.js +2 -2
- package/dist/next/middleware.d.ts +7 -3
- package/dist/next/middleware.js +12 -7
- package/dist/next/page.d.ts +16 -15
- package/dist/next/page.js +30 -10
- package/dist/next/render.d.ts +5 -0
- package/dist/next/render.js +17 -0
- package/dist/next/root-layout.d.ts +4 -0
- package/dist/next/root-layout.js +5 -0
- package/dist/next/server.d.ts +14 -13
- package/dist/next/server.js +13 -3
- package/dist/providers/file.d.ts +2 -0
- package/dist/providers/file.js +61 -1
- package/dist/providers/s3.d.ts +1 -0
- package/dist/providers/s3.js +14 -1
- package/dist/react/Media.d.ts +24 -0
- package/dist/react/Media.js +30 -0
- package/dist/react/Picture.d.ts +12 -0
- package/dist/react/Picture.js +11 -0
- package/dist/react/VideoPlayer.d.ts +13 -0
- package/dist/react/{GradialVideoPlayer.js → VideoPlayer.js} +6 -5
- package/dist/react/index.d.ts +333 -3
- package/dist/react/index.js +89 -3
- package/dist/registry.d.ts +15 -0
- package/dist/registry.js +10 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.js +15 -2
- package/dist/types/block-ref.d.ts +26 -0
- package/dist/types/block-ref.js +28 -0
- package/dist/types/component.d.ts +36 -41
- package/dist/types/config.d.ts +1 -2
- package/dist/types/image.d.ts +33 -20
- package/dist/types/image.js +42 -50
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/link.d.ts +38 -0
- package/dist/types/link.js +58 -0
- package/dist/types/media.d.ts +16 -35
- package/dist/types/media.js +15 -84
- package/dist/types/page.d.ts +1 -2
- package/dist/types/render-mode.d.ts +0 -1
- package/dist/types/renderer.d.ts +1 -1
- package/dist/types/rich-text.d.ts +40 -0
- package/dist/types/rich-text.js +39 -0
- package/dist/types/video.d.ts +7 -13
- package/dist/types/video.js +3 -3
- package/package.json +32 -74
- package/src/cli/compile-registry.mjs +236 -59
- package/src/cli/validate-content.mjs +38 -75
- package/src/types/image.ts +79 -60
- package/templates/astro/template/.aci.yaml +27 -0
- package/templates/astro/template/.storybook/main.ts +79 -0
- package/templates/astro/template/.storybook/manager.ts +6 -0
- package/templates/astro/template/.storybook/preview.ts +33 -0
- package/templates/astro/template/astro.config.mjs +37 -0
- package/templates/astro/template/package.json.tmpl +41 -0
- package/templates/astro/template/src/cms/contracts/layouts/index.ts +18 -0
- package/templates/astro/template/src/cms/registry.ts +28 -0
- package/templates/astro/template/src/components/chrome/Footer.astro +107 -0
- package/templates/astro/template/src/components/chrome/Header.astro +94 -0
- package/templates/astro/template/src/components/elements/AtomicBlock.astro +15 -0
- package/templates/astro/template/src/components/elements/AtomicBlocks.astro +16 -0
- package/templates/astro/template/src/components/elements/Button.astro +51 -0
- package/templates/astro/template/src/components/elements/ButtonBlock.astro +25 -0
- package/templates/astro/template/src/components/elements/Container.astro +11 -0
- package/templates/astro/template/src/components/elements/Icon.astro +31 -0
- package/templates/astro/template/src/components/elements/Logo.astro +29 -0
- package/templates/astro/template/src/components/elements/RemoteImage.astro +41 -0
- package/templates/astro/template/src/components/sections/ContainerSection.astro +32 -0
- package/templates/astro/template/src/components/sections/FeatureCard.astro +23 -0
- package/templates/astro/template/src/components/sections/FeatureGrid.astro +35 -0
- package/templates/astro/template/src/components/sections/HomeHero.astro +51 -0
- package/templates/astro/template/src/components/sections/StatsBar.astro +33 -0
- package/templates/astro/template/src/design-system/styles.css +399 -0
- package/templates/astro/template/src/design-system/tokens/colors.css +60 -0
- package/templates/astro/template/src/design-system/tokens/components.css +69 -0
- package/templates/astro/template/src/design-system/tokens/index.css +6 -0
- package/templates/astro/template/src/design-system/tokens/radii.css +5 -0
- package/templates/astro/template/src/design-system/tokens/shadows.css +5 -0
- package/templates/astro/template/src/design-system/tokens/spacing.css +13 -0
- package/templates/astro/template/src/design-system/tokens/typography.css +24 -0
- package/templates/astro/template/src/env.d.ts +1 -0
- package/templates/astro/template/src/fixtures/atomicBlocks.ts +33 -0
- package/templates/astro/template/src/fixtures/composition.ts +31 -0
- package/templates/astro/template/src/fixtures/index.ts +17 -0
- package/templates/astro/template/src/fixtures/sections.ts +39 -0
- package/templates/astro/template/src/layouts/SiteLayout.astro +30 -0
- package/templates/astro/template/src/pages/[...slug].astro +61 -0
- package/templates/astro/template/src/render/RenderPage.astro +66 -0
- package/templates/astro/template/src/stories/CompositionStoryFrame.astro +15 -0
- package/templates/astro/template/src/stories/HomeHero.stories.ts +24 -0
- package/templates/astro/template/src/stories/SectionStoryFrame.astro +16 -0
- package/templates/astro/template/src/stories/StoryDocFrame.astro +31 -0
- package/templates/astro/template/tsconfig.json +11 -0
- package/templates/common/.agents/AGENTS.md +37 -0
- package/templates/common/.agents/skills/aci/SKILL.md +162 -0
- package/templates/common/.agents/skills/authoring/SKILL.md +590 -0
- package/templates/common/.agents/skills/authoring/references/block-registry.md +151 -0
- package/templates/common/.agents/skills/authoring/references/contracts.md +153 -0
- package/templates/common/.agents/skills/authoring/references/design-system.md +171 -0
- package/templates/common/.agents/skills/authoring/references/image-assets.md +73 -0
- package/templates/common/.agents/skills/authoring/references/repo-structure.md +157 -0
- package/templates/common/.agents/skills/migration/SKILL.md +160 -0
- package/templates/common/.agents/skills/migration/references/analyzing-source.md +215 -0
- package/templates/common/.agents/skills/migration/references/component-migration.md +200 -0
- package/templates/common/.agents/skills/migration/references/selector-map.md +31 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
- package/templates/common/.content/config/site.json +192 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/asset.json +9 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/editorial.json +14 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/governance.json +23 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +37 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/asset.json +9 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/editorial.json +14 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/governance.json +23 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +37 -0
- package/templates/common/.content/fragments/navbar/_index.json +25 -0
- package/templates/common/.content/fragments/site-footer/_index.json +62 -0
- package/templates/common/.content/pages/about/_index.json +51 -0
- package/templates/common/.content/pages/home/_index.json +112 -0
- package/templates/common/.content/schemas/site.schema.json +123 -0
- package/templates/common/README.md.tmpl +45 -0
- package/templates/common/contracts/components/button.contract.ts +19 -0
- package/templates/common/contracts/components/container.contract.ts +19 -0
- package/templates/common/contracts/components/featureCard.contract.ts +15 -0
- package/templates/common/contracts/components/featureGrid.contract.ts +16 -0
- package/templates/common/contracts/components/homeHero.contract.ts +36 -0
- package/templates/common/contracts/components/index.ts +21 -0
- package/templates/common/contracts/components/siteFooter.contract.ts +45 -0
- package/templates/common/contracts/components/siteNavigation.contract.ts +30 -0
- package/templates/common/contracts/components/statsBar.contract.ts +19 -0
- package/templates/common/design-system/styles.css +346 -0
- package/templates/common/design-system/tokens/colors.css +110 -0
- package/templates/common/design-system/tokens/components.css +86 -0
- package/templates/common/design-system/tokens/index.css +6 -0
- package/templates/common/design-system/tokens/radii.css +5 -0
- package/templates/common/design-system/tokens/shadows.css +9 -0
- package/templates/common/design-system/tokens/spacing.css +26 -0
- package/templates/common/design-system/tokens/typography.css +32 -0
- package/templates/common/eslint-rules/no-raw-tailwind-values.js +127 -0
- package/templates/common/public/assets/Dwire-model.png +0 -0
- package/templates/common/public/assets/bare-metal-logo.png +0 -0
- package/templates/common/public/assets/meridian-logo.svg +8 -0
- package/templates/common/public/assets/seattle.avif +0 -0
- package/templates/nextjs/template/.aci.yaml +25 -0
- package/templates/nextjs/template/.storybook/main.ts +13 -0
- package/templates/nextjs/template/.storybook/preview.ts +34 -0
- package/templates/nextjs/template/eslint.config.mjs +36 -0
- package/templates/nextjs/template/next-env.d.ts +6 -0
- package/templates/nextjs/template/next.config.ts +8 -0
- package/templates/nextjs/template/package.json.tmpl +46 -0
- package/templates/nextjs/template/postcss.config.mjs +7 -0
- package/templates/nextjs/template/src/app/[[...slug]]/page.tsx +8 -0
- package/templates/nextjs/template/src/app/gradial/assets/[releaseId]/[...path]/route.ts +31 -0
- package/templates/nextjs/template/src/app/layout.tsx +29 -0
- package/templates/nextjs/template/src/cms/contracts/components/button.contract.ts +19 -0
- package/templates/nextjs/template/src/cms/contracts/components/container.contract.ts +20 -0
- package/templates/nextjs/template/src/cms/contracts/components/homeHero.contract.ts +36 -0
- package/templates/nextjs/template/src/cms/contracts/components/index.ts +15 -0
- package/templates/nextjs/template/src/cms/contracts/components/siteFooter.contract.ts +45 -0
- package/templates/nextjs/template/src/cms/contracts/components/siteNavigation.contract.ts +30 -0
- package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +16 -0
- package/templates/nextjs/template/src/cms/registry.ts +28 -0
- package/templates/nextjs/template/src/components/chrome/Footer.tsx +86 -0
- package/templates/nextjs/template/src/components/chrome/Header.tsx +74 -0
- package/templates/nextjs/template/src/components/elements/AtomicBlock.tsx +13 -0
- package/templates/nextjs/template/src/components/elements/Button.tsx +40 -0
- package/templates/nextjs/template/src/components/elements/ButtonBlock.tsx +18 -0
- package/templates/nextjs/template/src/components/elements/Container.tsx +14 -0
- package/templates/nextjs/template/src/components/elements/Icon.tsx +29 -0
- package/templates/nextjs/template/src/components/elements/Logo.tsx +38 -0
- package/templates/nextjs/template/src/components/sections/ContainerSection.tsx +28 -0
- package/templates/nextjs/template/src/components/sections/FeatureCard.tsx +25 -0
- package/templates/nextjs/template/src/components/sections/FeatureGrid.tsx +36 -0
- package/templates/nextjs/template/src/components/sections/HomeHero.tsx +62 -0
- package/templates/nextjs/template/src/components/sections/StatsBar.tsx +30 -0
- package/templates/nextjs/template/src/middleware.ts +4 -0
- package/templates/nextjs/template/src/stories/Button.stories.tsx +38 -0
- package/templates/nextjs/template/src/stories/Header.stories.tsx +30 -0
- package/templates/nextjs/template/src/stories/HomeHero.stories.tsx +33 -0
- package/templates/nextjs/template/tsconfig.json +23 -0
- package/templates/nextjs/template/vercel.json +6 -0
- package/dist/content/cache.test.d.ts +0 -1
- package/dist/content/cache.test.js +0 -86
- package/dist/content/contract.d.ts +0 -83
- package/dist/content/contract.js +0 -104
- package/dist/content/tailwind-validator.d.ts +0 -6
- package/dist/content/tailwind-validator.js +0 -31
- package/dist/content/validation.d.ts +0 -108
- package/dist/content/validation.js +0 -184
- package/dist/next/preview-banner.d.ts +0 -4
- package/dist/next/preview-banner.js +0 -51
- package/dist/next/preview.d.ts +0 -7
- package/dist/next/preview.js +0 -37
- package/dist/react/GradialMedia.d.ts +0 -24
- package/dist/react/GradialMedia.js +0 -31
- package/dist/react/GradialPicture.d.ts +0 -14
- package/dist/react/GradialPicture.js +0 -30
- package/dist/react/GradialVideoPlayer.d.ts +0 -13
- package/dist/sveltekit/index.d.ts +0 -23
- package/dist/sveltekit/index.js +0 -62
- package/dist/sveltekit/preview.d.ts +0 -5
- package/dist/sveltekit/preview.js +0 -33
- package/src/cli/verify-renderer.mjs +0 -73
- package/src/types/component.ts +0 -59
- package/src/types/config.ts +0 -38
- package/src/types/data.ts +0 -47
- package/src/types/index.ts +0 -10
- package/src/types/layout.ts +0 -29
- package/src/types/media.ts +0 -125
- package/src/types/page.ts +0 -48
- package/src/types/render-mode.ts +0 -18
- package/src/types/renderer.ts +0 -83
- package/src/types/video.ts +0 -66
package/dist/next/preview.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export async function validatePreviewToken(token, signKey) {
|
|
2
|
-
try {
|
|
3
|
-
const parts = token.split('.');
|
|
4
|
-
if (parts.length !== 3)
|
|
5
|
-
return null;
|
|
6
|
-
const signingInput = `${parts[0]}.${parts[1]}`;
|
|
7
|
-
const signature = base64URLToBytes(parts[2]);
|
|
8
|
-
const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(signKey), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
9
|
-
const expected = new Uint8Array(await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(signingInput)));
|
|
10
|
-
if (!constantTimeEqual(signature, expected))
|
|
11
|
-
return null;
|
|
12
|
-
const payload = new TextDecoder().decode(base64URLToBytes(parts[1]));
|
|
13
|
-
return JSON.parse(payload);
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function base64URLToBytes(value) {
|
|
20
|
-
const base64 = value.replace(/-/g, '+').replace(/_/g, '/');
|
|
21
|
-
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
|
|
22
|
-
const binary = atob(padded);
|
|
23
|
-
const bytes = new Uint8Array(binary.length);
|
|
24
|
-
for (let i = 0; i < binary.length; i++) {
|
|
25
|
-
bytes[i] = binary.charCodeAt(i);
|
|
26
|
-
}
|
|
27
|
-
return bytes;
|
|
28
|
-
}
|
|
29
|
-
function constantTimeEqual(left, right) {
|
|
30
|
-
if (left.length !== right.length)
|
|
31
|
-
return false;
|
|
32
|
-
let diff = 0;
|
|
33
|
-
for (let i = 0; i < left.length; i++) {
|
|
34
|
-
diff |= left[i] ^ right[i];
|
|
35
|
-
}
|
|
36
|
-
return diff === 0;
|
|
37
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { type CSSProperties, type ImgHTMLAttributes, type VideoHTMLAttributes } from 'react';
|
|
2
|
-
import type { GradialAsset } from '../types/media.js';
|
|
3
|
-
export interface GradialMediaProps {
|
|
4
|
-
/** The compiler-resolved asset to render. */
|
|
5
|
-
asset: GradialAsset;
|
|
6
|
-
/** CSS class applied to the rendered element. */
|
|
7
|
-
className?: string;
|
|
8
|
-
/** Inline styles applied to the rendered element. */
|
|
9
|
-
style?: CSSProperties;
|
|
10
|
-
/** Loading strategy for images. Defaults to 'lazy'. */
|
|
11
|
-
loading?: 'eager' | 'lazy';
|
|
12
|
-
/** Additional image attributes (only applied when asset is an image). */
|
|
13
|
-
imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'alt'>;
|
|
14
|
-
/** Additional video attributes (only applied when asset is a video). */
|
|
15
|
-
videoProps?: Omit<VideoHTMLAttributes<HTMLVideoElement>, 'src' | 'poster' | 'width' | 'height'>;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Polymorphic media component — renders a GradialAsset as the appropriate
|
|
19
|
-
* HTML element based on $type.
|
|
20
|
-
*
|
|
21
|
-
* - gradial.image → <picture> or <img> via GradialPicture
|
|
22
|
-
* - gradial.video → <video> via GradialVideoPlayer
|
|
23
|
-
*/
|
|
24
|
-
export declare function GradialMedia({ asset, className, style, loading, imgProps, videoProps, }: GradialMediaProps): import("react").FunctionComponentElement<import("./GradialPicture.js").GradialPictureProps> | import("react").FunctionComponentElement<import("./GradialVideoPlayer.js").GradialVideoPlayerProps> | null;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { createElement } from 'react';
|
|
2
|
-
import { GradialPicture } from './GradialPicture.js';
|
|
3
|
-
import { GradialVideoPlayer } from './GradialVideoPlayer.js';
|
|
4
|
-
/**
|
|
5
|
-
* Polymorphic media component — renders a GradialAsset as the appropriate
|
|
6
|
-
* HTML element based on $type.
|
|
7
|
-
*
|
|
8
|
-
* - gradial.image → <picture> or <img> via GradialPicture
|
|
9
|
-
* - gradial.video → <video> via GradialVideoPlayer
|
|
10
|
-
*/
|
|
11
|
-
export function GradialMedia({ asset, className, style, loading = 'lazy', imgProps, videoProps, }) {
|
|
12
|
-
switch (asset.$type) {
|
|
13
|
-
case 'gradial.image':
|
|
14
|
-
return createElement(GradialPicture, {
|
|
15
|
-
image: asset,
|
|
16
|
-
className,
|
|
17
|
-
style,
|
|
18
|
-
loading,
|
|
19
|
-
...imgProps,
|
|
20
|
-
});
|
|
21
|
-
case 'gradial.video':
|
|
22
|
-
return createElement(GradialVideoPlayer, {
|
|
23
|
-
video: asset,
|
|
24
|
-
className,
|
|
25
|
-
style,
|
|
26
|
-
...videoProps,
|
|
27
|
-
});
|
|
28
|
-
default:
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { type ImgHTMLAttributes } from 'react';
|
|
2
|
-
import type { GradialImage } from '../types/image.js';
|
|
3
|
-
export interface GradialPictureProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'alt'> {
|
|
4
|
-
image: GradialImage;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Renders a GradialImage as a <picture> element with responsive <source> entries,
|
|
8
|
-
* or a plain <img> when no art-directed sources exist.
|
|
9
|
-
*
|
|
10
|
-
* The compiler output determines the rendering:
|
|
11
|
-
* - sources[] populated → <picture> with <source> elements (art direction / format variants)
|
|
12
|
-
* - sources[] empty → <img> with fallback src
|
|
13
|
-
*/
|
|
14
|
-
export declare function GradialPicture({ image, ...attrs }: GradialPictureProps): import("react").DetailedReactHTMLElement<import("react").HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { createElement } from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* Renders a GradialImage as a <picture> element with responsive <source> entries,
|
|
4
|
-
* or a plain <img> when no art-directed sources exist.
|
|
5
|
-
*
|
|
6
|
-
* The compiler output determines the rendering:
|
|
7
|
-
* - sources[] populated → <picture> with <source> elements (art direction / format variants)
|
|
8
|
-
* - sources[] empty → <img> with fallback src
|
|
9
|
-
*/
|
|
10
|
-
export function GradialPicture({ image, ...attrs }) {
|
|
11
|
-
const img = createElement('img', {
|
|
12
|
-
src: image.fallback.src,
|
|
13
|
-
alt: image.alt,
|
|
14
|
-
width: image.fallback.width > 0 ? image.fallback.width : undefined,
|
|
15
|
-
height: image.fallback.height > 0 ? image.fallback.height : undefined,
|
|
16
|
-
loading: 'lazy',
|
|
17
|
-
decoding: 'async',
|
|
18
|
-
...attrs,
|
|
19
|
-
});
|
|
20
|
-
if (!image.sources?.length) {
|
|
21
|
-
return img;
|
|
22
|
-
}
|
|
23
|
-
return createElement('picture', null, ...image.sources.map((source, index) => createElement('source', {
|
|
24
|
-
key: index,
|
|
25
|
-
media: source.media,
|
|
26
|
-
type: source.type,
|
|
27
|
-
srcSet: source.srcset,
|
|
28
|
-
sizes: source.sizes,
|
|
29
|
-
})), img);
|
|
30
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { type VideoHTMLAttributes } from 'react';
|
|
2
|
-
import type { GradialVideo } from '../types/video.js';
|
|
3
|
-
export interface GradialVideoPlayerProps extends Omit<VideoHTMLAttributes<HTMLVideoElement>, 'src' | 'poster' | 'width' | 'height'> {
|
|
4
|
-
video: GradialVideo;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Renders a GradialVideo as a <video> element.
|
|
8
|
-
*
|
|
9
|
-
* - Uses poster.fallback.src for the poster attribute (single URL — browser limitation).
|
|
10
|
-
* - Includes <source> elements for format variants when available.
|
|
11
|
-
* - Falls back to src attribute on <video> when no source variants exist.
|
|
12
|
-
*/
|
|
13
|
-
export declare function GradialVideoPlayer({ video, ...attrs }: GradialVideoPlayerProps): import("react").DetailedReactHTMLElement<Record<string, unknown>, HTMLElement>;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { type GradialContentWatchOptions, type VitePluginLike } from '../dev/index.js';
|
|
2
|
-
import type { RenderInput } from '../content/types.js';
|
|
3
|
-
/**
|
|
4
|
-
* Generates prerender entries from the compiled content manifest for SvelteKit.
|
|
5
|
-
*
|
|
6
|
-
* Usage in svelte.config.js:
|
|
7
|
-
* ```js
|
|
8
|
-
* import { gradialEntries } from '@gradial/aci/sveltekit';
|
|
9
|
-
* export default { kit: { prerender: { entries: await gradialEntries() } } };
|
|
10
|
-
* ```
|
|
11
|
-
*
|
|
12
|
-
* Or in a catch-all `+page.server.ts`:
|
|
13
|
-
* ```ts
|
|
14
|
-
* import { gradialEntries } from '@gradial/aci/sveltekit';
|
|
15
|
-
* export const entries = gradialEntries;
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
export declare function gradialEntries(): Promise<string[]>;
|
|
19
|
-
export declare function getPageRuntimeRenderInput(event: {
|
|
20
|
-
request: Request;
|
|
21
|
-
}): RenderInput | null;
|
|
22
|
-
export { getPreviewMode, createPreviewBannerHTML, createPreviewCookies, type PreviewContext, type PreviewCookie, } from './preview.js';
|
|
23
|
-
export declare function withGradialAci(options?: GradialContentWatchOptions): VitePluginLike;
|
package/dist/sveltekit/index.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { gradialContentWatchPlugin, gradialDamAssetPlugin, devRefreshPort, devRefreshScriptTag, } from '../dev/index.js';
|
|
2
|
-
import { getPendingRenderInputFromHeaders } from '../runtime/page.js';
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// Entries helper — for SvelteKit SSG prerendering
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
/**
|
|
7
|
-
* Generates prerender entries from the compiled content manifest for SvelteKit.
|
|
8
|
-
*
|
|
9
|
-
* Usage in svelte.config.js:
|
|
10
|
-
* ```js
|
|
11
|
-
* import { gradialEntries } from '@gradial/aci/sveltekit';
|
|
12
|
-
* export default { kit: { prerender: { entries: await gradialEntries() } } };
|
|
13
|
-
* ```
|
|
14
|
-
*
|
|
15
|
-
* Or in a catch-all `+page.server.ts`:
|
|
16
|
-
* ```ts
|
|
17
|
-
* import { gradialEntries } from '@gradial/aci/sveltekit';
|
|
18
|
-
* export const entries = gradialEntries;
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
export async function gradialEntries() {
|
|
22
|
-
const { FileContentProvider } = await import('../providers/file.js');
|
|
23
|
-
const { normalizeRoute } = await import('../content/routes.js');
|
|
24
|
-
const provider = new FileContentProvider();
|
|
25
|
-
const routes = await provider.listRoutes();
|
|
26
|
-
return routes.map((entry) => {
|
|
27
|
-
const normalized = normalizeRoute(entry.path);
|
|
28
|
-
return normalized === '/' ? '/' : `/${normalized.replace(/^\/|\/$/g, '')}/`;
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
export function getPageRuntimeRenderInput(event) {
|
|
32
|
-
return getPendingRenderInputFromHeaders(event.request.headers);
|
|
33
|
-
}
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
// Preview support
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
export { getPreviewMode, createPreviewBannerHTML, createPreviewCookies, } from './preview.js';
|
|
38
|
-
// ---------------------------------------------------------------------------
|
|
39
|
-
// SvelteKit Vite plugin
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
export function withGradialAci(options = {}) {
|
|
42
|
-
const watchPlugin = gradialContentWatchPlugin(options);
|
|
43
|
-
const damAssetPlugin = gradialDamAssetPlugin(options);
|
|
44
|
-
return {
|
|
45
|
-
...watchPlugin,
|
|
46
|
-
name: 'gradial-sveltekit',
|
|
47
|
-
configureServer(server) {
|
|
48
|
-
damAssetPlugin.configureServer?.(server);
|
|
49
|
-
watchPlugin.configureServer?.(server);
|
|
50
|
-
},
|
|
51
|
-
transformIndexHtml(html) {
|
|
52
|
-
if (process.env.NODE_ENV === 'production') {
|
|
53
|
-
return html;
|
|
54
|
-
}
|
|
55
|
-
if (devRefreshPort(options) <= 0) {
|
|
56
|
-
return html;
|
|
57
|
-
}
|
|
58
|
-
const script = devRefreshScriptTag(options);
|
|
59
|
-
return html.includes('</body>') ? html.replace('</body>', `${script}</body>`) : `${html}${script}`;
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { type PreviewContext, type PreviewCookie } from '../preview/core.js';
|
|
2
|
-
export declare function getPreviewMode(request: Request, siteId: string): Promise<PreviewContext>;
|
|
3
|
-
export declare function createPreviewBannerHTML(releaseId: string): string;
|
|
4
|
-
export declare function createPreviewCookies(token: string, releaseId: string): PreviewCookie[];
|
|
5
|
-
export type { PreviewContext, PreviewCookie };
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { extractPreviewToken, resolvePreviewContext, createPreviewBannerHTML as coreCreatePreviewBannerHTML, createPreviewCookies as coreCreatePreviewCookies, previewSignKey, } from '../preview/core.js';
|
|
2
|
-
export async function getPreviewMode(request, siteId) {
|
|
3
|
-
const url = new URL(request.url);
|
|
4
|
-
const token = extractPreviewToken({
|
|
5
|
-
query: url.searchParams,
|
|
6
|
-
cookies: parseCookieHeader(request.headers.get('cookie') || ''),
|
|
7
|
-
authorization: request.headers.get('authorization') || undefined,
|
|
8
|
-
});
|
|
9
|
-
if (!token)
|
|
10
|
-
return { isPreview: false };
|
|
11
|
-
return resolvePreviewContext({ token, siteId, signKey: previewSignKey() });
|
|
12
|
-
}
|
|
13
|
-
export function createPreviewBannerHTML(releaseId) {
|
|
14
|
-
return coreCreatePreviewBannerHTML(releaseId);
|
|
15
|
-
}
|
|
16
|
-
export function createPreviewCookies(token, releaseId) {
|
|
17
|
-
return coreCreatePreviewCookies(token, releaseId);
|
|
18
|
-
}
|
|
19
|
-
function parseCookieHeader(header) {
|
|
20
|
-
const cookies = new Map();
|
|
21
|
-
for (const part of header.split(';')) {
|
|
22
|
-
const trimmed = part.trim();
|
|
23
|
-
if (!trimmed)
|
|
24
|
-
continue;
|
|
25
|
-
const idx = trimmed.indexOf('=');
|
|
26
|
-
if (idx < 0)
|
|
27
|
-
continue;
|
|
28
|
-
const name = trimmed.slice(0, idx).trim();
|
|
29
|
-
const value = trimmed.slice(idx + 1).trim();
|
|
30
|
-
cookies.set(name, decodeURIComponent(value));
|
|
31
|
-
}
|
|
32
|
-
return cookies;
|
|
33
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
-
|
|
6
|
-
const args = parseArgs(process.argv.slice(2));
|
|
7
|
-
|
|
8
|
-
try {
|
|
9
|
-
if (!args.renderer) throw new Error('missing --renderer');
|
|
10
|
-
await verifyRenderer(path.resolve(args.renderer));
|
|
11
|
-
console.log(JSON.stringify({ success: true, errors: [] }, null, 2));
|
|
12
|
-
} catch (error) {
|
|
13
|
-
console.error(JSON.stringify({ success: false, errors: [String(error?.message ?? error)] }, null, 2));
|
|
14
|
-
process.exitCode = 1;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async function verifyRenderer(rendererPath) {
|
|
18
|
-
try {
|
|
19
|
-
const mod = await import(pathToFileURL(rendererPath).href);
|
|
20
|
-
const renderer = mod.default ?? mod.renderer;
|
|
21
|
-
if (!renderer || typeof renderer.renderPage !== 'function') {
|
|
22
|
-
throw new Error('renderer must export a GradialRenderer with renderPage');
|
|
23
|
-
}
|
|
24
|
-
return;
|
|
25
|
-
} catch (error) {
|
|
26
|
-
if (!shouldUseStaticFallback(error)) {
|
|
27
|
-
throw error;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const source = fs.readFileSync(rendererPath, 'utf8');
|
|
32
|
-
if (!staticallyExportsRenderPage(source)) {
|
|
33
|
-
throw new Error('renderer must export a GradialRenderer with renderPage');
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function shouldUseStaticFallback(error) {
|
|
38
|
-
const message = String(error?.message ?? error);
|
|
39
|
-
return message.includes('Unknown file extension ".astro"');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function staticallyExportsRenderPage(source) {
|
|
43
|
-
if (!/\brenderPage\s*[:=]\s*(async\s*)?(\([^)]*\)|[A-Za-z_$][\w$]*)?\s*=>|\brenderPage\s*[:=]\s*(async\s*)?function\b|\basync\s+renderPage\s*\(|\brenderPage\s*\(/m.test(source)) {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
if (/export\s+default\s+{[\s\S]*\brenderPage\b[\s\S]*}/m.test(source)) {
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
const defaultName = source.match(/export\s+default\s+([A-Za-z_$][\w$]*)\s*;?/m)?.[1];
|
|
50
|
-
const namedExport = source.match(/export\s*{\s*([A-Za-z_$][\w$]*)\s+as\s+renderer\s*}/m)?.[1] ?? source.match(/export\s*{\s*([A-Za-z_$][\w$]*)\s*}/m)?.[1];
|
|
51
|
-
const rendererName = defaultName ?? namedExport;
|
|
52
|
-
if (!rendererName) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
const declaration = new RegExp(`(?:const|let|var)\\s+${rendererName}\\b[\\s\\S]*?\\{[\\s\\S]*?\\brenderPage\\b`, 'm');
|
|
56
|
-
return declaration.test(source);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function parseArgs(argv) {
|
|
60
|
-
const out = {};
|
|
61
|
-
for (let i = 0; i < argv.length; i++) {
|
|
62
|
-
const arg = argv[i];
|
|
63
|
-
if (!arg.startsWith('--')) continue;
|
|
64
|
-
const body = arg.slice(2);
|
|
65
|
-
const equals = body.indexOf('=');
|
|
66
|
-
if (equals >= 0) {
|
|
67
|
-
out[body.slice(0, equals)] = body.slice(equals + 1);
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
out[body] = argv[++i];
|
|
71
|
-
}
|
|
72
|
-
return out;
|
|
73
|
-
}
|
package/src/types/component.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { IslandMode, VaryDimension } from './render-mode.js';
|
|
2
|
-
import type { ImageSlotContract } from './image.js';
|
|
3
|
-
import type { z } from 'zod';
|
|
4
|
-
|
|
5
|
-
export interface ComponentRenderModes {
|
|
6
|
-
canStatic: boolean;
|
|
7
|
-
canSSR: boolean;
|
|
8
|
-
canClientIsland: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* A CMS-registered component — sync or async (server components).
|
|
13
|
-
* Accepts content props derived from the Zod schema.
|
|
14
|
-
*/
|
|
15
|
-
type CmsComponentFn<TProps = any> =
|
|
16
|
-
| ((props: TProps) => import('react').ReactElement | null)
|
|
17
|
-
| ((props: TProps) => Promise<import('react').ReactElement | null>);
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Infers the props type from a schema.
|
|
21
|
-
* If TSchema is a Zod type, returns z.infer<TSchema>.
|
|
22
|
-
* Otherwise returns Record<string, unknown>.
|
|
23
|
-
*/
|
|
24
|
-
type InferContentProps<TSchema> = TSchema extends z.ZodType<infer T> ? T : Record<string, unknown>;
|
|
25
|
-
|
|
26
|
-
export interface ComponentDefinition<TSchema = unknown> {
|
|
27
|
-
name: string;
|
|
28
|
-
/**
|
|
29
|
-
* The React component that renders this CMS block.
|
|
30
|
-
* Accepts at least the schema-derived props, but may accept additional
|
|
31
|
-
* composition params (className, etc.) passed by parent sections.
|
|
32
|
-
*/
|
|
33
|
-
component?: CmsComponentFn<any>;
|
|
34
|
-
schema: TSchema;
|
|
35
|
-
renderModes?: ComponentRenderModes;
|
|
36
|
-
render?: ComponentRenderModes;
|
|
37
|
-
defaultIslandMode?: IslandMode;
|
|
38
|
-
varyDimensions?: VaryDimension[];
|
|
39
|
-
vary?: VaryDimension[];
|
|
40
|
-
imageSlots?: Record<string, ImageSlotContract>;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface ComponentContract<TSchema = unknown> {
|
|
44
|
-
name: string;
|
|
45
|
-
component?: CmsComponentFn<any>;
|
|
46
|
-
schema: TSchema;
|
|
47
|
-
renderModes: ComponentRenderModes;
|
|
48
|
-
render: ComponentRenderModes;
|
|
49
|
-
defaultIslandMode?: IslandMode;
|
|
50
|
-
varyDimensions?: VaryDimension[];
|
|
51
|
-
vary?: VaryDimension[];
|
|
52
|
-
imageSlots?: Record<string, ImageSlotContract>;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export type InferComponentProps<TContract> = TContract extends ComponentContract<infer TSchema>
|
|
56
|
-
? InferContentProps<TSchema>
|
|
57
|
-
: Record<string, unknown>;
|
|
58
|
-
|
|
59
|
-
export type { CmsComponentFn, InferContentProps };
|
package/src/types/config.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { RenderCapabilities } from './renderer.js';
|
|
2
|
-
|
|
3
|
-
export interface BareMetalConfig {
|
|
4
|
-
version: '1';
|
|
5
|
-
siteId: string;
|
|
6
|
-
framework: 'astro' | 'next' | 'sveltekit' | 'custom';
|
|
7
|
-
source: {
|
|
8
|
-
root: string;
|
|
9
|
-
outDir?: string;
|
|
10
|
-
publicDir?: string;
|
|
11
|
-
};
|
|
12
|
-
componentRegistry: string;
|
|
13
|
-
dataRegistry?: string;
|
|
14
|
-
layoutRegistry: string;
|
|
15
|
-
rendererEntry: string;
|
|
16
|
-
capabilities: RenderCapabilities;
|
|
17
|
-
routes: RouteConfig;
|
|
18
|
-
dam?: DAMConfig;
|
|
19
|
-
externalDependencies?: ExternalDependency[];
|
|
20
|
-
rendererProtocol: 'http' | 'stdio-json';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface RouteConfig {
|
|
24
|
-
cmsManaged: string;
|
|
25
|
-
frameworkOwned?: string[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface ExternalDependency {
|
|
29
|
-
name: string;
|
|
30
|
-
host: string;
|
|
31
|
-
kind?: 'api' | 'client-script';
|
|
32
|
-
description?: string;
|
|
33
|
-
cacheTTL?: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface DAMConfig {
|
|
37
|
-
autoApproveUploads?: boolean;
|
|
38
|
-
}
|
package/src/types/data.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
type InferDataProps<TSchema> = TSchema extends z.ZodType<infer T>
|
|
4
|
-
? T
|
|
5
|
-
: Record<string, unknown>;
|
|
6
|
-
|
|
7
|
-
const dataRefPattern = /^\$ref:data\/[A-Za-z0-9][A-Za-z0-9_./-]*$/;
|
|
8
|
-
|
|
9
|
-
export interface DataModelDefinition<TSchema = unknown, TType extends string = string> {
|
|
10
|
-
type: TType;
|
|
11
|
-
schema: TSchema;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface DataModelContract<TSchema = unknown, TType extends string = string> {
|
|
15
|
-
type: TType;
|
|
16
|
-
schema: TSchema;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface DataEntry<TProps = Record<string, unknown>, TType extends string = string> {
|
|
20
|
-
$type: TType;
|
|
21
|
-
props: TProps;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type InferDataModelProps<TContract> = TContract extends DataModelContract<infer TSchema, string>
|
|
25
|
-
? InferDataProps<TSchema>
|
|
26
|
-
: Record<string, unknown>;
|
|
27
|
-
|
|
28
|
-
export type InferDataModelType<TContract> = TContract extends DataModelContract<unknown, infer TType>
|
|
29
|
-
? TType
|
|
30
|
-
: string;
|
|
31
|
-
|
|
32
|
-
export type DataModelRef<TContract extends DataModelContract> = DataEntry<
|
|
33
|
-
InferDataModelProps<TContract>,
|
|
34
|
-
InferDataModelType<TContract>
|
|
35
|
-
>;
|
|
36
|
-
|
|
37
|
-
export function dataModelRef<TContract extends DataModelContract<z.ZodTypeAny>>(
|
|
38
|
-
contract: TContract,
|
|
39
|
-
): z.ZodType<DataModelRef<TContract>> {
|
|
40
|
-
return z.union([
|
|
41
|
-
z.string().regex(dataRefPattern, `expected a $ref:data/... pointer for ${contract.type}`),
|
|
42
|
-
z.object({
|
|
43
|
-
$type: z.literal(contract.type),
|
|
44
|
-
props: contract.schema,
|
|
45
|
-
}),
|
|
46
|
-
]).describe(`data model reference: ${contract.type}`) as unknown as z.ZodType<DataModelRef<TContract>>;
|
|
47
|
-
}
|
package/src/types/index.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export * from './config.js';
|
|
2
|
-
export * from './component.js';
|
|
3
|
-
export * from './data.js';
|
|
4
|
-
export * from './layout.js';
|
|
5
|
-
export * from './page.js';
|
|
6
|
-
export * from './renderer.js';
|
|
7
|
-
export * from './render-mode.js';
|
|
8
|
-
export * from './image.js';
|
|
9
|
-
export * from './video.js';
|
|
10
|
-
export * from './media.js';
|
package/src/types/layout.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { FragmentRefNode } from './page.js';
|
|
2
|
-
|
|
3
|
-
export interface LayoutSlot {
|
|
4
|
-
name: string;
|
|
5
|
-
required: boolean;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/** Default content for non-required layout slots, keyed by slot name. */
|
|
9
|
-
export type LayoutDefaults = Record<string, FragmentRefNode[]>;
|
|
10
|
-
|
|
11
|
-
export interface LayoutDefinition {
|
|
12
|
-
name: string;
|
|
13
|
-
slots: LayoutSlot[];
|
|
14
|
-
/** Default fragment references for slots not provided by the page. */
|
|
15
|
-
defaults?: LayoutDefaults;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface LayoutContract {
|
|
19
|
-
name: string;
|
|
20
|
-
slots: LayoutSlot[];
|
|
21
|
-
defaults?: LayoutDefaults;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** A compiled layout file as stored on disk. */
|
|
25
|
-
export interface CompiledLayout {
|
|
26
|
-
name: string;
|
|
27
|
-
slots: LayoutSlot[];
|
|
28
|
-
defaults: LayoutDefaults;
|
|
29
|
-
}
|
package/src/types/media.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { GradialImageSchema, renderImageHTML, type GradialImage } from './image.js';
|
|
3
|
-
import { GradialVideoSchema, type GradialVideo } from './video.js';
|
|
4
|
-
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// GradialAsset — union of all DAM-backed asset types
|
|
7
|
-
//
|
|
8
|
-
// Content authors write: { "$type": "dam.assetRef", "assetId": "..." }
|
|
9
|
-
// The compiler resolves this to a GradialImage or GradialVideo based on
|
|
10
|
-
// the asset's media type in the DAM.
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
|
|
13
|
-
/** Discriminated union of all compiler-resolved asset types. */
|
|
14
|
-
export type GradialAsset = GradialImage | GradialVideo;
|
|
15
|
-
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Zod schema — discriminated on $type
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
|
|
20
|
-
export const GradialAssetSchema = z.discriminatedUnion('$type', [
|
|
21
|
-
GradialImageSchema,
|
|
22
|
-
GradialVideoSchema,
|
|
23
|
-
]);
|
|
24
|
-
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
// Type guards
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
|
|
29
|
-
export function isGradialImage(value: unknown): value is GradialImage {
|
|
30
|
-
return (
|
|
31
|
-
typeof value === 'object' &&
|
|
32
|
-
value !== null &&
|
|
33
|
-
(value as Record<string, unknown>).$type === 'gradial.image'
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function isGradialVideo(value: unknown): value is GradialVideo {
|
|
38
|
-
return (
|
|
39
|
-
typeof value === 'object' &&
|
|
40
|
-
value !== null &&
|
|
41
|
-
(value as Record<string, unknown>).$type === 'gradial.video'
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function isGradialAsset(value: unknown): value is GradialAsset {
|
|
46
|
-
return isGradialImage(value) || isGradialVideo(value);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
// HTML string renderers (framework-agnostic)
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Renders a GradialVideo to an HTML string.
|
|
55
|
-
* Uses poster.fallback.src for the poster attribute.
|
|
56
|
-
* Includes <source> elements for format variants.
|
|
57
|
-
*/
|
|
58
|
-
export function renderVideoHTML(
|
|
59
|
-
video: GradialVideo,
|
|
60
|
-
attrs: Record<string, string | number | boolean | undefined | null> = {},
|
|
61
|
-
): string {
|
|
62
|
-
const posterSrc = video.poster?.fallback.src;
|
|
63
|
-
const attrPairs: Array<[string, string | number | boolean | undefined | null]> = [
|
|
64
|
-
...Object.entries(attrs),
|
|
65
|
-
['src', video.sources?.length ? undefined : video.src],
|
|
66
|
-
['poster', posterSrc],
|
|
67
|
-
['width', video.width > 0 ? video.width : undefined],
|
|
68
|
-
['height', video.height > 0 ? video.height : undefined],
|
|
69
|
-
];
|
|
70
|
-
const attrString = renderAttrs(attrPairs);
|
|
71
|
-
|
|
72
|
-
const sources = (video.sources ?? [])
|
|
73
|
-
.map((s) => `<source${renderAttrs([['src', s.src], ['type', s.type]])}>`)
|
|
74
|
-
.join('');
|
|
75
|
-
|
|
76
|
-
// If no format variants, src is on the <video> element itself
|
|
77
|
-
if (!sources) {
|
|
78
|
-
return `<video${attrString}></video>`;
|
|
79
|
-
}
|
|
80
|
-
return `<video${attrString}>${sources}</video>`;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Renders any GradialAsset to an HTML string.
|
|
85
|
-
* Delegates to renderImageHTML or renderVideoHTML based on $type.
|
|
86
|
-
*/
|
|
87
|
-
export function renderAssetHTML(
|
|
88
|
-
asset: GradialAsset,
|
|
89
|
-
attrs: Record<string, string | number | boolean | undefined | null> = {},
|
|
90
|
-
): string {
|
|
91
|
-
if (asset.$type === 'gradial.image') {
|
|
92
|
-
return renderImageHTML(asset, attrs);
|
|
93
|
-
}
|
|
94
|
-
return renderVideoHTML(asset, attrs);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// ---------------------------------------------------------------------------
|
|
98
|
-
// Internal helpers
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
|
|
101
|
-
function renderAttrs(
|
|
102
|
-
pairs: Array<[string, string | number | boolean | undefined | null]>,
|
|
103
|
-
): string {
|
|
104
|
-
const rendered = pairs
|
|
105
|
-
.filter(([, value]) => value !== undefined && value !== null && value !== false)
|
|
106
|
-
.map(([name, value]) =>
|
|
107
|
-
value === true
|
|
108
|
-
? escapeName(name)
|
|
109
|
-
: `${escapeName(name)}="${escapeAttr(String(value))}"`,
|
|
110
|
-
);
|
|
111
|
-
if (!rendered.length) return '';
|
|
112
|
-
return ' ' + rendered.join(' ');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function escapeName(name: string): string {
|
|
116
|
-
return name.replace(/[^A-Za-z0-9_:-]/g, '');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function escapeAttr(value: string): string {
|
|
120
|
-
return value
|
|
121
|
-
.replace(/&/g, '&')
|
|
122
|
-
.replace(/"/g, '"')
|
|
123
|
-
.replace(/</g, '<')
|
|
124
|
-
.replace(/>/g, '>');
|
|
125
|
-
}
|