@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.
Files changed (43) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +515 -0
  3. package/contracts/render-policy.jcs +1 -0
  4. package/contracts/theme-styling-contract.jcs +1 -0
  5. package/package.json +96 -0
  6. package/src/core/errors.js +136 -0
  7. package/src/core/index.js +758 -0
  8. package/src/core/internal/appearance/bootstrap-script.js +159 -0
  9. package/src/core/internal/appearance/contract.js +193 -0
  10. package/src/core/internal/appearance/controller-markup.js +80 -0
  11. package/src/core/internal/appearance/styling-contract.js +509 -0
  12. package/src/core/internal/canonical-jcs.js +0 -0
  13. package/src/core/internal/content-security.js +0 -0
  14. package/src/core/internal/eleventy-render.js +220 -0
  15. package/src/core/internal/feeds.js +161 -0
  16. package/src/core/internal/fs-walk.js +38 -0
  17. package/src/core/internal/media/avif-probe.js +169 -0
  18. package/src/core/internal/media/font-policy.js +56 -0
  19. package/src/core/internal/media/jpeg-codec.js +0 -0
  20. package/src/core/internal/media/limits.js +129 -0
  21. package/src/core/internal/media/pipeline.js +353 -0
  22. package/src/core/internal/media/png-codec.js +406 -0
  23. package/src/core/internal/media/resize.js +147 -0
  24. package/src/core/internal/media/sniff.js +109 -0
  25. package/src/core/internal/media/webp-probe.js +139 -0
  26. package/src/core/internal/messages.js +78 -0
  27. package/src/core/internal/page-kinds.js +709 -0
  28. package/src/core/internal/provenance.js +49 -0
  29. package/src/core/internal/render-policy-content.js +295 -0
  30. package/src/core/internal/route-labels.js +68 -0
  31. package/src/core/internal/route.js +167 -0
  32. package/src/core/internal/search-index.js +119 -0
  33. package/src/core/internal/seo.js +114 -0
  34. package/src/core/internal/sitemap.js +77 -0
  35. package/src/core/internal/skeleton.js +413 -0
  36. package/src/core/internal/source-inventory.js +145 -0
  37. package/src/core/internal/text-direction.js +59 -0
  38. package/src/core/internal/theme-assets.js +387 -0
  39. package/src/core/internal/uuid.js +51 -0
  40. package/src/core/manifest.js +0 -0
  41. package/types/eleventy-plugin-syntaxhighlight.d.ts +26 -0
  42. package/types/eleventy.d.ts +38 -0
  43. 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
+ }