@cogenta/render 0.1.0
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/dist/astro/integration.d.ts +57 -0
- package/dist/astro/integration.d.ts.map +1 -0
- package/dist/astro/integration.js +72 -0
- package/dist/astro/integration.js.map +1 -0
- package/dist/build/index.d.ts +7 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/build/index.js +7 -0
- package/dist/build/index.js.map +1 -0
- package/dist/build/integration.d.ts +25 -0
- package/dist/build/integration.d.ts.map +1 -0
- package/dist/build/integration.js +41 -0
- package/dist/build/integration.js.map +1 -0
- package/dist/build/plan.d.ts +41 -0
- package/dist/build/plan.d.ts.map +1 -0
- package/dist/build/plan.js +48 -0
- package/dist/build/plan.js.map +1 -0
- package/dist/build/refusal.d.ts +14 -0
- package/dist/build/refusal.d.ts.map +1 -0
- package/dist/build/refusal.js +110 -0
- package/dist/build/refusal.js.map +1 -0
- package/dist/build/requirements.d.ts +59 -0
- package/dist/build/requirements.d.ts.map +1 -0
- package/dist/build/requirements.js +74 -0
- package/dist/build/requirements.js.map +1 -0
- package/dist/build/run-build.d.ts +43 -0
- package/dist/build/run-build.d.ts.map +1 -0
- package/dist/build/run-build.js +73 -0
- package/dist/build/run-build.js.map +1 -0
- package/dist/build/targets.d.ts +47 -0
- package/dist/build/targets.d.ts.map +1 -0
- package/dist/build/targets.js +69 -0
- package/dist/build/targets.js.map +1 -0
- package/dist/cache/index.d.ts +7 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +4 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/page-cache.d.ts +64 -0
- package/dist/cache/page-cache.d.ts.map +1 -0
- package/dist/cache/page-cache.js +128 -0
- package/dist/cache/page-cache.js.map +1 -0
- package/dist/cache/recorder.d.ts +49 -0
- package/dist/cache/recorder.d.ts.map +1 -0
- package/dist/cache/recorder.js +77 -0
- package/dist/cache/recorder.js.map +1 -0
- package/dist/cache/tags.d.ts +55 -0
- package/dist/cache/tags.d.ts.map +1 -0
- package/dist/cache/tags.js +62 -0
- package/dist/cache/tags.js.map +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/content/client.d.ts +35 -0
- package/dist/content/client.d.ts.map +1 -0
- package/dist/content/client.js +155 -0
- package/dist/content/client.js.map +1 -0
- package/dist/content/types.d.ts +104 -0
- package/dist/content/types.d.ts.map +1 -0
- package/dist/content/types.js +16 -0
- package/dist/content/types.js.map +1 -0
- package/dist/context/render-context.d.ts +31 -0
- package/dist/context/render-context.d.ts.map +1 -0
- package/dist/context/render-context.js +152 -0
- package/dist/context/render-context.js.map +1 -0
- package/dist/context/types.d.ts +49 -0
- package/dist/context/types.d.ts.map +1 -0
- package/dist/context/types.js +2 -0
- package/dist/context/types.js.map +1 -0
- package/dist/images/errors.d.ts +20 -0
- package/dist/images/errors.d.ts.map +1 -0
- package/dist/images/errors.js +58 -0
- package/dist/images/errors.js.map +1 -0
- package/dist/images/format.d.ts +18 -0
- package/dist/images/format.d.ts.map +1 -0
- package/dist/images/format.js +34 -0
- package/dist/images/format.js.map +1 -0
- package/dist/images/geometry.d.ts +46 -0
- package/dist/images/geometry.d.ts.map +1 -0
- package/dist/images/geometry.js +127 -0
- package/dist/images/geometry.js.map +1 -0
- package/dist/images/index.d.ts +29 -0
- package/dist/images/index.d.ts.map +1 -0
- package/dist/images/index.js +30 -0
- package/dist/images/index.js.map +1 -0
- package/dist/images/pipeline.d.ts +51 -0
- package/dist/images/pipeline.d.ts.map +1 -0
- package/dist/images/pipeline.js +105 -0
- package/dist/images/pipeline.js.map +1 -0
- package/dist/images/sharp.d.ts +57 -0
- package/dist/images/sharp.d.ts.map +1 -0
- package/dist/images/sharp.js +139 -0
- package/dist/images/sharp.js.map +1 -0
- package/dist/images/srcset.d.ts +33 -0
- package/dist/images/srcset.d.ts.map +1 -0
- package/dist/images/srcset.js +94 -0
- package/dist/images/srcset.js.map +1 -0
- package/dist/images/store.d.ts +32 -0
- package/dist/images/store.d.ts.map +1 -0
- package/dist/images/store.js +54 -0
- package/dist/images/store.js.map +1 -0
- package/dist/images/types.d.ts +128 -0
- package/dist/images/types.d.ts.map +1 -0
- package/dist/images/types.js +22 -0
- package/dist/images/types.js.map +1 -0
- package/dist/images/wasm.d.ts +43 -0
- package/dist/images/wasm.d.ts.map +1 -0
- package/dist/images/wasm.js +128 -0
- package/dist/images/wasm.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/pwa/build.d.ts +39 -0
- package/dist/pwa/build.d.ts.map +1 -0
- package/dist/pwa/build.js +38 -0
- package/dist/pwa/build.js.map +1 -0
- package/dist/pwa/client.d.ts +90 -0
- package/dist/pwa/client.d.ts.map +1 -0
- package/dist/pwa/client.js +76 -0
- package/dist/pwa/client.js.map +1 -0
- package/dist/pwa/index.d.ts +29 -0
- package/dist/pwa/index.d.ts.map +1 -0
- package/dist/pwa/index.js +22 -0
- package/dist/pwa/index.js.map +1 -0
- package/dist/pwa/manifest.d.ts +69 -0
- package/dist/pwa/manifest.d.ts.map +1 -0
- package/dist/pwa/manifest.js +108 -0
- package/dist/pwa/manifest.js.map +1 -0
- package/dist/pwa/offline.d.ts +44 -0
- package/dist/pwa/offline.d.ts.map +1 -0
- package/dist/pwa/offline.js +94 -0
- package/dist/pwa/offline.js.map +1 -0
- package/dist/pwa/service-worker.d.ts +71 -0
- package/dist/pwa/service-worker.d.ts.map +1 -0
- package/dist/pwa/service-worker.js +119 -0
- package/dist/pwa/service-worker.js.map +1 -0
- package/dist/pwa/strategy.d.ts +36 -0
- package/dist/pwa/strategy.d.ts.map +1 -0
- package/dist/pwa/strategy.js +180 -0
- package/dist/pwa/strategy.js.map +1 -0
- package/dist/pwa/sw-runtime.d.ts +109 -0
- package/dist/pwa/sw-runtime.d.ts.map +1 -0
- package/dist/pwa/sw-runtime.js +214 -0
- package/dist/pwa/sw-runtime.js.map +1 -0
- package/dist/pwa/types.d.ts +107 -0
- package/dist/pwa/types.d.ts.map +1 -0
- package/dist/pwa/types.js +18 -0
- package/dist/pwa/types.js.map +1 -0
- package/dist/pwa/version.d.ts +45 -0
- package/dist/pwa/version.d.ts.map +1 -0
- package/dist/pwa/version.js +89 -0
- package/dist/pwa/version.js.map +1 -0
- package/dist/skin/color.d.ts +50 -0
- package/dist/skin/color.d.ts.map +1 -0
- package/dist/skin/color.js +176 -0
- package/dist/skin/color.js.map +1 -0
- package/dist/skin/css.d.ts +19 -0
- package/dist/skin/css.d.ts.map +1 -0
- package/dist/skin/css.js +69 -0
- package/dist/skin/css.js.map +1 -0
- package/dist/skin/errors.d.ts +22 -0
- package/dist/skin/errors.d.ts.map +1 -0
- package/dist/skin/errors.js +59 -0
- package/dist/skin/errors.js.map +1 -0
- package/dist/skin/index.d.ts +15 -0
- package/dist/skin/index.d.ts.map +1 -0
- package/dist/skin/index.js +11 -0
- package/dist/skin/index.js.map +1 -0
- package/dist/skin/sheet.d.ts +42 -0
- package/dist/skin/sheet.d.ts.map +1 -0
- package/dist/skin/sheet.js +36 -0
- package/dist/skin/sheet.js.map +1 -0
- package/dist/skin/tokens.d.ts +90 -0
- package/dist/skin/tokens.d.ts.map +1 -0
- package/dist/skin/tokens.js +56 -0
- package/dist/skin/tokens.js.map +1 -0
- package/dist/skin/validate.d.ts +14 -0
- package/dist/skin/validate.d.ts.map +1 -0
- package/dist/skin/validate.js +272 -0
- package/dist/skin/validate.js.map +1 -0
- package/dist/theme/load-theme.d.ts +37 -0
- package/dist/theme/load-theme.d.ts.map +1 -0
- package/dist/theme/load-theme.js +102 -0
- package/dist/theme/load-theme.js.map +1 -0
- package/dist/theme/manifest.d.ts +48 -0
- package/dist/theme/manifest.d.ts.map +1 -0
- package/dist/theme/manifest.js +36 -0
- package/dist/theme/manifest.js.map +1 -0
- package/dist/theme/verify/forbidden.d.ts +39 -0
- package/dist/theme/verify/forbidden.d.ts.map +1 -0
- package/dist/theme/verify/forbidden.js +107 -0
- package/dist/theme/verify/forbidden.js.map +1 -0
- package/dist/theme/verify/scan-file.d.ts +15 -0
- package/dist/theme/verify/scan-file.d.ts.map +1 -0
- package/dist/theme/verify/scan-file.js +151 -0
- package/dist/theme/verify/scan-file.js.map +1 -0
- package/dist/theme/verify/scanner.d.ts +52 -0
- package/dist/theme/verify/scanner.d.ts.map +1 -0
- package/dist/theme/verify/scanner.js +304 -0
- package/dist/theme/verify/scanner.js.map +1 -0
- package/dist/theme/verify/verify-theme.d.ts +44 -0
- package/dist/theme/verify/verify-theme.d.ts.map +1 -0
- package/dist/theme/verify/verify-theme.js +179 -0
- package/dist/theme/verify/verify-theme.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { CogentaError } from '@cogenta/core';
|
|
2
|
+
/** Widths a responsive image is offered in. Fixed, so that variants stay countable. */
|
|
3
|
+
const SRCSET_WIDTHS = [320, 640, 960, 1280, 1920];
|
|
4
|
+
const DEFAULT_IMAGE_ENDPOINT = '/_image';
|
|
5
|
+
export function createRenderContext(options) {
|
|
6
|
+
const site = options.site;
|
|
7
|
+
const locale = options.locale;
|
|
8
|
+
const url = typeof options.url === 'string' ? new URL(options.url, site.url) : options.url;
|
|
9
|
+
const messages = options.messages ?? {};
|
|
10
|
+
const imageEndpoint = options.imageEndpoint ?? DEFAULT_IMAGE_ENDPOINT;
|
|
11
|
+
if (!site.locales.includes(locale)) {
|
|
12
|
+
throw new CogentaError({
|
|
13
|
+
code: 'CONFIG_INVALID',
|
|
14
|
+
message: `Cannot render locale "${locale}": the site declares ${site.locales.join(', ')}.`,
|
|
15
|
+
hint: 'Add the locale to site.locales, or render one the site knows about.',
|
|
16
|
+
details: { locale, locales: site.locales },
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
site: {
|
|
21
|
+
name: site.name,
|
|
22
|
+
url: site.url,
|
|
23
|
+
locales: site.locales,
|
|
24
|
+
defaultLocale: site.defaultLocale,
|
|
25
|
+
},
|
|
26
|
+
locale,
|
|
27
|
+
url,
|
|
28
|
+
content: options.content,
|
|
29
|
+
t(key, values) {
|
|
30
|
+
const template = messages[locale]?.[key] ?? messages[site.defaultLocale]?.[key] ?? key;
|
|
31
|
+
return values === undefined ? template : interpolate(template, values);
|
|
32
|
+
},
|
|
33
|
+
image(media, imageOptions) {
|
|
34
|
+
return buildImageSource(imageEndpoint, media, imageOptions ?? {});
|
|
35
|
+
},
|
|
36
|
+
link(target) {
|
|
37
|
+
return resolveLink(target, {
|
|
38
|
+
locale,
|
|
39
|
+
defaultLocale: site.defaultLocale,
|
|
40
|
+
resolveEntryPath: options.resolveEntryPath,
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** `{name}` only. A template language in a theme string is a template language to secure. */
|
|
46
|
+
function interpolate(template, values) {
|
|
47
|
+
return template.replace(/\{(\w+)\}/gu, (match, name) => {
|
|
48
|
+
const value = values[name];
|
|
49
|
+
return value === undefined ? match : String(value);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function resolveLink(target, resolution) {
|
|
53
|
+
if (typeof target === 'string') {
|
|
54
|
+
// An absolute or protocol-relative target is left exactly as written: it
|
|
55
|
+
// leaves the site, so localising it would be wrong.
|
|
56
|
+
if (/^([a-z][a-z0-9+.-]*:|\/\/|#)/iu.test(target))
|
|
57
|
+
return target;
|
|
58
|
+
return localise(target, resolution);
|
|
59
|
+
}
|
|
60
|
+
if ('path' in target)
|
|
61
|
+
return localise(target.path, resolution);
|
|
62
|
+
const resolve = resolution.resolveEntryPath;
|
|
63
|
+
if (resolve === undefined) {
|
|
64
|
+
throw new CogentaError({
|
|
65
|
+
code: 'CONTENT_ROUTE_INVALID',
|
|
66
|
+
message: `This render context cannot resolve the URL of ${target.collection}/${target.id}.`,
|
|
67
|
+
hint: 'The host must supply resolveEntryPath when a theme links to entries.',
|
|
68
|
+
details: { collection: target.collection, id: target.id },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const path = resolve(target);
|
|
72
|
+
if (path === null) {
|
|
73
|
+
// A dangling internal link is a content problem the site owner can fix,
|
|
74
|
+
// and contract A wants it detectable. Emitting `#` instead would ship a
|
|
75
|
+
// broken page that nobody notices.
|
|
76
|
+
throw new CogentaError({
|
|
77
|
+
code: 'CONTENT_NOT_FOUND',
|
|
78
|
+
message: `Nothing to link to: ${target.collection}/${target.id} has no route.`,
|
|
79
|
+
hint: 'The entry was deleted, is not published in this locale, or its collection has no routing pattern.',
|
|
80
|
+
details: { collection: target.collection, id: target.id },
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return localise(path, resolution);
|
|
84
|
+
}
|
|
85
|
+
/** The default locale owns the bare path; every other locale is prefixed. */
|
|
86
|
+
function localise(path, resolution) {
|
|
87
|
+
const clean = path.startsWith('/') ? path : `/${path}`;
|
|
88
|
+
if (resolution.locale === resolution.defaultLocale)
|
|
89
|
+
return clean;
|
|
90
|
+
return clean === '/' ? `/${resolution.locale}` : `/${resolution.locale}${clean}`;
|
|
91
|
+
}
|
|
92
|
+
function buildImageSource(endpoint, media, options) {
|
|
93
|
+
const width = options.width ?? media.width;
|
|
94
|
+
if (width === undefined) {
|
|
95
|
+
throw new CogentaError({
|
|
96
|
+
code: 'CONTENT_INVALID',
|
|
97
|
+
message: `The media ${media.id} has no width, and none was asked for.`,
|
|
98
|
+
hint: 'Pass a width to ctx.image(), or re-scan the media so that its intrinsic size is stored. A width is required: an image without one shifts the layout while it loads.',
|
|
99
|
+
details: { media: media.id },
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const height = options.height ?? scaledHeight(media, width);
|
|
103
|
+
const candidates = SRCSET_WIDTHS.filter((candidate) => media.width === undefined || candidate <= media.width);
|
|
104
|
+
const widths = [...new Set([...candidates, width])].sort((a, b) => a - b);
|
|
105
|
+
// A video is not resized and has no responsive source set: `kind` is what
|
|
106
|
+
// lets a theme render <video> instead of a broken <img> (contract D,
|
|
107
|
+
// theme@1.1). Without it every video in a hero renders as a broken image.
|
|
108
|
+
const kind = media.kind === 'video' ? 'video' : 'image';
|
|
109
|
+
if (kind === 'video') {
|
|
110
|
+
return {
|
|
111
|
+
kind,
|
|
112
|
+
src: variantUrl(endpoint, media, width, options),
|
|
113
|
+
srcset: '',
|
|
114
|
+
width,
|
|
115
|
+
height,
|
|
116
|
+
alt: media.alt ?? '',
|
|
117
|
+
focal: media.focal ?? null,
|
|
118
|
+
...(media.poster === undefined ? {} : { poster: media.poster }),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
kind,
|
|
123
|
+
src: variantUrl(endpoint, media, width, options),
|
|
124
|
+
srcset: widths.map((w) => `${variantUrl(endpoint, media, w, options)} ${w}w`).join(', '),
|
|
125
|
+
width,
|
|
126
|
+
height,
|
|
127
|
+
alt: media.alt ?? '',
|
|
128
|
+
focal: media.focal ?? null,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function scaledHeight(media, width) {
|
|
132
|
+
if (media.width === undefined || media.height === undefined || media.width === 0) {
|
|
133
|
+
throw new CogentaError({
|
|
134
|
+
code: 'CONTENT_INVALID',
|
|
135
|
+
message: `The media ${media.id} has no intrinsic size, so no height can be derived.`,
|
|
136
|
+
hint: 'Pass width and height to ctx.image(), or re-scan the media so that its size is stored.',
|
|
137
|
+
details: { media: media.id },
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return Math.round((media.height / media.width) * width);
|
|
141
|
+
}
|
|
142
|
+
function variantUrl(endpoint, media, width, options) {
|
|
143
|
+
const parameters = new URLSearchParams({ id: media.id, w: String(width) });
|
|
144
|
+
if (options.height !== undefined)
|
|
145
|
+
parameters.set('h', String(options.height));
|
|
146
|
+
if (options.format !== undefined)
|
|
147
|
+
parameters.set('f', options.format);
|
|
148
|
+
if (options.fit !== undefined)
|
|
149
|
+
parameters.set('fit', options.fit);
|
|
150
|
+
return `${endpoint}?${parameters.toString()}`;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=render-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-context.js","sourceRoot":"","sources":["../../src/context/render-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAiC5C,uFAAuF;AACvF,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAC1D,MAAM,sBAAsB,GAAG,SAAS,CAAA;AAExC,MAAM,UAAU,mBAAmB,CAAC,OAA6B;IAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;IACzB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;IAC7B,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAA;IAC1F,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAA;IACvC,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,sBAAsB,CAAA;IAErE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,yBAAyB,MAAM,wBAAwB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAC1F,IAAI,EAAE,qEAAqE;YAC3E,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;SAC3C,CAAC,CAAA;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE;YACJ,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,aAAa,EAAE,IAAI,CAAC,aAAa;SAClC;QACD,MAAM;QACN,GAAG;QACH,OAAO,EAAE,OAAO,CAAC,OAAO;QAExB,CAAC,CAAC,GAAG,EAAE,MAAM;YACX,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAA;YACtF,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACxE,CAAC;QAED,KAAK,CAAC,KAAK,EAAE,YAAY;YACvB,OAAO,gBAAgB,CAAC,aAAa,EAAE,KAAK,EAAE,YAAY,IAAI,EAAE,CAAC,CAAA;QACnE,CAAC;QAED,IAAI,CAAC,MAAM;YACT,OAAO,WAAW,CAAC,MAAM,EAAE;gBACzB,MAAM;gBACN,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;aAC3C,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC;AAED,6FAA6F;AAC7F,SAAS,WAAW,CAAC,QAAgB,EAAE,MAAiD;IACtF,OAAO,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;QAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;QAC1B,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACpD,CAAC,CAAC,CAAA;AACJ,CAAC;AAUD,SAAS,WAAW,CAAC,MAAkB,EAAE,UAA0B;IACjE,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,yEAAyE;QACzE,oDAAoD;QACpD,IAAI,gCAAgC,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAA;QAChE,OAAO,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACrC,CAAC;IAED,IAAI,MAAM,IAAI,MAAM;QAAE,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;IAE9D,MAAM,OAAO,GAAG,UAAU,CAAC,gBAAgB,CAAA;IAC3C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,iDAAiD,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,EAAE,GAAG;YAC3F,IAAI,EAAE,sEAAsE;YAC5E,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;SAC1D,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC5B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,wEAAwE;QACxE,wEAAwE;QACxE,mCAAmC;QACnC,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,mBAAmB;YACzB,OAAO,EAAE,uBAAuB,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,EAAE,gBAAgB;YAC9E,IAAI,EAAE,mGAAmG;YACzG,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;SAC1D,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;AACnC,CAAC;AAED,6EAA6E;AAC7E,SAAS,QAAQ,CAAC,IAAY,EAAE,UAA0B;IACxD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAA;IACtD,IAAI,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,aAAa;QAAE,OAAO,KAAK,CAAA;IAChE,OAAO,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,KAAK,EAAE,CAAA;AAClF,CAAC;AAED,SAAS,gBAAgB,CACvB,QAAgB,EAChB,KAAqB,EACrB,OAAqB;IAErB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAA;IAC1C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,aAAa,KAAK,CAAC,EAAE,wCAAwC;YACtE,IAAI,EAAE,qKAAqK;YAC3K,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE;SAC7B,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IAC3D,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CACrC,CAAC,SAAS,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,SAAS,IAAI,KAAK,CAAC,KAAK,CACrE,CAAA;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IAEzE,0EAA0E;IAC1E,qEAAqE;IACrE,0EAA0E;IAC1E,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;IAEvD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO;YACL,IAAI;YACJ,GAAG,EAAE,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC;YAChD,MAAM,EAAE,EAAE;YACV,KAAK;YACL,MAAM;YACN,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,EAAE;YACpB,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI;YAC1B,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;SAChE,CAAA;IACH,CAAC;IAED,OAAO;QACL,IAAI;QACJ,GAAG,EAAE,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC;QAChD,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACxF,KAAK;QACL,MAAM;QACN,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,EAAE;QACpB,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI;KAC3B,CAAA;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAAqB,EAAE,KAAa;IACxD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;QACjF,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,aAAa,KAAK,CAAC,EAAE,sDAAsD;YACpF,IAAI,EAAE,wFAAwF;YAC9F,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE;SAC7B,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAA;AACzD,CAAC;AAED,SAAS,UAAU,CACjB,QAAgB,EAChB,KAAqB,EACrB,KAAa,EACb,OAAqB;IAErB,MAAM,UAAU,GAAG,IAAI,eAAe,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC1E,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;QAAE,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IAC7E,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;QAAE,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACrE,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS;QAAE,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;IACjE,OAAO,GAAG,QAAQ,IAAI,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAA;AAC/C,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ContentClient, MediaReference } from '../content/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Contract D's `RenderContext`, frozen at `theme@1.0`.
|
|
4
|
+
*
|
|
5
|
+
* This declaration is the contract, transcribed. Adding an entry is a minor
|
|
6
|
+
* version; changing one is major. Nothing is added here "for later": what a
|
|
7
|
+
* theme can reach is what the sandbox has to defend, so the smallest surface
|
|
8
|
+
* that renders a site is the right one.
|
|
9
|
+
*
|
|
10
|
+
* Notably absent, and absent on purpose: the database, the configuration, the
|
|
11
|
+
* secrets, the filesystem, the logger, the request headers.
|
|
12
|
+
*/
|
|
13
|
+
export interface RenderContext {
|
|
14
|
+
readonly site: {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly url: string;
|
|
17
|
+
readonly locales: readonly string[];
|
|
18
|
+
readonly defaultLocale: string;
|
|
19
|
+
};
|
|
20
|
+
/** The locale being rendered. */
|
|
21
|
+
readonly locale: string;
|
|
22
|
+
/** The URL being rendered, already resolved. */
|
|
23
|
+
readonly url: URL;
|
|
24
|
+
/** Translation. An unknown key returns the key, never an empty string. */
|
|
25
|
+
t(key: string, values?: Readonly<Record<string, string | number>>): string;
|
|
26
|
+
/** Image variants. Returns what a responsive `<img>` needs, nothing more. */
|
|
27
|
+
image(media: MediaReference, options?: ImageOptions): ImageSource;
|
|
28
|
+
/** URL of an entry, of a path, or of an external target. Locale-aware. */
|
|
29
|
+
link(target: LinkTarget): string;
|
|
30
|
+
/** Read-only content access. The only door to data a theme has. */
|
|
31
|
+
readonly content: ContentClient;
|
|
32
|
+
}
|
|
33
|
+
export type LinkTarget = {
|
|
34
|
+
collection: string;
|
|
35
|
+
id: string;
|
|
36
|
+
} | {
|
|
37
|
+
path: string;
|
|
38
|
+
} | string;
|
|
39
|
+
/**
|
|
40
|
+
* Contract D's image types live with the pipeline that produces them.
|
|
41
|
+
*
|
|
42
|
+
* They were briefly declared here as well, at `theme@1.0` — without `kind`,
|
|
43
|
+
* which `theme@1.1` added so a theme can tell an image from a video. Two
|
|
44
|
+
* declarations of a frozen type is how one of them silently falls behind, and
|
|
45
|
+
* this one had already done so.
|
|
46
|
+
*/
|
|
47
|
+
import type { ImageOptions, ImageSource } from '../images/types.js';
|
|
48
|
+
export type { ImageOptions, ImageSource };
|
|
49
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/context/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAExE;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;QACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;QACpB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;QACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;KAC/B,CAAA;IACD,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,gDAAgD;IAChD,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;IAEjB,0EAA0E;IAC1E,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAA;IAE1E,6EAA6E;IAC7E,KAAK,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,WAAW,CAAA;IAEjE,0EAA0E;IAC1E,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAAA;IAEhC,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;CAChC;AAED,MAAM,MAAM,UAAU,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAA;AAEvF;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEnE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/context/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CogentaError } from '@cogenta/core';
|
|
2
|
+
/**
|
|
3
|
+
* Every failure the pipeline can produce, in one place.
|
|
4
|
+
*
|
|
5
|
+
* Both tiers raise the *same* error for the same cause, because a caller that
|
|
6
|
+
* branches on `code` must not have to know which tier is installed. libvips
|
|
7
|
+
* reports a decode failure as a WebAssembly exception in one tier and as a
|
|
8
|
+
* plain `Error` in the other; neither shape reaches the caller.
|
|
9
|
+
*
|
|
10
|
+
* There is no `IMAGE_*` entry in `ERROR_CODES` yet — adding one means touching
|
|
11
|
+
* `@cogenta/core`, which is a separate change — so these reuse the existing
|
|
12
|
+
* codes with the meanings they already carry: the input is at fault
|
|
13
|
+
* (`CONTENT_INVALID`), or the pipeline is (`INTERNAL`).
|
|
14
|
+
*/
|
|
15
|
+
export declare function unsupportedFormatError(bytes: Uint8Array, mediaId: string): CogentaError;
|
|
16
|
+
export declare function decodeFailedError(mediaId: string, cause: unknown): CogentaError;
|
|
17
|
+
export declare function encodeFailedError(mediaId: string, format: string, driver: string, cause: unknown): CogentaError;
|
|
18
|
+
export declare function invalidOptionError(message: string, details: Record<string, unknown>): CogentaError;
|
|
19
|
+
export declare function missingSizeError(mediaId: string): CogentaError;
|
|
20
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/images/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAG5C;;;;;;;;;;;;GAYG;AAEH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAOvF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAQ/E;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,GACb,YAAY,CAQd;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,YAAY,CAOd;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAO9D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { CogentaError } from '@cogenta/core';
|
|
2
|
+
import { describeContainer } from './format.js';
|
|
3
|
+
/**
|
|
4
|
+
* Every failure the pipeline can produce, in one place.
|
|
5
|
+
*
|
|
6
|
+
* Both tiers raise the *same* error for the same cause, because a caller that
|
|
7
|
+
* branches on `code` must not have to know which tier is installed. libvips
|
|
8
|
+
* reports a decode failure as a WebAssembly exception in one tier and as a
|
|
9
|
+
* plain `Error` in the other; neither shape reaches the caller.
|
|
10
|
+
*
|
|
11
|
+
* There is no `IMAGE_*` entry in `ERROR_CODES` yet — adding one means touching
|
|
12
|
+
* `@cogenta/core`, which is a separate change — so these reuse the existing
|
|
13
|
+
* codes with the meanings they already carry: the input is at fault
|
|
14
|
+
* (`CONTENT_INVALID`), or the pipeline is (`INTERNAL`).
|
|
15
|
+
*/
|
|
16
|
+
export function unsupportedFormatError(bytes, mediaId) {
|
|
17
|
+
return new CogentaError({
|
|
18
|
+
code: 'CONTENT_INVALID',
|
|
19
|
+
message: `The media ${mediaId} is ${describeContainer(bytes)}, which Cogenta cannot resize.`,
|
|
20
|
+
hint: 'Upload the image as AVIF, WebP, JPEG or PNG. Animated GIFs and SVGs are served untouched instead of being resized — do not send them through ctx.image().',
|
|
21
|
+
details: { media: mediaId },
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function decodeFailedError(mediaId, cause) {
|
|
25
|
+
return new CogentaError({
|
|
26
|
+
code: 'CONTENT_INVALID',
|
|
27
|
+
message: `The media ${mediaId} announces a format Cogenta supports but could not be decoded.`,
|
|
28
|
+
hint: 'The file is truncated or corrupted. Re-upload it, and check the storage driver if this happens to more than one image.',
|
|
29
|
+
cause,
|
|
30
|
+
details: { media: mediaId },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function encodeFailedError(mediaId, format, driver, cause) {
|
|
34
|
+
return new CogentaError({
|
|
35
|
+
code: 'INTERNAL',
|
|
36
|
+
message: `The ${driver} image driver could not encode ${mediaId} as ${format}.`,
|
|
37
|
+
hint: 'Ask for a different format — every build of libvips can write JPEG, PNG and WebP, while AVIF depends on how the codec was built. `cogenta doctor` reports which driver is running.',
|
|
38
|
+
cause,
|
|
39
|
+
details: { media: mediaId, format, driver },
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export function invalidOptionError(message, details) {
|
|
43
|
+
return new CogentaError({
|
|
44
|
+
code: 'CONTENT_INVALID',
|
|
45
|
+
message,
|
|
46
|
+
hint: 'Fix the arguments passed to ctx.image(), or the query string of the /_image request that produced them.',
|
|
47
|
+
details,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export function missingSizeError(mediaId) {
|
|
51
|
+
return new CogentaError({
|
|
52
|
+
code: 'CONTENT_INVALID',
|
|
53
|
+
message: `The media ${mediaId} has no intrinsic size, and none was asked for.`,
|
|
54
|
+
hint: 'Pass width and height to ctx.image(), or re-scan the media so that its size is stored. A size is required: an image without one shifts the layout while it loads.',
|
|
55
|
+
details: { media: mediaId },
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/images/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAE/C;;;;;;;;;;;;GAYG;AAEH,MAAM,UAAU,sBAAsB,CAAC,KAAiB,EAAE,OAAe;IACvE,OAAO,IAAI,YAAY,CAAC;QACtB,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,aAAa,OAAO,OAAO,iBAAiB,CAAC,KAAK,CAAC,gCAAgC;QAC5F,IAAI,EAAE,2JAA2J;QACjK,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;KAC5B,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,KAAc;IAC/D,OAAO,IAAI,YAAY,CAAC;QACtB,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,aAAa,OAAO,gEAAgE;QAC7F,IAAI,EAAE,wHAAwH;QAC9H,KAAK;QACL,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;KAC5B,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,OAAe,EACf,MAAc,EACd,MAAc,EACd,KAAc;IAEd,OAAO,IAAI,YAAY,CAAC;QACtB,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,OAAO,MAAM,kCAAkC,OAAO,OAAO,MAAM,GAAG;QAC/E,IAAI,EAAE,oLAAoL;QAC1L,KAAK;QACL,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE;KAC5C,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,OAAe,EACf,OAAgC;IAEhC,OAAO,IAAI,YAAY,CAAC;QACtB,IAAI,EAAE,iBAAiB;QACvB,OAAO;QACP,IAAI,EAAE,yGAAyG;QAC/G,OAAO;KACR,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,IAAI,YAAY,CAAC;QACtB,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,aAAa,OAAO,iDAAiD;QAC9E,IAAI,EAAE,mKAAmK;QACzK,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;KAC5B,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describeContainer, sniffImageFormat } from '@cogenta/core';
|
|
2
|
+
import type { ImageFormat } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Container sniffing itself now lives in `@cogenta/core` — the media upload
|
|
5
|
+
* route (`@cogenta/api`) needs the exact same magic-byte sniff, and depending
|
|
6
|
+
* on this package for four byte-signature checks would pull in Astro/sharp
|
|
7
|
+
* for no reason. This file re-exports it so the pipeline's own call sites
|
|
8
|
+
* are unchanged, and keeps the output-format concerns (MIME type, file
|
|
9
|
+
* suffix) that only the pipeline needs.
|
|
10
|
+
*
|
|
11
|
+
* `SniffedImageFormat` (core) and `ImageFormat` (here) are the same literal
|
|
12
|
+
* union by construction; re-exporting keeps one canonical implementation
|
|
13
|
+
* without forcing every caller through the same type alias.
|
|
14
|
+
*/
|
|
15
|
+
export { describeContainer, sniffImageFormat };
|
|
16
|
+
export declare function contentTypeOf(format: ImageFormat): string;
|
|
17
|
+
export declare function suffixOf(format: ImageFormat): string;
|
|
18
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/images/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAA;AAiB9C,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEpD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describeContainer, sniffImageFormat } from '@cogenta/core';
|
|
2
|
+
/**
|
|
3
|
+
* Container sniffing itself now lives in `@cogenta/core` — the media upload
|
|
4
|
+
* route (`@cogenta/api`) needs the exact same magic-byte sniff, and depending
|
|
5
|
+
* on this package for four byte-signature checks would pull in Astro/sharp
|
|
6
|
+
* for no reason. This file re-exports it so the pipeline's own call sites
|
|
7
|
+
* are unchanged, and keeps the output-format concerns (MIME type, file
|
|
8
|
+
* suffix) that only the pipeline needs.
|
|
9
|
+
*
|
|
10
|
+
* `SniffedImageFormat` (core) and `ImageFormat` (here) are the same literal
|
|
11
|
+
* union by construction; re-exporting keeps one canonical implementation
|
|
12
|
+
* without forcing every caller through the same type alias.
|
|
13
|
+
*/
|
|
14
|
+
export { describeContainer, sniffImageFormat };
|
|
15
|
+
const MIME = {
|
|
16
|
+
avif: 'image/avif',
|
|
17
|
+
webp: 'image/webp',
|
|
18
|
+
jpeg: 'image/jpeg',
|
|
19
|
+
png: 'image/png',
|
|
20
|
+
};
|
|
21
|
+
/** Suffix libvips and sharp both understand for an output format. */
|
|
22
|
+
const SUFFIX = {
|
|
23
|
+
avif: '.avif',
|
|
24
|
+
webp: '.webp',
|
|
25
|
+
jpeg: '.jpg',
|
|
26
|
+
png: '.png',
|
|
27
|
+
};
|
|
28
|
+
export function contentTypeOf(format) {
|
|
29
|
+
return MIME[format];
|
|
30
|
+
}
|
|
31
|
+
export function suffixOf(format) {
|
|
32
|
+
return SUFFIX[format];
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/images/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAGnE;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAA;AAE9C,MAAM,IAAI,GAA0C;IAClD,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;CACjB,CAAA;AAED,qEAAqE;AACrE,MAAM,MAAM,GAA0C;IACpD,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,MAAM;CACZ,CAAA;AAED,MAAM,UAAU,aAAa,CAAC,MAAmB;IAC/C,OAAO,IAAI,CAAC,MAAM,CAAC,CAAA;AACrB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAmB;IAC1C,OAAO,MAAM,CAAC,MAAM,CAAC,CAAA;AACvB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { FocalPoint, ImageFit, ImageFormat, ImageMetadata, Rect, Size, TransformOperation } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Everything geometric, decided here and only here.
|
|
4
|
+
*
|
|
5
|
+
* Both tiers receive a `TransformOperation` with the crop rectangle and the
|
|
6
|
+
* output size already in pixels. Neither is asked to interpret `cover`, a focal
|
|
7
|
+
* point, or "what should the height be" — those answers must not depend on which
|
|
8
|
+
* codec a host managed to install.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The largest side the pipeline will produce.
|
|
12
|
+
*
|
|
13
|
+
* `/_image?w=…` is a public URL: without a ceiling, one request for a 40000px
|
|
14
|
+
* variant is a memory exhaustion, and a loop over widths is a cache-filling
|
|
15
|
+
* attack. 8192 is generous for a 5K display at 2x.
|
|
16
|
+
*/
|
|
17
|
+
export declare const MAX_DIMENSION = 8192;
|
|
18
|
+
export declare const DEFAULT_QUALITY = 80;
|
|
19
|
+
export interface VariantRequest {
|
|
20
|
+
readonly width?: number | undefined;
|
|
21
|
+
readonly height?: number | undefined;
|
|
22
|
+
readonly format?: ImageFormat | undefined;
|
|
23
|
+
readonly fit?: ImageFit | undefined;
|
|
24
|
+
/** From the media entity, never from the theme: contract D is explicit. */
|
|
25
|
+
readonly focal?: FocalPoint | null | undefined;
|
|
26
|
+
readonly quality?: number | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The crop a `cover` fit implies, centred on the focal point.
|
|
30
|
+
*
|
|
31
|
+
* The focal point is *kept in frame*, not centred: pushing it to the middle
|
|
32
|
+
* would slide the crop off the image for a face near an edge, and clamping the
|
|
33
|
+
* result is what stops that. A missing focal point means the middle, which is
|
|
34
|
+
* what every naive implementation does and what this one falls back to.
|
|
35
|
+
*/
|
|
36
|
+
export declare function focalCrop(source: Size, target: Size, focal: FocalPoint | null): Rect;
|
|
37
|
+
/**
|
|
38
|
+
* Turns a request into an operation, or refuses it.
|
|
39
|
+
*
|
|
40
|
+
* Never upscales. A variant larger than its source costs bytes and buys nothing
|
|
41
|
+
* but blur, and a `srcset` that offers one makes the browser download it.
|
|
42
|
+
*/
|
|
43
|
+
export declare function planTransform(source: ImageMetadata, request: VariantRequest): TransformOperation;
|
|
44
|
+
/** The size the operation produces, without touching a pixel. */
|
|
45
|
+
export declare function outputSize(source: ImageMetadata, operation: TransformOperation): Size;
|
|
46
|
+
//# sourceMappingURL=geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../src/images/geometry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,WAAW,EACX,aAAa,EACb,IAAI,EACJ,IAAI,EACJ,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAEnB;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,OAAO,CAAA;AAEjC,eAAO,MAAM,eAAe,KAAK,CAAA;AAEjC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IACzC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;IACnC,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,CAAA;IAC9C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC;AAwCD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAoBpF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,GAAG,kBAAkB,CAqChG;AAgBD,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,kBAAkB,GAAG,IAAI,CAIrF"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { invalidOptionError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Everything geometric, decided here and only here.
|
|
4
|
+
*
|
|
5
|
+
* Both tiers receive a `TransformOperation` with the crop rectangle and the
|
|
6
|
+
* output size already in pixels. Neither is asked to interpret `cover`, a focal
|
|
7
|
+
* point, or "what should the height be" — those answers must not depend on which
|
|
8
|
+
* codec a host managed to install.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The largest side the pipeline will produce.
|
|
12
|
+
*
|
|
13
|
+
* `/_image?w=…` is a public URL: without a ceiling, one request for a 40000px
|
|
14
|
+
* variant is a memory exhaustion, and a loop over widths is a cache-filling
|
|
15
|
+
* attack. 8192 is generous for a 5K display at 2x.
|
|
16
|
+
*/
|
|
17
|
+
export const MAX_DIMENSION = 8192;
|
|
18
|
+
export const DEFAULT_QUALITY = 80;
|
|
19
|
+
function assertDimension(value, name) {
|
|
20
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value < 1) {
|
|
21
|
+
throw invalidOptionError(`An image ${name} must be a whole number of pixels, not ${value}.`, {
|
|
22
|
+
[name]: value,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
if (value > MAX_DIMENSION) {
|
|
26
|
+
throw invalidOptionError(`An image ${name} of ${value}px is above the ${MAX_DIMENSION}px Cogenta will render.`, { [name]: value, max: MAX_DIMENSION });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function assertFocal(focal) {
|
|
30
|
+
const outside = [focal.x, focal.y].some((value) => !Number.isFinite(value) || value < 0 || value > 1);
|
|
31
|
+
if (outside) {
|
|
32
|
+
throw invalidOptionError(`A focal point is expressed as fractions of the image, between 0 and 1 — got (${focal.x}, ${focal.y}).`, { focal });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function clampQuality(quality) {
|
|
36
|
+
if (quality === undefined)
|
|
37
|
+
return DEFAULT_QUALITY;
|
|
38
|
+
if (!Number.isFinite(quality) || quality < 1 || quality > 100) {
|
|
39
|
+
throw invalidOptionError(`An encoder quality must be between 1 and 100, not ${quality}.`, {
|
|
40
|
+
quality,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return Math.round(quality);
|
|
44
|
+
}
|
|
45
|
+
const atLeastOne = (value) => Math.max(1, Math.round(value));
|
|
46
|
+
/**
|
|
47
|
+
* The crop a `cover` fit implies, centred on the focal point.
|
|
48
|
+
*
|
|
49
|
+
* The focal point is *kept in frame*, not centred: pushing it to the middle
|
|
50
|
+
* would slide the crop off the image for a face near an edge, and clamping the
|
|
51
|
+
* result is what stops that. A missing focal point means the middle, which is
|
|
52
|
+
* what every naive implementation does and what this one falls back to.
|
|
53
|
+
*/
|
|
54
|
+
export function focalCrop(source, target, focal) {
|
|
55
|
+
const targetRatio = target.width / target.height;
|
|
56
|
+
const sourceRatio = source.width / source.height;
|
|
57
|
+
const width = sourceRatio > targetRatio ? atLeastOne(source.height * targetRatio) : source.width;
|
|
58
|
+
const height = sourceRatio > targetRatio ? source.height : atLeastOne(source.width / targetRatio);
|
|
59
|
+
const cropWidth = Math.min(width, source.width);
|
|
60
|
+
const cropHeight = Math.min(height, source.height);
|
|
61
|
+
const point = focal ?? { x: 0.5, y: 0.5 };
|
|
62
|
+
const clamp = (value, span, extent) => Math.max(0, Math.min(extent - span, Math.round(value)));
|
|
63
|
+
return {
|
|
64
|
+
left: clamp(point.x * source.width - cropWidth / 2, cropWidth, source.width),
|
|
65
|
+
top: clamp(point.y * source.height - cropHeight / 2, cropHeight, source.height),
|
|
66
|
+
width: cropWidth,
|
|
67
|
+
height: cropHeight,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Turns a request into an operation, or refuses it.
|
|
72
|
+
*
|
|
73
|
+
* Never upscales. A variant larger than its source costs bytes and buys nothing
|
|
74
|
+
* but blur, and a `srcset` that offers one makes the browser download it.
|
|
75
|
+
*/
|
|
76
|
+
export function planTransform(source, request) {
|
|
77
|
+
const format = request.format ?? source.format;
|
|
78
|
+
const quality = clampQuality(request.quality);
|
|
79
|
+
const focal = request.focal ?? null;
|
|
80
|
+
if (focal !== null)
|
|
81
|
+
assertFocal(focal);
|
|
82
|
+
if (request.width !== undefined)
|
|
83
|
+
assertDimension(request.width, 'width');
|
|
84
|
+
if (request.height !== undefined)
|
|
85
|
+
assertDimension(request.height, 'height');
|
|
86
|
+
const identity = { crop: null, resize: null, format, quality };
|
|
87
|
+
if (request.width === undefined && request.height === undefined)
|
|
88
|
+
return identity;
|
|
89
|
+
if (request.width !== undefined && request.height !== undefined) {
|
|
90
|
+
if ((request.fit ?? 'cover') === 'contain') {
|
|
91
|
+
// `contain` never crops: the whole image survives, and the caller gets
|
|
92
|
+
// back the size it actually got rather than the box it asked for.
|
|
93
|
+
const scale = Math.min(request.width / source.width, request.height / source.height, 1);
|
|
94
|
+
return withResize(source, scaleOf(source, scale), format, quality);
|
|
95
|
+
}
|
|
96
|
+
const crop = focalCrop(source, { width: request.width, height: request.height }, focal);
|
|
97
|
+
const scale = Math.min(request.width / crop.width, 1);
|
|
98
|
+
const resize = scaleOf(crop, scale);
|
|
99
|
+
const cropIsWhole = crop.width === source.width && crop.height === source.height;
|
|
100
|
+
return {
|
|
101
|
+
crop: cropIsWhole ? null : crop,
|
|
102
|
+
resize: resize.width === crop.width && resize.height === crop.height ? null : resize,
|
|
103
|
+
format,
|
|
104
|
+
quality,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const scale = request.width === undefined
|
|
108
|
+
? Math.min((request.height ?? source.height) / source.height, 1)
|
|
109
|
+
: Math.min(request.width / source.width, 1);
|
|
110
|
+
return withResize(source, scaleOf(source, scale), format, quality);
|
|
111
|
+
}
|
|
112
|
+
function scaleOf(size, scale) {
|
|
113
|
+
return { width: atLeastOne(size.width * scale), height: atLeastOne(size.height * scale) };
|
|
114
|
+
}
|
|
115
|
+
function withResize(source, resize, format, quality) {
|
|
116
|
+
const unchanged = resize.width === source.width && resize.height === source.height;
|
|
117
|
+
return { crop: null, resize: unchanged ? null : resize, format, quality };
|
|
118
|
+
}
|
|
119
|
+
/** The size the operation produces, without touching a pixel. */
|
|
120
|
+
export function outputSize(source, operation) {
|
|
121
|
+
if (operation.resize !== null)
|
|
122
|
+
return operation.resize;
|
|
123
|
+
if (operation.crop !== null)
|
|
124
|
+
return { width: operation.crop.width, height: operation.crop.height };
|
|
125
|
+
return { width: source.width, height: source.height };
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.js","sourceRoot":"","sources":["../../src/images/geometry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type DriverRegistry, type Logger } from '@cogenta/core';
|
|
2
|
+
import type { ImageConfig, ImageTransformer } from './types.js';
|
|
3
|
+
export { decodeFailedError, unsupportedFormatError } from './errors.js';
|
|
4
|
+
export { contentTypeOf, describeContainer, sniffImageFormat, suffixOf } from './format.js';
|
|
5
|
+
export type { VariantRequest } from './geometry.js';
|
|
6
|
+
export { DEFAULT_QUALITY, focalCrop, MAX_DIMENSION, outputSize, planTransform } from './geometry.js';
|
|
7
|
+
export type { ImagePipeline, ImagePipelineOptions, VariantParameters } from './pipeline.js';
|
|
8
|
+
export { createImagePipeline, parseVariantParameters, variantKey } from './pipeline.js';
|
|
9
|
+
export { createSharpTransformer, loadSharpModule, sharpImageDriver } from './sharp.js';
|
|
10
|
+
export type { SourceOptions } from './srcset.js';
|
|
11
|
+
export { candidateWidths, describeMedia, SRCSET_WIDTHS, variantUrl } from './srcset.js';
|
|
12
|
+
export type { MemoryVariantStoreOptions, VariantStore } from './store.js';
|
|
13
|
+
export { createMemoryVariantStore } from './store.js';
|
|
14
|
+
export type { FocalPoint, ImageConfig, ImageFit, ImageFormat, ImageMetadata, ImageOptions, ImageSource, ImageTransformer, MediaAsset, Rect, RenderedVariant, Size, TransformOperation, } from './types.js';
|
|
15
|
+
export { IMAGE_FORMATS } from './types.js';
|
|
16
|
+
export { createWasmTransformer, loadVips, wasmImageDriver } from './wasm.js';
|
|
17
|
+
export interface ImageRegistryOptions {
|
|
18
|
+
readonly logger?: Logger;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The image drivers Cogenta ships, in tier order.
|
|
22
|
+
*
|
|
23
|
+
* `sharp` first when it is installed, WebAssembly libvips otherwise — and the
|
|
24
|
+
* fallback is not a courtesy. Rule R10 and the L3 spec both say it plainly:
|
|
25
|
+
* `sharp` does not install everywhere, so the tier that always works is the one
|
|
26
|
+
* the default install gets.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createImageRegistry(options?: ImageRegistryOptions): DriverRegistry<ImageTransformer, ImageConfig>;
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/images/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAA;AAEtF,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAG/D,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACvE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAC1F,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AACpG,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAC3F,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AACvF,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACtF,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACvF,YAAY,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AACrD,YAAY,EACV,UAAU,EACV,WAAW,EACX,QAAQ,EACR,WAAW,EACX,aAAa,EACb,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,UAAU,EACV,IAAI,EACJ,eAAe,EACf,IAAI,EACJ,kBAAkB,GACnB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAE5E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,oBAAyB,GACjC,cAAc,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAU/C"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createDriverRegistry } from '@cogenta/core';
|
|
2
|
+
import { sharpImageDriver } from './sharp.js';
|
|
3
|
+
import { wasmImageDriver } from './wasm.js';
|
|
4
|
+
export { decodeFailedError, unsupportedFormatError } from './errors.js';
|
|
5
|
+
export { contentTypeOf, describeContainer, sniffImageFormat, suffixOf } from './format.js';
|
|
6
|
+
export { DEFAULT_QUALITY, focalCrop, MAX_DIMENSION, outputSize, planTransform } from './geometry.js';
|
|
7
|
+
export { createImagePipeline, parseVariantParameters, variantKey } from './pipeline.js';
|
|
8
|
+
export { createSharpTransformer, loadSharpModule, sharpImageDriver } from './sharp.js';
|
|
9
|
+
export { candidateWidths, describeMedia, SRCSET_WIDTHS, variantUrl } from './srcset.js';
|
|
10
|
+
export { createMemoryVariantStore } from './store.js';
|
|
11
|
+
export { IMAGE_FORMATS } from './types.js';
|
|
12
|
+
export { createWasmTransformer, loadVips, wasmImageDriver } from './wasm.js';
|
|
13
|
+
/**
|
|
14
|
+
* The image drivers Cogenta ships, in tier order.
|
|
15
|
+
*
|
|
16
|
+
* `sharp` first when it is installed, WebAssembly libvips otherwise — and the
|
|
17
|
+
* fallback is not a courtesy. Rule R10 and the L3 spec both say it plainly:
|
|
18
|
+
* `sharp` does not install everywhere, so the tier that always works is the one
|
|
19
|
+
* the default install gets.
|
|
20
|
+
*/
|
|
21
|
+
export function createImageRegistry(options = {}) {
|
|
22
|
+
const registry = createDriverRegistry({
|
|
23
|
+
need: 'images',
|
|
24
|
+
...(options.logger === undefined ? {} : { logger: options.logger }),
|
|
25
|
+
});
|
|
26
|
+
registry.register(sharpImageDriver());
|
|
27
|
+
registry.register(wasmImageDriver());
|
|
28
|
+
return registry;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=index.js.map
|