@rathnasgala/theme 2.0.4 → 2.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rathnasgala/theme",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "Managed Gala static-site theme payload",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "themePackage": {
4
4
  "name": "@rathnasgala/theme",
5
- "version": "2.0.4",
5
+ "version": "2.0.5",
6
6
  "availableDesignThemes": [
7
7
  "editorial",
8
8
  "modern",
@@ -28,7 +28,7 @@
28
28
  "lib/render-markdown.js": "275b7da721aef1f68cda515673153471c0d597061e8fdf867c5f2b32c98b91db",
29
29
  "lib/seo.js": "19cf1d67d5b711b8d3a533f207f267e770ee46fd4a4e6561c5f7fd4e9c1989ff",
30
30
  "lib/share-targets.js": "458aff86fab2b62a364442cd8c612565c6e0b53eef2f20cb6237cc562b1e0731",
31
- "lib/site-config.js": "679c0eaea066bbe7569bf0211696868106dd1d3bf6118fcfb7aeb75962055384",
31
+ "lib/site-config.js": "55eccfc4eab65a58fdcb06aa53f578b7b423cc186b93cad18c30705aec219d34",
32
32
  "package-lock.json": "dc11470b1ad897c58d91745f23ff25711c593e62972ec9fb83f4e10fd77e1ecd",
33
33
  "package.json": "1b3b70f6e94391d309625cb8cef503ee92ecd282bec587643b535ae7a14c24c0",
34
34
  "scripts/build-reader.js": "743194c8dc9cfb9b6631db1732b5f36a92e3a4f905a2965c71d56e1ef50c2e45",
@@ -27,6 +27,29 @@ const CONTACT_KEYS = Object.freeze([
27
27
  const PROFILE_KEYS = Object.freeze(['displayName', 'bio', 'avatarUrl', 'profileUrl']);
28
28
  const PUBLISHER_KEYS = Object.freeze(['name', 'url', 'logoUrl']);
29
29
 
30
+ export function validateCanonicalUrlTemplate(value) {
31
+ if (value == null || value === '' || value === 'unavailable') return '';
32
+ if (typeof value !== 'string' || [...value.trim()].length > 2048) {
33
+ throw new TypeError('seo.canonicalUrlTemplate must be a string of at most 2048 characters');
34
+ }
35
+ const template = value.trim();
36
+ const count = (token) => template.split(token).length - 1;
37
+ if (count('{language}') !== 1 || count('{slug}') !== 1
38
+ || /[{}]/.test(template.replace('{language}', '').replace('{slug}', ''))) {
39
+ throw new TypeError('seo.canonicalUrlTemplate must contain {language} and {slug} exactly once');
40
+ }
41
+ let resolved;
42
+ try {
43
+ resolved = new URL(template.replace('{language}', 'en').replace('{slug}', 'example-post'));
44
+ } catch {
45
+ throw new TypeError('seo.canonicalUrlTemplate must resolve to an absolute URL');
46
+ }
47
+ if (resolved.protocol !== 'https:' || resolved.username || resolved.password || resolved.hash) {
48
+ throw new TypeError('seo.canonicalUrlTemplate must use HTTPS without credentials or a fragment');
49
+ }
50
+ return template;
51
+ }
52
+
30
53
  /*
31
54
  * `layout` and `palette` have always been required. The rest were added later and are optional:
32
55
  * a publication written before they existed is still a valid publication, and the theme falls
@@ -182,6 +205,7 @@ export async function loadSiteConfiguration({
182
205
  validatePerformanceBudgets(config?.performance?.budgets);
183
206
  config.statistics = validateStatistics(config.statistics);
184
207
  config.contact = validateContact(config.contact);
208
+ config.seo = { canonicalUrlTemplate: validateCanonicalUrlTemplate(config.seo?.canonicalUrlTemplate) };
185
209
  const profile = validateProfile(config.site);
186
210
  config.site = {
187
211
  ...(config.site ?? {}),