@markuplint/types 5.0.0-rc.2 → 5.0.0-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +83 -75
  3. package/lib/check-multi-types.d.ts +2 -9
  4. package/lib/check-multi-types.js +2 -9
  5. package/lib/css-defs.d.ts +0 -8
  6. package/lib/css-defs.js +0 -8
  7. package/lib/css-overrides.d.ts +2 -5
  8. package/lib/css-overrides.js +2 -5
  9. package/lib/css-syntax.d.ts +0 -11
  10. package/lib/css-syntax.js +6 -18
  11. package/lib/css-tokenizers.d.ts +0 -6
  12. package/lib/css-tokenizers.js +0 -6
  13. package/lib/debug.d.ts +0 -3
  14. package/lib/debug.js +0 -3
  15. package/lib/defs.d.ts +0 -8
  16. package/lib/defs.js +308 -37
  17. package/lib/directive.d.ts +0 -14
  18. package/lib/directive.js +0 -14
  19. package/lib/enum.d.ts +0 -11
  20. package/lib/enum.js +0 -11
  21. package/lib/keyword-type.d.ts +0 -13
  22. package/lib/keyword-type.js +0 -13
  23. package/lib/list.d.ts +0 -13
  24. package/lib/list.js +0 -13
  25. package/lib/match-result.d.ts +0 -21
  26. package/lib/match-result.js +0 -21
  27. package/lib/number.d.ts +0 -12
  28. package/lib/number.js +0 -12
  29. package/lib/primitive/is-float.d.ts +3 -4
  30. package/lib/primitive/is-float.js +3 -4
  31. package/lib/primitive/is-int.d.ts +0 -5
  32. package/lib/primitive/is-int.js +0 -5
  33. package/lib/primitive/is-non-zero-uint.d.ts +0 -6
  34. package/lib/primitive/is-non-zero-uint.js +0 -6
  35. package/lib/primitive/is-quantity.d.ts +0 -8
  36. package/lib/primitive/is-quantity.js +0 -8
  37. package/lib/primitive/is-uint.d.ts +0 -6
  38. package/lib/primitive/is-uint.js +0 -6
  39. package/lib/primitive/range.d.ts +0 -8
  40. package/lib/primitive/range.js +0 -8
  41. package/lib/primitive/split-unit.d.ts +0 -6
  42. package/lib/primitive/split-unit.js +0 -6
  43. package/lib/rfc/is-bcp-47.d.ts +24 -1
  44. package/lib/rfc/is-bcp-47.js +88 -3
  45. package/lib/token/token-collection.d.ts +1 -111
  46. package/lib/token/token-collection.js +0 -108
  47. package/lib/token/token.d.ts +1 -73
  48. package/lib/token/token.js +0 -72
  49. package/lib/token/types.d.ts +0 -9
  50. package/lib/types.schema.d.ts +1 -1
  51. package/lib/w3c/check-content-security-policy.d.ts +18 -0
  52. package/lib/w3c/check-content-security-policy.js +363 -0
  53. package/lib/w3c/check-serialized-permissions-policy.d.ts +0 -2
  54. package/lib/w3c/check-serialized-permissions-policy.js +0 -2
  55. package/lib/whatwg/check-autocomplete.d.ts +26 -9
  56. package/lib/whatwg/check-autocomplete.js +42 -15
  57. package/lib/whatwg/check-datetime/date-string-with-optional-time.d.ts +11 -0
  58. package/lib/whatwg/check-datetime/date-string-with-optional-time.js +16 -0
  59. package/lib/whatwg/check-datetime/date-string.d.ts +0 -2
  60. package/lib/whatwg/check-datetime/date-string.js +0 -2
  61. package/lib/whatwg/check-datetime/datetime-tokens.d.ts +0 -13
  62. package/lib/whatwg/check-datetime/datetime-tokens.js +0 -16
  63. package/lib/whatwg/check-datetime/duration-string.d.ts +0 -4
  64. package/lib/whatwg/check-datetime/duration-string.js +0 -4
  65. package/lib/whatwg/check-datetime/global-date-and-time-string.d.ts +0 -2
  66. package/lib/whatwg/check-datetime/global-date-and-time-string.js +4 -4
  67. package/lib/whatwg/check-datetime/index.d.ts +0 -5
  68. package/lib/whatwg/check-datetime/index.js +0 -5
  69. package/lib/whatwg/check-datetime/local-date-and-time-string.d.ts +0 -4
  70. package/lib/whatwg/check-datetime/local-date-and-time-string.js +0 -4
  71. package/lib/whatwg/check-datetime/month-string.d.ts +0 -2
  72. package/lib/whatwg/check-datetime/month-string.js +0 -2
  73. package/lib/whatwg/check-datetime/time-string.d.ts +0 -2
  74. package/lib/whatwg/check-datetime/time-string.js +0 -2
  75. package/lib/whatwg/check-datetime/time-zone-offset-string.d.ts +0 -8
  76. package/lib/whatwg/check-datetime/time-zone-offset-string.js +0 -8
  77. package/lib/whatwg/check-datetime/week-string.d.ts +0 -2
  78. package/lib/whatwg/check-datetime/week-string.js +0 -2
  79. package/lib/whatwg/check-datetime/year-string.d.ts +0 -2
  80. package/lib/whatwg/check-datetime/year-string.js +0 -2
  81. package/lib/whatwg/check-datetime/yearless-date-string.d.ts +0 -2
  82. package/lib/whatwg/check-datetime/yearless-date-string.js +0 -2
  83. package/lib/whatwg/check-email.js +0 -2
  84. package/lib/whatwg/check-http-equiv-content-type.d.ts +12 -0
  85. package/lib/whatwg/check-http-equiv-content-type.js +32 -0
  86. package/lib/whatwg/check-http-equiv-refresh.d.ts +21 -0
  87. package/lib/whatwg/check-http-equiv-refresh.js +106 -0
  88. package/lib/whatwg/check-media-query-list.d.ts +61 -0
  89. package/lib/whatwg/check-media-query-list.js +484 -0
  90. package/lib/whatwg/check-mime-type.d.ts +0 -4
  91. package/lib/whatwg/check-mime-type.js +55 -3
  92. package/lib/whatwg/check-simple-color.d.ts +2 -4
  93. package/lib/whatwg/check-simple-color.js +2 -4
  94. package/lib/whatwg/check-url.d.ts +15 -7
  95. package/lib/whatwg/check-url.js +333 -25
  96. package/lib/whatwg/is-abs-url.d.ts +0 -2
  97. package/lib/whatwg/is-abs-url.js +0 -2
  98. package/lib/whatwg/is-browser-context-name.d.ts +0 -2
  99. package/lib/whatwg/is-browser-context-name.js +0 -2
  100. package/lib/whatwg/is-itemprop-name.d.ts +0 -2
  101. package/lib/whatwg/is-itemprop-name.js +0 -2
  102. package/lib/whatwg/is-navigable-target-name.d.ts +0 -2
  103. package/lib/whatwg/is-navigable-target-name.js +0 -2
  104. package/package.json +6 -5
  105. package/types.schema.json +9 -0
  106. package/ARCHITECTURE.ja.md +0 -256
  107. package/ARCHITECTURE.md +0 -256
  108. package/SKILL.md +0 -100
  109. package/docs/check-pipeline.ja.md +0 -494
  110. package/docs/check-pipeline.md +0 -494
  111. package/docs/token-system.ja.md +0 -584
  112. package/docs/token-system.md +0 -584
  113. package/docs/type-system.ja.md +0 -623
  114. package/docs/type-system.md +0 -623
  115. package/docs/validators.ja.md +0 -536
  116. package/docs/validators.md +0 -536
