@markuplint/types 4.8.1 → 5.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +256 -0
- package/ARCHITECTURE.md +256 -0
- package/CHANGELOG.md +18 -2
- package/README.md +37 -2
- package/SKILL.md +100 -0
- package/docs/check-pipeline.ja.md +494 -0
- package/docs/check-pipeline.md +494 -0
- package/docs/token-system.ja.md +584 -0
- package/docs/token-system.md +584 -0
- package/docs/type-system.ja.md +556 -0
- package/docs/type-system.md +556 -0
- package/docs/validators.ja.md +524 -0
- package/docs/validators.md +524 -0
- package/lib/check-base.d.ts +81 -1
- package/lib/check-base.js +87 -0
- package/lib/check-multi-types.d.ts +12 -1
- package/lib/check-multi-types.js +11 -0
- package/lib/check.d.ts +13 -0
- package/lib/check.js +13 -0
- package/lib/css-defs.d.ts +8 -0
- package/lib/css-defs.js +8 -0
- package/lib/css-overrides.d.ts +7 -0
- package/lib/css-overrides.js +7 -0
- package/lib/css-syntax.d.ts +11 -0
- package/lib/css-syntax.js +12 -1
- package/lib/css-tokenizers.d.ts +6 -0
- package/lib/css-tokenizers.js +6 -0
- package/lib/debug.d.ts +3 -0
- package/lib/debug.js +3 -0
- package/lib/defs.d.ts +8 -0
- package/lib/defs.js +26 -0
- package/lib/directive.d.ts +14 -0
- package/lib/directive.js +14 -0
- package/lib/enum.d.ts +11 -0
- package/lib/enum.js +11 -0
- package/lib/get-candidate.d.ts +11 -0
- package/lib/get-candidate.js +11 -0
- package/lib/index.d.ts +10 -1
- package/lib/index.js +9 -1
- package/lib/keyword-type.d.ts +13 -0
- package/lib/keyword-type.js +13 -0
- package/lib/list.d.ts +13 -0
- package/lib/list.js +13 -0
- package/lib/match-result.d.ts +22 -1
- package/lib/match-result.js +21 -0
- package/lib/number.d.ts +12 -0
- package/lib/number.js +12 -0
- package/lib/pattern.d.ts +12 -0
- package/lib/pattern.js +33 -0
- package/lib/primitive/is-float.d.ts +4 -1
- package/lib/primitive/is-float.js +4 -1
- package/lib/primitive/is-int.d.ts +4 -1
- package/lib/primitive/is-int.js +4 -1
- package/lib/primitive/is-non-zero-uint.d.ts +3 -2
- package/lib/primitive/is-non-zero-uint.js +3 -2
- package/lib/primitive/is-quantity.d.ts +5 -3
- package/lib/primitive/is-quantity.js +5 -3
- package/lib/primitive/is-uint.d.ts +5 -1
- package/lib/primitive/is-uint.js +5 -1
- package/lib/primitive/range.d.ts +5 -4
- package/lib/primitive/range.js +5 -4
- package/lib/primitive/split-unit.d.ts +3 -2
- package/lib/primitive/split-unit.js +3 -2
- package/lib/rfc/is-bcp-47.d.ts +2 -0
- package/lib/rfc/is-bcp-47.js +4 -2
- package/lib/token/token-collection.d.ts +108 -3
- package/lib/token/token-collection.js +110 -3
- package/lib/token/token.d.ts +66 -2
- package/lib/token/token.js +89 -21
- package/lib/token/types.d.ts +9 -0
- package/lib/types.d.ts +108 -1
- package/lib/types.schema.d.ts +4 -1
- package/lib/w3c/check-serialized-permissions-policy.d.ts +2 -0
- package/lib/w3c/check-serialized-permissions-policy.js +2 -0
- package/lib/whatwg/check-autocomplete.d.ts +10 -0
- package/lib/whatwg/check-autocomplete.js +214 -159
- package/lib/whatwg/check-datetime/date-string.d.ts +2 -0
- package/lib/whatwg/check-datetime/date-string.js +2 -0
- package/lib/whatwg/check-datetime/datetime-tokens.d.ts +13 -0
- package/lib/whatwg/check-datetime/datetime-tokens.js +14 -1
- package/lib/whatwg/check-datetime/duration-string.d.ts +7 -0
- package/lib/whatwg/check-datetime/duration-string.js +7 -0
- package/lib/whatwg/check-datetime/global-date-and-time-string.d.ts +2 -0
- package/lib/whatwg/check-datetime/global-date-and-time-string.js +2 -0
- package/lib/whatwg/check-datetime/index.d.ts +5 -0
- package/lib/whatwg/check-datetime/index.js +5 -0
- package/lib/whatwg/check-datetime/local-date-and-time-string.d.ts +4 -0
- package/lib/whatwg/check-datetime/local-date-and-time-string.js +4 -0
- package/lib/whatwg/check-datetime/month-string.d.ts +2 -0
- package/lib/whatwg/check-datetime/month-string.js +2 -0
- package/lib/whatwg/check-datetime/time-string.d.ts +2 -0
- package/lib/whatwg/check-datetime/time-string.js +2 -0
- package/lib/whatwg/check-datetime/time-zone-offset-string.d.ts +8 -0
- package/lib/whatwg/check-datetime/time-zone-offset-string.js +8 -0
- package/lib/whatwg/check-datetime/week-string.d.ts +2 -0
- package/lib/whatwg/check-datetime/week-string.js +2 -0
- package/lib/whatwg/check-datetime/year-string.d.ts +2 -0
- package/lib/whatwg/check-datetime/year-string.js +2 -0
- package/lib/whatwg/check-datetime/yearless-date-string.d.ts +2 -0
- package/lib/whatwg/check-datetime/yearless-date-string.js +2 -0
- package/lib/whatwg/check-link-type.d.ts +108 -1
- package/lib/whatwg/check-link-type.js +17 -7
- package/lib/whatwg/check-mime-type.d.ts +4 -1
- package/lib/whatwg/check-mime-type.js +6 -3
- package/lib/whatwg/is-abs-url.d.ts +2 -0
- package/lib/whatwg/is-abs-url.js +3 -4
- package/lib/whatwg/is-browser-context-name.d.ts +2 -2
- package/lib/whatwg/is-browser-context-name.js +2 -2
- package/lib/whatwg/is-custom-element-name.d.ts +1 -1
- package/lib/whatwg/is-custom-element-name.js +1 -1
- package/lib/whatwg/is-itemprop-name.d.ts +1 -0
- package/lib/whatwg/is-itemprop-name.js +1 -0
- package/lib/whatwg/is-navigable-target-name.d.ts +2 -0
- package/lib/whatwg/is-navigable-target-name.js +2 -0
- package/package.json +8 -6
- package/types.schema.json +10 -1
|
@@ -0,0 +1,524 @@
|
|
|
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
|
+
### DateTime Subsystem
|
|
88
|
+
|
|
89
|
+
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.
|
|
90
|
+
|
|
91
|
+
**Entry point:** `src/whatwg/check-datetime/index.ts`
|
|
92
|
+
|
|
93
|
+
The top-level `checkDateTime` function tries all formats using `checkMultiTypes` and returns the best match:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// src/whatwg/check-datetime/index.ts
|
|
97
|
+
const checks = [
|
|
98
|
+
checkDateString(),
|
|
99
|
+
checkTimeString(),
|
|
100
|
+
checkMonthString(),
|
|
101
|
+
checkYearlessDateString(),
|
|
102
|
+
checkLocalDateAndTimeString(),
|
|
103
|
+
checkNormalizedLocalDateAndTimeString(),
|
|
104
|
+
checkTimeZoneOffsetString(),
|
|
105
|
+
checkGlobalDateAndTimeString(),
|
|
106
|
+
checkWeekString(),
|
|
107
|
+
checkYearString(),
|
|
108
|
+
checkDurationISO8601LikeString(),
|
|
109
|
+
checkDurationComponentListString(),
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
export const checkDateTime: CustomSyntaxChecker = () => value => {
|
|
113
|
+
return checkMultiTypes(value, checks);
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### DateTime Format Checkers
|
|
118
|
+
|
|
119
|
+
| Function | File | Format | Example | Spec Reference |
|
|
120
|
+
| --------------------------------------- | -------------------------------- | ----------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
121
|
+
| `checkDateString` | `date-string.ts` | `YYYY-MM-DD` | `2024-01-15` | [Dates](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#dates) |
|
|
122
|
+
| `checkMonthString` | `month-string.ts` | `YYYY-MM` | `2024-01` | [Months](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-month-string) |
|
|
123
|
+
| `checkWeekString` | `week-string.ts` | `YYYY-Www` | `2024-W03` | [Weeks](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#weeks) |
|
|
124
|
+
| `checkTimeString` | `time-string.ts` | `HH:MM[:SS[.sss]]` | `14:30:00` | [Times](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#times) |
|
|
125
|
+
| `checkYearlessDateString` | `yearless-date-string.ts` | `MM-DD` | `01-15` | [Yearless dates](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#yearless-dates) |
|
|
126
|
+
| `checkYearString` | `year-string.ts` | `YYYY` (4+ digits, > 0) | `2024` | [Common microsyntaxes](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html) |
|
|
127
|
+
| `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) |
|
|
128
|
+
| `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) |
|
|
129
|
+
| `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) |
|
|
130
|
+
| `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) |
|
|
131
|
+
| `checkDurationISO8601LikeString` | `duration-string.ts` | ISO 8601-like (`PnDTnHnMnS`) | `PT1H30M` | [Durations](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#durations) |
|
|
132
|
+
| `checkDurationComponentListString` | `duration-string.ts` | Component list (`1h 30m 5s`) | `1h 30m 5s` | [Durations](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#durations) |
|
|
133
|
+
|
|
134
|
+
#### Shared Token Definitions
|
|
135
|
+
|
|
136
|
+
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:
|
|
137
|
+
|
|
138
|
+
```mermaid
|
|
139
|
+
graph TD
|
|
140
|
+
A[checkDateTime] --> B[checkMultiTypes]
|
|
141
|
+
B --> C[checkDateString]
|
|
142
|
+
B --> D[checkTimeString]
|
|
143
|
+
B --> E[checkMonthString]
|
|
144
|
+
B --> F[checkYearlessDateString]
|
|
145
|
+
B --> G[checkLocalDateAndTimeString]
|
|
146
|
+
B --> H[checkNormalizedLocalDateAndTimeString]
|
|
147
|
+
B --> I[checkGlobalDateAndTimeString]
|
|
148
|
+
B --> J[checkTimeZoneOffsetString]
|
|
149
|
+
B --> K[checkWeekString]
|
|
150
|
+
B --> L[checkYearString]
|
|
151
|
+
B --> M[checkDurationISO8601LikeString]
|
|
152
|
+
B --> N[checkDurationComponentListString]
|
|
153
|
+
|
|
154
|
+
C --> O[datetimeTokenCheck]
|
|
155
|
+
D --> O
|
|
156
|
+
E --> O
|
|
157
|
+
F --> O
|
|
158
|
+
G --> O
|
|
159
|
+
H --> O
|
|
160
|
+
I --> O
|
|
161
|
+
J --> O
|
|
162
|
+
K --> O
|
|
163
|
+
L --> O
|
|
164
|
+
M --> O
|
|
165
|
+
N --> O
|
|
166
|
+
|
|
167
|
+
O --> P[year]
|
|
168
|
+
O --> Q[month]
|
|
169
|
+
O --> R[date]
|
|
170
|
+
O --> S[hour]
|
|
171
|
+
O --> T[minute]
|
|
172
|
+
O --> U[second]
|
|
173
|
+
O --> V[secondFractionalPart]
|
|
174
|
+
O --> W[week]
|
|
175
|
+
O --> X[hyphen / colon / separators]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The shared token checks validate:
|
|
179
|
+
|
|
180
|
+
| Token Check | Validates | Constraints |
|
|
181
|
+
| ---------------------------------- | ---------------------- | --------------------------------------------- |
|
|
182
|
+
| `year` | Year component | 4+ ASCII digits, value > 0 |
|
|
183
|
+
| `month` | Month component | Exactly 2 digits, 1--12 |
|
|
184
|
+
| `date` | Day-of-month component | Exactly 2 digits, 1--maxday (leap-year aware) |
|
|
185
|
+
| `hour` | Hour component | Exactly 2 digits, 0--23 |
|
|
186
|
+
| `minute` | Minute component | Exactly 2 digits, 0--59 |
|
|
187
|
+
| `second` | Second component | Exactly 2 digits, 0--59 |
|
|
188
|
+
| `secondFractionalPart` | Fractional seconds | 1--3 ASCII digits |
|
|
189
|
+
| `week` | ISO week number | Exactly 2 digits, 1--maxweek for the year |
|
|
190
|
+
| `hyphen` | `-` separator | Exactly U+002D |
|
|
191
|
+
| `colon` | `:` separator | Exactly U+003A |
|
|
192
|
+
| `colonOrEnd` | `:` or end of input | Optional colon |
|
|
193
|
+
| `decimalPointOrEnd` | `.` or end of input | Optional period |
|
|
194
|
+
| `localDateTimeSeparator` | `T` or space | Date-time boundary |
|
|
195
|
+
| `normalizedlocalDateTimeSeparator` | `T` only | Strict normalized form |
|
|
196
|
+
| `plusOrMinusSign` | `+` or `-` | Time-zone sign |
|
|
197
|
+
| `weekSign` | `W` | Week string marker |
|
|
198
|
+
| `extra` | Trailing content | Must be empty (rejects extra tokens) |
|
|
199
|
+
|
|
200
|
+
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).
|
|
201
|
+
|
|
202
|
+
### Autocomplete Validator
|
|
203
|
+
|
|
204
|
+
**Source:** `src/whatwg/check-autocomplete.ts`
|
|
205
|
+
|
|
206
|
+
Validates the `autocomplete` attribute value according to the [WHATWG autofill specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete).
|
|
207
|
+
|
|
208
|
+
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:
|
|
209
|
+
|
|
210
|
+
- **Keywords:** `on`, `off` (standalone, no other tokens allowed)
|
|
211
|
+
- **Named groups:** Tokens starting with `section-` (optional prefix)
|
|
212
|
+
- **Address parts:** `shipping`, `billing` (optional)
|
|
213
|
+
- **Contacting tokens:** `home`, `work`, `mobile`, `fax`, `pager` (optional, only valid before contactable field names)
|
|
214
|
+
- **Autofill field names:** `name`, `given-name`, `postal-code`, `cc-number`, etc. (44 names)
|
|
215
|
+
- **Contactable field names:** `tel`, `tel-country-code`, `email`, `impp`, etc. (10 names)
|
|
216
|
+
- **WebAuthn:** `webauthn` (optional trailing token with category re-determination)
|
|
217
|
+
|
|
218
|
+
The backward parsing algorithm:
|
|
219
|
+
|
|
220
|
+
1. Determine the field name from the **last** token and classify into Normal, Contact, or Credential category
|
|
221
|
+
2. Handle `webauthn` credential token: if present, consume it and re-determine the category from the preceding token
|
|
222
|
+
3. Optionally consume a contacting token (only valid for Contact category fields)
|
|
223
|
+
4. Reject contacting tokens preceding Normal category fields
|
|
224
|
+
5. Optionally consume `shipping`/`billing`
|
|
225
|
+
6. Optionally consume `section-*` named group
|
|
226
|
+
7. Report any remaining tokens as extra
|
|
227
|
+
|
|
228
|
+
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.
|
|
229
|
+
|
|
230
|
+
### Link Type Validator
|
|
231
|
+
|
|
232
|
+
**Source:** `src/whatwg/check-link-type.ts`
|
|
233
|
+
|
|
234
|
+
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.
|
|
235
|
+
|
|
236
|
+
The validator is parameterized by element context:
|
|
237
|
+
|
|
238
|
+
| Option | Context | Description |
|
|
239
|
+
| ----------------- | -------------------- | ------------------------------------------ |
|
|
240
|
+
| `el: 'link'` | `<link>` in `<head>` | All link types allowed on the link element |
|
|
241
|
+
| `el: 'body link'` | `<link>` in `<body>` | Only link types with `body-ok` flag |
|
|
242
|
+
| `el: 'a, area'` | `<a>`, `<area>` | Link types allowed on anchor/area elements |
|
|
243
|
+
| `el: 'form'` | `<form>` | Link types allowed on form elements |
|
|
244
|
+
|
|
245
|
+
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.
|
|
246
|
+
|
|
247
|
+
This is registered in `defs.ts` as four separate type definitions: `LinkTypeForLinkElement`, `LinkTypeForLinkElementInBody`, `LinkTypeForAnchorAndAreaElement`, and `LinkTypeForFormElement`.
|
|
248
|
+
|
|
249
|
+
### MIME Type Validator
|
|
250
|
+
|
|
251
|
+
**Source:** `src/whatwg/check-mime-type.ts`
|
|
252
|
+
|
|
253
|
+
Validates MIME type strings according to the [WHATWG MIME Sniffing specification](https://mimesniff.spec.whatwg.org/#valid-mime-type).
|
|
254
|
+
|
|
255
|
+
The validator integrates the `whatwg-mimetype` npm package for parsing. It:
|
|
256
|
+
|
|
257
|
+
1. Attempts to parse the value using `MIMEType.parse()`
|
|
258
|
+
2. If parsing succeeds, checks whether the serialized essence matches the input
|
|
259
|
+
3. Optionally restricts to MIME types with no parameters (via the `withoutParameters` option)
|
|
260
|
+
4. Reports extra tokens or syntax errors with appropriate candidates
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// Invocation in defs.ts
|
|
264
|
+
MIMEType: {
|
|
265
|
+
ref: 'https://mimesniff.spec.whatwg.org/#valid-mime-type',
|
|
266
|
+
is: checkMIMEType(),
|
|
267
|
+
},
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### URL and Name Validators
|
|
271
|
+
|
|
272
|
+
These validators implement simple predicate-based checks for various WHATWG-defined name formats.
|
|
273
|
+
|
|
274
|
+
| Function | File | Validates | Logic |
|
|
275
|
+
| ----------------------- | ------------------------------------ | ---------------------------------- | ----------------------------------------------------------------------------------- |
|
|
276
|
+
| `isAbsURL` | `whatwg/is-abs-url.ts` | Absolute URL | Uses `new URL(value)` constructor; returns `false` on `ERR_INVALID_URL` |
|
|
277
|
+
| `isBrowserContextName` | `whatwg/is-browser-context-name.ts` | Browsing context name (deprecated) | Non-empty and does not start with `_` |
|
|
278
|
+
| `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 |
|
|
279
|
+
| `isItempropName` | `whatwg/is-itemprop-name.ts` | Itemprop property name | Must not contain `:`, `.`, or space |
|
|
280
|
+
| `isNavigableTargetName` | `whatwg/is-navigable-target-name.ts` | Navigable target name | Non-empty, no ASCII tab/newline, does not start with `_` |
|
|
281
|
+
|
|
282
|
+
**`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.
|
|
283
|
+
|
|
284
|
+
**`isBrowserContextName`** is deprecated in favor of `isNavigableTargetName`.
|
|
285
|
+
|
|
286
|
+
All of these are `FormattedPrimitiveTypeCreator` factories -- they return `() => (value: string) => boolean`.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## RFC Validators
|
|
291
|
+
|
|
292
|
+
### BCP 47 (Language Tags)
|
|
293
|
+
|
|
294
|
+
**Source:** `src/rfc/is-bcp-47.ts`
|
|
295
|
+
|
|
296
|
+
Validates language tags according to [BCP 47](https://tools.ietf.org/rfc/bcp/bcp47.html) (RFC 5646 + RFC 4647).
|
|
297
|
+
|
|
298
|
+
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:
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
// src/rfc/is-bcp-47.ts
|
|
302
|
+
export const isBCP47: FormattedPrimitiveTypeCreator = () => {
|
|
303
|
+
return value => {
|
|
304
|
+
const { language, privateuse } = parse(value);
|
|
305
|
+
return !!language || (privateuse != null && privateuse.length > 0);
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
This is registered in `defs.ts` as the `BCP47` type, used for the `lang` and `hreflang` attributes on HTML elements.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## W3C Validators
|
|
315
|
+
|
|
316
|
+
### Serialized Permissions Policy
|
|
317
|
+
|
|
318
|
+
**Source:** `src/w3c/check-serialized-permissions-policy.ts`
|
|
319
|
+
|
|
320
|
+
Validates serialized permissions policy strings according to the [W3C Permissions Policy specification](https://w3c.github.io/webappsec-permissions-policy/#serialized-permissions-policy).
|
|
321
|
+
|
|
322
|
+
The validator parses the ABNF grammar:
|
|
323
|
+
|
|
324
|
+
```abnf
|
|
325
|
+
serialized-permissions-policy = serialized-policy-directive *(";" serialized-policy-directive)
|
|
326
|
+
serialized-policy-directive = feature-identifier [RWS allow-list]
|
|
327
|
+
feature-identifier = 1*( ALPHA / DIGIT / "-")
|
|
328
|
+
allow-list = allow-list-value *(RWS allow-list-value)
|
|
329
|
+
allow-list-value = serialized-origin / "*" / "'self'" / "'src'" / "'none'"
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
> **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.
|
|
333
|
+
|
|
334
|
+
Key validation steps:
|
|
335
|
+
|
|
336
|
+
1. Splits the value on `;` into policy directives
|
|
337
|
+
2. For each directive, extracts the feature identifier and validates it matches `/^[\da-z-]+$/i`
|
|
338
|
+
3. For each allow-list value, checks against the keywords `*`, `'self'`, `'src'`, `'none'`, or validates as a serialized origin
|
|
339
|
+
4. Serialized origins are validated using the `URL` constructor, rejecting pathnames, queries, hashes, usernames, passwords, and characters that must be percent-encoded (`'`, `*`, `,`, `;`)
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Adding New Validators
|
|
344
|
+
|
|
345
|
+
Follow these steps to add a new validator to the system.
|
|
346
|
+
|
|
347
|
+
### Step 1: Create the Validator File
|
|
348
|
+
|
|
349
|
+
Place the file in the appropriate directory based on the specification:
|
|
350
|
+
|
|
351
|
+
- `src/primitive/` for basic format checks
|
|
352
|
+
- `src/whatwg/` for WHATWG HTML spec validators
|
|
353
|
+
- `src/rfc/` for RFC-defined formats
|
|
354
|
+
- `src/w3c/` for W3C specification formats
|
|
355
|
+
|
|
356
|
+
**Template for a `FormattedPrimitiveTypeCreator`:**
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
// src/whatwg/is-my-format.ts
|
|
360
|
+
import type { FormattedPrimitiveTypeCreator } from '../types.js';
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Checks whether a string is a valid my-format value.
|
|
364
|
+
*
|
|
365
|
+
* @see https://spec.example.org/my-format
|
|
366
|
+
*/
|
|
367
|
+
export const isMyFormat: FormattedPrimitiveTypeCreator = () => {
|
|
368
|
+
return value => {
|
|
369
|
+
// Validation logic returning boolean
|
|
370
|
+
return /^[a-z]+-[a-z]+$/.test(value);
|
|
371
|
+
};
|
|
372
|
+
};
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Template for a `CustomSyntaxChecker`:**
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
// src/whatwg/check-my-syntax.ts
|
|
379
|
+
import type { CustomSyntaxChecker } from '../types.js';
|
|
380
|
+
|
|
381
|
+
import { matched, unmatched } from '../match-result.js';
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Validates a my-syntax value.
|
|
385
|
+
*
|
|
386
|
+
* @see https://spec.example.org/my-syntax
|
|
387
|
+
*/
|
|
388
|
+
export const checkMySyntax: CustomSyntaxChecker = () => value => {
|
|
389
|
+
if (!value) {
|
|
390
|
+
return unmatched(value, 'empty-token');
|
|
391
|
+
}
|
|
392
|
+
// Validation logic returning Result
|
|
393
|
+
return matched();
|
|
394
|
+
};
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Step 2: Export from the Directory Index
|
|
398
|
+
|
|
399
|
+
If you placed the validator in a directory with an `index.ts`, add the export:
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
// src/primitive/index.ts
|
|
403
|
+
export { isMyFormat } from './is-my-format.js';
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Step 3: Register in defs.ts
|
|
407
|
+
|
|
408
|
+
Import the validator and add it to the `defs` object:
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
// src/defs.ts
|
|
412
|
+
import { isMyFormat } from './whatwg/is-my-format.js';
|
|
413
|
+
// or
|
|
414
|
+
import { checkMySyntax } from './whatwg/check-my-syntax.js';
|
|
415
|
+
|
|
416
|
+
export const defs: Defs = {
|
|
417
|
+
// ... existing definitions ...
|
|
418
|
+
|
|
419
|
+
// For FormattedPrimitiveTypeCreator (wrap with matches()):
|
|
420
|
+
MyFormat: {
|
|
421
|
+
ref: 'https://spec.example.org/my-format',
|
|
422
|
+
expects: [
|
|
423
|
+
{
|
|
424
|
+
type: 'format',
|
|
425
|
+
value: 'my format',
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
is: matches(isMyFormat()),
|
|
429
|
+
},
|
|
430
|
+
|
|
431
|
+
// For CustomSyntaxChecker (use directly):
|
|
432
|
+
MySyntax: {
|
|
433
|
+
ref: 'https://spec.example.org/my-syntax',
|
|
434
|
+
expects: [
|
|
435
|
+
{
|
|
436
|
+
type: 'format',
|
|
437
|
+
value: 'my syntax',
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
is: checkMySyntax(),
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Step 4: Add Tests
|
|
446
|
+
|
|
447
|
+
Create a test file alongside the validator:
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
// src/whatwg/is-my-format.spec.ts
|
|
451
|
+
import { isMyFormat } from './is-my-format.js';
|
|
452
|
+
|
|
453
|
+
const check = isMyFormat();
|
|
454
|
+
|
|
455
|
+
test('valid values', () => {
|
|
456
|
+
expect(check('foo-bar')).toBe(true);
|
|
457
|
+
expect(check('hello-world')).toBe(true);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test('invalid values', () => {
|
|
461
|
+
expect(check('')).toBe(false);
|
|
462
|
+
expect(check('UPPER-CASE')).toBe(false);
|
|
463
|
+
});
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Concrete Example: Adding a Hypothetical `isDataURL` Validator
|
|
467
|
+
|
|
468
|
+
Suppose you need to validate `data:` URLs specifically.
|
|
469
|
+
|
|
470
|
+
**1. Create the file:**
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
// src/whatwg/is-data-url.ts
|
|
474
|
+
import type { FormattedPrimitiveTypeCreator } from '../types.js';
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Checks whether a string is a valid data URL.
|
|
478
|
+
*
|
|
479
|
+
* @see https://fetch.spec.whatwg.org/#data-urls
|
|
480
|
+
*/
|
|
481
|
+
export const isDataURL: FormattedPrimitiveTypeCreator = () => {
|
|
482
|
+
return value => {
|
|
483
|
+
return /^data:[^,]*,/.test(value);
|
|
484
|
+
};
|
|
485
|
+
};
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**2. Register in `defs.ts`:**
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
import { isDataURL } from './whatwg/is-data-url.js';
|
|
492
|
+
|
|
493
|
+
// Inside the defs object:
|
|
494
|
+
DataURL: {
|
|
495
|
+
ref: 'https://fetch.spec.whatwg.org/#data-urls',
|
|
496
|
+
expects: [
|
|
497
|
+
{
|
|
498
|
+
type: 'format',
|
|
499
|
+
value: 'data URL',
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
is: matches(isDataURL()),
|
|
503
|
+
},
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
**3. Add tests:**
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
// src/whatwg/is-data-url.spec.ts
|
|
510
|
+
import { isDataURL } from './is-data-url.js';
|
|
511
|
+
|
|
512
|
+
const check = isDataURL();
|
|
513
|
+
|
|
514
|
+
test('valid data URLs', () => {
|
|
515
|
+
expect(check('data:text/plain,Hello')).toBe(true);
|
|
516
|
+
expect(check('data:text/html,<h1>Hello</h1>')).toBe(true);
|
|
517
|
+
expect(check('data:image/png;base64,abc123...')).toBe(true);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
test('invalid data URLs', () => {
|
|
521
|
+
expect(check('https://example.com')).toBe(false);
|
|
522
|
+
expect(check('data:')).toBe(false);
|
|
523
|
+
});
|
|
524
|
+
```
|
package/lib/check-base.d.ts
CHANGED
|
@@ -1,10 +1,90 @@
|
|
|
1
|
-
import type { Type, Result, List, CustomSyntax, CustomCssSyntax, Enum, KeywordDefinedType, Number, Directive, Defs } from './types.js';
|
|
1
|
+
import type { Type, Result, List, CustomSyntax, CustomCssSyntax, Enum, KeywordDefinedType, Number, Directive, Pattern, Defs } from './types.js';
|
|
2
2
|
import type { ReadonlyDeep } from 'type-fest';
|
|
3
|
+
/**
|
|
4
|
+
* Validates a string value against a specified type definition using the provided
|
|
5
|
+
* set of custom type definitions. This is the core type-checking dispatcher that
|
|
6
|
+
* routes validation to the appropriate checker based on the type's structure
|
|
7
|
+
* (keyword, list, enum, number, or directive).
|
|
8
|
+
*
|
|
9
|
+
* @param value - The string value to validate
|
|
10
|
+
* @param type - The type definition to validate against
|
|
11
|
+
* @param defs - A map of custom type definitions (both CSS syntax and custom syntax) used for resolving keyword types
|
|
12
|
+
* @param ref - An optional reference identifier used for error reporting context
|
|
13
|
+
* @param cache - Whether to use cached results for repeated checks with the same inputs
|
|
14
|
+
* @returns A result indicating whether the value matches the type, or details about the mismatch
|
|
15
|
+
* @throws Error if the type does not match any known type structure
|
|
16
|
+
*/
|
|
3
17
|
export declare function checkBase(value: string, type: ReadonlyDeep<Type>, defs: Defs, ref?: string, cache?: boolean): Result;
|
|
18
|
+
/**
|
|
19
|
+
* Determines whether a type definition is a keyword-based type.
|
|
20
|
+
* Keyword types are represented as plain strings (e.g., CSS syntax names
|
|
21
|
+
* like `"<color>"` or extended types like `"URL"`).
|
|
22
|
+
*
|
|
23
|
+
* @param type - The type definition to test
|
|
24
|
+
* @returns True if the type is a keyword-defined type string
|
|
25
|
+
*/
|
|
4
26
|
export declare function isKeyword(type: ReadonlyDeep<Type>): type is ReadonlyDeep<KeywordDefinedType>;
|
|
27
|
+
/**
|
|
28
|
+
* Determines whether a type definition represents a list type.
|
|
29
|
+
* List types define space-separated or comma-separated token sequences
|
|
30
|
+
* and are identified by having a `separator` property.
|
|
31
|
+
*
|
|
32
|
+
* @param type - The type definition to test
|
|
33
|
+
* @returns True if the type is a list definition
|
|
34
|
+
*/
|
|
5
35
|
export declare function isList(type: ReadonlyDeep<Type>): type is ReadonlyDeep<List>;
|
|
36
|
+
/**
|
|
37
|
+
* Determines whether a type definition represents an enumerated type.
|
|
38
|
+
* Enum types define a fixed set of allowed string values and are
|
|
39
|
+
* identified by having an `enum` property.
|
|
40
|
+
*
|
|
41
|
+
* @param type - The type definition to test
|
|
42
|
+
* @returns True if the type is an enum definition
|
|
43
|
+
*/
|
|
6
44
|
export declare function isEnum(type: ReadonlyDeep<Type>): type is ReadonlyDeep<Enum>;
|
|
45
|
+
/**
|
|
46
|
+
* Determines whether a type definition represents a numeric type.
|
|
47
|
+
* Number types specify either `float` or `integer` validation with
|
|
48
|
+
* optional range constraints, and are identified by having a `type`
|
|
49
|
+
* property set to one of those values.
|
|
50
|
+
*
|
|
51
|
+
* @param type - The type definition to test
|
|
52
|
+
* @returns True if the type is a number definition
|
|
53
|
+
*/
|
|
7
54
|
export declare function isNumber(type: ReadonlyDeep<Type>): type is ReadonlyDeep<Number>;
|
|
55
|
+
/**
|
|
56
|
+
* Determines whether a type definition represents a directive type.
|
|
57
|
+
* Directive types allow separating and individually validating parts of
|
|
58
|
+
* an attribute value, and are identified by having a `directive` property.
|
|
59
|
+
*
|
|
60
|
+
* @param type - The type definition to test
|
|
61
|
+
* @returns True if the type is a directive definition
|
|
62
|
+
*/
|
|
8
63
|
export declare function isDirective(type: ReadonlyDeep<Type>): type is ReadonlyDeep<Directive>;
|
|
64
|
+
/**
|
|
65
|
+
* Determines whether a type definition represents a pattern type.
|
|
66
|
+
* Pattern types validate attribute values against a regular expression
|
|
67
|
+
* or plain string, and are identified by having a `pattern` property.
|
|
68
|
+
*
|
|
69
|
+
* @param type - The type definition to test
|
|
70
|
+
* @returns True if the type is a pattern definition
|
|
71
|
+
*/
|
|
72
|
+
export declare function isPattern(type: ReadonlyDeep<Type>): type is ReadonlyDeep<Pattern>;
|
|
73
|
+
/**
|
|
74
|
+
* Determines whether a custom type definition uses CSS syntax matching.
|
|
75
|
+
* CSS syntax definitions are either plain strings or objects with a `syntax`
|
|
76
|
+
* property that specifies CSS value definition syntax for matching.
|
|
77
|
+
*
|
|
78
|
+
* @param type - The custom type definition to test
|
|
79
|
+
* @returns True if the definition uses CSS syntax matching
|
|
80
|
+
*/
|
|
9
81
|
export declare function isCSSSyntax(type: CustomSyntax | CustomCssSyntax): type is CustomCssSyntax;
|
|
82
|
+
/**
|
|
83
|
+
* Determines whether a custom type definition uses a programmatic custom
|
|
84
|
+
* syntax checker rather than CSS syntax matching. This is the inverse
|
|
85
|
+
* of {@link isCSSSyntax}.
|
|
86
|
+
*
|
|
87
|
+
* @param type - The custom type definition to test
|
|
88
|
+
* @returns True if the definition uses a custom programmatic checker function
|
|
89
|
+
*/
|
|
10
90
|
export declare function isCustomSyntax(type: CustomSyntax | CustomCssSyntax): type is CustomSyntax;
|