@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.
- package/CHANGELOG.md +38 -0
- package/README.md +83 -75
- package/lib/check-multi-types.d.ts +2 -9
- package/lib/check-multi-types.js +2 -9
- package/lib/css-defs.d.ts +0 -8
- package/lib/css-defs.js +0 -8
- package/lib/css-overrides.d.ts +2 -5
- package/lib/css-overrides.js +2 -5
- package/lib/css-syntax.d.ts +0 -11
- package/lib/css-syntax.js +6 -18
- package/lib/css-tokenizers.d.ts +0 -6
- package/lib/css-tokenizers.js +0 -6
- package/lib/debug.d.ts +0 -3
- package/lib/debug.js +0 -3
- package/lib/defs.d.ts +0 -8
- package/lib/defs.js +308 -37
- package/lib/directive.d.ts +0 -14
- package/lib/directive.js +0 -14
- package/lib/enum.d.ts +0 -11
- package/lib/enum.js +0 -11
- package/lib/keyword-type.d.ts +0 -13
- package/lib/keyword-type.js +0 -13
- package/lib/list.d.ts +0 -13
- package/lib/list.js +0 -13
- package/lib/match-result.d.ts +0 -21
- package/lib/match-result.js +0 -21
- package/lib/number.d.ts +0 -12
- package/lib/number.js +0 -12
- package/lib/primitive/is-float.d.ts +3 -4
- package/lib/primitive/is-float.js +3 -4
- package/lib/primitive/is-int.d.ts +0 -5
- package/lib/primitive/is-int.js +0 -5
- package/lib/primitive/is-non-zero-uint.d.ts +0 -6
- package/lib/primitive/is-non-zero-uint.js +0 -6
- package/lib/primitive/is-quantity.d.ts +0 -8
- package/lib/primitive/is-quantity.js +0 -8
- package/lib/primitive/is-uint.d.ts +0 -6
- package/lib/primitive/is-uint.js +0 -6
- package/lib/primitive/range.d.ts +0 -8
- package/lib/primitive/range.js +0 -8
- package/lib/primitive/split-unit.d.ts +0 -6
- package/lib/primitive/split-unit.js +0 -6
- package/lib/rfc/is-bcp-47.d.ts +24 -1
- package/lib/rfc/is-bcp-47.js +88 -3
- package/lib/token/token-collection.d.ts +1 -111
- package/lib/token/token-collection.js +0 -108
- package/lib/token/token.d.ts +1 -73
- package/lib/token/token.js +0 -72
- package/lib/token/types.d.ts +0 -9
- package/lib/types.schema.d.ts +1 -1
- package/lib/w3c/check-content-security-policy.d.ts +18 -0
- package/lib/w3c/check-content-security-policy.js +363 -0
- package/lib/w3c/check-serialized-permissions-policy.d.ts +0 -2
- package/lib/w3c/check-serialized-permissions-policy.js +0 -2
- package/lib/whatwg/check-autocomplete.d.ts +26 -9
- package/lib/whatwg/check-autocomplete.js +42 -15
- package/lib/whatwg/check-datetime/date-string-with-optional-time.d.ts +11 -0
- package/lib/whatwg/check-datetime/date-string-with-optional-time.js +16 -0
- package/lib/whatwg/check-datetime/date-string.d.ts +0 -2
- package/lib/whatwg/check-datetime/date-string.js +0 -2
- package/lib/whatwg/check-datetime/datetime-tokens.d.ts +0 -13
- package/lib/whatwg/check-datetime/datetime-tokens.js +0 -16
- package/lib/whatwg/check-datetime/duration-string.d.ts +0 -4
- package/lib/whatwg/check-datetime/duration-string.js +0 -4
- package/lib/whatwg/check-datetime/global-date-and-time-string.d.ts +0 -2
- package/lib/whatwg/check-datetime/global-date-and-time-string.js +4 -4
- package/lib/whatwg/check-datetime/index.d.ts +0 -5
- package/lib/whatwg/check-datetime/index.js +0 -5
- package/lib/whatwg/check-datetime/local-date-and-time-string.d.ts +0 -4
- package/lib/whatwg/check-datetime/local-date-and-time-string.js +0 -4
- package/lib/whatwg/check-datetime/month-string.d.ts +0 -2
- package/lib/whatwg/check-datetime/month-string.js +0 -2
- package/lib/whatwg/check-datetime/time-string.d.ts +0 -2
- package/lib/whatwg/check-datetime/time-string.js +0 -2
- package/lib/whatwg/check-datetime/time-zone-offset-string.d.ts +0 -8
- package/lib/whatwg/check-datetime/time-zone-offset-string.js +0 -8
- package/lib/whatwg/check-datetime/week-string.d.ts +0 -2
- package/lib/whatwg/check-datetime/week-string.js +0 -2
- package/lib/whatwg/check-datetime/year-string.d.ts +0 -2
- package/lib/whatwg/check-datetime/year-string.js +0 -2
- package/lib/whatwg/check-datetime/yearless-date-string.d.ts +0 -2
- package/lib/whatwg/check-datetime/yearless-date-string.js +0 -2
- package/lib/whatwg/check-email.js +0 -2
- package/lib/whatwg/check-http-equiv-content-type.d.ts +12 -0
- package/lib/whatwg/check-http-equiv-content-type.js +32 -0
- package/lib/whatwg/check-http-equiv-refresh.d.ts +21 -0
- package/lib/whatwg/check-http-equiv-refresh.js +106 -0
- package/lib/whatwg/check-media-query-list.d.ts +61 -0
- package/lib/whatwg/check-media-query-list.js +484 -0
- package/lib/whatwg/check-mime-type.d.ts +0 -4
- package/lib/whatwg/check-mime-type.js +55 -3
- package/lib/whatwg/check-simple-color.d.ts +2 -4
- package/lib/whatwg/check-simple-color.js +2 -4
- package/lib/whatwg/check-url.d.ts +15 -7
- package/lib/whatwg/check-url.js +333 -25
- package/lib/whatwg/is-abs-url.d.ts +0 -2
- package/lib/whatwg/is-abs-url.js +0 -2
- package/lib/whatwg/is-browser-context-name.d.ts +0 -2
- package/lib/whatwg/is-browser-context-name.js +0 -2
- package/lib/whatwg/is-itemprop-name.d.ts +0 -2
- package/lib/whatwg/is-itemprop-name.js +0 -2
- package/lib/whatwg/is-navigable-target-name.d.ts +0 -2
- package/lib/whatwg/is-navigable-target-name.js +0 -2
- package/package.json +6 -5
- package/types.schema.json +9 -0
- package/ARCHITECTURE.ja.md +0 -256
- package/ARCHITECTURE.md +0 -256
- package/SKILL.md +0 -100
- package/docs/check-pipeline.ja.md +0 -494
- package/docs/check-pipeline.md +0 -494
- package/docs/token-system.ja.md +0 -584
- package/docs/token-system.md +0 -584
- package/docs/type-system.ja.md +0 -623
- package/docs/type-system.md +0 -623
- package/docs/validators.ja.md +0 -536
- package/docs/validators.md +0 -536
package/lib/match-result.js
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates a value checker function that wraps a primitive type check
|
|
3
|
-
* and returns a matched or unmatched result.
|
|
4
|
-
*
|
|
5
|
-
* @param checker - The primitive type check function to wrap
|
|
6
|
-
* @param options - Optional settings for the unmatched result including ref and reason
|
|
7
|
-
* @returns A function that takes a string value and returns a match result
|
|
8
|
-
*/
|
|
9
1
|
export function matches(checker, options) {
|
|
10
2
|
return (value) => {
|
|
11
3
|
const valid = checker(value);
|
|
@@ -15,24 +7,11 @@ export function matches(checker, options) {
|
|
|
15
7
|
return valid ? matched() : unmatched(value, options?.reason, options);
|
|
16
8
|
};
|
|
17
9
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Creates a successful match result.
|
|
20
|
-
*
|
|
21
|
-
* @returns A matched result object
|
|
22
|
-
*/
|
|
23
10
|
export function matched() {
|
|
24
11
|
return {
|
|
25
12
|
matched: true,
|
|
26
13
|
};
|
|
27
14
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Creates a failed match result with location and reason information.
|
|
30
|
-
*
|
|
31
|
-
* @param value - The raw string value that failed to match
|
|
32
|
-
* @param reason - The reason for the mismatch
|
|
33
|
-
* @param options - Optional settings including ref URL and expected values
|
|
34
|
-
* @returns An unmatched result object with position details
|
|
35
|
-
*/
|
|
36
15
|
export function unmatched(value, reason, options) {
|
|
37
16
|
return {
|
|
38
17
|
...options,
|
package/lib/number.d.ts
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
1
|
import type { Result } from './types.js';
|
|
2
2
|
import type { Number as TypeNumber } from './types.schema.js';
|
|
3
|
-
/**
|
|
4
|
-
* Checks whether a value matches a number type definition.
|
|
5
|
-
*
|
|
6
|
-
* Validates the value as either an integer or float, then checks range
|
|
7
|
-
* constraints (gt, gte, lt, lte) if defined. Supports clampable values
|
|
8
|
-
* that suggest the nearest valid boundary value as a candidate.
|
|
9
|
-
*
|
|
10
|
-
* @param value - The string value to check
|
|
11
|
-
* @param type - The number type definition with format and range constraints
|
|
12
|
-
* @param ref - Optional reference URL for the unmatched result
|
|
13
|
-
* @returns The validation result
|
|
14
|
-
*/
|
|
15
3
|
export declare function checkNumber(value: string, type: Readonly<TypeNumber>, ref?: string): Result;
|
package/lib/number.js
CHANGED
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
import { matched, unmatched } from './match-result.js';
|
|
2
2
|
import { isFloat, isInt } from './primitive/index.js';
|
|
3
|
-
/**
|
|
4
|
-
* Checks whether a value matches a number type definition.
|
|
5
|
-
*
|
|
6
|
-
* Validates the value as either an integer or float, then checks range
|
|
7
|
-
* constraints (gt, gte, lt, lte) if defined. Supports clampable values
|
|
8
|
-
* that suggest the nearest valid boundary value as a candidate.
|
|
9
|
-
*
|
|
10
|
-
* @param value - The string value to check
|
|
11
|
-
* @param type - The number type definition with format and range constraints
|
|
12
|
-
* @param ref - Optional reference URL for the unmatched result
|
|
13
|
-
* @returns The validation result
|
|
14
|
-
*/
|
|
15
3
|
export function checkNumber(value, type, ref) {
|
|
16
4
|
if (!value) {
|
|
17
5
|
return unmatched(value, 'empty-token');
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Intentionally looser than the strict WHATWG grammar: `Number.parseFloat()`
|
|
3
|
+
* accepts leading-dot values (`.5`) and ignores trailing non-numeric
|
|
4
|
+
* characters (`1.5abc`), both of which the spec production rejects.
|
|
3
5
|
*
|
|
4
6
|
* @see https://html.spec.whatwg.org/dev/common-microsyntaxes.html#floating-point-numbers
|
|
5
|
-
*
|
|
6
|
-
* @param value - The string to validate
|
|
7
|
-
* @returns Whether the value is a valid floating-point number
|
|
8
7
|
*/
|
|
9
8
|
export declare function isFloat(value: string): boolean;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Intentionally looser than the strict WHATWG grammar: `Number.parseFloat()`
|
|
3
|
+
* accepts leading-dot values (`.5`) and ignores trailing non-numeric
|
|
4
|
+
* characters (`1.5abc`), both of which the spec production rejects.
|
|
3
5
|
*
|
|
4
6
|
* @see https://html.spec.whatwg.org/dev/common-microsyntaxes.html#floating-point-numbers
|
|
5
|
-
*
|
|
6
|
-
* @param value - The string to validate
|
|
7
|
-
* @returns Whether the value is a valid floating-point number
|
|
8
7
|
*/
|
|
9
8
|
export function isFloat(value) {
|
|
10
9
|
return value === value.trim() && Number.isFinite(Number.parseFloat(value));
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Checks whether a string is a valid signed integer.
|
|
3
|
-
*
|
|
4
2
|
* @see https://html.spec.whatwg.org/dev/common-microsyntaxes.html#signed-integers
|
|
5
|
-
*
|
|
6
|
-
* @param value - The string to validate
|
|
7
|
-
* @returns Whether the value is a valid signed integer
|
|
8
3
|
*/
|
|
9
4
|
export declare function isInt(value: string): boolean;
|
package/lib/primitive/is-int.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Checks whether a string is a valid signed integer.
|
|
3
|
-
*
|
|
4
2
|
* @see https://html.spec.whatwg.org/dev/common-microsyntaxes.html#signed-integers
|
|
5
|
-
*
|
|
6
|
-
* @param value - The string to validate
|
|
7
|
-
* @returns Whether the value is a valid signed integer
|
|
8
3
|
*/
|
|
9
4
|
export function isInt(value) {
|
|
10
5
|
return /^-?\d+$/.test(value);
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checks whether a string is a valid non-negative integer greater than zero.
|
|
3
|
-
*
|
|
4
|
-
* @param value - The string to validate
|
|
5
|
-
* @returns Whether the value is a valid non-zero unsigned integer
|
|
6
|
-
*/
|
|
7
1
|
export function isNonZeroUint(value) {
|
|
8
2
|
return /^\d+$/.test(value) && !/^0+$/.test(value);
|
|
9
3
|
}
|
|
@@ -1,9 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checks whether a string is a valid number with one of the allowed unit suffixes.
|
|
3
|
-
*
|
|
4
|
-
* @param value - The string to validate (e.g., "10px", "1.5em")
|
|
5
|
-
* @param units - The allowed unit suffixes (e.g., `["px", "em", "rem"]`)
|
|
6
|
-
* @param numberType - The number format constraint: `"int"`, `"uint"`, or `"float"` (defaults to `"float"`)
|
|
7
|
-
* @returns Whether the value is a valid quantity with an allowed unit
|
|
8
|
-
*/
|
|
9
1
|
export declare function isQuantity(value: string, units: readonly string[], numberType?: 'int' | 'uint' | 'float'): boolean;
|
|
@@ -2,14 +2,6 @@ import { isFloat } from './is-float.js';
|
|
|
2
2
|
import { isInt } from './is-int.js';
|
|
3
3
|
import { isUint } from './is-uint.js';
|
|
4
4
|
import { splitUnit } from './split-unit.js';
|
|
5
|
-
/**
|
|
6
|
-
* Checks whether a string is a valid number with one of the allowed unit suffixes.
|
|
7
|
-
*
|
|
8
|
-
* @param value - The string to validate (e.g., "10px", "1.5em")
|
|
9
|
-
* @param units - The allowed unit suffixes (e.g., `["px", "em", "rem"]`)
|
|
10
|
-
* @param numberType - The number format constraint: `"int"`, `"uint"`, or `"float"` (defaults to `"float"`)
|
|
11
|
-
* @returns Whether the value is a valid quantity with an allowed unit
|
|
12
|
-
*/
|
|
13
5
|
export function isQuantity(value, units, numberType = 'float') {
|
|
14
6
|
const { num, unit } = splitUnit(value);
|
|
15
7
|
if (!units.includes(unit.toLowerCase())) {
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Checks whether a string is a valid non-negative integer.
|
|
3
|
-
*
|
|
4
2
|
* @see https://html.spec.whatwg.org/dev/common-microsyntaxes.html#non-negative-integers
|
|
5
|
-
*
|
|
6
|
-
* @param value - The string to validate
|
|
7
|
-
* @param options - Optional constraints; use `gt` to require a value greater than the specified number
|
|
8
|
-
* @returns Whether the value is a valid non-negative integer within the constraints
|
|
9
3
|
*/
|
|
10
4
|
export declare function isUint(value: string, options?: {
|
|
11
5
|
readonly gt?: number;
|
package/lib/primitive/is-uint.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Checks whether a string is a valid non-negative integer.
|
|
3
|
-
*
|
|
4
2
|
* @see https://html.spec.whatwg.org/dev/common-microsyntaxes.html#non-negative-integers
|
|
5
|
-
*
|
|
6
|
-
* @param value - The string to validate
|
|
7
|
-
* @param options - Optional constraints; use `gt` to require a value greater than the specified number
|
|
8
|
-
* @returns Whether the value is a valid non-negative integer within the constraints
|
|
9
3
|
*/
|
|
10
4
|
export function isUint(value, options) {
|
|
11
5
|
const matched = /^\d+$/.test(value);
|
package/lib/primitive/range.d.ts
CHANGED
|
@@ -1,9 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checks whether a numeric string value falls within an inclusive range.
|
|
3
|
-
*
|
|
4
|
-
* @param value - The string to parse as a number and validate
|
|
5
|
-
* @param from - The minimum allowed value (inclusive)
|
|
6
|
-
* @param to - The maximum allowed value (inclusive)
|
|
7
|
-
* @returns Whether the parsed number is within the range `[from, to]`
|
|
8
|
-
*/
|
|
9
1
|
export declare function range(value: string, from: number, to: number): boolean;
|
package/lib/primitive/range.js
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checks whether a numeric string value falls within an inclusive range.
|
|
3
|
-
*
|
|
4
|
-
* @param value - The string to parse as a number and validate
|
|
5
|
-
* @param from - The minimum allowed value (inclusive)
|
|
6
|
-
* @param to - The maximum allowed value (inclusive)
|
|
7
|
-
* @returns Whether the parsed number is within the range `[from, to]`
|
|
8
|
-
*/
|
|
9
1
|
export function range(value, from, to) {
|
|
10
2
|
const num = Number.parseFloat(value);
|
|
11
3
|
if (Number.isNaN(num)) {
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Splits a value string into its numeric and unit parts.
|
|
3
|
-
*
|
|
4
|
-
* @param value - The string to split (e.g., "10px", "1.5em")
|
|
5
|
-
* @returns An object with `num` (the numeric part) and `unit` (the unit suffix, or empty string)
|
|
6
|
-
*/
|
|
7
1
|
export declare function splitUnit(value: string): {
|
|
8
2
|
num: string;
|
|
9
3
|
unit: string;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Splits a value string into its numeric and unit parts.
|
|
3
|
-
*
|
|
4
|
-
* @param value - The string to split (e.g., "10px", "1.5em")
|
|
5
|
-
* @returns An object with `num` (the numeric part) and `unit` (the unit suffix, or empty string)
|
|
6
|
-
*/
|
|
7
1
|
export function splitUnit(value) {
|
|
8
2
|
value = value.trim().toLowerCase();
|
|
9
3
|
const matched = value.match(/(^-?\.\d+|^-?\d+(?:\.\d+(?:e[+-]\d+)?)?)([a-z]+$)/i);
|
package/lib/rfc/is-bcp-47.d.ts
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
import type { FormattedPrimitiveTypeCreator } from '../types.js';
|
|
2
2
|
/**
|
|
3
|
-
* Checks
|
|
3
|
+
* Checks **validity**, not just well-formedness, per RFC 5646 §2.2.9:
|
|
4
|
+
* "Either the tag is in the list of grandfathered tags or all of its
|
|
5
|
+
* primary language, extended language, script, region, and variant subtags
|
|
6
|
+
* appear in the IANA Language Subtag Registry as of the particular registry
|
|
7
|
+
* date", "There are no duplicate variant subtags", and "There are no
|
|
8
|
+
* duplicate singleton (extension) subtags". Extension subtags themselves
|
|
9
|
+
* are not registry-checked — §2.2.9 defines that as validity *for a given
|
|
10
|
+
* extension* (governed by the extension's own RFC), a stricter class than
|
|
11
|
+
* plain validity.
|
|
12
|
+
*
|
|
13
|
+
* Deprecated subtags (e.g. `mo`) remain registered, so they stay valid —
|
|
14
|
+
* §2.2.9 draws no distinction; deprecation is advisory only.
|
|
15
|
+
*
|
|
16
|
+
* The `bcp-47` parser (with its default normalization) resolves the 26
|
|
17
|
+
* grandfathered tags: those with a modern replacement (e.g. `i-klingon` →
|
|
18
|
+
* `tlh`) come back as ordinary subtags that pass the registry test, and
|
|
19
|
+
* those without one (e.g. `i-default`) come back flagged `irregular` /
|
|
20
|
+
* `regular` and are accepted via the grandfathered branch of §2.2.9.
|
|
21
|
+
*
|
|
22
|
+
* Accepts privateuse-only tags (e.g. `x-default`, common in `hreflang`)
|
|
23
|
+
* in addition to ordinary language tags, because BCP 47's `Language-Tag`
|
|
24
|
+
* production is `langtag / privateuse / grandfathered` — a tag with no
|
|
25
|
+
* `language` subtag is still valid when it is entirely private use.
|
|
4
26
|
*
|
|
5
27
|
* @see https://tools.ietf.org/rfc/bcp/bcp47.html
|
|
28
|
+
* @see https://www.rfc-editor.org/rfc/rfc5646.html#section-2.2.9
|
|
6
29
|
*/
|
|
7
30
|
export declare const isBCP47: FormattedPrimitiveTypeCreator;
|
package/lib/rfc/is-bcp-47.js
CHANGED
|
@@ -1,12 +1,97 @@
|
|
|
1
1
|
import { parse } from 'bcp-47';
|
|
2
|
+
import extlangIndex from 'language-subtag-registry/data/json/extlang.json' with { type: 'json' };
|
|
3
|
+
import languageIndex from 'language-subtag-registry/data/json/language.json' with { type: 'json' };
|
|
4
|
+
import regionIndex from 'language-subtag-registry/data/json/region.json' with { type: 'json' };
|
|
5
|
+
import scriptIndex from 'language-subtag-registry/data/json/script.json' with { type: 'json' };
|
|
6
|
+
import variantIndex from 'language-subtag-registry/data/json/variant.json' with { type: 'json' };
|
|
2
7
|
/**
|
|
3
|
-
*
|
|
8
|
+
* Membership test against one subtag type of the IANA Language Subtag
|
|
9
|
+
* Registry (vendored as the `language-subtag-registry` npm package, which
|
|
10
|
+
* tracks the official registry; updating the dependency refreshes the data).
|
|
11
|
+
*
|
|
12
|
+
* The index files key every registered subtag in lowercase. Private-use
|
|
13
|
+
* allocations are recorded as ranged keys (`qaa..qtz` for languages,
|
|
14
|
+
* `qaaa..qabx` for scripts, `qm..qz` / `xa..xz` for regions) and are
|
|
15
|
+
* expanded here by lexicographic comparison — a subtag inside a range is
|
|
16
|
+
* registered per RFC 5646 §3.1.2 ("Private use subtags ... 'qaa..qtz'"
|
|
17
|
+
* range notation).
|
|
18
|
+
*
|
|
19
|
+
* @see https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
|
|
20
|
+
*/
|
|
21
|
+
function createSubtagLookup(index) {
|
|
22
|
+
const ranges = Object.keys(index)
|
|
23
|
+
.filter(key => key.includes('..'))
|
|
24
|
+
.map(key => key.split('..'));
|
|
25
|
+
return subtag => {
|
|
26
|
+
const lowered = subtag.toLowerCase();
|
|
27
|
+
if (Object.hasOwn(index, lowered)) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return ranges.some(([start, end]) => lowered.length === start.length && start <= lowered && lowered <= end);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const isRegisteredLanguage = createSubtagLookup(languageIndex);
|
|
34
|
+
const isRegisteredExtlang = createSubtagLookup(extlangIndex);
|
|
35
|
+
const isRegisteredScript = createSubtagLookup(scriptIndex);
|
|
36
|
+
const isRegisteredRegion = createSubtagLookup(regionIndex);
|
|
37
|
+
const isRegisteredVariant = createSubtagLookup(variantIndex);
|
|
38
|
+
/**
|
|
39
|
+
* Checks **validity**, not just well-formedness, per RFC 5646 §2.2.9:
|
|
40
|
+
* "Either the tag is in the list of grandfathered tags or all of its
|
|
41
|
+
* primary language, extended language, script, region, and variant subtags
|
|
42
|
+
* appear in the IANA Language Subtag Registry as of the particular registry
|
|
43
|
+
* date", "There are no duplicate variant subtags", and "There are no
|
|
44
|
+
* duplicate singleton (extension) subtags". Extension subtags themselves
|
|
45
|
+
* are not registry-checked — §2.2.9 defines that as validity *for a given
|
|
46
|
+
* extension* (governed by the extension's own RFC), a stricter class than
|
|
47
|
+
* plain validity.
|
|
48
|
+
*
|
|
49
|
+
* Deprecated subtags (e.g. `mo`) remain registered, so they stay valid —
|
|
50
|
+
* §2.2.9 draws no distinction; deprecation is advisory only.
|
|
51
|
+
*
|
|
52
|
+
* The `bcp-47` parser (with its default normalization) resolves the 26
|
|
53
|
+
* grandfathered tags: those with a modern replacement (e.g. `i-klingon` →
|
|
54
|
+
* `tlh`) come back as ordinary subtags that pass the registry test, and
|
|
55
|
+
* those without one (e.g. `i-default`) come back flagged `irregular` /
|
|
56
|
+
* `regular` and are accepted via the grandfathered branch of §2.2.9.
|
|
57
|
+
*
|
|
58
|
+
* Accepts privateuse-only tags (e.g. `x-default`, common in `hreflang`)
|
|
59
|
+
* in addition to ordinary language tags, because BCP 47's `Language-Tag`
|
|
60
|
+
* production is `langtag / privateuse / grandfathered` — a tag with no
|
|
61
|
+
* `language` subtag is still valid when it is entirely private use.
|
|
4
62
|
*
|
|
5
63
|
* @see https://tools.ietf.org/rfc/bcp/bcp47.html
|
|
64
|
+
* @see https://www.rfc-editor.org/rfc/rfc5646.html#section-2.2.9
|
|
6
65
|
*/
|
|
7
66
|
export const isBCP47 = () => {
|
|
8
67
|
return value => {
|
|
9
|
-
const { language, privateuse } = parse(value);
|
|
10
|
-
|
|
68
|
+
const { language, extendedLanguageSubtags, script, region, variants, extensions, privateuse, irregular, regular, } = parse(value);
|
|
69
|
+
if (irregular != null || regular != null) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
if (!language) {
|
|
73
|
+
return privateuse.length > 0;
|
|
74
|
+
}
|
|
75
|
+
if (!isRegisteredLanguage(language)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (!extendedLanguageSubtags.every(subtag => isRegisteredExtlang(subtag))) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if (script != null && !isRegisteredScript(script)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (region != null && !isRegisteredRegion(region)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (!variants.every(subtag => isRegisteredVariant(subtag))) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const loweredVariants = variants.map(subtag => subtag.toLowerCase());
|
|
91
|
+
if (new Set(loweredVariants).size !== loweredVariants.length) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const singletons = extensions.map(extension => extension.singleton.toLowerCase());
|
|
95
|
+
return new Set(singletons).size === singletons.length;
|
|
11
96
|
};
|
|
12
97
|
};
|
|
@@ -5,28 +5,11 @@ type TokenCollectionOptions = Partial<Omit<List, 'token'> & {
|
|
|
5
5
|
specificSeparator: string | string[];
|
|
6
6
|
}>;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
* Receives the current head token and remaining tail tokens.
|
|
10
|
-
* Returns a result to stop iteration, or `void` to continue.
|
|
8
|
+
* Returning a result stops iteration; returning `void` continues to the next token.
|
|
11
9
|
*/
|
|
12
10
|
export type TokenEachCheck = (head: Readonly<Token> | null, tail: TokenCollection) => Result | void;
|
|
13
|
-
/**
|
|
14
|
-
* An ordered collection of tokens parsed from a string value.
|
|
15
|
-
*
|
|
16
|
-
* Extends `Array<Token>` with parsing, validation, and query capabilities.
|
|
17
|
-
* Supports configurable separators (space, comma, or custom), uniqueness
|
|
18
|
-
* constraints, ordering checks, and case sensitivity options.
|
|
19
|
-
*/
|
|
20
11
|
export declare class TokenCollection extends Array<Token> {
|
|
21
12
|
#private;
|
|
22
|
-
/**
|
|
23
|
-
* Creates a TokenCollection by matching a value against a sequence of regex patterns.
|
|
24
|
-
*
|
|
25
|
-
* @param value - The source token or string to parse
|
|
26
|
-
* @param patterns - The regex patterns to match sequentially
|
|
27
|
-
* @param typeOptions - Optional collection configuration
|
|
28
|
-
* @returns A new TokenCollection containing the matched tokens
|
|
29
|
-
*/
|
|
30
13
|
static fromPatterns(value: Readonly<Token> | string, patterns: readonly Readonly<RegExp>[], typeOptions?: Omit<TokenCollectionOptions, 'specificSeparator'> & {
|
|
31
14
|
repeat?: boolean;
|
|
32
15
|
}): TokenCollection;
|
|
@@ -40,124 +23,31 @@ export declare class TokenCollection extends Array<Token> {
|
|
|
40
23
|
readonly unique: NonNullable<List['unique']>;
|
|
41
24
|
constructor(value?: string, typeOptions?: TokenCollectionOptions);
|
|
42
25
|
constructor(value?: number);
|
|
43
|
-
/**
|
|
44
|
-
* The concatenated string value of all tokens in this collection.
|
|
45
|
-
*/
|
|
46
26
|
get value(): string;
|
|
47
|
-
/**
|
|
48
|
-
* Validates the token collection structure against its configuration.
|
|
49
|
-
*
|
|
50
|
-
* Checks for unexpected spaces, consecutive commas, empty tokens,
|
|
51
|
-
* and duplicate values based on the collection's separator, allowEmpty,
|
|
52
|
-
* and unique settings.
|
|
53
|
-
*
|
|
54
|
-
* @param options - Optional validation settings including expected values and reference URL
|
|
55
|
-
* @returns The validation result
|
|
56
|
-
*/
|
|
57
27
|
check(options?: {
|
|
58
28
|
expects?: Expect[];
|
|
59
29
|
ref?: string;
|
|
60
30
|
cache?: boolean;
|
|
61
31
|
}): import("../types.js").MatchedResult | UnmatchedResult;
|
|
62
|
-
/**
|
|
63
|
-
* Splits this collection into chunks of the specified size.
|
|
64
|
-
*
|
|
65
|
-
* @param split - The number of tokens per chunk
|
|
66
|
-
* @returns An array of TokenCollection chunks
|
|
67
|
-
*/
|
|
68
32
|
chunk(split: number): TokenCollection[];
|
|
69
|
-
/**
|
|
70
|
-
* Iterates over consecutive token pairs, calling the callback for each pair.
|
|
71
|
-
*
|
|
72
|
-
* @param callback - A function receiving the previous and current token; return a token to stop iteration
|
|
73
|
-
* @returns The token returned by the callback, or `null` if iteration completes
|
|
74
|
-
*/
|
|
75
33
|
compareTokens(callback: (prev: Readonly<Token>, current: Readonly<Token>) => Readonly<Token> | null | void): Readonly<Token> | null | undefined;
|
|
76
|
-
/**
|
|
77
|
-
* Splits this collection into two at the given position.
|
|
78
|
-
*
|
|
79
|
-
* @param position - The index at which to split
|
|
80
|
-
* @returns A tuple of two TokenCollections: before and after the position
|
|
81
|
-
*/
|
|
82
34
|
divide(position: number): readonly [TokenCollection, TokenCollection];
|
|
83
|
-
/**
|
|
84
|
-
* Applies a sequence of check callbacks to consecutive tokens.
|
|
85
|
-
*
|
|
86
|
-
* Each callback receives the current head token and remaining tail.
|
|
87
|
-
* Tracks pass count and cumulative offset for error reporting.
|
|
88
|
-
*
|
|
89
|
-
* @param callbacks - The check functions to apply sequentially
|
|
90
|
-
* @returns The validation result
|
|
91
|
-
*/
|
|
92
35
|
eachCheck(...callbacks: readonly TokenEachCheck[]): Result;
|
|
93
|
-
/**
|
|
94
|
-
* Creates a new TokenCollection containing only tokens that pass the test.
|
|
95
|
-
*
|
|
96
|
-
* @param callback - The filter predicate function
|
|
97
|
-
* @returns A new TokenCollection with the filtered tokens
|
|
98
|
-
*/
|
|
99
36
|
filter(callback: Parameters<Array<Token>['filter']>[0]): TokenCollection;
|
|
100
|
-
/**
|
|
101
|
-
* Finds the first occurrence of two consecutive tokens of the same type.
|
|
102
|
-
*
|
|
103
|
-
* @param tokenType - The token type number to check for consecutive occurrences
|
|
104
|
-
* @returns The second consecutive token, or `null` if none found
|
|
105
|
-
*/
|
|
106
37
|
getConsecutiveToken(tokenType: number): Readonly<Token> | null;
|
|
107
|
-
/**
|
|
108
|
-
* Finds the first duplicated token in this collection.
|
|
109
|
-
*
|
|
110
|
-
* Comparison respects the `caseInsensitive` setting of this collection.
|
|
111
|
-
*
|
|
112
|
-
* @returns The duplicated token, or `null` if all tokens are unique
|
|
113
|
-
*/
|
|
114
38
|
getDuplicated(): Token | null;
|
|
115
|
-
/**
|
|
116
|
-
* Returns only the identifier tokens, excluding whitespace and separators.
|
|
117
|
-
*
|
|
118
|
-
* @returns A new TokenCollection containing only Ident-type tokens
|
|
119
|
-
*/
|
|
120
39
|
getIdentTokens(): TokenCollection;
|
|
121
|
-
/**
|
|
122
|
-
* Checks whether any token in this collection matches the given value.
|
|
123
|
-
*
|
|
124
|
-
* @param value - The token value, type number, regex, or array to check against
|
|
125
|
-
* @returns Whether any token matches
|
|
126
|
-
*/
|
|
127
40
|
has(value: TokenValue): boolean;
|
|
128
|
-
/**
|
|
129
|
-
* Splits this collection into a head token and a tail collection.
|
|
130
|
-
*
|
|
131
|
-
* @returns An object with `head` (first token or null) and `tail` (remaining tokens)
|
|
132
|
-
*/
|
|
133
41
|
headAndTail(): {
|
|
134
42
|
head: Token | null;
|
|
135
43
|
tail: TokenCollection;
|
|
136
44
|
};
|
|
137
|
-
/**
|
|
138
|
-
* Searches for the first token that includes the given value.
|
|
139
|
-
*
|
|
140
|
-
* @param value - The token value, type number, regex, or array to search for
|
|
141
|
-
* @returns The first matching token, or `null` if not found
|
|
142
|
-
*/
|
|
143
45
|
search(value: TokenValue): Token | null;
|
|
144
|
-
/**
|
|
145
|
-
* Verifies that tokens alternate between the specified types in order.
|
|
146
|
-
*
|
|
147
|
-
* @param tokenNumbers - The expected repeating pattern of token types
|
|
148
|
-
* @param lastTokenNumber - Optional expected type for the last token
|
|
149
|
-
* @returns An error object if the pattern is violated, or `null` if valid
|
|
150
|
-
*/
|
|
151
46
|
takeTurns(tokenNumbers: ReadonlyArray<number>, lastTokenNumber?: number): {
|
|
152
47
|
unexpectedLastToken: boolean;
|
|
153
48
|
expectedTokenNumber: number | undefined;
|
|
154
49
|
token: Token;
|
|
155
50
|
} | null;
|
|
156
|
-
/**
|
|
157
|
-
* Converts all tokens in this collection to plain JSON-serializable objects.
|
|
158
|
-
*
|
|
159
|
-
* @returns An array of plain token objects
|
|
160
|
-
*/
|
|
161
51
|
toJSON(): {
|
|
162
52
|
type: number;
|
|
163
53
|
value: string;
|