@fulldecent/nice-checkers-plugin 1.2.1 → 1.3.2

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/README.md CHANGED
@@ -6,7 +6,7 @@ An opinionated collection of essential HTML validation rules that promote best p
6
6
 
7
7
  ## Features
8
8
 
9
- - :white_check_mark: **Turnkey validation**: 8 rules covering SEO, security, accessibility, and best practices
9
+ - :white_check_mark: **Turnkey validation**: 11 rules covering SEO, security, accessibility, and best practices
10
10
  - :white_check_mark: **TypeScript**: full type definitions included
11
11
  - :warning: **Dual module support**: works with both ESM (`import`) and CJS (`require`) (known issue: ESM and CommonJS builds are [sometimes not building correctly](https://github.com/fulldecent/html-validate-nice-checkers/issues/6))
12
12
  - :white_check_mark: **Tree shakeable**: import only what you need
@@ -309,6 +309,55 @@ Ensures that package assets loaded from CDNs (like jsDelivr) are using the lates
309
309
  | `cacheDatabasePath` | `string` | `"cache/latest-packages.db"` | Path to the cache database file |
310
310
  | `skipUrlPatterns` | `string[]` | `[]` | Array of URL patterns to skip checking |
311
311
 
312
+ ### `nice-checkers/match-regex`
313
+
314
+ Requires page source to match all `mustMatch` regexes and none of the `mustNotMatch` regexes. This rule is off by default because it requires user-provided patterns.
315
+
316
+ Use this to enforce that specific content or HTML elements are present on every page, or to forbid certain words or patterns. All patterns are evaluated with the `s` (dotAll) flag enabled, so they can match across multiple lines.
317
+
318
+ When configuring via JSON, patterns must be given as strings. When configuring via JavaScript or TypeScript, each entry may be either a string or a `RegExp` instance. For `RegExp` inputs, all existing user flags are preserved and `s` is added if not already present.
319
+
320
+ For example, you might require a specific footer script on every page:
321
+
322
+ ```json
323
+ {
324
+ "rules": {
325
+ "nice-checkers/match-regex": [
326
+ "error",
327
+ {
328
+ "mustMatch": [
329
+ "<script src=\"/assets/global/site\\.js\\?[0-9a-f]+\" async></script>\\s*</body>"
330
+ ],
331
+ "mustNotMatch": ["naughty"]
332
+ }
333
+ ]
334
+ }
335
+ }
336
+ ```
337
+
338
+ ### Configuration
339
+
340
+ ```json
341
+ {
342
+ "rules": {
343
+ "nice-checkers/match-regex": [
344
+ "error",
345
+ {
346
+ "mustMatch": [],
347
+ "mustNotMatch": []
348
+ }
349
+ ]
350
+ }
351
+ }
352
+ ```
353
+
354
+ ### Configuration options
355
+
356
+ | Option | Type | Default | Description |
357
+ | -------------- | ---------------------- | ------- | -------------------------------------------- |
358
+ | `mustMatch` | `(string \| RegExp)[]` | `[]` | Patterns that the page source must match |
359
+ | `mustNotMatch` | `(string \| RegExp)[]` | `[]` | Patterns that the page source must not match |
360
+
312
361
  ### `nice-checkers/mailto-awesome`
313
362
 
314
363
  Enforces that `mailto:` links contain specific parameters to improve user experience. This rule ensures email links provide helpful context to users.
@@ -398,6 +447,41 @@ Note that these sources we reference have a conflict. One says that you may use
398
447
  - [Google: specify alternate language pages](https://developers.google.com/search/docs/specialty/international/localized-versions)
399
448
  - [W3C: link type 'alternate'](https://html.spec.whatwg.org/multipage/links.html#link-type-alternate)
400
449
 
450
+ ## Disabled html-validate core rules
451
+
452
+ `nice-checkers-plugin:recommended` explicitly turns off the following built-in html-validate rules because they conflict with common HTML minification tools.
453
+
454
+ ### `no-implicit-button-type` and `no-implicit-input-type`
455
+
456
+ HTML minifiers such as [`@minify-html/node`](https://github.com/wilsonzlin/minify-html) strip the `type` attribute from `<button>` and `<input>` elements when it equals the HTML-spec default value (e.g. `type="submit"` on `<button>`, `type="text"` on `<input>`). This is valid per the spec and reduces page size, but it causes false-positive warnings from these two core rules when validating **minified** output.
457
+
458
+ ```js
459
+ import { minify } from '@minify-html/node'
460
+
461
+ const html = '<button type="submit">Go</button><input type="text" name="q">'
462
+ const minified = minify(Buffer.from(html), {
463
+ /* options */
464
+ })
465
+ // → "<button>Go</button><input name=q>"
466
+ // type="submit" and type="text" are stripped as they are HTML defaults
467
+ ```
468
+
469
+ Extending `nice-checkers-plugin:recommended` disables both rules so that projects validating minified output do not need a workaround.
470
+
471
+ If you author your HTML by hand (i.e. you are **not** validating minified output) and want to enforce these rules, you can re-enable them explicitly in your own configuration:
472
+
473
+ ```json
474
+ {
475
+ "extends": ["htmlvalidate:recommended", "nice-checkers-plugin:recommended"],
476
+ "rules": {
477
+ "no-implicit-button-type": "error",
478
+ "no-implicit-input-type": "error"
479
+ }
480
+ }
481
+ ```
482
+
483
+ See [issue #23](https://github.com/fulldecent/html-validate-nice-checkers/issues/23) for the full discussion.
484
+
401
485
  ## Development
402
486
 
403
487
  This package is built with TypeScript and supports both ESM and CommonJS module systems. Thank you for contributing improvements to this project!
package/dist/index.cjs CHANGED
@@ -1,24 +1,24 @@
1
- 'use strict';var htmlValidate=require('html-validate'),child_process=require('child_process'),shellQuote=require('shell-quote'),J=require('cheerio'),te=require('better-sqlite3'),g=require('fs'),p=require('path');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var J__namespace=/*#__PURE__*/_interopNamespace(J);var te__default=/*#__PURE__*/_interopDefault(te);var g__default=/*#__PURE__*/_interopDefault(g);var p__default=/*#__PURE__*/_interopDefault(p);var $=Object.defineProperty;var d=(f,s)=>$(f,"name",{value:s,configurable:true});var B=class B extends htmlValidate.Rule{documentation(){return {description:'Requires <link rel="alternate" hreflang="..."> to use fully qualified URLs with protocol (https://).',url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.on("dom:ready",s=>{this.domReady(s);});}domReady(s){let{document:e}=s,a=e.querySelectorAll('link[rel="alternate"][hreflang]');for(let t of a){let r=t.getAttribute("href")?.value;typeof r!="string"||!r||r.includes("://")||this.report({node:t,message:`Alternate language link must use a fully qualified URL (with protocol://), found: ${r}`});}}};d(B,"AlternateLanguageUrlRule");var b=B;function v(f,s={}){let{timeoutSeconds:e=5,userAgent:a="Mozilla/5.0 (compatible; html-validate-nice-checkers)",headOnly:t=false,maxRedirs:r=0,failOnError:i=false,headers:o={}}=s,c=shellQuote.quote([f]),n=["curl","--silent","--max-time",String(e),"--max-redirs",String(r),"--user-agent",shellQuote.quote([a])];t&&n.push("--head"),i&&n.push("--fail");for(let[m,h]of Object.entries(o))n.push("--header",shellQuote.quote([`${m}: ${h}`]));n.push("--dump-header","-"),t&&n.push("--output","/dev/null"),n.push(c);let l=n.join(" ")+" || true";try{let m=child_process.execSync(l).toString(),h=m.match(/^HTTP\/[0-9.]+ (\d{3})/m),u=h?.[1]?parseInt(h[1],10):void 0,L=m.match(/^Location: (.+)/im)?.[1]?.trim(),y=m.split(/\r?\n\r?\n/),Q=y[0]||"",x=y.slice(1).join(`
1
+ 'use strict';var htmlValidate=require('html-validate'),child_process=require('child_process'),shellQuote=require('shell-quote'),$=require('cheerio'),re=require('better-sqlite3'),u=require('fs'),f=require('path');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var $__namespace=/*#__PURE__*/_interopNamespace($);var re__default=/*#__PURE__*/_interopDefault(re);var u__default=/*#__PURE__*/_interopDefault(u);var f__default=/*#__PURE__*/_interopDefault(f);var X=Object.defineProperty;var d=(p,a)=>X(p,"name",{value:a,configurable:true});var F=class F extends htmlValidate.Rule{documentation(){return {description:'Requires <link rel="alternate" hreflang="..."> to use fully qualified URLs with protocol (https://).',url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.on("dom:ready",a=>{this.domReady(a);});}domReady(a){let{document:e}=a,s=e.querySelectorAll('link[rel="alternate"][hreflang]');for(let i of s){let r=i.getAttribute("href")?.value;typeof r!="string"||!r||r.includes("://")||this.report({node:i,message:`Alternate language link must use a fully qualified URL (with protocol://), found: ${r}`});}}};d(F,"AlternateLanguageUrlRule");var b=F;function v(p,a={}){let{timeoutSeconds:e=5,userAgent:s="Mozilla/5.0 (compatible; html-validate-nice-checkers)",headOnly:i=false,maxRedirs:r=0,failOnError:t=false,headers:o={}}=a,c=shellQuote.quote([p]),n=["curl","--silent","--max-time",String(e),"--max-redirs",String(r),"--user-agent",shellQuote.quote([s])];i&&n.push("--head"),t&&n.push("--fail");for(let[m,h]of Object.entries(o))n.push("--header",shellQuote.quote([`${m}: ${h}`]));n.push("--dump-header","-"),i&&n.push("--output","/dev/null"),n.push(c);let l=n.join(" ")+" || true";try{let m=child_process.execSync(l).toString(),h=m.match(/^HTTP\/[0-9.]+ (\d{3})/m),g=h?.[1]?parseInt(h[1],10):void 0,B=m.match(/^Location: (.+)/im)?.[1]?.trim(),y=m.split(/\r?\n\r?\n/),Y=y[0]||"",R=y.slice(1).join(`
2
2
 
3
- `);return {success:u!==void 0&&u>=200&&u<400,statusCode:u,headers:Q,body:t?void 0:x,redirectTo:L}}catch(m){return {success:false,error:m instanceof Error?m.message:String(m)}}}d(v,"syncFetch");function R(f,s={}){return v(f,{...s,headOnly:true})}d(R,"syncHead");var F=class F extends htmlValidate.Rule{static schema(){return {}}documentation(){return {description:'Enforces best practices for alternate language links (rel="alternate" hreflang) in the <head> of HTML documents.',url:"https://developers.google.com/search/docs/specialty/international/localized-versions"}}setup(){this.on("dom:ready",s=>this.domReady(s));}domReady(s){let{document:e}=s,a=e.querySelector("html");a&&this.checkAlternateLanguageLinks(a);}checkAlternateLanguageLinks(s){let e=s.querySelector("head");if(!e)return;let a=e.querySelectorAll('link[rel="alternate"][hreflang]');if(a.length===0)return;let t=e.querySelector('link[rel="canonical"]');if(!t)return;let r=t.getAttribute("href")?.value;if(!r||typeof r!="string")return;let i=r,o=s.getAttribute("lang")?.value,c=false;for(let n of a){let l=n.getAttribute("href")?.value,m=typeof l=="string"?l:void 0,h=n.getAttribute("hreflang")?.value;m===i&&(c=true,o&&h!==o&&this.report({node:n,message:`Self alternate link hreflang (${h}) does not match page lang (${o})`})),(!m||!/^https?:\/\//.test(m))&&this.report({node:n,message:`Alternate link href must be fully qualified: ${m??"(empty)"}`});}c||this.report({node:e,message:"No self-link found in alternate language links (must match canonical URL)"});for(let n of a){let l=n.getAttribute("href")?.value,m=typeof l=="string"?l:void 0;if(!m||m===i)continue;let h=v(m,{timeoutSeconds:10,maxRedirs:5});if(!h.success||!h.body){this.report({node:n,message:`Alternate page ${m} is not accessible`});continue}let u=o??"",E=J__namespace.load(h.body),L=E('link[rel="alternate"][hreflang]'),y=false;L.each((Q,x)=>{let Y=E(x).attr("href"),Z=E(x).attr("hreflang");if(Y===i&&Z===u)return y=true,false}),y||this.report({node:n,message:`Reciprocal alternate link not found on ${m} (must link back to ${i} with hreflang="${u}")`});}}};d(F,"AlternateLanguageLinksRule");var P=F;var W=class W extends htmlValidate.Rule{documentation(){return {description:"Requires a <link rel='canonical'> in the <head> with a specific href format.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.on("dom:ready",s=>{this.domReady(s);});}domReady(s){let{document:e}=s,a=e.querySelector('head > link[rel="canonical"]');if(a===null){this.report({node:e.querySelector("head"),message:'<head> is missing <link rel="canonical" ...>'});return}let t=a.getAttribute("href")?.value;if(typeof t!="string"||!t){this.report({node:a,message:"<link rel='canonical'> is missing a non-empty href attribute"});return}/\.\w+$/.test(t)&&this.report({node:a,message:"Canonical link href should be extensionless (no .html, .php, etc.)"}),t.toLowerCase().endsWith("/index")&&this.report({node:a,message:'Canonical link href should be "/" instead of ending with "/index"'});}};d(W,"CanonicalLinkRule");var I=W;var re={proxyUrl:"",skipRegexes:[],cacheExpiryFoundSeconds:720*60*60,cacheExpiryNotFoundSeconds:4320*60,timeoutSeconds:5,cacheDatabasePath:"cache/external-links.db",userAgent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.9999.999 Safari/537.36",manuallyReviewedPath:"",manuallyReviewedExpirySeconds:365*24*60*60};function M(f){try{let s=new URL(f);return s.hostname=s.hostname.toLowerCase(),s.toString()}catch{return f}}d(M,"normalizeUrl");var z=class z extends htmlValidate.Rule{constructor(s){super({...re,...s}),this.skipRegexesCompiled=this.compileRegexes(this.options.skipRegexes),this.manuallyReviewedUrls=this.loadManuallyReviewedUrls();}static schema(){return {proxyUrl:{type:"string",description:"URL of a proxy server to check external links."},skipRegexes:{type:"array",items:{type:"string"},description:"An array of regex patterns (as strings) for URLs to skip."},cacheExpiryFoundSeconds:{type:"number",description:"Number of seconds to cache successful (2xx) responses."},cacheExpiryNotFoundSeconds:{type:"number",description:"Number of seconds to cache failed (non-2xx) responses."},timeoutSeconds:{type:"number",description:"Maximum time in seconds to wait for a response."},cacheDatabasePath:{type:"string",description:"File path for the SQLite cache database."},userAgent:{type:"string",description:"User-Agent string to use for requests."},manuallyReviewedPath:{type:"string",description:"Path to CSV file containing manually reviewed URLs with approval timestamps."},manuallyReviewedExpirySeconds:{type:"number",description:"Number of seconds before a manually reviewed URL approval expires (default: 365 days)."}}}documentation(){return {description:"Require all external links to be live.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.db=this.setupDatabase(),this.on("tag:ready",s=>this.tagReady(s));}setupDatabase(){let s=p__default.default.dirname(this.options.cacheDatabasePath);g__default.default.mkdirSync(s,{recursive:true});let e=new te__default.default(this.options.cacheDatabasePath);return e.exec(`
3
+ `);return {success:g!==void 0&&g>=200&&g<400,statusCode:g,headers:Y,body:i?void 0:R,redirectTo:B}}catch(m){return {success:false,error:m instanceof Error?m.message:String(m)}}}d(v,"syncFetch");function M(p,a={}){return v(p,{...a,headOnly:true})}d(M,"syncHead");var W=class W extends htmlValidate.Rule{static schema(){return {}}documentation(){return {description:'Enforces best practices for alternate language links (rel="alternate" hreflang) in the <head> of HTML documents.',url:"https://developers.google.com/search/docs/specialty/international/localized-versions"}}setup(){this.on("dom:ready",a=>this.domReady(a));}domReady(a){let{document:e}=a,s=e.querySelector("html");s&&this.checkAlternateLanguageLinks(s);}checkAlternateLanguageLinks(a){let e=a.querySelector("head");if(!e)return;let s=e.querySelectorAll('link[rel="alternate"][hreflang]');if(s.length===0)return;let i=e.querySelector('link[rel="canonical"]');if(!i)return;let r=i.getAttribute("href")?.value;if(!r||typeof r!="string")return;let t=r,o=a.getAttribute("lang")?.value,c=false;for(let n of s){let l=n.getAttribute("href")?.value,m=typeof l=="string"?l:void 0,h=n.getAttribute("hreflang")?.value;m===t&&(c=true,o&&h!==o&&this.report({node:n,message:`Self alternate link hreflang (${h}) does not match page lang (${o})`})),(!m||!/^https?:\/\//.test(m))&&this.report({node:n,message:`Alternate link href must be fully qualified: ${m??"(empty)"}`});}c||this.report({node:e,message:"No self-link found in alternate language links (must match canonical URL)"});for(let n of s){let l=n.getAttribute("href")?.value,m=typeof l=="string"?l:void 0;if(!m||m===t)continue;let h=v(m,{timeoutSeconds:10,maxRedirs:5});if(!h.success||!h.body){this.report({node:n,message:`Alternate page ${m} is not accessible`});continue}let g=o??"",x=$__namespace.load(h.body),B=x('link[rel="alternate"][hreflang]'),y=false;B.each((Y,R)=>{let Z=x(R).attr("href"),K=x(R).attr("hreflang");if(Z===t&&K===g)return y=true,false}),y||this.report({node:n,message:`Reciprocal alternate link not found on ${m} (must link back to ${t} with hreflang="${g}")`});}}};d(W,"AlternateLanguageLinksRule");var P=W;var U=class U extends htmlValidate.Rule{documentation(){return {description:"Requires a <link rel='canonical'> in the <head> with a specific href format.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.on("dom:ready",a=>{this.domReady(a);});}domReady(a){let{document:e}=a,s=e.querySelector('head > link[rel="canonical"]');if(s===null){this.report({node:e.querySelector("head"),message:'<head> is missing <link rel="canonical" ...>'});return}let i=s.getAttribute("href")?.value;if(typeof i!="string"||!i){this.report({node:s,message:"<link rel='canonical'> is missing a non-empty href attribute"});return}/\.\w+$/.test(i)&&this.report({node:s,message:"Canonical link href should be extensionless (no .html, .php, etc.)"}),i.toLowerCase().endsWith("/index")&&this.report({node:s,message:'Canonical link href should be "/" instead of ending with "/index"'});}};d(U,"CanonicalLinkRule");var I=U;var ce={proxyUrl:"",skipRegexes:[],cacheExpiryFoundSeconds:720*60*60,cacheExpiryNotFoundSeconds:4320*60,timeoutSeconds:5,cacheDatabasePath:"cache/external-links.db",userAgent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.9999.999 Safari/537.36",manuallyReviewedPath:"",manuallyReviewedExpirySeconds:365*24*60*60};function D(p){try{let a=new URL(p);return a.hostname=a.hostname.toLowerCase(),a.toString()}catch{return p}}d(D,"normalizeUrl");var V=class V extends htmlValidate.Rule{constructor(a){super({...ce,...a}),this.skipRegexesCompiled=this.compileRegexes(this.options.skipRegexes),this.manuallyReviewedUrls=this.loadManuallyReviewedUrls();}static schema(){return {proxyUrl:{type:"string",description:"URL of a proxy server to check external links."},skipRegexes:{type:"array",items:{type:"string"},description:"An array of regex patterns (as strings) for URLs to skip."},cacheExpiryFoundSeconds:{type:"number",description:"Number of seconds to cache successful (2xx) responses."},cacheExpiryNotFoundSeconds:{type:"number",description:"Number of seconds to cache failed (non-2xx) responses."},timeoutSeconds:{type:"number",description:"Maximum time in seconds to wait for a response."},cacheDatabasePath:{type:"string",description:"File path for the SQLite cache database."},userAgent:{type:"string",description:"User-Agent string to use for requests."},manuallyReviewedPath:{type:"string",description:"Path to CSV file containing manually reviewed URLs with approval timestamps."},manuallyReviewedExpirySeconds:{type:"number",description:"Number of seconds before a manually reviewed URL approval expires (default: 365 days)."}}}documentation(){return {description:"Require all external links to be live.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.db=this.setupDatabase(),this.on("tag:ready",a=>this.tagReady(a));}setupDatabase(){let a=f__default.default.dirname(this.options.cacheDatabasePath);u__default.default.mkdirSync(a,{recursive:true});let e=new re__default.default(this.options.cacheDatabasePath);return e.exec(`
4
4
  CREATE TABLE IF NOT EXISTS urls (url TEXT UNIQUE NOT NULL, status INTEGER, redirect_to TEXT, time INTEGER);
5
5
  CREATE INDEX IF NOT EXISTS time_idx ON urls (time);
6
- `),e.exec(`DELETE FROM urls WHERE status >= 200 AND status < 300 AND time < unixepoch() - ${this.options.cacheExpiryFoundSeconds}`),e.exec(`DELETE FROM urls WHERE (status < 200 OR status >= 300) AND time < unixepoch() - ${this.options.cacheExpiryNotFoundSeconds}`),e}compileRegexes(s){return s.map(e=>{try{return new RegExp(e)}catch{return console.error(`[html-validate-nice-checkers] Invalid regex pattern in 'skipRegexes' configuration: "${e}"`),null}}).filter(e=>e!==null)}loadManuallyReviewedUrls(){let s=new Map;if(!this.options.manuallyReviewedPath)return s;try{if(!this.options.manuallyReviewedPath||!g__default.default.existsSync(this.options.manuallyReviewedPath))return s;let a=g__default.default.readFileSync(this.options.manuallyReviewedPath,"utf-8").split(`
7
- `);for(let t=1;t<a.length;t++){let r=a[t];if(!r)continue;let i=r.trim();if(!i)continue;let o=i.split(",");if(o.length<2)continue;let c=o[0],n=o[1];if(!c||!n)continue;let l=c.replace(/^"|"$/g,"").trim(),m=n.replace(/^"|"$/g,"").trim();if(!l||!m)continue;let h=parseInt(m,10);isNaN(h)||s.set(M(l),h);}}catch(e){console.error(`[html-validate-nice-checkers] Error reading manually reviewed URLs from "${this.options.manuallyReviewedPath}": ${e}`);}return s}isManuallyApproved(s){let e=M(s),a=this.manuallyReviewedUrls.get(e);if(!a)return false;let t=Math.floor(Date.now()/1e3),r=a+this.options.manuallyReviewedExpirySeconds;return t<r}performCheck(s,e,a){let t=M(s),i=R(a??s,{timeoutSeconds:this.options.timeoutSeconds,userAgent:this.options.userAgent,maxRedirs:0,headers:{Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}}),o=i.statusCode??500,c=i.redirectTo??null;this.db.prepare("REPLACE INTO urls (url, status, redirect_to, time) VALUES (?, ?, ?, unixepoch())").run(t,o,c),(o<200||o>=300)&&(c?this.report({node:e,message:`External link ${s} redirects to: ${c}`}):this.report({node:e,message:`External link is broken with status ${o}: ${s}`}));}tagReady(s){let{target:e}=s,{tagName:a}=e,t=null;if(a==="a"||a==="link")t="href";else if(a==="script"||a==="img")t="src";else return;if(a==="link"){let n=e.getAttribute("rel")?.value;if(n==="canonical"||n==="alternate")return}let r=e.getAttribute(t)?.value;if(typeof r!="string"||!r)return;let i=r.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">");if(!i.startsWith("http")||this.skipRegexesCompiled.some(n=>n.test(i))||this.isManuallyApproved(i))return;let o=M(i),c=this.db.prepare("SELECT * FROM urls WHERE url = ?").get(o);if(c){if(c.status>=200&&c.status<300)return;c.redirect_to?this.report({node:e,message:`External link ${i} redirects to: ${c.redirect_to}`}):this.report({node:e,message:`External link is broken with status ${c.status}: ${i}`});return}this.options.proxyUrl?this.performCheck(i,e,`${this.options.proxyUrl}?url=${encodeURIComponent(i)}`):this.performCheck(i,e);}};d(z,"ExternalLinksRule");var C=z;var me={cacheExpiryFoundSeconds:720*60*60,cacheExpiryNotFoundSeconds:4320*60,timeoutSeconds:5,cacheDatabasePath:"cache/https-availability.db"},V=class V extends htmlValidate.Rule{constructor(s){super({...me,...s});}static schema(){return {cacheExpiryFoundSeconds:{type:"number",description:"Number of seconds to cache that a URL is available over HTTPS."},cacheExpiryNotFoundSeconds:{type:"number",description:"Number of seconds to cache that a URL is NOT available over HTTPS."},timeoutSeconds:{type:"number",description:"Maximum time in seconds to wait for a response when checking for HTTPS."},cacheDatabasePath:{type:"string",description:"File path for the SQLite cache database."}}}documentation(){return {description:"Report insecure HTTP links that are accessible via HTTPS.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.md#rules"}}setup(){this.db=this.setupDatabase(),this.on("tag:ready",s=>this.tagReady(s));}setupDatabase(){let s=p__default.default.dirname(this.options.cacheDatabasePath);g__default.default.mkdirSync(s,{recursive:true});let e=new te__default.default(this.options.cacheDatabasePath);return e.exec(`
6
+ `),e.exec(`DELETE FROM urls WHERE status >= 200 AND status < 300 AND time < unixepoch() - ${this.options.cacheExpiryFoundSeconds}`),e.exec(`DELETE FROM urls WHERE (status < 200 OR status >= 300) AND time < unixepoch() - ${this.options.cacheExpiryNotFoundSeconds}`),e}compileRegexes(a){return a.map(e=>{try{return new RegExp(e)}catch{return console.error(`[html-validate-nice-checkers] Invalid regex pattern in 'skipRegexes' configuration: "${e}"`),null}}).filter(e=>e!==null)}loadManuallyReviewedUrls(){let a=new Map;if(!this.options.manuallyReviewedPath)return a;try{if(!this.options.manuallyReviewedPath||!u__default.default.existsSync(this.options.manuallyReviewedPath))return a;let s=u__default.default.readFileSync(this.options.manuallyReviewedPath,"utf-8").split(`
7
+ `);for(let i=1;i<s.length;i++){let r=s[i];if(!r)continue;let t=r.trim();if(!t)continue;let o=t.split(",");if(o.length<2)continue;let c=o[0],n=o[1];if(!c||!n)continue;let l=c.replace(/^"|"$/g,"").trim(),m=n.replace(/^"|"$/g,"").trim();if(!l||!m)continue;let h=parseInt(m,10);isNaN(h)||a.set(D(l),h);}}catch(e){console.error(`[html-validate-nice-checkers] Error reading manually reviewed URLs from "${this.options.manuallyReviewedPath}": ${e}`);}return a}isManuallyApproved(a){let e=D(a),s=this.manuallyReviewedUrls.get(e);if(!s)return false;let i=Math.floor(Date.now()/1e3),r=s+this.options.manuallyReviewedExpirySeconds;return i<r}performCheck(a,e,s){let i=D(a),t=M(s??a,{timeoutSeconds:this.options.timeoutSeconds,userAgent:this.options.userAgent,maxRedirs:0,headers:{Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}}),o=t.statusCode??500,c=t.redirectTo??null;this.db.prepare("REPLACE INTO urls (url, status, redirect_to, time) VALUES (?, ?, ?, unixepoch())").run(i,o,c),(o<200||o>=300)&&(c?this.report({node:e,message:`External link ${a} redirects to: ${c}`}):this.report({node:e,message:`External link is broken with status ${o}: ${a}`}));}tagReady(a){let{target:e}=a,{tagName:s}=e,i=null;if(s==="a"||s==="link")i="href";else if(s==="script"||s==="img")i="src";else return;if(s==="link"){let n=e.getAttribute("rel")?.value;if(n==="canonical"||n==="alternate")return}let r=e.getAttribute(i)?.value;if(typeof r!="string"||!r)return;let t=r.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">");if(!t.startsWith("http")||this.skipRegexesCompiled.some(n=>n.test(t))||this.isManuallyApproved(t))return;let o=D(t),c=this.db.prepare("SELECT * FROM urls WHERE url = ?").get(o);if(c){if(c.status>=200&&c.status<300)return;c.redirect_to?this.report({node:e,message:`External link ${t} redirects to: ${c.redirect_to}`}):this.report({node:e,message:`External link is broken with status ${c.status}: ${t}`});return}this.options.proxyUrl?this.performCheck(t,e,`${this.options.proxyUrl}?url=${encodeURIComponent(t)}`):this.performCheck(t,e);}};d(V,"ExternalLinksRule");var C=V;var le={cacheExpiryFoundSeconds:720*60*60,cacheExpiryNotFoundSeconds:4320*60,timeoutSeconds:5,cacheDatabasePath:"cache/https-availability.db"},G=class G extends htmlValidate.Rule{constructor(a){super({...le,...a});}static schema(){return {cacheExpiryFoundSeconds:{type:"number",description:"Number of seconds to cache that a URL is available over HTTPS."},cacheExpiryNotFoundSeconds:{type:"number",description:"Number of seconds to cache that a URL is NOT available over HTTPS."},timeoutSeconds:{type:"number",description:"Maximum time in seconds to wait for a response when checking for HTTPS."},cacheDatabasePath:{type:"string",description:"File path for the SQLite cache database."}}}documentation(){return {description:"Report insecure HTTP links that are accessible via HTTPS.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.md#rules"}}setup(){this.db=this.setupDatabase(),this.on("tag:ready",a=>this.tagReady(a));}setupDatabase(){let a=f__default.default.dirname(this.options.cacheDatabasePath);u__default.default.mkdirSync(a,{recursive:true});let e=new re__default.default(this.options.cacheDatabasePath);return e.exec(`
8
8
  CREATE TABLE IF NOT EXISTS urls (
9
9
  url TEXT UNIQUE NOT NULL,
10
10
  found INTEGER NOT NULL,
11
11
  time INTEGER NOT NULL
12
12
  );
13
13
  CREATE INDEX IF NOT EXISTS time_idx ON urls (time);
14
- `),e.exec(`DELETE FROM urls WHERE found = 1 AND time < unixepoch() - ${this.options.cacheExpiryFoundSeconds}`),e.exec(`DELETE FROM urls WHERE found = 0 AND time < unixepoch() - ${this.options.cacheExpiryNotFoundSeconds}`),e}performHttpsCheck(s,e){let a=s.replace(/^http:/,"https:");R(a,{timeoutSeconds:this.options.timeoutSeconds,maxRedirs:0}).success?(this.db.prepare("REPLACE INTO urls (url, found, time) VALUES (?, 1, unixepoch())").run(s),this.report({node:e,message:`Insecure link can be upgraded to HTTPS: ${s}`})):this.db.prepare("REPLACE INTO urls (url, found, time) VALUES (?, 0, unixepoch())").run(s);}tagReady(s){let{target:e}=s,{tagName:a}=e,t=null;if(a==="a"||a==="link")t="href";else if(a==="script"||a==="img")t="src";else return;let r=e.getAttribute(t)?.value;if(typeof r!="string"||!r.startsWith("http://"))return;let i=r.replace(/&amp;/g,"&"),o=this.db.prepare("SELECT found FROM urls WHERE url = ?").get(i);if(o){o.found===1&&this.report({node:e,message:`Insecure link can be upgraded to HTTPS: ${i}`});return}this.performHttpsCheck(i,e);}};d(V,"HttpsLinksRule");var O=V;var le={webRoot:p__default.default.join(process.cwd(),"build"),alternativeExtensions:[".html",".php"],indexFile:"index.html"},G=class G extends htmlValidate.Rule{constructor(e){super({...le,...e});this.fileExistsCache=new Map;}static schema(){return {webRoot:{type:"string",description:'The root directory for resolving absolute links (starting with "/").'},alternativeExtensions:{type:"array",items:{type:"string"},description:'List of file extensions to check for extensionless links (e.g., [".html"]).'},indexFile:{type:"string",description:"The file to look for in directory links (e.g., 'index.html')."}}}documentation(){return {description:"Validate that all internal links point to existing files.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.md#rules"}}setup(){this.on("tag:ready",e=>this.tagReady(e));}doesFileExist(e){if(this.fileExistsCache.has(e))return this.fileExistsCache.get(e);if(!g__default.default.existsSync(e))return this.fileExistsCache.set(e,false),false;if(!g__default.default.lstatSync(e).isFile())return this.fileExistsCache.set(e,false),false;try{let a=p__default.default.dirname(e),t=p__default.default.basename(e),i=g__default.default.readdirSync(a).includes(t);return this.fileExistsCache.set(e,i),i}catch{return this.fileExistsCache.set(e,false),false}}checkLink(e,a){let t;if(e.startsWith("/"))t=p__default.default.join(this.options.webRoot,e);else {let r=p__default.default.dirname(a.location.filename);t=p__default.default.resolve(r,e);}if(e.endsWith("/")){let r=t;if(g__default.default.existsSync(r)&&g__default.default.lstatSync(r).isDirectory()){let i=p__default.default.join(r,this.options.indexFile);if(this.doesFileExist(i))return}}else {if(this.doesFileExist(t)||this.options.alternativeExtensions.some(r=>this.doesFileExist(t+r)))return;if(g__default.default.existsSync(t)&&g__default.default.lstatSync(t).isDirectory()){let r=p__default.default.join(t,this.options.indexFile);if(this.doesFileExist(r))return}}this.report({node:a,message:`Internal link to "${e}" is broken.`});}tagReady(e){let{target:a}=e,{tagName:t}=a,r=null;if(t==="a"||t==="link")r="href";else if(t==="script"||t==="img")r="src";else return;let i=a.getAttribute(r)?.value;if(typeof i!="string"||!i)return;let o=i.replace(/[#?].*$/,"");o&&(/^[a-z][a-z0-9+.-]*:/i.test(o)||this.checkLink(o,a));}};d(G,"InternalLinksRule");var T=G;var ye={cacheExpirySeconds:2880*60,timeoutSeconds:5,cacheDatabasePath:"cache/latest-packages.db",skipUrlPatterns:["googletagmanager.com"]},H=class H extends htmlValidate.Rule{constructor(s){super({...ye,...s});}static schema(){return {cacheExpirySeconds:{type:"number",description:"Number of seconds to cache package version lookup results."},timeoutSeconds:{type:"number",description:"Maximum time in seconds to wait for a response from the package registry API."},cacheDatabasePath:{type:"string",description:"File path for the SQLite cache database."},skipUrlPatterns:{type:"array",items:{type:"string"},description:"An array of substrings to identify URLs that should be ignored."}}}documentation(){return {description:"Ensures that package assets loaded from a CDN are the latest version and have SRI attributes.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.db=this.setupDatabase(),this.on("tag:ready",s=>this.tagReady(s));}setupDatabase(){let s=p__default.default.dirname(this.options.cacheDatabasePath);g__default.default.mkdirSync(s,{recursive:true});let e=new te__default.default(this.options.cacheDatabasePath);return e.exec(`
14
+ `),e.exec(`DELETE FROM urls WHERE found = 1 AND time < unixepoch() - ${this.options.cacheExpiryFoundSeconds}`),e.exec(`DELETE FROM urls WHERE found = 0 AND time < unixepoch() - ${this.options.cacheExpiryNotFoundSeconds}`),e}performHttpsCheck(a,e){let s=a.replace(/^http:/,"https:");M(s,{timeoutSeconds:this.options.timeoutSeconds,maxRedirs:0}).success?(this.db.prepare("REPLACE INTO urls (url, found, time) VALUES (?, 1, unixepoch())").run(a),this.report({node:e,message:`Insecure link can be upgraded to HTTPS: ${a}`})):this.db.prepare("REPLACE INTO urls (url, found, time) VALUES (?, 0, unixepoch())").run(a);}tagReady(a){let{target:e}=a,{tagName:s}=e,i=null;if(s==="a"||s==="link")i="href";else if(s==="script"||s==="img")i="src";else return;let r=e.getAttribute(i)?.value;if(typeof r!="string"||!r.startsWith("http://"))return;let t=r.replace(/&amp;/g,"&"),o=this.db.prepare("SELECT found FROM urls WHERE url = ?").get(t);if(o){o.found===1&&this.report({node:e,message:`Insecure link can be upgraded to HTTPS: ${t}`});return}this.performHttpsCheck(t,e);}};d(G,"HttpsLinksRule");var O=G;var pe={webRoot:f__default.default.join(process.cwd(),"build"),alternativeExtensions:[".html",".php"],indexFile:"index.html"},H=class H extends htmlValidate.Rule{constructor(e){super({...pe,...e});this.fileExistsCache=new Map;this.fileIdsCache=new Map;}static schema(){return {webRoot:{type:"string",description:'The root directory for resolving absolute links (starting with "/").'},alternativeExtensions:{type:"array",items:{type:"string"},description:'List of file extensions to check for extensionless links (e.g., [".html"]).'},indexFile:{type:"string",description:"The file to look for in directory links (e.g., 'index.html')."}}}documentation(){return {description:"Validate that all internal links point to existing files.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.md#rules"}}setup(){this.on("tag:ready",e=>this.tagReady(e));}doesFileExist(e){if(this.fileExistsCache.has(e))return this.fileExistsCache.get(e);if(!u__default.default.existsSync(e))return this.fileExistsCache.set(e,false),false;if(!u__default.default.lstatSync(e).isFile())return this.fileExistsCache.set(e,false),false;try{let s=f__default.default.dirname(e),i=f__default.default.basename(e),t=u__default.default.readdirSync(s).includes(i);return this.fileExistsCache.set(e,t),t}catch{return this.fileExistsCache.set(e,false),false}}checkLink(e,s,i){let r;if(e.startsWith("/"))r=f__default.default.join(this.options.webRoot,e);else {let t=f__default.default.dirname(i.location.filename);r=f__default.default.resolve(t,e);}if(e.endsWith("/")){let t=r;if(u__default.default.existsSync(t)&&u__default.default.lstatSync(t).isDirectory()){let o=f__default.default.join(t,this.options.indexFile);if(this.doesFileExist(o)){s&&this.checkFragment(s,o,f__default.default.basename(t)+"/",i);return}}}else {if(this.doesFileExist(r)){s&&this.checkFragment(s,r,e,i);return}for(let t of this.options.alternativeExtensions)if(this.doesFileExist(r+t)){s&&this.checkFragment(s,r+t,e,i);return}if(u__default.default.existsSync(r)&&u__default.default.lstatSync(r).isDirectory()){let t=f__default.default.join(r,this.options.indexFile);if(this.doesFileExist(t)){s&&this.checkFragment(s,t,e,i);return}}}this.report({node:i,message:`Internal link to "${e}" is broken.`});}getFileIds(e){if(this.fileIdsCache.has(e))return this.fileIdsCache.get(e);let s=new Set;try{let i=u__default.default.readFileSync(e,"utf-8"),r=/\sid=(["'])([^"']*)\1/gi,t;for(;(t=r.exec(i))!==null;)t[2]&&s.add(t[2]);}catch{}return this.fileIdsCache.set(e,s),s}checkFragment(e,s,i,r){this.getFileIds(s).has(e)||this.report({node:r,message:`Fragment "#${e}" not found in "${i}".`});}checkSelfFragment(e,s){let i=s.location.filename;this.getFileIds(i).has(e)||this.report({node:s,message:`Fragment "#${e}" not found in current document.`});}tagReady(e){let{target:s}=e,{tagName:i}=s,r=null;if(i==="a"||i==="link")r="href";else if(i==="script"||i==="img")r="src";else return;let t=s.getAttribute(r)?.value;if(typeof t!="string"||!t)return;let c=t.match(/#([^?]*)/)?.[1]??null,n=t.replace(/[#?].*$/,"");if(!n&&c){this.checkSelfFragment(c,s);return}n&&(/^[a-z][a-z0-9+.-]*:/i.test(n)||this.checkLink(n,c,s));}};d(H,"InternalLinksRule");var T=H;var ve={cacheExpirySeconds:2880*60,timeoutSeconds:5,cacheDatabasePath:"cache/latest-packages.db",skipUrlPatterns:["googletagmanager.com"]},j=class j extends htmlValidate.Rule{constructor(a){super({...ve,...a});}static schema(){return {cacheExpirySeconds:{type:"number",description:"Number of seconds to cache package version lookup results."},timeoutSeconds:{type:"number",description:"Maximum time in seconds to wait for a response from the package registry API."},cacheDatabasePath:{type:"string",description:"File path for the SQLite cache database."},skipUrlPatterns:{type:"array",items:{type:"string"},description:"An array of substrings to identify URLs that should be ignored."}}}documentation(){return {description:"Ensures that package assets loaded from a CDN are the latest version and have SRI attributes.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.db=this.setupDatabase(),this.on("tag:ready",a=>this.tagReady(a));}setupDatabase(){let a=f__default.default.dirname(this.options.cacheDatabasePath);u__default.default.mkdirSync(a,{recursive:true});let e=new re__default.default(this.options.cacheDatabasePath);return e.exec(`
15
15
  CREATE TABLE IF NOT EXISTS latest_packages (
16
16
  url TEXT UNIQUE NOT NULL,
17
17
  current INTEGER NOT NULL,
18
18
  time INTEGER NOT NULL
19
19
  );
20
20
  CREATE INDEX IF NOT EXISTS time_idx ON latest_packages (time);
21
- `),e.exec(`DELETE FROM latest_packages WHERE time < unixepoch() - ${this.options.cacheExpirySeconds}`),e}performPackageCheck(s,e){let a=s.match(/cdn\.jsdelivr\.net\/npm\/(@?[^/]+)@([^/]+)/);if(!a)return;let t=a[1],r=a[2],i=`https://data.jsdelivr.com/v1/package/npm/${t}`,o=v(i,{timeoutSeconds:this.options.timeoutSeconds,maxRedirs:0});if(!o.success||!o.body){console.error(`[html-validate-latest-packages] Error checking package version for ${s}: ${o.error??"Unknown error"}`);return}try{let c=JSON.parse(o.body);c&&c.tags&&Object.values(c.tags).includes(r)?this.db.prepare("REPLACE INTO latest_packages (url, current, time) VALUES (?, 1, unixepoch())").run(s):(this.db.prepare("REPLACE INTO latest_packages (url, current, time) VALUES (?, 0, unixepoch())").run(s),this.report({node:e,message:`Package "${t}" is not using a current version tag. Found "${r}", but latest is "${c.tags.latest}".`}));}catch(c){let n=c instanceof Error?c.message:String(c);console.error(`[html-validate-latest-packages] Error parsing package info for ${s}: ${n}`);}}tagReady(s){let{target:e}=s,{tagName:a}=s.target,t;if(a==="script"){let i=e.getAttribute("src")?.value;typeof i=="string"&&(t=i);}else if(a==="link"&&e.getAttribute("rel")?.value==="stylesheet"){let i=e.getAttribute("href")?.value;typeof i=="string"&&(t=i);}else return;if(!t||!t.startsWith("http")||this.options.skipUrlPatterns.some(i=>t.includes(i)))return;(!e.hasAttribute("integrity")||!e.hasAttribute("crossorigin"))&&this.report({node:e,message:`Package resource is missing required "integrity" and "crossorigin" attributes: ${t}`});let r=this.db.prepare("SELECT current FROM latest_packages WHERE url = ?").get(t);if(r){r.current===0&&this.report({node:e,message:`Package is using an outdated version: ${t}`});return}this.performPackageCheck(t,e);}};d(H,"LatestPackagesRule");var A=H;var ve={requiredParameters:["subject","body"]},j=class j extends htmlValidate.Rule{constructor(s){super({...ve,...s});}static schema(){return {requiredParameters:{type:"array",items:{type:"string"},description:'An array of parameters that must be present in a mailto: link (e.g., ["subject", "body"]).'}}}documentation(){return {description:"Enforce that mailto: links contain specific parameters.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.on("tag:ready",s=>this.tagReady(s));}tagReady(s){let{target:e}=s;if(e.tagName!=="a")return;let a=e.getAttribute("href")?.value;if(!(typeof a!="string"||!a)&&a.startsWith("mailto:"))try{let t=a.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">"),r=new URL(t),i=this.options.requiredParameters.filter(o=>!r.searchParams.has(o));i.length>0&&this.report({node:e,message:`mailto: link is missing required parameters: ${i.join(", ")}`});}catch{this.report({node:e,message:"mailto: link is malformed and could not be parsed."});}}};d(j,"MailtoAwesomeRule");var w=j;var q=class q extends htmlValidate.Rule{documentation(){return {description:"Disallow script tag with src including jQuery",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.on("tag:ready",s=>{let{target:e}=s;if(e.tagName!=="script")return;let a=e.getAttribute("src")?.value;typeof a!="string"||!a||a.toLowerCase().includes("jquery")&&this.report({node:e,message:"Script tag with src including jQuery is not allowed"});});}};d(q,"NoJqueryRule");var S=q;var D={"@context":{brick:"https://brickschema.org/schema/Brick#",csvw:"http://www.w3.org/ns/csvw#",dc:"http://purl.org/dc/elements/1.1/",dcam:"http://purl.org/dc/dcam/",dcat:"http://www.w3.org/ns/dcat#",dcmitype:"http://purl.org/dc/dcmitype/",dcterms:"http://purl.org/dc/terms/",doap:"http://usefulinc.com/ns/doap#",foaf:"http://xmlns.com/foaf/0.1/",odrl:"http://www.w3.org/ns/odrl/2/",org:"http://www.w3.org/ns/org#",owl:"http://www.w3.org/2002/07/owl#",prof:"http://www.w3.org/ns/dx/prof/",prov:"http://www.w3.org/ns/prov#",qb:"http://purl.org/linked-data/cube#",rdf:"http://www.w3.org/1999/02/22-rdf-syntax-ns#",rdfs:"http://www.w3.org/2000/01/rdf-schema#",schema:"https://schema.org/",sh:"http://www.w3.org/ns/shacl#",skos:"http://www.w3.org/2004/02/skos/core#",sosa:"http://www.w3.org/ns/sosa/",ssn:"http://www.w3.org/ns/ssn/",time:"http://www.w3.org/2006/time#",vann:"http://purl.org/vocab/vann/",void:"http://rdfs.org/ns/void#",xsd:"http://www.w3.org/2001/XMLSchema#"},"@graph":[{"@id":"schema:option","@type":"rdf:Property","rdfs:comment":"A sub property of object. The options subject to this action.","rdfs:label":"option","rdfs:subPropertyOf":{"@id":"schema:object"},"schema:domainIncludes":{"@id":"schema:ChooseAction"},"schema:rangeIncludes":[{"@id":"schema:Text"},{"@id":"schema:Thing"}],"schema:supersededBy":{"@id":"schema:actionOption"}},{"@id":"schema:minimumPaymentDue","@type":"rdf:Property","rdfs:comment":"The minimum payment required at this time.","rdfs:label":"minimumPaymentDue","schema:domainIncludes":{"@id":"schema:Invoice"},"schema:rangeIncludes":[{"@id":"schema:PriceSpecification"},{"@id":"schema:MonetaryAmount"}]},{"@id":"schema:DJMixAlbum","@type":"schema:MusicAlbumProductionType","rdfs:comment":"DJMixAlbum.","rdfs:label":"DJMixAlbum","schema:contributor":{"@id":"https://schema.org/docs/collab/MBZ"}},{"@id":"schema:Nonprofit501c27","@type":"schema:USNonprofitType","rdfs:comment":"Nonprofit501c27: Non-profit type referring to State-Sponsored Workers' Compensation Reinsurance Organizations.","rdfs:label":"Nonprofit501c27","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2543"}},{"@id":"schema:includedDataCatalog","@type":"rdf:Property","rdfs:comment":"A data catalog which contains this dataset (this property was previously 'catalog', preferred name is now 'includedInDataCatalog').","rdfs:label":"includedDataCatalog","schema:domainIncludes":{"@id":"schema:Dataset"},"schema:rangeIncludes":{"@id":"schema:DataCatalog"},"schema:supersededBy":{"@id":"schema:includedInDataCatalog"}},{"@id":"schema:UKTrust","@type":"schema:UKNonprofitType","rdfs:comment":"UKTrust: Non-profit type referring to a UK trust.","rdfs:label":"UKTrust","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2543"}},{"@id":"schema:discount","@type":"rdf:Property","rdfs:comment":"Any discount applied (to an Order).","rdfs:label":"discount","schema:domainIncludes":{"@id":"schema:Order"},"schema:rangeIncludes":[{"@id":"schema:Text"},{"@id":"schema:Number"}]},{"@id":"schema:printPage","@type":"rdf:Property","rdfs:comment":"If this NewsArticle appears in print, this field indicates the name of the page on which the article is found. Please note that this field is intended for the exact page name (e.g. A5, B18).","rdfs:label":"printPage","schema:domainIncludes":{"@id":"schema:NewsArticle"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:muscleAction","@type":"rdf:Property","rdfs:comment":"The movement the muscle generates.","rdfs:label":"muscleAction","schema:domainIncludes":{"@id":"schema:Muscle"},"schema:isPartOf":{"@id":"https://health-lifesci.schema.org"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:illustrator","@type":"rdf:Property","rdfs:comment":"The illustrator of the book.","rdfs:label":"illustrator","schema:domainIncludes":{"@id":"schema:Book"},"schema:rangeIncludes":{"@id":"schema:Person"}},{"@id":"schema:contentType","@type":"rdf:Property","rdfs:comment":"The supported content type(s) for an EntryPoint response.","rdfs:label":"contentType","schema:domainIncludes":{"@id":"schema:EntryPoint"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:servesCuisine","@type":"rdf:Property","rdfs:comment":"The cuisine of the restaurant.","rdfs:label":"servesCuisine","schema:domainIncludes":{"@id":"schema:FoodEstablishment"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:distance","@type":"rdf:Property","rdfs:comment":"The distance travelled, e.g. exercising or travelling.","rdfs:label":"distance","schema:domainIncludes":[{"@id":"schema:ExerciseAction"},{"@id":"schema:TravelAction"}],"schema:rangeIncludes":{"@id":"schema:Distance"}},{"@id":"schema:containedInPlace","@type":"rdf:Property","rdfs:comment":"The basic containment relation between a place and one that contains it.","rdfs:label":"containedInPlace","schema:domainIncludes":{"@id":"schema:Place"},"schema:inverseOf":{"@id":"schema:containsPlace"},"schema:rangeIncludes":{"@id":"schema:Place"}},{"@id":"schema:ReturnAtKiosk","@type":"schema:ReturnMethodEnumeration","rdfs:comment":"Specifies that product returns must be made at a kiosk.","rdfs:label":"ReturnAtKiosk","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2880"}},{"@id":"schema:broker","@type":"rdf:Property","rdfs:comment":"An entity that arranges for an exchange between a buyer and a seller. In most cases a broker never acquires or releases ownership of a product or service involved in an exchange. If it is not clear whether an entity is a broker, seller, or buyer, the latter two terms are preferred.","rdfs:label":"broker","schema:domainIncludes":[{"@id":"schema:Order"},{"@id":"schema:Reservation"},{"@id":"schema:Service"},{"@id":"schema:Invoice"}],"schema:rangeIncludes":[{"@id":"schema:Person"},{"@id":"schema:Organization"}]},{"@id":"schema:permittedUsage","@type":"rdf:Property","rdfs:comment":"Indications regarding the permitted usage of the accommodation.","rdfs:label":"permittedUsage","schema:contributor":{"@id":"https://schema.org/docs/collab/STI_Accommodation_Ontology"},"schema:domainIncludes":{"@id":"schema:Accommodation"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:AerobicActivity","@type":"schema:PhysicalActivityCategory","rdfs:comment":"Physical activity of relatively low intensity that depends primarily on the aerobic energy-generating process; during activity, the aerobic metabolism uses oxygen to adequately meet energy demands during exercise.","rdfs:label":"AerobicActivity","schema:isPartOf":{"@id":"https://health-lifesci.schema.org"}},{"@id":"schema:totalHistoricalEnrollment","@type":"rdf:Property","rdfs:comment":"The total number of students that have enrolled in the history of the course.","rdfs:label":"totalHistoricalEnrollment","schema:domainIncludes":{"@id":"schema:Course"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:Integer"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/3281"}},{"@id":"schema:WearableMeasurementHeight","@type":"schema:WearableMeasurementTypeEnumeration","rdfs:comment":"Measurement of the height, for example the heel height of a shoe.","rdfs:label":"WearableMeasurementHeight","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2811"}},{"@id":"schema:Clip","@type":"rdfs:Class","rdfs:comment":"A short TV or radio program or a segment/part of a program.","rdfs:label":"Clip","rdfs:subClassOf":{"@id":"schema:CreativeWork"}},{"@id":"schema:WatchAction","@type":"rdfs:Class","rdfs:comment":"The act of consuming dynamic/moving visual content.","rdfs:label":"WatchAction","rdfs:subClassOf":{"@id":"schema:ConsumeAction"}},{"@id":"schema:issuedBy","@type":"rdf:Property","rdfs:comment":"The organization issuing the item, for example a [[Permit]], [[Ticket]], or [[Certification]].","rdfs:label":"issuedBy","schema:domainIncludes":[{"@id":"schema:Permit"},{"@id":"schema:Ticket"},{"@id":"schema:Certification"}],"schema:rangeIncludes":{"@id":"schema:Organization"}},{"@id":"schema:DemoAlbum","@type":"schema:MusicAlbumProductionType","rdfs:comment":"DemoAlbum.","rdfs:label":"DemoAlbum","schema:contributor":{"@id":"https://schema.org/docs/collab/MBZ"}},{"@id":"schema:hasMemberProgram","@type":"rdf:Property","rdfs:comment":"MemberProgram offered by an Organization, for example an eCommerce merchant or an airline.","rdfs:label":"hasMemberProgram","schema:domainIncludes":{"@id":"schema:Organization"},"schema:rangeIncludes":{"@id":"schema:MemberProgram"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/3563"}},{"@id":"schema:hasEnergyEfficiencyCategory","@type":"rdf:Property","rdfs:comment":"Defines the energy efficiency Category (which could be either a rating out of range of values or a yes/no certification) for a product according to an international energy efficiency standard.","rdfs:label":"hasEnergyEfficiencyCategory","schema:domainIncludes":{"@id":"schema:EnergyConsumptionDetails"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:EnergyEfficiencyEnumeration"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2670"}},{"@id":"schema:rangeIncludes","@type":"rdf:Property","rdfs:comment":"Relates a property to a class that constitutes (one of) the expected type(s) for values of the property.","rdfs:label":"rangeIncludes","schema:domainIncludes":{"@id":"schema:Property"},"schema:isPartOf":{"@id":"https://meta.schema.org"},"schema:rangeIncludes":{"@id":"schema:Class"}},{"@id":"schema:contentLocation","@type":"rdf:Property","rdfs:comment":"The location depicted or described in the content. For example, the location in a photograph or painting.","rdfs:label":"contentLocation","schema:domainIncludes":{"@id":"schema:CreativeWork"},"schema:rangeIncludes":{"@id":"schema:Place"}},{"@id":"schema:NonprofitANBI","@type":"schema:NLNonprofitType","rdfs:comment":"NonprofitANBI: Non-profit type referring to a Public Benefit Organization (NL).","rdfs:label":"NonprofitANBI","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2543"}},{"@id":"schema:ExerciseGym","@type":"rdfs:Class","rdfs:comment":"A gym.","rdfs:label":"ExerciseGym","rdfs:subClassOf":{"@id":"schema:SportsActivityLocation"}},{"@id":"schema:MusicPlaylist","@type":"rdfs:Class","rdfs:comment":"A collection of music tracks in playlist form.","rdfs:label":"MusicPlaylist","rdfs:subClassOf":{"@id":"schema:CreativeWork"}},{"@id":"schema:weightPercentage","@type":"rdf:Property","rdfs:comment":"Fraction of the weight that is used to compute the shipping price.","rdfs:label":"weightPercentage","schema:domainIncludes":{"@id":"schema:ShippingRateSettings"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:Number"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/3617"}},{"@id":"schema:landlord","@type":"rdf:Property","rdfs:comment":"A sub property of participant. The owner of the real estate property.","rdfs:label":"landlord","rdfs:subPropertyOf":{"@id":"schema:participant"},"schema:domainIncludes":{"@id":"schema:RentAction"},"schema:rangeIncludes":[{"@id":"schema:Organization"},{"@id":"schema:Person"}]},{"@id":"schema:recordedAt","@type":"rdf:Property","rdfs:comment":"The Event where the CreativeWork was recorded. The CreativeWork may capture all or part of the event.","rdfs:label":"recordedAt","schema:domainIncludes":{"@id":"schema:CreativeWork"},"schema:inverseOf":{"@id":"schema:recordedIn"},"schema:rangeIncludes":{"@id":"schema:Event"}},{"@id":"schema:DigitalFormat","@type":"schema:MusicReleaseFormatType","rdfs:comment":"DigitalFormat.","rdfs:label":"DigitalFormat","schema:contributor":{"@id":"https://schema.org/docs/collab/MBZ"}},{"@id":"schema:EmployeeRole","@type":"rdfs:Class","rdfs:comment":"A subclass of OrganizationRole used to describe employee relationships.","rdfs:label":"EmployeeRole","rdfs:subClassOf":{"@id":"schema:OrganizationRole"}},{"@id":"schema:RejectAction","@type":"rdfs:Class","rdfs:comment":"The act of rejecting to/adopting an object.\\n\\nRelated actions:\\n\\n* [[AcceptAction]]: The antonym of RejectAction.","rdfs:label":"RejectAction","rdfs:subClassOf":{"@id":"schema:AllocateAction"}},{"@id":"schema:MerchantReturnFiniteReturnWindow","@type":"schema:MerchantReturnEnumeration","rdfs:comment":"Specifies that there is a finite window for product returns.","rdfs:label":"MerchantReturnFiniteReturnWindow","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2288"}},{"@id":"schema:ActiveActionStatus","@type":"schema:ActionStatusType","rdfs:comment":"An in-progress action (e.g., while watching the movie, or driving to a location).","rdfs:label":"ActiveActionStatus"},{"@id":"schema:EventVenue","@type":"rdfs:Class","rdfs:comment":"An event venue.","rdfs:label":"EventVenue","rdfs:subClassOf":{"@id":"schema:CivicStructure"}},{"@id":"schema:FreeReturn","@type":"schema:ReturnFeesEnumeration","rdfs:comment":"Specifies that product returns are free of charge for the customer.","rdfs:label":"FreeReturn","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2880"}},{"@id":"schema:frequency","@type":"rdf:Property","rdfs:comment":"How often the dose is taken, e.g. 'daily'.","rdfs:label":"frequency","schema:domainIncludes":{"@id":"schema:DoseSchedule"},"schema:isPartOf":{"@id":"https://health-lifesci.schema.org"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:polygon","@type":"rdf:Property","rdfs:comment":"A polygon is the area enclosed by a point-to-point path for which the starting and ending points are the same. A polygon is expressed as a series of four or more space delimited points where the first and final points are identical.","rdfs:label":"polygon","schema:domainIncludes":{"@id":"schema:GeoShape"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:isTierOf","@type":"rdf:Property","rdfs:comment":"The member program this tier is a part of.","rdfs:label":"isTierOf","schema:domainIncludes":{"@id":"schema:MemberProgramTier"},"schema:inverseOf":{"@id":"schema:hasTiers"},"schema:rangeIncludes":{"@id":"schema:MemberProgram"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/3563"}},{"@id":"schema:ScheduleAction","@type":"rdfs:Class","rdfs:comment":"Scheduling future actions, events, or tasks.\\n\\nRelated actions:\\n\\n* [[ReserveAction]]: Unlike ReserveAction, ScheduleAction allocates future actions (e.g. an event, a task, etc) towards a time slot / spatial allocation.","rdfs:label":"ScheduleAction","rdfs:subClassOf":{"@id":"schema:PlanAction"}},{"@id":"schema:itemDefectReturnLabelSource","@type":"rdf:Property","rdfs:comment":"The method (from an enumeration) by which the customer obtains a return shipping label for a defect product.","rdfs:label":"itemDefectReturnLabelSource","schema:domainIncludes":{"@id":"schema:MerchantReturnPolicy"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:ReturnLabelSourceEnumeration"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2880"}},{"@id":"schema:MedicalCode","@type":"rdfs:Class","rdfs:comment":"A code for a medical entity.","rdfs:label":"MedicalCode","rdfs:subClassOf":[{"@id":"schema:MedicalIntangible"},{"@id":"schema:CategoryCode"}],"schema:isPartOf":{"@id":"https://health-lifesci.schema.org"}},{"@id":"schema:isInvolvedInBiologicalProcess","@type":"rdf:Property","rdfs:comment":"Biological process this BioChemEntity is involved in; please use PropertyValue if you want to include any evidence.","rdfs:label":"isInvolvedInBiologicalProcess","schema:domainIncludes":{"@id":"schema:BioChemEntity"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":[{"@id":"schema:PropertyValue"},{"@id":"schema:URL"},{"@id":"schema:DefinedTerm"}],"schema:source":{"@id":"http://www.bioschemas.org/BioChemEntity"}},{"@id":"schema:productionDate","@type":"rdf:Property","rdfs:comment":"The date of production of the item, e.g. vehicle.","rdfs:label":"productionDate","schema:contributor":{"@id":"https://schema.org/docs/collab/Automotive_Ontology_Working_Group"},"schema:domainIncludes":[{"@id":"schema:Product"},{"@id":"schema:Vehicle"}],"schema:rangeIncludes":{"@id":"schema:Date"}},{"@id":"schema:emissionsCO2","@type":"rdf:Property","rdfs:comment":'The CO2 emissions in g/km. When used in combination with a QuantitativeValue, put "g/km" into the unitText property of that value, since there is no UN/CEFACT Common Code for "g/km".',"rdfs:label":"emissionsCO2","schema:contributor":{"@id":"https://schema.org/docs/collab/Automotive_Ontology_Working_Group"},"schema:domainIncludes":{"@id":"schema:Vehicle"},"schema:isPartOf":{"@id":"https://auto.schema.org"},"schema:rangeIncludes":{"@id":"schema:Number"}},{"@id":"schema:legislationDateVersion","@type":"rdf:Property","owl:equivalentProperty":{"@id":"http://data.europa.eu/eli/ontology#version_date"},"rdfs:comment":'The point-in-time at which the provided description of the legislation is valid (e.g.: when looking at the law on the 2016-04-07 (= dateVersion), I get the consolidation of 2015-04-12 of the "National Insurance Contributions Act 2015")',"rdfs:label":"legislationDateVersion","schema:contributor":{"@id":"https://schema.org/docs/collab/ELI"},"schema:domainIncludes":{"@id":"schema:Legislation"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:Date"},"schema:source":[{"@id":"https://op.europa.eu/en/web/eu-vocabularies/model/-/resource/dataset/eli"},{"@id":"https://github.com/schemaorg/schemaorg/issues/1156"}],"skos:exactMatch":{"@id":"http://data.europa.eu/eli/ontology#version_date"}},{"@id":"schema:Hotel","@type":"rdfs:Class","rdfs:comment":`A hotel is an establishment that provides lodging paid on a short-term basis (source: Wikipedia, the free encyclopedia, see http://en.wikipedia.org/wiki/Hotel).
21
+ `),e.exec(`DELETE FROM latest_packages WHERE time < unixepoch() - ${this.options.cacheExpirySeconds}`),e}performPackageCheck(a,e){let s=a.match(/cdn\.jsdelivr\.net\/npm\/(@?[^/]+)@([^/]+)/);if(!s)return;let i=s[1],r=s[2],t=`https://data.jsdelivr.com/v1/package/npm/${i}`,o=v(t,{timeoutSeconds:this.options.timeoutSeconds,maxRedirs:0});if(!o.success||!o.body){console.error(`[html-validate-latest-packages] Error checking package version for ${a}: ${o.error??"Unknown error"}`);return}try{let c=JSON.parse(o.body);c&&c.tags&&Object.values(c.tags).includes(r)?this.db.prepare("REPLACE INTO latest_packages (url, current, time) VALUES (?, 1, unixepoch())").run(a):(this.db.prepare("REPLACE INTO latest_packages (url, current, time) VALUES (?, 0, unixepoch())").run(a),this.report({node:e,message:`Package "${i}" is not using a current version tag. Found "${r}", but latest is "${c.tags.latest}".`}));}catch(c){let n=c instanceof Error?c.message:String(c);console.error(`[html-validate-latest-packages] Error parsing package info for ${a}: ${n}`);}}tagReady(a){let{target:e}=a,{tagName:s}=a.target,i;if(s==="script"){let t=e.getAttribute("src")?.value;typeof t=="string"&&(i=t);}else if(s==="link"&&e.getAttribute("rel")?.value==="stylesheet"){let t=e.getAttribute("href")?.value;typeof t=="string"&&(i=t);}else return;if(!i||!i.startsWith("http")||this.options.skipUrlPatterns.some(t=>i.includes(t)))return;(!e.hasAttribute("integrity")||!e.hasAttribute("crossorigin"))&&this.report({node:e,message:`Package resource is missing required "integrity" and "crossorigin" attributes: ${i}`});let r=this.db.prepare("SELECT current FROM latest_packages WHERE url = ?").get(i);if(r){r.current===0&&this.report({node:e,message:`Package is using an outdated version: ${i}`});return}this.performPackageCheck(i,e);}};d(j,"LatestPackagesRule");var A=j;var Ie={mustMatch:[],mustNotMatch:[]},q=class q extends htmlValidate.Rule{constructor(a){super({...Ie,...a}),this.mustMatchCompiled=this.compilePatterns(this.options.mustMatch,"mustMatch"),this.mustNotMatchCompiled=this.compilePatterns(this.options.mustNotMatch,"mustNotMatch");}compilePatterns(a,e){return a.map(s=>{try{if(s instanceof RegExp){let i=s.flags.includes("s")?s.flags:s.flags+"s";return new RegExp(s.source,i)}return new RegExp(s,"s")}catch{return console.error(`[html-validate-nice-checkers] Invalid regex pattern in '${e}' configuration: "${s}"`),null}}).filter(s=>s!==null)}static schema(){return {mustMatch:{type:"array",items:{type:"string"},description:"An array of regex patterns (as strings) that the page source must match."},mustNotMatch:{type:"array",items:{type:"string"},description:"An array of regex patterns (as strings) that the page source must not match."}}}documentation(){return {description:"Requires page source to match all mustMatch regexes and none of the mustNotMatch regexes.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.md#rules"}}setup(){this.on("dom:ready",a=>{let e=a.source.data;for(let s of this.mustMatchCompiled)s.test(e)||this.report({node:a.document.root,message:`Page source does not match required pattern: ${s.source}`});for(let s of this.mustNotMatchCompiled)s.test(e)&&this.report({node:a.document.root,message:`Page source matches forbidden pattern: ${s.source}`});});}};d(q,"MatchRegexRule");var w=q;var Oe={requiredParameters:["subject","body"]},_=class _ extends htmlValidate.Rule{constructor(a){super({...Oe,...a});}static schema(){return {requiredParameters:{type:"array",items:{type:"string"},description:'An array of parameters that must be present in a mailto: link (e.g., ["subject", "body"]).'}}}documentation(){return {description:"Enforce that mailto: links contain specific parameters.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.on("tag:ready",a=>this.tagReady(a));}tagReady(a){let{target:e}=a;if(e.tagName!=="a")return;let s=e.getAttribute("href")?.value;if(!(typeof s!="string"||!s)&&s.startsWith("mailto:"))try{let i=s.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">"),r=new URL(i),t=this.options.requiredParameters.filter(o=>!r.searchParams.has(o));t.length>0&&this.report({node:e,message:`mailto: link is missing required parameters: ${t.join(", ")}`});}catch{this.report({node:e,message:"mailto: link is malformed and could not be parsed."});}}};d(_,"MailtoAwesomeRule");var S=_;var Q=class Q extends htmlValidate.Rule{documentation(){return {description:"Disallow script tag with src including jQuery",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.m#rules"}}setup(){this.on("tag:ready",a=>{let{target:e}=a;if(e.tagName!=="script")return;let s=e.getAttribute("src")?.value;typeof s!="string"||!s||s.toLowerCase().includes("jquery")&&this.report({node:e,message:"Script tag with src including jQuery is not allowed"});});}};d(Q,"NoJqueryRule");var k=Q;var L={"@context":{brick:"https://brickschema.org/schema/Brick#",csvw:"http://www.w3.org/ns/csvw#",dc:"http://purl.org/dc/elements/1.1/",dcam:"http://purl.org/dc/dcam/",dcat:"http://www.w3.org/ns/dcat#",dcmitype:"http://purl.org/dc/dcmitype/",dcterms:"http://purl.org/dc/terms/",doap:"http://usefulinc.com/ns/doap#",foaf:"http://xmlns.com/foaf/0.1/",odrl:"http://www.w3.org/ns/odrl/2/",org:"http://www.w3.org/ns/org#",owl:"http://www.w3.org/2002/07/owl#",prof:"http://www.w3.org/ns/dx/prof/",prov:"http://www.w3.org/ns/prov#",qb:"http://purl.org/linked-data/cube#",rdf:"http://www.w3.org/1999/02/22-rdf-syntax-ns#",rdfs:"http://www.w3.org/2000/01/rdf-schema#",schema:"https://schema.org/",sh:"http://www.w3.org/ns/shacl#",skos:"http://www.w3.org/2004/02/skos/core#",sosa:"http://www.w3.org/ns/sosa/",ssn:"http://www.w3.org/ns/ssn/",time:"http://www.w3.org/2006/time#",vann:"http://purl.org/vocab/vann/",void:"http://rdfs.org/ns/void#",xsd:"http://www.w3.org/2001/XMLSchema#"},"@graph":[{"@id":"schema:option","@type":"rdf:Property","rdfs:comment":"A sub property of object. The options subject to this action.","rdfs:label":"option","rdfs:subPropertyOf":{"@id":"schema:object"},"schema:domainIncludes":{"@id":"schema:ChooseAction"},"schema:rangeIncludes":[{"@id":"schema:Text"},{"@id":"schema:Thing"}],"schema:supersededBy":{"@id":"schema:actionOption"}},{"@id":"schema:minimumPaymentDue","@type":"rdf:Property","rdfs:comment":"The minimum payment required at this time.","rdfs:label":"minimumPaymentDue","schema:domainIncludes":{"@id":"schema:Invoice"},"schema:rangeIncludes":[{"@id":"schema:PriceSpecification"},{"@id":"schema:MonetaryAmount"}]},{"@id":"schema:DJMixAlbum","@type":"schema:MusicAlbumProductionType","rdfs:comment":"DJMixAlbum.","rdfs:label":"DJMixAlbum","schema:contributor":{"@id":"https://schema.org/docs/collab/MBZ"}},{"@id":"schema:Nonprofit501c27","@type":"schema:USNonprofitType","rdfs:comment":"Nonprofit501c27: Non-profit type referring to State-Sponsored Workers' Compensation Reinsurance Organizations.","rdfs:label":"Nonprofit501c27","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2543"}},{"@id":"schema:includedDataCatalog","@type":"rdf:Property","rdfs:comment":"A data catalog which contains this dataset (this property was previously 'catalog', preferred name is now 'includedInDataCatalog').","rdfs:label":"includedDataCatalog","schema:domainIncludes":{"@id":"schema:Dataset"},"schema:rangeIncludes":{"@id":"schema:DataCatalog"},"schema:supersededBy":{"@id":"schema:includedInDataCatalog"}},{"@id":"schema:UKTrust","@type":"schema:UKNonprofitType","rdfs:comment":"UKTrust: Non-profit type referring to a UK trust.","rdfs:label":"UKTrust","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2543"}},{"@id":"schema:discount","@type":"rdf:Property","rdfs:comment":"Any discount applied (to an Order).","rdfs:label":"discount","schema:domainIncludes":{"@id":"schema:Order"},"schema:rangeIncludes":[{"@id":"schema:Text"},{"@id":"schema:Number"}]},{"@id":"schema:printPage","@type":"rdf:Property","rdfs:comment":"If this NewsArticle appears in print, this field indicates the name of the page on which the article is found. Please note that this field is intended for the exact page name (e.g. A5, B18).","rdfs:label":"printPage","schema:domainIncludes":{"@id":"schema:NewsArticle"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:muscleAction","@type":"rdf:Property","rdfs:comment":"The movement the muscle generates.","rdfs:label":"muscleAction","schema:domainIncludes":{"@id":"schema:Muscle"},"schema:isPartOf":{"@id":"https://health-lifesci.schema.org"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:illustrator","@type":"rdf:Property","rdfs:comment":"The illustrator of the book.","rdfs:label":"illustrator","schema:domainIncludes":{"@id":"schema:Book"},"schema:rangeIncludes":{"@id":"schema:Person"}},{"@id":"schema:contentType","@type":"rdf:Property","rdfs:comment":"The supported content type(s) for an EntryPoint response.","rdfs:label":"contentType","schema:domainIncludes":{"@id":"schema:EntryPoint"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:servesCuisine","@type":"rdf:Property","rdfs:comment":"The cuisine of the restaurant.","rdfs:label":"servesCuisine","schema:domainIncludes":{"@id":"schema:FoodEstablishment"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:distance","@type":"rdf:Property","rdfs:comment":"The distance travelled, e.g. exercising or travelling.","rdfs:label":"distance","schema:domainIncludes":[{"@id":"schema:ExerciseAction"},{"@id":"schema:TravelAction"}],"schema:rangeIncludes":{"@id":"schema:Distance"}},{"@id":"schema:containedInPlace","@type":"rdf:Property","rdfs:comment":"The basic containment relation between a place and one that contains it.","rdfs:label":"containedInPlace","schema:domainIncludes":{"@id":"schema:Place"},"schema:inverseOf":{"@id":"schema:containsPlace"},"schema:rangeIncludes":{"@id":"schema:Place"}},{"@id":"schema:ReturnAtKiosk","@type":"schema:ReturnMethodEnumeration","rdfs:comment":"Specifies that product returns must be made at a kiosk.","rdfs:label":"ReturnAtKiosk","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2880"}},{"@id":"schema:broker","@type":"rdf:Property","rdfs:comment":"An entity that arranges for an exchange between a buyer and a seller. In most cases a broker never acquires or releases ownership of a product or service involved in an exchange. If it is not clear whether an entity is a broker, seller, or buyer, the latter two terms are preferred.","rdfs:label":"broker","schema:domainIncludes":[{"@id":"schema:Order"},{"@id":"schema:Reservation"},{"@id":"schema:Service"},{"@id":"schema:Invoice"}],"schema:rangeIncludes":[{"@id":"schema:Person"},{"@id":"schema:Organization"}]},{"@id":"schema:permittedUsage","@type":"rdf:Property","rdfs:comment":"Indications regarding the permitted usage of the accommodation.","rdfs:label":"permittedUsage","schema:contributor":{"@id":"https://schema.org/docs/collab/STI_Accommodation_Ontology"},"schema:domainIncludes":{"@id":"schema:Accommodation"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:AerobicActivity","@type":"schema:PhysicalActivityCategory","rdfs:comment":"Physical activity of relatively low intensity that depends primarily on the aerobic energy-generating process; during activity, the aerobic metabolism uses oxygen to adequately meet energy demands during exercise.","rdfs:label":"AerobicActivity","schema:isPartOf":{"@id":"https://health-lifesci.schema.org"}},{"@id":"schema:totalHistoricalEnrollment","@type":"rdf:Property","rdfs:comment":"The total number of students that have enrolled in the history of the course.","rdfs:label":"totalHistoricalEnrollment","schema:domainIncludes":{"@id":"schema:Course"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:Integer"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/3281"}},{"@id":"schema:WearableMeasurementHeight","@type":"schema:WearableMeasurementTypeEnumeration","rdfs:comment":"Measurement of the height, for example the heel height of a shoe.","rdfs:label":"WearableMeasurementHeight","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2811"}},{"@id":"schema:Clip","@type":"rdfs:Class","rdfs:comment":"A short TV or radio program or a segment/part of a program.","rdfs:label":"Clip","rdfs:subClassOf":{"@id":"schema:CreativeWork"}},{"@id":"schema:WatchAction","@type":"rdfs:Class","rdfs:comment":"The act of consuming dynamic/moving visual content.","rdfs:label":"WatchAction","rdfs:subClassOf":{"@id":"schema:ConsumeAction"}},{"@id":"schema:issuedBy","@type":"rdf:Property","rdfs:comment":"The organization issuing the item, for example a [[Permit]], [[Ticket]], or [[Certification]].","rdfs:label":"issuedBy","schema:domainIncludes":[{"@id":"schema:Permit"},{"@id":"schema:Ticket"},{"@id":"schema:Certification"}],"schema:rangeIncludes":{"@id":"schema:Organization"}},{"@id":"schema:DemoAlbum","@type":"schema:MusicAlbumProductionType","rdfs:comment":"DemoAlbum.","rdfs:label":"DemoAlbum","schema:contributor":{"@id":"https://schema.org/docs/collab/MBZ"}},{"@id":"schema:hasMemberProgram","@type":"rdf:Property","rdfs:comment":"MemberProgram offered by an Organization, for example an eCommerce merchant or an airline.","rdfs:label":"hasMemberProgram","schema:domainIncludes":{"@id":"schema:Organization"},"schema:rangeIncludes":{"@id":"schema:MemberProgram"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/3563"}},{"@id":"schema:hasEnergyEfficiencyCategory","@type":"rdf:Property","rdfs:comment":"Defines the energy efficiency Category (which could be either a rating out of range of values or a yes/no certification) for a product according to an international energy efficiency standard.","rdfs:label":"hasEnergyEfficiencyCategory","schema:domainIncludes":{"@id":"schema:EnergyConsumptionDetails"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:EnergyEfficiencyEnumeration"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2670"}},{"@id":"schema:rangeIncludes","@type":"rdf:Property","rdfs:comment":"Relates a property to a class that constitutes (one of) the expected type(s) for values of the property.","rdfs:label":"rangeIncludes","schema:domainIncludes":{"@id":"schema:Property"},"schema:isPartOf":{"@id":"https://meta.schema.org"},"schema:rangeIncludes":{"@id":"schema:Class"}},{"@id":"schema:contentLocation","@type":"rdf:Property","rdfs:comment":"The location depicted or described in the content. For example, the location in a photograph or painting.","rdfs:label":"contentLocation","schema:domainIncludes":{"@id":"schema:CreativeWork"},"schema:rangeIncludes":{"@id":"schema:Place"}},{"@id":"schema:NonprofitANBI","@type":"schema:NLNonprofitType","rdfs:comment":"NonprofitANBI: Non-profit type referring to a Public Benefit Organization (NL).","rdfs:label":"NonprofitANBI","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2543"}},{"@id":"schema:ExerciseGym","@type":"rdfs:Class","rdfs:comment":"A gym.","rdfs:label":"ExerciseGym","rdfs:subClassOf":{"@id":"schema:SportsActivityLocation"}},{"@id":"schema:MusicPlaylist","@type":"rdfs:Class","rdfs:comment":"A collection of music tracks in playlist form.","rdfs:label":"MusicPlaylist","rdfs:subClassOf":{"@id":"schema:CreativeWork"}},{"@id":"schema:weightPercentage","@type":"rdf:Property","rdfs:comment":"Fraction of the weight that is used to compute the shipping price.","rdfs:label":"weightPercentage","schema:domainIncludes":{"@id":"schema:ShippingRateSettings"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:Number"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/3617"}},{"@id":"schema:landlord","@type":"rdf:Property","rdfs:comment":"A sub property of participant. The owner of the real estate property.","rdfs:label":"landlord","rdfs:subPropertyOf":{"@id":"schema:participant"},"schema:domainIncludes":{"@id":"schema:RentAction"},"schema:rangeIncludes":[{"@id":"schema:Organization"},{"@id":"schema:Person"}]},{"@id":"schema:recordedAt","@type":"rdf:Property","rdfs:comment":"The Event where the CreativeWork was recorded. The CreativeWork may capture all or part of the event.","rdfs:label":"recordedAt","schema:domainIncludes":{"@id":"schema:CreativeWork"},"schema:inverseOf":{"@id":"schema:recordedIn"},"schema:rangeIncludes":{"@id":"schema:Event"}},{"@id":"schema:DigitalFormat","@type":"schema:MusicReleaseFormatType","rdfs:comment":"DigitalFormat.","rdfs:label":"DigitalFormat","schema:contributor":{"@id":"https://schema.org/docs/collab/MBZ"}},{"@id":"schema:EmployeeRole","@type":"rdfs:Class","rdfs:comment":"A subclass of OrganizationRole used to describe employee relationships.","rdfs:label":"EmployeeRole","rdfs:subClassOf":{"@id":"schema:OrganizationRole"}},{"@id":"schema:RejectAction","@type":"rdfs:Class","rdfs:comment":"The act of rejecting to/adopting an object.\\n\\nRelated actions:\\n\\n* [[AcceptAction]]: The antonym of RejectAction.","rdfs:label":"RejectAction","rdfs:subClassOf":{"@id":"schema:AllocateAction"}},{"@id":"schema:MerchantReturnFiniteReturnWindow","@type":"schema:MerchantReturnEnumeration","rdfs:comment":"Specifies that there is a finite window for product returns.","rdfs:label":"MerchantReturnFiniteReturnWindow","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2288"}},{"@id":"schema:ActiveActionStatus","@type":"schema:ActionStatusType","rdfs:comment":"An in-progress action (e.g., while watching the movie, or driving to a location).","rdfs:label":"ActiveActionStatus"},{"@id":"schema:EventVenue","@type":"rdfs:Class","rdfs:comment":"An event venue.","rdfs:label":"EventVenue","rdfs:subClassOf":{"@id":"schema:CivicStructure"}},{"@id":"schema:FreeReturn","@type":"schema:ReturnFeesEnumeration","rdfs:comment":"Specifies that product returns are free of charge for the customer.","rdfs:label":"FreeReturn","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2880"}},{"@id":"schema:frequency","@type":"rdf:Property","rdfs:comment":"How often the dose is taken, e.g. 'daily'.","rdfs:label":"frequency","schema:domainIncludes":{"@id":"schema:DoseSchedule"},"schema:isPartOf":{"@id":"https://health-lifesci.schema.org"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:polygon","@type":"rdf:Property","rdfs:comment":"A polygon is the area enclosed by a point-to-point path for which the starting and ending points are the same. A polygon is expressed as a series of four or more space delimited points where the first and final points are identical.","rdfs:label":"polygon","schema:domainIncludes":{"@id":"schema:GeoShape"},"schema:rangeIncludes":{"@id":"schema:Text"}},{"@id":"schema:isTierOf","@type":"rdf:Property","rdfs:comment":"The member program this tier is a part of.","rdfs:label":"isTierOf","schema:domainIncludes":{"@id":"schema:MemberProgramTier"},"schema:inverseOf":{"@id":"schema:hasTiers"},"schema:rangeIncludes":{"@id":"schema:MemberProgram"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/3563"}},{"@id":"schema:ScheduleAction","@type":"rdfs:Class","rdfs:comment":"Scheduling future actions, events, or tasks.\\n\\nRelated actions:\\n\\n* [[ReserveAction]]: Unlike ReserveAction, ScheduleAction allocates future actions (e.g. an event, a task, etc) towards a time slot / spatial allocation.","rdfs:label":"ScheduleAction","rdfs:subClassOf":{"@id":"schema:PlanAction"}},{"@id":"schema:itemDefectReturnLabelSource","@type":"rdf:Property","rdfs:comment":"The method (from an enumeration) by which the customer obtains a return shipping label for a defect product.","rdfs:label":"itemDefectReturnLabelSource","schema:domainIncludes":{"@id":"schema:MerchantReturnPolicy"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:ReturnLabelSourceEnumeration"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2880"}},{"@id":"schema:MedicalCode","@type":"rdfs:Class","rdfs:comment":"A code for a medical entity.","rdfs:label":"MedicalCode","rdfs:subClassOf":[{"@id":"schema:MedicalIntangible"},{"@id":"schema:CategoryCode"}],"schema:isPartOf":{"@id":"https://health-lifesci.schema.org"}},{"@id":"schema:isInvolvedInBiologicalProcess","@type":"rdf:Property","rdfs:comment":"Biological process this BioChemEntity is involved in; please use PropertyValue if you want to include any evidence.","rdfs:label":"isInvolvedInBiologicalProcess","schema:domainIncludes":{"@id":"schema:BioChemEntity"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":[{"@id":"schema:PropertyValue"},{"@id":"schema:URL"},{"@id":"schema:DefinedTerm"}],"schema:source":{"@id":"http://www.bioschemas.org/BioChemEntity"}},{"@id":"schema:productionDate","@type":"rdf:Property","rdfs:comment":"The date of production of the item, e.g. vehicle.","rdfs:label":"productionDate","schema:contributor":{"@id":"https://schema.org/docs/collab/Automotive_Ontology_Working_Group"},"schema:domainIncludes":[{"@id":"schema:Product"},{"@id":"schema:Vehicle"}],"schema:rangeIncludes":{"@id":"schema:Date"}},{"@id":"schema:emissionsCO2","@type":"rdf:Property","rdfs:comment":'The CO2 emissions in g/km. When used in combination with a QuantitativeValue, put "g/km" into the unitText property of that value, since there is no UN/CEFACT Common Code for "g/km".',"rdfs:label":"emissionsCO2","schema:contributor":{"@id":"https://schema.org/docs/collab/Automotive_Ontology_Working_Group"},"schema:domainIncludes":{"@id":"schema:Vehicle"},"schema:isPartOf":{"@id":"https://auto.schema.org"},"schema:rangeIncludes":{"@id":"schema:Number"}},{"@id":"schema:legislationDateVersion","@type":"rdf:Property","owl:equivalentProperty":{"@id":"http://data.europa.eu/eli/ontology#version_date"},"rdfs:comment":'The point-in-time at which the provided description of the legislation is valid (e.g.: when looking at the law on the 2016-04-07 (= dateVersion), I get the consolidation of 2015-04-12 of the "National Insurance Contributions Act 2015")',"rdfs:label":"legislationDateVersion","schema:contributor":{"@id":"https://schema.org/docs/collab/ELI"},"schema:domainIncludes":{"@id":"schema:Legislation"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:Date"},"schema:source":[{"@id":"https://op.europa.eu/en/web/eu-vocabularies/model/-/resource/dataset/eli"},{"@id":"https://github.com/schemaorg/schemaorg/issues/1156"}],"skos:exactMatch":{"@id":"http://data.europa.eu/eli/ontology#version_date"}},{"@id":"schema:Hotel","@type":"rdfs:Class","rdfs:comment":`A hotel is an establishment that provides lodging paid on a short-term basis (source: Wikipedia, the free encyclopedia, see http://en.wikipedia.org/wiki/Hotel).
22
22
  <br /><br />
23
23
  See also the <a href="/docs/hotels.html">dedicated document on the use of schema.org for marking up hotels and other forms of accommodations</a>.
24
24
  `,"rdfs:label":"Hotel","rdfs:subClassOf":{"@id":"schema:LodgingBusiness"},"schema:contributor":{"@id":"https://schema.org/docs/collab/STI_Accommodation_Ontology"}},{"@id":"schema:diet","@type":"rdf:Property","rdfs:comment":"A sub property of instrument. The diet used in this action.","rdfs:label":"diet","rdfs:subPropertyOf":{"@id":"schema:instrument"},"schema:domainIncludes":{"@id":"schema:ExerciseAction"},"schema:isPartOf":{"@id":"https://health-lifesci.schema.org"},"schema:rangeIncludes":{"@id":"schema:Diet"}},{"@id":"schema:maximumVirtualAttendeeCapacity","@type":"rdf:Property","rdfs:comment":"The maximum virtual attendee capacity of an [[Event]] whose [[eventAttendanceMode]] is [[OnlineEventAttendanceMode]] (or the online aspects, in the case of a [[MixedEventAttendanceMode]]). ","rdfs:label":"maximumVirtualAttendeeCapacity","schema:domainIncludes":{"@id":"schema:Event"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:Integer"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/1842"}},{"@id":"schema:Neuro","@type":"schema:PhysicalExam","rdfs:comment":"Neurological system clinical examination.","rdfs:label":"Neuro","schema:isPartOf":{"@id":"https://health-lifesci.schema.org"}},{"@id":"schema:comment","@type":"rdf:Property","rdfs:comment":"Comments, typically from users.","rdfs:label":"comment","schema:domainIncludes":[{"@id":"schema:RsvpAction"},{"@id":"schema:CreativeWork"}],"schema:rangeIncludes":{"@id":"schema:Comment"}},{"@id":"schema:UnemploymentSupport","@type":"schema:GovernmentBenefitsType","rdfs:comment":"UnemploymentSupport: this is a benefit for unemployment support.","rdfs:label":"UnemploymentSupport","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2534"}},{"@id":"schema:hoursAvailable","@type":"rdf:Property","rdfs:comment":"The hours during which this service or contact is available.","rdfs:label":"hoursAvailable","schema:domainIncludes":[{"@id":"schema:Service"},{"@id":"schema:LocationFeatureSpecification"},{"@id":"schema:ContactPoint"}],"schema:rangeIncludes":{"@id":"schema:OpeningHoursSpecification"}},{"@id":"schema:PaymentMethod","@type":"rdfs:Class","rdfs:comment":`A payment method is a standardized procedure for transferring the monetary amount for a purchase. Payment methods are characterized by the legal and technical structures used, and by the organization or group carrying out the transaction. The following legacy values should be accepted:
@@ -519,6 +519,6 @@ E.g. Cheaper and slower: $5 in 5-7 days
519
519
  or Fast and expensive: $15 in 1-2 days.`,"rdfs:label":"OfferShippingDetails","rdfs:subClassOf":{"@id":"schema:StructuredValue"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2506"}},{"@id":"schema:SurgicalProcedure","@type":"rdfs:Class","owl:equivalentClass":{"@id":"http://purl.bioontology.org/ontology/SNOMEDCT/387713003"},"rdfs:comment":"A medical procedure involving an incision with instruments; performed for diagnose, or therapeutic purposes.","rdfs:label":"SurgicalProcedure","rdfs:subClassOf":{"@id":"schema:MedicalProcedure"},"schema:isPartOf":{"@id":"https://health-lifesci.schema.org"}},{"@id":"schema:hasVariant","@type":"rdf:Property","rdfs:comment":"Indicates a [[Product]] that is a member of this [[ProductGroup]] (or [[ProductModel]]).","rdfs:label":"hasVariant","schema:domainIncludes":{"@id":"schema:ProductGroup"},"schema:inverseOf":{"@id":"schema:isVariantOf"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:Product"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/1797"}},{"@id":"schema:remainingAttendeeCapacity","@type":"rdf:Property","rdfs:comment":"The number of attendee places for an event that remain unallocated.","rdfs:label":"remainingAttendeeCapacity","schema:domainIncludes":{"@id":"schema:Event"},"schema:rangeIncludes":{"@id":"schema:Integer"}},{"@id":"schema:paymentMethod","@type":"rdf:Property","rdfs:comment":"The name of the credit card or other method of payment for the order.","rdfs:label":"paymentMethod","schema:domainIncludes":[{"@id":"schema:Order"},{"@id":"schema:Invoice"}],"schema:rangeIncludes":[{"@id":"schema:Text"},{"@id":"schema:PaymentMethod"}],"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/3537"}},{"@id":"schema:ListPrice","@type":"schema:PriceTypeEnumeration","rdfs:comment":"Represents the list price of an offered product. Typically the same as the [MSRP](https://schema.org/MSRP).","rdfs:label":"ListPrice","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2712"}},{"@id":"schema:acceptsReservations","@type":"rdf:Property","rdfs:comment":"Indicates whether a FoodEstablishment accepts reservations. Values can be Boolean, an URL at which reservations can be made or (for backwards compatibility) the strings ```Yes``` or ```No```.","rdfs:label":"acceptsReservations","schema:domainIncludes":{"@id":"schema:FoodEstablishment"},"schema:rangeIncludes":[{"@id":"schema:Text"},{"@id":"schema:URL"},{"@id":"schema:Boolean"}]},{"@id":"schema:PatientExperienceHealthAspect","@type":"schema:HealthAspectEnumeration","rdfs:comment":"Content about the real life experience of patients or people that have lived a similar experience about the topic. May be forums, topics, Q-and-A and related material.","rdfs:label":"PatientExperienceHealthAspect","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2374"}},{"@id":"schema:customerRemorseReturnFees","@type":"rdf:Property","rdfs:comment":"The type of return fees if the product is returned due to customer remorse.","rdfs:label":"customerRemorseReturnFees","schema:domainIncludes":{"@id":"schema:MerchantReturnPolicy"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:ReturnFeesEnumeration"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2880"}},{"@id":"schema:renegotiableLoan","@type":"rdf:Property","rdfs:comment":"Whether the terms for payment of interest can be renegotiated during the life of the loan.","rdfs:label":"renegotiableLoan","schema:contributor":{"@id":"https://schema.org/docs/collab/FIBO"},"schema:domainIncludes":{"@id":"schema:LoanOrCredit"},"schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:rangeIncludes":{"@id":"schema:Boolean"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/1253"}},{"@id":"schema:BodyMeasurementNeck","@type":"schema:BodyMeasurementTypeEnumeration","rdfs:comment":"Girth of neck. Used, for example, to fit shirts.","rdfs:label":"BodyMeasurementNeck","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2811"}},{"@id":"schema:warranty","@type":"rdf:Property","rdfs:comment":"The warranty promise(s) included in the offer.","rdfs:label":"warranty","schema:contributor":{"@id":"https://schema.org/docs/collab/GoodRelationsTerms"},"schema:domainIncludes":[{"@id":"schema:Offer"},{"@id":"schema:Demand"}],"schema:rangeIncludes":{"@id":"schema:WarrantyPromise"}},{"@id":"schema:broadcastOfEvent","@type":"rdf:Property","rdfs:comment":"The event being broadcast such as a sporting event or awards ceremony.","rdfs:label":"broadcastOfEvent","schema:domainIncludes":{"@id":"schema:BroadcastEvent"},"schema:rangeIncludes":{"@id":"schema:Event"}},{"@id":"schema:DrugCostCategory","@type":"rdfs:Class","rdfs:comment":"Enumerated categories of medical drug costs.","rdfs:label":"DrugCostCategory","rdfs:subClassOf":{"@id":"schema:MedicalEnumeration"},"schema:isPartOf":{"@id":"https://health-lifesci.schema.org"}},{"@id":"schema:LimitedByGuaranteeCharity","@type":"schema:UKNonprofitType","rdfs:comment":"LimitedByGuaranteeCharity: Non-profit type referring to a charitable company that is limited by guarantee (UK).","rdfs:label":"LimitedByGuaranteeCharity","schema:isPartOf":{"@id":"https://pending.schema.org"},"schema:source":{"@id":"https://github.com/schemaorg/schemaorg/issues/2543"}},{"@id":"schema:director","@type":"rdf:Property","rdfs:comment":"A director of e.g. TV, radio, movie, video gaming etc. content, or of an event. Directors can be associated with individual items or with a series, episode, clip.","rdfs:label":"director","schema:domainIncludes":[{"@id":"schema:Clip"},{"@id":"schema:CreativeWorkSeason"},{"@id":"schema:Event"},{"@id":"schema:VideoObject"},{"@id":"schema:MovieSeries"},{"@id":"schema:TVSeries"},{"@id":"schema:VideoGame"},{"@id":"schema:Movie"},{"@id":"schema:RadioSeries"},{"@id":"schema:Episode"},{"@id":"schema:VideoGameSeries"}],"schema:rangeIncludes":{"@id":"schema:Person"}},{"@id":"schema:AutomatedTeller","@type":"rdfs:Class","rdfs:comment":"ATM/cash machine.","rdfs:label":"AutomatedTeller","rdfs:subClassOf":{"@id":"schema:FinancialService"}},{"@id":"schema:HotelRoom","@type":"rdfs:Class","rdfs:comment":`A hotel room is a single room in a hotel.
520
520
  <br /><br />
521
521
  See also the <a href="/docs/hotels.html">dedicated document on the use of schema.org for marking up hotels and other forms of accommodations</a>.
522
- `,"rdfs:label":"HotelRoom","rdfs:subClassOf":{"@id":"schema:Room"},"schema:contributor":{"@id":"https://schema.org/docs/collab/STI_Accommodation_Ontology"}},{"@id":"schema:PercutaneousProcedure","@type":"schema:MedicalProcedureType","rdfs:comment":"A type of medical procedure that involves percutaneous techniques, where access to organs or tissue is achieved via needle-puncture of the skin. For example, catheter-based procedures like stent delivery.","rdfs:label":"PercutaneousProcedure","schema:isPartOf":{"@id":"https://health-lifesci.schema.org"}}]};var _=class _ extends htmlValidate.Rule{constructor(){super();this.validClasses=new Set;this.validProperties=new Set;try{let e=D["@context"],a=e.rdfs+"Class",t=e.rdf+"Property";if(D["@graph"])for(let r of D["@graph"]){let i=Array.isArray(r["@type"])?r["@type"]:[r["@type"]];(i.includes("rdfs:Class")||i.includes(a))&&this.validClasses.add(r["rdfs:label"]),(i.includes("rdf:Property")||i.includes(t))&&this.validProperties.add(r["rdfs:label"]);}if(this.validClasses.size===0||this.validProperties.size===0)throw new Error("Schema loading resulted in zero classes or properties. The vocabulary format may have changed.")}catch(e){throw new Error(`[SchemaOrgJsonLdRule] CRITICAL: Failed to load or process schema: ${e.message}`)}}documentation(){return {description:"Validates JSON-LD against the bundled Schema.org vocabulary.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.md#schema-org-json-ld"}}setup(){this.on("element:ready",e=>this.elementReady(e));}elementReady(e){let{target:a}=e;if(a.tagName!=="script"||a.getAttribute("type")?.value!=="application/ld+json")return;let r=a.textContent;if(!r||r.trim()==="")return;let i;try{i=JSON.parse(r);}catch(o){this.report({node:a,message:`Invalid JSON syntax: ${o.message}`});return}this.validateSchemaObject(i,a,true);}validateSchemaObject(e,a,t){if(Array.isArray(e)){e.forEach(i=>this.validateSchemaObject(i,a,t));return}if(typeof e!="object"||e===null)return;if(t&&e["@graph"]&&Array.isArray(e["@graph"])){this.validateSchemaObject(e["@graph"],a,true);return}let r=e["@type"];if(t&&!r){this.report({node:a,message:'Top-level JSON-LD object is missing required "@type" property.'});return}if(r){let i=Array.isArray(r)?r:[r];for(let o of i)this.validClasses.has(o)||this.report({node:a,message:`Schema.org type "${o}" is not a valid class.`});}for(let i in e){if(i.startsWith("@"))continue;this.validProperties.has(i)||this.report({node:a,message:`Schema.org property "${i}" is not a valid property.`});let o=e[i];typeof o=="object"&&o!==null&&this.validateSchemaObject(o,a,false);}}};d(_,"SchemaOrgJsonLdRule");var k=_;var Oe={name:"nice-checkers-plugin",rules:{"nice-checkers/alternate-language-url":b,"nice-checkers/alternate-language-links":P,"nice-checkers/canonical-link":I,"nice-checkers/external-links":C,"nice-checkers/https-links":O,"nice-checkers/internal-links":T,"nice-checkers/latest-packages":A,"nice-checkers/mailto-awesome":w,"nice-checkers/no-jquery":S,"nice-checkers/schema-org-json-ld":k},configs:{recommended:{rules:{"nice-checkers/alternate-language-url":["error"],"nice-checkers/alternate-language-links":["error"],"nice-checkers/canonical-link":["error"],"nice-checkers/external-links":["error"],"nice-checkers/https-links":["error"],"nice-checkers/internal-links":["error"],"nice-checkers/latest-packages":["warn"],"nice-checkers/mailto-awesome":["error"],"nice-checkers/no-jquery":["error"],"nice-checkers/schema-org-json-ld":["error"]}}}},Ms=Oe;
523
- module.exports=Ms;//# sourceMappingURL=index.cjs.map
522
+ `,"rdfs:label":"HotelRoom","rdfs:subClassOf":{"@id":"schema:Room"},"schema:contributor":{"@id":"https://schema.org/docs/collab/STI_Accommodation_Ontology"}},{"@id":"schema:PercutaneousProcedure","@type":"schema:MedicalProcedureType","rdfs:comment":"A type of medical procedure that involves percutaneous techniques, where access to organs or tissue is achieved via needle-puncture of the skin. For example, catheter-based procedures like stent delivery.","rdfs:label":"PercutaneousProcedure","schema:isPartOf":{"@id":"https://health-lifesci.schema.org"}}]};var J=class J extends htmlValidate.Rule{constructor(){super();this.validClasses=new Set;this.validProperties=new Set;try{let e=L["@context"],s=e.rdfs+"Class",i=e.rdf+"Property";if(L["@graph"])for(let r of L["@graph"]){let t=Array.isArray(r["@type"])?r["@type"]:[r["@type"]];(t.includes("rdfs:Class")||t.includes(s))&&this.validClasses.add(r["rdfs:label"]),(t.includes("rdf:Property")||t.includes(i))&&this.validProperties.add(r["rdfs:label"]);}if(this.validClasses.size===0||this.validProperties.size===0)throw new Error("Schema loading resulted in zero classes or properties. The vocabulary format may have changed.")}catch(e){throw new Error(`[SchemaOrgJsonLdRule] CRITICAL: Failed to load or process schema: ${e.message}`)}}documentation(){return {description:"Validates JSON-LD against the bundled Schema.org vocabulary.",url:"https://github.com/fulldecent/html-validate-nice-checkers/blob/main/README.md#schema-org-json-ld"}}setup(){this.on("element:ready",e=>this.elementReady(e));}elementReady(e){let{target:s}=e;if(s.tagName!=="script"||s.getAttribute("type")?.value!=="application/ld+json")return;let r=s.textContent;if(!r||r.trim()==="")return;let t;try{t=JSON.parse(r);}catch(o){this.report({node:s,message:`Invalid JSON syntax: ${o.message}`});return}this.validateSchemaObject(t,s,true);}validateSchemaObject(e,s,i){if(Array.isArray(e)){e.forEach(t=>this.validateSchemaObject(t,s,i));return}if(typeof e!="object"||e===null)return;if(i&&e["@graph"]&&Array.isArray(e["@graph"])){this.validateSchemaObject(e["@graph"],s,true);return}let r=e["@type"];if(i&&!r){this.report({node:s,message:'Top-level JSON-LD object is missing required "@type" property.'});return}if(r){let t=Array.isArray(r)?r:[r];for(let o of t)this.validClasses.has(o)||this.report({node:s,message:`Schema.org type "${o}" is not a valid class.`});}for(let t in e){if(t.startsWith("@"))continue;this.validProperties.has(t)||this.report({node:s,message:`Schema.org property "${t}" is not a valid property.`});let o=e[t];typeof o=="object"&&o!==null&&this.validateSchemaObject(o,s,false);}}};d(J,"SchemaOrgJsonLdRule");var E=J;var Se={name:"nice-checkers-plugin",rules:{"nice-checkers/alternate-language-url":b,"nice-checkers/alternate-language-links":P,"nice-checkers/canonical-link":I,"nice-checkers/external-links":C,"nice-checkers/https-links":O,"nice-checkers/internal-links":T,"nice-checkers/latest-packages":A,"nice-checkers/match-regex":w,"nice-checkers/mailto-awesome":S,"nice-checkers/no-jquery":k,"nice-checkers/schema-org-json-ld":E},configs:{recommended:{rules:{"nice-checkers/alternate-language-url":["error"],"nice-checkers/alternate-language-links":["error"],"nice-checkers/canonical-link":["error"],"nice-checkers/external-links":["error"],"nice-checkers/https-links":["error"],"nice-checkers/internal-links":["error"],"nice-checkers/latest-packages":["warn"],"nice-checkers/match-regex":["off"],"nice-checkers/mailto-awesome":["error"],"nice-checkers/no-jquery":["error"],"nice-checkers/schema-org-json-ld":["error"],"no-implicit-button-type":["off"],"no-implicit-input-type":["off"]}}}},zs=Se;
523
+ module.exports=zs;//# sourceMappingURL=index.cjs.map
524
524
  //# sourceMappingURL=index.cjs.map