@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
package/README.md
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# `@rathnasgala2/template`
|
|
2
|
+
|
|
3
|
+
The deterministic Eleventy-backed static publication renderer for Galascribe's
|
|
4
|
+
portable author repository format. It is one of the three author-facing S2
|
|
5
|
+
artifacts, alongside one `@rathnasgala2/theme-*` package and
|
|
6
|
+
`@rathnasgala2/publish-action`.
|
|
7
|
+
|
|
8
|
+
This package is JavaScript ESM on Node 24, authored without TypeScript sources
|
|
9
|
+
(DEC-094): every export carries JSDoc types, checked by `tsc --checkJs --noEmit`
|
|
10
|
+
against a hand-maintained `.d.ts` surface.
|
|
11
|
+
|
|
12
|
+
## Status
|
|
13
|
+
|
|
14
|
+
S2-T02's repository scaffold (exact pins, `src/core/`-only layout, module-tree
|
|
15
|
+
absence gate, quality-gate tooling) plus S2-T03's renderer adapter:
|
|
16
|
+
`renderPublication(buildInput, options)`, exported from `src/core/index.js`, is
|
|
17
|
+
the package's one documented entry point. It:
|
|
18
|
+
|
|
19
|
+
1. validates a candidate `urn:gala:schema:build-input:2.0.0` document with
|
|
20
|
+
`@rathnasgala2/schemas`' `validateGalaDocument` and fails closed
|
|
21
|
+
(`BuildInputValidationError`, before anything is written) on an invalid one;
|
|
22
|
+
2. renders the publication profile (when selected) and every selected content
|
|
23
|
+
record, plus every authored static redirect document, through a single
|
|
24
|
+
confined, single-pass programmatic Eleventy 3.1.6 build behind
|
|
25
|
+
`src/core/internal/eleventy-render.js` — the only module in this repository
|
|
26
|
+
permitted to import `@11ty/eleventy`, so no Eleventy object, plug-in API,
|
|
27
|
+
config object or Nunjucks environment ever crosses back out of it; and
|
|
28
|
+
3. assembles, digests and schema-validates a closed
|
|
29
|
+
`urn:gala:schema:artifact-manifest:2.0.0` instance describing the candidate
|
|
30
|
+
output directory.
|
|
31
|
+
|
|
32
|
+
S2-T05 adds the media pipeline under `src/core/internal/media/`, run from
|
|
33
|
+
`renderPublication` after the Eleventy route listing so its output is never
|
|
34
|
+
mistaken for an HTML route:
|
|
35
|
+
|
|
36
|
+
- **Bounded decode.** JPEG (via the pinned pure-JS `jpeg-js` decoder, no native
|
|
37
|
+
addon or WebAssembly) and PNG (hand-written, using Node's built-in `zlib` with
|
|
38
|
+
a decompression-bomb-bounded inflate) are fully decoded to RGBA8; WebP and
|
|
39
|
+
AVIF get bounded container/declared-dimension validation without a full
|
|
40
|
+
entropy decode (a documented scope decision — see
|
|
41
|
+
`webp-probe.js`/`avif-probe.js`). Every format shares one set of resource
|
|
42
|
+
ceilings in `limits.js` (raster dimension, pixel count, decoded-buffer and
|
|
43
|
+
source-byte caps reused from DEC-097's own closed binary-asset decoder bounds,
|
|
44
|
+
plus this renderer's own documented per-publication ceilings).
|
|
45
|
+
- **Metadata strip, orientation.** PNG/JPEG derivatives are always re-encoded
|
|
46
|
+
from decoded pixels, never byte-copied, so no ancillary/EXIF metadata survives
|
|
47
|
+
into a derivative; JPEG's EXIF `Orientation` tag is read and applied
|
|
48
|
+
(rotate/flip) before any derivative is produced.
|
|
49
|
+
- **Deterministic responsive derivatives.** Content-addressed
|
|
50
|
+
`assets/media/<sourceDigest>/{original,<width>w}.<ext>` files at fixed widths
|
|
51
|
+
`320/640/960/1280/1920` (below the source width), re-encoded with fixed,
|
|
52
|
+
pinned encoder settings so two clean builds of the same source produce
|
|
53
|
+
byte-identical derivative files.
|
|
54
|
+
- **SVG rejection.** Any byte sequence recognized as SVG/XML is rejected
|
|
55
|
+
unconditionally as an image reference (`MEDIA_SVG_REJECTED`) — SVG is never
|
|
56
|
+
processed as author media.
|
|
57
|
+
- **Font policy.** `appearance.fontAssets` entries must be bounded WOFF2 (exact
|
|
58
|
+
signature, DEC-097's 4,194,304-byte font cap); a remote font reference cannot
|
|
59
|
+
enter `build-input` in the first place (`resolvedFile` is always a
|
|
60
|
+
repository-relative path), so "no remote font is ever retrieved" holds
|
|
61
|
+
structurally.
|
|
62
|
+
- **Fail-closed errors.** Every rejection — oversize, decompression bomb, digest
|
|
63
|
+
mismatch, SVG, unrecognized format, invalid font — is a `MediaPipelineError`
|
|
64
|
+
with a stable `reasonCode`, thrown before the manifest is assembled.
|
|
65
|
+
|
|
66
|
+
`options.sourceDirectory` (new in S2-T05) is the caller-mounted, read-only
|
|
67
|
+
repository source tree `build-input`'s `resolvedFile` references point into;
|
|
68
|
+
every referenced file's bytes are read from there and verified against
|
|
69
|
+
`build-input`'s declared `sourceDigest` before being decoded.
|
|
70
|
+
|
|
71
|
+
S2-T04 adds the complete output-security pipeline
|
|
72
|
+
(`src/core/internal/content-security.js`) on top of that adapter. **Division of
|
|
73
|
+
labour** (DEC-097 section 5: normalization replaces an authored body path with a
|
|
74
|
+
`renderableBody` carrying "the deterministic policy output/digest";
|
|
75
|
+
`build-input.schema.json` fixes `renderableBody.bodyMediaType` as
|
|
76
|
+
`const: "text/html"`): a `renderableBody.body` inside a validated
|
|
77
|
+
`build-input:2.0.0` instance is **already** render-policy-conformant HTML,
|
|
78
|
+
produced once upstream of this renderer. This package therefore exposes two
|
|
79
|
+
distinct public entry points:
|
|
80
|
+
|
|
81
|
+
- **`normalizeAuthoredMarkdown(markdownSource) -> { html, bodyDigest }`**
|
|
82
|
+
(exported from `src/core/index.js`) is the pipeline an upstream
|
|
83
|
+
`build-input:2.0.0` producer (`publish-kernel`/`publish-action`) runs exactly
|
|
84
|
+
once per authored Markdown source: `markdown-it` 14.3.1 configured exactly
|
|
85
|
+
`{ html: false, linkify: false, typographer: false }`, with a closed
|
|
86
|
+
URL-scheme allowlist (`https`, `http`, `mailto`, or a schemeless relative
|
|
87
|
+
path/fragment — rejecting `javascript:`, `data:`, `blob:`, `vbscript:` and
|
|
88
|
+
protocol-relative URLs, including WHATWG-style tab/newline scheme obfuscation)
|
|
89
|
+
and deterministic, collision-free heading `id`s; fenced code through an owned
|
|
90
|
+
highlighter over `@11ty/eleventy-plugin-syntaxhighlight` 5.0.2 (only a closed
|
|
91
|
+
grammar catalog — `bash`, `css`, `diff`, `java`, `javascript`, `json`, `jsx`,
|
|
92
|
+
`markdown`, `markup`, `python`, `sql`, `tsx`, `typescript`, `yaml` — is ever
|
|
93
|
+
highlighted; every other fence language falls back to escaped, unhighlighted
|
|
94
|
+
code); then `sanitize-html` 2.17.7 against a closed tag/attribute/class/scheme
|
|
95
|
+
allowlist (no `style`, no `on*`, no `srcdoc`, no `id` outside generated
|
|
96
|
+
heading IDs, no script/style/iframe/object/embed/ svg/math/template content)
|
|
97
|
+
and a fail-closed residual-active-content scan.
|
|
98
|
+
- **`renderPublication`** (the renderer itself) never runs Markdown parsing on a
|
|
99
|
+
`renderableBody.body` it receives — re-parsing already-rendered HTML under
|
|
100
|
+
`html:false` would double-escape every tag (`<p>` becoming `<p>`).
|
|
101
|
+
Instead it verifies, before any directory is created: every
|
|
102
|
+
`renderableBody.renderPolicy` (publication profile, footer card, every content
|
|
103
|
+
record) byte-equals
|
|
104
|
+
`SHA256(UTF8("GALA-RENDER-POLICY-V2\0") || <contract file bytes>)`; every
|
|
105
|
+
`bodyDigest` byte-equals a plain SHA-256 of its `body` bytes; and
|
|
106
|
+
re-sanitizing `body` reproduces it byte-for-byte (an idempotence check —
|
|
107
|
+
genuinely policy-conformant output cannot change under its own sanitizer). Any
|
|
108
|
+
mismatch, or a non-empty `modules`/`placements` value, fails closed with
|
|
109
|
+
`RenderPolicyViolationError` before anything is written. A verified body is
|
|
110
|
+
then inserted into the page verbatim.
|
|
111
|
+
|
|
112
|
+
`contracts/render-policy.jcs` is the real, closed sanitizer/parser catalog
|
|
113
|
+
(name, version, CSP baseline, markdown-it options, sanitizer allowlist,
|
|
114
|
+
highlighter grammar/token-class catalog) both surfaces above read from,
|
|
115
|
+
generated deterministically by `scripts/generate-contracts.mjs` from
|
|
116
|
+
`src/core/internal/render-policy-content.js` — the single source of truth, so
|
|
117
|
+
the published contract and the enforced policy cannot drift apart. Every
|
|
118
|
+
rendered page also carries the exact per-artifact CSP baseline as a
|
|
119
|
+
`<meta http-equiv="Content-Security-Policy">` tag (byte-identical on every
|
|
120
|
+
route, since S2 materializes no module package/configuration/output/runtime).
|
|
121
|
+
|
|
122
|
+
`img[src]` is restricted to repository-relative paths only in this pipeline;
|
|
123
|
+
full manifest-coverage verification of media references is the S2-T05 media
|
|
124
|
+
pipeline's job (immediately above). A handful of manifest fields (`builder`,
|
|
125
|
+
`sourceIdentity.repository`, `workflowIdentity`, `buildToolVersions`,
|
|
126
|
+
`excludedInputs`) describe facts `build-input` does not itself carry;
|
|
127
|
+
`renderPublication` accepts them as an explicit `options.provenance` bundle
|
|
128
|
+
rather than fabricating them — see `src/core/manifest.js`'s module
|
|
129
|
+
documentation. `contracts/theme-styling-contract.jcs` is S2-T12's real, closed
|
|
130
|
+
`templateStylingContract` catalog (see the S2-T12 section below).
|
|
131
|
+
|
|
132
|
+
S2-T06 adds the core semantic skeleton, page kinds and navigation renderer on
|
|
133
|
+
top of the S2-T04 output-security pipeline every generated page still passes
|
|
134
|
+
through:
|
|
135
|
+
|
|
136
|
+
- **`src/core/internal/skeleton.js`** renders the one landmark contract every
|
|
137
|
+
page kind shares, through its one `renderPageBody` composition: a skip link
|
|
138
|
+
(the first focusable element, targeting `#main-content`), one `<header>` (home
|
|
139
|
+
link with the validated publication name, plus the always-present
|
|
140
|
+
`header-actions` slot), one primary `<nav>` from `navigationNormalized.items`,
|
|
141
|
+
one `<main id="main-content">` (an optional breadcrumb `<nav>` before the
|
|
142
|
+
page's own single `<h1>`), and one `<footer>` (publication name, footer
|
|
143
|
+
navigation, the `footer-profile` slot, the remaining core slots, and the
|
|
144
|
+
template attribution line). Core owns nine versioned semantic slots
|
|
145
|
+
(`header-actions`, `article-preamble`, `article-end`, `footer-profile`,
|
|
146
|
+
`footer-auxiliary`, `account-intent`, `conversation`, `newsletter`,
|
|
147
|
+
`edition-selector`) plus the collapsed `article-footer-ad` slot (DEC-060):
|
|
148
|
+
every slot is one `data-gala-slot="<name>"` attribute on an otherwise empty,
|
|
149
|
+
non-landmark `<div>`, and the collapsed slot additionally carries `hidden`. S2
|
|
150
|
+
has no module system, so every slot except `footer-profile` (which always
|
|
151
|
+
wraps the publication's own footer card) and `header-actions` (S2-T07's
|
|
152
|
+
appearance control, core content rather than a module) renders empty. This
|
|
153
|
+
module also renders breadcrumbs and deterministic pagination controls
|
|
154
|
+
(`rel="prev"`/`rel="next"`, an `aria-current="page"` status).
|
|
155
|
+
- **`src/core/internal/page-kinds.js`** builds every generated page kind from a
|
|
156
|
+
validated `build-input:2.0.0`: `profile` (the optional publication profile),
|
|
157
|
+
`author` (one page per `build-input.authors` entry, at `/authors/<id>`),
|
|
158
|
+
`article`/`page` (one page per `content[]` record, at its own route), `index`
|
|
159
|
+
(a paginated reverse-chronological listing of every `status: "published"`,
|
|
160
|
+
`kind: "article"` record — an `unlisted` record is reachable only by its own
|
|
161
|
+
direct route, never through a generated listing), `tag`/`series` (one listing
|
|
162
|
+
page per distinct authored value, plus a `/tags`/`/series` root page linking
|
|
163
|
+
to each), and `archive` (one listing page per UTC calendar year, plus a
|
|
164
|
+
`/archive` root page). The `error` page kind (`renderErrorPageBody`) is
|
|
165
|
+
exported as a pure function for a future `404.html` caller (S2-T08, out of
|
|
166
|
+
this task's scope), not wired into any generated route here. Listing page size
|
|
167
|
+
(10), sort tie-break, and the tag/series/archive root pages are documented
|
|
168
|
+
scoped decisions the brief text this task read does not fix verbatim.
|
|
169
|
+
- **`src/core/internal/messages.js`** is the message catalog every user-visible
|
|
170
|
+
chrome string above is looked up from (never an inline literal), keyed by
|
|
171
|
+
BCP-47 tag with a fixed `en` fallback — the only catalog S2 authors;
|
|
172
|
+
additional locales are future work that changes no call site.
|
|
173
|
+
- **`src/core/internal/text-direction.js`** derives a page's `dir` attribute
|
|
174
|
+
from its own BCP-47 language tag (`build-input:2.0.0` carries no separate
|
|
175
|
+
direction field) against a closed right-to-left primary-language-subtag table
|
|
176
|
+
— the same approach `Intl.Locale`'s `textInfo.direction` uses, reimplemented
|
|
177
|
+
as a small static table to add no runtime dependency.
|
|
178
|
+
- **`src/core/internal/route-labels.js`** derives a deterministic,
|
|
179
|
+
collision-free route segment for an arbitrary tag/series label (a
|
|
180
|
+
`plainLabel`, not a route-safe `slug`): an ASCII-readable prefix plus a
|
|
181
|
+
content-hash suffix, so two distinct Unicode labels can never collide on the
|
|
182
|
+
same generated route.
|
|
183
|
+
|
|
184
|
+
Chrome text is resolved once, in the publication's own `defaultLanguage`, for
|
|
185
|
+
every page (a scoped decision: navigation/footer text does not flip language
|
|
186
|
+
page to page); each page's own `<html lang>`/`dir` still reflects that
|
|
187
|
+
individual page's own content language.
|
|
188
|
+
|
|
189
|
+
S2-T07 adds the Light/Dark/System appearance controller
|
|
190
|
+
(`src/core/internal/appearance/`), the only browser bootstrap S2 emits (brief S2
|
|
191
|
+
section 3, "Module absence and CSP equality"):
|
|
192
|
+
|
|
193
|
+
- **`contract.js`** is the one module every other appearance-related file
|
|
194
|
+
imports every name/value from: the two DEC-097-fixed root attributes
|
|
195
|
+
(`data-gala-publication-root` presence, `data-gala-resolved-color-mode`
|
|
196
|
+
exact-value `"light"`/`"dark"`), the template-internal
|
|
197
|
+
`data-gala-color-mode-selection` bookkeeping attribute, the three canonical
|
|
198
|
+
mode values (`light`/`dark`/`system`), the versioned local storage key
|
|
199
|
+
(`gala:appearance:color-mode:v1`), the control's `id`, the bootstrap script's
|
|
200
|
+
fixed output path/media type and the `color-scheme` meta content — the
|
|
201
|
+
contract S2-T12's published theme styling catalog targets.
|
|
202
|
+
- **`controller-markup.js`** renders a native `<select>` (a brief-admitted
|
|
203
|
+
pattern; needs no `aria-pressed`/`aria-checked` bookkeeping of its own, unlike
|
|
204
|
+
a custom button-group or radio-group would) bound to a visible `<label>`, with
|
|
205
|
+
message-catalog labels, inserted into the header's `header-actions` slot. Its
|
|
206
|
+
default selected option is always `system`; the bootstrap script's own
|
|
207
|
+
`DOMContentLoaded` phase corrects `select.value` to the actual stored/resolved
|
|
208
|
+
value once `<body>` exists.
|
|
209
|
+
- **`bootstrap-script.js`** is the exact deterministic pre-paint bootstrap
|
|
210
|
+
(every literal from `contract.js`, so it and the server-rendered control can
|
|
211
|
+
never drift): a synchronous phase, run the instant this blocking,
|
|
212
|
+
non-`defer`/non-`async`/non-`module` `<script src>` executes in `<head>` —
|
|
213
|
+
before `<body>` is even parsed — that reads the stored selection (falling back
|
|
214
|
+
to `system` on a missing/invalid/unreadable value; a `localStorage` read/write
|
|
215
|
+
failure is always caught, so a blocked or private-mode session stays fully
|
|
216
|
+
usable) and resolves it against `prefers-color-scheme` (`system` resolves to
|
|
217
|
+
`light` when unavailable), then applies both root attributes. It never touches
|
|
218
|
+
`document.documentElement.style` (no hide-until-ready trick of any kind), no
|
|
219
|
+
network, no cookie, no account/tracking identifier, and no storage key other
|
|
220
|
+
than its own. A `DOMContentLoaded`-deferred phase wires the control's `change`
|
|
221
|
+
event and a live `prefers-color-scheme` subscription that only updates the
|
|
222
|
+
resolved attribute while the current selection is still exactly `system` (an
|
|
223
|
+
explicit `light`/`dark` choice is never overridden by a later system change).
|
|
224
|
+
- `src/core/index.js` writes the bootstrap script into the candidate output
|
|
225
|
+
directory and enters it into the manifest as an ordinary `manifestAsset` row
|
|
226
|
+
(`{path, byteLength, sha256}` plus its fixed
|
|
227
|
+
`application/javascript; charset=utf-8` media type), after the Eleventy route
|
|
228
|
+
listing — exactly like an S2-T05 media derivative — so it is never mistaken
|
|
229
|
+
for an HTML route. `src/core/internal/eleventy-render.js`'s skeleton layout
|
|
230
|
+
carries the `data-gala-publication-root` attribute, the
|
|
231
|
+
`<meta name="color-scheme" content="light dark">` tag and the one
|
|
232
|
+
`<script src>` reference, all fixed literals from `contract.js`, so every
|
|
233
|
+
route's `<head>` is still byte-identical (S2 materializes no module
|
|
234
|
+
package/configuration/output/runtime).
|
|
235
|
+
- With JavaScript disabled, the control still renders (a native `<select>`,
|
|
236
|
+
fully keyboard-operable and usable with no script at all) and the resolved
|
|
237
|
+
root attribute is simply never set; `<meta name="color-scheme">` alone already
|
|
238
|
+
carries `prefers-color-scheme` through to user-agent styling and the initial
|
|
239
|
+
paint independent of any script.
|
|
240
|
+
|
|
241
|
+
S2-T12 replaces `contracts/theme-styling-contract.jcs`'s S2-T02 scaffold
|
|
242
|
+
placeholder with the real, closed `templateStylingContract` catalog DEC-097
|
|
243
|
+
section 4 defines, and wires the selected theme package's stylesheets into every
|
|
244
|
+
generated page:
|
|
245
|
+
|
|
246
|
+
- **`src/core/internal/appearance/styling-contract.js`** is the one reviewed
|
|
247
|
+
source module `scripts/generate-contracts.mjs` emits the contract from: the
|
|
248
|
+
ordered four-layer catalog (`gala-tokens`/`gala-components`/`gala-utilities`/
|
|
249
|
+
`gala-print`), the publication-root/resolved-palette selectors, the closed
|
|
250
|
+
type/class/id/attribute leaf catalogs, and exactly 64 `publicThemeSlotHooks` —
|
|
251
|
+
one per catalog leaf, so every leaf a theme could validly select is a named,
|
|
252
|
+
documented hook and no catalog member is orphaned. Every leaf is drawn from
|
|
253
|
+
what this renderer actually renders: 28 type-selector hooks (every
|
|
254
|
+
landmark/prose/code/control element `internal/skeleton.js`,
|
|
255
|
+
`internal/page-kinds.js` and the markdown-it CommonMark pipeline can produce),
|
|
256
|
+
15 class-selector hooks (the base Prism `.token` class plus one
|
|
257
|
+
`.language-<grammar>` hook per admitted highlight grammar — fine-grained
|
|
258
|
+
per-token-kind classes are a documented S2 scope exclusion), 2 id-selector
|
|
259
|
+
hooks (`#main-content`, the appearance `<select>`'s fixed id) and 19
|
|
260
|
+
attribute-value hooks (one per `data-gala-slot` value, one per the new
|
|
261
|
+
`data-gala-page-kind` value). `catalogDigest` is
|
|
262
|
+
`SHA256(UTF8("GALA-TEMPLATE-STYLING-CONTRACT-V2\0") || JCS(...))`, matching
|
|
263
|
+
DEC-097 section 8 and the digest profile `@rathnasgala2/schemas`' own internal
|
|
264
|
+
`templateStylingContract` profile uses (that function is not exported through
|
|
265
|
+
the package's public API, so this module reimplements the same
|
|
266
|
+
domain-separated formula rather than importing it).
|
|
267
|
+
`assertTemplateStylingContractShape` is this repository's own structural
|
|
268
|
+
self-validator (root/palette attribute rows, fixed layer/selector/composition
|
|
269
|
+
constants, hook count/uniqueness bound, `catalogDigest` recomputation); no
|
|
270
|
+
`urn:gala:schema:template-styling-contract` schema ID is registered in
|
|
271
|
+
`@rathnasgala2/schemas@2.0.0` for `validateGalaDocument` to check this object
|
|
272
|
+
against directly.
|
|
273
|
+
- **`data-gala-page-kind`** (`internal/page-kinds.js`) is a new template-owned
|
|
274
|
+
semantic attribute on every generated page's `<body>`, one exact value per
|
|
275
|
+
`PAGE_KIND_VALUES` entry (`archive`/`article`/`author`/`error`/`index`/
|
|
276
|
+
`page`/`profile`/`series`/`tag`), so a theme can vary presentation by page
|
|
277
|
+
kind without promoting a private selector.
|
|
278
|
+
- **`test/theme-styling-contract.test.js`** is the drift gate: it renders a rich
|
|
279
|
+
fixture whose one extra article exercises every markdown-derived hook (every
|
|
280
|
+
heading level, every admitted highlight grammar, lists, blockquote, emphasis,
|
|
281
|
+
a link and an image) and asserts, in both directions, that every published
|
|
282
|
+
hook is actually rendered and every rendered hook-like construct
|
|
283
|
+
(`data-gala-*` attribute, `<pre>`/`<code>` class, or element tag inside
|
|
284
|
+
`<body>`) is published. It also validates two `theme-contract:2.0.0` fixtures
|
|
285
|
+
— one light-dominant, one dark-dominant palette — with
|
|
286
|
+
`@rathnasgala2/schemas`' own exported `validateGalaDocument`.
|
|
287
|
+
- **`src/core/internal/theme-assets.js`** is the optional theme-asset
|
|
288
|
+
integration layer: given `options.themeDirectory` (an extracted theme package
|
|
289
|
+
directory, shaped exactly like the S2-T13 package file set — `theme.json`,
|
|
290
|
+
`tokens.css`, `components.css`, an optional `utilities.css`, `print.css`,
|
|
291
|
+
`package.json`, `LICENSE`, `README.md`, `assets/*`), it copies the declared
|
|
292
|
+
stylesheets and any declared non-CSS passive assets into `assets/theme/`,
|
|
293
|
+
enters each as an ordinary `{path, byteLength, sha256}` `manifestAsset` row,
|
|
294
|
+
and renders the ordered `<link>` markup every generated page's `<head>`
|
|
295
|
+
inserts (`print.css` alone carries `media="print"`). It validates the theme's
|
|
296
|
+
own declared `stylesheets` list against the two admitted shapes (with or
|
|
297
|
+
without `utilities.css`) and that `cssLayers` byte-equals the corresponding
|
|
298
|
+
layer projection, but does **not** re-implement DEC-097's full CSS Syntax
|
|
299
|
+
Module admission grammar — that is the shared `theme-release.yml` conformance
|
|
300
|
+
runner's job (S2-T11), run once per theme release, never per publication
|
|
301
|
+
build. `renderPublication` falls back to the pre-existing fixed
|
|
302
|
+
`assets/theme/print.css` link when no `options.themeDirectory` is supplied
|
|
303
|
+
(every caller before S2-T12, and this repository's own tests that do not
|
|
304
|
+
exercise theme integration). Minimal in-repo fixture theme packages for both
|
|
305
|
+
stylesheet-list shapes live under `test/fixtures/theme-fixture-full/` and
|
|
306
|
+
`test/fixtures/theme-fixture-minimal/` — deliberately not real, conformant
|
|
307
|
+
theme CSS (authoring one is S2-T13's task); they exist only to exercise the
|
|
308
|
+
copy/link/manifest mechanism.
|
|
309
|
+
- **basePath fix (LOCAL-7 follow-up; independent-review finding B2).** Before
|
|
310
|
+
S2-T12, the appearance bootstrap script's `<script src>` and the social-image
|
|
311
|
+
`og:image`/`twitter:image` URL were both root-absolute, unprefixed by the
|
|
312
|
+
publication's own `basePath` — a non-root `basePath` publication would 404
|
|
313
|
+
them. `internal/appearance/contract.js`'s new
|
|
314
|
+
`appearanceBootstrapScriptHref(basePath)` and `internal/seo.js`'s
|
|
315
|
+
`resolveSocialImageUrl` (now taking `basePath`) both join through
|
|
316
|
+
`internal/route.js`'s shared `joinBasePathAndRoute`/`projectFixedAssetPath`,
|
|
317
|
+
exactly like the pre-existing print-stylesheet convention (now folded into
|
|
318
|
+
`internal/theme-assets.js`) already did for _hrefs_. Independent review
|
|
319
|
+
(finding B2) found that this was only half the fix: the _physical files_ this
|
|
320
|
+
renderer writes for every asset class — theme CSS/passive assets, every S2-T05
|
|
321
|
+
media derivative, and the S2-T07 appearance bootstrap script — were still
|
|
322
|
+
written, and entered into the manifest, at their bare **unprefixed** paths.
|
|
323
|
+
Since the local-directory adapter serves a `manifestAsset.path` verbatim, a
|
|
324
|
+
non-root `basePath` publication's `<link>`/`<script src>`/`og:image` href
|
|
325
|
+
would point at `<basePath>/assets/...` while the actual file sat at the
|
|
326
|
+
unprefixed `assets/...` — a 404 in practice, not just a theoretical href
|
|
327
|
+
mismatch. `src/core/index.js` now joins `basePath` into the physical
|
|
328
|
+
destination path and the `manifestAsset.path` for media derivatives
|
|
329
|
+
(`joinedMediaAssets`, computed with the same `projectFixedAssetPath` every
|
|
330
|
+
other fixed-name generated asset already uses) and the appearance bootstrap
|
|
331
|
+
script (`joinedAppearanceScriptPath`); `internal/theme-assets.js` computes its
|
|
332
|
+
joined output path once per file and derives both the `<link href>` and the
|
|
333
|
+
file/manifest path from that exact same value, so they cannot drift apart.
|
|
334
|
+
`test/theme-styling-contract.test.js` carries a dedicated non-root,
|
|
335
|
+
multi-segment `basePath` (`/blog/2024`) fixture test asserting every one of
|
|
336
|
+
these hrefs (media, appearance script, theme CSS, print CSS) is consistently
|
|
337
|
+
joined **and** that the physical file for one asset of each class actually
|
|
338
|
+
exists at that exact joined path.
|
|
339
|
+
|
|
340
|
+
- **Theme-asset path containment (independent-review finding B1).** A theme
|
|
341
|
+
package's `theme.json` `stylesheets`/`assets` paths are untrusted input from
|
|
342
|
+
the selected theme package, not fixed literals. `internal/theme-assets.js`'s
|
|
343
|
+
`assertSafeThemeRelativePath` rejects an absolute path, a backslash, a non-NFC
|
|
344
|
+
path, and any empty/`.`/`..` segment, before the file is ever opened;
|
|
345
|
+
`assertContainedThemePath` then walks every path component under
|
|
346
|
+
`themeDirectory` with `lstat`, rejecting a symlink at any component (so a
|
|
347
|
+
symlink planted inside the theme directory cannot be used to escape it), and
|
|
348
|
+
verifies the fully resolved path still starts inside the resolved
|
|
349
|
+
`themeDirectory`. Only paths this module reads directly off `theme.json`'s own
|
|
350
|
+
declared arrays are ever opened or copied, so a file physically present in
|
|
351
|
+
`themeDirectory` but never declared there can never be copied into the
|
|
352
|
+
candidate output directory. `test/theme-styling-contract.test.js` has
|
|
353
|
+
dedicated path-containment tests for `../` traversal, an absolute path, a
|
|
354
|
+
backslash-separated path, a symlink escaping the theme directory, and an
|
|
355
|
+
undeclared file never being copied.
|
|
356
|
+
|
|
357
|
+
### Media pipeline scope decisions (documented, not silently assumed)
|
|
358
|
+
|
|
359
|
+
Neither the S2 brief nor DEC-097 fixes exact derivative widths, output
|
|
360
|
+
formats/quality, or a per-publication media byte/count ceiling for _author_
|
|
361
|
+
media (DEC-097's own numeric bounds are stated for the closed
|
|
362
|
+
`gala-theme-binary-assets-v2` _theme passive-asset_ profile). This renderer
|
|
363
|
+
therefore documents its own choices in `src/core/internal/media/limits.js`
|
|
364
|
+
rather than inventing undocumented magic numbers:
|
|
365
|
+
|
|
366
|
+
- decode/resource ceilings reuse DEC-097's theme binary-asset numbers
|
|
367
|
+
(dimension, pixel-count, decoded-buffer, source-byte caps), since the
|
|
368
|
+
decode-bomb risk they defend against is identical regardless of asset class;
|
|
369
|
+
- responsive derivative widths (`320/640/960/1280/1920`), JPEG derivative
|
|
370
|
+
quality (`82`), and the per-publication image/font count and aggregate byte
|
|
371
|
+
ceilings are this renderer's own conservative choices;
|
|
372
|
+
- WebP and AVIF get bounded container/dimension validation but no full
|
|
373
|
+
VP8/VP8L/AV1 entropy decode or derivative generation — hand-authoring an
|
|
374
|
+
independently verifiable decoder for either format was judged out of
|
|
375
|
+
proportion to this task, and is flagged as a follow-up rather than attempted
|
|
376
|
+
partially.
|
|
377
|
+
|
|
378
|
+
S2-T08 adds every remaining "Required generated output" from brief S2 section 3
|
|
379
|
+
on top of S2-T06's page kinds:
|
|
380
|
+
|
|
381
|
+
- **Feeds** (`src/core/internal/feeds.js`): deterministic Atom 1.0
|
|
382
|
+
(`/feed/atom.xml`) and RSS 2.0 (`/feed/rss.xml`) documents over the exact
|
|
383
|
+
reverse-chronological `status: "published"`, `kind: "article"` selection
|
|
384
|
+
`internal/page-kinds.js`'s `index` page kind paginates (the shared
|
|
385
|
+
`selectPublishedArticles` export), every item URL absolute against
|
|
386
|
+
`buildInput.baseUrl`, bounded to the most recent 50 entries (documented scoped
|
|
387
|
+
decision — neither the brief nor DEC-097 fixes a feed item count).
|
|
388
|
+
- **Sitemap** (`src/core/internal/sitemap.js`): `/sitemap.xml` over every page
|
|
389
|
+
`internal/page-kinds.js` generated that this renderer did not itself mark
|
|
390
|
+
`noindex` (unlisted content), deterministically sorted by each entry's own
|
|
391
|
+
absolute URL, with `<lastmod>` sourced from each page's own authored
|
|
392
|
+
`publishedAt`/`updatedAt` — never build time.
|
|
393
|
+
- **Static search index** (`src/core/internal/search-index.js`):
|
|
394
|
+
`/search-index.json`, a deterministic JSON document indexing every published
|
|
395
|
+
`article`/`page` record's title, description, a bounded plain-text excerpt,
|
|
396
|
+
tags and timestamps. Its shape is a documented scoped decision (neither the
|
|
397
|
+
brief nor DEC-097 fixes one). **No runtime search JavaScript accompanies it**
|
|
398
|
+
— the Light/Dark/System appearance controller (S2-T07) remains the only
|
|
399
|
+
browser bootstrap this renderer ever emits in all of S2 (acceptance test C).
|
|
400
|
+
- **SEO/Open-Graph/Twitter/localization metadata**: every generated page's
|
|
401
|
+
`<head>` now carries a viewport meta, an optional description, an optional
|
|
402
|
+
`robots` directive (`noindex, follow` for `status: "unlisted"` content and for
|
|
403
|
+
the generated `404.html`), a canonical link, `og:*`/ `twitter:*` meta tags
|
|
404
|
+
(image resolved to an absolute content-addressed media derivative URL via
|
|
405
|
+
`internal/seo.js`, never the raw source path) and the Atom/RSS feed-discovery
|
|
406
|
+
`<link>`s. **hreflang** is a documented scope decision, not a silent omission:
|
|
407
|
+
`build-input:2.0.0` has no translation/locale-variant linkage between distinct
|
|
408
|
+
`content[]` records (see `internal/seo.js`'s module documentation), so no
|
|
409
|
+
`<link rel="alternate" hreflang="...">` is ever emitted in S2.
|
|
410
|
+
- **`404.html`**: the S2-T06 `error` page kind's pure `renderErrorPageBody`, now
|
|
411
|
+
actually wired into `renderPublication` at DEC-097's exact `errorDocumentKey`
|
|
412
|
+
(`internal/route.js#errorDocumentPath`) — `404.html` at the root, or
|
|
413
|
+
`<basePath>/404.html` otherwise.
|
|
414
|
+
- **Redirect documents**: unchanged from S2-T03
|
|
415
|
+
(`internal/route.js#renderRedirectDocument`) — every authored redirect was
|
|
416
|
+
already an ordinary generated artifact with manifest status fixed at `200`;
|
|
417
|
+
this task's own scope note is documenting that fact here, not re-implementing
|
|
418
|
+
it.
|
|
419
|
+
- **Print CSS hookup** (`src/core/internal/print-stylesheet.js`): every page's
|
|
420
|
+
`<head>` carries a `<link rel="stylesheet" media="print">` at the documented
|
|
421
|
+
conventional path `<basePath>/assets/theme/print.css`. A selected theme's own
|
|
422
|
+
compiled `print.css` bytes land there through a later theme-integration layer,
|
|
423
|
+
outside this renderer's own `build-input:2.0.0` contract —
|
|
424
|
+
`appearanceNormalized` carries only the theme's package identity, never a
|
|
425
|
+
stylesheet path.
|
|
426
|
+
|
|
427
|
+
Every route this task adds carries the schema's exact `routeClass`/ `mediaType`
|
|
428
|
+
pair (`error`, `feed` ×2, `sitemap`, `asset`), classified in `src/core/index.js`
|
|
429
|
+
by the route's own fixed output path.
|
|
430
|
+
|
|
431
|
+
## Prerequisites
|
|
432
|
+
|
|
433
|
+
- Node.js 24.18.0
|
|
434
|
+
- npm 11.16.0
|
|
435
|
+
|
|
436
|
+
Both are enforced by `package.json` `engines`/`devEngines`; `.nvmrc` and
|
|
437
|
+
`.node-version` carry the same pin.
|
|
438
|
+
|
|
439
|
+
## Commands
|
|
440
|
+
|
|
441
|
+
```sh
|
|
442
|
+
npm install
|
|
443
|
+
npm run verify
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
`npm run verify` runs, in order: Prettier format check, ESLint,
|
|
447
|
+
`tsc --checkJs --noEmit`, the dependency-cruiser
|
|
448
|
+
architecture/module-tree-absence gate, `jscpd` duplication scan, the
|
|
449
|
+
`contracts/*.jcs` canonical-form check, `node --test`, the license inventory
|
|
450
|
+
check, and `cyclonedx-npm` SBOM generation plus presence check.
|
|
451
|
+
|
|
452
|
+
Individual gates:
|
|
453
|
+
|
|
454
|
+
```sh
|
|
455
|
+
npm run format # apply Prettier
|
|
456
|
+
npm run format:check # CI check
|
|
457
|
+
npm run lint # ESLint flat config
|
|
458
|
+
npm run typecheck # tsc --checkJs --noEmit against types/index.d.ts
|
|
459
|
+
npm run architecture # dependency-cruiser: no src/modules/, no circular deps
|
|
460
|
+
npm run duplication # jscpd, 3% / 50 tokens
|
|
461
|
+
npm run contracts:generate # (re)write contracts/*.jcs placeholders
|
|
462
|
+
npm run contracts:check # fail if committed contracts drifted
|
|
463
|
+
npm test # node --test
|
|
464
|
+
npm run licenses:generate # (re)write THIRD_PARTY_LICENSES.json
|
|
465
|
+
npm run licenses:check # fail if it drifted from package-lock.json
|
|
466
|
+
npm run sbom:generate # cyclonedx-npm -> sbom.cdx.json
|
|
467
|
+
npm run sbom:check # fail if sbom.cdx.json is missing
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
## Package layout
|
|
471
|
+
|
|
472
|
+
```text
|
|
473
|
+
src/core/ # the only source root
|
|
474
|
+
contracts/render-policy.jcs # normalized-body policy identity (S2-T04, real content)
|
|
475
|
+
contracts/theme-styling-contract.jcs # published styling catalog (S2-T12, real and closed)
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
No `src/modules/` tree, module import edge, registration stub, module
|
|
479
|
+
configuration, module output or module runtime is ever admitted beside
|
|
480
|
+
`src/core/`. `test/module-tree-absence.test.js` and `.dependency-cruiser.cjs`'s
|
|
481
|
+
`only-src-core-is-a-source-root` rule both enforce this; `interactions`,
|
|
482
|
+
`whitelabel`, `newsletter` and `prism` have no module directory, package,
|
|
483
|
+
configuration schema, stub, output, runtime code, network target or
|
|
484
|
+
compatibility promise in this MVP (DEC-097 section 2).
|
|
485
|
+
|
|
486
|
+
## Consuming `@rathnasgala2/schemas`
|
|
487
|
+
|
|
488
|
+
This repository consumes `@rathnasgala2/schemas@2.11.0` from the public npm
|
|
489
|
+
registry, declared as `"@rathnasgala2/schemas": "2.11.0"` (exact pin, no range)
|
|
490
|
+
so `package-lock.json` records the resolved version and integrity hash. The
|
|
491
|
+
LOCAL-1/LOCAL-39 local-tarball convention
|
|
492
|
+
(`file:../../local-packages/rathnasgala2-schemas-*.tgz`) is retired for this
|
|
493
|
+
package now that it publishes to the registry. The 2.8.0->2.11.0 convergence
|
|
494
|
+
changes none of the four roots this renderer validates, for the same reason the
|
|
495
|
+
2.7.0-2.8.0 delta below did not. Only the `"."` export (`validateGalaDocument`,
|
|
496
|
+
`GALA_SCHEMA_IDS`) is consumed; the 2.6.0 `./runtime-origins` narrow export and
|
|
497
|
+
every other addition through 2.6.1 (new OpenAPI operations, App route/catalog
|
|
498
|
+
copy, the `PrincipalSummary`/`AuthenticatorSummary`/keyset field additions) are
|
|
499
|
+
App/API-surface changes this renderer does not read. The `"."` export's 2.2.2
|
|
500
|
+
browser-safety refactor (Node builtins replaced with browser-safe equivalents
|
|
501
|
+
behind the same public API) is byte-identical in behaviour for a Node consumer
|
|
502
|
+
such as this one. The 2.7.0-2.8.0 delta (the `generationFence` sentinel,
|
|
503
|
+
`callClassBinding` and `providerBinding` in the four deployment roots, the
|
|
504
|
+
receipt-exchange discriminators, the `201` repository-binding response, the
|
|
505
|
+
review/deployment reads and the App route catalog moves) changes none of the
|
|
506
|
+
four roots this renderer validates -- `theme-contract`, `build-input`, `lock`
|
|
507
|
+
and `artifact-manifest` are byte-identical from 2.6.1 to 2.8.0.
|
|
508
|
+
|
|
509
|
+
## Governing documents
|
|
510
|
+
|
|
511
|
+
- [Slice brief S2: author-owned publication](../../orchestration/slice-briefs/S2-author-owned-publication.md),
|
|
512
|
+
section 3 (`@rathnasgala2/template`)
|
|
513
|
+
- [DEC-094](../../orchestration/decisions/DEC-094-javascript-esm-public-packages.md):
|
|
514
|
+
JavaScript ESM for public packages
|
|
515
|
+
- [WORKSPACE.md](../../orchestration/WORKSPACE.md), sections 4-8 and 13
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"contentSecurityPolicy":"default-src 'none'; base-uri 'none'; object-src 'none'; frame-ancestors 'none'; form-action 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'none'; media-src 'self'; manifest-src 'self'; worker-src 'none'","highlighter":{"grammars":["bash","css","diff","java","javascript","json","jsx","markdown","markup","python","sql","tsx","typescript","yaml"],"package":"@11ty/eleventy-plugin-syntaxhighlight","tokenClasses":["atrule","attr-equals","attr-name","attr-value","bold","boolean","builtin","cdata","char","class-name","comment","constant","content","coord","deleted","deleted-sign","doctype","entity","for-or-select","function","function-variable","important","inserted","inserted-sign","interpolation","interpolation-punctuation","italic","key","keyword","namespace","null","number","operator","prefix","prolog","property","punctuation","regex","rule","selector","shebang","string","symbol","tag","template-punctuation","template-string","title","token","url","variable"],"version":"5.0.2"},"markdownIt":{"options":{"html":false,"linkify":false,"typographer":false},"package":"markdown-it","version":"14.3.1"},"name":"gala-render-policy","sanitizer":{"allowedAttributes":{"a":["href","rel"],"code":["class"],"h1":["id"],"h2":["id"],"h3":["id"],"h4":["id"],"h5":["id"],"h6":["id"],"img":["src","alt"],"ol":["start"],"pre":["class"],"span":["class"]},"allowedTags":["p","br","hr","h1","h2","h3","h4","h5","h6","ul","ol","li","blockquote","pre","code","strong","em","b","i","u","s","del","ins","sub","sup","mark","small","wbr","a","img","table","thead","tbody","tr","th","td","dl","dt","dd","figure","figcaption","span"],"allowedUrlSchemes":["https","http","mailto"],"package":"sanitize-html","version":"2.17.7"},"version":"2.0.0"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"attributes":[{"match":"exact-value","name":"data-gala-page-kind","role":"semantic","values":["archive","article","author","error","index","page","profile","series","tag"]},{"match":"exact-value","name":"data-gala-resolved-color-mode","role":"state","values":["dark","light"]},{"match":"exact-value","name":"data-gala-slot","role":"semantic","values":["account-intent","article-end","article-footer-ad","article-preamble","conversation","edition-selector","footer-auxiliary","footer-profile","header-actions","newsletter"]},{"match":"presence","name":"data-gala-publication-root","role":"root","values":[]}],"catalogDigest":"sha256:46b43a6d1775f88bf2ded331512bd0cf27d232e256e40a89bda773e50897877f","classSelectors":["language-bash","language-css","language-diff","language-java","language-javascript","language-json","language-jsx","language-markdown","language-markup","language-python","language-sql","language-tsx","language-typescript","language-yaml","token"],"combinators":[" "," > "," + "," ~ "],"composition":{"compoundOrder":["type","id","class","attribute","pseudo-class","pseudo-element"],"functionalSelectorArguments":"compound-only","maximumFunctionalDepth":1,"nthExpressionProfile":"gala-positive-an-plus-b-v2","rootScope":"first-compound-required"},"contractVersion":"2.0.0","functionalPseudos":["is","where","not","nth-child","nth-last-child"],"idSelectors":["gala-appearance-color-mode","main-content"],"orderedLayers":["gala-tokens","gala-components","gala-utilities","gala-print"],"profile":"gala-template-styling-contract-v2","pseudoClasses":[],"pseudoElements":["after","before","marker","selection"],"publicThemeSlotHooks":[{"hookId":"code-block","selectorAtom":"pre"},{"hookId":"code-inline","selectorAtom":"code"},{"hookId":"code-language-bash","selectorAtom":".language-bash"},{"hookId":"code-language-css","selectorAtom":".language-css"},{"hookId":"code-language-diff","selectorAtom":".language-diff"},{"hookId":"code-language-java","selectorAtom":".language-java"},{"hookId":"code-language-javascript","selectorAtom":".language-javascript"},{"hookId":"code-language-json","selectorAtom":".language-json"},{"hookId":"code-language-jsx","selectorAtom":".language-jsx"},{"hookId":"code-language-markdown","selectorAtom":".language-markdown"},{"hookId":"code-language-markup","selectorAtom":".language-markup"},{"hookId":"code-language-python","selectorAtom":".language-python"},{"hookId":"code-language-sql","selectorAtom":".language-sql"},{"hookId":"code-language-tsx","selectorAtom":".language-tsx"},{"hookId":"code-language-typescript","selectorAtom":".language-typescript"},{"hookId":"code-language-yaml","selectorAtom":".language-yaml"},{"hookId":"code-token","selectorAtom":".token"},{"hookId":"content-article","selectorAtom":"article"},{"hookId":"control-appearance-select","selectorAtom":"#gala-appearance-color-mode"},{"hookId":"control-label","selectorAtom":"label"},{"hookId":"control-option","selectorAtom":"option"},{"hookId":"control-select","selectorAtom":"select"},{"hookId":"heading-1","selectorAtom":"h1"},{"hookId":"heading-2","selectorAtom":"h2"},{"hookId":"heading-3","selectorAtom":"h3"},{"hookId":"heading-4","selectorAtom":"h4"},{"hookId":"heading-5","selectorAtom":"h5"},{"hookId":"heading-6","selectorAtom":"h6"},{"hookId":"landmark-footer","selectorAtom":"footer"},{"hookId":"landmark-header","selectorAtom":"header"},{"hookId":"landmark-main","selectorAtom":"main"},{"hookId":"landmark-main-content","selectorAtom":"#main-content"},{"hookId":"landmark-navigation","selectorAtom":"nav"},{"hookId":"media-image","selectorAtom":"img"},{"hookId":"page-archive","selectorAtom":"[data-gala-page-kind=\"archive\"]"},{"hookId":"page-article","selectorAtom":"[data-gala-page-kind=\"article\"]"},{"hookId":"page-author","selectorAtom":"[data-gala-page-kind=\"author\"]"},{"hookId":"page-error","selectorAtom":"[data-gala-page-kind=\"error\"]"},{"hookId":"page-index","selectorAtom":"[data-gala-page-kind=\"index\"]"},{"hookId":"page-page","selectorAtom":"[data-gala-page-kind=\"page\"]"},{"hookId":"page-profile","selectorAtom":"[data-gala-page-kind=\"profile\"]"},{"hookId":"page-series","selectorAtom":"[data-gala-page-kind=\"series\"]"},{"hookId":"page-tag","selectorAtom":"[data-gala-page-kind=\"tag\"]"},{"hookId":"prose-blockquote","selectorAtom":"blockquote"},{"hookId":"prose-divider","selectorAtom":"hr"},{"hookId":"prose-emphasis","selectorAtom":"em"},{"hookId":"prose-link","selectorAtom":"a"},{"hookId":"prose-list-item","selectorAtom":"li"},{"hookId":"prose-list-ordered","selectorAtom":"ol"},{"hookId":"prose-list-unordered","selectorAtom":"ul"},{"hookId":"prose-paragraph","selectorAtom":"p"},{"hookId":"prose-span","selectorAtom":"span"},{"hookId":"prose-strong","selectorAtom":"strong"},{"hookId":"prose-time","selectorAtom":"time"},{"hookId":"slot-account-intent","selectorAtom":"[data-gala-slot=\"account-intent\"]"},{"hookId":"slot-article-end","selectorAtom":"[data-gala-slot=\"article-end\"]"},{"hookId":"slot-article-footer-ad","selectorAtom":"[data-gala-slot=\"article-footer-ad\"]"},{"hookId":"slot-article-preamble","selectorAtom":"[data-gala-slot=\"article-preamble\"]"},{"hookId":"slot-conversation","selectorAtom":"[data-gala-slot=\"conversation\"]"},{"hookId":"slot-edition-selector","selectorAtom":"[data-gala-slot=\"edition-selector\"]"},{"hookId":"slot-footer-auxiliary","selectorAtom":"[data-gala-slot=\"footer-auxiliary\"]"},{"hookId":"slot-footer-profile","selectorAtom":"[data-gala-slot=\"footer-profile\"]"},{"hookId":"slot-header-actions","selectorAtom":"[data-gala-slot=\"header-actions\"]"},{"hookId":"slot-newsletter","selectorAtom":"[data-gala-slot=\"newsletter\"]"}],"publicationRootSelector":"[data-gala-publication-root]","resolvedPaletteSelectors":{"dark":"[data-gala-publication-root][data-gala-resolved-color-mode=\"dark\"]","light":"[data-gala-publication-root][data-gala-resolved-color-mode=\"light\"]"},"stylesheetLayers":{"components":"gala-components","print":"gala-print","tokens":"gala-tokens","utilities":"gala-utilities"},"templatePackage":"@rathnasgala2/template","templateVersion":"2.0.0","typeSelectors":["a","article","blockquote","code","em","footer","h1","h2","h3","h4","h5","h6","header","hr","img","label","li","main","nav","ol","option","p","pre","select","span","strong","time","ul"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rathnasgala2/template",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Deterministic Eleventy-backed static publication renderer for Galascribe portable repositories.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rathnasgala2/template.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/rathnasgala2/template/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/rathnasgala2/template#readme",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./types/index.d.ts",
|
|
18
|
+
"import": "./src/core/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"types": "./types/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"src/core/",
|
|
25
|
+
"contracts/",
|
|
26
|
+
"types/",
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"NOTICE",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": "24.18.0",
|
|
33
|
+
"npm": "11.16.0"
|
|
34
|
+
},
|
|
35
|
+
"devEngines": {
|
|
36
|
+
"runtime": {
|
|
37
|
+
"name": "node",
|
|
38
|
+
"version": "24.18.0",
|
|
39
|
+
"onFail": "error"
|
|
40
|
+
},
|
|
41
|
+
"packageManager": {
|
|
42
|
+
"name": "npm",
|
|
43
|
+
"version": "11.16.0",
|
|
44
|
+
"onFail": "error"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"packageManager": "npm@11.16.0",
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public",
|
|
50
|
+
"provenance": true
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"format": "prettier --write .",
|
|
54
|
+
"format:check": "prettier --check .",
|
|
55
|
+
"lint": "eslint .",
|
|
56
|
+
"typecheck": "tsc --project tsconfig.json",
|
|
57
|
+
"architecture": "depcruise --validate .dependency-cruiser.cjs src contracts eslint.config.js",
|
|
58
|
+
"duplication": "jscpd src scripts test eslint.config.js",
|
|
59
|
+
"contracts:generate": "node scripts/generate-contracts.mjs",
|
|
60
|
+
"contracts:check": "node scripts/generate-contracts.mjs --check",
|
|
61
|
+
"test": "node --test",
|
|
62
|
+
"licenses:generate": "node scripts/generate-license-inventory.mjs",
|
|
63
|
+
"licenses:check": "node scripts/generate-license-inventory.mjs --check",
|
|
64
|
+
"sbom:generate": "cyclonedx-npm --output-file sbom.cdx.json --output-format JSON --spec-version 1.6 && node scripts/apply-sbom-normalization.mjs",
|
|
65
|
+
"sbom:check": "node scripts/check-sbom-current.mjs",
|
|
66
|
+
"workflows:check": "node scripts/check-workflow-pins.mjs",
|
|
67
|
+
"workflows:drift": "node scripts/check-workflow-drift.mjs",
|
|
68
|
+
"schema-pin:check": "node scripts/check-no-local-schema-pin.mjs",
|
|
69
|
+
"audit": "npm audit --audit-level=high",
|
|
70
|
+
"verify": "npm run format:check && npm run lint && npm run typecheck && npm run architecture && npm run duplication && npm run contracts:check && npm run schema-pin:check && npm test && npm run licenses:check && npm run sbom:check && npm run workflows:check && npm run audit"
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"@11ty/eleventy": "3.1.6",
|
|
74
|
+
"@11ty/eleventy-plugin-syntaxhighlight": "5.0.2",
|
|
75
|
+
"@rathnasgala2/schemas": "2.11.0",
|
|
76
|
+
"jpeg-js": "0.4.4",
|
|
77
|
+
"markdown-it": "14.3.1",
|
|
78
|
+
"sanitize-html": "2.17.7"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@cyclonedx/cyclonedx-npm": "6.0.1",
|
|
82
|
+
"@eslint/js": "10.0.1",
|
|
83
|
+
"@types/markdown-it": "14.2.0",
|
|
84
|
+
"@types/node": "24.13.4",
|
|
85
|
+
"@types/sanitize-html": "2.16.1",
|
|
86
|
+
"dependency-cruiser": "18.2.0",
|
|
87
|
+
"eslint": "10.10.0",
|
|
88
|
+
"eslint-plugin-jsdoc": "64.3.9",
|
|
89
|
+
"globals": "17.12.0",
|
|
90
|
+
"jscpd": "5.2.0",
|
|
91
|
+
"jsdom": "30.0.1",
|
|
92
|
+
"parse5": "8.0.1",
|
|
93
|
+
"prettier": "3.9.6",
|
|
94
|
+
"typescript": "5.9.3"
|
|
95
|
+
}
|
|
96
|
+
}
|