@rathnasgala2/template 2.0.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/LICENSE +201 -0
- package/README.md +515 -0
- package/contracts/render-policy.jcs +1 -0
- package/contracts/theme-styling-contract.jcs +1 -0
- package/package.json +96 -0
- package/src/core/errors.js +136 -0
- package/src/core/index.js +758 -0
- package/src/core/internal/appearance/bootstrap-script.js +159 -0
- package/src/core/internal/appearance/contract.js +193 -0
- package/src/core/internal/appearance/controller-markup.js +80 -0
- package/src/core/internal/appearance/styling-contract.js +509 -0
- package/src/core/internal/canonical-jcs.js +0 -0
- package/src/core/internal/content-security.js +0 -0
- package/src/core/internal/eleventy-render.js +220 -0
- package/src/core/internal/feeds.js +161 -0
- package/src/core/internal/fs-walk.js +38 -0
- package/src/core/internal/media/avif-probe.js +169 -0
- package/src/core/internal/media/font-policy.js +56 -0
- package/src/core/internal/media/jpeg-codec.js +0 -0
- package/src/core/internal/media/limits.js +129 -0
- package/src/core/internal/media/pipeline.js +353 -0
- package/src/core/internal/media/png-codec.js +406 -0
- package/src/core/internal/media/resize.js +147 -0
- package/src/core/internal/media/sniff.js +109 -0
- package/src/core/internal/media/webp-probe.js +139 -0
- package/src/core/internal/messages.js +78 -0
- package/src/core/internal/page-kinds.js +709 -0
- package/src/core/internal/provenance.js +49 -0
- package/src/core/internal/render-policy-content.js +295 -0
- package/src/core/internal/route-labels.js +68 -0
- package/src/core/internal/route.js +167 -0
- package/src/core/internal/search-index.js +119 -0
- package/src/core/internal/seo.js +114 -0
- package/src/core/internal/sitemap.js +77 -0
- package/src/core/internal/skeleton.js +413 -0
- package/src/core/internal/source-inventory.js +145 -0
- package/src/core/internal/text-direction.js +59 -0
- package/src/core/internal/theme-assets.js +387 -0
- package/src/core/internal/uuid.js +51 -0
- package/src/core/manifest.js +0 -0
- package/types/eleventy-plugin-syntaxhighlight.d.ts +26 -0
- package/types/eleventy.d.ts +38 -0
- package/types/index.d.ts +347 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded WebP container validation and declared-dimension extraction.
|
|
3
|
+
*
|
|
4
|
+
* Scope decision (documented, not silently assumed): this module validates
|
|
5
|
+
* the `RIFF`/`WEBP` container framing and the `VP8 `/`VP8L` chunk header
|
|
6
|
+
* closely enough to reject a malformed or oversized file and to read the
|
|
7
|
+
* format's own declared width/height, but it does not run a full VP8/VP8L
|
|
8
|
+
* entropy decoder — DEC-097's own closed `gala-theme-binary-assets-v2`
|
|
9
|
+
* profile requires a complete bounded VP8/VP8L bitstream decode for
|
|
10
|
+
* *passive theme assets*; hand-authoring an equivalent, independently
|
|
11
|
+
* verifiable decoder for *author media* was judged out of proportion to
|
|
12
|
+
* this task's scope and is flagged to the orchestrator as a follow-up
|
|
13
|
+
* rather than attempted partially. What this module still enforces, fully:
|
|
14
|
+
* exact container framing, a single recognized image chunk (any `VP8X`,
|
|
15
|
+
* animation, or a second image chunk rejects), and the shared
|
|
16
|
+
* dimension/pixel resource ceilings applied to the format's own declared
|
|
17
|
+
* size fields before any further processing — so a WebP cannot be used to
|
|
18
|
+
* request an over-large derivative or to smuggle a second payload past this
|
|
19
|
+
* pipeline. Because pixel data is never decoded, WebP is admitted as a
|
|
20
|
+
* *source* reference (recorded, digest-verified, dimension-bounded) but no
|
|
21
|
+
* responsive derivative is generated for it; the original bytes pass
|
|
22
|
+
* through as the sole asset entry.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { MediaPipelineError } from '../../errors.js';
|
|
26
|
+
import { assertDimensionsWithinCeiling } from './limits.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validate a WebP container and return its declared dimensions.
|
|
30
|
+
*
|
|
31
|
+
* @param {Buffer} bytes complete WebP file bytes
|
|
32
|
+
* @param {string} referencePath the source path, for error messages
|
|
33
|
+
* @returns {{width: number, height: number}} the declared dimensions
|
|
34
|
+
*/
|
|
35
|
+
export function probeWebp(bytes, referencePath) {
|
|
36
|
+
if (
|
|
37
|
+
bytes.length < 20 ||
|
|
38
|
+
bytes.subarray(0, 4).toString('ascii') !== 'RIFF' ||
|
|
39
|
+
bytes.subarray(8, 12).toString('ascii') !== 'WEBP'
|
|
40
|
+
) {
|
|
41
|
+
throw new MediaPipelineError(
|
|
42
|
+
'MEDIA_FORMAT_INVALID',
|
|
43
|
+
'missing RIFF/WEBP container signature',
|
|
44
|
+
referencePath,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const riffSize = bytes.readUInt32LE(4);
|
|
48
|
+
if (riffSize !== bytes.length - 8) {
|
|
49
|
+
throw new MediaPipelineError(
|
|
50
|
+
'MEDIA_FORMAT_INVALID',
|
|
51
|
+
'RIFF size does not equal the member length minus eight',
|
|
52
|
+
referencePath,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const fourCc = bytes.subarray(12, 16).toString('ascii');
|
|
56
|
+
const chunkSize = bytes.readUInt32LE(16);
|
|
57
|
+
const payloadStart = 20;
|
|
58
|
+
const payloadEnd = payloadStart + chunkSize;
|
|
59
|
+
if (payloadEnd > bytes.length) {
|
|
60
|
+
throw new MediaPipelineError(
|
|
61
|
+
'MEDIA_FORMAT_INVALID',
|
|
62
|
+
'WebP image chunk exceeds the container length',
|
|
63
|
+
referencePath,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
// No second chunk is admitted: the exact end of the declared image chunk
|
|
67
|
+
// (padded to an even offset) must be the exact end of the file.
|
|
68
|
+
const paddedEnd = payloadEnd + (chunkSize % 2);
|
|
69
|
+
if (paddedEnd !== bytes.length) {
|
|
70
|
+
throw new MediaPipelineError(
|
|
71
|
+
'MEDIA_FORMAT_INVALID',
|
|
72
|
+
'WebP container has a second chunk or trailing bytes; only one image chunk is admitted',
|
|
73
|
+
referencePath,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let width;
|
|
78
|
+
let height;
|
|
79
|
+
if (fourCc === 'VP8 ') {
|
|
80
|
+
if (chunkSize < 10) {
|
|
81
|
+
throw new MediaPipelineError(
|
|
82
|
+
'MEDIA_FORMAT_INVALID',
|
|
83
|
+
'VP8 chunk too small to contain a frame header',
|
|
84
|
+
referencePath,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
const frameTag = bytes.subarray(payloadStart, payloadStart + 3);
|
|
88
|
+
const syncCode = bytes.subarray(payloadStart + 3, payloadStart + 6);
|
|
89
|
+
if (!syncCode.equals(Buffer.from([0x9d, 0x01, 0x2a]))) {
|
|
90
|
+
throw new MediaPipelineError(
|
|
91
|
+
'MEDIA_FORMAT_INVALID',
|
|
92
|
+
'VP8 frame sync code mismatch',
|
|
93
|
+
referencePath,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const keyFrame = (frameTag[0] & 0x01) === 0;
|
|
97
|
+
if (!keyFrame) {
|
|
98
|
+
throw new MediaPipelineError(
|
|
99
|
+
'MEDIA_FORMAT_INVALID',
|
|
100
|
+
'VP8 payload is not a key frame',
|
|
101
|
+
referencePath,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
width = bytes.readUInt16LE(payloadStart + 6) & 0x3fff;
|
|
105
|
+
height = bytes.readUInt16LE(payloadStart + 8) & 0x3fff;
|
|
106
|
+
} else if (fourCc === 'VP8L') {
|
|
107
|
+
if (chunkSize < 5 || bytes[payloadStart] !== 0x2f) {
|
|
108
|
+
throw new MediaPipelineError(
|
|
109
|
+
'MEDIA_FORMAT_INVALID',
|
|
110
|
+
'VP8L lossless signature mismatch',
|
|
111
|
+
referencePath,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
const bits =
|
|
115
|
+
bytes.readUInt8(payloadStart + 1) |
|
|
116
|
+
(bytes.readUInt8(payloadStart + 2) << 8) |
|
|
117
|
+
(bytes.readUInt8(payloadStart + 3) << 16) |
|
|
118
|
+
(bytes.readUInt8(payloadStart + 4) << 24);
|
|
119
|
+
width = (bits & 0x3fff) + 1;
|
|
120
|
+
height = ((bits >>> 14) & 0x3fff) + 1;
|
|
121
|
+
const versionBits = (bits >>> 28) & 0x7;
|
|
122
|
+
if (versionBits !== 0) {
|
|
123
|
+
throw new MediaPipelineError(
|
|
124
|
+
'MEDIA_FORMAT_INVALID',
|
|
125
|
+
'VP8L version bits must be zero',
|
|
126
|
+
referencePath,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
throw new MediaPipelineError(
|
|
131
|
+
'MEDIA_FORMAT_INVALID',
|
|
132
|
+
`unsupported WebP image chunk "${fourCc}"; only VP8 and VP8L are admitted`,
|
|
133
|
+
referencePath,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
assertDimensionsWithinCeiling(width, height, referencePath, 'WebP');
|
|
138
|
+
return { width, height };
|
|
139
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core-owned chrome message catalog (brief S2 section 3: the semantic
|
|
3
|
+
* skeleton and navigation renderer; task packet S2-T06: "all user-visible
|
|
4
|
+
* chrome strings coming from a message catalog (not inline literals) so
|
|
5
|
+
* localization can find them").
|
|
6
|
+
*
|
|
7
|
+
* Every user-visible string this package's own generated chrome (skip link,
|
|
8
|
+
* navigation labels, breadcrumb labels, pagination controls, generated
|
|
9
|
+
* listing-page headings, the error page kind, the S2-T07 Light/Dark/System
|
|
10
|
+
* appearance control's label and its three option labels) emits is looked
|
|
11
|
+
* up here by key
|
|
12
|
+
* rather than written inline at its call site. S2 authors exactly one
|
|
13
|
+
* catalog, `en` (the only language the golden fixture's own core chrome is
|
|
14
|
+
* required to exercise); per-content localization of *authored* content is
|
|
15
|
+
* `publication`/`content` data, not core chrome, and full chrome
|
|
16
|
+
* localization (additional catalogs, a locale-selection policy) is future
|
|
17
|
+
* work this module is shaped to admit without changing any call site: every
|
|
18
|
+
* lookup already goes through {@link getMessages}, keyed by a BCP-47 tag,
|
|
19
|
+
* with a fixed fallback to `en` for a tag this catalog does not carry.
|
|
20
|
+
*
|
|
21
|
+
* @type {Readonly<Record<string, Readonly<Record<string, string | ((...args: string[]) => string)>>>>}
|
|
22
|
+
*/
|
|
23
|
+
const CATALOGS = Object.freeze({
|
|
24
|
+
en: Object.freeze({
|
|
25
|
+
skipToContent: 'Skip to content',
|
|
26
|
+
homeLinkLabel: 'Home',
|
|
27
|
+
primaryNavigationLabel: 'Primary',
|
|
28
|
+
footerNavigationLabel: 'Footer',
|
|
29
|
+
breadcrumbNavigationLabel: 'Breadcrumb',
|
|
30
|
+
paginationNavigationLabel: 'Pagination',
|
|
31
|
+
previousPageLabel: 'Previous',
|
|
32
|
+
nextPageLabel: 'Next',
|
|
33
|
+
/** @type {(current: string, total: string) => string} */
|
|
34
|
+
pageStatusLabel: (current, total) => `Page ${current} of ${total}`,
|
|
35
|
+
authorsSectionLabel: 'Authors',
|
|
36
|
+
tagsSectionLabel: 'Tags',
|
|
37
|
+
seriesSectionLabel: 'Series',
|
|
38
|
+
archiveSectionLabel: 'Archive',
|
|
39
|
+
indexHeading: 'All articles',
|
|
40
|
+
authorsIndexLabel: 'Authors',
|
|
41
|
+
tagIndexLabelPrefix: 'Tag:',
|
|
42
|
+
seriesIndexLabelPrefix: 'Series:',
|
|
43
|
+
archiveYearLabelPrefix: 'Archive:',
|
|
44
|
+
/** @type {(displayName: string) => string} */
|
|
45
|
+
aboutAuthorHeading: (displayName) => `About ${displayName}`,
|
|
46
|
+
/** @type {(displayName: string) => string} */
|
|
47
|
+
byLineLabel: (displayName) => `By ${displayName}`,
|
|
48
|
+
footerCopyrightPrefix: '©',
|
|
49
|
+
footerAttributionLabel: 'Published with the Galascribe template renderer.',
|
|
50
|
+
errorPageHeading: 'Page not found',
|
|
51
|
+
errorPageBody:
|
|
52
|
+
'The page you were looking for could not be found. It may have been ' +
|
|
53
|
+
'moved, renamed or removed.',
|
|
54
|
+
errorPageReturnHomeLabel: 'Return to the home page',
|
|
55
|
+
appearanceControlLabel: 'Appearance',
|
|
56
|
+
appearanceModeLightLabel: 'Light',
|
|
57
|
+
appearanceModeDarkLabel: 'Dark',
|
|
58
|
+
appearanceModeSystemLabel: 'System',
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/** @type {string} */
|
|
63
|
+
const DEFAULT_LOCALE = 'en';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolve the message catalog for a BCP-47 language tag, falling back to the
|
|
67
|
+
* default `en` catalog for any tag with no dedicated catalog (currently
|
|
68
|
+
* every tag, since only `en` is authored in S2).
|
|
69
|
+
*
|
|
70
|
+
* @param {string} [bcp47] a language tag (typically
|
|
71
|
+
* `publication.defaultLanguage` or a per-content `frontmatter.language`)
|
|
72
|
+
* @returns {Readonly<Record<string, string | ((...args: string[]) => string)>>}
|
|
73
|
+
* the resolved catalog
|
|
74
|
+
*/
|
|
75
|
+
export function getMessages(bcp47) {
|
|
76
|
+
const primarySubtag = (bcp47 ?? '').split('-')[0]?.toLowerCase() ?? '';
|
|
77
|
+
return CATALOGS[primarySubtag] ?? CATALOGS[DEFAULT_LOCALE];
|
|
78
|
+
}
|