@@ -1,536 +0,0 @@
1
- # Validators
2
-
3
- ## Overview
4
-
5
- The `@markuplint/types` package provides a layered validation system for HTML attribute values. Validators are functions that check whether a given string conforms to a specific type defined by web standards (WHATWG, W3C, RFC) or common primitive formats.
6
-
7
- ### Role in the Type System
8
-
9
- Validators sit at the core of the type-checking pipeline. When markuplint evaluates an attribute value, it:
10
-
11
- 1. Resolves the attribute's expected type from the schema (e.g., `DateTime`, `BCP47`, `Uint`)
12
- 2. Looks up the corresponding validator in the `defs` registry (`src/defs.ts`)
13
- 3. Invokes the validator, which returns a `Result` -- either `MatchedResult` or `UnmatchedResult` with detailed error information
14
-
15
- ### Validator Patterns
16
-
17
- Validators follow two primary patterns:
18
-
19
- - **`FormattedPrimitiveTypeCreator`** -- A factory that returns a boolean predicate `(value: string) => boolean`. Used for simpler checks like `isInt`, `isAbsURL`, `isCustomElementName`. These are wrapped with the `matches()` helper when registered in `defs.ts`.
20
- - **`CustomSyntaxChecker`** -- A factory that returns `(value: string) => Result`, providing detailed match/unmatch information with error positions, reasons, and suggestions. Used for complex validators like `checkDateTime`, `checkAutoComplete`, `checkSerializedPermissionsPolicy`.
21
-
22
- ### Categories
23
-
24
- Validators are organized into four categories by the specification they implement:
25
-
26
- | Category | Directory | Description |
27
- | --------- | ---------------- | -------------------------------------- |
28
- | Primitive | `src/primitive/` | Basic numeric and string format checks |
29
- | WHATWG | `src/whatwg/` | HTML Living Standard microsyntaxes |
30
- | RFC | `src/rfc/` | IETF RFC-defined formats |
31
- | W3C | `src/w3c/` | W3C specification formats |
32
-
33
- ---
34
-
35
- ## Primitive Validators
36
-
37
- Primitive validators handle fundamental numeric and unit-based format checks. They are used both directly and as building blocks for higher-level validators.
38
-
39
- **Source:** `src/primitive/index.ts`
40
-
41
- | Function | File | Description | Parameters | Return |
42
- | --------------- | ------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- | ------------------------------- |
43
- | `isInt` | `primitive/is-int.ts` | Checks for valid signed integer | `value: string` | `boolean` |
44
- | `isFloat` | `primitive/is-float.ts` | Checks for valid floating-point number | `value: string` | `boolean` |
45
- | `isUint` | `primitive/is-uint.ts` | Checks for valid non-negative integer | `value: string`, `options?: { gt?: number }` | `boolean` |
46
- | `isNonZeroUint` | `primitive/is-non-zero-uint.ts` | Checks for valid non-negative integer > 0 | `value: string` | `boolean` |
47
- | `isQuantity` | `primitive/is-quantity.ts` | Checks for number + unit suffix | `value: string`, `units: string[]`, `numberType?: 'int' \| 'uint' \| 'float'` | `boolean` |
48
- | `range` | `primitive/range.ts` | Checks if value is within a numeric range | `value: string`, `from: number`, `to: number` | `boolean` |
49
- | `splitUnit` | `primitive/split-unit.ts` | Splits a value into numeric and unit parts | `value: string` | `{ num: string, unit: string }` |
50
-
51
- ### Validation Logic Details
52
-
53
- **`isInt`** -- Uses the regex `/^-?\d+$/` to match an optional leading minus sign followed by one or more digits. Implements the [WHATWG signed integers](https://html.spec.whatwg.org/dev/common-microsyntaxes.html#signed-integers) microsyntax.
54
-
55
- ```typescript
56
- // src/primitive/is-int.ts
57
- export function isInt(value: string) {
58
- return /^-?\d+$/.test(value);
59
- }
60
- ```
61
-
62
- **`isFloat`** -- Trims the value and parses it with `Number.parseFloat()`, checking that the result is finite. This allows standard floating-point notation including scientific notation. Loosely based on the [WHATWG floating-point numbers](https://html.spec.whatwg.org/dev/common-microsyntaxes.html#floating-point-numbers) microsyntax. Note that `Number.parseFloat()` is more permissive than the strict WHATWG grammar (e.g., it accepts leading-dot values like `".5"` and ignores trailing non-numeric characters).
63
-
64
- ```typescript
65
- // src/primitive/is-float.ts
66
- export function isFloat(value: string) {
67
- return value === value.trim() && Number.isFinite(Number.parseFloat(value));
68
- }
69
- ```
70
-
71
- **`isUint`** -- Matches `/^\d+$/` for non-negative integers. Optionally accepts a `gt` constraint to require the parsed value to be strictly greater than a given number. Implements the [WHATWG non-negative integers](https://html.spec.whatwg.org/dev/common-microsyntaxes.html#non-negative-integers) microsyntax.
72
-
73
- **`isNonZeroUint`** -- Matches `/^\d+$/` and additionally rejects strings consisting entirely of zeros (`/^0+$/`). This ensures the value is a positive integer.
74
-
75
- **`isQuantity`** -- Composes `splitUnit` with `isInt`/`isUint`/`isFloat` to validate strings like `"10px"` or `"1.5em"`. First splits the value into numeric and unit parts, checks the unit against a list of allowed suffixes (case-insensitive), then validates the numeric part according to the specified `numberType`.
76
-
77
- **`range`** -- Parses the value as a float and checks whether it falls within the inclusive range `[from, to]`. Returns `false` if the value cannot be parsed as a number.
78
-
79
- **`splitUnit`** -- Uses the regex `/(^-?\.\d+|^-?\d+(?:\.\d+(?:e[+-]\d+)?)?)([a-z]+$)/i` to separate a value like `"10px"` into `{ num: "10", unit: "px" }`. If no unit suffix is found, returns `{ num: value, unit: "" }`.
80
-
81
- ---
82
-
83
- ## WHATWG Validators
84
-
85
- WHATWG validators implement the microsyntaxes defined in the [HTML Living Standard](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html).
86
-
87
- ### SimpleColor
88
-
89
- **File:** `src/whatwg/check-simple-color.ts`
90
-
91
- Validates a [valid simple color](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-simple-colour): exactly seven characters — `#` followed by six ASCII hex digits (e.g., `#ff0000`). This is **not** the same as the CSS `<color>` type, which accepts named colors, `rgb()`, `hsl()`, etc. Used for `input[type=color]` value validation.
92
-
93
- ### Email
94
-
95
- **File:** `src/whatwg/check-email.ts`
96
-
97
- Validates a [valid email address](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address) per the HTML spec. The regex is the verbatim pattern from the spec, using explicit ASCII ranges (`[a-zA-Z0-9]`) instead of `\w` to guarantee ASCII-only matching. Used for `input[type=email]` value validation.
98
-
99
- ### DateTime Subsystem
100
-
101
- The DateTime subsystem validates all date and time formats defined by the WHATWG specification. It is the most complex validator group, consisting of 12 individual format checkers that share a common token validation layer.
102
-
103
- **Entry point:** `src/whatwg/check-datetime/index.ts`
104
-
105
- The top-level `checkDateTime` function tries all formats using `checkMultiTypes` and returns the best match:
106
-
107
- ```typescript
108
- // src/whatwg/check-datetime/index.ts
109
- const checks = [
110
- checkDateString(),
111
- checkTimeString(),
112
- checkMonthString(),
113
- checkYearlessDateString(),
114
- checkLocalDateAndTimeString(),
115
- checkNormalizedLocalDateAndTimeString(),
116
- checkTimeZoneOffsetString(),
117
- checkGlobalDateAndTimeString(),
118
- checkWeekString(),
119
- checkYearString(),
120
- checkDurationISO8601LikeString(),
121
- checkDurationComponentListString(),
122
- ];
123
-
124
- export const checkDateTime: CustomSyntaxChecker = () => value => {
125
- return checkMultiTypes(value, checks);
126
- };
127
- ```
128
-
129
- #### DateTime Format Checkers
130
-
131
- | Function | File | Format | Example | Spec Reference |
132
- | --------------------------------------- | -------------------------------- | ----------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
133
- | `checkDateString` | `date-string.ts` | `YYYY-MM-DD` | `2024-01-15` | [Dates](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#dates) |
134
- | `checkMonthString` | `month-string.ts` | `YYYY-MM` | `2024-01` | [Months](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-month-string) |
135
- | `checkWeekString` | `week-string.ts` | `YYYY-Www` | `2024-W03` | [Weeks](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#weeks) |
136
- | `checkTimeString` | `time-string.ts` | `HH:MM[:SS[.sss]]` | `14:30:00` | [Times](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#times) |
137
- | `checkYearlessDateString` | `yearless-date-string.ts` | `MM-DD` | `01-15` | [Yearless dates](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#yearless-dates) |
138
- | `checkYearString` | `year-string.ts` | `YYYY` (4+ digits, > 0) | `2024` | [Common microsyntaxes](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html) |
139
- | `checkLocalDateAndTimeString` | `local-date-and-time-string.ts` | `YYYY-MM-DDThh:mm[:ss[.sss]]` | `2024-01-15T14:30` | [Local dates and times](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-local-date-and-time-string) |
140
- | `checkNormalizedLocalDateAndTimeString` | `local-date-and-time-string.ts` | Normalized local date-time | `2024-01-15T14:30` | [Normalized local dates and times](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-normalised-local-date-and-time-string) |
141
- | `checkGlobalDateAndTimeString` | `global-date-and-time-string.ts` | Date + time + time-zone | `2024-01-15T14:30:00Z` | [Global dates and times](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#global-dates-and-times) |
142
- | `checkTimeZoneOffsetString` | `time-zone-offset-string.ts` | `Z` or `+HH:MM` / `-HH:MM` | `+09:00` | [Time zones](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#time-zones) |
143
- | `checkDurationISO8601LikeString` | `duration-string.ts` | ISO 8601-like (`PnDTnHnMnS`) | `PT1H30M` | [Durations](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#durations) |
144
- | `checkDurationComponentListString` | `duration-string.ts` | Component list (`1h 30m 5s`) | `1h 30m 5s` | [Durations](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#durations) |
145
-
146
- #### Shared Token Definitions
147
-
148
- All datetime checkers share a common token validation layer defined in `datetime-tokens.ts`. The `datetimeTokenCheck` object provides reusable `TokenEachCheck` functions for each datetime component:
149
-
150
- ```mermaid
151
- graph TD
152
- A[checkDateTime] --> B[checkMultiTypes]
153
- B --> C[checkDateString]
154
- B --> D[checkTimeString]
155
- B --> E[checkMonthString]
156
- B --> F[checkYearlessDateString]
157
- B --> G[checkLocalDateAndTimeString]
158
- B --> H[checkNormalizedLocalDateAndTimeString]
159
- B --> I[checkGlobalDateAndTimeString]
160
- B --> J[checkTimeZoneOffsetString]
161
- B --> K[checkWeekString]
162
- B --> L[checkYearString]
163
- B --> M[checkDurationISO8601LikeString]
164
- B --> N[checkDurationComponentListString]
165
-
166
- C --> O[datetimeTokenCheck]
167
- D --> O
168
- E --> O
169
- F --> O
170
- G --> O
171
- H --> O
172
- I --> O
173
- J --> O
174
- K --> O
175
- L --> O
176
- M --> O
177
- N --> O
178
-
179
- O --> P[year]
180
- O --> Q[month]
181
- O --> R[date]
182
- O --> S[hour]
183
- O --> T[minute]
184
- O --> U[second]
185
- O --> V[secondFractionalPart]
186
- O --> W[week]
187
- O --> X[hyphen / colon / separators]
188
- ```
189
-
190
- The shared token checks validate:
191
-
192
- | Token Check | Validates | Constraints |
193
- | ---------------------------------- | ---------------------- | --------------------------------------------- |
194
- | `year` | Year component | 4+ ASCII digits, value > 0 |
195
- | `month` | Month component | Exactly 2 digits, 1--12 |
196
- | `date` | Day-of-month component | Exactly 2 digits, 1--maxday (leap-year aware) |
197
- | `hour` | Hour component | Exactly 2 digits, 0--23 |
198
- | `minute` | Minute component | Exactly 2 digits, 0--59 |
199
- | `second` | Second component | Exactly 2 digits, 0--59 |
200
- | `secondFractionalPart` | Fractional seconds | 1--3 ASCII digits |
201
- | `week` | ISO week number | Exactly 2 digits, 1--maxweek for the year |
202
- | `hyphen` | `-` separator | Exactly U+002D |
203
- | `colon` | `:` separator | Exactly U+003A |
204
- | `colonOrEnd` | `:` or end of input | Optional colon |
205
- | `decimalPointOrEnd` | `.` or end of input | Optional period |
206
- | `localDateTimeSeparator` | `T` or space | Date-time boundary |
207
- | `normalizedlocalDateTimeSeparator` | `T` only | Strict normalized form |
208
- | `plusOrMinusSign` | `+` or `-` | Time-zone sign |
209
- | `weekSign` | `W` | Week string marker |
210
- | `extra` | Trailing content | Must be empty (rejects extra tokens) |
211
-
212
- The `datetimeTokenCheck` object also maintains `_year` and `_month` as mutable state during validation, enabling the `date` checker to compute the correct maximum day for the given year and month (including leap year logic).
213
-
214
- ### Autocomplete Validator
215
-
216
- **Source:** `src/whatwg/check-autocomplete.ts`
217
-
218
- Validates the `autocomplete` attribute value according to the [WHATWG autofill specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete).
219
-
220
- The validator uses **backward parsing** (right-to-left), matching the WHATWG specification algorithm. The attribute value is parsed as an ordered set of space-separated tokens (ASCII case-insensitive). It recognizes:
221
-
222
- - **Keywords:** `on`, `off` (standalone, no other tokens allowed)
223
- - **Named groups:** Tokens starting with `section-` (optional prefix)
224
- - **Address parts:** `shipping`, `billing` (optional)
225
- - **Contacting tokens:** `home`, `work`, `mobile`, `fax`, `pager` (optional, only valid before contactable field names)
226
- - **Autofill field names:** `name`, `given-name`, `postal-code`, `cc-number`, etc. (44 names)
227
- - **Contactable field names:** `tel`, `tel-country-code`, `email`, `impp`, etc. (10 names)
228
- - **WebAuthn:** `webauthn` (optional trailing token with category re-determination)
229
-
230
- The backward parsing algorithm:
231
-
232
- 1. Determine the field name from the **last** token and classify into Normal, Contact, or Credential category
233
- 2. Handle `webauthn` credential token: if present, consume it and re-determine the category from the preceding token
234
- 3. Optionally consume a contacting token (only valid for Contact category fields)
235
- 4. Reject contacting tokens preceding Normal category fields
236
- 5. Optionally consume `shipping`/`billing`
237
- 6. Optionally consume `section-*` named group
238
- 7. Report any remaining tokens as extra
239
-
240
- The grammar enforced is: `[section-*] [shipping|billing] [home|work|...] <field-name> [webauthn]`. A field name is always required -- prefix-only values (e.g., `section-foo` or `shipping` alone) are rejected. Duplicate tokens, incorrect ordering, and unrecognized tokens produce detailed error results with Levenshtein-distance-based typo correction candidates.
241
-
242
- ### Link Type Validator
243
-
244
- **Source:** `src/whatwg/check-link-type.ts`
245
-
246
- Validates link type values (the `rel` attribute) against the [WHATWG link types registry](https://html.spec.whatwg.org/multipage/links.html#linkTypes) and the [Microformats existing-rel-values](https://microformats.org/wiki/existing-rel-values) registry.
247
-
248
- The validator is parameterized by element context:
249
-
250
- | Option | Context | Description |
251
- | ----------------- | -------------------- | ------------------------------------------ |
252
- | `el: 'link'` | `<link>` in `<head>` | All link types allowed on the link element |
253
- | `el: 'body link'` | `<link>` in `<body>` | Only link types with `body-ok` flag |
254
- | `el: 'a, area'` | `<a>`, `<area>` | Link types allowed on anchor/area elements |
255
- | `el: 'form'` | `<form>` | Link types allowed on form elements |
256
-
257
- The validator explicitly rejects keywords from the Microformats dropped, rejected, non-HTML, and dropped-without-prejudice lists. It builds an enumeration of allowed keywords for the given context and delegates to `checkList` for final validation.
258
-
259
- This is registered in `defs.ts` as four separate type definitions: `LinkTypeForLinkElement`, `LinkTypeForLinkElementInBody`, `LinkTypeForAnchorAndAreaElement`, and `LinkTypeForFormElement`.
260
-
261
- ### MIME Type Validator
262
-
263
- **Source:** `src/whatwg/check-mime-type.ts`
264
-
265
- Validates MIME type strings according to the [WHATWG MIME Sniffing specification](https://mimesniff.spec.whatwg.org/#valid-mime-type).
266
-
267
- The validator integrates the `whatwg-mimetype` npm package for parsing. It:
268
-
269
- 1. Attempts to parse the value using `MIMEType.parse()`
270
- 2. If parsing succeeds, checks whether the serialized essence matches the input
271
- 3. Optionally restricts to MIME types with no parameters (via the `withoutParameters` option)
272
- 4. Reports extra tokens or syntax errors with appropriate candidates
273
-
274
- ```typescript
275
- // Invocation in defs.ts
276
- MIMEType: {
277
- ref: 'https://mimesniff.spec.whatwg.org/#valid-mime-type',
278
- is: checkMIMEType(),
279
- },
280
- ```
281
-
282
- ### URL and Name Validators
283
-
284
- These validators implement simple predicate-based checks for various WHATWG-defined name formats.
285
-
286
- | Function | File | Validates | Logic |
287
- | ----------------------- | ------------------------------------ | ---------------------------------- | ----------------------------------------------------------------------------------- |
288
- | `isAbsURL` | `whatwg/is-abs-url.ts` | Absolute URL | Uses `new URL(value)` constructor; returns `false` on `ERR_INVALID_URL` |
289
- | `isBrowserContextName` | `whatwg/is-browser-context-name.ts` | Browsing context name (deprecated) | Non-empty and does not start with `_` |
290
- | `isCustomElementName` | `whatwg/is-custom-element-name.ts` | Custom element name | Must start with `[a-z]`, contain `-`, use only PCENChar, and not be a reserved name |
291
- | `isItempropName` | `whatwg/is-itemprop-name.ts` | Itemprop property name | Must not contain `:`, `.`, or space |
292
- | `isNavigableTargetName` | `whatwg/is-navigable-target-name.ts` | Navigable target name | Non-empty, no ASCII tab/newline, does not start with `_` |
293
-
294
- **`isCustomElementName`** is the most complex, implementing the [PotentialCustomElementName](https://html.spec.whatwg.org/multipage/custom-elements.html#prod-potentialcustomelementname) production rule. It rejects the eight reserved names (`annotation-xml`, `color-profile`, `font-face`, `font-face-src`, `font-face-uri`, `font-face-format`, `font-face-name`, `missing-glyph`), requires a leading ASCII lowercase alpha, a mandatory hyphen, and validates all characters against the PCENChar character class.
295
-
296
- **`isBrowserContextName`** is deprecated in favor of `isNavigableTargetName`.
297
-
298
- All of these are `FormattedPrimitiveTypeCreator` factories -- they return `() => (value: string) => boolean`.
299
-
300
- ---
301
-
302
- ## RFC Validators
303
-
304
- ### BCP 47 (Language Tags)
305
-
306
- **Source:** `src/rfc/is-bcp-47.ts`
307
-
308
- Validates language tags according to [BCP 47](https://tools.ietf.org/rfc/bcp/bcp47.html) (RFC 5646 + RFC 4647).
309
-
310
- The validator integrates the `bcp-47` npm package. It parses the value and checks that either a valid `language` subtag or a `privateuse` subtag (e.g., `x-default`) is present:
311
-
312
- ```typescript
313
- // src/rfc/is-bcp-47.ts
314
- export const isBCP47: FormattedPrimitiveTypeCreator = () => {
315
- return value => {
316
- const { language, privateuse } = parse(value);
317
- return !!language || (privateuse != null && privateuse.length > 0);
318
- };
319
- };
320
- ```
321
-
322
- This is registered in `defs.ts` as the `BCP47` type, used for the `lang` and `hreflang` attributes on HTML elements.
323
-
324
- ---
325
-
326
- ## W3C Validators
327
-
328
- ### Serialized Permissions Policy
329
-
330
- **Source:** `src/w3c/check-serialized-permissions-policy.ts`
331
-
332
- Validates serialized permissions policy strings according to the [W3C Permissions Policy specification](https://w3c.github.io/webappsec-permissions-policy/#serialized-permissions-policy).
333
-
334
- The validator parses the ABNF grammar:
335
-
336
- ```abnf
337
- serialized-permissions-policy = serialized-policy-directive *(";" serialized-policy-directive)
338
- serialized-policy-directive = feature-identifier [RWS allow-list]
339
- feature-identifier = 1*( ALPHA / DIGIT / "-")
340
- allow-list = allow-list-value *(RWS allow-list-value)
341
- allow-list-value = serialized-origin / "*" / "'self'" / "'src'" / "'none'"
342
- ```
343
-
344
- > **Note:** The ABNF above reflects the implementation's grammar, which makes `allow-list` optional (`[RWS allow-list]`). The W3C specification defines `serialized-policy-directive = feature-identifier RWS allow-list` with a mandatory `allow-list`. This deviation is intentional -- the implementation accepts feature identifiers without an explicit allow-list.
345
-
346
- Key validation steps:
347
-
348
- 1. Splits the value on `;` into policy directives
349
- 2. For each directive, extracts the feature identifier and validates it matches `/^[\da-z-]+$/i`
350
- 3. For each allow-list value, checks against the keywords `*`, `'self'`, `'src'`, `'none'`, or validates as a serialized origin
351
- 4. Serialized origins are validated using the `URL` constructor, rejecting pathnames, queries, hashes, usernames, passwords, and characters that must be percent-encoded (`'`, `*`, `,`, `;`)
352
-
353
- ---
354
-
355
- ## Adding New Validators
356
-
357
- Follow these steps to add a new validator to the system.
358
-
359
- ### Step 1: Create the Validator File
360
-
361
- Place the file in the appropriate directory based on the specification:
362
-
363
- - `src/primitive/` for basic format checks
364
- - `src/whatwg/` for WHATWG HTML spec validators
365
- - `src/rfc/` for RFC-defined formats
366
- - `src/w3c/` for W3C specification formats
367
-
368
- **Template for a `FormattedPrimitiveTypeCreator`:**
369
-
370
- ```typescript
371
- // src/whatwg/is-my-format.ts
372
- import type { FormattedPrimitiveTypeCreator } from '../types.js';
373
-
374
- /**
375
- * Checks whether a string is a valid my-format value.
376
- *
377
- * @see https://spec.example.org/my-format
378
- */
379
- export const isMyFormat: FormattedPrimitiveTypeCreator = () => {
380
- return value => {
381
- // Validation logic returning boolean
382
- return /^[a-z]+-[a-z]+$/.test(value);
383
- };
384
- };
385
- ```
386
-
387
- **Template for a `CustomSyntaxChecker`:**
388
-
389
- ```typescript
390
- // src/whatwg/check-my-syntax.ts
391
- import type { CustomSyntaxChecker } from '../types.js';
392
-
393
- import { matched, unmatched } from '../match-result.js';
394
-
395
- /**
396
- * Validates a my-syntax value.
397
- *
398
- * @see https://spec.example.org/my-syntax
399
- */
400
- export const checkMySyntax: CustomSyntaxChecker = () => value => {
401
- if (!value) {
402
- return unmatched(value, 'empty-token');
403
- }
404
- // Validation logic returning Result
405
- return matched();
406
- };
407
- ```
408
-
409
- ### Step 2: Export from the Directory Index
410
-
411
- If you placed the validator in a directory with an `index.ts`, add the export:
412
-
413
- ```typescript
414
- // src/primitive/index.ts
415
- export { isMyFormat } from './is-my-format.js';
416
- ```
417
-
418
- ### Step 3: Register in defs.ts
419
-
420
- Import the validator and add it to the `defs` object:
421
-
422
- ```typescript
423
- // src/defs.ts
424
- import { isMyFormat } from './whatwg/is-my-format.js';
425
- // or
426
- import { checkMySyntax } from './whatwg/check-my-syntax.js';
427
-
428
- export const defs: Defs = {
429
- // ... existing definitions ...
430
-
431
- // For FormattedPrimitiveTypeCreator (wrap with matches()):
432
- MyFormat: {
433
- ref: 'https://spec.example.org/my-format',
434
- expects: [
435
- {
436
- type: 'format',
437
- value: 'my format',
438
- },
439
- ],
440
- is: matches(isMyFormat()),
441
- },
442
-
443
- // For CustomSyntaxChecker (use directly):
444
- MySyntax: {
445
- ref: 'https://spec.example.org/my-syntax',
446
- expects: [
447
- {
448
- type: 'format',
449
- value: 'my syntax',
450
- },
451
- ],
452
- is: checkMySyntax(),
453
- },
454
- };
455
- ```
456
-
457
- ### Step 4: Add Tests
458
-
459
- Create a test file alongside the validator:
460
-
461
- ```typescript
462
- // src/whatwg/is-my-format.spec.ts
463
- import { isMyFormat } from './is-my-format.js';
464
-
465
- const check = isMyFormat();
466
-
467
- test('valid values', () => {
468
- expect(check('foo-bar')).toBe(true);
469
- expect(check('hello-world')).toBe(true);
470
- });
471
-
472
- test('invalid values', () => {
473
- expect(check('')).toBe(false);
474
- expect(check('UPPER-CASE')).toBe(false);
475
- });
476
- ```
477
-
478
- ### Concrete Example: Adding a Hypothetical `isDataURL` Validator
479
-
480
- Suppose you need to validate `data:` URLs specifically.
481
-
482
- **1. Create the file:**
483
-
484
- ```typescript
485
- // src/whatwg/is-data-url.ts
486
- import type { FormattedPrimitiveTypeCreator } from '../types.js';
487
-
488
- /**
489
- * Checks whether a string is a valid data URL.
490
- *
491
- * @see https://fetch.spec.whatwg.org/#data-urls
492
- */
493
- export const isDataURL: FormattedPrimitiveTypeCreator = () => {
494
- return value => {
495
- return /^data:[^,]*,/.test(value);
496
- };
497
- };
498
- ```
499
-
500
- **2. Register in `defs.ts`:**
501
-
502
- ```typescript
503
- import { isDataURL } from './whatwg/is-data-url.js';
504
-
505
- // Inside the defs object:
506
- DataURL: {
507
- ref: 'https://fetch.spec.whatwg.org/#data-urls',
508
- expects: [
509
- {
510
- type: 'format',
511
- value: 'data URL',
512
- },
513
- ],
514
- is: matches(isDataURL()),
515
- },
516
- ```
517
-
518
- **3. Add tests:**
519
-
520
- ```typescript
521
- // src/whatwg/is-data-url.spec.ts
522
- import { isDataURL } from './is-data-url.js';
523
-
524
- const check = isDataURL();
525
-
526
- test('valid data URLs', () => {
527
- expect(check('data:text/plain,Hello')).toBe(true);
528
- expect(check('data:text/html,<h1>Hello</h1>')).toBe(true);
529
- expect(check('data:image/png;base64,abc123...')).toBe(true);
530
- });
531
-
532
- test('invalid data URLs', () => {
533
- expect(check('https://example.com')).toBe(false);
534
- expect(check('data:')).toBe(false);
535
- });
536
- ```