@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,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
- * Checks whether a string is a valid floating-point number.
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
- * Checks whether a string is a valid floating-point number.
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;
@@ -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,7 +1 @@
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 declare function isNonZeroUint(value: string): boolean;
@@ -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;
@@ -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);
@@ -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;
@@ -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);
@@ -1,7 +1,30 @@
1
1
  import type { FormattedPrimitiveTypeCreator } from '../types.js';
2
2
  /**
3
- * Checks whether a string is a valid BCP 47 language tag.
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;
@@ -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
- * Checks whether a string is a valid BCP 47 language tag.
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
- return !!language || (privateuse != null && privateuse.length > 0);
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
- * Callback function for sequential token checking in {@link TokenCollection.eachCheck}.
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;